Skip to content

feat(options): replace the tracked option with no tracking - #514

Open
FreekVR wants to merge 9 commits into
mainfrom
feat/INT-1694-no-tracking
Open

feat(options): replace the tracked option with no tracking#514
FreekVR wants to merge 9 commits into
mainfrom
feat/INT-1694-no-tracking

Conversation

@FreekVR

@FreekVR FreekVR commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

replaces the deprecated tracked shipment option with its inverse, no tracking, and sends the x-dmp-no-tracking feature flag so the APIs stop returning the old option.

Tracking is the default wherever a carrier supports it, so the option only ever expresses an explicit opt-out. How that reaches the API differs per endpoint: the Order v2 pull (delivery-options) endpoint gets noTracking as an ADR-0013 empty object, with no key at all when the merchant wants tracking, while the shipment and order v1 endpoints get no_tracking with a truthy or falsy value. Merchants can set it per carrier and per product, wherever capabilities say the option exists. Verified against the acceptance API that sending the flag swaps tracked for noTracking in the contract definitions response, and that the two are never both present.

Which feature flags we send is a single config entry, read by one support class and injected in both request layers: a Guzzle middleware for the generated SDK clients, and the header list of the legacy API service. Adding or retiring a flag is a one-line config change. What each flag does differs per flag — see the API feature flags overview.

Swapping the entry in the order option definitions is what drives the option change: carrier settings, product settings, shipment options and both settings views all derive their keys from that registry. The option is no longer tied to holding a small package contract or to the international mailbox setting, which was carrier-specific reasoning the PDK should not carry. It still cannot be given a surcharge, and consumers cannot choose it in the checkout.

getContractDefinitions() gained a $fresh parameter so a caller can rewrite the cached copy rather than bypass it once. The plugin migrations need that: contract definitions stored before the flag was sent hold tracked and no noTracking, so they must be re-fetched.

Requires SDK 11.0.0-beta.31, the first release carrying no_tracking on the shipment options model.

Renames to pick up when upgrading:

  • TrackedDefinition is removed, use NoTrackingDefinition
  • CarrierSettings::$exportTracked is now $exportNoTracking, with the inverse meaning
  • ProductSettings::$exportTracked is now $exportNoTracking, with the inverse meaning
  • ShipmentOptions::$tracked is now $noTracking, with the inverse meaning

These are source-level renames. The only consumers are our own plugins, which are updated alongside this release, so it ships as a minor. Stored merchant values are converted by migrations in the plugin PRs that follow.

Translations for the new keys are in the PDK Vertalingen sheet. The generated files are gitignored, so none ship here.

Follow-ups: INT-1768 (OpenCart), INT-1769 (Shopify), INT-1289 (Magento).

Fixes INT-1694

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings July 30, 2026 15:12
@FreekVR
FreekVR requested a review from a team as a code owner July 30, 2026 15:12
@github-project-automation github-project-automation Bot moved this to Todo in PDK Jul 30, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the PDK’s shipment option model and related request/definition layers to replace the deprecated tracked option with its inverse noTracking, and introduces a centralized mechanism for sending API feature flags (notably x-dmp-no-tracking) on both SDK (generated) and legacy API requests to ensure the API contract returns the new option.

Changes:

  • Replace the tracked shipment option (and its settings keys) with noTracking across option definitions, resources, settings views, and tests/snapshots.
  • Add ApiFeatureFlags + a new Guzzle FeatureFlagMiddleware and wire it into the SDK client stack; add the same flags to legacy MyParcelApiService headers.
  • Add a $fresh parameter to CarrierCapabilitiesRepository::getContractDefinitions() to force a cache rewrite when contract-definition shape changes.

Reviewed changes

