inventory control

This commit is contained in:
2026-03-15 14:00:12 -05:00
parent 16582d3cea
commit 1fcb0c5480
14 changed files with 986 additions and 205 deletions

View File

@@ -366,6 +366,10 @@ function addMinutes(value: Date, minutes: number) {
return new Date(value.getTime() + minutes * 60 * 1000);
}
function shouldReserveForStatus(status: string) {
return status === "RELEASED" || status === "IN_PROGRESS" || status === "ON_HOLD";
}
function buildWorkOrderOperationPlan(
itemOperations: WorkOrderRecord["item"]["operations"],
quantity: number,
@@ -463,6 +467,46 @@ async function regenerateWorkOrderOperations(workOrderId: string) {
});
}
async function syncWorkOrderReservations(workOrderId: string) {
const workOrder = await getWorkOrderById(workOrderId);
if (!workOrder) {
return;
}
await prisma.inventoryReservation.deleteMany({
where: {
workOrderId,
sourceType: "WORK_ORDER",
},
});
if (!shouldReserveForStatus(workOrder.status)) {
return;
}
const reservations = workOrder.materialRequirements
.filter((requirement) => requirement.remainingQuantity > 0)
.map((requirement) => ({
itemId: requirement.componentItemId,
warehouseId: workOrder.warehouseId,
locationId: workOrder.locationId,
workOrderId,
sourceType: "WORK_ORDER",
sourceId: workOrderId,
quantity: requirement.remainingQuantity,
status: "ACTIVE",
notes: `${workOrder.workOrderNumber} component demand`,
}));
if (reservations.length === 0) {
return;
}
await prisma.inventoryReservation.createMany({
data: reservations,
});
}
async function nextWorkOrderNumber() {
const next = (await workOrderModel.count()) + 1;
return `WO-${String(next).padStart(5, "0")}`;
@@ -696,6 +740,7 @@ export async function createWorkOrder(payload: WorkOrderInput) {
});
await regenerateWorkOrderOperations(created.id);
await syncWorkOrderReservations(created.id);
const workOrder = await getWorkOrderById(created.id);
return workOrder ? { ok: true as const, workOrder } : { ok: false as const, reason: "Unable to load saved work order." };
@@ -738,6 +783,7 @@ export async function updateWorkOrder(workOrderId: string, payload: WorkOrderInp
});
await regenerateWorkOrderOperations(workOrderId);
await syncWorkOrderReservations(workOrderId);
const workOrder = await getWorkOrderById(workOrderId);
return workOrder ? { ok: true as const, workOrder } : { ok: false as const, reason: "Unable to load saved work order." };
@@ -773,6 +819,8 @@ export async function updateWorkOrderStatus(workOrderId: string, status: WorkOrd
},
});
await syncWorkOrderReservations(workOrderId);
const workOrder = await getWorkOrderById(workOrderId);
return workOrder ? { ok: true as const, workOrder } : { ok: false as const, reason: "Unable to load saved work order." };
}
@@ -859,6 +907,8 @@ export async function issueWorkOrderMaterial(workOrderId: string, payload: WorkO
});
});
await syncWorkOrderReservations(workOrderId);
const nextWorkOrder = await getWorkOrderById(workOrderId);
return nextWorkOrder ? { ok: true as const, workOrder: nextWorkOrder } : { ok: false as const, reason: "Unable to load updated work order." };
}
@@ -917,6 +967,8 @@ export async function recordWorkOrderCompletion(workOrderId: string, payload: Wo
});
});
await syncWorkOrderReservations(workOrderId);
const nextWorkOrder = await getWorkOrderById(workOrderId);
return nextWorkOrder ? { ok: true as const, workOrder: nextWorkOrder } : { ok: false as const, reason: "Unable to load updated work order." };
}