From e1db86179d02bb71422da2d14b17c29d55a52b00 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Fri, 19 Dec 2025 18:31:26 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9B=90=EC=9E=90=EC=9E=AC,=20=EA=B0=80?= =?UTF-8?q?=EA=B3=B5=EC=97=85=EC=B2=B4=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rawMaterialRequirementList.jsp | 383 ++++++++++++++++++ .../view/salesMng/purchaseListFormPopUp.jsp | 90 +++- .../ProductionPlanningController.java | 39 ++ src/com/pms/mapper/productionplanning.xml | 48 +++ src/com/pms/mapper/salesMng.xml | 4 +- .../service/ProductionPlanningService.java | 143 +++++++ 6 files changed, 702 insertions(+), 5 deletions(-) create mode 100644 WebContent/WEB-INF/view/productionplanning/rawMaterialRequirementList.jsp diff --git a/WebContent/WEB-INF/view/productionplanning/rawMaterialRequirementList.jsp b/WebContent/WEB-INF/view/productionplanning/rawMaterialRequirementList.jsp new file mode 100644 index 0000000..6ad46d8 --- /dev/null +++ b/WebContent/WEB-INF/view/productionplanning/rawMaterialRequirementList.jsp @@ -0,0 +1,383 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ page import="com.pms.common.utils.*"%> +<%@ page import="java.util.*" %> +<%@include file= "/init.jsp" %> +<% +// DB에서 메뉴명 조회 (공통 유틸 사용) +String menuObjId = request.getParameter("menuObjId"); +String menuName = CommonUtils.getMenuName(menuObjId, "원자재소요량"); +%> + + + + + <%=Constants.SYSTEM_NAME%> + + + + + + + +
+ + + +
+
+
+

+ <%=menuName%> +

