Pohoda XML — Postgres DATE → Date.toString() místo ISO YYYY-MM-DD
import { Aside } from ‘@astrojs/starlight/components’;
Příznak
Sekce “Příznak”Pohoda mServer odmítne XML s chybou jako Datum nemá platný formát nebo Invalid date format. V XML faktuře:
<inv:date>Sun Jun 07 2026 00:00:00 GMT+0200 (Central European Summer Time)</inv:date><inv:dateTax>Sun Jun 07 2026 00:00:00 GMT+0200 (Central European Summer Time)</inv:dateTax><inv:dateDue>Tue Jul 07 2026 00:00:00 GMT+0200 (Central European Summer Time)</inv:dateDue>Místo očekávaného:
<inv:date>2026-06-07</inv:date><inv:dateTax>2026-06-07</inv:dateTax><inv:dateDue>2026-07-07</inv:dateDue>Příčina
Sekce “Příčina”Postgres sloupec typu DATE se přes node-postgres (pg) deserializuje jako JS Date object, ne jako string. Když pak takovou hodnotu vložíš do template literálu:
const xml = `<inv:date>${data.issueDate}</inv:date>`;JavaScript implicitně zavolá data.issueDate.toString(), což vrací anglický locale-aware formát "Sun Jun 07 2026 00:00:00 GMT+0200 (Central European Summer Time)". Pohoda XSD očekává ISO formát YYYY-MM-DD.
Tip: TypeScript to nezachytí, protože v původním kódu byl typový cast as string:
issueDate: invoice.issue_date as string, // ← lež, je to Date objectCast existuje proto, že schema načítá row jako Record<string, unknown> a TS přesný typ neví. Cast je tichá lež.
Oprava
Sekce “Oprava”Normalizuj hodnotu před vrácením z data layeru:
const toIso = (v: unknown): string | null => { if (!v) return null; if (v instanceof Date) return v.toISOString().slice(0, 10); return String(v).slice(0, 10);};
return { payload: { issueDate: toIso(invoice.issue_date) || "", taxDate: toIso(invoice.tax_date), dueDate: toIso(invoice.due_date) || "", // ... }};Alternativně: nakonfiguruj pg parser na DATE columns aby vracel string:
import pg from "pg";// type OID 1082 = DATEpg.types.setTypeParser(1082, (v: string) => v);Tahle globální změna ovlivní VŠECHNY DATE columns v celé app — bezpečnější je lokální konverze v té vrstvě, která stringy potřebuje (XML/JSON export).
Jak na to přijít
Sekce “Jak na to přijít”E2E test:
const xml = await fetch("/api/crm-invoices/123/pohoda-xml").then(r => r.text());expect(xml).not.toContain("GMT+"); // žádný Date.toString() artefaktexpect(xml).toMatch(/<inv:date>\d{4}-\d{2}-\d{2}<\/inv:date>/); // ISO formátBez testů to nepoznáš — Postgres vrací data klidně bez výjimky, XML se vygeneruje, jen ho Pohoda odmítne, někdy s vágní chybovou hláškou.
Související
Sekce “Související”- Stejný problém má
QRCode.toBuffer({ dueDate })v SPAYD generátoru — viz QR PlatbadueDate.replace is not a function(TBD) Intl.DateTimeFormatprodukuje pohledné stringy, aletoISOString().slice(0, 10)je rychlejší a deterministický pro YYYY-MM-DD