From e09ca4407ca0822776ba95d50ca43e3cd5d1ebed Mon Sep 17 00:00:00 2001 From: yinq Date: Tue, 7 Apr 2026 08:45:46 +0800 Subject: [PATCH] =?UTF-8?q?1.1.11=20=E5=9B=9E=E6=AC=BE=E7=A1=AE=E8=AE=A4?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=90=88=E5=90=8C=E8=AE=A2=E5=8D=95=E5=9B=9E?= =?UTF-8?q?=E6=AC=BE=E6=AF=94=E4=BE=8B=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oa/erp/domain/ErpProjectPlanStage.java | 4 +- .../erp/domain/bo/ErpProjectPlanStageBo.java | 4 +- .../vo/ContractCollectionStageDetailVo.java | 16 ++- .../erp/domain/vo/ErpProjectPlanStageVo.java | 4 +- .../impl/ErpProjectPlanServiceImpl.java | 102 ++++++++++++++---- .../mapper/oa/erp/ErpProjectPlanMapper.xml | 10 +- 6 files changed, 106 insertions(+), 34 deletions(-) diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectPlanStage.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectPlanStage.java index 87623e3b..62d75034 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectPlanStage.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/ErpProjectPlanStage.java @@ -63,12 +63,12 @@ public class ErpProjectPlanStage extends TenantEntity { /** * 预计回款比例(%) */ - private Long repaymentRate; + private BigDecimal repaymentRate; /** * 预计回款金额 */ - private Long repaymentAmount; + private BigDecimal repaymentAmount; /** * 预计回款时间 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectPlanStageBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectPlanStageBo.java index 63d7db69..3a25203f 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectPlanStageBo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpProjectPlanStageBo.java @@ -62,12 +62,12 @@ public class ErpProjectPlanStageBo extends BaseEntity { /** * 预计回款比例(%) */ - private Long repaymentRate; + private BigDecimal repaymentRate; /** * 预计回款金额 */ - private Long repaymentAmount; + private BigDecimal repaymentAmount; /** * 预计回款时间 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ContractCollectionStageDetailVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ContractCollectionStageDetailVo.java index 63255dfc..62a52492 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ContractCollectionStageDetailVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ContractCollectionStageDetailVo.java @@ -29,20 +29,28 @@ public class ContractCollectionStageDetailVo { * 项目计划阶段回款确认信息(按 payment_stage_id 与合同付款节点关联) */ private Long planStageId; - private Long repaymentRate; + private BigDecimal repaymentRate; /** - * 预计回款金额(用于前端计算实际回款比例) + * 预计回款金额(阶段维度) */ - private Long repaymentAmount; + private BigDecimal repaymentAmount; + /** + * 合同订单金额(erp_project_info.amount),实际回款比例分母 + */ + private BigDecimal contractTotalPrice; private Date receivableDate; private BigDecimal actualRepaymentAmount; /** - * 实际回款比例(%),与 erp_project_plan_stage 持久化字段一致 + * 实际回款比例(%) = 本阶段实际回款 / 合同总金额 * 100,与 erp_project_plan_stage 持久化一致 */ private BigDecimal actualRepaymentRate; private Long collectionConfirmUserId; + /** + * 回款确认人昵称(关联 sys_user.nick_name) + */ + private String collectionConfirmNickName; private Date collectionConfirmTime; private String collectionConfirmStatus; private String collectionConfirmRemark; diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectPlanStageVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectPlanStageVo.java index 42a418ab..f9ec059e 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectPlanStageVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpProjectPlanStageVo.java @@ -84,13 +84,13 @@ public class ErpProjectPlanStageVo implements Serializable { * 预计回款比例(%) */ @ExcelProperty(value = "预计回款比例(%)") - private Long repaymentRate; + private BigDecimal repaymentRate; /** * 预计回款金额 */ @ExcelProperty(value = "预计回款金额") - private Long repaymentAmount; + private BigDecimal repaymentAmount; /** * 预计回款时间 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectPlanServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectPlanServiceImpl.java index b71e94af..661f44ec 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectPlanServiceImpl.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpProjectPlanServiceImpl.java @@ -22,6 +22,7 @@ import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.tenant.helper.TenantHelper; import org.dromara.oa.erp.constant.ProjectCategoryConstant; import org.dromara.oa.base.domain.BasePaymentStage; +import org.dromara.oa.erp.domain.ErpProjectInfo; import org.dromara.oa.erp.domain.ErpProjectPlan; import org.dromara.oa.erp.domain.ErpProjectPlanStage; import org.dromara.oa.erp.domain.bo.ErpProjectPlanBo; @@ -30,6 +31,7 @@ import org.dromara.oa.erp.domain.vo.ContractCollectionPageVo; import org.dromara.oa.erp.domain.vo.ContractCollectionStageDetailVo; import org.dromara.oa.erp.domain.vo.ErpProjectPlanStageVo; import org.dromara.oa.erp.domain.vo.ErpProjectPlanVo; +import org.dromara.oa.erp.mapper.ErpProjectInfoMapper; import org.dromara.oa.erp.mapper.ErpProjectPlanMapper; import org.dromara.oa.erp.mapper.ErpProjectPlanStageMapper; import org.dromara.oa.erp.service.IErpProjectPlanService; @@ -63,6 +65,8 @@ public class ErpProjectPlanServiceImpl implements IErpProjectPlanService { private final ErpProjectPlanStageMapper planStageMapper; + private final ErpProjectInfoMapper projectInfoMapper; + @DubboReference(timeout = 30000) private RemoteWorkflowService remoteWorkflowService; @@ -391,22 +395,21 @@ public class ErpProjectPlanServiceImpl implements IErpProjectPlanService { } stage.setReceivableDate(bo.getReceivableDate()); stage.setActualRepaymentAmount(bo.getActualRepaymentAmount()); - stage.setActualRepaymentRate(calcActualRepaymentRate(bo.getActualRepaymentAmount(), stage.getRepaymentAmount())); + BigDecimal contractOrderAmount = resolveContractOrderAmount(stage.getProjectPlanId(), stage.getProjectId()); + stage.setActualRepaymentRate(calcActualRepaymentRate(bo.getActualRepaymentAmount(), contractOrderAmount)); stage.setCollectionConfirmRemark(bo.getCollectionConfirmRemark()); stage.setCollectionConfirmUserId(LoginHelper.getUserId()); stage.setCollectionConfirmTime(new Date()); - String confirmStatus = bo.getCollectionConfirmStatus(); - if (StringUtils.isBlank(confirmStatus)) { - BigDecimal expected = stage.getRepaymentAmount() == null ? BigDecimal.ZERO : BigDecimal.valueOf(stage.getRepaymentAmount()); - BigDecimal actual = bo.getActualRepaymentAmount() == null ? BigDecimal.ZERO : bo.getActualRepaymentAmount(); - if (actual.compareTo(BigDecimal.ZERO) <= 0) { - confirmStatus = "0"; - } else if (expected.compareTo(BigDecimal.ZERO) > 0 && actual.compareTo(expected) >= 0) { - confirmStatus = "2"; - } else { - confirmStatus = "1"; - } + String confirmStatus = "0"; + BigDecimal expected = stage.getRepaymentAmount() == null ? BigDecimal.ZERO : stage.getRepaymentAmount(); + BigDecimal actual = bo.getActualRepaymentAmount() == null ? BigDecimal.ZERO : bo.getActualRepaymentAmount(); + if (actual.compareTo(BigDecimal.ZERO) <= 0) { + confirmStatus = "0"; + } else if (expected.compareTo(BigDecimal.ZERO) > 0 && actual.compareTo(expected) >= 0) { + confirmStatus = "2"; + } else { + confirmStatus = "1"; } stage.setCollectionConfirmStatus(confirmStatus); @@ -416,23 +419,80 @@ public class ErpProjectPlanServiceImpl implements IErpProjectPlanService { if (StringUtils.isNotBlank(bo.getReasonsExplanation())) { stage.setReasonsExplanation(bo.getReasonsExplanation()); } - return planStageMapper.updateById(stage) > 0; + boolean updated = planStageMapper.updateById(stage) > 0; + if (updated) { + refreshContractOrderPaymentRate(stage.getProjectPlanId(), stage.getProjectId()); + } + return updated; } /** - * 计算 实际回款比例(%) = 实际回款金额 / 预计回款金额 * 100 - * - * @param actualRepaymentAmount 实际回款金额 - * @param repaymentAmount 预计回款金额 + * 同步合同订单回款比例:本计划下各阶段 actual_repayment_rate(%) 相加,写入 erp_project_info.order_payment_rate */ - private BigDecimal calcActualRepaymentRate(BigDecimal actualRepaymentAmount, Long repaymentAmount) { + private void refreshContractOrderPaymentRate(Long projectPlanId, Long projectIdFromStage) { + if (projectPlanId == null) { + return; + } + List stages = planStageMapper.selectList( + Wrappers.lambdaQuery(ErpProjectPlanStage.class) + .eq(ErpProjectPlanStage::getProjectPlanId, projectPlanId) + .eq(ErpProjectPlanStage::getDelFlag, "0")); + BigDecimal sumRate = stages.stream() + .map(s -> s.getActualRepaymentRate() != null ? s.getActualRepaymentRate() : BigDecimal.ZERO) + .reduce(BigDecimal.ZERO, BigDecimal::add) + .setScale(2, RoundingMode.HALF_UP); + + Long projectId = projectIdFromStage; + if (projectId == null) { + ErpProjectPlan plan = baseMapper.selectById(projectPlanId); + if (plan == null) { + return; + } + projectId = plan.getProjectId(); + } + if (projectId == null) { + return; + } + projectInfoMapper.update(null, Wrappers.lambdaUpdate() + .set(ErpProjectInfo::getOrderPaymentRate, sumRate) + .eq(ErpProjectInfo::getProjectId, projectId)); + } + + /** + * 合同订单金额(erp_project_info.amount),作实际回款比例分母,不再查合同表 + */ + private BigDecimal resolveContractOrderAmount(Long projectPlanId, Long projectIdFromStage) { + Long projectId = projectIdFromStage; + if (projectId == null && projectPlanId != null) { + ErpProjectPlan plan = baseMapper.selectById(projectPlanId); + if (plan != null) { + projectId = plan.getProjectId(); + } + } + if (projectId == null) { + return null; + } + ErpProjectInfo project = projectInfoMapper.selectById(projectId); + if (project == null || project.getAmount() == null) { + return null; + } + return project.getAmount(); + } + + /** + * 计算 实际回款比例(%) = 本阶段实际回款金额 / 合同订单金额 * 100 + * + * @param actualRepaymentAmount 本阶段实际回款金额 + * @param contractOrderAmount 合同订单金额(erp_project_info.amount) + */ + private BigDecimal calcActualRepaymentRate(BigDecimal actualRepaymentAmount, BigDecimal contractOrderAmount) { BigDecimal actual = actualRepaymentAmount == null ? BigDecimal.ZERO : actualRepaymentAmount; - BigDecimal expected = repaymentAmount == null ? BigDecimal.ZERO : BigDecimal.valueOf(repaymentAmount); - if (expected.compareTo(BigDecimal.ZERO) <= 0) { + BigDecimal total = contractOrderAmount == null ? BigDecimal.ZERO : contractOrderAmount; + if (total.compareTo(BigDecimal.ZERO) <= 0) { return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); } return actual - .divide(expected, 6, RoundingMode.HALF_UP) + .divide(total, 6, RoundingMode.HALF_UP) .multiply(BigDecimal.valueOf(100)) .setScale(2, RoundingMode.HALF_UP); } diff --git a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectPlanMapper.xml b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectPlanMapper.xml index 662fef65..5db0dbe4 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectPlanMapper.xml +++ b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpProjectPlanMapper.xml @@ -420,25 +420,29 @@ ps.repayment_amount, ps.receivable_date, ps.actual_repayment_amount, + pi.amount as contractTotalPrice, case when ps.plan_stage_id is null then null when ps.actual_repayment_rate is not null then ps.actual_repayment_rate - when ps.repayment_amount is null or ps.repayment_amount = 0 then 0 - else round(ifnull(ps.actual_repayment_amount, 0) * 100 / ps.repayment_amount, 2) + when pi.amount is null or pi.amount = 0 then 0 + else round(ifnull(ps.actual_repayment_amount, 0) * 100 / pi.amount, 2) end as actual_repayment_rate, ps.collection_confirm_user_id, ps.collection_confirm_time, ps.collection_confirm_status, - ps.collection_confirm_remark + ps.collection_confirm_remark, + ucc.nick_name as collectionConfirmNickName from erp_project_plan pp inner join erp_contract_payment_method pm on pm.contract_id = pp.contract_id and pm.del_flag = '0' + left join erp_project_info pi on pi.project_id = pp.project_id and pi.del_flag = '0' left join base_payment_stage bps on bps.payment_stage_id = pm.payment_stage_id left join erp_project_plan_stage ps on ps.project_plan_id = pp.project_plan_id and ps.del_flag = '0' and ps.payment_stage_id is not null and ps.payment_stage_id = pm.payment_stage_id + left join sys_user ucc on ps.collection_confirm_user_id = ucc.user_id where pp.project_plan_id = #{projectPlanId} and pp.del_flag = '0' order by pm.sort_order asc