chore(inventory, core-flows): big number support (#8204)
This commit is contained in:
1 parent
c307972a99
commit
fb29b958fa
27 files changed
+362
-217
No files matched your search
@@ -292,16 +292,6 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should fail to update the location level to negative quantity", async () => {
|
it("should fail to update the location level to negative quantity", async () => {
|
||||||
await api.post(
|
|
||||||
`/admin/inventory-items/${inventoryItem1.id}/location-levels`,
|
|
||||||
{
|
|
||||||
location_id: stockLocation1.id,
|
|
||||||
stocked_quantity: 17,
|
|
||||||
incoming_quantity: 2,
|
|
||||||
},
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
const res = await api
|
const res = await api
|
||||||
.post(
|
.post(
|
||||||
`/admin/inventory-items/${inventoryItem1.id}/location-levels/${stockLocation1.id}`,
|
`/admin/inventory-items/${inventoryItem1.id}/location-levels/${stockLocation1.id}`,
|
||||||
@@ -751,12 +741,6 @@ medusaIntegrationTestRunner({
|
|||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
|
|
||||||
await api.post(
|
|
||||||
`/admin/inventory-items/${inventoryItem1.id}/location-levels`,
|
|
||||||
{ location_id: stockLocation1.id },
|
|
||||||
adminHeaders
|
|
||||||
)
|
|
||||||
|
|
||||||
await api.post(
|
await api.post(
|
||||||
`/admin/reservations`,
|
`/admin/reservations`,
|
||||||
{
|
{
|
||||||
@@ -784,7 +768,7 @@ medusaIntegrationTestRunner({
|
|||||||
adminHeaders
|
adminHeaders
|
||||||
)
|
)
|
||||||
).data
|
).data
|
||||||
expect(levelsResponse.count).toEqual(2)
|
expect(levelsResponse.count).toEqual(1)
|
||||||
|
|
||||||
const res = await api.delete(
|
const res = await api.delete(
|
||||||
`/admin/inventory-items/${inventoryItem1.id}`,
|
`/admin/inventory-items/${inventoryItem1.id}`,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import {
|
|||||||
StockLocationDTO,
|
StockLocationDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
|
BigNumber,
|
||||||
ContainerRegistrationKeys,
|
ContainerRegistrationKeys,
|
||||||
ModuleRegistrationName,
|
ModuleRegistrationName,
|
||||||
Modules,
|
Modules,
|
||||||
@@ -345,6 +346,10 @@ medusaIntegrationTestRunner({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should create a order fulfillment and cancel it", async () => {
|
it("should create a order fulfillment and cancel it", async () => {
|
||||||
|
const inventoryModule = container.resolve(
|
||||||
|
ModuleRegistrationName.INVENTORY
|
||||||
|
)
|
||||||
|
|
||||||
const order = await createOrderFixture({ container, product, location })
|
const order = await createOrderFixture({ container, product, location })
|
||||||
|
|
||||||
// Create a fulfillment
|
// Create a fulfillment
|
||||||
@@ -389,9 +394,6 @@ medusaIntegrationTestRunner({
|
|||||||
expect(orderFulfill.fulfillments).toHaveLength(1)
|
expect(orderFulfill.fulfillments).toHaveLength(1)
|
||||||
expect(orderFulfill.items[0].detail.fulfilled_quantity).toEqual(1)
|
expect(orderFulfill.items[0].detail.fulfilled_quantity).toEqual(1)
|
||||||
|
|
||||||
const inventoryModule = container.resolve(
|
|
||||||
ModuleRegistrationName.INVENTORY
|
|
||||||
)
|
|
||||||
const reservation = await inventoryModule.listReservationItems({
|
const reservation = await inventoryModule.listReservationItems({
|
||||||
line_item_id: order.items![0].id,
|
line_item_id: order.items![0].id,
|
||||||
})
|
})
|
||||||
@@ -401,7 +403,7 @@ medusaIntegrationTestRunner({
|
|||||||
inventoryItem.id,
|
inventoryItem.id,
|
||||||
[location.id]
|
[location.id]
|
||||||
)
|
)
|
||||||
expect(stockAvailability).toEqual(1)
|
expect(stockAvailability).toEqual(new BigNumber(1))
|
||||||
|
|
||||||
// Cancel the fulfillment
|
// Cancel the fulfillment
|
||||||
const cancelFulfillmentData: OrderWorkflow.CancelOrderFulfillmentWorkflowInput =
|
const cancelFulfillmentData: OrderWorkflow.CancelOrderFulfillmentWorkflowInput =
|
||||||
@@ -443,7 +445,7 @@ medusaIntegrationTestRunner({
|
|||||||
await inventoryModule.retrieveStockedQuantity(inventoryItem.id, [
|
await inventoryModule.retrieveStockedQuantity(inventoryItem.id, [
|
||||||
location.id,
|
location.id,
|
||||||
])
|
])
|
||||||
expect(stockAvailabilityAfterCancelled).toEqual(2)
|
expect(stockAvailabilityAfterCancelled.valueOf()).toEqual(2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should revert an order fulfillment when it fails and recreate it when tried again", async () => {
|
it("should revert an order fulfillment when it fails and recreate it when tried again", async () => {
|
||||||
@@ -518,7 +520,7 @@ medusaIntegrationTestRunner({
|
|||||||
inventoryItem.id,
|
inventoryItem.id,
|
||||||
[location.id]
|
[location.id]
|
||||||
)
|
)
|
||||||
expect(stockAvailability).toEqual(1)
|
expect(stockAvailability.valueOf()).toEqual(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -418,7 +418,7 @@ medusaIntegrationTestRunner({
|
|||||||
inventoryItem.id,
|
inventoryItem.id,
|
||||||
[location.id]
|
[location.id]
|
||||||
)
|
)
|
||||||
expect(stockAvailability).toEqual(1)
|
expect(stockAvailability.valueOf()).toEqual(1)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
Whitespace-only changes.
@@ -1,7 +1,7 @@
|
|||||||
import { IInventoryService, InventoryTypes } from "@medusajs/types"
|
import { IInventoryService, InventoryTypes } from "@medusajs/types"
|
||||||
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
import { StepResponse, createStep } from "@medusajs/workflows-sdk"
|
||||||
|
|
||||||
import { ModuleRegistrationName } from "@medusajs/utils"
|
import { MathBN, ModuleRegistrationName } from "@medusajs/utils"
|
||||||
|
|
||||||
export const adjustInventoryLevelsStepId = "adjust-inventory-levels-step"
|
export const adjustInventoryLevelsStepId = "adjust-inventory-levels-step"
|
||||||
export const adjustInventoryLevelsStep = createStep(
|
export const adjustInventoryLevelsStep = createStep(
|
||||||
@@ -30,7 +30,7 @@ export const adjustInventoryLevelsStep = createStep(
|
|||||||
input.map((item) => {
|
input.map((item) => {
|
||||||
return {
|
return {
|
||||||
...item,
|
...item,
|
||||||
adjustment: item.adjustment * -1,
|
adjustment: MathBN.mult(item.adjustment, -1),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,11 +1,16 @@
|
|||||||
import { FulfillmentDTO, OrderDTO, OrderWorkflow } from "@medusajs/types"
|
import {
|
||||||
|
BigNumberInput,
|
||||||
|
FulfillmentDTO,
|
||||||
|
OrderDTO,
|
||||||
|
OrderWorkflow,
|
||||||
|
} from "@medusajs/types"
|
||||||
import { MedusaError, Modules } from "@medusajs/utils"
|
import { MedusaError, Modules } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
|
WorkflowData,
|
||||||
createStep,
|
createStep,
|
||||||
createWorkflow,
|
createWorkflow,
|
||||||
parallelize,
|
parallelize,
|
||||||
transform,
|
transform,
|
||||||
WorkflowData,
|
|
||||||
} from "@medusajs/workflows-sdk"
|
} from "@medusajs/workflows-sdk"
|
||||||
import { useRemoteQueryStep } from "../../common"
|
import { useRemoteQueryStep } from "../../common"
|
||||||
import { cancelFulfillmentWorkflow } from "../../fulfillment"
|
import { cancelFulfillmentWorkflow } from "../../fulfillment"
|
||||||
@@ -82,7 +87,7 @@ function prepareInventoryUpdate({
|
|||||||
const inventoryAdjustment: {
|
const inventoryAdjustment: {
|
||||||
inventory_item_id: string
|
inventory_item_id: string
|
||||||
location_id: string
|
location_id: string
|
||||||
adjustment: number // TODO: BigNumberInput
|
adjustment: BigNumberInput
|
||||||
}[] = []
|
}[] = []
|
||||||
|
|
||||||
for (const item of fulfillment.items) {
|
for (const item of fulfillment.items) {
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
import {
|
import {
|
||||||
|
BigNumberInput,
|
||||||
FulfillmentDTO,
|
FulfillmentDTO,
|
||||||
FulfillmentWorkflow,
|
FulfillmentWorkflow,
|
||||||
OrderDTO,
|
OrderDTO,
|
||||||
OrderWorkflow,
|
OrderWorkflow,
|
||||||
ReservationItemDTO,
|
ReservationItemDTO,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { MedusaError, Modules } from "@medusajs/utils"
|
import { MathBN, MedusaError, Modules } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
WorkflowData,
|
WorkflowData,
|
||||||
createStep,
|
createStep,
|
||||||
@@ -68,6 +69,7 @@ function prepareFulfillmentData({
|
|||||||
order,
|
order,
|
||||||
input,
|
input,
|
||||||
shippingOption,
|
shippingOption,
|
||||||
|
shippingMethod,
|
||||||
reservations,
|
reservations,
|
||||||
}: {
|
}: {
|
||||||
order: OrderDTO
|
order: OrderDTO
|
||||||
@@ -77,6 +79,7 @@ function prepareFulfillmentData({
|
|||||||
provider_id: string
|
provider_id: string
|
||||||
service_zone: { fulfillment_set: { location?: { id: string } } }
|
service_zone: { fulfillment_set: { location?: { id: string } } }
|
||||||
}
|
}
|
||||||
|
shippingMethod: { data?: Record<string, unknown> | null }
|
||||||
reservations: ReservationItemDTO[]
|
reservations: ReservationItemDTO[]
|
||||||
}) {
|
}) {
|
||||||
const inputItems = input.items
|
const inputItems = input.items
|
||||||
@@ -120,8 +123,9 @@ function prepareFulfillmentData({
|
|||||||
location_id: locationId,
|
location_id: locationId,
|
||||||
provider_id: shippingOption.provider_id,
|
provider_id: shippingOption.provider_id,
|
||||||
shipping_option_id: shippingOption.id,
|
shipping_option_id: shippingOption.id,
|
||||||
|
data: shippingMethod.data,
|
||||||
items: fulfillmentItems,
|
items: fulfillmentItems,
|
||||||
labels: [] as FulfillmentWorkflow.CreateFulfillmentLabelWorkflowDTO[], // TODO: shipping labels
|
labels: input.labels ?? [],
|
||||||
delivery_address: shippingAddress as any,
|
delivery_address: shippingAddress as any,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -136,13 +140,13 @@ function prepareInventoryUpdate({ reservations, order, input, inputItemsMap }) {
|
|||||||
const toDelete: string[] = []
|
const toDelete: string[] = []
|
||||||
const toUpdate: {
|
const toUpdate: {
|
||||||
id: string
|
id: string
|
||||||
quantity: number // TODO: BigNumberInput
|
quantity: BigNumberInput
|
||||||
location_id: string
|
location_id: string
|
||||||
}[] = []
|
}[] = []
|
||||||
const inventoryAdjustment: {
|
const inventoryAdjustment: {
|
||||||
inventory_item_id: string
|
inventory_item_id: string
|
||||||
location_id: string
|
location_id: string
|
||||||
adjustment: number // TODO: BigNumberInput
|
adjustment: BigNumberInput
|
||||||
}[] = []
|
}[] = []
|
||||||
|
|
||||||
for (const item of order.items) {
|
for (const item of order.items) {
|
||||||
@@ -163,7 +167,7 @@ function prepareInventoryUpdate({ reservations, order, input, inputItemsMap }) {
|
|||||||
inventoryAdjustment.push({
|
inventoryAdjustment.push({
|
||||||
inventory_item_id: reservation.inventory_item_id,
|
inventory_item_id: reservation.inventory_item_id,
|
||||||
location_id: input.location_id ?? reservation.location_id,
|
location_id: input.location_id ?? reservation.location_id,
|
||||||
adjustment: -item.quantity, // TODO: MathBN.mul(-1, item.quantity)
|
adjustment: MathBN.mult(item.quantity, -1),
|
||||||
})
|
})
|
||||||
|
|
||||||
if (quantity === 0) {
|
if (quantity === 0) {
|
||||||
@@ -201,6 +205,7 @@ export const createOrderFulfillmentWorkflow = createWorkflow(
|
|||||||
"items.variant.manage_inventory",
|
"items.variant.manage_inventory",
|
||||||
"shipping_address.*",
|
"shipping_address.*",
|
||||||
"shipping_methods.shipping_option_id", // TODO: which shipping method to use when multiple?
|
"shipping_methods.shipping_option_id", // TODO: which shipping method to use when multiple?
|
||||||
|
"shipping_methods.data",
|
||||||
],
|
],
|
||||||
variables: { id: input.order_id },
|
variables: { id: input.order_id },
|
||||||
list: false,
|
list: false,
|
||||||
@@ -216,6 +221,10 @@ export const createOrderFulfillmentWorkflow = createWorkflow(
|
|||||||
}, {})
|
}, {})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const shippingMethod = transform(order, (data) => {
|
||||||
|
return { data: data.shipping_methods?.[0].data }
|
||||||
|
})
|
||||||
|
|
||||||
const shippingOptionId = transform(order, (data) => {
|
const shippingOptionId = transform(order, (data) => {
|
||||||
return data.shipping_methods?.[0]?.shipping_option_id
|
return data.shipping_methods?.[0]?.shipping_option_id
|
||||||
})
|
})
|
||||||
@@ -250,7 +259,7 @@ export const createOrderFulfillmentWorkflow = createWorkflow(
|
|||||||
}).config({ name: "get-reservations" })
|
}).config({ name: "get-reservations" })
|
||||||
|
|
||||||
const fulfillmentData = transform(
|
const fulfillmentData = transform(
|
||||||
{ order, input, shippingOption, reservations },
|
{ order, input, shippingOption, shippingMethod, reservations },
|
||||||
prepareFulfillmentData
|
prepareFulfillmentData
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -278,12 +287,12 @@ export const createOrderFulfillmentWorkflow = createWorkflow(
|
|||||||
prepareInventoryUpdate
|
prepareInventoryUpdate
|
||||||
)
|
)
|
||||||
|
|
||||||
|
adjustInventoryLevelsStep(inventoryAdjustment)
|
||||||
parallelize(
|
parallelize(
|
||||||
registerOrderFulfillmentStep(registerOrderFulfillmentData),
|
registerOrderFulfillmentStep(registerOrderFulfillmentData),
|
||||||
createRemoteLinkStep(link),
|
createRemoteLinkStep(link),
|
||||||
updateReservationsStep(toUpdate),
|
updateReservationsStep(toUpdate),
|
||||||
deleteReservationsStep(toDelete),
|
deleteReservationsStep(toDelete)
|
||||||
adjustInventoryLevelsStep(inventoryAdjustment)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// trigger event OrderModuleService.Events.FULFILLMENT_CREATED
|
// trigger event OrderModuleService.Events.FULFILLMENT_CREATED
|
||||||
|
|||||||
@@ -92,7 +92,7 @@ export const createOrderShipmentWorkflow = createWorkflow(
|
|||||||
const fulfillmentData = transform({ input }, ({ input }) => {
|
const fulfillmentData = transform({ input }, ({ input }) => {
|
||||||
return {
|
return {
|
||||||
id: input.fulfillment_id,
|
id: input.fulfillment_id,
|
||||||
labels: input.labels,
|
labels: input.labels ?? [],
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ function prepareFulfillmentData({
|
|||||||
provider_id: returnShippingOption.provider_id,
|
provider_id: returnShippingOption.provider_id,
|
||||||
shipping_option_id: input.return_shipping?.option_id,
|
shipping_option_id: input.return_shipping?.option_id,
|
||||||
items: fulfillmentItems,
|
items: fulfillmentItems,
|
||||||
labels: [] as FulfillmentWorkflow.CreateFulfillmentLabelWorkflowDTO[],
|
labels: input.return_shipping?.labels ?? [],
|
||||||
delivery_address: order.shipping_address ?? ({} as any), // TODO: should it be the stock location address?
|
delivery_address: order.shipping_address ?? ({} as any), // TODO: should it be the stock location address?
|
||||||
order: order,
|
order: order,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
NumericalComparisonOperator,
|
NumericalComparisonOperator,
|
||||||
StringComparisonOperator,
|
StringComparisonOperator,
|
||||||
} from "../../common"
|
} from "../../common"
|
||||||
|
import { BigNumberInput } from "../../totals/big-number"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The reservation item details.
|
* The reservation item details.
|
||||||
@@ -25,7 +26,7 @@ export interface ReservationItemDTO {
|
|||||||
/**
|
/**
|
||||||
* The quantity of the reservation item.
|
* The quantity of the reservation item.
|
||||||
*/
|
*/
|
||||||
quantity: number
|
quantity: BigNumberInput
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The associated line item's ID.
|
* The associated line item's ID.
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { BigNumberInput } from "../../totals/big-number"
|
||||||
|
|
||||||
export interface CreateInventoryLevelInput {
|
export interface CreateInventoryLevelInput {
|
||||||
/**
|
/**
|
||||||
* The ID of the associated inventory item.
|
* The ID of the associated inventory item.
|
||||||
@@ -66,5 +68,5 @@ export type BulkAdjustInventoryLevelInput = {
|
|||||||
/**
|
/**
|
||||||
* The quantity to adjust the inventory level by.
|
* The quantity to adjust the inventory level by.
|
||||||
*/
|
*/
|
||||||
adjustment: number // TODO: BigNumberInput
|
adjustment: BigNumberInput
|
||||||
} & UpdateInventoryLevelInput
|
} & UpdateInventoryLevelInput
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { BigNumberInput } from "../../totals"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @interface
|
* @interface
|
||||||
*
|
*
|
||||||
@@ -8,7 +10,7 @@ export interface UpdateReservationItemInput {
|
|||||||
/**
|
/**
|
||||||
* The reserved quantity.
|
* The reserved quantity.
|
||||||
*/
|
*/
|
||||||
quantity?: number
|
quantity?: BigNumberInput
|
||||||
/**
|
/**
|
||||||
* The ID of the associated location.
|
* The ID of the associated location.
|
||||||
*/
|
*/
|
||||||
@@ -48,7 +50,7 @@ export interface CreateReservationItemInput {
|
|||||||
/**
|
/**
|
||||||
* The reserved quantity.
|
* The reserved quantity.
|
||||||
*/
|
*/
|
||||||
quantity: number
|
quantity: BigNumberInput
|
||||||
/**
|
/**
|
||||||
* Allow backorder of the item. If true, it won't check inventory levels before reserving it.
|
* Allow backorder of the item. If true, it won't check inventory levels before reserving it.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { RestoreReturn, SoftDeleteReturn } from "../dal"
|
|||||||
import { FindConfig } from "../common"
|
import { FindConfig } from "../common"
|
||||||
import { IModuleService } from "../modules-sdk"
|
import { IModuleService } from "../modules-sdk"
|
||||||
import { Context } from "../shared-context"
|
import { Context } from "../shared-context"
|
||||||
|
import { BigNumberInput, IBigNumber } from "../totals"
|
||||||
import {
|
import {
|
||||||
FilterableInventoryItemProps,
|
FilterableInventoryItemProps,
|
||||||
FilterableInventoryLevelProps,
|
FilterableInventoryLevelProps,
|
||||||
@@ -1044,7 +1045,7 @@ export interface IInventoryService extends IModuleService {
|
|||||||
data: {
|
data: {
|
||||||
inventoryItemId: string
|
inventoryItemId: string
|
||||||
locationId: string
|
locationId: string
|
||||||
adjustment: number
|
adjustment: BigNumberInput
|
||||||
}[],
|
}[],
|
||||||
context?: Context
|
context?: Context
|
||||||
): Promise<InventoryLevelDTO[]>
|
): Promise<InventoryLevelDTO[]>
|
||||||
@@ -1052,7 +1053,7 @@ export interface IInventoryService extends IModuleService {
|
|||||||
adjustInventory(
|
adjustInventory(
|
||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationId: string,
|
locationId: string,
|
||||||
adjustment: number,
|
adjustment: BigNumberInput,
|
||||||
context?: Context
|
context?: Context
|
||||||
): Promise<InventoryLevelDTO>
|
): Promise<InventoryLevelDTO>
|
||||||
|
|
||||||
@@ -1076,7 +1077,7 @@ export interface IInventoryService extends IModuleService {
|
|||||||
confirmInventory(
|
confirmInventory(
|
||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[],
|
locationIds: string[],
|
||||||
quantity: number,
|
quantity: BigNumberInput,
|
||||||
context?: Context
|
context?: Context
|
||||||
): Promise<boolean>
|
): Promise<boolean>
|
||||||
|
|
||||||
@@ -1086,7 +1087,7 @@ export interface IInventoryService extends IModuleService {
|
|||||||
* @param {string} inventoryItemId - The inventory item's ID.
|
* @param {string} inventoryItemId - The inventory item's ID.
|
||||||
* @param {string[]} locationIds - The locations' IDs.
|
* @param {string[]} locationIds - The locations' IDs.
|
||||||
* @param {Context} context - A context used to share resources, such as transaction manager, between the application and the module.
|
* @param {Context} context - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
* @returns {Promise<number>} The available quantity of the item.
|
* @returns {Promise<BigNumber>} The available quantity of the item.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const availableQuantity =
|
* const availableQuantity =
|
||||||
@@ -1099,7 +1100,7 @@ export interface IInventoryService extends IModuleService {
|
|||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[],
|
locationIds: string[],
|
||||||
context?: Context
|
context?: Context
|
||||||
): Promise<number>
|
): Promise<IBigNumber>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method retrieves the stocked quantity of an inventory item in the specified location.
|
* This method retrieves the stocked quantity of an inventory item in the specified location.
|
||||||
@@ -1107,7 +1108,7 @@ export interface IInventoryService extends IModuleService {
|
|||||||
* @param {string} inventoryItemId - The inventory item's ID.
|
* @param {string} inventoryItemId - The inventory item's ID.
|
||||||
* @param {string[]} locationIds - The locations' IDs.
|
* @param {string[]} locationIds - The locations' IDs.
|
||||||
* @param {Context} context - A context used to share resources, such as transaction manager, between the application and the module.
|
* @param {Context} context - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
* @returns {Promise<number>} The stocked quantity of the item.
|
* @returns {Promise<BigNumber>} The stocked quantity of the item.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const stockedQuantity =
|
* const stockedQuantity =
|
||||||
@@ -1120,7 +1121,7 @@ export interface IInventoryService extends IModuleService {
|
|||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[],
|
locationIds: string[],
|
||||||
context?: Context
|
context?: Context
|
||||||
): Promise<number>
|
): Promise<IBigNumber>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method retrieves the reserved quantity of an inventory item in the specified location.
|
* This method retrieves the reserved quantity of an inventory item in the specified location.
|
||||||
@@ -1128,7 +1129,7 @@ export interface IInventoryService extends IModuleService {
|
|||||||
* @param {string} inventoryItemId - The inventory item's ID.
|
* @param {string} inventoryItemId - The inventory item's ID.
|
||||||
* @param {string[]} locationIds - The locations' IDs.
|
* @param {string[]} locationIds - The locations' IDs.
|
||||||
* @param {Context} context - A context used to share resources, such as transaction manager, between the application and the module.
|
* @param {Context} context - A context used to share resources, such as transaction manager, between the application and the module.
|
||||||
* @returns {Promise<number>} The reserved quantity of the item.
|
* @returns {Promise<BigNumber>} The reserved quantity of the item.
|
||||||
*
|
*
|
||||||
* @example
|
* @example
|
||||||
* const reservedQuantity =
|
* const reservedQuantity =
|
||||||
@@ -1141,5 +1142,5 @@ export interface IInventoryService extends IModuleService {
|
|||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[],
|
locationIds: string[],
|
||||||
context?: Context
|
context?: Context
|
||||||
): Promise<number>
|
): Promise<IBigNumber>
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,24 @@
|
|||||||
import BigNumberJS from "bignumber.js"
|
import BigNumberJS from "bignumber.js"
|
||||||
|
|
||||||
|
export interface IBigNumber {
|
||||||
|
numeric: number
|
||||||
|
raw?: BigNumberRawValue
|
||||||
|
bigNumber?: BigNumberJS
|
||||||
|
|
||||||
|
toJSON(): number
|
||||||
|
valueOf(): number
|
||||||
|
}
|
||||||
|
|
||||||
export type BigNumberRawValue = {
|
export type BigNumberRawValue = {
|
||||||
value: string | number
|
value: string | number
|
||||||
[key: string]: unknown
|
[key: string]: unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BigNumberInput = BigNumberRawValue | number | string | BigNumberJS
|
export type BigNumberInput =
|
||||||
|
| BigNumberRawValue
|
||||||
|
| number
|
||||||
|
| string
|
||||||
|
| BigNumberJS
|
||||||
|
| IBigNumber
|
||||||
|
|
||||||
export type BigNumberValue = BigNumberJS | number | string
|
export type BigNumberValue = BigNumberJS | number | string | IBigNumber
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BigNumberInput } from "../../totals"
|
import { BigNumberInput } from "../../totals"
|
||||||
|
import { CreateFulfillmentLabelWorkflowDTO } from "../fulfillment/create-fulfillment"
|
||||||
|
|
||||||
interface CreateOrderFulfillmentItem {
|
interface CreateOrderFulfillmentItem {
|
||||||
id: string
|
id: string
|
||||||
@@ -9,6 +10,7 @@ export interface CreateOrderFulfillmentWorkflowInput {
|
|||||||
order_id: string
|
order_id: string
|
||||||
created_by?: string // The id of the authenticated user
|
created_by?: string // The id of the authenticated user
|
||||||
items: CreateOrderFulfillmentItem[]
|
items: CreateOrderFulfillmentItem[]
|
||||||
|
labels?: CreateFulfillmentLabelWorkflowDTO[]
|
||||||
no_notification?: boolean
|
no_notification?: boolean
|
||||||
location_id?: string | null
|
location_id?: string | null
|
||||||
metadata?: Record<string, any> | null
|
metadata?: Record<string, any> | null
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { BigNumberInput } from "../../totals"
|
import { BigNumberInput } from "../../totals"
|
||||||
|
import { CreateFulfillmentLabelWorkflowDTO } from "../fulfillment/create-fulfillment"
|
||||||
|
|
||||||
export interface CreateReturnItem {
|
export interface CreateReturnItem {
|
||||||
id: string
|
id: string
|
||||||
@@ -16,6 +17,7 @@ export interface CreateOrderReturnWorkflowInput {
|
|||||||
return_shipping?: {
|
return_shipping?: {
|
||||||
option_id: string
|
option_id: string
|
||||||
price?: number
|
price?: number
|
||||||
|
labels?: CreateFulfillmentLabelWorkflowDTO[]
|
||||||
}
|
}
|
||||||
note?: string | null
|
note?: string | null
|
||||||
receive_now?: boolean
|
receive_now?: boolean
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export interface CreateOrderShipmentWorkflowInput {
|
|||||||
fulfillment_id: string
|
fulfillment_id: string
|
||||||
created_by?: string // The id of the authenticated user
|
created_by?: string // The id of the authenticated user
|
||||||
items: CreateOrderShipmentItem[]
|
items: CreateOrderShipmentItem[]
|
||||||
labels: CreateFulfillmentLabelWorkflowDTO[]
|
labels?: CreateFulfillmentLabelWorkflowDTO[]
|
||||||
no_notification?: boolean
|
no_notification?: boolean
|
||||||
metadata?: MetadataType
|
metadata?: MetadataType
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { BigNumberInput, BigNumberRawValue } from "@medusajs/types"
|
import { BigNumberInput, BigNumberRawValue, IBigNumber } from "@medusajs/types"
|
||||||
import { BigNumber as BigNumberJS } from "bignumber.js"
|
import { BigNumber as BigNumberJS } from "bignumber.js"
|
||||||
import { isBigNumber, isString } from "../common"
|
import { isBigNumber, isString } from "../common"
|
||||||
|
|
||||||
export class BigNumber {
|
export class BigNumber implements IBigNumber {
|
||||||
static DEFAULT_PRECISION = 20
|
static DEFAULT_PRECISION = 20
|
||||||
|
|
||||||
private numeric_: number
|
private numeric_: number
|
||||||
@@ -110,7 +110,7 @@ export class BigNumber {
|
|||||||
this.bignumber_ = newValue.bignumber_
|
this.bignumber_ = newValue.bignumber_
|
||||||
}
|
}
|
||||||
|
|
||||||
toJSON() {
|
toJSON(): number {
|
||||||
return this.bignumber_
|
return this.bignumber_
|
||||||
? this.bignumber_?.toNumber()
|
? this.bignumber_?.toNumber()
|
||||||
: this.raw_
|
: this.raw_
|
||||||
@@ -118,7 +118,7 @@ export class BigNumber {
|
|||||||
: this.numeric_
|
: this.numeric_
|
||||||
}
|
}
|
||||||
|
|
||||||
valueOf() {
|
valueOf(): number {
|
||||||
return this.numeric_
|
return this.numeric_
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+6
-6
@@ -1,6 +1,6 @@
|
|||||||
import { IInventoryService, InventoryItemDTO } from "@medusajs/types"
|
import { IInventoryService, InventoryItemDTO } from "@medusajs/types"
|
||||||
|
import { BigNumber, Module, Modules } from "@medusajs/utils"
|
||||||
import { moduleIntegrationTestRunner } from "medusa-test-utils"
|
import { moduleIntegrationTestRunner } from "medusa-test-utils"
|
||||||
import { Module, Modules } from "@medusajs/utils"
|
|
||||||
import { InventoryModuleService } from "../../src/services"
|
import { InventoryModuleService } from "../../src/services"
|
||||||
|
|
||||||
jest.setTimeout(100000)
|
jest.setTimeout(100000)
|
||||||
@@ -882,7 +882,7 @@ moduleIntegrationTestRunner<IInventoryService>({
|
|||||||
["location-1", "location-2"]
|
["location-1", "location-2"]
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(level).toEqual(6)
|
expect(level).toEqual(new BigNumber(6))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -930,7 +930,7 @@ moduleIntegrationTestRunner<IInventoryService>({
|
|||||||
["location-1", "location-2"]
|
["location-1", "location-2"]
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(stockedQuantity).toEqual(8)
|
expect(stockedQuantity.valueOf()).toEqual(8)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -991,12 +991,12 @@ moduleIntegrationTestRunner<IInventoryService>({
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("retrieves reserved quantity", async () => {
|
it("retrieves reserved quantity", async () => {
|
||||||
const reservedQuantity = await service.retrieveReservedQuantity(
|
const reservedQuantity = (await service.retrieveReservedQuantity(
|
||||||
inventoryItem.id,
|
inventoryItem.id,
|
||||||
["location-1", "location-2"]
|
["location-1", "location-2"]
|
||||||
)
|
)) as any
|
||||||
|
|
||||||
expect(reservedQuantity).toEqual(2)
|
expect(reservedQuantity + 0).toEqual(2)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -24,9 +24,6 @@ export class Migration20240307132720 extends Migration {
|
|||||||
this.addSql(
|
this.addSql(
|
||||||
'CREATE INDEX IF NOT EXISTS "IDX_inventory_level_location_id" ON "inventory_level" (location_id);'
|
'CREATE INDEX IF NOT EXISTS "IDX_inventory_level_location_id" ON "inventory_level" (location_id);'
|
||||||
)
|
)
|
||||||
this.addSql(
|
|
||||||
'CREATE INDEX IF NOT EXISTS "IDX_inventory_level_location_id" ON "inventory_level" (location_id);'
|
|
||||||
)
|
|
||||||
|
|
||||||
this.addSql(
|
this.addSql(
|
||||||
'create table if not exists "reservation_item" ("id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, "line_item_id" text null, "location_id" text not null, "quantity" integer not null, "external_id" text null, "description" text null, "created_by" text null, "metadata" jsonb null, "inventory_item_id" text not null, constraint "reservation_item_pkey" primary key ("id"));'
|
'create table if not exists "reservation_item" ("id" text not null, "created_at" timestamptz not null default now(), "updated_at" timestamptz not null default now(), "deleted_at" timestamptz null, "line_item_id" text null, "location_id" text not null, "quantity" integer not null, "external_id" text null, "description" text null, "created_by" text null, "metadata" jsonb null, "inventory_item_id" text not null, constraint "reservation_item_pkey" primary key ("id"));'
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { Migration } from "@mikro-orm/migrations"
|
||||||
|
|
||||||
|
export class Migration20240719123015 extends Migration {
|
||||||
|
async up(): Promise<void> {
|
||||||
|
this.addSql(
|
||||||
|
`
|
||||||
|
ALTER TABLE "reservation_item" ALTER COLUMN "quantity" TYPE numeric;
|
||||||
|
ALTER TABLE "reservation_item" ADD COLUMN IF NOT EXISTS "raw_quantity" JSONB NULL;
|
||||||
|
|
||||||
|
ALTER TABLE "inventory_level" ALTER COLUMN "stocked_quantity" TYPE numeric;
|
||||||
|
ALTER TABLE "inventory_level" ADD COLUMN IF NOT EXISTS "raw_stocked_quantity" JSONB NULL;
|
||||||
|
|
||||||
|
ALTER TABLE "inventory_level" ALTER COLUMN "reserved_quantity" TYPE numeric;
|
||||||
|
ALTER TABLE "inventory_level" ADD COLUMN IF NOT EXISTS "raw_reserved_quantity" JSONB NULL;
|
||||||
|
|
||||||
|
ALTER TABLE "inventory_level" ALTER COLUMN "incoming_quantity" TYPE numeric;
|
||||||
|
ALTER TABLE "inventory_level" ADD COLUMN IF NOT EXISTS "raw_incoming_quantity" JSONB NULL;
|
||||||
|
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS "IDX_inventory_item_sku_unique";
|
||||||
|
DROP INDEX IF EXISTS "IDX_inventory_level_inventory_item_id";
|
||||||
|
DROP INDEX IF EXISTS "IDX_inventory_level_location_id";
|
||||||
|
DROP INDEX IF EXISTS "IDX_reservation_item_line_item_id";
|
||||||
|
DROP INDEX IF EXISTS "IDX_reservation_item_location_id";
|
||||||
|
DROP INDEX IF EXISTS "IDX_reservation_item_inventory_item_id";
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS "IDX_inventory_item_sku_unique" ON "inventory_item" (sku) WHERE deleted_at IS NULL;
|
||||||
|
CREATE INDEX IF NOT EXISTS "IDX_inventory_level_inventory_item_id" ON "inventory_level" (inventory_item_id) WHERE deleted_at IS NULL;
|
||||||
|
CREATE INDEX IF NOT EXISTS "IDX_inventory_level_location_id" ON "inventory_level" (location_id) WHERE deleted_at IS NULL;
|
||||||
|
CREATE INDEX IF NOT EXISTS "IDX_reservation_item_line_item_id" ON "reservation_item" (line_item_id) WHERE deleted_at IS NULL;
|
||||||
|
CREATE INDEX IF NOT EXISTS "IDX_reservation_item_location_id" ON "reservation_item" (location_id) WHERE deleted_at IS NULL;
|
||||||
|
CREATE INDEX IF NOT EXISTS "IDX_reservation_item_inventory_item_id" ON "reservation_item" (inventory_item_id) WHERE deleted_at IS NULL;
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX "IDX_inventory_level_item_location" ON "inventory_level" (inventory_item_id, location_id) WHERE deleted_at IS NULL;
|
||||||
|
`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async down(): Promise<void> {
|
||||||
|
this.addSql(
|
||||||
|
`
|
||||||
|
ALTER TABLE "reservation_item" ALTER COLUMN "quantity" TYPE integer;
|
||||||
|
ALTER TABLE "reservation_item" DROP COLUMN IF EXISTS "raw_quantity";
|
||||||
|
|
||||||
|
ALTER TABLE "inventory_level" ALTER COLUMN "stocked_quantity" TYPE integer;
|
||||||
|
ALTER TABLE "inventory_level" DROP COLUMN IF NOT EXISTS "raw_stocked_quantity";
|
||||||
|
|
||||||
|
ALTER TABLE "inventory_level" ALTER COLUMN "reserved_quantity" TYPE integer;
|
||||||
|
ALTER TABLE "inventory_level" DROP COLUMN IF NOT EXISTS "raw_reserved_quantity";
|
||||||
|
|
||||||
|
ALTER TABLE "inventory_level" ALTER COLUMN "incoming_quantity" TYPE integer;
|
||||||
|
ALTER TABLE "inventory_level" DROP COLUMN IF NOT EXISTS "raw_incoming_quantity";
|
||||||
|
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS "IDX_inventory_item_sku_unique";
|
||||||
|
DROP INDEX IF EXISTS "IDX_inventory_level_inventory_item_id";
|
||||||
|
DROP INDEX IF EXISTS "IDX_inventory_level_location_id";
|
||||||
|
DROP INDEX IF EXISTS "IDX_reservation_item_line_item_id";
|
||||||
|
DROP INDEX IF EXISTS "IDX_reservation_item_location_id";
|
||||||
|
DROP INDEX IF EXISTS "IDX_reservation_item_inventory_item_id";
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX IF NOT EXISTS "IDX_inventory_item_sku_unique" ON "inventory_item" (sku);
|
||||||
|
CREATE INDEX IF NOT EXISTS "IDX_inventory_level_inventory_item_id" ON "inventory_level" (inventory_item_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS "IDX_inventory_level_location_id" ON "inventory_level" (location_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS "IDX_reservation_item_line_item_id" ON "reservation_item" (line_item_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS "IDX_reservation_item_location_id" ON "reservation_item" (location_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS "IDX_reservation_item_inventory_item_id" ON "reservation_item" (inventory_item_id);
|
||||||
|
|
||||||
|
DROP INDEX IF EXISTS "IDX_inventory_level_item_location"
|
||||||
|
`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ const InventoryItemSkuIndex = createPsqlIndexStatementHelper({
|
|||||||
tableName: "inventory_item",
|
tableName: "inventory_item",
|
||||||
columns: "sku",
|
columns: "sku",
|
||||||
unique: true,
|
unique: true,
|
||||||
|
where: "deleted_at IS NULL",
|
||||||
})
|
})
|
||||||
|
|
||||||
type InventoryItemOptionalProps = DAL.SoftDeletableModelDateColumns
|
type InventoryItemOptionalProps = DAL.SoftDeletableModelDateColumns
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DALUtils, isDefined } from "@medusajs/utils"
|
import { DALUtils, isDefined, MathBN } from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
BeforeCreate,
|
BeforeCreate,
|
||||||
Entity,
|
Entity,
|
||||||
@@ -11,9 +11,12 @@ import {
|
|||||||
Rel,
|
Rel,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
|
|
||||||
|
import { BigNumberRawValue } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
|
BigNumber,
|
||||||
createPsqlIndexStatementHelper,
|
createPsqlIndexStatementHelper,
|
||||||
generateEntityId,
|
generateEntityId,
|
||||||
|
MikroOrmBigNumberProperty,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { InventoryItem } from "./inventory-item"
|
import { InventoryItem } from "./inventory-item"
|
||||||
|
|
||||||
@@ -26,17 +29,21 @@ const InventoryLevelDeletedAtIndex = createPsqlIndexStatementHelper({
|
|||||||
const InventoryLevelInventoryItemIdIndex = createPsqlIndexStatementHelper({
|
const InventoryLevelInventoryItemIdIndex = createPsqlIndexStatementHelper({
|
||||||
tableName: "inventory_level",
|
tableName: "inventory_level",
|
||||||
columns: "inventory_item_id",
|
columns: "inventory_item_id",
|
||||||
|
where: "deleted_at IS NULL",
|
||||||
})
|
})
|
||||||
|
|
||||||
const InventoryLevelLocationIdIndex = createPsqlIndexStatementHelper({
|
const InventoryLevelLocationIdIndex = createPsqlIndexStatementHelper({
|
||||||
tableName: "inventory_level",
|
tableName: "inventory_level",
|
||||||
columns: "location_id",
|
columns: "location_id",
|
||||||
|
where: "deleted_at IS NULL",
|
||||||
})
|
})
|
||||||
|
|
||||||
const InventoryLevelLocationIdInventoryItemIdIndex =
|
const InventoryLevelLocationIdInventoryItemIdIndex =
|
||||||
createPsqlIndexStatementHelper({
|
createPsqlIndexStatementHelper({
|
||||||
tableName: "inventory_level",
|
tableName: "inventory_level",
|
||||||
columns: "location_id",
|
columns: ["inventory_item_id", "location_id"],
|
||||||
|
unique: true,
|
||||||
|
where: "deleted_at IS NULL",
|
||||||
})
|
})
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@@ -78,14 +85,23 @@ export class InventoryLevel {
|
|||||||
@Property({ type: "text" })
|
@Property({ type: "text" })
|
||||||
location_id: string
|
location_id: string
|
||||||
|
|
||||||
@Property({ type: "int" })
|
@MikroOrmBigNumberProperty()
|
||||||
stocked_quantity: number = 0
|
stocked_quantity: BigNumber | number = 0
|
||||||
|
|
||||||
@Property({ type: "int" })
|
@Property({ columnType: "jsonb" })
|
||||||
reserved_quantity: number = 0
|
raw_stocked_quantity: BigNumberRawValue
|
||||||
|
|
||||||
@Property({ type: "int" })
|
@MikroOrmBigNumberProperty()
|
||||||
incoming_quantity: number = 0
|
reserved_quantity: BigNumber | number = 0
|
||||||
|
|
||||||
|
@Property({ columnType: "jsonb" })
|
||||||
|
raw_reserved_quantity: BigNumberRawValue
|
||||||
|
|
||||||
|
@MikroOrmBigNumberProperty()
|
||||||
|
incoming_quantity: BigNumber | number = 0
|
||||||
|
|
||||||
|
@Property({ columnType: "jsonb" })
|
||||||
|
raw_incoming_quantity: BigNumberRawValue
|
||||||
|
|
||||||
@Property({ columnType: "jsonb", nullable: true })
|
@Property({ columnType: "jsonb", nullable: true })
|
||||||
metadata: Record<string, unknown> | null
|
metadata: Record<string, unknown> | null
|
||||||
@@ -95,7 +111,7 @@ export class InventoryLevel {
|
|||||||
})
|
})
|
||||||
inventory_item: Rel<InventoryItem>
|
inventory_item: Rel<InventoryItem>
|
||||||
|
|
||||||
available_quantity: number | null = null
|
available_quantity: BigNumber | number | null = null
|
||||||
|
|
||||||
@BeforeCreate()
|
@BeforeCreate()
|
||||||
private beforeCreate(): void {
|
private beforeCreate(): void {
|
||||||
@@ -111,7 +127,9 @@ export class InventoryLevel {
|
|||||||
@OnLoad()
|
@OnLoad()
|
||||||
private onLoad(): void {
|
private onLoad(): void {
|
||||||
if (isDefined(this.stocked_quantity) && isDefined(this.reserved_quantity)) {
|
if (isDefined(this.stocked_quantity) && isDefined(this.reserved_quantity)) {
|
||||||
this.available_quantity = this.stocked_quantity - this.reserved_quantity
|
this.available_quantity = new BigNumber(
|
||||||
|
MathBN.sub(this.raw_stocked_quantity, this.raw_reserved_quantity)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,8 +9,11 @@ import {
|
|||||||
Rel,
|
Rel,
|
||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
|
|
||||||
|
import { BigNumberRawValue } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
|
BigNumber,
|
||||||
DALUtils,
|
DALUtils,
|
||||||
|
MikroOrmBigNumberProperty,
|
||||||
createPsqlIndexStatementHelper,
|
createPsqlIndexStatementHelper,
|
||||||
generateEntityId,
|
generateEntityId,
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
@@ -24,16 +27,19 @@ const ReservationItemDeletedAtIndex = createPsqlIndexStatementHelper({
|
|||||||
const ReservationItemLineItemIdIndex = createPsqlIndexStatementHelper({
|
const ReservationItemLineItemIdIndex = createPsqlIndexStatementHelper({
|
||||||
tableName: "reservation_item",
|
tableName: "reservation_item",
|
||||||
columns: "line_item_id",
|
columns: "line_item_id",
|
||||||
|
where: "deleted_at IS NULL",
|
||||||
})
|
})
|
||||||
|
|
||||||
const ReservationItemInventoryItemIdIndex = createPsqlIndexStatementHelper({
|
const ReservationItemInventoryItemIdIndex = createPsqlIndexStatementHelper({
|
||||||
tableName: "reservation_item",
|
tableName: "reservation_item",
|
||||||
columns: "inventory_item_id",
|
columns: "inventory_item_id",
|
||||||
|
where: "deleted_at IS NULL",
|
||||||
})
|
})
|
||||||
|
|
||||||
const ReservationItemLocationIdIndex = createPsqlIndexStatementHelper({
|
const ReservationItemLocationIdIndex = createPsqlIndexStatementHelper({
|
||||||
tableName: "reservation_item",
|
tableName: "reservation_item",
|
||||||
columns: "location_id",
|
columns: "location_id",
|
||||||
|
where: "deleted_at IS NULL",
|
||||||
})
|
})
|
||||||
|
|
||||||
@Entity()
|
@Entity()
|
||||||
@@ -72,8 +78,11 @@ export class ReservationItem {
|
|||||||
@Property({ type: "text" })
|
@Property({ type: "text" })
|
||||||
location_id: string
|
location_id: string
|
||||||
|
|
||||||
@Property({ columnType: "integer" })
|
@MikroOrmBigNumberProperty()
|
||||||
quantity: number
|
quantity: BigNumber | number
|
||||||
|
|
||||||
|
@Property({ columnType: "jsonb" })
|
||||||
|
raw_quantity: BigNumberRawValue
|
||||||
|
|
||||||
@Property({ type: "text", nullable: true })
|
@Property({ type: "text", nullable: true })
|
||||||
external_id: string | null = null
|
external_id: string | null = null
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
import { Context } from "@medusajs/types"
|
import { Context } from "@medusajs/types"
|
||||||
import { InventoryLevel } from "@models"
|
import {
|
||||||
|
BigNumber,
|
||||||
|
MathBN,
|
||||||
|
mikroOrmBaseRepositoryFactory,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
import { SqlEntityManager } from "@mikro-orm/postgresql"
|
||||||
import { mikroOrmBaseRepositoryFactory } from "@medusajs/utils"
|
import { InventoryLevel } from "@models"
|
||||||
|
|
||||||
export class InventoryLevelRepository extends mikroOrmBaseRepositoryFactory(
|
export class InventoryLevelRepository extends mikroOrmBaseRepositoryFactory(
|
||||||
InventoryLevel
|
InventoryLevel
|
||||||
@@ -10,42 +14,42 @@ export class InventoryLevelRepository extends mikroOrmBaseRepositoryFactory(
|
|||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[],
|
locationIds: string[],
|
||||||
context: Context = {}
|
context: Context = {}
|
||||||
): Promise<number> {
|
): Promise<BigNumber> {
|
||||||
const manager = super.getActiveManager<SqlEntityManager>(context)
|
const manager = super.getActiveManager<SqlEntityManager>(context)
|
||||||
|
|
||||||
const [result] = (await manager
|
const result = await manager
|
||||||
.getKnex()({ il: "inventory_level" })
|
.getKnex()({ il: "inventory_level" })
|
||||||
.sum("reserved_quantity")
|
.select("raw_reserved_quantity")
|
||||||
.whereIn("location_id", locationIds)
|
.whereIn("location_id", locationIds)
|
||||||
.andWhere("inventory_item_id", inventoryItemId)) as {
|
.andWhere("inventory_item_id", inventoryItemId)
|
||||||
sum: string
|
.andWhereRaw("deleted_at IS NULL")
|
||||||
}[]
|
|
||||||
|
|
||||||
return parseInt(result.sum)
|
return new BigNumber(
|
||||||
|
MathBN.sum(...result.map((r) => r.raw_reserved_quantity))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAvailableQuantity(
|
async getAvailableQuantity(
|
||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[],
|
locationIds: string[],
|
||||||
context: Context = {}
|
context: Context = {}
|
||||||
): Promise<number> {
|
): Promise<BigNumber> {
|
||||||
const knex = super.getActiveManager<SqlEntityManager>(context).getKnex()
|
const knex = super.getActiveManager<SqlEntityManager>(context).getKnex()
|
||||||
|
|
||||||
const [result] = (await knex({
|
const result = await knex({
|
||||||
il: "inventory_level",
|
il: "inventory_level",
|
||||||
})
|
})
|
||||||
.sum({
|
.select("raw_stocked_quantity", "raw_reserved_quantity")
|
||||||
stocked_quantity: "stocked_quantity",
|
|
||||||
reserved_quantity: "reserved_quantity",
|
|
||||||
})
|
|
||||||
.whereIn("location_id", locationIds)
|
.whereIn("location_id", locationIds)
|
||||||
.andWhere("inventory_item_id", inventoryItemId)) as {
|
.andWhere("inventory_item_id", inventoryItemId)
|
||||||
reserved_quantity: string
|
.andWhereRaw("deleted_at IS NULL")
|
||||||
stocked_quantity: string
|
|
||||||
}[]
|
|
||||||
|
|
||||||
return (
|
return new BigNumber(
|
||||||
parseInt(result.stocked_quantity) - parseInt(result.reserved_quantity)
|
MathBN.sum(
|
||||||
|
...result.map((r) => {
|
||||||
|
return MathBN.sub(r.raw_stocked_quantity, r.raw_reserved_quantity)
|
||||||
|
})
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,20 +57,19 @@ export class InventoryLevelRepository extends mikroOrmBaseRepositoryFactory(
|
|||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[],
|
locationIds: string[],
|
||||||
context: Context = {}
|
context: Context = {}
|
||||||
): Promise<number> {
|
): Promise<BigNumber> {
|
||||||
const knex = super.getActiveManager<SqlEntityManager>(context).getKnex()
|
const knex = super.getActiveManager<SqlEntityManager>(context).getKnex()
|
||||||
|
|
||||||
const [result] = (await knex({
|
const result = await knex({
|
||||||
il: "inventory_level",
|
il: "inventory_level",
|
||||||
})
|
})
|
||||||
.sum({
|
.select("raw_stocked_quantity")
|
||||||
stocked_quantity: "stocked_quantity",
|
|
||||||
})
|
|
||||||
.whereIn("location_id", locationIds)
|
.whereIn("location_id", locationIds)
|
||||||
.andWhere("inventory_item_id", inventoryItemId)) as {
|
.andWhere("inventory_item_id", inventoryItemId)
|
||||||
stocked_quantity: string
|
.andWhereRaw("deleted_at IS NULL")
|
||||||
}[]
|
|
||||||
|
|
||||||
return parseInt(result.stocked_quantity)
|
return new BigNumber(
|
||||||
|
MathBN.sum(...result.map((r) => r.raw_stocked_quantity))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Context } from "@medusajs/types"
|
import { Context } from "@medusajs/types"
|
||||||
import { ModulesSdkUtils } from "@medusajs/utils"
|
import { BigNumber, ModulesSdkUtils } from "@medusajs/utils"
|
||||||
|
|
||||||
import { InventoryLevelRepository } from "@repositories"
|
import { InventoryLevelRepository } from "@repositories"
|
||||||
import { InventoryLevel } from "../models/inventory-level"
|
import { InventoryLevel } from "../models/inventory-level"
|
||||||
@@ -23,7 +23,7 @@ export default class InventoryLevelService extends ModulesSdkUtils.MedusaInterna
|
|||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[] | string,
|
locationIds: string[] | string,
|
||||||
context: Context = {}
|
context: Context = {}
|
||||||
): Promise<number> {
|
): Promise<BigNumber> {
|
||||||
const locationIdArray = Array.isArray(locationIds)
|
const locationIdArray = Array.isArray(locationIds)
|
||||||
? locationIds
|
? locationIds
|
||||||
: [locationIds]
|
: [locationIds]
|
||||||
@@ -39,7 +39,7 @@ export default class InventoryLevelService extends ModulesSdkUtils.MedusaInterna
|
|||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[] | string,
|
locationIds: string[] | string,
|
||||||
context: Context = {}
|
context: Context = {}
|
||||||
): Promise<number> {
|
): Promise<BigNumber> {
|
||||||
const locationIdArray = Array.isArray(locationIds)
|
const locationIdArray = Array.isArray(locationIds)
|
||||||
? locationIds
|
? locationIds
|
||||||
: [locationIds]
|
: [locationIds]
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { InternalModuleDeclaration } from "@medusajs/modules-sdk"
|
import { InternalModuleDeclaration } from "@medusajs/modules-sdk"
|
||||||
import {
|
import {
|
||||||
|
BigNumberInput,
|
||||||
Context,
|
Context,
|
||||||
DAL,
|
DAL,
|
||||||
InventoryTypes,
|
InventoryTypes,
|
||||||
@@ -11,19 +12,20 @@ import {
|
|||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { IInventoryService } from "@medusajs/types/dist/inventory"
|
import { IInventoryService } from "@medusajs/types/dist/inventory"
|
||||||
import {
|
import {
|
||||||
arrayDifference,
|
BigNumber,
|
||||||
CommonEvents,
|
CommonEvents,
|
||||||
EmitEvents,
|
EmitEvents,
|
||||||
InjectManager,
|
InjectManager,
|
||||||
InjectTransactionManager,
|
InjectTransactionManager,
|
||||||
InventoryEvents,
|
InventoryEvents,
|
||||||
isDefined,
|
MathBN,
|
||||||
isString,
|
|
||||||
MedusaContext,
|
MedusaContext,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
MedusaService,
|
MedusaService,
|
||||||
|
arrayDifference,
|
||||||
|
isDefined,
|
||||||
|
isString,
|
||||||
partitionArray,
|
partitionArray,
|
||||||
promiseAll,
|
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import { InventoryItem, InventoryLevel, ReservationItem } from "@models"
|
import { InventoryItem, InventoryLevel, ReservationItem } from "@models"
|
||||||
import { joinerConfig } from "../joiner-config"
|
import { joinerConfig } from "../joiner-config"
|
||||||
@@ -36,6 +38,14 @@ type InjectedDependencies = {
|
|||||||
reservationItemService: ModulesSdkTypes.IMedusaInternalService<any>
|
reservationItemService: ModulesSdkTypes.IMedusaInternalService<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type InventoryItemCheckLevel = {
|
||||||
|
id?: string
|
||||||
|
location_id: string
|
||||||
|
inventory_item_id: string
|
||||||
|
quantity?: BigNumberInput
|
||||||
|
allow_backorder?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export default class InventoryModuleService
|
export default class InventoryModuleService
|
||||||
extends MedusaService<{
|
extends MedusaService<{
|
||||||
InventoryItem: {
|
InventoryItem: {
|
||||||
@@ -84,14 +94,23 @@ export default class InventoryModuleService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async ensureInventoryLevels(
|
private async ensureInventoryLevels(
|
||||||
data: (
|
data: InventoryItemCheckLevel[],
|
||||||
| { location_id: string; inventory_item_id: string }
|
options?: {
|
||||||
| { id: string }
|
validateQuantityAtLocation?: boolean
|
||||||
)[],
|
},
|
||||||
context: Context
|
context?: Context
|
||||||
): Promise<InventoryTypes.InventoryLevelDTO[]> {
|
): Promise<InventoryTypes.InventoryLevelDTO[]> {
|
||||||
|
options ??= {}
|
||||||
|
const validateQuantityAtLocation =
|
||||||
|
options.validateQuantityAtLocation ?? false
|
||||||
|
|
||||||
|
const data_ = data.map((dt: any) => ({
|
||||||
|
location_id: dt.location_id,
|
||||||
|
inventory_item_id: dt.inventory_item_id,
|
||||||
|
})) as InventoryItemCheckLevel[]
|
||||||
|
|
||||||
const [idData, itemLocationData] = partitionArray(
|
const [idData, itemLocationData] = partitionArray(
|
||||||
data,
|
data_,
|
||||||
({ id }) => !!id
|
({ id }) => !!id
|
||||||
) as [
|
) as [
|
||||||
{ id: string }[],
|
{ id: string }[],
|
||||||
@@ -122,13 +141,14 @@ export default class InventoryModuleService
|
|||||||
return acc
|
return acc
|
||||||
}, new Map())
|
}, new Map())
|
||||||
|
|
||||||
const missing = data.filter((i) => {
|
const missing = data.filter((item) => {
|
||||||
if ("id" in i) {
|
if (item.id) {
|
||||||
return !inventoryLevelIdMap.has(i.id)
|
return !inventoryLevelIdMap.has(item.id!)
|
||||||
}
|
}
|
||||||
|
|
||||||
return !inventoryLevelItemLocationMap
|
return !inventoryLevelItemLocationMap
|
||||||
.get(i.inventory_item_id)
|
.get(item.inventory_item_id)
|
||||||
?.has(i.location_id)
|
?.has(item.location_id)
|
||||||
})
|
})
|
||||||
|
|
||||||
if (missing.length) {
|
if (missing.length) {
|
||||||
@@ -144,6 +164,27 @@ export default class InventoryModuleService
|
|||||||
throw new MedusaError(MedusaError.Types.NOT_FOUND, error)
|
throw new MedusaError(MedusaError.Types.NOT_FOUND, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (validateQuantityAtLocation) {
|
||||||
|
for (const item of data) {
|
||||||
|
if (!!item.allow_backorder) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
const locations = inventoryLevelItemLocationMap.get(
|
||||||
|
item.inventory_item_id
|
||||||
|
)!
|
||||||
|
|
||||||
|
const level = locations?.get(item.location_id)!
|
||||||
|
|
||||||
|
if (MathBN.lt(level.available_quantity, item.quantity!)) {
|
||||||
|
throw new MedusaError(
|
||||||
|
MedusaError.Types.NOT_ALLOWED,
|
||||||
|
`Not enough stock available for item ${item.inventory_item_id} at location ${item.location_id}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return inventoryLevels
|
return inventoryLevels
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +192,7 @@ export default class InventoryModuleService
|
|||||||
// We sanitize the inputs here to prevent that from being used to update it
|
// We sanitize the inputs here to prevent that from being used to update it
|
||||||
private sanitizeInventoryLevelInput<TDTO = unknown>(
|
private sanitizeInventoryLevelInput<TDTO = unknown>(
|
||||||
input: (TDTO & {
|
input: (TDTO & {
|
||||||
reserved_quantity?: number
|
reserved_quantity?: BigNumberInput
|
||||||
})[]
|
})[]
|
||||||
): TDTO[] {
|
): TDTO[] {
|
||||||
return input.map((input) => {
|
return input.map((input) => {
|
||||||
@@ -173,37 +214,6 @@ export default class InventoryModuleService
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private async ensureInventoryAvailability(
|
|
||||||
data: {
|
|
||||||
allow_backorder: boolean
|
|
||||||
inventory_item_id: string
|
|
||||||
location_id: string
|
|
||||||
quantity: number
|
|
||||||
}[],
|
|
||||||
context: Context
|
|
||||||
) {
|
|
||||||
const checkLevels = data.map(async (reservation) => {
|
|
||||||
if (!!reservation.allow_backorder) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const available = await this.retrieveAvailableQuantity(
|
|
||||||
reservation.inventory_item_id,
|
|
||||||
[reservation.location_id],
|
|
||||||
context
|
|
||||||
)
|
|
||||||
|
|
||||||
if (available < reservation.quantity) {
|
|
||||||
throw new MedusaError(
|
|
||||||
MedusaError.Types.NOT_ALLOWED,
|
|
||||||
`Not enough stock available for item ${reservation.inventory_item_id} at location ${reservation.location_id}`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
await promiseAll(checkLevels)
|
|
||||||
}
|
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
async createReservationItems(
|
async createReservationItems(
|
||||||
input: InventoryTypes.CreateReservationItemInput[],
|
input: InventoryTypes.CreateReservationItemInput[],
|
||||||
@@ -225,13 +235,6 @@ export default class InventoryModuleService
|
|||||||
InventoryTypes.ReservationItemDTO[] | InventoryTypes.ReservationItemDTO
|
InventoryTypes.ReservationItemDTO[] | InventoryTypes.ReservationItemDTO
|
||||||
> {
|
> {
|
||||||
const toCreate = Array.isArray(input) ? input : [input]
|
const toCreate = Array.isArray(input) ? input : [input]
|
||||||
const sanitized = toCreate.map((d) => ({
|
|
||||||
...d,
|
|
||||||
allow_backorder: d.allow_backorder || false,
|
|
||||||
}))
|
|
||||||
|
|
||||||
await this.ensureInventoryAvailability(sanitized, context)
|
|
||||||
|
|
||||||
const created = await this.createReservationItems_(toCreate, context)
|
const created = await this.createReservationItems_(toCreate, context)
|
||||||
|
|
||||||
context.messageAggregator?.saveRawMessageData(
|
context.messageAggregator?.saveRawMessageData(
|
||||||
@@ -262,10 +265,17 @@ export default class InventoryModuleService
|
|||||||
@MedusaContext() context: Context = {}
|
@MedusaContext() context: Context = {}
|
||||||
): Promise<ReservationItem[]> {
|
): Promise<ReservationItem[]> {
|
||||||
const inventoryLevels = await this.ensureInventoryLevels(
|
const inventoryLevels = await this.ensureInventoryLevels(
|
||||||
input.map(({ location_id, inventory_item_id }) => ({
|
input.map(
|
||||||
location_id,
|
({ location_id, inventory_item_id, quantity, allow_backorder }) => ({
|
||||||
inventory_item_id,
|
location_id,
|
||||||
})),
|
inventory_item_id,
|
||||||
|
quantity,
|
||||||
|
allow_backorder,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
{
|
||||||
|
validateQuantityAtLocation: true,
|
||||||
|
},
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
const created = await this.reservationItemService_.create(input, context)
|
const created = await this.reservationItemService_.create(input, context)
|
||||||
@@ -275,7 +285,7 @@ export default class InventoryModuleService
|
|||||||
const locationMap = acc.get(curr.inventory_item_id) ?? new Map()
|
const locationMap = acc.get(curr.inventory_item_id) ?? new Map()
|
||||||
|
|
||||||
const adjustment = locationMap.get(curr.location_id) ?? 0
|
const adjustment = locationMap.get(curr.location_id) ?? 0
|
||||||
locationMap.set(curr.location_id, adjustment + curr.quantity)
|
locationMap.set(curr.location_id, MathBN.add(adjustment, curr.quantity))
|
||||||
|
|
||||||
acc.set(curr.inventory_item_id, locationMap)
|
acc.set(curr.inventory_item_id, locationMap)
|
||||||
return acc
|
return acc
|
||||||
@@ -294,7 +304,7 @@ export default class InventoryModuleService
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: level.id,
|
id: level.id,
|
||||||
reserved_quantity: level.reserved_quantity + adjustment,
|
reserved_quantity: MathBN.add(level.reserved_quantity, adjustment),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -581,6 +591,7 @@ export default class InventoryModuleService
|
|||||||
location_id,
|
location_id,
|
||||||
inventory_item_id,
|
inventory_item_id,
|
||||||
})),
|
})),
|
||||||
|
undefined,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -682,21 +693,6 @@ export default class InventoryModuleService
|
|||||||
reservationItems.map((r) => [r.id, r])
|
reservationItems.map((r) => [r.id, r])
|
||||||
)
|
)
|
||||||
|
|
||||||
const availabilityData = input.map((data) => {
|
|
||||||
const reservation = reservationMap.get(data.id)!
|
|
||||||
|
|
||||||
return {
|
|
||||||
...data,
|
|
||||||
quantity: data.quantity ?? reservation.quantity,
|
|
||||||
allow_backorder:
|
|
||||||
data.allow_backorder || reservation.allow_backorder || false,
|
|
||||||
inventory_item_id: reservation.inventory_item_id,
|
|
||||||
location_id: data.location_id ?? reservation.location_id,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
await this.ensureInventoryAvailability(availabilityData, context)
|
|
||||||
|
|
||||||
const adjustments: Map<string, Map<string, number>> = input.reduce(
|
const adjustments: Map<string, Map<string, number>> = input.reduce(
|
||||||
(acc, update) => {
|
(acc, update) => {
|
||||||
const reservation = reservationMap.get(update.id)!
|
const reservation = reservationMap.get(update.id)!
|
||||||
@@ -711,7 +707,7 @@ export default class InventoryModuleService
|
|||||||
|
|
||||||
locationMap.set(
|
locationMap.set(
|
||||||
reservation.location_id,
|
reservation.location_id,
|
||||||
reservationLocationAdjustment - reservation.quantity
|
MathBN.sub(reservationLocationAdjustment, reservation.quantity)
|
||||||
)
|
)
|
||||||
|
|
||||||
const updateLocationAdjustment =
|
const updateLocationAdjustment =
|
||||||
@@ -719,18 +715,24 @@ export default class InventoryModuleService
|
|||||||
|
|
||||||
locationMap.set(
|
locationMap.set(
|
||||||
update.location_id,
|
update.location_id,
|
||||||
updateLocationAdjustment + (update.quantity || reservation.quantity)
|
MathBN.add(
|
||||||
|
updateLocationAdjustment,
|
||||||
|
update.quantity || reservation.quantity
|
||||||
|
)
|
||||||
)
|
)
|
||||||
} else if (
|
} else if (
|
||||||
isDefined(update.quantity) &&
|
isDefined(update.quantity) &&
|
||||||
update.quantity !== reservation.quantity
|
!MathBN.eq(update.quantity, reservation.quantity)
|
||||||
) {
|
) {
|
||||||
const locationAdjustment =
|
const locationAdjustment =
|
||||||
locationMap.get(reservation.location_id) ?? 0
|
locationMap.get(reservation.location_id) ?? 0
|
||||||
|
|
||||||
locationMap.set(
|
locationMap.set(
|
||||||
reservation.location_id,
|
reservation.location_id,
|
||||||
locationAdjustment + (update.quantity! - reservation.quantity)
|
MathBN.add(
|
||||||
|
locationAdjustment,
|
||||||
|
MathBN.sub(update.quantity!, reservation.quantity)
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -740,17 +742,28 @@ export default class InventoryModuleService
|
|||||||
},
|
},
|
||||||
new Map()
|
new Map()
|
||||||
)
|
)
|
||||||
|
const availabilityData = input.map((data) => {
|
||||||
|
const reservation = reservationMap.get(data.id)!
|
||||||
|
|
||||||
const result = await this.reservationItemService_.update(input, context)
|
return {
|
||||||
|
inventory_item_id: reservation.inventory_item_id,
|
||||||
|
location_id: data.location_id ?? reservation.location_id,
|
||||||
|
quantity: data.quantity ?? reservation.quantity,
|
||||||
|
allow_backorder:
|
||||||
|
data.allow_backorder || reservation.allow_backorder || false,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
const inventoryLevels = await this.ensureInventoryLevels(
|
const inventoryLevels = await this.ensureInventoryLevels(
|
||||||
reservationItems.map((r) => ({
|
availabilityData,
|
||||||
inventory_item_id: r.inventory_item_id,
|
{
|
||||||
location_id: r.location_id,
|
validateQuantityAtLocation: true,
|
||||||
})),
|
},
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const result = await this.reservationItemService_.update(input, context)
|
||||||
|
|
||||||
const levelAdjustmentUpdates = inventoryLevels
|
const levelAdjustmentUpdates = inventoryLevels
|
||||||
.map((level) => {
|
.map((level) => {
|
||||||
const adjustment = adjustments
|
const adjustment = adjustments
|
||||||
@@ -763,7 +776,7 @@ export default class InventoryModuleService
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: level.id,
|
id: level.id,
|
||||||
reserved_quantity: level.reserved_quantity + adjustment,
|
reserved_quantity: MathBN.add(level.reserved_quantity, adjustment),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
@@ -932,7 +945,7 @@ export default class InventoryModuleService
|
|||||||
adjustInventory(
|
adjustInventory(
|
||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationId: string,
|
locationId: string,
|
||||||
adjustment: number,
|
adjustment: BigNumberInput,
|
||||||
context: Context
|
context: Context
|
||||||
): Promise<InventoryTypes.InventoryLevelDTO>
|
): Promise<InventoryTypes.InventoryLevelDTO>
|
||||||
|
|
||||||
@@ -940,7 +953,7 @@ export default class InventoryModuleService
|
|||||||
data: {
|
data: {
|
||||||
inventoryItemId: string
|
inventoryItemId: string
|
||||||
locationId: string
|
locationId: string
|
||||||
adjustment: number
|
adjustment: BigNumberInput
|
||||||
}[],
|
}[],
|
||||||
context: Context
|
context: Context
|
||||||
): Promise<InventoryTypes.InventoryLevelDTO[]>
|
): Promise<InventoryTypes.InventoryLevelDTO[]>
|
||||||
@@ -950,7 +963,7 @@ export default class InventoryModuleService
|
|||||||
async adjustInventory(
|
async adjustInventory(
|
||||||
inventoryItemIdOrData: string | any,
|
inventoryItemIdOrData: string | any,
|
||||||
locationId?: string | Context,
|
locationId?: string | Context,
|
||||||
adjustment?: number,
|
adjustment?: BigNumberInput,
|
||||||
@MedusaContext() context: Context = {}
|
@MedusaContext() context: Context = {}
|
||||||
): Promise<
|
): Promise<
|
||||||
InventoryTypes.InventoryLevelDTO | InventoryTypes.InventoryLevelDTO[]
|
InventoryTypes.InventoryLevelDTO | InventoryTypes.InventoryLevelDTO[]
|
||||||
@@ -1000,7 +1013,7 @@ export default class InventoryModuleService
|
|||||||
async adjustInventory_(
|
async adjustInventory_(
|
||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationId: string,
|
locationId: string,
|
||||||
adjustment: number,
|
adjustment: BigNumberInput,
|
||||||
@MedusaContext() context: Context = {}
|
@MedusaContext() context: Context = {}
|
||||||
): Promise<InventoryLevel> {
|
): Promise<InventoryLevel> {
|
||||||
const inventoryLevel = await this.retrieveInventoryLevelByItemAndLocation(
|
const inventoryLevel = await this.retrieveInventoryLevelByItemAndLocation(
|
||||||
@@ -1012,7 +1025,10 @@ export default class InventoryModuleService
|
|||||||
const result = await this.inventoryLevelService_.update(
|
const result = await this.inventoryLevelService_.update(
|
||||||
{
|
{
|
||||||
id: inventoryLevel.id,
|
id: inventoryLevel.id,
|
||||||
stocked_quantity: inventoryLevel.stocked_quantity + adjustment,
|
stocked_quantity: MathBN.add(
|
||||||
|
inventoryLevel.stocked_quantity,
|
||||||
|
adjustment
|
||||||
|
),
|
||||||
},
|
},
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
@@ -1026,9 +1042,9 @@ export default class InventoryModuleService
|
|||||||
locationId: string,
|
locationId: string,
|
||||||
@MedusaContext() context: Context = {}
|
@MedusaContext() context: Context = {}
|
||||||
): Promise<InventoryTypes.InventoryLevelDTO> {
|
): Promise<InventoryTypes.InventoryLevelDTO> {
|
||||||
const [inventoryLevel] = await this.listInventoryLevels(
|
const inventoryLevel = await this.listInventoryLevels(
|
||||||
{ inventory_item_id: inventoryItemId, location_id: locationId },
|
{ inventory_item_id: inventoryItemId, location_id: locationId },
|
||||||
{ take: 1 },
|
{ take: null },
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1039,7 +1055,7 @@ export default class InventoryModuleService
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return inventoryLevel
|
return inventoryLevel[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1055,9 +1071,9 @@ export default class InventoryModuleService
|
|||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[],
|
locationIds: string[],
|
||||||
@MedusaContext() context: Context = {}
|
@MedusaContext() context: Context = {}
|
||||||
): Promise<number> {
|
): Promise<BigNumber> {
|
||||||
if (locationIds.length === 0) {
|
if (locationIds.length === 0) {
|
||||||
return 0
|
return new BigNumber(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.inventoryItemService_.retrieve(
|
await this.inventoryItemService_.retrieve(
|
||||||
@@ -1091,9 +1107,9 @@ export default class InventoryModuleService
|
|||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[],
|
locationIds: string[],
|
||||||
@MedusaContext() context: Context = {}
|
@MedusaContext() context: Context = {}
|
||||||
): Promise<number> {
|
): Promise<BigNumber> {
|
||||||
if (locationIds.length === 0) {
|
if (locationIds.length === 0) {
|
||||||
return 0
|
return new BigNumber(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Throws if item does not exist
|
// Throws if item does not exist
|
||||||
@@ -1128,7 +1144,7 @@ export default class InventoryModuleService
|
|||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[],
|
locationIds: string[],
|
||||||
@MedusaContext() context: Context = {}
|
@MedusaContext() context: Context = {}
|
||||||
): Promise<number> {
|
): Promise<BigNumber> {
|
||||||
// Throws if item does not exist
|
// Throws if item does not exist
|
||||||
await this.inventoryItemService_.retrieve(
|
await this.inventoryItemService_.retrieve(
|
||||||
inventoryItemId,
|
inventoryItemId,
|
||||||
@@ -1139,7 +1155,7 @@ export default class InventoryModuleService
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (locationIds.length === 0) {
|
if (locationIds.length === 0) {
|
||||||
return 0
|
return new BigNumber(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
const reservedQuantity =
|
const reservedQuantity =
|
||||||
@@ -1164,7 +1180,7 @@ export default class InventoryModuleService
|
|||||||
async confirmInventory(
|
async confirmInventory(
|
||||||
inventoryItemId: string,
|
inventoryItemId: string,
|
||||||
locationIds: string[],
|
locationIds: string[],
|
||||||
quantity: number,
|
quantity: BigNumberInput,
|
||||||
@MedusaContext() context: Context = {}
|
@MedusaContext() context: Context = {}
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const availableQuantity = await this.retrieveAvailableQuantity(
|
const availableQuantity = await this.retrieveAvailableQuantity(
|
||||||
@@ -1172,7 +1188,7 @@ export default class InventoryModuleService
|
|||||||
locationIds,
|
locationIds,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
return availableQuantity >= quantity
|
return MathBN.gte(availableQuantity, quantity)
|
||||||
}
|
}
|
||||||
|
|
||||||
private async adjustInventoryLevelsForReservationsDeletion(
|
private async adjustInventoryLevelsForReservationsDeletion(
|
||||||
@@ -1208,6 +1224,7 @@ export default class InventoryModuleService
|
|||||||
inventory_item_id: r.inventory_item_id,
|
inventory_item_id: r.inventory_item_id,
|
||||||
location_id: r.location_id,
|
location_id: r.location_id,
|
||||||
})),
|
})),
|
||||||
|
undefined,
|
||||||
context
|
context
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1218,8 +1235,11 @@ export default class InventoryModuleService
|
|||||||
const inventoryLevelMap = acc.get(curr.inventory_item_id) ?? new Map()
|
const inventoryLevelMap = acc.get(curr.inventory_item_id) ?? new Map()
|
||||||
|
|
||||||
const adjustment = inventoryLevelMap.has(curr.location_id)
|
const adjustment = inventoryLevelMap.has(curr.location_id)
|
||||||
? inventoryLevelMap.get(curr.location_id) + curr.quantity * multiplier
|
? MathBN.add(
|
||||||
: curr.quantity * multiplier
|
inventoryLevelMap.get(curr.location_id),
|
||||||
|
MathBN.mult(curr.quantity, multiplier)
|
||||||
|
)
|
||||||
|
: MathBN.mult(curr.quantity, multiplier)
|
||||||
|
|
||||||
inventoryLevelMap.set(curr.location_id, adjustment)
|
inventoryLevelMap.set(curr.location_id, adjustment)
|
||||||
acc.set(curr.inventory_item_id, inventoryLevelMap)
|
acc.set(curr.inventory_item_id, inventoryLevelMap)
|
||||||
@@ -1237,7 +1257,7 @@ export default class InventoryModuleService
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id: level.id,
|
id: level.id,
|
||||||
reserved_quantity: level.reserved_quantity + adjustment,
|
reserved_quantity: MathBN.add(level.reserved_quantity, adjustment),
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user