From ba66f2b219197d93e8a10ce5f83622af0b64623d Mon Sep 17 00:00:00 2001 From: hjjeong Date: Mon, 23 Feb 2026 11:49:35 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=EB=8F=99=EC=9D=BC=20=ED=92=88=EB=B2=88=20?= =?UTF-8?q?=EC=88=98=EC=A3=BC=20=EC=8B=9C=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=83=9D=EC=84=B1=20=EC=8B=9C=EC=A0=90=EC=97=90=20?= =?UTF-8?q?=EA=B8=B0=EC=A1=B4=20=EC=97=A0=EB=B4=84=20=EB=B3=B5=EC=82=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view/productionplanning/mBomMgmtList.jsp | 2 +- .../ProductionPlanningController.java | 6 +- src/com/pms/mapper/productionplanning.xml | 40 ++-- .../service/ContractMgmtService.java | 50 +++++ .../service/ProductionPlanningService.java | 204 ++++++++++++++++-- 5 files changed, 252 insertions(+), 50 deletions(-) diff --git a/WebContent/WEB-INF/view/productionplanning/mBomMgmtList.jsp b/WebContent/WEB-INF/view/productionplanning/mBomMgmtList.jsp index 32634f0..3d5180c 100644 --- a/WebContent/WEB-INF/view/productionplanning/mBomMgmtList.jsp +++ b/WebContent/WEB-INF/view/productionplanning/mBomMgmtList.jsp @@ -467,7 +467,7 @@ function fn_checkAssignmentAndOpenMbom(projectObjId) { dataType: "json", async: false, success: function(templateResponse) { - if(templateResponse && templateResponse.MBOM_PART_NO) { + if(templateResponse && templateResponse.TEMPLATE_HEADER_OBJID) { console.log("동일 품번의 M-BOM 템플릿 발견 - 팝업 열기"); fn_openMBomFormPopup(projectObjId); } else { diff --git a/src/com/pms/controller/ProductionPlanningController.java b/src/com/pms/controller/ProductionPlanningController.java index 906a8cd..ef9073f 100644 --- a/src/com/pms/controller/ProductionPlanningController.java +++ b/src/com/pms/controller/ProductionPlanningController.java @@ -1163,14 +1163,14 @@ public class ProductionPlanningController extends BaseService { // 할당된 BOM도 없으면 Machine 이외 제품은 템플릿 확인 String productCode = CommonUtils.checkNull(projectInfo.get("PRODUCT_CODE")); String partNo = CommonUtils.checkNull(projectInfo.get("PART_NO")); - + if(!"0000928".equals(productCode) && !"".equals(partNo)) { System.out.println("Machine 이외 제품 - 템플릿 조회 시도"); Map templateParam = new HashMap<>(); templateParam.put("partNo", partNo); - + Map template = commonService.selectOne("productionplanning.getLatestMbomTemplateByPartNo", request, templateParam); - + if(template != null && !template.isEmpty()) { bomReportObjid = CommonUtils.checkNull(template.get("TEMPLATE_HEADER_OBJID")); bomDataType = "TEMPLATE"; diff --git a/src/com/pms/mapper/productionplanning.xml b/src/com/pms/mapper/productionplanning.xml index bca42a8..f0edca5 100644 --- a/src/com/pms/mapper/productionplanning.xml +++ b/src/com/pms/mapper/productionplanning.xml @@ -3406,31 +3406,25 @@ BPQ.SEQ - + diff --git a/src/com/pms/salesmgmt/service/ContractMgmtService.java b/src/com/pms/salesmgmt/service/ContractMgmtService.java index a096230..47d2ddb 100644 --- a/src/com/pms/salesmgmt/service/ContractMgmtService.java +++ b/src/com/pms/salesmgmt/service/ContractMgmtService.java @@ -62,6 +62,9 @@ public class ContractMgmtService { @Autowired CommonService commonService; + @Autowired + com.pms.service.ProductionPlanningService productionPlanningService; + /** *
 	 * 계약관리 목록 조회
@@ -2784,6 +2787,15 @@ private String encodeImageToBase64(String imagePath) {
 								// 프로젝트 SETUP_TASK 등록
 								cnt = sqlSession.insert("contractMgmt.insertProjectSetupTask", projectParam);
 								
+								// 동일 품번 M-BOM 자동 복사 (Machine 제외)
+								if(!isMachine) {
+									copyMbomIfSamePartNoExists(sqlSession, 
+											(String)projectParam.get("OBJID"), 
+											CommonUtils.checkNull(item.get("PART_NO")), 
+											CommonUtils.checkNull(item.get("PART_NAME")), 
+											person.getUserId());
+								}
+								
 								// project_no - unit 폴더 생성
 								Map projectInfo = (Map)sqlSession.selectOne("project.getProjectMngInfo", projectParam);
 								
@@ -2999,6 +3011,15 @@ private String encodeImageToBase64(String imagePath) {
 							// 프로젝트 SETUP_TASK 등록
 							cnt = sqlSession.insert("contractMgmt.insertProjectSetupTask", projectParam);
 							
+							// 동일 품번 M-BOM 자동 복사 (Machine 제외)
+							if(!isMachine) {
+								copyMbomIfSamePartNoExists(sqlSession, 
+										(String)projectParam.get("OBJID"), 
+										CommonUtils.checkNull(item.get("PART_NO")), 
+										CommonUtils.checkNull(item.get("PART_NAME")), 
+										person.getUserId());
+							}
+							
 							// project_no - unit 폴더 생성
 							Map projectInfo = (Map)sqlSession.selectOne("project.getProjectMngInfo", projectParam);
 							
@@ -3787,4 +3808,33 @@ private String encodeImageToBase64(String imagePath) {
 			if(sqlSession != null) sqlSession.close();
 		}
 	}
+	
+	/**
+	 * 동일 품번의 최신 M-BOM이 있으면 자동 복사
+	 * 프로젝트 자동생성 시 WBS TASK 등록 직후 호출
+	 */
+	private void copyMbomIfSamePartNoExists(SqlSession sqlSession, String projectObjId, 
+			String partNo, String partName, String userId) {
+		try {
+			if(partNo == null || partNo.isEmpty()) return;
+			
+			// 동일 품번의 최신 M-BOM 템플릿 조회
+			Map templateParam = new HashMap<>();
+			templateParam.put("partNo", partNo);
+			Map template = sqlSession.selectOne("productionplanning.getLatestMbomTemplateByPartNo", templateParam);
+			
+			if(template != null && !template.isEmpty()) {
+				template = CommonUtils.toUpperCaseMapKey(template);
+				String templateHeaderObjid = CommonUtils.checkNull(template.get("TEMPLATE_HEADER_OBJID"));
+				
+				System.out.println("동일 품번(" + partNo + ") M-BOM 발견 → 프로젝트(" + projectObjId + ")에 자동 복사");
+				
+				productionPlanningService.copyMbomFromTemplate(
+						sqlSession, userId, projectObjId, templateHeaderObjid, partNo, partName);
+			}
+		} catch(Exception e) {
+			System.out.println("M-BOM 자동 복사 중 오류 (프로젝트 생성에는 영향 없음): " + e.getMessage());
+			e.printStackTrace();
+		}
+	}
 }
diff --git a/src/com/pms/service/ProductionPlanningService.java b/src/com/pms/service/ProductionPlanningService.java
index ba23b79..a3ea78e 100644
--- a/src/com/pms/service/ProductionPlanningService.java
+++ b/src/com/pms/service/ProductionPlanningService.java
@@ -1105,48 +1105,53 @@ public class ProductionPlanningService {
 		
 		try {
 			sqlSession = SqlMapConfig.getInstance().getSqlSession();
-			
-			// 현재 날짜 (YYMMDD)
+			mbomNo = generateMbomNo(sqlSession, sourceBomType, baseBomPartNo);
+		} catch(Exception e) {
+			e.printStackTrace();
+		} finally {
+			if(sqlSession != null) {
+				sqlSession.close();
+			}
+		}
+		
+		return mbomNo;
+	}
+	
+	/**
+	 * M-BOM 품번 생성 (기존 트랜잭션 공유)
+	 */
+	public String generateMbomNo(SqlSession sqlSession, String sourceBomType, String baseBomPartNo) {
+		String mbomNo = "";
+		
+		try {
 			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-" 제거
+					cleanPartNo = cleanPartNo.substring(2);
 				}
 				mbomPrefix = "M-" + cleanPartNo + "-" + dateStr;
 			} else if("TEMPLATE".equals(sourceBomType)) {
-				// 템플릿 기준: M-{품번}-YYMMDD
 				String cleanPartNo = baseBomPartNo.trim();
 				if(cleanPartNo.startsWith("M-")) {
-					cleanPartNo = cleanPartNo.substring(2); // "M-" 제거
+					cleanPartNo = cleanPartNo.substring(2);
 				}
 				mbomPrefix = "M-" + cleanPartNo + "-" + dateStr;
 			} else if("MBOM".equals(sourceBomType)) {
-				// M-BOM 기준: 기존 M-BOM 품번에서 날짜/순번 부분 제거 후 새 날짜 추가
-				// 예: M-000AN014000-251124-01 → M-000AN014000 → M-000AN014000-251126-01
-				
 				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);
 				}
 				
-				// 마지막의 모든 날짜-순번 패턴 제거 (중복 복사 대응)
-				// 예: 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);
-						// 날짜(6자리) 또는 순번(2자리)인 경우만 제거
 						if(suffix.matches("\\d{2}") || suffix.matches("\\d{6}")) {
 							basePart = basePart.substring(0, lastDashIndex);
 						} else {
@@ -1157,28 +1162,21 @@ public class ProductionPlanningService {
 					}
 				}
 				
-				// 새 날짜 추가
 				mbomPrefix = basePart + "-" + dateStr;
 			}
 			
-			// 같은 날짜의 최대 순번 조회
 			Map 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;
@@ -1591,6 +1589,166 @@ public class ProductionPlanningService {
 		return result;
 	}
 	
+	/**
+	 * 동일 품번의 최신 M-BOM을 새 프로젝트에 자동 복사 (독립 트랜잭션)
+	 */
+	public String copyMbomFromTemplate(HttpServletRequest request, String projectObjId, 
+			String templateHeaderObjId, String partNo, String partName) {
+		SqlSession sqlSession = null;
+		String newHeaderObjid = null;
+		
+		try {
+			sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
+			HttpSession session = request.getSession();
+			PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN);
+			String userId = CommonUtils.checkNull(person.getUserId());
+			
+			newHeaderObjid = copyMbomFromTemplate(sqlSession, userId, projectObjId, templateHeaderObjId, partNo, partName);
+			
+			if(newHeaderObjid != null) {
+				sqlSession.commit();
+			}
+		} catch(Exception e) {
+			if(sqlSession != null) sqlSession.rollback();
+			newHeaderObjid = null;
+			e.printStackTrace();
+		} finally {
+			if(sqlSession != null) sqlSession.close();
+		}
+		
+		return newHeaderObjid;
+	}
+	
+	/**
+	 * 동일 품번의 최신 M-BOM을 새 프로젝트에 자동 복사 (기존 트랜잭션 공유)
+	 * 수주 확정 → 프로젝트 자동생성 시 ContractMgmtService에서 호출
+	 */
+	public String copyMbomFromTemplate(SqlSession sqlSession, String userId, String projectObjId, 
+			String templateHeaderObjId, String partNo, String partName) {
+		String newHeaderObjid = null;
+		
+		try {
+			// 1. 새 MBOM_HEADER 생성 (M-BOM 품번 규칙 적용)
+			newHeaderObjid = CommonUtils.createObjId();
+			String mbomNo = generateMbomNo(sqlSession, "TEMPLATE", partNo);
+			Map headerParam = new HashMap<>();
+			headerParam.put("objid", newHeaderObjid);
+			headerParam.put("mbomNo", mbomNo);
+			headerParam.put("sourceBomType", "TEMPLATE");
+			headerParam.put("sourceEbomObjid", null);
+			headerParam.put("sourceMbomObjid", templateHeaderObjId);
+			headerParam.put("projectObjId", projectObjId);
+			headerParam.put("partNo", partNo);
+			headerParam.put("partName", partName);
+			headerParam.put("sessionUserId", userId);
+			sqlSession.insert("productionplanning.insertMbomHeader", headerParam);
+			
+			// 2. 템플릿의 MBOM_DETAIL 조회
+			Map detailParam = new HashMap<>();
+			detailParam.put("mbomHeaderObjid", templateHeaderObjId);
+			List> templateDetails = sqlSession.selectList("productionplanning.getMbomDetailList", detailParam);
+			
+			if(templateDetails != null && !templateDetails.isEmpty()) {
+				// 3. CHILD_OBJID 매핑 생성 (old → new)
+				Map childObjidMap = new HashMap<>();
+				for(Map detail : templateDetails) {
+					detail = CommonUtils.toUpperCaseMapKey(detail);
+					String oldObjid = CommonUtils.checkNull(detail.get("OBJID"));
+					String oldChildObjid = CommonUtils.checkNull(detail.get("CHILD_OBJID"));
+					if(!"".equals(oldObjid)) {
+						childObjidMap.put(oldObjid, CommonUtils.createObjId());
+					}
+					if(!"".equals(oldChildObjid) && !childObjidMap.containsKey(oldChildObjid)) {
+						childObjidMap.put(oldChildObjid, CommonUtils.createObjId());
+					}
+				}
+				
+				// 4. 매핑 적용하여 MBOM_DETAIL 복사 (수주/제작 수량은 초기화)
+				for(Map detail : templateDetails) {
+					detail = CommonUtils.toUpperCaseMapKey(detail);
+					String oldObjid = CommonUtils.checkNull(detail.get("OBJID"));
+					String oldChildObjid = CommonUtils.checkNull(detail.get("CHILD_OBJID"));
+					String oldParentObjid = CommonUtils.checkNull(detail.get("PARENT_OBJID"));
+					
+					String newObjid = childObjidMap.containsKey(oldObjid) ? childObjidMap.get(oldObjid) : CommonUtils.createObjId();
+					String newChildObjid = childObjidMap.containsKey(oldChildObjid) ? childObjidMap.get(oldChildObjid) : newObjid;
+					String newParentObjid = !"".equals(oldParentObjid) 
+							? (childObjidMap.containsKey(oldParentObjid) ? childObjidMap.get(oldParentObjid) : oldParentObjid) : "";
+					
+					Map insertParam = new HashMap<>();
+					insertParam.put("objid", newObjid);
+					insertParam.put("mbomHeaderObjid", newHeaderObjid);
+					insertParam.put("parentObjid", newParentObjid);
+					insertParam.put("childObjid", newChildObjid);
+					insertParam.put("seq", detail.get("SEQ"));
+					insertParam.put("level", detail.get("LEVEL"));
+					insertParam.put("partObjid", detail.get("PART_OBJID"));
+					insertParam.put("partNo", detail.get("PART_NO"));
+					insertParam.put("partName", detail.get("PART_NAME"));
+					insertParam.put("qty", detail.get("QTY"));
+					insertParam.put("unit", detail.get("UNIT"));
+					insertParam.put("supplyType", detail.get("SUPPLY_TYPE"));
+					insertParam.put("makeOrBuy", detail.get("MAKE_OR_BUY"));
+					insertParam.put("rawMaterialPartNo", detail.get("RAW_MATERIAL_PART_NO"));
+					insertParam.put("rawMaterialSpec", detail.get("RAW_MATERIAL_SPEC"));
+					insertParam.put("rawMaterial", detail.get("RAW_MATERIAL"));
+					insertParam.put("rawMaterialSize", detail.get("RAW_MATERIAL_SIZE"));
+					insertParam.put("processingVendor", detail.get("PROCESSING_VENDOR"));
+					insertParam.put("processingDeadline", detail.get("PROCESSING_DEADLINE"));
+					insertParam.put("grindingDeadline", detail.get("GRINDING_DEADLINE"));
+					insertParam.put("requiredQty", detail.get("REQUIRED_QTY"));
+					insertParam.put("orderQty", null);
+					insertParam.put("productionQty", null);
+					insertParam.put("stockQty", null);
+					insertParam.put("shortageQty", null);
+					insertParam.put("netQty", null);
+					insertParam.put("poQty", null);
+					insertParam.put("vendor", detail.get("VENDOR"));
+					insertParam.put("unitPrice", detail.get("UNIT_PRICE"));
+					insertParam.put("processingUnitPrice", detail.get("PROCESSING_UNIT_PRICE"));
+					insertParam.put("totalPrice", null);
+					insertParam.put("currency", detail.get("CURRENCY"));
+					insertParam.put("leadTime", detail.get("LEAD_TIME"));
+					insertParam.put("minOrderQty", detail.get("MIN_ORDER_QTY"));
+					insertParam.put("proposalDate", detail.get("PROPOSAL_DATE"));
+					insertParam.put("remark", detail.get("REMARK"));
+					insertParam.put("sessionUserId", userId);
+					
+					sqlSession.insert("productionplanning.insertMbomDetail", insertParam);
+				}
+			}
+			
+			// 5. PROJECT_MGMT MBOM_STATUS 업데이트
+			Map statusParam = new HashMap<>();
+			statusParam.put("projectObjId", projectObjId);
+			statusParam.put("sessionUserId", userId);
+			sqlSession.update("productionplanning.updateProjectMbomStatus", statusParam);
+			
+			// 6. 이력 저장
+			String historyObjid = CommonUtils.createObjId();
+			Map historyParam = new HashMap<>();
+			historyParam.put("objid", historyObjid);
+			historyParam.put("mbomHeaderObjid", newHeaderObjid);
+			historyParam.put("changeType", "COPY_FROM_TEMPLATE");
+			historyParam.put("changeDescription", "동일 품번(" + partNo + ") M-BOM 자동 복사");
+			historyParam.put("beforeData", "{}");
+			historyParam.put("afterData", "{\"sourceHeaderObjid\":\"" + templateHeaderObjId + "\"}");
+			historyParam.put("sessionUserId", userId);
+			sqlSession.insert("productionplanning.insertMbomHistory", historyParam);
+			
+			System.out.println("========== M-BOM 자동 복사 완료 ==========");
+			System.out.println("프로젝트: " + projectObjId);
+			System.out.println("원본 템플릿: " + templateHeaderObjId);
+			System.out.println("새 M-BOM: " + newHeaderObjid);
+			
+		} catch(Exception e) {
+			newHeaderObjid = null;
+			e.printStackTrace();
+		}
+		
+		return newHeaderObjid;
+	}
+	
 	/**
 	 * MBOM_HEADER 테이블에 해당 OBJID가 존재하는지 확인
 	 * @param objId
-- 
2.49.1


From 2b537f6ce8cfd8cfa94d9c95c931ce0483781c94 Mon Sep 17 00:00:00 2001
From: hjjeong 
Date: Mon, 23 Feb 2026 11:58:22 +0900
Subject: [PATCH 2/3] =?UTF-8?q?=EB=8B=A4=EB=A5=B8=20=EC=BB=AC=EB=9F=BC?=
 =?UTF-8?q?=EB=93=A4=EB=8F=84=20select2=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .../quality/processInspectionFormPopUp.jsp    | 36 ++++++++++++-------
 1 file changed, 24 insertions(+), 12 deletions(-)

diff --git a/WebContent/WEB-INF/view/quality/processInspectionFormPopUp.jsp b/WebContent/WEB-INF/view/quality/processInspectionFormPopUp.jsp
index 69db83d..ef0866e 100644
--- a/WebContent/WEB-INF/view/quality/processInspectionFormPopUp.jsp
+++ b/WebContent/WEB-INF/view/quality/processInspectionFormPopUp.jsp
@@ -234,18 +234,24 @@ function fn_search(){
 			editor:"date"
 		},
 		{title:'검사자', field:'INSPECTOR_ID', headerHozAlign:'center', hozAlign:'center', width:100,
-			editor: fnc_customSelectEditor,
+			editor: fn_select2Editor,
+			editorParams: {
+				valueId: "CODE", labelId: "NAME", values: _INSPECTOR_LIST,
+				placeholder: "검사자 검색..."
+			},
 			formatter: function(cell) {
 				return fnc_customSelectFormatter(cell, {valueId:"CODE", labelId:"NAME", values:_INSPECTOR_LIST});
-			},
-			editorParams: {valueId:"CODE", labelId:"NAME", values:_INSPECTOR_LIST}
+			}
 		},
 		{title:'진행공정', field:'PROCESS_CD', headerHozAlign:'center', hozAlign:'center', width:130,
-			editor: fnc_customSelectEditor,
+			editor: fn_select2Editor,
+			editorParams: {
+				valueId: "CODE", labelId: "NAME", values: _PROCESS_LIST,
+				placeholder: "공정 검색..."
+			},
 			formatter: function(cell) {
 				return fnc_customSelectFormatter(cell, {valueId:"CODE", labelId:"NAME", values:_PROCESS_LIST});
-			},
-			editorParams: {valueId:"CODE", labelId:"NAME", values:_PROCESS_LIST}
+			}
 		},
 		{title:'프로젝트번호', field:'PROJECT_OBJID', headerHozAlign:'center', hozAlign:'center', width:130,
 			editor: fn_select2Editor,
@@ -326,18 +332,24 @@ function fn_search(){
 			editorParams: {valueId:"CODE", labelId:"NAME", values:_INSPECTION_RESULT_LIST}
 		},
 		{title:'담당팀', field:'DEPT_CD', headerHozAlign:'center', hozAlign:'center', width:100,
-			editor: fnc_customSelectEditor,
+			editor: fn_select2Editor,
+			editorParams: {
+				valueId: "CODE", labelId: "NAME", values: _DEPT_LIST,
+				placeholder: "담당팀 검색..."
+			},
 			formatter: function(cell) {
 				return fnc_customSelectFormatter(cell, {valueId:"CODE", labelId:"NAME", values:_DEPT_LIST});
-			},
-			editorParams: {valueId:"CODE", labelId:"NAME", values:_DEPT_LIST}
+			}
 		},
 		{title:'담당자', field:'USER_ID', headerHozAlign:'center', hozAlign:'center', width:100,
-			editor: fnc_customSelectEditor,
+			editor: fn_select2Editor,
+			editorParams: {
+				valueId: "CODE", labelId: "NAME", values: _USER_LIST,
+				placeholder: "담당자 검색..."
+			},
 			formatter: function(cell) {
 				return fnc_customSelectFormatter(cell, {valueId:"CODE", labelId:"NAME", values:_USER_LIST});
-			},
-			editorParams: {valueId:"CODE", labelId:"NAME", values:_USER_LIST}
+			}
 		},
 		
 		{title:'특이사항', field:'REMARK', headerHozAlign:'center', hozAlign:'left', width:150,
-- 
2.49.1


From b753594a93c7f26c6550fe3f44ed30e9c6ae5dbe Mon Sep 17 00:00:00 2001
From: hjjeong 
Date: Mon, 23 Feb 2026 17:07:51 +0900
Subject: [PATCH 3/3] =?UTF-8?q?=ED=92=88=EC=9D=98=EC=84=9C=20=EC=83=9D?=
 =?UTF-8?q?=EC=84=B1=20=EB=A1=9C=EC=A7=81=20=EA=B0=9C=EC=84=A0:=20?=
 =?UTF-8?q?=EB=8F=99=EC=9D=BC=20=ED=92=88=EB=B2=88=20=EA=B7=B8=EB=A3=B9?=
 =?UTF-8?q?=ED=95=91=20+=20QTY=20=EB=B6=84=EA=B8=B0=20+=20M-BOM=20?=
 =?UTF-8?q?=EB=B3=B5=EC=82=AC=20=EA=B0=9C=EC=84=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

- 품의서 대상 조회 시 구매리스트와 동일 기준(PART_OBJID+SUPPLY_TYPE+RAW_MATERIAL+SIZE)으로 동일 품번 그룹핑
- 소재 유무에 따라 QTY를 PO_QTY(소재O) 또는 PRODUCTION_QTY(소재X)로 분기
- INSERT 쿼리를 VALUES 기반으로 변경하여 그룹핑된 합산 데이터 직접 삽입
- M-BOM 복사 시 수주수량 기반 제작수량 자동계산 + PROPOSAL_DATE 초기화

Co-authored-by: Cursor 
---
 src/com/pms/mapper/salesMng.xml               | 207 +++++++++++-------
 .../service/ContractMgmtService.java          |  12 +-
 .../salesmgmt/service/SalesMngService.java    |  16 +-
 .../service/ProductionPlanningService.java    |  19 +-
 4 files changed, 164 insertions(+), 90 deletions(-)

diff --git a/src/com/pms/mapper/salesMng.xml b/src/com/pms/mapper/salesMng.xml
index ed05500..d98ebda 100644
--- a/src/com/pms/mapper/salesMng.xml
+++ b/src/com/pms/mapper/salesMng.xml
@@ -3900,21 +3900,35 @@ ORDER BY V.PATH2
 
 
 
-
+
+
+
 
 
-
+
+
+
 
 
 
+
 
 
 
+
 
 
-
+
+
 
 
-
+
+