package com.by4cloud.platformx.business.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ObjUtil; import cn.hutool.core.util.StrUtil; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.by4cloud.platformx.business.dto.ContractPaymentScheduleProcessAddDTO; import com.by4cloud.platformx.business.entity.*; import com.by4cloud.platformx.business.mapper.*; import com.by4cloud.platformx.business.service.ContractPaymentScheduleProcessService; import com.by4cloud.platformx.business.vo.ContractPaymentScheduleVo; import com.by4cloud.platformx.business.vo.ScheduleProcessVo; import com.by4cloud.platformx.common.core.util.R; import com.by4cloud.platformx.common.security.util.SecurityUtils; import com.github.yulichang.wrapper.MPJLambdaWrapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.*; import java.util.stream.Collectors; /** * 履约节点 * * @author syt * @date 2026-05-11 11:10:26 */ @Service @RequiredArgsConstructor public class ContractPaymentScheduleProcessServiceImpl extends ServiceImpl implements ContractPaymentScheduleProcessService { private final ContractPaymentScheduleMapper contractPaymentScheduleMapper; private final ContractMapper contractMapper; private final ContractSubjectMatterMapper subjectMatterMapper; private final PaymentConfirmMapper paymentConfirmMapper; private final ContractOutBoundMapper contractOutBoundMapper; @Override public R add(ContractPaymentScheduleProcessAddDTO addDTO) { ContractPaymentSchedule schedule = contractPaymentScheduleMapper.selectById(addDTO.getScheduleId()); if (ObjUtil.isNull(schedule)) { return R.failed("履约阶段与合同不一致,请联系技术人员"); } if (schedule.getStageName().equals("货到签收") && ArrayUtil.isEmpty(addDTO.getContractOutBoundIds().toArray())) { return R.failed("请选择到货的出库信息"); } //合同 Contract contract = contractMapper.selectById(schedule.getContractId()); List subjectMatterList = subjectMatterMapper.selectList(Wrappers.lambdaQuery().eq(ContractSubjectMatter::getContractId, contract.getId())); if (ArrayUtil.isEmpty(subjectMatterList.toArray())) { return R.failed("该合同标的物异常,请联系技术人员"); } // if (!subjectMatterList.stream().allMatch(item -> item.getDeliveryStatus() == 2)) { // return R.failed("出库数量小于当前合同标的物数量,无法进行当前操作"); // } if (schedule.getStageName().equals("货到签收")) { //更新合同出库到货时间 addDTO.getContractOutBoundIds().forEach(contractOutBoundId -> { ContractOutBound contractOutBound = contractOutBoundMapper.selectById(contractOutBoundId); contractOutBound.setArrivalTime(addDTO.getProcessDate()); contractOutBound.setArrivalAttNames(addDTO.getOutBoundAttNames()); contractOutBound.setArrivalAttPaths(addDTO.getOutBoundAttPaths()); contractOutBoundMapper.updateById(contractOutBound); }); } //履约 ContractPaymentScheduleProcess contractPaymentScheduleProcess = BeanUtil.copyProperties(addDTO, ContractPaymentScheduleProcess.class); contractPaymentScheduleProcess.setContractId(schedule.getContractId()); contractPaymentScheduleProcess.setContractName(schedule.getContractName()); contractPaymentScheduleProcess.setScheduleId(schedule.getId()); contractPaymentScheduleProcess.setScheduleName(schedule.getStageName()); ContractPaymentScheduleProcess lastProcess = baseMapper.selectOne(Wrappers.lambdaQuery().eq(ContractPaymentScheduleProcess::getContractId, schedule.getContractId()) .eq(ContractPaymentScheduleProcess::getScheduleId, schedule.getId()).last("limit 1")); if (ObjUtil.isNull(lastProcess)) { baseMapper.insert(contractPaymentScheduleProcess); } //更新当前阶段 if (ObjUtil.isNull(schedule.getEffectiveDate()) && ObjUtil.isNull(schedule.getEffectiveEndDate())) { schedule.setEffectiveEndDate(DateUtil.offsetDay(addDTO.getProcessDate(), schedule.getAgreedDays())); contractPaymentScheduleMapper.updateById(schedule); } // //查询是否有之前阶段 // ContractPaymentSchedule beforeSchedule = contractPaymentScheduleMapper.selectOne(Wrappers.lambdaQuery().eq(ContractPaymentSchedule::getContractId, schedule.getContractId()) // .lt(ContractPaymentSchedule::getStageOrder, schedule.getStageOrder()).orderByDesc(ContractPaymentSchedule::getCreateTime).last("limit 1")); // if (ObjUtil.isNotNull(beforeSchedule) && ObjUtil.isNull(beforeSchedule.getEffectiveEndDate())) { // beforeSchedule.setEffectiveEndDate(schedule.getEffectiveDate()); // contractPaymentScheduleMapper.updateById(beforeSchedule); // } List contractOutBoundList = contractOutBoundMapper.selectList(Wrappers.lambdaQuery().eq(ContractOutBound::getContractId, contract.getId()) .isNotNull(ContractOutBound::getArrivalTime)); if (schedule.getStageName().equals("货到签收")) { if (ArrayUtil.isNotEmpty(contractOutBoundList.toArray()) && ArrayUtil.isNotEmpty(subjectMatterList.toArray())) { Map sumMap = contractOutBoundList.stream() .collect(Collectors.groupingBy( ContractOutBound::getSubjectMatterCode, Collectors.reducing( BigDecimal.ZERO, ContractOutBound::getOutBoundNum, BigDecimal::add ) )); Boolean outFlag = checkQuantityEquality(subjectMatterList, sumMap); //出库到货与合同标的物种类数量一致 if (outFlag) { //新增当前阶段应收 PaymentConfirm currentConfim = new PaymentConfirm(); currentConfim.setBusinessType(schedule.getStageName() + "应收"); currentConfim.setBusGuestId(contract.getPartyAId()); currentConfim.setBusGuestName(contract.getPartyA()); currentConfim.setContractId(contract.getId()); currentConfim.setContractName(contract.getContractName()); currentConfim.setContractNo(contract.getContractNo()); currentConfim.setScheduleId(schedule.getId()); currentConfim.setScheduleName(schedule.getStageName()); currentConfim.setConfirmTime(addDTO.getProcessDate()); currentConfim.setTransationAmount(schedule.getPlannedAmount()); currentConfim.setReceivableAmount(schedule.getPlannedAmount()); PaymentConfirm lastConfirm = paymentConfirmMapper.selectOne(Wrappers.lambdaQuery().eq(PaymentConfirm::getContractId, contract.getId()) .orderByDesc(PaymentConfirm::getCreateTime).last("limit 1")); BigDecimal lastTotal = new BigDecimal("0"); if (ObjUtil.isNotNull(lastConfirm)) { lastTotal = lastConfirm.getTotalAmount(); }else { //客户付款完成合同 List customerCompleteContractList = contractMapper.selectList(Wrappers.lambdaQuery().eq(Contract::getPartyAId, contract.getPartyAId()) .eq(Contract::getPartyBId, SecurityUtils.getUser().getCompId()).apply(" amount = paid_amount")); if (ArrayUtil.isNotEmpty(customerCompleteContractList)){ List outAmountList = new ArrayList<>(); for (Contract completeContract:customerCompleteContractList ) { PaymentConfirm contractLastConfirm = paymentConfirmMapper.selectOne(Wrappers.lambdaQuery().eq(PaymentConfirm::getContractId,completeContract.getId()) .orderByDesc(PaymentConfirm::getCreateTime).last("limit 1")); //查询客户付款完成合同中最后明细金额 有预收转入当前合同 if (contractLastConfirm.getTotalAmount().compareTo(new BigDecimal("0"))>0){ //新增完成付款合同 资金转出 PaymentConfirm outConfirm = BeanUtil.copyProperties(contractLastConfirm,PaymentConfirm.class,"id","transationAmount", "advanceAmount","receivableAmount","overdueAmount","totalAmount"); outConfirm.setBusinessType("资金转出"); outConfirm.setConfirmTime(new Date()); outConfirm.setTransationAmount(contractLastConfirm.getTotalAmount()); outConfirm.setTotalAmount(new BigDecimal("0")); paymentConfirmMapper.insert(outConfirm); outAmountList.add(outConfirm.getTransationAmount()); } } if (ArrayUtil.isNotEmpty(outAmountList.toArray())){ BigDecimal currentSum = new BigDecimal("0"); for (BigDecimal currentAmount:outAmountList ) { currentSum=currentSum.add(currentAmount); //新增当前合同 资金转入 PaymentConfirm inConfirm = BeanUtil.copyProperties(lastConfirm,PaymentConfirm.class,"id","transationAmount", "advanceAmount","receivableAmount","overdueAmount","totalAmount"); inConfirm.setBusinessType("资金转入"); inConfirm.setConfirmTime(new Date()); inConfirm.setTransationAmount(currentSum.subtract(currentAmount)); inConfirm.setTotalAmount(lastConfirm.getTotalAmount().add(currentSum)); if (inConfirm.getTotalAmount().compareTo(new BigDecimal("0"))>0){ inConfirm.setAdvanceAmount(inConfirm.getTotalAmount()); }else if (inConfirm.getTotalAmount().compareTo(new BigDecimal("0"))<0){ inConfirm.setReceivableAmount(inConfirm.getTotalAmount().multiply(new BigDecimal("-1"))); } paymentConfirmMapper.insert(inConfirm); } } } } currentConfim.setTotalAmount(lastTotal.subtract(currentConfim.getReceivableAmount())); if (schedule.getPaymentRatio().compareTo(new BigDecimal("0")) > 0) { paymentConfirmMapper.insert(currentConfim); //更新当前阶段付款情况 if (lastTotal.compareTo(new BigDecimal("0"))>0){ if (currentConfim.getTotalAmount().compareTo(new BigDecimal("0"))>=0) { schedule.setPaymentStatus(2); schedule.setPlannedAmount(schedule.getPlannedAmount()); contractPaymentScheduleMapper.updateById(schedule); }else { schedule.setPaymentStatus(1); schedule.setPlannedAmount(lastTotal); } } } //查询是否有后续阶段 List afterSchedule = contractPaymentScheduleMapper.selectList(Wrappers.lambdaQuery() .eq(ContractPaymentSchedule::getContractId, schedule.getContractId()) .gt(ContractPaymentSchedule::getStageOrder, schedule.getStageOrder())); if (ArrayUtil.isNotEmpty(afterSchedule.toArray()) && afterSchedule.size() == 1) { //最后阶段生效时间 ContractPaymentSchedule endSchedule = afterSchedule.get(0); if (StrUtil.equals(endSchedule.getStageName(), "质保金")) { endSchedule.setEffectiveEndDate(DateUtil.offsetDay(addDTO.getProcessDate(), endSchedule.getAgreedDays())); contractPaymentScheduleMapper.updateById(endSchedule); //当前阶段生效时间 // schedule.setEffectiveEndDate(DateUtil.offsetDay(addDTO.getProcessDate(), schedule.getAgreedDays())); // contractPaymentScheduleMapper.updateById(schedule); //最后阶段应收 PaymentConfirm newConfim = new PaymentConfirm(); newConfim.setBusinessType(endSchedule.getStageName() + "应收"); newConfim.setBusGuestId(contract.getPartyAId()); newConfim.setBusGuestName(contract.getPartyA()); newConfim.setContractId(contract.getId()); newConfim.setContractName(contract.getContractName()); newConfim.setContractNo(contract.getContractNo()); newConfim.setScheduleId(endSchedule.getId()); newConfim.setScheduleName(schedule.getStageName()); newConfim.setConfirmTime(addDTO.getProcessDate()); newConfim.setTransationAmount(endSchedule.getPlannedAmount()); newConfim.setReceivableAmount(endSchedule.getPlannedAmount()); PaymentConfirm lastNewConfirm = paymentConfirmMapper.selectOne(Wrappers.lambdaQuery().eq(PaymentConfirm::getContractId, contract.getId()) .orderByDesc(PaymentConfirm::getCreateTime).last("limit 1")); BigDecimal lastNewTotal = new BigDecimal("0"); if (ObjUtil.isNotNull(lastNewConfirm)) { lastNewTotal = lastNewConfirm.getTotalAmount(); } newConfim.setTotalAmount(lastNewTotal.subtract(newConfim.getReceivableAmount())); if (endSchedule.getPaymentRatio().compareTo(new BigDecimal("0")) > 0) { paymentConfirmMapper.insert(newConfim); } } //更新合同下个阶段 contract.setNextScheduleName(endSchedule.getStageName()); contractMapper.updateById(contract); } if (ArrayUtil.isNotEmpty(afterSchedule.toArray()) && afterSchedule.size() > 1) { //更新合同下个阶段 contract.setNextScheduleName(afterSchedule.get(0).getStageName()); contractMapper.updateById(contract); } if (ArrayUtil.isEmpty(afterSchedule.toArray())) { // schedule.setEffectiveEndDate(contract.getExpirationDate()); // contractPaymentScheduleMapper.updateById(schedule); //更新合同下个阶段 contract.setNextScheduleName("无"); contractMapper.updateById(contract); //当前为合同最后阶段 PaymentConfirm lastNewConfirm = paymentConfirmMapper.selectOne(Wrappers.lambdaQuery().eq(PaymentConfirm::getContractId, contract.getId()) .orderByDesc(PaymentConfirm::getCreateTime).last("limit 1")); if (lastNewConfirm.getTotalAmount().compareTo(new BigDecimal("0")) >= 0) { //有预付且超过应收 关闭合同状态 contract.setContractStatus(3); contractMapper.updateById(contract); } } } } } else { //新增当前阶段应收 PaymentConfirm currentConfim = new PaymentConfirm(); currentConfim.setBusinessType(schedule.getStageName() + "应收"); currentConfim.setBusGuestId(contract.getPartyAId()); currentConfim.setBusGuestName(contract.getPartyA()); currentConfim.setContractId(contract.getId()); currentConfim.setContractName(contract.getContractName()); currentConfim.setContractNo(contract.getContractNo()); currentConfim.setScheduleId(schedule.getId()); currentConfim.setScheduleName(schedule.getStageName()); currentConfim.setConfirmTime(addDTO.getProcessDate()); currentConfim.setTransationAmount(schedule.getPlannedAmount()); currentConfim.setReceivableAmount(schedule.getPlannedAmount()); PaymentConfirm lastConfirm = paymentConfirmMapper.selectOne(Wrappers.lambdaQuery().eq(PaymentConfirm::getContractId, contract.getId()) .orderByDesc(PaymentConfirm::getCreateTime).last("limit 1")); BigDecimal lastTotal = new BigDecimal("0"); if (ObjUtil.isNotNull(lastConfirm)) { lastTotal = lastConfirm.getTotalAmount(); } currentConfim.setTotalAmount(lastTotal.subtract(currentConfim.getReceivableAmount())); if (schedule.getPaymentRatio().compareTo(new BigDecimal("0")) > 0) { paymentConfirmMapper.insert(currentConfim); //更新当前阶段付款情况 if (lastTotal.compareTo(new BigDecimal("0"))>0){ if (currentConfim.getTotalAmount().compareTo(new BigDecimal("0"))>=0) { schedule.setPaymentStatus(2); schedule.setPlannedAmount(schedule.getPlannedAmount()); contractPaymentScheduleMapper.updateById(schedule); }else { schedule.setPaymentStatus(1); schedule.setPlannedAmount(lastTotal); } } } //查询是否有后续阶段 List afterSchedule = contractPaymentScheduleMapper.selectList(Wrappers.lambdaQuery() .eq(ContractPaymentSchedule::getContractId, schedule.getContractId()) .gt(ContractPaymentSchedule::getStageOrder, schedule.getStageOrder())); if (ArrayUtil.isNotEmpty(afterSchedule.toArray()) && afterSchedule.size() == 1) { //最后阶段生效时间 ContractPaymentSchedule endSchedule = afterSchedule.get(0); if (StrUtil.equals(endSchedule.getStageName(), "质保金")) { endSchedule.setEffectiveEndDate(DateUtil.offsetDay(addDTO.getProcessDate(), endSchedule.getAgreedDays())); contractPaymentScheduleMapper.updateById(endSchedule); //当前阶段生效时间 schedule.setEffectiveEndDate(DateUtil.offsetDay(addDTO.getProcessDate(), schedule.getAgreedDays())); contractPaymentScheduleMapper.updateById(schedule); //最后阶段应收 PaymentConfirm newConfim = new PaymentConfirm(); newConfim.setBusinessType(endSchedule.getStageName() + "应收"); newConfim.setBusGuestId(contract.getPartyAId()); newConfim.setBusGuestName(contract.getPartyA()); newConfim.setContractId(contract.getId()); newConfim.setContractName(contract.getContractName()); newConfim.setContractNo(contract.getContractNo()); newConfim.setScheduleId(endSchedule.getId()); newConfim.setScheduleName(schedule.getStageName()); newConfim.setConfirmTime(addDTO.getProcessDate()); newConfim.setTransationAmount(endSchedule.getPlannedAmount()); newConfim.setReceivableAmount(endSchedule.getPlannedAmount()); PaymentConfirm lastNewConfirm = paymentConfirmMapper.selectOne(Wrappers.lambdaQuery().eq(PaymentConfirm::getContractId, contract.getId()) .orderByDesc(PaymentConfirm::getCreateTime).last("limit 1")); BigDecimal lastNewTotal = new BigDecimal("0"); if (ObjUtil.isNotNull(lastNewConfirm)) { lastNewTotal = lastNewConfirm.getTotalAmount(); } newConfim.setTotalAmount(lastNewTotal.subtract(newConfim.getReceivableAmount())); if (endSchedule.getPaymentRatio().compareTo(new BigDecimal("0")) > 0) { paymentConfirmMapper.insert(newConfim); //更新当前阶段付款情况 if (lastNewTotal.compareTo(new BigDecimal("0"))>0){ if (newConfim.getTotalAmount().compareTo(new BigDecimal("0"))>=0) { schedule.setPaymentStatus(2); schedule.setPlannedAmount(schedule.getPlannedAmount()); contractPaymentScheduleMapper.updateById(schedule); }else { schedule.setPaymentStatus(1); schedule.setPlannedAmount(lastNewTotal); } } } } //更新合同下个阶段 contract.setNextScheduleName(endSchedule.getStageName()); contractMapper.updateById(contract); } if (ArrayUtil.isNotEmpty(afterSchedule.toArray()) && afterSchedule.size() > 1) { //更新合同下个阶段 contract.setNextScheduleName(afterSchedule.get(0).getStageName()); contractMapper.updateById(contract); } if (ArrayUtil.isEmpty(afterSchedule.toArray())) { // schedule.setEffectiveEndDate(contract.getExpirationDate()); // contractPaymentScheduleMapper.updateById(schedule); //更新合同下个阶段 contract.setNextScheduleName("无"); contractMapper.updateById(contract); //当前为合同最后阶段 PaymentConfirm lastNewConfirm = paymentConfirmMapper.selectOne(Wrappers.lambdaQuery().eq(PaymentConfirm::getContractId, contract.getId()) .orderByDesc(PaymentConfirm::getCreateTime).last("limit 1")); if (lastNewConfirm.getTotalAmount().compareTo(new BigDecimal("0")) >= 0) { //有预付且超过应收 关闭合同状态 contract.setContractStatus(3); contractMapper.updateById(contract); } } } return R.ok(); } public boolean checkQuantityEquality(List subjectMatterList, Map materialCodeMap) { // 1. 基本校验 if (subjectMatterList == null || materialCodeMap == null) { return false; } // 如果要求两者包含的物料代码完全一致且数量一致,先比较 Key 的数量 // 提取 List 中非空的 materialCode List listCodes = subjectMatterList.stream() .map(ContractSubjectMatter::getMaterialCode) .filter(Objects::nonNull) .distinct() .sorted() .collect(Collectors.toList()); List mapKeys = materialCodeMap.keySet().stream() .sorted() .collect(Collectors.toList()); // 如果物料编码集合不一致,直接返回 false if (!listCodes.equals(mapKeys)) { return false; } // 2. 逐个比较数量 // 假设 ContractSubjectMatter 中有 getQuantity() 方法返回 BigDecimal 或 Number for (ContractSubjectMatter item : subjectMatterList) { String code = item.getMaterialCode(); if (code == null) continue; BigDecimal mapValue = materialCodeMap.get(code); BigDecimal listValue = item.getQuantity(); // 假设这是 List 中代表数量的字段 // 处理 null 情况 if (mapValue == null && listValue == null) { continue; // 都为空视为相等,或根据业务定为不等 } if (mapValue == null || listValue == null) { return false; // 一个为空一个不为空,不等 } // 使用 compareTo 比较 BigDecimal 值,0 表示相等 if (listValue.compareTo(mapValue) != 0) { return false; // 发现不相等,立即返回 } } return true; // 所有匹配项数量均相等 } @Override public R selectScheduleProcess(Long id) { MPJLambdaWrapper wrapper = new MPJLambdaWrapper() .select(ContractPaymentSchedule::getId, ContractPaymentSchedule::getStageName, ContractPaymentSchedule::getEffectiveDate, ContractPaymentSchedule::getPlannedAmount, ContractPaymentSchedule::getPlannedAmount, ContractPaymentSchedule::getEffectiveEndDate, ContractPaymentSchedule::getPaymentDate, ContractPaymentSchedule::getPaymentStatus, ContractPaymentSchedule::getActualAmount, ContractPaymentSchedule::getAgreedDays,ContractPaymentSchedule::getPaymentRatio) .select(ContractPaymentScheduleProcess::getProcessDate) .leftJoin(ContractPaymentScheduleProcess.class, ContractPaymentScheduleProcess::getScheduleId, ContractPaymentSchedule::getId) .eq(ContractPaymentSchedule::getContractId, id) .orderByAsc(ContractPaymentSchedule::getCreateTime); List scheduleVoList = contractPaymentScheduleMapper.selectJoinList(ContractPaymentScheduleVo.class, wrapper); return R.ok(scheduleVoList); } }