Rebrand from Forte (music-store-specific) to LunarFront (any small business): - Package namespace @forte/* → @lunarfront/* - Database forte/forte_test → lunarfront/lunarfront_test - Docker containers, volumes, connection strings - UI branding, localStorage keys, test emails - All documentation and planning docs Generalize music-specific terminology: - instrumentDescription → itemDescription - instrumentCount → itemCount - instrumentType → itemCategory (on service templates) - New migration 0027_generalize_terminology for column renames - Seed data updated with generic examples - RBAC descriptions updated
32 KiB
LunarFront — Small Business Management Platform
Implementation Roadmap
Version 1.0 | Draft
1. Purpose
This document defines the phased implementation order for LunarFront — a music store management platform built by Lunarfront Tech LLC. Each phase builds on the previous, produces independently testable output, and is scoped for a solo developer working in 2-4 week increments. The goal is a working POS as early as possible, then layer on domain complexity.
Tech stack: TypeScript / Bun / Fastify / Drizzle ORM / Zod / BullMQ / PostgreSQL 16 / Valkey 8 / Turborepo monorepo. See 17_Backend_Technical_Architecture.md for full stack details.
1.1 Project Conventions
Name | Value
App name | LunarFront
Package namespace | @lunarfront/shared, @lunarfront/backend, etc.
Database (dev) | forte
Database (test) | lunarfront_test
Logging | JSON structured logging via Pino (Fastify built-in)
Linting | ESLint + Prettier at monorepo root
Auth (Phase 1-2) | Dev bypass via X-Dev-User header — JWT planned, wired in Phase 2
Auth (Phase 2+) | Self-issued JWTs + bcrypt, swap to Clerk/Auth0 later
Request tracing | Auto-generated request IDs via Fastify (included from Phase 1)
Test strategy | Separate lunarfront_test database, reset between test runs
Dev ports | API: 8000, Postgres: 5432, Valkey: 6379 (all exposed to host for dev tooling)
Multi-tenant | company_id on all tables for tenant scoping, location_id where per-location tracking needed (inventory, transactions, drawer, delivery)
2. Phase Summary
Phase | Name | Dependencies | MVP? 1 | Monorepo Scaffold, Database, Dev Environment | None | Yes 2 | Accounts, Inventory, Auth | Phase 1 | Yes 3 | POS Transactions & Cash Drawer | Phase 2 | Yes 4 | Stripe Integration (Card Payments) | Phase 3 | Yes 5 | Desktop App Shell (Electron) | Phase 4 | Yes 6 | Rentals | Phase 4 | Yes 7 | Lessons | Phase 4 | No 8 | Repairs | Phase 4 | No 9 | Accounting & QuickBooks Export | Phases 6, 7, 8 | No 10 | Licensing & Module Enforcement | Phase 9 | No 11 | Admin Panel (Web) | Phase 10 | No 12 | Customer Portal (Web) | Phase 10 | No 13 | Batch Repairs & Delivery | Phase 8 | No 14 | Advanced Billing | Phases 6, 7 | No 15 | Mobile App (iOS) | Phase 4 | No 16 | Self-Hosted Installer | Phase 10 | No 17 | AIM Migration | Phase 2 | No 18 | Personnel (Time Clock, Scheduling, Time Off) | Phase 2 | No
MVP for beta store: Phases 1–6 (scaffold through rentals + desktop app).
Phases 5–8 are largely independent after Phase 4 and can be interleaved. Phases 11–13 are also independent. Phase 18 (Personnel) can be built any time after Phase 2 — it only needs employee/user tables.
3. Phase Details
3.1 Phase 1 — Monorepo Scaffold, Database, Dev Environment
Reference docs: 01_Overall_Architecture.md, 17_Backend_Technical_Architecture.md
Goal
A running Turborepo monorepo with Docker Compose dev environment, Fastify server connected to Postgres and Redis, a health check endpoint, and the shared package exporting its first types.
Deliverables
Area | Files / Artifacts
Root config | turbo.json, root package.json (workspaces), tsconfig.base.json, .env.example, CLAUDE.md
Linting | .eslintrc.cjs, .prettierrc, root lint/format scripts in Turborepo pipeline
Docker | docker-compose.dev.yml — PostgreSQL 16 (forte database) + Valkey 8, ports exposed to host
Shared package | packages/shared/package.json (@lunarfront/shared), packages/shared/src/types/index.ts, packages/shared/src/schemas/index.ts, packages/shared/src/utils/currency.ts, packages/shared/src/utils/dates.ts
Backend package | packages/backend/package.json (@lunarfront/backend), packages/backend/src/main.ts (Fastify entry with Pino JSON logging + request ID tracing), packages/backend/src/plugins/database.ts (Drizzle connection), packages/backend/src/plugins/redis.ts, packages/backend/src/plugins/error-handler.ts, packages/backend/src/plugins/cors.ts, packages/backend/src/plugins/dev-auth.ts (dev bypass via X-Dev-User header), packages/backend/src/routes/v1/health.ts
Database | packages/backend/src/db/index.ts (client export), packages/backend/src/db/schema/companies.ts (company table — the tenant anchor), packages/backend/src/db/schema/locations.ts (location table — physical store locations), drizzle.config.ts
Seed script | packages/backend/src/db/seed.ts — creates a test company + location + admin user for local dev
Testing | vitest.config.ts, health endpoint integration test, lunarfront_test database for test isolation
Architecture Decisions Settled
company_id(tenant) +location_id(physical store) scoping pattern established on the first domain tables- Inventory and transaction tables use both
company_idandlocation_id; other tables usecompany_idonly - Drizzle migration workflow (generate → migrate)
- Shared package import convention (
@lunarfront/shared) - Standardized error response format (consistent JSON shape for errors)
- Request context pattern (
companyId,locationId, anduseron request) - JSON structured logging via Pino with request ID on every log line
- Dev auth bypass (
X-Dev-Userheader) — replaced by real JWT auth in Phase 2
Testable Outcome
bun run dev starts Fastify on port 8000. GET /v1/health returns { status: "ok", db: "connected", redis: "connected" }.
3.2 Phase 2 — Accounts, Inventory, Auth
Reference docs: 02_Domain_Accounts_Customers.md, 03_Domain_Inventory.md (sale inventory only)
Goal
The foundational domain entities that every other feature depends on — accounts, members, products, inventory units, categories, suppliers. Plus authentication so routes are protected.
Deliverables
Area | Files / Artifacts
Auth plugin | packages/backend/src/plugins/auth.ts — self-issued JWTs + bcrypt (swap to Clerk/Auth0 later)
User/Employee schema | packages/backend/src/db/schema/users.ts — user table with company_id, role enum (admin, manager, staff, technician, instructor), hashed password
Account domain | packages/backend/src/db/schema/accounts.ts — account, member, account_payment_method tables
Account routes | packages/backend/src/routes/v1/accounts.ts — CRUD + search (name, phone, email, account_number)
Account service | packages/backend/src/services/account.service.ts
Inventory domain | packages/backend/src/db/schema/inventory.ts — product, inventory_unit, category tables
Inventory routes | packages/backend/src/routes/v1/inventory.ts — CRUD, search by SKU/UPC/name, stock level queries
Inventory service | packages/backend/src/services/inventory.service.ts
Supplier schema | packages/backend/src/db/schema/suppliers.ts — supplier table
Shared types/schemas | packages/shared/src/types/account.ts, packages/shared/src/schemas/account.schema.ts, packages/shared/src/types/inventory.ts, packages/shared/src/schemas/inventory.schema.ts
Business Rules Enforced
- Every query scoped by
company_id; inventory queries also scoped bylocation_id - Account soft-delete only — financial history must be retained
- Duplicate account detection on email and phone during creation
- Product
is_serializedflag controls whetherinventory_unitrecords are required - Legacy fields (
legacy_id,legacy_source,migrated_at) present on all domain tables from day one
Testable Outcome
Full CRUD on accounts, members, products, and inventory via authenticated API calls. Account search by multiple fields works.
3.3 Phase 3 — POS Transactions & Cash Drawer
Reference docs: 07_Domain_Sales_POS.md
Goal
A working POS transaction loop — create a transaction, add line items, apply discounts, calculate tax, complete with cash payment. Cash drawer open/close with over/short tracking. This is the minimum viable POS.
Deliverables
Area | Files / Artifacts
Transaction domain | packages/backend/src/db/schema/transactions.ts — transaction (with company_id + location_id), transaction_line_item, discount, discount_audit tables
Transaction routes | packages/backend/src/routes/v1/transactions.ts — create, add line items, apply discount, complete, void, refund
Transaction service | packages/backend/src/services/transaction.service.ts — tax calculation, discount application, inventory decrement on completion
Cash drawer | packages/backend/src/db/schema/drawer.ts — drawer_session table (open, close, denominations, over/short)
Drawer routes | packages/backend/src/routes/v1/drawer.ts
Shared types/schemas | packages/shared/src/types/payment.ts, packages/shared/src/schemas/transaction.schema.ts
Business Rules Enforced
- Discounts above manager threshold require approval — logged in
discount_audit - Manual price overrides treated as discounts with reason code
- Inventory
qty_on_handdecremented on sale (non-serialized) or status changed tosold(serialized) - Transaction lifecycle:
pending→completed/voided - Refund creates a new transaction of type
refundlinked to original - Discount audit records are immutable
Testable Outcome
API-driven POS flow: open drawer, create transaction, add line items from inventory, apply discount, complete with cash, close drawer with denomination count, see over/short. Inventory quantities updated.
3.4 Phase 4 — Stripe Integration (Card Payments)
Reference docs: 08_Domain_Payments_Billing.md, 07_Domain_Sales_POS.md
Goal
Card-present and card-keyed payments via Stripe Terminal and Stripe Elements. Webhook handling for payment confirmation. This turns the cash-only POS into a real POS.
Deliverables
Area | Files / Artifacts
Payment provider interface | packages/backend/src/payment-providers/base.ts — PaymentProvider interface (supports future Global Payments addition)
Stripe provider | packages/backend/src/payment-providers/stripe.ts — charge, refund, terminal reader discovery, terminal payment collection, setup session
Provider factory | packages/backend/src/payment-providers/factory.ts
Webhook handler | packages/backend/src/routes/v1/webhooks.ts — payment_intent.succeeded, payment_intent.payment_failed
Webhook storage | packages/backend/src/db/schema/webhooks.ts — stripe_webhook_event table (store-before-process pattern)
Stripe customer sync | Account creation triggers Stripe Customer creation, stripe_customer_id stored on account
Architecture Notes
The PaymentProvider interface is defined now even though only Stripe is implemented. Global Payments (PAY-GP) can be added later without changing business logic.
Critical design difference: Stripe manages recurring billing via its Subscriptions API (provider-managed). Global Payments only provides tokenized charge capability — the platform must own the billing schedule via BullMQ cron jobs (platform-managed). The interface exposes managedSubscriptions: boolean so the billing service knows whether to delegate or self-manage. For GP stores, the platform also handles proration calculations internally, whereas Stripe calculates proration automatically. See 08_Domain_Payments_Billing.md section 6 for full details.
Testable Outcome
Create a transaction, complete with Stripe Terminal (test mode reader) or Stripe Elements. Webhook confirms payment. Transaction marked completed. Refund via Stripe works.
3.5 Phase 5 — Desktop App Shell (Electron)
Reference docs: 01_Overall_Architecture.md, 17_Backend_Technical_Architecture.md
Goal
An Electron app that runs the POS. Not feature-complete — just the shell with navigation, auth login, and the POS transaction screen wired to the API. This is the first time a human can use the system.
Deliverables
Area | Files / Artifacts
Desktop package | packages/desktop/package.json — Electron + React + Vite
App shell | Main window, menu bar, navigation sidebar
Auth screen | Login form, JWT storage, auto-refresh
POS screen | Product search/scan, cart, discount application, payment method selection (cash/card), complete transaction
Customer lookup | Search by name/phone/email, attach account to transaction
Inventory browse | Product list, stock levels, basic filters
API client | Shared API client layer (packages/shared/src/api/client.ts or generated via tools/codegen) — reused by all React apps
Testable Outcome
A human can log in, search inventory, ring up a sale, take cash or card payment, and see the transaction complete.
3.6 Phase 6 — Rentals
Reference docs: 04_Domain_Rentals.md, 08_Domain_Payments_Billing.md, 11_Domain_Billing_Date_Management.md
Goal
Full rental lifecycle — create contracts (all four types), track rental fleet, handle deposits, process returns. Recurring billing via Stripe Subscriptions. Rent-to-own equity tracking and buyout.
Deliverables
Area | Files / Artifacts
Rental domain | packages/backend/src/db/schema/rentals.ts — rental, rental_payment tables
Rental routes | packages/backend/src/routes/v1/rentals.ts — create, return, buyout, list by account
Rental service | packages/backend/src/services/rental.service.ts
Billing service | packages/backend/src/services/billing.service.ts — Stripe subscription create, add/remove line items, billing group consolidation
RTO business logic | packages/shared/src/business-logic/rto.ts — equity calculation, buyout amount
Webhook additions | invoice.paid, invoice.payment_failed, customer.subscription.deleted handlers
Billing anchor | billing_anchor_day column on rental, capped at 28, change audit log
Desktop UI | Rental intake form, rental list, return workflow, RTO buyout screen
Business Rules Enforced
- Billing group consolidation vs split billing
- RTO equity accumulation per payment
- Deposit collected at start (tracked as liability)
- Return updates
inventory_unitstatus toavailableorin_repair - Mid-cycle rental add prorates via Stripe
- Billing anchor day requests for 29th/30th/31st capped to 28th
Testable Outcome
Create a rental, see Stripe subscription created (test mode). Simulate payment via webhook. See equity accumulate on RTO. Process return. Process buyout.
3.7 Phase 7 — Lessons
Reference docs: 05_Domain_Lessons.md
Goal
Instructor management, scheduling, enrollments, attendance tracking, makeup credits. Recurring billing via Stripe Subscriptions. Session notes, homework, grading scales, and lesson plans.
Deliverables
Area | Files / Artifacts
Lesson domain | packages/backend/src/db/schema/lessons.ts — instructor, lesson_type, schedule_slot, enrollment, lesson_session tables
Grading | grading_scale, grading_scale_level tables + default seed data (Standard Letter, Numeric 1-10, ABRSM Style, Progress, Concert Readiness, Simple)
Lesson plans | member_lesson_plan, lesson_plan_section, lesson_plan_item, lesson_plan_item_grade_history, lesson_session_plan_item tables
Templates | lesson_plan_template, lesson_plan_template_section, lesson_plan_template_item tables
Lesson routes | packages/backend/src/routes/v1/lessons.ts — enrollment CRUD, session CRUD, attendance, lesson plan CRUD, grading
Lesson service | packages/backend/src/services/lesson.service.ts
Billing | Enrollment creates Stripe subscription (or adds line item to existing) — reuses billing service from Phase 6
Desktop UI | Schedule view, enrollment form, attendance marking, post-lesson notes form, lesson plan editor
Business Rules Enforced
- No double-booking (student or instructor at same day/time)
- Group lesson
max_studentscap onschedule_slot - Makeup credits linked to original missed session via
makeup_for_session_id - Only one active lesson plan per enrollment at a time
instructor_notesnever exposed to student/parent API routes- Scale levels used in grade history cannot be deleted — only deactivated
- Skipped items excluded from progress percentage
Testable Outcome
Create instructor, define schedule, enroll student, mark attendance, record session notes with grades, see lesson plan progress percentage.
3.8 Phase 8 — Repairs
Reference docs: 06_Domain_Repairs.md, 03_Domain_Inventory.md (repair parts sections)
Goal
Full individual repair ticket lifecycle from intake through pickup/payment. Parts logging, labor tracking, flat-rate services. Repair parts inventory separate from sale inventory.
Deliverables
Area | Files / Artifacts
Repair domain | packages/backend/src/db/schema/repairs.ts — repair_ticket, repair_line_item tables
Repair parts | packages/backend/src/db/schema/repair-parts.ts — repair_part, repair_part_usage_template, repair_part_usage tables
Repair routes | packages/backend/src/routes/v1/repairs.ts — full lifecycle CRUD, parts logging, estimate generation
Repair service | packages/backend/src/services/repair.service.ts
Repair payment | Payment at pickup integrates with transaction system (Phase 3)
Seed data | Default bow hair usage templates (full size, cello, bass, fractional sizes)
Desktop UI | Repair intake form, technician work screen with parts logging, repair queue/list, pickup/payment flow
Business Rules Enforced
- Status lifecycle with required transitions (intake → diagnosing → pending_approval → approved → in_progress → ready → picked_up)
- Estimate approval required before work begins (waivable with manager override)
- Parts cost recorded at time of use — historical accuracy preserved
- Technician cannot log more parts than
qty_on_hand - Dual-use parts decrement sale inventory
qty_on_hand - Shop supply parts tracked for cost but never appear on customer invoice
- Flat-rate services bundle labor + material into single invoice line item
- Actual cost variance from estimate requires reason code in audit trail
Testable Outcome
Create repair ticket, log labor and parts, generate estimate, approve, mark complete, collect payment at pickup. Repair parts inventory decremented.
3.9 Phase 9 — Accounting & QuickBooks Export
Reference docs: 12_Domain_Accounting_Journal_Entries.md
Goal
Automatic double-entry journal entry generation for every financial event. Store-configurable chart of accounts matching QuickBooks. CSV export for QB import. In-platform financial reports.
Deliverables
Area | Files / Artifacts
Accounting domain | packages/backend/src/db/schema/accounting.ts — account_code, journal_entry, journal_entry_line, export_batch tables
JE generation | packages/backend/src/services/accounting.service.ts — functions for each event type (cash sale, card sale, rental payment, RTO payment/buyout, lesson payment, repair payment, deposits, refunds, discounts, drawer variance, proration, inventory purchase, bad debt write-off)
JE rules | packages/shared/src/business-logic/accounting.ts — journal entry mapping rules (which accounts debit/credit per event)
Export | packages/backend/src/routes/v1/accounting.ts — export by date range (CSV), mark exported, reconciliation confirmation
Seed data | Default chart of accounts (assets 1000–1320, liabilities 2000–2400, revenue 4000–4500, contra 4900–4920, COGS 5000–5100, expenses 6000–6300)
Reports | Revenue summary, gross vs net, AR aging, deferred revenue, cash flow, Stripe clearing reconciliation, cash over/short history, COGS vs revenue
Architecture Note
This phase retrofits accounting hooks into all previously built financial flows. AccountingService.record*() calls are added to transaction, rental, lesson, and repair services. Deferred to this phase intentionally — operational flows must be correct before accounting hooks are layered on.
Business Rules Enforced
total_debitsmust equaltotal_credits— entries that don't balance are rejected- Entry date cannot be future, and past > 30 days requires manager override
- Voided entries generate reversing entries (equal and opposite) — never deleted
- Exported entries excluded from subsequent exports unless manager override
- Chart of accounts
quickbooks_account_namemust match QB exactly
Testable Outcome
Complete a sale, see journal entry generated automatically. Export date range as CSV. Verify debits equal credits on every entry. AR aging report shows outstanding balances.
3.10 Phase 10 — Licensing & Module Enforcement
Reference docs: 15_Licensing_Modules_Pricing.md
Goal
Ed25519 signed license files control which modules are active. API route guards block unlicensed routes. UI shows upgrade prompts for locked modules. Trial licenses enable 30-day evaluation.
Deliverables
packages/backend/src/license/verify.ts— Ed25519 signature verification, public key embedded at build timepackages/backend/src/license/guard.ts—requireModule()Fastify preHandler hookpackages/backend/src/plugins/license.ts— loads license on startup, caches in memorypackages/shared/src/types/license.ts—License,LicenseModuletypespackages/shared/src/constants/modules.ts—MODULE_IDSenum (CORE, MOD-RENTALS, MOD-LESSONS, MOD-REPAIRS, etc.)packages/shared/src/business-logic/license.ts—hasModule()function for frontend checks- Trial license API endpoint — generates 30-day trial for any unlicensed module
- Shared React upgrade prompt component for unlicensed module screens
Testable Outcome
API route returns 403 with upgrade prompt when module not licensed. UI shows upgrade prompt instead of module content. Trial license enables module for 30 days.
3.11 Phase 11 — Admin Panel (Web)
Reference docs: 01_Overall_Architecture.md
Deliverables
packages/admin/— React + Vite web app- User management — CRUD for employee accounts, role assignment
- Store settings — store name, address, tax configuration, timezone, billing preferences
- License management — upload license file, view active modules, start trials
- Audit log viewer — searchable log of discount audits, billing changes, refunds, write-offs
- Reporting dashboards — sales summary, rental/lesson enrollment counts, repair queue metrics
packages/backend/src/routes/v1/admin.ts— admin-only endpoints
3.12 Phase 12 — Customer Portal (Web)
Reference docs: 05_Domain_Lessons.md (parent portal sections)
Deliverables
packages/web-portal/— React + Vite web app- Separate customer auth flow (email/password or magic link) — scoped to account data only
- Lesson view — enrollment summary, upcoming lessons, session notes (student-facing only), homework, lesson plan progress, grade history
- Repair status — ticket status tracking, estimated completion date
- Billing — invoice history, payment history, online payment via Stripe Elements
packages/backend/src/routes/v1/portal.ts— customer-scoped endpoints (never expose instructor private notes)
3.13 Phase 13 — Batch Repairs & Delivery
Reference docs: 09_Domain_Batch_Repairs.md, 10_Domain_Delivery_Chain_of_Custody.md
Deliverables
repair_batchtable — batch lifecycle (scheduled → picked_up → intake_complete → pending_approval → approved → in_progress → ready → delivered → invoiced → paid → closed)- Batch approval cascading to all child
repair_ticketrecords - Batch invoicing — itemized per instrument with work, parts, labor
delivery_event,delivery_event_itemtables — condition tracking at every handoff, signature capture (base64), photo URLs (S3)- School PO number on batch and invoice
- Desktop UI for batch intake, delivery scheduling, batch invoice generation
- Delivery completion auto-updates linked repair ticket and batch status
Business Rules
- Batch status auto-advances to
readywhen all child tickets reachreadyorcancelled - Instrument count discrepancy flagged at pickup — requires staff acknowledgement
- Missing instrument (
was_transferred = false) triggers manager alert - Signature required for
store_pickupandstore_deliveryevents
3.14 Phase 14 — Advanced Billing
Reference docs: 11_Domain_Billing_Date_Management.md
Deliverables
billing_anchor_change_logtable — append-only audit- Billing anchor change routes — preview proration, execute change, bulk preview, bulk execute, history
- Proration logic — earlier day (credit), later day (charge), consolidation, splitting
- Edge cases — 48hr pending invoice window warning, failed payment blocks anchor change, paused subscription (no proration), RTO equity unaffected
- Bulk change — grouped under
bulk_change_id, sequential Stripe API calls, full rollback on partial failure - Bulk change preview screen showing per-subscription proration breakdown
3.15 Phase 15 — Mobile App (iOS)
Reference docs: 01_Overall_Architecture.md
Deliverables
packages/mobile/— React Native iOS app- Convention POS — product search, cart, payment via Stripe Terminal Bluetooth reader
- Customer lookup
- Delivery workflow — pull up delivery event, check off instruments, capture condition/photos/signature
- Offline queue — local SQLite for transactions when offline, sync on reconnect
- Convention cash drawer (account 1010)
3.16 Phase 16 — Self-Hosted Installer
Reference docs: 14_Self_Hosted_Installer_Platform_Compatibility.md, 16_Windows_Installer_PowerShell.md
Deliverables
- Production
docker-compose.yml— api, postgres, valkey, admin (nginx), portal (nginx), nginx (reverse proxy), updater, backup services Dockerfile— multi-stage build: compile backend withbun build --compile, copy binary to clean Ubuntu 24.04 imagetools/installer/install.ps1— phased PowerShell installer with Hyper-V/WSL2 detection, Docker install, resume-after-reboot via registry Run key, NSSM service registrationtools/installer/tray-app/— Go system tray app with health monitoring (green/yellow/orange/red), update checker, manual backup trigger, log viewer- Automated backup service — daily pg_dump, 30-day retention, integrity verification
- Update registry API —
GET /check?version=X&license=Yreturns latest version info - macOS
.dmgwith menu bar app - Linux one-line install script with systemd service
3.17 Phase 17 — AIM Migration
Reference docs: 01_Overall_Architecture.md (migration sections), 08_Domain_Payments_Billing.md (payment transition)
Deliverables
tools/migration/— TypeScript ETL scripts connecting directly to AIM MSSQL database- Per-domain extraction scripts — accounts/customers, inventory, open rentals, active lessons, repair history
- Data validation — dry-run mode, exception logging, manual review of edge cases
- Duplicate detection — run before import, staff reviews conflicts
- Legacy tagging — all migrated records tagged with
legacy_id,legacy_source = 'aim',migrated_at - Payment transition —
requires_payment_update = trueon migrated payment methods, staff prompted to collect new card at each renewal - Old processor wind-down — 120-day window after last transaction before closing old processor account
3.18 Phase 18 — Personnel (Time Clock, Scheduling, Time Off)
Reference docs: 19_Domain_Personnel.md
Goal
Employee records, time clock with location tracking, work schedules, time-off requests with approval workflow, overtime calculation, and payroll export. This is a licensed module (MOD-PERSONNEL).
Deliverables
employee,time_clock_entry,schedule,schedule_recurring_template,time_off_request,time_off_balancetables- Employee CRUD with multi-role support (staff, technician, instructor, manager, admin)
- Time clock routes — clock in/out, manager edit with audit trail
- Schedule management — recurring templates, concrete entries, conflict detection
- Time-off workflow — request, approve/deny, balance tracking, accrual
- Overtime calculation based on configurable rules (weekly/daily thresholds)
- Payroll CSV export for external payroll services
- Integration hooks: instructor_id and technician_id reference employee table
Business Rules
- Employee can work at any location (not locked to one)
- Clock entries cannot overlap — no simultaneous clock-ins
- Auto clock-out at midnight if forgotten, flagged for manager review
- Per-lesson instructors tracked per session, not via time clock
- Terminated employees retain all historical records
Testable Outcome
Create employee, clock in/out at a location, create schedule, submit and approve time-off request, generate payroll export CSV.
4. Dependency Map
P1 Scaffold → P2 Accounts/Inventory → P3 POS → P4 Stripe
| |
P18 +-------+-------+-------+
Personnel | | | |
P5 P6 P7 P8
Desktop Rentals Lessons Repairs
| | | |
+-------+---+---+-------+
|
P9 Accounting
|
P10 Licensing
|
+-------+---+---+-------+
| | |
P11 P12 P13
Admin Portal Batch/Delivery
| | |
+-------+-------+-------+
|
P14 Adv Billing
|
+-----+-----+
| |
P15 P16
Mobile Installer
|
P17
Migration
5. MVP Definition
The minimum viable product for a beta store deployment is Phases 1–6:
- Monorepo + dev environment (Phase 1)
- Accounts + inventory + auth (Phase 2)
- POS with cash payments (Phase 3)
- Card payments via Stripe (Phase 4)
- Desktop Electron app (Phase 5)
- Instrument rentals with recurring billing (Phase 6)
This covers the most common daily operations — a store can use it alongside AIM for new transactions while legacy data remains in AIM.
6. Verification Strategy
After each phase:
- Run
bun test— all tests pass docker compose -f docker-compose.dev.yml up -d— all services healthy- Hit API endpoints via curl or httpie to verify CRUD operations work
- After Phase 5+: manual testing through desktop app
- After Phase 9: verify every journal entry has
total_debits = total_credits - After Phase 16: clean install on a fresh Windows machine, platform running within 30 minutes
7. Cross-Cutting Concerns Not Yet Addressed
The following items are referenced across multiple planning documents but do not have dedicated domain docs. They should be designed before or during their dependent phase:
Concern | Needed By | Notes
Tax configuration | Phase 3 | Sales tax collected at POS but no tax rate config, jurisdiction handling, or TaxJar/Avalara integration designed
Notification system | Phase 6 | Repairs, billing, and lessons reference "notify customer" but no channel definitions, preferences schema, or template system exists
Offline sync strategy | Phase 15 | iOS app and desktop both reference offline queuing but conflict resolution is not designed
Search/indexing | Phase 2 | Account/customer lookup described but no full-text search, trigram indexes, or search infrastructure specified
Cash drawer schema | Phase 3 | Mentioned in POS and accounting docs but no drawer_session table defined in any planning doc
Frontend themes | Phase 5 | All frontend apps should support themes with per-user preference storage
Resolved
- Employee/user table schema → now covered by
19_Domain_Personnel.md(Phase 18) storeentity ambiguity → resolved ascompany(tenant) +location(physical store)studententity naming → renamed tomemberto support multiple adults per account- Redis → replaced with Valkey 8 (Redis-compatible fork)
- Payment provider billing ownership → documented in
08_Domain_Payments_Billing.mdsection 6 (Stripe = provider-managed, GP = platform-managed)