생산관리>M-BOM 관리 — PR-A2 단건 상세 + read-only 트리 4분기 (wace mBomPopupLeft.do 1:1)
행 더블클릭 → MbomDetailDialog (헤더 메타 + 동적 LEVEL × 19컬럼 트리 그리드). 운영판 ProductionPlanningController:1113~1276 의 4분기 자동 판별을 백엔드에서 처리: 1) SAVED mbom_header.status='Y' 우선 → getSavedMbomTreeList CTE 2) ASSIGNED_EBOM source_bom_type='EBOM' → partMng.getBOMTreeList(working) CTE 3) ASSIGNED_MBOM source_bom_type='MBOM' → getMbomStructureOnly CTE 4) TEMPLATE Machine 이외 + 동일 part_no → mbom_header 템플릿 CTE 5) NONE 빈 트리 backend: - mbomService.getDetail (getProjectMgmtDetail 1:1, TOTAL_PROD_QTY = production_plan 우선) - mbomService.getTree (4분기 orchestrator + 매퍼 4종 CTE 1:1) - GET /api/production/mbom/detail/:objid - GET /api/production/mbom/tree/:objid frontend: - lib/api/mbom.ts : MbomDetail / MbomTreeRow / MbomBomDataType / getDetail / getTree - components/production/MbomDetailDialog.tsx (max-w-1600px, 헤더 14필드 + 트리 그리드) - page.tsx 행 더블클릭 핸들러 검증: O-RING (593315995) SAVED 분기 5행 정상. TOTAL_PROD_QTY production_plan=5 / QUANTITY=2 fallback 확인. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
// ============================================================
|
||||
// 생산관리 > M-BOM 관리 (PR-A1) — wace productionplanning.xml 1:1 이식.
|
||||
// 생산관리 > M-BOM 관리 — wace productionplanning.xml 1:1 이식.
|
||||
// 라우트:
|
||||
// GET /api/production/mbom/list M-BOM 관리 그리드 (PROJECT_MGMT × CONTRACT_ITEM 펼침)
|
||||
// GET /api/production/mbom/list 그리드 (PROJECT_MGMT × CONTRACT_ITEM 펼침)
|
||||
// GET /api/production/mbom/detail/:objid 단건 상세 (mBomHeaderPopup.do 1:1)
|
||||
// GET /api/production/mbom/tree/:objid read-only 트리 4분기 자동 판별 (mBomPopupLeft.do 1:1)
|
||||
// ============================================================
|
||||
|
||||
import { Response } from "express";
|
||||
@@ -25,3 +27,28 @@ export async function getList(req: AuthenticatedRequest, res: Response) {
|
||||
return res.status(500).json({ success: false, message: e.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function getDetail(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const objid = String(req.params.objid ?? "").trim();
|
||||
if (!objid) return res.status(400).json({ success: false, message: "objid 누락" });
|
||||
const data = await svc.getDetail(objid);
|
||||
if (!data) return res.status(404).json({ success: false, message: "프로젝트를 찾을 수 없습니다" });
|
||||
return res.json({ success: true, data });
|
||||
} catch (e: any) {
|
||||
logger.error("M-BOM 단건 상세 실패", { error: e.message });
|
||||
return res.status(500).json({ success: false, message: e.message });
|
||||
}
|
||||
}
|
||||
|
||||
export async function getTree(req: AuthenticatedRequest, res: Response) {
|
||||
try {
|
||||
const objid = String(req.params.objid ?? "").trim();
|
||||
if (!objid) return res.status(400).json({ success: false, message: "objid 누락" });
|
||||
const data = await svc.getTree(objid);
|
||||
return res.json({ success: true, data });
|
||||
} catch (e: any) {
|
||||
logger.error("M-BOM 트리 조회 실패", { error: e.message });
|
||||
return res.status(500).json({ success: false, message: e.message });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// ============================================================
|
||||
// 생산관리 > M-BOM 관리 (PR-A1) 라우트.
|
||||
// 생산관리 > M-BOM 관리 라우트.
|
||||
// app.ts: app.use("/api/production/mbom", productionMbomRoutes)
|
||||
// ============================================================
|
||||
|
||||
@@ -10,6 +10,8 @@ import * as ctrl from "../controllers/mbomController";
|
||||
const router = Router();
|
||||
router.use(authenticateToken);
|
||||
|
||||
router.get("/list", ctrl.getList);
|
||||
router.get("/list", ctrl.getList);
|
||||
router.get("/detail/:objid", ctrl.getDetail);
|
||||
router.get("/tree/:objid", ctrl.getTree);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
// ============================================================
|
||||
// 생산관리 > M-BOM 관리 (PR-A1) — wace productionplanning.xml 1:1 이식.
|
||||
// 생산관리 > M-BOM 관리 — wace productionplanning.xml 1:1 이식.
|
||||
//
|
||||
// 매퍼 매핑 (원본: wace_plm/src/com/pms/mapper/productionplanning.xml):
|
||||
// mBomMgmtGridList → list() (라인 2874~3119, PROJECT_MGMT × CONTRACT_ITEM 펼침)
|
||||
// 매퍼 매핑 (원본: wace_plm/src/com/pms/mapper/productionplanning.xml,
|
||||
// wace_plm/src/com/pms/mapper/partMng.xml):
|
||||
// mBomMgmtGridList → list() (PR-A1, ~3119)
|
||||
// getProjectMgmtDetail → getDetail() (PR-A2, ~3218)
|
||||
// getLatestMbomByProjectId → getLatestSavedMbom() (PR-A2, ~3570)
|
||||
// getLatestMbomTemplateByPartNo → getLatestTemplate() (PR-A2, ~3591)
|
||||
// getSavedMbomTreeList → getSavedTree() (PR-A2, ~4359)
|
||||
// getMbomStructureOnly → getStructureOnly() (PR-A2, ~4538)
|
||||
// getMbomTemplateDetails → getTemplateDetails() (PR-A2, ~3794)
|
||||
// partMng.getBOMTreeList → getEbomWorkingTree() (PR-A2, partMng.xml ~3549)
|
||||
//
|
||||
// 그리드 베이스: PROJECT_MGMT × CONTRACT_ITEM 펼침 (1 프로젝트 = 1+ 행).
|
||||
// 9 검색 필터 + 30+ 출력 컬럼 (M-BOM 상태/저장일/작성자 + 구매리스트 매칭).
|
||||
// vexplor_rps 의존: project_mgmt / contract_mgmt / contract_item / contract_item_serial
|
||||
// / mbom_header (PR-A0) / mbom_history / sales_request_master / client_mng
|
||||
// / supply_mng / part_bom_report / comm_code / user_info / user_name() fn
|
||||
// 트리 분기 (mBomPopupLeft.do 1:1):
|
||||
// 1) SAVED — mbom_header 에 status='Y' 가 있으면 그 트리
|
||||
// 2) ASSIGNED_EBOM — project_mgmt.source_bom_type='EBOM' + source_ebom_objid
|
||||
// 3) ASSIGNED_MBOM — project_mgmt.source_bom_type='MBOM' + source_mbom_objid
|
||||
// 4) TEMPLATE — Machine 이외(product != '0000928') + part_no 있으면 동일 품번 mbom_header
|
||||
// 5) NONE — 빈 트리
|
||||
// ============================================================
|
||||
|
||||
import { getPool } from "../database/db";
|
||||
@@ -275,3 +284,583 @@ export async function list(filter: MbomListFilter) {
|
||||
pageSize,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── 단건 상세 (getProjectMgmtDetail) ──────────────────────────
|
||||
//
|
||||
// 매퍼 productionplanning.xml getProjectMgmtDetail (라인 3150~3218) 1:1.
|
||||
// TOTAL_PROD_QTY = production_plan 우선 → PM.QUANTITY fallback.
|
||||
|
||||
export async function getDetail(objid: string) {
|
||||
const pool = getPool();
|
||||
const sql = `
|
||||
SELECT
|
||||
PM.OBJID::VARCHAR AS objid,
|
||||
PM.CONTRACT_OBJID AS contract_objid,
|
||||
PM.PROJECT_NO AS project_no,
|
||||
PM.BOM_REPORT_OBJID AS bom_report_objid,
|
||||
PM.PART_OBJID AS part_objid,
|
||||
PM.PART_NO AS part_no,
|
||||
PM.PART_NAME AS part_name,
|
||||
PM.SOURCE_BOM_TYPE AS source_bom_type,
|
||||
PM.SOURCE_EBOM_OBJID AS source_ebom_objid,
|
||||
PM.SOURCE_MBOM_OBJID AS source_mbom_objid,
|
||||
PM.QUANTITY AS quantity,
|
||||
COALESCE(
|
||||
(SELECT NULLIF(PP.TOTAL_PROD_QTY, '')::numeric
|
||||
FROM PRODUCTION_PLAN PP
|
||||
WHERE PP.PROJECT_OBJID = PM.OBJID::VARCHAR
|
||||
AND UPPER(PP.STATUS) = 'ACTIVE'
|
||||
LIMIT 1),
|
||||
COALESCE(NULLIF(PM.QUANTITY, '')::numeric, 0)
|
||||
) AS total_prod_qty,
|
||||
COALESCE(
|
||||
(SELECT PBR.PART_NO FROM PART_BOM_REPORT PBR
|
||||
WHERE PBR.OBJID::VARCHAR = PM.BOM_REPORT_OBJID
|
||||
AND PM.MBOM_STATUS = 'Y'
|
||||
LIMIT 1), ''
|
||||
) AS mbom_part_no,
|
||||
CM.CATEGORY_CD AS category_cd,
|
||||
COALESCE((SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.CATEGORY_CD LIMIT 1), '') AS category_name,
|
||||
CM.PRODUCT AS product,
|
||||
CM.PRODUCT AS product_code,
|
||||
COALESCE((SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.PRODUCT LIMIT 1), '') AS product_name,
|
||||
CM.AREA_CD AS area_cd,
|
||||
COALESCE((SELECT CODE_NAME FROM COMM_CODE WHERE CODE_ID = CM.AREA_CD LIMIT 1), '') AS area_name,
|
||||
CM.CUSTOMER_OBJID AS customer_objid,
|
||||
COALESCE(
|
||||
CASE WHEN CM.CUSTOMER_OBJID LIKE 'C_%'
|
||||
THEN (SELECT CLIENT_NM FROM CLIENT_MNG AS C
|
||||
WHERE 'C_' || C.OBJID::VARCHAR = CM.CUSTOMER_OBJID LIMIT 1)
|
||||
ELSE (SELECT SUPPLY_NAME FROM SUPPLY_MNG
|
||||
WHERE OBJID::VARCHAR = CM.CUSTOMER_OBJID::VARCHAR LIMIT 1) END,
|
||||
''
|
||||
) AS customer_name,
|
||||
CM.PAID_TYPE AS paid_type,
|
||||
CM.REQ_DEL_DATE AS req_del_date,
|
||||
TO_CHAR(PM.REGDATE, 'YYYY-MM-DD') AS receipt_date,
|
||||
COALESCE(
|
||||
(SELECT TO_CHAR(PBR.REGDATE, 'YYYY-MM-DD')
|
||||
FROM PART_BOM_REPORT PBR
|
||||
WHERE PBR.OBJID::VARCHAR = PM.BOM_REPORT_OBJID
|
||||
AND PM.MBOM_STATUS = 'Y'
|
||||
LIMIT 1),
|
||||
TO_CHAR(PM.REGDATE, 'YYYY-MM-DD')
|
||||
) AS mbom_regdate
|
||||
FROM PROJECT_MGMT PM
|
||||
INNER JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
|
||||
WHERE PM.OBJID::VARCHAR = $1
|
||||
LIMIT 1
|
||||
`;
|
||||
const r = await pool.query(sql, [objid]);
|
||||
return r.rows[0] ?? null;
|
||||
}
|
||||
|
||||
// ─── 분기 진입점 (mBomPopupLeft.do 1:1) ────────────────────────
|
||||
//
|
||||
// 운영판 ProductionPlanningController:1113~1276 의 분기 로직:
|
||||
// 1) 저장된 M-BOM (mbom_header.status='Y') 우선
|
||||
// 2) 없으면 source_bom_type 으로 EBOM/MBOM 분기
|
||||
// 3) 그래도 없으면 Machine 이외 + part_no 매칭으로 템플릿
|
||||
// 4) 모두 없으면 빈 트리
|
||||
|
||||
export type BomDataType = "SAVED" | "ASSIGNED_EBOM" | "ASSIGNED_MBOM" | "TEMPLATE" | "NONE";
|
||||
|
||||
export interface MbomTreeResult {
|
||||
bom_data_type: BomDataType;
|
||||
bom_report_objid: string | null;
|
||||
max_level: number;
|
||||
rows: any[];
|
||||
}
|
||||
|
||||
export async function getTree(objid: string): Promise<MbomTreeResult> {
|
||||
const detail = await getDetail(objid);
|
||||
if (!detail) {
|
||||
return { bom_data_type: "NONE", bom_report_objid: null, max_level: 1, rows: [] };
|
||||
}
|
||||
|
||||
// 1) SAVED — getLatestMbomByProjectId
|
||||
const saved = await getLatestSavedMbom(objid);
|
||||
if (saved && saved.objid) {
|
||||
const rows = await getSavedTree(saved.objid);
|
||||
return finalize("SAVED", saved.objid, rows);
|
||||
}
|
||||
|
||||
// 2) ASSIGNED_EBOM
|
||||
if (detail.source_bom_type === "EBOM" && detail.source_ebom_objid) {
|
||||
const rows = await getEbomWorkingTree(detail.source_ebom_objid);
|
||||
return finalize("ASSIGNED_EBOM", detail.source_ebom_objid, rows);
|
||||
}
|
||||
|
||||
// 3) ASSIGNED_MBOM
|
||||
if (detail.source_bom_type === "MBOM" && detail.source_mbom_objid) {
|
||||
const rows = await getStructureOnly(detail.source_mbom_objid);
|
||||
return finalize("ASSIGNED_MBOM", detail.source_mbom_objid, rows);
|
||||
}
|
||||
|
||||
// 4) TEMPLATE — Machine 이외 + part_no
|
||||
if (detail.product_code !== "0000928" && detail.part_no) {
|
||||
const tpl = await getLatestTemplate(detail.part_no);
|
||||
if (tpl && tpl.template_header_objid) {
|
||||
const rows = await getTemplateDetails(tpl.template_header_objid);
|
||||
return finalize("TEMPLATE", tpl.template_header_objid, rows);
|
||||
}
|
||||
}
|
||||
|
||||
// 5) NONE
|
||||
return { bom_data_type: "NONE", bom_report_objid: detail.bom_report_objid ?? null, max_level: 1, rows: [] };
|
||||
}
|
||||
|
||||
function finalize(type: BomDataType, bomReportObjid: string, rows: any[]): MbomTreeResult {
|
||||
let maxLevel = 1;
|
||||
for (const r of rows) {
|
||||
const lv = Number(r.level ?? r.LEVEL ?? 1);
|
||||
if (lv > maxLevel) maxLevel = lv;
|
||||
}
|
||||
return { bom_data_type: type, bom_report_objid: bomReportObjid, max_level: maxLevel, rows };
|
||||
}
|
||||
|
||||
// ─── 분기 1) SAVED 진입 ──────────────────────────────────────
|
||||
//
|
||||
// 매퍼 getLatestMbomByProjectId (productionplanning.xml:3555~3570) 1:1.
|
||||
|
||||
async function getLatestSavedMbom(projectObjId: string) {
|
||||
const pool = getPool();
|
||||
const r = await pool.query(
|
||||
`SELECT OBJID::VARCHAR AS objid, MBOM_NO AS mbom_no,
|
||||
SOURCE_BOM_TYPE AS source_bom_type,
|
||||
SOURCE_EBOM_OBJID AS source_ebom_objid,
|
||||
SOURCE_MBOM_OBJID AS source_mbom_objid,
|
||||
PROJECT_OBJID AS project_objid, STATUS AS status, REGDATE AS regdate
|
||||
FROM MBOM_HEADER
|
||||
WHERE PROJECT_OBJID = $1 AND STATUS = 'Y'
|
||||
ORDER BY REGDATE DESC LIMIT 1`,
|
||||
[projectObjId],
|
||||
);
|
||||
return r.rows[0] ?? null;
|
||||
}
|
||||
|
||||
// ─── 분기 4) TEMPLATE 진입 ───────────────────────────────────
|
||||
//
|
||||
// 매퍼 getLatestMbomTemplateByPartNo (productionplanning.xml:3573~3591) 1:1.
|
||||
|
||||
async function getLatestTemplate(partNo: string) {
|
||||
const pool = getPool();
|
||||
const r = await pool.query(
|
||||
`SELECT MH.OBJID::VARCHAR AS template_header_objid,
|
||||
MH.MBOM_NO AS template_mbom_no,
|
||||
MH.PART_NO AS part_no,
|
||||
MH.PART_NAME AS part_name,
|
||||
MH.SOURCE_BOM_TYPE AS source_bom_type,
|
||||
MH.SOURCE_EBOM_OBJID AS source_ebom_objid,
|
||||
MH.SOURCE_MBOM_OBJID AS source_mbom_objid,
|
||||
TO_CHAR(MH.REGDATE, 'YYYY-MM-DD HH24:MI:SS') AS regdate
|
||||
FROM MBOM_HEADER MH
|
||||
INNER JOIN PROJECT_MGMT PM ON MH.PROJECT_OBJID = PM.OBJID::VARCHAR
|
||||
INNER JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
|
||||
WHERE MH.PART_NO = $1
|
||||
AND MH.STATUS = 'Y'
|
||||
AND CM.PRODUCT != '0000928'
|
||||
ORDER BY MH.REGDATE DESC LIMIT 1`,
|
||||
[partNo],
|
||||
);
|
||||
return r.rows[0] ?? null;
|
||||
}
|
||||
|
||||
// ─── 분기 1-SAVED 트리 ───────────────────────────────────────
|
||||
//
|
||||
// 매퍼 getSavedMbomTreeList (productionplanning.xml:4114~4359) 1:1.
|
||||
// RECURSIVE CTE + PART_MNG 조인 + ATTACH_FILE_INFO 카운트 (CU01/02/03_CNT) + 소재소요량.
|
||||
|
||||
async function getSavedTree(mbomHeaderObjid: string) {
|
||||
const pool = getPool();
|
||||
const r = await pool.query(SAVED_TREE_SQL, [mbomHeaderObjid]);
|
||||
return r.rows;
|
||||
}
|
||||
|
||||
// ─── 분기 3-ASSIGNED_MBOM 구조만 ─────────────────────────────
|
||||
//
|
||||
// 매퍼 getMbomStructureOnly (productionplanning.xml:4362~4538) 1:1.
|
||||
// 생산 정보는 NULL — 구조만 표시.
|
||||
|
||||
async function getStructureOnly(mbomHeaderObjid: string) {
|
||||
const pool = getPool();
|
||||
const r = await pool.query(STRUCTURE_ONLY_SQL, [mbomHeaderObjid]);
|
||||
return r.rows;
|
||||
}
|
||||
|
||||
// ─── 분기 4-TEMPLATE 트리 ────────────────────────────────────
|
||||
//
|
||||
// 매퍼 getMbomTemplateDetails (productionplanning.xml:3594~3794) 1:1.
|
||||
// ORDER_QTY/PRODUCTION_QTY 가 빠진 점만 SAVED 와 다름.
|
||||
|
||||
async function getTemplateDetails(mbomHeaderObjid: string) {
|
||||
const pool = getPool();
|
||||
const r = await pool.query(TEMPLATE_TREE_SQL, [mbomHeaderObjid]);
|
||||
return r.rows;
|
||||
}
|
||||
|
||||
// ─── 분기 2-ASSIGNED_EBOM 트리 ───────────────────────────────
|
||||
//
|
||||
// 매퍼 partMng.getBOMTreeList (partMng.xml:3289~3549) - search_type='working' 1:1.
|
||||
// bom_part_qty RECURSIVE CTE + PART_MNG 조인.
|
||||
|
||||
async function getEbomWorkingTree(bomReportObjid: string) {
|
||||
const pool = getPool();
|
||||
const r = await pool.query(EBOM_WORKING_TREE_SQL, [bomReportObjid]);
|
||||
return r.rows;
|
||||
}
|
||||
|
||||
// ─── 트리 SELECT 본문 (매퍼 4종 1:1, lowercase alias) ──────
|
||||
|
||||
const SAVED_TREE_SQL = `
|
||||
WITH RECURSIVE VIEW_BOM(
|
||||
MBOM_HEADER_OBJID, OBJID, PARENT_OBJID, CHILD_OBJID,
|
||||
PART_OBJID, PART_NO, PART_NAME, QTY, ITEM_QTY, QTY_TEMP,
|
||||
REGDATE, SEQ, STATUS, LEV, PATH, PATH2, CYCLE,
|
||||
UNIT, SUPPLY_TYPE, MAKE_OR_BUY,
|
||||
RAW_MATERIAL_PART_NO, RAW_MATERIAL_SPEC, RAW_MATERIAL, RAW_MATERIAL_SIZE,
|
||||
PROCESSING_VENDOR, PROCESSING_DEADLINE, GRINDING_DEADLINE,
|
||||
REQUIRED_QTY, ORDER_QTY, PRODUCTION_QTY, STOCK_QTY, SHORTAGE_QTY,
|
||||
VENDOR, UNIT_PRICE, PROCESSING_UNIT_PRICE, TOTAL_PRICE, CURRENCY,
|
||||
LEAD_TIME, MIN_ORDER_QTY, WRITER, EDITER, EDIT_DATE, REMARK
|
||||
) AS (
|
||||
SELECT A.MBOM_HEADER_OBJID, A.OBJID, A.PARENT_OBJID, A.CHILD_OBJID,
|
||||
A.PART_OBJID, A.PART_NO, A.PART_NAME, A.QTY, A.ITEM_QTY, A.QTY,
|
||||
A.REGDATE, A.SEQ, A.STATUS, 1,
|
||||
ARRAY [A.CHILD_OBJID::TEXT],
|
||||
ARRAY [LPAD(A.SEQ::TEXT, 10, '0')],
|
||||
FALSE,
|
||||
A.UNIT, A.SUPPLY_TYPE, A.MAKE_OR_BUY,
|
||||
A.RAW_MATERIAL_PART_NO, A.RAW_MATERIAL_SPEC, A.RAW_MATERIAL, A.RAW_MATERIAL_SIZE,
|
||||
A.PROCESSING_VENDOR, A.PROCESSING_DEADLINE, A.GRINDING_DEADLINE,
|
||||
A.REQUIRED_QTY, A.ORDER_QTY, A.PRODUCTION_QTY, A.STOCK_QTY, A.SHORTAGE_QTY,
|
||||
A.VENDOR, A.UNIT_PRICE, A.PROCESSING_UNIT_PRICE, A.TOTAL_PRICE, A.CURRENCY,
|
||||
A.LEAD_TIME, A.MIN_ORDER_QTY, A.WRITER, A.EDITER, A.EDIT_DATE, A.REMARK
|
||||
FROM MBOM_DETAIL A
|
||||
WHERE 1=1
|
||||
AND (A.PARENT_OBJID IS NULL OR A.PARENT_OBJID = '')
|
||||
AND A.MBOM_HEADER_OBJID = $1
|
||||
AND A.STATUS = 'ACTIVE'
|
||||
UNION ALL
|
||||
SELECT B.MBOM_HEADER_OBJID, B.OBJID, B.PARENT_OBJID, B.CHILD_OBJID,
|
||||
B.PART_OBJID, B.PART_NO, B.PART_NAME, B.QTY, B.ITEM_QTY, B.QTY,
|
||||
B.REGDATE, B.SEQ, B.STATUS, LEV + 1,
|
||||
PATH || B.CHILD_OBJID::TEXT,
|
||||
PATH2 || LPAD(B.SEQ::TEXT, 10, '0'),
|
||||
B.PARENT_OBJID = ANY(PATH),
|
||||
B.UNIT, B.SUPPLY_TYPE, B.MAKE_OR_BUY,
|
||||
B.RAW_MATERIAL_PART_NO, B.RAW_MATERIAL_SPEC, B.RAW_MATERIAL, B.RAW_MATERIAL_SIZE,
|
||||
B.PROCESSING_VENDOR, B.PROCESSING_DEADLINE, B.GRINDING_DEADLINE,
|
||||
B.REQUIRED_QTY, B.ORDER_QTY, B.PRODUCTION_QTY, B.STOCK_QTY, B.SHORTAGE_QTY,
|
||||
B.VENDOR, B.UNIT_PRICE, B.PROCESSING_UNIT_PRICE, B.TOTAL_PRICE, B.CURRENCY,
|
||||
B.LEAD_TIME, B.MIN_ORDER_QTY, B.WRITER, B.EDITER, B.EDIT_DATE, B.REMARK
|
||||
FROM MBOM_DETAIL B
|
||||
JOIN VIEW_BOM ON B.PARENT_OBJID = VIEW_BOM.CHILD_OBJID
|
||||
AND VIEW_BOM.MBOM_HEADER_OBJID = B.MBOM_HEADER_OBJID
|
||||
AND B.STATUS = 'ACTIVE'
|
||||
)
|
||||
SELECT
|
||||
V.MBOM_HEADER_OBJID AS bom_report_objid,
|
||||
V.OBJID AS objid,
|
||||
V.PARENT_OBJID AS parent_objid,
|
||||
V.CHILD_OBJID AS child_objid,
|
||||
V.PART_OBJID AS part_objid,
|
||||
V.PART_NO AS part_no,
|
||||
V.PART_NAME AS part_name,
|
||||
V.QTY AS qty,
|
||||
V.ITEM_QTY AS item_qty,
|
||||
V.QTY_TEMP AS qty_temp,
|
||||
V.LEV AS level,
|
||||
(SELECT COUNT(*) FROM MBOM_DETAIL WHERE PARENT_OBJID = V.CHILD_OBJID) AS sub_part_cnt,
|
||||
V.SEQ AS seq,
|
||||
V.STATUS AS status,
|
||||
V.UNIT AS unit,
|
||||
V.SUPPLY_TYPE AS supply_type,
|
||||
V.MAKE_OR_BUY AS make_or_buy,
|
||||
V.RAW_MATERIAL_PART_NO AS raw_material_no,
|
||||
V.RAW_MATERIAL_SPEC AS raw_material_spec,
|
||||
V.RAW_MATERIAL AS raw_material,
|
||||
V.RAW_MATERIAL_SIZE AS size,
|
||||
V.PROCESSING_VENDOR AS processing_vendor,
|
||||
(SELECT CLIENT_NM FROM CLIENT_MNG WHERE OBJID::VARCHAR = V.PROCESSING_VENDOR) AS processing_vendor_name,
|
||||
V.PROCESSING_DEADLINE AS processing_deadline,
|
||||
V.GRINDING_DEADLINE AS grinding_deadline,
|
||||
V.REQUIRED_QTY AS required_qty,
|
||||
V.ORDER_QTY AS order_qty,
|
||||
V.PRODUCTION_QTY AS production_qty,
|
||||
V.STOCK_QTY AS stock_qty,
|
||||
V.SHORTAGE_QTY AS shortage_qty,
|
||||
V.VENDOR AS vendor,
|
||||
(SELECT CLIENT_NM FROM CLIENT_MNG WHERE OBJID::VARCHAR = V.VENDOR) AS vendor_name,
|
||||
V.UNIT_PRICE AS unit_price,
|
||||
V.PROCESSING_UNIT_PRICE AS processing_unit_price,
|
||||
V.TOTAL_PRICE AS total_price,
|
||||
V.CURRENCY AS currency,
|
||||
V.LEAD_TIME AS lead_time,
|
||||
V.MIN_ORDER_QTY AS min_order_qty,
|
||||
V.WRITER AS writer,
|
||||
TO_CHAR(V.REGDATE, 'YYYY-MM-DD HH24:MI:SS') AS regdate,
|
||||
V.EDITER AS editer,
|
||||
CASE WHEN V.EDIT_DATE IS NOT NULL THEN TO_CHAR(V.EDIT_DATE, 'YYYY-MM-DD HH24:MI:SS') END AS edit_date,
|
||||
V.REMARK AS remark,
|
||||
CASE WHEN V.LEV = 1 THEN V.OBJID END AS root_objid,
|
||||
CASE WHEN V.LEV = 1 THEN V.OBJID END AS sub_root_objid,
|
||||
1 AS leaf,
|
||||
P.SPEC, P.MATERIAL, P.WEIGHT, P.PART_TYPE, P.REVISION, P.MAKER,
|
||||
P.THICKNESS, P.WIDTH, P.HEIGHT, P.OUT_DIAMETER, P.IN_DIAMETER, P.LENGTH,
|
||||
P.SOURCING_CODE, P.HEAT_TREATMENT_HARDNESS, P.HEAT_TREATMENT_METHOD, P.SURFACE_TREATMENT,
|
||||
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = P.UNIT) AS unit_title,
|
||||
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = P.PART_TYPE) AS part_type_title,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('3D_CAD')) AS cu01_cnt,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('2D_DRAWING_CAD')) AS cu02_cnt,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('2D_PDF_CAD')) AS cu03_cnt,
|
||||
COALESCE((SELECT NULLIF(MP.UNIT_QTY, '')::numeric FROM PART_MNG MP WHERE MP.PART_NO = V.RAW_MATERIAL_PART_NO LIMIT 1), 0) AS part_unit_qty,
|
||||
COALESCE((SELECT NULLIF(MP.UNIT_LENGTH, '')::numeric FROM PART_MNG MP WHERE MP.PART_NO = V.RAW_MATERIAL_PART_NO LIMIT 1), 0) AS part_unit_length
|
||||
FROM VIEW_BOM V
|
||||
INNER JOIN PART_MNG P ON P.OBJID = V.PART_OBJID
|
||||
ORDER BY V.PATH2
|
||||
`;
|
||||
|
||||
const STRUCTURE_ONLY_SQL = `
|
||||
WITH RECURSIVE VIEW_BOM(
|
||||
MBOM_HEADER_OBJID, OBJID, PARENT_OBJID, CHILD_OBJID,
|
||||
PART_OBJID, PART_NO, PART_NAME, QTY, ITEM_QTY, QTY_TEMP,
|
||||
REGDATE, SEQ, STATUS, LEV, PATH, PATH2, CYCLE,
|
||||
UNIT, WRITER, RAW_MATERIAL_PART_NO
|
||||
) AS (
|
||||
SELECT A.MBOM_HEADER_OBJID, A.OBJID, A.PARENT_OBJID, A.CHILD_OBJID,
|
||||
A.PART_OBJID, A.PART_NO, A.PART_NAME, A.QTY, A.QTY, A.QTY,
|
||||
A.REGDATE, A.SEQ, A.STATUS, 1,
|
||||
ARRAY [A.CHILD_OBJID::TEXT],
|
||||
ARRAY [LPAD(A.SEQ::TEXT, 10, '0')],
|
||||
FALSE,
|
||||
A.UNIT, A.WRITER, A.RAW_MATERIAL_PART_NO
|
||||
FROM MBOM_DETAIL A
|
||||
WHERE 1=1
|
||||
AND (A.PARENT_OBJID IS NULL OR A.PARENT_OBJID = '')
|
||||
AND A.MBOM_HEADER_OBJID = $1
|
||||
AND A.STATUS = 'ACTIVE'
|
||||
UNION ALL
|
||||
SELECT B.MBOM_HEADER_OBJID, B.OBJID, B.PARENT_OBJID, B.CHILD_OBJID,
|
||||
B.PART_OBJID, B.PART_NO, B.PART_NAME, B.QTY, B.QTY, B.QTY,
|
||||
B.REGDATE, B.SEQ, B.STATUS, LEV + 1,
|
||||
PATH || B.CHILD_OBJID::TEXT,
|
||||
PATH2 || LPAD(B.SEQ::TEXT, 10, '0'),
|
||||
B.PARENT_OBJID = ANY(PATH),
|
||||
B.UNIT, B.WRITER, B.RAW_MATERIAL_PART_NO
|
||||
FROM MBOM_DETAIL B
|
||||
JOIN VIEW_BOM ON B.PARENT_OBJID = VIEW_BOM.CHILD_OBJID
|
||||
AND VIEW_BOM.MBOM_HEADER_OBJID = B.MBOM_HEADER_OBJID
|
||||
AND B.STATUS = 'ACTIVE'
|
||||
)
|
||||
SELECT
|
||||
V.MBOM_HEADER_OBJID AS bom_report_objid,
|
||||
V.OBJID AS objid,
|
||||
V.PARENT_OBJID AS parent_objid,
|
||||
V.CHILD_OBJID AS child_objid,
|
||||
V.PART_OBJID AS part_objid,
|
||||
V.PART_NO AS part_no,
|
||||
V.PART_NAME AS part_name,
|
||||
V.QTY AS qty,
|
||||
V.ITEM_QTY AS item_qty,
|
||||
V.QTY_TEMP AS qty_temp,
|
||||
V.LEV AS level,
|
||||
(SELECT COUNT(*) FROM MBOM_DETAIL WHERE PARENT_OBJID = V.CHILD_OBJID) AS sub_part_cnt,
|
||||
V.SEQ, V.STATUS, V.UNIT, V.WRITER,
|
||||
TO_CHAR(V.REGDATE, 'YYYY-MM-DD HH24:MI:SS') AS regdate,
|
||||
NULL::text AS supply_type, NULL::text AS make_or_buy,
|
||||
NULL::text AS raw_material_no, NULL::text AS raw_material_spec,
|
||||
NULL::text AS raw_material, NULL::text AS size,
|
||||
NULL::text AS processing_vendor, NULL::text AS processing_deadline, NULL::text AS grinding_deadline,
|
||||
NULL::numeric AS required_qty, NULL::numeric AS order_qty, NULL::numeric AS production_qty,
|
||||
NULL::numeric AS stock_qty, NULL::numeric AS shortage_qty,
|
||||
NULL::text AS vendor, NULL::numeric AS unit_price, NULL::numeric AS processing_unit_price,
|
||||
NULL::numeric AS total_price, NULL::text AS currency,
|
||||
NULL::int AS lead_time, NULL::numeric AS min_order_qty,
|
||||
NULL::text AS editer, NULL::text AS edit_date, NULL::text AS remark,
|
||||
CASE WHEN V.LEV = 1 THEN V.OBJID END AS root_objid,
|
||||
CASE WHEN V.LEV = 1 THEN V.OBJID END AS sub_root_objid,
|
||||
1 AS leaf,
|
||||
P.SPEC, P.MATERIAL, P.WEIGHT, P.PART_TYPE, P.REVISION, P.MAKER,
|
||||
P.THICKNESS, P.WIDTH, P.HEIGHT, P.OUT_DIAMETER, P.IN_DIAMETER, P.LENGTH,
|
||||
P.SOURCING_CODE, P.HEAT_TREATMENT_HARDNESS, P.HEAT_TREATMENT_METHOD, P.SURFACE_TREATMENT,
|
||||
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = P.UNIT) AS unit_title,
|
||||
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = P.PART_TYPE) AS part_type_title,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('3D_CAD')) AS cu01_cnt,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('2D_DRAWING_CAD')) AS cu02_cnt,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('2D_PDF_CAD')) AS cu03_cnt,
|
||||
COALESCE((SELECT NULLIF(MP.UNIT_QTY, '')::numeric FROM PART_MNG MP WHERE MP.PART_NO = V.RAW_MATERIAL_PART_NO LIMIT 1), 0) AS part_unit_qty,
|
||||
COALESCE((SELECT NULLIF(MP.UNIT_LENGTH, '')::numeric FROM PART_MNG MP WHERE MP.PART_NO = V.RAW_MATERIAL_PART_NO LIMIT 1), 0) AS part_unit_length
|
||||
FROM VIEW_BOM V
|
||||
INNER JOIN PART_MNG P ON P.OBJID = V.PART_OBJID
|
||||
ORDER BY V.PATH2
|
||||
`;
|
||||
|
||||
// TEMPLATE 트리는 SAVED 와 동일한 CTE — 운영판도 mbom_detail 한 테이블에서 가져옴.
|
||||
// 차이: TEMPLATE 은 ORDER_QTY/PRODUCTION_QTY 를 결과에서 표시하지 않음 (재계산 대상).
|
||||
const TEMPLATE_TREE_SQL = `
|
||||
WITH RECURSIVE VIEW_BOM(
|
||||
MBOM_HEADER_OBJID, OBJID, PARENT_OBJID, CHILD_OBJID,
|
||||
PART_OBJID, PART_NO, PART_NAME, QTY, ITEM_QTY, QTY_TEMP,
|
||||
REGDATE, SEQ, STATUS, LEV, PATH, PATH2, CYCLE,
|
||||
UNIT, SUPPLY_TYPE, MAKE_OR_BUY,
|
||||
RAW_MATERIAL_PART_NO, RAW_MATERIAL_SPEC, RAW_MATERIAL, RAW_MATERIAL_SIZE,
|
||||
PROCESSING_VENDOR, PROCESSING_DEADLINE, GRINDING_DEADLINE,
|
||||
REQUIRED_QTY, WRITER, EDITER, EDIT_DATE, REMARK
|
||||
) AS (
|
||||
SELECT A.MBOM_HEADER_OBJID, A.OBJID, A.PARENT_OBJID, A.CHILD_OBJID,
|
||||
A.PART_OBJID, A.PART_NO, A.PART_NAME, A.QTY, A.QTY, A.QTY,
|
||||
A.REGDATE, A.SEQ, A.STATUS, 1,
|
||||
ARRAY [A.CHILD_OBJID::TEXT],
|
||||
ARRAY [LPAD(A.SEQ::TEXT, 10, '0')],
|
||||
FALSE,
|
||||
A.UNIT, A.SUPPLY_TYPE, A.MAKE_OR_BUY,
|
||||
A.RAW_MATERIAL_PART_NO, A.RAW_MATERIAL_SPEC, A.RAW_MATERIAL, A.RAW_MATERIAL_SIZE,
|
||||
A.PROCESSING_VENDOR, A.PROCESSING_DEADLINE, A.GRINDING_DEADLINE,
|
||||
A.REQUIRED_QTY, A.WRITER, A.EDITER, A.EDIT_DATE, A.REMARK
|
||||
FROM MBOM_DETAIL A
|
||||
WHERE 1=1
|
||||
AND (A.PARENT_OBJID IS NULL OR A.PARENT_OBJID = '')
|
||||
AND A.MBOM_HEADER_OBJID = $1
|
||||
AND A.STATUS = 'ACTIVE'
|
||||
UNION ALL
|
||||
SELECT B.MBOM_HEADER_OBJID, B.OBJID, B.PARENT_OBJID, B.CHILD_OBJID,
|
||||
B.PART_OBJID, B.PART_NO, B.PART_NAME, B.QTY, B.QTY, B.QTY,
|
||||
B.REGDATE, B.SEQ, B.STATUS, LEV + 1,
|
||||
PATH || B.CHILD_OBJID::TEXT,
|
||||
PATH2 || LPAD(B.SEQ::TEXT, 10, '0'),
|
||||
B.PARENT_OBJID = ANY(PATH),
|
||||
B.UNIT, B.SUPPLY_TYPE, B.MAKE_OR_BUY,
|
||||
B.RAW_MATERIAL_PART_NO, B.RAW_MATERIAL_SPEC, B.RAW_MATERIAL, B.RAW_MATERIAL_SIZE,
|
||||
B.PROCESSING_VENDOR, B.PROCESSING_DEADLINE, B.GRINDING_DEADLINE,
|
||||
B.REQUIRED_QTY, B.WRITER, B.EDITER, B.EDIT_DATE, B.REMARK
|
||||
FROM MBOM_DETAIL B
|
||||
JOIN VIEW_BOM ON B.PARENT_OBJID = VIEW_BOM.CHILD_OBJID
|
||||
AND VIEW_BOM.MBOM_HEADER_OBJID = B.MBOM_HEADER_OBJID
|
||||
AND B.STATUS = 'ACTIVE'
|
||||
)
|
||||
SELECT
|
||||
V.MBOM_HEADER_OBJID AS bom_report_objid,
|
||||
V.OBJID, V.PARENT_OBJID AS parent_objid, V.CHILD_OBJID AS child_objid,
|
||||
V.PART_OBJID AS part_objid, V.PART_NO AS part_no, V.PART_NAME AS part_name,
|
||||
V.QTY AS qty, V.ITEM_QTY AS item_qty, V.QTY_TEMP AS qty_temp,
|
||||
V.LEV AS level,
|
||||
(SELECT COUNT(*) FROM MBOM_DETAIL WHERE PARENT_OBJID = V.CHILD_OBJID) AS sub_part_cnt,
|
||||
V.SEQ AS seq, V.STATUS AS status,
|
||||
V.UNIT, V.SUPPLY_TYPE AS supply_type, V.MAKE_OR_BUY AS make_or_buy,
|
||||
V.RAW_MATERIAL_PART_NO AS raw_material_no,
|
||||
V.RAW_MATERIAL_SPEC AS raw_material_spec,
|
||||
V.RAW_MATERIAL AS raw_material,
|
||||
V.RAW_MATERIAL_SIZE AS size,
|
||||
V.PROCESSING_VENDOR AS processing_vendor,
|
||||
V.PROCESSING_DEADLINE AS processing_deadline,
|
||||
V.GRINDING_DEADLINE AS grinding_deadline,
|
||||
V.REQUIRED_QTY AS required_qty,
|
||||
NULL::numeric AS order_qty, NULL::numeric AS production_qty,
|
||||
V.WRITER AS writer,
|
||||
TO_CHAR(V.REGDATE, 'YYYY-MM-DD HH24:MI:SS') AS regdate,
|
||||
V.EDITER AS editer,
|
||||
CASE WHEN V.EDIT_DATE IS NOT NULL THEN TO_CHAR(V.EDIT_DATE, 'YYYY-MM-DD HH24:MI:SS') END AS edit_date,
|
||||
V.REMARK AS remark,
|
||||
CASE WHEN V.LEV = 1 THEN V.OBJID END AS root_objid,
|
||||
CASE WHEN V.LEV = 1 THEN V.OBJID END AS sub_root_objid,
|
||||
1 AS leaf,
|
||||
P.SPEC, P.MATERIAL, P.WEIGHT, P.PART_TYPE, P.REVISION, P.MAKER,
|
||||
P.THICKNESS, P.WIDTH, P.HEIGHT, P.OUT_DIAMETER, P.IN_DIAMETER, P.LENGTH,
|
||||
P.SOURCING_CODE, P.HEAT_TREATMENT_HARDNESS, P.HEAT_TREATMENT_METHOD, P.SURFACE_TREATMENT,
|
||||
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = P.UNIT) AS unit_title,
|
||||
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = P.PART_TYPE) AS part_type_title,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('3D_CAD')) AS cu01_cnt,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('2D_DRAWING_CAD')) AS cu02_cnt,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('2D_PDF_CAD')) AS cu03_cnt,
|
||||
COALESCE((SELECT NULLIF(MP.UNIT_QTY, '')::numeric FROM PART_MNG MP WHERE MP.PART_NO = V.RAW_MATERIAL_PART_NO LIMIT 1), 0) AS part_unit_qty,
|
||||
COALESCE((SELECT NULLIF(MP.UNIT_LENGTH, '')::numeric FROM PART_MNG MP WHERE MP.PART_NO = V.RAW_MATERIAL_PART_NO LIMIT 1), 0) AS part_unit_length
|
||||
FROM VIEW_BOM V
|
||||
LEFT JOIN PART_MNG P ON V.PART_OBJID = P.OBJID
|
||||
ORDER BY V.PATH2
|
||||
`;
|
||||
|
||||
// 매퍼 partMng.getBOMTreeList search_type='working' 1:1.
|
||||
// E-BOM 호환 — part_no 컬럼명 충돌 회피 위해 운영판처럼 V.PART_NO 는 PART_OBJID 로 alias.
|
||||
const EBOM_WORKING_TREE_SQL = `
|
||||
WITH RECURSIVE VIEW_BOM(
|
||||
BOM_REPORT_OBJID, OBJID, PARENT_OBJID, CHILD_OBJID,
|
||||
PARENT_PART_NO, PART_NO, LAST_PART_OBJID,
|
||||
QTY, ITEM_QTY, QTY_TEMP, REGDATE, SEQ, STATUS,
|
||||
PART_MNG_NO, PARENT_PART_MNG_NO, LEV, PATH, PATH2, CYCLE
|
||||
) AS (
|
||||
SELECT A.BOM_REPORT_OBJID, A.OBJID, A.PARENT_OBJID, A.CHILD_OBJID,
|
||||
A.PARENT_PART_NO, A.PART_NO, A.LAST_PART_OBJID,
|
||||
A.QTY, A.ITEM_QTY, A.QTY_TEMP, A.REGDATE, A.SEQ, A.STATUS,
|
||||
(SELECT PART_NO FROM PART_MNG P WHERE P.OBJID::varchar = A.PART_NO) AS PART_MNG_NO,
|
||||
(SELECT PART_NO FROM PART_MNG P WHERE P.OBJID::varchar = A.PARENT_PART_NO) AS PARENT_PART_MNG_NO,
|
||||
1,
|
||||
ARRAY [A.CHILD_OBJID::TEXT],
|
||||
ARRAY [LPAD(A.SEQ::TEXT, 10, '0')],
|
||||
FALSE
|
||||
FROM BOM_PART_QTY A
|
||||
WHERE 1=1
|
||||
AND (A.PARENT_OBJID IS NULL OR A.PARENT_OBJID = '')
|
||||
AND A.BOM_REPORT_OBJID = $1
|
||||
AND (A.STATUS NOT IN ('deleting', 'deleted') OR A.STATUS IS NULL)
|
||||
UNION ALL
|
||||
SELECT B.BOM_REPORT_OBJID, B.OBJID, B.PARENT_OBJID, B.CHILD_OBJID,
|
||||
B.PARENT_PART_NO, B.PART_NO, B.LAST_PART_OBJID,
|
||||
B.QTY, B.ITEM_QTY, B.QTY_TEMP, B.REGDATE, B.SEQ, B.STATUS,
|
||||
(SELECT PART_NO FROM PART_MNG P WHERE P.OBJID::varchar = B.PART_NO) AS PART_MNG_NO,
|
||||
(SELECT PART_NO FROM PART_MNG P WHERE P.OBJID::varchar = B.PARENT_PART_NO) AS PARENT_PART_MNG_NO,
|
||||
LEV + 1,
|
||||
PATH || B.CHILD_OBJID::TEXT,
|
||||
PATH2 || LPAD(B.SEQ::TEXT, 10, '0'),
|
||||
B.PARENT_OBJID = ANY(PATH)
|
||||
FROM BOM_PART_QTY B
|
||||
JOIN VIEW_BOM ON B.PARENT_OBJID = VIEW_BOM.CHILD_OBJID
|
||||
AND VIEW_BOM.BOM_REPORT_OBJID = B.BOM_REPORT_OBJID
|
||||
AND (B.STATUS NOT IN ('deleting', 'deleted') OR B.STATUS IS NULL)
|
||||
)
|
||||
SELECT
|
||||
V.BOM_REPORT_OBJID AS bom_report_objid,
|
||||
V.OBJID AS objid,
|
||||
V.PARENT_OBJID AS parent_objid,
|
||||
V.CHILD_OBJID AS child_objid,
|
||||
V.PARENT_PART_NO AS parent_part_no,
|
||||
V.PART_NO AS part_objid,
|
||||
V.LAST_PART_OBJID AS bom_last_part_objid,
|
||||
V.QTY AS qty,
|
||||
V.ITEM_QTY AS item_qty,
|
||||
(CASE WHEN V.STATUS = 'deploy' THEN V.QTY
|
||||
WHEN V.STATUS = 'beforeEdit' THEN V.QTY
|
||||
WHEN V.STATUS != 'editing' AND (V.QTY_TEMP IS NULL OR V.QTY_TEMP = '') THEN V.QTY
|
||||
ELSE COALESCE(V.QTY_TEMP, V.QTY) END) AS qty_temp,
|
||||
V.LEV AS level,
|
||||
(SELECT COUNT(*) FROM BOM_PART_QTY WHERE PARENT_OBJID = V.CHILD_OBJID) AS sub_part_cnt,
|
||||
V.SEQ AS seq, V.STATUS AS status,
|
||||
P.OBJID AS last_part_objid,
|
||||
P.PART_NAME AS part_name,
|
||||
P.PART_NO AS part_no,
|
||||
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CODE_ID = P.UNIT) AS unit_title,
|
||||
P.SPEC, P.MATERIAL, P.WEIGHT, P.REVISION, P.MAKER,
|
||||
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CODE_ID = P.PART_TYPE) AS part_type_title,
|
||||
P.REMARK AS part_remark,
|
||||
P.THICKNESS, P.WIDTH, P.HEIGHT, P.OUT_DIAMETER, P.IN_DIAMETER, P.LENGTH,
|
||||
P.SOURCING_CODE, P.HEAT_TREATMENT_HARDNESS, P.HEAT_TREATMENT_METHOD, P.SURFACE_TREATMENT,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('3D_CAD')) AS cu01_cnt,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('2D_DRAWING_CAD')) AS cu02_cnt,
|
||||
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE P.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('2D_PDF_CAD')) AS cu03_cnt,
|
||||
-- E-BOM 분기는 생산정보가 없으므로 NULL 로 채워 SAVED 와 동일한 키셋 유지
|
||||
NULL::text AS supply_type, NULL::text AS make_or_buy,
|
||||
NULL::text AS raw_material_no, NULL::text AS raw_material_spec,
|
||||
NULL::text AS raw_material, NULL::text AS size,
|
||||
NULL::text AS processing_vendor, NULL::text AS processing_vendor_name,
|
||||
NULL::text AS processing_deadline, NULL::text AS grinding_deadline,
|
||||
NULL::numeric AS required_qty, NULL::numeric AS order_qty, NULL::numeric AS production_qty,
|
||||
NULL::numeric AS stock_qty, NULL::numeric AS shortage_qty,
|
||||
NULL::text AS vendor, NULL::text AS vendor_name,
|
||||
NULL::numeric AS unit_price, NULL::numeric AS processing_unit_price,
|
||||
NULL::numeric AS total_price, NULL::text AS currency,
|
||||
NULL::int AS lead_time, NULL::numeric AS min_order_qty,
|
||||
NULL::text AS writer, NULL::text AS regdate, NULL::text AS editer, NULL::text AS edit_date, NULL::text AS remark
|
||||
FROM VIEW_BOM V
|
||||
INNER JOIN PART_MNG P ON P.OBJID = COALESCE(V.LAST_PART_OBJID, V.PART_NO)
|
||||
ORDER BY V.PATH2
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user