feat(medusa,core-flows,types): add cart <> tax integration workflows + steps (#6580)
what: - adds tax lines to cart when item operations take place RESOLVES CORE-1821 RESOLVES CORE-1822 RESOLVES CORE-1823 RESOLVES CORE-1824
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/core-flows": patch
|
||||||
|
"@medusajs/medusa": patch
|
||||||
|
"@medusajs/types": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(medusa,core-flows,types): add cart <> tax integration workflows + steps
|
||||||
@@ -12,11 +12,13 @@ import {
|
|||||||
IPromotionModuleService,
|
IPromotionModuleService,
|
||||||
IRegionModuleService,
|
IRegionModuleService,
|
||||||
ISalesChannelModuleService,
|
ISalesChannelModuleService,
|
||||||
|
ITaxModuleService,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { PromotionRuleOperator, PromotionType } from "@medusajs/utils"
|
import { PromotionRuleOperator, PromotionType } from "@medusajs/utils"
|
||||||
|
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
||||||
import adminSeeder from "../../../../helpers/admin-seeder"
|
import adminSeeder from "../../../../helpers/admin-seeder"
|
||||||
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
|
import { createAuthenticatedCustomer } from "../../../helpers/create-authenticated-customer"
|
||||||
import { medusaIntegrationTestRunner } from "medusa-test-utils"
|
import { setupTaxStructure } from "../../fixtures"
|
||||||
|
|
||||||
jest.setTimeout(50000)
|
jest.setTimeout(50000)
|
||||||
|
|
||||||
@@ -27,38 +29,36 @@ medusaIntegrationTestRunner({
|
|||||||
testSuite: ({ dbConnection, getContainer, api }) => {
|
testSuite: ({ dbConnection, getContainer, api }) => {
|
||||||
describe("Store Carts API", () => {
|
describe("Store Carts API", () => {
|
||||||
let appContainer
|
let appContainer
|
||||||
let cartModuleService: ICartModuleService
|
let cartModule: ICartModuleService
|
||||||
let regionModuleService: IRegionModuleService
|
let regionModule: IRegionModuleService
|
||||||
let scModuleService: ISalesChannelModuleService
|
let scModule: ISalesChannelModuleService
|
||||||
let customerModule: ICustomerModuleService
|
let customerModule: ICustomerModuleService
|
||||||
let productModule: IProductModuleService
|
let productModule: IProductModuleService
|
||||||
let pricingModule: IPricingModuleService
|
let pricingModule: IPricingModuleService
|
||||||
let remoteLink: RemoteLink
|
let remoteLink: RemoteLink
|
||||||
let promotionModule: IPromotionModuleService
|
let promotionModule: IPromotionModuleService
|
||||||
|
let taxModule: ITaxModuleService
|
||||||
|
|
||||||
let defaultRegion
|
let defaultRegion
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
appContainer = getContainer()
|
appContainer = getContainer()
|
||||||
cartModuleService = appContainer.resolve(ModuleRegistrationName.CART)
|
cartModule = appContainer.resolve(ModuleRegistrationName.CART)
|
||||||
regionModuleService = appContainer.resolve(
|
regionModule = appContainer.resolve(ModuleRegistrationName.REGION)
|
||||||
ModuleRegistrationName.REGION
|
scModule = appContainer.resolve(ModuleRegistrationName.SALES_CHANNEL)
|
||||||
)
|
|
||||||
scModuleService = appContainer.resolve(
|
|
||||||
ModuleRegistrationName.SALES_CHANNEL
|
|
||||||
)
|
|
||||||
customerModule = appContainer.resolve(ModuleRegistrationName.CUSTOMER)
|
customerModule = appContainer.resolve(ModuleRegistrationName.CUSTOMER)
|
||||||
productModule = appContainer.resolve(ModuleRegistrationName.PRODUCT)
|
productModule = appContainer.resolve(ModuleRegistrationName.PRODUCT)
|
||||||
pricingModule = appContainer.resolve(ModuleRegistrationName.PRICING)
|
pricingModule = appContainer.resolve(ModuleRegistrationName.PRICING)
|
||||||
remoteLink = appContainer.resolve(LinkModuleUtils.REMOTE_LINK)
|
remoteLink = appContainer.resolve(LinkModuleUtils.REMOTE_LINK)
|
||||||
promotionModule = appContainer.resolve(ModuleRegistrationName.PROMOTION)
|
promotionModule = appContainer.resolve(ModuleRegistrationName.PROMOTION)
|
||||||
|
taxModule = appContainer.resolve(ModuleRegistrationName.TAX)
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await adminSeeder(dbConnection)
|
await adminSeeder(dbConnection)
|
||||||
|
|
||||||
// Here, so we don't have to create a region for each test
|
// Here, so we don't have to create a region for each test
|
||||||
defaultRegion = await regionModuleService.create({
|
defaultRegion = await regionModule.create({
|
||||||
name: "Default Region",
|
name: "Default Region",
|
||||||
currency_code: "dkk",
|
currency_code: "dkk",
|
||||||
})
|
})
|
||||||
@@ -66,12 +66,12 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
describe("POST /store/carts", () => {
|
describe("POST /store/carts", () => {
|
||||||
it("should create a cart", async () => {
|
it("should create a cart", async () => {
|
||||||
const region = await regionModuleService.create({
|
const region = await regionModule.create({
|
||||||
name: "US",
|
name: "US",
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
})
|
})
|
||||||
|
|
||||||
const salesChannel = await scModuleService.create({
|
const salesChannel = await scModule.create({
|
||||||
name: "Webshop",
|
name: "Webshop",
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -172,10 +172,44 @@ medusaIntegrationTestRunner({
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create cart with customer from email", async () => {
|
it("should create cart with customer from email and tax lines", async () => {
|
||||||
|
await setupTaxStructure(taxModule)
|
||||||
|
|
||||||
|
const [product] = await productModule.create([
|
||||||
|
{
|
||||||
|
title: "Test product default tax",
|
||||||
|
variants: [{ title: "Test variant default tax" }],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const [priceSet] = await pricingModule.create([
|
||||||
|
{ prices: [{ amount: 3000, currency_code: "usd" }] },
|
||||||
|
])
|
||||||
|
|
||||||
|
await remoteLink.create([
|
||||||
|
{
|
||||||
|
productService: { variant_id: product.variants[0].id },
|
||||||
|
pricingService: { price_set_id: priceSet.id },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
const created = await api.post(`/store/carts`, {
|
const created = await api.post(`/store/carts`, {
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
email: "tony@stark-industries.com",
|
email: "tony@stark-industries.com",
|
||||||
|
shipping_address: {
|
||||||
|
address_1: "test address 1",
|
||||||
|
address_2: "test address 2",
|
||||||
|
city: "NY",
|
||||||
|
country_code: "US",
|
||||||
|
province: "NY",
|
||||||
|
postal_code: "94016",
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
quantity: 1,
|
||||||
|
variant_id: product.variants[0].id,
|
||||||
|
},
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(created.status).toEqual(200)
|
expect(created.status).toEqual(200)
|
||||||
@@ -188,12 +222,25 @@ medusaIntegrationTestRunner({
|
|||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
email: "tony@stark-industries.com",
|
email: "tony@stark-industries.com",
|
||||||
}),
|
}),
|
||||||
|
items: [
|
||||||
|
expect.objectContaining({
|
||||||
|
tax_lines: [
|
||||||
|
expect.objectContaining({
|
||||||
|
description: "NY Default Rate",
|
||||||
|
code: "NYDEFAULT",
|
||||||
|
rate: 6,
|
||||||
|
provider_id: "system",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
adjustments: [],
|
||||||
|
}),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should create cart with any region", async () => {
|
it("should create cart with any region", async () => {
|
||||||
await regionModuleService.create({
|
await regionModule.create({
|
||||||
name: "US",
|
name: "US",
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
})
|
})
|
||||||
@@ -217,7 +264,7 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should create cart with region currency code", async () => {
|
it("should create cart with region currency code", async () => {
|
||||||
const region = await regionModuleService.create({
|
const region = await regionModule.create({
|
||||||
name: "US",
|
name: "US",
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
})
|
})
|
||||||
@@ -278,6 +325,8 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
describe("POST /store/carts/:id", () => {
|
describe("POST /store/carts/:id", () => {
|
||||||
it("should update a cart with promo codes with a replace action", async () => {
|
it("should update a cart with promo codes with a replace action", async () => {
|
||||||
|
await setupTaxStructure(taxModule)
|
||||||
|
|
||||||
const targetRules = [
|
const targetRules = [
|
||||||
{
|
{
|
||||||
attribute: "product_id",
|
attribute: "product_id",
|
||||||
@@ -293,7 +342,7 @@ medusaIntegrationTestRunner({
|
|||||||
type: "fixed",
|
type: "fixed",
|
||||||
target_type: "items",
|
target_type: "items",
|
||||||
allocation: "each",
|
allocation: "each",
|
||||||
value: "300",
|
value: 300,
|
||||||
apply_to_quantity: 1,
|
apply_to_quantity: 1,
|
||||||
max_quantity: 1,
|
max_quantity: 1,
|
||||||
target_rules: targetRules,
|
target_rules: targetRules,
|
||||||
@@ -307,15 +356,23 @@ medusaIntegrationTestRunner({
|
|||||||
type: "fixed",
|
type: "fixed",
|
||||||
target_type: "items",
|
target_type: "items",
|
||||||
allocation: "across",
|
allocation: "across",
|
||||||
value: "1000",
|
value: 1000,
|
||||||
apply_to_quantity: 1,
|
apply_to_quantity: 1,
|
||||||
target_rules: targetRules,
|
target_rules: targetRules,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const cart = await cartModuleService.create({
|
const cart = await cartModule.create({
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
email: "tony@stark.com",
|
email: "tony@stark.com",
|
||||||
|
shipping_address: {
|
||||||
|
address_1: "test address 1",
|
||||||
|
address_2: "test address 2",
|
||||||
|
city: "NY",
|
||||||
|
country_code: "US",
|
||||||
|
province: "NY",
|
||||||
|
postal_code: "94016",
|
||||||
|
},
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: "item-1",
|
id: "item-1",
|
||||||
@@ -327,7 +384,7 @@ medusaIntegrationTestRunner({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
const [adjustment] = await cartModuleService.addLineItemAdjustments([
|
const [adjustment] = await cartModule.addLineItemAdjustments([
|
||||||
{
|
{
|
||||||
code: appliedPromotion.code!,
|
code: appliedPromotion.code!,
|
||||||
amount: 300,
|
amount: 300,
|
||||||
@@ -353,6 +410,14 @@ medusaIntegrationTestRunner({
|
|||||||
items: [
|
items: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "item-1",
|
id: "item-1",
|
||||||
|
tax_lines: [
|
||||||
|
expect.objectContaining({
|
||||||
|
description: "NY Default Rate",
|
||||||
|
code: "NYDEFAULT",
|
||||||
|
rate: 6,
|
||||||
|
provider_id: "system",
|
||||||
|
}),
|
||||||
|
],
|
||||||
adjustments: [
|
adjustments: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.not.stringContaining(adjustment.id),
|
id: expect.not.stringContaining(adjustment.id),
|
||||||
@@ -363,7 +428,6 @@ medusaIntegrationTestRunner({
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
// Should remove all adjustments from other promo codes
|
// Should remove all adjustments from other promo codes
|
||||||
updated = await api.post(`/store/carts/${cart.id}`, {
|
updated = await api.post(`/store/carts/${cart.id}`, {
|
||||||
promo_codes: [],
|
promo_codes: [],
|
||||||
@@ -377,26 +441,61 @@ medusaIntegrationTestRunner({
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: "item-1",
|
id: "item-1",
|
||||||
adjustments: [],
|
adjustments: [],
|
||||||
|
tax_lines: [
|
||||||
|
expect.objectContaining({
|
||||||
|
description: "NY Default Rate",
|
||||||
|
code: "NYDEFAULT",
|
||||||
|
rate: 6,
|
||||||
|
provider_id: "system",
|
||||||
|
}),
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should update a cart's region, sales channel and customer data", async () => {
|
it("should update a cart's region, sales channel, customer data and tax lines", async () => {
|
||||||
const region = await regionModuleService.create({
|
await setupTaxStructure(taxModule)
|
||||||
|
|
||||||
|
const region = await regionModule.create({
|
||||||
name: "US",
|
name: "US",
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
})
|
})
|
||||||
|
|
||||||
const salesChannel = await scModuleService.create({
|
const salesChannel = await scModule.create({
|
||||||
name: "Webshop",
|
name: "Webshop",
|
||||||
})
|
})
|
||||||
|
|
||||||
const cart = await cartModuleService.create({
|
const cart = await cartModule.create({
|
||||||
currency_code: "eur",
|
currency_code: "eur",
|
||||||
|
email: "tony@stark.com",
|
||||||
|
shipping_address: {
|
||||||
|
address_1: "test address 1",
|
||||||
|
address_2: "test address 2",
|
||||||
|
city: "NY",
|
||||||
|
country_code: "US",
|
||||||
|
province: "NY",
|
||||||
|
postal_code: "94016",
|
||||||
|
},
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "item-1",
|
||||||
|
unit_price: 2000,
|
||||||
|
quantity: 1,
|
||||||
|
title: "Test item",
|
||||||
|
product_id: "prod_tshirt",
|
||||||
|
} as any,
|
||||||
|
],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Manually inserting shipping methods here since the cart does not
|
||||||
|
// currently support it. Move to API when ready.
|
||||||
|
await cartModule.addShippingMethods(cart.id, [
|
||||||
|
{ amount: 500, name: "express" },
|
||||||
|
{ amount: 500, name: "standard" },
|
||||||
|
])
|
||||||
|
|
||||||
let updated = await api.post(`/store/carts/${cart.id}`, {
|
let updated = await api.post(`/store/carts/${cart.id}`, {
|
||||||
region_id: region.id,
|
region_id: region.id,
|
||||||
email: "tony@stark.com",
|
email: "tony@stark.com",
|
||||||
@@ -417,6 +516,53 @@ medusaIntegrationTestRunner({
|
|||||||
email: "tony@stark.com",
|
email: "tony@stark.com",
|
||||||
}),
|
}),
|
||||||
sales_channel_id: salesChannel.id,
|
sales_channel_id: salesChannel.id,
|
||||||
|
shipping_address: expect.objectContaining({
|
||||||
|
city: "NY",
|
||||||
|
country_code: "US",
|
||||||
|
province: "NY",
|
||||||
|
}),
|
||||||
|
shipping_methods: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
shipping_option_id: null,
|
||||||
|
amount: 500,
|
||||||
|
tax_lines: [
|
||||||
|
expect.objectContaining({
|
||||||
|
description: "NY Default Rate",
|
||||||
|
code: "NYDEFAULT",
|
||||||
|
rate: 6,
|
||||||
|
provider_id: "system",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
adjustments: [],
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
shipping_option_id: null,
|
||||||
|
amount: 500,
|
||||||
|
tax_lines: [
|
||||||
|
expect.objectContaining({
|
||||||
|
description: "NY Default Rate",
|
||||||
|
code: "NYDEFAULT",
|
||||||
|
rate: 6,
|
||||||
|
provider_id: "system",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
adjustments: [],
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
items: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "item-1",
|
||||||
|
tax_lines: [
|
||||||
|
expect.objectContaining({
|
||||||
|
description: "NY Default Rate",
|
||||||
|
code: "NYDEFAULT",
|
||||||
|
rate: 6,
|
||||||
|
provider_id: "system",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
adjustments: [],
|
||||||
|
}),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -432,11 +578,21 @@ medusaIntegrationTestRunner({
|
|||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
email: null,
|
email: null,
|
||||||
customer_id: null,
|
customer_id: null,
|
||||||
region: expect.objectContaining({
|
|
||||||
id: region.id,
|
|
||||||
currency_code: "usd",
|
|
||||||
}),
|
|
||||||
sales_channel_id: null,
|
sales_channel_id: null,
|
||||||
|
items: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: "item-1",
|
||||||
|
tax_lines: [
|
||||||
|
expect.objectContaining({
|
||||||
|
description: "NY Default Rate",
|
||||||
|
code: "NYDEFAULT",
|
||||||
|
rate: 6,
|
||||||
|
provider_id: "system",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
adjustments: [],
|
||||||
|
}),
|
||||||
|
],
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
@@ -444,12 +600,12 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
describe("GET /store/carts/:id", () => {
|
describe("GET /store/carts/:id", () => {
|
||||||
it("should create and update a cart", async () => {
|
it("should create and update a cart", async () => {
|
||||||
const region = await regionModuleService.create({
|
const region = await regionModule.create({
|
||||||
name: "US",
|
name: "US",
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
})
|
})
|
||||||
|
|
||||||
const salesChannel = await scModuleService.create({
|
const salesChannel = await scModule.create({
|
||||||
name: "Webshop",
|
name: "Webshop",
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -494,16 +650,16 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
describe("GET /store/carts", () => {
|
describe("GET /store/carts", () => {
|
||||||
it("should get cart", async () => {
|
it("should get cart", async () => {
|
||||||
const region = await regionModuleService.create({
|
const region = await regionModule.create({
|
||||||
name: "US",
|
name: "US",
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
})
|
})
|
||||||
|
|
||||||
const salesChannel = await scModuleService.create({
|
const salesChannel = await scModule.create({
|
||||||
name: "Webshop",
|
name: "Webshop",
|
||||||
})
|
})
|
||||||
|
|
||||||
const cart = await cartModuleService.create({
|
const cart = await cartModule.create({
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
@@ -542,19 +698,40 @@ medusaIntegrationTestRunner({
|
|||||||
|
|
||||||
describe("POST /store/carts/:id/line-items", () => {
|
describe("POST /store/carts/:id/line-items", () => {
|
||||||
it("should add item to cart", async () => {
|
it("should add item to cart", async () => {
|
||||||
const [product] = await productModule.create([
|
await setupTaxStructure(taxModule)
|
||||||
|
|
||||||
|
const customer = await customerModule.create({
|
||||||
|
email: "tony@stark-industries.com",
|
||||||
|
})
|
||||||
|
|
||||||
|
const [productWithSpecialTax] = await productModule.create([
|
||||||
{
|
{
|
||||||
|
// This product ID is setup in the tax structure fixture (setupTaxStructure)
|
||||||
|
id: "product_id_1",
|
||||||
title: "Test product",
|
title: "Test product",
|
||||||
variants: [
|
variants: [{ title: "Test variant" }],
|
||||||
{
|
} as any,
|
||||||
title: "Test variant",
|
])
|
||||||
},
|
|
||||||
],
|
const [productWithDefaultTax] = await productModule.create([
|
||||||
|
{
|
||||||
|
title: "Test product default tax",
|
||||||
|
variants: [{ title: "Test variant default tax" }],
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
const cart = await cartModuleService.create({
|
const cart = await cartModule.create({
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
|
customer_id: customer.id,
|
||||||
|
shipping_address: {
|
||||||
|
customer_id: customer.id,
|
||||||
|
address_1: "test address 1",
|
||||||
|
address_2: "test address 2",
|
||||||
|
city: "SF",
|
||||||
|
country_code: "US",
|
||||||
|
province: "CA",
|
||||||
|
postal_code: "94016",
|
||||||
|
},
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
id: "item-1",
|
id: "item-1",
|
||||||
@@ -573,61 +750,60 @@ medusaIntegrationTestRunner({
|
|||||||
type: "fixed",
|
type: "fixed",
|
||||||
target_type: "items",
|
target_type: "items",
|
||||||
allocation: "across",
|
allocation: "across",
|
||||||
value: "300",
|
value: 300,
|
||||||
apply_to_quantity: 2,
|
apply_to_quantity: 2,
|
||||||
target_rules: [
|
target_rules: [
|
||||||
{
|
{
|
||||||
attribute: "product_id",
|
attribute: "product_id",
|
||||||
operator: "in",
|
operator: "in",
|
||||||
values: ["prod_mat", product.id],
|
values: ["prod_mat", productWithSpecialTax.id],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const [lineItemAdjustment] =
|
const [lineItemAdjustment] = await cartModule.addLineItemAdjustments([
|
||||||
await cartModuleService.addLineItemAdjustments([
|
{
|
||||||
{
|
code: appliedPromotion.code!,
|
||||||
code: appliedPromotion.code!,
|
amount: 300,
|
||||||
amount: 300,
|
item_id: "item-1",
|
||||||
item_id: "item-1",
|
promotion_id: appliedPromotion.id,
|
||||||
promotion_id: appliedPromotion.id,
|
},
|
||||||
},
|
])
|
||||||
])
|
|
||||||
|
|
||||||
const priceSet = await pricingModule.create({
|
const [priceSet, priceSetDefaultTax] = await pricingModule.create([
|
||||||
prices: [
|
{
|
||||||
{
|
prices: [{ amount: 3000, currency_code: "usd" }],
|
||||||
amount: 3000,
|
},
|
||||||
currency_code: "usd",
|
{
|
||||||
},
|
prices: [{ amount: 2000, currency_code: "usd" }],
|
||||||
],
|
},
|
||||||
})
|
])
|
||||||
|
|
||||||
await remoteLink.create([
|
await remoteLink.create([
|
||||||
{
|
{
|
||||||
productService: {
|
productService: {
|
||||||
variant_id: product.variants[0].id,
|
variant_id: productWithSpecialTax.variants[0].id,
|
||||||
},
|
},
|
||||||
pricingService: {
|
pricingService: { price_set_id: priceSet.id },
|
||||||
price_set_id: priceSet.id,
|
},
|
||||||
|
{
|
||||||
|
productService: {
|
||||||
|
variant_id: productWithDefaultTax.variants[0].id,
|
||||||
},
|
},
|
||||||
|
pricingService: { price_set_id: priceSetDefaultTax.id },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[Modules.CART]: { cart_id: cart.id },
|
||||||
|
[Modules.PROMOTION]: { promotion_id: appliedPromotion.id },
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|
||||||
await remoteLink.create({
|
let response = await api.post(`/store/carts/${cart.id}/line-items`, {
|
||||||
[Modules.CART]: { cart_id: cart.id },
|
variant_id: productWithSpecialTax.variants[0].id,
|
||||||
[Modules.PROMOTION]: { promotion_id: appliedPromotion.id },
|
quantity: 1,
|
||||||
})
|
})
|
||||||
|
|
||||||
const response = await api.post(
|
|
||||||
`/store/carts/${cart.id}/line-items`,
|
|
||||||
{
|
|
||||||
variant_id: product.variants[0].id,
|
|
||||||
quantity: 1,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(response.status).toEqual(200)
|
expect(response.status).toEqual(200)
|
||||||
expect(response.data.cart).toEqual(
|
expect(response.data.cart).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -638,6 +814,14 @@ medusaIntegrationTestRunner({
|
|||||||
unit_price: 3000,
|
unit_price: 3000,
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
title: "Test variant",
|
title: "Test variant",
|
||||||
|
tax_lines: [
|
||||||
|
expect.objectContaining({
|
||||||
|
description: "CA Reduced Rate for Products",
|
||||||
|
code: "CAREDUCE_PROD",
|
||||||
|
rate: 3,
|
||||||
|
provider_id: "system",
|
||||||
|
}),
|
||||||
|
],
|
||||||
adjustments: [
|
adjustments: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
code: "PROMOTION_APPLIED",
|
code: "PROMOTION_APPLIED",
|
||||||
@@ -649,6 +833,7 @@ medusaIntegrationTestRunner({
|
|||||||
unit_price: 2000,
|
unit_price: 2000,
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
title: "Test item",
|
title: "Test item",
|
||||||
|
tax_lines: [],
|
||||||
adjustments: [
|
adjustments: [
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: expect.not.stringContaining(lineItemAdjustment.id),
|
id: expect.not.stringContaining(lineItemAdjustment.id),
|
||||||
@@ -660,17 +845,45 @@ medusaIntegrationTestRunner({
|
|||||||
]),
|
]),
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
response = await api.post(`/store/carts/${cart.id}/line-items`, {
|
||||||
|
variant_id: productWithDefaultTax.variants[0].id,
|
||||||
|
quantity: 1,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(response.data.cart).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: cart.id,
|
||||||
|
currency_code: "usd",
|
||||||
|
items: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
unit_price: 2000,
|
||||||
|
quantity: 1,
|
||||||
|
title: "Test variant default tax",
|
||||||
|
tax_lines: [
|
||||||
|
// Uses the california default rate
|
||||||
|
expect.objectContaining({
|
||||||
|
description: "CA Default Rate",
|
||||||
|
code: "CADEFAULT",
|
||||||
|
rate: 5,
|
||||||
|
provider_id: "system",
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("POST /store/carts/:id/payment-collections", () => {
|
describe("POST /store/carts/:id/payment-collections", () => {
|
||||||
it("should create a payment collection for the cart", async () => {
|
it("should create a payment collection for the cart", async () => {
|
||||||
const region = await regionModuleService.create({
|
const region = await regionModule.create({
|
||||||
name: "US",
|
name: "US",
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
})
|
})
|
||||||
|
|
||||||
const cart = await cartModuleService.create({
|
const cart = await cartModule.create({
|
||||||
currency_code: "usd",
|
currency_code: "usd",
|
||||||
region_id: region.id,
|
region_id: region.id,
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./tax"
|
||||||
@@ -0,0 +1,245 @@
|
|||||||
|
import { ITaxModuleService } from "@medusajs/types"
|
||||||
|
|
||||||
|
export const setupTaxStructure = async (service: ITaxModuleService) => {
|
||||||
|
// Setup for this specific test
|
||||||
|
//
|
||||||
|
// Using the following structure to setup tests.
|
||||||
|
// US - default 2%
|
||||||
|
// - Region: CA - default 5%
|
||||||
|
// - Override: Reduced rate (for 3 product ids): 3%
|
||||||
|
// - Override: Reduced rate (for product type): 1%
|
||||||
|
// - Region: NY - default: 6%
|
||||||
|
// - Region: FL - default: 4%
|
||||||
|
//
|
||||||
|
// Denmark - default 25%
|
||||||
|
//
|
||||||
|
// Germany - default 19%
|
||||||
|
// - Override: Reduced Rate (for product type) - 7%
|
||||||
|
//
|
||||||
|
// Canada - default 5%
|
||||||
|
// - Override: Reduced rate (for product id) - 3%
|
||||||
|
// - Override: Reduced rate (for product type) - 3.5%
|
||||||
|
// - Region: QC - default 2%
|
||||||
|
// - Override: Reduced rate (for same product type as country reduced rate): 1%
|
||||||
|
// - Region: BC - default 2%
|
||||||
|
//
|
||||||
|
const [us, dk, de, ca] = await service.createTaxRegions([
|
||||||
|
{
|
||||||
|
country_code: "US",
|
||||||
|
default_tax_rate: { name: "US Default Rate", rate: 2, code: "US_DEF" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
country_code: "DK",
|
||||||
|
default_tax_rate: {
|
||||||
|
name: "Denmark Default Rate",
|
||||||
|
rate: 25,
|
||||||
|
code: "DK_DEF",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
country_code: "DE",
|
||||||
|
default_tax_rate: {
|
||||||
|
code: "DE19",
|
||||||
|
name: "Germany Default Rate",
|
||||||
|
rate: 19,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
country_code: "CA",
|
||||||
|
default_tax_rate: { name: "Canada Default Rate", rate: 5 },
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// Create province regions within the US
|
||||||
|
const [cal, ny, fl, qc, bc] = await service.createTaxRegions([
|
||||||
|
{
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "CA",
|
||||||
|
parent_id: us.id,
|
||||||
|
default_tax_rate: {
|
||||||
|
rate: 5,
|
||||||
|
name: "CA Default Rate",
|
||||||
|
code: "CADEFAULT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "NY",
|
||||||
|
parent_id: us.id,
|
||||||
|
default_tax_rate: {
|
||||||
|
rate: 6,
|
||||||
|
name: "NY Default Rate",
|
||||||
|
code: "NYDEFAULT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
country_code: "US",
|
||||||
|
province_code: "FL",
|
||||||
|
parent_id: us.id,
|
||||||
|
default_tax_rate: {
|
||||||
|
rate: 4,
|
||||||
|
name: "FL Default Rate",
|
||||||
|
code: "FLDEFAULT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
country_code: "CA",
|
||||||
|
province_code: "QC",
|
||||||
|
parent_id: ca.id,
|
||||||
|
default_tax_rate: {
|
||||||
|
rate: 2,
|
||||||
|
name: "QC Default Rate",
|
||||||
|
code: "QCDEFAULT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
country_code: "CA",
|
||||||
|
province_code: "BC",
|
||||||
|
parent_id: ca.id,
|
||||||
|
default_tax_rate: {
|
||||||
|
rate: 2,
|
||||||
|
name: "BC Default Rate",
|
||||||
|
code: "BCDEFAULT",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const [calProd, calType, deType, canProd, canType, qcType] =
|
||||||
|
await service.create([
|
||||||
|
{
|
||||||
|
tax_region_id: cal.id,
|
||||||
|
name: "CA Reduced Rate for Products",
|
||||||
|
rate: 3,
|
||||||
|
code: "CAREDUCE_PROD",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tax_region_id: cal.id,
|
||||||
|
name: "CA Reduced Rate for Product Type",
|
||||||
|
rate: 1,
|
||||||
|
code: "CAREDUCE_TYPE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tax_region_id: de.id,
|
||||||
|
name: "Germany Reduced Rate for Product Type",
|
||||||
|
rate: 7,
|
||||||
|
code: "DEREDUCE_TYPE",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tax_region_id: ca.id,
|
||||||
|
name: "Canada Reduced Rate for Product",
|
||||||
|
rate: 3,
|
||||||
|
code: "CAREDUCE_PROD_CA",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tax_region_id: ca.id,
|
||||||
|
name: "Canada Reduced Rate for Product Type",
|
||||||
|
rate: 3.5,
|
||||||
|
code: "CAREDUCE_TYPE_CA",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
tax_region_id: qc.id,
|
||||||
|
name: "QC Reduced Rate for Product Type",
|
||||||
|
rate: 1,
|
||||||
|
code: "QCREDUCE_TYPE",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
// Create tax rate rules for specific products and product types
|
||||||
|
await service.createTaxRateRules([
|
||||||
|
{
|
||||||
|
reference: "product",
|
||||||
|
reference_id: "product_id_1",
|
||||||
|
tax_rate_id: calProd.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reference: "product",
|
||||||
|
reference_id: "product_id_2",
|
||||||
|
tax_rate_id: calProd.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reference: "product",
|
||||||
|
reference_id: "product_id_3",
|
||||||
|
tax_rate_id: calProd.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reference: "product_type",
|
||||||
|
reference_id: "product_type_id_1",
|
||||||
|
tax_rate_id: calType.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reference: "product_type",
|
||||||
|
reference_id: "product_type_id_2",
|
||||||
|
tax_rate_id: deType.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reference: "product",
|
||||||
|
reference_id: "product_id_4",
|
||||||
|
tax_rate_id: canProd.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reference: "product_type",
|
||||||
|
reference_id: "product_type_id_3",
|
||||||
|
tax_rate_id: canType.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reference: "product_type",
|
||||||
|
reference_id: "product_type_id_3",
|
||||||
|
tax_rate_id: qcType.id,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
return {
|
||||||
|
us: {
|
||||||
|
country: us,
|
||||||
|
children: {
|
||||||
|
cal: {
|
||||||
|
province: cal,
|
||||||
|
overrides: {
|
||||||
|
calProd,
|
||||||
|
calType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ny: {
|
||||||
|
province: ny,
|
||||||
|
overrides: {},
|
||||||
|
},
|
||||||
|
fl: {
|
||||||
|
province: fl,
|
||||||
|
overrides: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
overrides: {},
|
||||||
|
},
|
||||||
|
dk: {
|
||||||
|
country: dk,
|
||||||
|
children: {},
|
||||||
|
overrides: {},
|
||||||
|
},
|
||||||
|
de: {
|
||||||
|
country: de,
|
||||||
|
children: {},
|
||||||
|
overrides: {
|
||||||
|
deType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ca: {
|
||||||
|
country: ca,
|
||||||
|
children: {
|
||||||
|
qc: {
|
||||||
|
province: qc,
|
||||||
|
overrides: {
|
||||||
|
qcType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
bc: {
|
||||||
|
province: bc,
|
||||||
|
overrides: {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
overrides: {
|
||||||
|
canProd,
|
||||||
|
canType,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import {
|
||||||
|
CartLineItemDTO,
|
||||||
|
CartShippingMethodDTO,
|
||||||
|
CartWorkflowDTO,
|
||||||
|
ITaxModuleService,
|
||||||
|
ItemTaxLineDTO,
|
||||||
|
ShippingTaxLineDTO,
|
||||||
|
TaxCalculationContext,
|
||||||
|
TaxableItemDTO,
|
||||||
|
TaxableShippingDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
import { ModuleRegistrationName } from "../../../../../modules-sdk/dist"
|
||||||
|
|
||||||
|
interface StepInput {
|
||||||
|
cart: CartWorkflowDTO
|
||||||
|
items: CartLineItemDTO[]
|
||||||
|
shipping_methods: CartShippingMethodDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeTaxModuleContext(
|
||||||
|
cart: CartWorkflowDTO
|
||||||
|
): TaxCalculationContext | null {
|
||||||
|
const address = cart.shipping_address
|
||||||
|
|
||||||
|
if (!address || !address.country_code) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
let customer = cart.customer
|
||||||
|
? {
|
||||||
|
id: cart.customer.id,
|
||||||
|
email: cart.customer.email,
|
||||||
|
customer_groups: cart.customer.groups?.map((g) => g.id) || [],
|
||||||
|
}
|
||||||
|
: undefined
|
||||||
|
|
||||||
|
return {
|
||||||
|
address: {
|
||||||
|
country_code: address.country_code,
|
||||||
|
province_code: address.province,
|
||||||
|
address_1: address.address_1,
|
||||||
|
address_2: address.address_2,
|
||||||
|
city: address.city,
|
||||||
|
postal_code: address.postal_code,
|
||||||
|
},
|
||||||
|
customer,
|
||||||
|
// TODO: Should probably come in from order module, defaulting to false
|
||||||
|
is_return: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeLineItemsForTax(
|
||||||
|
cart: CartWorkflowDTO,
|
||||||
|
items: CartLineItemDTO[]
|
||||||
|
): TaxableItemDTO[] {
|
||||||
|
return items.map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
product_id: item.product_id!,
|
||||||
|
product_name: item.variant_title,
|
||||||
|
product_sku: item.variant_sku,
|
||||||
|
product_type: item.product_type,
|
||||||
|
product_type_id: item.product_type,
|
||||||
|
quantity: item.quantity,
|
||||||
|
unit_price: item.unit_price,
|
||||||
|
currency_code: cart.currency_code,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeLineItemsForShipping(
|
||||||
|
cart: CartWorkflowDTO,
|
||||||
|
shippingMethods: CartShippingMethodDTO[]
|
||||||
|
): TaxableShippingDTO[] {
|
||||||
|
return shippingMethods.map((shippingMethod) => ({
|
||||||
|
id: shippingMethod.id,
|
||||||
|
shipping_option_id: shippingMethod.shipping_option_id!,
|
||||||
|
unit_price: shippingMethod.amount,
|
||||||
|
currency_code: cart.currency_code,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getItemTaxLinesStepId = "get-item-tax-lines"
|
||||||
|
export const getItemTaxLinesStep = createStep(
|
||||||
|
getItemTaxLinesStepId,
|
||||||
|
async (data: StepInput, { container }) => {
|
||||||
|
const { cart, items, shipping_methods: shippingMethods } = data
|
||||||
|
const taxService = container.resolve<ITaxModuleService>(
|
||||||
|
ModuleRegistrationName.TAX
|
||||||
|
)
|
||||||
|
|
||||||
|
const taxContext = normalizeTaxModuleContext(cart)
|
||||||
|
|
||||||
|
if (!taxContext) {
|
||||||
|
return new StepResponse({
|
||||||
|
lineItemTaxLines: [],
|
||||||
|
shippingMethodsTaxLines: [],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const lineItemTaxLines = (await taxService.getTaxLines(
|
||||||
|
normalizeLineItemsForTax(cart, items),
|
||||||
|
taxContext
|
||||||
|
)) as ItemTaxLineDTO[]
|
||||||
|
|
||||||
|
const shippingMethodsTaxLines = (await taxService.getTaxLines(
|
||||||
|
normalizeLineItemsForShipping(cart, shippingMethods),
|
||||||
|
taxContext
|
||||||
|
)) as ShippingTaxLineDTO[]
|
||||||
|
|
||||||
|
return new StepResponse({
|
||||||
|
lineItemTaxLines,
|
||||||
|
shippingMethodsTaxLines,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -6,12 +6,15 @@ export * from "./find-one-or-any-region"
|
|||||||
export * from "./find-or-create-customer"
|
export * from "./find-or-create-customer"
|
||||||
export * from "./find-sales-channel"
|
export * from "./find-sales-channel"
|
||||||
export * from "./get-actions-to-compute-from-promotions"
|
export * from "./get-actions-to-compute-from-promotions"
|
||||||
|
export * from "./get-item-tax-lines"
|
||||||
export * from "./get-variant-price-sets"
|
export * from "./get-variant-price-sets"
|
||||||
export * from "./get-variants"
|
export * from "./get-variants"
|
||||||
export * from "./prepare-adjustments-from-promotion-actions"
|
export * from "./prepare-adjustments-from-promotion-actions"
|
||||||
export * from "./remove-line-item-adjustments"
|
export * from "./remove-line-item-adjustments"
|
||||||
export * from "./remove-shipping-method-adjustments"
|
export * from "./remove-shipping-method-adjustments"
|
||||||
export * from "./retrieve-cart"
|
export * from "./retrieve-cart"
|
||||||
|
export * from "./retrieve-cart-with-links"
|
||||||
|
export * from "./set-tax-lines-for-items"
|
||||||
export * from "./update-cart-promotions"
|
export * from "./update-cart-promotions"
|
||||||
export * from "./update-carts"
|
export * from "./update-carts"
|
||||||
export * from "./validate-variants-existence"
|
export * from "./validate-variants-existence"
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { LinkModuleUtils, Modules } from "@medusajs/modules-sdk"
|
||||||
|
import { CartWorkflowDTO } from "@medusajs/types"
|
||||||
|
import { isObject, remoteQueryObjectFromString } from "@medusajs/utils"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
interface StepInput {
|
||||||
|
cart_or_cart_id: string | CartWorkflowDTO
|
||||||
|
fields: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const retrieveCartWithLinksStepId = "retrieve-cart-with-links"
|
||||||
|
export const retrieveCartWithLinksStep = createStep(
|
||||||
|
retrieveCartWithLinksStepId,
|
||||||
|
async (data: StepInput, { container }) => {
|
||||||
|
const { cart_or_cart_id: cartOrCartId, fields } = data
|
||||||
|
|
||||||
|
if (isObject(cartOrCartId)) {
|
||||||
|
return new StepResponse(cartOrCartId)
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = cartOrCartId
|
||||||
|
const remoteQuery = container.resolve(LinkModuleUtils.REMOTE_QUERY)
|
||||||
|
const query = remoteQueryObjectFromString({
|
||||||
|
entryPoint: Modules.CART,
|
||||||
|
fields,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [cart] = await remoteQuery(query, { cart: { id } })
|
||||||
|
|
||||||
|
return new StepResponse(cart)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
CartWorkflowDTO,
|
||||||
|
CreateLineItemTaxLineDTO,
|
||||||
|
CreateShippingMethodTaxLineDTO,
|
||||||
|
ICartModuleService,
|
||||||
|
ItemTaxLineDTO,
|
||||||
|
ShippingTaxLineDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
|
interface StepInput {
|
||||||
|
cart: CartWorkflowDTO
|
||||||
|
item_tax_lines: ItemTaxLineDTO[]
|
||||||
|
shipping_tax_lines: ShippingTaxLineDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setTaxLinesForItemsStepId = "set-tax-lines-for-items"
|
||||||
|
export const setTaxLinesForItemsStep = createStep(
|
||||||
|
setTaxLinesForItemsStepId,
|
||||||
|
async (data: StepInput, { container }) => {
|
||||||
|
const { cart, item_tax_lines, shipping_tax_lines } = data
|
||||||
|
const cartService = container.resolve<ICartModuleService>(
|
||||||
|
ModuleRegistrationName.CART
|
||||||
|
)
|
||||||
|
|
||||||
|
const getShippingTaxLinesPromise =
|
||||||
|
await cartService.listShippingMethodTaxLines({
|
||||||
|
shipping_method_id: shipping_tax_lines.map((t) => t.shipping_line_id),
|
||||||
|
})
|
||||||
|
|
||||||
|
const getItemTaxLinesPromise = await cartService.listLineItemTaxLines({
|
||||||
|
item_id: item_tax_lines.map((t) => t.line_item_id),
|
||||||
|
})
|
||||||
|
|
||||||
|
const itemsTaxLinesData = normalizeItemTaxLinesForCart(item_tax_lines)
|
||||||
|
const setItemTaxLinesPromise = itemsTaxLinesData.length
|
||||||
|
? cartService.setLineItemTaxLines(cart.id, itemsTaxLinesData)
|
||||||
|
: 0
|
||||||
|
|
||||||
|
const shippingTaxLinesData =
|
||||||
|
normalizeShippingTaxLinesForCart(shipping_tax_lines)
|
||||||
|
const setShippingTaxLinesPromise = shippingTaxLinesData.length
|
||||||
|
? await cartService.setShippingMethodTaxLines(
|
||||||
|
cart.id,
|
||||||
|
shippingTaxLinesData
|
||||||
|
)
|
||||||
|
: 0
|
||||||
|
|
||||||
|
const [existingShippingMethodTaxLines, existingLineItemTaxLines] =
|
||||||
|
await Promise.all([
|
||||||
|
getShippingTaxLinesPromise,
|
||||||
|
getItemTaxLinesPromise,
|
||||||
|
setItemTaxLinesPromise,
|
||||||
|
setShippingTaxLinesPromise,
|
||||||
|
])
|
||||||
|
|
||||||
|
return new StepResponse(null, {
|
||||||
|
cart,
|
||||||
|
existingLineItemTaxLines,
|
||||||
|
existingShippingMethodTaxLines,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
async (revertData, { container }) => {
|
||||||
|
if (!revertData) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { cart, existingLineItemTaxLines, existingShippingMethodTaxLines } =
|
||||||
|
revertData
|
||||||
|
|
||||||
|
const cartService = container.resolve<ICartModuleService>(
|
||||||
|
ModuleRegistrationName.CART
|
||||||
|
)
|
||||||
|
|
||||||
|
if (existingLineItemTaxLines) {
|
||||||
|
await cartService.setLineItemTaxLines(
|
||||||
|
cart.id,
|
||||||
|
existingLineItemTaxLines.map((taxLine) => ({
|
||||||
|
description: taxLine.description,
|
||||||
|
tax_rate_id: taxLine.tax_rate_id,
|
||||||
|
code: taxLine.code,
|
||||||
|
rate: taxLine.rate,
|
||||||
|
provider_id: taxLine.provider_id,
|
||||||
|
item_id: taxLine.item_id,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
await cartService.setShippingMethodTaxLines(
|
||||||
|
cart.id,
|
||||||
|
existingShippingMethodTaxLines.map((taxLine) => ({
|
||||||
|
description: taxLine.description,
|
||||||
|
tax_rate_id: taxLine.tax_rate_id,
|
||||||
|
code: taxLine.code,
|
||||||
|
rate: taxLine.rate,
|
||||||
|
provider_id: taxLine.provider_id,
|
||||||
|
shipping_method_id: taxLine.shipping_method_id,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
function normalizeItemTaxLinesForCart(
|
||||||
|
taxLines: ItemTaxLineDTO[]
|
||||||
|
): CreateLineItemTaxLineDTO[] {
|
||||||
|
return taxLines.map((taxLine) => ({
|
||||||
|
description: taxLine.name,
|
||||||
|
tax_rate_id: taxLine.rate_id,
|
||||||
|
code: taxLine.code!,
|
||||||
|
rate: taxLine.rate!,
|
||||||
|
provider_id: taxLine.provider_id,
|
||||||
|
item_id: taxLine.line_item_id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeShippingTaxLinesForCart(
|
||||||
|
taxLines: ShippingTaxLineDTO[]
|
||||||
|
): CreateShippingMethodTaxLineDTO[] {
|
||||||
|
return taxLines.map((taxLine) => ({
|
||||||
|
description: taxLine.name,
|
||||||
|
tax_rate_id: taxLine.rate_id,
|
||||||
|
code: taxLine.code!,
|
||||||
|
rate: taxLine.rate!,
|
||||||
|
provider_id: taxLine.provider_id,
|
||||||
|
shipping_method_id: taxLine.shipping_line_id,
|
||||||
|
}))
|
||||||
|
}
|
||||||
@@ -48,6 +48,6 @@ export const updateCartsStep = createStep(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
await cartModule.update(dataToUpdate)
|
return await cartModule.update(dataToUpdate)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
import {
|
||||||
|
CartLineItemDTO,
|
||||||
|
CartShippingMethodDTO,
|
||||||
|
CartWorkflowDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
import { updateTaxLinesWorkflow } from "../workflows"
|
||||||
|
|
||||||
|
interface StepInput {
|
||||||
|
cart_or_cart_id: CartWorkflowDTO | string
|
||||||
|
items?: CartLineItemDTO[]
|
||||||
|
shipping_methods?: CartShippingMethodDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateTaxLinesStepId = "update-tax-lines-step"
|
||||||
|
export const updateTaxLinesStep = createStep(
|
||||||
|
updateTaxLinesStepId,
|
||||||
|
async (input: StepInput, { container }) => {
|
||||||
|
// TODO: manually trigger rollback on workflow when step fails
|
||||||
|
await updateTaxLinesWorkflow(container).run({ input })
|
||||||
|
|
||||||
|
return new StepResponse(null)
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -16,12 +16,8 @@ export const validateVariantsExistStep = createStep(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const variants = await productModuleService.listVariants(
|
const variants = await productModuleService.listVariants(
|
||||||
{
|
{ id: data.variantIds },
|
||||||
id: data.variantIds,
|
{ select: ["id"] }
|
||||||
},
|
|
||||||
{
|
|
||||||
select: ["id"],
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const variantIdToData = new Set(variants.map((v) => v.id))
|
const variantIdToData = new Set(variants.map((v) => v.id))
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ interface Input {
|
|||||||
|
|
||||||
export function prepareLineItemData(data: Input) {
|
export function prepareLineItemData(data: Input) {
|
||||||
const { variant, unitPrice, quantity, metadata, cartId } = data
|
const { variant, unitPrice, quantity, metadata, cartId } = data
|
||||||
|
|
||||||
const lineItem: any = {
|
const lineItem: any = {
|
||||||
quantity,
|
quantity,
|
||||||
title: variant.title,
|
title: variant.title,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
validateVariantsExistStep,
|
validateVariantsExistStep,
|
||||||
} from "../steps"
|
} from "../steps"
|
||||||
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
||||||
|
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||||
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
||||||
|
|
||||||
// TODO: The AddToCartWorkflow are missing the following steps:
|
// TODO: The AddToCartWorkflow are missing the following steps:
|
||||||
@@ -25,12 +26,12 @@ export const addToCartWorkflowId = "add-to-cart"
|
|||||||
export const addToCartWorkflow = createWorkflow(
|
export const addToCartWorkflow = createWorkflow(
|
||||||
addToCartWorkflowId,
|
addToCartWorkflowId,
|
||||||
(input: WorkflowData<AddToCartWorkflowInputDTO>) => {
|
(input: WorkflowData<AddToCartWorkflowInputDTO>) => {
|
||||||
const variantIds = transform({ input }, (data) => {
|
const variantIds = validateVariantsExistStep({
|
||||||
return (data.input.items ?? []).map((i) => i.variant_id)
|
variantIds: transform({ input }, (data) => {
|
||||||
|
return (data.input.items ?? []).map((i) => i.variant_id)
|
||||||
|
}),
|
||||||
})
|
})
|
||||||
|
|
||||||
validateVariantsExistStep({ variantIds })
|
|
||||||
|
|
||||||
// TODO: This is on par with the context used in v1.*, but we can be more flexible.
|
// TODO: This is on par with the context used in v1.*, but we can be more flexible.
|
||||||
const pricingContext = transform({ cart: input.cart }, (data) => {
|
const pricingContext = transform({ cart: input.cart }, (data) => {
|
||||||
return {
|
return {
|
||||||
@@ -45,31 +46,55 @@ export const addToCartWorkflow = createWorkflow(
|
|||||||
context: pricingContext,
|
context: pricingContext,
|
||||||
})
|
})
|
||||||
|
|
||||||
const variants = getVariantsStep({
|
const variants = getVariantsStep(
|
||||||
filter: { id: variantIds },
|
transform({ variantIds }, (data) => {
|
||||||
})
|
return {
|
||||||
|
filter: { id: data.variantIds },
|
||||||
const lineItems = transform(
|
config: {
|
||||||
{ priceSets, input, variants, cart: input.cart },
|
select: [
|
||||||
(data) => {
|
"id",
|
||||||
const items = (data.input.items ?? []).map((item) => {
|
"title",
|
||||||
const variant = data.variants.find((v) => v.id === item.variant_id)!
|
"sku",
|
||||||
|
"barcode",
|
||||||
return prepareLineItemData({
|
"product.id",
|
||||||
variant: variant,
|
"product.title",
|
||||||
unitPrice: data.priceSets[item.variant_id].calculated_amount,
|
"product.description",
|
||||||
quantity: item.quantity,
|
"product.subtitle",
|
||||||
metadata: item?.metadata ?? {},
|
"product.thumbnail",
|
||||||
cartId: data.cart.id,
|
"product.type",
|
||||||
}) as CreateLineItemForCartDTO
|
"product.collection",
|
||||||
})
|
"product.handle",
|
||||||
|
],
|
||||||
return items
|
relations: ["product"],
|
||||||
}
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const lineItems = transform({ priceSets, input, variants }, (data) => {
|
||||||
|
const items = (data.input.items ?? []).map((item) => {
|
||||||
|
const variant = data.variants.find((v) => v.id === item.variant_id)!
|
||||||
|
|
||||||
|
return prepareLineItemData({
|
||||||
|
variant: variant,
|
||||||
|
unitPrice: data.priceSets[item.variant_id].calculated_amount,
|
||||||
|
quantity: item.quantity,
|
||||||
|
metadata: item?.metadata ?? {},
|
||||||
|
cartId: data.input.cart.id,
|
||||||
|
}) as CreateLineItemForCartDTO
|
||||||
|
})
|
||||||
|
|
||||||
|
return items
|
||||||
|
})
|
||||||
|
|
||||||
const items = addToCartStep({ items: lineItems })
|
const items = addToCartStep({ items: lineItems })
|
||||||
|
|
||||||
|
updateTaxLinesStep({
|
||||||
|
cart_or_cart_id: input.cart,
|
||||||
|
items,
|
||||||
|
// TODO: add shipping methods here when its ready
|
||||||
|
})
|
||||||
|
|
||||||
refreshCartPromotionsStep({ id: input.cart.id })
|
refreshCartPromotionsStep({ id: input.cart.id })
|
||||||
|
|
||||||
return items
|
return items
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
getVariantsStep,
|
getVariantsStep,
|
||||||
validateVariantsExistStep,
|
validateVariantsExistStep,
|
||||||
} from "../steps"
|
} from "../steps"
|
||||||
|
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||||
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
||||||
|
|
||||||
// TODO: The UpdateLineItemsWorkflow are missing the following steps:
|
// TODO: The UpdateLineItemsWorkflow are missing the following steps:
|
||||||
@@ -83,26 +84,30 @@ export const createCartWorkflow = createWorkflow(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const variants = getVariantsStep({
|
const variants = getVariantsStep(
|
||||||
filter: { id: variantIds },
|
transform({ variantIds }, (data) => {
|
||||||
config: {
|
return {
|
||||||
select: [
|
filter: { id: data.variantIds },
|
||||||
"id",
|
config: {
|
||||||
"title",
|
select: [
|
||||||
"sku",
|
"id",
|
||||||
"barcode",
|
"title",
|
||||||
"product.id",
|
"sku",
|
||||||
"product.title",
|
"barcode",
|
||||||
"product.description",
|
"product.id",
|
||||||
"product.subtitle",
|
"product.title",
|
||||||
"product.thumbnail",
|
"product.description",
|
||||||
"product.type",
|
"product.subtitle",
|
||||||
"product.collection",
|
"product.thumbnail",
|
||||||
"product.handle",
|
"product.type",
|
||||||
],
|
"product.collection",
|
||||||
relations: ["product"],
|
"product.handle",
|
||||||
},
|
],
|
||||||
})
|
relations: ["product"],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
const lineItems = transform({ priceSets, input, variants }, (data) => {
|
const lineItems = transform({ priceSets, input, variants }, (data) => {
|
||||||
const items = (data.input.items ?? []).map((item) => {
|
const items = (data.input.items ?? []).map((item) => {
|
||||||
@@ -127,9 +132,10 @@ export const createCartWorkflow = createWorkflow(
|
|||||||
})
|
})
|
||||||
|
|
||||||
const carts = createCartsStep([cartToCreate])
|
const carts = createCartsStep([cartToCreate])
|
||||||
|
|
||||||
const cart = transform({ carts }, (data) => data.carts?.[0])
|
const cart = transform({ carts }, (data) => data.carts?.[0])
|
||||||
|
|
||||||
|
updateTaxLinesStep({ cart_or_cart_id: cart.id })
|
||||||
|
|
||||||
return cart
|
return cart
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,3 +5,4 @@ export * from "./refresh-payment-collection"
|
|||||||
export * from "./update-cart"
|
export * from "./update-cart"
|
||||||
export * from "./update-cart-promotions"
|
export * from "./update-cart-promotions"
|
||||||
export * from "./update-line-item-in-cart"
|
export * from "./update-line-item-in-cart"
|
||||||
|
export * from "./update-tax-lines"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CartDTO, UpdateCartWorkflowInputDTO } from "@medusajs/types"
|
import { UpdateCartWorkflowInputDTO } from "@medusajs/types"
|
||||||
import { PromotionActions, isPresent } from "@medusajs/utils"
|
import { PromotionActions, isPresent } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
WorkflowData,
|
WorkflowData,
|
||||||
@@ -10,16 +10,16 @@ import {
|
|||||||
findOneOrAnyRegionStep,
|
findOneOrAnyRegionStep,
|
||||||
findOrCreateCustomerStep,
|
findOrCreateCustomerStep,
|
||||||
findSalesChannelStep,
|
findSalesChannelStep,
|
||||||
retrieveCartStep,
|
|
||||||
updateCartsStep,
|
updateCartsStep,
|
||||||
} from "../steps"
|
} from "../steps"
|
||||||
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
||||||
|
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||||
import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection"
|
import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection"
|
||||||
|
|
||||||
export const updateCartWorkflowId = "update-cart"
|
export const updateCartWorkflowId = "update-cart"
|
||||||
export const updateCartWorkflow = createWorkflow(
|
export const updateCartWorkflow = createWorkflow(
|
||||||
updateCartWorkflowId,
|
updateCartWorkflowId,
|
||||||
(input: WorkflowData<UpdateCartWorkflowInputDTO>): WorkflowData<CartDTO> => {
|
(input: WorkflowData<UpdateCartWorkflowInputDTO>): WorkflowData<void> => {
|
||||||
const [salesChannel, region, customerData] = parallelize(
|
const [salesChannel, region, customerData] = parallelize(
|
||||||
findSalesChannelStep({
|
findSalesChannelStep({
|
||||||
salesChannelId: input.sales_channel_id,
|
salesChannelId: input.sales_channel_id,
|
||||||
@@ -61,8 +61,9 @@ export const updateCartWorkflow = createWorkflow(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
updateCartsStep([cartInput])
|
const carts = updateCartsStep([cartInput])
|
||||||
|
|
||||||
|
updateTaxLinesStep({ cart_or_cart_id: carts[0].id })
|
||||||
refreshCartPromotionsStep({
|
refreshCartPromotionsStep({
|
||||||
id: input.id,
|
id: input.id,
|
||||||
promo_codes: input.promo_codes,
|
promo_codes: input.promo_codes,
|
||||||
@@ -72,19 +73,5 @@ export const updateCartWorkflow = createWorkflow(
|
|||||||
refreshPaymentCollectionForCartStep({
|
refreshPaymentCollectionForCartStep({
|
||||||
cart_id: input.id,
|
cart_id: input.id,
|
||||||
})
|
})
|
||||||
|
|
||||||
const retrieveCartInput = {
|
|
||||||
id: input.id,
|
|
||||||
config: {
|
|
||||||
relations: [
|
|
||||||
"items",
|
|
||||||
"items.adjustments",
|
|
||||||
"shipping_methods",
|
|
||||||
"shipping_methods.adjustments",
|
|
||||||
],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return retrieveCartStep(retrieveCartInput)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import {
|
||||||
|
CartLineItemDTO,
|
||||||
|
CartShippingMethodDTO,
|
||||||
|
CartWorkflowDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import {
|
||||||
|
WorkflowData,
|
||||||
|
createWorkflow,
|
||||||
|
transform,
|
||||||
|
} from "@medusajs/workflows-sdk"
|
||||||
|
import {
|
||||||
|
getItemTaxLinesStep,
|
||||||
|
retrieveCartWithLinksStep,
|
||||||
|
setTaxLinesForItemsStep,
|
||||||
|
} from "../steps"
|
||||||
|
|
||||||
|
const cartFields = [
|
||||||
|
"id",
|
||||||
|
"currency_code",
|
||||||
|
"email",
|
||||||
|
"items.id",
|
||||||
|
"items.variant_id",
|
||||||
|
"items.product_id",
|
||||||
|
"items.product_title",
|
||||||
|
"items.product_description",
|
||||||
|
"items.product_subtitle",
|
||||||
|
"items.product_type",
|
||||||
|
"items.product_collection",
|
||||||
|
"items.product_handle",
|
||||||
|
"items.variant_sku",
|
||||||
|
"items.variant_barcode",
|
||||||
|
"items.variant_title",
|
||||||
|
"items.title",
|
||||||
|
"items.quantity",
|
||||||
|
"items.unit_price",
|
||||||
|
"items.tax_lines.id",
|
||||||
|
"items.tax_lines.description",
|
||||||
|
"items.tax_lines.code",
|
||||||
|
"items.tax_lines.rate",
|
||||||
|
"items.tax_lines.provider_id",
|
||||||
|
"shipping_methods.tax_lines.id",
|
||||||
|
"shipping_methods.tax_lines.description",
|
||||||
|
"shipping_methods.tax_lines.code",
|
||||||
|
"shipping_methods.tax_lines.rate",
|
||||||
|
"shipping_methods.tax_lines.provider_id",
|
||||||
|
"shipping_methods.shipping_option_id",
|
||||||
|
"shipping_methods.amount",
|
||||||
|
"customer.id",
|
||||||
|
"customer.email",
|
||||||
|
"customer.groups.id",
|
||||||
|
"shipping_address.id",
|
||||||
|
"shipping_address.address_1",
|
||||||
|
"shipping_address.address_2",
|
||||||
|
"shipping_address.city",
|
||||||
|
"shipping_address.postal_code",
|
||||||
|
"shipping_address.country_code",
|
||||||
|
"shipping_address.region_code",
|
||||||
|
"shipping_address.province",
|
||||||
|
]
|
||||||
|
|
||||||
|
type WorkflowInput = {
|
||||||
|
cart_or_cart_id: string | CartWorkflowDTO
|
||||||
|
items?: CartLineItemDTO[]
|
||||||
|
shipping_methods?: CartShippingMethodDTO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const updateTaxLinesWorkflowId = "update-tax-lines"
|
||||||
|
export const updateTaxLinesWorkflow = createWorkflow(
|
||||||
|
updateTaxLinesWorkflowId,
|
||||||
|
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
|
||||||
|
const cart = retrieveCartWithLinksStep({
|
||||||
|
cart_or_cart_id: input.cart_or_cart_id,
|
||||||
|
fields: cartFields,
|
||||||
|
})
|
||||||
|
|
||||||
|
const taxLineItems = getItemTaxLinesStep(
|
||||||
|
transform({ input, cart }, (data) => ({
|
||||||
|
cart: data.cart,
|
||||||
|
items: data.input.items || data.cart.items,
|
||||||
|
shipping_methods:
|
||||||
|
data.input.shipping_methods || data.cart.shipping_methods,
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
setTaxLinesForItemsStep({
|
||||||
|
cart,
|
||||||
|
item_tax_lines: taxLineItems.lineItemTaxLines,
|
||||||
|
shipping_tax_lines: taxLineItems.shippingMethodsTaxLines,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
)
|
||||||
@@ -1,18 +1,20 @@
|
|||||||
import { addToCartWorkflow } from "@medusajs/core-flows"
|
import { addToCartWorkflow } from "@medusajs/core-flows"
|
||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
import { LinkModuleUtils, Modules } from "@medusajs/modules-sdk"
|
||||||
import { ICartModuleService } from "@medusajs/types"
|
|
||||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||||
import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
|
import { MedusaRequest, MedusaResponse } from "../../../../../types/routing"
|
||||||
import { defaultStoreCartFields } from "../../query-config"
|
import { defaultStoreCartFields } from "../../query-config"
|
||||||
import { StorePostCartsCartLineItemsReq } from "./validators"
|
import { StorePostCartsCartLineItemsReq } from "./validators"
|
||||||
|
|
||||||
export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
|
export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||||
const cartModuleService = req.scope.resolve<ICartModuleService>(
|
const remoteQuery = req.scope.resolve(LinkModuleUtils.REMOTE_QUERY)
|
||||||
ModuleRegistrationName.CART
|
|
||||||
)
|
|
||||||
|
|
||||||
const cart = await cartModuleService.retrieve(req.params.id, {
|
const query = remoteQueryObjectFromString({
|
||||||
select: ["id", "region_id", "currency_code"],
|
entryPoint: Modules.CART,
|
||||||
|
fields: defaultStoreCartFields,
|
||||||
|
})
|
||||||
|
|
||||||
|
const [cart] = await remoteQuery(query, {
|
||||||
|
cart: { id: req.params.id },
|
||||||
})
|
})
|
||||||
|
|
||||||
const workflowInput = {
|
const workflowInput = {
|
||||||
@@ -29,13 +31,6 @@ export const POST = async (req: MedusaRequest, res: MedusaResponse) => {
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
|
||||||
|
|
||||||
const query = remoteQueryObjectFromString({
|
|
||||||
entryPoint: "cart",
|
|
||||||
fields: defaultStoreCartFields,
|
|
||||||
})
|
|
||||||
|
|
||||||
const [updatedCart] = await remoteQuery(query, {
|
const [updatedCart] = await remoteQuery(query, {
|
||||||
cart: { id: req.params.id },
|
cart: { id: req.params.id },
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { updateCartWorkflow } from "@medusajs/core-flows"
|
import { updateCartWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { LinkModuleUtils, Modules } from "@medusajs/modules-sdk"
|
||||||
import { UpdateCartDataDTO } from "@medusajs/types"
|
import { UpdateCartDataDTO } from "@medusajs/types"
|
||||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||||
|
|
||||||
@@ -6,12 +7,11 @@ import { MedusaRequest, MedusaResponse } from "../../../../types/routing"
|
|||||||
import { defaultStoreCartFields } from "../query-config"
|
import { defaultStoreCartFields } from "../query-config"
|
||||||
|
|
||||||
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
|
||||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
const remoteQuery = req.scope.resolve(LinkModuleUtils.REMOTE_QUERY)
|
||||||
|
|
||||||
const variables = { id: req.params.id }
|
const variables = { id: req.params.id }
|
||||||
|
|
||||||
const query = remoteQueryObjectFromString({
|
const query = remoteQueryObjectFromString({
|
||||||
entryPoint: "cart",
|
entryPoint: Modules.CART,
|
||||||
fields: defaultStoreCartFields,
|
fields: defaultStoreCartFields,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -38,16 +38,16 @@ export const POST = async (
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
const remoteQuery = req.scope.resolve(LinkModuleUtils.REMOTE_QUERY)
|
||||||
|
|
||||||
const query = remoteQueryObjectFromString({
|
const query = remoteQueryObjectFromString({
|
||||||
entryPoint: "cart",
|
entryPoint: Modules.CART,
|
||||||
fields: defaultStoreCartFields,
|
fields: defaultStoreCartFields,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [updatedCart] = await remoteQuery(query, {
|
const [cart] = await remoteQuery(query, {
|
||||||
cart: { id: req.params.id },
|
cart: { id: req.params.id },
|
||||||
})
|
})
|
||||||
|
|
||||||
res.status(200).json({ cart: updatedCart })
|
res.status(200).json({ cart })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,40 @@ export const defaultStoreCartFields = [
|
|||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
"items.id",
|
"items.id",
|
||||||
|
"items.variant_id",
|
||||||
|
"items.product_id",
|
||||||
|
"items.product_title",
|
||||||
|
"items.product_description",
|
||||||
|
"items.product_subtitle",
|
||||||
|
"items.product_type",
|
||||||
|
"items.product_collection",
|
||||||
|
"items.product_handle",
|
||||||
|
"items.variant_sku",
|
||||||
|
"items.variant_barcode",
|
||||||
|
"items.variant_title",
|
||||||
"items.created_at",
|
"items.created_at",
|
||||||
"items.updated_at",
|
"items.updated_at",
|
||||||
"items.title",
|
"items.title",
|
||||||
"items.quantity",
|
"items.quantity",
|
||||||
"items.unit_price",
|
"items.unit_price",
|
||||||
|
"items.tax_lines.id",
|
||||||
|
"items.tax_lines.description",
|
||||||
|
"items.tax_lines.code",
|
||||||
|
"items.tax_lines.rate",
|
||||||
|
"items.tax_lines.provider_id",
|
||||||
"items.adjustments.id",
|
"items.adjustments.id",
|
||||||
"items.adjustments.code",
|
"items.adjustments.code",
|
||||||
"items.adjustments.amount",
|
"items.adjustments.amount",
|
||||||
"customer.id",
|
"customer.id",
|
||||||
"customer.email",
|
"customer.email",
|
||||||
|
"customer.groups.id",
|
||||||
|
"shipping_methods.tax_lines.id",
|
||||||
|
"shipping_methods.tax_lines.description",
|
||||||
|
"shipping_methods.tax_lines.code",
|
||||||
|
"shipping_methods.tax_lines.rate",
|
||||||
|
"shipping_methods.tax_lines.provider_id",
|
||||||
|
"shipping_methods.shipping_option_id",
|
||||||
|
"shipping_methods.amount",
|
||||||
"shipping_methods.adjustments.id",
|
"shipping_methods.adjustments.id",
|
||||||
"shipping_methods.adjustments.code",
|
"shipping_methods.adjustments.code",
|
||||||
"shipping_methods.adjustments.amount",
|
"shipping_methods.adjustments.amount",
|
||||||
@@ -27,6 +51,7 @@ export const defaultStoreCartFields = [
|
|||||||
"shipping_address.postal_code",
|
"shipping_address.postal_code",
|
||||||
"shipping_address.country_code",
|
"shipping_address.country_code",
|
||||||
"shipping_address.region_code",
|
"shipping_address.region_code",
|
||||||
|
"shipping_address.province",
|
||||||
"shipping_address.phone",
|
"shipping_address.phone",
|
||||||
"billing_address.id",
|
"billing_address.id",
|
||||||
"billing_address.first_name",
|
"billing_address.first_name",
|
||||||
@@ -51,23 +76,29 @@ export const defaultStoreCartFields = [
|
|||||||
|
|
||||||
export const defaultStoreCartRelations = [
|
export const defaultStoreCartRelations = [
|
||||||
"items",
|
"items",
|
||||||
|
"items.tax_lines",
|
||||||
"items.adjustments",
|
"items.adjustments",
|
||||||
"region",
|
"region",
|
||||||
"customer",
|
"customer",
|
||||||
|
"customer.groups",
|
||||||
"shipping_address",
|
"shipping_address",
|
||||||
"billing_address",
|
"billing_address",
|
||||||
"shipping_methods",
|
"shipping_methods",
|
||||||
|
"shipping_methods.tax_lines",
|
||||||
"shipping_methods.adjustments",
|
"shipping_methods.adjustments",
|
||||||
]
|
]
|
||||||
|
|
||||||
export const allowedRelations = [
|
export const allowedRelations = [
|
||||||
"items",
|
"items",
|
||||||
|
"items.tax_lines",
|
||||||
"items.adjustments",
|
"items.adjustments",
|
||||||
"region",
|
"region",
|
||||||
"customer",
|
"customer",
|
||||||
|
"customer.groups",
|
||||||
"shipping_address",
|
"shipping_address",
|
||||||
"billing_address",
|
"billing_address",
|
||||||
"shipping_methods",
|
"shipping_methods",
|
||||||
|
"shipping_methods.tax_lines",
|
||||||
"shipping_methods.adjustments",
|
"shipping_methods.adjustments",
|
||||||
"sales_channel",
|
"sales_channel",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { createCartWorkflow } from "@medusajs/core-flows"
|
import { createCartWorkflow } from "@medusajs/core-flows"
|
||||||
|
import { LinkModuleUtils, Modules } from "@medusajs/modules-sdk"
|
||||||
import { CreateCartWorkflowInputDTO } from "@medusajs/types"
|
import { CreateCartWorkflowInputDTO } from "@medusajs/types"
|
||||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
@@ -25,16 +26,13 @@ export const POST = async (
|
|||||||
throw errors[0].error
|
throw errors[0].error
|
||||||
}
|
}
|
||||||
|
|
||||||
const remoteQuery = req.scope.resolve("remoteQuery")
|
const remoteQuery = req.scope.resolve(LinkModuleUtils.REMOTE_QUERY)
|
||||||
|
|
||||||
const variables = { id: result.id }
|
|
||||||
|
|
||||||
const query = remoteQueryObjectFromString({
|
const query = remoteQueryObjectFromString({
|
||||||
entryPoint: "cart",
|
entryPoint: Modules.CART,
|
||||||
fields: defaultStoreCartFields,
|
fields: defaultStoreCartFields,
|
||||||
})
|
})
|
||||||
|
|
||||||
const [cart] = await remoteQuery(query, { cart: variables })
|
const [cart] = await remoteQuery(query, { cart: { id: result.id } })
|
||||||
|
|
||||||
res.status(200).json({ cart })
|
res.status(200).json({ cart })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,14 @@ export class StorePostCartReq {
|
|||||||
@IsString()
|
@IsString()
|
||||||
region_id?: string
|
region_id?: string
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsType([AddressPayload, String])
|
||||||
|
shipping_address?: AddressPayload | string
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsType([AddressPayload, String])
|
||||||
|
billing_address?: AddressPayload | string
|
||||||
|
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
email?: string
|
email?: string
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export default class SystemTaxService implements ITaxProvider {
|
|||||||
name: r.name,
|
name: r.name,
|
||||||
code: r.code,
|
code: r.code,
|
||||||
line_item_id: l.line_item.id,
|
line_item_id: l.line_item.id,
|
||||||
|
provider_id: this.getIdentifier(),
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ export default class SystemTaxService implements ITaxProvider {
|
|||||||
name: r.name,
|
name: r.name,
|
||||||
code: r.code,
|
code: r.code,
|
||||||
shipping_line_id: l.shipping_line.id,
|
shipping_line_id: l.shipping_line.id,
|
||||||
|
provider_id: this.getIdentifier(),
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import {
|
|||||||
InternalModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
ModuleJoinerConfig,
|
ModuleJoinerConfig,
|
||||||
ModulesSdkTypes,
|
ModulesSdkTypes,
|
||||||
|
TaxRegionDTO,
|
||||||
TaxTypes,
|
TaxTypes,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
@@ -14,16 +15,14 @@ import {
|
|||||||
MedusaContext,
|
MedusaContext,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
ModulesSdkUtils,
|
ModulesSdkUtils,
|
||||||
arrayDifference,
|
|
||||||
isDefined,
|
isDefined,
|
||||||
isString,
|
isString,
|
||||||
promiseAll,
|
promiseAll,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { TaxProvider, TaxRate, TaxRegion, TaxRateRule } from "@models"
|
import { TaxProvider, TaxRate, TaxRateRule, TaxRegion } from "@models"
|
||||||
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
||||||
import { TaxRegionDTO } from "@medusajs/types"
|
|
||||||
import { uniqueRateReferenceIndexName } from "../models/tax-rate-rule"
|
|
||||||
import { singleDefaultRegionIndexName } from "../models/tax-rate"
|
import { singleDefaultRegionIndexName } from "../models/tax-rate"
|
||||||
|
import { uniqueRateReferenceIndexName } from "../models/tax-rate-rule"
|
||||||
import { countryCodeProvinceIndexName } from "../models/tax-region"
|
import { countryCodeProvinceIndexName } from "../models/tax-region"
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { CustomerDTO } from "../customer"
|
||||||
|
import { ProductDTO } from "../product"
|
||||||
import { CartDTO, CartLineItemDTO } from "./common"
|
import { CartDTO, CartLineItemDTO } from "./common"
|
||||||
import { UpdateLineItemDTO } from "./mutations"
|
import { UpdateLineItemDTO } from "./mutations"
|
||||||
|
|
||||||
@@ -70,7 +72,7 @@ export interface CreateCartWorkflowInputDTO {
|
|||||||
|
|
||||||
export interface AddToCartWorkflowInputDTO {
|
export interface AddToCartWorkflowInputDTO {
|
||||||
items: CreateCartCreateLineItemDTO[]
|
items: CreateCartCreateLineItemDTO[]
|
||||||
cart: CartDTO
|
cart: CartWorkflowDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateCartWorkflowInputDTO {
|
export interface UpdateCartWorkflowInputDTO {
|
||||||
@@ -91,3 +93,8 @@ export interface CreatePaymentCollectionForCartWorkflowInputDTO {
|
|||||||
amount: number
|
amount: number
|
||||||
metadata?: Record<string, unknown>
|
metadata?: Record<string, unknown>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CartWorkflowDTO extends CartDTO {
|
||||||
|
customer?: CustomerDTO
|
||||||
|
product?: ProductDTO
|
||||||
|
}
|
||||||
|
|||||||
@@ -165,6 +165,7 @@ interface TaxLineDTO {
|
|||||||
rate: number | null
|
rate: number | null
|
||||||
code: string | null
|
code: string | null
|
||||||
name: string
|
name: string
|
||||||
|
provider_id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ItemTaxLineDTO extends TaxLineDTO {
|
export interface ItemTaxLineDTO extends TaxLineDTO {
|
||||||
|
|||||||
Reference in New Issue
Block a user