8 min readAug 18, 2026by jakub

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_from are 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.
  • VersionedRecordInterface getters given the @return docblocks Magento's webapi reflection requires (surfaced once a consumer of the interface became REST-exposed).

Operational notes

  • bin/magento cache:flush is 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() derives direction, retention_basis, and retention_until itself; 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 actor on 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_editor ACL 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_assignment table (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/type instead 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_decimal link-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 PUT forces source=api server-side, matching how actor is already forced.

Fixed

  • product_id on an assignment row is nullable with onDelete="SET NULL" (was CASCADE): deleting a Magento product no longer destroys assignment history. A product_sku snapshot 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_from tie (a total-order tiebreak plus ORDER BY on 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 LocalizedException message 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. New packaging_format v1 code list, importable via the existing ppwr:codelist:import --dir command.
  • 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).