feat(fulfillment): Module service implementation first iteration (#6381)

This commit is contained in:
Adrien de Peretti
2024-02-14 18:43:42 +01:00
committed by GitHub
parent 02c53ec93f
commit fafde4f54d
18 changed files with 3986 additions and 74 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/types": patch
---
feat(fulfillment): Module service implementation first iteration
@@ -1,5 +0,0 @@
describe("noop", function () {
it("should run", function () {
expect(true).toBe(true)
})
})
@@ -2,17 +2,12 @@ import { TestDatabaseUtils } from "medusa-test-utils"
import * as Models from "@models" import * as Models from "@models"
const pathToMigrations = "../../src/migrations"
const mikroOrmEntities = Models as unknown as any[] const mikroOrmEntities = Models as unknown as any[]
export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper( export const MikroOrmWrapper = TestDatabaseUtils.getMikroOrmWrapper(
mikroOrmEntities, mikroOrmEntities,
pathToMigrations null,
) process.env.MEDUSA_FULFILLMENT_DB_SCHEMA
export const MikroOrmConfig = TestDatabaseUtils.getMikroOrmConfig(
mikroOrmEntities,
pathToMigrations
) )
export const DB_URL = TestDatabaseUtils.getDatabaseURL() export const DB_URL = TestDatabaseUtils.getDatabaseURL()
@@ -1 +1,2 @@
export * from "./database" export * from "./database"
export * from "./get-init-module-config"
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,84 @@
import { Migration } from '@mikro-orm/migrations';
export class Migration20240214103108 extends Migration {
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 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 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 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 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_deleted_at" ON "service_zone" (deleted_at) WHERE deleted_at IS NOT NULL;');
this.addSql('create table if not exists "geo_zone" ("id" text not null, "type" text check ("type" in (\'country\', \'province\', \'city\', \'zip\')) not null default \'country\', "country_code" text not null, "province_code" text null, "city" text null, "service_zone_id" text not null, "postal_expression" jsonb null, "metadata" jsonb null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "geo_zone_pkey" primary key ("id"));');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_geo_zone_country_code" ON "geo_zone" (country_code) WHERE deleted_at IS NULL;');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_geo_zone_province_code" ON "geo_zone" (province_code) WHERE deleted_at IS NULL AND province_code IS NOT NULL;');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_geo_zone_city" ON "geo_zone" (city) WHERE deleted_at IS NULL AND city IS NOT NULL;');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_geo_zone_service_zone_id" ON "geo_zone" (service_zone_id) WHERE deleted_at IS NULL;');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_geo_zone_deleted_at" ON "geo_zone" (deleted_at) WHERE deleted_at IS NOT NULL;');
this.addSql('create table if not exists "shipping_option_type" ("id" text not null, "label" text not null, "description" text null, "code" text not null, "shipping_option_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "shipping_option_type_pkey" primary key ("id"));');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_type_shipping_option_id" ON "shipping_option_type" (shipping_option_id) WHERE deleted_at IS NULL;');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_shipping_option_type_deleted_at" ON "shipping_option_type" (deleted_at) WHERE deleted_at IS NOT NULL;');
this.addSql('create table if not exists "shipping_profile" ("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 "shipping_profile_pkey" primary key ("id"));');
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 not null, "service_provider_id" text not null, "shipping_option_type_id" text null, "data" jsonb null, "metadata" jsonb 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('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_service_provider_id" ON "shipping_option" (service_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_deleted_at" ON "shipping_option" (deleted_at) WHERE deleted_at IS NOT NULL;');
this.addSql('create table if not exists "shipping_option_rule" ("id" text not null, "attribute" text not null, "operator" text not null, "value" jsonb null, "shipping_option_id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, constraint "shipping_option_rule_pkey" primary key ("id"));');
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 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_shipping_option_id" ON "fulfillment" (shipping_option_id) WHERE deleted_at IS NULL;');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_deleted_at" ON "fulfillment" (deleted_at) WHERE deleted_at IS NOT NULL;');
this.addSql('create table if not exists "fulfillment_label" ("id" text not null, "tracking_number" text not null, "tracking_url" text not null, "label_url" text not 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_label_pkey" primary key ("id"));');
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 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 INDEX IF NOT EXISTS "IDX_fulfillment_item_fulfillment_id" ON "fulfillment_item" (line_item_id) WHERE deleted_at IS NULL;');
this.addSql('CREATE INDEX IF NOT EXISTS "IDX_fulfillment_item_fulfillment_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_deleted_at" ON "fulfillment_item" (deleted_at) WHERE deleted_at IS NOT NULL;');
this.addSql('alter table if exists "service_zone" add constraint "service_zone_fulfillment_set_id_foreign" foreign key ("fulfillment_set_id") references "fulfillment_set" ("id") on update cascade;');
this.addSql('alter table if exists "geo_zone" add constraint "geo_zone_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;');
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;');
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;');
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_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_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_item" add constraint "fulfillment_item_fulfillment_id_foreign" foreign key ("fulfillment_id") references "fulfillment" ("id") on update cascade;');
}
}
@@ -6,11 +6,12 @@ import {
import { import {
BeforeCreate, BeforeCreate,
Cascade,
Collection, Collection,
Entity, Entity,
Filter, Filter,
Index, Index,
ManyToMany, OneToMany,
OnInit, OnInit,
OptionalProps, OptionalProps,
PrimaryKey, PrimaryKey,
@@ -29,6 +30,15 @@ const deletedAtIndexStatement = createPsqlIndexStatementHelper({
where: "deleted_at IS NOT NULL", where: "deleted_at IS NOT NULL",
}) })
const nameIndexName = "IDX_fulfillment_set_name_unique"
const nameIndexStatement = createPsqlIndexStatementHelper({
name: nameIndexName,
tableName: "fulfillment_set",
columns: "name",
unique: true,
where: "deleted_at IS NULL",
})
@Entity() @Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) @Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class FulfillmentSet { export default class FulfillmentSet {
@@ -38,6 +48,10 @@ export default class FulfillmentSet {
id: string id: string
@Property({ columnType: "text" }) @Property({ columnType: "text" })
@Index({
name: nameIndexName,
expression: nameIndexStatement,
})
name: string name: string
@Property({ columnType: "text" }) @Property({ columnType: "text" })
@@ -46,11 +60,9 @@ export default class FulfillmentSet {
@Property({ columnType: "jsonb", nullable: true }) @Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null metadata: Record<string, unknown> | null = null
@ManyToMany(() => ServiceZone, "fulfillment_sets", { @OneToMany(() => ServiceZone, "fulfillment_set", {
owner: true, cascade: [Cascade.PERSIST, "soft-remove"] as any,
pivotTable: "fulfillment_set_service_zones", orphanRemoval: true,
joinColumn: "fulfillment_set_id",
inverseJoinColumn: "service_zone_id",
}) })
service_zones = new Collection<ServiceZone>(this) service_zones = new Collection<ServiceZone>(this)
+22 -4
View File
@@ -7,12 +7,11 @@ import {
import { import {
BeforeCreate, BeforeCreate,
Collection,
Entity, Entity,
Enum, Enum,
Filter, Filter,
Index, Index,
ManyToMany, ManyToOne,
OnInit, OnInit,
OptionalProps, OptionalProps,
PrimaryKey, PrimaryKey,
@@ -55,6 +54,14 @@ const cityIndexStatement = createPsqlIndexStatementHelper({
where: "deleted_at IS NULL AND city IS NOT NULL", where: "deleted_at IS NULL AND city IS NOT NULL",
}) })
const serviceZoneIdIndexName = "IDX_geo_zone_service_zone_id"
const serviceZoneIdStatement = createPsqlIndexStatementHelper({
name: serviceZoneIdIndexName,
tableName: "geo_zone",
columns: "service_zone_id",
where: "deleted_at IS NULL",
})
@Entity() @Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) @Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class GeoZone { export default class GeoZone {
@@ -87,6 +94,13 @@ export default class GeoZone {
@Property({ columnType: "text", nullable: true }) @Property({ columnType: "text", nullable: true })
city: string | null = null city: string | null = null
@Property({ columnType: "text" })
@Index({
name: serviceZoneIdIndexName,
expression: serviceZoneIdStatement,
})
service_zone_id: string
// TODO: Do we have an example or idea of what would be stored in this field? like lat/long for example? // TODO: Do we have an example or idea of what would be stored in this field? like lat/long for example?
@Property({ columnType: "jsonb", nullable: true }) @Property({ columnType: "jsonb", nullable: true })
postal_expression: Record<string, unknown> | null = null postal_expression: Record<string, unknown> | null = null
@@ -94,8 +108,10 @@ export default class GeoZone {
@Property({ columnType: "jsonb", nullable: true }) @Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null metadata: Record<string, unknown> | null = null
@ManyToMany(() => ServiceZone, (serviceZone) => serviceZone.geo_zones) @ManyToOne(() => ServiceZone, {
service_zones = new Collection<ServiceZone>(this) persist: false,
})
service_zone: ServiceZone
@Property({ @Property({
onCreate: () => new Date(), onCreate: () => new Date(),
@@ -122,10 +138,12 @@ export default class GeoZone {
@BeforeCreate() @BeforeCreate()
onCreate() { onCreate() {
this.id = generateEntityId(this.id, " fgz") this.id = generateEntityId(this.id, " fgz")
this.service_zone_id ??= this.service_zone?.id
} }
@OnInit() @OnInit()
onInit() { onInit() {
this.id = generateEntityId(this.id, "fgz") this.id = generateEntityId(this.id, "fgz")
this.service_zone_id ??= this.service_zone?.id
} }
} }
+37 -11
View File
@@ -6,11 +6,12 @@ import {
import { import {
BeforeCreate, BeforeCreate,
Cascade,
Collection, Collection,
Entity, Entity,
Filter, Filter,
Index, Index,
ManyToMany, ManyToOne,
OneToMany, OneToMany,
OnInit, OnInit,
OptionalProps, OptionalProps,
@@ -32,6 +33,23 @@ const deletedAtIndexStatement = createPsqlIndexStatementHelper({
where: "deleted_at IS NOT NULL", where: "deleted_at IS NOT NULL",
}) })
const nameIndexName = "IDX_service_zone_name_unique"
const nameIndexStatement = createPsqlIndexStatementHelper({
name: nameIndexName,
tableName: "service_zone",
columns: "name",
unique: true,
where: "deleted_at IS NULL",
})
const fulfillmentSetIdIndexName = "IDX_service_zone_fulfillment_set_id"
const fulfillmentSetIdIndexStatement = createPsqlIndexStatementHelper({
name: fulfillmentSetIdIndexName,
tableName: "service_zone",
columns: "fulfillment_set_id",
where: "deleted_at IS NULL",
})
@Entity() @Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) @Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class ServiceZone { export default class ServiceZone {
@@ -41,22 +59,28 @@ export default class ServiceZone {
id: string id: string
@Property({ columnType: "text" }) @Property({ columnType: "text" })
@Index({
name: nameIndexName,
expression: nameIndexStatement,
})
name: string name: string
@Property({ columnType: "jsonb", nullable: true }) @Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null metadata: Record<string, unknown> | null = null
@ManyToMany( @Property({ columnType: "text" })
() => FulfillmentSet, @Index({
(fulfillmentSet) => fulfillmentSet.service_zones name: fulfillmentSetIdIndexName,
) expression: fulfillmentSetIdIndexStatement,
fulfillment_sets = new Collection<FulfillmentSet>(this) })
fulfillment_set_id: string
@ManyToMany(() => GeoZone, "service_zones", { @ManyToOne(() => FulfillmentSet, { persist: false })
owner: true, fulfillment_set: FulfillmentSet
pivotTable: "service_zone_geo_zones",
joinColumn: "service_zone_id", @OneToMany(() => GeoZone, "service_zone", {
inverseJoinColumn: "geo_zone_id", cascade: [Cascade.PERSIST, "soft-remove"] as any,
orphanRemoval: true,
}) })
geo_zones = new Collection<GeoZone>(this) geo_zones = new Collection<GeoZone>(this)
@@ -91,10 +115,12 @@ export default class ServiceZone {
@BeforeCreate() @BeforeCreate()
onCreate() { onCreate() {
this.id = generateEntityId(this.id, "serzo") this.id = generateEntityId(this.id, "serzo")
this.fulfillment_set_id ??= this.fulfillment_set?.id
} }
@OnInit() @OnInit()
onInit() { onInit() {
this.id = generateEntityId(this.id, "serzo") this.id = generateEntityId(this.id, "serzo")
this.fulfillment_set_id ??= this.fulfillment_set?.id
} }
} }
@@ -0,0 +1,59 @@
/*
import { Context, FulfillmentTypes } from "@medusajs/types"
import { DALUtils, promiseAll } from "@medusajs/utils"
import { FulfillmentSet, ServiceZone } from "@models"
import { SqlEntityManager } from "@mikro-orm/postgresql"
interface CreateFulfillmentSetDTO
extends FulfillmentTypes.CreateFulfillmentSetDTO {
service_zones: { id: string; name: string }[]
}
export class FulfillmentSetRepository extends DALUtils.mikroOrmBaseRepositoryFactory<FulfillmentSet>(
FulfillmentSet
) {
async update(
data: {
entity: FulfillmentSet
update: FulfillmentTypes.FulfillmentSetDTO
}[],
context?: Context
): Promise<FulfillmentSet[]> {
const manager = this.getActiveManager<SqlEntityManager>(context)
// init all service zones collections
await promiseAll(
data.map(async ({ entity }) => {
return await entity.service_zones.init()
})
)
const flfillmentSetsToUpdate = data.map(({ entity, update }) => {
const { service_zones, ...restToUpdate } = update
const currentServiceZones = entity.service_zones.getItems()
const serviceZonesToDetach = currentServiceZones.filter(
(serviceZone) =>
!update.service_zones.find(
(newServiceZone) => newServiceZone.id === serviceZone.id
)
)
const serviceZonesToAttach = update.service_zones.filter(
(newServiceZone) =>
!currentServiceZones.find(
(serviceZone) => serviceZone.id === newServiceZone.id
)
)
entity.service_zones.remove(serviceZonesToDetach)
entity.service_zones.add(serviceZonesToAttach as unknown as ServiceZone[])
return manager.assign(entity, restToUpdate)
})
manager.persist(flfillmentSetsToUpdate)
return flfillmentSetsToUpdate
}
}
*/
@@ -8,20 +8,32 @@ import {
ModulesSdkTypes, ModulesSdkTypes,
UpdateFulfillmentSetDTO, UpdateFulfillmentSetDTO,
} from "@medusajs/types" } from "@medusajs/types"
import { InjectTransactionManager, ModulesSdkUtils } from "@medusajs/utils" import {
InjectManager,
InjectTransactionManager,
MedusaContext,
MedusaError,
ModulesSdkUtils,
promiseAll,
getSetDifference
} from "@medusajs/utils"
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
import { FulfillmentSet, ServiceZone, ShippingOption } from "@models" import { FulfillmentSet, GeoZone, ServiceZone, ShippingOption } from "@models"
const generateMethodForModels = [ServiceZone, ShippingOption] const generateMethodForModels = [ServiceZone, ShippingOption, GeoZone]
type InjectedDependencies = { type InjectedDependencies = {
baseRepository: DAL.RepositoryService baseRepository: DAL.RepositoryService
fulfillmentService: ModulesSdkTypes.InternalModuleService<any> fulfillmentSetService: ModulesSdkTypes.InternalModuleService<any>
serviceZoneService: ModulesSdkTypes.InternalModuleService<any>
geoZoneService: ModulesSdkTypes.InternalModuleService<any>
} }
export default class FulfillmentModuleService< export default class FulfillmentModuleService<
TEntity extends FulfillmentSet = FulfillmentSet TEntity extends FulfillmentSet = FulfillmentSet,
TServiceZoneEntity extends ServiceZone = ServiceZone,
TGeoZoneEntity extends GeoZone = GeoZone
> >
extends ModulesSdkUtils.abstractModuleServiceFactory< extends ModulesSdkUtils.abstractModuleServiceFactory<
InjectedDependencies, InjectedDependencies,
@@ -30,21 +42,31 @@ export default class FulfillmentModuleService<
FulfillmentSet: { dto: FulfillmentTypes.FulfillmentSetDTO } FulfillmentSet: { dto: FulfillmentTypes.FulfillmentSetDTO }
ServiceZone: { dto: FulfillmentTypes.ServiceZoneDTO } ServiceZone: { dto: FulfillmentTypes.ServiceZoneDTO }
ShippingOption: { dto: FulfillmentTypes.ShippingOptionDTO } ShippingOption: { dto: FulfillmentTypes.ShippingOptionDTO }
GeoZone: { dto: FulfillmentTypes.GeoZoneDTO }
} }
>(FulfillmentSet, generateMethodForModels, entityNameToLinkableKeysMap) >(FulfillmentSet, generateMethodForModels, entityNameToLinkableKeysMap)
implements IFulfillmentModuleService implements IFulfillmentModuleService
{ {
protected baseRepository_: DAL.RepositoryService protected baseRepository_: DAL.RepositoryService
protected readonly fulfillmentService_: ModulesSdkTypes.InternalModuleService<TEntity> protected readonly fulfillmentSetService_: ModulesSdkTypes.InternalModuleService<TEntity>
protected readonly serviceZoneService_: ModulesSdkTypes.InternalModuleService<TServiceZoneEntity>
protected readonly geoZoneService_: ModulesSdkTypes.InternalModuleService<TGeoZoneEntity>
constructor( constructor(
{ baseRepository, fulfillmentService }: InjectedDependencies, {
baseRepository,
fulfillmentSetService,
serviceZoneService,
geoZoneService,
}: InjectedDependencies,
protected readonly moduleDeclaration: InternalModuleDeclaration protected readonly moduleDeclaration: InternalModuleDeclaration
) { ) {
// @ts-ignore // @ts-ignore
super(...arguments) super(...arguments)
this.baseRepository_ = baseRepository this.baseRepository_ = baseRepository
this.fulfillmentService_ = fulfillmentService this.fulfillmentSetService_ = fulfillmentSetService
this.serviceZoneService_ = serviceZoneService
this.geoZoneService_ = geoZoneService
} }
__joinerConfig(): ModuleJoinerConfig { __joinerConfig(): ModuleJoinerConfig {
@@ -60,16 +82,41 @@ export default class FulfillmentModuleService<
sharedContext?: Context sharedContext?: Context
): Promise<FulfillmentTypes.FulfillmentSetDTO> ): Promise<FulfillmentTypes.FulfillmentSetDTO>
@InjectTransactionManager("baseRepository_") @InjectManager("baseRepository_")
async create( async create(
data: data:
| FulfillmentTypes.CreateFulfillmentSetDTO | FulfillmentTypes.CreateFulfillmentSetDTO
| FulfillmentTypes.CreateFulfillmentSetDTO[], | FulfillmentTypes.CreateFulfillmentSetDTO[],
sharedContext?: Context @MedusaContext() sharedContext: Context = {}
): Promise< ): Promise<
FulfillmentTypes.FulfillmentSetDTO | FulfillmentTypes.FulfillmentSetDTO[] FulfillmentTypes.FulfillmentSetDTO | FulfillmentTypes.FulfillmentSetDTO[]
> { > {
return [] const createdFulfillmentSets = await this.create_(data, sharedContext)
return await this.baseRepository_.serialize<
FulfillmentTypes.FulfillmentSetDTO | FulfillmentTypes.FulfillmentSetDTO[]
>(createdFulfillmentSets, {
populate: true,
})
}
@InjectTransactionManager("baseRepository_")
protected async create_(
data:
| FulfillmentTypes.CreateFulfillmentSetDTO
| FulfillmentTypes.CreateFulfillmentSetDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity | TEntity[]> {
const data_ = Array.isArray(data) ? data : [data]
const createdFulfillmentSets = await this.fulfillmentSetService_.create(
data_,
sharedContext
)
return Array.isArray(data)
? createdFulfillmentSets
: createdFulfillmentSets[0]
} }
createServiceZones( createServiceZones(
@@ -81,16 +128,46 @@ export default class FulfillmentModuleService<
sharedContext?: Context sharedContext?: Context
): Promise<FulfillmentTypes.ServiceZoneDTO> ): Promise<FulfillmentTypes.ServiceZoneDTO>
@InjectTransactionManager("baseRepository_") @InjectManager("baseRepository_")
async createServiceZones( async createServiceZones(
data: data:
| FulfillmentTypes.CreateServiceZoneDTO[] | FulfillmentTypes.CreateServiceZoneDTO[]
| FulfillmentTypes.CreateServiceZoneDTO, | FulfillmentTypes.CreateServiceZoneDTO,
sharedContext?: Context @MedusaContext() sharedContext: Context = {}
): Promise< ): Promise<
FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[] FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[]
> { > {
return [] const createdServiceZones = await this.createServiceZones_(
data,
sharedContext
)
return await this.baseRepository_.serialize<
FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[]
>(createdServiceZones, {
populate: true,
})
}
@InjectTransactionManager("baseRepository_")
protected async createServiceZones_(
data:
| FulfillmentTypes.CreateServiceZoneDTO[]
| FulfillmentTypes.CreateServiceZoneDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<TServiceZoneEntity | TServiceZoneEntity[]> {
let data_ = Array.isArray(data) ? data : [data]
if (!data_.length) {
return []
}
const createdServiceZones = await this.serviceZoneService_.create(
data_,
sharedContext
)
return Array.isArray(data) ? createdServiceZones : createdServiceZones[0]
} }
createShippingOptions( createShippingOptions(
@@ -107,13 +184,42 @@ export default class FulfillmentModuleService<
data: data:
| FulfillmentTypes.CreateShippingOptionDTO[] | FulfillmentTypes.CreateShippingOptionDTO[]
| FulfillmentTypes.CreateShippingOptionDTO, | FulfillmentTypes.CreateShippingOptionDTO,
sharedContext?: Context @MedusaContext() sharedContext: Context = {}
): Promise< ): Promise<
FulfillmentTypes.ShippingOptionDTO | FulfillmentTypes.ShippingOptionDTO[] FulfillmentTypes.ShippingOptionDTO | FulfillmentTypes.ShippingOptionDTO[]
> { > {
return [] return []
} }
createGeoZones(
data: FulfillmentTypes.CreateGeoZoneDTO[],
sharedContext?: Context
): Promise<FulfillmentTypes.GeoZoneDTO[]>
createGeoZones(
data: FulfillmentTypes.CreateGeoZoneDTO,
sharedContext?: Context
): Promise<FulfillmentTypes.GeoZoneDTO>
@InjectManager("baseRepository_")
async createGeoZones(
data:
| FulfillmentTypes.CreateGeoZoneDTO
| FulfillmentTypes.CreateGeoZoneDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<FulfillmentTypes.GeoZoneDTO | FulfillmentTypes.GeoZoneDTO[]> {
const createdGeoZones = await this.geoZoneService_.create(
data,
sharedContext
)
return await this.baseRepository_.serialize<FulfillmentTypes.GeoZoneDTO[]>(
createdGeoZones,
{
populate: true,
}
)
}
update( update(
data: FulfillmentTypes.UpdateFulfillmentSetDTO[], data: FulfillmentTypes.UpdateFulfillmentSetDTO[],
sharedContext?: Context sharedContext?: Context
@@ -123,14 +229,170 @@ export default class FulfillmentModuleService<
sharedContext?: Context sharedContext?: Context
): Promise<FulfillmentTypes.FulfillmentSetDTO> ): Promise<FulfillmentTypes.FulfillmentSetDTO>
@InjectTransactionManager("baseRepository_") @InjectManager("baseRepository_")
async update( async update(
data: UpdateFulfillmentSetDTO[] | UpdateFulfillmentSetDTO, data: UpdateFulfillmentSetDTO[] | UpdateFulfillmentSetDTO,
sharedContext?: Context @MedusaContext() sharedContext: Context = {}
): Promise< ): Promise<
FulfillmentTypes.FulfillmentSetDTO[] | FulfillmentTypes.FulfillmentSetDTO FulfillmentTypes.FulfillmentSetDTO[] | FulfillmentTypes.FulfillmentSetDTO
> { > {
return [] const updatedFulfillmentSets = await this.update_(data, sharedContext)
return await this.baseRepository_.serialize<
FulfillmentTypes.FulfillmentSetDTO | FulfillmentTypes.FulfillmentSetDTO[]
>(updatedFulfillmentSets, {
populate: true,
})
}
@InjectTransactionManager("baseRepository_")
protected async update_(
data: UpdateFulfillmentSetDTO[] | UpdateFulfillmentSetDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<TEntity[] | TEntity> {
const data_ = Array.isArray(data) ? data : [data]
if (!data_.length) {
return []
}
const fulfillmentSetIds = data_.map((f) => f.id)
if (!fulfillmentSetIds.length) {
return []
}
const fulfillmentSets = await this.fulfillmentSetService_.list(
{
id: fulfillmentSetIds,
},
{
relations: ["service_zones", "service_zones.geo_zones"],
},
sharedContext
)
const fulfillmentSetSet = new Set(fulfillmentSets.map((f) => f.id))
const expectedFulfillmentSetSet = new Set(data_.map((f) => f.id))
const missingFulfillmentSetIds = getSetDifference(
expectedFulfillmentSetSet,
fulfillmentSetSet
)
if (missingFulfillmentSetIds.size) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`The following fulfillment sets does not exists: ${Array.from(
missingFulfillmentSetIds
).join(", ")}`
)
}
const fulfillmentSetMap = new Map<string, TEntity>(
fulfillmentSets.map((f) => [f.id, f])
)
// find service zones to delete
const serviceZoneIdsToDelete: string[] = []
const geoZoneIdsToDelete: string[] = []
data_.forEach((fulfillmentSet) => {
if (fulfillmentSet.service_zones) {
/**
* Detect and delete service zones that are not in the updated
*/
const existingFulfillmentSet = fulfillmentSetMap.get(fulfillmentSet.id)!
const existingServiceZones = existingFulfillmentSet.service_zones
const updatedServiceZones = fulfillmentSet.service_zones
const toDeleteServiceZoneIds = getSetDifference(
new Set(existingServiceZones.map((s) => s.id)),
new Set(
updatedServiceZones
.map((s) => "id" in s && s.id)
.filter((id): id is string => !!id)
)
)
if (toDeleteServiceZoneIds.size) {
serviceZoneIdsToDelete.push(...Array.from(toDeleteServiceZoneIds))
geoZoneIdsToDelete.push(
...existingServiceZones
.filter((s) => toDeleteServiceZoneIds.has(s.id))
.flatMap((s) => s.geo_zones.map((g) => g.id))
)
}
/**
* Detect and re assign service zones to the fulfillment set that are still present
*/
const serviceZonesMap = new Map(
existingFulfillmentSet.service_zones.map((serviceZone) => [
serviceZone.id,
serviceZone,
])
)
const serviceZonesSet = new Set(
existingServiceZones
.map((s) => "id" in s && s.id)
.filter((id): id is string => !!id)
)
const expectedServiceZoneSet = new Set(
fulfillmentSet.service_zones
.map((s) => "id" in s && s.id)
.filter((id): id is string => !!id)
)
const missingServiceZoneIds = getSetDifference(
expectedServiceZoneSet,
serviceZonesSet
)
if (missingServiceZoneIds.size) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`The following service zones does not exists: ${Array.from(
missingServiceZoneIds
).join(", ")}`
)
}
// re assign service zones to the fulfillment set
if (fulfillmentSet.service_zones) {
fulfillmentSet.service_zones = fulfillmentSet.service_zones.map(
(serviceZone) => {
if (!("id" in serviceZone)) {
return serviceZone
}
return serviceZonesMap.get(serviceZone.id)!
}
)
}
}
})
if (serviceZoneIdsToDelete.length) {
await promiseAll([
this.geoZoneService_.delete(
{
id: geoZoneIdsToDelete,
},
sharedContext
),
this.serviceZoneService_.delete(
{
id: serviceZoneIdsToDelete,
},
sharedContext
),
])
}
const updatedFulfillmentSets = await this.fulfillmentSetService_.update(
data_,
sharedContext
)
return Array.isArray(data)
? updatedFulfillmentSets
: updatedFulfillmentSets[0]
} }
updateServiceZones( updateServiceZones(
@@ -142,16 +404,146 @@ export default class FulfillmentModuleService<
sharedContext?: Context sharedContext?: Context
): Promise<FulfillmentTypes.ServiceZoneDTO> ): Promise<FulfillmentTypes.ServiceZoneDTO>
@InjectTransactionManager("baseRepository_") @InjectManager("baseRepository_")
async updateServiceZones( async updateServiceZones(
data: data:
| FulfillmentTypes.UpdateServiceZoneDTO[] | FulfillmentTypes.UpdateServiceZoneDTO[]
| FulfillmentTypes.UpdateServiceZoneDTO, | FulfillmentTypes.UpdateServiceZoneDTO,
sharedContext?: Context @MedusaContext() sharedContext: Context = {}
): Promise< ): Promise<
FulfillmentTypes.ServiceZoneDTO[] | FulfillmentTypes.ServiceZoneDTO FulfillmentTypes.ServiceZoneDTO[] | FulfillmentTypes.ServiceZoneDTO
> { > {
return [] const updatedServiceZones = await this.updateServiceZones_(
data,
sharedContext
)
return await this.baseRepository_.serialize<
FulfillmentTypes.ServiceZoneDTO | FulfillmentTypes.ServiceZoneDTO[]
>(updatedServiceZones, {
populate: true,
})
}
@InjectTransactionManager("baseRepository_")
protected async updateServiceZones_(
data:
| FulfillmentTypes.UpdateServiceZoneDTO[]
| FulfillmentTypes.UpdateServiceZoneDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<TServiceZoneEntity | TServiceZoneEntity[]> {
const data_ = Array.isArray(data) ? data : [data]
if (!data_.length) {
return []
}
const serviceZoneIds = data_.map((s) => s.id)
if (!serviceZoneIds.length) {
return []
}
const serviceZones = await this.serviceZoneService_.list(
{
id: serviceZoneIds,
},
{
relations: ["geo_zones"],
},
sharedContext
)
const serviceZoneSet = new Set(serviceZones.map((s) => s.id))
const expectedServiceZoneSet = new Set(data_.map((s) => s.id))
const missingServiceZoneIds = getSetDifference(
expectedServiceZoneSet,
serviceZoneSet
)
if (missingServiceZoneIds.size) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`The following service zones does not exists: ${Array.from(
missingServiceZoneIds
).join(", ")}`
)
}
const serviceZoneMap = new Map<string, TServiceZoneEntity>(
serviceZones.map((s) => [s.id, s])
)
const serviceZoneIdsToDelete: string[] = []
const geoZoneIdsToDelete: string[] = []
data_.forEach((serviceZone) => {
if (serviceZone.geo_zones) {
const existingServiceZone = serviceZoneMap.get(serviceZone.id)!
const existingGeoZones = existingServiceZone.geo_zones
const updatedGeoZones = serviceZone.geo_zones
const toDeleteGeoZoneIds = getSetDifference(
new Set(existingGeoZones.map((g) => g.id)),
new Set(
updatedGeoZones
.map((g) => "id" in g && g.id)
.filter((id): id is string => !!id)
)
)
if (toDeleteGeoZoneIds.size) {
geoZoneIdsToDelete.push(...Array.from(toDeleteGeoZoneIds))
}
const geoZonesMap = new Map(
existingServiceZone.geo_zones.map((geoZone) => [geoZone.id, geoZone])
)
const geoZonesSet = new Set(
existingGeoZones
.map((g) => "id" in g && g.id)
.filter((id): id is string => !!id)
)
const expectedGeoZoneSet = new Set(
serviceZone.geo_zones
.map((g) => "id" in g && g.id)
.filter((id): id is string => !!id)
)
const missingGeoZoneIds = getSetDifference(
expectedGeoZoneSet,
geoZonesSet
)
if (missingGeoZoneIds.size) {
throw new MedusaError(
MedusaError.Types.NOT_FOUND,
`The following geo zones does not exists: ${Array.from(
missingGeoZoneIds
).join(", ")}`
)
}
serviceZone.geo_zones = serviceZone.geo_zones.map((geoZone) => {
if (!("id" in geoZone)) {
return geoZone
}
return geoZonesMap.get(geoZone.id)!
})
}
})
if (geoZoneIdsToDelete.length) {
await this.geoZoneService_.delete(
{
id: geoZoneIdsToDelete,
},
sharedContext
)
}
const updatedServiceZones = await this.serviceZoneService_.update(
data_,
sharedContext
)
return Array.isArray(data) ? updatedServiceZones : updatedServiceZones[0]
} }
updateShippingOptions( updateShippingOptions(
@@ -168,10 +560,40 @@ export default class FulfillmentModuleService<
data: data:
| FulfillmentTypes.UpdateShippingOptionDTO[] | FulfillmentTypes.UpdateShippingOptionDTO[]
| FulfillmentTypes.UpdateShippingOptionDTO, | FulfillmentTypes.UpdateShippingOptionDTO,
sharedContext?: Context @MedusaContext() sharedContext: Context = {}
): Promise< ): Promise<
FulfillmentTypes.ShippingOptionDTO[] | FulfillmentTypes.ShippingOptionDTO FulfillmentTypes.ShippingOptionDTO[] | FulfillmentTypes.ShippingOptionDTO
> { > {
return [] return []
} }
updateGeoZones(
data: FulfillmentTypes.UpdateGeoZoneDTO[],
sharedContext?: Context
): Promise<FulfillmentTypes.GeoZoneDTO[]>
updateGeoZones(
data: FulfillmentTypes.UpdateGeoZoneDTO,
sharedContext?: Context
): Promise<FulfillmentTypes.GeoZoneDTO>
@InjectManager("baseRepository_")
async updateGeoZones(
data:
| FulfillmentTypes.UpdateGeoZoneDTO
| FulfillmentTypes.UpdateGeoZoneDTO[],
@MedusaContext() sharedContext: Context = {}
): Promise<FulfillmentTypes.GeoZoneDTO | FulfillmentTypes.GeoZoneDTO[]> {
const updatedGeoZones = await this.geoZoneService_.update(
data,
sharedContext
)
const serialized = await this.baseRepository_.serialize<
FulfillmentTypes.GeoZoneDTO[]
>(updatedGeoZones, {
populate: true,
})
return Array.isArray(data) ? serialized : serialized[0]
}
} }
@@ -3,10 +3,12 @@ import { CreateServiceZoneDTO } from "./service-zone"
export interface CreateFulfillmentSetDTO { export interface CreateFulfillmentSetDTO {
name: string name: string
type: string type: string
service_zones: Omit<CreateServiceZoneDTO, "fulfillment_set_id">[] service_zones?: Omit<CreateServiceZoneDTO, 'fulfillment_set_id'>[]
} }
export interface UpdateFulfillmentSetDTO export interface UpdateFulfillmentSetDTO {
extends Partial<CreateFulfillmentSetDTO> {
id: string id: string
name?: string
type?: string
service_zones?: (Omit<CreateServiceZoneDTO, 'fulfillment_set_id'> | { id: string })[]
} }
@@ -9,24 +9,20 @@ interface CreateGeoZoneBaseDTO {
interface CreateCountryGeoZoneDTO extends CreateGeoZoneBaseDTO { interface CreateCountryGeoZoneDTO extends CreateGeoZoneBaseDTO {
type: "country" type: "country"
country_code: string
} }
interface CreateProvinceGeoZoneDTO extends CreateGeoZoneBaseDTO { interface CreateProvinceGeoZoneDTO extends CreateGeoZoneBaseDTO {
type: "province" type: "province"
country_code: string
province_code: string province_code: string
} }
interface CreateCityGeoZoneDTO extends CreateGeoZoneBaseDTO { interface CreateCityGeoZoneDTO extends CreateGeoZoneBaseDTO {
type: "city" type: "city"
country_code: string
city: string city: string
} }
interface CreateZipGeoZoneDTO extends CreateGeoZoneBaseDTO { interface CreateZipGeoZoneDTO extends CreateGeoZoneBaseDTO {
type: "zip" type: "zip"
country_code: string
postal_expression: Record<string, any> postal_expression: Record<string, any>
} }
@@ -42,24 +38,20 @@ interface UpdateGeoZoneBaseDTO extends Partial<CreateGeoZoneBaseDTO> {
interface UpdateCountryGeoZoneDTO extends UpdateGeoZoneBaseDTO { interface UpdateCountryGeoZoneDTO extends UpdateGeoZoneBaseDTO {
type: "country" type: "country"
country_code: string
} }
interface UpdateProvinceGeoZoneDTO extends UpdateGeoZoneBaseDTO { interface UpdateProvinceGeoZoneDTO extends UpdateGeoZoneBaseDTO {
type: "province" type: "province"
country_code: string
province_code: string province_code: string
} }
interface UpdateCityGeoZoneDTO extends UpdateGeoZoneBaseDTO { interface UpdateCityGeoZoneDTO extends UpdateGeoZoneBaseDTO {
type: "city" type: "city"
country_code: string
city: string city: string
} }
interface UpdateZipGeoZoneDTO extends UpdateGeoZoneBaseDTO { interface UpdateZipGeoZoneDTO extends UpdateGeoZoneBaseDTO {
type: "zip" type: "zip"
country_code: string
postal_expression: Record<string, any> postal_expression: Record<string, any>
} }
@@ -1,14 +1,13 @@
import { CreateGeoZoneDTO, UpdateGeoZoneDTO } from "./geo-zone" import { CreateGeoZoneDTO } from "./geo-zone"
export interface CreateServiceZoneDTO { export interface CreateServiceZoneDTO {
fulfillment_set_id: string
name: string name: string
geo_zones: ( fulfillment_set_id: string
| Omit<CreateGeoZoneDTO, "service_zone_id"> geo_zones?: Omit<CreateGeoZoneDTO, "service_zone_id">[]
| Omit<UpdateGeoZoneDTO, "service_zone_id">
)[]
} }
export interface UpdateServiceZoneDTO extends Partial<CreateServiceZoneDTO> { export interface UpdateServiceZoneDTO {
id: string id: string
name?: string
geo_zones?: (Omit<CreateGeoZoneDTO, "service_zone_id"> | { id: string })[]
} }
+88
View File
@@ -1,9 +1,11 @@
import { IModuleService } from "../modules-sdk" import { IModuleService } from "../modules-sdk"
import { import {
FilterableFulfillmentSetProps, FilterableFulfillmentSetProps,
FilterableGeoZoneProps,
FilterableServiceZoneProps, FilterableServiceZoneProps,
FilterableShippingOptionProps, FilterableShippingOptionProps,
FulfillmentSetDTO, FulfillmentSetDTO,
GeoZoneDTO,
ServiceZoneDTO, ServiceZoneDTO,
ShippingOptionDTO, ShippingOptionDTO,
} from "./common" } from "./common"
@@ -12,9 +14,11 @@ import { Context } from "../shared-context"
import { RestoreReturn, SoftDeleteReturn } from "../dal" import { RestoreReturn, SoftDeleteReturn } from "../dal"
import { import {
CreateFulfillmentSetDTO, CreateFulfillmentSetDTO,
CreateGeoZoneDTO,
CreateServiceZoneDTO, CreateServiceZoneDTO,
CreateShippingOptionDTO, CreateShippingOptionDTO,
UpdateFulfillmentSetDTO, UpdateFulfillmentSetDTO,
UpdateGeoZoneDTO,
UpdateServiceZoneDTO, UpdateServiceZoneDTO,
UpdateShippingOptionDTO, UpdateShippingOptionDTO,
} from "./mutations" } from "./mutations"
@@ -62,6 +66,20 @@ export interface IFulfillmentModuleService extends IModuleService {
sharedContext?: Context sharedContext?: Context
): Promise<ShippingOptionDTO> ): Promise<ShippingOptionDTO>
/**
* Create a new geo zone
* @param data
* @param sharedContext
*/
createGeoZones(
data: CreateGeoZoneDTO[],
sharedContext?: Context
): Promise<GeoZoneDTO[]>
createGeoZones(
data: CreateGeoZoneDTO,
sharedContext?: Context
): Promise<GeoZoneDTO>
/** /**
* Update a fulfillment set * Update a fulfillment set
* @param data * @param data
@@ -104,6 +122,20 @@ export interface IFulfillmentModuleService extends IModuleService {
sharedContext?: Context sharedContext?: Context
): Promise<ShippingOptionDTO> ): Promise<ShippingOptionDTO>
/**
* Update a geo zone
* @param data
* @param sharedContext
*/
updateGeoZones(
data: UpdateGeoZoneDTO[],
sharedContext?: Context
): Promise<GeoZoneDTO[]>
updateGeoZones(
data: UpdateGeoZoneDTO,
sharedContext?: Context
): Promise<GeoZoneDTO>
/** /**
* Delete a fulfillment set * Delete a fulfillment set
* @param ids * @param ids
@@ -128,6 +160,14 @@ export interface IFulfillmentModuleService extends IModuleService {
deleteShippingOptions(ids: string[], sharedContext?: Context): Promise<void> deleteShippingOptions(ids: string[], sharedContext?: Context): Promise<void>
deleteShippingOptions(id: string, sharedContext?: Context): Promise<void> deleteShippingOptions(id: string, sharedContext?: Context): Promise<void>
/**
* Delete a geo zone
* @param ids
* @param sharedContext
*/
deleteGeoZones(ids: string[], sharedContext?: Context): Promise<void>
deleteGeoZones(id: string, sharedContext?: Context): Promise<void>
/** /**
* Retrieve a fulfillment set * Retrieve a fulfillment set
* @param id * @param id
@@ -164,6 +204,18 @@ export interface IFulfillmentModuleService extends IModuleService {
sharedContext?: Context sharedContext?: Context
): Promise<ShippingOptionDTO> ): Promise<ShippingOptionDTO>
/**
* Retrieve a geo zone
* @param id
* @param config
* @param sharedContext
*/
retrieveGeoZone(
id: string,
config?: FindConfig<GeoZoneDTO>,
sharedContext?: Context
): Promise<GeoZoneDTO>
/** /**
* List fulfillment sets * List fulfillment sets
* @param filters * @param filters
@@ -200,6 +252,18 @@ export interface IFulfillmentModuleService extends IModuleService {
sharedContext?: Context sharedContext?: Context
): Promise<ShippingOptionDTO[]> ): Promise<ShippingOptionDTO[]>
/**
* List geo zones
* @param filters
* @param config
* @param sharedContext
*/
listGeoZones(
filters?: FilterableGeoZoneProps,
config?: FindConfig<GeoZoneDTO>,
sharedContext?: Context
): Promise<GeoZoneDTO[]>
/** /**
* List and count fulfillment sets * List and count fulfillment sets
* @param filters * @param filters
@@ -236,6 +300,18 @@ export interface IFulfillmentModuleService extends IModuleService {
sharedContext?: Context sharedContext?: Context
): Promise<[ShippingOptionDTO[], number]> ): Promise<[ShippingOptionDTO[], number]>
/**
* List and count geo zones
* @param filters
* @param config
* @param sharedContext
*/
listAndCountGeoZones(
filters?: FilterableGeoZoneProps,
config?: FindConfig<GeoZoneDTO>,
sharedContext?: Context
): Promise<[GeoZoneDTO[], number]>
/** /**
* Soft delete fulfillment sets * Soft delete fulfillment sets
* @param fulfillmentIds * @param fulfillmentIds
@@ -272,6 +348,18 @@ export interface IFulfillmentModuleService extends IModuleService {
sharedContext?: Context sharedContext?: Context
): Promise<Record<string, string[]> | void> ): Promise<Record<string, string[]> | void>
/**
* Soft delete geo zones
* @param geoZoneIds
* @param config
* @param sharedContext
*/
softDeleteGeoZones<TReturnableLinkableKeys extends string = string>(
geoZoneIds: string[],
config?: SoftDeleteReturn<TReturnableLinkableKeys>,
sharedContext?: Context
): Promise<Record<string, string[]> | void>
restore<TReturnableLinkableKeys extends string = string>( restore<TReturnableLinkableKeys extends string = string>(
fulfillmentIds: string[], fulfillmentIds: string[],
config?: RestoreReturn<TReturnableLinkableKeys>, config?: RestoreReturn<TReturnableLinkableKeys>,
@@ -0,0 +1,19 @@
/**
* Get the difference between two sets. The difference is the elements that are in the original set but not in the compare set.
* @param orignalSet
* @param compareSet
*/
export function getSetDifference<T>(
orignalSet: Set<T>,
compareSet: Set<T>
): Set<T> {
const difference = new Set<T>()
orignalSet.forEach((element) => {
if (!compareSet.has(element)) {
difference.add(element)
}
})
return difference
}
+1 -1
View File
@@ -1,5 +1,6 @@
export * from "./alter-columns-helper" export * from "./alter-columns-helper"
export * from "./array-difference" export * from "./array-difference"
export * from "./get-set-difference"
export * from "./build-query" export * from "./build-query"
export * from "./camel-to-snake-case" export * from "./camel-to-snake-case"
export * from "./container" export * from "./container"
@@ -46,4 +47,3 @@ export * from "./to-pascal-case"
export * from "./transaction" export * from "./transaction"
export * from "./upper-case-first" export * from "./upper-case-first"
export * from "./wrap-handler" export * from "./wrap-handler"