fix(peppol): read Qvalia's prefixed UBL-JSON keys, add incoming probe (#1786)

Qvalia's UBL-JSON keeps namespace prefixes (cac:AccountingSupplierParty,
cbc:EndpointID) with attributes under `$` (verified live 2026-08-21 on the
inbound test invoice Joanna sent to 0007:5595386219), not the unprefixed
OASIS form the 409-recovery extractor assumed. Accept both. The probe gains
`incoming [integrationId]` to list inbound statuses or print one inbound
invoice as XML without marking it read.

Refs #546


Claude-Session: https://claude.ai/code/session_01TqFpxeWqbpR7bcwUJLRERQ

Co-authored-by: Jakob Wennberg <311770904+jakobwennberg-oss@users.noreply.github.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jakob Wennberg
2026-08-21 16:09:40 +02:00
committed by GitHub
co-authored by Jakob Wennberg Claude Fable 5
parent 72c81c21e7
commit 316189675a
3 changed files with 54 additions and 9 deletions
+19
View File
@@ -16,6 +16,9 @@
* Lists the Peppol identifiers registered on the partner account.
* npx tsx scripts/peppol/qvalia-probe.ts outgoing
* Lists the three latest outgoing invoice statuses (read-only).
* npx tsx scripts/peppol/qvalia-probe.ts incoming [integrationId]
* Lists the latest incoming invoice statuses, or prints one incoming
* invoice as XML (read-only: uses the non-marking endpoint).
* npx tsx scripts/peppol/qvalia-probe.ts send path/to/invoice.xml
* Submits a BIS Billing 3 XML through the adapter. Sandbox only: the
* script refuses a production base URL.
@@ -110,6 +113,22 @@ async function main(): Promise<void> {
)
return
}
case 'incoming': {
if (argument) {
const response = await fetch(
`${config!.baseUrl}/partner/${partner}/transaction/${account}/invoices/incoming?integrationId=${encodeURIComponent(argument)}&includeRead=true&limit=1`,
{ headers: { Authorization: headerFor(config!.authScheme), accept: 'application/xml' } },
)
console.log(`HTTP ${response.status}`)
console.log((await response.text()).slice(0, 6000))
return
}
await show(
'GET /partner/{p}/transaction/{a}/invoices/incoming/status',
await rawGet(`/partner/${partner}/transaction/${account}/invoices/incoming/status?includeRead=true&limit=5`),
)
return
}
case 'webhook': {
await show('GET /partner/{p}/webhook/configure', await rawGet(`/partner/${partner}/webhook/configure`))
return