Skip to content

Commit 243659d

Browse files
Implement addParcelToDelivery order update action with comprehensive tests
Co-authored-by: mvantellingen <245297+mvantellingen@users.noreply.github.com>
1 parent 2092397 commit 243659d

File tree

3 files changed

+312
-1
lines changed

3 files changed

+312
-1
lines changed

.changeset/shiny-moons-hear.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@labdigital/commercetools-mock": patch
3+
---
4+
5+
Add missing addParcelToDelivery order update action

src/repositories/order/actions.ts

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
CustomLineItemReturnItem,
33
LineItemReturnItem,
44
Order,
5+
OrderAddParcelToDeliveryAction,
56
OrderAddPaymentAction,
67
OrderAddReturnInfoAction,
78
OrderChangeOrderStateAction,
@@ -22,6 +23,7 @@ import type {
2223
OrderTransitionStateAction,
2324
OrderUpdateAction,
2425
OrderUpdateSyncInfoAction,
26+
Parcel,
2527
ReturnInfo,
2628
State,
2729
Store,
@@ -31,7 +33,7 @@ import { getBaseResourceProperties } from "~src/helpers";
3133
import type { Writable } from "~src/types";
3234
import type { RepositoryContext, UpdateHandlerInterface } from "../abstract";
3335
import { AbstractUpdateHandler } from "../abstract";
34-
import { createAddress } from "../helpers";
36+
import { createAddress, createCustomFields } from "../helpers";
3537

3638
export class OrderUpdateHandler
3739
extends AbstractUpdateHandler
@@ -62,6 +64,65 @@ export class OrderUpdateHandler
6264
});
6365
}
6466

