Skip to content

Commit 0fb00be

Browse files
authored
feat: shipping info on order (#344)
1 parent e7fc10b commit 0fb00be

File tree

5 files changed

+577
-139
lines changed

5 files changed

+577
-139
lines changed

.changeset/green-waves-lead.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 shippingInfo to orders

src/repositories/cart/index.ts

Lines changed: 11 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,16 @@ import { v4 as uuidv4 } from "uuid";
2323
import type { Config } from "~src/config";
2424
import { CommercetoolsError } from "~src/exceptions";
2525
import { getBaseResourceProperties } from "~src/helpers";
26-
import { getShippingMethodsMatchingCart } from "~src/shipping";
26+
import {
27+
createShippingInfoFromMethod,
28+
getShippingMethodsMatchingCart,
29+
} from "~src/shipping";
2730
import type { Writable } from "~src/types";
2831
import {
2932
AbstractResourceRepository,
3033
type RepositoryContext,
3134
} from "../abstract";
32-
import {
33-
createAddress,
34-
createCentPrecisionMoney,
35-
createCustomFields,
36-
createTypedMoney,
37-
roundDecimal,
38-
} from "../helpers";
35+
import { createAddress, createCustomFields } from "../helpers";
3936
import { CartUpdateHandler } from "./actions";
4037
import {
4138
calculateCartTotalPrice,
@@ -284,15 +281,6 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
284281
throw new Error("External tax rate is not supported");
285282
}
286283

287-
const country = resource.shippingAddress?.country;
288-
289-
if (!country) {
290-
throw new CommercetoolsError<InvalidOperationError>({
291-
code: "InvalidOperation",
292-
message: `The cart with ID '${resource.id}' does not have a shipping address set.`,
293-
});
294-
}
295-
296284
// Bit of a hack: calling this checks that the resource identifier is
297285
// valid (i.e. id xor key) and that the shipping method exists.
298286
this._storage.getByResourceIdentifier<"shipping-method">(
@@ -327,126 +315,12 @@ export class CartRepository extends AbstractResourceRepository<"cart"> {
327315
});
328316
}
329317

330-
const taxCategory = this._storage.getByResourceIdentifier<"tax-category">(
331-
context.projectKey,
332-
method.taxCategory,
333-
);
334-
335-
// TODO: match state in addition to country
336-
const taxRate = taxCategory.rates.find((rate) => rate.country === country);
337-
338-
if (!taxRate) {
339-
throw new CommercetoolsError<MissingTaxRateForCountryError>({
340-
code: "MissingTaxRateForCountry",
341-
message: `Tax category '${taxCategory.id}' is missing a tax rate for country '${country}'.`,
342-
taxCategoryId: taxCategory.id,
343-
});
344-
}
345-
346-
// There should only be one zone rate matching the address, since
347-
// Locations cannot be assigned to more than one zone.
348-
// See https://docs.commercetools.com/api/projects/zones#location
349-
const zoneRate = method.zoneRates.find((rate) =>
350-
rate.zone.obj?.locations.some((loc) => loc.country === country),
318+
// Use the shared shipping info creation logic
319+
return createShippingInfoFromMethod(
320+
context,
321+
this._storage,
322+
resource,
323+
method,
351324
);
352-
353-
if (!zoneRate) {
354-
// This shouldn't happen because getShippingMethodsMatchingCart already
355-
// filtered out shipping methods without any zones matching the address
356-
throw new Error("Zone rate not found");
357-
}
358-
359-
// Shipping rates are defined by currency, and getShippingMethodsMatchingCart
360-
// also matches on currency, so there should only be one in the array.
361-
// See https://docs.commercetools.com/api/projects/shippingMethods#zonerate
362-
const shippingRate = zoneRate.shippingRates[0];
363-
if (!shippingRate) {
364-
// This shouldn't happen because getShippingMethodsMatchingCart already
365-
// filtered out shipping methods without any matching rates
366-
throw new Error("Shipping rate not found");
367-
}
368-
369-
const shippingRateTier = shippingRate.tiers.find((tier) => tier.isMatching);
370-
if (shippingRateTier && shippingRateTier.type !== "CartValue") {
371-
throw new Error("Non-CartValue shipping rate tier is not supported");
372-
}
373-
374-
let shippingPrice = shippingRateTier
375-
? createCentPrecisionMoney(shippingRateTier.price)
376-
: shippingRate.price;
377-
378-
// Handle freeAbove: if cart total is above the freeAbove threshold, shipping is free
379-
if (
380-
shippingRate.freeAbove &&
381-
shippingRate.freeAbove.currencyCode ===
382-
resource.totalPrice.currencyCode &&
383-
resource.totalPrice.centAmount >= shippingRate.freeAbove.centAmount
384-
) {
385-
shippingPrice = {
386-
...shippingPrice,
387-
centAmount: 0,
388-
};
389-
}
390-
391-
// Calculate tax amounts
392-
const totalGross: CentPrecisionMoney = taxRate.includedInPrice
393-
? shippingPrice
394-
: {
395-
...shippingPrice,
396-
centAmount: roundDecimal(
397-
new Decimal(shippingPrice.centAmount).mul(1 + taxRate.amount),
398-
resource.taxRoundingMode,
399-
).toNumber(),
400-
};
401-
402-
const totalNet: CentPrecisionMoney = taxRate.includedInPrice
403-
? {
404-
...shippingPrice,
405-
centAmount: roundDecimal(
406-
new Decimal(shippingPrice.centAmount).div(1 + taxRate.amount),
407-
resource.taxRoundingMode,
408-
).toNumber(),
409-
}
410-
: shippingPrice;
411-
412-
const taxPortions: TaxPortion[] = [
413-
{
414-
name: taxRate.name,
415-
rate: taxRate.amount,
416-
amount: {
417-
...shippingPrice,
418-
centAmount: totalGross.centAmount - totalNet.centAmount,
419-
},
420-
},
421-
];
422-
423-
const totalTax: CentPrecisionMoney = {
424-
...shippingPrice,
425-
centAmount: taxPortions.reduce(
426-
(acc, portion) => acc + portion.amount.centAmount,
427-
0,
428-
),
429-
};
430-
431-
const taxedPrice: TaxedItemPrice = {
432-
totalNet,
433-
totalGross,
434-
taxPortions,
435-
totalTax,
436-
};
437-
438-
return {
439-
shippingMethod: {
440-
typeId: "shipping-method" as const,
441-
id: method.id,
442-
},
443-
shippingMethodName: method.name,
444-
price: shippingPrice,
445-
shippingRate,
446-
taxedPrice,
447-
taxRate,
448-
taxCategory: method.taxCategory,
449-
shippingMethodState: "MatchesCart",
450-
};
451325
}
452326
}

0 commit comments

Comments
 (0)