Přeskočit na obsah

Pohoda XML — Postgres DATE → Date.toString() místo ISO YYYY-MM-DD

import { Aside } from ‘@astrojs/starlight/components’;

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>

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 object

Cast existuje proto, že schema načítá row jako Record<string, unknown> a TS přesný typ neví. Cast je tichá lež.

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 = DATE
pg.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).

E2E test:

const xml = await fetch("/api/crm-invoices/123/pohoda-xml").then(r => r.text());
expect(xml).not.toContain("GMT+"); // žádný Date.toString() artefakt
expect(xml).toMatch(/<inv:date>\d{4}-\d{2}-\d{2}<\/inv:date>/); // ISO formát

Bez 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.

  • Stejný problém má QRCode.toBuffer({ dueDate }) v SPAYD generátoru — viz QR Platba dueDate.replace is not a function (TBD)
  • Intl.DateTimeFormat produkuje pohledné stringy, ale toISOString().slice(0, 10) je rychlejší a deterministický pro YYYY-MM-DD
Přidal aiarchitekt.cz · 7. 6. 2026 18:00
Provozuje aiarchitekt.cz