개발관리>E-BOM 조회 — 운영판 1:1 그리드 + 토글 + 품번 상세 + 검색 anchor 정정
(1) 정전개 트리 화면 운영판 wace structureAscendingList.jsp 1:1 정정:
- L1..LMaxLevel 컬럼 — row.lev 와 일치하는 컬럼에만 "*" 표시 (이전엔 품번 표시)
- 별도 품번 컬럼 1개 (모든 행 part_no)
- 3D/2D/PDF — renderType: "folder" (wace fnc_getFolderIcon 1:1)
- 컬럼 운영판 1:1 : 품번/품명/수량/항목수량/3D/2D/PDF/재료/열처리경도/열처리방법/표면처리/메이커/범주이름/비고
- 제거 : 변경일/REV/규격/중량 (운영판 미사용)
(2) 토글 -/+ 버튼 추가 (wace 트리 1:1):
- 첫 컬럼 __toggle — 자식 있는 행만 − / + 표시, 클릭 시 자식 숨김/표시
- collapsedChildIds Set<string> 상태로 접힘 관리
- ancestor 체인: parent_objid → 부모 행 child_objid 추적 (cycle guard)
- 가시 행 필터: ancestor 중 하나라도 collapsed Set 에 있으면 hide → 자손 전체 숨김
- 새 조회 시 collapsed Set 초기화 (모두 펼침)
(3) 품번 셀 클릭 → PartDetailDialog (wace partMngDetailPopUp 1:1):
- row.part_no = part_mng.objid::varchar 이므로 그대로 detail dialog 의 objid 로 전달
- ebom-search 페이지에 PartDetailDialog 임포트 + state
(4) 검색 필터 anchor 정정 (사용자 검증: 1행만 나오고 자식 안 풀림):
- 이전: search_part_no/search_part_name 을 결과 단계 WHERE PM.part_no LIKE ... 로 적용
→ 매칭 행 1개만 살아남고 자식 잘림
- 정정: anchor 단계에서 매칭된 PART 가 들어있는 bom_report_objid 전체를 startWhere 로
→ 재귀 CTE 가 자식 모두 풀어냄 (운영판 1:1)
- search_level (1~5) 은 결과 단계 유지 (트리 깊이 제한)
- ascending / ascendingForExcel 양쪽 동일 패턴
(5) ascending SELECT 풀 컬럼 보강:
- 추가 : item_qty(p_qty), heat_treatment_hardness/method, surface_treatment,
maker, part_type, part_type_title (comm_code.code_name)
- TREE CTE 컬럼에 item_qty 추가
- BomTreeRow 타입 동기 (lib/api/devBom.ts)
(6) 상태변경 시 확정일(DEPLOY_DATE) 처리 — 사용자 요청:
- status = 'Y' 변경 시 DEPLOY_DATE = TO_CHAR(NOW(), 'YYYY-MM-DD') 채움 (varchar)
- 'N' 변경 시 기존 DEPLOY_DATE 보존
- $5 prepared statement 타입 추론 충돌 (varchar vs unknown) → $5::varchar 명시 캐스팅
- STATUS_TITLE 매핑은 운영판 1:1 — CREATE/CHANGEDESIGN/DEPLOY 만 라벨, Y/N 등은 raw 표시
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -163,15 +163,18 @@ export async function getByObjid(objid: string) {
|
||||
|
||||
export async function updateStatus(userId: string, objid: string, body: BomReportStatusBody) {
|
||||
if (!body.status) throw new Error("status는 필수입니다.");
|
||||
// RPS 정책: 상태를 'Y' 로 변경한 시점을 확정일(DEPLOY_DATE) 로 기록.
|
||||
// 'N' 으로 변경 시는 기존 DEPLOY_DATE 보존 (마지막 확정 기록 유지).
|
||||
const sql = `
|
||||
UPDATE PART_BOM_REPORT
|
||||
SET PRODUCT_CD = COALESCE($1, PRODUCT_CD),
|
||||
PART_NO = COALESCE($2, PART_NO),
|
||||
PART_NAME = COALESCE($3, PART_NAME),
|
||||
REVISION = COALESCE($4, REVISION),
|
||||
STATUS = $5,
|
||||
editer = $6,
|
||||
edit_date = NOW()
|
||||
SET PRODUCT_CD = COALESCE($1, PRODUCT_CD),
|
||||
PART_NO = COALESCE($2, PART_NO),
|
||||
PART_NAME = COALESCE($3, PART_NAME),
|
||||
REVISION = COALESCE($4, REVISION),
|
||||
STATUS = $5::varchar,
|
||||
DEPLOY_DATE = CASE WHEN UPPER($5::varchar) = 'Y' THEN TO_CHAR(NOW(), 'YYYY-MM-DD') ELSE DEPLOY_DATE END,
|
||||
editer = $6,
|
||||
edit_date = NOW()
|
||||
WHERE OBJID = $7
|
||||
`;
|
||||
const r = await getPool().query(sql, [
|
||||
@@ -216,7 +219,9 @@ export async function ascending(filter: BomTreeFilter) {
|
||||
const conds: string[] = [];
|
||||
let idx = 1;
|
||||
|
||||
// 시작점 필터: 명시적 bom_report_objid 또는 part_bom_report 필터로 좁힘
|
||||
// 시작점 필터 (anchor): 명시적 bom_report_objid 또는 part_bom_report 필터로 좁힘.
|
||||
// 품번/품명 검색은 결과 필터가 아니라 매칭된 PART 가 들어있는 BOM_REPORT 전체를 anchor 로
|
||||
// 잡아야 트리 자식들이 같이 풀림 (wace 운영판 동작 1:1).
|
||||
if (filter.bom_report_objid) {
|
||||
conds.push(`BP.bom_report_objid = $${idx++}`);
|
||||
params.push(filter.bom_report_objid);
|
||||
@@ -226,18 +231,32 @@ export async function ascending(filter: BomTreeFilter) {
|
||||
if (filter.unit_code) { subConds.push(`unit_code = $${idx++}`); params.push(filter.unit_code); }
|
||||
conds.push(`BP.bom_report_objid IN (SELECT objid FROM part_bom_report WHERE ${subConds.join(" AND ")})`);
|
||||
}
|
||||
const startWhere = conds.length ? conds.join(" AND ") : "1=1";
|
||||
|
||||
// PART 검색 필터는 결과 단계 적용
|
||||
const finalConds: string[] = [];
|
||||
if (filter.search_part_no) {
|
||||
finalConds.push(`UPPER(PM.part_no) LIKE UPPER($${idx++})`);
|
||||
conds.push(`BP.bom_report_objid IN (
|
||||
SELECT DISTINCT BQ.bom_report_objid FROM bom_part_qty BQ
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM part_mng PMS
|
||||
WHERE PMS.objid::varchar = BQ.part_no
|
||||
AND UPPER(PMS.part_no) LIKE UPPER($${idx++})
|
||||
)
|
||||
)`);
|
||||
params.push(`%${filter.search_part_no}%`);
|
||||
}
|
||||
if (filter.search_part_name) {
|
||||
finalConds.push(`UPPER(PM.part_name) LIKE UPPER($${idx++})`);
|
||||
conds.push(`BP.bom_report_objid IN (
|
||||
SELECT DISTINCT BQ.bom_report_objid FROM bom_part_qty BQ
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM part_mng PMS
|
||||
WHERE PMS.objid::varchar = BQ.part_no
|
||||
AND UPPER(PMS.part_name) LIKE UPPER($${idx++})
|
||||
)
|
||||
)`);
|
||||
params.push(`%${filter.search_part_name}%`);
|
||||
}
|
||||
const startWhere = conds.length ? conds.join(" AND ") : "1=1";
|
||||
|
||||
// 결과 단계 필터 — search_level 만 (트리 깊이 제한)
|
||||
const finalConds: string[] = [];
|
||||
if (filter.search_level) {
|
||||
finalConds.push(`T.lev <= $${idx++}::int`);
|
||||
params.push(filter.search_level);
|
||||
@@ -245,25 +264,29 @@ export async function ascending(filter: BomTreeFilter) {
|
||||
const finalWhere = finalConds.length ? `WHERE ${finalConds.join(" AND ")}` : "";
|
||||
|
||||
const sql = `
|
||||
WITH RECURSIVE TREE(bom_report_objid, objid, parent_objid, child_objid, part_no, qty, seq, status, lev, path, cycle) AS (
|
||||
WITH RECURSIVE TREE(bom_report_objid, objid, parent_objid, child_objid, part_no, qty, item_qty, seq, status, lev, path, cycle) AS (
|
||||
SELECT BP.bom_report_objid, BP.objid, BP.parent_objid, BP.child_objid,
|
||||
BP.part_no, BP.qty, BP.seq, BP.status,
|
||||
BP.part_no, BP.qty, BP.item_qty, BP.seq, BP.status,
|
||||
1, ARRAY[BP.objid::varchar], FALSE
|
||||
FROM bom_part_qty BP
|
||||
WHERE (BP.parent_objid IS NULL OR BP.parent_objid = '')
|
||||
AND ${startWhere}
|
||||
UNION ALL
|
||||
SELECT B.bom_report_objid, B.objid, B.parent_objid, B.child_objid,
|
||||
B.part_no, B.qty, B.seq, B.status,
|
||||
B.part_no, B.qty, B.item_qty, B.seq, B.status,
|
||||
T.lev + 1, T.path || B.objid::varchar, B.objid::varchar = ANY(T.path)
|
||||
FROM bom_part_qty B
|
||||
JOIN TREE T ON B.parent_objid = T.child_objid AND NOT T.cycle
|
||||
)
|
||||
SELECT T.bom_report_objid, T.objid, T.parent_objid, T.child_objid, T.part_no, T.qty, T.seq, T.status,
|
||||
T.lev, T.path,
|
||||
T.item_qty AS p_qty,
|
||||
PM.part_no AS pm_part_no,
|
||||
PM.part_name AS pm_part_name,
|
||||
PM.spec, PM.material, PM.weight, PM.remark,
|
||||
PM.heat_treatment_hardness, PM.heat_treatment_method, PM.surface_treatment,
|
||||
PM.maker, PM.part_type,
|
||||
CC.code_name AS part_type_title,
|
||||
PM.edit_date,
|
||||
PM.eo_no, PM.revision,
|
||||
(SELECT COUNT(1) FROM attach_file_info F WHERE F.target_objid = PM.objid::varchar AND F.doc_type='3D_CAD') AS cu01_cnt,
|
||||
@@ -271,7 +294,8 @@ export async function ascending(filter: BomTreeFilter) {
|
||||
(SELECT COUNT(1) FROM attach_file_info F WHERE F.target_objid = PM.objid::varchar AND F.doc_type='2D_PDF_CAD') AS cu03_cnt,
|
||||
(SELECT COALESCE(MAX(lev), 0) FROM TREE) AS max_level
|
||||
FROM TREE T
|
||||
LEFT JOIN part_mng PM ON T.part_no = PM.objid::varchar
|
||||
LEFT JOIN part_mng PM ON T.part_no = PM.objid::varchar
|
||||
LEFT JOIN comm_code CC ON CC.code_id = PM.part_type
|
||||
${finalWhere}
|
||||
ORDER BY T.path
|
||||
`;
|
||||
@@ -302,18 +326,33 @@ function buildAscendingExcelSql(filter: BomTreeFilter, startIdx: number) {
|
||||
if (filter.unit_code) { subConds.push(`unit_code = $${idx++}`); params.push(filter.unit_code); }
|
||||
conds.push(`BP.bom_report_objid IN (SELECT objid FROM part_bom_report WHERE ${subConds.join(" AND ")})`);
|
||||
}
|
||||
const startWhere = conds.length ? conds.join(" AND ") : "1=1";
|
||||
|
||||
const finalConds: string[] = [];
|
||||
// 품번/품명 검색 — anchor 단계로 (매칭 PART 가 들어있는 BOM 전체 트리 표시)
|
||||
if (filter.search_part_no) {
|
||||
finalConds.push(`UPPER(PM.part_no) LIKE UPPER($${idx++})`);
|
||||
conds.push(`BP.bom_report_objid IN (
|
||||
SELECT DISTINCT BQ.bom_report_objid FROM bom_part_qty BQ
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM part_mng PMS
|
||||
WHERE PMS.objid::varchar = BQ.part_no
|
||||
AND UPPER(PMS.part_no) LIKE UPPER($${idx++})
|
||||
)
|
||||
)`);
|
||||
params.push(`%${filter.search_part_no}%`);
|
||||
}
|
||||
if (filter.search_part_name) {
|
||||
finalConds.push(`UPPER(PM.part_name) LIKE UPPER($${idx++})`);
|
||||
conds.push(`BP.bom_report_objid IN (
|
||||
SELECT DISTINCT BQ.bom_report_objid FROM bom_part_qty BQ
|
||||
WHERE EXISTS (
|
||||
SELECT 1 FROM part_mng PMS
|
||||
WHERE PMS.objid::varchar = BQ.part_no
|
||||
AND UPPER(PMS.part_name) LIKE UPPER($${idx++})
|
||||
)
|
||||
)`);
|
||||
params.push(`%${filter.search_part_name}%`);
|
||||
}
|
||||
const finalWhere = finalConds.length ? `WHERE ${finalConds.join(" AND ")}` : "";
|
||||
const startWhere = conds.length ? conds.join(" AND ") : "1=1";
|
||||
|
||||
// 엑셀은 search_level 적용 안 함 (전체 트리 다운로드가 자연스러움). 필요 시 추가.
|
||||
const finalWhere = "";
|
||||
|
||||
return { params, startWhere, finalWhere };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user