From 1aa7cb5544f3b789bc3354e22386b37a60244312 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Fri, 19 Dec 2025 18:06:48 +0900 Subject: [PATCH 1/3] =?UTF-8?q?=EC=83=9D=EC=82=B0=EA=B4=80=EB=A6=AC=5F?= =?UTF-8?q?=EB=B0=98=EC=A0=9C=ED=92=88=EC=86=8C=EC=9A=94=EB=9F=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../semiProductRequirementList.jsp | 344 ++++++++++++++++++ .../ProductionPlanningController.java | 39 ++ src/com/pms/mapper/productionplanning.xml | 21 ++ .../service/ProductionPlanningService.java | 107 ++++++ 4 files changed, 511 insertions(+) create mode 100644 WebContent/WEB-INF/view/productionplanning/semiProductRequirementList.jsp diff --git a/WebContent/WEB-INF/view/productionplanning/semiProductRequirementList.jsp b/WebContent/WEB-INF/view/productionplanning/semiProductRequirementList.jsp new file mode 100644 index 0000000..f59eb89 --- /dev/null +++ b/WebContent/WEB-INF/view/productionplanning/semiProductRequirementList.jsp @@ -0,0 +1,344 @@ +<%@ 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/src/com/pms/controller/ProductionPlanningController.java b/src/com/pms/controller/ProductionPlanningController.java index 41b40c5..0b62a9a 100644 --- a/src/com/pms/controller/ProductionPlanningController.java +++ b/src/com/pms/controller/ProductionPlanningController.java @@ -1844,4 +1844,43 @@ public class ProductionPlanningController extends BaseService { return resultMap; } + /** + * 반제품소요량 조회 화면 + */ + @RequestMapping("/productionplanning/semiProductRequirementList.do") + public String semiProductRequirementList(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/semiProductRequirementList"; + } + + /** + * M-BOM 기준 반제품 소요량 조회 + * @param request + * @param paramMap - mbomItems: [{mbomObjid, qty}, ...] + * @return 품번별 합산된 소요량 목록 + */ + @ResponseBody + @RequestMapping(value="/productionplanning/getSemiProductRequirementList.do", produces="application/json;charset=UTF-8") + public Map getSemiProductRequirementList(HttpServletRequest request, @RequestBody Map paramMap) { + Map resultMap = new HashMap(); + try { + List list = productionPlanningService.getSemiProductRequirementList(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 33ea7d3..0473770 100644 --- a/src/com/pms/mapper/productionplanning.xml +++ b/src/com/pms/mapper/productionplanning.xml @@ -4743,5 +4743,26 @@ MODIFIER = #{userId} WHERE OBJID = #{OBJID} + + + diff --git a/src/com/pms/service/ProductionPlanningService.java b/src/com/pms/service/ProductionPlanningService.java index fea250b..3873fd4 100644 --- a/src/com/pms/service/ProductionPlanningService.java +++ b/src/com/pms/service/ProductionPlanningService.java @@ -2,6 +2,7 @@ package com.pms.service; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -1827,4 +1828,110 @@ public class ProductionPlanningService { return result; } + + /** + * M-BOM 기준 반제품 소요량 조회 + * - 여러 M-BOM을 입력받아 범주가 '부품', '조립품'인 항목만 조회 + * - 동일 품번은 합산하여 반환 + * @param paramMap - mbomItems: [{mbomObjid, qty}, ...] + * @return 품번별 합산된 소요량 목록 + */ + public List getSemiProductRequirementList(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 (key: PART_NO, value: {품번정보, 소요량합계}) + Map> partNoMap = 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; + } + + // M-BOM 항목 조회 (범주가 '부품', '조립품'인 것만) + Map queryParam = new HashMap<>(); + queryParam.put("mbomHeaderObjid", mbomObjid); + List> bomItems = sqlSession.selectList("productionplanning.getMbomSemiProductItems", queryParam); + + // 소요량 합산 (PostgreSQL은 소문자 키로 반환) + for(Map bomItem : bomItems) { + String partNo = CommonUtils.nullToEmpty((String)bomItem.get("part_no")); + if("".equals(partNo)) continue; + + // M-BOM의 항목수량 + int itemQty = 0; + Object itemQtyObj = bomItem.get("item_qty"); + if(itemQtyObj != null) { + if(itemQtyObj instanceof Number) { + itemQty = ((Number)itemQtyObj).intValue(); + } else { + try { + itemQty = Integer.parseInt(itemQtyObj.toString()); + } catch(NumberFormatException e) { + itemQty = 0; + } + } + } + + // 소요량 = 입력수량 × 항목수량 + int requiredQty = inputQty * itemQty; + + if(partNoMap.containsKey(partNo)) { + // 기존 품번이면 소요량만 합산 + Map existingItem = partNoMap.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); + partNoMap.put(partNo, newItem); + } + } + } + + // Map -> List 변환 + resultList = new ArrayList(partNoMap.values()); + + } catch(Exception e) { + e.printStackTrace(); + } finally { + if(sqlSession != null) { + sqlSession.close(); + } + } + + return resultList; + } } -- 2.49.1 From e1db86179d02bb71422da2d14b17c29d55a52b00 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Fri, 19 Dec 2025 18:31:26 +0900 Subject: [PATCH 2/3] =?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; + } + } } -- 2.49.1 From 57b14daf1961789d2ee9335450f35a633434bcb9 Mon Sep 17 00:00:00 2001 From: hjjeong Date: Mon, 22 Dec 2025 11:26:55 +0900 Subject: [PATCH 3/3] =?UTF-8?q?=EC=9B=90=EC=9E=90=EC=9E=AC,=20=EA=B5=AC?= =?UTF-8?q?=EB=A7=A4=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EA=B0=80=EA=B3=B5?= =?UTF-8?q?=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 | 24 ++++--- .../semiProductRequirementList.jsp | 2 +- .../view/salesMng/purchaseListFormPopUp.jsp | 35 +++++----- src/com/pms/mapper/productionplanning.xml | 4 +- src/com/pms/mapper/salesMng.xml | 9 ++- .../service/ProductionPlanningService.java | 65 +++++++++++++------ 6 files changed, 90 insertions(+), 49 deletions(-) diff --git a/WebContent/WEB-INF/view/productionplanning/rawMaterialRequirementList.jsp b/WebContent/WEB-INF/view/productionplanning/rawMaterialRequirementList.jsp index 6ad46d8..09265d3 100644 --- a/WebContent/WEB-INF/view/productionplanning/rawMaterialRequirementList.jsp +++ b/WebContent/WEB-INF/view/productionplanning/rawMaterialRequirementList.jsp @@ -176,11 +176,12 @@ function fn_initResultGrid() { data: [], layout: "fitColumns", height: "calc(100vh - 450px)", + pagination: false, // 페이지네이션 비활성화 (전체 데이터 표시) columns: [ { title: '구분', field: 'CATEGORY_NAME', - width: 100, + width: 150, headerHozAlign: 'center', headerSort: true, hozAlign: 'center' @@ -188,7 +189,7 @@ function fn_initResultGrid() { { title: '품번', field: 'PART_NO', - width: 180, + width: 320, headerHozAlign: 'center', headerSort: true, hozAlign: 'left' @@ -203,7 +204,7 @@ function fn_initResultGrid() { { title: '소요량', field: 'REQUIRED_QTY', - width: 100, + width: 150, headerHozAlign: 'center', headerSort: true, hozAlign: 'right', @@ -218,7 +219,7 @@ function fn_initResultGrid() { { title: '소재', field: 'MATERIAL', - width: 120, + width: 150, headerHozAlign: 'center', headerSort: true, hozAlign: 'center' @@ -226,7 +227,7 @@ function fn_initResultGrid() { { title: '사이즈', field: 'SPEC', - width: 120, + width: 150, headerHozAlign: 'center', headerSort: true, hozAlign: 'center' @@ -234,7 +235,7 @@ function fn_initResultGrid() { { title: '소재품번', field: 'MATERIAL_PART_NO', - width: 180, + width: 250, headerHozAlign: 'center', headerSort: true, hozAlign: 'left' @@ -242,7 +243,7 @@ function fn_initResultGrid() { { title: '소재소요량', field: 'MATERIAL_REQUIRED_QTY', - width: 120, + width: 150, headerHozAlign: 'center', headerSort: true, hozAlign: 'right', @@ -321,10 +322,17 @@ function fn_search() { data: JSON.stringify({mbomItems: mbomItems}), dataType: "json", success: function(data) { + console.log("=== 서버 응답 데이터 ==="); + console.log("전체 응답:", data); + console.log("리스트 개수:", data.list ? data.list.length : 0); + console.log("리스트 데이터:", data.list); + if(data.result === "success") { resultGrid.setData(data.list || []); var cnt = data.list ? data.list.length : 0; $("#resultCnt").text(cnt); + + console.log("그리드 데이터 개수:", resultGrid.getData().length); if(cnt === 0) { Swal.fire({ title: '조회 결과 없음', @@ -374,7 +382,7 @@ function fn_search() { -
원자재 소요량 (구매품) - 조회결과: 0
+
원자재 소요량 (구매품, 원소재) - 조회결과: 0
diff --git a/WebContent/WEB-INF/view/productionplanning/semiProductRequirementList.jsp b/WebContent/WEB-INF/view/productionplanning/semiProductRequirementList.jsp index f59eb89..4773041 100644 --- a/WebContent/WEB-INF/view/productionplanning/semiProductRequirementList.jsp +++ b/WebContent/WEB-INF/view/productionplanning/semiProductRequirementList.jsp @@ -188,7 +188,7 @@ function fn_initResultGrid() { { title: '품번', field: 'PART_NO', - width: 300, + width: 320, headerHozAlign: 'center', headerSort: true, hozAlign: 'left' diff --git a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp index 962c2a8..9ede541 100644 --- a/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp +++ b/WebContent/WEB-INF/view/salesMng/purchaseListFormPopUp.jsp @@ -85,7 +85,7 @@ body, html { - ${resultMap.PROJECT_NO} + ${resultMap.PROJECT_NUMBER} @@ -115,7 +115,7 @@ body, html { - ${resultMap.DELIVERY_REQUEST_DATE} + ${resultMap.DUE_DATE} @@ -542,7 +542,13 @@ function fn_initGrid() { }, formatter: function(cell) { var value = cell.getValue(); - if(!value) return ''; + + // 저장된 값이 없으면 기본값 '5001'(RPS) 설정 + if(value === undefined || value === null || value === '') { + value = '5001'; + cell.getRow().update({PROCESSING_VENDOR: value}, false); + } + // OBJID로 업체명 조회하여 표시 for(var i = 0; i < processingVendorList.length; i++) { if(processingVendorList[i].id == value) { @@ -632,28 +638,27 @@ function fn_initGrid() { return value ? Number(value).toLocaleString() : '0'; } }, - // 30. 공급업체 (수정가능 - 기준정보에서 선택) + // 30. 공급업체 (수정가능 - Select2 에디터) { headerHozAlign: 'center', hozAlign: 'left', width: 150, title: '공급업체', field: 'VENDOR_PM', - editor: 'list', - editorParams: function(cell) { - return { - values: vendorList, - autocomplete: true, - listOnEmpty: true, - freetext: true, - allowEmpty: true - }; + 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 ''; - // vendorList에서 해당 값의 이름 찾기 - return vendorList[value] || value; + // processingVendorList에서 해당 값의 이름 찾기 + for(var i = 0; i < processingVendorList.length; i++) { + if(processingVendorList[i].id == value) { + return processingVendorList[i].text; + } + } + return value; } }, // 31. 단가 (수정가능) diff --git a/src/com/pms/mapper/productionplanning.xml b/src/com/pms/mapper/productionplanning.xml index 5f2c41f..717efaa 100644 --- a/src/com/pms/mapper/productionplanning.xml +++ b/src/com/pms/mapper/productionplanning.xml @@ -4796,7 +4796,7 @@ SELECT MD.RAW_MATERIAL_PART_NO AS PART_NO, MD.RAW_MATERIAL AS PART_NAME, - COALESCE(MD.REQUIRED_QTY, '1')::INTEGER AS ITEM_QTY, + COALESCE(MD.REQUIRED_QTY, '0')::NUMERIC AS ITEM_QTY, '원소재' AS CATEGORY_NAME, '' AS UNIT, MD.RAW_MATERIAL AS MATERIAL, @@ -4804,7 +4804,7 @@ MD.RAW_MATERIAL, MD.RAW_MATERIAL_SIZE, MD.RAW_MATERIAL_PART_NO, - COALESCE(MD.REQUIRED_QTY, '1')::INTEGER AS RAW_MATERIAL_QTY + COALESCE(MD.REQUIRED_QTY, '0')::NUMERIC AS RAW_MATERIAL_QTY FROM MBOM_DETAIL MD WHERE MD.MBOM_HEADER_OBJID = #{mbomHeaderObjid} AND MD.STATUS = 'ACTIVE' diff --git a/src/com/pms/mapper/salesMng.xml b/src/com/pms/mapper/salesMng.xml index a1e8c8f..3009f6a 100644 --- a/src/com/pms/mapper/salesMng.xml +++ b/src/com/pms/mapper/salesMng.xml @@ -665,6 +665,9 @@ VALUES SRM.PROJECT_NO, PM.PROJECT_NO AS PROJECT_NUMBER, PM.PROJECT_NAME, + PM.PART_NO, + PM.PART_NAME, + PM.DUE_DATE, COALESCE( PM.OBJID, (SELECT OBJID FROM PROJECT_MGMT WHERE PROJECT_NO = SRM.PROJECT_NO LIMIT 1) @@ -718,9 +721,9 @@ VALUES COALESCE(NULLIF(SRM.AREA_CD, ''), SM.AREA_CD) AS AREA_CD, -- 국내/해외 COALESCE(NULLIF(SRM.CUSTOMER_OBJID, ''), SM.OBJID::VARCHAR) AS CUSTOMER_OBJID, -- 고객사 COALESCE(NULLIF(SRM.PAID_TYPE, ''), CM.PAID_TYPE) AS PAID_TYPE, -- 유/무상 - CM.CATEGORY_CD AS CATEGORY_CD, -- 제품유형 코드 ID (드롭다운 선택용) + CM.CATEGORY_CD AS CATEGORY_CD -- 제품유형 코드 ID (드롭다운 선택용) -- 품번/품명: 첫 번째 품목 + 외 N건 형태 - (SELECT + FROM SALES_REQUEST_MASTER SRM diff --git a/src/com/pms/service/ProductionPlanningService.java b/src/com/pms/service/ProductionPlanningService.java index 56077d3..0091073 100644 --- a/src/com/pms/service/ProductionPlanningService.java +++ b/src/com/pms/service/ProductionPlanningService.java @@ -2004,10 +2004,10 @@ public class ProductionPlanningService { 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("MATERIAL", ""); + newItem.put("SPEC", ""); newItem.put("REQUIRED_QTY", requiredQty); - // 구매품은 소재 관련 컬럼 빈값 newItem.put("RAW_MATERIAL", ""); newItem.put("RAW_MATERIAL_SIZE", ""); newItem.put("MATERIAL_PART_NO", ""); @@ -2017,40 +2017,50 @@ public class ProductionPlanningService { } // 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; + String materialPartNo = CommonUtils.nullToEmpty((String)bomItem.get("raw_material_part_no")); + if("".equals(materialPartNo)) continue; - int itemQty = getIntValue(bomItem.get("item_qty")); - int requiredQty = inputQty * itemQty; + double itemQty = getDoubleValue(bomItem.get("item_qty")); + double 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); + if(rawSourcePartNoMap.containsKey(materialPartNo)) { + Map existingItem = rawSourcePartNoMap.get(materialPartNo); + double existingQty = (Double)existingItem.get("MATERIAL_REQUIRED_QTY_RAW"); + existingItem.put("MATERIAL_REQUIRED_QTY_RAW", existingQty + requiredQty); } else { Map newItem = new LinkedHashMap<>(); - newItem.put("PART_NO", partNo); - newItem.put("PART_NAME", bomItem.get("part_name")); + // 원소재는 품번, 품명, 소요량 빈값 + newItem.put("PART_NO", ""); + newItem.put("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("REQUIRED_QTY", ""); + // 원소재 관련 컬럼만 표시 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); + newItem.put("MATERIAL_PART_NO", materialPartNo); + newItem.put("MATERIAL_REQUIRED_QTY_RAW", requiredQty); // 합산용 (소수점) + rawSourcePartNoMap.put(materialPartNo, newItem); } } } - // 구매품 먼저, 원소재 나중에 합침 + // 구매품 먼저 추가 resultList.addAll(purchasePartNoMap.values()); - resultList.addAll(rawSourcePartNoMap.values()); + + // 원소재는 소수점 합산 후 올림 처리하여 추가 + for(Map item : rawSourcePartNoMap.values()) { + double rawQty = (Double)item.get("MATERIAL_REQUIRED_QTY_RAW"); + int ceilQty = (int)Math.ceil(rawQty); // 올림 처리 + item.put("MATERIAL_REQUIRED_QTY", ceilQty); + item.remove("MATERIAL_REQUIRED_QTY_RAW"); // 임시 필드 제거 + resultList.add(item); + } } catch(Exception e) { e.printStackTrace(); @@ -2077,4 +2087,19 @@ public class ProductionPlanningService { return 0; } } + + /** + * Object를 double로 변환하는 유틸 메서드 + */ + private double getDoubleValue(Object obj) { + if(obj == null) return 0.0; + if(obj instanceof Number) { + return ((Number)obj).doubleValue(); + } + try { + return Double.parseDouble(obj.toString()); + } catch(NumberFormatException e) { + return 0.0; + } + } } -- 2.49.1