| doc/20260527_ProductionOrderController_pick库存数量前端联调文档.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| doc/20260527_采购台账简易新增_采购申请通知前端联调文档.md | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/production/bean/vo/ProductionOrderPickVo.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/purchase/controller/PurchaseLedgerController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/purchase/service/IPurchaseLedgerService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
| src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 |
doc/20260527_ProductionOrderController_pick¿â´æÊýÁ¿Ç°¶ËÁªµ÷Îĵµ.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,48 @@ # `ProductionOrderController.pick` æ¥å£å端èè°ææ¡£ ## æ¥å£ä¿¡æ¯ - æ¥å£å°åï¼`GET /productionOrder/pick/{productionOrderId}` - ä½ç¨ï¼æ ¹æ®ç产订åIDæ¥è¯¢ BOM 颿åæç» - æ¬æ¬¡è¡¥å åæ®µï¼`stockQuantity` ## åæ®µè¯´æ `stockQuantity` 为产ååºåæ»éï¼å£å¾å¦ä¸ï¼ - æ `productModelId` æ±æ» - åªç»è®¡äº§ååºå表 `stock_inventory` - ä¸åºåæ¹å· `batchNo` - åå¼ä¸ºå½å产åè§æ ¼ä¸ææåºåæ°éä¹å ## è¿åç¤ºä¾ ```json [ { "id": 1001, "productionOrderId": 20001, "productModelId": 30001, "productName": "åææA", "model": "A-01", "unit": "kg", "pickQuantity": 120, "stockQuantity": 860, "batchNoList": ["B20260501", "B20260508"] } ] ``` ## å端èè°æ³¨æç¹ - å表å±ç¤ºæ¶ç´æ¥ä½¿ç¨ `stockQuantity` å³å¯ï¼ä¸éè¦åç«¯åææ¹å·ç´¯å ã - 妿 `stockQuantity` 为 `0`ï¼è¡¨ç¤ºè¯¥äº§åè§æ ¼å½å没æå¯ç¨åºåã - `batchNoList` ä»ç¶ä¿çï¼ç¨äºæ¹å·å±ç¤ºæåç»æ¹å·éæ©ï¼ä¸å½±å `stockQuantity` ç计ç®ã - è¯¥åæ®µå±äºæç»è¿ååæ®µï¼å端åªè¦æ¿å° `pick` æ¥å£è¿åå°±è½å±ç¤ºï¼æ éé¢å¤è¯·æ±åºåæ¥å£ã ## éªè¯æ¹å¼ - æå¼ç产订å详æ 页 - è°ç¨ `GET /productionOrder/pick/{productionOrderId}` - 确认æ¯ä¸æ¡ BOM 颿æç»é½è¿å `stockQuantity` - æ ¸å¯¹åºåæ°éä¸åºå模åä¸åä¸ `productModelId` çåºåæ»éä¸è´ doc/20260527_²É¹ºÌ¨Õ˼òÒ×ÐÂÔö_²É¹ºÉêÇë֪ͨǰ¶ËÁªµ÷Îĵµ.md
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,133 @@ # éè´å°è´¦ç®ææ°å¢ä¸éè´ç³è¯·éç¥å端èè°ææ¡£ ## 1. ç®æ æ¬æ¬¡æ°å¢çæ¯éè´å°è´¦çâç®ææ°å¢âè½åï¼ä¸é¨ç¨äºçäº§é¢æåºåä¸è¶³åºæ¯ã è¿ä¸ªå ¥å£çè¡ä¸ºæ¯ï¼ 1. å ä¿å䏿¡éè´è稿ï¼ä¸è¿å ¥éè´å®¡æ ¸ã 2. èªå¨ç»æé人åé䏿¡éè´ç³è¯·æéæ¶æ¯ã 3. åç³è¯·äººä¼è¢«åå ¥éè´å°è´¦å¤æ³¨ã 4. éè´å°è´¦å½å ¥äººåºå®ä¸ºæé人ã 5. æéäººè¡¥å ¨éè´è®¢åä¿¡æ¯åï¼åèµ°åææ£å¼æ°å¢æ¥å£è¿å ¥éè´å®¡æ ¸ã ## 2. æ°å¢æ¥å£ ### 2.1 ä¿åéè´è稿 - è¯·æ±æ¹å¼ï¼`POST` - æ¥å£è·¯å¾ï¼`/purchase/ledger/saveShortagePurchaseDraft` - 请æ±ä½ï¼`PurchaseLedgerDto` ### 2.2 ä»ç¶ä¿ççæ£å¼æäº¤æ¥å£ - è¯·æ±æ¹å¼ï¼`POST` - æ¥å£è·¯å¾ï¼`/purchase/ledger/addOrEditPurchase` è¿ä¸ªæ¥å£ç»§ç»èµ°åæéè´å®¡æ ¸æµç¨ãå端å¨èç¨¿è¡¥å ¨å®æåï¼ä»ç¶ä½¿ç¨å®æäº¤æ£å¼éè´åã ## 3. 请æ±å段 ä¿åè稿æ¶ï¼å端è³å°ä¼ è¿äºåæ®µï¼ - `salesContractNo`ï¼éå®è®¢åå· - `productData`ï¼éè´äº§åæç»æ°ç» - `ccUserId`ï¼æé人 ID å¯éåæ®µæ `PurchaseLedgerDto` åæç»æä¼ å ¥å³å¯ï¼ä¾å¦ï¼ - `purchaseContractNumber` - `supplierId` - `supplierName` - `entryDate` - `remarks` - `storageBlobDTOS` - `ccUserName` 说æï¼ - `purchaseContractNumber` 妿ä¸ä¼ ï¼å端ä¼èªå¨çæã - è稿ä¿åé¶æ®µä¸è¦æ±è¡¥å ¨éè´å®¡æ ¸ç¸å ³ä¿¡æ¯ã ## 4. è¿åç»æ è稿ä¿åæååï¼è¿åéè´å°è´¦ä¸»é® `id`ã 示ä¾ï¼ ```json { "code": 200, "msg": "ä¿åæå", "data": 12345 } ``` å端åºä¿åè¿ä¸ª `id`ï¼åç»è¡¥å ¨æ¶ç´æ¥å¸¦çè¿ä¸ª `id` è°ç¨æ£å¼æäº¤æ¥å£ã ## 5. å端è¡ä¸º ### 5.1 è稿ä¿åé»è¾ åç«¯ä¼æ ¹æ® `salesContractNo` æ¥è¯¢éå®è®¢åï¼å¹¶æéå®è®¢åä¿¡æ¯åå¡«å°éè´è稿éï¼å æ¬ï¼ - `salesLedgerId` - `salesContractNo` - `projectName` åæ¶ï¼ - `approvalStatus` åºå®ä¸º `0`ï¼è¡¨ç¤ºè稿 - 产åæç»ä¼æéè´å°è´¦å表ä¿å - ä¸ä¼å建éè´å®¡æ¹æµç¨ - `recorderId` å `recorderName` ä¼è®¾ç½®ä¸ºæé人 ### 5.2 夿³¨è§å åç«¯ä¼æåç³è¯·äººä¿¡æ¯åå ¥éè´å°è´¦å¤æ³¨ï¼å¤æ³¨å å®¹ç±»ä¼¼ï¼ - `åç³è¯·äººï¼å¼ ä¸ï¼ç±æé人æåè¡¥å ¨éè´è®¢åä¿¡æ¯åæäº¤å®¡æ ¸ã` 妿å端已ç»ä¼ äºå¤æ³¨ï¼å端ä¼å¨å夿³¨å追å è¿æ®µè¯´æã ### 5.3 éè´ç³è¯·æéæ¶æ¯ è稿å建åï¼å端ä¼èªå¨åé䏿¡ç«å æ¶æ¯ / APP æéç»æéäººï¼ - æ é¢ï¼`éè´ç³è¯·æé` - å å®¹ï¼æç¤ºè¯¥éå®è®¢å对åºçéè´ç³è¯·å·²å建ï¼éè¦è¡¥å ¨éè´è®¢åä¿¡æ¯ååæäº¤å®¡æ ¸ - 跳转å°åï¼`/purchaseLedger/edit?id={id}` ## 6. å端èè°å»ºè®® ### 6.1 æ°å»ºèç¨¿é¡µé¢ å端建议æ°å¢ä¸ä¸ªâç®æéè´ç³è¯·âå ¥å£ï¼å段æå°åªæ¾ï¼ - éå®è®¢åå· - æé人 - éè´äº§åæç» ä¿åå跳转å°éè´èç¨¿è¯¦æ æç¼è¾é¡µï¼ç»§ç»è¡¥å ¨ä¾åºåãå½å ¥æ¥æã夿³¨çä¿¡æ¯ã ### 6.2 è稿ç¼è¾é¡µ è稿详æ 页éè¦æ¯æï¼ - 读åè稿详æ - ç¼è¾éè´äº§åæç» - è¡¥å ¨éè´åä¸»è¡¨ä¿¡æ¯ - æç»è°ç¨ `/purchase/ledger/addOrEditPurchase` æ£å¼æäº¤ ### 6.3 æ¶æ¯ä¸å¿ 妿åç«¯ææ¶æ¯å表页ï¼ç¨æ·ç¹å»è¿æ¡éè´ç³è¯·æéåï¼åºè·³å°éè´è稿ç¼è¾é¡µã ## 7. è°è¯ç¹ 1. `salesContractNo` ä¸å卿¶ï¼å端ä¼ç´æ¥æ¥éã 2. è稿ä¿ååä¸ä¼çæå®¡æ¹åã 3. èç¨¿è¡¥å ¨ååèµ°æ£å¼æäº¤æ¥å£ï¼æä¼è¿å ¥å®¡æ¹æµã 4. æ¶æ¯æ¥æ¶äººæ¯æé人ï¼ä¸æ¯åç³è¯·äººã 5. åç³è¯·äººä¿¡æ¯åªä¼åå ¥å¤æ³¨ã src/main/java/com/ruoyi/production/bean/vo/ProductionOrderPickVo.java
@@ -15,7 +15,7 @@ @Schema(description = "åä½") private String unit; @Schema(description = "åºåæ°é") @Schema(description = "产ååºåæ»éï¼æ productModelId æ±æ»ï¼ä¸åºåæ¹å·ï¼") private BigDecimal stockQuantity; @Schema(description = "é¢ç¨æ°é") src/main/java/com/ruoyi/production/service/impl/ProductionOrderPickServiceImpl.java
@@ -189,6 +189,7 @@ return Collections.emptyList(); } List<ProductionOrderPickVo> detailList = baseMapper.listPickedDetailByOrderId(productionOrderId); fillStockQuantity(detailList); fillBatchNoList(detailList); fillSelectableBatchNoList(detailList); return detailList; @@ -1121,6 +1122,34 @@ } } private void fillStockQuantity(List<ProductionOrderPickVo> detailList) { if (detailList == null || detailList.isEmpty()) { return; } Set<Long> productModelIdSet = detailList.stream() .map(ProductionOrderPickVo::getProductModelId) .filter(Objects::nonNull) .collect(Collectors.toSet()); if (productModelIdSet.isEmpty()) { return; } List<StockInventory> stockList = stockInventoryMapper.selectList( Wrappers.<StockInventory>lambdaQuery() .in(StockInventory::getProductModelId, productModelIdSet)); Map<Long, BigDecimal> stockQuantityMap = new HashMap<>(); for (StockInventory stockInventory : stockList) { if (stockInventory == null || stockInventory.getProductModelId() == null) { continue; } stockQuantityMap.merge(stockInventory.getProductModelId(), defaultDecimal(stockInventory.getQualitity()), BigDecimal::add); } for (ProductionOrderPickVo detail : detailList) { detail.setStockQuantity(stockQuantityMap.getOrDefault(detail.getProductModelId(), BigDecimal.ZERO)); } } private String buildBatchNoGroupKey(ProductionOrderPickVo detail) { // æå»ºæ¹æ¬¡èååç»é®ã return detail.getProductionOrderId() + "|" src/main/java/com/ruoyi/purchase/controller/PurchaseLedgerController.java
@@ -137,6 +137,17 @@ } /** * éè´èç¨¿ç®ææ°å¢ */ @Log(title = "éè´å°è´¦", businessType = BusinessType.INSERT) @PostMapping("/saveShortagePurchaseDraft") @Operation(summary = "ç®ææ°å¢éè´è稿") public AjaxResult saveShortagePurchaseDraft(@RequestBody PurchaseLedgerDto purchaseLedgerDto) throws Exception { Long id = purchaseLedgerService.saveShortagePurchaseDraft(purchaseLedgerDto); return AjaxResult.success("ä¿åæå", id); } /** * æ¥è¯¢éè´æ¨¡æ¿ */ @Operation(summary = "/æ¥è¯¢éè´æ¨¡æ¿") src/main/java/com/ruoyi/purchase/dto/PurchaseLedgerDto.java
@@ -69,6 +69,16 @@ private String recorderName; /** * æé人ID */ private Long ccUserId; /** * æé人å§å */ private String ccUserName; /** * éå®ååå· */ @Excel(name = "éå®ååå·") src/main/java/com/ruoyi/purchase/pojo/PurchaseLedger.java
@@ -158,7 +158,4 @@ @TableField(fill = FieldFill.INSERT) private Long deptId; @Schema(description = "模æ¿id") private Long templateId; } src/main/java/com/ruoyi/purchase/service/IPurchaseLedgerService.java
@@ -23,6 +23,8 @@ int addOrEditPurchase(PurchaseLedgerDto purchaseLedgerDto) throws Exception; Long saveShortagePurchaseDraft(PurchaseLedgerDto purchaseLedgerDto) throws Exception; void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct); int deletePurchaseLedgerByIds(Long[] ids); src/main/java/com/ruoyi/purchase/service/impl/PurchaseLedgerServiceImpl.java
@@ -10,6 +10,7 @@ import com.ruoyi.approve.pojo.ApproveProcess; import com.ruoyi.approve.service.impl.ApproveProcessServiceImpl; import com.ruoyi.basic.enums.ApplicationTypeEnum; import com.ruoyi.common.enums.ApprovalStatusEnum; import com.ruoyi.basic.enums.RecordTypeEnum; import com.ruoyi.basic.mapper.ProductMapper; import com.ruoyi.basic.mapper.ProductModelMapper; @@ -30,6 +31,7 @@ import com.ruoyi.procurementrecord.pojo.ProcurementRecordStorage; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysUserMapper; import com.ruoyi.project.system.service.ISysNoticeService; import com.ruoyi.purchase.dto.PurchaseLedgerDto; import com.ruoyi.purchase.dto.PurchaseLedgerImportDto; import com.ruoyi.purchase.dto.PurchaseLedgerProductImportDto; @@ -99,6 +101,7 @@ private final QualityInspectParamMapper qualityInspectParamMapper; private final ApproveProcessServiceImpl approveProcessService; private final ProcurementRecordMapper procurementRecordStorageMapper; private final ISysNoticeService sysNoticeService; private final FileUtil fileUtil; @Override @@ -174,7 +177,91 @@ return 1; } @Override @Transactional(rollbackFor = Exception.class) public Long saveShortagePurchaseDraft(PurchaseLedgerDto purchaseLedgerDto) throws Exception { if (purchaseLedgerDto == null) { throw new BaseException("éè´å°è´¦æ°æ®ä¸è½ä¸ºç©º"); } if (StringUtils.isBlank(purchaseLedgerDto.getSalesContractNo())) { throw new BaseException("éå®è®¢åå·ä¸è½ä¸ºç©º"); } if (CollectionUtils.isEmpty(purchaseLedgerDto.getProductData())) { throw new BaseException("éè´äº§åä¿¡æ¯ä¸è½ä¸ºç©º"); } SalesLedger salesLedger = salesLedgerMapper.selectOne(new LambdaQueryWrapper<SalesLedger>() .eq(SalesLedger::getSalesContractNo, purchaseLedgerDto.getSalesContractNo()) .last("limit 1")); if (salesLedger == null) { throw new BaseException("éå®è®¢åä¸åå¨"); } PurchaseLedger purchaseLedger = new PurchaseLedger(); BeanUtils.copyProperties(purchaseLedgerDto, purchaseLedger); purchaseLedger.setSalesLedgerId(salesLedger.getId()); purchaseLedger.setSalesContractNo(salesLedger.getSalesContractNo()); purchaseLedger.setProjectName(salesLedger.getProjectName()); if (purchaseLedger.getEntryDate() == null) { purchaseLedger.setEntryDate(salesLedger.getEntryDate() != null ? salesLedger.getEntryDate() : new Date()); } if (!StringUtils.hasText(purchaseLedger.getPurchaseContractNumber())) { purchaseLedger.setPurchaseContractNumber(getPurchaseNo()); } LoginUser loginUser = SecurityUtils.getLoginUser(); Long currentUserId = loginUser != null && loginUser.getUserId() != null ? loginUser.getUserId() : null; if (loginUser != null && loginUser.getTenantId() != null) { purchaseLedger.setTenantId(loginUser.getTenantId()); } SysUser recorderUser = resolveShortagePurchaseRecorderUser(purchaseLedgerDto, currentUserId); if (ObjectUtils.isNotEmpty(recorderUser)) { purchaseLedger.setRecorderId(recorderUser.getUserId()); purchaseLedger.setRecorderName(recorderUser.getNickName()); purchaseLedger.setPhoneNumber(recorderUser.getPhonenumber()); } // String originalApplicantName = resolveOriginalApplicantName(salesLedger); // 溯æºéå®ç³è¯·äºº String originalApplicantName = SecurityUtils.getLoginUser().getNickName(); // å½åç»å½ç¨æ· purchaseLedger.setRemarks(mergeShortagePurchaseRemark( purchaseLedger.getRemarks(), originalApplicantName, ObjectUtils.isNotEmpty(recorderUser) ? recorderUser.getNickName() : null )); purchaseLedger.setApprovalStatus(ApprovalStatusEnum.DRAFT.getCode()); boolean isNewDraft = purchaseLedger.getId() == null; if (isNewDraft) { purchaseLedgerMapper.insert(purchaseLedger); } else { PurchaseLedger dbPurchaseLedger = purchaseLedgerMapper.selectById(purchaseLedger.getId()); if (dbPurchaseLedger == null) { throw new BaseException("éè´å°è´¦ä¸åå¨"); } if (!ApprovalStatusEnum.DRAFT.getCode().equals(dbPurchaseLedger.getApprovalStatus())) { throw new BaseException("éèç¨¿ç¶æçéè´å°è´¦ä¸å 许éè¿ç®ææ°å¢ä¿®æ¹"); } purchaseLedgerMapper.updateById(purchaseLedger); } handleSalesLedgerProducts(purchaseLedger.getId(), purchaseLedgerDto.getProductData(), purchaseLedgerDto.getType()); fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_LEDGER, purchaseLedger.getId(), purchaseLedgerDto.getStorageBlobDTOS()); if (isNewDraft) { Long noticeUserId = resolveShortagePurchaseCopyUserId(purchaseLedgerDto, currentUserId); if (noticeUserId != null && noticeUserId > 0) { sysNoticeService.simpleNoticeByUser( "éè´ç³è¯·æé", "éå®è®¢åå· " + salesLedger.getSalesContractNo() + " çéè´ç³è¯·å·²å建ï¼è¯·è¡¥å ¨éè´è®¢åä¿¡æ¯åæäº¤å®¡æ ¸ã", Collections.singletonList(noticeUserId), "/procurementManagement/procurementLedger?purchaseContractNumber=" + purchaseLedger.getPurchaseContractNumber() ); } } return purchaseLedger.getId(); } public void addQualityInspect(PurchaseLedger purchaseLedger, SalesLedgerProduct saleProduct) { QualityInspect qualityInspect = new QualityInspect(); qualityInspect.setInspectType(0); @@ -626,6 +713,59 @@ approveProcessService.addApprove(approveProcessVO); } private Long resolveShortagePurchaseCopyUserId(PurchaseLedgerDto purchaseLedgerDto, Long currentUserId) { if (purchaseLedgerDto != null && purchaseLedgerDto.getCcUserId() != null) { return purchaseLedgerDto.getCcUserId(); } return currentUserId; } private SysUser resolveShortagePurchaseRecorderUser(PurchaseLedgerDto purchaseLedgerDto, Long currentUserId) { if (purchaseLedgerDto != null && purchaseLedgerDto.getCcUserId() != null) { SysUser ccUser = sysUserMapper.selectUserById(purchaseLedgerDto.getCcUserId()); if (ccUser != null) { return ccUser; } } if (purchaseLedgerDto != null && StringUtils.isNotBlank(purchaseLedgerDto.getCcUserName())) { SysUser ccUser = sysUserMapper.selectOne(new LambdaQueryWrapper<SysUser>() .eq(SysUser::getNickName, purchaseLedgerDto.getCcUserName()) .last("limit 1")); if (ccUser != null) { return ccUser; } } return currentUserId == null ? null : sysUserMapper.selectUserById(currentUserId); } private String resolveOriginalApplicantName(SalesLedger salesLedger) { if (salesLedger == null) { return null; } if (salesLedger.getCreateUser() != null) { SysUser applicant = sysUserMapper.selectUserById(salesLedger.getCreateUser().longValue()); if (applicant != null && StringUtils.hasText(applicant.getNickName())) { return applicant.getNickName(); } } if (StringUtils.hasText(salesLedger.getEntryPerson())) { return salesLedger.getEntryPerson(); } return null; } private String mergeShortagePurchaseRemark(String originalRemark, String applicantName, String recorderName) { String sentence = "åç³è¯·äººï¼" + (StringUtils.hasText(applicantName) ? applicantName : "æªè¯å«") + "ï¼ç±æé人" + (StringUtils.hasText(recorderName) ? recorderName : "æªè¯å«") + "è¡¥å ¨éè´è®¢åä¿¡æ¯åæäº¤å®¡æ ¸ã"; if (!StringUtils.hasText(originalRemark)) { return sentence; } if (originalRemark.contains("åç³è¯·äººï¼")) { return originalRemark; } return originalRemark + "\\n" + sentence; } /** * ä¸å线å½å转驼峰å½å */