Skip to content

Add missing addParcelToDelivery order update action #309

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/shiny-moons-hear.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@labdigital/commercetools-mock": patch
---

Add missing addParcelToDelivery order update action
91 changes: 90 additions & 1 deletion src/repositories/order/actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import type {
CustomLineItemReturnItem,
InvalidInputError,
LineItemReturnItem,
Order,
OrderAddParcelToDeliveryAction,
OrderAddPaymentAction,
OrderAddReturnInfoAction,
OrderChangeOrderStateAction,
Expand All @@ -22,16 +24,18 @@ import type {
OrderTransitionStateAction,
OrderUpdateAction,
OrderUpdateSyncInfoAction,
Parcel,
ReturnInfo,
State,
Store,
SyncInfo,
} from "@commercetools/platform-sdk";
import { CommercetoolsError } from "~src/exceptions";
import { getBaseResourceProperties } from "~src/helpers";
import type { Writable } from "~src/types";
import type { RepositoryContext, UpdateHandlerInterface } from "../abstract";
import { AbstractUpdateHandler } from "../abstract";
import { createAddress } from "../helpers";
import { createAddress, createCustomFields } from "../helpers";

export class OrderUpdateHandler
extends AbstractUpdateHandler
Expand Down Expand Up @@ -62,6 +66,91 @@ export class OrderUpdateHandler
});
}

addParcelToDelivery(
context: RepositoryContext,
resource: Writable<Order>,
{
deliveryId,
deliveryKey,
parcelKey,
measurements,
trackingData,
items,
custom,
}: OrderAddParcelToDeliveryAction,
) {
if (!resource.shippingInfo) {
throw new CommercetoolsError<InvalidInputError>({
code: "InvalidInput",
message: "Order has no shipping info",
errors: [
{
code: "InvalidInput",
message: "Order has no shipping info",
},
],
});
}

if (!deliveryId && !deliveryKey) {
throw new CommercetoolsError<InvalidInputError>({
code: "InvalidInput",
message: "Either deliveryId or deliveryKey must be provided",
errors: [
{
code: "InvalidInput",
message: "Either deliveryId or deliveryKey must be provided",
},
],
});
}

// Find the delivery by id or key
let targetDelivery = null;
for (const delivery of resource.shippingInfo.deliveries || []) {
if (
(deliveryId && delivery.id === deliveryId) ||
(deliveryKey && delivery.key === deliveryKey)
) {
targetDelivery = delivery;
break;
}
}

if (!targetDelivery) {
const identifier = deliveryId || deliveryKey;
const message = `Delivery with ${deliveryId ? "id" : "key"} '${identifier}' not found`;
throw new CommercetoolsError<InvalidInputError>({
code: "InvalidInput",
message,
errors: [
{
code: "InvalidInput",
message,
},
],
});
}

// Create the new parcel
const newParcel: Parcel = {
...getBaseResourceProperties(),
...(parcelKey && { key: parcelKey }),
...(measurements && { measurements }),
...(trackingData && { trackingData }),
items: items || [],
...(custom && {
custom: createCustomFields(custom, context.projectKey, this._storage),
}),
};

// Add the parcel to the delivery
if (!targetDelivery.parcels) {
targetDelivery.parcels = [];
}
targetDelivery.parcels.push(newParcel);
}

addReturnInfo(
context: RepositoryContext,
resource: Writable<Order>,
Expand Down
245 changes: 245 additions & 0 deletions src/services/order.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -794,6 +794,251 @@ describe("Order Update Actions", () => {
},
]);
});