67+
addParcelToDelivery(
68+
context: RepositoryContext,
69+
resource: Writable<Order>,
70+
{
71+
deliveryId,
72+
deliveryKey,
73+
parcelKey,
74+
measurements,
75+
trackingData,
76+
items,
77+
custom,
78+
}: OrderAddParcelToDeliveryAction,
79+
) {
80+
if (!resource.shippingInfo) {
81+
throw new Error("Order has no shipping info");
82+
}
83+
84+
if (!deliveryId && !deliveryKey) {
85+
throw new Error("Either deliveryId or deliveryKey must be provided");
86+
}
87+
88+
// Find the delivery by id or key
89+
let targetDelivery = null;
90+
for (const delivery of resource.shippingInfo.deliveries || []) {
91+
if (
92+
(deliveryId && delivery.id === deliveryId) ||
93+
(deliveryKey && delivery.key === deliveryKey)
94+
) {
95+
targetDelivery = delivery;
96+
break;
97+
}
98+
}
99+
100+
if (!targetDelivery) {
101+
const identifier = deliveryId || deliveryKey;
102+
throw new Error(
103+
`Delivery with ${deliveryId ? "id" : "key"} '${identifier}' not found`,
104+
);
105+
}
106+
107+
// Create the new parcel
108+
const newParcel: Parcel = {
109+
...getBaseResourceProperties(),
110+
...(parcelKey && { key: parcelKey }),
111+
...(measurements && { measurements }),
112+
...(trackingData && { trackingData }),
113+
items: items || [],
114+
...(custom && {
115+
custom: createCustomFields(custom, context.projectKey, this._storage),
116+
}),
117+
};
118+
119+
// Add the parcel to the delivery
120+
if (!targetDelivery.parcels) {
121+
targetDelivery.parcels = [];
122+
}
123+
targetDelivery.parcels.push(newParcel);
124+
}
125+
65126
addReturnInfo(
66127
context: RepositoryContext,
67128
resource: Writable<Order>,

src/services/order.test.ts

Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,251 @@ describe("Order Update Actions", () => {
794794
},
795795
]);
796796
});
797+
798+
test("addParcelToDelivery", async () => {
799+
const order: Order = {
800+
...getBaseResourceProperties(),
801+
customLineItems: [],
802+
lastMessageSequenceNumber: 0,
803+
lineItems: [],
804+
orderNumber: "1391",
805+
orderState: "Open",
806+
origin: "Customer",
807+
refusedGifts: [],
808+
shippingInfo: {
809+
shippingMethodName: "Standard delivery",
810+
price: {
811+
type: "centPrecision",
812+
currencyCode: "EUR",
813+
centAmount: 500,
814+
fractionDigits: 2,
815+
},
816+
shippingRate: {
817+
price: {
818+
type: "centPrecision",
819+
currencyCode: "EUR",
820+
centAmount: 500,
821+
fractionDigits: 2,
822+
},
823+
tiers: [],
824+
},
825+
deliveries: [
826+
{
827+
id: "delivery-1",
828+
key: "DELIVERY-001",
829+
createdAt: "2024-01-01T10:00:00.000Z",
830+
items: [
831+
{
832+
id: "line-item-1",
833+
quantity: 2,
834+
},
835+
],
836+
parcels: [],
837+
},
838+
],
839+
shippingMethodState: "MatchesCart",
840+
},
841+
shipping: [],
842+
shippingMode: "Single",
843+
syncInfo: [],
844+
totalPrice: {
845+
type: "centPrecision",
846+
fractionDigits: 2,
847+
centAmount: 1500,
848+
currencyCode: "EUR",
849+
},
850+
};
851+
ctMock.project("dummy").add("order", order);
852+
853+
const response = await supertest(ctMock.app).get(
854+
`/dummy/orders/order-number=${order.orderNumber}`,
855+
);
856+
expect(response.status).toBe(200);
857+
858+
// Test adding parcel by deliveryId
859+
const updateResponse = await supertest(ctMock.app)
860+
.post(`/dummy/orders/${response.body.id}`)
861+
.send({
862+
version: 0,
863+
actions: [
864+
{
865+
action: "addParcelToDelivery",
866+
deliveryId: "delivery-1",
867+
parcelKey: "parcel-001",
868+
measurements: {
869+
heightInMillimeter: 100,
870+
lengthInMillimeter: 200,
871+
widthInMillimeter: 150,
872+
weightInGram: 500,
873+
},
874+
trackingData: {
875+
trackingId: "TRACK123",
876+
carrier: "DHL",
877+
},
878+
items: [
879+
{
880+
id: "line-item-1",
881+
quantity: 1,
882+
},
883+
],
884+
},
885+
],
886+
});
887+
expect(updateResponse.status).toBe(200);
888+
expect(updateResponse.body.version).toBe(1);
889+
890+
const delivery = updateResponse.body.shippingInfo.deliveries[0];
891+
expect(delivery.parcels).toHaveLength(1);
892+
893+
const parcel = delivery.parcels[0];
894+
expect(parcel.key).toBe("parcel-001");
895+
expect(parcel.measurements).toMatchObject({
896+
heightInMillimeter: 100,
897+
lengthInMillimeter: 200,
898+
widthInMillimeter: 150,
899+
weightInGram: 500,
900+
});
901+
expect(parcel.trackingData).toMatchObject({
902+
trackingId: "TRACK123",
903+
carrier: "DHL",
904+
});
905+
expect(parcel.items).toMatchObject([
906+
{
907+
id: "line-item-1",
908+
quantity: 1,
909+
},
910+
]);
911+
912+
// Test adding parcel by deliveryKey
913+
const updateResponse2 = await supertest(ctMock.app)
914+
.post(`/dummy/orders/${response.body.id}`)
915+
.send({
916+
version: 1,
917+
actions: [
918+
{
919+
action: "addParcelToDelivery",
920+
deliveryKey: "DELIVERY-001",
921+
parcelKey: "parcel-002",
922+
items: [
923+
{
924+
id: "line-item-1",
925+
quantity: 1,
926+
},
927+
],
928+
},
929+
],
930+
});
931+
expect(updateResponse2.status).toBe(200);
932+
expect(updateResponse2.body.version).toBe(2);
933+
934+
const delivery2 = updateResponse2.body.shippingInfo.deliveries[0];
935+
expect(delivery2.parcels).toHaveLength(2);
936+
expect(delivery2.parcels[1].key).toBe("parcel-002");
937+
});
938+
939+
test("addParcelToDelivery - error cases", async () => {
940+
const order: Order = {
941+
...getBaseResourceProperties(),
942+
customLineItems: [],
943+
lastMessageSequenceNumber: 0,
944+
lineItems: [],
945+
orderNumber: "1392",
946+
orderState: "Open",
947+
origin: "Customer",
948+
refusedGifts: [],
949+
shippingInfo: {
950+
shippingMethodName: "Standard delivery",
951+
price: {
952+
type: "centPrecision",
953+
currencyCode: "EUR",
954+
centAmount: 500,
955+
fractionDigits: 2,
956+
},
957+
shippingRate: {
958+
price: {
959+
type: "centPrecision",
960+
currencyCode: "EUR",
961+
centAmount: 500,
962+
fractionDigits: 2,
963+
},
964+
tiers: [],
965+
},
966+
deliveries: [
967+
{
968+
id: "delivery-1",
969+
key: "DELIVERY-001",
970+
createdAt: "2024-01-01T10:00:00.000Z",
971+
items: [],
972+
parcels: [],
973+
},
974+
],
975+
shippingMethodState: "MatchesCart",
976+
},
977+
shipping: [],
978+
shippingMode: "Single",
979+
syncInfo: [],
980+
totalPrice: {
981+
type: "centPrecision",
982+
fractionDigits: 2,
983+
centAmount: 1500,
984+
currencyCode: "EUR",
985+
},
986+
};
987+
ctMock.project("dummy").add("order", order);
988+
989+
const response = await supertest(ctMock.app).get(
990+
`/dummy/orders/order-number=${order.orderNumber}`,
991+
);
992+
expect(response.status).toBe(200);
993+
994+
// Test error: no deliveryId or deliveryKey provided
995+
const errorResponse1 = await supertest(ctMock.app)
996+
.post(`/dummy/orders/${response.body.id}`)
997+
.send({
998+
version: 0,
999+
actions: [
1000+
{
1001+
action: "addParcelToDelivery",
1002+
parcelKey: "parcel-001",
1003+
},
1004+
],
1005+
});
1006+
expect(errorResponse1.status).toBe(500);
1007+
1008+
// Test error: delivery not found
1009+
const errorResponse2 = await supertest(ctMock.app)
1010+
.post(`/dummy/orders/${response.body.id}`)
1011+
.send({
1012+
version: 0,
1013+
actions: [
1014+
{
1015+
action: "addParcelToDelivery",
1016+
deliveryId: "nonexistent-delivery",
1017+
parcelKey: "parcel-001",
1018+
},
1019+
],
1020+
});
1021+
expect(errorResponse2.status).toBe(500);
1022+
});
1023+
1024+
test("addParcelToDelivery - order without shipping info", async () => {
1025+
assert(order, "order not created");
1026+
1027+
// Test error: order has no shipping info
1028+
const errorResponse = await supertest(ctMock.app)
1029+
.post(`/dummy/orders/${order.id}`)
1030+
.send({
1031+
version: 1,
1032+
actions: [
1033+
{
1034+
action: "addParcelToDelivery",
1035+
deliveryId: "delivery-1",
1036+
parcelKey: "parcel-001",
1037+
},
1038+
],
1039+
});
1040+
expect(errorResponse.status).toBe(500);
1041+
});
7971042
});
7981043

7991044
describe("Order Import", () => {

0 commit comments

Comments
 (0)