70 lines
3.3 KiB
TypeScript
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;
|
|
}
|