fix: 견적서 저장/조회 버그 수정 및 여러 차수 작성 기능 추가

- 견적현황 undefined 문제 해결 (EST_STATUS 필드 추가)
- 견적서 목록 팝업 대문자 키 이름 강제
- 견적서 데이터 로드 엔드포인트 추가 (getEstimateTemplateDataByObjId)
- 한 영업번호에 여러 견적서 작성 가능하도록 수정
- PostgreSQL 타입 캐스팅 에러 수정
- 품목 저장 시 서브쿼리 다중 행 에러 수정
This commit is contained in:
2025-10-16 10:27:23 +09:00
parent b0383d3479
commit 81f071cc34
6 changed files with 794 additions and 164 deletions

View File

@@ -1774,13 +1774,20 @@ public class ContractMgmtController {
@RequestMapping("/contractMgmt/estimateTemplate1.do")
public String estimateTemplate1(HttpSession session, HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
String objId = CommonUtils.checkNull(paramMap.get("objId"));
String templateObjId = CommonUtils.checkNull(paramMap.get("templateObjId"));
try{
Map estimate = null;
List<Map> items = new ArrayList<Map>();
if(!"".equals(objId) && !"-1".equals(objId)){
// 기존 견적서 데이터 조회
// templateObjId가 있으면 기존 견적서 조회 (견적현황에서 클릭한 경우)
if(!"".equals(templateObjId) && !"-1".equals(templateObjId)){
paramMap.put("templateObjId", templateObjId);
estimate = contractMgmtService.getEstimateTemplateByObjId(paramMap);
items = contractMgmtService.getEstimateTemplateItemsByTemplateObjId(paramMap);
}
// objId만 있으면 CONTRACT 정보로 견적서 신규 작성
else if(!"".equals(objId) && !"-1".equals(objId)){
estimate = contractMgmtService.getEstimateTemplateInfo(paramMap);
items = contractMgmtService.getEstimateTemplateItems(paramMap);
}
@@ -1805,12 +1812,20 @@ public class ContractMgmtController {
@RequestMapping("/contractMgmt/estimateTemplate2.do")
public String estimateTemplate2(HttpSession session, HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
String objId = CommonUtils.checkNull(paramMap.get("objId"));
String templateObjId = CommonUtils.checkNull(paramMap.get("templateObjId"));
try{
Map estimate = null;
List<Map> items = new ArrayList<Map>();
if(!"".equals(objId) && !"-1".equals(objId)){
// templateObjId가 있으면 기존 견적서 조회 (견적현황에서 클릭한 경우)
if(!"".equals(templateObjId) && !"-1".equals(templateObjId)){
paramMap.put("templateObjId", templateObjId);
estimate = contractMgmtService.getEstimateTemplateByObjId(paramMap);
items = contractMgmtService.getEstimateTemplateItemsByTemplateObjId(paramMap);
}
// objId만 있으면 CONTRACT 정보로 견적서 신규 작성
else if(!"".equals(objId) && !"-1".equals(objId)){
// 기존 견적서 데이터 조회
estimate = contractMgmtService.getEstimateTemplateInfo(paramMap);
items = contractMgmtService.getEstimateTemplateItems(paramMap);
@@ -1854,6 +1869,43 @@ public class ContractMgmtController {
return resultMap;
}
/**
* 견적서 목록 조회 (AJAX)
*/
@ResponseBody
@RequestMapping(value="/contractMgmt/getEstimateTemplateList.do", method=RequestMethod.POST)
public Map getEstimateTemplateList(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
return contractMgmtService.getEstimateTemplateList(request, paramMap);
}
/**
* 견적서 템플릿 데이터 조회 (OBJID로) (AJAX)
*/
@ResponseBody
@RequestMapping(value="/contractMgmt/getEstimateTemplateDataByObjId.do", method=RequestMethod.POST)
public Map getEstimateTemplateDataByObjId(HttpServletRequest request, @RequestParam Map<String, Object> paramMap){
Map resultMap = new HashMap();
try {
// 템플릿 기본 정보 조회
Map template = contractMgmtService.getEstimateTemplateByObjId(paramMap);
// 템플릿 품목 조회
List<Map> items = contractMgmtService.getEstimateTemplateItemsByTemplateObjId(paramMap);
resultMap.put("result", "success");
resultMap.put("template", template);
resultMap.put("items", items);
} catch (Exception e) {
e.printStackTrace();
resultMap.put("result", "error");
resultMap.put("message", e.getMessage());
}
return resultMap;
}
/**
* 견적서 저장 (AJAX)
* @param request

View File

@@ -486,20 +486,27 @@
,CODE_NAME(AREA_CD) AS AREA_NAME
,MECHANICAL_TYPE
,OVERHAUL_ORDER
,PAID_TYPE
,CASE
WHEN PAID_TYPE = 'paid' THEN '유상'
WHEN PAID_TYPE = 'free' THEN '무상'
ELSE PAID_TYPE
END AS PAID_TYPE
,RECEIPT_DATE
,PART_NO
,PART_NAME
,SERIAL_NO
,QUANTITY
,CUSTOMER_REQUEST
,EXCHANGE_RATE
,A.APPR_STATUS
,A.APPROVAL_OBJID
,A.ROUTE_OBJID
FROM
CONTRACT_MGMT AS T
LEFT OUTER JOIN
,QUANTITY
,CUSTOMER_REQUEST
,EXCHANGE_RATE
,EST_PRICE
,EST_SUPPLY_PRICE
,(SELECT COUNT(1) FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = T.OBJID) AS EST_STATUS
,A.APPR_STATUS
,A.APPROVAL_OBJID
,A.ROUTE_OBJID
FROM
CONTRACT_MGMT AS T
LEFT OUTER JOIN
(
SELECT
B.OBJID AS ROUTE_OBJID,
@@ -3678,7 +3685,11 @@ ORDER BY ASM.SUPPLY_NAME
CUSTOMER_PROJECT_NAME,
AREA_CD,
CODE_NAME(AREA_CD) AS AREA_NAME,
PAID_TYPE,
CASE
WHEN PAID_TYPE = 'paid' THEN '유상'
WHEN PAID_TYPE = 'free' THEN '무상'
ELSE PAID_TYPE
END AS PAID_TYPE,
RECEIPT_DATE,
PART_NO,
PART_NAME,
@@ -3696,7 +3707,31 @@ ORDER BY ASM.SUPPLY_NAME
FROM
CONTRACT_MGMT AS T
WHERE
OBJID = #{objId}::NUMERIC
OBJID::VARCHAR = #{objId}
</select>
<!-- 견적서 템플릿 목록 조회 (CONTRACT_OBJID 기준) -->
<select id="getEstimateTemplateList" parameterType="map" resultType="map">
SELECT
OBJID AS "OBJID",
CONTRACT_OBJID AS "CONTRACT_OBJID",
TEMPLATE_TYPE AS "TEMPLATE_TYPE",
CASE
WHEN TEMPLATE_TYPE = '1' THEN '일반 견적서'
WHEN TEMPLATE_TYPE = '2' THEN '장비 견적서'
ELSE TEMPLATE_TYPE
END AS "TEMPLATE_TYPE_NAME",
ESTIMATE_NO AS "ESTIMATE_NO",
WRITER AS "WRITER",
TO_CHAR(REGDATE, 'YYYY-MM-DD HH24:MI') AS "REGDATE",
CHG_USER_ID AS "CHG_USER_ID",
TO_CHAR(CHGDATE, 'YYYY-MM-DD HH24:MI') AS "CHGDATE",
ROW_NUMBER() OVER (PARTITION BY TEMPLATE_TYPE ORDER BY REGDATE) AS "REVISION"
FROM
ESTIMATE_TEMPLATE
WHERE
CONTRACT_OBJID = #{objId}
ORDER BY TEMPLATE_TYPE, REGDATE DESC
</select>
<!-- 견적서 템플릿 데이터 조회 (ESTIMATE_TEMPLATE 테이블) -->
@@ -3724,7 +3759,7 @@ ORDER BY ASM.SUPPLY_NAME
FROM
ESTIMATE_TEMPLATE
WHERE
CONTRACT_OBJID = #{objId}::NUMERIC
CONTRACT_OBJID = #{objId}
<if test="template_type != null and template_type != ''">
AND TEMPLATE_TYPE = #{template_type}
</if>
@@ -3732,6 +3767,56 @@ ORDER BY ASM.SUPPLY_NAME
LIMIT 1
</select>
<!-- 견적서 템플릿 데이터 조회 (OBJID 기준) -->
<select id="getEstimateTemplateByObjId" parameterType="map" resultType="map">
SELECT
OBJID,
CONTRACT_OBJID,
TEMPLATE_TYPE,
EXECUTOR,
RECIPIENT,
ESTIMATE_NO,
CONTACT_PERSON,
GREETING_TEXT,
MODEL_NAME,
MODEL_CODE,
EXECUTOR_DATE,
NOTE1,
NOTE2,
NOTE3,
NOTE4,
WRITER,
TO_CHAR(REGDATE, 'YYYY-MM-DD HH24:MI') AS REGDATE,
CHG_USER_ID,
TO_CHAR(CHGDATE, 'YYYY-MM-DD HH24:MI') AS CHGDATE
FROM
ESTIMATE_TEMPLATE
WHERE
OBJID = #{templateObjId}
</select>
<!-- 견적서 템플릿 품목 조회 (TEMPLATE_OBJID 기준) -->
<select id="getEstimateTemplateItemsByTemplateObjId" parameterType="map" resultType="map">
SELECT
OBJID,
TEMPLATE_OBJID,
SEQ,
CATEGORY,
DESCRIPTION,
SPECIFICATION,
QUANTITY,
UNIT,
UNIT_PRICE,
AMOUNT,
NOTE,
REMARK
FROM
ESTIMATE_TEMPLATE_ITEM
WHERE
TEMPLATE_OBJID = #{templateObjId}
ORDER BY SEQ
</select>
<!-- 견적서 템플릿 품목 조회 -->
<select id="getEstimateTemplateItems" parameterType="map" resultType="map">
SELECT
@@ -3753,7 +3838,7 @@ ORDER BY ASM.SUPPLY_NAME
TEMPLATE_OBJID IN (
SELECT OBJID
FROM ESTIMATE_TEMPLATE
WHERE CONTRACT_OBJID = #{objId}::NUMERIC
WHERE CONTRACT_OBJID = #{objId}
)
ORDER BY SEQ
</select>
@@ -3761,6 +3846,7 @@ ORDER BY ASM.SUPPLY_NAME
<!-- 견적서 템플릿 저장 -->
<insert id="insertEstimateTemplate" parameterType="map">
INSERT INTO ESTIMATE_TEMPLATE (
OBJID,
CONTRACT_OBJID,
TEMPLATE_TYPE,
EXECUTOR,
@@ -3780,7 +3866,8 @@ ORDER BY ASM.SUPPLY_NAME
CHG_USER_ID,
CHGDATE
) VALUES (
#{objId}::NUMERIC,
#{template_objid},
#{objId},
#{template_type},
#{executor},
#{recipient},
@@ -3820,7 +3907,7 @@ ORDER BY ASM.SUPPLY_NAME
CHG_USER_ID = #{chg_user_id},
CHGDATE = NOW()
WHERE
CONTRACT_OBJID = #{objId}::NUMERIC
CONTRACT_OBJID = #{objId}
AND TEMPLATE_TYPE = #{template_type}
</update>
@@ -3828,11 +3915,7 @@ ORDER BY ASM.SUPPLY_NAME
<delete id="deleteEstimateTemplateItems" parameterType="map">
DELETE FROM ESTIMATE_TEMPLATE_ITEM
WHERE
TEMPLATE_OBJID IN (
SELECT OBJID
FROM ESTIMATE_TEMPLATE
WHERE CONTRACT_OBJID = #{objId}::NUMERIC
)
TEMPLATE_OBJID = #{template_objid}
</delete>
<!-- 견적서 템플릿 품목 저장 -->
@@ -3851,17 +3934,25 @@ ORDER BY ASM.SUPPLY_NAME
REMARK
)
SELECT
(SELECT OBJID FROM ESTIMATE_TEMPLATE WHERE CONTRACT_OBJID = #{objId}::NUMERIC AND TEMPLATE_TYPE = #{template_type}),
(json_array_elements(#{items_json}::json)->>'seq')::INTEGER,
json_array_elements(#{items_json}::json)->>'category',
json_array_elements(#{items_json}::json)->>'description',
json_array_elements(#{items_json}::json)->>'specification',
json_array_elements(#{items_json}::json)->>'quantity',
json_array_elements(#{items_json}::json)->>'unit',
(json_array_elements(#{items_json}::json)->>'unit_price')::NUMERIC,
(json_array_elements(#{items_json}::json)->>'amount')::NUMERIC,
json_array_elements(#{items_json}::json)->>'note',
json_array_elements(#{items_json}::json)->>'remark'
#{template_objid},
(item->>'seq')::INTEGER,
item->>'category',
item->>'description',
item->>'specification',
item->>'quantity',
item->>'unit',
CASE
WHEN item->>'unit_price' = '' THEN NULL
ELSE (item->>'unit_price')::NUMERIC
END,
CASE
WHEN item->>'amount' = '' THEN NULL
ELSE (item->>'amount')::NUMERIC
END,
item->>'note',
item->>'remark'
FROM json_array_elements(#{items_json}::json) AS item
WHERE COALESCE(item->>'description', '') != ''
</insert>
<!-- 견적서 템플릿 카테고리 업데이트 (장비 견적서용) -->
@@ -3872,7 +3963,7 @@ ORDER BY ASM.SUPPLY_NAME
CHG_USER_ID = #{chg_user_id},
CHGDATE = NOW()
WHERE
CONTRACT_OBJID = #{objId}::NUMERIC
CONTRACT_OBJID = #{objId}
AND TEMPLATE_TYPE = #{template_type}
</update>

View File

@@ -1271,6 +1271,105 @@ public class ContractMgmtService {
return resultList;
}
/**
* 견적서 템플릿 목록 조회
* @param request
* @param paramMap
* @return
*/
public Map getEstimateTemplateList(HttpServletRequest request, Map paramMap){
Map resultMap = new HashMap();
SqlSession sqlSession = null;
try{
sqlSession = SqlMapConfig.getInstance().getSqlSession();
List<Map> list = sqlSession.selectList("contractMgmt.getEstimateTemplateList", paramMap);
resultMap.put("result", "success");
resultMap.put("list", list);
}catch(Exception e){
resultMap.put("result", "error");
resultMap.put("msg", "목록 조회 중 오류가 발생했습니다.");
e.printStackTrace();
}finally{
if(sqlSession != null) sqlSession.close();
}
return resultMap;
}
/**
* 견적서 템플릿 OBJID로 조회 (견적현황 클릭 시)
* @param paramMap
* @return
*/
public Map getEstimateTemplateByObjId(Map paramMap){
Map resultMap = new HashMap();
SqlSession sqlSession = null;
try{
sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
String templateObjId = CommonUtils.checkNull(paramMap.get("templateObjId"));
if(!"".equals(templateObjId) && !"-1".equals(templateObjId)){
// ESTIMATE_TEMPLATE 테이블에서 직접 조회
resultMap = (Map) sqlSession.selectOne("contractMgmt.getEstimateTemplateByObjId", paramMap);
// CONTRACT_MGMT 정보도 함께 조회
if(resultMap != null && !resultMap.isEmpty()){
String contractObjId = CommonUtils.checkNull(resultMap.get("CONTRACT_OBJID"));
if(!"".equals(contractObjId)){
Map contractInfo = new HashMap();
contractInfo.put("objId", contractObjId);
Map contractData = (Map) sqlSession.selectOne("contractMgmt.getEstimateTemplateInfo", contractInfo);
if(contractData != null && !contractData.isEmpty()){
// CONTRACT 정보를 resultMap에 추가 (템플릿 정보가 우선)
contractData.putAll(resultMap);
resultMap = contractData;
}
}
}
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(sqlSession != null) sqlSession.close();
}
return resultMap;
}
/**
* 견적서 템플릿 품목 조회 (템플릿 OBJID로)
* @param paramMap
* @return
*/
public List<Map> getEstimateTemplateItemsByTemplateObjId(Map paramMap){
List<Map> resultList = new ArrayList<Map>();
SqlSession sqlSession = null;
try{
sqlSession = SqlMapConfig.getInstance().getSqlSession(false);
String templateObjId = CommonUtils.checkNull(paramMap.get("templateObjId"));
if(!"".equals(templateObjId) && !"-1".equals(templateObjId)){
resultList = sqlSession.selectList("contractMgmt.getEstimateTemplateItemsByTemplateObjId", paramMap);
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(sqlSession != null) sqlSession.close();
}
return resultList;
}
/**
* 견적서 템플릿 저장
* @param request
@@ -1287,7 +1386,8 @@ public class ContractMgmtService {
PersonBean person = (PersonBean)request.getSession().getAttribute(Constants.PERSON_BEAN);
String userId = person.getUserId();
String objId = CommonUtils.checkNull(paramMap.get("objId"));
String objId = CommonUtils.checkNull(paramMap.get("objId")); // CONTRACT_OBJID
String templateObjId = CommonUtils.checkNull(paramMap.get("templateObjId")); // 기존 템플릿 수정 시
String templateType = CommonUtils.checkNull(paramMap.get("template_type"));
String itemsJson = CommonUtils.checkNull(paramMap.get("items"));
String categoriesJson = CommonUtils.checkNull(paramMap.get("categories"));
@@ -1295,14 +1395,28 @@ public class ContractMgmtService {
paramMap.put("writer", userId);
paramMap.put("chg_user_id", userId);
// 견적서 템플릿 정보 저장/수정
Map existingTemplate = (Map) sqlSession.selectOne("contractMgmt.getEstimateTemplateData", paramMap);
// 기존 템플릿 수정인지 신규 작성인지 확인
// 중요: templateObjId가 명시적으로 있을 때만 수정, 없으면 항상 신규 작성
boolean isUpdate = false;
if(existingTemplate != null && !existingTemplate.isEmpty()){
// 기존 데이터 업데이트
if(!"".equals(templateObjId) && !"-1".equals(templateObjId)){
// templateObjId가 명시적으로 있으면 해당 템플릿 수정 (견적현황에서 기존 견적서 열어서 수정한 경우)
Map existingTemplate = (Map) sqlSession.selectOne("contractMgmt.getEstimateTemplateByObjId", paramMap);
if(existingTemplate != null && !existingTemplate.isEmpty()){
isUpdate = true;
paramMap.put("template_objid", templateObjId);
}
}
// else: templateObjId가 없으면 항상 신규 견적서 작성 (같은 영업번호에 여러 견적서 가능)
if(isUpdate){
// 기존 견적서 수정
sqlSession.update("contractMgmt.updateEstimateTemplate", paramMap);
} else {
// 신규 데이터 삽입
// 신규 견적서 작성 - 새로운 OBJID 생성
templateObjId = CommonUtils.createObjId();
paramMap.put("template_objid", templateObjId);
paramMap.put("contract_objid", objId);
sqlSession.insert("contractMgmt.insertEstimateTemplate", paramMap);
}