feat: correct B2B/B2C capabilities via an address-based isBusiness flag - #504
Conversation
There was a problem hiding this comment.
Pull request overview
This PR fixes incorrect B2B/B2C behavior in capabilities lookups by preserving a true tri-state isBusiness on the wire (true/false/omitted) and by deriving isBusiness from the presence of a recipient company name (without leaking the company into PII-free contexts like the cart/config).
Changes:
- Ensure capabilities requests omit
isBusinessunless explicitly provided by the caller (workaround for generated SDK defaulting to business). - Derive and propagate
isBusinessfrom address/company data through order calculators and checkout carrier filtering, while keeping cart context PII-free. - Update unit tests and snapshots to reflect the new
isBusinesspropagation/serialization.
Reviewed changes
Copilot reviewed 46 out of 46 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Unit/SdkApi/Service/CoreApi/Shipment/CapabilitiesServiceTest.php | Adds coverage for tri-state isBusiness wire behavior (true/false/omitted). |
| tests/Unit/Base/Model/ContactDetailsTest.php | Verifies ContactDetails derives/updates isBusiness from company. |
| tests/Unit/Base/Model/AddressTest.php | Updates expected address serialization and adds isBusiness derivation tests. |
| tests/Unit/App/Order/Calculator/CapabilitiesOptionCalculatorTest.php | Asserts order capabilities calls send isBusiness based on shipping address company. |
| tests/Unit/App/DeliveryOptions/Service/DeliveryOptionsServiceCapabilitiesTest.php | Asserts checkout carrier-filter capabilities calls include derived isBusiness. |
| tests/Unit/App/Context/Model/DeliveryOptionsConfigTest.php | Ensures checkout config exposes isBusiness to the widget. |
| tests/Unit/App/Cart/Service/CartCalculationServiceTest.php | Updates cart address fixtures to include isBusiness. |
| tests/snapshots/ShipmentTest__it_returns_empty_fulfilment_shipment_when_no_pdk_shipment_is_passed__1.json | Snapshot update: include recipient isBusiness default. |
| tests/snapshots/ShipmentTest__it_creates_fulfilment_shipment_from_pdk_shipment_with_data_set_shipment_with_all_options__1.json | Snapshot update: include recipient isBusiness. |
| tests/snapshots/ShipmentTest__it_creates_fulfilment_shipment_from_pdk_shipment_with_data_set_empty_shipment__1.json | Snapshot update: include recipient isBusiness default. |
| tests/snapshots/SaveOrderTest__it_creates_a_valid_order_collection_from_api_data_with_data_set_order_with_pickup__1.json | Snapshot update: include address isBusiness. |
| tests/snapshots/SaveOrderTest__it_creates_a_valid_order_collection_from_api_data_with_data_set_order_containing_many_attributes__1.json | Snapshot update: include address isBusiness. |
| tests/snapshots/QueryTest__it_creates_shipment_collection_from_queried_data_with_data_set_shipment_with_pickup__1.json | Snapshot update: include isBusiness on sender/recipient. |
| tests/snapshots/QueryTest__it_creates_shipment_collection_from_queried_data_with_data_set_shipment_with_drop-off_point__1.json | Snapshot update: include isBusiness on sender/recipient. |
| tests/snapshots/QueryTest__it_creates_shipment_collection_from_queried_data_with_data_set_shipment_with_contract__1.json | Snapshot update: include isBusiness on sender/recipient. |
| tests/snapshots/QueryTest__it_creates_shipment_collection_from_queried_data_with_data_set_normal_shipment__1.json | Snapshot update: include isBusiness on sender/recipient. |
| tests/snapshots/QueryTest__it_creates_order_collection_from_queried_data__1.json | Snapshot update: include isBusiness in queried order addresses. |
| tests/snapshots/PostOrdersTest__it_creates_a_valid_order_collection_from_api_data_with_data_set_one_order_with_pickup__1.json | Snapshot update: include address isBusiness. |
| tests/snapshots/PostOrdersTest__it_creates_a_valid_order_collection_from_api_data_with_data_set_one_order_containing_many_attributes__1.json | Snapshot update: include address isBusiness. |
| tests/snapshots/PdkOrderTest__it_creates_pdk_order_from_fulfilment_order_with_data_set_one_order_with_pickup__1.json | Snapshot update: include shippingAddress isBusiness. |
| tests/snapshots/PdkOrderTest__it_creates_pdk_order_from_fulfilment_order_with_data_set_one_order_containing_many_attributes__1.json | Snapshot update: include shippingAddress isBusiness. |
| tests/snapshots/OrderTest__it_creates_fulfilment_order_from_pdk_order_with_data_set_order_without_shipments__1.json | Snapshot update: include recipient isBusiness. |
| tests/snapshots/OrderTest__it_creates_fulfilment_order_from_pdk_order_with_data_set_order_with_shipments__1.json | Snapshot update: include recipient isBusiness default. |
| tests/snapshots/OrderTest__it_creates_fulfilment_order_from_pdk_order_with_data_set_empty_order__1.json | Snapshot update: include recipient isBusiness default. |
| tests/snapshots/FrontendRenderServiceTest__it_renders_component_with_data_set_order_list_column__1.json | Snapshot update: include shippingAddress isBusiness. |
| tests/snapshots/FrontendRenderServiceTest__it_renders_component_with_data_set_order_box__1.json | Snapshot update: include shippingAddress isBusiness. |
| tests/snapshots/FrontendRenderServiceTest__it_renders_component_with_data_set_init_script__1.json | Snapshot update: include isBusiness and endpoint ordering changes. |
| tests/snapshots/FrontendRenderServiceTest__it_renders_component_with_data_set_delivery_options__1.json | Snapshot update: include config isBusiness. |
| tests/snapshots/ExportReturnActionTest__it_exports_return_with_data_set_simple_orders__1.json | Snapshot update: include isBusiness on addresses. |
| tests/snapshots/ExportReturnActionTest__it_exports_return_with_data_set_no_return_capabilities__1.json | Snapshot update: include shippingAddress/recipient isBusiness. |
| tests/snapshots/ExportReturnActionTest__it_exports_return_with_data_set_insurance__1.json | Snapshot update: include shippingAddress/recipient isBusiness. |
| tests/snapshots/CreateReturnShipmentsTest__it_creates_return_shipment_with_data_set_simple_domestic_shipment__1.json | Snapshot update: include isBusiness on addresses. |
| tests/snapshots/ContextServiceTest__it_gets_context_data_with_data_set_single_order__1.json | Snapshot update: include shippingAddress isBusiness. |
| tests/snapshots/ContextServiceTest__it_gets_context_data_with_data_set_multiple_orders__1.json | Snapshot update: include shippingAddress isBusiness in context. |
| tests/snapshots/AccountTest__it_creates_storable_array__1.json | Snapshot update: include account address isBusiness. |
| src/SdkApi/Service/CoreApi/Shipment/CapabilitiesService.php | Implements tri-state isBusiness omission workaround before SDK serialization. |
| src/Context/Model/DeliveryOptionsConfig.php | Adds isBusiness to widget config derived from cart shipping address. |
| src/Carrier/Service/CapabilitiesValidationService.php | Threads optional isBusiness through package-type weight aggregation for cache alignment. |
| src/Base/Model/ContactDetails.php | Keeps isBusiness synced when company is set/changed. |
| src/Base/Model/Address.php | Adds isBusiness attribute + derivation rule (company presence → business). |
| src/App/Order/Calculator/General/InsuranceCalculator.php | Uses shared capabilities-recipient builder (includes isBusiness). |
| src/App/Order/Calculator/General/CapabilitiesPackageTypeCalculator.php | Uses shared capabilities-recipient builder (includes isBusiness). |
| src/App/Order/Calculator/General/CapabilitiesOptionCalculator.php | Uses shared capabilities-recipient builder (includes isBusiness). |
| src/App/Order/Calculator/General/CapabilitiesDeliveryTypeCalculator.php | Uses shared capabilities-recipient builder (includes isBusiness). |
| src/App/Order/Calculator/AbstractPdkOrderOptionCalculator.php | Introduces shared capabilitiesRecipient() helper for calculators. |
| src/App/DeliveryOptions/Service/DeliveryOptionsService.php | Ensures checkout carrier filtering and type ordering include isBusiness in capabilities args. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #504 +/- ##
============================================
+ Coverage 94.29% 94.31% +0.01%
- Complexity 2391 2398 +7
============================================
Files 351 351
Lines 7663 7689 +26
============================================
+ Hits 7226 7252 +26
Misses 437 437 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
the admin now tells the capabilities request whether an order is for a business or a consumer, so it gets back the right options. It reads a true/false flag (isBusiness) that the PDK works out from the recipient's company name, and passes it along on both capabilities requests. Only that flag travels — never the company name or other personal data — and it's left off when it isn't set. This is the admin side of the PDK change in myparcelnl/pdk#504. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
59994d0 to
3cd2026
Compare
the admin now tells the capabilities request whether an order is for a business or a consumer, so it gets back the right options. It reads a true/false flag (isBusiness) that the PDK works out from the recipient's company name, and passes it along on both capabilities requests. Only that flag travels — never the company name or other personal data — and it's left off when it isn't set. This is the admin side of the PDK change in myparcelnl/pdk#504. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the checkout now tells the capabilities check whether an order is for a business or a consumer, so each cart sees the carriers and options that apply to it. The flag comes from whether the delivery address has a company name. The cart repository passes the company to the PDK, which turns it into an isBusiness flag and drops the name (so no personal data is stored), and the carrier-list hook derives the same flag for its own capabilities request. Only the true/false flag is sent, never the company name. Depends on myparcelnl/pdk#504 — needs a PDK release with the isBusiness flag before this can merge; the tests here rely on it. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
218db0b to
5809bfe
Compare
the admin now tells the capabilities request whether an order is for a business or a consumer, so it gets back the right options. It reads a true/false flag (isBusiness) that the PDK works out from the recipient's company name, and passes it along on both capabilities requests. Only that flag travels — never the company name or other personal data — and it's left off when it isn't set. This is the admin side of the PDK change in myparcelnl/pdk#504. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the admin now tells the capabilities request whether an order is for a business or a consumer, so it gets back the right options. It reads a true/false flag (isBusiness) that the PDK works out from the recipient's company name, and passes it along on both capabilities requests. Only that flag travels — never the company name or other personal data — and it's left off when it isn't set. This is the admin side of the PDK change in myparcelnl/pdk#504. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
5809bfe to
837c5a2
Compare
the admin now tells the capabilities request whether an order is for a business or a consumer, so it gets back the right options. It reads a true/false flag (isBusiness) that the PDK works out from the recipient's company name, and passes it along on both capabilities requests. Only that flag travels — never the company name or other personal data — and it's left off when it isn't set. This is the admin side of the PDK change in myparcelnl/pdk#504. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the admin now tells the capabilities request whether an order is for a business or a consumer, so it gets back the right options. It reads a true/false flag (isBusiness) that the PDK works out from the recipient's company name, and passes it along on both capabilities requests. Only that flag travels — never the company name or other personal data — and it's left off when it isn't set. This is the admin side of the PDK change in myparcelnl/pdk#504. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the admin now tells the capabilities request whether an order is for a business or a consumer, so it gets back the right options. It reads a true/false flag (isBusiness) that the PDK works out from the recipient's company name, and passes it along on both capabilities requests. Only that flag travels — never the company name or other personal data — and it's left off when it isn't set. This is the admin side of the PDK change in myparcelnl/pdk#504. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the admin now tells the capabilities request whether an order is for a business or a consumer, so it gets back the right options. It reads a true/false flag (isBusiness) that the PDK works out from the recipient's company name, and passes it along on both capabilities requests. Only that flag travels — never the company name or other personal data — and it's left off when it isn't set. This is the admin side of the PDK change in myparcelnl/pdk#504. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the admin now tells the capabilities request whether an order is for a business or a consumer, so it gets back the right options. It reads a true/false flag (isBusiness) that the PDK works out from the recipient's company name, and passes it along on both capabilities requests. Only that flag travels — never the company name or other personal data — and it's left off when it isn't set. This is the admin side of the PDK change in myparcelnl/pdk#504. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The generated SDK forces isBusiness=true on capabilities recipients when the flag is unset, so every capabilities request was silently treated as B2B — returning B2B package types, weight limits and options for consumer shipments. Capabilities requests now omit isBusiness unless a caller sets it explicitly, letting the API apply its own default. Temporary workaround for the OpenAPI spec default; see the @todo referencing myparcelnl/core-api#4658. Explicit true/false values are always respected. Resolves INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rect capabilities set Capabilities requests now carry an explicit isBusiness flag derived from whether the recipient has a company name, so business and consumer shipments receive the delivery types, package types, weight limits and shipment options that actually apply to them. The flag flows through the whole order option calculator chain and the checkout carrier filter, not only the delivery options. Detection lives in one place on the Address model: a company name yields isBusiness=true, its absence false. Only the derived boolean — never the company name — travels into the PII-free cart context, so no personal data leaks there. Context-less queries (weight aggregation, return support) omit the flag. Resolves INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SDK beta.30 (sdk#631) defaults isBusiness to null, so an unset flag is left off the capabilities request on its own. This removes the local omission we added as a temporary bridge — the three-state behaviour (true, false, or left off) is unchanged. Bumps the SDK requirement to ^11.0.0-beta.30. Resolves INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…he cart adds a test proving fromCart(cart with a company) exposes isBusiness on the config the checkout widget reads. Resolves INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
837c5a2 to
0a88a74
Compare
NabDevs
left a comment
There was a problem hiding this comment.
Looks good, 1 potential issue that needs to be checked I think.
the admin now tells the capabilities request whether an order is for a business or a consumer, so it gets back the right options. It reads a true/false flag (isBusiness) that the PDK works out from the recipient's company name, and passes it along on both capabilities requests. Only that flag travels — never the company name or other personal data — and it's left off when it isn't set. This is the admin side of the PDK change in myparcelnl/pdk#504. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the checkout now tells the capabilities check whether an order is for a business or a consumer, so each cart sees the carriers and options that apply to it. The flag comes from whether the delivery address has a company name. The cart repository passes the company to the PDK, which turns it into an isBusiness flag and drops the name (so no personal data is stored), and the carrier-list hook derives the same flag for its own capabilities request. Only the true/false flag is sent, never the company name. Depends on myparcelnl/pdk#504 — needs a PDK release with the isBusiness flag before this can merge; the tests here rely on it. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the checkout now tells the capabilities check whether an order is for a business or a consumer, so each cart sees the carriers and options that apply to it. The flag comes from whether the delivery address has a company name. The cart repository passes the company to the PDK, which turns it into an isBusiness flag and drops the name (so no personal data is stored), and the carrier-list hook derives the same flag for its own capabilities request. Only the true/false flag is sent, never the company name. Depends on myparcelnl/pdk#504 — needs a PDK release with the isBusiness flag before this can merge; the tests here rely on it. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
the checkout now tells the capabilities check whether an order is for a business or a consumer, so each cart sees the carriers and options that apply to it. The flag comes from whether the delivery address has a company name. The cart repository passes the company to the PDK, which turns it into an isBusiness flag and drops the name (so no personal data is stored), and the carrier-list hook derives the same flag for its own capabilities request. Only the true/false flag is sent, never the company name. Depends on myparcelnl/pdk#504 — needs a PDK release with the isBusiness flag before this can merge; the tests here rely on it. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
## [4.5.0](v4.4.2...v4.5.0) (2026-08-05) ### ✨ New Features * correct B2B/B2C capabilities via an address-based isBusiness flag ([#504](#504)) ([2bc58a5](2bc58a5)), closes [myparcelnl/core-api#4658](https://github.com/myparcelnl/core-api/issues/4658) [sdk#631](myparcelnl/sdk#631)
|
🎉 This PR is included in version 4.5.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
#359) the admin now tells the capabilities request whether an order is for a business or a consumer, so it gets back the right options. It reads a true/false flag (isBusiness) that the PDK works out from the recipient's company name, and passes it along on both capabilities requests. Only that flag travels — never the company name or other personal data — and it's left off when it isn't set. This is the admin side of the PDK change in myparcelnl/pdk#504. Fixes INT-1690 Depends on myparcelnl/pdk#504 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…merce cart adds a test proving a cart's company name reaches the PDK as the isBusiness flag — and that the company itself is not stored on the cart — plus a mock update to match the PDK's new getPackageTypeWeights signature. WooCommerce needs no production change: its address adapter already sends the company, and the PDK turns it into the flag. The test guards that boundary so it can't silently break. Depends on myparcelnl/pdk#504 — the assertions rely on the PDK isBusiness flag, so this can merge once a PDK release ships it. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…merce cart adds a test proving a cart's company name reaches the PDK as the isBusiness flag — and that the company itself is not stored on the cart — plus a mock update to match the PDK's new getPackageTypeWeights signature. WooCommerce needs no production change: its address adapter already sends the company, and the PDK turns it into the flag. The test guards that boundary so it can't silently break. Depends on myparcelnl/pdk#504 — the assertions rely on the PDK isBusiness flag, so this can merge once a PDK release ships it. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…umer orders (#1675) * test: cover the business/consumer flag reaching the PDK from a WooCommerce cart adds a test proving a cart's company name reaches the PDK as the isBusiness flag — and that the company itself is not stored on the cart — plus a mock update to match the PDK's new getPackageTypeWeights signature. WooCommerce needs no production change: its address adapter already sends the company, and the PDK turns it into the flag. The test guards that boundary so it can't silently break. Depends on myparcelnl/pdk#504 — the assertions rely on the PDK isBusiness flag, so this can merge once a PDK release ships it. Fixes INT-1690 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * build(deps): bump pdk to 4.5.0 and admin packages to 2.1.0 Points the plugin at the releases that carry the business/consumer flag. The PHP tests assert an isBusiness flag on order addresses, which ships in PDK 4.5.0. The admin needs pdk-admin 2.1.0 so it actually sends that flag when it loads shipment options. Deduped the myparcel packages afterwards so pdk-common stays a single copy instead of ending up at both 2.0.0 and 2.1.0. Fixes INT-1690 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds support for explicit
isBusinessflags based on the adress (a filled company name denotes a B2B shipment, an empty one sets it to false and makes it B2C) and fixes issue where it was set totrueinstead ofnullNote
Follow-ups (separate PRs, other repos)
order.shippingAddress.isBusinesson the proxy capabilities call.isBusinessboolean on the recipient (no company field on the widget address).companyinto the cart array + reuse the rule in the carrier-list hook. WooCommerce: test-only (its adapter already supplies the company).Fixes INT-1690
🤖 Generated with Claude Code