feat(sales): 添加每件数量字段支持及多业务流程优化
- 在销售台账产品中新增 singleQuantity 字段,用于计算实际发货数量
- 在销售报价产品中新增 singleQuantity 字段,完善报价管理功能
- 优化发货流程中的数量计算逻辑,支持数量乘以每件数量的转换
- 实现销售报价和发货流程的自动审批处理机制
- 添加发货明细验证,确保发货明细不能为空
- 更新数据库查询和更新逻辑以支持新的数量计算方式
- 移除审批流程中的冗余审批人ID字段传递
- 重构审核流程中的无审核人处理逻辑,支持多种业务类型
- 清理销售台账控制器中的冗余代码和未使用的变量
- 创建详细的前端联调文档说明字段变更和接口调整内容
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # Sales 模åãæ¯ä»¶æ°éãå端èè°ææ¡£ |
| | | |
| | | ## 1. åæ´èå´ |
| | | |
| | | - éå®å°è´¦ï¼éå®äº§åæ°å¢å段 `singleQuantity`ï¼æ¯ä»¶æ°éï¼ |
| | | - é宿¥ä»·ï¼æ¥ä»·äº§åæ°å¢å段 `singleQuantity`ï¼æ¯ä»¶æ°éï¼ |
| | | - åè´§/åºåºæ°é计ç®ï¼æ `æ°é * æ¯ä»¶æ°é` 计ç®å¹¶è½åº |
| | | |
| | | ## 1.1 èè°åç½®ï¼æ°æ®åºï¼ |
| | | |
| | | éä¿è¯ä»¥ä¸ä¸¤å¼ 表已æ°å¢å `single_quantity`ï¼å»ºè®®é»è®¤å¼ `1`ï¼ï¼ |
| | | |
| | | - `sales_ledger_product.single_quantity` |
| | | - `sales_quotation_product.single_quantity` |
| | | |
| | | ## 2. åæ®µå®ä¹ |
| | | |
| | | - åæ®µåï¼`singleQuantity` |
| | | - å«ä¹ï¼æ¯ä»¶æ°é |
| | | - ç±»åï¼`number`ï¼å端 `BigDecimal`ï¼ |
| | | - é»è®¤è§åï¼æªä¼ ã为 `null`ãæ `<= 0` æ¶ï¼å端æ `1` å¤ç |
| | | |
| | | ## 3. æ¥å£åæ´æ¸
å |
| | | |
| | | ### 3.1 éå®å°è´¦ |
| | | |
| | | 1. `POST /sales/ledger/addOrUpdateSalesLedger` |
| | | - å
¥å `productData[]` æ°å¢å段 `singleQuantity` |
| | | 2. `POST /sales/product/addOrUpdateSalesLedgerProduct` |
| | | - å
¥åæ°å¢å段 `singleQuantity` |
| | | 3. `GET /sales/product/list` |
| | | - è¿ååæ®µæ°å¢ `singleQuantity` |
| | | - `noQuantity`ï¼å¾
åè´§æ°éï¼è®¡ç®æ¹ä¸ºï¼ |
| | | - `noQuantity = quantity * singleQuantity - shippedQuantity` |
| | | - åè´§ç¶æè®¡ç®åæ¥åºäºä¸é¢å
¬å¼ |
| | | |
| | | ### 3.2 é宿¥ä»· |
| | | |
| | | 1. `POST /sales/quotation/add` |
| | | - å
¥å `products[]` æ°å¢å段 `singleQuantity` |
| | | 2. `POST /sales/quotation/update` |
| | | - å
¥å `products[]` æ°å¢å段 `singleQuantity` |
| | | 3. `GET /sales/quotation/list` |
| | | - è¿å `products[]` æ°å¢å段 `singleQuantity` |
| | | |
| | | ### 3.3 åè´§ä¸åºåº |
| | | |
| | | 1. `POST /shippingInfo/add` |
| | | - åç«¯ä¼æéå®äº§å `singleQuantity` èªå¨æ¢ç®åè´§æç»æ°éï¼ |
| | | - `shipping_product_detail.quantity = åç«¯ä¼ å
¥quantity * singleQuantity` |
| | | - åºåºè®°å½æ°éä¸åè´§æç»ä¸è´ï¼åæ ·ä¸ºä¹ç§¯åçæ°éï¼ |
| | | |
| | | ## 4. å端èè°è¦æ± |
| | | |
| | | 1. éå®å°è´¦äº§åè¡ãé宿¥ä»·äº§åè¡æ°å¢ãæ¯ä»¶æ°éãè¾å
¥é¡¹ï¼å段å `singleQuantity` |
| | | 2. `singleQuantity` 建议éå¶ä¸ºå¤§äº `0` çæ°å |
| | | 3. åè´§æäº¤æµç¨ä¸ï¼`batchNoDetailList[].quantity` ä¼ âä»¶æ°âï¼ä¸è¦å¨å端åä¹ `singleQuantity` |
| | | - åå ï¼å端已ç»ä¸æ¢ç®ï¼å端åä¹ä¼å¯¼è´é夿¾å¤§ |
| | | 4. å表å±ç¤ºå¾
åè´§æ°éæ¶ï¼ç´æ¥ä½¿ç¨æ¥å£è¿åç `noQuantity` |
| | | |
| | | ## 5. èè°ç¤ºä¾ |
| | | |
| | | ### 5.1 éå®å°è´¦æ°å¢/ç¼è¾ï¼äº§åçæ®µï¼ |
| | | |
| | | ```json |
| | | { |
| | | "productData": [ |
| | | { |
| | | "productModelId": 101, |
| | | "quantity": 10, |
| | | "singleQuantity": 12 |
| | | } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ### 5.2 é宿¥ä»·æ°å¢/ç¼è¾ï¼äº§åçæ®µï¼ |
| | | |
| | | ```json |
| | | { |
| | | "products": [ |
| | | { |
| | | "productModelId": 101, |
| | | "quantity": 10, |
| | | "singleQuantity": 12 |
| | | } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ### 5.3 åè´§æäº¤ï¼æç»çæ®µï¼ |
| | | |
| | | ```json |
| | | { |
| | | "salesLedgerProductId": 2001, |
| | | "batchNoDetailList": [ |
| | | { |
| | | "productModelId": 101, |
| | | "batchNo": "B20260522001", |
| | | "quantity": 3 |
| | | } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | 说æï¼è¥è¯¥ `salesLedgerProductId` å¯¹åº `singleQuantity=12`ï¼åå®é
åè´§/åºåºæ°é为 `36`ã |
| | |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | |
| | | import com.ruoyi.purchase.pojo.PurchaseLedger; |
| | | import com.ruoyi.sales.mapper.CommonFileMapper; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.mapper.SalesQuotationMapper; |
| | | import com.ruoyi.sales.mapper.ShippingInfoMapper; |
| | | import com.ruoyi.sales.pojo.CommonFile; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.sales.pojo.SalesQuotation; |
| | | import com.ruoyi.sales.pojo.ShippingInfo; |
| | | import com.ruoyi.sales.service.impl.CommonFileServiceImpl; |
| | | import lombok.RequiredArgsConstructor; |
| | |
| | | private final ISysNoticeService sysNoticeService; |
| | | private final PurchaseLedgerMapper purchaseLedgerMapper; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private final SalesQuotationMapper salesQuotationMapper; |
| | | private final StockUtils stockUtils; |
| | | private final ShippingInfoMapper shippingInfoMapper; |
| | | private final ApproveNodeMapper approveNodeMapper; |
| | |
| | | .collect(Collectors.toList()); |
| | | // æ å®¡æ ¸äººé»è¾æ·»å |
| | | if (CollectionUtils.isEmpty(nodeIds)) { |
| | | autoPassPurchaseApproveIfNoApprover(approveProcessVO); // éè´åæ å®¡æ ¸äººé»è¾ |
| | | switch (approveProcessVO.getApproveType()){ |
| | | case 5: |
| | | autoPassPurchaseApproveIfNoApprover(approveProcessVO); // éè´åæ å®¡æ ¸äººé»è¾ |
| | | break; |
| | | case 6: |
| | | autoPassSalesApproveIfNoApprover(approveProcessVO); // é宿¥ä»·æ å®¡æ ¸äººé»è¾ |
| | | break; |
| | | case 7: |
| | | autoShippingApproveIfNoApprover(approveProcessVO); // éå®åè´§æ å®¡æ ¸äººé»è¾ |
| | | break; |
| | | } |
| | | return; |
| | | } |
| | | List<SysUser> sysUsers = sysUserMapper.selectUserByIds(nodeIds); |
| | |
| | | } |
| | | } |
| | | |
| | | private void autoPassSalesApproveIfNoApprover(ApproveProcessVO approveProcessVO) { |
| | | if (!StringUtils.hasText(approveProcessVO.getApproveReason())) { |
| | | return; |
| | | } |
| | | salesQuotationMapper.update(null, new LambdaUpdateWrapper<SalesQuotation>() |
| | | .eq(SalesQuotation::getQuotationNo, approveProcessVO.getApproveReason()) |
| | | .set(SalesQuotation::getStatus, "éè¿")); |
| | | } |
| | | |
| | | private void autoShippingApproveIfNoApprover(ApproveProcessVO approveProcessVO) { |
| | | if (!StringUtils.hasText(approveProcessVO.getApproveReason())) { |
| | | return; |
| | | } |
| | | ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>() |
| | | .eq(ShippingInfo::getShippingNo, approveProcessVO.getApproveReason()) |
| | | .last("limit 1")); |
| | | if(shippingInfo == null){ |
| | | return; |
| | | } |
| | | shippingInfoMapper.update(null, new LambdaUpdateWrapper<ShippingInfo>() |
| | | .eq(ShippingInfo::getShippingNo, approveProcessVO.getApproveReason()) |
| | | .set(ShippingInfo::getStatus, "å®¡æ ¸éè¿")); |
| | | //æ´æ¹åºåºå®¡æ ¸ç¶æï¼å¾
ç¡®è®¤æ¹æå¾
å®¡æ ¸ï¼ |
| | | stockUtils.shipmentStatus(StockOutQualifiedRecordTypeEnum.SALE_SHIP_STOCK_OUT.getCode(), shippingInfo.getId()); |
| | | } |
| | | |
| | | private void autoPassPurchaseApproveIfNoApprover(ApproveProcessVO approveProcessVO) { |
| | | if (!Objects.equals(approveProcessVO.getApproveType(), 5) |
| | | || !StringUtils.hasText(approveProcessVO.getApproveReason())) { |
| | | throw new RuntimeException("å®¡æ ¸ç¨æ·ä¸åå¨"); |
| | | if (!StringUtils.hasText(approveProcessVO.getApproveReason())) { |
| | | return; |
| | | } |
| | | |
| | | purchaseLedgerMapper.update(null, new LambdaUpdateWrapper<PurchaseLedger>() |
| | |
| | | @GetMapping("/listPage") |
| | | public IPage<SalesLedgerVo> listPage(Page page, SalesLedgerDto salesLedgerDto) { |
| | | IPage<SalesLedgerVo> iPage = salesLedgerService.selectSalesLedgerListPage(page, salesLedgerDto); |
| | | |
| | | // æ¥è¯¢ç»æä¸ºç©º,ç´æ¥è¿å |
| | | if (CollectionUtils.isEmpty(iPage.getRecords())) { |
| | | return iPage; |
| | | } |
| | | |
| | | // è·åå½å页ææå°è´¦è®°å½ç ID éå |
| | | List<Long> salesLedgerIds = iPage.getRecords().stream().map(SalesLedger::getId).collect(Collectors.toList()); |
| | | |
| | | |
| | | |
| | | |
| | | // 转æ¢åæ¬¾æ°æ®, key 为å°è´¦ID, value 为该å°è´¦çæ»å款éé¢ |
| | | Map<Long, BigDecimal> receiptTotals = new HashMap<>(); |
| | | |
| | | |
| | | for (SalesLedgerVo salesLedgerVo : iPage.getRecords()) { |
| | | Long ledgerId = salesLedgerVo.getId(); |
| | | // ååæ»éé¢ |
| | | BigDecimal contractAmount = salesLedgerVo.getContractAmount() == null ? BigDecimal.ZERO : salesLedgerVo.getContractAmount(); |
| | | // å¼ç¥¨æ»é¢å忬¾æ»é¢ |
| | | BigDecimal receiptPaymentAmountTotal = receiptTotals.getOrDefault(ledgerId, BigDecimal.ZERO); |
| | | |
| | | // å¦æå·²ç»æè¿å¼ç¥¨æå款æä½,åä¸å
许ç¼è¾ |
| | | boolean hasReceiptOperation = receiptPaymentAmountTotal.compareTo(BigDecimal.ZERO) > 0; |
| | | salesLedgerVo.setIsEdit(hasReceiptOperation); |
| | | |
| | | salesLedgerVo.setStorageBlobVOs(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, ledgerId)); |
| | | salesLedgerVo.setStorageBlobVOs(fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.FILE, RecordTypeEnum.SALES_LEDGER, salesLedgerVo.getId())); |
| | | } |
| | | |
| | | if (ObjectUtils.isNotEmpty(salesLedgerDto.getStatus())) { |
| | | if (salesLedgerDto.getStatus()) { |
| | | iPage.setTotal(iPage.getRecords().size()); |
| | | } |
| | | } |
| | | |
| | | return iPage; |
| | | } |
| | | |
| | |
| | | public AjaxResult add(@RequestBody ShippingInfoDto req) throws Exception { |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | String sh = OrderUtils.countTodayByCreateTime(shippingInfoMapper, "SH","shipping_no"); |
| | | // æ·»å åè´§æ¶æ¯ |
| | | req.setShippingNo(sh); |
| | | req.setStatus("å¾
å®¡æ ¸"); |
| | | boolean save = shippingInfoService.add(req); |
| | | // åè´§å®¡æ¹ |
| | | ApproveProcessVO approveProcessVO = new ApproveProcessVO(); |
| | | approveProcessVO.setApproveType(7); |
| | | approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId()); |
| | | approveProcessVO.setApproveReason(sh);//åè´§ç¼å· |
| | | approveProcessVO.setApproveUserIds(req.getApproveUserIds()); |
| | | approveProcessVO.setApproveUser(loginUser.getUserId()); |
| | | approveProcessVO.setApproveTime(LocalDate.now().toString()); |
| | | approveProcessService.addApprove(approveProcessVO); |
| | | // æ·»å åè´§æ¶æ¯ |
| | | req.setShippingNo(sh); |
| | | req.setStatus("å¾
å®¡æ ¸"); |
| | | boolean save = shippingInfoService.add(req); |
| | | return save ? AjaxResult.success() : AjaxResult.error(); |
| | | } |
| | | |
| | |
| | | private BigDecimal quantity; |
| | | |
| | | /** |
| | | * æ¯ä»¶æ°é |
| | | */ |
| | | @Excel(name = "æ¯ä»¶æ°é") |
| | | private BigDecimal singleQuantity; |
| | | |
| | | /** |
| | | * ç¨ç |
| | | */ |
| | | @Excel(name = "ç¨ç") |
| | |
| | | public class SalesQuotationDto extends SalesQuotation { |
| | | @Schema(description = "æ¥ä»·åå") |
| | | private List<SalesQuotationProduct> products; |
| | | |
| | | /** |
| | | * 审æ¹äººidå表 |
| | | */ |
| | | // 审æ¹äºº |
| | | private String approveUserIds; |
| | | } |
| | |
| | | @Data |
| | | public class ShippingInfoDto extends ShippingInfo { |
| | | |
| | | /** |
| | | * 审æ¹äººidå表 |
| | | */ |
| | | // 审æ¹äºº |
| | | private String approveUserIds; |
| | | |
| | | private String type; // åè´§ç±»å |
| | | |
| | |
| | | */ |
| | | @Excel(name = "æ°é") |
| | | private BigDecimal quantity; |
| | | @TableField(value = "single_quantity") |
| | | @Excel(name = "æ¯ä»¶æ°é") |
| | | private BigDecimal singleQuantity; |
| | | @Excel(name = "æä½åºåæ°é") |
| | | private BigDecimal minStock; |
| | | /** |
| | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | |
| | | private Double unitPrice; |
| | | @Schema(description = "æ°é") |
| | | private Integer quantity; |
| | | @TableField(value = "single_quantity") |
| | | @Schema(description = "æ¯ä»¶æ°é") |
| | | private BigDecimal singleQuantity; |
| | | @Schema(description = "éé¢") |
| | | private Double amount; |
| | | @Schema(description = "å建æ¶é´") |
| | |
| | | |
| | | int result; |
| | | Long salesLedgerId = salesLedgerProduct.getSalesLedgerId(); |
| | | salesLedgerProduct.setSingleQuantity(normalizeSingleQuantity(salesLedgerProduct.getSingleQuantity())); |
| | | if (salesLedgerProduct.getId() == null) { |
| | | salesLedgerProduct.setRegisterDate(LocalDateTime.now()); |
| | | result = salesLedgerProductMapper.insert(salesLedgerProduct); |
| | |
| | | return R.ok(); |
| | | } |
| | | |
| | | private BigDecimal normalizeSingleQuantity(BigDecimal singleQuantity) { |
| | | if (singleQuantity == null || singleQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ONE; |
| | | } |
| | | return singleQuantity; |
| | | } |
| | | |
| | | private String generateNextPlanNo(String datePrefix) { |
| | | QueryWrapper<ProductionPlan> queryWrapper = new QueryWrapper<>(); |
| | | queryWrapper.likeRight("mps_no", "JH" + datePrefix); |
| | |
| | | for (SalesLedgerProductImportDto salesLedgerProductImportDto : salesLedgerProductImportDtos) { |
| | | SalesLedgerProduct salesLedgerProduct = new SalesLedgerProduct(); |
| | | BeanUtils.copyProperties(salesLedgerProductImportDto, salesLedgerProduct); |
| | | salesLedgerProduct.setSingleQuantity(normalizeSingleQuantity(salesLedgerProduct.getSingleQuantity())); |
| | | salesLedgerProduct.setSalesLedgerId(salesLedger.getId()); |
| | | salesLedgerProduct.setType(1); |
| | | // 计ç®ä¸å«ç¨æ»ä»· |
| | |
| | | public void handleSalesLedgerProducts(Long salesLedgerId, List<SalesLedgerProduct> products, SaleEnum type) { |
| | | // æIDåç»ï¼åºåæ°å¢åæ´æ°çè®°å½ |
| | | Map<Boolean, List<SalesLedgerProduct>> partitionedProducts = products.stream() |
| | | .peek(p -> p.setSalesLedgerId(salesLedgerId)) |
| | | .peek(p -> { |
| | | p.setSalesLedgerId(salesLedgerId); |
| | | p.setSingleQuantity(normalizeSingleQuantity(p.getSingleQuantity())); |
| | | }) |
| | | .collect(Collectors.partitioningBy(p -> p.getId() != null)); |
| | | |
| | | List<SalesLedgerProduct> updateList = partitionedProducts.get(true); |
| | |
| | | return entity; |
| | | } |
| | | |
| | | private BigDecimal normalizeSingleQuantity(BigDecimal singleQuantity) { |
| | | if (singleQuantity == null || singleQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ONE; |
| | | } |
| | | return singleQuantity; |
| | | } |
| | | |
| | | @Transactional(readOnly = true) |
| | | public String generateSalesContractNo() { |
| | | LocalDate currentDate = LocalDate.now(); |
| | |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | |
| | | List<SalesQuotationProduct> products = salesQuotationDto.getProducts().stream().map(product -> { |
| | | SalesQuotationProduct salesQuotationProduct = new SalesQuotationProduct(); |
| | | BeanUtils.copyProperties(product, salesQuotationProduct); |
| | | salesQuotationProduct.setSingleQuantity(normalizeSingleQuantity(salesQuotationProduct.getSingleQuantity())); |
| | | salesQuotationProduct.setSalesQuotationId(salesQuotation.getId()); |
| | | return salesQuotationProduct; |
| | | }).collect(Collectors.toList()); |
| | |
| | | approveProcessVO.setApproveType(6); |
| | | approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId()); |
| | | approveProcessVO.setApproveReason(quotationNo); |
| | | approveProcessVO.setApproveUserIds(salesQuotationDto.getApproveUserIds()); |
| | | approveProcessVO.setApproveUser(loginUser.getUserId()); |
| | | approveProcessVO.setApproveTime(LocalDate.now().toString()); |
| | | approveProcessVO.setPrice(salesQuotationDto.getTotalAmount()); |
| | |
| | | approveProcessService.addApprove(approveProcessVO); |
| | | }catch (Exception e){ |
| | | log.error("SalesQuotationServiceImpl error:{}", e); |
| | | throw new RuntimeException("审æ¹å¤±è´¥"); |
| | | throw new RuntimeException("审æ¹å¤±è´¥"); |
| | | } |
| | | return true; |
| | | } |
| | |
| | | List<SalesQuotationProduct> products = salesQuotationDto.getProducts().stream().map(product -> { |
| | | SalesQuotationProduct salesQuotationProduct = new SalesQuotationProduct(); |
| | | BeanUtils.copyProperties(product, salesQuotationProduct); |
| | | salesQuotationProduct.setSingleQuantity(normalizeSingleQuantity(salesQuotationProduct.getSingleQuantity())); |
| | | salesQuotationProduct.setSalesQuotationId(salesQuotation.getId()); |
| | | return salesQuotationProduct; |
| | | }).collect(Collectors.toList()); |
| | | |
| | | salesQuotationProductService.saveBatch(products); |
| | | // ä¿®æ¹æ¥ä»·å®¡æ¹ |
| | | vo.setApproveUserIds(salesQuotationDto.getApproveUserIds()); |
| | | vo.setApproveType(6); |
| | | vo.setApproveReason(salesQuotationDto.getQuotationNo()); |
| | | approveProcessService.updateApproveUser(vo); |
| | |
| | | return true; |
| | | } |
| | | |
| | | private BigDecimal normalizeSingleQuantity(BigDecimal singleQuantity) { |
| | | if (singleQuantity == null || singleQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ONE; |
| | | } |
| | | return singleQuantity; |
| | | } |
| | | |
| | | |
| | | } |
| | |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.mapper.ShippingInfoMapper; |
| | | import com.ruoyi.sales.mapper.ShippingProductDetailMapper; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.sales.pojo.ShippingInfo; |
| | | import com.ruoyi.sales.pojo.ShippingProductDetail; |
| | | import com.ruoyi.sales.service.ShippingInfoService; |
| | |
| | | import org.apache.commons.collections4.CollectionUtils; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | /** |
| | |
| | | |
| | | @Override |
| | | public boolean add(ShippingInfoDto req) { |
| | | if (CollectionUtils.isEmpty(req.getBatchNoDetailList())) { |
| | | throw new RuntimeException("åè´§æç»ä¸è½ä¸ºç©º"); |
| | | } |
| | | // normalizeShippingQuantity(req.getBatchNoDetailList(), req.getSalesLedgerProductId()); |
| | | this.save(req); |
| | | req.getBatchNoDetailList().forEach(item -> item.setShippingInfoId(req.getId())); |
| | | shippingProductDetailMapper.insert(req.getBatchNoDetailList()); |
| | |
| | | shippingApproveDto.setShippingProductDetailDtoList(dateilByShippingNo); |
| | | return shippingApproveDto; |
| | | } |
| | | |
| | | /** |
| | | * æ£å¸¸ååè´§æ°é |
| | | * @param shippingProductDetails |
| | | * @param salesLedgerProductId |
| | | */ |
| | | private void normalizeShippingQuantity(List<ShippingProductDetail> shippingProductDetails, Long salesLedgerProductId) { |
| | | if (CollectionUtils.isEmpty(shippingProductDetails)) { |
| | | return; |
| | | } |
| | | BigDecimal singleQuantity = BigDecimal.ONE; |
| | | if (salesLedgerProductId != null) { |
| | | SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(salesLedgerProductId); |
| | | if (salesLedgerProduct != null && salesLedgerProduct.getSingleQuantity() != null |
| | | && salesLedgerProduct.getSingleQuantity().compareTo(BigDecimal.ZERO) > 0) { |
| | | singleQuantity = salesLedgerProduct.getSingleQuantity(); |
| | | } |
| | | } |
| | | if (singleQuantity.compareTo(BigDecimal.ONE) == 0) { |
| | | return; |
| | | } |
| | | for (ShippingProductDetail shippingProductDetail : shippingProductDetails) { |
| | | if (shippingProductDetail.getQuantity() != null) { |
| | | shippingProductDetail.setQuantity(shippingProductDetail.getQuantity().multiply(singleQuantity)); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | T1.sales_ledger_id, |
| | | T1.warn_num, |
| | | T1.quantity, |
| | | T1.single_quantity, |
| | | T1.min_stock, |
| | | T1.tax_rate, |
| | | T1.tax_inclusive_unit_price, |
| | |
| | | WHEN (IFNULL(t2.qualitity, 0) - IFNULL(t2.locked_quantity, 0)) >0 THEN 1 |
| | | ELSE 0 |
| | | END as has_sufficient_stock, |
| | | (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0)) as no_quantity, |
| | | (IFNULL(T1.quantity, 0) * IFNULL(NULLIF(T1.single_quantity, 0), 1) - IFNULL(t3.shipped_quantity, 0)) as no_quantity, |
| | | CASE |
| | | WHEN IFNULL(t3.shipped_quantity, 0) = 0 THEN 'å¾
åè´§' |
| | | WHEN (IFNULL(T1.quantity, 0) - IFNULL(t3.shipped_quantity, 0)) > 0 THEN 'é¨ååè´§' |
| | | WHEN (IFNULL(T1.quantity, 0) * IFNULL(NULLIF(T1.single_quantity, 0), 1) - IFNULL(t3.shipped_quantity, 0)) > 0 THEN 'é¨ååè´§' |
| | | ELSE 'å·²åè´§' |
| | | END as shippingStatus, |
| | | CASE |
| | |
| | | "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.sales.mapper.SalesQuotationMapper"> |
| | | <select id="listPage" resultType="com.ruoyi.sales.dto.SalesQuotationDto"> |
| | | SELECT t1.*, |
| | | t2.approve_user_ids |
| | | SELECT t1.* |
| | | FROM sales_quotation t1 |
| | | LEFT JOIN approve_process t2 ON t1.quotation_no = t2.approve_reason and t2.approve_type = 6 |
| | | WHERE 1=1 |
| | | and t2.approve_deleted = 0 |
| | | <if test="salesQuotationDto.quotationNo != null and salesQuotationDto.quotationNo != '' "> |
| | | AND t1.quotation_no LIKE CONCAT('%',#{salesQuotationDto.quotationNo},'%') |
| | | </if> |
| | | <if test="salesQuotationDto.customer != null and salesQuotationDto.customer != '' "> |
| | | AND t1.customer = #{salesQuotationDto.customer} |
| | | </if> |
| | | <if test="salesQuotationDto.status != null and salesQuotationDto.status != '' "> |
| | | AND t1.status = #{salesQuotationDto.status} |
| | | </if> |
| | | <where> |
| | | <if test="salesQuotationDto.quotationNo != null and salesQuotationDto.quotationNo != '' "> |
| | | AND t1.quotation_no LIKE CONCAT('%',#{salesQuotationDto.quotationNo},'%') |
| | | </if> |
| | | <if test="salesQuotationDto.customer != null and salesQuotationDto.customer != '' "> |
| | | AND t1.customer = #{salesQuotationDto.customer} |
| | | </if> |
| | | <if test="salesQuotationDto.status != null and salesQuotationDto.status != '' "> |
| | | AND t1.status = #{salesQuotationDto.status} |
| | | </if> |
| | | </where> |
| | | |
| | | </select> |
| | | </mapper> |