Skip to content

Support pickup-point delivery#28

Open
CherryCIC wants to merge 2 commits into
mainfrom
codex/pickup-point-delivery-backend
Open

Support pickup-point delivery#28
CherryCIC wants to merge 2 commits into
mainfrom
codex/pickup-point-delivery-backend

Conversation

@CherryCIC
Copy link
Copy Markdown
Contributor

@CherryCIC CherryCIC commented May 20, 2026

Summary

  • Add backend support for Sendcloud pickup-point lookup with the frontend response contract under data.pickupPoints.
  • Validate pickup_point orders and persist a pickup-point snapshot on the order.
  • Resolve service-point shipping methods on the server and create Sendcloud parcels with to_service_point.
  • Add config, documentation and tests for lookup, validation, persistence and shipment payload mapping.

Review Fixes

Addressed the PR #28 review follow-up in 5e12567 fix: address PR #28 review issues:

  • Prefer the validated order.shippingOptionId before carrier-based Sendcloud config fallback for pickup-point shipments.
  • Preserve shippingWeight: 0 through normalisation so Joi rejects it with min(1) instead of applying the default weight.
  • Handle Sendcloud carrier objects in normaliseCarrier using code, then name, avoiding "[object Object]" carrier keys.
  • Send an empty house_number for pickup-point parcel destinations so locker addresses are not mixed with the buyer's home house number.
  • Added tests for shipping method priority, carrier object normalisation, zero-weight validation and pickup-point parcel house_number.

Validation

  • npm run build
  • npm test -- --runInBand
  • git diff --check

Related

And FE PR:

Summary by CodeRabbit

  • New Features
    • Added pickup-point delivery option with resolved shipping method selection and order-level pickup metadata.
  • Documentation
    • Added pickup-point delivery guide and updated environment/configuration notes.
  • Chores
    • Added Sendcloud-related environment variables and updated config/defaults; refined TypeScript include/exclude.
  • Tests
    • Expanded test coverage for order, shipping, resolver, and shipment flows including pickup-point scenarios.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

📝 Walkthrough

Walkthrough

Implements delivery-method-driven order flow with Sendcloud pickup-point support: env/config parsing for multi-carrier method IDs, order validation/input normalization for deliveryMethod, pickup-point normalization and snapshot storage on orders, shipping-method resolution for pickup_point vs home, shipment creation mapping to Sendcloud service points, tests, and docs updates.

Changes

Pickup-Point Delivery Backend

Layer / File(s) Summary
Configuration & Shipping Method Resolution Helpers
src/shared/config/sendcloudConfig.ts, src/modules/shipping/services/shippingMethodResolver.ts
sendcloudConfig parses pickup-point shipping method mappings and default weight from env; new shippingMethodResolver normalises carriers and resolves pickup-point vs home Sendcloud method IDs.
Order Validation & Input Normalization
src/modules/order/validators/orderValidator.ts, src/modules/order/tests/orderValidator.test.ts
Joi schema extended for deliveryMethod with conditional pickupPoint, address tweaks, optional/defaulted shipping fields (including default weight); added normaliseOrderBody to accept snake_case/camelCase and derive deliveryMethod; tests updated for pickup-point/home and normalization.
Order Model & Repository - Pickup Point Snapshot Storage
src/modules/order/model/Order.ts, src/modules/order/repositories/OrderRepository.ts, src/modules/order/tests/orderRepository.test.ts
Order and PickupPointSelection types extended with pickup-point identifiers, address, carrier, and geo/distance fields; repository persists and reads pickup-point snapshot fields; tests assert persisted shape.
Order Controller - Delivery-Method Shipping Resolution
src/modules/order/controllers/orderController.ts, src/modules/order/tests/orderController.test.ts
createOrder now branches on deliveryMethod: resolves pickup-point shipping via delivery options/config mapping or derives/validates home shipping method; writes resolved shipping fields and conditional pickupPointSnapshot; response payloads include order with deliveryMethod and shipmentStatus; tests verify selection and responses.
Pickup Point Normalization & Filtering
src/modules/shipping/services/CheckoutShippingService.ts, src/modules/shipping/controllers/shippingController.ts, src/modules/shipping/tests/checkoutShippingService.test.ts, src/modules/shipping/tests/shippingController.test.ts
getPickupPoints normalises Sendcloud service-point objects (alias support, composed addressLine1), filters out incomplete points via hasRequiredPickupPointFields, and shippingController defaults radius to 5000; tests validate normalization, filtering, and controller behavior.
Shipment Creation & Service Point Mapping
src/modules/shipping/services/ShipmentService.ts, src/modules/shipping/models/Shipment.ts, src/modules/shipping/services/SendcloudService.ts, src/modules/shipping/tests/shipmentService.test.ts, src/modules/shipping/tests/sendcloudService.test.ts
createShipmentForPaidOrder resolves a validated Sendcloud method id, builds destination and parcel differently for pickup_point vs home (including to_service_point and house_number handling), logs errors on Sendcloud failures, persists shippingOptionId/checkoutIdentifier from resolved id; Shipment types extended; tests added/updated accordingly.
API Contracts & Validation Messages
src/modules/order/routes/orderRoutes.ts, src/modules/shipping/routes/shippingRoutes.ts, src/modules/shipping/validators/shippingValidator.ts, src/modules/shipping/tests/shippingValidator.test.ts
OpenAPI docs updated to document deliveryMethod and conditional pickupPoint; shipping method fields documented as backend-resolved; validator messages use British spelling (metres); radius validation test added.
Documentation & Environment Configuration
.env.example, README.md, docs/pickup-point-delivery.md, tsconfig.json
Added Sendcloud env vars and default weight to .env.example, updated README env notes and guidance to source pickup-point method IDs from Sendcloud, added docs/pickup-point-delivery.md documenting flow and config, and added explicit include/exclude to tsconfig.json.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

