fix: Handle region updates on cart (1/n) (#9369)

**What**

On cart creation:
- If region only has one country -> create cart with country code

On cart updates:
- If shipping address country code is provided in input ->
  - If cart region doesn't include that country -> throw
  - If cart includes the country -> update shipping address
- If region is provided in input and is different from the one currently on the cart -> 
  - If there is a shipping address on the cart -> clear the address
    - If the region only has one country -> set country code of address
  - If there is not a shipping address on the cart ->
    - If the region only has one country -> set country code of address


Closes CC-545
This commit is contained in:
Oli Juhl
2024-10-04 13:33:36 +02:00
committed by GitHub
parent ed174826a4
commit a114f90358
5 changed files with 543 additions and 80 deletions

View File

@@ -18,7 +18,9 @@ export const findOneOrAnyRegionStep = createStep(
if (data.regionId) {
try {
const region = await service.retrieveRegion(data.regionId)
const region = await service.retrieveRegion(data.regionId, {
relations: ["countries"],
})
return new StepResponse(region)
} catch (error) {
return new StepResponse(null)
@@ -31,9 +33,12 @@ export const findOneOrAnyRegionStep = createStep(
throw new MedusaError(MedusaError.Types.NOT_FOUND, "Store not found")
}
const [region] = await service.listRegions({
id: store.default_region_id,
})
const [region] = await service.listRegions(
{
id: store.default_region_id,
},
{ relations: ["countries"] }
)
if (!region) {
return new StepResponse(null)

View File

@@ -27,9 +27,6 @@ import { refreshPaymentCollectionForCartWorkflow } from "./refresh-payment-colle
import { updateCartPromotionsWorkflow } from "./update-cart-promotions"
import { updateTaxLinesWorkflow } from "./update-tax-lines"
// TODO: The createCartWorkflow are missing the following steps:
// - Refresh/delete shipping methods (fulfillment module)
export const createCartWorkflowId = "create-cart"
/**
* This workflow creates a cart.
@@ -119,6 +116,13 @@ export const createCartWorkflow = createWorkflow(
data_.sales_channel_id = data.salesChannel.id
}
// If there is only one country in the region, we prepare a shipping address with that country's code.
if (!data.input.shipping_address && data.region.countries.length === 1) {
data_.shipping_address = {
country_code: data.region.countries[0].iso_2,
}
}
return data_
}
)

View File

@@ -2,11 +2,7 @@ import {
AdditionalData,
UpdateCartWorkflowInputDTO,
} from "@medusajs/framework/types"
import {
MedusaError,
PromotionActions,
isPresent,
} from "@medusajs/framework/utils"
import { MedusaError, PromotionActions } from "@medusajs/framework/utils"
import {
WorkflowData,
WorkflowResponse,
@@ -14,10 +10,10 @@ import {
createWorkflow,
parallelize,
transform,
when,
} from "@medusajs/framework/workflows-sdk"
import { useRemoteQueryStep } from "../../common"
import {
findOneOrAnyRegionStep,
findOrCreateCustomerStep,
findSalesChannelStep,
refreshCartShippingMethodsStep,
@@ -35,35 +31,85 @@ export const updateCartWorkflowId = "update-cart"
export const updateCartWorkflow = createWorkflow(
updateCartWorkflowId,
(input: WorkflowData<UpdateCartWorkflowInputDTO & AdditionalData>) => {
const [salesChannel, region, customerData] = parallelize(
const cartToUpdate = useRemoteQueryStep({
entry_point: "cart",
variables: { id: input.id },
fields: ["id", "shipping_address.*", "region.*", "region.countries.*"],
list: false,
throw_if_key_not_found: true,
}).config({ name: "get-cart" })
const [salesChannel, customerData] = parallelize(
findSalesChannelStep({
salesChannelId: input.sales_channel_id,
}),
findOneOrAnyRegionStep({
regionId: input.region_id,
}),
findOrCreateCustomerStep({
customerId: input.customer_id,
email: input.email,
})
)
const newRegion = when({ input }, (data) => {
return !!data.input.region_id
}).then(() => {
return useRemoteQueryStep({
entry_point: "region",
variables: { id: input.region_id },
fields: ["id", "countries.*", "currency_code", "name"],
list: false,
throw_if_key_not_found: true,
}).config({ name: "get-region" })
})
const region = transform({ cartToUpdate, newRegion }, (data) => {
return data.newRegion ?? data.cartToUpdate.region
})
const cartInput = transform(
{ input, region, customerData, salesChannel },
{ input, region, customerData, salesChannel, cartToUpdate },
(data) => {
const { promo_codes, ...updateCartData } = data.input
const data_ = { ...updateCartData }
if (isPresent(updateCartData.region_id)) {
if (!data.region) {
const data_ = {
...updateCartData,
currency_code: data.region?.currency_code,
region_id: data.region?.id, // This is either the region from the input or the region from the cart or null
}
// When the region is updated, we do a few things:
// - We need to make sure the provided shipping address country code is in the new region
// - We clear the shipping address if the new region has more than one country
const regionIsNew = data.region?.id !== data.cartToUpdate.region?.id
const shippingAddress = data.input.shipping_address
if (shippingAddress?.country_code) {
const country = data.region.countries.find(
(c) => c.iso_2 === shippingAddress.country_code
)
if (!country) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
"Region not found"
MedusaError.Types.INVALID_DATA,
`Country with code ${shippingAddress.country_code} is not within region ${data.region.name}`
)
}
data_.currency_code = data.region.currency_code
data_.region_id = data.region.id
data_.shipping_address = {
...shippingAddress,
country_code: country.iso_2,
}
}
if (regionIsNew) {
if (data.region.countries.length === 1) {
data_.shipping_address = {
country_code: data.region.countries[0].iso_2,
}
}
if (!data_.shipping_address?.country_code) {
data_.shipping_address = null
}
}
if (