chore(): start moving some packages to the core directory (#7215)
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
export * from "./steps"
|
||||
export * from "./workflows"
|
||||
@@ -0,0 +1,33 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CreateApiKeyDTO, IApiKeyModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type CreateApiKeysStepInput = {
|
||||
api_keys: CreateApiKeyDTO[]
|
||||
}
|
||||
|
||||
export const createApiKeysStepId = "create-api-keys"
|
||||
export const createApiKeysStep = createStep(
|
||||
createApiKeysStepId,
|
||||
async (data: CreateApiKeysStepInput, { container }) => {
|
||||
const service = container.resolve<IApiKeyModuleService>(
|
||||
ModuleRegistrationName.API_KEY
|
||||
)
|
||||
const created = await service.create(data.api_keys)
|
||||
return new StepResponse(
|
||||
created,
|
||||
created.map((apiKey) => apiKey.id)
|
||||
)
|
||||
},
|
||||
async (createdIds, { container }) => {
|
||||
if (!createdIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<IApiKeyModuleService>(
|
||||
ModuleRegistrationName.API_KEY
|
||||
)
|
||||
|
||||
await service.delete(createdIds)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IApiKeyModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const deleteApiKeysStepId = "delete-api-keys"
|
||||
export const deleteApiKeysStep = createStep(
|
||||
{ name: deleteApiKeysStepId, noCompensation: true },
|
||||
async (ids: string[], { container }) => {
|
||||
const service = container.resolve<IApiKeyModuleService>(
|
||||
ModuleRegistrationName.API_KEY
|
||||
)
|
||||
|
||||
await service.delete(ids)
|
||||
return new StepResponse(void 0)
|
||||
},
|
||||
async () => {}
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
export * from "./link-sales-channels-to-publishable-key"
|
||||
export * from "./create-api-keys"
|
||||
export * from "./delete-api-keys"
|
||||
export * from "./revoke-api-keys"
|
||||
export * from "./update-api-keys"
|
||||
export * from "./validate-sales-channel-exists"
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { LinkWorkflowInput } from "@medusajs/types/src"
|
||||
import { ContainerRegistrationKeys, promiseAll } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const linkSalesChannelsToApiKeyStepId = "link-sales-channels-to-api-key"
|
||||
export const linkSalesChannelsToApiKeyStep = createStep(
|
||||
linkSalesChannelsToApiKeyStepId,
|
||||
async (input: LinkWorkflowInput, { container }) => {
|
||||
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||
if (!input || (!input.add?.length && !input.remove?.length)) {
|
||||
return
|
||||
}
|
||||
|
||||
const linksToCreate = (input.add ?? []).map((salesChannelId) => {
|
||||
return {
|
||||
[Modules.API_KEY]: {
|
||||
publishable_key_id: input.id,
|
||||
},
|
||||
[Modules.SALES_CHANNEL]: {
|
||||
sales_channel_id: salesChannelId,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const linksToDismiss = (input.remove ?? []).map((salesChannelId) => {
|
||||
return {
|
||||
[Modules.API_KEY]: {
|
||||
publishable_key_id: input.id,
|
||||
},
|
||||
[Modules.SALES_CHANNEL]: {
|
||||
sales_channel_id: salesChannelId,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const promises: Promise<any>[] = []
|
||||
if (linksToCreate.length) {
|
||||
promises.push(remoteLink.create(linksToCreate))
|
||||
}
|
||||
if (linksToDismiss.length) {
|
||||
promises.push(remoteLink.dismiss(linksToDismiss))
|
||||
}
|
||||
await promiseAll(promises)
|
||||
|
||||
return new StepResponse(void 0, { linksToCreate, linksToDismiss })
|
||||
},
|
||||
async (prevData, { container }) => {
|
||||
if (!prevData) {
|
||||
return
|
||||
}
|
||||
|
||||
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||
if (prevData.linksToCreate.length) {
|
||||
await remoteLink.dismiss(prevData.linksToCreate)
|
||||
}
|
||||
|
||||
if (prevData.linksToDismiss.length) {
|
||||
await remoteLink.create(prevData.linksToDismiss)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
FilterableApiKeyProps,
|
||||
IApiKeyModuleService,
|
||||
RevokeApiKeyDTO,
|
||||
} from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type RevokeApiKeysStepInput = {
|
||||
selector: FilterableApiKeyProps
|
||||
revoke: RevokeApiKeyDTO
|
||||
}
|
||||
|
||||
export const revokeApiKeysStepId = "revoke-api-keys"
|
||||
export const revokeApiKeysStep = createStep(
|
||||
{ name: revokeApiKeysStepId, noCompensation: true },
|
||||
async (data: RevokeApiKeysStepInput, { container }) => {
|
||||
const service = container.resolve<IApiKeyModuleService>(
|
||||
ModuleRegistrationName.API_KEY
|
||||
)
|
||||
|
||||
const apiKeys = await service.revoke(data.selector, data.revoke)
|
||||
return new StepResponse(apiKeys)
|
||||
},
|
||||
async () => {}
|
||||
)
|
||||
@@ -0,0 +1,51 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
FilterableApiKeyProps,
|
||||
IApiKeyModuleService,
|
||||
UpdateApiKeyDTO,
|
||||
} from "@medusajs/types"
|
||||
import { getSelectsAndRelationsFromObjectArray } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type UpdateApiKeysStepInput = {
|
||||
selector: FilterableApiKeyProps
|
||||
update: UpdateApiKeyDTO
|
||||
}
|
||||
|
||||
export const updateApiKeysStepId = "update-api-keys"
|
||||
export const updateApiKeysStep = createStep(
|
||||
updateApiKeysStepId,
|
||||
async (data: UpdateApiKeysStepInput, { container }) => {
|
||||
const service = container.resolve<IApiKeyModuleService>(
|
||||
ModuleRegistrationName.API_KEY
|
||||
)
|
||||
|
||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
|
||||
data.update,
|
||||
])
|
||||
|
||||
const prevData = await service.list(data.selector, {
|
||||
select: selects,
|
||||
relations,
|
||||
})
|
||||
|
||||
const apiKeys = await service.update(data.selector, data.update)
|
||||
return new StepResponse(apiKeys, prevData)
|
||||
},
|
||||
async (prevData, { container }) => {
|
||||
if (!prevData?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<IApiKeyModuleService>(
|
||||
ModuleRegistrationName.API_KEY
|
||||
)
|
||||
|
||||
await service.upsert(
|
||||
prevData.map((r) => ({
|
||||
id: r.id,
|
||||
title: r.title,
|
||||
}))
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { ISalesChannelModuleService } from "@medusajs/types"
|
||||
import { MedusaError, arrayDifference } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
sales_channel_ids: string[]
|
||||
}
|
||||
|
||||
export const validateSalesChannelsExistStepId = "validate-sales-channels-exist"
|
||||
export const validateSalesChannelsExistStep = createStep(
|
||||
validateSalesChannelsExistStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const salesChannelModuleService =
|
||||
container.resolve<ISalesChannelModuleService>(
|
||||
ModuleRegistrationName.SALES_CHANNEL
|
||||
)
|
||||
|
||||
const salesChannels = await salesChannelModuleService.list(
|
||||
{ id: data.sales_channel_ids },
|
||||
{ select: ["id"] }
|
||||
)
|
||||
|
||||
const salesChannelIds = salesChannels.map((v) => v.id)
|
||||
|
||||
const notFound = arrayDifference(data.sales_channel_ids, salesChannelIds)
|
||||
|
||||
if (notFound.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Sales channels with IDs ${notFound.join(", ")} do not exist`
|
||||
)
|
||||
}
|
||||
|
||||
return new StepResponse(salesChannelIds)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
import { ApiKeyDTO, CreateApiKeyDTO } from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { createApiKeysStep } from "../steps"
|
||||
|
||||
type WorkflowInput = { api_keys: CreateApiKeyDTO[] }
|
||||
|
||||
export const createApiKeysWorkflowId = "create-api-keys"
|
||||
export const createApiKeysWorkflow = createWorkflow(
|
||||
createApiKeysWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<ApiKeyDTO[]> => {
|
||||
return createApiKeysStep(input)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { removeRemoteLinkStep } from "../../common/steps/remove-remote-links"
|
||||
import { deleteApiKeysStep } from "../steps"
|
||||
|
||||
type WorkflowInput = { ids: string[] }
|
||||
|
||||
export const deleteApiKeysWorkflowId = "delete-api-keys"
|
||||
export const deleteApiKeysWorkflow = createWorkflow(
|
||||
deleteApiKeysWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
|
||||
deleteApiKeysStep(input.ids)
|
||||
|
||||
// Please note, the ids here should be publishable key IDs
|
||||
removeRemoteLinkStep({
|
||||
[Modules.API_KEY]: { publishable_key_id: input.ids },
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from "./create-api-keys"
|
||||
export * from "./delete-api-keys"
|
||||
export * from "./update-api-keys"
|
||||
export * from "./revoke-api-keys"
|
||||
export * from "./link-sales-channels-to-publishable-key"
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
linkSalesChannelsToApiKeyStep,
|
||||
validateSalesChannelsExistStep,
|
||||
} from "../steps"
|
||||
import { LinkWorkflowInput } from "@medusajs/types/src"
|
||||
|
||||
export const linkSalesChannelsToApiKeyWorkflowId =
|
||||
"link-sales-channels-to-api-key"
|
||||
export const linkSalesChannelsToApiKeyWorkflow = createWorkflow(
|
||||
linkSalesChannelsToApiKeyWorkflowId,
|
||||
(input: WorkflowData<LinkWorkflowInput>) => {
|
||||
validateSalesChannelsExistStep({
|
||||
sales_channel_ids: input.add ?? [],
|
||||
})
|
||||
|
||||
linkSalesChannelsToApiKeyStep(input)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
ApiKeyDTO,
|
||||
FilterableApiKeyProps,
|
||||
RevokeApiKeyDTO,
|
||||
} from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { revokeApiKeysStep } from "../steps"
|
||||
|
||||
type RevokeApiKeysStepInput = {
|
||||
selector: FilterableApiKeyProps
|
||||
revoke: RevokeApiKeyDTO
|
||||
}
|
||||
|
||||
type WorkflowInput = RevokeApiKeysStepInput
|
||||
|
||||
export const revokeApiKeysWorkflowId = "revoke-api-keys"
|
||||
export const revokeApiKeysWorkflow = createWorkflow(
|
||||
revokeApiKeysWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<ApiKeyDTO[]> => {
|
||||
return revokeApiKeysStep(input)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
ApiKeyDTO,
|
||||
FilterableApiKeyProps,
|
||||
UpdateApiKeyDTO,
|
||||
} from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { updateApiKeysStep } from "../steps"
|
||||
|
||||
type UpdateApiKeysStepInput = {
|
||||
selector: FilterableApiKeyProps
|
||||
update: UpdateApiKeyDTO
|
||||
}
|
||||
|
||||
type WorkflowInput = UpdateApiKeysStepInput
|
||||
|
||||
export const updateApiKeysWorkflowId = "update-api-keys"
|
||||
export const updateApiKeysWorkflow = createWorkflow(
|
||||
updateApiKeysWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<ApiKeyDTO[]> => {
|
||||
return updateApiKeysStep(input)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./steps"
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./set-auth-app-metadata"
|
||||
@@ -0,0 +1,60 @@
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IAuthModuleService } from "@medusajs/types"
|
||||
import { isDefined } from "@medusajs/utils"
|
||||
|
||||
type StepInput = {
|
||||
authUserId: string
|
||||
key: string
|
||||
value: string
|
||||
}
|
||||
|
||||
export const setAuthAppMetadataStepId = "set-auth-app-metadata"
|
||||
export const setAuthAppMetadataStep = createStep(
|
||||
setAuthAppMetadataStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const service = container.resolve<IAuthModuleService>(
|
||||
ModuleRegistrationName.AUTH
|
||||
)
|
||||
|
||||
const authUser = await service.retrieve(data.authUserId)
|
||||
|
||||
const appMetadata = authUser.app_metadata || {}
|
||||
if (isDefined(appMetadata[data.key])) {
|
||||
throw new Error(`Key ${data.key} already exists in app metadata`)
|
||||
}
|
||||
|
||||
appMetadata[data.key] = data.value
|
||||
|
||||
await service.update({
|
||||
id: authUser.id,
|
||||
app_metadata: appMetadata,
|
||||
})
|
||||
|
||||
return new StepResponse(authUser, { id: authUser.id, key: data.key })
|
||||
},
|
||||
async (idAndKey, { container }) => {
|
||||
if (!idAndKey) {
|
||||
return
|
||||
}
|
||||
|
||||
const { id, key } = idAndKey
|
||||
|
||||
const service = container.resolve<IAuthModuleService>(
|
||||
ModuleRegistrationName.AUTH
|
||||
)
|
||||
|
||||
const authUser = await service.retrieve(id)
|
||||
|
||||
const appMetadata = authUser.app_metadata || {}
|
||||
if (isDefined(appMetadata[key])) {
|
||||
delete appMetadata[key]
|
||||
}
|
||||
|
||||
await service.update({
|
||||
id: authUser.id,
|
||||
app_metadata: appMetadata,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./steps/remove-remote-links"
|
||||
export * from "./steps/use-remote-query"
|
||||
@@ -0,0 +1,50 @@
|
||||
import { DeleteEntityInput, RemoteLink } from "@medusajs/modules-sdk"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
|
||||
type RemoveRemoteLinksStepInput = DeleteEntityInput | DeleteEntityInput[]
|
||||
|
||||
export const removeRemoteLinkStepId = "remove-remote-links"
|
||||
export const removeRemoteLinkStep = createStep(
|
||||
removeRemoteLinkStepId,
|
||||
async (data: RemoveRemoteLinksStepInput, { container }) => {
|
||||
const entries = Array.isArray(data) ? data : [data]
|
||||
const grouped: DeleteEntityInput = {}
|
||||
|
||||
for (const entry of entries) {
|
||||
for (const moduleName of Object.keys(entry)) {
|
||||
grouped[moduleName] ??= {}
|
||||
|
||||
for (const linkableKey of Object.keys(entry[moduleName])) {
|
||||
grouped[moduleName][linkableKey] ??= []
|
||||
|
||||
const keys = Array.isArray(entry[moduleName][linkableKey])
|
||||
? entry[moduleName][linkableKey]
|
||||
: [entry[moduleName][linkableKey]]
|
||||
|
||||
grouped[moduleName][linkableKey] = (
|
||||
grouped[moduleName][linkableKey] as string[]
|
||||
).concat(keys as string[])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const link = container.resolve<RemoteLink>(
|
||||
ContainerRegistrationKeys.REMOTE_LINK
|
||||
)
|
||||
await link.delete(grouped)
|
||||
|
||||
return new StepResponse(grouped, grouped)
|
||||
},
|
||||
async (removedLinks, { container }) => {
|
||||
if (!removedLinks) {
|
||||
return
|
||||
}
|
||||
|
||||
const link = container.resolve<RemoteLink>(
|
||||
ContainerRegistrationKeys.REMOTE_LINK
|
||||
)
|
||||
await link.restore(removedLinks)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
entry_point: string
|
||||
fields: string[]
|
||||
variables?: Record<string, any>
|
||||
throw_if_key_not_found?: boolean
|
||||
throw_if_relation_not_found?: boolean | string[]
|
||||
list?: boolean
|
||||
}
|
||||
|
||||
export const useRemoteQueryStepId = "use-remote-query"
|
||||
export const useRemoteQueryStep = createStep(
|
||||
useRemoteQueryStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { list = true, fields, variables, entry_point: entryPoint } = data
|
||||
const query = container.resolve("remoteQuery")
|
||||
|
||||
const queryObject = remoteQueryObjectFromString({
|
||||
entryPoint,
|
||||
fields,
|
||||
variables,
|
||||
})
|
||||
|
||||
const config = {
|
||||
throwIfKeyNotFound: !!data.throw_if_key_not_found,
|
||||
throwIfRelationNotFound: data.throw_if_key_not_found
|
||||
? data.throw_if_relation_not_found
|
||||
: undefined,
|
||||
}
|
||||
|
||||
const entities = await query(queryObject, undefined, config)
|
||||
const result = list ? entities : entities[0]
|
||||
|
||||
return new StepResponse(result)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./workflows"
|
||||
export * from "./steps"
|
||||
@@ -0,0 +1,33 @@
|
||||
import { CreateCustomerGroupDTO, ICustomerModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export const createCustomerGroupsStepId = "create-customer-groups"
|
||||
export const createCustomerGroupsStep = createStep(
|
||||
createCustomerGroupsStepId,
|
||||
async (data: CreateCustomerGroupDTO[], { container }) => {
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
const createdCustomerGroups = await service.createCustomerGroup(data)
|
||||
|
||||
return new StepResponse(
|
||||
createdCustomerGroups,
|
||||
createdCustomerGroups.map(
|
||||
(createdCustomerGroups) => createdCustomerGroups.id
|
||||
)
|
||||
)
|
||||
},
|
||||
async (createdCustomerGroupIds, { container }) => {
|
||||
if (!createdCustomerGroupIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await service.delete(createdCustomerGroupIds)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,28 @@
|
||||
import { GroupCustomerPair, ICustomerModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export const deleteCustomerGroupCustomersStepId =
|
||||
"delete-customer-group-customers"
|
||||
export const deleteCustomerGroupCustomersStep = createStep(
|
||||
deleteCustomerGroupCustomersStepId,
|
||||
async (data: GroupCustomerPair[], { container }) => {
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await service.removeCustomerFromGroup(data)
|
||||
|
||||
return new StepResponse(void 0, data)
|
||||
},
|
||||
async (groupPairs, { container }) => {
|
||||
if (!groupPairs?.length) {
|
||||
return
|
||||
}
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await service.addCustomerToGroup(groupPairs)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ICustomerModuleService } from "@medusajs/types"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
type DeleteCustomerGroupStepInput = string[]
|
||||
|
||||
export const deleteCustomerGroupStepId = "delete-customer-groups"
|
||||
export const deleteCustomerGroupStep = createStep(
|
||||
deleteCustomerGroupStepId,
|
||||
async (ids: DeleteCustomerGroupStepInput, { container }) => {
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await service.softDeleteCustomerGroups(ids)
|
||||
|
||||
return new StepResponse(void 0, ids)
|
||||
},
|
||||
async (prevCustomerGroups, { container }) => {
|
||||
if (!prevCustomerGroups) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await service.restoreCustomerGroups(prevCustomerGroups)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from "./update-customer-groups"
|
||||
export * from "./delete-customer-groups"
|
||||
export * from "./create-customer-groups"
|
||||
export * from "./link-customers-customer-group"
|
||||
@@ -0,0 +1,56 @@
|
||||
import { ICustomerModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { LinkWorkflowInput } from "@medusajs/types/src"
|
||||
import { promiseAll } from "@medusajs/utils"
|
||||
|
||||
export const linkCustomersToCustomerGroupStepId =
|
||||
"link-customers-to-customer-group"
|
||||
export const linkCustomersToCustomerGroupStep = createStep(
|
||||
linkCustomersToCustomerGroupStepId,
|
||||
async (data: LinkWorkflowInput, { container }) => {
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
const toAdd = (data.add ?? []).map((customerId) => {
|
||||
return {
|
||||
customer_id: customerId,
|
||||
customer_group_id: data.id,
|
||||
}
|
||||
})
|
||||
|
||||
const toRemove = (data.remove ?? []).map((customerId) => {
|
||||
return {
|
||||
customer_id: customerId,
|
||||
customer_group_id: data.id,
|
||||
}
|
||||
})
|
||||
|
||||
const promises: Promise<any>[] = []
|
||||
if (toAdd.length) {
|
||||
promises.push(service.addCustomerToGroup(toAdd))
|
||||
}
|
||||
if (toRemove.length) {
|
||||
promises.push(service.removeCustomerFromGroup(toRemove))
|
||||
}
|
||||
await promiseAll(promises)
|
||||
|
||||
return new StepResponse(void 0, { toAdd, toRemove })
|
||||
},
|
||||
async (prevData, { container }) => {
|
||||
if (!prevData) {
|
||||
return
|
||||
}
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
if (prevData.toAdd.length) {
|
||||
await service.removeCustomerFromGroup(prevData.toAdd)
|
||||
}
|
||||
if (prevData.toRemove.length) {
|
||||
await service.addCustomerToGroup(prevData.toRemove)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
CustomerGroupUpdatableFields,
|
||||
FilterableCustomerGroupProps,
|
||||
ICustomerModuleService,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
getSelectsAndRelationsFromObjectArray,
|
||||
promiseAll,
|
||||
} from "@medusajs/utils"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
type UpdateCustomerGroupStepInput = {
|
||||
selector: FilterableCustomerGroupProps
|
||||
update: CustomerGroupUpdatableFields
|
||||
}
|
||||
|
||||
export const updateCustomerGroupStepId = "update-customer-groups"
|
||||
export const updateCustomerGroupsStep = createStep(
|
||||
updateCustomerGroupStepId,
|
||||
async (data: UpdateCustomerGroupStepInput, { container }) => {
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
|
||||
data.update,
|
||||
])
|
||||
const prevCustomerGroups = await service.listCustomerGroups(data.selector, {
|
||||
select: selects,
|
||||
relations,
|
||||
})
|
||||
|
||||
const customers = await service.updateCustomerGroups(
|
||||
data.selector,
|
||||
data.update
|
||||
)
|
||||
|
||||
return new StepResponse(customers, prevCustomerGroups)
|
||||
},
|
||||
async (prevCustomerGroups, { container }) => {
|
||||
if (!prevCustomerGroups) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await promiseAll(
|
||||
prevCustomerGroups.map((c) =>
|
||||
service.updateCustomerGroups(c.id, {
|
||||
name: c.name,
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
import { CustomerGroupDTO, CreateCustomerGroupDTO } from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { createCustomerGroupsStep } from "../steps"
|
||||
|
||||
type WorkflowInput = { customersData: CreateCustomerGroupDTO[] }
|
||||
|
||||
export const createCustomerGroupsWorkflowId = "create-customer-groups"
|
||||
export const createCustomerGroupsWorkflow = createWorkflow(
|
||||
createCustomerGroupsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<CustomerGroupDTO[]> => {
|
||||
return createCustomerGroupsStep(input.customersData)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { deleteCustomerGroupStep } from "../steps"
|
||||
|
||||
type WorkflowInput = { ids: string[] }
|
||||
|
||||
export const deleteCustomerGroupsWorkflowId = "delete-customer-groups"
|
||||
export const deleteCustomerGroupsWorkflow = createWorkflow(
|
||||
deleteCustomerGroupsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
|
||||
return deleteCustomerGroupStep(input.ids)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from "./update-customer-groups"
|
||||
export * from "./delete-customer-groups"
|
||||
export * from "./create-customer-groups"
|
||||
export * from "./link-customers-customer-group"
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { linkCustomersToCustomerGroupStep } from "../steps"
|
||||
import { LinkWorkflowInput } from "@medusajs/types/src"
|
||||
|
||||
export const linkCustomersToCustomerGroupWorkflowId =
|
||||
"link-customers-to-customer-group"
|
||||
export const linkCustomersToCustomerGroupWorkflow = createWorkflow(
|
||||
linkCustomersToCustomerGroupWorkflowId,
|
||||
(input: WorkflowData<LinkWorkflowInput>): WorkflowData<void> => {
|
||||
return linkCustomersToCustomerGroupStep(input)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
import {
|
||||
CustomerGroupDTO,
|
||||
FilterableCustomerGroupProps,
|
||||
CustomerGroupUpdatableFields,
|
||||
} from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { updateCustomerGroupsStep } from "../steps"
|
||||
|
||||
type WorkflowInput = {
|
||||
selector: FilterableCustomerGroupProps
|
||||
update: CustomerGroupUpdatableFields
|
||||
}
|
||||
|
||||
export const updateCustomerGroupsWorkflowId = "update-customer-groups"
|
||||
export const updateCustomerGroupsWorkflow = createWorkflow(
|
||||
updateCustomerGroupsWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<CustomerGroupDTO[]> => {
|
||||
return updateCustomerGroupsStep(input)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./steps"
|
||||
export * from "./workflows"
|
||||
@@ -0,0 +1,34 @@
|
||||
import {
|
||||
CreateCustomerAddressDTO,
|
||||
ICustomerModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export const createCustomerAddressesStepId = "create-customer-addresses"
|
||||
export const createCustomerAddressesStep = createStep(
|
||||
createCustomerAddressesStepId,
|
||||
async (data: CreateCustomerAddressDTO[], { container }) => {
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
const addresses = await service.addAddresses(data)
|
||||
|
||||
return new StepResponse(
|
||||
addresses,
|
||||
addresses.map((address) => address.id)
|
||||
)
|
||||
},
|
||||
async (ids, { container }) => {
|
||||
if (!ids?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await service.deleteAddresses(ids)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
import { CreateCustomerDTO, ICustomerModuleService } from "@medusajs/types"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
|
||||
export const createCustomersStepId = "create-customers"
|
||||
export const createCustomersStep = createStep(
|
||||
createCustomersStepId,
|
||||
async (data: CreateCustomerDTO[], { container }) => {
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
const createdCustomers = await service.create(data)
|
||||
|
||||
return new StepResponse(
|
||||
createdCustomers,
|
||||
createdCustomers.map((createdCustomers) => createdCustomers.id)
|
||||
)
|
||||
},
|
||||
async (createdCustomerIds, { container }) => {
|
||||
if (!createdCustomerIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await service.delete(createdCustomerIds)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { ICustomerModuleService } from "@medusajs/types"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
type DeleteCustomerAddressStepInput = string[]
|
||||
export const deleteCustomerAddressesStepId = "delete-customer-addresses"
|
||||
export const deleteCustomerAddressesStep = createStep(
|
||||
deleteCustomerAddressesStepId,
|
||||
async (ids: DeleteCustomerAddressStepInput, { container }) => {
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
const existing = await service.listAddresses({
|
||||
id: ids,
|
||||
})
|
||||
await service.deleteAddresses(ids)
|
||||
|
||||
return new StepResponse(void 0, existing)
|
||||
},
|
||||
async (prevAddresses, { container }) => {
|
||||
if (!prevAddresses?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await service.addAddresses(prevAddresses)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { ICustomerModuleService } from "@medusajs/types"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
type DeleteCustomerStepInput = string[]
|
||||
|
||||
export const deleteCustomersStepId = "delete-customers"
|
||||
export const deleteCustomersStep = createStep(
|
||||
deleteCustomersStepId,
|
||||
async (ids: DeleteCustomerStepInput, { container }) => {
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await service.softDelete(ids)
|
||||
|
||||
return new StepResponse(void 0, ids)
|
||||
},
|
||||
async (prevCustomerIds, { container }) => {
|
||||
if (!prevCustomerIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await service.restore(prevCustomerIds)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
export * from "./create-customers"
|
||||
export * from "./update-customers"
|
||||
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 {
|
||||
CreateCustomerAddressDTO,
|
||||
UpdateCustomerAddressDTO,
|
||||
FilterableCustomerAddressProps,
|
||||
ICustomerModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { createStep } from "@medusajs/workflows-sdk"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { unsetForCreate, unsetForUpdate } from "./utils"
|
||||
import { isDefined } from "@medusajs/utils"
|
||||
|
||||
type StepInput = {
|
||||
create?: CreateCustomerAddressDTO[]
|
||||
update?: {
|
||||
selector: FilterableCustomerAddressProps
|
||||
update: UpdateCustomerAddressDTO
|
||||
}
|
||||
}
|
||||
|
||||
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.updateAddresses(
|
||||
{ id: addressesToSet },
|
||||
{ is_default_billing: true }
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
CreateCustomerAddressDTO,
|
||||
UpdateCustomerAddressDTO,
|
||||
FilterableCustomerAddressProps,
|
||||
ICustomerModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { createStep } from "@medusajs/workflows-sdk"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { unsetForCreate, unsetForUpdate } from "./utils"
|
||||
import { isDefined } from "@medusajs/utils"
|
||||
|
||||
type StepInput = {
|
||||
create?: CreateCustomerAddressDTO[]
|
||||
update?: {
|
||||
selector: FilterableCustomerAddressProps
|
||||
update: UpdateCustomerAddressDTO
|
||||
}
|
||||
}
|
||||
|
||||
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.updateAddresses(
|
||||
{ id: addressesToSet },
|
||||
{ is_default_shipping: true }
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,54 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
UpdateCustomerAddressDTO,
|
||||
FilterableCustomerAddressProps,
|
||||
ICustomerModuleService,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
getSelectsAndRelationsFromObjectArray,
|
||||
promiseAll,
|
||||
} from "@medusajs/utils"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
type UpdateCustomerAddresseStepInput = {
|
||||
selector: FilterableCustomerAddressProps
|
||||
update: UpdateCustomerAddressDTO
|
||||
}
|
||||
|
||||
export const updateCustomerAddresseStepId = "update-customer-addresses"
|
||||
export const updateCustomerAddressesStep = createStep(
|
||||
updateCustomerAddresseStepId,
|
||||
async (data: UpdateCustomerAddresseStepInput, { container }) => {
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
|
||||
data.update,
|
||||
])
|
||||
const prevCustomers = await service.listAddresses(data.selector, {
|
||||
select: selects,
|
||||
relations,
|
||||
})
|
||||
|
||||
const customerAddresses = await service.updateAddresses(
|
||||
data.selector,
|
||||
data.update
|
||||
)
|
||||
|
||||
return new StepResponse(customerAddresses, prevCustomers)
|
||||
},
|
||||
async (prevCustomerAddresses, { container }) => {
|
||||
if (!prevCustomerAddresses) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await promiseAll(
|
||||
prevCustomerAddresses.map((c) => service.updateAddresses(c.id, { ...c }))
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,59 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
FilterableCustomerProps,
|
||||
ICustomerModuleService,
|
||||
CustomerUpdatableFields,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
getSelectsAndRelationsFromObjectArray,
|
||||
promiseAll,
|
||||
} from "@medusajs/utils"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
type UpdateCustomersStepInput = {
|
||||
selector: FilterableCustomerProps
|
||||
update: CustomerUpdatableFields
|
||||
}
|
||||
|
||||
export const updateCustomersStepId = "update-customer"
|
||||
export const updateCustomersStep = createStep(
|
||||
updateCustomersStepId,
|
||||
async (data: UpdateCustomersStepInput, { container }) => {
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
|
||||
data.update,
|
||||
])
|
||||
const prevCustomers = await service.list(data.selector, {
|
||||
select: selects,
|
||||
relations,
|
||||
})
|
||||
|
||||
const customers = await service.update(data.selector, data.update)
|
||||
|
||||
return new StepResponse(customers, prevCustomers)
|
||||
},
|
||||
async (prevCustomers, { container }) => {
|
||||
if (!prevCustomers?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await promiseAll(
|
||||
prevCustomers.map((c) =>
|
||||
service.update(c.id, {
|
||||
first_name: c.first_name,
|
||||
last_name: c.last_name,
|
||||
email: c.email,
|
||||
phone: c.phone,
|
||||
metadata: c.metadata,
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -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.updateAddresses(
|
||||
{ customer_id: customerIds, [field]: true },
|
||||
{ [field]: false }
|
||||
)
|
||||
|
||||
return new StepResponse(
|
||||
void 0,
|
||||
customerDefaultAddresses.map((address) => address.id)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import {
|
||||
UpdateCustomerAddressDTO,
|
||||
FilterableCustomerAddressProps,
|
||||
ICustomerModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const unsetForUpdate = async (
|
||||
data: {
|
||||
selector: FilterableCustomerAddressProps
|
||||
update: UpdateCustomerAddressDTO
|
||||
},
|
||||
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.updateAddresses(
|
||||
{ customer_id: customerIds, [field]: true },
|
||||
{ [field]: false }
|
||||
)
|
||||
|
||||
return new StepResponse(
|
||||
void 0,
|
||||
customerDefaultAddresses.map((address) => address.id)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { CreateCustomerAddressDTO, CustomerAddressDTO } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
parallelize,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
createCustomerAddressesStep,
|
||||
maybeUnsetDefaultBillingAddressesStep,
|
||||
maybeUnsetDefaultShippingAddressesStep,
|
||||
} from "../steps"
|
||||
|
||||
type WorkflowInput = { addresses: CreateCustomerAddressDTO[] }
|
||||
|
||||
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)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import { CreateCustomerDTO, CustomerDTO } from "@medusajs/types"
|
||||
import { createWorkflow, WorkflowData } from "@medusajs/workflows-sdk"
|
||||
import { createCustomersStep } from "../steps"
|
||||
import { setAuthAppMetadataStep } from "../../auth/steps"
|
||||
import { transform } from "@medusajs/workflows-sdk"
|
||||
|
||||
type WorkflowInput = {
|
||||
authUserId: string
|
||||
customersData: CreateCustomerDTO
|
||||
}
|
||||
|
||||
export const createCustomerAccountWorkflowId = "create-customer-account"
|
||||
export const createCustomerAccountWorkflow = createWorkflow(
|
||||
createCustomerAccountWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<CustomerDTO> => {
|
||||
const customers = createCustomersStep([input.customersData])
|
||||
|
||||
const customer = transform(
|
||||
customers,
|
||||
(customers: CustomerDTO[]) => customers[0]
|
||||
)
|
||||
|
||||
setAuthAppMetadataStep({
|
||||
authUserId: input.authUserId,
|
||||
key: "customer_id",
|
||||
value: customer.id,
|
||||
})
|
||||
|
||||
return customer
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
import { CustomerDTO, CreateCustomerDTO } from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { createCustomersStep } from "../steps"
|
||||
|
||||
type WorkflowInput = { customersData: CreateCustomerDTO[] }
|
||||
|
||||
export const createCustomersWorkflowId = "create-customers"
|
||||
export const createCustomersWorkflow = createWorkflow(
|
||||
createCustomersWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<CustomerDTO[]> => {
|
||||
return createCustomersStep(input.customersData)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { deleteCustomerAddressesStep } from "../steps"
|
||||
|
||||
type WorkflowInput = { ids: string[] }
|
||||
|
||||
export const deleteCustomerAddressesWorkflowId = "delete-customer-addresses"
|
||||
export const deleteCustomerAddressesWorkflow = createWorkflow(
|
||||
deleteCustomerAddressesWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
|
||||
return deleteCustomerAddressesStep(input.ids)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { deleteCustomersStep } from "../steps"
|
||||
|
||||
type WorkflowInput = { ids: string[] }
|
||||
|
||||
export const deleteCustomersWorkflowId = "delete-customers"
|
||||
export const deleteCustomersWorkflow = createWorkflow(
|
||||
deleteCustomersWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<void> => {
|
||||
return deleteCustomersStep(input.ids)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
export * from "./create-customers"
|
||||
export * from "./update-customers"
|
||||
export * from "./delete-customers"
|
||||
export * from "./create-customer-account"
|
||||
export * from "./create-addresses"
|
||||
export * from "./update-addresses"
|
||||
export * from "./delete-addresses"
|
||||
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
FilterableCustomerAddressProps,
|
||||
CustomerAddressDTO,
|
||||
UpdateCustomerAddressDTO,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
parallelize,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import {
|
||||
maybeUnsetDefaultBillingAddressesStep,
|
||||
maybeUnsetDefaultShippingAddressesStep,
|
||||
updateCustomerAddressesStep,
|
||||
} from "../steps"
|
||||
|
||||
type WorkflowInput = {
|
||||
selector: FilterableCustomerAddressProps
|
||||
update: UpdateCustomerAddressDTO
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
import {
|
||||
CustomerDTO,
|
||||
CustomerUpdatableFields,
|
||||
FilterableCustomerProps,
|
||||
} from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { updateCustomersStep } from "../steps"
|
||||
|
||||
type UpdateCustomersStepInput = {
|
||||
selector: FilterableCustomerProps
|
||||
update: CustomerUpdatableFields
|
||||
}
|
||||
|
||||
type WorkflowInput = UpdateCustomersStepInput
|
||||
|
||||
export const updateCustomersWorkflowId = "update-customers"
|
||||
export const updateCustomersWorkflow = createWorkflow(
|
||||
updateCustomersWorkflowId,
|
||||
(input: WorkflowData<WorkflowInput>): WorkflowData<CustomerDTO[]> => {
|
||||
return updateCustomersStep(input)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from "./steps"
|
||||
export * from "./workflows"
|
||||
@@ -0,0 +1,49 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CreateStoreDTO, IStoreModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
import { createStoresWorkflow } from "../../store"
|
||||
|
||||
type CreateDefaultStoreStepInput = {
|
||||
store: CreateStoreDTO
|
||||
}
|
||||
|
||||
export const createDefaultStoreStepId = "create-default-store"
|
||||
export const createDefaultStoreStep = createStep(
|
||||
createDefaultStoreStepId,
|
||||
async (data: CreateDefaultStoreStepInput, { container }) => {
|
||||
const storeService = container.resolve(ModuleRegistrationName.STORE)
|
||||
|
||||
let shouldDelete = false
|
||||
let [store] = await storeService.list({}, { take: 1 })
|
||||
|
||||
if (!store) {
|
||||
store = await createStoresWorkflow(container).run({
|
||||
input: {
|
||||
stores: [
|
||||
{
|
||||
// TODO: Revisit for a more sophisticated approach
|
||||
...data.store,
|
||||
supported_currency_codes: ["usd"],
|
||||
default_currency_code: "usd",
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
|
||||
shouldDelete = true
|
||||
}
|
||||
|
||||
return new StepResponse(store, { storeId: store.id, shouldDelete })
|
||||
},
|
||||
async (data, { container }) => {
|
||||
if (!data || !data.shouldDelete) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<IStoreModuleService>(
|
||||
ModuleRegistrationName.STORE
|
||||
)
|
||||
|
||||
await service.delete(data.storeId)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./create-default-store"
|
||||
@@ -0,0 +1,23 @@
|
||||
import { createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { createDefaultSalesChannelStep } from "../../sales-channel"
|
||||
import { createDefaultStoreStep } from "../steps/create-default-store"
|
||||
|
||||
export const createDefaultsWorkflowID = "create-defaults"
|
||||
export const createDefaultsWorkflow = createWorkflow(
|
||||
createDefaultsWorkflowID,
|
||||
(input) => {
|
||||
const salesChannel = createDefaultSalesChannelStep({
|
||||
data: {
|
||||
name: "Default Sales Channel",
|
||||
description: "Created by Medusa",
|
||||
},
|
||||
})
|
||||
const store = createDefaultStoreStep({
|
||||
store: {
|
||||
default_sales_channel_id: salesChannel.id,
|
||||
},
|
||||
})
|
||||
|
||||
return store
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1 @@
|
||||
export * from "./create-defaults"
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from "./steps"
|
||||
export * from "./workflows"
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CreateShippingMethodDTO, ICartModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
shipping_methods: CreateShippingMethodDTO[]
|
||||
}
|
||||
|
||||
export const addShippingMethodToCartStepId = "add-shipping-method-to-cart-step"
|
||||
export const addShippingMethodToCartStep = createStep(
|
||||
addShippingMethodToCartStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const cartService = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const methods = await cartService.addShippingMethods(data.shipping_methods)
|
||||
|
||||
return new StepResponse(methods, methods)
|
||||
},
|
||||
async (methods, { container }) => {
|
||||
const cartService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
if (!methods?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await cartService.deleteShippingMethods(methods.map((m) => m.id))
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CreateLineItemForCartDTO, ICartModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
items: CreateLineItemForCartDTO[]
|
||||
}
|
||||
|
||||
export const addToCartStepId = "add-to-cart-step"
|
||||
export const addToCartStep = createStep(
|
||||
addToCartStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const cartService = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const items = await cartService.addLineItems(data.items)
|
||||
|
||||
return new StepResponse(items, items)
|
||||
},
|
||||
async (createdLineItems, { container }) => {
|
||||
const cartService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
if (!createdLineItems?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await cartService.deleteLineItems(createdLineItems.map((c) => c.id))
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,47 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IInventoryService } from "@medusajs/types"
|
||||
import { promiseAll } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
interface StepInput {
|
||||
items: {
|
||||
inventory_item_id: string
|
||||
required_quantity: number
|
||||
quantity: number
|
||||
location_ids: string[]
|
||||
}[]
|
||||
}
|
||||
|
||||
export const confirmInventoryStepId = "confirm-inventory-step"
|
||||
export const confirmInventoryStep = createStep(
|
||||
confirmInventoryStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const inventoryService = container.resolve<IInventoryService>(
|
||||
ModuleRegistrationName.INVENTORY
|
||||
)
|
||||
|
||||
// TODO: Should be bulk
|
||||
const promises = data.items.map(async (item) => {
|
||||
const itemQuantity = item.required_quantity * item.quantity
|
||||
|
||||
return await inventoryService.confirmInventory(
|
||||
item.inventory_item_id,
|
||||
item.location_ids,
|
||||
itemQuantity
|
||||
)
|
||||
})
|
||||
|
||||
const inventoryCoverage = await promiseAll(promises)
|
||||
|
||||
if (inventoryCoverage.some((hasCoverage) => !hasCoverage)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_ALLOWED,
|
||||
`Some variant does not have the required inventory`,
|
||||
MedusaError.Codes.INSUFFICIENT_INVENTORY
|
||||
)
|
||||
}
|
||||
|
||||
return new StepResponse(null)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CreateCartDTO, ICartModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const createCartsStepId = "create-carts"
|
||||
export const createCartsStep = createStep(
|
||||
createCartsStepId,
|
||||
async (data: CreateCartDTO[], { container }) => {
|
||||
const service = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const createdCarts = await service.create(data)
|
||||
|
||||
return new StepResponse(
|
||||
createdCarts,
|
||||
createdCarts.map((cart) => cart.id)
|
||||
)
|
||||
},
|
||||
async (createdCartsIds, { container }) => {
|
||||
if (!createdCartsIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
await service.delete(createdCartsIds)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,41 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
CreateLineItemAdjustmentDTO,
|
||||
ICartModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
lineItemAdjustmentsToCreate: CreateLineItemAdjustmentDTO[]
|
||||
}
|
||||
|
||||
export const createLineItemAdjustmentsStepId = "create-line-item-adjustments"
|
||||
export const createLineItemAdjustmentsStep = createStep(
|
||||
createLineItemAdjustmentsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { lineItemAdjustmentsToCreate = [] } = data
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const createdLineItemAdjustments =
|
||||
await cartModuleService.addLineItemAdjustments(
|
||||
lineItemAdjustmentsToCreate
|
||||
)
|
||||
|
||||
return new StepResponse(void 0, createdLineItemAdjustments)
|
||||
},
|
||||
async (createdLineItemAdjustments, { container }) => {
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
if (!createdLineItemAdjustments?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await cartModuleService.softDeleteLineItemAdjustments(
|
||||
createdLineItemAdjustments.map((c) => c.id)
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,57 @@
|
||||
import { CartWorkflowDTO } from "@medusajs/types"
|
||||
import { OrderStatus } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
import { createOrdersWorkflow } from "../../../order/workflows/create-orders"
|
||||
|
||||
interface StepInput {
|
||||
cart: CartWorkflowDTO
|
||||
}
|
||||
|
||||
export const createOrderFromCartStepId = "create-order-from-cart"
|
||||
export const createOrderFromCartStep = createStep(
|
||||
createOrderFromCartStepId,
|
||||
async (input: StepInput, { container }) => {
|
||||
const { cart } = input
|
||||
|
||||
const itemAdjustments = (cart.items || [])
|
||||
?.map((item) => item.adjustments || [])
|
||||
.flat(1)
|
||||
const shippingAdjustments = (cart.shipping_methods || [])
|
||||
?.map((sm) => sm.adjustments || [])
|
||||
.flat(1)
|
||||
|
||||
const promoCodes = [...itemAdjustments, ...shippingAdjustments]
|
||||
.map((adjustment) => adjustment.code)
|
||||
.filter((code) => Boolean) as string[]
|
||||
|
||||
const { transaction, result } = await createOrdersWorkflow(container).run({
|
||||
input: {
|
||||
region_id: cart.region?.id,
|
||||
customer_id: cart.customer?.id,
|
||||
sales_channel_id: cart.sales_channel_id,
|
||||
status: OrderStatus.PENDING,
|
||||
email: cart.email,
|
||||
currency_code: cart.currency_code,
|
||||
shipping_address: cart.shipping_address,
|
||||
billing_address: cart.billing_address,
|
||||
// TODO: This should be handle correctly
|
||||
no_notification: false,
|
||||
items: cart.items,
|
||||
shipping_methods: cart.shipping_methods,
|
||||
metadata: cart.metadata,
|
||||
promo_codes: promoCodes,
|
||||
},
|
||||
})
|
||||
|
||||
return new StepResponse(result, { transaction })
|
||||
},
|
||||
async (flow, { container }) => {
|
||||
if (!flow) {
|
||||
return
|
||||
}
|
||||
|
||||
await createOrdersWorkflow(container).cancel({
|
||||
transaction: flow.transaction,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { BigNumberInput, IPaymentModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type StepInput = {
|
||||
region_id: string
|
||||
currency_code: string
|
||||
amount: BigNumberInput
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export const createPaymentCollectionsStepId = "create-payment-collections"
|
||||
export const createPaymentCollectionsStep = createStep(
|
||||
createPaymentCollectionsStepId,
|
||||
async (data: StepInput[], { container }) => {
|
||||
const service = container.resolve<IPaymentModuleService>(
|
||||
ModuleRegistrationName.PAYMENT
|
||||
)
|
||||
|
||||
const created = await service.createPaymentCollections(data)
|
||||
|
||||
return new StepResponse(
|
||||
created,
|
||||
created.map((collection) => collection.id)
|
||||
)
|
||||
},
|
||||
async (createdIds, { container }) => {
|
||||
if (!createdIds?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<IPaymentModuleService>(
|
||||
ModuleRegistrationName.PAYMENT
|
||||
)
|
||||
|
||||
await service.deletePaymentCollections(createdIds)
|
||||
}
|
||||
)
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
CreateShippingMethodAdjustmentDTO,
|
||||
ICartModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
shippingMethodAdjustmentsToCreate: CreateShippingMethodAdjustmentDTO[]
|
||||
}
|
||||
|
||||
export const createShippingMethodAdjustmentsStepId =
|
||||
"create-shipping-method-adjustments"
|
||||
export const createShippingMethodAdjustmentsStep = createStep(
|
||||
createShippingMethodAdjustmentsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { shippingMethodAdjustmentsToCreate = [] } = data
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const createdShippingMethodAdjustments =
|
||||
await cartModuleService.addShippingMethodAdjustments(
|
||||
shippingMethodAdjustmentsToCreate
|
||||
)
|
||||
|
||||
return new StepResponse(void 0, createdShippingMethodAdjustments)
|
||||
},
|
||||
async (createdShippingMethodAdjustments, { container }) => {
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
if (!createdShippingMethodAdjustments?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await cartModuleService.softDeleteShippingMethodAdjustments(
|
||||
createdShippingMethodAdjustments.map((c) => c.id)
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IRegionModuleService } from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const findOneOrAnyRegionStepId = "find-one-or-any-region"
|
||||
export const findOneOrAnyRegionStep = createStep(
|
||||
findOneOrAnyRegionStepId,
|
||||
async (data: { regionId?: string }, { container }) => {
|
||||
const service = container.resolve<IRegionModuleService>(
|
||||
ModuleRegistrationName.REGION
|
||||
)
|
||||
|
||||
if (!data.regionId) {
|
||||
const regions = await service.list({})
|
||||
|
||||
if (!regions?.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
"No regions found"
|
||||
)
|
||||
}
|
||||
|
||||
return new StepResponse(regions[0])
|
||||
}
|
||||
|
||||
const region = await service.retrieve(data.regionId)
|
||||
|
||||
return new StepResponse(region)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,93 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CustomerDTO, ICustomerModuleService } from "@medusajs/types"
|
||||
import { validateEmail } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
customerId?: string | null
|
||||
email?: string | null
|
||||
}
|
||||
|
||||
interface StepOutput {
|
||||
customer?: CustomerDTO | null
|
||||
email?: string | null
|
||||
}
|
||||
|
||||
interface StepCompensateInput {
|
||||
customer?: CustomerDTO
|
||||
customerWasCreated: boolean
|
||||
}
|
||||
|
||||
export const findOrCreateCustomerStepId = "find-or-create-customer"
|
||||
export const findOrCreateCustomerStep = createStep(
|
||||
findOrCreateCustomerStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
if (
|
||||
typeof data.customerId === undefined &&
|
||||
typeof data.email === undefined
|
||||
) {
|
||||
return new StepResponse(
|
||||
{
|
||||
customer: undefined,
|
||||
email: undefined,
|
||||
},
|
||||
{ customerWasCreated: false }
|
||||
)
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
const customerData: StepOutput = {
|
||||
customer: null,
|
||||
email: null,
|
||||
}
|
||||
let customerWasCreated = false
|
||||
|
||||
if (data.customerId) {
|
||||
const customer = await service.retrieve(data.customerId)
|
||||
customerData.customer = customer
|
||||
customerData.email = customer.email
|
||||
|
||||
return new StepResponse(customerData, {
|
||||
customerWasCreated,
|
||||
})
|
||||
}
|
||||
|
||||
if (data.email) {
|
||||
const validatedEmail = validateEmail(data.email)
|
||||
|
||||
let [customer] = await service.list({
|
||||
email: validatedEmail,
|
||||
has_account: false,
|
||||
})
|
||||
|
||||
if (!customer) {
|
||||
customer = await service.create({ email: validatedEmail })
|
||||
customerWasCreated = true
|
||||
}
|
||||
|
||||
customerData.customer = customer
|
||||
customerData.email = customer.email
|
||||
}
|
||||
|
||||
return new StepResponse(customerData, {
|
||||
customer: customerData.customer,
|
||||
customerWasCreated,
|
||||
})
|
||||
},
|
||||
async (compData, { container }) => {
|
||||
const { customer, customerWasCreated } = compData as StepCompensateInput
|
||||
|
||||
if (!customerWasCreated || !customer?.id) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<ICustomerModuleService>(
|
||||
ModuleRegistrationName.CUSTOMER
|
||||
)
|
||||
|
||||
await service.delete(customer.id)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { ISalesChannelModuleService, SalesChannelDTO } from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
salesChannelId?: string | null
|
||||
}
|
||||
|
||||
export const findSalesChannelStepId = "find-sales-channel"
|
||||
export const findSalesChannelStep = createStep(
|
||||
findSalesChannelStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const salesChannelService = container.resolve<ISalesChannelModuleService>(
|
||||
ModuleRegistrationName.SALES_CHANNEL
|
||||
)
|
||||
|
||||
if (data.salesChannelId === null) {
|
||||
return new StepResponse(null)
|
||||
}
|
||||
|
||||
let salesChannel: SalesChannelDTO | undefined
|
||||
if (data.salesChannelId) {
|
||||
salesChannel = await salesChannelService.retrieve(data.salesChannelId)
|
||||
} else {
|
||||
// TODO: Find default sales channel from store
|
||||
}
|
||||
|
||||
if (salesChannel?.is_disabled) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Unable to assign cart to disabled Sales Channel: ${salesChannel.name}`
|
||||
)
|
||||
}
|
||||
|
||||
return new StepResponse(salesChannel)
|
||||
}
|
||||
)
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { LinkModuleUtils, ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CartDTO, IPromotionModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
cart: CartDTO
|
||||
}
|
||||
|
||||
export const getActionsToComputeFromPromotionsStepId =
|
||||
"get-actions-to-compute-from-promotions"
|
||||
export const getActionsToComputeFromPromotionsStep = createStep(
|
||||
getActionsToComputeFromPromotionsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { cart } = data
|
||||
const remoteQuery = container.resolve(LinkModuleUtils.REMOTE_QUERY)
|
||||
const promotionService = container.resolve<IPromotionModuleService>(
|
||||
ModuleRegistrationName.PROMOTION
|
||||
)
|
||||
|
||||
const existingCartPromotionLinks = await remoteQuery({
|
||||
cart_promotion: {
|
||||
__args: { cart_id: [cart.id] },
|
||||
fields: ["id", "cart_id", "promotion_id", "deleted_at"],
|
||||
},
|
||||
})
|
||||
|
||||
const existingPromotions = await promotionService.list(
|
||||
{ id: existingCartPromotionLinks.map((l) => l.promotion_id) },
|
||||
{ take: null, select: ["code"] }
|
||||
)
|
||||
|
||||
const actionsToCompute = await promotionService.computeActions(
|
||||
existingPromotions.map((p) => p.code!),
|
||||
cart as any
|
||||
)
|
||||
|
||||
return new StepResponse(actionsToCompute)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,136 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
CartLineItemDTO,
|
||||
CartShippingMethodDTO,
|
||||
CartWorkflowDTO,
|
||||
ITaxModuleService,
|
||||
ItemTaxLineDTO,
|
||||
ShippingTaxLineDTO,
|
||||
TaxCalculationContext,
|
||||
TaxableItemDTO,
|
||||
TaxableShippingDTO,
|
||||
} from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
cart: CartWorkflowDTO
|
||||
items: CartLineItemDTO[]
|
||||
shipping_methods: CartShippingMethodDTO[]
|
||||
force_tax_calculation?: boolean
|
||||
}
|
||||
|
||||
function normalizeTaxModuleContext(
|
||||
cart: CartWorkflowDTO,
|
||||
forceTaxCalculation: boolean
|
||||
): TaxCalculationContext | null {
|
||||
const address = cart.shipping_address
|
||||
const shouldCalculateTax = forceTaxCalculation || cart.region?.automatic_taxes
|
||||
|
||||
if (!shouldCalculateTax) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (forceTaxCalculation && !address?.country_code) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`country code is required to calculate taxes`
|
||||
)
|
||||
}
|
||||
|
||||
if (!address?.country_code) {
|
||||
return null
|
||||
}
|
||||
|
||||
const 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,
|
||||
force_tax_calculation: forceTaxCalculation = false,
|
||||
} = data
|
||||
|
||||
const taxService = container.resolve<ITaxModuleService>(
|
||||
ModuleRegistrationName.TAX
|
||||
)
|
||||
|
||||
const taxContext = normalizeTaxModuleContext(cart, forceTaxCalculation)
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,82 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IPricingModuleService } from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
variantIds: string[]
|
||||
context?: Record<string, unknown>
|
||||
}
|
||||
|
||||
export const getVariantPriceSetsStepId = "get-variant-price-sets"
|
||||
export const getVariantPriceSetsStep = createStep(
|
||||
getVariantPriceSetsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
if (!data.variantIds.length) {
|
||||
return new StepResponse({})
|
||||
}
|
||||
|
||||
const pricingModuleService = container.resolve<IPricingModuleService>(
|
||||
ModuleRegistrationName.PRICING
|
||||
)
|
||||
|
||||
const remoteQuery = container.resolve("remoteQuery")
|
||||
|
||||
const variantPriceSets = await remoteQuery(
|
||||
{
|
||||
variant: {
|
||||
fields: ["id"],
|
||||
price_set: {
|
||||
fields: ["id"],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
variant: {
|
||||
id: data.variantIds,
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
const notFound: string[] = []
|
||||
const priceSetIds: string[] = []
|
||||
|
||||
variantPriceSets.forEach((v) => {
|
||||
if (v.price_set?.id) {
|
||||
priceSetIds.push(v.price_set.id)
|
||||
} else {
|
||||
notFound.push(v.id)
|
||||
}
|
||||
})
|
||||
|
||||
if (notFound.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Variants with IDs ${notFound.join(", ")} do not have a price`
|
||||
)
|
||||
}
|
||||
|
||||
const calculatedPriceSets = await pricingModuleService.calculatePrices(
|
||||
{ id: priceSetIds },
|
||||
{ context: data.context as Record<string, string | number> }
|
||||
)
|
||||
|
||||
const idToPriceSet = new Map<string, Record<string, any>>(
|
||||
calculatedPriceSets.map((p) => [p.id, p])
|
||||
)
|
||||
|
||||
const variantToCalculatedPriceSets = variantPriceSets.reduce(
|
||||
(acc, { id, price_set }) => {
|
||||
const calculatedPriceSet = idToPriceSet.get(price_set?.id)
|
||||
if (calculatedPriceSet) {
|
||||
acc[id] = calculatedPriceSet
|
||||
}
|
||||
|
||||
return acc
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
return new StepResponse(variantToCalculatedPriceSets)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
FilterableProductVariantProps,
|
||||
FindConfig,
|
||||
IProductModuleService,
|
||||
ProductVariantDTO,
|
||||
} from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
filter?: FilterableProductVariantProps
|
||||
config?: FindConfig<ProductVariantDTO>
|
||||
}
|
||||
|
||||
export const getVariantsStepId = "get-variants"
|
||||
export const getVariantsStep = createStep(
|
||||
getVariantsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const productModuleService = container.resolve<IProductModuleService>(
|
||||
ModuleRegistrationName.PRODUCT
|
||||
)
|
||||
|
||||
const variants = await productModuleService.listVariants(
|
||||
data.filter,
|
||||
data.config
|
||||
)
|
||||
|
||||
return new StepResponse(variants)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
export * from "./add-shipping-method-to-cart"
|
||||
export * from "./add-to-cart"
|
||||
export * from "./confirm-inventory"
|
||||
export * from "./create-carts"
|
||||
export * from "./create-line-item-adjustments"
|
||||
export * from "./create-order-from-cart"
|
||||
export * from "./create-shipping-method-adjustments"
|
||||
export * from "./find-one-or-any-region"
|
||||
export * from "./find-or-create-customer"
|
||||
export * from "./find-sales-channel"
|
||||
export * from "./get-actions-to-compute-from-promotions"
|
||||
export * from "./get-item-tax-lines"
|
||||
export * from "./get-variant-price-sets"
|
||||
export * from "./get-variants"
|
||||
export * from "./prepare-adjustments-from-promotion-actions"
|
||||
export * from "./refresh-cart-shipping-methods"
|
||||
export * from "./remove-line-item-adjustments"
|
||||
export * from "./remove-shipping-method-adjustments"
|
||||
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-carts"
|
||||
export * from "./validate-cart-shipping-options"
|
||||
export * from "./validate-variants-existence"
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type StepInput = {
|
||||
links: {
|
||||
cart_id: string
|
||||
payment_collection_id: string
|
||||
}[]
|
||||
}
|
||||
|
||||
export const linkCartAndPaymentCollectionsStepId =
|
||||
"link-cart-payment-collection"
|
||||
export const linkCartAndPaymentCollectionsStep = createStep(
|
||||
linkCartAndPaymentCollectionsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||
|
||||
const links = data.links.map((d) => ({
|
||||
[Modules.CART]: { cart_id: d.cart_id },
|
||||
[Modules.PAYMENT]: { payment_collection_id: d.payment_collection_id },
|
||||
}))
|
||||
|
||||
await remoteLink.create(links)
|
||||
|
||||
return new StepResponse(void 0, data)
|
||||
},
|
||||
async (data, { container }) => {
|
||||
if (!data) {
|
||||
return
|
||||
}
|
||||
|
||||
const remoteLink = container.resolve(ContainerRegistrationKeys.REMOTE_LINK)
|
||||
|
||||
const links = data.links.map((d) => ({
|
||||
[Modules.CART]: { cart_id: d.cart_id },
|
||||
[Modules.PAYMENT]: { payment_collection_id: d.payment_collection_id },
|
||||
}))
|
||||
|
||||
await remoteLink.dismiss(links)
|
||||
}
|
||||
)
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
AddItemAdjustmentAction,
|
||||
AddShippingMethodAdjustment,
|
||||
ComputeActions,
|
||||
IPromotionModuleService,
|
||||
PromotionDTO,
|
||||
RemoveItemAdjustmentAction,
|
||||
RemoveShippingMethodAdjustment,
|
||||
} from "@medusajs/types"
|
||||
import { ComputedActions } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
actions: ComputeActions[]
|
||||
}
|
||||
|
||||
export const prepareAdjustmentsFromPromotionActionsStepId =
|
||||
"prepare-adjustments-from-promotion-actions"
|
||||
export const prepareAdjustmentsFromPromotionActionsStep = createStep(
|
||||
prepareAdjustmentsFromPromotionActionsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const promotionModuleService: IPromotionModuleService = container.resolve(
|
||||
ModuleRegistrationName.PROMOTION
|
||||
)
|
||||
|
||||
const { actions = [] } = data
|
||||
const promotions = await promotionModuleService.list(
|
||||
{ code: actions.map((a) => a.code) },
|
||||
{ select: ["id", "code"] }
|
||||
)
|
||||
|
||||
const promotionsMap = new Map<string, PromotionDTO>(
|
||||
promotions.map((promotion) => [promotion.code!, promotion])
|
||||
)
|
||||
|
||||
const lineItemAdjustmentsToCreate = actions
|
||||
.filter((a) => a.action === ComputedActions.ADD_ITEM_ADJUSTMENT)
|
||||
.map((action) => ({
|
||||
code: action.code,
|
||||
amount: (action as AddItemAdjustmentAction).amount,
|
||||
item_id: (action as AddItemAdjustmentAction).item_id,
|
||||
promotion_id: promotionsMap.get(action.code)?.id,
|
||||
}))
|
||||
|
||||
const lineItemAdjustmentIdsToRemove = actions
|
||||
.filter((a) => a.action === ComputedActions.REMOVE_ITEM_ADJUSTMENT)
|
||||
.map((a) => (a as RemoveItemAdjustmentAction).adjustment_id)
|
||||
|
||||
const shippingMethodAdjustmentsToCreate = actions
|
||||
.filter(
|
||||
(a) => a.action === ComputedActions.ADD_SHIPPING_METHOD_ADJUSTMENT
|
||||
)
|
||||
.map((action) => ({
|
||||
code: action.code,
|
||||
amount: (action as AddShippingMethodAdjustment).amount,
|
||||
shipping_method_id: (action as AddShippingMethodAdjustment)
|
||||
.shipping_method_id,
|
||||
promotion_id: promotionsMap.get(action.code)?.id,
|
||||
}))
|
||||
|
||||
const shippingMethodAdjustmentIdsToRemove = actions
|
||||
.filter(
|
||||
(a) => a.action === ComputedActions.REMOVE_SHIPPING_METHOD_ADJUSTMENT
|
||||
)
|
||||
.map((a) => (a as RemoveShippingMethodAdjustment).adjustment_id)
|
||||
|
||||
return new StepResponse({
|
||||
lineItemAdjustmentsToCreate,
|
||||
lineItemAdjustmentIdsToRemove,
|
||||
shippingMethodAdjustmentsToCreate,
|
||||
shippingMethodAdjustmentIdsToRemove,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,30 @@
|
||||
import { PromotionActions } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
import { updateCartPromotionsWorkflow } from "../workflows"
|
||||
|
||||
interface StepInput {
|
||||
id: string
|
||||
promo_codes?: string[]
|
||||
action?:
|
||||
| PromotionActions.ADD
|
||||
| PromotionActions.REMOVE
|
||||
| PromotionActions.REPLACE
|
||||
}
|
||||
|
||||
export const refreshCartPromotionsStepId = "refresh-cart-promotions"
|
||||
export const refreshCartPromotionsStep = createStep(
|
||||
refreshCartPromotionsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { promo_codes = [], id, action = PromotionActions.ADD } = data
|
||||
|
||||
await updateCartPromotionsWorkflow(container).run({
|
||||
input: {
|
||||
action,
|
||||
promoCodes: promo_codes,
|
||||
cartId: id,
|
||||
},
|
||||
})
|
||||
|
||||
return new StepResponse(null)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,75 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
CartDTO,
|
||||
ICartModuleService,
|
||||
IFulfillmentModuleService,
|
||||
} from "@medusajs/types"
|
||||
import { arrayDifference } from "@medusajs/utils"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
cart: CartDTO
|
||||
}
|
||||
|
||||
export const refreshCartShippingMethodsStepId = "refresh-cart-shipping-methods"
|
||||
export const refreshCartShippingMethodsStep = createStep(
|
||||
refreshCartShippingMethodsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { cart } = data
|
||||
const { shipping_methods: shippingMethods = [] } = cart
|
||||
|
||||
if (!shippingMethods?.length) {
|
||||
return new StepResponse(void 0, [])
|
||||
}
|
||||
|
||||
const fulfillmentModule = container.resolve<IFulfillmentModuleService>(
|
||||
ModuleRegistrationName.FULFILLMENT
|
||||
)
|
||||
|
||||
const cartModule = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const shippingOptionIds: string[] = shippingMethods.map(
|
||||
(sm) => sm.shipping_option_id!
|
||||
)
|
||||
|
||||
const validShippingOptions =
|
||||
await fulfillmentModule.listShippingOptionsForContext(
|
||||
{
|
||||
id: shippingOptionIds,
|
||||
context: { ...cart },
|
||||
address: {
|
||||
country_code: cart.shipping_address?.country_code,
|
||||
province_code: cart.shipping_address?.province,
|
||||
city: cart.shipping_address?.city,
|
||||
postal_expression: cart.shipping_address?.postal_code,
|
||||
},
|
||||
},
|
||||
{ relations: ["rules"] }
|
||||
)
|
||||
|
||||
const validShippingOptionIds = validShippingOptions.map((o) => o.id)
|
||||
const invalidShippingOptionIds = arrayDifference(
|
||||
shippingOptionIds,
|
||||
validShippingOptionIds
|
||||
)
|
||||
|
||||
const shippingMethodsToDelete = shippingMethods
|
||||
.filter((sm) => invalidShippingOptionIds.includes(sm.shipping_option_id!))
|
||||
.map((sm) => sm.id)
|
||||
|
||||
await cartModule.softDeleteShippingMethods(shippingMethodsToDelete)
|
||||
|
||||
return new StepResponse(void 0, shippingMethodsToDelete)
|
||||
},
|
||||
async (shippingMethodsToRestore, { container }) => {
|
||||
if (shippingMethodsToRestore?.length) {
|
||||
const cartModule = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
await cartModule.restoreShippingMethods(shippingMethodsToRestore)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,37 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { ICartModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
lineItemAdjustmentIdsToRemove: string[]
|
||||
}
|
||||
|
||||
export const removeLineItemAdjustmentsStepId = "remove-line-item-adjustments"
|
||||
export const removeLineItemAdjustmentsStep = createStep(
|
||||
removeLineItemAdjustmentsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { lineItemAdjustmentIdsToRemove = [] } = data
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
await cartModuleService.softDeleteLineItemAdjustments(
|
||||
lineItemAdjustmentIdsToRemove
|
||||
)
|
||||
|
||||
return new StepResponse(void 0, lineItemAdjustmentIdsToRemove)
|
||||
},
|
||||
async (lineItemAdjustmentIdsToRemove, { container }) => {
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
if (!lineItemAdjustmentIdsToRemove?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await cartModuleService.restoreLineItemAdjustments(
|
||||
lineItemAdjustmentIdsToRemove
|
||||
)
|
||||
}
|
||||
)
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { ICartModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
shippingMethodAdjustmentIdsToRemove: string[]
|
||||
}
|
||||
|
||||
export const removeShippingMethodAdjustmentsStepId =
|
||||
"remove-shipping-method-adjustments"
|
||||
export const removeShippingMethodAdjustmentsStep = createStep(
|
||||
removeShippingMethodAdjustmentsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { shippingMethodAdjustmentIdsToRemove = [] } = data
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
await cartModuleService.softDeleteShippingMethodAdjustments(
|
||||
shippingMethodAdjustmentIdsToRemove
|
||||
)
|
||||
|
||||
return new StepResponse(void 0, shippingMethodAdjustmentIdsToRemove)
|
||||
},
|
||||
async (shippingMethodAdjustmentIdsToRemove, { container }) => {
|
||||
const cartModuleService: ICartModuleService = container.resolve(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
if (!shippingMethodAdjustmentIdsToRemove?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
await cartModuleService.restoreShippingMethodAdjustments(
|
||||
shippingMethodAdjustmentIdsToRemove
|
||||
)
|
||||
}
|
||||
)
|
||||
@@ -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,37 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CartDTO, FindConfig, ICartModuleService } from "@medusajs/types"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
id: string
|
||||
config?: FindConfig<CartDTO>
|
||||
}
|
||||
|
||||
export const retrieveCartStepId = "retrieve-cart"
|
||||
export const retrieveCartStep = createStep(
|
||||
retrieveCartStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const cartModuleService = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const cart = await cartModuleService.retrieve(data.id, data.config)
|
||||
|
||||
// TODO: remove this when cart handles totals calculation
|
||||
cart.items = cart.items?.map((item) => {
|
||||
item.subtotal = item.unit_price
|
||||
|
||||
return item
|
||||
})
|
||||
|
||||
// TODO: remove this when cart handles totals calculation
|
||||
cart.shipping_methods = cart.shipping_methods?.map((shipping_method) => {
|
||||
// TODO: should we align all amounts/prices fields to be unit_price?
|
||||
shipping_method.subtotal = shipping_method.amount
|
||||
|
||||
return shipping_method
|
||||
})
|
||||
|
||||
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,
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import {
|
||||
LinkModuleUtils,
|
||||
ModuleRegistrationName,
|
||||
Modules,
|
||||
} from "@medusajs/modules-sdk"
|
||||
import { IPromotionModuleService } from "@medusajs/types"
|
||||
import { PromotionActions } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
id: string
|
||||
promo_codes?: string[]
|
||||
action?:
|
||||
| PromotionActions.ADD
|
||||
| PromotionActions.REMOVE
|
||||
| PromotionActions.REPLACE
|
||||
}
|
||||
|
||||
export const updateCartPromotionsStepId = "update-cart-promotions"
|
||||
export const updateCartPromotionsStep = createStep(
|
||||
updateCartPromotionsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { promo_codes = [], id, action = PromotionActions.ADD } = data
|
||||
|
||||
const remoteLink = container.resolve(LinkModuleUtils.REMOTE_LINK)
|
||||
const remoteQuery = container.resolve(LinkModuleUtils.REMOTE_QUERY)
|
||||
const promotionService = container.resolve<IPromotionModuleService>(
|
||||
ModuleRegistrationName.PROMOTION
|
||||
)
|
||||
|
||||
const existingCartPromotionLinks = await remoteQuery({
|
||||
cart_promotion: {
|
||||
__args: { cart_id: [id] },
|
||||
fields: ["cart_id", "promotion_id"],
|
||||
},
|
||||
})
|
||||
|
||||
const promotionLinkMap = new Map<string, any>(
|
||||
existingCartPromotionLinks.map((link) => [link.promotion_id, link])
|
||||
)
|
||||
|
||||
const promotions = await promotionService.list(
|
||||
{ code: promo_codes },
|
||||
{ select: ["id"] }
|
||||
)
|
||||
|
||||
const linksToCreate: any[] = []
|
||||
const linksToDismiss: any[] = []
|
||||
|
||||
for (const promotion of promotions) {
|
||||
const linkObject = {
|
||||
[Modules.CART]: { cart_id: id },
|
||||
[Modules.PROMOTION]: { promotion_id: promotion.id },
|
||||
}
|
||||
|
||||
if ([PromotionActions.ADD, PromotionActions.REPLACE].includes(action)) {
|
||||
linksToCreate.push(linkObject)
|
||||
}
|
||||
|
||||
if (action === PromotionActions.REMOVE) {
|
||||
const link = promotionLinkMap.get(promotion.id)
|
||||
|
||||
if (link) {
|
||||
linksToDismiss.push(linkObject)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (action === PromotionActions.REPLACE) {
|
||||
for (const link of existingCartPromotionLinks) {
|
||||
linksToDismiss.push({
|
||||
[Modules.CART]: { cart_id: link.cart_id },
|
||||
[Modules.PROMOTION]: { promotion_id: link.promotion_id },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const linksToDismissPromise = linksToDismiss.length
|
||||
? remoteLink.dismiss(linksToDismiss)
|
||||
: []
|
||||
|
||||
const linksToCreatePromise = linksToCreate.length
|
||||
? remoteLink.create(linksToCreate)
|
||||
: []
|
||||
|
||||
const [_, createdLinks] = await Promise.all([
|
||||
linksToDismissPromise,
|
||||
linksToCreatePromise,
|
||||
])
|
||||
|
||||
return new StepResponse(null, {
|
||||
createdLinkIds: createdLinks.map((link) => link.id),
|
||||
dismissedLinks: linksToDismiss,
|
||||
})
|
||||
},
|
||||
async (revertData, { container }) => {
|
||||
const remoteLink = container.resolve(LinkModuleUtils.REMOTE_LINK)
|
||||
|
||||
if (revertData?.dismissedLinks?.length) {
|
||||
await remoteLink.create(revertData.dismissedLinks)
|
||||
}
|
||||
|
||||
if (revertData?.createdLinkIds?.length) {
|
||||
await remoteLink.delete(revertData.createdLinkIds)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,53 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
ICartModuleService,
|
||||
UpdateCartDTO,
|
||||
UpdateCartWorkflowInputDTO,
|
||||
} from "@medusajs/types"
|
||||
import { getSelectsAndRelationsFromObjectArray } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
export const updateCartsStepId = "update-carts"
|
||||
export const updateCartsStep = createStep(
|
||||
updateCartsStepId,
|
||||
async (data: UpdateCartWorkflowInputDTO[], { container }) => {
|
||||
const cartModule = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray(data)
|
||||
const cartsBeforeUpdate = await cartModule.list(
|
||||
{ id: data.map((d) => d.id) },
|
||||
{ select: selects, relations }
|
||||
)
|
||||
|
||||
const updatedCart = await cartModule.update(data)
|
||||
|
||||
return new StepResponse(updatedCart, cartsBeforeUpdate)
|
||||
},
|
||||
async (cartsBeforeUpdate, { container }) => {
|
||||
if (!cartsBeforeUpdate) {
|
||||
return
|
||||
}
|
||||
|
||||
const cartModule = container.resolve<ICartModuleService>(
|
||||
ModuleRegistrationName.CART
|
||||
)
|
||||
|
||||
const dataToUpdate: UpdateCartDTO[] = []
|
||||
|
||||
for (const cart of cartsBeforeUpdate) {
|
||||
dataToUpdate.push({
|
||||
id: cart.id,
|
||||
region_id: cart.region_id,
|
||||
customer_id: cart.customer_id,
|
||||
sales_channel_id: cart.sales_channel_id,
|
||||
email: cart.email,
|
||||
currency_code: cart.currency_code,
|
||||
metadata: cart.metadata,
|
||||
})
|
||||
}
|
||||
|
||||
return await cartModule.update(dataToUpdate)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,35 @@
|
||||
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[]
|
||||
force_tax_calculation?: boolean
|
||||
}
|
||||
|
||||
export const updateTaxLinesStepId = "update-tax-lines-step"
|
||||
export const updateTaxLinesStep = createStep(
|
||||
updateTaxLinesStepId,
|
||||
async (input: StepInput, { container }) => {
|
||||
const { transaction } = await updateTaxLinesWorkflow(container).run({
|
||||
input,
|
||||
})
|
||||
|
||||
return new StepResponse(null, { transaction })
|
||||
},
|
||||
async (flow, { container }) => {
|
||||
if (!flow) {
|
||||
return
|
||||
}
|
||||
|
||||
await updateTaxLinesWorkflow(container).cancel({
|
||||
transaction: flow.transaction,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,53 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { CartDTO, IFulfillmentModuleService } from "@medusajs/types"
|
||||
import { arrayDifference, MedusaError } from "@medusajs/utils"
|
||||
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
cart: CartDTO
|
||||
option_ids: string[]
|
||||
}
|
||||
|
||||
export const validateCartShippingOptionsStepId =
|
||||
"validate-cart-shipping-options"
|
||||
export const validateCartShippingOptionsStep = createStep(
|
||||
validateCartShippingOptionsStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const { option_ids: optionIds = [], cart } = data
|
||||
|
||||
if (!optionIds.length) {
|
||||
return new StepResponse(void 0)
|
||||
}
|
||||
|
||||
const fulfillmentModule = container.resolve<IFulfillmentModuleService>(
|
||||
ModuleRegistrationName.FULFILLMENT
|
||||
)
|
||||
|
||||
const validShippingOptions =
|
||||
await fulfillmentModule.listShippingOptionsForContext(
|
||||
{
|
||||
id: optionIds,
|
||||
context: { ...cart },
|
||||
address: {
|
||||
country_code: cart.shipping_address?.country_code,
|
||||
province_code: cart.shipping_address?.province,
|
||||
city: cart.shipping_address?.city,
|
||||
postal_expression: cart.shipping_address?.postal_code,
|
||||
},
|
||||
},
|
||||
{ relations: ["rules"] }
|
||||
)
|
||||
|
||||
const validShippingOptionIds = validShippingOptions.map((o) => o.id)
|
||||
const invalidOptionIds = arrayDifference(optionIds, validShippingOptionIds)
|
||||
|
||||
if (invalidOptionIds.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Shipping Options are invalid for cart.`
|
||||
)
|
||||
}
|
||||
|
||||
return new StepResponse(void 0)
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IProductModuleService } from "@medusajs/types"
|
||||
import { MedusaError } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
interface StepInput {
|
||||
variantIds: string[]
|
||||
}
|
||||
|
||||
export const validateVariantsExistStepId = "validate-variants-exist"
|
||||
export const validateVariantsExistStep = createStep(
|
||||
validateVariantsExistStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
const productModuleService = container.resolve<IProductModuleService>(
|
||||
ModuleRegistrationName.PRODUCT
|
||||
)
|
||||
|
||||
const variants = await productModuleService.listVariants(
|
||||
{ id: data.variantIds },
|
||||
{ select: ["id"] }
|
||||
)
|
||||
|
||||
const variantIdToData = new Set(variants.map((v) => v.id))
|
||||
|
||||
const notFoundVariants = new Set(
|
||||
[...data.variantIds].filter((x) => !variantIdToData.has(x))
|
||||
)
|
||||
|
||||
if (notFoundVariants.size) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Variants with IDs ${[...notFoundVariants].join(", ")} do not exist`
|
||||
)
|
||||
}
|
||||
|
||||
return new StepResponse(Array.from(variants.map((v) => v.id)))
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,52 @@
|
||||
export const cartFieldsForRefreshSteps = [
|
||||
"region_id",
|
||||
"currency_code",
|
||||
"region.*",
|
||||
"items.*",
|
||||
"items.tax_lines.*",
|
||||
"shipping_address.*",
|
||||
"shipping_methods.*",
|
||||
"shipping_methods.tax_lines*",
|
||||
"customer.*",
|
||||
"customer.groups.*",
|
||||
]
|
||||
|
||||
export const completeCartFields = [
|
||||
"id",
|
||||
"currency_code",
|
||||
"email",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"total",
|
||||
"subtotal",
|
||||
"tax_total",
|
||||
"discount_total",
|
||||
"discount_tax_total",
|
||||
"original_total",
|
||||
"original_tax_total",
|
||||
"item_total",
|
||||
"item_subtotal",
|
||||
"item_tax_total",
|
||||
"sales_channel_id",
|
||||
"original_item_total",
|
||||
"original_item_subtotal",
|
||||
"original_item_tax_total",
|
||||
"shipping_total",
|
||||
"shipping_subtotal",
|
||||
"shipping_tax_total",
|
||||
"original_shipping_tax_total",
|
||||
"original_shipping_tax_subtotal",
|
||||
"original_shipping_total",
|
||||
"items.*",
|
||||
"items.tax_lines.*",
|
||||
"items.adjustments.*",
|
||||
"customer.*",
|
||||
"shipping_methods.*",
|
||||
"shipping_methods.tax_lines.*",
|
||||
"shipping_methods.adjustments.*",
|
||||
"shipping_address.*",
|
||||
"billing_address.*",
|
||||
"region.*",
|
||||
"payment_collection.*",
|
||||
"payment_collection.payment_sessions.*",
|
||||
]
|
||||
@@ -0,0 +1,66 @@
|
||||
import { BigNumberInput } from "@medusajs/types"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
|
||||
interface ConfirmInventoryPreparationInput {
|
||||
product_variant_inventory_items: {
|
||||
variant_id: string
|
||||
inventory_item_id: string
|
||||
required_quantity: number
|
||||
}[]
|
||||
items: { variant_id?: string; quantity: BigNumberInput }[]
|
||||
variants: { id: string; manage_inventory?: boolean }[]
|
||||
location_ids: string[]
|
||||
}
|
||||
|
||||
interface ConfirmInventoryItem {
|
||||
inventory_item_id: string
|
||||
required_quantity: number
|
||||
quantity: number
|
||||
location_ids: string[]
|
||||
}
|
||||
|
||||
export const prepareConfirmInventoryInput = ({
|
||||
product_variant_inventory_items,
|
||||
location_ids,
|
||||
items,
|
||||
variants,
|
||||
}: ConfirmInventoryPreparationInput) => {
|
||||
if (!product_variant_inventory_items.length) {
|
||||
return []
|
||||
}
|
||||
|
||||
const variantsMap = new Map<
|
||||
string,
|
||||
{ id: string; manage_inventory?: boolean }
|
||||
>(variants.map((v) => [v.id, v]))
|
||||
|
||||
const itemsToConfirm: ConfirmInventoryItem[] = []
|
||||
|
||||
items.forEach((item) => {
|
||||
const variant = variantsMap.get(item.variant_id!)
|
||||
|
||||
if (!variant?.manage_inventory) {
|
||||
return
|
||||
}
|
||||
|
||||
const variantInventoryItem = product_variant_inventory_items.find(
|
||||
(i) => i.variant_id === item.variant_id
|
||||
)
|
||||
|
||||
if (!variantInventoryItem) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Variant ${item.variant_id} does not have any inventory items associated with it.`
|
||||
)
|
||||
}
|
||||
|
||||
itemsToConfirm.push({
|
||||
inventory_item_id: variantInventoryItem.inventory_item_id,
|
||||
required_quantity: variantInventoryItem.required_quantity,
|
||||
quantity: item.quantity as number, // TODO: update type to BigNumberInput
|
||||
location_ids: location_ids,
|
||||
})
|
||||
})
|
||||
|
||||
return itemsToConfirm
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import {
|
||||
BigNumberInput,
|
||||
CreateOrderAdjustmentDTO,
|
||||
CreateOrderLineItemTaxLineDTO,
|
||||
ProductVariantDTO,
|
||||
} from "@medusajs/types"
|
||||
|
||||
interface Input {
|
||||
quantity: BigNumberInput
|
||||
metadata?: Record<string, any>
|
||||
unitPrice: BigNumberInput
|
||||
variant: ProductVariantDTO
|
||||
taxLines?: CreateOrderLineItemTaxLineDTO[]
|
||||
adjustments?: CreateOrderAdjustmentDTO[]
|
||||
cartId?: string
|
||||
}
|
||||
|
||||
export function prepareLineItemData(data: Input) {
|
||||
const {
|
||||
variant,
|
||||
unitPrice,
|
||||
quantity,
|
||||
metadata,
|
||||
cartId,
|
||||
taxLines,
|
||||
adjustments,
|
||||
} = data
|
||||
|
||||
if (!variant.product) {
|
||||
throw new Error("Variant does not have a product")
|
||||
}
|
||||
|
||||
const lineItem: any = {
|
||||
quantity,
|
||||
title: variant.title,
|
||||
|
||||
subtitle: variant.product.title,
|
||||
thumbnail: variant.product.thumbnail,
|
||||
|
||||
product_id: variant.product.id,
|
||||
product_title: variant.product.title,
|
||||
product_description: variant.product.description,
|
||||
product_subtitle: variant.product.subtitle,
|
||||
product_type: variant.product.type?.[0].value ?? null,
|
||||
product_collection: variant.product.collection?.[0]?.value ?? null,
|
||||
product_handle: variant.product.handle,
|
||||
|
||||
variant_id: variant.id,
|
||||
variant_sku: variant.sku,
|
||||
variant_barcode: variant.barcode,
|
||||
variant_title: variant.title,
|
||||
|
||||
unit_price: unitPrice,
|
||||
metadata,
|
||||
}
|
||||
|
||||
if (taxLines) {
|
||||
lineItem.tax_lines = prepareTaxLinesData(taxLines)
|
||||
}
|
||||
|
||||
if (adjustments) {
|
||||
lineItem.adjustments = prepareAdjustmentsData(adjustments)
|
||||
}
|
||||
|
||||
if (cartId) {
|
||||
lineItem.cart_id = cartId
|
||||
}
|
||||
|
||||
return lineItem
|
||||
}
|
||||
|
||||
export function prepareTaxLinesData(data: CreateOrderLineItemTaxLineDTO[]) {
|
||||
return data.map((d) => ({
|
||||
description: d.description,
|
||||
tax_rate_id: d.tax_rate_id,
|
||||
code: d.code,
|
||||
rate: d.rate,
|
||||
provider_id: d.provider_id,
|
||||
}))
|
||||
}
|
||||
|
||||
export function prepareAdjustmentsData(data: CreateOrderAdjustmentDTO[]) {
|
||||
return data.map((d) => ({
|
||||
code: d.code,
|
||||
amount: d.amount,
|
||||
description: d.description,
|
||||
promotion_id: d.promotion_id,
|
||||
provider_id: d.promotion_id,
|
||||
}))
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
||||
import {
|
||||
addShippingMethodToCartStep,
|
||||
validateCartShippingOptionsStep,
|
||||
} from "../steps"
|
||||
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
||||
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||
import { cartFieldsForRefreshSteps } from "../utils/fields"
|
||||
|
||||
interface AddShippingMethodToCartWorkflowInput {
|
||||
cart_id: string
|
||||
options: {
|
||||
id: string
|
||||
data?: Record<string, unknown>
|
||||
}[]
|
||||
}
|
||||
|
||||
export const addShippingMethodToCartWorkflowId = "add-shipping-method-to-cart"
|
||||
export const addShippingMethodToWorkflow = createWorkflow(
|
||||
addShippingMethodToCartWorkflowId,
|
||||
(
|
||||
input: WorkflowData<AddShippingMethodToCartWorkflowInput>
|
||||
): WorkflowData<void> => {
|
||||
const cart = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: cartFieldsForRefreshSteps,
|
||||
variables: { id: input.cart_id },
|
||||
list: false,
|
||||
})
|
||||
|
||||
const optionIds = transform({ input }, (data) => {
|
||||
return (data.input.options ?? []).map((i) => i.id)
|
||||
})
|
||||
|
||||
validateCartShippingOptionsStep({
|
||||
option_ids: optionIds,
|
||||
cart,
|
||||
})
|
||||
|
||||
const shippingOptions = useRemoteQueryStep({
|
||||
entry_point: "shipping_option",
|
||||
fields: ["id", "name", "calculated_price.calculated_amount"],
|
||||
variables: {
|
||||
id: optionIds,
|
||||
calculated_price: {
|
||||
context: { currency_code: cart.currency_code },
|
||||
},
|
||||
},
|
||||
}).config({ name: "fetch-shipping-option" })
|
||||
|
||||
const shippingMethodInput = transform(
|
||||
{ input, shippingOptions },
|
||||
(data) => {
|
||||
const options = (data.input.options ?? []).map((option) => {
|
||||
const shippingOption = data.shippingOptions.find(
|
||||
(so) => so.id === option.id
|
||||
)!
|
||||
|
||||
return {
|
||||
shipping_option_id: shippingOption.id,
|
||||
amount: shippingOption.calculated_price.calculated_amount,
|
||||
data: option.data ?? {},
|
||||
name: shippingOption.name,
|
||||
cart_id: data.input.cart_id,
|
||||
}
|
||||
})
|
||||
|
||||
return options
|
||||
}
|
||||
)
|
||||
|
||||
const shippingMethods = addShippingMethodToCartStep({
|
||||
shipping_methods: shippingMethodInput,
|
||||
})
|
||||
refreshCartPromotionsStep({ id: input.cart_id })
|
||||
updateTaxLinesStep({
|
||||
cart_or_cart_id: input.cart_id,
|
||||
shipping_methods: shippingMethods,
|
||||
})
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,180 @@
|
||||
import {
|
||||
AddToCartWorkflowInputDTO,
|
||||
CreateLineItemForCartDTO,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
||||
import {
|
||||
addToCartStep,
|
||||
confirmInventoryStep,
|
||||
refreshCartShippingMethodsStep,
|
||||
} from "../steps"
|
||||
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
||||
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||
import { cartFieldsForRefreshSteps } from "../utils/fields"
|
||||
import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input"
|
||||
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
||||
import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection"
|
||||
|
||||
// TODO: The AddToCartWorkflow are missing the following steps:
|
||||
// - Refresh/delete shipping methods (fulfillment module)
|
||||
|
||||
export const addToCartWorkflowId = "add-to-cart"
|
||||
export const addToCartWorkflow = createWorkflow(
|
||||
addToCartWorkflowId,
|
||||
(input: WorkflowData<AddToCartWorkflowInputDTO>) => {
|
||||
const variantIds = transform({ input }, (data) => {
|
||||
return (data.input.items ?? []).map((i) => i.variant_id)
|
||||
})
|
||||
|
||||
// TODO: This is on par with the context used in v1.*, but we can be more flexible.
|
||||
const pricingContext = transform({ cart: input.cart }, (data) => {
|
||||
return {
|
||||
currency_code: data.cart.currency_code,
|
||||
region_id: data.cart.region_id,
|
||||
customer_id: data.cart.customer_id,
|
||||
}
|
||||
})
|
||||
|
||||
const variants = useRemoteQueryStep({
|
||||
entry_point: "variants",
|
||||
fields: [
|
||||
"id",
|
||||
"title",
|
||||
"sku",
|
||||
"barcode",
|
||||
"manage_inventory",
|
||||
"product.id",
|
||||
"product.title",
|
||||
"product.description",
|
||||
"product.subtitle",
|
||||
"product.thumbnail",
|
||||
"product.type",
|
||||
"product.collection",
|
||||
"product.handle",
|
||||
|
||||
"calculated_price.calculated_amount",
|
||||
|
||||
"inventory_items.inventory_item_id",
|
||||
"inventory_items.required_quantity",
|
||||
|
||||
"inventory_items.inventory.location_levels.stock_locations.id",
|
||||
"inventory_items.inventory.location_levels.stock_locations.name",
|
||||
|
||||
"inventory_items.inventory.location_levels.stock_locations.sales_channels.id",
|
||||
"inventory_items.inventory.location_levels.stock_locations.sales_channels.name",
|
||||
],
|
||||
variables: {
|
||||
id: variantIds,
|
||||
calculated_price: {
|
||||
context: pricingContext,
|
||||
},
|
||||
},
|
||||
throw_if_key_not_found: true,
|
||||
})
|
||||
|
||||
const confirmInventoryInput = transform({ input, variants }, (data) => {
|
||||
const managedVariants = data.variants.filter((v) => v.manage_inventory)
|
||||
if (!managedVariants.length) {
|
||||
return { items: [] }
|
||||
}
|
||||
|
||||
const productVariantInventoryItems: any[] = []
|
||||
|
||||
const stockLocations = data.variants
|
||||
.map((v) => v.inventory_items)
|
||||
.flat()
|
||||
.map((ii) => {
|
||||
productVariantInventoryItems.push({
|
||||
variant_id: ii.variant_id,
|
||||
inventory_item_id: ii.inventory_item_id,
|
||||
required_quantity: ii.required_quantity,
|
||||
})
|
||||
|
||||
return ii.inventory.location_levels
|
||||
})
|
||||
.flat()
|
||||
.map((ll) => ll.stock_locations)
|
||||
.flat()
|
||||
|
||||
const salesChannelId = data.input.cart.sales_channel_id
|
||||
if (salesChannelId) {
|
||||
const salesChannels = stockLocations
|
||||
.map((sl) => sl.sales_channels)
|
||||
.flat()
|
||||
.filter((sc) => sc.id === salesChannelId)
|
||||
|
||||
if (!salesChannels.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Sales channel ${salesChannelId} is not associated with any stock locations.`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const priceNotFound: string[] = data.variants
|
||||
.filter((v) => !v.calculated_price)
|
||||
.map((v) => v.id)
|
||||
|
||||
if (priceNotFound.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Variants with IDs ${priceNotFound.join(", ")} do not have a price`
|
||||
)
|
||||
}
|
||||
|
||||
const items = prepareConfirmInventoryInput({
|
||||
product_variant_inventory_items: productVariantInventoryItems,
|
||||
location_ids: stockLocations.map((l) => l.id),
|
||||
items: data.input.items!,
|
||||
variants: data.variants.map((v) => ({
|
||||
id: v.id,
|
||||
manage_inventory: v.manage_inventory,
|
||||
})),
|
||||
})
|
||||
|
||||
return { items }
|
||||
})
|
||||
|
||||
confirmInventoryStep(confirmInventoryInput)
|
||||
|
||||
const lineItems = transform({ 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: variant.calculated_price.calculated_amount,
|
||||
quantity: item.quantity,
|
||||
metadata: item?.metadata ?? {},
|
||||
cartId: data.input.cart.id,
|
||||
}) as CreateLineItemForCartDTO
|
||||
})
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
const items = addToCartStep({ items: lineItems })
|
||||
|
||||
const cart = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: cartFieldsForRefreshSteps,
|
||||
variables: { id: input.cart.id },
|
||||
list: false,
|
||||
}).config({ name: "refetch–cart" })
|
||||
|
||||
refreshCartShippingMethodsStep({ cart })
|
||||
// TODO: since refreshCartShippingMethodsStep potentially removes cart shipping methods, we need the updated cart here
|
||||
// for the following 2 steps as they act upon final cart shape
|
||||
updateTaxLinesStep({ cart_or_cart_id: cart, items })
|
||||
refreshCartPromotionsStep({ id: input.cart.id })
|
||||
refreshPaymentCollectionForCartStep({ cart_id: input.cart.id })
|
||||
|
||||
return items
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,43 @@
|
||||
import { CompleteCartWorkflowInputDTO, OrderDTO } from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { useRemoteQueryStep } from "../../../common"
|
||||
import { createOrderFromCartStep } from "../steps"
|
||||
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||
import { completeCartFields } from "../utils/fields"
|
||||
|
||||
/*
|
||||
- [] Create Tax Lines
|
||||
- [] Authorize Payment
|
||||
- fail:
|
||||
- [] Delete Tax lines
|
||||
- [] Reserve Item from inventory (if enabled)
|
||||
- fail:
|
||||
- [] Delete reservations
|
||||
- [] Cancel Payment
|
||||
- [] Create order
|
||||
*/
|
||||
export const completeCartWorkflowId = "complete-cart"
|
||||
export const completeCartWorkflow = createWorkflow(
|
||||
completeCartWorkflowId,
|
||||
(
|
||||
input: WorkflowData<CompleteCartWorkflowInputDTO>
|
||||
): WorkflowData<OrderDTO> => {
|
||||
const cart = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: completeCartFields,
|
||||
variables: { id: input.id },
|
||||
list: false,
|
||||
})
|
||||
|
||||
updateTaxLinesStep({ cart_or_cart_id: cart, force_tax_calculation: true })
|
||||
|
||||
const finalCart = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: completeCartFields,
|
||||
variables: { id: input.id },
|
||||
list: false,
|
||||
}).config({ name: "final-cart" })
|
||||
|
||||
return createOrderFromCartStep({ cart: finalCart })
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,228 @@
|
||||
import { CartDTO, CreateCartWorkflowInputDTO } from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
parallelize,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { MedusaError } from "medusa-core-utils"
|
||||
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
||||
import {
|
||||
confirmInventoryStep,
|
||||
createCartsStep,
|
||||
findOneOrAnyRegionStep,
|
||||
findOrCreateCustomerStep,
|
||||
findSalesChannelStep,
|
||||
getVariantPriceSetsStep,
|
||||
} from "../steps"
|
||||
import { refreshCartPromotionsStep } from "../steps/refresh-cart-promotions"
|
||||
import { updateTaxLinesStep } from "../steps/update-tax-lines"
|
||||
import { prepareConfirmInventoryInput } from "../utils/prepare-confirm-inventory-input"
|
||||
import { prepareLineItemData } from "../utils/prepare-line-item-data"
|
||||
import { refreshPaymentCollectionForCartStep } from "./refresh-payment-collection"
|
||||
|
||||
// TODO: The createCartWorkflow are missing the following steps:
|
||||
// - Refresh/delete shipping methods (fulfillment module)
|
||||
|
||||
export const createCartWorkflowId = "create-cart"
|
||||
export const createCartWorkflow = createWorkflow(
|
||||
createCartWorkflowId,
|
||||
(input: WorkflowData<CreateCartWorkflowInputDTO>): WorkflowData<CartDTO> => {
|
||||
const variantIds = transform({ input }, (data) => {
|
||||
return (data.input.items ?? []).map((i) => i.variant_id)
|
||||
})
|
||||
|
||||
const [salesChannel, region, customerData] = parallelize(
|
||||
findSalesChannelStep({
|
||||
salesChannelId: input.sales_channel_id,
|
||||
}),
|
||||
findOneOrAnyRegionStep({
|
||||
regionId: input.region_id,
|
||||
}),
|
||||
findOrCreateCustomerStep({
|
||||
customerId: input.customer_id,
|
||||
email: input.email,
|
||||
})
|
||||
)
|
||||
|
||||
// TODO: This is on par with the context used in v1.*, but we can be more flexible.
|
||||
const pricingContext = transform(
|
||||
{ input, region, customerData },
|
||||
(data) => {
|
||||
return {
|
||||
currency_code: data.input.currency_code ?? data.region.currency_code,
|
||||
region_id: data.region.id,
|
||||
customer_id: data.customerData.customer?.id,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const variants = useRemoteQueryStep({
|
||||
entry_point: "variants",
|
||||
fields: [
|
||||
"id",
|
||||
"title",
|
||||
"sku",
|
||||
"manage_inventory",
|
||||
"barcode",
|
||||
"product.id",
|
||||
"product.title",
|
||||
"product.description",
|
||||
"product.subtitle",
|
||||
"product.thumbnail",
|
||||
"product.type",
|
||||
"product.collection",
|
||||
"product.handle",
|
||||
|
||||
"calculated_price.calculated_amount",
|
||||
|
||||
"inventory_items.inventory_item_id",
|
||||
"inventory_items.required_quantity",
|
||||
|
||||
"inventory_items.inventory.location_levels.stock_locations.id",
|
||||
"inventory_items.inventory.location_levels.stock_locations.name",
|
||||
|
||||
"inventory_items.inventory.location_levels.stock_locations.sales_channels.id",
|
||||
"inventory_items.inventory.location_levels.stock_locations.sales_channels.name",
|
||||
],
|
||||
variables: {
|
||||
id: variantIds,
|
||||
calculated_price: {
|
||||
context: pricingContext,
|
||||
},
|
||||
},
|
||||
throw_if_key_not_found: true,
|
||||
})
|
||||
|
||||
const confirmInventoryInput = transform(
|
||||
{ input, salesChannel, variants },
|
||||
(data) => {
|
||||
const managedVariants = data.variants.filter((v) => v.manage_inventory)
|
||||
if (!managedVariants.length) {
|
||||
return { items: [] }
|
||||
}
|
||||
|
||||
const productVariantInventoryItems: any[] = []
|
||||
|
||||
const stockLocations = managedVariants
|
||||
.map((v) => v.inventory_items)
|
||||
.flat()
|
||||
.map((ii) => {
|
||||
productVariantInventoryItems.push({
|
||||
variant_id: ii.variant_id,
|
||||
inventory_item_id: ii.inventory_item_id,
|
||||
required_quantity: ii.required_quantity,
|
||||
})
|
||||
|
||||
return ii.inventory.location_levels
|
||||
})
|
||||
.flat()
|
||||
.map((ll) => ll.stock_locations)
|
||||
.flat()
|
||||
|
||||
const salesChannelId = data.salesChannel?.id
|
||||
if (salesChannelId) {
|
||||
const salesChannels = stockLocations
|
||||
.map((sl) => sl.sales_channels)
|
||||
.flat()
|
||||
.filter((sc) => sc.id === salesChannelId)
|
||||
|
||||
if (!salesChannels.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Sales channel ${salesChannelId} is not associated with any stock locations.`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const priceNotFound: string[] = data.variants
|
||||
.filter((v) => !v.calculated_price)
|
||||
.map((v) => v.id)
|
||||
|
||||
if (priceNotFound.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Variants with IDs ${priceNotFound.join(", ")} do not have a price`
|
||||
)
|
||||
}
|
||||
|
||||
const items = prepareConfirmInventoryInput({
|
||||
product_variant_inventory_items: productVariantInventoryItems,
|
||||
location_ids: stockLocations.map((l) => l.id),
|
||||
items: data.input.items!,
|
||||
variants: data.variants.map((v) => ({
|
||||
id: v.id,
|
||||
manage_inventory: v.manage_inventory,
|
||||
})),
|
||||
})
|
||||
|
||||
return { items }
|
||||
}
|
||||
)
|
||||
|
||||
confirmInventoryStep(confirmInventoryInput)
|
||||
|
||||
const priceSets = getVariantPriceSetsStep({
|
||||
variantIds,
|
||||
context: pricingContext,
|
||||
})
|
||||
|
||||
const cartInput = transform(
|
||||
{ input, region, customerData, salesChannel },
|
||||
(data) => {
|
||||
const data_ = {
|
||||
...data.input,
|
||||
currency_code: data.input.currency_code ?? data.region.currency_code,
|
||||
region_id: data.region.id,
|
||||
}
|
||||
|
||||
if (data.customerData.customer?.id) {
|
||||
data_.customer_id = data.customerData.customer.id
|
||||
data_.email = data.input?.email ?? data.customerData.customer.email
|
||||
}
|
||||
|
||||
if (data.salesChannel?.id) {
|
||||
data_.sales_channel_id = data.salesChannel.id
|
||||
}
|
||||
|
||||
return data_
|
||||
}
|
||||
)
|
||||
|
||||
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 ?? {},
|
||||
})
|
||||
})
|
||||
|
||||
return items
|
||||
})
|
||||
|
||||
const cartToCreate = transform({ lineItems, cartInput }, (data) => {
|
||||
return {
|
||||
...data.cartInput,
|
||||
items: data.lineItems,
|
||||
}
|
||||
})
|
||||
|
||||
const carts = createCartsStep([cartToCreate])
|
||||
const cart = transform({ carts }, (data) => data.carts?.[0])
|
||||
|
||||
refreshCartPromotionsStep({
|
||||
id: cart.id,
|
||||
promo_codes: input.promo_codes,
|
||||
})
|
||||
updateTaxLinesStep({ cart_or_cart_id: cart.id })
|
||||
refreshPaymentCollectionForCartStep({
|
||||
cart_id: cart.id,
|
||||
})
|
||||
|
||||
return cart
|
||||
}
|
||||
)
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
import {
|
||||
CartDTO,
|
||||
CreatePaymentCollectionForCartWorkflowInputDTO,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
WorkflowData,
|
||||
createWorkflow,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { retrieveCartStep } from "../steps"
|
||||
import { createPaymentCollectionsStep } from "../steps/create-payment-collection"
|
||||
import { linkCartAndPaymentCollectionsStep } from "../steps/link-cart-payment-collection"
|
||||
|
||||
export const createPaymentCollectionForCartWorkflowId =
|
||||
"create-payment-collection-for-cart"
|
||||
export const createPaymentCollectionForCartWorkflow = createWorkflow(
|
||||
createPaymentCollectionForCartWorkflowId,
|
||||
(
|
||||
input: WorkflowData<CreatePaymentCollectionForCartWorkflowInputDTO>
|
||||
): WorkflowData<CartDTO> => {
|
||||
const created = createPaymentCollectionsStep([input])
|
||||
|
||||
const link = transform({ cartId: input.cart_id, created }, (data) => ({
|
||||
links: [
|
||||
{
|
||||
cart_id: data.cartId,
|
||||
payment_collection_id: data.created[0].id,
|
||||
},
|
||||
],
|
||||
}))
|
||||
|
||||
linkCartAndPaymentCollectionsStep(link)
|
||||
|
||||
const cart = retrieveCartStep({ id: input.cart_id })
|
||||
|
||||
return cart
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,11 @@
|
||||
export * from "./add-shipping-method-to-cart"
|
||||
export * from "./add-to-cart"
|
||||
export * from "./complete-cart"
|
||||
export * from "./create-carts"
|
||||
export * from "./create-payment-collection-for-cart"
|
||||
export * from "./list-shipping-options-for-cart"
|
||||
export * from "./refresh-payment-collection"
|
||||
export * from "./update-cart"
|
||||
export * from "./update-cart-promotions"
|
||||
export * from "./update-line-item-in-cart"
|
||||
export * from "./update-tax-lines"
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
import { ListShippingOptionsForCartWorkflowInputDTO } from "@medusajs/types"
|
||||
import { deepFlatMap, MedusaError } from "@medusajs/utils"
|
||||
import {
|
||||
createWorkflow,
|
||||
transform,
|
||||
WorkflowData,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
||||
|
||||
export const listShippingOptionsForCartWorkflowId =
|
||||
"list-shipping-options-for-cart"
|
||||
export const listShippingOptionsForCartWorkflow = createWorkflow(
|
||||
listShippingOptionsForCartWorkflowId,
|
||||
(input: WorkflowData<ListShippingOptionsForCartWorkflowInputDTO>) => {
|
||||
const scLocationFulfillmentSets = useRemoteQueryStep({
|
||||
entry_point: "sales_channels",
|
||||
fields: [
|
||||
"stock_locations.fulfillment_sets.id",
|
||||
"stock_locations.fulfillment_sets.name",
|
||||
"stock_locations.fulfillment_sets.price_type",
|
||||
"stock_locations.fulfillment_sets.service_zone_id",
|
||||
"stock_locations.fulfillment_sets.shipping_profile_id",
|
||||
"stock_locations.fulfillment_sets.provider_id",
|
||||
"stock_locations.fulfillment_sets.data",
|
||||
"stock_locations.fulfillment_sets.amount",
|
||||
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.name",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.price_type",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.service_zone_id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.shipping_profile_id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.provider_id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.data",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.amount",
|
||||
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.type.id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.type.label",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.type.description",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.type.code",
|
||||
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.provider.id",
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.provider.is_enabled",
|
||||
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.calculated_price.calculated_amount",
|
||||
],
|
||||
variables: {
|
||||
id: input.sales_channel_id,
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options": {
|
||||
context: {
|
||||
address: {
|
||||
city: input.shipping_address?.city,
|
||||
country_code: input.shipping_address?.country_code,
|
||||
province_code: input.shipping_address?.province,
|
||||
},
|
||||
},
|
||||
},
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.calculated_price":
|
||||
{
|
||||
context: {
|
||||
currency_code: input.currency_code,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
const shippingOptionsWithPrice = transform(
|
||||
{ options: scLocationFulfillmentSets },
|
||||
(data) => {
|
||||
const optionsMissingPrices: string[] = []
|
||||
|
||||
const options = deepFlatMap(
|
||||
data.options,
|
||||
"stock_locations.fulfillment_sets.service_zones.shipping_options.calculated_price",
|
||||
({ shipping_options }) => {
|
||||
const { calculated_price, ...options } = shipping_options ?? {}
|
||||
|
||||
if (!calculated_price) {
|
||||
optionsMissingPrices.push(options.id)
|
||||
}
|
||||
|
||||
return {
|
||||
...options,
|
||||
amount: calculated_price?.calculated_amount,
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (optionsMissingPrices.length) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.INVALID_DATA,
|
||||
`Shipping options with IDs ${optionsMissingPrices.join(
|
||||
", "
|
||||
)} do not have a price`
|
||||
)
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
)
|
||||
|
||||
return shippingOptionsWithPrice
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,70 @@
|
||||
import {
|
||||
StepResponse,
|
||||
WorkflowData,
|
||||
createStep,
|
||||
createWorkflow,
|
||||
transform,
|
||||
} from "@medusajs/workflows-sdk"
|
||||
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
||||
import {
|
||||
deletePaymentSessionStep,
|
||||
updatePaymentCollectionStep,
|
||||
} from "../../payment-collection"
|
||||
|
||||
type WorklowInput = {
|
||||
cart_id: string
|
||||
}
|
||||
|
||||
interface StepInput {
|
||||
cart_id: string
|
||||
}
|
||||
|
||||
// We export a step running the workflow too, so that we can use it as a subworkflow e.g. in the update cart workflows
|
||||
export const refreshPaymentCollectionForCartStepId =
|
||||
"refresh-payment-collection-for-cart"
|
||||
export const refreshPaymentCollectionForCartStep = createStep(
|
||||
refreshPaymentCollectionForCartStepId,
|
||||
async (data: StepInput, { container }) => {
|
||||
await refreshPaymentCollectionForCartWorkflow(container).run({
|
||||
input: {
|
||||
cart_id: data.cart_id,
|
||||
},
|
||||
})
|
||||
|
||||
return new StepResponse(null)
|
||||
}
|
||||
)
|
||||
|
||||
export const refreshPaymentCollectionForCartWorkflowId =
|
||||
"refresh-payment-collection-for-cart"
|
||||
export const refreshPaymentCollectionForCartWorkflow = createWorkflow(
|
||||
refreshPaymentCollectionForCartWorkflowId,
|
||||
(input: WorkflowData<WorklowInput>): WorkflowData<void> => {
|
||||
const carts = useRemoteQueryStep({
|
||||
entry_point: "cart",
|
||||
fields: [
|
||||
"id",
|
||||
"total",
|
||||
"currency_code",
|
||||
"payment_collection.id",
|
||||
"payment_collection.payment_sessions.id",
|
||||
],
|
||||
variables: { id: input.cart_id },
|
||||
throw_if_key_not_found: true,
|
||||
})
|
||||
|
||||
const cart = transform({ carts }, (data) => data.carts[0])
|
||||
|
||||
deletePaymentSessionStep({
|
||||
payment_session_id: cart.payment_collection.payment_sessions?.[0].id,
|
||||
})
|
||||
|
||||
updatePaymentCollectionStep({
|
||||
selector: { id: cart.payment_collection.id },
|
||||
update: {
|
||||
amount: cart.total,
|
||||
currency_code: cart.currency_code,
|
||||
},
|
||||
})
|
||||
}
|
||||
)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user