Changelog
Current version: 0.3.0
This page merges the release history of all five qoliber/ppwr modules into
one timeline. Each version below unions every module's own changes; a module
with no entry for a given version simply isn't listed under it.
0.3.0 — 2026-08-16
This release is a PPWR packaging-data technical preview, not a PPWR-compliant or complete solution. It ships the implemented half of the roadmap — packaging master data and catalog assignment — to a trustworthy, tested standard. It does not ship the accounting half (fulfilment capture and EPR reporting) or any of the human regulatory gates a real filing requires. See the scope note for the full, itemised what-works / what-doesn't breakdown.
Release-wide, across all five modules: a 22/22-green Playwright E2E suite
covering every shipped admin surface, and unit + integration coverage for the
three implemented modules. qoliber/ppwr-fulfillment and qoliber/ppwr-epr
are required at ^0.3 alongside the other three — all five modules release
in lockstep — even though the two of them still ship no functional code.
Not in this release: fulfilment manifest capture, dispatch recognition, a
transactional outbox, an immutable EPR ledger, external-movements ingestion,
source-period closure, reconciliation, report finalization, a
country/Member-State adapter, or storefront labelling. The shipped regulatory
code lists are provisional. Using this release for an actual EPR filing
requires the missing accounting half plus review by an EPR specialist — this
extension does not determine legal compliance with any EPR scheme.
Operational note: run bin/magento cache:flush after installing or
upgrading to this release — a compiled config cache from a previous version
will otherwise keep serving a stale DI binding until flushed.
PPWR Core
Added
- Admin menu (System > PPWR & Packaging) and a code-list grid with an ACL-gated mass-activate action.
bin/magento ppwr:codelist:import --dir— audited command-line import of regulatory code lists.- Per-document storage-adapter resolution: a document read now resolves through the adapter it was actually stored under (local/S3), not the currently configured one — switching the configured adapter no longer strands previously-stored documents.
- Immutable-retention and delete guard on stored documents (mirrors the
traceability-record guard already in
Qoliber_PpwrPackaging): no update to a persisted document row, and no delete before its retention date has passed. - Real file-signature inspection on document upload — rejects a browser-claimed MIME type that doesn't match the file's actual bytes.
- A real lineage-chain accessor on the versioning primitive, backed by a forced audit-failure integration test.
Changed
- Document retention date is now derived server-side (document type + reference date) instead of being accepted from the caller.
- Market-scope and legal-entity-role revisions with a future
effective_from/valid_fromare rejected up front, with an actionable message, instead of silently leaving a gap with no resolvable scope between predecessor and successor. - Market-scope creation validates that its pinned recognition-rule code/version actually exists before saving.
- Regulatory code-list seed datasets ship inside the module
(
docs/regulatory/codelists/v1/) instead of being referenced from outside it. These datasets remain provisional pending a regulatory review gate. - Downloaded PPWR documents get a sensible, human-readable filename instead of a hash.
- S3 credentials are decrypted before the S3 client is constructed.
Fixed
- The document download controller no longer leaks a plaintext copy of an encrypted document to temporary storage.
- Versioning/audit core hardened: atomic write+audit (no window where a write can succeed without its audit event), a cascading-delete guard, business-key uniqueness with a safe backfill migration, and deduplication of pre-existing duplicate-open business-key rows before the uniqueness index is added.
- Code-list mass-activate ACL matrix and grid mass-action wiring corrected.
VersionedRecordInterfacegetters given the@returndocblocks Magento's webapi reflection requires (surfaced once a consumer of the interface became REST-exposed).
Operational notes
bin/magento cache:flushis required after installing/upgrading to this release for a newly-added DI preference (DocumentStorageLocatorInterface) to take effect — a compiled config cache from a previous version will otherwise keep serving the old binding.
PPWR Packaging
Added
- Packaging admin UX: revision review/approve/supersede screens and a quick-create flow, including an "also create a recipe" checkbox and an optional document attachment on quick-create.
- Recipe/BOM domain: packaging recipes and recipe items with calculation-basis-driven flattening math, a dedicated BOM editor admin UI, and REST endpoints for recipes and recipe items.
- An explicit admin form action and REST endpoint for recording an Article 22 supplier-side traceability transaction against an approved revision — the caller supplies the transaction date, the source transaction/lot reference, and the packaged-product supplier reference.
Changed
- Article 22 traceability records are now created from an explicit supply-transaction action instead of being fabricated automatically when a revision is approved (approval is not a supply event).
TraceabilityRecordRepository::create()derivesdirection,retention_basis, andretention_untilitself; caller-supplied values for these fields are never trusted.- A packaging revision, once approved, is permanent history; a supersession
target is re-validated at
approve()time rather than trusted from the request. - The evidence-policy evaluator never fails open on malformed configuration.
- REST
actoron audited writes comes from the authenticated identity, not a caller-supplied field. - Recipe code/version immutability is guarded at the resource level, and recipe versioning is enforced as a real uniqueness constraint at the database, not only in application code.
Fixed
- Approval now runs a completeness validator (components, weights, quantities, composite/separability, reusable-system info, classification fields) before a revision can become immutable and "approved" — an incomplete revision can no longer be approved.
- Packaging components can no longer be re-parented to a different revision or forged-deleted.
packaging_editorACL is now proven (and enforced) on the recipe REST routes.
Note
- Article 22 traceability records recorded by this module are evidence for a merchant's own EPR/customs process; this module does not itself file, submit, or determine legal compliance with any EPR scheme.
PPWR Catalog
This is the module's first functional release — 0.1.0 was an empty module shell.
Added
- Product-to-recipe assignment: a bitemporal, system-versioned
ppwr_product_assignmenttable (product/website to recipe + pinned recipe version, effective-dated), never hard-deleted. ProductPackagingResolverInterface/ProductPackagingResolver: resolves the packaging that applies to a product — simple, configurable, bundle, grouped, virtual, downloadable — in a fixed, small number of queries regardless of batch size.- Assignment Web API endpoints: idempotent SKU/website/recipe-code upsert, and ACL-gated get/list/close.
- Admin assignments grid and a dedicated CSV import/export for assignments (never wired into core product import), plus a completeness report of enabled/shippable products with no valid current assignment.
- A "PPWR Packaging" section on the product edit form.
- Full Playwright E2E coverage for the assignment CSV import and the product-form section.
Changed
- Bundle resolution respects each option's
required/typeinstead of summing every selection of every option as if it were shipped — restricted to the bundle's default, required composition. Documented remaining limitation: no true per-order selection data. - Grouped-product component quantities are read from the real
catalog_product_link_attribute_decimallink-quantity attribute instead of being hardcoded to 1. - Resolution quantities are bcmath decimal strings end to end, matching the
recipe-flattening discipline in
Qoliber_PpwrPackaging— no PHP floats. - The completeness report now delegates to the resolver itself, so it can no longer disagree with what the resolver actually returns; it also exposes a covered-by-fallback/covered-by-components distinction instead of silently hiding indirectly-covered products.
- CSV export neutralises spreadsheet-formula injection (a leading
=/+/-/@in an exported cell). CSV import enforces a 5 MiB file-size limit, a 10,000-row limit, exact per-row column-count validation, and real date/priority format validation — an invalid priority is a rejected row, not a silent 0. CSV import also tolerates a leading UTF-8 BOM from Excel-exported files. - REST
PUTforcessource=apiserver-side, matching howactoris already forced.
Fixed
product_idon an assignment row is nullable withonDelete="SET NULL"(wasCASCADE): deleting a Magento product no longer destroys assignment history. Aproduct_skusnapshot is written server-side on every create/revise so history stays identifiable by SKU even after the underlying product is gone.- Resolution is now deterministic on a full priority/
valid_fromtie (a total-order tiebreak plusORDER BYon every candidate query) — two replays at the same cutoff can no longer disagree. - Assignment upsert has an atomic, database-level uniqueness guarantee (was read-then-write, so two concurrent requests could both open a duplicate lineage).
- Raw exception detail no longer reaches the admin CSV import report; only a
LocalizedExceptionmessage is shown, everything else is logged server-side and returns a generic message.
Note
- Catalog assignment resolves which packaging applies to a product. It does not capture what was actually shipped, and it is not a fulfilment or EPR-reporting system.
PPWR Fulfillment
Still no functional code. Version-bumped in lockstep with the other four modules so the metapackage can pin a single consistent release. Fulfilment manifest capture and dispatch recognition are Phase D work, not in this release.
Changed
- Adopted the house-canonical PHP support range
(
~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0).
PPWR Epr
Still no functional code. Version-bumped in lockstep with the other four modules so the metapackage can pin a single consistent release. The EPR ledger, reconciliation, and report finalization are Phase D work, not in this release.
Changed
- Adopted the house-canonical PHP support range
(
~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0).
0.2.0 — 2026-08-08
PPWR Core
Added
- Core foundation: legal entities and market scopes (single-entity fast path, auto-provisioned default entity), effective-dated jurisdiction-scoped entity roles, a reusable system-versioning service (the same conventional-bitemporality invariant used in the 0.3.0 catalog-assignment table), versioned regulatory code lists (import, explicit audited activation, version pinning), append-only audit trail, and private document storage (local + S3-compatible adapters behind one shared contract, MIME/size validation, checksum-verified round trip).
- Declarative schema:
ppwr_legal_entity,ppwr_legal_entity_role,ppwr_market_scope,ppwr_code_list,ppwr_code_list_entry,ppwr_document,ppwr_audit_event. - Service contracts in
Api/:LegalEntityRepositoryInterface,LegalEntityRoleRepositoryInterface,MarketScopeRepositoryInterface,CodeListProviderInterface,DocumentStorageInterface,AuditRecorderInterface,VersionedRecordServiceInterface,LegalEntityCompletenessInterface. - Minimal admin surfaces: ACL-protected code-list activation action and document download controller.
docs/regulatory/codelists/v1/— provisional seed datasets (material_category,packaging_level,recognition_rule) and their JSON Schema, pending a regulatory review gate.
PPWR Packaging
Released 2026-08-09.
Added
- Packaging master data: packaging specs (stable, immutable, slug-validated
packaging_code), immutable-once-approved packaging revisions, material components (code-list-validated), a draft/in_review/approved/superseded workflow with the single-user fast path and configurable evidence policy, and Article 22 supplier-side traceability records with enforced retention. - Declarative schema:
ppwr_packaging_spec,ppwr_packaging_revision,ppwr_packaging_component,ppwr_traceability_record. Newpackaging_formatv1 code list, importable via the existingppwr:codelist:import --dircommand. - Service contracts in
Api/:PackagingSpecRepositoryInterface,PackagingRevisionRepositoryInterface,PackagingComponentRepositoryInterface,TraceabilityRecordRepositoryInterface,PackagingWorkflowInterface. - REST endpoints for specs, revisions, and workflow transitions, ACL-gated under the existing PPWR resource tree.
bin/magento ppwr:traceability:purge-expired— the one production entry point that deletes traceability records, still fully subject to the retention guard.
0.1.0 — 2026-08-08
Package skeleton: five module shells (Qoliber_PpwrCore,
Qoliber_PpwrPackaging, Qoliber_PpwrCatalog, Qoliber_PpwrFulfillment,
Qoliber_PpwrEpr), each with registration.php and etc/module.xml, no
functional code yet. Module load order fixed via etc/module.xml
<sequence>: Core ← Packaging ← Catalog ← Fulfillment ←
Epr (one-way). Composer packaging: one magento2-module package per module,
plus the qoliber/ppwr magento2-metapackage aggregating all five for
one-line installs — supersedes an earlier single magento2-component layout.
CI foundation: gate scripts, violation fixtures, and a GitLab CI pipeline
wiring composer validation, coding standard, static analysis, PHP
parse-floor, architecture grep gates, setup:di:compile, and an integration
test bootstrap.
PPWR Core
Initial module shell: registration.php, etc/module.xml. No functional
code yet. Root of the module dependency chain.
PPWR Packaging
Initial module shell: registration.php, etc/module.xml. No functional
code yet. Sequenced after Qoliber_PpwrCore.
PPWR Catalog
Initial module shell: registration.php, etc/module.xml. No functional
code yet. Sequenced after Qoliber_PpwrPackaging.
PPWR Fulfillment
Initial module shell: registration.php, etc/module.xml. No functional
code yet. Sequenced after Qoliber_PpwrCatalog.
PPWR Epr
Initial module shell: registration.php, etc/module.xml. No functional
code yet. Sequenced after Qoliber_PpwrFulfillment (last module in the
chain).