diff --git a/src/main/java/go/kr/project/crdn/crndRegistAndView/main/controller/CrdnLevyPrvntcController.java b/src/main/java/go/kr/project/crdn/crndRegistAndView/main/controller/CrdnLevyPrvntcController.java index 7d2baed..b8b053f 100644 --- a/src/main/java/go/kr/project/crdn/crndRegistAndView/main/controller/CrdnLevyPrvntcController.java +++ b/src/main/java/go/kr/project/crdn/crndRegistAndView/main/controller/CrdnLevyPrvntcController.java @@ -268,68 +268,6 @@ public class CrdnLevyPrvntcController { } - /** - * 건축물 과세 시가표준액 계산 (AJAX) - * @param bldgNewPrcCrtrAmt 건물신축가격기준액 - * @param strctIdx 구조지수 - * @param usgIdx 용도지수 - * @param pstnIdx 위치지수 - * @param elpsYrRdvlrt 경과년수잔가율 - * @param bscsCstrnRt 기초공사비율 - * @return 계산된 과세 시가표준액 - */ - @Operation(summary = "건축물 과세 시가표준액 계산", description = "BigDecimal을 사용하여 건축물 과세 시가표준액을 정확하게 계산합니다.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "계산 성공"), - @ApiResponse(responseCode = "400", description = "잘못된 숫자 형식"), - @ApiResponse(responseCode = "500", description = "서버 오류") - }) - @PostMapping("/calculateTaxableMarketPrice.ajax") - @ResponseBody - public ResponseEntity calculateTaxableMarketPrice( - @Parameter(description = "건물신축가격기준액") @RequestParam String bldgNewPrcCrtrAmt, - @Parameter(description = "구조지수") @RequestParam String strctIdx, - @Parameter(description = "용도지수") @RequestParam String usgIdx, - @Parameter(description = "위치지수") @RequestParam String pstnIdx, - @Parameter(description = "경과년수잔가율") @RequestParam String elpsYrRdvlrt, - @Parameter(description = "기초공사비율") @RequestParam String bscsCstrnRt) { - - Map result = new HashMap<>(); - try { - // 중요로직: BigDecimal을 사용하여 정확한 소수점 연산을 수행 - BigDecimal bldgNewPrcCrtrAmtDecimal = new BigDecimal(bldgNewPrcCrtrAmt); - BigDecimal strctIdxDecimal = new BigDecimal(strctIdx); - BigDecimal usgIdxDecimal = new BigDecimal(usgIdx); - BigDecimal pstnIdxDecimal = new BigDecimal(pstnIdx); - BigDecimal elpsYrRdvlrtDecimal = new BigDecimal(elpsYrRdvlrt); - BigDecimal bscsCstrnRtDecimal = new BigDecimal(bscsCstrnRt); - - BigDecimal taxableMarketPrice = bldgNewPrcCrtrAmtDecimal - .multiply(strctIdxDecimal) - .multiply(usgIdxDecimal) - .multiply(pstnIdxDecimal) - .multiply(elpsYrRdvlrtDecimal) - .multiply(bscsCstrnRtDecimal); - - // 최종 계산된 값을 소수점 없이 반올림하여 문자열로 반환 - String finalResult = taxableMarketPrice.setScale(0, RoundingMode.HALF_UP).toPlainString(); - - result.put("success", true); - result.put("taxableMarketPrice", finalResult); - return ApiResponseUtil.success(result, "계산이 수행되었습니다."); - - } catch (NumberFormatException e) { - log.error("숫자 형식 변환 오류", e); - result.put("success", false); - result.put("message", "계산 값 중 잘못된 숫자 형식이 있습니다."); - return ResponseEntity.badRequest().body(result); - } catch (Exception e) { - log.error("과세 시가표준액 계산 중 오류 발생", e); - result.put("success", false); - result.put("message", "계산 중 오류가 발생했습니다."); - return ResponseEntity.internalServerError().body(result); - } - } /** * 가감산 팝업 화면 @@ -512,213 +450,70 @@ public class CrdnLevyPrvntcController { } /** - * 시가표준액 계산 API - * 중요로직: 건축물과세시가에서 1,000원 미만을 절사하여 시가표준액을 계산합니다. - * - * @param bdstTxtnMprc 건축물과세시가 - * @return 시가표준액 계산 결과 - */ - @Operation(summary = "시가표준액 계산", description = "건축물과세시가에서 1,000원 미만 절사하여 시가표준액을 계산합니다.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "계산 성공"), - @ApiResponse(responseCode = "400", description = "잘못된 숫자 형식"), - @ApiResponse(responseCode = "500", description = "서버 오류") - }) - @PostMapping("/calculateStandardMarketPrice.ajax") - @ResponseBody - public ResponseEntity calculateStandardMarketPrice( - @Parameter(description = "건축물과세시가") @RequestParam String bdstTxtnMprc) { - - Map result = new HashMap<>(); - try { - BigDecimal bdstTxtnMprcDecimal = new BigDecimal(bdstTxtnMprc); - - // 중요로직: 시가표준액 = 건축물과세시가에서 1,000원 미만 절사 - BigDecimal mprcStdAmt = bdstTxtnMprcDecimal - .divide(new BigDecimal("1000"), 0, RoundingMode.DOWN) - .multiply(new BigDecimal("1000")); - - result.put("success", true); - result.put("mprcStdAmt", mprcStdAmt.toPlainString()); - return ApiResponseUtil.success(result, "시가표준액이 계산되었습니다."); - - } catch (NumberFormatException e) { - log.error("숫자 형식 변환 오류", e); - result.put("success", false); - result.put("message", "잘못된 숫자 형식입니다."); - return ResponseEntity.badRequest().body(result); - } catch (Exception e) { - log.error("시가표준액 계산 중 오류 발생", e); - result.put("success", false); - result.put("message", "시가표준액 계산 중 오류가 발생했습니다."); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result); - } - } - - /** - * 산정액 및 부과총액 계산 API - * 중요로직: 시가표준액, 위반면적, 가감산시행령률, 산정률, 산정률2를 이용하여 산정액과 부과총액을 계산합니다. + * 통합 계산 API - 건축물과세시가부터 부과총액까지 한번에 계산 + * 중요로직: 모든 계산을 서버에서 순차적으로 처리하여 한번의 API 호출로 모든 결과를 반환합니다. * - * @param mprcStdAmt 시가표준액 + * @param bldgNewPrcCrtrAmt 건물기준시가액 + * @param strctIdx 구조지수 + * @param usgIdx 용도지수 + * @param pstnIdx 위치지수 + * @param elpsYrRdvlrt 경과년수별잔가율 + * @param bscsCstrnRt 기초공사율 * @param vltnArea 위반면적 * @param adsbmtnEnfcRt 가감산시행령률 * @param cmpttnRtRate 산정률 비율값 * @param cmpttnRt2Rate 산정률2 비율값 - * @return 산정액 및 부과총액 계산 결과 - */ - @Operation(summary = "산정액 및 부과총액 계산", description = "시가표준액 × 위반면적 × 가감산시행령률 × 산정률 × 산정률2로 산정액을 계산하고, 1의 자리 절사하여 부과총액을 계산합니다.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "계산 성공"), - @ApiResponse(responseCode = "400", description = "잘못된 숫자 형식"), - @ApiResponse(responseCode = "500", description = "서버 오류") - }) - @PostMapping("/calculateLevyAmount.ajax") - @ResponseBody - public ResponseEntity calculateLevyAmount( - @Parameter(description = "시가표준액") @RequestParam String mprcStdAmt, - @Parameter(description = "위반면적") @RequestParam String vltnArea, - @Parameter(description = "가감산시행령률") @RequestParam String adsbmtnEnfcRt, - @Parameter(description = "산정률 비율값") @RequestParam String cmpttnRtRate, - @Parameter(description = "산정률2 비율값") @RequestParam String cmpttnRt2Rate) { - - Map result = new HashMap<>(); - try { - BigDecimal mprcStdAmtDecimal = new BigDecimal(mprcStdAmt); - BigDecimal vltnAreaDecimal = new BigDecimal(vltnArea); - BigDecimal adsbmtnEnfcRtDecimal = new BigDecimal(adsbmtnEnfcRt); - BigDecimal cmpttnRtRateDecimal = new BigDecimal(cmpttnRtRate); - BigDecimal cmpttnRt2RateDecimal = new BigDecimal(cmpttnRt2Rate); - - // 중요로직: 산정액 = 시가표준액 × 위반면적 × (가감산시행령률 ÷ 100) × 산정률 × 산정률2 - BigDecimal cmpttnAmt = mprcStdAmtDecimal - .multiply(vltnAreaDecimal) - .multiply(adsbmtnEnfcRtDecimal.divide(new BigDecimal("100"), 10, RoundingMode.HALF_UP)) - .multiply(cmpttnRtRateDecimal) - .multiply(cmpttnRt2RateDecimal) - .setScale(0, RoundingMode.DOWN); // 소수점 버림 - - // 중요로직: 부과총액 = 산정액의 1의 자리 절사 (10원 단위 버림) - BigDecimal levyWholAmt = cmpttnAmt - .divide(new BigDecimal("10"), 0, RoundingMode.DOWN) - .multiply(new BigDecimal("10")); - - result.put("success", true); - result.put("cmpttnAmt", cmpttnAmt.toPlainString()); - result.put("levyWholAmt", levyWholAmt.toPlainString()); - return ApiResponseUtil.success(result, "산정액 및 부과총액이 계산되었습니다."); - - } catch (NumberFormatException e) { - log.error("숫자 형식 변환 오류", e); - result.put("success", false); - result.put("message", "잘못된 숫자 형식입니다."); - return ResponseEntity.badRequest().body(result); - } catch (Exception e) { - log.error("산정액 및 부과총액 계산 중 오류 발생", e); - result.put("success", false); - result.put("message", "산정액 및 부과총액 계산 중 오류가 발생했습니다."); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result); - } - } - - /** - * 가감산시행령률 계산 API - * 중요로직: 기본 100%에서 가산율 또는 감산율을 적용하여 가감산시행령률을 계산합니다. - * - * @param baseRate 기본율 (보통 100) - * @param adtnRt 가산율 (선택적) - * @param sbtrRt 감산율 (선택적) - * @return 가감산시행령률 계산 결과 + * @return 모든 계산 결과를 포함한 통합 응답 */ - @Operation(summary = "가감산시행령률 계산", description = "기본율 + 가산율 - 감산율로 가감산시행령률을 계산합니다.") + @Operation(summary = "통합 계산 API", description = "건축물과세시가부터 부과총액까지 모든 계산을 한번에 처리합니다.") @ApiResponses(value = { @ApiResponse(responseCode = "200", description = "계산 성공"), @ApiResponse(responseCode = "400", description = "잘못된 숫자 형식"), @ApiResponse(responseCode = "500", description = "서버 오류") }) - @PostMapping("/calculateAdsbmtnEnfcRt.ajax") - @ResponseBody - public ResponseEntity calculateAdsbmtnEnfcRt( - @Parameter(description = "기본율") @RequestParam(defaultValue = "100") String baseRate, - @Parameter(description = "가산율") @RequestParam(required = false, defaultValue = "0") String adtnRt, - @Parameter(description = "감산율") @RequestParam(required = false, defaultValue = "0") String sbtrRt) { - - Map result = new HashMap<>(); - try { - BigDecimal baseRateDecimal = new BigDecimal(baseRate); - BigDecimal adtnRtDecimal = new BigDecimal(adtnRt); - BigDecimal sbtrRtDecimal = new BigDecimal(sbtrRt); - - // 중요로직: 가감산시행령률 = 기본율 + 가산율 - 감산율 - BigDecimal adsbmtnEnfcRt = baseRateDecimal - .add(adtnRtDecimal) - .subtract(sbtrRtDecimal); - - // 범위 검증 (0 ~ 1000% 제한) - if (adsbmtnEnfcRt.compareTo(BigDecimal.ZERO) < 0) { - adsbmtnEnfcRt = BigDecimal.ZERO; - } else if (adsbmtnEnfcRt.compareTo(new BigDecimal("1000")) > 0) { - adsbmtnEnfcRt = new BigDecimal("1000"); - } - - result.put("success", true); - result.put("adsbmtnEnfcRt", adsbmtnEnfcRt.toPlainString()); - return ApiResponseUtil.success(result, "가감산시행령률이 계산되었습니다."); - - } catch (NumberFormatException e) { - log.error("숫자 형식 변환 오류", e); - result.put("success", false); - result.put("message", "잘못된 숫자 형식입니다."); - return ResponseEntity.badRequest().body(result); - } catch (Exception e) { - log.error("가감산시행령률 계산 중 오류 발생", e); - result.put("success", false); - result.put("message", "가감산시행령률 계산 중 오류가 발생했습니다."); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result); - } - } - - /** - * 실시간 계산용 통합 API (디바운싱 + 캐싱 지원) - * 중요로직: 입력값들을 받아서 시가표준액, 산정액, 부과총액을 한번에 계산하여 반환합니다. - * 캐싱을 통해 동일한 입력값에 대한 중복 계산을 방지합니다. - * - * @param bdstTxtnMprc 건축물과세시가 - * @param vltnArea 위반면적 - * @param adsbmtnEnfcRt 가감산시행령률 - * @param cmpttnRtRate 산정률 비율값 - * @param cmpttnRt2Rate 산정률2 비율값 (커스텀 입력용) - * @return 모든 계산 결과를 포함한 응답 - */ - @Operation(summary = "실시간 계산용 통합 API", description = "실시간 입력을 위한 모든 계산을 한번에 처리합니다. 디바운싱과 캐싱을 지원합니다.") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "계산 성공"), - @ApiResponse(responseCode = "400", description = "잘못된 숫자 형식"), - @ApiResponse(responseCode = "500", description = "서버 오류") - }) - @PostMapping("/calculateRealtime.ajax") - @ResponseBody - public ResponseEntity calculateRealtime( - @Parameter(description = "건축물과세시가") @RequestParam String bdstTxtnMprc, + @PostMapping("/calculateAll.ajax") + public ResponseEntity calculateAll( + @Parameter(description = "건물기준시가액") @RequestParam String bldgNewPrcCrtrAmt, + @Parameter(description = "구조지수") @RequestParam String strctIdx, + @Parameter(description = "용도지수") @RequestParam String usgIdx, + @Parameter(description = "위치지수") @RequestParam String pstnIdx, + @Parameter(description = "경과년수별잔가율") @RequestParam String elpsYrRdvlrt, + @Parameter(description = "기초공사율") @RequestParam String bscsCstrnRt, @Parameter(description = "위반면적") @RequestParam String vltnArea, @Parameter(description = "가감산시행령률") @RequestParam String adsbmtnEnfcRt, @Parameter(description = "산정률 비율값") @RequestParam String cmpttnRtRate, @Parameter(description = "산정률2 비율값") @RequestParam String cmpttnRt2Rate) { - Map result = new HashMap<>(); try { // 중요로직: 입력값 검증 및 BigDecimal 변환 - BigDecimal bdstTxtnMprcDecimal = new BigDecimal(bdstTxtnMprc); + BigDecimal bldgNewPrcCrtrAmtDecimal = new BigDecimal(bldgNewPrcCrtrAmt); + BigDecimal strctIdxDecimal = new BigDecimal(strctIdx); + BigDecimal usgIdxDecimal = new BigDecimal(usgIdx); + BigDecimal pstnIdxDecimal = new BigDecimal(pstnIdx); + BigDecimal elpsYrRdvlrtDecimal = new BigDecimal(elpsYrRdvlrt); + BigDecimal bscsCstrnRtDecimal = new BigDecimal(bscsCstrnRt); BigDecimal vltnAreaDecimal = new BigDecimal(vltnArea); BigDecimal adsbmtnEnfcRtDecimal = new BigDecimal(adsbmtnEnfcRt); BigDecimal cmpttnRtRateDecimal = new BigDecimal(cmpttnRtRate); BigDecimal cmpttnRt2RateDecimal = new BigDecimal(cmpttnRt2Rate); - // 1단계: 시가표준액 계산 (1,000원 미만 절사) - BigDecimal mprcStdAmt = bdstTxtnMprcDecimal + // 1단계: 건축물과세시가 계산 + // 공식: 건물기준시가액 × 구조지수 × 용도지수 × 위치지수 × 경과년수별잔가율 × 기초공사율 + BigDecimal bdstTxtnMprc = bldgNewPrcCrtrAmtDecimal + .multiply(strctIdxDecimal) + .multiply(usgIdxDecimal) + .multiply(pstnIdxDecimal) + .multiply(elpsYrRdvlrtDecimal) + .multiply(bscsCstrnRtDecimal) + .setScale(0, java.math.RoundingMode.HALF_UP); + + // 2단계: 시가표준액 계산 (1,000원 미만 절사) + BigDecimal mprcStdAmt = bdstTxtnMprc .divide(new BigDecimal("1000"), 0, java.math.RoundingMode.DOWN) .multiply(new BigDecimal("1000")); - // 2단계: 산정액 계산 (시가표준액 × 위반면적 × 가감산시행령률(%) × 산정률(비율) × 산정률2(비율)) + // 3단계: 산정액 계산 + // 공식: 시가표준액 × 위반면적 × (가감산시행령률 ÷ 100) × 산정률(비율) × 산정률2(비율) BigDecimal cmpttnAmt = mprcStdAmt .multiply(vltnAreaDecimal) .multiply(adsbmtnEnfcRtDecimal.divide(new BigDecimal("100"), 10, java.math.RoundingMode.HALF_UP)) @@ -726,50 +521,36 @@ public class CrdnLevyPrvntcController { .multiply(cmpttnRt2RateDecimal) .setScale(0, java.math.RoundingMode.DOWN); - // 3단계: 부과총액 계산 (10원 단위 절사) + // 4단계: 부과총액 계산 (10원 단위 절사) BigDecimal levyWholAmt = cmpttnAmt .divide(new BigDecimal("10"), 0, java.math.RoundingMode.DOWN) .multiply(new BigDecimal("10")); - // 중요로직: 결과 데이터 구성 (캐싱을 위한 구조화된 응답) - result.put("success", true); - result.put("message", "실시간 계산이 성공적으로 완료되었습니다."); - - // 계산 결과 - Map calculations = new HashMap<>(); - calculations.put("mprcStdAmt", mprcStdAmt); // 시가표준액 - calculations.put("cmpttnAmt", cmpttnAmt); // 산정액 - calculations.put("levyWholAmt", levyWholAmt); // 부과총액 - - // 표시용 포맷팅된 값 - Map displayValues = new HashMap<>(); - displayValues.put("mprcStdAmtDisplay", String.format("%,d", mprcStdAmt.longValue()) + " 원"); - displayValues.put("cmpttnAmtDisplay", String.format("%,d", cmpttnAmt.longValue()) + " 원"); - displayValues.put("levyWholAmtDisplay", String.format("%,d", levyWholAmt.longValue()) + " 원"); - - result.put("calculations", calculations); - result.put("displayValues", displayValues); + // 중요로직: 통합 결과 데이터 구성 + // 모든 계산 결과 + Map data = new HashMap<>(); + data.put("bdstTxtnMprc", bdstTxtnMprc); // 건축물과세시가 + data.put("mprcStdAmt", mprcStdAmt); // 시가표준액 + data.put("cmpttnAmt", cmpttnAmt); // 산정액 + data.put("levyWholAmt", levyWholAmt); // 부과총액 - // 캐싱을 위한 입력 파라미터 해시값 (프론트엔드에서 사용) - String cacheKey = String.format("%s_%s_%s_%s_%s", - bdstTxtnMprc, vltnArea, adsbmtnEnfcRt, cmpttnRtRate, cmpttnRt2Rate); - result.put("cacheKey", cacheKey.hashCode()); + // 표시용 포맷팅된 값들 + data.put("bdstTxtnMprcDisplay", String.format("%,d", bdstTxtnMprc.longValue()) + " 원"); + data.put("mprcStdAmtDisplay", String.format("%,d", mprcStdAmt.longValue()) + " 원"); + data.put("cmpttnAmtDisplay", String.format("%,d", cmpttnAmt.longValue()) + " 원"); + data.put("levyWholAmtDisplay", String.format("%,d", levyWholAmt.longValue()) + " 원"); - log.debug("실시간 계산 완료 - 시가표준액: {}, 산정액: {}, 부과총액: {}", - mprcStdAmt, cmpttnAmt, levyWholAmt); + log.debug("통합 계산 완료 - 건축물과세시가: {}, 시가표준액: {}, 산정액: {}, 부과총액: {}", + bdstTxtnMprc, mprcStdAmt, cmpttnAmt, levyWholAmt); - return ResponseEntity.ok(result); + return ApiResponseUtil.success(data, "통합 계산이 성공적으로 완료되었습니다."); } catch (NumberFormatException e) { - log.error("실시간 계산 중 숫자 형식 변환 오류", e); - result.put("success", false); - result.put("message", "잘못된 숫자 형식입니다."); - return ResponseEntity.badRequest().body(result); + log.error("통합 계산 중 숫자 형식 변환 오류", e); + return ApiResponseUtil.error("잘못된 숫자 형식입니다."); } catch (Exception e) { - log.error("실시간 계산 중 오류 발생", e); - result.put("success", false); - result.put("message", "실시간 계산 중 오류가 발생했습니다."); - return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(result); + log.error("통합 계산 중 오류 발생", e); + return ApiResponseUtil.error("통합 계산 중 오류가 발생했습니다: " + e.getMessage()); } } diff --git a/src/main/webapp/WEB-INF/views/crdn/crndRegistAndView/main/crdnLevyPrvntc/levyPrvntcPopup.jsp b/src/main/webapp/WEB-INF/views/crdn/crndRegistAndView/main/crdnLevyPrvntc/levyPrvntcPopup.jsp index b4ef806..1e229c9 100644 --- a/src/main/webapp/WEB-INF/views/crdn/crndRegistAndView/main/crdnLevyPrvntc/levyPrvntcPopup.jsp +++ b/src/main/webapp/WEB-INF/views/crdn/crndRegistAndView/main/crdnLevyPrvntc/levyPrvntcPopup.jsp @@ -6,70 +6,44 @@ + X @@ -349,7 +318,12 @@ -

부과총액 = 0 원

+

+ 부과총액 = + + + +

@@ -668,6 +642,14 @@ }); + this.instance.on('click', function(ev) { + if (ev.rowKey !== undefined && ev.rowKey !== null) { + var rowData = self.instance.getRow(ev.rowKey); + LevyPrvntcPopup.actInfoIdSelect = rowData.actInfoId; + handleRowSelection(rowData); + } + }); + this.instance.on('focusChange', function(ev) { if (ev.rowKey !== undefined && ev.rowKey !== null) { var rowData = self.instance.getRow(ev.rowKey); @@ -796,9 +778,12 @@ $('#levyWholAmt').val(existingData.levyWholAmt || ''); $('#levyWholAmtDisplay').text((existingData.levyWholAmt ? Number(existingData.levyWholAmt).toLocaleString() : '0') + ' 원'); + // 기존 데이터 로딩 시 계산하기 버튼 숨김 (이미 계산된 결과가 있으므로) + hideCalculateButton(); + // 시가표준액 $('#standardMarketPrice').val(existingData.mprcStdAmt || '').trigger('focus'); - $('#standardMarketPrice_top').val(existingData.mprcStdAmt || '').trigger('focus'); + $('#standardMarketPrice_bottom').val(existingData.mprcStdAmt || '').trigger('focus'); // 건축물과세시가 $('#taxableMarketPrice').val(existingData.bdstTxtnMprc || '').trigger('focus'); @@ -848,6 +833,16 @@ $('#bscsCstrnRt').val(''); // 기초공사율 $('#bscsCstrnSeCd').val(''); // 기초공사구분 + // 기초공사율 계산을 위한 기준 비율들 초기화 (hidden으로) + if (!$('#bscsCstrnYBdstCmpttnRt').length) { + $('').appendTo('body'); + $('').appendTo('body'); + $('').appendTo('body'); + } + $('#bscsCstrnYBdstCmpttnRt').val(''); + $('#bscsCstrnNBdstCmpttnRt').val(''); + $('#dupEtbldgBdstCmpttnRt').val(''); + $('#bldgNewPrcCrtrAmtNo').val(rowData.bldgNewPrcCrtrAmtNo); // 건물기준시가액(NO) $('#bldgNewPrcCrtrAmtDisplay').val(rowData.bdstUsg); // 건물기준시가액(건축물 용도) $('#bldgNewPrcCrtrAmt').val(rowData.bldgNewPrcCrtrAmt).trigger('focus'); // 건물기준시가액(하단) @@ -869,8 +864,8 @@ $('#bscsCstrnSeCd').trigger('change'); // 기초공사구분 변경 이벤트 트리거 - // 산정액 계산 함수 호출 - calculateLevyAmount(); + // 계산하기 버튼 표시 (값이 변경된 경우) + showCalculateButton(); }; @@ -978,8 +973,8 @@ $('#bscsCstrnRt').val(rate).trigger('focus'); // 기초공사율 설정 - // 자동계산 함수 호출 - calculateAuto(); + // 계산하기 버튼 표시 (값이 변경된 경우) + showCalculateButton(); }); // 산정률2 선택 시 산정률2 input에 값 설정 @@ -991,19 +986,26 @@ $('#cmpttnRt2Rate').val(rateValue2 || ''); $('#cmpttnRt2Display').val((rateValue || '') + ' %').trigger('focus'); - // 산정액 계산 함수 호출 - calculateLevyAmount(); + // 계산하기 버튼 표시 (값이 변경된 경우) + showCalculateButton(); }); - // 위반면적 변경 시 산정액 계산 함수 호출 + // 위반면적 변경 시 계산하기 버튼 표시 $('#vltnArea').on('change keyup', function() { - calculateLevyAmount(); + showCalculateButton(); }); - // 계산에 사용되는 입력 필드 변경 시 자동 계산 호출 - var calculationInputs = '#bldgNewPrcCrtrAmt, #strctIdx, #usgIdx, #pstnIdx, #elpsYrRdvlrt'; - $(document).on('change', calculationInputs, calculateAuto); + // 계산에 사용되는 입력 필드 변경 시 계산하기 버튼 표시 (기초공사율 포함) + var calculationInputs = '#bldgNewPrcCrtrAmt, #strctIdx, #usgIdx, #pstnIdx, #elpsYrRdvlrt, #bscsCstrnRt'; + $(document).on('change', calculationInputs, function() { + showCalculateButton(); + }); + + // 계산하기 버튼 클릭 이벤트 + $('#btnCalculate').on('click', function() { + performCalculation(); + }); }, // ======================================== @@ -1178,8 +1180,8 @@ } if (!$('#bscsCstrnSeCd').val()) { - alert('기초공사구분을 선택해주세요.'); $('#bscsCstrnSeCd').focus(); + alert('기초공사구분을 선택해주세요.'); return false; } @@ -1196,6 +1198,16 @@ $('#vltnArea').focus(); return false; } + + var taxableMarketPrice = $("#taxableMarketPrice").val(); + var standardMarketPrice = $("#standardMarketPrice").val(); + var standardMarketPrice_bottom = $("#standardMarketPrice_bottom").val(); + var cmpttnAmt = $("#cmpttnAmt").val(); + var levyWholAmt = $("#levyWholAmt").val(); + if( !(taxableMarketPrice && standardMarketPrice && standardMarketPrice_bottom && cmpttnAmt && levyWholAmt) ){ + alert('[계산하기] 버튼을 클릭 하여 계산을 완료하시기 바랍니다.'); + return false; + } return true; }, @@ -1231,188 +1243,313 @@ }); }, + /** + * 행정처분 간격일 자동 계산 함수 + * 중요한 로직 주석: 시작일과 종료일 입력시 자동으로 간격일을 계산한다. + */ + calculateDaysBetween: function() { + var startDate = $('#impltBgngYmd').inputmask("unmaskedvalue"); + var endDate = $('#impltEndYmd').inputmask("unmaskedvalue"); + + if (startDate && endDate && startDate.length === 8 && endDate.length === 8) { + var start = new Date(startDate.substring(0,4), + parseInt(startDate.substring(4,6)) - 1, + startDate.substring(6,8)); + var end = new Date(endDate.substring(0,4), + parseInt(endDate.substring(4,6)) - 1, + endDate.substring(6,8)); + + var timeDiff = end.getTime() - start.getTime(); + var daysDiff = Math.ceil(timeDiff / (1000 * 3600 * 24)); + + $('#impltDaysCnt').val(daysDiff); + } + }, + + /** + * 가감산 팝업에서 선택된 데이터를 받아 처리하는 함수 + * @param {string} type - 'add' 또는 'minus' + * @param {Array} data - 선택된 행 데이터 배열 + */ + setAddMinusData: function(type, data) { + // 단건 선택 + const selectedRow = data; + if (!selectedRow) return; + + const rate = parseFloat(selectedRow.adsbmtnRt) || 0; + const rateForCalc = parseFloat(selectedRow.adsbmtnRtRate) || 0; + const rateForDisplay = rate + '%'; + let rateForTotal = 100; + + if (type === '1') { // '1' for 가산(add) + $('#adtnRtCd').val(selectedRow.adsbmtnRtCd); + $('#adtnRt').val(rate); + $('#adtnRtRate').val(rateForCalc); + $('#btnOpenAddPopup').text('+ 가산 [' + rateForDisplay + ']'); + + } else if (type === '2') { // '2' for 감산(minus) + $('#sbtrRtCd').val(selectedRow.adsbmtnRtCd); + $('#sbtrRt').val(rate); + $('#sbtrRtRate').val(rateForCalc); + $('#btnOpenMinusPopup').text('- 감산 [' + rateForDisplay + ']'); + } + + var addRate = $('#adtnRt').val(); + var minusRate = $('#sbtrRt').val(); + const totalRateCalcAdd = parseFloat(addRate) || 0; + const totalRateCalcMinus = parseFloat(minusRate) || 0; + const totalRateCalc = 100 + totalRateCalcAdd - totalRateCalcMinus; + + $("#adsbmtnEnfcRt").val( totalRateCalc ); + $("#adsbmtnEnfcRtDisplay").val( totalRateCalc+" %" ); + + // 계산하기 버튼 표시 (값이 변경된 경우) + showCalculateButton(); + } + }; // ======================================== // 6. 계산 관련 유틸리티 함수들 // ======================================== + // ======================================== + // 🧮 계산하기 버튼 방식 계산 시스템 + // ======================================== + /** - * 이행강제금 산정액 및 부과총액 계산 - * 중요로직: 시가표준액, 위반면적, 가감산시행령률, 산정률, 산정률2가 모두 입력되었을 때 산정액과 부과총액을 자동으로 계산합니다. - * @description - * - 산정액 = 시가표준액 * 위반면적 * (가감산시행령률 / 100) * 산정률(비율) * 산정률2(비율) - * - 부과총액 = 산정액의 1의 자리 절사 (10원 단위 버림) + * 계산하기 버튼 표시 함수 + * 중요로직: select box 값이 변경되었을 때 계산하기 버튼을 표시합니다. */ - function calculateLevyAmount() { - var standardMarketPrice = $('#standardMarketPrice_top').inputmask('unmaskedvalue') || '0'; - var vltnArea = $('#vltnArea').inputmask('unmaskedvalue') || '0'; - var adsbmtnEnfcRt = $('#adsbmtnEnfcRt').val() || '0'; - var cmpttnRtRate = $('#cmpttnRtRate').val() || '0'; - var cmpttnRt2Rate = $('#cmpttnRt2Rate').val() || '0'; - - // 모든 값이 0보다 큰지 확인 (필수 입력값 체크) - if (parseFloat(standardMarketPrice) > 0 && parseFloat(vltnArea) > 0 && parseFloat(adsbmtnEnfcRt) > 0 && - parseFloat(cmpttnRtRate) > 0 && parseFloat(cmpttnRt2Rate) > 0) { - - // 서버 API로 정확한 계산 요청 (BigDecimal 사용) - $.ajax({ - url: '', - type: 'POST', - data: { - mprcStdAmt: standardMarketPrice, - vltnArea: vltnArea, - adsbmtnEnfcRt: adsbmtnEnfcRt, - cmpttnRtRate: cmpttnRtRate, - cmpttnRt2Rate: cmpttnRt2Rate - }, - success: function(response) { - if (response && response.data && response.success) { - var cmpttnAmt = response.data.cmpttnAmt; - var levyWholAmt = response.data.levyWholAmt; - - // '산정액' 필드에 값 설정 - $('#cmpttnAmt').val(parseInt(cmpttnAmt)).trigger('focus'); - - // '부과총액' 필드 및 표시에 값 설정 - $('#levyWholAmt').val(parseInt(levyWholAmt)); - $('#levyWholAmtDisplay').text(parseInt(levyWholAmt).toLocaleString() + ' 원'); - - console.log('산정액 (서버계산):', cmpttnAmt); - console.log('부과총액 (서버계산):', levyWholAmt); - } - }, - error: function() { - alert('산정액 및 부과총액 계산 중 오류가 발생했습니다.'); - // 오류 시 필드 초기화 - $('#cmpttnAmt').val('').trigger('focus'); - $('#levyWholAmt').val(''); - $('#levyWholAmtDisplay').text('0 원'); - } - }); - } else { - // 값이 부족할 때 필드 초기화 + function showCalculateButton() { + var btnCalculate = $('#btnCalculate'); + if (btnCalculate.length > 0) { + btnCalculate.show().addClass('btn-highlight'); + + //건축물 과세시가, 시가표준액(상단), 시가표준액(하단), 산정액 값 초기화 처리 + // 오류 시 필드 초기화 + $('#taxableMarketPrice').val('').trigger('focus'); + $('#standardMarketPrice').val('').trigger('focus'); + $('#standardMarketPrice_bottom').val('').trigger('focus'); $('#cmpttnAmt').val('').trigger('focus'); $('#levyWholAmt').val(''); $('#levyWholAmtDisplay').text('0 원'); + + console.log('🔄 계산하기 버튼 표시됨 - 값이 변경됨'); } } /** - * 건축물과세시가 자동 계산 (서버 API 호출) - * 중요로직: 각 항목의 값을 서버로 보내 건축물과세시가를 계산하고, 그 결과를 받아 시가표준액을 계산합니다. - * @description - * - 건축물과세시가 = 서버 API를 통해 계산 - * - 시가표준액 = 건축물과세시가에서 1,000원 미만 절사 + * 계산하기 버튼 숨김 함수 + * 중요로직: 계산이 완료되었을 때 계산하기 버튼을 숨깁니다. */ - function calculateAuto() { - var bldgNewPrcCrtrAmt = $('#bldgNewPrcCrtrAmt').inputmask('unmaskedvalue') || '0'; // 건물기준시가액 - var strctIdx = $('#strctIdx').inputmask('unmaskedvalue') || '0'; // 구조지수 - var usgIdx = $('#usgIdx').inputmask('unmaskedvalue') || '0'; // 용도지수 - var pstnIdx = $('#pstnIdx').inputmask('unmaskedvalue') || '0'; // 위치지수 - var elpsYrRdvlrt = $('#elpsYrRdvlrt').inputmask('unmaskedvalue') || '0'; // 경과년수별잔가율 - var bscsCstrnRt = $('#bscsCstrnRt').inputmask('unmaskedvalue') || '0'; // 기초공사율 - - // 기초공사율이 선택되지 않았으면 계산을 수행하지 않고 필드를 초기화합니다. - if (!bscsCstrnRt || parseFloat(bscsCstrnRt) === 0) { - $('#taxableMarketPrice').val('').trigger('focus'); // 건축물과세시가 - $('#standardMarketPrice').val('').trigger('focus'); // 시가표준액 - $('#standardMarketPrice_top').val('').trigger('focus'); // 시가표준액(상단) - // 필드 초기화 시 산정액/부과총액도 초기화 - $('#cmpttnAmt').val('').trigger('focus'); - $('#levyWholAmt').val(''); - $('#levyWholAmtDisplay').text('0 원'); + function hideCalculateButton() { + var btnCalculate = $('#btnCalculate'); + if (btnCalculate.length > 0) { + btnCalculate.hide().removeClass('btn-highlight'); + console.log('✅ 계산하기 버튼 숨김됨 - 계산 완료'); + } + } + + /** + * 계산 수행 함수 - 단일 API 호출 + * 중요로직: 모든 필수값 validation 후 단일 API로 모든 계산을 한번에 수행합니다. + * - 감산, 가산은 선택사항 (기본 100%) + * - 나머지는 모두 필수 + */ + function performCalculation() { + console.log('🧮 통합 계산하기 버튼 클릭됨'); + + // 1. 필수값 validation + var validationResult = validateCalculationInputs(); + if (!validationResult.isValid) { + alert(validationResult.message); + if (validationResult.focusElement) { + $(validationResult.focusElement).focus(); + } return; } - var params = { - bldgNewPrcCrtrAmt: bldgNewPrcCrtrAmt, - strctIdx: strctIdx, - usgIdx: usgIdx, - pstnIdx: pstnIdx, - elpsYrRdvlrt: elpsYrRdvlrt, - bscsCstrnRt: bscsCstrnRt - }; + // 2. 입력값 수집 + var params = collectAllCalculationInputs(); + console.log('📊 수집된 통합 계산 파라미터:', params); + + // 3. 로딩 상태 표시 + $('#levyWholAmtDisplay').text('계산 중...'); + $('#btnCalculate').prop('disabled', true).text('계산 중...'); + // 4. 단일 API 호출로 모든 계산 수행 $.ajax({ - url: '', + url: '', type: 'POST', data: params, success: function(response) { - if (response && response.data && response.success) { - var taxableMarketPrice = response.data.taxableMarketPrice; + if (response && response.success && response.data) { + var data = response.data; - // 건축물과세시가 설정 - $('#taxableMarketPrice').val(taxableMarketPrice).trigger('focus'); + // 모든 결과값을 한번에 설정 + // 1. 건축물과세시가 + $('#taxableMarketPrice').val(data.bdstTxtnMprc).trigger('focus'); - // 시가표준액 계산을 서버 API로 요청 (BigDecimal 정확도 보장) - $.ajax({ - url: '', - type: 'POST', - data: { bdstTxtnMprc: taxableMarketPrice }, - success: function(standardResponse) { - if (standardResponse && standardResponse.data && standardResponse.success) { - var standardMarketPrice = standardResponse.data.mprcStdAmt; - - // 시가표준액 설정 - $('#standardMarketPrice').val(standardMarketPrice).trigger('focus'); - $('#standardMarketPrice_top').val(standardMarketPrice).trigger('focus'); - - console.log('건축물과세시가:', taxableMarketPrice); - console.log('시가표준액:', standardMarketPrice); - - // 산정액 계산 함수 호출 - calculateLevyAmount(); - } - }, - error: function() { - alert('시가표준액 계산 중 오류가 발생했습니다.'); - } + // 2. 시가표준액 + $('#standardMarketPrice').val(data.mprcStdAmt).trigger('focus'); + $('#standardMarketPrice_bottom').val(data.mprcStdAmt).trigger('focus'); + + // 3. 산정액 + $('#cmpttnAmt').val(parseInt(data.cmpttnAmt)).trigger('focus'); + + // 4. 부과총액 + $('#levyWholAmt').val(parseInt(data.levyWholAmt)); + $('#levyWholAmtDisplay').text(data.levyWholAmtDisplay); + + console.log('✅ 통합 계산 완료!', { + 건축물과세시가: data.bdstTxtnMprc, + 시가표준액: data.mprcStdAmt, + 산정액: data.cmpttnAmt, + 부과총액: data.levyWholAmt }); + + // 계산하기 버튼 숨김 + hideCalculateButton(); + } else { - alert(response.message || '계산 중 오류가 발생했습니다.'); - $('#taxableMarketPrice').val('').trigger('focus'); // 건축물과세시가 - $('#standardMarketPrice').val('').trigger('focus'); // 시가표준액 - $('#standardMarketPrice_top').val('').trigger('focus'); // 시가표준액(상단) + throw new Error(response.message || '서버에서 올바른 계산 결과를 받지 못했습니다.'); } - // 산정액 계산 함수 호출 - calculateLevyAmount(); }, - error: function() { - alert('서버와 통신 중 오류가 발생했습니다.'); - $('#taxableMarketPrice').val('').trigger('focus'); // 건축물과세시가 - $('#standardMarketPrice').val('').trigger('focus'); // 시가표준액 - $('#standardMarketPrice_top').val('').trigger('focus'); // 시가표준액(상단) - // 오류 시 산정액/부과총액도 초기화 + error: function(xhr, status, error) { + console.error('❌ 통합 계산 오류:', error); + alert('계산 중 오류가 발생했습니다.'); + + // 오류 시 필드 초기화 + $('#taxableMarketPrice').val('').trigger('focus'); + $('#standardMarketPrice').val('').trigger('focus'); + $('#standardMarketPrice_bottom').val('').trigger('focus'); $('#cmpttnAmt').val('').trigger('focus'); $('#levyWholAmt').val(''); $('#levyWholAmtDisplay').text('0 원'); + }, + complete: function() { + // 로딩 상태 해제 + $('#btnCalculate').prop('disabled', false).text('계산하기'); } }); } /** - * 행정처분 간격일 자동 계산 함수 - * 중요한 로직 주석: 시작일과 종료일 입력시 자동으로 간격일을 계산한다. + * 통합 계산 입력값 validation 함수 + * 중요로직: 건축물과세시가 계산부터 부과총액까지 전체 계산에 필요한 모든 필수값을 검증합니다. */ - window.calculateDaysBetween = function() { - var startDate = $('#impltBgngYmd').inputmask("unmaskedvalue"); - var endDate = $('#impltEndYmd').inputmask("unmaskedvalue"); - - if (startDate && endDate && startDate.length === 8 && endDate.length === 8) { - var start = new Date(startDate.substring(0,4), - parseInt(startDate.substring(4,6)) - 1, - startDate.substring(6,8)); - var end = new Date(endDate.substring(0,4), - parseInt(endDate.substring(4,6)) - 1, - endDate.substring(6,8)); - - var timeDiff = end.getTime() - start.getTime(); - var daysDiff = Math.ceil(timeDiff / (1000 * 3600 * 24)); - - $('#impltDaysCnt').val(daysDiff); + function validateCalculationInputs() { + // 1. 건축물과세시가 계산에 필요한 값들 검증 + var bldgNewPrcCrtrAmt = $('#bldgNewPrcCrtrAmt').inputmask('unmaskedvalue') || '0'; + if (!bldgNewPrcCrtrAmt || parseFloat(bldgNewPrcCrtrAmt) <= 0) { + return { + isValid: false, + message: '건물기준시가액을 입력해주세요.', + focusElement: '#bldgNewPrcCrtrAmt' + }; } - }; + + var strctIdx = $('#strctIdx').inputmask('unmaskedvalue') || '0'; + if (!strctIdx || parseFloat(strctIdx) <= 0) { + return { + isValid: false, + message: '구조지수를 입력해주세요.', + focusElement: '#strctIdx' + }; + } + + var usgIdx = $('#usgIdx').inputmask('unmaskedvalue') || '0'; + if (!usgIdx || parseFloat(usgIdx) <= 0) { + return { + isValid: false, + message: '용도지수를 입력해주세요.', + focusElement: '#usgIdx' + }; + } + + var pstnIdx = $('#pstnIdx').inputmask('unmaskedvalue') || '0'; + if (!pstnIdx || parseFloat(pstnIdx) <= 0) { + return { + isValid: false, + message: '위치지수를 입력해주세요.', + focusElement: '#pstnIdx' + }; + } + + var elpsYrRdvlrt = $('#elpsYrRdvlrt').inputmask('unmaskedvalue') || '0'; + if (!elpsYrRdvlrt || parseFloat(elpsYrRdvlrt) <= 0) { + return { + isValid: false, + message: '경과년수별잔가율을 입력해주세요.', + focusElement: '#elpsYrRdvlrt' + }; + } + + var bscsCstrnRt = $('#bscsCstrnRt').inputmask('unmaskedvalue') || '0'; + if (!bscsCstrnRt || parseFloat(bscsCstrnRt) <= 0) { + return { + isValid: false, + message: '기초공사율을 선택해주세요.', + focusElement: '#bscsCstrnSeCd' + }; + } + + // 2. 위반면적 검증 + var vltnArea = $('#vltnArea').inputmask('unmaskedvalue') || '0'; + if (!vltnArea || parseFloat(vltnArea) <= 0) { + return { + isValid: false, + message: '위반면적을 입력해주세요.', + focusElement: '#vltnArea' + }; + } + + // 3. 산정률 검증 + var cmpttnRtRate = $('#cmpttnRtRate').val(); + if (!cmpttnRtRate || parseFloat(cmpttnRtRate) <= 0) { + return { + isValid: false, + message: '산정률을 선택해주세요.', + focusElement: '#cmpttnRtCd' + }; + } + + // 4. 산정률2 검증 + var cmpttnRt2Rate = $('#cmpttnRt2Rate').val(); + if (!cmpttnRt2Rate || parseFloat(cmpttnRt2Rate) <= 0) { + return { + isValid: false, + message: '산정률2를 선택해주세요.', + focusElement: '#cmpttnRt2Cd' + }; + } + + return { isValid: true }; + } + + /** + * 통합 계산용 입력값 수집 함수 + * 중요로직: 모든 계산에 필요한 파라미터를 한번에 수집합니다. + */ + function collectAllCalculationInputs() { + return { + // 건축물과세시가 계산용 파라미터 + bldgNewPrcCrtrAmt: $('#bldgNewPrcCrtrAmt').inputmask('unmaskedvalue') || '0', + strctIdx: $('#strctIdx').inputmask('unmaskedvalue') || '0', + usgIdx: $('#usgIdx').inputmask('unmaskedvalue') || '0', + pstnIdx: $('#pstnIdx').inputmask('unmaskedvalue') || '0', + elpsYrRdvlrt: $('#elpsYrRdvlrt').inputmask('unmaskedvalue') || '0', + bscsCstrnRt: $('#bscsCstrnRt').inputmask('unmaskedvalue') || '0', + + // 산정액/부과총액 계산용 파라미터 + vltnArea: $('#vltnArea').inputmask('unmaskedvalue') || '0', + adsbmtnEnfcRt: $('#adsbmtnEnfcRt').val() || '100', + cmpttnRtRate: $('#cmpttnRtRate').val() || '0', + cmpttnRt2Rate: $('#cmpttnRt2Rate').val() || '0' + }; + } + // ======================================== // 7. 초기화 실행 @@ -1429,47 +1566,46 @@ // 전역 네임스페이스에 모듈 노출 window.LevyPrvntcPopup = LevyPrvntcPopup; - /** - * 가감산 팝업에서 선택된 데이터를 받아 처리하는 함수 - * @param {string} type - 'add' 또는 'minus' - * @param {Array} data - 선택된 행 데이터 배열 - */ - window.setAddMinusData = function(type, data) { - // 단건 선택 - const selectedRow = data; - if (!selectedRow) return; - - const rate = parseFloat(selectedRow.adsbmtnRt) || 0; - const rateForCalc = parseFloat(selectedRow.adsbmtnRtRate) || 0; - const rateForDisplay = rate + '%'; - let rateForTotal = 100; - - if (type === '1') { // '1' for 가산(add) - $('#adtnRtCd').val(selectedRow.adsbmtnRtCd); - $('#adtnRt').val(rate); - $('#adtnRtRate').val(rateForCalc); - $('#btnOpenAddPopup').text('+ 가산 [' + rateForDisplay + ']'); - - } else if (type === '2') { // '2' for 감산(minus) - $('#sbtrRtCd').val(selectedRow.adsbmtnRtCd); - $('#sbtrRt').val(rate); - $('#sbtrRtRate').val(rateForCalc); - $('#btnOpenMinusPopup').text('- 감산 [' + rateForDisplay + ']'); - } + // 외부 페이지에서 호출할 수 있도록 전역 함수로 노출 + window.calculateDaysBetween = LevyPrvntcPopup.calculateDaysBetween; + window.setAddMinusData = LevyPrvntcPopup.setAddMinusData; - var addRate = $('#adtnRt').val(); - var minusRate = $('#sbtrRt').val(); - const totalRateCalcAdd = parseFloat(addRate) || 0; - const totalRateCalcMinus = parseFloat(minusRate) || 0; - const totalRateCalc = 100 + totalRateCalcAdd - totalRateCalcMinus; - $("#adsbmtnEnfcRt").val( totalRateCalc ); - $("#adsbmtnEnfcRtDisplay").val( totalRateCalc+" %" ); +})(window, jQuery); - // 산정액 계산 함수 호출 - calculateLevyAmount(); - }; + -})(window, jQuery); + \ No newline at end of file