Merge pull request 'V2025111901' (#69) from V2025111901 into main

Reviewed-on: #69
This commit was merged in pull request #69.
This commit is contained in:
2025-11-20 04:21:10 +00:00
8 changed files with 198 additions and 117 deletions

View File

@@ -24,12 +24,6 @@ $(document).ready(function(){
//날짜
_fnc_datepick();
// 그리드 높이 동적 계산 (Total 합계 영역 + 여유 공간 80px)
fnc_calculateContentHeight("gridDiv", 80);
$(window).resize(function() {
fnc_calculateContentHeight("gridDiv", 80);
});
$('.select2').select2();
// 품번/품명 Select2 AJAX 초기화 (common.js의 새 함수 사용)
@@ -242,8 +236,15 @@ function fn_search(){
_tabulGrid.off("renderComplete");
_tabulGrid.on("renderComplete", function(){
fn_calculateTotalFromGrid();
// 그리드 렌더링 완료 후 높이 계산
fnc_calculateContentHeight("gridDiv", 20);
});
}
// 윈도우 리사이즈 이벤트 (한 번만 등록)
$(window).off("resize.gridHeight").on("resize.gridHeight", function() {
fnc_calculateContentHeight("gridDiv", 20);
});
}
// 그리드에 표시된 데이터의 원화총액 합계 계산

View File

@@ -219,8 +219,13 @@
html += '<td><input type="text" class="item-part-no" value="' + (item.PART_NO || '') + '" readonly style="background:#f5f5f5;" /></td>';
html += '<td><input type="text" class="item-part-name" value="' + (item.PART_NAME || '') + '" readonly style="background:#f5f5f5;" /></td>';
html += '<td><input type="text" class="item-serial-no" value="' + serialNoDisplay + '" readonly style="background:#f5f5f5;" title="' + serialNo + '" /></td>';
// ORDER_QUANTITY으면 QUANTITY 사용 (견적서에서 가져온 값)
html += '<td><input type="text" class="item-quantity" value="' + (item.ORDER_QUANTITY || item.QUANTITY || '') + '" numberOnly required /></td>';
// Machine이고 프로젝트으면 수량 수정 불가
var isQuantityReadonly = ("${isMachine}" === "Y" && "${hasProject}" === "Y");
if(isQuantityReadonly) {
html += '<td><input type="text" class="item-quantity" value="' + (item.ORDER_QUANTITY || item.QUANTITY || '') + '" numberOnly readonly style="background:#f5f5f5;" title="Machine 제품은 프로젝트 생성 후 수량 변경이 불가능합니다." /></td>';
} else {
html += '<td><input type="text" class="item-quantity" value="' + (item.ORDER_QUANTITY || item.QUANTITY || '') + '" numberOnly required /></td>';
}
// ORDER_UNIT_PRICE 수정 가능
html += '<td><input type="text" class="item-unit-price" value="' + (item.ORDER_UNIT_PRICE || '') + '" numberOnly required /></td>';
// ORDER_SUPPLY_PRICE 자동 계산

View File

@@ -122,6 +122,14 @@ var columns = [
]
},
{
title:'장비', headerHozAlign:'center',
columns:[
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '조립', field : 'ASSEMBLY', sorter:"string"},
{headerHozAlign : 'center', hozAlign : 'center', width : '90', title : '검증', field : 'VERIFICATION', sorter:"string"}
]
},
{
title:'출하', headerHozAlign:'center',
columns:[

View File

@@ -5990,6 +5990,7 @@ SELECT T1.LEV, T1.BOM_REPORT_OBJID, T1.ROOT_PART_NO, T1.PATH, T1.LEAF, T2.*
LEFT JOIN PART_BOM_REPORT PBR ON BPQ.BOM_REPORT_OBJID = PBR.OBJID
WHERE COALESCE(BPQ.PARENT_OBJID, '') = ''
AND COALESCE(BPQ.STATUS, '') NOT IN ('deleting', 'deleted')
AND PBR.STATUS = 'Y'
<!-- BOM REPORT 검색 조건 -->
<if test="customer_cd != null and !''.equals(customer_cd)">
AND PBR.CUSTOMER_OBJID = #{customer_cd}
@@ -6113,6 +6114,7 @@ SELECT T1.LEV, T1.BOM_REPORT_OBJID, T1.ROOT_PART_NO, T1.PATH, T1.LEAF, T2.*
LEFT JOIN PART_MNG PM ON BT.LAST_PART_OBJID = PM.OBJID
LEFT JOIN PART_BOM_REPORT PBR ON BT.BOM_REPORT_OBJID = PBR.OBJID
WHERE (PM.OBJID IS NULL OR PM.STATUS IN ('create', 'release'))
AND PBR.STATUS = 'Y'
<!-- 품번/품명 검색 시: 검색된 품번을 포함하는 PATH만 표시 -->
<if test="search_partNo != null and !''.equals(search_partNo)">
AND EXISTS (
@@ -6401,9 +6403,11 @@ SELECT T1.LEV, T1.BOM_REPORT_OBJID, T1.ROOT_PART_NO, T1.PATH, T1.LEAF, T2.*
ARRAY[BPQ.PART_NO::TEXT] AS PATH
FROM BOM_PART_QTY BPQ
INNER JOIN PART_MNG PM ON COALESCE(BPQ.LAST_PART_OBJID, BPQ.PART_NO) = PM.OBJID
INNER JOIN PART_BOM_REPORT PBR ON BPQ.BOM_REPORT_OBJID = PBR.OBJID
WHERE 1=1
AND COALESCE(BPQ.STATUS, '') NOT IN ('deleting', 'deleted')
AND PM.STATUS IN ('create', 'release')
AND PBR.STATUS = 'Y'
<if test="search_partNo != null and !''.equals(search_partNo)">
AND UPPER(PM.PART_NO) LIKE UPPER('%${search_partNo}%')
</if>
@@ -6447,14 +6451,9 @@ SELECT T1.LEV, T1.BOM_REPORT_OBJID, T1.ROOT_PART_NO, T1.PATH, T1.LEAF, T2.*
BT.ITEM_QTY,
BT.QTY AS P_QTY,
BT.SEQ,
-- LEAF 계산 (역전개: 하위 항목이 있는지 체크 - 정전개와 동일)
(
SELECT CASE WHEN COUNT(*) > 0 THEN 0 ELSE 1 END
FROM BOM_PART_QTY BPQ2
WHERE BPQ2.PARENT_OBJID = BT.CHILD_OBJID
AND BPQ2.BOM_REPORT_OBJID = BT.BOM_REPORT_OBJID
AND COALESCE(BPQ2.STATUS, '') NOT IN ('deleting', 'deleted')
) AS LEAF,
-- LEAF 계산 (역전개: 검색 시작점만 LEAF = 1, 나머지는 0)
-- 검색 시작점은 LEV = 1 (역순 표시하면 MAX_LEVEL)
CASE WHEN BT.LEV = 1 THEN 1 ELSE 0 END AS LEAF,
-- PART 정보
PM.OBJID AS PART_OBJID,
PM.PART_NO,
@@ -6494,6 +6493,7 @@ SELECT T1.LEV, T1.BOM_REPORT_OBJID, T1.ROOT_PART_NO, T1.PATH, T1.LEAF, T2.*
LEFT JOIN PART_BOM_REPORT PBR ON BT.BOM_REPORT_OBJID = PBR.OBJID
WHERE 1=1
AND (PM.OBJID IS NULL OR PM.STATUS IN ('create', 'release'))
AND PBR.STATUS = 'Y'
ORDER BY
PBR.REGDATE DESC,

View File

@@ -7464,39 +7464,57 @@ SELECT
<!-- 신규 PROJECT_NO 생성 로직: 주문유형-제품구분-날짜-순번 형식 (예: R-AS-250302-001) -->
,(
SELECT
-- 주문유형 코드 (CATEGORY_CD를 영문 약어로 매핑)
CASE CODE_NAME(CATEGORY_CD)
WHEN '오버홀' THEN 'O'
WHEN '개조' THEN 'M'
WHEN '개발' THEN 'D'
WHEN '견적' THEN 'Q'
WHEN '수리' THEN 'R'
WHEN '판매' THEN 'S'
ELSE 'T'
END || '-' ||
-- 제품구분 코드 (PRODUCT의 CODE_NAME에서 슬래시 제거)
REPLACE(CODE_NAME(PRODUCT), '/', '') || '-' ||
SELECT
-- 주문유형 코드 (CATEGORY_CD를 영문 약어로 매핑)
CASE CODE_NAME(CATEGORY_CD)
WHEN '오버홀' THEN 'O'
WHEN '개조' THEN 'M'
WHEN '개발' THEN 'D'
WHEN '견적' THEN 'Q'
WHEN '수리' THEN 'R'
WHEN '판매' THEN 'S'
ELSE 'T'
END || '-' ||
-- 제품구분 코드 (PRODUCT를 약어로 매핑)
CASE CODE_NAME(PRODUCT)
WHEN 'Machine' THEN 'MC'
WHEN 'A/S' THEN 'AS'
WHEN 'D/S' THEN 'DS'
WHEN 'B/S' THEN 'BS'
WHEN 'C/T' THEN 'CT'
WHEN 'A/C' THEN 'AC'
WHEN 'W/M' THEN 'WM'
ELSE REPLACE(CODE_NAME(PRODUCT), '/', '')
END || '-' ||
-- 날짜 (YYMMDD)
TO_CHAR(CURRENT_DATE, 'YYMMDD') || '-' ||
-- 순번 (001, 002, ...)
LPAD(
COALESCE(
(
SELECT MAX(SUBSTRING(PROJECT_NO FROM '\d{3}$')::INTEGER) + 1
FROM PROJECT_MGMT
WHERE PROJECT_NO LIKE
CASE CODE_NAME(CATEGORY_CD)
WHEN '오버홀' THEN 'O'
WHEN '개조' THEN 'M'
WHEN '개발' THEN 'D'
WHEN '견적' THEN 'Q'
WHEN '수리' THEN 'R'
WHEN '판매' THEN 'S'
ELSE 'T'
END || '-' ||
REPLACE(CODE_NAME(PRODUCT), '/', '') || '-' ||
TO_CHAR(CURRENT_DATE, 'YYMMDD') || '-%'
SELECT MAX(SUBSTRING(PROJECT_NO FROM '\d{3}$')::INTEGER) + 1
FROM PROJECT_MGMT
WHERE PROJECT_NO LIKE
CASE CODE_NAME(CATEGORY_CD)
WHEN '오버홀' THEN 'O'
WHEN '개조' THEN 'M'
WHEN '개발' THEN 'D'
WHEN '견적' THEN 'Q'
WHEN '수리' THEN 'R'
WHEN '판매' THEN 'S'
ELSE 'T'
END || '-' ||
CASE CODE_NAME(PRODUCT)
WHEN 'Machine' THEN 'MC'
WHEN 'A/S' THEN 'AS'
WHEN 'D/S' THEN 'DS'
WHEN 'B/S' THEN 'BS'
WHEN 'C/T' THEN 'CT'
WHEN 'A/C' THEN 'AC'
WHEN 'W/M' THEN 'WM'
ELSE REPLACE(CODE_NAME(PRODUCT), '/', '')
END || '-' ||
TO_CHAR(CURRENT_DATE, 'YYMMDD') || '-%'
),
1
)::TEXT,
@@ -7530,14 +7548,14 @@ SELECT
,EST_USER_ID
,EST_COMP_DATE
,EST_RESULT_CD
,AREA_CD
,MECHANICAL_TYPE
,#{overhaul_order}
,#{is_temp}
,#{part_objid}
,#{part_no}
,#{part_name}
,#{quantity}
,AREA_CD
,MECHANICAL_TYPE
,#{overhaul_order}
,#{is_temp}
,#{part_objid}
,#{part_no}
,#{part_name}
,#{quantity}
FROM CONTRACT_MGMT
WHERE OBJID=#{objId}
)

View File

@@ -2481,6 +2481,12 @@ public class ContractMgmtController {
String apprStatus = CommonUtils.nullToEmpty((String)info.get("APPR_STATUS"));
boolean useEstimateTemplate = "결재완료".equals(apprStatus);
// Machine 여부 및 프로젝트 존재 여부 확인
String productCd = CommonUtils.nullToEmpty((String)info.get("PRODUCT"));
boolean isMachine = "0000928".equals(productCd);
Map projectInfo = contractMgmtService.checkProjectExists(paramMap);
boolean hasProject = (projectInfo != null);
request.setAttribute("code_map", code_map);
request.setAttribute("info", info);
request.setAttribute("contractInfo", contractInfo);
@@ -2488,6 +2494,8 @@ public class ContractMgmtController {
request.setAttribute("objId", objId);
request.setAttribute("actionType", actionType);
request.setAttribute("useEstimateTemplate", useEstimateTemplate ? "Y" : "N");
request.setAttribute("isMachine", isMachine ? "Y" : "N");
request.setAttribute("hasProject", hasProject ? "Y" : "N");
} catch(Exception e) {
e.printStackTrace();

View File

@@ -535,7 +535,7 @@
-- 수주수량 (CONTRACT_MGMT의 QUANTITY 또는 CONTRACT_ITEM 합계)
,COALESCE(
NULLIF(T.QUANTITY, '')::NUMERIC,
(SELECT COALESCE(SUM(CAST(QUANTITY AS NUMERIC)), 0) FROM CONTRACT_ITEM WHERE CONTRACT_OBJID = T.OBJID AND STATUS = 'ACTIVE')
(SELECT COALESCE(SUM(CAST(ORDER_QUANTITY AS NUMERIC)), 0) FROM CONTRACT_ITEM WHERE CONTRACT_OBJID = T.OBJID AND STATUS = 'ACTIVE')
) AS ORDER_QUANTITY
,CASE
WHEN T.ORDER_TOTAL_AMOUNT IS NOT NULL AND T.ORDER_TOTAL_AMOUNT != ''
@@ -4758,6 +4758,16 @@ WHERE
WHERE OBJID = #{objId}
</update>
<!-- 계약 기본 정보 조회 (제품구분 등) -->
<select id="getContractBasicInfo" parameterType="map" resultType="map">
SELECT
OBJID,
PRODUCT,
CATEGORY_CD
FROM CONTRACT_MGMT
WHERE OBJID = #{objId}
</select>
<!-- 계약 품목 조회 -->
<select id="getContractItems" parameterType="map" resultType="map">
SELECT

View File

@@ -2659,85 +2659,116 @@ private String encodeImageToBase64(String imagePath) {
resultList = sqlSession.selectOne("contractMgmt.getProjectListBycontractObjid", paramMap);
boolean hasProject = (resultList != null);
// 제품구분 확인 (DB에서 조회)
Map<String, Object> contractInfo = (Map<String, Object>) sqlSession.selectOne("contractMgmt.getContractBasicInfo", paramMap);
// MyBatis resultType="map"은 소문자로 반환되므로 대문자로 변환
contractInfo = CommonUtils.toUpperCaseMapKey(contractInfo);
String product_cd = contractInfo != null ? CommonUtils.checkNull(contractInfo.get("PRODUCT")) : "";
boolean isMachine = "0000928".equals(product_cd);
if(isMachine) {
System.out.println("제품구분: Machine(0000928) - 품목별 수량만큼 프로젝트 생성");
}
// 품목별로 프로젝트 생성 또는 업데이트
List<Map> contractItems = getContractItems(paramMap);
if(contractItems != null && !contractItems.isEmpty()) {
System.out.println("품목 개수: " + contractItems.size() + "개 - 프로젝트 " + (hasProject ? "업데이트" : "생성") + " 시작");
System.out.println("품목 개수: " + contractItems.size() + "개 - 프로젝트 " + (hasProject ? "업데이트" : "생성") + " 시작" + (isMachine ? " (Machine - 수량별 생성)" : ""));
for(Map item : contractItems) {
if(!hasProject) {
// 프로젝트가 없으면 모든 품목에 대해 생성
Map<String, Object> projectParam = new HashMap<String, Object>();
projectParam.putAll(paramMap); // 기본 정보 복사
// 품목별 정보 설정
projectParam.put("OBJID", CommonUtils.createObjId());
projectParam.put("is_temp", '1');
projectParam.put("part_objid", item.get("PART_OBJID"));
projectParam.put("part_no", item.get("PART_NO"));
projectParam.put("part_name", item.get("PART_NAME"));
projectParam.put("quantity", item.get("ORDER_QUANTITY") != null ? item.get("ORDER_QUANTITY") : item.get("QUANTITY"));
projectParam.put("due_date", item.get("DUE_DATE"));
if("0000170".equals(category_cd) || "0000171".equals(category_cd)){
projectParam.put("overhaul_project_no", target_project_no);
}
System.out.println("프로젝트 생성 - PART_OBJID: " + item.get("PART_OBJID") + ", 품번: " + item.get("PART_NO") + ", 품명: " + item.get("PART_NAME"));
// 프로젝트 등록
cnt = sqlSession.update("project.createProject", projectParam);
// 프로젝트 TASK 등록
cnt = sqlSession.insert("contractMgmt.insertProjectTask", projectParam);
// 프로젝트 SETUP_TASK 등록
cnt = sqlSession.insert("contractMgmt.insertProjectSetupTask", projectParam);
// project_no - unit 폴더 생성
Map<String,Object> projectInfo = (Map)sqlSession.selectOne("project.getProjectMngInfo", projectParam);
projectParam.put("contract_objid", contract_objid);
projectParam.put("customer_product", projectParam.get("mechanical_type"));
List<Map<String,Object>> taskUnitList = (ArrayList)sqlSession.selectList("project.getWbsTaskListByProject", projectParam);
if(CommonUtils.isNotEmpty(taskUnitList) && !taskUnitList.isEmpty()){
String projectNo = (String)projectInfo.get("project_no");
String filepath = Constants.FILE_STORAGE+"\\PART_DATA\\";
for (Map<String, Object> map : taskUnitList) {
File file = new File(filepath+File.separator+projectNo+File.separator+CommonUtils.checkNull((String)map.get("unit_no"))+"-"+CommonUtils.checkNull((String)map.get("task_name")));
if(!file.exists()){
file.mkdirs();
// 수량 가져오기
Object quantityObj = item.get("ORDER_QUANTITY") != null ? item.get("ORDER_QUANTITY") : item.get("QUANTITY");
int itemQuantity = 1;
try {
itemQuantity = Integer.parseInt(String.valueOf(quantityObj));
} catch (Exception e) {
itemQuantity = 1;
}
// Machine인 경우 수량만큼 반복, 아니면 1번만 실행
int loopCount = (isMachine && !hasProject) ? itemQuantity : 1;
for(int q = 0; q < loopCount; q++) {
if(!hasProject) {
// 프로젝트가 없으면 모든 품목에 대해 생성
Map<String, Object> projectParam = new HashMap<String, Object>();
projectParam.putAll(paramMap); // 기본 정보 복사
// 품목별 정보 설정
projectParam.put("OBJID", CommonUtils.createObjId());
projectParam.put("is_temp", '1');
projectParam.put("part_objid", item.get("PART_OBJID"));
projectParam.put("part_no", item.get("PART_NO"));
projectParam.put("part_name", item.get("PART_NAME"));
// Machine인 경우 각 프로젝트의 수량은 1, 아니면 원래 수량
projectParam.put("quantity", isMachine ? "1" : String.valueOf(itemQuantity));
projectParam.put("due_date", item.get("DUE_DATE"));
if("0000170".equals(category_cd) || "0000171".equals(category_cd)){
projectParam.put("overhaul_project_no", target_project_no);
}
if(isMachine) {
System.out.println("프로젝트 생성 [" + (q+1) + "/" + loopCount + "] - PART_OBJID: " + item.get("PART_OBJID") + ", 품번: " + item.get("PART_NO") + ", 품명: " + item.get("PART_NAME") + ", 수량: 1");
} else {
System.out.println("프로젝트 생성 - PART_OBJID: " + item.get("PART_OBJID") + ", 품번: " + item.get("PART_NO") + ", 품명: " + item.get("PART_NAME") + ", 수량: " + itemQuantity);
}
// 프로젝트 등록
cnt = sqlSession.update("project.createProject", projectParam);
// 프로젝트 TASK 등록
cnt = sqlSession.insert("contractMgmt.insertProjectTask", projectParam);
// 프로젝트 SETUP_TASK 등록
cnt = sqlSession.insert("contractMgmt.insertProjectSetupTask", projectParam);
// project_no - unit 폴더 생성
Map<String,Object> projectInfo = (Map)sqlSession.selectOne("project.getProjectMngInfo", projectParam);
projectParam.put("contract_objid", contract_objid);
projectParam.put("customer_product", projectParam.get("mechanical_type"));
List<Map<String,Object>> taskUnitList = (ArrayList)sqlSession.selectList("project.getWbsTaskListByProject", projectParam);
if(CommonUtils.isNotEmpty(taskUnitList) && !taskUnitList.isEmpty()){
String projectNo = (String)projectInfo.get("project_no");
String filepath = Constants.FILE_STORAGE+"\\PART_DATA\\";
for (Map<String, Object> map : taskUnitList) {
File file = new File(filepath+File.separator+projectNo+File.separator+CommonUtils.checkNull((String)map.get("unit_no"))+"-"+CommonUtils.checkNull((String)map.get("task_name")));
if(!file.exists()){
file.mkdirs();
}
}
}
} else {
// 프로젝트가 있으면 모든 품목 업데이트 (수량, 금액 등만)
Map<String, Object> updateParam = new HashMap<String, Object>();
updateParam.putAll(paramMap);
updateParam.put("part_objid", item.get("PART_OBJID"));
updateParam.put("quantity", String.valueOf(itemQuantity));
updateParam.put("due_date", item.get("DUE_DATE"));
System.out.println("프로젝트 업데이트 - PART_OBJID: " + item.get("PART_OBJID") + ", 수량: " + itemQuantity);
sqlSession.update("project.ModifyProjectByContract", updateParam);
}
} else {
// 프로젝트가 있으면 모든 품목 업데이트 (수량, 금액 등만)
Map<String, Object> updateParam = new HashMap<String, Object>();
updateParam.putAll(paramMap);
updateParam.put("part_objid", item.get("PART_OBJID"));
updateParam.put("quantity", item.get("ORDER_QUANTITY") != null ? item.get("ORDER_QUANTITY") : item.get("QUANTITY"));
updateParam.put("due_date", item.get("DUE_DATE"));
System.out.println("프로젝트 업데이트 - PART_OBJID: " + item.get("PART_OBJID") + ", 수량: " + updateParam.get("quantity"));
sqlSession.update("project.ModifyProjectByContract", updateParam);
}
}
} else {
System.out.println("품목이 없습니다 - 기존 방식으로 프로젝트 생성");
// 품목이 없는 경우 기존 방식대로 처리
if(!hasProject){
paramMap.put("OBJID", CommonUtils.createObjId());
paramMap.put("is_temp", '1');
if("0000170".equals(category_cd) || "0000171".equals(category_cd)){
paramMap.put("overhaul_project_no", target_project_no);
}
cnt = sqlSession.update("project.createProject", paramMap);
cnt = sqlSession.insert("contractMgmt.insertProjectTask", paramMap);
cnt = sqlSession.insert("contractMgmt.insertProjectSetupTask", paramMap);
}else{
sqlSession.update("project.ModifyProjectByContract", paramMap);
} else {
System.out.println("품목이 없습니다 - 기존 방식으로 프로젝트 생성");
// 품목이 없는 경우 기존 방식대로 처리
if(!hasProject){
paramMap.put("OBJID", CommonUtils.createObjId());
paramMap.put("is_temp", '1');
if("0000170".equals(category_cd) || "0000171".equals(category_cd)){
paramMap.put("overhaul_project_no", target_project_no);
}
cnt = sqlSession.update("project.createProject", paramMap);
cnt = sqlSession.insert("contractMgmt.insertProjectTask", paramMap);
cnt = sqlSession.insert("contractMgmt.insertProjectSetupTask", paramMap);
}else{
sqlSession.update("project.ModifyProjectByContract", paramMap);
}
}
}
// if(cnt > 0){
//계약완료 일시 메일
// if("0000964".equals(CommonUtils.checkNull(paramMap.get("contract_result")))){