fix(medusa): Draft order inventory management (#3040)

* reserve quantities when creating a draft order

* only adjust quantities if a location is provided on a fulfillment

* initial fix for fulfillments

* changeset

* add check for inventory service when cancelling fulfillment

* update test-request with module loaders

* remove unused variable
This commit is contained in:
Philip Korsholm
2023-01-17 16:14:58 +01:00
committed by GitHub
parent cb1ec0076b
commit 76d1752310
5 changed files with 95 additions and 40 deletions
@@ -3,6 +3,7 @@ import {
DraftOrderService,
OrderService,
PaymentProviderService,
ProductVariantInventoryService,
} from "../../../../services"
import {
defaultAdminOrdersFields as defaultOrderFields,
@@ -10,6 +11,8 @@ import {
} from "../orders/index"
import { EntityManager } from "typeorm"
import { Order } from "../../../../models"
import { MedusaError } from "medusa-core-utils"
/**
* @oas [post] /draft-orders/{id}/pay
@@ -71,43 +74,90 @@ export default async (req, res) => {
)
const orderService: OrderService = req.scope.resolve("orderService")
const cartService: CartService = req.scope.resolve("cartService")
const productVariantInventoryService: ProductVariantInventoryService =
req.scope.resolve("productVariantInventoryService")
const entityManager: EntityManager = req.scope.resolve("manager")
let result
await entityManager.transaction(async (manager) => {
const draftOrder = await draftOrderService
.withTransaction(manager)
.retrieve(id)
const order = await entityManager.transaction(async (manager) => {
const draftOrderServiceTx = draftOrderService.withTransaction(manager)
const orderServiceTx = orderService.withTransaction(manager)
const cartServiceTx = cartService.withTransaction(manager)
const cart = await cartService
.withTransaction(manager)
.retrieveWithTotals(draftOrder.cart_id)
const productVariantInventoryServiceTx =
productVariantInventoryService.withTransaction(manager)
const draftOrder = await draftOrderServiceTx.retrieve(id)
const cart = await cartServiceTx.retrieveWithTotals(draftOrder.cart_id)
await paymentProviderService
.withTransaction(manager)
.createSession("system", cart)
await cartService
await cartServiceTx.setPaymentSession(cart.id, "system")
await cartServiceTx.createTaxLines(cart.id)
await cartServiceTx.authorizePayment(cart.id)
let order = await orderServiceTx.createFromCart(cart.id)
await draftOrderServiceTx.registerCartCompletion(draftOrder.id, order.id)
await orderServiceTx.capturePayment(order.id)
order = await orderService
.withTransaction(manager)
.setPaymentSession(cart.id, "system")
.retrieveWithTotals(order.id, {
relations: defaultOrderRelations,
select: defaultOrderFields,
})
await cartService.withTransaction(manager).createTaxLines(cart.id)
await reserveQuantityForDraftOrder(order, {
productVariantInventoryService: productVariantInventoryServiceTx,
})
await cartService.withTransaction(manager).authorizePayment(cart.id)
result = await orderService.withTransaction(manager).createFromCart(cart.id)
await draftOrderService
.withTransaction(manager)
.registerCartCompletion(draftOrder.id, result.id)
await orderService.withTransaction(manager).capturePayment(result.id)
})
const order = await orderService.retrieveWithTotals(result.id, {
relations: defaultOrderRelations,
select: defaultOrderFields,
return order
})
res.status(200).json({ order })
}
export const reserveQuantityForDraftOrder = async (
order: Order,
context: {
productVariantInventoryService: ProductVariantInventoryService
locationId?: string
}
) => {
const { productVariantInventoryService, locationId } = context
await Promise.all(
order.items.map(async (item) => {
if (item.variant_id) {
const inventoryConfirmed =
await productVariantInventoryService.confirmInventory(
item.variant_id,
item.quantity,
{ salesChannelId: order.sales_channel_id }
)
if (!inventoryConfirmed) {
throw new MedusaError(
MedusaError.Types.NOT_ALLOWED,
`Variant with id: ${item.variant_id} does not have the required inventory`,
MedusaError.Codes.INSUFFICIENT_INVENTORY
)
}
await productVariantInventoryService.reserveQuantity(
item.variant_id,
item.quantity,
{
lineItemId: item.id,
salesChannelId: order.sales_channel_id,
}
)
}
})
)
}