🐰 I hopped through configs, tests, and code,
Mapped lockers where the parcels go,
Snapshots tucked in every load,
Sendcloud IDs now steer the flow,
Home or locker — hop and go!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Support pickup-point delivery' clearly and concisely summarizes the main change: adding backend support for pickup-point delivery functionality.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed Pull request description is comprehensive and well-structured, covering summary, validation steps, related links, and addressing review feedback.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/pickup-point-delivery-backend

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@CherryCIC CherryCIC changed the title [codex] Support pickup-point delivery Support pickup-point delivery May 20, 2026
@CherryCIC CherryCIC marked this pull request as ready for review May 21, 2026 11:28
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e85319fc69

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +115 to +117
const methodId = String(
configuredMethod?.id || order.shippingOptionId || '',
).trim();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Prefer saved pickup-point method over config override

resolveShippingMethodId currently prioritizes configuredMethod?.id ahead of order.shippingOptionId, so a stale or mismatched SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS entry can override the method that was validated and stored at checkout. In that case, shipment creation can use the wrong Sendcloud method (or fail) even though the order has a valid selected method, and the shipment record can diverge from the order’s shipping option.

Useful? React with 👍 / 👎.

shippingMethodId:
body.shippingMethodId || body.shipping_method_id || body.shippingOptionId,
shippingCarrier: body.shippingCarrier || body.shipping_carrier,
shippingWeight: body.shippingWeight || body.shipping_weight,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Avoid treating zero shipping weight as “missing”

Using || here coerces shippingWeight: 0 to the fallback path, which then lets Joi apply the default weight instead of failing the min(1) constraint. That means an invalid payload can silently pass and produce a shipment label with an unintended weight, masking client errors and creating incorrect shipping data. Use nullish checks so only truly absent values default.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (4)
.env.example (1)

43-43: ⚡ Quick win

Clarify the example format for multi-carrier mapping.

The example SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS=inpost_gb= shows the key format but leaves the value incomplete. Consider either showing a complete example (e.g., inpost_gb=12345) or leaving it empty like other variables to avoid confusion.

📝 Suggested improvement
-SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS=inpost_gb=
+SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS=inpost_gb=12345

Or alternatively, leave it empty to match the pattern of other variables:

-SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS=inpost_gb=
+SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS=
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.env.example at line 43, Update the .env.example entry for
SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS to show a complete example or leave
it empty to match others; specifically, replace the incomplete value
`inpost_gb=` with either a full example like `inpost_gb=12345` to demonstrate
the expected "carrier_key=shipping_method_id" mapping, or set it to an empty
value `SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS=` so it isn’t confusing—refer
to the variable name SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS and the example
carrier key `inpost_gb` when making the change.
src/modules/order/controllers/orderController.ts (2)

138-194: ⚡ Quick win

Resolve shipping after payment verification to reduce wasted Sendcloud calls.

