diff --git a/frontend/src/lib/printer/unicode.ts b/frontend/src/lib/printer/unicode.ts index 6840c8bf..cabe7619 100644 --- a/frontend/src/lib/printer/unicode.ts +++ b/frontend/src/lib/printer/unicode.ts @@ -34,6 +34,7 @@ export const CURRENCY_ASCII_MAP: Record = { '₵': 'Gh', '₡': 'Cr', '₲': 'Pg', + 'Kč': 'Kc', // Czech Koruna }; export function normalizeCurrencyToAscii(text: string): string { @@ -46,8 +47,10 @@ export function normalizeCurrencyToAscii(text: string): string { /** * Pads a resolved currency symbol to a fixed 2-character slot so amount - * columns line up whether the symbol is 1 or 2 characters wide. + * columns line up whether the symbol is 1 or 2 characters wide, and keeps + * the Czech receipt currency ASCII even when Unicode printing is enabled. */ export function padCurrencyPrefix(prefix: string): string { - return prefix.length >= 2 ? prefix : ' '.repeat(2 - prefix.length) + prefix; + const normalized = prefix.replaceAll('Kč', 'Kc'); + return normalized.length >= 2 ? normalized : ' '.repeat(2 - normalized.length) + normalized; } diff --git a/main/routes/orders.ts b/main/routes/orders.ts index ff9ca27e..d55bc367 100644 --- a/main/routes/orders.ts +++ b/main/routes/orders.ts @@ -1471,4 +1471,97 @@ router.patch('/:id/items/:itemId/discount', requireRole('owner', 'manager'), (re } }); + +router.delete('/:id', requireRole('owner', 'manager'), (req: Request, res: Response) => { + try { + const orderId = Number(req.params.id); + + if (!Number.isInteger(orderId) || orderId <= 0) { + return res.status(400).json({ error: 'Invalid order id' }); + } + + const db = getDatabase(); + + const order = db.prepare( + 'SELECT id, order_number FROM orders WHERE id = ?' + ).get(orderId) as { id: number; order_number: string } | undefined; + + if (!order) { + return res.status(404).json({ error: 'Order not found' }); + } + + withTxn(() => { + // Najdi ├║─Źet nav├ízan├Ż na objedn├ívku. + const bills = db.prepare( + 'SELECT id FROM bills WHERE order_id = ?' + ).all(orderId) as { id: number }[]; + + const billIds = bills.map(bill => bill.id); + + // Sma┼ż z├íznamy nav├ízan├ę na bill, kter├ę nemaj├ş ON DELETE CASCADE. + if (billIds.length > 0) { + const placeholders = billIds.map(() => '?').join(','); + + db.prepare( + `DELETE FROM print_logs + WHERE bill_id IN (${placeholders})` + ).run(...billIds); + + db.prepare( + `DELETE FROM whatsapp_messages + WHERE bill_id IN (${placeholders})` + ).run(...billIds); + + // bill_items maj├ş ON DELETE CASCADE. + db.prepare( + `DELETE FROM bills + WHERE id IN (${placeholders})` + ).run(...billIds); + } + + // Sma┼ż addon z├íznamy objedn├ívkov├Żch polo┼żek. + const items = db.prepare( + 'SELECT id FROM order_items WHERE order_id = ?' + ).all(orderId) as { id: number }[]; + + const itemIds = items.map(item => item.id); + + if (itemIds.length > 0) { + const placeholders = itemIds.map(() => '?').join(','); + + db.prepare( + `DELETE FROM order_item_addons + WHERE order_item_id IN (${placeholders})` + ).run(...itemIds); + + db.prepare( + `DELETE FROM order_items + WHERE id IN (${placeholders})` + ).run(...itemIds); + } + + // Nakonec samotn├í objedn├ívka. + db.prepare( + 'DELETE FROM orders WHERE id = ?' + ).run(orderId); + }); + + notifyOrderUpdated(); + + return res.json({ + ok: true, + deleted: true, + order_number: order.order_number, + }); + } catch (error: any) { + console.error('[API] Delete order error:', error); + + return res.status(error.statusCode || 500).json({ + error: error.statusCode + ? error.message + : 'Failed to delete order', + }); + } +}); + export const orderRoutes = router;