feat(medusa): Convert FulfillmentService to TypeScript (#1962)
This commit is contained in:
7
.changeset/new-icons-chew.md
Normal file
7
.changeset/new-icons-chew.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"medusa-fulfillment-manual": patch
|
||||
"medusa-interfaces": patch
|
||||
"@medusajs/medusa": patch
|
||||
---
|
||||
|
||||
Convert FulfillmentService to TypeScript
|
||||
@@ -19,7 +19,7 @@ class ManualFulfillmentService extends FulfillmentService {
|
||||
]
|
||||
}
|
||||
|
||||
validateFulfillmentData(data, cart) {
|
||||
validateFulfillmentData(_, data, cart) {
|
||||
return data
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
191
packages/medusa/src/services/fulfillment-provider.ts
Normal file
191
packages/medusa/src/services/fulfillment-provider.ts
Normal file
@@ -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<void> {
|
||||
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<FulfillmentProvider[]> {
|
||||
const fpRepo = this.manager_.getCustomRepository(
|
||||
this.fulfillmentProviderRepository_
|
||||
)
|
||||
|
||||
return await fpRepo.find({})
|
||||
}
|
||||
|
||||
async listFulfillmentOptions(
|
||||
providerIds: string[]
|
||||
): Promise<FulfillmentOptions[]> {
|
||||
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<Fulfillment, "beforeInsert">
|
||||
): Promise<Record<string, unknown>> {
|
||||
const provider = this.retrieveProvider(method.shipping_option.provider_id)
|
||||
return provider.createFulfillment(
|
||||
method.data,
|
||||
items,
|
||||
order,
|
||||
fulfillment
|
||||
) as unknown as Record<string, unknown>
|
||||
}
|
||||
|
||||
async canCalculate(option: ShippingOption): Promise<boolean> {
|
||||
const provider = this.retrieveProvider(option.provider_id)
|
||||
return provider.canCalculate(option.data) as unknown as boolean
|
||||
}
|
||||
|
||||
async validateFulfillmentData(
|
||||
option: ShippingOption,
|
||||
data: Record<string, unknown>,
|
||||
cart: Cart | Record<string, unknown>
|
||||
): Promise<Record<string, unknown>> {
|
||||
const provider = this.retrieveProvider(option.provider_id)
|
||||
return provider.validateFulfillmentData(
|
||||
option.data,
|
||||
data,
|
||||
cart
|
||||
) as unknown as Record<string, unknown>
|
||||
}
|
||||
|
||||
async cancelFulfillment(fulfillment: Fulfillment): Promise<Fulfillment> {
|
||||
const provider = this.retrieveProvider(fulfillment.provider_id)
|
||||
return provider.cancelFulfillment(
|
||||
fulfillment.data
|
||||
) as unknown as Fulfillment
|
||||
}
|
||||
|
||||
async calculatePrice(
|
||||
option: ShippingOption,
|
||||
data: Record<string, unknown>,
|
||||
cart?: Order | Cart
|
||||
): Promise<number> {
|
||||
const provider = this.retrieveProvider(option.provider_id)
|
||||
return provider.calculatePrice(option.data, data, cart) as unknown as number
|
||||
}
|
||||
|
||||
async validateOption(option: ShippingOption): Promise<boolean> {
|
||||
const provider = this.retrieveProvider(option.provider_id)
|
||||
return provider.validateOption(option.data) as unknown as boolean
|
||||
}
|
||||
|
||||
async createReturn(
|
||||
returnOrder: CreateReturnType
|
||||
): Promise<Record<string, unknown>> {
|
||||
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<string, unknown>,
|
||||
documentType: "invoice" | "label"
|
||||
): Promise<any> {
|
||||
const provider = this.retrieveProvider(providerId)
|
||||
return provider.retrieveDocuments(fulfillmentData, documentType)
|
||||
}
|
||||
}
|
||||
|
||||
export default FulfillmentProviderService
|
||||
@@ -252,7 +252,7 @@ class ShippingOptionService extends TransactionBaseService {
|
||||
*/
|
||||
async createShippingMethod(
|
||||
optionId: string,
|
||||
data: object,
|
||||
data: Record<string, unknown>,
|
||||
config: CreateShippingMethodDto
|
||||
): Promise<ShippingMethod> {
|
||||
return await this.atomicPhase_(async (manager) => {
|
||||
@@ -678,7 +678,7 @@ class ShippingOptionService extends TransactionBaseService {
|
||||
*/
|
||||
async getPrice_(
|
||||
option: ShippingOption,
|
||||
data: object,
|
||||
data: Record<string, unknown>,
|
||||
cart: Cart | Order | undefined
|
||||
): Promise<number> {
|
||||
if (option.price_type === "calculated") {
|
||||
|
||||
8
packages/medusa/src/types/fulfillment-provider.ts
Normal file
8
packages/medusa/src/types/fulfillment-provider.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { Return } from "../models"
|
||||
|
||||
export type FulfillmentOptions = {
|
||||
provider_id: string
|
||||
options: Record<string, unknown>[]
|
||||
}
|
||||
|
||||
export type CreateReturnType = Omit<Return, "beforeInsert">
|
||||
Reference in New Issue
Block a user