From db3500032e6799e75077c9d9b5d3d007cbcc1211 Mon Sep 17 00:00:00 2001 From: Bram Verhulst Date: Mon, 26 Oct 2026 21:58:23 +0100 Subject: [PATCH] fix: import exell for tabs --- .../src/planned-tabs/planned-tabs.service.spec.ts | 15 +++++++++++++-- apps/api/src/planned-tabs/planned-tabs.service.ts | 10 ++++++++-- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/apps/api/src/planned-tabs/planned-tabs.service.spec.ts b/apps/api/src/planned-tabs/planned-tabs.service.spec.ts index ef96c0a..c0aaa05 100644 --- a/apps/api/src/planned-tabs/planned-tabs.service.spec.ts +++ b/apps/api/src/planned-tabs/planned-tabs.service.spec.ts @@ -1,9 +1,9 @@ import { PlannedTabsService } from './planned-tabs.service'; -type Row = { id: string; venueId: string; customerName: string; email: string | null; position: number }; +type Row = { id: string; venueId: string; customerName: string; email: string | null; position: number; deliveredAt?: Date | null }; function createService(initial: Omit[]) { - let rows: Row[] = initial.map((r) => ({ ...r, venueId: 'v1' })); + let rows: Row[] = initial.map((r) => ({ deliveredAt: null, ...r, venueId: 'v1' })); const tx = { plannedTab: { findMany: jest.fn(async () => [...rows].sort((a, b) => a.position - b.position)), @@ -60,6 +60,17 @@ describe('PlannedTabsService.import', () => { expect(rows()[0].email).toBe('piet@example.com'); }); + it('queues delivered customers again so a deleted tab is reopened', async () => { + const { service, rows } = createService([ + { id: 'a', customerName: 'Jan', email: 'jan@example.com', position: 0, deliveredAt: new Date() }, + ]); + + const result = await service.import('v1', [{ customerName: 'jan' }], 'merge'); + + expect(result).toEqual({ created: 0, updated: 1, unchanged: 0, removed: 0 }); + expect(rows()[0]).toMatchObject({ deliveredAt: null, email: 'jan@example.com' }); + }); + it('removes names missing from the import only in replace mode', async () => { const initial = [ { id: 'a', customerName: 'Jan', email: null, position: 0 }, diff --git a/apps/api/src/planned-tabs/planned-tabs.service.ts b/apps/api/src/planned-tabs/planned-tabs.service.ts index 84f6147..d4ad9ea 100644 --- a/apps/api/src/planned-tabs/planned-tabs.service.ts +++ b/apps/api/src/planned-tabs/planned-tabs.service.ts @@ -84,6 +84,7 @@ export class PlannedTabsService { * Names match case-insensitively and existing spelling is kept, because the terminal matches * planned names to tabs exactly and a respelled name would open a second tab. * A missing email in the import never clears an existing one. + * Customers already delivered are queued again, so a tab deleted since is reopened on the next sync. */ async import(venueId: string, entries: { customerName: string; email?: string }[], mode: 'merge' | 'replace') { const incoming = new Map(); @@ -112,8 +113,13 @@ export class PlannedTabsService { data: { id: uuidv4(), venueId, customerName: entry.customerName, customerNameKey: key, email: entry.email ?? null, position: nextPosition++ }, }); created++; - } else if (entry.email && entry.email !== current.email) { - await tx.plannedTab.update({ where: { id: current.id }, data: { email: entry.email } }); + } else if (current.deliveredAt || (entry.email && entry.email !== current.email)) { + // Re-importing a delivered customer queues them again: their tab may have been deleted since, and the + // next sync skips anyone who still has an open tab + await tx.plannedTab.update({ + where: { id: current.id }, + data: { deliveredAt: null, ...(entry.email && entry.email !== current.email ? { email: entry.email } : {}) }, + }); updated++; } else { unchanged++;