@labdigital/commercetools-mock 2.59.0 → 2.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import bodyParser from "body-parser";
9
9
  import { randomBytes } from "node:crypto";
10
10
  import { isDeepStrictEqual } from "node:util";
11
11
  import { Decimal } from "decimal.js/decimal";
12
+ import { Decimal as Decimal$1 } from "decimal.js";
12
13
  import assert from "node:assert";
13
14
  import { z } from "zod";
14
15
  import { fromZodError } from "zod-validation-error";
@@ -521,70 +522,6 @@ var ProductTailoringUpdateHandler = class extends AbstractUpdateHandler {
521
522
  }
522
523
  };
523
524
 
524
- //#endregion
525
- //#region src/shipping.ts
526
- const markMatchingShippingRate = (cart, shippingRate) => {
527
- const isMatching = shippingRate.price.currencyCode === cart.totalPrice.currencyCode;
528
- return {
529
- ...shippingRate,
530
- tiers: markMatchingShippingRatePriceTiers(cart, shippingRate.tiers),
531
- isMatching
532
- };
533
- };
534
- const markMatchingShippingRatePriceTiers = (cart, tiers) => {
535
- if (tiers.length === 0) return [];
536
- if (new Set(tiers.map((tier) => tier.type)).size > 1) throw new Error("Can't handle multiple types of tiers");
537
- const tierType = tiers[0].type;
538
- switch (tierType) {
539
- case "CartValue": return markMatchingCartValueTiers(cart, tiers);
540
- default: throw new Error(`Unsupported tier type: ${tierType}`);
541
- }
542
- };
543
- const markMatchingCartValueTiers = (cart, tiers) => {
544
- const sortedTiers = [...tiers].sort((a, b) => b.minimumCentAmount - a.minimumCentAmount);
545
- const result = {};
546
- let hasMatchingTier = false;
547
- for (const tier of sortedTiers) {
548
- const isMatching = !hasMatchingTier && cart.totalPrice.currencyCode === tier.price.currencyCode && cart.totalPrice.centAmount >= tier.minimumCentAmount;
549
- if (isMatching) hasMatchingTier = true;
550
- result[tier.minimumCentAmount] = {
551
- ...tier,
552
- isMatching
553
- };
554
- }
555
- return tiers.map((tier) => result[tier.minimumCentAmount]);
556
- };
557
- const getShippingMethodsMatchingCart = (context, storage, cart, params = {}) => {
558
- if (!cart.shippingAddress?.country) throw new CommercetoolsError({
559
- code: "InvalidOperation",
560
- message: `The cart with ID '${cart.id}' does not have a shipping address set.`
561
- });
562
- const zones = storage.query(context.projectKey, "zone", {
563
- where: [`locations(country="${cart.shippingAddress.country}"))`],
564
- limit: 100
565
- });
566
- const zoneIds = zones.results.map((zone) => zone.id);
567
- const shippingMethods = storage.query(context.projectKey, "shipping-method", {
568
- where: ["zoneRates(zone(id in (:zoneIds)))", `zoneRates(shippingRates(price(currencyCode="${cart.totalPrice.currencyCode}")))`],
569
- "var.zoneIds": zoneIds,
570
- expand: params.expand
571
- });
572
- const results = shippingMethods.results.map((shippingMethod) => {
573
- const rates = shippingMethod.zoneRates.map((zoneRate) => ({
574
- zone: zoneRate.zone,
575
- shippingRates: zoneRate.shippingRates.map((rate) => markMatchingShippingRate(cart, rate)).filter((rate) => rate.isMatching)
576
- })).filter((zoneRate) => zoneRate.shippingRates.length > 0);
577
- return {
578
- ...shippingMethod,
579
- zoneRates: rates
580
- };
581
- }).filter((shippingMethod) => shippingMethod.zoneRates.length > 0);
582
- return {
583
- ...shippingMethods,
584
- results
585
- };
586
- };
587
-
588
525
  //#endregion
589
526
  //#region src/repositories/helpers.ts
590
527
  const createAddress = (base, projectKey, storage) => {
@@ -750,25 +687,71 @@ const getBusinessUnitKeyReference = (id, projectKey, storage) => {
750
687
  };
751
688
 
752
689
  //#endregion
753
- //#region src/repositories/cart/helpers.ts
754
- const selectPrice = ({ prices, currency, country }) => {
755
- if (!prices) return void 0;
756
- return prices.find((price) => {
757
- const countryMatch = !price.country || price.country === country;
758
- const currencyMatch = price.value.currencyCode === currency;
759
- return countryMatch && currencyMatch;
690
+ //#region src/lib/tax.ts
691
+ const calculateTaxTotals = (resource) => {
692
+ const taxedItemPrices = [];
693
+ resource.lineItems.forEach((item) => {
694
+ if (item.taxedPrice) taxedItemPrices.push(item.taxedPrice);
760
695
  });
696
+ resource.customLineItems.forEach((item) => {
697
+ if (item.taxedPrice) taxedItemPrices.push(item.taxedPrice);
698
+ });
699
+ let taxedShippingPrice;
700
+ if (resource.shippingInfo?.taxedPrice) {
701
+ taxedShippingPrice = resource.shippingInfo.taxedPrice;
702
+ taxedItemPrices.push(resource.shippingInfo.taxedPrice);
703
+ }
704
+ if (!taxedItemPrices.length) return {
705
+ taxedPrice: void 0,
706
+ taxedShippingPrice
707
+ };
708
+ const currencyCode = resource.totalPrice.currencyCode;
709
+ const toMoney = (centAmount) => createCentPrecisionMoney({
710
+ currencyCode,
711
+ centAmount
712
+ });
713
+ let totalNet = 0;
714
+ let totalGross = 0;
715
+ let totalTax = 0;
716
+ const taxPortionsByRate = new Map();
717
+ taxedItemPrices.forEach((price) => {
718
+ totalNet += price.totalNet.centAmount;
719
+ totalGross += price.totalGross.centAmount;
720
+ const priceTax = price.totalTax ? price.totalTax.centAmount : price.totalGross.centAmount - price.totalNet.centAmount;
721
+ totalTax += Math.max(priceTax, 0);
722
+ price.taxPortions?.forEach((portion) => {
723
+ const key = `${portion.rate}-${portion.name ?? ""}`;
724
+ const existing = taxPortionsByRate.get(key) ?? {
725
+ rate: portion.rate,
726
+ name: portion.name,
727
+ centAmount: 0
728
+ };
729
+ existing.centAmount += portion.amount.centAmount;
730
+ taxPortionsByRate.set(key, existing);
731
+ });
732
+ });
733
+ const taxPortions = Array.from(taxPortionsByRate.values()).map((portion) => ({
734
+ rate: portion.rate,
735
+ name: portion.name,
736
+ amount: toMoney(portion.centAmount)
737
+ }));
738
+ return {
739
+ taxedPrice: {
740
+ totalNet: toMoney(totalNet),
741
+ totalGross: toMoney(totalGross),
742
+ taxPortions,
743
+ totalTax: totalTax > 0 ? toMoney(totalTax) : void 0
744
+ },
745
+ taxedShippingPrice
746
+ };
761
747
  };
762
- const calculateLineItemTotalPrice = (lineItem) => lineItem.price?.value.centAmount * lineItem.quantity;
763
- const calculateCartTotalPrice = (cart) => {
764
- const lineItemsTotal = cart.lineItems.reduce((cur, item) => cur + item.totalPrice.centAmount, 0);
765
- const customLineItemsTotal = cart.customLineItems.reduce((cur, item) => cur + item.totalPrice.centAmount, 0);
766
- return lineItemsTotal + customLineItemsTotal;
767
- };
768
- const calculateTaxedPrice = (amount, taxCategory, currency, country) => {
769
- if (!taxCategory || !taxCategory.rates.length) return void 0;
770
- const taxRate = taxCategory.rates.find((rate) => !rate.country || rate.country === country) || taxCategory.rates[0];
748
+ const buildTaxedPriceFromRate = (amount, currencyCode, taxRate) => {
771
749
  if (!taxRate) return void 0;
750
+ const toMoney = (centAmount) => createCentPrecisionMoney({
751
+ type: "centPrecision",
752
+ currencyCode,
753
+ centAmount
754
+ });
772
755
  let netAmount;
773
756
  let grossAmount;
774
757
  let taxAmount;
@@ -782,36 +765,173 @@ const calculateTaxedPrice = (amount, taxCategory, currency, country) => {
782
765
  grossAmount = netAmount + taxAmount;
783
766
  }
784
767
  return {
785
- totalNet: {
786
- type: "centPrecision",
787
- currencyCode: currency,
788
- centAmount: netAmount,
789
- fractionDigits: 2
790
- },
791
- totalGross: {
792
- type: "centPrecision",
793
- currencyCode: currency,
794
- centAmount: grossAmount,
795
- fractionDigits: 2
796
- },
768
+ totalNet: toMoney(netAmount),
769
+ totalGross: toMoney(grossAmount),
770
+ totalTax: taxAmount > 0 ? toMoney(taxAmount) : void 0,
797
771
  taxPortions: taxAmount > 0 ? [{
798
772
  rate: taxRate.amount,
799
- amount: {
800
- type: "centPrecision",
801
- currencyCode: currency,
802
- centAmount: taxAmount,
803
- fractionDigits: 2
804
- },
805
- name: taxRate.name
806
- }] : [],
807
- totalTax: taxAmount > 0 ? {
808
- type: "centPrecision",
809
- currencyCode: currency,
810
- centAmount: taxAmount,
811
- fractionDigits: 2
812
- } : void 0
773
+ name: taxRate.name,
774
+ amount: toMoney(taxAmount)
775
+ }] : []
776
+ };
777
+ };
778
+ const calculateTaxedPriceFromRate = (amount, currencyCode, taxRate) => buildTaxedPriceFromRate(amount, currencyCode, taxRate);
779
+ const calculateTaxedPrice = (amount, taxCategory, currency, country) => {
780
+ if (!taxCategory || !taxCategory.rates.length) return void 0;
781
+ const taxRate = taxCategory.rates.find((rate) => !rate.country || rate.country === country) || taxCategory.rates[0];
782
+ const taxedItemPrice = buildTaxedPriceFromRate(amount, currency, taxRate);
783
+ if (!taxedItemPrice) return void 0;
784
+ return {
785
+ totalNet: taxedItemPrice.totalNet,
786
+ totalGross: taxedItemPrice.totalGross,
787
+ taxPortions: taxedItemPrice.taxPortions,
788
+ totalTax: taxedItemPrice.totalTax
789
+ };
790
+ };
791
+
792
+ //#endregion
793
+ //#region src/shipping.ts
794
+ const markMatchingShippingRate = (cart, shippingRate) => {
795
+ const isMatching = shippingRate.price.currencyCode === cart.totalPrice.currencyCode;
796
+ return {
797
+ ...shippingRate,
798
+ tiers: markMatchingShippingRatePriceTiers(cart, shippingRate.tiers),
799
+ isMatching
800
+ };
801
+ };
802
+ const markMatchingShippingRatePriceTiers = (cart, tiers) => {
803
+ if (tiers.length === 0) return [];
804
+ if (new Set(tiers.map((tier) => tier.type)).size > 1) throw new Error("Can't handle multiple types of tiers");
805
+ const tierType = tiers[0].type;
806
+ switch (tierType) {
807
+ case "CartValue": return markMatchingCartValueTiers(cart, tiers);
808
+ default: throw new Error(`Unsupported tier type: ${tierType}`);
809
+ }
810
+ };
811
+ const markMatchingCartValueTiers = (cart, tiers) => {
812
+ const sortedTiers = [...tiers].sort((a, b) => b.minimumCentAmount - a.minimumCentAmount);
813
+ const result = {};
814
+ let hasMatchingTier = false;
815
+ for (const tier of sortedTiers) {
816
+ const isMatching = !hasMatchingTier && cart.totalPrice.currencyCode === tier.price.currencyCode && cart.totalPrice.centAmount >= tier.minimumCentAmount;
817
+ if (isMatching) hasMatchingTier = true;
818
+ result[tier.minimumCentAmount] = {
819
+ ...tier,
820
+ isMatching
821
+ };
822
+ }
823
+ return tiers.map((tier) => result[tier.minimumCentAmount]);
824
+ };
825
+ const getShippingMethodsMatchingCart = (context, storage, cart, params = {}) => {
826
+ if (!cart.shippingAddress?.country) throw new CommercetoolsError({
827
+ code: "InvalidOperation",
828
+ message: `The cart with ID '${cart.id}' does not have a shipping address set.`
829
+ });
830
+ const zones = storage.query(context.projectKey, "zone", {
831
+ where: [`locations(country="${cart.shippingAddress.country}"))`],
832
+ limit: 100
833
+ });
834
+ const zoneIds = zones.results.map((zone) => zone.id);
835
+ const shippingMethods = storage.query(context.projectKey, "shipping-method", {
836
+ where: ["zoneRates(zone(id in (:zoneIds)))", `zoneRates(shippingRates(price(currencyCode="${cart.totalPrice.currencyCode}")))`],
837
+ "var.zoneIds": zoneIds,
838
+ expand: params.expand
839
+ });
840
+ const results = shippingMethods.results.map((shippingMethod) => {
841
+ const rates = shippingMethod.zoneRates.map((zoneRate) => ({
842
+ zone: zoneRate.zone,
843
+ shippingRates: zoneRate.shippingRates.map((rate) => markMatchingShippingRate(cart, rate)).filter((rate) => rate.isMatching)
844
+ })).filter((zoneRate) => zoneRate.shippingRates.length > 0);
845
+ return {
846
+ ...shippingMethod,
847
+ zoneRates: rates
848
+ };
849
+ }).filter((shippingMethod) => shippingMethod.zoneRates.length > 0);
850
+ return {
851
+ ...shippingMethods,
852
+ results
853
+ };
854
+ };
855
+ /**
856
+ * Creates shipping info from a shipping method, handling all tax calculations and pricing logic.
857
+ */
858
+ const createShippingInfoFromMethod = (context, storage, resource, method) => {
859
+ const country = resource.shippingAddress.country;
860
+ const zoneRate = method.zoneRates.find((rate) => rate.zone.obj?.locations.some((loc) => loc.country === country));
861
+ if (!zoneRate) throw new Error("Zone rate not found");
862
+ const shippingRate = zoneRate.shippingRates[0];
863
+ if (!shippingRate) throw new Error("Shipping rate not found");
864
+ const taxCategory = storage.getByResourceIdentifier(context.projectKey, method.taxCategory);
865
+ const taxRate = taxCategory.rates.find((rate) => rate.country === country);
866
+ if (!taxRate) throw new CommercetoolsError({
867
+ code: "MissingTaxRateForCountry",
868
+ message: `Tax category '${taxCategory.id}' is missing a tax rate for country '${country}'.`,
869
+ taxCategoryId: taxCategory.id
870
+ });
871
+ const shippingRateTier = shippingRate.tiers.find((tier) => tier.isMatching);
872
+ if (shippingRateTier && shippingRateTier.type !== "CartValue") throw new Error("Non-CartValue shipping rate tier is not supported");
873
+ let shippingPrice = shippingRateTier ? createCentPrecisionMoney(shippingRateTier.price) : shippingRate.price;
874
+ if (shippingRate.freeAbove && shippingRate.freeAbove.currencyCode === resource.totalPrice.currencyCode && resource.totalPrice.centAmount >= shippingRate.freeAbove.centAmount) shippingPrice = {
875
+ ...shippingPrice,
876
+ centAmount: 0
877
+ };
878
+ const totalGross = taxRate.includedInPrice ? shippingPrice : {
879
+ ...shippingPrice,
880
+ centAmount: roundDecimal(new Decimal$1(shippingPrice.centAmount).mul(1 + taxRate.amount), resource.taxRoundingMode || "HalfEven").toNumber()
881
+ };
882
+ const totalNet = taxRate.includedInPrice ? {
883
+ ...shippingPrice,
884
+ centAmount: roundDecimal(new Decimal$1(shippingPrice.centAmount).div(1 + taxRate.amount), resource.taxRoundingMode || "HalfEven").toNumber()
885
+ } : shippingPrice;
886
+ const taxPortions = [{
887
+ name: taxRate.name,
888
+ rate: taxRate.amount,
889
+ amount: {
890
+ ...shippingPrice,
891
+ centAmount: totalGross.centAmount - totalNet.centAmount
892
+ }
893
+ }];
894
+ const totalTax = {
895
+ ...shippingPrice,
896
+ centAmount: taxPortions.reduce((acc, portion) => acc + portion.amount.centAmount, 0)
897
+ };
898
+ const taxedPrice = {
899
+ totalNet,
900
+ totalGross,
901
+ taxPortions,
902
+ totalTax
903
+ };
904
+ return {
905
+ shippingMethod: {
906
+ typeId: "shipping-method",
907
+ id: method.id
908
+ },
909
+ shippingMethodName: method.name,
910
+ price: shippingPrice,
911
+ shippingRate,
912
+ taxedPrice,
913
+ taxRate,
914
+ taxCategory: method.taxCategory,
915
+ shippingMethodState: "MatchesCart"
813
916
  };
814
917
  };
918
+
919
+ //#endregion
920
+ //#region src/repositories/cart/helpers.ts
921
+ const selectPrice = ({ prices, currency, country }) => {
922
+ if (!prices) return void 0;
923
+ return prices.find((price) => {
924
+ const countryMatch = !price.country || price.country === country;
925
+ const currencyMatch = price.value.currencyCode === currency;
926
+ return countryMatch && currencyMatch;
927
+ });
928
+ };
929
+ const calculateLineItemTotalPrice = (lineItem) => lineItem.price?.value.centAmount * lineItem.quantity;
930
+ const calculateCartTotalPrice = (cart) => {
931
+ const lineItemsTotal = cart.lineItems.reduce((cur, item) => cur + item.totalPrice.centAmount, 0);
932
+ const customLineItemsTotal = cart.customLineItems.reduce((cur, item) => cur + item.totalPrice.centAmount, 0);
933
+ return lineItemsTotal + customLineItemsTotal;
934
+ };
815
935
  const createCustomLineItemFromDraft = (projectKey, draft, storage, country) => {
816
936
  const quantity = draft.quantity ?? 1;
817
937
  const taxCategoryRef = draft.taxCategory ? getReferenceFromResourceIdentifier(draft.taxCategory, projectKey, storage) : void 0;
@@ -1164,6 +1284,39 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
1164
1284
  };
1165
1285
  }
1166
1286
  }
1287
+ setLineItemPrice(context, resource, { lineItemId, lineItemKey, externalPrice }) {
1288
+ const lineItem = resource.lineItems.find((x) => lineItemId && x.id === lineItemId || lineItemKey && x.key === lineItemKey);
1289
+ if (!lineItem) throw new CommercetoolsError({
1290
+ code: "General",
1291
+ message: lineItemKey ? `A line item with key '${lineItemKey}' not found.` : `A line item with ID '${lineItemId}' not found.`
1292
+ });
1293
+ if (!externalPrice && lineItem.priceMode !== "ExternalPrice") return;
1294
+ if (externalPrice && externalPrice.currencyCode !== resource.totalPrice.currencyCode) throw new CommercetoolsError({
1295
+ code: "General",
1296
+ message: `Currency mismatch. Expected '${resource.totalPrice.currencyCode}' but got '${externalPrice.currencyCode}'.`
1297
+ });
1298
+ if (externalPrice) {
1299
+ lineItem.priceMode = "ExternalPrice";
1300
+ const priceValue = createTypedMoney(externalPrice);
1301
+ lineItem.price = lineItem.price ?? { id: v4() };
1302
+ lineItem.price.value = priceValue;
1303
+ } else {
1304
+ lineItem.priceMode = "Platform";
1305
+ const price = selectPrice({
1306
+ prices: lineItem.variant.prices,
1307
+ currency: resource.totalPrice.currencyCode,
1308
+ country: resource.country
1309
+ });
1310
+ if (!price) throw new Error(`No valid price found for ${lineItem.productId} for country ${resource.country} and currency ${resource.totalPrice.currencyCode}`);
1311
+ lineItem.price = price;
1312
+ }
1313
+ const lineItemTotal = calculateLineItemTotalPrice(lineItem);
1314
+ lineItem.totalPrice = createCentPrecisionMoney({
1315
+ ...lineItem.price.value,
1316
+ centAmount: lineItemTotal
1317
+ });
1318
+ resource.totalPrice.centAmount = calculateCartTotalPrice(resource);
1319
+ }
1167
1320
  setLineItemShippingDetails(context, resource, { action, shippingDetails, lineItemId, lineItemKey }) {
1168
1321
  const lineItem = resource.lineItems.find((x) => lineItemId && x.id === lineItemId || lineItemKey && x.key === lineItemKey);
1169
1322
  if (!lineItem) throw new CommercetoolsError({
@@ -1296,6 +1449,9 @@ var CartRepository = class extends AbstractResourceRepository {
1296
1449
  key: draft.store.key
1297
1450
  } : void 0;
1298
1451
  if (draft.shippingMethod) resource.shippingInfo = this.createShippingInfo(context, resource, draft.shippingMethod);
1452
+ const { taxedPrice, taxedShippingPrice } = calculateTaxTotals(resource);
1453
+ resource.taxedPrice = taxedPrice;
1454
+ resource.taxedShippingPrice = taxedShippingPrice;
1299
1455
  return this.saveNew(context, resource);
1300
1456
  }
1301
1457
  getActiveCart(projectKey) {
@@ -1355,11 +1511,6 @@ var CartRepository = class extends AbstractResourceRepository {
1355
1511
  };
1356
1512
  createShippingInfo(context, resource, shippingMethodRef) {
1357
1513
  if (resource.taxMode === "External") throw new Error("External tax rate is not supported");
1358
- const country = resource.shippingAddress?.country;
1359
- if (!country) throw new CommercetoolsError({
1360
- code: "InvalidOperation",
1361
- message: `The cart with ID '${resource.id}' does not have a shipping address set.`
1362
- });
1363
1514
  this._storage.getByResourceIdentifier(context.projectKey, shippingMethodRef);
1364
1515
  const shippingMethods = getShippingMethodsMatchingCart(context, this._storage, resource, { expand: ["zoneRates[*].zone"] });
1365
1516
  const method = shippingMethods.results.find((candidate) => shippingMethodRef.id ? candidate.id === shippingMethodRef.id : candidate.key === shippingMethodRef.key);
@@ -1367,63 +1518,7 @@ var CartRepository = class extends AbstractResourceRepository {
1367
1518
  code: "ShippingMethodDoesNotMatchCart",
1368
1519
  message: `The shipping method with ${shippingMethodRef.id ? `ID '${shippingMethodRef.id}'` : `key '${shippingMethodRef.key}'`} is not allowed for the cart with ID '${resource.id}'.`
1369
1520
  });
1370
- const taxCategory = this._storage.getByResourceIdentifier(context.projectKey, method.taxCategory);
1371
- const taxRate = taxCategory.rates.find((rate) => rate.country === country);
1372
- if (!taxRate) throw new CommercetoolsError({
1373
- code: "MissingTaxRateForCountry",
1374
- message: `Tax category '${taxCategory.id}' is missing a tax rate for country '${country}'.`,
1375
- taxCategoryId: taxCategory.id
1376
- });
1377
- const zoneRate = method.zoneRates.find((rate) => rate.zone.obj?.locations.some((loc) => loc.country === country));
1378
- if (!zoneRate) throw new Error("Zone rate not found");
1379
- const shippingRate = zoneRate.shippingRates[0];
1380
- if (!shippingRate) throw new Error("Shipping rate not found");
1381
- const shippingRateTier = shippingRate.tiers.find((tier) => tier.isMatching);
1382
- if (shippingRateTier && shippingRateTier.type !== "CartValue") throw new Error("Non-CartValue shipping rate tier is not supported");
1383
- let shippingPrice = shippingRateTier ? createCentPrecisionMoney(shippingRateTier.price) : shippingRate.price;
1384
- if (shippingRate.freeAbove && shippingRate.freeAbove.currencyCode === resource.totalPrice.currencyCode && resource.totalPrice.centAmount >= shippingRate.freeAbove.centAmount) shippingPrice = {
1385
- ...shippingPrice,
1386
- centAmount: 0
1387
- };
1388
- const totalGross = taxRate.includedInPrice ? shippingPrice : {
1389
- ...shippingPrice,
1390
- centAmount: roundDecimal(new Decimal(shippingPrice.centAmount).mul(1 + taxRate.amount), resource.taxRoundingMode).toNumber()
1391
- };
1392
- const totalNet = taxRate.includedInPrice ? {
1393
- ...shippingPrice,
1394
- centAmount: roundDecimal(new Decimal(shippingPrice.centAmount).div(1 + taxRate.amount), resource.taxRoundingMode).toNumber()
1395
- } : shippingPrice;
1396
- const taxPortions = [{
1397
- name: taxRate.name,
1398
- rate: taxRate.amount,
1399
- amount: {
1400
- ...shippingPrice,
1401
- centAmount: totalGross.centAmount - totalNet.centAmount
1402
- }
1403
- }];
1404
- const totalTax = {
1405
- ...shippingPrice,
1406
- centAmount: taxPortions.reduce((acc, portion) => acc + portion.amount.centAmount, 0)
1407
- };
1408
- const taxedPrice = {
1409
- totalNet,
1410
- totalGross,
1411
- taxPortions,
1412
- totalTax
1413
- };
1414
- return {
1415
- shippingMethod: {
1416
- typeId: "shipping-method",
1417
- id: method.id
1418
- },
1419
- shippingMethodName: method.name,
1420
- price: shippingPrice,
1421
- shippingRate,
1422
- taxedPrice,
1423
- taxRate,
1424
- taxCategory: method.taxCategory,
1425
- shippingMethodState: "MatchesCart"
1426
- };
1521
+ return createShippingInfoFromMethod(context, this._storage, resource, method);
1427
1522
  }
1428
1523
  };
1429
1524
 
@@ -1633,6 +1728,7 @@ var OrderRepository = class extends AbstractResourceRepository {
1633
1728
  refusedGifts: [],
1634
1729
  shipping: cart.shipping,
1635
1730
  shippingAddress: cart.shippingAddress,
1731
+ shippingInfo: cart.shippingInfo,
1636
1732
  shippingMode: cart.shippingMode,
1637
1733
  syncInfo: [],
1638
1734
  taxCalculationMode: cart.taxCalculationMode,
@@ -1643,6 +1739,14 @@ var OrderRepository = class extends AbstractResourceRepository {
1643
1739
  totalPrice: cart.totalPrice,
1644
1740
  store: cart.store
1645
1741
  };
1742
+ const { taxedPrice, taxedShippingPrice } = calculateTaxTotals({
1743
+ lineItems: cart.lineItems,
1744
+ customLineItems: cart.customLineItems,
1745
+ shippingInfo: cart.shippingInfo,
1746
+ totalPrice: cart.totalPrice
1747
+ });
1748
+ resource.taxedPrice = resource.taxedPrice ?? taxedPrice;
1749
+ resource.taxedShippingPrice = resource.taxedShippingPrice ?? taxedShippingPrice;
1646
1750
  return this.saveNew(context, resource);
1647
1751
  }
1648
1752
  import(context, draft) {
@@ -1666,12 +1770,36 @@ var OrderRepository = class extends AbstractResourceRepository {
1666
1770
  refusedGifts: [],
1667
1771
  shippingMode: "Single",
1668
1772
  shipping: [],
1773
+ shippingInfo: void 0,
1669
1774
  store: resolveStoreReference(draft.store, context.projectKey, this._storage),
1670
1775
  syncInfo: [],
1671
1776
  lineItems: draft.lineItems?.map((item) => this.lineItemFromImportDraft.bind(this)(context, item)) || [],
1672
1777
  customLineItems: draft.customLineItems?.map((item) => this.customLineItemFromImportDraft.bind(this)(context, item)) || [],
1673
1778
  totalPrice: createCentPrecisionMoney(draft.totalPrice)
1674
1779
  };
1780
+ if (draft.shippingInfo?.shippingMethod) {
1781
+ const { ...shippingMethodRef } = draft.shippingInfo.shippingMethod;
1782
+ if (shippingMethodRef.key && !shippingMethodRef.id) {
1783
+ const shippingMethod = this._storage.getByResourceIdentifier(context.projectKey, shippingMethodRef);
1784
+ if (!shippingMethod) throw new CommercetoolsError({
1785
+ code: "General",
1786
+ message: `A shipping method with key '${shippingMethodRef.key}' does not exist.`
1787
+ });
1788
+ shippingMethodRef.id = shippingMethod.id;
1789
+ }
1790
+ resource.shippingInfo = this.createShippingInfo(context, resource, {
1791
+ typeId: "shipping-method",
1792
+ id: shippingMethodRef.id
1793
+ });
1794
+ }
1795
+ const { taxedPrice, taxedShippingPrice } = calculateTaxTotals({
1796
+ lineItems: resource.lineItems,
1797
+ customLineItems: resource.customLineItems,
1798
+ shippingInfo: resource.shippingInfo,
1799
+ totalPrice: resource.totalPrice
1800
+ });
1801
+ resource.taxedPrice = resource.taxedPrice ?? taxedPrice;
1802
+ resource.taxedShippingPrice = resource.taxedShippingPrice ?? taxedShippingPrice;
1675
1803
  return this.saveNew(context, resource);
1676
1804
  }
1677
1805
  lineItemFromImportDraft(context, draft) {
@@ -1692,6 +1820,11 @@ var OrderRepository = class extends AbstractResourceRepository {
1692
1820
  else variant = product.masterData.current.variants.find((v) => v.sku === draft.variant.sku);
1693
1821
  if (!variant) throw new Error("Internal state error");
1694
1822
  } else throw new Error("No product found");
1823
+ const quantity = draft.quantity ?? 1;
1824
+ const totalPrice = createCentPrecisionMoney({
1825
+ ...draft.price.value,
1826
+ centAmount: (draft.price.value.centAmount ?? 0) * quantity
1827
+ });
1695
1828
  const lineItem = {
1696
1829
  ...getBaseResourceProperties(),
1697
1830
  custom: createCustomFields(draft.custom, context.projectKey, this._storage),
@@ -1702,12 +1835,13 @@ var OrderRepository = class extends AbstractResourceRepository {
1702
1835
  priceMode: "Platform",
1703
1836
  productId: product.id,
1704
1837
  productType: product.productType,
1705
- quantity: draft.quantity,
1838
+ quantity,
1706
1839
  state: draft.state || [],
1707
1840
  taxRate: draft.taxRate,
1841
+ taxedPrice: calculateTaxedPriceFromRate(totalPrice.centAmount, totalPrice.currencyCode, draft.taxRate),
1708
1842
  taxedPricePortions: [],
1709
1843
  perMethodTaxRate: [],
1710
- totalPrice: createCentPrecisionMoney(draft.price.value),
1844
+ totalPrice,
1711
1845
  variant: {
1712
1846
  id: variant.id,
1713
1847
  sku: variant.sku,
@@ -1718,18 +1852,24 @@ var OrderRepository = class extends AbstractResourceRepository {
1718
1852
  return lineItem;
1719
1853
  }
1720
1854
  customLineItemFromImportDraft(context, draft) {
1855
+ const quantity = draft.quantity ?? 1;
1856
+ const totalPrice = createCentPrecisionMoney({
1857
+ ...draft.money,
1858
+ centAmount: (draft.money.centAmount ?? 0) * quantity
1859
+ });
1721
1860
  const lineItem = {
1722
1861
  ...getBaseResourceProperties(),
1723
1862
  custom: createCustomFields(draft.custom, context.projectKey, this._storage),
1724
1863
  discountedPricePerQuantity: [],
1725
1864
  money: createTypedMoney(draft.money),
1726
1865
  name: draft.name,
1727
- quantity: draft.quantity ?? 0,
1866
+ quantity,
1728
1867
  perMethodTaxRate: [],
1729
1868
  priceMode: draft.priceMode ?? "Standard",
1730
1869
  slug: draft.slug,
1731
1870
  state: [],
1732
- totalPrice: createCentPrecisionMoney(draft.money),
1871
+ totalPrice,
1872
+ taxedPrice: calculateTaxedPriceFromRate(totalPrice.centAmount, totalPrice.currencyCode, draft.taxRate),
1733
1873
  taxedPricePortions: []
1734
1874
  };
1735
1875
  return lineItem;
@@ -1743,6 +1883,32 @@ var OrderRepository = class extends AbstractResourceRepository {
1743
1883
  if (result.count > 1) throw new Error("Duplicate order numbers");
1744
1884
  return;
1745
1885
  }
1886
+ createShippingInfo(context, resource, shippingMethodRef) {
1887
+ const cartLikeForMatching = {
1888
+ ...resource,
1889
+ cartState: "Active",
1890
+ inventoryMode: "None",
1891
+ itemShippingAddresses: [],
1892
+ priceRoundingMode: resource.taxRoundingMode || "HalfEven",
1893
+ taxMode: resource.taxMode || "Platform",
1894
+ taxCalculationMode: resource.taxCalculationMode || "LineItemLevel",
1895
+ taxRoundingMode: resource.taxRoundingMode || "HalfEven",
1896
+ discountCodes: resource.discountCodes || [],
1897
+ directDiscounts: resource.directDiscounts || [],
1898
+ shippingInfo: void 0
1899
+ };
1900
+ const shippingMethods = getShippingMethodsMatchingCart(context, this._storage, cartLikeForMatching, { expand: ["zoneRates[*].zone"] });
1901
+ const method = shippingMethods.results.find((candidate) => candidate.id === shippingMethodRef.id);
1902
+ if (!method) throw new CommercetoolsError({
1903
+ code: "ShippingMethodDoesNotMatchCart",
1904
+ message: `The shipping method with ID '${shippingMethodRef.id}' is not allowed for the order with ID '${resource.id}'.`
1905
+ });
1906
+ const baseShippingInfo = createShippingInfoFromMethod(context, this._storage, resource, method);
1907
+ return {
1908
+ ...baseShippingInfo,
1909
+ deliveries: []
1910
+ };
1911
+ }
1746
1912
  };
1747
1913
 
1748
1914
  //#endregion