fix customer code calculate taxes (#922)
* fix: 🐛 calculate taxes customerCode * build: 👷 changeset * refactor: ♻️ address feedback * feat: ✨ add migration * Empty-Commit
This commit is contained in:
parent
6f1c5c9436
commit
34efd39dcf
12 changed files with 118 additions and 23 deletions
5
.changeset/fifty-radios-beam.md
Normal file
5
.changeset/fifty-radios-beam.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"saleor-app-taxes": patch
|
||||
---
|
||||
|
||||
Fixed the issue when user id was not available during tax calculation. Now, to identify the user during tax calculation, we use the user email.
|
|
@ -10,11 +10,5 @@ fragment CalculateTaxesEvent on Event {
|
|||
value
|
||||
}
|
||||
}
|
||||
issuingPrincipal {
|
||||
__typename
|
||||
... on User {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -60,11 +60,14 @@ fragment TaxBase on TaxableObject {
|
|||
...TaxBaseLine
|
||||
}
|
||||
sourceObject {
|
||||
__typename
|
||||
... on Checkout {
|
||||
avataxEntityCode: metafield(key: "avataxEntityCode")
|
||||
email
|
||||
}
|
||||
... on Order {
|
||||
avataxEntityCode: metafield(key: "avataxEntityCode")
|
||||
userEmail
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
15
apps/taxes/scripts/migrations/1.15-taxes-migration.ts
Normal file
15
apps/taxes/scripts/migrations/1.15-taxes-migration.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
/* eslint-disable multiline-comment-style */
|
||||
import { checkoutCalculateTaxesSyncWebhook } from "../../src/pages/api/webhooks/checkout-calculate-taxes";
|
||||
import { orderCalculateTaxesSyncWebhook } from "../../src/pages/api/webhooks/order-calculate-taxes";
|
||||
import { AppWebhookMigrator } from "./app-webhook-migrator";
|
||||
|
||||
/**
|
||||
* Contains the migration logic for the Taxes App. In the 1st step, it is expected to only write, not delete. The cleanup will be done in the 2nd step.
|
||||
* @param webhookMigrator - The AppWebhookMigrator instance.
|
||||
*/
|
||||
export async function migrateTaxes(webhookMigrator: AppWebhookMigrator) {
|
||||
// Migration plan:
|
||||
// 1. Update subscriptionQuery of all calculateTaxes webhooks
|
||||
webhookMigrator.updateWebhookQueryByHandler(orderCalculateTaxesSyncWebhook);
|
||||
webhookMigrator.updateWebhookQueryByHandler(checkoutCalculateTaxesSyncWebhook);
|
||||
}
|
|
@ -8,6 +8,8 @@ type AppWebhookMigratorOptions = {
|
|||
mode: "report" | "migrate";
|
||||
};
|
||||
|
||||
type AppWebhookHandler = SaleorSyncWebhook | SaleorAsyncWebhook;
|
||||
|
||||
export class AppWebhookMigrator {
|
||||
private appWebhookRepository: AppWebhookRepository;
|
||||
private appId: string;
|
||||
|
@ -33,7 +35,7 @@ export class AppWebhookMigrator {
|
|||
this.mode = mode;
|
||||
}
|
||||
|
||||
private registerWebhookFromHandler(webhookHandler: SaleorSyncWebhook | SaleorAsyncWebhook) {
|
||||
private registerWebhookFromHandler(webhookHandler: AppWebhookHandler) {
|
||||
const manifest = webhookHandler.getWebhookManifest(this.apiUrl);
|
||||
|
||||
if (!manifest.query) {
|
||||
|
@ -44,6 +46,9 @@ export class AppWebhookMigrator {
|
|||
throw new Error("Webhook name is required");
|
||||
}
|
||||
|
||||
console.log(`⏳ Webhook ${manifest.name} will be registered`);
|
||||
|
||||
if (this.mode === "migrate") {
|
||||
return this.appWebhookRepository.create({
|
||||
appId: this.appId,
|
||||
name: manifest.name,
|
||||
|
@ -54,6 +59,7 @@ export class AppWebhookMigrator {
|
|||
isActive: manifest.isActive ?? true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async deleteWebhookById(webhookId: string) {
|
||||
console.log(`⏳ Webhook ${webhookId} will be deleted`);
|
||||
|
@ -119,6 +125,28 @@ export class AppWebhookMigrator {
|
|||
await this.deleteWebhookById(webhook.id);
|
||||
}
|
||||
|
||||
async updateWebhookQueryByHandler(webhookHandler: AppWebhookHandler) {
|
||||
const webhooks = await this.getAppWebhooks();
|
||||
|
||||
const manifest = webhookHandler.getWebhookManifest(this.apiUrl);
|
||||
const webhookName = manifest.name;
|
||||
|
||||
const webhook = webhooks.find((webhook) => webhook.name === webhookName);
|
||||
|
||||
if (!webhook) {
|
||||
console.log(`🚧 Webhook ${webhookName} not found`);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`⏳ Webhook ${webhookName} query will be updated`);
|
||||
|
||||
if (this.mode === "migrate") {
|
||||
await this.appWebhookRepository.update(webhook.id, { query: manifest.query });
|
||||
console.log(`✅ Webhook ${webhookName} query updated`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a webhook if it doesn't exist based on a handler.
|
||||
* @param webhookHandler - The handler of the webhook we want to register.
|
||||
|
|
|
@ -14,6 +14,10 @@ import {
|
|||
EnableWebhookMutationVariables,
|
||||
FetchAppWebhooksDocument,
|
||||
FetchAppWebhooksQuery,
|
||||
UpdateAppWebhookDocument,
|
||||
UpdateAppWebhookMutation,
|
||||
UpdateAppWebhookMutationVariables,
|
||||
WebhookUpdateInput,
|
||||
} from "../../generated/graphql";
|
||||
|
||||
gql`
|
||||
|
@ -55,6 +59,16 @@ gql`
|
|||
}
|
||||
`;
|
||||
|
||||
gql`
|
||||
mutation UpdateAppWebhook($id: ID!, $input: WebhookUpdateInput!) {
|
||||
webhookUpdate(id: $id, input: $input) {
|
||||
webhook {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
gql`
|
||||
mutation DeleteAppWebhook($id: ID!) {
|
||||
webhookDelete(id: $id) {
|
||||
|
@ -168,4 +182,21 @@ export class AppWebhookRepository {
|
|||
|
||||
return data?.webhookDelete?.webhook?.id;
|
||||
}
|
||||
|
||||
async update(id: string, input: WebhookUpdateInput) {
|
||||
const { error, data } = await this.client
|
||||
.mutation<UpdateAppWebhookMutation>(UpdateAppWebhookDocument, {
|
||||
id,
|
||||
input,
|
||||
} as UpdateAppWebhookMutationVariables)
|
||||
.toPromise();
|
||||
|
||||
if (error) {
|
||||
console.log(`❌ Was not able to update webhook ${id}`, error.message);
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return data?.webhookUpdate?.webhook?.id;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import * as dotenv from "dotenv";
|
||||
import { createAppWebhookMigrator } from "./app-webhook-migrator";
|
||||
import { fetchCloudAplEnvs, verifyRequiredEnvs } from "./migration-utils";
|
||||
import { migrateTaxes } from "./1.13-taxes-migration";
|
||||
import { migrateTaxes } from "./1.15-taxes-migration";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import * as dotenv from "dotenv";
|
||||
import { createAppWebhookMigrator } from "./app-webhook-migrator";
|
||||
import { fetchCloudAplEnvs, verifyRequiredEnvs } from "./migration-utils";
|
||||
import { migrateTaxes } from "./1.13-taxes-migration";
|
||||
import { migrateTaxes } from "./1.15-taxes-migration";
|
||||
|
||||
dotenv.config();
|
||||
|
||||
|
|
|
@ -107,6 +107,8 @@ const defaultTaxBase: TaxBase = {
|
|||
],
|
||||
sourceObject: {
|
||||
avataxEntityCode: null,
|
||||
__typename: "Checkout",
|
||||
email: "test@saleor.io",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -66,8 +66,10 @@ describe("AvataxCalculateTaxesPayloadTransformer", () => {
|
|||
|
||||
expect(payload.model.discount).toEqual(0);
|
||||
});
|
||||
it("when no issuingPrincipal.id, throws an error", async () => {
|
||||
const taxBaseMock = mockGenerator.generateTaxBase();
|
||||
it("when no email in sourceObject, throws an error", async () => {
|
||||
const taxBaseMock = mockGenerator.generateTaxBase({
|
||||
sourceObject: { email: undefined, __typename: "Checkout" },
|
||||
});
|
||||
const matchesMock = mockGenerator.generateTaxCodeMatches();
|
||||
|
||||
const payloadMock = {
|
||||
|
|
|
@ -21,6 +21,19 @@ export class AvataxCalculateTaxesPayloadTransformer {
|
|||
return DocumentType.SalesOrder;
|
||||
}
|
||||
|
||||
// During the checkout process, it appears the customer id is not always available. We can use the email address instead.
|
||||
private resolveCustomerCode(payload: CalculateTaxesPayload): string {
|
||||
if (payload.taxBase.sourceObject.__typename === "Checkout") {
|
||||
return taxProviderUtils.resolveStringOrThrow(payload.taxBase.sourceObject.email);
|
||||
}
|
||||
|
||||
if (payload.taxBase.sourceObject.__typename === "Order") {
|
||||
return taxProviderUtils.resolveStringOrThrow(payload.taxBase.sourceObject.userEmail);
|
||||
}
|
||||
|
||||
throw new Error("Cannot resolve customer code");
|
||||
}
|
||||
|
||||
async transform(
|
||||
payload: CalculateTaxesPayload,
|
||||
avataxConfig: AvataxConfig,
|
||||
|
@ -33,9 +46,7 @@ export class AvataxCalculateTaxesPayloadTransformer {
|
|||
payload.taxBase.sourceObject.avataxEntityCode,
|
||||
);
|
||||
|
||||
const customerCode = taxProviderUtils.resolveStringOrThrow(
|
||||
payload.issuingPrincipal?.__typename === "User" ? payload.issuingPrincipal.id : undefined,
|
||||
);
|
||||
const customerCode = this.resolveCustomerCode(payload);
|
||||
|
||||
return {
|
||||
model: {
|
||||
|
|
|
@ -96,6 +96,8 @@ const taxIncludedTaxBase: TaxBase = {
|
|||
],
|
||||
sourceObject: {
|
||||
avataxEntityCode: null,
|
||||
__typename: "Checkout",
|
||||
email: "test@saleor.io",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -191,6 +193,8 @@ const taxExcludedTaxBase: TaxBase = {
|
|||
],
|
||||
sourceObject: {
|
||||
avataxEntityCode: null,
|
||||
__typename: "Checkout",
|
||||
email: "test@saleor.io",
|
||||
},
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue