@labdigital/commercetools-mock 3.0.0-beta.3 → 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.
@@ -2160,4 +2160,637 @@ describe("Cart Update Actions", () => {
2160
2160
  expect(customLineItem.taxedPrice.taxPortions).toHaveLength(1);
2161
2161
  expect(customLineItem.taxedPrice.taxPortions[0].rate).toBe(0.21);
2162
2162
  });
2163
+
2164
+ describe("External tax mode", () => {
2165
+ // €10.00 product, picked so tax math stays trivial: 1000 cents × rate.
2166
+ const simpleProductDraft = (sku: string) => ({
2167
+ ...productDraft,
2168
+ masterVariant: {
2169
+ sku,
2170
+ prices: [
2171
+ {
2172
+ value: {
2173
+ type: "centPrecision" as const,
2174
+ currencyCode: "EUR",
2175
+ centAmount: 1000,
2176
+ fractionDigits: 2,
2177
+ } as CentPrecisionMoney,
2178
+ },
2179
+ ],
2180
+ attributes: [],
2181
+ },
2182
+ variants: [],
2183
+ slug: { "nl-NL": `simple-${sku}` },
2184
+ });
2185
+
2186
+ test("addLineItem applies externalTaxRate and aggregates cart taxedPrice", async () => {
2187
+ const product = await productFactory.create(simpleProductDraft("ext-1"));
2188
+ const externalCart = await cartFactory.create({
2189
+ currency: "EUR",
2190
+ country: "NL",
2191
+ taxMode: "External",
2192
+ });
2193
+ assert(product, "product not created");
2194
+
2195
+ // €10 × 2 = €20 net; 10% tax = €2; gross = €22.
2196
+ const response = await ctMock.app.inject({
2197
+ method: "POST",
2198
+ url: `/dummy/carts/${externalCart.id}`,
2199
+ payload: {
2200
+ version: 1,
2201
+ actions: [
2202
+ {
2203
+ action: "addLineItem",
2204
+ sku: "ext-1",
2205
+ quantity: 2,
2206
+ externalTaxRate: {
2207
+ name: "External NL VAT",
2208
+ amount: 0.1,
2209
+ country: "NL",
2210
+ includedInPrice: false,
2211
+ },
2212
+ },
2213
+ ],
2214
+ },
2215
+ });
2216
+
2217
+ expect(response.statusCode).toBe(200);
2218
+ const cart = response.json();
2219
+ const lineItem = cart.lineItems[0];
2220
+ expect(lineItem.taxRate.name).toBe("External NL VAT");
2221
+ expect(lineItem.taxRate.amount).toBe(0.1);
2222
+ expect(lineItem.taxedPrice.totalNet.centAmount).toBe(2000);
2223
+ expect(lineItem.taxedPrice.totalGross.centAmount).toBe(2200);
2224
+ expect(cart.taxedPrice).toBeDefined();
2225
+ expect(cart.taxedPrice.totalNet.centAmount).toBe(2000);
2226
+ expect(cart.taxedPrice.totalGross.centAmount).toBe(2200);
2227
+ });
2228
+
2229
+ test("addCustomLineItem applies externalTaxRate", async () => {
2230
+ const externalCart = await cartFactory.create({
2231
+ currency: "EUR",
2232
+ country: "NL",
2233
+ taxMode: "External",
2234
+ });
2235
+
2236
+ // €10 net; 20% tax = €2; gross = €12.
2237
+ const response = await ctMock.app.inject({
2238
+ method: "POST",
2239
+ url: `/dummy/carts/${externalCart.id}`,
2240
+ payload: {
2241
+ version: 1,
2242
+ actions: [
2243
+ {
2244
+ action: "addCustomLineItem",
2245
+ name: { en: "Service" },
2246
+ slug: "service",
2247
+ money: { currencyCode: "EUR", centAmount: 1000 },
2248
+ quantity: 1,
2249
+ externalTaxRate: {
2250
+ name: "Ext",
2251
+ amount: 0.2,
2252
+ country: "NL",
2253
+ includedInPrice: false,
2254
+ },
2255
+ },
2256
+ ],
2257
+ },
2258
+ });
2259
+
2260
+ expect(response.statusCode).toBe(200);
2261
+ const customLineItem = response.json().customLineItems[0];
2262
+ expect(customLineItem.taxRate.amount).toBe(0.2);
2263
+ expect(customLineItem.taxedPrice.totalNet.centAmount).toBe(1000);
2264
+ expect(customLineItem.taxedPrice.totalGross.centAmount).toBe(1200);
2265
+ });
2266
+
2267
+ test("setLineItemTaxRate updates the line item tax", async () => {
2268
+ const product = await productFactory.create(simpleProductDraft("ext-2"));
2269
+ const externalCart = await cartFactory.create({
2270
+ currency: "EUR",
2271
+ country: "NL",
2272
+ taxMode: "External",
2273
+ });
2274
+ assert(product, "product not created");
2275
+
2276
+ const added = await ctMock.app.inject({
2277
+ method: "POST",
2278
+ url: `/dummy/carts/${externalCart.id}`,
2279
+ payload: {
2280
+ version: 1,
2281
+ actions: [{ action: "addLineItem", sku: "ext-2", quantity: 1 }],
2282
+ },
2283
+ });
2284
+ expect(added.statusCode).toBe(200);
2285
+ const lineItemId = added.json().lineItems[0].id;
2286
+
2287
+ // €10 net; 5% tax = €0.50; gross = €10.50.
2288
+ const response = await ctMock.app.inject({
2289
+ method: "POST",
2290
+ url: `/dummy/carts/${externalCart.id}`,
2291
+ payload: {
2292
+ version: 2,
2293
+ actions: [
2294
+ {
2295
+ action: "setLineItemTaxRate",
2296
+ lineItemId,
2297
+ externalTaxRate: {
2298
+ name: "Late VAT",
2299
+ amount: 0.05,
2300
+ country: "NL",
2301
+ includedInPrice: false,
2302
+ },
2303
+ },
2304
+ ],
2305
+ },
2306
+ });
2307
+
2308
+ expect(response.statusCode).toBe(200);
2309
+ const lineItem = response.json().lineItems[0];
2310
+ expect(lineItem.taxRate.amount).toBe(0.05);
2311
+ expect(lineItem.taxedPrice.totalGross.centAmount).toBe(1050);
2312
+ });
2313
+
2314
+ test("setLineItemTaxRate rejects non-External tax mode", async () => {
2315
+ const product = await productFactory.create(simpleProductDraft("ext-3"));
2316
+ assert(cart, "cart not created");
2317
+ assert(product, "product not created");
2318
+
2319
+ const added = await ctMock.app.inject({
2320
+ method: "POST",
2321
+ url: `/dummy/carts/${cart.id}`,
2322
+ payload: {
2323
+ version: 1,
2324
+ actions: [{ action: "addLineItem", sku: "ext-3", quantity: 1 }],
2325
+ },
2326
+ });
2327
+ const lineItemId = added.json().lineItems[0].id;
2328
+
2329
+ const response = await ctMock.app.inject({
2330
+ method: "POST",
2331
+ url: `/dummy/carts/${cart.id}`,
2332
+ payload: {
2333
+ version: 2,
2334
+ actions: [
2335
+ {
2336
+ action: "setLineItemTaxRate",
2337
+ lineItemId,
2338
+ externalTaxRate: {
2339
+ name: "Bad",
2340
+ amount: 0.1,
2341
+ country: "NL",
2342
+ },
2343
+ },
2344
+ ],
2345
+ },
2346
+ });
2347
+
2348
+ expect(response.statusCode).toBe(400);
2349
+ });
2350
+
2351
+ test("setCustomLineItemTaxRate updates the custom line item tax", async () => {
2352
+ const externalCart = await cartFactory.create({
2353
+ currency: "EUR",
2354
+ country: "NL",
2355
+ taxMode: "External",
2356
+ });
2357
+
2358
+ const added = await ctMock.app.inject({
2359
+ method: "POST",
2360
+ url: `/dummy/carts/${externalCart.id}`,
2361
+ payload: {
2362
+ version: 1,
2363
+ actions: [
2364
+ {
2365
+ action: "addCustomLineItem",
2366
+ name: { en: "Svc" },
2367
+ slug: "svc",
2368
+ money: { currencyCode: "EUR", centAmount: 1000 },
2369
+ quantity: 1,
2370
+ },
2371
+ ],
2372
+ },
2373
+ });
2374
+ const cliId = added.json().customLineItems[0].id;
2375
+
2376
+ const response = await ctMock.app.inject({
2377
+ method: "POST",
2378
+ url: `/dummy/carts/${externalCart.id}`,
2379
+ payload: {
2380
+ version: 2,
2381
+ actions: [
2382
+ {
2383
+ action: "setCustomLineItemTaxRate",
2384
+ customLineItemId: cliId,
2385
+ externalTaxRate: {
2386
+ name: "Ext",
2387
+ amount: 0.25,
2388
+ country: "NL",
2389
+ includedInPrice: false,
2390
+ },
2391
+ },
2392
+ ],
2393
+ },
2394
+ });
2395
+
2396
+ expect(response.statusCode).toBe(200);
2397
+ const cli = response.json().customLineItems[0];
2398
+ expect(cli.taxRate.amount).toBe(0.25);
2399
+ expect(cli.taxedPrice.totalGross.centAmount).toBe(1250);
2400
+ });
2401
+
2402
+ test("setShippingMethodTaxRate updates the shipping tax", async () => {
2403
+ const zone = await zoneFactory.create({
2404
+ name: "NL",
2405
+ locations: [{ country: "NL" }],
2406
+ });
2407
+ const shippingMethod = await shippingMethodFactory.create({
2408
+ name: "Std",
2409
+ taxCategory: { typeId: "tax-category", id: taxCategory.id },
2410
+ zoneRates: [
2411
+ {
2412
+ zone: { typeId: "zone", id: zone.id },
2413
+ shippingRates: [
2414
+ {
2415
+ price: { currencyCode: "EUR", centAmount: 500 },
2416
+ },
2417
+ ],
2418
+ },
2419
+ ],
2420
+ isDefault: false,
2421
+ });
2422
+
2423
+ const externalCart = await cartFactory.create({
2424
+ currency: "EUR",
2425
+ country: "NL",
2426
+ taxMode: "External",
2427
+ shippingAddress: { country: "NL" },
2428
+ });
2429
+
2430
+ await ctMock.app.inject({
2431
+ method: "POST",
2432
+ url: `/dummy/carts/${externalCart.id}`,
2433
+ payload: {
2434
+ version: 1,
2435
+ actions: [
2436
+ {
2437
+ action: "setShippingMethod",
2438
+ shippingMethod: {
2439
+ typeId: "shipping-method",
2440
+ id: shippingMethod.id,
2441
+ },
2442
+ externalTaxRate: {
2443
+ name: "Initial",
2444
+ amount: 0.1,
2445
+ country: "NL",
2446
+ includedInPrice: false,
2447
+ },
2448
+ },
2449
+ ],
2450
+ },
2451
+ });
2452
+
2453
+ const response = await ctMock.app.inject({
2454
+ method: "POST",
2455
+ url: `/dummy/carts/${externalCart.id}`,
2456
+ payload: {
2457
+ version: 2,
2458
+ actions: [
2459
+ {
2460
+ action: "setShippingMethodTaxRate",
2461
+ externalTaxRate: {
2462
+ name: "Updated",
2463
+ amount: 0.2,
2464
+ country: "NL",
2465
+ includedInPrice: false,
2466
+ },
2467
+ },
2468
+ ],
2469
+ },
2470
+ });
2471
+
2472
+ expect(response.statusCode).toBe(200);
2473
+ const shippingInfo = response.json().shippingInfo;
2474
+ expect(shippingInfo.taxRate.amount).toBe(0.2);
2475
+ expect(shippingInfo.taxedPrice.totalGross.centAmount).toBe(600);
2476
+ });
2477
+
2478
+ test("setCustomShippingMethod accepts externalTaxRate", async () => {
2479
+ const externalCart = await cartFactory.create({
2480
+ currency: "EUR",
2481
+ country: "NL",
2482
+ taxMode: "External",
2483
+ shippingAddress: { country: "NL" },
2484
+ });
2485
+
2486
+ const response = await ctMock.app.inject({
2487
+ method: "POST",
2488
+ url: `/dummy/carts/${externalCart.id}`,
2489
+ payload: {
2490
+ version: 1,
2491
+ actions: [
2492
+ {
2493
+ action: "setCustomShippingMethod",
2494
+ shippingMethodName: "Custom",
2495
+ shippingRate: {
2496
+ price: { currencyCode: "EUR", centAmount: 1000 },
2497
+ },
2498
+ externalTaxRate: {
2499
+ name: "Custom VAT",
2500
+ amount: 0.15,
2501
+ country: "NL",
2502
+ includedInPrice: false,
2503
+ },
2504
+ },
2505
+ ],
2506
+ },
2507
+ });
2508
+
2509
+ expect(response.statusCode).toBe(200);
2510
+ const shippingInfo = response.json().shippingInfo;
2511
+ expect(shippingInfo.taxRate.amount).toBe(0.15);
2512
+ expect(shippingInfo.taxedPrice.totalGross.centAmount).toBe(1150);
2513
+ });
2514
+ });
2515
+
2516
+ describe("ExternalAmount tax mode", () => {
2517
+ const simpleProductDraft = (sku: string) => ({
2518
+ ...productDraft,
2519
+ masterVariant: {
2520
+ sku,
2521
+ prices: [
2522
+ {
2523
+ value: {
2524
+ type: "centPrecision" as const,
2525
+ currencyCode: "EUR",
2526
+ centAmount: 1000,
2527
+ fractionDigits: 2,
2528
+ } as CentPrecisionMoney,
2529
+ },
2530
+ ],
2531
+ attributes: [],
2532
+ },
2533
+ variants: [],
2534
+ slug: { "nl-NL": `simple-${sku}` },
2535
+ });
2536
+
2537
+ test("setLineItemTaxAmount sets explicit gross and derives net", async () => {
2538
+ const product = await productFactory.create(simpleProductDraft("amt-1"));
2539
+ const externalCart = await cartFactory.create({
2540
+ currency: "EUR",
2541
+ country: "NL",
2542
+ taxMode: "ExternalAmount",
2543
+ });
2544
+ assert(product, "product not created");
2545
+
2546
+ const added = await ctMock.app.inject({
2547
+ method: "POST",
2548
+ url: `/dummy/carts/${externalCart.id}`,
2549
+ payload: {
2550
+ version: 1,
2551
+ actions: [{ action: "addLineItem", sku: "amt-1", quantity: 1 }],
2552
+ },
2553
+ });
2554
+ const lineItemId = added.json().lineItems[0].id;
2555
+
2556
+ const response = await ctMock.app.inject({
2557
+ method: "POST",
2558
+ url: `/dummy/carts/${externalCart.id}`,
2559
+ payload: {
2560
+ version: 2,
2561
+ actions: [
2562
+ {
2563
+ action: "setLineItemTaxAmount",
2564
+ lineItemId,
2565
+ externalTaxAmount: {
2566
+ totalGross: { currencyCode: "EUR", centAmount: 1210 },
2567
+ taxRate: {
2568
+ name: "Ext VAT",
2569
+ amount: 0.21,
2570
+ country: "NL",
2571
+ includedInPrice: true,
2572
+ },
2573
+ },
2574
+ },
2575
+ ],
2576
+ },
2577
+ });
2578
+
2579
+ expect(response.statusCode).toBe(200);
2580
+ const lineItem = response.json().lineItems[0];
2581
+ expect(lineItem.taxRate.amount).toBe(0.21);
2582
+ expect(lineItem.taxedPrice.totalGross.centAmount).toBe(1210);
2583
+ expect(lineItem.taxedPrice.totalNet.centAmount).toBe(1000);
2584
+ expect(lineItem.taxedPrice.totalTax.centAmount).toBe(210);
2585
+ });
2586
+
2587
+ test("setLineItemTaxAmount rejects non-ExternalAmount tax mode", async () => {
2588
+ const product = await productFactory.create(simpleProductDraft("amt-2"));
2589
+ assert(cart, "cart not created");
2590
+ assert(product, "product not created");
2591
+
2592
+ const added = await ctMock.app.inject({
2593
+ method: "POST",
2594
+ url: `/dummy/carts/${cart.id}`,
2595
+ payload: {
2596
+ version: 1,
2597
+ actions: [{ action: "addLineItem", sku: "amt-2", quantity: 1 }],
2598
+ },
2599
+ });
2600
+ const lineItemId = added.json().lineItems[0].id;
2601
+
2602
+ const response = await ctMock.app.inject({
2603
+ method: "POST",
2604
+ url: `/dummy/carts/${cart.id}`,
2605
+ payload: {
2606
+ version: 2,
2607
+ actions: [
2608
+ {
2609
+ action: "setLineItemTaxAmount",
2610
+ lineItemId,
2611
+ externalTaxAmount: {
2612
+ totalGross: { currencyCode: "EUR", centAmount: 100 },
2613
+ taxRate: {
2614
+ name: "X",
2615
+ amount: 0.21,
2616
+ country: "NL",
2617
+ },
2618
+ },
2619
+ },
2620
+ ],
2621
+ },
2622
+ });
2623
+
2624
+ expect(response.statusCode).toBe(400);
2625
+ });
2626
+
2627
+ test("setCustomLineItemTaxAmount sets explicit gross", async () => {
2628
+ const externalCart = await cartFactory.create({
2629
+ currency: "EUR",
2630
+ country: "NL",
2631
+ taxMode: "ExternalAmount",
2632
+ });
2633
+
2634
+ const added = await ctMock.app.inject({
2635
+ method: "POST",
2636
+ url: `/dummy/carts/${externalCart.id}`,
2637
+ payload: {
2638
+ version: 1,
2639
+ actions: [
2640
+ {
2641
+ action: "addCustomLineItem",
2642
+ name: { en: "Svc" },
2643
+ slug: "svc",
2644
+ money: { currencyCode: "EUR", centAmount: 1000 },
2645
+ quantity: 1,
2646
+ },
2647
+ ],
2648
+ },
2649
+ });
2650
+ const cliId = added.json().customLineItems[0].id;
2651
+
2652
+ const response = await ctMock.app.inject({
2653
+ method: "POST",
2654
+ url: `/dummy/carts/${externalCart.id}`,
2655
+ payload: {
2656
+ version: 2,
2657
+ actions: [
2658
+ {
2659
+ action: "setCustomLineItemTaxAmount",
2660
+ customLineItemId: cliId,
2661
+ externalTaxAmount: {
2662
+ totalGross: { currencyCode: "EUR", centAmount: 1250 },
2663
+ taxRate: {
2664
+ name: "Ext",
2665
+ amount: 0.25,
2666
+ country: "NL",
2667
+ },
2668
+ },
2669
+ },
2670
+ ],
2671
+ },
2672
+ });
2673
+
2674
+ expect(response.statusCode).toBe(200);
2675
+ const cli = response.json().customLineItems[0];
2676
+ expect(cli.taxedPrice.totalGross.centAmount).toBe(1250);
2677
+ expect(cli.taxedPrice.totalNet.centAmount).toBe(1000);
2678
+ });
2679
+
2680
+ test("setShippingMethodTaxAmount sets explicit shipping gross", async () => {
2681
+ const externalCart = await cartFactory.create({
2682
+ currency: "EUR",
2683
+ country: "NL",
2684
+ taxMode: "ExternalAmount",
2685
+ shippingAddress: { country: "NL" },
2686
+ });
2687
+
2688
+ await ctMock.app.inject({
2689
+ method: "POST",
2690
+ url: `/dummy/carts/${externalCart.id}`,
2691
+ payload: {
2692
+ version: 1,
2693
+ actions: [
2694
+ {
2695
+ action: "setCustomShippingMethod",
2696
+ shippingMethodName: "Custom",
2697
+ shippingRate: {
2698
+ price: { currencyCode: "EUR", centAmount: 1000 },
2699
+ },
2700
+ },
2701
+ ],
2702
+ },
2703
+ });
2704
+
2705
+ const response = await ctMock.app.inject({
2706
+ method: "POST",
2707
+ url: `/dummy/carts/${externalCart.id}`,
2708
+ payload: {
2709
+ version: 2,
2710
+ actions: [
2711
+ {
2712
+ action: "setShippingMethodTaxAmount",
2713
+ externalTaxAmount: {
2714
+ totalGross: { currencyCode: "EUR", centAmount: 1200 },
2715
+ taxRate: {
2716
+ name: "Ship Tax",
2717
+ amount: 0.2,
2718
+ country: "NL",
2719
+ },
2720
+ },
2721
+ },
2722
+ ],
2723
+ },
2724
+ });
2725
+
2726
+ expect(response.statusCode).toBe(200);
2727
+ const body = response.json();
2728
+ expect(body.shippingInfo.taxedPrice.totalGross.centAmount).toBe(1200);
2729
+ expect(body.shippingInfo.taxedPrice.totalNet.centAmount).toBe(1000);
2730
+ // taxedShippingPrice mirrors shippingInfo.taxedPrice even in
2731
+ // ExternalAmount mode (it's a subtotal, not the cart total).
2732
+ expect(body.taxedShippingPrice.totalGross.centAmount).toBe(1200);
2733
+ expect(body.taxedShippingPrice.totalNet.centAmount).toBe(1000);
2734
+ });
2735
+
2736
+ test("setCartTotalTax overrides aggregated taxedPrice", async () => {
2737
+ const product = await productFactory.create(simpleProductDraft("amt-3"));
2738
+ const externalCart = await cartFactory.create({
2739
+ currency: "EUR",
2740
+ country: "NL",
2741
+ taxMode: "ExternalAmount",
2742
+ });
2743
+ assert(product, "product not created");
2744
+
2745
+ const response = await ctMock.app.inject({
2746
+ method: "POST",
2747
+ url: `/dummy/carts/${externalCart.id}`,
2748
+ payload: {
2749
+ version: 1,
2750
+ actions: [
2751
+ { action: "addLineItem", sku: "amt-3", quantity: 1 },
2752
+ {
2753
+ action: "setCartTotalTax",
2754
+ externalTotalGross: { currencyCode: "EUR", centAmount: 1210 },
2755
+ externalTaxPortions: [
2756
+ {
2757
+ rate: 0.21,
2758
+ name: "NL",
2759
+ amount: { currencyCode: "EUR", centAmount: 210 },
2760
+ },
2761
+ ],
2762
+ },
2763
+ ],
2764
+ },
2765
+ });
2766
+
2767
+ expect(response.statusCode).toBe(200);
2768
+ const body = response.json();
2769
+ expect(body.taxedPrice.totalGross.centAmount).toBe(1210);
2770
+ expect(body.taxedPrice.totalNet.centAmount).toBe(1000);
2771
+ expect(body.taxedPrice.totalTax.centAmount).toBe(210);
2772
+ expect(body.taxedPrice.taxPortions).toHaveLength(1);
2773
+ expect(body.taxedPrice.taxPortions[0].rate).toBe(0.21);
2774
+ });
2775
+
2776
+ test("setCartTotalTax rejects non-ExternalAmount tax mode", async () => {
2777
+ assert(cart, "cart not created");
2778
+
2779
+ const response = await ctMock.app.inject({
2780
+ method: "POST",
2781
+ url: `/dummy/carts/${cart.id}`,
2782
+ payload: {
2783
+ version: 1,
2784
+ actions: [
2785
+ {
2786
+ action: "setCartTotalTax",
2787
+ externalTotalGross: { currencyCode: "EUR", centAmount: 100 },
2788
+ },
2789
+ ],
2790
+ },
2791
+ });
2792
+
2793
+ expect(response.statusCode).toBe(400);
2794
+ });
2795
+ });
2163
2796
  });
