diff --git a/.changeset/spicy-pillows-drum.md b/.changeset/spicy-pillows-drum.md new file mode 100644 index 0000000000..4aaf21d2c4 --- /dev/null +++ b/.changeset/spicy-pillows-drum.md @@ -0,0 +1,6 @@ +--- +"@medusajs/types": patch +"@medusajs/utils": patch +--- + +feat(fulfillment): Initialize models work diff --git a/packages/fulfillment/mikro-orm.config.dev.ts b/packages/fulfillment/mikro-orm.config.dev.ts index 0cf4358c6c..905695bc8d 100644 --- a/packages/fulfillment/mikro-orm.config.dev.ts +++ b/packages/fulfillment/mikro-orm.config.dev.ts @@ -1,8 +1,12 @@ import * as entities from "./src/models" +import { TSMigrationGenerator } from "@medusajs/utils" module.exports = { entities: Object.values(entities), schema: "public", clientUrl: "postgres://postgres@localhost/medusa-fulfillment", type: "postgresql", + migrations: { + generator: TSMigrationGenerator, + }, } diff --git a/packages/fulfillment/src/models/address.ts b/packages/fulfillment/src/models/address.ts new file mode 100644 index 0000000000..80a64345e1 --- /dev/null +++ b/packages/fulfillment/src/models/address.ts @@ -0,0 +1,112 @@ +import { DAL } from "@medusajs/types" +import { + createPsqlIndexStatementHelper, + generateEntityId, +} from "@medusajs/utils" +import { + BeforeCreate, + Entity, + Index, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" + +type OptionalAddressProps = DAL.SoftDeletableEntityDateColumns + +const fulfillmentIdIndexName = "IDX_fulfillment_address_fulfillment_id" +const fulfillmentIdIndexStatement = createPsqlIndexStatementHelper({ + name: fulfillmentIdIndexName, + tableName: "fulfillment_address", + columns: "fulfillment_id", + where: "deleted_at IS NULL", +}) + +const fulfillmentDeletedAtIndexName = "IDX_fulfillment_address_deleted_at" +const fulfillmentDeletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: fulfillmentDeletedAtIndexName, + tableName: "fulfillment_address", + columns: "deleted_at", + where: "deleted_at IS NOT NULL", +}) + +@Entity({ tableName: "fulfillment_address" }) +export default class Address { + [OptionalProps]: OptionalAddressProps + + @PrimaryKey({ columnType: "text" }) + id!: string + + @Property({ columnType: "text", nullable: true }) + @Index({ + name: fulfillmentIdIndexName, + expression: fulfillmentIdIndexStatement, + }) + fulfillment_id: string | null = null + + @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 | 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 }) + @Index({ + name: fulfillmentDeletedAtIndexName, + expression: fulfillmentDeletedAtIndexStatement, + }) + deleted_at: Date | null = null + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "fuladdr") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "fuladdr") + } +} diff --git a/packages/fulfillment/src/models/fulfillment-item.ts b/packages/fulfillment/src/models/fulfillment-item.ts new file mode 100644 index 0000000000..4baaf05380 --- /dev/null +++ b/packages/fulfillment/src/models/fulfillment-item.ts @@ -0,0 +1,130 @@ +import { + createPsqlIndexStatementHelper, + DALUtils, + generateEntityId, +} from "@medusajs/utils" + +import { + BeforeCreate, + Entity, + Filter, + Index, + ManyToOne, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { DAL } from "@medusajs/types" +import Fulfillment from "./fulfillment" + +type FulfillmentItemOptionalProps = DAL.SoftDeletableEntityDateColumns + +const fulfillmentIdIndexName = "IDX_fulfillment_item_fulfillment_id" +const fulfillmentIdIndexStatement = createPsqlIndexStatementHelper({ + name: fulfillmentIdIndexName, + tableName: "fulfillment_item", + columns: "fulfillment_id", + where: "deleted_at IS NULL", +}) + +const lineItemIndexName = "IDX_fulfillment_item_line_item_id" +const lineItemIdIndexStatement = createPsqlIndexStatementHelper({ + name: fulfillmentIdIndexName, + tableName: "fulfillment_item", + columns: "line_item_id", + where: "deleted_at IS NULL", +}) + +const inventoryItemIndexName = "IDX_fulfillment_item_inventory_item_id" +const inventoryItemIdIndexStatement = createPsqlIndexStatementHelper({ + name: fulfillmentIdIndexName, + tableName: "fulfillment_item", + columns: "inventory_item_id", + where: "deleted_at IS NULL", +}) + +const fulfillmentItemDeletedAtIndexName = "IDX_fulfillment_item_deleted_at" +const fulfillmentItemDeletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: fulfillmentItemDeletedAtIndexName, + 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 + + @Property({ columnType: "numeric", serializer: Number }) + quantity: number // TODO: probably allow big numbers here + + @Property({ columnType: "text", nullable: true }) + @Index({ + name: lineItemIndexName, + expression: lineItemIdIndexStatement, + }) + line_item_id: string | null = null + + @Property({ columnType: "text", nullable: true }) + @Index({ + name: inventoryItemIndexName, + expression: inventoryItemIdIndexStatement, + }) + inventory_item_id: string | null = null + + @Property({ columnType: "text" }) + @Index({ + name: fulfillmentIdIndexName, + expression: fulfillmentIdIndexStatement, + }) + fulfillment_id: string + + @ManyToOne(() => Fulfillment) + fulfillment: 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 + + @Index({ + name: fulfillmentItemDeletedAtIndexName, + expression: fulfillmentItemDeletedAtIndexStatement, + }) + @Property({ columnType: "timestamptz", nullable: true }) + deleted_at: Date | null = null + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "fulit") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "fulit") + } +} diff --git a/packages/fulfillment/src/models/fulfillment-label.ts b/packages/fulfillment/src/models/fulfillment-label.ts new file mode 100644 index 0000000000..585e92b8ae --- /dev/null +++ b/packages/fulfillment/src/models/fulfillment-label.ts @@ -0,0 +1,112 @@ +import { + createPsqlIndexStatementHelper, + DALUtils, + generateEntityId, +} from "@medusajs/utils" + +import { + BeforeCreate, + Entity, + Filter, + Index, + ManyToOne, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { DAL } from "@medusajs/types" +import Fulfillment from "./fulfillment" + +type FulfillmentLabelOptionalProps = DAL.SoftDeletableEntityDateColumns + +const fulfillmentIdIndexName = "IDX_fulfillment_label_fulfillment_id" +const fulfillmentIdIndexStatement = createPsqlIndexStatementHelper({ + name: fulfillmentIdIndexName, + tableName: "fulfillment_label", + columns: "fulfillment_id", + where: "deleted_at IS NULL", +}) + +const providerIdIndexName = "IDX_fulfillment_label_provider_id" +const providerIdIndexStatement = createPsqlIndexStatementHelper({ + name: providerIdIndexName, + tableName: "fulfillment_label", + columns: "provider_id", + where: "deleted_at IS NULL", +}) + +const deletedAtIndexName = "IDX_fulfillment_label_deleted_at" +const deletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: deletedAtIndexName, + 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 + + @Property({ columnType: "text" }) + @Index({ + name: fulfillmentIdIndexName, + expression: fulfillmentIdIndexStatement, + }) + provider_id: string + + @Property({ columnType: "text" }) + @Index({ + name: providerIdIndexName, + expression: providerIdIndexStatement, + }) + fulfillment_id: string + + @ManyToOne(() => Fulfillment) + fulfillment: 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 }) + @Index({ + name: deletedAtIndexName, + expression: deletedAtIndexStatement, + }) + deleted_at: Date | null = null + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "fulla") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "fulla") + } +} diff --git a/packages/fulfillment/src/models/fulfillment.ts b/packages/fulfillment/src/models/fulfillment.ts new file mode 100644 index 0000000000..4f3731e84c --- /dev/null +++ b/packages/fulfillment/src/models/fulfillment.ts @@ -0,0 +1,168 @@ +import { + createPsqlIndexStatementHelper, + DALUtils, + generateEntityId, +} from "@medusajs/utils" + +import { + BeforeCreate, + Collection, + Entity, + Filter, + Index, + ManyToOne, + OneToMany, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { DAL } from "@medusajs/types" +import ShippingOption from "./shipping-option" +import ServiceProvider from "./service-provider" +import Address from "./address" +import FulfillmentItem from "./fulfillment-item" +import FulfillmentLabel from "./fulfillment-label" + +type FulfillmentOptionalProps = DAL.SoftDeletableEntityDateColumns + +const fulfillmentDeletedAtIndexName = "IDX_fulfillment_deleted_at" +const fulfillmentDeletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: fulfillmentDeletedAtIndexName, + tableName: "fulfillment", + columns: "deleted_at", + where: "deleted_at IS NOT NULL", +}) + +const fulfillmentProviderIdIndexName = "IDX_fulfillment_provider_id" +const fulfillmentProviderIdIndexStatement = createPsqlIndexStatementHelper({ + name: fulfillmentProviderIdIndexName, + tableName: "fulfillment", + columns: "provider_id", + where: "deleted_at IS NULL", +}) + +const fulfillmentLocationIdIndexName = "IDX_fulfillment_location_id" +const fulfillmentLocationIdIndexStatement = createPsqlIndexStatementHelper({ + name: fulfillmentLocationIdIndexName, + tableName: "fulfillment", + columns: "location_id", + where: "deleted_at IS NULL", +}) + +const fulfillmentShippingOptionIdIndexName = + "IDX_fulfillment_shipping_option_id" +const fulfillmentShippingOptionIdIndexStatement = + createPsqlIndexStatementHelper({ + name: fulfillmentShippingOptionIdIndexName, + 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" }) + @Index({ + name: fulfillmentLocationIdIndexName, + expression: fulfillmentLocationIdIndexStatement, + }) + location_id: string + + @Property({ + columnType: "timestamptz", + nullable: true, + }) + packed_at: Date | null = null + + @Property({ + columnType: "timestamptz", + nullable: true, + }) + shipped_at: Date | 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 | null = null + + @Property({ columnType: "text" }) + @Index({ + name: fulfillmentProviderIdIndexName, + expression: fulfillmentProviderIdIndexStatement, + }) + provider_id: string + + @Property({ columnType: "text", nullable: true }) + @Index({ + name: fulfillmentShippingOptionIdIndexName, + expression: fulfillmentShippingOptionIdIndexStatement, + }) + shipping_option_id: string | null = null + + @Property({ columnType: "jsonb", nullable: true }) + metadata: Record | null = null + + @ManyToOne(() => ShippingOption, { nullable: true }) + shipping_option: ShippingOption | null + + @ManyToOne(() => ServiceProvider) + provider: ServiceProvider + + @ManyToOne(() => Address) + delivery_address: Address + + @ManyToOne(() => FulfillmentItem) + items: FulfillmentItem + + @OneToMany(() => FulfillmentLabel, (label) => label.fulfillment) + labels = new Collection(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: fulfillmentDeletedAtIndexName, + expression: fulfillmentDeletedAtIndexStatement, + }) + @Property({ columnType: "timestamptz", nullable: true }) + deleted_at: Date | null = null + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "ful") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "ful") + } +} diff --git a/packages/fulfillment/src/models/fullfilment-set.ts b/packages/fulfillment/src/models/fullfilment-set.ts index 2a9af29ecd..54eaaf5f3c 100644 --- a/packages/fulfillment/src/models/fullfilment-set.ts +++ b/packages/fulfillment/src/models/fullfilment-set.ts @@ -1,19 +1,34 @@ -import { DALUtils, generateEntityId } from "@medusajs/utils" +import { + createPsqlIndexStatementHelper, + DALUtils, + generateEntityId, +} from "@medusajs/utils" import { BeforeCreate, + Collection, Entity, Filter, Index, + ManyToMany, OnInit, OptionalProps, PrimaryKey, Property, } from "@mikro-orm/core" import { DAL } from "@medusajs/types" +import ServiceZone from "./service-zone" type FulfillmentSetOptionalProps = DAL.SoftDeletableEntityDateColumns +const deletedAtIndexName = "IDX_fulfillment_set_deleted_at" +const deletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: deletedAtIndexName, + tableName: "fulfillment_set", + columns: "deleted_at", + where: "deleted_at IS NOT NULL", +}) + @Entity() @Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) export default class FulfillmentSet { @@ -25,6 +40,20 @@ export default class FulfillmentSet { @Property({ columnType: "text" }) name: string + @Property({ columnType: "text" }) + type: string + + @Property({ columnType: "jsonb", nullable: true }) + metadata: Record | null = null + + @ManyToMany(() => ServiceZone, "fulfillment_sets", { + owner: true, + pivotTable: "fulfillment_set_service_zones", + joinColumn: "fulfillment_set_id", + inverseJoinColumn: "service_zone_id", + }) + service_zones = new Collection(this) + @Property({ onCreate: () => new Date(), columnType: "timestamptz", @@ -40,8 +69,11 @@ export default class FulfillmentSet { }) updated_at: Date - @Index({ name: "IDX_fulfillment_set_deleted_at" }) @Property({ columnType: "timestamptz", nullable: true }) + @Index({ + name: deletedAtIndexName, + expression: deletedAtIndexStatement, + }) deleted_at: Date | null = null @BeforeCreate() diff --git a/packages/fulfillment/src/models/geo-zone.ts b/packages/fulfillment/src/models/geo-zone.ts new file mode 100644 index 0000000000..f7f6074a41 --- /dev/null +++ b/packages/fulfillment/src/models/geo-zone.ts @@ -0,0 +1,131 @@ +import { + createPsqlIndexStatementHelper, + DALUtils, + generateEntityId, + GeoZoneType, +} from "@medusajs/utils" + +import { + BeforeCreate, + Collection, + Entity, + Enum, + Filter, + Index, + ManyToMany, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { DAL } from "@medusajs/types" +import ServiceZone from "./service-zone" + +type GeoZoneOptionalProps = DAL.SoftDeletableEntityDateColumns + +const deletedAtIndexName = "IDX_geo_zone_deleted_at" +const deletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: deletedAtIndexName, + tableName: "geo_zone", + columns: "deleted_at", + where: "deleted_at IS NOT NULL", +}) + +const countryCodeIndexName = "IDX_geo_zone_country_code" +const countryCodeIndexStatement = createPsqlIndexStatementHelper({ + name: countryCodeIndexName, + tableName: "geo_zone", + columns: "country_code", + where: "deleted_at IS NULL", +}) + +const provinceCodeIndexName = "IDX_geo_zone_province_code" +const provinceCodeIndexStatement = createPsqlIndexStatementHelper({ + name: provinceCodeIndexName, + tableName: "geo_zone", + columns: "province_code", + where: "deleted_at IS NULL AND province_code IS NOT NULL", +}) + +const cityIndexName = "IDX_geo_zone_city" +const cityIndexStatement = createPsqlIndexStatementHelper({ + name: cityIndexName, + tableName: "geo_zone", + columns: "city", + where: "deleted_at IS NULL AND city IS NOT NULL", +}) + +@Entity() +@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) +export default class GeoZone { + [OptionalProps]?: GeoZoneOptionalProps + + @PrimaryKey({ columnType: "text" }) + id: string + + @Enum({ items: () => GeoZoneType, default: GeoZoneType.COUNTRY }) + type: GeoZoneType + + @Index({ + name: countryCodeIndexName, + expression: countryCodeIndexStatement, + }) + @Property({ columnType: "text" }) + country_code: string + + @Index({ + name: provinceCodeIndexName, + expression: provinceCodeIndexStatement, + }) + @Property({ columnType: "text", nullable: true }) + province_code: string | null = null + + @Index({ + name: cityIndexName, + expression: cityIndexStatement, + }) + @Property({ columnType: "text", nullable: true }) + city: string | null = null + + // 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 }) + postal_expression: Record | null = null + + @Property({ columnType: "jsonb", nullable: true }) + metadata: Record | null = null + + @ManyToMany(() => ServiceZone, (serviceZone) => serviceZone.geo_zones) + service_zones = new Collection(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, " fgz") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "fgz") + } +} diff --git a/packages/fulfillment/src/models/index.ts b/packages/fulfillment/src/models/index.ts index 4a32567351..9408a1bfad 100644 --- a/packages/fulfillment/src/models/index.ts +++ b/packages/fulfillment/src/models/index.ts @@ -1 +1,12 @@ export { default as FulfillmentSet } from "./fullfilment-set" +export { default as Fulfillment } from "./fulfillment" +export { default as Address } from "./address" +export { default as GeoZone } from "./geo-zone" +export { default as ServiceZone } from "./service-zone" +export { default as FulfillmentItem } from "./fulfillment-item" +export { default as FulfillmentLabel } from "./fulfillment-label" +export { default as ServiceProvider } from "./service-provider" +export { default as ShippingOption } from "./shipping-option" +export { default as ShippingOptionType } from "./shipping-option-type" +export { default as ShippingOptionRule } from "./shipping-option-rule" +export { default as ShippingProfile } from "./shipping-profile" diff --git a/packages/fulfillment/src/models/service-provider.ts b/packages/fulfillment/src/models/service-provider.ts new file mode 100644 index 0000000000..414418ee27 --- /dev/null +++ b/packages/fulfillment/src/models/service-provider.ts @@ -0,0 +1,80 @@ +import { + createPsqlIndexStatementHelper, + DALUtils, + generateEntityId, +} from "@medusajs/utils" + +import { + BeforeCreate, + Collection, + Entity, + Filter, + Index, + OneToMany, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { DAL } from "@medusajs/types" +import ShippingOption from "./shipping-option" + +type ServiceProviderOptionalProps = DAL.SoftDeletableEntityDateColumns + +const deletedAtIndexName = "IDX_service_provider_deleted_at" +const deletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: deletedAtIndexName, + tableName: "service_provider", + columns: "deleted_at", + where: "deleted_at IS NOT NULL", +}) + +@Entity() +@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) +export default class ServiceProvider { + [OptionalProps]?: ServiceProviderOptionalProps + + @PrimaryKey({ columnType: "text" }) + id: string + + @Property({ columnType: "jsonb", nullable: true }) + metadata: Record | null = null + + @OneToMany( + () => ShippingOption, + (shippingOption) => shippingOption.service_provider + ) + shipping_options = new Collection(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, "serpro") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "serpro") + } +} diff --git a/packages/fulfillment/src/models/service-zone.ts b/packages/fulfillment/src/models/service-zone.ts new file mode 100644 index 0000000000..415570e53f --- /dev/null +++ b/packages/fulfillment/src/models/service-zone.ts @@ -0,0 +1,100 @@ +import { + createPsqlIndexStatementHelper, + DALUtils, + generateEntityId, +} from "@medusajs/utils" + +import { + BeforeCreate, + Collection, + Entity, + Filter, + Index, + ManyToMany, + OneToMany, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { DAL } from "@medusajs/types" +import FulfillmentSet from "./fullfilment-set" +import GeoZone from "./geo-zone" +import ShippingOption from "./shipping-option" + +type ServiceZoneOptionalProps = DAL.SoftDeletableEntityDateColumns + +const deletedAtIndexName = "IDX_service_zone_deleted_at" +const deletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: deletedAtIndexName, + tableName: "service_zone", + columns: "deleted_at", + where: "deleted_at IS NOT NULL", +}) + +@Entity() +@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) +export default class ServiceZone { + [OptionalProps]?: ServiceZoneOptionalProps + + @PrimaryKey({ columnType: "text" }) + id: string + + @Property({ columnType: "text" }) + name: string + + @Property({ columnType: "jsonb", nullable: true }) + metadata: Record | null = null + + @ManyToMany( + () => FulfillmentSet, + (fulfillmentSet) => fulfillmentSet.service_zones + ) + fulfillment_sets = new Collection(this) + + @ManyToMany(() => GeoZone, "service_zones", { + owner: true, + pivotTable: "service_zone_geo_zones", + joinColumn: "service_zone_id", + inverseJoinColumn: "geo_zone_id", + }) + geo_zones = new Collection(this) + + @OneToMany( + () => ShippingOption, + (shippingOption) => shippingOption.service_zone + ) + shipping_options = new Collection(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") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "serzo") + } +} diff --git a/packages/fulfillment/src/models/shipping-option-rule.ts b/packages/fulfillment/src/models/shipping-option-rule.ts new file mode 100644 index 0000000000..8cc79c494d --- /dev/null +++ b/packages/fulfillment/src/models/shipping-option-rule.ts @@ -0,0 +1,88 @@ +import { + createPsqlIndexStatementHelper, + DALUtils, + generateEntityId, +} from "@medusajs/utils" + +import { + BeforeCreate, + Entity, + Filter, + Index, + ManyToOne, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { DAL } from "@medusajs/types" +import ShippingOption from "./shipping-option" + +type ShippingOptionRuleOptionalProps = DAL.SoftDeletableEntityDateColumns + +// TODO: need some test to see if we continue with this kind of structure or we change it +// More adjustments can appear as I move forward + +const deletedAtIndexName = "IDX_shipping_option_rule_deleted_at" +const deletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: deletedAtIndexName, + tableName: "shipping_option_rule", + columns: "deleted_at", + where: "deleted_at IS NOT NULL", +}) + +@Entity() +@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) +export default class ShippingOptionRule { + [OptionalProps]?: ShippingOptionRuleOptionalProps + + @PrimaryKey({ columnType: "text" }) + id: string + + @Property({ columnType: "text" }) + attribute: string + + @Property({ columnType: "text" }) + operator: string + + @Property({ columnType: "jsonb", nullable: true }) + value: { value: string | string[] } | null = null + + @Property({ columnType: "text" }) + shipping_option_id: string + + @ManyToOne(() => ShippingOption) + shipping_option: 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 + + @Index({ + name: deletedAtIndexName, + expression: deletedAtIndexStatement, + }) + @Property({ columnType: "timestamptz", nullable: true }) + deleted_at: Date | null = null + + @BeforeCreate() + onCreate() { + this.id = generateEntityId(this.id, "sorul") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "sorul") + } +} diff --git a/packages/fulfillment/src/models/shipping-option-type.ts b/packages/fulfillment/src/models/shipping-option-type.ts new file mode 100644 index 0000000000..be1b4438ed --- /dev/null +++ b/packages/fulfillment/src/models/shipping-option-type.ts @@ -0,0 +1,79 @@ +import { + createPsqlIndexStatementHelper, + DALUtils, + generateEntityId, +} from "@medusajs/utils" + +import { + BeforeCreate, + Entity, + Filter, + Index, + OneToOne, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { DAL } from "@medusajs/types" +import ShippingOption from "./shipping-option" + +type ShippingOptionTypeOptionalProps = DAL.SoftDeletableEntityDateColumns + +const deletedAtIndexName = "IDX_shipping_option_type_deleted_at" +const deletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: deletedAtIndexName, + tableName: "shipping_option_type", + columns: "deleted_at", + where: "deleted_at IS NOT NULL", +}) + +@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 + + @OneToOne(() => ShippingOption, (so) => so.shipping_option_type) + shipping_option: 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 + + @Index({ + name: deletedAtIndexName, + expression: deletedAtIndexStatement, + }) + @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") + } +} diff --git a/packages/fulfillment/src/models/shipping-option.ts b/packages/fulfillment/src/models/shipping-option.ts new file mode 100644 index 0000000000..0e913d05b9 --- /dev/null +++ b/packages/fulfillment/src/models/shipping-option.ts @@ -0,0 +1,176 @@ +import { + createPsqlIndexStatementHelper, + DALUtils, + generateEntityId, + ShippingOptionPriceType, +} from "@medusajs/utils" + +import { + BeforeCreate, + Collection, + Entity, + Enum, + Filter, + Index, + ManyToOne, + OneToMany, + OneToOne, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { DAL } from "@medusajs/types" +import ServiceZone from "./service-zone" +import ShippingProfile from "./shipping-profile" +import ServiceProvider from "./service-provider" +import ShippingOptionType from "./shipping-option-type" +import ShippingOptionRule from "./shipping-option-rule" +import Fulfillment from "./fulfillment" + +type ShippingOptionOptionalProps = DAL.SoftDeletableEntityDateColumns + +const deletedAtIndexName = "IDX_shipping_option_deleted_at" +const deletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: deletedAtIndexName, + tableName: "shipping_option", + columns: "deleted_at", + where: "deleted_at IS NOT NULL", +}) + +const serviceZoneIdIndexName = "IDX_shipping_option_service_zone_id" +const serviceZoneIdIndexStatement = createPsqlIndexStatementHelper({ + name: serviceZoneIdIndexName, + tableName: "shipping_option", + columns: "service_zone_id", + where: "deleted_at IS NULL", +}) + +const shippingProfileIdIndexName = "IDX_shipping_option_shipping_profile_id" +const shippingProfileIdIndexStatement = createPsqlIndexStatementHelper({ + name: shippingProfileIdIndexName, + tableName: "shipping_option", + columns: "shipping_profile_id", + where: "deleted_at IS NULL", +}) + +const serviceProviderIdIndexName = "IDX_shipping_option_service_provider_id" +const serviceProviderIdIndexStatement = createPsqlIndexStatementHelper({ + name: serviceProviderIdIndexName, + tableName: "shipping_option", + columns: "service_provider_id", + where: "deleted_at IS NULL", +}) + +const shippingOptionTypeIdIndexName = + "IDX_shipping_option_shipping_option_type_id" +const shippingOptionTypeIdIndexStatement = createPsqlIndexStatementHelper({ + name: shippingOptionTypeIdIndexName, + 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 + + @Property({ columnType: "text" }) + name: string + + @Enum({ + items: () => ShippingOptionPriceType, + default: ShippingOptionPriceType.CALCULATED, + }) + price_type: ShippingOptionPriceType + + @Property({ columnType: "text" }) + @Index({ + name: serviceZoneIdIndexName, + expression: serviceZoneIdIndexStatement, + }) + service_zone_id: string + + @Property({ columnType: "text" }) + @Index({ + name: shippingProfileIdIndexName, + expression: shippingProfileIdIndexStatement, + }) + shipping_profile_id: string + + @Property({ columnType: "text" }) + @Index({ + name: serviceProviderIdIndexName, + expression: serviceProviderIdIndexStatement, + }) + service_provider_id: string + + @Property({ columnType: "text", nullable: true }) + @Index({ + name: shippingOptionTypeIdIndexName, + expression: shippingOptionTypeIdIndexStatement, + }) + shipping_option_type_id: string | null = null + + @Property({ columnType: "jsonb", nullable: true }) + data: Record | null = null + + @Property({ columnType: "jsonb", nullable: true }) + metadata: Record | null = null + + @ManyToOne(() => ServiceZone) + service_zone: ServiceZone + + @ManyToOne(() => ShippingProfile) + shipping_profile: ShippingProfile + + @ManyToOne(() => ServiceProvider) + service_provider: ServiceProvider + + @OneToOne(() => ShippingOptionType, (so) => so.shipping_option, { + owner: true, + }) + shipping_option_type: ShippingOptionType + + @OneToMany(() => ShippingOptionRule, (sor) => sor.shipping_option) + rules = new Collection(this) + + @OneToMany(() => Fulfillment, (fulfillment) => fulfillment.shipping_option) + fulfillments = new Collection(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, "so") + } + + @OnInit() + onInit() { + this.id = generateEntityId(this.id, "so") + } +} diff --git a/packages/fulfillment/src/models/shipping-profile.ts b/packages/fulfillment/src/models/shipping-profile.ts new file mode 100644 index 0000000000..f2371ec575 --- /dev/null +++ b/packages/fulfillment/src/models/shipping-profile.ts @@ -0,0 +1,80 @@ +import { + createPsqlIndexStatementHelper, + DALUtils, + generateEntityId, +} from "@medusajs/utils" + +import { + BeforeCreate, + Collection, + Entity, + Filter, + Index, + OneToMany, + OnInit, + OptionalProps, + PrimaryKey, + Property, +} from "@mikro-orm/core" +import { DAL } from "@medusajs/types" +import ShippingOption from "./shipping-option" + +type ShippingProfileOptionalProps = DAL.SoftDeletableEntityDateColumns + +const deletedAtIndexName = "IDX_shipping_profile_deleted_at" +const deletedAtIndexStatement = createPsqlIndexStatementHelper({ + name: deletedAtIndexName, + tableName: "shipping_profile", + columns: "deleted_at", + where: "deleted_at IS NOT NULL", +}) + +@Entity() +@Filter(DALUtils.mikroOrmSoftDeletableFilterOptions) +export default class ShippingProfile { + [OptionalProps]?: ShippingProfileOptionalProps + + @PrimaryKey({ columnType: "text" }) + id: string + + @OneToMany( + () => ShippingOption, + (shippingOption) => shippingOption.shipping_profile + ) + shipping_options = new Collection(this) + + @Property({ columnType: "jsonb", nullable: true }) + metadata: Record | 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 + + @Index({ + name: deletedAtIndexName, + expression: deletedAtIndexStatement, + }) + @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") + } +} diff --git a/packages/types/src/fulfillment/common.ts b/packages/types/src/fulfillment/common.ts index bcc8e44f07..04b1cf5fbf 100644 --- a/packages/types/src/fulfillment/common.ts +++ b/packages/types/src/fulfillment/common.ts @@ -1,3 +1,5 @@ +export type GeoZoneType = "country" | "province" | "city" | "zip" + export interface FulfillmentDTO { id: string name: string diff --git a/packages/utils/src/bundles.ts b/packages/utils/src/bundles.ts index 3f43e1d849..2279b1fe14 100644 --- a/packages/utils/src/bundles.ts +++ b/packages/utils/src/bundles.ts @@ -3,10 +3,10 @@ export * as DecoratorUtils from "./decorators" export * as DefaultsUtils from "./defaults" export * as EventBusUtils from "./event-bus" export * as FeatureFlagUtils from "./feature-flags" +export * as FulfillmentUtils from "./fulfillment" export * as ModulesSdkUtils from "./modules-sdk" export * as OrchestrationUtils from "./orchestration" export * as ProductUtils from "./product" export * as PromotionUtils from "./promotion" export * as SearchUtils from "./search" export * as ShippingProfileUtils from "./shipping" - diff --git a/packages/utils/src/common/__tests__/create-psql-index-helper.ts b/packages/utils/src/common/__tests__/create-psql-index-helper.ts new file mode 100644 index 0000000000..56166a2656 --- /dev/null +++ b/packages/utils/src/common/__tests__/create-psql-index-helper.ts @@ -0,0 +1,64 @@ +import { createPsqlIndexStatementHelper } from "../create-psql-index-helper" + +describe("createPsqlIndexStatementHelper", function () { + it("should generate a simple index", function () { + const options = { + name: "index_name", + tableName: "table_name", + columns: "column_name", + } + + const indexStatement = createPsqlIndexStatementHelper(options) + expect(indexStatement).toEqual( + `CREATE INDEX IF NOT EXISTS ${options.name} ON ${options.tableName} (${options.columns})` + ) + }) + + it("should generate a composite index", function () { + const options = { + name: "index_name", + tableName: "table_name", + columns: ["column_name_1", "column_name_2"], + } + + const indexStatement = createPsqlIndexStatementHelper(options) + expect(indexStatement).toEqual( + `CREATE INDEX IF NOT EXISTS ${options.name} ON ${ + options.tableName + } (${options.columns.join(", ")})` + ) + }) + + it("should generate an index with where clauses", function () { + const options = { + name: "index_name", + tableName: "table_name", + columns: ["column_name_1", "column_name_2"], + where: "column_name_1 IS NOT NULL", + } + + const indexStatement = createPsqlIndexStatementHelper(options) + expect(indexStatement).toEqual( + `CREATE INDEX IF NOT EXISTS ${options.name} ON ${ + options.tableName + } (${options.columns.join(", ")}) WHERE ${options.where}` + ) + }) + + it("should generate an index with where clauses and index type", function () { + const options = { + name: "index_name", + tableName: "table_name", + columns: ["column_name_1", "column_name_2"], + type: "GIN", + where: "column_name_1 IS NOT NULL", + } + + const indexStatement = createPsqlIndexStatementHelper(options) + expect(indexStatement).toEqual( + `CREATE INDEX IF NOT EXISTS ${options.name} ON ${ + options.tableName + } USING GIN (${options.columns.join(", ")}) WHERE ${options.where}` + ) + }) +}) diff --git a/packages/utils/src/common/create-psql-index-helper.ts b/packages/utils/src/common/create-psql-index-helper.ts new file mode 100644 index 0000000000..a2a030dcbc --- /dev/null +++ b/packages/utils/src/common/create-psql-index-helper.ts @@ -0,0 +1,47 @@ +/** + * Create a PSQL index statement + * @param name The name of the index + * @param tableName The name of the table + * @param columns The columns to index + * @param type The type of index (e.g GIN, GIST, BTREE, etc) + * @param where The where clause + * + * @example + * createPsqlIndexStatementHelper({ + * name: "idx_user_email", + * tableName: "user", + * columns: "email", + * type: "btree", + * where: "email IS NOT NULL" + * }); + * + * // CREATE INDEX IF NOT EXISTS idx_user_email ON user USING btree (email) WHERE email IS NOT NULL; + * + * createPsqlIndexStatementHelper({ + * name: "idx_user_email", + * tableName: "user", + * columns: "email" + * }); + * + * // CREATE INDEX IF NOT EXISTS idx_user_email ON user (email); + * + */ +export function createPsqlIndexStatementHelper({ + name, + tableName, + columns, + type, + where, +}: { + name: string + tableName: string + columns: string | string[] + type?: string + where?: string +}) { + columns = Array.isArray(columns) ? columns.join(", ") : columns + const typeStr = type ? ` USING ${type}` : "" + const optionsStr = where ? ` WHERE ${where}` : "" + + return `CREATE INDEX IF NOT EXISTS ${name} ON ${tableName}${typeStr} (${columns})${optionsStr}` +} diff --git a/packages/utils/src/common/index.ts b/packages/utils/src/common/index.ts index 01066b41e7..2c8a76c74e 100644 --- a/packages/utils/src/common/index.ts +++ b/packages/utils/src/common/index.ts @@ -3,6 +3,7 @@ export * from "./build-query" export * from "./camel-to-snake-case" export * from "./container" export * from "./create-container-like" +export * from "./create-psql-index-helper" export * from "./deduplicate" export * from "./deep-equal-obj" export * from "./errors" diff --git a/packages/utils/src/dal/mikro-orm/__tests__/ts-migration-generator.spec.ts b/packages/utils/src/dal/mikro-orm/__tests__/ts-migration-generator.spec.ts new file mode 100644 index 0000000000..b6328a421c --- /dev/null +++ b/packages/utils/src/dal/mikro-orm/__tests__/ts-migration-generator.spec.ts @@ -0,0 +1,111 @@ +import { TSMigrationGenerator } from "../mikro-orm-create-connection" + +function unwrapSql(sql: string) { + return sql.match(/this.addSql\('(.*?)'\)/)?.[1] +} + +describe("TSMigrationGenerator", () => { + it('should add "if not exists" to "create table" statements', () => { + const sql = "create table my_table (id int)" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe("create table if not exists my_table (id int)") + }) + + it('should add "if exists" to "alter table" statements as well as to the add column', () => { + const sql = "alter table my_table add column name varchar(100)" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe( + "alter table if exists my_table add column if not exists name varchar(100)" + ) + }) + + it('should add "if exists" to "alter table" statements as well as to the drop column', () => { + const sql = "alter table my_table drop column name" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe( + "alter table if exists my_table drop column if exists name" + ) + }) + + it('should add "if exists" to "alter table" statements as well as to the alter column', () => { + const sql = "alter table my_table alter column name" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe( + "alter table if exists my_table alter column if exists name" + ) + }) + + it('should add "if not exists" to "create index" statements', () => { + const sql = "create index idx_name on my_table(name)" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe("create index if not exists idx_name on my_table(name)") + }) + + it('should add "if exists" to "drop index" statements', () => { + const sql = "drop index idx_name" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe("drop index if exists idx_name") + }) + + it('should add "if not exists" to "create unique index" statements', () => { + const sql = "create unique index idx_unique_name on my_table(name)" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe( + "create unique index if not exists idx_unique_name on my_table(name)" + ) + }) + + it('should add "if exists" to "drop unique index" statements', () => { + const sql = "drop unique index idx_unique_name" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe("drop unique index if exists idx_unique_name") + }) + + it('should add "if not exists" to "add column" statements', () => { + const sql = "add column name varchar(100)" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe("add column if not exists name varchar(100)") + }) + + it('should add "if exists" to "drop column" statements', () => { + const sql = "drop column name" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe("drop column if exists name") + }) + + it('should add "if exists" to "alter column" statements', () => { + const sql = "alter column name" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe("alter column if exists name") + }) + + it('should add "if exists" to "drop constraint" statements', () => { + const sql = "drop constraint fk_name" + const result = unwrapSql( + TSMigrationGenerator.prototype.createStatement(sql, 0) + ) + expect(result).toBe("drop constraint if exists fk_name") + }) +}) diff --git a/packages/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts b/packages/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts index 7b35fa2f9f..e8db1bed15 100644 --- a/packages/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts +++ b/packages/utils/src/dal/mikro-orm/mikro-orm-create-connection.ts @@ -1,4 +1,71 @@ import { ModuleServiceInitializeOptions } from "@medusajs/types" +import { TSMigrationGenerator } from "@mikro-orm/migrations" +import { isString } from "../../common" + +// Monkey patch due to the compilation version issue which prevents us from creating a proper class that extends the TSMigrationGenerator +const originalCreateStatement = TSMigrationGenerator.prototype.createStatement + +/** + * Safe migration generation for MikroORM + * + * @param sql The sql statement + * @param padLeft The padding + * + * @example see test file + */ +TSMigrationGenerator.prototype.createStatement = function ( + sql: string, + padLeft: number +) { + if (isString(sql)) { + if (!sql.includes("create table if not exists")) { + sql = sql.replace("create table", "create table if not exists") + } + + if (!sql.includes("alter table if exists")) { + sql = sql.replace("alter table", "alter table if exists") + } + + if (!sql.includes("create index if not exists")) { + sql = sql.replace("create index", "create index if not exists") + } + + if (!sql.includes("drop index if exists")) { + sql = sql.replace("drop index", "drop index if exists") + } + + if (!sql.includes("create unique index if not exists")) { + sql = sql.replace( + "create unique index", + "create unique index if not exists" + ) + } + + if (!sql.includes("drop unique index if exists")) { + sql = sql.replace("drop unique index", "drop unique index if exists") + } + + if (!sql.includes("add column if not exists")) { + sql = sql.replace("add column", "add column if not exists") + } + + if (!sql.includes("alter column if exists exists")) { + sql = sql.replace("alter column", "alter column if exists") + } + + if (!sql.includes("drop column if exists")) { + sql = sql.replace("drop column", "drop column if exists") + } + + if (!sql.includes("drop constraint if exists")) { + sql = sql.replace("drop constraint", "drop constraint if exists") + } + } + + return originalCreateStatement(sql, padLeft) +} + +export { TSMigrationGenerator } export async function mikroOrmCreateConnection( database: ModuleServiceInitializeOptions["database"] & { connection?: any }, @@ -35,6 +102,7 @@ export async function mikroOrmCreateConnection( type: "postgresql", migrations: { path: pathToMigrations, + generator: TSMigrationGenerator, }, pool: database.pool as any, }) diff --git a/packages/utils/src/fulfillment/geo-zone.ts b/packages/utils/src/fulfillment/geo-zone.ts new file mode 100644 index 0000000000..e498e9288a --- /dev/null +++ b/packages/utils/src/fulfillment/geo-zone.ts @@ -0,0 +1,6 @@ +export enum GeoZoneType { + COUNTRY = "country", + PROVINCE = "province", + CITY = "city", + ZIP = "zip", +} diff --git a/packages/utils/src/fulfillment/index.ts b/packages/utils/src/fulfillment/index.ts new file mode 100644 index 0000000000..969389b91e --- /dev/null +++ b/packages/utils/src/fulfillment/index.ts @@ -0,0 +1,2 @@ +export * from "./geo-zone" +export * from "./shipping-options" diff --git a/packages/utils/src/fulfillment/shipping-options.ts b/packages/utils/src/fulfillment/shipping-options.ts new file mode 100644 index 0000000000..b627607236 --- /dev/null +++ b/packages/utils/src/fulfillment/shipping-options.ts @@ -0,0 +1,4 @@ +export enum ShippingOptionPriceType { + CALCULATED = "calculated", + FLAT = "flat", +} diff --git a/packages/utils/src/index.ts b/packages/utils/src/index.ts index b45e6910d7..814ab3a098 100644 --- a/packages/utils/src/index.ts +++ b/packages/utils/src/index.ts @@ -7,6 +7,7 @@ export * from "./defaults" export * from "./event-bus" export * from "./exceptions" export * from "./feature-flags" +export * from "./fulfillment" export * from "./modules-sdk" export * from "./orchestration" export * from "./payment"