package com.ruoyi.sales.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum; import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; import com.ruoyi.framework.web.domain.R; import com.ruoyi.procurementrecord.utils.StockUtils; import com.ruoyi.production.mapper.*; import com.ruoyi.production.pojo.ProductionAccount; import com.ruoyi.production.pojo.ProductionOperationTask; import com.ruoyi.production.pojo.ProductionPlan; import com.ruoyi.production.pojo.ProductionProductMain; import com.ruoyi.purchase.mapper.PurchaseLedgerMapper; import com.ruoyi.purchase.pojo.PurchaseLedger; import com.ruoyi.quality.mapper.QualityInspectMapper; import com.ruoyi.quality.pojo.QualityInspect; import com.ruoyi.sales.dto.InvoiceRegistrationProductDto; import com.ruoyi.sales.dto.SalesLedgerProductDto; import com.ruoyi.sales.mapper.InvoiceRegistrationProductMapper; import com.ruoyi.sales.mapper.SalesLedgerMapper; import com.ruoyi.sales.mapper.SalesLedgerProductMapper; import com.ruoyi.sales.mapper.ShippingInfoMapper; import com.ruoyi.sales.pojo.SalesLedger; import com.ruoyi.sales.pojo.SalesLedgerProduct; import com.ruoyi.sales.pojo.ShippingInfo; import com.ruoyi.sales.service.ISalesLedgerProductService; import com.ruoyi.stock.mapper.StockInventoryMapper; import com.ruoyi.stock.pojo.StockInventory; import com.ruoyi.technology.bean.vo.TechnologyBomStructureVo; import com.ruoyi.technology.mapper.TechnologyBomStructureMapper; import com.ruoyi.technology.mapper.TechnologyRoutingMapper; import com.ruoyi.technology.pojo.TechnologyRouting; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import java.lang.reflect.Field; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDateTime; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; /** * 产品信息Service业务层处理 * * @author ruoyi * @date 2025-05-08 */ @Service @RequiredArgsConstructor public class SalesLedgerProductServiceImpl extends ServiceImpl implements ISalesLedgerProductService { private final SalesLedgerProductMapper salesLedgerProductMapper; private final ProductionAccountMapper productionAccountMapper; private final SalesLedgerMapper salesLedgerMapper; private final PurchaseLedgerMapper purchaseLedgerMapper; private final ProductionPlanMapper productionPlanMapper; private final ProductionOperationTaskMapper productionOperationTaskMapper; private final TechnologyRoutingMapper technologyRoutingMapper; private final TechnologyBomStructureMapper technologyBomStructureMapper; private final InvoiceRegistrationProductMapper invoiceRegistrationProductMapper; private final ProductionProductMainMapper productionProductMainMapper; private final ProductionProductOutputMapper productionProductOutputMapper; private final ProductionProductInputMapper productionProductInputMapper; private final QualityInspectMapper qualityInspectMapper; private final ShippingInfoMapper shippingInfoMapper; private final ShippingInfoServiceImpl shippingInfoService; private final StockUtils stockUtils; private final StockInventoryMapper stockInventoryMapper; @Override public SalesLedgerProduct selectSalesLedgerProductById(Long id) { return salesLedgerProductMapper.selectById(id); } @Override public List selectSalesLedgerProductList(SalesLedgerProduct salesLedgerProduct) { // LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); // queryWrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerProduct.getSalesLedgerId()) // .eq(SalesLedgerProduct::getType, salesLedgerProduct.getType()); List salesLedgerProducts = salesLedgerProductMapper.selectSalesLedgerProductList(salesLedgerProduct); if(!CollectionUtils.isEmpty(salesLedgerProducts)){ salesLedgerProducts.forEach(item -> { // 发货信息 ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper() .eq(ShippingInfo::getSalesLedgerProductId, item.getId()) .orderByDesc(ShippingInfo::getCreateTime) .last("limit 1")); if(shippingInfo != null){ item.setShippingDate(shippingInfo.getShippingDate()); item.setShippingCarNumber(shippingInfo.getShippingCarNumber()); item.setShippingStatus(shippingInfo.getStatus()); item.setExpressCompany(shippingInfo.getExpressCompany()); item.setExpressNumber(shippingInfo.getExpressNumber()); } }); // 开票 InvoiceRegistrationProductDto invoiceRegistrationProductDto = new InvoiceRegistrationProductDto(); invoiceRegistrationProductDto.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId().intValue()); List invoiceRegistrationProductDtoList = invoiceRegistrationProductMapper.invoiceRegistrationProductList(invoiceRegistrationProductDto); // 统计开票登记产品的已开票数/已开票金额 if (!CollectionUtils.isEmpty(invoiceRegistrationProductDtoList)) { for (SalesLedgerProduct ledgerProduct : salesLedgerProducts) { BigDecimal invoiceNum = BigDecimal.ZERO; BigDecimal invoiceAmount = BigDecimal.ZERO; BigDecimal noInvoiceNum = BigDecimal.ZERO; BigDecimal noInvoiceAmount = BigDecimal.ZERO; for (InvoiceRegistrationProductDto registrationProductDto : invoiceRegistrationProductDtoList) { if(ledgerProduct.getId().intValue() == registrationProductDto.getSalesLedgerProductId()){ invoiceNum = invoiceNum.add(registrationProductDto.getInvoiceNum()); invoiceAmount = invoiceAmount.add(registrationProductDto.getInvoiceAmount()); } } noInvoiceNum = ledgerProduct.getQuantity().subtract(invoiceNum); noInvoiceAmount = ledgerProduct.getTaxInclusiveTotalPrice().subtract(invoiceAmount); ledgerProduct.setInvoiceNum(invoiceNum); ledgerProduct.setInvoiceAmount(invoiceAmount); ledgerProduct.setNoInvoiceNum(noInvoiceNum); ledgerProduct.setNoInvoiceAmount(noInvoiceAmount); } } } return salesLedgerProducts; } @Override @Transactional(rollbackFor = Exception.class) public int deleteSalesLedgerProductByIds(Long[] ids) { if (ids == null || ids.length == 0) { return 0; } // 1. 先查询要删除的子表记录,获取对应的 salesLedgerId List deletedProducts = salesLedgerProductMapper.selectBatchIds(Arrays.asList(ids)); if (deletedProducts.isEmpty()) { return 0; // 没有可删除的数据 } //删除发货信息 List shippingInfos = shippingInfoMapper.selectList(new LambdaQueryWrapper() .in(ShippingInfo::getSalesLedgerProductId, Arrays.asList(ids))); if(!CollectionUtils.isEmpty(shippingInfos)){ shippingInfoService.delete(shippingInfos.stream().map(ShippingInfo::getId).collect(Collectors.toList())); } // 可能属于多个主表 Set mainIds = deletedProducts.stream() .map(SalesLedgerProduct::getSalesLedgerId) .filter(Objects::nonNull) .collect(Collectors.toSet()); // 2. 执行删除操作 int result = salesLedgerProductMapper.deleteBatchIds(Arrays.asList(ids)); //删除对应的生产订单 deleteProductionData(Arrays.asList(ids)); // 3. 对每个主表ID进行金额更新 for (Long salesLedgerId : mainIds) { LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId); List remainingProducts = salesLedgerProductMapper.selectList(wrapper); // 调用通用方法更新主表金额 updateMainContractAmount( salesLedgerId, remainingProducts, SalesLedgerProduct::getTaxInclusiveTotalPrice, salesLedgerMapper, SalesLedger.class ); } return result; } @Override @Transactional(rollbackFor = Exception.class) public int addOrUpdateSalesLedgerProduct(SalesLedgerProduct salesLedgerProduct) { // 待回款,付款 if(salesLedgerProduct.getType().equals(1)){ salesLedgerProduct.setPendingInvoiceTotal(salesLedgerProduct.getTaxInclusiveTotalPrice().subtract(salesLedgerProduct.getInvoiceTotal())); //未开票数量+金额 salesLedgerProduct.setNoInvoiceNum(salesLedgerProduct.getQuantity()); salesLedgerProduct.setNoInvoiceAmount(salesLedgerProduct.getTaxInclusiveTotalPrice()); }else{ salesLedgerProduct.setPendingTicketsTotal(salesLedgerProduct.getTaxInclusiveTotalPrice().subtract(salesLedgerProduct.getTicketsTotal())); // 未来票数量+金额 salesLedgerProduct.setFutureTickets(salesLedgerProduct.getQuantity()); salesLedgerProduct.setFutureTicketsAmount(salesLedgerProduct.getTaxInclusiveTotalPrice()); } int result; Long salesLedgerId = salesLedgerProduct.getSalesLedgerId(); if (salesLedgerProduct.getId() == null) { salesLedgerProduct.setRegisterDate(LocalDateTime.now()); result = salesLedgerProductMapper.insert(salesLedgerProduct); addProductionData(salesLedgerProduct); } else { //查询原本的产品型号id salesLedgerProduct.setFutureTickets(salesLedgerProduct.getQuantity()); result = salesLedgerProductMapper.updateById(salesLedgerProduct); /*删除对应的生产数据并重新新增*/ deleteProductionData(Arrays.asList(salesLedgerProduct.getId())); // 删除生产核算数据 addProductionData(salesLedgerProduct); } // 如果插入或更新成功,并且有 salesLedgerId,才继续更新主表金额 if (result > 0 && salesLedgerId != null) { // 查询该主表下的所有子表数据 LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); wrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId) .eq(SalesLedgerProduct::getType, salesLedgerProduct.getType()); List productList = salesLedgerProductMapper.selectList(wrapper); if (salesLedgerProduct.getType() == 1) { // 调用通用方法更新主表金额 updateMainContractAmount( salesLedgerId, productList, SalesLedgerProduct::getTaxInclusiveTotalPrice, salesLedgerMapper, SalesLedger.class ); } else { // 调用通用方法更新主表金额 updateMainContractAmount( salesLedgerId, productList, SalesLedgerProduct::getTaxInclusiveTotalPrice, purchaseLedgerMapper, PurchaseLedger.class ); } } return result; } /** * 新增生产数据 */ public void addProductionData(SalesLedgerProduct salesLedgerProduct) { if (!Integer.valueOf(1).equals(salesLedgerProduct.getType())) { return; } SalesLedger salesLedger = salesLedgerMapper.selectById(salesLedgerProduct.getSalesLedgerId()); ProductionPlan productionPlan = new ProductionPlan(); productionPlan.setApplyNo(buildSalesPlanApplyNo(salesLedgerProduct.getId())); productionPlan.setSource("sales"); productionPlan.setProductModelId(salesLedgerProduct.getProductModelId()); productionPlan.setQtyRequired(salesLedgerProduct.getQuantity()); productionPlan.setRemark("销售台账自动生成"); productionPlan.setIssued(Boolean.FALSE); productionPlan.setStatus(0); if (salesLedger != null) { productionPlan.setMpsNo(salesLedger.getSalesContractNo()); if (salesLedger.getDeliveryDate() != null) { productionPlan.setRequiredDate(salesLedger.getDeliveryDate().atStartOfDay()); productionPlan.setPromisedDeliveryDate(salesLedger.getDeliveryDate().atStartOfDay()); } } productionPlanMapper.insert(productionPlan); } /** * 删除生产数据 */ public void deleteProductionData(List productIds) { List applyNos = productIds.stream() .filter(Objects::nonNull) .map(this::buildSalesPlanApplyNo) .collect(Collectors.toList()); if (CollectionUtils.isEmpty(applyNos)) { return; } List productionPlans = productionPlanMapper.selectList( new LambdaQueryWrapper().in(ProductionPlan::getApplyNo, applyNos)); if (CollectionUtils.isEmpty(productionPlans)) { return; } boolean hasIssued = productionPlans.stream().anyMatch(item -> Boolean.TRUE.equals(item.getIssued()) || (item.getStatus() != null && item.getStatus() > 0)); if (hasIssued) { throw new ServiceException("对应生产计划已下发生成生产订单,请先处理生产计划/生产订单后再修改销售台账"); } productionPlanMapper.deleteByIds(productionPlans.stream().map(ProductionPlan::getId).collect(Collectors.toList())); } @Override public IPage listPage(Page page, SalesLedgerProductDto salesLedgerProduct) { IPage salesLedgerProductDtoIPage = salesLedgerProductMapper.listPage(page, salesLedgerProduct); salesLedgerProductDtoIPage.getRecords().forEach(item -> { // 判断状态 if(item.getTaxInclusiveTotalPrice().compareTo(item.getInvoiceTotal()) == 0){ item.setStatusName("已完成付款"); }else{ item.setStatusName("未完成付款"); } }); return salesLedgerProductDtoIPage; } @Override public IPage listPagePurchaseLedger(Page page, SalesLedgerProductDto salesLedgerProduct) { IPage salesLedgerProductDtoIPage = salesLedgerProductMapper.listPagePurchaseLedger(page, salesLedgerProduct); salesLedgerProductDtoIPage.getRecords().forEach(item -> { // 判断状态 if(item.getTaxInclusiveTotalPrice().compareTo(item.getTicketsTotal()) == 0){ item.setStatusName("已完成付款"); }else{ item.setStatusName("未完成付款"); } }); return salesLedgerProductDtoIPage; } /** * 通用方法:根据主表ID和子表列表,更新主表的合同金额 */ public void updateMainContractAmount( Long mainId, List subList, Function amountGetter, BaseMapper mainMapper, Class mainEntityClass) { if (mainId == null || subList == null || subList.isEmpty()) { return; } BigDecimal totalAmount = subList.stream() .map(amountGetter) .filter(Objects::nonNull) .reduce(BigDecimal.ZERO, BigDecimal::add); try { S entity = mainEntityClass.getDeclaredConstructor().newInstance(); Field idField = mainEntityClass.getDeclaredField("id"); idField.setAccessible(true); idField.set(entity, mainId); Field amountField = mainEntityClass.getDeclaredField("contractAmount"); amountField.setAccessible(true); amountField.set(entity, totalAmount); mainMapper.updateById(entity); } catch (Exception e) { throw new RuntimeException("动态更新主表金额失败", e); } } @Override public R judgmentInventory(SalesLedgerProduct salesLedgerProduct) { TechnologyRouting routing = technologyRoutingMapper.selectOne( new QueryWrapper().lambda() .eq(TechnologyRouting::getProductModelId, salesLedgerProduct.getProductModelId()) .orderByDesc(TechnologyRouting::getCreateTime) .last("LIMIT 1")); if (routing == null) { return R.fail("请先设置工艺路线"); } List structureList = technologyBomStructureMapper.listByBomId(routing.getBomId().longValue()); if (structureList == null || structureList.isEmpty()) { return R.fail("请先设置产品结构"); } int count = 0; StringBuilder stringBuffer = new StringBuilder(); for (TechnologyBomStructureVo structure : structureList) { if (structure.getParentId() == null) { continue; } StockInventory stockInventory = stockInventoryMapper.selectOne( new QueryWrapper().lambda().eq(StockInventory::getProductModelId, structure.getProductModelId())); if (stockInventory == null) { count++; stringBuffer.append(structure.getProductName()).append("-").append(structure.getModel()).append("库存不足").append(System.lineSeparator()); continue; } BigDecimal required = salesLedgerProduct.getQuantity().multiply(structure.getUnitQuantity() == null ? BigDecimal.ZERO : structure.getUnitQuantity()); BigDecimal remain = stockInventory.getQualitity() .subtract(stockInventory.getLockedQuantity()) .subtract(required) .divide(BigDecimal.ONE, 2, RoundingMode.CEILING); if (remain.compareTo(BigDecimal.ZERO) < 0) { count++; stringBuffer.append(structure.getProductName()) .append("-") .append(structure.getModel()) .append("库存不足,少") .append(remain.abs()) .append(System.lineSeparator()); } } if (count > 0) { return R.fail(stringBuffer.toString()); } return R.ok(); } private String buildSalesPlanApplyNo(Long salesLedgerProductId) { return "SALE_PRODUCT_" + salesLedgerProductId; } }