chore(fulfillment, utils): Migrate module to DML (#10617)

**What**
- Allow to provide `foreignKeyName` option for hasOne and belongsTo relationships
  - `model.hasOne(() => OtherEntity, { foreignKey: true, foreignKeyName: 'other_entity_something_id' })`
  - The above will also output a generated type that takes into consideration the custom fk name 🔽 
- Update types to account for defined custom foreign key name
- Fix joiner config linkable generation to account for custom linkable keys that provide a public API for their model but are not part of the list of the models included in the MedusaService
  - This was supposed to be handled correctly but the implementation was not considering that custom linkable keys could reference models not part of the one provided to medusa service
- Migrate fulfillment module to DML
- Fix has one with fk behaviour and hooks (the relation should be assigned but not the fk)
- Fix has one belongsTo hooks (the relation should be assigned but not the fk)
- Fix hasOneWithFk and belongsTo non persisted fk to be selectable
- Allow to define `belongsTo` without other side definition for `ManyToOne` with no counter part defined
  - Meaning that if a user defined `belongsTo` on one side andnot mapped by and no counter part on the other entity it will be considered as a `ManyToOne`
- `orphanRemoval` on `OneToOne` have been removed, this means that when assigning a new object relation to an entity, the previous one gets deconected but not deleted automatically. This prevent removing data un volountarely

**NOTE**
As per our convention here are some information to keep in mind

**HasOne <> BelongsTo**
Define `OneToOne`, The foreign key is owned by the belongs to and the relation needs to be provided to cascade if wanted

**HasMany <> BelongsTo**
Define `OneToMane` <> `ManyToOne`, the foreign key is owned by the many to one and for those relation no cascade will be performed, the foreign key must be provided. For the `HasMany` the cascade is available

**HasOne (with FK)**
Will act similarly to belongs to with **HasOne <> BelongsTo**

Co-authored-by: Carlos R. L. Rodrigues <37986729+carlos-r-l-rodrigues@users.noreply.github.com>
This commit is contained in:
Adrien de Peretti
2024-12-19 16:40:11 +00:00
committed by GitHub
co-authored by Carlos R. L. Rodrigues
parent 65007c49f6
commit 100da64242
39 changed files with 1154 additions and 1858 deletions
@@ -1,91 +1,16 @@
import { DAL } from "@medusajs/framework/types"
import {
createPsqlIndexStatementHelper,
generateEntityId,
} from "@medusajs/framework/utils"
import {
BeforeCreate,
Entity,
OnInit,
OptionalProps,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import { model } from "@medusajs/framework/utils"
type OptionalAddressProps = DAL.SoftDeletableModelDateColumns
const FulfillmentDeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment_address",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
export const FulfillmentAddress = model.define("fulfillment_address", {
id: model.id({ prefix: "fuladdr" }).primaryKey(),
company: model.text().nullable(),
first_name: model.text().nullable(),
last_name: model.text().nullable(),
address_1: model.text().nullable(),
address_2: model.text().nullable(),
city: model.text().nullable(),
country_code: model.text().nullable(),
province: model.text().nullable(),
postal_code: model.text().nullable(),
phone: model.text().nullable(),
metadata: model.json().nullable(),
})
@Entity({ tableName: "fulfillment_address" })
export default class FulfillmentAddress {
[OptionalProps]: OptionalAddressProps
@PrimaryKey({ columnType: "text" })
id!: string
@Property({ columnType: "text", nullable: true })
company: string | null = null
@Property({ columnType: "text", nullable: true })
first_name: string | null = null
@Property({ columnType: "text", nullable: true })
last_name: string | null = null
@Property({ columnType: "text", nullable: true })
address_1: string | null = null
@Property({ columnType: "text", nullable: true })
address_2: string | null = null
@Property({ columnType: "text", nullable: true })
city: string | null = null
@Property({ columnType: "text", nullable: true })
country_code: string | null = null
@Property({ columnType: "text", nullable: true })
province: string | null = null
@Property({ columnType: "text", nullable: true })
postal_code: string | null = null
@Property({ columnType: "text", nullable: true })
phone: string | null = null
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@Property({ columnType: "timestamptz", nullable: true })
@FulfillmentDeletedAtIndex.MikroORMIndex()
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "fuladdr")
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "fuladdr")
}
}
@@ -1,122 +1,27 @@
import {
BigNumber,
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
MikroOrmBigNumberProperty,
} from "@medusajs/framework/utils"
import { model } from "@medusajs/framework/utils"
import { BigNumberRawValue, DAL } from "@medusajs/framework/types"
import {
BeforeCreate,
Entity,
Filter,
ManyToOne,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import Fulfillment from "./fulfillment"
import { Fulfillment } from "./fulfillment"
type FulfillmentItemOptionalProps = DAL.SoftDeletableModelDateColumns
const FulfillmentIdIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment_item",
columns: "fulfillment_id",
where: "deleted_at IS NULL",
})
const LineItemIdIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment_item",
columns: "line_item_id",
where: "deleted_at IS NULL",
})
const InventoryItemIdIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment_item",
columns: "inventory_item_id",
where: "deleted_at IS NULL",
})
const FulfillmentItemDeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment_item",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
})
@Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class FulfillmentItem {
[OptionalProps]?: FulfillmentItemOptionalProps
@PrimaryKey({ columnType: "text" })
id: string
@Property({ columnType: "text" })
title: string
@Property({ columnType: "text" })
sku: string
@Property({ columnType: "text" })
barcode: string
@MikroOrmBigNumberProperty()
quantity: BigNumber | number
@Property({ columnType: "jsonb" })
raw_quantity: BigNumberRawValue
@Property({ columnType: "text", nullable: true })
@LineItemIdIndex.MikroORMIndex()
line_item_id: string | null = null
@Property({ columnType: "text", nullable: true })
@InventoryItemIdIndex.MikroORMIndex()
inventory_item_id: string | null = null
@ManyToOne(() => Fulfillment, {
columnType: "text",
mapToPk: true,
fieldName: "fulfillment_id",
onDelete: "cascade",
export const FulfillmentItem = model
.define("fulfillment_item", {
id: model.id({ prefix: "fulit" }).primaryKey(),
title: model.text(),
sku: model.text(),
barcode: model.text(),
quantity: model.bigNumber(),
line_item_id: model.text().nullable(),
inventory_item_id: model.text().nullable(),
fulfillment: model.belongsTo(() => Fulfillment, {
mappedBy: "items",
}),
})
@FulfillmentIdIndex.MikroORMIndex()
fulfillment_id: string
@ManyToOne(() => Fulfillment, { persist: false })
fulfillment: Rel<Fulfillment>
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@FulfillmentItemDeletedAtIndex.MikroORMIndex()
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "fulit")
this.fulfillment_id ??= this.fulfillment.id
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "fulit")
this.fulfillment_id ??= this.fulfillment.id
}
}
.indexes([
{
on: ["inventory_item_id"],
where: "deleted_at IS NULL",
},
{
on: ["line_item_id"],
where: "deleted_at IS NULL",
},
])
@@ -1,94 +1,13 @@
import {
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
} from "@medusajs/framework/utils"
import { model } from "@medusajs/framework/utils"
import { DAL } from "@medusajs/framework/types"
import {
BeforeCreate,
Entity,
Filter,
ManyToOne,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import Fulfillment from "./fulfillment"
import { Fulfillment } from "./fulfillment"
type FulfillmentLabelOptionalProps = DAL.SoftDeletableModelDateColumns
const FulfillmentIdIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment_label",
columns: "fulfillment_id",
where: "deleted_at IS NULL",
export const FulfillmentLabel = model.define("fulfillment_label", {
id: model.id({ prefix: "fulla" }).primaryKey(),
tracking_number: model.text(),
tracking_url: model.text(),
label_url: model.text(),
fulfillment: model.belongsTo(() => Fulfillment, {
mappedBy: "labels",
}),
})
const DeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment_label",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
})
@Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class FulfillmentLabel {
[OptionalProps]?: FulfillmentLabelOptionalProps
@PrimaryKey({ columnType: "text" })
id: string
@Property({ columnType: "text" })
tracking_number: string
@Property({ columnType: "text" })
tracking_url: string
@Property({ columnType: "text" })
label_url: string
@ManyToOne(() => Fulfillment, {
columnType: "text",
mapToPk: true,
fieldName: "fulfillment_id",
onDelete: "cascade",
})
@FulfillmentIdIndex.MikroORMIndex()
fulfillment_id: string
@ManyToOne(() => Fulfillment, { persist: false })
fulfillment: Rel<Fulfillment>
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@Property({ columnType: "timestamptz", nullable: true })
@DeletedAtIndex.MikroORMIndex()
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "fulla")
this.fulfillment_id ??= this.fulfillment.id
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "fulla")
this.fulfillment_id ??= this.fulfillment.id
}
}
@@ -1,28 +1,6 @@
import { Searchable, generateEntityId } from "@medusajs/framework/utils"
import {
BeforeCreate,
Entity,
OnInit,
PrimaryKey,
Property,
} from "@mikro-orm/core"
import { model } from "@medusajs/framework/utils"
@Entity()
export default class FulfillmentProvider {
@Searchable()
@PrimaryKey({ columnType: "text" })
id: string
@Property({ columnType: "boolean", defaultRaw: "true" })
is_enabled: boolean = true
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "serpro")
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "serpro")
}
}
export const FulfillmentProvider = model.define("fulfillment_provider", {
id: model.id({ prefix: "serpro" }).primaryKey(),
is_enabled: model.boolean().default(true),
})
@@ -1,90 +1,24 @@
import {
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
} from "@medusajs/framework/utils"
import { model } from "@medusajs/framework/utils"
import { DAL } from "@medusajs/framework/types"
import {
BeforeCreate,
Cascade,
Collection,
Entity,
Filter,
OneToMany,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import ServiceZone from "./service-zone"
import { ServiceZone } from "./service-zone"
type FulfillmentSetOptionalProps = DAL.SoftDeletableModelDateColumns
const DeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment_set",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
})
const NameIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment_set",
columns: "name",
unique: true,
where: "deleted_at IS NULL",
})
@Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class FulfillmentSet {
[OptionalProps]?: FulfillmentSetOptionalProps
@PrimaryKey({ columnType: "text" })
id: string
@Property({ columnType: "text" })
@NameIndex.MikroORMIndex()
name: string
@Property({ columnType: "text" })
type: string
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@OneToMany(() => ServiceZone, "fulfillment_set", {
cascade: [Cascade.PERSIST, "soft-remove"] as any,
orphanRemoval: true,
export const FulfillmentSet = model
.define("fulfillment_set", {
id: model.id({ prefix: "fuset" }).primaryKey(),
name: model.text(),
type: model.text(),
service_zones: model.hasMany(() => ServiceZone, {
mappedBy: "fulfillment_set",
}),
metadata: model.json().nullable(),
})
service_zones = new Collection<Rel<ServiceZone>>(this)
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
.indexes([
{
on: ["name"],
where: "deleted_at IS NULL",
unique: true,
},
])
.cascades({
delete: ["service_zones"],
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@Property({ columnType: "timestamptz", nullable: true })
@DeletedAtIndex.MikroORMIndex()
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "fuset")
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "fuset")
}
}
@@ -1,183 +1,54 @@
import {
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
} from "@medusajs/framework/utils"
import { model } from "@medusajs/framework/utils"
import { DAL } from "@medusajs/framework/types"
import {
BeforeCreate,
Cascade,
Collection,
Entity,
Filter,
ManyToOne,
OneToMany,
OneToOne,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import FulfillmentAddress from "./address"
import FulfillmentItem from "./fulfillment-item"
import FulfillmentLabel from "./fulfillment-label"
import FulfillmentProvider from "./fulfillment-provider"
import ShippingOption from "./shipping-option"
import { FulfillmentAddress } from "./address"
import { FulfillmentItem } from "./fulfillment-item"
import { FulfillmentLabel } from "./fulfillment-label"
import { FulfillmentProvider } from "./fulfillment-provider"
import { ShippingOption } from "./shipping-option"
type FulfillmentOptionalProps = DAL.SoftDeletableModelDateColumns
const FulfillmentDeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
})
const FulfillmentProviderIdIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment",
columns: "provider_id",
where: "deleted_at IS NULL",
})
const FulfillmentLocationIdIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment",
columns: "location_id",
where: "deleted_at IS NULL",
})
const FulfillmentShippingOptionIdIndex = createPsqlIndexStatementHelper({
tableName: "fulfillment",
columns: "shipping_option_id",
where: "deleted_at IS NULL",
})
@Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class Fulfillment {
[OptionalProps]?: FulfillmentOptionalProps
@PrimaryKey({ columnType: "text" })
id: string
@Property({ columnType: "text" })
@FulfillmentLocationIdIndex.MikroORMIndex()
location_id: string
@Property({
columnType: "timestamptz",
nullable: true,
export const Fulfillment = model
.define("fulfillment", {
id: model.id({ prefix: "ful" }).primaryKey(),
location_id: model.text(),
packed_at: model.dateTime().nullable(),
shipped_at: model.dateTime().nullable(),
marked_shipped_by: model.text().nullable(),
created_by: model.text().nullable(),
delivered_at: model.dateTime().nullable(),
canceled_at: model.dateTime().nullable(),
data: model.json().nullable(),
requires_shipping: model.boolean().default(true),
items: model.hasMany(() => FulfillmentItem, {
mappedBy: "fulfillment",
}),
labels: model.hasMany(() => FulfillmentLabel, {
mappedBy: "fulfillment",
}),
provider: model
.hasOne(() => FulfillmentProvider, {
foreignKey: true,
mappedBy: undefined,
})
.nullable(),
shipping_option: model
.belongsTo(() => ShippingOption, {
mappedBy: "fulfillments",
})
.nullable(),
delivery_address: model
.hasOne(() => FulfillmentAddress, {
foreignKey: true,
mappedBy: undefined,
})
.nullable(),
metadata: model.json().nullable(),
})
packed_at: Date | null = null
@Property({
columnType: "timestamptz",
nullable: true,
.indexes([
{
on: ["location_id"],
where: "deleted_at IS NULL",
},
])
.cascades({
delete: ["delivery_address", "items", "labels"],
})
shipped_at: Date | null = null
@Property({ columnType: "text", nullable: true })
marked_shipped_by: string | null = null
@Property({ columnType: "text", nullable: true })
created_by: string | null = null
@Property({
columnType: "timestamptz",
nullable: true,
})
delivered_at: Date | null = null
@Property({
columnType: "timestamptz",
nullable: true,
})
canceled_at: Date | null = null
@Property({ columnType: "jsonb", nullable: true })
data: Record<string, unknown> | null = null
@ManyToOne(() => FulfillmentProvider, {
columnType: "text",
fieldName: "provider_id",
mapToPk: true,
nullable: true,
onDelete: "set null",
})
@FulfillmentProviderIdIndex.MikroORMIndex()
provider_id: string
@ManyToOne(() => ShippingOption, {
columnType: "text",
fieldName: "shipping_option_id",
nullable: true,
mapToPk: true,
onDelete: "set null",
})
@FulfillmentShippingOptionIdIndex.MikroORMIndex()
shipping_option_id: string | null = null
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@ManyToOne(() => ShippingOption, { persist: false })
shipping_option: ShippingOption | null
@ManyToOne(() => FulfillmentProvider, { persist: false })
provider: Rel<FulfillmentProvider>
@OneToOne({
entity: () => FulfillmentAddress,
owner: true,
cascade: [Cascade.PERSIST, "soft-remove"] as any,
nullable: true,
onDelete: "cascade",
})
delivery_address!: Rel<FulfillmentAddress>
@Property({ columnType: "boolean", default: true })
requires_shipping: boolean = true
@OneToMany(() => FulfillmentItem, (item) => item.fulfillment, {
cascade: [Cascade.PERSIST, "soft-remove"] as any,
orphanRemoval: true,
})
items = new Collection<Rel<FulfillmentItem>>(this)
@OneToMany(() => FulfillmentLabel, (label) => label.fulfillment, {
cascade: [Cascade.PERSIST, "soft-remove"] as any,
orphanRemoval: true,
})
labels = new Collection<Rel<FulfillmentLabel>>(this)
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@FulfillmentDeletedAtIndex.MikroORMIndex()
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "ful")
this.provider_id ??= this.provider_id ?? this.provider?.id
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "ful")
this.provider_id ??= this.provider_id ?? this.provider?.id
}
}
@@ -1,128 +1,53 @@
import {
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
BelongsTo,
DmlEntity,
DMLEntitySchemaBuilder,
GeoZoneType,
IdProperty,
JSONProperty,
model,
NullableModifier,
PrimaryKeyModifier,
TextProperty,
} from "@medusajs/framework/utils"
import { DAL } from "@medusajs/framework/types"
import {
BeforeCreate,
Entity,
Enum,
Filter,
ManyToOne,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import ServiceZone from "./service-zone"
import { ServiceZone } from "./service-zone"
type GeoZoneOptionalProps = DAL.SoftDeletableModelDateColumns
const DeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "geo_zone",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
})
const CountryCodeIndex = createPsqlIndexStatementHelper({
tableName: "geo_zone",
columns: "country_code",
where: "deleted_at IS NULL",
})
const ProvinceCodeIndex = createPsqlIndexStatementHelper({
tableName: "geo_zone",
columns: "province_code",
where: "deleted_at IS NULL AND province_code IS NOT NULL",
})
const CityIndex = createPsqlIndexStatementHelper({
tableName: "geo_zone",
columns: "city",
where: "deleted_at IS NULL AND city IS NOT NULL",
})
const ServiceZoneIdIndex = createPsqlIndexStatementHelper({
tableName: "geo_zone",
columns: "service_zone_id",
where: "deleted_at IS NULL",
})
@Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class GeoZone {
[OptionalProps]?: GeoZoneOptionalProps
@PrimaryKey({ columnType: "text" })
id: string
@Enum({ items: () => GeoZoneType, default: GeoZoneType.COUNTRY })
type: GeoZoneType
@CountryCodeIndex.MikroORMIndex()
@Property({ columnType: "text" })
country_code: string
@ProvinceCodeIndex.MikroORMIndex()
@Property({ columnType: "text", nullable: true })
province_code: string | null = null
@CityIndex.MikroORMIndex()
@Property({ columnType: "text", nullable: true })
city: string | null = null
@ManyToOne(() => ServiceZone, {
type: "text",
mapToPk: true,
fieldName: "service_zone_id",
onDelete: "cascade",
})
@ServiceZoneIdIndex.MikroORMIndex()
service_zone_id: string
@Property({ columnType: "jsonb", nullable: true })
postal_expression: Record<string, unknown> | null = null
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@ManyToOne(() => ServiceZone, {
persist: false,
})
service_zone: Rel<ServiceZone>
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@DeletedAtIndex.MikroORMIndex()
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "fgz")
this.service_zone_id ??= this.service_zone?.id
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "fgz")
this.service_zone_id ??= this.service_zone?.id
}
export type GeoZoneSchema = {
id: PrimaryKeyModifier<string, IdProperty>
type: TextProperty
country_code: TextProperty
province_code?: NullableModifier<string, TextProperty>
city?: NullableModifier<string, TextProperty>
postal_expression?: NullableModifier<Record<string, unknown>, JSONProperty>
service_zone: BelongsTo<() => typeof ServiceZone>
metadata?: NullableModifier<Record<string, unknown>, JSONProperty>
}
export const GeoZone = model
.define("geo_zone", {
id: model.id({ prefix: "fgz" }).primaryKey(),
type: model.enum(GeoZoneType).default(GeoZoneType.COUNTRY),
country_code: model.text(),
province_code: model.text().nullable(),
city: model.text().nullable(),
postal_expression: model.json().nullable(),
service_zone: model.belongsTo<() => typeof ServiceZone>(() => ServiceZone, {
mappedBy: "geo_zones",
}),
metadata: model.json().nullable(),
})
.indexes([
{
on: ["country_code"],
where: "deleted_at IS NULL",
},
{
on: ["province_code"],
where: "deleted_at IS NULL",
},
{
on: ["city"],
where: "deleted_at IS NULL",
},
]) as unknown as DmlEntity<DMLEntitySchemaBuilder<GeoZoneSchema>, "GeoZone">
@@ -1,12 +1,12 @@
export { default as FulfillmentAddress } from "./address"
export { default as Fulfillment } from "./fulfillment"
export { default as FulfillmentItem } from "./fulfillment-item"
export { default as FulfillmentLabel } from "./fulfillment-label"
export { default as FulfillmentProvider } from "./fulfillment-provider"
export { default as FulfillmentSet } from "./fulfillment-set"
export { default as GeoZone } from "./geo-zone"
export { default as ServiceZone } from "./service-zone"
export { default as ShippingOption } from "./shipping-option"
export { default as ShippingOptionRule } from "./shipping-option-rule"
export { default as ShippingOptionType } from "./shipping-option-type"
export { default as ShippingProfile } from "./shipping-profile"
export { FulfillmentAddress } from "./address"
export { Fulfillment } from "./fulfillment"
export { FulfillmentItem } from "./fulfillment-item"
export { FulfillmentLabel } from "./fulfillment-label"
export { FulfillmentProvider } from "./fulfillment-provider"
export { FulfillmentSet } from "./fulfillment-set"
export { GeoZone } from "./geo-zone"
export { ServiceZone } from "./service-zone"
export { ShippingOption } from "./shipping-option"
export { ShippingOptionRule } from "./shipping-option-rule"
export { ShippingOptionType } from "./shipping-option-type"
export { ShippingProfile } from "./shipping-profile"
@@ -1,126 +1,60 @@
import {
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
BelongsTo,
DmlEntity,
DMLEntitySchemaBuilder,
HasMany,
IdProperty,
JSONProperty,
model,
NullableModifier,
PrimaryKeyModifier,
TextProperty,
} from "@medusajs/framework/utils"
import { DAL } from "@medusajs/framework/types"
import {
BeforeCreate,
Cascade,
Collection,
Entity,
Filter,
Index,
ManyToOne,
OneToMany,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import FulfillmentSet from "./fulfillment-set"
import GeoZone from "./geo-zone"
import ShippingOption from "./shipping-option"
import { FulfillmentSet } from "./fulfillment-set"
import { GeoZone } from "./geo-zone"
import { ShippingOption } from "./shipping-option"
type ServiceZoneOptionalProps = DAL.SoftDeletableModelDateColumns
const deletedAtIndexName = "IDX_service_zone_deleted_at"
const deletedAtIndexStatement = createPsqlIndexStatementHelper({
name: deletedAtIndexName,
tableName: "service_zone",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
}).expression
const NameIndex = createPsqlIndexStatementHelper({
tableName: "service_zone",
columns: "name",
unique: true,
where: "deleted_at IS NULL",
})
const FulfillmentSetIdIndex = createPsqlIndexStatementHelper({
tableName: "service_zone",
columns: "fulfillment_set_id",
where: "deleted_at IS NULL",
})
@Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class ServiceZone {
[OptionalProps]?: ServiceZoneOptionalProps
@PrimaryKey({ columnType: "text" })
id: string
@Property({ columnType: "text" })
@NameIndex.MikroORMIndex()
name: string
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@ManyToOne(() => FulfillmentSet, {
type: "text",
mapToPk: true,
fieldName: "fulfillment_set_id",
onDelete: "cascade",
})
@FulfillmentSetIdIndex.MikroORMIndex()
fulfillment_set_id: string
@ManyToOne(() => FulfillmentSet, { persist: false })
fulfillment_set: Rel<FulfillmentSet>
@OneToMany(() => GeoZone, "service_zone", {
cascade: [Cascade.PERSIST, "soft-remove"] as any,
orphanRemoval: true,
})
geo_zones = new Collection<Rel<GeoZone>>(this)
@OneToMany(
() => ShippingOption,
(shippingOption) => shippingOption.service_zone,
{
cascade: [Cascade.PERSIST, "soft-remove"] as any,
orphanRemoval: true,
}
)
shipping_options = new Collection<Rel<ShippingOption>>(this)
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@Index({
name: deletedAtIndexName,
expression: deletedAtIndexStatement,
})
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "serzo")
this.fulfillment_set_id ??= this.fulfillment_set?.id
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "serzo")
this.fulfillment_set_id ??= this.fulfillment_set?.id
}
export type ServiceZoneSchema = {
id: PrimaryKeyModifier<string, IdProperty>
name: TextProperty
fulfillment_set: BelongsTo<() => typeof FulfillmentSet>
geo_zones: HasMany<() => typeof GeoZone>
shipping_options: HasMany<() => typeof ShippingOption>
metadata: NullableModifier<Record<string, unknown>, JSONProperty>
}
export const ServiceZone = model
.define("service_zone", {
id: model.id({ prefix: "serzo" }).primaryKey(),
name: model.text(),
fulfillment_set: model.belongsTo<() => typeof FulfillmentSet>(
() => FulfillmentSet,
{
mappedBy: "service_zones",
}
),
geo_zones: model.hasMany<() => typeof GeoZone>(() => GeoZone, {
mappedBy: "service_zone",
}),
shipping_options: model.hasMany<() => typeof ShippingOption>(
() => ShippingOption,
{
mappedBy: "service_zone",
}
),
metadata: model.json().nullable(),
})
.indexes([
{
on: ["name"],
unique: true,
where: "deleted_at IS NULL",
},
])
.cascades({
delete: ["geo_zones", "shipping_options"],
}) as unknown as DmlEntity<
DMLEntitySchemaBuilder<ServiceZoneSchema>,
"ServiceZone"
>
@@ -1,100 +1,12 @@
import { DAL } from "@medusajs/framework/types"
import {
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
RuleOperator,
} from "@medusajs/framework/utils"
import {
BeforeCreate,
Entity,
Enum,
Filter,
ManyToOne,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import ShippingOption from "./shipping-option"
import { model, RuleOperator } from "@medusajs/framework/utils"
import { ShippingOption } from "./shipping-option"
type ShippingOptionRuleOptionalProps = DAL.SoftDeletableModelDateColumns
const DeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "shipping_option_rule",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
export const ShippingOptionRule = model.define("shipping_option_rule", {
id: model.id({ prefix: "sorul" }).primaryKey(),
attribute: model.text(),
operator: model.enum(RuleOperator),
value: model.json().nullable(),
shipping_option: model.belongsTo(() => ShippingOption, {
mappedBy: "rules",
}),
})
const ShippingOptionIdIndex = createPsqlIndexStatementHelper({
tableName: "shipping_option_rule",
columns: "shipping_option_id",
where: "deleted_at IS NULL",
})
@Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class ShippingOptionRule {
[OptionalProps]?: ShippingOptionRuleOptionalProps
@PrimaryKey({ columnType: "text" })
id: string
@Property({ columnType: "text" })
attribute: string
@Enum({
items: () => Object.values(RuleOperator),
columnType: "text",
})
operator: Lowercase<keyof typeof RuleOperator>
@Property({ columnType: "jsonb", nullable: true })
value: string | string[] | null = null
@ManyToOne(() => ShippingOption, {
type: "text",
mapToPk: true,
fieldName: "shipping_option_id",
onDelete: "cascade",
})
@ShippingOptionIdIndex.MikroORMIndex()
shipping_option_id: string
@ManyToOne(() => ShippingOption, {
persist: false,
})
shipping_option: Rel<ShippingOption>
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@DeletedAtIndex.MikroORMIndex()
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "sorul")
this.shipping_option_id ??= this.shipping_option?.id
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "sorul")
this.shipping_option_id ??= this.shipping_option?.id
}
}
@@ -1,80 +1,13 @@
import {
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
} from "@medusajs/framework/utils"
import { model } from "@medusajs/framework/utils"
import { DAL } from "@medusajs/framework/types"
import {
BeforeCreate,
Entity,
Filter,
OneToOne,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import ShippingOption from "./shipping-option"
import { ShippingOption } from "./shipping-option"
type ShippingOptionTypeOptionalProps = DAL.SoftDeletableModelDateColumns
const DeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "shipping_option_type",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
export const ShippingOptionType = model.define("shipping_option_type", {
id: model.id({ prefix: "sotype" }).primaryKey(),
label: model.text(),
description: model.text().nullable(),
code: model.text(),
shipping_option: model.hasOne(() => ShippingOption, {
mappedBy: "type",
}),
})
@Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class ShippingOptionType {
[OptionalProps]?: ShippingOptionTypeOptionalProps
@PrimaryKey({ columnType: "text" })
id: string
@Property({ columnType: "text" })
label: string
@Property({ columnType: "text", nullable: true })
description: string | null = null
@Property({ columnType: "text" })
code: string
@OneToOne(() => ShippingOption, (so) => so.type, {
type: "text",
onDelete: "cascade",
})
shipping_option: Rel<ShippingOption>
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@DeletedAtIndex.MikroORMIndex()
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "sotype")
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "sotype")
}
}
@@ -1,182 +1,42 @@
import {
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
Searchable,
ShippingOptionPriceType,
} from "@medusajs/framework/utils"
import { model, ShippingOptionPriceType } from "@medusajs/framework/utils"
import { DAL } from "@medusajs/framework/types"
import {
BeforeCreate,
Cascade,
Collection,
Entity,
Enum,
Filter,
ManyToOne,
OneToMany,
OneToOne,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import Fulfillment from "./fulfillment"
import FulfillmentProvider from "./fulfillment-provider"
import ServiceZone from "./service-zone"
import ShippingOptionRule from "./shipping-option-rule"
import ShippingOptionType from "./shipping-option-type"
import ShippingProfile from "./shipping-profile"
import { Fulfillment } from "./fulfillment"
import { FulfillmentProvider } from "./fulfillment-provider"
import { ServiceZone } from "./service-zone"
import { ShippingOptionRule } from "./shipping-option-rule"
import { ShippingOptionType } from "./shipping-option-type"
import { ShippingProfile } from "./shipping-profile"
type ShippingOptionOptionalProps = DAL.SoftDeletableModelDateColumns
const DeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "shipping_option",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
})
const ServiceZoneIdIndex = createPsqlIndexStatementHelper({
tableName: "shipping_option",
columns: "service_zone_id",
where: "deleted_at IS NULL",
})
const ShippingProfileIdIndex = createPsqlIndexStatementHelper({
tableName: "shipping_option",
columns: "shipping_profile_id",
where: "deleted_at IS NULL",
})
const FulfillmentProviderIdIndex = createPsqlIndexStatementHelper({
tableName: "shipping_option",
columns: "provider_id",
where: "deleted_at IS NULL",
})
const ShippingOptionTypeIdIndex = createPsqlIndexStatementHelper({
tableName: "shipping_option",
columns: "shipping_option_type_id",
where: "deleted_at IS NULL",
})
@Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class ShippingOption {
[OptionalProps]?: ShippingOptionOptionalProps
@PrimaryKey({ columnType: "text" })
id: string
@Searchable()
@Property({ columnType: "text" })
name: string
@Enum({
items: () => ShippingOptionPriceType,
default: ShippingOptionPriceType.FLAT,
export const ShippingOption = model
.define("shipping_option", {
id: model.id({ prefix: "so" }).primaryKey(),
name: model.text(),
price_type: model
.enum(ShippingOptionPriceType)
.default(ShippingOptionPriceType.FLAT),
data: model.json().nullable(),
metadata: model.json().nullable(),
service_zone: model.belongsTo(() => ServiceZone, {
mappedBy: "shipping_options",
}),
shipping_profile: model
.belongsTo(() => ShippingProfile, {
mappedBy: "shipping_options",
})
.nullable(),
provider: model.belongsTo(() => FulfillmentProvider).nullable(),
type: model.hasOne(() => ShippingOptionType, {
foreignKey: true,
foreignKeyName: "shipping_option_type_id",
mappedBy: undefined,
}),
rules: model.hasMany(() => ShippingOptionRule, {
mappedBy: "shipping_option",
}),
fulfillments: model.hasMany(() => Fulfillment, {
mappedBy: "shipping_option",
}),
})
price_type: ShippingOptionPriceType
@ManyToOne(() => ServiceZone, {
type: "text",
fieldName: "service_zone_id",
mapToPk: true,
onDelete: "cascade",
.cascades({
delete: ["rules", "type"],
})
@ServiceZoneIdIndex.MikroORMIndex()
service_zone_id: string
@ManyToOne(() => ShippingProfile, {
type: "text",
fieldName: "shipping_profile_id",
mapToPk: true,
nullable: true,
onDelete: "set null",
})
@ShippingProfileIdIndex.MikroORMIndex()
shipping_profile_id: string | null
@ManyToOne(() => FulfillmentProvider, {
type: "text",
fieldName: "provider_id",
mapToPk: true,
nullable: true,
})
@FulfillmentProviderIdIndex.MikroORMIndex()
provider_id: string
@Property({ columnType: "text", persist: false })
@ShippingOptionTypeIdIndex.MikroORMIndex()
shipping_option_type_id: string | null = null
@Property({ columnType: "jsonb", nullable: true })
data: Record<string, unknown> | null = null
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@ManyToOne(() => ServiceZone, { persist: false })
service_zone: Rel<ServiceZone>
@ManyToOne(() => ShippingProfile, {
persist: false,
})
shipping_profile: Rel<ShippingProfile> | null
@ManyToOne(() => FulfillmentProvider, {
persist: false,
})
provider: Rel<FulfillmentProvider> | null
@OneToOne(() => ShippingOptionType, (so) => so.shipping_option, {
owner: true,
cascade: [Cascade.PERSIST, "soft-remove"] as any,
orphanRemoval: true,
fieldName: "shipping_option_type_id",
onDelete: "cascade",
})
type: Rel<ShippingOptionType>
@OneToMany(() => ShippingOptionRule, "shipping_option", {
cascade: [Cascade.PERSIST, "soft-remove"] as any,
orphanRemoval: true,
})
rules = new Collection<Rel<ShippingOptionRule>>(this)
@OneToMany(() => Fulfillment, (fulfillment) => fulfillment.shipping_option)
fulfillments = new Collection<Rel<Fulfillment>>(this)
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@DeletedAtIndex.MikroORMIndex()
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "so")
this.shipping_option_type_id ??= this.type?.id
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "so")
this.shipping_option_type_id ??= this.type?.id
}
}
@@ -1,92 +1,21 @@
import {
createPsqlIndexStatementHelper,
DALUtils,
generateEntityId,
Searchable,
} from "@medusajs/framework/utils"
import { model } from "@medusajs/framework/utils"
import { DAL } from "@medusajs/framework/types"
import {
BeforeCreate,
Collection,
Entity,
Filter,
OneToMany,
OnInit,
OptionalProps,
PrimaryKey,
Property,
Rel,
} from "@mikro-orm/core"
import ShippingOption from "./shipping-option"
import { ShippingOption } from "./shipping-option"
type ShippingProfileOptionalProps = DAL.SoftDeletableModelDateColumns
const DeletedAtIndex = createPsqlIndexStatementHelper({
tableName: "shipping_profile",
columns: "deleted_at",
where: "deleted_at IS NOT NULL",
})
const ShippingProfileTypeIndex = createPsqlIndexStatementHelper({
tableName: "shipping_profile",
columns: "name",
unique: true,
where: "deleted_at IS NULL",
})
@Entity()
@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions)
export default class ShippingProfile {
[OptionalProps]?: ShippingProfileOptionalProps
@PrimaryKey({ columnType: "text" })
id: string
@Searchable()
@Property({ columnType: "text" })
@ShippingProfileTypeIndex.MikroORMIndex()
name: string
@Searchable()
@Property({ columnType: "text" })
type: string
@OneToMany(
() => ShippingOption,
(shippingOption) => shippingOption.shipping_profile
)
shipping_options = new Collection<Rel<ShippingOption>>(this)
@Property({ columnType: "jsonb", nullable: true })
metadata: Record<string, unknown> | null = null
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
export const ShippingProfile = model
.define("shipping_profile", {
id: model.id({ prefix: "sp" }).primaryKey(),
name: model.text(),
type: model.text(),
shipping_options: model.hasMany(() => ShippingOption, {
mappedBy: "shipping_profile",
}),
metadata: model.json().nullable(),
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
@DeletedAtIndex.MikroORMIndex()
@Property({ columnType: "timestamptz", nullable: true })
deleted_at: Date | null = null
@BeforeCreate()
onCreate() {
this.id = generateEntityId(this.id, "sp")
}
@OnInit()
onInit() {
this.id = generateEntityId(this.id, "sp")
}
}
.indexes([
{
on: ["name"],
unique: true,
where: "deleted_at IS NULL",
},
])