feat(customer): manage default address selection (#6295)
**What** - Catches unique constraints on customer_id, is_default_billing/is_default_shipping and reformats - Adds an step to create and update of addresses that unsets the previous default shipping/billing address if necessary. - This creates a behavior in the API where you can always set an address to be default and it will automatically unset the previous one for you.
This commit is contained in:
@@ -4,3 +4,5 @@ export * from "./delete-customers"
|
||||
export * from "./create-addresses"
|
||||
export * from "./update-addresses"
|
||||
export * from "./delete-addresses"
|
||||
export * from "./maybe-unset-default-billing-addresses"
|
||||
export * from "./maybe-unset-default-shipping-addresses"
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import {
|
||||
ICustomerModuleService,
|
||||
CreateCustomerAddressDTO,
|
||||
FilterableCustomerAddressProps,
|
||||
CustomerAddressDTO,
|
||||
} from "@medusajs/types"
|
||||
import { createStep } from "@medusajs/workflows-sdk"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { unsetForUpdate, unsetForCreate } from "./utils"
|
||||
import { isDefined } from "@medusajs/utils"
|
||||
|
||||
type StepInput = {
|
||||
create?: CreateCustomerAddressDTO[]
|
||||
update?: {
|
||||
selector: FilterableCustomerAddressProps
|
||||
update: Partial<CustomerAddressDTO>
|
||||
}
|
||||
}
|
||||
|
||||
export const maybeUnsetDefaultBillingAddressesStepId =
|
||||
"maybe-unset-default-billing-customer-addresses"
|
||||
export const maybeUnsetDefaultBillingAddressesStep = createStep(
|
||||
maybeUnsetDefaultBillingAddressesStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const customerModuleService = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
if (isDefined(data.create)) {
|
||||
return unsetForCreate(
|
||||
data.create,
|
||||
customerModuleService,
|
||||
"is_default_billing"
|
||||
)
|
||||
}
|
||||
|
||||
if (isDefined(data.update)) {
|
||||
return unsetForUpdate(
|
||||
data.update,
|
||||
customerModuleService,
|
||||
"is_default_billing"
|
||||
)
|
||||
}
|
||||
|
||||
throw new Error("Invalid step input")
|
||||
},
|
||||
async (addressesToSet, { container }) => {
|
||||
if (!addressesToSet?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const customerModuleService = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await customerModuleService.updateAddress(
|
||||
{ id: addressesToSet },
|
||||
{ is_default_billing: true }
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
ICustomerModuleService,
|
||||
CreateCustomerAddressDTO,
|
||||
FilterableCustomerAddressProps,
|
||||
CustomerAddressDTO,
|
||||
} from "@medusajs/types"
|
||||
import { createStep } from "@medusajs/workflows-sdk"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { unsetForUpdate, unsetForCreate } from "./utils"
|
||||
import { isDefined } from "@medusajs/utils"
|
||||
|
||||
type StepInput = {
|
||||
create?: CreateCustomerAddressDTO[]
|
||||
update?: {
|
||||
selector: FilterableCustomerAddressProps
|
||||
update: Partial<CustomerAddressDTO>
|
||||
}
|
||||
}
|
||||
|
||||
export const maybeUnsetDefaultShippingAddressesStepId =
|
||||
"maybe-unset-default-shipping-customer-addresses"
|
||||
export const maybeUnsetDefaultShippingAddressesStep = createStep(
|
||||
maybeUnsetDefaultShippingAddressesStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const customerModuleService = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
if (isDefined(data.create)) {
|
||||
return unsetForCreate(
|
||||
data.create,
|
||||
customerModuleService,
|
||||
"is_default_shipping"
|
||||
)
|
||||
}
|
||||
|
||||
if (isDefined(data.update)) {
|
||||
return unsetForUpdate(
|
||||
data.update,
|
||||
customerModuleService,
|
||||
"is_default_shipping"
|
||||
)
|
||||
}
|
||||
|
||||
throw new Error("Invalid step input")
|
||||
},
|
||||
async (addressesToSet, { container }) => {
|
||||
if (!addressesToSet?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const customerModuleService = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await customerModuleService.updateAddress(
|
||||
{ id: addressesToSet },
|
||||
{ is_default_shipping: true }
|
||||
)
|
||||
}
|
||||
)
|
||||
2
packages/core-flows/src/customer/steps/utils/index.ts
Normal file
2
packages/core-flows/src/customer/steps/utils/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export * from "./unset-address-for-create"
|
||||
export * from "./unset-address-for-update"
|
||||
@@ -0,0 +1,33 @@
|
||||
import {
|
||||
CreateCustomerAddressDTO,
|
||||
ICustomerModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const unsetForCreate = async (
|
||||
data: CreateCustomerAddressDTO[],
|
||||
customerService: ICustomerModuleService,
|
||||
field: "is_default_billing" | "is_default_shipping"
|
||||
) => {
|
||||
const customerIds = data.reduce<string[]>((acc, curr) => {
|
||||
if (curr[field]) {
|
||||
acc.push(curr.customer_id)
|
||||
}
|
||||
return acc
|
||||
}, [])
|
||||
|
||||
const customerDefaultAddresses = await customerService.listAddresses({
|
||||
customer_id: customerIds,
|
||||
[field]: true,
|
||||
})
|
||||
|
||||
await customerService.updateAddress(
|
||||
{ customer_id: customerIds, [field]: true },
|
||||
{ [field]: false }
|
||||
)
|
||||
|
||||
return new StepResponse(
|
||||
void 0,
|
||||
customerDefaultAddresses.map((address) => address.id)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
CustomerAddressDTO,
|
||||
FilterableCustomerAddressProps,
|
||||
ICustomerModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const unsetForUpdate = async (
|
||||
data: {
|
||||
selector: FilterableCustomerAddressProps
|
||||
update: Partial<CustomerAddressDTO>
|
||||
},
|
||||
customerService: ICustomerModuleService,
|
||||
field: "is_default_billing" | "is_default_shipping"
|
||||
) => {
|
||||
if (!data.update[field]) {
|
||||
return new StepResponse(void 0)
|
||||
}
|
||||
|
||||
const affectedCustomers = await customerService.listAddresses(data.selector, {
|
||||
select: ["id", "customer_id"],
|
||||
})
|
||||
|
||||
const customerIds = affectedCustomers.map((address) => address.customer_id)
|
||||
|
||||
const customerDefaultAddresses = await customerService.listAddresses({
|
||||
customer_id: customerIds,
|
||||
[field]: true,
|
||||
})
|
||||
|
||||
await customerService.updateAddress(
|
||||
{ customer_id: customerIds, [field]: true },
|
||||
{ [field]: false }
|
||||
)
|
||||
|
||||
return new StepResponse(
|
||||
void 0,
|
||||
customerDefaultAddresses.map((address) => address.id)
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,15 @@
|
||||
import { CreateCustomerAddressDTO, CustomerAddressDTO } from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { createCustomerAddressesStep } from "../steps"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
parallelize,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
createCustomerAddressesStep,
|
||||
maybeUnsetDefaultBillingAddressesStep,
|
||||
maybeUnsetDefaultShippingAddressesStep,
|
||||
} from "../steps"
|
||||
|
||||
type WorkflowInput = { addresses: CreateCustomerAddressDTO[] }
|
||||
|
||||
@@ -8,6 +17,15 @@ export const createCustomerAddressesWorkflowId = "create-customer-addresses"
|
||||
export const createCustomerAddressesWorkflow = createWorkflow(
|
||||
createCustomerAddressesWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<CustomerAddressDTO[]> => {
|
||||
const unsetInput = transform(input, (data) => ({
|
||||
create: data.addresses,
|
||||
}))
|
||||
|
||||
parallelize(
|
||||
maybeUnsetDefaultShippingAddressesStep(unsetInput),
|
||||
maybeUnsetDefaultBillingAddressesStep(unsetInput)
|
||||
)
|
||||
|
||||
return createCustomerAddressesStep(input.addresses)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -2,8 +2,17 @@ import {
|
||||
FilterableCustomerAddressProps,
|
||||
CustomerAddressDTO,
|
||||
} from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { updateCustomerAddressesStep } from "../steps"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
parallelize,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
maybeUnsetDefaultBillingAddressesStep,
|
||||
maybeUnsetDefaultShippingAddressesStep,
|
||||
updateCustomerAddressesStep,
|
||||
} from "../steps"
|
||||
|
||||
type WorkflowInput = {
|
||||
selector: FilterableCustomerAddressProps
|
||||
@@ -14,6 +23,15 @@ export const updateCustomerAddressesWorkflowId = "update-customer-addresses"
|
||||
export const updateCustomerAddressesWorkflow = createWorkflow(
|
||||
updateCustomerAddressesWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<CustomerAddressDTO[]> => {
|
||||
const unsetInput = transform(input, (data) => ({
|
||||
update: data,
|
||||
}))
|
||||
|
||||
parallelize(
|
||||
maybeUnsetDefaultShippingAddressesStep(unsetInput),
|
||||
maybeUnsetDefaultBillingAddressesStep(unsetInput)
|
||||
)
|
||||
|
||||
return updateCustomerAddressesStep(input)
|
||||
}
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user