diff --git a/backend/src/controllers/settings.ts b/backend/src/controllers/settings.ts index 7446ad9..14ac7d7 100644 --- a/backend/src/controllers/settings.ts +++ b/backend/src/controllers/settings.ts @@ -13,41 +13,38 @@ export const getSettings = () => { export const updateSettings = (data: Record) => { const db = getDatabase(); const results: Setting[] = []; + // Normalize legacy/frontend alias keys to canonical settings table keys. + const canonicalKey = (key: string) => + ({ + taxId: "companyTaxId", + phone: "companyPhone", + email: "companyEmail", + countryCode: "companyCountryCode", + })[key] ?? key; + const clearableKeys = new Set([ + "companyTaxId", + "companyPhone", + "companyEmail", + "companyCountryCode", + "companyCity", + "companyPostalCode", + "locale", + ]); for (const [key, raw] of Object.entries(data)) { + const targetKey = canonicalKey(key); // Treat explicit empty strings for certain keys as clearing the setting - const shouldClear = [ - "companyTaxId", - "taxId", // alias that may slip through - "companyPhone", - "phone", // alias - "companyEmail", - "email", // alias - "companyCountryCode", - "countryCode", // alias - "companyCity", - "companyPostalCode", - "locale", - ].includes(key) && String(raw).trim() === ""; + const shouldClear = clearableKeys.has(targetKey) && String(raw).trim() === ""; - if (shouldClear) { - // delete the setting row if present - db.query("DELETE FROM settings WHERE key = ?", [ - key === "taxId" ? "companyTaxId" : key, - ]); - results.push({ key: key === "taxId" ? "companyTaxId" : key, value: "" }); - continue; - } - - const value = String(raw); + const value = shouldClear ? "" : String(raw); // Upsert the setting - const existing = db.query("SELECT * FROM settings WHERE key = ?", [key]); + const existing = db.query("SELECT * FROM settings WHERE key = ?", [targetKey]); if (existing.length > 0) { - db.query("UPDATE settings SET value = ? WHERE key = ?", [value, key]); + db.query("UPDATE settings SET value = ? WHERE key = ?", [value, targetKey]); } else { - db.query("INSERT INTO settings (key, value) VALUES (?, ?)", [key, value]); + db.query("INSERT INTO settings (key, value) VALUES (?, ?)", [targetKey, value]); } - results.push({ key, value }); + results.push({ key: targetKey, value }); } return results; diff --git a/frontend/src/lib/components/InvoiceEditor.svelte b/frontend/src/lib/components/InvoiceEditor.svelte index 6f122c7..0ccf5d5 100644 --- a/frontend/src/lib/components/InvoiceEditor.svelte +++ b/frontend/src/lib/components/InvoiceEditor.svelte @@ -13,6 +13,23 @@ let saving = $state(false); let error = $state(""); + function createItemId() { + if (typeof globalThis.crypto?.randomUUID === "function") { + return globalThis.crypto.randomUUID(); + } + + if (typeof globalThis.crypto?.getRandomValues === "function") { + const bytes = new Uint8Array(16); + globalThis.crypto.getRandomValues(bytes); + bytes[6] = (bytes[6] & 0x0f) | 0x40; + bytes[8] = (bytes[8] & 0x3f) | 0x80; + const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")); + return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10, 16).join("")}`; + } + + return `${Date.now()}-${Math.random().toString(16).slice(2)}`; + } + let form = $state({ customerId: initInvoice?.customerId || "", invoiceNumber: initInvoice?.invoiceNumber ?? initNextInvoiceNumber, @@ -32,13 +49,13 @@ initInvoice?.items?.length ? initInvoice.items.map((i: any) => ({ ...i, - id: crypto.randomUUID(), + id: createItemId(), unit: i.unit || "", productId: i.productId || "", })) : [ { - id: crypto.randomUUID(), + id: createItemId(), productId: "", description: "", quantity: 1, @@ -56,7 +73,7 @@ function addItem() { items.push({ - id: crypto.randomUUID(), + id: createItemId(), productId: "", description: "", quantity: 1,