feat(mes): 增加盘号字段支持和优化报工质检流程
- 在多个数据库表中新增 reel_no 字段以支持盘号追踪
- 重构报工流程,将报工与质检分离,报工审批后生成待检产出
- 新增批量逐条下发MPS功能,禁用合并下发功能
- 添加多个错误码常量用于盘号校验和MPS状态控制
- 在报工相关接口中增加盘号字段支持
- 优化报工审批逻辑,移除工序检验标识对产出入库的控制
- 修复报工质检回调逻辑,确保质量数量正确回填
- 添加报工时间字段支持,可在快速报工时指定报工时间
- 增强MPS下发权限控制,仅销售订单创建人可下发生产计划
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # ç产æ¹é ï¼æ§é»è¾ä¸å
¼å®¹ç¹ |
| | | |
| | | æ¤æ¸
åè®°å½å·²æ ¸å®çå½å代ç è¡ä¸ºï¼ä¸ä»£è¡¨ç¸å
³åè½å·²ç»åç¨ãæ¹é 宿ååºæ´æ°ç¶æï¼ç¦æ¢å°åå²å
¼å®¹é»è¾è¯¯å½ææ°ä¸å¡å
¥å£ã |
| | | |
| | | å¾
åï¼å·¥èµè®¡ç®å
¬å¼å°æªä¸å®¢æ·ç¡®è®¤ï¼æ¬æ¬¡ä¸å®æ½å·¥èµè®¡ç®ï¼å¾
确认工åºã设å¤ãå·¥æ¶å®é¢åè¾
å©å·¥æ¶ç计ç®å£å¾ååæ¹é ã |
| | | |
| | | çå·è¿½æº¯ï¼æ°ç产产åºå³ä½¿ç©ææªå¯ç¨æ®éæ¹æ¬¡ç®¡çï¼ä¹ä¼ä¸ºé空çå·å建å
é¨è¿½æº¯æ¹æ¬¡ï¼åºåä»ææ¹æ¬¡åå¼ï¼åºå页é¢åæ£è´§éæ©å¨æ¾ç¤ºçå·ï¼é宿£è´§ååºåºæ¶æ ¸å¯¹é宿ç»çå·ãå岿 çå·åºåä¿ç空å¼ï¼ä¸èªå¨æ¨æµææåï¼éå®æç»æçå·æ¶ï¼è¿ç±»æ§åºåä¸è½ç´æ¥ä½ä¸ºå¹é
åºååºåºï¼éå
æ ¸å®å¹¶å¤çå岿°æ®ãå
é¨è¿½æº¯æ¹æ¬¡åç°æåºä½æ¹æ¬¡æ··æ¾è§å约æï¼æ£å¼ä½¿ç¨åééªè¯ç°åºåºä½é
ç½®ã |
| | | |
| | | | ä½ç½® | å½åç¶æ | æ¹é 注æäºé¡¹ | |
| | | | --- | --- | --- | |
| | | | `MesProMpsController.mergeAndIssueMps` å `MesProMpsServiceImpl.mergeAndIssueMps` | å·²åç¨ï¼æ§å
¥å£è¿ååç¨æç¤º | æ°æ¥å£éæ¡æ¹éä¸åï¼åå²åå¹¶å·¥ååæç»ä»éä¿çæ¥è¯¢ååæ¶å
¼å®¹ã | |
| | | | `MesProMpsServiceImpl.generateMergeBatchNo` | å·²åç¨ï¼æ è°ç¨ | æ§åå¹¶ä¸å使ç¨çæ¹æ¬¡å·çææ¹æ³ï¼ä¸ç¨äºæ°åæ®ã | |
| | | | `MesProMpsServiceImpl.cancelMps` ä¸å
±äº«å·¥åå¤ç | åå²å
¼å®¹é»è¾ï¼å½åææ | å³ä½¿æ°è®¡åä¸ååå¹¶ä¸åï¼ä¹ä¸è½ç´æ¥å é¤ï¼å¦åå¯è½å½±ååå²åå¹¶ååæ¶ã | |
| | | | `MesProFeedbackServiceImpl` æå·¥åºæ£éªæ è¯ç´æ¥å
¥åºçæ§åæ¯ | å·²ç§»é¤ | æ°æ¥å·¥äº§åºç»ä¸å¾
æ£ï¼æ§å·¥åºæ è¯ååå²è®°å½æä¿çå
¼å®¹ï¼äººå·¥æ£éªéè¦æç¡®å
³èæ¥å·¥äº§åºè¡ã | |
| | | | å端ç产æ¥å·¥è¡¨åæå·¥åºæ è¯åæ¢æ£éªé»è¾ | å·²ç§»é¤ | æ¥å·¥è¡¨åæç»ä¸å¾
æ£è§åæäº¤ï¼ä¸å读åå·¥åºæ£éªæ è¯ã | |
| | | | `MesQcIpqcServiceImpl` æä»»å¡èªå¨éåç¬¬ä¸æ¡å¾
æ£æ¥å·¥çæ§é»è¾ | å·²ç§»é¤ | ç¬ç«æ£éªä¸æ¨æµæ¥æºï¼å
³èæ¥å·¥æ¶å¿
é¡»æç¡®æå®æ¥æºè¡ï¼é¿å
夿¬¡æ¥å·¥æä¸åçå·ä¸²åã | |
| | | | `MesProFeedbackDO.lotNumber` | é¢çç产æ¹å·å段ï¼ä¸è½è®¤å®ä¸ºçå· | çå·å¯ä¸èå´å追溯è§å确认ååå³å®æ¯å¦å¤ç¨ï¼é¿å
è¦çæ¢ææ¹å·è¯ä¹ã | |
| | | | `MesProRouteController` å·¥èºè·¯çº¿å¯¼å
¥æ¥å£ | å½åææ | å·²ææ®é导å
¥å宿´è·¯çº¿å¯¼å
¥ï¼åºå
éªè¯æ¨¡æ¿ï¼åç¡®å®æ¯å¦éè¦æ©å±ã | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- 2026-10-27 æ§è¡åæ£æ¥ï¼åªè¯»ï¼ |
| | | -- å
å¨å®¢æ·ç«¯éæ©ç®æ æ°æ®åºã |
| | | SELECT DATABASE() AS selected_database, VERSION() AS mysql_version; |
| | | -- åºæ 8 å¼ ä¸å¡è¡¨ï¼ç¼ºå¤±ç表请å¿ç»§ç»æ§è¡åæ®µèæ¬ã |
| | | SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME IN ('erp_sale_order_items', 'mes_pro_mps', 'mes_pro_work_order', 'mes_qc_ipqc', 'mes_qc_oqc', 'mes_wm_product_produce_line', 'mes_wm_product_produce_detail', 'mes_wm_batch') ORDER BY TABLE_NAME; |
| | | -- ååºå·²åå¨ççå·å段ï¼é¢æå为 varchar(128)ãå
许 NULLã |
| | | SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_COMMENT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME IN ('erp_sale_order_items', 'mes_pro_mps', 'mes_pro_work_order', 'mes_qc_ipqc', 'mes_qc_oqc', 'mes_wm_product_produce_line', 'mes_wm_product_produce_detail', 'mes_wm_batch') AND COLUMN_NAME = 'reel_no' ORDER BY TABLE_NAME; |
| | | -- æ¥è¯¢æéæ¸
çæ¶åå 餿é®åè§è²ææï¼è¯·å¨ç¡®è®¤å端å
¼å®¹åååç¬è¯ä¼°æ§è¡ã |
| | | |
| | | -- æ¹éä¸å沿ç¨åæ¡ä¸åæéï¼æ£æ¥ä¸¤ç§æé®åå
¶ç¶æã |
| | | SELECT id, name, permission, type, status, deleted FROM system_menu |
| | | WHERE permission IN ('mes:pro-mps:issue', 'mes:pro-mps:merge-issue') |
| | | ORDER BY permission, id; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- 2026-10-27 æ¬æ¬¡æ¹é ï¼çå·å段ï¼MySQLï¼ |
| | | -- å
éæ©ç®æ æ°æ®åºå¹¶æ§è¡ 20261027_00_preflight.sqlï¼æ ¸å¯¹åºåãå¤ä»½åç°æåæ®µç±»åã |
| | | -- æ¬èæ¬åªå¨å段ä¸å卿¶æ·»å åæ®µï¼é夿§è¡ä¼è·³è¿å·²åå¨å段ã |
| | | -- å·²æåååæ®µä½ç±»åæç¨éä¸åï¼ä¸ä¼èªå¨ä¿®æ£ï¼è¯·å
æ ¸å¯¹ã |
| | | -- MySQL ALTER TABLE ä¼éå¼æäº¤ï¼å¤±è´¥å鿣æ¥å®é
æ§è¡ç»æåéè¯ã |
| | | |
| | | -- erp_sale_order_items.reel_no |
| | | SET @reel_ddl = IF(EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'erp_sale_order_items' AND COLUMN_NAME = 'reel_no'), 'SELECT ''erp_sale_order_items.reel_no å·²åå¨ï¼è·³è¿'' AS message', 'ALTER TABLE `erp_sale_order_items` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT ''é宿ç»çå·'''); |
| | | PREPARE reel_stmt FROM @reel_ddl; |
| | | EXECUTE reel_stmt; |
| | | DEALLOCATE PREPARE reel_stmt; |
| | | |
| | | -- mes_pro_mps.reel_no |
| | | SET @reel_ddl = IF(EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'mes_pro_mps' AND COLUMN_NAME = 'reel_no'), 'SELECT ''mes_pro_mps.reel_no å·²åå¨ï¼è·³è¿'' AS message', 'ALTER TABLE `mes_pro_mps` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT ''é宿ç»çå·å¿«ç
§'''); |
| | | PREPARE reel_stmt FROM @reel_ddl; |
| | | EXECUTE reel_stmt; |
| | | DEALLOCATE PREPARE reel_stmt; |
| | | |
| | | -- mes_pro_work_order.reel_no |
| | | SET @reel_ddl = IF(EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'mes_pro_work_order' AND COLUMN_NAME = 'reel_no'), 'SELECT ''mes_pro_work_order.reel_no å·²åå¨ï¼è·³è¿'' AS message', 'ALTER TABLE `mes_pro_work_order` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT ''é宿ç»çå·å¿«ç
§'''); |
| | | PREPARE reel_stmt FROM @reel_ddl; |
| | | EXECUTE reel_stmt; |
| | | DEALLOCATE PREPARE reel_stmt; |
| | | |
| | | -- mes_qc_ipqc.reel_no |
| | | SET @reel_ddl = IF(EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'mes_qc_ipqc' AND COLUMN_NAME = 'reel_no'), 'SELECT ''mes_qc_ipqc.reel_no å·²åå¨ï¼è·³è¿'' AS message', 'ALTER TABLE `mes_qc_ipqc` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT ''é宿ç»çå·å¿«ç
§'''); |
| | | PREPARE reel_stmt FROM @reel_ddl; |
| | | EXECUTE reel_stmt; |
| | | DEALLOCATE PREPARE reel_stmt; |
| | | |
| | | -- mes_qc_oqc.reel_no |
| | | SET @reel_ddl = IF(EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'mes_qc_oqc' AND COLUMN_NAME = 'reel_no'), 'SELECT ''mes_qc_oqc.reel_no å·²åå¨ï¼è·³è¿'' AS message', 'ALTER TABLE `mes_qc_oqc` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT ''é宿ç»çå·å¿«ç
§'''); |
| | | PREPARE reel_stmt FROM @reel_ddl; |
| | | EXECUTE reel_stmt; |
| | | DEALLOCATE PREPARE reel_stmt; |
| | | |
| | | -- mes_wm_product_produce_line.reel_no |
| | | SET @reel_ddl = IF(EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'mes_wm_product_produce_line' AND COLUMN_NAME = 'reel_no'), 'SELECT ''mes_wm_product_produce_line.reel_no å·²åå¨ï¼è·³è¿'' AS message', 'ALTER TABLE `mes_wm_product_produce_line` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT ''é宿ç»çå·å¿«ç
§'''); |
| | | PREPARE reel_stmt FROM @reel_ddl; |
| | | EXECUTE reel_stmt; |
| | | DEALLOCATE PREPARE reel_stmt; |
| | | |
| | | -- mes_wm_product_produce_detail.reel_no |
| | | SET @reel_ddl = IF(EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'mes_wm_product_produce_detail' AND COLUMN_NAME = 'reel_no'), 'SELECT ''mes_wm_product_produce_detail.reel_no å·²åå¨ï¼è·³è¿'' AS message', 'ALTER TABLE `mes_wm_product_produce_detail` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT ''é宿ç»çå·å¿«ç
§'''); |
| | | PREPARE reel_stmt FROM @reel_ddl; |
| | | EXECUTE reel_stmt; |
| | | DEALLOCATE PREPARE reel_stmt; |
| | | |
| | | -- mes_wm_batch.reel_no |
| | | SET @reel_ddl = IF(EXISTS (SELECT 1 FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'mes_wm_batch' AND COLUMN_NAME = 'reel_no'), 'SELECT ''mes_wm_batch.reel_no å·²åå¨ï¼è·³è¿'' AS message', 'ALTER TABLE `mes_wm_batch` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT ''ç产追溯çå·'''); |
| | | PREPARE reel_stmt FROM @reel_ddl; |
| | | EXECUTE reel_stmt; |
| | | DEALLOCATE PREPARE reel_stmt; |
| | | |
| | | SET @reel_ddl = NULL; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- åªè¯»é¢è§ï¼æ¥è¯¢æéæ¸
çèæ¬å°å½±åçæé®åè§è²æææ°éã |
| | | -- 注æï¼å®æ´å端ä»å¼ç¨å
¶ä¸å¤ç§æéï¼è¯·å¿æ®æ¤ç´æ¥æ§è¡å é¤èæ¬ã |
| | | SELECT m.id, m.name, m.permission, m.parent_id, m.deleted, |
| | | COUNT(rm.id) AS role_menu_rows |
| | | FROM system_menu m |
| | | LEFT JOIN system_role_menu rm ON rm.menu_id = m.id |
| | | WHERE m.type = 3 AND m.permission IN ( |
| | | 'aftersales:problem:query', |
| | | 'aftersales:return:query', |
| | | 'aftersales:ticket:query', |
| | | 'aftersales:visit:query', |
| | | 'ai:api-key:query', |
| | | 'ai:knowledge:query', |
| | | 'ai:model:query', |
| | | 'bi:chart-config:query', |
| | | 'bi:dashboard:query', |
| | | 'bi:data-source-config:query', |
| | | 'bpm:business-attach:query', |
| | | 'bpm:category:query', |
| | | 'bpm:form:query', |
| | | 'bpm:model:query', |
| | | 'bpm:oa-leave:query', |
| | | 'bpm:oa-notice:query', |
| | | 'bpm:process-auto-approve:query', |
| | | 'bpm:process-definition:query', |
| | | 'bpm:process-expression:query', |
| | | 'bpm:process-instance-cc:query', |
| | | 'bpm:process-listener:query', |
| | | 'bpm:task:query', |
| | | 'bpm:user-group:query', |
| | | 'crm:business-status:query', |
| | | 'crm:business:query', |
| | | 'crm:clue:query', |
| | | 'crm:contact:query', |
| | | 'crm:contract-config:query', |
| | | 'crm:contract:query', |
| | | 'crm:customer-limit-config:query', |
| | | 'crm:customer-pool-config:query', |
| | | 'crm:customer:query', |
| | | 'crm:invoice:query', |
| | | 'crm:receivable-plan:query', |
| | | 'crm:receivable:query', |
| | | 'crm:sale-quotation:query', |
| | | 'crm:sales-target:query', |
| | | 'crm:statistics-customer:query', |
| | | 'crm:statistics-funnel:query', |
| | | 'crm:statistics-performance:query', |
| | | 'crm:statistics-portrait:query', |
| | | 'crm:statistics-rank:query', |
| | | 'energy:alert:query', |
| | | 'energy:analysis:query', |
| | | 'energy:record:query', |
| | | 'erp:account:query', |
| | | 'erp:finance-payment:query', |
| | | 'erp:finance-receipt:query', |
| | | 'erp:purchase-invoice:query', |
| | | 'erp:purchase-order:query', |
| | | 'erp:purchase-request:query', |
| | | 'erp:sale-order:query', |
| | | 'erp:statistics:query', |
| | | 'erp:supplier:query', |
| | | 'finance:account-subject:query', |
| | | 'finance:fixed-asset:query', |
| | | 'finance:intangible-asset:query', |
| | | 'finance:ledger:query', |
| | | 'finance:statistics:query', |
| | | 'finance:voucher:query', |
| | | 'hrm:attendance-exception:query', |
| | | 'hrm:attendance-holiday:query', |
| | | 'hrm:attendance-record:query', |
| | | 'hrm:attendance-rule:query', |
| | | 'hrm:attendance-summary:query', |
| | | 'hrm:employee-contract:query', |
| | | 'hrm:employee-schedule:query', |
| | | 'hrm:employee-social-security:query', |
| | | 'hrm:employee:query', |
| | | 'hrm:leave-type-config:query', |
| | | 'hrm:leave:query', |
| | | 'hrm:overtime:query', |
| | | 'hrm:resignation:query', |
| | | 'hrm:salary-calculation:query', |
| | | 'hrm:salary-payment:query', |
| | | 'hrm:salary-structure:query', |
| | | 'hrm:shift:query', |
| | | 'hrm:social-security-scheme:query', |
| | | 'hrm:social-security:query', |
| | | 'hrm:tax-rate-config:query', |
| | | 'hrm:transfer:query', |
| | | 'im:manager:channel-material:query', |
| | | 'im:manager:channel-message:query', |
| | | 'im:manager:channel:query', |
| | | 'im:manager:face-pack-item:query', |
| | | 'im:manager:face-pack:query', |
| | | 'im:manager:face-user-item:query', |
| | | 'im:manager:friend-request:query', |
| | | 'im:manager:friend:query', |
| | | 'im:manager:group-request:query', |
| | | 'im:manager:group:query', |
| | | 'im:manager:message:query', |
| | | 'im:manager:rtc:query', |
| | | 'im:manager:sensitive-word:query', |
| | | 'im:manager:statistics:query', |
| | | 'infra:api-access-log:query', |
| | | 'infra:api-error-log:query', |
| | | 'infra:codegen:query', |
| | | 'infra:config:query', |
| | | 'infra:data-source-config:query', |
| | | 'infra:demo01-contact:query', |
| | | 'infra:demo02-category:query', |
| | | 'infra:demo03-student:query', |
| | | 'infra:file-config:query', |
| | | 'infra:file:query', |
| | | 'infra:job:query', |
| | | 'mdm:brand:query', |
| | | 'mdm:item-category:query', |
| | | 'mdm:item:query', |
| | | 'mdm:unit:query', |
| | | 'mdm:warehouse:query', |
| | | 'member:config:query', |
| | | 'member:experience-record:query', |
| | | 'member:group:query', |
| | | 'member:level-record:query', |
| | | 'member:level:query', |
| | | 'member:tag:query', |
| | | 'member:user:query', |
| | | 'mes:cal-holiday:query', |
| | | 'mes:cal-plan:query', |
| | | 'mes:cal-team-shift:query', |
| | | 'mes:cal-team:query', |
| | | 'mes:dv-check-plan:query', |
| | | 'mes:dv-check-record:query', |
| | | 'mes:dv-machinery-type:query', |
| | | 'mes:dv-machinery:query', |
| | | 'mes:dv-mainten-record:query', |
| | | 'mes:dv-metering-check:query', |
| | | 'mes:dv-metering:query', |
| | | 'mes:dv-repair:query', |
| | | 'mes:dv-spare:query', |
| | | 'mes:dv-subject:query', |
| | | 'mes:home:query', |
| | | 'mes:md-item-type:query', |
| | | 'mes:md-item:query', |
| | | 'mes:md-unit-measure:query', |
| | | 'mes:md-workshop:query', |
| | | 'mes:md-workstation:query', |
| | | 'mes:pd-archive:query', |
| | | 'mes:pd-document-audit:query', |
| | | 'mes:pd-document:query', |
| | | 'mes:pd-process-param-template:query', |
| | | 'mes:pd-process-param:query', |
| | | 'mes:pd-project:query', |
| | | 'mes:pro-andon-config:query', |
| | | 'mes:pro-andon-record:query', |
| | | 'mes:pro-card:query', |
| | | 'mes:pro-feedback:query', |
| | | 'mes:pro-mps:query', |
| | | 'mes:pro-process:query', |
| | | 'mes:pro-route:query', |
| | | 'mes:pro-task-issue:query', |
| | | 'mes:pro-task:query', |
| | | 'mes:pro-work-order:query', |
| | | 'mes:pro-workrecord:query', |
| | | 'mes:qc-defect:query', |
| | | 'mes:qc-indicator:query', |
| | | 'mes:qc-inspection-ledger:query', |
| | | 'mes:qc-ipqc:query', |
| | | 'mes:qc-iqc:query', |
| | | 'mes:qc-ncr:query', |
| | | 'mes:qc-oqc:query', |
| | | 'mes:qc-pending-inspect:query', |
| | | 'mes:qc-rqc:query', |
| | | 'mes:qc-template:query', |
| | | 'mes:tm-tool-type:query', |
| | | 'mes:tm-tool:query', |
| | | 'mes:wm-arrival-notice:query', |
| | | 'mes:wm-barcode-config:query', |
| | | 'mes:wm-barcode:query', |
| | | 'mes:wm-batch:query', |
| | | 'mes:wm-item-receipt:query', |
| | | 'mes:wm-material-stock:query', |
| | | 'mes:wm-misc-issue:query', |
| | | 'mes:wm-outsource-issue:query', |
| | | 'mes:wm-outsource-receipt:query', |
| | | 'mes:wm-package:query', |
| | | 'mes:wm-product-issue:query', |
| | | 'mes:wm-product-produce:query', |
| | | 'mes:wm-product-receipt:query', |
| | | 'mes:wm-product-sales:query', |
| | | 'mes:wm-return-issue:query', |
| | | 'mes:wm-return-sales:query', |
| | | 'mes:wm-return-vendor:query', |
| | | 'mes:wm-sales-notice:query', |
| | | 'mes:wm-sn:query', |
| | | 'mes:wm-stock-taking-plan:query', |
| | | 'mes:wm-stock-taking-task:query', |
| | | 'mes:wm-transaction:query', |
| | | 'mes:wm-transfer:query', |
| | | 'mes:wm-warehouse:query', |
| | | 'mes:wm:misc-receipt:query', |
| | | 'point:record:query', |
| | | 'point:sign-in-config:query', |
| | | 'point:sign-in-record:query', |
| | | 'qc-report:instance:query', |
| | | 'qc-report:template:query', |
| | | 'srm:bid-award:query', |
| | | 'srm:bid-evaluation:query', |
| | | 'srm:bid-open:query', |
| | | 'srm:statistics:query', |
| | | 'srm:supplier-apply:query', |
| | | 'srm:supplier-certificate:query', |
| | | 'srm:supplier-contact:query', |
| | | 'srm:supplier-evaluation:query', |
| | | 'srm:supplier-quote:query', |
| | | 'srm:supplier:query', |
| | | 'srm:tender-bid:query', |
| | | 'srm:tender-config:query', |
| | | 'srm:tender-project:query', |
| | | 'system:dept:query', |
| | | 'system:dict:query', |
| | | 'system:login-log:query', |
| | | 'system:mail-account:query', |
| | | 'system:mail-log:query', |
| | | 'system:mail-template:query', |
| | | 'system:menu:query', |
| | | 'system:notice:query', |
| | | 'system:notify-message:query', |
| | | 'system:notify-template:query', |
| | | 'system:oauth2-client:query', |
| | | 'system:operate-log:query', |
| | | 'system:post:query', |
| | | 'system:role:query', |
| | | 'system:sms-channel:query', |
| | | 'system:sms-log:query', |
| | | 'system:sms-template:query', |
| | | 'system:social-client:query', |
| | | 'system:social-user:query', |
| | | 'system:storage-attachment:query', |
| | | 'system:tenant-package:query', |
| | | 'system:tenant:query', |
| | | 'system:user:query' |
| | | ) |
| | | GROUP BY m.id, m.name, m.permission, m.parent_id, m.deleted |
| | | ORDER BY m.permission, m.id; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- çå·ç±éå®è®¢å产åæç»æå·¥å½å
¥ï¼è®¡ååå·¥åä¿åä¸åæ¶çå¿«ç
§ã |
| | | -- æ§è¡å确认ååå°ä¸åå¨å¹¶å¤ä»½ï¼åå²è®°å½ä¿ç NULLï¼ä¸æ¨æµçå·ã |
| | | ALTER TABLE `erp_sale_order_items` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT 'é宿ç»çå·'; |
| | | ALTER TABLE `mes_pro_mps` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT 'é宿ç»çå·å¿«ç
§'; |
| | | ALTER TABLE `mes_pro_work_order` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT 'é宿ç»çå·å¿«ç
§'; |
| | | ALTER TABLE `mes_qc_ipqc` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT 'é宿ç»çå·å¿«ç
§'; |
| | | ALTER TABLE `mes_qc_oqc` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT 'é宿ç»çå·å¿«ç
§'; |
| | | ALTER TABLE `mes_wm_product_produce_line` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT 'é宿ç»çå·å¿«ç
§'; |
| | | ALTER TABLE `mes_wm_product_produce_detail` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT 'é宿ç»çå·å¿«ç
§'; |
| | | ALTER TABLE `mes_wm_batch` ADD COLUMN `reel_no` varchar(128) DEFAULT NULL COMMENT 'ç产追溯çå·'; |
| | |
| | | @Schema(description = "产åç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long productId; |
| | | |
| | | @Schema(description = "éå®äº§åæç»æå·¥å½å
¥ççå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "产ååä½ç¼å·", example = "1") |
| | | private Long productUnitId; |
| | | |
| | |
| | | @Schema(description = "产åç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long productId; |
| | | |
| | | @Schema(description = "éå®äº§åæç»æå·¥å½å
¥ççå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "产ååä½ç¼å·", example = "1") |
| | | private Long productUnitId; |
| | | |
| | |
| | | @Schema(description = "订åç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "éå®è®¢ååå»ºäººç¨æ·ç¼å·") |
| | | private String creator; |
| | | |
| | | @Schema(description = "éå®è®¢åå·", requiredMode = Schema.RequiredMode.REQUIRED, example = "SO001") |
| | | private String no; |
| | | |
| | |
| | | @Schema(description = "ç©æç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long productId; |
| | | |
| | | @Schema(description = "éå®äº§åæç»æå·¥å½å
¥ççå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "ç©æåç§°", example = "产åA") |
| | | private String productName; |
| | | |
| | |
| | | @Schema(description = "产åç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") |
| | | private Long productId; |
| | | |
| | | @Schema(description = "éå®äº§åæç»æå·¥å½å
¥ççå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "产ååä½åä½", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") |
| | | private Long productUnitId; |
| | | |
| | |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Size; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | |
| | | @NotNull(message = "产åç¼å·ä¸è½ä¸ºç©º") |
| | | private Long productId; |
| | | |
| | | @Schema(description = "éå®äº§åæç»æå·¥å½å
¥ççå·") |
| | | @Size(max = 128, message = "çå·ä¸è½è¶
è¿128个å符") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "产ååä½åä½", requiredMode = Schema.RequiredMode.REQUIRED, example = "3113") |
| | | @NotNull(message = "产ååä½åä½ä¸è½ä¸ºç©º") |
| | | private Long productUnitId; |
| | |
| | | |
| | | } |
| | | |
| | | } |
| | | } |
| | |
| | | * å
³è {@link cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO#getId()} |
| | | */ |
| | | private Long productId; |
| | | |
| | | /** éå®äº§åæç»æå·¥å½å
¥ççå·å¿«ç
§ã */ |
| | | private String reelNo; |
| | | /** |
| | | * 计éåä½ç¼å· |
| | | * |
| | |
| | | createReqDTO.setSaleOrderId(saleOrder.getId()); |
| | | createReqDTO.setSaleOrderNo(saleOrder.getNo()); |
| | | createReqDTO.setSaleOrderItemId(item.getId()); |
| | | createReqDTO.setReelNo(item.getReelNo()); |
| | | createReqDTO.setMdmItemId(item.getProductId()); |
| | | createReqDTO.setProductName(productName); |
| | | createReqDTO.setQuantity(item.getCount()); |
| | |
| | | @Schema(description = "éå®è®¢åæç»ID", example = "200") |
| | | private Long saleOrderItemId; |
| | | |
| | | @Schema(description = "éå®æç»æå·¥å¡«åççå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "MDM ç©æIDï¼éå®è®¢åæç»ç productIdï¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") |
| | | private Long mdmItemId; |
| | | |
| | |
| | | @Schema(description = "夿³¨", example = "èªå¨çæ") |
| | | private String remark; |
| | | |
| | | } |
| | | } |
| | |
| | | @Data |
| | | public class MesProMpsRespDTO { |
| | | |
| | | @Schema(description = "çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "MPS ç¼å·", example = "1024") |
| | | private Long id; |
| | | |
| | |
| | | @Schema(description = "åå¹¶æ¹æ¬¡å·", example = "MERGE-001") |
| | | private String mergeBatchNo; |
| | | |
| | | } |
| | | } |
| | |
| | | createReqDTO.getQuantity(), |
| | | createReqDTO.getClientId(), |
| | | createReqDTO.getRequestDate(), |
| | | createReqDTO.getRemark() |
| | | createReqDTO.getRemark(), |
| | | createReqDTO.getReelNo() |
| | | ); |
| | | } |
| | | |
| | |
| | | return mpsService.checkAndFinishMps(mpsId); |
| | | } |
| | | |
| | | } |
| | | } |
| | |
| | | findAndThen(processCheckMap, vo.getProcessId(), vo::setCheckFlag)); |
| | | // å·¥å |
| | | findAndThen(workOrderMap, vo.getWorkOrderId(), wo -> |
| | | vo.setWorkOrderCode(wo.getCode()).setWorkOrderName(wo.getName())); |
| | | vo.setWorkOrderCode(wo.getCode()).setWorkOrderName(wo.getName()).setReelNo(wo.getReelNo())); |
| | | // ä»»å¡ |
| | | findAndThen(taskMap, vo.getTaskId(), task -> |
| | | vo.setTaskCode(task.getCode())); |
| | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - MES æ¹éæ¥å·¥ Request VO") |
| | |
| | | @NotNull(message = "æ¥å·¥æ°éä¸è½ä¸ºç©º") |
| | | private BigDecimal feedbackQuantity; |
| | | |
| | | @Schema(description = "æ¥å·¥æ¶é´ï¼åºç¨å°ææä»»å¡ï¼ï¼ä¸å¡«æ¶ä½¿ç¨å½åæ¶é´") |
| | | private LocalDateTime feedbackTime; |
| | | |
| | | @Schema(description = "夿³¨", example = "æ¹éæ¥å·¥") |
| | | private String remark; |
| | | } |
| | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - MES å¿«éæ¥å·¥ Request VO") |
| | |
| | | @NotNull(message = "æ¥å·¥æ°éä¸è½ä¸ºç©º") |
| | | private BigDecimal feedbackQuantity; |
| | | |
| | | @Schema(description = "æ¥å·¥æ¶é´ï¼ä¸å¡«æ¶ä½¿ç¨å½åæ¶é´") |
| | | private LocalDateTime feedbackTime; |
| | | |
| | | @Schema(description = "å·¥èºåæ°å¼") |
| | | private List<MesProFeedbackSaveReqVO.ParamValue> paramValues; |
| | | |
| | |
| | | @ExcelProperty("å·¥åç¼ç ") |
| | | private String workOrderCode; |
| | | |
| | | @Schema(description = "é宿ç»çå·") |
| | | @ExcelProperty("çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "å·¥ååç§°", example = "åä¸èºä¸å") |
| | | @ExcelProperty("å·¥ååç§°") |
| | | private String workOrderName; |
| | |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.ArrayList; |
| | | import jakarta.validation.constraints.NotEmpty; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import lombok.Data; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.PRO_MPS_MERGE_ISSUE_DISABLED; |
| | | |
| | | @Tag(name = "管çåå° - MES ç产主计å") |
| | | @RestController |
| | | @RequestMapping("/mes/pro-mps") |
| | | @Validated |
| | | @Slf4j |
| | | public class MesProMpsController { |
| | | |
| | | @Resource |
| | |
| | | @Operation(summary = "åå¹¶ä¸åç产主计å") |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-mps:merge-issue')") |
| | | public CommonResult<Long> mergeAndIssueMps(@RequestBody List<Long> mpsIds) { |
| | | return success(mpsService.mergeAndIssueMps(mpsIds)); |
| | | // æ§å
¥å£ä¿çç¨äºæç¡®æç¤ºæ§å®¢æ·ç«¯å级ï¼ç¦æ¢ç»è¿æ°çéæ¡ä¸åæéæ ¡éªã |
| | | throw exception(PRO_MPS_MERGE_ISSUE_DISABLED); |
| | | } |
| | | |
| | | @PostMapping("/batch-issue") |
| | | @Operation(summary = "æ¹ééæ¡ä¸åç产主计å") |
| | | @PreAuthorize("@ss.hasPermission('mes:pro-mps:issue')") |
| | | public CommonResult<List<BatchIssueResult>> batchIssueMps(@RequestBody @NotEmpty List<@NotNull Long> ids) { |
| | | List<BatchIssueResult> results = new ArrayList<>(); |
| | | for (Long id : ids) { |
| | | BatchIssueResult result = new BatchIssueResult(); |
| | | result.setMpsId(id); |
| | | try { |
| | | result.setWorkOrderId(mpsService.issueMps(id)); |
| | | } catch (ServiceException ex) { |
| | | result.setError(ex.getMessage()); |
| | | } catch (Exception ex) { |
| | | log.error("[batchIssueMps] ä¸å失败ï¼mpsId={}", id, ex); |
| | | result.setError("ä¸å失败ï¼è¯·è系管çåæ¥çæ¥å¿"); |
| | | } |
| | | results.add(result); |
| | | } |
| | | return success(results); |
| | | } |
| | | |
| | | @Data |
| | | public static class BatchIssueResult { |
| | | private Long mpsId; |
| | | private Long workOrderId; |
| | | private String error; |
| | | } |
| | | |
| | | @PostMapping("/cancel") |
| | |
| | | }); |
| | | } |
| | | |
| | | } |
| | | } |
| | |
| | | @Schema(description = "产åç¼å·", example = "100") |
| | | private Long productId; |
| | | |
| | | @Schema(description = "éå®äº§åæç»æå·¥å½å
¥ççå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "产åç¼ç ", example = "P-001") |
| | | @ExcelProperty("产åç¼ç ") |
| | | private String productCode; |
| | |
| | | @NotNull(message = "产åä¸è½ä¸ºç©º") |
| | | private Long productId; |
| | | |
| | | @Schema(description = "éå®äº§åæç»æå·¥å½å
¥ççå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "计åç产æ°é", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") |
| | | @NotNull(message = "计åç产æ°éä¸è½ä¸ºç©º") |
| | | private BigDecimal quantity; |
| | |
| | | @Schema(description = "产åç¼å·", example = "100") |
| | | private Long productId; |
| | | |
| | | @Schema(description = "éå®äº§åæç»æå·¥å½å
¥ççå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "产ååç§°", example = "çµè·¯æ¿ A") |
| | | @ExcelProperty("产ååç§°") |
| | | private String productName; |
| | |
| | | @NotNull(message = "产åä¸è½ä¸ºç©º") |
| | | private Long productId; |
| | | |
| | | @Schema(description = "éå®äº§åæç»æå·¥å½å
¥ççå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "ç产æ°é", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.00") |
| | | @NotNull(message = "ç产æ°éä¸è½ä¸ºç©º") |
| | | private BigDecimal quantity; |
| | |
| | | @Schema(description = "ç产工å ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") |
| | | private Long workOrderId; |
| | | |
| | | @Schema(description = "é宿ç»çå·") |
| | | @ExcelProperty("çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "å·¥åç¼å·", example = "WO20250101001") |
| | | @ExcelProperty("å·¥åç¼å·") |
| | | private String workOrderCode; |
| | |
| | | @Schema(description = "æ¥æºåæ®ç¼å·", example = "DOC20250101001") |
| | | private String sourceDocCode; |
| | | |
| | | @Schema(description = "çå·") |
| | | @ExcelProperty("çå·") |
| | | private String reelNo; |
| | | |
| | | // ========== å®¢æ· ========== |
| | | |
| | | @Schema(description = "å®¢æ· ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "10") |
| | |
| | | @Schema(description = "æ¥æºåæ®è¡ ID", example = "300") |
| | | private Long sourceLineId; |
| | | |
| | | @Schema(description = "çå·ï¼å
³èéå®åºåºæ¶ç±é宿ç»å¸¦å
¥") |
| | | @Size(max = 128, message = "çå·ä¸è½è¶
è¿128个å符") |
| | | private String reelNo; |
| | | |
| | | // ========== å®¢æ· ========== |
| | | |
| | | @Schema(description = "å®¢æ· IDï¼å¯ä¸ºç©º", example = "10") |
| | |
| | | @Schema(description = "ç产工å ID", example = "10") |
| | | private Long workOrderId; |
| | | |
| | | @Schema(description = "ç产工å对åºçé宿ç»çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "å·¥ä½ç« ID", example = "20") |
| | | private Long workstationId; |
| | | |
| | |
| | | @Schema(description = "è®°å½æ¶é´") |
| | | private LocalDateTime recordTime; |
| | | |
| | | } |
| | | } |
| | |
| | | */ |
| | | private String batchCode; |
| | | |
| | | /** çäº§æ¥æºçå·ï¼é空æ¶å³ä½¿ç©ææªå¯ç¨æ®éæ¹æ¬¡ç®¡çä¹ä¿çè¿½æº¯æ¹æ¬¡ã */ |
| | | private String reelNo; |
| | | |
| | | /** |
| | | * çäº§æ¥æ |
| | | */ |
| | |
| | | public class MesWmMaterialStockController { |
| | | |
| | | @Resource |
| | | private cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService batchService; |
| | | |
| | | @Resource |
| | | private MesWmMaterialStockService materialStockService; |
| | | @Resource |
| | | private MdmItemApi mdmItemApi; |
| | |
| | | Map<Long, SrmSupplierRespDTO> vendorMap = srmSupplierApi.getSupplierList( |
| | | convertSet(list, MesWmMaterialStockDO::getVendorId)).getCheckedData().stream() |
| | | .collect(Collectors.toMap(SrmSupplierRespDTO::getId, s -> s)); |
| | | Map<Long, cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO> batchMap = |
| | | batchService.getBatchMap(convertSet(list, MesWmMaterialStockDO::getBatchId)); |
| | | // 2. æå»ºç»æ |
| | | return BeanUtils.toBean(list, MesWmMaterialStockRespVO.class, vo -> { |
| | | 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()); |
| | | MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | |
| | | @ExcelProperty("æ¹æ¬¡å·") |
| | | private String batchCode; |
| | | |
| | | @Schema(description = "ç产追溯çå·ï¼åå²åºåå¯è½ä¸ºç©º") |
| | | @ExcelProperty("çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "ä»åºç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | private Long warehouseId; |
| | | |
| | |
| | | @Schema(description = "æ¹æ¬¡å·", example = "BATCH20260101001") |
| | | private String batchCode; |
| | | |
| | | @Schema(description = "é宿ç»çå·") |
| | | private String reelNo; |
| | | |
| | | @Schema(description = "æææ") |
| | | private LocalDateTime expireDate; |
| | | |
| | |
| | | */ |
| | | private Long productId; |
| | | |
| | | /** éå®äº§åæç»æå·¥å½å
¥ççå·å¿«ç
§ã */ |
| | | private String reelNo; |
| | | |
| | | /** |
| | | * 计åç产æ°é |
| | | */ |
| | |
| | | * å
³è {@link MesMdItemDO#getId()} |
| | | */ |
| | | private Long productId; |
| | | |
| | | /** éå®äº§åæç»æå·¥å½å
¥ççå·å¿«ç
§ã */ |
| | | private String reelNo; |
| | | /** |
| | | * ç产æ°é |
| | | */ |
| | |
| | | * å
³è {@link MesProWorkOrderDO#getId()} |
| | | */ |
| | | private Long workOrderId; |
| | | |
| | | /** å建æ£éªåæ¶ä»ç产工å带å
¥çéå®çå·å¿«ç
§ã */ |
| | | private String reelNo; |
| | | /** |
| | | * çäº§ä»»å¡ ID |
| | | * |
| | |
| | | */ |
| | | private String sourceDocCode; |
| | | |
| | | /** é宿ç»çå·å¿«ç
§ï¼ç¬ç«æ£éªå¯ç±è´¨æ£äººåå¡«åã */ |
| | | private String reelNo; |
| | | |
| | | // ========== å®¢æ· ========== |
| | | |
| | | /** |
| | |
| | | @AllArgsConstructor |
| | | public class MesWmBatchDO extends BaseDO { |
| | | |
| | | /** é宿ç»çå·ï¼éç©ºæ¶æ¹æ¬¡æ¿æ
çå·åºå追溯ã */ |
| | | private String reelNo; |
| | | |
| | | /** |
| | | * æ¹æ¬¡ID |
| | | */ |
| | |
| | | * æ¹æ¬¡å· |
| | | */ |
| | | private String batchCode; |
| | | |
| | | /** ä»ç产产åºè¡ç»§æ¿ççå·å¿«ç
§ã */ |
| | | private String reelNo; |
| | | /** |
| | | * ä»åº ID |
| | | * |
| | |
| | | * æ¹æ¬¡å· |
| | | */ |
| | | private String batchCode; |
| | | |
| | | /** ä»ç产工å带å
¥çé宿ç»çå·å¿«ç
§ã */ |
| | | private String reelNo; |
| | | /** |
| | | * è¿ææ¥æ |
| | | */ |
| | |
| | | @Mapper |
| | | public interface MesProFeedbackMapper extends BaseMapperX<MesProFeedbackDO> { |
| | | |
| | | default MesProFeedbackDO selectByIdForUpdate(Long id) { |
| | | return selectOne(new LambdaQueryWrapperX<MesProFeedbackDO>() |
| | | .eq(MesProFeedbackDO::getId, id).last("FOR UPDATE")); |
| | | } |
| | | |
| | | default PageResult<MesProFeedbackDO> selectPage(MesProFeedbackPageReqVO reqVO) { |
| | | // 彿任å¡ç¼ç æ¨¡ç³æ¥è¯¢æ¶ï¼éè¦ JOIN ä»»å¡è¡¨ |
| | | if (StrUtil.isNotBlank(reqVO.getTaskCode())) { |
| | |
| | | @Mapper |
| | | public interface MesProMpsMapper extends BaseMapperX<MesProMpsDO> { |
| | | |
| | | default MesProMpsDO selectByIdForUpdate(Long id) { |
| | | return selectOne(new LambdaQueryWrapperX<MesProMpsDO>() |
| | | .eq(MesProMpsDO::getId, id).last("FOR UPDATE")); |
| | | } |
| | | |
| | | default PageResult<MesProMpsDO> selectPage(MesProMpsPageReqVO reqVO) { |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<MesProMpsDO>() |
| | | .likeIfPresent(MesProMpsDO::getCode, reqVO.getCode()) |
| | |
| | | .setSql("quantity_produced = IFNULL(quantity_produced, 0) + " + incrQuantity)); |
| | | } |
| | | |
| | | } |
| | | } |
| | |
| | | default MesWmBatchDO selectFirst(MesWmBatchDO batch) { |
| | | LambdaQueryWrapper<MesWmBatchDO> query = new LambdaQueryWrapper<>(); |
| | | query.eq(MesWmBatchDO::getItemId, batch.getItemId()); |
| | | if (ObjUtil.isNull(batch.getReelNo())) { |
| | | query.isNull(MesWmBatchDO::getReelNo); |
| | | } else { |
| | | query.eq(MesWmBatchDO::getReelNo, batch.getReelNo()); |
| | | } |
| | | if (ObjUtil.isNull(batch.getVendorId())) { |
| | | query.isNull(MesWmBatchDO::getVendorId); |
| | | } else { |
| | |
| | | ErrorCode WM_BATCH_QUALITY_STATUS_REQUIRED = new ErrorCode(1_040_717_013, "æ¹æ¬¡é
ç½®è¦æ±è´¨éç¶æä¸è½ä¸ºç©º"); |
| | | ErrorCode WM_BATCH_NOT_EXISTS = new ErrorCode(1_040_717_014, "æ¹æ¬¡ä¸åå¨"); |
| | | ErrorCode WM_BATCH_ITEM_MISMATCH = new ErrorCode(1_040_717_015, "æ¹æ¬¡ä¸å±äºå½åç©æ"); |
| | | ErrorCode WM_BATCH_REEL_MISMATCH = new ErrorCode(1_040_717_018, "æéæ¹æ¬¡ççå·ä¸ç产订åçå·ä¸ä¸è´"); |
| | | ErrorCode WM_BATCH_CLIENT_MISMATCH = new ErrorCode(1_040_717_016, "æ¹æ¬¡ä¸å±äºå½å客æ·"); |
| | | ErrorCode WM_BATCH_VENDOR_MISMATCH = new ErrorCode(1_040_717_017, "æ¹æ¬¡ä¸å±äºå½åä¾åºå"); |
| | | |
| | |
| | | ErrorCode PRO_MPS_NOT_ISSUED = new ErrorCode(1_040_509_002, "åªæå·²ä¸åç¶æç MPS æè½æ§è¡æ¤æä½"); |
| | | ErrorCode PRO_MPS_ALREADY_ISSUED = new ErrorCode(1_040_509_003, "MPS å·²ä¸åï¼æ æ³ä¿®æ¹"); |
| | | ErrorCode PRO_MPS_MERGE_AT_LEAST_TWO = new ErrorCode(1_040_509_004, "åå¹¶ä¸åè³å°éè¦éæ©ä¸¤æ¡ MPS"); |
| | | ErrorCode PRO_MPS_ISSUE_FORBIDDEN = new ErrorCode(1_040_509_010, "ä»
éå®è®¢åå建人æè¶
级管çåå¯ä»¥ä¸åç产计å"); |
| | | ErrorCode PRO_MPS_MERGE_ISSUE_DISABLED = new ErrorCode(1_040_509_011, "åå¹¶ä¸åå·²åç¨ï¼è¯·ä½¿ç¨æ¹éä¸å"); |
| | | ErrorCode PRO_MPS_MERGE_PRODUCT_MISMATCH = new ErrorCode(1_040_509_005, "åå¹¶ä¸åç MPS å¿
é¡»æ¯ç¸å产å"); |
| | | ErrorCode PRO_MPS_CODE_DUPLICATE = new ErrorCode(1_040_509_006, "MPS ç¼ç å·²åå¨"); |
| | | ErrorCode PRO_MPS_HAS_WORK_ORDER = new ErrorCode(1_040_509_007, "MPS å·²å
³èç产工åï¼æ æ³å é¤"); |
| | |
| | | ErrorCode QC_IPQC_SOURCE_LINE_NOT_BELONG = new ErrorCode(1_040_604_009, "æ¥æºåæ®è¡ä¸å±äºè¯¥æ¥å·¥å"); |
| | | ErrorCode QC_IPQC_SOURCE_LINE_REQUIRED = new ErrorCode(1_040_604_010, "æ¥æºåæ®ç±»å为æ¥å·¥æ¶ï¼æ¥æºäº§åºè¡ ID ä¸è½ä¸ºç©º"); |
| | | ErrorCode QC_IPQC_SOURCE_LINE_NOT_PENDING = new ErrorCode(1_040_604_011, "æ¥æºäº§åºè¡ä¸æ¯å¾
æ£éªç¶æ"); |
| | | ErrorCode QC_IPQC_WORK_ORDER_STATUS_INVALID = new ErrorCode(1_040_604_012, "ä»
已确认æå·²å®æçç产工åå¯å建è¿ç¨æ£éª"); |
| | | // ========== MES è´¨é管ç-è¿ç¨æ£éªè¡ï¼1-040-604-100ï¼ ========== |
| | | ErrorCode QC_IPQC_LINE_NOT_EXISTS = new ErrorCode(1_040_604_100, "è¿ç¨æ£éªè¡ä¸åå¨"); |
| | | |
| | |
| | | // ========== MES ä»åºç®¡ç-ç产å
¥åºåï¼1-040-709-000ï¼ ========== |
| | | ErrorCode WM_PRODUCT_PRODUCE_NOT_EXISTS = new ErrorCode(1_040_709_000, "ç产å
¥åºåä¸åå¨"); |
| | | ErrorCode WM_PRODUCT_PRODUCE_STATUS_INVALID = new ErrorCode(1_040_709_001, "ç产å
¥åºåç¶æä¸æ£ç¡®ï¼æ æ³æ§è¡è¯¥æä½"); |
| | | ErrorCode WM_PRODUCT_PRODUCE_QUALITY_PENDING = new ErrorCode(1_040_709_006, "ç产产åºå°æªæ£éªï¼ä¸è½å
¥åº"); |
| | | ErrorCode WM_PRODUCT_PRODUCE_INSPECTION_INVALID = new ErrorCode(1_040_709_007, "æ£éªç»æä¸å¾
æ£äº§åºä¸å¹é
"); |
| | | ErrorCode WM_PRODUCT_PRODUCE_NO_LINE = new ErrorCode(1_040_709_002, "ç产å
¥åºåè³å°éè¦ä¸æ¡è¡æ°æ®"); |
| | | ErrorCode WM_PRODUCT_PRODUCE_DETAIL_QUANTITY_MISMATCH = new ErrorCode(1_040_709_003, "ç产å
¥åºåè¡æ°éä¸æç»æ°éä¸ä¸è´"); |
| | | ErrorCode WM_PRODUCT_PRODUCE_CANCEL_NOT_ALLOWED = new ErrorCode(1_040_709_004, "ç产å
¥åºå已宿æå·²åæ¶ï¼æ æ³åæ¶"); |
| | |
| | | ErrorCode WM_PRODUCT_SALES_LINE_QUANTITY_INVALID = new ErrorCode(1_040_714_014, "åºåºæ°éå¿
é¡»å¤§äº 0"); |
| | | ErrorCode WM_PRODUCT_SALES_DETAIL_LINE_NOT_MATCH = new ErrorCode(1_040_714_015, "æ£è´§æç»ä¸å±äºæå®çéå®åºåºå"); |
| | | ErrorCode WM_PRODUCT_SALES_DETAIL_ITEM_MISMATCH = new ErrorCode(1_040_714_016, "æ£è´§æç»çç©æä¸éå®åºåºåè¡çç©æä¸ä¸è´"); |
| | | ErrorCode WM_PRODUCT_SALES_REEL_MISMATCH = new ErrorCode(1_040_714_034, "æéåºåçå·ä¸é宿ç»çå·ä¸ä¸è´ï¼è¯·éæ°éæ©å¯è¿½æº¯åºå"); |
| | | ErrorCode WM_PRODUCT_SALES_REEL_SOURCE_MISSING = new ErrorCode(1_040_714_035, "éå®è®¢åæç»ä¸åå¨ï¼æ æ³æ ¸å¯¹åºåçå·"); |
| | | ErrorCode WM_PRODUCT_SALES_LINE_SALES_NOTICE_LINE_REQUIRED = new ErrorCode(1_040_714_017, |
| | | "åºåºåå
³èäºåè´§éç¥åï¼å¿
须鿩åè´§éç¥åè¡"); |
| | | ErrorCode WM_PRODUCT_SALES_LINE_SALES_NOTICE_LINE_NOT_ALLOWED = new ErrorCode(1_040_714_018, |
| | |
| | | * <ol> |
| | | * <li>æ ¡éªæ¥å·¥åç¶æåæ°é</li> |
| | | * <li>çæç©ææ¶èåï¼æ£å线边åºç©æ</li> |
| | | * <li>æ ¹æ®å·¥åºé
ç½®ï¼å
³é®å·¥åº & æ¯å¦éè¦æ£éªï¼åæ
åµå¤çï¼ |
| | | * <ul> |
| | | * <li><b>å
³é®å·¥åº + éè¦æ£éª</b>ï¼çæäº§åºåï¼è´¨éç¶æ=å¾
æ£éªï¼ï¼æ¥å·¥åç¶æ â å¾
æ£éªï¼çå¾
è´¨æ£å®æåè°</li> |
| | | * <li><b>å
³é®å·¥åº + æ 鿣éª</b>ï¼çæäº§åºåï¼æåæ ¼/ä¸åæ ¼æè¡ï¼ï¼æ§è¡äº§åå
¥åºï¼æ´æ°ä»»å¡/å·¥åå·²ç产æ°éï¼æ¥å·¥åç¶æ â 已宿</li> |
| | | * <li><b>éå
³é®å·¥åº</b>ï¼ä¸çæäº§åºåï¼ä¸æ´æ°ä»»å¡/工忰éï¼æ¥å·¥åç¶æ â 已宿</li> |
| | | * </ul> |
| | | * </li> |
| | | * <li>çæå¾
æ£äº§åºï¼ç´¯è®¡ç产æ°éï¼äººå·¥è´¨æ£å®æåååè´¨éæ°éå¹¶å¤çå
¥åº</li> |
| | | * </ol> |
| | | * |
| | | * @param reqVO 审æ¹è¯·æ±ï¼å«æ¥å·¥åç¼å·åå¯éæ¹æ¬¡å±æ§ï¼ |
| | |
| | | * IPQC 宿ååè°ï¼å®ææ¥å·¥åå¹¶æ´æ°ä»»å¡/å·¥åè¿åº¦ |
| | | * |
| | | * <p>ç± IPQC æ£éªå®ææ¶è°ç¨ï¼å°æ¥å·¥åç¶æä»å¾
æ£éªæ¹ä¸ºå·²å®æï¼ |
| | | * å¹¶æ ¹æ®æ£éªç»æåååæ ¼/ä¸åæ ¼/åºåæ°éï¼åæ¶æ´æ°ä»»å¡/å·¥åçå·²ç产æ°éã |
| | | * å¹¶æ ¹æ®æ£éªç»æåååæ ¼/ä¸åæ ¼/åºåæ°éï¼å·²ç产æ°é卿¥å·¥å®¡æ¹æ¶ç´¯è®¡ã |
| | | * |
| | | * @param feedbackId æ¥å·¥è®°å½ ID |
| | | * @param sourceLineId æ¥æºäº§åºè¡ IDï¼ç¨äºç´æ¥å®ä½å¾
æ£è¡ï¼ |
| | |
| | | // 1.3 æ ¡éªä»»å¡æªå®æ |
| | | taskService.validateTaskNotFinished(feedback.getTaskId()); |
| | | |
| | | // 2.1 æ¥è¯¢å·¥åºçå
³é®å·¥åºæ è¯ + æ£éªæ è¯ï¼éå¨ uncheckQuantity æ ¡éªä¹åï¼å 为质æ£å·¥åºå
许 uncheckQuantity > 0ï¼ |
| | | // å·¥åºæ£éªæ è¯ä¸åæ§å¶äº§åºå
¥åºï¼æææ°æ¥å·¥äº§åºåé人工质æ£ã |
| | | MesProRouteProcessDO routeProcess = routeProcessService.getRouteProcessByRouteIdAndProcessId( |
| | | feedback.getRouteId(), feedback.getProcessId()); |
| | | boolean keyFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getKeyFlag()); |
| | | boolean checkFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getCheckFlag()); |
| | | // 2.2 éè´¨æ£å·¥åºï¼åæ ¼å/ä¸è¯åæ°éç±è´¨æ£ç¡®å®ï¼æ¬å·¥åºä¸ç»è¿è´¨æ£ï¼æ¥å·¥æªå¡«åæ¶é»è®¤å
¨é¨ä¸ºåæ ¼å |
| | | if (!checkFlag |
| | | && feedback.getQualifiedQuantity() == null |
| | | && feedback.getUnqualifiedQuantity() == null) { |
| | | feedback.setQualifiedQuantity(feedback.getFeedbackQuantity()); |
| | | feedback.setUnqualifiedQuantity(BigDecimal.ZERO); |
| | | feedbackMapper.updateById(new MesProFeedbackDO().setId(feedback.getId()) |
| | | .setQualifiedQuantity(feedback.getFeedbackQuantity()) |
| | | .setUnqualifiedQuantity(BigDecimal.ZERO)); |
| | | } |
| | | // 2.3 éè´¨æ£å·¥åºï¼ä»æå¾
æ£æ°éæ¶ä¸è½å®¡æ¹ï¼è´¨æ£å·¥åºç uncheckQuantity > 0 æ¯æ£å¸¸ç¶æï¼ä¸åæ¦æªï¼ |
| | | if (!checkFlag |
| | | && feedback.getUncheckQuantity() != null |
| | | && feedback.getUncheckQuantity().compareTo(BigDecimal.ZERO) > 0) { |
| | | throw exception(PRO_FEEDBACK_UNCHECK_QUANTITY_EXISTS, feedback.getUncheckQuantity()); |
| | | } |
| | | |
| | | // 3. ç©ææ¶èï¼åå²å·¥åºææ¬å·¥åºææ BOM èªå¨æ£æï¼éåå²å·¥åºè·³è¿ï¼ç©æèµ°æå·¥é¢æ/ææï¼ |
| | | boolean backflushFlag = routeProcess != null && Boolean.TRUE.equals(routeProcess.getBackflushFlag()); |
| | |
| | | } |
| | | } |
| | | |
| | | // 5. å
æ´æ°æ¥å·¥ç¶æä¸ºå·²å®æï¼ç¡®ä¿ checkAndFinishMps æ£æ¥æ¶å½åæ¥å·¥å·²æ è®°å®æï¼ |
| | | // æ¥å·¥ä¸è´¨æ£åå¼ï¼æ¥å·¥å®¡æ¹å®æå³ç´¯è®¡äº§éï¼è´¨éæ°éç±è´¨æ£åå¡«ã |
| | | feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId()) |
| | | .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus()) |
| | | .setUncheckQuantity(BigDecimal.ZERO)); |
| | | .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus()) |
| | | .setQualifiedQuantity(BigDecimal.ZERO) |
| | | .setUnqualifiedQuantity(BigDecimal.ZERO) |
| | | .setUncheckQuantity(feedback.getFeedbackQuantity())); |
| | | feedback.setQualifiedQuantity(BigDecimal.ZERO).setUnqualifiedQuantity(BigDecimal.ZERO); |
| | | productProduceService.generateProductProduce(feedback, true, reqVO); |
| | | |
| | | // 6. æ ¹æ®æ¯å¦éè¦æ£éªï¼å³å®äº§åºåå
¥åºæ¹å¼ |
| | | // 6.2 è°æ´ åªè¦å®¡æ¹æåç´æ¥æ´æ° |
| | |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | | |
| | | } |
| | | if (checkFlag) { |
| | | // 6.1 éè¦æ£éªï¼çæäº§åºåï¼è´¨éç¶æ=å¾
æ£éªï¼ï¼æ´æ°æ¥å·¥ç¶æä¸ºå¾
æ£éªï¼çè´¨æ£å®æåè°ååå
¥åº |
| | | // 宿æ¶åè°è§ï¼MesQcIpqcServiceImpl#finishIpqc â splitPendingAndFinishProduce + updateProFeedbackWhenIpqcFinish |
| | | productProduceService.generateProductProduce(feedback, true, reqVO); |
| | | feedbackMapper.updateById(new MesProFeedbackDO().setId(reqVO.getId()) |
| | | .setStatus(MesProFeedbackStatusEnum.UNCHECK.getStatus())); |
| | | equipmentService.afterApproved(feedback); |
| | | return false; |
| | | } else { |
| | | // 6.2 æ 鿣éªï¼çæäº§åºåï¼æåæ ¼/ä¸åæ ¼æè¡ï¼ï¼ç´æ¥å®æå
¥åº |
| | | MesWmProductProduceDO produce = productProduceService.generateProductProduce(feedback, false, reqVO); |
| | | productProduceService.finishProductProduce(produce.getId()); |
| | | |
| | | } |
| | | |
| | | equipmentService.afterApproved(feedback); |
| | | return true; // 已宿 |
| | | return false; // 产åºçå¾
è´¨æ£ï¼æ¥å·¥äº§é已累计 |
| | | } |
| | | |
| | | /** |
| | |
| | | * @param feedback æ¥å·¥å |
| | | */ |
| | | private void updateTaskAndWorkOrderByFeedback(MesProFeedbackDO feedback) { |
| | | // 1. æ¥è¯¢è¯¥æ¥å·¥åå
³èçææäº§åºåè¡ï¼æè´¨éç¶æèåæ°é |
| | | BigDecimal qualifiedQty = BigDecimal.ZERO; |
| | | BigDecimal unqualifiedQty = BigDecimal.ZERO; |
| | | List<MesWmProductProduceLineDO> lines = produceLineService.getProductProduceLineListByFeedbackId(feedback.getId()); |
| | | for (MesWmProductProduceLineDO line : lines) { |
| | | if (ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.FAIL.getStatus())) { |
| | | unqualifiedQty = unqualifiedQty.add(line.getQuantity()); |
| | | } |
| | | if (ObjUtil.equal(line.getQualityStatus(), MesWmQualityStatusEnum.PASS.getStatus())) { |
| | | qualifiedQty = qualifiedQty.add(line.getQuantity()); |
| | | } |
| | | } |
| | | // 1.1 å
åºï¼å
³é®éè´¨æ£å·¥åºå®¡æ¹æ¶äº§åºåè¡å°æªçæï¼ç´æ¥éç¨æ¥å·¥åä¸çåæ ¼/ä¸è¯æ°é |
| | | // ï¼éè´¨æ£å·¥åºé»è®¤å
¨é¨ä¸ºåæ ¼åï¼è´¨æ£å·¥åºçåæ ¼/ä¸è¯å¨è´¨æ£åè°åæäº§åºè¡èåï¼ |
| | | if (CollUtil.isEmpty(lines)) { |
| | | qualifiedQty = ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO); |
| | | unqualifiedQty = ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO); |
| | | } |
| | | |
| | | // 2. æ´æ°ä»»å¡çå·²ç产/åæ ¼/ä¸åæ ¼æ°é |
| | | // æ¥å·¥å®¡æ¹åªç´¯è®¡äº§éï¼åæ ¼ä¸ä¸åæ ¼æ°éå¨è´¨æ£å®ææ¶ç´¯è®¡ã |
| | | taskService.updateProducedQuantity(feedback.getTaskId(), |
| | | feedback.getFeedbackQuantity(), qualifiedQty, unqualifiedQty); |
| | | feedback.getFeedbackQuantity(), BigDecimal.ZERO, BigDecimal.ZERO); |
| | | // 3. æ´æ°è®¢åçå·²ç产æ°é |
| | | // æ°å¢é»è¾ï¼åªæå¨è¯¥ç¬è®¢åæåä¸ä¸ªå·¥åºææ´æ°ç产订åçå·²ç产æ°é |
| | | MesProWorkOrderProcessDO lastProcess = workOrderProcessService.getLastWorkOrderProcessByWorkOrderId(feedback.getWorkOrderId()); |
| | |
| | | private void checkAndPublishProductionFinished(Long workOrderId) { |
| | | // 1. è·åå·¥åå
³èç MPS |
| | | MesProMpsRespDTO mps = mpsApi.getMpsByWorkOrderId(workOrderId); |
| | | if (mps == null || mps.getSaleOrderId() == null) { |
| | | if (mps == null || mps.getSaleOrderId() == null |
| | | || ObjUtil.equal(mps.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum.FINISHED.getStatus()) |
| | | || ObjUtil.equal(mps.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProMpsStatusEnum.CANCELED.getStatus())) { |
| | | return; // ééå®è®¢åæ¥æºçå·¥åï¼è·³è¿ |
| | | } |
| | | |
| | | // 2. æ£æ¥å¹¶èªå¨å®æ MPSï¼å¦ææ»¡è¶³æ¡ä»¶ï¼ |
| | | mpsApi.checkAndFinishMps(mps.getId()); |
| | | if (!Boolean.TRUE.equals(mpsApi.checkAndFinishMps(mps.getId()))) { |
| | | return; |
| | | } |
| | | |
| | | // 3. æ£æ¥éå®è®¢åææç产æ¯å¦å®æ |
| | | if (mpsApi.checkSaleOrderProductionFinished(mps.getSaleOrderId())) { |
| | |
| | | throw exception(PRO_FEEDBACK_ROUTE_PROCESS_INVALID); |
| | | } |
| | | // 4.1 æ ¡éªæ°éï¼æ 论æ¯å¦éè¦æ£éªï¼æ¥å·¥åªéå¡«æ¥å·¥æ°éï¼ä¸å¿
é¡» > 0 |
| | | // åæ ¼å/ä¸è¯åæ°éç±è´¨æ£ç¡®å®ï¼éè´¨æ£å·¥åºå¨å®¡æ¹æ¶é»è®¤å
¨é¨ä¸ºåæ ¼åï¼ |
| | | // åæ ¼å/ä¸è¯åæ°éç±è´¨æ£ç¡®å®ã |
| | | if (reqVO.getFeedbackQuantity() == null |
| | | || reqVO.getFeedbackQuantity().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw exception(PRO_FEEDBACK_QUANTITY_MUST_POSITIVE); |
| | |
| | | .setItemId(task.getItemId()) |
| | | .setFeedbackQuantity(reqVO.getFeedbackQuantity()) |
| | | .setScheduledQuantity(task.getQuantity()) |
| | | .setFeedbackTime(LocalDateTime.now()) |
| | | .setFeedbackTime(reqVO.getFeedbackTime() != null ? reqVO.getFeedbackTime() : LocalDateTime.now()) |
| | | .setStatus(MesProFeedbackStatusEnum.APPROVING.getStatus()) |
| | | .setRemark(reqVO.getRemark()); |
| | | equipmentService.validateSubmission(feedback); |
| | |
| | | BigDecimal produced = task.getProducedQuantity() == null ? BigDecimal.ZERO : task.getProducedQuantity(); |
| | | BigDecimal inTransit = BigDecimal.ZERO; |
| | | for (MesProFeedbackDO fb : getFeedbackListByTaskId(task.getId())) { |
| | | if (!ObjUtil.equal(fb.getStatus(), MesProFeedbackStatusEnum.FINISHED.getStatus())) { |
| | | if (!ObjUtil.equal(fb.getStatus(), MesProFeedbackStatusEnum.FINISHED.getStatus()) |
| | | && !ObjUtil.equal(fb.getStatus(), MesProFeedbackStatusEnum.UNCHECK.getStatus())) { |
| | | inTransit = inTransit.add(fb.getFeedbackQuantity() == null ? BigDecimal.ZERO : fb.getFeedbackQuantity()); |
| | | } |
| | | } |
| | |
| | | MesProFeedbackQuickCreateReqVO singleReq = new MesProFeedbackQuickCreateReqVO(); |
| | | singleReq.setTaskId(taskId); |
| | | singleReq.setFeedbackQuantity(reqVO.getFeedbackQuantity()); |
| | | singleReq.setFeedbackTime(reqVO.getFeedbackTime()); |
| | | singleReq.setRemark(reqVO.getRemark()); |
| | | MesProFeedbackQuickCreateRespVO resp = quickCreateFeedback(singleReq); |
| | | results.add(new MesProFeedbackBatchCreateRespVO.BatchResult() |
| | |
| | | BigDecimal qualifiedQty, BigDecimal unqualifiedQty, |
| | | BigDecimal laborScrapQty, BigDecimal materialScrapQty, BigDecimal otherScrapQty) { |
| | | // 1. æ ¡éªæ¥å·¥ååå¨ä¸ä¸ºå¾
æ£éªç¶æ |
| | | MesProFeedbackDO feedback = validateFeedbackExists(feedbackId); |
| | | MesProFeedbackDO feedback = feedbackMapper.selectByIdForUpdate(feedbackId); |
| | | if (feedback == null) { |
| | | throw exception(PRO_FEEDBACK_NOT_EXISTS); |
| | | } |
| | | if (ObjUtil.notEqual(feedback.getStatus(), MesProFeedbackStatusEnum.UNCHECK.getStatus())) { |
| | | throw exception(PRO_FEEDBACK_NOT_UNCHECK); |
| | | } |
| | |
| | | .setOtherScrapQuantity(otherScrapQty) |
| | | .setStatus(MesProFeedbackStatusEnum.FINISHED.getStatus())); |
| | | |
| | | // 4. æ´æ°ä»»å¡/å·¥åçå·²ç产æ°é |
| | | // 4. 产é已卿¥å·¥å®¡æ¹æ¶ç´¯è®¡ï¼æ¤å¤åªåå¡«è´¨éæ°éã |
| | | feedback.setQualifiedQuantity(qualifiedQty).setUnqualifiedQuantity(unqualifiedQty); |
| | | taskService.updateQualityQuantity(feedback.getTaskId(), |
| | | ObjectUtil.defaultIfNull(qualifiedQty, BigDecimal.ZERO), |
| | | ObjectUtil.defaultIfNull(unqualifiedQty, BigDecimal.ZERO)); |
| | | //5.æ£æ¥å¹¶åå¸çäº§å®æäºä»¶ |
| | | checkAndPublishProductionFinished(feedback.getWorkOrderId()); |
| | | } |
| | |
| | | */ |
| | | Long createMps(Long saleOrderId, String saleOrderNo, Long saleOrderItemId, |
| | | Long mdmItemId, String productName, BigDecimal quantity, Long clientId, |
| | | String requestDate, String remark); |
| | | String requestDate, String remark, String reelNo); |
| | | |
| | | /** |
| | | * å建 MPS |
| | |
| | | * @param mpsIds MPS ç¼å·å表 |
| | | * @return çæçå·¥å ID |
| | | */ |
| | | @Deprecated |
| | | Long mergeAndIssueMps(List<Long> mpsIds); |
| | | |
| | | /** |
| | |
| | | */ |
| | | Boolean checkAndFinishMps(Long mpsId); |
| | | |
| | | } |
| | | } |
| | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import cn.iocoder.yudao.module.system.api.permission.PermissionApi; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.mps.vo.MesProMpsSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.workorder.vo.MesProWorkOrderSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.mps.MesProMpsMergeItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.mps.MesProMpsMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.mps.MesProMpsMergeItemMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.feedback.MesProFeedbackMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.workorder.MesProWorkOrderMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; |
| | |
| | | @Resource |
| | | private MesProMpsMapper mpsMapper; |
| | | @Resource |
| | | private MesProMpsMergeItemMapper mergeItemMapper; |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | @Resource |
| | | private PermissionApi permissionApi; |
| | | @Resource |
| | | private MesProWorkOrderService workOrderService; |
| | | @Resource |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createMps(Long saleOrderId, String saleOrderNo, Long saleOrderItemId, |
| | | Long mdmItemId, String productName, BigDecimal quantity, Long clientId, |
| | | String requestDate, String remark) { |
| | | String requestDate, String remark, String reelNo) { |
| | | // 1. æ ¹æ® MDM ç©æ ID æ¥æ¾ MES 产å |
| | | cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO mesItem = itemService.getItemByMdmItemId(mdmItemId); |
| | | if (mesItem == null) { |
| | |
| | | mps.setSaleOrderId(saleOrderId); |
| | | mps.setSaleOrderNo(saleOrderNo); |
| | | mps.setSaleOrderItemId(saleOrderItemId); |
| | | mps.setReelNo(reelNo); |
| | | mps.setProductId(mesItem.getId()); // ä½¿ç¨ MES 产å ID |
| | | mps.setQuantity(quantity); |
| | | mps.setQuantityProduced(BigDecimal.ZERO); |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long issueMps(Long id) { |
| | | // æ ¡éªåå¨ + èç¨¿ç¶æ |
| | | MesProMpsDO mps = validateMpsExistsAndDraft(id); |
| | | MesProMpsDO mps = mpsMapper.selectByIdForUpdate(id); |
| | | if (mps == null) { |
| | | throw exception(PRO_MPS_NOT_EXISTS); |
| | | } |
| | | if (!MesProMpsStatusEnum.DRAFT.getStatus().equals(mps.getStatus())) { |
| | | throw exception(PRO_MPS_NOT_DRAFT); |
| | | } |
| | | validateIssuePermission(mps); |
| | | |
| | | // å建ç产工å |
| | | Long workOrderId = createWorkOrderFromMps(mps, mps.getQuantity()); |
| | |
| | | return workOrderId; |
| | | } |
| | | |
| | | private void validateIssuePermission(MesProMpsDO mps) { |
| | | Long userId = SecurityFrameworkUtils.getLoginUserId(); |
| | | if (userId == null) { |
| | | throw exception(PRO_MPS_ISSUE_FORBIDDEN); |
| | | } |
| | | if (permissionApi.hasAnyRoles(userId, "super_admin")) { |
| | | return; |
| | | } |
| | | // æå·¥åå»ºä¸æ²¡æéå®è®¢åç计å以计åå建人为åã |
| | | String creator = mps.getCreator(); |
| | | if (mps.getSaleOrderId() != null) { |
| | | var order = saleOrderApi.getSaleOrder(mps.getSaleOrderId()).getCheckedData(); |
| | | creator = order != null ? order.getCreator() : null; |
| | | } |
| | | if (!String.valueOf(userId).equals(creator)) { |
| | | throw exception(PRO_MPS_ISSUE_FORBIDDEN); |
| | | } |
| | | } |
| | | |
| | | /** æ§åå¹¶ä¸åå
¥å£å·²åç¨ï¼åå²åå¹¶è®°å½ä»ç±åæ¶åæ¥è¯¢é»è¾å¤çã */ |
| | | @Deprecated |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long mergeAndIssueMps(List<Long> mpsIds) { |
| | | // æ ¡éª MPS å表 |
| | | if (CollUtil.isEmpty(mpsIds) || mpsIds.size() < 2) { |
| | | throw exception(PRO_MPS_MERGE_AT_LEAST_TWO); |
| | | } |
| | | |
| | | List<MesProMpsDO> mpsList = mpsMapper.selectBatchIds(mpsIds); |
| | | if (mpsList.size() != mpsIds.size()) { |
| | | throw exception(PRO_MPS_NOT_EXISTS); |
| | | } |
| | | |
| | | // æ ¡éªï¼å¿
é¡»æ¯ç¸å产åãèç¨¿ç¶æ |
| | | Long productId = null; |
| | | for (MesProMpsDO mps : mpsList) { |
| | | if (!MesProMpsStatusEnum.DRAFT.getStatus().equals(mps.getStatus())) { |
| | | throw exception(PRO_MPS_NOT_DRAFT); |
| | | } |
| | | if (productId == null) { |
| | | productId = mps.getProductId(); |
| | | } else if (!productId.equals(mps.getProductId())) { |
| | | throw exception(PRO_MPS_MERGE_PRODUCT_MISMATCH); |
| | | } |
| | | } |
| | | |
| | | // 计ç®åå¹¶æ°é |
| | | BigDecimal totalQuantity = mpsList.stream() |
| | | .map(MesProMpsDO::getQuantity) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | |
| | | // åç¬¬ä¸æ¡ MPS ä½ä¸ºä¸»è®°å½ï¼å建ç产工å |
| | | MesProMpsDO firstMps = mpsList.get(0); |
| | | Long workOrderId = createWorkOrderFromMps(firstMps, totalQuantity); |
| | | |
| | | // çæåå¹¶æ¹æ¬¡å· |
| | | String mergeBatchNo = generateMergeBatchNo(); |
| | | |
| | | // æ´æ°ææ MPS ç¶æï¼è®°å½åå¹¶å
³ç³» |
| | | for (MesProMpsDO mps : mpsList) { |
| | | mpsMapper.updateById(new MesProMpsDO() |
| | | .setId(mps.getId()) |
| | | .setStatus(MesProMpsStatusEnum.ISSUED.getStatus()) |
| | | .setWorkOrderId(workOrderId) |
| | | .setMergeFlag(1) |
| | | .setMergeBatchNo(mergeBatchNo) |
| | | .setIssueDate(LocalDateTime.now())); |
| | | |
| | | // è®°å½åå¹¶æç» |
| | | MesProMpsMergeItemDO mergeItem = new MesProMpsMergeItemDO(); |
| | | mergeItem.setMergeBatchNo(mergeBatchNo); |
| | | mergeItem.setMpsId(mps.getId()); |
| | | mergeItem.setWorkOrderId(workOrderId); |
| | | mergeItem.setQuantity(mps.getQuantity()); |
| | | mergeItemMapper.insert(mergeItem); |
| | | } |
| | | |
| | | log.info("[mergeAndIssueMps] MPS åå¹¶ä¸åæåï¼mpsIds={}, workOrderId={}, totalQuantity={}", |
| | | mpsIds, workOrderId, totalQuantity); |
| | | return workOrderId; |
| | | throw exception(PRO_MPS_MERGE_ISSUE_DISABLED); |
| | | } |
| | | |
| | | @Override |
| | |
| | | mps.getId(), quantityProduced, mps.getQuantity()); |
| | | return false; |
| | | } |
| | | // 2. æ£æ¥å·¥åä¸æææ¥å·¥æ¯å¦å®æï¼ç¡®ä¿è´¨æ£å®æï¼ |
| | | // 2. 已审æ¹å¾
æ£çæ¥å·¥å·²è®¡å
¥ç产è¿åº¦ï¼ä¸ä»¥è´¨æ£ç¶æé»å¡çäº§å®æã |
| | | if (mps.getWorkOrderId() != null) { |
| | | Long unfinishedCount = feedbackMapper.selectCount( |
| | | new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MesProFeedbackDO>() |
| | | .eq(MesProFeedbackDO::getWorkOrderId, mps.getWorkOrderId()) |
| | | .ne(MesProFeedbackDO::getStatus, MesProFeedbackStatusEnum.FINISHED.getStatus())); |
| | | .notIn(MesProFeedbackDO::getStatus, MesProFeedbackStatusEnum.FINISHED.getStatus(), |
| | | MesProFeedbackStatusEnum.UNCHECK.getStatus())); |
| | | log.debug("[isMpsFinished] MPS {} unfinished feedback count: {}", mps.getId(), unfinishedCount); |
| | | if (unfinishedCount > 0) { |
| | | return false; |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * çæåå¹¶æ¹æ¬¡å· |
| | | */ |
| | | /** å·²åç¨ï¼æ§åå¹¶ä¸åæ¹æ¬¡å·çæé»è¾ï¼ä»
ä¾è¯å«åå²å®ç°ï¼ä¸åè°ç¨ã */ |
| | | @Deprecated |
| | | private String generateMergeBatchNo() { |
| | | return "MERGE" + System.currentTimeMillis(); |
| | | } |
| | |
| | | workOrderReq.setOrderSourceType(MesProWorkOrderSourceTypeEnum.ORDER.getType()); |
| | | workOrderReq.setOrderSourceCode(mps.getSaleOrderNo()); |
| | | workOrderReq.setProductId(mps.getProductId()); |
| | | workOrderReq.setReelNo(mps.getReelNo()); |
| | | workOrderReq.setQuantity(quantity); |
| | | workOrderReq.setClientId(mps.getClientId()); |
| | | workOrderReq.setRequestDate(mps.getRequestDate() != null ? mps.getRequestDate() : LocalDateTime.now()); |
| | |
| | | BigDecimal incrQualifyQuantity, |
| | | BigDecimal incrUnqualifyQuantity); |
| | | |
| | | /** è´¨æ£ç»æåå¡«ï¼ä»
æ´æ°è´¨éæ°éï¼ä¸ä¿®æ¹äº§éææ¥å·¥æ¶é´ã */ |
| | | void updateQualityQuantity(Long id, BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity); |
| | | |
| | | /** |
| | | * ä¸é®æäº§é¢è§ |
| | | * |
| | |
| | | } |
| | | |
| | | @Override |
| | | public void updateQualityQuantity(Long id, BigDecimal qualifiedQuantity, BigDecimal unqualifiedQuantity) { |
| | | validateTaskExists(id); |
| | | taskMapper.updateProducedQuantity(id, BigDecimal.ZERO, qualifiedQuantity, unqualifiedQuantity); |
| | | } |
| | | |
| | | @Override |
| | | public void updateProducedQuantity(Long id, |
| | | BigDecimal incrProducedQuantity, |
| | | BigDecimal incrQualifyQuantity, |
| | |
| | | // 2. æå
¥ä¸»è¡¨ |
| | | MesQcIpqcDO ipqc = BeanUtils.toBean(createReqVO, MesQcIpqcDO.class) |
| | | .setItemId(itemId) |
| | | .setReelNo(workOrder.getReelNo()) |
| | | .setProcessId(getProcessId(itemId, ctx.workstation().getProcessId())) |
| | | .setTemplateId(templateId) |
| | | .setSourceDocCode(sourceDocCode) |
| | |
| | | MesMdWorkstationDO workstation = workstationService.validateWorkstationExists(reqVO.getWorkstationId()); |
| | | adminUserApi.validateUser(reqVO.getInspectorUserId()); |
| | | // æ ¡éªå·¥ååå¨ |
| | | MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderConfirmed(reqVO.getWorkOrderId()); |
| | | MesProWorkOrderDO workOrder = workOrderService.validateWorkOrderExists(reqVO.getWorkOrderId()); |
| | | if (!Objects.equals(workOrder.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum.CONFIRMED.getStatus()) |
| | | && !Objects.equals(workOrder.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum.FINISHED.getStatus())) { |
| | | throw exception(QC_IPQC_WORK_ORDER_STATUS_INVALID); |
| | | } |
| | | if (reqVO.getProcessId() != null && ObjUtil.notEqual(workstation.getProcessId(), reqVO.getProcessId())) { |
| | | throw exception(PRO_WORKSTATION_PROCESS_MISMATCH); |
| | | } |
| | | // æ ¡éªä»»å¡å
³ç³»ï¼å¦ææå®äºä»»å¡ï¼ |
| | | MesProTaskDO task = null; |
| | | if (reqVO.getTaskId() != null) { |
| | | task = taskService.validateTaskNotFinished(reqVO.getTaskId()); |
| | | task = taskService.validateTaskExists(reqVO.getTaskId()); |
| | | if (Objects.equals(task.getStatus(), cn.iocoder.yudao.module.mes.enums.pro.MesProTaskStatusEnum.CANCELED.getStatus())) { |
| | | throw exception(PRO_TASK_ALREADY_FINISHED); |
| | | } |
| | | validateTaskRelation(task, workstation, workOrder, reqVO); |
| | | } else { |
| | | // æ 任塿¶ï¼itemId ä»éä¸å·¥å产åä¸è´ |
| | |
| | | Long sourceDocId = ipqc.getSourceDocId(); |
| | | Long sourceLineId = ipqc.getSourceLineId(); |
| | | |
| | | // 妿 IPQC åæ²¡æè®¾ç½®æ¥æºï¼å°è¯éè¿ taskId æ¥æ¾å
³èçå¾
æ£éªæ¥å·¥ |
| | | if (sourceDocId == null && ipqc.getTaskId() != null) { |
| | | MesProFeedbackDO feedback = feedbackService.getFeedbackListByTaskId(ipqc.getTaskId()).stream() |
| | | .filter(f -> MesProFeedbackStatusEnum.UNCHECK.getStatus().equals(f.getStatus())) |
| | | .findFirst().orElse(null); |
| | | if (feedback != null) { |
| | | sourceDocId = feedback.getId(); |
| | | log.info("[writeBackSourceDoc] IPQC({}) æªè®¾ç½®æ¥æºåæ®ï¼éè¿ taskId({}) æ¾å°å¾
æ£éªæ¥å·¥({})", |
| | | ipqc.getId(), ipqc.getTaskId(), feedback.getId()); |
| | | } |
| | | } |
| | | |
| | | if (sourceDocId == null) { |
| | | // ç¬ç«æ£éªä¸æ¨æµå
³èæ¥å·¥ï¼åä¸ä»»å¡å¯è½æå¤æ¬¡æ¥å·¥åä¸åçå·ã |
| | | if (ipqc.getSourceDocType() == null && sourceDocId == null && sourceLineId == null) { |
| | | log.warn("[writeBackSourceDoc] IPQC({}) æ æ³ç¡®å®æ¥æºæ¥å·¥ï¼è·³è¿åå", ipqc.getId()); |
| | | return; |
| | | } |
| | | if (!Objects.equals(ipqc.getSourceDocType(), MesBizTypeConstants.PRO_FEEDBACK) |
| | | || sourceDocId == null || sourceLineId == null) { |
| | | throw new IllegalArgumentException("è¿ç¨æ£éªçæ¥å·¥æ¥æºæäº§åºè¡ä¸å®æ´"); |
| | | } |
| | | |
| | | // 2. å忥工 |
| | | feedbackService.updateProFeedbackWhenIpqcFinish(sourceDocId, sourceLineId, |
| | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.operatelog.core.util.OperateLogUtils; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcImportExcelVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.oqc.vo.MesQcOqcSaveReqVO; |
| | |
| | | |
| | | @Resource |
| | | private MesQcOqcMapper oqcMapper; |
| | | |
| | | @Resource |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | |
| | | @Resource |
| | | private MesQcTemplateItemService templateItemService; |
| | |
| | | .setTemplateId(templateId) |
| | | .setSourceDocCode(sourceDocCode) |
| | | .setStatus(MesQcStatusEnum.DRAFT.getStatus()); |
| | | if (createReqVO.getSourceLineId() != null) { |
| | | oqc.setReelNo(resolveSalesReelNo(productSalesLineService |
| | | .validateProductSalesLineExists(createReqVO.getSourceLineId()))); |
| | | } |
| | | oqcMapper.insert(oqc); |
| | | |
| | | // 3. 仿¨¡æ¿ææ èªå¨çææ£éªè¡ |
| | |
| | | @Override |
| | | public void updateOqc(MesQcOqcSaveReqVO updateReqVO) { |
| | | // 1.1 æ ¡éªåå¨ + èç¨¿ç¶æ |
| | | validateOqcStatusPrepare(updateReqVO.getId()); |
| | | MesQcOqcDO existingOqc = validateOqcStatusPrepare(updateReqVO.getId()); |
| | | // 1.2 æ ¡éªæ°æ® |
| | | validateOqcSaveData(updateReqVO); |
| | | |
| | |
| | | updateObj.setSourceDocType(null).setSourceDocId(null).setSourceLineId(null); // ä¸å
è®¸ä¿®æ¹æ¥æºåæ® |
| | | updateObj.setTemplateId(null); // ä¸å
è®¸ä¿®æ¹æ¨¡æ¿ |
| | | updateObj.setItemId(null); // ä¸å
许修æ¹ç©æ |
| | | if (existingOqc.getSourceLineId() != null) updateObj.setReelNo(null); // æ¥æºçå·ä¿æå¿«ç
§ |
| | | // ç¼è¾æ¶å
许æ¸
空客æ·ï¼å®ææ£éªçå±é¨æ´æ°ä»ä¿çå客æ·ã |
| | | updateObj.setClientId(null); |
| | | oqcMapper.update(updateObj, new LambdaUpdateWrapper<MesQcOqcDO>() |
| | |
| | | oqc.setClientId(clientId); |
| | | oqc.setItemId(line.getItemId()); |
| | | oqc.setBatchCode(line.getBatchCode()); |
| | | oqc.setReelNo(resolveSalesReelNo(line)); |
| | | oqc.setOutQuantity(line.getQuantity()); |
| | | oqc.setCheckQuantity(BigDecimal.ZERO); // å¾
å¡«å |
| | | oqc.setQualifiedQuantity(BigDecimal.ZERO); // å¾
å¡«å |
| | |
| | | return oqc.getId(); |
| | | } |
| | | |
| | | private String resolveSalesReelNo(MesWmProductSalesLineDO line) { |
| | | if (line == null || line.getSaleOrderItemId() == null) return null; |
| | | var item = saleOrderApi.getSaleOrderItem(line.getSaleOrderItemId()).getCheckedData(); |
| | | return item == null ? null : item.getReelNo(); |
| | | } |
| | | |
| | | /** |
| | | * è·åç©æä¿¡æ¯ï¼ç¼ç +åç§°ï¼ |
| | | */ |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect.vo.MesQcPendingInspectPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.pendinginspect.vo.MesQcPendingInspectRespVO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.qc.pendinginspect.MesQcPendingInspectMapper; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | |
| | | |
| | | @Resource |
| | | private MesQcPendingInspectMapper pendingInspectMapper; |
| | | @Resource |
| | | private MesProWorkOrderService workOrderService; |
| | | |
| | | @Override |
| | | public PageResult<MesQcPendingInspectRespVO> getPendingInspectPage(MesQcPendingInspectPageReqVO pageReqVO) { |
| | |
| | | IPage<MesQcPendingInspectRespVO> page = MyBatisUtils.buildPage(pageReqVO); |
| | | pendingInspectMapper.selectQcPendingPage(page, |
| | | pageReqVO.getSourceDocCode(), pageReqVO.getQcType(), pageReqVO.getItemId()); |
| | | var orderIds = page.getRecords().stream().map(MesQcPendingInspectRespVO::getWorkOrderId) |
| | | .filter(java.util.Objects::nonNull).collect(java.util.stream.Collectors.toSet()); |
| | | if (!orderIds.isEmpty()) { |
| | | var orders = workOrderService.getWorkOrderMap(orderIds); |
| | | page.getRecords().forEach(row -> { |
| | | var order = row.getWorkOrderId() == null ? null : orders.get(row.getWorkOrderId()); |
| | | if (order != null) row.setReelNo(order.getReelNo()); |
| | | }); |
| | | } |
| | | // è¿åç»æ |
| | | return new PageResult<>(page.getRecords(), page.getTotal()); |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | |
| | | import java.util.List; |
| | | import java.util.Collection; |
| | | import java.util.Map; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * æ¹æ¬¡ç®¡ç Service æ¥å£ |
| | |
| | | */ |
| | | MesWmBatchDO getBatch(Long id); |
| | | |
| | | List<MesWmBatchDO> getBatchList(Collection<Long> ids); |
| | | |
| | | default Map<Long, MesWmBatchDO> getBatchMap(Collection<Long> ids) { |
| | | return getBatchList(ids).stream().collect(Collectors.toMap(MesWmBatchDO::getId, batch -> batch)); |
| | | } |
| | | |
| | | /** |
| | | * æ ¹æ®æ¹æ¬¡å·è·å¾æ¹æ¬¡ |
| | | * |
| | |
| | | * æ ¹æ®ç©ææ¹æ¬¡é
ç½®ï¼æ¥è¯¢æçææ¹æ¬¡è®°å½ |
| | | * |
| | | * @param reqVO æ¹æ¬¡åæ°ï¼å
å« itemId åå
¶ä»å¯é屿§ï¼ |
| | | * @return æ¹æ¬¡è®°å½ï¼å¦æç©ææªå¯ç¨æ¹æ¬¡ç®¡çåè¿å nullï¼ |
| | | * @return æ¹æ¬¡è®°å½ï¼æªå¯ç¨æ¹æ¬¡ç®¡ç䏿ªä¼ çå·æ¶è¿å nullï¼ |
| | | */ |
| | | MesWmBatchDO getOrGenerateBatchCode(MesWmBatchGenerateReqVO reqVO); |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | public List<MesWmBatchDO> getBatchList(java.util.Collection<Long> ids) { |
| | | return CollUtil.isEmpty(ids) ? java.util.Collections.emptyList() : batchMapper.selectByIds(ids); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public MesWmBatchDO getOrGenerateBatchCode(MesWmBatchGenerateReqVO reqVO) { |
| | | // 1.1 æ¥è¯¢ç©æä¿¡æ¯ï¼æ£æ¥æ¯å¦å¯ç¨æ¹æ¬¡ç®¡ç |
| | | MesMdmItemDO item = mdmItemService.validateItemExists(reqVO.getItemId()); |
| | | if (Boolean.FALSE.equals(item.getIsBatchManaged())) { |
| | | if (Boolean.FALSE.equals(item.getIsBatchManaged()) && StrUtil.isBlank(reqVO.getReelNo())) { |
| | | // æªå¯ç¨æ¹æ¬¡ç®¡çï¼è¿å null |
| | | return null; |
| | | } |
| | |
| | | MdmItemBatchConfigRespDTO config = itemBatchConfigApi.getItemBatchConfig(reqVO.getItemId()); |
| | | |
| | | // 2. æ ¹æ®é
ç½®æ ¡éªå¿
å¡«åæ°å¹¶æ¸
空ä¸éè¦çåæ° |
| | | MesWmBatchDO batch = MesWmBatchDO.builder().itemId(reqVO.getItemId()).build(); |
| | | String reelNo = StrUtil.isBlank(reqVO.getReelNo()) ? null : reqVO.getReelNo(); |
| | | MesWmBatchDO batch = MesWmBatchDO.builder().itemId(reqVO.getItemId()).reelNo(reelNo).build(); |
| | | if (config != null) { |
| | | // ========== å¿
å¡«æ ¡éªï¼flag=true è¡¨ç¤ºè¯¥åæ®µä¸ºå¿
å¡«ï¼ ========== |
| | | // çäº§æ¥æ |
| | |
| | | if (!existingBatch.getItemId().equals(reqVO.getItemId())) { |
| | | throw exception(WM_BATCH_ITEM_MISMATCH); |
| | | } |
| | | // å·²ææ¹æ¬¡ä¸è½æ¹åçå·ï¼é¿å
åå²åºåè¢«è¯¯æ æä¸åçå·æ··åã |
| | | String existingReelNo = StrUtil.isBlank(existingBatch.getReelNo()) ? null : existingBatch.getReelNo(); |
| | | if (!ObjUtil.equal(existingReelNo, reelNo)) { |
| | | throw exception(WM_BATCH_REEL_MISMATCH); |
| | | } |
| | | if (existingReelNo != null && !ObjUtil.equal(existingBatch.getWorkOrderId(), reqVO.getWorkOrderId())) { |
| | | // å·²æè¿½æº¯æ¹æ¬¡ä¸è½è¢«å¦ä¸å·¥åå¤ç¨å¹¶è¦çæ¥æºã |
| | | throw exception(WM_BATCH_REEL_MISMATCH); |
| | | } |
| | | // æ´æ°æ¹æ¬¡å±æ§ï¼ä»
æ´æ°ä¼ å
¥çé空å¼ï¼ |
| | | updateBatchProperties(existingBatch, batch); |
| | | return existingBatch; |
| | |
| | | * æ ¹æ®æ¥å·¥è®°å½ï¼èªå¨çæäº§å产åºåï¼å¤´ + è¡ + æç»ï¼ |
| | | * |
| | | * @param feedback æ¥å·¥è®°å½ |
| | | * @param checkFlag æ¯å¦éè¦æ£éªï¼true=å¾
æ£éªï¼false=æåæ ¼/ä¸åæ ¼æåè¡ï¼ |
| | | * @param checkFlag æ§åæ°ï¼å¿
须为 trueï¼false çç´æ¥å
¥åºé»è¾å·²åç¨ |
| | | * @param approveReqVO 审æ¹è¯·æ±ï¼å«å¯éæ¹æ¬¡å±æ§ï¼å¯ä¸º nullï¼ |
| | | * @return çæç产å产åºå |
| | | */ |
| | |
| | | |
| | | private void createTransactionList(Long produceId, List<MesWmProductProduceLineDO> lines) { |
| | | for (MesWmProductProduceLineDO line : lines) { |
| | | if (MesWmQualityStatusEnum.FAIL.getStatus().equals(line.getQualityStatus())) { |
| | | // ä¸åæ ¼äº§åºä¿çè®°å½ï¼äº¤ç±ä¸åæ ¼åå¤çï¼ä¸çæåæ ¼åºåæµæ°´ã |
| | | continue; |
| | | } |
| | | if (!MesWmQualityStatusEnum.PASS.getStatus().equals(line.getQualityStatus())) { |
| | | throw exception(WM_PRODUCT_PRODUCE_QUALITY_PENDING); |
| | | } |
| | | // 1. æ ¡éªæ¯è¡æç»æ°éä¹åçäºè¡æ°é |
| | | List<MesWmProductProduceDetailDO> details = productProduceDetailService.getProductProduceDetailListByLineId( |
| | | line.getId()); |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public MesWmProductProduceDO generateProductProduce(MesProFeedbackDO feedback, boolean checkFlag, |
| | | MesProFeedbackApproveReqVO approveReqVO) { |
| | | if (!checkFlag) throw exception(WM_PRODUCT_PRODUCE_QUALITY_PENDING); |
| | | // 0. æ¥è¯¢å
³èçå·¥åï¼ç¨äºè·å clientId çä¿¡æ¯ï¼ |
| | | MesProWorkOrderDO workOrder = workOrderService.getWorkOrder(feedback.getWorkOrderId()); |
| | | |
| | |
| | | // 2. è·åæçææ¹æ¬¡ï¼å®¡æ¹è¯·æ±ä¸çæ¹æ¬¡å±æ§ä¼å
ï¼ |
| | | MesWmBatchGenerateReqVO batchReqVO = new MesWmBatchGenerateReqVO() |
| | | .setItemId(feedback.getItemId()) |
| | | .setReelNo(workOrder != null ? workOrder.getReelNo() : null) |
| | | .setProduceDate(LocalDate.now().atStartOfDay()) // æªæå°å½å¤©é¶ç¹ï¼ç¡®ä¿åä¸å¤©çæ¥å·¥çæç¸åæ¹æ¬¡å· |
| | | .setExpireDate(feedback.getExpireDate()) |
| | | .setWorkOrderId(feedback.getWorkOrderId()) |
| | |
| | | Long batchId = batch != null ? batch.getId() : null; |
| | | String batchCode = batch != null ? batch.getCode() : null; |
| | | |
| | | WarehouseTarget outputTarget = resolveOutputTarget(feedback.getWorkOrderId(), feedback.getProcessId(), feedback.getItemId()); |
| | | |
| | | // 3. æ ¹æ®æ¯å¦éè¦æ£éªåæ¯å¤ç |
| | | if (checkFlag) { |
| | | // 3.1 éè¦æ£éªï¼åå»ºä¸æ¡è¡ï¼è´¨éç¶æ=å¾
æ£éªï¼ï¼ä¸çææç» |
| | | MesWmProductProduceLineDO line = buildProduceLine(produce, feedback, batchId, batchCode, |
| | | feedback.getFeedbackQuantity(), MesWmQualityStatusEnum.PENDING.getStatus()); |
| | | productProduceLineService.createProductProduceLine(line); |
| | | // 注æï¼checkFlag=true æ¶ä¸çææç»è¡ï¼ç IPQC æ£éªå®æåç± splitPendingAndFinishProduce æè´¨éç»ææè¡+çææç» |
| | | } else { |
| | | // 3.2 æ 鿣éªï¼æåæ ¼å/ä¸åæ ¼ååçæä¸è¡ + æç» |
| | | BigDecimal qualifiedQty = ObjectUtil.defaultIfNull(feedback.getQualifiedQuantity(), BigDecimal.ZERO); |
| | | BigDecimal unqualifiedQty = ObjectUtil.defaultIfNull(feedback.getUnqualifiedQuantity(), BigDecimal.ZERO); |
| | | // 3.2.0 å
åºï¼åæ ¼/ä¸è¯æ°é齿ªå¡«åæ¶ï¼æ¥å·¥åªå¡«æ¥å·¥æ°éï¼ï¼é»è®¤æ´åä¸ºåæ ¼å |
| | | if (qualifiedQty.compareTo(BigDecimal.ZERO) <= 0 && unqualifiedQty.compareTo(BigDecimal.ZERO) <= 0) { |
| | | qualifiedQty = feedback.getFeedbackQuantity(); |
| | | } |
| | | // 3.2.1 ä¸åæ ¼åè¡ + æç» |
| | | if (unqualifiedQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | MesWmProductProduceLineDO unqualifiedLine = buildProduceLine(produce, feedback, batchId, batchCode, |
| | | unqualifiedQty, MesWmQualityStatusEnum.FAIL.getStatus()); |
| | | productProduceLineService.createProductProduceLine(unqualifiedLine); |
| | | MesWmProductProduceDetailDO unqualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode, |
| | | unqualifiedLine.getId(), unqualifiedQty, |
| | | outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId()); |
| | | productProduceDetailService.createProductProduceDetail(unqualifiedDetail); |
| | | } |
| | | // 3.2.2 åæ ¼åè¡ + æç» |
| | | if (qualifiedQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | MesWmProductProduceLineDO qualifiedLine = buildProduceLine(produce, feedback, batchId, batchCode, |
| | | qualifiedQty, MesWmQualityStatusEnum.PASS.getStatus()); |
| | | productProduceLineService.createProductProduceLine(qualifiedLine); |
| | | MesWmProductProduceDetailDO qualifiedDetail = buildProduceDetail(produce, feedback, batchId, batchCode, |
| | | qualifiedLine.getId(), qualifiedQty, |
| | | outputTarget.warehouseId(), outputTarget.locationId(), outputTarget.areaId()); |
| | | productProduceDetailService.createProductProduceDetail(qualifiedDetail); |
| | | } |
| | | } |
| | | // æ°æ¥å·¥äº§åºç»ä¸å¾
æ£ï¼åå²ä¸æ£éªå³å
¥åºåæ¯å·²ç§»é¤ã |
| | | MesWmProductProduceLineDO line = buildProduceLine(produce, feedback, batchId, batchCode, |
| | | feedback.getFeedbackQuantity(), MesWmQualityStatusEnum.PENDING.getStatus(), |
| | | workOrder != null ? workOrder.getReelNo() : null); |
| | | productProduceLineService.createProductProduceLine(line); |
| | | return produce; |
| | | } |
| | | |
| | | private MesWmProductProduceLineDO buildProduceLine(MesWmProductProduceDO produce, MesProFeedbackDO feedback, |
| | | Long batchId, String batchCode, |
| | | BigDecimal quantity, Integer qualityStatus) { |
| | | BigDecimal quantity, Integer qualityStatus, String reelNo) { |
| | | return MesWmProductProduceLineDO.builder() |
| | | .produceId(produce.getId()).feedbackId(feedback.getId()) |
| | | .itemId(feedback.getItemId()).quantity(quantity) |
| | | .batchId(batchId).batchCode(batchCode) |
| | | .batchId(batchId).batchCode(batchCode).reelNo(reelNo) |
| | | .expireDate(feedback.getExpireDate()).lotNumber(feedback.getLotNumber()) |
| | | .qualityStatus(qualityStatus) |
| | | .build(); |
| | | } |
| | | |
| | | private MesWmProductProduceDetailDO buildProduceDetail(MesWmProductProduceDO produce, MesProFeedbackDO feedback, |
| | | Long batchId, String batchCode, |
| | | Long lineId, BigDecimal quantity, |
| | | Long warehouseId, Long locationId, Long areaId) { |
| | | return MesWmProductProduceDetailDO.builder() |
| | | .produceId(produce.getId()).lineId(lineId) |
| | | .itemId(feedback.getItemId()).quantity(quantity) |
| | | .batchId(batchId).batchCode(batchCode) |
| | | .warehouseId(warehouseId).locationId(locationId).areaId(areaId) |
| | | .build(); |
| | | } |
| | | |
| | |
| | | } |
| | | // 2. éè¿ sourceLineId ç´æ¥å®ä½å¾
æ£éªè¡ |
| | | MesWmProductProduceLineDO pendingLine = productProduceLineService.validateProductProduceLineExists(sourceLineId); |
| | | if (!ObjUtil.equal(pendingLine.getProduceId(), produce.getId()) |
| | | || !ObjUtil.equal(pendingLine.getFeedbackId(), feedbackId) |
| | | || !ObjUtil.equal(pendingLine.getQualityStatus(), MesWmQualityStatusEnum.PENDING.getStatus()) |
| | | || qualifiedQty == null || unqualifiedQty == null || pendingLine.getQuantity() == null |
| | | || qualifiedQty.signum() < 0 || unqualifiedQty.signum() < 0 |
| | | || qualifiedQty.add(unqualifiedQty).compareTo(pendingLine.getQuantity()) != 0) { |
| | | throw exception(WM_PRODUCT_PRODUCE_INSPECTION_INVALID); |
| | | } |
| | | WarehouseTarget outputTarget = resolveOutputTarget(produce.getWorkOrderId(), produce.getProcessId(), pendingLine.getItemId()); |
| | | |
| | | // 3A. æ
åµä¸ï¼åå¨ä¸åæ ¼åæ°éï¼éè¦æåè¡ |
| | |
| | | .produceId(produce.getId()).feedbackId(feedbackId) |
| | | .itemId(pendingLine.getItemId()).quantity(unqualifiedQty) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()) |
| | | .expireDate(pendingLine.getExpireDate()).lotNumber(pendingLine.getLotNumber()) |
| | | .expireDate(pendingLine.getExpireDate()).lotNumber(pendingLine.getLotNumber()).reelNo(pendingLine.getReelNo()) |
| | | .qualityStatus(MesWmQualityStatusEnum.FAIL.getStatus()) |
| | | .build(); |
| | | productProduceLineService.createProductProduceLine(unqualifiedLine); |
| | | productProduceDetailService.createProductProduceDetail(MesWmProductProduceDetailDO.builder() |
| | | .produceId(produce.getId()).lineId(unqualifiedLine.getId()) |
| | | .itemId(pendingLine.getItemId()).quantity(unqualifiedQty) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()).reelNo(pendingLine.getReelNo()) |
| | | .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId()) |
| | | .build()); |
| | | // 3A.2 åæ ¼åï¼å¤ç¨åå¾
æ£è¡ï¼æ´æ°æ°éåç¶æ + æç» |
| | |
| | | productProduceDetailService.createProductProduceDetail(MesWmProductProduceDetailDO.builder() |
| | | .produceId(produce.getId()).lineId(pendingLine.getId()) |
| | | .itemId(pendingLine.getItemId()).quantity(qualifiedQty) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()).reelNo(pendingLine.getReelNo()) |
| | | .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId()) |
| | | .build()); |
| | | } |
| | |
| | | productProduceDetailService.createProductProduceDetail(MesWmProductProduceDetailDO.builder() |
| | | .produceId(produce.getId()).lineId(pendingLine.getId()) |
| | | .itemId(pendingLine.getItemId()).quantity(pendingLine.getQuantity()) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()) |
| | | .batchId(pendingLine.getBatchId()).batchCode(pendingLine.getBatchCode()).reelNo(pendingLine.getReelNo()) |
| | | .warehouseId(outputTarget.warehouseId()).locationId(outputTarget.locationId()).areaId(outputTarget.areaId()) |
| | | .build()); |
| | | } |
| | |
| | | private MesWmMaterialStockService materialStockService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService warehouseAreaService; |
| | | @Resource |
| | | private MesWmSalesReelGuard salesReelGuard; |
| | | |
| | | @Override |
| | | public Long createProductSalesDetail(MesWmProductSalesDetailSaveReqVO createReqVO) { |
| | |
| | | materialStockService.validateSelectedStock( |
| | | reqVO.getMaterialStockId(), reqVO.getItemId(), reqVO.getBatchId(), reqVO.getBatchCode(), |
| | | reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId(), reqVO.getQuantity()); |
| | | salesReelGuard.validate(line, reqVO.getBatchId()); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | @Resource |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | @Resource |
| | | private MesWmSalesReelGuard salesReelGuard; |
| | | |
| | | @Override |
| | | public Long createProductSales(MesWmProductSalesSaveReqVO createReqVO) { |
| | |
| | | } |
| | | // æ ¡éªåºåå
足并å建å ç¨ |
| | | for (MesWmProductSalesDetailDO detail : details) { |
| | | salesReelGuard.validate(line, detail.getBatchId()); |
| | | materialStockService.validateSelectedStock( |
| | | detail.getMaterialStockId(), |
| | | detail.getItemId(), |
| | |
| | | |
| | | private void createTransactionList(MesWmProductSalesDO sales) { |
| | | List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListBySalesId(sales.getId()); |
| | | for (MesWmProductSalesDetailDO detail : details) { |
| | | MesWmProductSalesLineDO line = productSalesLineService.getProductSalesLine(detail.getLineId()); |
| | | if (line == null || !ObjUtil.equal(line.getSalesId(), sales.getId())) { |
| | | throw exception(WM_PRODUCT_SALES_DETAIL_LINE_NOT_MATCH); |
| | | } |
| | | salesReelGuard.validate(line, detail.getBatchId()); |
| | | } |
| | | wmTransactionService.createTransactionList(convertList(details, detail -> new MesWmTransactionSaveReqDTO() |
| | | .setType(MesWmTransactionTypeEnum.OUT.getType()).setItemId(detail.getItemId()) |
| | | .setQuantity(detail.getQuantity().negate()) // åºåºæ°éä¸ºè´æ° |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.wm.productsales; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.*; |
| | | |
| | | /** æé宿ç»çå·æ ¡éªå®é
éä¸çåºåæ¹æ¬¡ï¼æ çå·çåå²åæ®ä¿çåææµç¨ã */ |
| | | @Service |
| | | public class MesWmSalesReelGuard { |
| | | @Resource private ErpSaleOrderApi saleOrderApi; |
| | | @Resource private MesWmBatchService batchService; |
| | | |
| | | public void validate(MesWmProductSalesLineDO line, Long batchId) { |
| | | if (line.getSaleOrderItemId() == null) return; |
| | | var item = saleOrderApi.getSaleOrderItem(line.getSaleOrderItemId()).getCheckedData(); |
| | | if (item == null) throw exception(WM_PRODUCT_SALES_REEL_SOURCE_MISSING); |
| | | if (StrUtil.isBlank(item.getReelNo())) return; |
| | | var batch = batchId == null ? null : batchService.getBatch(batchId); |
| | | if (batch == null || !item.getReelNo().equals(batch.getReelNo())) { |
| | | throw exception(WM_PRODUCT_SALES_REEL_MISMATCH); |
| | | } |
| | | } |
| | | } |
| | |
| | | if (ObjUtil.notEqual(materialStock.getItemId(), reqDTO.getItemId()) |
| | | || ObjUtil.notEqual(materialStock.getWarehouseId(), reqDTO.getWarehouseId()) |
| | | || ObjUtil.notEqual(materialStock.getLocationId(), reqDTO.getLocationId()) |
| | | || ObjUtil.notEqual(materialStock.getAreaId(), reqDTO.getAreaId())) { |
| | | || ObjUtil.notEqual(materialStock.getAreaId(), reqDTO.getAreaId()) |
| | | || ObjUtil.notEqual(materialStock.getBatchId(), reqDTO.getBatchId())) { |
| | | throw new ServiceException(WM_MATERIAL_STOCK_SELECTION_MISMATCH.getCode(), |
| | | "该åºåè®°å½å·²åå¨ï¼å¯è½è¢«ç§»åº/åå¹¶/æåï¼ï¼ä¸åºåºæç»ä¸ä¸è´ï¼æ æ³æ£åï¼è¯·éåéæ°æ£è´§"); |
| | | } |
| | |
| | | assertEquals(0, otherScrapQty.compareTo(updatedFeedback.getOtherScrapQuantity())); |
| | | |
| | | // 产éå·²ç»å¨æ¥å·¥å®¡æ¹æ¶ç´¯è®¡ï¼è´¨æ£åè°ä¸è½éå¤ç´¯è®¡ã |
| | | verify(taskService).updateQualityQuantity(taskId, qualifiedQty, unqualifiedQty); |
| | | verify(taskService, never()).updateProducedQuantity(anyLong(), any(), any(), any()); |
| | | verify(workOrderService, never()).updateProducedQuantity(anyLong(), any()); |
| | | } |
| | |
| | | when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId)) |
| | | .thenReturn(routeProcess); |
| | | |
| | | // mock: 产å产åºå |
| | | MesWmProductProduceDO produce = randomPojo(MesWmProductProduceDO.class); |
| | | when(productProduceService.generateProductProduce(any(), eq(false), any())).thenReturn(produce); |
| | | |
| | | // mock: 产åºè¡ï¼ç¨äº updateTaskAndWorkOrderByFeedbackï¼ |
| | | // å
³é®æ«éå·¥åºç´¯è®¡å·¥å产éï¼è´¨éæ°éçå¾
æ£éªã |
| | | when(workOrderProcessService.getLastWorkOrderProcessByWorkOrderId(feedback.getWorkOrderId())) |
| | | .thenReturn(new MesProWorkOrderProcessDO().setProcessId(processId)); |
| | | MesWmProductProduceLineDO qualifiedLine = MesWmProductProduceLineDO.builder() |
| | | .quantity(BigDecimal.valueOf(80)) |
| | | .qualityStatus(MesWmQualityStatusEnum.PASS.getStatus()).build(); |
| | | when(produceLineService.getProductProduceLineListByFeedbackId(feedback.getId())) |
| | | .thenReturn(ListUtil.of(qualifiedLine)); |
| | | |
| | | // è°ç¨ |
| | | MesProFeedbackApproveReqVO reqVO = new MesProFeedbackApproveReqVO(); |
| | | reqVO.setId(feedback.getId()); |
| | | boolean result = feedbackService.approveFeedback(reqVO); |
| | | |
| | | // æè¨ 1ï¼è¿å trueï¼å·²å®æï¼ |
| | | assertTrue(result); |
| | | assertFalse(result); |
| | | |
| | | // æè¨ 2ï¼ç¶æä¸ºå·²å®æï¼uncheckQuantity æ¸
é¶ |
| | | // æªç»æ£éªçæ¥å·¥ä¸å
è®¸ç´æ¥å
¥åºã |
| | | MesProFeedbackDO updated = feedbackMapper.selectById(feedback.getId()); |
| | | assertEquals(MesProFeedbackStatusEnum.FINISHED.getStatus(), updated.getStatus()); |
| | | assertEquals(0, BigDecimal.ZERO.compareTo(updated.getUncheckQuantity())); |
| | | assertEquals(MesProFeedbackStatusEnum.UNCHECK.getStatus(), updated.getStatus()); |
| | | assertEquals(0, feedback.getFeedbackQuantity().compareTo(updated.getUncheckQuantity())); |
| | | assertEquals(0, BigDecimal.ZERO.compareTo(updated.getQualifiedQuantity())); |
| | | |
| | | // æè¨ 3ï¼è°ç¨äºäº§åºåçæ + å
¥åº + ä»»å¡/å·¥åæ´æ° |
| | | verify(productProduceService).generateProductProduce(any(), eq(false), any()); |
| | | verify(productProduceService).finishProductProduce(produce.getId()); |
| | | verify(productProduceService).generateProductProduce(any(), eq(true), any()); |
| | | verify(productProduceService, never()).finishProductProduce(anyLong()); |
| | | verify(taskService).updateProducedQuantity(eq(feedback.getTaskId()), |
| | | any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class)); |
| | | verify(workOrderService).updateProducedQuantity(eq(feedback.getWorkOrderId()), |
| | |
| | | .keyFlag(false).checkFlag(false).build(); |
| | | when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId)) |
| | | .thenReturn(routeProcess); |
| | | MesWmProductProduceDO produce = randomPojo(MesWmProductProduceDO.class); |
| | | when(productProduceService.generateProductProduce(any(), eq(false), any())).thenReturn(produce); |
| | | |
| | | // è°ç¨ |
| | | MesProFeedbackApproveReqVO reqVO = new MesProFeedbackApproveReqVO(); |
| | | reqVO.setId(feedback.getId()); |
| | | boolean result = feedbackService.approveFeedback(reqVO); |
| | | |
| | | // æè¨ 1ï¼è¿å trueï¼å·²å®æï¼ |
| | | assertTrue(result); |
| | | assertFalse(result); |
| | | |
| | | // æè¨ 2ï¼ç¶æä¸ºå·²å®æ |
| | | MesProFeedbackDO updated = feedbackMapper.selectById(feedback.getId()); |
| | | assertEquals(MesProFeedbackStatusEnum.FINISHED.getStatus(), updated.getStatus()); |
| | | assertEquals(MesProFeedbackStatusEnum.UNCHECK.getStatus(), updated.getStatus()); |
| | | |
| | | // éå
³é®å·¥åºä¹çæäº§åºå¹¶ç´¯è®¡ä»»å¡è¿åº¦ï¼ä½ä¸ç´¯è®¡å·¥åæ»äº§éã |
| | | verify(productProduceService).generateProductProduce(any(), eq(false), any()); |
| | | verify(productProduceService).finishProductProduce(produce.getId()); |
| | | verify(productProduceService).generateProductProduce(any(), eq(true), any()); |
| | | verify(productProduceService, never()).finishProductProduce(anyLong()); |
| | | verify(taskService).updateProducedQuantity(eq(feedback.getTaskId()), any(), any(), any()); |
| | | verify(workOrderService, never()).updateProducedQuantity(anyLong(), any()); |
| | | } |
| | |
| | | when(routeProcessService.getRouteProcessByRouteIdAndProcessId(routeId, processId)) |
| | | .thenReturn(routeProcess); |
| | | |
| | | // è°ç¨ï¼åºè¯¥æå¼å¸¸ |
| | | // å¾
æ£æ°éä¸å黿¢éæ£éªæ è¯å·¥åºæ¥å·¥ã |
| | | MesProFeedbackApproveReqVO reqVO = new MesProFeedbackApproveReqVO(); |
| | | reqVO.setId(feedback.getId()); |
| | | assertThrows(Exception.class, () -> |
| | | feedbackService.approveFeedback(reqVO)); |
| | | |
| | | // æè¨ï¼ä¸åºè¯¥æ§è¡ä»»ä½åç»æä½ |
| | | verify(productProduceService, never()).generateProductProduce(any(), anyBoolean(), any()); |
| | | verify(itemConsumeService, never()).generateItemConsume(any()); |
| | | assertFalse(feedbackService.approveFeedback(reqVO)); |
| | | verify(productProduceService).generateProductProduce(any(), eq(true), any()); |
| | | verify(productProduceService, never()).finishProductProduce(anyLong()); |
| | | } |
| | | |
| | | @Test |
| | |
| | | // è´¨æ£å·¥åºæ 论æ¯å¦å
³é®å·¥åºï¼é½è¿å
¥å¾
æ£éªã |
| | | MesProFeedbackDO updated = feedbackMapper.selectById(feedback.getId()); |
| | | assertEquals(MesProFeedbackStatusEnum.UNCHECK.getStatus(), updated.getStatus()); |
| | | assertEquals(0, BigDecimal.ZERO.compareTo(updated.getUncheckQuantity())); |
| | | assertEquals(0, feedback.getFeedbackQuantity().compareTo(updated.getUncheckQuantity())); |
| | | |
| | | verify(productProduceService).generateProductProduce(any(), eq(true), any()); |
| | | verify(productProduceService, never()).finishProductProduce(anyLong()); |
| | |
| | | MesProFeedbackQuickCreateReqVO req = new MesProFeedbackQuickCreateReqVO(); |
| | | req.setTaskId(curTaskId); |
| | | req.setFeedbackQuantity(BigDecimal.TEN); |
| | | req.setFeedbackTime(java.time.LocalDateTime.of(2026, 10, 20, 9, 30)); |
| | | MesProFeedbackQuickCreateRespVO resp = feedbackService.quickCreateFeedback(req); |
| | | assertNotNull(resp); |
| | | assertNotNull(resp.getFeedbackId()); |
| | | assertEquals(req.getFeedbackTime(), feedbackMapper.selectById(resp.getFeedbackId()).getFeedbackTime()); |
| | | verify(autoCodeRecordService).generateAutoCode(anyString()); |
| | | } |
| | | |
| | |
| | | @Import(MesQcIpqcServiceImpl.class) |
| | | public class MesQcIpqcServiceImplTest extends BaseDbUnitTest { |
| | | |
| | | @MockitoBean |
| | | private cn.iocoder.yudao.module.mes.service.qc.imports.MesQcImportHelper qcImportHelper; |
| | | @MockitoBean |
| | | private cn.iocoder.yudao.module.mes.service.qc.ncr.MesQcNcrService ncrService; |
| | | |
| | | @Resource |
| | | private MesQcIpqcServiceImpl ipqcService; |
| | | |
| | |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | // çäº§å®æåä»å¯æå·¥è¡¥å½è´¨æ£ã |
| | | workOrder.setStatus(cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum.FINISHED.getStatus()); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | // mock 模æ¿å¹é
|
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | when(templateItemService.getTemplateItemList(eq(productId), anyInt(), isNull(), isNull())).thenReturn(java.util.List.of(templateItem)); |
| | | // mock å·¥èºè·¯çº¿ |
| | | MesProRouteProductDO routeProduct = new MesProRouteProductDO(); |
| | | routeProduct.setRouteId(routeId); |
| | |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | workOrder.setStatus(cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum.CONFIRMED.getStatus()); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | // mock ä»»å¡ |
| | |
| | | task.setWorkOrderId(workOrderId); |
| | | task.setWorkstationId(workstationId); |
| | | task.setItemId(productId); |
| | | when(taskService.validateTaskNotFinished(taskId)).thenReturn(task); |
| | | when(taskService.validateTaskExists(taskId)).thenReturn(task); |
| | | // mock 模æ¿å¹é
|
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | when(templateItemService.getTemplateItemList(eq(productId), anyInt(), isNull(), isNull())).thenReturn(java.util.List.of(templateItem)); |
| | | |
| | | // è°ç¨ |
| | | Long ipqcId = ipqcService.createIpqc(reqVO); |
| | |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | workOrder.setStatus(cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum.CONFIRMED.getStatus()); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | // mock 模æ¿å¹é
|
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | when(templateItemService.getTemplateItemList(eq(productId), anyInt(), isNull(), isNull())).thenReturn(java.util.List.of(templateItem)); |
| | | // mock å·¥èºè·¯çº¿åå¨ |
| | | MesProRouteProductDO routeProduct = new MesProRouteProductDO(); |
| | | routeProduct.setRouteId(routeId); |
| | |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | workOrder.setStatus(cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum.CONFIRMED.getStatus()); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | when(templateItemService.getTemplateItemList(eq(productId), anyInt(), isNull(), isNull())).thenReturn(java.util.List.of(templateItem)); |
| | | when(feedbackService.validateFeedbackExists(feedbackId)).thenReturn(new MesProFeedbackDO()); |
| | | |
| | | // è°ç¨ï¼æè¨å¼å¸¸ |
| | |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | workOrder.setStatus(cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum.CONFIRMED.getStatus()); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | when(templateItemService.getTemplateItemList(eq(productId), anyInt(), isNull(), isNull())).thenReturn(java.util.List.of(templateItem)); |
| | | when(feedbackService.validateFeedbackExists(feedbackId)).thenReturn(new MesProFeedbackDO()); |
| | | // mockï¼äº§åºè¡åå¨ä½ feedbackId ä¸å¹é
|
| | | MesWmProductProduceLineDO line = new MesWmProductProduceLineDO(); |
| | |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | workOrder.setStatus(cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum.CONFIRMED.getStatus()); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | when(templateItemService.getTemplateItemList(eq(productId), anyInt(), isNull(), isNull())).thenReturn(java.util.List.of(templateItem)); |
| | | when(feedbackService.validateFeedbackExists(feedbackId)).thenReturn(new MesProFeedbackDO()); |
| | | // mockï¼äº§åºè¡åå¨ä¸å½å±æ£ç¡®ï¼ä½ä¸æ¯ PENDING ç¶æ |
| | | MesWmProductProduceLineDO line = new MesWmProductProduceLineDO(); |
| | |
| | | MesProWorkOrderDO workOrder = new MesProWorkOrderDO(); |
| | | workOrder.setId(workOrderId); |
| | | workOrder.setProductId(productId); |
| | | workOrder.setStatus(cn.iocoder.yudao.module.mes.enums.pro.MesProWorkOrderStatusEnum.CONFIRMED.getStatus()); |
| | | when(workOrderService.validateWorkOrderExists(workOrderId)).thenReturn(workOrder); |
| | | when(workOrderService.validateWorkOrderConfirmed(workOrderId)).thenReturn(workOrder); |
| | | MesQcTemplateItemDO templateItem = new MesQcTemplateItemDO(); |
| | | templateItem.setTemplateId(templateId); |
| | | when(templateItemService.getRequiredTemplateByItemIdAndType(eq(productId), anyInt())).thenReturn(templateItem); |
| | | when(templateItemService.getTemplateItemList(eq(productId), anyInt(), isNull(), isNull())).thenReturn(java.util.List.of(templateItem)); |
| | | MesProFeedbackDO feedback = new MesProFeedbackDO(); |
| | | feedback.setId(feedbackId); |
| | | feedback.setCode("FB-001"); |
| | |
| | | package cn.iocoder.yudao.module.mes.service.qc.oqc; |
| | | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.oqc.MesQcOqcDO; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderItemRespDTO; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.template.MesQcTemplateItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO; |
| | |
| | | @Mock private MesQcTemplateItemService templates; |
| | | @Mock private MesQcOqcLineService lines; |
| | | @Mock private MesWmProductSalesService sales; |
| | | @Mock private ErpSaleOrderApi saleOrderApi; |
| | | |
| | | @Test |
| | | void generatedInspectionUsesHistoricalShipmentDateAndPreservesPendingQuantities() { |
| | |
| | | .thenReturn(new MesQcTemplateItemDO().setTemplateId(20L)); |
| | | when(sales.getProductSales(30L)).thenReturn(new MesWmProductSalesDO().setSalesDate(date)); |
| | | MesWmProductSalesLineDO line = new MesWmProductSalesLineDO().setId(40L) |
| | | .setItemId(10L).setQuantity(BigDecimal.valueOf(200)).setBatchCode("B001"); |
| | | .setItemId(10L).setSaleOrderItemId(60L).setQuantity(BigDecimal.valueOf(200)).setBatchCode("B001"); |
| | | ErpSaleOrderItemRespDTO saleItem = new ErpSaleOrderItemRespDTO(); |
| | | saleItem.setReelNo("PAN-001"); |
| | | when(saleOrderApi.getSaleOrderItem(60L)).thenReturn(CommonResult.success(saleItem)); |
| | | service.createOqcFromProductSales(30L, line, "SL001", 50L); |
| | | ArgumentCaptor<MesQcOqcDO> captor = ArgumentCaptor.forClass(MesQcOqcDO.class); |
| | | verify(mapper).insert(captor.capture()); |
| | |
| | | assertEquals(BigDecimal.ZERO, row.getQualifiedQuantity()); |
| | | assertEquals(30L, row.getSourceDocId()); |
| | | assertEquals(40L, row.getSourceLineId()); |
| | | assertEquals("PAN-001", row.getReelNo()); |
| | | } |
| | | |
| | | @Test |
| | | void historicalShipmentWithoutSaleItemDoesNotGuessReel() { |
| | | when(templates.getRequiredTemplateByItemIdAndType(10L, 3)) |
| | | .thenReturn(new MesQcTemplateItemDO().setTemplateId(20L)); |
| | | MesWmProductSalesLineDO line = new MesWmProductSalesLineDO().setId(40L) |
| | | .setItemId(10L).setQuantity(BigDecimal.ONE); |
| | | |
| | | service.createOqcFromProductSales(30L, line, "SL001", 50L); |
| | | |
| | | ArgumentCaptor<MesQcOqcDO> captor = ArgumentCaptor.forClass(MesQcOqcDO.class); |
| | | verify(mapper).insert(captor.capture()); |
| | | assertNull(captor.getValue().getReelNo()); |
| | | verifyNoInteractions(saleOrderApi); |
| | | } |
| | | } |
| | |
| | | @Import(MesQcOqcServiceImpl.class) |
| | | public class MesQcOqcServiceImplTest extends BaseDbUnitTest { |
| | | |
| | | @MockitoBean |
| | | private cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi saleOrderApi; |
| | | @MockitoBean |
| | | private cn.iocoder.yudao.module.mdm.api.item.MdmItemApi mdmItemApi; |
| | | @MockitoBean |
| | | private cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi customerApi; |
| | | @MockitoBean |
| | | private cn.iocoder.yudao.module.mes.service.qc.imports.MesQcImportHelper qcImportHelper; |
| | | @MockitoBean |
| | | private cn.iocoder.yudao.module.mes.service.qc.ncr.MesQcNcrService ncrService; |
| | | |
| | | @Resource |
| | | private MesQcOqcServiceImpl oqcService; |
| | | |
| | |
| | | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchGenerateReqVO; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.itemconsume.MesWmItemConsumeDetailDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; |
| | |
| | | |
| | | import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId; |
| | | import static org.junit.jupiter.api.Assertions.*; |
| | | import static org.mockito.Mockito.*; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_BATCH_REEL_MISMATCH; |
| | | |
| | | /** |
| | | * {@link MesWmBatchServiceImpl} çåå
æµè¯ |
| | |
| | | */ |
| | | @Import(MesWmBatchServiceImpl.class) |
| | | public class MesWmBatchServiceImplTest extends BaseDbUnitTest { |
| | | |
| | | @MockitoBean |
| | | private cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService mdmItemService; |
| | | @MockitoBean |
| | | private cn.iocoder.yudao.module.mdm.api.item.MdmItemBatchConfigApi mdmBatchConfigApi; |
| | | @MockitoBean |
| | | private cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi customerApi; |
| | | |
| | | @Resource |
| | | private MesWmBatchServiceImpl batchService; |
| | |
| | | @MockitoBean |
| | | private MesWmBarcodeService barcodeService; |
| | | |
| | | @Test |
| | | public void testNonBatchManagedItemUsesDistinctTraceBatchesForReels() { |
| | | when(mdmItemService.validateItemExists(10L)).thenReturn(new MesMdmItemDO().setId(10L) |
| | | .setName("产å").setIsBatchManaged(false)); |
| | | when(autoCodeRecordService.generateAutoCode(anyString())).thenReturn("TRACE-1", "TRACE-2"); |
| | | MesWmBatchGenerateReqVO first = new MesWmBatchGenerateReqVO().setItemId(10L) |
| | | .setWorkOrderId(20L).setReelNo("PAN-1"); |
| | | MesWmBatchGenerateReqVO second = new MesWmBatchGenerateReqVO().setItemId(10L) |
| | | .setWorkOrderId(20L).setReelNo("PAN-2"); |
| | | |
| | | MesWmBatchDO a = batchService.getOrGenerateBatchCode(first); |
| | | MesWmBatchDO b = batchService.getOrGenerateBatchCode(second); |
| | | assertNotNull(a.getId()); |
| | | assertNotEquals(a.getId(), b.getId()); |
| | | assertEquals(a.getId(), batchService.getOrGenerateBatchCode(first).getId()); |
| | | assertEquals("PAN-2", batchMapper.selectById(b.getId()).getReelNo()); |
| | | assertNull(batchService.getOrGenerateBatchCode(new MesWmBatchGenerateReqVO().setItemId(10L))); |
| | | } |
| | | |
| | | @Test |
| | | public void testExistingBatchCannotBeRelabeledWithDifferentReel() { |
| | | when(mdmItemService.validateItemExists(10L)).thenReturn(new MesMdmItemDO().setId(10L) |
| | | .setName("产å").setIsBatchManaged(false)); |
| | | batchMapper.insert(new MesWmBatchDO().setCode("EXISTING").setItemId(10L).setReelNo("PAN-1")); |
| | | MesWmBatchGenerateReqVO request = new MesWmBatchGenerateReqVO().setItemId(10L) |
| | | .setBatchCode("EXISTING").setReelNo("PAN-2"); |
| | | ServiceException ex = assertThrows(ServiceException.class, () -> batchService.getOrGenerateBatchCode(request)); |
| | | assertEquals(WM_BATCH_REEL_MISMATCH.getCode(), ex.getCode()); |
| | | } |
| | | |
| | | @Test |
| | | public void testExistingReelBatchCannotBeReassignedToAnotherOrder() { |
| | | when(mdmItemService.validateItemExists(10L)).thenReturn(new MesMdmItemDO().setId(10L) |
| | | .setName("产å").setIsBatchManaged(false)); |
| | | batchMapper.insert(new MesWmBatchDO().setCode("EXISTING").setItemId(10L) |
| | | .setReelNo("PAN-1").setWorkOrderId(20L)); |
| | | MesWmBatchGenerateReqVO request = new MesWmBatchGenerateReqVO().setItemId(10L) |
| | | .setBatchCode("EXISTING").setReelNo("PAN-1").setWorkOrderId(21L); |
| | | ServiceException ex = assertThrows(ServiceException.class, () -> batchService.getOrGenerateBatchCode(request)); |
| | | assertEquals(WM_BATCH_REEL_MISMATCH.getCode(), ex.getCode()); |
| | | } |
| | | |
| | | // ==================== åå追溯 ==================== |
| | | |
| | | @Test |
| | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.feedback.MesProFeedbackDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.workorder.MesProWorkOrderDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceDetailDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productproduce.MesWmProductProduceLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; |
| | |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductProduceStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmQualityStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.workorder.MesProWorkOrderProcessService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.route.MesProRouteProductService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | |
| | | @MockitoBean |
| | | private MesProWorkOrderService workOrderService; |
| | | @MockitoBean |
| | | private MesProWorkOrderProcessService workOrderProcessService; |
| | | @MockitoBean |
| | | private MesProRouteProductService routeProductService; |
| | | @MockitoBean |
| | | private MesWmBatchService batchService; |
| | | @MockitoBean |
| | | private MesWmTransactionService wmTransactionService; |
| | |
| | | private MesWmWarehouseLocationService locationService; |
| | | @MockitoBean |
| | | private MesWmWarehouseAreaService areaService; |
| | | |
| | | @Test |
| | | public void testGeneratePendingProduceCopiesOrderReel() { |
| | | MesProFeedbackDO feedback = new MesProFeedbackDO().setId(2L).setWorkOrderId(3L) |
| | | .setTaskId(4L).setItemId(5L).setFeedbackQuantity(BigDecimal.TEN); |
| | | when(workOrderService.getWorkOrder(3L)).thenReturn(new MesProWorkOrderDO().setReelNo("PAN-001")); |
| | | |
| | | productProduceService.generateProductProduce(feedback, true, null); |
| | | |
| | | ArgumentCaptor<MesWmProductProduceLineDO> captor = ArgumentCaptor.forClass(MesWmProductProduceLineDO.class); |
| | | verify(productProduceLineService).createProductProduceLine(captor.capture()); |
| | | assertEquals("PAN-001", captor.getValue().getReelNo()); |
| | | assertEquals(MesWmQualityStatusEnum.PENDING.getStatus(), captor.getValue().getQualityStatus()); |
| | | } |
| | | |
| | | @Test |
| | | public void testGenerateProduceRejectsDirectInbound() { |
| | | ServiceException ex = assertThrows(ServiceException.class, |
| | | () -> productProduceService.generateProductProduce(new MesProFeedbackDO(), false, null)); |
| | | assertEquals(WM_PRODUCT_PRODUCE_QUALITY_PENDING.getCode(), ex.getCode()); |
| | | verifyNoInteractions(wmTransactionService); |
| | | } |
| | | |
| | | @Test |
| | | public void testFinishProductProduceRejectsPendingInspection() { |
| | | MesWmProductProduceDO produce = randomPojo(MesWmProductProduceDO.class, |
| | | o -> o.setStatus(MesWmProductProduceStatusEnum.PREPARE.getStatus())); |
| | | productProduceMapper.insert(produce); |
| | | MesWmProductProduceLineDO pending = MesWmProductProduceLineDO.builder() |
| | | .id(1L).produceId(produce.getId()).quantity(BigDecimal.TEN) |
| | | .qualityStatus(MesWmQualityStatusEnum.PENDING.getStatus()).build(); |
| | | when(productProduceLineService.getProductProduceLineListByProduceId(produce.getId())) |
| | | .thenReturn(ListUtil.of(pending)); |
| | | |
| | | ServiceException ex = assertThrows(ServiceException.class, |
| | | () -> productProduceService.finishProductProduce(produce.getId())); |
| | | assertEquals(WM_PRODUCT_PRODUCE_QUALITY_PENDING.getCode(), ex.getCode()); |
| | | verify(wmTransactionService, never()).createTransaction(any()); |
| | | } |
| | | |
| | | @Test |
| | | public void testSplitPendingRejectsWrongQuantity() { |
| | | Long feedbackId = randomLongId(); |
| | | MesWmProductProduceDO produce = randomPojo(MesWmProductProduceDO.class, |
| | | o -> o.setFeedbackId(feedbackId).setStatus(MesWmProductProduceStatusEnum.PREPARE.getStatus())); |
| | | productProduceMapper.insert(produce); |
| | | MesWmProductProduceLineDO pending = MesWmProductProduceLineDO.builder() |
| | | .id(1L).produceId(produce.getId()).feedbackId(feedbackId).quantity(BigDecimal.TEN) |
| | | .qualityStatus(MesWmQualityStatusEnum.PENDING.getStatus()).build(); |
| | | when(productProduceLineService.validateProductProduceLineExists(1L)).thenReturn(pending); |
| | | |
| | | ServiceException ex = assertThrows(ServiceException.class, |
| | | () -> productProduceService.splitPendingAndFinishProduce(feedbackId, 1L, |
| | | BigDecimal.valueOf(9), BigDecimal.ZERO)); |
| | | assertEquals(WM_PRODUCT_PRODUCE_INSPECTION_INVALID.getCode(), ex.getCode()); |
| | | verify(wmTransactionService, never()).createTransaction(any()); |
| | | } |
| | | |
| | | @Test |
| | | public void testSplitPendingAndFinishProduce_withUnqualified() { |
| | |
| | | .id(sourceLineId).produceId(produce.getId()).feedbackId(feedbackId) |
| | | .itemId(itemId).quantity(BigDecimal.valueOf(100)) |
| | | .batchId(batchId).batchCode(batchCode) |
| | | .reelNo("PAN-001") |
| | | .qualityStatus(MesWmQualityStatusEnum.PENDING.getStatus()) |
| | | .build(); |
| | | when(productProduceLineService.validateProductProduceLineExists(sourceLineId)) |
| | |
| | | assertEquals(MesWmQualityStatusEnum.FAIL.getStatus(), createdLine.getQualityStatus()); |
| | | assertEquals(0, BigDecimal.valueOf(20).compareTo(createdLine.getQuantity())); |
| | | assertEquals(itemId, createdLine.getItemId()); |
| | | assertEquals("PAN-001", createdLine.getReelNo()); |
| | | |
| | | // æè¨ 2ï¼åå¾
æ£è¡ - æ´æ°ä¸ºåæ ¼å |
| | | ArgumentCaptor<MesWmProductProduceLineDO> updateCaptor = ArgumentCaptor.forClass(MesWmProductProduceLineDO.class); |
| | |
| | | assertEquals(0, BigDecimal.valueOf(80).compareTo(updatedLine.getQuantity())); |
| | | |
| | | // æè¨ 3ï¼çæäº 2 æ¡æç»ï¼ä¸åæ ¼åæç» + åæ ¼åæç»ï¼ |
| | | verify(productProduceDetailService, times(2)).createProductProduceDetail(any()); |
| | | ArgumentCaptor<MesWmProductProduceDetailDO> detailCaptor = ArgumentCaptor.forClass(MesWmProductProduceDetailDO.class); |
| | | verify(productProduceDetailService, times(2)).createProductProduceDetail(detailCaptor.capture()); |
| | | assertTrue(detailCaptor.getAllValues().stream().allMatch(d -> "PAN-001".equals(d.getReelNo()))); |
| | | |
| | | // æè¨ 4ï¼äº§åºåç¶ææ´æ°ä¸ºå·²å®æ |
| | | MesWmProductProduceDO updatedProduce = productProduceMapper.selectById(produce.getId()); |
| | | assertEquals(MesWmProductProduceStatusEnum.FINISHED.getStatus(), updatedProduce.getStatus()); |
| | | |
| | | // æè¨ 5ï¼å建äºåºåäºå¡ |
| | | verify(wmTransactionService, times(2)).createTransaction(any()); |
| | | // æè¨ 5ï¼åªæåæ ¼åçæåºåäºå¡ï¼ä¸åæ ¼åä¸è¿å
¥åæ ¼åºå |
| | | verify(wmTransactionService, times(1)).createTransaction(any()); |
| | | } |
| | | |
| | | @Test |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.wm.productsales; |
| | | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; |
| | | import cn.iocoder.yudao.module.erp.api.sale.dto.ErpSaleOrderItemRespDTO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.junit.jupiter.api.extension.ExtendWith; |
| | | import org.mockito.InjectMocks; |
| | | import org.mockito.Mock; |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PRODUCT_SALES_REEL_MISMATCH; |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_PRODUCT_SALES_REEL_SOURCE_MISSING; |
| | | import static org.junit.jupiter.api.Assertions.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | @ExtendWith(MockitoExtension.class) |
| | | class MesWmSalesReelGuardTest { |
| | | @InjectMocks private MesWmSalesReelGuard guard; |
| | | @Mock private ErpSaleOrderApi saleOrderApi; |
| | | @Mock private MesWmBatchService batchService; |
| | | |
| | | @Test |
| | | void acceptsOnlyMatchingTracedInventory() { |
| | | var line = new MesWmProductSalesLineDO().setSaleOrderItemId(10L); |
| | | var item = new ErpSaleOrderItemRespDTO(); |
| | | item.setReelNo("PAN-1"); |
| | | when(saleOrderApi.getSaleOrderItem(10L)).thenReturn(CommonResult.success(item)); |
| | | when(batchService.getBatch(20L)).thenReturn(new MesWmBatchDO().setReelNo("PAN-1")); |
| | | when(batchService.getBatch(21L)).thenReturn(new MesWmBatchDO().setReelNo("PAN-2")); |
| | | |
| | | assertDoesNotThrow(() -> guard.validate(line, 20L)); |
| | | ServiceException wrong = assertThrows(ServiceException.class, () -> guard.validate(line, 21L)); |
| | | assertEquals(WM_PRODUCT_SALES_REEL_MISMATCH.getCode(), wrong.getCode()); |
| | | assertThrows(ServiceException.class, () -> guard.validate(line, null)); |
| | | } |
| | | |
| | | @Test |
| | | void preservesLegacyWithoutSalesItem() { |
| | | assertDoesNotThrow(() -> guard.validate(new MesWmProductSalesLineDO(), null)); |
| | | verifyNoInteractions(saleOrderApi, batchService); |
| | | } |
| | | |
| | | @Test |
| | | void rejectsMissingReferencedSalesItem() { |
| | | when(saleOrderApi.getSaleOrderItem(10L)).thenReturn(CommonResult.success(null)); |
| | | ServiceException ex = assertThrows(ServiceException.class, |
| | | () -> guard.validate(new MesWmProductSalesLineDO().setSaleOrderItemId(10L), 20L)); |
| | | assertEquals(WM_PRODUCT_SALES_REEL_SOURCE_MISSING.getCode(), ex.getCode()); |
| | | verifyNoInteractions(batchService); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.wm.transaction; |
| | | |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; |
| | | import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.junit.jupiter.api.extension.ExtendWith; |
| | | import org.mockito.InjectMocks; |
| | | import org.mockito.Mock; |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_MATERIAL_STOCK_SELECTION_MISMATCH; |
| | | import static org.junit.jupiter.api.Assertions.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | @ExtendWith(MockitoExtension.class) |
| | | class MesWmTransactionReelSafetyTest { |
| | | @InjectMocks private MesWmTransactionServiceImpl service; |
| | | @Mock private MesWmMaterialStockService stockService; |
| | | |
| | | @Test |
| | | void rejectsSelectedStockWithDifferentBatch() { |
| | | when(stockService.getMaterialStock(1L)).thenReturn(new MesWmMaterialStockDO().setId(1L) |
| | | .setItemId(2L).setWarehouseId(3L).setLocationId(4L).setAreaId(5L).setBatchId(6L)); |
| | | MesWmTransactionSaveReqDTO req = new MesWmTransactionSaveReqDTO() |
| | | .setType(MesWmTransactionTypeEnum.OUT.getType()).setQuantity(BigDecimal.ONE.negate()) |
| | | .setItemId(2L).setWarehouseId(3L).setLocationId(4L).setAreaId(5L) |
| | | .setBatchId(7L).setBatchCode("other-batch").setMaterialStockId(1L); |
| | | |
| | | ServiceException ex = assertThrows(ServiceException.class, () -> service.createTransaction(req)); |
| | | assertEquals(WM_MATERIAL_STOCK_SELECTION_MISMATCH.getCode(), ex.getCode()); |
| | | verify(stockService, never()).updateMaterialStockQuantity(anyLong(), any(), anyBoolean()); |
| | | } |
| | | } |
| | |
| | | "source_line_id" bigint DEFAULT NULL, |
| | | "source_doc_code" varchar(64) DEFAULT NULL, |
| | | "work_order_id" bigint DEFAULT NULL, |
| | | "reel_no" varchar(128) DEFAULT NULL, |
| | | "task_id" bigint DEFAULT NULL, |
| | | "workstation_id" bigint DEFAULT NULL, |
| | | "process_id" bigint DEFAULT NULL, |
| | |
| | | "quantity" decimal(12,2) DEFAULT NULL, |
| | | "batch_id" bigint DEFAULT NULL, |
| | | "batch_code" varchar(255) DEFAULT NULL, |
| | | "reel_no" varchar(128) DEFAULT NULL, |
| | | "expire_date" timestamp DEFAULT NULL, |
| | | "lot_number" varchar(128) DEFAULT NULL, |
| | | "quality_status" int DEFAULT NULL, |
| | |
| | | "quantity" decimal(12,2) DEFAULT NULL, |
| | | "batch_id" bigint DEFAULT NULL, |
| | | "batch_code" varchar(255) DEFAULT NULL, |
| | | "reel_no" varchar(128) DEFAULT NULL, |
| | | "warehouse_id" bigint DEFAULT NULL, |
| | | "location_id" bigint DEFAULT NULL, |
| | | "area_id" bigint DEFAULT NULL, |
| | |
| | | "source_doc_id" bigint DEFAULT NULL, |
| | | "source_line_id" bigint DEFAULT NULL, |
| | | "source_doc_code" varchar(64) DEFAULT NULL, |
| | | "reel_no" varchar(128) DEFAULT NULL, |
| | | "client_id" bigint DEFAULT NULL, |
| | | "batch_code" varchar(64) DEFAULT NULL, |
| | | "item_id" bigint NOT NULL, |
| | |
| | | "id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY, |
| | | "code" varchar(128) DEFAULT NULL, |
| | | "item_id" bigint DEFAULT NULL, |
| | | "reel_no" varchar(128) DEFAULT NULL, |
| | | "produce_date" timestamp DEFAULT NULL, |
| | | "expire_date" timestamp DEFAULT NULL, |
| | | "receipt_date" timestamp DEFAULT NULL, |