수주등록 견적서 내용 기본입력되도록 수정 #60

Merged
hjjeong merged 1 commits from feature/estimate-template-improvements into main 2025-11-11 07:51:47 +00:00
5 changed files with 222 additions and 17 deletions

View File

@@ -1263,7 +1263,7 @@ function fn_generateAndUploadPdf(callback) {
<td rowspan="2">1</td>
<td rowspan="2"><input type="text" class="item-desc" value="초음파 CNC Machine" style="width: 100%; border: none; background: transparent;"></td>
<td><input type="text" class="item-spec" value="Hole 가공" style="width: 100%; border: none; background: transparent; text-align: center;"></td>
<td><input type="text" class="item-qty" value="1" style="width: 100%; border: none; background: transparent; text-align: center;"></td>
<td><input type="text" class="item-qty" value="${not empty items[0] ? items[0].QUANTITY : '1'}" style="width: 100%; border: none; background: transparent; text-align: center;"></td>
<td><input type="text" class="item-price" value="" style="width: 100%; border: none; background: transparent; text-align: right;"></td>
<td><input type="text" class="item-amount" value="" readonly style="width: 100%; border: none; background: transparent; text-align: right;"></td>
<td><input type="text" class="item-remark" value="" style="width: 100%; border: none; background: transparent;"></td>

View File

@@ -219,10 +219,15 @@
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>';
// ORDER_UNIT_PRICE 수정 가능
html += '<td><input type="text" class="item-unit-price" value="' + (item.ORDER_UNIT_PRICE || '') + '" numberOnly required /></td>';
// ORDER_SUPPLY_PRICE 자동 계산
html += '<td><input type="text" class="item-supply-price" value="' + (item.ORDER_SUPPLY_PRICE || '') + '" numberOnly readonly style="background:#f5f5f5;" /></td>';
html += '<td><input type="text" class="item-vat" value="' + (item.ORDER_VAT || '') + '" numberOnly /></td>'; // readonly 제거
// ORDER_VAT 수정 가능
html += '<td><input type="text" class="item-vat" value="' + (item.ORDER_VAT || '') + '" numberOnly /></td>';
// ORDER_TOTAL_AMOUNT 자동 계산
html += '<td><input type="text" class="item-total-amount" value="' + (item.ORDER_TOTAL_AMOUNT || '') + '" numberOnly readonly style="background:#f5f5f5;" /></td>';
html += '<td style="text-align:center;">-</td>'; // 삭제 불가
html += '<input type="hidden" class="item-objid" value="' + (item.OBJID || '') + '" />';

View File

@@ -2440,17 +2440,9 @@ public class ContractMgmtController {
Map resultMap = new HashMap();
try {
// 결재완료 상태인 경우 최종 견적서 템플릿에서 품목 조회
String useEstimateTemplate = CommonUtils.checkNull(paramMap.get("useEstimateTemplate"));
List<Map> items = null;
if("Y".equals(useEstimateTemplate)) {
// 최종 견적서 템플릿의 품목 조회
items = contractMgmtService.getEstimateTemplateItemsForOrder(paramMap);
} else {
// 기존 방식: CONTRACT_ITEM에서 조회
items = contractMgmtService.getContractItems(paramMap);
}
// getContractItems()에서 ORDER_* 정보가 없으면 자동으로 견적서에서 가져옴
// 일반 견적서와 장비 견적서 모두 처리 가능
List<Map> items = contractMgmtService.getContractItems(paramMap);
resultMap.put("result", "success");
resultMap.put("items", items);

View File

@@ -4096,6 +4096,30 @@ ORDER BY ASM.SUPPLY_NAME
ORDER BY SEQ
</select>
<!-- 견적서 템플릿 품목 조회 (PART_OBJID로) -->
<select id="getEstimateTemplateItemByPartObjId" parameterType="map" resultType="map">
SELECT
OBJID,
TEMPLATE_OBJID,
SEQ,
CATEGORY,
PART_OBJID,
DESCRIPTION,
SPECIFICATION,
QUANTITY,
UNIT,
UNIT_PRICE,
AMOUNT,
NOTE,
REMARK
FROM
ESTIMATE_TEMPLATE_ITEM
WHERE
TEMPLATE_OBJID = #{templateObjId}
AND PART_OBJID = #{partObjId}
LIMIT 1
</select>
<!-- 견적서 템플릿 저장 -->
<insert id="insertEstimateTemplate" parameterType="map">
INSERT INTO ESTIMATE_TEMPLATE (

View File

@@ -1613,6 +1613,116 @@ public class ContractMgmtService {
sqlSession.update("contractMgmt.updateEstimateTemplateCategories", paramMap);
}
// 장비 견적서도 ESTIMATE_TEMPLATE_ITEM에 저장 (일관성을 위해)
// 기존 품목 삭제
sqlSession.delete("contractMgmt.deleteEstimateTemplateItems", paramMap);
// 품목 정보 생성 및 저장
String partName = CommonUtils.checkNull(paramMap.get("part_name"));
String partObjId = CommonUtils.checkNull(paramMap.get("part_objid"));
Object totalAmount = paramMap.get("total_amount");
if(!"".equals(partName) && totalAmount != null) {
try {
long amount = Long.parseLong(totalAmount.toString().replaceAll("[^0-9]", ""));
// 수량은 categories JSON에서 CNC Machine의 quantity를 가져옴
int quantity = 1; // 기본값
System.out.println("=== 장비 견적서 수량 추출 시작 ===");
System.out.println("categoriesJson: " + categoriesJson);
if(!"".equals(categoriesJson)) {
try {
// JSON 파싱
org.json.simple.parser.JSONParser parser = new org.json.simple.parser.JSONParser();
org.json.simple.JSONArray categoriesArray = (org.json.simple.JSONArray) parser.parse(categoriesJson);
System.out.println("categories 배열 크기: " + categoriesArray.size());
// CNC Machine 카테고리 찾기
for(int i = 0; i < categoriesArray.size(); i++) {
org.json.simple.JSONObject category = (org.json.simple.JSONObject) categoriesArray.get(i);
String categoryId = (String) category.get("category");
System.out.println("카테고리 " + i + ": " + categoryId);
if("cnc_machine".equals(categoryId)) {
System.out.println("CNC Machine 카테고리 찾음!");
// cnc_machine의 quantity는 items 배열 안에 있음
org.json.simple.JSONArray items = (org.json.simple.JSONArray) category.get("items");
System.out.println("items 배열: " + items);
if(items != null && items.size() > 0) {
org.json.simple.JSONObject firstItem = (org.json.simple.JSONObject) items.get(0);
Object qtyObj = firstItem.get("quantity");
System.out.println("quantity 객체: " + qtyObj);
System.out.println("quantity 타입: " + (qtyObj != null ? qtyObj.getClass().getName() : "null"));
if(qtyObj != null && !"".equals(qtyObj.toString().trim())) {
// quantity는 텍스트 형식일 수 있으므로 첫 줄만 추출
String qtyStr = qtyObj.toString().split("\n")[0].trim();
System.out.println("추출된 수량 문자열: '" + qtyStr + "'");
if(!qtyStr.isEmpty()) {
String numOnly = qtyStr.replaceAll("[^0-9]", "");
System.out.println("숫자만 추출: '" + numOnly + "'");
if(!numOnly.isEmpty()) {
quantity = Integer.parseInt(numOnly);
System.out.println("최종 수량: " + quantity);
}
}
} else {
System.out.println("quantity가 null이거나 비어있음");
}
} else {
System.out.println("items 배열이 비어있음");
}
break;
}
}
System.out.println("최종 결정된 수량: " + quantity);
} catch(Exception e) {
System.out.println("수량 추출 실패, 기본값 1 사용");
e.printStackTrace();
}
} else {
System.out.println("categoriesJson이 비어있음");
}
// 단가 계산 (총액 / 수량)
long unitPrice = quantity > 0 ? amount / quantity : amount;
// JSON 형식으로 품목 데이터 생성
String itemJson = "[{" +
"\"seq\": 1," +
"\"category\": \"\"," +
"\"part_objid\": \"" + partObjId + "\"," +
"\"description\": \"" + partName + "\"," +
"\"specification\": \"\"," +
"\"quantity\": " + quantity + "," +
"\"unit\": \"EA\"," +
"\"unit_price\": " + unitPrice + "," +
"\"amount\": " + amount + "," +
"\"note\": \"\"," +
"\"remark\": \"\"" +
"}]";
paramMap.put("items_json", itemJson);
sqlSession.insert("contractMgmt.insertEstimateTemplateItems", paramMap);
System.out.println("장비 견적서 품목 저장 완료 - 품명: " + partName + ", 수량: " + quantity + ", 단가: " + unitPrice);
} catch(Exception e) {
System.out.println("장비 견적서 품목 저장 실패: " + e.getMessage());
e.printStackTrace();
// 품목 저장 실패해도 견적서는 저장되도록 예외를 던지지 않음
}
}
sqlSession.commit();
resultMap.put("result", "success");
@@ -2234,9 +2344,76 @@ private String encodeImageToBase64(String imagePath) {
sqlSession = SqlMapConfig.getInstance().getSqlSession();
items = sqlSession.selectList("contractMgmt.getContractItems", paramMap);
System.out.println("=== getContractItems 디버깅 시작 ===");
System.out.println("조회된 품목 수: " + (items != null ? items.size() : 0));
// ORDER_* 정보가 없으면 견적서에서 가져오기
if(items != null && !items.isEmpty()) {
for(Map item : items) {
// ORDER_QUANTITY가 없거나 0이면 견적서에서 가져오기
Object orderQty = item.get("order_quantity");
System.out.println("품목 PART_NO: " + item.get("part_no") + ", ORDER_QUANTITY: " + orderQty);
if(orderQty == null || "".equals(orderQty.toString().trim()) || "0".equals(orderQty.toString().trim())) {
System.out.println("ORDER_QUANTITY가 비어있음 - 견적서에서 조회 시작");
// 최종 견적서 조회
Map estimateParam = new HashMap();
estimateParam.put("objId", paramMap.get("contractObjId"));
Map latestEstimate = sqlSession.selectOne("contractMgmt.getLatestEstimateTemplate", estimateParam);
System.out.println("최종 견적서 조회 결과: " + (latestEstimate != null ? "있음" : "없음"));
if(latestEstimate != null) {
// 일반 견적서와 장비 견적서 모두 ESTIMATE_TEMPLATE_ITEM에서 조회
Map itemParam = new HashMap();
itemParam.put("templateObjId", latestEstimate.get("OBJID"));
itemParam.put("partObjId", item.get("part_objid"));
System.out.println("견적서 품목 조회 - templateObjId: " + latestEstimate.get("OBJID") + ", partObjId: " + item.get("part_objid"));
Map estimateItem = sqlSession.selectOne("contractMgmt.getEstimateTemplateItemByPartObjId", itemParam);
System.out.println("견적서 품목 조회 결과: " + (estimateItem != null ? "있음" : "없음"));
if(estimateItem != null) {
System.out.println("견적서 품목 - quantity: " + estimateItem.get("quantity") + ", unit_price: " + estimateItem.get("unit_price"));
// 견적서의 수량, 단가 정보를 ORDER_* 필드에 매핑
item.put("order_quantity", estimateItem.get("quantity"));
item.put("order_unit_price", estimateItem.get("unit_price"));
// 공급가액 계산
Object quantity = estimateItem.get("quantity");
Object unitPrice = estimateItem.get("unit_price");
long supplyPrice = 0;
if(quantity != null && unitPrice != null) {
try {
long qty = Long.parseLong(quantity.toString().replaceAll("[^0-9]", ""));
long price = Long.parseLong(unitPrice.toString().replaceAll("[^0-9]", ""));
supplyPrice = qty * price;
} catch(Exception e) {
System.out.println("금액 계산 실패: " + e.getMessage());
}
}
item.put("order_supply_price", supplyPrice);
item.put("order_vat", Math.round(supplyPrice * 0.1));
item.put("order_total_amount", supplyPrice + Math.round(supplyPrice * 0.1));
System.out.println("계산 완료 - 공급가액: " + supplyPrice + ", 부가세: " + Math.round(supplyPrice * 0.1));
}
}
}
}
}
// 대문자 변환
items = CommonUtils.keyChangeUpperList(items);
System.out.println("=== getContractItems 디버깅 종료 ===");
}catch(Exception e){
e.printStackTrace();
}finally{
@@ -2249,6 +2426,11 @@ private String encodeImageToBase64(String imagePath) {
/**
* 견적서 템플릿 품목 조회 (수주등록용)
* 최종 견적서의 품목 정보를 수주등록 형식으로 변환하여 반환
*
* 참고: 이 메서드는 사용되지 않음. getContractItems()를 사용하세요.
* 일반 견적서와 장비 견적서 모두 ESTIMATE_TEMPLATE_ITEM에 저장되므로
* 동일한 방식으로 처리 가능
*
* @param paramMap - contractObjId
* @return
*/
@@ -2263,15 +2445,17 @@ private String encodeImageToBase64(String imagePath) {
// 1. 최종 견적서 템플릿 조회
Map templateParam = new HashMap();
templateParam.put("objId", paramMap.get("contractObjId"));
Map template = sqlSession.selectOne("contractMgmt.getEstimateTemplateData", templateParam);
Map template = sqlSession.selectOne("contractMgmt.getLatestEstimateTemplate", templateParam);
if(template != null) {
// 2. 견적서 템플릿의 품목 조회
template = CommonUtils.toUpperCaseMapKey(template);
// 일반 견적서와 장비 견적서 모두 ESTIMATE_TEMPLATE_ITEM에서 조회
Map itemParam = new HashMap();
itemParam.put("templateObjId", template.get("objid"));
itemParam.put("templateObjId", template.get("OBJID"));
List<Map> templateItems = sqlSession.selectList("contractMgmt.getEstimateTemplateItemsByTemplateObjId", itemParam);
// 3. 수주등록 형식으로 변환
// 수주등록 형식으로 변환
for(Map templateItem : templateItems) {
Map orderItem = new HashMap();