import { strict as assert } from 'node:assert'; import { test } from 'node:test'; import * as XLSX from 'xlsx'; import type { SaleTemplate } from '@/api/offlineSales'; import { buildTemplateWorkbook, parseSalesWorkbook, summarise, templateFilename, toBills, } from './salesSheet'; const TEMPLATE: SaleTemplate = { tenantid: 1135, locationid: 0, locations: [ { locationid: 1, locationname: 'R mart — RS Puram', productcount: 2 }, { locationid: 2, locationname: 'R mart — Peelamedu', productcount: 1 }, ], products: [ { tenantid: 1135, locationid: 1, locationname: 'R mart — RS Puram', productid: 9001, productname: 'Aachi Chilli Powder 100gm', currentstock: 24, price: 35, taxpercent: 5 }, { tenantid: 1135, locationid: 1, locationname: 'R mart — RS Puram', productid: 9004, productname: 'Amul Butter 500g', currentstock: 2, price: 265, taxpercent: 12 }, { tenantid: 1135, locationid: 2, locationname: 'R mart — Peelamedu', productid: 9007, productname: 'Nestle Milkmaid 400g', currentstock: 40, price: 160, taxpercent: 12 }, ], }; /** Round-trip helper: build the workbook, fill in cells, hand back a buffer. */ function filled(edits: Record>): ArrayBuffer { const workbook = buildTemplateWorkbook(TEMPLATE); const sheet = workbook.Sheets['Sales'] as XLSX.WorkSheet; const header = XLSX.utils.sheet_to_json(sheet, { header: 1 })[0] as string[]; const column = (name: string) => header.findIndex((cell) => String(cell).toLowerCase().startsWith(name)); for (const [row, values] of Object.entries(edits)) { for (const [name, value] of Object.entries(values)) { const address = XLSX.utils.encode_cell({ r: Number(row) - 1, c: column(name) }); sheet[address] = typeof value === 'number' ? { t: 'n', v: value } : { t: 's', v: value }; } } const out = XLSX.write(workbook, { type: 'array', bookType: 'xlsx' }) as ArrayBuffer; return out; } test('the workbook carries every product, ids filled in', () => { const workbook = buildTemplateWorkbook(TEMPLATE); assert.deepEqual(workbook.SheetNames, ['Sales', 'Store Info', 'Instructions']); const rows = XLSX.utils.sheet_to_json(workbook.Sheets['Sales'] as XLSX.WorkSheet, { header: 1, }); assert.equal(rows.length, 4, 'header plus three products'); assert.equal((rows[1] as unknown[])[3], 9001, 'productid is pre-filled'); assert.equal((rows[1] as unknown[])[6], '', 'qtysold is left empty, never defaulted to 0'); }); test('the filename says what the workbook spans', () => { assert.equal(templateFilename(TEMPLATE, '2026-08-25'), 'counter-sales-all-stores-2026-08-25.xlsx'); assert.equal( templateFilename({ ...TEMPLATE, locations: [TEMPLATE.locations[0] as never] }, '2026-08-25'), 'counter-sales-r-mart-rs-puram-2026-08-25.xlsx', ); }); test('rows with no quantity are skipped, not reported as problems', () => { const parsed = parseSalesWorkbook(filled({ 2: { qtysold: 3 } }), { tenantid: 1135 }); assert.deepEqual(parsed.fatal, []); assert.equal(parsed.rows.length, 1); assert.equal(parsed.skipped, 2); assert.equal(parsed.rows[0]?.productid, 9001); }); test('a file from another account is refused whole', () => { const parsed = parseSalesWorkbook(filled({ 2: { qtysold: 1 } }), { tenantid: 9999 }); assert.ok(parsed.fatal[0]?.includes('different account')); }); test('selling more than the shelf holds is an error on that row', () => { const parsed = parseSalesWorkbook(filled({ 3: { qtysold: 5 } }), { tenantid: 1135 }); assert.equal(parsed.rows.length, 1); assert.ok(parsed.rows[0]?.errors.some((problem) => problem.includes('only 2 in stock'))); }); test('a pinned upload refuses rows belonging to another branch', () => { const parsed = parseSalesWorkbook(filled({ 2: { qtysold: 1 }, 4: { qtysold: 1 } }), { tenantid: 1135, locationid: 1, }); const foreign = parsed.rows.find((row) => row.locationid === 2); assert.ok(foreign, 'the row is still parsed, so the operator can see it'); assert.ok(foreign?.errors.some((problem) => problem.includes('another store'))); const own = parsed.rows.find((row) => row.locationid === 1); assert.deepEqual(own?.errors, []); }); test('an unpriced line is a warning, not a blocker', () => { const parsed = parseSalesWorkbook(filled({ 2: { qtysold: 1, unitprice: 0 } }), { tenantid: 1135, }); assert.deepEqual(parsed.rows[0]?.errors, []); assert.ok(parsed.rows[0]?.warnings.some((note) => note.includes('no price'))); }); test('an unknown payment mode is caught before upload', () => { const parsed = parseSalesWorkbook(filled({ 2: { qtysold: 1, paymentmode: 'cheque' } }), { tenantid: 1135, }); assert.ok(parsed.rows[0]?.errors.some((problem) => problem.includes('not Cash, Card or UPI'))); }); test('the same bill number at two branches stays two bills', () => { const parsed = parseSalesWorkbook( filled({ 2: { qtysold: 1, billno: '1' }, 4: { qtysold: 1, billno: '1' } }), { tenantid: 1135 }, ); const bills = toBills(parsed.rows); assert.equal(bills.length, 2); assert.deepEqual( bills.map((bill) => bill.locationid).sort(), [1, 2], ); }); test('rows sharing a bill number become one bill with its lines', () => { const parsed = parseSalesWorkbook( filled({ 2: { qtysold: 2, billno: 'A-7' }, 3: { qtysold: 1, billno: 'A-7' } }), { tenantid: 1135 }, ); const bills = toBills(parsed.rows); assert.equal(bills.length, 1); assert.equal(bills[0]?.items.length, 2); assert.equal(bills[0]?.billno, 'A-7'); }); test('bill-level fields are taken from whichever row carries them', () => { const parsed = parseSalesWorkbook( filled({ 2: { qtysold: 1, billno: 'B-2' }, 3: { qtysold: 1, billno: 'B-2', paymentmode: 'UPI', customername: 'Kavitha' }, }), { tenantid: 1135 }, ); const bill = toBills(parsed.rows)[0]; assert.equal(bill?.paymentmode, 'UPI'); assert.equal(bill?.customername, 'Kavitha'); }); test('the preview total is unit price by quantity, less discount', () => { const parsed = parseSalesWorkbook( filled({ 2: { qtysold: 4, discountamount: 20 }, 4: { qtysold: 2 } }), { tenantid: 1135 }, ); const totals = summarise(parsed.rows); assert.equal(totals.lines, 2); assert.equal(totals.units, 6); assert.equal(totals.amount, 35 * 4 - 20 + 160 * 2); assert.equal(totals.stores, 2); assert.equal(totals.bills, 2, 'no bill number means one bill per branch'); }); test('a file with nothing filled in says so rather than uploading nothing', () => { const parsed = parseSalesWorkbook(filled({}), { tenantid: 1135 }); assert.ok(parsed.fatal[0]?.includes('No sold quantities')); });