test("addParcelToDelivery", async () => {
const order: Order = {
...getBaseResourceProperties(),
customLineItems: [],
lastMessageSequenceNumber: 0,
lineItems: [],
orderNumber: "1391",
orderState: "Open",
origin: "Customer",
refusedGifts: [],
shippingInfo: {
shippingMethodName: "Standard delivery",
price: {
type: "centPrecision",
currencyCode: "EUR",
centAmount: 500,
fractionDigits: 2,
},
shippingRate: {
price: {
type: "centPrecision",
currencyCode: "EUR",
centAmount: 500,
fractionDigits: 2,
},
tiers: [],
},
deliveries: [
{
id: "delivery-1",
key: "DELIVERY-001",
createdAt: "2024-01-01T10:00:00.000Z",
items: [
{
id: "line-item-1",
quantity: 2,
},
],
parcels: [],
},
],
shippingMethodState: "MatchesCart",
},
shipping: [],
shippingMode: "Single",
syncInfo: [],
totalPrice: {
type: "centPrecision",
fractionDigits: 2,
centAmount: 1500,
currencyCode: "EUR",
},
};
ctMock.project("dummy").add("order", order);

const response = await supertest(ctMock.app).get(
`/dummy/orders/order-number=${order.orderNumber}`,
);
expect(response.status).toBe(200);

// Test adding parcel by deliveryId
const updateResponse = await supertest(ctMock.app)
.post(`/dummy/orders/${response.body.id}`)
.send({
version: 0,
actions: [
{
action: "addParcelToDelivery",
deliveryId: "delivery-1",
parcelKey: "parcel-001",
measurements: {
heightInMillimeter: 100,
lengthInMillimeter: 200,
widthInMillimeter: 150,
weightInGram: 500,
},
trackingData: {
trackingId: "TRACK123",
carrier: "DHL",
},
items: [
{
id: "line-item-1",
quantity: 1,
},
],
},
],
});
expect(updateResponse.status).toBe(200);
expect(updateResponse.body.version).toBe(1);

const delivery = updateResponse.body.shippingInfo.deliveries[0];
expect(delivery.parcels).toHaveLength(1);

const parcel = delivery.parcels[0];
expect(parcel.key).toBe("parcel-001");
expect(parcel.measurements).toMatchObject({
heightInMillimeter: 100,
lengthInMillimeter: 200,
widthInMillimeter: 150,
weightInGram: 500,
});
expect(parcel.trackingData).toMatchObject({
trackingId: "TRACK123",
carrier: "DHL",
});
expect(parcel.items).toMatchObject([
{
id: "line-item-1",
quantity: 1,
},
]);

// Test adding parcel by deliveryKey
const updateResponse2 = await supertest(ctMock.app)
.post(`/dummy/orders/${response.body.id}`)
.send({
version: 1,
actions: [
{
action: "addParcelToDelivery",
deliveryKey: "DELIVERY-001",
parcelKey: "parcel-002",
items: [
{
id: "line-item-1",
quantity: 1,
},
],
},
],
});
expect(updateResponse2.status).toBe(200);
expect(updateResponse2.body.version).toBe(2);

const delivery2 = updateResponse2.body.shippingInfo.deliveries[0];
expect(delivery2.parcels).toHaveLength(2);
expect(delivery2.parcels[1].key).toBe("parcel-002");
});

test("addParcelToDelivery - error cases", async () => {
const order: Order = {
...getBaseResourceProperties(),
customLineItems: [],
lastMessageSequenceNumber: 0,
lineItems: [],
orderNumber: "1392",
orderState: "Open",
origin: "Customer",
refusedGifts: [],
shippingInfo: {
shippingMethodName: "Standard delivery",
price: {
type: "centPrecision",
currencyCode: "EUR",
centAmount: 500,
fractionDigits: 2,
},
shippingRate: {
price: {
type: "centPrecision",
currencyCode: "EUR",
centAmount: 500,
fractionDigits: 2,
},
tiers: [],
},
deliveries: [
{
id: "delivery-1",
key: "DELIVERY-001",
createdAt: "2024-01-01T10:00:00.000Z",
items: [],
parcels: [],
},
],
shippingMethodState: "MatchesCart",
},
shipping: [],
shippingMode: "Single",
syncInfo: [],
totalPrice: {
type: "centPrecision",
fractionDigits: 2,
centAmount: 1500,
currencyCode: "EUR",
},
};
ctMock.project("dummy").add("order", order);

const response = await supertest(ctMock.app).get(
`/dummy/orders/order-number=${order.orderNumber}`,
);
expect(response.status).toBe(200);

// Test error: no deliveryId or deliveryKey provided
const errorResponse1 = await supertest(ctMock.app)
.post(`/dummy/orders/${response.body.id}`)
.send({
version: 0,
actions: [
{
action: "addParcelToDelivery",
parcelKey: "parcel-001",
},
],
});
expect(errorResponse1.status).toBe(400);

// Test error: delivery not found
const errorResponse2 = await supertest(ctMock.app)
.post(`/dummy/orders/${response.body.id}`)
.send({
version: 0,
actions: [
{
action: "addParcelToDelivery",
deliveryId: "nonexistent-delivery",
parcelKey: "parcel-001",
},
],
});
expect(errorResponse2.status).toBe(400);
});

test("addParcelToDelivery - order without shipping info", async () => {
assert(order, "order not created");

// Test error: order has no shipping info
const errorResponse = await supertest(ctMock.app)
.post(`/dummy/orders/${order.id}`)
.send({
version: 1,
actions: [
{
action: "addParcelToDelivery",
deliveryId: "delivery-1",
parcelKey: "parcel-001",
},
],
});
expect(errorResponse.status).toBe(400);
});
});

describe("Order Import", () => {
Expand Down
Loading