부과예고의 계산하기 버튼 추가로 인한 계산 로직으로 변경... 진행중...

dev
박성영 3 months ago
parent 8f230673d8
commit b523ad335b

@ -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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> 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<String, Object> calculations = new HashMap<>();
calculations.put("mprcStdAmt", mprcStdAmt); // 시가표준액
calculations.put("cmpttnAmt", cmpttnAmt); // 산정액
calculations.put("levyWholAmt", levyWholAmt); // 부과총액
// 표시용 포맷팅된 값
Map<String, String> 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<String, Object> 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());
}
}

@ -6,70 +6,44 @@
<!--
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
■ 부과예고 이행강제금 산출조사서 계산 로직 상세 명세서 (VO 컬럼명 기준) - BigDecimal 서버 계산 방식
■ 부과예고 이행강제금 산출조사서 통합 계산 시스템 (VO 컬럼명 기준) - 단일 API 방식
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
1. 건축물과세시가(bdstTxtnMprc) 계산 【서버 API】
- 공식: bldgCrtrMprcAmt × strctIdx × usgIdx × pstnIdx × elpsYrRdvlrt × bscsCstrnRt
- 설명: 건물신축가격기준액 × 구조지수 × 용도지수 × 위치지수 × 경과년수별잔가율 × 기초공사율
- 정확도: BigDecimal 사용으로 부동소수점 오차 방지
- 반올림: 소수점 없이 반올림 (RoundingMode.HALF_UP)
- API: POST /calculateTaxableMarketPrice.ajax
- 처리위치: CrdnLevyPrvntcController.calculateTaxableMarketPrice()
■ 통합 계산 API: /calculateAll.ajax
- 한번의 API 호출로 모든 계산 단계를 수행합니다
- BigDecimal 사용으로 완전한 정확도 보장
- 사용자는 "계산하기" 버튼 클릭 시에만 계산 수행
2. 시가표준액(mprcStdAmt) 계산 【서버 API】
- 공식: bdstTxtnMprc.divide(1000).multiply(1000) with RoundingMode.DOWN
■ 4단계 계산 프로세스 (서버에서 순차 처리)
1. 건축물과세시가(bdstTxtnMprc) 계산 - 이부분은 율 이라고 표현되지만 0.85, 1 등 이미지 % 100 처리 된 값으로 표현됨
- 공식: bldgNewPrcCrtrAmt × strctIdx × usgIdx × pstnIdx × (elpsYrRdvlrt) × (bscsCstrnRt)
- 설명: 건물기준시가액 × 구조지수 × 용도지수 × 위치지수 × 경과년수별잔가율 × 기초공사율
- 반올림: setScale(0, RoundingMode.HALF_UP) - 소수점 반올림
2. 시가표준액(mprcStdAmt) 계산
- 공식: bdstTxtnMprc.divide(1000, RoundingMode.DOWN).multiply(1000)
- 설명: 건축물과세시가에서 1,000원 미만 절사
- 정확도: BigDecimal 사용으로 정확한 절사 보장
- API: POST /calculateStandardMarketPrice.ajax
- 처리위치: CrdnLevyPrvntcController.calculateStandardMarketPrice()
3. 가감산시행령률(adsbmtnEnfcRt) 계산 【서버 API】
- 공식: baseRate + adtnRt - sbtrRt (BigDecimal 연산)
- 기본값: 100%
- 가산 적용 시: 100 + adtnRt (가산율)
- 감산 적용 시: 100 - sbtrRt (감산율)
- 범위 제한: 0 ≤ rate ≤ 1000% (서버에서 검증)
- API: POST /calculateAdsbmtnEnfcRt.ajax
- 처리위치: CrdnLevyPrvntcController.calculateAdsbmtnEnfcRt()
4. 산정률(cmpttnRt) / 산정률 비율값(cmpttnRtRate)
- 표시용 값(cmpttnRt): 행위유형별 고정값 (예: 40.00%)
- 계산용 값(cmpttnRtRate): 소수점 비율 (예: 0.40)
- 변환: cmpttnRtRate = cmpttnRt ÷ 100 (서버에서 BigDecimal 처리)
- 데이터 출처: 행위정보에서 자동 설정
5. 산정률2(cmpttnRt2) / 산정률2 비율값(cmpttnRt2Rate)
- 표시용 값(cmpttnRt2): 위반사항별 값 (예: 50.00%)
- 계산용 값(cmpttnRt2Rate): 소수점 비율 (예: 0.50)
- 변환: cmpttnRt2Rate = cmpttnRt2 ÷ 100 (서버에서 BigDecimal 처리)
- 선택: 담당자가 드롭다운에서 선택
6. 산정액(cmpttnAmt) 계산 【서버 API】
- 공식: mprcStdAmt × vltnArea × (adsbmtnEnfcRt ÷ 100) × cmpttnRtRate × cmpttnRt2Rate
- 예시: 1,234,567원 → 1,234,000원
3. 산정액(cmpttnAmt) 계산
- 공식: mprcStdAmt × vltnArea × (adsbmtnEnfcRt÷100) × cmpttnRtRate × cmpttnRt2Rate
- 설명: 시가표준액 × 위반면적 × 가감산시행령률(%) × 산정률(비율) × 산정률2(비율)
- 정확도: BigDecimal.multiply() 체인 연산으로 정확도 보장
- 반올림: setScale(0, RoundingMode.DOWN) - 소수점 버림
- API: POST /calculateLevyAmount.ajax
- 처리위치: CrdnLevyPrvntcController.calculateLevyAmount()
7. 부과총액(levyWholAmt) 계산 【서버 API】
4. 부과총액(levyWholAmt) 계산
- 공식: cmpttnAmt.divide(10, RoundingMode.DOWN).multiply(10)
- 설명: 산정액에서 1의 자리 절사 (10원 단위 버림)
- 정확도: BigDecimal 연산으로 정확한 절사 보장
- 설명: 산정액에서 10원 단위 절사 (1의 자리 버림)
- 예시: 1,234,567원 → 1,234,560원
- API: POST /calculateLevyAmount.ajax (산정액과 함께 반환)
- 처리위치: CrdnLevyPrvntcController.calculateLevyAmount()
8. 이행강제금(impltCpsrAmt) 계산
5. 이행강제금(impltCpsrAmt) 계산 (별도 서비스 로직)
- 공식: SUM(levyWholAmt) WHERE crdnYr = ? AND crdnNo = ? AND impltTaskSeCd = ?
- 설명: 해당 단속의 모든 행위정보별 부과총액 합계
- 조건: 모든 행위정보에 대한 부과정보가 등록된 경우에만 계산
- 정확도: DB에서 SUM 집계 함수 사용으로 정확성 보장
- 조건: 모든 행위정보에 대한 부과정보가 등록된 경우에만 자동 계산
- 처리위치: CrdnLevyPrvntcServiceImpl.updateImpltCpsrAmt()
■ 주요 VO 컬럼 설명
- bldgCrtrMprcAmt: 건물신축가격기준액 (BigDecimal)
- bldgNewPrcCrtrAmt: 건물기준시가액 (BigDecimal)
- strctIdx: 구조지수 (BigDecimal)
- usgIdx: 용도지수 (BigDecimal)
- pstnIdx: 위치지수 (BigDecimal)
@ -87,27 +61,22 @@
- levyWholAmt: 부과총액 (BigDecimal)
- impltCpsrAmt: 이행강제금 (BigDecimal)
계산 처리 흐름 (완전한 서버 사이드 BigDecimal 계산)
사용자 인터페이스 흐름
┌─────────────────────────────────────────────────────────────────────────────┐
│ 1단계: 건축물과세시가 계산 【calculateTaxableMarketPrice.ajax】 │
│ ├─ Input: 건물기준시가액, 구조지수, 용도지수, 위치지수, 경과년수별잔가율, 기초공사율 │
│ ├─ Process: BigDecimal 6개 값 곱셈 연산 │
│ └─ Output: 정확한 건축물과세시가 │
│ 1. 사용자 입력: 모든 필수 데이터 입력 │
│ - 건물기준시가액, 구조지수, 용도지수, 위치지수, 경과년수별잔가율, 기초공사율 │
│ - 위반면적, 가감산시행령률, 산정률, 산정률2 │
│ ↓ │
│ 2단계: 시가표준액 계산 【calculateStandardMarketPrice.ajax】 │
│ ├─ Input: 건축물과세시가 │
│ ├─ Process: BigDecimal.divide(1000).multiply(1000) 절사 연산 │
│ └─ Output: 1,000원 미만 절사된 시가표준액 │
│ 2. "계산하기" 버튼 클릭 → 단일 API 호출 【calculateAll.ajax】 │
│ - 입력값 validation 수행 │
│ - 모든 계산을 서버에서 한번에 처리 │
│ ↓ │
│ 3단계: 산정액/부과총액 계산 【calculateLevyAmount.ajax】 │
│ ├─ Input: 시가표준액, 위반면적, 가감산시행령률, 산정률, 산정률2 │
│ ├─ Process: BigDecimal 5개 값 곱셈 → 소수점 버림 → 10원 단위 절사 │
│ └─ Output: 정확한 산정액 + 부과총액 │
│ 3. 계산 결과 일괄 표시 │
│ - 건축물과세시가, 시가표준액, 산정액, 부과총액 동시 업데이트 │
│ - 계산하기 버튼 자동 숨김 │
│ ↓ │
│ 4단계: 이행강제금 계산 【Service Layer + DB SUM】 │
│ ├─ Input: 모든 행위정보별 부과총액 │
│ ├─ Process: DB SUM 집계 함수로 합산 │
│ └─ Output: 최종 이행강제금 │
│ 4. 데이터 저장 시 이행강제금 자동 계산 │
│ - 모든 행위정보 완료 시 자동 합산 │
└─────────────────────────────────────────────────────────────────────────────┘
■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
@ -292,7 +261,7 @@
</tr>
<tr>
<td rowspan="2" class="b-right-0 b-bottom-0">
<input type="text" class="input numericMask" id="standardMarketPrice_top" readonly/><!-- 시가표준액 -->
<input type="text" class="input numericMask" id="standardMarketPrice_bottom" readonly/><!-- 시가표준액 -->
</td>
<td rowspan="2" class="op-cell b-right-0 b-bottom-0">X</td>
<td rowspan="2" class="b-right-0 b-bottom-0">
@ -349,7 +318,12 @@
<tr>
<td colspan="12" class="b-top-0">
<h2><strong>부과총액 = <span id="levyWholAmtDisplay">0 원</span></strong></h2>
<h2 style="max-height: 45px;">
<strong>부과총액 = <span id="levyWholAmtDisplay"></span></strong>
<c:if test="${mode ne 'V'}">
<button type="button" id="btnCalculate" class="newbtns bg3" style="vertical-align: top; margin-left: 10px;">계산하기</button>
</c:if>
</h2>
<input type="hidden" class="input numericMask" id="levyWholAmt" value="">
</td>
</tr>
@ -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) {
$('<input type="hidden" id="bscsCstrnYBdstCmpttnRt">').appendTo('body');
$('<input type="hidden" id="bscsCstrnNBdstCmpttnRt">').appendTo('body');
$('<input type="hidden" id="dupEtbldgBdstCmpttnRt">').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: '<c:url value="/crdn/crndRegistAndView/crdnLevyPrvntc/calculateLevyAmount.ajax"/>',
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: '<c:url value="/crdn/crndRegistAndView/crdnLevyPrvntc/calculateTaxableMarketPrice.ajax"/>',
url: '<c:url value="/crdn/crndRegistAndView/crdnLevyPrvntc/calculateAll.ajax"/>',
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: '<c:url value="/crdn/crndRegistAndView/crdnLevyPrvntc/calculateStandardMarketPrice.ajax"/>',
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();
};
</script>
})(window, jQuery);
<style>
/* 계산하기 버튼 기본 스타일 */
#btnCalculate {
display: none; /* 기본적으로 숨김 */
transition: all 0.3s ease;
}
</script>
/* 계산하기 버튼 강조 스타일 */
#btnCalculate.btn-highlight {
background-color: #ff6b35 !important;
border-color: #ff6b35 !important;
box-shadow: 0 0 10px rgba(255, 107, 53, 0.3);
animation: pulse 5s infinite;
}
/* 계산하기 버튼 펄스 애니메이션 */
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(255, 107, 53, 0.7);
}
70% {
box-shadow: 0 0 0 10px rgba(255, 107, 53, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(255, 107, 53, 0);
}
}
/* 계산하기 버튼 비활성 상태 */
#btnCalculate:disabled {
opacity: 0.7;
animation: none;
}
</style>
Loading…
Cancel
Save