Copilot reviewed 74 out of 74 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/Unit/Shipment/Model/Options/ShipmentOptionsTest.php Updates unit tests to assert noTracking key/value preservation when mapping from capabilities.
tests/Unit/Shipment/Model/Options/DeliveryOptionsTest.php Updates delivery options fixture data to use noTracking instead of tracked.
tests/Unit/SdkApi/Middleware/FeatureFlagMiddlewareTest.php Adds unit tests ensuring SDK requests receive configured feature flag headers.
tests/Unit/Fulfilment/Model/ShipmentOptionsTest.php Updates fulfilment shipment-options test input to noTracking.
tests/Unit/Context/Model/OrderDataContextTest.php Updates order context fixtures to use noTracking tri-state values.
tests/Unit/Carrier/Repository/CarrierCapabilitiesRepositoryTest.php Adds coverage for the new “fresh” refetch behavior + uses noTracking option in contract definitions.
tests/Unit/Base/Support/ApiFeatureFlagsTest.php Adds unit tests for converting configured flags to request headers.
tests/Unit/App/Options/Definition/NoTrackingDefinitionTest.php Adds unit tests for the new NoTrackingDefinition mapping/constraints.
tests/Unit/App/Endpoint/Resource/DeliveryOptionsV1ResourceTest.php Replaces old tracked inversion tests with explicit noTracking tri-state formatting assertions.
tests/Unit/App/Action/Backend/Settings/UpdateProductSettingsActionTest.php Updates product settings update test to use NoTrackingDefinition.
tests/Unit/App/Action/Backend/Account/UpdateAccountActionTest.php Updates account update assertions from getTracked() to getNoTracking().
tests/Unit/Api/Service/MyParcelApiServiceTest.php Adds coverage for including configured feature flags in legacy API headers.
tests/SdkApi/Response/ExampleContractDefinitionsResponse.php Removes deprecated tracked option from example contract definitions response.
tests/Bootstrap/MockCarrierCapabilitiesRepository.php Updates mocked capability keys from tracked to noTracking.
tests/snapshots/UpdateShipmentsActionTest__it_updates_shipments__1.json Snapshot update for noTracking tri-state serialization.
tests/snapshots/ShipmentTest__it_returns_empty_fulfilment_shipment_when_no_pdk_shipment_is_passed__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/ShipmentTest__it_creates_fulfilment_shipment_from_pdk_shipment_with_data_set_shipment_with_all_options__1.json Snapshot update replacing tracked with noTracking and option lists.
tests/snapshots/ShipmentTest__it_creates_fulfilment_shipment_from_pdk_shipment_with_data_set_empty_shipment__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/SettingsViewTest__it_gets_settings_view_with_data_set_product_settings__1.json Snapshot update renaming exportTracked to exportNoTracking and translation keys.
tests/snapshots/SettingsViewTest__it_gets_settings_view_with_data_set_carrier_settings__1.json Snapshot update renaming exportTracked to exportNoTracking across carriers.
tests/snapshots/SaveOrderTest__it_creates_a_valid_order_collection_from_api_data_with_data_set_order_with_pickup__1.json Snapshot update replacing tracked with noTracking in option lists.
tests/snapshots/SaveOrderTest__it_creates_a_valid_order_collection_from_api_data_with_data_set_order_containing_many_attributes__1.json Snapshot update replacing tracked with noTracking in option lists.
tests/snapshots/QueryTest__it_creates_shipment_collection_from_queried_data_with_data_set_shipment_with_pickup__1.json Snapshot update replacing tracked with noTracking in option lists + tri-state.
tests/snapshots/QueryTest__it_creates_shipment_collection_from_queried_data_with_data_set_shipment_with_drop-off_point__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/QueryTest__it_creates_shipment_collection_from_queried_data_with_data_set_shipment_with_contract__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/QueryTest__it_creates_shipment_collection_from_queried_data_with_data_set_normal_shipment__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/QueryTest__it_creates_order_collection_from_queried_data__1.json Snapshot update replacing tracked with noTracking in options.
tests/snapshots/PostOrdersTest__it_creates_a_valid_order_collection_from_api_data_with_data_set_one_order_with_pickup__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/PostOrdersTest__it_creates_a_valid_order_collection_from_api_data_with_data_set_one_order_containing_many_attributes__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/PdkOrderTest__it_creates_pdk_order_from_fulfilment_order_with_data_set_one_order_with_pickup__1.json Snapshot update replacing tracked/exportTracked with noTracking/exportNoTracking.
tests/snapshots/PdkOrderTest__it_creates_pdk_order_from_fulfilment_order_with_data_set_one_order_containing_many_attributes__1.json Snapshot update replacing tracked/exportTracked with noTracking/exportNoTracking.
tests/snapshots/PdkOrderTest__it_creates_a_storable_array_with_data_set_various_delivery_options__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/PdkOrderTest__it_creates_a_storable_array_with_data_set_single_order__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/PdkOrderTest__it_creates_a_storable_array_with_data_set_carrier_UPS_to_Netherlands_express__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/PdkOrderTest__it_creates_a_storable_array_with_data_set_carrier_UPS_to_Belgium__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/PdkOrderTest__it_creates_a_storable_array_with_data_set_carrier_GLS_with_saturday_delivery__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/PdkOrderTest__it_creates_a_storable_array_with_data_set_carrier_GLS_with_insurance__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/PdkOrderTest__it_creates_a_storable_array_with_data_set_carrier_GLS_to_Netherlands__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/PdkOrderTest__it_creates_a_storable_array_with_data_set_carrier_GLS_to_Germany__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/PdkOrderTest__it_creates_a_storable_array_with_data_set_carrier_GLS_custom_contract__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/PdkOrderTest__it_creates_a_storable_array_with_data_set_carrier_dhl_for_you__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/OrderTest__it_creates_fulfilment_order_from_pdk_order_with_data_set_order_without_shipments__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/OrderTest__it_creates_fulfilment_order_from_pdk_order_with_data_set_order_with_shipments__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/OrderTest__it_creates_fulfilment_order_from_pdk_order_with_data_set_empty_order__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/FrontendRenderServiceTest__it_renders_component_with_data_set_product_settings__1.json Snapshot update for exportNoTracking in rendered data.
tests/snapshots/FrontendRenderServiceTest__it_renders_component_with_data_set_plugin_settings__1.json Snapshot update for exportNoTracking in rendered plugin settings.
tests/snapshots/FrontendRenderServiceTest__it_renders_component_with_data_set_order_list_column__1.json Snapshot update replacing tracked with noTracking in rendered order list data.
tests/snapshots/FrontendRenderServiceTest__it_renders_component_with_data_set_order_box__1.json Snapshot update replacing tracked with noTracking in rendered order box data.
tests/snapshots/FrontendRenderServiceTest__it_renders_component_with_data_set_init_script__1.json Snapshot update for endpoint ordering + noTracking in initial data.
tests/snapshots/FrontendRenderServiceTest__it_renders_component_with_data_set_child_product_settings__1.json Snapshot update for exportNoTracking in child product settings data.
tests/snapshots/ExportReturnActionTest__it_exports_return_with_data_set_simple_orders__1.json Snapshot update replacing tracked/exportTracked with noTracking/exportNoTracking.
tests/snapshots/ExportReturnActionTest__it_exports_return_with_data_set_no_return_capabilities__1.json Snapshot update replacing tracked/exportTracked with noTracking/exportNoTracking.
tests/snapshots/ExportReturnActionTest__it_exports_return_with_data_set_insurance__1.json Snapshot update replacing tracked/exportTracked with noTracking/exportNoTracking.
tests/snapshots/CreateReturnShipmentsTest__it_creates_return_shipment_with_data_set_simple_domestic_shipment__1.json Snapshot update replacing tracked with noTracking.
tests/snapshots/ContextServiceTest__it_gets_context_data_with_data_set_single_product__1.json Snapshot update for exportNoTracking.
tests/snapshots/ContextServiceTest__it_gets_context_data_with_data_set_single_order__1.json Snapshot update replacing tracked with noTracking in context payload.
tests/snapshots/ContextServiceTest__it_gets_context_data_with_data_set_multiple_products__1.json Snapshot update for exportNoTracking.
tests/snapshots/ContextServiceTest__it_gets_context_data_with_data_set_multiple_orders__1.json Snapshot update replacing tracked with noTracking in context payload.
tests/snapshots/AccountTest__it_creates_storable_array__1.json Snapshot update replacing tracked with noTracking in stored account data.
tests/snapshots/AbstractSettingsRepositoryTest__it_retrieves_all_categories_and_fields__1.json Snapshot update for exportNoTracking settings fields.
src/Settings/Model/ProductSettings.php Updates product settings model docblock property to exportNoTracking.
src/Settings/Model/CarrierSettings.php Updates carrier settings model docblock property to exportNoTracking.
src/SdkApi/Service/AbstractSdkApiService.php Injects the feature flag middleware into the SDK Guzzle handler stack.
src/SdkApi/Middleware/FeatureFlagMiddleware.php Adds a new SDK middleware to apply configured feature flags to outgoing requests.
src/Frontend/View/CarrierSettingsItemView.php Removes carrier-specific tracked/mailbox settings logic from the UI layer.
src/Carrier/Repository/CarrierCapabilitiesRepository.php Adds $fresh option to refetch and rewrite cached contract definitions.
src/Base/Support/ApiFeatureFlags.php Introduces a central helper to convert configured flags to headers.
src/App/Options/Definition/TrackedDefinition.php Removes the deprecated TrackedDefinition.
src/App/Options/Definition/NoTrackingDefinition.php Adds NoTrackingDefinition mapping option keys to SDK contract keys.
src/App/Endpoint/Resource/DeliveryOptionsV1Resource.php Changes formatting logic to emit noTracking opt-out per ADR-0013 and never send tracked.
src/Api/Service/MyParcelApiService.php Adds configured feature flag headers to legacy API requests.
config/pdk-default.php Adds apiFeatureFlags config default enabling x-dmp-no-tracking.
config/pdk-business-logic.php Swaps option registry entry from TrackedDefinition to NoTrackingDefinition.
composer.json Bumps myparcelnl/sdk requirement to ^11.0.0-beta.31@beta.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/SdkApi/Middleware/FeatureFlagMiddleware.php
@codecov

