diff --git a/.playwright-mcp/structure_popup_main.png b/.playwright-mcp/structure_popup_main.png deleted file mode 100644 index d398cfc..0000000 Binary files a/.playwright-mcp/structure_popup_main.png and /dev/null differ diff --git a/WebContent/WEB-INF/classes/com/pms/mapper/productionplanning.xml b/WebContent/WEB-INF/classes/com/pms/mapper/productionplanning.xml index 5d60271..2926e4e 100644 --- a/WebContent/WEB-INF/classes/com/pms/mapper/productionplanning.xml +++ b/WebContent/WEB-INF/classes/com/pms/mapper/productionplanning.xml @@ -3011,4 +3011,326 @@ LEFT JOIN USER_INFO UI ON UI.USER_ID = T.WRITER WHERE T.OBJID::VARCHAR = #{objid} + + + + + + + + + + INSERT INTO m_bom_data ( + objid, + project_mgmt_objid, + bom_report_objid, + parent_objid, + child_objid, + parent_part_no, + part_no, + part_name, + qty, + aggregate_qty, + level, + material, + heat_treat_hardness, + heat_treat_method, + surface_treatment, + supplier_name, + category_name, + raw_material, + size, + order_qty, + quantity, + production_qty, + processor_name, + process_due_date, + grinding_due_date, + writer, + regdate + ) VALUES ( + #{OBJID}::NUMERIC, + #{projectMgmtObjid}::NUMERIC, + #{BOM_REPORT_OBJID}::NUMERIC, + + + #{PARENT_OBJID}::NUMERIC, + + + NULL, + + + + + #{CHILD_OBJID}::NUMERIC, + + + NULL, + + + #{PARENT_PART_NO}, + #{PART_NO}, + #{PART_NAME}, + #{QTY}::NUMERIC, + #{AGGREGATE_QTY}::NUMERIC, + #{LEVEL}::INTEGER, + #{MATERIAL}, + #{HEAT_TREAT_HARDNESS}, + #{HEAT_TREAT_METHOD}, + #{SURFACE_TREATMENT}, + #{SUPPLIER_NAME}, + #{CATEGORY_NAME}, + #{RAW_MATERIAL}, + #{SIZE}, + #{ORDER_QTY}::NUMERIC, + #{QUANTITY}::NUMERIC, + #{PRODUCTION_QTY}::NUMERIC, + #{PROCESSOR_NAME}, + #{PROCESS_DUE_DATE}, + #{GRINDING_DUE_DATE}, + #{writer}, + NOW() + ) + + + + + UPDATE project_mgmt + SET + mbom_version = COALESCE(mbom_version, 0) + 1, + mbom_regdate = NOW(), + mbom_writer = #{writer}, + mbom_status = 'Y' + WHERE objid = #{projectMgmtObjid} + + + + + UPDATE m_bom_data + SET + STATUS = 'DELETED', + EDIT_DATE = NOW() + WHERE PROJECT_MGMT_OBJID = #{projectMgmtObjid}::NUMERIC + AND STATUS = 'ACTIVE' + + + + + UPDATE project_mgmt + SET + mbom_status = NULL, + mbom_version = NULL, + mbom_regdate = NULL, + mbom_writer = NULL + WHERE objid = #{projectMgmtObjid} + + + + diff --git a/WebContent/WEB-INF/view/productionplanning/mBomEbomSelectPopup.jsp b/WebContent/WEB-INF/view/productionplanning/mBomEbomSelectPopup.jsp index 1e522bc..48645a5 100644 --- a/WebContent/WEB-INF/view/productionplanning/mBomEbomSelectPopup.jsp +++ b/WebContent/WEB-INF/view/productionplanning/mBomEbomSelectPopup.jsp @@ -28,32 +28,33 @@ } .current-ebom-section { background: #f9f9f9; - padding: 20px; + padding: 10px 15px; margin-bottom: 10px; border: 1px solid #ddd; border-radius: 5px; } .current-ebom-section h4 { - margin: 0 0 15px 0; + margin: 0 0 8px 0; color: #333; - font-size: 16px; + font-size: 13px; } .ebom-info-table { width: 100%; border-collapse: collapse; background: white; - margin-bottom: 15px; + margin-bottom: 8px; + font-size: 12px; } .ebom-info-table th { background: #f5f5f5; - padding: 10px; + padding: 6px 8px; text-align: left; border: 1px solid #ddd; - width: 150px; + width: 120px; font-weight: bold; } .ebom-info-table td { - padding: 10px; + padding: 6px 8px; border: 1px solid #ddd; } .ebom-list-section { @@ -314,7 +315,7 @@ function fn_removeEbom() {
-

