SNAPSHOT W7 已部署稳定态 — 凯迪ERP+OA一体化平台 (MET 73.3%)
恢复点(restore point)。别人改崩后可 git reset --hard 回到此提交。 == 此快照内容 == - 后端 oa-backend: 734 控制器 / 711 实体 (Spring Boot 3.2.5 + SQLite, 端口8091) - 前端 modern-ui/app: Vue3+Vite, 约700页 (构建产物已在 oa-backend/src/main/resources/static) - 数据库 oa-backend/data/oa.db: 含全部演示数据 (强制入库, 6.6MB) - 交接文档 go.md + go-code-reference/endpoints/entities/database.md - 多代理建设脚本 .claude/wf-*.js == 状态 == - 对 凯迪科技ERP_20260507.xlsx 合规 MET ~73.3% (PARTIAL 75: 34可建+6种子/bug+35外部硬天花板) - 安全: 5轮红队+5轮复检, default-deny分级鉴权, 连续零可利用 - W3~W7 累计补完436缺口; W8末轮(40缺口)为半成品(源码树可编译但未集成) - 运行: cd oa-backend; java -jar build/libs/oa-backend-0.1.0.jar --server.port=8091; admin/123456 == 排除(gitignore, 可再生) == node_modules / oa-backend/build / .jdks / *.log / Backup-ERP-* / 弃用的OFBiz核心(只保留modern-ui) 完整文件夹备份见同目录 Backup-ERP-20260615-191517/ (含上述全部, 仅缺 node_modules) 时间戳: 20260615-191517 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,395 @@
|
||||
package com.kaidi.oa.web;
|
||||
|
||||
import com.kaidi.oa.common.ApiException;
|
||||
import com.kaidi.oa.common.ApiResp;
|
||||
import com.kaidi.oa.common.Money;
|
||||
import com.kaidi.oa.common.NotFoundException;
|
||||
import com.kaidi.oa.domain.FinEmpLoan;
|
||||
import com.kaidi.oa.domain.Voucher;
|
||||
import com.kaidi.oa.repository.FinEmpLoanRepository;
|
||||
import com.kaidi.oa.repository.VoucherRepository;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 财务部·员工借款管理(缺口 #2)。
|
||||
*
|
||||
* 审计缺口:借款管理(员工借款申请/还款核销/逾期扣工资)无专属控制器,
|
||||
* 仅 FinAdvances 覆盖预收/预付场景,员工借款未独立建模。
|
||||
*
|
||||
* 本控制器提供:
|
||||
* GET / —— 列表(按 status/department/loanType 过滤)
|
||||
* GET /{id} —— 单条明细
|
||||
* POST / —— 新建借款申请(草稿)
|
||||
* PUT /{id} —— 编辑(草稿/已驳回可修改)
|
||||
* DELETE /{id} —— 删除(仅草稿)
|
||||
* POST /{id}/submit —— 提交审批:草稿 → 待审批
|
||||
* POST /{id}/approve —— 审批通过:待审批 → 已批准,自动生成借款凭证
|
||||
* POST /{id}/reject —— 驳回:待审批 → 草稿
|
||||
* POST /{id}/repay —— 还款核销:已批准/还款中,累加还款额,推进状态
|
||||
* POST /{id}/deduct-salary —— 标记从工资扣除(逾期处理)
|
||||
* GET /overdue-check —— 逾期检查清单(超还款日且未结清)
|
||||
* GET /summary —— 汇总(各状态借款总额/未还余额)
|
||||
*
|
||||
* 调度:每日 09:00 自动检查逾期,超过预计还款日且未结清的置为「逾期」状态。
|
||||
*
|
||||
* 写口:FINANCE_PREFIXES(/api/oa/fin-emp-loans) 限 ADMIN/APPROVER。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/oa/fin-emp-loans")
|
||||
public class FinEmpLoanController {
|
||||
|
||||
private final FinEmpLoanRepository loanRepo;
|
||||
private final VoucherRepository voucherRepo;
|
||||
|
||||
public FinEmpLoanController(FinEmpLoanRepository loanRepo, VoucherRepository voucherRepo) {
|
||||
this.loanRepo = loanRepo;
|
||||
this.voucherRepo = voucherRepo;
|
||||
}
|
||||
|
||||
// ===================== CRUD =====================
|
||||
|
||||
@GetMapping
|
||||
public ApiResp<List<FinEmpLoan>> list(
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) String department,
|
||||
@RequestParam(required = false) String loanType,
|
||||
@RequestParam(required = false) String employeeName) {
|
||||
if (status != null && department != null)
|
||||
return ApiResp.ok(loanRepo.findByStatusAndDepartment(status, department));
|
||||
if (status != null) return ApiResp.ok(loanRepo.findByStatus(status));
|
||||
if (department != null) return ApiResp.ok(loanRepo.findByDepartment(department));
|
||||
if (loanType != null) return ApiResp.ok(loanRepo.findByLoanType(loanType));
|
||||
if (employeeName != null) return ApiResp.ok(loanRepo.findByEmployeeName(employeeName));
|
||||
return ApiResp.ok(loanRepo.findAll());
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ApiResp<FinEmpLoan> get(@PathVariable Long id) {
|
||||
return ApiResp.ok(loanRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("员工借款记录不存在: " + id)));
|
||||
}
|
||||
|
||||
public record LoanRequest(
|
||||
String employeeName, String employeeId, String department,
|
||||
String loanType, String purpose,
|
||||
Double loanAmount, String applyDate, String expectedRepayDate,
|
||||
String remark, String operator) {}
|
||||
|
||||
@PostMapping
|
||||
public ApiResp<FinEmpLoan> create(@RequestBody LoanRequest req) {
|
||||
if (req.employeeName() == null || req.employeeName().isBlank())
|
||||
throw new ApiException(400, "员工姓名(employeeName)不能为空");
|
||||
if (req.loanAmount() == null || req.loanAmount() <= 0)
|
||||
throw new ApiException(400, "借款金额(loanAmount)必须大于 0");
|
||||
FinEmpLoan loan = new FinEmpLoan();
|
||||
loan.setLoanCode("EL-" + System.currentTimeMillis() % 1000000);
|
||||
loan.setEmployeeName(req.employeeName());
|
||||
loan.setEmployeeId(req.employeeId());
|
||||
loan.setDepartment(req.department());
|
||||
loan.setLoanType(req.loanType() == null ? FinEmpLoan.TYPE_OTHER : req.loanType());
|
||||
loan.setPurpose(req.purpose());
|
||||
BigDecimal amt = Money.of(req.loanAmount());
|
||||
loan.setLoanAmount(amt);
|
||||
loan.setRepaidAmount(BigDecimal.ZERO);
|
||||
loan.setRemainAmount(amt);
|
||||
loan.setApplyDate(req.applyDate() != null ? req.applyDate() : LocalDate.now().toString());
|
||||
loan.setExpectedRepayDate(req.expectedRepayDate());
|
||||
loan.setDeductFromSalary(Boolean.FALSE);
|
||||
loan.setStatus(FinEmpLoan.S_DRAFT);
|
||||
loan.setRemark(req.remark());
|
||||
loan.setOperator(req.operator());
|
||||
loan.setCreatedAt(Instant.now());
|
||||
loan.setUpdatedAt(Instant.now());
|
||||
return ApiResp.ok(loanRepo.save(loan));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ApiResp<FinEmpLoan> update(@PathVariable Long id, @RequestBody LoanRequest req) {
|
||||
FinEmpLoan loan = loanRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("员工借款记录不存在: " + id));
|
||||
if (!FinEmpLoan.S_DRAFT.equals(loan.getStatus()))
|
||||
throw new ApiException(409, "仅草稿状态可编辑,当前状态:" + loan.getStatus());
|
||||
if (req.employeeName() != null) loan.setEmployeeName(req.employeeName());
|
||||
if (req.employeeId() != null) loan.setEmployeeId(req.employeeId());
|
||||
if (req.department() != null) loan.setDepartment(req.department());
|
||||
if (req.loanType() != null) loan.setLoanType(req.loanType());
|
||||
if (req.purpose() != null) loan.setPurpose(req.purpose());
|
||||
if (req.loanAmount() != null) {
|
||||
BigDecimal amt = Money.of(req.loanAmount());
|
||||
loan.setLoanAmount(amt);
|
||||
loan.setRemainAmount(amt.subtract(Money.nz(loan.getRepaidAmount())));
|
||||
}
|
||||
if (req.applyDate() != null) loan.setApplyDate(req.applyDate());
|
||||
if (req.expectedRepayDate() != null) loan.setExpectedRepayDate(req.expectedRepayDate());
|
||||
if (req.remark() != null) loan.setRemark(req.remark());
|
||||
loan.setUpdatedAt(Instant.now());
|
||||
return ApiResp.ok(loanRepo.save(loan));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ApiResp<Void> delete(@PathVariable Long id) {
|
||||
FinEmpLoan loan = loanRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("员工借款记录不存在: " + id));
|
||||
if (!FinEmpLoan.S_DRAFT.equals(loan.getStatus()))
|
||||
throw new ApiException(409, "仅草稿状态可删除,当前状态:" + loan.getStatus());
|
||||
loanRepo.deleteById(id);
|
||||
return ApiResp.ok(null);
|
||||
}
|
||||
|
||||
// ===================== 状态机 =====================
|
||||
|
||||
@PostMapping("/{id}/submit")
|
||||
@Transactional
|
||||
public ApiResp<FinEmpLoan> submit(@PathVariable Long id) {
|
||||
FinEmpLoan loan = loanRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("员工借款记录不存在: " + id));
|
||||
if (!FinEmpLoan.S_DRAFT.equals(loan.getStatus()))
|
||||
throw new ApiException(409, "仅草稿状态可提交审批,当前状态:" + loan.getStatus());
|
||||
if (Money.nz(loan.getLoanAmount()).signum() <= 0)
|
||||
throw new ApiException(400, "借款金额必须大于 0");
|
||||
loan.setStatus(FinEmpLoan.S_PENDING);
|
||||
loan.setUpdatedAt(Instant.now());
|
||||
return ApiResp.ok(loanRepo.save(loan));
|
||||
}
|
||||
|
||||
public record ApproveRequest(String approver, String approvalComment) {}
|
||||
|
||||
/** 审批通过:待审批 → 已批准,自动生成借款发放凭证。 */
|
||||
@PostMapping("/{id}/approve")
|
||||
@Transactional
|
||||
public ApiResp<Map<String, Object>> approve(@PathVariable Long id,
|
||||
@RequestBody(required = false) ApproveRequest req) {
|
||||
FinEmpLoan loan = loanRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("员工借款记录不存在: " + id));
|
||||
if (!FinEmpLoan.S_PENDING.equals(loan.getStatus()))
|
||||
throw new ApiException(409, "仅待审批状态可审批,当前状态:" + loan.getStatus());
|
||||
loan.setStatus(FinEmpLoan.S_APPROVED);
|
||||
if (req != null) {
|
||||
loan.setApprover(req.approver());
|
||||
loan.setApprovalComment(req.approvalComment());
|
||||
}
|
||||
loan.setApprovalDate(LocalDate.now().toString());
|
||||
loan.setUpdatedAt(Instant.now());
|
||||
loanRepo.save(loan);
|
||||
|
||||
// 自动生成借款发放凭证(借 其他应收款 / 贷 银行存款)
|
||||
Voucher v = new Voucher();
|
||||
v.setVoucherNo("EL-" + loan.getLoanCode() + "-" + System.currentTimeMillis() % 10000);
|
||||
v.setVoucherDate(LocalDate.now().toString());
|
||||
v.setSummary("员工借款发放:" + loan.getEmployeeName() + " / " + loan.getLoanType()
|
||||
+ " / " + loan.getPurpose());
|
||||
v.setDebitAccount("1221 其他应收款");
|
||||
v.setCreditAccount("1002 银行存款");
|
||||
v.setAmount(Money.nz(loan.getLoanAmount()));
|
||||
v.setStatus(Voucher.S_DRAFT);
|
||||
v.setPreparer(req != null && req.approver() != null ? req.approver() : "系统");
|
||||
v.setReversed(Boolean.FALSE);
|
||||
v.setIsReversal(Boolean.FALSE);
|
||||
v.setSourceType("emp-loan");
|
||||
v.setSourceId(loan.getId());
|
||||
v.setCreatedAt(Instant.now());
|
||||
Voucher saved = voucherRepo.save(v);
|
||||
loan.setVoucherId(saved.getId());
|
||||
loanRepo.save(loan);
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("loanId", loan.getId());
|
||||
result.put("loanCode", loan.getLoanCode());
|
||||
result.put("status", loan.getStatus());
|
||||
result.put("voucherId", saved.getId());
|
||||
result.put("message", "借款审批通过,已生成借款发放凭证 #" + saved.getId());
|
||||
return ApiResp.ok(result);
|
||||
}
|
||||
|
||||
public record RejectRequest(String approver, String approvalComment) {}
|
||||
|
||||
/** 驳回:待审批 → 草稿。 */
|
||||
@PostMapping("/{id}/reject")
|
||||
@Transactional
|
||||
public ApiResp<FinEmpLoan> reject(@PathVariable Long id,
|
||||
@RequestBody RejectRequest req) {
|
||||
FinEmpLoan loan = loanRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("员工借款记录不存在: " + id));
|
||||
if (!FinEmpLoan.S_PENDING.equals(loan.getStatus()))
|
||||
throw new ApiException(409, "仅待审批状态可驳回,当前状态:" + loan.getStatus());
|
||||
if (req.approvalComment() == null || req.approvalComment().isBlank())
|
||||
throw new ApiException(400, "驳回须填写审批意见(approvalComment)");
|
||||
loan.setStatus(FinEmpLoan.S_DRAFT);
|
||||
loan.setApprover(req.approver());
|
||||
loan.setApprovalComment(req.approvalComment());
|
||||
loan.setApprovalDate(LocalDate.now().toString());
|
||||
loan.setUpdatedAt(Instant.now());
|
||||
return ApiResp.ok(loanRepo.save(loan));
|
||||
}
|
||||
|
||||
public record RepayRequest(Double repayAmount, String repayDate,
|
||||
String relatedExpenseCode, String operator) {}
|
||||
|
||||
/** 还款核销:已批准/还款中 → 更新余额,若余额为 0 则置为「已结清」。 */
|
||||
@PostMapping("/{id}/repay")
|
||||
@Transactional
|
||||
public ApiResp<FinEmpLoan> repay(@PathVariable Long id, @RequestBody RepayRequest req) {
|
||||
FinEmpLoan loan = loanRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("员工借款记录不存在: " + id));
|
||||
if (!FinEmpLoan.S_APPROVED.equals(loan.getStatus())
|
||||
&& !FinEmpLoan.S_REPAYING.equals(loan.getStatus())
|
||||
&& !FinEmpLoan.S_OVERDUE.equals(loan.getStatus()))
|
||||
throw new ApiException(409, "仅已批准/还款中/逾期状态可还款,当前状态:" + loan.getStatus());
|
||||
if (req.repayAmount() == null || req.repayAmount() <= 0)
|
||||
throw new ApiException(400, "还款金额(repayAmount)必须大于 0");
|
||||
|
||||
BigDecimal repay = Money.of(req.repayAmount());
|
||||
BigDecimal remain = Money.nz(loan.getRemainAmount());
|
||||
if (repay.compareTo(remain) > 0)
|
||||
throw new ApiException(400, "还款金额 " + repay + " 超过未还余额 " + remain);
|
||||
|
||||
BigDecimal newRepaid = Money.nz(loan.getRepaidAmount()).add(repay);
|
||||
BigDecimal newRemain = remain.subtract(repay);
|
||||
loan.setRepaidAmount(newRepaid);
|
||||
loan.setRemainAmount(newRemain);
|
||||
loan.setStatus(newRemain.signum() == 0 ? FinEmpLoan.S_SETTLED : FinEmpLoan.S_REPAYING);
|
||||
if (newRemain.signum() == 0) loan.setSettleDate(req.repayDate() != null ? req.repayDate() : LocalDate.now().toString());
|
||||
if (req.relatedExpenseCode() != null) loan.setRelatedExpenseCode(req.relatedExpenseCode());
|
||||
loan.setUpdatedAt(Instant.now());
|
||||
|
||||
// 生成还款凭证(借 银行存款 / 贷 其他应收款)
|
||||
Voucher v = new Voucher();
|
||||
v.setVoucherNo("ELR-" + loan.getLoanCode() + "-" + System.currentTimeMillis() % 10000);
|
||||
v.setVoucherDate(req.repayDate() != null ? req.repayDate() : LocalDate.now().toString());
|
||||
v.setSummary("员工还款核销:" + loan.getEmployeeName() + " 还款 " + repay + " 元");
|
||||
v.setDebitAccount("1002 银行存款");
|
||||
v.setCreditAccount("1221 其他应收款");
|
||||
v.setAmount(repay);
|
||||
v.setStatus(Voucher.S_DRAFT);
|
||||
v.setPreparer(req.operator() != null ? req.operator() : "系统");
|
||||
v.setReversed(Boolean.FALSE);
|
||||
v.setIsReversal(Boolean.FALSE);
|
||||
v.setSourceType("emp-loan-repay");
|
||||
v.setSourceId(loan.getId());
|
||||
v.setCreatedAt(Instant.now());
|
||||
voucherRepo.save(v);
|
||||
|
||||
return ApiResp.ok(loanRepo.save(loan));
|
||||
}
|
||||
|
||||
/** 标记从工资代扣:逾期/已批准 → 设置 deductFromSalary=true。 */
|
||||
@PostMapping("/{id}/deduct-salary")
|
||||
@Transactional
|
||||
public ApiResp<FinEmpLoan> deductSalary(@PathVariable Long id) {
|
||||
FinEmpLoan loan = loanRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("员工借款记录不存在: " + id));
|
||||
if (FinEmpLoan.S_SETTLED.equals(loan.getStatus()))
|
||||
throw new ApiException(409, "已结清的借款无需从工资扣除");
|
||||
loan.setDeductFromSalary(Boolean.TRUE);
|
||||
loan.setUpdatedAt(Instant.now());
|
||||
return ApiResp.ok(loanRepo.save(loan));
|
||||
}
|
||||
|
||||
// ===================== 聚合端点 =====================
|
||||
|
||||
/** 逾期检查:返回超过预计还款日且未结清的借款清单。 */
|
||||
@GetMapping("/overdue-check")
|
||||
public ApiResp<List<Map<String, Object>>> overdueCheck() {
|
||||
List<FinEmpLoan> all = loanRepo.findAll();
|
||||
String today = LocalDate.now().toString();
|
||||
List<Map<String, Object>> result = new ArrayList<>();
|
||||
for (FinEmpLoan loan : all) {
|
||||
if (FinEmpLoan.S_SETTLED.equals(loan.getStatus())) continue;
|
||||
String repayDate = loan.getExpectedRepayDate();
|
||||
if (repayDate == null) continue;
|
||||
if (repayDate.compareTo(today) < 0) {
|
||||
long overdueDays = ChronoUnit.DAYS.between(
|
||||
LocalDate.parse(repayDate), LocalDate.now());
|
||||
Map<String, Object> row = new LinkedHashMap<>();
|
||||
row.put("id", loan.getId());
|
||||
row.put("loanCode", loan.getLoanCode());
|
||||
row.put("employeeName", loan.getEmployeeName());
|
||||
row.put("department", loan.getDepartment());
|
||||
row.put("loanAmount", Money.nz(loan.getLoanAmount()));
|
||||
row.put("remainAmount", Money.nz(loan.getRemainAmount()));
|
||||
row.put("expectedRepayDate", repayDate);
|
||||
row.put("overdueDays", overdueDays);
|
||||
row.put("status", loan.getStatus());
|
||||
row.put("deductFromSalary", loan.getDeductFromSalary());
|
||||
result.add(row);
|
||||
}
|
||||
}
|
||||
return ApiResp.ok(result);
|
||||
}
|
||||
|
||||
/** 汇总:各状态借款金额统计 + 未还余额汇总。 */
|
||||
@GetMapping("/summary")
|
||||
public ApiResp<Map<String, Object>> summary() {
|
||||
List<FinEmpLoan> all = loanRepo.findAll();
|
||||
BigDecimal totalLoan = BigDecimal.ZERO;
|
||||
BigDecimal totalRepaid = BigDecimal.ZERO;
|
||||
BigDecimal totalRemain = BigDecimal.ZERO;
|
||||
BigDecimal totalOverdue = BigDecimal.ZERO;
|
||||
long overdueCount = 0;
|
||||
|
||||
for (FinEmpLoan loan : all) {
|
||||
totalLoan = totalLoan.add(Money.nz(loan.getLoanAmount()));
|
||||
totalRepaid = totalRepaid.add(Money.nz(loan.getRepaidAmount()));
|
||||
totalRemain = totalRemain.add(Money.nz(loan.getRemainAmount()));
|
||||
if (FinEmpLoan.S_OVERDUE.equals(loan.getStatus())) {
|
||||
totalOverdue = totalOverdue.add(Money.nz(loan.getRemainAmount()));
|
||||
overdueCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> result = new LinkedHashMap<>();
|
||||
result.put("totalLoanAmount", totalLoan);
|
||||
result.put("totalRepaidAmount", totalRepaid);
|
||||
result.put("totalRemainAmount", totalRemain);
|
||||
result.put("totalOverdueAmount", totalOverdue);
|
||||
result.put("overdueCount", overdueCount);
|
||||
result.put("totalCount", all.size());
|
||||
return ApiResp.ok(result);
|
||||
}
|
||||
|
||||
// ===================== 调度 — 每日逾期自动标记 =====================
|
||||
|
||||
/**
|
||||
* 每日 09:00 自动检查逾期:超过预计还款日且未结清的记录置为「逾期」状态。
|
||||
*/
|
||||
@Scheduled(cron = "0 0 9 * * *")
|
||||
@Transactional
|
||||
public void autoMarkOverdue() {
|
||||
String today = LocalDate.now().toString();
|
||||
List<FinEmpLoan> active = loanRepo.findAll().stream()
|
||||
.filter(l -> !FinEmpLoan.S_SETTLED.equals(l.getStatus())
|
||||
&& !FinEmpLoan.S_DRAFT.equals(l.getStatus())
|
||||
&& !FinEmpLoan.S_PENDING.equals(l.getStatus()))
|
||||
.toList();
|
||||
for (FinEmpLoan loan : active) {
|
||||
String repayDate = loan.getExpectedRepayDate();
|
||||
if (repayDate != null && repayDate.compareTo(today) < 0
|
||||
&& !FinEmpLoan.S_OVERDUE.equals(loan.getStatus())) {
|
||||
long days = ChronoUnit.DAYS.between(LocalDate.parse(repayDate), LocalDate.now());
|
||||
loan.setStatus(FinEmpLoan.S_OVERDUE);
|
||||
loan.setOverdueDays((int) days);
|
||||
loan.setUpdatedAt(Instant.now());
|
||||
loanRepo.save(loan);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user