
이번 작업의 목표는 “업무 흐름이 연결된다”를 백엔드에서 증명하는 것이다.
단순히 발주를 저장하는 수준이 아니라, 입고 확정 시 발주 라인의 입고 누계(qty_received)가 갱신되고,
그 결과로 발주 상태(OPEN / PARTIAL_RECEIVED / RECEIVED)가 자동으로 바뀌는 흐름을 완성했다.
poLineId를 optional로 받아 연결 가능하게 구성purchase_order_line.qty_received 증가 처리poLineId의 품목과 inbound 품목이 일치하는지 검증 추가ERP에서 중요한 건 화면이 아니라 데이터의 흐름이다. 발주가 존재해도 입고가 발주와 연결되지 않으면, 실제 운영에서는 “어떤 발주가 얼마나 들어왔는지”를 추적할 수 없다. 이번 단계는 Inbound 확정이라는 단일 트랜잭션 안에서 아래 3개가 함께 수행되도록 구성했다.
Inbound 확정 로직은 기존대로 재고 반영과 원장 기록을 수행하되,
poLineId가 존재하는 경우에만 발주 라인 누계를 갱신하고 PO 상태를 자동 계산하도록 확장했다.
if (line.poLineId() != null) {
PurchaseOrderLine poLine = purchaseOrderLineRepository.findById(line.poLineId())
.orElseThrow(() -> new IllegalArgumentException(
"존재하지 않는 발주 라인입니다. poLineId=" + line.poLineId()
));
if (!poLine.getItem().getItemId().equals(item.getItemId())) {
throw new IllegalArgumentException("poLineId의 품목과 inbound 품목이 일치하지 않습니다.");
}
poLine.receive(line.qty());
purchaseOrderLineRepository.save(poLine);
PurchaseOrder po = poLine.getPurchaseOrder();
PurchaseOrderStatus next = resolvePoStatus(
purchaseOrderLineRepository.findByPurchaseOrder_PoId(po.getPoId())
);
po.changeStatus(next);
purchaseOrderRepository.save(po);
}
PO 상태는 “전체 발주 수량 대비 누적 입고 수량”을 기준으로 계산한다. 실제 운영에서 가장 중요한 건 중간 단계(부분입고)가 자연스럽게 표현되는 것이다.
received == 0 → OPEN0 < received < ordered → PARTIAL_RECEIVEDreceived >= ordered → RECEIVEDprivate PurchaseOrderStatus resolvePoStatus(List<PurchaseOrderLine> lines) {
int ordered = 0;
int received = 0;
for (PurchaseOrderLine l : lines) {
ordered += l.getQtyOrdered();
received += l.getQtyReceived();
}
if (received == 0) return PurchaseOrderStatus.OPEN;
if (received < ordered) return PurchaseOrderStatus.PARTIAL_RECEIVED;
return PurchaseOrderStatus.RECEIVED;
}
테스트는 “실제 존재하는 po_line_id를 사용”하는 게 핵심이다. 기존에 임의로 7, 8 같은 값을 넣으면 400이 나는 게 정상이고, DB에 존재하는 poLineId로 요청하면 연결이 완료된다.
POST /api/inbounds/confirm
{
"warehouseId": 1,
"vendorId": 1,
"memo": "PO 연동 입고",
"createdBy": 1001,
"lines": [
{ "itemId": 2, "qty": 10, "poLineId": 2, "memo": "line1" },
{ "itemId": 3, "qty": 5, "poLineId": 3, "memo": "line2" }
]
}
이번 단계에서 CoreERP는 “데이터가 서로 연결되는 ERP 흐름”으로 넘어왔다. 발주(PO)가 단순 저장 데이터가 아니라, 입고 확정을 통해 누계와 상태가 자동으로 갱신되는 구조가 고정되었다.