feat: 新增废料管理并完善合同导出
新增废料入库、出售单、财务审批回调、库存过账及按生产订单统计,并提供业务表、权限菜单和审批流程模板。仓库设置支持仓库类型,库存台账展示物料技术参数。
合同 Word 导出改用中性模板,补充合同查询权限与数据校验,模板预留供方和交易条款。
废料审批模型需导入发布;Word 模板未完成渲染版式检查。
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ä»åºç±»å为å¯éææ¬ï¼ä¿çåå²ä»åºè®°å½åå¼ã |
| | | ALTER TABLE `mes_wm_warehouse` |
| | | ADD COLUMN `warehouse_type` varchar(50) NULL DEFAULT NULL COMMENT 'ä»åºç±»å' AFTER `name`; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ç¬ç«åºæä¸å¡ï¼å
¥åºæç产订åç»è®°ï¼åºå®åå¨è´¢å¡å®¡æ¹éè¿åè¿è´¦ã |
| | | -- æ¤èæ¬ä»
å建ä¸å¡è¡¨ï¼ä¸åå»ºå®¡æ¹æ¨¡åãèåæä»åºåºç¡æ°æ®ã |
| | | CREATE TABLE IF NOT EXISTS `mes_wm_scrap_receipt` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT, |
| | | `code` varchar(64) NOT NULL COMMENT 'å
¥åºåå·', |
| | | `work_order_id` bigint NOT NULL COMMENT 'ç产工åç¼å·', |
| | | `work_order_code` varchar(64) NOT NULL COMMENT 'ç产工åå·å¿«ç
§', |
| | | `item_id` bigint NOT NULL COMMENT 'åºæç©æç¼å·', |
| | | `quantity` decimal(20,4) NOT NULL COMMENT 'å
¥åºæ°é', |
| | | `warehouse_id` bigint NOT NULL, |
| | | `location_id` bigint NOT NULL, |
| | | `area_id` bigint NOT NULL, |
| | | `batch_id` bigint DEFAULT NULL, |
| | | `batch_code` varchar(100) DEFAULT NULL, |
| | | `receipt_time` datetime NOT NULL, |
| | | `transaction_id` bigint DEFAULT NULL COMMENT 'å
¥åºåºåæµæ°´ç¼å·', |
| | | `remark` varchar(500) DEFAULT NULL, |
| | | `creator` varchar(64) DEFAULT '', |
| | | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | `updater` varchar(64) DEFAULT '', |
| | | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | `deleted` bit(1) NOT NULL DEFAULT b'0', |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `uk_scrap_receipt_code` (`code`), |
| | | KEY `idx_scrap_receipt_order_time` (`work_order_id`,`receipt_time`), |
| | | KEY `idx_scrap_receipt_area` (`area_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='åºæå
¥åº'; |
| | | |
| | | CREATE TABLE IF NOT EXISTS `mes_wm_scrap_sale` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT, |
| | | `code` varchar(64) NOT NULL COMMENT 'åºå®åå·', |
| | | `buyer` varchar(200) NOT NULL COMMENT 'è´ä¹°æ¹', |
| | | `status` tinyint NOT NULL DEFAULT 0 COMMENT '0è稿 1审æ¹ä¸ 2å·²åºåº 3已驳å 4已忶', |
| | | `process_instance_id` varchar(64) DEFAULT NULL COMMENT 'å®¡æ¹æµç¨å®ä¾', |
| | | `remark` varchar(500) DEFAULT NULL, |
| | | `creator` varchar(64) DEFAULT '', |
| | | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | `updater` varchar(64) DEFAULT '', |
| | | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | `deleted` bit(1) NOT NULL DEFAULT b'0', |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `uk_scrap_sale_code` (`code`), |
| | | KEY `idx_scrap_sale_process` (`process_instance_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='åºæåºå®å'; |
| | | |
| | | CREATE TABLE IF NOT EXISTS `mes_wm_scrap_sale_line` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT, |
| | | `sale_id` bigint NOT NULL, |
| | | `material_stock_id` bigint NOT NULL COMMENT 'æå®åºåè®°å½', |
| | | `quantity` decimal(20,4) NOT NULL, |
| | | `unit_price` decimal(20,4) NOT NULL, |
| | | `transaction_id` bigint DEFAULT NULL COMMENT '审æ¹éè¿åçåºåºæµæ°´ç¼å·', |
| | | `creator` varchar(64) DEFAULT '', |
| | | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP, |
| | | `updater` varchar(64) DEFAULT '', |
| | | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, |
| | | `deleted` bit(1) NOT NULL DEFAULT b'0', |
| | | PRIMARY KEY (`id`), |
| | | KEY `idx_scrap_sale_line_sale` (`sale_id`), |
| | | KEY `idx_scrap_sale_line_stock` (`material_stock_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='åºæåºå®æç»'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- åºæç®¡çèååæéï¼ä¸å¡è¡¨éå
æ§è¡ 20261103_wm_scrap.sqlã |
| | | -- åå§ææè¶
级管çåï¼å
¶ä»è§è²è¯·å¨è§è²ç®¡ç䏿èè´£é
ç½®ã |
| | | INSERT INTO system_menu(name,permission,type,sort,parent_id,path,icon,component,component_name,status,visible,keep_alive,always_show,creator,updater,deleted) |
| | | SELECT 'åºæç®¡ç','',2,80,5780,'scrap','ep:box','wls/scrap/index','WlsScrap',0,b'1',b'0',b'1','1','1',b'0' |
| | | WHERE EXISTS (SELECT 1 FROM system_menu WHERE id=5780 AND deleted=b'0') |
| | | AND NOT EXISTS (SELECT 1 FROM system_menu WHERE component='wls/scrap/index' AND deleted=b'0'); |
| | | |
| | | SET @scrap_menu_id = (SELECT id FROM system_menu WHERE component='wls/scrap/index' AND deleted=b'0' ORDER BY id LIMIT 1); |
| | | INSERT INTO system_menu(name,permission,type,sort,parent_id,path,icon,component,component_name,status,visible,keep_alive,always_show,creator,updater,deleted) |
| | | SELECT p.name,p.permission,3,p.sort,@scrap_menu_id,'','','','',0,b'1',b'0',b'1','1','1',b'0' |
| | | FROM ( |
| | | SELECT 'åºæå
¥åºæ¥è¯¢' name,'mes:wm-scrap-receipt:query' permission,1 sort UNION ALL |
| | | SELECT 'åºæå
¥åºç»è®°','mes:wm-scrap-receipt:create',2 UNION ALL |
| | | SELECT 'åºæåºå®æ¥è¯¢','mes:wm-scrap-sale:query',3 UNION ALL |
| | | SELECT 'åºæåºå®å建','mes:wm-scrap-sale:create',4 UNION ALL |
| | | SELECT 'åºæåºå®æäº¤å®¡æ¹','mes:wm-scrap-sale:submit',5 |
| | | ) p |
| | | WHERE @scrap_menu_id IS NOT NULL AND NOT EXISTS (SELECT 1 FROM system_menu m WHERE m.permission=p.permission AND m.deleted=b'0'); |
| | | |
| | | INSERT INTO system_role_menu(role_id,menu_id,creator,updater,deleted) |
| | | SELECT r.id,m.id,'1','1',b'0' FROM system_role r JOIN system_menu m |
| | | ON m.deleted=b'0' AND (m.id=@scrap_menu_id OR m.parent_id=@scrap_menu_id) |
| | | WHERE r.code='super_admin' AND r.deleted=b'0' |
| | | AND NOT EXISTS (SELECT 1 FROM system_role_menu rm WHERE rm.role_id=r.id AND rm.menu_id=m.id AND rm.deleted=b'0'); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" |
| | | xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
| | | xmlns:flowable="http://flowable.org/bpmn" |
| | | xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" |
| | | xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" |
| | | xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" |
| | | targetNamespace="http://www.iocoder.cn/bpmn"> |
| | | <process id="wm_scrap_sale_finance" name="åºæåºå®è´¢å¡å®¡æ¹" isExecutable="true"> |
| | | <startEvent id="start" name="æäº¤" /> |
| | | <sequenceFlow id="to_review" sourceRef="start" targetRef="finance_review" /> |
| | | <userTask id="finance_review" name="è´¢å¡å®¡æ ¸" flowable:candidateStrategy="10" flowable:candidateParam="1"> |
| | | <extensionElements><flowable:assignStartUserHandlerType>1</flowable:assignStartUserHandlerType></extensionElements> |
| | | </userTask> |
| | | <sequenceFlow id="to_end" sourceRef="finance_review" targetRef="end" /> |
| | | <endEvent id="end" name="宿" /> |
| | | </process> |
| | | <bpmndi:BPMNDiagram id="diagram"> |
| | | <bpmndi:BPMNPlane id="plane" bpmnElement="wm_scrap_sale_finance"> |
| | | <bpmndi:BPMNShape id="start_shape" bpmnElement="start"><omgdc:Bounds x="100" y="100" width="36" height="36" /></bpmndi:BPMNShape> |
| | | <bpmndi:BPMNShape id="review_shape" bpmnElement="finance_review"><omgdc:Bounds x="200" y="78" width="120" height="80" /></bpmndi:BPMNShape> |
| | | <bpmndi:BPMNShape id="end_shape" bpmnElement="end"><omgdc:Bounds x="390" y="100" width="36" height="36" /></bpmndi:BPMNShape> |
| | | <bpmndi:BPMNEdge id="to_review_edge" bpmnElement="to_review"><omgdi:waypoint x="136" y="118" /><omgdi:waypoint x="200" y="118" /></bpmndi:BPMNEdge> |
| | | <bpmndi:BPMNEdge id="to_end_edge" bpmnElement="to_end"><omgdi:waypoint x="320" y="118" /><omgdi:waypoint x="390" y="118" /></bpmndi:BPMNEdge> |
| | | </bpmndi:BPMNPlane> |
| | | </bpmndi:BPMNDiagram> |
| | | </definitions> |
| | |
| | | |
| | | @GetMapping("/download-document") |
| | | @Operation(summary = "æè´éå忍¡æ¿ä¸è½½åå Word æä»¶") |
| | | @PreAuthorize("@ss.hasPermission('crm:contract:query')") |
| | | public void downloadDocument(@RequestParam("id") Long id, HttpServletResponse response) throws IOException { |
| | | // getContract å
å«ååæ°æ®æéæ ¡éªï¼æ²¿ç¨è¯¦æ
页ç产åãåä½å客æ·ä¿¡æ¯ã |
| | | CrmContractRespVO contract = buildContractDetail(contractService.getContract(id)); |
| | | Assert.notNull(contract, "ååä¸åå¨"); |
| | | if (contract.getProducts() == null || contract.getProducts().isEmpty()) { |
| | | throw new cn.iocoder.yudao.framework.common.exception.ServiceException(400, "ååæ²¡æäº§åæç»ï¼è¯·å
ç»´æ¤äº§åå导åº"); |
| | | } |
| | | if (contract.getTotalPrice() == null || contract.getProducts().stream() |
| | | .anyMatch(product -> product.getContractPrice() == null || product.getCount() == null)) { |
| | | throw new cn.iocoder.yudao.framework.common.exception.ServiceException(400, "ååéé¢ãäº§åæ°éååä»·ä¸è½ä¸ºç©ºï¼è¯·å
è¡¥å
¨å导åº"); |
| | | } |
| | | byte[] content = cn.iocoder.yudao.module.crm.service.contract.CrmContractDocumentRenderer.render(contract); |
| | | String filename = "è´éåå-" + contract.getNo() + ".docx"; |
| | | response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"); |
| | |
| | | BigDecimal discount = productTotal.subtract(total); |
| | | replace(document, Map.of("customerName", text(contract.getCustomerName()), |
| | | "contractNo", text(contract.getNo()), |
| | | "contractName", text(contract.getName()), |
| | | "contractDate", contract.getOrderDate() == null ? "____________" : contract.getOrderDate().format(DateTimeFormatter.ofPattern("yyyyå¹´MMæddæ¥")), |
| | | "startDate", date(contract.getStartTime()), |
| | | "endDate", date(contract.getEndTime()), |
| | | "totalPrice", money(total), "totalUpper", Convert.digitToChinese(total), |
| | | "discountNote", discount.signum() == 0 ? "" : "ï¼æç»å计 " + money(productTotal) + " å
ï¼æ´å伿 " + money(discount) + " å
ï¼")); |
| | | "discountNote", discount.signum() == 0 ? "" : "ï¼æç»å计 " + money(productTotal) + " å
ï¼ä¸ååéé¢çå·®é¢ " + money(discount) + " å
ï¼")); |
| | | if (document.getDocumentElement().getTextContent().contains("${")) throw new IOException("å忍¡æ¿å卿ªå¡«åæ§½ä½"); |
| | | var transformerFactory = TransformerFactory.newInstance(); |
| | | transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); |
| | |
| | | } |
| | | |
| | | private static String money(BigDecimal value) { return value.setScale(2, RoundingMode.HALF_UP).toPlainString(); } |
| | | private static String date(java.time.LocalDateTime value) { return value == null ? "____________" : value.format(DateTimeFormatter.ofPattern("yyyyå¹´MMæddæ¥")); } |
| | | private static String text(String value) { return value == null ? "" : value; } |
| | | } |
| | |
| | | String xml = new String(parts.get("word/document.xml"), StandardCharsets.UTF_8); |
| | | assertTrue(xml.contains("HT20261015001")); assertTrue(xml.contains("2026å¹´10æ15æ¥")); |
| | | assertTrue(xml.contains("10000.00")); assertTrue(xml.contains("壹ä¸å
æ´")); |
| | | assertTrue(xml.contains("å»å¹²å¬è«å¤èï¼2500级ä¸çï¼")); assertTrue(xml.contains("è¯ä¿¡åè§")); |
| | | assertTrue(xml.contains("å»å¹²å¬è«å¤èï¼2500级ä¸çï¼")); assertTrue(xml.contains("交ä»ä¸éªæ¶")); |
| | | assertFalse(xml.contains("å京åä»å ")); assertFalse(xml.contains("28504001040001454")); |
| | | assertFalse(xml.contains("${")); |
| | | try(var template = new ClassPathResource("templates/contract/sale-contract.docx").getInputStream()) { |
| | | var original = unzip(template.readAllBytes()); assertEquals(original.keySet(),parts.keySet()); |
| | |
| | | contract.setProducts(Collections.nCopies(25,contract.getProducts().getFirst())); |
| | | contract.setTotalPrice(new BigDecimal("249000")); |
| | | String xml = new String(unzip(CrmContractDocumentRenderer.render(contract)).get("word/document.xml"),StandardCharsets.UTF_8); |
| | | assertTrue(xml.contains("客æ·<&>å
¬å¸")); assertTrue(xml.contains("æ´å伿 1000.00 å
")); |
| | | assertTrue(xml.contains("客æ·<&>å
¬å¸")); assertTrue(xml.contains("ä¸ååéé¢çå·®é¢ 1000.00 å
")); |
| | | assertEquals(25,xml.split("å»å¹²å¬è«å¤èï¼2500级ä¸çï¼",-1).length-1); |
| | | assertEquals(25,xml.split("<w:cantSplit",-1).length-1); |
| | | assertTrue(xml.contains("249000.00")); assertFalse(xml.contains("${")); |
| | | assertFalse(xml.contains("乿¹ï¼éæ¹ï¼ï¼ä¸å½ç§å¦é¢è¥¿åé«åçç©ç ç©¶æ")); |
| | | assertTrue(xml.contains("乿¹ï¼éæ¹ï¼ï¼å®¢æ·<&>å
¬å¸")); |
| | | String output = System.getProperty("contract.document.sample"); |
| | | if (output != null) Files.write(Path.of(output).resolveSibling("generated-contract-many.docx"), CrmContractDocumentRenderer.render(contract)); |
| | | } |
| | |
| | | @Schema(description = "è§æ ¼åå·", example = "M8*20") |
| | | private String specification; |
| | | |
| | | @Schema(description = "ææ¯åæ°") |
| | | private String technicalParameters; |
| | | |
| | | @Schema(description = "ç©æåç±»ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long categoryId; |
| | | |
| | |
| | | MapUtils.findAndThen(batchMap, vo.getBatchId(), batch -> vo.setReelNo(batch.getReelNo())); |
| | | MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { |
| | | vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()); |
| | | vo.setTechnicalParameters(item.getTechnicalParameters()); |
| | | MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | | unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName())); |
| | | }); |
| | |
| | | @ExcelProperty("è§æ ¼åå·") |
| | | private String specification; |
| | | |
| | | @Schema(description = "ææ¯åæ°") |
| | | @ExcelProperty("ææ¯åæ°") |
| | | private String technicalParameters; |
| | | |
| | | @Schema(description = "计éåä½åç§°", example = "åå
") |
| | | @ExcelProperty("计éåä½") |
| | | private String unitMeasureName; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.wm.scrap; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.scrap.MesWmScrapReceiptDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.scrap.MesWmScrapReceiptMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import jakarta.validation.constraints.DecimalMin; |
| | | import jakarta.validation.constraints.Digits; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Size; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.Pattern; |
| | | import lombok.Data; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.time.YearMonth; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | /** ç¬ç«åºæå
¥åºï¼è®°å½ååºåæµæ°´å¨åä¸ä¸ªäºå¡ä¸æäº¤ã */ |
| | | @RestController |
| | | @RequestMapping("/mes/wm/scrap-receipt") |
| | | @Validated |
| | | public class MesWmScrapReceiptController { |
| | | @Resource private MesWmScrapReceiptMapper receiptMapper; |
| | | @Resource private MesProWorkOrderMapper workOrderMapper; |
| | | @Resource private MesWmTransactionService transactionService; |
| | | @Resource private MesWmWarehouseAreaService areaService; |
| | | |
| | | @PostMapping("/create") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-scrap-receipt:create')") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public CommonResult<Long> create(@Valid @RequestBody CreateReq req) { |
| | | var order = workOrderMapper.selectById(req.getWorkOrderId()); |
| | | if (order == null) throw new ServiceException(400, "ç产工åä¸åå¨"); |
| | | areaService.validateWarehouseAreaExists(req.getWarehouseId(), req.getLocationId(), req.getAreaId()); |
| | | String code = "SCR-" + req.getRequestId(); |
| | | if (receiptMapper.selectByCode(code) != null) throw new ServiceException(400, "请æ±å·²æäº¤ï¼è¯·å·æ°å
¥åºè®°å½"); |
| | | MesWmScrapReceiptDO receipt = new MesWmScrapReceiptDO(); |
| | | receipt.setCode(code); |
| | | receipt.setWorkOrderId(order.getId()); |
| | | receipt.setWorkOrderCode(order.getCode()); |
| | | receipt.setItemId(req.getItemId()); |
| | | receipt.setQuantity(req.getQuantity()); |
| | | receipt.setWarehouseId(req.getWarehouseId()); |
| | | receipt.setLocationId(req.getLocationId()); |
| | | receipt.setAreaId(req.getAreaId()); |
| | | receipt.setBatchId(req.getBatchId()); |
| | | receipt.setBatchCode(req.getBatchCode()); |
| | | receipt.setReceiptTime(LocalDateTime.now()); |
| | | receipt.setRemark(req.getRemark()); |
| | | receiptMapper.insert(receipt); |
| | | Long transactionId = transactionService.createTransaction(new MesWmTransactionSaveReqDTO() |
| | | .setType(MesWmTransactionTypeEnum.IN.getType()).setBizType(cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants.WM_SCRAP_RECEIPT) |
| | | .setBizId(receipt.getId()).setBizLineId(receipt.getId()).setBizCode(receipt.getCode()) |
| | | .setItemId(receipt.getItemId()).setQuantity(receipt.getQuantity()) |
| | | .setWarehouseId(receipt.getWarehouseId()).setLocationId(receipt.getLocationId()) |
| | | .setAreaId(receipt.getAreaId()).setBatchId(receipt.getBatchId()).setBatchCode(receipt.getBatchCode()) |
| | | .setReceiptTime(receipt.getReceiptTime()).setForceCreateNewStock(true)); |
| | | receipt.setTransactionId(transactionId); |
| | | receiptMapper.updateById(receipt); |
| | | return success(receipt.getId()); |
| | | } |
| | | |
| | | @GetMapping("/monthly-summary") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-scrap-receipt:query')") |
| | | public CommonResult<List<Map<String, Object>>> monthlySummary(@RequestParam String month) { |
| | | YearMonth period = YearMonth.parse(month); |
| | | return success(receiptMapper.summarize(period.atDay(1).atStartOfDay(), |
| | | period.plusMonths(1).atDay(1).atStartOfDay())); |
| | | } |
| | | |
| | | @GetMapping("/page") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-scrap-receipt:query')") |
| | | public CommonResult<PageResult<MesWmScrapReceiptDO>> page(@Valid PageParam page, |
| | | @RequestParam(required = false) Long workOrderId) { |
| | | return success(receiptMapper.selectReceiptPage(page, workOrderId)); |
| | | } |
| | | |
| | | @Data |
| | | public static class CreateReq { |
| | | @NotBlank @Pattern(regexp = "[A-Za-z0-9-]{1,60}") private String requestId; |
| | | @NotNull private Long workOrderId; |
| | | @NotNull private Long itemId; |
| | | @NotNull @DecimalMin(value = "0", inclusive = false) @Digits(integer = 16, fraction = 4) private BigDecimal quantity; |
| | | @NotNull private Long warehouseId; |
| | | @NotNull private Long locationId; |
| | | @NotNull private Long areaId; |
| | | private Long batchId; |
| | | @Size(max = 100) private String batchCode; |
| | | @Size(max = 500) private String remark; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.wm.scrap; |
| | | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.scrap.MesWmScrapSaleDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.scrap.MesWmScrapSaleLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.scrap.MesWmScrapReceiptMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.scrap.MesWmScrapSaleLineMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.scrap.MesWmScrapSaleMapper; |
| | | import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.scrap.MesWmScrapSaleService; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import jakarta.validation.constraints.DecimalMin; |
| | | import jakarta.validation.constraints.Digits; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotEmpty; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Pattern; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| | | |
| | | @RestController |
| | | @RequestMapping("/mes/wm/scrap-sale") |
| | | @Validated |
| | | public class MesWmScrapSaleController { |
| | | @Resource private MesWmScrapSaleMapper saleMapper; |
| | | @Resource private MesWmScrapSaleLineMapper lineMapper; |
| | | @Resource private MesWmScrapReceiptMapper receiptMapper; |
| | | @Resource private MesWmMaterialStockService stockService; |
| | | @Resource private MesWmScrapSaleService saleService; |
| | | |
| | | @GetMapping("/page") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-scrap-sale:query')") |
| | | public CommonResult<PageResult<MesWmScrapSaleDO>> page(@Valid PageParam page) { |
| | | return success(saleMapper.selectPage(page, new LambdaQueryWrapperX<MesWmScrapSaleDO>() |
| | | .orderByDesc(MesWmScrapSaleDO::getId))); |
| | | } |
| | | |
| | | @GetMapping("/lines") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-scrap-sale:query')") |
| | | public CommonResult<List<MesWmScrapSaleLineDO>> lines(@RequestParam Long id) { |
| | | return success(lineMapper.selectBySaleId(id)); |
| | | } |
| | | |
| | | @PutMapping("/submit") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-scrap-sale:submit')") |
| | | public CommonResult<Boolean> submit(@RequestParam Long id) { |
| | | saleService.submit(id, getLoginUserId()); |
| | | return success(true); |
| | | } |
| | | |
| | | /** requestId ç±å®¢æ·ç«¯ä¸ºæ¯æ¬¡ä¸å¡æä½çæå¹¶å¨éè¯æ¶å¤ç¨ã */ |
| | | @PostMapping("/create") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-scrap-sale:create')") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public CommonResult<Long> create(@Valid @RequestBody CreateReq req) { |
| | | String code = "SS-" + req.getRequestId(); |
| | | if (saleMapper.selectByCode(code) != null) { |
| | | throw new ServiceException(400, "请æ±å·²æäº¤ï¼è¯·å·æ°åºå®åå表"); |
| | | } |
| | | Set<Long> stocks = new HashSet<>(); |
| | | for (LineReq line : req.getLines()) { |
| | | if (!stocks.add(line.getMaterialStockId())) throw new ServiceException(400, "åä¸åºåä¸è½é夿·»å "); |
| | | if (stockService.getMaterialStock(line.getMaterialStockId()) == null |
| | | || receiptMapper.countByStockId(line.getMaterialStockId()) == 0) { |
| | | throw new ServiceException(400, "è¯·éæ©åºæå
¥åºäº§ççåºå"); |
| | | } |
| | | } |
| | | MesWmScrapSaleDO sale = new MesWmScrapSaleDO(); |
| | | sale.setCode(code); |
| | | sale.setBuyer(req.getBuyer()); |
| | | sale.setRemark(req.getRemark()); |
| | | sale.setStatus(0); |
| | | saleMapper.insert(sale); |
| | | for (LineReq input : req.getLines()) { |
| | | MesWmScrapSaleLineDO line = new MesWmScrapSaleLineDO(); |
| | | line.setSaleId(sale.getId()); |
| | | line.setMaterialStockId(input.getMaterialStockId()); |
| | | line.setQuantity(input.getQuantity()); |
| | | line.setUnitPrice(input.getUnitPrice()); |
| | | lineMapper.insert(line); |
| | | } |
| | | return success(sale.getId()); |
| | | } |
| | | |
| | | @Data public static class CreateReq { |
| | | @NotBlank @Pattern(regexp = "[A-Za-z0-9-]{1,60}") private String requestId; |
| | | @NotBlank @Size(max = 200) private String buyer; |
| | | @Size(max = 500) private String remark; |
| | | @NotEmpty @Size(max = 100) private List<@Valid LineReq> lines; |
| | | } |
| | | @Data public static class LineReq { |
| | | @NotNull private Long materialStockId; |
| | | @NotNull @DecimalMin(value = "0", inclusive = false) @Digits(integer = 16, fraction = 4) private BigDecimal quantity; |
| | | @NotNull @DecimalMin("0") @Digits(integer = 16, fraction = 4) private BigDecimal unitPrice; |
| | | } |
| | | } |
| | |
| | | @Schema(description = "ä»åºåç§°", example = "åæä»") |
| | | private String name; |
| | | |
| | | @Schema(description = "ä»åºç±»å") |
| | | private String warehouseType; |
| | | |
| | | @Schema(description = "æ¯å¦å»ç»", example = "false") |
| | | private Boolean frozen; |
| | | |
| | |
| | | @ExcelProperty("ä»åºåç§°") |
| | | private String name; |
| | | |
| | | @Schema(description = "ä»åºç±»å", example = "åæä»") |
| | | @ExcelProperty("ä»åºç±»å") |
| | | private String warehouseType; |
| | | |
| | | @Schema(description = "ä»åºå°å", example = "A åºä¸å·æ¥¼") |
| | | @ExcelProperty("ä»åºå°å") |
| | | private String address; |
| | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotEmpty; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | |
| | | @NotEmpty(message = "ä»åºåç§°ä¸è½ä¸ºç©º") |
| | | private String name; |
| | | |
| | | @Schema(description = "ä»åºç±»å", example = "åæä»") |
| | | @Size(max = 50, message = "ä»åºç±»åä¸è½è¶
è¿ 50 个å符") |
| | | private String warehouseType; |
| | | |
| | | @Schema(description = "ä»åºå°å", example = "A åºä¸å·æ¥¼") |
| | | private String address; |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.wm.scrap; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @TableName("mes_wm_scrap_receipt") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class MesWmScrapReceiptDO extends BaseDO { |
| | | @TableId |
| | | private Long id; |
| | | private String code; |
| | | private Long workOrderId; |
| | | private String workOrderCode; |
| | | private Long itemId; |
| | | private BigDecimal quantity; |
| | | private Long warehouseId; |
| | | private Long locationId; |
| | | private Long areaId; |
| | | private Long batchId; |
| | | private String batchCode; |
| | | private LocalDateTime receiptTime; |
| | | private Long transactionId; |
| | | private String remark; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.wm.scrap; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @TableName("mes_wm_scrap_sale") |
| | | public class MesWmScrapSaleDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private String code; |
| | | private String buyer; |
| | | private Integer status; |
| | | private String processInstanceId; |
| | | private String remark; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.wm.scrap; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @TableName("mes_wm_scrap_sale_line") |
| | | public class MesWmScrapSaleLineDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private Long saleId; |
| | | private Long materialStockId; |
| | | private BigDecimal quantity; |
| | | private BigDecimal unitPrice; |
| | | private Long transactionId; |
| | | } |
| | |
| | | * ä»åºåç§° |
| | | */ |
| | | private String name; |
| | | /** ä»åºç±»å */ |
| | | private String warehouseType; |
| | | /** |
| | | * ä»åºå°å |
| | | */ |
| | |
| | | @Mapper |
| | | public interface MesWmMaterialStockMapper extends BaseMapperX<MesWmMaterialStockDO> { |
| | | |
| | | default MesWmMaterialStockDO lockById(Long id) { |
| | | return selectOne(new LambdaQueryWrapperX<MesWmMaterialStockDO>() |
| | | .eq(MesWmMaterialStockDO::getId, id).last("FOR UPDATE")); |
| | | } |
| | | |
| | | default PageResult<MesWmMaterialStockDO> selectPage(MesWmMaterialStockPageReqVO reqVO, |
| | | Collection<Long> itemTypeIds, |
| | | Collection<Long> itemIds, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.wm.scrap; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.scrap.MesWmScrapReceiptDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Select; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | @Mapper |
| | | public interface MesWmScrapReceiptMapper extends BaseMapperX<MesWmScrapReceiptDO> { |
| | | default MesWmScrapReceiptDO selectByCode(String code) { |
| | | return selectOne(MesWmScrapReceiptDO::getCode, code); |
| | | } |
| | | @Select("SELECT COUNT(*) FROM mes_wm_scrap_receipt r JOIN mes_wm_transaction t " + |
| | | "ON t.id = r.transaction_id AND t.deleted = 0 " + |
| | | "WHERE r.deleted = 0 AND t.material_stock_id = #{stockId}") |
| | | long countByStockId(@Param("stockId") Long stockId); |
| | | default PageResult<MesWmScrapReceiptDO> selectReceiptPage(PageParam page, Long workOrderId) { |
| | | return selectPage(page, new LambdaQueryWrapperX<MesWmScrapReceiptDO>() |
| | | .eqIfPresent(MesWmScrapReceiptDO::getWorkOrderId, workOrderId) |
| | | .orderByDesc(MesWmScrapReceiptDO::getId)); |
| | | } |
| | | @Select("SELECT work_order_id AS workOrderId, work_order_code AS workOrderCode, item_id AS itemId, " + |
| | | "SUM(quantity) AS quantity FROM mes_wm_scrap_receipt " + |
| | | "WHERE deleted = 0 AND receipt_time >= #{start} AND receipt_time < #{end} " + |
| | | "GROUP BY work_order_id, work_order_code, item_id ORDER BY work_order_code, item_id") |
| | | List<Map<String, Object>> summarize(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.wm.scrap; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.scrap.MesWmScrapSaleLineDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import java.util.List; |
| | | |
| | | @Mapper |
| | | public interface MesWmScrapSaleLineMapper extends BaseMapperX<MesWmScrapSaleLineDO> { |
| | | default List<MesWmScrapSaleLineDO> selectBySaleId(Long id) { |
| | | return selectList(MesWmScrapSaleLineDO::getSaleId, id); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.wm.scrap; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.scrap.MesWmScrapSaleDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | @Mapper |
| | | public interface MesWmScrapSaleMapper extends BaseMapperX<MesWmScrapSaleDO> { |
| | | default MesWmScrapSaleDO lockById(Long id) { |
| | | return selectOne(new LambdaQueryWrapperX<MesWmScrapSaleDO>() |
| | | .eq(MesWmScrapSaleDO::getId, id).last("FOR UPDATE")); |
| | | } |
| | | |
| | | default MesWmScrapSaleDO selectByCode(String code) { |
| | | return selectOne(MesWmScrapSaleDO::getCode, code); |
| | | } |
| | | } |
| | |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<MesWmWarehouseDO>() |
| | | .likeIfPresent(MesWmWarehouseDO::getCode, reqVO.getCode()) |
| | | .likeIfPresent(MesWmWarehouseDO::getName, reqVO.getName()) |
| | | .likeIfPresent(MesWmWarehouseDO::getWarehouseType, reqVO.getWarehouseType()) |
| | | .eqIfPresent(MesWmWarehouseDO::getFrozen, reqVO.getFrozen()) |
| | | .orderByDesc(MesWmWarehouseDO::getId)); |
| | | } |
| | |
| | | public static final int WM_RETURN_VENDOR = 125; // ä¾åºåéè´§åºåºï¼MesWmReturnVendorDO |
| | | public static final int WM_STOCK_ADJUST_IN = 126; // åºåè°æ´å
¥åºï¼èªå®ä¹å
¥åºï¼ |
| | | public static final int WM_STOCK_ADJUST_OUT = 127; // åºåè°æ´åºåºï¼èªå®ä¹åºåºï¼ |
| | | public static final int WM_SCRAP_RECEIPT = 128; // åºæå
¥åº |
| | | public static final int WM_SCRAP_SALE = 129; // åºæåºå®åºåº |
| | | |
| | | // ========== CAL æç模å [200, 300) ========== |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.wm.scrap; |
| | | |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent; |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | @Component |
| | | public class MesWmScrapSaleApprovalListener extends BpmProcessInstanceStatusEventListener { |
| | | @Resource private MesWmScrapSaleService service; |
| | | @Override protected String getProcessDefinitionKey() { return MesWmScrapSaleService.PROCESS_KEY; } |
| | | @Override protected void onEvent(BpmProcessInstanceStatusEvent event) { |
| | | service.onApproval(Long.valueOf(event.getBusinessKey()), event.getId(), event.getStatus()); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.wm.scrap; |
| | | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; |
| | | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.scrap.MesWmScrapSaleDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.scrap.MesWmScrapSaleLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.scrap.MesWmScrapReceiptMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.scrap.MesWmScrapSaleLineMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.scrap.MesWmScrapSaleMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import java.math.BigDecimal; |
| | | import java.util.Comparator; |
| | | import java.util.Objects; |
| | | import java.util.Map; |
| | | |
| | | @Service |
| | | public class MesWmScrapSaleService { |
| | | public static final String PROCESS_KEY = "wm_scrap_sale_finance"; |
| | | @Resource private MesWmScrapSaleMapper saleMapper; |
| | | @Resource private MesWmScrapSaleLineMapper lineMapper; |
| | | @Resource private MesWmScrapReceiptMapper receiptMapper; |
| | | @Resource private MesWmMaterialStockMapper stockMapper; |
| | | @Resource private MesWmTransactionService transactionService; |
| | | @Resource private BpmProcessInstanceApi processApi; |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void submit(Long id, Long userId) { |
| | | MesWmScrapSaleDO sale = saleMapper.lockById(id); |
| | | if (sale == null || !Objects.equals(sale.getStatus(), 0)) throw new ServiceException(400, "ä»
èç¨¿å¯æäº¤å®¡æ¹"); |
| | | var lines = lineMapper.selectBySaleId(id); |
| | | if (lines.isEmpty()) throw new ServiceException(400, "åºå®å没ææç»"); |
| | | // è´¢å¡å®¡æ ¸ä¸å¯è¢«å
¨å±å
审æ¹ç»è¿ã |
| | | if (processApi.isGlobalAutoApprove()) throw new ServiceException(400, "å
¨å±å
审æ¹å·²å¼å¯ï¼åºæåºå®å¿
é¡»ç»è¿è´¢å¡å®¡æ ¸"); |
| | | MesWmScrapSaleDO update = new MesWmScrapSaleDO(); |
| | | update.setId(id); |
| | | update.setStatus(1); |
| | | saleMapper.updateById(update); |
| | | String processId = processApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(PROCESS_KEY).setBusinessKey(String.valueOf(id)) |
| | | .setVariables(Map.of("saleCode", sale.getCode(), "buyer", sale.getBuyer(), |
| | | "totalAmount", lines.stream().map(line -> line.getQuantity().multiply(line.getUnitPrice())) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add)))); |
| | | MesWmScrapSaleDO processUpdate = new MesWmScrapSaleDO(); |
| | | processUpdate.setId(id); |
| | | processUpdate.setProcessInstanceId(processId); |
| | | saleMapper.updateById(processUpdate); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void onApproval(Long id, String processId, Integer status) { |
| | | MesWmScrapSaleDO sale = saleMapper.lockById(id); |
| | | if (sale == null || !Objects.equals(sale.getStatus(), 1)) return; |
| | | if (sale.getProcessInstanceId() != null && !sale.getProcessInstanceId().equals(processId)) return; |
| | | if (Objects.equals(status, BpmProcessInstanceStatusEnum.APPROVE.getStatus())) { |
| | | var lines = lineMapper.selectBySaleId(id); |
| | | lines.sort(Comparator.comparing(MesWmScrapSaleLineDO::getMaterialStockId)); |
| | | for (MesWmScrapSaleLineDO line : lines) { |
| | | var stock = stockMapper.lockById(line.getMaterialStockId()); |
| | | if (stock == null || receiptMapper.countByStockId(line.getMaterialStockId()) == 0) |
| | | throw new ServiceException(400, "åºæåºåä¸å卿已åå¨"); |
| | | BigDecimal available = zero(stock.getQuantity()).subtract(zero(stock.getFrozenQuantity())).subtract(zero(stock.getReservedQuantity())); |
| | | if (available.compareTo(line.getQuantity()) < 0) throw new ServiceException(400, "åºæå¯ç¨åºåä¸è¶³ï¼æ æ³åºåº"); |
| | | Long transactionId = transactionService.createTransaction(new MesWmTransactionSaveReqDTO() |
| | | .setType(MesWmTransactionTypeEnum.OUT.getType()).setBizType(MesBizTypeConstants.WM_SCRAP_SALE) |
| | | .setBizId(id).setBizCode(sale.getCode()).setBizLineId(line.getId()) |
| | | .setMaterialStockId(stock.getId()).setItemId(stock.getItemId()).setQuantity(line.getQuantity().negate()) |
| | | .setWarehouseId(stock.getWarehouseId()).setLocationId(stock.getLocationId()).setAreaId(stock.getAreaId()) |
| | | .setBatchId(stock.getBatchId()).setBatchCode(stock.getBatchCode())); |
| | | MesWmScrapSaleLineDO updateLine = new MesWmScrapSaleLineDO(); |
| | | updateLine.setId(line.getId()); |
| | | updateLine.setTransactionId(transactionId); |
| | | lineMapper.updateById(updateLine); |
| | | } |
| | | setStatus(id, 2); |
| | | } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.REJECT.getStatus())) { |
| | | setStatus(id, 3); |
| | | } else if (Objects.equals(status, BpmProcessInstanceStatusEnum.CANCEL.getStatus())) { |
| | | setStatus(id, 4); |
| | | } |
| | | } |
| | | |
| | | private void setStatus(Long id, int status) { |
| | | MesWmScrapSaleDO update = new MesWmScrapSaleDO(); |
| | | update.setId(id); |
| | | update.setStatus(status); |
| | | saleMapper.updateById(update); |
| | | } |
| | | private BigDecimal zero(BigDecimal value) { return value == null ? BigDecimal.ZERO : value; } |
| | | } |