feat: Update service zone (#6990)

This commit is contained in:
Oli Juhl
2024-04-07 17:30:55 +02:00
committed by GitHub
parent b6879f017a
commit a24d6e6c97
16 changed files with 576 additions and 194 deletions
@@ -26,7 +26,7 @@ medusaIntegrationTestRunner({
}) })
describe("POST /admin/fulfillment-sets/:id/service-zones", () => { 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( const stockLocationResponse = await api.post(
`/admin/stock-locations`, `/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 () => { it("should throw if invalid type is passed", async () => {
@@ -251,60 +322,6 @@ medusaIntegrationTestRunner({
}) })
describe("POST /admin/fulfillment-sets/:id/service-zones", () => { 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 () => { it("should throw when fulfillment set doesn't exist", async () => {
const deleteResponse = await api const deleteResponse = await api
.delete( .delete(
@@ -314,7 +331,45 @@ medusaIntegrationTestRunner({
.catch((e) => e.response) .catch((e) => e.response)
expect(deleteResponse.status).toEqual(404) 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 "./add-rules-to-fulfillment-shipping-option"
export * from "./create-service-zones" export * from "./create-service-zones"
export * from "./delete-service-zones"
export * from "./create-shipping-options" export * from "./create-shipping-options"
export * from "./delete-service-zones"
export * from "./remove-rules-from-fulfillment-shipping-option" 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 = { const updateData = {
id: createdServiceZone.id,
name: "updated-service-zone-test", name: "updated-service-zone-test",
geo_zones: [ geo_zones: [
{ {
@@ -296,12 +295,13 @@ moduleIntegrationTestRunner({
} }
const updatedServiceZone = await service.updateServiceZones( const updatedServiceZone = await service.updateServiceZones(
createdServiceZone.id,
updateData updateData
) )
expect(updatedServiceZone).toEqual( expect(updatedServiceZone).toEqual(
expect.objectContaining({ expect.objectContaining({
id: updateData.id, id: createdServiceZone.id,
name: updateData.name, name: updateData.name,
geo_zones: expect.arrayContaining([ geo_zones: expect.arrayContaining([
expect.objectContaining({ 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({ const fulfillmentSet = await service.create({
name: "test", name: "test",
type: "test-type", type: "test-type",
@@ -360,7 +413,7 @@ moduleIntegrationTestRunner({
}) })
) )
const updatedServiceZones = await service.updateServiceZones( const updatedServiceZones = await service.upsertServiceZones(
updateData 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 { import {
Context, Context,
DAL, DAL,
@@ -11,20 +12,21 @@ import {
ModulesSdkTypes, ModulesSdkTypes,
ShippingOptionDTO, ShippingOptionDTO,
UpdateFulfillmentSetDTO, UpdateFulfillmentSetDTO,
UpdateServiceZoneDTO,
} from "@medusajs/types" } from "@medusajs/types"
import { import {
arrayDifference,
EmitEvents, EmitEvents,
FulfillmentUtils, FulfillmentUtils,
getSetDifference,
InjectManager, InjectManager,
InjectTransactionManager, InjectTransactionManager,
MedusaContext, MedusaContext,
MedusaError, MedusaError,
ModulesSdkUtils, ModulesSdkUtils,
arrayDifference,
getSetDifference,
isString,
promiseAll, promiseAll,
} from "@medusajs/utils" } from "@medusajs/utils"
import { import {
Fulfillment, Fulfillment,
FulfillmentSet, FulfillmentSet,
@@ -38,7 +40,6 @@ import {
import { isContextValid, validateRules } from "@utils" import { isContextValid, validateRules } from "@utils"
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
import FulfillmentProviderService from "./fulfillment-provider" import FulfillmentProviderService from "./fulfillment-provider"
import { Modules } from "@medusajs/modules-sdk"
const generateMethodForModels = [ const generateMethodForModels = [
ServiceZone, ServiceZone,
@@ -791,31 +792,56 @@ export default class FulfillmentModuleService<
} }
updateServiceZones( updateServiceZones(
data: FulfillmentTypes.UpdateServiceZoneDTO[], id: string,
sharedContext?: Context
): Promise<FulfillmentTypes.ServiceZoneDTO[]>
updateServiceZones(
data: FulfillmentTypes.UpdateServiceZoneDTO, data: FulfillmentTypes.UpdateServiceZoneDTO,
sharedContext?: Context sharedContext?: Context
): Promise<FulfillmentTypes.ServiceZoneDTO> ): Promise<FulfillmentTypes.ServiceZoneDTO>
updateServiceZones(
selector: FulfillmentTypes.FilterableServiceZoneProps,
data: FulfillmentTypes.UpdateServiceZoneDTO,
sharedContext?: Context
): Promise<FulfillmentTypes.ServiceZoneDTO[]>
@InjectManager("baseRepository_") @InjectManager("baseRepository_")
async updateServiceZones( async updateServiceZones(
data: idOrSelector: string | FulfillmentTypes.FilterableServiceZoneProps,
| FulfillmentTypes.UpdateServiceZoneDTO[] data: FulfillmentTypes.UpdateServiceZoneDTO,
| FulfillmentTypes.UpdateServiceZoneDTO,
@MedusaContext() sharedContext: Context = {} @MedusaContext() sharedContext: Context = {}
): Promise< ): Promise<
FulfillmentTypes.ServiceZoneDTO[] | FulfillmentTypes.ServiceZoneDTO 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_( const updatedServiceZones = await this.updateServiceZones_(
data, normalizedInput,
sharedContext sharedContext
) )
const toReturn = isString(idOrSelector)
? updatedServiceZones[0]
: updatedServiceZones
return await this.baseRepository_.serialize< return await this.baseRepository_.serialize<
FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[] FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[]
>(updatedServiceZones, { >(toReturn, {
populate: true, populate: true,
}) })
} }
@@ -873,7 +899,7 @@ export default class FulfillmentModuleService<
data_.forEach((serviceZone) => { data_.forEach((serviceZone) => {
if (serviceZone.geo_zones) { if (serviceZone.geo_zones) {
const existingServiceZone = serviceZoneMap.get(serviceZone.id)! const existingServiceZone = serviceZoneMap.get(serviceZone.id!)!
const existingGeoZones = existingServiceZone.geo_zones const existingGeoZones = existingServiceZone.geo_zones
const updatedGeoZones = serviceZone.geo_zones const updatedGeoZones = serviceZone.geo_zones
const toDeleteGeoZoneIds = getSetDifference( const toDeleteGeoZoneIds = getSetDifference(
@@ -920,7 +946,9 @@ export default class FulfillmentModuleService<
FulfillmentModuleService.validateGeoZones([geoZone]) FulfillmentModuleService.validateGeoZones([geoZone])
return 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] 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( updateShippingOptions(
data: FulfillmentTypes.UpdateShippingOptionDTO[], data: FulfillmentTypes.UpdateShippingOptionDTO[],
sharedContext?: Context sharedContext?: Context
@@ -1,14 +1,78 @@
import {
deleteServiceZonesWorkflow,
updateServiceZonesWorkflow,
} from "@medusajs/core-flows"
import { ModuleRegistrationName } from "@medusajs/modules-sdk" import { ModuleRegistrationName } from "@medusajs/modules-sdk"
import { IFulfillmentModuleService } from "@medusajs/types" import {
import { deleteServiceZonesWorkflow } from "../../../../../../../../core-flows/dist" AdminFulfillmentSetResponse,
AdminServiceZoneDeleteResponse,
IFulfillmentModuleService,
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
MedusaError,
remoteQueryObjectFromString,
} from "@medusajs/utils"
import { import {
AuthenticatedMedusaRequest, AuthenticatedMedusaRequest,
MedusaRequest,
MedusaResponse, MedusaResponse,
} from "../../../../../../types/routing" } 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 ( export const DELETE = async (
req: AuthenticatedMedusaRequest, req: AuthenticatedMedusaRequest,
res: MedusaResponse res: MedusaResponse<AdminServiceZoneDeleteResponse>
) => { ) => {
const { id, zone_id } = req.params const { id, zone_id } = req.params
@@ -16,7 +80,17 @@ export const DELETE = async (
ModuleRegistrationName.FULFILLMENT 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({ const { errors } = await deleteServiceZonesWorkflow(req.scope).run({
input: { ids: [zone_id] }, input: { ids: [zone_id] },
@@ -6,6 +6,7 @@ import * as QueryConfig from "./query-config"
import { import {
AdminCreateFulfillmentSetServiceZonesSchema, AdminCreateFulfillmentSetServiceZonesSchema,
AdminFulfillmentSetParams, AdminFulfillmentSetParams,
AdminUpdateFulfillmentSetServiceZonesSchema,
} from "./validators" } from "./validators"
export const adminFulfillmentSetsRoutesMiddlewares: MiddlewareRoute[] = [ export const adminFulfillmentSetsRoutesMiddlewares: MiddlewareRoute[] = [
@@ -30,4 +31,15 @@ export const adminFulfillmentSetsRoutesMiddlewares: MiddlewareRoute[] = [
matcher: "/admin/fulfillment-sets/:id/service-zones/:zone_id", matcher: "/admin/fulfillment-sets/:id/service-zones/:zone_id",
middlewares: [], 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 { z } from "zod"
import { createFindParams, createOperatorMap } from "../../utils/validators" import { createFindParams, createOperatorMap } from "../../utils/validators"
import {
const geoZoneBaseSchema = z.object({ geoZoneCitySchema,
country_code: z.string(), geoZoneCountrySchema,
metadata: z.record(z.unknown()).optional(), geoZoneProvinceSchema,
}) geoZoneZipSchema,
} from "./validators/geo-zone"
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()),
})
)
export const AdminCreateFulfillmentSetServiceZonesSchema = z export const AdminCreateFulfillmentSetServiceZonesSchema = z
.object({ .object({
name: z.string(), name: z.string(),
geo_zones: z.array( geo_zones: z
z.union([ .array(
geoZoneCountrySchema, z.union([
geoZoneProvinceSchema, geoZoneCountrySchema,
geoZoneCitySchema, geoZoneProvinceSchema,
geoZoneZipSchema, geoZoneCitySchema,
]) geoZoneZipSchema,
), ])
)
.optional(),
}) })
.strict() .strict()
export type AdminCreateFulfillmentSetServiceZonesType = z.infer< export const AdminUpdateFulfillmentSetServiceZonesSchema = z
typeof AdminCreateFulfillmentSetServiceZonesSchema .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({ export const AdminFulfillmentSetParams = createFindParams({
limit: 20, limit: 20,
offset: 0, offset: 0,
@@ -71,3 +53,13 @@ export const AdminFulfillmentSetParams = createFindParams({
deleted_at: createOperatorMap().optional(), 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 { export interface UpdateServiceZoneDTO {
id: string id?: string
name?: string name?: string
geo_zones?: ( geo_zones?: (
| Omit<CreateCountryGeoZoneDTO, "service_zone_id"> | Omit<CreateCountryGeoZoneDTO, "service_zone_id">
@@ -27,3 +27,5 @@ export interface UpdateServiceZoneDTO {
| { id: string } | { id: string }
)[] )[]
} }
export interface UpsertServiceZoneDTO extends UpdateServiceZoneDTO {}
+23 -10
View File
@@ -1,4 +1,7 @@
import { FindConfig } from "../common"
import { RestoreReturn, SoftDeleteReturn } from "../dal"
import { IModuleService } from "../modules-sdk" import { IModuleService } from "../modules-sdk"
import { Context } from "../shared-context"
import { import {
FilterableFulfillmentProps, FilterableFulfillmentProps,
FilterableFulfillmentSetProps, FilterableFulfillmentSetProps,
@@ -18,9 +21,6 @@ import {
ShippingOptionTypeDTO, ShippingOptionTypeDTO,
ShippingProfileDTO, ShippingProfileDTO,
} from "./common" } from "./common"
import { FindConfig } from "../common"
import { Context } from "../shared-context"
import { RestoreReturn, SoftDeleteReturn } from "../dal"
import { import {
CreateFulfillmentSetDTO, CreateFulfillmentSetDTO,
CreateGeoZoneDTO, CreateGeoZoneDTO,
@@ -34,9 +34,10 @@ import {
UpdateShippingOptionDTO, UpdateShippingOptionDTO,
UpdateShippingOptionRuleDTO, UpdateShippingOptionRuleDTO,
UpdateShippingProfileDTO, UpdateShippingProfileDTO,
UpsertServiceZoneDTO,
} from "./mutations" } from "./mutations"
import { CreateShippingProfileDTO } from "./mutations/shipping-profile"
import { CreateFulfillmentDTO } from "./mutations/fulfillment" import { CreateFulfillmentDTO } from "./mutations/fulfillment"
import { CreateShippingProfileDTO } from "./mutations/shipping-profile"
export interface IFulfillmentModuleService extends IModuleService { export interface IFulfillmentModuleService extends IModuleService {
/** /**
@@ -176,17 +177,29 @@ export interface IFulfillmentModuleService extends IModuleService {
): Promise<ServiceZoneDTO> ): Promise<ServiceZoneDTO>
/** /**
* Update a service zone * Update a service zone
* @param data
* @param sharedContext
*/ */
updateServiceZones( updateServiceZones(
data: UpdateServiceZoneDTO[], id: string,
sharedContext?: Context
): Promise<ServiceZoneDTO[]>
updateServiceZones(
data: UpdateServiceZoneDTO, data: UpdateServiceZoneDTO,
sharedContext?: Context sharedContext?: Context
): Promise<ServiceZoneDTO> ): 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 * Delete a service zone
* @param ids * @param ids
@@ -3,7 +3,7 @@ import { AdminServiceZoneResponse } from "./service-zone"
/** /**
* @experimental * @experimental
*/ */
export interface AdminFulfillmentSetResponse { export interface FulfillmentSetResponse {
id: string id: string
name: string name: string
type: string type: string
@@ -13,3 +13,10 @@ export interface AdminFulfillmentSetResponse {
updated_at: Date updated_at: Date
deleted_at: Date | null 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" import { AdminGeoZoneResponse } from "./geo-zone"
/** /**
@@ -12,3 +13,13 @@ export interface AdminServiceZoneResponse {
updated_at: Date updated_at: Date
deleted_at: Date | null 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 "./create-shipping-options"
export * from "./service-zones"
@@ -3,6 +3,7 @@ import {
CreateCountryGeoZoneDTO, CreateCountryGeoZoneDTO,
CreateProvinceGeoZoneDTO, CreateProvinceGeoZoneDTO,
CreateZipGeoZoneDTO, CreateZipGeoZoneDTO,
FilterableServiceZoneProps,
} from "../../fulfillment" } from "../../fulfillment"
interface CreateServiceZone { interface CreateServiceZone {
@@ -19,3 +20,19 @@ interface CreateServiceZone {
export interface CreateServiceZonesWorkflowInput { export interface CreateServiceZonesWorkflowInput {
data: CreateServiceZone[] 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
}