codecov Bot commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.14286% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.36%. Comparing base (0a13274) to head (c11a797).

Files with missing lines Patch % Lines
.../Carrier/Service/CapabilitiesValidationService.php 75.00% 1 Missing ⚠️
...tings/Repository/AbstractPdkSettingsRepository.php 85.71% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main     #514      +/-   ##
============================================
+ Coverage     94.33%   94.36%   +0.03%     
- Complexity     2414     2428      +14     
============================================
  Files           352      355       +3     
  Lines          7726     7772      +46     
============================================
+ Hits           7288     7334      +46     
  Misses          438      438              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

NabDevs and others added 8 commits August 7, 2026 14:34
* Guard carrier settings

* test: clean carrier guard

* fix: validate carrier entries

* fix: ignore malformed carrier settings

* refactor: simplify carrier settings guard
The MyParcel APIs expose feature flags as x-dmp-* headers. Which flags we send is now a single
config entry, read by one support class and injected in both request layers: a Guzzle middleware for
the generated SDK clients, and the header list of the legacy API service. Adding or removing a flag
is a one-line config change.

New settings/behaviour: the apiFeatureFlags config entry, shipping with x-dmp-no-tracking enabled,
which makes the capabilities API return the noTracking option instead of the deprecated tracked one.

Fixes INT-1694

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The API is retiring the tracked shipment option in favour of its inverse, no tracking. Tracking is
the default wherever a carrier supports it, so the option now only expresses an explicit opt-out: it
goes to the orders API as an empty object when a merchant switched it on, and is omitted otherwise.