package/src/shipping.ts CHANGED
@@ -2,6 +2,7 @@ import type {
2
2
  Cart,
3
3
  CartValueTier,
4
4
  CentPrecisionMoney,
5
+ ExternalTaxRateDraft,
5
6
  InvalidOperationError,
6
7
  MissingTaxRateForCountryError,
7
8
  ShippingInfo,
@@ -10,9 +11,11 @@ import type {
10
11
  ShippingRatePriceTier,
11
12
  TaxedItemPrice,
12
13
  TaxPortion,
14
+ TaxRate,
13
15
  } from "@commercetools/platform-sdk";
14
16
  import { Decimal } from "decimal.js";
15
17
  import { CommercetoolsError } from "./exceptions.ts";
18
+ import { taxRateFromExternalDraft } from "./lib/tax.ts";
16
19
  import type { GetParams, RepositoryContext } from "./repositories/abstract.ts";
17
20
  import {
18
21
  createCentPrecisionMoney,
@@ -190,6 +193,7 @@ export const createShippingInfoFromMethod = async (
190
193
  storage: AbstractStorage,
191
194
  resource: ShippingCalculationSource,
192
195
  method: ShippingMethod,
196
+ externalTaxRate?: ExternalTaxRateDraft,
193
197
  ): Promise<Omit<ShippingInfo, "deliveries">> => {
194
198
  const country = resource.shippingAddress!.country;
195
199
 
@@ -228,20 +232,25 @@ export const createShippingInfoFromMethod = async (
228
232
  );
229
233
  }
230
234
 
231
- const taxCategory = await storage.getByResourceIdentifier<"tax-category">(
232
- context.projectKey,
233
- method.taxCategory,
234
- );
235
+ let taxRate: TaxRate | undefined;
236
+ if (externalTaxRate) {
237
+ taxRate = taxRateFromExternalDraft(externalTaxRate);
238
+ } else {
239
+ const taxCategory = await storage.getByResourceIdentifier<"tax-category">(
240
+ context.projectKey,
241
+ method.taxCategory,
242
+ );
235
243
 
236
- // TODO: match state in addition to country
237
- const taxRate = taxCategory.rates.find((rate) => rate.country === country);
244
+ // TODO: match state in addition to country
245
+ taxRate = taxCategory.rates.find((rate) => rate.country === country);
238
246
 
239
- if (!taxRate) {
240
- throw new CommercetoolsError<MissingTaxRateForCountryError>({
241
- code: "MissingTaxRateForCountry",
242
- message: `Tax category '${taxCategory.id}' is missing a tax rate for country '${country}'.`,
243
- taxCategoryId: taxCategory.id,
244
- });
247
+ if (!taxRate) {
248
+ throw new CommercetoolsError<MissingTaxRateForCountryError>({
249
+ code: "MissingTaxRateForCountry",
250
+ message: `Tax category '${taxCategory.id}' is missing a tax rate for country '${country}'.`,
251
+ taxCategoryId: taxCategory.id,
252
+ });
253
+ }
245
254
  }
246
255
 
247
256
  const shippingRateTier = shippingRate.tiers.find((tier) => tier.isMatching);