feat: repair-POS integration, receipt formats, manager overrides, price adjustments

- Add thermal/full-page receipt format toggle (per-device, localStorage)
- Full-page receipt uses clean invoice layout matching repair PDF style
- Settings page reorganized into tabbed sections (Store, Locations, Modules, Receipt, POS Security, Advanced)
- Manager override system: configurable PIN prompt for void, refund, discount, cash in/out
- Discount threshold setting: require manager approval above X%
- Consumable product type: tracked for internal job costing, excluded from POS search, receipts, and customer-facing totals
- Repair line item dialog: product picker dropdown for parts/consumables from inventory
- Repair → POS checkout: load ready-for-pickup tickets into repair_payment transactions with proper tax categories (labor=service, parts=goods)
- Transaction completion auto-updates repair ticket status to picked_up
- POS Repairs dialog with Pickup and New Intake tabs, customer account lookup
- Inline price adjustment on cart items: % off, $ off, or set price with live preview
- Order-level discount button with same three input modes
- Backend: migration 0043 (consumable enum + is_consumable flag), createFromRepairTicket service, ready-for-pickup endpoint
- Fix: backend dev script uses --env-file for turbo compatibility

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ryan
2026-04-05 01:32:28 +00:00
parent e16655d133
commit 9d51fb2118
32 changed files with 1507 additions and 199 deletions

View File

@@ -61,6 +61,7 @@ export const ProductCreateSchema = z.object({
isSerialized: z.boolean().default(false),
isRental: z.boolean().default(false),
isDualUseRepair: z.boolean().default(false),
isConsumable: z.boolean().default(false),
price: z.number().min(0).optional(),
minPrice: z.number().min(0).optional(),
rentalRateMonthly: z.number().min(0).optional(),

View File

@@ -13,7 +13,7 @@ export const RepairTicketStatus = z.enum([
])
export type RepairTicketStatus = z.infer<typeof RepairTicketStatus>
export const RepairLineItemType = z.enum(['labor', 'part', 'flat_rate', 'misc'])
export const RepairLineItemType = z.enum(['labor', 'part', 'flat_rate', 'misc', 'consumable'])
export type RepairLineItemType = z.infer<typeof RepairLineItemType>
export const RepairConditionIn = z.enum(['excellent', 'good', 'fair', 'poor'])