m-bom 추가, 삭제, 변경

This commit is contained in:
2025-11-25 13:10:30 +09:00
parent 93d62035cc
commit 9a2bd56d09
6 changed files with 1003 additions and 463 deletions

View File

@@ -3637,86 +3637,230 @@
<!-- 저장된 M-BOM 트리 조회 (MBOM_DETAIL 테이블) -->
<select id="getSavedMbomTreeList" parameterType="map" resultType="com.pms.common.UpperKeyMap">
SELECT
MD.OBJID,
MD.MBOM_HEADER_OBJID AS BOM_REPORT_OBJID,
MD.PARENT_OBJID,
MD.CHILD_OBJID,
MD.SEQ,
MD.LEVEL,
MD.PART_OBJID,
MD.PART_NO,
MD.PART_NAME,
MD.QTY,
MD.QTY AS QTY_TEMP,
MD.QTY AS ITEM_QTY,
MD.UNIT,
-- 생산 정보
MD.SUPPLY_TYPE,
MD.MAKE_OR_BUY,
MD.RAW_MATERIAL_PART_NO AS RAW_MATERIAL_NO, -- JSP 그리드 필드명에 맞춤
MD.RAW_MATERIAL_SPEC,
MD.RAW_MATERIAL,
MD.RAW_MATERIAL_SIZE AS SIZE, -- JSP 그리드 필드명에 맞춤
MD.PROCESSING_VENDOR,
MD.PROCESSING_DEADLINE,
MD.GRINDING_DEADLINE,
MD.REQUIRED_QTY,
MD.ORDER_QTY,
MD.PRODUCTION_QTY,
MD.STOCK_QTY,
MD.SHORTAGE_QTY,
-- 구매 정보
MD.VENDOR,
MD.UNIT_PRICE,
MD.TOTAL_PRICE,
MD.CURRENCY,
MD.LEAD_TIME,
MD.MIN_ORDER_QTY,
-- 기타
MD.STATUS,
MD.WRITER,
TO_CHAR(MD.REGDATE, 'YYYY-MM-DD HH24:MI:SS') AS REGDATE,
MD.EDITER,
CASE WHEN MD.EDIT_DATE IS NOT NULL THEN TO_CHAR(MD.EDIT_DATE, 'YYYY-MM-DD HH24:MI:SS') ELSE NULL END AS EDIT_DATE,
MD.REMARK,
-- E-BOM 호환을 위한 추가 컬럼
MD.PART_OBJID AS LAST_PART_OBJID,
MD.PART_OBJID AS BOM_LAST_PART_OBJID,
NULL AS PARENT_PART_NO,
NULL AS CONTRACT_OBJID,
0 AS SUB_PART_CNT,
CASE WHEN MD.LEVEL = 1 THEN MD.OBJID ELSE NULL END AS ROOT_OBJID,
CASE WHEN MD.LEVEL = 1 THEN MD.OBJID ELSE NULL END AS SUB_ROOT_OBJID,
1 AS LEAF,
-- PART_MNG 테이블에서 추가 정보 조회
PM.SPEC,
PM.MATERIAL,
PM.WEIGHT,
PM.PART_TYPE,
PM.REMARK,
PM.REVISION,
PM.MAKER,
PM.THICKNESS,
PM.WIDTH,
PM.HEIGHT,
PM.OUT_DIAMETER,
PM.IN_DIAMETER,
PM.LENGTH,
PM.SOURCING_CODE,
PM.HEAT_TREATMENT_HARDNESS,
PM.HEAT_TREATMENT_METHOD,
PM.SURFACE_TREATMENT,
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = PM.UNIT) AS UNIT_TITLE,
(SELECT CODE_NAME FROM COMM_CODE CC WHERE CC.CODE_ID = PM.PART_TYPE) AS PART_TYPE_TITLE,
(SELECT COUNT(1) FROM ATTACH_FILE_INFO F WHERE PM.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 PM.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 PM.OBJID = F.TARGET_OBJID AND F.STATUS = 'Active' AND F.DOC_TYPE IN ('2D_PDF_CAD')) AS CU03_CNT
FROM MBOM_DETAIL MD
LEFT JOIN PART_MNG PM ON MD.PART_OBJID = PM.OBJID
WHERE MD.MBOM_HEADER_OBJID = #{mbomHeaderObjid}
AND MD.STATUS = 'ACTIVE'
ORDER BY MD.SEQ
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,
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.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.ORDER_QTY,
A.PRODUCTION_QTY,
A.STOCK_QTY,
A.SHORTAGE_QTY,
A.VENDOR,
A.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 = #{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.ORDER_QTY,
B.PRODUCTION_QTY,
B.STOCK_QTY,
B.SHORTAGE_QTY,
B.VENDOR,
B.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,
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,
V.ORDER_QTY,
V.PRODUCTION_QTY,
V.STOCK_QTY,
V.SHORTAGE_QTY,
V.VENDOR,
V.UNIT_PRICE,
V.TOTAL_PRICE,
V.CURRENCY,
V.LEAD_TIME,
V.MIN_ORDER_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 테이블에서 추가 정보
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
INNER JOIN PART_MNG P ON P.OBJID = V.PART_OBJID
WHERE 1=1
ORDER BY V.PATH2
</select>
</mapper>

