feat(medusa): Convert FulfillmentService to TypeScript (#1962)
This commit is contained in:
@@ -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
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ class BaseFulfillmentService extends BaseService {
|
|||||||
* to create shipping options in Medusa that can be chosen between by the
|
* to create shipping options in Medusa that can be chosen between by the
|
||||||
* customer.
|
* 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
|
* 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
|
* 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
|
* fulfillment provider to enforce that the correct data is being sent
|
||||||
* through.
|
* through.
|
||||||
|
* @param {object} optionData - the data to validate
|
||||||
* @param {object} data - 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
|
* @return {object} the data to populate `cart.shipping_methods.$.data` this
|
||||||
* is usually important for future actions like generating shipping labels
|
* 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")
|
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.
|
* 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")
|
throw Error("calculatePrice must be overridden by the child class")
|
||||||
}
|
}
|
||||||
|
|
||||||
createFulfillment() {
|
createFulfillment(data, items, order, fulfillment) {
|
||||||
throw Error("createOrder must be overridden by the child class")
|
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) {
|
getShipmentDocuments(data) {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
retrieveDocuments(fulfillmentData, documentType) {
|
||||||
|
throw Error("retrieveDocuments must be overridden by the child class")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BaseFulfillmentService
|
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
|
|
||||||
@@ -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(
|
async createShippingMethod(
|
||||||
optionId: string,
|
optionId: string,
|
||||||
data: object,
|
data: Record<string, unknown>,
|
||||||
config: CreateShippingMethodDto
|
config: CreateShippingMethodDto
|
||||||
): Promise<ShippingMethod> {
|
): Promise<ShippingMethod> {
|
||||||
return await this.atomicPhase_(async (manager) => {
|
return await this.atomicPhase_(async (manager) => {
|
||||||
@@ -678,7 +678,7 @@ class ShippingOptionService extends TransactionBaseService {
|
|||||||
*/
|
*/
|
||||||
async getPrice_(
|
async getPrice_(
|
||||||
option: ShippingOption,
|
option: ShippingOption,
|
||||||
data: object,
|
data: Record<string, unknown>,
|
||||||
cart: Cart | Order | undefined
|
cart: Cart | Order | undefined
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
if (option.price_type === "calculated") {
|
if (option.price_type === "calculated") {
|
||||||
|
|||||||
@@ -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