Skip to content

Courier request: align pickup time with DPD's slot contract, fix crash on JSON API responses - #181

Merged
GytisZum merged 1 commit into
DPDBaltics:DGS-446-courier-request-pickup-time-slotsfrom
GantasG:DGS-446-courier-request-pickup-time-slots
Aug 31, 2026
Merged

Courier request: align pickup time with DPD's slot contract, fix crash on JSON API responses#181
GytisZum merged 1 commit into
DPDBaltics:DGS-446-courier-request-pickup-time-slotsfrom
GantasG:DGS-446-courier-request-pickup-time-slots

Conversation

@GantasG

@GantasG GantasG commented Aug 31, 2026

Copy link
Copy Markdown

Problem

Since DPD's live API contract change, no courier request can be created through the module (reported in #179; tracked internally as DGS-444 / DGS-446):

  1. The live pickupOrderSave_ endpoint now requires a pickupTimeFrom/pickupTimeTo slot pair from fixed whitelists; the module still sends a single free-form pickupTime datetime, so every request is rejected:
    Available "pickupTimeFrom" values: [08:00, 09:00, 10:00, 11:00, 12:00, 12:30, 13:00, 13:30, 14:00, 14:30, 15:00]. Available "pickupTimeTo" values: [15:00, 16:00, 17:00, 18:00].
    
  2. That rejection is a JSON object, which the module's error handling passes to strpos() as if it were a string — on PHP 8 that's a fatal TypeError at CourierRequestService.php:109, so the BO crashes with a 500 instead of showing DPD's message.
  3. Independently, the auto-filled default pick-up time is a raw 'now + 30 min' second-precision value that never lands on a DPD slot.

Both reproduced on stock v3.3.1 (PS 8.2.7 / PHP 8.1) with the live endpoint's exact rejection replayed by a local stub.

What changed

Request side:

  • The 'Desired pick-up time' / 'Last pick-up time' datetime inputs are replaced with a Pick-up date field and two slot dropdowns constrained to DPD's whitelists.
  • Defaults are slot-aligned: next available slot today, or tomorrow 08:00 after the last slot.
  • CourierRequestValidator::validatePickupTimeSlots() re-checks the whitelist server-side.
  • The API payload now carries pickupDate/pickupTimeFrom/pickupTimeTo alongside the legacy pickupTime/senderWorkUntil datetimes — kept for DPD endpoints still on the old contract (EE/LV) and for the module's own DPDCourierRequest entity (no DB schema change; stored columns are composed from the slot fields).

Response side:

  • New CourierRequestResponseParser accepts both response shapes: legacy <p>DONE / 402 Error: strings and the new JSON object, whose body->message (or errlog) is shown to the merchant instead of crashing.

Tests: CourierRequestResponseParserTest (8 tests, incl. a stdClass fixture of the exact live rejection), PickupTimeSlotUtilityTest (4), CourierRequestValidatorTest (6) — green on PHP 8.1 / PHPUnit 8.5. The unit-test bootstrap now defines _PS_VERSION_, without which any test touching module classes silently exits on the file guards.

Depends on

Invertus/dpdbaltics-api#6 — adds the slot setters to CourierRequestRequest (that package's jsonSerialize() builds the payload, so the new params can't reach the wire from module code alone). Merged into developer; composer.lock here is bumped to that commit (e372e0b). A fresh composer install of this branch now pulls the updated DTO.

Verification (local PS 8.2.7 / PHP 8.1, live LT endpoint stubbed)

Captured outgoing request after the fix:

pickupTime=2026-08-31 14:00:00   senderWorkUntil=2026-08-31 17:00:00   (legacy, kept)
pickupDate=2026-08-31   pickupTimeFrom=14:00   pickupTimeTo=17:00     (new)
  • DPD rejection → shown as a normal BO error, no 500.
  • Success response (<p>DONE) → 'Successful creation', record saved, list view unchanged.
  • Tampered POST with a non-slot value → rejected server-side with the whitelist message.
  • Defaults land on valid slots (e.g. 13:52 → 14:00).

Verdict: ship with follow-ups (below).

Needs human verification

  • One real submission against live integracijos.dpd.lt with production credentials. The new param names match DPD's current API data model and the live error message, but the new ws-mapper contract isn't publicly documented and DPD's sandbox doesn't enforce it, so the exact wire naming is unverified against production. If DPD expects different naming, the merchant now sees DPD's answer as a readable error instead of a crash.
  • Success-response shape of the new contract: the parser treats legacy DONE strings as success and any JSON object as a surfaced error; if the live endpoint starts answering success as JSON, that shape needs adding.

Confidence: 75%

…ontract

DPD's live pickupOrderSave_ endpoint now rejects any pickup time that is not
one of its fixed pickupTimeFrom/pickupTimeTo slots, and answers with a JSON
object instead of the legacy plain-text response.

Request side:
- The 'Desired pick-up time' / 'Last pick-up time' datetime inputs are
  replaced with a pick-up date field and two slot dropdowns constrained
  to DPD's whitelists, so an invalid value can no longer be submitted.
- Defaults are slot-aligned (next available slot today, or tomorrow 08:00)
  instead of the raw 'now + 30 min' value DPD always rejected.
- CourierRequestValidator rejects non-whitelisted slots server-side.
- The API request now carries pickupDate/pickupTimeFrom/pickupTimeTo alongside
  the legacy pickupTime/senderWorkUntil datetimes, which are kept for DPD
  endpoints still running the old contract and for the module's own entity.

Response side:
- CourierRequestResponseParser accepts both response shapes: the legacy
  '<p>DONE' / '402 Error:' strings and the new JSON object, whose
  body->message (or errlog) is surfaced to the merchant. Previously the JSON
  object reached strpos() and fatally crashed the admin page on PHP 8.

The unit-test bootstrap now defines _PS_VERSION_, without which every test
touching module classes silently exits on the file guards.

Requires invertus/dpdbaltics-api with CourierRequestRequest slot setters
(companion PR on that repo); composer.lock to be bumped once it lands on
the developer branch.
@GantasG
GantasG marked this pull request as ready for review August 31, 2026 11:01
@GytisZum
GytisZum changed the base branch from main to DGS-446-courier-request-pickup-time-slots August 31, 2026 11:19
@GytisZum
GytisZum merged commit 8ac62dd into DPDBaltics:DGS-446-courier-request-pickup-time-slots Aug 31, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants