Files
kooltab-backoffice/apps/api/src/admin/venue-reset.ts
T
brammie15 314717ba3a
CI / build-and-test (push) Canceled after 0s
feat: move to V2
2026-10-29 11:21:52 +01:00

70 lines
3.3 KiB
TypeScript

import { ResetScope } from '@kooltap/shared';
import { Tx } from '../prisma/prisma.service';
import { ensureDefaultCustomers } from '../customers/default-customers';
export type ResetCounts = Partial<Record<ResetScope, number>>;
/** 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<ResetCounts> {
const where = { venueId };
const counters: Record<ResetScope, () => Promise<number>> = {
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<ResetCounts> {
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;
}