feat: named registers, X/Z reports, daily rollup, fix drawerSessionId
Registers: - New register table with location association - CRUD service + API routes (POST/GET/PATCH/DELETE /registers) - Drawer sessions now link to a register via registerId - Register ID persisted in localStorage per device X/Z Reports: - ReportService with getDrawerReport() (X or Z depending on session state) - Z report auto-displayed on drawer close in the drawer dialog - X report (Current Shift Report) button on open drawer view - Report shows: sales summary, payment breakdown, discounts, cash accountability, adjustments Daily Rollup: - ReportService.getDailyReport() aggregates all sessions at a location for a date - New /reports/daily endpoint with locationId + date params - Frontend daily report page with date picker, location selector, session breakdown Critical Fix: - drawerSessionId is now populated on transactions when completing (was never set before) - This enables accurate per-drawer reporting and cash accountability Migration 0044: register table, drawer_session.register_id column Tests: 14 new (register CRUD, drawer report X/Z, drawerSessionId population, daily rollup, register-drawer link) Full suite: 367 passed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -13,6 +13,7 @@ type ReceiptFormat = 'thermal' | 'full'
|
||||
interface POSState {
|
||||
currentTransactionId: string | null
|
||||
locationId: string | null
|
||||
registerId: string | null
|
||||
drawerSessionId: string | null
|
||||
locked: boolean
|
||||
cashier: POSUser | null
|
||||
@@ -25,6 +26,7 @@ interface POSState {
|
||||
receiptFormat: ReceiptFormat
|
||||
setTransaction: (id: string | null) => void
|
||||
setLocation: (id: string) => void
|
||||
setRegister: (id: string | null) => void
|
||||
setDrawerSession: (id: string | null) => void
|
||||
unlock: (user: POSUser, token: string) => void
|
||||
lock: () => void
|
||||
@@ -45,7 +47,8 @@ function getStoredReceiptFormat(): ReceiptFormat {
|
||||
export const usePOSStore = create<POSState>((set) => ({
|
||||
currentTransactionId: null,
|
||||
locationId: null,
|
||||
drawerSessionId: null,
|
||||
registerId: localStorage.getItem('pos_register_id') ?? null,
|
||||
drawerSessionId: localStorage.getItem('pos_drawer_session_id') ?? null,
|
||||
locked: true,
|
||||
cashier: null,
|
||||
token: null,
|
||||
@@ -57,7 +60,8 @@ export const usePOSStore = create<POSState>((set) => ({
|
||||
receiptFormat: getStoredReceiptFormat(),
|
||||
setTransaction: (id) => set({ currentTransactionId: id }),
|
||||
setLocation: (id) => set({ locationId: id }),
|
||||
setDrawerSession: (id) => set({ drawerSessionId: id }),
|
||||
setRegister: (id) => { if (id) localStorage.setItem('pos_register_id', id); else localStorage.removeItem('pos_register_id'); set({ registerId: id }) },
|
||||
setDrawerSession: (id) => { if (id) localStorage.setItem('pos_drawer_session_id', id); else localStorage.removeItem('pos_drawer_session_id'); set({ drawerSessionId: id }) },
|
||||
unlock: (user, token) => set({ locked: false, cashier: user, token, lastActivity: Date.now() }),
|
||||
lock: () => set({ locked: true, currentTransactionId: null }),
|
||||
touchActivity: () => set({ lastActivity: Date.now() }),
|
||||
|
||||
Reference in New Issue
Block a user