fix checkout calculate taxes (#919)
* fix: 🐛 value of customerCode in calculateTaxes * build: 👷 add changeset * fix: 🐛 tests
This commit is contained in:
parent
4a635620c4
commit
45ed9fb444
17 changed files with 114 additions and 63 deletions
5
.changeset/unlucky-tips-smash.md
Normal file
5
.changeset/unlucky-tips-smash.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
"saleor-app-taxes": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fixed the error when checkout couldn't calculate taxes when no customerCode was provided. In calculate taxes, the customerCode is now derived from issuingPrincipal's id.
|
|
@ -10,5 +10,11 @@ fragment CalculateTaxesEvent on Event {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
issuingPrincipal {
|
||||||
|
__typename
|
||||||
|
... on User {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,15 +62,9 @@ fragment TaxBase on TaxableObject {
|
||||||
sourceObject {
|
sourceObject {
|
||||||
... on Checkout {
|
... on Checkout {
|
||||||
avataxEntityCode: metafield(key: "avataxEntityCode")
|
avataxEntityCode: metafield(key: "avataxEntityCode")
|
||||||
user {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
... on Order {
|
... on Order {
|
||||||
avataxEntityCode: metafield(key: "avataxEntityCode")
|
avataxEntityCode: metafield(key: "avataxEntityCode")
|
||||||
user {
|
|
||||||
id
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import { AvataxConfig, defaultAvataxConfig } from "./avatax-connection-schema";
|
||||||
import { AvataxCalculateTaxesAdapter } from "./calculate-taxes/avatax-calculate-taxes-adapter";
|
import { AvataxCalculateTaxesAdapter } from "./calculate-taxes/avatax-calculate-taxes-adapter";
|
||||||
import { AvataxOrderCancelledAdapter } from "./order-cancelled/avatax-order-cancelled-adapter";
|
import { AvataxOrderCancelledAdapter } from "./order-cancelled/avatax-order-cancelled-adapter";
|
||||||
import { AvataxOrderConfirmedAdapter } from "./order-confirmed/avatax-order-confirmed-adapter";
|
import { AvataxOrderConfirmedAdapter } from "./order-confirmed/avatax-order-confirmed-adapter";
|
||||||
|
import { CalculateTaxesPayload } from "../../pages/api/webhooks/checkout-calculate-taxes";
|
||||||
|
|
||||||
export class AvataxWebhookService implements ProviderWebhookService {
|
export class AvataxWebhookService implements ProviderWebhookService {
|
||||||
config = defaultAvataxConfig;
|
config = defaultAvataxConfig;
|
||||||
|
@ -27,10 +28,10 @@ export class AvataxWebhookService implements ProviderWebhookService {
|
||||||
this.client = avataxClient;
|
this.client = avataxClient;
|
||||||
}
|
}
|
||||||
|
|
||||||
async calculateTaxes(taxBase: TaxBaseFragment) {
|
async calculateTaxes(payload: CalculateTaxesPayload) {
|
||||||
const adapter = new AvataxCalculateTaxesAdapter(this.config, this.authData);
|
const adapter = new AvataxCalculateTaxesAdapter(this.config, this.authData);
|
||||||
|
|
||||||
const response = await adapter.send({ taxBase });
|
const response = await adapter.send(payload);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { AuthData } from "@saleor/app-sdk/APL";
|
import { AuthData } from "@saleor/app-sdk/APL";
|
||||||
import { TaxBaseFragment } from "../../../../generated/graphql";
|
|
||||||
import { Logger, createLogger } from "../../../lib/logger";
|
import { Logger, createLogger } from "../../../lib/logger";
|
||||||
|
import { CalculateTaxesPayload } from "../../../pages/api/webhooks/checkout-calculate-taxes";
|
||||||
import { CalculateTaxesResponse } from "../../taxes/tax-provider-webhook";
|
import { CalculateTaxesResponse } from "../../taxes/tax-provider-webhook";
|
||||||
import { WebhookAdapter } from "../../taxes/tax-webhook-adapter";
|
import { WebhookAdapter } from "../../taxes/tax-webhook-adapter";
|
||||||
import { AvataxClient, CreateTransactionArgs } from "../avatax-client";
|
import { AvataxClient, CreateTransactionArgs } from "../avatax-client";
|
||||||
|
@ -10,26 +10,27 @@ import { AvataxCalculateTaxesResponseTransformer } from "./avatax-calculate-taxe
|
||||||
|
|
||||||
export const SHIPPING_ITEM_CODE = "Shipping";
|
export const SHIPPING_ITEM_CODE = "Shipping";
|
||||||
|
|
||||||
export type AvataxCalculateTaxesPayload = {
|
|
||||||
taxBase: TaxBaseFragment;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type AvataxCalculateTaxesTarget = CreateTransactionArgs;
|
export type AvataxCalculateTaxesTarget = CreateTransactionArgs;
|
||||||
export type AvataxCalculateTaxesResponse = CalculateTaxesResponse;
|
export type AvataxCalculateTaxesResponse = CalculateTaxesResponse;
|
||||||
|
|
||||||
export class AvataxCalculateTaxesAdapter
|
export class AvataxCalculateTaxesAdapter
|
||||||
implements WebhookAdapter<AvataxCalculateTaxesPayload, AvataxCalculateTaxesResponse>
|
implements WebhookAdapter<CalculateTaxesPayload, AvataxCalculateTaxesResponse>
|
||||||
{
|
{
|
||||||
private logger: Logger;
|
private logger: Logger;
|
||||||
constructor(private readonly config: AvataxConfig, private authData: AuthData) {
|
constructor(
|
||||||
|
private readonly config: AvataxConfig,
|
||||||
|
private authData: AuthData,
|
||||||
|
) {
|
||||||
this.logger = createLogger({ name: "AvataxCalculateTaxesAdapter" });
|
this.logger = createLogger({ name: "AvataxCalculateTaxesAdapter" });
|
||||||
}
|
}
|
||||||
|
|
||||||
// todo: refactor because its getting too big
|
async send(payload: CalculateTaxesPayload): Promise<AvataxCalculateTaxesResponse> {
|
||||||
async send(payload: AvataxCalculateTaxesPayload): Promise<AvataxCalculateTaxesResponse> {
|
this.logger.debug(
|
||||||
this.logger.debug("Transforming the Saleor payload for calculating taxes with AvaTax...");
|
{ payload },
|
||||||
|
"Transforming the Saleor payload for calculating taxes with AvaTax...",
|
||||||
|
);
|
||||||
const payloadService = new AvataxCalculateTaxesPayloadService(this.authData);
|
const payloadService = new AvataxCalculateTaxesPayloadService(this.authData);
|
||||||
const target = await payloadService.getPayload(payload.taxBase, this.config);
|
const target = await payloadService.getPayload(payload, this.config);
|
||||||
|
|
||||||
this.logger.debug("Calling AvaTax createTransaction with transformed payload...");
|
this.logger.debug("Calling AvaTax createTransaction with transformed payload...");
|
||||||
|
|
||||||
|
|
|
@ -107,9 +107,6 @@ const defaultTaxBase: TaxBase = {
|
||||||
],
|
],
|
||||||
sourceObject: {
|
sourceObject: {
|
||||||
avataxEntityCode: null,
|
avataxEntityCode: null,
|
||||||
user: {
|
|
||||||
id: "VXNlcjoyMDg0NTEwNDEw",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||||
import { AvataxCalculateTaxesMockGenerator } from "./avatax-calculate-taxes-mock-generator";
|
import { AvataxCalculateTaxesMockGenerator } from "./avatax-calculate-taxes-mock-generator";
|
||||||
import { AvataxCalculateTaxesPayloadTransformer } from "./avatax-calculate-taxes-payload-transformer";
|
import { AvataxCalculateTaxesPayloadTransformer } from "./avatax-calculate-taxes-payload-transformer";
|
||||||
import { DocumentType } from "avatax/lib/enums/DocumentType";
|
import { DocumentType } from "avatax/lib/enums/DocumentType";
|
||||||
|
import { CalculateTaxesPayload } from "../../../pages/api/webhooks/checkout-calculate-taxes";
|
||||||
|
|
||||||
const mockGenerator = new AvataxCalculateTaxesMockGenerator();
|
const mockGenerator = new AvataxCalculateTaxesMockGenerator();
|
||||||
const avataxConfigMock = mockGenerator.generateAvataxConfig();
|
const avataxConfigMock = mockGenerator.generateAvataxConfig();
|
||||||
|
@ -10,11 +11,18 @@ describe("AvataxCalculateTaxesPayloadTransformer", () => {
|
||||||
it("returns document type of SalesInvoice", async () => {
|
it("returns document type of SalesInvoice", async () => {
|
||||||
const taxBaseMock = mockGenerator.generateTaxBase();
|
const taxBaseMock = mockGenerator.generateTaxBase();
|
||||||
const matchesMock = mockGenerator.generateTaxCodeMatches();
|
const matchesMock = mockGenerator.generateTaxCodeMatches();
|
||||||
|
const payloadMock = {
|
||||||
|
taxBase: taxBaseMock,
|
||||||
|
issuingPrincipal: {
|
||||||
|
__typename: "User",
|
||||||
|
id: "1",
|
||||||
|
},
|
||||||
|
} as unknown as CalculateTaxesPayload;
|
||||||
|
|
||||||
const payload = await new AvataxCalculateTaxesPayloadTransformer().transform(
|
const payload = await new AvataxCalculateTaxesPayloadTransformer().transform(
|
||||||
taxBaseMock,
|
payloadMock,
|
||||||
avataxConfigMock,
|
avataxConfigMock,
|
||||||
matchesMock
|
matchesMock,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(payload.model.type).toBe(DocumentType.SalesOrder);
|
expect(payload.model.type).toBe(DocumentType.SalesOrder);
|
||||||
|
@ -22,27 +30,56 @@ describe("AvataxCalculateTaxesPayloadTransformer", () => {
|
||||||
it("when discounts, calculates the sum of discounts", async () => {
|
it("when discounts, calculates the sum of discounts", async () => {
|
||||||
const taxBaseMock = mockGenerator.generateTaxBase({ discounts: [{ amount: { amount: 10 } }] });
|
const taxBaseMock = mockGenerator.generateTaxBase({ discounts: [{ amount: { amount: 10 } }] });
|
||||||
const matchesMock = mockGenerator.generateTaxCodeMatches();
|
const matchesMock = mockGenerator.generateTaxCodeMatches();
|
||||||
|
const payloadMock = {
|
||||||
|
taxBase: taxBaseMock,
|
||||||
|
issuingPrincipal: {
|
||||||
|
__typename: "User",
|
||||||
|
id: "1",
|
||||||
|
},
|
||||||
|
} as unknown as CalculateTaxesPayload;
|
||||||
|
|
||||||
const payload = await new AvataxCalculateTaxesPayloadTransformer().transform(
|
const payload = await new AvataxCalculateTaxesPayloadTransformer().transform(
|
||||||
taxBaseMock,
|
payloadMock,
|
||||||
avataxConfigMock,
|
avataxConfigMock,
|
||||||
matchesMock
|
matchesMock,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(payload.model.discount).toEqual(10);
|
expect(payload.model.discount).toEqual(10);
|
||||||
});
|
});
|
||||||
it("when no discounts, the sum of discount is 0", async () => {
|
it("when no discounts, the sum of discount is 0", async () => {
|
||||||
const mockGenerator = new AvataxCalculateTaxesMockGenerator();
|
|
||||||
const avataxConfigMock = mockGenerator.generateAvataxConfig();
|
|
||||||
const taxBaseMock = mockGenerator.generateTaxBase();
|
const taxBaseMock = mockGenerator.generateTaxBase();
|
||||||
const matchesMock = mockGenerator.generateTaxCodeMatches();
|
const matchesMock = mockGenerator.generateTaxCodeMatches();
|
||||||
|
|
||||||
|
const payloadMock = {
|
||||||
|
taxBase: taxBaseMock,
|
||||||
|
issuingPrincipal: {
|
||||||
|
__typename: "User",
|
||||||
|
id: "1",
|
||||||
|
},
|
||||||
|
} as unknown as CalculateTaxesPayload;
|
||||||
|
|
||||||
const payload = await new AvataxCalculateTaxesPayloadTransformer().transform(
|
const payload = await new AvataxCalculateTaxesPayloadTransformer().transform(
|
||||||
taxBaseMock,
|
payloadMock,
|
||||||
avataxConfigMock,
|
avataxConfigMock,
|
||||||
matchesMock
|
matchesMock,
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(payload.model.discount).toEqual(0);
|
expect(payload.model.discount).toEqual(0);
|
||||||
});
|
});
|
||||||
|
it("when no issuingPrincipal.id, throws an error", async () => {
|
||||||
|
const taxBaseMock = mockGenerator.generateTaxBase();
|
||||||
|
const matchesMock = mockGenerator.generateTaxCodeMatches();
|
||||||
|
|
||||||
|
const payloadMock = {
|
||||||
|
taxBase: taxBaseMock,
|
||||||
|
} as unknown as CalculateTaxesPayload;
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
new AvataxCalculateTaxesPayloadTransformer().transform(
|
||||||
|
payloadMock,
|
||||||
|
avataxConfigMock,
|
||||||
|
matchesMock,
|
||||||
|
),
|
||||||
|
).rejects.toThrow("This field must be defined.");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,6 +7,8 @@ import { AvataxConfig, defaultAvataxConfig } from "../avatax-connection-schema";
|
||||||
import { AvataxTaxCodeMatches } from "../tax-code/avatax-tax-code-match-repository";
|
import { AvataxTaxCodeMatches } from "../tax-code/avatax-tax-code-match-repository";
|
||||||
import { AvataxCalculateTaxesPayloadLinesTransformer } from "./avatax-calculate-taxes-payload-lines-transformer";
|
import { AvataxCalculateTaxesPayloadLinesTransformer } from "./avatax-calculate-taxes-payload-lines-transformer";
|
||||||
import { AvataxEntityTypeMatcher } from "../avatax-entity-type-matcher";
|
import { AvataxEntityTypeMatcher } from "../avatax-entity-type-matcher";
|
||||||
|
import { taxProviderUtils } from "../../taxes/tax-provider-utils";
|
||||||
|
import { CalculateTaxesPayload } from "../../../pages/api/webhooks/checkout-calculate-taxes";
|
||||||
|
|
||||||
export class AvataxCalculateTaxesPayloadTransformer {
|
export class AvataxCalculateTaxesPayloadTransformer {
|
||||||
private matchDocumentType(config: AvataxConfig): DocumentType {
|
private matchDocumentType(config: AvataxConfig): DocumentType {
|
||||||
|
@ -20,32 +22,38 @@ export class AvataxCalculateTaxesPayloadTransformer {
|
||||||
}
|
}
|
||||||
|
|
||||||
async transform(
|
async transform(
|
||||||
taxBase: TaxBaseFragment,
|
payload: CalculateTaxesPayload,
|
||||||
avataxConfig: AvataxConfig,
|
avataxConfig: AvataxConfig,
|
||||||
matches: AvataxTaxCodeMatches
|
matches: AvataxTaxCodeMatches,
|
||||||
): Promise<CreateTransactionArgs> {
|
): Promise<CreateTransactionArgs> {
|
||||||
const payloadLinesTransformer = new AvataxCalculateTaxesPayloadLinesTransformer();
|
const payloadLinesTransformer = new AvataxCalculateTaxesPayloadLinesTransformer();
|
||||||
const avataxClient = new AvataxClient(avataxConfig);
|
const avataxClient = new AvataxClient(avataxConfig);
|
||||||
const entityTypeMatcher = new AvataxEntityTypeMatcher({ client: avataxClient });
|
const entityTypeMatcher = new AvataxEntityTypeMatcher({ client: avataxClient });
|
||||||
const entityUseCode = await entityTypeMatcher.match(taxBase.sourceObject.avataxEntityCode);
|
const entityUseCode = await entityTypeMatcher.match(
|
||||||
|
payload.taxBase.sourceObject.avataxEntityCode,
|
||||||
|
);
|
||||||
|
|
||||||
|
const customerCode = taxProviderUtils.resolveStringOrThrow(
|
||||||
|
payload.issuingPrincipal?.__typename === "User" ? payload.issuingPrincipal.id : undefined,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
model: {
|
model: {
|
||||||
type: this.matchDocumentType(avataxConfig),
|
type: this.matchDocumentType(avataxConfig),
|
||||||
entityUseCode,
|
entityUseCode,
|
||||||
customerCode: taxBase.sourceObject.user?.id ?? "",
|
customerCode,
|
||||||
companyCode: avataxConfig.companyCode ?? defaultAvataxConfig.companyCode,
|
companyCode: avataxConfig.companyCode ?? defaultAvataxConfig.companyCode,
|
||||||
// * commit: If true, the transaction will be committed immediately after it is created. See: https://developer.avalara.com/communications/dev-guide_rest_v2/commit-uncommit
|
// * commit: If true, the transaction will be committed immediately after it is created. See: https://developer.avalara.com/communications/dev-guide_rest_v2/commit-uncommit
|
||||||
commit: avataxConfig.isAutocommit,
|
commit: avataxConfig.isAutocommit,
|
||||||
addresses: {
|
addresses: {
|
||||||
shipFrom: avataxAddressFactory.fromChannelAddress(avataxConfig.address),
|
shipFrom: avataxAddressFactory.fromChannelAddress(avataxConfig.address),
|
||||||
shipTo: avataxAddressFactory.fromSaleorAddress(taxBase.address!),
|
shipTo: avataxAddressFactory.fromSaleorAddress(payload.taxBase.address!),
|
||||||
},
|
},
|
||||||
currencyCode: taxBase.currency,
|
currencyCode: payload.taxBase.currency,
|
||||||
lines: payloadLinesTransformer.transform(taxBase, avataxConfig, matches),
|
lines: payloadLinesTransformer.transform(payload.taxBase, avataxConfig, matches),
|
||||||
date: new Date(),
|
date: new Date(),
|
||||||
discount: discountUtils.sumDiscounts(
|
discount: discountUtils.sumDiscounts(
|
||||||
taxBase.discounts.map((discount) => discount.amount.amount)
|
payload.taxBase.discounts.map((discount) => discount.amount.amount),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { CreateTransactionArgs } from "../avatax-client";
|
||||||
import { AvataxConfig } from "../avatax-connection-schema";
|
import { AvataxConfig } from "../avatax-connection-schema";
|
||||||
import { AvataxTaxCodeMatchesService } from "../tax-code/avatax-tax-code-matches.service";
|
import { AvataxTaxCodeMatchesService } from "../tax-code/avatax-tax-code-matches.service";
|
||||||
import { AvataxCalculateTaxesPayloadTransformer } from "./avatax-calculate-taxes-payload-transformer";
|
import { AvataxCalculateTaxesPayloadTransformer } from "./avatax-calculate-taxes-payload-transformer";
|
||||||
|
import { CalculateTaxesPayload } from "../../../pages/api/webhooks/checkout-calculate-taxes";
|
||||||
|
|
||||||
export class AvataxCalculateTaxesPayloadService {
|
export class AvataxCalculateTaxesPayloadService {
|
||||||
constructor(private authData: AuthData) {}
|
constructor(private authData: AuthData) {}
|
||||||
|
@ -15,12 +16,12 @@ export class AvataxCalculateTaxesPayloadService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPayload(
|
async getPayload(
|
||||||
taxBase: TaxBaseFragment,
|
payload: CalculateTaxesPayload,
|
||||||
avataxConfig: AvataxConfig
|
avataxConfig: AvataxConfig,
|
||||||
): Promise<CreateTransactionArgs> {
|
): Promise<CreateTransactionArgs> {
|
||||||
const matches = await this.getMatches();
|
const matches = await this.getMatches();
|
||||||
const payloadTransformer = new AvataxCalculateTaxesPayloadTransformer();
|
const payloadTransformer = new AvataxCalculateTaxesPayloadTransformer();
|
||||||
|
|
||||||
return payloadTransformer.transform(taxBase, avataxConfig, matches);
|
return payloadTransformer.transform(payload, avataxConfig, matches);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ export class AvataxOrderConfirmedPayloadTransformer {
|
||||||
async transform(
|
async transform(
|
||||||
order: OrderConfirmedSubscriptionFragment,
|
order: OrderConfirmedSubscriptionFragment,
|
||||||
avataxConfig: AvataxConfig,
|
avataxConfig: AvataxConfig,
|
||||||
matches: AvataxTaxCodeMatches
|
matches: AvataxTaxCodeMatches,
|
||||||
): Promise<CreateTransactionArgs> {
|
): Promise<CreateTransactionArgs> {
|
||||||
const avataxClient = new AvataxClient(avataxConfig);
|
const avataxClient = new AvataxClient(avataxConfig);
|
||||||
|
|
||||||
|
@ -46,9 +46,7 @@ export class AvataxOrderConfirmedPayloadTransformer {
|
||||||
code,
|
code,
|
||||||
type: this.matchDocumentType(avataxConfig),
|
type: this.matchDocumentType(avataxConfig),
|
||||||
entityUseCode,
|
entityUseCode,
|
||||||
customerCode:
|
customerCode: taxProviderUtils.resolveStringOrThrow(order.user?.id),
|
||||||
order.user?.id ??
|
|
||||||
"" /* In Saleor AvaTax plugin, the customer code is 0. In Taxes App, we set it to the user id. */,
|
|
||||||
companyCode: avataxConfig.companyCode ?? defaultAvataxConfig.companyCode,
|
companyCode: avataxConfig.companyCode ?? defaultAvataxConfig.companyCode,
|
||||||
// * commit: If true, the transaction will be committed immediately after it is created. See: https://developer.avalara.com/communications/dev-guide_rest_v2/commit-uncommit
|
// * commit: If true, the transaction will be committed immediately after it is created. See: https://developer.avalara.com/communications/dev-guide_rest_v2/commit-uncommit
|
||||||
commit: avataxConfig.isAutocommit,
|
commit: avataxConfig.isAutocommit,
|
||||||
|
@ -62,7 +60,7 @@ export class AvataxOrderConfirmedPayloadTransformer {
|
||||||
lines: linesTransformer.transform(order, avataxConfig, matches),
|
lines: linesTransformer.transform(order, avataxConfig, matches),
|
||||||
date,
|
date,
|
||||||
discount: discountUtils.sumDiscounts(
|
discount: discountUtils.sumDiscounts(
|
||||||
order.discounts.map((discount) => discount.amount.amount)
|
order.discounts.map((discount) => discount.amount.amount),
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { AvataxWebhookService } from "../avatax/avatax-webhook.service";
|
||||||
import { ProviderConnection } from "../provider-connections/provider-connections";
|
import { ProviderConnection } from "../provider-connections/provider-connections";
|
||||||
import { TaxJarWebhookService } from "../taxjar/taxjar-webhook.service";
|
import { TaxJarWebhookService } from "../taxjar/taxjar-webhook.service";
|
||||||
import { ProviderWebhookService } from "./tax-provider-webhook";
|
import { ProviderWebhookService } from "./tax-provider-webhook";
|
||||||
|
import { CalculateTaxesPayload } from "../../pages/api/webhooks/checkout-calculate-taxes";
|
||||||
|
|
||||||
// todo: refactor to a factory
|
// todo: refactor to a factory
|
||||||
class ActiveTaxProviderService implements ProviderWebhookService {
|
class ActiveTaxProviderService implements ProviderWebhookService {
|
||||||
|
@ -47,7 +48,7 @@ class ActiveTaxProviderService implements ProviderWebhookService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async calculateTaxes(payload: TaxBaseFragment) {
|
async calculateTaxes(payload: CalculateTaxesPayload) {
|
||||||
return this.client.calculateTaxes(payload);
|
return this.client.calculateTaxes(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,10 @@ function resolveOptionalOrThrow<T>(value: T | undefined | null, error?: Error):
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveStringOrThrow(value: string | undefined | null): string {
|
function resolveStringOrThrow(value: string | undefined | null): string {
|
||||||
return z.string().min(1, { message: "This field can not be empty." }).parse(value);
|
return z
|
||||||
|
.string({ required_error: "This field must be defined." })
|
||||||
|
.min(1, { message: "This field can not be empty." })
|
||||||
|
.parse(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const taxProviderUtils = {
|
export const taxProviderUtils = {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { SyncWebhookResponsesMap } from "@saleor/app-sdk/handlers/next";
|
import { SyncWebhookResponsesMap } from "@saleor/app-sdk/handlers/next";
|
||||||
import { OrderConfirmedSubscriptionFragment, TaxBaseFragment } from "../../../generated/graphql";
|
import { OrderConfirmedSubscriptionFragment } from "../../../generated/graphql";
|
||||||
|
import { CalculateTaxesPayload } from "../../pages/api/webhooks/checkout-calculate-taxes";
|
||||||
import { OrderCancelledPayload } from "../../pages/api/webhooks/order-cancelled";
|
import { OrderCancelledPayload } from "../../pages/api/webhooks/order-cancelled";
|
||||||
|
|
||||||
export type CalculateTaxesResponse = SyncWebhookResponsesMap["ORDER_CALCULATE_TAXES"];
|
export type CalculateTaxesResponse = SyncWebhookResponsesMap["ORDER_CALCULATE_TAXES"];
|
||||||
|
@ -7,7 +8,7 @@ export type CalculateTaxesResponse = SyncWebhookResponsesMap["ORDER_CALCULATE_TA
|
||||||
export type CreateOrderResponse = { id: string };
|
export type CreateOrderResponse = { id: string };
|
||||||
|
|
||||||
export interface ProviderWebhookService {
|
export interface ProviderWebhookService {
|
||||||
calculateTaxes: (payload: TaxBaseFragment) => Promise<CalculateTaxesResponse>;
|
calculateTaxes: (payload: CalculateTaxesPayload) => Promise<CalculateTaxesResponse>;
|
||||||
confirmOrder: (payload: OrderConfirmedSubscriptionFragment) => Promise<CreateOrderResponse>;
|
confirmOrder: (payload: OrderConfirmedSubscriptionFragment) => Promise<CreateOrderResponse>;
|
||||||
cancelOrder: (payload: OrderCancelledPayload) => Promise<void>;
|
cancelOrder: (payload: OrderCancelledPayload) => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -96,9 +96,6 @@ const taxIncludedTaxBase: TaxBase = {
|
||||||
],
|
],
|
||||||
sourceObject: {
|
sourceObject: {
|
||||||
avataxEntityCode: null,
|
avataxEntityCode: null,
|
||||||
user: {
|
|
||||||
id: "VXNlcjoyMDg0NTEwNDEw",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -194,9 +191,6 @@ const taxExcludedTaxBase: TaxBase = {
|
||||||
],
|
],
|
||||||
sourceObject: {
|
sourceObject: {
|
||||||
avataxEntityCode: null,
|
avataxEntityCode: null,
|
||||||
user: {
|
|
||||||
id: "VXNlcjoyMDg0NTEwNDEw",
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { TaxJarCalculateTaxesAdapter } from "./calculate-taxes/taxjar-calculate-
|
||||||
import { TaxJarOrderConfirmedAdapter } from "./order-confirmed/taxjar-order-confirmed-adapter";
|
import { TaxJarOrderConfirmedAdapter } from "./order-confirmed/taxjar-order-confirmed-adapter";
|
||||||
import { TaxJarClient } from "./taxjar-client";
|
import { TaxJarClient } from "./taxjar-client";
|
||||||
import { TaxJarConfig } from "./taxjar-connection-schema";
|
import { TaxJarConfig } from "./taxjar-connection-schema";
|
||||||
|
import { CalculateTaxesPayload } from "../../pages/api/webhooks/checkout-calculate-taxes";
|
||||||
|
|
||||||
export class TaxJarWebhookService implements ProviderWebhookService {
|
export class TaxJarWebhookService implements ProviderWebhookService {
|
||||||
client: TaxJarClient;
|
client: TaxJarClient;
|
||||||
|
@ -29,10 +30,10 @@ export class TaxJarWebhookService implements ProviderWebhookService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async calculateTaxes(taxBase: TaxBaseFragment) {
|
async calculateTaxes(payload: CalculateTaxesPayload) {
|
||||||
const adapter = new TaxJarCalculateTaxesAdapter(this.config, this.authData);
|
const adapter = new TaxJarCalculateTaxesAdapter(this.config, this.authData);
|
||||||
|
|
||||||
const response = await adapter.send({ taxBase });
|
const response = await adapter.send(payload);
|
||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,10 @@ export const config = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
type CalculateTaxesPayload = Extract<CalculateTaxesEventFragment, { __typename: "CalculateTaxes" }>;
|
export type CalculateTaxesPayload = Extract<
|
||||||
|
CalculateTaxesEventFragment,
|
||||||
|
{ __typename: "CalculateTaxes" }
|
||||||
|
>;
|
||||||
|
|
||||||
function verifyCalculateTaxesPayload(payload: CalculateTaxesPayload) {
|
function verifyCalculateTaxesPayload(payload: CalculateTaxesPayload) {
|
||||||
if (!payload.taxBase.lines) {
|
if (!payload.taxBase.lines) {
|
||||||
|
@ -52,11 +55,11 @@ export default checkoutCalculateTaxesSyncWebhook.createHandler(async (req, res,
|
||||||
const activeConnectionService = getActiveConnectionService(
|
const activeConnectionService = getActiveConnectionService(
|
||||||
channelSlug,
|
channelSlug,
|
||||||
appMetadata,
|
appMetadata,
|
||||||
ctx.authData
|
ctx.authData,
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.info("Found active connection service. Calculating taxes...");
|
logger.info("Found active connection service. Calculating taxes...");
|
||||||
const calculatedTaxes = await activeConnectionService.calculateTaxes(payload.taxBase);
|
const calculatedTaxes = await activeConnectionService.calculateTaxes(payload);
|
||||||
|
|
||||||
logger.info({ calculatedTaxes }, "Taxes calculated");
|
logger.info({ calculatedTaxes }, "Taxes calculated");
|
||||||
return webhookResponse.success(ctx.buildResponse(calculatedTaxes));
|
return webhookResponse.success(ctx.buildResponse(calculatedTaxes));
|
||||||
|
|
|
@ -52,11 +52,11 @@ export default orderCalculateTaxesSyncWebhook.createHandler(async (req, res, ctx
|
||||||
const activeConnectionService = getActiveConnectionService(
|
const activeConnectionService = getActiveConnectionService(
|
||||||
channelSlug,
|
channelSlug,
|
||||||
appMetadata,
|
appMetadata,
|
||||||
ctx.authData
|
ctx.authData,
|
||||||
);
|
);
|
||||||
|
|
||||||
logger.info("Found active connection service. Calculating taxes...");
|
logger.info("Found active connection service. Calculating taxes...");
|
||||||
const calculatedTaxes = await activeConnectionService.calculateTaxes(payload.taxBase);
|
const calculatedTaxes = await activeConnectionService.calculateTaxes(payload);
|
||||||
|
|
||||||
logger.info({ calculatedTaxes }, "Taxes calculated");
|
logger.info({ calculatedTaxes }, "Taxes calculated");
|
||||||
return webhookResponse.success(ctx.buildResponse(calculatedTaxes));
|
return webhookResponse.success(ctx.buildResponse(calculatedTaxes));
|
||||||
|
|
Loading…
Reference in a new issue