feat(attachments): multi-annex embed for Sales Invoice (part 1) - #263
feat(attachments): multi-annex embed for Sales Invoice (part 1)#263dafrose wants to merge 36 commits into
Conversation
3e495c6 to
53520f4
Compare
Confidence Score: 5/5Safe to merge — all core paths (migration, embedding, permission checks, rollback on DB failure) are correctly implemented and the previously flagged issues are resolved. Every issue raised in previous review rounds is confirmed fixed. The migration path correctly handles draft, submitted, and cancelled invoices through direct DB writes, rolls back on failure, deduplicates the background job, and gates the whitelisted endpoint behind System Manager. The validate_doc hook correctly guards attachment-table validation behind the multi-embed setting. DocType JSON timestamps carry proper microseconds and the auto-generated Python type annotations match the JSON field definitions. Files Needing Attention: No files require special attention. The one remaining note (misleading parameter name in
|
d3710c5 to
d0582ca
Compare
d0582ca to
a5c5022
Compare
077bfb3 to
49cfcc5
Compare
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
Pin legacy embed behaviour with YAML scenarios (empty field, local 916, https and /api/method/ remote uri_id) and Drafthorse assertions on mocked find_file_by_url. Ignore .vscode/ in the app repo.
Replace _embed_attachment with get_legacy_embed_attachment and _embed_attachments so create_einvoice passes a URL list. Add integration test and before_tests fixtures so embed tests use governed master data instead of site-specific rows.
…ation Introduce E Invoice Attachment Row, einvoice_attachments on Sales Invoice, multi_attachment_embed_enabled setting, first-row-wins dedup on validate, and Desk attach-button UX.
Branch create_einvoice on get_embed_attachments; move attachment resolution to sales_invoice_attachments; consolidate tests and helpers.
Move einvoice_embedded_document into einvoice_attachments when multi-attachment embed is enabled, with per-save migration on Sales Invoice validate and a deduplicated bulk RQ job from E Invoice Settings. Lock the legacy attach field when multi-embed is on, fail embed on missing File rows, and add integration coverage for migration, lockdown, and broken links.
…units Add an integration test that embeds a table annex through attach_xml_to_pdf and verifies the payload survives extraction from the hybrid Factur-X PDF. Introduce shared helpers for minimal PDF bytes and CII AttachmentBinaryObject assertions. Remove unit tests for deduplicate_attachment_rows, get_legacy_embed_attachment, and mocked get_table_embed_attachments; those paths are already covered by integration tests.
Regenerate POT/PO from the attachment migration strings and translate 23 new and 1 adapted msgid in de.po.
Add an Include submitted Sales Invoices option on the E Invoice Settings migrate button. Draft invoices still migrate via save; submitted invoices use direct child-row inserts and db.set_value so post-submit restrictions do not block the job. Replace the bulk migration integration test with a draft/submitted × include_submitted matrix and cancel submitted fixtures before teardown.
Throw when an einvoice_attachments row points at a File without a file_url, load File rows via the normal get_doc path during legacy embed resolution, and restrict site-wide migration to System Manager.
Restructure attachment test coverage: split get_embed_attachments branching into subTest cases, drop redundant table-principles unit class, extract URL-order resolver checks, and restore multi-embed settings in integration setUp/tearDown. Move set_multi_attachment_embed_enabled and assert_single_orange_message into tests/helpers.py. Add integration coverage for bulk migration of cancelled submitted invoices and prefer a positive docstatus check in bulk_migrate_legacy_embed_attachments. Regenerate fixtures to update attachment row schema and enable file id field visibility, for the row filter to work on Sales Invoice Attachments table.
…d XML Share case-insensitive duplicate detection between validate_einvoice_attachment_rows and _get_table_embed_attachments; remove validate_attachments and the warn-only path so save-time checks align with the DB constraint. Require file_name on E Invoice Attachment Row and document display_name as print-only.
…bility Show only the legacy attach field before multi-embed is enabled, and only the Embedded Documents table afterward. Sync Custom Field flags from settings updates, after_install, and after_migrate, and update German locale strings.
…ex bytes Rollback the transaction when site-wide legacy embed migration fails mid-persist so a later commit cannot flush orphaned child rows. Read embedded annex files with get_content(encodings=[]) so PNG/PDF bytes are not text-decoded before base64. Add regression tests and stabilize embed test fixtures when pdf_on_submit is installed.
Update since 19 Jun 2026Focus: annex validation hardening, Desk field exclusivity, and two production fixes. What changed
Tests: Reviewer setup — unique constraintThere is no migrate patch for If your review site already had E Invoice Attachment Row before pulling this branch:
bench --site <site> execute eu_einvoice.european_e_invoice.doctype.e_invoice_attachment_row.e_invoice_attachment_row.on_doctype_update
Alternatively: fresh site + Test plan
Automated: |
barredterra
left a comment
There was a problem hiding this comment.
Suggested merge bar
Solid after the earlier review rounds (permissions, POST-only, orphaned-row rollback, binary encodings=[], field exclusivity). CI is green. Please address these three correctness items before merge:
- Don’t use validating
save()for bulk draft migration (or skip e-invoice validation there). - Always enforce duplicate annex filenames when multi-embed is on.
- Set
idxon direct child inserts.
Inline comments below.
There was a problem hiding this comment.
Custom fields should likely be (re-)created through a patch or bumping execute:from eu_einvoice.install import after_install; after_install() # 17 to #18
There was a problem hiding this comment.
Would it also make sense to include the custom field sync in after_migrate or is the patch bump the preferred solution? @barredterra
There was a problem hiding this comment.
Strictly speaking, I don't think we actually need the after_migrate part, since it does during every update what is really only necessary during the first update (via patch).
There was a problem hiding this comment.
I would have expected removal of after_migrate logic because it should now be redundant, right?
Route all successful bulk legacy-embed migrations through _persist_legacy_embed_migration_db so unrelated Sales Invoice validation cannot block drafts when Action on Validation Error during Save is enabled.
BR-DE-22 uniqueness is a format rule, not an e-invoice error-action toggle. Run duplicate-filename validation whenever multi-embed annex rows are validated, including on Postgres where the unique index is case-sensitive.
Assign max(idx) + 1 when bulk legacy migration inserts E Invoice Attachment Row records so Desk grid order is stable instead of defaulting to idx 0.
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
…mbeds Queue site-wide bulk migration when Multiple Attachment Embedding is first enabled on E Invoice Settings. Fail XML/download when the legacy attach field is still set so annexes cannot silently drop after the setting flip. Bulk migration always covers all docstatus via direct DB persist; remove the include_submitted option from the API and migrate dialog. Share enqueue logic through queue_bulk_migrate_legacy_embed_attachments and publish progress and completion updates to the requesting user via realtime msgprint.
6b26a7a to
0c00060
Compare
Validate table embed rows against File.attached_to_* on save and at XML generation so API/import paths cannot reference arbitrary File ids. Add unit and integration coverage; attach test annex files to invoices in helpers.
Drop the site-wide file_url fallback in _resolve_embed_file_for_invoice. A shared URL on another Sales Invoice could be migrated into the child table and then fail validate_einvoice_attachment_rows, blocking every save until manual DB intervention. Only consider File rows attached to the current invoice; treat unmatched legacy URLs as broken links. Add an integration test for the cross-invoice URL collision case.
Only block embed when the legacy field is set and the annex table is empty. On save, clear unresolvable legacy URLs when table rows already exist so embed can proceed from the table.
Skip inserting an E Invoice Attachment Row when the resolved File is already listed in einvoice_attachments; still clear einvoice_embedded_document on bulk migrate and on-save. Count bulk skips as already_migrated and suppress the orange “moved” msgprint when no row was appended. Add integration coverage for bulk and validate paths. Regenerate POT/PO and translate the broken-legacy-link-cleared message plus workspace labels.
Embedded Documents cannot be filled until the invoice is saved (File needs attached_to_name). Hide Add Row via cannot_add_rows while is_new so users are not offered empty, unusable rows.
|
Too many files changed for review (24 files, 15 file limit). Bypass the limit by tagging |

Summary
Part 1 of selectable PDF annexes for Sales Invoice (#261, coordination #76). This PR is supposed to only add support for multiple attachments via the same embedding path as before. File attachments are still embedded into XML (which may be embedded into PDF). It does not provide support for embedding additional files directly into PDF. This is planned for a follow-up PR.
When Multiple Attachment Embedding is enabled on E Invoice Settings, annexes are selected from the new
einvoice_attachmentschild table and embedded in the e-invoice XML as 916 supporting documents (same_embed_attachmentsloop as today). When the setting is off, behaviour stays on the legacyeinvoice_embedded_documentattach field.einvoice_attachmentschild table on Sales Invoice with Desk attach button and first-row-wins dedup on validatemulti_attachment_embed_enabledcheckbox on E Invoice Settingsget_embed_attachmentsbranches between table and legacy field; shared modulesales_invoice_attachments.pyget_legacy_embed_attachment+_embed_attachmentsURL loop (no change to hybrid PDF mechanics yet — annex bytes still travel inside embedded Factur-X XML)Not in this PR (possible follow-ups): PDF/A-3 factur-x pipeline with N PDF file attachments (#262), profile-aware 916 encoding, MIME allowlist, v15/develop backports.
Settings:

Migration Button UI:

Sales Invoice EInvoice tab:

Test plan
bench --site <site> run-tests --app eu_einvoice --lightmode— 22 tests greeneinvoice_embedded_documentonly → save with setting on → row appears in Embedded Documents, legacy field cleared, orange migration messageeinvoice_attachments→ submit/download PDF → annex payloads present in embedded Factur-X XML (Desk spot-check or XML download)Notes for reviewers
attach_xml_to_pdfstill embeds invoice XML only; new integration test verifies table annex content survives the PDF round-trip inside that XML.version-16-hotfixper rollout plan; develop backport differs (patch migration drops legacy field).Ticket Ref
LMK-16