| | |
| | | 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 lombok.AllArgsConstructor; |
| | | 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; |
| | |
| | | * @date 2025-05-08 |
| | | */ |
| | | @Service |
| | | @AllArgsConstructor |
| | | @RequiredArgsConstructor |
| | | public class SalesLedgerProductServiceImpl extends ServiceImpl<SalesLedgerProductMapper, SalesLedgerProduct> implements ISalesLedgerProductService { |
| | | |
| | | private SalesLedgerProductMapper salesLedgerProductMapper; |
| | | |
| | | private SalesLedgerMapper salesLedgerMapper; |
| | | 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) { |
| | |
| | | |
| | | @Override |
| | | public List<SalesLedgerProduct> selectSalesLedgerProductList(SalesLedgerProduct salesLedgerProduct) { |
| | | LambdaQueryWrapper<SalesLedgerProduct> queryWrapper = new LambdaQueryWrapper<>(); |
| | | queryWrapper.eq(SalesLedgerProduct::getSalesLedgerId,salesLedgerProduct.getSalesLedgerId()); |
| | | return salesLedgerProductMapper.selectList(queryWrapper); |
| | | // LambdaQueryWrapper<SalesLedgerProduct> queryWrapper = new LambdaQueryWrapper<>(); |
| | | // queryWrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerProduct.getSalesLedgerId()) |
| | | // .eq(SalesLedgerProduct::getType, salesLedgerProduct.getType()); |
| | | List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectSalesLedgerProductList(salesLedgerProduct); |
| | | if(!CollectionUtils.isEmpty(salesLedgerProducts)){ |
| | | salesLedgerProducts.forEach(item -> { |
| | | // 发货信息 |
| | | ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>() |
| | | .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<InvoiceRegistrationProductDto> 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 |
| | |
| | | if (deletedProducts.isEmpty()) { |
| | | return 0; // 没有可删除的数据 |
| | | } |
| | | //删除发货信息 |
| | | List<ShippingInfo> shippingInfos = shippingInfoMapper.selectList(new LambdaQueryWrapper<ShippingInfo>() |
| | | .in(ShippingInfo::getSalesLedgerProductId, Arrays.asList(ids))); |
| | | if(!CollectionUtils.isEmpty(shippingInfos)){ |
| | | shippingInfoService.delete(shippingInfos.stream().map(ShippingInfo::getId).collect(Collectors.toList())); |
| | | } |
| | | |
| | | // 可能属于多个主表(但通常一个接口只处理一个主表) |
| | | // 可能属于多个主表 |
| | | Set<Long> mainIds = deletedProducts.stream() |
| | | .map(SalesLedgerProduct::getSalesLedgerId) |
| | | .filter(Objects::nonNull) |
| | |
| | | |
| | | // 2. 执行删除操作 |
| | | int result = salesLedgerProductMapper.deleteBatchIds(Arrays.asList(ids)); |
| | | //删除对应的生产订单 |
| | | deleteProductionData(Arrays.asList(ids)); |
| | | |
| | | // 3. 对每个主表ID进行金额更新 |
| | | for (Long salesLedgerId : mainIds) { |
| | |
| | | @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<SalesLedgerProduct> wrapper = new LambdaQueryWrapper<>(); |
| | | wrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId); |
| | | wrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId) |
| | | .eq(SalesLedgerProduct::getType, salesLedgerProduct.getType()); |
| | | List<SalesLedgerProduct> productList = salesLedgerProductMapper.selectList(wrapper); |
| | | |
| | | // 调用通用方法更新主表金额 |
| | | updateMainContractAmount( |
| | | salesLedgerId, |
| | | productList, |
| | | SalesLedgerProduct::getTaxInclusiveTotalPrice, |
| | | salesLedgerMapper, |
| | | SalesLedger.class |
| | | ); |
| | | 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<Long> productIds) { |
| | | List<String> applyNos = productIds.stream() |
| | | .filter(Objects::nonNull) |
| | | .map(this::buildSalesPlanApplyNo) |
| | | .collect(Collectors.toList()); |
| | | if (CollectionUtils.isEmpty(applyNos)) { |
| | | return; |
| | | } |
| | | List<ProductionPlan> productionPlans = productionPlanMapper.selectList( |
| | | new LambdaQueryWrapper<ProductionPlan>().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<SalesLedgerProductDto> listPage(Page page, SalesLedgerProductDto salesLedgerProduct) { |
| | | IPage<SalesLedgerProductDto> 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<SalesLedgerProductDto> listPagePurchaseLedger(Page page, SalesLedgerProductDto salesLedgerProduct) { |
| | | IPage<SalesLedgerProductDto> salesLedgerProductDtoIPage = salesLedgerProductMapper.listPagePurchaseLedger(page, salesLedgerProduct); |
| | | salesLedgerProductDtoIPage.getRecords().forEach(item -> { |
| | | // 判断状态 |
| | | if(item.getTaxInclusiveTotalPrice().compareTo(item.getTicketsTotal()) == 0){ |
| | | item.setStatusName("已完成付款"); |
| | | }else{ |
| | | item.setStatusName("未完成付款"); |
| | | } |
| | | }); |
| | | return salesLedgerProductDtoIPage; |
| | | } |
| | | |
| | | /** |
| | |
| | | throw new RuntimeException("动态更新主表金额失败", e); |
| | | } |
| | | } |
| | | @Override |
| | | public R judgmentInventory(SalesLedgerProduct salesLedgerProduct) { |
| | | TechnologyRouting routing = technologyRoutingMapper.selectOne( |
| | | new QueryWrapper<TechnologyRouting>().lambda() |
| | | .eq(TechnologyRouting::getProductModelId, salesLedgerProduct.getProductModelId()) |
| | | .orderByDesc(TechnologyRouting::getCreateTime) |
| | | .last("LIMIT 1")); |
| | | if (routing == null) { |
| | | return R.fail("请先设置工艺路线"); |
| | | } |
| | | List<TechnologyBomStructureVo> 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<StockInventory>().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; |
| | | } |
| | | } |