feat: Update service zone (#6990)
This commit is contained in:
@@ -26,7 +26,7 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
|
||||
describe("POST /admin/fulfillment-sets/:id/service-zones", () => {
|
||||
it("should create a service zone for a fulfillment set", async () => {
|
||||
it("should create, update, and delete a service zone for a fulfillment set", async () => {
|
||||
const stockLocationResponse = await api.post(
|
||||
`/admin/stock-locations`,
|
||||
{
|
||||
@@ -120,6 +120,77 @@ medusaIntegrationTestRunner({
|
||||
]),
|
||||
})
|
||||
)
|
||||
|
||||
const serviceZoneId = fset.service_zones[0].id
|
||||
|
||||
const countryGeoZone = fset.service_zones[0].geo_zones.find(
|
||||
(z) => z.type === "country"
|
||||
)
|
||||
|
||||
// Updates an existing and creates a new one
|
||||
const updateResponse = await api.post(
|
||||
`/admin/fulfillment-sets/${fulfillmentSetId}/service-zones/${serviceZoneId}`,
|
||||
{
|
||||
name: "Test Zone Updated",
|
||||
geo_zones: [
|
||||
{
|
||||
id: countryGeoZone.id,
|
||||
country_code: "us",
|
||||
type: "country",
|
||||
},
|
||||
{
|
||||
country_code: "ca",
|
||||
type: "country",
|
||||
},
|
||||
],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const updatedFset = updateResponse.data.fulfillment_set
|
||||
|
||||
expect(updateResponse.status).toEqual(200)
|
||||
expect(updatedFset).toEqual(
|
||||
expect.objectContaining({
|
||||
name: "Fulfillment Set",
|
||||
type: "shipping",
|
||||
service_zones: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: serviceZoneId,
|
||||
name: "Test Zone Updated",
|
||||
fulfillment_set_id: updatedFset.id,
|
||||
geo_zones: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: countryGeoZone.id,
|
||||
country_code: "us",
|
||||
type: "country",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
country_code: "ca",
|
||||
type: "country",
|
||||
}),
|
||||
]),
|
||||
}),
|
||||
]),
|
||||
})
|
||||
)
|
||||
|
||||
const deleteResponse = await api.delete(
|
||||
`/admin/fulfillment-sets/${fulfillmentSetId}/service-zones/${serviceZoneId}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(deleteResponse.status).toEqual(200)
|
||||
expect(deleteResponse.data).toEqual(
|
||||
expect.objectContaining({
|
||||
id: serviceZoneId,
|
||||
object: "service-zone",
|
||||
deleted: true,
|
||||
parent: expect.objectContaining({
|
||||
id: fulfillmentSetId,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw if invalid type is passed", async () => {
|
||||
@@ -251,60 +322,6 @@ medusaIntegrationTestRunner({
|
||||
})
|
||||
|
||||
describe("POST /admin/fulfillment-sets/:id/service-zones", () => {
|
||||
it("should delete a service zone for a fulfillment set", async () => {
|
||||
const stockLocationResponse = await api.post(
|
||||
`/admin/stock-locations`,
|
||||
{
|
||||
name: "test location",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const stockLocationId = stockLocationResponse.data.stock_location.id
|
||||
|
||||
const locationWithFSetResponse = await api.post(
|
||||
`/admin/stock-locations/${stockLocationId}/fulfillment-sets?fields=id,*fulfillment_sets`,
|
||||
{
|
||||
name: "Fulfillment Set",
|
||||
type: "shipping",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const fulfillmentSetId =
|
||||
locationWithFSetResponse.data.stock_location.fulfillment_sets[0].id
|
||||
|
||||
const response = await api.post(
|
||||
`/admin/fulfillment-sets/${fulfillmentSetId}/service-zones`,
|
||||
{
|
||||
name: "Test Zone",
|
||||
geo_zones: [],
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const fset = response.data.fulfillment_set
|
||||
|
||||
const serviceZoneId = fset.service_zones[0].id
|
||||
|
||||
const deleteResponse = await api.delete(
|
||||
`/admin/fulfillment-sets/${fulfillmentSetId}/service-zones/${serviceZoneId}`,
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
expect(deleteResponse.status).toEqual(200)
|
||||
expect(deleteResponse.data).toEqual(
|
||||
expect.objectContaining({
|
||||
id: serviceZoneId,
|
||||
object: "service-zone",
|
||||
deleted: true,
|
||||
parent: expect.objectContaining({
|
||||
id: fulfillmentSetId,
|
||||
}),
|
||||
})
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw when fulfillment set doesn't exist", async () => {
|
||||
const deleteResponse = await api
|
||||
.delete(
|
||||
@@ -314,7 +331,45 @@ medusaIntegrationTestRunner({
|
||||
.catch((e) => e.response)
|
||||
|
||||
expect(deleteResponse.status).toEqual(404)
|
||||
expect(deleteResponse.data.message).toEqual("FulfillmentSet with id: foo was not found")
|
||||
expect(deleteResponse.data.message).toEqual(
|
||||
"FulfillmentSet with id: foo was not found"
|
||||
)
|
||||
})
|
||||
|
||||
it("should throw when fulfillment set doesn't have service zone", async () => {
|
||||
const stockLocationResponse = await api.post(
|
||||
`/admin/stock-locations`,
|
||||
{
|
||||
name: "test location",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const stockLocationId = stockLocationResponse.data.stock_location.id
|
||||
|
||||
const locationWithFSetResponse = await api.post(
|
||||
`/admin/stock-locations/${stockLocationId}/fulfillment-sets?fields=id,*fulfillment_sets`,
|
||||
{
|
||||
name: "Fulfillment Set",
|
||||
type: "shipping",
|
||||
},
|
||||
adminHeaders
|
||||
)
|
||||
|
||||
const fulfillmentSetId =
|
||||
locationWithFSetResponse.data.stock_location.fulfillment_sets[0].id
|
||||
|
||||
const deleteResponse = await api
|
||||
.delete(
|
||||
`/admin/fulfillment-sets/${fulfillmentSetId}/service-zones/foo`,
|
||||
adminHeaders
|
||||
)
|
||||
.catch((e) => e.response)
|
||||
|
||||
expect(deleteResponse.status).toEqual(404)
|
||||
expect(deleteResponse.data.message).toEqual(
|
||||
"Service zone with id: foo not found on fulfillment set"
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { FulfillmentWorkflow, IFulfillmentModuleService } from "@medusajs/types"
|
||||
import { getSelectsAndRelationsFromObjectArray } from "@medusajs/utils"
|
||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||
|
||||
type StepInput = FulfillmentWorkflow.UpdateServiceZonesWorkflowInput
|
||||
|
||||
export const updateServiceZonesStepId = "update-service-zones"
|
||||
export const updateServiceZonesStep = createStep(
|
||||
updateServiceZonesStepId,
|
||||
async (input: StepInput, { container }) => {
|
||||
const service = container.resolve<IFulfillmentModuleService>(
|
||||
ModuleRegistrationName.FULFILLMENT
|
||||
)
|
||||
|
||||
const { selects, relations } = getSelectsAndRelationsFromObjectArray([
|
||||
input.update,
|
||||
])
|
||||
|
||||
const prevData = await service.listServiceZones(input.selector, {
|
||||
select: selects,
|
||||
relations,
|
||||
})
|
||||
|
||||
const updatedServiceZones = await service.updateServiceZones(
|
||||
input.selector,
|
||||
input.update
|
||||
)
|
||||
|
||||
return new StepResponse(updatedServiceZones, prevData)
|
||||
},
|
||||
async (prevData, { container }) => {
|
||||
if (!prevData?.length) {
|
||||
return
|
||||
}
|
||||
|
||||
const service = container.resolve<IFulfillmentModuleService>(
|
||||
ModuleRegistrationName.FULFILLMENT
|
||||
)
|
||||
|
||||
await service.upsertServiceZones(prevData)
|
||||
}
|
||||
)
|
||||
@@ -1,5 +1,6 @@
|
||||
export * from "./add-rules-to-fulfillment-shipping-option"
|
||||
export * from "./create-service-zones"
|
||||
export * from "./delete-service-zones"
|
||||
export * from "./create-shipping-options"
|
||||
export * from "./delete-service-zones"
|
||||
export * from "./remove-rules-from-fulfillment-shipping-option"
|
||||
export * from "./update-service-zones"
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
import { FulfillmentWorkflow } from "@medusajs/types"
|
||||
import { WorkflowData, createWorkflow } from "@medusajs/workflows-sdk"
|
||||
import { updateServiceZonesStep } from "../steps/update-service-zones"
|
||||
|
||||
export const updateServiceZonesWorkflowId = "update-service-zones-workflow"
|
||||
export const updateServiceZonesWorkflow = createWorkflow(
|
||||
updateServiceZonesWorkflowId,
|
||||
(
|
||||
input: WorkflowData<FulfillmentWorkflow.UpdateServiceZonesWorkflowInput>
|
||||
): WorkflowData => {
|
||||
const serviceZones = updateServiceZonesStep(input)
|
||||
|
||||
return serviceZones
|
||||
}
|
||||
)
|
||||
@@ -284,7 +284,6 @@ moduleIntegrationTestRunner({
|
||||
)
|
||||
|
||||
const updateData = {
|
||||
id: createdServiceZone.id,
|
||||
name: "updated-service-zone-test",
|
||||
geo_zones: [
|
||||
{
|
||||
@@ -296,12 +295,13 @@ moduleIntegrationTestRunner({
|
||||
}
|
||||
|
||||
const updatedServiceZone = await service.updateServiceZones(
|
||||
createdServiceZone.id,
|
||||
updateData
|
||||
)
|
||||
|
||||
expect(updatedServiceZone).toEqual(
|
||||
expect.objectContaining({
|
||||
id: updateData.id,
|
||||
id: createdServiceZone.id,
|
||||
name: updateData.name,
|
||||
geo_zones: expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
@@ -314,7 +314,60 @@ moduleIntegrationTestRunner({
|
||||
)
|
||||
})
|
||||
|
||||
it("should update a collection of service zones", async function () {
|
||||
it("should fail on duplicated service zone name", async function () {
|
||||
const fulfillmentSet = await service.create({
|
||||
name: "test",
|
||||
type: "test-type",
|
||||
})
|
||||
|
||||
const createData: CreateServiceZoneDTO[] = [
|
||||
{
|
||||
name: "service-zone-test",
|
||||
fulfillment_set_id: fulfillmentSet.id,
|
||||
geo_zones: [
|
||||
{
|
||||
type: GeoZoneType.COUNTRY,
|
||||
country_code: "fr",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "service-zone-test2",
|
||||
fulfillment_set_id: fulfillmentSet.id,
|
||||
geo_zones: [
|
||||
{
|
||||
type: GeoZoneType.COUNTRY,
|
||||
country_code: "us",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const createdServiceZones = await service.createServiceZones(
|
||||
createData
|
||||
)
|
||||
|
||||
const updateData: UpdateServiceZoneDTO = {
|
||||
name: "service-zone-test",
|
||||
geo_zones: [
|
||||
{
|
||||
type: GeoZoneType.COUNTRY,
|
||||
country_code: "us",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const err = await service
|
||||
.updateServiceZones(createdServiceZones[1].id, updateData)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(err).toBeDefined()
|
||||
expect(err.message).toContain("exists")
|
||||
})
|
||||
})
|
||||
|
||||
describe("on upsert", () => {
|
||||
it("should upsert a collection of service zones", async function () {
|
||||
const fulfillmentSet = await service.create({
|
||||
name: "test",
|
||||
type: "test-type",
|
||||
@@ -360,7 +413,7 @@ moduleIntegrationTestRunner({
|
||||
})
|
||||
)
|
||||
|
||||
const updatedServiceZones = await service.updateServiceZones(
|
||||
const updatedServiceZones = await service.upsertServiceZones(
|
||||
updateData
|
||||
)
|
||||
|
||||
@@ -385,58 +438,6 @@ moduleIntegrationTestRunner({
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("should fail on duplicated service zone name", async function () {
|
||||
const fulfillmentSet = await service.create({
|
||||
name: "test",
|
||||
type: "test-type",
|
||||
})
|
||||
|
||||
const createData: CreateServiceZoneDTO[] = [
|
||||
{
|
||||
name: "service-zone-test",
|
||||
fulfillment_set_id: fulfillmentSet.id,
|
||||
geo_zones: [
|
||||
{
|
||||
type: GeoZoneType.COUNTRY,
|
||||
country_code: "fr",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "service-zone-test2",
|
||||
fulfillment_set_id: fulfillmentSet.id,
|
||||
geo_zones: [
|
||||
{
|
||||
type: GeoZoneType.COUNTRY,
|
||||
country_code: "us",
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const createdServiceZones = await service.createServiceZones(
|
||||
createData
|
||||
)
|
||||
|
||||
const updateData: UpdateServiceZoneDTO = {
|
||||
id: createdServiceZones[1].id,
|
||||
name: "service-zone-test",
|
||||
geo_zones: [
|
||||
{
|
||||
type: GeoZoneType.COUNTRY,
|
||||
country_code: "us",
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
const err = await service
|
||||
.updateServiceZones(updateData)
|
||||
.catch((e) => e)
|
||||
|
||||
expect(err).toBeDefined()
|
||||
expect(err.message).toContain("exists")
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
import {
|
||||
Context,
|
||||
DAL,
|
||||
@@ -11,20 +12,21 @@ import {
|
||||
ModulesSdkTypes,
|
||||
ShippingOptionDTO,
|
||||
UpdateFulfillmentSetDTO,
|
||||
UpdateServiceZoneDTO,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
arrayDifference,
|
||||
EmitEvents,
|
||||
FulfillmentUtils,
|
||||
getSetDifference,
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
ModulesSdkUtils,
|
||||
arrayDifference,
|
||||
getSetDifference,
|
||||
isString,
|
||||
promiseAll,
|
||||
} from "@medusajs/utils"
|
||||
|
||||
import {
|
||||
Fulfillment,
|
||||
FulfillmentSet,
|
||||
@@ -38,7 +40,6 @@ import {
|
||||
import { isContextValid, validateRules } from "@utils"
|
||||
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
||||
import FulfillmentProviderService from "./fulfillment-provider"
|
||||
import { Modules } from "@medusajs/modules-sdk"
|
||||
|
||||
const generateMethodForModels = [
|
||||
ServiceZone,
|
||||
@@ -791,31 +792,56 @@ export default class FulfillmentModuleService<
|
||||
}
|
||||
|
||||
updateServiceZones(
|
||||
data: FulfillmentTypes.UpdateServiceZoneDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<FulfillmentTypes.ServiceZoneDTO[]>
|
||||
updateServiceZones(
|
||||
id: string,
|
||||
data: FulfillmentTypes.UpdateServiceZoneDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<FulfillmentTypes.ServiceZoneDTO>
|
||||
updateServiceZones(
|
||||
selector: FulfillmentTypes.FilterableServiceZoneProps,
|
||||
data: FulfillmentTypes.UpdateServiceZoneDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<FulfillmentTypes.ServiceZoneDTO[]>
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async updateServiceZones(
|
||||
data:
|
||||
| FulfillmentTypes.UpdateServiceZoneDTO[]
|
||||
| FulfillmentTypes.UpdateServiceZoneDTO,
|
||||
idOrSelector: string | FulfillmentTypes.FilterableServiceZoneProps,
|
||||
data: FulfillmentTypes.UpdateServiceZoneDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<
|
||||
FulfillmentTypes.ServiceZoneDTO[] | FulfillmentTypes.ServiceZoneDTO
|
||||
> {
|
||||
const normalizedInput: UpdateServiceZoneDTO[] = []
|
||||
|
||||
if (isString(idOrSelector)) {
|
||||
normalizedInput.push({ id: idOrSelector, ...data })
|
||||
} else {
|
||||
const serviceZones = await this.serviceZoneService_.list(
|
||||
{ ...idOrSelector },
|
||||
{},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
if (!serviceZones.length) {
|
||||
return []
|
||||
}
|
||||
|
||||
for (const serviceZone of serviceZones) {
|
||||
normalizedInput.push({ id: serviceZone.id, ...data })
|
||||
}
|
||||
}
|
||||
|
||||
const updatedServiceZones = await this.updateServiceZones_(
|
||||
data,
|
||||
normalizedInput,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const toReturn = isString(idOrSelector)
|
||||
? updatedServiceZones[0]
|
||||
: updatedServiceZones
|
||||
|
||||
return await this.baseRepository_.serialize<
|
||||
FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[]
|
||||
>(updatedServiceZones, {
|
||||
>(toReturn, {
|
||||
populate: true,
|
||||
})
|
||||
}
|
||||
@@ -873,7 +899,7 @@ export default class FulfillmentModuleService<
|
||||
|
||||
data_.forEach((serviceZone) => {
|
||||
if (serviceZone.geo_zones) {
|
||||
const existingServiceZone = serviceZoneMap.get(serviceZone.id)!
|
||||
const existingServiceZone = serviceZoneMap.get(serviceZone.id!)!
|
||||
const existingGeoZones = existingServiceZone.geo_zones
|
||||
const updatedGeoZones = serviceZone.geo_zones
|
||||
const toDeleteGeoZoneIds = getSetDifference(
|
||||
@@ -920,7 +946,9 @@ export default class FulfillmentModuleService<
|
||||
FulfillmentModuleService.validateGeoZones([geoZone])
|
||||
return geoZone
|
||||
}
|
||||
return geoZonesMap.get(geoZone.id)!
|
||||
const existing = geoZonesMap.get(geoZone.id)!
|
||||
|
||||
return { ...existing, ...geoZone }
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -942,6 +970,81 @@ export default class FulfillmentModuleService<
|
||||
return Array.isArray(data) ? updatedServiceZones : updatedServiceZones[0]
|
||||
}
|
||||
|
||||
upsertServiceZones(
|
||||
data: FulfillmentTypes.UpsertServiceZoneDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<FulfillmentTypes.ServiceZoneDTO>
|
||||
upsertServiceZones(
|
||||
data: FulfillmentTypes.UpsertServiceZoneDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<FulfillmentTypes.ServiceZoneDTO[]>
|
||||
|
||||
@InjectManager("baseRepository_")
|
||||
async upsertServiceZones(
|
||||
data:
|
||||
| FulfillmentTypes.UpsertServiceZoneDTO
|
||||
| FulfillmentTypes.UpsertServiceZoneDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<
|
||||
FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[]
|
||||
> {
|
||||
const upsertServiceZones = await this.upsertServiceZones_(
|
||||
data,
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const allServiceZones = await this.baseRepository_.serialize<
|
||||
FulfillmentTypes.ServiceZoneDTO[] | FulfillmentTypes.ServiceZoneDTO
|
||||
>(upsertServiceZones)
|
||||
|
||||
return Array.isArray(data) ? allServiceZones : allServiceZones[0]
|
||||
}
|
||||
|
||||
@InjectTransactionManager("baseRepository_")
|
||||
async upsertServiceZones_(
|
||||
data:
|
||||
| FulfillmentTypes.UpsertServiceZoneDTO[]
|
||||
| FulfillmentTypes.UpsertServiceZoneDTO,
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TServiceZoneEntity[] | TServiceZoneEntity> {
|
||||
const input = Array.isArray(data) ? data : [data]
|
||||
const forUpdate = input.filter(
|
||||
(serviceZone): serviceZone is FulfillmentTypes.UpdateServiceZoneDTO =>
|
||||
!!serviceZone.id
|
||||
)
|
||||
const forCreate = input.filter(
|
||||
(serviceZone): serviceZone is FulfillmentTypes.CreateServiceZoneDTO =>
|
||||
!serviceZone.id
|
||||
)
|
||||
|
||||
const created: TServiceZoneEntity[] = []
|
||||
const updated: TServiceZoneEntity[] = []
|
||||
|
||||
if (forCreate.length) {
|
||||
const createdServiceZones = await this.createServiceZones_(
|
||||
forCreate,
|
||||
sharedContext
|
||||
)
|
||||
const toPush = Array.isArray(createdServiceZones)
|
||||
? createdServiceZones
|
||||
: [createdServiceZones]
|
||||
created.push(...toPush)
|
||||
}
|
||||
|
||||
if (forUpdate.length) {
|
||||
const updatedServiceZones = await this.updateServiceZones_(
|
||||
forUpdate,
|
||||
sharedContext
|
||||
)
|
||||
const toPush = Array.isArray(updatedServiceZones)
|
||||
? updatedServiceZones
|
||||
: [updatedServiceZones]
|
||||
updated.push(...toPush)
|
||||
}
|
||||
|
||||
return [...created, ...updated]
|
||||
}
|
||||
|
||||
updateShippingOptions(
|
||||
data: FulfillmentTypes.UpdateShippingOptionDTO[],
|
||||
sharedContext?: Context
|
||||
|
||||
@@ -1,14 +1,78 @@
|
||||
import {
|
||||
deleteServiceZonesWorkflow,
|
||||
updateServiceZonesWorkflow,
|
||||
} from "@medusajs/core-flows"
|
||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||
import { IFulfillmentModuleService } from "@medusajs/types"
|
||||
import { deleteServiceZonesWorkflow } from "../../../../../../../../core-flows/dist"
|
||||
import {
|
||||
AdminFulfillmentSetResponse,
|
||||
AdminServiceZoneDeleteResponse,
|
||||
IFulfillmentModuleService,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
ContainerRegistrationKeys,
|
||||
MedusaError,
|
||||
remoteQueryObjectFromString,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
AuthenticatedMedusaRequest,
|
||||
MedusaRequest,
|
||||
MedusaResponse,
|
||||
} from "../../../../../../types/routing"
|
||||
import { AdminUpdateFulfillmentSetServiceZonesType } from "../../../validators"
|
||||
|
||||
export const POST = async (
|
||||
req: MedusaRequest<AdminUpdateFulfillmentSetServiceZonesType>,
|
||||
res: MedusaResponse<AdminFulfillmentSetResponse>
|
||||
) => {
|
||||
const fulfillmentModuleService = req.scope.resolve<IFulfillmentModuleService>(
|
||||
ModuleRegistrationName.FULFILLMENT
|
||||
)
|
||||
|
||||
// ensure fulfillment set exists and that the service zone is part of it
|
||||
const fulfillmentSet = await fulfillmentModuleService.retrieve(
|
||||
req.params.id,
|
||||
{ relations: ["service_zones"] }
|
||||
)
|
||||
|
||||
if (!fulfillmentSet.service_zones.find((s) => s.id === req.params.zone_id)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Service zone with id: ${req.params.zone_id} not found on fulfillment set`
|
||||
)
|
||||
}
|
||||
|
||||
const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY)
|
||||
|
||||
const workflowInput = {
|
||||
selector: { id: req.params.zone_id },
|
||||
update: req.validatedBody,
|
||||
}
|
||||
|
||||
const { errors } = await updateServiceZonesWorkflow(req.scope).run({
|
||||
input: workflowInput,
|
||||
throwOnError: false,
|
||||
})
|
||||
|
||||
if (Array.isArray(errors) && errors[0]) {
|
||||
throw errors[0].error
|
||||
}
|
||||
|
||||
const [fulfillment_set] = await remoteQuery(
|
||||
remoteQueryObjectFromString({
|
||||
entryPoint: "fulfillment_sets",
|
||||
variables: {
|
||||
id: req.params.id,
|
||||
},
|
||||
fields: req.remoteQueryConfig.fields,
|
||||
})
|
||||
)
|
||||
|
||||
res.status(200).json({ fulfillment_set })
|
||||
}
|
||||
|
||||
export const DELETE = async (
|
||||
req: AuthenticatedMedusaRequest,
|
||||
res: MedusaResponse
|
||||
res: MedusaResponse<AdminServiceZoneDeleteResponse>
|
||||
) => {
|
||||
const { id, zone_id } = req.params
|
||||
|
||||
@@ -16,7 +80,17 @@ export const DELETE = async (
|
||||
ModuleRegistrationName.FULFILLMENT
|
||||
)
|
||||
|
||||
const fulfillmentSet = await fulfillmentModuleService.retrieve(id)
|
||||
// ensure fulfillment set exists and that the service zone is part of it
|
||||
const fulfillmentSet = await fulfillmentModuleService.retrieve(id, {
|
||||
relations: ["service_zones"],
|
||||
})
|
||||
|
||||
if (!fulfillmentSet.service_zones.find((s) => s.id === zone_id)) {
|
||||
throw new MedusaError(
|
||||
MedusaError.Types.NOT_FOUND,
|
||||
`Service zone with id: ${zone_id} not found on fulfillment set`
|
||||
)
|
||||
}
|
||||
|
||||
const { errors } = await deleteServiceZonesWorkflow(req.scope).run({
|
||||
input: { ids: [zone_id] },
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as QueryConfig from "./query-config"
|
||||
import {
|
||||
AdminCreateFulfillmentSetServiceZonesSchema,
|
||||
AdminFulfillmentSetParams,
|
||||
AdminUpdateFulfillmentSetServiceZonesSchema,
|
||||
} from "./validators"
|
||||
|
||||
export const adminFulfillmentSetsRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
@@ -30,4 +31,15 @@ export const adminFulfillmentSetsRoutesMiddlewares: MiddlewareRoute[] = [
|
||||
matcher: "/admin/fulfillment-sets/:id/service-zones/:zone_id",
|
||||
middlewares: [],
|
||||
},
|
||||
{
|
||||
method: ["POST"],
|
||||
matcher: "/admin/fulfillment-sets/:id/service-zones/:zone_id",
|
||||
middlewares: [
|
||||
validateAndTransformBody(AdminUpdateFulfillmentSetServiceZonesSchema),
|
||||
validateAndTransformQuery(
|
||||
AdminFulfillmentSetParams,
|
||||
QueryConfig.retrieveTransformQueryConfig
|
||||
),
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -1,62 +1,44 @@
|
||||
import { z } from "zod"
|
||||
import { createFindParams, createOperatorMap } from "../../utils/validators"
|
||||
|
||||
const geoZoneBaseSchema = z.object({
|
||||
country_code: z.string(),
|
||||
metadata: z.record(z.unknown()).optional(),
|
||||
})
|
||||
|
||||
const geoZoneCountrySchema = geoZoneBaseSchema.merge(
|
||||
z.object({
|
||||
type: z.literal("country"),
|
||||
})
|
||||
)
|
||||
|
||||
const geoZoneProvinceSchema = geoZoneBaseSchema.merge(
|
||||
z.object({
|
||||
type: z.literal("province"),
|
||||
province_code: z.string(),
|
||||
})
|
||||
)
|
||||
|
||||
const geoZoneCitySchema = geoZoneBaseSchema.merge(
|
||||
z.object({
|
||||
type: z.literal("city"),
|
||||
province_code: z.string(),
|
||||
city: z.string(),
|
||||
})
|
||||
)
|
||||
|
||||
const geoZoneZipSchema = geoZoneBaseSchema.merge(
|
||||
z.object({
|
||||
type: z.literal("zip"),
|
||||
province_code: z.string(),
|
||||
city: z.string(),
|
||||
postal_expression: z.record(z.unknown()),
|
||||
})
|
||||
)
|
||||
import {
|
||||
geoZoneCitySchema,
|
||||
geoZoneCountrySchema,
|
||||
geoZoneProvinceSchema,
|
||||
geoZoneZipSchema,
|
||||
} from "./validators/geo-zone"
|
||||
|
||||
export const AdminCreateFulfillmentSetServiceZonesSchema = z
|
||||
.object({
|
||||
name: z.string(),
|
||||
geo_zones: z.array(
|
||||
z.union([
|
||||
geoZoneCountrySchema,
|
||||
geoZoneProvinceSchema,
|
||||
geoZoneCitySchema,
|
||||
geoZoneZipSchema,
|
||||
])
|
||||
),
|
||||
geo_zones: z
|
||||
.array(
|
||||
z.union([
|
||||
geoZoneCountrySchema,
|
||||
geoZoneProvinceSchema,
|
||||
geoZoneCitySchema,
|
||||
geoZoneZipSchema,
|
||||
])
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
.strict()
|
||||
|
||||
export type AdminCreateFulfillmentSetServiceZonesType = z.infer<
|
||||
typeof AdminCreateFulfillmentSetServiceZonesSchema
|
||||
>
|
||||
export const AdminUpdateFulfillmentSetServiceZonesSchema = z
|
||||
.object({
|
||||
name: z.string().optional(),
|
||||
geo_zones: z
|
||||
.array(
|
||||
z.union([
|
||||
geoZoneCountrySchema.merge(z.object({ id: z.string().optional() })),
|
||||
geoZoneProvinceSchema.merge(z.object({ id: z.string().optional() })),
|
||||
geoZoneCitySchema.merge(z.object({ id: z.string().optional() })),
|
||||
geoZoneZipSchema.merge(z.object({ id: z.string().optional() })),
|
||||
])
|
||||
)
|
||||
.optional(),
|
||||
})
|
||||
.strict()
|
||||
|
||||
export type AdminFulfillmentSetParamsType = z.infer<
|
||||
typeof AdminFulfillmentSetParams
|
||||
>
|
||||
export const AdminFulfillmentSetParams = createFindParams({
|
||||
limit: 20,
|
||||
offset: 0,
|
||||
@@ -71,3 +53,13 @@ export const AdminFulfillmentSetParams = createFindParams({
|
||||
deleted_at: createOperatorMap().optional(),
|
||||
})
|
||||
)
|
||||
|
||||
export type AdminCreateFulfillmentSetServiceZonesType = z.infer<
|
||||
typeof AdminCreateFulfillmentSetServiceZonesSchema
|
||||
>
|
||||
export type AdminUpdateFulfillmentSetServiceZonesType = z.infer<
|
||||
typeof AdminUpdateFulfillmentSetServiceZonesSchema
|
||||
>
|
||||
export type AdminFulfillmentSetParamsType = z.infer<
|
||||
typeof AdminFulfillmentSetParams
|
||||
>
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { z } from "zod"
|
||||
|
||||
const geoZoneBaseSchema = z.object({
|
||||
country_code: z.string(),
|
||||
metadata: z.record(z.unknown()).optional(),
|
||||
})
|
||||
|
||||
export const geoZoneCountrySchema = geoZoneBaseSchema.merge(
|
||||
z.object({
|
||||
type: z.literal("country"),
|
||||
})
|
||||
)
|
||||
|
||||
export const geoZoneProvinceSchema = geoZoneBaseSchema.merge(
|
||||
z.object({
|
||||
type: z.literal("province"),
|
||||
province_code: z.string(),
|
||||
})
|
||||
)
|
||||
|
||||
export const geoZoneCitySchema = geoZoneBaseSchema.merge(
|
||||
z.object({
|
||||
type: z.literal("city"),
|
||||
province_code: z.string(),
|
||||
city: z.string(),
|
||||
})
|
||||
)
|
||||
|
||||
export const geoZoneZipSchema = geoZoneBaseSchema.merge(
|
||||
z.object({
|
||||
type: z.literal("zip"),
|
||||
province_code: z.string(),
|
||||
city: z.string(),
|
||||
postal_expression: z.record(z.unknown()),
|
||||
})
|
||||
)
|
||||
@@ -17,7 +17,7 @@ export interface CreateServiceZoneDTO {
|
||||
}
|
||||
|
||||
export interface UpdateServiceZoneDTO {
|
||||
id: string
|
||||
id?: string
|
||||
name?: string
|
||||
geo_zones?: (
|
||||
| Omit<CreateCountryGeoZoneDTO, "service_zone_id">
|
||||
@@ -27,3 +27,5 @@ export interface UpdateServiceZoneDTO {
|
||||
| { id: string }
|
||||
)[]
|
||||
}
|
||||
|
||||
export interface UpsertServiceZoneDTO extends UpdateServiceZoneDTO {}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { FindConfig } from "../common"
|
||||
import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
||||
import { IModuleService } from "../modules-sdk"
|
||||
import { Context } from "../shared-context"
|
||||
import {
|
||||
FilterableFulfillmentProps,
|
||||
FilterableFulfillmentSetProps,
|
||||
@@ -18,9 +21,6 @@ import {
|
||||
ShippingOptionTypeDTO,
|
||||
ShippingProfileDTO,
|
||||
} from "./common"
|
||||
import { FindConfig } from "../common"
|
||||
import { Context } from "../shared-context"
|
||||
import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
||||
import {
|
||||
CreateFulfillmentSetDTO,
|
||||
CreateGeoZoneDTO,
|
||||
@@ -34,9 +34,10 @@ import {
|
||||
UpdateShippingOptionDTO,
|
||||
UpdateShippingOptionRuleDTO,
|
||||
UpdateShippingProfileDTO,
|
||||
UpsertServiceZoneDTO,
|
||||
} from "./mutations"
|
||||
import { CreateShippingProfileDTO } from "./mutations/shipping-profile"
|
||||
import { CreateFulfillmentDTO } from "./mutations/fulfillment"
|
||||
import { CreateShippingProfileDTO } from "./mutations/shipping-profile"
|
||||
|
||||
export interface IFulfillmentModuleService extends IModuleService {
|
||||
/**
|
||||
@@ -176,17 +177,29 @@ export interface IFulfillmentModuleService extends IModuleService {
|
||||
): Promise<ServiceZoneDTO>
|
||||
/**
|
||||
* Update a service zone
|
||||
* @param data
|
||||
* @param sharedContext
|
||||
*/
|
||||
updateServiceZones(
|
||||
data: UpdateServiceZoneDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<ServiceZoneDTO[]>
|
||||
updateServiceZones(
|
||||
id: string,
|
||||
data: UpdateServiceZoneDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<ServiceZoneDTO>
|
||||
updateServiceZones(
|
||||
selector: FilterableServiceZoneProps,
|
||||
data: UpdateServiceZoneDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<ServiceZoneDTO[]>
|
||||
|
||||
/**
|
||||
* Upsert a service zone
|
||||
*/
|
||||
upsertServiceZones(
|
||||
data: UpsertServiceZoneDTO,
|
||||
sharedContext?: Context
|
||||
): Promise<ServiceZoneDTO>
|
||||
upsertServiceZones(
|
||||
data: UpsertServiceZoneDTO[],
|
||||
sharedContext?: Context
|
||||
): Promise<ServiceZoneDTO[]>
|
||||
/**
|
||||
* Delete a service zone
|
||||
* @param ids
|
||||
|
||||
@@ -3,7 +3,7 @@ import { AdminServiceZoneResponse } from "./service-zone"
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export interface AdminFulfillmentSetResponse {
|
||||
export interface FulfillmentSetResponse {
|
||||
id: string
|
||||
name: string
|
||||
type: string
|
||||
@@ -13,3 +13,10 @@ export interface AdminFulfillmentSetResponse {
|
||||
updated_at: Date
|
||||
deleted_at: Date | null
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export interface AdminFulfillmentSetResponse {
|
||||
fulfillment_set: FulfillmentSetResponse
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { FulfillmentSetResponse } from "./fulfillment-set"
|
||||
import { AdminGeoZoneResponse } from "./geo-zone"
|
||||
|
||||
/**
|
||||
@@ -12,3 +13,13 @@ export interface AdminServiceZoneResponse {
|
||||
updated_at: Date
|
||||
deleted_at: Date | null
|
||||
}
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export interface AdminServiceZoneDeleteResponse {
|
||||
id: string
|
||||
object: "service-zone"
|
||||
deleted: boolean
|
||||
parent: FulfillmentSetResponse
|
||||
}
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
export * from "./create-service-zones"
|
||||
export * from "./create-shipping-options"
|
||||
export * from "./service-zones"
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
CreateCountryGeoZoneDTO,
|
||||
CreateProvinceGeoZoneDTO,
|
||||
CreateZipGeoZoneDTO,
|
||||
FilterableServiceZoneProps,
|
||||
} from "../../fulfillment"
|
||||
|
||||
interface CreateServiceZone {
|
||||
@@ -19,3 +20,19 @@ interface CreateServiceZone {
|
||||
export interface CreateServiceZonesWorkflowInput {
|
||||
data: CreateServiceZone[]
|
||||
}
|
||||
|
||||
interface UpdateServiceZone {
|
||||
name?: string
|
||||
geo_zones?: (
|
||||
| Omit<CreateCountryGeoZoneDTO, "service_zone_id">
|
||||
| Omit<CreateProvinceGeoZoneDTO, "service_zone_id">
|
||||
| Omit<CreateCityGeoZoneDTO, "service_zone_id">
|
||||
| Omit<CreateZipGeoZoneDTO, "service_zone_id">
|
||||
| { id: string }
|
||||
)[]
|
||||
}
|
||||
|
||||
export interface UpdateServiceZonesWorkflowInput {
|
||||
selector: FilterableServiceZoneProps
|
||||
update: UpdateServiceZone
|
||||
}
|
||||
Reference in New Issue
Block a user