fix: customer history query, seed transactions tied to accounts

- Fix customerHistoryOptions closure bug (historySearch was inaccessible)
- Pass itemSearch as parameter instead of capturing from outer scope
- Seed 5 completed transactions tied to accounts (Smith, Johnson, Garcia, Chen)
- Seed admin user with employee number 1001 and PIN 1234

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
ryan
2026-04-04 21:15:53 +00:00
parent d21972212b
commit 0fd73015f7
2 changed files with 64 additions and 5 deletions

View File

@@ -46,15 +46,15 @@ function accountSearchOptions(search: string) {
}) })
} }
function customerHistoryOptions(accountId: string | null) { function customerHistoryOptions(accountId: string | null, itemSearch?: string) {
return queryOptions({ return queryOptions({
queryKey: ['pos', 'customer-history', accountId], queryKey: ['pos', 'customer-history', accountId, itemSearch ?? ''],
queryFn: () => api.get<{ data: Transaction[] }>('/v1/transactions', { queryFn: () => api.get<{ data: Transaction[] }>('/v1/transactions', {
accountId, accountId,
limit: 10, limit: 10,
sort: 'created_at', sort: 'created_at',
order: 'desc', order: 'desc',
...(historySearch ? { itemSearch: historySearch } : {}), ...(itemSearch ? { itemSearch } : {}),
}), }),
enabled: !!accountId, enabled: !!accountId,
}) })
@@ -74,7 +74,7 @@ export function POSCustomerDialog({ open, onOpenChange }: POSCustomerDialogProps
const { data: searchData, isLoading } = useQuery(accountSearchOptions(search)) const { data: searchData, isLoading } = useQuery(accountSearchOptions(search))
const accounts = searchData?.data ?? [] 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 ?? [] const history = historyData?.data ?? []
function handleSelect(account: Account) { function handleSelect(account: Account) {

View File

@@ -44,7 +44,8 @@ async function seed() {
if (!adminUser) { if (!adminUser) {
const bcrypt = await import('bcryptjs') const bcrypt = await import('bcryptjs')
const hashedPw = await bcrypt.hash(adminPassword, 10) 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` const [adminRole] = await sql`SELECT id FROM role WHERE slug = 'admin' LIMIT 1`
if (adminRole) { if (adminRole) {
await sql`INSERT INTO user_role_assignment (user_id, role_id) VALUES (${user.id}, ${adminRole.id}) ON CONFLICT DO NOTHING` 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`) 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!') console.log('\nDev seed complete!')
await sql.end() await sql.end()
} }