Skip to content

V2 Scope

The full prioritized scope for racket-book V2, plus the four locked data-model decisions that the rest of the team builds against. Cross-cuts: V1 baseline, use cases, data model, NFRs.

MoSCoW

Must (V2 — ship target)

# Item
M1 Web app, mobile-first responsive (phone + desktop, no native apps)
M2 Multi-tenant: multiple stringers on one platform, strict data isolation
M3 Stringer onboarding by admin (Stefan) via emailed magic link → self-fill profile (registration step). See M21 for ongoing sign-in.
M4 Auth on the listing — kill public exposure of client names
M5 Person + ClientProfile entities (was "Player" pre-2026-05-02): platform-level Person (display name, optional email, locale, notification prefs) + per-stringer ClientProfile (private notes, is_stringer_self flag, per-client notification overrides). See client identity & sharing.
M6 Single Racket entity with optional serial / instance ID + manual-entry fallback
M7 Strings catalogue with manual override for client-specific strings
M8 Unified Order entity (no self/client split); self-only fields optional on every order
M9 "Add Stringjob" flow: search-client → select → "copy last" (or auto-prefill) → adjust → save. Editable price.
M10 Lifecycle dates Ordered / Strung / Returned / Paid on every order; blank-allowed for self
M11 Per-side BYO flag on Order
M12 Manual paid-date toggle (no online payments, no invoicing)
M13 A4 PDF receipt — clean modern redesign, total in top-left quarter, regeneratable
M14 Receipt delivered both printed AND email at Strung; one receipt per racket-order
M15 Migrate full XLSX history: 329 client orders + 416 self orders + Stefan's rackets list
M16 Reporting: year/month rollups, top clients, mean turnaround (Ordered → Strung)
M17 Shared catalogue (Strings + Rackets) with admin-moderated promotion via request-queue (stringer submits → admin inbox + unread badge → admin promotes or rejects). No unilateral flag-flip by stringers.
M18 Rule 1 — Stringer-initiated per-job share. Stringer A grants Stringer B read-only access to specific past jobs A performed for a given Person (via A's ClientProfile for that Person), with a bulk-UI "share all existing past jobs for this client" convenience that compiles down to per-job grant rows. Receiving stringer sees the jobs in a "Shared with me" inbox; the visibility/redaction matrix hides client PII (except first name), pricing, and comments while exposing technical fields (racket, strings, tension, DT, color, BYO, lifecycle dates). Either side can revoke at any time; revocation is server-side instant. Every grant create + revoke + access is audit-logged. This is "Rule 1" of the three sharing rules; Rules 2 (client-initiated per-job) and 3 (client global preference, future-inclusive) require the V3 client portal and are out of V2 scope. See client identity & sharing for the full rule set, identity model, and acceptance criteria.
M19 EN + DE UI and PDF receipt templates
M20 Per-stringer self-service export: XLSX + full-context JSON
M21 Stringer auth: first sign-in via magic-link (registration); password sign-in available thereafter. Both methods supported per stringer.

Should (V2 if cheap, else V3)

# Item Notes
S1 SMS notifications Only if integration is free / near-free
S2 Per-client notification overrides Template + channel preferences per client

Could (V3)

# Item
C1 Client-facing magic-link portal (view own history, receipts, "ready" status)
C2 Auto pickup-ready notification dispatch (data model captured in V2)
C3 Bulk import of historical data on stringer onboarding
C4 PWA / offline order entry with deferred sync

See V3 vision for fuller detail on the V3 candidates.

Won't (out of V2 and V3 scope)

# Item Notes
W1 Online payments / payment gateway integration Cash + transfer + manual paid-date is enough
W2 Native mobile apps (iOS / Android) Responsive web only
W3 Partial-return / cancellation / warranty workflow Handled informally (free re-string at 25 CHF "to not break stats")

Locked decisions

Four data-model decisions confirmed by Stefan on 2026-04-27. All downstream design is built against these.

1. Unified Order entity

V2 has a single Order entity for both self-stringing and client-stringing jobs. The V1 split (Self-Stringing-Orders vs Client-Stringing-Orders) was an artifact of separate spreadsheets, not a real domain difference.

The "is this a self job?" distinction lives on the ClientProfile entity (the stringer-scoped record about a Person; was called "Player" pre-2026-05-02), via an is_stringer_self boolean. Each stringer has 0..1 self-ClientProfile records. "Self vs client" reporting is preserved as WHERE client_profile.is_stringer_self = true.

2. Optional self-fields on every order

The fields previously self-only become nullable columns on every Order, available regardless of which ClientProfile the order is for:

  • per-side string color (Main / Cross)
  • method (e.g. "Standard")
  • post-stringing dynamic tension reading

UI surfaces them prominently on self-orders, collapses them behind "advanced" on client orders. No separate table.

3. Present-but-blank lifecycle dates on self-orders

Self-orders carry the same Ordered / Strung / Returned / Paid columns as client orders, but in practice only Strung is filled — the other three stay null. No conditional schema, no UI gymnastics; the form just doesn't surface them prominently for self-orders.

4. Single Racket entity

One Racket table covers Stefan's own rackets and every client's racket. Optional serial_number / instance ID disambiguates same-model duplicates. Stefan's V1 Lists.My Racquets migrates as Racket rows owned by his self-ClientProfile; client rackets migrate as Racket rows owned by their respective client ClientProfiles. Per-racket history works uniformly across both populations.

Tradeoffs accepted

  • Sparser rows on a typical client order (empty color / method / post-DT). Stefan accepts this in exchange for one form, one list, one report path.
  • ~~Duplicate Players across stringers: the same human client of two different stringers is stored as two Player records. No automatic linking. The "share player" button (M18) is the explicit, manual mechanism for crossing that boundary.~~ Superseded 2026-05-02: the same human is now one platform-level Person, with one stringer-scoped ClientProfile per stringer that has them as a client. Cross-stringer visibility of stringing-job data is via the three sharing rules in client identity & sharing. Identity matching is verified-email only.