1
0
Эх сурвалжийг харах

feat(core): Include address-based tax zone strategy (#3198)

Martijn 1 жил өмнө
parent
commit
5547128e41

+ 17 - 1
license/signatures/version1/cla.json

@@ -279,6 +279,22 @@
       "created_at": "2024-10-31T08:42:52Z",
       "repoId": 136938012,
       "pullRequestNo": 3174
+    },
+    {
+      "name": "kkerti",
+      "id": 47832952,
+      "comment_id": 2458191015,
+      "created_at": "2024-11-05T21:33:05Z",
+      "repoId": 136938012,
+      "pullRequestNo": 3187
+    },
+    {
+      "name": "shingoaoyama1",
+      "id": 17615101,
+      "comment_id": 2459213307,
+      "created_at": "2024-11-06T10:15:37Z",
+      "repoId": 136938012,
+      "pullRequestNo": 3192
     }
   ]
-}
+}

+ 1 - 0
packages/core/src/config/index.ts

@@ -87,6 +87,7 @@ export * from './system/health-check-strategy';
 export * from './system/error-handler-strategy';
 export * from './tax/default-tax-line-calculation-strategy';
 export * from './tax/default-tax-zone-strategy';
+export * from './tax/address-based-tax-zone-strategy';
 export * from './tax/tax-line-calculation-strategy';
 export * from './tax/tax-zone-strategy';
 export * from './vendure-config';

+ 52 - 0
packages/core/src/config/tax/address-based-tax-zone-strategy.spec.ts

@@ -0,0 +1,52 @@
+import { RequestContext, Zone, Channel, Order } from '@vendure/core';
+import { describe, it, expect } from 'vitest';
+
+import { Logger } from '../logger/vendure-logger';
+
+import { AddressBasedTaxZoneStrategy } from './address-based-tax-zone-strategy';
+
+describe('AddressBasedTaxZoneStrategy', () => {
+    const strategy = new AddressBasedTaxZoneStrategy();
+
+    const ctx = {} as RequestContext;
+    const defaultZone = { name: 'Default Zone' } as Zone;
+    const channel = { defaultTaxZone: defaultZone } as Channel;
+
+    const zones: Zone[] = [
+        defaultZone,
+        { name: 'US Zone', members: [{ code: 'US' }] } as Zone,
+        { name: 'DE Zone', members: [{ code: 'DE' }] } as Zone,
+    ];
+
+    it('Determines zone based on billing address country', () => {
+        const order = {
+            billingAddress: { countryCode: 'US' },
+            shippingAddress: { countryCode: 'DE' },
+        } as Order;
+        const result = strategy.determineTaxZone(ctx, zones, channel, order);
+        expect(result.name).toBe('US Zone');
+    });
+
+    it('Determines zone based on shipping address if no billing address set', () => {
+        const order = { shippingAddress: { countryCode: 'DE' } } as Order;
+        const result = strategy.determineTaxZone(ctx, zones, channel, order);
+        expect(result.name).toBe('DE Zone');
+    });
+
+    it('Returns the default zone if no matching zone is found', () => {
+        const order = { billingAddress: { countryCode: 'FR' } } as Order;
+        const result = strategy.determineTaxZone(ctx, zones, channel, order);
+        expect(result.name).toBe('Default Zone');
+    });
+
+    it('Returns the default zone if no order is provided', () => {
+        const result = strategy.determineTaxZone(ctx, zones, channel);
+        expect(result.name).toBe('Default Zone');
+    });
+
+    it('Returns the default zone if no address is set on order', () => {
+        const order = {} as Order;
+        const result = strategy.determineTaxZone(ctx, zones, channel, order);
+        expect(result.name).toBe('Default Zone');
+    });
+});

+ 42 - 0
packages/core/src/config/tax/address-based-tax-zone-strategy.ts

@@ -0,0 +1,42 @@
+import { RequestContext } from '../../api/common/request-context';
+import { Channel, Order, Zone } from '../../entity';
+import { Logger } from '../logger/vendure-logger';
+
+import { TaxZoneStrategy } from './tax-zone-strategy';
+
+const loggerCtx = 'AddressBasedTaxZoneStrategy';
+
+/**
+ * @description
+ * Address based {@link TaxZoneStrategy} which tries to find the applicable {@link Zone} based on the
+ * country of the billing address, or else the country of the shipping address of the Order.
+ *
+ * Returns the default {@link Channel}'s default tax zone if no applicable zone is found.
+ *
+ * @since 3.1.0
+ *
+ * :::info
+ *
+ * This is configured via `taxOptions.taxZoneStrategy = new AddressBasedTaxZoneStrategy()` in
+ * your VendureConfig.
+ *
+ * :::
+ *
+ * @docsCategory tax
+ */
+export class AddressBasedTaxZoneStrategy implements TaxZoneStrategy {
+    determineTaxZone(ctx: RequestContext, zones: Zone[], channel: Channel, order?: Order): Zone {
+        const countryCode = order?.billingAddress?.countryCode ?? order?.shippingAddress?.countryCode;
+        if (order && countryCode) {
+            const zone = zones.find(z => z.members?.find(member => member.code === countryCode));
+            if (zone) {
+                return zone;
+            }
+            Logger.debug(
+                `No tax zone found for country ${countryCode}. Returning default ${channel.defaultTaxZone.name} for order ${order.code}`,
+                loggerCtx,
+            );
+        }
+        return channel.defaultTaxZone;
+    }
+}