Repository navigation
PDFCLOUD-6571 Add ZUGFeRD PDF creation - #57
Conversation
✅ Deploy Preview for pdfrest-python ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
- Add typed rendering options for invoice appearance and localization. - Validate uploaded resources, color channels, fonts, and regeneration. - Serialize the public input shape into the pdfRest wire contract. Assisted-by: Codex
- Add synchronous and asynchronous helpers for uploaded invoice XML. - Support optional PDFs, logos, rendering controls, and request options. - Cover serialization, validation, response mapping, and both transports. Assisted-by: Codex
- Exercise every closed rendering option through both client transports. - Verify regenerated PDFs, response metadata, and server-side rejection. - Add a deterministic minimum-profile invoice fixture. Assisted-by: Codex
- Add a standalone typed example with a deterministic invoice fixture. - Verify the generated PDF and print useful response metadata. - Link the helper from the API guide and example inventories. Assisted-by: Codex
10508bb to
7d951b6
Compare
There was a problem hiding this comment.
Requesting changes on 7d951b6291e4dad1395286f1ea5b20bfa2833f89. Please split XML generation from supplied-PDF conversion before publishing the API. Two helpers are preferred:
convert_invoice_xml_to_zugferd_pdf(invoice, ...)convert_pdf_to_zugferd_pdf(pdf, *, invoice, allow_regeneration=False, ...)
The supplied-PDF helper should reject logo/render options when regeneration permission is disabled, and explain that those options apply only if replacement actually occurs. A three-helper alternative with preservation-only conversion plus convert_pdf_to_zugferd_pdf_allowing_regeneration is acceptable. The existing naming/design thread now records the complete proposal, shared request/payload requirements, and the still-open convention discussion. This is the requested design direction; the guidelines' coherent-contract exception supports combining preservation and permitted replacement in the required-PDF helper.
The other findings are covered without duplicate threads:
- Null-language exception and accepted aliases: the existing language-validation thread now includes the reproduced sync/async
label_language=NoneAttributeError, the required validation-error behavior, and the previously reported alias/canonicalization fix. - Unit validation/MIME/boundary coverage: the existing coverage thread now specifies the missing paired resource guards, supported MIME variants, optional serialization, response assertions and request counts.
- Live contract coverage: that same coverage thread now includes isolated options, invalid catalogs/bounds, observable font/render assertions, preservation, mismatch rejection and permitted replacement. Existing positive live literals pass; successful conversion alone does not establish every rendering behavior.
- Prefer built-in string constraints: one new, lower-priority cleanup thread requests constrained internal string aliases and small semantic validators, including the verified Pydantic union-ordering caveat. Existing color/resource adaptation remains appropriate custom logic.
The earlier required minor-version bump and shared published-font catalog comments remain applicable and were left unchanged. Apply the final API consistently to both transports, examples, tests and documentation. Preserve compatibility for any names already released.
Validation during this review on the unchanged HEAD: the non-live Nox matrix passed on Python 3.10–3.14 with 3,231 passes and 10 expected skips per interpreter; all 12 focused ZUGFeRD live tests passed on Python 3.11; the new example passed on Python 3.10; all four client-class function gates passed across the five interpreters. Read-only Ruff formatting, changed-file Ruff lint, basedpyright, strict docs and diff whitespace checks passed. Public MockTransport probes reproduced the null-language exception through both clients. Repository-wide Ruff encounters 39 errors in an existing untracked local notebook outside this PR.
The full service-wide live suite, full local example matrix and mutating local pre-commit hooks were not repeated; the new live module/example and relevant read-only checks were exercised, and recorded PR CI on this HEAD is successful for the full test/example matrices, live job and pre-commit. This update edits the existing changes-requested review and relevant threads rather than submitting another overlapping review. No source files were edited.
- Separate XML rendering from supplied-PDF preservation and require explicit permission for replacement-only options. - Canonicalize supported label-language aliases and share the readable font catalog across conversion APIs. - Cover sync and async serialization, validation, response handling, and request customization. Assisted-by: Codex
- Exercise every closed render-option value through sync and async clients. - Verify visual preservation, observable rendering changes, mismatch rejection, and explicitly permitted replacement. - Send invalid option boundaries to the service and assert response metadata and warnings. Assisted-by: Codex
- Rename the XML generation example: create_zugferd_pdf/ -> convert_invoice_xml_to_zugferd_pdf/ - Add a supplied-PDF example that makes regeneration permission explicit. - Explain preservation and replacement behavior in the API guidance. Assisted-by: Codex
- Record the new public ZUGFeRD conversion helpers as a minor release. - Keep the locked editable package metadata synchronized. Assisted-by: Codex
|
Reopening immediately to trigger GitHub Actions for the updated head commit; the review fixes are pushed and all review threads are resolved. |
- Incorporate the Apache-2.0 licensing and contributor documentation from the 1.4.1 release preparation. - Retain version 1.5.0 for the new public conversion workflows and keep lock metadata synchronized. Assisted-by: Codex
- Pair synchronous and asynchronous checks for render options and bounds. - Assert request routes, serialized payloads, and concrete responses. - Cover open font names and resource cardinality failures. Assisted-by: Codex
- Derive live literal catalogs from the public SDK types. - Verify requested fonts, isolated visual controls, and replacement output. - Add a licensed two-item invoice fixture for alternate-row evidence. Assisted-by: Codex
- Extend compatibility checks to runtime and static type contracts. - Require unchanged unrelated tests and self-contained literal docs. - Add explicit base comparisons and rendered-reference inspection. Assisted-by: Codex
|
Addressed the compatibility and API-skill review on
Validation included the unchanged structured-document tests, the full Python 3.10-3.14 non-live Nox matrix, basedpyright, Ruff format/check, strict MkDocs, client-class coverage gates, package build, and the focused ZUGFeRD live module. |
Assisted-by: Codex
datalogics-kam
left a comment
There was a problem hiding this comment.
Changes requested on f6689bc42022d52b773a688a7f9c703d95a35039. Three remaining coverage findings are attached to the current changed code in this new review:
- P2 — Live rendering controls: cover the remaining four color controls and date format with isolated sync/async result assertions, plus a rejected language case.
- P2 — Supplied-PDF unit parity: add sync regeneration and async preservation success assertions. Forcing sync regeneration off still passes all 462 focused unit tests.
- P3 — Interior precision boundaries: add legal precision values 1 and 3. Rejecting precision 1 still passes all 462 focused unit tests.
These are outstanding regression-protection gaps. No production correctness or CI regression was reproduced. The earlier compatibility, restored unrelated tests, self-contained literal documentation, API-skill corrections, helper split, regeneration guard, language validation, and version bump are verified addressed. The fresh inline threads in this review identify the remaining work; the older coverage discussion is historical context.
Validation of this committed head: full non-live Nox matrix passed on Python 3.10–3.14 with 3,432 passed and 10 expected skips each; all four client-class function coverage gates passed across the matrix; 68 focused live cases passed on Python 3.11; both changed examples passed on Python 3.10. Ruff formatting/lint, basedpyright, runtime/static public-contract comparisons, strict MkDocs and rendered literal-list inspection, wheel/sdist builds, and whitespace checks passed. The two passing mutation probes expose test gaps; both targets were restored byte-for-byte.
The complete service-wide live suite, full local example matrix, and mutating local pre-commit hooks were not repeated. The focused endpoint/examples ran and exact-head CI is green. No project source or skill files were changed or pushed.
- Pair preservation and regeneration success paths across both clients. - Exercise precision boundary interiors through payload and client tests. - Verify remaining render controls and language rejection with live calls. Assisted-by: Codex
datalogics-kam
left a comment
There was a problem hiding this comment.
Changes requested on f8efb8fabd942cabafd33ce3a1ee037c75fec63f for the remaining live enum-behavior assertion gap attached below.
The three findings from the preceding requested review are addressed. This additional policy check found that the live enum tests verify accepted requests and PDF metadata without establishing the requested logo position or localized label/currency/numeric/precision result. A focused live mutation that removed those requested settings still passed all 10 selected sync/async cases.
My earlier recommendation to approve was incorrect under this repository's required review policy. I had classified the missing semantic assertions as an optional follow-up and marked the corresponding rules pass using evidence for different rendering controls. TESTING_GUIDELINES.md:232–235 explicitly requires validating that the server honors these optional arguments, and AGENTS.md:241–251 makes server-observable live coverage a release gate. This new review makes the remaining work visible; it does not revise older review comments.
Required change: add paired semantic output assertions that distinguish each requested enum setting from defaults, and show that ignoring those settings causes the relevant cases to fail. The inline finding lists the affected options, exact evidence and policy citations.
Validation on this same head remains: 3,440 non-live tests passed with 10 expected skips on each Python 3.10–3.14 interpreter; all four client-class coverage gates passed; 84 unmodified focused live cases passed; Ruff/type checks and exact-head CI are green. Additional hermetic and differential checks passed (44 and 1,613 respectively). Those successful checks do not close this policy gap: the new deliberate enum-dropping mutation also passed 10 live cases. No production defect was reproduced, and no tracked source file was edited or pushed.
Withdrawing this finding: the paired SDK MockTransport tests already assert these serialized options. The live-only mutation did not demonstrate missing SDK interface coverage; verifying pdfRest renderer internals is outside this SDK PR.
datalogics-kam
left a comment
There was a problem hiding this comment.
Approved on f8efb8fabd942cabafd33ce3a1ee037c75fec63f.
The requested SDK changes are addressed: focused XML and supplied-PDF helpers, explicit regeneration permission, language normalization and validation surfaces, preserved existing public type contracts and self-contained literal documentation, and paired sync/async request and boundary coverage. The final update completes supplied-PDF success parity and interior precision cases. All review threads are resolved.
This approval covers the SDK interface: input validation, normalization, exact request serialization, response/error handling, both transports, and live interoperability. Existing MockTransport tests detect dropped render-option values; additional assertions about pdfRest's rendering implementation are not required to approve this SDK change. No remaining actionable SDK finding was identified.
Validation on this head: the full non-live Python 3.10–3.14 Nox matrix passed with 3,440 tests passed and 10 expected skips per interpreter; all four client-class function coverage gates passed across the matrix; 84 focused live cases passed on Python 3.11; Ruff formatting/lint and basedpyright passed. Additional interface and compatibility probes passed, and deliberate SDK option-dropping mutations were detected by the existing unit tests.
Exact-head CI is green, including the complete test/example matrices, live job, docs, and pre-commit. The full service-wide live suite and full example matrix were not repeated locally; focused live validation and the exact-head CI provide that coverage boundary. No source changes were made during review.
PDFCLOUD-6571
Why this change
The Python SDK does not expose pdfRest's ZUGFeRD PDF operation, so callers cannot use the typed clients to turn CII invoice XML into a validated ZUGFeRD or Factur-X PDF/A-3 invoice or safely combine that XML with an existing visual PDF. This adds both workflows to the synchronous and asynchronous clients while making the replacement risk explicit.
What changed (high level)
The public API is split by caller intent.
convert_invoice_xml_to_zugferd_pdfalways renders a PDF from uploaded CII XML and accepts appearance controls.convert_pdf_to_zugferd_pdfstarts from an uploaded PDF, preserves it by default, and requiresallow_regeneration=Truebefore replacement-only logo or rendering options are accepted.Dedicated Pydantic payload models own resource validation and wire serialization. ZUGFeRD label languages use constrained strings plus canonicalization for English, German, and French aliases, and the structured-document and ZUGFeRD APIs now share one readable-font catalog while retaining their existing public aliases.
The package version advances to 1.5.0. Runnable examples and API guidance cover each workflow separately.
Behavior changes
Callers choose XML rendering or supplied-PDF preservation before making the request, so invalid option combinations are rejected locally instead of being exposed through one mode-dependent signature. The supplied-PDF workflow rejects logo and rendering controls unless regeneration is explicitly permitted. If regeneration is not permitted and visual consistency cannot be confirmed, the service error is surfaced.
Supported two-letter, three-letter, and regional label-language forms are canonicalized to
en,de, orfr; explicitNone, unsupported languages, malformed tags, unsupported symbol fonts, invalid MIME types, multiple single-resource inputs, malformed colors, and unsafe output prefixes fail as Pydantic validation errors before transport.These changes replace the provisional
create_zugferd_pdfhelper introduced only within this unmerged PR; no released SDK API is removed.Validation
The complete non-live suite passed on Python 3.10 through 3.14 with 3,308 tests passed, 10 expected skips, and 96% overall coverage on each interpreter. The client class-function coverage gate passed at its 90% threshold.
The endpoint-specific live suite exercised all closed render-option values, server-side invalid boundaries, visual rendering changes, matching-PDF preservation, mismatch rejection, explicitly permitted replacement, response metadata, and warnings through both sync and async clients. All 56 live cases were verified across the full run and focused correction rerun. Both standalone examples completed against the live service.
Ruff formatting and linting, basedpyright, the complete pre-commit suite, strict MkDocs build, and wheel/sdist build all passed. Pre-commit and package build were rerun after merging current
main. The full repository-wide live suite was not run; live validation was scoped to this endpoint.Risks and follow-ups
The former #55 stack dependency is resolved and the branch includes current
main. Font availability remains deployment-specific; published names are discoverable suggestions while installed text-capable font names remain accepted. Main-repository CI provides the final clean-environment verification.