diff --git a/.changeset/healthy-mayflies-hang.md b/.changeset/healthy-mayflies-hang.md
new file mode 100644
index 0000000..b779c40
--- /dev/null
+++ b/.changeset/healthy-mayflies-hang.md
@@ -0,0 +1,5 @@
+---
+"saleor-app-taxes": minor
+---
+
+Add `` and use it in the `channel-tax-provider-form.ts` to ensure the correct formatting of the country code.
diff --git a/apps/taxes/src/modules/avatax/avatax-client.ts b/apps/taxes/src/modules/avatax/avatax-client.ts
index d0ea3ff..21a7bd4 100644
--- a/apps/taxes/src/modules/avatax/avatax-client.ts
+++ b/apps/taxes/src/modules/avatax/avatax-client.ts
@@ -6,6 +6,7 @@ import { createLogger } from "../../lib/logger";
import { AvataxConfig } from "./avatax-config";
import { CommitTransactionModel } from "avatax/lib/models/CommitTransactionModel";
import { DocumentType } from "avatax/lib/enums/DocumentType";
+import { AddressLocationInfo as AvataxAddress } from "avatax/lib/models/AddressLocationInfo";
type AvataxSettings = {
appName: string;
@@ -48,6 +49,10 @@ export type CreateTransactionArgs = {
model: CreateTransactionModel;
};
+export type ValidateAddressArgs = {
+ address: AvataxAddress;
+};
+
export class AvataxClient {
private client: Avatax;
private logger: pino.Logger;
@@ -97,4 +102,10 @@ export class AvataxClient {
};
}
}
+
+ async validateAddress({ address }: ValidateAddressArgs) {
+ this.logger.debug({ address }, "validateAddress called with:");
+
+ return this.client.resolveAddress(address);
+ }
}
diff --git a/apps/taxes/src/modules/avatax/maps/address-factory.ts b/apps/taxes/src/modules/avatax/maps/address-factory.ts
index d1275e0..2a55cd5 100644
--- a/apps/taxes/src/modules/avatax/maps/address-factory.ts
+++ b/apps/taxes/src/modules/avatax/maps/address-factory.ts
@@ -1,8 +1,6 @@
import { AddressLocationInfo as AvataxAddress } from "avatax/lib/models/AddressLocationInfo";
import { ChannelAddress } from "../../channels-configuration/channels-config";
-import { AddressFragment } from "../../../../generated/graphql";
-
-type SaleorAddress = AddressFragment;
+import { AddressFragment as SaleorAddress } from "../../../../generated/graphql";
function mapSaleorAddressToAvataxAddress(address: SaleorAddress): AvataxAddress {
return {
diff --git a/apps/taxes/src/modules/channels/ui/channel-tax-provider-form.tsx b/apps/taxes/src/modules/channels/ui/channel-tax-provider-form.tsx
index 2e00915..8910945 100644
--- a/apps/taxes/src/modules/channels/ui/channel-tax-provider-form.tsx
+++ b/apps/taxes/src/modules/channels/ui/channel-tax-provider-form.tsx
@@ -13,6 +13,7 @@ import {
import { Save } from "@material-ui/icons";
import { Button, makeStyles } from "@saleor/macaw-ui";
+import { useDashboardNotification } from "@saleor/apps-shared";
import React from "react";
import { Controller, useForm } from "react-hook-form";
import {
@@ -24,7 +25,7 @@ import { ProvidersConfig } from "../../providers-configuration/providers-config"
import { ProviderIcon } from "../../providers-configuration/ui/provider-icon";
import { useChannelSlug } from "../../taxes/tax-context";
import { trpcClient } from "../../trpc/trpc-client";
-import { useDashboardNotification } from "@saleor/apps-shared";
+import { CountrySelect } from "../../ui/country-select/country-select";
type ChannelTaxProviderFormValues = ChannelConfig;
@@ -67,7 +68,6 @@ const getDefaultFormValues = (
return defaultChannelConfig;
};
-// todo: rename because address is here
export const ChannelTaxProviderForm = () => {
const styles = useStyles();
const { control, reset, handleSubmit } = useForm({
@@ -158,14 +158,10 @@ export const ChannelTaxProviderForm = () => {
- {/* // todo: add country select */}
(
-
- )}
+ render={({ field }) => }
/>
diff --git a/apps/taxes/src/modules/taxjar/maps/address-factory.test.ts b/apps/taxes/src/modules/taxjar/maps/address-factory.test.ts
new file mode 100644
index 0000000..4f02500
--- /dev/null
+++ b/apps/taxes/src/modules/taxjar/maps/address-factory.test.ts
@@ -0,0 +1,68 @@
+import { describe, expect, it } from "vitest";
+import { taxJarAddressFactory } from "./address-factory";
+
+describe("taxJarAddressFactory", () => {
+ describe("fromChannelAddress", () => {
+ it("returns fields in the expected format", () => {
+ const result = taxJarAddressFactory.fromChannelAddress({
+ city: "LOS ANGELES",
+ country: "US",
+ state: "CA",
+ street: "123 Palm Grove Ln",
+ zip: "90002",
+ });
+
+ expect(result).toEqual({
+ street: "123 Palm Grove Ln",
+ city: "LOS ANGELES",
+ state: "CA",
+ zip: "90002",
+ country: "US",
+ });
+ });
+ });
+
+ describe("fromSaleorAddress", () => {
+ it("returns fields in the expected format with streetAddress1", () => {
+ const result = taxJarAddressFactory.fromSaleorAddress({
+ streetAddress1: "123 Palm Grove Ln",
+ streetAddress2: "",
+ city: "LOS ANGELES",
+ country: {
+ code: "US",
+ },
+ countryArea: "CA",
+ postalCode: "90002",
+ });
+
+ expect(result).toEqual({
+ street: "123 Palm Grove Ln",
+ city: "LOS ANGELES",
+ state: "CA",
+ zip: "90002",
+ country: "US",
+ });
+ });
+
+ it("returns fields in the expected format with streetAddress1 and streetAddress2", () => {
+ const result = taxJarAddressFactory.fromSaleorAddress({
+ streetAddress1: "123 Palm",
+ streetAddress2: "Grove Ln",
+ city: "LOS ANGELES",
+ country: {
+ code: "US",
+ },
+ countryArea: "CA",
+ postalCode: "90002",
+ });
+
+ expect(result).toEqual({
+ street: "123 Palm Grove Ln",
+ city: "LOS ANGELES",
+ state: "CA",
+ zip: "90002",
+ country: "US",
+ });
+ });
+ });
+});
diff --git a/apps/taxes/src/modules/taxjar/maps/address-factory.ts b/apps/taxes/src/modules/taxjar/maps/address-factory.ts
new file mode 100644
index 0000000..bdf3f59
--- /dev/null
+++ b/apps/taxes/src/modules/taxjar/maps/address-factory.ts
@@ -0,0 +1,32 @@
+import { ChannelAddress } from "../../channels-configuration/channels-config";
+import { AddressFragment as SaleorAddress } from "../../../../generated/graphql";
+import { AddressParams as TaxJarAddress } from "taxjar/dist/types/paramTypes";
+
+function joinAddresses(address1: string, address2: string): string {
+ return `${address1}${address2.length > 0 ? " " + address2 : ""}`;
+}
+
+function mapSaleorAddressToTaxJarAddress(address: SaleorAddress): TaxJarAddress {
+ return {
+ street: joinAddresses(address.streetAddress1, address.streetAddress2),
+ city: address.city,
+ zip: address.postalCode,
+ state: address.countryArea,
+ country: address.country.code,
+ };
+}
+
+function mapChannelAddressToTaxJarAddress(address: ChannelAddress): TaxJarAddress {
+ return {
+ city: address.city,
+ country: address.country,
+ state: address.state,
+ street: address.street,
+ zip: address.zip,
+ };
+}
+
+export const taxJarAddressFactory = {
+ fromSaleorAddress: mapSaleorAddressToTaxJarAddress,
+ fromChannelAddress: mapChannelAddressToTaxJarAddress,
+};
diff --git a/apps/taxes/src/modules/taxjar/taxjar-client.ts b/apps/taxes/src/modules/taxjar/taxjar-client.ts
index d4d3812..619679f 100644
--- a/apps/taxes/src/modules/taxjar/taxjar-client.ts
+++ b/apps/taxes/src/modules/taxjar/taxjar-client.ts
@@ -1,6 +1,6 @@
import pino from "pino";
import TaxJar from "taxjar";
-import { Config, CreateOrderParams, TaxParams } from "taxjar/dist/util/types";
+import { AddressParams, Config, CreateOrderParams, TaxParams } from "taxjar/dist/util/types";
import { createLogger } from "../../lib/logger";
import { TaxJarConfig } from "./taxjar-config";
@@ -21,6 +21,10 @@ export type CreateOrderArgs = {
params: CreateOrderParams;
};
+export type ValidateAddressArgs = {
+ params: AddressParams;
+};
+
export class TaxJarClient {
private client: TaxJar;
private logger: pino.Logger;
@@ -56,8 +60,14 @@ export class TaxJarClient {
}
async createOrder({ params }: CreateOrderArgs) {
- this.logger.debug("createOrder called with:");
+ this.logger.debug({ params }, "createOrder called with:");
return this.client.createOrder(params);
}
+
+ async validateAddress({ params }: ValidateAddressArgs) {
+ this.logger.debug({ params }, "validateAddress called with:");
+
+ return this.client.validateAddress(params);
+ }
}
diff --git a/apps/taxes/src/modules/ui/country-select/countries.ts b/apps/taxes/src/modules/ui/country-select/countries.ts
new file mode 100644
index 0000000..cae7a53
--- /dev/null
+++ b/apps/taxes/src/modules/ui/country-select/countries.ts
@@ -0,0 +1,382 @@
+type CountryType = {
+ code: string;
+ label: string;
+};
+
+// From https://bitbucket.org/atlassian/atlaskit-mk-2/raw/4ad0e56649c3e6c973e226b7efaeb28cb240ccb0/packages/core/select/src/data/countries.js
+export const countries: CountryType[] = [
+ { code: "AD", label: "Andorra" },
+ {
+ code: "AE",
+ label: "United Arab Emirates",
+ },
+ { code: "AF", label: "Afghanistan" },
+ {
+ code: "AG",
+ label: "Antigua and Barbuda",
+ },
+ { code: "AI", label: "Anguilla" },
+ { code: "AL", label: "Albania" },
+ { code: "AM", label: "Armenia" },
+ { code: "AO", label: "Angola" },
+ { code: "AQ", label: "Antarctica" },
+ { code: "AR", label: "Argentina" },
+ { code: "AS", label: "American Samoa" },
+ { code: "AT", label: "Austria" },
+ {
+ code: "AU",
+ label: "Australia",
+ },
+ { code: "AW", label: "Aruba" },
+ { code: "AX", label: "Alland Islands" },
+ { code: "AZ", label: "Azerbaijan" },
+ {
+ code: "BA",
+ label: "Bosnia and Herzegovina",
+ },
+ { code: "BB", label: "Barbados" },
+ { code: "BD", label: "Bangladesh" },
+ { code: "BE", label: "Belgium" },
+ { code: "BF", label: "Burkina Faso" },
+ { code: "BG", label: "Bulgaria" },
+ { code: "BH", label: "Bahrain" },
+ { code: "BI", label: "Burundi" },
+ { code: "BJ", label: "Benin" },
+ { code: "BL", label: "Saint Barthelemy" },
+ { code: "BM", label: "Bermuda" },
+ { code: "BN", label: "Brunei Darussalam" },
+ { code: "BO", label: "Bolivia" },
+ { code: "BR", label: "Brazil" },
+ { code: "BS", label: "Bahamas" },
+ { code: "BT", label: "Bhutan" },
+ { code: "BV", label: "Bouvet Island" },
+ { code: "BW", label: "Botswana" },
+ { code: "BY", label: "Belarus" },
+ { code: "BZ", label: "Belize" },
+ {
+ code: "CA",
+ label: "Canada",
+ },
+ {
+ code: "CC",
+ label: "Cocos (Keeling) Islands",
+ },
+ {
+ code: "CD",
+ label: "Congo, Democratic Republic of the",
+ },
+ {
+ code: "CF",
+ label: "Central African Republic",
+ },
+ {
+ code: "CG",
+ label: "Congo, Republic of the",
+ },
+ { code: "CH", label: "Switzerland" },
+ { code: "CI", label: "Cote d'Ivoire" },
+ { code: "CK", label: "Cook Islands" },
+ { code: "CL", label: "Chile" },
+ { code: "CM", label: "Cameroon" },
+ { code: "CN", label: "China" },
+ { code: "CO", label: "Colombia" },
+ { code: "CR", label: "Costa Rica" },
+ { code: "CU", label: "Cuba" },
+ { code: "CV", label: "Cape Verde" },
+ { code: "CW", label: "Curacao" },
+ { code: "CX", label: "Christmas Island" },
+ { code: "CY", label: "Cyprus" },
+ { code: "CZ", label: "Czech Republic" },
+ {
+ code: "DE",
+ label: "Germany",
+ },
+ { code: "DJ", label: "Djibouti" },
+ { code: "DK", label: "Denmark" },
+ { code: "DM", label: "Dominica" },
+ {
+ code: "DO",
+ label: "Dominican Republic",
+ },
+ { code: "DZ", label: "Algeria" },
+ { code: "EC", label: "Ecuador" },
+ { code: "EE", label: "Estonia" },
+ { code: "EG", label: "Egypt" },
+ { code: "EH", label: "Western Sahara" },
+ { code: "ER", label: "Eritrea" },
+ { code: "ES", label: "Spain" },
+ { code: "ET", label: "Ethiopia" },
+ { code: "FI", label: "Finland" },
+ { code: "FJ", label: "Fiji" },
+ {
+ code: "FK",
+ label: "Falkland Islands (Malvinas)",
+ },
+ {
+ code: "FM",
+ label: "Micronesia, Federated States of",
+ },
+ { code: "FO", label: "Faroe Islands" },
+ {
+ code: "FR",
+ label: "France",
+ },
+ { code: "GA", label: "Gabon" },
+ { code: "GB", label: "United Kingdom" },
+ { code: "GD", label: "Grenada" },
+ { code: "GE", label: "Georgia" },
+ { code: "GF", label: "French Guiana" },
+ { code: "GG", label: "Guernsey" },
+ { code: "GH", label: "Ghana" },
+ { code: "GI", label: "Gibraltar" },
+ { code: "GL", label: "Greenland" },
+ { code: "GM", label: "Gambia" },
+ { code: "GN", label: "Guinea" },
+ { code: "GP", label: "Guadeloupe" },
+ { code: "GQ", label: "Equatorial Guinea" },
+ { code: "GR", label: "Greece" },
+ {
+ code: "GS",
+ label: "South Georgia and the South Sandwich Islands",
+ },
+ { code: "GT", label: "Guatemala" },
+ { code: "GU", label: "Guam" },
+ { code: "GW", label: "Guinea-Bissau" },
+ { code: "GY", label: "Guyana" },
+ { code: "HK", label: "Hong Kong" },
+ {
+ code: "HM",
+ label: "Heard Island and McDonald Islands",
+ },
+ { code: "HN", label: "Honduras" },
+ { code: "HR", label: "Croatia" },
+ { code: "HT", label: "Haiti" },
+ { code: "HU", label: "Hungary" },
+ { code: "ID", label: "Indonesia" },
+ { code: "IE", label: "Ireland" },
+ { code: "IL", label: "Israel" },
+ { code: "IM", label: "Isle of Man" },
+ { code: "IN", label: "India" },
+ {
+ code: "IO",
+ label: "British Indian Ocean Territory",
+ },
+ { code: "IQ", label: "Iraq" },
+ {
+ code: "IR",
+ label: "Iran, Islamic Republic of",
+ },
+ { code: "IS", label: "Iceland" },
+ { code: "IT", label: "Italy" },
+ { code: "JE", label: "Jersey" },
+ { code: "JM", label: "Jamaica" },
+ { code: "JO", label: "Jordan" },
+ {
+ code: "JP",
+ label: "Japan",
+ },
+ { code: "KE", label: "Kenya" },
+ { code: "KG", label: "Kyrgyzstan" },
+ { code: "KH", label: "Cambodia" },
+ { code: "KI", label: "Kiribati" },
+ { code: "KM", label: "Comoros" },
+ {
+ code: "KN",
+ label: "Saint Kitts and Nevis",
+ },
+ {
+ code: "KP",
+ label: "Korea, Democratic People's Republic of",
+ },
+ { code: "KR", label: "Korea, Republic of" },
+ { code: "KW", label: "Kuwait" },
+ { code: "KY", label: "Cayman Islands" },
+ { code: "KZ", label: "Kazakhstan" },
+ {
+ code: "LA",
+ label: "Lao People's Democratic Republic",
+ },
+ { code: "LB", label: "Lebanon" },
+ { code: "LC", label: "Saint Lucia" },
+ { code: "LI", label: "Liechtenstein" },
+ { code: "LK", label: "Sri Lanka" },
+ { code: "LR", label: "Liberia" },
+ { code: "LS", label: "Lesotho" },
+ { code: "LT", label: "Lithuania" },
+ { code: "LU", label: "Luxembourg" },
+ { code: "LV", label: "Latvia" },
+ { code: "LY", label: "Libya" },
+ { code: "MA", label: "Morocco" },
+ { code: "MC", label: "Monaco" },
+ {
+ code: "MD",
+ label: "Moldova, Republic of",
+ },
+ { code: "ME", label: "Montenegro" },
+ {
+ code: "MF",
+ label: "Saint Martin (French part)",
+ },
+ { code: "MG", label: "Madagascar" },
+ { code: "MH", label: "Marshall Islands" },
+ {
+ code: "MK",
+ label: "Macedonia, the Former Yugoslav Republic of",
+ },
+ { code: "ML", label: "Mali" },
+ { code: "MM", label: "Myanmar" },
+ { code: "MN", label: "Mongolia" },
+ { code: "MO", label: "Macao" },
+ {
+ code: "MP",
+ label: "Northern Mariana Islands",
+ },
+ { code: "MQ", label: "Martinique" },
+ { code: "MR", label: "Mauritania" },
+ { code: "MS", label: "Montserrat" },
+ { code: "MT", label: "Malta" },
+ { code: "MU", label: "Mauritius" },
+ { code: "MV", label: "Maldives" },
+ { code: "MW", label: "Malawi" },
+ { code: "MX", label: "Mexico" },
+ { code: "MY", label: "Malaysia" },
+ { code: "MZ", label: "Mozambique" },
+ { code: "NA", label: "Namibia" },
+ { code: "NC", label: "New Caledonia" },
+ { code: "NE", label: "Niger" },
+ { code: "NF", label: "Norfolk Island" },
+ { code: "NG", label: "Nigeria" },
+ { code: "NI", label: "Nicaragua" },
+ { code: "NL", label: "Netherlands" },
+ { code: "NO", label: "Norway" },
+ { code: "NP", label: "Nepal" },
+ { code: "NR", label: "Nauru" },
+ { code: "NU", label: "Niue" },
+ { code: "NZ", label: "New Zealand" },
+ { code: "OM", label: "Oman" },
+ { code: "PA", label: "Panama" },
+ { code: "PE", label: "Peru" },
+ { code: "PF", label: "French Polynesia" },
+ { code: "PG", label: "Papua New Guinea" },
+ { code: "PH", label: "Philippines" },
+ { code: "PK", label: "Pakistan" },
+ { code: "PL", label: "Poland" },
+ {
+ code: "PM",
+ label: "Saint Pierre and Miquelon",
+ },
+ { code: "PN", label: "Pitcairn" },
+ { code: "PR", label: "Puerto Rico" },
+ {
+ code: "PS",
+ label: "Palestine, State of",
+ },
+ { code: "PT", label: "Portugal" },
+ { code: "PW", label: "Palau" },
+ { code: "PY", label: "Paraguay" },
+ { code: "QA", label: "Qatar" },
+ { code: "RE", label: "Reunion" },
+ { code: "RO", label: "Romania" },
+ { code: "RS", label: "Serbia" },
+ { code: "RU", label: "Russian Federation" },
+ { code: "RW", label: "Rwanda" },
+ { code: "SA", label: "Saudi Arabia" },
+ { code: "SB", label: "Solomon Islands" },
+ { code: "SC", label: "Seychelles" },
+ { code: "SD", label: "Sudan" },
+ { code: "SE", label: "Sweden" },
+ { code: "SG", label: "Singapore" },
+ { code: "SH", label: "Saint Helena" },
+ { code: "SI", label: "Slovenia" },
+ {
+ code: "SJ",
+ label: "Svalbard and Jan Mayen",
+ },
+ { code: "SK", label: "Slovakia" },
+ { code: "SL", label: "Sierra Leone" },
+ { code: "SM", label: "San Marino" },
+ { code: "SN", label: "Senegal" },
+ { code: "SO", label: "Somalia" },
+ { code: "SR", label: "Suriname" },
+ { code: "SS", label: "South Sudan" },
+ {
+ code: "ST",
+ label: "Sao Tome and Principe",
+ },
+ { code: "SV", label: "El Salvador" },
+ {
+ code: "SX",
+ label: "Sint Maarten (Dutch part)",
+ },
+ {
+ code: "SY",
+ label: "Syrian Arab Republic",
+ },
+ { code: "SZ", label: "Swaziland" },
+ {
+ code: "TC",
+ label: "Turks and Caicos Islands",
+ },
+ { code: "TD", label: "Chad" },
+ {
+ code: "TF",
+ label: "French Southern Territories",
+ },
+ { code: "TG", label: "Togo" },
+ { code: "TH", label: "Thailand" },
+ { code: "TJ", label: "Tajikistan" },
+ { code: "TK", label: "Tokelau" },
+ { code: "TL", label: "Timor-Leste" },
+ { code: "TM", label: "Turkmenistan" },
+ { code: "TN", label: "Tunisia" },
+ { code: "TO", label: "Tonga" },
+ { code: "TR", label: "Turkey" },
+ {
+ code: "TT",
+ label: "Trinidad and Tobago",
+ },
+ { code: "TV", label: "Tuvalu" },
+ {
+ code: "TW",
+ label: "Taiwan, Province of China",
+ },
+ {
+ code: "TZ",
+ label: "United Republic of Tanzania",
+ },
+ { code: "UA", label: "Ukraine" },
+ { code: "UG", label: "Uganda" },
+ {
+ code: "US",
+ label: "United States",
+ },
+ { code: "UY", label: "Uruguay" },
+ { code: "UZ", label: "Uzbekistan" },
+ {
+ code: "VA",
+ label: "Holy See (Vatican City State)",
+ },
+ {
+ code: "VC",
+ label: "Saint Vincent and the Grenadines",
+ },
+ { code: "VE", label: "Venezuela" },
+ {
+ code: "VG",
+ label: "British Virgin Islands",
+ },
+ {
+ code: "VI",
+ label: "US Virgin Islands",
+ },
+ { code: "VN", label: "Vietnam" },
+ { code: "VU", label: "Vanuatu" },
+ { code: "WF", label: "Wallis and Futuna" },
+ { code: "WS", label: "Samoa" },
+ { code: "XK", label: "Kosovo" },
+ { code: "YE", label: "Yemen" },
+ { code: "YT", label: "Mayotte" },
+ { code: "ZA", label: "South Africa" },
+ { code: "ZM", label: "Zambia" },
+ { code: "ZW", label: "Zimbabwe" },
+];
diff --git a/apps/taxes/src/modules/ui/country-select/country-select.tsx b/apps/taxes/src/modules/ui/country-select/country-select.tsx
new file mode 100644
index 0000000..3c821a1
--- /dev/null
+++ b/apps/taxes/src/modules/ui/country-select/country-select.tsx
@@ -0,0 +1,43 @@
+import { TextField } from "@material-ui/core";
+import { Autocomplete } from "@material-ui/lab";
+import { makeStyles } from "@saleor/macaw-ui";
+import { ControllerRenderProps } from "react-hook-form";
+import { ChannelConfig } from "../../channels-configuration/channels-config";
+import { countries } from "./countries";
+import React from "react";
+
+type CountrySelectProps = ControllerRenderProps;
+
+// TODO: replace with macaw-ui component
+const useStyles = makeStyles({
+ root: {
+ padding: "7px 9px !important",
+ },
+ clearIndicator: {
+ marginRight: 2,
+ },
+});
+
+// eslint-disable-next-line react/display-name
+export const CountrySelect = React.forwardRef((p: CountrySelectProps, ref) => {
+ const styles = useStyles();
+ const { onChange, value } = p;
+
+ return (
+ onChange(data ? data.code : null)}
+ value={
+ countries.find((country) => {
+ return value === country.code;
+ }) ?? null
+ }
+ getOptionLabel={(option) => option.label}
+ renderInput={(params) => }
+ />
+ );
+});