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 37 commits into
Conversation
3e495c6 to
53520f4
Compare
Confidence Score: 5/5
|
d3710c5 to
d0582ca
Compare
d0582ca to
a5c5022
Compare
077bfb3 to
49cfcc5
Compare
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
087b3fb to
73b240e
Compare
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.
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.
Share hidden/read_only flags via legacy_embed_field_lockdown_properties() so get_custom_fields and set_legacy_embed_field_lockdown stay aligned. This prevents after_install and migrate patches from re-exposing the legacy attach field when multi-attachment embedding is enabled.
Give operators explicit feedback when bulk legacy-embed migration hits unresolvable URLs: skip (Error Log, field unchanged) or remove (clear field, site warning log). Add a dialog toggle, split the job summary into skipped, removed, and error counts, and restrict the enqueue API to POST.
…elated validation errors
Show an orange msgprint when per-save migration cannot resolve einvoice_embedded_document, in addition to the existing Error Log. Guide invoice owners to ask a System Manager to run bulk migration on E Invoice Settings. Remove unused _log_removed_broken_legacy_embed (inlined in _handle_broken_legacy_embed). Add tests for the validate warning and bulk migration summary segments.
Expand Google-style docstrings on embed resolution, legacy migration, bulk migrate APIs, and test helpers. Regenerate locale catalogs and translate milestone 1 user-facing strings in de.po for the multi-annex embed settings, migration dialog, and Sales Invoice annex grid.
…ution Run setup_complete with company and fiscal-year args so lightmode CI seeds ERPNext without wizard insert errors. Prefer the legacy attach-field File row when submit sync created duplicate URLs for the same path, and align bulk migration tests with Desk attach behaviour.
…le id Replace deduplicate_attachment_rows with save-time validate_einvoice_attachment_rows: duplicate file_name respects E Invoice Settings error action; identical content_hash is warn-only. Resolve embeds via EmbedAttachment (File id + file_name). validate_attachments blocks duplicate filenames and missing File rows at generation time. _embed_attachments uses row.file_name for ARD 916 @filename (BR-DE-22).
Add a composite unique constraint on E Invoice Attachment Row for parent and file_name so duplicate embed filenames cannot be persisted for one Sales Invoice. Cover the schema hook with a database-specific constraint test.
…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.
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.
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.
834a1d6 to
eaf3eee
Compare
Custom field exclusivity is applied once via the make_custom_fields patch and on E Invoice Settings save; drop the per-migrate hook barredterra flagged as redundant on PR alyf-de#263.

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