feat: Avatax metadata tax calculation date (#843)

* feat:  add metadata tax calculation date

* build: 👷 add changeset
This commit is contained in:
Adrian Pilarczyk 2023-08-03 11:48:43 +02:00 committed by GitHub
parent 2b073e7113
commit 6106e1d5fa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 78 additions and 2 deletions

View file

@ -0,0 +1,5 @@
---
"saleor-app-taxes": minor
---
Added support for reading the tax calculation date from metadata field `avataxTaxCalculationDate`. The value has to be valid UTC datetime string (e.g. "2021-08-31T13:00:00.000Z").

View file

@ -65,6 +65,7 @@ fragment OrderConfirmedSubscription on Order {
}
}
avataxEntityCode: metafield(key: "avataxEntityCode")
avataxTaxCalculationDate: metafield(key: "avataxTaxCalculationDate")
}
fragment OrderConfirmedEventSubscription on Event {
__typename

View file

@ -0,0 +1,31 @@
import { describe, expect, it } from "vitest";
import { OrderConfirmedSubscriptionFragment } from "../../../../generated/graphql";
import { AvataxOrderConfirmedCalculationDateResolver } from "./avatax-order-confirmed-calculation-date-resolver";
const resolver = new AvataxOrderConfirmedCalculationDateResolver();
describe("AvataxOrderConfirmedCalculationDateResolver", () => {
it("should return the metadata tax calculation date if it is set", () => {
const order = {
avataxTaxCalculationDate: "2021-01-01T00:00:00.000Z",
created: "2021-01-02T00:00:00.000Z",
} as any as OrderConfirmedSubscriptionFragment;
expect(resolver.resolve(order)).toEqual(new Date("2021-01-01T00:00:00.000Z"));
});
it("should fallback to order created when metadata tax calculation date is not a string datetime", () => {
const order = {
avataxTaxCalculationDate: "not-a-datetime",
created: "2021-01-02T00:00:00.000Z",
} as any as OrderConfirmedSubscriptionFragment;
expect(resolver.resolve(order)).toEqual(new Date("2021-01-02T00:00:00.000Z"));
});
it("should return the order creation date if the metadata tax calculation date is not set", () => {
const order = {
created: "2021-01-02T00:00:00.000Z",
} as any as OrderConfirmedSubscriptionFragment;
expect(resolver.resolve(order)).toEqual(new Date("2021-01-02T00:00:00.000Z"));
});
});

View file

@ -0,0 +1,33 @@
import { z } from "zod";
import { OrderConfirmedSubscriptionFragment } from "../../../../generated/graphql";
import { createLogger } from "../../../lib/logger";
export class AvataxOrderConfirmedCalculationDateResolver {
private logger = createLogger({
name: "AvataxOrderConfirmedCalculationDateResolver",
});
resolve(order: OrderConfirmedSubscriptionFragment): Date {
if (!order.avataxTaxCalculationDate) {
this.logger.info("No tax calculation date provided. Falling back to order created date.");
return new Date(order.created);
}
// UTC datetime string, e.g. "2021-08-31T13:00:00.000Z"
const taxCalculationParse = z.string().datetime().safeParse(order.avataxTaxCalculationDate);
if (taxCalculationParse.success) {
// The user is able to pass other tax calculation date than the order creation date.
this.logger.info(
"Valid UTC tax calculation date found in metadata. Using it for tax calculation."
);
return new Date(taxCalculationParse.data);
} else {
this.logger.warn(
`The tax calculation date ${order.avataxTaxCalculationDate} is not a valid UTC datetime. Falling back to order created date.`
);
return new Date(order.created);
}
}
}

View file

@ -7,6 +7,7 @@ import { AvataxConfig } from "../avatax-connection-schema";
import { AvataxTaxCodeMatches } from "../tax-code/avatax-tax-code-match-repository";
import { AvataxOrderConfirmedPayloadLinesTransformer } from "./avatax-order-confirmed-payload-lines-transformer";
import { AvataxEntityTypeMatcher } from "../avatax-entity-type-matcher";
import { AvataxOrderConfirmedCalculationDateResolver } from "./avatax-order-confirmed-calculation-date-resolver";
export const SHIPPING_ITEM_CODE = "Shipping";
@ -19,15 +20,20 @@ export class AvataxOrderConfirmedPayloadTransformer {
return DocumentType.SalesInvoice;
}
async transform(
order: OrderConfirmedSubscriptionFragment,
avataxConfig: AvataxConfig,
matches: AvataxTaxCodeMatches
): Promise<CreateTransactionArgs> {
const linesTransformer = new AvataxOrderConfirmedPayloadLinesTransformer();
const avataxClient = new AvataxClient(avataxConfig);
const linesTransformer = new AvataxOrderConfirmedPayloadLinesTransformer();
const dateResolver = new AvataxOrderConfirmedCalculationDateResolver();
const entityTypeMatcher = new AvataxEntityTypeMatcher({ client: avataxClient });
const entityUseCode = await entityTypeMatcher.match(order.avataxEntityCode);
const date = dateResolver.resolve(order);
return {
model: {
@ -47,7 +53,7 @@ export class AvataxOrderConfirmedPayloadTransformer {
currencyCode: order.total.currency,
email: order.user?.email ?? "",
lines: linesTransformer.transform(order, avataxConfig, matches),
date: new Date(order.created),
date,
discount: discountUtils.sumDiscounts(
order.discounts.map((discount) => discount.amount.amount)
),