feat: disable document recording (#803)
* feat: ✨ add disable document recording * build: 👷 add changeset * refactor: ♻️ address feedback * feat: 💄 increase margin on HelperText * refactor: ♻️ add default value for isDocumentRecordingEnabled
This commit is contained in:
parent
1b47ad22da
commit
ce608872bd
13 changed files with 125 additions and 24 deletions
5
.changeset/eleven-geese-agree.md
Normal file
5
.changeset/eleven-geese-agree.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"saleor-app-taxes": minor
|
||||
---
|
||||
|
||||
Added "document recording" toggle. It is turned on by default. When turned off, the document type of all Avatax transactions change to "SalesOrder", making them not record. Read more about document recording [here](https://developer.avalara.com/ecommerce-integration-guide/sales-tax-badge/designing/disable-document-recording/).
|
|
@ -14,6 +14,7 @@ const mockedProviders: ProviderConnections = [
|
|||
companyCode: "DEFAULT",
|
||||
isAutocommit: false,
|
||||
isSandbox: true,
|
||||
isDocumentRecordingEnabled: true,
|
||||
name: "avatax-1",
|
||||
shippingTaxCode: "FR000000",
|
||||
credentials: {
|
||||
|
|
|
@ -6,6 +6,7 @@ const defaultAvataxConfig: AvataxConfig = {
|
|||
isSandbox: true,
|
||||
name: "Avatax-1",
|
||||
shippingTaxCode: "FR000000",
|
||||
isDocumentRecordingEnabled: true,
|
||||
address: {
|
||||
country: "US",
|
||||
zip: "95008",
|
||||
|
|
|
@ -27,6 +27,7 @@ export const avataxConfigSchema = z
|
|||
companyCode: z.string().optional(),
|
||||
isAutocommit: z.boolean(),
|
||||
shippingTaxCode: z.string().optional(),
|
||||
isDocumentRecordingEnabled: z.boolean().default(true),
|
||||
address: addressSchema,
|
||||
})
|
||||
.merge(baseAvataxConfigSchema);
|
||||
|
@ -38,6 +39,7 @@ export const defaultAvataxConfig: AvataxConfig = {
|
|||
companyCode: "",
|
||||
isSandbox: false,
|
||||
isAutocommit: false,
|
||||
isDocumentRecordingEnabled: true,
|
||||
shippingTaxCode: "",
|
||||
credentials: {
|
||||
username: "",
|
||||
|
|
|
@ -1,11 +1,25 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { AvataxCalculateTaxesMockGenerator } from "./avatax-calculate-taxes-mock-generator";
|
||||
import { AvataxCalculateTaxesPayloadTransformer } from "./avatax-calculate-taxes-payload-transformer";
|
||||
import { DocumentType } from "avatax/lib/enums/DocumentType";
|
||||
|
||||
const mockGenerator = new AvataxCalculateTaxesMockGenerator();
|
||||
const avataxConfigMock = mockGenerator.generateAvataxConfig();
|
||||
|
||||
describe("AvataxCalculateTaxesPayloadTransformer", () => {
|
||||
it("returns document type of SalesInvoice", () => {
|
||||
const taxBaseMock = mockGenerator.generateTaxBase();
|
||||
const matchesMock = mockGenerator.generateTaxCodeMatches();
|
||||
|
||||
const payload = new AvataxCalculateTaxesPayloadTransformer().transform(
|
||||
taxBaseMock,
|
||||
avataxConfigMock,
|
||||
matchesMock
|
||||
);
|
||||
|
||||
expect(payload.model.type).toBe(DocumentType.SalesOrder);
|
||||
});
|
||||
it("when discounts, calculates the sum of discounts", () => {
|
||||
const mockGenerator = new AvataxCalculateTaxesMockGenerator();
|
||||
const avataxConfigMock = mockGenerator.generateAvataxConfig();
|
||||
const taxBaseMock = mockGenerator.generateTaxBase({ discounts: [{ amount: { amount: 10 } }] });
|
||||
const matchesMock = mockGenerator.generateTaxCodeMatches();
|
||||
|
||||
|
@ -18,8 +32,6 @@ describe("AvataxCalculateTaxesPayloadTransformer", () => {
|
|||
expect(payload.model.discount).toEqual(10);
|
||||
});
|
||||
it("when no discounts, the sum of discount is 0", () => {
|
||||
const mockGenerator = new AvataxCalculateTaxesMockGenerator();
|
||||
const avataxConfigMock = mockGenerator.generateAvataxConfig();
|
||||
const taxBaseMock = mockGenerator.generateTaxBase();
|
||||
const matchesMock = mockGenerator.generateTaxCodeMatches();
|
||||
|
||||
|
|
|
@ -8,6 +8,16 @@ import { AvataxTaxCodeMatches } from "../tax-code/avatax-tax-code-match-reposito
|
|||
import { AvataxCalculateTaxesPayloadLinesTransformer } from "./avatax-calculate-taxes-payload-lines-transformer";
|
||||
|
||||
export class AvataxCalculateTaxesPayloadTransformer {
|
||||
private matchDocumentType(config: AvataxConfig): DocumentType {
|
||||
/*
|
||||
* * For calculating taxes, we always use DocumentType.SalesOrder because it doesn't cause transaction recording.
|
||||
* * The full flow is described here: https://developer.avalara.com/ecommerce-integration-guide/sales-tax-badge/design-document-workflow/should-i-commit/
|
||||
* * config.isDocumentRecordingEnabledEnabled is used to determine if the transaction should be recorded (hence if the document type should be SalesOrder).
|
||||
* * Given that we never want to record the transaction in calculate taxes, we always return DocumentType.SalesOrder.
|
||||
*/
|
||||
return DocumentType.SalesOrder;
|
||||
}
|
||||
|
||||
transform(
|
||||
taxBase: TaxBaseFragment,
|
||||
avataxConfig: AvataxConfig,
|
||||
|
@ -17,7 +27,7 @@ export class AvataxCalculateTaxesPayloadTransformer {
|
|||
|
||||
return {
|
||||
model: {
|
||||
type: DocumentType.SalesOrder,
|
||||
type: this.matchDocumentType(avataxConfig),
|
||||
customerCode: taxBase.sourceObject.user?.id ?? "",
|
||||
companyCode: avataxConfig.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
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { AvataxOrderCreatedMockGenerator } from "./avatax-order-created-mock-generator";
|
||||
import { AvataxOrderCreatedPayloadTransformer } from "./avatax-order-created-payload-transformer";
|
||||
import { DocumentType } from "avatax/lib/enums/DocumentType";
|
||||
|
||||
const mockGenerator = new AvataxOrderCreatedMockGenerator();
|
||||
|
||||
|
@ -16,12 +17,29 @@ const discountedOrderMock = mockGenerator.generateOrder({
|
|||
],
|
||||
});
|
||||
|
||||
const transformer = new AvataxOrderCreatedPayloadTransformer();
|
||||
|
||||
export const avataxConfigMock = mockGenerator.generateAvataxConfig();
|
||||
|
||||
describe("AvataxOrderCreatedPayloadTransformer", () => {
|
||||
it("returns lines with discounted: true when there are discounts", () => {
|
||||
const transformer = new AvataxOrderCreatedPayloadTransformer();
|
||||
it("returns document type of SalesInvoice when isDocumentRecordingEnabled is true", () => {
|
||||
const payload = transformer.transform(orderMock, avataxConfigMock, []);
|
||||
|
||||
expect(payload.model.type).toBe(DocumentType.SalesInvoice);
|
||||
}),
|
||||
it("returns document type of SalesOrder when isDocumentRecordingEnabled is false", () => {
|
||||
const payload = transformer.transform(
|
||||
orderMock,
|
||||
{
|
||||
...avataxConfigMock,
|
||||
isDocumentRecordingEnabled: false,
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
expect(payload.model.type).toBe(DocumentType.SalesOrder);
|
||||
});
|
||||
it("returns lines with discounted: true when there are discounts", () => {
|
||||
const payload = transformer.transform(discountedOrderMock, avataxConfigMock, []);
|
||||
|
||||
const linesWithoutShipping = payload.model.lines.slice(0, -1);
|
||||
|
@ -30,7 +48,6 @@ describe("AvataxOrderCreatedPayloadTransformer", () => {
|
|||
expect(check).toBe(true);
|
||||
});
|
||||
it("returns lines with discounted: false when there are no discounts", () => {
|
||||
const transformer = new AvataxOrderCreatedPayloadTransformer();
|
||||
const payload = transformer.transform(orderMock, avataxConfigMock, []);
|
||||
|
||||
const linesWithoutShipping = payload.model.lines.slice(0, -1);
|
||||
|
|
|
@ -10,6 +10,14 @@ import { AvataxOrderCreatedPayloadLinesTransformer } from "./avatax-order-create
|
|||
export const SHIPPING_ITEM_CODE = "Shipping";
|
||||
|
||||
export class AvataxOrderCreatedPayloadTransformer {
|
||||
private matchDocumentType(config: AvataxConfig): DocumentType {
|
||||
if (!config.isDocumentRecordingEnabled) {
|
||||
// isDocumentRecordingEnabled = false changes all the DocTypes within your AvaTax requests to SalesOrder. This will stop any transaction from being recorded within AvaTax.
|
||||
return DocumentType.SalesOrder;
|
||||
}
|
||||
|
||||
return DocumentType.SalesInvoice;
|
||||
}
|
||||
transform(
|
||||
order: OrderCreatedSubscriptionFragment,
|
||||
avataxConfig: AvataxConfig,
|
||||
|
@ -19,7 +27,7 @@ export class AvataxOrderCreatedPayloadTransformer {
|
|||
|
||||
return {
|
||||
model: {
|
||||
type: DocumentType.SalesInvoice,
|
||||
type: this.matchDocumentType(avataxConfig),
|
||||
customerCode:
|
||||
order.user?.id ??
|
||||
"" /* In Saleor Avatax plugin, the customer code is 0. In Taxes App, we set it to the user id. */,
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
|
||||
const MOCK_AVATAX_CONFIG: AvataxConfig = {
|
||||
companyCode: "DEFAULT",
|
||||
isDocumentRecordingEnabled: true,
|
||||
isAutocommit: false,
|
||||
isSandbox: true,
|
||||
name: "Avatax-1",
|
||||
|
@ -129,8 +130,6 @@ describe("getTransactionCodeFromMetadata", () => {
|
|||
});
|
||||
});
|
||||
|
||||
const transformer = new AvataxOrderFulfilledPayloadTransformer(MOCK_AVATAX_CONFIG);
|
||||
|
||||
const MOCKED_ORDER_FULFILLED_PAYLOAD: {
|
||||
order: OrderFulfilledSubscriptionFragment;
|
||||
} = {
|
||||
|
@ -138,16 +137,35 @@ const MOCKED_ORDER_FULFILLED_PAYLOAD: {
|
|||
};
|
||||
|
||||
describe("AvataxOrderFulfilledPayloadTransformer", () => {
|
||||
it("returns transformed payload", () => {
|
||||
const mappedPayload = transformer.transform(MOCKED_ORDER_FULFILLED_PAYLOAD);
|
||||
|
||||
expect(mappedPayload).toEqual({
|
||||
transactionCode: "transaction-code",
|
||||
companyCode: "DEFAULT",
|
||||
documentType: DocumentType.SalesInvoice,
|
||||
model: {
|
||||
commit: true,
|
||||
},
|
||||
it("returns document type of SalesOrder when isDocumentRecordingEnabled is false", () => {
|
||||
const transformer = new AvataxOrderFulfilledPayloadTransformer({
|
||||
...MOCK_AVATAX_CONFIG,
|
||||
isDocumentRecordingEnabled: false,
|
||||
});
|
||||
|
||||
const payload = transformer.transform(MOCKED_ORDER_FULFILLED_PAYLOAD);
|
||||
|
||||
expect(payload.documentType).toBe(DocumentType.SalesOrder);
|
||||
}),
|
||||
it("returns document type of SalesInvoice when isDocumentRecordingEnabled is true", () => {
|
||||
const transformer = new AvataxOrderFulfilledPayloadTransformer(MOCK_AVATAX_CONFIG);
|
||||
|
||||
const payload = transformer.transform(MOCKED_ORDER_FULFILLED_PAYLOAD);
|
||||
|
||||
expect(payload.documentType).toBe(DocumentType.SalesInvoice);
|
||||
}),
|
||||
it("returns transformed payload", () => {
|
||||
const transformer = new AvataxOrderFulfilledPayloadTransformer(MOCK_AVATAX_CONFIG);
|
||||
|
||||
const mappedPayload = transformer.transform(MOCKED_ORDER_FULFILLED_PAYLOAD);
|
||||
|
||||
expect(mappedPayload).toEqual({
|
||||
transactionCode: "transaction-code",
|
||||
companyCode: "DEFAULT",
|
||||
documentType: DocumentType.SalesInvoice,
|
||||
model: {
|
||||
commit: true,
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -24,13 +24,20 @@ export function getTransactionCodeFromMetadata(
|
|||
|
||||
export class AvataxOrderFulfilledPayloadTransformer {
|
||||
constructor(private readonly config: AvataxConfig) {}
|
||||
private matchDocumentType(config: AvataxConfig): DocumentType {
|
||||
if (!config.isDocumentRecordingEnabled) {
|
||||
return DocumentType.SalesOrder;
|
||||
}
|
||||
|
||||
return DocumentType.SalesInvoice;
|
||||
}
|
||||
transform({ order }: AvataxOrderFulfilledPayload): AvataxOrderFulfilledTarget {
|
||||
const transactionCode = getTransactionCodeFromMetadata(order.privateMetadata);
|
||||
|
||||
return {
|
||||
transactionCode,
|
||||
companyCode: this.config.companyCode ?? "",
|
||||
documentType: DocumentType.SalesInvoice,
|
||||
documentType: this.matchDocumentType(this.config),
|
||||
model: {
|
||||
commit: true,
|
||||
},
|
||||
|
|
|
@ -121,7 +121,24 @@ export const AvataxConfigurationCredentialsFragment = (
|
|||
}
|
||||
name="isSandbox"
|
||||
/>
|
||||
|
||||
<AppToggle
|
||||
control={control}
|
||||
label="Document recording"
|
||||
helperText={
|
||||
<HelperText>
|
||||
When turned off, the document type will always be set to <i>SalesOrder</i>. This
|
||||
means the transactions will not be recorded in Avatax. Read more{" "}
|
||||
<TextLink
|
||||
href="https://developer.avalara.com/ecommerce-integration-guide/sales-tax-badge/designing/disable-document-recording/"
|
||||
newTab
|
||||
>
|
||||
here
|
||||
</TextLink>
|
||||
.
|
||||
</HelperText>
|
||||
}
|
||||
name="isDocumentRecordingEnabled"
|
||||
/>
|
||||
<AppToggle
|
||||
control={control}
|
||||
label="Autocommit"
|
||||
|
|
|
@ -12,6 +12,8 @@ export const HelperText = ({
|
|||
<Text
|
||||
color={disabled ? "textNeutralDisabled" : "textNeutralSubdued"}
|
||||
fontWeight={"captionLarge"}
|
||||
marginTop={2}
|
||||
as="p"
|
||||
>
|
||||
{children}
|
||||
</Text>
|
||||
|
|
|
@ -21,6 +21,7 @@ const mockedProviders: ProviderConnections = [
|
|||
provider: "avatax",
|
||||
id: "1",
|
||||
config: {
|
||||
isDocumentRecordingEnabled: true,
|
||||
companyCode: "DEFAULT",
|
||||
isAutocommit: false,
|
||||
isSandbox: true,
|
||||
|
|
Loading…
Reference in a new issue