diff --git a/backend-node/src/controllers/outboundController.ts b/backend-node/src/controllers/outboundController.ts
index 29ec2320..fcdf389b 100644
--- a/backend-node/src/controllers/outboundController.ts
+++ b/backend-node/src/controllers/outboundController.ts
@@ -180,7 +180,7 @@ export async function create(req: AuthenticatedRequest, res: Response) {
item.outbound_status || "대기",
manager_id || item.manager_id || null,
memo || item.memo || null,
- item.source_type || null,
+ item.source_type || item.source_table || null,
item.sales_order_id || null,
item.shipment_plan_id || null,
item.item_info_id || null,
@@ -275,11 +275,12 @@ export async function create(req: AuthenticatedRequest, res: Response) {
);
}
- // 판매출고인 경우 출하지시의 ship_qty 업데이트 + 수주상세 ship_qty 반영
+ // 판매출고인 경우 출하지시의 ship_qty 업데이트 + 수주상세 ship_qty 반영 + master status 자동 전환
+ const itemSourceTable = item.source_type || item.source_table;
if (
item.outbound_type === "판매출고" &&
item.source_id &&
- item.source_type === "shipment_instruction_detail"
+ itemSourceTable === "shipment_instruction_detail"
) {
const outQtyNum = Number(item.outbound_qty) || 0;
await client.query(
@@ -292,9 +293,12 @@ export async function create(req: AuthenticatedRequest, res: Response) {
// 출하지시 상세의 detail_id로 수주상세(sales_order_detail) ship_qty도 업데이트
const sidRes = await client.query(
- `SELECT detail_id FROM shipment_instruction_detail WHERE id = $1 AND company_code = $2`,
+ `SELECT instruction_id, detail_id
+ FROM shipment_instruction_detail
+ WHERE id = $1 AND company_code = $2`,
[item.source_id, companyCode],
);
+ const instructionId = sidRes.rows[0]?.instruction_id;
const detailId = sidRes.rows[0]?.detail_id;
if (detailId) {
await client.query(
@@ -306,6 +310,27 @@ export async function create(req: AuthenticatedRequest, res: Response) {
[outQtyNum, detailId, companyCode],
);
}
+
+ // shipment_instruction master status 자동 전환 (입고의 purchase_detail → purchase_order_mng 패턴)
+ // 모든 detail 의 잔량이 0 이면 COMPLETED, 아니면 IN_PROGRESS
+ if (instructionId) {
+ const unshippedRes = await client.query(
+ `SELECT COUNT(*)::int AS unshipped
+ FROM shipment_instruction_detail
+ WHERE instruction_id = $1 AND company_code = $2
+ AND COALESCE(plan_qty, 0) > COALESCE(ship_qty, 0)`,
+ [instructionId, companyCode],
+ );
+ const unshipped = unshippedRes.rows[0]?.unshipped ?? 0;
+ const newStatus = unshipped === 0 ? "COMPLETED" : "IN_PROGRESS";
+ await client.query(
+ `UPDATE shipment_instruction
+ SET status = $1,
+ updated_date = NOW()
+ WHERE id = $2 AND company_code = $3`,
+ [newStatus, instructionId, companyCode],
+ );
+ }
}
}
@@ -569,12 +594,18 @@ export async function getShipmentInstructions(
) {
try {
const companyCode = req.user!.companyCode;
- const { keyword } = req.query;
+ const { keyword, customer } = req.query;
const conditions: string[] = ["si.company_code = $1"];
const params: any[] = [companyCode];
let paramIdx = 2;
+ if (customer) {
+ conditions.push(`si.partner_id = $${paramIdx}`);
+ params.push(customer);
+ paramIdx++;
+ }
+
if (keyword) {
conditions.push(
`(si.instruction_no ILIKE $${paramIdx} OR sid.item_name ILIKE $${paramIdx} OR sid.item_code ILIKE $${paramIdx})`,
@@ -591,6 +622,7 @@ export async function getShipmentInstructions(
si.instruction_no,
si.instruction_date,
si.partner_id,
+ COALESCE(c.customer_name, si.partner_id, '') AS customer_name,
si.status AS instruction_status,
sid.item_code,
sid.item_name,
@@ -605,6 +637,9 @@ export async function getShipmentInstructions(
JOIN shipment_instruction_detail sid
ON si.id = sid.instruction_id
AND si.company_code = sid.company_code
+ LEFT JOIN customer_mng c
+ ON si.partner_id = c.customer_code
+ AND si.company_code = c.company_code
WHERE ${conditions.join(" AND ")}
AND COALESCE(sid.plan_qty, 0) > COALESCE(sid.ship_qty, 0)
ORDER BY si.instruction_date DESC, si.instruction_no`,
diff --git a/backend-node/src/routes/inspectionResultRoutes.ts b/backend-node/src/routes/inspectionResultRoutes.ts
index 1e9d7960..5fbe31a1 100644
--- a/backend-node/src/routes/inspectionResultRoutes.ts
+++ b/backend-node/src/routes/inspectionResultRoutes.ts
@@ -17,30 +17,32 @@ router.get("/info", async (req: Request, res: Response) => {
return res.status(401).json({ success: false, message: "인증 정보 없음" });
}
- const conditions: string[] = ["company_code = $1", "is_active = 'Y'"];
+ const conditions: string[] = ["iii.company_code = $1", "iii.is_active = '사용'"];
const params: unknown[] = [companyCode];
let idx = 2;
if (itemCode) {
- conditions.push(`item_code = $${idx++}`);
+ conditions.push(`iii.item_code = $${idx++}`);
params.push(itemCode);
}
if (itemId) {
- conditions.push(`item_id = $${idx++}`);
+ conditions.push(`iii.item_id = $${idx++}`);
params.push(itemId);
}
if (inspectionType) {
- conditions.push(`inspection_type = $${idx++}`);
+ conditions.push(`iii.inspection_type = $${idx++}`);
params.push(inspectionType);
}
const sql = `
- SELECT id, item_id, item_code, item_name,
- inspection_type, inspection_item_name, inspection_standard,
- inspection_method, pass_criteria, is_required, sort_order, memo
- FROM item_inspection_info
+ SELECT iii.id, iii.item_id, iii.item_code, iii.item_name,
+ iii.inspection_type, iii.inspection_item_name, iii.inspection_standard,
+ iii.inspection_method, iii.pass_criteria, iii.is_required, iii.sort_order, iii.memo,
+ ist.judgment_criteria
+ FROM item_inspection_info iii
+ LEFT JOIN inspection_standard ist ON ist.id = iii.inspection_standard_id
WHERE ${conditions.join(" AND ")}
- ORDER BY sort_order, inspection_item_name
+ ORDER BY iii.sort_order, iii.inspection_item_name
`;
try {
diff --git a/frontend/app/(main)/COMPANY_10/pop/_components/inbound/InspectionModal.tsx b/frontend/app/(main)/COMPANY_10/pop/_components/inbound/InspectionModal.tsx
index 1bf9079f..e47f8e2c 100644
--- a/frontend/app/(main)/COMPANY_10/pop/_components/inbound/InspectionModal.tsx
+++ b/frontend/app/(main)/COMPANY_10/pop/_components/inbound/InspectionModal.tsx
@@ -14,6 +14,8 @@ export interface InspectionItem {
inspection_method: string;
pass_criteria: string;
is_required: string;
+ /** "CAT_JC_01" 수치(범위) | "CAT_JC_02" 텍스트입력 | "CAT_JC_03" O/X | "CAT_JC_04" 선택형 */
+ judgment_criteria?: string;
/** User-entered measured value */
measured_value: string;
/** "pass" | "fail" | null */
@@ -143,6 +145,7 @@ export function InspectionModal({
inspection_method: String(r.inspection_method ?? ""),
pass_criteria: String(r.pass_criteria ?? ""),
is_required: String(r.is_required ?? "Y"),
+ judgment_criteria: String(r.judgment_criteria ?? ""),
measured_value: "",
result: null,
})),
@@ -397,23 +400,25 @@ export function InspectionModal({
{/* Input + result buttons */}
-
+ {item.judgment_criteria !== "CAT_JC_03" && (
+
+ )}