diff --git a/packages/admin/src/components/pos/pos-customer-dialog.tsx b/packages/admin/src/components/pos/pos-customer-dialog.tsx index a2bf740..c580dbb 100644 --- a/packages/admin/src/components/pos/pos-customer-dialog.tsx +++ b/packages/admin/src/components/pos/pos-customer-dialog.tsx @@ -46,15 +46,15 @@ function accountSearchOptions(search: string) { }) } -function customerHistoryOptions(accountId: string | null) { +function customerHistoryOptions(accountId: string | null, itemSearch?: string) { return queryOptions({ - queryKey: ['pos', 'customer-history', accountId], + queryKey: ['pos', 'customer-history', accountId, itemSearch ?? ''], queryFn: () => api.get<{ data: Transaction[] }>('/v1/transactions', { accountId, limit: 10, sort: 'created_at', order: 'desc', - ...(historySearch ? { itemSearch: historySearch } : {}), + ...(itemSearch ? { itemSearch } : {}), }), enabled: !!accountId, }) @@ -74,7 +74,7 @@ export function POSCustomerDialog({ open, onOpenChange }: POSCustomerDialogProps const { data: searchData, isLoading } = useQuery(accountSearchOptions(search)) const accounts = searchData?.data ?? [] - const { data: historyData } = useQuery(customerHistoryOptions(showHistory ? accountId : null)) + const { data: historyData } = useQuery(customerHistoryOptions(showHistory ? accountId : null, historySearch || undefined)) const history = historyData?.data ?? [] function handleSelect(account: Account) { diff --git a/packages/backend/src/db/seeds/dev-seed.ts b/packages/backend/src/db/seeds/dev-seed.ts index 8a49f49..b5f15ee 100644 --- a/packages/backend/src/db/seeds/dev-seed.ts +++ b/packages/backend/src/db/seeds/dev-seed.ts @@ -44,7 +44,8 @@ async function seed() { if (!adminUser) { const bcrypt = await import('bcryptjs') const hashedPw = await bcrypt.hash(adminPassword, 10) - const [user] = await sql`INSERT INTO "user" (email, password_hash, first_name, last_name, role) VALUES ('admin@lunarfront.dev', ${hashedPw}, 'Admin', 'User', 'admin') RETURNING id` + const pinHash = await bcrypt.hash('1234', 10) + const [user] = await sql`INSERT INTO "user" (email, password_hash, first_name, last_name, role, employee_number, pin_hash) VALUES ('admin@lunarfront.dev', ${hashedPw}, 'Admin', 'User', 'admin', '1001', ${pinHash}) RETURNING id` const [adminRole] = await sql`SELECT id FROM role WHERE slug = 'admin' LIMIT 1` if (adminRole) { await sql`INSERT INTO user_role_assignment (user_id, role_id) VALUES (${user.id}, ${adminRole.id}) ON CONFLICT DO NOTHING` @@ -171,6 +172,64 @@ async function seed() { console.log(` Batch: Lincoln High School — 5 items`) } + // --- Sample POS Transactions tied to accounts --- + const [adminUsr] = await sql`SELECT id FROM "user" WHERE email = 'admin@lunarfront.dev'` + const [mainLoc] = await sql`SELECT id FROM location WHERE name = 'Main Store'` + if (adminUsr && mainLoc) { + const existingTxns = await sql`SELECT id FROM transaction WHERE account_id IS NOT NULL LIMIT 1` + if (existingTxns.length === 0) { + const salesData = [ + { account: 'Smith Family', items: [ + { desc: 'Violin Strings — Dominant 4/4 Set', qty: 1, price: '59.99', tax: '4.95' }, + { desc: 'Rosin — Pirastro Goldflex', qty: 1, price: '12.00', tax: '0.99' }, + ]}, + { account: 'Smith Family', items: [ + { desc: 'Shoulder Rest — Kun Original 4/4', qty: 1, price: '35.00', tax: '2.89' }, + ]}, + { account: 'Johnson Family', items: [ + { desc: 'Cello Strings — Jargar Superior Set', qty: 1, price: '89.99', tax: '7.42' }, + { desc: 'Bow Grip — Leather Cello/Bass', qty: 2, price: '6.00', tax: '0.99' }, + ]}, + { account: 'Garcia Workshop', items: [ + { desc: 'Bridge — Violin 4/4 Blank', qty: 3, price: '18.00', tax: '4.46' }, + { desc: 'Bow Hair — Mongolian White (hank)', qty: 2, price: '18.00', tax: '2.97' }, + { desc: 'Bow Tip Plate — Violin Ivory-Style', qty: 5, price: '7.00', tax: '2.89' }, + ]}, + { account: 'Emily Chen', items: [ + { desc: 'Violin Strings — Dominant 4/4 Set', qty: 1, price: '59.99', tax: '4.95' }, + { desc: 'Chin Rest — Guarneri Ebony 4/4', qty: 1, price: '28.00', tax: '2.31' }, + ]}, + ] + + for (const sale of salesData) { + const acctId = acctIds[sale.account] + if (!acctId) continue + const subtotal = sale.items.reduce((s, i) => s + parseFloat(i.price) * i.qty, 0) + const taxTotal = sale.items.reduce((s, i) => s + parseFloat(i.tax), 0) + const total = Math.round((subtotal + taxTotal) * 100) / 100 + const txnNum = `TXN-SEED-${String(Math.floor(1000 + Math.random() * 9000))}` + + const [txn] = await sql`INSERT INTO transaction ( + transaction_number, transaction_type, status, location_id, account_id, processed_by, + subtotal, discount_total, tax_total, total, payment_method, completed_at + ) VALUES ( + ${txnNum}, 'sale', 'completed', ${mainLoc.id}, ${acctId}, ${adminUsr.id}, + ${subtotal.toFixed(2)}, '0.00', ${taxTotal.toFixed(2)}, ${total.toFixed(2)}, 'card_present', NOW() + ) RETURNING id` + + for (const item of sale.items) { + const lineTotal = Math.round((parseFloat(item.price) * item.qty + parseFloat(item.tax)) * 100) / 100 + await sql`INSERT INTO transaction_line_item ( + transaction_id, description, qty, unit_price, tax_rate, tax_amount, line_total + ) VALUES ( + ${txn.id}, ${item.desc}, ${item.qty}, ${item.price}, '0.0825', ${item.tax}, ${lineTotal.toFixed(2)} + )` + } + console.log(` Transaction: ${txnNum} — ${sale.account} — $${total.toFixed(2)}`) + } + } + } + console.log('\nDev seed complete!') await sql.end() }