feat(fulfillment): Separate list and context rules validation (#6674)
**What** - Add method to validate fulfillment option from the provider - Separate list/list and count from context rules validation and add listShippingOptionsForContext FIXES CORE-1861
This commit is contained in:
1 parent
1956dce80a
commit
3188e703b3
16 files changed
+1011
-326
No files matched your search
@@ -1413,7 +1413,7 @@ medusaIntegrationTestRunner({
|
|||||||
geo_zones: [
|
geo_zones: [
|
||||||
{
|
{
|
||||||
type: "country",
|
type: "country",
|
||||||
country_code: "us",
|
country_code: "dk",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -1637,7 +1637,7 @@ medusaIntegrationTestRunner({
|
|||||||
geo_zones: [
|
geo_zones: [
|
||||||
{
|
{
|
||||||
type: "country",
|
type: "country",
|
||||||
country_code: "us",
|
country_code: "dk",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { ListShippingOptionsForCartWorkflowInputDTO } from "@medusajs/types"
|
import { ListShippingOptionsForCartWorkflowInputDTO } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
WorkflowData,
|
|
||||||
createWorkflow,
|
createWorkflow,
|
||||||
transform,
|
transform,
|
||||||
|
WorkflowData,
|
||||||
} from "@medusajs/workflows-sdk"
|
} from "@medusajs/workflows-sdk"
|
||||||
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
import { useRemoteQueryStep } from "../../../common/steps/use-remote-query"
|
||||||
import { listShippingOptionsForContextStep } from "../../../shipping-options"
|
import { listShippingOptionsForContextStep } from "../../../shipping-options"
|
||||||
@@ -33,12 +33,10 @@ export const listShippingOptionsForCartWorkflow = createWorkflow(
|
|||||||
return {
|
return {
|
||||||
context: {
|
context: {
|
||||||
fulfillment_set_id: fulfillmentSetIds,
|
fulfillment_set_id: fulfillmentSetIds,
|
||||||
service_zone: {
|
address: {
|
||||||
geo_zones: {
|
city: data.input.shipping_address?.city,
|
||||||
city: data.input.shipping_address?.city,
|
country_code: data.input.shipping_address?.country_code,
|
||||||
country_code: data.input.shipping_address?.country_code,
|
province_code: data.input.shipping_address?.province,
|
||||||
province_code: data.input.shipping_address?.province,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
config: {
|
config: {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
IFulfillmentModuleService,
|
IFulfillmentModuleService,
|
||||||
ShippingOptionDTO,
|
ShippingOptionDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
import { createStep, StepResponse } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
interface StepInput {
|
interface StepInput {
|
||||||
context: Record<string, unknown>
|
context: Record<string, unknown>
|
||||||
@@ -20,10 +20,11 @@ export const listShippingOptionsForContextStep = createStep(
|
|||||||
ModuleRegistrationName.FULFILLMENT
|
ModuleRegistrationName.FULFILLMENT
|
||||||
)
|
)
|
||||||
|
|
||||||
const shippingOptions = await fulfillmentService.listShippingOptions(
|
const shippingOptions =
|
||||||
data.context,
|
await fulfillmentService.listShippingOptionsForContext(
|
||||||
data.config
|
data.context,
|
||||||
)
|
data.config
|
||||||
|
)
|
||||||
|
|
||||||
return new StepResponse(shippingOptions)
|
return new StepResponse(shippingOptions)
|
||||||
}
|
}
|
||||||
|
|||||||
+205
-119
@@ -1,4 +1,4 @@
|
|||||||
import {Modules} from "@medusajs/modules-sdk"
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
import {
|
import {
|
||||||
CreateFulfillmentSetDTO,
|
CreateFulfillmentSetDTO,
|
||||||
CreateServiceZoneDTO,
|
CreateServiceZoneDTO,
|
||||||
@@ -6,8 +6,8 @@ import {
|
|||||||
ServiceZoneDTO,
|
ServiceZoneDTO,
|
||||||
UpdateFulfillmentSetDTO,
|
UpdateFulfillmentSetDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {GeoZoneType} from "@medusajs/utils"
|
import { GeoZoneType } from "@medusajs/utils"
|
||||||
import {moduleIntegrationTestRunner, SuiteOptions} from "medusa-test-utils"
|
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||||
|
|
||||||
jest.setTimeout(100000)
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
@@ -17,123 +17,123 @@ moduleIntegrationTestRunner({
|
|||||||
describe("Fulfillment Module Service", () => {
|
describe("Fulfillment Module Service", () => {
|
||||||
describe("read", () => {
|
describe("read", () => {
|
||||||
it("should list fulfillment sets with a filter", async function () {
|
it("should list fulfillment sets with a filter", async function () {
|
||||||
const createdSet1 = await service.create({
|
const createdSet1 = await service.create({
|
||||||
name: "test",
|
name: "test",
|
||||||
type: "test-type",
|
type: "test-type",
|
||||||
})
|
|
||||||
const createdSet2 = await service.create({
|
|
||||||
name: "test2",
|
|
||||||
type: "test-type",
|
|
||||||
service_zones: [
|
|
||||||
{
|
|
||||||
name: "test",
|
|
||||||
geo_zones: [
|
|
||||||
{
|
|
||||||
type: GeoZoneType.COUNTRY,
|
|
||||||
country_code: "fr",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "test2",
|
|
||||||
geo_zones: [
|
|
||||||
{
|
|
||||||
type: GeoZoneType.COUNTRY,
|
|
||||||
country_code: "fr",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "_test",
|
|
||||||
geo_zones: [
|
|
||||||
{
|
|
||||||
type: GeoZoneType.COUNTRY,
|
|
||||||
country_code: "fr",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
let listedSets = await service.list(
|
|
||||||
{
|
|
||||||
type: createdSet1.type,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
relations: ["service_zones"],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const listedSets2 = await service.list(
|
|
||||||
{
|
|
||||||
type: createdSet1.type,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
relations: ["service_zones"],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(listedSets).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdSet1.id }),
|
|
||||||
expect.objectContaining({ id: createdSet2.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
|
|
||||||
// Respecting order id by default
|
|
||||||
expect(listedSets[1].service_zones).toEqual([
|
|
||||||
expect.objectContaining({ name: "test" }),
|
|
||||||
expect.objectContaining({ name: "test2" }),
|
|
||||||
expect.objectContaining({ name: "_test" }),
|
|
||||||
])
|
|
||||||
|
|
||||||
expect(listedSets2).toEqual(listedSets2)
|
|
||||||
|
|
||||||
listedSets = await service.list({
|
|
||||||
name: createdSet2.name,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(listedSets).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdSet2.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
expect(listedSets).not.toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdSet1.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
|
|
||||||
listedSets = await service.list({
|
|
||||||
service_zones: { name: "test" },
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(listedSets).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdSet2.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
expect(listedSets).not.toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdSet1.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
|
|
||||||
listedSets = await service.list({
|
|
||||||
service_zones: { geo_zones: { country_code: "fr" } },
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(listedSets).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdSet2.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
expect(listedSets).not.toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdSet1.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
const createdSet2 = await service.create({
|
||||||
|
name: "test2",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "_test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
let listedSets = await service.list(
|
||||||
|
{
|
||||||
|
type: createdSet1.type,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relations: ["service_zones"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const listedSets2 = await service.list(
|
||||||
|
{
|
||||||
|
type: createdSet1.type,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relations: ["service_zones"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(listedSets).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdSet1.id }),
|
||||||
|
expect.objectContaining({ id: createdSet2.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
// Respecting order id by default
|
||||||
|
expect(listedSets[1].service_zones).toEqual([
|
||||||
|
expect.objectContaining({ name: "test" }),
|
||||||
|
expect.objectContaining({ name: "test2" }),
|
||||||
|
expect.objectContaining({ name: "_test" }),
|
||||||
|
])
|
||||||
|
|
||||||
|
expect(listedSets2).toEqual(listedSets2)
|
||||||
|
|
||||||
|
listedSets = await service.list({
|
||||||
|
name: createdSet2.name,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(listedSets).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdSet2.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
expect(listedSets).not.toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdSet1.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
listedSets = await service.list({
|
||||||
|
service_zones: { name: "test" },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(listedSets).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdSet2.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
expect(listedSets).not.toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdSet1.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
listedSets = await service.list({
|
||||||
|
service_zones: { geo_zones: { country_code: "fr" } },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(listedSets).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdSet2.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
expect(listedSets).not.toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdSet1.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("mutations", () => {
|
describe("mutations", () => {
|
||||||
@@ -349,6 +349,7 @@ moduleIntegrationTestRunner({
|
|||||||
{
|
{
|
||||||
type: GeoZoneType.CITY,
|
type: GeoZoneType.CITY,
|
||||||
country_code: "fr",
|
country_code: "fr",
|
||||||
|
province_code: "test",
|
||||||
city: "lyon",
|
city: "lyon",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -400,6 +401,91 @@ moduleIntegrationTestRunner({
|
|||||||
expect(err).toBeDefined()
|
expect(err).toBeDefined()
|
||||||
expect(err.constraint).toBe("IDX_fulfillment_set_name_unique")
|
expect(err.constraint).toBe("IDX_fulfillment_set_name_unique")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should fail on creating a new fulfillment set with new service zones and new geo zones that are not valid", async function () {
|
||||||
|
let data: CreateFulfillmentSetDTO = {
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.PROVINCE,
|
||||||
|
country_code: "fr",
|
||||||
|
} as any,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
let err = await service.create(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(
|
||||||
|
"Missing required property province_code for geo zone type province"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.CITY,
|
||||||
|
country_code: "fr",
|
||||||
|
province_code: "test",
|
||||||
|
} as any,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
err = await service.create(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(
|
||||||
|
"Missing required property city for geo zone type city"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.ZIP,
|
||||||
|
postal_expression: "test",
|
||||||
|
} as any,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
err = await service.create(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(
|
||||||
|
"Missing required property country_code for geo zone type zip"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: "unknown",
|
||||||
|
postal_expression: "test",
|
||||||
|
} as any,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
err = await service.create(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(`Invalid geo zone type: unknown`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("on update", () => {
|
describe("on update", () => {
|
||||||
|
|||||||
+102
-48
@@ -1,11 +1,11 @@
|
|||||||
import {Modules} from "@medusajs/modules-sdk"
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
import {
|
import {
|
||||||
CreateGeoZoneDTO,
|
CreateGeoZoneDTO,
|
||||||
IFulfillmentModuleService,
|
IFulfillmentModuleService,
|
||||||
UpdateGeoZoneDTO,
|
UpdateGeoZoneDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {GeoZoneType} from "@medusajs/utils"
|
import { GeoZoneType } from "@medusajs/utils"
|
||||||
import {moduleIntegrationTestRunner, SuiteOptions} from "medusa-test-utils"
|
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||||
|
|
||||||
jest.setTimeout(100000)
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
@@ -15,52 +15,52 @@ moduleIntegrationTestRunner({
|
|||||||
describe("Fulfillment Module Service", () => {
|
describe("Fulfillment Module Service", () => {
|
||||||
describe("read", () => {
|
describe("read", () => {
|
||||||
it("should list geo zones with a filter", async function () {
|
it("should list geo zones with a filter", async function () {
|
||||||
const fulfillmentSet = await service.create({
|
const fulfillmentSet = await service.create({
|
||||||
name: "test",
|
name: "test",
|
||||||
type: "test-type",
|
type: "test-type",
|
||||||
})
|
|
||||||
const serviceZone = await service.createServiceZones({
|
|
||||||
name: "test",
|
|
||||||
fulfillment_set_id: fulfillmentSet.id,
|
|
||||||
})
|
|
||||||
|
|
||||||
const createdZone1 = await service.createGeoZones({
|
|
||||||
service_zone_id: serviceZone.id,
|
|
||||||
type: GeoZoneType.COUNTRY,
|
|
||||||
country_code: "fr",
|
|
||||||
})
|
|
||||||
const createdZone2 = await service.createGeoZones({
|
|
||||||
service_zone_id: serviceZone.id,
|
|
||||||
type: GeoZoneType.COUNTRY,
|
|
||||||
country_code: "us",
|
|
||||||
})
|
|
||||||
|
|
||||||
let listedZones = await service.listGeoZones({
|
|
||||||
type: createdZone1.type,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(listedZones).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdZone1.id }),
|
|
||||||
expect.objectContaining({ id: createdZone2.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
|
|
||||||
listedZones = await service.listGeoZones({
|
|
||||||
country_code: createdZone2.country_code,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(listedZones).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdZone2.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
expect(listedZones).not.toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdZone1.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const createdZone1 = await service.createGeoZones({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
})
|
||||||
|
const createdZone2 = await service.createGeoZones({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "us",
|
||||||
|
})
|
||||||
|
|
||||||
|
let listedZones = await service.listGeoZones({
|
||||||
|
type: createdZone1.type,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(listedZones).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdZone1.id }),
|
||||||
|
expect.objectContaining({ id: createdZone2.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
listedZones = await service.listGeoZones({
|
||||||
|
country_code: createdZone2.country_code,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(listedZones).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdZone2.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
expect(listedZones).not.toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdZone1.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("mutations", () => {
|
describe("mutations", () => {
|
||||||
@@ -131,6 +131,60 @@ moduleIntegrationTestRunner({
|
|||||||
++i
|
++i
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should fail to create new geo zones that are not valid", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
let data: CreateGeoZoneDTO = {
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: GeoZoneType.PROVINCE,
|
||||||
|
country_code: "fr",
|
||||||
|
} as any
|
||||||
|
|
||||||
|
let err = await service.createGeoZones(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(
|
||||||
|
"Missing required property province_code for geo zone type province"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: GeoZoneType.CITY,
|
||||||
|
country_code: "fr",
|
||||||
|
province_code: "test",
|
||||||
|
} as any
|
||||||
|
|
||||||
|
err = await service.createGeoZones(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(
|
||||||
|
"Missing required property city for geo zone type city"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: GeoZoneType.ZIP,
|
||||||
|
postal_expression: "test",
|
||||||
|
} as any
|
||||||
|
|
||||||
|
err = await service.createGeoZones(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(
|
||||||
|
"Missing required property country_code for geo zone type zip"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: "unknown",
|
||||||
|
postal_expression: "test",
|
||||||
|
} as any
|
||||||
|
|
||||||
|
err = await service.createGeoZones(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(`Invalid geo zone type: unknown`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("on update", () => {
|
describe("on update", () => {
|
||||||
|
|||||||
+119
-49
@@ -16,56 +16,56 @@ moduleIntegrationTestRunner({
|
|||||||
describe("Fulfillment Module Service", () => {
|
describe("Fulfillment Module Service", () => {
|
||||||
describe("read", () => {
|
describe("read", () => {
|
||||||
it("should list service zones with a filter", async function () {
|
it("should list service zones with a filter", async function () {
|
||||||
const fulfillmentSet = await service.create({
|
const fulfillmentSet = await service.create({
|
||||||
name: "test",
|
name: "test",
|
||||||
type: "test-type",
|
type: "test-type",
|
||||||
})
|
|
||||||
|
|
||||||
const createdZone1 = await service.createServiceZones({
|
|
||||||
name: "test",
|
|
||||||
fulfillment_set_id: fulfillmentSet.id,
|
|
||||||
})
|
|
||||||
const createdZone2 = await service.createServiceZones({
|
|
||||||
name: "test2",
|
|
||||||
fulfillment_set_id: fulfillmentSet.id,
|
|
||||||
geo_zones: [
|
|
||||||
{
|
|
||||||
type: GeoZoneType.COUNTRY,
|
|
||||||
country_code: "fr",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
|
|
||||||
let listedZones = await service.listServiceZones({
|
|
||||||
name: createdZone2.name,
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(listedZones).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdZone2.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
expect(listedZones).not.toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdZone1.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
|
|
||||||
listedZones = await service.listServiceZones({
|
|
||||||
geo_zones: { country_code: "fr" },
|
|
||||||
})
|
|
||||||
|
|
||||||
expect(listedZones).toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdZone2.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
expect(listedZones).not.toEqual(
|
|
||||||
expect.arrayContaining([
|
|
||||||
expect.objectContaining({ id: createdZone1.id }),
|
|
||||||
])
|
|
||||||
)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const createdZone1 = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
const createdZone2 = await service.createServiceZones({
|
||||||
|
name: "test2",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
let listedZones = await service.listServiceZones({
|
||||||
|
name: createdZone2.name,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(listedZones).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdZone2.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
expect(listedZones).not.toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdZone1.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
listedZones = await service.listServiceZones({
|
||||||
|
geo_zones: { country_code: "fr" },
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(listedZones).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdZone2.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
expect(listedZones).not.toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: createdZone1.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("mutations", () => {
|
describe("mutations", () => {
|
||||||
@@ -189,6 +189,76 @@ moduleIntegrationTestRunner({
|
|||||||
expect(err).toBeDefined()
|
expect(err).toBeDefined()
|
||||||
expect(err.constraint).toBe("IDX_service_zone_name_unique")
|
expect(err.constraint).toBe("IDX_service_zone_name_unique")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it("should fail on creating a service zone and new geo zones that are not valid", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
|
||||||
|
let data: CreateServiceZoneDTO = {
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.PROVINCE,
|
||||||
|
country_code: "fr",
|
||||||
|
} as any,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
let err = await service.createServiceZones(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(
|
||||||
|
"Missing required property province_code for geo zone type province"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.CITY,
|
||||||
|
country_code: "fr",
|
||||||
|
province_code: "test",
|
||||||
|
} as any,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
err = await service.createServiceZones(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(
|
||||||
|
"Missing required property city for geo zone type city"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.ZIP,
|
||||||
|
postal_expression: "test",
|
||||||
|
} as any,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
err = await service.createServiceZones(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(
|
||||||
|
"Missing required property country_code for geo zone type zip"
|
||||||
|
)
|
||||||
|
|
||||||
|
data = {
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: "unknown",
|
||||||
|
postal_expression: "test",
|
||||||
|
} as any,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
err = await service.createServiceZones(data).catch((e) => e)
|
||||||
|
expect(err.message).toBe(`Invalid geo zone type: unknown`)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("on update", () => {
|
describe("on update", () => {
|
||||||
|
|||||||
+216
-9
@@ -8,6 +8,7 @@ import { generateCreateShippingOptionsData } from "../../__fixtures__"
|
|||||||
import { resolve } from "path"
|
import { resolve } from "path"
|
||||||
import { FulfillmentProviderService } from "@services"
|
import { FulfillmentProviderService } from "@services"
|
||||||
import { FulfillmentProviderServiceFixtures } from "../../__fixtures__/providers"
|
import { FulfillmentProviderServiceFixtures } from "../../__fixtures__/providers"
|
||||||
|
import { GeoZoneType } from "@medusajs/utils"
|
||||||
|
|
||||||
jest.setTimeout(100000)
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
@@ -35,10 +36,7 @@ const providerId = FulfillmentProviderService.getRegistrationIdentifier(
|
|||||||
moduleIntegrationTestRunner({
|
moduleIntegrationTestRunner({
|
||||||
moduleName: Modules.FULFILLMENT,
|
moduleName: Modules.FULFILLMENT,
|
||||||
moduleOptions,
|
moduleOptions,
|
||||||
testSuite: ({
|
testSuite: ({ service }: SuiteOptions<IFulfillmentModuleService>) => {
|
||||||
MikroOrmWrapper,
|
|
||||||
service,
|
|
||||||
}: SuiteOptions<IFulfillmentModuleService>) => {
|
|
||||||
describe("Fulfillment Module Service", () => {
|
describe("Fulfillment Module Service", () => {
|
||||||
describe("read", () => {
|
describe("read", () => {
|
||||||
it("should list shipping options with a filter", async function () {
|
it("should list shipping options with a filter", async function () {
|
||||||
@@ -158,7 +156,7 @@ moduleIntegrationTestRunner({
|
|||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
|
|
||||||
let listedOptions = await service.listShippingOptions({
|
let listedOptions = await service.listShippingOptionsForContext({
|
||||||
context: {
|
context: {
|
||||||
"test-attribute": "test",
|
"test-attribute": "test",
|
||||||
"test-attribute2": {
|
"test-attribute2": {
|
||||||
@@ -175,8 +173,12 @@ moduleIntegrationTestRunner({
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
|
|
||||||
listedOptions = await service.listShippingOptions({
|
listedOptions = await service.listShippingOptionsForContext({
|
||||||
fulfillment_set_id: { $ne: fulfillmentSet.id },
|
service_zone: {
|
||||||
|
fulfillment_set: {
|
||||||
|
id: { $ne: fulfillmentSet.id },
|
||||||
|
},
|
||||||
|
},
|
||||||
context: {
|
context: {
|
||||||
"test-attribute": "test",
|
"test-attribute": "test",
|
||||||
"test-attribute2": {
|
"test-attribute2": {
|
||||||
@@ -187,8 +189,12 @@ moduleIntegrationTestRunner({
|
|||||||
|
|
||||||
expect(listedOptions).toHaveLength(0)
|
expect(listedOptions).toHaveLength(0)
|
||||||
|
|
||||||
listedOptions = await service.listShippingOptions({
|
listedOptions = await service.listShippingOptionsForContext({
|
||||||
fulfillment_set_type: "non-existing-type",
|
service_zone: {
|
||||||
|
fulfillment_set: {
|
||||||
|
type: "non-existing-type",
|
||||||
|
},
|
||||||
|
},
|
||||||
context: {
|
context: {
|
||||||
"test-attribute": "test",
|
"test-attribute": "test",
|
||||||
"test-attribute2": {
|
"test-attribute2": {
|
||||||
@@ -199,6 +205,207 @@ moduleIntegrationTestRunner({
|
|||||||
|
|
||||||
expect(listedOptions).toHaveLength(0)
|
expect(listedOptions).toHaveLength(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it(`should list the shipping options for a context with a specific address`, async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.ZIP,
|
||||||
|
country_code: "fr",
|
||||||
|
province_code: "rhone",
|
||||||
|
city: "paris",
|
||||||
|
postal_expression: "75006",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const [shippingOption1, , shippingOption3] =
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test-attribute",
|
||||||
|
operator: "in",
|
||||||
|
value: ["test"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test-attribute",
|
||||||
|
operator: "in",
|
||||||
|
value: ["test-test"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test-attribute",
|
||||||
|
operator: "eq",
|
||||||
|
value: "test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute: "test-attribute2.options",
|
||||||
|
operator: "in",
|
||||||
|
value: ["test", "test2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
let shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "fr",
|
||||||
|
province_code: "rhone",
|
||||||
|
city: "paris",
|
||||||
|
postal_expression: "75006",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(shippingOptions).toHaveLength(3)
|
||||||
|
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "fr",
|
||||||
|
province_code: "rhone",
|
||||||
|
city: "paris",
|
||||||
|
postal_expression: "75001",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(shippingOptions).toHaveLength(0)
|
||||||
|
|
||||||
|
shippingOptions = await service.listShippingOptionsForContext({
|
||||||
|
address: {
|
||||||
|
country_code: "fr",
|
||||||
|
province_code: "rhone",
|
||||||
|
city: "paris",
|
||||||
|
postal_expression: "75006",
|
||||||
|
},
|
||||||
|
context: {
|
||||||
|
"test-attribute": "test",
|
||||||
|
"test-attribute2": {
|
||||||
|
options: "test2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(shippingOptions).toHaveLength(2)
|
||||||
|
expect(shippingOptions).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: shippingOption1.id }),
|
||||||
|
expect.objectContaining({ id: shippingOption3.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should validate if a shipping option is applicable to a context", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const [shippingOption1, shippingOption2, shippingOption3] =
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test-attribute",
|
||||||
|
operator: "in",
|
||||||
|
value: ["test"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test-attribute",
|
||||||
|
operator: "in",
|
||||||
|
value: ["test-test"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
provider_id: providerId,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test-attribute",
|
||||||
|
operator: "eq",
|
||||||
|
value: "test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute: "test-attribute2.options",
|
||||||
|
operator: "in",
|
||||||
|
value: ["test", "test2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
let listedOptions = await service.listShippingOptions()
|
||||||
|
|
||||||
|
expect(listedOptions).toHaveLength(3)
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
"test-attribute": "test",
|
||||||
|
"test-attribute2": {
|
||||||
|
options: "test2",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const isShippingOption1Applicable =
|
||||||
|
await service.validateShippingOption(shippingOption1.id, context)
|
||||||
|
expect(isShippingOption1Applicable).toBeTruthy()
|
||||||
|
|
||||||
|
const isShippingOption2Applicable =
|
||||||
|
await service.validateShippingOption(shippingOption2.id, context)
|
||||||
|
expect(isShippingOption2Applicable).toBeFalsy()
|
||||||
|
|
||||||
|
const isShippingOption3Applicable =
|
||||||
|
await service.validateShippingOption(shippingOption3.id, context)
|
||||||
|
expect(isShippingOption3Applicable).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("mutations", () => {
|
describe("mutations", () => {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
"watch:test": "tsc --build tsconfig.spec.json --watch",
|
"watch:test": "tsc --build tsconfig.spec.json --watch",
|
||||||
"prepublishOnly": "cross-env NODE_ENV=production tsc --build && tsc-alias -p tsconfig.json",
|
"prepublishOnly": "cross-env NODE_ENV=production tsc --build && tsc-alias -p tsconfig.json",
|
||||||
"build": "rimraf dist && tsc --build && tsc-alias -p tsconfig.json",
|
"build": "rimraf dist && tsc --build && tsc-alias -p tsconfig.json",
|
||||||
"test": "jest --runInBand --bail --forceExit -- src/**/__tests__/**/*.ts",
|
"test": "jest --runInBand --bail --forceExit --passWithNoTests -- src/**/__tests__/**/*.ts",
|
||||||
"test:integration": "jest --forceExit -- integration-tests/**/__tests__/**/*.spec.ts",
|
"test:integration": "jest --forceExit -- integration-tests/**/__tests__/**/*.spec.ts",
|
||||||
"migration:generate": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:generate",
|
"migration:generate": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:generate",
|
||||||
"migration:initial": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:create --initial -n InitialSetupMigration",
|
"migration:initial": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm migration:create --initial -n InitialSetupMigration",
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
describe("noop", function () {
|
|
||||||
it("should run", function () {
|
|
||||||
expect(true).toBe(true)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
@@ -2,8 +2,6 @@ import {
|
|||||||
Context,
|
Context,
|
||||||
DAL,
|
DAL,
|
||||||
FilterableFulfillmentSetProps,
|
FilterableFulfillmentSetProps,
|
||||||
FilterableShippingOptionProps,
|
|
||||||
FilterQuery,
|
|
||||||
FindConfig,
|
FindConfig,
|
||||||
FulfillmentDTO,
|
FulfillmentDTO,
|
||||||
FulfillmentTypes,
|
FulfillmentTypes,
|
||||||
@@ -131,69 +129,17 @@ export default class FulfillmentModuleService<
|
|||||||
return joinerConfig
|
return joinerConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static normalizeShippingOptionsListParams(
|
|
||||||
filters: FilterableShippingOptionProps = {},
|
|
||||||
config: FindConfig<ShippingOptionDTO> = {}
|
|
||||||
) {
|
|
||||||
let { fulfillment_set_id, fulfillment_set_type, context, ...where } =
|
|
||||||
filters
|
|
||||||
|
|
||||||
const normalizedConfig = { ...config }
|
|
||||||
normalizedConfig.relations = [
|
|
||||||
"rules",
|
|
||||||
"type",
|
|
||||||
"shipping_profile",
|
|
||||||
"provider",
|
|
||||||
...(normalizedConfig.relations ?? []),
|
|
||||||
]
|
|
||||||
// The assumption is that there won't be an infinite amount of shipping options. So if a context filtering needs to be applied we can retrieve them all.
|
|
||||||
normalizedConfig.take =
|
|
||||||
normalizedConfig.take ?? (context ? null : undefined)
|
|
||||||
|
|
||||||
let normalizedFilters = { ...where } as FilterQuery
|
|
||||||
|
|
||||||
if (fulfillment_set_id || fulfillment_set_type) {
|
|
||||||
const fulfillmentSetConstraints = {}
|
|
||||||
|
|
||||||
if (fulfillment_set_id) {
|
|
||||||
fulfillmentSetConstraints["id"] = fulfillment_set_id
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fulfillment_set_type) {
|
|
||||||
fulfillmentSetConstraints["type"] = fulfillment_set_type
|
|
||||||
}
|
|
||||||
|
|
||||||
normalizedFilters = {
|
|
||||||
...normalizedFilters,
|
|
||||||
service_zone: {
|
|
||||||
fulfillment_set: fulfillmentSetConstraints,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
normalizedConfig.relations.push("service_zone.fulfillment_set")
|
|
||||||
}
|
|
||||||
|
|
||||||
normalizedConfig.relations = Array.from(new Set(normalizedConfig.relations))
|
|
||||||
|
|
||||||
return {
|
|
||||||
filters: normalizedFilters,
|
|
||||||
config: normalizedConfig,
|
|
||||||
context,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
@InjectManager("baseRepository_")
|
||||||
// @ts-ignore
|
async listShippingOptionsForContext(
|
||||||
async listShippingOptions(
|
filters: FulfillmentTypes.FilterableShippingOptionForContextProps,
|
||||||
filters: FilterableShippingOptionProps = {},
|
|
||||||
config: FindConfig<ShippingOptionDTO> = {},
|
config: FindConfig<ShippingOptionDTO> = {},
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<FulfillmentTypes.ShippingOptionDTO[]> {
|
): Promise<FulfillmentTypes.ShippingOptionDTO[]> {
|
||||||
const {
|
const {
|
||||||
filters: normalizedFilters,
|
|
||||||
config: normalizedConfig,
|
|
||||||
context,
|
context,
|
||||||
} = FulfillmentModuleService.normalizeShippingOptionsListParams(
|
config: normalizedConfig,
|
||||||
|
filters: normalizedFilters,
|
||||||
|
} = FulfillmentModuleService.normalizeListShippingOptionsForContextParams(
|
||||||
filters,
|
filters,
|
||||||
config
|
config
|
||||||
)
|
)
|
||||||
@@ -204,7 +150,6 @@ export default class FulfillmentModuleService<
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
// Apply rules context filtering
|
|
||||||
if (context) {
|
if (context) {
|
||||||
shippingOptions = shippingOptions.filter((shippingOption) => {
|
shippingOptions = shippingOptions.filter((shippingOption) => {
|
||||||
if (!shippingOption.rules?.length) {
|
if (!shippingOption.rules?.length) {
|
||||||
@@ -245,14 +190,6 @@ export default class FulfillmentModuleService<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async retrieveFulfillmentOptions(
|
|
||||||
providerId: string
|
|
||||||
): Promise<Record<string, any>[]> {
|
|
||||||
return await this.fulfillmentProviderService_.getFulfillmentOptions(
|
|
||||||
providerId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@InjectManager("baseRepository_")
|
@InjectManager("baseRepository_")
|
||||||
async listFulfillments(
|
async listFulfillments(
|
||||||
filters: FulfillmentTypes.FilterableFulfillmentProps = {},
|
filters: FulfillmentTypes.FilterableFulfillmentProps = {},
|
||||||
@@ -331,6 +268,20 @@ export default class FulfillmentModuleService<
|
|||||||
): Promise<TEntity | TEntity[]> {
|
): Promise<TEntity | TEntity[]> {
|
||||||
const data_ = Array.isArray(data) ? data : [data]
|
const data_ = Array.isArray(data) ? data : [data]
|
||||||
|
|
||||||
|
if (!data_.length) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const fulfillmentSet of data_) {
|
||||||
|
if (fulfillmentSet.service_zones?.length) {
|
||||||
|
for (const serviceZone of fulfillmentSet.service_zones) {
|
||||||
|
if (serviceZone.geo_zones?.length) {
|
||||||
|
FulfillmentModuleService.validateGeoZones(serviceZone.geo_zones)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const createdFulfillmentSets = await this.fulfillmentSetService_.create(
|
const createdFulfillmentSets = await this.fulfillmentSetService_.create(
|
||||||
data_,
|
data_,
|
||||||
sharedContext
|
sharedContext
|
||||||
@@ -384,6 +335,14 @@ export default class FulfillmentModuleService<
|
|||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const serviceZone of data_) {
|
||||||
|
if (serviceZone.geo_zones?.length) {
|
||||||
|
if (serviceZone.geo_zones?.length) {
|
||||||
|
FulfillmentModuleService.validateGeoZones(serviceZone.geo_zones)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const createdServiceZones = await this.serviceZoneService_.create(
|
const createdServiceZones = await this.serviceZoneService_.create(
|
||||||
data_,
|
data_,
|
||||||
sharedContext
|
sharedContext
|
||||||
@@ -520,13 +479,17 @@ export default class FulfillmentModuleService<
|
|||||||
| FulfillmentTypes.CreateGeoZoneDTO[],
|
| FulfillmentTypes.CreateGeoZoneDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<FulfillmentTypes.GeoZoneDTO | FulfillmentTypes.GeoZoneDTO[]> {
|
): Promise<FulfillmentTypes.GeoZoneDTO | FulfillmentTypes.GeoZoneDTO[]> {
|
||||||
|
const data_ = Array.isArray(data) ? data : [data]
|
||||||
|
|
||||||
|
FulfillmentModuleService.validateGeoZones(data_)
|
||||||
|
|
||||||
const createdGeoZones = await this.geoZoneService_.create(
|
const createdGeoZones = await this.geoZoneService_.create(
|
||||||
data,
|
data_,
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
return await this.baseRepository_.serialize<FulfillmentTypes.GeoZoneDTO[]>(
|
return await this.baseRepository_.serialize<FulfillmentTypes.GeoZoneDTO[]>(
|
||||||
createdGeoZones,
|
Array.isArray(data) ? createdGeoZones : createdGeoZones[0],
|
||||||
{
|
{
|
||||||
populate: true,
|
populate: true,
|
||||||
}
|
}
|
||||||
@@ -777,6 +740,11 @@ export default class FulfillmentModuleService<
|
|||||||
fulfillmentSet.service_zones = fulfillmentSet.service_zones.map(
|
fulfillmentSet.service_zones = fulfillmentSet.service_zones.map(
|
||||||
(serviceZone) => {
|
(serviceZone) => {
|
||||||
if (!("id" in serviceZone)) {
|
if (!("id" in serviceZone)) {
|
||||||
|
if (serviceZone.geo_zones?.length) {
|
||||||
|
FulfillmentModuleService.validateGeoZones(
|
||||||
|
serviceZone.geo_zones
|
||||||
|
)
|
||||||
|
}
|
||||||
return serviceZone
|
return serviceZone
|
||||||
}
|
}
|
||||||
return serviceZonesMap.get(serviceZone.id)!
|
return serviceZonesMap.get(serviceZone.id)!
|
||||||
@@ -940,6 +908,7 @@ export default class FulfillmentModuleService<
|
|||||||
|
|
||||||
serviceZone.geo_zones = serviceZone.geo_zones.map((geoZone) => {
|
serviceZone.geo_zones = serviceZone.geo_zones.map((geoZone) => {
|
||||||
if (!("id" in geoZone)) {
|
if (!("id" in geoZone)) {
|
||||||
|
FulfillmentModuleService.validateGeoZones([geoZone])
|
||||||
return geoZone
|
return geoZone
|
||||||
}
|
}
|
||||||
return geoZonesMap.get(geoZone.id)!
|
return geoZonesMap.get(geoZone.id)!
|
||||||
@@ -1138,6 +1107,14 @@ export default class FulfillmentModuleService<
|
|||||||
| FulfillmentTypes.UpdateGeoZoneDTO[],
|
| FulfillmentTypes.UpdateGeoZoneDTO[],
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<FulfillmentTypes.GeoZoneDTO | FulfillmentTypes.GeoZoneDTO[]> {
|
): Promise<FulfillmentTypes.GeoZoneDTO | FulfillmentTypes.GeoZoneDTO[]> {
|
||||||
|
const data_ = Array.isArray(data) ? data : [data]
|
||||||
|
|
||||||
|
if (!data_.length) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
FulfillmentModuleService.validateGeoZones(data_)
|
||||||
|
|
||||||
const updatedGeoZones = await this.geoZoneService_.update(
|
const updatedGeoZones = await this.geoZoneService_.update(
|
||||||
data,
|
data,
|
||||||
sharedContext
|
sharedContext
|
||||||
@@ -1271,6 +1248,41 @@ export default class FulfillmentModuleService<
|
|||||||
return Array.isArray(result) ? result[0] : result
|
return Array.isArray(result) ? result[0] : result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async retrieveFulfillmentOptions(
|
||||||
|
providerId: string
|
||||||
|
): Promise<Record<string, any>[]> {
|
||||||
|
return await this.fulfillmentProviderService_.getFulfillmentOptions(
|
||||||
|
providerId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateFulfillmentOption(
|
||||||
|
providerId: string,
|
||||||
|
data: Record<string, unknown>
|
||||||
|
): Promise<boolean> {
|
||||||
|
return await this.fulfillmentProviderService_.validateOption(
|
||||||
|
providerId,
|
||||||
|
data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async validateShippingOption(
|
||||||
|
shippingOptionId: string,
|
||||||
|
context: Record<string, unknown> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
) {
|
||||||
|
const shippingOptions = await this.listShippingOptionsForContext(
|
||||||
|
{ id: shippingOptionId, context },
|
||||||
|
{
|
||||||
|
relations: ["rules"],
|
||||||
|
},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return !!shippingOptions.length
|
||||||
|
}
|
||||||
|
|
||||||
protected static canCancelFulfillmentOrThrow(fulfillment: Fulfillment) {
|
protected static canCancelFulfillmentOrThrow(fulfillment: Fulfillment) {
|
||||||
if (fulfillment.shipped_at) {
|
if (fulfillment.shipped_at) {
|
||||||
throw new MedusaError(
|
throw new MedusaError(
|
||||||
@@ -1336,4 +1348,213 @@ export default class FulfillmentModuleService<
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected static validateGeoZones(
|
||||||
|
geoZones: (
|
||||||
|
| (Partial<FulfillmentTypes.CreateGeoZoneDTO> & { type: string })
|
||||||
|
| (Partial<FulfillmentTypes.UpdateGeoZoneDTO> & { type: string })
|
||||||
|
)[]
|
||||||
|
) {
|
||||||
|
const requirePropForType = {
|
||||||
|
country: ["country_code"],
|
||||||
|
province: ["country_code", "province_code"],
|
||||||
|
city: ["country_code", "province_code", "city"],
|
||||||
|
zip: ["country_code", "province_code", "city", "postal_expression"],
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const geoZone of geoZones) {
|
||||||
|
if (!requirePropForType[geoZone.type]) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Invalid geo zone type: ${geoZone.type}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const prop of requirePropForType[geoZone.type]) {
|
||||||
|
if (!geoZone[prop]) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Missing required property ${prop} for geo zone type ${geoZone.type}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static normalizeListShippingOptionsForContextParams(
|
||||||
|
filters: FulfillmentTypes.FilterableShippingOptionForContextProps,
|
||||||
|
config: FindConfig<ShippingOptionDTO> = {}
|
||||||
|
) {
|
||||||
|
let {
|
||||||
|
fulfillment_set_id,
|
||||||
|
fulfillment_set_type,
|
||||||
|
address,
|
||||||
|
context,
|
||||||
|
...where
|
||||||
|
} = filters
|
||||||
|
|
||||||
|
const normalizedConfig = { ...config }
|
||||||
|
normalizedConfig.relations = [
|
||||||
|
"rules",
|
||||||
|
"type",
|
||||||
|
"shipping_profile",
|
||||||
|
"provider",
|
||||||
|
...(normalizedConfig.relations ?? []),
|
||||||
|
]
|
||||||
|
|
||||||
|
normalizedConfig.take =
|
||||||
|
normalizedConfig.take ?? (context ? null : undefined)
|
||||||
|
|
||||||
|
let normalizedFilters = { ...where }
|
||||||
|
|
||||||
|
if (fulfillment_set_id || fulfillment_set_type) {
|
||||||
|
const fulfillmentSetConstraints = {}
|
||||||
|
|
||||||
|
if (fulfillment_set_id) {
|
||||||
|
fulfillmentSetConstraints["id"] = fulfillment_set_id
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fulfillment_set_type) {
|
||||||
|
fulfillmentSetConstraints["type"] = fulfillment_set_type
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedFilters = {
|
||||||
|
...normalizedFilters,
|
||||||
|
service_zone: {
|
||||||
|
...(normalizedFilters.service_zone ?? {}),
|
||||||
|
fulfillment_set: {
|
||||||
|
...(normalizedFilters.service_zone?.fulfillment_set ?? {}),
|
||||||
|
...fulfillmentSetConstraints,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedConfig.relations.push("service_zone.fulfillment_set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (address) {
|
||||||
|
const geoZoneConstraints =
|
||||||
|
FulfillmentModuleService.buildGeoZoneConstraintsFromAddress(address)
|
||||||
|
|
||||||
|
normalizedFilters = {
|
||||||
|
...normalizedFilters,
|
||||||
|
service_zone: {
|
||||||
|
...(normalizedFilters.service_zone ?? {}),
|
||||||
|
geo_zones: {
|
||||||
|
$or: geoZoneConstraints.map((geoZoneConstraint) => ({
|
||||||
|
// Apply eventually provided constraints on the geo zone along side the address constraints
|
||||||
|
...(normalizedFilters.service_zone?.geo_zones ?? {}),
|
||||||
|
...geoZoneConstraint,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedConfig.relations.push("service_zone.geo_zones")
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedConfig.relations = Array.from(new Set(normalizedConfig.relations))
|
||||||
|
|
||||||
|
return {
|
||||||
|
filters: normalizedFilters,
|
||||||
|
config: normalizedConfig,
|
||||||
|
context,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the constraints for the geo zones based on the address properties
|
||||||
|
* available and the hierarchy of required properties.
|
||||||
|
* We build a OR constraint from the narrowest to the broadest
|
||||||
|
* e.g. if we have a postal expression we build a constraint for the postal expression require props of type zip
|
||||||
|
* and a constraint for the city required props of type city
|
||||||
|
* and a constraint for the province code required props of type province
|
||||||
|
* and a constraint for the country code required props of type country
|
||||||
|
* example:
|
||||||
|
* {
|
||||||
|
* $or: [
|
||||||
|
* {
|
||||||
|
* type: "zip",
|
||||||
|
* country_code: "SE",
|
||||||
|
* province_code: "AB",
|
||||||
|
* city: "Stockholm",
|
||||||
|
* postal_expression: "12345"
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* type: "city",
|
||||||
|
* country_code: "SE",
|
||||||
|
* province_code: "AB",
|
||||||
|
* city: "Stockholm"
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* type: "province",
|
||||||
|
* country_code: "SE",
|
||||||
|
* province_code: "AB"
|
||||||
|
* },
|
||||||
|
* {
|
||||||
|
* type: "country",
|
||||||
|
* country_code: "SE"
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
private static buildGeoZoneConstraintsFromAddress(
|
||||||
|
address: FulfillmentTypes.FilterableShippingOptionForContextProps["address"]
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* Define the hierarchy of required properties for the geo zones.
|
||||||
|
*/
|
||||||
|
const geoZoneRequirePropertyHierarchy = {
|
||||||
|
postal_expression: [
|
||||||
|
"country_code",
|
||||||
|
"province_code",
|
||||||
|
"city",
|
||||||
|
"postal_expression",
|
||||||
|
],
|
||||||
|
city: ["country_code", "province_code", "city"],
|
||||||
|
province_code: ["country_code", "province_code"],
|
||||||
|
country_code: ["country_code"],
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that the address has the required properties for the geo zones
|
||||||
|
* constraints to build after. We are going from the narrowest to the broadest
|
||||||
|
*/
|
||||||
|
Object.entries(geoZoneRequirePropertyHierarchy).forEach(
|
||||||
|
([prop, requiredProps]) => {
|
||||||
|
if (address![prop]) {
|
||||||
|
for (const requiredProp of requiredProps) {
|
||||||
|
if (!address![requiredProp]) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Missing required property ${requiredProp} for address when property ${prop} is set`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const geoZoneConstraints = Object.entries(geoZoneRequirePropertyHierarchy)
|
||||||
|
.map(([prop, requiredProps]) => {
|
||||||
|
if (address![prop]) {
|
||||||
|
return requiredProps.reduce((geoZoneConstraint, prop) => {
|
||||||
|
geoZoneConstraint.type =
|
||||||
|
prop === "postal_expression"
|
||||||
|
? "zip"
|
||||||
|
: prop === "city"
|
||||||
|
? "city"
|
||||||
|
: prop === "province_code"
|
||||||
|
? "province"
|
||||||
|
: "country"
|
||||||
|
geoZoneConstraint[prop] = address![prop]
|
||||||
|
return geoZoneConstraint
|
||||||
|
}, {} as Record<string, string | undefined>)
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
.filter((v): v is Record<string, any> => !!v)
|
||||||
|
|
||||||
|
return geoZoneConstraints
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -78,8 +78,8 @@ export default class FulfillmentProviderService extends ModulesSdkUtils.internal
|
|||||||
return await provider.validateFulfillmentData(optionData, data, context)
|
return await provider.validateFulfillmentData(optionData, data, context)
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateOption(data: any) {
|
async validateOption(providerId: string, data: Record<string, unknown>) {
|
||||||
const provider = this.retrieveProviderRegistration(data.provider_id)
|
const provider = this.retrieveProviderRegistration(providerId)
|
||||||
return await provider.validateOption(data)
|
return await provider.validateOption(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ export function isContextValid(
|
|||||||
} = {
|
} = {
|
||||||
someAreValid: false,
|
someAreValid: false,
|
||||||
}
|
}
|
||||||
) {
|
): boolean {
|
||||||
const { someAreValid } = options
|
const { someAreValid } = options
|
||||||
|
|
||||||
const loopComparator = someAreValid ? rules.some : rules.every
|
const loopComparator = someAreValid ? rules.some : rules.every
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { FulfillmentSetDTO } from "./fulfillment-set"
|
import {
|
||||||
|
FilterableFulfillmentSetProps,
|
||||||
|
FulfillmentSetDTO,
|
||||||
|
} from "./fulfillment-set"
|
||||||
import { FilterableGeoZoneProps, GeoZoneDTO } from "./geo-zone"
|
import { FilterableGeoZoneProps, GeoZoneDTO } from "./geo-zone"
|
||||||
import { ShippingOptionDTO } from "./shipping-option"
|
import { ShippingOptionDTO } from "./shipping-option"
|
||||||
import { BaseFilterable, OperatorMap } from "../../dal"
|
import { BaseFilterable, OperatorMap } from "../../dal"
|
||||||
@@ -20,5 +23,6 @@ export interface FilterableServiceZoneProps
|
|||||||
id?: string | string[] | OperatorMap<string | string[]>
|
id?: string | string[] | OperatorMap<string | string[]>
|
||||||
name?: string | string[] | OperatorMap<string | string[]>
|
name?: string | string[] | OperatorMap<string | string[]>
|
||||||
geo_zones?: FilterableGeoZoneProps
|
geo_zones?: FilterableGeoZoneProps
|
||||||
|
fulfillment_set?: FilterableFulfillmentSetProps
|
||||||
shipping_options?: any // TODO
|
shipping_options?: any // TODO
|
||||||
}
|
}
|
||||||
@@ -39,9 +39,7 @@ export interface FilterableShippingOptionProps
|
|||||||
extends BaseFilterable<FilterableShippingOptionProps> {
|
extends BaseFilterable<FilterableShippingOptionProps> {
|
||||||
id?: string | string[] | OperatorMap<string | string[]>
|
id?: string | string[] | OperatorMap<string | string[]>
|
||||||
name?: string | string[] | OperatorMap<string | string[]>
|
name?: string | string[] | OperatorMap<string | string[]>
|
||||||
fulfillment_set_id?: string | string[] | OperatorMap<string | string[]>
|
|
||||||
shipping_profile_id?: string | string[] | OperatorMap<string | string[]>
|
shipping_profile_id?: string | string[] | OperatorMap<string | string[]>
|
||||||
fulfillment_set_type?: string | string[] | OperatorMap<string | string[]>
|
|
||||||
price_type?:
|
price_type?:
|
||||||
| ShippingOptionPriceType
|
| ShippingOptionPriceType
|
||||||
| ShippingOptionPriceType[]
|
| ShippingOptionPriceType[]
|
||||||
@@ -49,5 +47,22 @@ export interface FilterableShippingOptionProps
|
|||||||
service_zone?: FilterableServiceZoneProps
|
service_zone?: FilterableServiceZoneProps
|
||||||
shipping_option_type?: FilterableShippingOptionTypeProps
|
shipping_option_type?: FilterableShippingOptionTypeProps
|
||||||
rules?: FilterableShippingOptionRuleProps
|
rules?: FilterableShippingOptionRuleProps
|
||||||
context?: Record<string, unknown>
|
}
|
||||||
|
|
||||||
|
export interface FilterableShippingOptionForContextProps
|
||||||
|
extends FilterableShippingOptionProps {
|
||||||
|
fulfillment_set_id?: string | string[] | OperatorMap<string | string[]>
|
||||||
|
fulfillment_set_type?: string | string[] | OperatorMap<string | string[]>
|
||||||
|
/**
|
||||||
|
* The address is a shortcut to filter through geo_zones
|
||||||
|
* and build opinionated validation and filtering around the geo_zones.
|
||||||
|
* For custom filtering you can go through the service_zone.geo_zones directly.
|
||||||
|
*/
|
||||||
|
address?: {
|
||||||
|
country_code?: string
|
||||||
|
province_code?: string
|
||||||
|
city?: string
|
||||||
|
postal_expression?: string
|
||||||
|
}
|
||||||
|
context?: Record<string, any>
|
||||||
}
|
}
|
||||||
@@ -18,11 +18,14 @@ export interface CreateProvinceGeoZoneDTO extends CreateGeoZoneBaseDTO {
|
|||||||
|
|
||||||
export interface CreateCityGeoZoneDTO extends CreateGeoZoneBaseDTO {
|
export interface CreateCityGeoZoneDTO extends CreateGeoZoneBaseDTO {
|
||||||
type: "city"
|
type: "city"
|
||||||
|
province_code: string
|
||||||
city: string
|
city: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreateZipGeoZoneDTO extends CreateGeoZoneBaseDTO {
|
export interface CreateZipGeoZoneDTO extends CreateGeoZoneBaseDTO {
|
||||||
type: "zip"
|
type: "zip"
|
||||||
|
province_code: string
|
||||||
|
city: string
|
||||||
postal_expression: Record<string, any>
|
postal_expression: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,12 +50,15 @@ export interface UpdateProvinceGeoZoneDTO extends UpdateGeoZoneBaseDTO {
|
|||||||
|
|
||||||
export interface UpdateCityGeoZoneDTO extends UpdateGeoZoneBaseDTO {
|
export interface UpdateCityGeoZoneDTO extends UpdateGeoZoneBaseDTO {
|
||||||
type: "city"
|
type: "city"
|
||||||
city: string
|
province_code?: string
|
||||||
|
city?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateZipGeoZoneDTO extends UpdateGeoZoneBaseDTO {
|
export interface UpdateZipGeoZoneDTO extends UpdateGeoZoneBaseDTO {
|
||||||
type: "zip"
|
type: "zip"
|
||||||
postal_expression: Record<string, any>
|
province_code?: string
|
||||||
|
city?: string
|
||||||
|
postal_expression?: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UpdateGeoZoneDTO =
|
export type UpdateGeoZoneDTO =
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
FilterableFulfillmentSetProps,
|
FilterableFulfillmentSetProps,
|
||||||
FilterableGeoZoneProps,
|
FilterableGeoZoneProps,
|
||||||
FilterableServiceZoneProps,
|
FilterableServiceZoneProps,
|
||||||
|
FilterableShippingOptionForContextProps,
|
||||||
FilterableShippingOptionProps,
|
FilterableShippingOptionProps,
|
||||||
FilterableShippingOptionRuleProps,
|
FilterableShippingOptionRuleProps,
|
||||||
FilterableShippingOptionTypeProps,
|
FilterableShippingOptionTypeProps,
|
||||||
@@ -326,13 +327,24 @@ export interface IFulfillmentModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<ShippingOptionDTO[]>
|
): Promise<ShippingOptionDTO[]>
|
||||||
/**
|
/**
|
||||||
* List and count shipping options
|
* List shipping options and eventually filter the result based on the context and their rules
|
||||||
|
* @param filters
|
||||||
|
* @param config
|
||||||
|
* @param sharedContext
|
||||||
|
*/
|
||||||
|
listShippingOptionsForContext(
|
||||||
|
filters: FilterableShippingOptionForContextProps,
|
||||||
|
config?: FindConfig<ShippingOptionDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<ShippingOptionDTO[]>
|
||||||
|
/**
|
||||||
|
* List and count shipping options without taking into account the context
|
||||||
* @param filters
|
* @param filters
|
||||||
* @param config
|
* @param config
|
||||||
* @param sharedContext
|
* @param sharedContext
|
||||||
*/
|
*/
|
||||||
listAndCountShippingOptions(
|
listAndCountShippingOptions(
|
||||||
filters?: FilterableShippingOptionProps,
|
filters?: Omit<FilterableShippingOptionProps, "context">,
|
||||||
config?: FindConfig<ShippingOptionDTO>,
|
config?: FindConfig<ShippingOptionDTO>,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<[ShippingOptionDTO[], number]>
|
): Promise<[ShippingOptionDTO[], number]>
|
||||||
@@ -664,4 +676,20 @@ export interface IFulfillmentModuleService extends IModuleService {
|
|||||||
retrieveFulfillmentOptions(
|
retrieveFulfillmentOptions(
|
||||||
providerId: string
|
providerId: string
|
||||||
): Promise<Record<string, unknown>[]>
|
): Promise<Record<string, unknown>[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate the given shipping option fulfillment option from the provided data
|
||||||
|
*/
|
||||||
|
validateFulfillmentOption(
|
||||||
|
providerId: string,
|
||||||
|
data: Record<string, unknown>
|
||||||
|
): Promise<boolean>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate if the given shipping option is valid for a given context
|
||||||
|
*/
|
||||||
|
validateShippingOption(
|
||||||
|
shippingOptionId: string,
|
||||||
|
context: Record<string, unknown>
|
||||||
|
): Promise<boolean>
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user