diff --git a/.changeset/clean-news-sin.md b/.changeset/clean-news-sin.md new file mode 100644 index 0000000000000..9593492311b8c --- /dev/null +++ b/.changeset/clean-news-sin.md @@ -0,0 +1,8 @@ +--- +"@medusajs/fulfillment": patch +"@medusajs/types": patch +"@medusajs/utils": patch +"@medusajs/medusa": patch +--- + +chore: fulfillment module DML diff --git a/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts b/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts index 8907c3563ec1a..6ecab2a7f8ae4 100644 --- a/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts +++ b/integration-tests/http/__tests__/shipping-option/admin/shipping-option.spec.ts @@ -237,10 +237,10 @@ medusaIntegrationTestRunner({ }, ]), provider_id: "manual_test-provider", - provider: { + provider: expect.objectContaining({ id: "manual_test-provider", is_enabled: true, - }, + }), rules: [], service_zone_id: expect.any(String), service_zone: { diff --git a/integration-tests/http/__tests__/stock-location/admin/fulfillment-provider.spec.ts b/integration-tests/http/__tests__/stock-location/admin/fulfillment-provider.spec.ts index f3d39a72788eb..24088474ba1b2 100644 --- a/integration-tests/http/__tests__/stock-location/admin/fulfillment-provider.spec.ts +++ b/integration-tests/http/__tests__/stock-location/admin/fulfillment-provider.spec.ts @@ -39,7 +39,10 @@ medusaIntegrationTestRunner({ expect(response.status).toEqual(200) expect(response.data.stock_location.fulfillment_providers).toEqual([ - { id: "manual_test-provider", is_enabled: true }, + expect.objectContaining({ + id: "manual_test-provider", + is_enabled: true, + }), ]) }) diff --git a/integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts b/integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts index aa28d5421304b..c19bb2debdc2e 100644 --- a/integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts +++ b/integration-tests/modules/__tests__/shipping-options/workflows/update-shipping-options.ts @@ -502,6 +502,7 @@ medusaIntegrationTestRunner({ provider_id: provider_id, data: null, metadata: null, + shipping_option_type_id: expect.any(String), type: expect.objectContaining({ id: expect.any(String), code: shippingOptionData.type.code, diff --git a/packages/core/types/src/dml/index.ts b/packages/core/types/src/dml/index.ts index 4f126af40414d..253c4d2df4dd4 100644 --- a/packages/core/types/src/dml/index.ts +++ b/packages/core/types/src/dml/index.ts @@ -147,7 +147,9 @@ export interface EntityConstructor extends Function { */ export type InferForeignKeys = { [K in keyof Schema as Schema[K] extends { $foreignKey: true } - ? `${K & string}_id` + ? Schema[K] extends { $foreignKeyName: `${infer FkName}` } + ? `${FkName & string}` + : `${K & string}_id` : never]: Schema[K] extends { $foreignKey: true } ? null extends Schema[K]["$dataType"] ? string | null diff --git a/packages/core/types/src/fulfillment/mutations/shipping-option.ts b/packages/core/types/src/fulfillment/mutations/shipping-option.ts index d697838eb6a1c..3f76237281876 100644 --- a/packages/core/types/src/fulfillment/mutations/shipping-option.ts +++ b/packages/core/types/src/fulfillment/mutations/shipping-option.ts @@ -87,7 +87,7 @@ export interface UpdateShippingOptionDTO { /** * The shipping option type associated with the shipping option. */ - type: + type?: | Omit | { /** diff --git a/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts b/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts index 4880b2795457b..8c29ad3a08111 100644 --- a/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts +++ b/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts @@ -3018,6 +3018,7 @@ describe("Entity builder", () => { persist: false, name: "user_id", nullable: false, + formula: expect.any(Function), reference: "scalar", setter: false, type: "string", @@ -3123,14 +3124,16 @@ describe("Entity builder", () => { reference: "1:1", name: "email", entity: "Email", + fieldName: "email_id", }, email_id: { columnType: "text", type: "string", reference: "scalar", name: "email_id", + formula: expect.any(Function), nullable: false, - persist: true, + persist: false, getter: false, setter: false, }, @@ -3233,14 +3236,16 @@ describe("Entity builder", () => { name: "emails", entity: "Email", nullable: true, + fieldName: "emails_id", }, emails_id: { columnType: "text", type: "string", reference: "scalar", name: "emails_id", + formula: expect.any(Function), nullable: true, - persist: true, + persist: false, getter: false, setter: false, }, @@ -3334,14 +3339,16 @@ describe("Entity builder", () => { name: "email", entity: "Email", mappedBy: "owner", + fieldName: "email_id", }, email_id: { columnType: "text", type: "string", reference: "scalar", name: "email_id", + formula: expect.any(Function), nullable: false, - persist: true, + persist: false, getter: false, setter: false, }, @@ -3439,14 +3446,16 @@ describe("Entity builder", () => { entity: "Email", cascade: ["persist", "soft-remove"], mappedBy: "user", + fieldName: "email_id", }, email_id: { columnType: "text", type: "string", reference: "scalar", name: "email_id", + formula: expect.any(Function), nullable: false, - persist: true, + persist: false, getter: false, setter: false, }, @@ -3614,14 +3623,16 @@ describe("Entity builder", () => { entity: "Email", cascade: ["persist", "soft-remove"], mappedBy: "user", + fieldName: "email_id", }, email_id: { columnType: "text", type: "string", reference: "scalar", + formula: expect.any(Function), name: "email_id", nullable: false, - persist: true, + persist: false, getter: false, setter: false, }, @@ -3704,6 +3715,7 @@ describe("Entity builder", () => { name: "user_id", nullable: false, reference: "scalar", + formula: expect.any(Function), setter: false, type: "string", persist: false, @@ -4649,6 +4661,7 @@ describe("Entity builder", () => { reference: "scalar", persist: false, type: "string", + formula: expect.any(Function), columnType: "text", nullable: false, name: "user_id", @@ -4848,6 +4861,7 @@ describe("Entity builder", () => { type: "string", columnType: "text", nullable: true, + formula: expect.any(Function), name: "user_id", getter: false, setter: false, @@ -5268,23 +5282,6 @@ describe("Entity builder", () => { }) }) - test("throw error when other side relationship is missing", () => { - const email = model.define("email", { - email: model.text(), - isVerified: model.boolean(), - user: model.belongsTo(() => user), - }) - - const user = model.define("user", { - id: model.number(), - username: model.text(), - }) - - expect(() => toMikroORMEntity(email)).toThrow( - 'Missing property "email" on "User" entity. Make sure to define it as a relationship' - ) - }) - test("throw error when other side relationship is invalid", () => { const email = model.define("email", { email: model.text(), @@ -5482,6 +5479,7 @@ describe("Entity builder", () => { type: "string", persist: false, columnType: "text", + formula: expect.any(Function), nullable: false, name: "user_id", getter: false, @@ -5682,6 +5680,7 @@ describe("Entity builder", () => { type: "string", columnType: "text", nullable: false, + formula: expect.any(Function), name: "user_id", getter: false, setter: false, @@ -5903,6 +5902,7 @@ describe("Entity builder", () => { type: "string", columnType: "text", reference: "scalar", + formula: expect.any(Function), persist: false, getter: false, setter: false, diff --git a/packages/core/utils/src/dml/entity-builder.ts b/packages/core/utils/src/dml/entity-builder.ts index 77d7578684314..0d473a6871842 100644 --- a/packages/core/utils/src/dml/entity-builder.ts +++ b/packages/core/utils/src/dml/entity-builder.ts @@ -3,15 +3,9 @@ import { IDmlEntityConfig, RelationshipOptions, } from "@medusajs/types" -import { DmlEntity } from "./entity" -import { - createBigNumberProperties, - DMLSchemaWithBigNumber, -} from "./helpers/entity-builder/create-big-number-properties" -import { - createDefaultProperties, - DMLSchemaDefaults, -} from "./helpers/entity-builder/create-default-properties" +import { DmlEntity, DMLEntitySchemaBuilder } from "./entity" +import { createBigNumberProperties } from "./helpers/entity-builder/create-big-number-properties" +import { createDefaultProperties } from "./helpers/entity-builder/create-default-properties" import { ArrayProperty } from "./properties/array" import { AutoIncrementProperty } from "./properties/autoincrement" import { BigNumberProperty } from "./properties/big-number" @@ -131,20 +125,14 @@ export class EntityBuilder { define( nameOrConfig: TConfig, schema: Schema - ): DmlEntity< - Schema & DMLSchemaWithBigNumber & DMLSchemaDefaults, - TConfig - > { + ): DmlEntity, TConfig> { this.#disallowImplicitProperties(schema) return new DmlEntity(nameOrConfig, { ...schema, ...createBigNumberProperties(schema), ...createDefaultProperties(), - }) as unknown as DmlEntity< - Schema & DMLSchemaWithBigNumber & DMLSchemaDefaults, - TConfig - > + }) as unknown as DmlEntity, TConfig> } /** @@ -253,7 +241,7 @@ export class EntityBuilder { /** * This method defines a float property that allows for * values with decimal places - * + * * @version 2.1.2 * * @example @@ -398,26 +386,31 @@ export class EntityBuilder { * * @customNamespace Relationship Methods */ - hasOne( + hasOne( entityBuilder: T, options: RelationshipOptions & { foreignKey: true + foreignKeyName?: ForeignKeyName } - ): HasOneWithForeignKey + ): HasOneWithForeignKey hasOne( entityBuilder: T, options?: RelationshipOptions & { foreignKey?: false } ): HasOne - hasOne( + hasOne( entityBuilder: T, options?: RelationshipOptions & { foreignKey?: boolean + foreignKeyName?: ForeignKeyName } - ): HasOneWithForeignKey | HasOne { + ): HasOneWithForeignKey | HasOne { if (options?.foreignKey) { - return new HasOneWithForeignKey(entityBuilder, options || {}) + return new HasOneWithForeignKey( + entityBuilder, + options || {} + ) } return new HasOne(entityBuilder, options || {}) } @@ -445,8 +438,13 @@ export class EntityBuilder { * * @customNamespace Relationship Methods */ - belongsTo(entityBuilder: T, options?: RelationshipOptions) { - return new BelongsTo(entityBuilder, options || {}) + belongsTo( + entityBuilder: T, + options?: RelationshipOptions & { + foreignKeyName?: ForeignKeyName + } + ) { + return new BelongsTo(entityBuilder, options || {}) } /** diff --git a/packages/core/utils/src/dml/entity.ts b/packages/core/utils/src/dml/entity.ts index ba77e613de932..ddcfb2e753ef7 100644 --- a/packages/core/utils/src/dml/entity.ts +++ b/packages/core/utils/src/dml/entity.ts @@ -12,9 +12,16 @@ import { import { isObject, isString, toCamelCase, upperCaseFirst } from "../common" import { transformIndexWhere } from "./helpers/entity-builder/build-indexes" import { BelongsTo } from "./relations/belongs-to" +import { + DMLSchemaDefaults, + DMLSchemaWithBigNumber, +} from "./helpers/entity-builder" const IsDmlEntity = Symbol.for("isDmlEntity") +export type DMLEntitySchemaBuilder = + DMLSchemaWithBigNumber & DMLSchemaDefaults & Schema + function extractNameAndTableName( nameOrConfig: Config ) { diff --git a/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts b/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts index 0d506b91f38db..c1bfaf37d3687 100644 --- a/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts +++ b/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts @@ -7,6 +7,7 @@ import { } from "@medusajs/types" import { BeforeCreate, + BeforeUpdate, Cascade, ManyToMany, ManyToOne, @@ -19,13 +20,13 @@ import { } from "@mikro-orm/core" import { camelToSnakeCase, pluralize } from "../../../common" import { DmlEntity } from "../../entity" +import { BelongsTo } from "../../relations" import { HasMany } from "../../relations/has-many" import { HasOne } from "../../relations/has-one" import { HasOneWithForeignKey } from "../../relations/has-one-fk" import { ManyToMany as DmlManyToMany } from "../../relations/many-to-many" import { applyEntityIndexes } from "../mikro-orm/apply-indexes" import { parseEntityName } from "./parse-entity-name" -import { BelongsTo } from "../../relations" type Context = { MANY_TO_MANY_TRACKED_RELATIONS: Record @@ -181,7 +182,10 @@ export function defineHasOneWithFKRelationship( { relatedModelName }: { relatedModelName: string }, cascades: EntityCascades ) { - const foreignKeyName = camelToSnakeCase(`${relationship.name}Id`) + const foreignKeyName = + relationship.options.foreignKeyName ?? + camelToSnakeCase(`${relationship.name}Id`) + const shouldRemoveRelated = !!cascades.delete?.includes(relationship.name) let mappedBy: string | undefined = camelToSnakeCase(MikroORMEntity.name) @@ -189,21 +193,83 @@ export function defineHasOneWithFKRelationship( mappedBy = relationship.mappedBy } - OneToOne({ + const oneToOneOptions = { entity: relatedModelName, + fieldName: foreignKeyName, ...(relationship.nullable ? { nullable: relationship.nullable } : {}), ...(mappedBy ? { mappedBy } : {}), - cascade: shouldRemoveRelated - ? (["persist", "soft-remove"] as any) - : undefined, - } as OneToOneOptions)(MikroORMEntity.prototype, relationship.name) + //orphanRemoval: true, + } as OneToOneOptions + + if (shouldRemoveRelated) { + oneToOneOptions.cascade = ["persist", "soft-remove"] as any + } + + OneToOne(oneToOneOptions)(MikroORMEntity.prototype, relationship.name) Property({ type: "string", columnType: "text", nullable: relationship.nullable, - persist: true, + persist: false, + formula(alias) { + return alias + "." + foreignKeyName + }, })(MikroORMEntity.prototype, foreignKeyName) + + const hookFactory = function ( + name: string, + type: "init" | "create" | "update", + hookFn: Function + ) { + MikroORMEntity.prototype[name] = function ( + this: typeof MikroORMEntity.prototype + ) { + if (type !== "update") { + // During creation + const relationMeta = this.__meta.relations.find( + (relation) => relation.name === relationship.name + ).targetMeta + this[relationship.name] ??= rel( + relationMeta.class, + this[foreignKeyName] + ) + this[foreignKeyName] ??= this[relationship.name]?.id + + return + } + + if (this[relationship.name]) { + this[foreignKeyName] = this[relationship.name].id + } + + if (this[relationship.name] === null) { + this[foreignKeyName] = null + } + + return + } + hookFn()(MikroORMEntity.prototype, name) + } + + /** + * Hook to handle foreign key assignation + */ + hookFactory( + `assignRelationFromForeignKeyValue${foreignKeyName}_init`, + "init", + OnInit + ) + hookFactory( + `assignRelationFromForeignKeyValue${foreignKeyName}_create`, + "create", + BeforeCreate + ) + hookFactory( + `assignRelationFromForeignKeyValue${foreignKeyName}_update`, + "update", + BeforeUpdate + ) } /** @@ -261,82 +327,108 @@ export function defineBelongsToRelationship( */ const shouldCascade = !!relationCascades.delete?.includes(mappedBy) - /** - * Ensure the mapped by is defined as relationship on the other side - */ - if (!otherSideRelation) { - throw new Error( - `Missing property "${mappedBy}" on "${relatedModelName}" entity. Make sure to define it as a relationship` - ) - } - function applyForeignKeyAssignationHooks(foreignKeyName: string) { - const hookName = `assignRelationFromForeignKeyValue${foreignKeyName}` - /** - * Hook to handle foreign key assignation - */ - MikroORMEntity.prototype[hookName] = function () { - /** - * In case of has one relation, in order to be able to have both ways - * to associate a relation (through the relation or the foreign key) we need to handle it - * specifically - */ - if ( - HasOne.isHasOne(otherSideRelation) || - HasOneWithForeignKey.isHasOneWithForeignKey(otherSideRelation) + const hookFactory = function ( + name: string, + type: "init" | "create" | "update", + hookFn: Function + ) { + MikroORMEntity.prototype[name] = function ( + this: typeof MikroORMEntity.prototype ) { - const relationMeta = this.__meta.relations.find( - (relation) => relation.name === relationship.name - ).targetMeta - this[relationship.name] ??= rel( - relationMeta.class, - this[foreignKeyName] - ) - this[relationship.name] ??= this[relationship.name]?.id - return - } + /** + * In case of has one relation, in order to be able to have both ways + * to associate a relation (through the relation or the foreign key) we need to handle it + * specifically + */ + if ( + HasOne.isHasOne(otherSideRelation) || + HasOneWithForeignKey.isHasOneWithForeignKey(otherSideRelation) + ) { + if (type !== "update") { + // During creation + const relationMeta = this.__meta.relations.find( + (relation) => relation.name === relationship.name + ).targetMeta + this[relationship.name] ??= rel( + relationMeta.class, + this[foreignKeyName] + ) + this[foreignKeyName] ??= this[relationship.name]?.id + + return + } - /** - * Do not override the existing foreign key value if - * exists - */ - if (this[foreignKeyName] !== undefined) { - return - } + if (this[relationship.name]) { + this[foreignKeyName] = this[relationship.name].id + } - /** - * Set the foreign key when the relationship is initialized - * as null - */ - if (this[relationship.name] === null) { - this[foreignKeyName] = null - return - } + if (this[relationship.name] === null) { + this[foreignKeyName] = null + } - /** - * Set the foreign key when the relationship is initialized - * and as the id - */ - if (this[relationship.name] && "id" in this[relationship.name]) { - this[foreignKeyName] = this[relationship.name].id + return + } + + /** + * Do not override the existing foreign key value if + * exists + */ + if (this[foreignKeyName] !== undefined) { + return + } + + /** + * Set the foreign key when the relationship is initialized + * as null + */ + if (this[relationship.name] === null) { + this[foreignKeyName] = null + return + } + + /** + * Set the foreign key when the relationship is initialized + * and as the id + */ + if (this[relationship.name] && "id" in this[relationship.name]) { + this[foreignKeyName] = this[relationship.name].id + } } + hookFn()(MikroORMEntity.prototype, name) } /** - * Execute hook via lifecycle decorators + * Hook to handle foreign key assignation */ - BeforeCreate()(MikroORMEntity.prototype, hookName) - OnInit()(MikroORMEntity.prototype, hookName) + hookFactory( + `assignRelationFromForeignKeyValue${foreignKeyName}_init`, + "init", + OnInit + ) + hookFactory( + `assignRelationFromForeignKeyValue${foreignKeyName}_create`, + "create", + BeforeCreate + ) + hookFactory( + `assignRelationFromForeignKeyValue${foreignKeyName}_update`, + "update", + BeforeUpdate + ) } /** * Otherside is a has many. Hence we should defined a ManyToOne */ if ( + !otherSideRelation || HasMany.isHasMany(otherSideRelation) || DmlManyToMany.isManyToMany(otherSideRelation) ) { - const foreignKeyName = camelToSnakeCase(`${relationship.name}Id`) + const foreignKeyName = + relationship.options.foreignKeyName ?? + camelToSnakeCase(`${relationship.name}Id`) const detachCascade = !!relationship.mappedBy && relationCascades.detach?.includes(relationship.mappedBy) @@ -391,20 +483,18 @@ export function defineBelongsToRelationship( HasOne.isHasOne(otherSideRelation) || HasOneWithForeignKey.isHasOneWithForeignKey(otherSideRelation) ) { - const foreignKeyName = camelToSnakeCase(`${relationship.name}Id`) - - Object.defineProperty(MikroORMEntity.prototype, foreignKeyName, { - value: null, - configurable: true, - enumerable: true, - writable: true, - }) + const foreignKeyName = + relationship.options.foreignKeyName ?? + camelToSnakeCase(`${relationship.name}Id`) Property({ columnType: "text", type: "string", nullable: relationship.nullable, persist: false, + formula(alias) { + return alias + "." + foreignKeyName + }, })(MikroORMEntity.prototype, foreignKeyName) const oneToOneOptions: Parameters[0] = { @@ -413,6 +503,7 @@ export function defineBelongsToRelationship( mappedBy: mappedBy, fieldName: foreignKeyName, owner: true, + // orphanRemoval: true, onDelete: shouldCascade ? "cascade" : undefined, } diff --git a/packages/core/utils/src/dml/relations/belongs-to.ts b/packages/core/utils/src/dml/relations/belongs-to.ts index cbd40fd0713d8..c868c6ec4de94 100644 --- a/packages/core/utils/src/dml/relations/belongs-to.ts +++ b/packages/core/utils/src/dml/relations/belongs-to.ts @@ -1,11 +1,15 @@ import { BaseRelationship } from "./base" import { RelationNullableModifier } from "./nullable" -export class BelongsTo extends BaseRelationship { +export class BelongsTo< + T, + const OptionalForeignKeyName extends string | undefined = undefined +> extends BaseRelationship { type = "belongsTo" as const declare $foreignKey: true + declare $foreignKeyName: OptionalForeignKeyName - static isBelongsTo(relationship: any): relationship is BelongsTo { + static isBelongsTo(relationship: any): relationship is BelongsTo { return relationship?.type === "belongsTo" } @@ -13,6 +17,10 @@ export class BelongsTo extends BaseRelationship { * Apply nullable modifier on the schema */ nullable() { - return new RelationNullableModifier, true>(this) + return new RelationNullableModifier< + T, + BelongsTo, + true + >(this) } } diff --git a/packages/core/utils/src/dml/relations/has-one-fk.ts b/packages/core/utils/src/dml/relations/has-one-fk.ts index a6123f59f3ad6..b6ec47aa48861 100644 --- a/packages/core/utils/src/dml/relations/has-one-fk.ts +++ b/packages/core/utils/src/dml/relations/has-one-fk.ts @@ -11,13 +11,17 @@ import { RelationNullableModifier } from "./nullable" * You may use the "BelongsTo" relationship to define the inverse * of the "HasOne" relationship */ -export class HasOneWithForeignKey extends BaseRelationship { +export class HasOneWithForeignKey< + T, + const OptionalForeignKeyName extends string | undefined = undefined +> extends BaseRelationship { type = "hasOneWithFK" as const declare $foreignKey: true + declare $foreignKeyName: OptionalForeignKeyName static isHasOneWithForeignKey( relationship: any - ): relationship is HasOneWithForeignKey { + ): relationship is HasOneWithForeignKey { return relationship?.type === "hasOneWithFK" } @@ -25,6 +29,10 @@ export class HasOneWithForeignKey extends BaseRelationship { * Apply nullable modifier on the schema */ nullable() { - return new RelationNullableModifier, true>(this) + return new RelationNullableModifier< + T, + HasOneWithForeignKey, + true + >(this) } } diff --git a/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts b/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts index dcdfe6c00efb7..8c78424a2a15c 100644 --- a/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts +++ b/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts @@ -613,10 +613,15 @@ describe("joiner-config-builder", () => { } ) - const linkConfig = buildLinkConfigFromModelObjects("myService", { - user, - car, - }) + const linkableKeysFromDml = buildLinkableKeysFromDmlObjects([user, car]) + const linkConfig = buildLinkConfigFromModelObjects( + "myService", + { + user, + car, + }, + linkableKeysFromDml + ) expectTypeOf(linkConfig).toMatchTypeOf<{ user: { diff --git a/packages/core/utils/src/modules-sdk/joiner-config-builder.ts b/packages/core/utils/src/modules-sdk/joiner-config-builder.ts index b31278a88b5ae..c71a28403decf 100644 --- a/packages/core/utils/src/modules-sdk/joiner-config-builder.ts +++ b/packages/core/utils/src/modules-sdk/joiner-config-builder.ts @@ -413,41 +413,41 @@ export function buildLinkConfigFromModelObjects< } } } + } - /** - * If the joiner config specify some custom linkable keys, we merge them with the - * existing linkable keys infered from the model above. - */ - const linkableKeysPerModel = Object.entries(linkableKeys).reduce( - (acc, [key, entityName]) => { - acc[entityName] ??= [] - acc[entityName].push(key) - return acc - }, - {} - ) + /** + * If the joiner config specify some custom linkable keys, we merge them with the + * existing linkable keys infered from the model above. + */ + for (const [linkableKey, modelName] of Object.entries(linkableKeys) ?? []) { + const snakeCasedModelName = camelToSnakeCase(toCamelCase(modelName)) - for (const linkableKey of linkableKeysPerModel[classLikeModelName] ?? []) { - const snakeCasedModelName = camelToSnakeCase(toCamelCase(model.name)) + // Linkable keys by default are prepared with snake cased model name _id + // So to be able to compare only the property we have to remove the first part + const inferredReferenceProperty = linkableKey.replace( + `${snakeCasedModelName}_`, + "" + ) - // Linkable keys by default are prepared with snake cased model name _id - // So to be able to compare only the property we have to remove the first part - const inferredReferenceProperty = linkableKey.replace( - `${snakeCasedModelName}_`, - "" - ) + linkConfig[lowerCaseFirst(modelName)] ??= { + toJSON: function () { + const linkables = Object.entries(this) + .filter(([name]) => name !== "toJSON") + .map(([, object]) => object) + return linkables[0] + }, + } - if (modelLinkConfig[inferredReferenceProperty]) { - continue - } + if (linkConfig[lowerCaseFirst(modelName)][inferredReferenceProperty]) { + continue + } - modelLinkConfig[linkableKey] = { - linkable: linkableKey, - primaryKey: linkableKey, - serviceName, - field: lowerCaseFirst(model.name), - entity: upperCaseFirst(model.name), - } + linkConfig[lowerCaseFirst(modelName)][inferredReferenceProperty] = { + linkable: linkableKey, + primaryKey: inferredReferenceProperty, + serviceName, + field: lowerCaseFirst(modelName), + entity: upperCaseFirst(modelName), } } diff --git a/packages/medusa/src/api/store/carts/query-config.ts b/packages/medusa/src/api/store/carts/query-config.ts index d66d20e424e36..8c18db2889134 100644 --- a/packages/medusa/src/api/store/carts/query-config.ts +++ b/packages/medusa/src/api/store/carts/query-config.ts @@ -88,6 +88,7 @@ export const defaultStoreCartFields = [ "shipping_methods.adjustments.code", "shipping_methods.adjustments.amount", "shipping_methods.shipping_option_id", + "shipping_address_id", "shipping_address.id", "shipping_address.first_name", "shipping_address.last_name", @@ -100,6 +101,7 @@ export const defaultStoreCartFields = [ "shipping_address.region_code", "shipping_address.province", "shipping_address.phone", + "billing_address_id", "billing_address.id", "billing_address.first_name", "billing_address.last_name", diff --git a/packages/modules/fulfillment/integration-tests/__tests__/fulfillment-module-service/index.spec.ts b/packages/modules/fulfillment/integration-tests/__tests__/fulfillment-module-service/index.spec.ts index e6600f4e1ab18..29f1444f54e77 100644 --- a/packages/modules/fulfillment/integration-tests/__tests__/fulfillment-module-service/index.spec.ts +++ b/packages/modules/fulfillment/integration-tests/__tests__/fulfillment-module-service/index.spec.ts @@ -42,6 +42,7 @@ async function list( "service_zones.shipping_options.provider", "service_zones.shipping_options.type", "service_zones.shipping_options.rules", + "service_zones.shipping_options.shipping_profile", "service_zones.shipping_options.fulfillments.labels", "service_zones.shipping_options.fulfillments.items", "service_zones.shipping_options.fulfillments.delivery_address", @@ -112,20 +113,23 @@ moduleIntegrationTestRunner({ service: FulfillmentModuleService, }).linkable - expect(Object.keys(linkable)).toEqual([ - "fulfillmentAddress", - "fulfillmentItem", - "fulfillmentLabel", - "fulfillmentProvider", - "fulfillmentSet", - "fulfillment", - "geoZone", - "serviceZone", - "shippingOptionRule", - "shippingOptionType", - "shippingOption", - "shippingProfile", - ]) + expect(Object.keys(linkable)).toHaveLength(12) + expect(Object.keys(linkable)).toEqual( + expect.arrayContaining([ + "fulfillmentAddress", + "fulfillmentItem", + "fulfillmentLabel", + "fulfillmentProvider", + "fulfillmentSet", + "fulfillment", + "geoZone", + "serviceZone", + "shippingOptionRule", + "shippingOptionType", + "shippingOption", + "shippingProfile", + ]) + ) Object.keys(linkable).forEach((key) => { delete linkable[key].toJSON diff --git a/packages/modules/fulfillment/integration-tests/__tests__/fulfillment-module-service/shipping-option.spec.ts b/packages/modules/fulfillment/integration-tests/__tests__/fulfillment-module-service/shipping-option.spec.ts index 109c23d606148..c73595bbb9030 100644 --- a/packages/modules/fulfillment/integration-tests/__tests__/fulfillment-module-service/shipping-option.spec.ts +++ b/packages/modules/fulfillment/integration-tests/__tests__/fulfillment-module-service/shipping-option.spec.ts @@ -20,7 +20,7 @@ import { } from "../../__fixtures__" import { FulfillmentProviderServiceFixtures } from "../../__fixtures__/providers" -jest.setTimeout(100000) +jest.setTimeout(1000000) const moduleOptions = { providers: [ @@ -709,7 +709,18 @@ moduleIntegrationTestRunner({ const existingRule = shippingOption.rules[0]! - const updateData: UpdateShippingOptionDTO = { + const updateData: UpdateShippingOptionDTO & { + type: { + code: string + description: string + label: string + } + rules: { + attribute: string + operator: string + value: string + }[] + } = { id: shippingOption.id, name: "updated-test", price_type: "calculated", @@ -767,9 +778,9 @@ moduleIntegrationTestRunner({ }), expect.objectContaining({ id: expect.any(String), - attribute: updateData.rules[1].attribute, - operator: updateData.rules[1].operator, - value: updateData.rules[1].value, + attribute: updateData.rules![1].attribute, + operator: updateData.rules![1].operator, + value: updateData.rules![1].value, }), ]), }) @@ -789,13 +800,15 @@ moduleIntegrationTestRunner({ ) const types = await service.listShippingOptionTypes() - expect(types).toHaveLength(1) - expect(types[0]).toEqual( - expect.objectContaining({ - code: updateData.type.code, - description: updateData.type.description, - label: updateData.type.label, - }) + expect(types).toHaveLength(2) + expect(types).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + code: updateData.type.code, + description: updateData.type.description, + label: updateData.type.label, + }), + ]) ) expect(eventBusEmitSpy.mock.calls[0][0]).toHaveLength(5) @@ -1059,7 +1072,7 @@ moduleIntegrationTestRunner({ ) const types = await service.listShippingOptionTypes() - expect(types).toHaveLength(2) + expect(types).toHaveLength(4) expect(types).toEqual( expect.arrayContaining([ expect.objectContaining({ @@ -1090,7 +1103,7 @@ moduleIntegrationTestRunner({ type: "default", }) - const shippingOptionData = { + const shippingOptionData: UpdateShippingOptionDTO = { id: "sp_jdafwfleiwuonl", name: "test", price_type: "flat", diff --git a/packages/modules/fulfillment/src/migrations/.snapshot-medusa-fulfillment.json b/packages/modules/fulfillment/src/migrations/.snapshot-medusa-fulfillment.json index aaffec751b5f5..029dbf91db475 100644 --- a/packages/modules/fulfillment/src/migrations/.snapshot-medusa-fulfillment.json +++ b/packages/modules/fulfillment/src/migrations/.snapshot-medusa-fulfillment.json @@ -1,5 +1,7 @@ { - "namespaces": ["public"], + "namespaces": [ + "public" + ], "name": "public", "tables": [ { @@ -150,15 +152,17 @@ "indexes": [ { "keyName": "IDX_fulfillment_address_deleted_at", - "columnNames": ["deleted_at"], + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_address_deleted_at\" ON \"fulfillment_address\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_address_deleted_at\" ON \"fulfillment_address\" (deleted_at) WHERE deleted_at IS NULL" }, { "keyName": "fulfillment_address_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -187,14 +191,56 @@ "nullable": false, "default": "true", "mappedType": "boolean" + }, + "created_at": { + "name": "created_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "default": "now()", + "mappedType": "datetime" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "length": 6, + "default": "now()", + "mappedType": "datetime" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamptz", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "length": 6, + "mappedType": "datetime" } }, "name": "fulfillment_provider", "schema": "public", "indexes": [ + { + "keyName": "IDX_fulfillment_provider_deleted_at", + "columnNames": [], + "composite": false, + "primary": false, + "unique": false, + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_provider_deleted_at\" ON \"fulfillment_provider\" (deleted_at) WHERE deleted_at IS NULL" + }, { "keyName": "fulfillment_provider_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -278,24 +324,26 @@ "schema": "public", "indexes": [ { - "keyName": "IDX_fulfillment_set_name_unique", - "columnNames": ["name"], + "keyName": "IDX_fulfillment_set_deleted_at", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_fulfillment_set_name_unique\" ON \"fulfillment_set\" (name) WHERE deleted_at IS NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_set_deleted_at\" ON \"fulfillment_set\" (deleted_at) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_fulfillment_set_deleted_at", - "columnNames": ["deleted_at"], + "keyName": "IDX_fulfillment_set_name_unique", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_set_deleted_at\" ON \"fulfillment_set\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_fulfillment_set_name_unique\" ON \"fulfillment_set\" (name) WHERE deleted_at IS NULL" }, { "keyName": "fulfillment_set_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -324,15 +372,6 @@ "nullable": false, "mappedType": "text" }, - "metadata": { - "name": "metadata", - "type": "jsonb", - "unsigned": false, - "autoincrement": false, - "primary": false, - "nullable": true, - "mappedType": "json" - }, "fulfillment_set_id": { "name": "fulfillment_set_id", "type": "text", @@ -342,6 +381,15 @@ "nullable": false, "mappedType": "text" }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "json" + }, "created_at": { "name": "created_at", "type": "timestamptz", @@ -379,32 +427,34 @@ "schema": "public", "indexes": [ { - "keyName": "IDX_service_zone_name_unique", - "columnNames": ["name"], + "keyName": "IDX_service_zone_fulfillment_set_id", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_service_zone_name_unique\" ON \"service_zone\" (name) WHERE deleted_at IS NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_service_zone_fulfillment_set_id\" ON \"service_zone\" (fulfillment_set_id) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_service_zone_fulfillment_set_id", - "columnNames": ["fulfillment_set_id"], + "keyName": "IDX_service_zone_deleted_at", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_service_zone_fulfillment_set_id\" ON \"service_zone\" (fulfillment_set_id) WHERE deleted_at IS NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_service_zone_deleted_at\" ON \"service_zone\" (deleted_at) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_service_zone_deleted_at", - "columnNames": ["deleted_at"], + "keyName": "IDX_service_zone_name_unique", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_service_zone_deleted_at\" ON \"service_zone\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_service_zone_name_unique\" ON \"service_zone\" (name) WHERE deleted_at IS NULL" }, { "keyName": "service_zone_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -414,9 +464,13 @@ "foreignKeys": { "service_zone_fulfillment_set_id_foreign": { "constraintName": "service_zone_fulfillment_set_id_foreign", - "columnNames": ["fulfillment_set_id"], + "columnNames": [ + "fulfillment_set_id" + ], "localTableName": "public.service_zone", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.fulfillment_set", "deleteRule": "cascade", "updateRule": "cascade" @@ -442,7 +496,12 @@ "primary": false, "nullable": false, "default": "'country'", - "enumItems": ["country", "province", "city", "zip"], + "enumItems": [ + "country", + "province", + "city", + "zip" + ], "mappedType": "enum" }, "country_code": { @@ -472,15 +531,6 @@ "nullable": true, "mappedType": "text" }, - "service_zone_id": { - "name": "service_zone_id", - "type": "text", - "unsigned": false, - "autoincrement": false, - "primary": false, - "nullable": false, - "mappedType": "text" - }, "postal_expression": { "name": "postal_expression", "type": "jsonb", @@ -490,6 +540,15 @@ "nullable": true, "mappedType": "json" }, + "service_zone_id": { + "name": "service_zone_id", + "type": "text", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "text" + }, "metadata": { "name": "metadata", "type": "jsonb", @@ -536,48 +595,50 @@ "schema": "public", "indexes": [ { - "keyName": "IDX_geo_zone_country_code", - "columnNames": ["country_code"], + "keyName": "IDX_geo_zone_service_zone_id", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_geo_zone_country_code\" ON \"geo_zone\" (country_code) WHERE deleted_at IS NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_geo_zone_service_zone_id\" ON \"geo_zone\" (service_zone_id) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_geo_zone_province_code", - "columnNames": ["province_code"], + "keyName": "IDX_geo_zone_deleted_at", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "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" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_geo_zone_deleted_at\" ON \"geo_zone\" (deleted_at) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_geo_zone_city", - "columnNames": ["city"], + "keyName": "IDX_geo_zone_country_code", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_geo_zone_city\" ON \"geo_zone\" (city) WHERE deleted_at IS NULL AND city IS NOT NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_geo_zone_country_code\" ON \"geo_zone\" (country_code) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_geo_zone_service_zone_id", - "columnNames": ["service_zone_id"], + "keyName": "IDX_geo_zone_province_code", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_geo_zone_service_zone_id\" ON \"geo_zone\" (service_zone_id) WHERE deleted_at IS NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_geo_zone_province_code\" ON \"geo_zone\" (province_code) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_geo_zone_deleted_at", - "columnNames": ["deleted_at"], + "keyName": "IDX_geo_zone_city", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_geo_zone_deleted_at\" ON \"geo_zone\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_geo_zone_city\" ON \"geo_zone\" (city) WHERE deleted_at IS NULL" }, { "keyName": "geo_zone_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -587,9 +648,13 @@ "foreignKeys": { "geo_zone_service_zone_id_foreign": { "constraintName": "geo_zone_service_zone_id_foreign", - "columnNames": ["service_zone_id"], + "columnNames": [ + "service_zone_id" + ], "localTableName": "public.geo_zone", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.service_zone", "deleteRule": "cascade", "updateRule": "cascade" @@ -672,15 +737,17 @@ "indexes": [ { "keyName": "IDX_shipping_option_type_deleted_at", - "columnNames": ["deleted_at"], + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_type_deleted_at\" ON \"shipping_option_type\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_type_deleted_at\" ON \"shipping_option_type\" (deleted_at) WHERE deleted_at IS NULL" }, { "keyName": "shipping_option_type_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -764,24 +831,26 @@ "schema": "public", "indexes": [ { - "keyName": "IDX_shipping_profile_name_unique", - "columnNames": ["name"], + "keyName": "IDX_shipping_profile_deleted_at", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_shipping_profile_name_unique\" ON \"shipping_profile\" (name) WHERE deleted_at IS NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_profile_deleted_at\" ON \"shipping_profile\" (deleted_at) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_shipping_profile_deleted_at", - "columnNames": ["deleted_at"], + "keyName": "IDX_shipping_profile_name_unique", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_profile_deleted_at\" ON \"shipping_profile\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE UNIQUE INDEX IF NOT EXISTS \"IDX_shipping_profile_name_unique\" ON \"shipping_profile\" (name) WHERE deleted_at IS NULL" }, { "keyName": "shipping_profile_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -818,9 +887,30 @@ "primary": false, "nullable": false, "default": "'flat'", - "enumItems": ["calculated", "flat"], + "enumItems": [ + "calculated", + "flat" + ], "mappedType": "enum" }, + "data": { + "name": "data", + "type": "jsonb", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "json" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": true, + "mappedType": "json" + }, "service_zone_id": { "name": "service_zone_id", "type": "text", @@ -848,24 +938,6 @@ "nullable": true, "mappedType": "text" }, - "data": { - "name": "data", - "type": "jsonb", - "unsigned": false, - "autoincrement": false, - "primary": false, - "nullable": true, - "mappedType": "json" - }, - "metadata": { - "name": "metadata", - "type": "jsonb", - "unsigned": false, - "autoincrement": false, - "primary": false, - "nullable": true, - "mappedType": "json" - }, "shipping_option_type_id": { "name": "shipping_option_type_id", "type": "text", @@ -911,16 +983,9 @@ "name": "shipping_option", "schema": "public", "indexes": [ - { - "columnNames": ["shipping_option_type_id"], - "composite": false, - "keyName": "shipping_option_shipping_option_type_id_unique", - "primary": false, - "unique": true - }, { "keyName": "IDX_shipping_option_service_zone_id", - "columnNames": ["service_zone_id"], + "columnNames": [], "composite": false, "primary": false, "unique": false, @@ -928,39 +993,25 @@ }, { "keyName": "IDX_shipping_option_shipping_profile_id", - "columnNames": ["shipping_profile_id"], + "columnNames": [], "composite": false, "primary": false, "unique": false, "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_shipping_profile_id\" ON \"shipping_option\" (shipping_profile_id) WHERE deleted_at IS NULL" }, - { - "keyName": "IDX_shipping_option_provider_id", - "columnNames": ["provider_id"], - "composite": false, - "primary": false, - "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_provider_id\" ON \"shipping_option\" (provider_id) WHERE deleted_at IS NULL" - }, - { - "keyName": "IDX_shipping_option_shipping_option_type_id", - "columnNames": ["shipping_option_type_id"], - "composite": false, - "primary": false, - "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_shipping_option_type_id\" ON \"shipping_option\" (shipping_option_type_id) WHERE deleted_at IS NULL" - }, { "keyName": "IDX_shipping_option_deleted_at", - "columnNames": ["deleted_at"], + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_deleted_at\" ON \"shipping_option\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_deleted_at\" ON \"shipping_option\" (deleted_at) WHERE deleted_at IS NULL" }, { "keyName": "shipping_option_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -970,39 +1021,29 @@ "foreignKeys": { "shipping_option_service_zone_id_foreign": { "constraintName": "shipping_option_service_zone_id_foreign", - "columnNames": ["service_zone_id"], + "columnNames": [ + "service_zone_id" + ], "localTableName": "public.shipping_option", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.service_zone", "deleteRule": "cascade", "updateRule": "cascade" }, "shipping_option_shipping_profile_id_foreign": { "constraintName": "shipping_option_shipping_profile_id_foreign", - "columnNames": ["shipping_profile_id"], + "columnNames": [ + "shipping_profile_id" + ], "localTableName": "public.shipping_option", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.shipping_profile", "deleteRule": "set null", "updateRule": "cascade" - }, - "shipping_option_provider_id_foreign": { - "constraintName": "shipping_option_provider_id_foreign", - "columnNames": ["provider_id"], - "localTableName": "public.shipping_option", - "referencedColumnNames": ["id"], - "referencedTableName": "public.fulfillment_provider", - "deleteRule": "set null", - "updateRule": "cascade" - }, - "shipping_option_shipping_option_type_id_foreign": { - "constraintName": "shipping_option_shipping_option_type_id_foreign", - "columnNames": ["shipping_option_type_id"], - "localTableName": "public.shipping_option", - "referencedColumnNames": ["id"], - "referencedTableName": "public.shipping_option_type", - "deleteRule": "cascade", - "updateRule": "cascade" } } }, @@ -1033,7 +1074,16 @@ "autoincrement": false, "primary": false, "nullable": false, - "enumItems": ["in", "eq", "ne", "gt", "gte", "lt", "lte", "nin"], + "enumItems": [ + "in", + "eq", + "ne", + "gt", + "gte", + "lt", + "lte", + "nin" + ], "mappedType": "enum" }, "value": { @@ -1092,7 +1142,7 @@ "indexes": [ { "keyName": "IDX_shipping_option_rule_shipping_option_id", - "columnNames": ["shipping_option_id"], + "columnNames": [], "composite": false, "primary": false, "unique": false, @@ -1100,15 +1150,17 @@ }, { "keyName": "IDX_shipping_option_rule_deleted_at", - "columnNames": ["deleted_at"], + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_rule_deleted_at\" ON \"shipping_option_rule\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_shipping_option_rule_deleted_at\" ON \"shipping_option_rule\" (deleted_at) WHERE deleted_at IS NULL" }, { "keyName": "shipping_option_rule_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -1118,9 +1170,13 @@ "foreignKeys": { "shipping_option_rule_shipping_option_id_foreign": { "constraintName": "shipping_option_rule_shipping_option_id_foreign", - "columnNames": ["shipping_option_id"], + "columnNames": [ + "shipping_option_id" + ], "localTableName": "public.shipping_option_rule", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.shipping_option", "deleteRule": "cascade", "updateRule": "cascade" @@ -1214,6 +1270,16 @@ "nullable": true, "mappedType": "json" }, + "requires_shipping": { + "name": "requires_shipping", + "type": "boolean", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "default": "true", + "mappedType": "boolean" + }, "provider_id": { "name": "provider_id", "type": "text", @@ -1232,15 +1298,6 @@ "nullable": true, "mappedType": "text" }, - "metadata": { - "name": "metadata", - "type": "jsonb", - "unsigned": false, - "autoincrement": false, - "primary": false, - "nullable": true, - "mappedType": "json" - }, "delivery_address_id": { "name": "delivery_address_id", "type": "text", @@ -1250,15 +1307,14 @@ "nullable": true, "mappedType": "text" }, - "requires_shipping": { - "name": "requires_shipping", - "type": "boolean", + "metadata": { + "name": "metadata", + "type": "jsonb", "unsigned": false, "autoincrement": false, "primary": false, - "nullable": false, - "default": "true", - "mappedType": "boolean" + "nullable": true, + "mappedType": "json" }, "created_at": { "name": "created_at", @@ -1297,47 +1353,34 @@ "schema": "public", "indexes": [ { - "columnNames": ["delivery_address_id"], - "composite": false, - "keyName": "fulfillment_delivery_address_id_unique", - "primary": false, - "unique": true - }, - { - "keyName": "IDX_fulfillment_location_id", - "columnNames": ["location_id"], - "composite": false, - "primary": false, - "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_location_id\" ON \"fulfillment\" (location_id) WHERE deleted_at IS NULL" - }, - { - "keyName": "IDX_fulfillment_provider_id", - "columnNames": ["provider_id"], + "keyName": "IDX_fulfillment_shipping_option_id", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_provider_id\" ON \"fulfillment\" (provider_id) WHERE deleted_at IS NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_shipping_option_id\" ON \"fulfillment\" (shipping_option_id) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_fulfillment_shipping_option_id", - "columnNames": ["shipping_option_id"], + "keyName": "IDX_fulfillment_deleted_at", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_shipping_option_id\" ON \"fulfillment\" (shipping_option_id) WHERE deleted_at IS NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_deleted_at\" ON \"fulfillment\" (deleted_at) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_fulfillment_deleted_at", - "columnNames": ["deleted_at"], + "keyName": "IDX_fulfillment_location_id", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_deleted_at\" ON \"fulfillment\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_location_id\" ON \"fulfillment\" (location_id) WHERE deleted_at IS NULL" }, { "keyName": "fulfillment_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -1345,32 +1388,18 @@ ], "checks": [], "foreignKeys": { - "fulfillment_provider_id_foreign": { - "constraintName": "fulfillment_provider_id_foreign", - "columnNames": ["provider_id"], - "localTableName": "public.fulfillment", - "referencedColumnNames": ["id"], - "referencedTableName": "public.fulfillment_provider", - "deleteRule": "set null", - "updateRule": "cascade" - }, "fulfillment_shipping_option_id_foreign": { "constraintName": "fulfillment_shipping_option_id_foreign", - "columnNames": ["shipping_option_id"], + "columnNames": [ + "shipping_option_id" + ], "localTableName": "public.fulfillment", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.shipping_option", "deleteRule": "set null", "updateRule": "cascade" - }, - "fulfillment_delivery_address_id_foreign": { - "constraintName": "fulfillment_delivery_address_id_foreign", - "columnNames": ["delivery_address_id"], - "localTableName": "public.fulfillment", - "referencedColumnNames": ["id"], - "referencedTableName": "public.fulfillment_address", - "deleteRule": "cascade", - "updateRule": "cascade" } } }, @@ -1459,7 +1488,7 @@ "indexes": [ { "keyName": "IDX_fulfillment_label_fulfillment_id", - "columnNames": ["fulfillment_id"], + "columnNames": [], "composite": false, "primary": false, "unique": false, @@ -1467,15 +1496,17 @@ }, { "keyName": "IDX_fulfillment_label_deleted_at", - "columnNames": ["deleted_at"], + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_label_deleted_at\" ON \"fulfillment_label\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_label_deleted_at\" ON \"fulfillment_label\" (deleted_at) WHERE deleted_at IS NULL" }, { "keyName": "fulfillment_label_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -1485,9 +1516,13 @@ "foreignKeys": { "fulfillment_label_fulfillment_id_foreign": { "constraintName": "fulfillment_label_fulfillment_id_foreign", - "columnNames": ["fulfillment_id"], + "columnNames": [ + "fulfillment_id" + ], "localTableName": "public.fulfillment_label", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.fulfillment", "deleteRule": "cascade", "updateRule": "cascade" @@ -1541,15 +1576,6 @@ "nullable": false, "mappedType": "decimal" }, - "raw_quantity": { - "name": "raw_quantity", - "type": "jsonb", - "unsigned": false, - "autoincrement": false, - "primary": false, - "nullable": false, - "mappedType": "json" - }, "line_item_id": { "name": "line_item_id", "type": "text", @@ -1577,6 +1603,15 @@ "nullable": false, "mappedType": "text" }, + "raw_quantity": { + "name": "raw_quantity", + "type": "jsonb", + "unsigned": false, + "autoincrement": false, + "primary": false, + "nullable": false, + "mappedType": "json" + }, "created_at": { "name": "created_at", "type": "timestamptz", @@ -1614,40 +1649,42 @@ "schema": "public", "indexes": [ { - "keyName": "IDX_fulfillment_item_line_item_id", - "columnNames": ["line_item_id"], + "keyName": "IDX_fulfillment_item_fulfillment_id", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_item_line_item_id\" ON \"fulfillment_item\" (line_item_id) WHERE deleted_at IS NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_item_fulfillment_id\" ON \"fulfillment_item\" (fulfillment_id) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_fulfillment_item_inventory_item_id", - "columnNames": ["inventory_item_id"], + "keyName": "IDX_fulfillment_item_deleted_at", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_item_inventory_item_id\" ON \"fulfillment_item\" (inventory_item_id) WHERE deleted_at IS NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_item_deleted_at\" ON \"fulfillment_item\" (deleted_at) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_fulfillment_item_fulfillment_id", - "columnNames": ["fulfillment_id"], + "keyName": "IDX_fulfillment_item_inventory_item_id", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_item_fulfillment_id\" ON \"fulfillment_item\" (fulfillment_id) WHERE deleted_at IS NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_item_inventory_item_id\" ON \"fulfillment_item\" (inventory_item_id) WHERE deleted_at IS NULL" }, { - "keyName": "IDX_fulfillment_item_deleted_at", - "columnNames": ["deleted_at"], + "keyName": "IDX_fulfillment_item_line_item_id", + "columnNames": [], "composite": false, "primary": false, "unique": false, - "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_item_deleted_at\" ON \"fulfillment_item\" (deleted_at) WHERE deleted_at IS NOT NULL" + "expression": "CREATE INDEX IF NOT EXISTS \"IDX_fulfillment_item_line_item_id\" ON \"fulfillment_item\" (line_item_id) WHERE deleted_at IS NULL" }, { "keyName": "fulfillment_item_pkey", - "columnNames": ["id"], + "columnNames": [ + "id" + ], "composite": false, "primary": true, "unique": true @@ -1657,9 +1694,13 @@ "foreignKeys": { "fulfillment_item_fulfillment_id_foreign": { "constraintName": "fulfillment_item_fulfillment_id_foreign", - "columnNames": ["fulfillment_id"], + "columnNames": [ + "fulfillment_id" + ], "localTableName": "public.fulfillment_item", - "referencedColumnNames": ["id"], + "referencedColumnNames": [ + "id" + ], "referencedTableName": "public.fulfillment", "deleteRule": "cascade", "updateRule": "cascade" diff --git a/packages/modules/fulfillment/src/migrations/Migration20241217110416.ts b/packages/modules/fulfillment/src/migrations/Migration20241217110416.ts new file mode 100644 index 0000000000000..52a188497957f --- /dev/null +++ b/packages/modules/fulfillment/src/migrations/Migration20241217110416.ts @@ -0,0 +1,81 @@ +import { Migration } from "@mikro-orm/migrations" + +export class Migration20241217110416 extends Migration { + async up(): Promise { + this.addSql( + 'alter table if exists "shipping_option" drop constraint if exists "shipping_option_provider_id_foreign";' + ) + this.addSql( + 'alter table if exists "shipping_option" drop constraint if exists "shipping_option_shipping_option_type_id_foreign";' + ) + + this.addSql( + 'alter table if exists "fulfillment" drop constraint if exists "fulfillment_provider_id_foreign";' + ) + this.addSql( + 'alter table if exists "fulfillment" drop constraint if exists "fulfillment_delivery_address_id_foreign";' + ) + + this.addSql( + 'alter table if exists "fulfillment_provider" add column if not exists "created_at" timestamptz not null default now(), add column if not exists "updated_at" timestamptz not null default now(), add column if not exists "deleted_at" timestamptz null;' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_fulfillment_provider_deleted_at" ON "fulfillment_provider" (deleted_at) WHERE deleted_at IS NULL;' + ) + + this.addSql( + 'alter table if exists "shipping_option" drop constraint if exists "shipping_option_shipping_option_type_id_unique";' + ) + this.addSql('drop index if exists "IDX_shipping_option_provider_id";') + this.addSql( + 'drop index if exists "IDX_shipping_option_shipping_option_type_id";' + ) + + this.addSql( + 'alter table if exists "fulfillment" drop constraint if exists "fulfillment_delivery_address_id_unique";' + ) + this.addSql('drop index if exists "IDX_fulfillment_provider_id";') + } + + async down(): Promise { + this.addSql('drop index if exists "IDX_fulfillment_provider_deleted_at";') + this.addSql( + 'alter table if exists "fulfillment_provider" drop column if exists "created_at";' + ) + this.addSql( + 'alter table if exists "fulfillment_provider" drop column if exists "updated_at";' + ) + this.addSql( + 'alter table if exists "fulfillment_provider" drop column if exists "deleted_at";' + ) + + this.addSql( + 'alter table if exists "shipping_option" add constraint "shipping_option_provider_id_foreign" foreign key ("provider_id") references "fulfillment_provider" ("id") on update cascade on delete set null;' + ) + this.addSql( + 'alter table if exists "shipping_option" add constraint "shipping_option_shipping_option_type_id_foreign" foreign key ("shipping_option_type_id") references "shipping_option_type" ("id") on update cascade on delete cascade;' + ) + this.addSql( + 'alter table if exists "shipping_option" add constraint "shipping_option_shipping_option_type_id_unique" unique ("shipping_option_type_id");' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_shipping_option_provider_id" ON "shipping_option" (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( + 'alter table if exists "fulfillment" add constraint "fulfillment_provider_id_foreign" foreign key ("provider_id") references "fulfillment_provider" ("id") on update cascade on delete set null;' + ) + 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 on delete cascade;' + ) + this.addSql( + 'alter table if exists "fulfillment" add constraint "fulfillment_delivery_address_id_unique" unique ("delivery_address_id");' + ) + this.addSql( + 'CREATE INDEX IF NOT EXISTS "IDX_fulfillment_provider_id" ON "fulfillment" (provider_id) WHERE deleted_at IS NULL;' + ) + } +} diff --git a/packages/modules/fulfillment/src/models/address.ts b/packages/modules/fulfillment/src/models/address.ts index 781f2a06c19c0..d1335ba97dfbd 100644 --- a/packages/modules/fulfillment/src/models/address.ts +++ b/packages/modules/fulfillment/src/models/address.ts @@ -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" - -type OptionalAddressProps = DAL.SoftDeletableModelDateColumns - -const FulfillmentDeletedAtIndex = createPsqlIndexStatementHelper({ - tableName: "fulfillment_address", - columns: "deleted_at", - where: "deleted_at IS NOT NULL", +import { model } from "@medusajs/framework/utils" + +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 | 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") - } -} diff --git a/packages/modules/fulfillment/src/models/fulfillment-item.ts b/packages/modules/fulfillment/src/models/fulfillment-item.ts index af55445f15076..22bc48b817ecf 100644 --- a/packages/modules/fulfillment/src/models/fulfillment-item.ts +++ b/packages/modules/fulfillment/src/models/fulfillment-item.ts @@ -1,122 +1,27 @@ -import { - BigNumber, - createPsqlIndexStatementHelper, - DALUtils, - generateEntityId, - MikroOrmBigNumberProperty, -} 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" - -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", - }) - @FulfillmentIdIndex.MikroORMIndex() - fulfillment_id: string - - @ManyToOne(() => Fulfillment, { persist: false }) - fulfillment: Rel - - @Property({ - onCreate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", - }) - created_at: Date - - @Property({ - onCreate: () => new Date(), - onUpdate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", +import { model } from "@medusajs/framework/utils" + +import { Fulfillment } from "./fulfillment" + +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", + }), }) - 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", + }, + ]) diff --git a/packages/modules/fulfillment/src/models/fulfillment-label.ts b/packages/modules/fulfillment/src/models/fulfillment-label.ts index 27b8164d62ea8..bebf72dfd52ef 100644 --- a/packages/modules/fulfillment/src/models/fulfillment-label.ts +++ b/packages/modules/fulfillment/src/models/fulfillment-label.ts @@ -1,94 +1,13 @@ -import { - createPsqlIndexStatementHelper, - DALUtils, - generateEntityId, -} 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" - -type FulfillmentLabelOptionalProps = DAL.SoftDeletableModelDateColumns - -const FulfillmentIdIndex = createPsqlIndexStatementHelper({ - tableName: "fulfillment_label", - columns: "fulfillment_id", - where: "deleted_at IS NULL", -}) - -const DeletedAtIndex = createPsqlIndexStatementHelper({ - tableName: "fulfillment_label", - columns: "deleted_at", - where: "deleted_at IS NOT NULL", +import { model } from "@medusajs/framework/utils" + +import { Fulfillment } from "./fulfillment" + +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", + }), }) - -@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 - - @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 - } -} diff --git a/packages/modules/fulfillment/src/models/fulfillment-provider.ts b/packages/modules/fulfillment/src/models/fulfillment-provider.ts index b12a485f03fe3..c73a577ac25a9 100644 --- a/packages/modules/fulfillment/src/models/fulfillment-provider.ts +++ b/packages/modules/fulfillment/src/models/fulfillment-provider.ts @@ -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), +}) diff --git a/packages/modules/fulfillment/src/models/fulfillment-set.ts b/packages/modules/fulfillment/src/models/fulfillment-set.ts index 30193687f518c..03765c22e9268 100644 --- a/packages/modules/fulfillment/src/models/fulfillment-set.ts +++ b/packages/modules/fulfillment/src/models/fulfillment-set.ts @@ -1,90 +1,24 @@ -import { - createPsqlIndexStatementHelper, - DALUtils, - generateEntityId, -} 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" - -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 | null = null - - @OneToMany(() => ServiceZone, "fulfillment_set", { - cascade: [Cascade.PERSIST, "soft-remove"] as any, - orphanRemoval: true, - }) - service_zones = new Collection>(this) - - @Property({ - onCreate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", +import { model } from "@medusajs/framework/utils" + +import { ServiceZone } from "./service-zone" + +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(), }) - created_at: Date - - @Property({ - onCreate: () => new Date(), - onUpdate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", + .indexes([ + { + on: ["name"], + where: "deleted_at IS NULL", + unique: true, + }, + ]) + .cascades({ + delete: ["service_zones"], }) - 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") - } -} diff --git a/packages/modules/fulfillment/src/models/fulfillment.ts b/packages/modules/fulfillment/src/models/fulfillment.ts index dce04f0f34f26..14fd10b56a912 100644 --- a/packages/modules/fulfillment/src/models/fulfillment.ts +++ b/packages/modules/fulfillment/src/models/fulfillment.ts @@ -1,183 +1,54 @@ -import { - createPsqlIndexStatementHelper, - DALUtils, - generateEntityId, -} 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" - -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, - }) - packed_at: Date | null = null - - @Property({ - columnType: "timestamptz", - nullable: true, - }) - 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 | 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 | null = null - - @ManyToOne(() => ShippingOption, { persist: false }) - shipping_option: ShippingOption | null - - @ManyToOne(() => FulfillmentProvider, { persist: false }) - provider: Rel - - @OneToOne({ - entity: () => FulfillmentAddress, - owner: true, - cascade: [Cascade.PERSIST, "soft-remove"] as any, - nullable: true, - onDelete: "cascade", - }) - delivery_address!: Rel - - @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>(this) - - @OneToMany(() => FulfillmentLabel, (label) => label.fulfillment, { - cascade: [Cascade.PERSIST, "soft-remove"] as any, - orphanRemoval: true, - }) - labels = new Collection>(this) - - @Property({ - onCreate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", +import { model } from "@medusajs/framework/utils" + +import { FulfillmentAddress } from "./address" +import { FulfillmentItem } from "./fulfillment-item" +import { FulfillmentLabel } from "./fulfillment-label" +import { FulfillmentProvider } from "./fulfillment-provider" +import { ShippingOption } from "./shipping-option" + +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(), + }) + .indexes([ + { + on: ["location_id"], + where: "deleted_at IS NULL", + }, + ]) + .cascades({ + delete: ["delivery_address", "items", "labels"], }) - 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 - } -} diff --git a/packages/modules/fulfillment/src/models/geo-zone.ts b/packages/modules/fulfillment/src/models/geo-zone.ts index fc7e2f9175488..aac5c076c7b63 100644 --- a/packages/modules/fulfillment/src/models/geo-zone.ts +++ b/packages/modules/fulfillment/src/models/geo-zone.ts @@ -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" - -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 | null = null - - @Property({ columnType: "jsonb", nullable: true }) - metadata: Record | null = null - - @ManyToOne(() => ServiceZone, { - persist: false, - }) - service_zone: Rel - - @Property({ - onCreate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", - }) - created_at: Date +import { ServiceZone } from "./service-zone" + +export type GeoZoneSchema = { + id: PrimaryKeyModifier + type: TextProperty + country_code: TextProperty + province_code?: NullableModifier + city?: NullableModifier + postal_expression?: NullableModifier, JSONProperty> + service_zone: BelongsTo<() => typeof ServiceZone> + metadata?: NullableModifier, JSONProperty> +} - @Property({ - onCreate: () => new Date(), - onUpdate: () => new Date(), - columnType: "timestamptz", - defaultRaw: "now()", +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(), }) - 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 - } -} + .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, "GeoZone"> diff --git a/packages/modules/fulfillment/src/models/index.ts b/packages/modules/fulfillment/src/models/index.ts index bf0716c836ab0..1ef9c84c0f911 100644 --- a/packages/modules/fulfillment/src/models/index.ts +++ b/packages/modules/fulfillment/src/models/index.ts @@ -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" diff --git a/packages/modules/fulfillment/src/models/service-zone.ts b/packages/modules/fulfillment/src/models/service-zone.ts index 50b21a9f8711c..eaedfa0e91532 100644 --- a/packages/modules/fulfillment/src/models/service-zone.ts +++ b/packages/modules/fulfillment/src/models/service-zone.ts @@ -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" - -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 | 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 +import { FulfillmentSet } from "./fulfillment-set" +import { GeoZone } from "./geo-zone" +import { ShippingOption } from "./shipping-option" + +export type ServiceZoneSchema = { + id: PrimaryKeyModifier + name: TextProperty + fulfillment_set: BelongsTo<() => typeof FulfillmentSet> + geo_zones: HasMany<() => typeof GeoZone> + shipping_options: HasMany<() => typeof ShippingOption> + metadata: NullableModifier, JSONProperty> +} - @OneToMany(() => GeoZone, "service_zone", { - cascade: [Cascade.PERSIST, "soft-remove"] as any, - orphanRemoval: true, +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(), }) - geo_zones = new Collection>(this) - - @OneToMany( - () => ShippingOption, - (shippingOption) => shippingOption.service_zone, + .indexes([ { - cascade: [Cascade.PERSIST, "soft-remove"] as any, - orphanRemoval: true, - } - ) - 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") - this.fulfillment_set_id ??= this.fulfillment_set?.id - } - - @OnInit() - onInit() { - this.id = generateEntityId(this.id, "serzo") - this.fulfillment_set_id ??= this.fulfillment_set?.id - } -} + on: ["name"], + unique: true, + where: "deleted_at IS NULL", + }, + ]) + .cascades({ + delete: ["geo_zones", "shipping_options"], + }) as unknown as DmlEntity< + DMLEntitySchemaBuilder, + "ServiceZone" +> diff --git a/packages/modules/fulfillment/src/models/shipping-option-rule.ts b/packages/modules/fulfillment/src/models/shipping-option-rule.ts index a237c07b75c76..f4cc9525275fb 100644 --- a/packages/modules/fulfillment/src/models/shipping-option-rule.ts +++ b/packages/modules/fulfillment/src/models/shipping-option-rule.ts @@ -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" - -type ShippingOptionRuleOptionalProps = DAL.SoftDeletableModelDateColumns - -const DeletedAtIndex = createPsqlIndexStatementHelper({ - tableName: "shipping_option_rule", - columns: "deleted_at", - where: "deleted_at IS NOT NULL", -}) - -const ShippingOptionIdIndex = createPsqlIndexStatementHelper({ - tableName: "shipping_option_rule", - columns: "shipping_option_id", - where: "deleted_at IS NULL", +import { model, RuleOperator } from "@medusajs/framework/utils" +import { ShippingOption } from "./shipping-option" + +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", + }), }) - -@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 - - @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 - - @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 - } -} diff --git a/packages/modules/fulfillment/src/models/shipping-option-type.ts b/packages/modules/fulfillment/src/models/shipping-option-type.ts index 7425d2bb65ba9..8de32d5b02d0a 100644 --- a/packages/modules/fulfillment/src/models/shipping-option-type.ts +++ b/packages/modules/fulfillment/src/models/shipping-option-type.ts @@ -1,80 +1,13 @@ -import { - createPsqlIndexStatementHelper, - DALUtils, - generateEntityId, -} 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" - -type ShippingOptionTypeOptionalProps = DAL.SoftDeletableModelDateColumns - -const DeletedAtIndex = createPsqlIndexStatementHelper({ - tableName: "shipping_option_type", - columns: "deleted_at", - where: "deleted_at IS NOT NULL", +import { model } from "@medusajs/framework/utils" + +import { ShippingOption } from "./shipping-option" + +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 - - @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") - } -} diff --git a/packages/modules/fulfillment/src/models/shipping-option.ts b/packages/modules/fulfillment/src/models/shipping-option.ts index 5f110b24f127f..ee02078ef6265 100644 --- a/packages/modules/fulfillment/src/models/shipping-option.ts +++ b/packages/modules/fulfillment/src/models/shipping-option.ts @@ -1,182 +1,42 @@ -import { - createPsqlIndexStatementHelper, - DALUtils, - generateEntityId, - Searchable, - 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" - -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, - }) - price_type: ShippingOptionPriceType - - @ManyToOne(() => ServiceZone, { - type: "text", - fieldName: "service_zone_id", - mapToPk: true, - onDelete: "cascade", - }) - @ServiceZoneIdIndex.MikroORMIndex() - service_zone_id: string - - @ManyToOne(() => ShippingProfile, { - type: "text", - fieldName: "shipping_profile_id", - mapToPk: true, - nullable: true, - onDelete: "set null", +import { model, ShippingOptionPriceType } from "@medusajs/framework/utils" + +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" + +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", + }), + }) + .cascades({ + delete: ["rules", "type"], }) - @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 | null = null - - @Property({ columnType: "jsonb", nullable: true }) - metadata: Record | null = null - - @ManyToOne(() => ServiceZone, { persist: false }) - service_zone: Rel - - @ManyToOne(() => ShippingProfile, { - persist: false, - }) - shipping_profile: Rel | null - - @ManyToOne(() => FulfillmentProvider, { - persist: false, - }) - provider: Rel | 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 - - @OneToMany(() => ShippingOptionRule, "shipping_option", { - cascade: [Cascade.PERSIST, "soft-remove"] as any, - orphanRemoval: true, - }) - 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 - - @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 - } -} diff --git a/packages/modules/fulfillment/src/models/shipping-profile.ts b/packages/modules/fulfillment/src/models/shipping-profile.ts index c8d7fba37772c..1ecb640f01110 100644 --- a/packages/modules/fulfillment/src/models/shipping-profile.ts +++ b/packages/modules/fulfillment/src/models/shipping-profile.ts @@ -1,92 +1,21 @@ -import { - createPsqlIndexStatementHelper, - DALUtils, - generateEntityId, - Searchable, -} 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" - -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>(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()", +import { model } from "@medusajs/framework/utils" + +import { ShippingOption } from "./shipping-option" + +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(), }) - 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", + }, + ]) diff --git a/packages/modules/fulfillment/src/repositories/fulfillment-set.ts b/packages/modules/fulfillment/src/repositories/fulfillment-set.ts deleted file mode 100644 index 85d443f8eecd9..0000000000000 --- a/packages/modules/fulfillment/src/repositories/fulfillment-set.ts +++ /dev/null @@ -1,59 +0,0 @@ -/* -import { Context, FulfillmentTypes } from "@medusajs/framework/types" -import { DALUtils, promiseAll } from "@medusajs/framework/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 -) { - async update( - data: { - entity: FulfillmentSet - update: FulfillmentTypes.FulfillmentSetDTO - }[], - context?: Context - ): Promise { - const manager = this.getActiveManager(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 - } -} -*/ diff --git a/packages/modules/fulfillment/src/repositories/index.ts b/packages/modules/fulfillment/src/repositories/index.ts deleted file mode 100644 index 390afcdb9c439..0000000000000 --- a/packages/modules/fulfillment/src/repositories/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { MikroOrmBaseRepository as BaseRepository } from "@medusajs/framework/utils" diff --git a/packages/modules/fulfillment/src/services/fulfillment-module-service.ts b/packages/modules/fulfillment/src/services/fulfillment-module-service.ts index 63e6513e0d134..f5f8652bbd239 100644 --- a/packages/modules/fulfillment/src/services/fulfillment-module-service.ts +++ b/packages/modules/fulfillment/src/services/fulfillment-module-service.ts @@ -8,6 +8,7 @@ import { FulfillmentOption, FulfillmentTypes, IFulfillmentModuleService, + InferEntityType, InternalModuleDeclaration, Logger, ModuleJoinerConfig, @@ -19,6 +20,7 @@ import { } from "@medusajs/framework/types" import { arrayDifference, + deepCopy, deepEqualObj, EmitEvents, getSetDifference, @@ -49,6 +51,7 @@ import { buildCreatedServiceZoneEvents, eventBuilders, isContextValid, + Rule, validateAndNormalizeRules, } from "@utils" import { joinerConfig } from "../joiner-config" @@ -70,6 +73,7 @@ const generateMethodForModels = { type InjectedDependencies = { baseRepository: DAL.RepositoryService + fulfillmentAddressService: ModulesSdkTypes.IMedusaInternalService fulfillmentSetService: ModulesSdkTypes.IMedusaInternalService serviceZoneService: ModulesSdkTypes.IMedusaInternalService geoZoneService: ModulesSdkTypes.IMedusaInternalService @@ -96,15 +100,31 @@ export default class FulfillmentModuleService implements IFulfillmentModuleService { protected baseRepository_: DAL.RepositoryService - protected readonly fulfillmentSetService_: ModulesSdkTypes.IMedusaInternalService - protected readonly serviceZoneService_: ModulesSdkTypes.IMedusaInternalService - protected readonly geoZoneService_: ModulesSdkTypes.IMedusaInternalService - protected readonly shippingProfileService_: ModulesSdkTypes.IMedusaInternalService - protected readonly shippingOptionService_: ModulesSdkTypes.IMedusaInternalService - protected readonly shippingOptionRuleService_: ModulesSdkTypes.IMedusaInternalService - protected readonly shippingOptionTypeService_: ModulesSdkTypes.IMedusaInternalService + protected readonly fulfillmentSetService_: ModulesSdkTypes.IMedusaInternalService< + InferEntityType + > + protected readonly serviceZoneService_: ModulesSdkTypes.IMedusaInternalService< + InferEntityType + > + protected readonly geoZoneService_: ModulesSdkTypes.IMedusaInternalService< + InferEntityType + > + protected readonly shippingProfileService_: ModulesSdkTypes.IMedusaInternalService< + InferEntityType + > + protected readonly shippingOptionService_: ModulesSdkTypes.IMedusaInternalService< + InferEntityType + > + protected readonly shippingOptionRuleService_: ModulesSdkTypes.IMedusaInternalService< + InferEntityType + > + protected readonly shippingOptionTypeService_: ModulesSdkTypes.IMedusaInternalService< + InferEntityType + > protected readonly fulfillmentProviderService_: FulfillmentProviderService - protected readonly fulfillmentService_: ModulesSdkTypes.IMedusaInternalService + protected readonly fulfillmentService_: ModulesSdkTypes.IMedusaInternalService< + InferEntityType + > constructor( { @@ -118,6 +138,7 @@ export default class FulfillmentModuleService shippingOptionTypeService, fulfillmentProviderService, fulfillmentService, + fulfillmentAddressService, }: InjectedDependencies, protected readonly moduleDeclaration: InternalModuleDeclaration ) { @@ -188,7 +209,7 @@ export default class FulfillmentModuleService return isContextValid( context, - shippingOption.rules.map((r) => r) + shippingOption.rules.map((r) => r) as unknown as Rule[] ) }) } @@ -292,7 +313,7 @@ export default class FulfillmentModuleService | FulfillmentTypes.CreateFulfillmentSetDTO | FulfillmentTypes.CreateFulfillmentSetDTO[], @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise[]> { const data_ = Array.isArray(data) ? data : [data] if (!data_.length) { @@ -358,7 +379,7 @@ export default class FulfillmentModuleService | FulfillmentTypes.CreateServiceZoneDTO[] | FulfillmentTypes.CreateServiceZoneDTO, @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise[]> { const data_ = Array.isArray(data) ? data : [data] if (!data_.length) { @@ -422,7 +443,7 @@ export default class FulfillmentModuleService | FulfillmentTypes.CreateShippingOptionDTO[] | FulfillmentTypes.CreateShippingOptionDTO, @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise[]> { const data_ = Array.isArray(data) ? data : [data] if (!data_.length) { @@ -491,7 +512,7 @@ export default class FulfillmentModuleService | FulfillmentTypes.CreateShippingProfileDTO[] | FulfillmentTypes.CreateShippingProfileDTO, @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise[]> { const data_ = Array.isArray(data) ? data : [data] if (!data_.length) { @@ -580,7 +601,7 @@ export default class FulfillmentModuleService | FulfillmentTypes.CreateShippingOptionRuleDTO[] | FulfillmentTypes.CreateShippingOptionRuleDTO, @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise[]> { const data_ = Array.isArray(data) ? data : [data] if (!data_.length) { @@ -625,7 +646,7 @@ export default class FulfillmentModuleService try { const providerResult = await this.fulfillmentProviderService_.createFulfillment( - provider_id, + provider_id!, // TODO: should we add a runtime check on provider_id being provided? fulfillmentData || {}, items.map((i) => i), order, @@ -692,7 +713,7 @@ export default class FulfillmentModuleService try { const providerResult = await this.fulfillmentProviderService_.createReturn( - fulfillment.provider_id, + fulfillment.provider_id!, // TODO: should we add a runtime check on provider_id being provided?, fulfillment as Record ) await this.fulfillmentService_.update( @@ -750,7 +771,10 @@ export default class FulfillmentModuleService protected async updateFulfillmentSets_( data: UpdateFulfillmentSetDTO[] | UpdateFulfillmentSetDTO, @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise< + | InferEntityType[] + | InferEntityType + > { const data_ = Array.isArray(data) ? data : [data] if (!data_.length) { @@ -789,9 +813,10 @@ export default class FulfillmentModuleService ) } - const fulfillmentSetMap = new Map( - fulfillmentSets.map((f) => [f.id, f]) - ) + const fulfillmentSetMap = new Map< + string, + InferEntityType + >(fulfillmentSets.map((f) => [f.id, f])) const serviceZoneIdsToDelete: string[] = [] const geoZoneIdsToDelete: string[] = [] @@ -1013,7 +1038,9 @@ export default class FulfillmentModuleService | FulfillmentTypes.UpdateServiceZoneDTO[] | FulfillmentTypes.UpdateServiceZoneDTO, @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise< + InferEntityType | InferEntityType[] + > { const data_ = Array.isArray(data) ? data : [data] if (!data_.length) { @@ -1052,7 +1079,7 @@ export default class FulfillmentModuleService ) } - const serviceZoneMap = new Map( + const serviceZoneMap = new Map>( serviceZones.map((s) => [s.id, s]) ) @@ -1209,7 +1236,9 @@ export default class FulfillmentModuleService | FulfillmentTypes.UpsertServiceZoneDTO[] | FulfillmentTypes.UpsertServiceZoneDTO, @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise< + InferEntityType[] | InferEntityType + > { const input = Array.isArray(data) ? data : [data] const forUpdate = input.filter( (serviceZone): serviceZone is FulfillmentTypes.UpdateServiceZoneDTO => @@ -1220,8 +1249,8 @@ export default class FulfillmentModuleService !serviceZone.id ) - const created: ServiceZone[] = [] - const updated: ServiceZone[] = [] + const created: InferEntityType[] = [] + const updated: InferEntityType[] = [] if (forCreate.length) { const createdServiceZones = await this.createServiceZones_( @@ -1304,8 +1333,13 @@ export default class FulfillmentModuleService async updateShippingOptions_( data: UpdateShippingOptionsInput[] | UpdateShippingOptionsInput, @MedusaContext() sharedContext: Context = {} - ): Promise { - const dataArray = Array.isArray(data) ? data : [data] + ): Promise< + | InferEntityType + | InferEntityType[] + > { + const dataArray = Array.isArray(data) + ? data.map((d) => deepCopy(d)) + : [deepCopy(data)] if (!dataArray.length) { return [] @@ -1365,7 +1399,8 @@ export default class FulfillmentModuleService const existingRulesMap: Map< string, - FulfillmentTypes.UpdateShippingOptionRuleDTO | ShippingOptionRule + | FulfillmentTypes.UpdateShippingOptionRuleDTO + | InferEntityType > = new Map(existingRules.map((rule) => [rule.id, rule])) const updatedRules = shippingOption.rules @@ -1378,6 +1413,13 @@ export default class FulfillmentModuleService updatedRuleIds.push(rule.id) } + // @ts-ignore + delete rule.created_at + // @ts-ignore + delete rule.updated_at + // @ts-ignore + delete rule.deleted_at + const ruleData: FulfillmentTypes.UpdateShippingOptionRuleDTO = { ...existingRule, ...rule, @@ -1530,7 +1572,7 @@ export default class FulfillmentModuleService | FulfillmentTypes.UpsertShippingOptionDTO[] | FulfillmentTypes.UpsertShippingOptionDTO, @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise[]> { const input = Array.isArray(data) ? data : [data] const forUpdate = input.filter( (shippingOption): shippingOption is UpdateShippingOptionsInput => @@ -1543,8 +1585,8 @@ export default class FulfillmentModuleService !shippingOption.id ) - let created: ShippingOption[] = [] - let updated: ShippingOption[] = [] + let created: InferEntityType[] = [] + let updated: InferEntityType[] = [] if (forCreate.length) { const createdShippingOptions = await this.createShippingOptions_( @@ -1649,8 +1691,8 @@ export default class FulfillmentModuleService (prof): prof is FulfillmentTypes.CreateShippingProfileDTO => !prof.id ) - let created: ShippingProfile[] = [] - let updated: ShippingProfile[] = [] + let created: InferEntityType[] = [] + let updated: InferEntityType[] = [] if (forCreate.length) { created = await this.shippingProfileService_.create( @@ -1755,7 +1797,10 @@ export default class FulfillmentModuleService | FulfillmentTypes.UpdateShippingOptionRuleDTO[] | FulfillmentTypes.UpdateShippingOptionRuleDTO, @MedusaContext() sharedContext: Context = {} - ): Promise { + ): Promise< + | InferEntityType + | InferEntityType[] + > { const data_ = Array.isArray(data) ? data : [data] if (!data_.length) { @@ -1796,8 +1841,8 @@ export default class FulfillmentModuleService id: string, data: FulfillmentTypes.UpdateFulfillmentDTO, @MedusaContext() sharedContext: Context - ): Promise { - const existingFulfillment: Fulfillment = + ): Promise> { + const existingFulfillment: InferEntityType = await this.fulfillmentService_.retrieve( id, { @@ -1868,7 +1913,7 @@ export default class FulfillmentModuleService } private handleFulfillmentUpdateEvents( - fulfillment: Fulfillment, + fulfillment: InferEntityType, existingLabelIds: string[], updatedLabelIds: string[], deletedLabelIds: string[], @@ -1919,7 +1964,7 @@ export default class FulfillmentModuleService if (!fulfillment.canceled_at) { try { await this.fulfillmentProviderService_.cancelFulfillment( - fulfillment.provider_id, + fulfillment.provider_id!, // TODO: should we add a runtime check on provider_id being provided?, fulfillment.data ?? {} ) } catch (error) { @@ -2094,7 +2139,9 @@ export default class FulfillmentModuleService } } - protected static canCancelFulfillmentOrThrow(fulfillment: Fulfillment) { + protected static canCancelFulfillmentOrThrow( + fulfillment: InferEntityType + ) { if (fulfillment.shipped_at) { throw new MedusaError( MedusaError.Types.INVALID_DATA, @@ -2113,7 +2160,7 @@ export default class FulfillmentModuleService } protected static validateMissingShippingOptions_( - shippingOptions: ShippingOption[], + shippingOptions: InferEntityType[], shippingOptionsData: UpdateShippingOptionsInput[] ) { const missingShippingOptionIds = arrayDifference( @@ -2132,7 +2179,7 @@ export default class FulfillmentModuleService } protected static validateMissingShippingOptionRules( - shippingOption: ShippingOption, + shippingOption: InferEntityType, shippingOptionUpdateData: FulfillmentTypes.UpdateShippingOptionDTO ) { if (!shippingOptionUpdateData.rules) { diff --git a/packages/modules/fulfillment/src/utils/events.ts b/packages/modules/fulfillment/src/utils/events.ts index f99f6d575cc39..292d7e7f973ed 100644 --- a/packages/modules/fulfillment/src/utils/events.ts +++ b/packages/modules/fulfillment/src/utils/events.ts @@ -7,7 +7,7 @@ import { ShippingOptionRule, ShippingOptionType, } from "@models" -import { Context } from "@medusajs/framework/types" +import { Context, InferEntityType } from "@medusajs/framework/types" import { CommonEvents, FulfillmentEvents, @@ -172,7 +172,7 @@ export function buildCreatedFulfillmentEvents({ fulfillments, sharedContext, }: { - fulfillments: Fulfillment[] + fulfillments: InferEntityType[] sharedContext: Context }) { if (!fulfillments.length) { @@ -210,7 +210,7 @@ export function buildCreatedShippingOptionEvents({ shippingOptions, sharedContext, }: { - shippingOptions: ShippingOption[] + shippingOptions: InferEntityType[] sharedContext: Context }) { if (!shippingOptions.length) { @@ -218,8 +218,8 @@ export function buildCreatedShippingOptionEvents({ } const options: { id: string }[] = [] - const types: ShippingOptionType[] = [] - const rules: ShippingOptionRule[] = [] + const types: InferEntityType[] = [] + const rules: InferEntityType[] = [] shippingOptions.forEach((shippingOption) => { options.push({ id: shippingOption.id }) @@ -242,14 +242,14 @@ export function buildCreatedFulfillmentSetEvents({ fulfillmentSets, sharedContext, }: { - fulfillmentSets: FulfillmentSet[] + fulfillmentSets: InferEntityType[] sharedContext: Context }): void { if (!fulfillmentSets.length) { return } - const serviceZones: ServiceZone[] = [] + const serviceZones: InferEntityType[] = [] fulfillmentSets.forEach((fulfillmentSet) => { if (!fulfillmentSet.service_zones?.length) { @@ -268,14 +268,14 @@ export function buildCreatedServiceZoneEvents({ serviceZones, sharedContext, }: { - serviceZones: ServiceZone[] + serviceZones: InferEntityType[] sharedContext: Context }): void { if (!serviceZones.length) { return } - const geoZones: GeoZone[] = [] + const geoZones: InferEntityType[] = [] serviceZones.forEach((serviceZone) => { if (!serviceZone.geo_zones.length) { diff --git a/packages/modules/fulfillment/src/utils/utils.ts b/packages/modules/fulfillment/src/utils/utils.ts index 025f70505c7f2..7e3fe27fb088c 100644 --- a/packages/modules/fulfillment/src/utils/utils.ts +++ b/packages/modules/fulfillment/src/utils/utils.ts @@ -16,7 +16,7 @@ import { export type Rule = { attribute: string - operator: Lowercase + operator: Lowercase | (string & {}) value: string | string[] | null } diff --git a/packages/modules/payment/integration-tests/__tests__/services/payment-module/index.spec.ts b/packages/modules/payment/integration-tests/__tests__/services/payment-module/index.spec.ts index 9ab9e14909ea5..cbfe413d98c40 100644 --- a/packages/modules/payment/integration-tests/__tests__/services/payment-module/index.spec.ts +++ b/packages/modules/payment/integration-tests/__tests__/services/payment-module/index.spec.ts @@ -23,11 +23,13 @@ moduleIntegrationTestRunner({ service: PaymentModuleService, }).linkable + expect(Object.keys(linkable)).toHaveLength(5) expect(Object.keys(linkable)).toEqual([ "paymentCollection", "paymentSession", "payment", "refundReason", + "paymentProvider", ]) Object.keys(linkable).forEach((key) => { @@ -71,6 +73,15 @@ moduleIntegrationTestRunner({ field: "refundReason", }, }, + paymentProvider: { + id: { + linkable: "payment_provider_id", + entity: "PaymentProvider", + primaryKey: "id", + serviceName: "payment", + field: "paymentProvider", + }, + }, }) }) diff --git a/packages/modules/promotion/integration-tests/__tests__/services/promotion-module/promotion.spec.ts b/packages/modules/promotion/integration-tests/__tests__/services/promotion-module/promotion.spec.ts index dc675df38d475..8cc7b9b54da0b 100644 --- a/packages/modules/promotion/integration-tests/__tests__/services/promotion-module/promotion.spec.ts +++ b/packages/modules/promotion/integration-tests/__tests__/services/promotion-module/promotion.spec.ts @@ -854,6 +854,7 @@ moduleIntegrationTestRunner({ code: "PROMOTION_1", application_method: { id: expect.any(String), + promotion_id: expect.any(String), type: "fixed", }, },