resolvePickupPointShipping performs a live Sendcloud getDeliveryOptions call before verifySucceededPaymentIntentForUser. Any request with an invalid/unsucceeded paymentIntentId (including potential abuse traffic) will still hit Sendcloud, adding latency and external load with no benefit. Verifying the payment first would short-circuit those requests cheaply.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/order/controllers/orderController.ts` around lines 138 - 194, The
current flow calls resolvePickupPointShipping (which triggers a Sendcloud
getDeliveryOptions) before verifying payment; move the payment verification step
(verifySucceededPaymentIntentForUser in PaymentService) above any shipping
resolution to short-circuit invalid/unsucceeded paymentIntentIds and avoid
external Sendcloud calls; after successful verifySucceededPaymentIntentForUser,
run the existing shipping resolution logic (resolvePickupPointShipping for
'pickup_point' or resolveHomeShippingMethodId + normaliseCarrier for home
delivery), preserving the same ResponseHandler error responses and catch blocks
(including the specific handling for errors that include 'shipping method').

163-178: ⚡ Quick win

Distinguish validation vs. unexpected errors via an error type instead of message substring matching.

err.message.includes('shipping method') is fragile — any future error string change in resolvePickupPointShipping, getDeliveryOptions, or downstream Sendcloud calls can silently flip 500s into 400s or vice versa. Throwing a typed/sentinel error (e.g. InvalidShippingMethodError) from the resolver and instanceof-checking here would be more robust and explicit.

♻️ Sketch
-    } catch (err) {
-      if (err instanceof Error && err.message.includes('shipping method')) {
+    } catch (err) {
+      if (err instanceof InvalidShippingMethodError) {
         ResponseHandler.badRequest(
           res,
           'Invalid shipping method',
           err.message,
         );
         return;
       }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/order/controllers/orderController.ts` around lines 163 - 178, The