+
+ + + +
+
+ + +
+
M-BOM 선택 및 수량 입력
+
+
+ + +
원자재 소요량 (구매품) - 조회결과: 0
+
+
+
+
+ + diff --git a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp index 2638627..962c2a8 100644 --- a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp +++ b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp @@ -134,6 +134,7 @@ var projectMgmtObjid = "${resolvedProjectId}"; var bomReportObjid = "${resolvedBomReportObjid}"; var mbomHeaderObjid = "${resolvedMbomHeaderObjid}"; // MBOM_HEADER.OBJID (M-BOM 관리 화면에서 사용하는 ID) var vendorList = []; // 공급업체 목록 +var processingVendorList = []; // 가공업체 목록 (Select2용 배열) // 디버그: resultMap 내용 확인 (주석처리) // console.log("=== JSP resultMap 디버그 ==="); @@ -156,8 +157,17 @@ function logError(){ } $(document).ready(function(){ - // 공급업체 목록 먼저 로드 + // 공급업체 목록 로드 (가공업체도 동일 테이블 사용) fn_loadVendorList(function(){ + // vendorList에서 processingVendorList 변환 (Select2용 배열 형태) + processingVendorList = []; + for(var key in vendorList) { + if(key !== '') { // 빈 값 제외 + processingVendorList.push({id: key, text: vendorList[key]}); + } + } + console.log("가공업체 목록 변환 완료:", processingVendorList.length + "개"); + fn_initGrid(); logDebug("purchaseListFormPopUp :: grid initialized"); fn_loadInitialData(); @@ -196,6 +206,7 @@ function fn_loadVendorList(callback) { }); } + function fn_loadInitialData(){ logDebug("purchaseListFormPopUp :: fn_loadInitialData start", "projectMgmtObjid=", projectMgmtObjid, @@ -224,6 +235,62 @@ function fn_loadInitialData(){ } } +// Select2 커스텀 에디터 생성 함수 +function createSelect2Editor(options) { + return function(cell, onRendered, success, cancel, editorParams) { + var cellValue = cell.getValue(); + var container = document.createElement("span"); + var select = document.createElement("select"); + select.style.width = "100%"; + + // 빈 옵션 추가 + var emptyOption = document.createElement("option"); + emptyOption.value = ""; + emptyOption.text = "선택"; + select.appendChild(emptyOption); + + // 옵션 추가 + options.forEach(function(opt) { + var option = document.createElement("option"); + if(typeof opt === 'object') { + option.value = opt.id || opt.value || ''; + option.text = opt.text || opt.label || ''; + } else { + option.value = opt; + option.text = opt; + } + if(option.value == cellValue) { + option.selected = true; + } + select.appendChild(option); + }); + + container.appendChild(select); + + onRendered(function() { + $(select).select2({ + width: '100%', + dropdownAutoWidth: true, + allowClear: true, + placeholder: '선택' + }); + $(select).select2('open'); + }); + + $(select).on('select2:select select2:clear', function(e) { + success($(select).val()); + }); + + $(select).on('select2:close', function() { + setTimeout(function() { + success($(select).val()); + }, 100); + }); + + return container; + }; +} + // Tabulator 그리드 초기화 function fn_initGrid() { var columns = [ @@ -462,13 +529,28 @@ function fn_initGrid() { return value ? Number(value).toLocaleString() : '0'; } }, - // 24. 가공업체 + // 24. 가공업체 (수정가능 - Select2 에디터) { headerHozAlign: 'center', hozAlign: 'left', width: 150, - title: '가공업체', - field: 'PROCESSING_VENDOR' + title: '가공업체', + field: 'PROCESSING_VENDOR', + editor: function(cell, onRendered, success, cancel, editorParams) { + // Select2 에디터 + return createSelect2Editor(processingVendorList)(cell, onRendered, success, cancel, editorParams); + }, + formatter: function(cell) { + var value = cell.getValue(); + if(!value) return ''; + // OBJID로 업체명 조회하여 표시 + for(var i = 0; i < processingVendorList.length; i++) { + if(processingVendorList[i].id == value) { + return processingVendorList[i].text; + } + } + return value; + } }, /* // 25. 가공납기 - 주석처리 { diff --git a/src/com/pms/controller/ProductionPlanningController.java b/src/com/pms/controller/ProductionPlanningController.java index 0b62a9a..6b2db3a 100644 --- a/src/com/pms/controller/ProductionPlanningController.java +++ b/src/com/pms/controller/ProductionPlanningController.java @@ -1883,4 +1883,43 @@ public class ProductionPlanningController extends BaseService { return resultMap; } + /** + * 원자재소요량 조회 화면 + */ + @RequestMapping("/productionplanning/rawMaterialRequirementList.do") + public String rawMaterialRequirementList(HttpServletRequest request, @RequestParam Map paramMap) { + Map code_map = new HashMap(); + try { + // M-BOM 목록 (셀렉트박스용) + code_map.put("mbom_list", commonService.bizMakeOptionList("", "", "productionplanning.getMbomListForSelect2")); + + request.setAttribute("code_map", code_map); + } catch(Exception e) { + e.printStackTrace(); + } + return "/productionplanning/rawMaterialRequirementList"; + } + + /** + * M-BOM 기준 원자재(구매품) 소요량 조회 + * @param request + * @param paramMap - mbomItems: [{mbomObjid, qty}, ...] + * @return 품번별 합산된 소요량 목록 + */ + @ResponseBody + @RequestMapping(value="/productionplanning/getRawMaterialRequirementList.do", produces="application/json;charset=UTF-8") + public Map getRawMaterialRequirementList(HttpServletRequest request, @RequestBody Map paramMap) { + Map resultMap = new HashMap(); + try { + List list = productionPlanningService.getRawMaterialRequirementList(paramMap); + resultMap.put("result", "success"); + resultMap.put("list", list); + } catch(Exception e) { + e.printStackTrace(); + resultMap.put("result", "fail"); + resultMap.put("msg", "조회 실패: " + e.getMessage()); + } + return resultMap; + } + } diff --git a/src/com/pms/mapper/productionplanning.xml b/src/com/pms/mapper/productionplanning.xml index 0473770..5f2c41f 100644 --- a/src/com/pms/mapper/productionplanning.xml +++ b/src/com/pms/mapper/productionplanning.xml @@ -4764,5 +4764,53 @@ AND P.PART_TYPE IN ('0001812', '0001813') -- 부품, 조립품 ORDER BY P.PART_TYPE, MD.PART_NO + + + + + + diff --git a/src/com/pms/mapper/salesMng.xml b/src/com/pms/mapper/salesMng.xml index 3cb547d..a1e8c8f 100644 --- a/src/com/pms/mapper/salesMng.xml +++ b/src/com/pms/mapper/salesMng.xml @@ -3084,7 +3084,7 @@ UPDATE SET 0 AS ORDER_QTY, 0 AS ITEM_QTY2, 0 AS PRODUCTION_QTY, - '' AS PROCESSING_VENDOR, + COALESCE(SRP.PROCESSING_VENDOR, '') AS PROCESSING_VENDOR, NULL AS PROCESSING_DEADLINE, NULL AS GRINDING_DEADLINE, -- 구매 관련 컬럼 (SALES_REQUEST_PART에서 조회) @@ -3376,6 +3376,7 @@ ORDER BY V.PATH2 NET_QTY = COALESCE(NULLIF(TRIM(#{NET_QTY}::TEXT), '')::NUMERIC, 0), PO_QTY = COALESCE(NULLIF(TRIM(#{PO_QTY}::TEXT), '')::NUMERIC, 0), VENDOR = #{VENDOR_PM}, + PROCESSING_VENDOR = #{PROCESSING_VENDOR}, UNIT_PRICE = COALESCE(NULLIF(TRIM(#{UNIT_PRICE}::TEXT), '')::NUMERIC, 0), TOTAL_PRICE = COALESCE(NULLIF(TRIM(#{TOTAL_PRICE}::TEXT), '')::NUMERIC, 0), EDITER = #{EDITER}, @@ -3390,6 +3391,7 @@ ORDER BY V.PATH2 NET_QTY = COALESCE(NULLIF(TRIM(#{NET_QTY}::TEXT), '')::NUMERIC, 0), PO_QTY = COALESCE(NULLIF(TRIM(#{PO_QTY}::TEXT), '')::NUMERIC, 0), VENDOR_PM = #{VENDOR_PM}, + PROCESSING_VENDOR = #{PROCESSING_VENDOR}, UNIT_PRICE = COALESCE(NULLIF(TRIM(#{UNIT_PRICE}::TEXT), '')::NUMERIC, 0), TOTAL_PRICE = COALESCE(NULLIF(TRIM(#{TOTAL_PRICE}::TEXT), '')::NUMERIC, 0), WRITER = #{EDITER} diff --git a/src/com/pms/service/ProductionPlanningService.java b/src/com/pms/service/ProductionPlanningService.java index 3873fd4..56077d3 100644 --- a/src/com/pms/service/ProductionPlanningService.java +++ b/src/com/pms/service/ProductionPlanningService.java @@ -1934,4 +1934,147 @@ public class ProductionPlanningService { return resultList; } + + /** + * M-BOM 기준 원자재(구매품 + 원소재) 소요량 조회 + * - 여러 M-BOM을 입력받아 PART_TYPE이 '0000063'(구매품)인 항목 조회 + * - RAW_MATERIAL_PART_NO가 있는 항목은 "원소재"로 별도 조회 + * - 동일 품번은 합산하여 반환 + * @param paramMap - mbomItems: [{mbomObjid, qty}, ...] + * @return 품번별 합산된 소요량 목록 + */ + public List getRawMaterialRequirementList(Map paramMap) { + SqlSession sqlSession = null; + List resultList = new ArrayList(); + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + + // 입력받은 M-BOM 목록 + List> mbomItems = (List>) paramMap.get("mbomItems"); + if(mbomItems == null || mbomItems.isEmpty()) { + return resultList; + } + + // 구매품 품번별 소요량 Map + Map> purchasePartNoMap = new LinkedHashMap<>(); + // 원소재 품번별 소요량 Map + Map> rawSourcePartNoMap = new LinkedHashMap<>(); + + // 각 M-BOM별로 조회하여 합산 + for(Map mbomItem : mbomItems) { + String mbomObjid = CommonUtils.nullToEmpty((String)mbomItem.get("mbomObjid")); + int inputQty = 0; + Object qtyObj = mbomItem.get("qty"); + if(qtyObj != null) { + if(qtyObj instanceof Number) { + inputQty = ((Number)qtyObj).intValue(); + } else { + try { + inputQty = Integer.parseInt(qtyObj.toString()); + } catch(NumberFormatException e) { + inputQty = 0; + } + } + } + + if("".equals(mbomObjid) || inputQty <= 0) { + continue; + } + + Map queryParam = new HashMap<>(); + queryParam.put("mbomHeaderObjid", mbomObjid); + + // 1. 구매품 조회 + List> purchaseItems = sqlSession.selectList("productionplanning.getMbomRawMaterialItems", queryParam); + for(Map bomItem : purchaseItems) { + String partNo = CommonUtils.nullToEmpty((String)bomItem.get("part_no")); + if("".equals(partNo)) continue; + + int itemQty = getIntValue(bomItem.get("item_qty")); + int requiredQty = inputQty * itemQty; + + if(purchasePartNoMap.containsKey(partNo)) { + Map existingItem = purchasePartNoMap.get(partNo); + int existingQty = (Integer)existingItem.get("REQUIRED_QTY"); + existingItem.put("REQUIRED_QTY", existingQty + requiredQty); + } else { + Map newItem = new LinkedHashMap<>(); + newItem.put("PART_NO", partNo); + newItem.put("PART_NAME", bomItem.get("part_name")); + newItem.put("CATEGORY_NAME", bomItem.get("category_name")); + newItem.put("UNIT", bomItem.get("unit")); + newItem.put("MATERIAL", bomItem.get("material")); + newItem.put("SPEC", bomItem.get("spec")); + newItem.put("REQUIRED_QTY", requiredQty); + // 구매품은 소재 관련 컬럼 빈값 + newItem.put("RAW_MATERIAL", ""); + newItem.put("RAW_MATERIAL_SIZE", ""); + newItem.put("MATERIAL_PART_NO", ""); + newItem.put("MATERIAL_REQUIRED_QTY", ""); + purchasePartNoMap.put(partNo, newItem); + } + } + + // 2. 원소재 조회 (RAW_MATERIAL_PART_NO가 있는 항목) + List> rawSourceItems = sqlSession.selectList("productionplanning.getMbomRawSourceItems", queryParam); + for(Map bomItem : rawSourceItems) { + String partNo = CommonUtils.nullToEmpty((String)bomItem.get("part_no")); + if("".equals(partNo)) continue; + + int itemQty = getIntValue(bomItem.get("item_qty")); + int requiredQty = inputQty * itemQty; + + if(rawSourcePartNoMap.containsKey(partNo)) { + Map existingItem = rawSourcePartNoMap.get(partNo); + int existingQty = (Integer)existingItem.get("REQUIRED_QTY"); + existingItem.put("REQUIRED_QTY", existingQty + requiredQty); + } else { + Map newItem = new LinkedHashMap<>(); + newItem.put("PART_NO", partNo); + newItem.put("PART_NAME", bomItem.get("part_name")); + newItem.put("CATEGORY_NAME", "원소재"); + newItem.put("UNIT", ""); + newItem.put("MATERIAL", bomItem.get("raw_material")); + newItem.put("SPEC", bomItem.get("raw_material_size")); + newItem.put("REQUIRED_QTY", requiredQty); + // 원소재 관련 컬럼 + newItem.put("RAW_MATERIAL", bomItem.get("raw_material")); + newItem.put("RAW_MATERIAL_SIZE", bomItem.get("raw_material_size")); + newItem.put("MATERIAL_PART_NO", bomItem.get("raw_material_part_no")); + newItem.put("MATERIAL_REQUIRED_QTY", requiredQty); + rawSourcePartNoMap.put(partNo, newItem); + } + } + } + + // 구매품 먼저, 원소재 나중에 합침 + resultList.addAll(purchasePartNoMap.values()); + resultList.addAll(rawSourcePartNoMap.values()); + + } catch(Exception e) { + e.printStackTrace(); + } finally { + if(sqlSession != null) { + sqlSession.close(); + } + } + + return resultList; + } + + /** + * Object를 int로 변환하는 유틸 메서드 + */ + private int getIntValue(Object obj) { + if(obj == null) return 0; + if(obj instanceof Number) { + return ((Number)obj).intValue(); + } + try { + return Integer.parseInt(obj.toString()); + } catch(NumberFormatException e) { + return 0; + } + } }