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.enums.StockOutQualifiedRecordTypeEnum;
|
import com.ruoyi.common.enums.StockInUnQualifiedRecordTypeEnum;
|
import com.ruoyi.framework.web.domain.R;
|
import com.ruoyi.procurementrecord.utils.StockUtils;
|
import com.ruoyi.production.mapper.*;
|
import com.ruoyi.production.pojo.*;
|
import com.ruoyi.production.service.ProductionOrderService;
|
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.AllArgsConstructor;
|
import org.springframework.beans.factory.annotation.Autowired;
|
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.LocalDate;
|
import java.time.LocalDateTime;
|
import java.time.format.DateTimeFormatter;
|
import java.util.*;
|
import java.util.function.Function;
|
import java.util.stream.Collectors;
|
|
/**
|
* 产品信息Service业务层处理
|
*
|
* @author ruoyi
|
* @date 2025-05-08
|
*/
|
@Service
|
@AllArgsConstructor
|
public class SalesLedgerProductServiceImpl extends ServiceImpl<SalesLedgerProductMapper, SalesLedgerProduct> implements ISalesLedgerProductService {
|
|
private SalesLedgerProductMapper salesLedgerProductMapper;
|
private ProductionAccountMapper productionAccountMapper;
|
|
private SalesLedgerMapper salesLedgerMapper;
|
|
private PurchaseLedgerMapper purchaseLedgerMapper;
|
|
private ProductionOrderMapper productionOrderMapper;
|
private ProductionOperationTaskMapper productionOperationTaskMapper;
|
private ProductionOrderService productionOrderService;
|
private TechnologyRoutingMapper technologyRoutingMapper;
|
private TechnologyBomStructureMapper technologyBomStructureMapper;
|
|
private InvoiceRegistrationProductMapper invoiceRegistrationProductMapper;
|
private ProductionProductMainMapper productionProductMainMapper;
|
private ProductionProductOutputMapper productionProductOutputMapper;
|
private ProductionProductInputMapper productionProductInputMapper;
|
private QualityInspectMapper qualityInspectMapper;
|
private ShippingInfoMapper shippingInfoMapper;
|
private ShippingInfoServiceImpl shippingInfoService;
|
|
private StockUtils stockUtils;
|
|
|
|
@Autowired
|
private StockInventoryMapper stockInventoryMapper;
|
@Override
|
public SalesLedgerProduct selectSalesLedgerProductById(Long id) {
|
return salesLedgerProductMapper.selectById(id);
|
}
|
|
@Override
|
public List<SalesLedgerProduct> selectSalesLedgerProductList(SalesLedgerProduct salesLedgerProduct) {
|
// 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
|
@Transactional(rollbackFor = Exception.class)
|
public int deleteSalesLedgerProductByIds(Long[] ids) {
|
if (ids == null || ids.length == 0) {
|
return 0;
|
}
|
|
// 1. 先查询要删除的子表记录,获取对应的 salesLedgerId
|
List<SalesLedgerProduct> deletedProducts = salesLedgerProductMapper.selectBatchIds(Arrays.asList(ids));
|
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)
|
.collect(Collectors.toSet());
|
|
// 2. 执行删除操作
|
int result = salesLedgerProductMapper.deleteBatchIds(Arrays.asList(ids));
|
//删除对应的生产订单
|
deleteProductionData(Arrays.asList(ids));
|
|
// 3. 对每个主表ID进行金额更新
|
for (Long salesLedgerId : mainIds) {
|
LambdaQueryWrapper<SalesLedgerProduct> wrapper = new LambdaQueryWrapper<>();
|
wrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId);
|
List<SalesLedgerProduct> 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<SalesLedgerProduct> wrapper = new LambdaQueryWrapper<>();
|
wrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerId)
|
.eq(SalesLedgerProduct::getType, salesLedgerProduct.getType());
|
List<SalesLedgerProduct> 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) {
|
ProductionOrder productionOrder = new ProductionOrder();
|
productionOrder.setSalesLedgerId(salesLedgerProduct.getSalesLedgerId());
|
productionOrder.setProductModelId(salesLedgerProduct.getProductModelId());
|
productionOrder.setSaleLedgerProductId(salesLedgerProduct.getId().intValue());
|
productionOrder.setNpsNo(generateNextOrderNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))));
|
productionOrder.setQuantity(salesLedgerProduct.getQuantity());
|
productionOrder.setCompleteQuantity(BigDecimal.ZERO);
|
|
TechnologyRouting routing = technologyRoutingMapper.selectOne(
|
new QueryWrapper<TechnologyRouting>().lambda()
|
.eq(TechnologyRouting::getProductModelId, salesLedgerProduct.getProductModelId())
|
.orderByDesc(TechnologyRouting::getCreateTime)
|
.last("LIMIT 1"));
|
if (routing != null) {
|
productionOrder.setTechnologyRoutingId(routing.getId());
|
}
|
productionOrderMapper.insert(productionOrder);
|
if (productionOrder.getTechnologyRoutingId() != null) {
|
productionOrderService.syncProductionOrderSnapshot(productionOrder.getId());
|
}
|
}
|
|
/**
|
* 删除生产数据
|
*/
|
public void deleteProductionData(List<Long> productIds) {
|
List<ProductionOrder> productionOrders = productionOrderMapper.selectList(
|
new LambdaQueryWrapper<ProductionOrder>()
|
.in(ProductionOrder::getSaleLedgerProductId, productIds.stream().map(Long::intValue).collect(Collectors.toList())));
|
if (org.springframework.util.CollectionUtils.isEmpty(productionOrders)) {
|
return;
|
}
|
List<Long> orderIds = productionOrders.stream().map(ProductionOrder::getId).collect(Collectors.toList());
|
List<Long> taskIds = productionOperationTaskMapper.selectList(
|
new LambdaQueryWrapper<ProductionOperationTask>()
|
.in(ProductionOperationTask::getProductionOrderId, orderIds))
|
.stream().map(ProductionOperationTask::getId).collect(Collectors.toList());
|
if (!taskIds.isEmpty()) {
|
List<ProductionProductMain> productMains = productionProductMainMapper.selectList(
|
new LambdaQueryWrapper<ProductionProductMain>()
|
.in(ProductionProductMain::getProductionOperationTaskId, taskIds));
|
List<Long> productMainIds = productMains.stream().map(ProductionProductMain::getId).collect(Collectors.toList());
|
if (!productMainIds.isEmpty()) {
|
List<QualityInspect> qualityInspects = qualityInspectMapper.selectList(
|
new LambdaQueryWrapper<QualityInspect>().in(QualityInspect::getProductMainId, productMainIds));
|
qualityInspects.forEach(qualityInspect -> {
|
if (qualityInspect.getInspectState() == 1) {
|
throw new RuntimeException("已提交的检验单不能删除");
|
}
|
});
|
productionProductOutputMapper.deleteByProductMainIds(productMainIds);
|
productionProductInputMapper.deleteByProductMainIds(productMainIds);
|
qualityInspectMapper.deleteByProductMainIds(productMainIds);
|
productionAccountMapper.delete(new LambdaQueryWrapper<ProductionAccount>()
|
.in(ProductionAccount::getProductionProductMainId, productMainIds));
|
for (Long productMainId : productMainIds) {
|
stockUtils.deleteStockOutRecord(productMainId, StockOutQualifiedRecordTypeEnum.PRODUCTION_REPORT_STOCK_OUT.getCode());
|
stockUtils.deleteStockInRecord(productMainId, StockInUnQualifiedRecordTypeEnum.PRODUCTION_SCRAP.getCode());
|
}
|
}
|
productionProductMainMapper.delete(new LambdaQueryWrapper<ProductionProductMain>()
|
.in(ProductionProductMain::getProductionOperationTaskId, taskIds));
|
}
|
productionOrderService.removeProductionOrder(orderIds);
|
}
|
|
@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;
|
}
|
|
/**
|
* 通用方法:根据主表ID和子表列表,更新主表的合同金额
|
*/
|
public <T, S> void updateMainContractAmount(
|
Long mainId,
|
List<T> subList,
|
Function<T, BigDecimal> amountGetter,
|
BaseMapper<S> mainMapper,
|
Class<S> 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<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 generateNextOrderNo(String datePrefix) {
|
QueryWrapper<ProductionOrder> queryWrapper = new QueryWrapper<>();
|
queryWrapper.likeRight("nps_no", "SC" + datePrefix);
|
queryWrapper.orderByDesc("nps_no");
|
queryWrapper.last("LIMIT 1");
|
ProductionOrder latestOrder = productionOrderMapper.selectOne(queryWrapper);
|
int sequence = 1;
|
if (latestOrder != null && latestOrder.getNpsNo() != null && !latestOrder.getNpsNo().isEmpty()) {
|
String sequenceStr = latestOrder.getNpsNo().substring(("SC" + datePrefix).length());
|
try {
|
sequence = Integer.parseInt(sequenceStr) + 1;
|
} catch (NumberFormatException e) {
|
sequence = 1;
|
}
|
}
|
return "SC" + datePrefix + String.format("%04d", sequence);
|
}
|
}
|