Files
lunarfront-app/planning/18_Implementation_Roadmap.md
Ryan Moon 9400828f62 Rename Forte to LunarFront, generalize for any small business
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
2026-03-30 08:51:54 -05:00

32 KiB
Raw Blame History

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 16 (scaffold through rentals + desktop app).

Phases 58 are largely independent after Phase 4 and can be interleaved. Phases 1113 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_id and location_id; other tables use company_id only
  • 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, and user on request)
  • JSON structured logging via Pino with request ID on every log line
  • Dev auth bypass (X-Dev-User header) — 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.tsuser table with company_id, role enum (admin, manager, staff, technician, instructor), hashed password Account domain | packages/backend/src/db/schema/accounts.tsaccount, 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.tsproduct, 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.tssupplier 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 by location_id
  • Account soft-delete only — financial history must be retained
  • Duplicate account detection on email and phone during creation
  • Product is_serialized flag controls whether inventory_unit records 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.tstransaction (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.tsdrawer_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_hand decremented on sale (non-serialized) or status changed to sold (serialized)
  • Transaction lifecycle: pendingcompleted / voided
  • Refund creates a new transaction of type refund linked 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.tsPaymentProvider 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.tspayment_intent.succeeded, payment_intent.payment_failed Webhook storage | packages/backend/src/db/schema/webhooks.tsstripe_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.tsrental, 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_unit status to available or in_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.tsinstructor, 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_students cap on schedule_slot
  • Makeup credits linked to original missed session via makeup_for_session_id
  • Only one active lesson plan per enrollment at a time
  • instructor_notes never 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.tsrepair_ticket, repair_line_item tables Repair parts | packages/backend/src/db/schema/repair-parts.tsrepair_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.tsaccount_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 10001320, liabilities 20002400, revenue 40004500, contra 49004920, COGS 50005100, expenses 60006300) 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_debits must equal total_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_name must 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 time
  • packages/backend/src/license/guard.tsrequireModule() Fastify preHandler hook
  • packages/backend/src/plugins/license.ts — loads license on startup, caches in memory
  • packages/shared/src/types/license.tsLicense, LicenseModule types
  • packages/shared/src/constants/modules.tsMODULE_IDS enum (CORE, MOD-RENTALS, MOD-LESSONS, MOD-REPAIRS, etc.)
  • packages/shared/src/business-logic/license.tshasModule() 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_batch table — batch lifecycle (scheduled → picked_up → intake_complete → pending_approval → approved → in_progress → ready → delivered → invoiced → paid → closed)
  • Batch approval cascading to all child repair_ticket records
  • Batch invoicing — itemized per instrument with work, parts, labor
  • delivery_event, delivery_event_item tables — 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 ready when all child tickets reach ready or cancelled
  • Instrument count discrepancy flagged at pickup — requires staff acknowledgement
  • Missing instrument (was_transferred = false) triggers manager alert
  • Signature required for store_pickup and store_delivery events

3.14 Phase 14 — Advanced Billing

Reference docs: 11_Domain_Billing_Date_Management.md

Deliverables

  • billing_anchor_change_log table — 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 with bun build --compile, copy binary to clean Ubuntu 24.04 image
  • tools/installer/install.ps1 — phased PowerShell installer with Hyper-V/WSL2 detection, Docker install, resume-after-reboot via registry Run key, NSSM service registration
  • tools/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=Y returns latest version info
  • macOS .dmg with 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 = true on 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_balance tables
  • 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 16:

  • 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)
  • store entity ambiguity → resolved as company (tenant) + location (physical store)
  • student entity naming → renamed to member to support multiple adults per account
  • Redis → replaced with Valkey 8 (Redis-compatible fork)
  • Payment provider billing ownership → documented in 08_Domain_Payments_Billing.md section 6 (Stripe = provider-managed, GP = platform-managed)