Swapping the entry in the order option definitions is what drives this: carrier settings, product
settings, shipment options and both settings views all derive their keys from that registry. The
option is offered per carrier and per product wherever capabilities allow it, and is no longer tied
to holding a small package contract or to the international mailbox setting, which was
carrier-specific reasoning the PDK should not carry. It still cannot be given a surcharge, and
consumers still cannot choose it in the checkout.

Requires SDK 11.0.0-beta.31, the first release carrying no_tracking on the shipment options model.

Renames to pick up when upgrading, converted for merchants by the plugin migrations:
- TrackedDefinition is removed, use NoTrackingDefinition
- CarrierSettings::$exportTracked is now $exportNoTracking, with the inverse meaning
- ProductSettings::$exportTracked is now $exportNoTracking, with the inverse meaning
- ShipmentOptions::$tracked is now $noTracking, with the inverse meaning

Fixes INT-1694

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contract definitions are cached per carrier, which is right for normal use but wrong during an
upgrade that changes the shape of the response. Callers can now pass $fresh to call the API again and
rewrite the cache, so a migration does not have to reach around the repository and delete its cache
key.

The parameter is named for what the caller wants rather than for how the cache behaves. Repository's
own $force keeps its name, since renaming a public method's parameter would break anyone calling it
with named arguments on PHP 8.

Fixes INT-1694

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A migration that has to touch every order or product cannot run inline, because a large shop would
time out. This service splits the work into chunks, hands each to the cron service as its own task,
and staggers them so they do not all fire at once.

Finding the records stays with the caller, since orders and products are queried in completely
different ways per platform. Callers pass a fetcher that returns one page of ids at a time. The key the
ids arrive under is configurable, so a caller can keep a key that already queued jobs rely on instead
of stranding work scheduled before an upgrade.

Extracted rather than copied: both plugins need this for the no tracking migration, and WooCommerce
already had a private version of it on a version-based migration.

Fixes INT-1694

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A page holding fewer records than the page size is the last one, so asking for another only costs a
round trip. This matches the loop the WooCommerce version-based migration already used, which the
service was extracted from.

Fixes INT-1694

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The parameter existed so a refactored Migration6_5_1 could keep passing its own "orderIds" key. That
migration has already run for merchants and is being left untouched, so nothing needs the parameter and
every caller uses the default. The ids are now always passed under "ids".

Fixes INT-1694

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@myparcel-bot
myparcel-bot Bot force-pushed the feat/INT-1694-no-tracking branch from a6aa8a9 to e188c27 Compare August 7, 2026 14:34
Adds a case to FeatureFlagMiddlewareTest for a request that already sets
a flag header itself. The request value wins, matching the legacy header
merge in MyParcelApiService.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants