@labdigital/commercetools-mock 3.0.0-beta.2 → 3.0.0-beta.4

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.
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import * as _commercetools_platform_sdk0 from "@commercetools/platform-sdk";
3
- import { Address, AssociateRole, AssociateRoleDraft, AttributeGroup, AttributeGroupDraft, BaseResource, BusinessUnit, BusinessUnitDraft, Cart, CartDiscount, CartDiscountDraft, CartDraft, CartReference, Category, CategoryDraft, Channel, ChannelDraft, ChannelReference, ChannelResourceIdentifier, CustomFields, CustomObject, CustomObjectDraft, Customer, CustomerCreatePasswordResetToken, CustomerDraft, CustomerGroup, CustomerGroupDraft, CustomerResetPassword, CustomerToken, DiscountCode, DiscountCodeDraft, DiscountGroup, DiscountGroupDraft, Extension, ExtensionDraft, FacetResults, FieldContainer, FilteredFacetResult, InventoryEntry, InventoryEntryDraft, LineItem, LineItemDraft, MyCustomerChangePassword, MyCustomerEmailVerify, MyCustomerResetPassword, MyOrderFromCartDraft, MyQuoteRequestDraft, Order, OrderEdit, OrderEditDraft, OrderFromCartDraft, OrderImportDraft, OrderPagedSearchResponse, OrderSearchRequest, PagedQueryResponse, Payment, PaymentDraft, Product, ProductDiscount, ProductDiscountDraft, ProductDraft, ProductPagedSearchResponse, ProductProjection, ProductProjectionPagedSearchResponse, ProductSearchRequest, ProductSelection, ProductSelectionDraft, ProductTailoring, ProductType, ProductTypeDraft, Project, QueryParam, Quote, QuoteDraft, QuoteRequest, QuoteRequestDraft, RangeFacetResult, RecurrencePolicy, RecurrencePolicyDraft, RecurringOrder, RecurringOrderDraft, ResourceIdentifier, Review, ReviewDraft, ReviewRatingStatistics, ShippingInfo, ShippingMethod, ShippingMethodDraft, ShippingMethodReference, ShoppingList, ShoppingListDraft, ShoppingListLineItem, StagedQuote, StagedQuoteDraft, StandalonePrice, StandalonePriceDraft, State, StateDraft, Store, StoreDraft, StoreKeyReference, Subscription, SubscriptionDraft, TaxCategory, TaxCategoryDraft, TermFacetResult, Type, TypeDraft, TypeResourceIdentifier, UpdateAction, Zone, ZoneDraft, ZoneReference } from "@commercetools/platform-sdk";
3
+ import { Address, AssociateRole, AssociateRoleDraft, AttributeGroup, AttributeGroupDraft, BaseResource, BusinessUnit, BusinessUnitDraft, Cart, CartDiscount, CartDiscountDraft, CartDraft, CartReference, Category, CategoryDraft, Channel, ChannelDraft, ChannelReference, ChannelResourceIdentifier, CustomFields, CustomObject, CustomObjectDraft, Customer, CustomerCreatePasswordResetToken, CustomerDraft, CustomerGroup, CustomerGroupDraft, CustomerResetPassword, CustomerToken, DiscountCode, DiscountCodeDraft, DiscountGroup, DiscountGroupDraft, Extension, ExtensionDraft, ExternalTaxRateDraft, FacetResults, FieldContainer, FilteredFacetResult, InventoryEntry, InventoryEntryDraft, LineItem, LineItemDraft, MyCustomerChangePassword, MyCustomerEmailVerify, MyCustomerResetPassword, MyOrderFromCartDraft, MyQuoteRequestDraft, Order, OrderEdit, OrderEditDraft, OrderFromCartDraft, OrderImportDraft, OrderPagedSearchResponse, OrderSearchRequest, PagedQueryResponse, Payment, PaymentDraft, Product, ProductDiscount, ProductDiscountDraft, ProductDraft, ProductPagedSearchResponse, ProductProjection, ProductProjectionPagedSearchResponse, ProductSearchRequest, ProductSelection, ProductSelectionDraft, ProductTailoring, ProductType, ProductTypeDraft, Project, QueryParam, Quote, QuoteDraft, QuoteRequest, QuoteRequestDraft, RangeFacetResult, RecurrencePolicy, RecurrencePolicyDraft, RecurringOrder, RecurringOrderDraft, ResourceIdentifier, Review, ReviewDraft, ReviewRatingStatistics, ShippingInfo, ShippingMethod, ShippingMethodDraft, ShippingMethodReference, ShoppingList, ShoppingListDraft, ShoppingListLineItem, StagedQuote, StagedQuoteDraft, StandalonePrice, StandalonePriceDraft, State, StateDraft, Store, StoreDraft, StoreKeyReference, Subscription, SubscriptionDraft, TaxCategory, TaxCategoryDraft, TermFacetResult, Type, TypeDraft, TypeResourceIdentifier, UpdateAction, Zone, ZoneDraft, ZoneReference } from "@commercetools/platform-sdk";
4
4
 
5
5
  //#region src/repositories/abstract.d.ts
6
6
  type QueryParams$1 = {
@@ -110,8 +110,8 @@ declare class CartRepository extends AbstractResourceRepository<"cart"> {
110
110
  constructor(config: Config);
111
111
  create(context: RepositoryContext, draft: CartDraft): Promise<Cart>;
112
112
  getActiveCart(projectKey: string): Promise<Cart | undefined>;
113
- draftLineItemtoLineItem: (projectKey: string, draftLineItem: LineItemDraft, currency: string, country: string | undefined) => Promise<LineItem>;
114
- createShippingInfo(context: RepositoryContext, resource: Writable<Cart>, shippingMethodRef: NonNullable<CartDraft["shippingMethod"]>): Promise<NonNullable<Cart["shippingInfo"]>>;
113
+ draftLineItemtoLineItem: (projectKey: string, draftLineItem: LineItemDraft, currency: string, country: string | undefined, taxMode?: Cart["taxMode"], roundingMode?: Cart["taxRoundingMode"]) => Promise<LineItem>;
114
+ createShippingInfo(context: RepositoryContext, resource: Writable<Cart>, shippingMethodRef: NonNullable<CartDraft["shippingMethod"]>, externalTaxRate?: ExternalTaxRateDraft): Promise<NonNullable<Cart["shippingInfo"]>>;
115
115
  }
116
116
  //#endregion
117
117
  //#region src/orderSearch.d.ts
@@ -131,7 +131,9 @@ declare class OrderRepository extends AbstractResourceRepository<"order"> {
131
131
  private lineItemFromImportDraft;
132
132
  private customLineItemFromImportDraft;
133
133
  getWithOrderNumber(context: RepositoryContext, orderNumber: string, params?: QueryParams$1): Promise<Order | undefined>;
134
- createShippingInfo(context: RepositoryContext, resource: Writable<Order>, shippingMethodRef: ShippingMethodReference): Promise<ShippingInfo>;
134
+ createShippingInfo(context: RepositoryContext, resource: Writable<Order>, shippingMethodRef: ShippingMethodReference): Promise<Writable<ShippingInfo>>;
135
+ private shippingInfoFromImportDraft;
136
+ private deliveriesFromImportDraft;
135
137
  search(context: RepositoryContext, searchRequest: OrderSearchRequest): Promise<OrderPagedSearchResponse>;
136
138
  }
137
139
  //#endregion
@@ -1715,4 +1717,4 @@ type Config = {
1715
1717
  };
1716
1718
  //#endregion
1717
1719
  export { QueryParams as a, ResourceType as c, ProjectStorage as i, RepositoryMap as l, AbstractStorage as n, PagedQueryResponseMap as o, GetParams as r, ResourceMap as s, Config as t, GetParams$1 as u };
1718
- //# sourceMappingURL=config-BcNSzPZz.d.mts.map
1720
+ //# sourceMappingURL=config-CN3DgmWu.d.mts.map
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { a as QueryParams, c as ResourceType, i as ProjectStorage, l as RepositoryMap, n as AbstractStorage, o as PagedQueryResponseMap, r as GetParams, s as ResourceMap, t as Config, u as GetParams$1 } from "./config-BcNSzPZz.mjs";
1
+ import { a as QueryParams, c as ResourceType, i as ProjectStorage, l as RepositoryMap, n as AbstractStorage, o as PagedQueryResponseMap, r as GetParams, s as ResourceMap, t as Config, u as GetParams$1 } from "./config-CN3DgmWu.mjs";
2
2
  import * as fastify from "fastify";
3
3
  import { FastifyBaseLogger, FastifyBaseLogger as FastifyBaseLogger$1, FastifyInstance } from "fastify";
4
4
  import * as msw from "msw";
package/dist/index.mjs CHANGED
@@ -1811,6 +1811,39 @@ var ProductTailoringUpdateHandler = class extends AbstractUpdateHandler {
1811
1811
 
1812
1812
  //#endregion
1813
1813
  //#region src/lib/tax.ts
1814
+ const roundCents = (value, mode = "HalfEven") => roundDecimal(new Decimal$1(value), mode).toNumber();
1815
+ const buildTaxedPriceFromExternalAmount = (draft, roundingMode = "HalfEven") => {
1816
+ const taxRate = taxRateFromExternalDraft(draft.taxRate);
1817
+ const totalGross = createCentPrecisionMoney(draft.totalGross);
1818
+ const currencyCode = totalGross.currencyCode;
1819
+ const toMoney = (centAmount) => createCentPrecisionMoney({
1820
+ currencyCode,
1821
+ centAmount
1822
+ });
1823
+ const grossAmount = totalGross.centAmount;
1824
+ const taxAmount = taxRate.amount > 0 ? roundCents(new Decimal$1(grossAmount).mul(taxRate.amount).div(1 + taxRate.amount).toNumber(), roundingMode) : 0;
1825
+ return {
1826
+ totalNet: toMoney(grossAmount - taxAmount),
1827
+ totalGross,
1828
+ totalTax: taxAmount > 0 ? toMoney(taxAmount) : void 0,
1829
+ taxPortions: taxAmount > 0 ? [{
1830
+ rate: taxRate.amount,
1831
+ name: taxRate.name,
1832
+ amount: toMoney(taxAmount)
1833
+ }] : []
1834
+ };
1835
+ };
1836
+ const taxRateFromExternalDraft = (draft) => {
1837
+ const amount = draft.amount ?? draft.subRates?.reduce((acc, subRate) => acc + subRate.amount, 0) ?? 0;
1838
+ return {
1839
+ name: draft.name,
1840
+ amount,
1841
+ includedInPrice: draft.includedInPrice ?? false,
1842
+ country: draft.country,
1843
+ state: draft.state,
1844
+ subRates: draft.subRates
1845
+ };
1846
+ };
1814
1847
  const calculateTaxTotals = (resource) => {
1815
1848
  const taxedItemPrices = [];
1816
1849
  resource.lineItems.forEach((item) => {
@@ -1868,7 +1901,7 @@ const calculateTaxTotals = (resource) => {
1868
1901
  taxedShippingPrice
1869
1902
  };
1870
1903
  };
1871
- const buildTaxedPriceFromRate = (amount, currencyCode, taxRate) => {
1904
+ const calculateTaxedPriceFromRate = (amount, currencyCode, taxRate, roundingMode = "HalfEven") => {
1872
1905
  if (!taxRate) return;
1873
1906
  const toMoney = (centAmount) => createCentPrecisionMoney({
1874
1907
  type: "centPrecision",
@@ -1880,11 +1913,11 @@ const buildTaxedPriceFromRate = (amount, currencyCode, taxRate) => {
1880
1913
  let taxAmount;
1881
1914
  if (taxRate.includedInPrice) {
1882
1915
  grossAmount = amount;
1883
- taxAmount = Math.round(grossAmount * taxRate.amount / (1 + taxRate.amount));
1916
+ taxAmount = roundCents(new Decimal$1(grossAmount).mul(taxRate.amount).div(1 + taxRate.amount).toNumber(), roundingMode);
1884
1917
  netAmount = grossAmount - taxAmount;
1885
1918
  } else {
1886
1919
  netAmount = amount;
1887
- taxAmount = Math.round(netAmount * taxRate.amount);
1920
+ taxAmount = roundCents(new Decimal$1(netAmount).mul(taxRate.amount).toNumber(), roundingMode);
1888
1921
  grossAmount = netAmount + taxAmount;
1889
1922
  }
1890
1923
  return {
@@ -1898,10 +1931,9 @@ const buildTaxedPriceFromRate = (amount, currencyCode, taxRate) => {
1898
1931
  }] : []
1899
1932
  };
1900
1933
  };
1901
- const calculateTaxedPriceFromRate = (amount, currencyCode, taxRate) => buildTaxedPriceFromRate(amount, currencyCode, taxRate);
1902
- const calculateTaxedPrice = (amount, taxCategory, currency, country) => {
1934
+ const calculateTaxedPrice = (amount, taxCategory, currency, country, roundingMode = "HalfEven") => {
1903
1935
  if (!taxCategory || !taxCategory.rates.length) return;
1904
- const taxedItemPrice = buildTaxedPriceFromRate(amount, currency, taxCategory.rates.find((rate) => !rate.country || rate.country === country) || taxCategory.rates[0]);
1936
+ const taxedItemPrice = calculateTaxedPriceFromRate(amount, currency, taxCategory.rates.find((rate) => !rate.country || rate.country === country) || taxCategory.rates[0], roundingMode);
1905
1937
  if (!taxedItemPrice) return;
1906
1938
  return {
1907
1939
  totalNet: taxedItemPrice.totalNet,
@@ -2018,7 +2050,7 @@ const getShippingMethodsMatchingCart = async (context, storage, cart, params = {
2018
2050
  /**
2019
2051
  * Creates shipping info from a shipping method, handling all tax calculations and pricing logic.
2020
2052
  */
2021
- const createShippingInfoFromMethod = async (context, storage, resource, method) => {
2053
+ const createShippingInfoFromMethod = async (context, storage, resource, method, externalTaxRate) => {
2022
2054
  const country = resource.shippingAddress.country;
2023
2055
  const zoneRate = method.zoneRates.find((rate) => rate.zone.obj?.locations.some((loc) => loc.country === country));
2024
2056
  if (!zoneRate) throw new CommercetoolsError({
@@ -2030,13 +2062,17 @@ const createShippingInfoFromMethod = async (context, storage, resource, method)
2030
2062
  code: "InvalidOperation",
2031
2063
  message: "Shipping rate not found"
2032
2064
  }, 400);
2033
- const taxCategory = await storage.getByResourceIdentifier(context.projectKey, method.taxCategory);
2034
- const taxRate = taxCategory.rates.find((rate) => rate.country === country);
2035
- if (!taxRate) throw new CommercetoolsError({
2036
- code: "MissingTaxRateForCountry",
2037
- message: `Tax category '${taxCategory.id}' is missing a tax rate for country '${country}'.`,
2038
- taxCategoryId: taxCategory.id
2039
- });
2065
+ let taxRate;
2066
+ if (externalTaxRate) taxRate = taxRateFromExternalDraft(externalTaxRate);
2067
+ else {
2068
+ const taxCategory = await storage.getByResourceIdentifier(context.projectKey, method.taxCategory);
2069
+ taxRate = taxCategory.rates.find((rate) => rate.country === country);
2070
+ if (!taxRate) throw new CommercetoolsError({
2071
+ code: "MissingTaxRateForCountry",
2072
+ message: `Tax category '${taxCategory.id}' is missing a tax rate for country '${country}'.`,
2073
+ taxCategoryId: taxCategory.id
2074
+ });
2075
+ }
2040
2076
  const shippingRateTier = shippingRate.tiers.find((tier) => tier.isMatching);
2041
2077
  if (shippingRateTier && shippingRateTier.type !== "CartValue") throw new CommercetoolsError({
2042
2078
  code: "InvalidOperation",
@@ -2101,12 +2137,17 @@ const calculateLineItemTotalPrice = (lineItem) => {
2101
2137
  if (!lineItem.price?.value) return 0;
2102
2138
  return calculateMoneyTotalCentAmount(lineItem.price.value, lineItem.quantity);
2103
2139
  };
2140
+ const computeItemTaxedPrice = (item, roundingMode = "HalfEven") => {
2141
+ if (!item.taxRate) return void 0;
2142
+ return calculateTaxedPriceFromRate(item.totalPrice.centAmount, item.totalPrice.currencyCode, item.taxRate, roundingMode);
2143
+ };
2104
2144
  const calculateCartTotalPrice = (cart) => {
2105
2145
  return cart.lineItems.reduce((cur, item) => cur + item.totalPrice.centAmount, 0) + cart.customLineItems.reduce((cur, item) => cur + item.totalPrice.centAmount, 0);
2106
2146
  };
2107
- const createCustomLineItemFromDraft = async (projectKey, draft, storage, country) => {
2147
+ const createCustomLineItemFromDraft = async (projectKey, draft, storage, country, taxMode = "Platform", roundingMode = "HalfEven") => {
2108
2148
  const quantity = draft.quantity ?? 1;
2109
- const taxCategoryRef = draft.taxCategory ? await getReferenceFromResourceIdentifier(draft.taxCategory, projectKey, storage) : void 0;
2149
+ const isTaxExternal = taxMode === "External";
2150
+ const taxCategoryRef = !isTaxExternal && draft.taxCategory ? await getReferenceFromResourceIdentifier(draft.taxCategory, projectKey, storage) : void 0;
2110
2151
  let taxCategory;
2111
2152
  if (taxCategoryRef) try {
2112
2153
  taxCategory = await storage.get(projectKey, "tax-category", taxCategoryRef.id, {}) || void 0;
@@ -2115,8 +2156,22 @@ const createCustomLineItemFromDraft = async (projectKey, draft, storage, country
2115
2156
  currencyCode: draft.money.currencyCode,
2116
2157
  centAmount: calculateMoneyTotalCentAmount(draft.money, quantity)
2117
2158
  });
2118
- const taxedPrice = taxCategory ? calculateTaxedPrice(totalPrice.centAmount, taxCategory, totalPrice.currencyCode, country) : void 0;
2119
- const taxRate = taxCategory ? taxCategory.rates.find((rate) => !rate.country || rate.country === country) : void 0;
2159
+ const resolveTax = () => {
2160
+ if (isTaxExternal) {
2161
+ if (!draft.externalTaxRate) return {};
2162
+ const taxRate = taxRateFromExternalDraft(draft.externalTaxRate);
2163
+ return {
2164
+ taxRate,
2165
+ taxedPrice: calculateTaxedPriceFromRate(totalPrice.centAmount, totalPrice.currencyCode, taxRate, roundingMode)
2166
+ };
2167
+ }
2168
+ if (!taxCategory) return {};
2169
+ return {
2170
+ taxRate: taxCategory.rates.find((rate) => !rate.country || rate.country === country),
2171
+ taxedPrice: calculateTaxedPrice(totalPrice.centAmount, taxCategory, totalPrice.currencyCode, country, roundingMode)
2172
+ };
2173
+ };
2174
+ const { taxRate, taxedPrice } = resolveTax();
2120
2175
  return {
2121
2176
  id: v4(),
2122
2177
  key: draft.key,
@@ -2161,11 +2216,24 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
2161
2216
  super(storage);
2162
2217
  this.repository = repository;
2163
2218
  }
2219
+ async apply(context, resource, version, actions) {
2220
+ const updated = await super.apply(context, resource, version, actions);
2221
+ if (updated.version !== resource.version) {
2222
+ const cart = updated;
2223
+ if (cart.taxMode === "ExternalAmount") cart.taxedShippingPrice = cart.shippingInfo?.taxedPrice;
2224
+ else {
2225
+ const { taxedPrice, taxedShippingPrice } = calculateTaxTotals(cart);
2226
+ cart.taxedPrice = taxedPrice;
2227
+ cart.taxedShippingPrice = taxedShippingPrice;
2228
+ }
2229
+ }
2230
+ return updated;
2231
+ }
2164
2232
  addItemShippingAddress(context, resource, { action, address }) {
2165
2233
  const newAddress = createAddress(address, context.projectKey, this._storage);
2166
2234
  if (newAddress) resource.itemShippingAddresses.push(newAddress);
2167
2235
  }
2168
- async addLineItem(context, resource, { productId, variantId, sku, custom, quantity = 1, addedAt, key }) {
2236
+ async addLineItem(context, resource, { productId, variantId, sku, custom, quantity = 1, addedAt, key, externalTaxRate }) {
2169
2237
  let product = null;
2170
2238
  if (productId && variantId) product = await this._storage.get(context.projectKey, "product", productId, {});
2171
2239
  else if (sku) {
@@ -2189,6 +2257,7 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
2189
2257
  if (x.productId === product?.id && x.variant.id === variant?.id) {
2190
2258
  x.quantity += quantity;
2191
2259
  x.totalPrice.centAmount = calculateLineItemTotalPrice(x);
2260
+ x.taxedPrice = computeItemTaxedPrice(x, resource.taxRoundingMode);
2192
2261
  }
2193
2262
  });
2194
2263
  else {
@@ -2210,6 +2279,8 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
2210
2279
  currencyCode: price.value.currencyCode,
2211
2280
  centAmount: calculateMoneyTotalCentAmount(price.value, quantity)
2212
2281
  });
2282
+ const taxRate = resource.taxMode === "External" && externalTaxRate ? taxRateFromExternalDraft(externalTaxRate) : void 0;
2283
+ const taxedPrice = taxRate ? calculateTaxedPriceFromRate(totalPrice.centAmount, totalPrice.currencyCode, taxRate, resource.taxRoundingMode) : void 0;
2213
2284
  resource.lineItems.push({
2214
2285
  id: v4(),
2215
2286
  key,
@@ -2221,6 +2292,8 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
2221
2292
  name: product.masterData.current.name,
2222
2293
  variant,
2223
2294
  price,
2295
+ taxRate,
2296
+ taxedPrice,
2224
2297
  taxedPricePortions: [],
2225
2298
  perMethodTaxRate: [],
2226
2299
  totalPrice,
@@ -2257,6 +2330,7 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
2257
2330
  if (x.id === lineItemId && quantity) {
2258
2331
  x.quantity = quantity;
2259
2332
  x.totalPrice.centAmount = calculateLineItemTotalPrice(x);
2333
+ x.taxedPrice = computeItemTaxedPrice(x, resource.taxRoundingMode);
2260
2334
  }
2261
2335
  });
2262
2336
  resource.totalPrice.centAmount = calculateCartTotalPrice(resource);
@@ -2283,21 +2357,23 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
2283
2357
  if (x.id === lineItemId && quantity) {
2284
2358
  x.quantity -= quantity;
2285
2359
  x.totalPrice.centAmount = calculateLineItemTotalPrice(x);
2360
+ x.taxedPrice = computeItemTaxedPrice(x, resource.taxRoundingMode);
2286
2361
  }
2287
2362
  });
2288
2363
  resource.totalPrice.centAmount = calculateCartTotalPrice(resource);
2289
2364
  }
2290
- async addCustomLineItem(context, resource, { money, name, slug, quantity = 1, taxCategory, custom, priceMode = "Standard", key }) {
2365
+ async addCustomLineItem(context, resource, { money, name, slug, quantity = 1, taxCategory, externalTaxRate, custom, priceMode = "Standard", key }) {
2291
2366
  const customLineItem = await createCustomLineItemFromDraft(context.projectKey, {
2292
2367
  money,
2293
2368
  name,
2294
2369
  slug,
2295
2370
  quantity,
2296
2371
  taxCategory,
2372
+ externalTaxRate,
2297
2373
  custom,
2298
2374
  priceMode,
2299
2375
  key
2300
- }, this._storage, resource.shippingAddress?.country ?? resource.country);
2376
+ }, this._storage, resource.shippingAddress?.country ?? resource.country, resource.taxMode, resource.taxRoundingMode);
2301
2377
  resource.customLineItems.push(customLineItem);
2302
2378
  resource.totalPrice.centAmount = calculateCartTotalPrice(resource);
2303
2379
  }
@@ -2341,6 +2417,7 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
2341
2417
  currencyCode: customLineItem.money.currencyCode,
2342
2418
  centAmount: calculateMoneyTotalCentAmount(customLineItem.money, quantity)
2343
2419
  });
2420
+ customLineItem.taxedPrice = computeItemTaxedPrice(customLineItem, resource.taxRoundingMode);
2344
2421
  };
2345
2422
  if (customLineItemId) {
2346
2423
  customLineItem = resource.customLineItems.find((x) => x.id === customLineItemId);
@@ -2364,6 +2441,7 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
2364
2441
  currencyCode: money.currencyCode,
2365
2442
  centAmount: calculateMoneyTotalCentAmount(money, customLineItem.quantity)
2366
2443
  });
2444
+ customLineItem.taxedPrice = computeItemTaxedPrice(customLineItem, resource.taxRoundingMode);
2367
2445
  };
2368
2446
  if (customLineItemId) {
2369
2447
  customLineItem = resource.customLineItems.find((x) => x.id === customLineItemId);
@@ -2424,22 +2502,28 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
2424
2502
  });
2425
2503
  }
2426
2504
  async setCustomShippingMethod(context, resource, { shippingMethodName, shippingRate, taxCategory, externalTaxRate }) {
2427
- if (externalTaxRate) throw new CommercetoolsError({
2505
+ const isTaxExternal = resource.taxMode === "External";
2506
+ if (externalTaxRate && !isTaxExternal) throw new CommercetoolsError({
2428
2507
  code: "InvalidOperation",
2429
- message: "External tax rate is not supported"
2508
+ message: "An external tax rate can only be set for a Cart with External tax mode."
2430
2509
  });
2431
- const tax = taxCategory ? await this._storage.getByResourceIdentifier(context.projectKey, taxCategory) : void 0;
2510
+ const tax = !isTaxExternal && taxCategory ? await this._storage.getByResourceIdentifier(context.projectKey, taxCategory) : void 0;
2511
+ const taxRate = isTaxExternal && externalTaxRate ? taxRateFromExternalDraft(externalTaxRate) : void 0;
2512
+ const price = createCentPrecisionMoney(shippingRate.price);
2513
+ const taxedPrice = taxRate ? calculateTaxedPriceFromRate(price.centAmount, price.currencyCode, taxRate, resource.taxRoundingMode) : void 0;
2432
2514
  resource.shippingInfo = {
2433
2515
  shippingMethodName,
2434
- price: createCentPrecisionMoney(shippingRate.price),
2516
+ price,
2435
2517
  shippingRate: {
2436
- price: createCentPrecisionMoney(shippingRate.price),
2518
+ price,
2437
2519
  tiers: []
2438
2520
  },
2439
2521
  taxCategory: tax ? {
2440
2522
  typeId: "tax-category",
2441
2523
  id: tax?.id
2442
2524
  } : void 0,
2525
+ taxRate,
2526
+ taxedPrice,
2443
2527
  shippingMethodState: "MatchesCart"
2444
2528
  };
2445
2529
  }
@@ -2526,6 +2610,7 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
2526
2610
  currencyCode: lineItem.price.value.currencyCode,
2527
2611
  centAmount: lineItemTotal
2528
2612
  });
2613
+ lineItem.taxedPrice = computeItemTaxedPrice(lineItem, resource.taxRoundingMode);
2529
2614
  resource.totalPrice.centAmount = calculateCartTotalPrice(resource);
2530
2615
  }
2531
2616
  setLineItemShippingDetails(context, resource, { action, shippingDetails, lineItemId, lineItemKey }) {
@@ -2579,10 +2664,134 @@ var CartUpdateHandler = class extends AbstractUpdateHandler {
2579
2664
  fields: custom.fields || {}
2580
2665
  };
2581
2666
  }
2582
- async setShippingMethod(context, resource, { shippingMethod }) {
2583
- if (shippingMethod) resource.shippingInfo = await this.repository.createShippingInfo(context, resource, shippingMethod);
2667
+ async setShippingMethod(context, resource, { shippingMethod, externalTaxRate }) {
2668
+ if (shippingMethod) resource.shippingInfo = await this.repository.createShippingInfo(context, resource, shippingMethod, externalTaxRate ?? void 0);
2584
2669
  else resource.shippingInfo = void 0;
2585
2670
  }
2671
+ setLineItemTaxRate(_context, resource, { lineItemId, lineItemKey, externalTaxRate }) {
2672
+ if (resource.taxMode !== "External") throw new CommercetoolsError({
2673
+ code: "InvalidOperation",
2674
+ message: "A line item tax rate can only be set for a Cart with External tax mode."
2675
+ });
2676
+ const lineItem = resource.lineItems.find((x) => lineItemId && x.id === lineItemId || lineItemKey && x.key === lineItemKey);
2677
+ if (!lineItem) throw new CommercetoolsError({
2678
+ code: "General",
2679
+ message: lineItemKey ? `A line item with key '${lineItemKey}' not found.` : `A line item with ID '${lineItemId}' not found.`
2680
+ });
2681
+ const writable = lineItem;
2682
+ writable.taxRate = externalTaxRate ? taxRateFromExternalDraft(externalTaxRate) : void 0;
2683
+ writable.taxedPrice = computeItemTaxedPrice(writable, resource.taxRoundingMode);
2684
+ }
2685
+ setCustomLineItemTaxRate(_context, resource, { customLineItemId, customLineItemKey, externalTaxRate }) {
2686
+ if (resource.taxMode !== "External") throw new CommercetoolsError({
2687
+ code: "InvalidOperation",
2688
+ message: "A custom line item tax rate can only be set for a Cart with External tax mode."
2689
+ });
2690
+ const customLineItem = resource.customLineItems.find((x) => customLineItemId && x.id === customLineItemId || customLineItemKey && x.key === customLineItemKey);
2691
+ if (!customLineItem) throw new CommercetoolsError({
2692
+ code: "General",
2693
+ message: customLineItemKey ? `A custom line item with key '${customLineItemKey}' not found.` : `A custom line item with ID '${customLineItemId}' not found.`
2694
+ });
2695
+ const writable = customLineItem;
2696
+ writable.taxRate = externalTaxRate ? taxRateFromExternalDraft(externalTaxRate) : void 0;
2697
+ writable.taxedPrice = computeItemTaxedPrice(writable, resource.taxRoundingMode);
2698
+ }
2699
+ setShippingMethodTaxRate(_context, resource, { externalTaxRate }) {
2700
+ if (resource.taxMode !== "External") throw new CommercetoolsError({
2701
+ code: "InvalidOperation",
2702
+ message: "A shipping method tax rate can only be set for a Cart with External tax mode."
2703
+ });
2704
+ if (!resource.shippingInfo) throw new CommercetoolsError({
2705
+ code: "InvalidOperation",
2706
+ message: "Cart has no shipping method."
2707
+ });
2708
+ const shippingInfo = resource.shippingInfo;
2709
+ const taxRate = externalTaxRate ? taxRateFromExternalDraft(externalTaxRate) : void 0;
2710
+ shippingInfo.taxRate = taxRate;
2711
+ shippingInfo.taxedPrice = taxRate ? calculateTaxedPriceFromRate(shippingInfo.price.centAmount, shippingInfo.price.currencyCode, taxRate, resource.taxRoundingMode) : void 0;
2712
+ }
2713
+ setLineItemTaxAmount(_context, resource, { lineItemId, lineItemKey, externalTaxAmount }) {
2714
+ if (resource.taxMode !== "ExternalAmount") throw new CommercetoolsError({
2715
+ code: "InvalidOperation",
2716
+ message: "A line item tax amount can only be set for a Cart with ExternalAmount tax mode."
2717
+ });
2718
+ const lineItem = resource.lineItems.find((x) => lineItemId && x.id === lineItemId || lineItemKey && x.key === lineItemKey);
2719
+ if (!lineItem) throw new CommercetoolsError({
2720
+ code: "General",
2721
+ message: lineItemKey ? `A line item with key '${lineItemKey}' not found.` : `A line item with ID '${lineItemId}' not found.`
2722
+ });
2723
+ const writable = lineItem;
2724
+ if (externalTaxAmount) {
2725
+ writable.taxRate = taxRateFromExternalDraft(externalTaxAmount.taxRate);
2726
+ writable.taxedPrice = buildTaxedPriceFromExternalAmount(externalTaxAmount, resource.taxRoundingMode);
2727
+ } else {
2728
+ writable.taxRate = void 0;
2729
+ writable.taxedPrice = void 0;
2730
+ }
2731
+ }
2732
+ setCustomLineItemTaxAmount(_context, resource, { customLineItemId, customLineItemKey, externalTaxAmount }) {
2733
+ if (resource.taxMode !== "ExternalAmount") throw new CommercetoolsError({
2734
+ code: "InvalidOperation",
2735
+ message: "A custom line item tax amount can only be set for a Cart with ExternalAmount tax mode."
2736
+ });
2737
+ const customLineItem = resource.customLineItems.find((x) => customLineItemId && x.id === customLineItemId || customLineItemKey && x.key === customLineItemKey);
2738
+ if (!customLineItem) throw new CommercetoolsError({
2739
+ code: "General",
2740
+ message: customLineItemKey ? `A custom line item with key '${customLineItemKey}' not found.` : `A custom line item with ID '${customLineItemId}' not found.`
2741
+ });
2742
+ const writable = customLineItem;
2743
+ if (externalTaxAmount) {
2744
+ writable.taxRate = taxRateFromExternalDraft(externalTaxAmount.taxRate);
2745
+ writable.taxedPrice = buildTaxedPriceFromExternalAmount(externalTaxAmount, resource.taxRoundingMode);
2746
+ } else {
2747
+ writable.taxRate = void 0;
2748
+ writable.taxedPrice = void 0;
2749
+ }
2750
+ }
2751
+ setShippingMethodTaxAmount(_context, resource, { externalTaxAmount }) {
2752
+ if (resource.taxMode !== "ExternalAmount") throw new CommercetoolsError({
2753
+ code: "InvalidOperation",
2754
+ message: "A shipping method tax amount can only be set for a Cart with ExternalAmount tax mode."
2755
+ });
2756
+ if (!resource.shippingInfo) throw new CommercetoolsError({
2757
+ code: "InvalidOperation",
2758
+ message: "Cart has no shipping method."
2759
+ });
2760
+ const shippingInfo = resource.shippingInfo;
2761
+ if (externalTaxAmount) {
2762
+ shippingInfo.taxRate = taxRateFromExternalDraft(externalTaxAmount.taxRate);
2763
+ shippingInfo.taxedPrice = buildTaxedPriceFromExternalAmount(externalTaxAmount, resource.taxRoundingMode);
2764
+ } else {
2765
+ shippingInfo.taxRate = void 0;
2766
+ shippingInfo.taxedPrice = void 0;
2767
+ }
2768
+ }
2769
+ setCartTotalTax(_context, resource, { externalTotalGross, externalTaxPortions }) {
2770
+ if (resource.taxMode !== "ExternalAmount") throw new CommercetoolsError({
2771
+ code: "InvalidOperation",
2772
+ message: "A cart total tax can only be set for a Cart with ExternalAmount tax mode."
2773
+ });
2774
+ const totalGross = createCentPrecisionMoney(externalTotalGross);
2775
+ const currencyCode = totalGross.currencyCode;
2776
+ const portions = externalTaxPortions?.map((portion) => ({
2777
+ name: portion.name,
2778
+ rate: portion.rate,
2779
+ amount: createCentPrecisionMoney(portion.amount)
2780
+ })) ?? [];
2781
+ const totalTaxCentAmount = portions.reduce((acc, portion) => acc + portion.amount.centAmount, 0);
2782
+ resource.taxedPrice = {
2783
+ totalNet: createCentPrecisionMoney({
2784
+ currencyCode,
2785
+ centAmount: totalGross.centAmount - totalTaxCentAmount
2786
+ }),
2787
+ totalGross,
2788
+ totalTax: totalTaxCentAmount > 0 ? createCentPrecisionMoney({
2789
+ currencyCode,
2790
+ centAmount: totalTaxCentAmount
2791
+ }) : void 0,
2792
+ taxPortions: portions
2793
+ };
2794
+ }
2586
2795
  setShippingAddressCustomField(context, resource, { name, value }) {
2587
2796
  if (!resource.shippingAddress) throw new CommercetoolsError({
2588
2797
  code: "InvalidOperation",
@@ -2633,8 +2842,10 @@ var CartRepository = class extends AbstractResourceRepository {
2633
2842
  id: draft.businessUnit.id,
2634
2843
  key: draft.businessUnit.key
2635
2844
  });
2636
- const lineItems = draft.lineItems ? await Promise.all(draft.lineItems.map((draftLineItem) => this.draftLineItemtoLineItem(context.projectKey, draftLineItem, draft.currency, draft.country))) : [];
2637
- const customLineItems = draft.customLineItems ? await Promise.all(draft.customLineItems.map((draftCustomLineItem) => createCustomLineItemFromDraft(context.projectKey, draftCustomLineItem, this._storage, draft.shippingAddress?.country ?? draft.country))) : [];
2845
+ const taxMode = draft.taxMode ?? "Platform";
2846
+ const taxRoundingMode = draft.taxRoundingMode ?? "HalfEven";
2847
+ const lineItems = draft.lineItems ? await Promise.all(draft.lineItems.map((draftLineItem) => this.draftLineItemtoLineItem(context.projectKey, draftLineItem, draft.currency, draft.country, taxMode, taxRoundingMode))) : [];
2848
+ const customLineItems = draft.customLineItems ? await Promise.all(draft.customLineItems.map((draftCustomLineItem) => createCustomLineItemFromDraft(context.projectKey, draftCustomLineItem, this._storage, draft.shippingAddress?.country ?? draft.country, taxMode, taxRoundingMode))) : [];
2638
2849
  const discountCodeInfo = [];
2639
2850
  if (draft.discountCodes?.length) {
2640
2851
  const seen = /* @__PURE__ */ new Set();
@@ -2667,8 +2878,8 @@ var CartRepository = class extends AbstractResourceRepository {
2667
2878
  locale: draft.locale,
2668
2879
  priceRoundingMode: draft.priceRoundingMode ?? "HalfEven",
2669
2880
  taxCalculationMode: draft.taxCalculationMode ?? "LineItemLevel",
2670
- taxMode: draft.taxMode ?? "Platform",
2671
- taxRoundingMode: draft.taxRoundingMode ?? "HalfEven",
2881
+ taxMode,
2882
+ taxRoundingMode,
2672
2883
  totalPrice: {
2673
2884
  type: "centPrecision",
2674
2885
  centAmount: 0,
@@ -2691,7 +2902,7 @@ var CartRepository = class extends AbstractResourceRepository {
2691
2902
  typeId: "store",
2692
2903
  key: draft.store.key
2693
2904
  } : void 0;
2694
- if (draft.shippingMethod) resource.shippingInfo = await this.createShippingInfo(context, resource, draft.shippingMethod);
2905
+ if (draft.shippingMethod) resource.shippingInfo = await this.createShippingInfo(context, resource, draft.shippingMethod, draft.externalTaxRateForShippingMethod ?? void 0);
2695
2906
  const { taxedPrice, taxedShippingPrice } = calculateTaxTotals(resource);
2696
2907
  resource.taxedPrice = taxedPrice;
2697
2908
  resource.taxedShippingPrice = taxedShippingPrice;
@@ -2701,7 +2912,7 @@ var CartRepository = class extends AbstractResourceRepository {
2701
2912
  const results = await this._storage.query(projectKey, this.getTypeId(), { where: [`cartState="Active"`] });
2702
2913
  if (results.count > 0) return results.results[0];
2703
2914
  }
2704
- draftLineItemtoLineItem = async (projectKey, draftLineItem, currency, country) => {
2915
+ draftLineItemtoLineItem = async (projectKey, draftLineItem, currency, country, taxMode = "Platform", roundingMode = "HalfEven") => {
2705
2916
  const { productId, quantity, variantId, sku } = draftLineItem;
2706
2917
  let product = null;
2707
2918
  if (productId && variantId) product = await this._storage.get(projectKey, "product", productId, {});
@@ -2736,6 +2947,8 @@ var CartRepository = class extends AbstractResourceRepository {
2736
2947
  currencyCode: price.value.currencyCode,
2737
2948
  centAmount: calculateMoneyTotalCentAmount(price.value, quant)
2738
2949
  });
2950
+ const taxRate = taxMode === "External" && draftLineItem.externalTaxRate ? taxRateFromExternalDraft(draftLineItem.externalTaxRate) : void 0;
2951
+ const taxedPrice = taxRate ? calculateTaxedPriceFromRate(totalPrice.centAmount, totalPrice.currencyCode, taxRate, roundingMode) : void 0;
2739
2952
  return {
2740
2953
  id: v4(),
2741
2954
  productId: product.id,
@@ -2746,6 +2959,8 @@ var CartRepository = class extends AbstractResourceRepository {
2746
2959
  variant,
2747
2960
  price,
2748
2961
  totalPrice,
2962
+ taxRate,
2963
+ taxedPrice,
2749
2964
  taxedPricePortions: [],
2750
2965
  perMethodTaxRate: [],
2751
2966
  quantity: quant,
@@ -2756,10 +2971,10 @@ var CartRepository = class extends AbstractResourceRepository {
2756
2971
  custom: await createCustomFields(draftLineItem.custom, projectKey, this._storage)
2757
2972
  };
2758
2973
  };
2759
- async createShippingInfo(context, resource, shippingMethodRef) {
2760
- if (resource.taxMode === "External") throw new CommercetoolsError({
2974
+ async createShippingInfo(context, resource, shippingMethodRef, externalTaxRate) {
2975
+ if (resource.taxMode === "External" && !externalTaxRate) throw new CommercetoolsError({
2761
2976
  code: "InvalidOperation",
2762
- message: "External tax rate is not supported"
2977
+ message: "An external tax rate is required for a cart with External tax mode."
2763
2978
  });
2764
2979
  await this._storage.getByResourceIdentifier(context.projectKey, shippingMethodRef);
2765
2980
  const method = (await getShippingMethodsMatchingCart(context, this._storage, resource, { expand: ["zoneRates[*].zone"] })).results.find((candidate) => shippingMethodRef.id ? candidate.id === shippingMethodRef.id : candidate.key === shippingMethodRef.key);
@@ -2767,7 +2982,7 @@ var CartRepository = class extends AbstractResourceRepository {
2767
2982
  code: "ShippingMethodDoesNotMatchCart",
2768
2983
  message: `The shipping method with ${shippingMethodRef.id ? `ID '${shippingMethodRef.id}'` : `key '${shippingMethodRef.key}'`} is not allowed for the cart with ID '${resource.id}'.`
2769
2984
  });
2770
- return await createShippingInfoFromMethod(context, this._storage, resource, method);
2985
+ return await createShippingInfoFromMethod(context, this._storage, resource, method, resource.taxMode === "External" ? externalTaxRate : void 0);
2771
2986
  }
2772
2987
  };
2773
2988
 
@@ -3120,21 +3335,7 @@ var OrderRepository = class extends AbstractResourceRepository {
3120
3335
  customLineItems,
3121
3336
  totalPrice: createCentPrecisionMoney(draft.totalPrice)
3122
3337
  };
3123
- if (draft.shippingInfo?.shippingMethod) {
3124
- const { ...shippingMethodRef } = draft.shippingInfo.shippingMethod;
3125
- if (shippingMethodRef.key && !shippingMethodRef.id) {
3126
- const shippingMethod = await this._storage.getByResourceIdentifier(context.projectKey, shippingMethodRef);
3127
- if (!shippingMethod) throw new CommercetoolsError({
3128
- code: "General",
3129
- message: `A shipping method with key '${shippingMethodRef.key}' does not exist.`
3130
- });
3131
- shippingMethodRef.id = shippingMethod.id;
3132
- }
3133
- resource.shippingInfo = await this.createShippingInfo(context, resource, {
3134
- typeId: "shipping-method",
3135
- id: shippingMethodRef.id
3136
- });
3137
- }
3338
+ if (draft.shippingInfo) resource.shippingInfo = await this.shippingInfoFromImportDraft(context, resource, draft.shippingInfo);
3138
3339
  const { taxedPrice, taxedShippingPrice } = calculateTaxTotals({
3139
3340
  lineItems: resource.lineItems,
3140
3341
  customLineItems: resource.customLineItems,
@@ -3256,6 +3457,39 @@ var OrderRepository = class extends AbstractResourceRepository {
3256
3457
  deliveries: []
3257
3458
  };
3258
3459
  }
3460
+ async shippingInfoFromImportDraft(context, resource, draft) {
3461
+ if (!draft.shippingMethod) return;
3462
+ const { ...shippingMethodRef } = draft.shippingMethod;
3463
+ if (shippingMethodRef.key && !shippingMethodRef.id) {
3464
+ const shippingMethod = await this._storage.getByResourceIdentifier(context.projectKey, shippingMethodRef);
3465
+ if (!shippingMethod) throw new CommercetoolsError({
3466
+ code: "General",
3467
+ message: `A shipping method with key '${shippingMethodRef.key}' does not exist.`
3468
+ });
3469
+ shippingMethodRef.id = shippingMethod.id;
3470
+ }
3471
+ const shippingInfo = await this.createShippingInfo(context, resource, {
3472
+ typeId: "shipping-method",
3473
+ id: shippingMethodRef.id
3474
+ });
3475
+ shippingInfo.deliveries = await this.deliveriesFromImportDraft(context, draft);
3476
+ return shippingInfo;
3477
+ }
3478
+ async deliveriesFromImportDraft(context, draft) {
3479
+ if (!draft.deliveries) return [];
3480
+ return Promise.all(draft.deliveries.map(async (deliveryDraft) => ({
3481
+ ...getBaseResourceProperties(),
3482
+ key: deliveryDraft.key,
3483
+ items: deliveryDraft.items ?? [],
3484
+ parcels: await Promise.all(deliveryDraft.parcels?.map(async (parcel) => ({
3485
+ ...getBaseResourceProperties(),
3486
+ ...parcel,
3487
+ custom: await createCustomFields(parcel.custom, context.projectKey, this._storage)
3488
+ })) ?? []),
3489
+ address: createAddress(deliveryDraft.address, context.projectKey, this._storage),
3490
+ custom: await createCustomFields(deliveryDraft.custom, context.projectKey, this._storage)
3491
+ })));
3492
+ }
3259
3493
  async search(context, searchRequest) {
3260
3494
  return await this._searchService.search(context.projectKey, searchRequest);
3261
3495
  }