package com.by4cloud.platformx.business.service.impl; import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.date.DatePattern; 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.OutBoundAddDTO; import com.by4cloud.platformx.business.dto.OutSubjectMatterAddDTO; import com.by4cloud.platformx.business.entity.*; import com.by4cloud.platformx.business.mapper.*; import com.by4cloud.platformx.business.service.OutBoundService; 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; /** * EPR出库记录 * * @author syt * @date 2026-04-29 11:32:05 */ @Service @RequiredArgsConstructor public class OutBoundServiceImpl extends ServiceImpl implements OutBoundService { private final ContractPaymentScheduleMapper scheduleMapper; private final ContractSubjectMatterMapper contractSubjectMatterMapper; private final PaymentConfirmMapper paymentConfirmMapper; private final ContractMapper contractMapper; private final ContractPaymentScheduleProcessMapper scheduleProcessMapper; private final ContractOutBoundMapper contractOutBoundMapper; private final DeliveryOverdueMapper deliveryOverdueMapper; @Override public R add(OutBoundAddDTO addDTO) { if (ArrayUtil.isEmpty(addDTO.getSubjectMatterList().toArray())) { return R.failed("出库标的物不能为空"); } MPJLambdaWrapper wrapper = new MPJLambdaWrapper() .selectAll(ContractSubjectMatter.class) .ne(ContractSubjectMatter::getDeliveryStatus, 2) .in(ContractSubjectMatter::getMaterialCode, addDTO.getSubjectMatterList().stream().map(item -> item.getSubjectMatterCode()).collect(Collectors.toList())) .exists(Contract.class, contractQuery -> contractQuery.select(ContractSubjectMatter::getId) .eq(Contract::getId, ContractSubjectMatter::getContractId) .eq(StrUtil.isNotBlank(addDTO.getBusGuestName()), Contract::getPartyA, addDTO.getBusGuestName()) .eq(ObjUtil.isNotNull(addDTO.getBusGuestId()), Contract::getPartyAId, addDTO.getBusGuestId()) ) .orderByAsc(ContractSubjectMatter::getCreateTime); List subjectMatterList = contractSubjectMatterMapper.selectList(wrapper); if (ArrayUtil.isEmpty(subjectMatterList.toArray())) { return R.failed("没有查询到相关合同订单"); } List erpCodeList = subjectMatterList.stream().map(ContractSubjectMatter::getMaterialCode).collect(Collectors.toList()); BigDecimal orderNum = subjectMatterList.stream().filter(item -> item.getDeliveryStatus() != 2) .map(item -> item.getDeliveryStatus() == 0 ? item.getQuantity() : item.getRemainingQuantity()) .reduce(BigDecimal.ZERO, BigDecimal::add); BigDecimal outTotal = addDTO.getSubjectMatterList().stream() .filter(item -> erpCodeList.contains(item.getSubjectMatterCode())) .map(OutSubjectMatterAddDTO::getOutBoundNum) .filter(num -> num != null) // 过滤掉 null 值,防止 NullPointerException .reduce(BigDecimal.ZERO, BigDecimal::add); if (outTotal.compareTo(orderNum) > 0) { return R.failed("出库数量超出合同订单数量"); } //当前入库标的物 Map currentOutMap = new HashMap<>(); //出库批次 String batchNumber = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN); //出库 addDTO.getSubjectMatterList().stream().forEach(outSubjectMatterAddDTO -> { OutBound outBound = BeanUtil.copyProperties(outSubjectMatterAddDTO, OutBound.class); outBound.setBatchNumber(batchNumber); outBound.setBusGuestName(addDTO.getBusGuestName()); outBound.setBusGuestId(addDTO.getBusGuestId()); outBound.setOutBoundTime(addDTO.getOutBoundTime()); baseMapper.insert(outBound); BigDecimal remainNum = outSubjectMatterAddDTO.getOutBoundNum(); for (ContractSubjectMatter subjectMatter : subjectMatterList) { if (outSubjectMatterAddDTO.getSubjectMatterCode().equals(subjectMatter.getMaterialCode())) { Contract contract = contractMapper.selectById(subjectMatter.getContractId()); if (subjectMatter.getDeliveredQuantity().compareTo(new BigDecimal("0")) > 0) { remainNum = remainNum.subtract(subjectMatter.getRemainingQuantity()); //部分交付状态下 全部出库 if (remainNum.compareTo(new BigDecimal("0")) >= 0) { BigDecimal currentOut = subjectMatter.getRemainingQuantity(); subjectMatter.setDeliveredQuantity(subjectMatter.getQuantity()); subjectMatter.setRemainingQuantity(new BigDecimal("0")); subjectMatter.setActualDeliveryDate(DateUtil.today()); subjectMatter.setDeliveryStatus(2); subjectMatter.setLastDeliveredQuantity(currentOut); contractSubjectMatterMapper.updateById(subjectMatter); currentOutMap.put(subjectMatter.getContractId(), subjectMatter); //新增合同出库历史 saveContractOutBound(contract, addDTO, subjectMatter, outBound.getId()); } else { BigDecimal currentOut = remainNum.add(subjectMatter.getRemainingQuantity()); subjectMatter.setDeliveredQuantity(subjectMatter.getQuantity().add(remainNum)); subjectMatter.setRemainingQuantity(subjectMatter.getRemainingQuantity().subtract(currentOut)); subjectMatter.setActualDeliveryDate(DateUtil.today()); subjectMatter.setDeliveryStatus(1); subjectMatter.setLastDeliveredQuantity(currentOut); contractSubjectMatterMapper.updateById(subjectMatter); currentOutMap.put(subjectMatter.getContractId(), subjectMatter); //新增合同出库历史 saveContractOutBound(contract, addDTO, subjectMatter, outBound.getId()); break; } } else { //未交付状态下 remainNum = remainNum.subtract(subjectMatter.getQuantity()); if (remainNum.compareTo(new BigDecimal("0")) >= 0) { subjectMatter.setDeliveredQuantity(subjectMatter.getQuantity()); subjectMatter.setRemainingQuantity(new BigDecimal("0")); subjectMatter.setActualDeliveryDate(DateUtil.today()); subjectMatter.setDeliveryStatus(2); subjectMatter.setLastDeliveredQuantity(subjectMatter.getQuantity()); contractSubjectMatterMapper.updateById(subjectMatter); currentOutMap.put(subjectMatter.getContractId(), subjectMatter); //新增合同出库历史 saveContractOutBound(contract, addDTO, subjectMatter, outBound.getId()); } else { BigDecimal currentOut = remainNum.add(subjectMatter.getQuantity()); subjectMatter.setDeliveredQuantity(subjectMatter.getQuantity().add(remainNum)); subjectMatter.setRemainingQuantity(remainNum.multiply(new BigDecimal("-1"))); subjectMatter.setActualDeliveryDate(DateUtil.today()); subjectMatter.setDeliveryStatus(1); subjectMatter.setLastDeliveredQuantity(currentOut); contractSubjectMatterMapper.updateById(subjectMatter); currentOutMap.put(subjectMatter.getContractId(), subjectMatter); //新增合同出库历史 saveContractOutBound(contract, addDTO, subjectMatter, outBound.getId()); break; } } } } }); //出库应收款明细 if (ArrayUtil.isNotEmpty(currentOutMap.keySet())) { for (Long contractId : currentOutMap.keySet()) { //当前出库合同出库标的物 List contractSubjectMatterList = contractSubjectMatterMapper.selectList(Wrappers.lambdaQuery() .eq(ContractSubjectMatter::getContractId, contractId).ne(ContractSubjectMatter::getDeliveryStatus, 0)); //查询合同是否有出库阶段 ContractPaymentSchedule schedule = scheduleMapper.selectOne(Wrappers.lambdaQuery().eq(ContractPaymentSchedule::getContractId, contractId) .eq(ContractPaymentSchedule::getStageName, "发货前")); //存在出库阶段 if (ObjUtil.isNotNull(schedule)) { //当前出库金额 BigDecimal outPirce = contractSubjectMatterList.stream() .map(item -> (item.getDeliveryStatus() == 1 ? item.getDeliveredQuantity().multiply(item.getUnitPrice()) : item.getQuantity().multiply(item.getUnitPrice()))) .reduce(BigDecimal.ZERO, BigDecimal::add); //合同金额 Contract contract = contractMapper.selectById(contractId); //出库是否完成 if (outPirce.compareTo(contract.getAmount()) < 0) { contract.setBillingStatus("1"); contractMapper.updateById(contract); } if (outPirce.compareTo(contract.getAmount()) == 0) { contract.setBillingStatus("2"); contractMapper.updateById(contract); } //计算发货前应收更新资金明细 PaymentConfirm paymentConfirm = new PaymentConfirm(); paymentConfirm.setBusinessType("发货前应收"); paymentConfirm.setBusGuestId(contract.getPartyAId()); paymentConfirm.setBusGuestName(contract.getPartyA()); paymentConfirm.setContractId(contract.getId()); paymentConfirm.setContractName(contract.getContractName()); paymentConfirm.setContractNo(contract.getContractNo()); paymentConfirm.setScheduleId(schedule.getId()); paymentConfirm.setScheduleName(schedule.getStageName()); paymentConfirm.setConfirmTime(addDTO.getOutBoundTime()); //按出库资金比例 计算发货前应收 paymentConfirm.setTransationAmount(outPirce.divide(contract.getAmount(), 10, RoundingMode.HALF_UP).multiply(schedule.getPlannedAmount())); paymentConfirm.setReceivableAmount(paymentConfirm.getTransationAmount()); //最近应收 PaymentConfirm lastConfirm = paymentConfirmMapper.selectOne(Wrappers.lambdaQuery().eq(PaymentConfirm::getContractId, contract.getId()) .orderByDesc(PaymentConfirm::getCreateTime).last("limit 1")); if (ObjUtil.isNotNull(lastConfirm)) { //已存在发货应收 if (StrUtil.equals(lastConfirm.getBusinessType(), paymentConfirm.getBusinessType())) { //查询之前所有发货前应收 List fhConfirmList = paymentConfirmMapper.selectList(Wrappers.lambdaQuery().eq(PaymentConfirm::getContractId, contract.getId()) .eq(PaymentConfirm::getScheduleId, schedule.getId()).orderByDesc(PaymentConfirm::getCreateTime)); BigDecimal fhTotalAmount = fhConfirmList.stream().map(item -> item.getTransationAmount()).reduce(BigDecimal.ZERO, BigDecimal::add); //本次发货前应收 出库总额 - 所有发货前应收 paymentConfirm.setTransationAmount(paymentConfirm.getTransationAmount().subtract(fhTotalAmount)); paymentConfirm.setReceivableAmount(paymentConfirm.getTransationAmount()); paymentConfirm.setTotalAmount(lastConfirm.getTotalAmount().subtract(paymentConfirm.getTransationAmount())); if (schedule.getPaymentRatio().compareTo(new BigDecimal("0")) > 0) { paymentConfirmMapper.insert(paymentConfirm); } } else { //不存在发货应收 出库总额 paymentConfirm.setTotalAmount(lastConfirm.getTotalAmount().subtract(paymentConfirm.getTransationAmount())); if (schedule.getPaymentRatio().compareTo(new BigDecimal("0")) > 0) { paymentConfirmMapper.insert(paymentConfirm); } } } else { //无之前阶段应收 paymentConfirm.setTotalAmount(paymentConfirm.getTransationAmount().multiply(new BigDecimal("-1"))); if (schedule.getPaymentRatio().compareTo(new BigDecimal("0")) > 0) { paymentConfirmMapper.insert(paymentConfirm); //客户付款完成合同 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 currentInSum = outAmountList.stream().reduce(BigDecimal.ZERO, BigDecimal::add); //新增当前合同 资金转入 PaymentConfirm inConfirm = BeanUtil.copyProperties(paymentConfirm, PaymentConfirm.class, "id", "transationAmount", "advanceAmount", "receivableAmount", "overdueAmount", "totalAmount"); inConfirm.setBusinessType("资金转入"); inConfirm.setConfirmTime(new Date()); inConfirm.setTransationAmount(currentInSum); inConfirm.setTotalAmount(paymentConfirm.getTotalAmount().add(currentInSum)); //更新当前阶段实际收款 和 收款状态 schedule.setActualAmount(inConfirm.getTransationAmount()); schedule.setPaymentStatus(2); 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"))); schedule.setActualAmount(paymentConfirm.getTotalAmount().subtract(currentInSum)); schedule.setPaymentStatus(1); } paymentConfirmMapper.insert(inConfirm); //更新当前阶段实际收款 和 收款状态 scheduleMapper.updateById(schedule); } } } } //更新发货前收款生效时间 if (ObjUtil.isNull(schedule.getEffectiveDate())) { schedule.setEffectiveEndDate(DateUtil.offsetDay(addDTO.getOutBoundTime(), schedule.getAgreedDays())); //查询是否有后续阶段 // List afterSchedule = scheduleMapper.selectList(Wrappers.lambdaQuery().eq(ContractPaymentSchedule::getContractId, contractId) // .gt(ContractPaymentSchedule::getStageOrder, schedule.getStageOrder())); // if (ArrayUtil.isEmpty(afterSchedule.toArray())) { // schedule.setEffectiveEndDate(contract.getExpirationDate()); // } scheduleMapper.updateById(schedule); //查询是否有之前阶段 // ContractPaymentSchedule beforeSchedule = scheduleMapper.selectOne(Wrappers.lambdaQuery().eq(ContractPaymentSchedule::getContractId, schedule.getContractId()) // .lt(ContractPaymentSchedule::getStageOrder, schedule.getStageOrder()).orderByDesc(ContractPaymentSchedule::getCreateTime).last("limit 1")); // if (ObjUtil.isNotNull(beforeSchedule)) { // beforeSchedule.setEffectiveEndDate(schedule.getEffectiveDate()); // scheduleMapper.updateById(beforeSchedule); // } //更新履约 ContractPaymentScheduleProcess contractPaymentScheduleProcess = new ContractPaymentScheduleProcess(); contractPaymentScheduleProcess.setContractId(schedule.getContractId()); contractPaymentScheduleProcess.setContractName(schedule.getContractName()); contractPaymentScheduleProcess.setScheduleId(schedule.getId()); contractPaymentScheduleProcess.setScheduleName(schedule.getStageName()); contractPaymentScheduleProcess.setProcessDate(addDTO.getOutBoundTime()); ContractPaymentScheduleProcess lastProcess = scheduleProcessMapper.selectOne(Wrappers.lambdaQuery().eq(ContractPaymentScheduleProcess::getContractId, schedule.getContractId()) .eq(ContractPaymentScheduleProcess::getScheduleId, schedule.getId()).last("limit 1")); if (ObjUtil.isNull(lastProcess)) { scheduleProcessMapper.insert(contractPaymentScheduleProcess); } } //查询是否有后续阶段 List afterSchedule = scheduleMapper.selectList(Wrappers.lambdaQuery() .eq(ContractPaymentSchedule::getContractId, schedule.getContractId()) .gt(ContractPaymentSchedule::getStageOrder, schedule.getStageOrder()) .orderByAsc(ContractPaymentSchedule::getCreateTime)); if (ArrayUtil.isNotEmpty(afterSchedule.toArray()) && afterSchedule.size() == 1) { //最后阶段生效时间 ContractPaymentSchedule endSchedule = afterSchedule.get(0); if (StrUtil.equals(endSchedule.getStageName(), "质保金")) { // endSchedule.setEffectiveEndDate(DateUtil.offsetDay(addDTO.getOutBoundTime(), endSchedule.getAgreedDays())); // scheduleMapper.updateById(endSchedule); //当前阶段生效时间 // schedule.setEffectiveEndDate(DateUtil.offsetDay(addDTO.getOutBoundTime(), schedule.getAgreedDays())); // scheduleMapper.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.getOutBoundTime()); 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()); // scheduleMapper.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(); } private void saveContractOutBound(Contract contract, OutBoundAddDTO addDTO, ContractSubjectMatter subjectMatter, Long outBoundId) { ContractOutBound contractOutBound = new ContractOutBound(); contractOutBound.setContractId(contract.getId()); contractOutBound.setContractName(contract.getContractName()); contractOutBound.setBusGuestId(contract.getPartyAId()); contractOutBound.setBusGuestName(contract.getPartyA()); contractOutBound.setSubjectMatterCode(subjectMatter.getMaterialCode()); contractOutBound.setSubjectMatterName(subjectMatter.getMaterialInternalName()); contractOutBound.setOutBoundTime(addDTO.getOutBoundTime()); contractOutBound.setOutBoundNum(subjectMatter.getLastDeliveredQuantity()); contractOutBound.setOutBoundAttNames(addDTO.getOutBoundAttNames()); contractOutBound.setOutBoundAttPaths(addDTO.getOutBoundAttPaths()); contractOutBound.setOutBoundId(outBoundId); contractOutBoundMapper.insert(contractOutBound); if (StrUtil.isEmpty(contract.getContractCategory())){ if (DateUtil.compare(addDTO.getOutBoundTime(),contract.getExpirationDate())>0){ saveOverdueOutBound(contract, addDTO, subjectMatter); } } } private void saveOverdueOutBound(Contract contract, OutBoundAddDTO addDTO, ContractSubjectMatter subjectMatter) { DeliveryOverdue overdue = new DeliveryOverdue(); overdue.setContractId(contract.getId()); overdue.setContractName(contract.getContractName()); overdue.setBusGuestId(contract.getPartyAId()); overdue.setBusGuestName(contract.getPartyA()); overdue.setMaterialName(subjectMatter.getMaterialName()); overdue.setMaterialInternalName(subjectMatter.getMaterialInternalName()); overdue.setMaterialCode(subjectMatter.getMaterialCode()); overdue.setOverdueNum(subjectMatter.getLastDeliveredQuantity()); overdue.setTotalAmount(subjectMatter.getUnitPrice().multiply(subjectMatter.getLastDeliveredQuantity())); overdue.setOverdueDuration(new BigDecimal(DateUtil.betweenDay(addDTO.getOutBoundTime(),contract.getExpirationDate(),true))); deliveryOverdueMapper.insert(overdue); } }