View File

@@ -1007,4 +1007,486 @@ public class ProductionPlanningService {
return result;
}
/**
* BOM 할당 정보 저장
*/
public boolean saveBomAssignment(HttpServletRequest request, Map<String, Object> paramMap) {
SqlSession sqlSession = null;
boolean result = false;
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
int updateCount = sqlSession.update("productionplanning.saveBomAssignment", paramMap);
result = (updateCount > 0);
sqlSession.commit();
} catch(Exception e) {
if(sqlSession != null) {
sqlSession.rollback();
}
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return result;
}
/**
* M-BOM 할당 정보 조회
*/
public Map<String, Object> getMbomAssignment(String projectObjId) {
SqlSession sqlSession = null;
Map<String, Object> result = null;
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("projectObjId", projectObjId);
result = sqlSession.selectOne("productionplanning.getMbomAssignment", paramMap);
} catch(Exception e) {
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return result;
}
/**
* M-BOM 품번 생성
* E-BOM 기준: M-{E-BOM품번}-YYMMDD-01
* M-BOM 기준: {기존품번앞부분}-YYMMDD-01
*/
public String generateMbomNo(HttpServletRequest request, String sourceBomType, String baseBomPartNo) {
SqlSession sqlSession = null;
String mbomNo = "";
try {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
// 현재 날짜 (YYMMDD)
java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("yyMMdd");
String dateStr = sdf.format(new java.util.Date());
String mbomPrefix = "";
if("EBOM".equals(sourceBomType)) {
// E-BOM 기준: M-{E-BOM품번}-YYMMDD
// E-BOM 품번이 이미 M-으로 시작하면 중복 방지
String cleanPartNo = baseBomPartNo;
if(cleanPartNo.startsWith("M-")) {
cleanPartNo = cleanPartNo.substring(2); // "M-" 제거
}
mbomPrefix = "M-" + cleanPartNo + "-" + dateStr;
} else if("MBOM".equals(sourceBomType)) {
// M-BOM 기준: 기존 M-BOM 품번에서 날짜/순번 부분 제거 후 새 날짜 추가
// 예: M-000AN014000-251124-01 → M-000AN014000 → M-000AN014000-251124
String basePart = baseBomPartNo;
// 잘못된 형식 정규화 (M-M-..., M-E-... 등)
// 예: M-M-000AN014000-251124-01-251124 → M-000AN014000-251124-01-251124
while(basePart.startsWith("M-M-") || basePart.startsWith("M-E-")) {
basePart = "M-" + basePart.substring(4);
}
// 마지막 두 개의 "-XX" 부분 제거 (날짜-순번)
// 여러 번 중복된 경우를 대비해 반복 제거
for(int i = 0; i < 2; i++) {
int lastDashIndex = basePart.lastIndexOf("-");
if(lastDashIndex > 0) {
String suffix = basePart.substring(lastDashIndex + 1);
// 날짜(6자리) 또는 순번(2자리)인 경우만 제거
if(suffix.matches("\\d{2}") || suffix.matches("\\d{6}")) {
basePart = basePart.substring(0, lastDashIndex);
} else {
break;
}
}
}
// 새 날짜 추가
mbomPrefix = basePart + "-" + dateStr;
}
// 같은 날짜의 최대 순번 조회
Map<String, Object> seqParam = new HashMap<>();
seqParam.put("mbomPrefix", mbomPrefix);
Integer maxSeq = (Integer) sqlSession.selectOne("productionplanning.getMaxMbomSeqByDate", seqParam);
int nextSeq = (maxSeq != null ? maxSeq : 0) + 1;
// 최종 M-BOM 품번 생성
mbomNo = mbomPrefix + "-" + String.format("%02d", nextSeq);
System.out.println("생성된 M-BOM 품번: " + mbomNo);
} catch(Exception e) {
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return mbomNo;
}
/**
* M-BOM 저장
* 최초 저장: MBOM_HEADER, MBOM_DETAIL 생성
* 수정 저장: 기존 데이터 업데이트 + MBOM_HISTORY 이력 저장
*/
public boolean saveMbom(HttpServletRequest request, Map<String, Object> paramMap) {
SqlSession sqlSession = null;
boolean result = false;
try {
// 트랜잭션 처리를 위해 autoCommit = false로 설정
sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
// 세션에서 사용자 정보 가져오기
HttpSession session = request.getSession();
PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
String userId = CommonUtils.checkNull(person.getUserId());
paramMap.put("sessionUserId", userId);
boolean isUpdate = (Boolean) paramMap.get("isUpdate");
String mbomNo = (String) paramMap.get("mbomNo");
if(isUpdate) {
// 수정: 기존 M-BOM 조회 (최신 버전)
System.out.println("========== UPDATE M-BOM ==========");
System.out.println("projectObjId: " + paramMap.get("projectObjId"));
System.out.println("mbomNo: " + paramMap.get("mbomNo"));
Map<String, Object> existingMbom = sqlSession.selectOne("productionplanning.getMbomByNo", paramMap);
if(existingMbom != null) {
System.out.println("조회된 기존 M-BOM:");
System.out.println(" OBJID: " + existingMbom.get("OBJID"));
System.out.println(" MBOM_NO: " + existingMbom.get("MBOM_NO"));
System.out.println(" REGDATE: " + existingMbom.get("REGDATE"));
// 기존 M-BOM 상세 데이터 조회
Map<String, Object> detailParam = new HashMap<>();
detailParam.put("mbomHeaderObjid", existingMbom.get("OBJID"));
List<Map<String, Object>> existingDetails = sqlSession.selectList("productionplanning.getMbomDetailList", detailParam);
System.out.println("기존 M-BOM 상세 항목 수: " + existingDetails.size());
// 새 데이터
List<Map<String, Object>> mbomData = (List<Map<String, Object>>) paramMap.get("mbomData");
// 변경 사항 비교 (childObjid 기준)
Map<String, Map<String, Object>> beforeMap = new HashMap<>();
Map<String, Map<String, Object>> afterMap = new HashMap<>();
// 기존 데이터 매핑
System.out.println("========== 기존 데이터 매핑 (beforeMap) ==========");
for(Map<String, Object> detail : existingDetails) {
String childObjid = CommonUtils.checkNull(detail.get("CHILD_OBJID"));
if(!"".equals(childObjid)) {
beforeMap.put(childObjid, detail);
System.out.println("Before - childObjid: " + childObjid + ", partNo: " + detail.get("PART_NO"));
}
}
// 새 데이터 매핑
System.out.println("========== 새 데이터 매핑 (afterMap) ==========");
if(mbomData != null) {
for(Map<String, Object> item : mbomData) {
String childObjid = CommonUtils.checkNull(item.get("childObjid"));
if(!"".equals(childObjid)) {
afterMap.put(childObjid, item);
System.out.println("After - childObjid: " + childObjid + ", partNo: " + item.get("partNo"));
}
}
}
// 변경된 항목만 추출
List<Map<String, Object>> changedItems = new ArrayList<>();
System.out.println("========== 변경 사항 비교 ==========");
System.out.println("기존 항목 수 (beforeMap): " + beforeMap.size());
System.out.println("새 항목 수 (afterMap): " + afterMap.size());
// 추가된 항목
for(String childObjid : afterMap.keySet()) {
if(!beforeMap.containsKey(childObjid)) {
Map<String, Object> change = new HashMap<>();
change.put("changeType", "ADD");
change.put("childObjid", childObjid);
change.put("afterData", afterMap.get(childObjid));
changedItems.add(change);
System.out.println("추가된 항목: " + childObjid + " - " + afterMap.get(childObjid).get("partNo"));
}
}
// 삭제된 항목
for(String childObjid : beforeMap.keySet()) {
if(!afterMap.containsKey(childObjid)) {
Map<String, Object> change = new HashMap<>();
change.put("changeType", "DELETE");
change.put("childObjid", childObjid);
change.put("beforeData", beforeMap.get(childObjid));
changedItems.add(change);
System.out.println("삭제된 항목: " + childObjid + " - " + beforeMap.get(childObjid).get("PART_NO"));
}
}
// 수정된 항목
for(String childObjid : afterMap.keySet()) {
if(beforeMap.containsKey(childObjid)) {
Map<String, Object> before = beforeMap.get(childObjid);
Map<String, Object> after = afterMap.get(childObjid);
// 필드별 비교 (실제 편집 가능한 필드만)
List<Map<String, Object>> fieldChanges = new ArrayList<>();
// [DB 필드명, JSP 전송 필드명] 매핑
// JSP getMbomTreeData()에서 전송하는 필드명 기준
String[][] fieldMapping = {
{"PART_OBJID", "partObjid"}, // 파트 변경 감지
{"PART_NO", "partNo"}, // 파트 변경 감지
{"PART_NAME", "partName"}, // 파트 변경 감지
{"SUPPLY_TYPE", "supplyType"},
{"RAW_MATERIAL", "rawMaterial"},
{"RAW_MATERIAL_SIZE", "rawMaterialSize"},
{"RAW_MATERIAL_PART_NO", "rawMaterialPartNo"},
{"REQUIRED_QTY", "requiredQty"},
{"PRODUCTION_QTY", "productionQty"},
{"PROCESSING_VENDOR", "processingVendor"},
{"PROCESSING_DEADLINE", "processingDeadline"},
{"GRINDING_DEADLINE", "grindingDeadline"}
};
for(String[] mapping : fieldMapping) {
String dbField = mapping[0];
String jsField = mapping[1];
// before는 DB에서 대문자로, after는 JSP에서 camelCase로
Object beforeObj = before.get(dbField);
Object afterObj = after.get(jsField);
String beforeValue = (beforeObj == null) ? "" : String.valueOf(beforeObj);
String afterValue = (afterObj == null) ? "" : String.valueOf(afterObj);
// 숫자 필드는 소수점 정규화 (1.0000 vs 1 비교 문제 해결)
if(dbField.contains("QTY") || dbField.equals("QTY")) {
try {
double beforeNum = Double.parseDouble(beforeValue);
double afterNum = Double.parseDouble(afterValue);
if(Math.abs(beforeNum - afterNum) < 0.0001) {
continue; // 실질적으로 같은 값
}
} catch(Exception e) {
// 숫자 변환 실패 시 문자열 비교
}
}
if(!beforeValue.equals(afterValue)) {
Map<String, Object> fieldChange = new HashMap<>();
fieldChange.put("field", dbField);
fieldChange.put("before", beforeValue);
fieldChange.put("after", afterValue);
fieldChanges.add(fieldChange);
System.out.println("필드 변경 감지: " + dbField + " - Before: [" + beforeValue + "] After: [" + afterValue + "]");
}
}
if(!fieldChanges.isEmpty()) {
Map<String, Object> change = new HashMap<>();
change.put("changeType", "MODIFY");
change.put("childObjid", childObjid);
change.put("partNo", after.get("partNo"));
change.put("partName", after.get("partName"));
change.put("fieldChanges", fieldChanges);
changedItems.add(change);
System.out.println("변경된 항목 추가: " + after.get("partNo") + " - " + fieldChanges.size() + "개 필드 변경");
}
}
}
// M-BOM 업데이트
paramMap.put("mbomHeaderObjid", existingMbom.get("OBJID"));
sqlSession.update("productionplanning.updateMbomHeader", paramMap);
sqlSession.delete("productionplanning.deleteMbomDetail", paramMap);
// 새 상세 데이터 삽입
if(mbomData != null && !mbomData.isEmpty()) {
for(Map<String, Object> item : mbomData) {
item.put("mbomHeaderObjid", existingMbom.get("OBJID"));
item.put("sessionUserId", userId);
// SEQ가 없으면 기존 SEQ 유지 (JSP에서 전송된 seq 사용)
// 없는 경우에만 새로 부여
if(item.get("seq") == null || "".equals(item.get("seq"))) {
item.put("seq", 999); // 임시값 (나중에 정렬 필요)
}
// 기존 항목인 경우 objid와 childObjid 유지
// 새 항목인 경우에만 생성
String objid = CommonUtils.checkNull(item.get("objid"));
String childObjid = CommonUtils.checkNull(item.get("childObjid"));
if("".equals(objid)) {
objid = CommonUtils.createObjId();
item.put("objid", objid);
}
if("".equals(childObjid)) {
childObjid = objid; // 새 항목은 objid와 동일
item.put("childObjid", childObjid);
}
// LEVEL이 없으면 기본값 1 설정
if(item.get("level") == null || "".equals(item.get("level"))) {
item.put("level", 1);
}
System.out.println("UPDATE M-BOM DETAIL: seq=" + item.get("seq") +
", level=" + item.get("level") +
", partNo=" + item.get("partNo") +
", parentObjid=" + item.get("parentObjid") +
", childObjid=" + childObjid);
sqlSession.insert("productionplanning.insertMbomDetail", item);
}
}
// 변경 사항이 있을 때만 이력 저장
if(!changedItems.isEmpty()) {
String historyObjid = CommonUtils.createObjId();
Map<String, Object> historyParam = new HashMap<>();
historyParam.put("objid", historyObjid);
historyParam.put("mbomHeaderObjid", existingMbom.get("OBJID"));
historyParam.put("changeType", "UPDATE");
historyParam.put("changeDescription", changedItems.size() + "개 항목 변경");
historyParam.put("beforeData", new com.google.gson.Gson().toJson(changedItems));
historyParam.put("afterData", "{}");
historyParam.put("sessionUserId", userId);
sqlSession.insert("productionplanning.insertMbomHistory", historyParam);
}
result = true;
}
} else {
// 최초 저장: 새 M-BOM 생성
String newObjid = CommonUtils.createObjId();
paramMap.put("objid", newObjid);
paramMap.put("mbomNo", mbomNo);
// sourceBomType에 따라 sourceEbomObjid 또는 sourceMbomObjid 설정
String sourceBomType = (String) paramMap.get("sourceBomType");
String sourceBomObjId = (String) paramMap.get("sourceBomObjId");
if("EBOM".equals(sourceBomType)) {
paramMap.put("sourceEbomObjid", sourceBomObjId);
paramMap.put("sourceMbomObjid", null);
} else if("MBOM".equals(sourceBomType)) {
paramMap.put("sourceEbomObjid", null);
paramMap.put("sourceMbomObjid", sourceBomObjId);
}
// MBOM_HEADER 삽입
sqlSession.insert("productionplanning.insertMbomHeader", paramMap);
// MBOM_DETAIL 삽입
List<Map<String, Object>> mbomData = (List<Map<String, Object>>) paramMap.get("mbomData");
if(mbomData != null && !mbomData.isEmpty()) {
// CHILD_OBJID 매핑 (기존 E-BOM의 CHILD_OBJID -> 새 M-BOM의 CHILD_OBJID)
Map<String, String> childObjidMap = new HashMap<>();
// 1단계: 모든 항목에 대해 새 CHILD_OBJID 생성
for(Map<String, Object> item : mbomData) {
String oldChildObjid = CommonUtils.checkNull(item.get("childObjid"));
if(!"".equals(oldChildObjid) && !childObjidMap.containsKey(oldChildObjid)) {
String newChildObjid = CommonUtils.createObjId();
childObjidMap.put(oldChildObjid, newChildObjid);
}
}
// 2단계: PARENT_OBJID와 CHILD_OBJID를 새 ID로 매핑하여 삽입
for(Map<String, Object> item : mbomData) {
String oldChildObjid = CommonUtils.checkNull(item.get("childObjid"));
String oldParentObjid = CommonUtils.checkNull(item.get("parentObjid"));
// 새 CHILD_OBJID 설정
String newChildObjid = childObjidMap.get(oldChildObjid);
if(newChildObjid == null) {
newChildObjid = CommonUtils.createObjId();
}
// 새 PARENT_OBJID 설정 (부모가 있는 경우에만)
String newParentObjid = "";
if(!"".equals(oldParentObjid)) {
newParentObjid = childObjidMap.get(oldParentObjid);
if(newParentObjid == null) {
newParentObjid = oldParentObjid; // 매핑 실패 시 원본 유지
}
}
item.put("mbomHeaderObjid", newObjid);
item.put("objid", newChildObjid); // OBJID = CHILD_OBJID
item.put("childObjid", newChildObjid);
item.put("parentObjid", newParentObjid);
item.put("sessionUserId", userId);
// SEQ 유지 (JSP에서 전송된 seq 사용)
if(item.get("seq") == null || "".equals(item.get("seq"))) {
item.put("seq", 999); // 임시값
}
// LEVEL이 없으면 기본값 1 설정
if(item.get("level") == null || "".equals(item.get("level"))) {
item.put("level", 1);
}
System.out.println("INSERT M-BOM DETAIL: seq=" + item.get("seq") +
", level=" + item.get("level") +
", partNo=" + item.get("partNo") +
", parentObjid=" + newParentObjid +
", childObjid=" + newChildObjid);
sqlSession.insert("productionplanning.insertMbomDetail", item);
}
}
// 이력 저장 (생성)
String historyObjid = CommonUtils.createObjId();
paramMap.put("objid", historyObjid); // MBOM_HISTORY의 OBJID
paramMap.put("mbomHeaderObjid", newObjid);
paramMap.put("changeType", "CREATE");
paramMap.put("afterData", new com.google.gson.Gson().toJson(paramMap));
sqlSession.insert("productionplanning.insertMbomHistory", paramMap);
// PROJECT_MGMT 업데이트 (MBOM_STATUS = 'Y')
sqlSession.update("productionplanning.updateProjectMbomStatus", paramMap);
result = true;
}
sqlSession.commit();
} catch(Exception e) {
if(sqlSession != null) {
sqlSession.rollback();
}
e.printStackTrace();
} finally {
if(sqlSession != null) {
sqlSession.close();
}
}
return result;
}
}