다른 E-BOM으로 변경하려면 "E-BOM 변경" 버튼을 클릭하세요.

+

다른 E-BOM으로 변경하려면 "E-BOM 변경" 버튼을 클릭하세요.

diff --git a/WebContent/WEB-INF/view/productionplanning/mBomFormPopup.jsp b/WebContent/WEB-INF/view/productionplanning/mBomFormPopup.jsp new file mode 100644 index 0000000..c8f5ddb --- /dev/null +++ b/WebContent/WEB-INF/view/productionplanning/mBomFormPopup.jsp @@ -0,0 +1,694 @@ +<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> +<%@ page import="com.pms.common.utils.*"%> +<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%> +<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> +<%@ page import="java.util.*" %> +<%@include file= "/init.jsp" %> + + + + +<%=Constants.SYSTEM_NAME%> + + + + + + +
+ + +
+ +
+

M-BOM 관리 - 프로젝트: ${projectInfo.PROJECT_NO} / 품번: ${projectInfo.PART_NO} / 품명: ${projectInfo.PART_NAME}

+
+ + + +
+
+ + +
+
+
+
+
+ + + diff --git a/WebContent/WEB-INF/view/productionplanning/mBomMgmtList.jsp b/WebContent/WEB-INF/view/productionplanning/mBomMgmtList.jsp index d467ff0..cce200a 100644 --- a/WebContent/WEB-INF/view/productionplanning/mBomMgmtList.jsp +++ b/WebContent/WEB-INF/view/productionplanning/mBomMgmtList.jsp @@ -283,7 +283,7 @@ function fn_openEBomSelectPopup(projectMgmtObjid, partNo, partName, bomReportObj // M-BOM 팝업 function fn_openMBomPopup(objId) { - var popup_width = 1800; + var popup_width = 1400; var popup_height = 800; var url = "/productionplanning/mBomFormPopup.do?objId=" + objId; fn_centerPopup(popup_width, popup_height, url, 'mbomPopup'); diff --git a/src/com/pms/controller/ProductionPlanningController.java b/src/com/pms/controller/ProductionPlanningController.java index 70e4ec3..0862221 100644 --- a/src/com/pms/controller/ProductionPlanningController.java +++ b/src/com/pms/controller/ProductionPlanningController.java @@ -1007,4 +1007,168 @@ public class ProductionPlanningController extends BaseService { } return resultMap; } + + /** + * M-BOM 팝업 + * @param request + * @param paramMap + * @return + */ + @RequestMapping("/productionplanning/mBomFormPopup.do") + public String mBomFormPopup(HttpServletRequest request, @RequestParam Map paramMap) { + try { + String objId = CommonUtils.checkNull(paramMap.get("objId")); + + // PROJECT_MGMT 기본 정보 조회 + if(!objId.isEmpty()) { + Map projectInfo = productionPlanningService.getProjectMgmtInfo(objId); + request.setAttribute("projectInfo", projectInfo); + + // E-BOM이 할당되어 있는지 확인 (PART_OBJID 사용) + String partObjid = CommonUtils.checkNull(projectInfo.get("PART_OBJID")); + if(!partObjid.isEmpty()) { + // E-BOM 정보 조회 + Map ebomInfo = productionPlanningService.getEbomInfo(partObjid); + request.setAttribute("ebomInfo", ebomInfo); + } + } + } catch(Exception e) { + e.printStackTrace(); + } + return "/productionplanning/mBomFormPopup"; + } + + /** + * M-BOM 그리드 데이터 조회 (저장된 M-BOM 또는 빈 데이터) + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/productionplanning/getMBomGridData.do") + public Map getMBomGridData(HttpServletRequest request, @RequestParam Map paramMap) { + Map resultMap = new HashMap<>(); + try { + String projectMgmtObjid = CommonUtils.checkNull(paramMap.get("projectMgmtObjid")); + + if(projectMgmtObjid.isEmpty()) { + resultMap.put("success", false); + resultMap.put("message", "프로젝트 정보가 없습니다."); + return resultMap; + } + + // 저장된 M-BOM 데이터 조회 (없으면 빈 리스트) + List> mbomList = productionPlanningService.getSavedMBomData(projectMgmtObjid); + + resultMap.put("success", true); + resultMap.put("data", mbomList); + } catch(Exception e) { + e.printStackTrace(); + resultMap.put("success", false); + resultMap.put("message", "오류가 발생했습니다: " + e.getMessage()); + } + return resultMap; + } + + /** + * E-BOM을 M-BOM으로 복사 + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/productionplanning/copyEbomToMbom.do") + public Map copyEbomToMbom(HttpServletRequest request, @RequestParam Map paramMap) { + Map resultMap = new HashMap<>(); + try { + System.out.println("===== copyEbomToMbom 호출됨 ====="); + System.out.println("paramMap: " + paramMap); + System.out.println("paramMap.projectMgmtObjid: " + paramMap.get("projectMgmtObjid")); + + String projectMgmtObjid = CommonUtils.checkNull(paramMap.get("projectMgmtObjid")); + System.out.println("변환된 projectMgmtObjid: " + projectMgmtObjid); + + if(projectMgmtObjid.isEmpty()) { + System.out.println("projectMgmtObjid가 비어있음!"); + resultMap.put("success", false); + resultMap.put("message", "프로젝트 정보가 없습니다."); + return resultMap; + } + + // E-BOM 데이터를 M-BOM으로 복사 + System.out.println("getMBomDataFromEbom 호출 - projectMgmtObjid: " + projectMgmtObjid); + List> mbomList = productionPlanningService.getMBomDataFromEbom(projectMgmtObjid); + System.out.println("조회된 mbomList 크기: " + (mbomList != null ? mbomList.size() : "null")); + + if(mbomList == null || mbomList.isEmpty()) { + resultMap.put("success", false); + resultMap.put("message", "E-BOM 데이터가 없습니다. 먼저 E-BOM을 할당해주세요."); + return resultMap; + } + + resultMap.put("success", true); + resultMap.put("data", mbomList); + } catch(Exception e) { + e.printStackTrace(); + resultMap.put("success", false); + resultMap.put("message", "오류가 발생했습니다: " + e.getMessage()); + } + return resultMap; + } + + /** + * M-BOM 데이터 저장 + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/productionplanning/saveMBomData.do") + public Map saveMBomData(HttpServletRequest request, @RequestParam Map paramMap) { + Map resultMap = new HashMap<>(); + try { + // M-BOM 데이터 저장 로직 + productionPlanningService.saveMBomData(request, paramMap); + + resultMap.put("success", true); + resultMap.put("message", "M-BOM이 저장되었습니다."); + } catch(Exception e) { + e.printStackTrace(); + resultMap.put("success", false); + resultMap.put("message", "저장 중 오류가 발생했습니다: " + e.getMessage()); + } + return resultMap; + } + + /** + * M-BOM 데이터 삭제 + * @param request + * @param paramMap + * @return + */ + @ResponseBody + @RequestMapping("/productionplanning/deleteMBomData.do") + public Map deleteMBomData(HttpServletRequest request, @RequestParam Map paramMap) { + Map resultMap = new HashMap<>(); + try { + String projectMgmtObjid = CommonUtils.checkNull(paramMap.get("projectMgmtObjid")); + + if(projectMgmtObjid.isEmpty()) { + resultMap.put("success", false); + resultMap.put("message", "프로젝트 정보가 없습니다."); + return resultMap; + } + + // M-BOM 데이터 삭제 로직 + productionPlanningService.deleteMBomData(projectMgmtObjid); + + resultMap.put("success", true); + resultMap.put("message", "M-BOM이 삭제되었습니다."); + } catch(Exception e) { + e.printStackTrace(); + resultMap.put("success", false); + resultMap.put("message", "삭제 중 오류가 발생했습니다: " + e.getMessage()); + } + return resultMap; + } } diff --git a/src/com/pms/mapper/productionplanning.xml b/src/com/pms/mapper/productionplanning.xml index 5d60271..2926e4e 100644 --- a/src/com/pms/mapper/productionplanning.xml +++ b/src/com/pms/mapper/productionplanning.xml @@ -3011,4 +3011,326 @@ LEFT JOIN USER_INFO UI ON UI.USER_ID = T.WRITER WHERE T.OBJID::VARCHAR = #{objid} + + + + + + + + + + INSERT INTO m_bom_data ( + objid, + project_mgmt_objid, + bom_report_objid, + parent_objid, + child_objid, + parent_part_no, + part_no, + part_name, + qty, + aggregate_qty, + level, + material, + heat_treat_hardness, + heat_treat_method, + surface_treatment, + supplier_name, + category_name, + raw_material, + size, + order_qty, + quantity, + production_qty, + processor_name, + process_due_date, + grinding_due_date, + writer, + regdate + ) VALUES ( + #{OBJID}::NUMERIC, + #{projectMgmtObjid}::NUMERIC, + #{BOM_REPORT_OBJID}::NUMERIC, + + + #{PARENT_OBJID}::NUMERIC, + + + NULL, + + + + + #{CHILD_OBJID}::NUMERIC, + + + NULL, + + + #{PARENT_PART_NO}, + #{PART_NO}, + #{PART_NAME}, + #{QTY}::NUMERIC, + #{AGGREGATE_QTY}::NUMERIC, + #{LEVEL}::INTEGER, + #{MATERIAL}, + #{HEAT_TREAT_HARDNESS}, + #{HEAT_TREAT_METHOD}, + #{SURFACE_TREATMENT}, + #{SUPPLIER_NAME}, + #{CATEGORY_NAME}, + #{RAW_MATERIAL}, + #{SIZE}, + #{ORDER_QTY}::NUMERIC, + #{QUANTITY}::NUMERIC, + #{PRODUCTION_QTY}::NUMERIC, + #{PROCESSOR_NAME}, + #{PROCESS_DUE_DATE}, + #{GRINDING_DUE_DATE}, + #{writer}, + NOW() + ) + + + + + UPDATE project_mgmt + SET + mbom_version = COALESCE(mbom_version, 0) + 1, + mbom_regdate = NOW(), + mbom_writer = #{writer}, + mbom_status = 'Y' + WHERE objid = #{projectMgmtObjid} + + + + + UPDATE m_bom_data + SET + STATUS = 'DELETED', + EDIT_DATE = NOW() + WHERE PROJECT_MGMT_OBJID = #{projectMgmtObjid}::NUMERIC + AND STATUS = 'ACTIVE' + + + + + UPDATE project_mgmt + SET + mbom_status = NULL, + mbom_version = NULL, + mbom_regdate = NULL, + mbom_writer = NULL + WHERE objid = #{projectMgmtObjid} + + + + diff --git a/src/com/pms/service/ProductionPlanningService.java b/src/com/pms/service/ProductionPlanningService.java index 34e60e7..ff25306 100644 --- a/src/com/pms/service/ProductionPlanningService.java +++ b/src/com/pms/service/ProductionPlanningService.java @@ -779,4 +779,201 @@ public class ProductionPlanningService { return resultMap; } + + /** + * PROJECT_MGMT 정보 조회 + * @param projectMgmtObjid + * @return + */ + public Map getProjectMgmtInfo(String projectMgmtObjid) { + Map resultMap = new HashMap<>(); + SqlSession sqlSession = null; + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + Map paramMap = new HashMap<>(); + paramMap.put("objid", projectMgmtObjid); + resultMap = sqlSession.selectOne("productionplanning.getProjectMgmtInfo", paramMap); + } catch(Exception e) { + e.printStackTrace(); + } finally { + if(sqlSession != null) { + sqlSession.close(); + } + } + + return resultMap; + } + + /** + * 저장된 M-BOM 데이터 조회 (없으면 빈 리스트 반환) + * @param projectMgmtObjid + * @return + * @throws Exception + */ + public List> getSavedMBomData(String projectMgmtObjid) throws Exception { + List> resultList = new ArrayList<>(); + SqlSession sqlSession = null; + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + + Map paramMap = new HashMap<>(); + paramMap.put("projectMgmtObjid", projectMgmtObjid); + + // 저장된 M-BOM 데이터 조회 + resultList = sqlSession.selectList("productionplanning.getSavedMBomData", paramMap); + + } catch(Exception e) { + e.printStackTrace(); + throw e; + } finally { + if(sqlSession != null) { + sqlSession.close(); + } + } + + return resultList; + } + + /** + * E-BOM 데이터를 기반으로 M-BOM 데이터 조회/생성 + * @param projectMgmtObjid + * @return + * @throws Exception + */ + public List> getMBomDataFromEbom(String projectMgmtObjid) throws Exception { + List> resultList = new ArrayList<>(); + SqlSession sqlSession = null; + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(); + + // PROJECT_MGMT 정보 조회 + Map paramMap = new HashMap<>(); + paramMap.put("objid", projectMgmtObjid); + + Map projectInfo = sqlSession.selectOne("productionplanning.getProjectMgmtInfo", paramMap); + + if(projectInfo != null) { + // PART_OBJID 사용 (E-BOM의 OBJID) + String partObjid = CommonUtils.checkNull(projectInfo.get("PART_OBJID")); + + if(!partObjid.isEmpty()) { + // E-BOM이 할당되어 있으면 E-BOM 데이터를 복사하여 M-BOM으로 반환 + paramMap.put("bomReportObjid", partObjid); + resultList = sqlSession.selectList("productionplanning.getMBomDataFromEbom", paramMap); + } + } + + } catch(Exception e) { + e.printStackTrace(); + throw e; + } finally { + if(sqlSession != null) { + sqlSession.close(); + } + } + + return resultList; + } + + /** + * M-BOM 데이터 저장 + * @param request + * @param paramMap + * @throws Exception + */ + public void saveMBomData(HttpServletRequest request, Map paramMap) throws Exception { + SqlSession sqlSession = null; + + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); + HttpSession session = request.getSession(); + PersonBean person = (PersonBean)session.getAttribute(Constants.PERSON_BEAN); + String userId = person.getUserId(); + + // M-BOM 그리드 데이터 파싱 + List> gridDataList = JsonUtil.JsonToList(CommonUtils.checkNull(paramMap.get("gridData"))); + + String projectMgmtObjid = CommonUtils.checkNull(paramMap.get("projectMgmtObjid")); + + // 기존 M-BOM 데이터 삭제 (필요한 경우) + if(!projectMgmtObjid.isEmpty()) { + sqlSession.delete("productionplanning.deleteMBomData", paramMap); + } + + // M-BOM 데이터 저장 + for(Map data : gridDataList) { + data.put("projectMgmtObjid", projectMgmtObjid); + data.put("writer", userId); + + // 항상 새로운 OBJID 생성 (E-BOM의 OBJID와 충돌 방지) + data.put("OBJID", CommonUtils.createObjId()); + + // 빈 문자열을 null로 변환 (NUMERIC 타입 컬럼들) + if(CommonUtils.checkNull(data.get("PARENT_OBJID")).isEmpty()) { + data.put("PARENT_OBJID", null); + } + if(CommonUtils.checkNull(data.get("CHILD_OBJID")).isEmpty()) { + data.put("CHILD_OBJID", null); + } + + sqlSession.insert("productionplanning.insertMBomData", data); + } + + // PROJECT_MGMT의 M-BOM 버전 업데이트 + Map updateMap = new HashMap<>(); + updateMap.put("projectMgmtObjid", projectMgmtObjid); + updateMap.put("writer", userId); + sqlSession.update("productionplanning.updateMBomVersion", updateMap); + + sqlSession.commit(); + + } catch(Exception e) { + if(sqlSession != null) { + sqlSession.rollback(); + } + e.printStackTrace(); + throw e; + } finally { + if(sqlSession != null) { + sqlSession.close(); + } + } + } + + /** + * M-BOM 데이터 삭제 + * @param projectMgmtObjid + * @throws Exception + */ + public void deleteMBomData(String projectMgmtObjid) throws Exception { + SqlSession sqlSession = null; + try { + sqlSession = SqlMapConfig.getInstance().getSqlSession(false); + + Map paramMap = new HashMap<>(); + paramMap.put("projectMgmtObjid", projectMgmtObjid); + + // M-BOM 데이터 삭제 + sqlSession.delete("productionplanning.deleteMBomData", paramMap); + + // PROJECT_MGMT의 MBOM_STATUS를 NULL로 업데이트 + sqlSession.update("productionplanning.resetMBomStatus", paramMap); + + sqlSession.commit(); + + } catch(Exception e) { + if(sqlSession != null) { + sqlSession.rollback(); + } + e.printStackTrace(); + throw e; + } finally { + if(sqlSession != null) { + sqlSession.close(); + } + } + } } diff --git a/test_contract_registration.md b/test_contract_registration.md deleted file mode 100644 index 9ad83bd..0000000 --- a/test_contract_registration.md +++ /dev/null @@ -1,166 +0,0 @@ -# 영업관리 등록창 테스트 가이드 - -## 📋 테스트 개요 - -`docs/영업_계약_수정.md` 문서에 따라 구현된 새로운 영업관리 등록창의 데이터 저장 기능을 테스트합니다. - -## 🚀 테스트 환경 - -- **서버 URL**: http://localhost:8090 -- **테스트 계정**: plm_admin (패스워드는 관리자에게 문의) -- **테스트 페이지**: http://localhost:8090/contractMgmt/contracMgmtFormPopup.do - -## ✅ 구현 완료 사항 - -### 1. 백엔드 수정 완료 -- **ContractMgmtController.java**: 신규 공통코드 2개 추가 (통화단위, 계약방식) -- **ContractMgmtService.java**: CONTRACT_MGMT 테이블 사용하도록 변경, 25개 신규 필드 처리 -- **contractMgmt.xml**: saveContractMgmtInfo 쿼리에 25개 신규 필드 추가 - -### 2. 프론트엔드 수정 완료 -- **contracMgmtFormPopup.jsp**: 5개 섹션으로 재구성 - - 📋 [영업정보] - - 🔧 [사양상세] - - 📈 [영업진행] - - 💰 [견적이력 및 결과] - - 📝 [특이사항] - -### 3. 데이터베이스 준비 완료 -- **공통코드 데이터**: 6개 공통코드의 부모/하위 데이터 준비 완료 -- **테이블 구조**: CONTRACT_MGMT 테이블에 25개 신규 필드 확인 - -## 🧪 테스트 절차 - -### Step 1: 로그인 -1. http://localhost:8090 접속 -2. plm_admin 계정으로 로그인 - -### Step 2: 영업관리 화면 접근 -1. 메뉴에서 "영업관리" → "계약관리" 선택 -2. "등록" 버튼 클릭하여 등록창 열기 - -### Step 3: 테스트 데이터 입력 - -#### 📋 [영업정보] 섹션 -- **계약구분**: 개발 선택 -- **과거프로젝트번호**: PRJ-2024-001 -- **고객사**: 기존 고객사 선택 -- **제품군**: 기존 제품 선택 -- **장비명**: 테스트 압력용기 시스템 -- **설비대수**: 2 -- **요청납기일**: 2025-12-31 -- **입고지**: 서울특별시 강남구 -- **셋업지**: 경기도 성남시 - -#### 🔧 [사양상세] 섹션 -- **재질**: SUS304 -- **압력(BAR)**: 10.5 -- **온도(℃)**: 85 -- **용량(LITER)**: 1000 -- **Closure Type**: Bolted Cover -- **기타(소모품)**: 가스켓, 볼트 -- **전압**: 220V -- **인증여부**: KS 인증 완료 - -#### 📈 [영업진행] 섹션 -- **진행단계**: 견적제출 선택 - -#### 💰 [견적이력 및 결과] 섹션 -- **통화**: KRW 선택 -- **견적금액(1차)**: 50,000,000 -- **견적금액(2차)**: 48,000,000 -- **견적금액(3차)**: 45,000,000 -- **수주일**: 2025-08-15 -- **수주가**: 자동계산 확인 (90,000,000) -- **Result**: 수주 선택 -- **계약방식**: 조달 선택 -- **P/O No**: PO-2025-001 -- **PM**: 기존 사용자 선택 -- **당사프로젝트명**: 압력용기 개발 프로젝트 - -#### 📝 [특이사항] 섹션 -``` -고객 요구사항: 내압 테스트 필수 -납기일 엄수 요청 -품질 인증서 제출 필요 -``` - -### Step 4: 저장 테스트 -1. "저장" 버튼 클릭 -2. 성공 메시지 확인 -3. 저장된 데이터 목록에서 확인 - -## 🔍 검증 포인트 - -### 1. 화면 구성 검증 -- [ ] 5개 섹션이 올바르게 표시되는가? -- [ ] 공통코드 선택 옵션이 정상 로딩되는가? -- [ ] 자동계산 기능이 동작하는가? (수주가 = 최신견적금액 × 설비대수) - -### 2. 데이터 저장 검증 -- [ ] 25개 신규 필드가 모두 저장되는가? -- [ ] 기존 필드와 신규 필드가 함께 저장되는가? -- [ ] 저장 후 목록에서 데이터가 확인되는가? - -### 3. 오류 처리 검증 -- [ ] 필수 필드 누락 시 적절한 오류 메시지가 표시되는가? -- [ ] 잘못된 데이터 입력 시 검증이 동작하는가? - -## 🐛 알려진 이슈 - -### 1. 로그인 세션 필요 -- API 직접 호출 시 세션 인증이 필요함 -- 브라우저에서 로그인 후 테스트 권장 - -### 2. 공통코드 데이터 -- 신규 공통코드 2개(통화단위, 계약방식)가 아직 데이터베이스에 등록되지 않았을 수 있음 -- 필요시 `docs/insert_common_codes.sql` 실행 - -## 📊 테스트 결과 기록 - -### 성공 케이스 -```json -{ - "RESULT": { - "result": true, - "msg": "저장되었습니다." - } -} -``` - -### 실패 케이스 -```json -{ - "RESULT": { - "result": false, - "msg": "저장에 실패하였습니다." - } -} -``` - -## 🔧 문제 해결 - -### 1. 저장 실패 시 -1. 브라우저 개발자 도구에서 네트워크 탭 확인 -2. 서버 로그 확인: `docker-compose -f docker-compose.dev.yml logs plm-ilshin` -3. 데이터베이스 연결 상태 확인 - -### 2. 화면 오류 시 -1. 브라우저 콘솔에서 JavaScript 오류 확인 -2. CSS 파일 로딩 상태 확인 -3. JSP 컴파일 오류 확인 - -## 📞 지원 - -테스트 중 문제 발생 시: -1. 브라우저 개발자 도구 스크린샷 -2. 서버 로그 복사 -3. 입력한 테스트 데이터 기록 - -위 정보와 함께 문의하시기 바랍니다. - ---- - -**마지막 업데이트**: 2025-07-14 -**테스트 환경**: Docker 개발환경, PostgreSQL 데이터베이스 -**구현 완료도**: 95% (로그인 세션 테스트 제외) diff --git a/test_contract_registration_final.md b/test_contract_registration_final.md deleted file mode 100644 index 8f853c5..0000000 --- a/test_contract_registration_final.md +++ /dev/null @@ -1,207 +0,0 @@ -# 🎯 영업관리 등록창 최종 테스트 가이드 - -## 📋 현재 구현 상태 - -### ✅ **완료된 작업 (95%)** - -#### 1. **백엔드 수정 완료** - -- **ContractMgmtController.java**: 신규 공통코드 2개 추가 (통화단위, 계약방식) -- **ContractMgmtService.java**: CONTRACT_MGMT 테이블 사용하도록 변경, 25개 신규 필드 처리 -- **contractMgmt.xml**: saveContractMgmtInfo 쿼리에 25개 신규 필드 추가 - -#### 2. **프론트엔드 수정 완료** - -- **contracMgmtFormPopup.jsp**: 5개 섹션으로 완전 재구성 - - 📋 [영업정보]: 계약구분, 과거프로젝트번호, 국내/해외, 고객사, 제품군, 제품코드, 장비명, 설비대수, 요청납기일, 입고지, 셋업지 - - 🔧 [사양상세]: 재질, 압력(BAR), 온도(℃), 용량(LITER), Closure Type, 기타(소모품), 전압, 인증여부 - - 📈 [영업진행]: 진행단계 선택 - - 💰 [견적이력 및 결과]: 통화, 견적금액(1/2/3차), 수주일, 수주가(자동계산), Result, 계약방식, 실패사유, P/O No, PM, 당사프로젝트명 - - 📝 [특이사항]: 텍스트 영역 - -#### 3. **데이터베이스 준비 완료** - -- **공통코드 데이터**: 6개 공통코드의 부모/하위 데이터 완전 작성 -- **테이블 구조**: CONTRACT_MGMT 테이블에 25개 신규 필드 확인 - -### 🚫 **현재 문제점 (5%)** - -#### API 호출 시 세션 인증 문제 - -- **현상**: `{"RESULT":{"result":false,"msg":"저장에 실패하였습니다."}}` -- **원인**: PersonBean 세션 정보 없음으로 인한 NullPointerException -- **해결**: 브라우저에서 로그인 후 테스트 필요 - -## 🧪 **브라우저 테스트 방법** - -### Step 1: 서버 접근 - -``` -URL: http://localhost:8090 -상태: ✅ 정상 실행 중 -``` - -### Step 2: 로그인 - -``` -계정: plm_admin (또는 시스템 관리자에게 문의) -패스워드: 관리자에게 문의 -``` - -### Step 3: 영업관리 화면 접근 - -1. 메뉴에서 **"영업관리"** 클릭 -2. **"계약관리"** 하위 메뉴 클릭 -3. **"신규 등록"** 버튼 클릭 - -### Step 4: 등록창 테스트 - -URL: `http://localhost:8090/contractMgmt/contracMgmtFormPopup.do` - -#### 필수 입력 필드 테스트: - -``` -[영업정보] -- 계약구분: "개발" 선택 -- 장비명: "테스트 장비명" 입력 -- 설비대수: "1" 입력 - -[사양상세] -- 재질: "SUS316L" 입력 -- 압력(BAR): "10.5" 입력 - -[영업진행] -- 진행단계: "사양협의" 선택 - -[특이사항] -- 특이사항: "테스트용 영업관리 데이터입니다." 입력 -``` - -### Step 5: 저장 테스트 - -1. **"저장"** 버튼 클릭 -2. **성공 메시지** 확인: "저장되었습니다." -3. **리스트 화면**에서 저장된 데이터 확인 - -## 🔧 **자동계산 기능 테스트** - -### 수주가 자동계산 테스트: - -1. **견적금액(1차)**: "1000000" 입력 -2. **설비대수**: "2" 입력 -3. **수주가**: 자동으로 "2000000" 계산 확인 - -### 계산 공식: - -```javascript -수주가 = 최신 견적금액 × 설비대수 -``` - -## 📊 **예상 결과** - -### ✅ **성공 시나리오** - -```json -{ - "RESULT": { - "result": true, - "msg": "저장되었습니다." - } -} -``` - -### 🔍 **데이터 확인 방법** - -1. **리스트 화면**: 저장된 데이터가 목록에 표시 -2. **상세 화면**: 저장된 모든 필드값 확인 -3. **데이터베이스**: CONTRACT_MGMT 테이블에 레코드 생성 확인 - -## 🎯 **테스트 체크리스트** - -### 기본 기능 테스트: - -- [ ] 로그인 성공 -- [ ] 등록창 정상 로딩 (5개 섹션 표시) -- [ ] 공통코드 정상 로딩 (계약구분, 진행단계, 통화, 계약방식 등) -- [ ] 필수 필드 입력 -- [ ] 저장 버튼 클릭 -- [ ] 성공 메시지 확인 -- [ ] 리스트에서 데이터 확인 - -### 고급 기능 테스트: - -- [ ] 자동계산 기능 (수주가 = 견적금액 × 설비대수) -- [ ] 캘린더 기능 (요청납기일, 수주일) -- [ ] 파일 첨부 기능 (입수자료, 제출자료) -- [ ] 수정 기능 -- [ ] 삭제 기능 - -## 🚨 **문제 발생 시 대응** - -### 로그인 실패 시: - -``` -1. 계정 정보 확인 -2. 시스템 관리자에게 문의 -3. 데이터베이스 사용자 테이블 확인 -``` - -### 저장 실패 시: - -``` -1. 필수 필드 입력 확인 -2. 브라우저 개발자 도구 > 네트워크 탭에서 오류 확인 -3. 서버 로그 확인 -``` - -### 화면 로딩 실패 시: - -``` -1. 서버 상태 확인: http://localhost:8090 -2. 브라우저 캐시 클리어 -3. 다른 브라우저에서 테스트 -``` - -## 📈 **성능 확인 사항** - -### 응답 시간: - -- **등록창 로딩**: 2초 이내 -- **저장 처리**: 3초 이내 -- **리스트 조회**: 2초 이내 - -### 브라우저 호환성: - -- **Chrome**: ✅ 권장 -- **Firefox**: ✅ 지원 -- **Safari**: ✅ 지원 -- **IE**: ⚠️ 제한적 지원 - -## 🎉 **최종 결과 예상** - -### 성공 시: - -``` -✅ 영업관리 등록창 정상 동작 -✅ 25개 신규 필드 모두 저장 -✅ 자동계산 기능 정상 동작 -✅ 공통코드 정상 연동 -✅ 파일 첨부 기능 정상 동작 -``` - -### 완료도: **95%** - -**남은 5%는 실제 브라우저 테스트를 통한 최종 검증입니다.** - ---- - -## 📞 **지원 연락처** - -문제 발생 시 다음 정보와 함께 문의하세요: - -- 브라우저 종류 및 버전 -- 발생한 오류 메시지 -- 입력한 데이터 -- 스크린샷 (가능한 경우) - -**모든 백엔드 로직, 프론트엔드 화면, 데이터베이스 구조가 완성되어 실제 사용 가능한 상태입니다!** 🎯