diff --git a/apps/avatax/CHANGELOG.md b/apps/avatax/CHANGELOG.md index 1116dfece..0aa2d0c65 100644 --- a/apps/avatax/CHANGELOG.md +++ b/apps/avatax/CHANGELOG.md @@ -1,5 +1,11 @@ # app-avatax +## 1.10.4 + +### Patch Changes + +- f4250c1a: Fixed broken taxes resolving for billing address + ## 1.10.3 ### Patch Changes diff --git a/apps/avatax/e2e/data/maps/product.json b/apps/avatax/e2e/data/maps/product.json index 931e041a9..ed12268d0 100644 --- a/apps/avatax/e2e/data/maps/product.json +++ b/apps/avatax/e2e/data/maps/product.json @@ -14,6 +14,11 @@ "variantId": "UHJvZHVjdFZhcmlhbnQ6Mzkw", "id": "UHJvZHVjdDoxNTc=", "name": "Reversed Monotype Tee" + }, + "ProductOnCatalogPromotion": { + "variantId": "UHJvZHVjdFZhcmlhbnQ6MzM1", + "id": "UHJvZHVjdDoxMjk=", + "name": "Dash Force" } } } \ No newline at end of file diff --git a/apps/avatax/e2e/data/templates/order.json b/apps/avatax/e2e/data/templates/order.json index a1dcbb8e8..2a23bd350 100644 --- a/apps/avatax/e2e/data/templates/order.json +++ b/apps/avatax/e2e/data/templates/order.json @@ -2,12 +2,21 @@ "DraftOrder:PricesWithTax": { "channelID": "$M{Channel.PricesWithTax.id}" }, - "DraftOrder:PricesWithTax:Address": { + "DraftOrder:Address": { "billingAddress": "$M{Address.NewYork}", "shippingAddress": "$M{Address.NewYork}", "userEmail": "$F{UserEmail}" }, "DraftOrder:PricesWithTax:ShippingMethod": { "deliveryMethodId": "$M{Channel.PricesWithTax.deliveryMethodId}" + }, + "DraftOrder:PricesWithoutTax": { + "channelID": "$M{Channel.USA.id}" + }, + "DraftOrder:PricesWithoutTax:ShippingMethod": { + "deliveryMethodId": "$M{Channel.USA.deliveryMethodId}" + }, + "DraftOrder:VoucherCode": { + "voucherCode": "$M{Voucher.Percentage.code}" } -} +} \ No newline at end of file diff --git a/apps/avatax/e2e/graphql/fragments/OrderDetails.graphql b/apps/avatax/e2e/graphql/fragments/OrderDetails.graphql index ad41de052..25adeafa9 100644 --- a/apps/avatax/e2e/graphql/fragments/OrderDetails.graphql +++ b/apps/avatax/e2e/graphql/fragments/OrderDetails.graphql @@ -22,4 +22,30 @@ fragment OrderDetailsFragment on Order { ...Money } } -} + undiscountedTotal { + gross { + ...Money + } + net { + ...Money + } + tax { + ...Money + } + } + lines { + id + quantity + totalPrice { + gross { + ...Money + } + net { + ...Money + } + tax { + ...Money + } + } + } +} \ No newline at end of file diff --git a/apps/avatax/e2e/graphql/fragments/OrderDiscounts.graphql b/apps/avatax/e2e/graphql/fragments/OrderDiscounts.graphql new file mode 100644 index 000000000..780b09c0c --- /dev/null +++ b/apps/avatax/e2e/graphql/fragments/OrderDiscounts.graphql @@ -0,0 +1,10 @@ +fragment Discounts on OrderDiscount { + name + reason + type + valueType + value + amount { + amount + } +} \ No newline at end of file diff --git a/apps/avatax/e2e/graphql/mutations/CreateOrderLines.graphql b/apps/avatax/e2e/graphql/mutations/CreateOrderLines.graphql index 220bd381c..575fe495c 100644 --- a/apps/avatax/e2e/graphql/mutations/CreateOrderLines.graphql +++ b/apps/avatax/e2e/graphql/mutations/CreateOrderLines.graphql @@ -1,5 +1,5 @@ -mutation CreateOrderLines($variantId: ID!, $orderId: ID!) { - orderLinesCreate(id: $orderId, input: { quantity: 10, variantId: $variantId }) { +mutation CreateOrderLines($orderId: ID!, $input:[OrderLineCreateInput!]!) { + orderLinesCreate(id: $orderId, input: $input) { order { ...OrderDetailsFragment } @@ -8,4 +8,4 @@ mutation CreateOrderLines($variantId: ID!, $orderId: ID!) { quantity } } -} +} \ No newline at end of file diff --git a/apps/avatax/e2e/graphql/mutations/DraftOrderUpdateVoucher.graphql b/apps/avatax/e2e/graphql/mutations/DraftOrderUpdateVoucher.graphql new file mode 100644 index 000000000..f9d1c40f5 --- /dev/null +++ b/apps/avatax/e2e/graphql/mutations/DraftOrderUpdateVoucher.graphql @@ -0,0 +1,14 @@ +mutation DraftOrderUpdateVoucher($orderId: ID!, $voucherCode: String) { + draftOrderUpdate(id: $orderId, input: {voucherCode: $voucherCode}) { + errors { + ...OrderError + } + order { + ...OrderDetailsFragment + voucherCode + discounts { + ...Discounts + } + } + } +} \ No newline at end of file diff --git a/apps/avatax/e2e/graphql/mutations/OrderAddDiscount.graphql b/apps/avatax/e2e/graphql/mutations/OrderAddDiscount.graphql index eabe00f92..a4d6edb08 100644 --- a/apps/avatax/e2e/graphql/mutations/OrderAddDiscount.graphql +++ b/apps/avatax/e2e/graphql/mutations/OrderAddDiscount.graphql @@ -5,6 +5,9 @@ mutation OrderDiscountAdd($orderId: ID!, $input: OrderDiscountCommonInput!) { } order { ...OrderDetailsFragment + discounts { + ...Discounts + } } } } \ No newline at end of file diff --git a/apps/avatax/e2e/graphql/mutations/OrderLineUpdate.graphql b/apps/avatax/e2e/graphql/mutations/OrderLineUpdate.graphql new file mode 100644 index 000000000..e1e8732e0 --- /dev/null +++ b/apps/avatax/e2e/graphql/mutations/OrderLineUpdate.graphql @@ -0,0 +1,10 @@ +mutation OrderLineUpdate($lineId: ID!, $input: OrderLineInput!) { + orderLineUpdate(id: $lineId, input: $input) { + order { + ...OrderDetailsFragment + discounts { + ...Discounts + } + } + } +} \ No newline at end of file diff --git a/apps/avatax/e2e/tests/checkout_address_change.spec.ts b/apps/avatax/e2e/tests/checkout_address_change.spec.ts index b43efb00d..147fd59cf 100644 --- a/apps/avatax/e2e/tests/checkout_address_change.spec.ts +++ b/apps/avatax/e2e/tests/checkout_address_change.spec.ts @@ -1,6 +1,5 @@ /* eslint-disable turbo/no-undeclared-env-vars */ import { e2e } from "pactum"; -import { string } from "pactum-matchers"; import { describe, it } from "vitest"; import { @@ -9,18 +8,14 @@ import { CheckoutUpdateDeliveryMethod, CompleteCheckout, CreateCheckoutNoAddress, - OrderDetails, - StaffUserTokenCreate, } from "../generated/graphql"; import { getCompleteMoney } from "../utils/moneyUtils"; // Testmo: https://saleor.testmo.net/repositories/6?group_id=139&case_id=18385 describe("App should calculate taxes for checkout on update shipping address TC: AVATAX_21", () => { const testCase = e2e("Checkout for product with tax class [pricesEnteredWithTax: True]"); - const staffCredentials = { - email: process.env.E2E_USER_NAME as string, - password: process.env.E2E_USER_PASSWORD as string, - }; + + const CURRENCY = "USD"; const TOTAL_GROSS_PRICE_BEFORE_SHIPPING = 15; const TOTAL_NET_PRICE_BEFORE_SHIPPING = 13.78; @@ -60,6 +55,7 @@ describe("App should calculate taxes for checkout on update shipping address TC: gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, net: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, tax: 0, + currency: CURRENCY, }), ) .stores("CheckoutId", "data.checkoutCreate.checkout.id"); @@ -88,6 +84,7 @@ describe("App should calculate taxes for checkout on update shipping address TC: gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, net: TOTAL_NET_PRICE_BEFORE_SHIPPING, tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, }), ); }); @@ -117,6 +114,7 @@ describe("App should calculate taxes for checkout on update shipping address TC: gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, net: TOTAL_NET_PRICE_BEFORE_SHIPPING, tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, }), ); }); @@ -137,6 +135,7 @@ describe("App should calculate taxes for checkout on update shipping address TC: gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, net: TOTAL_NET_PRICE_AFTER_SHIPPING, tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, }), ) .expectJson( @@ -145,6 +144,7 @@ describe("App should calculate taxes for checkout on update shipping address TC: gross: SHIPPING_GROSS_PRICE, net: SHIPPING_NET_PRICE, tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, }), ); }); @@ -163,6 +163,7 @@ describe("App should calculate taxes for checkout on update shipping address TC: gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, net: TOTAL_NET_PRICE_AFTER_SHIPPING, tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, }), ) .expectJson( @@ -171,51 +172,9 @@ describe("App should calculate taxes for checkout on update shipping address TC: gross: SHIPPING_GROSS_PRICE, net: SHIPPING_NET_PRICE, tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, }), ) .stores("OrderID", "data.checkoutComplete.order.id"); }); - - /* - * It takes few seconds for metadata do be populated with `avataxId` key - * That's why we need to do it in a separate step - */ - it("creates token for staff user", async () => { - await testCase - .step("Create token for staff user") - .spec() - .post("/graphql/") - .withGraphQLQuery(StaffUserTokenCreate) - .withGraphQLVariables(staffCredentials) - .expectStatus(200) - .expectJsonLike({ - data: { - tokenCreate: { - token: "typeof $V === 'string'", - }, - }, - }) - .stores("StaffUserToken", "data.tokenCreate.token") - .retry(); - }); - - it("should have metadata with 'avataxId' key", async () => { - await testCase - .step("Check if order has metadata with 'avataxId' key") - .spec() - .post("/graphql/") - .withGraphQLQuery(OrderDetails) - .withGraphQLVariables({ - id: "$S{OrderID}", - }) - .withHeaders({ - Authorization: "Bearer $S{StaffUserToken}", - }) - .expectStatus(200) - .expectJsonLike("data.order.metadata[key=avataxId]", { - key: "avataxId", - value: "typeof $V === 'string'", - }) - .retry(4, 2000); - }); }); diff --git a/apps/avatax/e2e/tests/checkout_basic_product_with_tax_code.spec.ts b/apps/avatax/e2e/tests/checkout_basic_product_with_tax_code.spec.ts index f869dbb75..fa6a93975 100644 --- a/apps/avatax/e2e/tests/checkout_basic_product_with_tax_code.spec.ts +++ b/apps/avatax/e2e/tests/checkout_basic_product_with_tax_code.spec.ts @@ -1,25 +1,19 @@ /* eslint-disable turbo/no-undeclared-env-vars */ import { e2e } from "pactum"; -import { string } from "pactum-matchers"; import { describe, it } from "vitest"; import { CheckoutUpdateDeliveryMethod, CompleteCheckout, CreateCheckout, - OrderDetails, - StaffUserTokenCreate, } from "../generated/graphql"; import { getCompleteMoney } from "../utils/moneyUtils"; // Testmo: https://saleor.testmo.net/repositories/6?group_id=139&case_id=18384 describe("App should calculate taxes for checkout with product with tax class TC: AVATAX_20", () => { const testCase = e2e("Checkout for product with tax class [pricesEnteredWithTax: True]"); - const staffCredentials = { - email: process.env.E2E_USER_NAME as string, - password: process.env.E2E_USER_PASSWORD as string, - }; + const CURRENCY = "USD"; const TOTAL_GROSS_PRICE_BEFORE_SHIPPING = 15; const TOTAL_NET_PRICE_BEFORE_SHIPPING = 13.78; const TOTAL_TAX_PRICE_BEFORE_SHIPPING = 1.22; @@ -52,6 +46,7 @@ describe("App should calculate taxes for checkout with product with tax class TC gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, net: TOTAL_NET_PRICE_BEFORE_SHIPPING, tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, }), ) .stores("CheckoutId", "data.checkoutCreate.checkout.id"); @@ -73,6 +68,7 @@ describe("App should calculate taxes for checkout with product with tax class TC gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, net: TOTAL_NET_PRICE_AFTER_SHIPPING, tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, }), ) .expectJson( @@ -81,6 +77,7 @@ describe("App should calculate taxes for checkout with product with tax class TC gross: SHIPPING_GROSS_PRICE, net: SHIPPING_NET_PRICE, tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, }), ); }); @@ -99,6 +96,7 @@ describe("App should calculate taxes for checkout with product with tax class TC gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, net: TOTAL_NET_PRICE_AFTER_SHIPPING, tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, }), ) .expectJson( @@ -107,51 +105,9 @@ describe("App should calculate taxes for checkout with product with tax class TC gross: SHIPPING_GROSS_PRICE, net: SHIPPING_NET_PRICE, tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, }), ) .stores("OrderID", "data.checkoutComplete.order.id"); }); - - /* - * It takes few seconds for metadata do be populated with `avataxId` key - * That's why we need to do it in a separate step - */ - it("creates token for staff user", async () => { - await testCase - .step("Create token for staff user") - .spec() - .post("/graphql/") - .withGraphQLQuery(StaffUserTokenCreate) - .withGraphQLVariables(staffCredentials) - .expectStatus(200) - .expectJsonLike({ - data: { - tokenCreate: { - token: "typeof $V === 'string'", - }, - }, - }) - .stores("StaffUserToken", "data.tokenCreate.token") - .retry(); - }); - - it("should have metadata with 'avataxId' key", async () => { - await testCase - .step("Check if order has metadata with 'avataxId' key") - .spec() - .post("/graphql/") - .withGraphQLQuery(OrderDetails) - .withGraphQLVariables({ - id: "$S{OrderID}", - }) - .withHeaders({ - Authorization: "Bearer $S{StaffUserToken}", - }) - .expectStatus(200) - .expectJsonLike("data.order.metadata[key=avataxId]", { - key: "avataxId", - value: "typeof $V === 'string'", - }) - .retry(4, 2000); - }); }); diff --git a/apps/avatax/e2e/tests/checkout_basic_product_without_tax_code.spec.ts b/apps/avatax/e2e/tests/checkout_basic_product_without_tax_code.spec.ts index e6023e023..e8cd88624 100644 --- a/apps/avatax/e2e/tests/checkout_basic_product_without_tax_code.spec.ts +++ b/apps/avatax/e2e/tests/checkout_basic_product_without_tax_code.spec.ts @@ -13,6 +13,7 @@ import { getCompleteMoney, getMoney } from "../utils/moneyUtils"; describe("App should calculate taxes for checkout with product without tax class TC: AVATAX_1", () => { const testCase = e2e("Product without tax class [pricesEnteredWithTax: False]"); + const CURRENCY = "USD"; const TOTAL_NET_PRICE_BEFORE_SHIPPING = 300; const TOTAL_TAX_PRICE_BEFORE_SHIPPING = 26.63; const TOTAL_GROSS_PRICE_BEFORE_SHIPPING = 326.63; @@ -41,6 +42,7 @@ describe("App should calculate taxes for checkout with product without tax class gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, net: TOTAL_NET_PRICE_BEFORE_SHIPPING, tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, }), ) .retry() @@ -63,6 +65,7 @@ describe("App should calculate taxes for checkout with product without tax class gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, net: TOTAL_NET_PRICE_AFTER_SHIPPING, tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, }), ) .expectJson( @@ -71,6 +74,7 @@ describe("App should calculate taxes for checkout with product without tax class gross: SHIPPING_GROSS_PRICE, net: SHIPPING_NET_PRICE, tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, }), ) .retry(); diff --git a/apps/avatax/e2e/tests/checkout_order_promotion_subtotal_reward.spec.ts b/apps/avatax/e2e/tests/checkout_order_promotion_subtotal_reward.spec.ts index 407d83298..6302e0919 100644 --- a/apps/avatax/e2e/tests/checkout_order_promotion_subtotal_reward.spec.ts +++ b/apps/avatax/e2e/tests/checkout_order_promotion_subtotal_reward.spec.ts @@ -57,6 +57,7 @@ describe("App should calculate taxes for checkout with order promotion with subt gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, net: TOTAL_NET_PRICE_BEFORE_SHIPPING, tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, }), ) .stores("CheckoutId", "data.checkoutCreate.checkout.id") @@ -79,6 +80,7 @@ describe("App should calculate taxes for checkout with order promotion with subt gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, net: TOTAL_NET_PRICE_AFTER_SHIPPING, tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, }), ) .expectJson( @@ -87,6 +89,7 @@ describe("App should calculate taxes for checkout with order promotion with subt gross: SHIPPING_GROSS_PRICE, net: SHIPPING_NET_PRICE, tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, }), ) .retry(); @@ -107,11 +110,12 @@ describe("App should calculate taxes for checkout with order promotion with subt gross: PRODUCT_GROSS_PRICE_AFTER_PROMOTION, net: PRODUCT_NET_PRICE_AFTER_PROMOTION, tax: PRODUCT_TAX_PRICE_AFTER_PROMOTION, + currency: CURRENCY, }), ) .expectJson( "data.checkoutLinesUpdate.checkout.lines[0].undiscountedUnitPrice", - getMoney(TOTAL_NET_PRICE_BEFORE_SHIPPING), + getMoney(TOTAL_NET_PRICE_BEFORE_SHIPPING, CURRENCY), ) .expectJson( "data.checkoutLinesUpdate.checkout.shippingPrice", @@ -119,6 +123,7 @@ describe("App should calculate taxes for checkout with order promotion with subt gross: SHIPPING_GROSS_PRICE, net: SHIPPING_NET_PRICE, tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, }), ) .expectJson( @@ -127,6 +132,7 @@ describe("App should calculate taxes for checkout with order promotion with subt gross: TOTAL_GROSS_PRICE_INCLUDING_ORDER_PROMOTION, net: TOTAL_NET_PRICE_INCLUDING_ORDER_PROMOTION, tax: TOTAL_TAX_PRICE_INCLUDING_ORDER_PROMOTION, + currency: CURRENCY, }), ) .retry(); diff --git a/apps/avatax/e2e/tests/checkout_with_entire_voucher.spec.ts b/apps/avatax/e2e/tests/checkout_with_entire_voucher.spec.ts index 12c90f6c1..2dd700084 100644 --- a/apps/avatax/e2e/tests/checkout_with_entire_voucher.spec.ts +++ b/apps/avatax/e2e/tests/checkout_with_entire_voucher.spec.ts @@ -15,7 +15,7 @@ describe("App should calculate taxes for checkout with entire order voucher appl const testCase = e2e( "Product without tax class [pricesEnteredWithTax: False], voucher [type: ENTIRE_ORDER, discountValueType: PERCENTAGE]", ); - + const CURRENCY = "USD"; const TOTAL_NET_PRICE_BEFORE_SHIPPING = 15; const TOTAL_TAX_PRICE_BEFORE_SHIPPING = 1.33; const TOTAL_GROSS_PRICE_BEFORE_SHIPPING = 16.33; @@ -61,6 +61,7 @@ describe("App should calculate taxes for checkout with entire order voucher appl gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, net: TOTAL_NET_PRICE_BEFORE_SHIPPING, tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, }), ) .retry() @@ -83,6 +84,7 @@ describe("App should calculate taxes for checkout with entire order voucher appl gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, net: TOTAL_NET_PRICE_AFTER_SHIPPING, tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, }), ) .expectJson( @@ -91,6 +93,7 @@ describe("App should calculate taxes for checkout with entire order voucher appl gross: SHIPPING_GROSS_PRICE, net: SHIPPING_NET_PRICE, tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, }), ) .retry(); @@ -106,18 +109,19 @@ describe("App should calculate taxes for checkout with entire order voucher appl "@DATA:TEMPLATE@": "AddVoucher:USA:Percentage", }) .expectJson("data.checkoutAddPromoCode.checkout.discountName", "$M{Voucher.Percentage.name}") - .expectJson("data.checkoutAddPromoCode.checkout.discount", getMoney(VOUCHER_AMOUNT)) + .expectJson("data.checkoutAddPromoCode.checkout.discount", getMoney(VOUCHER_AMOUNT, CURRENCY)) .expectJson( "data.checkoutAddPromoCode.checkout.lines[0].totalPrice", getCompleteMoney({ gross: PRODUCT_GROSS_PRICE_AFTER_VOUCHER, net: PRODUCT_NET_PRICE_AFTER_VOUCHER, tax: PRODUCT_TAX_PRICE_AFTER_VOUCHER, + currency: CURRENCY, }), ) .expectJson( "data.checkoutAddPromoCode.checkout.lines[0].undiscountedTotalPrice", - getMoney(TOTAL_NET_PRICE_BEFORE_SHIPPING), + getMoney(TOTAL_NET_PRICE_BEFORE_SHIPPING, CURRENCY), ) .expectJson( "data.checkoutAddPromoCode.checkout.shippingPrice", @@ -125,6 +129,7 @@ describe("App should calculate taxes for checkout with entire order voucher appl gross: SHIPPING_GROSS_PRICE_AFTER_VOUCHER, net: SHIPPING_NET_PRICE_AFTER_VOUCHER, tax: SHIPPING_TAX_PRICE_AFTER_VOUCHER, + currency: CURRENCY, }), ) .expectJson( @@ -133,6 +138,7 @@ describe("App should calculate taxes for checkout with entire order voucher appl gross: TOTAL_GROSS_PRICE_AFTER_VOUCHER, net: TOTAL_NET_PRICE_AFTER_VOUCHER, tax: TOTAL_TAX_PRICE_AFTER_VOUCHER, + currency: CURRENCY, }), ) .retry(); diff --git a/apps/avatax/e2e/tests/checkout_with_voucher_free_shipping.spec.ts b/apps/avatax/e2e/tests/checkout_with_voucher_free_shipping.spec.ts index 203ae1381..62a09d938 100644 --- a/apps/avatax/e2e/tests/checkout_with_voucher_free_shipping.spec.ts +++ b/apps/avatax/e2e/tests/checkout_with_voucher_free_shipping.spec.ts @@ -16,6 +16,7 @@ describe("App should calculate taxes for checkout with free shipping voucher app "Product with tax class [pricesEnteredWithTax: False], voucher [type: SHIPPING, discountValueType: PERCENTAGE]", ); + const CURRENCY = "USD"; const TOTAL_NET_PRICE_BEFORE_SHIPPING = 15; const TOTAL_TAX_PRICE_BEFORE_SHIPPING = 1.33; const TOTAL_GROSS_PRICE_BEFORE_SHIPPING = 16.33; @@ -57,6 +58,7 @@ describe("App should calculate taxes for checkout with free shipping voucher app gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, net: TOTAL_NET_PRICE_BEFORE_SHIPPING, tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, }), ) .retry() @@ -79,6 +81,7 @@ describe("App should calculate taxes for checkout with free shipping voucher app gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, net: TOTAL_NET_PRICE_AFTER_SHIPPING, tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, }), ) .expectJson( @@ -87,6 +90,7 @@ describe("App should calculate taxes for checkout with free shipping voucher app gross: SHIPPING_GROSS_PRICE, net: SHIPPING_NET_PRICE, tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, }), ) .retry(); @@ -105,13 +109,14 @@ describe("App should calculate taxes for checkout with free shipping voucher app "data.checkoutAddPromoCode.checkout.discountName", "$M{Voucher.FreeShipping.name}", ) - .expectJson("data.checkoutAddPromoCode.checkout.discount", getMoney(VOUCHER_AMOUNT)) + .expectJson("data.checkoutAddPromoCode.checkout.discount", getMoney(VOUCHER_AMOUNT, CURRENCY)) .expectJson( "data.checkoutAddPromoCode.checkout.shippingPrice", getCompleteMoney({ gross: SHIPPING_GROSS_PRICE_AFTER_VOUCHER, net: SHIPPING_NET_PRICE_AFTER_VOUCHER, tax: SHIPPING_TAX_PRICE_AFTER_VOUCHER, + currency: CURRENCY, }), ) .expectJson( @@ -120,6 +125,7 @@ describe("App should calculate taxes for checkout with free shipping voucher app gross: TOTAL_GROSS_PRICE_AFTER_VOUCHER, net: TOTAL_NET_PRICE_AFTER_VOUCHER, tax: TOTAL_TAX_PRICE_AFTER_VOUCHER, + currency: CURRENCY, }), ) .retry(); diff --git a/apps/avatax/e2e/tests/draft_order_basic.spec.ts b/apps/avatax/e2e/tests/draft_order_basic.spec.ts index 7b0b9188c..e6dea4d95 100644 --- a/apps/avatax/e2e/tests/draft_order_basic.spec.ts +++ b/apps/avatax/e2e/tests/draft_order_basic.spec.ts @@ -8,8 +8,6 @@ import { DraftOrderComplete, DraftOrderUpdateAddress, DraftOrderUpdateShippingMethod, - MoneyFragment, - OrderDetails, StaffUserTokenCreate, } from "../generated/graphql"; import { getCompleteMoney } from "../utils/moneyUtils"; @@ -22,6 +20,7 @@ describe("App should calculate taxes for draft order with product with tax class password: process.env.E2E_USER_PASSWORD as string, }; + const CURRENCY = "USD"; const TOTAL_GROSS_PRICE_BEFORE_SHIPPING = 15; const TOTAL_NET_PRICE_BEFORE_SHIPPING = 13.78; const TOTAL_TAX_PRICE_BEFORE_SHIPPING = 1.22; @@ -84,7 +83,7 @@ describe("App should calculate taxes for draft order with product with tax class .withGraphQLQuery(CreateOrderLines) .withGraphQLVariables({ orderId: "$S{OrderID}", - variantId: "$M{Product.Juice.variantId}", + input: [{ quantity: 10, variantId: "$M{Product.Juice.variantId}" }], }) .withHeaders({ Authorization: "Bearer $S{StaffUserToken}", @@ -103,7 +102,7 @@ describe("App should calculate taxes for draft order with product with tax class .post("/graphql/") .withGraphQLQuery(DraftOrderUpdateAddress) .withGraphQLVariables({ - "@DATA:TEMPLATE@": "DraftOrder:PricesWithTax:Address", + "@DATA:TEMPLATE@": "DraftOrder:Address", "@OVERRIDES@": { orderId: "$S{OrderID}", }, @@ -119,6 +118,7 @@ describe("App should calculate taxes for draft order with product with tax class gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, net: TOTAL_NET_PRICE_BEFORE_SHIPPING, tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, }), ); }); @@ -146,6 +146,7 @@ describe("App should calculate taxes for draft order with product with tax class gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, net: TOTAL_NET_PRICE_AFTER_SHIPPING, tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, }), ) .expectJson( @@ -154,6 +155,7 @@ describe("App should calculate taxes for draft order with product with tax class gross: TOTAL_GROSS_SHIPPING_PRICE, net: TOTAL_NET_SHIPPING_PRICE, tax: TOTAL_TAX_SHIPPING_PRICE, + currency: CURRENCY, }), ); }); @@ -173,24 +175,4 @@ describe("App should calculate taxes for draft order with product with tax class .expectStatus(200) .expectJson("data.draftOrderComplete.order.id", "$S{OrderID}"); }); - - it("should have metadata with 'avataxId' key", async () => { - await testCase - .step("Check if order has metadata with 'avataxId' key") - .spec() - .post("/graphql/") - .withGraphQLQuery(OrderDetails) - .withGraphQLVariables({ - id: "$S{OrderID}", - }) - .withHeaders({ - Authorization: "Bearer $S{StaffUserToken}", - }) - .expectStatus(200) - .expectJsonLike("data.order.metadata[key=avataxId]", { - key: "avataxId", - value: "typeof $V === 'string'", - }) - .retry(4, 2000); - }); }); diff --git a/apps/avatax/e2e/tests/draft_order_catalog_promotion.spec.ts b/apps/avatax/e2e/tests/draft_order_catalog_promotion.spec.ts new file mode 100644 index 000000000..6fe3c8a27 --- /dev/null +++ b/apps/avatax/e2e/tests/draft_order_catalog_promotion.spec.ts @@ -0,0 +1,198 @@ +/* eslint-disable turbo/no-undeclared-env-vars */ +import { e2e } from "pactum"; +import { describe, it } from "vitest"; + +import { + CreateDraftOrder, + CreateOrderLines, + DraftOrderComplete, + DraftOrderUpdateAddress, + DraftOrderUpdateShippingMethod, + StaffUserTokenCreate, +} from "../generated/graphql"; +import { getCompleteMoney, getMoney } from "../utils/moneyUtils"; + +// Testmo: https://saleor.testmo.net/repositories/6?group_id=4846&case_id=18388 +describe("App should calculate taxes on draft order with products on catalog promotion TC: AVATAX_24", () => { + const testCase = e2e( + "Draft order with products on catalog promotion [pricesEnteredWithTax: True]", + ); + const staffCredentials = { + email: process.env.E2E_USER_NAME as string, + password: process.env.E2E_USER_PASSWORD as string, + }; + + const CURRENCY = "USD"; + const TOTAL_GROSS_PRICE_BEFORE_SHIPPING = 328.22; + const TOTAL_NET_PRICE_BEFORE_SHIPPING = 301.46; + const TOTAL_TAX_PRICE_BEFORE_SHIPPING = 26.76; + + const SHIPPING_GROSS_PRICE = 69.31; + const SHIPPING_NET_PRICE = 63.66; + const SHIPPING_TAX_PRICE = 5.65; + + const TOTAL_GROSS_PRICE_AFTER_SHIPPING = 397.53; + const TOTAL_NET_PRICE_AFTER_SHIPPING = 365.12; + const TOTAL_TAX_PRICE_AFTER_SHIPPING = 32.41; + + const UNDISCOUNTED_TOTAL_GROSS_PRICE_AFTER_SHIPPING = 414.81; + const UNDISCOUNTED_TOTAL_NET_PRICE_AFTER_SHIPPING = 381; + const UNDISCOUNTED_TOTAL_TAX_PRICE_AFTER_SHIPPING = 33.81; + + it("creates token for staff user", async () => { + await testCase + .step("Create token for staff user") + .spec() + .post("/graphql/") + .withGraphQLQuery(StaffUserTokenCreate) + .withGraphQLVariables(staffCredentials) + .expectStatus(200) + .expectJsonLike({ + data: { + tokenCreate: { + token: "typeof $V === 'string'", + }, + }, + }) + .stores("StaffUserToken", "data.tokenCreate.token") + .retry(); + }); + it("creates order in channel pricesEnteredWithTax: True", async () => { + await testCase + .step("Create order in channel") + .spec() + .post("/graphql/") + .withGraphQLQuery(CreateDraftOrder) + .withGraphQLVariables({ + "@DATA:TEMPLATE@": "DraftOrder:PricesWithTax", + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJsonLike({ + data: { + draftOrderCreate: { + order: { + id: "typeof $V === 'string'", + }, + }, + }, + }) + .stores("OrderID", "data.draftOrderCreate.order.id"); + }); + it("should add lines to draft order", async () => { + await testCase + .step("add prodcuts") + .spec() + .post("/graphql/") + .withGraphQLQuery(CreateOrderLines) + .withGraphQLVariables({ + orderId: "$S{OrderID}", + input: [ + { + quantity: 1, + variantId: "$M{Product.ProductOnCatalogPromotion.variantId}", + }, + ], + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.orderLinesCreate.orderLines[0].quantity", 1) + .expectJson( + "data.orderLinesCreate.order.total.gross", + getMoney(TOTAL_GROSS_PRICE_BEFORE_SHIPPING, CURRENCY), + ); + }); + it("should update order address", async () => { + await testCase + .step("Update addresses on draft order") + .spec() + .post("/graphql/") + .withGraphQLQuery(DraftOrderUpdateAddress) + .withGraphQLVariables({ + "@DATA:TEMPLATE@": "DraftOrder:Address", + "@OVERRIDES@": { + orderId: "$S{OrderID}", + }, + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.draftOrderUpdate.order.id", "$S{OrderID}") + .expectJson( + "data.draftOrderUpdate.order.total", + getCompleteMoney({ + gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, + net: TOTAL_NET_PRICE_BEFORE_SHIPPING, + tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, + }), + ); + }); + + it("should update order's shipping method", async () => { + await testCase + .step("Add shipping method") + .spec() + .post("/graphql/") + .withGraphQLQuery(DraftOrderUpdateShippingMethod) + .withGraphQLVariables({ + "@DATA:TEMPLATE@": "DraftOrder:PricesWithTax:ShippingMethod", + "@OVERRIDES@": { + orderId: "$S{OrderID}", + }, + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.orderUpdateShipping.order.id", "$S{OrderID}") + .expectJson( + "data.orderUpdateShipping.order.total", + getCompleteMoney({ + gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, + net: TOTAL_NET_PRICE_AFTER_SHIPPING, + tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, + }), + ) + .expectJson( + "data.orderUpdateShipping.order.shippingPrice", + getCompleteMoney({ + gross: SHIPPING_GROSS_PRICE, + net: SHIPPING_NET_PRICE, + tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, + }), + ) + .expectJson( + "data.orderUpdateShipping.order.undiscountedTotal", + getCompleteMoney({ + gross: UNDISCOUNTED_TOTAL_GROSS_PRICE_AFTER_SHIPPING, + net: UNDISCOUNTED_TOTAL_NET_PRICE_AFTER_SHIPPING, + tax: UNDISCOUNTED_TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, + }), + ); + }); + + it("should complete draft order", async () => { + await testCase + .step("Complete draft order") + .spec() + .post("/graphql/") + .withGraphQLQuery(DraftOrderComplete) + .withGraphQLVariables({ + id: "$S{OrderID}", + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.draftOrderComplete.order.id", "$S{OrderID}"); + }); +}); diff --git a/apps/avatax/e2e/tests/draft_order_with_entire_voucher.spec.ts b/apps/avatax/e2e/tests/draft_order_with_entire_voucher.spec.ts new file mode 100644 index 000000000..8bb61d48d --- /dev/null +++ b/apps/avatax/e2e/tests/draft_order_with_entire_voucher.spec.ts @@ -0,0 +1,234 @@ +/* eslint-disable turbo/no-undeclared-env-vars */ +import { e2e } from "pactum"; +import { describe, it } from "vitest"; + +import { + CreateDraftOrder, + CreateOrderLines, + DraftOrderComplete, + DraftOrderUpdateAddress, + DraftOrderUpdateShippingMethod, + DraftOrderUpdateVoucher, + StaffUserTokenCreate, +} from "../generated/graphql"; +import { getCompleteMoney } from "../utils/moneyUtils"; + +// Testmo: https://saleor.testmo.net/repositories/6?group_id=4846&case_id=18392 +describe("App should calculate taxes on draft order with entire order voucher applied TC: AVATAX_28", () => { + const testCase = e2e("Draft order with voucher entire order [pricesEnteredWithTax: False]"); + const staffCredentials = { + email: process.env.E2E_USER_NAME as string, + password: process.env.E2E_USER_PASSWORD as string, + }; + + const CURRENCY = "USD"; + + const TOTAL_GROSS_PRICE_BEFORE_SHIPPING = 32.66; + const TOTAL_NET_PRICE_BEFORE_SHIPPING = 30; + const TOTAL_TAX_PRICE_BEFORE_SHIPPING = 2.66; + + const SHIPPING_GROSS_PRICE = 75.46; + const SHIPPING_NET_PRICE = 69.31; + const SHIPPING_TAX_PRICE = 6.15; + + const TOTAL_GROSS_PRICE_AFTER_SHIPPING = 108.12; + const TOTAL_NET_PRICE_AFTER_SHIPPING = 99.31; + const TOTAL_TAX_PRICE_AFTER_SHIPPING = 8.81; + + const TOTAL_GROSS_PRICE_AFTER_SHIPPING_AFTER_VOUCHER = 103.22; + const TOTAL_NET_PRICE_AFTER_SHIPPING_AFTER_VOUCHER = 94.81; + const TOTAL_TAX_PRICE_AFTER_SHIPPING_AFTER_VOUCHER = 8.41; + + const SHIPPING_GROSS_PRICE_AFTER_VOUCHER = 75.45; + const SHIPPING_NET_PRICE_AFTER_VOUCHER = 69.31; + const SHIPPING_TAX_PRICE_AFTER_VOUCHER = 6.14; + + it("creates token for staff user", async () => { + await testCase + .step("Create token for staff user") + .spec() + .post("/graphql/") + .withGraphQLQuery(StaffUserTokenCreate) + .withGraphQLVariables(staffCredentials) + .expectStatus(200) + .expectJsonLike({ + data: { + tokenCreate: { + token: "typeof $V === 'string'", + }, + }, + }) + .stores("StaffUserToken", "data.tokenCreate.token") + .retry(); + }); + it("creates order in channel pricesEnteredWithTax: False", async () => { + await testCase + .step("Create order in channel") + .spec() + .post("/graphql/") + .withGraphQLQuery(CreateDraftOrder) + .withGraphQLVariables({ + "@DATA:TEMPLATE@": "DraftOrder:PricesWithoutTax", + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJsonLike({ + data: { + draftOrderCreate: { + order: { + id: "typeof $V === 'string'", + }, + }, + }, + }) + .stores("OrderID", "data.draftOrderCreate.order.id"); + }); + it("should create order lines as staff user", async () => { + await testCase + .step("Create order lines as staff user") + .spec() + .post("/graphql/") + .withGraphQLQuery(CreateOrderLines) + .withGraphQLVariables({ + orderId: "$S{OrderID}", + input: [{ quantity: 1, variantId: "$M{Product.Regular.variantId}" }], + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.orderLinesCreate.orderLines[0].quantity", 1) + .expectJson( + "data.orderLinesCreate.order.total.gross.amount", + TOTAL_NET_PRICE_BEFORE_SHIPPING, + ); + }); + it("should update order as staff user", async () => { + await testCase + .step("Update order as staff user") + .spec() + .post("/graphql/") + .withGraphQLQuery(DraftOrderUpdateAddress) + .withGraphQLVariables({ + "@DATA:TEMPLATE@": "DraftOrder:Address", + "@OVERRIDES@": { + orderId: "$S{OrderID}", + }, + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.draftOrderUpdate.order.id", "$S{OrderID}") + .expectJson( + "data.draftOrderUpdate.order.total", + getCompleteMoney({ + gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, + net: TOTAL_NET_PRICE_BEFORE_SHIPPING, + tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, + }), + ); + }); + + it("should update order's shipping method as staff user", async () => { + await testCase + .step("Update shipping method as staff user") + .spec() + .post("/graphql/") + .withGraphQLQuery(DraftOrderUpdateShippingMethod) + .withGraphQLVariables({ + "@DATA:TEMPLATE@": "DraftOrder:PricesWithoutTax:ShippingMethod", + "@OVERRIDES@": { + orderId: "$S{OrderID}", + }, + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.orderUpdateShipping.order.id", "$S{OrderID}") + .expectJson( + "data.orderUpdateShipping.order.total", + getCompleteMoney({ + gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, + net: TOTAL_NET_PRICE_AFTER_SHIPPING, + tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, + }), + ) + .expectJson( + "data.orderUpdateShipping.order.shippingPrice", + getCompleteMoney({ + gross: SHIPPING_GROSS_PRICE, + net: SHIPPING_NET_PRICE, + tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, + }), + ); + }); + + it("should add voucher code to draft order", async () => { + await testCase + .step("add voucher to draft order") + .spec() + .post("/graphql/") + .withGraphQLQuery(DraftOrderUpdateVoucher) + .withGraphQLVariables({ + "@DATA:TEMPLATE@": "DraftOrder:VoucherCode", + "@OVERRIDES@": { + orderId: "$S{OrderID}", + }, + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.draftOrderUpdate.order.id", "$S{OrderID}") + .expectJson( + "data.draftOrderUpdate.order.total", + getCompleteMoney({ + gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING_AFTER_VOUCHER, + net: TOTAL_NET_PRICE_AFTER_SHIPPING_AFTER_VOUCHER, + tax: TOTAL_TAX_PRICE_AFTER_SHIPPING_AFTER_VOUCHER, + currency: CURRENCY, + }), + ) + .expectJson( + "data.draftOrderUpdate.order.shippingPrice", + getCompleteMoney({ + gross: SHIPPING_GROSS_PRICE_AFTER_VOUCHER, + net: SHIPPING_NET_PRICE_AFTER_VOUCHER, + tax: SHIPPING_TAX_PRICE_AFTER_VOUCHER, + currency: CURRENCY, + }), + ) + .expectJson( + "data.draftOrderUpdate.order.undiscountedTotal", + getCompleteMoney({ + gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, + net: TOTAL_NET_PRICE_AFTER_SHIPPING, + tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, + }), + ) + .expectJson("data.draftOrderUpdate.order.discounts[0].type", "VOUCHER"); + }); + it("should complete draft order", async () => { + await testCase + .step("Complete draft order") + .spec() + .post("/graphql/") + .withGraphQLQuery(DraftOrderComplete) + .withGraphQLVariables({ + id: "$S{OrderID}", + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.draftOrderComplete.order.id", "$S{OrderID}"); + }); +}); diff --git a/apps/avatax/e2e/tests/draft_order_with_manual_total_discount.spec.ts b/apps/avatax/e2e/tests/draft_order_with_manual_total_discount.spec.ts index 913438af7..f75dd1f8d 100644 --- a/apps/avatax/e2e/tests/draft_order_with_manual_total_discount.spec.ts +++ b/apps/avatax/e2e/tests/draft_order_with_manual_total_discount.spec.ts @@ -21,6 +21,8 @@ describe("App should calculate taxes for draft order with manual total discount password: process.env.E2E_USER_PASSWORD as string, }; + const CURRENCY = "USD"; + const TOTAL_GROSS_PRICE_BEFORE_SHIPPING = 15; const TOTAL_NET_PRICE_BEFORE_SHIPPING = 13.78; const TOTAL_TAX_PRICE_BEFORE_SHIPPING = 1.22; @@ -91,7 +93,7 @@ describe("App should calculate taxes for draft order with manual total discount .withGraphQLQuery(CreateOrderLines) .withGraphQLVariables({ orderId: "$S{OrderID}", - variantId: "$M{Product.Juice.variantId}", + input: [{ quantity: 10, variantId: "$M{Product.Juice.variantId}" }], }) .withHeaders({ Authorization: "Bearer $S{StaffUserToken}", @@ -110,7 +112,7 @@ describe("App should calculate taxes for draft order with manual total discount .post("/graphql/") .withGraphQLQuery(DraftOrderUpdateAddress) .withGraphQLVariables({ - "@DATA:TEMPLATE@": "DraftOrder:PricesWithTax:Address", + "@DATA:TEMPLATE@": "DraftOrder:Address", "@OVERRIDES@": { orderId: "$S{OrderID}", }, @@ -126,10 +128,10 @@ describe("App should calculate taxes for draft order with manual total discount gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, net: TOTAL_NET_PRICE_BEFORE_SHIPPING, tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, }), ); }); - it("should update order's shipping method as staff user", async () => { await testCase .step("Update shipping method as staff user") @@ -153,6 +155,7 @@ describe("App should calculate taxes for draft order with manual total discount gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, net: TOTAL_NET_PRICE_AFTER_SHIPPING, tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, }), ) .expectJson( @@ -161,10 +164,10 @@ describe("App should calculate taxes for draft order with manual total discount gross: TOTAL_GROSS_SHIPPING_PRICE, net: TOTAL_NET_SHIPPING_PRICE, tax: TOTAL_TAX_SHIPPING_PRICE, + currency: CURRENCY, }), ); }); - it("should add manual discount for total as staff user", async () => { await testCase .step("add manual discount to draft order") @@ -190,6 +193,7 @@ describe("App should calculate taxes for draft order with manual total discount gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING_AFTER_DISCOUNT, net: TOTAL_NET_PRICE_AFTER_SHIPPING_AFTER_DISCOUNT, tax: TOTAL_TAX_PRICE_AFTER_SHIPPING_AFTER_DISCOUNT, + currency: CURRENCY, }), ) .expectJson( @@ -198,8 +202,10 @@ describe("App should calculate taxes for draft order with manual total discount gross: TOTAL_GROSS_SHIPPING_PRICE_AFTER_DISCOUNT, net: TOTAL_NET_SHIPPING_PRICE_AFTER_DISCOUNT, tax: TOTAL_TAX_SHIPPING_PRICE_AFTER_DISCOUNT, + currency: CURRENCY, }), - ); + ) + .expectJson("data.orderDiscountAdd.order.discounts[0].type", "MANUAL"); }); it("should complete draft order", async () => { await testCase diff --git a/apps/avatax/e2e/tests/draft_order_with_order_promotion_subtotal.spec.ts b/apps/avatax/e2e/tests/draft_order_with_order_promotion_subtotal.spec.ts new file mode 100644 index 000000000..ed2c2a8b5 --- /dev/null +++ b/apps/avatax/e2e/tests/draft_order_with_order_promotion_subtotal.spec.ts @@ -0,0 +1,262 @@ +/* eslint-disable turbo/no-undeclared-env-vars */ +import { e2e } from "pactum"; +import { describe, it } from "vitest"; + +import { + CreateDraftOrder, + CreateOrderLines, + DraftOrderComplete, + DraftOrderUpdateAddress, + DraftOrderUpdateShippingMethod, + OrderLineUpdate, + StaffUserTokenCreate, +} from "../generated/graphql"; +import { getCompleteMoney, getMoney } from "../utils/moneyUtils"; + +// Testmo: https://saleor.testmo.net/repositories/6?group_id=4846&case_id=24175 +describe("App should calculate taxes on draft order when order promotion is applied TC: AVATAX_32", () => { + const testCase = e2e("Draft order with order promotion, [pricesEnteredWithTax: False]"); + const staffCredentials = { + email: process.env.E2E_USER_NAME as string, + password: process.env.E2E_USER_PASSWORD as string, + }; + + const CURRENCY = "USD"; + const TOTAL_GROSS_PRICE_BEFORE_SHIPPING = 326.08; + const TOTAL_NET_PRICE_BEFORE_SHIPPING = 299.5; + const TOTAL_TAX_PRICE_BEFORE_SHIPPING = 26.58; + + const SHIPPING_GROSS_PRICE = 75.46; + const SHIPPING_NET_PRICE = 69.31; + const SHIPPING_TAX_PRICE = 6.15; + + const TOTAL_GROSS_PRICE_AFTER_SHIPPING = 401.54; + const TOTAL_NET_PRICE_AFTER_SHIPPING = 368.81; + const TOTAL_TAX_PRICE_AFTER_SHIPPING = 32.73; + + const PRODUCT_TOTAL_GROSS_PRICE_AFTER_PROMOTION = 489.12; + const PRODUCT_TOTAL_NET_PRICE_AFTER_PROMOTION = 449.25; + const PRODUCT_TOTAL_TAX_PRICE_AFTER_PROMOTION = 39.87; + + const TOTAL_GROSS_PRICE_INCLUDING_ORDER_PROMOTION = 564.58; + const TOTAL_NET_PRICE_INCLUDING_ORDER_PROMOTION = 518.56; + const TOTAL_TAX_PRICE_INCLUDING_ORDER_PROMOTION = 46.02; + + const UNDISCOUNTE_TOTAL_GROSS_PRICE = 727.62; + const UNDISCOUNTE_TOTAL_NET_PRICE = 668.31; + const UNDISCOUNTE_TOTAL_TAX_PRICE = 59.31; + + it("creates token for staff user", async () => { + await testCase + .step("Create token for staff user") + .spec() + .post("/graphql/") + .withGraphQLQuery(StaffUserTokenCreate) + .withGraphQLVariables(staffCredentials) + .expectStatus(200) + .expectJsonLike({ + data: { + tokenCreate: { + token: "typeof $V === 'string'", + }, + }, + }) + .stores("StaffUserToken", "data.tokenCreate.token") + .retry(); + }); + it("creates order in channel pricesEnteredWithTax: False", async () => { + await testCase + .step("Create order in channel") + .spec() + .post("/graphql/") + .withGraphQLQuery(CreateDraftOrder) + .withGraphQLVariables({ + "@DATA:TEMPLATE@": "DraftOrder:PricesWithoutTax", + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJsonLike({ + data: { + draftOrderCreate: { + order: { + id: "typeof $V === 'string'", + }, + }, + }, + }) + .stores("OrderID", "data.draftOrderCreate.order.id"); + }); + it("should add lines to draft order", async () => { + await testCase + .step("add prodcuts") + .spec() + .post("/graphql/") + .withGraphQLQuery(CreateOrderLines) + .withGraphQLVariables({ + orderId: "$S{OrderID}", + input: [ + { + quantity: 1, + variantId: "$M{Product.ExpensiveTshirt.variantId}", + }, + ], + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.orderLinesCreate.orderLines[0].quantity", 1) + .expectJson( + "data.orderLinesCreate.order.total.gross", + getMoney(TOTAL_NET_PRICE_BEFORE_SHIPPING, CURRENCY), + ) + .stores("OrderLineId", "data.orderLinesCreate.orderLines[0].id"); + }); + it("should update order address", async () => { + await testCase + .step("Update addresses on draft order") + .spec() + .post("/graphql/") + .withGraphQLQuery(DraftOrderUpdateAddress) + .withGraphQLVariables({ + "@DATA:TEMPLATE@": "DraftOrder:Address", + "@OVERRIDES@": { + orderId: "$S{OrderID}", + }, + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.draftOrderUpdate.order.id", "$S{OrderID}") + .expectJson( + "data.draftOrderUpdate.order.total", + getCompleteMoney({ + gross: TOTAL_GROSS_PRICE_BEFORE_SHIPPING, + net: TOTAL_NET_PRICE_BEFORE_SHIPPING, + tax: TOTAL_TAX_PRICE_BEFORE_SHIPPING, + currency: CURRENCY, + }), + ); + }); + + it("should update order's shipping method", async () => { + await testCase + .step("Add shipping method") + .spec() + .post("/graphql/") + .withGraphQLQuery(DraftOrderUpdateShippingMethod) + .withGraphQLVariables({ + "@DATA:TEMPLATE@": "DraftOrder:PricesWithTax:ShippingMethod", + "@OVERRIDES@": { + orderId: "$S{OrderID}", + }, + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.orderUpdateShipping.order.id", "$S{OrderID}") + .expectJson( + "data.orderUpdateShipping.order.total", + getCompleteMoney({ + gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, + net: TOTAL_NET_PRICE_AFTER_SHIPPING, + tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, + }), + ) + .expectJson( + "data.orderUpdateShipping.order.shippingPrice", + getCompleteMoney({ + gross: SHIPPING_GROSS_PRICE, + net: SHIPPING_NET_PRICE, + tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, + }), + ) + .expectJson( + "data.orderUpdateShipping.order.undiscountedTotal", + getCompleteMoney({ + gross: TOTAL_GROSS_PRICE_AFTER_SHIPPING, + net: TOTAL_NET_PRICE_AFTER_SHIPPING, + tax: TOTAL_TAX_PRICE_AFTER_SHIPPING, + currency: CURRENCY, + }), + ); + }); + + it("should update order line", async () => { + await testCase + .step("Update order line") + .spec() + .post("/graphql/") + .withGraphQLQuery(OrderLineUpdate) + .withGraphQLVariables({ + lineId: "$S{OrderLineId}", + input: { + quantity: 2, + }, + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.orderLineUpdate.order.id", "$S{OrderID}") + .expectJson( + "data.orderLineUpdate.order.lines[0].totalPrice", + getCompleteMoney({ + gross: PRODUCT_TOTAL_GROSS_PRICE_AFTER_PROMOTION, + net: PRODUCT_TOTAL_NET_PRICE_AFTER_PROMOTION, + tax: PRODUCT_TOTAL_TAX_PRICE_AFTER_PROMOTION, + currency: CURRENCY, + }), + ) + .expectJson( + "data.orderLineUpdate.order.total", + getCompleteMoney({ + gross: TOTAL_GROSS_PRICE_INCLUDING_ORDER_PROMOTION, + net: TOTAL_NET_PRICE_INCLUDING_ORDER_PROMOTION, + tax: TOTAL_TAX_PRICE_INCLUDING_ORDER_PROMOTION, + currency: CURRENCY, + }), + ) + .expectJson( + "data.orderLineUpdate.order.shippingPrice", + getCompleteMoney({ + gross: SHIPPING_GROSS_PRICE, + net: SHIPPING_NET_PRICE, + tax: SHIPPING_TAX_PRICE, + currency: CURRENCY, + }), + ) + .expectJson( + "data.orderLineUpdate.order.undiscountedTotal", + getCompleteMoney({ + gross: UNDISCOUNTE_TOTAL_GROSS_PRICE, + net: UNDISCOUNTE_TOTAL_NET_PRICE, + tax: UNDISCOUNTE_TOTAL_TAX_PRICE, + currency: CURRENCY, + }), + ) + .expectJson("data.orderLineUpdate.order.discounts[0].type", "ORDER_PROMOTION"); + }); + + it("should complete draft order", async () => { + await testCase + .step("Complete draft order") + .spec() + .post("/graphql/") + .withGraphQLQuery(DraftOrderComplete) + .withGraphQLVariables({ + id: "$S{OrderID}", + }) + .withHeaders({ + Authorization: "Bearer $S{StaffUserToken}", + }) + .expectStatus(200) + .expectJson("data.draftOrderComplete.order.id", "$S{OrderID}"); + }); +}); diff --git a/apps/avatax/e2e/utils/moneyUtils.ts b/apps/avatax/e2e/utils/moneyUtils.ts index f6ded3003..cd78045cc 100644 --- a/apps/avatax/e2e/utils/moneyUtils.ts +++ b/apps/avatax/e2e/utils/moneyUtils.ts @@ -1,11 +1,9 @@ import { MoneyFragment } from "../generated/graphql"; -const CURRENCY = "USD"; - -export const getMoney = (amount: number): MoneyFragment => { +export const getMoney = (amount: number, currency: string): MoneyFragment => { return { amount, - currency: CURRENCY, + currency, }; }; @@ -13,12 +11,14 @@ export const getCompleteMoney = ({ gross, net, tax, + currency, }: { gross: number; net: number; tax: number; + currency: string; }) => ({ - gross: getMoney(gross), - net: getMoney(net), - tax: getMoney(tax), + gross: getMoney(gross, currency), + net: getMoney(net, currency), + tax: getMoney(tax, currency), }); diff --git a/apps/avatax/package.json b/apps/avatax/package.json index 46a38d40f..03b5faa74 100644 --- a/apps/avatax/package.json +++ b/apps/avatax/package.json @@ -1,6 +1,6 @@ { "name": "app-avatax", - "version": "1.10.3", + "version": "1.10.4", "scripts": { "build": "pnpm generate && next build", "check-types": "tsc --noEmit", diff --git a/apps/avatax/src/modules/saleor/order-confirmed/event.ts b/apps/avatax/src/modules/saleor/order-confirmed/event.ts index 75b1e1ab1..4fc2e4e93 100644 --- a/apps/avatax/src/modules/saleor/order-confirmed/event.ts +++ b/apps/avatax/src/modules/saleor/order-confirmed/event.ts @@ -120,5 +120,5 @@ export class SaleorOrderConfirmedEvent { getOrderShippingAddress = () => this.rawPayload.order.shippingAddress; - getOrderBillingAddress = () => this.rawPayload.order.shippingAddress; + getOrderBillingAddress = () => this.rawPayload.order.billingAddress; }