package com.by4cloud.platformx.business.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.DateUtil; 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.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.math.RoundingMode; 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("履约阶段与合同不一致,请联系技术人员"); } //合同 Contract contract = contractMapper.selectById(schedule.getContractId()); if (ObjUtil.isNull(contract)) { return R.failed("合同信息异常"); } return handleSchedule(addDTO, schedule, contract); } public void saveScheduleProcess(Date date, ContractPaymentSchedule schedule) { //更新履约 ContractPaymentScheduleProcess contractPaymentScheduleProcess = new ContractPaymentScheduleProcess(); contractPaymentScheduleProcess.setContractId(schedule.getContractId()); contractPaymentScheduleProcess.setContractName(schedule.getContractName()); contractPaymentScheduleProcess.setScheduleId(schedule.getId()); contractPaymentScheduleProcess.setScheduleName(schedule.getStageName()); contractPaymentScheduleProcess.setProcessDate(date); 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); } } private R handleSchedule(ContractPaymentScheduleProcessAddDTO addDTO, ContractPaymentSchedule schedule, Contract contract) { if (schedule.getStageName().equals("货到签收")) { if (CollUtil.isEmpty(addDTO.getContractOutBoundIds())) { return R.failed("签收信息不能为空"); } //更新合同出库到货时间 addDTO.getContractOutBoundIds().forEach(contractOutBoundId -> { ContractOutBound contractOutBound = contractOutBoundMapper.selectById(contractOutBoundId); contractOutBound.setArrivalTime(addDTO.getProcessDate()); contractOutBound.setArrivalAttNames(addDTO.getOutBoundAttNames()); contractOutBound.setArrivalAttPaths(addDTO.getOutBoundAttPaths()); contractOutBoundMapper.updateById(contractOutBound); }); } //付款比例不为0 保存收款明细 if (schedule.getPaymentRatio().compareTo(new BigDecimal("0")) > 0) { savePaymentConfirm(addDTO, contract, schedule); } updateCurrentSchedule(addDTO.getProcessDate(), schedule, contract); //保存履约记录 saveScheduleProcess(addDTO.getProcessDate(), schedule); return R.ok(); } private void savePaymentConfirm(ContractPaymentScheduleProcessAddDTO addDTO, Contract contract, ContractPaymentSchedule schedule) { BigDecimal amount = schedule.getPlannedAmount(); if (schedule.getStageName().equals("货到签收")) { amount = new BigDecimal("0"); //计算签收价格 for (Long contractOutBoundId : addDTO.getContractOutBoundIds()) { ContractOutBound contractOutBound = contractOutBoundMapper.selectById(contractOutBoundId); ContractSubjectMatter subjectMatter = subjectMatterMapper.selectOne(Wrappers.lambdaQuery() .eq(ContractSubjectMatter::getContractId, contract.getId()) .eq(ContractSubjectMatter::getMaterialCode, contractOutBound.getSubjectMatterCode()) .eq(ContractSubjectMatter::getLineNo, contractOutBound.getLineNo()) .last("limit 1")); amount = amount.add(contractOutBound.getOutBoundNum().multiply(subjectMatter.getUnitPrice())); } //实际到货金额按比例计算 amount = schedule.getPlannedAmount().multiply(amount.divide(contract.getAmount(), 2, RoundingMode.HALF_UP)); } //新增当前阶段应收 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(amount); currentConfim.setReceivableAmount(amount); PaymentConfirm lastConfirm = paymentConfirmMapper.selectOne(Wrappers.lambdaQuery().eq(PaymentConfirm::getContractId, contract.getId()) .orderByDesc(PaymentConfirm::getCreateTime).last("limit 1")); if (ObjUtil.isNotNull(lastConfirm)) { currentConfim.setTotalAmount(lastConfirm.getTotalAmount().subtract(currentConfim.getTransationAmount())); if (contract.getContractNo().startsWith("ZB-")) { paymentConfirmMapper.insert(currentConfim); } return; } currentConfim.setTotalAmount(currentConfim.getTransationAmount().multiply(new BigDecimal("-1"))); paymentConfirmMapper.insert(currentConfim); handleBeforeRemain(currentConfim, contract); } public void handleBeforeRemain(PaymentConfirm currentConfim, Contract contract) { //客户付款完成合同 List customerCompleteContractList = contractMapper.selectList(Wrappers.lambdaQuery().eq(Contract::getPartyAId, contract.getPartyAId()) .eq(Contract::getPartyBId, SecurityUtils.getUser().getCompId()).eq(Contract::getContractStatus, 3)); if (CollUtil.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 && contract.getContractNo().startsWith("ZB-")) { //新增完成付款合同 资金转出 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")); if (contract.getContractNo().startsWith("ZB-")) { paymentConfirmMapper.insert(outConfirm); } outAmountList.add(contractLastConfirm.getTotalAmount()); } } if (CollUtil.isNotEmpty(outAmountList)) { BigDecimal currentSum = outAmountList.stream().reduce(BigDecimal.ZERO, BigDecimal::add); //新增当前合同 资金转入 PaymentConfirm inConfirm = BeanUtil.copyProperties(currentConfim, PaymentConfirm.class, "id", "transationAmount", "advanceAmount", "receivableAmount", "overdueAmount", "totalAmount"); inConfirm.setBusinessType("资金转入"); inConfirm.setConfirmTime(new Date()); inConfirm.setTransationAmount(currentSum); inConfirm.setTotalAmount(currentConfim.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"))); } if (contract.getContractNo().startsWith("ZB-")) { paymentConfirmMapper.insert(inConfirm); } contract.setPaidAmount(currentSum); contractMapper.updateById(contract); } } } private void updateCurrentSchedule(Date date, ContractPaymentSchedule schedule, Contract contract) { //查询是否有后续阶段 ContractPaymentSchedule afterOneSchedule = contractPaymentScheduleMapper.selectOne(Wrappers.lambdaQuery() .eq(ContractPaymentSchedule::getContractId, schedule.getContractId()) .gt(ContractPaymentSchedule::getStageOrder, schedule.getStageOrder()) .orderByAsc(ContractPaymentSchedule::getCreateTime) .last("limit 1")); if (ObjUtil.isNotNull(afterOneSchedule)) { //更新下阶段名称 List contractOutBoundList = contractOutBoundMapper.selectList(Wrappers.lambdaQuery().eq(ContractOutBound::getContractId, contract.getId()) .isNull(ContractOutBound::getArrivalTime)); if (CollUtil.isEmpty(contractOutBoundList)) { contract.setNextScheduleName(afterOneSchedule.getStageName()); } } if (ObjUtil.isNull(afterOneSchedule)) { contract.setNextScheduleName(""); } contractMapper.updateById(contract); //更新履约阶段时间 if (ObjUtil.isNull(schedule.getEffectiveDate()) && ObjUtil.isNull(schedule.getEffectiveEndDate())) { schedule.setEffectiveEndDate(DateUtil.offsetDay(date, schedule.getAgreedDays())); } contractPaymentScheduleMapper.updateById(schedule); } @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); } }