diff --git a/.changeset/new-icons-chew.md b/.changeset/new-icons-chew.md new file mode 100644 index 0000000000..269b235443 --- /dev/null +++ b/.changeset/new-icons-chew.md @@ -0,0 +1,7 @@ +--- +"medusa-fulfillment-manual": patch +"medusa-interfaces": patch +"@medusajs/medusa": patch +--- + +Convert FulfillmentService to TypeScript diff --git a/packages/medusa-fulfillment-manual/src/services/manual-fulfillment.js b/packages/medusa-fulfillment-manual/src/services/manual-fulfillment.js index a03d5499e7..c0b5ddedc1 100644 --- a/packages/medusa-fulfillment-manual/src/services/manual-fulfillment.js +++ b/packages/medusa-fulfillment-manual/src/services/manual-fulfillment.js @@ -19,7 +19,7 @@ class ManualFulfillmentService extends FulfillmentService { ] } - validateFulfillmentData(data, cart) { + validateFulfillmentData(_, data, cart) { return data } diff --git a/packages/medusa-interfaces/src/fulfillment-service.js b/packages/medusa-interfaces/src/fulfillment-service.js index 4e56063a63..912480a0b0 100644 --- a/packages/medusa-interfaces/src/fulfillment-service.js +++ b/packages/medusa-interfaces/src/fulfillment-service.js @@ -24,7 +24,9 @@ class BaseFulfillmentService extends BaseService { * to create shipping options in Medusa that can be chosen between by the * customer. */ - getFulfillmentOptions() {} + getFulfillmentOptions() { + throw Error("getFulfillmentOptions must be overridden by the child class") + } /** * Called before a shipping method is set on a cart to ensure that the data @@ -32,12 +34,13 @@ class BaseFulfillmentService extends BaseService { * data about the shipment such as an id of a drop point. It is up to the * fulfillment provider to enforce that the correct data is being sent * through. + * @param {object} optionData - the data to validate * @param {object} data - the data to validate - * @param {object} cart - the cart to which the shipping method will be applied + * @param {object | undefined} cart - the cart to which the shipping method will be applied * @return {object} the data to populate `cart.shipping_methods.$.data` this * is usually important for future actions like generating shipping labels */ - validateFulfillmentData(data, cart) { + validateFulfillmentData(optionData, data, cart) { throw Error("validateFulfillmentData must be overridden by the child class") } @@ -56,12 +59,16 @@ class BaseFulfillmentService extends BaseService { /** * Used to calculate a price for a given shipping option. */ - calculatePrice(data, cart) { + calculatePrice(optionData, data, cart) { throw Error("calculatePrice must be overridden by the child class") } - createFulfillment() { - throw Error("createOrder must be overridden by the child class") + createFulfillment(data, items, order, fulfillment) { + throw Error("createFulfillment must be overridden by the child class") + } + + cancelFulfillment(fulfillment) { + throw Error("cancelFulfillment must be overridden by the child class") } /** @@ -94,6 +101,10 @@ class BaseFulfillmentService extends BaseService { getShipmentDocuments(data) { return [] } + + retrieveDocuments(fulfillmentData, documentType) { + throw Error("retrieveDocuments must be overridden by the child class") + } } export default BaseFulfillmentService diff --git a/packages/medusa/src/services/fulfillment-provider.js b/packages/medusa/src/services/fulfillment-provider.js deleted file mode 100644 index cc6e5ffca4..0000000000 --- a/packages/medusa/src/services/fulfillment-provider.js +++ /dev/null @@ -1,108 +0,0 @@ -import { MedusaError } from "medusa-core-utils" - -/** - * Helps retrive fulfillment providers - */ -class FulfillmentProviderService { - constructor(container) { - /** @private {logger} */ - this.container_ = container - } - - async registerInstalledProviders(providers) { - const { manager, fulfillmentProviderRepository } = this.container_ - const model = manager.getCustomRepository(fulfillmentProviderRepository) - await model.update({}, { is_installed: false }) - - for (const p of providers) { - const n = model.create({ id: p, is_installed: true }) - await model.save(n) - } - } - - async list() { - const { manager, fulfillmentProviderRepository } = this.container_ - const fpRepo = manager.getCustomRepository(fulfillmentProviderRepository) - - return await fpRepo.find({}) - } - - async listFulfillmentOptions(providers) { - const result = await Promise.all( - providers.map(async (p) => { - const provider = await this.retrieveProvider(p) - return { - provider_id: p, - options: await provider.getFulfillmentOptions(), - } - }) - ) - - return result - } - - /** - * @param {string} provider_id - the provider id - * @return {FulfillmentService} the payment fulfillment provider - */ - retrieveProvider(provider_id) { - try { - return this.container_[`fp_${provider_id}`] - } catch (err) { - throw new MedusaError( - MedusaError.Types.NOT_FOUND, - `Could not find a fulfillment provider with id: ${provider_id}` - ) - } - } - - async createFulfillment(method, items, order, fulfillment) { - const provider = this.retrieveProvider(method.shipping_option.provider_id) - return provider.createFulfillment(method.data, items, order, fulfillment) - } - - async canCalculate(option) { - const provider = this.retrieveProvider(option.provider_id) - return provider.canCalculate(option.data) - } - - async validateFulfillmentData(option, data, cart) { - const provider = this.retrieveProvider(option.provider_id) - return provider.validateFulfillmentData(option.data, data, cart) - } - - async cancelFulfillment(fulfillment) { - const provider = this.retrieveProvider(fulfillment.provider_id) - return provider.cancelFulfillment(fulfillment.data) - } - - async calculatePrice(option, data, cart) { - const provider = this.retrieveProvider(option.provider_id) - return provider.calculatePrice(option.data, data, cart) - } - - async validateOption(option) { - const provider = this.retrieveProvider(option.provider_id) - return provider.validateOption(option.data) - } - - async createReturn(returnOrder) { - const option = returnOrder.shipping_method.shipping_option - const provider = this.retrieveProvider(option.provider_id) - return provider.createReturn(returnOrder) - } - - /** - * Fetches documents from the fulfillment provider - * @param {string} providerId - the id of the provider - * @param {object} fulfillmentData - the data relating to the fulfillment - * @param {"invoice" | "label"} documentType - the typ of - * document to fetch - */ - async retrieveDocuments(providerId, fulfillmentData, documentType) { - const provider = this.retrieveProvider(providerId) - return provider.retrieveDocuments(fulfillmentData, documentType) - } -} - -export default FulfillmentProviderService diff --git a/packages/medusa/src/services/fulfillment-provider.ts b/packages/medusa/src/services/fulfillment-provider.ts new file mode 100644 index 0000000000..e58181d921 --- /dev/null +++ b/packages/medusa/src/services/fulfillment-provider.ts @@ -0,0 +1,191 @@ +import { MedusaError } from "medusa-core-utils" +import BaseFulfillmentService from "medusa-interfaces/dist/fulfillment-service" +import { EntityManager } from "typeorm" +import { TransactionBaseService } from "../interfaces" +import { + Cart, + Fulfillment, + FulfillmentProvider, + LineItem, + Order, + Return, + ShippingMethod, + ShippingOption, +} from "../models" +import { FulfillmentProviderRepository } from "../repositories/fulfillment-provider" +import { CreateFulfillmentOrder } from "../types/fulfillment" +import { + CreateReturnType, + FulfillmentOptions, +} from "../types/fulfillment-provider" +import { MedusaContainer } from "../types/global" + +type FulfillmentProviderKey = `fp_${string}` + +type FulfillmentProviderContainer = MedusaContainer & { + fulfillmentProviderRepository: typeof FulfillmentProviderRepository + manager: EntityManager +} & { + [key in `${FulfillmentProviderKey}`]: BaseFulfillmentService +} + +/** + * Helps retrive fulfillment providers + */ +class FulfillmentProviderService extends TransactionBaseService { + protected manager_: EntityManager + protected transactionManager_: EntityManager | undefined + + protected readonly container_: FulfillmentProviderContainer + + protected readonly fulfillmentProviderRepository_: typeof FulfillmentProviderRepository + + constructor(container: FulfillmentProviderContainer) { + super(container) + + const { manager, fulfillmentProviderRepository } = container + + this.container_ = container + this.manager_ = manager + this.fulfillmentProviderRepository_ = fulfillmentProviderRepository + } + + async registerInstalledProviders(providers: string[]): Promise { + return await this.atomicPhase_(async (manager) => { + const fulfillmentProviderRepo = manager.getCustomRepository( + this.fulfillmentProviderRepository_ + ) + await fulfillmentProviderRepo.update({}, { is_installed: false }) + + for (const p of providers) { + const n = fulfillmentProviderRepo.create({ id: p, is_installed: true }) + await fulfillmentProviderRepo.save(n) + } + }) + } + + async list(): Promise { + const fpRepo = this.manager_.getCustomRepository( + this.fulfillmentProviderRepository_ + ) + + return await fpRepo.find({}) + } + + async listFulfillmentOptions( + providerIds: string[] + ): Promise { + return await Promise.all( + providerIds.map(async (p) => { + const provider = await this.retrieveProvider(p) + return { + provider_id: p, + options: + (await provider.getFulfillmentOptions()) as unknown as Record< + string, + unknown + >[], + } + }) + ) + } + + /** + * @param providerId - the provider id + * @return the payment fulfillment provider + */ + retrieveProvider(providerId: string): BaseFulfillmentService { + try { + return this.container_[`fp_${providerId}`] + } catch (err) { + throw new MedusaError( + MedusaError.Types.NOT_FOUND, + `Could not find a fulfillment provider with id: ${providerId}` + ) + } + } + + async createFulfillment( + method: ShippingMethod, + items: LineItem[], + order: CreateFulfillmentOrder, + fulfillment: Omit + ): Promise> { + const provider = this.retrieveProvider(method.shipping_option.provider_id) + return provider.createFulfillment( + method.data, + items, + order, + fulfillment + ) as unknown as Record + } + + async canCalculate(option: ShippingOption): Promise { + const provider = this.retrieveProvider(option.provider_id) + return provider.canCalculate(option.data) as unknown as boolean + } + + async validateFulfillmentData( + option: ShippingOption, + data: Record, + cart: Cart | Record + ): Promise> { + const provider = this.retrieveProvider(option.provider_id) + return provider.validateFulfillmentData( + option.data, + data, + cart + ) as unknown as Record + } + + async cancelFulfillment(fulfillment: Fulfillment): Promise { + const provider = this.retrieveProvider(fulfillment.provider_id) + return provider.cancelFulfillment( + fulfillment.data + ) as unknown as Fulfillment + } + + async calculatePrice( + option: ShippingOption, + data: Record, + cart?: Order | Cart + ): Promise { + const provider = this.retrieveProvider(option.provider_id) + return provider.calculatePrice(option.data, data, cart) as unknown as number + } + + async validateOption(option: ShippingOption): Promise { + const provider = this.retrieveProvider(option.provider_id) + return provider.validateOption(option.data) as unknown as boolean + } + + async createReturn( + returnOrder: CreateReturnType + ): Promise> { + const option = returnOrder.shipping_method.shipping_option + const provider = this.retrieveProvider(option.provider_id) + return provider.createReturn(returnOrder) as unknown as Record< + string, + unknown + > + } + + /** + * Fetches documents from the fulfillment provider + * @param providerId - the id of the provider + * @param fulfillmentData - the data relating to the fulfillment + * @param documentType - the typ of + * @returns document to fetch + */ + // TODO: consider removal in favor of "getReturnDocuments" and "getShipmentDocuments" + async retrieveDocuments( + providerId: string, + fulfillmentData: Record, + documentType: "invoice" | "label" + ): Promise { + const provider = this.retrieveProvider(providerId) + return provider.retrieveDocuments(fulfillmentData, documentType) + } +} + +export default FulfillmentProviderService diff --git a/packages/medusa/src/services/shipping-option.ts b/packages/medusa/src/services/shipping-option.ts index a11a68ae94..83dee51501 100644 --- a/packages/medusa/src/services/shipping-option.ts +++ b/packages/medusa/src/services/shipping-option.ts @@ -252,7 +252,7 @@ class ShippingOptionService extends TransactionBaseService { */ async createShippingMethod( optionId: string, - data: object, + data: Record, config: CreateShippingMethodDto ): Promise { return await this.atomicPhase_(async (manager) => { @@ -678,7 +678,7 @@ class ShippingOptionService extends TransactionBaseService { */ async getPrice_( option: ShippingOption, - data: object, + data: Record, cart: Cart | Order | undefined ): Promise { if (option.price_type === "calculated") { diff --git a/packages/medusa/src/types/fulfillment-provider.ts b/packages/medusa/src/types/fulfillment-provider.ts new file mode 100644 index 0000000000..641bcba459 --- /dev/null +++ b/packages/medusa/src/types/fulfillment-provider.ts @@ -0,0 +1,8 @@ +import { Return } from "../models" + +export type FulfillmentOptions = { + provider_id: string + options: Record[] +} + +export type CreateReturnType = Omit