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:
@@ -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) {
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user