chore: split credentials and settings (#886)
* feat: ✨ grey out disabled links * chore: 🚚 move fields to new avatax settings fragment * build: 👷 add changeset * refactor: 🚚 move companyCode to credentials * refactor: ♻️ make helper texts more accurate * refactor: 🚚 Avatax -> AvaTax
This commit is contained in:
parent
c50797e836
commit
be761b251e
37 changed files with 219 additions and 203 deletions
5
.changeset/brown-tools-dance.md
Normal file
5
.changeset/brown-tools-dance.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"saleor-app-taxes": minor
|
||||
---
|
||||
|
||||
Changed the layout in the AvaTax configuration form to consist of three sections: "Credentials", "Settings" (new) and "Address". Now, the "Credentials" section contains only fields that affect authentication.
|
|
@ -13,7 +13,7 @@ const avataxCredentialsSchema = z.object({
|
|||
password: z.string().min(1, { message: "Password requires at least one character." }),
|
||||
});
|
||||
|
||||
// All that is needed to create Avatax configuration.
|
||||
// All that is needed to create AvaTax configuration.
|
||||
export const baseAvataxConfigSchema = z.object({
|
||||
isSandbox: z.boolean(),
|
||||
credentials: avataxCredentialsSchema,
|
||||
|
|
|
@ -49,7 +49,7 @@ export const avataxConnectionRouter = router({
|
|||
|
||||
await ctx.connectionService.verifyConnections();
|
||||
|
||||
logger.info("Avatax connections were successfully verified");
|
||||
logger.info("AvaTax connections were successfully verified");
|
||||
|
||||
return { ok: true };
|
||||
}),
|
||||
|
@ -62,7 +62,7 @@ export const avataxConnectionRouter = router({
|
|||
|
||||
const result = await ctx.connectionService.getById(input.id);
|
||||
|
||||
logger.info(`Avatax configuration with an id: ${result.id} was successfully retrieved`);
|
||||
logger.info(`AvaTax configuration with an id: ${result.id} was successfully retrieved`);
|
||||
|
||||
return result;
|
||||
}),
|
||||
|
@ -76,7 +76,7 @@ export const avataxConnectionRouter = router({
|
|||
|
||||
const result = await ctx.connectionService.create(input.value);
|
||||
|
||||
logger.info("Avatax configuration was successfully created");
|
||||
logger.info("AvaTax configuration was successfully created");
|
||||
|
||||
return result;
|
||||
}),
|
||||
|
@ -92,7 +92,7 @@ export const avataxConnectionRouter = router({
|
|||
|
||||
const result = await ctx.connectionService.delete(input.id);
|
||||
|
||||
logger.info(`Avatax configuration with an id: ${input.id} was deleted`);
|
||||
logger.info(`AvaTax configuration with an id: ${input.id} was deleted`);
|
||||
|
||||
return result;
|
||||
}),
|
||||
|
@ -108,7 +108,7 @@ export const avataxConnectionRouter = router({
|
|||
|
||||
const result = await ctx.connectionService.update(input.id, input.value);
|
||||
|
||||
logger.info(`Avatax configuration with an id: ${input.id} was successfully updated`);
|
||||
logger.info(`AvaTax configuration with an id: ${input.id} was successfully updated`);
|
||||
|
||||
return result;
|
||||
}),
|
||||
|
@ -137,7 +137,7 @@ export const avataxConnectionRouter = router({
|
|||
|
||||
const result = await addressValidationService.validate(input.id, input.value);
|
||||
|
||||
logger.info(`Avatax address was successfully validated`);
|
||||
logger.info(`AvaTax address was successfully validated`);
|
||||
|
||||
return result;
|
||||
}),
|
||||
|
@ -157,7 +157,7 @@ export const avataxConnectionRouter = router({
|
|||
|
||||
const result = await addressValidation.validate(input.value.address);
|
||||
|
||||
logger.info(`Avatax address was successfully validated`);
|
||||
logger.info(`AvaTax address was successfully validated`);
|
||||
|
||||
return result;
|
||||
}),
|
||||
|
@ -184,7 +184,7 @@ export const avataxConnectionRouter = router({
|
|||
|
||||
await authValidation.validate(input.id, input.value);
|
||||
|
||||
logger.info(`Avatax client was successfully validated`);
|
||||
logger.info(`AvaTax client was successfully validated`);
|
||||
}),
|
||||
createValidateCredentials: protectedClientProcedure
|
||||
.input(z.object({ value: baseAvataxConfigSchema }))
|
||||
|
@ -200,7 +200,7 @@ export const avataxConnectionRouter = router({
|
|||
|
||||
const result = await authValidation.validate();
|
||||
|
||||
logger.info(`Avatax client was successfully validated`);
|
||||
logger.info(`AvaTax client was successfully validated`);
|
||||
|
||||
return result;
|
||||
}),
|
||||
|
|
|
@ -15,7 +15,7 @@ export class AvataxDocumentCodeResolver {
|
|||
const code = avataxDocumentCode ?? orderId;
|
||||
|
||||
/*
|
||||
* The requirement from Avatax API is that document code is a string that must be between 1 and 20 characters long.
|
||||
* The requirement from AvaTax API is that document code is a string that must be between 1 and 20 characters long.
|
||||
* // todo: document that its sliced
|
||||
*/
|
||||
return code.slice(0, 20);
|
||||
|
|
|
@ -27,21 +27,21 @@ export class AvataxCalculateTaxesAdapter
|
|||
|
||||
// todo: refactor because its getting too big
|
||||
async send(payload: AvataxCalculateTaxesPayload): Promise<AvataxCalculateTaxesResponse> {
|
||||
this.logger.debug("Transforming the Saleor payload for calculating taxes with Avatax...");
|
||||
this.logger.debug("Transforming the Saleor payload for calculating taxes with AvaTax...");
|
||||
const payloadService = new AvataxCalculateTaxesPayloadService(this.authData);
|
||||
const target = await payloadService.getPayload(payload.taxBase, this.config);
|
||||
|
||||
this.logger.debug("Calling Avatax createTransaction with transformed payload...");
|
||||
this.logger.debug("Calling AvaTax createTransaction with transformed payload...");
|
||||
|
||||
const client = new AvataxClient(this.config);
|
||||
const response = await client.createTransaction(target);
|
||||
|
||||
this.logger.debug("Avatax createTransaction successfully responded");
|
||||
this.logger.debug("AvaTax createTransaction successfully responded");
|
||||
|
||||
const responseTransformer = new AvataxCalculateTaxesResponseTransformer();
|
||||
const transformedResponse = responseTransformer.transform(response);
|
||||
|
||||
this.logger.debug("Transformed Avatax createTransaction response");
|
||||
this.logger.debug("Transformed AvaTax createTransaction response");
|
||||
|
||||
return transformedResponse;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ export class AvataxCalculateTaxesPayloadLinesTransformer {
|
|||
});
|
||||
|
||||
if (taxBase.shippingPrice.amount !== 0) {
|
||||
// * In Avatax, shipping is a regular line
|
||||
// * In AvaTax, shipping is a regular line
|
||||
const shippingLine: LineItemModel = {
|
||||
amount: taxBase.shippingPrice.amount,
|
||||
itemCode: SHIPPING_ITEM_CODE,
|
||||
|
|
|
@ -16,7 +16,7 @@ export class AvataxAuthValidationService {
|
|||
const result = await this.avataxClient.ping();
|
||||
|
||||
if (!result.authenticated) {
|
||||
throw new Error("Invalid Avatax credentials.");
|
||||
throw new Error("Invalid AvaTax credentials.");
|
||||
}
|
||||
} catch (error) {
|
||||
const errorResolver = new AvataxValidationErrorResolver();
|
||||
|
|
|
@ -27,9 +27,9 @@ describe("AvataxValidationErrorResolver", () => {
|
|||
});
|
||||
|
||||
expect(result).toBeInstanceOf(Error);
|
||||
expect(result.message).toBe("Invalid Avatax credentials.");
|
||||
expect(result.message).toBe("Invalid AvaTax credentials.");
|
||||
});
|
||||
it("when other Avatax error, should return error with first message", () => {
|
||||
it("when other AvaTax error, should return error with first message", () => {
|
||||
const result = errorResolver.resolve({
|
||||
code: "error",
|
||||
details: [
|
||||
|
@ -57,6 +57,6 @@ describe("AvataxValidationErrorResolver", () => {
|
|||
const result = errorResolver.resolve("error");
|
||||
|
||||
expect(result).toBeInstanceOf(Error);
|
||||
expect(result.message).toBe("Unknown error while validating Avatax configuration.");
|
||||
expect(result.message).toBe("Unknown error while validating AvaTax configuration.");
|
||||
});
|
||||
});
|
||||
|
|
|
@ -26,12 +26,12 @@ export class AvataxValidationErrorResolver {
|
|||
const parseResult = avataxErrorSchema.safeParse(error);
|
||||
const isErrorParsed = parseResult.success;
|
||||
|
||||
// Avatax doesn't return a type for their error format, so we need to parse the error
|
||||
// AvaTax doesn't return a type for their error format, so we need to parse the error
|
||||
if (isErrorParsed) {
|
||||
const { code, details } = parseResult.data;
|
||||
|
||||
if (code === "AuthenticationException") {
|
||||
return new Error("Invalid Avatax credentials.");
|
||||
return new Error("Invalid AvaTax credentials.");
|
||||
}
|
||||
|
||||
return new Error(details[0].message);
|
||||
|
@ -41,7 +41,7 @@ export class AvataxValidationErrorResolver {
|
|||
return error;
|
||||
}
|
||||
|
||||
this.logger.error("Unknown error while validating Avatax configuration.");
|
||||
return new Error("Unknown error while validating Avatax configuration.");
|
||||
this.logger.error("Unknown error while validating AvaTax configuration.");
|
||||
return new Error("Unknown error while validating AvaTax configuration.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ export class PublicAvataxConnectionService {
|
|||
const connections = await this.connectionService.getAll();
|
||||
|
||||
if (connections.length === 0) {
|
||||
throw new Error("No Avatax connections found");
|
||||
throw new Error("No AvaTax connections found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,12 +15,12 @@ export class AvataxOrderCancelledAdapter implements WebhookAdapter<OrderCancelle
|
|||
}
|
||||
|
||||
async send(payload: OrderCancelledPayload) {
|
||||
this.logger.debug("Transforming the Saleor payload for cancelling transaction with Avatax...");
|
||||
this.logger.debug("Transforming the Saleor payload for cancelling transaction with AvaTax...");
|
||||
|
||||
const payloadTransformer = new AvataxOrderCancelledPayloadTransformer(this.config);
|
||||
const target = payloadTransformer.transform({ ...payload });
|
||||
|
||||
this.logger.debug("Calling Avatax voidTransaction with transformed payload...");
|
||||
this.logger.debug("Calling AvaTax voidTransaction with transformed payload...");
|
||||
|
||||
const client = new AvataxClient(this.config);
|
||||
|
||||
|
|
|
@ -23,22 +23,22 @@ export class AvataxOrderConfirmedAdapter
|
|||
}
|
||||
|
||||
async send(payload: AvataxOrderConfirmedPayload): Promise<AvataxOrderConfirmedResponse> {
|
||||
this.logger.debug("Transforming the Saleor payload for creating order with Avatax...");
|
||||
this.logger.debug("Transforming the Saleor payload for creating order with AvaTax...");
|
||||
|
||||
const payloadService = new AvataxOrderConfirmedPayloadService(this.authData);
|
||||
const target = await payloadService.getPayload(payload.order, this.config);
|
||||
|
||||
this.logger.debug("Calling Avatax createTransaction with transformed payload...");
|
||||
this.logger.debug("Calling AvaTax createTransaction with transformed payload...");
|
||||
|
||||
const client = new AvataxClient(this.config);
|
||||
const response = await client.createTransaction(target);
|
||||
|
||||
this.logger.debug("Avatax createTransaction successfully responded");
|
||||
this.logger.debug("AvaTax createTransaction successfully responded");
|
||||
|
||||
const responseTransformer = new AvataxOrderConfirmedResponseTransformer();
|
||||
const transformedResponse = responseTransformer.transform(response);
|
||||
|
||||
this.logger.debug("Transformed Avatax createTransaction response");
|
||||
this.logger.debug("Transformed AvaTax createTransaction response");
|
||||
|
||||
return transformedResponse;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ export class AvataxOrderConfirmedPayloadLinesTransformer {
|
|||
});
|
||||
|
||||
if (order.shippingPrice.net.amount !== 0) {
|
||||
// * In Avatax, shipping is a regular line
|
||||
// * In AvaTax, shipping is a regular line
|
||||
const shippingLine: LineItemModel = {
|
||||
amount: order.shippingPrice.gross.amount,
|
||||
taxIncluded: true,
|
||||
|
|
|
@ -48,7 +48,7 @@ export class AvataxOrderConfirmedPayloadTransformer {
|
|||
entityUseCode,
|
||||
customerCode:
|
||||
order.user?.id ??
|
||||
"" /* In Saleor Avatax plugin, the customer code is 0. In Taxes App, we set it to the 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,
|
||||
// * 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,
|
||||
|
|
|
@ -8,7 +8,7 @@ export class AvataxOrderConfirmedResponseTransformer {
|
|||
id: taxProviderUtils.resolveOptionalOrThrow(
|
||||
response.code,
|
||||
new Error(
|
||||
"Could not update the order metadata with Avatax transaction code because it was not returned from the createTransaction mutation."
|
||||
"Could not update the order metadata with AvaTax transaction code because it was not returned from the createTransaction mutation."
|
||||
)
|
||||
),
|
||||
};
|
||||
|
|
|
@ -24,22 +24,22 @@ export class AvataxOrderCreatedAdapter
|
|||
}
|
||||
|
||||
async send(payload: AvataxOrderCreatedPayload): Promise<AvataxOrderCreatedResponse> {
|
||||
this.logger.debug("Transforming the Saleor payload for creating order with Avatax...");
|
||||
this.logger.debug("Transforming the Saleor payload for creating order with AvaTax...");
|
||||
|
||||
const payloadService = new AvataxOrderCreatedPayloadService(this.authData);
|
||||
const target = await payloadService.getPayload(payload.order, this.config);
|
||||
|
||||
this.logger.debug("Calling Avatax createTransaction with transformed payload...");
|
||||
this.logger.debug("Calling AvaTax createTransaction with transformed payload...");
|
||||
|
||||
const client = new AvataxClient(this.config);
|
||||
const response = await client.createTransaction(target);
|
||||
|
||||
this.logger.debug("Avatax createTransaction successfully responded");
|
||||
this.logger.debug("AvaTax createTransaction successfully responded");
|
||||
|
||||
const responseTransformer = new AvataxOrderCreatedResponseTransformer();
|
||||
const transformedResponse = responseTransformer.transform(response);
|
||||
|
||||
this.logger.debug("Transformed Avatax createTransaction response");
|
||||
this.logger.debug("Transformed AvaTax createTransaction response");
|
||||
|
||||
return transformedResponse;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ export class AvataxOrderCreatedPayloadLinesTransformer {
|
|||
});
|
||||
|
||||
if (order.shippingPrice.net.amount !== 0) {
|
||||
// * In Avatax, shipping is a regular line
|
||||
// * In AvaTax, shipping is a regular line
|
||||
const shippingLine: LineItemModel = {
|
||||
amount: order.shippingPrice.gross.amount,
|
||||
taxIncluded: true,
|
||||
|
|
|
@ -48,7 +48,7 @@ export class AvataxOrderCreatedPayloadTransformer {
|
|||
code,
|
||||
customerCode:
|
||||
order.user?.id ??
|
||||
"" /* In Saleor Avatax plugin, the customer code is 0. In Taxes App, we set it to the user id. */,
|
||||
"" /* In Saleor AvaTax plugin, the customer code is 0. In Taxes App, we set it to the 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
|
||||
commit: avataxConfig.isAutocommit,
|
||||
|
|
|
@ -8,7 +8,7 @@ export class AvataxOrderCreatedResponseTransformer {
|
|||
id: taxProviderUtils.resolveOptionalOrThrow(
|
||||
response.code,
|
||||
new Error(
|
||||
"Could not update the order metadata with Avatax transaction code because it was not returned from the createTransaction mutation."
|
||||
"Could not update the order metadata with AvaTax transaction code because it was not returned from the createTransaction mutation."
|
||||
)
|
||||
),
|
||||
};
|
||||
|
|
|
@ -22,22 +22,22 @@ export class AvataxOrderFulfilledAdapter
|
|||
}
|
||||
|
||||
async send(payload: AvataxOrderFulfilledPayload): Promise<AvataxOrderFulfilledResponse> {
|
||||
this.logger.debug("Transforming the Saleor payload for commiting transaction with Avatax...");
|
||||
this.logger.debug("Transforming the Saleor payload for commiting transaction with AvaTax...");
|
||||
|
||||
const payloadTransformer = new AvataxOrderFulfilledPayloadTransformer(this.config);
|
||||
const target = payloadTransformer.transform({ ...payload });
|
||||
|
||||
this.logger.debug("Calling Avatax commitTransaction with transformed payload...");
|
||||
this.logger.debug("Calling AvaTax commitTransaction with transformed payload...");
|
||||
|
||||
const client = new AvataxClient(this.config);
|
||||
const response = await client.commitTransaction(target);
|
||||
|
||||
this.logger.debug("Avatax commitTransaction succesfully responded");
|
||||
this.logger.debug("AvaTax commitTransaction succesfully responded");
|
||||
|
||||
const responseTransformer = new AvataxOrderFulfilledResponseTransformer();
|
||||
const transformedResponse = responseTransformer.transform(response);
|
||||
|
||||
this.logger.debug("Transformed Avatax commitTransaction response");
|
||||
this.logger.debug("Transformed AvaTax commitTransaction response");
|
||||
|
||||
return transformedResponse;
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ const MOCK_AVATAX_CONFIG: AvataxConfig = {
|
|||
isDocumentRecordingEnabled: true,
|
||||
isAutocommit: false,
|
||||
isSandbox: true,
|
||||
name: "Avatax-1",
|
||||
name: "AvaTax-1",
|
||||
shippingTaxCode: "FR000000",
|
||||
address: {
|
||||
country: "US",
|
||||
|
|
|
@ -55,7 +55,6 @@ export const AvataxConfigurationCredentialsFragment = (
|
|||
return (
|
||||
<>
|
||||
<FormSection title="Credentials">
|
||||
<Box paddingY={4} display={"flex"} flexDirection={"column"} gap={10}>
|
||||
<div>
|
||||
<Input
|
||||
control={control}
|
||||
|
@ -65,8 +64,8 @@ export const AvataxConfigurationCredentialsFragment = (
|
|||
helperText={formState.errors.credentials?.username?.message}
|
||||
/>
|
||||
<HelperText>
|
||||
You can obtain it in the <i>API Keys</i> section of <i>Settings</i> → <i>License</i>{" "}
|
||||
in your Avalara Dashboard.
|
||||
You can obtain it in the <i>API Keys</i> section of <i>Settings</i> → <i>License</i> in
|
||||
your Avalara Dashboard.
|
||||
</HelperText>
|
||||
</div>
|
||||
<div>
|
||||
|
@ -79,20 +78,19 @@ export const AvataxConfigurationCredentialsFragment = (
|
|||
helperText={formState.errors.credentials?.password?.message}
|
||||
/>
|
||||
<HelperText>
|
||||
You can obtain it in the <i>API Keys</i> section of <i>Settings</i> → <i>License</i>{" "}
|
||||
in your Avalara Dashboard.
|
||||
You can obtain it in the <i>API Keys</i> section of <i>Settings</i> → <i>License</i> in
|
||||
your Avalara Dashboard.
|
||||
</HelperText>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
control={control}
|
||||
name="companyCode"
|
||||
label="Company name"
|
||||
label="Company code"
|
||||
helperText={formState.errors.companyCode?.message}
|
||||
/>
|
||||
<HelperText>
|
||||
When not provided, the default company will be used.{" "}
|
||||
When not provided, the default company code will be used.{" "}
|
||||
<TextLink
|
||||
newTab
|
||||
href="https://developer.avalara.com/erp-integration-guide/sales-tax-badge/transactions/simple-transactions/company-codes/"
|
||||
|
@ -102,60 +100,23 @@ export const AvataxConfigurationCredentialsFragment = (
|
|||
about company codes.
|
||||
</HelperText>
|
||||
</div>
|
||||
</Box>
|
||||
<Box paddingY={4} display={"flex"} flexDirection={"column"} gap={10}>
|
||||
<AppToggle
|
||||
control={control}
|
||||
label="Use sandbox mode"
|
||||
helperText={
|
||||
<HelperText>
|
||||
Toggling between{" "}
|
||||
Choose between
|
||||
<TextLink
|
||||
href="https://developer.avalara.com/erp-integration-guide/sales-tax-badge/authentication-in-avatax/sandbox-vs-production/"
|
||||
newTab
|
||||
>
|
||||
<q>Production</q> and <q>Sandbox</q>
|
||||
</TextLink>{" "}
|
||||
environment.
|
||||
environment according to your credentials.
|
||||
</HelperText>
|
||||
}
|
||||
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"
|
||||
helperText={
|
||||
<HelperText>
|
||||
If enabled, the order will be automatically{" "}
|
||||
<TextLink
|
||||
href="https://developer.avalara.com/communications/dev-guide_rest_v2/commit-uncommit/"
|
||||
newTab
|
||||
>
|
||||
commited to Avalara.
|
||||
</TextLink>{" "}
|
||||
</HelperText>
|
||||
}
|
||||
name="isAutocommit"
|
||||
/>
|
||||
</Box>
|
||||
</FormSection>
|
||||
<Box display="flex" justifyContent={"flex-end"}>
|
||||
<Button variant="secondary" onClick={verifyCredentials}>
|
||||
|
|
|
@ -14,9 +14,9 @@ import {
|
|||
} from "../avatax-connection-schema";
|
||||
import { AvataxConfigurationAddressFragment } from "./avatax-configuration-address-fragment";
|
||||
import { AvataxConfigurationCredentialsFragment } from "./avatax-configuration-credentials-fragment";
|
||||
import { AvataxConfigurationTaxesFragment } from "./avatax-configuration-taxes-fragment";
|
||||
import { HelperText } from "./form-helper-text";
|
||||
import { AvataxConfigurationSettingsFragment } from "./avatax-configuration-settings-fragment";
|
||||
import { useAvataxConfigurationStatus } from "./configuration-status";
|
||||
import { HelperText } from "./form-helper-text";
|
||||
|
||||
type AvataxConfigurationFormProps = {
|
||||
submit: {
|
||||
|
@ -76,13 +76,13 @@ export const AvataxConfigurationForm = (props: AvataxConfigurationFormProps) =>
|
|||
isLoading={props.validateCredentials.isLoading}
|
||||
/>
|
||||
<Divider marginY={8} />
|
||||
<AvataxConfigurationSettingsFragment />
|
||||
<Divider marginY={8} />
|
||||
<AvataxConfigurationAddressFragment
|
||||
onValidateAddress={props.validateAddress.handleFn}
|
||||
isLoading={props.validateAddress.isLoading}
|
||||
/>
|
||||
<Divider marginY={8} />
|
||||
<AvataxConfigurationTaxesFragment />
|
||||
<Divider marginY={8} />
|
||||
|
||||
<Box display={"flex"} justifyContent={"space-between"} alignItems={"center"}>
|
||||
{props.leftButton}
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
import { TextLink } from "@saleor/apps-ui";
|
||||
import { Input } from "@saleor/react-hook-form-macaw";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
import { AppToggle } from "../../ui/app-toggle";
|
||||
import { AvataxConfig } from "../avatax-connection-schema";
|
||||
import { useAvataxConfigurationStatus } from "./configuration-status";
|
||||
import { HelperText } from "./form-helper-text";
|
||||
import { FormSection } from "./form-section";
|
||||
|
||||
export const AvataxConfigurationSettingsFragment = () => {
|
||||
const { control, formState } = useFormContext<AvataxConfig>();
|
||||
const { status } = useAvataxConfigurationStatus();
|
||||
|
||||
const disabled = status === "not_authenticated";
|
||||
|
||||
return (
|
||||
<FormSection title="Settings" disabled={disabled}>
|
||||
<AppToggle
|
||||
control={control}
|
||||
label="Document recording"
|
||||
disabled={disabled}
|
||||
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"
|
||||
disabled={disabled}
|
||||
helperText={
|
||||
<HelperText>
|
||||
If enabled, the order will be automatically{" "}
|
||||
<TextLink
|
||||
href="https://developer.avalara.com/communications/dev-guide_rest_v2/commit-uncommit/"
|
||||
newTab
|
||||
>
|
||||
commited to Avalara.
|
||||
</TextLink>{" "}
|
||||
</HelperText>
|
||||
}
|
||||
name="isAutocommit"
|
||||
/>
|
||||
<div>
|
||||
<Input
|
||||
disabled={disabled}
|
||||
control={control}
|
||||
name="shippingTaxCode"
|
||||
label="Shipping tax code"
|
||||
helperText={formState.errors.shippingTaxCode?.message}
|
||||
/>
|
||||
<HelperText disabled={disabled}>
|
||||
Tax code that for the shipping line sent to AvaTax.{" "}
|
||||
<TextLink newTab href="https://taxcode.avatax.avalara.com">
|
||||
Must match AvaTax tax codes format.
|
||||
</TextLink>
|
||||
</HelperText>
|
||||
</div>
|
||||
</FormSection>
|
||||
);
|
||||
};
|
|
@ -1,34 +0,0 @@
|
|||
import { TextLink } from "@saleor/apps-ui";
|
||||
import { Input } from "@saleor/react-hook-form-macaw";
|
||||
import React from "react";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
import { AvataxConfig } from "../avatax-connection-schema";
|
||||
import { HelperText } from "./form-helper-text";
|
||||
import { FormSection } from "./form-section";
|
||||
import { useAvataxConfigurationStatus } from "./configuration-status";
|
||||
|
||||
export const AvataxConfigurationTaxesFragment = () => {
|
||||
const { control, formState } = useFormContext<AvataxConfig>();
|
||||
const { status } = useAvataxConfigurationStatus();
|
||||
const disabled = status === "not_authenticated";
|
||||
|
||||
return (
|
||||
<FormSection title="Tax codes" disabled={disabled}>
|
||||
<div>
|
||||
<Input
|
||||
disabled={disabled}
|
||||
control={control}
|
||||
name="shippingTaxCode"
|
||||
label="Shipping tax code"
|
||||
helperText={formState.errors.shippingTaxCode?.message}
|
||||
/>
|
||||
<HelperText disabled={disabled}>
|
||||
Tax code that for the shipping line sent to Avatax.{" "}
|
||||
<TextLink newTab href="https://taxcode.avatax.avalara.com">
|
||||
Must match Avatax tax codes format.
|
||||
</TextLink>
|
||||
</HelperText>
|
||||
</div>
|
||||
</FormSection>
|
||||
);
|
||||
};
|
|
@ -5,7 +5,7 @@ import { Section } from "../../ui/app-section";
|
|||
export const AvataxInstructions = () => {
|
||||
return (
|
||||
<Section.Description
|
||||
title="Avatax Configuration"
|
||||
title="AvaTax Configuration"
|
||||
description={
|
||||
<>
|
||||
<Text as="p" marginBottom={8}>
|
||||
|
@ -49,7 +49,7 @@ export const AvataxInstructions = () => {
|
|||
</Text>
|
||||
<Text as="p" marginBottom={4}>
|
||||
Verifying the Address will display suggestions that reflect the resolution of the
|
||||
address by Avatax address validation service. Applying the suggestions is not required
|
||||
address by AvaTax address validation service. Applying the suggestions is not required
|
||||
but recommended. If the address is not valid, the calculation of taxes will fail.
|
||||
</Text>
|
||||
</>
|
||||
|
|
|
@ -31,7 +31,7 @@ const useGetTaxCodes = () => {
|
|||
|
||||
React.useEffect(() => {
|
||||
if (result.error) {
|
||||
notifyError("Error", "Unable to fetch Avatax tax codes.");
|
||||
notifyError("Error", "Unable to fetch AvaTax tax codes.");
|
||||
setTimeout(() => {
|
||||
router.push("/configuration");
|
||||
}, 1000);
|
||||
|
@ -61,7 +61,7 @@ const SelectTaxCode = ({ taxClassId }: { taxClassId: string }) => {
|
|||
|
||||
const { mutate: updateMutation } = trpcClient.avataxMatches.upsert.useMutation({
|
||||
onSuccess() {
|
||||
notifySuccess("Success", "Updated Avatax tax code matches");
|
||||
notifySuccess("Success", "Updated AvaTax tax code matches");
|
||||
},
|
||||
onError(error) {
|
||||
notifyError("Error", error.message);
|
||||
|
@ -113,7 +113,7 @@ export const AvataxTaxCodeMatcherTable = () => {
|
|||
<Table.THead>
|
||||
<Table.TR>
|
||||
<Table.TH>Saleor tax class</Table.TH>
|
||||
<Table.TH>Avatax tax code</Table.TH>
|
||||
<Table.TH>AvaTax tax code</Table.TH>
|
||||
</Table.TR>
|
||||
</Table.THead>
|
||||
<Table.TBody>
|
||||
|
|
|
@ -31,7 +31,7 @@ export const EditAvataxConfiguration = () => {
|
|||
const { mutate: patchMutation, isLoading: isPatchLoading } =
|
||||
trpcClient.avataxConnection.update.useMutation({
|
||||
onSuccess() {
|
||||
notifySuccess("Success", "Updated Avatax configuration");
|
||||
notifySuccess("Success", "Updated AvaTax configuration");
|
||||
refetchProvidersConfigurationData();
|
||||
},
|
||||
onError(error) {
|
||||
|
@ -42,7 +42,7 @@ export const EditAvataxConfiguration = () => {
|
|||
const { mutate: deleteMutation, isLoading: isDeleteLoading } =
|
||||
trpcClient.avataxConnection.delete.useMutation({
|
||||
onSuccess() {
|
||||
notifySuccess("Success", "Deleted Avatax configuration");
|
||||
notifySuccess("Success", "Deleted AvaTax configuration");
|
||||
refetchProvidersConfigurationData();
|
||||
router.push("/configuration");
|
||||
},
|
||||
|
|
5
apps/taxes/src/modules/avatax/ui/form-section.module.css
Normal file
5
apps/taxes/src/modules/avatax/ui/form-section.module.css
Normal file
|
@ -0,0 +1,5 @@
|
|||
/* when formSection is disabled, change the color of the a element: */
|
||||
.formSection[disabled] a,
|
||||
.formSection[disabled] a span {
|
||||
color: inherit;
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
import { Box, Text } from "@saleor/macaw-ui/next";
|
||||
import React, { PropsWithChildren } from "react";
|
||||
import styles from "./form-section.module.css";
|
||||
|
||||
export const FormSection = ({
|
||||
title,
|
||||
|
@ -8,7 +9,14 @@ export const FormSection = ({
|
|||
disabled = false,
|
||||
}: PropsWithChildren<{ title: string; subtitle?: string; disabled?: boolean }>) => {
|
||||
return (
|
||||
<>
|
||||
<Box
|
||||
as={"fieldset"}
|
||||
className={styles.formSection}
|
||||
disabled={disabled}
|
||||
__borderWidth={0}
|
||||
padding={0}
|
||||
margin={0}
|
||||
>
|
||||
<Text
|
||||
marginBottom={4}
|
||||
as="h3"
|
||||
|
@ -25,6 +33,6 @@ export const FormSection = ({
|
|||
<Box display="grid" gridTemplateColumns={2} gap={12}>
|
||||
{children}
|
||||
</Box>
|
||||
</>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -35,7 +35,7 @@ class ActiveTaxProviderService implements ProviderWebhookService {
|
|||
}
|
||||
|
||||
case "avatax": {
|
||||
this.logger.debug("Selecting Avatax as tax provider");
|
||||
this.logger.debug("Selecting AvaTax as tax provider");
|
||||
this.client = new AvataxWebhookService(providerConnection.config, this.authData);
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -54,14 +54,14 @@ const breadcrumbsForRoute: Record<string, Breadcrumb[]> = {
|
|||
"/providers/avatax": [
|
||||
...newProviderBreadcrumbs,
|
||||
{
|
||||
label: "Avatax",
|
||||
label: "AvaTax",
|
||||
href: "/providers/avatax",
|
||||
},
|
||||
],
|
||||
"/providers/avatax/matcher": [
|
||||
...newProviderBreadcrumbs,
|
||||
{
|
||||
label: "Avatax",
|
||||
label: "AvaTax",
|
||||
href: "/providers/avatax",
|
||||
},
|
||||
{
|
||||
|
@ -72,7 +72,7 @@ const breadcrumbsForRoute: Record<string, Breadcrumb[]> = {
|
|||
"/providers/avatax/[id]": [
|
||||
...newProviderBreadcrumbs,
|
||||
{
|
||||
label: "Editing Avatax provider",
|
||||
label: "Editing AvaTax provider",
|
||||
href: "/providers/avatax",
|
||||
},
|
||||
],
|
||||
|
|
|
@ -11,7 +11,7 @@ const providerConfig = {
|
|||
icon: TaxJarIcon,
|
||||
},
|
||||
avatax: {
|
||||
label: "Avatax",
|
||||
label: "AvaTax",
|
||||
icon: AvataxIcon,
|
||||
},
|
||||
stripeTax: {
|
||||
|
|
|
@ -5,7 +5,7 @@ import { AppColumns } from "../../../modules/ui/app-columns";
|
|||
import { Section } from "../../../modules/ui/app-section";
|
||||
|
||||
const Header = () => {
|
||||
return <Section.Header>Edit your existing Avatax configuration</Section.Header>;
|
||||
return <Section.Header>Edit your existing AvaTax configuration</Section.Header>;
|
||||
};
|
||||
|
||||
const EditAvataxPage = () => {
|
||||
|
|
|
@ -8,7 +8,7 @@ const Header = () => {
|
|||
return (
|
||||
<Box>
|
||||
<Text as="p" variant="body">
|
||||
Create new Avatax configuration
|
||||
Create new AvaTax configuration
|
||||
</Text>
|
||||
</Box>
|
||||
);
|
||||
|
|
|
@ -9,21 +9,21 @@ import { useRouter } from "next/router";
|
|||
import { trpcClient } from "../../../modules/trpc/trpc-client";
|
||||
|
||||
const Header = () => {
|
||||
return <Section.Header>Match Saleor tax classes to Avatax tax codes</Section.Header>;
|
||||
return <Section.Header>Match Saleor tax classes to AvaTax tax codes</Section.Header>;
|
||||
};
|
||||
|
||||
const Description = () => {
|
||||
return (
|
||||
<Section.Description
|
||||
title="Avatax tax code matcher"
|
||||
title="AvaTax tax code matcher"
|
||||
description={
|
||||
<>
|
||||
<Text display="block" as="span" marginBottom={4}>
|
||||
To extend the base tax rate of your products, you can map Saleor tax classes to Avatax
|
||||
To extend the base tax rate of your products, you can map Saleor tax classes to AvaTax
|
||||
tax codes.
|
||||
</Text>
|
||||
<Text display="block" as="span" marginBottom={4}>
|
||||
This way, the product's Saleor tax class will be used to determine the Avatax tax
|
||||
This way, the product's Saleor tax class will be used to determine the AvaTax tax
|
||||
code needed to calculate the tax rate.
|
||||
</Text>
|
||||
<Text as="p" marginBottom={4}>
|
||||
|
@ -37,9 +37,9 @@ const Description = () => {
|
|||
view.
|
||||
</Text>
|
||||
<Text as="p" marginBottom={4}>
|
||||
To learn more about Avatax tax codes, please visit{" "}
|
||||
To learn more about AvaTax tax codes, please visit{" "}
|
||||
<TextLink href="https://taxcode.avatax.avalara.com/search?q=OF400000" newTab>
|
||||
Avatax documentation
|
||||
AvaTax documentation
|
||||
</TextLink>
|
||||
.
|
||||
</Text>
|
||||
|
@ -55,7 +55,7 @@ const AvataxMatcher = () => {
|
|||
|
||||
const { isLoading } = trpcClient.avataxConnection.verifyConnections.useQuery(undefined, {
|
||||
onError: () => {
|
||||
notifyError("Error", "You must configure Avatax first.");
|
||||
notifyError("Error", "You must configure AvaTax first.");
|
||||
router.push("/configuration");
|
||||
},
|
||||
});
|
||||
|
|
|
@ -38,7 +38,7 @@ const providerConfig = {
|
|||
avatax: {
|
||||
description: (
|
||||
<p>
|
||||
Avatax is a comprehensive tax automation software service that helps businesses calculate
|
||||
AvaTax is a comprehensive tax automation software service that helps businesses calculate
|
||||
and manage sales tax accurately and efficiently.
|
||||
</p>
|
||||
),
|
||||
|
|
Loading…
Reference in a new issue