| | |
| | | package com.ruoyi.sales.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; |
| | |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | | import com.ruoyi.project.system.mapper.SysDeptMapper; |
| | | import com.ruoyi.project.system.mapper.SysUserMapper; |
| | | import com.ruoyi.purchase.dto.SimpleReturnOrderGroupDto; |
| | | import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper; |
| | | import com.ruoyi.quality.mapper.QualityInspectMapper; |
| | | import com.ruoyi.sales.dto.*; |
| | | import com.ruoyi.sales.mapper.CommonFileMapper; |
| | |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.sales.pojo.ShippingInfo; |
| | | import com.ruoyi.sales.service.ISalesLedgerService; |
| | | import com.ruoyi.sales.vo.FrameworkOrderVo; |
| | | import com.ruoyi.sales.vo.SalesLedgerVo; |
| | | import lombok.RequiredArgsConstructor; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | |
| | | import java.io.InputStream; |
| | | import java.lang.reflect.Field; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.time.LocalDate; |
| | |
| | | @Autowired |
| | | private ProductionProductMainService productionProductMainService; |
| | | @Autowired |
| | | private PurchaseReturnOrderProductsMapper purchaseReturnOrderProductsMapper; |
| | | ; |
| | | @Autowired |
| | | private SysUserMapper sysUserMapper; |
| | | |
| | | @Override |
| | |
| | | productWrapper.eq(SalesLedgerProduct::getType, type.getCode()); |
| | | List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(productWrapper); |
| | | if (type.equals(SaleEnum.PURCHASE)) { |
| | | // 查询退货信息 |
| | | List<Long> productIds = salesLedgerProducts.stream().map(SalesLedgerProduct::getProductModelId).collect(Collectors.toList()); |
| | | List<SimpleReturnOrderGroupDto> groupListByProductIds = new ArrayList<>(); |
| | | if(CollectionUtils.isNotEmpty(productIds)){ |
| | | groupListByProductIds = purchaseReturnOrderProductsMapper.getReturnOrderGroupListByProductIds(productIds); |
| | | } |
| | | Map<Long, BigDecimal> returnOrderGroupDtoMap = groupListByProductIds.stream().collect(Collectors.toMap(SimpleReturnOrderGroupDto::getProductModelId, SimpleReturnOrderGroupDto::getSumReturnQuantity)); |
| | | salesLedgerProducts.forEach(item -> { |
| | | BigDecimal returnQuality = returnOrderGroupDtoMap.getOrDefault(item.getProductModelId(), BigDecimal.ZERO); |
| | | item.setReturnQuality(returnQuality); |
| | | item.setAvailableQuality(item.getQuantity().subtract(returnQuality)); |
| | | BigDecimal returnQuantity = item.getReturnQuantity() == null ? BigDecimal.ZERO : item.getReturnQuantity(); |
| | | item.setReturnQuantity(returnQuantity); |
| | | BigDecimal quantity = item.getQuantity() == null ? BigDecimal.ZERO : item.getQuantity(); |
| | | item.setAvailableQuality(quantity.subtract(returnQuantity)); |
| | | }); |
| | | } |
| | | return salesLedgerProducts; |
| | |
| | | salesLedger.setContractAmount(salesLedgerProductImportDtos.stream() |
| | | .map(SalesLedgerProductImportDto::getTaxInclusiveTotalPrice) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add)); |
| | | salesLedger.setNetContractAmount(salesLedger.getContractAmount()); |
| | | salesLedgerMapper.insert(salesLedger); |
| | | |
| | | |
| | |
| | | salesLedgerProduct.setApproveStatus(0); |
| | | salesLedgerProduct.setIsProduction(salesLedgerProductImportDto.getIsProduction() == 1); |
| | | salesLedgerProductMapper.insert(salesLedgerProduct); |
| | | // 添加生产数据 |
| | | salesLedgerProductServiceImpl.addProductionData(salesLedgerProduct); |
| | | addProductionDataIfNeeded(salesLedgerProduct); |
| | | } |
| | | } |
| | | |
| | |
| | | if (CollectionUtils.isEmpty(idList)) { |
| | | return 0; |
| | | } |
| | | long childCount = salesLedgerMapper.selectCount(new LambdaQueryWrapper<SalesLedger>() |
| | | .in(SalesLedger::getParentContractId, idList)); |
| | | if (childCount > 0) { |
| | | throw new BaseException("框架合同存在框架订单,不能删除"); |
| | | } |
| | | // 删除销售管理数据 |
| | | LambdaQueryWrapper<SalesLedgerProduct> queryWrapper = new LambdaQueryWrapper<>(); |
| | | queryWrapper.in(SalesLedgerProduct::getSalesLedgerId, idList) |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public int addOrUpdateSalesLedger(SalesLedgerDto salesLedgerDto) { |
| | | log.info("addOrUpdateSalesLedger 收到 contractType={}, parentContractId={}, id={}", |
| | | salesLedgerDto == null ? null : salesLedgerDto.getContractType(), |
| | | salesLedgerDto == null ? null : salesLedgerDto.getParentContractId(), |
| | | salesLedgerDto == null ? null : salesLedgerDto.getId()); |
| | | // 1. 校验客户信息 |
| | | Customer customer = customerMapper.selectById(salesLedgerDto.getCustomerId()); |
| | | if (customer == null) { |
| | |
| | | |
| | | // 2. DTO转Entity |
| | | SalesLedger salesLedger = convertToEntity(salesLedgerDto); |
| | | log.info("convertToEntity 后 contractType={}", salesLedger.getContractType()); |
| | | salesLedger.setCustomerName(customer.getCustomerName()); |
| | | salesLedger.setTenantId(customer.getTenantId()); |
| | | // 录入人:如果未传则使用当前登录用户 |
| | | if (StringUtils.isEmpty(salesLedger.getEntryPerson())) { |
| | | salesLedger.setEntryPerson(String.valueOf(SecurityUtils.getUserId())); |
| | | } |
| | | // 3. 新增或更新主表 |
| | | if (salesLedger.getId() == null) { |
| | | String contractNo = salesLedger.getSalesContractNo(); |
| | | if (StringUtils.isEmpty(contractNo)) { |
| | | contractNo = generateSalesContractNo(); |
| | | contractNo = generateSalesContractNo(salesLedgerDto.getEntryDate()); |
| | | } |
| | | salesLedger.setSalesContractNo(contractNo); |
| | | salesLedgerMapper.insert(salesLedger); |
| | | } else { |
| | | salesLedgerMapper.updateById(salesLedger); |
| | | } |
| | | // 诊断:insert/update 后回查数据库实际写入的 contract_type |
| | | SalesLedger dbCheckLedger = salesLedgerMapper.selectById(salesLedger.getId()); |
| | | log.info("DB 实际 contract_type={}, id={}", dbCheckLedger == null ? null : dbCheckLedger.getContractType(), salesLedger.getId()); |
| | | |
| | | |
| | | // 4. 处理子表数据 |
| | |
| | | ); |
| | | } |
| | | |
| | | // 5. 保存销售台账附件 |
| | | // 5. 兜底:确保实际合同金额有初始值等于合同金额 |
| | | if (salesLedger.getNetContractAmount() == null) { |
| | | SalesLedger dbLedger = salesLedgerMapper.selectById(salesLedger.getId()); |
| | | if (dbLedger != null && dbLedger.getNetContractAmount() == null && dbLedger.getContractAmount() != null) { |
| | | SalesLedger updateEntity = new SalesLedger(); |
| | | updateEntity.setId(salesLedger.getId()); |
| | | updateEntity.setNetContractAmount(dbLedger.getContractAmount()); |
| | | salesLedgerMapper.updateById(updateEntity); |
| | | } |
| | | } |
| | | |
| | | // 6. 保存销售台账附件 |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, salesLedger.getId(), salesLedgerDto.getStorageBlobDTOs()); |
| | | |
| | | return 1; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public int createFrameworkOrder(SalesLedgerDto dto) { |
| | | if (dto == null || dto.getId() == null) { |
| | | throw new BaseException("请选择框架合同"); |
| | | } |
| | | SalesLedger parent = salesLedgerMapper.selectById(dto.getId()); |
| | | if (parent == null || !Objects.equals(parent.getContractType(), 2)) { |
| | | throw new BaseException("父台账不是框架合同"); |
| | | } |
| | | if (CollectionUtils.isEmpty(dto.getProductData())) { |
| | | throw new BaseException("请填写框架订单产品"); |
| | | } |
| | | SalesLedger order = new SalesLedger(); |
| | | order.setCustomerContractNo(parent.getCustomerContractNo()); |
| | | order.setProjectName(parent.getProjectName()); |
| | | order.setSalesman(parent.getSalesman()); |
| | | order.setCustomerId(parent.getCustomerId()); |
| | | order.setCustomerName(parent.getCustomerName()); |
| | | order.setEntryPerson(String.valueOf(SecurityUtils.getUserId())); |
| | | order.setRemarks(dto.getRemarks()); |
| | | order.setTenantId(parent.getTenantId()); |
| | | order.setExecutionDate(parent.getExecutionDate()); |
| | | order.setPaymentMethod(parent.getPaymentMethod()); |
| | | order.setEntryDate(parent.getEntryDate()); |
| | | order.setId(null); |
| | | order.setContractType(3); |
| | | order.setParentContractId(parent.getId()); |
| | | order.setSalesContractNo(generateSalesContractNo(new Date())); |
| | | order.setDeliveryDate(null); |
| | | order.setContractAmount(BigDecimal.ZERO); |
| | | order.setNetContractAmount(BigDecimal.ZERO); |
| | | salesLedgerMapper.insert(order); |
| | | |
| | | Set<Long> parentProductIds = dto.getProductData().stream() |
| | | .map(SalesLedgerProduct::getParentProductId) |
| | | .collect(Collectors.toSet()); |
| | | if (parentProductIds.contains(null) || parentProductIds.size() != dto.getProductData().size()) { |
| | | throw new BaseException("框架订单产品必须关联不同的父产品"); |
| | | } |
| | | Map<Long, SalesLedgerProduct> parentProducts = salesLedgerProductMapper.selectBatchIds(parentProductIds).stream() |
| | | .collect(Collectors.toMap(SalesLedgerProduct::getId, Function.identity())); |
| | | for (SalesLedgerProduct requestProduct : dto.getProductData()) { |
| | | if (requestProduct.getDeliveryDate() == null) { |
| | | throw new BaseException("框架订单产品必须关联父产品并填写交货日期"); |
| | | } |
| | | SalesLedgerProduct parentProduct = parentProducts.get(requestProduct.getParentProductId()); |
| | | if (parentProduct == null || !Objects.equals(parentProduct.getSalesLedgerId(), parent.getId()) |
| | | || !Objects.equals(parentProduct.getType(), SaleEnum.SALE.getCode())) { |
| | | throw new BaseException("框架订单产品不属于该框架合同"); |
| | | } |
| | | SalesLedgerProduct product = new SalesLedgerProduct(); |
| | | BeanUtils.copyProperties(parentProduct, product); |
| | | product.setId(null); |
| | | product.setSalesLedgerId(order.getId()); |
| | | product.setParentProductId(parentProduct.getId()); |
| | | product.setQuantity(null); |
| | | product.setDeliveryDate(requestProduct.getDeliveryDate()); |
| | | product.setIsProduction(false); |
| | | product.setType(SaleEnum.SALE.getCode()); |
| | | if (product.getTaxRate() == null) { |
| | | product.setTaxRate(BigDecimal.ZERO); |
| | | } |
| | | salesLedgerProductMapper.insert(product); |
| | | // 框架订单创建时生成生产计划(数量留空,下发时填写) |
| | | salesLedgerProductServiceImpl.addProductionData(product); |
| | | } |
| | | return 1; |
| | | } |
| | | |
| | | @Override |
| | | public List<FrameworkOrderVo> listFrameworkOrders(Long parentContractId) { |
| | | return salesLedgerMapper.listFrameworkOrders(parentContractId); |
| | | } |
| | | |
| | | @Override |
| | | public BigDecimal getFrameworkContractShippedQuantity(Long parentContractId) { |
| | | return salesLedgerMapper.sumShippedByParentContract(parentContractId); |
| | | } |
| | | |
| | | @Override |
| | |
| | | if (!insertList.isEmpty()) { |
| | | for (SalesLedgerProduct salesLedgerProduct : insertList) { |
| | | salesLedgerProduct.setType(type.getCode()); |
| | | if (salesLedgerProduct.getTaxRate() == null) { |
| | | salesLedgerProduct.setTaxRate(BigDecimal.ZERO); |
| | | } |
| | | salesLedgerProductMapper.insert(salesLedgerProduct); |
| | | // 添加生产数据 |
| | | salesLedgerProductServiceImpl.addProductionData(salesLedgerProduct); |
| | | addProductionDataIfNeeded(salesLedgerProduct); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void addProductionDataIfNeeded(SalesLedgerProduct product) { |
| | | SalesLedger ledger = salesLedgerMapper.selectById(product.getSalesLedgerId()); |
| | | if (ledger != null && !Objects.equals(ledger.getContractType(), 2) |
| | | && !Objects.equals(ledger.getContractType(), 3)) { |
| | | salesLedgerProductServiceImpl.addProductionData(product); |
| | | } |
| | | } |
| | | |
| | |
| | | } |
| | | |
| | | @Transactional(readOnly = true) |
| | | public String generateSalesContractNo() { |
| | | LocalDate currentDate = LocalDate.now(); |
| | | public String generateSalesContractNo(Date entryDate) { |
| | | LocalDate currentDate = entryDate != null ? DateUtils.toLocalDate(entryDate) : LocalDate.now(); |
| | | String datePart = currentDate.format(DateTimeFormatter.BASIC_ISO_DATE); |
| | | String lockKey = LOCK_PREFIX + datePart; |
| | | String lockValue = Thread.currentThread().getId() + "-" + System.nanoTime(); // 唯一标识锁持有者 |
| | |
| | | .filter(Objects::nonNull) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | |
| | | // 构造主表更新对象 |
| | | // 仅更新金额列,避免整实体 updateById 把其它非空字段(如 contract_type 默认值 1)一并覆盖 |
| | | UpdateWrapper<S> wrapper = Wrappers.update(); |
| | | wrapper.eq("id", mainId) |
| | | .set("contract_amount", totalAmount); |
| | | // 实际合同金额初始值等于合同金额 |
| | | boolean hasNetAmount; |
| | | 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); |
| | | mainEntityClass.getDeclaredField("netContractAmount"); |
| | | hasNetAmount = true; |
| | | } catch (NoSuchFieldException e) { |
| | | hasNetAmount = false; |
| | | } |
| | | if (hasNetAmount) { |
| | | wrapper.set("net_contract_amount", totalAmount); |
| | | } |
| | | mainMapper.update(null, wrapper); |
| | | } |
| | | |
| | | // 内部类用于存储聚合结果 |