Files
accounted/lib/api/v1/load-routes.ts
T
Jakob WennbergandClaude Opus 4.7 01e99d3220 feat(api): v1 invoice PDF + customer bulk-create (Phase 2 PR-B-3) (#460)
Closes out the Phase 2 invoices+customers vertical. After this PR, every
write/read the dashboard does on these two resources is reachable via the
public API.

GET /api/v1/companies/{companyId}/invoices/{id}/pdf
  Read-only application/pdf endpoint. Mirrors the dashboard's internal
  /api/invoices/[id]/pdf so a downloaded PDF is byte-equivalent across
  surfaces. Drafts render with the "faktura-utkast-<id-slice>.pdf"
  filename (preview before send is a legitimate workflow); sent invoices
  use "faktura-<number>.pdf"; credit notes use "kreditfaktura-<number>.pdf"
  and embed the original invoice's löpnummer per ML 17 kap 22–23§
  back-reference; proforma + delivery notes get their own prefixes.

  Error codes: INVOICE_PDF_RENDER_FAILED (500, new),
  INVOICE_SEND_COMPANY_SETTINGS_MISSING (404, reused — same condition,
  same remediation).

POST /api/v1/companies/{companyId}/customers/bulk-create
  Mirrors /invoices/bulk-create exactly: same `{ results, summary }`
  shape, same all_or_nothing: true → 501 NOT_IMPLEMENTED contract, same
  50-item cap, same sequential processing. Per-item rollback isn't
  needed (customer insert is a single row), but per-item 23505 →
  CUSTOMER_DUPLICATE_ORG_NUMBER failure surfaces in the results array
  without echoing org_number (GDPR Art.5(1)(c): for sole traders
  org_number IS the personnummer). VIES validation runs per item,
  best-effort — a timeout leaves vat_number_validated=false but does
  NOT fail the item.

Registry: extended EndpointDefinition.response with an optional
  `contentType` field so the OpenAPI generator can emit
  `format: binary` schemas for non-JSON responses. The PDF endpoint is
  the first consumer; future binary endpoints (SIE export, ICS feeds)
  use the same hook.

Scope catalogue: added GET .../pdf → invoices:read,
  POST .../customers/bulk-create → customers:write.

Tests: 14 new integration cases (7 for PDF: sent / draft / credit-note
filename, 404, render-error 500, non-UUID 400, scope rejection; 7 for
customer bulk-create: happy path, dup-org error masking, max-50 cap,
all_or_nothing 501, dry-run preview, empty array, scope rejection).
Suite green: 3232 passing. Build + lint clean.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:53:51 +02:00

34 lines
1.5 KiB
TypeScript

/**
* Side-effect import that ensures every v1 route module's top-level
* `registerEndpoint()` call has been executed before the OpenAPI generator
* reads the registry.
*
* Why this exists: route files register themselves at module load time. The
* OpenAPI endpoint runs in its own module which would otherwise not pull in
* the other route files. Importing them here as side-effects populates the
* shared `ENDPOINTS` map.
*
* When a new v1 route is added, append a `import '...'` line.
*/
// Phase 1 surface.
import '@/app/api/v1/health/route'
import '@/app/api/v1/companies/route'
// Phase 2 PR-A — invoice + customer reads.
import '@/app/api/v1/companies/[companyId]/invoices/route'
import '@/app/api/v1/companies/[companyId]/invoices/[id]/route'
import '@/app/api/v1/companies/[companyId]/customers/route'
import '@/app/api/v1/companies/[companyId]/customers/[id]/route'
// Phase 2 PR-B-2b — invoice action verbs.
import '@/app/api/v1/companies/[companyId]/invoices/[id]/mark-sent/route'
import '@/app/api/v1/companies/[companyId]/invoices/[id]/mark-paid/route'
import '@/app/api/v1/companies/[companyId]/invoices/[id]/credit/route'
import '@/app/api/v1/companies/[companyId]/invoices/[id]/send/route'
import '@/app/api/v1/companies/[companyId]/invoices/bulk-create/route'
// Phase 2 PR-B-3 — invoice PDF + customer bulk-create.
import '@/app/api/v1/companies/[companyId]/invoices/[id]/pdf/route'
import '@/app/api/v1/companies/[companyId]/customers/bulk-create/route'
export {}