import { ResetScope } from '@kooltap/shared'; import { Tx } from '../prisma/prisma.service'; import { ensureDefaultCustomers } from '../customers/default-customers'; export type ResetCounts = Partial>; /** How many rows each scope would remove (or, for stock, how many products it would set). */ export async function countReset(tx: Tx, venueId: string, scopes: ResetScope[]): Promise { const where = { venueId }; const counters: Record Promise> = { tabs: () => tx.tab.count({ where }), settlements: () => tx.settlement.count({ where }), tabEvents: () => tx.tabEvent.count({ where }), customers: () => tx.customer.count({ where }), stock: () => tx.product.count({ where: { venueId, stock: { not: null } } }), stockHistory: () => tx.stockMovement.count({ where }), auditLog: () => tx.auditLog.count({ where }), }; const counts: ResetCounts = {}; for (const scope of scopes) counts[scope] = await counters[scope](); return counts; } /** * Wipes [scopes] of a venue inside [tx], which must have locked it. Users, terminals and the catalog always stay: a * reset is for starting a new event or season with the same bar. * * - tabs: terminals drop them on their next sync; drinks a terminal still has to send bring a tab back * - tabEvents: also the record of which events were applied, so a terminal that sends an old event again would * apply it again: only wipe these when every terminal has synced * - customers: and the reminders sent to them * - stock: every tracked product gets [stockValue] as a count, recorded after the stock history is wiped so the * history explains it */ export async function resetVenue( tx: Tx, venueId: string, scopes: ResetScope[], options: { stockValue: number; userId: string; now: Date }, ): Promise { const where = { venueId }; const counts: ResetCounts = {}; const has = (scope: ResetScope) => scopes.includes(scope); if (has('tabs')) counts.tabs = (await tx.tab.deleteMany({ where })).count; if (has('settlements')) counts.settlements = (await tx.settlement.deleteMany({ where })).count; if (has('tabEvents')) counts.tabEvents = (await tx.tabEvent.deleteMany({ where })).count; if (has('customers')) { await tx.reminder.deleteMany({ where }); counts.customers = (await tx.customer.deleteMany({ where })).count; } // The Free Drinks tab is always there: added again, and opened on the next sync if (has('tabs') || has('customers')) await ensureDefaultCustomers(tx, venueId); if (has('stockHistory')) counts.stockHistory = (await tx.stockMovement.deleteMany({ where })).count; if (has('stock')) { const { now, userId, stockValue } = options; const tracked = await tx.product.findMany({ where: { venueId, stock: { not: null } }, select: { id: true } }); await tx.product.updateMany({ where: { venueId, stock: { not: null } }, data: { stock: stockValue, countedAt: now } }); await tx.stockMovement.createMany({ data: tracked.map((product) => ({ venueId, productId: product.id, type: 'count' as const, quantity: stockValue, stockAfter: stockValue, note: 'Stock reset', userId, createdAt: now, })), }); counts.stock = tracked.length; } if (has('auditLog')) counts.auditLog = (await tx.auditLog.deleteMany({ where })).count; return counts; }