From 98d7a3ef31642bab0e347b9817abe88f85d6c679 Mon Sep 17 00:00:00 2001 From: shopwareBot Date: Thu, 13 Aug 2026 09:28:16 +0000 Subject: [PATCH] [create-pull-request] automated change --- ...-generation-v1-to-v2-migration-strategy.md | 162 ++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 resources/references/adr/2026-08-05-document-generation-v1-to-v2-migration-strategy.md diff --git a/resources/references/adr/2026-08-05-document-generation-v1-to-v2-migration-strategy.md b/resources/references/adr/2026-08-05-document-generation-v1-to-v2-migration-strategy.md new file mode 100644 index 000000000..023d4b4a2 --- /dev/null +++ b/resources/references/adr/2026-08-05-document-generation-v1-to-v2-migration-strategy.md @@ -0,0 +1,162 @@ +--- +title: Migration strategy from document generation v1 to v2 +date: 2026-08-05 +area: after-sales +tags: [core, documents] +--- + +# Migration strategy from document generation v1 to v2 + +::: info +This document represents an architecture decision record (ADR) and has been mirrored from the ADR section in our Shopware 6 repository. +You can find the original version [here](https://github.com/shopware/shopware/blob/trunk/adr/2026-08-05-document-generation-v1-to-v2-migration-strategy.md) +::: + +## Context + +[2026-03-17-refactor-of-document-generation.md](https://github.com/shopware/shopware/blob/trunk/adr/2026-03-17-refactor-of-document-generation.md) +decided to rewrite the document generation system, this document outlines the concrete migration strategy. +Both implementations will coexist throughout the 6.7 and 6.8 release lines. The new implementation persists into the same +`document` table and both render the same Twig document templates. The feature flag `DOCUMENT_GENERATION_REWORK` opts +into version 2. + +## Decision Roadmap + +| Phase | Version | Action | +| ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Phase 1 | 6.7 | Opt-in: v2 is available behind the feature flag (default off). Version 1 is fully deprecated for removal in 6.9. Compatibility gaps are closed and the backfill process is prepared. | +| Phase 2 | 6.8 | Opt-out: v2 becomes the default (flag flips to `default: true`). The `@experimental` annotations are removed, the marked surface becomes the stable public API. | +| Phase 3 | 6.9 | Version 1 and the feature flag are completely removed. Backfills are executed. | +| Phase 4 | post-6.9 | Destructive schema drops are executed to finalize the database cleanup. | + +### Core Principles + +- When the feature flag is off, the system runs purely on v1. The v1 code paths take no dependency on v2. +- Turning the flag on means all Shopware-driven surfaces use v2, including the admin UI, Flow Builder actions, mail attachments, bulk edits, and customer-facing download routes. +- The v1 APIs stay functional in both flag states and remain a public contract until their removal in 6.9. +- Data compatibility strictly flows in one direction, meaning v2 can read v1 data. + +### Compatibility model + +| | v1 document | v2 document | +| ----------- | ------------------ | ----------- | +| v1 Surfaces | Native | Best effort | +| v2 Surfaces | Read-time fallback | Native | + +#### Read-time fallback + +A `document` missing `document_file` rows exposes virtual formats derived from v1 fields. The `documentMediaFileId` maps +to the format matching the file extension, while the `documentA11yMediaFileId` maps to HTML. Extensions outside the format +registry stay as download-only files, exactly like legacy Zugferd types. + +#### Best effort compatibility + +A `document` row has two media slots, `documentMediaFileId` and `documentA11yMediaFileId`, and v1 selects between them +purely by file extension, there is no hard PDF assumption. The v2 persister fills both from its generated formats: the +primary slot takes the PDF if present, otherwise the single generated format. The a11y slot takes the HTML. So a v2 +document stays usable in v1 surfaces after the flag is switched off whenever at least one format lands in a slot. + +#### No regeneration by v1 + +The read path for v1 auto-generates missing media for non-static documents. For documents that already carry `document_file` +rows, it skips this and fails gracefully instead. Otherwise, switching back would produce a divergent v1 rendering from the +nearly empty v2 `document.config`. This guard is the single deliberate exception to v1 taking no dependency on v2 and will +be removed alongside v1. + +#### Legacy Zugferd types + +The `zugferd_invoice`, `zugferd_embedded_invoice`, and the four storno or credit-note variants are displayed exactly as +they are in the v2 UI. They appear under their legacy type name and are available for download only. + +#### Idempotent backfill + +A migration script creates the missing `document_file` rows for v1 documents and normalizes legacy Zugferd types into a +base type plus format. For example, a `zugferd_invoice` becomes a standard `invoice` with a `zugferd_xml` file. This is +prepared during 6.7 but only relied upon in 6.9, once no v1 writer exists anymore. At that point, a catch-up run covers any +documents generated by v1 in between. This process never touches the actual media files. + +### Routing and Flags + +The v2 admin API routes are available regardless of the flag state. The flag only switches the Shopware-driven surfaces. +Entry points for v2 are annotated `@experimental stableVersion:v6.8.0 feature:DOCUMENT_GENERATION_REWORK` and may change +until v2 becomes the default in 6.8. + +For customer-facing downloads, the storefront and Store API routes gain a flag-gated branch. This branch resolves files +through the new document file structure first before falling back to legacy media fields. The URLs do not change and the +routes survive the removal of v1 in 6.9. + +### Public Extension Surface + +The subset of v2 classes that becomes public API is marked `@experimental stableVersion:v6.8.0 +feature:DOCUMENT_GENERATION_REWORK` from the start. This includes abstract extension base classes, render pipeline structs, +configuration DTOs, file entity classes, domain exceptions, and events. Everything that stays internal carries plain +`@internal` and is never marked experimental. In 6.8, the `@experimental` annotations are removed and the marked classes +become the stable public API, while orchestration internals stay restricted. +The `DocumentV2` namespace is permanent, and reused v1 classes (`DocumentEntity`, `DocumentDefinition`, `DocumentCollection`, +`ReferenceInvoiceLoader`, the `DocumentBaseConfig` aggregates) move into it with the removal of v1 in 6.9. + +### Deprecations and Entity Removal + +Everything slated for removal in 6.9 is deprecated now, during 6.7, with `@deprecated tag:v6.9.0`. This covers the v1 +domain, legacy parts of document and mail actions, admin components, and legacy Twig branches. + +Document types and formats are now code-registered strings rather than database entities. The legacy foreign keys currently +live across three tables: `document`, `document_base_config`, and `document_base_config_sales_channel`. + +#### During 6.7 + +A nullable `type_name` column is added to all three affected tables. Version 2 writes both the string and the legacy +foreign key, while v1 continues writing only the foreign key. The legacy entities `document_type` and +`document_type_translation` are deprecated with `reason:remove-entity` for removal in 6.9. + +#### During 6.9 + +The `type_name` columns are backfilled from the foreign keys and the legacy `document_type_id` columns are made nullable. +The entity classes, dependency injection registrations, and foreign key fields are removed. Persisted merchant data is migrated +from IDs to technical names. + +## Version 6.8 Major Checklist + +1. Flip `DOCUMENT_GENERATION_REWORK` to `default: true`, the flag becomes the opt-out. +2. Remove the `@experimental` annotations, the marked classes become the stable public API. +3. Write the `UPGRADE-6.8.md` entries for the default switch and the stabilized extension surface. + +## Version 6.9 Major Checklist + +1. Remove `DOCUMENT_GENERATION_REWORK` and every gate (PHP, DI, Twig, admin JS, system config XML). +2. Delete the v1 domain, v1 admin components, v1 flow/mail branches, v1-only Twig branches, and the v1 entries in the PHPStan tagged-service contracts. +3. Move the surviving shared classes into the `DocumentV2` namespace. +4. Execute the prepared backfills: `document_file` rows (incl. Zugferd normalization), the `type_name` columns, rule/flow payloads; make the `document_type_id` columns nullable. +5. Drop the `document_base_config.config` JSON blob (destructive) and remove the `DocumentBaseConfigSyncSubscriber`. +6. Make the v2 branch of the storefront and Store API download routes unconditional. +7. Write the `UPGRADE-6.9.md` entries and the destructive migrations (`updateDestructive()`: `document_type` tables, the `document_type_id` columns, `document.document_media_file_id`, `document.document_a11y_media_file_id`). + +## Consequences + +Merchants have the freedom to switch between v1 and v2 freely during the 6.7 and 6.8 lifecycles. The only caveat is that v1 +surfaces represent at most two formats per document and default to PDF, so a v2 document generated without a PDF is +reachable in v1 only by an explicit file-type request while the flag is off. Already generated documents remain fully +accessible after 6.9 without any need for regeneration, as the backfills simply add the required metadata. + +### For extension developers + +- Twig template overrides keep working: v2 renders the same `@Framework/documents/*.html.twig` files, so + presentation only extensions are largely unaffected. +- Code extension points are not carried over: custom document types or formats registered through the v1 + `document.renderer` / `document_type.renderer` tags, v1 document events, and decorators of the v1 `DocumentGenerator` + are never invoked by the new pipeline. v2 discovers types, formats, and data exclusively through its own tagged + services (`shopware.document_v2.{type,provider,renderer}`) or the app manifest. + +To stay compatible, such extensions must be ported to v2 (a data provider, a renderer, and a type, or an app manifest +entry). During the transition an extension may register its v2 and v1 variants side by side to work in either flag +state. Step-by-step guidance lives in `UPGRADE-6.7.md` and `UPGRADE-6.8.md`. + +### For merchants and partners + +- **6.7:** opt in to v2 and test it (in staging). Confirm your document related extensions still work under v2. +- **6.8:** v2 is the default. Before upgrading, verify every document related extension is v2 ready. If one is not, opt-out, upgrade safely, and migrate later. +- **6.9:** v1 and the flag are gone, opting out is no longer possible, so everything must be on v2. + +The extra major cycle adds time flexibility without significantly changing the overall workload: because v2 becomes the default in 6.8, an extension that is +not v2 ready works only for merchants who explicitly opt out. Extension authors should therefore be v2 ready by 6.8, or +coordinate opt-out with their merchants, and fully migrated by 6.9.