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:
@@ -2,6 +2,7 @@ import { useState } from 'react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { queryOptions } from '@tanstack/react-query'
|
||||
import { api } from '@/lib/api-client'
|
||||
import { usePOSStore } from '@/stores/pos.store'
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Button } from '@/components/ui/button'
|
||||
@@ -57,6 +58,7 @@ export function POSTransactionsDialog({ open, onOpenChange }: POSTransactionsDia
|
||||
queryFn: () => api.get<{ data: AppConfigEntry[] }>('/v1/config'),
|
||||
enabled: !!receiptTxnId,
|
||||
})
|
||||
const receiptFormat = usePOSStore((s) => s.receiptFormat)
|
||||
const receiptConfig = {
|
||||
header: configData?.data?.find((c) => c.key === 'receipt_header')?.value || undefined,
|
||||
footer: configData?.data?.find((c) => c.key === 'receipt_footer')?.value || undefined,
|
||||
@@ -68,11 +70,11 @@ export function POSTransactionsDialog({ open, onOpenChange }: POSTransactionsDia
|
||||
if (receiptTxnId && receiptData) {
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-sm max-h-[90vh] overflow-y-auto">
|
||||
<DialogContent className={`${receiptFormat === 'full' ? 'max-w-2xl' : 'max-w-sm'} max-h-[90vh] overflow-y-auto`}>
|
||||
<div className="flex justify-between items-center mb-2">
|
||||
<Button variant="ghost" size="sm" onClick={() => setReceiptTxnId(null)}>Back</Button>
|
||||
<div className="flex gap-2">
|
||||
<Button variant="outline" size="sm" onClick={() => downloadReceiptPDF(receiptData.transaction.transactionNumber)}>
|
||||
<Button variant="outline" size="sm" onClick={() => downloadReceiptPDF(receiptData.transaction.transactionNumber, receiptFormat)}>
|
||||
Save PDF
|
||||
</Button>
|
||||
<Button size="sm" onClick={printReceipt} className="gap-2">
|
||||
@@ -81,7 +83,7 @@ export function POSTransactionsDialog({ open, onOpenChange }: POSTransactionsDia
|
||||
</div>
|
||||
</div>
|
||||
<div id="pos-receipt-print">
|
||||
<POSReceipt data={receiptData} config={receiptConfig} />
|
||||
<POSReceipt data={receiptData} size={receiptFormat} config={receiptConfig} />
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
Reference in New Issue
Block a user