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

Reviewed-on: #73
This commit was merged in pull request #73.
This commit is contained in:
2025-11-21 08:00:40 +00:00
10 changed files with 2073 additions and 106 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -892,6 +892,7 @@ function openProjectFormPopUp(objId){
<body class="bodyNoScroll">
<form name="form1" id="form1" action="" method="post">
<input type="hidden" name="actionType" value="" />
<input type="hidden" name="list_type" value="estimate" />
<!-- 대시보드용 -->
<input type="hidden" name="contract_month" id="contract_month" value="${param.contract_month}">

View File

@@ -54,18 +54,34 @@ $(document).ready(function(){
$(".btnRegist").click(function(){
var selectedData = _tabulGrid.getSelectedData();
if(selectedData.length < 1){
Swal.fire("수주등록할 영업정보를 선택해주십시오.");
return false;
// 선택이 없으면 견적요청+수주 통합 등록 팝업 오픈
var popup_width = 1600;
var popup_height = 800;
var params = "?actionType=regist";
var url = "/contractMgmt/estimateAndOrderRegistFormPopup.do"+params;
fn_centerPopup(popup_width, popup_height, url);
} else if(selectedData.length > 1){
Swal.fire("한번에 한개의 수주만 등록 가능합니다.");
return false;
} else {
var contractObjId = fnc_checkNull(selectedData[0].OBJID);
var popup_width = 1400;
var popup_height = 450;
var params = "?actionType=regist&contractObjId="+contractObjId;
var url = "/contractMgmt/orderRegistFormPopup.do"+params;
fn_centerPopup(popup_width, popup_height, url);
var isDirectOrder = fnc_checkNull(selectedData[0].IS_DIRECT_ORDER);
// 통합 등록 건이면 통합 팝업 오픈 (수정 모드)
if(isDirectOrder === 'Y') {
var popup_width = 1600;
var popup_height = 800;
var params = "?actionType=update&objId="+contractObjId;
var url = "/contractMgmt/estimateAndOrderRegistFormPopup.do"+params;
fn_centerPopup(popup_width, popup_height, url);
} else {
// 일반 견적요청 건이면 기존 수주등록 팝업 오픈
var popup_width = 1400;
var popup_height = 450;
var params = "?actionType=regist&contractObjId="+contractObjId;
var url = "/contractMgmt/orderRegistFormPopup.do"+params;
fn_centerPopup(popup_width, popup_height, url);
}
}
});

View File

@@ -250,7 +250,7 @@ $(document).ready(function(){
,datatype: "local"
,colNames: ["상태","모품번","품번","품명","수량","항목수량","재료","열처리경도","열처리방법","표면처리","공급업체","범주이름"]
,colModel: [
{name:"NOTE",index:"NOTE", width: 160, align:"center", hidden: false, sortable:false, editable:true
{name:"NOTE",index:"NOTE", width: 200, align:"center", hidden: false, sortable:false, editable:true
,editoptions:{
dataInit : function(e){
e.style.fontSize = 13;
@@ -947,17 +947,15 @@ function fn_save(){
</div>
</section>
<div style="font-size:10px;float:left;margin:13px 13px 5px 13px">
<table class="" style="border:0px solid #dd2a00;">
<div style="font-size:10px;float:left;margin:40px 8px 5px 13px;width:39%;">
<table class="" style="border:0px solid #dd2a00;width:100%;">
<colgroup>
<col width="100px">
<col width="180px">
<col width="70px">
<col width="180px">
<col width="70px">
<col width="180px">
<col width="70px">
<col width="150px">
<col width="15%">
<col width="25%">
<col width="10%">
<col width="25%">
<col width="10%">
<col width="15%">
</colgroup>
<tr>
<td style="font-size:12px;"><label for="product_cd">제품구분</label></td>
@@ -969,27 +967,29 @@ function fn_save(){
</td>
<td style="font-size:12px;" class="align_c"><label for="bom_part_no">품번</label></td>
<td>
<input type="text" name="bom_part_no" id="bom_part_no" required reqTitle="품번" value="<%= bomPartNo %>" style="width: 170px;"/>
<input type="text" name="bom_part_no" id="bom_part_no" required reqTitle="품번" value="<%= bomPartNo %>" style="width: 100%;"/>
</td>
<td style="font-size:12px;" class="align_c"><label for="bom_part_name">품명</label></td>
<td>
<input type="text" name="bom_part_name" id="bom_part_name" required reqTitle="품명" value="<%= bomPartName %>" style="width: 170px;"/>
</td>
<td style="font-size:12px;" class="align_c"><label for="version">Version</label></td>
<td>
<input type="text" name="version" id="version" reqTitle="Version" value="<%= bomVersion %>" style="width: 140px;"/>
<input type="text" name="bom_part_name" id="bom_part_name" required reqTitle="품명" value="<%= bomPartName %>" style="width: 200px;"/>
</td>
</tr>
<tr>
<td style="font-size:12px;"><label for="copy_bom_select">E-BOM 복사 대상</label></td>
<td colspan="5">
<select name="copy_bom_select" id="copy_bom_select" style="width: 600px;" class="select2">
<td style="font-size:12px;"><label for="version">Version</label></td>
<td colspan="">
<input type="text" name="version" id="version" reqTitle="Version" value="<%= bomVersion %>" style="width: 170px;"/>
</td>
</tr>
<tr>
<td style="font-size:12px;"><label for="copy_bom_select">E-BOM 복사</label></td>
<td colspan="4">
<select name="copy_bom_select" id="copy_bom_select" style="width: 100%;" class="select2">
<option value="">선택</option>
${code_map.bom_list}
</select>
</td>
<td colspan="2" style="text-align:center;">
<input type="button" class="plm_btns" value="복사" id="btnCopyBom" style="width: 80px;">
<td style="text-align:center;">
<input type="button" class="plm_btns" value="복사" id="btnCopyBom" style="margin-left: 100px; width: 50%;">
</td>
</tr>
@@ -1032,7 +1032,27 @@ function fn_save(){
</td> -->
</tr>
</table>
</div>
</div>
<div style="float:left;margin:5px 13px 5px 8px;width:58%;">
<div id="partExcelPopupFormWrap">
<div class="form_popup_title" style="position:relative;margin-bottom:5px;">&nbsp;&nbsp;&nbsp;CSV upload<!--<img src="/images/btnExcel.png" style="position:absolute; top:9px; right:135px;"/><span style="position:absolute; top:0px; right:10px; cursor:pointer;" id="templateDownload">Template Download</span>--></div>
<div id="excelUploadPopupForm">
<div class="fileDnDWrap">
<div id="excelImportDropZone" class="dropzone" style="height:50px;border:2px dashed #ccc;text-align:center;line-height:50px;background:#f9f9f9;">Drag & Drop CSV 템플릿</div>
<div id="excelImportList">
<table id="excelImportTable" class="excelUploadPopupForm">
<thead>
<tr><td colspan="3">Excel 첨부파일</td></tr>
</thead>
<tbody id="excelImportArea">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- <div id="expenseApplyPopupFormWrap1" style="margin-top:30px;position: ">
@@ -1096,31 +1116,11 @@ function fn_save(){
<div style="clear:both"></div>
<div style="width:100%; display: inline-block; float:left;">
<div style=" margin: 0 8px;">
<div id="partExcelPopupFormWrap">
<div class="form_popup_title" style="position:relative;">&nbsp;&nbsp;&nbsp;CSV upload<!--<img src="/images/btnExcel.png" style="position:absolute; top:9px; right:135px;"/><span style="position:absolute; top:0px; right:10px; cursor:pointer;" id="templateDownload">Template Download</span>--></div>
<div id="excelUploadPopupForm">
<div class="fileDnDWrap">
<div id="excelImportDropZone" class="dropzone" style="height:50px;">Drag & Drop CSV 템플릿</div>
<div id="excelImportList">
<table id="excelImportTable" class="excelUploadPopupForm">
<thead>
<tr><td colspan="3">Excel 첨부파일</td></tr>
</thead>
<tbody id="excelImportArea">
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<section>
<div class="btn_wrap">
<div class="plm_btn_wrap" style="padding:0 8 0 8; text-align: right;">
<input type="button" class="plm_btns" value="저장" onclick="fn_save();">
<input type="button" class="plm_btns" value="저장" id="btnSave" onclick="fn_save();">
<input type="button" class="plm_btns" value="닫기" onclick="window.close();" >
</div>
</div>

View File

@@ -196,29 +196,8 @@ ui-jqgrid tr.jqgrow td {
{headerHozAlign : 'center', hozAlign : 'center', width : '120', title : '비고', field : 'REMARK' }
];
// 중복 요청 방지 플래그
var isSearching = false;
function fn_search(){
// 이미 검색 중이면 중단
if (isSearching) {
console.log('검색 중입니다. 잠시만 기다려주세요.');
return;
}
isSearching = true;
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/partMng/partMngTempGridList.do", columns, true, function() {
// 검색 완료 후 플래그 해제
isSearching = false;
});
// 타임아웃 방어 (10초 후 자동 해제)
setTimeout(function() {
if (isSearching) {
isSearching = false;
}
}, 10000);
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/partMng/partMngTempGridList.do", columns, true);
}
function openPartMngPopup(objId){

View File

@@ -234,31 +234,9 @@ var columns = [
{headerHozAlign : 'center', hozAlign : 'center', width : '110', title : '상태', field : 'STATUS' }
];
// 중복 요청 방지를 위한 로딩 플래그
var isSearching = false;
//var grid;
function fn_search(){
// 이미 검색 중이면 중복 요청 방지
if (isSearching) {
console.log('검색 중입니다. 잠시만 기다려주세요.');
return;
}
isSearching = true;
// 기존 그리드 검색 함수 실행
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/partMng/searchStructureGridList.do", columns, true, function() {
// 검색 완료 후 플래그 해제
isSearching = false;
});
// fnc_tabul_search가 콜백을 지원하지 않을 경우를 위한 타임아웃 처리
setTimeout(function() {
if (isSearching) {
isSearching = false;
}
}, 10000); // 10초 타임아웃
_tabulGrid = fnc_tabul_search(_tabul_layout_fitColumns, _tabulGrid, "/partMng/searchStructureGridList.do", columns, true);
}
//양산제품에 해당하는 SPEC 정보 목록을 가져온다.

View File

@@ -80,6 +80,7 @@ function fn_initGrid() {
width: 60,
title: '선택',
field: 'RADIO',
headerSort: false,
formatter: function(cell) {
var rowData = cell.getData();
return '<input type="radio" name="checkedPartNo" value="' + rowData.CHILD_OBJID + '" ' +
@@ -113,6 +114,7 @@ function fn_initGrid() {
width: 30,
title: i,
field: 'LEVEL_' + i,
headerSort: false,
formatter: function(cell) {
return cell.getValue() === '*' ? '*' : '';
}
@@ -122,6 +124,7 @@ function fn_initGrid() {
columns.push({
title: '수준',
headerHozAlign: 'center',
headerSort: false,
columns: levelColumns
});
@@ -133,6 +136,7 @@ function fn_initGrid() {
width: 150,
title: '품번',
field: 'PART_NO',
headerSort: false,
formatter: function(cell) {
var rowData = cell.getData();
return '<a href="#" onclick="openPartMngPopup(\'' +
@@ -148,7 +152,8 @@ function fn_initGrid() {
hozAlign: 'left',
width: 200,
title: '품명',
field: 'PART_NAME'
field: 'PART_NAME',
headerSort: false
},
{
headerHozAlign: 'center',
@@ -156,6 +161,7 @@ function fn_initGrid() {
width: 60,
title: '수량',
field: 'QTY_TEMP',
headerSort: false,
editor: function(cell, onRendered, success, cancel) {
var rowData = cell.getData();
if(rowData.STATUS === 'adding') {
@@ -185,7 +191,8 @@ function fn_initGrid() {
hozAlign: 'center',
width: 100,
title: '항목 수량',
field: 'ITEM_QTY'
field: 'ITEM_QTY',
headerSort: false
},
{
headerHozAlign: 'center',
@@ -193,6 +200,7 @@ function fn_initGrid() {
width: 60,
title: '3D',
field: 'CU01_CNT',
headerSort: false,
formatter: function(cell) {
var rowData = cell.getData();
var isEmpty = cell.getValue() == 0;
@@ -208,6 +216,7 @@ function fn_initGrid() {
width: 60,
title: '2D',
field: 'CU02_CNT',
headerSort: false,
formatter: function(cell) {
var rowData = cell.getData();
var isEmpty = cell.getValue() == 0;
@@ -223,6 +232,7 @@ function fn_initGrid() {
width: 60,
title: 'PDF',
field: 'CU03_CNT',
headerSort: false,
formatter: function(cell) {
var rowData = cell.getData();
var isEmpty = cell.getValue() == 0;
@@ -237,42 +247,48 @@ function fn_initGrid() {
hozAlign: 'left',
width: 100,
title: '재료',
field: 'MATERIAL'
field: 'MATERIAL',
headerSort: false
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 120,
title: '열처리경도',
field: 'HEAT_TREATMENT_HARDNESS'
field: 'HEAT_TREATMENT_HARDNESS',
headerSort: false
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 120,
title: '열처리방법',
field: 'HEAT_TREATMENT_METHOD'
field: 'HEAT_TREATMENT_METHOD',
headerSort: false
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 120,
title: '표면처리',
field: 'SURFACE_TREATMENT'
field: 'SURFACE_TREATMENT',
headerSort: false
},
{
headerHozAlign: 'center',
hozAlign: 'left',
width: 120,
title: '공급업체',
field: 'MAKER'
field: 'MAKER',
headerSort: false
},
{
headerHozAlign: 'center',
hozAlign: 'center',
width: 120,
title: '범주 이름',
field: 'PART_TYPE_TITLE'
field: 'PART_TYPE_TITLE',
headerSort: false
}
);
@@ -280,6 +296,7 @@ function fn_initGrid() {
layout: "fitColumns",
height: "650px",
pagination: false,
headerSort: false, // 정렬 비활성화
columns: columns,
rowFormatter: function(row) {
var data = row.getData();

View File

@@ -261,7 +261,7 @@ public class ContractMgmtController {
}
/**
* 계약관리 - 계약관리 목록 페이징
* 계약관리 - 계약관리 목록 페이징 (주문서관리)
* @param request
* @param paramMap
* @return
@@ -270,6 +270,8 @@ public class ContractMgmtController {
@RequestMapping("/contractMgmt/contractGridList.do")
public Map getProductKindSpecListPaging(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
try {
// 주문서관리에서는 모든 건 표시 (list_type을 전달하지 않음)
// 페이징 데이터 조회
commonService.selectListPagingNew("contractMgmt.contractGridList", request, paramMap);
@@ -1674,6 +1676,8 @@ public class ContractMgmtController {
@ResponseBody
@RequestMapping("/contractMgmt/estimateGridList.do")
public Map getestimateListPaging(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
// 견적관리에서는 통합 등록 건 제외
paramMap.put("list_type", "estimate");
commonService.selectListPagingNew("contractMgmt.estimateGridList", request, paramMap);
return paramMap;
}
@@ -2445,6 +2449,75 @@ public class ContractMgmtController {
return resultMap;
}
/**
* 견적요청 및 수주 통합 등록 팝업 페이지
* @param session
* @param request
* @param paramMap
* @return
*/
@RequestMapping("/contractMgmt/estimateAndOrderRegistFormPopup.do")
public String estimateAndOrderRegistFormPopup(HttpSession session, HttpServletRequest request, @RequestParam Map<String, Object> paramMap) {
String actionType = CommonUtils.checkNull(paramMap.get("actionType"));
String objId = CommonUtils.checkNull(paramMap.get("objId"));
Map code_map = new HashMap();
Map info = new HashMap();
List itemList = new ArrayList();
try {
// 수정 모드인 경우 기존 데이터 조회
if("update".equals(actionType) && StringUtils.isNotBlank(objId)) {
paramMap.put("objId", objId);
paramMap.put("contractObjId", objId); // getContractItems에서 사용
paramMap.put("actionType", actionType); // 통합 팝업 수정 모드 표시
info = CommonUtils.keyChangeUpperMap(contractMgmtService.getContractMgmtInfo(paramMap));
// 품목 정보 조회
itemList = contractMgmtService.getContractItems(paramMap);
}
// Machine 여부 및 프로젝트 존재 여부 확인
String productCd = CommonUtils.nullToEmpty((String)info.get("PRODUCT"));
boolean isMachine = "0000928".equals(productCd);
Map projectInfo = contractMgmtService.checkProjectExists(paramMap);
boolean hasProject = (projectInfo != null);
// 주문유형 코드
code_map.put("category_cd", commonService.bizMakeOptionList("0000167", CommonUtils.nullToEmpty((String)info.get("CATEGORY_CD")), "common.getCodeselect"));
// 제품구분 코드
code_map.put("product_cd", commonService.bizMakeOptionList("0000001", CommonUtils.nullToEmpty((String)info.get("PRODUCT")), "common.getCodeselect"));
// 국내/해외 코드
code_map.put("area_cd", commonService.bizMakeOptionList("0001219", CommonUtils.nullToEmpty((String)info.get("AREA_CD")), "common.getCodeselect"));
// 고객사 코드
code_map.put("customer_cd", commonService.bizMakeOptionList("", CommonUtils.nullToEmpty((String)info.get("CUSTOMER_OBJID")), "common.getsupplyselect"));
// 수주상태 코드
code_map.put("contract_result", commonService.bizMakeOptionList("0000963", CommonUtils.nullToEmpty((String)info.get("CONTRACT_RESULT")), "common.getCodeselect"));
// 환종 코드
code_map.put("contract_currency", commonService.bizMakeOptionList("0001533", CommonUtils.nullToEmpty((String)info.get("CONTRACT_CURRENCY")), "common.getCodeselect"));
// 반납사유 코드
code_map.put("return_reason_cd", commonService.bizMakeOptionList("0002268", "", "common.getCodeselect"));
request.setAttribute("code_map",code_map);
request.setAttribute("info", info);
request.setAttribute("itemList", itemList);
request.setAttribute("actionType", actionType);
request.setAttribute("objId", objId);
request.setAttribute("isMachine", isMachine ? "Y" : "N");
request.setAttribute("hasProject", hasProject ? "Y" : "N");
} catch(Exception e) {
e.printStackTrace();
}
return "/contractMgmt/estimateAndOrderRegistFormPopup";
}
/**
* 수주등록 팝업 페이지
* @param session
@@ -2504,6 +2577,23 @@ public class ContractMgmtController {
return "/contractMgmt/orderRegistFormPopup";
}
/**
* 견적요청 및 수주 통합 저장
* @param request
* @param paramMap
* @return
*/
@RequestMapping("/contractMgmt/saveEstimateAndOrderInfo.do")
public String saveEstimateAndOrderInfo(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
try {
request.setAttribute("RESULT", CommonUtils.getJsonMap(contractMgmtService.saveEstimateAndOrderInfo(request, paramMap)) );
} catch (Exception e) {
e.printStackTrace();
}
return "/ajax/ajaxResult";
}
/**
* 수주정보 저장
* @param session

View File

@@ -502,6 +502,7 @@
,EXCHANGE_RATE
,EST_PRICE
,EST_SUPPLY_PRICE
,IS_DIRECT_ORDER
,(SELECT COUNT(1) FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = T.OBJID) AS EST_STATUS
,(
SELECT IS_SEND
@@ -794,6 +795,12 @@
<if test="due_end_date != null and !''.equals(due_end_date)">
AND TO_DATE(DUE_DATE,'YYYY-MM-DD') <![CDATA[ <= ]]> TO_DATE(#{due_end_date}, 'YYYY-MM-DD')
</if>
<!-- 통합 등록 건 제외 (견적관리에서는 숨김) -->
<if test="list_type != null and 'estimate'.equals(list_type)">
AND COALESCE(IS_DIRECT_ORDER, 'N') != 'Y'
</if>
ORDER BY REGDATE DESC
</select>
@@ -4758,6 +4765,88 @@ WHERE
WHERE OBJID = #{objId}
</update>
<!-- 수주 합계만 업데이트 -->
<update id="updateOrderTotalAmounts" parameterType="map">
UPDATE CONTRACT_MGMT
SET
ORDER_SUPPLY_PRICE = #{order_supply_price},
ORDER_VAT = #{order_vat},
ORDER_TOTAL_AMOUNT = #{order_total_amount}
WHERE OBJID = #{objId}
</update>
<!-- 통합 등록 팝업용 견적요청 및 수주 정보 저장/수정 -->
<update id="saveEstimateAndOrderInfo" parameterType="map">
INSERT INTO CONTRACT_MGMT
(
OBJID,
CATEGORY_CD,
CUSTOMER_OBJID,
PRODUCT,
AREA_CD,
CUSTOMER_EQUIP_NAME,
CUSTOMER_PROJECT_NAME,
CUSTOMER_PRODUCTION_NO,
MECHANICAL_TYPE,
PAID_TYPE,
RECEIPT_DATE,
REQ_DEL_DATE,
CONTRACT_RESULT,
PO_NO,
ORDER_DATE,
CONTRACT_CURRENCY,
EXCHANGE_RATE,
REGDATE,
WRITER,
CONTRACT_NO,
IS_DIRECT_ORDER
)
VALUES
(
#{objId},
#{category_cd},
#{customer_objid},
#{product},
#{area_cd},
#{customer_equip_name},
#{customer_project_name},
#{customer_production_no},
#{mechanical_type},
#{paid_type},
#{receipt_date},
#{req_del_date},
#{contract_result},
#{po_no},
#{order_date},
#{contract_currency},
#{exchange_rate},
NOW(),
#{writer},
(SELECT TO_CHAR(NOW(),'yy')::VARCHAR ||'C-'||LPAD((SELECT NEXTVAL('contract_mgmt_seq'))::VARCHAR ,4,'0')),
#{is_direct_order}
)
ON CONFLICT (OBJID) DO
UPDATE
SET
CATEGORY_CD = #{category_cd},
CUSTOMER_OBJID = #{customer_objid},
PRODUCT = #{product},
AREA_CD = #{area_cd},
CUSTOMER_EQUIP_NAME = #{customer_equip_name},
CUSTOMER_PROJECT_NAME = #{customer_project_name},
CUSTOMER_PRODUCTION_NO = #{customer_production_no},
MECHANICAL_TYPE = #{mechanical_type},
PAID_TYPE = #{paid_type},
RECEIPT_DATE = #{receipt_date},
REQ_DEL_DATE = #{req_del_date},
CONTRACT_RESULT = #{contract_result},
PO_NO = #{po_no},
ORDER_DATE = #{order_date},
CONTRACT_CURRENCY = #{contract_currency},
EXCHANGE_RATE = #{exchange_rate},
IS_DIRECT_ORDER = #{is_direct_order}
</update>
<!-- 계약 기본 정보 조회 (제품구분 등) -->
<select id="getContractBasicInfo" parameterType="map" resultType="map">
SELECT
@@ -4915,6 +5004,49 @@ WHERE
)
</insert>
<!-- 통합 등록 팝업용 품목 저장 (수주 정보 포함) -->
<insert id="insertContractItemWithOrder" parameterType="map">
INSERT INTO CONTRACT_ITEM (
OBJID,
CONTRACT_OBJID,
SEQ,
PART_OBJID,
PART_NO,
PART_NAME,
QUANTITY,
DUE_DATE,
CUSTOMER_REQUEST,
RETURN_REASON,
REGDATE,
WRITER,
STATUS,
ORDER_QUANTITY,
ORDER_UNIT_PRICE,
ORDER_SUPPLY_PRICE,
ORDER_VAT,
ORDER_TOTAL_AMOUNT
) VALUES (
#{objId},
#{contractObjId},
#{seq},
#{partObjId},
#{partNo},
#{partName},
CASE WHEN #{quantity} = '' OR #{quantity} IS NULL THEN NULL ELSE #{quantity}::INTEGER END,
#{dueDate},
#{customerRequest},
#{returnReason},
NOW(),
#{writer},
'ACTIVE',
#{orderQuantity},
#{orderUnitPrice},
#{orderSupplyPrice},
#{orderVat},
#{orderTotalAmount}
)
</insert>
<!-- 품목별 S/N 저장 -->
<insert id="insertContractItemSerial" parameterType="map">
INSERT INTO CONTRACT_ITEM_SERIAL (
@@ -4951,6 +5083,11 @@ WHERE
CI.RETURN_REASON,
CI.REGDATE,
CI.WRITER,
CI.ORDER_QUANTITY,
CI.ORDER_UNIT_PRICE,
CI.ORDER_SUPPLY_PRICE,
CI.ORDER_VAT,
CI.ORDER_TOTAL_AMOUNT,
STRING_AGG(CIS.SERIAL_NO, ', ' ORDER BY CIS.SEQ) AS SERIAL_NOS,
COUNT(CIS.OBJID) AS SERIAL_COUNT
FROM
@@ -4975,8 +5112,14 @@ WHERE
CI.QUANTITY,
CI.DUE_DATE,
CI.CUSTOMER_REQUEST,
CI.RETURN_REASON,
CI.REGDATE,
CI.WRITER
CI.WRITER,
CI.ORDER_QUANTITY,
CI.ORDER_UNIT_PRICE,
CI.ORDER_SUPPLY_PRICE,
CI.ORDER_VAT,
CI.ORDER_TOTAL_AMOUNT
ORDER BY
CI.SEQ
</select>

View File

@@ -2574,6 +2574,279 @@ private String encodeImageToBase64(String imagePath) {
return items;
}
/**
* 견적요청 및 수주 통합 저장
* 견적요청 정보와 수주 정보를 한번에 저장
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public Map saveEstimateAndOrderInfo(HttpServletRequest request, Map paramMap){
Map resultMap = new HashMap();
SqlSession sqlSession = null;
try{
sqlSession = SqlMapConfig.getInstance().getSqlSession();
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
paramMap.put("writer", person.getUserId());
// 통합 등록 플래그 설정 (견적관리에서 숨김, 주문서관리에만 표시)
String objId = CommonUtils.checkNull(paramMap.get("objId"));
String actionType = CommonUtils.checkNull(paramMap.get("actionType"));
// 신규 등록인 경우 OBJID 생성 및 is_direct_order를 'Y'로 설정
if("regist".equals(actionType) || "".equals(objId)) {
objId = CommonUtils.createObjId();
paramMap.put("objId", objId);
paramMap.put("is_direct_order", "Y");
} else {
// 수정인 경우 기존 objId 유지 및 is_direct_order 값 유지
paramMap.put("objId", objId);
Map<String, Object> existingInfo = (Map<String, Object>) sqlSession.selectOne("contractMgmt.getContractMgmtInfo", paramMap);
if(existingInfo != null) {
paramMap.put("is_direct_order", CommonUtils.checkNull(existingInfo.get("IS_DIRECT_ORDER"), "Y"));
} else {
paramMap.put("is_direct_order", "Y");
}
}
// 1. 견적요청 및 수주 기본 정보 저장 (통합 팝업용 쿼리 사용)
int cnt = sqlSession.update("contractMgmt.saveEstimateAndOrderInfo", paramMap);
// 생성된 CONTRACT_OBJID 가져오기
String contract_objid = objId;
// 2. 품목 정보 저장 (CONTRACT_ITEM 테이블)
String itemsJson = CommonUtils.checkNull(paramMap.get("items_json"));
long totalSupplyPrice = 0;
long totalVat = 0;
long totalAmount = 0;
if(!"".equals(itemsJson)){
try {
JSONParser parser = new JSONParser();
JSONArray jsonArray = (JSONArray) parser.parse(itemsJson);
// 기존 품목 삭제 (전체 삭제 후 재등록 방식)
Map<String, Object> deleteParam = new HashMap<String, Object>();
deleteParam.put("contractObjId", contract_objid);
deleteParam.put("userId", person.getUserId());
sqlSession.update("contractMgmt.deleteContractItems", deleteParam);
for(int i = 0; i < jsonArray.size(); i++) {
JSONObject item = (JSONObject) jsonArray.get(i);
Map<String, Object> itemMap = new HashMap<String, Object>();
// 품목 기본 정보
String itemObjId = item.get("objId") != null ? item.get("objId").toString() : "";
if("".equals(itemObjId) || itemObjId.startsWith("temp_")) {
itemObjId = CommonUtils.createObjId();
}
itemMap.put("objId", itemObjId);
itemMap.put("contractObjId", contract_objid);
itemMap.put("seq", i + 1); // SEQ는 1부터 시작
itemMap.put("partObjId", item.get("partObjId") != null ? item.get("partObjId").toString() : "");
itemMap.put("partNo", item.get("partNo") != null ? item.get("partNo").toString() : "");
itemMap.put("partName", item.get("partName") != null ? item.get("partName").toString() : "");
itemMap.put("dueDate", item.get("dueDate") != null ? item.get("dueDate").toString() : "");
itemMap.put("customerRequest", item.get("customerRequest") != null ? item.get("customerRequest").toString() : "");
itemMap.put("returnReason", item.get("returnReason") != null ? item.get("returnReason").toString() : "");
// 수주 정보
String orderQuantity = item.get("orderQuantity") != null ? item.get("orderQuantity").toString().replace(",", "") : "0";
String orderUnitPrice = item.get("orderUnitPrice") != null ? item.get("orderUnitPrice").toString().replace(",", "") : "0";
String orderSupplyPrice = item.get("orderSupplyPrice") != null ? item.get("orderSupplyPrice").toString().replace(",", "") : "0";
String orderVat = item.get("orderVat") != null ? item.get("orderVat").toString().replace(",", "") : "0";
String orderTotalAmount = item.get("orderTotalAmount") != null ? item.get("orderTotalAmount").toString().replace(",", "") : "0";
// 통합 등록에서는 견적수량을 입력받지 않으므로 수주수량을 quantity에도 저장
itemMap.put("quantity", orderQuantity);
itemMap.put("orderQuantity", orderQuantity);
itemMap.put("orderUnitPrice", orderUnitPrice);
itemMap.put("orderSupplyPrice", orderSupplyPrice);
itemMap.put("orderVat", orderVat);
itemMap.put("orderTotalAmount", orderTotalAmount);
itemMap.put("writer", person.getUserId());
// 품목 저장 (통합 등록용 - 수주 정보 포함)
sqlSession.insert("contractMgmt.insertContractItemWithOrder", itemMap);
// S/N 저장 (별도 테이블)
JSONArray snArray = (JSONArray) item.get("snList");
if(snArray != null && snArray.size() > 0) {
for(int j = 0; j < snArray.size(); j++) {
JSONObject sn = (JSONObject) snArray.get(j);
Map<String, Object> snMap = new HashMap<String, Object>();
snMap.put("objId", CommonUtils.createObjId());
snMap.put("itemObjId", itemObjId);
snMap.put("seq", j + 1);
snMap.put("serialNo", sn.get("value").toString());
snMap.put("writer", person.getUserId());
sqlSession.insert("contractMgmt.insertContractItemSerial", snMap);
}
}
// 합계 계산
try {
totalSupplyPrice += Long.parseLong(orderSupplyPrice);
totalVat += Long.parseLong(orderVat);
totalAmount += Long.parseLong(orderTotalAmount);
} catch (NumberFormatException e) {
// 숫자 변환 실패 시 무시
}
}
} catch (Exception e) {
e.printStackTrace();
throw new Exception("품목 정보 저장 중 오류가 발생했습니다.");
}
}
// 3. 수주 합계 업데이트
paramMap.put("order_supply_price", String.valueOf(totalSupplyPrice));
paramMap.put("order_vat", String.valueOf(totalVat));
paramMap.put("order_total_amount", String.valueOf(totalAmount));
// 합계만 별도 업데이트 (기본 정보는 이미 saveEstimateAndOrderInfo에서 저장됨)
sqlSession.update("contractMgmt.updateOrderTotalAmounts", paramMap);
// 4. 프로젝트 생성 로직 (수주 또는 수주(FCST)인 경우)
String result_cd = CommonUtils.checkNull(paramMap.get("contract_result"));
String category_cd = CommonUtils.checkNull(paramMap.get("category_cd"));
String target_project_no = CommonUtils.checkNull(paramMap.get("target_project_no_direct"));
// 수주(0000964) 또는 수주(FCST)(0000968)인 경우 프로젝트 생성
if("0000964".equals(result_cd) || "0000968".equals(result_cd)){
// CONTRACT_OBJID로 프로젝트 존재 여부 확인
Map resultList = sqlSession.selectOne("contractMgmt.getProjectListBycontractObjid", paramMap);
boolean hasProject = (resultList != null);
// 제품구분 확인 (DB에서 조회)
Map<String, Object> contractInfo = (Map<String, Object>) sqlSession.selectOne("contractMgmt.getContractBasicInfo", paramMap);
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) - 품목별 수량만큼 프로젝트 생성");
}
// 품목별로 프로젝트 생성 또는 업데이트
paramMap.put("contractObjId", contract_objid);
List<Map> contractItems = getContractItems(paramMap);
if(contractItems != null && !contractItems.isEmpty()) {
System.out.println("품목 개수: " + contractItems.size() + "개 - 프로젝트 " + (hasProject ? "업데이트" : "생성") + " 시작" + (isMachine ? " (Machine - 수량별 생성)" : ""));
for(Map item : contractItems) {
// 수량 가져오기
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 {
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);
}
}
}
sqlSession.commit();
resultMap.put("result", "true");
resultMap.put("msg", "저장되었습니다.");
} catch (Exception e) {
e.printStackTrace();
resultMap.put("result", "false");
resultMap.put("msg", "저장 중 오류가 발생했습니다: " + e.getMessage());
if(sqlSession != null) {
sqlSession.rollback();
}
} finally {
if(sqlSession != null) sqlSession.close();
}
return resultMap;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public Map saveOrderInfo(HttpServletRequest request, Map paramMap){
Map resultMap = new HashMap();
@@ -2672,6 +2945,7 @@ private String encodeImageToBase64(String imagePath) {
}
// 품목별로 프로젝트 생성 또는 업데이트
paramMap.put("contractObjId", contract_objid);
List<Map> contractItems = getContractItems(paramMap);
if(contractItems != null && !contractItems.isEmpty()) {