catch block in orderController currently classifies validation vs unexpected
errors by checking err.message.includes('shipping method'), which is fragile;
instead define and throw a specific error class (e.g.,
InvalidShippingMethodError) from the resolver functions
(resolvePickupPointShipping, getDeliveryOptions and any Sendcloud wrapper) when
the shipping method is invalid, then update the catch in orderController (the
catch handling around the resolve flow) to use instanceof
InvalidShippingMethodError to return ResponseHandler.badRequest('Invalid
shipping method', ...), and fall back to ResponseHandler.internalServerError for
all other errors.
src/modules/order/tests/orderController.test.ts (1)

241-285: 💤 Low value

Consider asserting the negative case as well.

This test confirms the auto-resolution path when exactly one pickup-point option exists, but the controller behaviour when multiple options are returned and no shippingMethodId is submitted isn't asserted anywhere in this file. Worth adding a follow-up case (e.g., returns 400 / requires explicit selection) to lock down the contract and prevent ambiguous future regressions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/order/tests/orderController.test.ts` around lines 241 - 285, Add
a negative test to cover the case where multiple pickup-point shipping options
exist but no shippingMethodId is submitted: in
src/modules/order/tests/orderController.test.ts add a new it(...) similar to the
existing "uses the only available pickup-point..." test but have the mocked
service that lists pickup options return more than one option and call
createOrder(req,res) with payloadWithoutShippingMethod; then assert that
createOrder does NOT call mockCreateOrder (or is not invoked with auto-populated
shippingOptionId) and that the response returns the expected error status (e.g.,
res.status called with 400) and error body; reference the existing symbols
createOrder, mockCreateOrder, payloadWithoutShippingMethod and
mockCreateShipmentForPaidOrder to locate where to add and how to mock the
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/modules/shipping/services/CheckoutShippingService.ts`:
- Around line 134-139: The carrier fallback can end up as a plain object (e.g.,
{name: 'InPost'}) causing String(carrier).toLowerCase() to become "[object
Object]"; update the fallback logic around the carrier variable (the expression
using point?.carrier?.code, point?.carrier_code, point?.carrier,
point?.carrierName) to prefer carrier.name and carrierName before falling back
to the raw point?.carrier object, and ensure downstream normalization (where
String(carrier).toLowerCase() is used) always receives a string (e.g., select
point?.carrier?.code || point?.carrier?.name || point?.carrierName ||
point?.carrier_code || null and coerce to a string only after choosing a scalar
value).

In `@src/modules/shipping/services/ShipmentService.ts`:
- Around line 29-57: The parcelData currently always sets house_number from
order.shipping.address.house_number which mismatches pickup-point destinations;
update the logic in ShipmentService so that when deliveryType === 'pickup_point'
(i.e. when destinationAddress is returned by getPickupPointDestination and
parcelData.to_service_point is set using toServicePointId(pickupPoint.id)) the
parcelData.house_number is omitted or taken from the pickupPoint data (from
getPickupPointDestination) instead of order.shipping.address.house_number;
adjust the construction of parcelData (or post-process it) to source
house_number from destinationAddress when present or delete it for service-point
deliveries to keep parcelData consistent with destinationAddress and
to_service_point.

---

Nitpick comments:
In @.env.example:
- Line 43: Update the .env.example entry for
SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS to show a complete example or leave
it empty to match others; specifically, replace the incomplete value
`inpost_gb=` with either a full example like `inpost_gb=12345` to demonstrate
the expected "carrier_key=shipping_method_id" mapping, or set it to an empty
value `SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS=` so it isn’t confusing—refer
to the variable name SENDCLOUD_PICKUP_POINT_SHIPPING_METHOD_IDS and the example
carrier key `inpost_gb` when making the change.

In `@src/modules/order/controllers/orderController.ts`:
- Around line 138-194: The current flow calls resolvePickupPointShipping (which
triggers a Sendcloud getDeliveryOptions) before verifying payment; move the
payment verification step (verifySucceededPaymentIntentForUser in
PaymentService) above any shipping resolution to short-circuit
invalid/unsucceeded paymentIntentIds and avoid external Sendcloud calls; after
successful verifySucceededPaymentIntentForUser, run the existing shipping
resolution logic (resolvePickupPointShipping for 'pickup_point' or
resolveHomeShippingMethodId + normaliseCarrier for home delivery), preserving
the same ResponseHandler error responses and catch blocks (including the
specific handling for errors that include 'shipping method').
- Around line 163-178: The catch block in orderController currently classifies
validation vs unexpected errors by checking err.message.includes('shipping
method'), which is fragile; instead define and throw a specific error class
(e.g., InvalidShippingMethodError) from the resolver functions
(resolvePickupPointShipping, getDeliveryOptions and any Sendcloud wrapper) when
the shipping method is invalid, then update the catch in orderController (the
catch handling around the resolve flow) to use instanceof
InvalidShippingMethodError to return ResponseHandler.badRequest('Invalid
shipping method', ...), and fall back to ResponseHandler.internalServerError for
all other errors.

In `@src/modules/order/tests/orderController.test.ts`:
- Around line 241-285: Add a negative test to cover the case where multiple
pickup-point shipping options exist but no shippingMethodId is submitted: in
src/modules/order/tests/orderController.test.ts add a new it(...) similar to the
existing "uses the only available pickup-point..." test but have the mocked
service that lists pickup options return more than one option and call
createOrder(req,res) with payloadWithoutShippingMethod; then assert that
createOrder does NOT call mockCreateOrder (or is not invoked with auto-populated
shippingOptionId) and that the response returns the expected error status (e.g.,
res.status called with 400) and error body; reference the existing symbols
createOrder, mockCreateOrder, payloadWithoutShippingMethod and
mockCreateShipmentForPaidOrder to locate where to add and how to mock the
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e9a126c3-3c76-404b-8fd3-b0bc306e8be4

📥 Commits

Reviewing files that changed from the base of the PR and between f0f0e79 and e85319f.

📒 Files selected for processing (26)
  • .env.example
  • README.md
  • docs/pickup-point-delivery.md
  • src/modules/order/controllers/orderController.ts
  • src/modules/order/model/Order.ts
  • src/modules/order/repositories/OrderRepository.ts
  • src/modules/order/routes/orderRoutes.ts
  • src/modules/order/tests/orderController.test.ts
  • src/modules/order/tests/orderRepository.test.ts
  • src/modules/order/tests/orderValidator.test.ts
  • src/modules/order/validators/orderValidator.ts
  • src/modules/shipping/controllers/shippingController.ts
  • src/modules/shipping/models/Shipment.ts
  • src/modules/shipping/routes/shippingRoutes.ts
  • src/modules/shipping/services/CheckoutShippingService.ts
  • src/modules/shipping/services/SendcloudService.ts
  • src/modules/shipping/services/ShipmentService.ts
  • src/modules/shipping/services/shippingMethodResolver.ts
  • src/modules/shipping/tests/checkoutShippingService.test.ts
  • src/modules/shipping/tests/sendcloudService.test.ts
  • src/modules/shipping/tests/shipmentService.test.ts
  • src/modules/shipping/tests/shippingController.test.ts
  • src/modules/shipping/tests/shippingValidator.test.ts
  • src/modules/shipping/validators/shippingValidator.ts
  • src/shared/config/sendcloudConfig.ts
  • tsconfig.json

Comment on lines +134 to +139
const carrier =
point?.carrier?.code ||
point?.carrier_code ||
point?.carrier ||
point?.carrierName ||
null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Carrier fallback can yield "[object object]" when carrier is an object without a code.

The fallback chain matches point?.carrier?.code first (anticipating object form), but if point.carrier is an object like { name: 'InPost' } (no code), point?.carrier_code is undefined, then point?.carrier resolves to the object itself. String(carrier).toLowerCase() on line 157 then produces "[object object]" as the normalized carrier value, which will mismatch downstream carrier-based shipping-method resolution.

🛡️ Suggested fix: prefer carrier.name before falling back to the raw object
     const carrier =
       point?.carrier?.code ||
+      point?.carrier?.name ||
       point?.carrier_code ||
-      point?.carrier ||
       point?.carrierName ||
+      (typeof point?.carrier === 'string' ? point.carrier : null) ||
       null;

Also applies to: 157-157

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/shipping/services/CheckoutShippingService.ts` around lines 134 -
139, The carrier fallback can end up as a plain object (e.g., {name: 'InPost'})
causing String(carrier).toLowerCase() to become "[object Object]"; update the
fallback logic around the carrier variable (the expression using
point?.carrier?.code, point?.carrier_code, point?.carrier, point?.carrierName)
to prefer carrier.name and carrierName before falling back to the raw
point?.carrier object, and ensure downstream normalization (where
String(carrier).toLowerCase() is used) always receives a string (e.g., select
point?.carrier?.code || point?.carrier?.name || point?.carrierName ||
point?.carrier_code || null and coerce to a string only after choosing a scalar
value).

Comment thread src/modules/shipping/services/ShipmentService.ts
@CherryCIC CherryCIC requested a review from yusuphjoluwasen May 21, 2026 11:41
@CherryCIC CherryCIC assigned JaseElder and unassigned JaseElder May 21, 2026
@CherryCIC CherryCIC requested a review from JaseElder May 21, 2026 11:41
@CherryCIC CherryCIC added help wanted Extra attention is needed MVP Backend labels May 21, 2026
- Fix shipping method resolution to prefer order.shippingOptionId over config for pickup-point delivery
- Fix shippingWeight || operator to ?? to prevent 0 bypassing min(1) validation
- Fix normaliseCarrier to handle object inputs and prevent "[object Object]" carrier codes
- Fix house_number to use empty string for pickup-point parcel destinations
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/modules/order/validators/orderValidator.ts (1)

65-70: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Implicit 'home' fallback can mask malformed client payloads.

When the request body omits all of deliveryMethod/delivery_method/deliveryType/delivery_type and pickupPoint, this normalises deliveryMethod to 'home'. The Joi required() rule on deliveryMethod will therefore never trip for this case, and a malformed client payload will be silently accepted as a home order rather than rejected as a missing field. Consider leaving deliveryMethod undefined when no signal is present so the schema's any.required error is surfaced.

🛡️ Suggested fix
   const deliveryMethod =
     body.deliveryMethod ||
     body.delivery_method ||
     body.deliveryType ||
     body.delivery_type ||
-    (body.pickupPoint ? 'pickup_point' : 'home');
+    (body.pickupPoint ? 'pickup_point' : undefined);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/order/validators/orderValidator.ts` around lines 65 - 70, The
current normalization sets deliveryMethod to 'home' by default which hides
missing-field errors; change the logic in orderValidator.ts around the
deliveryMethod computation (the deliveryMethod variable) so it only takes
body.deliveryMethod, body.delivery_method, body.deliveryType,
body.delivery_type, or 'pickup_point' when body.pickupPoint is present, and
otherwise leave deliveryMethod undefined (no implicit 'home' default) so the Joi
required() validation on deliveryMethod can surface a missing-field error.
src/modules/shipping/services/ShipmentService.ts (1)

173-178: ⚠️ Potential issue | 🔴 Critical | 💤 Low value

Fail fast on invalid to_service_point values.

Sendcloud’s API defines to_service_point as an integer; passing a non-numeric fallback string risks unexpected coercion/rejection away from the source of the error. Since toServicePointId currently returns the original string for invalid inputs, changing it to throw early on non-positive/non-integer IDs (e.g., number return type + error with the received value) will make misuse obvious and improve debugging signal.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/shipping/services/ShipmentService.ts` around lines 173 - 178, The
toServicePointId helper currently returns the original string on invalid input
which hides bad data; change private toServicePointId(servicePointId: string):
string | number to instead return a number (remove string return type) and throw
a clear error when the parsed value is not a positive integer (use
Number(servicePointId), Number.isInteger(...) and > 0) including the received
servicePointId in the error message so failures fail fast; update any callers to
expect a number or to handle the thrown error accordingly.
🧹 Nitpick comments (1)
src/modules/order/controllers/orderController.ts (1)

164-179: ⚡ Quick win

Avoid classifying errors by substring matching.

Deciding between badRequest and internalServerError based on err.message.includes('shipping method') is brittle: any rewording of the messages in resolvePickupPointShipping / resolveHomeShippingMethodId, or any unrelated downstream error that happens to contain the phrase (e.g. from CheckoutShippingService.getDeliveryOptions), will silently change the HTTP status returned to clients. Prefer a typed sentinel — for example, a dedicated InvalidShippingMethodError subclass — and instanceof-check it here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/order/controllers/orderController.ts` around lines 164 - 179,
Replace brittle substring matching on err.message with a typed sentinel error:
create a dedicated error class (e.g., InvalidShippingMethodError) and throw it
from the functions that validate shipping (e.g., resolvePickupPointShipping,
resolveHomeShippingMethodId, or CheckoutShippingService.getDeliveryOptions) when
the shipping method is invalid; then change the catch in orderController.ts to
check err instanceof InvalidShippingMethodError and call
ResponseHandler.badRequest, otherwise call ResponseHandler.internalServerError
with err.message. Ensure the new error class is exported from its module and
imported into the controller for the instanceof check.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/modules/order/validators/orderValidator.ts`:
- Around line 65-70: The current normalization sets deliveryMethod to 'home' by
default which hides missing-field errors; change the logic in orderValidator.ts
around the deliveryMethod computation (the deliveryMethod variable) so it only
takes body.deliveryMethod, body.delivery_method, body.deliveryType,
body.delivery_type, or 'pickup_point' when body.pickupPoint is present, and
otherwise leave deliveryMethod undefined (no implicit 'home' default) so the Joi
required() validation on deliveryMethod can surface a missing-field error.

In `@src/modules/shipping/services/ShipmentService.ts`:
- Around line 173-178: The toServicePointId helper currently returns the
original string on invalid input which hides bad data; change private
toServicePointId(servicePointId: string): string | number to instead return a
number (remove string return type) and throw a clear error when the parsed value
is not a positive integer (use Number(servicePointId), Number.isInteger(...) and
> 0) including the received servicePointId in the error message so failures fail
fast; update any callers to expect a number or to handle the thrown error
accordingly.

---

Nitpick comments:
In `@src/modules/order/controllers/orderController.ts`:
- Around line 164-179: Replace brittle substring matching on err.message with a
typed sentinel error: create a dedicated error class (e.g.,
InvalidShippingMethodError) and throw it from the functions that validate
shipping (e.g., resolvePickupPointShipping, resolveHomeShippingMethodId, or
CheckoutShippingService.getDeliveryOptions) when the shipping method is invalid;
then change the catch in orderController.ts to check err instanceof
InvalidShippingMethodError and call ResponseHandler.badRequest, otherwise call
ResponseHandler.internalServerError with err.message. Ensure the new error class
is exported from its module and imported into the controller for the instanceof
check.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a238df40-11b9-405d-b4ae-915b940621ed

📥 Commits

Reviewing files that changed from the base of the PR and between e85319f and 5e12567.

📒 Files selected for processing (7)
  • src/modules/order/controllers/orderController.ts
  • src/modules/order/tests/orderValidator.test.ts
  • src/modules/order/validators/orderValidator.ts
  • src/modules/shipping/services/ShipmentService.ts
  • src/modules/shipping/services/shippingMethodResolver.ts
  • src/modules/shipping/tests/shipmentService.test.ts
  • src/modules/shipping/tests/shippingMethodResolver.test.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Backend help wanted Extra attention is needed MVP

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants