제작수량 오류 등 수정

This commit is contained in:
2025-11-26 11:58:52 +09:00
parent a3d0d09355
commit 2f9c7bbaf2
6 changed files with 369 additions and 62 deletions

View File

@@ -386,9 +386,32 @@ function fn_saveMbom() {
if(data && data.result === "success") {
alert("M-BOM이 저장되었습니다.");
// 부모 창 새로고침
if(window.opener && window.opener.fn_search) {
window.opener.fn_search();
// 부모 창(M-BOM 목록) 새로고침
try {
// window.opener가 있으면 (팝업으로 열린 경우)
if(window.opener && !window.opener.closed) {
console.log("window.opener 찾음");
if(window.opener.fn_search) {
console.log("window.opener.fn_search 호출");
window.opener.fn_search();
} else if(window.opener.location) {
console.log("window.opener.location.reload 호출");
window.opener.location.reload();
}
}
// window.top이 현재 창이 아니면 (iframe인 경우)
else if(window.top !== window.self && window.top.opener && !window.top.opener.closed) {
console.log("window.top.opener 찾음");
if(window.top.opener.fn_search) {
console.log("window.top.opener.fn_search 호출");
window.top.opener.fn_search();
} else if(window.top.opener.location) {
console.log("window.top.opener.location.reload 호출");
window.top.opener.location.reload();
}
}
} catch(e) {
console.error("부모 창 새로고침 실패:", e);
}
// 현재 창 닫기

View File

@@ -425,13 +425,13 @@ function fn_initGrid() {
step: 1
},
formatter: function(cell) {
// 초기값은 소재발주수량과 동일
// 저장된 값이 있으면 그대로 사용, 없으면 항목수량 × 수주수량으로 계산
var value = cell.getValue();
if(value === undefined || value === null || value === '' || value === 0) {
var data = cell.getRow().getData();
// ORDER_QTY 값을 그대로 사용
value = data.ORDER_QTY || 0;
var itemQty = parseFloat(data.ITEM_QTY) || 0;
value = itemQty * projectQuantity;
// 실제 데이터에도 저장 (getMbomTreeData에서 사용)
cell.getRow().update({PRODUCTION_QTY: value}, false);

View File

@@ -55,6 +55,9 @@ public class PartMngController {
@Autowired
CommonService commonService;
@Autowired
com.pms.service.ProductionPlanningService productionPlanningService;
@Autowired
public void setProjectConceptService(ProjectConceptService service){
@@ -1177,12 +1180,32 @@ public class PartMngController {
List bomTreeList = null;
try{
Map info = partMngService.getBOMStructureStandardInfo(request,paramMap);
// search_type 파라미터를 info에 복사 (adding 상태 조회를 위해)
if(paramMap.containsKey("search_type")) {
info.put("search_type", paramMap.get("search_type"));
String objId = CommonUtils.checkNull((String)paramMap.get("objId"));
// M-BOM인지 확인 (MBOM_HEADER 테이블에 존재하는지 체크)
boolean isMbom = productionPlanningService.isMbomHeader(objId);
if(isMbom) {
// M-BOM 트리 조회
System.out.println("M-BOM 트리 조회: " + objId);
bomTreeList = productionPlanningService.getSavedMbomTreeList(objId);
} else {
// E-BOM 트리 조회 (기존 로직)
System.out.println("E-BOM 트리 조회: " + objId);
Map info = partMngService.getBOMStructureStandardInfo(request,paramMap);
// info가 null이 아닐 때만 처리
if(info != null) {
// search_type 파라미터를 info에 복사 (adding 상태 조회를 위해)
if(paramMap.containsKey("search_type")) {
info.put("search_type", paramMap.get("search_type"));
}
bomTreeList = partMngService.getBOMPartTreeList(info);
} else {
System.out.println("E-BOM 정보를 찾을 수 없음: " + objId);
bomTreeList = new ArrayList<Map>();
}
}
bomTreeList = partMngService.getBOMPartTreeList(info);
}catch(Exception e){
e.printStackTrace();
}

View File

@@ -1122,7 +1122,7 @@ public class ProductionPlanningController extends BaseService {
System.out.println("MBOM_STATUS: " + projectInfo.get("MBOM_STATUS"));
String bomReportObjid = "";
boolean isSavedMbom = false;
String bomDataType = ""; // "SAVED", "TEMPLATE", "ASSIGNED_EBOM", "ASSIGNED_MBOM"
// 1. 먼저 저장된 M-BOM이 있는지 확인 (MBOM_HEADER 테이블)
Map<String, Object> savedMbomParam = new HashMap<>();
@@ -1132,7 +1132,7 @@ public class ProductionPlanningController extends BaseService {
if(savedMbom != null && savedMbom.get("OBJID") != null) {
// 저장된 M-BOM이 있으면 해당 M-BOM 사용
bomReportObjid = CommonUtils.checkNull(savedMbom.get("OBJID"));
isSavedMbom = true;
bomDataType = "SAVED";
System.out.println("저장된 M-BOM 사용: " + bomReportObjid);
} else {
// 저장된 M-BOM이 없으면 할당된 BOM 정보 사용
@@ -1142,9 +1142,11 @@ public class ProductionPlanningController extends BaseService {
if("EBOM".equals(sourceBomType) && !"".equals(sourceEbomObjId)) {
bomReportObjid = sourceEbomObjId;
bomDataType = "ASSIGNED_EBOM";
System.out.println("할당된 E-BOM 사용: " + bomReportObjid);
} else if("MBOM".equals(sourceBomType) && !"".equals(sourceMbomObjId)) {
bomReportObjid = sourceMbomObjId;
bomDataType = "ASSIGNED_MBOM";
System.out.println("할당된 M-BOM 사용: " + bomReportObjid);
} else {
// 할당된 BOM도 없으면 Machine 이외 제품은 템플릿 확인
@@ -1160,7 +1162,7 @@ public class ProductionPlanningController extends BaseService {
if(template != null && !template.isEmpty()) {
bomReportObjid = CommonUtils.checkNull(template.get("TEMPLATE_HEADER_OBJID"));
isSavedMbom = true; // 템플릿도 MBOM_DETAIL에서 조회
bomDataType = "TEMPLATE";
System.out.println("템플릿 M-BOM 사용: " + bomReportObjid);
} else {
bomReportObjid = CommonUtils.checkNull(projectInfo.get("BOM_REPORT_OBJID"));
@@ -1177,21 +1179,34 @@ public class ProductionPlanningController extends BaseService {
List<Map<String, Object>> mbomDetailList = null;
if(!"".equals(bomReportObjid)) {
if(isSavedMbom) {
// 저장된 M-BOM: MBOM_DETAIL 테이블에서 조회
if("SAVED".equals(bomDataType)) {
// 저장된 M-BOM: MBOM_DETAIL 테이블에서 조회 (ORDER_QTY, PRODUCTION_QTY 포함)
Map<String, Object> mbomParam = new HashMap<>();
mbomParam.put("mbomHeaderObjid", bomReportObjid);
List tempList = commonService.selectList("productionplanning.getSavedMbomTreeList", request, mbomParam);
// MyBatis resultType="map"은 소문자로 반환하므로 대문자로 변환 필요
mbomDetailList = (List<Map<String, Object>>) (List<?>) CommonUtils.keyChangeUpperList(tempList);
System.out.println("저장된 M-BOM 조회 - mbomDetailList size: " + (mbomDetailList != null ? mbomDetailList.size() : 0));
} else {
// 할당된 E-BOM/M-BOM: BOM_PART_QTY 테이블에서 조회
} else if("TEMPLATE".equals(bomDataType)) {
// 템플릿 M-BOM: MBOM_DETAIL 테이블에서 조회 (ORDER_QTY, PRODUCTION_QTY 제외)
Map<String, Object> mbomParam = new HashMap<>();
mbomParam.put("mbomHeaderObjid", bomReportObjid);
List tempList = commonService.selectList("productionplanning.getMbomTemplateDetails", request, mbomParam);
mbomDetailList = (List<Map<String, Object>>) (List<?>) CommonUtils.keyChangeUpperList(tempList);
System.out.println("템플릿 M-BOM 조회 - mbomDetailList size: " + (mbomDetailList != null ? mbomDetailList.size() : 0));
} else if("ASSIGNED_EBOM".equals(bomDataType)) {
// 할당된 E-BOM: BOM_PART_QTY 테이블에서 조회
Map<String, Object> bomParam = new HashMap<>();
bomParam.put("bomReportObjId", bomReportObjid);
bomParam.put("search_type", "working");
mbomDetailList = commonService.selectList("partMng.getBOMTreeList", request, bomParam);
System.out.println("할당된 BOM 조회 - mbomDetailList size: " + (mbomDetailList != null ? mbomDetailList.size() : 0));
System.out.println("할당된 E-BOM 조회 - mbomDetailList size: " + (mbomDetailList != null ? mbomDetailList.size() : 0));
} else if("ASSIGNED_MBOM".equals(bomDataType)) {
// 할당된 M-BOM: MBOM_DETAIL 테이블에서 조회 (ORDER_QTY, PRODUCTION_QTY 포함)
Map<String, Object> mbomParam = new HashMap<>();
mbomParam.put("mbomHeaderObjid", bomReportObjid);
List tempList = commonService.selectList("productionplanning.getSavedMbomTreeList", request, mbomParam);
mbomDetailList = (List<Map<String, Object>>) (List<?>) CommonUtils.keyChangeUpperList(tempList);
System.out.println("할당된 M-BOM 조회 - mbomDetailList size: " + (mbomDetailList != null ? mbomDetailList.size() : 0));
}
}

View File

@@ -2968,16 +2968,13 @@
''
) AS MBOM_PART_NO,
'1.0' AS MBOM_VERSION,
-- M-BOM 저장일: 새 MBOM_HEADER 테이블에서 조회
COALESCE(
(SELECT TO_CHAR(MH.REGDATE, 'YYYY-MM-DD')
FROM MBOM_HEADER MH
WHERE MH.PROJECT_OBJID = PM.OBJID::VARCHAR
AND MH.STATUS = 'Y'
ORDER BY MH.REGDATE DESC
LIMIT 1),
TO_CHAR(PM.REGDATE, 'YYYY-MM-DD')
) AS MBOM_REGDATE
-- M-BOM 저장일: 새 MBOM_HEADER 테이블에서 조회 (수정일 우선, 없으면 등록일)
(SELECT TO_CHAR(COALESCE(MH.EDIT_DATE, MH.REGDATE), 'YYYY-MM-DD')
FROM MBOM_HEADER MH
WHERE MH.PROJECT_OBJID = PM.OBJID::VARCHAR
AND MH.STATUS = 'Y'
ORDER BY COALESCE(MH.EDIT_DATE, MH.REGDATE) DESC
LIMIT 1) AS MBOM_REGDATE
FROM
PROJECT_MGMT PM
LEFT JOIN CONTRACT_MGMT CM ON PM.CONTRACT_OBJID = CM.OBJID
@@ -3466,34 +3463,190 @@
<!-- 템플릿 M-BOM의 상세 항목 조회 -->
<select id="getMbomTemplateDetails" parameterType="map" resultType="com.pms.common.UpperKeyMap">
SELECT
MD.OBJID,
MD.MBOM_HEADER_OBJID,
MD.PARENT_OBJID,
MD.CHILD_OBJID,
MD.SEQ,
MD.LEVEL,
MD.PART_OBJID,
MD.PART_NO,
MD.PART_NAME,
MD.QTY,
MD.UNIT,
MD.SUPPLY_TYPE,
MD.MAKE_OR_BUY,
MD.RAW_MATERIAL_PART_NO,
MD.RAW_MATERIAL_SPEC,
MD.RAW_MATERIAL,
MD.RAW_MATERIAL_SIZE,
MD.PROCESSING_VENDOR,
MD.PROCESSING_DEADLINE,
MD.GRINDING_DEADLINE,
MD.REQUIRED_QTY,
-- ORDER_QTY, PRODUCTION_QTY는 새 프로젝트에서 재계산되므로 제외
MD.REMARK
FROM MBOM_DETAIL MD
WHERE MD.MBOM_HEADER_OBJID = #{mbomHeaderObjid}
AND MD.STATUS = 'ACTIVE'
ORDER BY MD.SEQ, MD.LEVEL
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 [A.SEQ::TEXT],
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 = #{mbomHeaderObjid}
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||B.SEQ::TEXT,
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,
V.CHILD_OBJID,
V.PART_OBJID,
V.PART_OBJID AS LAST_PART_OBJID,
V.PART_OBJID AS BOM_LAST_PART_OBJID,
V.PART_NO,
V.PART_NAME,
V.QTY,
V.ITEM_QTY,
V.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,
-- M-BOM 전용 필드
V.UNIT,
V.SUPPLY_TYPE,
V.MAKE_OR_BUY,
V.RAW_MATERIAL_PART_NO AS RAW_MATERIAL_NO,
V.RAW_MATERIAL_SPEC,
V.RAW_MATERIAL,
V.RAW_MATERIAL_SIZE AS SIZE,
V.PROCESSING_VENDOR,
V.PROCESSING_DEADLINE,
V.GRINDING_DEADLINE,
V.REQUIRED_QTY,
-- ORDER_QTY, PRODUCTION_QTY는 새 프로젝트에서 재계산되므로 제외
V.WRITER,
TO_CHAR(V.REGDATE, 'YYYY-MM-DD HH24:MI:SS') AS REGDATE,
V.EDITER,
CASE WHEN V.EDIT_DATE IS NOT NULL THEN TO_CHAR(V.EDIT_DATE, 'YYYY-MM-DD HH24:MI:SS') ELSE NULL END AS EDIT_DATE,
V.REMARK,
-- E-BOM 호환 필드
NULL AS PARENT_PART_NO,
NULL AS CONTRACT_OBJID,
CASE WHEN V.LEV = 1 THEN V.OBJID ELSE NULL END AS ROOT_OBJID,
CASE WHEN V.LEV = 1 THEN V.OBJID ELSE NULL END AS SUB_ROOT_OBJID,
1 AS LEAF,
-- PART_MNG 테이블에서 추가 정보 (3D/2D/PDF, 재료, 열처리 등)
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,
V.LEV
FROM VIEW_BOM V
LEFT JOIN PART_MNG P ON V.PART_OBJID = P.OBJID
ORDER BY V.PATH2
</select>
<!-- M-BOM 할당 정보 조회 -->
@@ -3942,4 +4095,13 @@
ORDER BY V.PATH2
</select>
<!-- MBOM_HEADER 테이블에 OBJID 존재 여부 확인 -->
<select id="checkMbomHeaderExists" parameterType="map" resultType="int">
/* productionplanning.checkMbomHeaderExists - M-BOM 존재 확인 */
SELECT COUNT(*)
FROM MBOM_HEADER
WHERE OBJID = #{objid}
AND STATUS = 'Y'
</select>
</mapper>

View File

@@ -1128,7 +1128,7 @@ public class ProductionPlanningService {
mbomPrefix = "M-" + cleanPartNo + "-" + dateStr;
} else if("MBOM".equals(sourceBomType)) {
// M-BOM 기준: 기존 M-BOM 품번에서 날짜/순번 부분 제거 후 새 날짜 추가
// 예: M-000AN014000-251124-01 → M-000AN014000 → M-000AN014000-251124
// 예: M-000AN014000-251124-01 → M-000AN014000 → M-000AN014000-251126-01
String basePart = baseBomPartNo;
@@ -1138,9 +1138,10 @@ public class ProductionPlanningService {
basePart = "M-" + basePart.substring(4);
}
// 마지막 두 개의 "-XX" 부분 제거 (날짜-순번)
// 여러 번 중복된 경우를 대비해 반복 제거
for(int i = 0; i < 2; i++) {
// 마지막의 모든 날짜-순번 패턴 제거 (중복 복사 대응)
// 예: M-000AN014000-251124-01-251125-01 → M-000AN014000
// 날짜(6자리) 또는 순번(2자리)가 나오지 않을 때까지 반복
while(true) {
int lastDashIndex = basePart.lastIndexOf("-");
if(lastDashIndex > 0) {
String suffix = basePart.substring(lastDashIndex + 1);
@@ -1150,6 +1151,8 @@ public class ProductionPlanningService {
} else {
break;
}
} else {
break;
}
}
@@ -1536,4 +1539,85 @@ public class ProductionPlanningService {
return result;
}
/**
* MBOM_HEADER 테이블에 해당 OBJID가 존재하는지 확인
* @param objId
* @return
*/
public boolean isMbomHeader(String objId) {
SqlSession sqlSession = null;
boolean result = false;
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("objid", objId);
System.out.println("========== isMbomHeader 체크 시작 ==========");
System.out.println("objId: [" + objId + "]");
System.out.println("objId length: " + (objId != null ? objId.length() : "null"));
Integer count = sqlSession.selectOne("productionplanning.checkMbomHeaderExists", paramMap);
result = (count != null && count > 0);
System.out.println("count: " + count);
System.out.println("isMbomHeader result: " + result);
System.out.println("========== isMbomHeader 체크 종료 ==========");
} catch(Exception e) {
System.out.println("isMbomHeader 에러 발생:");
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return result;
}
/**
* M-BOM 트리 조회 (MBOM_DETAIL 테이블)
* @param mbomHeaderObjid
* @return
*/
public List<Map> getSavedMbomTreeList(String mbomHeaderObjid) {
SqlSession sqlSession = null;
List<Map> resultList = new ArrayList<>();
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("mbomHeaderObjid", mbomHeaderObjid);
resultList = sqlSession.selectList("productionplanning.getSavedMbomTreeList", paramMap);
// 대문자 변환 (JSP에서 사용하기 위해)
resultList = CommonUtils.keyChangeUpperList(resultList);
// MAX_LEVEL 계산
int maxLevel = 0;
for(Map item : resultList) {
Integer level = (Integer) item.get("LEVEL");
if(level != null && level > maxLevel) {
maxLevel = level;
}
}
// 모든 항목에 MAX_LEVEL 추가
for(Map item : resultList) {
item.put("MAX_LEVEL", maxLevel);
}
System.out.println("getSavedMbomTreeList - mbomHeaderObjid: " + mbomHeaderObjid + ", count: " + resultList.size() + ", maxLevel: " + maxLevel);
} catch(Exception e) {
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return resultList;
}
}