Feat(fulfillment): service provider registration + fulfillment management (#6524)
**What** - Create the fulfillment manual package with a first iteration API - Create a new `AbstractFulfillmentProviderService` and `IFulfillmentProvider` - Modify the module service interface to add new methods to manipulate the fulfillment and the communication with the external provider - create (no bulk) - cancel (no bulk) - update (no bulk) - list - listAndCount - retrieve - Add new methods to the service provider service to include communication with the third party provider - get options - create - cancel - validate data - validate option - Update/create interfaces and DTO's - fix repository serializer to allow non entity to be passed without throwing - split module tests into multiple files to simplify navigation - Add integration tests to validate fulfillments manipulation and external provider loading + communication FIXES CORE-1729 FIXES CORE-1785 FIXES CORE-1784 FIXES CORE-1766
This commit is contained in:
1 parent
f9ef37a2f2
commit
62a7bcc30c
55 files changed
+4066
-2749
No files matched your search
@@ -0,0 +1,8 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/types": patch
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
"medusa-test-utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Feat(fulfillment): service provider registration + fulfillment management
|
||||||
|
|
||||||
@@ -31,6 +31,7 @@ packages/*
|
|||||||
!packages/workflow-engine-redis
|
!packages/workflow-engine-redis
|
||||||
!packages/workflow-engine-inmemory
|
!packages/workflow-engine-inmemory
|
||||||
!packages/fulfillment
|
!packages/fulfillment
|
||||||
|
!packages/fulfillment-manual
|
||||||
|
|
||||||
|
|
||||||
**/models/*
|
**/models/*
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ module.exports = {
|
|||||||
"./packages/workflow-engine-redis/tsconfig.spec.json",
|
"./packages/workflow-engine-redis/tsconfig.spec.json",
|
||||||
"./packages/workflow-engine-inmemory/tsconfig.spec.json",
|
"./packages/workflow-engine-inmemory/tsconfig.spec.json",
|
||||||
"./packages/fulfillment/tsconfig.spec.json",
|
"./packages/fulfillment/tsconfig.spec.json",
|
||||||
|
"./packages/fulfillment-manual/tsconfig.spec.json",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
dist
|
||||||
|
node_modules
|
||||||
|
.DS_store
|
||||||
|
yarn.lock
|
||||||
Whitespace-only changes.
Whitespace-only changes.
@@ -0,0 +1,13 @@
|
|||||||
|
module.exports = {
|
||||||
|
globals: {
|
||||||
|
"ts-jest": {
|
||||||
|
tsconfig: "tsconfig.spec.json",
|
||||||
|
isolatedModules: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
transform: {
|
||||||
|
"^.+\\.[jt]s?$": "ts-jest",
|
||||||
|
},
|
||||||
|
testEnvironment: `node`,
|
||||||
|
moduleFileExtensions: [`js`, `jsx`, `ts`, `tsx`, `json`],
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"name": "@medusajs/fulfillment-manual",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Manual fulfillment for Medusa",
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/medusajs/medusa",
|
||||||
|
"directory": "packages/fulfillment-manual"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
},
|
||||||
|
"author": "Medusa",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"prepublishOnly": "cross-env NODE_ENV=production tsc --build",
|
||||||
|
"test": "jest --passWithNoTests src",
|
||||||
|
"build": "rimraf dist && tsc -p ./tsconfig.json",
|
||||||
|
"watch": "tsc --watch"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"cross-env": "^5.2.1",
|
||||||
|
"jest": "^25.5.4",
|
||||||
|
"rimraf": "^5.0.1",
|
||||||
|
"typescript": "^4.9.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@medusajs/utils": "^1.11.3",
|
||||||
|
"body-parser": "^1.19.0",
|
||||||
|
"express": "^4.17.1"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"medusa-plugin",
|
||||||
|
"medusa-plugin-fulfillment"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { ModuleProviderExports } from "@medusajs/types"
|
||||||
|
import { ManualFulfillmentService } from "./services/manual-fulfillment"
|
||||||
|
|
||||||
|
const services = [ManualFulfillmentService]
|
||||||
|
|
||||||
|
const providerExport: ModuleProviderExports = {
|
||||||
|
services,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default providerExport
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { AbstractFulfillmentProviderService } from "@medusajs/utils"
|
||||||
|
|
||||||
|
// TODO rework type and DTO's
|
||||||
|
|
||||||
|
export class ManualFulfillmentService extends AbstractFulfillmentProviderService {
|
||||||
|
static identifier = "manual"
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super()
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFulfillmentOptions(): Promise<Record<string, unknown>[]> {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "manual-fulfillment",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "manual-fulfillment-return",
|
||||||
|
is_return: true,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateFulfillmentData(
|
||||||
|
optionData: Record<string, unknown>,
|
||||||
|
data: Record<string, unknown>,
|
||||||
|
context: Record<string, unknown>
|
||||||
|
): Promise<any> {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateOption(data: Record<string, unknown>): Promise<boolean> {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
async createFulfillment(): Promise<Record<string, unknown>> {
|
||||||
|
// No data is being sent anywhere
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelFulfillment(fulfillment: Record<string, unknown>): Promise<any> {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"lib": [
|
||||||
|
"es5",
|
||||||
|
"es6",
|
||||||
|
"es2019"
|
||||||
|
],
|
||||||
|
"target": "es5",
|
||||||
|
"jsx": "react-jsx" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"declaration": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"noImplicitReturns": true,
|
||||||
|
"strictNullChecks": true,
|
||||||
|
"strictFunctionTypes": true,
|
||||||
|
"noImplicitThis": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"downlevelIteration": true, // to use ES5 specific tooling
|
||||||
|
"inlineSourceMap": true /* Emit a single file with source maps instead of having a separate file. */
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": [
|
||||||
|
"dist",
|
||||||
|
"build",
|
||||||
|
"src/**/__tests__",
|
||||||
|
"src/**/__mocks__",
|
||||||
|
"src/**/__fixtures__",
|
||||||
|
"node_modules",
|
||||||
|
".eslintrc.js"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"include": ["src"],
|
||||||
|
"exclude": ["node_modules"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import { CreateFulfillmentDTO } from "@medusajs/types"
|
||||||
|
|
||||||
|
export function generateCreateFulfillmentData(
|
||||||
|
data: Partial<CreateFulfillmentDTO> & {
|
||||||
|
provider_id: string
|
||||||
|
shipping_option_id: string
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return {
|
||||||
|
location_id: "test-location",
|
||||||
|
packed_at: null,
|
||||||
|
shipped_at: null,
|
||||||
|
delivered_at: null,
|
||||||
|
canceled_at: null,
|
||||||
|
data: null,
|
||||||
|
provider_id: data.provider_id,
|
||||||
|
shipping_option_id: data.shipping_option_id,
|
||||||
|
metadata: data.metadata ?? null,
|
||||||
|
delivery_address: data.delivery_address ?? {
|
||||||
|
address_1: "test-address",
|
||||||
|
address_2: "test-address",
|
||||||
|
city: "test-city",
|
||||||
|
postal_code: "test-postal-code",
|
||||||
|
country_code: "test-country-code",
|
||||||
|
province: "test-province",
|
||||||
|
phone: "test-phone",
|
||||||
|
full_name: "test-full-name",
|
||||||
|
},
|
||||||
|
items: data.items ?? [
|
||||||
|
{
|
||||||
|
title: "test-title",
|
||||||
|
sku: "test-sku",
|
||||||
|
quantity: 1,
|
||||||
|
barcode: "test-barcode",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
labels: data.labels ?? [
|
||||||
|
{
|
||||||
|
tracking_number: "test-tracking-number",
|
||||||
|
tracking_url: "test-tracking-url",
|
||||||
|
label_url: "test-label-url",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
order: data.order ?? {},
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,39 +1,2 @@
|
|||||||
import { CreateShippingOptionDTO } from "@medusajs/types"
|
export * from "./shipping-options"
|
||||||
|
export * from "./fulfillment"
|
||||||
export function generateCreateShippingOptionsData({
|
|
||||||
name,
|
|
||||||
service_zone_id,
|
|
||||||
shipping_profile_id,
|
|
||||||
service_provider_id,
|
|
||||||
price_type,
|
|
||||||
rules,
|
|
||||||
type,
|
|
||||||
data,
|
|
||||||
}: Omit<CreateShippingOptionDTO, "name" | "price_type" | "type"> & {
|
|
||||||
price_type?: CreateShippingOptionDTO["price_type"]
|
|
||||||
name?: string
|
|
||||||
type?: CreateShippingOptionDTO["type"]
|
|
||||||
}): Required<CreateShippingOptionDTO> {
|
|
||||||
return {
|
|
||||||
service_zone_id: service_zone_id,
|
|
||||||
shipping_profile_id: shipping_profile_id,
|
|
||||||
service_provider_id: service_provider_id,
|
|
||||||
type: type ?? {
|
|
||||||
code: "test-type",
|
|
||||||
description: "test-description",
|
|
||||||
label: "test-label",
|
|
||||||
},
|
|
||||||
data: data ?? {
|
|
||||||
amount: 1000,
|
|
||||||
},
|
|
||||||
name: name ?? Math.random().toString(36).substring(7),
|
|
||||||
price_type: price_type ?? "flat",
|
|
||||||
rules: rules ?? [
|
|
||||||
{
|
|
||||||
attribute: "weight",
|
|
||||||
operator: "eq",
|
|
||||||
value: "test",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { AbstractFulfillmentProviderService } from "@medusajs/utils/src"
|
||||||
|
|
||||||
|
export class FulfillmentProviderServiceFixtures extends AbstractFulfillmentProviderService {
|
||||||
|
static identifier = "fixtures-fulfillment-provider"
|
||||||
|
|
||||||
|
async createFulfillment(data, items, order, fulfillment): Promise<any> {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelFulfillment(fulfillment): Promise<any> {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFulfillmentOptions(): Promise<any> {
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const services = [FulfillmentProviderServiceFixtures]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export * from "./default-provider"
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { CreateShippingOptionDTO } from "@medusajs/types"
|
||||||
|
|
||||||
|
export function generateCreateShippingOptionsData({
|
||||||
|
name,
|
||||||
|
service_zone_id,
|
||||||
|
shipping_profile_id,
|
||||||
|
fulfillment_provider_id,
|
||||||
|
price_type,
|
||||||
|
rules,
|
||||||
|
type,
|
||||||
|
data,
|
||||||
|
}: Omit<CreateShippingOptionDTO, "name" | "price_type" | "type"> & {
|
||||||
|
price_type?: CreateShippingOptionDTO["price_type"]
|
||||||
|
name?: string
|
||||||
|
type?: CreateShippingOptionDTO["type"]
|
||||||
|
}): Required<CreateShippingOptionDTO> {
|
||||||
|
return {
|
||||||
|
service_zone_id: service_zone_id,
|
||||||
|
shipping_profile_id: shipping_profile_id,
|
||||||
|
fulfillment_provider_id: fulfillment_provider_id,
|
||||||
|
type: type ?? {
|
||||||
|
code: "test-type",
|
||||||
|
description: "test-description",
|
||||||
|
label: "test-label",
|
||||||
|
},
|
||||||
|
data: data ?? {
|
||||||
|
amount: 1000,
|
||||||
|
},
|
||||||
|
name: name ?? Math.random().toString(36).substring(7),
|
||||||
|
price_type: price_type ?? "flat",
|
||||||
|
rules: rules ?? [
|
||||||
|
{
|
||||||
|
attribute: "weight",
|
||||||
|
operator: "eq",
|
||||||
|
value: "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
}
|
||||||
File diff suppressed because it is too large.
Load diff
+865
@@ -0,0 +1,865 @@
|
|||||||
|
import {Modules} from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
CreateFulfillmentSetDTO,
|
||||||
|
CreateServiceZoneDTO,
|
||||||
|
IFulfillmentModuleService,
|
||||||
|
ServiceZoneDTO,
|
||||||
|
UpdateFulfillmentSetDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import {GeoZoneType} from "@medusajs/utils"
|
||||||
|
import {moduleIntegrationTestRunner, SuiteOptions} from "medusa-test-utils"
|
||||||
|
|
||||||
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
|
moduleIntegrationTestRunner({
|
||||||
|
moduleName: Modules.FULFILLMENT,
|
||||||
|
testSuite: ({ service }: SuiteOptions<IFulfillmentModuleService>) => {
|
||||||
|
describe("Fulfillment Module Service", () => {
|
||||||
|
describe("read", () => {
|
||||||
|
it("should list fulfillment sets with a filter", async function () {
|
||||||
|
const createdSet1 = await service.create({
|
||||||
|
name: "test",
|
||||||
|
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 }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("mutations", () => {
|
||||||
|
describe("on create", () => {
|
||||||
|
it("should create a new fulfillment set", async function () {
|
||||||
|
const data: CreateFulfillmentSetDTO = {
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
}
|
||||||
|
|
||||||
|
const fulfillmentSet = await service.create(data)
|
||||||
|
|
||||||
|
expect(fulfillmentSet).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: data.name,
|
||||||
|
type: data.type,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a collection of fulfillment sets", async function () {
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
type: "test-type2",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const fulfillmentSets = await service.create(data)
|
||||||
|
|
||||||
|
expect(fulfillmentSets).toHaveLength(2)
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
for (const data_ of data) {
|
||||||
|
expect(fulfillmentSets[i]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: data_.name,
|
||||||
|
type: data_.type,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a new fulfillment set with new service zones", async function () {
|
||||||
|
const data = {
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const fulfillmentSet = await service.create(data)
|
||||||
|
|
||||||
|
expect(fulfillmentSet).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: data.name,
|
||||||
|
type: data.type,
|
||||||
|
service_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: data.service_zones[0].name,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a collection of fulfillment sets with new service zones", async function () {
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
type: "test-type2",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test3",
|
||||||
|
type: "test-type3",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test3",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const fulfillmentSets = await service.create(data)
|
||||||
|
|
||||||
|
expect(fulfillmentSets).toHaveLength(3)
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
for (const data_ of data) {
|
||||||
|
expect(fulfillmentSets[i]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: data_.name,
|
||||||
|
type: data_.type,
|
||||||
|
service_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: data_.service_zones[0].name,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a new fulfillment set with new service zones and new geo zones", async function () {
|
||||||
|
const data: CreateFulfillmentSetDTO = {
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const fulfillmentSet = await service.create(data)
|
||||||
|
|
||||||
|
expect(fulfillmentSet).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: data.name,
|
||||||
|
type: data.type,
|
||||||
|
service_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: (data.service_zones![0] as any).name,
|
||||||
|
geo_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
type: (data.service_zones![0] as any).geo_zones[0].type,
|
||||||
|
country_code: (data.service_zones![0] as any)
|
||||||
|
.geo_zones[0].country_code,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a collection of fulfillment sets with new service zones and new geo zones", async function () {
|
||||||
|
const data: CreateFulfillmentSetDTO[] = [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
type: "test-type2",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test3",
|
||||||
|
type: "test-type3",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "test3",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.CITY,
|
||||||
|
country_code: "fr",
|
||||||
|
city: "lyon",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const fulfillmentSets = await service.create(data)
|
||||||
|
|
||||||
|
expect(fulfillmentSets).toHaveLength(3)
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
for (const data_ of data) {
|
||||||
|
expect(fulfillmentSets[i]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: data_.name,
|
||||||
|
type: data_.type,
|
||||||
|
service_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: (data_.service_zones![0] as any).name,
|
||||||
|
geo_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
type: (data_.service_zones![0] as any).geo_zones[0]
|
||||||
|
.type,
|
||||||
|
country_code: (data_.service_zones![0] as any)
|
||||||
|
.geo_zones[0].country_code,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`should fail on duplicated fulfillment set name`, async function () {
|
||||||
|
const data: CreateFulfillmentSetDTO = {
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
}
|
||||||
|
|
||||||
|
await service.create(data)
|
||||||
|
const err = await service.create(data).catch((e) => e)
|
||||||
|
|
||||||
|
expect(err).toBeDefined()
|
||||||
|
expect(err.constraint).toBe("IDX_fulfillment_set_name_unique")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("on update", () => {
|
||||||
|
it("should update an existing fulfillment set", async function () {
|
||||||
|
const createData: CreateFulfillmentSetDTO = {
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdFulfillmentSet = await service.create(createData)
|
||||||
|
|
||||||
|
const updateData = {
|
||||||
|
id: createdFulfillmentSet.id,
|
||||||
|
name: "updated-test",
|
||||||
|
type: "updated-test-type",
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedFulfillmentSets = await service.update(updateData)
|
||||||
|
|
||||||
|
expect(updatedFulfillmentSets).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: createdFulfillmentSet.id,
|
||||||
|
name: updateData.name,
|
||||||
|
type: updateData.type,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update a collection of fulfillment sets", async function () {
|
||||||
|
const createData = [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
type: "test-type2",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const createdFulfillmentSets = await service.create(createData)
|
||||||
|
|
||||||
|
const updateData = createdFulfillmentSets.map(
|
||||||
|
(fulfillmentSet, index) => ({
|
||||||
|
id: fulfillmentSet.id,
|
||||||
|
name: `updated-test${index + 1}`,
|
||||||
|
type: `updated-test-type${index + 1}`,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const updatedFulfillmentSets = await service.update(updateData)
|
||||||
|
const fullfillmentSets = await service.list({
|
||||||
|
id: updateData.map((ud) => ud.id),
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(updatedFulfillmentSets).toHaveLength(2)
|
||||||
|
|
||||||
|
for (const data_ of updateData) {
|
||||||
|
const currentFullfillmentSet = fullfillmentSets.find(
|
||||||
|
(fs) => fs.id === data_.id
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(currentFullfillmentSet).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: data_.id,
|
||||||
|
name: data_.name,
|
||||||
|
type: data_.type,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update an existing fulfillment set and replace old service zones by a new one", async function () {
|
||||||
|
const createData: CreateFulfillmentSetDTO = {
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "service-zone-test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdFulfillmentSet = await service.create(createData)
|
||||||
|
|
||||||
|
const createServiceZoneData: CreateServiceZoneDTO = {
|
||||||
|
fulfillment_set_id: createdFulfillmentSet.id,
|
||||||
|
name: "service-zone-test2",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "us",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData: UpdateFulfillmentSetDTO = {
|
||||||
|
id: createdFulfillmentSet.id,
|
||||||
|
name: "updated-test",
|
||||||
|
type: "updated-test-type",
|
||||||
|
service_zones: [createServiceZoneData],
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedFulfillmentSet = await service.update(updateData)
|
||||||
|
|
||||||
|
expect(updatedFulfillmentSet).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updateData.id,
|
||||||
|
name: updateData.name,
|
||||||
|
type: updateData.type,
|
||||||
|
service_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: (updateData.service_zones![0] as ServiceZoneDTO).name,
|
||||||
|
geo_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
type: (updateData.service_zones![0] as ServiceZoneDTO)
|
||||||
|
.geo_zones[0].type,
|
||||||
|
country_code: (
|
||||||
|
updateData.service_zones![0] as ServiceZoneDTO
|
||||||
|
).geo_zones[0].country_code,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const serviceZones = await service.listServiceZones()
|
||||||
|
|
||||||
|
expect(serviceZones).toHaveLength(1)
|
||||||
|
expect(serviceZones[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updatedFulfillmentSet.service_zones[0].id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update an existing fulfillment set and add a new service zone", async function () {
|
||||||
|
const createData: CreateFulfillmentSetDTO = {
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "service-zone-test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdFulfillmentSet = await service.create(createData)
|
||||||
|
|
||||||
|
const createServiceZoneData: CreateServiceZoneDTO = {
|
||||||
|
fulfillment_set_id: createdFulfillmentSet.id,
|
||||||
|
name: "service-zone-test2",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "us",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateData: UpdateFulfillmentSetDTO = {
|
||||||
|
id: createdFulfillmentSet.id,
|
||||||
|
name: "updated-test",
|
||||||
|
type: "updated-test-type",
|
||||||
|
service_zones: [
|
||||||
|
{ id: createdFulfillmentSet.service_zones[0].id },
|
||||||
|
createServiceZoneData,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedFulfillmentSet = await service.update(updateData)
|
||||||
|
|
||||||
|
expect(updatedFulfillmentSet).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updateData.id,
|
||||||
|
name: updateData.name,
|
||||||
|
type: updateData.type,
|
||||||
|
service_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: createdFulfillmentSet.service_zones[0].id,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: (updateData.service_zones![1] as ServiceZoneDTO).name,
|
||||||
|
geo_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
type: (updateData.service_zones![1] as ServiceZoneDTO)
|
||||||
|
.geo_zones[0].type,
|
||||||
|
country_code: (
|
||||||
|
updateData.service_zones![1] as ServiceZoneDTO
|
||||||
|
).geo_zones[0].country_code,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail on duplicated fulfillment set name", async function () {
|
||||||
|
const createData = [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
type: "test-type2",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const createdFulfillmentSets = await service.create(createData)
|
||||||
|
|
||||||
|
const updateData = {
|
||||||
|
id: createdFulfillmentSets[1].id,
|
||||||
|
name: "test", // This is the name of the first fulfillment set
|
||||||
|
type: "updated-test-type2",
|
||||||
|
}
|
||||||
|
|
||||||
|
const err = await service.update(updateData).catch((e) => e)
|
||||||
|
|
||||||
|
expect(err).toBeDefined()
|
||||||
|
expect(err.constraint).toBe("IDX_fulfillment_set_name_unique")
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update a collection of fulfillment sets and replace old service zones by new ones", async function () {
|
||||||
|
const createData: CreateFulfillmentSetDTO[] = [
|
||||||
|
{
|
||||||
|
name: "test1",
|
||||||
|
type: "test-type1",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "service-zone-test1",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
type: "test-type2",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "service-zone-test2",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "us",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const createdFulfillmentSets = await service.create(createData)
|
||||||
|
|
||||||
|
const updateData: UpdateFulfillmentSetDTO[] =
|
||||||
|
createdFulfillmentSets.map((fulfillmentSet, index) => ({
|
||||||
|
id: fulfillmentSet.id,
|
||||||
|
name: `updated-test${index + 1}`,
|
||||||
|
type: `updated-test-type${index + 1}`,
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: `new-service-zone-test${index + 1}`,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}))
|
||||||
|
|
||||||
|
const updatedFulfillmentSets = await service.update(updateData)
|
||||||
|
|
||||||
|
expect(updatedFulfillmentSets).toHaveLength(2)
|
||||||
|
|
||||||
|
for (const data_ of updateData) {
|
||||||
|
const expectedFulfillmentSet = updatedFulfillmentSets.find(
|
||||||
|
(f) => f.id === data_.id
|
||||||
|
)
|
||||||
|
expect(expectedFulfillmentSet).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: data_.id,
|
||||||
|
name: data_.name,
|
||||||
|
type: data_.type,
|
||||||
|
service_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: (data_.service_zones![0] as ServiceZoneDTO).name,
|
||||||
|
geo_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
type: (data_.service_zones![0] as ServiceZoneDTO)
|
||||||
|
.geo_zones[0].type,
|
||||||
|
country_code: (
|
||||||
|
data_.service_zones![0] as ServiceZoneDTO
|
||||||
|
).geo_zones[0].country_code,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceZones = await service.listServiceZones()
|
||||||
|
|
||||||
|
expect(serviceZones).toHaveLength(2)
|
||||||
|
expect(serviceZones).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
name: (updateData[0].service_zones![0] as ServiceZoneDTO)
|
||||||
|
.name,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
name: (updateData[1].service_zones![0] as ServiceZoneDTO)
|
||||||
|
.name,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update a collection of fulfillment sets and add new service zones", async function () {
|
||||||
|
const createData: CreateFulfillmentSetDTO[] = [
|
||||||
|
{
|
||||||
|
name: "test1",
|
||||||
|
type: "test-type1",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "service-zone-test1",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
type: "test-type2",
|
||||||
|
service_zones: [
|
||||||
|
{
|
||||||
|
name: "service-zone-test2",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "us",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const createdFulfillmentSets = await service.create(createData)
|
||||||
|
|
||||||
|
const updateData: UpdateFulfillmentSetDTO[] =
|
||||||
|
createdFulfillmentSets.map((fulfillmentSet, index) => ({
|
||||||
|
id: fulfillmentSet.id,
|
||||||
|
name: `updated-test${index + 1}`,
|
||||||
|
type: `updated-test-type${index + 1}`,
|
||||||
|
service_zones: [
|
||||||
|
...fulfillmentSet.service_zones,
|
||||||
|
{
|
||||||
|
name: `added-service-zone-test${index + 1}`,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}))
|
||||||
|
|
||||||
|
const updatedFulfillmentSets = await service.update(updateData)
|
||||||
|
|
||||||
|
expect(updatedFulfillmentSets).toHaveLength(2)
|
||||||
|
|
||||||
|
for (const data_ of updateData) {
|
||||||
|
const expectedFulfillmentSet = updatedFulfillmentSets.find(
|
||||||
|
(f) => f.id === data_.id
|
||||||
|
)
|
||||||
|
expect(expectedFulfillmentSet).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: data_.id,
|
||||||
|
name: data_.name,
|
||||||
|
type: data_.type,
|
||||||
|
service_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: (data_.service_zones![1] as ServiceZoneDTO).name,
|
||||||
|
geo_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
type: (data_.service_zones![1] as ServiceZoneDTO)
|
||||||
|
.geo_zones[0].type,
|
||||||
|
country_code: (
|
||||||
|
data_.service_zones![1] as ServiceZoneDTO
|
||||||
|
).geo_zones[0].country_code,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceZones = await service.listServiceZones()
|
||||||
|
|
||||||
|
expect(serviceZones).toHaveLength(4)
|
||||||
|
expect(serviceZones).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
name: createdFulfillmentSets[0].service_zones![0].name,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
name: createdFulfillmentSets[1].service_zones![0].name,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
name: (updateData[0].service_zones![1] as ServiceZoneDTO)
|
||||||
|
.name,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
name: (updateData[1].service_zones![1] as ServiceZoneDTO)
|
||||||
|
.name,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
+287
@@ -0,0 +1,287 @@
|
|||||||
|
import { resolve } from "path"
|
||||||
|
import { Modules, ModulesDefinition } from "@medusajs/modules-sdk"
|
||||||
|
import { IFulfillmentModuleService } from "@medusajs/types"
|
||||||
|
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||||
|
import {
|
||||||
|
generateCreateFulfillmentData,
|
||||||
|
generateCreateShippingOptionsData,
|
||||||
|
} from "../../__fixtures__"
|
||||||
|
import { initModules } from "medusa-test-utils/dist"
|
||||||
|
import { FulfillmentProviderService } from "@services"
|
||||||
|
import { FulfillmentProviderServiceFixtures } from "../../__fixtures__/providers"
|
||||||
|
|
||||||
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
|
const moduleOptions = {
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
resolve: resolve(
|
||||||
|
process.cwd() +
|
||||||
|
"/integration-tests/__fixtures__/providers/default-provider"
|
||||||
|
),
|
||||||
|
options: {
|
||||||
|
config: {
|
||||||
|
"test-provider": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerId = "fixtures-fulfillment-provider_test-provider"
|
||||||
|
|
||||||
|
moduleIntegrationTestRunner({
|
||||||
|
moduleName: Modules.FULFILLMENT,
|
||||||
|
moduleOptions: moduleOptions,
|
||||||
|
testSuite: ({
|
||||||
|
MikroOrmWrapper,
|
||||||
|
service,
|
||||||
|
}: SuiteOptions<IFulfillmentModuleService>) => {
|
||||||
|
describe("Fulfillment Module Service", () => {
|
||||||
|
it("should load and save all the providers on bootstrap", async () => {
|
||||||
|
const databaseConfig = {
|
||||||
|
schema: "public",
|
||||||
|
clientUrl: MikroOrmWrapper.clientUrl,
|
||||||
|
}
|
||||||
|
|
||||||
|
const providersConfig = {}
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
providersConfig[`provider-${i}`] = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
const moduleOptions = {
|
||||||
|
databaseConfig,
|
||||||
|
modulesConfig: {
|
||||||
|
[Modules.FULFILLMENT]: {
|
||||||
|
definition: ModulesDefinition[Modules.FULFILLMENT],
|
||||||
|
options: {
|
||||||
|
databaseConfig,
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
resolve: resolve(
|
||||||
|
process.cwd() +
|
||||||
|
"/integration-tests/__fixtures__/providers/default-provider"
|
||||||
|
),
|
||||||
|
options: {
|
||||||
|
config: providersConfig,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
const { shutdown } = await initModules(moduleOptions)
|
||||||
|
|
||||||
|
const fulfillmentProviderrs =
|
||||||
|
await MikroOrmWrapper.forkManager().execute(
|
||||||
|
`SELECT * FROM fulfillment_provider`
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(fulfillmentProviderrs).toHaveLength(
|
||||||
|
Object.keys(providersConfig).length + 1 // +1 for the default provider
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const [name] of Object.entries(providersConfig)) {
|
||||||
|
const provider = fulfillmentProviderrs.find((p) => {
|
||||||
|
return (
|
||||||
|
p.id ===
|
||||||
|
FulfillmentProviderService.getRegistrationIdentifier(
|
||||||
|
FulfillmentProviderServiceFixtures,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
expect(provider).toBeDefined()
|
||||||
|
}
|
||||||
|
|
||||||
|
await shutdown()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("Fulfillment Module Service", () => {
|
||||||
|
describe("read", () => {
|
||||||
|
it("should list fulfillment", async () => {
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption = await service.createShippingOptions(
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const fulfillment = await service.createFulfillment(
|
||||||
|
generateCreateFulfillmentData({
|
||||||
|
provider_id: providerId,
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const result = await service.listFulfillments({
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(result.length).toEqual(1)
|
||||||
|
expect(result[0].id).toEqual(fulfillment.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should retrieve the fulfillment options", async () => {
|
||||||
|
const fulfillmentOptions = await service.retrieveFulfillmentOptions(
|
||||||
|
providerId
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(fulfillmentOptions).toEqual({})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("mutations", () => {
|
||||||
|
describe("on create", () => {
|
||||||
|
it("should create a fulfillment", async () => {
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption = await service.createShippingOptions(
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const fulfillment = await service.createFulfillment(
|
||||||
|
generateCreateFulfillmentData({
|
||||||
|
provider_id: providerId,
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(fulfillment).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
packed_at: null,
|
||||||
|
shipped_at: null,
|
||||||
|
delivered_at: null,
|
||||||
|
canceled_at: null,
|
||||||
|
data: null,
|
||||||
|
provider_id: providerId,
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
metadata: null,
|
||||||
|
delivery_address: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
}),
|
||||||
|
items: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
labels: [
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("on cancel", () => {
|
||||||
|
let fulfillment
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption = await service.createShippingOptions(
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
fulfillment = await service.createFulfillment(
|
||||||
|
generateCreateFulfillmentData({
|
||||||
|
provider_id: providerId,
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should cancel a fulfillment successfully", async () => {
|
||||||
|
const result = await service.cancelFulfillment(fulfillment.id)
|
||||||
|
// should be idempotent
|
||||||
|
const idempotentResult = await service.cancelFulfillment(
|
||||||
|
fulfillment.id
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(result.canceled_at).not.toBeNull()
|
||||||
|
expect(idempotentResult.canceled_at).not.toBeNull()
|
||||||
|
expect(idempotentResult.canceled_at).toEqual(result.canceled_at)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to cancel a fulfillment that is already shipped", async () => {
|
||||||
|
await service.updateFulfillment(fulfillment.id, {
|
||||||
|
shipped_at: new Date(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const err = await service
|
||||||
|
.cancelFulfillment(fulfillment.id)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(err.message).toEqual(
|
||||||
|
`Fulfillment with id ${fulfillment.id} already shipped`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to cancel a fulfillment that is already delivered", async () => {
|
||||||
|
await service.updateFulfillment(fulfillment.id, {
|
||||||
|
delivered_at: new Date(),
|
||||||
|
})
|
||||||
|
|
||||||
|
const err = await service
|
||||||
|
.cancelFulfillment(fulfillment.id)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(err.message).toEqual(
|
||||||
|
`Fulfillment with id ${fulfillment.id} already delivered`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
+228
@@ -0,0 +1,228 @@
|
|||||||
|
import {Modules} from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
CreateGeoZoneDTO,
|
||||||
|
IFulfillmentModuleService,
|
||||||
|
UpdateGeoZoneDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import {GeoZoneType} from "@medusajs/utils"
|
||||||
|
import {moduleIntegrationTestRunner, SuiteOptions} from "medusa-test-utils"
|
||||||
|
|
||||||
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
|
moduleIntegrationTestRunner({
|
||||||
|
moduleName: Modules.FULFILLMENT,
|
||||||
|
testSuite: ({ service }: SuiteOptions<IFulfillmentModuleService>) => {
|
||||||
|
describe("Fulfillment Module Service", () => {
|
||||||
|
describe("read", () => {
|
||||||
|
it("should list geo zones with a filter", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
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 }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("mutations", () => {
|
||||||
|
describe("on create", () => {
|
||||||
|
it("should create a new geo zone", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const data: CreateGeoZoneDTO = {
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
}
|
||||||
|
|
||||||
|
const geoZone = await service.createGeoZones(data)
|
||||||
|
|
||||||
|
expect(geoZone).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
type: data.type,
|
||||||
|
country_code: data.country_code,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a collection of geo zones", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const data: CreateGeoZoneDTO[] = [
|
||||||
|
{
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "us",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const geoZones = await service.createGeoZones(data)
|
||||||
|
|
||||||
|
expect(geoZones).toHaveLength(2)
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
for (const data_ of data) {
|
||||||
|
expect(geoZones[i]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
type: data_.type,
|
||||||
|
country_code: data_.country_code,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("on update", () => {
|
||||||
|
it("should update an existing geo zone", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const createData: CreateGeoZoneDTO = {
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdGeoZone = await service.createGeoZones(createData)
|
||||||
|
|
||||||
|
const updateData: UpdateGeoZoneDTO = {
|
||||||
|
id: createdGeoZone.id,
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "us",
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedGeoZone = await service.updateGeoZones(updateData)
|
||||||
|
|
||||||
|
expect(updatedGeoZone).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updateData.id,
|
||||||
|
type: updateData.type,
|
||||||
|
country_code: updateData.country_code,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update a collection of geo zones", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const createData: CreateGeoZoneDTO[] = [
|
||||||
|
{
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "us",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const createdGeoZones = await service.createGeoZones(createData)
|
||||||
|
|
||||||
|
const updateData: UpdateGeoZoneDTO[] = createdGeoZones.map(
|
||||||
|
(geoZone, index) => ({
|
||||||
|
id: geoZone.id,
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: index % 2 === 0 ? "us" : "fr",
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const updatedGeoZones = await service.updateGeoZones(updateData)
|
||||||
|
|
||||||
|
expect(updatedGeoZones).toHaveLength(2)
|
||||||
|
|
||||||
|
for (const data_ of updateData) {
|
||||||
|
const expectedGeoZone = updatedGeoZones.find(
|
||||||
|
(geoZone) => geoZone.id === data_.id
|
||||||
|
)
|
||||||
|
expect(expectedGeoZone).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: data_.id,
|
||||||
|
type: data_.type,
|
||||||
|
country_code: data_.country_code,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
+374
@@ -0,0 +1,374 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
CreateServiceZoneDTO,
|
||||||
|
GeoZoneDTO,
|
||||||
|
IFulfillmentModuleService,
|
||||||
|
UpdateServiceZoneDTO,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { GeoZoneType } from "@medusajs/utils"
|
||||||
|
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||||
|
|
||||||
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
|
moduleIntegrationTestRunner({
|
||||||
|
moduleName: Modules.FULFILLMENT,
|
||||||
|
testSuite: ({ service }: SuiteOptions<IFulfillmentModuleService>) => {
|
||||||
|
describe("Fulfillment Module Service", () => {
|
||||||
|
describe("read", () => {
|
||||||
|
it("should list service zones with a filter", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
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 }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("mutations", () => {
|
||||||
|
describe("on create", () => {
|
||||||
|
it("should create a new service zone", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
|
||||||
|
const data: CreateServiceZoneDTO = {
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const serviceZone = await service.createServiceZones(data)
|
||||||
|
|
||||||
|
expect(serviceZone).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: data.name,
|
||||||
|
geo_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
type: (data.geo_zones![0] as GeoZoneDTO).type,
|
||||||
|
country_code: (data.geo_zones![0] as GeoZoneDTO)
|
||||||
|
.country_code,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create a collection of service zones", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
|
||||||
|
const data: CreateServiceZoneDTO[] = [
|
||||||
|
{
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test2",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test3",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "uk",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const serviceZones = await service.createServiceZones(data)
|
||||||
|
|
||||||
|
expect(serviceZones).toHaveLength(3)
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
for (const data_ of data) {
|
||||||
|
expect(serviceZones[i]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: data_.name,
|
||||||
|
geo_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
type: (data_.geo_zones![0] as GeoZoneDTO).type,
|
||||||
|
country_code: (data_.geo_zones![0] as GeoZoneDTO)
|
||||||
|
.country_code,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail on duplicated service zone name", async function () {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
|
||||||
|
const data: CreateServiceZoneDTO = {
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
await service.createServiceZones(data)
|
||||||
|
const err = await service.createServiceZones(data).catch((e) => e)
|
||||||
|
|
||||||
|
expect(err).toBeDefined()
|
||||||
|
expect(err.constraint).toBe("IDX_service_zone_name_unique")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("on update", () => {
|
||||||
|
it("should update an existing service zone", 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",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdServiceZone = await service.createServiceZones(
|
||||||
|
createData
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateData = {
|
||||||
|
id: createdServiceZone.id,
|
||||||
|
name: "updated-service-zone-test",
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
id: createdServiceZone.geo_zones[0].id,
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: "us",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedServiceZone = await service.updateServiceZones(
|
||||||
|
updateData
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updatedServiceZone).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updateData.id,
|
||||||
|
name: updateData.name,
|
||||||
|
geo_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updateData.geo_zones[0].id,
|
||||||
|
type: updateData.geo_zones[0].type,
|
||||||
|
country_code: updateData.geo_zones[0].country_code,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update a collection of service zones", 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[] = createdServiceZones.map(
|
||||||
|
(serviceZone, index) => ({
|
||||||
|
id: serviceZone.id,
|
||||||
|
name: `updated-service-zone-test${index + 1}`,
|
||||||
|
geo_zones: [
|
||||||
|
{
|
||||||
|
type: GeoZoneType.COUNTRY,
|
||||||
|
country_code: index % 2 === 0 ? "us" : "fr",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const updatedServiceZones = await service.updateServiceZones(
|
||||||
|
updateData
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updatedServiceZones).toHaveLength(2)
|
||||||
|
|
||||||
|
for (const data_ of updateData) {
|
||||||
|
const expectedServiceZone = updatedServiceZones.find(
|
||||||
|
(serviceZone) => serviceZone.id === data_.id
|
||||||
|
)
|
||||||
|
expect(expectedServiceZone).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: data_.id,
|
||||||
|
name: data_.name,
|
||||||
|
geo_zones: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
type: (data_.geo_zones![0] as GeoZoneDTO).type,
|
||||||
|
country_code: (data_.geo_zones![0] as GeoZoneDTO)
|
||||||
|
.country_code,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
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.constraint).toBe("IDX_service_zone_name_unique")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
+982
@@ -0,0 +1,982 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
CreateShippingOptionDTO,
|
||||||
|
IFulfillmentModuleService,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||||
|
import { generateCreateShippingOptionsData } from "../../__fixtures__"
|
||||||
|
import { resolve } from "path"
|
||||||
|
import { FulfillmentProviderService } from "@services"
|
||||||
|
import { FulfillmentProviderServiceFixtures } from "../../__fixtures__/providers"
|
||||||
|
|
||||||
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
|
const moduleOptions = {
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
resolve: resolve(
|
||||||
|
process.cwd() +
|
||||||
|
"/integration-tests/__fixtures__/providers/default-provider"
|
||||||
|
),
|
||||||
|
options: {
|
||||||
|
config: {
|
||||||
|
"test-provider": {},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const providerId = FulfillmentProviderService.getRegistrationIdentifier(
|
||||||
|
FulfillmentProviderServiceFixtures,
|
||||||
|
"test-provider"
|
||||||
|
)
|
||||||
|
|
||||||
|
moduleIntegrationTestRunner({
|
||||||
|
moduleName: Modules.FULFILLMENT,
|
||||||
|
moduleOptions,
|
||||||
|
testSuite: ({
|
||||||
|
MikroOrmWrapper,
|
||||||
|
service,
|
||||||
|
}: SuiteOptions<IFulfillmentModuleService>) => {
|
||||||
|
describe("Fulfillment Module Service", () => {
|
||||||
|
describe("read", () => {
|
||||||
|
it("should list shipping options with a filter", 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] = await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_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,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test-attribute",
|
||||||
|
operator: "eq",
|
||||||
|
value: "test",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
attribute: "test-attribute2.options",
|
||||||
|
operator: "in",
|
||||||
|
value: ["test", "test2"],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
const listedOptions = await service.listShippingOptions({
|
||||||
|
name: shippingOption1.name,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(listedOptions).toHaveLength(1)
|
||||||
|
expect(listedOptions[0].id).toEqual(shippingOption1.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should list shipping options with 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, , shippingOption3] =
|
||||||
|
await service.createShippingOptions([
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: fulfillmentSet.service_zones[0].id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_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,
|
||||||
|
fulfillment_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,
|
||||||
|
fulfillment_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({
|
||||||
|
context: {
|
||||||
|
"test-attribute": "test",
|
||||||
|
"test-attribute2": {
|
||||||
|
options: "test2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(listedOptions).toHaveLength(2)
|
||||||
|
expect(listedOptions).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({ id: shippingOption1.id }),
|
||||||
|
expect.objectContaining({ id: shippingOption3.id }),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
listedOptions = await service.listShippingOptions({
|
||||||
|
fulfillment_set_id: { $ne: fulfillmentSet.id },
|
||||||
|
context: {
|
||||||
|
"test-attribute": "test",
|
||||||
|
"test-attribute2": {
|
||||||
|
options: "test2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(listedOptions).toHaveLength(0)
|
||||||
|
|
||||||
|
listedOptions = await service.listShippingOptions({
|
||||||
|
fulfillment_set_type: "non-existing-type",
|
||||||
|
context: {
|
||||||
|
"test-attribute": "test",
|
||||||
|
"test-attribute2": {
|
||||||
|
options: "test2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(listedOptions).toHaveLength(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("mutations", () => {
|
||||||
|
describe("on create", () => {
|
||||||
|
it("should create a new shipping option", async function () {
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const createData: CreateShippingOptionDTO =
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
})
|
||||||
|
|
||||||
|
const createdShippingOption = await service.createShippingOptions(
|
||||||
|
createData
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(createdShippingOption).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: createData.name,
|
||||||
|
price_type: createData.price_type,
|
||||||
|
service_zone_id: createData.service_zone_id,
|
||||||
|
shipping_profile_id: createData.shipping_profile_id,
|
||||||
|
fulfillment_provider_id: createData.fulfillment_provider_id,
|
||||||
|
shipping_option_type_id: expect.any(String),
|
||||||
|
type: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
code: createData.type.code,
|
||||||
|
description: createData.type.description,
|
||||||
|
label: createData.type.label,
|
||||||
|
}),
|
||||||
|
data: createData.data,
|
||||||
|
rules: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
attribute: createData.rules![0].attribute,
|
||||||
|
operator: createData.rules![0].operator,
|
||||||
|
value: createData.rules![0].value,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create multiple new shipping options", async function () {
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const createData: CreateShippingOptionDTO[] = [
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
}),
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
|
||||||
|
const createdShippingOptions = await service.createShippingOptions(
|
||||||
|
createData
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(createdShippingOptions).toHaveLength(2)
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
for (const data_ of createData) {
|
||||||
|
expect(createdShippingOptions[i]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
name: data_.name,
|
||||||
|
price_type: data_.price_type,
|
||||||
|
service_zone_id: data_.service_zone_id,
|
||||||
|
shipping_profile_id: data_.shipping_profile_id,
|
||||||
|
fulfillment_provider_id: data_.fulfillment_provider_id,
|
||||||
|
shipping_option_type_id: expect.any(String),
|
||||||
|
type: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
code: data_.type.code,
|
||||||
|
description: data_.type.description,
|
||||||
|
label: data_.type.label,
|
||||||
|
}),
|
||||||
|
data: data_.data,
|
||||||
|
rules: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
attribute: data_.rules![0].attribute,
|
||||||
|
operator: data_.rules![0].operator,
|
||||||
|
value: data_.rules![0].value,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to create a new shipping option with invalid rules", async function () {
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const createData: CreateShippingOptionDTO =
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test-attribute",
|
||||||
|
operator: "invalid" as any,
|
||||||
|
value: "test-value",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
const err = await service
|
||||||
|
.createShippingOptions(createData)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(err).toBeDefined()
|
||||||
|
expect(err.message).toBe(
|
||||||
|
"Rule operator invalid is not supported. Must be one of in, eq, ne, gt, gte, lt, lte, nin"
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("on update", () => {
|
||||||
|
it("should update a shipping option", async () => {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOptionData = generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption = await service.createShippingOptions(
|
||||||
|
shippingOptionData
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateData = {
|
||||||
|
id: shippingOption.id,
|
||||||
|
name: "updated-test",
|
||||||
|
price_type: "calculated",
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
type: {
|
||||||
|
code: "updated-test",
|
||||||
|
description: "updated-test",
|
||||||
|
label: "updated-test",
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
amount: 2000,
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "new-test",
|
||||||
|
operator: "eq",
|
||||||
|
value: "new-test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedShippingOption = await service.updateShippingOptions(
|
||||||
|
updateData
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updatedShippingOption).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updateData.id,
|
||||||
|
name: updateData.name,
|
||||||
|
price_type: updateData.price_type,
|
||||||
|
service_zone_id: updateData.service_zone_id,
|
||||||
|
shipping_profile_id: updateData.shipping_profile_id,
|
||||||
|
fulfillment_provider_id: updateData.fulfillment_provider_id,
|
||||||
|
shipping_option_type_id: expect.any(String),
|
||||||
|
type: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
code: updateData.type.code,
|
||||||
|
description: updateData.type.description,
|
||||||
|
label: updateData.type.label,
|
||||||
|
}),
|
||||||
|
data: updateData.data,
|
||||||
|
rules: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
attribute: updateData.rules[0].attribute,
|
||||||
|
operator: updateData.rules[0].operator,
|
||||||
|
value: updateData.rules[0].value,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const rules = await service.listShippingOptionRules()
|
||||||
|
expect(rules).toHaveLength(1)
|
||||||
|
expect(rules[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updatedShippingOption.rules[0].id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const types = await service.listShippingOptionTypes()
|
||||||
|
expect(types).toHaveLength(1)
|
||||||
|
expect(types[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: updateData.type.code,
|
||||||
|
description: updateData.type.description,
|
||||||
|
label: updateData.type.label,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update a shipping option without updating the rules or the type", async () => {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOptionData = generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption = await service.createShippingOptions(
|
||||||
|
shippingOptionData
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateData = {
|
||||||
|
id: shippingOption.id,
|
||||||
|
name: "updated-test",
|
||||||
|
price_type: "calculated",
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
data: {
|
||||||
|
amount: 2000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
await service.updateShippingOptions(updateData)
|
||||||
|
|
||||||
|
const updatedShippingOption = await service.retrieveShippingOption(
|
||||||
|
shippingOption.id,
|
||||||
|
{
|
||||||
|
relations: ["rules", "type"],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updatedShippingOption).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updateData.id,
|
||||||
|
name: updateData.name,
|
||||||
|
price_type: updateData.price_type,
|
||||||
|
service_zone_id: updateData.service_zone_id,
|
||||||
|
shipping_profile_id: updateData.shipping_profile_id,
|
||||||
|
fulfillment_provider_id: updateData.fulfillment_provider_id,
|
||||||
|
shipping_option_type_id: expect.any(String),
|
||||||
|
type: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
code: shippingOptionData.type.code,
|
||||||
|
description: shippingOptionData.type.description,
|
||||||
|
label: shippingOptionData.type.label,
|
||||||
|
}),
|
||||||
|
data: updateData.data,
|
||||||
|
rules: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
attribute: shippingOptionData.rules[0].attribute,
|
||||||
|
operator: shippingOptionData.rules[0].operator,
|
||||||
|
value: shippingOptionData.rules[0].value,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const rules = await service.listShippingOptionRules()
|
||||||
|
expect(rules).toHaveLength(1)
|
||||||
|
expect(rules[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updatedShippingOption.rules[0].id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const types = await service.listShippingOptionTypes()
|
||||||
|
expect(types).toHaveLength(1)
|
||||||
|
expect(types[0]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
code: shippingOptionData.type.code,
|
||||||
|
description: shippingOptionData.type.description,
|
||||||
|
label: shippingOptionData.type.label,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should update a collection of shipping options", async () => {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOptionData = [
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
}),
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
|
||||||
|
const shippingOptions = await service.createShippingOptions(
|
||||||
|
shippingOptionData
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateData = [
|
||||||
|
{
|
||||||
|
id: shippingOptions[0].id,
|
||||||
|
name: "updated-test",
|
||||||
|
price_type: "calculated",
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
type: {
|
||||||
|
code: "updated-test",
|
||||||
|
description: "updated-test",
|
||||||
|
label: "updated-test",
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
amount: 2000,
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "new-test",
|
||||||
|
operator: "eq",
|
||||||
|
value: "new-test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: shippingOptions[1].id,
|
||||||
|
name: "updated-test",
|
||||||
|
price_type: "calculated",
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
type: {
|
||||||
|
code: "updated-test",
|
||||||
|
description: "updated-test",
|
||||||
|
label: "updated-test",
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
amount: 2000,
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "new-test",
|
||||||
|
operator: "eq",
|
||||||
|
value: "new-test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const updatedShippingOption = await service.updateShippingOptions(
|
||||||
|
updateData
|
||||||
|
)
|
||||||
|
|
||||||
|
for (const data_ of updateData) {
|
||||||
|
const expectedShippingOption = updatedShippingOption.find(
|
||||||
|
(shippingOption) => shippingOption.id === data_.id
|
||||||
|
)
|
||||||
|
expect(expectedShippingOption).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: data_.id,
|
||||||
|
name: data_.name,
|
||||||
|
price_type: data_.price_type,
|
||||||
|
service_zone_id: data_.service_zone_id,
|
||||||
|
shipping_profile_id: data_.shipping_profile_id,
|
||||||
|
fulfillment_provider_id: data_.fulfillment_provider_id,
|
||||||
|
shipping_option_type_id: expect.any(String),
|
||||||
|
type: expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
code: data_.type.code,
|
||||||
|
description: data_.type.description,
|
||||||
|
label: data_.type.label,
|
||||||
|
}),
|
||||||
|
data: data_.data,
|
||||||
|
rules: expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
attribute: data_.rules[0].attribute,
|
||||||
|
operator: data_.rules[0].operator,
|
||||||
|
value: data_.rules[0].value,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const rules = await service.listShippingOptionRules()
|
||||||
|
expect(rules).toHaveLength(2)
|
||||||
|
expect(rules).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updatedShippingOption[0].rules[0].id,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updatedShippingOption[1].rules[0].id,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
|
||||||
|
const types = await service.listShippingOptionTypes()
|
||||||
|
expect(types).toHaveLength(2)
|
||||||
|
expect(types).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
code: updateData[0].type.code,
|
||||||
|
description: updateData[0].type.description,
|
||||||
|
label: updateData[0].type.label,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
code: updateData[1].type.code,
|
||||||
|
description: updateData[1].type.description,
|
||||||
|
label: updateData[1].type.label,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to update a non-existent shipping option", async () => {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const [fulfillmentProvider] =
|
||||||
|
await MikroOrmWrapper.forkManager().execute(
|
||||||
|
"insert into fulfillment_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingOptionData = {
|
||||||
|
id: "sp_jdafwfleiwuonl",
|
||||||
|
name: "test",
|
||||||
|
price_type: "flat",
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: fulfillmentProvider.id,
|
||||||
|
type: {
|
||||||
|
code: "test",
|
||||||
|
description: "test",
|
||||||
|
label: "test",
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
amount: 1000,
|
||||||
|
},
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test",
|
||||||
|
operator: "eq",
|
||||||
|
value: "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
const err = await service
|
||||||
|
.updateShippingOptions(shippingOptionData)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(err).toBeDefined()
|
||||||
|
expect(err.message).toBe(
|
||||||
|
`The following shipping options do not exist: ${shippingOptionData.id}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to update a shipping option when adding non existing rules", async () => {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOptionData = generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption = await service.createShippingOptions(
|
||||||
|
shippingOptionData
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateData = [
|
||||||
|
{
|
||||||
|
id: shippingOption.id,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
id: "sp_jdafwfleiwuonl",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const err = await service
|
||||||
|
.updateShippingOptions(updateData)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(err).toBeDefined()
|
||||||
|
expect(err.message).toBe(
|
||||||
|
`The following rules does not exists: ${updateData[0].rules[0].id} on shipping option ${shippingOption.id}`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to update a shipping option when adding invalid rules", async () => {
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOptionData = generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption = await service.createShippingOptions(
|
||||||
|
shippingOptionData
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateData = [
|
||||||
|
{
|
||||||
|
id: shippingOption.id,
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
attribute: "test",
|
||||||
|
operator: "invalid",
|
||||||
|
value: "test",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const err = await service
|
||||||
|
.updateShippingOptions(updateData)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(err).toBeDefined()
|
||||||
|
expect(err.message).toBe(
|
||||||
|
`Rule operator invalid is not supported. Must be one of in, eq, ne, gt, gte, lt, lte, nin`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("on create shipping option rules", () => {
|
||||||
|
it("should create a new rule", async () => {
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
// service provider
|
||||||
|
const [{ id: providerId }] =
|
||||||
|
await MikroOrmWrapper.forkManager().execute(
|
||||||
|
"insert into fulfillment_provider (id) values ('sp_jdafwfleiwuonl') returning id"
|
||||||
|
)
|
||||||
|
|
||||||
|
const shippingOption = await service.createShippingOptions(
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const ruleData = {
|
||||||
|
attribute: "test-attribute",
|
||||||
|
operator: "eq",
|
||||||
|
value: "test-value",
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
}
|
||||||
|
|
||||||
|
const rule = await service.createShippingOptionRules(ruleData)
|
||||||
|
|
||||||
|
expect(rule).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: expect.any(String),
|
||||||
|
attribute: ruleData.attribute,
|
||||||
|
operator: ruleData.operator,
|
||||||
|
value: ruleData.value,
|
||||||
|
shipping_option_id: ruleData.shipping_option_id,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const rules = await service.listShippingOptionRules()
|
||||||
|
expect(rules).toHaveLength(2)
|
||||||
|
expect(rules).toEqual(
|
||||||
|
expect.arrayContaining([
|
||||||
|
expect.objectContaining({
|
||||||
|
id: rule.id,
|
||||||
|
attribute: ruleData.attribute,
|
||||||
|
operator: ruleData.operator,
|
||||||
|
value: ruleData.value,
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
id: shippingOption.rules[0].id,
|
||||||
|
attribute: shippingOption.rules[0].attribute,
|
||||||
|
operator: shippingOption.rules[0].operator,
|
||||||
|
value: shippingOption.rules[0].value,
|
||||||
|
shipping_option_id: shippingOption.id,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("on update shipping option rules", () => {
|
||||||
|
it("should update a shipping option rule", async () => {
|
||||||
|
const shippingProfile = await service.createShippingProfiles({
|
||||||
|
name: "test",
|
||||||
|
type: "default",
|
||||||
|
})
|
||||||
|
const fulfillmentSet = await service.create({
|
||||||
|
name: "test",
|
||||||
|
type: "test-type",
|
||||||
|
})
|
||||||
|
const serviceZone = await service.createServiceZones({
|
||||||
|
name: "test",
|
||||||
|
fulfillment_set_id: fulfillmentSet.id,
|
||||||
|
})
|
||||||
|
|
||||||
|
const shippingOption = await service.createShippingOptions(
|
||||||
|
generateCreateShippingOptionsData({
|
||||||
|
service_zone_id: serviceZone.id,
|
||||||
|
shipping_profile_id: shippingProfile.id,
|
||||||
|
fulfillment_provider_id: providerId,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const updateData = {
|
||||||
|
id: shippingOption.rules[0].id,
|
||||||
|
attribute: "updated-test",
|
||||||
|
operator: "eq",
|
||||||
|
value: "updated-test",
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedRule = await service.updateShippingOptionRules(
|
||||||
|
updateData
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(updatedRule).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
id: updateData.id,
|
||||||
|
attribute: updateData.attribute,
|
||||||
|
operator: updateData.operator,
|
||||||
|
value: updateData.value,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail to update a non-existent shipping option rule", async () => {
|
||||||
|
const updateData = {
|
||||||
|
id: "sp_jdafwfleiwuonl",
|
||||||
|
attribute: "updated-test",
|
||||||
|
operator: "eq",
|
||||||
|
value: "updated-test",
|
||||||
|
}
|
||||||
|
|
||||||
|
const err = await service
|
||||||
|
.updateShippingOptionRules(updateData)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(err).toBeDefined()
|
||||||
|
expect(err.message).toBe(
|
||||||
|
`ShippingOptionRule with id "${updateData.id}" not found`
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
+82
@@ -0,0 +1,82 @@
|
|||||||
|
import { Modules } from "@medusajs/modules-sdk"
|
||||||
|
import {
|
||||||
|
CreateShippingProfileDTO,
|
||||||
|
IFulfillmentModuleService,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { moduleIntegrationTestRunner, SuiteOptions } from "medusa-test-utils"
|
||||||
|
|
||||||
|
jest.setTimeout(100000)
|
||||||
|
|
||||||
|
moduleIntegrationTestRunner({
|
||||||
|
moduleName: Modules.FULFILLMENT,
|
||||||
|
testSuite: ({ service }: SuiteOptions<IFulfillmentModuleService>) => {
|
||||||
|
describe("Fulfillment Module Service", () => {
|
||||||
|
describe("mutations", () => {
|
||||||
|
describe("on create", () => {
|
||||||
|
it("should create a new shipping profile", async function () {
|
||||||
|
const createData: CreateShippingProfileDTO = {
|
||||||
|
name: "test-default-profile",
|
||||||
|
type: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
const createdShippingProfile = await service.createShippingProfiles(
|
||||||
|
createData
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(createdShippingProfile).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
name: createData.name,
|
||||||
|
type: createData.type,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should create multiple new shipping profiles", async function () {
|
||||||
|
const createData: CreateShippingProfileDTO[] = [
|
||||||
|
{
|
||||||
|
name: "test-profile-1",
|
||||||
|
type: "default",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "test-profile-2",
|
||||||
|
type: "custom",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const createdShippingProfiles =
|
||||||
|
await service.createShippingProfiles(createData)
|
||||||
|
|
||||||
|
expect(createdShippingProfiles).toHaveLength(2)
|
||||||
|
|
||||||
|
let i = 0
|
||||||
|
for (const data_ of createData) {
|
||||||
|
expect(createdShippingProfiles[i]).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
name: data_.name,
|
||||||
|
type: data_.type,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
++i
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should fail on duplicated shipping profile name", async function () {
|
||||||
|
const createData: CreateShippingProfileDTO = {
|
||||||
|
name: "test-default-profile",
|
||||||
|
type: "default",
|
||||||
|
}
|
||||||
|
|
||||||
|
await service.createShippingProfiles(createData)
|
||||||
|
|
||||||
|
const err = await service
|
||||||
|
.createShippingProfiles(createData)
|
||||||
|
.catch((e) => e)
|
||||||
|
|
||||||
|
expect(err).toBeDefined()
|
||||||
|
expect(err.constraint).toBe("IDX_shipping_profile_name_unique")
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -10,7 +10,7 @@ module.exports = {
|
|||||||
"^.+\\.[jt]s?$": [
|
"^.+\\.[jt]s?$": [
|
||||||
"ts-jest",
|
"ts-jest",
|
||||||
{
|
{
|
||||||
tsConfig: "tsconfig.spec.json",
|
tsconfig: "tsconfig.spec.json",
|
||||||
isolatedModules: true,
|
isolatedModules: true,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
"orm:cache:clear": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm cache:clear"
|
"orm:cache:clear": " MIKRO_ORM_CLI=./mikro-orm.config.dev.ts mikro-orm cache:clear"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@medusajs/fulfillment-manual": "workspace:*",
|
||||||
"@mikro-orm/cli": "5.9.7",
|
"@mikro-orm/cli": "5.9.7",
|
||||||
"cross-env": "^5.2.1",
|
"cross-env": "^5.2.1",
|
||||||
"jest": "^29.6.3",
|
"jest": "^29.6.3",
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import { moduleProviderLoader } from "@medusajs/modules-sdk"
|
||||||
|
import { LoaderOptions, ModuleProvider, ModulesSdkTypes } from "@medusajs/types"
|
||||||
|
import { asFunction, asValue, Lifetime } from "awilix"
|
||||||
|
import { FulfillmentIdentifiersRegistrationName } from "@types"
|
||||||
|
import { lowerCaseFirst } from "@medusajs/utils"
|
||||||
|
import { FulfillmentProviderService } from "@services"
|
||||||
|
import { ContainerRegistrationKeys } from "@medusajs/utils/src"
|
||||||
|
|
||||||
|
const registrationFn = async (klass, container, pluginOptions) => {
|
||||||
|
Object.entries(pluginOptions.config || []).map(([name, config]) => {
|
||||||
|
const key = FulfillmentProviderService.getRegistrationIdentifier(
|
||||||
|
klass,
|
||||||
|
name
|
||||||
|
)
|
||||||
|
|
||||||
|
container.register({
|
||||||
|
["fp_" + key]: asFunction((cradle) => new klass(cradle, config), {
|
||||||
|
lifetime: klass.LIFE_TIME || Lifetime.SINGLETON,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
|
container.registerAdd(FulfillmentIdentifiersRegistrationName, asValue(key))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async ({
|
||||||
|
container,
|
||||||
|
options,
|
||||||
|
}: LoaderOptions<
|
||||||
|
(
|
||||||
|
| ModulesSdkTypes.ModuleServiceInitializeOptions
|
||||||
|
| ModulesSdkTypes.ModuleServiceInitializeCustomDataLayerOptions
|
||||||
|
) & { providers: ModuleProvider[] }
|
||||||
|
>): Promise<void> => {
|
||||||
|
container.registerAdd(
|
||||||
|
FulfillmentIdentifiersRegistrationName,
|
||||||
|
asValue(undefined)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Local providers
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
await moduleProviderLoader({
|
||||||
|
container,
|
||||||
|
providers: options?.providers || [],
|
||||||
|
registerServiceFn: registrationFn,
|
||||||
|
})
|
||||||
|
|
||||||
|
await syncDatabaseProviders({
|
||||||
|
container,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function syncDatabaseProviders({ container }) {
|
||||||
|
const providerServiceRegistrationKey = lowerCaseFirst(
|
||||||
|
FulfillmentProviderService.name
|
||||||
|
)
|
||||||
|
|
||||||
|
const logger = container.resolve(ContainerRegistrationKeys.LOGGER)
|
||||||
|
try {
|
||||||
|
const providerIdentifiers: string[] = (
|
||||||
|
container.resolve(FulfillmentIdentifiersRegistrationName) ?? []
|
||||||
|
).filter(Boolean)
|
||||||
|
|
||||||
|
const providerService: ModulesSdkTypes.InternalModuleService<any> =
|
||||||
|
container.resolve(providerServiceRegistrationKey)
|
||||||
|
|
||||||
|
const providers = await providerService.list({
|
||||||
|
id: providerIdentifiers,
|
||||||
|
})
|
||||||
|
|
||||||
|
const loadedProvidersMap = new Map(providers.map((p) => [p.id, p]))
|
||||||
|
|
||||||
|
const providersToCreate: any[] = []
|
||||||
|
for (const identifier of providerIdentifiers) {
|
||||||
|
if (loadedProvidersMap.has(identifier)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
providersToCreate.push({ id: identifier })
|
||||||
|
}
|
||||||
|
|
||||||
|
await providerService.create(providersToCreate)
|
||||||
|
} catch (error) {
|
||||||
|
logger.error(`Error syncing providers: ${error.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -15,15 +15,6 @@
|
|||||||
"nullable": false,
|
"nullable": false,
|
||||||
"mappedType": "text"
|
"mappedType": "text"
|
||||||
},
|
},
|
||||||
"fulfillment_id": {
|
|
||||||
"name": "fulfillment_id",
|
|
||||||
"type": "text",
|
|
||||||
"unsigned": false,
|
|
||||||
"autoincrement": false,
|
|
||||||
"primary": false,
|
|
||||||
"nullable": true,
|
|
||||||
"mappedType": "text"
|
|
||||||
},
|
|
||||||
"company": {
|
"company": {
|
||||||
"name": "company",
|
"name": "company",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -159,16 +150,6 @@
|
|||||||
"name": "fulfillment_address",
|
"name": "fulfillment_address",
|
||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
{
|
|
||||||
"keyName": "IDX_fulfillment_address_fulfillment_id",
|
|
||||||
"columnNames": [
|
|
||||||
"fulfillment_id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
|
||||||
"primary": false,
|
|
||||||
"unique": false,
|
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_address_fulfillment_id\" ON \"fulfillment_address\" (fulfillment_id) WHERE deleted_at IS NULL"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"keyName": "IDX_fulfillment_address_deleted_at",
|
"keyName": "IDX_fulfillment_address_deleted_at",
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
@@ -192,6 +173,85 @@
|
|||||||
"checks": [],
|
"checks": [],
|
||||||
"foreignKeys": {}
|
"foreignKeys": {}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"columns": {
|
||||||
|
"id": {
|
||||||
|
"name": "id",
|
||||||
|
"type": "text",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"mappedType": "text"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"name": "metadata",
|
||||||
|
"type": "jsonb",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": true,
|
||||||
|
"mappedType": "json"
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"name": "created_at",
|
||||||
|
"type": "timestamptz",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"length": 6,
|
||||||
|
"default": "now()",
|
||||||
|
"mappedType": "datetime"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"name": "updated_at",
|
||||||
|
"type": "timestamptz",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"length": 6,
|
||||||
|
"default": "now()",
|
||||||
|
"mappedType": "datetime"
|
||||||
|
},
|
||||||
|
"deleted_at": {
|
||||||
|
"name": "deleted_at",
|
||||||
|
"type": "timestamptz",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": true,
|
||||||
|
"length": 6,
|
||||||
|
"mappedType": "datetime"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"name": "fulfillment_provider",
|
||||||
|
"schema": "public",
|
||||||
|
"indexes": [
|
||||||
|
{
|
||||||
|
"keyName": "IDX_fulfillment_provider_deleted_at",
|
||||||
|
"columnNames": [
|
||||||
|
"deleted_at"
|
||||||
|
],
|
||||||
|
"composite": false,
|
||||||
|
"primary": false,
|
||||||
|
"unique": false,
|
||||||
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_provider_deleted_at\" ON \"fulfillment_provider\" (deleted_at) WHERE deleted_at IS NOT NULL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"keyName": "fulfillment_provider_pkey",
|
||||||
|
"columnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"composite": false,
|
||||||
|
"primary": true,
|
||||||
|
"unique": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checks": [],
|
||||||
|
"foreignKeys": {}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
@@ -299,85 +359,6 @@
|
|||||||
"checks": [],
|
"checks": [],
|
||||||
"foreignKeys": {}
|
"foreignKeys": {}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"columns": {
|
|
||||||
"id": {
|
|
||||||
"name": "id",
|
|
||||||
"type": "text",
|
|
||||||
"unsigned": false,
|
|
||||||
"autoincrement": false,
|
|
||||||
"primary": false,
|
|
||||||
"nullable": false,
|
|
||||||
"mappedType": "text"
|
|
||||||
},
|
|
||||||
"metadata": {
|
|
||||||
"name": "metadata",
|
|
||||||
"type": "jsonb",
|
|
||||||
"unsigned": false,
|
|
||||||
"autoincrement": false,
|
|
||||||
"primary": false,
|
|
||||||
"nullable": true,
|
|
||||||
"mappedType": "json"
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"name": "created_at",
|
|
||||||
"type": "timestamptz",
|
|
||||||
"unsigned": false,
|
|
||||||
"autoincrement": false,
|
|
||||||
"primary": false,
|
|
||||||
"nullable": false,
|
|
||||||
"length": 6,
|
|
||||||
"default": "now()",
|
|
||||||
"mappedType": "datetime"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"name": "updated_at",
|
|
||||||
"type": "timestamptz",
|
|
||||||
"unsigned": false,
|
|
||||||
"autoincrement": false,
|
|
||||||
"primary": false,
|
|
||||||
"nullable": false,
|
|
||||||
"length": 6,
|
|
||||||
"default": "now()",
|
|
||||||
"mappedType": "datetime"
|
|
||||||
},
|
|
||||||
"deleted_at": {
|
|
||||||
"name": "deleted_at",
|
|
||||||
"type": "timestamptz",
|
|
||||||
"unsigned": false,
|
|
||||||
"autoincrement": false,
|
|
||||||
"primary": false,
|
|
||||||
"nullable": true,
|
|
||||||
"length": 6,
|
|
||||||
"mappedType": "datetime"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"name": "service_provider",
|
|
||||||
"schema": "public",
|
|
||||||
"indexes": [
|
|
||||||
{
|
|
||||||
"keyName": "IDX_service_provider_deleted_at",
|
|
||||||
"columnNames": [
|
|
||||||
"deleted_at"
|
|
||||||
],
|
|
||||||
"composite": false,
|
|
||||||
"primary": false,
|
|
||||||
"unique": false,
|
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_service_provider_deleted_at\" ON \"service_provider\" (deleted_at) WHERE deleted_at IS NOT NULL"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"keyName": "service_provider_pkey",
|
|
||||||
"columnNames": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"composite": false,
|
|
||||||
"primary": true,
|
|
||||||
"unique": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"checks": [],
|
|
||||||
"foreignKeys": {}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"columns": {
|
"columns": {
|
||||||
"id": {
|
"id": {
|
||||||
@@ -957,8 +938,8 @@
|
|||||||
"nullable": true,
|
"nullable": true,
|
||||||
"mappedType": "text"
|
"mappedType": "text"
|
||||||
},
|
},
|
||||||
"service_provider_id": {
|
"fulfillment_provider_id": {
|
||||||
"name": "service_provider_id",
|
"name": "fulfillment_provider_id",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
"unsigned": false,
|
"unsigned": false,
|
||||||
"autoincrement": false,
|
"autoincrement": false,
|
||||||
@@ -1059,14 +1040,14 @@
|
|||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_shipping_profile_id\" ON \"shipping_option\" (shipping_profile_id) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_shipping_profile_id\" ON \"shipping_option\" (shipping_profile_id) WHERE deleted_at IS NULL"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "IDX_shipping_option_service_provider_id",
|
"keyName": "IDX_shipping_option_fulfillment_provider_id",
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
"service_provider_id"
|
"fulfillment_provider_id"
|
||||||
],
|
],
|
||||||
"composite": false,
|
"composite": false,
|
||||||
"primary": false,
|
"primary": false,
|
||||||
"unique": false,
|
"unique": false,
|
||||||
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_service_provider_id\" ON \"shipping_option\" (service_provider_id) WHERE deleted_at IS NULL"
|
"expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_fulfillment_provider_id\" ON \"shipping_option\" (fulfillment_provider_id) WHERE deleted_at IS NULL"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"keyName": "IDX_shipping_option_shipping_option_type_id",
|
"keyName": "IDX_shipping_option_shipping_option_type_id",
|
||||||
@@ -1125,16 +1106,16 @@
|
|||||||
"deleteRule": "set null",
|
"deleteRule": "set null",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
},
|
},
|
||||||
"shipping_option_service_provider_id_foreign": {
|
"shipping_option_fulfillment_provider_id_foreign": {
|
||||||
"constraintName": "shipping_option_service_provider_id_foreign",
|
"constraintName": "shipping_option_fulfillment_provider_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
"service_provider_id"
|
"fulfillment_provider_id"
|
||||||
],
|
],
|
||||||
"localTableName": "public.shipping_option",
|
"localTableName": "public.shipping_option",
|
||||||
"referencedColumnNames": [
|
"referencedColumnNames": [
|
||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"referencedTableName": "public.service_provider",
|
"referencedTableName": "public.fulfillment_provider",
|
||||||
"deleteRule": "set null",
|
"deleteRule": "set null",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
},
|
},
|
||||||
@@ -1397,15 +1378,6 @@
|
|||||||
"nullable": false,
|
"nullable": false,
|
||||||
"mappedType": "text"
|
"mappedType": "text"
|
||||||
},
|
},
|
||||||
"items_id": {
|
|
||||||
"name": "items_id",
|
|
||||||
"type": "text",
|
|
||||||
"unsigned": false,
|
|
||||||
"autoincrement": false,
|
|
||||||
"primary": false,
|
|
||||||
"nullable": false,
|
|
||||||
"mappedType": "text"
|
|
||||||
},
|
|
||||||
"created_at": {
|
"created_at": {
|
||||||
"name": "created_at",
|
"name": "created_at",
|
||||||
"type": "timestamptz",
|
"type": "timestamptz",
|
||||||
@@ -1442,6 +1414,15 @@
|
|||||||
"name": "fulfillment",
|
"name": "fulfillment",
|
||||||
"schema": "public",
|
"schema": "public",
|
||||||
"indexes": [
|
"indexes": [
|
||||||
|
{
|
||||||
|
"columnNames": [
|
||||||
|
"delivery_address_id"
|
||||||
|
],
|
||||||
|
"composite": false,
|
||||||
|
"keyName": "fulfillment_delivery_address_id_unique",
|
||||||
|
"primary": false,
|
||||||
|
"unique": true
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"keyName": "IDX_fulfillment_location_id",
|
"keyName": "IDX_fulfillment_location_id",
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
@@ -1494,6 +1475,18 @@
|
|||||||
],
|
],
|
||||||
"checks": [],
|
"checks": [],
|
||||||
"foreignKeys": {
|
"foreignKeys": {
|
||||||
|
"fulfillment_provider_id_foreign": {
|
||||||
|
"constraintName": "fulfillment_provider_id_foreign",
|
||||||
|
"columnNames": [
|
||||||
|
"provider_id"
|
||||||
|
],
|
||||||
|
"localTableName": "public.fulfillment",
|
||||||
|
"referencedColumnNames": [
|
||||||
|
"id"
|
||||||
|
],
|
||||||
|
"referencedTableName": "public.fulfillment_provider",
|
||||||
|
"updateRule": "cascade"
|
||||||
|
},
|
||||||
"fulfillment_shipping_option_id_foreign": {
|
"fulfillment_shipping_option_id_foreign": {
|
||||||
"constraintName": "fulfillment_shipping_option_id_foreign",
|
"constraintName": "fulfillment_shipping_option_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
@@ -1507,18 +1500,6 @@
|
|||||||
"deleteRule": "set null",
|
"deleteRule": "set null",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
},
|
},
|
||||||
"fulfillment_provider_id_foreign": {
|
|
||||||
"constraintName": "fulfillment_provider_id_foreign",
|
|
||||||
"columnNames": [
|
|
||||||
"provider_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.fulfillment",
|
|
||||||
"referencedColumnNames": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.service_provider",
|
|
||||||
"updateRule": "cascade"
|
|
||||||
},
|
|
||||||
"fulfillment_delivery_address_id_foreign": {
|
"fulfillment_delivery_address_id_foreign": {
|
||||||
"constraintName": "fulfillment_delivery_address_id_foreign",
|
"constraintName": "fulfillment_delivery_address_id_foreign",
|
||||||
"columnNames": [
|
"columnNames": [
|
||||||
@@ -1530,18 +1511,6 @@
|
|||||||
],
|
],
|
||||||
"referencedTableName": "public.fulfillment_address",
|
"referencedTableName": "public.fulfillment_address",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
},
|
|
||||||
"fulfillment_items_id_foreign": {
|
|
||||||
"constraintName": "fulfillment_items_id_foreign",
|
|
||||||
"columnNames": [
|
|
||||||
"items_id"
|
|
||||||
],
|
|
||||||
"localTableName": "public.fulfillment",
|
|
||||||
"referencedColumnNames": [
|
|
||||||
"id"
|
|
||||||
],
|
|
||||||
"referencedTableName": "public.fulfillment_item",
|
|
||||||
"updateRule": "cascade"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1670,6 +1639,7 @@
|
|||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"referencedTableName": "public.fulfillment",
|
"referencedTableName": "public.fulfillment",
|
||||||
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1721,6 +1691,15 @@
|
|||||||
"nullable": false,
|
"nullable": false,
|
||||||
"mappedType": "decimal"
|
"mappedType": "decimal"
|
||||||
},
|
},
|
||||||
|
"raw_quantity": {
|
||||||
|
"name": "raw_quantity",
|
||||||
|
"type": "jsonb",
|
||||||
|
"unsigned": false,
|
||||||
|
"autoincrement": false,
|
||||||
|
"primary": false,
|
||||||
|
"nullable": false,
|
||||||
|
"mappedType": "json"
|
||||||
|
},
|
||||||
"line_item_id": {
|
"line_item_id": {
|
||||||
"name": "line_item_id",
|
"name": "line_item_id",
|
||||||
"type": "text",
|
"type": "text",
|
||||||
@@ -1846,6 +1825,7 @@
|
|||||||
"id"
|
"id"
|
||||||
],
|
],
|
||||||
"referencedTableName": "public.fulfillment",
|
"referencedTableName": "public.fulfillment",
|
||||||
|
"deleteRule": "cascade",
|
||||||
"updateRule": "cascade"
|
"updateRule": "cascade"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-15
@@ -1,19 +1,18 @@
|
|||||||
import { Migration } from '@mikro-orm/migrations';
|
import { Migration } from '@mikro-orm/migrations';
|
||||||
|
|
||||||
export class Migration20240221164918_InitialSetupMigration extends Migration {
|
export class Migration20240305095931_InitialSetupMigration extends Migration {
|
||||||
|
|
||||||
async up(): Promise<void> {
|
async up(): Promise<void> {
|
||||||
this.addSql('create table if not exists "fulfillment_address" ("id" text not null, "fulfillment_id" text null, "company" text null, "first_name" text null, "last_name" text null, "address_1" text null, "address_2" text null, "city" text null, "country_code" text null, "province" text null, "postal_code" text null, "phone" text null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "fulfillment_address_pkey" primary key ("id"));');
|
this.addSql('create table if not exists "fulfillment_address" ("id" text not null, "company" text null, "first_name" text null, "last_name" text null, "address_1" text null, "address_2" text null, "city" text null, "country_code" text null, "province" text null, "postal_code" text null, "phone" text null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "fulfillment_address_pkey" primary key ("id"));');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_address_fulfillment_id" ON "fulfillment_address" (fulfillment_id) WHERE deleted_at IS NULL;');
|
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_address_deleted_at" ON "fulfillment_address" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_address_deleted_at" ON "fulfillment_address" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
||||||
|
|
||||||
|
this.addSql('create table if not exists "fulfillment_provider" ("id" text not null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "fulfillment_provider_pkey" primary key ("id"));');
|
||||||
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_provider_deleted_at" ON "fulfillment_provider" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
||||||
|
|
||||||
this.addSql('create table if not exists "fulfillment_set" ("id" text not null, "name" text not null, "type" text not null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "fulfillment_set_pkey" primary key ("id"));');
|
this.addSql('create table if not exists "fulfillment_set" ("id" text not null, "name" text not null, "type" text not null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "fulfillment_set_pkey" primary key ("id"));');
|
||||||
this.addSql('CREATE UNIQUE INDEX IF NOT EXISTS "IDX_fulfillment_set_name_unique" ON "fulfillment_set" (name) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE UNIQUE INDEX IF NOT EXISTS "IDX_fulfillment_set_name_unique" ON "fulfillment_set" (name) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_set_deleted_at" ON "fulfillment_set" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_set_deleted_at" ON "fulfillment_set" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
||||||
|
|
||||||
this.addSql('create table if not exists "service_provider" ("id" text not null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "service_provider_pkey" primary key ("id"));');
|
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_service_provider_deleted_at" ON "service_provider" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
|
||||||
|
|
||||||
this.addSql('create table if not exists "service_zone" ("id" text not null, "name" text not null, "metadata" jsonb null, "fulfillment_set_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "service_zone_pkey" primary key ("id"));');
|
this.addSql('create table if not exists "service_zone" ("id" text not null, "name" text not null, "metadata" jsonb null, "fulfillment_set_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "service_zone_pkey" primary key ("id"));');
|
||||||
this.addSql('CREATE UNIQUE INDEX IF NOT EXISTS "IDX_service_zone_name_unique" ON "service_zone" (name) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE UNIQUE INDEX IF NOT EXISTS "IDX_service_zone_name_unique" ON "service_zone" (name) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_service_zone_fulfillment_set_id" ON "service_zone" (fulfillment_set_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_service_zone_fulfillment_set_id" ON "service_zone" (fulfillment_set_id) WHERE deleted_at IS NULL;');
|
||||||
@@ -33,11 +32,11 @@ export class Migration20240221164918_InitialSetupMigration extends Migration {
|
|||||||
this.addSql('CREATE UNIQUE INDEX IF NOT EXISTS "IDX_shipping_profile_name_unique" ON "shipping_profile" (name) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE UNIQUE INDEX IF NOT EXISTS "IDX_shipping_profile_name_unique" ON "shipping_profile" (name) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_profile_deleted_at" ON "shipping_profile" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_profile_deleted_at" ON "shipping_profile" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
||||||
|
|
||||||
this.addSql('create table if not exists "shipping_option" ("id" text not null, "name" text not null, "price_type" text check ("price_type" in (\'calculated\', \'flat\')) not null default \'calculated\', "service_zone_id" text not null, "shipping_profile_id" text null, "service_provider_id" text null, "data" jsonb null, "metadata" jsonb null, "shipping_option_type_id" text null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "shipping_option_pkey" primary key ("id"));');
|
this.addSql('create table if not exists "shipping_option" ("id" text not null, "name" text not null, "price_type" text check ("price_type" in (\'calculated\', \'flat\')) not null default \'calculated\', "service_zone_id" text not null, "shipping_profile_id" text null, "fulfillment_provider_id" text null, "data" jsonb null, "metadata" jsonb null, "shipping_option_type_id" text null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "shipping_option_pkey" primary key ("id"));');
|
||||||
this.addSql('alter table if exists "shipping_option" add constraint "shipping_option_shipping_option_type_id_unique" unique ("shipping_option_type_id");');
|
this.addSql('alter table if exists "shipping_option" add constraint "shipping_option_shipping_option_type_id_unique" unique ("shipping_option_type_id");');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_service_zone_id" ON "shipping_option" (service_zone_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_service_zone_id" ON "shipping_option" (service_zone_id) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_shipping_profile_id" ON "shipping_option" (shipping_profile_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_shipping_profile_id" ON "shipping_option" (shipping_profile_id) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_service_provider_id" ON "shipping_option" (service_provider_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_fulfillment_provider_id" ON "shipping_option" (fulfillment_provider_id) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_shipping_option_type_id" ON "shipping_option" (shipping_option_type_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_shipping_option_type_id" ON "shipping_option" (shipping_option_type_id) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_deleted_at" ON "shipping_option" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_deleted_at" ON "shipping_option" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
||||||
|
|
||||||
@@ -45,7 +44,8 @@ export class Migration20240221164918_InitialSetupMigration extends Migration {
|
|||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_rule_shipping_option_id" ON "shipping_option_rule" (shipping_option_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_rule_shipping_option_id" ON "shipping_option_rule" (shipping_option_id) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_rule_deleted_at" ON "shipping_option_rule" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_rule_deleted_at" ON "shipping_option_rule" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
||||||
|
|
||||||
this.addSql('create table if not exists "fulfillment" ("id" text not null, "location_id" text not null, "packed_at" timestamptz null, "shipped_at" timestamptz null, "delivered_at" timestamptz null, "canceled_at" timestamptz null, "data" jsonb null, "provider_id" text not null, "shipping_option_id" text null, "metadata" jsonb null, "delivery_address_id" text not null, "items_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "fulfillment_pkey" primary key ("id"));');
|
this.addSql('create table if not exists "fulfillment" ("id" text not null, "location_id" text not null, "packed_at" timestamptz null, "shipped_at" timestamptz null, "delivered_at" timestamptz null, "canceled_at" timestamptz null, "data" jsonb null, "provider_id" text not null, "shipping_option_id" text null, "metadata" jsonb null, "delivery_address_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "fulfillment_pkey" primary key ("id"));');
|
||||||
|
this.addSql('alter table if exists "fulfillment" add constraint "fulfillment_delivery_address_id_unique" unique ("delivery_address_id");');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_location_id" ON "fulfillment" (location_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_location_id" ON "fulfillment" (location_id) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_provider_id" ON "fulfillment" (provider_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_provider_id" ON "fulfillment" (provider_id) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_shipping_option_id" ON "fulfillment" (shipping_option_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_shipping_option_id" ON "fulfillment" (shipping_option_id) WHERE deleted_at IS NULL;');
|
||||||
@@ -55,7 +55,7 @@ export class Migration20240221164918_InitialSetupMigration extends Migration {
|
|||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_label_fulfillment_id" ON "fulfillment_label" (fulfillment_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_label_fulfillment_id" ON "fulfillment_label" (fulfillment_id) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_label_deleted_at" ON "fulfillment_label" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_label_deleted_at" ON "fulfillment_label" (deleted_at) WHERE deleted_at IS NOT NULL;');
|
||||||
|
|
||||||
this.addSql('create table if not exists "fulfillment_item" ("id" text not null, "title" text not null, "sku" text not null, "barcode" text not null, "quantity" numeric not null, "line_item_id" text null, "inventory_item_id" text null, "fulfillment_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "fulfillment_item_pkey" primary key ("id"));');
|
this.addSql('create table if not exists "fulfillment_item" ("id" text not null, "title" text not null, "sku" text not null, "barcode" text not null, "quantity" numeric not null, "raw_quantity" jsonb not null, "line_item_id" text null, "inventory_item_id" text null, "fulfillment_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "fulfillment_item_pkey" primary key ("id"));');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_item_line_item_id" ON "fulfillment_item" (line_item_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_item_line_item_id" ON "fulfillment_item" (line_item_id) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_item_inventory_item_id" ON "fulfillment_item" (inventory_item_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_item_inventory_item_id" ON "fulfillment_item" (inventory_item_id) WHERE deleted_at IS NULL;');
|
||||||
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_item_fulfillment_id" ON "fulfillment_item" (fulfillment_id) WHERE deleted_at IS NULL;');
|
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_item_fulfillment_id" ON "fulfillment_item" (fulfillment_id) WHERE deleted_at IS NULL;');
|
||||||
@@ -67,19 +67,18 @@ export class Migration20240221164918_InitialSetupMigration extends Migration {
|
|||||||
|
|
||||||
this.addSql('alter table if exists "shipping_option" add constraint "shipping_option_service_zone_id_foreign" foreign key ("service_zone_id") references "service_zone" ("id") on update cascade;');
|
this.addSql('alter table if exists "shipping_option" add constraint "shipping_option_service_zone_id_foreign" foreign key ("service_zone_id") references "service_zone" ("id") on update cascade;');
|
||||||
this.addSql('alter table if exists "shipping_option" add constraint "shipping_option_shipping_profile_id_foreign" foreign key ("shipping_profile_id") references "shipping_profile" ("id") on update cascade on delete set null;');
|
this.addSql('alter table if exists "shipping_option" add constraint "shipping_option_shipping_profile_id_foreign" foreign key ("shipping_profile_id") references "shipping_profile" ("id") on update cascade on delete set null;');
|
||||||
this.addSql('alter table if exists "shipping_option" add constraint "shipping_option_service_provider_id_foreign" foreign key ("service_provider_id") references "service_provider" ("id") on update cascade on delete set null;');
|
this.addSql('alter table if exists "shipping_option" add constraint "shipping_option_fulfillment_provider_id_foreign" foreign key ("fulfillment_provider_id") references "fulfillment_provider" ("id") on update cascade on delete set null;');
|
||||||
this.addSql('alter table if exists "shipping_option" add constraint "shipping_option_shipping_option_type_id_foreign" foreign key ("shipping_option_type_id") references "shipping_option_type" ("id") on update cascade on delete cascade;');
|
this.addSql('alter table if exists "shipping_option" add constraint "shipping_option_shipping_option_type_id_foreign" foreign key ("shipping_option_type_id") references "shipping_option_type" ("id") on update cascade on delete cascade;');
|
||||||
|
|
||||||
this.addSql('alter table if exists "shipping_option_rule" add constraint "shipping_option_rule_shipping_option_id_foreign" foreign key ("shipping_option_id") references "shipping_option" ("id") on update cascade;');
|
this.addSql('alter table if exists "shipping_option_rule" add constraint "shipping_option_rule_shipping_option_id_foreign" foreign key ("shipping_option_id") references "shipping_option" ("id") on update cascade;');
|
||||||
|
|
||||||
|
this.addSql('alter table if exists "fulfillment" add constraint "fulfillment_provider_id_foreign" foreign key ("provider_id") references "fulfillment_provider" ("id") on update cascade;');
|
||||||
this.addSql('alter table if exists "fulfillment" add constraint "fulfillment_shipping_option_id_foreign" foreign key ("shipping_option_id") references "shipping_option" ("id") on update cascade on delete set null;');
|
this.addSql('alter table if exists "fulfillment" add constraint "fulfillment_shipping_option_id_foreign" foreign key ("shipping_option_id") references "shipping_option" ("id") on update cascade on delete set null;');
|
||||||
this.addSql('alter table if exists "fulfillment" add constraint "fulfillment_provider_id_foreign" foreign key ("provider_id") references "service_provider" ("id") on update cascade;');
|
|
||||||
this.addSql('alter table if exists "fulfillment" add constraint "fulfillment_delivery_address_id_foreign" foreign key ("delivery_address_id") references "fulfillment_address" ("id") on update cascade;');
|
this.addSql('alter table if exists "fulfillment" add constraint "fulfillment_delivery_address_id_foreign" foreign key ("delivery_address_id") references "fulfillment_address" ("id") on update cascade;');
|
||||||
this.addSql('alter table if exists "fulfillment" add constraint "fulfillment_items_id_foreign" foreign key ("items_id") references "fulfillment_item" ("id") on update cascade;');
|
|
||||||
|
|
||||||
this.addSql('alter table if exists "fulfillment_label" add constraint "fulfillment_label_fulfillment_id_foreign" foreign key ("fulfillment_id") references "fulfillment" ("id") on update cascade;');
|
this.addSql('alter table if exists "fulfillment_label" add constraint "fulfillment_label_fulfillment_id_foreign" foreign key ("fulfillment_id") references "fulfillment" ("id") on update cascade on delete cascade;');
|
||||||
|
|
||||||
this.addSql('alter table if exists "fulfillment_item" add constraint "fulfillment_item_fulfillment_id_foreign" foreign key ("fulfillment_id") references "fulfillment" ("id") on update cascade;');
|
this.addSql('alter table if exists "fulfillment_item" add constraint "fulfillment_item_fulfillment_id_foreign" foreign key ("fulfillment_id") references "fulfillment" ("id") on update cascade on delete cascade;');
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -14,12 +14,6 @@ import {
|
|||||||
|
|
||||||
type OptionalAddressProps = DAL.SoftDeletableEntityDateColumns
|
type OptionalAddressProps = DAL.SoftDeletableEntityDateColumns
|
||||||
|
|
||||||
const FulfillmentIdIndex = createPsqlIndexStatementHelper({
|
|
||||||
tableName: "fulfillment_address",
|
|
||||||
columns: "fulfillment_id",
|
|
||||||
where: "deleted_at IS NULL",
|
|
||||||
})
|
|
||||||
|
|
||||||
const FulfillmentDeletedAtIndex = createPsqlIndexStatementHelper({
|
const FulfillmentDeletedAtIndex = createPsqlIndexStatementHelper({
|
||||||
tableName: "fulfillment_address",
|
tableName: "fulfillment_address",
|
||||||
columns: "deleted_at",
|
columns: "deleted_at",
|
||||||
@@ -33,10 +27,6 @@ export default class Address {
|
|||||||
@PrimaryKey({ columnType: "text" })
|
@PrimaryKey({ columnType: "text" })
|
||||||
id!: string
|
id!: string
|
||||||
|
|
||||||
@Property({ columnType: "text", nullable: true })
|
|
||||||
@FulfillmentIdIndex.MikroORMIndex()
|
|
||||||
fulfillment_id: string | null = null
|
|
||||||
|
|
||||||
@Property({ columnType: "text", nullable: true })
|
@Property({ columnType: "text", nullable: true })
|
||||||
company: string | null = null
|
company: string | null = null
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
|
BigNumber,
|
||||||
createPsqlIndexStatementHelper,
|
createPsqlIndexStatementHelper,
|
||||||
DALUtils,
|
DALUtils,
|
||||||
generateEntityId,
|
generateEntityId,
|
||||||
|
MikroOrmBigNumberProperty,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
|
|
||||||
import { DAL } from "@medusajs/types"
|
import { BigNumberRawValue, DAL } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
BeforeCreate,
|
BeforeCreate,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -60,8 +62,11 @@ export default class FulfillmentItem {
|
|||||||
@Property({ columnType: "text" })
|
@Property({ columnType: "text" })
|
||||||
barcode: string
|
barcode: string
|
||||||
|
|
||||||
@Property({ columnType: "numeric", serializer: Number })
|
@MikroOrmBigNumberProperty()
|
||||||
quantity: number // TODO: probably allow big numbers here
|
quantity: BigNumber | number
|
||||||
|
|
||||||
|
@Property({ columnType: "jsonb" })
|
||||||
|
raw_quantity: BigNumberRawValue
|
||||||
|
|
||||||
@Property({ columnType: "text", nullable: true })
|
@Property({ columnType: "text", nullable: true })
|
||||||
@LineItemIdIndex.MikroORMIndex()
|
@LineItemIdIndex.MikroORMIndex()
|
||||||
@@ -71,11 +76,16 @@ export default class FulfillmentItem {
|
|||||||
@InventoryItemIdIndex.MikroORMIndex()
|
@InventoryItemIdIndex.MikroORMIndex()
|
||||||
inventory_item_id: string | null = null
|
inventory_item_id: string | null = null
|
||||||
|
|
||||||
@Property({ columnType: "text" })
|
@ManyToOne(() => Fulfillment, {
|
||||||
|
columnType: "text",
|
||||||
|
mapToPk: true,
|
||||||
|
fieldName: "fulfillment_id",
|
||||||
|
onDelete: "cascade",
|
||||||
|
})
|
||||||
@FulfillmentIdIndex.MikroORMIndex()
|
@FulfillmentIdIndex.MikroORMIndex()
|
||||||
fulfillment_id: string
|
fulfillment_id: string
|
||||||
|
|
||||||
@ManyToOne(() => Fulfillment)
|
@ManyToOne(() => Fulfillment, { persist: false })
|
||||||
fulfillment: Fulfillment
|
fulfillment: Fulfillment
|
||||||
|
|
||||||
@Property({
|
@Property({
|
||||||
@@ -100,10 +110,12 @@ export default class FulfillmentItem {
|
|||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
onCreate() {
|
onCreate() {
|
||||||
this.id = generateEntityId(this.id, "fulit")
|
this.id = generateEntityId(this.id, "fulit")
|
||||||
|
this.fulfillment_id ??= this.fulfillment.id
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnInit()
|
@OnInit()
|
||||||
onInit() {
|
onInit() {
|
||||||
this.id = generateEntityId(this.id, "fulit")
|
this.id = generateEntityId(this.id, "fulit")
|
||||||
|
this.fulfillment_id ??= this.fulfillment.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -48,11 +48,16 @@ export default class FulfillmentLabel {
|
|||||||
@Property({ columnType: "text" })
|
@Property({ columnType: "text" })
|
||||||
label_url: string
|
label_url: string
|
||||||
|
|
||||||
@Property({ columnType: "text" })
|
@ManyToOne(() => Fulfillment, {
|
||||||
|
columnType: "text",
|
||||||
|
mapToPk: true,
|
||||||
|
fieldName: "fulfillment_id",
|
||||||
|
onDelete: "cascade",
|
||||||
|
})
|
||||||
@FulfillmentIdIndex.MikroORMIndex()
|
@FulfillmentIdIndex.MikroORMIndex()
|
||||||
fulfillment_id: string
|
fulfillment_id: string
|
||||||
|
|
||||||
@ManyToOne(() => Fulfillment)
|
@ManyToOne(() => Fulfillment, { persist: false })
|
||||||
fulfillment: Fulfillment
|
fulfillment: Fulfillment
|
||||||
|
|
||||||
@Property({
|
@Property({
|
||||||
@@ -77,10 +82,12 @@ export default class FulfillmentLabel {
|
|||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
onCreate() {
|
onCreate() {
|
||||||
this.id = generateEntityId(this.id, "fulla")
|
this.id = generateEntityId(this.id, "fulla")
|
||||||
|
this.fulfillment_id ??= this.fulfillment.id
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnInit()
|
@OnInit()
|
||||||
onInit() {
|
onInit() {
|
||||||
this.id = generateEntityId(this.id, "fulla")
|
this.id = generateEntityId(this.id, "fulla")
|
||||||
|
this.fulfillment_id ??= this.fulfillment.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+5
-5
@@ -18,18 +18,18 @@ import {
|
|||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import ShippingOption from "./shipping-option"
|
import ShippingOption from "./shipping-option"
|
||||||
|
|
||||||
type ServiceProviderOptionalProps = DAL.SoftDeletableEntityDateColumns
|
type FulfillmentProviderOptionalProps = DAL.SoftDeletableEntityDateColumns
|
||||||
|
|
||||||
const DeletedAtIndex = createPsqlIndexStatementHelper({
|
const DeletedAtIndex = createPsqlIndexStatementHelper({
|
||||||
tableName: "service_provider",
|
tableName: "fulfillment_provider",
|
||||||
columns: "deleted_at",
|
columns: "deleted_at",
|
||||||
where: "deleted_at IS NOT NULL",
|
where: "deleted_at IS NOT NULL",
|
||||||
})
|
})
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
|
||||||
export default class ServiceProvider {
|
export default class FulfillmentProvider {
|
||||||
[OptionalProps]?: ServiceProviderOptionalProps
|
[OptionalProps]?: FulfillmentProviderOptionalProps
|
||||||
|
|
||||||
@PrimaryKey({ columnType: "text" })
|
@PrimaryKey({ columnType: "text" })
|
||||||
id: string
|
id: string
|
||||||
@@ -39,7 +39,7 @@ export default class ServiceProvider {
|
|||||||
|
|
||||||
@OneToMany(
|
@OneToMany(
|
||||||
() => ShippingOption,
|
() => ShippingOption,
|
||||||
(shippingOption) => shippingOption.service_provider
|
(shippingOption) => shippingOption.fulfillment_provider
|
||||||
)
|
)
|
||||||
shipping_options = new Collection<ShippingOption>(this)
|
shipping_options = new Collection<ShippingOption>(this)
|
||||||
|
|
||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
Filter,
|
Filter,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
|
OneToOne,
|
||||||
OnInit,
|
OnInit,
|
||||||
OptionalProps,
|
OptionalProps,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
@@ -20,7 +21,7 @@ import {
|
|||||||
import Address from "./address"
|
import Address from "./address"
|
||||||
import FulfillmentItem from "./fulfillment-item"
|
import FulfillmentItem from "./fulfillment-item"
|
||||||
import FulfillmentLabel from "./fulfillment-label"
|
import FulfillmentLabel from "./fulfillment-label"
|
||||||
import ServiceProvider from "./service-provider"
|
import FulfillmentProvider from "./fulfillment-provider"
|
||||||
import ShippingOption from "./shipping-option"
|
import ShippingOption from "./shipping-option"
|
||||||
|
|
||||||
type FulfillmentOptionalProps = DAL.SoftDeletableEntityDateColumns
|
type FulfillmentOptionalProps = DAL.SoftDeletableEntityDateColumns
|
||||||
@@ -88,28 +89,37 @@ export default class Fulfillment {
|
|||||||
@Property({ columnType: "jsonb", nullable: true })
|
@Property({ columnType: "jsonb", nullable: true })
|
||||||
data: Record<string, unknown> | null = null
|
data: Record<string, unknown> | null = null
|
||||||
|
|
||||||
@Property({ columnType: "text" })
|
@ManyToOne(() => FulfillmentProvider, {
|
||||||
|
columnType: "text",
|
||||||
|
fieldName: "provider_id",
|
||||||
|
mapToPk: true,
|
||||||
|
})
|
||||||
@FulfillmentProviderIdIndex.MikroORMIndex()
|
@FulfillmentProviderIdIndex.MikroORMIndex()
|
||||||
provider_id: string
|
provider_id: string
|
||||||
|
|
||||||
@Property({ columnType: "text", nullable: true })
|
@ManyToOne(() => ShippingOption, {
|
||||||
|
columnType: "text",
|
||||||
|
fieldName: "shipping_option_id",
|
||||||
|
nullable: true,
|
||||||
|
mapToPk: true,
|
||||||
|
})
|
||||||
@FulfillmentShippingOptionIdIndex.MikroORMIndex()
|
@FulfillmentShippingOptionIdIndex.MikroORMIndex()
|
||||||
shipping_option_id: string | null = null
|
shipping_option_id: string | null = null
|
||||||
|
|
||||||
@Property({ columnType: "jsonb", nullable: true })
|
@Property({ columnType: "jsonb", nullable: true })
|
||||||
metadata: Record<string, unknown> | null = null
|
metadata: Record<string, unknown> | null = null
|
||||||
|
|
||||||
@ManyToOne(() => ShippingOption, { nullable: true })
|
@ManyToOne(() => ShippingOption, { persist: false })
|
||||||
shipping_option: ShippingOption | null
|
shipping_option: ShippingOption | null
|
||||||
|
|
||||||
@ManyToOne(() => ServiceProvider)
|
@ManyToOne(() => FulfillmentProvider, { persist: false })
|
||||||
provider: ServiceProvider
|
provider: FulfillmentProvider
|
||||||
|
|
||||||
@ManyToOne(() => Address)
|
@OneToOne()
|
||||||
delivery_address: Address
|
delivery_address!: Address
|
||||||
|
|
||||||
@ManyToOne(() => FulfillmentItem)
|
@OneToMany(() => FulfillmentItem, (item) => item.fulfillment)
|
||||||
items: FulfillmentItem
|
items = new Collection<FulfillmentItem>(this)
|
||||||
|
|
||||||
@OneToMany(() => FulfillmentLabel, (label) => label.fulfillment)
|
@OneToMany(() => FulfillmentLabel, (label) => label.fulfillment)
|
||||||
labels = new Collection<FulfillmentLabel>(this)
|
labels = new Collection<FulfillmentLabel>(this)
|
||||||
@@ -136,10 +146,12 @@ export default class Fulfillment {
|
|||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
onCreate() {
|
onCreate() {
|
||||||
this.id = generateEntityId(this.id, "ful")
|
this.id = generateEntityId(this.id, "ful")
|
||||||
|
this.provider_id ??= this.provider.id
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnInit()
|
@OnInit()
|
||||||
onInit() {
|
onInit() {
|
||||||
this.id = generateEntityId(this.id, "ful")
|
this.id = generateEntityId(this.id, "ful")
|
||||||
|
this.provider_id ??= this.provider.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,7 +5,7 @@ export { default as GeoZone } from "./geo-zone"
|
|||||||
export { default as ServiceZone } from "./service-zone"
|
export { default as ServiceZone } from "./service-zone"
|
||||||
export { default as FulfillmentItem } from "./fulfillment-item"
|
export { default as FulfillmentItem } from "./fulfillment-item"
|
||||||
export { default as FulfillmentLabel } from "./fulfillment-label"
|
export { default as FulfillmentLabel } from "./fulfillment-label"
|
||||||
export { default as ServiceProvider } from "./service-provider"
|
export { default as FulfillmentProvider } from "./fulfillment-provider"
|
||||||
export { default as ShippingOption } from "./shipping-option"
|
export { default as ShippingOption } from "./shipping-option"
|
||||||
export { default as ShippingOptionType } from "./shipping-option-type"
|
export { default as ShippingOptionType } from "./shipping-option-type"
|
||||||
export { default as ShippingOptionRule } from "./shipping-option-rule"
|
export { default as ShippingOptionRule } from "./shipping-option-rule"
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import {
|
|||||||
Property,
|
Property,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import Fulfillment from "./fulfillment"
|
import Fulfillment from "./fulfillment"
|
||||||
import ServiceProvider from "./service-provider"
|
import FulfillmentProvider from "./fulfillment-provider"
|
||||||
import ServiceZone from "./service-zone"
|
import ServiceZone from "./service-zone"
|
||||||
import ShippingOptionRule from "./shipping-option-rule"
|
import ShippingOptionRule from "./shipping-option-rule"
|
||||||
import ShippingOptionType from "./shipping-option-type"
|
import ShippingOptionType from "./shipping-option-type"
|
||||||
@@ -48,9 +48,9 @@ const ShippingProfileIdIndex = createPsqlIndexStatementHelper({
|
|||||||
where: "deleted_at IS NULL",
|
where: "deleted_at IS NULL",
|
||||||
})
|
})
|
||||||
|
|
||||||
const ServiceProviderIdIndex = createPsqlIndexStatementHelper({
|
const FulfillmentProviderIdIndex = createPsqlIndexStatementHelper({
|
||||||
tableName: "shipping_option",
|
tableName: "shipping_option",
|
||||||
columns: "service_provider_id",
|
columns: "fulfillment_provider_id",
|
||||||
where: "deleted_at IS NULL",
|
where: "deleted_at IS NULL",
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -94,14 +94,14 @@ export default class ShippingOption {
|
|||||||
@ShippingProfileIdIndex.MikroORMIndex()
|
@ShippingProfileIdIndex.MikroORMIndex()
|
||||||
shipping_profile_id: string | null
|
shipping_profile_id: string | null
|
||||||
|
|
||||||
@ManyToOne(() => ServiceProvider, {
|
@ManyToOne(() => FulfillmentProvider, {
|
||||||
type: "text",
|
type: "text",
|
||||||
fieldName: "service_provider_id",
|
fieldName: "fulfillment_provider_id",
|
||||||
mapToPk: true,
|
mapToPk: true,
|
||||||
nullable: true,
|
nullable: true,
|
||||||
})
|
})
|
||||||
@ServiceProviderIdIndex.MikroORMIndex()
|
@FulfillmentProviderIdIndex.MikroORMIndex()
|
||||||
service_provider_id: string
|
fulfillment_provider_id: string
|
||||||
|
|
||||||
@Property({ columnType: "text", persist: false })
|
@Property({ columnType: "text", persist: false })
|
||||||
@ShippingOptionTypeIdIndex.MikroORMIndex()
|
@ShippingOptionTypeIdIndex.MikroORMIndex()
|
||||||
@@ -121,10 +121,10 @@ export default class ShippingOption {
|
|||||||
})
|
})
|
||||||
shipping_profile: ShippingProfile | null
|
shipping_profile: ShippingProfile | null
|
||||||
|
|
||||||
@ManyToOne(() => ServiceProvider, {
|
@ManyToOne(() => FulfillmentProvider, {
|
||||||
persist: false,
|
persist: false,
|
||||||
})
|
})
|
||||||
service_provider: ServiceProvider | null
|
fulfillment_provider: FulfillmentProvider | null
|
||||||
|
|
||||||
@OneToOne(() => ShippingOptionType, (so) => so.shipping_option, {
|
@OneToOne(() => ShippingOptionType, (so) => so.shipping_option, {
|
||||||
owner: true,
|
owner: true,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import * as Models from "@models"
|
|||||||
import * as ModuleModels from "@models"
|
import * as ModuleModels from "@models"
|
||||||
import { ModulesSdkUtils } from "@medusajs/utils"
|
import { ModulesSdkUtils } from "@medusajs/utils"
|
||||||
import * as ModuleRepositories from "@repositories"
|
import * as ModuleRepositories from "@repositories"
|
||||||
|
import loadProviders from "./loaders/providers"
|
||||||
|
|
||||||
const migrationScriptOptions = {
|
const migrationScriptOptions = {
|
||||||
moduleName: Modules.FULFILLMENT,
|
moduleName: Modules.FULFILLMENT,
|
||||||
@@ -34,7 +35,7 @@ const connectionLoader = ModulesSdkUtils.mikroOrmConnectionLoaderFactory({
|
|||||||
})
|
})
|
||||||
|
|
||||||
const service = FulfillmentModuleService
|
const service = FulfillmentModuleService
|
||||||
const loaders = [containerLoader, connectionLoader]
|
const loaders = [containerLoader, connectionLoader, loadProviders]
|
||||||
|
|
||||||
export const moduleDefinition: ModuleExports = {
|
export const moduleDefinition: ModuleExports = {
|
||||||
service,
|
service,
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
Context,
|
Context,
|
||||||
DAL,
|
DAL,
|
||||||
|
FilterableFulfillmentSetProps,
|
||||||
FilterableShippingOptionProps,
|
FilterableShippingOptionProps,
|
||||||
FilterQuery,
|
FilterQuery,
|
||||||
FindConfig,
|
FindConfig,
|
||||||
|
FulfillmentDTO,
|
||||||
FulfillmentTypes,
|
FulfillmentTypes,
|
||||||
IFulfillmentModuleService,
|
IFulfillmentModuleService,
|
||||||
InternalModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
@@ -25,6 +27,7 @@ import {
|
|||||||
|
|
||||||
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
|
||||||
import {
|
import {
|
||||||
|
Fulfillment,
|
||||||
FulfillmentSet,
|
FulfillmentSet,
|
||||||
GeoZone,
|
GeoZone,
|
||||||
ServiceZone,
|
ServiceZone,
|
||||||
@@ -34,6 +37,7 @@ import {
|
|||||||
ShippingProfile,
|
ShippingProfile,
|
||||||
} from "@models"
|
} from "@models"
|
||||||
import { isContextValid, validateRules } from "@utils"
|
import { isContextValid, validateRules } from "@utils"
|
||||||
|
import FulfillmentProviderService from "./fulfillment-provider"
|
||||||
|
|
||||||
const generateMethodForModels = [
|
const generateMethodForModels = [
|
||||||
ServiceZone,
|
ServiceZone,
|
||||||
@@ -42,6 +46,7 @@ const generateMethodForModels = [
|
|||||||
ShippingProfile,
|
ShippingProfile,
|
||||||
ShippingOptionRule,
|
ShippingOptionRule,
|
||||||
ShippingOptionType,
|
ShippingOptionType,
|
||||||
|
// Not adding Fulfillment to not auto generate the methods under the hood and only provide the methods we want to expose8
|
||||||
]
|
]
|
||||||
|
|
||||||
type InjectedDependencies = {
|
type InjectedDependencies = {
|
||||||
@@ -53,6 +58,8 @@ type InjectedDependencies = {
|
|||||||
shippingOptionService: ModulesSdkTypes.InternalModuleService<any>
|
shippingOptionService: ModulesSdkTypes.InternalModuleService<any>
|
||||||
shippingOptionRuleService: ModulesSdkTypes.InternalModuleService<any>
|
shippingOptionRuleService: ModulesSdkTypes.InternalModuleService<any>
|
||||||
shippingOptionTypeService: ModulesSdkTypes.InternalModuleService<any>
|
shippingOptionTypeService: ModulesSdkTypes.InternalModuleService<any>
|
||||||
|
fulfillmentProviderService: FulfillmentProviderService
|
||||||
|
fulfillmentService: ModulesSdkTypes.InternalModuleService<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class FulfillmentModuleService<
|
export default class FulfillmentModuleService<
|
||||||
@@ -62,7 +69,8 @@ export default class FulfillmentModuleService<
|
|||||||
TShippingProfileEntity extends ShippingProfile = ShippingProfile,
|
TShippingProfileEntity extends ShippingProfile = ShippingProfile,
|
||||||
TShippingOptionEntity extends ShippingOption = ShippingOption,
|
TShippingOptionEntity extends ShippingOption = ShippingOption,
|
||||||
TShippingOptionRuleEntity extends ShippingOptionRule = ShippingOptionRule,
|
TShippingOptionRuleEntity extends ShippingOptionRule = ShippingOptionRule,
|
||||||
TSippingOptionTypeEntity extends ShippingOptionType = ShippingOptionType
|
TSippingOptionTypeEntity extends ShippingOptionType = ShippingOptionType,
|
||||||
|
TFulfillmentEntity extends Fulfillment = Fulfillment
|
||||||
>
|
>
|
||||||
extends ModulesSdkUtils.abstractModuleServiceFactory<
|
extends ModulesSdkUtils.abstractModuleServiceFactory<
|
||||||
InjectedDependencies,
|
InjectedDependencies,
|
||||||
@@ -87,6 +95,8 @@ export default class FulfillmentModuleService<
|
|||||||
protected readonly shippingOptionService_: ModulesSdkTypes.InternalModuleService<TShippingOptionEntity>
|
protected readonly shippingOptionService_: ModulesSdkTypes.InternalModuleService<TShippingOptionEntity>
|
||||||
protected readonly shippingOptionRuleService_: ModulesSdkTypes.InternalModuleService<TShippingOptionRuleEntity>
|
protected readonly shippingOptionRuleService_: ModulesSdkTypes.InternalModuleService<TShippingOptionRuleEntity>
|
||||||
protected readonly shippingOptionTypeService_: ModulesSdkTypes.InternalModuleService<TSippingOptionTypeEntity>
|
protected readonly shippingOptionTypeService_: ModulesSdkTypes.InternalModuleService<TSippingOptionTypeEntity>
|
||||||
|
protected readonly fulfillmentProviderService_: FulfillmentProviderService
|
||||||
|
protected readonly fulfillmentService_: ModulesSdkTypes.InternalModuleService<TFulfillmentEntity>
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
{
|
{
|
||||||
@@ -98,6 +108,8 @@ export default class FulfillmentModuleService<
|
|||||||
shippingOptionService,
|
shippingOptionService,
|
||||||
shippingOptionRuleService,
|
shippingOptionRuleService,
|
||||||
shippingOptionTypeService,
|
shippingOptionTypeService,
|
||||||
|
fulfillmentProviderService,
|
||||||
|
fulfillmentService,
|
||||||
}: InjectedDependencies,
|
}: InjectedDependencies,
|
||||||
protected readonly moduleDeclaration: InternalModuleDeclaration
|
protected readonly moduleDeclaration: InternalModuleDeclaration
|
||||||
) {
|
) {
|
||||||
@@ -111,6 +123,8 @@ export default class FulfillmentModuleService<
|
|||||||
this.shippingOptionService_ = shippingOptionService
|
this.shippingOptionService_ = shippingOptionService
|
||||||
this.shippingOptionRuleService_ = shippingOptionRuleService
|
this.shippingOptionRuleService_ = shippingOptionRuleService
|
||||||
this.shippingOptionTypeService_ = shippingOptionTypeService
|
this.shippingOptionTypeService_ = shippingOptionTypeService
|
||||||
|
this.fulfillmentProviderService_ = fulfillmentProviderService
|
||||||
|
this.fulfillmentService_ = fulfillmentService
|
||||||
}
|
}
|
||||||
|
|
||||||
__joinerConfig(): ModuleJoinerConfig {
|
__joinerConfig(): ModuleJoinerConfig {
|
||||||
@@ -129,7 +143,7 @@ export default class FulfillmentModuleService<
|
|||||||
"rules",
|
"rules",
|
||||||
"type",
|
"type",
|
||||||
"shipping_profile",
|
"shipping_profile",
|
||||||
"service_provider",
|
"fulfillment_provider",
|
||||||
...(normalizedConfig.relations ?? []),
|
...(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.
|
// 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.
|
||||||
@@ -173,7 +187,7 @@ export default class FulfillmentModuleService<
|
|||||||
async listShippingOptions(
|
async listShippingOptions(
|
||||||
filters: FilterableShippingOptionProps = {},
|
filters: FilterableShippingOptionProps = {},
|
||||||
config: FindConfig<ShippingOptionDTO> = {},
|
config: FindConfig<ShippingOptionDTO> = {},
|
||||||
sharedContext?: Context
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<FulfillmentTypes.ShippingOptionDTO[]> {
|
): Promise<FulfillmentTypes.ShippingOptionDTO[]> {
|
||||||
const {
|
const {
|
||||||
filters: normalizedFilters,
|
filters: normalizedFilters,
|
||||||
@@ -211,6 +225,76 @@ export default class FulfillmentModuleService<
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async retrieveFulfillment(
|
||||||
|
id: string,
|
||||||
|
config: FindConfig<FulfillmentTypes.FulfillmentDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<FulfillmentTypes.FulfillmentDTO> {
|
||||||
|
const fulfillment = await this.fulfillmentService_.retrieve(
|
||||||
|
id,
|
||||||
|
config,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>(
|
||||||
|
fulfillment,
|
||||||
|
{
|
||||||
|
populate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrieveFulfillmentOptions(
|
||||||
|
providerId: string
|
||||||
|
): Promise<Record<string, any>[]> {
|
||||||
|
return await this.fulfillmentProviderService_.getFulfillmentOptions(
|
||||||
|
providerId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async listFulfillments(
|
||||||
|
filters: FulfillmentTypes.FilterableFulfillmentProps = {},
|
||||||
|
config: FindConfig<FulfillmentTypes.FulfillmentDTO> = {},
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<FulfillmentTypes.FulfillmentDTO[]> {
|
||||||
|
const fulfillments = await this.fulfillmentService_.list(
|
||||||
|
filters,
|
||||||
|
config,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return await this.baseRepository_.serialize<
|
||||||
|
FulfillmentTypes.FulfillmentDTO[]
|
||||||
|
>(fulfillments, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async listAndCountFulfillments(
|
||||||
|
filters?: FilterableFulfillmentSetProps,
|
||||||
|
config?: FindConfig<FulfillmentDTO>,
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<[FulfillmentDTO[], number]> {
|
||||||
|
const [fulfillments, count] = await this.fulfillmentService_.listAndCount(
|
||||||
|
filters,
|
||||||
|
config,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
return [
|
||||||
|
await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO[]>(
|
||||||
|
fulfillments,
|
||||||
|
{
|
||||||
|
populate: true,
|
||||||
|
}
|
||||||
|
),
|
||||||
|
count,
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
create(
|
create(
|
||||||
data: FulfillmentTypes.CreateFulfillmentSetDTO[],
|
data: FulfillmentTypes.CreateFulfillmentSetDTO[],
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
@@ -504,6 +588,55 @@ export default class FulfillmentModuleService<
|
|||||||
: createdShippingOptionRules[0]
|
: createdShippingOptionRules[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async createFulfillment(
|
||||||
|
data: FulfillmentTypes.CreateFulfillmentDTO,
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<FulfillmentTypes.FulfillmentDTO> {
|
||||||
|
const { order, ...fulfillmentDataToCreate } = data
|
||||||
|
|
||||||
|
const fulfillment = await this.fulfillmentService_.create(
|
||||||
|
fulfillmentDataToCreate,
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
const {
|
||||||
|
items,
|
||||||
|
data: fulfillmentData,
|
||||||
|
provider_id,
|
||||||
|
...fulfillmentRest
|
||||||
|
} = fulfillment
|
||||||
|
|
||||||
|
let fulfillmentThirdPartyData!: any
|
||||||
|
try {
|
||||||
|
fulfillmentThirdPartyData =
|
||||||
|
await this.fulfillmentProviderService_.createFulfillment(
|
||||||
|
provider_id,
|
||||||
|
fulfillmentData || {},
|
||||||
|
items.map((i) => i),
|
||||||
|
order,
|
||||||
|
fulfillmentRest
|
||||||
|
)
|
||||||
|
await this.fulfillmentService_.update(
|
||||||
|
{
|
||||||
|
id: fulfillment.id,
|
||||||
|
data: fulfillmentThirdPartyData ?? {},
|
||||||
|
},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
await this.fulfillmentService_.delete(fulfillment.id, sharedContext)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>(
|
||||||
|
fulfillment,
|
||||||
|
{
|
||||||
|
populate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
update(
|
update(
|
||||||
data: FulfillmentTypes.UpdateFulfillmentSetDTO[],
|
data: FulfillmentTypes.UpdateFulfillmentSetDTO[],
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
@@ -1074,6 +1207,88 @@ export default class FulfillmentModuleService<
|
|||||||
: updatedShippingOptionRules[0]
|
: updatedShippingOptionRules[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@InjectTransactionManager("baseRepository_")
|
||||||
|
async updateFulfillment(
|
||||||
|
id: string,
|
||||||
|
data: FulfillmentTypes.UpdateFulfillmentDTO,
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<FulfillmentTypes.FulfillmentDTO> {
|
||||||
|
const fulfillment = await this.fulfillmentService_.update(
|
||||||
|
{ id, ...data },
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
const serialized =
|
||||||
|
await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>(
|
||||||
|
fulfillment,
|
||||||
|
{
|
||||||
|
populate: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return Array.isArray(serialized) ? serialized[0] : serialized
|
||||||
|
}
|
||||||
|
|
||||||
|
@InjectManager("baseRepository_")
|
||||||
|
async cancelFulfillment(
|
||||||
|
id: string,
|
||||||
|
@MedusaContext() sharedContext: Context = {}
|
||||||
|
): Promise<FulfillmentDTO> {
|
||||||
|
const canceledAt = new Date()
|
||||||
|
|
||||||
|
let fulfillment = await this.fulfillmentService_.retrieve(
|
||||||
|
id,
|
||||||
|
{},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
|
||||||
|
FulfillmentModuleService.canCancelFulfillmentOrThrow(fulfillment)
|
||||||
|
|
||||||
|
// Make this action idempotent
|
||||||
|
if (!fulfillment.canceled_at) {
|
||||||
|
try {
|
||||||
|
await this.fulfillmentProviderService_.cancelFulfillment(
|
||||||
|
fulfillment.provider_id,
|
||||||
|
fulfillment.data ?? {}
|
||||||
|
)
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
|
||||||
|
fulfillment = await this.fulfillmentService_.update(
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
canceled_at: canceledAt,
|
||||||
|
},
|
||||||
|
sharedContext
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await this.baseRepository_.serialize(fulfillment, {
|
||||||
|
populate: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
return Array.isArray(result) ? result[0] : result
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static canCancelFulfillmentOrThrow(fulfillment: Fulfillment) {
|
||||||
|
if (fulfillment.shipped_at) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Fulfillment with id ${fulfillment.id} already shipped`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fulfillment.delivered_at) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.INVALID_DATA,
|
||||||
|
`Fulfillment with id ${fulfillment.id} already delivered`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
protected static validateMissingShippingOptions_(
|
protected static validateMissingShippingOptions_(
|
||||||
shippingOptions: ShippingOption[],
|
shippingOptions: ShippingOption[],
|
||||||
shippingOptionsData: FulfillmentTypes.UpdateShippingOptionDTO[]
|
shippingOptionsData: FulfillmentTypes.UpdateShippingOptionDTO[]
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
import {
|
||||||
|
Constructor,
|
||||||
|
DAL,
|
||||||
|
FulfillmentTypes,
|
||||||
|
IFulfillmentProvider,
|
||||||
|
} from "@medusajs/types"
|
||||||
|
import { ModulesSdkUtils, promiseAll } from "@medusajs/utils"
|
||||||
|
import { MedusaError } from "medusa-core-utils"
|
||||||
|
import { FulfillmentProvider } from "@models"
|
||||||
|
|
||||||
|
type InjectedDependencies = {
|
||||||
|
fulfillmentProviderRepository: DAL.RepositoryService
|
||||||
|
[key: `fp_${string}`]: FulfillmentTypes.IFulfillmentProvider
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO rework DTO's
|
||||||
|
|
||||||
|
export default class FulfillmentProviderService extends ModulesSdkUtils.internalModuleServiceFactory<InjectedDependencies>(
|
||||||
|
FulfillmentProvider
|
||||||
|
) {
|
||||||
|
protected readonly fulfillmentProviderRepository_: DAL.RepositoryService
|
||||||
|
|
||||||
|
constructor(container: InjectedDependencies) {
|
||||||
|
super(container)
|
||||||
|
this.fulfillmentProviderRepository_ =
|
||||||
|
container.fulfillmentProviderRepository
|
||||||
|
}
|
||||||
|
|
||||||
|
static getRegistrationIdentifier(
|
||||||
|
providerClass: Constructor<IFulfillmentProvider>,
|
||||||
|
optionName?: string
|
||||||
|
) {
|
||||||
|
return `${(providerClass as any).identifier}_${optionName}`
|
||||||
|
}
|
||||||
|
|
||||||
|
protected retrieveProviderRegistration(
|
||||||
|
providerId: string
|
||||||
|
): FulfillmentTypes.IFulfillmentProvider {
|
||||||
|
try {
|
||||||
|
return this.__container__[`fp_${providerId}`]
|
||||||
|
} catch (err) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_FOUND,
|
||||||
|
`Could not find a fulfillment provider with id: ${providerId}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async listFulfillmentOptions(providerIds: string[]): Promise<any[]> {
|
||||||
|
return await promiseAll(
|
||||||
|
providerIds.map(async (p) => {
|
||||||
|
const provider = this.retrieveProviderRegistration(p)
|
||||||
|
return {
|
||||||
|
provider_id: p,
|
||||||
|
options: (await provider.getFulfillmentOptions()) as Record<
|
||||||
|
string,
|
||||||
|
unknown
|
||||||
|
>[],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFulfillmentOptions(
|
||||||
|
providerId: string
|
||||||
|
): Promise<Record<string, unknown>[]> {
|
||||||
|
const provider = this.retrieveProviderRegistration(providerId)
|
||||||
|
return await provider.getFulfillmentOptions()
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateFulfillmentData(
|
||||||
|
providerId: string,
|
||||||
|
optionData: Record<string, unknown>,
|
||||||
|
data: Record<string, unknown>,
|
||||||
|
context: Record<string, unknown>
|
||||||
|
) {
|
||||||
|
const provider = this.retrieveProviderRegistration(providerId)
|
||||||
|
return await provider.validateFulfillmentData(optionData, data, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateOption(data: any) {
|
||||||
|
const provider = this.retrieveProviderRegistration(data.provider_id)
|
||||||
|
return await provider.validateOption(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
async createFulfillment(
|
||||||
|
providerId: string,
|
||||||
|
data: object,
|
||||||
|
items: object[],
|
||||||
|
order: object,
|
||||||
|
fulfillment: Record<string, unknown>
|
||||||
|
): Promise<Record<string, unknown>> {
|
||||||
|
const provider = this.retrieveProviderRegistration(providerId)
|
||||||
|
return await provider.createFulfillment(data, items, order, fulfillment)
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelFulfillment(
|
||||||
|
providerId: string,
|
||||||
|
fulfillment: Record<string, unknown>
|
||||||
|
): Promise<any> {
|
||||||
|
const provider = this.retrieveProviderRegistration(providerId)
|
||||||
|
return await provider.cancelFulfillment(fulfillment)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
export { default as FulfillmentModuleService } from "./fulfillment-module-service"
|
export { default as FulfillmentModuleService } from "./fulfillment-module-service"
|
||||||
|
export { default as FulfillmentProviderService } from "./fulfillment-provider"
|
||||||
@@ -4,3 +4,6 @@ export type InitializeModuleInjectableDependencies = {
|
|||||||
logger?: Logger
|
logger?: Logger
|
||||||
eventBusService?: IEventBusModuleService
|
eventBusService?: IEventBusModuleService
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const FulfillmentIdentifiersRegistrationName =
|
||||||
|
"fulfillment_providers_identifier"
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
|
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
|
||||||
import { initModules, InitModulesOptions } from "./init-modules"
|
import { initModules, InitModulesOptions } from "./init-modules"
|
||||||
import { MedusaAppOutput, ModulesDefinition } from "@medusajs/modules-sdk"
|
import {
|
||||||
|
MedusaAppOutput,
|
||||||
|
MedusaModuleConfig,
|
||||||
|
ModulesDefinition
|
||||||
|
} from "@medusajs/modules-sdk"
|
||||||
import { getDatabaseURL, getMikroOrmWrapper, TestDatabase } from "./database"
|
import { getDatabaseURL, getMikroOrmWrapper, TestDatabase } from "./database"
|
||||||
|
|
||||||
import { MockEventBusService } from "."
|
import { MockEventBusService } from "."
|
||||||
@@ -18,6 +22,7 @@ export interface SuiteOptions<TService = unknown> {
|
|||||||
export function moduleIntegrationTestRunner({
|
export function moduleIntegrationTestRunner({
|
||||||
moduleName,
|
moduleName,
|
||||||
moduleModels,
|
moduleModels,
|
||||||
|
moduleOptions = {},
|
||||||
joinerConfig = [],
|
joinerConfig = [],
|
||||||
schema = "public",
|
schema = "public",
|
||||||
debug = false,
|
debug = false,
|
||||||
@@ -26,6 +31,7 @@ export function moduleIntegrationTestRunner({
|
|||||||
}: {
|
}: {
|
||||||
moduleName: string
|
moduleName: string
|
||||||
moduleModels?: any[]
|
moduleModels?: any[]
|
||||||
|
moduleOptions?: Record<string, any>
|
||||||
joinerConfig?: any[]
|
joinerConfig?: any[]
|
||||||
schema?: string
|
schema?: string
|
||||||
dbName?: string
|
dbName?: string
|
||||||
@@ -62,11 +68,12 @@ export function moduleIntegrationTestRunner({
|
|||||||
database: dbConfig,
|
database: dbConfig,
|
||||||
},
|
},
|
||||||
database: dbConfig,
|
database: dbConfig,
|
||||||
|
...moduleOptions,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const moduleOptions: InitModulesOptions = {
|
const moduleOptions_: InitModulesOptions = {
|
||||||
injectedDependencies: {
|
injectedDependencies: {
|
||||||
[ContainerRegistrationKeys.PG_CONNECTION]: connection,
|
[ContainerRegistrationKeys.PG_CONNECTION]: connection,
|
||||||
eventBusService: new MockEventBusService(),
|
eventBusService: new MockEventBusService(),
|
||||||
@@ -106,7 +113,7 @@ export function moduleIntegrationTestRunner({
|
|||||||
const beforeEach_ = async () => {
|
const beforeEach_ = async () => {
|
||||||
try {
|
try {
|
||||||
await MikroOrmWrapper.setupDatabase()
|
await MikroOrmWrapper.setupDatabase()
|
||||||
const output = await initModules(moduleOptions)
|
const output = await initModules(moduleOptions_)
|
||||||
shutdown = output.shutdown
|
shutdown = output.shutdown
|
||||||
medusaApp = output.medusaApp
|
medusaApp = output.medusaApp
|
||||||
moduleService = output.medusaApp.modules[moduleName]
|
moduleService = output.medusaApp.modules[moduleName]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { ServiceProviderDTO } from "./service-provider"
|
|||||||
import { FulfillmentAddressDTO } from "./address"
|
import { FulfillmentAddressDTO } from "./address"
|
||||||
import { FulfillmentItemDTO } from "./fulfillment-item"
|
import { FulfillmentItemDTO } from "./fulfillment-item"
|
||||||
import { FulfillmentLabelDTO } from "./fulfillment-label"
|
import { FulfillmentLabelDTO } from "./fulfillment-label"
|
||||||
|
import { BaseFilterable, OperatorMap } from "../../dal"
|
||||||
|
|
||||||
export interface FulfillmentDTO {
|
export interface FulfillmentDTO {
|
||||||
id: string
|
id: string
|
||||||
@@ -24,3 +25,17 @@ export interface FulfillmentDTO {
|
|||||||
updated_at: Date
|
updated_at: Date
|
||||||
deleted_at: Date | null
|
deleted_at: Date | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FilterableFulfillmentProps
|
||||||
|
extends BaseFilterable<FilterableFulfillmentProps> {
|
||||||
|
id?: string | string[] | OperatorMap<string | string[]>
|
||||||
|
location_id?: string | string[] | OperatorMap<string | string[]>
|
||||||
|
packed_at?: Date | OperatorMap<string | string[]>
|
||||||
|
shipped_at?: Date | OperatorMap<string | string[]>
|
||||||
|
delivered_at?: Date | OperatorMap<string | string[]>
|
||||||
|
canceled_at?: Date | OperatorMap<string | string[]>
|
||||||
|
provider_id?: string | string[] | OperatorMap<string | string[]>
|
||||||
|
shipping_option_id?: string | null
|
||||||
|
created_at?: Date | OperatorMap<string | string[]>
|
||||||
|
updated_at?: Date | OperatorMap<string | string[]>
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
export * from "./common"
|
export * from "./common"
|
||||||
export * from "./mutations"
|
export * from "./mutations"
|
||||||
export * from "./service"
|
export * from "./service"
|
||||||
|
export * from "./provider"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
export interface CreateFulfillmentAddressDTO {
|
||||||
|
fulfillment_id: string
|
||||||
|
company?: string | null
|
||||||
|
first_name?: string | null
|
||||||
|
last_name?: string | null
|
||||||
|
address_1?: string | null
|
||||||
|
address_2?: string | null
|
||||||
|
city?: string | null
|
||||||
|
country_code?: string | null
|
||||||
|
province?: string | null
|
||||||
|
postal_code?: string | null
|
||||||
|
phone?: string | null
|
||||||
|
metadata?: Record<string, unknown> | null
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
export interface CreateFulfillmentItemDTO {
|
||||||
|
fulfillment_id: string
|
||||||
|
title: string
|
||||||
|
sku: string
|
||||||
|
quantity: number
|
||||||
|
barcode: string
|
||||||
|
line_item_id?: string | null
|
||||||
|
inventory_item_id?: string | null
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
export interface CreateFulfillmentLabelDTO {
|
||||||
|
tracking_number: string
|
||||||
|
tracking_url: string
|
||||||
|
label_url: string
|
||||||
|
fulfillment_id: string
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { CreateFulfillmentAddressDTO } from "./fulfillment-address"
|
||||||
|
import { CreateFulfillmentItemDTO } from "./fulfillment-item"
|
||||||
|
import { CreateFulfillmentLabelDTO } from "./fulfillment-label"
|
||||||
|
|
||||||
|
export interface CreateFulfillmentOrderDTO {}
|
||||||
|
|
||||||
|
export interface CreateFulfillmentDTO {
|
||||||
|
location_id: string
|
||||||
|
packed_at?: Date | null
|
||||||
|
shipped_at?: Date | null
|
||||||
|
delivered_at?: Date | null
|
||||||
|
canceled_at?: Date | null
|
||||||
|
data?: Record<string, unknown> | null
|
||||||
|
provider_id: string
|
||||||
|
shipping_option_id?: string | null
|
||||||
|
metadata?: Record<string, unknown> | null
|
||||||
|
delivery_address: Omit<CreateFulfillmentAddressDTO, "fulfillment_id">
|
||||||
|
items: Omit<CreateFulfillmentItemDTO, "fulfillment_id">[]
|
||||||
|
labels: Omit<CreateFulfillmentLabelDTO, "fulfillment_id">[]
|
||||||
|
order: CreateFulfillmentOrderDTO
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateFulfillmentDTO {
|
||||||
|
location_id?: string
|
||||||
|
packed_at?: Date | null
|
||||||
|
shipped_at?: Date | null
|
||||||
|
delivered_at?: Date | null
|
||||||
|
data?: Record<string, unknown> | null
|
||||||
|
metadata?: Record<string, unknown> | null
|
||||||
|
}
|
||||||
@@ -5,3 +5,7 @@ export * from "./geo-zone"
|
|||||||
export * from "./service-zone"
|
export * from "./service-zone"
|
||||||
export * from "./shipping-option"
|
export * from "./shipping-option"
|
||||||
export * from "./fulfillment-set"
|
export * from "./fulfillment-set"
|
||||||
|
export * from "./fulfillment"
|
||||||
|
export * from "./fulfillment-address"
|
||||||
|
export * from "./fulfillment-label"
|
||||||
|
export * from "./fulfillment-item"
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
export interface IFulfillmentProvider {
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Return a unique identifier to retrieve the fulfillment plugin provider
|
||||||
|
*/
|
||||||
|
getIdentifier(): string
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Return the available fulfillment options for the given data.
|
||||||
|
*/
|
||||||
|
getFulfillmentOptions(): Promise<Record<string, unknown>[]>
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Validate the given fulfillment data.
|
||||||
|
*/
|
||||||
|
validateFulfillmentData(
|
||||||
|
optionData: Record<string, unknown>,
|
||||||
|
data: Record<string, unknown>,
|
||||||
|
context: Record<string, unknown>
|
||||||
|
): Promise<any>
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Validate the given option.
|
||||||
|
*/
|
||||||
|
validateOption(data: Record<string, unknown>): Promise<boolean>
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Check if the provider can calculate the fulfillment price.
|
||||||
|
*/
|
||||||
|
canCalculate(data: Record<string, unknown>): Promise<any>
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Calculate the price for the given fulfillment option.
|
||||||
|
*/
|
||||||
|
calculatePrice(
|
||||||
|
optionData: Record<string, unknown>,
|
||||||
|
data: Record<string, unknown>,
|
||||||
|
context: Record<string, unknown>
|
||||||
|
): Promise<any>
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Create a fulfillment for the given data.
|
||||||
|
*/
|
||||||
|
createFulfillment(
|
||||||
|
data: object,
|
||||||
|
items: object[],
|
||||||
|
order: object,
|
||||||
|
fulfillment: Record<string, unknown>
|
||||||
|
): Promise<Record<string, unknown>>
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Cancel the given fulfillment.
|
||||||
|
*/
|
||||||
|
cancelFulfillment(fulfillment: Record<string, unknown>): Promise<any>
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Get the documents for the given fulfillment data.
|
||||||
|
*/
|
||||||
|
getFulfillmentDocuments(data: Record<string, unknown>): Promise<any>
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Create a return for the given data.
|
||||||
|
*/
|
||||||
|
createReturnFulfillment(fromData: Record<string, unknown>): Promise<any>
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Get the documents for the given return data.
|
||||||
|
*/
|
||||||
|
retrieveDocuments(
|
||||||
|
fulfillmentData: Record<string, unknown>,
|
||||||
|
documentType: string
|
||||||
|
): Promise<any>
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Get the documents for the given return data.
|
||||||
|
*/
|
||||||
|
getReturnDocuments(data: Record<string, unknown>): Promise<any>
|
||||||
|
/**
|
||||||
|
* @ignore
|
||||||
|
*
|
||||||
|
* Get the documents for the given shipment data.
|
||||||
|
*/
|
||||||
|
getShipmentDocuments(data: Record<string, unknown>): Promise<any>
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
FilterableShippingOptionRuleProps,
|
FilterableShippingOptionRuleProps,
|
||||||
FilterableShippingOptionTypeProps,
|
FilterableShippingOptionTypeProps,
|
||||||
FilterableShippingProfileProps,
|
FilterableShippingProfileProps,
|
||||||
|
FulfillmentDTO,
|
||||||
FulfillmentSetDTO,
|
FulfillmentSetDTO,
|
||||||
GeoZoneDTO,
|
GeoZoneDTO,
|
||||||
ServiceZoneDTO,
|
ServiceZoneDTO,
|
||||||
@@ -24,6 +25,7 @@ import {
|
|||||||
CreateServiceZoneDTO,
|
CreateServiceZoneDTO,
|
||||||
CreateShippingOptionDTO,
|
CreateShippingOptionDTO,
|
||||||
CreateShippingOptionRuleDTO,
|
CreateShippingOptionRuleDTO,
|
||||||
|
UpdateFulfillmentDTO,
|
||||||
UpdateFulfillmentSetDTO,
|
UpdateFulfillmentSetDTO,
|
||||||
UpdateGeoZoneDTO,
|
UpdateGeoZoneDTO,
|
||||||
UpdateServiceZoneDTO,
|
UpdateServiceZoneDTO,
|
||||||
@@ -31,6 +33,7 @@ import {
|
|||||||
UpdateShippingOptionRuleDTO,
|
UpdateShippingOptionRuleDTO,
|
||||||
} from "./mutations"
|
} from "./mutations"
|
||||||
import { CreateShippingProfileDTO } from "./mutations/shipping-profile"
|
import { CreateShippingProfileDTO } from "./mutations/shipping-profile"
|
||||||
|
import { CreateFulfillmentDTO } from "./mutations/fulfillment"
|
||||||
|
|
||||||
export interface IFulfillmentModuleService extends IModuleService {
|
export interface IFulfillmentModuleService extends IModuleService {
|
||||||
/**
|
/**
|
||||||
@@ -202,6 +205,17 @@ export interface IFulfillmentModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<ShippingOptionRuleDTO>
|
): Promise<ShippingOptionRuleDTO>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update a fulfillment
|
||||||
|
* @param data
|
||||||
|
* @param sharedContext
|
||||||
|
*/
|
||||||
|
updateFulfillment(
|
||||||
|
id: string,
|
||||||
|
data: UpdateFulfillmentDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<FulfillmentDTO>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete a fulfillment set
|
* Delete a fulfillment set
|
||||||
* @param ids
|
* @param ids
|
||||||
@@ -337,6 +351,18 @@ export interface IFulfillmentModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<ShippingOptionTypeDTO>
|
): Promise<ShippingOptionTypeDTO>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve a fulfillment
|
||||||
|
* @param id
|
||||||
|
* @param config
|
||||||
|
* @param sharedContext
|
||||||
|
*/
|
||||||
|
retrieveFulfillment(
|
||||||
|
id: string,
|
||||||
|
config?: FindConfig<FulfillmentDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<FulfillmentDTO>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List fulfillment sets
|
* List fulfillment sets
|
||||||
* @param filters
|
* @param filters
|
||||||
@@ -421,6 +447,18 @@ export interface IFulfillmentModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<ShippingOptionTypeDTO[]>
|
): Promise<ShippingOptionTypeDTO[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List fulfillments
|
||||||
|
* @param filters
|
||||||
|
* @param config
|
||||||
|
* @param sharedContext
|
||||||
|
*/
|
||||||
|
listFulfillments(
|
||||||
|
filters?: FilterableFulfillmentSetProps,
|
||||||
|
config?: FindConfig<FulfillmentDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<FulfillmentDTO[]>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List and count fulfillment sets
|
* List and count fulfillment sets
|
||||||
* @param filters
|
* @param filters
|
||||||
@@ -505,6 +543,18 @@ export interface IFulfillmentModuleService extends IModuleService {
|
|||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<[ShippingOptionTypeDTO[], number]>
|
): Promise<[ShippingOptionTypeDTO[], number]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List and count fulfillments
|
||||||
|
* @param filters
|
||||||
|
* @param config
|
||||||
|
* @param sharedContext
|
||||||
|
*/
|
||||||
|
listAndCountFulfillments(
|
||||||
|
filters?: FilterableFulfillmentSetProps,
|
||||||
|
config?: FindConfig<FulfillmentDTO>,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<[FulfillmentDTO[], number]>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Soft delete fulfillment sets
|
* Soft delete fulfillment sets
|
||||||
* @param fulfillmentIds
|
* @param fulfillmentIds
|
||||||
@@ -572,4 +622,31 @@ export interface IFulfillmentModuleService extends IModuleService {
|
|||||||
): Promise<Record<string, string[]> | void>
|
): Promise<Record<string, string[]> | void>
|
||||||
|
|
||||||
// TODO define needed soft delete/delete/restore methods
|
// TODO define needed soft delete/delete/restore methods
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the available fulfillment options for the given data.
|
||||||
|
*/
|
||||||
|
retrieveFulfillmentOptions(
|
||||||
|
providerId: string
|
||||||
|
): Promise<Record<string, unknown>[]>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new fulfillment including into the third party provider
|
||||||
|
* @param data
|
||||||
|
* @param sharedContext
|
||||||
|
*/
|
||||||
|
createFulfillment(
|
||||||
|
data: CreateFulfillmentDTO,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<FulfillmentDTO>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the given fulfillment including into the third party provider
|
||||||
|
* @param id
|
||||||
|
* @param sharedContext
|
||||||
|
*/
|
||||||
|
cancelFulfillment(
|
||||||
|
id: string,
|
||||||
|
sharedContext?: Context
|
||||||
|
): Promise<FulfillmentDTO>
|
||||||
}
|
}
|
||||||
@@ -148,11 +148,30 @@ export const mikroOrmSerializer = async <TOutput extends object>(
|
|||||||
options?: any
|
options?: any
|
||||||
): Promise<TOutput> => {
|
): Promise<TOutput> => {
|
||||||
options ??= {}
|
options ??= {}
|
||||||
|
|
||||||
|
const data_ = Array.isArray(data) ? data : [data]
|
||||||
|
|
||||||
|
const forSerialization: unknown[] = []
|
||||||
|
const notForSerialization: unknown[] = []
|
||||||
|
|
||||||
|
data_.forEach((object) => {
|
||||||
|
if (object.__meta) {
|
||||||
|
return forSerialization.push(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
return notForSerialization.push(object)
|
||||||
|
})
|
||||||
|
|
||||||
const { serialize } = await import("@mikro-orm/core")
|
const { serialize } = await import("@mikro-orm/core")
|
||||||
const result = serialize(data, {
|
let result: any = serialize(forSerialization, {
|
||||||
forceObject: true,
|
forceObject: true,
|
||||||
populate: true,
|
populate: true,
|
||||||
...options,
|
...options,
|
||||||
})
|
}) as TOutput[]
|
||||||
return result as unknown as Promise<TOutput>
|
|
||||||
|
if (notForSerialization.length) {
|
||||||
|
result = result.concat(notForSerialization)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.isArray(data) ? result : result[0]
|
||||||
}
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
export * from "./geo-zone"
|
export * from "./geo-zone"
|
||||||
export * from "./shipping-options"
|
export * from "./shipping-options"
|
||||||
|
export * from "./provider"
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { IFulfillmentProvider } from "@medusajs/types"
|
||||||
|
|
||||||
|
export class AbstractFulfillmentProviderService
|
||||||
|
implements IFulfillmentProvider
|
||||||
|
{
|
||||||
|
static identifier: string
|
||||||
|
|
||||||
|
static _isFulfillmentService = true
|
||||||
|
|
||||||
|
static isFulfillmentService(obj) {
|
||||||
|
return obj?.constructor?._isFulfillmentService
|
||||||
|
}
|
||||||
|
|
||||||
|
getIdentifier() {
|
||||||
|
return (this.constructor as any).identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFulfillmentOptions(): Promise<Record<string, unknown>[]> {
|
||||||
|
throw Error("getFulfillmentOptions must be overridden by the child class")
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateFulfillmentData(optionData, data, context): Promise<any> {
|
||||||
|
throw Error("validateFulfillmentData must be overridden by the child class")
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateOption(data): Promise<boolean> {
|
||||||
|
throw Error("validateOption must be overridden by the child class")
|
||||||
|
}
|
||||||
|
|
||||||
|
async canCalculate(data) {
|
||||||
|
throw Error("canCalculate must be overridden by the child class")
|
||||||
|
}
|
||||||
|
|
||||||
|
async calculatePrice(optionData, data, cart) {
|
||||||
|
throw Error("calculatePrice must be overridden by the child class")
|
||||||
|
}
|
||||||
|
|
||||||
|
async createFulfillment(data, items, order, fulfillment): Promise<any> {
|
||||||
|
throw Error("createFulfillment must be overridden by the child class")
|
||||||
|
}
|
||||||
|
|
||||||
|
async cancelFulfillment(fulfillment): Promise<any> {
|
||||||
|
throw Error("cancelFulfillment must be overridden by the child class")
|
||||||
|
}
|
||||||
|
|
||||||
|
async getFulfillmentDocuments(data) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
async createReturnFulfillment(fromData): Promise<any> {
|
||||||
|
throw Error("createReturn must be overridden by the child class")
|
||||||
|
}
|
||||||
|
|
||||||
|
async getReturnDocuments(data) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
async getShipmentDocuments(data) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
async retrieveDocuments(fulfillmentData, documentType) {
|
||||||
|
throw Error("retrieveDocuments must be overridden by the child class")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8234,10 +8234,25 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
|
"@medusajs/fulfillment-manual@workspace:*, @medusajs/fulfillment-manual@workspace:packages/fulfillment-manual":
|
||||||
|
version: 0.0.0-use.local
|
||||||
|
resolution: "@medusajs/fulfillment-manual@workspace:packages/fulfillment-manual"
|
||||||
|
dependencies:
|
||||||
|
"@medusajs/utils": ^1.11.3
|
||||||
|
body-parser: ^1.19.0
|
||||||
|
cross-env: ^5.2.1
|
||||||
|
express: ^4.17.1
|
||||||
|
jest: ^25.5.4
|
||||||
|
rimraf: ^5.0.1
|
||||||
|
typescript: ^4.9.5
|
||||||
|
languageName: unknown
|
||||||
|
linkType: soft
|
||||||
|
|
||||||
"@medusajs/fulfillment@workspace:packages/fulfillment":
|
"@medusajs/fulfillment@workspace:packages/fulfillment":
|
||||||
version: 0.0.0-use.local
|
version: 0.0.0-use.local
|
||||||
resolution: "@medusajs/fulfillment@workspace:packages/fulfillment"
|
resolution: "@medusajs/fulfillment@workspace:packages/fulfillment"
|
||||||
dependencies:
|
dependencies:
|
||||||
|
"@medusajs/fulfillment-manual": "workspace:*"
|
||||||
"@medusajs/modules-sdk": ^1.12.4
|
"@medusajs/modules-sdk": ^1.12.4
|
||||||
"@medusajs/types": ^1.11.8
|
"@medusajs/types": ^1.11.8
|
||||||
"@medusajs/utils": ^1.11.1
|
"@medusajs/utils": ^1.11.1
|
||||||
|
|||||||
Reference in new issue
Block a user