feat(crm,mes): 扩展客户字段与重构批次号生成逻辑
- 客户实体新增社会信用代码及上级经销商等扩展字段
- 客户查询与保存接口支持新增字段的传入与返回
- 增加客户所属上级经销商不能选择自身的校验逻辑
- 重构生产批次号生成规则为四位年份加三位流水号
- 生产批次支持手动输入批次号或系统自动生成
- 简化工作站仓库层级处理并移除库区库位强校验
- 批次列表接口新增返回关联的正式托盘码列表
- 新增袋码与托盘码的批量扫码内容反查方法
- 更新扫码入库与库存袋码查询相关的接口文档
已添加38个文件
已删除2个文件
已修改90个文件
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # 客æ·ç®¡çæ°å¢å段ï¼ç¤¾ä¼ä¿¡ç¨ä»£ç ç 7 项ï¼- å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - CRM â 客æ·ç®¡ç â 客æ·å表 |
| | | - CRM â 客æ·ç®¡ç â æ°å¢/ç¼è¾å®¢æ·å¼¹çª |
| | | - CRM â 客æ·ç®¡ç â 客æ·è¯¦æ
|
| | | |
| | | ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å
¥ |
| | | |
| | | 1. æ°å¢/ç¼è¾å®¢æ·æ¶ï¼å¯å½å
¥ 7 个æ°å段ï¼ä¿ååæä¹
åå° `crm_customer` 表ã |
| | | 2. å表å±ç¤º 7 个æ°å段ï¼ãæå±ä¸çº§ç»éåãå±ç¤ºå
¶åç§°ï¼`parentDealerName`ï¼ã |
| | | 3. åè¡¨æ¯ææ 6 个æ°å段çéï¼ç¤¾ä¼ä¿¡ç¨ä»£ç ã身份è¯å·ã客æ·å§åã夿¡æµæ°´å·ãæå±åºåãæå±ä¸çº§ç»éåï¼ã |
| | | 4. ãæå±ä¸çº§ç»éåãå¤ç¨ CRM 客æ·ä¸»æ°æ®ï¼éæ©å¼¹çªä¸å®¢æ·éæ©ç»ä»¶ä¸è´ï¼åç«¯æ ¡éªä¸è½éæ©å®¢æ·èªèº«ã |
| | | 5. 详æ
页å±ç¤º 7 个æ°å段ï¼å
¶ä¸ãæå±ä¸çº§ç»éåãå±ç¤ºåç§°ã |
| | | |
| | | ## API |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /crm/customer/page | 客æ·åé¡µï¼æ°å¢çéåæ°ãååºåæ®µï¼ | |
| | | | POST | /crm/customer/create | æ°å¢å®¢æ·ï¼æ°å¢è¯·æ±åæ®µï¼ | |
| | | | PUT | /crm/customer/update | ä¿®æ¹å®¢æ·ï¼æ°å¢è¯·æ±åæ®µï¼ | |
| | | | GET | /crm/customer/get | 客æ·è¯¦æ
ï¼æ°å¢ååºåæ®µï¼ | |
| | | |
| | | ### 请æ±å段æ°å¢ï¼create / updateï¼ |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | socialCreditCode | String | å¦ | 社ä¼ä¿¡ç¨ä»£ç ï¼æé¿ 64 å符 | |
| | | | idCardNo | String | å¦ | 身份è¯å·ï¼æé¿ 32 å符 | |
| | | | customerName | String | å¦ | 客æ·å§åï¼æé¿ 64 å符 | |
| | | | filingSerialNo | String | å¦ | 夿¡æµæ°´å·ï¼æé¿ 64 å符 | |
| | | | bankAccountName | String | å¦ | é¶è¡è´¦æ·åç§°ï¼æé¿ 128 å符 | |
| | | | region | String | å¦ | æå±åºåï¼æé¿ 64 å符 | |
| | | | parentDealerId | Long | å¦ | æå±ä¸çº§ç»éåç¼å·ï¼å
³è客æ·è¡¨ `id` | |
| | | |
| | | ### æ¥è¯¢åæ°æ°å¢ï¼pageï¼ |
| | | |
| | | | åæ° | ç±»å | å¹é
æ¹å¼ | 说æ | |
| | | |------|------|----------|------| |
| | | | socialCreditCode | String | 模ç³å¹é
| 社ä¼ä¿¡ç¨ä»£ç | |
| | | | idCardNo | String | 模ç³å¹é
| 身份è¯å· | |
| | | | customerName | String | 模ç³å¹é
| 客æ·å§å | |
| | | | filingSerialNo | String | 模ç³å¹é
| 夿¡æµæ°´å· | |
| | | | region | String | 模ç³å¹é
| æå±åºå | |
| | | | parentDealerId | Long | 精确å¹é
| æå±ä¸çº§ç»éåç¼å· | |
| | | |
| | | ### ååºå段æ°å¢ï¼page / getï¼ |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | socialCreditCode | String | 社ä¼ä¿¡ç¨ä»£ç | |
| | | | idCardNo | String | 身份è¯å· | |
| | | | customerName | String | 客æ·å§å | |
| | | | filingSerialNo | String | 夿¡æµæ°´å· | |
| | | | bankAccountName | String | é¶è¡è´¦æ·åç§° | |
| | | | region | String | æå±åºå | |
| | | | parentDealerId | Long | æå±ä¸çº§ç»éåç¼å· | |
| | | | parentDealerName | String | æå±ä¸çº§ç»éååç§°ï¼ç±å端æ¹éåå¡«ï¼ä»
å±ç¤ºç¨ï¼ | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": { |
| | | "id": 1001, |
| | | "name": "ææåä¸åä½ç¤¾", |
| | | "customerName": "å¼ ä¸", |
| | | "socialCreditCode": "91320600MA1XXXXXXX", |
| | | "idCardNo": "320602199001011234", |
| | | "filingSerialNo": "BA2026000001", |
| | | "bankAccountName": "åéææå䏿éå
¬å¸", |
| | | "region": "åé", |
| | | "parentDealerId": 900, |
| | | "parentDealerName": "ææç»éå" |
| | | }, |
| | | "msg": "" |
| | | } |
| | | ``` |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |------|----------|------| |
| | | | customerName | æ°å¢/ç¼è¾ãå表ãçéã详æ
| 客æ·å§åï¼ä¸ã客æ·åç§°ãå¹¶å | |
| | | | socialCreditCode | æ°å¢/ç¼è¾ãå表ãçéã详æ
| 社ä¼ä¿¡ç¨ä»£ç | |
| | | | idCardNo | æ°å¢/ç¼è¾ãå表ãçéã详æ
| 身份è¯å· | |
| | | | filingSerialNo | æ°å¢/ç¼è¾ãå表ãçéã详æ
| 夿¡æµæ°´å· | |
| | | | bankAccountName | æ°å¢/ç¼è¾ãå表ã详æ
| é¶è¡è´¦æ·åç§° | |
| | | | region | æ°å¢/ç¼è¾ãå表ãçéã详æ
| æå±åºåï¼èªç±ææ¬ï¼ | |
| | | | parentDealerId | æ°å¢/ç¼è¾ãçé | æå±ä¸çº§ç»éåï¼å¤ç¨å®¢æ·éæ©ç»ä»¶ | |
| | | | parentDealerName | å表ã详æ
| æå±ä¸çº§ç»éååç§°ï¼ç©ºå¼å±ç¤º `-` | |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | | åºæ¯ | è§å | |
| | | |------|------| |
| | | | æå±ä¸çº§ç»éå | å¤ç¨ CRM 客æ·ä¸»æ°æ®ï¼èªå
³è `parent_dealer_id â crm_customer.id` | |
| | | | èªå
³èæ ¡éª | ç¼è¾å®¢æ·æ¶ï¼æå±ä¸çº§ç»éåä¸è½éæ©å®¢æ·èªèº«ï¼å端è¿åãæå±ä¸çº§ç»éåä¸è½éæ©å®¢æ·èªèº«ã | |
| | | | æ°æ®æé | å表æ¥è¯¢ä»å CRM æ°æ®æéæ§å¶ï¼ãæå±ä¸çº§ç»éåã䏿å¯éèå´ç±å®¢æ·éæ©ç»ä»¶å³å® | |
| | | | ç©ºå¼ | æ°å¢å段åéå¿
å¡«ï¼å表空å¼å±ç¤º `-` | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - ã客æ·å§åãï¼`customerName`ï¼ä¸æ¢æã客æ·åç§°ãï¼`name`ï¼ä¸ºä¸¤ä¸ªç¬ç«å段ï¼ä¸è¦æ··ç¨ã |
| | | - ãæå±åºåãï¼`region`ï¼ä¸ºèªç±ææ¬ï¼ä¸æ¢æãå°åãï¼`areaId` çå¸åºçº§èï¼äºä¸å½±åã |
| | | - æ°å¢å段已å å
¥å¯¼åº Excel åï¼å¯¼åºæä»¶èªå¨å
å«ï¼`parentDealerName` ä»
ç¨äºå±ç¤ºï¼ä¸å¯ä½ä¸ºä¿åå
¥åã |
| | | - æ°æ®åºéå
æ§è¡ `sql/mysql/crm_customer_dealer_fields_20261106.sql`ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # åºåç°æé/åºåäºå¡æµæ°´ è§æ ¼ä¸ç±»å«å段 - å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - åºåç°æéï¼`src/views/wls/materialstock`ï¼ |
| | | - åºåç°æé鿩弹çªï¼ç©æåºåéæ©ï¼`useSelectGridColumns`ï¼ |
| | | - åºåäºå¡æµæ°´ï¼`src/views/wls/transaction`ï¼ |
| | | |
| | | ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å
¥ |
| | | |
| | | 1. åºåç°æéå表ï¼ç©æä¸»æ°æ®ç `specification`ï¼è§æ ¼åå·ï¼å±ç¤ºä¸ºâè§æ ¼âåï¼ç©ææå± MDM åç±»åç§°å±ç¤ºä¸ºâç±»å«âåã |
| | | 2. åºåäºå¡æµæ°´å表ï¼å¨åæç©æç¼ç ãç©æåç§°ä¹åï¼è¡¥å
âè§æ ¼âåâç±»å«â两åï¼æ°æ®åæ ·æ¥èªç©æä¸»æ°æ®ã |
| | | |
| | | ## API |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /mes/wm/material-stock/page | åºåç°æéå页 | |
| | | | GET | /mes/wm/material-stock/get | åºåç°æé详æ
| |
| | | | GET | /mes/wm/material-stock/picking-list | éå®åºåºæè¡æ¥è¯¢åæ ¼åºå | |
| | | | GET | /mes/wm/transaction/page | åºåäºå¡æµæ°´å页 | |
| | | |
| | | ### ååºå段æ°å¢ |
| | | |
| | | **`MesWmMaterialStockRespVO`ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | itemTypeName | String | ç©æç±»å«åç§°ï¼MDM ç©æåç±»åç§°ï¼ | |
| | | |
| | | ååæ®µ `specification`ï¼è§æ ¼åå·ï¼ç»§ç»ä¿çï¼ç±å端ç¨äºâè§æ ¼âåå±ç¤ºã |
| | | |
| | | **`MesWmTransactionRespVO`ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | specification | String | è§æ ¼åå·ï¼åç§æªç»´æ¤æ¶åé MES ç©æè§æ ¼åå·ï¼ | |
| | | | itemTypeName | String | ç©æç±»å«åç§°ï¼ä¼å
MDM ç©æåç±»ï¼å
¶æ¬¡ MES ç©æåç±»ï¼ | |
| | | | palletCode | String | æçç ï¼éå®åºåºåéå®æç»æçï¼å
¶ä½æµæ°´åéåè¢ç å½åç»å®æçï¼ | |
| | | | shipmentId | Number | åè´§åç¼å· | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": { |
| | | "itemCode": "M001", |
| | | "itemName": "颿¿", |
| | | "specification": "10mm*100mm", |
| | | "itemTypeName": "äºéé
ä»¶" |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |------|----------|------| |
| | | | itemTypeName | åºåç°æéå表ã鿩弹çªãäºå¡æµæ°´å表 | ååâç±»å«â | |
| | | | specification | åºåç°æéå表ã鿩弹çªãäºå¡æµæ°´å表 | ååâè§æ ¼â | |
| | | | palletCode | äºå¡æµæ°´å表 | ååâæçç â | |
| | | |
| | | **å顺åºï¼** |
| | | |
| | | - åºåäºå¡æµæ°´ï¼äºå¡ç±»å â ä¸å¡ç±»å â ç©æç¼ç â ç©æåç§° â è§æ ¼ â ç±»å« â ä¸å¡åå· â æ¹æ¬¡å· â æçç â è¢ç â æ°é â ä»åº â äºå¡æ¶é´ã |
| | | - åºåç°æéï¼äº§åç©æç¼ç â 产åç©æåç§° â è§æ ¼ â ç±»å« â åºåç¶æ â æ°éå⦠|
| | | - åºåç°æé鿩弹çªï¼äº§åç©æç¼ç â 产åç©æåç§° â è§æ ¼ â ç±»å« â å
¥åºæ¹æ¬¡å·â¦ |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | | åºæ¯ | è§å | |
| | | |------|------| |
| | | | ç±»å«åç§°æ¥æº | ä¼å
æç©æä¸»æ°æ®ç `categoryId` æ¥è¯¢ MDM ç©æåç±»ï¼æªç»´æ¤åç±»æ¶å鿥 MES ç©æï¼`mes_md_item.mdm_item_id`ï¼çç©æåç±»ï¼`mes_md_item_type.name`ï¼ | |
| | | | è§æ ¼æ¥æº | ä¼å
åç©æä¸»æ°æ®ç `specification`ï¼ä¸ºç©ºæ¶åéå MES ç©æç `specification` | |
| | | | æçç æ¥æº | éå®åºåºæµæ°´æâéå®å+è¢ç +åè´§åâå¹é
é宿ç»ä¸çæçï¼å
¶ä»æµæ°´ï¼è´¨æ£ãç产å
¥åºçï¼åè¢ç å½åç»å®çæçç | |
| | | | ç©æç¼ºå¤± | ç©æä¿¡æ¯ä¸å卿¶ï¼è§æ ¼ä¸ç±»å«è¿å空 | |
| | | | å类缺失 | ç©ææªç»´æ¤åç±»æå类已å 餿¶ï¼ç±»å«è¿å空 | |
| | | | æ°æ®ç»´æ¤å
¥å£ | è§æ ¼åå·å¨âåç§ç®¡çâæâMES ç©æâç»´æ¤ï¼MDM åç±»éå¨ `/mdm/category` ç»´æ¤ï¼MES ç©æåç±»å¨âMES ç©æåç±»âç»´æ¤ | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - åºåç°æé页é¢åæçâåä½âåå·²æ¿æ¢ä¸ºâè§æ ¼âï¼âè§æ ¼åå·âåå·²æ¿æ¢ä¸ºâç±»å«âï¼ä¸åå±ç¤ºè®¡éåä½ã |
| | | - ç©æåç§°ãè§æ ¼ãç±»å«ä¸åéç¸é»å±ç¤ºï¼å¹¶æ´ä½åç§»ï¼åºåäºå¡æµæ°´åç§»å°ä¸å¡åå·ä¹åï¼åºåç°æéåç§»å°åºåç¶æä¹åã |
| | | - Excel 导åºä¸æ°å¢âç±»å«âåï¼åæâè§æ ¼åå·ââ计éåä½âåä¿æä¸åã |
| | |
| | | | eventId | String | æ¯ | è¾¹ç¼è®¾å¤çæçå¹çäºä»¶å· | |
| | | | bagCode | String | æ¯ | å·¥ä¸ç¸æºè¯å«åºçè¢ç | |
| | | | quantity | Decimal | æ¯ | å
¥åºæ°éï¼æ¬æä¸è¢é常为 1 | |
| | | | deviceCode | String | æ¯ | 设å¤ç¼ç ï¼ç¨äºå®¡è®¡ | |
| | | | deviceCode | String | å¦ | 设å¤ç¼ç ï¼ç¨äºå®¡è®¡ | |
| | | | lineCode | String | å¦ | 产线ç¼ç ï¼ç¨äºå®¡è®¡ | |
| | | | locationId | Long | æ¯ | WIP æååºåºç¼å· | |
| | | | areaId | Long | æ¯ | WIP æååºä½ç¼å· | |
| | | | locationId | Long | å¦ | WIP æååºåºç¼å·ï¼ä»åºå·²æ¹ä¸ºãä»åº â åºåãä¸¤çº§ç»æï¼å¯ä¸ä¼ | |
| | | | areaId | Long | å¦ | WIP æååºä½ç¼å·ï¼å¯ä¸ä¼ | |
| | | | remark | String | å¦ | 夿³¨ | |
| | | |
| | | **ååºå段ï¼** |
| | |
| | | - è¾¹ç¼ç¨åºä¸å¾ä¸ä¼ å¾çæè§é¢ï¼æ¬æ¥å£åªæ¥æ¶è§£ç åçäºç»´ç ç»æã |
| | | - å端工ä½å°åºå¨é¡µé¢å¸è½½æ¶åæ¢å·æ°ä»»å¡ã |
| | | - å
¥åºå¤±è´¥æ¶å±ç¤ºå端è¿åçä¸å¡é误ï¼ä¸èªå¨è°ç¨éç¨åºåè°æ´æ¥å£éè¯ã |
| | | - WIP åºåºååºä½éæåé
ç½®ï¼æ¥å£ä¸æ¥å客æ·ç«¯æå®ä»åºç¼å·ã |
| | | - æ«ç å
¥åºç»ä¸è½å° WIP ç产æåä»ï¼åºåºãåºä½å¯ä¸ä¼ ï¼æ¥å£ä¸æ¥å客æ·ç«¯æå®ä»åºç¼å·ã |
| | |
| | | |------|------|------|------| |
| | | | materialStockId | Long | æ¯ | åºåæ±æ»è®°å½ ID | |
| | | | bagCode | String | å¦ | è¢ç æ¨¡ç³æ¥è¯¢ | |
| | | | includeHistory | Boolean | å¦ | 为 true æ¶åæ¶è¿åå·²åºåºãå·²æ¥åºè¢ç ï¼é»è®¤ä»
è¿åå¨åºè¢ç | |
| | | | pageNo | Integer | æ¯ | 页ç | |
| | | | pageSize | Integer | æ¯ | æ¯é¡µæ¡æ° | |
| | | |
| | |
| | | |
| | | - åä¸è¢ç åªè½åå¨ä¸æ¡åºåè¢ç æç»ã |
| | | - è´¨æ£æå®é
è¢ç å®ä½æååºåï¼ä¸ææ¹æ¬¡éæºæ£åã |
| | | - æ¥åºè¢ç ä¸è®¡å
¥å¨åºæç»æ¥è¯¢ï¼ä½ä¿çæç»è®°å½ç¨äºè¿½æº¯ã |
| | | - æ¥åºåå·²åºåºè¢ç ä¸è®¡å
¥é»è®¤å¨åºæç»æ¥è¯¢ï¼å¯éè¿ `includeHistory` æ¥è¯¢ä¿ççå½åç¶æãå·²åºåºä¸ä¸å®æ¯éå®åè´§ï¼åºç»ååºåºæµæ°´å¤æä¸å¡ç±»åã |
| | | - æ æ³ä»å岿±æ»åºåè¿åè¢ç æ¶ï¼ä¸ä¼ªé è¢ç æç»ã |
| | | - SQL èæ¬ä¸å
å« `tenant_id` åæ®µã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # çäº§æ¹æ¬¡å·è§å - å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - æ¹æ¬¡ç产管ç - æ°å¢æ¹æ¬¡ï¼`/mes/pro/batch` æ°å¢å¼¹çªï¼ |
| | | - æ¹æ¬¡ç产管ç - è¡¥å½æ¹æ¬¡ä¿¡æ¯ï¼ä¸´æ¶æ¹æ¬¡ç è¡¥å½å¼¹çªï¼ |
| | | - æ¹æ¬¡ç产管ç - ç¼è¾æ¹æ¬¡ |
| | | |
| | | ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å
¥ |
| | | |
| | | 1. æ°å¢æ¹æ¬¡ï¼æ¹æ¬¡å·å段å¯ç±äººå·¥å¡«åï¼ç空æ¶ç±å端æãå½å年份 + 3 使µæ°´ãèªå¨çæï¼å¦ 2026001ï¼ã |
| | | 2. é¢çæäºç»´ç -> è¡¥å½æ¹æ¬¡ä¿¡æ¯ï¼è¡¥å½æ¶åæ ·æ¯æäººå·¥å¡«åæ¹æ¬¡å·ï¼ç空åèªå¨çæï¼çæåä¸´æ¶æ¹æ¬¡ç ä»ä¿çç¨äºæ«ç 追溯ã |
| | | 3. ç¼è¾æ¹æ¬¡ï¼å·²çææ£å¼æ¹æ¬¡å·çæ¹æ¬¡ï¼æ¹æ¬¡å·ä¸åååï¼ä¿®æ¹å å·¥åä½ä¸ä¼éæ°çææ¹æ¬¡å·ï¼ï¼ä»
åå²ä¸´æ¶æ¹æ¬¡ï¼å°æ æ£å¼æ¹æ¬¡å·ï¼å¨ç¼è¾æ¶è¡¥çæã |
| | | |
| | | ## API |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | POST | /mes/pro/batch/create | å建çäº§æ¹æ¬¡ | |
| | | | PUT | /mes/pro/batch/fill-info | è¡¥å½çäº§æ¹æ¬¡ä¸å¡ä¿¡æ¯ | |
| | | | PUT | /mes/pro/batch/update | æ´æ°çäº§æ¹æ¬¡ | |
| | | |
| | | **请æ±åæ°æ°å¢ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | code | String | å¦ | æ¹æ¬¡å·ãä¸ä¼ æç空æ¶ç±ç³»ç»æã4 ä½å¹´ä»½ + 3 使µæ°´ãèªå¨çæï¼å¦ `2026001`ï¼ä¼ å
¥æ¶åç«¯æ ¡éªæ ¼å¼ä¸å¯ä¸æ§ | |
| | | |
| | | **æ¹æ¬¡å·æ ¼å¼è§åï¼** |
| | | |
| | | | è§å | 说æ | |
| | | |------|------| |
| | | | é¿åº¦ | åºå® 7 ä½çº¯æ°åï¼å¹´ä»½ 4 ä½ + æµæ°´ 3 ä½ï¼ | |
| | | | ç¤ºä¾ | `2026001`ã`2026002` ⦠| |
| | | | 年份èå´ | 2000 ~ 2100 | |
| | | | æµæ°´èå´ | 001 ~ 999ï¼è¶
åºåå端æç¤ºãæ¬å¹´åº¦çäº§æ¹æ¬¡å·å·²ç¨å°½ã | |
| | | | å¯ä¸æ§ | å
¨åºå¯ä¸ï¼å
å«å·²é»è¾å é¤è®°å½ï¼é夿¶è¿åé误 | |
| | | |
| | | **ååºï¼** |
| | | |
| | | - `create`ï¼`{ "code": 0, "data": æ¹æ¬¡ç¼å· }` |
| | | - `fill-info` / `update`ï¼`{ "code": 0, "data": true }` |
| | | |
| | | **é误æç¤ºï¼** |
| | | |
| | | | åºæ¯ | æç¤º | |
| | | |------|------| |
| | | | æ ¼å¼ä¸æ£ç¡® | çäº§æ¹æ¬¡å·æ ¼å¼ä¸æ£ç¡®ï¼åºä¸ºå¹´ä»½+3使µæ°´å·ï¼å¦ 2026001 | |
| | | | æ¹æ¬¡å·éå¤ | çäº§æ¹æ¬¡å·ã2026001ãå·²åå¨ | |
| | | | å½å¹´æµæ°´ç¨å°½ | æ¬å¹´åº¦çäº§æ¹æ¬¡å·å·²ç¨å°½ï¼001-999ï¼ | |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |------|----------|------| |
| | | | code | æ°å¢å¼¹çª | å¯ç¼è¾è¾å
¥æ¡ï¼éå¿
å¡«ï¼å ä½æç¤ºãç空èªå¨çæï¼å¦ 2026001ã | |
| | | | code | è¡¥å½å¼¹çª | å¯ç¼è¾è¾å
¥æ¡ï¼éå¿
å¡«ï¼ç空èªå¨çæ | |
| | | | code | ç¼è¾/详æ
å¼¹çª | åªè¯»å±ç¤ºï¼ä¸æ¯æä¿®æ¹ | |
| | | | code | å表 | æãæ¹æ¬¡å·ãåå±ç¤º | |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | | åºæ¯ | è§å | |
| | | |------|------| |
| | | | 人工è¾å
¥æ¹æ¬¡å· | å端å°ç¨æ·è¾å
¥å¼æ¾å
¥ `code` åæ®µæäº¤ï¼åç«¯æ ¡éª 7 使°åæ ¼å¼åå¯ä¸æ§ | |
| | | | ç空èªå¨çæ | ä¸ä¼ æä¼ 空å符串ï¼å端åå½åå¹´ä»½å·²ç¨æå¤§æµæ°´ + 1ï¼æ ¼å¼å为 3 使µæ°´ | |
| | | | ç¼è¾å·²ææ¹æ¬¡ | å端ä¿çåæ¹æ¬¡å·ï¼å¿½ç¥è¯·æ±ä¸ç `code` åæ´ï¼ä¿®æ¹å å·¥åä½ä¸åéæ°çææ¹æ¬¡å· | |
| | | | ä¸´æ¶æ¹æ¬¡è¡¥å½ | è¡¥å½æååææ¥ææ£å¼æ¹æ¬¡å·ï¼è¡¥å½åå表å±ç¤ºä¸´æ¶æ¹æ¬¡ç | |
| | | | å¹¶åæ°å¢ | æ¹æ¬¡å·åå卿°æ®åºå¯ä¸ç´¢å¼ï¼éå¤æäº¤ä¼è¿åéå¤æç¤º | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - æ°å¢ãè¡¥å½å¼¹çªçãæ¹æ¬¡å·ãå·²æ¹ä¸ºå¯äººå·¥è¾å
¥ï¼éå¿
å¡«ï¼ç空èªå¨çæï¼ï¼ç¼è¾ã详æ
å¼¹çªä¸ºåªè¯»å±ç¤ºã |
| | | - å端æªåæ ¼å¼å¼ºæ ¡éªï¼ä»¥æå¡ç«¯è¿åçé误æç¤ºä¸ºåï¼å¦é峿¶æ ¡éªï¼å¯æ `^\d{7}$` ä¸å¹´ä»½å¨ 2000-2100ãæµæ°´ ⥠001 夿ã |
| | | - æ¹æ¬¡å·çæä¾èµæå¡ç«¯å½å年份ï¼å端ä¸è¦èªè¡æ¼æ¥å¹´ä»½ææµæ°´å·ã |
| | | - ç¼è¾å¼¹çªåæ¾ç `code` ä»
ç¨äºå±ç¤ºï¼æäº¤æ¶å³ä½¿æºå¸¦ä¹ä¸ä¼æ¹åå·²ææ¹æ¬¡å·ã |
| | | - å·²æå岿¹æ¬¡å·ï¼æ§ç PC åç¼/16 ä½éæºç ï¼ç»§ç»ä¿çï¼ä¸åæ°è§åå½±åï¼æ°è§åä»
ä½ç¨äºæ°çææäººå·¥è¾å
¥çæ¹æ¬¡å·ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # çäº§æ¹æ¬¡å表æ°å¢ãæçç ãå - å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - æ¹æ¬¡ç产管ç - çäº§æ¹æ¬¡å表ï¼`/mes/pro/batch`ï¼ |
| | | |
| | | ## API |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /mes/pro/batch/page | çäº§æ¹æ¬¡å页ï¼ååºæ°å¢æçç åè¡¨ï¼ | |
| | | | GET | /mes/pro/batch/get | çäº§æ¹æ¬¡è¯¦æ
ï¼ååºåæ¥æ°å¢æçç åè¡¨ï¼ | |
| | | |
| | | **ååºå段æ°å¢ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | palletCodes | String[] | è¯¥æ¹æ¬¡ä¸å
¨é¨å·²çæçæ£å¼æçç ï¼ææçåºå·ååºï¼æ æçæ¶ä¸ºç©ºæ°ç» | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": { |
| | | "list": [ |
| | | { |
| | | "id": 1024, |
| | | "code": "2026001", |
| | | "itemName": "塿²³4å·", |
| | | "palletCodes": ["2026001-P001", "2026001-P002"] |
| | | } |
| | | ], |
| | | "total": 1 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |------|----------|------| |
| | | | palletCodes | çäº§æ¹æ¬¡å表 | ä½äºãæ¹æ¬¡å·ãåä¹åããåç§åç§°ãåä¹åï¼å¤ä¸ªæçç ç¨è±æéå·è¿æ¥å±ç¤ºï¼è¶
é¿æ¶æ¬æµ®æç¤ºå®æ´å
容 | |
| | | | palletCodes | çäº§æ¹æ¬¡å表 | æ æçç æ¶å±ç¤º `-` | |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | | åºæ¯ | è§å | |
| | | |------|------| |
| | | | æ¹æ¬¡å·²ç»å®/çææç | è¿åè¯¥æ¹æ¬¡ä¸æææ£å¼æçç ï¼`code` é空ï¼ï¼ææçåºå·ååº | |
| | | | æ¹æ¬¡ä»
æä¸´æ¶æç | ä¸´æ¶æçå°æªçææ£å¼æçç ï¼ä¸è¿å | |
| | | | æ¹æ¬¡æªç»å®æçï¼å¦å¨è¢æ¹æ¬¡ï¼ | è¿å空æ°ç»ï¼å表å±ç¤º `-` | |
| | | | äºç»´ç é¢çææ¹æ¬¡ | æªè¡¥å½æ£å¼ä¿¡æ¯åæ æ£å¼æçç ï¼è¿å空æ°ç» | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - æçç ä¸ºæ¹æ¬¡ä¸å¤å¼å段ï¼å端以æ°ç»æ¥æ¶ï¼å±ç¤ºå±èªè¡æ¼æ¥ï¼ä¸è¦æåå¼å¤çã |
| | | - æ¥è¯¢ä¸ºæ¹éå
³èï¼å表å页æ¯é¡µä»
å¢å 䏿¬¡æçæ¥è¯¢ï¼æ éå端é¢å¤è¯·æ±ã |
| | | - 详æ
æ¥å£è¿åç `palletCodes` ä¸å表ä¸è´ï¼å®æ´çæçä¿¡æ¯ä»éè¿è¯¦æ
ä¸ç `pallets` åæ®µè·åã |
| | | - Excel å¯¼åºæªæ°å¢è¯¥åï¼è¥åç»éè¦å¯¼åºæçç 请å¦è¡ç¡®è®¤æ ¼å¼ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # ç§åè´¨æ£ BPM è¿ç§»è®°å½ |
| | | |
| | | ## ç®æ æµç¨ |
| | | |
| | | ä»¥ç¨æ·æä¾çç产æµç¨å¾ä¸ºåãå å·¥é¨åéå®é¨åèªè¿è¡è´¨æ£ï¼åæ£ä¸åæ ¼æ¶å¤æ£ï¼å¤æ£ä¸åæ ¼è¿å
¥å¤æ£/鿝审æ¹å¹¶çåè®°å½ï¼æ£éªå®æååå«ç±é¨é¨é¢å¯¼å®¡æ¹ï¼æ±ååè¿å
¥å
¬å¸è´¨æ£å®¡æ¹ã审æ¹éè¿åäº§åæ£å¼å
¥åºã |
| | | |
| | | ç»éåéè´§è¿å
¥éå®é¨è´¨æ£ï¼ä¸åæ ¼æ¶å¤æ£ï¼å¤æ£ä¸åæ ¼ä¸äºéè´§å¹¶çåè®°å½ï¼éåè¿å
¥éå®é¨é¢å¯¼å®¡æ¹ãå审æ¹èç¹æç± admin å¤çã |
| | | |
| | | ## æ¬æ¬¡ä»£ç è°æ´ |
| | | |
| | | - çäº§è´¨æ£æå å·¥é¨ãéå®é¨åå«å½å
¥ï¼æ¥å£å¿
é¡»ä¼ `qcLine=1/2`ï¼åä¸è¢ç å¯å½¢æä¸¤æ¡ç¬ç«æ£éªè®°å½ã |
| | | - æ¹é夿£æè´¨æ£çº¿éç¦»ï¼æäº¤æ¶æ ¡éªæ¹æ¬¡ãè´¨æ£çº¿åæµç¨ç¶æä¸è´ï¼é¿å
跨线è¦ç宿µç»æã |
| | | - åæ£åæ ¼è¿å
¥é¨é¨å®¡æ¹ï¼åæ£ä¸åæ ¼å
夿£ï¼é¨é¨å®¡æ¹å®æåï¼ä¸¤æ¡çº¿æ±åå°å
¬å¸è´¨æ£å®¡æ¹ãéè´§å¤æ£ç»æè¿å
¥éå®é¨å®¡æ¹ã |
| | | - BPM åè°ææµç¨å®ä¾ç¼å·å¹é
è®°å½ï¼å®¡æ¹é©³å忏
ç对åºå®ä¾ç¼å·ï¼ä»¥ä¾¿éæ£åéæ°åèµ·ã |
| | | - è´¨æ£è®°å½é¡µæä¾ BPM 详æ
å
¥å£ï¼æµç¨å¯å¨å¤±è´¥æ¶å¯å¯¹è¯¥è®°å½éæ°åèµ·ã |
| | | - ä¸ä»½ BPMN 模åæä»¶ä½äº `yudao-module-mes/src/main/resources/bpm/`ï¼åé人æä¸ºå½åæ°æ®åºé»è®¤ admin ç¨æ· ID 1ã |
| | | |
| | | ## ä¸çº¿åç½®å·¥ä½ |
| | | |
| | | - å¨ BPM æµç¨æ¨¡å管çä¸å¯¼å
¥å¹¶åå¸ä¸ä»½ BPMN æä»¶ï¼æµç¨å®ä¹ key å¿
é¡»ä¸æä»¶ä¸ `process id` ä¸è´ãå¯ç¨ `scripts/deploy-seed-quality-bpm.ps1 -ApiBaseUrl <管çåå° API æ ¹å°å> -AccessToken <admin 令ç>` åå»ºææ´æ°å¹¶åå¸ãèæ¬æ¾å¼è®¾ç½® adminï¼ç¨æ· ID 1ï¼ä¸ºå®¡æ¹äººä¸ç¦ç¨èªå¨å®¡æ¹ï¼ç®æ ç¯å¢ä¸åºå
确认 admin ç ID 为 1ã |
| | | - æ§è¡ `sql/mysql/mes_seed_quality_dual_line_20261105.sql`ï¼ç¡®ä¿ `qc_line`ã`biz_type`ãæµç¨å®ä¾å段åå线å¯ä¸ç´¢å¼å·²å°±ç»ªã |
| | | - å¤çæ§çå¨é人工审æ¹è®°å½åå·²ç»äº§çç BPM æµç¨å®ä¾ï¼é¿å
æ°æ§ç¶ææ··ç¨ã |
| | | - ä¸ä¸ª BPM æµç¨åå¸å¹¶å®æå¨éæ°æ®å¤çåï¼æ§è¡ `sql/mysql/mes_qc_approver_menu_retire_20261106.sql` éèæ§å®¡æ ¸äººé
ç½®èåãåå²é
ç½®æ°æ®ä¸å é¤ã |
| | | - æ°å线记å½çäººå·¥å®¡æ¹æ¥å£ä¼æç»æä½å¹¶æç¤ºè½¬å° BPM å¾
åï¼å岿ªåºåè´¨æ£çº¿çè®°å½ä»ä¿ç人工æ¥å£å
¼å®¹ã页é¢éèæ°è®°å½çæ¹é人工审æ¹å
¥å£ã |
| | | |
| | | æ¬ææ¯è¿ç§»å·¥ä½è®°å½ï¼BPM 模åå°æªå¨ç®æ æ°æ®åºåå¸ï¼ç¨æ·å°ç»ä¸è´è´£æµè¯ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # è´¨æ£é¡¹åæ ¼èå´é
ç½®ï¼æäº§åç»´æ¤ï¼- å端èè°æ¹æ¡ |
| | | |
| | | ## åæ´èæ¯ |
| | | |
| | | åãè´¨é管ç > è´¨æ£é¡¹åæ ¼èå´é
ç½®ãå°ææäº§åå
±ç¨çº¯åº¦ãå度ãåè½çãæ°´åå项åºå®é
ç½®ã |
| | | ç°æ¹ä¸º**æäº§åç»´æ¤è´¨æ£é¡¹**ï¼æ¯ä¸ªè´¨æ£é¡¹å¿
é¡»ç»å®ä¸ä¸ªäº§åï¼`mdm_item` ä¸çå¯ç¨ç©æï¼æ¬ä¸å¡å³ç§ååç§ï¼ï¼ |
| | | åä¸äº§åä¸è´¨æ£é¡¹ç¼ç å¯ä¸ï¼å¯èªç±æ°å¢ãç¼è¾ãå é¤ãå¯ååæåºã |
| | | |
| | | åå²åé¡¹æ°æ®èªå¨è¿ç§»ä¸º**éç¨é»è®¤é¡¹**ï¼`itemId` 为空ï¼ï¼äº§åæªé
ç½®ä¸å±é¡¹æ¶åé使ç¨ï¼ä¿è¯åéæ°æ®ä¸æµç¨ä¸åå½±åã |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - è´¨é管ç â è´¨æ£é¡¹åæ ¼èå´é
ç½®ï¼è·¯ç± `mes/qc/seedRange/index`ï¼ |
| | | - è´¨é管ç â ç§åè´¨æ£ï¼æ¡é¢ç«¯ï¼è·¯ç± `mes/qc/seedQuality/index`ï¼ |
| | | - è´¨é管ç â ç§åè´¨æ£è®°å½ï¼æ¡é¢ç«¯ï¼è·¯ç± `mes/qc/seedQuality/record`ï¼ |
| | | - ç§»å¨ç«¯ â ç§åè´¨æ£ï¼`/mobile/quality`ï¼ |
| | | - ç§»å¨ç«¯ â è´¨æ£è®°å½ / æ¹éæ£éªä¸å®¡æ ¸ï¼`/mobile/quality-records`ï¼ |
| | | |
| | | ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å
¥ |
| | | |
| | | 1. é
ç½®é¡µéæ©äº§å â åè¡¨åªæ¾ç¤ºè¯¥äº§å已维æ¤çè´¨æ£é¡¹ï¼äº§åæªé
ç½®æ¶æ¾ç¤ºç©ºæå¹¶æç¤º"è´¨æ£æ¶ä½¿ç¨éç¨é»è®¤é¡¹"ã |
| | | 2. è´¨æ£é¡µæ«ç ï¼è¢ç / æçç / çäº§æ¹æ¬¡ç ï¼â è§£ææ¥å£ç´æ¥è¿å该产åç**çæè´¨æ£é¡¹å表**ï¼å«ç¼ç ãåç§°ãåä½ãåæ ¼ä¸ä¸éï¼ã |
| | | 3. è´¨æ£é¡µæè¿åçææ åè¡¨å¨ææ¸²æè¾å
¥é¡¹ï¼ä¸ååºå®å项ãä¸åéå¶ 0â100ã |
| | | 4. æäº¤æ¶å端æ"çæé
ç½® / åè®°å½ææ éå"æ ¡éªææ æ°éä¸ç¼ç éåï¼å端ä¸å¾èªè¡å¢å ææ ã |
| | | 5. 夿£ä½¿ç¨åè´¨æ£è®°å½çææ éåï¼é
ç½®åç»åæ´ä¸å½±ååå²è®°å½å¤æ£ã |
| | | |
| | | ## API |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /mes/qc/seed-quality/ranges | æ¥è¯¢è´¨æ£é¡¹é
ç½®ï¼`itemId` 为空æ¥éç¨é»è®¤é¡¹ï¼ | |
| | | | GET | /mes/qc/seed-quality/ranges/effective | æ¥è¯¢æå®äº§åçæçè´¨æ£é¡¹ | |
| | | | POST | /mes/qc/seed-quality/ranges | æ°å¢è´¨æ£é¡¹ | |
| | | | PUT | /mes/qc/seed-quality/ranges/{id} | ä¿®æ¹è´¨æ£é¡¹ | |
| | | | DELETE | /mes/qc/seed-quality/ranges/{id} | å é¤è´¨æ£é¡¹ | |
| | | | POST | /mes/qc/seed-quality/ranges/copy-default?itemId= | å¤å¶éç¨é»è®¤é¡¹å°æå®äº§å | |
| | | |
| | | **æ¥è¯¢åæ°ï¼GET /rangesï¼ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | itemId | Long | å¦ | 产åç¼å·ï¼ä¸ä¼ æ¥è¯¢éç¨é»è®¤é¡¹ | |
| | | | keyword | String | å¦ | è´¨æ£é¡¹åç§°/ç¼ç 模ç³å¹é
| |
| | | | status | Integer | å¦ | 0 å¯ç¨ 1 åç¨ | |
| | | |
| | | **ä¿ååæ°ï¼POST / PUTï¼ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | itemId | Long | æ¯ | 产åç¼å·ï¼å¯ç¨ç¶æçç©æï¼ | |
| | | | indicatorCode | String | æ¯ | ç¼ç ï¼åæ¯å¼å¤´ï¼ä»
忝/æ°å/ä¸åçº¿ï¼æé¿ 64ï¼åä¸äº§åä¸å¯ä¸ | |
| | | | indicatorName | String | æ¯ | è´¨æ£é¡¹åç§° | |
| | | | unit | String | å¦ | åä½ï¼é»è®¤ `%` | |
| | | | minValue | BigDecimal | æ¯ | åæ ¼ä¸éï¼â¥ 0 | |
| | | | maxValue | BigDecimal | æ¯ | åæ ¼ä¸éï¼â¥ ä¸é | |
| | | | sort | Integer | å¦ | æåºï¼é»è®¤ 0 | |
| | | | status | Integer | å¦ | 0 å¯ç¨ 1 åç¨ï¼é»è®¤ 0 | |
| | | |
| | | **ååºå段ï¼å表 / çæå表ï¼ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | id | Long | ä¸»é® | |
| | | | itemId | Long | 产åç¼å·ï¼ä¸ºç©ºè¡¨ç¤ºéç¨é»è®¤é¡¹ | |
| | | | indicatorCode | String | ç¼ç | |
| | | | indicatorName | String | åç§° | |
| | | | unit | String | åä½ | |
| | | | minValue | BigDecimal | åæ ¼ä¸é | |
| | | | maxValue | BigDecimal | åæ ¼ä¸é | |
| | | | sort | Integer | æåº | |
| | | | status | Integer | ç¶æ | |
| | | | updateTime | Date | æ´æ°æ¶é´ | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": [ |
| | | { "id": 1, "itemId": 1001, "indicatorCode": "purity", "indicatorName": "纯度", |
| | | "unit": "%", "minValue": 95, "maxValue": 100, "sort": 10, "status": 0 } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ## è´¨æ£æ¥å£ååºæ°å¢å段 |
| | | |
| | | `/mes/qc/seed-quality/resolve`ã`/retest-scope/resolve` ååºæ°å¢ï¼ |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | itemId | Long | 产åç¼å· | |
| | | | itemCode | String | 产åç¼ç | |
| | | | itemName | String | 产ååç§° | |
| | | | indicators | Array | çæè´¨æ£é¡¹ï¼`{ code, name, unit, minValue, maxValue }` | |
| | | |
| | | è´¨æ£è®°å½å页æ°å¢ `itemId / itemCode / itemName`ï¼ææ æç»æ°å¢ `indicatorName / unit`ã |
| | | |
| | | ## çæè§å |
| | | |
| | | | åºæ¯ | è§å | |
| | | |------|------| |
| | | | 产åé
ç½®äºå¯ç¨çä¸å±è´¨æ£é¡¹ | 使ç¨è¯¥äº§åçä¸å±é
ç½® | |
| | | | äº§åæªé
置任ä½ä¸å±è´¨æ£é¡¹ | åé使ç¨éç¨é»è®¤é¡¹ï¼`item_id` 为空ä¸å¯ç¨ï¼ | |
| | | | 产ååå¨ä¸å±é¡¹ä½å
¨é¨åç¨ | è§ä¸ºæªé
ç½®ï¼åééç¨é»è®¤é¡¹ | |
| | | | 产åä¸éç¨é»è®¤é¡¹é½ä¸ºç©º | è´¨æ£æ¥å£æ¥é"该产åå°æªé
置质æ£é¡¹ï¼è¯·è系管çåç»´æ¤" | |
| | | | ææ æ ¡éª | æäº¤çææ ç¼ç éåå¿
é¡»ä¸çæé
ç½®å®å
¨ä¸è´ï¼æ°éãç¼ç ä¸å¾å¢å | |
| | | | æ°å¼æ ¡éª | 宿µå¼ãä¸ä¸éå ⥠0ï¼ä¸é ⤠ä¸éï¼å®æµå¼å¨åºé´å
å¤å®åæ ¼ | |
| | | | 夿£ | æåè´¨æ£è®°å½çææ éåæ ¡éªï¼ä¸ä½¿ç¨å½åé
ç½® | |
| | | |
| | | ## è¿ç§»æ¥éª¤ |
| | | |
| | | 1. æ§è¡ `sql/mysql/mes_qc_seed_indicator_range_product_20261105.sql`ï¼å¹çï¼å¯é夿§è¡ï¼ã |
| | | 2. èæ¬ä¼æåå项æ 记为éç¨é»è®¤é¡¹ï¼`item_id` 为空ï¼ï¼ä¸å½±åä»»ä½åéè´¨æ£æµç¨ã |
| | | 3. å产åå¯å¨é
置页éè¿"å¤å¶éç¨é»è®¤é¡¹"å¿«éçæä¸å±é
ç½®ï¼åæéè°æ´ã |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - è´¨æ£é¡¹ç¼ç å¨åä¸äº§åä¸å¯ä¸ï¼ç¼è¾æ¶ç¼ç ä¸å¯ä¿®æ¹ï¼å¦éæ´æ¢è¯·å é¤åæ°å¢ã |
| | | - 产åä¸æåªæ¾ç¤ºå¯ç¨ç¶æçç©æï¼è¯·ç¡®ä¿å¾
é
ç½®çç§ååç§å·²ç»´æ¤ä¸ºå¯ç¨ç©æã |
| | | - å端ä¸åå¯¹ææ å 0â100 ç硬æ§éå¶ï¼æ¹ä¸ºéè´ + ä¸é ⤠ä¸éï¼è¯·å¿å¨å端èªè¡è¿æ»¤æè¡¥å
¨ææ ã |
| | | - ç§»å¨ç«¯ä¸æ¡é¢ç«¯åä»¥è§£ææ¥å£è¿åç `indicators` 为åï¼ä¸ååç¬è°ç¨èå´é
ç½®æ¥å£ã |
| | | - å é¤è´¨æ£é¡¹åªå½±ååç»è´¨æ£ï¼åå²è®°å½ä¸çææ åç§°/åä½ä»¥å¿«ç
§å½¢å¼ä¿åï¼ä¸åå½±åã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # è´¨æ£è®°å½æçäº§æ¹æ¬¡æå å±ç¤º - å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - è´¨é管ç > è´¨æ£è®°å½ï¼`/qc/seed-quality-record`ï¼ |
| | | |
| | | ## 页é¢ç»æ |
| | | |
| | | 1. é¡¶é¨é¡µå¤´ä¸ç»æç»è®¡å¡çä¿æåæ ·ã |
| | | 2. è®°å½åºåç±ãæ´è¡¨å¹³éºãæ¹ä¸ºãæçäº§æ¹æ¬¡æå ãï¼ |
| | | - æ¯ä¸ªçäº§æ¹æ¬¡ä¸ºä¸å¼ 坿å åç»å¡çï¼é»è®¤å±å¼ç¬¬ä¸æ¡ã |
| | | - å¡ç头é¨å±ç¤ºæ¹æ¬¡å·ãåç§ãå å·¥åä½ãçäº§æ¥æãæè¿æ£éªæ¶é´ï¼ä»¥ååæ ¼/ä¸åæ ¼/å¤çä¸/å¾
å®¡æ ¸ç»è®¡ã |
| | | - å±å¼åå±ç¤ºè¯¥æ¹æ¬¡ä¸çè´¨æ£æç»è¡¨ï¼å页ãå¾éãæ¹éæä½ä»å¯ç¨ï¼ã |
| | | 3. åºé¨ä¸ºãæ¹æ¬¡å页ãï¼ææ¹æ¬¡ç¿»é¡µï¼æç»å页卿¯å¼ å¡çå
é¨ç¬ç«ç»´æ¤ã |
| | | |
| | | ## API |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /mes/qc/seed-quality/batch-page | æ°å¢ï¼è´¨æ£è®°å½æçäº§æ¹æ¬¡åç»åé¡µï¼æ¹æ¬¡æ±æ»ï¼ | |
| | | | GET | /mes/qc/seed-quality/page | åæï¼è´¨æ£æç»åé¡µï¼æ°å¢ä¸¤ä¸ªå¯éè¿æ»¤åæ° | |
| | | |
| | | ### 1. æ¹æ¬¡åç»å页 |
| | | |
| | | `GET /mes/qc/seed-quality/batch-page` |
| | | |
| | | **请æ±åæ°ï¼ä¸æç»å页ä¸è´ï¼åå¯éï¼ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | pageNo | Integer | å¦ | æ¹æ¬¡é¡µç ï¼é»è®¤ 1 | |
| | | | pageSize | Integer | å¦ | æ¯é¡µæ¹æ¬¡æ°ï¼é»è®¤ 10ï¼æå¤§ 100 | |
| | | | code | String | å¦ | è¢ç ï¼æ¯ææ«ç å
容ï¼å端èªå¨è§£æä¸ºè¢ç ï¼ | |
| | | | batchCode | String | å¦ | çäº§æ¹æ¬¡å·ï¼æ¨¡ç³å¹é
ï¼ | |
| | | | result | Integer | å¦ | æ£éªç»æï¼1 åæ ¼ï¼2 ä¸åæ ¼ï¼0 å¤çä¸ | |
| | | | workflowStatus | Integer | å¦ | æµç¨ç¶æï¼åå¼è§ä¸è¡¨ | |
| | | |
| | | **ååºå段ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | batchId | Long | çäº§æ¹æ¬¡ç¼å·ï¼æªç»å®æ¹æ¬¡æ¶ä¸ºç©º | |
| | | | batchCode | String | çäº§æ¹æ¬¡å·ï¼æªç»å®æ¶ä¸ºç©ºå符串 | |
| | | | batchLabel | String | æ¹æ¬¡å±ç¤ºåï¼æªç»å®æ¶ä¸ºãæªç»å®æ¹æ¬¡ã | |
| | | | itemId | Long | åç§ç¼å· | |
| | | | itemCode | String | åç§ç¼ç | |
| | | | itemName | String | åç§åç§° | |
| | | | lineName | String | å å·¥åä½ï¼äº§çº¿åç§°ï¼ | |
| | | | produceDate | LocalDateTime | çäº§æ¥æ | |
| | | | totalCount | Integer | ç»å
è´¨æ£è®°å½æ»æ° | |
| | | | passCount | Integer | åæ ¼æ°é | |
| | | | failCount | Integer | ä¸åæ ¼æ°é | |
| | | | processingCount | Integer | å¤ç䏿°é | |
| | | | pendingRetestCount | Integer | å¾
夿£/鿰忣æ°éï¼10ã50ï¼ | |
| | | | pendingProcessReviewCount | Integer | å¾
åæ£å®¡æ ¸æ°éï¼20ã25ï¼ | |
| | | | pendingCompanyReviewCount | Integer | å¾
夿£å®¡æ ¸æ°éï¼30ã60ï¼ | |
| | | | releasedCount | Integer | å·²æ¾è¡æ°éï¼40ï¼ | |
| | | | scrappedCount | Integer | å·²æ¥åºæ°éï¼70ï¼ | |
| | | | lastInspectedAt | LocalDateTime | ç»å
æè¿æ£éªæ¶é´ï¼ç¨äºæåºä¸å±ç¤º | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": { |
| | | "list": [ |
| | | { |
| | | "batchId": 1024, |
| | | "batchCode": "2026001", |
| | | "batchLabel": "2026001", |
| | | "itemCode": "SEED001", |
| | | "itemName": "塿²³4å·", |
| | | "lineName": "ä¸å·å 工线", |
| | | "produceDate": "2026-11-01 08:00:00", |
| | | "totalCount": 120, |
| | | "passCount": 80, |
| | | "failCount": 10, |
| | | "processingCount": 30, |
| | | "pendingRetestCount": 5, |
| | | "pendingProcessReviewCount": 15, |
| | | "pendingCompanyReviewCount": 10, |
| | | "releasedCount": 80, |
| | | "scrappedCount": 10, |
| | | "lastInspectedAt": "2026-11-05 09:30:00" |
| | | } |
| | | ], |
| | | "total": 1 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 2. æç»å页æ°å¢åæ° |
| | | |
| | | `GET /mes/qc/seed-quality/page` |
| | | |
| | | å¨åæåæ°åºç¡ä¸æ°å¢ï¼ |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | exactBatchCode | String | å¦ | çäº§æ¹æ¬¡å·ç²¾ç¡®å¹é
ï¼å±å¼æä¸ªæ¹æ¬¡æ¶ä¼ å
¥ | |
| | | | unbound | Boolean | å¦ | ä»
æ¥è¯¢æªç»å®çäº§æ¹æ¬¡çè®°å½ï¼å±å¼ãæªç»å®æ¹æ¬¡ãæ¶ä¼ true | |
| | | |
| | | **ååºå段æ ååã** |
| | | |
| | | ## æ¥å£è°ç¨æ¶åº |
| | | |
| | | 1. 页é¢å è½½æçéæ¥è¯¢ï¼è°ç¨ `batch-page` è·åå½åé¡µæ¹æ¬¡æ±æ»ã |
| | | 2. é»è®¤å±å¼ç¬¬ä¸æ¡æ¹æ¬¡ï¼è°ç¨ `page`ï¼ä¼ `exactBatchCode`ï¼æ `unbound=true`ï¼+ å½åç鿡件ã |
| | | 3. å±å¼å
¶ä»æ¹æ¬¡ï¼æéè°ç¨ `page`ï¼æ¯ä¸ªæ¹æ¬¡ç»´æ¤ç¬ç«ç页ç 䏿¯é¡µæ¡æ°ã |
| | | 4. æ¹æ¬¡ç¿»é¡µï¼éæ°è°ç¨ `batch-page`ã |
| | | 5. 夿£ãå®¡æ ¸ãæµç¨è®°å½çæä½æ²¿ç¨åæ¥å£ï¼æä½æååéæ°å è½½æ¹æ¬¡æ±æ»åå·²å±å¼æ¹æ¬¡çæç»ã |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |------|----------|------| |
| | | | batchLabel | æ¹æ¬¡å¡ç头 | æ¹æ¬¡ä¸»æ é¢ï¼æªç»å®æ¹æ¬¡æ¾ç¤ºãæªç»å®æ¹æ¬¡ã | |
| | | | itemName / lineName / produceDate / lastInspectedAt | æ¹æ¬¡å¡ç头 | 坿 é¢ä¿¡æ¯ï¼ç¼ºå¤±æ¶éè | |
| | | | totalCount / passCount / failCount / processingCount | æ¹æ¬¡å¡ç头 | ç»è®¡è¶åï¼å§ç»å±ç¤º | |
| | | | pendingProcessReviewCount / pendingCompanyReviewCount / pendingRetestCount / releasedCount / scrappedCount | æ¹æ¬¡å¡ç头 | ç¶ææ ç¾ï¼æ°é为 0 æ¶éè | |
| | | | è´¨æ£ç¼å·ãè¢ç ãæçç ãæ£éªç»æãæµç¨ç¶æãä¸åæ ¼åå ãæ£éªæ¶é´ãæäº¤æ¶é´ãæä½ | å±å¼åçæç»è¡¨ | æç»è¡¨å
ä¸åéå¤å±ç¤ºçäº§æ¹æ¬¡å | |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | | åºæ¯ | è§å | |
| | | |------|------| |
| | | | åç»ä¾æ® | æè´¨æ£è®°å½ç `batch_code` åç»ï¼å䏿¹æ¬¡çææè¢ç è´¨æ£è®°å½å½ä¸ºä¸ç» | |
| | | | æªç»å®æ¹æ¬¡ | `batch_code` 为空çè®°å½å½å
¥ãæªç»å®æ¹æ¬¡ãç»ï¼æå¨ææ¹æ¬¡è®°å½ä¹å | |
| | | | æåº | æç»å
æè¿æ£éªæ¶é´ååºï¼å
¶æ¬¡æç»å
æå¤§è®°å½ç¼å·ååº | |
| | | | çé | è¢ç ãæ¹æ¬¡å·ãæ£éªç»æãæµç¨ç¶æçéåæ¶ä½ç¨äºåç»ç»è®¡ä¸æç» | |
| | | | ç»è®¡å£å¾ | ç»è®¡æ°éååºäºå½åç鿡件ä¸çè®°å½ï¼ä¸çäºæ¹æ¬¡å
¨é¨è¢ç æ° | |
| | | | å页 | æ¹æ¬¡å页䏿ç»å页ç¸äºç¬ç«ï¼æ¹æ¬¡å页æ¹ååæç»éæ°å è½½ | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - æ¹æ¬¡åç»æ¥å£è¿åçæ¯æ±æ»æ°æ®ï¼ä¸å
嫿ç»ï¼æç»å¿
é¡»éè¿ `page` æ¥å£ææ¹æ¬¡å è½½ã |
| | | - å±å¼æ¹æ¬¡æ¶ä¸è¦å¤ç¨ãçäº§æ¹æ¬¡ã模ç³çéå¼ï¼å¿
é¡»ä½¿ç¨ `exactBatchCode` æ `unbound`ï¼å¦åä¼ä¸²å
¥å
¶ä»æ¹æ¬¡æ°æ®ã |
| | | - `processingCount` ä¸ºç»æä¸ºç©ºæ 0 çè®°å½æ°ï¼ä¸ãå¤çä¸ãå±ç¤ºå£å¾ä¸è´ã |
| | | - å¾éè®°å½è·¨æ¹æ¬¡ä¿çï¼æ¹éæä½ä»ææµç¨ç¶æä¸çäº§æ¹æ¬¡åç»åç°ã |
| | | - è¥åç»æ°å¢è´¨æ£è®°å½å段ï¼åç»ç»è®¡æ éè°æ´ï¼è¥æ°å¢æµç¨ç¶æï¼éè¦åæ¥è¡¥å
ç»è®¡å段ä¸åç»å¤´æ ç¾ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # ç§åè´¨æ£å线æµç¨ï¼å«ç»éåéè´§è´¨æ£ï¼- å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - ç§åè´¨æ£æäº¤é¡µï¼`views/mes/qc/seedQuality/index.vue`ï¼ |
| | | - ç§åè´¨æ£è®°å½é¡µï¼`views/mes/qc/seedQuality/record.vue`ï¼ |
| | | - éå®éè´§åå表页ï¼`views/wls/returnsales/index.vue`ï¼åéè´§è´¨æ£å¼¹çª |
| | | |
| | | ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å
¥ |
| | | |
| | | 1. å å·¥é¨å¼éå®å
¥åºï¼è¢ç å
¥åºå坿«ç è´¨æ£ï¼æçç æ¯ 48 è¢æ 100 è¢çæï¼æ¹æ¬¡ç å¸¦åºæ´æ¹è¢æ°ã |
| | | 2. **䏿¬¡ç§åè´¨æ£æäº¤å³åæ¶çæä¸¤æ¡è´¨æ£è®°å½**ï¼`qcLine=1`ï¼å å·¥é¨ï¼ä¸ `qcLine=2`ï¼éå®é¨ï¼ï¼ä¸¤æ¡è®°å½çææ ãç»è®ºå®å
¨ä¸è´ï¼åç»å¤æ£ã审æ¹åå«æè´¨æ£çº¿ç¬ç«è¿è¡ã |
| | | 3. åæ£åæ ¼ç´æ¥è¿å
¥æ¬çº¿é¢å¯¼ BPM 审æ¹ï¼åæ£ä¸åæ ¼å
夿£ï¼åè¿å
¥æ¬çº¿é¢å¯¼å®¡æ¹ãé¨é¨å®¡æ¹é©³åå鿰忣ã |
| | | 4. 两æ¡çº¿åå«ä½¿ç¨æµç¨å®ä¹ `mes_seed_quality_line_approve`ï¼businessKey 为 `è´¨æ£çº¿#çäº§æ¹æ¬¡å·`ï¼å®¡æ¹éè¿åè¿å
¥å
¬å¸è´¨æ£å¾
å®¡ç¶æã |
| | | 5. åä¸çäº§æ¹æ¬¡å
ææè¢ç两æ¡çº¿é½å®æé¨é¨å®¡æ¹åï¼ç³»ç»èªå¨åèµ·**䏿¬¡**å
¬å¸è´¨æ£å®¡æ¹ï¼æµç¨å®ä¹ `mes_seed_quality_company_approve`ï¼businessKey 为**çäº§æ¹æ¬¡å·**ï¼ãå
¬å¸å®¡æ¹**ææ¹æ¬¡åèµ·ãæè¢è½åº**ï¼æ¯ä¸ªè¢ä¸¤æ¡çº¿é½ä¸ºåæ ¼ææ¾è¡å
¥åºï¼ä»»ä¸æ¡ä¸åæ ¼åè¯¥è¢æ¥åºï¼é©³ååæ´æ¹ä¸¤æ¡çº¿ä¸èµ·éåå¾
夿£ã |
| | | 6. æ¾è¡åäº§åæ£å¼å
¥åºï¼åç»åºç®¡æ«ç åºåºãéå®è½¬ç§»ç»éåãç»ç«¯åæ·æ«ç 溯æºã |
| | | 7. ç»éåéè´§åæ¯ï¼éå®éè´§å确认ååèµ·éè´§è´¨æ£ â 夿£ â éå®é¨é¢å¯¼å®¡æ¹ï¼ä¸åæ ¼åéè´§åèªå¨åæ¶ãä¸äºéè´§å¹¶ä¿çæ£éªè®°å½ã |
| | | |
| | | ## API |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |---|---|---| |
| | | | GET | `/mes/qc/seed-quality/resolve` | è¯å«è¢ç /æçç /æ¹æ¬¡ç ï¼è¿åå¾
æ£æ¸
åä¸å·²æè´¨æ£çº¿ | |
| | | | POST | `/mes/qc/seed-quality/submit-group` | æè¢ç ãæçç ãæ¹æ¬¡ç æäº¤åæ£ï¼ä¸æ¬¡çæå å·¥é¨ãéå®é¨ä¸¤æ¡è®°å½ | |
| | | | POST | `/mes/qc/seed-quality/submit` | åè¢æäº¤åæ£ï¼ä¸æ¬¡çæå å·¥é¨ãéå®é¨ä¸¤æ¡è®°å½ | |
| | | | GET | `/mes/qc/seed-quality/page` | è´¨æ£è®°å½åé¡µï¼æ°å¢ `qcLine`ã`bizType` çéä¸è¿ååæ®µï¼ | |
| | | | GET | `/mes/qc/seed-quality/batch-page` | æçäº§æ¹æ¬¡åç»çè´¨æ£æ±æ» | |
| | | | POST | `/mes/qc/seed-quality/process-approve` | åæ£å®¡æ ¸ï¼å å·¥é¨/éå®é¨é¢å¯¼ï¼éè´§è´¨æ£çéå®é¨å®¡æ¹å¤ç¨æ¤æ¥å£ï¼ | |
| | | | POST | `/mes/qc/seed-quality/process-approve-batch` | æ¹éåæ£å®¡æ ¸ | |
| | | | POST | `/mes/qc/seed-quality/company-approve` | å
¬å¸è´¨æ£å®¡æ¹ï¼æè¢å并两æ¡çº¿å¤çï¼ | |
| | | | POST | `/mes/qc/seed-quality/company-approve-batch` | æ¹éå
¬å¸è´¨æ£å®¡æ¹ï¼æè¢ç åç»åå¹¶ï¼ | |
| | | | POST | `/mes/qc/seed-quality/retest` | åæ¡è®°å½å¤æ£/鿰忣 | |
| | | | POST | `/mes/qc/seed-quality/retest-batch` | æè®°å½ ID æ¹é夿£ | |
| | | | GET | `/mes/qc/seed-quality/retest-scope/resolve` | ææçç /æ¹æ¬¡ç è§£æå¾
夿£ä¸å¾
å®¡æ ¸æ¸
å | |
| | | | POST | `/mes/qc/seed-quality/retest-scope` | ææçç /æ¹æ¬¡ç æ¹é夿£æéæ°åæ£ | |
| | | | GET | `/mes/qc/seed-quality/return/resolve` | è§£æç»éåéè´§åå¾
è´¨æ£ä¿¡æ¯ | |
| | | | POST | `/mes/qc/seed-quality/return/submit` | æäº¤ç»éåéè´§è´¨æ£ | |
| | | | POST | `/mes/qc/seed-quality/return/approve` | ç»éåéè´§è´¨æ£éå®é¨é¢å¯¼å®¡æ¹ | |
| | | |
| | | ### ååºå段æ°å¢ |
| | | |
| | | **`/resolve`ï¼`SeedQualityScan`ï¼æ°å¢ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |---|---|---| |
| | | | qualityIds | Number[] | 该è¢å·²åå¨çå
¨é¨è´¨æ£è®°å½ç¼å· | |
| | | | qualityLines | Object[] | å·²åå¨è´¨æ£çº¿ï¼`{ id, qcLine, workflowStatus }`ï¼ä¸å«éè´§è´¨æ£è®°å½ï¼ | |
| | | |
| | | **`/page`ï¼`SeedQualityRecord`ï¼æ°å¢ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |---|---|---| |
| | | | qcLine | Number | è´¨æ£çº¿ï¼1 å å·¥é¨ï¼2 éå®é¨ï¼0 æªåºåï¼å岿°æ®ï¼ | |
| | | | bizType | Number | ä¸å¡ç±»åï¼1 ç产å
¥åºè´¨æ£ï¼2 ç»éåéè´§è´¨æ£ | |
| | | | processInstanceId | String | æ¬çº¿ BPM æµç¨å®ä¾ | |
| | | | companyProcessInstanceId | String | å
¬å¸è´¨æ£ BPM æµç¨å®ä¾ | |
| | | | returnSalesId / returnSalesCode | Number / String | å
³èéå®éè´§å | |
| | | | retestedAt / processApprovedAt / lineFinishedAt | String | 夿£ãåæ£å®¡æ ¸ãæ¬çº¿å®ææ¶é´ | |
| | | | companySubmittedAt / companyApprovedAt | String | å
¬å¸å®¡æ¹åèµ·ã宿æ¶é´ | |
| | | |
| | | **请æ±åæ°æ°å¢ï¼** |
| | | |
| | | `/page`ã`/batch-page` æ¯æ `qcLine`ã`bizType` çéã |
| | | |
| | | **`/return/resolve` ååºï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |---|---|---| |
| | | | returnSalesId / returnSalesCode / returnSalesStatus | Number / String / Number | éè´§åä¿¡æ¯ | |
| | | | itemId / batchId / batchCode | Number / Number / String | ä»éè´§åæç»å¸¦åº | |
| | | | indicators | Object[] | 该产åçæè´¨æ£é¡¹ `{ code, name, unit, minValue, maxValue }` | |
| | | | qualityId / result / workflowStatus | Number | å·²åå¨è´¨æ£è®°å½æ¶è¿åï¼ç¨äºç¦æ¢éå¤æäº¤ | |
| | | |
| | | **`/return/submit` 请æ±ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |---|---|---|---| |
| | | | returnSalesId | Number | æ¯ | éå®éè´§åç¼å· | |
| | | | indicators | Object[] | æ¯ | `{ code, value, minValue, maxValue }`ï¼é¡»è¦çå
¨é¨çæè´¨æ£é¡¹ | |
| | | | reason | String | ä¸åæ ¼æ¶å¿
å¡« | ä¸åæ ¼åå | |
| | | |
| | | ## æµç¨ç¶æè¯´æ |
| | | |
| | | | ç¶æå¼ | å«ä¹ | å¤ç人/è§¦åæ¹å¼ | |
| | | |---|---|---| |
| | | | 10 | å¾
夿£ | å å·¥é¨/éå®é¨è´¨æ£å | |
| | | | 20 | å¾
é¨é¨å®¡æ¹ | æ¬çº¿é¢å¯¼ BPM å¾
å | |
| | | | 25 | å¾
é¨é¨ä¸åæ ¼å®¡æ¹ | æ¬çº¿é¢å¯¼ BPM å¾
å | |
| | | | 30 | å¾
å
¬å¸è´¨æ£å®¡æ¹ | å
¬å¸è´¨æ£ï¼å线é¨é¨å®¡æ¹å®æåèªå¨åèµ·ï¼ | |
| | | | 40 | å·²æ¾è¡ | å
¬å¸å®¡æ¹éè¿ï¼åºå转å
¥åæ ¼åº | |
| | | | 50 | 忣驳åå¾
鿣 | æ¬çº¿é¢å¯¼é©³åå | |
| | | | 60 | å¾
å
¬å¸è´¨æ£ä¸åæ ¼å¤ç | å
¬å¸è´¨æ£ï¼å线é¨é¨å®¡æ¹å®æåèªå¨åèµ·ï¼ | |
| | | | 70 | å·²æ¥åº | å
¬å¸å®¡æ¹ç¡®è®¤ä¸åæ ¼ | |
| | | |
| | | ç»éåéè´§è´¨æ£å¤ç¨åä¸ç»ç¶æï¼åæ£åæ ¼ â 20ï¼å¾
éå®é¨é¢å¯¼å®¡æ¹ï¼ï¼ä¸åæ ¼ â 10ï¼å¾
夿£ï¼ï¼å¤æ£åæ 论ç»è®ºåè¿å
¥ 20 å¹¶åèµ·éå®é¨å®¡æ¹ã审æ¹éè¿ä¸æ£éªåæ ¼ â 40ï¼éè´§åè¿å
¥âå¾
æ§è¡âï¼å®¡æ¹é©³åææ£éªä¸åæ ¼ â 70ï¼éè´§ååæ¶ã |
| | | |
| | | ## BPM æµç¨å®ä¹ |
| | | |
| | | | æµç¨å®ä¹ key | businessKey | æµç¨åé | |
| | | |---|---|---| |
| | | | `mes_seed_quality_line_approve` | `{qcLine}#{çäº§æ¹æ¬¡å·}` | `qcLine`ã`batchCode` | |
| | | | `mes_seed_quality_company_approve` | çäº§æ¹æ¬¡å· | `batchCode`ã`code`ï¼å
¼å®¹æ§æµç¨ï¼ã`passed` | |
| | | | `mes_seed_return_approve` | éå®éè´§åç¼å· | `returnSalesId`ã`returnSalesCode`ã`code` | |
| | | |
| | | - æµç¨çå¬å¨å¨æµç¨ç»æï¼éè¿/驳åï¼æ¶ååè´¨æ£ç¶æï¼åæ¶ï¼CANCELï¼ä¸æ¹åä¸å¡ç¶æã |
| | | - è¥ BPM æµç¨å®ä¹æªé¨ç½²ï¼åèµ·æµç¨ä¼å¤±è´¥å¹¶è®°å½æ¥å¿ï¼ä¿®å¤é
ç½®åå¯éè¿è´¨æ£è®°å½é¡µçâéæ°å起审æ¹âæ¢å¤ãæ°å线记å½ä¸è½ä½¿ç¨äººå·¥å®¡æ¹æ¥å£ã |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |---|---|---| |
| | | | qcLine | è®°å½å表ã详æ
| å å·¥é¨/éå®é¨æ ç¾ï¼å岿°æ®æ¾ç¤ºâæªåºåâ | |
| | | | bizType | è®°å½å表 | éè´§è´¨æ£è®°å½æ¾ç¤ºâéè´§âæ ç¾ä¸éè´§åå· | |
| | | | workflowStatus | è®°å½å表ã详æ
| æä¸å¡ç±»åå±ç¤ºï¼éè´§ç¶æ 20 æ¾ç¤ºâå¾
éå®é¨é¢å¯¼å®¡æ¹â | |
| | | | returnSalesCode | è®°å½å表 | éè´§è´¨æ£å
³èçç»éåéè´§å | |
| | | | processInstanceId / companyProcessInstanceId | 详æ
| å±ç¤ºå¯¹åºç BPM 审æ¹å®ä¾ï¼å¯è·³è½¬æµç¨è¯¦æ
ï¼ | |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | | åºæ¯ | è§å | |
| | | |---|---| |
| | | | 忣æäº¤ | æ¯è¢åºå®çæå å·¥é¨ãéå®é¨ä¸¤æ¡è®°å½ï¼ææ ä¸ç»è®ºå®å
¨ä¸è´ | |
| | | | éå¤æäº¤ | åä¸è¢ç å·²æä»»æä¸æ¡ç产å
¥åºè´¨æ£è®°å½æ¶æç»å次æäº¤ | |
| | | | 线æ¡å®¡æ¹ | æ¯æ¡çº¿ç¬ç«å®¡æ¹ï¼BPM businessKey æ `è´¨æ£çº¿#çäº§æ¹æ¬¡å·` å½å¹¶ï¼åæ¹æ¬¡å线æ¡ä¸æ¬¡å®¡æ¹å
¨é¨å¾
å®¡è®°å½ | |
| | | | å
¬å¸å®¡æ¹ï¼BPMï¼ | æçäº§æ¹æ¬¡åªåèµ·ä¸æ¬¡ï¼æ¹æ¬¡å
ææç产å
¥åºè´¨æ£è®°å½é½å¤äº 30/60 æ¶æåèµ·ï¼æµç¨ businessKey 为çäº§æ¹æ¬¡å·ï¼æµç¨å®æåæè¢è½åºï¼æ¯ä¸ªè¢ä¸¤æ¡çº¿é½ä¸º 30 æ¶æ¾è¡ï¼å¦åæ¥åºï¼é©³åæ¶æ´æ¹ä¸¤æ¡çº¿ä¸èµ·åå° 10ï¼å¾
夿£ï¼ | |
| | | | å
¬å¸å®¡æ¹ï¼äººå·¥æ¥å£ï¼ | 仿è¢å¤çï¼å¿
须两æ¡çº¿é½å¤äº 30/60ï¼ä¸¤æ¡çº¿é½ä¸º 30 æ¶æ¾è¡ï¼å¦åæ¥åºï¼é©³åæ¶ä¸¤æ¡çº¿ä¸èµ·åå° 10ï¼å¾
夿£ï¼ | |
| | | | åºåæµè½¬ | å
¬å¸å®¡æ¹éè¿åæ´è¢è½¬å
¥åæ ¼åºï¼æ¥åºæ¶ä¸å
¥åæ ¼åºï¼ä¿çè´¨æ£ä¸å®¡æ¹ç迹 | |
| | | | éè´§è´¨æ£ | éè´§åç¶æä¸ºè稿æå¾
æ£éªæ¶å¯æäº¤ï¼ä¸åæ ¼èªå¨åæ¶éè´§åå¹¶ä¿çè´¨æ£è®°å½ | |
| | | | æé | 忣æäº¤ `mes:qc-seed-quality:submit`ï¼å¤æ£ `mes:qc-seed-quality:retest`ï¼æ¬çº¿/éå®é¨å®¡æ¹ `mes:qc-seed-quality:process-approve`ï¼å
¬å¸å®¡æ¹ `mes:qc-seed-quality:company-approve` | |
| | | |
| | | ## æ°æ®åºåæ´ |
| | | |
| | | æ§è¡èæ¬ï¼`sql/mysql/mes_seed_quality_dual_line_20261105.sql` |
| | | |
| | | - æ°å¢å段ï¼`qc_line`ã`biz_type`ã`process_instance_id`ã`company_process_instance_id`ã`line_finished_at`ã`company_submitted_at`ã`return_sales_id`ã`return_sales_code`ã |
| | | - åå¯ä¸ç´¢å¼ `uk_code` è°æ´ä¸º `uk_code_line(code, qc_line)`ï¼å
许åä¸è¢ç åå¨ä¸¤æ¡è®°å½ã |
| | | - å岿°æ® `biz_type` å填为 1ï¼ç产å
¥åºè´¨æ£ï¼ï¼`qc_line` 为 0ï¼æªåºåï¼ã |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - å端å表éæâè¢ç + è´¨æ£çº¿âå±ç¤ºï¼é¿å
æåä¸è¢ç两æ¡è®°å½å½æä¸¤è¢ç»è®¡ï¼æ¹æ¬¡æ±æ»æ¥å£å·²æè¢ç å»éã |
| | | - å
¬å¸å®¡æ¹å³ä½¿åªéä¸ä¸æ¡çº¿è®°å½ï¼å端ä¹ä¼å¤çæ´è¢ä¸¤æ¡çº¿ï¼å端æç¤ºé说æâæè¢åå¹¶å¤çâã |
| | | - 夿£ãåæ£å®¡æ ¸æè®°å½ ID æä½æ¶åªå½±åæéè®°å½æå¨çº¿æ¡ï¼éè¦å线ä¸èµ·å¤ç请使ç¨å
¬å¸å®¡æ¹ã |
| | | - ç»éåéè´§è´¨æ£å
¥å£ä½äºâéå®éè´§åâå表ï¼ä»
âå¾
æ£éªâç¶ææ¾ç¤ºï¼æäº¤å审æ¹å¨âè´¨æ£è®°å½â页宿ã |
| | | - éè´§åè¢«åæ¶æè´¨æ£ä¸åæ ¼åï¼ä¸å¾å次æäº¤è´¨æ£ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # æ«ç äºä»¶åæ¶å±ç¤ºæçç 䏿¹æ¬¡ç - å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - MES / ä¸è¢ä¸ç 管ç / ç产线æ«ç å
¥åºï¼`/bagcode/scan-in`ï¼ |
| | | - MES / æ«ç äºä»¶æ¥è¯¢é¡µï¼å¦å±ç¤ºæ«ç äºä»¶å表ï¼å¯éåæ¥ï¼ |
| | | |
| | | ## ä¸å¡è¯´æ |
| | | |
| | | æ«ç äºä»¶çã追溯ç ãæ¯å®é
æ«æå°çç å¼ï¼ææ«ç ç±»åä¸åå¯è½æ¯è¢ç ãæçç ææ¹æ¬¡ç ï¼åçè¯¥åæ æ³åºåæç䏿¹æ¬¡ãæ¬æ¬¡å¨ååºä¸è¡¥å
æçç ï¼ä½¿å表å¯ä»¥åæ¶å±ç¤ºãæçç ãä¸ãæ¹æ¬¡ç ãã |
| | | |
| | | | æ«ç ç±»å | 追溯ç ï¼bagCodeï¼ | æçç ï¼palletCodeï¼ | æ¹æ¬¡ç ï¼batchCodeï¼ | |
| | | |----------|-------------------|----------------------|---------------------| |
| | | | è¢ç BAG | æ«æçè¢ç /ä¸å¡ç | è¯¥è¢æå±æçç | è¢æå±æ¹æ¬¡å· | |
| | | | æçç PALLET | æ«æçæçç /ä¸å¡ç /临æ¶ç | æçæ£å¼ç ï¼æ æ£å¼ç æ¶åé临æ¶ç ï¼ | æçæå±æ¹æ¬¡å· | |
| | | | æ¹æ¬¡ç BATCH | æ«æçæ¹æ¬¡ç | ç©ºï¼æ´æ¹å¯è½è·¨å¤ä¸ªæçï¼ä¸å½å±å䏿çï¼ | æ¹æ¬¡å· | |
| | | |
| | | æçç ä¸ºè¯»åæ¶å®æ¶åæ¥å¾åºï¼ |
| | | |
| | | - æçç äºä»¶ææ«ç å
容忥æçï¼å
¼å®¹ä¸å¡ç ãæ£å¼æçç ãä¸´æ¶æçç ï¼ï¼ |
| | | - è¢ç äºä»¶æè¢ç ç¼å·æç å¼åæ¥è¢ç ï¼ååå
¶æå±æçï¼ |
| | | - å æ¤å岿«ç äºä»¶åæ ·è½å±ç¤ºæçç ï¼æ éæ°æ®è¿ç§»ã |
| | | |
| | | ## API |
| | | |
| | | ### 1. æ«ç äºä»¶å页 |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | `/mes/pro/scan-in/page` | æ«ç äºä»¶åé¡µï¼æé `mes:pro-scan-in:query`ï¼ | |
| | | |
| | | **ååºå段æ°å¢ï¼`data.list[]`ï¼ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | palletId | Long | æçç¼å·ï¼æ æ³å½å±æçæ¶ä¸ºç©º | |
| | | | palletCode | String | æçç ï¼æ æ³å½å±æçæ¶ä¸ºç©ºï¼æ¹æ¬¡æ«ç åºå®ä¸ºç©ºï¼ | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": { |
| | | "list": [ |
| | | { |
| | | "id": 20, |
| | | "bagCode": "3508118133307512", |
| | | "scanType": "PALLET", |
| | | "palletId": 13708, |
| | | "palletCode": "3508118133307512", |
| | | "batchCode": "2026002", |
| | | "status": "SUCCESS" |
| | | } |
| | | ], |
| | | "total": 1 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ### 2. ææ°æ«ç äºä»¶ / çæ¿ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | `/mes/pro/scan-in/latest` | ææ°æ«ç äºä»¶ï¼æé `mes:pro-scan-in:query`ï¼ | |
| | | | GET | `/mes/pro/scan-in/dashboard` | æ«ç çæ¿ï¼`data.latest` åæ ·å
嫿°åæ®µï¼ | |
| | | |
| | | ### 3. å
¶ä»è¿åæ«ç äºä»¶çæ¥å£ |
| | | |
| | | `/mes/pro/scan-in/my-page`ã`/mes/pro/scan-in/my-event`ã`/mes/pro/scan-in/inbound` è¿åç»æä¸è´ï¼åå
å« `palletId`ã`palletCode`ã |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |------|----------|------| |
| | | | bagCode | å表 | ä¿çåæã追溯ç ãåï¼å±ç¤ºå®é
æ«æç å¼ | |
| | | | palletCode | å表 | æ°å¢ãæçç ãåï¼ç©ºå¼æ¾ç¤º `-` | |
| | | | batchCode | å表 | ååç±ãçäº§æ¹æ¬¡ãè°æ´ä¸ºãæ¹æ¬¡ç ãï¼ç©ºå¼æ¾ç¤º `-` | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - æ¹æ¬¡ç æ«ç äºä»¶ä¸å½å±å䏿çï¼`palletCode` ä¸ºç©ºå±æ£å¸¸ç°è±¡ï¼å端æ¾ç¤º `-` å³å¯ã |
| | | - æçç ä¸ºå®æ¶åæ¥ç»æï¼è¥æç被é»è¾å é¤ï¼åå²äºä»¶å¯è½å±ç¤ºä¸ºç©ºã |
| | | - æ¬æ¹å¨ä¸æ¶åæ°æ®åºè¡¨ç»æåæ´ï¼æ éæ§è¡ SQL èæ¬ã |
| | |
| | | | eventId | String | æ¯ | - | äºä»¶å·ï¼å»ºè®®ç¨ `{scanType}-{æ¶é´æ³}` ä¿è¯å¯ä¸ï¼ | |
| | | | quantity | Number | æ¯ | - | è¢ç åºæ¯ä¼ 1ï¼æç/æ¹æ¬¡åºæ¯å端忽ç¥è¯¥å¼ï¼æå·²è£
è¢æ°/æ¹æ¬¡è¢æ°è®¡ç® | |
| | | | lineCode | String | å¦ | - | 产线ç¼ç ï¼åç«¯ä¸æéæ©ï¼ | |
| | | | locationId | Long | æ¯ | - | WIP æååºåºç¼å·ï¼åç«¯ä¸æéæ©ï¼ | |
| | | | areaId | Long | æ¯ | - | WIP æååºä½ç¼å·ï¼åç«¯ä¸æéæ©ï¼ | |
| | | | locationId | Long | å¦ | æ¹ä¸ºéå¿
å¡« | WIP æååºåºç¼å·ï¼ä»åºç§»é¤åºåº/åºä½åå端ä¸åä¼ ï¼ | |
| | | | areaId | Long | å¦ | æ¹ä¸ºéå¿
å¡« | WIP æååºä½ç¼å·ï¼å端ä¸åä¼ ï¼ | |
| | | |
| | | **ååºæ°å¢å段ï¼** |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # ç³»ç»å¤è§è®¾ç½®ï¼ç»å½é¡µèæ¯å¾ + ä¼ä¸ Logoï¼- å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - ç³»ç»ç®¡ç â å¤è§è®¾ç½®ï¼`system/appearance/index`ï¼ |
| | | - ç»å½é¡µï¼åç Logoãç»å½èæ¯å¾ï¼ |
| | | - é¦é¡µï¼ä¾§è¾¹æ ä¼ä¸ Logoï¼ |
| | | |
| | | ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å
¥ |
| | | |
| | | 1. 管çåå¨ãå¤è§è®¾ç½®ã页ä¸ä¼ ç»å½é¡µèæ¯å¾ / ä¼ä¸ Logoï¼å¾çéè¿å
Œ
±ä¸ä¼ æ¥å£ç´æ¥è·å¾æ°¸ä¹
访é®å°åä¸ `blobId`ã |
| | | 2. ç¹å»ä¿ååï¼å端æ `blobId` ä¸åºå®è®°å½ï¼`recordType = system_appearance`ã`recordId = 1`ï¼ç»å®ï¼å¹¶åå«ä»¥ `application = logo`ã`application = login_background` åºå两类å¾çã |
| | | 3. å端ä¿åæååç«å³åºç¨ï¼ç»å½é¡µèæ¯ãç»å½é¡µ Logoãé¦é¡µä¾§è¾¹æ Logo 宿¶ååã |
| | | 4. æµè§å¨å·æ°æéæ°ç»å½æ¶ï¼å端å¨åºç¨å¯å¨é¶æ®µè°ç¨ãè·å¾ç³»ç»å¤è§ãæ¥å£ï¼è¯»åæå¡ç«¯é
置并åºç¨ï¼æªç»å½çç»å½é¡µä¹è½æ¾ç¤ºå·²é
ç½®çèæ¯å¾ä¸ Logoã |
| | | |
| | | ## API |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | æé | |
| | | |------|------|------|------| |
| | | | GET | /system/appearance/get | è·å¾ç³»ç»å¤è§é
ç½® | å¿åå¯è®¿é®ï¼@PermitAllï¼ | |
| | | | POST | /system/appearance/save | ä¿åç³»ç»å¤è§é
ç½® | `system:appearance:update` | |
| | | | POST | /system/storage-blob/public-upload | å
Œ
±æä»¶ä¸ä¼ ï¼æ°¸ä¹
ææï¼ | ç»å½ç¨æ· | |
| | | | GET | /system/storage-blob/preview/{fileName}?publicKey=xxx | å¾çé¢è§ï¼æ°¸ä¹
å°åï¼ | å¿åå¯è®¿é®ï¼@PermitAllï¼ | |
| | | |
| | | ### è·å¾ç³»ç»å¤è§ |
| | | |
| | | **ååºå段ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | logoBlobId | Long | ä¼ä¸ Logo çæä»¶ç¼å·ï¼æªé
置为 null | |
| | | | logoUrl | String | ä¼ä¸ Logo çæ°¸ä¹
访é®å°åï¼æªé
置为 null | |
| | | | loginBackgroundBlobId | Long | ç»å½èæ¯å¾çæä»¶ç¼å·ï¼æªé
置为 null | |
| | | | loginBackgroundUrl | String | ç»å½èæ¯å¾çæ°¸ä¹
访é®å°åï¼æªé
置为 null | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": { |
| | | "logoBlobId": 1024, |
| | | "logoUrl": "http://192.168.0.10:48080/admin-api/system/storage-blob/preview/xxx.png?publicKey=yyy", |
| | | "loginBackgroundBlobId": 1025, |
| | | "loginBackgroundUrl": "http://192.168.0.10:48080/admin-api/system/storage-blob/preview/zzz.png?publicKey=www" |
| | | }, |
| | | "msg": "" |
| | | } |
| | | ``` |
| | | |
| | | ### å
Œ
±æä»¶ä¸ä¼ |
| | | |
| | | - è¯·æ±æ¹å¼ï¼`multipart/form-data` |
| | | - åæ°ï¼`files`ï¼å¯å¤ä¸ªæä»¶ï¼æ¬åºæ¯ä¸æ¬¡ä¸ä¸ªæä»¶ï¼ |
| | | - ååºä¸ºæä»¶æ°ç»ï¼æ¬åºæ¯å第ä¸ä¸ªå
ç´ ï¼ä½¿ç¨å
¶ä¸ç `id`ï¼å³ `blobId`ï¼ä¸ `previewURL`ï¼æ°¸ä¹
访é®å°åï¼ã |
| | | |
| | | ### ä¿åç³»ç»å¤è§ |
| | | |
| | | **请æ±åæ°ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | logoBlobId | Long | å¦ | ä¼ä¸ Logo æä»¶ç¼å·ï¼ä¼ null 表示æ¸
é¤ | |
| | | | loginBackgroundBlobId | Long | å¦ | ç»å½èæ¯å¾æä»¶ç¼å·ï¼ä¼ null 表示æ¸
é¤ | |
| | | |
| | | **请æ±ç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "logoBlobId": 1024, |
| | | "loginBackgroundBlobId": 1025 |
| | | } |
| | | ``` |
| | | |
| | | **ååºï¼** `{ "code": 0, "data": true }` |
| | | |
| | | ## æ¥å£è°ç¨æ¶åº |
| | | |
| | | 1. è¿å
¥ãå¤è§è®¾ç½®ã页 â è°ç¨ `GET /system/appearance/get`ï¼åæ¾å·²é
ç½®çå¾çä¸ `blobId`ã |
| | | 2. éæ©å¾ç â è°ç¨ `POST /system/storage-blob/public-upload` â è®°å½è¿åç `blobId` ä¸ `previewURL`ï¼é¡µé¢ç«å³é¢è§ã |
| | | 3. ç¹å»ä¿å â è°ç¨ `POST /system/appearance/save`ï¼æäº¤ä¸¤ä¸ª `blobId`ã |
| | | 4. ä¿åæåå â ç«å³æ´æ°é¦é¡µ Logo ä¸ç»å½é¡µèæ¯/Logoï¼æ éå·æ°ã |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |------|----------|------| |
| | | | loginBackgroundUrl | å¤è§è®¾ç½®é¡µé¢è§ãç»å½é¡µæ´é¡µèæ¯ | ç»å½é¡µèæ¯å¾ï¼æªé
ç½®æ¶ä½¿ç¨ç³»ç»é»è®¤èæ¯ | |
| | | | logoUrl | å¤è§è®¾ç½®é¡µé¢è§ãç»å½é¡µåçåº Logoãé¦é¡µä¾§è¾¹æ Logo | ä¸å¥é
ç½®åæ¶ä½ç¨äºç»å½é¡µä¸é¦é¡µï¼æªé
ç½®æ¶ä½¿ç¨ç³»ç»é»è®¤ Logo | |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | | åºæ¯ | è§å | |
| | | |------|------| |
| | | | ä¸ä¼ | ä»
æ¥å常è§å¾çæ ¼å¼ï¼image/*ï¼ï¼ä½¿ç¨å
Œ
±ä¸ä¼ æ¥å£ï¼è¿åæ°¸ä¹
å¯è®¿é®å°å | |
| | | | ä¿å | 两类å¾çç¬ç«ä¿åï¼æ¿æ¢å
¶ä¸ä¸å¼ ä¸å½±åå¦ä¸å¼ | |
| | | | æ¸
é¤ | å¯¹åº `blobId` ä¼ nullï¼ä¿å忢å¤ç³»ç»é»è®¤æ ·å¼ | |
| | | | æªç»å½è®¿é® | ç»å½é¡µéè¿ `@PermitAll` çå¤è§æ¥è¯¢æ¥å£è¯»åé
ç½®ï¼èæ¯å¾/Logo ä½¿ç¨ `?publicKey=` æ°¸ä¹
å°åï¼æ éç»å½ | |
| | | | å 载失败 | å端å¯å¨é¶æ®µæ¥è¯¢å¤è§å¤±è´¥æ¶éé»ä¿çé»è®¤æ ·å¼ï¼ä¸é»å¡åºç¨å¯å¨ | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - å¾çä¸ä¼ ä¸ä¿åæ¯ä¸¤æ¥æä½ï¼ä¸ä¼ æå使ªç¹å»ä¿åä¸ä¼çæã |
| | | - å¤è§é
置为å
¨ç³»ç»çº§ï¼`recordId = 1`ï¼ï¼ä¸åºåç§æ·ä¸ç¨æ·ã |
| | | - å端å¯å¨é¶æ®µæ¥è¯¢å¤è§æ¥å£é带 `tenant-id` 请æ±å¤´ã |
| | | - èåãå¤è§è®¾ç½®ãç± SQL èæ¬åå§åï¼è¶
级管çåé»è®¤æ¥ææéï¼å
¶ä»è§è²éå¨è§è²èåä¸å¾éãä¿åå¤è§è®¾ç½®ãæé®æéã |
| | | - æ´æ¢å¾çå建议使ç¨åºå®å°ºå¯¸ç横çèæ¯å¾ï¼å¦ 1920Ã1080ï¼ï¼Logo 建议使ç¨éæèæ¯ PNGã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # ä»åºç§»é¤åºåº/åºä½ - å端èè°æ¹æ¡ |
| | | |
| | | ## èæ¯ |
| | | |
| | | ä»åºç®¡çæ¹ä¸ºãä»åº â åºåãä¸¤çº§ç»æï¼ä¸åç»´æ¤åºåºï¼`warehouse_location`ï¼ååºä½ï¼`warehouse_area`ï¼ã |
| | | å端已å é¤åºåºãåºä½ç®¡ç页é¢åææä¸å¡è¡¨åä¸çåºåº/åºä½éæ©é¡¹ï¼åç«¯åæ¥ç§»é¤å¯¹åºç®¡çæ¥å£ï¼å¹¶æ¾å®½åºåº/åºä½çå¼ºæ ¡éªï¼æªä¼ `locationId`ã`areaId` æ¶ï¼ä¸å¡æä»åºç»´åº¦æ£å¸¸æ§è¡ã |
| | | |
| | | > 注æï¼ä»£ç ä¸ `warehouse_location` 对åºä¸æãåºåºãï¼`warehouse_area` 对åºä¸æãåºä½ãï¼ä¸æ§å½ååé¢å«ä¹ç¸åï¼èè°æ¶è¯·ä»¥å段为åã |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - ä»å¨ç©æµ - ä»åºè®¾ç½® |
| | | - ä»å¨ç©æµ - åºåºå表ãåºä½å表ï¼å·²å é¤ï¼ |
| | | - ä»å¨ç©æµ - å
¥åº/åºåº/ç§»åº/çç¹/æé¡¹æ¶åè´§/å§å¤çåæ®æç» |
| | | - ä»å¨ç©æµ - åºåæ¥è¯¢ãåºåç§»åºãåºåè°æ´ |
| | | - MES - å·¥ä½ç«ç»´æ¤ |
| | | - ç§»å¨ç«¯ - æ«ç å
¥åºãåºåæ¥è¯¢ãç§»åºãçç¹ãéå®åºåºãæé¡¹é¢æãéè´§é¢æ |
| | | |
| | | ## ç§»é¤çæ¥å£ |
| | | |
| | | 以䏿¥å£ï¼åºåºãåºä½ç®¡çï¼å·²ä»å端å é¤ï¼è°ç¨å°è¿å 404ï¼ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | `/mes/wm/warehouse-location/page` | åºåºå页 | |
| | | | GET | `/mes/wm/warehouse-location/simple-list` | åºåºç²¾ç®å表 | |
| | | | GET | `/mes/wm/warehouse-location/get` | åºåºè¯¦æ
| |
| | | | POST | `/mes/wm/warehouse-location/create` | æ°å¢åºåº | |
| | | | PUT | `/mes/wm/warehouse-location/update` | ä¿®æ¹åºåº | |
| | | | DELETE | `/mes/wm/warehouse-location/delete` | å é¤åºåº | |
| | | | GET | `/mes/wm/warehouse-area/page` | åºä½å页 | |
| | | | GET | `/mes/wm/warehouse-area/simple-list` | åºä½ç²¾ç®å表 | |
| | | | GET | `/mes/wm/warehouse-area/get` | åºä½è¯¦æ
| |
| | | | POST | `/mes/wm/warehouse-area/create` | æ°å¢åºä½ | |
| | | | PUT | `/mes/wm/warehouse-area/update` | ä¿®æ¹åºä½ | |
| | | | DELETE | `/mes/wm/warehouse-area/delete` | å é¤åºä½ | |
| | | |
| | | ## ä¿çåæ®µè¯´æ |
| | | |
| | | 以䏿¥å£ç `locationId`ã`areaId`ã`locationName`ã`areaName` åæ®µ**ä¿çä½ä¸åä½ä¸ºå¿
填项**ï¼ |
| | | |
| | | | æ¶åæ¥å£ | åæ®µ | è§å | |
| | | |----------|------|------| |
| | | | åºåå页/详æ
| `warehouseId`ã`locationId`ã`areaId` | æ¥è¯¢æ¡ä»¶å¯ä¸ä¼ ï¼è¿åå¼ä¸ºå岿°æ®æ¶å¯è½é空 | |
| | | | åç±»å
¥åº/åºåºæç»ä¿å | `warehouseId`ã`locationId`ã`areaId` | ä»
`warehouseId` å¿
å¡«ï¼`locationId`ã`areaId` å¯ä¸ä¼ | |
| | | | ç§»åºåæç»ä¿å | `fromWarehouseId`ã`fromLocationId`ã`fromAreaId` | ä»
`fromWarehouseId` å¿
å¡« | |
| | | | ç§»åºå头 | `toWarehouseId`ã`toLocationId`ã`toAreaId` | ä»
`toWarehouseId` å¿
å¡« | |
| | | | å·¥ä½ç«ä¿å | `warehouseId`ã`locationId`ã`areaId` | ä»
`warehouseId` å䏿 ¡éªï¼æªä¼ æ¶èªå¨è½å°èæçº¿è¾¹ä» | |
| | | | åºåç§»åº/åºåè°æ´ | `toWarehouseId`ã`toLocationId`ã`toAreaId` | ä»
`toWarehouseId` å¿
å¡«ï¼ç®æ ä»åºä¸å½åä»åºç¸åæ¶ç¦æ¢ç§»åº | |
| | | |
| | | ## ä¸å¡è§ååå |
| | | |
| | | | åºæ¯ | è°æ´å | è°æ´å | |
| | | |------|--------|--------| |
| | | | ä»åºå é¤ | ä»åºä¸åå¨åºåºæ¶ç¦æ¢å é¤ | ä¸åæ ¡éªåºåºï¼åå¨å·¥ä½ç«æåºåæ¶ä»ç¦æ¢å é¤ | |
| | | | åºåå»ç»æ ¡éª | åºåº/åºä½ä¸å卿¶æ¥é | `locationId`ã`areaId` 为空æ¶è·³è¿åºåº/åºä½å»ç»æ ¡éª | |
| | | | åºåæ··æ¾æ ¡éª | åºä½ä¸å卿¶æ¥é | æªæå®åºä½æ¶è·³è¿ç©æ/æ¹æ¬¡æ··æ¾æ ¡éª | |
| | | | åºåéæ©æ ¡éª | åç«¯ä¼ å
¥çåºåº/åºä½å¿
é¡»ä¸åºåè®°å½å®å
¨ä¸è´ | ä»
å¨ä¼ å
¥é空 `locationId`ã`areaId` æ¶æ¯å¯¹ | |
| | | | å·¥ä½ç«ä¿å | åºåºå¿
é¡»å±äºä»åºãåºä½å¿
é¡»å±äºåºåº | ä»
æ ¡éªä»åºåå¨ï¼åºåº/åºä½ä¸ååä¸ | |
| | | | ç§åè´¨æ£åæ ¼å
¥åº | åæ ¼åºæªé
ç½®åºåº/åºä½æ¶æ¥é | åºåº/åºä½ååæ ¼åºä¸é¦ä¸ªéå»ç»åºåº/åºä½ï¼æªé
ç½®æ¶æä»åºç´æ¥å
¥åº | |
| | | | åºååå¹¶ï¼ç§»åº/åæ ¼å
¥åºï¼ | ç»åé®ä¸åºåº/åºä½éç¨ç²¾ç¡®å¹é
| `locationId`ã`areaId` 为空æ¶å¿½ç¥è¯¥ç»´åº¦ï¼æç©æ+ä»åº+æ¹æ¬¡å¹é
| |
| | | |
| | | ## æ°æ®å¸¦å
¥è¯´æ |
| | | |
| | | 1. åæ®æç»éæ©åºåè®°å½åï¼é¡µé¢åªå¸¦å
¥ `itemId`ã`batchId`ã`batchCode`ã`quantity`ã`warehouseId`ï¼ä¸å带å
¥ `itemName`/`batchCode` ä¹å¤çåºåºãåºä½åç§°ã |
| | | 2. ç§»åºãåºåè°æ´çç®æ ä½ç½®åªéæ©ä»åºï¼æäº¤æ¶åªä¼ ç®æ ä»åºç¼å·ã |
| | | 3. ç§»å¨ç«¯æ«ç åºå
¥åºæè¿½æº¯ç è§£æç©æä¸æ¹æ¬¡ï¼è½åºä½ç½®é»è®¤ä¸ºç产æåä»/åæ ¼åºï¼ä¸åè¦æ±éæ©åºåºãåºä½ã |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - å岿°æ®ä¸ `location_id`ã`area_id` å¯è½ä»ä¸ºé空å¼ï¼å±ç¤ºå±å¯å¿½ç¥ï¼ä¸å½±åæ¥è¯¢ä¸åºå
¥åºã |
| | | - å端è°ç¨åºå
¥åºãç§»åºãçç¹çä¿åæ¥å£æ¶ï¼è¯·ä¸è¦åä¼ åºåº/åºä½ï¼è¥ä»ä¼ å
¥åå² IDï¼å端ä¼ç
§å¸¸æ ¡éªå
¶å卿§ï¼ID 失æå°å¯¼è´æ¥éã |
| | | - å·²æ§è¡ `sql/mysql/mes_wm_remove_warehouse_location_area_menu.sql` æ¸
çåºåº/åºä½èå䏿éåï¼å端ä¸è½åéè¿èåè¿å
¥æ§é¡µé¢ã |
| | | - åºåç»åé®ï¼ç©æ+ä»åº+åºåº+åºä½+æ¹æ¬¡+ä¾åºåï¼ä¸åºåº/åºä½å为å¯éï¼åä¸ä»åºåæ¹æ¬¡ä¸ååºåºçåå²åºåå¯è½å¨ç§»åºåå¹¶æ¶è¢«åå¹¶å°ä¸æ¡è®°å½ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # éå®åºåºä¼åä¿®æ¹è¯´æ |
| | | |
| | | ## 1. ä¿®æ¹èå´åå½åç¶æ |
| | | |
| | | æ¬æ¬¡ä¿®æ¹è¦çéå®åºåºå次åè´§ãPC åç§»å¨ç«¯æ£è´§ãåºåè¢ç ç¶æãåºåäºå¡æµæ°´åæ¹æ¬¡è¿½æº¯ã代ç å·²ç»ä¿®æ¹ï¼**æ¬æ¬¡æ´å¥ä¿®æ¹å°æªæ§è¡ç¼è¯ãèªå¨åæµè¯æé¡µé¢èè°**ï¼ä»¥ä¸æè¿°æ¯å®ç°æå¾åå¾
éªè¯è¡ä¸ºï¼ä¸ä»£è¡¨éªæ¶å·²éè¿ã |
| | | |
| | | åå²çæ¬ä¸æ¾å¯¹é¨ååç«¯ä»£ç æ§è¡ç¼è¯ï¼ä½ä¸è½ä½ä¸ºæ¬æ¬¡å
¨é¨ä¿®æ¹çéªè¯ç»æãæµè¯æé¨ç½²åï¼åºå
æ ¸å¯¹å½åå·¥ä½åºçå®é
代ç åæ°æ®åºç»æã |
| | | |
| | | ## 2. åè½ä¿®æ¹ |
| | | |
| | | ### 2.1 éå®åºåºä¸å次åè´§ |
| | | |
| | | - ä¸å¼ éå®åºåºåå¯åå»ºå¤æ¬¡åè´§è®°å½ãæ¯æ¬¡å
æ£è´§ãå¡«åè¿åï¼åæ§è¡æ¬æ¬¡åºåºã |
| | | - æ¬æ¬¡è³å°éæ©ä¸æ¡å¾
åæç»ï¼åè¡ç´¯è®¡æ£è´§æ°éä¸å¾è¶
è¿è¯¥è¡è®¡åæ°éãå
许åªåé¨åç©æè¡ã |
| | | - æ¬æ¬¡åºåºåªå¤ç该åè´§è®°å½çæç»å¹¶çæå¯¹åºåºåæµæ°´ï¼åè´§éç¥åææ¬æ¬¡å®é
æç»æ°é累计ååã |
| | | - å
¨é¨è®¡åæ°éåºåºååæ®å®æï¼æªå
¨é¨å宿¶ï¼åæ®è¿åå¾
æ£è´§ï¼å¯ç»§ç»ä¸ä¸æ¬¡åè´§ãå·²é¨ååè´§çåæ®å¨é¡µé¢ä¸æ¾ç¤ºç¸åºæç¤ºã |
| | | - å¾
è¿åæå¾
åºåºæ¶å¯ä»¥åæ¶æ¬æ¬¡åè´§ï¼éæ¾å ç¨ï¼æç»ä¿çå¹¶è¿åå¾
æ£è´§ç¶æãå·²æåè´§ä½ä¸åç»§ç»åè´§æ¶ï¼å¯å
ç§»é¤æ¬è½®æªåæç»ï¼å¡«ååå å
³éå©ä½æ°éã |
| | | - å·²åé
åè´§è®°å½çæç»ä¸è½ç´æ¥ä¿®æ¹æå é¤ï¼å·²åºåºåççº æ£åºèµ°éè´§çä¸å¡æµç¨ã |
| | | |
| | | ### 2.2 æ«ç åæå·¥æ£è´§ |
| | | |
| | | - æ¯ææ«ææè¾å
¥æ¹æ¬¡ç ãæçç ãè¢ç ï¼è¯å«åæå®é
è¢ç éæ©ãä¾å¦â1 æ¹ + 4 æç + 8 è¢âå¯ä»¥å次è¯å«åå å
¥å䏿¬¡æ£è´§æ¸
åã |
| | | - è¢ç æå®é
éä¸ç»æçææç»ï¼éå¤ãå·²åºåºãæªè´¨æ£åæ ¼ãå»ç»æä¸å¨ææåæ ¼åºåçè¢ç åºè¢«æ¦æªææ æä¸å¯éåå ã |
| | | - PC 端æå·¥æ£è´§å¯å
éæ©åºåï¼åå è½½å¹¶éæ©è¯¥åºåçè¢ç ï¼ç§»å¨ç«¯å¯ä»åºåºåè¡éæ©åºåè¢ç ï¼ä¹æ¯ææå¨è¾å
¥ç å¼ã |
| | | - æå¨åºè¢ç æç»çåºåä¸è½ç´æ¥å建æ è¢ç çæ°éæ£è´§æç»ï¼æ å¨åºè¢ç æç»çåå²åºåä¿çææ°éæ£è´§å
¥å£ï¼ä½æ æ³éè¢è¿½æº¯ã |
| | | - æç»ä¸çæ«ç ç±»å忥æºç æ¾ç¤ºä¸ºä¸æï¼ä¾å¦âæ«ç ç±»å æ¹æ¬¡ç ï¼æ¥æºæ¹æ¬¡ç â¦âï¼âè¢ç 䏿«ç æ¥æºâ忝æé¼ æ æ¬åæ¥çã |
| | | |
| | | ### 2.3 åºåãæµæ°´ä¸è¿½æº¯ |
| | | |
| | | - åºåå表é»è®¤æé¤é¶åºåï¼PC åç§»å¨ç«¯å¯éæ©å
å«é¶åºåè®°å½å¹¶æ¥çä»å
³è该åºåè®°å½çè¢ç åå²ã |
| | | - è¢ç æç»æ¾ç¤ºå¨åºãå·²æ£éãå·²å ç¨ãå·²åºåºæå·²æ¥åºçç¶æï¼åªæå
³èæµæ°´å¯å¤æä¸ºéå®åºåºæ¶ææ¾ç¤ºâéå®å·²åºåºâã |
| | | - åºåäºå¡æµæ°´å¢å æçç ãè¢ç ååè´§è®°å½å
³èãå岿µæ°´æ²¡æå
³èæ°æ®æ¶æ¾ç¤ºæªè®°å½ï¼ä¸æ¨æµå
·ä½è¢ç ã |
| | | - æ¹æ¬¡è¿½æº¯ä¸çéå®äºä»¶æå®é
éå®åºåºæµæ°´èåï¼å¯å±å¼æ¥è¯¢ç¸åºåºåºåãæ¹æ¬¡ååè´§è®°å½çæµæ°´ã |
| | | |
| | | ## 3. æ°æ®åºåé¨ç½² |
| | | |
| | | 1. å¤ä»½ç®æ æ°æ®åºï¼ç¡®è®¤å·²æåºåè¢ç ãéå®åºåºè¿½æº¯çåºç¡è¡¨ååæ®µå·²æåçæ¬é¨ç½²ã |
| | | 2. æ ¸å¯¹ç®æ åºåï¼æ§è¡ä¸æ¬¡ `sql/mysql/mes_wm_product_sales_shipment.sql`ï¼æ°å¢ `mes_wm_product_sales_shipment` 表ï¼å¨éå®åºåºåå¢å `close_reason`ï¼å¨åºåºæç»ååºåäºå¡æµæ°´å¢å `shipment_id` åç´¢å¼ã |
| | | 3. 该 SQL **ä¸å¯é夿§è¡**ï¼å
æ£æ¥ç®æ åºæ¯å¦å·²ç»å
å«è¡¨ååæ®µï¼é¿å
é夿§è¡ãä¸è¦æå®ä¸å
¶ä»æªç¡®è®¤ç¨éç SQL ä¸å¹¶æ§è¡ã |
| | | 4. åè°åå¸å端å PCãç§»å¨ç«¯å端ï¼ç¡®ä¿ä»£ç åæ°æ®åºç»æçæ¬ä¸è´ï¼å¨æµè¯ç¯å¢å®æä¸åéªè¯åå宿æ£å¼ç¯å¢åå¸ã |
| | | |
| | | ### å岿°æ®å
¼å®¹ |
| | | |
| | | - åå²å·²å®æåæ®çæç»åæµæ°´ä¸ä¼èªå¨çæåè´§è®°å½ï¼å岿µæ°´ä»ä»¥åææ°æ®ä¸ºåã |
| | | - å级æ¶å·²å¤äºå¾
å¡«åè¿åæå¾
æ§è¡åºåºçæ§åæ®ï¼ç»§ç»æä½æ¶ä¼ä¸ºå·²ææç»å»ºç«åè´§è®°å½ï¼ééç¹éªè¯åºåå ç¨åéç¥åååã |
| | | - æ æ³ä»ä»
ææ°éçå岿ç»å¯é è¿åå®é
è¢ç ï¼ä¸èªå¨è¡¥é è¢ç ã |
| | | - å½åè¢ç æç»è®°å½çæ¯è¢ç å½åå
³èçåºåè®°å½åç¶æï¼ä¸åºå°å
¶å½ä½ææåå²ç§»åºä½ç½®ç宿´å¿«ç
§ï¼å岿µè½¬ä»¥äºå¡æµæ°´ä¸ºåã |
| | | |
| | | ## 4. ä¸»è¦æ¥å£åæé |
| | | |
| | | | æ¥å£ | ç¨é | æé | |
| | | | --- | --- | --- | |
| | | | `GET /mes/wm/product-sales/shipments` | æ¥çåè´§è®°å½ | `mes:wm-product-sales:query` | |
| | | | `GET /mes/wm/product-sales/scan-preview` | è¯å«æ¹æ¬¡ãæçæè¢ç ï¼è¿åè¢ç å¯éç¶æ | `mes:wm-product-sales:stock` | |
| | | | `POST /mes/wm/product-sales/scan` | å°éä¸çè¢ç å å
¥æ£è´§ï¼æå·¥éè¢å¯æå®è¡ååºå | `mes:wm-product-sales:stock` | |
| | | | `PUT /mes/wm/product-sales/stock` | å®ææ¬æ¬¡æ£è´§å¹¶å ç¨åºå | `mes:wm-product-sales:stock` | |
| | | | `PUT /mes/wm/product-sales/shipping` | å¡«åæ¬æ¬¡è¿å | `mes:wm-product-sales:shipping` | |
| | | | `PUT /mes/wm/product-sales/finish` | æ§è¡æ¬æ¬¡åºåº | `mes:wm-product-sales:finish` | |
| | | | `PUT /mes/wm/product-sales/cancel-shipment` | åæ¶å°æªåºåºçæ¬æ¬¡åè´§å¹¶éæ¾å ç¨ | `mes:wm-product-sales:cancel` | |
| | | | `PUT /mes/wm/product-sales/close-remaining` | å¡«ååå å¹¶å
³éå©ä½æ°é | `mes:wm-product-sales:cancel` | |
| | | | `GET /mes/wm/material-stock/picking-list` | æ ¹æ®åºåºåè¡æ¥è¯¢åæ ¼åºå | åæ¶éè¦é宿£è´§ååºåæ¥è¯¢æé | |
| | | | `POST /mes/wm/product-sales-detail/create-mobile-manual` | ç§»å¨ç«¯ææ°éæ£éæ è¢ç åå²åºå | `mes:wm-product-sales:stock` | |
| | | | `GET /mes/wm/material-stock/bag-details` | æ¥è¯¢åºåè¢ç ï¼`includeHistory=true` å
å«åå²ç¶æ | `mes:wm-material-stock:query` | |
| | | | `GET /mes/wm/transaction/page` | æ¥è¯¢åºåæµæ°´ï¼æ¯æç²¾ç¡®åå·ãæ¹æ¬¡ååè´§è®°å½çé | `mes:wm-transaction:query` | |
| | | |
| | | ## 5. æ´ä½æµè¯æ¸
å |
| | | |
| | | ä¸å项ç®å为**å¾
éªè¯**ï¼ |
| | | |
| | | 1. **忬¡åè´§**ï¼åä¸å¼ åæ®å两次åè´§ï¼éæ¬¡æ ¸å¯¹åºåãåè´§è®°å½ãè¿åãåè¡å·²åºåºåå©ä½éãéç¥å累计éåç¶æãè¦çä»
åé¨åç©æè¡ã |
| | | 2. **æ··åæ«ç **ï¼ææ¹æ¬¡ãæçãåè¢æ··åéæ©ï¼æ ¸å¯¹å®é
è¢ç ãæ¥æºç±»å䏿¥æºç ãè¦çé夿«æã交åèå´ãè·¨åå¹¶åãå»ç»ãæªè´¨æ£åæ ¼åè¶
åºå©ä½æ°éã |
| | | 3. **æå·¥æ£è´§**ï¼PC åç§»å¨ç«¯åå«éæ©å®é
è¢ç ï¼å¯¹æ è¢ç åå²åºåéªè¯æ°éæ£è´§åæ è¢ç æç¤ºï¼éªè¯å·²æè¢ç åºåä¸è½ç»è¿éè¢éæ©ã |
| | | 4. **åæ¶ä¸å
³é**ï¼åæ¶å¾
åºåºåè´§åæ ¸å¯¹å ç¨éæ¾åæç»è°æ´ï¼å·²æåºåºåå
³éå©ä½éï¼æ ¸å¯¹å
³éåå åéç¥åä¸ä¼è¯¯æ å
¨é¨åºåºã |
| | | 5. **追溯ååå²**ï¼æ ¸å¯¹é¶åºåè¢ç åå²ãåºåæµæ°´çæ¹æ¬¡ï¼æçï¼è¢ç ãæ¹æ¬¡æ¶é´çº¿çæ¯æ¬¡åºåºãè¦çåå²å·²å®æåæ®åå级åå¾
è¿åï¼å¾
åºåºåæ®ã |
| | | 6. **å¼å¸¸ä¸æé**ï¼è¦ç请æ±è¶
æ¶åçç§»å¨ç«¯ç»ææ ¸å®ãéå¤æäº¤ãä¸åç¨æ·å¹¶åæ£éãåºåä¸è¶³ä»¥å缺å°ç¸å
³èåæéçæ
åµã |
| | | |
| | | å
³é®æ ¸å¯¹å
³ç³»ï¼åä¸åè´§è®°å½å
ï¼æ¬æ¬¡å®é
æç»æ°éãåºåæ£åæ°éãéå®åºåºæµæ°´æ°éåºä¸è´ï¼éç¥å累计åºåºéåºçäºå次å®é
ååéï¼ä¸è½è¶
è¿è®¡åéãä¸åç©ææä¸ååä½åºå嫿 ¸å¯¹ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | param( |
| | | [Parameter(Mandatory = $true)][string]$ApiBaseUrl, |
| | | [Parameter(Mandatory = $true)][string]$AccessToken, |
| | | [long]$AdminUserId = 1, |
| | | # æµç¨åç±»ç¼ç ï¼éä¸ BPMãæµç¨åç±»ãä¸ç code ä¸è´ï¼ä¸ºç©ºå模åä¸ä¼æ¾ç¤ºå¨æµç¨æ¨¡å页 |
| | | [string]$CategoryCode = 'mes_qc_approve' |
| | | ) |
| | | |
| | | $ErrorActionPreference = 'Stop' |
| | | $base = $ApiBaseUrl.TrimEnd('/') |
| | | $headers = @{ Authorization = "Bearer $AccessToken" } |
| | | $resourceRoot = Join-Path $PSScriptRoot '../yudao-module-mes/src/main/resources/bpm' |
| | | # 䏿å½åç»ä¸ä» BPMN æä»¶è¯»åï¼é¿å
Windows PowerShell 5.1 æ ANSI è§£æèæ¬å¯¼è´ä¹±ç |
| | | $models = @( |
| | | 'mes_seed_quality_line_approve', |
| | | 'mes_seed_quality_company_approve', |
| | | 'mes_seed_return_approve' |
| | | ) |
| | | |
| | | function Invoke-BpmApi([string]$Method, [string]$Path, $Body) { |
| | | $request = @{ Method = $Method; Uri = "$base$Path"; Headers = $headers } |
| | | if ($null -ne $Body) { |
| | | $json = ConvertTo-Json -InputObject $Body -Depth 12 -Compress |
| | | # Windows PowerShell 5.1 é»è®¤æ ISO-8859-1 ç¼ç 请æ±ä½ï¼å¿
é¡»æ¾å¼è½¬ UTF-8 åè |
| | | $request.ContentType = 'application/json; charset=utf-8' |
| | | $request.Body = [System.Text.Encoding]::UTF8.GetBytes($json) |
| | | } |
| | | $result = Invoke-RestMethod @request |
| | | if ($result.code -ne 0) { throw "BPM API $Path failed: $($result.msg)" } |
| | | return $result.data |
| | | } |
| | | |
| | | $existing = @(Invoke-BpmApi 'GET' '/bpm/model/list' $null) |
| | | foreach ($key in $models) { |
| | | $path = Join-Path $resourceRoot "$key.bpmn20.xml" |
| | | $xml = Get-Content -LiteralPath $path -Raw -Encoding UTF8 |
| | | if (-not $xml.Contains('flowable:candidateParam="1"')) { |
| | | throw "BPM model $key has no admin candidate placeholder" |
| | | } |
| | | $xml = $xml -replace 'flowable:candidateParam="1"', "flowable:candidateParam=`"$AdminUserId`"" |
| | | [xml]$doc = $xml |
| | | $processName = [string]$doc.definitions.process.name |
| | | $model = $existing | Where-Object { $_.key -eq $key } | Select-Object -First 1 |
| | | $body = @{ |
| | | key = $key; name = $processName; category = $CategoryCode; type = 10; formType = 20 |
| | | formCustomCreatePath = '/mes/qc/seed-quality-record' |
| | | formCustomViewPath = '/mes/qc/seed-quality-record' |
| | | visible = $false; startUserIds = @(); managerUserIds = @($AdminUserId) |
| | | autoApprovalType = 0; bpmnXml = $xml |
| | | } |
| | | if ($null -eq $model) { |
| | | $id = Invoke-BpmApi 'POST' '/bpm/model/create' $body |
| | | } else { |
| | | $id = $model.id |
| | | $body.id = $id |
| | | [void](Invoke-BpmApi 'PUT' '/bpm/model/update' $body) |
| | | } |
| | | [void](Invoke-BpmApi 'POST' "/bpm/model/deploy?id=$([uri]::EscapeDataString([string]$id))" $null) |
| | | Write-Output "Deployed $key ($id)" |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ============================================= |
| | | -- CRM å®¢æ·æ°å¢ç»éå夿¡ç¸å
³å段 |
| | | -- ç®æ åºï¼mom-thzy |
| | | -- 说æï¼MySQL ç ALTER TABLE ADD COLUMN 䏿¯æ IF NOT EXISTSï¼ |
| | | -- å
æ¥ information_schema åæ§è¡ï¼ä¿è¯èæ¬å¯é夿§è¡ã |
| | | -- ============================================= |
| | | |
| | | SET @col_exists = ( |
| | | SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = DATABASE() |
| | | AND table_name = 'crm_customer' |
| | | AND column_name = 'social_credit_code' |
| | | ); |
| | | |
| | | SET @ddl = IF(@col_exists = 0, |
| | | 'ALTER TABLE `crm_customer` |
| | | ADD COLUMN `social_credit_code` VARCHAR(64) NULL COMMENT ''社ä¼ä¿¡ç¨ä»£ç '' AFTER `name`, |
| | | ADD COLUMN `id_card_no` VARCHAR(32) NULL COMMENT ''身份è¯å·'' AFTER `social_credit_code`, |
| | | ADD COLUMN `customer_name` VARCHAR(64) NULL COMMENT ''客æ·å§å'' AFTER `id_card_no`, |
| | | ADD COLUMN `filing_serial_no` VARCHAR(64) NULL COMMENT ''夿¡æµæ°´å·'' AFTER `customer_name`, |
| | | ADD COLUMN `bank_account_name` VARCHAR(128) NULL COMMENT ''é¶è¡è´¦æ·åç§°'' AFTER `filing_serial_no`, |
| | | ADD COLUMN `region` VARCHAR(64) NULL COMMENT ''æå±åºå'' AFTER `bank_account_name`, |
| | | ADD COLUMN `parent_dealer_id` BIGINT NULL COMMENT ''æå±ä¸çº§ç»éåç¼å·'' AFTER `region`', |
| | | 'SELECT 1'); |
| | | |
| | | PREPARE stmt FROM @ddl; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | -- ç´¢å¼åæ ·ç¨ information_schema 夿ï¼MySQL 8.4 ç ADD INDEX IF NOT EXISTS ä¸å¯ç¨ï¼ |
| | | SET @idx_exists = ( |
| | | SELECT COUNT(*) FROM information_schema.statistics |
| | | WHERE table_schema = DATABASE() AND table_name = 'crm_customer' |
| | | AND index_name = 'idx_crm_customer_parent_dealer_id' |
| | | ); |
| | | SET @ddl = IF(@idx_exists = 0, |
| | | 'ALTER TABLE `crm_customer` ADD INDEX `idx_crm_customer_parent_dealer_id` (`parent_dealer_id`)', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @ddl; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @idx_exists = ( |
| | | SELECT COUNT(*) FROM information_schema.statistics |
| | | WHERE table_schema = DATABASE() AND table_name = 'crm_customer' |
| | | AND index_name = 'idx_crm_customer_social_credit_code' |
| | | ); |
| | | SET @ddl = IF(@idx_exists = 0, |
| | | 'ALTER TABLE `crm_customer` ADD INDEX `idx_crm_customer_social_credit_code` (`social_credit_code`)', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @ddl; |
| | | EXECUTE stmt; |
| | | DEALLOCATE PREPARE stmt; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- BPM è´¨æ£æµç¨å¯ç¨åæ§è¡ãä»
éèæ§å®¡æ ¸äººé
ç½®èååæé®ï¼ä¿çé
置记å½ä»¥ä¾åå²è¿½æº¯ã |
| | | -- æ§è¡å确认ä¸ä¸ª BPM æµç¨å®ä¹å·²åå¸ï¼å¹¶å·²å¤çæ§çå¨é人工审æ¹è®°å½ã |
| | | UPDATE system_menu child |
| | | JOIN system_menu parent ON child.parent_id = parent.id |
| | | SET child.status = 1, child.visible = b'0', child.update_time = NOW() |
| | | WHERE parent.parent_id = 5500 AND parent.path = 'approver-config' |
| | | AND parent.deleted = b'0' AND child.deleted = b'0'; |
| | | |
| | | UPDATE system_menu |
| | | SET status = 1, visible = b'0', update_time = NOW() |
| | | WHERE parent_id = 5500 AND path = 'approver-config' AND deleted = b'0'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- è´¨æ£é¡¹åæ ¼èå´é
ç½®ï¼ç±å
¨å±åºå®å项æ¹ä¸ºæäº§åç»´æ¤ |
| | | -- 1. 主é®ç± indicator_code æ¹ä¸ºèªå¢ id |
| | | -- 2. æ°å¢äº§åç»å®ãåç§°ãåä½ãæåºãç¶æåæ®µ |
| | | -- 3. åå²å项èªå¨è¿ç§»ä¸ºéç¨é»è®¤é¡¹ï¼item_id 为空ï¼ï¼äº§åæªé
ç½®æ¶åéä½¿ç¨ |
| | | -- 4. è´¨æ£ææ è®°å½è¡¥å
åç§°ä¸åä½å¿«ç
§ |
| | | |
| | | -- ===== 1. 䏻鮿¹ä¸ºèªå¢ id ===== |
| | | SET @has_id = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = DATABASE() AND table_name = 'mes_qc_seed_indicator_range' AND column_name = 'id'); |
| | | SET @sql = IF(@has_id = 0, |
| | | 'ALTER TABLE mes_qc_seed_indicator_range DROP PRIMARY KEY, ADD COLUMN id BIGINT NOT NULL AUTO_INCREMENT FIRST, ADD PRIMARY KEY (id)', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | -- ===== 2. æ°å¢é
ç½®åæ®µï¼MySQL 䏿¯æ ADD COLUMN IF NOT EXISTSï¼éå夿ï¼===== |
| | | SET @has_item_id = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = DATABASE() AND table_name = 'mes_qc_seed_indicator_range' AND column_name = 'item_id'); |
| | | SET @sql = IF(@has_item_id = 0, |
| | | 'ALTER TABLE mes_qc_seed_indicator_range ADD COLUMN item_id BIGINT NULL COMMENT ''ç»å®äº§åç¼å·ï¼mdm_itemï¼itemType=2ï¼ï¼ä¸ºç©ºè¡¨ç¤ºéç¨é»è®¤é¡¹'' AFTER id', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @has_name = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = DATABASE() AND table_name = 'mes_qc_seed_indicator_range' AND column_name = 'indicator_name'); |
| | | SET @sql = IF(@has_name = 0, |
| | | 'ALTER TABLE mes_qc_seed_indicator_range ADD COLUMN indicator_name VARCHAR(64) NULL COMMENT ''è´¨æ£é¡¹åç§°'' AFTER indicator_code', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @has_unit = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = DATABASE() AND table_name = 'mes_qc_seed_indicator_range' AND column_name = 'unit'); |
| | | SET @sql = IF(@has_unit = 0, |
| | | 'ALTER TABLE mes_qc_seed_indicator_range ADD COLUMN unit VARCHAR(16) NULL COMMENT ''计éåä½'' AFTER indicator_name', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @has_sort = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = DATABASE() AND table_name = 'mes_qc_seed_indicator_range' AND column_name = 'sort'); |
| | | SET @sql = IF(@has_sort = 0, |
| | | 'ALTER TABLE mes_qc_seed_indicator_range ADD COLUMN sort INT NOT NULL DEFAULT 0 COMMENT ''æåº'' AFTER max_value', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @has_status = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = DATABASE() AND table_name = 'mes_qc_seed_indicator_range' AND column_name = 'status'); |
| | | SET @sql = IF(@has_status = 0, |
| | | 'ALTER TABLE mes_qc_seed_indicator_range ADD COLUMN status TINYINT NOT NULL DEFAULT 0 COMMENT ''ç¶æï¼0å¯ç¨ 1åç¨'' AFTER sort', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | -- ===== 3. å岿°æ®åå¡« ===== |
| | | UPDATE mes_qc_seed_indicator_range |
| | | SET indicator_name = CASE indicator_code |
| | | WHEN 'purity' THEN '纯度' |
| | | WHEN 'cleanliness' THEN 'å度' |
| | | WHEN 'germination_rate' THEN 'åè½ç' |
| | | WHEN 'moisture' THEN 'æ°´å' |
| | | ELSE indicator_code END |
| | | WHERE indicator_name IS NULL OR indicator_name = ''; |
| | | |
| | | UPDATE mes_qc_seed_indicator_range SET unit = '%' WHERE unit IS NULL OR unit = ''; |
| | | |
| | | UPDATE mes_qc_seed_indicator_range |
| | | SET sort = CASE indicator_code |
| | | WHEN 'purity' THEN 10 |
| | | WHEN 'cleanliness' THEN 20 |
| | | WHEN 'germination_rate' THEN 30 |
| | | WHEN 'moisture' THEN 40 |
| | | ELSE 999 END |
| | | WHERE sort IS NULL OR sort = 0; |
| | | |
| | | -- ===== 4. 产å + ç¼ç ç´¢å¼ ===== |
| | | SET @has_index = (SELECT COUNT(*) FROM information_schema.statistics |
| | | WHERE table_schema = DATABASE() AND table_name = 'mes_qc_seed_indicator_range' AND index_name = 'idx_item_code'); |
| | | SET @sql = IF(@has_index = 0, |
| | | 'ALTER TABLE mes_qc_seed_indicator_range ADD INDEX idx_item_code (item_id, indicator_code)', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | -- ===== 5. è´¨æ£ææ è®°å½è¡¥å
åç§°ä¸åä½å¿«ç
§ ===== |
| | | SET @has_ind_name = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = DATABASE() AND table_name = 'mes_qc_seed_quality_indicator' AND column_name = 'indicator_name'); |
| | | SET @sql = IF(@has_ind_name = 0, |
| | | 'ALTER TABLE mes_qc_seed_quality_indicator ADD COLUMN indicator_name VARCHAR(64) NULL COMMENT ''ææ åç§°å¿«ç
§'' AFTER indicator_code', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @has_ind_unit = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = DATABASE() AND table_name = 'mes_qc_seed_quality_indicator' AND column_name = 'unit'); |
| | | SET @sql = IF(@has_ind_unit = 0, |
| | | 'ALTER TABLE mes_qc_seed_quality_indicator ADD COLUMN unit VARCHAR(16) NULL COMMENT ''ææ åä½å¿«ç
§'' AFTER indicator_name', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | UPDATE mes_qc_seed_quality_indicator |
| | | SET indicator_name = CASE indicator_code |
| | | WHEN 'purity' THEN '纯度' |
| | | WHEN 'cleanliness' THEN 'å度' |
| | | WHEN 'germination_rate' THEN 'åè½ç' |
| | | WHEN 'moisture' THEN 'æ°´å' |
| | | ELSE indicator_code END |
| | | WHERE indicator_name IS NULL OR indicator_name = ''; |
| | | |
| | | UPDATE mes_qc_seed_quality_indicator SET unit = '%' WHERE unit IS NULL OR unit = ''; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ===================================================================== |
| | | -- ç§åè´¨æ£ãå å·¥é¨ / éå®é¨ãå线æ£éª + BPM å®¡æ¹ + ç»éåéè´§å¤æ£ |
| | | -- |
| | | -- åæ´å
å®¹ï¼ |
| | | -- 1. 䏿¡è¢ç ç忣忶çæå å·¥é¨ãéå®é¨ä¸¤æ¡è´¨æ£è®°å½ï¼qc_lineï¼ |
| | | -- 2. 两æ¡çº¿åèªèµ°é¨é¨é¢å¯¼å®¡æ¹ï¼äºä¸å½±å |
| | | -- 3. 两æ¡çº¿é½è¿å
¥å¤æ£å®¡æ ¸åï¼ç³»ç»èªå¨åèµ·å
¬å¸è´¨æ£å®¡æ¹ |
| | | -- 4. ç»éåéè´§å¤æ£å¤ç¨åä¸å¼ 表ï¼biz_type=2ï¼ï¼åç¬èµ°éå®é¨çº¿ |
| | | -- |
| | | -- æ¬èæ¬å
¨é¨ DDL 带å卿§å¤æï¼å¯å¯¹åä¸ç¯å¢é夿§è¡ã |
| | | -- ä¸ä½¿ç¨å¤ç§æ·ï¼æ tenant_id åæ®µã |
| | | -- æ§è¡å请å
use <ç®æ åº>ã |
| | | -- ===================================================================== |
| | | |
| | | SET @db = DATABASE(); |
| | | |
| | | -- --------------------------------------------------------------------- |
| | | -- 1. è´¨æ£çº¿ï¼1 å å·¥é¨ 2 éå®é¨ 0 æªåºåï¼å岿°æ®ï¼ |
| | | -- --------------------------------------------------------------------- |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'qc_line'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD COLUMN `qc_line` TINYINT NOT NULL DEFAULT 0 COMMENT ''è´¨æ£çº¿ï¼1å å·¥é¨ 2éå®é¨ 0æªåºåï¼å岿°æ®ï¼'' AFTER `code`', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | -- --------------------------------------------------------------------- |
| | | -- 2. ä¸å¡ç±»åï¼1 ç产å
¥åºè´¨æ£ 2 ç»éåéè´§è´¨æ£ |
| | | -- --------------------------------------------------------------------- |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'biz_type'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD COLUMN `biz_type` TINYINT NOT NULL DEFAULT 1 COMMENT ''ä¸å¡ç±»åï¼1ç产å
¥åºè´¨æ£ 2ç»éåéè´§è´¨æ£'' AFTER `qc_line`', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | -- --------------------------------------------------------------------- |
| | | -- 3. BPM æµç¨å®ä¾ä¸æ¶é´èç¹ |
| | | -- --------------------------------------------------------------------- |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'process_instance_id'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD COLUMN `process_instance_id` VARCHAR(64) DEFAULT NULL COMMENT ''æ¬çº¿é¨é¨å®¡æ¹æµç¨å®ä¾ç¼å·'' AFTER `workflow_status`', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'company_process_instance_id'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD COLUMN `company_process_instance_id` VARCHAR(64) DEFAULT NULL COMMENT ''å
¬å¸è´¨æ£å®¡æ¹æµç¨å®ä¾ç¼å·'' AFTER `process_instance_id`', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'line_finished_at'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD COLUMN `line_finished_at` DATETIME DEFAULT NULL COMMENT ''æ¬çº¿é¨é¨å®¡æ¹å®ææ¶é´'' AFTER `process_approved_at`', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'company_submitted_at'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD COLUMN `company_submitted_at` DATETIME DEFAULT NULL COMMENT ''å
¬å¸è´¨æ£å®¡æ¹åèµ·æ¶é´'' AFTER `line_finished_at`', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | -- --------------------------------------------------------------------- |
| | | -- 4. ç»éåéè´§å
³è |
| | | -- --------------------------------------------------------------------- |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'return_sales_id'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD COLUMN `return_sales_id` BIGINT DEFAULT NULL COMMENT ''ç»éåéè´§åç¼å·'' AFTER `qualified_warehouse_id`', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'return_sales_code'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD COLUMN `return_sales_code` VARCHAR(64) DEFAULT NULL COMMENT ''ç»éåéè´§åå·'' AFTER `return_sales_id`', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | -- --------------------------------------------------------------------- |
| | | -- 5. å¯ä¸é®ç± (code) è°æ´ä¸º (code, qc_line)ï¼åè¢ç å
许两æ¡è´¨æ£çº¿è®°å½ |
| | | -- å
å 餿§å¯ä¸é®ï¼åå建æ°å¯ä¸é®ï¼å带å卿§å¤æ |
| | | -- --------------------------------------------------------------------- |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.statistics |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND index_name = 'uk_code'); |
| | | SET @sql = IF(@exists > 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` DROP INDEX `uk_code`', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.statistics |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND index_name = 'uk_code_line'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD UNIQUE KEY `uk_code_line` (`code`, `qc_line`)', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | -- --------------------------------------------------------------------- |
| | | -- 6. è¾
å©ç´¢å¼ï¼æµç¨å®ä¾åæ¥ãä¸å¡ç±»åçé |
| | | -- --------------------------------------------------------------------- |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.statistics |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND index_name = 'idx_process_instance'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD INDEX `idx_process_instance` (`process_instance_id`)', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.statistics |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND index_name = 'idx_company_process_instance'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD INDEX `idx_company_process_instance` (`company_process_instance_id`)', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | SET @exists = (SELECT COUNT(*) FROM information_schema.statistics |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND index_name = 'idx_biz_type'); |
| | | SET @sql = IF(@exists = 0, |
| | | 'ALTER TABLE `mes_qc_seed_quality` ADD INDEX `idx_biz_type` (`biz_type`, `workflow_status`)', |
| | | 'SELECT 1'); |
| | | PREPARE stmt FROM @sql; EXECUTE stmt; DEALLOCATE PREPARE stmt; |
| | | |
| | | -- åå²è®°å½ç»ä¸å½ä¸ºç产å
¥åºè´¨æ£ï¼è´¨æ£çº¿ä¿æ 0ï¼æªåºåï¼ï¼é¿å
éå¤å®¡æ¹ |
| | | UPDATE `mes_qc_seed_quality` SET `biz_type` = 1 WHERE `biz_type` IS NULL OR `biz_type` = 0; |
| | | |
| | | -- --------------------------------------------------------------------- |
| | | -- 7. æ§è¡åèªæ£ï¼present=1 è¡¨ç¤ºåæ®µ/ç´¢å¼å·²å°±ç»ª |
| | | -- --------------------------------------------------------------------- |
| | | SELECT 'mes_qc_seed_quality.qc_line' AS item, COUNT(*) AS present FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'qc_line' |
| | | UNION ALL SELECT 'mes_qc_seed_quality.biz_type', COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'biz_type' |
| | | UNION ALL SELECT 'mes_qc_seed_quality.process_instance_id', COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'process_instance_id' |
| | | UNION ALL SELECT 'mes_qc_seed_quality.company_process_instance_id', COUNT(*) FROM information_schema.columns |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND column_name = 'company_process_instance_id' |
| | | UNION ALL SELECT 'INDEX uk_code_line', COUNT(*) FROM information_schema.statistics |
| | | WHERE table_schema = @db AND table_name = 'mes_qc_seed_quality' AND index_name = 'uk_code_line'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- 忬¡åè´§ç»æï¼ä¸çº¿åå¯¹ç®æ åºæ§è¡ä¸æ¬¡ï¼åå²å·²å®æåæ®ä¿çåæµæ°´ï¼ä¸èªå¨æ¨æåè´§æ¹æ¬¡ã |
| | | CREATE TABLE mes_wm_product_sales_shipment ( |
| | | id BIGINT NOT NULL AUTO_INCREMENT COMMENT 'åè´§è®°å½ç¼å·', |
| | | sales_id BIGINT NOT NULL COMMENT 'éå®åºåºåç¼å·', |
| | | status TINYINT NOT NULL DEFAULT 0 COMMENT '0æ£è´§ä¸ï¼10å¾
è¿åï¼20å¾
åºåºï¼30å·²åºåºï¼40已忶', |
| | | carrier VARCHAR(100) DEFAULT NULL COMMENT 'æ¿è¿å', |
| | | shipping_number VARCHAR(100) DEFAULT NULL COMMENT 'è¿åå·', |
| | | outbound_time DATETIME DEFAULT NULL COMMENT 'å®é
åºåºæ¶é´', |
| | | outbound_user_id BIGINT DEFAULT NULL COMMENT 'åºåºæä½äºº', |
| | | creator VARCHAR(64) DEFAULT '' COMMENT 'å建è
', |
| | | create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', |
| | | updater VARCHAR(64) DEFAULT '' COMMENT 'æ´æ°è
', |
| | | update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', |
| | | deleted BIT NOT NULL DEFAULT 0 COMMENT 'æ¯å¦å é¤', |
| | | PRIMARY KEY (id), |
| | | KEY idx_sales_status (sales_id, status, deleted) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='éå®åºåºå次åè´§è®°å½'; |
| | | |
| | | ALTER TABLE mes_wm_product_sales |
| | | ADD COLUMN close_reason VARCHAR(500) DEFAULT NULL COMMENT 'é¨ååè´§åå
³éå©ä½æ°éçåå '; |
| | | |
| | | ALTER TABLE mes_wm_product_sales_detail |
| | | ADD COLUMN shipment_id BIGINT DEFAULT NULL COMMENT 'åè´§è®°å½IDï¼å岿ç»ä¸ºç©º', |
| | | ADD KEY idx_shipment_id (shipment_id); |
| | | |
| | | ALTER TABLE mes_wm_transaction |
| | | ADD COLUMN shipment_id BIGINT DEFAULT NULL COMMENT 'éå®åè´§è®°å½IDï¼å岿µæ°´ä¸ºç©º', |
| | | ADD KEY idx_shipment_id (shipment_id); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ============================================= |
| | | -- ç§»é¤ä»åº"åºåº/åºä½"èå䏿é |
| | | -- ä»åºä¸åç»´æ¤åºåºï¼warehouse_locationï¼ãåºä½ï¼warehouse_areaï¼ï¼ |
| | | -- 忥æ¸
çåå°èååè§è²ææï¼é¿å
å端访é®å·²å é¤ç页é¢ç»ä»¶ã |
| | | -- ============================================= |
| | | |
| | | -- 1. æ¸
çè§è²ä¸åºåº/åºä½èåçææå
³è |
| | | DELETE rm FROM system_role_menu rm |
| | | JOIN system_menu m ON m.id = rm.menu_id |
| | | WHERE m.component LIKE 'wls/warehouse/location%' |
| | | OR m.component LIKE 'wls/warehouse/area%' |
| | | OR m.component LIKE 'mes/wm/warehouse/location%' |
| | | OR m.component LIKE 'mes/wm/warehouse/area%' |
| | | OR m.permission LIKE 'mes:wm-warehouse-location%' |
| | | OR m.permission LIKE 'mes:wm-warehouse-area%'; |
| | | |
| | | -- 2. å é¤åºåº/åºä½èå |
| | | DELETE FROM system_menu |
| | | WHERE component LIKE 'wls/warehouse/location%' |
| | | OR component LIKE 'wls/warehouse/area%' |
| | | OR component LIKE 'mes/wm/warehouse/location%' |
| | | OR component LIKE 'mes/wm/warehouse/area%' |
| | | OR permission LIKE 'mes:wm-warehouse-location%' |
| | | OR permission LIKE 'mes:wm-warehouse-area%'; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- è´¨é管çï¼æ°å¢ãå å·¥é¨è´¨æ£ããéå®é¨è´¨æ£ãèåï¼å¤ç¨è´¨æ£è®°å½é¡µé¢ï¼æè´¨æ£çº¿åºå®è¿æ»¤ï¼ |
| | | -- ç¶èåï¼5500 è´¨é管ç |
| | | |
| | | -- å å·¥é¨è´¨æ£ |
| | | INSERT INTO `system_menu` |
| | | (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, |
| | | `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) |
| | | SELECT 1065303, 'å å·¥é¨è´¨æ£', '', 2, 96, 5500, 'seed-quality-record-processing', 'ep:document-checked', |
| | | 'mes/qc/seedQuality/record-processing', 'MesQcSeedQualityRecordProcessing', |
| | | 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | WHERE NOT EXISTS (SELECT 1 FROM `system_menu` WHERE id = 1065303); |
| | | |
| | | -- éå®é¨è´¨æ£ |
| | | INSERT INTO `system_menu` |
| | | (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, |
| | | `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) |
| | | SELECT 1065304, 'éå®é¨è´¨æ£', '', 2, 97, 5500, 'seed-quality-record-sales', 'ep:document-checked', |
| | | 'mes/qc/seedQuality/record-sales', 'MesQcSeedQualityRecordSales', |
| | | 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | WHERE NOT EXISTS (SELECT 1 FROM `system_menu` WHERE id = 1065304); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ============================================= |
| | | -- ç³»ç»å¤è§è®¾ç½®èååå§å |
| | | -- ç³»ç»ç®¡ç -> å¤è§è®¾ç½®ï¼ç»å½é¡µèæ¯å¾ãä¼ä¸ Logoï¼ |
| | | -- 页é¢è·¯ç± /system/appearance/index |
| | | -- ä¿åæé system:appearance:update |
| | | -- ç®æ åºï¼mom-thzy |
| | | -- ============================================= |
| | | |
| | | -- 页é¢èåï¼æå¨ãç³»ç»ç®¡çãä¸ï¼ |
| | | INSERT INTO `system_menu` |
| | | (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, |
| | | `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) |
| | | SELECT 1065301, 'å¤è§è®¾ç½®', '', 2, 15, 1, 'appearance', 'ep:picture', 'system/appearance/index', 'SystemAppearance', |
| | | 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | WHERE NOT EXISTS (SELECT 1 FROM `system_menu` WHERE id = 1065301); |
| | | |
| | | -- ä¿åæé®æé |
| | | INSERT INTO `system_menu` |
| | | (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, |
| | | `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) |
| | | SELECT 1065302, 'ä¿åå¤è§è®¾ç½®', 'system:appearance:update', 3, 1, 1065301, '', '', '', NULL, |
| | | 0, b'1', b'1', b'1', '1', NOW(), '1', NOW(), b'0' |
| | | WHERE EXISTS (SELECT 1 FROM `system_menu` WHERE id = 1065301) |
| | | AND NOT EXISTS (SELECT 1 FROM `system_menu` WHERE id = 1065302); |
| | |
| | | import java.io.IOException; |
| | | import java.time.LocalDateTime; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.stream.Stream; |
| | | |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| | |
| | | Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId)); |
| | | // 1.2 è·åè·ç¦»è¿å
¥å
¬æµ·çæ¶é´ |
| | | Map<Long, Long> poolDayMap = getPoolDayMap(list); |
| | | // 1.3 è·åæå±ä¸çº§ç»éååç§° |
| | | Set<Long> parentDealerIds = convertSet(list, CrmCustomerDO::getParentDealerId); |
| | | parentDealerIds.remove(null); |
| | | Map<Long, CrmCustomerDO> parentDealerMap = CollUtil.isEmpty(parentDealerIds) |
| | | ? Collections.emptyMap() : customerService.getCustomerMap(parentDealerIds); |
| | | // 2. è½¬æ¢æ VO |
| | | return BeanUtils.toBean(list, CrmCustomerRespVO.class, customerVO -> { |
| | | customerVO.setAreaName(AreaUtils.format(customerVO.getAreaId())); |
| | | MapUtils.findAndThen(parentDealerMap, customerVO.getParentDealerId(), |
| | | dealer -> customerVO.setParentDealerName(dealer.getName())); |
| | | // 2.1 设置å建人ãè´è´£äººåç§° |
| | | MapUtils.findAndThen(userMap, NumberUtils.parseLong(customerVO.getCreator()), |
| | | user -> customerVO.setCreatorName(user.getNickname())); |
| | |
| | | @Schema(description = "客æ·åç§°", example = "èµµå
") |
| | | private String name; |
| | | |
| | | @Schema(description = "社ä¼ä¿¡ç¨ä»£ç ", example = "91320600MA1XXXXXXX") |
| | | private String socialCreditCode; |
| | | |
| | | @Schema(description = "身份è¯å·", example = "320602199001011234") |
| | | private String idCardNo; |
| | | |
| | | @Schema(description = "客æ·å§å", example = "å¼ ä¸") |
| | | private String customerName; |
| | | |
| | | @Schema(description = "夿¡æµæ°´å·", example = "BA2026000001") |
| | | private String filingSerialNo; |
| | | |
| | | @Schema(description = "æå±åºå", example = "åé") |
| | | private String region; |
| | | |
| | | @Schema(description = "æå±ä¸çº§ç»éåç¼å·", example = "1024") |
| | | private Long parentDealerId; |
| | | |
| | | @Schema(description = "ææº", example = "18000000000") |
| | | private String mobile; |
| | | |
| | |
| | | @ExcelProperty("客æ·åç§°") |
| | | private String name; |
| | | |
| | | @Schema(description = "社ä¼ä¿¡ç¨ä»£ç ", example = "91320600MA1XXXXXXX") |
| | | @ExcelProperty("社ä¼ä¿¡ç¨ä»£ç ") |
| | | private String socialCreditCode; |
| | | |
| | | @Schema(description = "身份è¯å·", example = "320602199001011234") |
| | | @ExcelProperty("身份è¯å·") |
| | | private String idCardNo; |
| | | |
| | | @Schema(description = "客æ·å§å", example = "å¼ ä¸") |
| | | @ExcelProperty("客æ·å§å") |
| | | private String customerName; |
| | | |
| | | @Schema(description = "夿¡æµæ°´å·", example = "BA2026000001") |
| | | @ExcelProperty("夿¡æµæ°´å·") |
| | | private String filingSerialNo; |
| | | |
| | | @Schema(description = "é¶è¡è´¦æ·åç§°", example = "åéææå䏿éå
¬å¸") |
| | | @ExcelProperty("é¶è¡è´¦æ·åç§°") |
| | | private String bankAccountName; |
| | | |
| | | @Schema(description = "æå±åºå", example = "åé") |
| | | @ExcelProperty("æå±åºå") |
| | | private String region; |
| | | |
| | | @Schema(description = "æå±ä¸çº§ç»éåç¼å·", example = "1024") |
| | | @ExcelProperty("æå±ä¸çº§ç»éåç¼å·") |
| | | private Long parentDealerId; |
| | | |
| | | @Schema(description = "æå±ä¸çº§ç»éååç§°", example = "ææç»éå") |
| | | @ExcelProperty("æå±ä¸çº§ç»éå") |
| | | private String parentDealerName; |
| | | |
| | | @Schema(description = "è·è¿ç¶æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "13563") |
| | | @ExcelProperty(value = "è·è¿ç¶æ", converter = DictConvert.class) |
| | | @DictFormat(DictTypeConstants.BOOLEAN_STRING) |
| | |
| | | @NotEmpty(message = "客æ·åç§°ä¸è½ä¸ºç©º") |
| | | private String name; |
| | | |
| | | @Schema(description = "社ä¼ä¿¡ç¨ä»£ç ", example = "91320600MA1XXXXXXX") |
| | | @DiffLogField(name = "社ä¼ä¿¡ç¨ä»£ç ") |
| | | @Size(max = 64, message = "社ä¼ä¿¡ç¨ä»£ç é¿åº¦ä¸è½è¶
è¿ 64 个å符") |
| | | private String socialCreditCode; |
| | | |
| | | @Schema(description = "身份è¯å·", example = "320602199001011234") |
| | | @DiffLogField(name = "身份è¯å·") |
| | | @Size(max = 32, message = "身份è¯å·é¿åº¦ä¸è½è¶
è¿ 32 个å符") |
| | | private String idCardNo; |
| | | |
| | | @Schema(description = "客æ·å§å", example = "å¼ ä¸") |
| | | @DiffLogField(name = "客æ·å§å") |
| | | @Size(max = 64, message = "客æ·å§åé¿åº¦ä¸è½è¶
è¿ 64 个å符") |
| | | private String customerName; |
| | | |
| | | @Schema(description = "夿¡æµæ°´å·", example = "BA2026000001") |
| | | @DiffLogField(name = "夿¡æµæ°´å·") |
| | | @Size(max = 64, message = "夿¡æµæ°´å·é¿åº¦ä¸è½è¶
è¿ 64 个å符") |
| | | private String filingSerialNo; |
| | | |
| | | @Schema(description = "é¶è¡è´¦æ·åç§°", example = "åéææå䏿éå
¬å¸") |
| | | @DiffLogField(name = "é¶è¡è´¦æ·åç§°") |
| | | @Size(max = 128, message = "é¶è¡è´¦æ·åç§°é¿åº¦ä¸è½è¶
è¿ 128 个å符") |
| | | private String bankAccountName; |
| | | |
| | | @Schema(description = "æå±åºå", example = "åé") |
| | | @DiffLogField(name = "æå±åºå") |
| | | @Size(max = 64, message = "æå±åºåé¿åº¦ä¸è½è¶
è¿ 64 个å符") |
| | | private String region; |
| | | |
| | | @Schema(description = "æå±ä¸çº§ç»éåç¼å·", example = "1024") |
| | | @DiffLogField(name = "æå±ä¸çº§ç»éåç¼å·") |
| | | private Long parentDealerId; |
| | | |
| | | @Schema(description = "䏿¬¡èç³»æ¶é´") |
| | | @DiffLogField(name = "䏿¬¡èç³»æ¶é´") |
| | | @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
| | |
| | | private String name; |
| | | |
| | | /** |
| | | * 社ä¼ä¿¡ç¨ä»£ç |
| | | */ |
| | | private String socialCreditCode; |
| | | /** |
| | | * 身份è¯å· |
| | | */ |
| | | private String idCardNo; |
| | | /** |
| | | * 客æ·å§å |
| | | */ |
| | | private String customerName; |
| | | /** |
| | | * 夿¡æµæ°´å· |
| | | */ |
| | | private String filingSerialNo; |
| | | /** |
| | | * é¶è¡è´¦æ·åç§° |
| | | */ |
| | | private String bankAccountName; |
| | | /** |
| | | * æå±åºå |
| | | */ |
| | | private String region; |
| | | /** |
| | | * æå±ä¸çº§ç»éåç¼å· |
| | | * |
| | | * å
³è {@link CrmCustomerDO#getId()} åæ®µ |
| | | */ |
| | | private Long parentDealerId; |
| | | |
| | | /** |
| | | * è·è¿ç¶æ |
| | | */ |
| | | private Boolean followUpStatus; |
| | |
| | | // æ¼æ¥èªèº«çæ¥è¯¢æ¡ä»¶ |
| | | query.selectAll(CrmCustomerDO.class) |
| | | .likeIfPresent(CrmCustomerDO::getName, pageReqVO.getName()) |
| | | .likeIfPresent(CrmCustomerDO::getSocialCreditCode, pageReqVO.getSocialCreditCode()) |
| | | .likeIfPresent(CrmCustomerDO::getIdCardNo, pageReqVO.getIdCardNo()) |
| | | .likeIfPresent(CrmCustomerDO::getCustomerName, pageReqVO.getCustomerName()) |
| | | .likeIfPresent(CrmCustomerDO::getFilingSerialNo, pageReqVO.getFilingSerialNo()) |
| | | .likeIfPresent(CrmCustomerDO::getRegion, pageReqVO.getRegion()) |
| | | .eqIfPresent(CrmCustomerDO::getParentDealerId, pageReqVO.getParentDealerId()) |
| | | .eqIfPresent(CrmCustomerDO::getMobile, pageReqVO.getMobile()) |
| | | .eqIfPresent(CrmCustomerDO::getIndustryId, pageReqVO.getIndustryId()) |
| | | .eqIfPresent(CrmCustomerDO::getLevel, pageReqVO.getLevel()) |
| | |
| | | ErrorCode CUSTOMER_CREATE_NAME_NOT_NULL = new ErrorCode(1_020_006_013, "客æ·åç§°ä¸è½ä¸ºç©ºï¼"); |
| | | ErrorCode CUSTOMER_NAME_EXISTS = new ErrorCode(1_020_006_014, "å·²åå¨å为ã{}ãç客æ·ï¼"); |
| | | ErrorCode CUSTOMER_UPDATE_DEAL_STATUS_FAIL = new ErrorCode(1_020_006_015, "æ´æ°å®¢æ·çæäº¤ç¶æå¤±è´¥ï¼åå ï¼å·²ç»æ¯è¯¥ç¶æï¼æ éæ´æ°"); |
| | | ErrorCode CUSTOMER_PARENT_DEALER_SELF = new ErrorCode(1_020_006_016, "æå±ä¸çº§ç»éåä¸è½éæ©å®¢æ·èªèº«"); |
| | | |
| | | // ========== æé管ç 1_020_007_000 ========== |
| | | ErrorCode CRM_PERMISSION_NOT_EXISTS = new ErrorCode(1_020_007_000, "æ°æ®æéä¸åå¨"); |
| | |
| | | updateReqVO.setOwnerUserId(null); // æ´æ°çæ¶åï¼è¦æ updateReqVO è´è´£äººè®¾ç½®ä¸ºç©ºï¼é¿å
ä¿®æ¹ |
| | | // 1. æ ¡éªåå¨ |
| | | CrmCustomerDO oldCustomer = validateCustomerExists(updateReqVO.getId()); |
| | | // 1.1 æ ¡éªæå±ä¸çº§ç»éåä¸è½æ¯èªå·± |
| | | if (updateReqVO.getParentDealerId() != null |
| | | && updateReqVO.getParentDealerId().equals(updateReqVO.getId())) { |
| | | throw exception(CUSTOMER_PARENT_DEALER_SELF); |
| | | } |
| | | |
| | | // 2. æ´æ°å®¢æ· |
| | | CrmCustomerDO updateObj = BeanUtils.toBean(updateReqVO, CrmCustomerDO.class); |
| | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | |
| | | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( |
| | | convertSet(list, MesProBatchDO::getOwnerUserId)); |
| | | Map<Long, MesMdmItemDO> itemMap = mdmItemService.getItemMap(convertSet(list, MesProBatchDO::getItemId)); |
| | | // æ¹æ¬¡ä¸å·²çæçæ£å¼æçç ï¼æ¹éæ¥è¯¢ï¼é¿å
éè¡æ¥è¯¢ï¼ |
| | | Map<Long, List<String>> palletCodeMap = palletService.getPalletListByBatchIds( |
| | | convertSet(list, MesProBatchDO::getId)).stream() |
| | | .filter(pallet -> StrUtil.isNotBlank(pallet.getCode())) |
| | | .collect(Collectors.groupingBy(MesProPalletDO::getBatchId, |
| | | Collectors.mapping(MesProPalletDO::getCode, Collectors.toList()))); |
| | | return BeanUtils.toBean(list, MesProBatchRespVO.class, vo -> { |
| | | MapUtils.findAndThen(userMap, vo.getOwnerUserId(), user -> vo.setOwnerUserName(user.getNickname())); |
| | | MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { vo.setItemCode(item.getCode()); vo.setItemName(item.getName()); }); |
| | | vo.setPalletCodes(palletCodeMap.getOrDefault(vo.getId(), Collections.emptyList())); |
| | | }); |
| | | } |
| | | |
| | |
| | | @ExcelProperty("æçç æ°é") |
| | | private Integer palletQuantity; |
| | | |
| | | @Schema(description = "æ¹æ¬¡ä¸å
¨é¨æ£å¼æçç ") |
| | | @ExcelIgnore |
| | | private List<String> palletCodes; |
| | | |
| | | @Schema(description = "æ¹æ¬¡ç¶æï¼åå
¸ mes_pro_batch_statusï¼", example = "20") |
| | | @ExcelProperty(value = "æ¹æ¬¡ç¶æ", converter = DictConvert.class) |
| | | @DictFormat(DictTypeConstants.MES_PRO_BATCH_STATUS) |
| | |
| | | |
| | | @Schema(description = "ç¼å·", example = "1024") |
| | | private Long id; |
| | | @Schema(description = "æ¹æ¬¡å·ï¼å¯éï¼äººå·¥è¾å
¥ï¼ä¸ä¼ åç³»ç»æã年份+3使µæ°´ãèªå¨çæï¼å¦ 2026001ï¼", example = "2026001") |
| | | private String code; |
| | | @Schema(description = "åç§ç¼å·ï¼ç±ç»å®æçæå¨è¢å¸¦å
¥å¹¶ç»åç«¯æ ¡éª") |
| | | private Long itemId; |
| | | |
| | |
| | | private String scanType; |
| | | @NotNull @DecimalMin("0.000001") private BigDecimal quantity; |
| | | /** |
| | | * 设å¤ç¼ç ï¼ä¸åå¿
å¡«ï¼äº§çº¿æ«ç 以产线+åºåºåºä½å®ä½ï¼ |
| | | * 设å¤ç¼ç ï¼ä¸åå¿
å¡«ï¼äº§çº¿æ«ç 以产线å®ä½ï¼ |
| | | */ |
| | | @Size(max = 64) private String deviceCode; |
| | | private String lineCode; |
| | | @NotNull private Long locationId; |
| | | @NotNull private Long areaId; |
| | | /** åºä½ãåºåºéå¿
å¡«ï¼æ«ç å
¥åºç»ä¸è½å° WIP ç产æåä»ï¼å端ä¸åè¦æ±éæ©åºåºåºä½ */ |
| | | private Long locationId; |
| | | private Long areaId; |
| | | private String remark; |
| | | } |
| | |
| | | private String bagCode; |
| | | private String scanType; |
| | | private String batchCode; |
| | | private Long palletId; |
| | | private String palletCode; |
| | | private BigDecimal quantity; |
| | | private String deviceCode; |
| | | private String lineCode; |
| | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityBatchRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualitySubmitReqVO; |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityBatchApprovalReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityBatchRetestReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityScopeRetestReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedReturnQualitySubmitReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedIndicatorRangeSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedIndicatorRangeRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityActionRespVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualityActionDO; |
| | |
| | | @Resource private MesQcSeedIndicatorRangeService rangeService; |
| | | @Resource private MesProBagCodeMapper bagCodeMapper; |
| | | @Resource private AdminUserApi adminUserApi; |
| | | @GetMapping("/ranges") @Operation(summary = "è·å¾ç§åè´¨æ£åæ ¼èå´") |
| | | @GetMapping("/ranges") @Operation(summary = "è·å¾äº§åè´¨æ£é¡¹é
ç½®") |
| | | @PreAuthorize("@ss.hasAnyPermissions('mes:qc-seed-quality:submit', 'mes:qc-seed-quality:retest', 'mes:qc-seed-range:query')") |
| | | public CommonResult<java.util.List<cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedIndicatorRangeDO>> ranges() { |
| | | return success(rangeService.getRanges()); |
| | | public CommonResult<List<MesQcSeedIndicatorRangeRespVO>> ranges(@RequestParam(required = false) Long itemId, |
| | | @RequestParam(required = false) String keyword, |
| | | @RequestParam(required = false) Integer status) { |
| | | return success(BeanUtils.toBean(rangeService.getRanges(itemId, keyword, status), MesQcSeedIndicatorRangeRespVO.class)); |
| | | } |
| | | |
| | | @PutMapping("/ranges") @Operation(summary = "é
ç½®ç§åè´¨æ£åæ ¼èå´") |
| | | @GetMapping("/ranges/effective") @Operation(summary = "è·å¾äº§åçæçè´¨æ£é¡¹") |
| | | @PreAuthorize("@ss.hasAnyPermissions('mes:qc-seed-quality:submit', 'mes:qc-seed-quality:retest', 'mes:qc-seed-range:query')") |
| | | public CommonResult<List<MesQcSeedIndicatorRangeRespVO>> effectiveRanges(@RequestParam Long itemId) { |
| | | return success(BeanUtils.toBean(rangeService.getEffectiveRanges(itemId), MesQcSeedIndicatorRangeRespVO.class)); |
| | | } |
| | | |
| | | @PostMapping("/ranges") @Operation(summary = "æ°å¢äº§åè´¨æ£é¡¹") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-range:update')") |
| | | public CommonResult<Boolean> saveRanges(@Valid @RequestBody MesQcSeedIndicatorRangeSaveReqVO reqVO) { |
| | | rangeService.saveRanges(reqVO); |
| | | public CommonResult<Long> createRange(@Valid @RequestBody MesQcSeedIndicatorRangeSaveReqVO reqVO) { |
| | | return success(rangeService.create(reqVO)); |
| | | } |
| | | |
| | | @PutMapping("/ranges/{id}") @Operation(summary = "ä¿®æ¹äº§åè´¨æ£é¡¹") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-range:update')") |
| | | public CommonResult<Boolean> updateRange(@PathVariable Long id, @Valid @RequestBody MesQcSeedIndicatorRangeSaveReqVO reqVO) { |
| | | rangeService.update(id, reqVO); |
| | | return success(true); |
| | | } |
| | | |
| | | @DeleteMapping("/ranges/{id}") @Operation(summary = "å é¤äº§åè´¨æ£é¡¹") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-range:update')") |
| | | public CommonResult<Boolean> deleteRange(@PathVariable Long id) { |
| | | rangeService.delete(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @PostMapping("/ranges/copy-default") @Operation(summary = "å¤å¶éç¨é»è®¤è´¨æ£é¡¹å°äº§å") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-range:update')") |
| | | public CommonResult<Integer> copyDefaultRanges(@RequestParam Long itemId) { |
| | | return success(rangeService.copyDefaults(itemId)); |
| | | } |
| | | @GetMapping("/resolve") |
| | | @Operation(summary = "è§£æå¾
è´¨æ£è¢ç å确认该è¢è´¨æ£ç»æ") |
| | | @PreAuthorize("@ss.hasAnyPermissions('mes:qc-seed-quality:submit', 'mes:qc-seed-quality:retest', 'mes:qc-seed-quality:process-approve', 'mes:qc-seed-quality:company-approve')") |
| | | public CommonResult<java.util.Map<String, Object>> resolve(@RequestParam String code, @RequestParam(defaultValue = "BAG") String scanType) { |
| | | return success(service.resolveScan(code, scanType)); |
| | | public CommonResult<java.util.Map<String, Object>> resolve(@RequestParam String code, @RequestParam(defaultValue = "BAG") String scanType, |
| | | @RequestParam(defaultValue = "1") Integer qcLine) { |
| | | return success(service.resolveScan(code, scanType, qcLine)); |
| | | } |
| | | @GetMapping("/page") |
| | | @Operation(summary = "è·å¾ç§åè´¨æ£è®°å½å页") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-quality:query')") |
| | | @PreAuthorize("@ss.hasAnyPermissions('mes:qc-seed-quality:query', 'mes:qc-seed-quality:submit', " |
| | | + "'mes:qc-seed-quality:retest', 'mes:qc-seed-quality:process-approve', 'mes:qc-seed-quality:company-approve')") |
| | | public CommonResult<PageResult<MesQcSeedQualityRespVO>> getQualityPage(@Valid MesQcSeedQualityPageReqVO pageReqVO) { |
| | | PageResult<MesQcSeedQualityRespVO> page = BeanUtils.toBean( |
| | | service.getQualityPage(pageReqVO), MesQcSeedQualityRespVO.class); |
| | |
| | | return success(page); |
| | | } |
| | | |
| | | @GetMapping("/indicators") @Operation(summary = "è·å¾ç§åè´¨æ£ææ°ææ ") |
| | | @GetMapping("/batch-page") |
| | | @Operation(summary = "è·å¾ç§åè´¨æ£è®°å½æçäº§æ¹æ¬¡åç»å页") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-quality:query')") |
| | | public CommonResult<PageResult<MesQcSeedQualityBatchRespVO>> getQualityBatchPage(@Valid MesQcSeedQualityPageReqVO pageReqVO) { |
| | | return success(service.getQualityBatchPage(pageReqVO)); |
| | | } |
| | | |
| | | @GetMapping("/indicators") @Operation(summary = "è·å¾ç§åè´¨æ£ææ°ææ ") |
| | | @PreAuthorize("@ss.hasAnyPermissions('mes:qc-seed-quality:query', 'mes:qc-seed-quality:submit', " |
| | | + "'mes:qc-seed-quality:retest', 'mes:qc-seed-quality:process-approve', 'mes:qc-seed-quality:company-approve')") |
| | | public CommonResult<java.util.List<cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualityIndicatorDO>> indicators(@RequestParam Long id) { |
| | | return success(service.getIndicatorsByQualityId(id)); |
| | | } |
| | | @GetMapping("/actions") @Operation(summary = "è·å¾ç§åè´¨æ£æµç¨çç") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-quality:query')") |
| | | @PreAuthorize("@ss.hasAnyPermissions('mes:qc-seed-quality:query', 'mes:qc-seed-quality:submit', " |
| | | + "'mes:qc-seed-quality:retest', 'mes:qc-seed-quality:process-approve', 'mes:qc-seed-quality:company-approve')") |
| | | public CommonResult<List<MesQcSeedQualityActionRespVO>> actions(@RequestParam Long id) { |
| | | List<MesQcSeedQualityActionDO> actions = service.getActions(id); |
| | | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(actions.stream() |
| | |
| | | return success(service.submitGroup(reqVO)); |
| | | } |
| | | |
| | | @PostMapping("/retry-bpm") @Operation(summary = "éæ°åèµ·è´¨æ£ BPM 审æ¹") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-quality:process-approve')") |
| | | public CommonResult<Boolean> retryBpm(@RequestParam Long id) { |
| | | service.retryBpmProcess(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @PostMapping("/retest") @Operation(summary = "ç§åè´¨æ£å¤æ£") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-quality:retest')") |
| | | public CommonResult<Boolean> retest(@RequestParam Long id, @Valid @RequestBody MesQcSeedQualityRetestReqVO reqVO) { |
| | |
| | | |
| | | @GetMapping("/retest-scope/resolve") @Operation(summary = "ææçç æçäº§æ¹æ¬¡ç è§£æå¾
æ¹éè´¨æ£è®°å½") |
| | | @PreAuthorize("@ss.hasAnyPermissions('mes:qc-seed-quality:submit', 'mes:qc-seed-quality:retest', 'mes:qc-seed-quality:process-approve', 'mes:qc-seed-quality:company-approve')") |
| | | public CommonResult<java.util.Map<String, Object>> resolveRetestScope(@RequestParam String code, @RequestParam String scanType) { |
| | | return success(service.resolveRetestScope(code, scanType)); |
| | | public CommonResult<java.util.Map<String, Object>> resolveRetestScope(@RequestParam String code, @RequestParam String scanType, |
| | | @RequestParam(defaultValue = "1") Integer qcLine) { |
| | | return success(service.resolveRetestScope(code, scanType, qcLine)); |
| | | } |
| | | |
| | | @PostMapping("/retest-scope") @Operation(summary = "ææçç æçäº§æ¹æ¬¡ç æ¹é夿£æéæ°åæ£") |
| | |
| | | service.companyApproveBatch(reqVO.getIds(), reqVO.getApproved(), reqVO.getOpinion()); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/return/resolve") @Operation(summary = "è§£æç»éåéè´§åå¾
è´¨æ£ä¿¡æ¯") |
| | | @PreAuthorize("@ss.hasAnyPermissions('mes:qc-seed-quality:submit', 'mes:qc-seed-quality:retest', 'mes:qc-seed-quality:process-approve')") |
| | | public CommonResult<Map<String, Object>> resolveReturnQuality(@RequestParam Long returnSalesId) { |
| | | return success(service.resolveReturnQuality(returnSalesId)); |
| | | } |
| | | |
| | | @PostMapping("/return/submit") @Operation(summary = "æäº¤ç»éåéè´§è´¨æ£") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-quality:submit')") |
| | | public CommonResult<Long> submitReturnQuality(@Valid @RequestBody MesQcSeedReturnQualitySubmitReqVO reqVO) { |
| | | return success(service.submitReturnQuality(reqVO)); |
| | | } |
| | | |
| | | @PostMapping("/return/approve") @Operation(summary = "ç»éåéè´§è´¨æ£éå®é¨å®¡æ¹") |
| | | @PreAuthorize("@ss.hasPermission('mes:qc-seed-quality:process-approve')") |
| | | public CommonResult<Boolean> returnApprove(@Valid @RequestBody MesQcSeedQualityApprovalReqVO reqVO) { |
| | | service.returnApprove(reqVO.getId(), reqVO.getApproved(), reqVO.getOpinion()); |
| | | return success(true); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | | public class MesQcSeedIndicatorRangeRespVO { |
| | | private Long id; |
| | | private Long itemId; |
| | | private String indicatorCode; |
| | | private String indicatorName; |
| | | private String unit; |
| | | private BigDecimal minValue; |
| | | private BigDecimal maxValue; |
| | | private Integer sort; |
| | | /** ç¶æï¼0 å¯ç¨ 1 åç¨ */ |
| | | private Integer status; |
| | | private LocalDateTime updateTime; |
| | | } |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo; |
| | | |
| | | import jakarta.validation.Valid; |
| | | import jakarta.validation.constraints.DecimalMax; |
| | | import jakarta.validation.constraints.DecimalMin; |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import jakarta.validation.constraints.NotEmpty; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import jakarta.validation.constraints.Pattern; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | public class MesQcSeedIndicatorRangeSaveReqVO { |
| | | @NotEmpty |
| | | private List<@Valid Item> ranges; |
| | | |
| | | @Data |
| | | public static class Item { |
| | | @NotBlank private String indicatorCode; |
| | | @NotNull @DecimalMin("0") @DecimalMax("100") private BigDecimal minValue; |
| | | @NotNull @DecimalMin("0") @DecimalMax("100") private BigDecimal maxValue; |
| | | } |
| | | /** ç»å®äº§åï¼mdm_itemï¼itemType=2ï¼ */ |
| | | @NotNull |
| | | private Long itemId; |
| | | @NotBlank |
| | | @Pattern(regexp = "[A-Za-z][A-Za-z0-9_]{0,63}", message = "ææ ç¼ç å¿
须以忝å¼å¤´ï¼ä»
å¯å
å«åæ¯ãæ°ååä¸å线") |
| | | private String indicatorCode; |
| | | @NotBlank |
| | | private String indicatorName; |
| | | /** åä½ï¼å¦ %ãç²/å
¬æ¤ */ |
| | | private String unit; |
| | | @NotNull |
| | | @DecimalMin("0") |
| | | private BigDecimal minValue; |
| | | @NotNull |
| | | @DecimalMin("0") |
| | | private BigDecimal maxValue; |
| | | private Integer sort; |
| | | /** ç¶æï¼0 å¯ç¨ 1 åç¨ */ |
| | | private Integer status; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Schema(description = "管çåå° - MES ç§åè´¨æ£è®°å½æçäº§æ¹æ¬¡åç» Response VO") |
| | | @Data |
| | | public class MesQcSeedQualityBatchRespVO { |
| | | |
| | | @Schema(description = "çäº§æ¹æ¬¡ç¼å·ï¼æªç»å®æ¹æ¬¡æ¶ä¸ºç©º") |
| | | private Long batchId; |
| | | @Schema(description = "çäº§æ¹æ¬¡å·ï¼æªç»å®æ¶ä¸ºç©ºå符串") |
| | | private String batchCode; |
| | | @Schema(description = "æ¹æ¬¡å±ç¤ºåç§°ï¼æªç»å®æ¶ä¸ºãæªç»å®æ¹æ¬¡ã") |
| | | private String batchLabel; |
| | | @Schema(description = "åç§ç¼å·") |
| | | private Long itemId; |
| | | @Schema(description = "åç§ç¼ç ") |
| | | private String itemCode; |
| | | @Schema(description = "åç§åç§°") |
| | | private String itemName; |
| | | @Schema(description = "å å·¥åä½ï¼äº§çº¿åç§°ï¼") |
| | | private String lineName; |
| | | @Schema(description = "çäº§æ¥æ") |
| | | private LocalDateTime produceDate; |
| | | |
| | | @Schema(description = "ç»å
è´¨æ£è®°å½æ»æ°") |
| | | private Integer totalCount; |
| | | @Schema(description = "åæ ¼æ°é") |
| | | private Integer passCount; |
| | | @Schema(description = "ä¸åæ ¼æ°é") |
| | | private Integer failCount; |
| | | @Schema(description = "å¤ç䏿°é") |
| | | private Integer processingCount; |
| | | @Schema(description = "å¾
夿£/鿰忣æ°é") |
| | | private Integer pendingRetestCount; |
| | | @Schema(description = "å¾
åæ£å®¡æ ¸æ°é") |
| | | private Integer pendingProcessReviewCount; |
| | | @Schema(description = "å¾
夿£å®¡æ ¸æ°é") |
| | | private Integer pendingCompanyReviewCount; |
| | | @Schema(description = "å·²æ¾è¡æ°é") |
| | | private Integer releasedCount; |
| | | @Schema(description = "å·²æ¥åºæ°é") |
| | | private Integer scrappedCount; |
| | | @Schema(description = "ç»å
æè¿æ£éªæ¶é´") |
| | | private LocalDateTime lastInspectedAt; |
| | | } |
| | |
| | | @Schema(description = "è´¨æ£è¢ç ") |
| | | private String code; |
| | | |
| | | @Schema(description = "çäº§æ¹æ¬¡") |
| | | @Schema(description = "çäº§æ¹æ¬¡ï¼æ¨¡ç³å¹é
ï¼") |
| | | private String batchCode; |
| | | |
| | | @Schema(description = "çäº§æ¹æ¬¡ï¼ç²¾ç¡®å¹é
ï¼ææ¹æ¬¡å±å¼æç»æ¶ä½¿ç¨ï¼") |
| | | private String exactBatchCode; |
| | | |
| | | @Schema(description = "æ¯å¦ä»
æ¥è¯¢æªç»å®çäº§æ¹æ¬¡çè®°å½ï¼æãæªç»å®æ¹æ¬¡ãå±å¼æç»æ¶ä½¿ç¨ï¼") |
| | | private Boolean unbound; |
| | | |
| | | @Schema(description = "æ£éªç»æï¼1åæ ¼ 2ä¸åæ ¼") |
| | | private Integer result; |
| | | |
| | | @Schema(description = "æµç¨ç¶æï¼10å¾
夿£ 20/50å¾
å å·¥é¨ 30/60å¾
å
¬å¸è´¨æ£ 40å·²æ¾è¡ 70å·²æ¥åº") |
| | | private Integer workflowStatus; |
| | | |
| | | @Schema(description = "è´¨æ£çº¿ï¼1å å·¥é¨ 2éå®é¨ 0æªåºåï¼å岿°æ®ï¼") |
| | | private Integer qcLine; |
| | | |
| | | @Schema(description = "ä¸å¡ç±»åï¼1ç产å
¥åºè´¨æ£ 2ç»éåéè´§è´¨æ£") |
| | | private Integer bizType; |
| | | } |
| | |
| | | |
| | | private Long id; |
| | | private String code; |
| | | @Schema(description = "è´¨æ£çº¿ï¼1å å·¥é¨ 2éå®é¨ 0æªåºåï¼å岿°æ®ï¼") |
| | | private Integer qcLine; |
| | | @Schema(description = "ä¸å¡ç±»åï¼1ç产å
¥åºè´¨æ£ 2ç»éåéè´§è´¨æ£") |
| | | private Integer bizType; |
| | | @Schema(description = "å¯¹åºæçç ï¼æªç»å®æçæ¶ä¸ºç©º") |
| | | private String palletCode; |
| | | private Long itemId; |
| | |
| | | private String batchCode; |
| | | private Integer result; |
| | | private Integer workflowStatus; |
| | | private String processInstanceId; |
| | | private String companyProcessInstanceId; |
| | | private Long returnSalesId; |
| | | private String returnSalesCode; |
| | | private Integer retestResult; |
| | | private LocalDateTime retestedAt; |
| | | private LocalDateTime processApprovedAt; |
| | | private LocalDateTime lineFinishedAt; |
| | | private LocalDateTime companySubmittedAt; |
| | | private LocalDateTime companyApprovedAt; |
| | | private String reason; |
| | | private Long inspectorId; |
| | |
| | | public class MesQcSeedQualityScopeRetestReqVO { |
| | | @NotBlank private String code; |
| | | @NotBlank private String scanType; |
| | | @NotNull private Integer qcLine; |
| | | @NotNull private Integer workflowStatus; |
| | | @NotEmpty private List<@NotNull Long> qualityIds; |
| | | @NotEmpty @Valid private List<MesQcSeedQualitySubmitReqVO.Indicator> indicators; |
| | |
| | | @Data |
| | | public class MesQcSeedQualitySubmitReqVO { |
| | | @NotBlank private String code; |
| | | /** å
¼å®¹å段ï¼ä¸æ¬¡æäº¤ä¼åæ¶çæå å·¥é¨ä¸éå®é¨ä¸¤æ¡è®°å½ï¼å端ä¸åæè¯¥å段æåã */ |
| | | private Integer qcLine; |
| | | private String batchCode; |
| | | @NotEmpty @Valid private List<Indicator> indicators; |
| | | private String reason; |
| | |
| | | @NotNull private Long qualifiedWarehouseId; |
| | | @Data public static class Indicator { |
| | | @NotBlank private String code; |
| | | @NotNull @DecimalMin("0") @DecimalMax("100") private BigDecimal value; |
| | | @NotNull @DecimalMin("0") @DecimalMax("100") private BigDecimal minValue; |
| | | @NotNull @DecimalMin("0") @DecimalMax("100") private BigDecimal maxValue; |
| | | @NotNull @DecimalMin("0") private BigDecimal value; |
| | | @NotNull @DecimalMin("0") private BigDecimal minValue; |
| | | @NotNull @DecimalMin("0") private BigDecimal maxValue; |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo; |
| | | |
| | | import jakarta.validation.Valid; |
| | | import jakarta.validation.constraints.NotEmpty; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * 管çåå° - ç»éåéè´§ç§åè´¨æ£æäº¤ Request VO |
| | | */ |
| | | @Data |
| | | public class MesQcSeedReturnQualitySubmitReqVO { |
| | | |
| | | @NotNull |
| | | private Long returnSalesId; |
| | | |
| | | @NotEmpty |
| | | @Valid |
| | | private List<MesQcSeedQualitySubmitReqVO.Indicator> indicators; |
| | | |
| | | /** ä¸åæ ¼åå */ |
| | | private String reason; |
| | | } |
| | |
| | | |
| | | @Schema(description = "éå®åºåºåå·", example = "SO20250801001") |
| | | private String salesCode; |
| | | private Long shipmentId; |
| | | |
| | | @Schema(description = "åºåºæ¥æ") |
| | | private LocalDateTime salesDate; |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.materialstock.vo.MesWmStockSummaryRespVO; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO; |
| | | import cn.iocoder.yudao.module.mdm.api.category.MdmItemCategoryApi; |
| | | import cn.iocoder.yudao.module.mdm.api.category.dto.MdmItemCategoryRespDTO; |
| | | import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | |
| | |
| | | public class MesWmMaterialStockController { |
| | | |
| | | @Resource |
| | | private cn.iocoder.yudao.module.mes.dal.mysql.wm.transaction.MesWmTransactionMapper transactionMapper; |
| | | |
| | | @Resource |
| | | private MesWmMaterialStockService materialStockService; |
| | | @Resource |
| | | private MdmItemApi mdmItemApi; |
| | | @Resource |
| | | private MdmItemCategoryApi mdmItemCategoryApi; |
| | | @Resource |
| | | private MdmUnitMeasureApi mdmUnitMeasureApi; |
| | | @Resource |
| | |
| | | private SrmSupplierApi srmSupplierApi; |
| | | @Resource |
| | | private MesWmMaterialStockBagMapper materialStockBagMapper; |
| | | @Resource |
| | | private cn.iocoder.yudao.module.mes.service.wm.productsales.MesWmProductSalesLineService productSalesLineService; |
| | | @Resource private cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper salesDetailMapper; |
| | | @Resource private cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesMapper salesMapper; |
| | | @Resource private cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesShipmentMapper shipmentMapper; |
| | | |
| | | @GetMapping("/picking-list") |
| | | @Operation(summary = "æéå®åºåºåè¡æ¥è¯¢åæ ¼åºå") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:stock') and @ss.hasPermission('mes:wm-material-stock:query')") |
| | | public CommonResult<List<MesWmMaterialStockRespVO>> getPickingStocks(@RequestParam("lineId") Long lineId) { |
| | | var line = productSalesLineService.getProductSalesLine(lineId); |
| | | if (line == null) throw new IllegalArgumentException("åºåºåè¡ä¸åå¨"); |
| | | return success(buildRespVOList(materialStockService.getQualifiedMaterialStockListByItemAndBatch(line.getItemId(), line.getBatchId()))); |
| | | } |
| | | |
| | | @GetMapping("/get") |
| | | @Operation(summary = "è·å¾åºåè®°å½") |
| | |
| | | PageResult<MesWmMaterialStockBagRespVO> result = new PageResult<>(); |
| | | PageResult<cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO> page = |
| | | materialStockBagMapper.selectPage(pageReqVO); |
| | | return success(new PageResult<>(BeanUtils.toBean(page.getList(), MesWmMaterialStockBagRespVO.class), page.getTotal())); |
| | | var transactionIds = convertSet(page.getList(), cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO::getLastTransactionId); |
| | | Map<Long, cn.iocoder.yudao.module.mes.dal.dataobject.wm.transaction.MesWmTransactionDO> transactions = transactionIds.isEmpty() |
| | | ? Collections.emptyMap() : transactionMapper.selectBatchIds(transactionIds).stream() |
| | | .collect(Collectors.toMap(cn.iocoder.yudao.module.mes.dal.dataobject.wm.transaction.MesWmTransactionDO::getId, tx -> tx)); |
| | | var rows = BeanUtils.toBean(page.getList(), MesWmMaterialStockBagRespVO.class); |
| | | var bagIds = convertSet(page.getList(), cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstockbag.MesWmMaterialStockBagDO::getBagCodeId); |
| | | var pickingDetails = salesDetailMapper.selectByBagIds(bagIds); |
| | | var saleIds = convertSet(pickingDetails, cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO::getSalesId); |
| | | var shipmentIds = convertSet(pickingDetails, cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO::getShipmentId); |
| | | var saleMap = saleIds.isEmpty() ? Collections.<Long, cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO>emptyMap() |
| | | : salesMapper.selectBatchIds(saleIds).stream().collect(Collectors.toMap(cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO::getId, s -> s)); |
| | | var shipmentMap = shipmentIds.isEmpty() ? Collections.<Long, cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO>emptyMap() |
| | | : shipmentMapper.selectBatchIds(shipmentIds).stream().collect(Collectors.toMap(cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO::getId, s -> s)); |
| | | Map<Long, String> pickingByBag = new java.util.HashMap<>(); |
| | | for (var detail : pickingDetails) { |
| | | var sale = saleMap.get(detail.getSalesId()); |
| | | var shipment = detail.getShipmentId() == null ? null : shipmentMap.get(detail.getShipmentId()); |
| | | if (sale == null) continue; |
| | | if (detail.getShipmentId() == null && Integer.valueOf(2).equals(sale.getStatus())) pickingByBag.putIfAbsent(detail.getBagCodeId(), "PICKED"); |
| | | else if ((detail.getShipmentId() == null && (Integer.valueOf(10).equals(sale.getStatus()) || Integer.valueOf(3).equals(sale.getStatus()))) |
| | | || (shipment != null && (Integer.valueOf(10).equals(shipment.getStatus()) || Integer.valueOf(20).equals(shipment.getStatus())))) pickingByBag.put(detail.getBagCodeId(), "RESERVED"); |
| | | } |
| | | for (int i = 0; i < rows.size(); i++) { |
| | | var bag = page.getList().get(i); |
| | | var tx = bag.getLastTransactionId() == null ? null : transactions.get(bag.getLastTransactionId()); |
| | | if (Integer.valueOf(2).equals(bag.getStatus()) && tx != null && Integer.valueOf(2).equals(tx.getType())) { |
| | | rows.get(i).setOutboundBizType(tx.getBizType()); |
| | | } |
| | | if (Integer.valueOf(1).equals(bag.getStatus())) rows.get(i).setPickingStatus(pickingByBag.get(bag.getBagCodeId())); |
| | | } |
| | | return success(new PageResult<>(rows, page.getTotal())); |
| | | } |
| | | |
| | | @PutMapping("/update-frozen") |
| | |
| | | // 1. è·å¾å
³èæ°æ® |
| | | Map<Long, MdmItemRespDTO> itemMap = mdmItemApi.getItemMap( |
| | | convertSet(list, MesWmMaterialStockDO::getItemId)); |
| | | Map<Long, MdmItemCategoryRespDTO> itemCategoryMap = mdmItemCategoryApi.getItemCategoryList( |
| | | convertSet(itemMap.values(), MdmItemRespDTO::getCategoryId)).getCheckedData().stream() |
| | | .collect(Collectors.toMap(MdmItemCategoryRespDTO::getId, c -> c)); |
| | | Map<Long, MdmUnitMeasureRespDTO> unitMeasureMap = mdmUnitMeasureApi.getUnitMeasureList( |
| | | convertSet(itemMap.values(), MdmItemRespDTO::getUnitMeasureId)).getCheckedData() |
| | | .stream().collect(Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e)); |
| | |
| | | convertSet(list, MesWmMaterialStockDO::getLocationId)); |
| | | Map<Long, MesWmWarehouseAreaDO> areaMap = areaService.getWarehouseAreaMap( |
| | | convertSet(list, MesWmMaterialStockDO::getAreaId)); |
| | | Map<Long, Long> bagCountMap = materialStockBagMapper.selectListByMaterialStockIds( |
| | | convertSet(list, MesWmMaterialStockDO::getId)).stream() |
| | | List<MesWmMaterialStockBagDO> bagHistory = materialStockBagMapper.selectHistoryByMaterialStockIds( |
| | | convertSet(list, MesWmMaterialStockDO::getId)); |
| | | Map<Long, Long> bagCountMap = bagHistory.stream().filter(bag -> Integer.valueOf(1).equals(bag.getStatus())) |
| | | .collect(Collectors.groupingBy(MesWmMaterialStockBagDO::getMaterialStockId, Collectors.counting())); |
| | | var stockIdsWithBagHistory = convertSet(bagHistory, MesWmMaterialStockBagDO::getMaterialStockId); |
| | | Map<Long, SrmSupplierRespDTO> vendorMap = srmSupplierApi.getSupplierList( |
| | | convertSet(list, MesWmMaterialStockDO::getVendorId)).getCheckedData().stream() |
| | | .collect(Collectors.toMap(SrmSupplierRespDTO::getId, s -> s)); |
| | |
| | | return BeanUtils.toBean(list, MesWmMaterialStockRespVO.class, vo -> { |
| | | MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { |
| | | vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()); |
| | | MapUtils.findAndThen(itemCategoryMap, item.getCategoryId(), |
| | | category -> vo.setItemTypeName(category.getName())); |
| | | MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | | unitMeasure -> vo.setUnitMeasureName(unitMeasure.getName())); |
| | | }); |
| | |
| | | MapUtils.findAndThen(vendorMap, vo.getVendorId(), |
| | | vendor -> vo.setVendorName(vendor.getName())); |
| | | vo.setBagCount(bagCountMap.getOrDefault(vo.getId(), 0L)); |
| | | vo.setBagDetailSupported(bagCountMap.containsKey(vo.getId())); |
| | | vo.setBagDetailSupported(stockIdsWithBagHistory.contains(vo.getId())); |
| | | // 计ç®å¯ç¨é = å¨åºæ°é - å»ç»æ°é - å ç¨é |
| | | BigDecimal quantity = vo.getQuantity() != null ? vo.getQuantity() : BigDecimal.ZERO; |
| | | BigDecimal frozenQty = vo.getFrozenQuantity() != null ? vo.getFrozenQuantity() : BigDecimal.ZERO; |
| | |
| | | @NotNull(message = "ä»åºIDä¸è½ä¸ºç©º") |
| | | private Long warehouseId; |
| | | |
| | | @Schema(description = "åºåºID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºåºIDä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºåºID", example = "1") |
| | | private Long locationId; |
| | | |
| | | @Schema(description = "åºä½ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºä½IDä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºä½ID", example = "1") |
| | | private Long areaId; |
| | | |
| | | // ========== æ¹æ¬¡ç¸å
³å段 ========== |
| | |
| | | |
| | | @Schema(description = "è¢ç ") |
| | | private String bagCode; |
| | | |
| | | @Schema(description = "å
å«å·²åºåºåå·²æ¥åºè¢ç ") |
| | | private Boolean includeHistory; |
| | | } |
| | |
| | | private Boolean frozen; |
| | | private LocalDateTime inboundTime; |
| | | private LocalDateTime outboundTime; |
| | | @Schema(description = "æè¿ä¸æ¬¡åºåºçä¸å¡ç±»åï¼ä»
å¨è¢ç å·²åºåºä¸åå¨å
³èæµæ°´æ¶è¿å") |
| | | private Integer outboundBizType; |
| | | @Schema(description = "é宿£è´§ç¶æï¼PICKEDå·²æ£éãRESERVEDå·²å ç¨") |
| | | private String pickingStatus; |
| | | } |
| | |
| | | @Schema(description = "æ¯å¦å»ç»", example = "false") |
| | | private Boolean frozen; |
| | | |
| | | @Schema(description = "å
å«é¶æ°éåºåè®°å½ï¼ç¨äºæ¥è¯¢åå²") |
| | | private Boolean includeZero; |
| | | |
| | | } |
| | |
| | | @ExcelProperty("è§æ ¼åå·") |
| | | private String specification; |
| | | |
| | | @Schema(description = "ç©æç±»å«åç§°", example = "äºéé
ä»¶") |
| | | @ExcelProperty("ç±»å«") |
| | | private String itemTypeName; |
| | | |
| | | @Schema(description = "计éåä½åç§°", example = "åå
") |
| | | @ExcelProperty("计éåä½") |
| | | private String unitMeasureName; |
| | |
| | | @NotNull(message = "ä»åºä¸è½ä¸ºç©º") |
| | | private Long warehouseId; |
| | | |
| | | @Schema(description = "åºåºç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºåºä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºåºç¼å·", example = "1") |
| | | private Long locationId; |
| | | |
| | | @Schema(description = "åºä½ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºä½ä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºä½ç¼å·", example = "1") |
| | | private Long areaId; |
| | | |
| | | @Schema(description = "夿³¨", example = "夿³¨") |
| | |
| | | @NotNull(message = "ä»åºä¸è½ä¸ºç©º") |
| | | private Long warehouseId; |
| | | |
| | | @Schema(description = "åºåºç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºåºä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºåºç¼å·", example = "1") |
| | | private Long locationId; |
| | | |
| | | @Schema(description = "åºä½ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºä½ä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºä½ç¼å·", example = "1") |
| | | private Long areaId; |
| | | |
| | | @Schema(description = "夿³¨", example = "夿³¨") |
| | |
| | | @RequestMapping("/mes/wm/product-sales") |
| | | @Validated |
| | | public class MesWmProductSalesController { |
| | | @Resource private cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesShipmentMapper shipmentMapper; |
| | | |
| | | @Resource |
| | | private MesWmProductSalesService productSalesService; |
| | |
| | | return success(productSalesService.checkProductSalesQuantity(id)); |
| | | } |
| | | |
| | | @GetMapping("/shipments") |
| | | @Operation(summary = "æ¥è¯¢åºåºååè´§è®°å½") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:query')") |
| | | public CommonResult<List<cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO>> getShipments(@RequestParam("id") Long id) { |
| | | return success(productSalesService.getShipments(id)); |
| | | } |
| | | |
| | | @PostMapping("/scan") |
| | | @Operation(summary = "æ«ç æ£è´§") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:stock')") |
| | |
| | | return success(true); |
| | | } |
| | | |
| | | @PutMapping("/close-remaining") |
| | | @Operation(summary = "å
³éé¨ååè´§åçå©ä½æ°é") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:cancel')") |
| | | public CommonResult<Boolean> closeRemaining(@RequestParam("id") Long id, @RequestParam("reason") String reason) { |
| | | productSalesService.closeRemaining(id, reason); |
| | | return success(true); |
| | | } |
| | | |
| | | @PutMapping("/cancel-shipment") |
| | | @Operation(summary = "åæ¶æ¬æ¬¡æªåºåºåè´§å¹¶éæ¾å ç¨") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:cancel')") |
| | | public CommonResult<Boolean> cancelShipment(@RequestParam("id") Long id) { |
| | | productSalesService.cancelShipment(id); |
| | | return success(true); |
| | | } |
| | | |
| | | // ==================== æ¼æ¥ VO ==================== |
| | | |
| | | private List<MesWmProductSalesRespVO> buildRespVOList(List<MesWmProductSalesDO> list) { |
| | |
| | | convertSet(list, MesWmProductSalesDO::getNoticeId)); |
| | | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( |
| | | convertSet(list, MesWmProductSalesDO::getOutboundUserId)); |
| | | var partiallyShippedIds = convertSet(shipmentMapper.selectCompletedBySalesIds(convertSet(list, MesWmProductSalesDO::getId)), |
| | | cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO::getSalesId); |
| | | // 2. æå»ºç»æ |
| | | return BeanUtils.toBean(list, MesWmProductSalesRespVO.class, vo -> { |
| | | vo.setPartiallyShipped(partiallyShippedIds.contains(vo.getId()) && (Integer.valueOf(2).equals(vo.getStatus()) |
| | | || Integer.valueOf(10).equals(vo.getStatus()) || Integer.valueOf(3).equals(vo.getStatus()))); |
| | | MapUtils.findAndThen(customerMap, vo.getClientId(), customer -> |
| | | vo.setClientName(customer.getName())); |
| | | if (vo.getClientName() == null) { |
| | |
| | | return success(productSalesDetailService.createProductSalesDetail(createReqVO)); |
| | | } |
| | | |
| | | @PostMapping("/create-mobile-manual") |
| | | @Operation(summary = "ç§»å¨ç«¯ææ°éæ£éæ è¢ç åå²åºå") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:stock')") |
| | | public CommonResult<Long> createMobileManual(@Valid @RequestBody MesWmProductSalesDetailSaveReqVO reqVO) { |
| | | if (reqVO.getRemark() == null || !reqVO.getRemark().matches("ç§»å¨ç«¯æ è¢ç æ£è´§ #[a-f0-9-]{36}")) |
| | | throw new IllegalArgumentException("ç¼ºå°ææçæ£è´§è¯·æ±æ è¯"); |
| | | return success(productSalesDetailService.createProductSalesDetail(reqVO)); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "ä¿®æ¹éå®åºåºæç»") |
| | | @PreAuthorize("@ss.hasPermission('mes:wm-product-sales:update')") |
| | |
| | | @RequestMapping("/mes/wm/product-sales-line") |
| | | @Validated |
| | | public class MesWmProductSalesLineController { |
| | | @Resource private cn.iocoder.yudao.module.mes.dal.mysql.wm.transaction.MesWmTransactionMapper transactionMapper; |
| | | |
| | | @Resource |
| | | private MesWmProductSalesLineService productSalesLineService; |
| | |
| | | .stream().collect(Collectors.toMap(MdmUnitMeasureRespDTO::getId, e -> e)); |
| | | // 2. æå»ºç»æ |
| | | return BeanUtils.toBean(list, MesWmProductSalesLineRespVO.class, vo -> { |
| | | var shipped = transactionMapper.sumSalesOutboundByLineId(vo.getId()); |
| | | vo.setShippedQuantity(shipped == null ? java.math.BigDecimal.ZERO : shipped); |
| | | vo.setRemainingQuantity((vo.getQuantity() == null ? java.math.BigDecimal.ZERO : vo.getQuantity()).subtract(vo.getShippedQuantity()).max(java.math.BigDecimal.ZERO)); |
| | | MapUtils.findAndThen(itemMap, vo.getItemId(), item -> { |
| | | vo.setItemCode(item.getCode()).setItemName(item.getName()).setSpecification(item.getSpecification()); |
| | | MapUtils.findAndThen(unitMeasureMap, item.getUnitMeasureId(), |
| | |
| | | @Schema(description = "ç¶æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | @ExcelProperty("ç¶æ") |
| | | private Integer status; |
| | | private String closeReason; |
| | | @Schema(description = "æ¯å¦å·²é¨ååè´§ä¸ä»æåç»å¤ç") |
| | | private Boolean partiallyShipped; |
| | | |
| | | @Schema(description = "夿³¨", example = "夿³¨") |
| | | @ExcelProperty("夿³¨") |
| | |
| | | @ExcelProperty("å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | } |
| | | } |
| | |
| | | @Schema(description = "æ¬æ¬¡æç¡®éä¸çè¢ç IDï¼æçåæ¹æ¬¡å¿
须鿩") |
| | | private List<@NotNull Long> bagIds; |
| | | |
| | | @Schema(description = "æå·¥éæ©æ¶æå®åºåºåè¡") |
| | | private Long lineId; |
| | | |
| | | @Schema(description = "æå·¥éæ©æ¶æå®åºåè®°å½") |
| | | private Long materialStockId; |
| | | |
| | | @Schema(description = "æ¯å¦ä¸ºæå·¥éæ©è¢ç ") |
| | | private Boolean manual; |
| | | |
| | | } |
| | |
| | | private String code; |
| | | private String businessCode; |
| | | private Long palletId; |
| | | private Long materialStockId; |
| | | private Boolean selectable; |
| | | private String unavailableReason; |
| | | } |
| | |
| | | @Schema(description = "åºåºåID", example = "1") |
| | | private Long salesId; |
| | | |
| | | @Schema(description = "åè´§è®°å½IDï¼ä¸ºç©ºè¡¨ç¤ºæ¬æ¬¡å¾
æ£æå岿ç»") |
| | | private Long shipmentId; |
| | | |
| | | @Schema(description = "ç©æID", example = "1") |
| | | private Long itemId; |
| | | |
| | |
| | | |
| | | @Schema(description = "åºåºæ°é", example = "100") |
| | | private BigDecimal quantity; |
| | | @Schema(description = "å®é
å·²åºåºæ°é") |
| | | private BigDecimal shippedQuantity; |
| | | @Schema(description = "å©ä½æªåºåºæ°é") |
| | | private BigDecimal remainingQuantity; |
| | | |
| | | @Schema(description = "æ¹æ¬¡ID", example = "1") |
| | | private Long batchId; |
| | |
| | | @NotNull(message = "ä»åºç¼å·ä¸è½ä¸ºç©º") |
| | | private Long warehouseId; |
| | | |
| | | @Schema(description = "åºåºç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºåºç¼å·ä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºåºç¼å·", example = "1") |
| | | private Long locationId; |
| | | |
| | | @Schema(description = "åºä½ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºä½ç¼å·ä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºä½ç¼å·", example = "1") |
| | | private Long areaId; |
| | | |
| | | @Schema(description = "夿³¨", example = "夿³¨") |
| | |
| | | @NotNull(message = "ä»åºIDä¸è½ä¸ºç©º") |
| | | private Long warehouseId; |
| | | |
| | | @Schema(description = "åºåºID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºåºIDä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºåºID", example = "1") |
| | | private Long locationId; |
| | | |
| | | @Schema(description = "åºä½ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºä½IDä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºä½ID", example = "1") |
| | | private Long areaId; |
| | | |
| | | @Schema(description = "夿³¨", example = "夿³¨") |
| | |
| | | @NotNull(message = "ä»åº ID ä¸è½ä¸ºç©º") |
| | | private Long warehouseId; |
| | | |
| | | @Schema(description = "åºåº ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºåº ID ä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºåº ID", example = "1") |
| | | private Long locationId; |
| | | |
| | | @Schema(description = "åºä½ ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åºä½ ID ä¸è½ä¸ºç©º") |
| | | @Schema(description = "åºä½ ID", example = "1") |
| | | private Long areaId; |
| | | |
| | | @Schema(description = "çç¹æ°é", requiredMode = Schema.RequiredMode.REQUIRED, example = "100.000000") |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.wm.transaction; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.transaction.vo.MesWmTransactionPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.transaction.vo.MesWmTransactionRespVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.item.MesMdItemTypeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.transaction.MesWmTransactionDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.md.item.MesMdItemMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; |
| | | import cn.iocoder.yudao.module.mes.enums.MesBizTypeConstants; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmTransactionTypeEnum; |
| | | import cn.iocoder.yudao.module.mes.service.md.item.MesMdItemTypeService; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; |
| | | import cn.iocoder.yudao.module.mdm.api.category.MdmItemCategoryApi; |
| | | import cn.iocoder.yudao.module.mdm.api.category.dto.MdmItemCategoryRespDTO; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | |
| | | @Resource |
| | | private MesMdmItemService mdmItemService; |
| | | @Resource |
| | | private MdmItemCategoryApi mdmItemCategoryApi; |
| | | @Resource |
| | | private MesWmWarehouseService warehouseService; |
| | | @Resource |
| | | private MesWmWarehouseLocationService locationService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService areaService; |
| | | @Resource private MesProBagCodeMapper bagCodeMapper; |
| | | @Resource private MesProPalletMapper palletMapper; |
| | | @Resource private MesWmProductSalesDetailMapper salesDetailMapper; |
| | | @Resource private MesMdItemMapper mesMdItemMapper; |
| | | @Resource private MesMdItemTypeService mesMdItemTypeService; |
| | | |
| | | @GetMapping("/page") |
| | | @Operation(summary = "è·ååºåäºå¡æµæ°´å页") |
| | |
| | | // å
³èæ¥è¯¢ç©æä¿¡æ¯ |
| | | Map<Long, MesMdmItemDO> itemMap = mdmItemService.getItemMap( |
| | | convertSet(pageResult.getList(), MesWmTransactionDO::getItemId)); |
| | | // å
³èæ¥è¯¢ç©æç±»å«ä¿¡æ¯ |
| | | Map<Long, MdmItemCategoryRespDTO> itemCategoryMap = mdmItemCategoryApi.getItemCategoryList( |
| | | convertSet(itemMap.values(), MesMdmItemDO::getCategoryId)).getCheckedData().stream() |
| | | .collect(Collectors.toMap(MdmItemCategoryRespDTO::getId, c -> c)); |
| | | // å
³èæ¥è¯¢ MES ç©æï¼åç§åæ¥çæï¼ï¼åç§æªç»´æ¤è§æ ¼/åç±»æ¶åéä½¿ç¨ |
| | | Set<Long> mdmItemIds = convertSet(itemMap.values(), MesMdmItemDO::getId); |
| | | Map<Long, MesMdItemDO> mesItemMap = mdmItemIds.isEmpty() ? Map.of() |
| | | : mesMdItemMapper.selectList(MesMdItemDO::getMdmItemId, mdmItemIds).stream() |
| | | .collect(Collectors.toMap(MesMdItemDO::getMdmItemId, i -> i, (a, b) -> a)); |
| | | Set<Long> mesItemTypeIds = convertSet(mesItemMap.values(), MesMdItemDO::getItemTypeId); |
| | | mesItemTypeIds.remove(null); |
| | | Map<Long, MesMdItemTypeDO> mesItemTypeMap = mesItemTypeIds.isEmpty() ? Map.of() |
| | | : mesMdItemTypeService.getItemTypeMap(mesItemTypeIds); |
| | | // å
³èæ¥è¯¢ä»åºä¿¡æ¯ |
| | | Map<Long, MesWmWarehouseDO> warehouseMap = warehouseService.getWarehouseMap( |
| | | convertSet(pageResult.getList(), MesWmTransactionDO::getWarehouseId)); |
| | |
| | | Map<Long, MesWmWarehouseAreaDO> areaMap = areaService.getWarehouseAreaMap( |
| | | convertSet(pageResult.getList(), MesWmTransactionDO::getAreaId)); |
| | | |
| | | var bagIds = convertSet(pageResult.getList(), MesWmTransactionDO::getBagCodeId); |
| | | bagIds.remove(null); |
| | | Map<Long, MesProBagCodeDO> bagMap = bagIds.isEmpty() ? Map.of() : bagCodeMapper.selectBatchIds(bagIds) |
| | | .stream().collect(Collectors.toMap(MesProBagCodeDO::getId, b -> b)); |
| | | var salesIds = pageResult.getList().stream().filter(t -> Integer.valueOf(MesBizTypeConstants.WM_PRODUCT_SALES).equals(t.getBizType())) |
| | | .map(MesWmTransactionDO::getBizId).filter(java.util.Objects::nonNull).collect(Collectors.toSet()); |
| | | var details = salesDetailMapper.selectBySalesIdsAndBagIds(salesIds, bagIds); |
| | | var palletIds = convertSet(details, MesWmProductSalesDetailDO::getPalletCodeId); |
| | | palletIds.remove(null); |
| | | Map<Long, MesProPalletDO> pallets = palletIds.isEmpty() ? Map.of() : palletMapper.selectBatchIds(palletIds) |
| | | .stream().collect(Collectors.toMap(MesProPalletDO::getId, p -> p)); |
| | | Map<String, MesWmProductSalesDetailDO> detailMap = details.stream().collect(Collectors.toMap( |
| | | d -> d.getSalesId() + ":" + d.getBagCodeId() + ":" + d.getShipmentId(), d -> d, (a, b) -> a)); |
| | | |
| | | // 转æ¢ä¸º VO |
| | | List<MesWmTransactionRespVO> voList = pageResult.getList().stream().map(transaction -> { |
| | | MesWmTransactionRespVO vo = BeanUtils.toBean(transaction, MesWmTransactionRespVO.class); |
| | | MesProBagCodeDO bag = transaction.getBagCodeId() == null ? null : bagMap.get(transaction.getBagCodeId()); |
| | | if ((vo.getBagCode() == null || vo.getBagCode().isBlank()) && bag != null) vo.setBagCode(bag.getCode()); |
| | | if (Integer.valueOf(MesBizTypeConstants.WM_PRODUCT_SALES).equals(transaction.getBizType())) { |
| | | MesWmProductSalesDetailDO detail = detailMap.get(transaction.getBizId() + ":" + transaction.getBagCodeId() + ":" + transaction.getShipmentId()); |
| | | MesProPalletDO pallet = detail == null || detail.getPalletCodeId() == null ? null : pallets.get(detail.getPalletCodeId()); |
| | | if (pallet != null) vo.setPalletCode(pallet.getCode()); |
| | | } |
| | | // è´¨æ£ãç产å
¥åºçæµæ°´æ²¡æé宿ç»ï¼æçç åéåè¢ç å½åç»å®çæç |
| | | if (StrUtil.isBlank(vo.getPalletCode()) && bag != null) { |
| | | vo.setPalletCode(bag.getPalletCode()); |
| | | } |
| | | // äºå¡ç±»ååç§° |
| | | MesWmTransactionTypeEnum typeEnum = MesWmTransactionTypeEnum.valueOf(transaction.getType()); |
| | | vo.setTypeName(typeEnum != null ? typeEnum.getName() : "æªç¥"); |
| | |
| | | // ç©æä¿¡æ¯ |
| | | MesMdmItemDO item = itemMap.get(transaction.getItemId()); |
| | | if (item != null) { |
| | | MesMdItemDO mesItem = mesItemMap.get(item.getId()); |
| | | vo.setItemCode(item.getCode()); |
| | | vo.setItemName(item.getName()); |
| | | // è§æ ¼ï¼åç§ç®¡çæªç»´æ¤æ¶ï¼åéå MES ç©æè§æ ¼åå· |
| | | vo.setSpecification(StrUtil.blankToDefault(item.getSpecification(), |
| | | mesItem != null ? mesItem.getSpecification() : null)); |
| | | // ç±»å«ï¼ä¼å
åç§åç±»ï¼å
¶æ¬¡ MES ç©æåç±» |
| | | MdmItemCategoryRespDTO category = itemCategoryMap.get(item.getCategoryId()); |
| | | String itemTypeName = category != null ? category.getName() : null; |
| | | if (StrUtil.isBlank(itemTypeName) && mesItem != null && mesItem.getItemTypeId() != null) { |
| | | MesMdItemTypeDO itemType = mesItemTypeMap.get(mesItem.getItemTypeId()); |
| | | if (itemType != null) { |
| | | itemTypeName = itemType.getName(); |
| | | } |
| | | } |
| | | vo.setItemTypeName(itemTypeName); |
| | | } |
| | | // ä»åºä¿¡æ¯ |
| | | MesWmWarehouseDO warehouse = warehouseMap.get(transaction.getWarehouseId()); |
| | |
| | | } |
| | | } |
| | | |
| | | } |
| | | } |
| | |
| | | @Schema(description = "æ¹æ¬¡å·", example = "BATCH001") |
| | | private String batchCode; |
| | | |
| | | @Schema(description = "ç²¾ç¡®æ¹æ¬¡å·") |
| | | private String batchCodeExact; |
| | | |
| | | @Schema(description = "è¢ç ", example = "BG202608180001") |
| | | private String bagCode; |
| | | private Long shipmentId; |
| | | |
| | | @Schema(description = "ä»åºID", example = "1") |
| | | private Long warehouseId; |
| | |
| | | @Schema(description = "ä¸å¡åå·", example = "STOCK_ADJUST_1") |
| | | private String bizCode; |
| | | |
| | | @Schema(description = "精确ä¸å¡åå·") |
| | | private String bizCodeExact; |
| | | |
| | | @Schema(description = "åºåè®°å½ID", example = "1") |
| | | private Long materialStockId; |
| | | |
| | |
| | | @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) |
| | | private LocalDateTime endTime; |
| | | |
| | | } |
| | | } |
| | |
| | | @Schema(description = "ç©æåç§°", example = "èºä¸") |
| | | private String itemName; |
| | | |
| | | @Schema(description = "è§æ ¼åå·", example = "10mm*100mm") |
| | | private String specification; |
| | | |
| | | @Schema(description = "ç©æç±»å«åç§°", example = "äºéé
ä»¶") |
| | | private String itemTypeName; |
| | | |
| | | @Schema(description = "å卿°éï¼æ£æ°=å
¥åºï¼è´æ°=åºåºï¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "100") |
| | | private BigDecimal quantity; |
| | | |
| | |
| | | @Schema(description = "è¢ç ", example = "BG202608180001") |
| | | private String bagCode; |
| | | |
| | | @Schema(description = "éå®åºåºæç»è®°å½çæçç ") |
| | | private String palletCode; |
| | | private Long shipmentId; |
| | | |
| | | // ========== åºåä½ç½® ========== |
| | | |
| | | @Schema(description = "ä»åºID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | |
| | | * <p> |
| | | * æ¹æ¬¡ç产管çï¼ä¸è¢ä¸ç 模åï¼ï¼å
³è产线ï¼äº§çº¿ç»´æ¤ï¼ä¸ç产人åï¼äººåé
ç½®è§ {@link MesProBatchWorkerDO}ï¼ï¼è´£ä»»è½å®å°äººã |
| | | * åç§å·²ä¸æ²å°è¢ç æ°æ®å
ï¼èµç 管çï¼ï¼æ¹æ¬¡ä¸åç»å®åç§ã |
| | | * æ¹æ¬¡å·èªå¨çæï¼ç¼ç è§åå
嫿¥æã产线ç¼ç ãæµæ°´å·çè¦ç´ ï¼å¹¶ä¸è¢ç ï¼ä¸è¢ä¸ç ï¼ãæçç èªå¨å
³èã |
| | | * æ¹æ¬¡å·æ¯æäººå·¥è¾å
¥æç³»ç»çæï¼æ ¼å¼ä¸ºã4 ä½å¹´ä»½ + 3 使µæ°´ãï¼å¦ 2026001ï¼ï¼å¹¶ä¸è¢ç ï¼ä¸è¢ä¸ç ï¼ãæçç èªå¨å
³èã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | |
| | | @TableId |
| | | private Long id; |
| | | /** |
| | | * æ¹æ¬¡å·ï¼èªå¨çæï¼åç¼ + æ¥æ + 产线ç¼ç + æµæ°´å·ï¼è¡¥å½ä¸å¡ä¿¡æ¯åçæï¼ |
| | | * æ¹æ¬¡å·ï¼äººå·¥è¾å
¥æç³»ç»çæï¼4 ä½å¹´ä»½ + 3 使µæ°´ï¼å¦ 2026001ï¼è¡¥å½ä¸å¡ä¿¡æ¯åçæï¼ |
| | | */ |
| | | private String code; |
| | | /** å
é¨åºå
¥åºä¸å¡ç ï¼ä¿æç¨³å®ï¼ä¸éæ£å¼ç è¡¥å½æç»å®ååã */ |
| | |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | |
| | | private Long bagCodeId; |
| | | private Long batchId; |
| | | private String batchCode; |
| | | /** |
| | | * æçç¼å·ï¼éæä¹
åï¼æè¢ç /æçç æ«ç äºä»¶å®æ¶åæ¥ï¼åå²äºä»¶åæ ·å¯å±ç¤ºï¼ |
| | | */ |
| | | @TableField(exist = false) |
| | | private Long palletId; |
| | | /** |
| | | * æçç ï¼éæä¹
åï¼æ¥æºåä¸ï¼ |
| | | */ |
| | | @TableField(exist = false) |
| | | private String palletCode; |
| | | private Long itemId; |
| | | private BigDecimal quantity; |
| | | private String deviceCode; |
| | |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * ç§åè´¨æ£é¡¹åæ ¼èå´é
ç½® |
| | | * |
| | | * itemId 为空表示éç¨é»è®¤é¡¹ï¼äº§åé
ç½®ä¼å
äºéç¨é»è®¤é¡¹ã |
| | | */ |
| | | @TableName("mes_qc_seed_indicator_range") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class MesQcSeedIndicatorRangeDO extends BaseDO { |
| | | @TableId(type = IdType.INPUT) |
| | | @TableId(type = IdType.AUTO) |
| | | private Long id; |
| | | /** 产åç¼å·ï¼mdm_itemï¼itemType=2ï¼ï¼ä¸ºç©ºè¡¨ç¤ºéç¨é»è®¤é¡¹ */ |
| | | private Long itemId; |
| | | private String indicatorCode; |
| | | private String indicatorName; |
| | | private String unit; |
| | | private BigDecimal minValue; |
| | | private BigDecimal maxValue; |
| | | private Integer sort; |
| | | /** ç¶æï¼0 å¯ç¨ 1 åç¨ */ |
| | | private Integer status; |
| | | } |
| | |
| | | public class MesQcSeedQualityDO extends BaseDO { |
| | | @TableId private Long id; |
| | | private String code; |
| | | /** è´¨æ£çº¿ï¼1 å å·¥é¨ï¼2 éå®é¨ï¼0 æªåºåï¼å岿°æ®ï¼ */ |
| | | private Integer qcLine; |
| | | /** ä¸å¡ç±»åï¼1 ç产å
¥åºè´¨æ£ï¼2 ç»éåéè´§è´¨æ£ */ |
| | | private Integer bizType; |
| | | private Long itemId; |
| | | /** çäº§æ¹æ¬¡ ID */ |
| | | private Long batchId; |
| | |
| | | private String reason; |
| | | /** 10 å¾
夿£ï¼20 å¾
åæ£å®¡æ ¸ï¼25 å¾
忣ä¸åæ ¼å¤çï¼30 å¾
夿£å®¡æ ¸ï¼40 å·²æ¾è¡ï¼50 忣驳åå¾
鿣ï¼60 å¾
夿£ä¸åæ ¼å¤çï¼70 å·²æ¥åºã */ |
| | | private Integer workflowStatus; |
| | | /** æ¬çº¿é¨é¨å®¡æ¹æµç¨å®ä¾ç¼å· */ |
| | | private String processInstanceId; |
| | | /** å
¬å¸è´¨æ£å®¡æ¹æµç¨å®ä¾ç¼å· */ |
| | | private String companyProcessInstanceId; |
| | | private Long qualifiedWarehouseId; |
| | | /** ç»éåéè´§åç¼å·ï¼bizType=2 æ¶ä½¿ç¨ï¼ */ |
| | | private Long returnSalesId; |
| | | /** ç»éåéè´§åå·ï¼bizType=2 æ¶ä½¿ç¨ï¼ */ |
| | | private String returnSalesCode; |
| | | private Integer retestResult; |
| | | private LocalDateTime retestedAt; |
| | | private LocalDateTime processApprovedAt; |
| | | private LocalDateTime lineFinishedAt; |
| | | private LocalDateTime companySubmittedAt; |
| | | private LocalDateTime companyApprovedAt; |
| | | private Long inspectorId; |
| | | private LocalDateTime inspectedAt; |
| | |
| | | @TableId private Long id; |
| | | private Long qualityId; |
| | | private String indicatorCode; |
| | | /** ææ åç§°å¿«ç
§ */ |
| | | private String indicatorName; |
| | | /** ææ åä½å¿«ç
§ */ |
| | | private String unit; |
| | | private BigDecimal value; |
| | | private BigDecimal minValue; |
| | | private BigDecimal maxValue; |
| | |
| | | * æä¸¾ {@link MesWmProductSalesStatusEnum} |
| | | */ |
| | | private Integer status; |
| | | private String closeReason; |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | |
| | | */ |
| | | private String processDefinitionKey; |
| | | |
| | | } |
| | | } |
| | |
| | | * å
³è {@link MesWmProductSalesDO#getId()} |
| | | */ |
| | | private Long salesId; |
| | | /** å·²åé
çåè´§è®°å½ï¼ç©ºå¼è¡¨ç¤ºæ¬è½®å¾
æ£è´§æå岿ç»ã */ |
| | | private Long shipmentId; |
| | | /** |
| | | * ç©æID |
| | | * |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** éå®åºåºåç忬¡åè´§è®°å½ã */ |
| | | @TableName("mes_wm_product_sales_shipment") |
| | | @KeySequence("mes_wm_product_sales_shipment_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class MesWmProductSalesShipmentDO extends BaseDO { |
| | | @TableId |
| | | private Long id; |
| | | private Long salesId; |
| | | private Integer status; |
| | | private String carrier; |
| | | private String shippingNumber; |
| | | private LocalDateTime outboundTime; |
| | | private Long outboundUserId; |
| | | } |
| | |
| | | private Long bagCodeId; |
| | | /** è¢ç */ |
| | | private String bagCode; |
| | | private Long shipmentId; |
| | | |
| | | // ==================== åºåä½ç½® ==================== |
| | | |
| | |
| | | .and(q -> q.eq(MesProBagCodeDO::getBusinessCode, code).or().eq(MesProBagCodeDO::getCode, code))); |
| | | } |
| | | |
| | | /** æ¹éææ«ç å
容忥è¢ç ï¼å
¼å®¹ä¸å¡ç ã追溯ç ï¼ï¼ç¨äºæ«ç äºä»¶å±ç¤ºæå±æçç */ |
| | | default List<MesProBagCodeDO> selectListByScanCodes(Collection<String> codes) { |
| | | return selectList(new LambdaQueryWrapperX<MesProBagCodeDO>().and(q -> |
| | | q.in(MesProBagCodeDO::getBusinessCode, codes).or().in(MesProBagCodeDO::getCode, codes))); |
| | | } |
| | | |
| | | default MesProBagCodeDO selectByScanCodeForUpdate(String code) { |
| | | return selectOne(new LambdaQueryWrapperX<MesProBagCodeDO>() |
| | | .and(q -> q.eq(MesProBagCodeDO::getBusinessCode, code).or().eq(MesProBagCodeDO::getCode, code)) |
| | |
| | | return selectOne(MesProBatchDO::getCode, code); |
| | | } |
| | | |
| | | /** |
| | | * ææ¹æ¬¡å·æ¥è¯¢ï¼å
å«å·²é»è¾å é¤è®°å½ï¼ |
| | | * <p> |
| | | * æ¹æ¬¡å·åå¨å¯ä¸ç´¢å¼ï¼é»è¾å é¤çè®°å½ä»å ç¨ç å¼ï¼æ ¡éªé夿¶å¿
é¡»ä¸å¹¶çº³å
¥ã |
| | | */ |
| | | @Select("SELECT * FROM mes_pro_batch WHERE code = #{code} LIMIT 1") |
| | | MesProBatchDO selectByCodeIgnoreDeleted(@org.apache.ibatis.annotations.Param("code") String code); |
| | | |
| | | /** |
| | | * æ¥è¯¢æå®å¹´ä»½å·²ä½¿ç¨çæå¤§æ¹æ¬¡æµæ°´å·ï¼æ¹æ¬¡å·æ ¼å¼ä¸º 4 ä½å¹´ä»½ + 3 使µæ°´ï¼ |
| | | */ |
| | | @Select("SELECT COALESCE(MAX(CAST(SUBSTRING(code, 5) AS UNSIGNED)), 0) FROM mes_pro_batch " |
| | | + "WHERE code LIKE CONCAT(#{year}, '%') AND code REGEXP '^[0-9]{7}$'") |
| | | Integer selectMaxCodeSerialByYear(@org.apache.ibatis.annotations.Param("year") String year); |
| | | |
| | | default MesProBatchDO selectByScanCode(String code) { |
| | | return selectOne(new LambdaQueryWrapperX<MesProBatchDO>().and(q -> |
| | | q.eq(MesProBatchDO::getBusinessCode, code).or().eq(MesProBatchDO::getCode, code) |
| | |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Select; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | |
| | | /** |
| | |
| | | return selectList(MesProPalletDO::getBatchId, batchId); |
| | | } |
| | | |
| | | /** æ¹éææ«ç å
容忥æçï¼å
¼å®¹ä¸å¡ç ãæ£å¼æçç ãä¸´æ¶æçç ï¼ï¼ç¨äºæ«ç äºä»¶å±ç¤ºæçç */ |
| | | default List<MesProPalletDO> selectListByScanCodes(Collection<String> codes) { |
| | | return selectList(new LambdaQueryWrapperX<MesProPalletDO>().and(q -> |
| | | q.in(MesProPalletDO::getBusinessCode, codes).or().in(MesProPalletDO::getCode, codes) |
| | | .or().in(MesProPalletDO::getTempCode, codes))); |
| | | } |
| | | |
| | | /** |
| | | * æ¹éæ¥è¯¢å¤ä¸ªæ¹æ¬¡ä¸çæçç ï¼ææçåºå·ååºï¼ |
| | | */ |
| | | default List<MesProPalletDO> selectListByBatchIds(Collection<Long> batchIds) { |
| | | return selectList(new LambdaQueryWrapperX<MesProPalletDO>() |
| | | .in(MesProPalletDO::getBatchId, batchIds) |
| | | .orderByAsc(MesProPalletDO::getPalletNo)); |
| | | } |
| | | |
| | | /** |
| | | * å页æ¥è¯¢æªç»å®çäº§æ¹æ¬¡çä¸´æ¶æçï¼æ æ£å¼æçç 䏿ªä½åºï¼ï¼ä¾æ¹æ¬¡æ°å¢/è¡¥å½ç»å®æçä¸æä½¿ç¨ã |
| | | * <p> |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.qc.seedquality; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedIndicatorRangeDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Mapper |
| | | public interface MesQcSeedIndicatorRangeMapper extends BaseMapperX<MesQcSeedIndicatorRangeDO> { |
| | | |
| | | default List<MesQcSeedIndicatorRangeDO> selectListByItemId(Long itemId, String keyword, Integer status) { |
| | | LambdaQueryWrapperX<MesQcSeedIndicatorRangeDO> wrapper = new LambdaQueryWrapperX<>(); |
| | | if (itemId == null) { |
| | | wrapper.isNull(MesQcSeedIndicatorRangeDO::getItemId); |
| | | } else { |
| | | wrapper.eq(MesQcSeedIndicatorRangeDO::getItemId, itemId); |
| | | } |
| | | wrapper.and(StrUtil.isNotBlank(keyword), w -> w |
| | | .like(MesQcSeedIndicatorRangeDO::getIndicatorName, keyword) |
| | | .or().like(MesQcSeedIndicatorRangeDO::getIndicatorCode, keyword)); |
| | | wrapper.eqIfPresent(MesQcSeedIndicatorRangeDO::getStatus, status) |
| | | .orderByAsc(MesQcSeedIndicatorRangeDO::getSort) |
| | | .orderByAsc(MesQcSeedIndicatorRangeDO::getId); |
| | | return selectList(wrapper); |
| | | } |
| | | |
| | | /** 产åä¸å±ä¸å¯ç¨çææ ï¼ææåºè¿å */ |
| | | default List<MesQcSeedIndicatorRangeDO> selectEnabledByItemId(Long itemId) { |
| | | return selectList(new LambdaQueryWrapperX<MesQcSeedIndicatorRangeDO>() |
| | | .eq(MesQcSeedIndicatorRangeDO::getItemId, itemId) |
| | | .eq(MesQcSeedIndicatorRangeDO::getStatus, 0) |
| | | .orderByAsc(MesQcSeedIndicatorRangeDO::getSort) |
| | | .orderByAsc(MesQcSeedIndicatorRangeDO::getId)); |
| | | } |
| | | |
| | | /** æªç»å®äº§åçéç¨é»è®¤ææ ï¼ææåºè¿å */ |
| | | default List<MesQcSeedIndicatorRangeDO> selectEnabledDefaults() { |
| | | return selectList(new LambdaQueryWrapperX<MesQcSeedIndicatorRangeDO>() |
| | | .isNull(MesQcSeedIndicatorRangeDO::getItemId) |
| | | .eq(MesQcSeedIndicatorRangeDO::getStatus, 0) |
| | | .orderByAsc(MesQcSeedIndicatorRangeDO::getSort) |
| | | .orderByAsc(MesQcSeedIndicatorRangeDO::getId)); |
| | | } |
| | | |
| | | default MesQcSeedIndicatorRangeDO selectByItemIdAndCode(Long itemId, String indicatorCode) { |
| | | LambdaQueryWrapperX<MesQcSeedIndicatorRangeDO> wrapper = new LambdaQueryWrapperX<>(); |
| | | if (itemId == null) { |
| | | wrapper.isNull(MesQcSeedIndicatorRangeDO::getItemId); |
| | | } else { |
| | | wrapper.eq(MesQcSeedIndicatorRangeDO::getItemId, itemId); |
| | | } |
| | | wrapper.eq(MesQcSeedIndicatorRangeDO::getIndicatorCode, indicatorCode); |
| | | return selectOne(wrapper); |
| | | } |
| | | } |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.qc.seedquality; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityPageReqVO; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualityDO; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | @Mapper |
| | | public interface MesQcSeedQualityMapper extends BaseMapperX<MesQcSeedQualityDO> { |
| | |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<MesQcSeedQualityDO>() |
| | | .likeIfPresent(MesQcSeedQualityDO::getCode, reqVO.getCode()) |
| | | .likeIfPresent(MesQcSeedQualityDO::getBatchCode, reqVO.getBatchCode()) |
| | | .eqIfPresent(MesQcSeedQualityDO::getBatchCode, reqVO.getExactBatchCode()) |
| | | .eqIfPresent(MesQcSeedQualityDO::getResult, reqVO.getResult()) |
| | | .eqIfPresent(MesQcSeedQualityDO::getWorkflowStatus, reqVO.getWorkflowStatus()) |
| | | .eqIfPresent(MesQcSeedQualityDO::getQcLine, reqVO.getQcLine()) |
| | | .eqIfPresent(MesQcSeedQualityDO::getBizType, reqVO.getBizType()) |
| | | .isNull(Boolean.TRUE.equals(reqVO.getUnbound()), MesQcSeedQualityDO::getBatchCode) |
| | | .orderByAsc(MesQcSeedQualityDO::getCode) |
| | | .orderByAsc(MesQcSeedQualityDO::getQcLine) |
| | | .orderByDesc(MesQcSeedQualityDO::getInspectedAt) |
| | | .orderByDesc(MesQcSeedQualityDO::getId)); |
| | | } |
| | | default MesQcSeedQualityDO selectByCode(String code) { |
| | | return selectOne(new LambdaQueryWrapperX<MesQcSeedQualityDO>().eq(MesQcSeedQualityDO::getCode, code)); |
| | | |
| | | /** åä¸è¢ç å¯è½åæ¶åå¨å å·¥é¨ãéå®é¨ä¸¤æ¡è®°å½ï¼æè´¨æ£çº¿ååºè¿å */ |
| | | default List<MesQcSeedQualityDO> selectListByCode(String code) { |
| | | return selectList(new LambdaQueryWrapperX<MesQcSeedQualityDO>() |
| | | .eq(MesQcSeedQualityDO::getCode, code) |
| | | .orderByAsc(MesQcSeedQualityDO::getQcLine)); |
| | | } |
| | | default List<MesQcSeedQualityDO> selectListByCodes(java.util.Collection<String> codes) { |
| | | |
| | | default List<MesQcSeedQualityDO> selectListByCodeForUpdate(String code) { |
| | | return selectList(new LambdaQueryWrapperX<MesQcSeedQualityDO>() |
| | | .eq(MesQcSeedQualityDO::getCode, code) |
| | | .orderByAsc(MesQcSeedQualityDO::getQcLine) |
| | | .last("FOR UPDATE")); |
| | | } |
| | | |
| | | default MesQcSeedQualityDO selectByCodeAndLine(String code, Integer qcLine) { |
| | | return selectOne(new LambdaQueryWrapperX<MesQcSeedQualityDO>() |
| | | .eq(MesQcSeedQualityDO::getCode, code) |
| | | .eq(MesQcSeedQualityDO::getQcLine, qcLine)); |
| | | } |
| | | |
| | | default MesQcSeedQualityDO selectByReturnSalesId(Long returnSalesId) { |
| | | return selectOne(new LambdaQueryWrapperX<MesQcSeedQualityDO>() |
| | | .eq(MesQcSeedQualityDO::getReturnSalesId, returnSalesId) |
| | | .orderByDesc(MesQcSeedQualityDO::getId)); |
| | | } |
| | | |
| | | default MesQcSeedQualityDO selectByReturnSalesIdForUpdate(Long returnSalesId) { |
| | | return selectOne(new LambdaQueryWrapperX<MesQcSeedQualityDO>() |
| | | .eq(MesQcSeedQualityDO::getReturnSalesId, returnSalesId) |
| | | .orderByDesc(MesQcSeedQualityDO::getId) |
| | | .last("FOR UPDATE")); |
| | | } |
| | | |
| | | /** æ¸
空åä¸è¢ç çå
¬å¸è´¨æ£æµç¨å®ä¾ç¼å·ï¼ä¾¿äºå
¬å¸å®¡æ¹é©³ååéæ°åèµ· */ |
| | | default void clearCompanyProcessInstance(String code) { |
| | | update(null, new com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<MesQcSeedQualityDO>() |
| | | .eq(MesQcSeedQualityDO::getCode, code) |
| | | .set(MesQcSeedQualityDO::getCompanyProcessInstanceId, null)); |
| | | } |
| | | |
| | | default void clearLineProcessInstance(Long id) { |
| | | update(null, new com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<MesQcSeedQualityDO>() |
| | | .eq(MesQcSeedQualityDO::getId, id) |
| | | .set(MesQcSeedQualityDO::getProcessInstanceId, null)); |
| | | } |
| | | |
| | | default List<MesQcSeedQualityDO> selectListByCodes(Collection<String> codes) { |
| | | if (codes.isEmpty()) return List.of(); |
| | | return selectList(new LambdaQueryWrapperX<MesQcSeedQualityDO>().in(MesQcSeedQualityDO::getCode, codes)); |
| | | } |
| | | default List<MesQcSeedQualityDO> selectListByCodesForUpdate(java.util.Collection<String> codes) { |
| | | |
| | | default List<MesQcSeedQualityDO> selectListByCodesForUpdate(Collection<String> codes) { |
| | | if (codes.isEmpty()) return List.of(); |
| | | return selectList(new LambdaQueryWrapperX<MesQcSeedQualityDO>().in(MesQcSeedQualityDO::getCode, codes).last("FOR UPDATE")); |
| | | } |
| | | default MesQcSeedQualityDO selectByCodeForUpdate(String code) { |
| | | return selectOne(new LambdaQueryWrapperX<MesQcSeedQualityDO>().eq(MesQcSeedQualityDO::getCode, code).last("FOR UPDATE")); |
| | | } |
| | | |
| | | default MesQcSeedQualityDO selectByIdForUpdate(Long id) { |
| | | return selectOne(new LambdaQueryWrapperX<MesQcSeedQualityDO>().eq(MesQcSeedQualityDO::getId, id).last("FOR UPDATE")); |
| | | } |
| | | default List<MesQcSeedQualityDO> selectListByIdsForUpdate(java.util.Collection<Long> ids) { |
| | | |
| | | default List<MesQcSeedQualityDO> selectListByIdsForUpdate(Collection<Long> ids) { |
| | | if (ids.isEmpty()) return List.of(); |
| | | return selectList(new LambdaQueryWrapperX<MesQcSeedQualityDO>() |
| | | .in(MesQcSeedQualityDO::getId, ids) |
| | |
| | | .last("FOR UPDATE")); |
| | | } |
| | | |
| | | default List<MesQcSeedQualityDO> selectListByProcessInstanceId(String processInstanceId) { |
| | | return selectList(new LambdaQueryWrapperX<MesQcSeedQualityDO>() |
| | | .eq(MesQcSeedQualityDO::getProcessInstanceId, processInstanceId)); |
| | | } |
| | | |
| | | default List<MesQcSeedQualityDO> selectListByCompanyProcessInstanceId(String processInstanceId) { |
| | | return selectList(new LambdaQueryWrapperX<MesQcSeedQualityDO>() |
| | | .eq(MesQcSeedQualityDO::getCompanyProcessInstanceId, processInstanceId)); |
| | | } |
| | | |
| | | default List<MesQcSeedQualityDO> selectListByBatchCode(String batchCode) { |
| | | return selectList(new LambdaQueryWrapperX<MesQcSeedQualityDO>() |
| | | .eq(MesQcSeedQualityDO::getBatchCode, batchCode) |
| | | .orderByDesc(MesQcSeedQualityDO::getInspectedAt)); |
| | | } |
| | | |
| | | /** æçäº§æ¹æ¬¡åç»çç»æ°ï¼æªç»å®æ¹æ¬¡å½å
¥ç©ºæ¹æ¬¡ç»ï¼ */ |
| | | default long countBatchGroups(MesQcSeedQualityPageReqVO reqVO) { |
| | | QueryWrapper<MesQcSeedQualityDO> wrapper = buildBatchGroupFilter(reqVO); |
| | | wrapper.select("COUNT(DISTINCT IFNULL(batch_code, '')) AS cnt"); |
| | | List<Object> values = selectObjs(wrapper); |
| | | if (values.isEmpty() || values.get(0) == null) { |
| | | return 0L; |
| | | } |
| | | return ((Number) values.get(0)).longValue(); |
| | | } |
| | | |
| | | /** |
| | | * æçäº§æ¹æ¬¡åç»çåé¡µæ±æ»ï¼offset/limit ç±è°ç¨æ¹ææ¹æ¬¡é¡µè®¡ç®ã |
| | | * åä¸è¢ç åå¨å å·¥é¨ãéå®é¨ä¸¤æ¡è®°å½ï¼å æ¤æè¢ç å»éç»è®¡ã |
| | | */ |
| | | default List<Map<String, Object>> selectBatchGroups(MesQcSeedQualityPageReqVO reqVO, long offset, int limit) { |
| | | QueryWrapper<MesQcSeedQualityDO> wrapper = buildBatchGroupFilter(reqVO); |
| | | wrapper.select( |
| | | "IFNULL(batch_code, '') AS batchCode", |
| | | "MAX(batch_id) AS batchId", |
| | | "MAX(item_id) AS itemId", |
| | | "COUNT(DISTINCT code) AS totalCount", |
| | | "COUNT(DISTINCT CASE WHEN result = 1 THEN code END) AS passCount", |
| | | "COUNT(DISTINCT CASE WHEN result = 2 THEN code END) AS failCount", |
| | | "COUNT(DISTINCT CASE WHEN result IS NULL OR result = 0 THEN code END) AS processingCount", |
| | | "COUNT(DISTINCT CASE WHEN workflow_status IN (10, 50) THEN code END) AS pendingRetestCount", |
| | | "COUNT(DISTINCT CASE WHEN workflow_status IN (20, 25) THEN code END) AS pendingProcessReviewCount", |
| | | "COUNT(DISTINCT CASE WHEN workflow_status IN (30, 60) THEN code END) AS pendingCompanyReviewCount", |
| | | "COUNT(DISTINCT CASE WHEN workflow_status = 40 THEN code END) AS releasedCount", |
| | | "COUNT(DISTINCT CASE WHEN workflow_status = 70 THEN code END) AS scrappedCount", |
| | | "MAX(inspected_at) AS lastInspectedAt"); |
| | | wrapper.groupBy("IFNULL(batch_code, '')"); |
| | | wrapper.orderByDesc("MAX(inspected_at)", "MAX(id)"); |
| | | wrapper.last("LIMIT " + offset + ", " + limit); |
| | | return selectMaps(wrapper); |
| | | } |
| | | |
| | | private static QueryWrapper<MesQcSeedQualityDO> buildBatchGroupFilter(MesQcSeedQualityPageReqVO reqVO) { |
| | | QueryWrapper<MesQcSeedQualityDO> wrapper = new QueryWrapper<>(); |
| | | if (StrUtil.isNotBlank(reqVO.getCode())) { |
| | | wrapper.like("code", reqVO.getCode()); |
| | | } |
| | | if (StrUtil.isNotBlank(reqVO.getBatchCode())) { |
| | | wrapper.like("batch_code", reqVO.getBatchCode()); |
| | | } |
| | | if (reqVO.getResult() != null) { |
| | | wrapper.eq("result", reqVO.getResult()); |
| | | } |
| | | if (reqVO.getWorkflowStatus() != null) { |
| | | wrapper.eq("workflow_status", reqVO.getWorkflowStatus()); |
| | | } |
| | | if (reqVO.getQcLine() != null) { |
| | | wrapper.eq("qc_line", reqVO.getQcLine()); |
| | | } |
| | | if (reqVO.getBizType() != null) { |
| | | wrapper.eq("biz_type", reqVO.getBizType()); |
| | | } |
| | | return wrapper; |
| | | } |
| | | } |
| | |
| | | .eqIfPresent(MesWmMaterialStockDO::getVendorId, reqVO.getVendorId()) |
| | | .eqIfPresent(MesWmMaterialStockDO::getStockState, reqVO.getStockState()) |
| | | .eqIfPresent(MesWmMaterialStockDO::getFrozen, reqVO.getFrozen()); |
| | | wrapper.ne(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO) |
| | | .orderByAsc(MesWmMaterialStockDO::getReceiptTime); |
| | | if (!Boolean.TRUE.equals(reqVO.getIncludeZero())) { |
| | | wrapper.ne(MesWmMaterialStockDO::getQuantity, BigDecimal.ZERO); |
| | | } |
| | | wrapper.orderByAsc(MesWmMaterialStockDO::getReceiptTime); |
| | | // èæä»è¿æ»¤ï¼Service å±å·²å° virtualFilter è§£æä¸º virtualWarehouseIdï¼ |
| | | if (virtualWarehouseId != null) { |
| | | if (MesWmMaterialStockPageReqVO.VIRTUAL_FILTER_ONLY.equals(reqVO.getVirtualFilter())) { |
| | |
| | | LambdaQueryWrapperX<MesWmMaterialStockDO> wrapper = new LambdaQueryWrapperX<MesWmMaterialStockDO>() |
| | | .eq(MesWmMaterialStockDO::getItemId, itemId) |
| | | .eq(MesWmMaterialStockDO::getWarehouseId, warehouseId) |
| | | .eq(MesWmMaterialStockDO::getLocationId, locationId) |
| | | .eq(MesWmMaterialStockDO::getAreaId, areaId) |
| | | .eqIfPresent(MesWmMaterialStockDO::getLocationId, locationId) |
| | | .eqIfPresent(MesWmMaterialStockDO::getAreaId, areaId) |
| | | .eq(MesWmMaterialStockDO::getStockState, MesWmStockStateEnum.QUALIFIED.getState()); |
| | | // batchId=null æ¶ç²¾ç¡®å¹é
is null |
| | | if (batchId != null) { |
| | |
| | | LambdaQueryWrapperX<MesWmMaterialStockDO> wrapper = new LambdaQueryWrapperX<MesWmMaterialStockDO>() |
| | | .eq(MesWmMaterialStockDO::getItemId, itemId) |
| | | .eq(MesWmMaterialStockDO::getWarehouseId, warehouseId) |
| | | .eq(MesWmMaterialStockDO::getLocationId, locationId) |
| | | .eq(MesWmMaterialStockDO::getAreaId, areaId) |
| | | .eqIfPresent(MesWmMaterialStockDO::getLocationId, locationId) |
| | | .eqIfPresent(MesWmMaterialStockDO::getAreaId, areaId) |
| | | .eq(MesWmMaterialStockDO::getStockState, MesWmStockStateEnum.QUALIFIED.getState()); |
| | | // batchId=null æ¶ç²¾ç¡®å¹é
is null |
| | | if (batchId != null) { |
| | |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<MesWmMaterialStockBagDO>() |
| | | .eq(MesWmMaterialStockBagDO::getMaterialStockId, reqVO.getMaterialStockId()) |
| | | .likeIfPresent(MesWmMaterialStockBagDO::getBagCode, reqVO.getBagCode()) |
| | | .eq(MesWmMaterialStockBagDO::getStatus, 1) |
| | | .eqIfPresent(MesWmMaterialStockBagDO::getStatus, Boolean.TRUE.equals(reqVO.getIncludeHistory()) ? null : 1) |
| | | .orderByAsc(MesWmMaterialStockBagDO::getInboundTime)); |
| | | } |
| | | |
| | |
| | | .orderByAsc(MesWmMaterialStockBagDO::getInboundTime)); |
| | | } |
| | | |
| | | default List<MesWmMaterialStockBagDO> selectHistoryByMaterialStockIds(Collection<Long> materialStockIds) { |
| | | if (materialStockIds.isEmpty()) return List.of(); |
| | | return selectList(new LambdaQueryWrapperX<MesWmMaterialStockBagDO>() |
| | | .in(MesWmMaterialStockBagDO::getMaterialStockId, materialStockIds)); |
| | | } |
| | | |
| | | default List<MesWmMaterialStockBagDO> selectListByMaterialStockId(Long materialStockId) { |
| | | return selectList(new LambdaQueryWrapperX<MesWmMaterialStockBagDO>() |
| | | .eq(MesWmMaterialStockBagDO::getMaterialStockId, materialStockId) |
| | |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.List; |
| | | import java.util.Collection; |
| | | |
| | | /** |
| | | * MES éå®åºåºæç» Mapper |
| | |
| | | @Mapper |
| | | public interface MesWmProductSalesDetailMapper extends BaseMapperX<MesWmProductSalesDetailDO> { |
| | | |
| | | default List<MesWmProductSalesDetailDO> selectBySalesIdsAndBagIds(Collection<Long> salesIds, Collection<Long> bagIds) { |
| | | if (salesIds.isEmpty() || bagIds.isEmpty()) return List.of(); |
| | | return selectList(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MesWmProductSalesDetailDO>() |
| | | .in(MesWmProductSalesDetailDO::getSalesId, salesIds) |
| | | .in(MesWmProductSalesDetailDO::getBagCodeId, bagIds)); |
| | | } |
| | | |
| | | default List<MesWmProductSalesDetailDO> selectByBagIds(Collection<Long> bagIds) { |
| | | if (bagIds.isEmpty()) return List.of(); |
| | | return selectList(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MesWmProductSalesDetailDO>() |
| | | .in(MesWmProductSalesDetailDO::getBagCodeId, bagIds)); |
| | | } |
| | | |
| | | // ä»
æªåæ¶ä¸å°æªå®æçåºåºåå ç¨è¢ç ãæ§æ´ææç»ä¹åä¸éå 夿ã |
| | | @org.apache.ibatis.annotations.Select(""" |
| | | SELECT d.* FROM mes_wm_product_sales_detail d |
| | | JOIN mes_wm_product_sales s ON s.id = d.sales_id |
| | | LEFT JOIN mes_wm_product_sales_shipment sh ON sh.id = d.shipment_id AND sh.deleted = 0 |
| | | WHERE d.deleted = 0 AND s.deleted = 0 AND s.status IN (2, 10, 3) |
| | | AND (d.shipment_id IS NULL OR sh.status IN (10, 20)) |
| | | AND d.batch_code = #{batchCode} |
| | | """) |
| | | List<MesWmProductSalesDetailDO> selectPickingByBatchCode(String batchCode); |
| | |
| | | @org.apache.ibatis.annotations.Select(""" |
| | | SELECT d.* FROM mes_wm_product_sales_detail d |
| | | JOIN mes_wm_product_sales s ON s.id = d.sales_id |
| | | LEFT JOIN mes_wm_product_sales_shipment sh ON sh.id = d.shipment_id AND sh.deleted = 0 |
| | | WHERE d.deleted = 0 AND s.deleted = 0 AND s.status IN (2, 10, 3) |
| | | AND (d.shipment_id IS NULL OR sh.status IN (10, 20)) |
| | | AND d.batch_code = #{batchCode} FOR UPDATE |
| | | """) |
| | | List<MesWmProductSalesDetailDO> selectPickingByBatchCodeForUpdate(String batchCode); |
| | |
| | | return selectList(MesWmProductSalesDetailDO::getLineId, lineId); |
| | | } |
| | | |
| | | default void unassignShipment(Long shipmentId) { |
| | | update(null, new com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper<MesWmProductSalesDetailDO>() |
| | | .eq(MesWmProductSalesDetailDO::getShipmentId, shipmentId) |
| | | .set(MesWmProductSalesDetailDO::getShipmentId, null)); |
| | | } |
| | | |
| | | default List<MesWmProductSalesDetailDO> selectListBySalesId(Long salesId) { |
| | | return selectList(MesWmProductSalesDetailDO::getSalesId, salesId); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.List; |
| | | import java.util.Collection; |
| | | |
| | | @Mapper |
| | | public interface MesWmProductSalesShipmentMapper extends BaseMapperX<MesWmProductSalesShipmentDO> { |
| | | default List<MesWmProductSalesShipmentDO> selectBySalesId(Long salesId) { |
| | | return selectList(new LambdaQueryWrapperX<MesWmProductSalesShipmentDO>() |
| | | .eq(MesWmProductSalesShipmentDO::getSalesId, salesId) |
| | | .orderByAsc(MesWmProductSalesShipmentDO::getId)); |
| | | } |
| | | |
| | | default List<MesWmProductSalesShipmentDO> selectCompletedBySalesIds(Collection<Long> salesIds) { |
| | | if (salesIds.isEmpty()) return List.of(); |
| | | return selectList(new LambdaQueryWrapperX<MesWmProductSalesShipmentDO>() |
| | | .in(MesWmProductSalesShipmentDO::getSalesId, salesIds).eq(MesWmProductSalesShipmentDO::getStatus, 30)); |
| | | } |
| | | |
| | | default MesWmProductSalesShipmentDO selectActive(Long salesId) { |
| | | return selectOne(new LambdaQueryWrapperX<MesWmProductSalesShipmentDO>() |
| | | .eq(MesWmProductSalesShipmentDO::getSalesId, salesId) |
| | | .in(MesWmProductSalesShipmentDO::getStatus, 10, 20) |
| | | .orderByDesc(MesWmProductSalesShipmentDO::getId).last("LIMIT 1")); |
| | | } |
| | | } |
| | |
| | | @Mapper |
| | | public interface MesWmTransactionMapper extends BaseMapperX<MesWmTransactionDO> { |
| | | |
| | | @org.apache.ibatis.annotations.Select("SELECT COALESCE(-SUM(quantity), 0) FROM mes_wm_transaction WHERE biz_type = 119 AND type = 2 AND biz_line_id = #{lineId} AND deleted = 0") |
| | | java.math.BigDecimal sumSalesOutboundByLineId(Long lineId); |
| | | |
| | | default PageResult<MesWmTransactionDO> selectPage(MesWmTransactionPageReqVO reqVO) { |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<MesWmTransactionDO>() |
| | | .eqIfPresent(MesWmTransactionDO::getItemId, reqVO.getItemId()) |
| | | .likeIfPresent(MesWmTransactionDO::getBatchCode, reqVO.getBatchCode()) |
| | | .eqIfPresent(MesWmTransactionDO::getBatchCode, reqVO.getBatchCodeExact()) |
| | | .likeIfPresent(MesWmTransactionDO::getBagCode, reqVO.getBagCode()) |
| | | .eqIfPresent(MesWmTransactionDO::getShipmentId, reqVO.getShipmentId()) |
| | | .eqIfPresent(MesWmTransactionDO::getWarehouseId, reqVO.getWarehouseId()) |
| | | .eqIfPresent(MesWmTransactionDO::getLocationId, reqVO.getLocationId()) |
| | | .eqIfPresent(MesWmTransactionDO::getAreaId, reqVO.getAreaId()) |
| | | .eqIfPresent(MesWmTransactionDO::getType, reqVO.getType()) |
| | | .eqIfPresent(MesWmTransactionDO::getBizType, reqVO.getBizType()) |
| | | .likeIfPresent(MesWmTransactionDO::getBizCode, reqVO.getBizCode()) |
| | | .eqIfPresent(MesWmTransactionDO::getBizCode, reqVO.getBizCodeExact()) |
| | | .eqIfPresent(MesWmTransactionDO::getMaterialStockId, reqVO.getMaterialStockId()) |
| | | .geIfPresent(MesWmTransactionDO::getTransactionTime, reqVO.getStartTime()) |
| | | .leIfPresent(MesWmTransactionDO::getTransactionTime, reqVO.getEndTime()) |
| | |
| | | private static final String BAG_NO_SEQ_KEY = "mes:bag_no:seq"; |
| | | |
| | | /** |
| | | * çäº§æ¹æ¬¡æµæ°´å·èªå¢ KEY åç¼ï¼æå¹´ä»½éç¦»ï¼ |
| | | */ |
| | | private static final String BATCH_NO_SEQ_KEY_PREFIX = "mes:pro_batch_code:seq:"; |
| | | |
| | | /** |
| | | * 忬¡æ¹éåç çæå¤§ä¸ªæ° |
| | | * <p> |
| | | * å Redis åæ¡å½ä»¤åæ°è§æ¨¡ä¸ååºä½å¤§å°çº¦æï¼è¿å¤§åèå¢å 忬¡é»å¡æ¶é´ã |
| | |
| | | + "local base = tonumber(ARGV[2]) " |
| | | + "if (not cur) or (tonumber(cur) < base) then redis.call('SET', KEYS[1], base) end " |
| | | + "return redis.call('INCRBY', KEYS[1], ARGV[1])", |
| | | Long.class); |
| | | |
| | | /** |
| | | * åååé
çäº§æ¹æ¬¡æµæ°´å·ï¼å
æåºå
å½åæå¤§æµæ°´æ ¡æ£åºåå¼ï¼åèªå¢ã |
| | | * æå¨å½å
¥è¾å¤§æ¹æ¬¡å·æç´æ¥å¯¼å
¥æ°æ®åï¼ä¸ä¸æ¬¡èªå¨åå·ä¸ä¼ä¸ä¹éå¤ã |
| | | */ |
| | | private static final RedisScript<Long> ALLOCATE_BATCH_NO_SCRIPT = new DefaultRedisScript<>( |
| | | "local cur = redis.call('GET', KEYS[1]) " |
| | | + "local base = tonumber(ARGV[1]) " |
| | | + "if (not cur) or (tonumber(cur) < base) then redis.call('SET', KEYS[1], base) end " |
| | | + "return redis.call('INCR', KEYS[1])", |
| | | Long.class); |
| | | |
| | | @Resource |
| | |
| | | } |
| | | |
| | | /** |
| | | * åååé
çäº§æ¹æ¬¡æµæ°´å·ï¼å
æåºå
å½åæå¤§æµæ°´æ ¡æ£åºåå¼ï¼åèªå¢ã |
| | | * |
| | | * @param year æ¹æ¬¡å·å¹´ä»½ï¼4 ä½ï¼ |
| | | * @param dbMaxSerial æ°æ®åºå½å¹´å·²ç¨æå¤§æµæ°´å·ï¼ä¼ null è§ä¸º 0 |
| | | * @return ä¸ä¸ä¸ªæµæ°´å· |
| | | */ |
| | | public int allocateBatchNo(String year, Integer dbMaxSerial) { |
| | | Long next = stringRedisTemplate.execute(ALLOCATE_BATCH_NO_SCRIPT, |
| | | List.of(BATCH_NO_SEQ_KEY_PREFIX + year), |
| | | String.valueOf(dbMaxSerial == null ? 0 : dbMaxSerial)); |
| | | if (next == null) { |
| | | throw new IllegalStateException("åé
çäº§æ¹æ¬¡æµæ°´å·å¤±è´¥ï¼è¯·éè¯"); |
| | | } |
| | | return next.intValue(); |
| | | } |
| | | |
| | | /** |
| | | * åååé
䏿®µè¿ç»çè¢åºå·ï¼è¿ååºé´èµ·å§å¼ã |
| | | * <p> |
| | | * è¢åºå·åå
ç±ãæ¥åºå
æå¤§å¼å +1ãå¾å°ï¼å¹¶åçææ¶ä¸¤ä¸ªè¯·æ±ä¼è¯»å°åä¸ä¸ªæå¤§å¼ï¼ |
| | |
| | | ErrorCode PRO_QR_EXPORT_TOO_MANY_FOR_SYNC = new ErrorCode(1_040_750_015, "æ¬æ¬¡éå¯¼åº {} å¼ ï¼è¶
è¿åæ¥å¯¼åºä¸é {} å¼ ï¼è¯·æ¹ç¨åå°å¯¼åºä»»å¡"); |
| | | ErrorCode PRO_QR_EXPORT_TASK_FILE_MISSING = new ErrorCode(1_040_750_016, "å¯¼åºæä»¶ä¸åå¨æå·²è¢«å é¤ï¼è¯·éæ°å¯¼åº"); |
| | | ErrorCode PRO_QR_EXPORT_TASK_STATUS_INVALID = new ErrorCode(1_040_750_017, "å½å导åºä»»å¡ç¶æä¸å
许该æä½"); |
| | | ErrorCode PRO_BATCH_CODE_INVALID = new ErrorCode(1_040_750_018, "çäº§æ¹æ¬¡å·æ ¼å¼ä¸æ£ç¡®ï¼åºä¸ºå¹´ä»½+3使µæ°´å·ï¼å¦ 2026001"); |
| | | ErrorCode PRO_BATCH_CODE_DUPLICATE = new ErrorCode(1_040_750_019, "çäº§æ¹æ¬¡å·ã{}ãå·²åå¨"); |
| | | ErrorCode PRO_BATCH_CODE_EXHAUSTED = new ErrorCode(1_040_750_025, "æ¬å¹´åº¦çäº§æ¹æ¬¡å·å·²ç¨å°½ï¼001-999ï¼"); |
| | | |
| | | // ========== MES ç产管ç-è¢ç èµç ï¼1-040-752-000ï¼ ========== |
| | | ErrorCode PRO_BAG_CODE_NOT_EXISTS = new ErrorCode(1_040_752_000, "è¢ç ä¸åå¨"); |
| | |
| | | * |
| | | * å¯¹åº MesWmProductSalesService#cancelProductSales æ¹æ³ |
| | | */ |
| | | CANCELED(MesOrderStatusConstants.CANCELLED, "已忶"); |
| | | CANCELED(MesOrderStatusConstants.CANCELLED, "已忶"), |
| | | CLOSED(MesOrderStatusConstants.CLOSED, "å·²å
³é"); |
| | | |
| | | public static final Integer[] ARRAYS = Arrays.stream(values()).map(MesWmProductSalesStatusEnum::getStatus).toArray(Integer[]::new); |
| | | |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.md.workstation.vo.MesMdWorkstationSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkshopDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.md.workstation.MesMdWorkstationDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseLocationDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.md.workstation.MesMdWorkstationMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.BarcodeBizTypeEnum; |
| | | import cn.iocoder.yudao.module.mes.service.wm.barcode.MesWmBarcodeService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseLocationService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.process.MesProProcessService; |
| | | import jakarta.annotation.Resource; |
| | |
| | | private MesMdWorkshopService workshopService; |
| | | @Resource |
| | | private MesWmWarehouseService warehouseService; |
| | | @Resource |
| | | private MesWmWarehouseLocationService locationService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService areaService; |
| | | @Resource |
| | | private MesWmBarcodeService barcodeService; |
| | | @Resource |
| | |
| | | } |
| | | |
| | | /** |
| | | * æ ¡éªå¹¶å¤çä»åºå±çº§å
³ç³» |
| | | * |
| | | * ä»åºå±çº§ç»æï¼ä»åº -> åºåº -> åºä½ |
| | | * å¤çè§åï¼ |
| | | * 1. 妿ä»åº/åºåº/åºä½é½æªå¡«åï¼åèªå¨è®¾ç½®èæçº¿è¾¹åº |
| | | * 2. 妿填åäºåºåºï¼å¿
须填åä»åºï¼ä¸åºåºå¿
é¡»å±äºè¯¥ä»åº |
| | | * 3. 妿填åäºåºä½ï¼å¿
须填ååºåºï¼ä¸åºä½å¿
é¡»å±äºè¯¥åºåº |
| | | * å¤çä»åºï¼æªå¡«åä»åºæ¶èªå¨è®¾ç½®èæçº¿è¾¹ä» |
| | | * |
| | | * @param reqVO å·¥ä½ç«ä¿å请æ±å¯¹è±¡ |
| | | */ |
| | | private void handleWarehouseHierarchy(MesMdWorkstationSaveReqVO reqVO) { |
| | | // 1.1 è·åä»åºãåºåºãåºä½ ID |
| | | Long warehouseId = reqVO.getWarehouseId(); |
| | | Long locationId = reqVO.getLocationId(); |
| | | Long areaId = reqVO.getAreaId(); |
| | | // 1.2 妿é½ä¸ºç©ºï¼åèªå¨è®¾ç½®èæçº¿è¾¹åº |
| | | if (warehouseId == null && locationId == null && areaId == null) { |
| | | if (warehouseId == null) { |
| | | MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); |
| | | MesWmWarehouseLocationDO location = locationService.getWarehouseLocationByCode( |
| | | MesWmWarehouseLocationDO.WIP_VIRTUAL_LOCATION); |
| | | MesWmWarehouseAreaDO area = areaService.getWarehouseAreaByCode( |
| | | MesWmWarehouseAreaDO.WIP_VIRTUAL_AREA); |
| | | reqVO.setWarehouseId(warehouse.getId()); |
| | | reqVO.setLocationId(location.getId()); |
| | | reqVO.setAreaId(area.getId()); |
| | | return; |
| | | } |
| | | |
| | | // 2. æ ¡éªä»åºæ¯å¦åå¨ |
| | | if (warehouseId != null) { |
| | | warehouseService.validateWarehouseExists(warehouseId); |
| | | } |
| | | |
| | | // 3. æ ¡éªåºåºï¼å¦æå¡«åäºåºåºï¼å¿
须填åä»åºï¼ä¸åºåºå¿
é¡»å±äºè¯¥ä»åº |
| | | MesWmWarehouseLocationDO location; |
| | | if (locationId != null) { |
| | | // 3.1 æ ¡éªåºåºæ¯å¦åå¨ |
| | | location = locationService.validateWarehouseLocationExists(locationId); |
| | | // 3.2 æ ¡éªå¿
须填åä»åº |
| | | if (warehouseId == null) { |
| | | throw exception(WM_WAREHOUSE_REQUIRED); |
| | | } |
| | | // 3.3 æ ¡éªåºåºæ¯å¦å±äºè¯¥ä»åº |
| | | if (ObjUtil.notEqual(location.getWarehouseId(), warehouseId)) { |
| | | throw exception(WM_WAREHOUSE_LOCATION_RELATION_INVALID); |
| | | } |
| | | } |
| | | |
| | | // 4. æ ¡éªåºä½ï¼å¦æå¡«åäºåºä½ï¼å¿
须填ååºåºï¼ä¸åºä½å¿
é¡»å±äºè¯¥åºåº |
| | | if (areaId == null) { |
| | | return; |
| | | } |
| | | // 4.1 æ ¡éªåºä½æ¯å¦åå¨ |
| | | MesWmWarehouseAreaDO area = areaService.validateWarehouseAreaExists(areaId); |
| | | // 4.2 æ ¡éªå¿
须填ååºåº |
| | | if (locationId == null) { |
| | | throw exception(WM_WAREHOUSE_LOCATION_REQUIRED); |
| | | } |
| | | // 4.3 æ ¡éªåºä½æ¯å¦å±äºè¯¥åºåº |
| | | if (ObjUtil.notEqual(area.getLocationId(), locationId)) { |
| | | throw exception(WM_WAREHOUSE_AREA_RELATION_INVALID); |
| | | } |
| | | warehouseService.validateWarehouseExists(warehouseId); |
| | | } |
| | | |
| | | private void validateWorkstationCodeUnique(Long id, String code) { |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchWorkerMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.redis.MesCodeRedisDAO; |
| | | import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.line.MesProLineService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.bagcode.MesProBagCodeService; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.regex.Pattern; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| | |
| | | @Validated |
| | | @Slf4j |
| | | public class MesProBatchServiceImpl implements MesProBatchService { |
| | | |
| | | /** |
| | | * æ¹æ¬¡å·æ ¼å¼ï¼4 ä½å¹´ä»½ + 3 使µæ°´ï¼å¦ 2026001ï¼ |
| | | */ |
| | | private static final Pattern BATCH_CODE_PATTERN = Pattern.compile("^[0-9]{7}$"); |
| | | private static final int BATCH_CODE_MAX_SERIAL = 999; |
| | | |
| | | @Resource |
| | | private MesProBatchMapper batchMapper; |
| | |
| | | private MesCodeRedisDAO codeRedisDao; |
| | | |
| | | @Resource |
| | | private MesMdAutoCodeRecordService autoCodeRecordService; |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | | @Resource |
| | | private MesProLineService lineService; |
| | |
| | | validateRelations(createReqVO); |
| | | createReqVO.setItemId(resolveBindingItem(null, createReqVO)); |
| | | |
| | | // 2. çææ¹æ¬¡å·ï¼ç¼ç è§åï¼åç¼ + æ¥æ + 产线ç¼ç + æµæ°´å·ï¼ |
| | | String code = generateBatchCode(line.getCode()); |
| | | // 2. ç¡®å®æ¹æ¬¡å·ï¼äººå·¥è¾å
¥ä¼å
ï¼æªè¾å
¥åæã年份+3使µæ°´ãèªå¨çæï¼ |
| | | String code = resolveBatchCode(createReqVO.getCode(), null); |
| | | |
| | | // 3. æå
¥æ¹æ¬¡ï¼äº§çº¿ç¼ç /åç§°åä½åå¡«ï¼å¾éç»å®æçæ¶æ¹æ¬¡ç´æ¥è¿å
¥ç产ä¸ï¼ |
| | | boolean hasBindings = hasBindings(createReqVO); |
| | |
| | | if (StrUtil.isNotEmpty(batch.getCode())) { |
| | | throw exception(PRO_BATCH_ALREADY_FILLED); |
| | | } |
| | | // 2. æ ¡éªå
³èæ°æ®å¹¶çææ£å¼æ¹æ¬¡å· |
| | | // 2. æ ¡éªå
³èæ°æ®å¹¶ç¡®å®æ£å¼æ¹æ¬¡å·ï¼äººå·¥è¾å
¥ä¼å
ï¼æªè¾å
¥åèªå¨çæï¼ |
| | | MesProLineDO line = lineService.validateLineExistsAndEnable(fillReqVO.getLineId()); |
| | | validateRelations(fillReqVO); |
| | | fillReqVO.setItemId(resolveBindingItem(batch, fillReqVO)); |
| | | String code = generateBatchCode(line.getCode()); |
| | | String code = resolveBatchCode(fillReqVO.getCode(), null); |
| | | // 3. è½åºä¸å¡ä¿¡æ¯ï¼ä¿çä¸´æ¶æ¹æ¬¡ç ç¨äºå岿«ç 追溯ï¼äº§çº¿ç¼ç /åç§°åä½åå¡«ï¼å¾éç»å®æçæ¶æ¹æ¬¡ç´æ¥è¿å
¥ç产ä¸ï¼ |
| | | boolean hasBindings = hasBindings(fillReqVO); |
| | | MesProBatchDO updateObj = BeanUtils.toBean(fillReqVO, MesProBatchDO.class) |
| | |
| | | validateRelations(updateReqVO); |
| | | updateReqVO.setItemId(resolveBindingItem(oldBatch, updateReqVO)); |
| | | |
| | | // 3. æªçææ£å¼æ¹æ¬¡å·æäº§çº¿ååæ¶éæ°çææ¹æ¬¡å·ï¼æ¹æ¬¡å·å
å«äº§çº¿ç¼ç è¦ç´ ï¼ |
| | | // 3. æ¹æ¬¡å·ä»
å«å¹´ä»½ä¸æµæ°´å·ï¼çæåä¸åé产线ååï¼æªçææ£å¼æ¹æ¬¡å·æ¶è¡¥çæ |
| | | boolean codeEmpty = StrUtil.isEmpty(oldBatch.getCode()); |
| | | boolean lineChanged = !Objects.equals(oldBatch.getLineId(), updateReqVO.getLineId()); |
| | | String code = oldBatch.getCode(); |
| | | String code = codeEmpty ? resolveBatchCode(updateReqVO.getCode(), oldBatch.getId()) : oldBatch.getCode(); |
| | | MesProBatchDO updateObj = BeanUtils.toBean(updateReqVO, MesProBatchDO.class); |
| | | if (codeEmpty || lineChanged) { |
| | | // åæ´äº§çº¿æ¶ï¼ç®æ 产线å¿
é¡»å¯ç¨ï¼æªåæ´åä»
æ ¡éªåå¨ï¼é¿å
åç¨äº§çº¿çå岿¹æ¬¡æ æ³ç¼è¾å
¶ä»åæ®µï¼ |
| | | MesProLineDO line = (lineChanged || codeEmpty) |
| | | ? lineService.validateLineExistsAndEnable(updateReqVO.getLineId()) |
| | | : lineService.validateLineExists(updateReqVO.getLineId()); |
| | | code = generateBatchCode(line.getCode()); |
| | | if (lineChanged || codeEmpty) { |
| | | // åæ´äº§çº¿æ¶ï¼ç®æ 产线å¿
é¡»å¯ç¨ |
| | | MesProLineDO line = lineService.validateLineExistsAndEnable(updateReqVO.getLineId()); |
| | | updateObj.setLineCode(line.getCode()).setLineName(line.getName()); |
| | | } |
| | | // 4. æ´æ°æ¹æ¬¡ï¼å¾éç»å®æçæ¶æ¹æ¬¡ç´æ¥è¿å
¥ç产ä¸ï¼åæ¶å¾éä¸åéï¼å·²ç»å®æçä¸å¯è§£ç»ï¼ |
| | |
| | | } |
| | | |
| | | /** |
| | | * çææ¹æ¬¡å· |
| | | * <p> |
| | | * ç¼ç è§åï¼åºå®åç¼(PC) + æ¥æ(yyyyMMdd) + è¾å
¥å符(产线ç¼ç ) + æµæ°´å·(ææ¥å¾ªç¯) |
| | | * ç¡®å®æ¹æ¬¡å·ï¼äººå·¥è¾å
¥ä¼å
ï¼æªè¾å
¥åæãå½å年份 + 3 使µæ°´ãèªå¨çæ |
| | | * |
| | | * @param lineCode 产线ç¼ç |
| | | * @return æ¹æ¬¡å· |
| | | * @param requestedCode 人工è¾å
¥çæ¹æ¬¡å·ï¼å¯ä¸ºç©º |
| | | * @param selfId å½åæ¹æ¬¡ç¼å·ï¼æ´æ°æ¶æé¤èªèº«ï¼æ°å¢ä¼ nullï¼ |
| | | * @return æç»æ¹æ¬¡å· |
| | | */ |
| | | private String generateBatchCode(String lineCode) { |
| | | String inputChar = StrUtil.emptyToDefault(lineCode, ""); |
| | | return autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.PRO_BATCH_CODE.getCode(), inputChar); |
| | | private String resolveBatchCode(String requestedCode, Long selfId) { |
| | | if (StrUtil.isBlank(requestedCode)) { |
| | | return generateNextBatchCode(); |
| | | } |
| | | String code = StrUtil.trim(requestedCode); |
| | | validateBatchCode(code, selfId); |
| | | return code; |
| | | } |
| | | |
| | | /** |
| | | * æ ¡éªæ¹æ¬¡å·æ ¼å¼ï¼4 ä½å¹´ä»½ + 3 使µæ°´ï¼å¦ 2026001ï¼åå¯ä¸æ§ |
| | | */ |
| | | private void validateBatchCode(String code, Long selfId) { |
| | | if (!BATCH_CODE_PATTERN.matcher(code).matches()) { |
| | | throw exception(PRO_BATCH_CODE_INVALID); |
| | | } |
| | | int year = Integer.parseInt(code.substring(0, 4)); |
| | | int serial = Integer.parseInt(code.substring(4)); |
| | | if (year < 2000 || year > 2100 || serial < 1) { |
| | | throw exception(PRO_BATCH_CODE_INVALID); |
| | | } |
| | | MesProBatchDO existing = batchMapper.selectByCodeIgnoreDeleted(code); |
| | | if (existing != null && !Objects.equals(existing.getId(), selfId)) { |
| | | throw exception(PRO_BATCH_CODE_DUPLICATE, code); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * çæå½å年份çä¸ä¸ä¸ªæ¹æ¬¡å· |
| | | */ |
| | | private String generateNextBatchCode() { |
| | | String year = String.valueOf(LocalDate.now().getYear()); |
| | | Integer maxSerial = batchMapper.selectMaxCodeSerialByYear(year); |
| | | int serial = codeRedisDao.allocateBatchNo(year, maxSerial); |
| | | if (serial > BATCH_CODE_MAX_SERIAL) { |
| | | throw exception(PRO_BATCH_CODE_EXHAUSTED); |
| | | } |
| | | return year + String.format("%03d", serial); |
| | | } |
| | | |
| | | /** |
| | |
| | | List<MesProPalletDO> getPalletListByBatchId(Long batchId); |
| | | |
| | | /** |
| | | * æ¹éè·å¾å¤ä¸ªæ¹æ¬¡çæçç å表 |
| | | * |
| | | * @param batchIds æ¹æ¬¡ç¼å·å表 |
| | | * @return æçç å表 |
| | | */ |
| | | List<MesProPalletDO> getPalletListByBatchIds(Collection<Long> batchIds); |
| | | |
| | | /** |
| | | * å页è·å¾æªç»å®çäº§æ¹æ¬¡çä¸´æ¶æçï¼ä¾æ¹æ¬¡æ°å¢/è¡¥å½ç»å®æçä¸æä½¿ç¨ï¼ |
| | | * |
| | | * @param reqVO å页ä¸å
³é®åæ¡ä»¶ |
| | |
| | | } |
| | | |
| | | @Override |
| | | public List<MesProPalletDO> getPalletListByBatchIds(Collection<Long> batchIds) { |
| | | if (CollUtil.isEmpty(batchIds)) { |
| | | return List.of(); |
| | | } |
| | | return palletMapper.selectListByBatchIds(batchIds); |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<MesProPalletDO> getUnboundPalletPage(MesProPalletUnboundPageReqVO reqVO) { |
| | | return palletMapper.selectUnboundPage(reqVO); |
| | | } |
| | |
| | | package cn.iocoder.yudao.module.mes.service.pro.scan; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventCreateReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.pro.scan.vo.MesProScanEventPageReqVO; |
| | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.HashMap; |
| | | import java.util.HashSet; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProBagCodeStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProPalletStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.enums.pro.MesProBatchStatusEnum; |
| | |
| | | if (batch == null) return failed(event, "MES_SCAN_BATCH_NOT_EXISTS", "çäº§æ¹æ¬¡ä¸åå¨"); |
| | | batchId = batch.getId(); |
| | | targetId = pallet.getId(); |
| | | event.setPalletId(pallet.getId()) |
| | | .setPalletCode(StrUtil.blankToDefault(pallet.getCode(), pallet.getTempCode())); |
| | | quantity = BigDecimal.valueOf(bagCodeMapper.selectPendingInboundCountByPalletId(pallet.getId())); |
| | | } else if (SCAN_TYPE_BATCH.equals(scanType)) { |
| | | batch = batchMapper.selectByScanCodeForUpdate(scanCode); |
| | |
| | | if (MesProBagCodeStatusEnum.CANCELED.getStatus().equals(bag.getStatus())) |
| | | return failed(event, "MES_SCAN_BAG_CANCELED", "è¢ç å·²ä½åºï¼ä¸å¯å
¥åº"); |
| | | event.setBagCodeId(bag.getId()); |
| | | if (bag.getPalletId() != null) { |
| | | event.setPalletId(bag.getPalletId()).setPalletCode(bag.getPalletCode()); |
| | | } |
| | | bagCodeId = bag.getId(); |
| | | // åºåæµæ°´ä¿æå追溯ç 为è§èç ï¼ä¸å¡ç ä»
ç¨äºæ¥æ¾åä¸è¢ï¼é¿å
åç ææä¸¤ä»½åºåã |
| | | bagCode = bag.getCode(); |
| | |
| | | return failed(event, "MES_SCAN_BATCH_CANCELED", "çäº§æ¹æ¬¡å·²ä½åºï¼ä¸å¯å
¥åº"); |
| | | if (quantity.signum() <= 0) |
| | | return failed(event, "MES_SCAN_NO_PENDING_BAGS", "没æå¾
å
¥åºçææè¢ç ï¼è¯·å¿éå¤å
¥åº"); |
| | | event.setBatchId(batchId).setBatchCode(batch.getCode()).setItemId(batch.getItemId()).setQuantity(quantity); |
| | | // æ¹æ¬¡æªè¡¥å½æ£å¼æ¹æ¬¡å·æ¶åéä¸´æ¶æ¹æ¬¡ç ï¼ä¿è¯ä»»ä½æ«ç ç±»åçäºä»¶é½å¸¦åºæ¹æ¬¡ç |
| | | event.setBatchId(batchId).setBatchCode(StrUtil.blankToDefault(batch.getCode(), batch.getTempCode())) |
| | | .setItemId(batch.getItemId()).setQuantity(quantity); |
| | | MesWmWarehouseDO warehouse = warehouseService.getWarehouseByCode(MesWmWarehouseDO.WIP_VIRTUAL_WAREHOUSE); |
| | | if (warehouse == null) return failed(event, "MES_SCAN_WIP_WAREHOUSE_NOT_EXISTS", "WIPèæä»ä¸åå¨"); |
| | | event.setWarehouseId(warehouse.getId()).setLocationId(req.getLocationId()).setAreaId(req.getAreaId()); |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 为æ«ç äºä»¶è¡¥å
æçç ï¼æçç äºä»¶ææ«æå
容忥æçï¼è¢ç äºä»¶åæå±æçï¼ |
| | | * æ¹æ¬¡äºä»¶æ´æ¹å¯è½è·¨å¤ä¸ªæçï¼ä¸å½å±å䏿çï¼ä¿æä¸ºç©ºã |
| | | * <p> |
| | | * æçç ä¸è½åºï¼è¯»åæ¶å®æ¶åæ¥ï¼ä¿è¯åå²äºä»¶ä¹è½å±ç¤ºã |
| | | */ |
| | | private void fillPalletInfo(List<MesProScanEventDO> events) { |
| | | if (CollUtil.isEmpty(events)) { |
| | | return; |
| | | } |
| | | // 1. æçç äºä»¶ï¼æ«æå
容å¯è½æ¯ä¸å¡ç ãæ£å¼ç æä¸´æ¶ç ï¼ç»ä¸åæ¥ |
| | | List<String> palletScanCodes = events.stream() |
| | | .filter(event -> SCAN_TYPE_PALLET.equals(event.getScanType()) && StrUtil.isNotBlank(event.getBagCode())) |
| | | .map(MesProScanEventDO::getBagCode).distinct().toList(); |
| | | Map<String, MesProPalletDO> palletByScanCode = new HashMap<>(); |
| | | if (CollUtil.isNotEmpty(palletScanCodes)) { |
| | | for (MesProPalletDO pallet : palletMapper.selectListByScanCodes(palletScanCodes)) { |
| | | if (StrUtil.isNotBlank(pallet.getBusinessCode())) { |
| | | palletByScanCode.put(pallet.getBusinessCode(), pallet); |
| | | } |
| | | if (StrUtil.isNotBlank(pallet.getCode())) { |
| | | palletByScanCode.put(pallet.getCode(), pallet); |
| | | } |
| | | if (StrUtil.isNotBlank(pallet.getTempCode())) { |
| | | palletByScanCode.put(pallet.getTempCode(), pallet); |
| | | } |
| | | } |
| | | } |
| | | // 2. è¢ç äºä»¶ï¼æè¢ç ç¼å·æç å¼åæ¥è¢ç ï¼ååæå±æç |
| | | List<Long> bagIds = events.stream() |
| | | .filter(event -> SCAN_TYPE_BAG.equals(event.getScanType())) |
| | | .map(MesProScanEventDO::getBagCodeId).filter(Objects::nonNull).distinct().toList(); |
| | | List<String> bagScanCodes = events.stream() |
| | | .filter(event -> SCAN_TYPE_BAG.equals(event.getScanType()) && StrUtil.isNotBlank(event.getBagCode())) |
| | | .map(MesProScanEventDO::getBagCode).distinct().toList(); |
| | | Map<Long, MesProBagCodeDO> bagById = new HashMap<>(); |
| | | Map<String, MesProBagCodeDO> bagByScanCode = new HashMap<>(); |
| | | if (CollUtil.isNotEmpty(bagIds)) { |
| | | for (MesProBagCodeDO bag : bagCodeMapper.selectBatchIds(bagIds)) { |
| | | bagById.put(bag.getId(), bag); |
| | | } |
| | | } |
| | | if (CollUtil.isNotEmpty(bagScanCodes)) { |
| | | for (MesProBagCodeDO bag : bagCodeMapper.selectListByScanCodes(bagScanCodes)) { |
| | | bagByScanCode.putIfAbsent(bag.getCode(), bag); |
| | | if (StrUtil.isNotBlank(bag.getBusinessCode())) { |
| | | bagByScanCode.putIfAbsent(bag.getBusinessCode(), bag); |
| | | } |
| | | } |
| | | } |
| | | // 3. è¢ç å使çç 为空æ¶ï¼åæ¥æç忣å¼ç /临æ¶ç |
| | | Set<Long> missingPalletIds = new HashSet<>(); |
| | | for (MesProBagCodeDO bag : bagById.values()) { |
| | | if (bag.getPalletId() != null && StrUtil.isBlank(bag.getPalletCode())) { |
| | | missingPalletIds.add(bag.getPalletId()); |
| | | } |
| | | } |
| | | for (MesProBagCodeDO bag : bagByScanCode.values()) { |
| | | if (bag.getPalletId() != null && StrUtil.isBlank(bag.getPalletCode())) { |
| | | missingPalletIds.add(bag.getPalletId()); |
| | | } |
| | | } |
| | | Map<Long, MesProPalletDO> palletById = new HashMap<>(); |
| | | if (CollUtil.isNotEmpty(missingPalletIds)) { |
| | | for (MesProPalletDO pallet : palletMapper.selectBatchIds(missingPalletIds)) { |
| | | palletById.put(pallet.getId(), pallet); |
| | | } |
| | | } |
| | | for (MesProScanEventDO event : events) { |
| | | if (StrUtil.isNotBlank(event.getPalletCode())) { |
| | | continue; |
| | | } |
| | | if (SCAN_TYPE_PALLET.equals(event.getScanType())) { |
| | | MesProPalletDO pallet = palletByScanCode.get(event.getBagCode()); |
| | | if (pallet != null) { |
| | | event.setPalletId(pallet.getId()) |
| | | .setPalletCode(StrUtil.blankToDefault(pallet.getCode(), pallet.getTempCode())); |
| | | } |
| | | } else if (SCAN_TYPE_BAG.equals(event.getScanType())) { |
| | | MesProBagCodeDO bag = event.getBagCodeId() != null |
| | | ? bagById.get(event.getBagCodeId()) : bagByScanCode.get(event.getBagCode()); |
| | | if (bag == null || bag.getPalletId() == null) { |
| | | continue; |
| | | } |
| | | event.setPalletId(bag.getPalletId()); |
| | | if (StrUtil.isNotBlank(bag.getPalletCode())) { |
| | | event.setPalletCode(bag.getPalletCode()); |
| | | } else { |
| | | MesProPalletDO pallet = palletById.get(bag.getPalletId()); |
| | | if (pallet != null) { |
| | | event.setPalletCode(StrUtil.blankToDefault(pallet.getCode(), pallet.getTempCode())); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void fillPalletInfo(MesProScanEventDO event) { |
| | | if (event != null) { |
| | | fillPalletInfo(List.of(event)); |
| | | } |
| | | } |
| | | |
| | | private MesProScanEventDO failed(MesProScanEventDO event, String code, String message) { |
| | | event.setStatus(MesProScanEventStatusEnum.FAILED).setErrorCode(code).setErrorMessage(message) |
| | | .setFailedTime(LocalDateTime.now()).setEventTime(LocalDateTime.now()); |
| | |
| | | return event; |
| | | } |
| | | |
| | | @Override public MesProScanEventDO getByEventId(String eventId) { return eventMapper.selectOne(MesProScanEventDO::getEventId, eventId); } |
| | | @Override public PageResult<MesProScanEventDO> getPage(MesProScanEventPageReqVO reqVO) { return eventMapper.selectPage(reqVO); } |
| | | @Override public MesProScanEventDO getLatest(String deviceCode) { return eventMapper.selectLatest(deviceCode); } |
| | | @Override public List<MesProScanEventDO> getByBatchId(Long batchId) { return eventMapper.selectListByBatchId(batchId); } |
| | | @Override public MesProScanEventDO getByEventId(String eventId) { |
| | | MesProScanEventDO event = eventMapper.selectOne(MesProScanEventDO::getEventId, eventId); |
| | | fillPalletInfo(event); |
| | | return event; |
| | | } |
| | | @Override public PageResult<MesProScanEventDO> getPage(MesProScanEventPageReqVO reqVO) { |
| | | PageResult<MesProScanEventDO> page = eventMapper.selectPage(reqVO); |
| | | fillPalletInfo(page.getList()); |
| | | return page; |
| | | } |
| | | @Override public MesProScanEventDO getLatest(String deviceCode) { |
| | | MesProScanEventDO event = eventMapper.selectLatest(deviceCode); |
| | | fillPalletInfo(event); |
| | | return event; |
| | | } |
| | | @Override public List<MesProScanEventDO> getByBatchId(Long batchId) { |
| | | List<MesProScanEventDO> events = eventMapper.selectListByBatchId(batchId); |
| | | fillPalletInfo(events); |
| | | return events; |
| | | } |
| | | @Override public Map<String, Object> getDashboard() { |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("latest", eventMapper.selectLatest(null)); |
| | | MesProScanEventDO latest = eventMapper.selectLatest(null); |
| | | fillPalletInfo(latest); |
| | | result.put("latest", latest); |
| | | result.put("total", eventMapper.selectCount(null)); |
| | | result.put("success", eventMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MesProScanEventDO>().eq(MesProScanEventDO::getStatus, MesProScanEventStatusEnum.SUCCESS))); |
| | | result.put("failed", eventMapper.selectCount(new com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper<MesProScanEventDO>().eq(MesProScanEventDO::getStatus, MesProScanEventStatusEnum.FAILED))); |
| | |
| | | package cn.iocoder.yudao.module.mes.service.qc.seedquality; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedIndicatorRangeSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedIndicatorRangeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.qc.seedquality.MesQcSeedIndicatorRangeMapper; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Set; |
| | | import java.math.BigDecimal; |
| | | import java.util.stream.Collectors; |
| | | import java.util.Map; |
| | | |
| | | @Service |
| | | public class MesQcSeedIndicatorRangeService { |
| | | private static final Set<String> CODES = Set.of("purity", "cleanliness", "germination_rate", "moisture"); |
| | | @Resource private MesQcSeedIndicatorRangeMapper mapper; |
| | | |
| | | public List<MesQcSeedIndicatorRangeDO> getRanges() { |
| | | return mapper.selectList().stream().filter(item -> CODES.contains(item.getIndicatorCode())).toList(); |
| | | @Resource |
| | | private MesQcSeedIndicatorRangeMapper mapper; |
| | | @Resource |
| | | private MesMdmItemService mdmItemService; |
| | | |
| | | /** é
置页æ¥è¯¢ï¼itemId 为空表示éç¨é»è®¤é¡¹ */ |
| | | public List<MesQcSeedIndicatorRangeDO> getRanges(Long itemId, String keyword, Integer status) { |
| | | return mapper.selectListByItemId(itemId, keyword, status); |
| | | } |
| | | |
| | | /** |
| | | * çæææ ï¼äº§åä¸å±é
ç½®ä¼å
ï¼æªé
ç½®æ¶åééç¨é»è®¤é¡¹ã |
| | | */ |
| | | public List<MesQcSeedIndicatorRangeDO> getEffectiveRanges(Long itemId) { |
| | | List<MesQcSeedIndicatorRangeDO> productRanges = itemId == null ? List.of() : mapper.selectEnabledByItemId(itemId); |
| | | return productRanges.isEmpty() ? mapper.selectEnabledDefaults() : productRanges; |
| | | } |
| | | |
| | | public Map<String, MesQcSeedIndicatorRangeDO> getEffectiveMap(Long itemId) { |
| | | Map<String, MesQcSeedIndicatorRangeDO> result = new LinkedHashMap<>(); |
| | | for (MesQcSeedIndicatorRangeDO range : getEffectiveRanges(itemId)) { |
| | | result.put(range.getIndicatorCode(), range); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void saveRanges(MesQcSeedIndicatorRangeSaveReqVO request) { |
| | | List<MesQcSeedIndicatorRangeSaveReqVO.Item> items = request.getRanges(); |
| | | if (items == null || items.size() != CODES.size() || items.stream().anyMatch(item -> item == null || item.getIndicatorCode() == null |
| | | || item.getMinValue() == null || item.getMaxValue() == null) |
| | | || !items.stream().map(MesQcSeedIndicatorRangeSaveReqVO.Item::getIndicatorCode) |
| | | .collect(Collectors.toSet()).equals(CODES)) |
| | | throw new IllegalArgumentException("å¿
须宿´é
置纯度ãå度ãåè½çãæ°´ååé¡¹ææ "); |
| | | for (MesQcSeedIndicatorRangeSaveReqVO.Item item : items) { |
| | | if (item.getMinValue().compareTo(BigDecimal.ZERO) < 0 || item.getMaxValue().compareTo(new BigDecimal("100")) > 0 |
| | | || item.getMinValue().compareTo(item.getMaxValue()) > 0) |
| | | throw new IllegalArgumentException("ææ åæ ¼èå´å¿
é¡»å¤äº 0â100 ä¸ä¸éä¸å¤§äºä¸éï¼" + item.getIndicatorCode()); |
| | | MesQcSeedIndicatorRangeDO row = new MesQcSeedIndicatorRangeDO(); |
| | | row.setIndicatorCode(item.getIndicatorCode()); |
| | | row.setMinValue(item.getMinValue()); |
| | | row.setMaxValue(item.getMaxValue()); |
| | | if (mapper.selectById(item.getIndicatorCode()) == null) mapper.insert(row); |
| | | else mapper.updateById(row); |
| | | } |
| | | public Long create(MesQcSeedIndicatorRangeSaveReqVO reqVO) { |
| | | validateProduct(reqVO.getItemId()); |
| | | validateRange(reqVO); |
| | | if (mapper.selectByItemIdAndCode(reqVO.getItemId(), reqVO.getIndicatorCode()) != null) |
| | | throw new IllegalArgumentException("该产åå·²åå¨ç¸åç¼ç çè´¨æ£é¡¹ï¼" + reqVO.getIndicatorCode()); |
| | | MesQcSeedIndicatorRangeDO row = buildRow(reqVO); |
| | | mapper.insert(row); |
| | | return row.getId(); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void update(Long id, MesQcSeedIndicatorRangeSaveReqVO reqVO) { |
| | | MesQcSeedIndicatorRangeDO existing = mapper.selectById(id); |
| | | if (existing == null) throw new IllegalArgumentException("è´¨æ£é¡¹ä¸å卿已å é¤"); |
| | | validateProduct(reqVO.getItemId()); |
| | | validateRange(reqVO); |
| | | MesQcSeedIndicatorRangeDO sameCode = mapper.selectByItemIdAndCode(reqVO.getItemId(), reqVO.getIndicatorCode()); |
| | | if (sameCode != null && !sameCode.getId().equals(id)) |
| | | throw new IllegalArgumentException("该产åå·²åå¨ç¸åç¼ç çè´¨æ£é¡¹ï¼" + reqVO.getIndicatorCode()); |
| | | MesQcSeedIndicatorRangeDO update = buildRow(reqVO); |
| | | update.setId(id); |
| | | mapper.updateById(update); |
| | | } |
| | | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void delete(Long id) { |
| | | MesQcSeedIndicatorRangeDO existing = mapper.selectById(id); |
| | | if (existing == null) throw new IllegalArgumentException("è´¨æ£é¡¹ä¸å卿已å é¤"); |
| | | mapper.deleteById(id); |
| | | } |
| | | |
| | | /** å°éç¨é»è®¤é¡¹å¤å¶å°æå®äº§åï¼å·²åå¨çç¼ç è·³è¿ */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public int copyDefaults(Long itemId) { |
| | | validateProduct(itemId); |
| | | int copied = 0; |
| | | for (MesQcSeedIndicatorRangeDO source : mapper.selectEnabledDefaults()) { |
| | | if (mapper.selectByItemIdAndCode(itemId, source.getIndicatorCode()) != null) continue; |
| | | MesQcSeedIndicatorRangeDO row = new MesQcSeedIndicatorRangeDO(); |
| | | row.setItemId(itemId); |
| | | row.setIndicatorCode(source.getIndicatorCode()); |
| | | row.setIndicatorName(source.getIndicatorName()); |
| | | row.setUnit(source.getUnit()); |
| | | row.setMinValue(source.getMinValue()); |
| | | row.setMaxValue(source.getMaxValue()); |
| | | row.setSort(source.getSort()); |
| | | row.setStatus(0); |
| | | mapper.insert(row); |
| | | copied++; |
| | | } |
| | | return copied; |
| | | } |
| | | |
| | | private void validateProduct(Long itemId) { |
| | | if (itemId == null) throw new IllegalArgumentException("请å
éæ©äº§å"); |
| | | if (mdmItemService.getItem(itemId) == null) throw new IllegalArgumentException("产åä¸åå¨ï¼è¯·éæ°éæ©"); |
| | | } |
| | | |
| | | private void validateRange(MesQcSeedIndicatorRangeSaveReqVO reqVO) { |
| | | if (reqVO.getMinValue().compareTo(reqVO.getMaxValue()) > 0) |
| | | throw new IllegalArgumentException("åæ ¼ä¸éä¸è½å¤§äºä¸éï¼" + reqVO.getIndicatorName()); |
| | | } |
| | | |
| | | private MesQcSeedIndicatorRangeDO buildRow(MesQcSeedIndicatorRangeSaveReqVO reqVO) { |
| | | MesQcSeedIndicatorRangeDO row = new MesQcSeedIndicatorRangeDO(); |
| | | row.setItemId(reqVO.getItemId()); |
| | | row.setIndicatorCode(reqVO.getIndicatorCode().trim()); |
| | | row.setIndicatorName(reqVO.getIndicatorName().trim()); |
| | | row.setUnit(StrUtil.blankToDefault(reqVO.getUnit(), "%")); |
| | | row.setMinValue(reqVO.getMinValue()); |
| | | row.setMaxValue(reqVO.getMaxValue()); |
| | | row.setSort(reqVO.getSort() == null ? 0 : reqVO.getSort()); |
| | | row.setStatus(reqVO.getStatus() == null ? 0 : reqVO.getStatus()); |
| | | return row; |
| | | } |
| | | } |
| | |
| | | |
| | | java.util.Map<String, Object> resolveScan(String code); |
| | | java.util.Map<String, Object> resolveScan(String code, String scanType); |
| | | java.util.Map<String, Object> resolveScan(String code, String scanType, Integer qcLine); |
| | | java.util.Map<String, Object> submitGroup(cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityGroupSubmitReqVO reqVO); |
| | | void retest(Long id, cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityRetestReqVO reqVO); |
| | | void retestBatch(cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityBatchRetestReqVO reqVO); |
| | | java.util.Map<String, Object> resolveRetestScope(String code, String scanType); |
| | | java.util.Map<String, Object> resolveRetestScope(String code, String scanType, Integer qcLine); |
| | | java.util.Map<String, Object> retestScope(cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityScopeRetestReqVO reqVO); |
| | | void processApprove(Long id, boolean approved, String opinion); |
| | | void companyApprove(Long id, boolean approved, String opinion); |
| | | void processApproveBatch(List<Long> ids, boolean approved, String opinion); |
| | | void companyApproveBatch(List<Long> ids, boolean approved, String opinion); |
| | | |
| | | /** è§£æç»éåéè´§åçå¾
è´¨æ£ä¿¡æ¯ */ |
| | | java.util.Map<String, Object> resolveReturnQuality(Long returnSalesId); |
| | | /** æäº¤ç»éåéè´§è´¨æ£ */ |
| | | Long submitReturnQuality(cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedReturnQualitySubmitReqVO reqVO); |
| | | /** ç»éåéè´§è´¨æ£éå®é¨é¢å¯¼å®¡æ¹ */ |
| | | void returnApprove(Long id, boolean approved, String opinion); |
| | | /** 管çå卿µç¨å®ä¹åå¸åï¼å¯¹å°æªçæ BPM å®ä¾çè®°å½éè¯å起审æ¹ã */ |
| | | void retryBpmProcess(Long id); |
| | | |
| | | /** BPM åè°ï¼æ¬çº¿é¢å¯¼å®¡æ¹å®æ */ |
| | | void handleLineProcessResult(String processInstanceId, Integer qcLine, String batchCode, boolean approved, String reason); |
| | | /** BPM åè°ï¼å
¬å¸è´¨æ£å®¡æ¹å®æï¼businessKey 为çäº§æ¹æ¬¡å·ï¼å岿°æ®å¯è½ä¸ºè¢ç ï¼ */ |
| | | void handleCompanyProcessResult(String processInstanceId, String businessKey, boolean approved, String reason); |
| | | /** BPM åè°ï¼ç»éåéè´§éå®é¨å®¡æ¹å®æ */ |
| | | void handleReturnProcessResult(String processInstanceId, Long returnSalesId, boolean approved, String reason); |
| | | |
| | | PageResult<MesQcSeedQualityDO> getQualityPage(MesQcSeedQualityPageReqVO reqVO); |
| | | |
| | | /** |
| | | * æçäº§æ¹æ¬¡åç»å页æ¥è¯¢è´¨æ£è®°å½æ±æ»ï¼æªç»å®æ¹æ¬¡çè®°å½å½å
¥ç©ºæ¹æ¬¡ç»ã |
| | | * |
| | | * @param reqVO æ¥è¯¢æ¡ä»¶ï¼ä¸æç»å页ä¸è´ï¼ |
| | | * @return æ¹æ¬¡åç»åé¡µç»æ |
| | | */ |
| | | PageResult<cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityBatchRespVO> |
| | | getQualityBatchPage(MesQcSeedQualityPageReqVO reqVO); |
| | | |
| | | /** |
| | | * æ ¹æ®æ¹æ¬¡å·æ¥è¯¢è´¨æ£è®°å½ï¼ææ£éªæ¶é´ååºï¼åææ°ï¼ï¼ç¨äºå
¬å¼æº¯æºã |
| | | * ç§åè´¨æ£è¡¨æªåå§åæ¶è¿å空å表ï¼å®¹éï¼ä¸å½±å追溯ï¼ã |
| | | * |
| | |
| | | import cn.hutool.json.JSONUtil; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; |
| | | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityBatchRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualitySubmitReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedReturnQualitySubmitReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.*; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.returnsales.MesWmReturnSalesLineDO; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmReturnSalesStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.materialstock.MesWmMaterialStockDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseAreaDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.warehouse.MesWmWarehouseDO; |
| | |
| | | import cn.iocoder.yudao.module.mes.service.pro.batch.MesProBatchService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.approver.MesQcApproverService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.materialstockbag.MesWmMaterialStockBagService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.returnsales.MesWmReturnSalesLineService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.returnsales.MesWmReturnSalesService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.MesWmTransactionService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.transaction.dto.MesWmTransactionSaveReqDTO; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | |
| | | import org.springframework.jdbc.BadSqlGrammarException; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.transaction.support.TransactionSynchronization; |
| | | import org.springframework.transaction.support.TransactionSynchronizationManager; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import java.math.BigDecimal; |
| | | import java.sql.Timestamp; |
| | | import java.time.LocalDateTime; |
| | | import java.util.*; |
| | | import java.util.function.Function; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| | |
| | | |
| | | @Service @Validated @Slf4j |
| | | public class MesQcSeedQualityServiceImpl implements MesQcSeedQualityService { |
| | | private static final Set<String> REQUIRED = Set.of("purity", "cleanliness", "germination_rate", "moisture"); |
| | | private static final int MAX_BATCH_SIZE = 500, WRITE_BATCH_SIZE = 100; |
| | | private static final int WAIT_RETEST = 10, WAIT_PROCESS = 20, WAIT_INITIAL_FAIL_REVIEW = 25, WAIT_COMPANY = 30, |
| | | RELEASED = 40, WAIT_INITIAL_RECHECK = 50, WAIT_COMPANY_FAIL = 60, SCRAPPED = 70; |
| | | /** è´¨æ£çº¿ï¼1 å å·¥é¨ï¼2 éå®é¨ */ |
| | | private static final int QC_LINE_PROCESS = 1, QC_LINE_SALES = 2; |
| | | /** ä¸å¡ç±»åï¼1 ç产å
¥åºè´¨æ£ï¼2 ç»éåéè´§è´¨æ£ */ |
| | | private static final int BIZ_TYPE_PRODUCTION = 1, BIZ_TYPE_RETURN = 2; |
| | | /** æ¬çº¿é¢å¯¼å®¡æ¹æµç¨å®ä¹æ è¯ */ |
| | | private static final String LINE_PROCESS_KEY = "mes_seed_quality_line_approve"; |
| | | /** å
¬å¸è´¨æ£å®¡æ¹æµç¨å®ä¹æ è¯ */ |
| | | private static final String COMPANY_PROCESS_KEY = "mes_seed_quality_company_approve"; |
| | | /** ç»éåéè´§è´¨æ£å®¡æ¹æµç¨å®ä¹æ è¯ */ |
| | | private static final String RETURN_PROCESS_KEY = "mes_seed_return_approve"; |
| | | @Resource private MesQcSeedQualityMapper qualityMapper; |
| | | @Resource private BpmProcessInstanceApi processInstanceApi; |
| | | @Resource private MesWmReturnSalesService returnSalesService; |
| | | @Resource private MesWmReturnSalesLineService returnSalesLineService; |
| | | @Resource private MesQcApproverService approverService; |
| | | @Resource private MesQcSeedQualityActionMapper actionMapper; |
| | | @Resource private MesQcSeedQualityIndicatorMapper indicatorMapper; |
| | |
| | | @Resource private MesWmWarehouseService warehouseService; |
| | | @Resource private MesWmWarehouseLocationService warehouseLocationService; |
| | | @Resource private MesWmWarehouseAreaService warehouseAreaService; |
| | | @Resource private MesMdmItemService mdmItemService; |
| | | @Resource private MesQcSeedIndicatorRangeService rangeService; |
| | | |
| | | @Override |
| | | public PageResult<MesQcSeedQualityDO> getQualityPage(MesQcSeedQualityPageReqVO reqVO) { |
| | | resolveBagScanCode(reqVO); |
| | | return qualityMapper.selectPage(reqVO); |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<MesQcSeedQualityBatchRespVO> getQualityBatchPage(MesQcSeedQualityPageReqVO reqVO) { |
| | | resolveBagScanCode(reqVO); |
| | | int pageNo = reqVO.getPageNo() == null || reqVO.getPageNo() < 1 ? 1 : reqVO.getPageNo(); |
| | | int pageSize = reqVO.getPageSize() == null || reqVO.getPageSize() < 1 ? 10 : Math.min(reqVO.getPageSize(), 100); |
| | | long total = qualityMapper.countBatchGroups(reqVO); |
| | | if (total == 0) { |
| | | return new PageResult<>(Collections.emptyList(), 0L); |
| | | } |
| | | List<MesQcSeedQualityBatchRespVO> list = qualityMapper.selectBatchGroups(reqVO, (long) (pageNo - 1) * pageSize, pageSize) |
| | | .stream().map(this::toBatchRespVO).collect(Collectors.toList()); |
| | | // åå¡«æ¹æ¬¡ä¸åç§ä¿¡æ¯ |
| | | Set<Long> batchIds = list.stream().map(MesQcSeedQualityBatchRespVO::getBatchId) |
| | | .filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | Map<Long, MesProBatchDO> batchMap = batchIds.isEmpty() ? Collections.emptyMap() |
| | | : batchMapper.selectBatchIds(batchIds).stream() |
| | | .collect(Collectors.toMap(MesProBatchDO::getId, Function.identity(), (first, ignored) -> first)); |
| | | Map<Long, MesMdmItemDO> itemMap = mdmItemService.getItemMap(list.stream() |
| | | .map(MesQcSeedQualityBatchRespVO::getItemId).filter(Objects::nonNull).collect(Collectors.toSet())); |
| | | for (MesQcSeedQualityBatchRespVO vo : list) { |
| | | vo.setBatchLabel(StrUtil.blankToDefault(vo.getBatchCode(), "æªç»å®æ¹æ¬¡")); |
| | | MesProBatchDO batch = batchMap.get(vo.getBatchId()); |
| | | if (batch != null) { |
| | | vo.setLineName(batch.getLineName()); |
| | | vo.setProduceDate(batch.getProduceDate()); |
| | | } |
| | | MesMdmItemDO item = itemMap.get(vo.getItemId()); |
| | | if (item != null) { |
| | | vo.setItemCode(item.getCode()); |
| | | vo.setItemName(item.getName()); |
| | | } |
| | | } |
| | | return new PageResult<>(list, total); |
| | | } |
| | | |
| | | private void resolveBagScanCode(MesQcSeedQualityPageReqVO reqVO) { |
| | | if (StrUtil.isNotBlank(reqVO.getCode())) { |
| | | MesProBagCodeDO bag = bagCodeMapper.selectByScanCode(reqVO.getCode()); |
| | | if (bag != null) reqVO.setCode(bag.getCode()); |
| | | if (bag != null) { |
| | | reqVO.setCode(bag.getCode()); |
| | | } |
| | | } |
| | | return qualityMapper.selectPage(reqVO); |
| | | } |
| | | |
| | | private MesQcSeedQualityBatchRespVO toBatchRespVO(Map<String, Object> row) { |
| | | MesQcSeedQualityBatchRespVO vo = new MesQcSeedQualityBatchRespVO(); |
| | | vo.setBatchCode((String) row.get("batchCode")); |
| | | vo.setBatchId(toLong(row.get("batchId"))); |
| | | vo.setItemId(toLong(row.get("itemId"))); |
| | | vo.setTotalCount(toInt(row.get("totalCount"))); |
| | | vo.setPassCount(toInt(row.get("passCount"))); |
| | | vo.setFailCount(toInt(row.get("failCount"))); |
| | | vo.setProcessingCount(toInt(row.get("processingCount"))); |
| | | vo.setPendingRetestCount(toInt(row.get("pendingRetestCount"))); |
| | | vo.setPendingProcessReviewCount(toInt(row.get("pendingProcessReviewCount"))); |
| | | vo.setPendingCompanyReviewCount(toInt(row.get("pendingCompanyReviewCount"))); |
| | | vo.setReleasedCount(toInt(row.get("releasedCount"))); |
| | | vo.setScrappedCount(toInt(row.get("scrappedCount"))); |
| | | vo.setLastInspectedAt(toLocalDateTime(row.get("lastInspectedAt"))); |
| | | return vo; |
| | | } |
| | | |
| | | private static Long toLong(Object value) { |
| | | return value instanceof Number number ? number.longValue() : null; |
| | | } |
| | | |
| | | private static Integer toInt(Object value) { |
| | | return value instanceof Number number ? number.intValue() : 0; |
| | | } |
| | | |
| | | private static LocalDateTime toLocalDateTime(Object value) { |
| | | if (value instanceof LocalDateTime dateTime) { |
| | | return dateTime; |
| | | } |
| | | if (value instanceof Timestamp timestamp) { |
| | | return timestamp.toLocalDateTime(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | @Override |
| | |
| | | result.put("batchCode", bag.getBatchCode()); |
| | | result.put("inbound", bag.getInboundEventId() != null); |
| | | result.put("canceled", Integer.valueOf(30).equals(bag.getStatus())); |
| | | MesQcSeedQualityDO quality = qualityMapper.selectByCode(bag.getCode()); |
| | | Long itemId = bag.getItemId(); |
| | | MesMdmItemDO item = itemId == null ? null : mdmItemService.getItem(itemId); |
| | | result.put("itemId", itemId); |
| | | result.put("itemCode", item == null ? null : item.getCode()); |
| | | result.put("itemName", item == null ? null : item.getName()); |
| | | result.put("indicators", toRangePayload(itemId)); |
| | | List<MesQcSeedQualityDO> qualities = qualityMapper.selectListByCode(bag.getCode()); |
| | | MesQcSeedQualityDO quality = qualities.stream() |
| | | .filter(q -> !isReturnQuality(q)).findFirst().orElse(null); |
| | | result.put("qualityId", quality == null ? null : quality.getId()); |
| | | result.put("result", quality == null ? null : quality.getResult()); |
| | | result.put("qualityIds", qualities.stream().map(MesQcSeedQualityDO::getId).toList()); |
| | | result.put("qualityLines", qualities.stream() |
| | | .filter(q -> !isReturnQuality(q)) |
| | | .map(q -> { |
| | | Map<String, Object> line = new HashMap<>(); |
| | | line.put("id", q.getId()); |
| | | line.put("qcLine", q.getQcLine()); |
| | | line.put("workflowStatus", q.getWorkflowStatus()); |
| | | return line; |
| | | }) |
| | | .toList()); |
| | | return result; |
| | | } |
| | | |
| | |
| | | |
| | | @Override |
| | | public Map<String, Object> resolveScan(String code, String scanType) { |
| | | return resolveScan(code, scanType, QC_LINE_PROCESS); |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> resolveScan(String code, String scanType, Integer qcLine) { |
| | | requireQcLine(qcLine); |
| | | Scope scope = scope(code, scanType, false); |
| | | List<MesProBagCodeDO> effective = scope.bags().stream().filter(b -> !Integer.valueOf(30).equals(b.getStatus())).toList(); |
| | | // 䏿¬¡æäº¤çæä¸¤æ¡è´¨æ£çº¿è®°å½ï¼ä»»ä¸å·²åå¨å³è§ä¸ºè¯¥è¢å·²å®æåæ£ã |
| | | Set<String> inspected = qualityMapper.selectListByCodes(effective.stream().map(MesProBagCodeDO::getCode).toList()).stream() |
| | | .filter(q -> !isReturnQuality(q)) |
| | | .map(MesQcSeedQualityDO::getCode).collect(java.util.stream.Collectors.toSet()); |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("scanType", scanType); result.put("code", scope.code()); result.put("businessCode", scope.businessCode()); |
| | |
| | | result.put("inbound", !effective.isEmpty() && effective.stream().allMatch(b -> b.getInboundEventId() != null)); |
| | | result.put("pendingBagIds", effective.stream().filter(b -> b.getInboundEventId() != null && !inspected.contains(b.getCode())).map(MesProBagCodeDO::getId).sorted().toList()); |
| | | result.put("completedBagIds", effective.stream().filter(b -> inspected.contains(b.getCode())).map(MesProBagCodeDO::getId).sorted().toList()); |
| | | Long itemId = resolveItemId(scope); |
| | | MesMdmItemDO item = itemId == null ? null : mdmItemService.getItem(itemId); |
| | | result.put("itemId", itemId); |
| | | result.put("itemCode", item == null ? null : item.getCode()); |
| | | result.put("itemName", item == null ? null : item.getName()); |
| | | result.put("indicators", toRangePayload(itemId)); |
| | | if ("BAG".equals(scanType)) { |
| | | MesQcSeedQualityDO quality = qualityMapper.selectByCode(scope.code()); |
| | | result.put("qualityId", quality == null ? null : quality.getId()); result.put("result", quality == null ? null : quality.getResult()); |
| | | List<MesQcSeedQualityDO> qualities = qualityMapper.selectListByCode(scope.code()); |
| | | MesQcSeedQualityDO quality = qualities.stream() |
| | | .filter(q -> !isReturnQuality(q)).findFirst().orElse(null); |
| | | result.put("qualityId", quality == null ? null : quality.getId()); |
| | | result.put("result", quality == null ? null : quality.getResult()); |
| | | result.put("qualityIds", qualities.stream().map(MesQcSeedQualityDO::getId).toList()); |
| | | } |
| | | return result; |
| | | } |
| | |
| | | if (scope.canceled()) throw invalidParamException("è´¨æ£å¯¹è±¡å·²ä½åº"); |
| | | List<MesProBagCodeDO> effective = scope.bags().stream().filter(b -> !Integer.valueOf(30).equals(b.getStatus())).toList(); |
| | | if (effective.isEmpty()) throw new IllegalArgumentException("æ²¡æææè¢ç "); |
| | | // 䏿¬¡æäº¤åæ¶çæä¸¤æ¡è´¨æ£çº¿è®°å½ï¼ä»»ä¸å·²åå¨å³è§ä¸ºè¯¥è¢å·²è´¨æ£ã |
| | | Set<String> inspected = qualityMapper.selectListByCodes(effective.stream().map(MesProBagCodeDO::getCode).toList()).stream() |
| | | .filter(q -> !isReturnQuality(q)) |
| | | .map(MesQcSeedQualityDO::getCode).collect(java.util.stream.Collectors.toSet()); |
| | | List<MesProBagCodeDO> pendingPreview = effective.stream().filter(b -> b.getInboundEventId() != null && !inspected.contains(b.getCode())) |
| | | .sorted(Comparator.comparing(MesProBagCodeDO::getId)).toList(); |
| | |
| | | Set<Long> currentIds = current.bags().stream().filter(b -> !Integer.valueOf(30).equals(b.getStatus())).map(MesProBagCodeDO::getId).collect(java.util.stream.Collectors.toSet()); |
| | | if (!currentIds.containsAll(confirmed)) |
| | | throw new IllegalArgumentException("è¢ç å½å±å·²ååï¼è¯·éæ°æ«ææ ¸å¯¹"); |
| | | if (!qualityMapper.selectListByCodesForUpdate(locked.stream().map(MesProBagCodeDO::getCode).toList()).isEmpty()) |
| | | if (qualityMapper.selectListByCodesForUpdate(locked.stream().map(MesProBagCodeDO::getCode).toList()).stream() |
| | | .anyMatch(q -> !isReturnQuality(q))) |
| | | throw new IllegalArgumentException("å¾
è´¨æ£æ¸
åå·²ååæå·²å
¨é¨è´¨æ£ï¼è¯·éæ°æ«æï¼ç¦æ¢éå¤å¤ç"); |
| | | List<MesProBagCodeDO> pending = locked.stream().sorted(Comparator.comparing(MesProBagCodeDO::getId)).toList(); |
| | | submitInitialBatch(pending, req); |
| | |
| | | |
| | | private void submitInitialBatch(List<MesProBagCodeDO> bags, |
| | | cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityGroupSubmitReqVO req) { |
| | | boolean passed = validateIndicators(req.getIndicators()); |
| | | if (!passed && StrUtil.isBlank(req.getReason())) throw new IllegalArgumentException("è´¨æ£ä¸åæ ¼å¿
须填ååå "); |
| | | Long batchId = bags.get(0).getBatchId(); |
| | | if (batchId == null || bags.stream().anyMatch(bag -> !Objects.equals(batchId, bag.getBatchId()))) |
| | | throw new IllegalArgumentException("æ¹é忣åªå
许åä¸çäº§æ¹æ¬¡çè¢ç "); |
| | | MesProBatchDO batch = proBatchService.getBatch(batchId); |
| | | if (batch == null || batch.getItemId() == null) throw new IllegalArgumentException("æªæ ¹æ®è¢ç è§£æå°ç©æä¿¡æ¯"); |
| | | Map<String, IndicatorMeta> expected = requireEffectiveIndicators(batch.getItemId()); |
| | | boolean passed = validateIndicators(req.getIndicators(), expected); |
| | | if (!passed && StrUtil.isBlank(req.getReason())) throw new IllegalArgumentException("è´¨æ£ä¸åæ ¼å¿
须填ååå "); |
| | | if (Integer.valueOf(40).equals(batch.getStatus())) throw new IllegalArgumentException("çäº§æ¹æ¬¡å·²ä½åºï¼æ æ³è´¨æ£"); |
| | | MesWmBatchDO wmBatch = mesWmBatchMapper.selectByCode(batch.getCode()); |
| | | if (wmBatch == null || materialStockMapper.selectTempStockCountByBatchId(wmBatch.getId()) == 0) |
| | |
| | | if (qualifiedWarehouse == null) throw exception(WM_WAREHOUSE_NOT_EXISTS); |
| | | if (!MesWmWarehouseTypeEnum.QUALIFIED.getType().equals(qualifiedWarehouse.getType())) |
| | | throw exception(WM_WAREHOUSE_TYPE_NOT_QUALIFIED); |
| | | // æåæ ¡éªåæ ¼åºå·²é
ç½®åºåº/åºä½ï¼é¿å
夿£å®¡æ ¸æ¾è¡æ¶æå¤±è´¥ |
| | | resolveQualifiedLocation(qualifiedWarehouse); |
| | | |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | Long operatorId = getLoginUserId(); |
| | | int result = passed ? 1 : 2; |
| | | int status = passed ? WAIT_PROCESS : WAIT_INITIAL_FAIL_REVIEW; |
| | | List<MesQcSeedQualityDO> qualities = bags.stream().map(bag -> MesQcSeedQualityDO.builder() |
| | | .code(bag.getCode()).itemId(batch.getItemId()).batchId(batch.getId()).batchCode(batch.getCode()) |
| | | .result(result).reason(passed ? "" : req.getReason().trim()).workflowStatus(status) |
| | | .qualifiedWarehouseId(qualifiedWarehouse.getId()).inspectorId(operatorId).inspectedAt(now).build()).toList(); |
| | | int status = passed ? WAIT_PROCESS : WAIT_RETEST; |
| | | // 䏿¬¡æäº¤åæ¶çæå å·¥é¨ä¸éå®é¨ä¸¤æ¡è®°å½ï¼ææ ä¸ç»è®ºå®å
¨ä¸è´ã |
| | | List<MesQcSeedQualityDO> qualities = new ArrayList<>(bags.size() * 2); |
| | | for (MesProBagCodeDO bag : bags) { |
| | | for (int qcLine : new int[]{QC_LINE_PROCESS, QC_LINE_SALES}) { |
| | | qualities.add(MesQcSeedQualityDO.builder() |
| | | .code(bag.getCode()).qcLine(qcLine).bizType(BIZ_TYPE_PRODUCTION) |
| | | .itemId(batch.getItemId()).batchId(batch.getId()).batchCode(batch.getCode()) |
| | | .result(result).reason(passed ? "" : req.getReason().trim()).workflowStatus(status) |
| | | .qualifiedWarehouseId(qualifiedWarehouse.getId()).inspectorId(operatorId).inspectedAt(now).build()); |
| | | } |
| | | } |
| | | qualityMapper.insertBatch(qualities, WRITE_BATCH_SIZE); |
| | | if (qualities.stream().anyMatch(quality -> quality.getId() == null)) |
| | | throw new IllegalStateException("æ¹éåæ£è®°å½ç¼å·çæå¤±è´¥"); |
| | | |
| | | List<MesQcSeedQualityIndicatorDO> indicators = new ArrayList<>(qualities.size() * REQUIRED.size()); |
| | | List<MesQcSeedQualityIndicatorDO> indicators = new ArrayList<>(qualities.size() * expected.size()); |
| | | List<MesQcSeedQualityActionDO> actions = new ArrayList<>(qualities.size()); |
| | | for (MesQcSeedQualityDO quality : qualities) { |
| | | for (MesQcSeedQualitySubmitReqVO.Indicator item : req.getIndicators()) { |
| | | IndicatorMeta meta = expected.get(item.getCode()); |
| | | indicators.add(MesQcSeedQualityIndicatorDO.builder().qualityId(quality.getId()).indicatorCode(item.getCode()) |
| | | .indicatorName(meta.name()).unit(meta.unit()) |
| | | .value(item.getValue()).minValue(item.getMinValue()).maxValue(item.getMaxValue()).build()); |
| | | } |
| | | actions.add(newAction(quality.getId(), "INITIAL", result, req.getReason(), req.getIndicators(), operatorId, now)); |
| | | } |
| | | indicatorMapper.insertBatch(indicators, WRITE_BATCH_SIZE * REQUIRED.size()); |
| | | indicatorMapper.insertBatch(indicators, WRITE_BATCH_SIZE * expected.size()); |
| | | actionMapper.insertBatch(actions, WRITE_BATCH_SIZE); |
| | | String batchCode = batch.getCode(); |
| | | if (passed) { |
| | | afterCommit(() -> startLineProcess(QC_LINE_PROCESS, batchCode, operatorId)); |
| | | afterCommit(() -> startLineProcess(QC_LINE_SALES, batchCode, operatorId)); |
| | | } |
| | | } |
| | | |
| | | @Override @Transactional(rollbackFor = Exception.class) |
| | | public Long submit(MesQcSeedQualitySubmitReqVO reqVO) { |
| | | if (reqVO.getIndicators().size() != 4) throw new IllegalArgumentException("å¿
é¡»æäº¤åé¡¹ææ "); |
| | | // ç§åè´¨æ£å¿
须以è¢ç ï¼åè¢ï¼æäº¤ï¼ç©æä¿¡æ¯ç±è¢ç èªå¨è§£æï¼å端ä¸ååç¬éæ©ç©æ |
| | | MesProBagCodeDO bag = bagCodeMapper.selectByScanCodeForUpdate(reqVO.getCode()); |
| | | if (bag == null) { |
| | |
| | | if (Integer.valueOf(30).equals(bag.getStatus()) || bag.getInboundEventId() == null) |
| | | throw new IllegalArgumentException("è¢ç å·²ä½åºæå°æªå
¥åºï¼æ æ³è´¨æ£"); |
| | | // åç éå®åä¸è¢ï¼å¹¶ä»¥è¿½æº¯ç è®°å½ç»æï¼é²æ¢æ¢ç æå¹¶åé夿£ååºåã |
| | | if (qualityMapper.selectByCodeForUpdate(bag.getCode()) != null) throw new IllegalArgumentException("该è¢ç 已质æ£ï¼ç¦æ¢éå¤æäº¤"); |
| | | if (qualityMapper.selectListByCodeForUpdate(bag.getCode()).stream() |
| | | .anyMatch(q -> !isReturnQuality(q))) |
| | | throw new IllegalArgumentException("该è¢ç 已质æ£ï¼ç¦æ¢éå¤æäº¤"); |
| | | MesProBatchDO batch = bag.getBatchId() == null ? null : proBatchService.getBatch(bag.getBatchId()); |
| | | if (batch == null || batch.getItemId() == null) { |
| | | throw new IllegalArgumentException("æªæ ¹æ®è¢ç è§£æå°ç©æä¿¡æ¯ï¼è¯·ç¡®è®¤æ«ç å
å®¹ææ¹æ¬¡å·æ£ç¡®"); |
| | |
| | | if (!MesWmWarehouseTypeEnum.QUALIFIED.getType().equals(qualifiedWarehouse.getType())) { |
| | | throw exception(WM_WAREHOUSE_TYPE_NOT_QUALIFIED); |
| | | } |
| | | // æåæ ¡éªåæ ¼åºå·²é
ç½®åºåº/åºä½ï¼é¿å
夿£å®¡æ ¸æ¾è¡æ¶æå¤±è´¥ |
| | | resolveQualifiedLocation(qualifiedWarehouse); |
| | | Set<String> codes = new HashSet<>(); |
| | | boolean passed = true; |
| | | MesQcSeedQualityDO quality = MesQcSeedQualityDO.builder().code(bag.getCode()).itemId(batch.getItemId()) |
| | | .batchCode(batch.getCode()).batchId(batch.getId()).reason(reqVO.getReason()).result(0) |
| | | .qualifiedWarehouseId(qualifiedWarehouse.getId()).inspectorId(getLoginUserId()) |
| | | .inspectedAt(LocalDateTime.now()).build(); |
| | | qualityMapper.insert(quality); |
| | | for (MesQcSeedQualitySubmitReqVO.Indicator item : reqVO.getIndicators()) { |
| | | if (item == null || item.getCode() == null || item.getValue() == null || item.getMinValue() == null || item.getMaxValue() == null) |
| | | throw new IllegalArgumentException("åé¡¹ææ ç宿µå¼åæ¬æ¬¡åæ ¼èå´å¿
须宿´"); |
| | | if (!REQUIRED.contains(item.getCode()) || !codes.add(item.getCode())) throw new IllegalArgumentException("ææ å¿
须为纯度ãå度ãåè½çãæ°´åä¸ä¸å¯éå¤"); |
| | | if (item.getMinValue().compareTo(item.getMaxValue()) > 0) throw new IllegalArgumentException("ææ ä¸éä¸è½å¤§äºä¸é"); |
| | | if (item.getValue().compareTo(BigDecimal.ZERO) < 0 || item.getValue().compareTo(new BigDecimal("100")) > 0 |
| | | || item.getMinValue().compareTo(BigDecimal.ZERO) < 0 || item.getMaxValue().compareTo(new BigDecimal("100")) > 0) |
| | | throw new IllegalArgumentException("ç¾åæ¯ææ å¿
é¡»å¤äº 0â100"); |
| | | if (item.getValue().compareTo(item.getMinValue()) < 0 || item.getValue().compareTo(item.getMaxValue()) > 0) passed = false; |
| | | indicatorMapper.insert(MesQcSeedQualityIndicatorDO.builder().qualityId(quality.getId()).indicatorCode(item.getCode()) |
| | | .value(item.getValue()).minValue(item.getMinValue()).maxValue(item.getMaxValue()).build()); |
| | | } |
| | | if (!codes.equals(REQUIRED)) throw new IllegalArgumentException("åé¡¹ææ ä¸å®æ´"); |
| | | Map<String, IndicatorMeta> expected = requireEffectiveIndicators(batch.getItemId()); |
| | | boolean passed = validateIndicators(reqVO.getIndicators(), expected); |
| | | if (!passed && StrUtil.isBlank(reqVO.getReason())) throw new IllegalArgumentException("è´¨æ£ä¸åæ ¼å¿
须填ååå "); |
| | | quality.setResult(passed ? 1 : 2); |
| | | quality.setWorkflowStatus(passed ? WAIT_PROCESS : WAIT_INITIAL_FAIL_REVIEW); |
| | | qualityMapper.updateById(quality); |
| | | recordAction(quality.getId(), "INITIAL", quality.getResult(), reqVO.getReason(), reqVO.getIndicators()); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | Long operatorId = getLoginUserId(); |
| | | int result = passed ? 1 : 2; |
| | | int status = passed ? WAIT_PROCESS : WAIT_RETEST; |
| | | // 䏿¬¡æäº¤åæ¶çæå å·¥é¨ä¸éå®é¨ä¸¤æ¡è®°å½ï¼ææ ä¸ç»è®ºå®å
¨ä¸è´ã |
| | | Long firstQualityId = null; |
| | | for (int qcLine : new int[]{QC_LINE_PROCESS, QC_LINE_SALES}) { |
| | | MesQcSeedQualityDO quality = MesQcSeedQualityDO.builder().code(bag.getCode()) |
| | | .qcLine(qcLine).bizType(BIZ_TYPE_PRODUCTION) |
| | | .itemId(batch.getItemId()).batchCode(batch.getCode()).batchId(batch.getId()) |
| | | .reason(passed ? "" : reqVO.getReason().trim()).result(result).workflowStatus(status) |
| | | .qualifiedWarehouseId(qualifiedWarehouse.getId()).inspectorId(operatorId) |
| | | .inspectedAt(now).build(); |
| | | qualityMapper.insert(quality); |
| | | for (MesQcSeedQualitySubmitReqVO.Indicator item : reqVO.getIndicators()) { |
| | | IndicatorMeta meta = expected.get(item.getCode()); |
| | | indicatorMapper.insert(MesQcSeedQualityIndicatorDO.builder().qualityId(quality.getId()).indicatorCode(item.getCode()) |
| | | .indicatorName(meta.name()).unit(meta.unit()) |
| | | .value(item.getValue()).minValue(item.getMinValue()).maxValue(item.getMaxValue()).build()); |
| | | } |
| | | recordAction(quality.getId(), "INITIAL", result, reqVO.getReason(), reqVO.getIndicators()); |
| | | if (firstQualityId == null) firstQualityId = quality.getId(); |
| | | } |
| | | // 忣ä»
è®°å½ç»è®ºï¼è¢ä»ç卿ååºï¼å¾
åæ£å®¡æ ¸ã夿£åæç»å®¡æ ¸ååæ¾è¡ææ¥åºã |
| | | return quality.getId(); |
| | | String batchCode = batch.getCode(); |
| | | if (passed) { |
| | | afterCommit(() -> startLineProcess(QC_LINE_PROCESS, batchCode, operatorId)); |
| | | afterCommit(() -> startLineProcess(QC_LINE_SALES, batchCode, operatorId)); |
| | | } |
| | | return firstQualityId; |
| | | } |
| | | |
| | | private void finalizeStock(MesQcSeedQualityDO quality, boolean passed) { |
| | |
| | | MesQcSeedQualityDO quality = qualityMapper.selectByIdForUpdate(id); |
| | | if (quality == null || !Set.of(WAIT_RETEST, WAIT_INITIAL_RECHECK).contains(quality.getWorkflowStatus())) |
| | | throw new IllegalArgumentException("å½åç¶æä¸è½æäº¤å¤æ£ï¼è¯·å·æ°åéè¯"); |
| | | boolean returnQuality = Objects.equals(quality.getBizType(), BIZ_TYPE_RETURN); |
| | | if (returnQuality && !Objects.equals(quality.getWorkflowStatus(), WAIT_RETEST)) |
| | | throw new IllegalArgumentException("ç»éåéè´§è´¨æ£å½åç¶æä¸è½å¤æ£ï¼è¯·å·æ°åéè¯"); |
| | | boolean initialRecheck = Objects.equals(quality.getWorkflowStatus(), WAIT_INITIAL_RECHECK); |
| | | if (!Objects.equals(quality.getCode(), req.getCode())) throw new IllegalArgumentException("æ£éªè¢ç ä¸åè´¨æ£è®°å½ä¸ä¸è´"); |
| | | boolean passed = validateIndicators(req.getIndicators()); |
| | | if (!passed && StrUtil.isBlank(req.getReason())) throw new IllegalArgumentException("æ£éªä¸åæ ¼å¿
须填ååå "); |
| | | recordAction(id, initialRecheck ? "INITIAL_RECHECK" : "RETEST", passed ? 1 : 2, req.getReason(), req.getIndicators()); |
| | | MesQcSeedQualityDO update = MesQcSeedQualityDO.builder().id(id).result(passed ? 1 : 2) |
| | | .reason(passed ? "" : req.getReason().trim()) |
| | | .inspectorId(getLoginUserId()).inspectedAt(LocalDateTime.now()) |
| | | .workflowStatus(initialRecheck ? (passed ? WAIT_PROCESS : WAIT_INITIAL_FAIL_REVIEW) |
| | | : (passed ? WAIT_COMPANY : WAIT_COMPANY_FAIL)).build(); |
| | | if (!initialRecheck) { |
| | | update.setRetestResult(passed ? 1 : 2); |
| | | update.setRetestedAt(LocalDateTime.now()); |
| | | } |
| | | qualityMapper.updateById(update); |
| | | // 页é¢ãå
¬å¼è¿½æº¯è¯»åä¸»ææ è¡¨ï¼åæ¥ä¸ºææ°ä¸æ¬¡æµå¼ï¼å次æµå¼å¨æä½çç表ä¿çã |
| | | // 夿£æ²¿ç¨åè®°å½çææ éåï¼é¿å
é
ç½®åæ´å¯¼è´åå²è®°å½æ æ³å¤æ£ |
| | | Map<String, MesQcSeedQualityIndicatorDO> existing = new HashMap<>(); |
| | | for (MesQcSeedQualityIndicatorDO indicator : indicatorMapper.selectListByQualityId(id)) |
| | | existing.put(indicator.getIndicatorCode(), indicator); |
| | | boolean passed = validateIndicators(req.getIndicators(), toMetaMap(existing.values(), quality.getItemId())); |
| | | if (!passed && StrUtil.isBlank(req.getReason())) throw new IllegalArgumentException("æ£éªä¸åæ ¼å¿
须填ååå "); |
| | | recordAction(id, initialRecheck ? "INITIAL_RECHECK" : "RETEST", passed ? 1 : 2, req.getReason(), req.getIndicators()); |
| | | Long operatorId = getLoginUserId(); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | int nextStatus; |
| | | if (returnQuality) { |
| | | // 夿£ç»è®ºç»ä¸äº¤ç±éå®é¨é¢å¯¼å®¡æ¹ï¼ä¸åæ ¼æ¶å®¡æ¹åä¸äºéè´§ã |
| | | nextStatus = WAIT_PROCESS; |
| | | } else if (initialRecheck) { |
| | | nextStatus = passed ? WAIT_PROCESS : WAIT_INITIAL_FAIL_REVIEW; |
| | | } else { |
| | | nextStatus = passed ? WAIT_PROCESS : WAIT_INITIAL_FAIL_REVIEW; |
| | | } |
| | | MesQcSeedQualityDO update = MesQcSeedQualityDO.builder().id(id).result(passed ? 1 : 2) |
| | | .reason(passed ? "" : req.getReason().trim()) |
| | | .inspectorId(operatorId).inspectedAt(now) |
| | | .workflowStatus(nextStatus).build(); |
| | | if (!initialRecheck) { |
| | | update.setRetestResult(passed ? 1 : 2); |
| | | update.setRetestedAt(now); |
| | | } |
| | | qualityMapper.updateById(update); |
| | | if (returnQuality) { |
| | | afterCommit(() -> startReturnProcess(quality, operatorId)); |
| | | } else if (initialRecheck) { |
| | | afterCommit(() -> startLineProcess(quality.getQcLine(), quality.getBatchCode(), operatorId)); |
| | | } else if (!initialRecheck) { |
| | | // 忣ä¸åæ ¼ç夿£å®æåï¼å
交æ¬çº¿é¨é¨å®¡æ¹ã |
| | | afterCommit(() -> startLineProcess(quality.getQcLine(), quality.getBatchCode(), operatorId)); |
| | | } |
| | | // 页é¢ãå
¬å¼è¿½æº¯è¯»åä¸»ææ è¡¨ï¼åæ¥ä¸ºææ°ä¸æ¬¡æµå¼ï¼å次æµå¼å¨æä½çç表ä¿çã |
| | | for (MesQcSeedQualitySubmitReqVO.Indicator item : req.getIndicators()) { |
| | | MesQcSeedQualityIndicatorDO indicator = existing.get(item.getCode()); |
| | | if (indicator == null) throw new IllegalArgumentException("è´¨æ£ææ è®°å½ç¼ºå¤±"); |
| | |
| | | validateLockedIds(ids, qualities); |
| | | String batchCode = null; |
| | | Integer workflowStatus = null; |
| | | Integer batchQcLine = null; |
| | | boolean first = true; |
| | | for (MesQcSeedQualityDO quality : qualities) { |
| | | if (!Set.of(WAIT_RETEST, WAIT_INITIAL_RECHECK).contains(quality.getWorkflowStatus())) |
| | |
| | | if (first) { |
| | | batchCode = quality.getBatchCode(); |
| | | workflowStatus = quality.getWorkflowStatus(); |
| | | batchQcLine = quality.getQcLine(); |
| | | first = false; |
| | | } else if (!Objects.equals(batchCode, quality.getBatchCode()) || !Objects.equals(workflowStatus, quality.getWorkflowStatus())) { |
| | | throw new IllegalArgumentException("æ¹éè´¨æ£åªå
许åçäº§æ¹æ¬¡ãåæµç¨ç¶æçè®°å½"); |
| | | } else if (!Objects.equals(batchCode, quality.getBatchCode()) |
| | | || !Objects.equals(workflowStatus, quality.getWorkflowStatus()) |
| | | || !Objects.equals(batchQcLine, quality.getQcLine())) { |
| | | throw new IllegalArgumentException("æ¹éè´¨æ£åªå
许åçäº§æ¹æ¬¡ãåè´¨æ£çº¿ååæµç¨ç¶æçè®°å½"); |
| | | } |
| | | } |
| | | applyRetestBatch(qualities, req.getIndicators(), req.getReason()); |
| | |
| | | |
| | | @Override |
| | | public Map<String, Object> resolveRetestScope(String code, String scanType) { |
| | | return resolveRetestScope(code, scanType, QC_LINE_PROCESS); |
| | | } |
| | | |
| | | @Override |
| | | public Map<String, Object> resolveRetestScope(String code, String scanType, Integer qcLine) { |
| | | requireQcLine(qcLine); |
| | | checkRetestScanType(scanType); |
| | | Scope scope = scope(code, scanType, false); |
| | | if (scope.canceled()) throw invalidParamException("è´¨æ£å¯¹è±¡å·²ä½åº"); |
| | | List<String> codes = scope.bags().stream().filter(b -> !Integer.valueOf(30).equals(b.getStatus()) && b.getInboundEventId() != null) |
| | | .map(MesProBagCodeDO::getCode).toList(); |
| | | List<MesQcSeedQualityDO> qualities = codes.isEmpty() ? List.of() : qualityMapper.selectListByCodes(codes); |
| | | List<MesQcSeedQualityDO> qualities = codes.isEmpty() ? List.of() : qualityMapper.selectListByCodes(codes).stream() |
| | | .filter(q -> Objects.equals(q.getQcLine(), qcLine) && !isReturnQuality(q)).toList(); |
| | | List<Long> retestIds = qualities.stream().filter(q -> Objects.equals(q.getWorkflowStatus(), WAIT_RETEST)) |
| | | .map(MesQcSeedQualityDO::getId).sorted().toList(); |
| | | List<Long> initialRecheckIds = qualities.stream().filter(q -> Objects.equals(q.getWorkflowStatus(), WAIT_INITIAL_RECHECK)) |
| | |
| | | result.put("pendingCompanyIds", pendingCompanyIds); result.put("pendingCompanyFailIds", pendingCompanyFailIds); |
| | | result.put("pendingProcessCount", pendingProcessIds.size() + pendingInitialFailIds.size()); |
| | | result.put("pendingCompanyCount", pendingCompanyIds.size() + pendingCompanyFailIds.size()); |
| | | Long itemId = qualities.stream().map(MesQcSeedQualityDO::getItemId).filter(Objects::nonNull).findFirst().orElse(null); |
| | | result.put("itemId", itemId); |
| | | MesQcSeedQualityDO actionable = qualities.stream() |
| | | .filter(q -> Set.of(WAIT_RETEST, WAIT_INITIAL_RECHECK).contains(q.getWorkflowStatus())) |
| | | .findFirst().orElse(null); |
| | | result.put("indicators", actionable == null ? List.of() |
| | | : toIndicatorPayload(indicatorMapper.selectListByQualityId(actionable.getId()), itemId)); |
| | | return result; |
| | | } |
| | | |
| | | @Override @Transactional(rollbackFor = Exception.class) |
| | | public Map<String, Object> retestScope(cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityScopeRetestReqVO req) { |
| | | requireQcLine(req.getQcLine()); |
| | | checkRetestScanType(req.getScanType()); |
| | | if (!Set.of(WAIT_RETEST, WAIT_INITIAL_RECHECK).contains(req.getWorkflowStatus())) |
| | | throw new IllegalArgumentException("æ¹éè´¨æ£ç¶ææ æ"); |
| | |
| | | if (effective.isEmpty()) throw new IllegalArgumentException("å½åèå´æ²¡æå·²å
¥åºçææè¢ç "); |
| | | Set<String> effectiveCodes = effective.stream().map(MesProBagCodeDO::getCode).collect(java.util.stream.Collectors.toSet()); |
| | | List<MesQcSeedQualityDO> pendingPreview = qualityMapper.selectListByCodes(effectiveCodes).stream() |
| | | .filter(q -> Objects.equals(q.getWorkflowStatus(), req.getWorkflowStatus())) |
| | | .filter(q -> Objects.equals(q.getQcLine(), req.getQcLine()) |
| | | && !isReturnQuality(q) && Objects.equals(q.getWorkflowStatus(), req.getWorkflowStatus())) |
| | | .sorted(Comparator.comparing(MesQcSeedQualityDO::getId)).toList(); |
| | | List<Long> expectedChunk = pendingPreview.stream().limit(MAX_BATCH_SIZE).map(MesQcSeedQualityDO::getId).toList(); |
| | | if (expectedChunk.isEmpty() || !confirmed.equals(expectedChunk)) |
| | |
| | | List<MesQcSeedQualityDO> pending = qualityMapper.selectListByIdsForUpdate(confirmed); |
| | | validateLockedIds(confirmed, pending); |
| | | if (pending.stream().anyMatch(quality -> !Objects.equals(quality.getWorkflowStatus(), req.getWorkflowStatus()) |
| | | || !Objects.equals(quality.getQcLine(), req.getQcLine()) || isReturnQuality(quality) |
| | | || !effectiveCodes.contains(quality.getCode()))) |
| | | throw new IllegalArgumentException("å¾
è´¨æ£è®°å½ç¶ææå½å±å·²ååï¼è¯·éæ°æ«ææ ¸å¯¹"); |
| | | applyRetestBatch(pending, req.getIndicators(), req.getReason()); |
| | |
| | | |
| | | private void applyRetestBatch(List<MesQcSeedQualityDO> qualities, |
| | | List<MesQcSeedQualitySubmitReqVO.Indicator> submittedIndicators, String reason) { |
| | | boolean passed = validateIndicators(submittedIndicators); |
| | | if (!passed && StrUtil.isBlank(reason)) throw new IllegalArgumentException("æ£éªä¸åæ ¼å¿
须填ååå "); |
| | | List<Long> ids = qualities.stream().map(MesQcSeedQualityDO::getId).toList(); |
| | | Map<Long, Map<String, MesQcSeedQualityIndicatorDO>> existingByQuality = new HashMap<>(); |
| | | for (MesQcSeedQualityIndicatorDO indicator : indicatorMapper.selectListByQualityIds(ids)) { |
| | | existingByQuality.computeIfAbsent(indicator.getQualityId(), ignored -> new HashMap<>()) |
| | | .put(indicator.getIndicatorCode(), indicator); |
| | | } |
| | | Map<String, MesQcSeedQualityIndicatorDO> firstIndicators = existingByQuality.get(qualities.get(0).getId()); |
| | | Map<String, IndicatorMeta> expected = toMetaMap( |
| | | firstIndicators == null ? Collections.<MesQcSeedQualityIndicatorDO>emptyList() : firstIndicators.values(), |
| | | qualities.get(0).getItemId()); |
| | | boolean passed = validateIndicators(submittedIndicators, expected); |
| | | if (!passed && StrUtil.isBlank(reason)) throw new IllegalArgumentException("æ£éªä¸åæ ¼å¿
须填ååå "); |
| | | |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | Long operatorId = getLoginUserId(); |
| | | int result = passed ? 1 : 2; |
| | | List<MesQcSeedQualityDO> qualityUpdates = new ArrayList<>(qualities.size()); |
| | | List<MesQcSeedQualityIndicatorDO> indicatorUpdates = new ArrayList<>(qualities.size() * REQUIRED.size()); |
| | | List<MesQcSeedQualityIndicatorDO> indicatorUpdates = new ArrayList<>(qualities.size() * submittedIndicators.size()); |
| | | List<MesQcSeedQualityActionDO> actions = new ArrayList<>(qualities.size()); |
| | | Set<String> lineProcessTargets = new HashSet<>(); |
| | | List<MesQcSeedQualityDO> returnReady = new ArrayList<>(); |
| | | for (MesQcSeedQualityDO quality : qualities) { |
| | | boolean returnQuality = Objects.equals(quality.getBizType(), BIZ_TYPE_RETURN); |
| | | boolean initialRecheck = Objects.equals(quality.getWorkflowStatus(), WAIT_INITIAL_RECHECK); |
| | | if (!initialRecheck && !Objects.equals(quality.getWorkflowStatus(), WAIT_RETEST)) |
| | | if (returnQuality && !Objects.equals(quality.getWorkflowStatus(), WAIT_RETEST)) |
| | | throw new IllegalArgumentException("æéè®°å½å
å«ä¸å¯æ£éªçç¶æï¼è¯·å·æ°åéè¯"); |
| | | if (!returnQuality && !initialRecheck && !Objects.equals(quality.getWorkflowStatus(), WAIT_RETEST)) |
| | | throw new IllegalArgumentException("æéè®°å½å
å«ä¸å¯æ£éªçç¶æï¼è¯·å·æ°åéè¯"); |
| | | int nextStatus; |
| | | if (returnQuality) { |
| | | nextStatus = WAIT_PROCESS; |
| | | returnReady.add(quality); |
| | | } else if (initialRecheck) { |
| | | nextStatus = passed ? WAIT_PROCESS : WAIT_INITIAL_FAIL_REVIEW; |
| | | lineProcessTargets.add(quality.getQcLine() + "#" + quality.getBatchCode()); |
| | | } else { |
| | | nextStatus = passed ? WAIT_PROCESS : WAIT_INITIAL_FAIL_REVIEW; |
| | | lineProcessTargets.add(quality.getQcLine() + "#" + quality.getBatchCode()); |
| | | } |
| | | MesQcSeedQualityDO update = MesQcSeedQualityDO.builder().id(quality.getId()).result(result) |
| | | .reason(passed ? "" : reason.trim()).inspectorId(operatorId).inspectedAt(now) |
| | | .workflowStatus(initialRecheck ? (passed ? WAIT_PROCESS : WAIT_INITIAL_FAIL_REVIEW) |
| | | : (passed ? WAIT_COMPANY : WAIT_COMPANY_FAIL)).build(); |
| | | .workflowStatus(nextStatus).build(); |
| | | if (!initialRecheck) { |
| | | update.setRetestResult(result); |
| | | update.setRetestedAt(now); |
| | |
| | | reason, submittedIndicators, operatorId, now)); |
| | | } |
| | | qualityMapper.updateBatch(qualityUpdates, WRITE_BATCH_SIZE); |
| | | indicatorMapper.updateBatch(indicatorUpdates, WRITE_BATCH_SIZE * REQUIRED.size()); |
| | | indicatorMapper.updateBatch(indicatorUpdates, WRITE_BATCH_SIZE * submittedIndicators.size()); |
| | | actionMapper.insertBatch(actions, WRITE_BATCH_SIZE); |
| | | // 夿£æè®°å½æå±è´¨æ£çº¿åå«åèµ·æ¬çº¿å®¡æ¹ï¼é¿å
æå触åå¦ä¸æ¡çº¿ã |
| | | lineProcessTargets.forEach(target -> { |
| | | String[] parts = target.split("#", 2); |
| | | afterCommit(() -> startLineProcess(Integer.valueOf(parts[0]), parts[1], operatorId)); |
| | | }); |
| | | returnReady.forEach(quality -> afterCommit(() -> startReturnProcess(quality, operatorId))); |
| | | } |
| | | |
| | | @Override @Transactional(rollbackFor = Exception.class) |
| | |
| | | MesQcSeedQualityDO quality = qualityMapper.selectByIdForUpdate(id); |
| | | if (quality == null || !Set.of(WAIT_PROCESS, WAIT_INITIAL_FAIL_REVIEW).contains(quality.getWorkflowStatus())) |
| | | throw new IllegalArgumentException("å½åç¶æä¸è½è¿è¡åæ£å®¡æ ¸"); |
| | | approverService.validateApprover(MesQcApprovalTypeEnum.SEED_QUALITY.getType(), |
| | | MesQcApproveNodeEnum.PROCESS.getNode(), getLoginUserId()); |
| | | requireLegacyApproval(quality); |
| | | applyProcessApproval(quality, approved, opinion, true); |
| | | } |
| | | |
| | | /** |
| | | * æ¬çº¿é¢å¯¼å®¡æ¹ï¼ç产质æ£éè¿åè¿å
¥å
¬å¸è´¨æ£å®¡æ¹ï¼é©³åå鿰忣ï¼ç»éåéè´§è´¨æ£åç´æ¥ç»å®¡ã |
| | | */ |
| | | private void applyProcessApproval(MesQcSeedQualityDO quality, boolean approved, String opinion, boolean validateApprover) { |
| | | if (validateApprover) { |
| | | approverService.validateApprover(MesQcApprovalTypeEnum.SEED_QUALITY.getType(), |
| | | MesQcApproveNodeEnum.PROCESS.getNode(), getLoginUserId()); |
| | | } |
| | | if (!approved && StrUtil.isBlank(opinion)) throw new IllegalArgumentException("é©³åæ¶è¯·å¡«åå®¡æ¹æè§"); |
| | | int next = approved ? WAIT_RETEST : WAIT_INITIAL_RECHECK; |
| | | qualityMapper.updateById(MesQcSeedQualityDO.builder().id(id).workflowStatus(next) |
| | | .processApprovedAt(LocalDateTime.now()).build()); |
| | | recordAction(id, approved ? "PROCESS_APPROVE" : "PROCESS_REJECT", quality.getResult(), opinion, null); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | Long operatorId = getLoginUserId(); |
| | | if (Objects.equals(quality.getBizType(), BIZ_TYPE_RETURN)) { |
| | | if (!Objects.equals(quality.getWorkflowStatus(), WAIT_PROCESS)) |
| | | throw new IllegalArgumentException("ç»éåéè´§è´¨æ£å½åç¶æä¸è½è¿è¡éå®é¨å®¡æ¹"); |
| | | applyReturnApproval(quality, approved, opinion, operatorId, now); |
| | | return; |
| | | } |
| | | int next = approved ? (Objects.equals(quality.getQcLine(), 0) ? WAIT_RETEST |
| | | : Objects.equals(quality.getResult(), 1) ? WAIT_COMPANY : WAIT_COMPANY_FAIL) |
| | | : WAIT_INITIAL_RECHECK; |
| | | qualityMapper.updateById(MesQcSeedQualityDO.builder().id(quality.getId()).workflowStatus(next) |
| | | .processApprovedAt(now).build()); |
| | | if (!approved) qualityMapper.clearLineProcessInstance(quality.getId()); |
| | | actionMapper.insert(newAction(quality.getId(), approved ? "PROCESS_APPROVE" : "PROCESS_REJECT", |
| | | quality.getResult(), opinion, null, operatorId, now)); |
| | | if (approved && !Objects.equals(quality.getQcLine(), 0)) |
| | | afterCommit(() -> maybeStartCompanyProcess(quality.getBatchCode(), operatorId)); |
| | | } |
| | | |
| | | @Override @Transactional(rollbackFor = Exception.class) |
| | | public void companyApprove(Long id, boolean approved, String opinion) { |
| | | MesQcSeedQualityDO quality = qualityMapper.selectByIdForUpdate(id); |
| | | if (quality == null || !Set.of(WAIT_COMPANY, WAIT_COMPANY_FAIL).contains(quality.getWorkflowStatus())) |
| | | if (quality == null) throw new IllegalArgumentException("è´¨æ£è®°å½ä¸åå¨"); |
| | | if (Objects.equals(quality.getBizType(), BIZ_TYPE_RETURN)) |
| | | throw new IllegalArgumentException("ç»éåéè´§è´¨æ£è¯·ä½¿ç¨éå®é¨å®¡æ¹"); |
| | | if (!Set.of(WAIT_COMPANY, WAIT_COMPANY_FAIL).contains(quality.getWorkflowStatus())) |
| | | throw new IllegalArgumentException("å½åç¶æä¸è½è¿è¡å¤æ£å®¡æ ¸"); |
| | | requireLegacyApproval(quality); |
| | | approverService.validateApprover(MesQcApprovalTypeEnum.SEED_QUALITY.getType(), |
| | | MesQcApproveNodeEnum.COMPANY.getNode(), getLoginUserId()); |
| | | applyCompanyApproval(quality.getCode(), approved, opinion, getLoginUserId()); |
| | | } |
| | | |
| | | /** |
| | | * å
¬å¸è´¨æ£å®¡æ¹ï¼ä»¥ãè¢ã为å使坹审æ¹ï¼ä¸¤æ¡è´¨æ£çº¿é½èµ°å®æå
许审æ¹ã |
| | | * 两æ¡çº¿ååæ ¼ææ¾è¡å
¥åºï¼ä»»ä¸æ¡çº¿ä¸åæ ¼åæ´è¢æ¥åºï¼é©³åå两æ¡çº¿å
¨é¨éå夿£ã |
| | | */ |
| | | private void applyCompanyApproval(String code, boolean approved, String opinion, Long operatorId) { |
| | | if (!approved && StrUtil.isBlank(opinion)) throw new IllegalArgumentException("é©³åæ¶è¯·å¡«åå®¡æ¹æè§"); |
| | | if (approved) finalizeStock(quality, quality.getWorkflowStatus() == WAIT_COMPANY); |
| | | int next = approved ? (quality.getWorkflowStatus() == WAIT_COMPANY ? RELEASED : SCRAPPED) : WAIT_RETEST; |
| | | qualityMapper.updateById(MesQcSeedQualityDO.builder().id(id).workflowStatus(next) |
| | | .companyApprovedAt(LocalDateTime.now()).build()); |
| | | recordAction(id, approved ? "COMPANY_APPROVE" : "COMPANY_REJECT", quality.getResult(), opinion, null); |
| | | List<MesQcSeedQualityDO> pair = qualityMapper.selectListByCodeForUpdate(code).stream() |
| | | .filter(q -> !isReturnQuality(q)).toList(); |
| | | if (pair.size() != 2 || pair.stream().anyMatch(q -> !Objects.equals(q.getQcLine(), QC_LINE_PROCESS) |
| | | && !Objects.equals(q.getQcLine(), QC_LINE_SALES))) |
| | | throw new IllegalArgumentException("è¢ç " + code + " çå线质æ£è®°å½ä¸å®æ´ï¼æ æ³å
¬å¸å®¡æ¹"); |
| | | if (pair.stream().anyMatch(q -> !Set.of(WAIT_COMPANY, WAIT_COMPANY_FAIL).contains(q.getWorkflowStatus()))) |
| | | throw new IllegalArgumentException("è¢ç " + code + " çå线质æ£å°æªå
¨é¨å®æï¼ä¸è½è¿è¡å
¬å¸è´¨æ£å®¡æ¹"); |
| | | boolean passed = pair.stream().allMatch(q -> Objects.equals(q.getWorkflowStatus(), WAIT_COMPANY)); |
| | | MesQcSeedQualityDO representative = pair.stream() |
| | | .filter(q -> Objects.equals(q.getQcLine(), QC_LINE_PROCESS)).findFirst().orElse(pair.get(0)); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | if (approved) { |
| | | finalizeStock(representative, passed); |
| | | qualityMapper.updateBatch(pair.stream().map(quality -> MesQcSeedQualityDO.builder() |
| | | .id(quality.getId()).workflowStatus(passed ? RELEASED : SCRAPPED) |
| | | .companyApprovedAt(now).build()).toList(), WRITE_BATCH_SIZE); |
| | | } else { |
| | | qualityMapper.clearCompanyProcessInstance(code); |
| | | pair.forEach(quality -> qualityMapper.clearLineProcessInstance(quality.getId())); |
| | | qualityMapper.updateBatch(pair.stream().map(quality -> MesQcSeedQualityDO.builder() |
| | | .id(quality.getId()).workflowStatus(WAIT_RETEST).companyApprovedAt(now).build()).toList(), WRITE_BATCH_SIZE); |
| | | } |
| | | actionMapper.insertBatch(pair.stream().map(quality -> newAction(quality.getId(), |
| | | approved ? "COMPANY_APPROVE" : "COMPANY_REJECT", quality.getResult(), opinion, null, operatorId, now)).toList(), |
| | | WRITE_BATCH_SIZE); |
| | | } |
| | | |
| | | @Override @Transactional(rollbackFor = Exception.class) |
| | |
| | | List<Long> confirmed = approvalIds(ids); |
| | | List<MesQcSeedQualityDO> qualities = qualityMapper.selectListByIdsForUpdate(confirmed); |
| | | validateLockedIds(confirmed, qualities); |
| | | qualities.forEach(MesQcSeedQualityServiceImpl::requireLegacyApproval); |
| | | if (qualities.stream().anyMatch(quality -> !Set.of(WAIT_PROCESS, WAIT_INITIAL_FAIL_REVIEW).contains(quality.getWorkflowStatus()))) |
| | | throw new IllegalArgumentException("æéè®°å½å
å«ä¸è½è¿è¡åæ£å®¡æ ¸çç¶æï¼è¯·å·æ°åéè¯"); |
| | | approverService.validateApprover(MesQcApprovalTypeEnum.SEED_QUALITY.getType(), |
| | |
| | | if (!approved && StrUtil.isBlank(opinion)) throw new IllegalArgumentException("é©³åæ¶è¯·å¡«åå®¡æ¹æè§"); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | Long operatorId = getLoginUserId(); |
| | | List<MesQcSeedQualityDO> production = qualities.stream() |
| | | .filter(quality -> !Objects.equals(quality.getBizType(), BIZ_TYPE_RETURN)).toList(); |
| | | qualities.stream().filter(quality -> Objects.equals(quality.getBizType(), BIZ_TYPE_RETURN)) |
| | | .forEach(quality -> applyProcessApproval(quality, approved, opinion, false)); |
| | | if (production.isEmpty()) return; |
| | | int next = approved ? WAIT_RETEST : WAIT_INITIAL_RECHECK; |
| | | qualityMapper.updateBatch(qualities.stream().map(quality -> MesQcSeedQualityDO.builder().id(quality.getId()) |
| | | .workflowStatus(next).processApprovedAt(now).build()).toList(), WRITE_BATCH_SIZE); |
| | | actionMapper.insertBatch(qualities.stream().map(quality -> newAction(quality.getId(), |
| | | qualityMapper.updateBatch(production.stream().map(quality -> MesQcSeedQualityDO.builder().id(quality.getId()) |
| | | .workflowStatus(next) |
| | | .processApprovedAt(now).build()).toList(), WRITE_BATCH_SIZE); |
| | | if (!approved) production.forEach(quality -> qualityMapper.clearLineProcessInstance(quality.getId())); |
| | | actionMapper.insertBatch(production.stream().map(quality -> newAction(quality.getId(), |
| | | approved ? "PROCESS_APPROVE" : "PROCESS_REJECT", quality.getResult(), opinion, null, operatorId, now)).toList(), |
| | | WRITE_BATCH_SIZE); |
| | | } |
| | |
| | | List<Long> confirmed = approvalIds(ids); |
| | | List<MesQcSeedQualityDO> qualities = qualityMapper.selectListByIdsForUpdate(confirmed); |
| | | validateLockedIds(confirmed, qualities); |
| | | if (qualities.stream().anyMatch(quality -> !Set.of(WAIT_COMPANY, WAIT_COMPANY_FAIL).contains(quality.getWorkflowStatus()))) |
| | | throw new IllegalArgumentException("æéè®°å½å
å«ä¸è½è¿è¡å¤æ£å®¡æ ¸çç¶æï¼è¯·å·æ°åéè¯"); |
| | | qualities.forEach(MesQcSeedQualityServiceImpl::requireLegacyApproval); |
| | | if (qualities.stream().anyMatch(quality -> Objects.equals(quality.getBizType(), BIZ_TYPE_RETURN))) |
| | | throw new IllegalArgumentException("ç»éåéè´§è´¨æ£è¯·ä½¿ç¨éå®é¨å®¡æ¹"); |
| | | approverService.validateApprover(MesQcApprovalTypeEnum.SEED_QUALITY.getType(), |
| | | MesQcApproveNodeEnum.COMPANY.getNode(), getLoginUserId()); |
| | | if (!approved && StrUtil.isBlank(opinion)) throw new IllegalArgumentException("é©³åæ¶è¯·å¡«åå®¡æ¹æè§"); |
| | | if (approved) { |
| | | for (MesQcSeedQualityDO quality : qualities) finalizeStock(quality, quality.getWorkflowStatus() == WAIT_COMPANY); |
| | | } |
| | | Long operatorId = getLoginUserId(); |
| | | // å
¬å¸å®¡æ¹ä»¥è¢ä¸ºå使坹å¤çï¼åä¸è¢ç两æ¡çº¿è®°å½ä¸æ¬¡å®¡æ¹ |
| | | qualities.stream().map(MesQcSeedQualityDO::getCode).distinct().sorted() |
| | | .forEach(code -> applyCompanyApproval(code, approved, opinion, operatorId)); |
| | | } |
| | | |
| | | // ==================== ç»éåéè´§è´¨æ£ ==================== |
| | | |
| | | @Override |
| | | public Map<String, Object> resolveReturnQuality(Long returnSalesId) { |
| | | MesWmReturnSalesDO returnSales = returnSalesService.getReturnSales(returnSalesId); |
| | | if (returnSales == null) throw new IllegalArgumentException("éå®éè´§åä¸åå¨"); |
| | | List<MesWmReturnSalesLineDO> lines = returnSalesLineService.getReturnSalesLineListByReturnId(returnSalesId); |
| | | MesWmReturnSalesLineDO first = lines.stream().filter(line -> line.getItemId() != null).findFirst().orElse(null); |
| | | Long itemId = first == null ? null : first.getItemId(); |
| | | MesQcSeedQualityDO quality = qualityMapper.selectByReturnSalesId(returnSalesId); |
| | | Map<String, Object> result = new HashMap<>(); |
| | | result.put("returnSalesId", returnSales.getId()); |
| | | result.put("returnSalesCode", returnSales.getCode()); |
| | | result.put("returnSalesStatus", returnSales.getStatus()); |
| | | result.put("itemId", itemId); |
| | | result.put("batchId", first == null ? null : first.getBatchId()); |
| | | result.put("batchCode", first == null ? null : first.getBatchCode()); |
| | | result.put("indicators", toRangePayload(itemId)); |
| | | result.put("qualityId", quality == null ? null : quality.getId()); |
| | | result.put("result", quality == null ? null : quality.getResult()); |
| | | result.put("workflowStatus", quality == null ? null : quality.getWorkflowStatus()); |
| | | return result; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long submitReturnQuality(MesQcSeedReturnQualitySubmitReqVO reqVO) { |
| | | MesWmReturnSalesDO returnSales = returnSalesService.getReturnSales(reqVO.getReturnSalesId()); |
| | | if (returnSales == null) throw new IllegalArgumentException("éå®éè´§åä¸åå¨"); |
| | | if (qualityMapper.selectByReturnSalesId(returnSales.getId()) != null) |
| | | throw new IllegalArgumentException("该éå®éè´§å已宿éè´§è´¨æ£ï¼ç¦æ¢éå¤æäº¤"); |
| | | if (!Set.of(MesWmReturnSalesStatusEnum.PREPARE.getStatus(), |
| | | MesWmReturnSalesStatusEnum.CONFIRMED.getStatus()).contains(returnSales.getStatus())) |
| | | throw new IllegalArgumentException("å½åéå®éè´§åç¶æä¸è½è¿è¡éè´§è´¨æ£"); |
| | | List<MesWmReturnSalesLineDO> lines = returnSalesLineService.getReturnSalesLineListByReturnId(returnSales.getId()); |
| | | if (CollUtil.isEmpty(lines)) throw new IllegalArgumentException("éå®éè´§åæ²¡ææç»ï¼æ æ³è´¨æ£"); |
| | | MesWmReturnSalesLineDO first = lines.stream().filter(line -> line.getItemId() != null).findFirst() |
| | | .orElseThrow(() -> new IllegalArgumentException("éå®éè´§åæç»ç¼ºå°ç©æï¼æ æ³è´¨æ£")); |
| | | Map<String, IndicatorMeta> expected = requireEffectiveIndicators(first.getItemId()); |
| | | boolean passed = validateIndicators(reqVO.getIndicators(), expected); |
| | | if (!passed && StrUtil.isBlank(reqVO.getReason())) throw new IllegalArgumentException("è´¨æ£ä¸åæ ¼å¿
须填ååå "); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | Long operatorId = getLoginUserId(); |
| | | List<MesQcSeedQualityDO> updates = qualities.stream().map(quality -> MesQcSeedQualityDO.builder().id(quality.getId()) |
| | | .workflowStatus(approved ? (quality.getWorkflowStatus() == WAIT_COMPANY ? RELEASED : SCRAPPED) : WAIT_RETEST) |
| | | .companyApprovedAt(now).build()).toList(); |
| | | qualityMapper.updateBatch(updates, WRITE_BATCH_SIZE); |
| | | actionMapper.insertBatch(qualities.stream().map(quality -> newAction(quality.getId(), |
| | | approved ? "COMPANY_APPROVE" : "COMPANY_REJECT", quality.getResult(), opinion, null, operatorId, now)).toList(), |
| | | int result = passed ? 1 : 2; |
| | | MesQcSeedQualityDO quality = MesQcSeedQualityDO.builder() |
| | | .code(returnSales.getCode()).qcLine(QC_LINE_SALES).bizType(BIZ_TYPE_RETURN) |
| | | .itemId(first.getItemId()).batchId(first.getBatchId()).batchCode(first.getBatchCode()) |
| | | .result(result).reason(passed ? "" : reqVO.getReason().trim()) |
| | | // åæ£åæ ¼è¿å
¥éå®é¨å®¡æ¹ï¼ä¸åæ ¼å
夿£ã |
| | | .workflowStatus(passed ? WAIT_PROCESS : WAIT_RETEST) |
| | | .returnSalesId(returnSales.getId()).returnSalesCode(returnSales.getCode()) |
| | | .inspectorId(operatorId).inspectedAt(now).build(); |
| | | qualityMapper.insert(quality); |
| | | for (MesQcSeedQualitySubmitReqVO.Indicator item : reqVO.getIndicators()) { |
| | | IndicatorMeta meta = expected.get(item.getCode()); |
| | | indicatorMapper.insert(MesQcSeedQualityIndicatorDO.builder().qualityId(quality.getId()) |
| | | .indicatorCode(item.getCode()).indicatorName(meta.name()).unit(meta.unit()) |
| | | .value(item.getValue()).minValue(item.getMinValue()).maxValue(item.getMaxValue()).build()); |
| | | } |
| | | recordAction(quality.getId(), "INITIAL", result, reqVO.getReason(), reqVO.getIndicators()); |
| | | returnSalesService.updateReturnSalesStatus(returnSales.getId(), MesWmReturnSalesStatusEnum.CONFIRMED.getStatus()); |
| | | if (passed) afterCommit(() -> startReturnProcess(quality, operatorId)); |
| | | return quality.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void returnApprove(Long id, boolean approved, String opinion) { |
| | | MesQcSeedQualityDO quality = qualityMapper.selectByIdForUpdate(id); |
| | | if (quality == null || !Objects.equals(quality.getBizType(), BIZ_TYPE_RETURN)) |
| | | throw new IllegalArgumentException("è´¨æ£è®°å½ä¸å卿䏿¯ç»éåéè´§è´¨æ£"); |
| | | requireLegacyApproval(quality); |
| | | if (!Objects.equals(quality.getWorkflowStatus(), WAIT_PROCESS)) |
| | | throw new IllegalArgumentException("å½åç¶æä¸è½è¿è¡éå®é¨å®¡æ¹"); |
| | | approverService.validateApprover(MesQcApprovalTypeEnum.SEED_QUALITY.getType(), |
| | | MesQcApproveNodeEnum.PROCESS.getNode(), getLoginUserId()); |
| | | applyReturnApproval(quality, approved, opinion, getLoginUserId(), LocalDateTime.now()); |
| | | } |
| | | |
| | | /** |
| | | * éå®é¨é¢å¯¼å®¡æ¹ï¼éè¿åéè´§åè¿å
¥æ§è¡ï¼ä¸éè¿åä¸äºéè´§å¹¶åæ¶éè´§åï¼è´¨æ£è®°å½çåã |
| | | */ |
| | | private void applyReturnApproval(MesQcSeedQualityDO quality, boolean approved, String opinion, |
| | | Long operatorId, LocalDateTime now) { |
| | | if (!approved && StrUtil.isBlank(opinion)) throw new IllegalArgumentException("é©³åæ¶è¯·å¡«åå®¡æ¹æè§"); |
| | | if (approved && Objects.equals(quality.getResult(), 1)) { |
| | | returnSalesService.updateReturnSalesStatus(quality.getReturnSalesId(), |
| | | MesWmReturnSalesStatusEnum.APPROVING.getStatus()); |
| | | qualityMapper.updateById(MesQcSeedQualityDO.builder().id(quality.getId()) |
| | | .workflowStatus(RELEASED).processApprovedAt(now).build()); |
| | | } else { |
| | | cancelReturnSalesQuietly(quality.getReturnSalesId()); |
| | | qualityMapper.updateById(MesQcSeedQualityDO.builder().id(quality.getId()) |
| | | .workflowStatus(SCRAPPED).processApprovedAt(now).build()); |
| | | } |
| | | actionMapper.insert(newAction(quality.getId(), approved ? "RETURN_APPROVE" : "RETURN_REJECT", |
| | | quality.getResult(), opinion, null, operatorId, now)); |
| | | } |
| | | |
| | | // ==================== BPM æµç¨åè° ==================== |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void handleLineProcessResult(String processInstanceId, Integer qcLine, String batchCode, boolean approved, String reason) { |
| | | if (StrUtil.isBlank(processInstanceId) || qcLine == null || StrUtil.isBlank(batchCode)) return; |
| | | List<MesQcSeedQualityDO> pending = qualityMapper.selectListByProcessInstanceId(processInstanceId).stream() |
| | | .filter(quality -> Objects.equals(quality.getQcLine(), qcLine) |
| | | && Objects.equals(quality.getBatchCode(), batchCode) |
| | | && !isReturnQuality(quality) |
| | | && Set.of(WAIT_PROCESS, WAIT_INITIAL_FAIL_REVIEW).contains(quality.getWorkflowStatus())) |
| | | .toList(); |
| | | if (pending.isEmpty()) { |
| | | log.info("[handleLineProcessResult][æ¹æ¬¡ {} è´¨æ£çº¿ {} æ å¾
审æ¹è®°å½ï¼å¿½ç¥ BPM äºä»¶]", batchCode, qcLine); |
| | | return; |
| | | } |
| | | List<Long> ids = pending.stream().map(MesQcSeedQualityDO::getId).sorted().toList(); |
| | | List<MesQcSeedQualityDO> locked = qualityMapper.selectListByIdsForUpdate(ids); |
| | | validateLockedIds(ids, locked); |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | int next = approved ? WAIT_COMPANY : WAIT_INITIAL_RECHECK; |
| | | String finalReason = StrUtil.blankToDefault(reason, approved ? "审æ¹éè¿" : "审æ¹é©³å"); |
| | | qualityMapper.updateBatch(locked.stream().map(quality -> MesQcSeedQualityDO.builder().id(quality.getId()) |
| | | .workflowStatus(approved && Objects.equals(quality.getResult(), 2) ? WAIT_COMPANY_FAIL : next) |
| | | .processApprovedAt(now).build()).toList(), WRITE_BATCH_SIZE); |
| | | if (!approved) locked.forEach(quality -> qualityMapper.clearLineProcessInstance(quality.getId())); |
| | | actionMapper.insertBatch(locked.stream().map(quality -> newAction(quality.getId(), |
| | | approved ? "PROCESS_APPROVE" : "PROCESS_REJECT", quality.getResult(), finalReason, null, null, now)).toList(), |
| | | WRITE_BATCH_SIZE); |
| | | if (approved) { |
| | | Long inspectorId = locked.stream().map(MesQcSeedQualityDO::getInspectorId) |
| | | .filter(Objects::nonNull).findFirst().orElse(null); |
| | | afterCommit(() -> maybeStartCompanyProcess(batchCode, inspectorId)); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void handleCompanyProcessResult(String processInstanceId, String businessKey, boolean approved, String reason) { |
| | | if (StrUtil.isBlank(processInstanceId)) return; |
| | | // å
¬å¸å®¡æ¹æçäº§æ¹æ¬¡åèµ·ï¼ä¸ä¸ªæµç¨å®ä¾è¦çè¯¥æ¹æ¬¡çææè¢ï¼ |
| | | // ææµç¨å®ä¾ç¼å·åæ¥å¯åæ¶å
¼å®¹å岿è¢ç åèµ·ç审æ¹ã |
| | | List<MesQcSeedQualityDO> pending = qualityMapper.selectListByCompanyProcessInstanceId(processInstanceId).stream() |
| | | .filter(quality -> !isReturnQuality(quality) |
| | | && Set.of(WAIT_COMPANY, WAIT_COMPANY_FAIL).contains(quality.getWorkflowStatus())) |
| | | .toList(); |
| | | if (pending.isEmpty()) { |
| | | log.info("[handleCompanyProcessResult][æµç¨ {} æ å¾
å
¬å¸å®¡æ¹è®°å½ï¼å¿½ç¥ BPM äºä»¶]", processInstanceId); |
| | | return; |
| | | } |
| | | String finalReason = StrUtil.blankToDefault(reason, approved ? "å
¬å¸è´¨æ£å®¡æ¹éè¿" : "å
¬å¸è´¨æ£å®¡æ¹é©³å"); |
| | | // æ¾è¡/æ¥åºä»¥è¢ä¸ºåä½è½åºï¼å䏿¹æ¬¡çå¤ä¸ªè¢ä¾æ¬¡å¤ç |
| | | pending.stream().map(MesQcSeedQualityDO::getCode).distinct().sorted() |
| | | .forEach(code -> applyCompanyApproval(code, approved, finalReason, null)); |
| | | log.info("[handleCompanyProcessResult][å
¬å¸è´¨æ£å®¡æ¹ {} å®æï¼æ¹æ¬¡/è¢ç {} å
±å¤ç {} æ¡è®°å½]", |
| | | processInstanceId, businessKey, pending.size()); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void handleReturnProcessResult(String processInstanceId, Long returnSalesId, boolean approved, String reason) { |
| | | if (StrUtil.isBlank(processInstanceId) || returnSalesId == null) return; |
| | | MesQcSeedQualityDO quality = qualityMapper.selectByReturnSalesIdForUpdate(returnSalesId); |
| | | if (quality == null || !Objects.equals(quality.getBizType(), BIZ_TYPE_RETURN) |
| | | || !Objects.equals(quality.getProcessInstanceId(), processInstanceId) |
| | | || !Objects.equals(quality.getWorkflowStatus(), WAIT_PROCESS)) { |
| | | log.info("[handleReturnProcessResult][éå®éè´§å {} æ å¾
审æ¹éè´§è´¨æ£ï¼å¿½ç¥ BPM äºä»¶]", returnSalesId); |
| | | return; |
| | | } |
| | | applyReturnApproval(quality, approved, StrUtil.blankToDefault(reason, approved ? "审æ¹éè¿" : "审æ¹é©³å"), |
| | | null, LocalDateTime.now()); |
| | | } |
| | | |
| | | private void startLineProcess(Integer qcLine, String batchCode, Long operatorId) { |
| | | if (qcLine == null || StrUtil.isBlank(batchCode) || operatorId == null) return; |
| | | List<MesQcSeedQualityDO> pending = qualityMapper.selectListByBatchCode(batchCode).stream() |
| | | .filter(quality -> Objects.equals(quality.getQcLine(), qcLine) |
| | | && !isReturnQuality(quality) |
| | | && StrUtil.isBlank(quality.getProcessInstanceId()) |
| | | && Set.of(WAIT_PROCESS, WAIT_INITIAL_FAIL_REVIEW).contains(quality.getWorkflowStatus())) |
| | | .toList(); |
| | | if (pending.isEmpty()) return; |
| | | Map<String, Object> variables = new HashMap<>(); |
| | | variables.put("qcLine", qcLine); |
| | | variables.put("batchCode", batchCode); |
| | | String processInstanceId = processInstanceApi.createProcessInstance(operatorId, |
| | | new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(LINE_PROCESS_KEY) |
| | | .setBusinessKey(qcLine + "#" + batchCode) |
| | | .setVariables(variables)); |
| | | for (MesQcSeedQualityDO quality : pending) { |
| | | qualityMapper.updateById(MesQcSeedQualityDO.builder().id(quality.getId()) |
| | | .processInstanceId(processInstanceId).build()); |
| | | } |
| | | log.info("[startLineProcess][æ¹æ¬¡ {} è´¨æ£çº¿ {} å·²åèµ·æ¬çº¿é¢å¯¼å®¡æ¹ {}", batchCode, qcLine, processInstanceId); |
| | | } |
| | | |
| | | /** |
| | | * å线é¨é¨å®¡æ¹å
¨é¨å®æåï¼æçäº§æ¹æ¬¡ç»ä¸åèµ·ä¸æ¬¡å
¬å¸è´¨æ£å®¡æ¹ã |
| | | * å䏿¹æ¬¡çå¤è¢å
±ç¨åä¸ä¸ªæµç¨å®ä¾ï¼é¿å
æ¯è¢åå䏿¡å®¡æ¹ã |
| | | */ |
| | | private void maybeStartCompanyProcess(String batchCode, Long operatorId) { |
| | | if (StrUtil.isBlank(batchCode) || operatorId == null) return; |
| | | List<MesQcSeedQualityDO> records = qualityMapper.selectListByBatchCode(batchCode).stream() |
| | | .filter(quality -> !isReturnQuality(quality)) |
| | | // å·²æ¾è¡/å·²æ¥åºçè®°å½å±äºå·²ç»ç»çæ¹æ¬¡ï¼ä¸åå䏿°çå
¬å¸å®¡æ¹ |
| | | .filter(quality -> !Set.of(RELEASED, SCRAPPED).contains(quality.getWorkflowStatus())) |
| | | .toList(); |
| | | if (records.isEmpty()) return; |
| | | if (records.stream().anyMatch(quality -> !Set.of(WAIT_COMPANY, WAIT_COMPANY_FAIL) |
| | | .contains(quality.getWorkflowStatus()))) return; |
| | | if (records.stream().anyMatch(quality -> StrUtil.isNotBlank(quality.getCompanyProcessInstanceId()))) return; |
| | | Map<String, Object> variables = new HashMap<>(); |
| | | variables.put("batchCode", batchCode); |
| | | // å
¼å®¹æµç¨æ¡ä»¶ä¸å¼ç¨ç code åé |
| | | variables.put("code", batchCode); |
| | | variables.put("passed", records.stream().allMatch(quality -> Objects.equals(quality.getWorkflowStatus(), WAIT_COMPANY))); |
| | | String processInstanceId = processInstanceApi.createProcessInstance(operatorId, |
| | | new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(COMPANY_PROCESS_KEY) |
| | | .setBusinessKey(batchCode) |
| | | .setVariables(variables)); |
| | | for (MesQcSeedQualityDO quality : records) { |
| | | qualityMapper.updateById(MesQcSeedQualityDO.builder().id(quality.getId()) |
| | | .companyProcessInstanceId(processInstanceId).build()); |
| | | } |
| | | log.info("[maybeStartCompanyProcess][æ¹æ¬¡ {} å线质æ£å®æï¼å·²åèµ·å
¬å¸è´¨æ£å®¡æ¹ {}ï¼è¦ç {} æ¡è®°å½]", |
| | | batchCode, processInstanceId, records.size()); |
| | | } |
| | | |
| | | private void startReturnProcess(MesQcSeedQualityDO quality, Long operatorId) { |
| | | if (quality == null || quality.getReturnSalesId() == null || StrUtil.isNotBlank(quality.getProcessInstanceId())) |
| | | return; |
| | | Map<String, Object> variables = new HashMap<>(); |
| | | variables.put("returnSalesId", quality.getReturnSalesId()); |
| | | variables.put("returnSalesCode", quality.getReturnSalesCode()); |
| | | variables.put("code", quality.getCode()); |
| | | String processInstanceId = processInstanceApi.createProcessInstance(operatorId, |
| | | new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(RETURN_PROCESS_KEY) |
| | | .setBusinessKey(String.valueOf(quality.getReturnSalesId())) |
| | | .setVariables(variables)); |
| | | qualityMapper.updateById(MesQcSeedQualityDO.builder().id(quality.getId()) |
| | | .processInstanceId(processInstanceId).build()); |
| | | log.info("[startReturnProcess][éå®éè´§å {} å·²åèµ·éå®é¨é¢å¯¼å®¡æ¹ {}]", quality.getReturnSalesId(), processInstanceId); |
| | | } |
| | | |
| | | @Override |
| | | public void retryBpmProcess(Long id) { |
| | | MesQcSeedQualityDO quality = qualityMapper.selectById(id); |
| | | if (quality == null) throw invalidParamException("è´¨æ£è®°å½ä¸åå¨"); |
| | | Long operatorId = getLoginUserId(); |
| | | if (Objects.equals(quality.getBizType(), BIZ_TYPE_RETURN)) { |
| | | if (!Objects.equals(quality.getWorkflowStatus(), WAIT_PROCESS)) |
| | | throw invalidParamException("å½åéè´§è´¨æ£ä¸éè¦å起审æ¹"); |
| | | if (StrUtil.isNotBlank(quality.getProcessInstanceId())) |
| | | throw invalidParamException("éè´§è´¨æ£å®¡æ¹æµç¨å·²ç»åèµ·"); |
| | | startReturnProcess(quality, operatorId); |
| | | } else if (Set.of(WAIT_PROCESS, WAIT_INITIAL_FAIL_REVIEW).contains(quality.getWorkflowStatus())) { |
| | | if (StrUtil.isNotBlank(quality.getProcessInstanceId())) |
| | | throw invalidParamException("é¨é¨å®¡æ¹æµç¨å·²ç»åèµ·"); |
| | | startLineProcess(quality.getQcLine(), quality.getBatchCode(), operatorId); |
| | | } else if (Set.of(WAIT_COMPANY, WAIT_COMPANY_FAIL).contains(quality.getWorkflowStatus())) { |
| | | String batchCode = quality.getBatchCode(); |
| | | if (StrUtil.isBlank(batchCode)) |
| | | throw invalidParamException("è´¨æ£è®°å½æªç»å®çäº§æ¹æ¬¡ï¼ä¸è½åèµ·å
¬å¸å®¡æ¹"); |
| | | List<MesQcSeedQualityDO> batchRecords = qualityMapper.selectListByBatchCode(batchCode).stream() |
| | | .filter(q -> !isReturnQuality(q)) |
| | | .filter(q -> !Set.of(RELEASED, SCRAPPED).contains(q.getWorkflowStatus())) |
| | | .toList(); |
| | | if (batchRecords.isEmpty() || batchRecords.stream().anyMatch(q -> !Set.of(WAIT_COMPANY, WAIT_COMPANY_FAIL) |
| | | .contains(q.getWorkflowStatus()))) |
| | | throw invalidParamException("åæ¹æ¬¡å线é¨é¨å®¡æ¹å°æªå
¨é¨å®æï¼ä¸è½åèµ·å
¬å¸å®¡æ¹"); |
| | | if (batchRecords.stream().anyMatch(q -> StrUtil.isNotBlank(q.getCompanyProcessInstanceId()))) |
| | | throw invalidParamException("å
¬å¸è´¨æ£å®¡æ¹æµç¨å·²ç»åèµ·"); |
| | | maybeStartCompanyProcess(batchCode, operatorId); |
| | | } else { |
| | | throw invalidParamException("å½åè´¨æ£ç¶æä¸éè¦å起审æ¹"); |
| | | } |
| | | } |
| | | |
| | | private void cancelReturnSalesQuietly(Long returnSalesId) { |
| | | if (returnSalesId == null) return; |
| | | try { |
| | | returnSalesService.cancelReturnSales(returnSalesId); |
| | | } catch (Exception ex) { |
| | | log.warn("[cancelReturnSalesQuietly][éå®éè´§å {} åæ¶å¤±è´¥: {}]", returnSalesId, ex.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void afterCommit(Runnable action) { |
| | | TransactionSynchronization synchronization = new TransactionSynchronization() { |
| | | @Override |
| | | public void afterCommit() { |
| | | try { |
| | | action.run(); |
| | | } catch (Exception ex) { |
| | | log.error("[afterCommit][BPM æµç¨å¯å¨å¤±è´¥ï¼è¯·ä¿®å¤æµç¨é
置并å¨è´¨æ£è®°å½ä¸éæ°å起审æ¹]", ex); |
| | | } |
| | | } |
| | | }; |
| | | if (TransactionSynchronizationManager.isSynchronizationActive()) { |
| | | TransactionSynchronizationManager.registerSynchronization(synchronization); |
| | | } else { |
| | | synchronization.afterCommit(); |
| | | } |
| | | } |
| | | |
| | | /** å岿°æ® bizType ä¸ºç©ºï¼æç产å
¥åºè´¨æ£å¤ç */ |
| | | private static void requireQcLine(Integer qcLine) { |
| | | if (!Objects.equals(qcLine, QC_LINE_PROCESS) && !Objects.equals(qcLine, QC_LINE_SALES)) |
| | | throw new IllegalArgumentException("è´¨æ£çº¿å¿
须为å 工鍿éå®é¨"); |
| | | } |
| | | |
| | | /** ç产å线记å½åªè½èµ° BPM 审æ¹ï¼ç»éåéè´§è´¨æ£ä¸æåè´¨æ£çº¿ï¼ä»å¯ä½¿ç¨äººå·¥å®¡æ¹æ¥å£ã */ |
| | | private static void requireLegacyApproval(MesQcSeedQualityDO quality) { |
| | | if (!isReturnQuality(quality) && (Objects.equals(quality.getQcLine(), QC_LINE_PROCESS) |
| | | || Objects.equals(quality.getQcLine(), QC_LINE_SALES))) |
| | | throw new IllegalArgumentException("该质æ£è®°å½ç± BPM 审æ¹ï¼è¯·å¨æµç¨å¾
åä¸å¤ç"); |
| | | } |
| | | |
| | | private static boolean isReturnQuality(MesQcSeedQualityDO quality) { |
| | | return Objects.equals(quality.getBizType(), BIZ_TYPE_RETURN); |
| | | } |
| | | |
| | | private List<Long> approvalIds(List<Long> ids) { |
| | |
| | | return quality; |
| | | } |
| | | |
| | | private boolean validateIndicators(List<MesQcSeedQualitySubmitReqVO.Indicator> indicators) { |
| | | if (indicators == null || indicators.size() != REQUIRED.size()) throw new IllegalArgumentException("å¿
é¡»æäº¤åé¡¹ææ "); |
| | | private record IndicatorMeta(String code, String name, String unit) { } |
| | | |
| | | private Map<String, IndicatorMeta> requireEffectiveIndicators(Long itemId) { |
| | | Map<String, IndicatorMeta> result = new LinkedHashMap<>(); |
| | | for (MesQcSeedIndicatorRangeDO range : rangeService.getEffectiveRanges(itemId)) { |
| | | result.put(range.getIndicatorCode(), new IndicatorMeta(range.getIndicatorCode(), |
| | | StrUtil.blankToDefault(range.getIndicatorName(), range.getIndicatorCode()), |
| | | StrUtil.blankToDefault(range.getUnit(), "%"))); |
| | | } |
| | | if (result.isEmpty()) |
| | | throw new IllegalArgumentException("该产åå°æªé
置质æ£é¡¹ï¼è¯·å
å¨ãè´¨æ£é¡¹åæ ¼èå´é
ç½®ãä¸ç»´æ¤"); |
| | | return result; |
| | | } |
| | | |
| | | private Map<String, IndicatorMeta> toMetaMap(Collection<MesQcSeedQualityIndicatorDO> indicators, Long itemId) { |
| | | Map<String, MesQcSeedIndicatorRangeDO> config = rangeService.getEffectiveMap(itemId); |
| | | Map<String, IndicatorMeta> result = new LinkedHashMap<>(); |
| | | for (MesQcSeedQualityIndicatorDO indicator : indicators) { |
| | | MesQcSeedIndicatorRangeDO range = config.get(indicator.getIndicatorCode()); |
| | | String name = StrUtil.blankToDefault(indicator.getIndicatorName(), |
| | | range == null ? indicator.getIndicatorCode() : range.getIndicatorName()); |
| | | String unit = StrUtil.blankToDefault(indicator.getUnit(), range == null ? "%" : range.getUnit()); |
| | | result.put(indicator.getIndicatorCode(), new IndicatorMeta(indicator.getIndicatorCode(), name, unit)); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private boolean validateIndicators(List<MesQcSeedQualitySubmitReqVO.Indicator> indicators, |
| | | Map<String, IndicatorMeta> expected) { |
| | | if (indicators == null || indicators.isEmpty()) throw new IllegalArgumentException("请è³å°æäº¤ä¸é¡¹è´¨æ£ææ "); |
| | | if (expected.isEmpty()) throw new IllegalArgumentException("该产åå°æªé
置质æ£é¡¹"); |
| | | if (indicators.size() != expected.size()) |
| | | throw new IllegalArgumentException("è´¨æ£é¡¹æ°éä¸äº§åé
ç½®ä¸ä¸è´ï¼è¯·å·æ°åéæ°å½å
¥"); |
| | | Set<String> codes = new HashSet<>(); |
| | | boolean passed = true; |
| | | for (MesQcSeedQualitySubmitReqVO.Indicator item : indicators) { |
| | | if (item == null || !REQUIRED.contains(item.getCode()) || !codes.add(item.getCode()) |
| | | if (item == null || StrUtil.isBlank(item.getCode()) || !codes.add(item.getCode()) |
| | | || item.getValue() == null || item.getMinValue() == null || item.getMaxValue() == null) |
| | | throw new IllegalArgumentException("ææ å¿
须为纯度ãå度ãåè½çãæ°´åä¸ä¸å¯éå¤"); |
| | | if (item.getMinValue().compareTo(item.getMaxValue()) > 0) throw new IllegalArgumentException("ææ ä¸éä¸è½å¤§äºä¸é"); |
| | | if (item.getValue().compareTo(BigDecimal.ZERO) < 0 || item.getValue().compareTo(new BigDecimal("100")) > 0 |
| | | || item.getMinValue().compareTo(BigDecimal.ZERO) < 0 || item.getMaxValue().compareTo(new BigDecimal("100")) > 0) |
| | | throw new IllegalArgumentException("ç¾åæ¯ææ å¿
é¡»å¤äº 0â100"); |
| | | throw new IllegalArgumentException("è´¨æ£é¡¹ç宿µå¼ååæ ¼èå´å¿
须宿´ä¸ä¸å¯éå¤"); |
| | | if (!expected.containsKey(item.getCode())) |
| | | throw new IllegalArgumentException("è´¨æ£é¡¹æªå¨äº§åé
ç½®ä¸å¯ç¨ï¼" + item.getCode()); |
| | | if (item.getMinValue().compareTo(item.getMaxValue()) > 0) throw new IllegalArgumentException("è´¨æ£é¡¹ä¸éä¸è½å¤§äºä¸é"); |
| | | if (item.getValue().compareTo(BigDecimal.ZERO) < 0 || item.getMinValue().compareTo(BigDecimal.ZERO) < 0 |
| | | || item.getMaxValue().compareTo(BigDecimal.ZERO) < 0) |
| | | throw new IllegalArgumentException("è´¨æ£é¡¹æ°å¼ä¸è½ä¸ºè´æ°"); |
| | | if (item.getValue().compareTo(item.getMinValue()) < 0 || item.getValue().compareTo(item.getMaxValue()) > 0) passed = false; |
| | | } |
| | | if (!codes.equals(expected.keySet())) throw new IllegalArgumentException("产åè´¨æ£é¡¹ä¸å®æ´ï¼è¯·å·æ°åéæ°å½å
¥"); |
| | | return passed; |
| | | } |
| | | |
| | | private Long resolveItemId(Scope scope) { |
| | | Long itemId = scope.bags().stream().map(MesProBagCodeDO::getItemId).filter(Objects::nonNull).findFirst().orElse(null); |
| | | if (itemId != null || scope.bags().isEmpty()) return itemId; |
| | | Long batchId = scope.bags().get(0).getBatchId(); |
| | | if (batchId == null) return null; |
| | | MesProBatchDO batch = proBatchService.getBatch(batchId); |
| | | return batch == null ? null : batch.getItemId(); |
| | | } |
| | | |
| | | private List<Map<String, Object>> toRangePayload(Long itemId) { |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | for (MesQcSeedIndicatorRangeDO range : rangeService.getEffectiveRanges(itemId)) { |
| | | Map<String, Object> item = new HashMap<>(); |
| | | item.put("code", range.getIndicatorCode()); |
| | | item.put("name", StrUtil.blankToDefault(range.getIndicatorName(), range.getIndicatorCode())); |
| | | item.put("unit", StrUtil.blankToDefault(range.getUnit(), "%")); |
| | | item.put("minValue", range.getMinValue()); |
| | | item.put("maxValue", range.getMaxValue()); |
| | | result.add(item); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private List<Map<String, Object>> toIndicatorPayload(List<MesQcSeedQualityIndicatorDO> indicators, Long itemId) { |
| | | Map<String, MesQcSeedIndicatorRangeDO> config = rangeService.getEffectiveMap(itemId); |
| | | List<Map<String, Object>> result = new ArrayList<>(); |
| | | for (MesQcSeedQualityIndicatorDO indicator : indicators) { |
| | | MesQcSeedIndicatorRangeDO range = config.get(indicator.getIndicatorCode()); |
| | | Map<String, Object> item = new HashMap<>(); |
| | | item.put("code", indicator.getIndicatorCode()); |
| | | item.put("name", StrUtil.blankToDefault(indicator.getIndicatorName(), |
| | | range == null ? indicator.getIndicatorCode() : range.getIndicatorName())); |
| | | item.put("unit", StrUtil.blankToDefault(indicator.getUnit(), range == null ? "%" : range.getUnit())); |
| | | item.put("minValue", indicator.getMinValue()); |
| | | item.put("maxValue", indicator.getMaxValue()); |
| | | result.add(item); |
| | | } |
| | | return result; |
| | | } |
| | | |
| | | private void recordAction(Long id, String action, Integer result, String opinion, |
| | |
| | | /** |
| | | * è§£æåæ ¼åºçé»è®¤å
¥åºåºåºä¸åºä½ï¼å第ä¸ä¸ªåæ¶é
ç½®äºåºä½çéå»ç»åºåºã |
| | | * |
| | | * è´¨æ£æä»åºç»´åº¦éæ©å
¥åºç®æ ï¼åºåº/åºä½ç±ç³»ç»èªå¨è½ç¹ï¼åæ ¼åºç¼ºå°å¯ç¨åºåºæåºä½æ¶ï¼ |
| | | * åºåäºå¡æ æ³åå
¥ï¼ç´æ¥æç¤ºç®¡çåå®åä»åºé
ç½®ã |
| | | * åºåº/åºä½ä¸ºå¯é项ï¼ä»åºæªé
ç½®æ¶æä»åºå±çº§ç´æ¥å
¥åºï¼è¿å nullã |
| | | */ |
| | | private Long[] resolveQualifiedLocation(MesWmWarehouseDO qualifiedWarehouse) { |
| | | List<MesWmWarehouseLocationDO> locations = warehouseLocationService.getWarehouseLocationList(qualifiedWarehouse.getId()) |
| | | .stream().filter(location -> !Boolean.TRUE.equals(location.getFrozen())).toList(); |
| | | if (CollUtil.isEmpty(locations)) |
| | | throw new IllegalArgumentException("åæ ¼åºã" + qualifiedWarehouse.getName() + "ãæªé
ç½®å¯ç¨åºåºï¼è¯·å
å¨ä»åºç®¡çä¸å®ååºåºä¸åºä½"); |
| | | if (CollUtil.isEmpty(locations)) { |
| | | return new Long[]{null, null}; |
| | | } |
| | | for (MesWmWarehouseLocationDO location : locations) { |
| | | List<MesWmWarehouseAreaDO> areas = warehouseAreaService.getWarehouseAreaList(location.getId()) |
| | | .stream().filter(area -> !Boolean.TRUE.equals(area.getFrozen())).toList(); |
| | | if (CollUtil.isNotEmpty(areas)) return new Long[]{location.getId(), areas.get(0).getId()}; |
| | | } |
| | | throw new IllegalArgumentException("åæ ¼åºã" + qualifiedWarehouse.getName() + "ãæªé
ç½®å¯ç¨åºä½ï¼è¯·å
å¨ä»åºç®¡çä¸å®ååºåºä¸åºä½"); |
| | | return new Long[]{null, null}; |
| | | } |
| | | |
| | | private MesWmMaterialStockDO getOrCreateQualifiedStock(MesWmMaterialStockDO tempStock, Long qualifiedWarehouseId, |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.qc.seedquality.listener; |
| | | |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent; |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.qc.seedquality.MesQcSeedQualityService; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * ç§åè´¨æ£å
¬å¸è´¨æ£å®¡æ¹ç¶æçå¬å¨ |
| | | * |
| | | * <p>æµç¨å®ä¹ keyï¼mes_seed_quality_company_approveï¼businessKey 为çäº§æ¹æ¬¡å·ã |
| | | * è¯¥æ¹æ¬¡å线质æ£å
¨é¨å®æååªåèµ·ä¸æ¬¡è¯¥æµç¨ï¼å®¡æ¹å®æåææ¯ä¸ªè¢ç两æ¡çº¿ç»æç»ä¸æ¾è¡ææ¥åºã |
| | | */ |
| | | @Component |
| | | @Slf4j |
| | | public class MesQcSeedCompanyStatusListener extends BpmProcessInstanceStatusEventListener { |
| | | |
| | | private static final String BPM_PROCESS_DEFINITION_KEY = "mes_seed_quality_company_approve"; |
| | | |
| | | @Resource |
| | | private MesQcSeedQualityService seedQualityService; |
| | | |
| | | @Override |
| | | protected String getProcessDefinitionKey() { |
| | | return BPM_PROCESS_DEFINITION_KEY; |
| | | } |
| | | |
| | | @Override |
| | | protected void onEvent(BpmProcessInstanceStatusEvent event) { |
| | | if (!BpmProcessInstanceStatusEnum.isProcessEndStatus(event.getStatus()) |
| | | || BpmProcessInstanceStatusEnum.CANCEL.getStatus().equals(event.getStatus())) { |
| | | return; |
| | | } |
| | | String businessKey = event.getBusinessKey(); |
| | | if (businessKey == null || businessKey.isBlank()) { |
| | | log.warn("[onEvent][å
¬å¸è´¨æ£å®¡æ¹ businessKey 为空ï¼å¿½ç¥]"); |
| | | return; |
| | | } |
| | | boolean approved = BpmProcessInstanceStatusEnum.APPROVE.getStatus().equals(event.getStatus()); |
| | | log.info("[onEvent][å
¬å¸è´¨æ£å®¡æ¹å®æï¼æ¹æ¬¡={} ç»æ={}]", businessKey, event.getStatus()); |
| | | seedQualityService.handleCompanyProcessResult(event.getId(), businessKey, approved, event.getReason()); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.qc.seedquality.listener; |
| | | |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent; |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.qc.seedquality.MesQcSeedQualityService; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * ç§åè´¨æ£æ¬çº¿é¢å¯¼å®¡æ¹ç¶æçå¬å¨ |
| | | * |
| | | * <p>æµç¨å®ä¹ keyï¼mes_seed_quality_line_approveï¼businessKey æ ¼å¼ï¼{è´¨æ£çº¿}#{çäº§æ¹æ¬¡å·}ã |
| | | * 审æ¹éè¿åæ¬çº¿è¿å
¥å¤æ£ï¼é©³ååæ¬çº¿éæ°åæ£ã |
| | | */ |
| | | @Component |
| | | @Slf4j |
| | | public class MesQcSeedLineStatusListener extends BpmProcessInstanceStatusEventListener { |
| | | |
| | | private static final String BPM_PROCESS_DEFINITION_KEY = "mes_seed_quality_line_approve"; |
| | | |
| | | @Resource |
| | | private MesQcSeedQualityService seedQualityService; |
| | | |
| | | @Override |
| | | protected String getProcessDefinitionKey() { |
| | | return BPM_PROCESS_DEFINITION_KEY; |
| | | } |
| | | |
| | | @Override |
| | | protected void onEvent(BpmProcessInstanceStatusEvent event) { |
| | | if (!BpmProcessInstanceStatusEnum.isProcessEndStatus(event.getStatus()) |
| | | || BpmProcessInstanceStatusEnum.CANCEL.getStatus().equals(event.getStatus())) { |
| | | return; |
| | | } |
| | | String businessKey = event.getBusinessKey(); |
| | | int index = businessKey == null ? -1 : businessKey.indexOf('#'); |
| | | if (index <= 0 || index == businessKey.length() - 1) { |
| | | log.warn("[onEvent][æ¬çº¿å®¡æ¹ businessKey({}) æ ¼å¼æ æï¼åºä¸º è´¨æ£çº¿#æ¹æ¬¡å·]", businessKey); |
| | | return; |
| | | } |
| | | Integer qcLine; |
| | | try { |
| | | qcLine = Integer.valueOf(businessKey.substring(0, index)); |
| | | } catch (NumberFormatException ex) { |
| | | log.warn("[onEvent][æ¬çº¿å®¡æ¹ businessKey({}) è´¨æ£çº¿æ æ]", businessKey); |
| | | return; |
| | | } |
| | | String batchCode = businessKey.substring(index + 1); |
| | | boolean approved = BpmProcessInstanceStatusEnum.APPROVE.getStatus().equals(event.getStatus()); |
| | | log.info("[onEvent][æ¬çº¿å®¡æ¹å®æï¼æ¹æ¬¡={} è´¨æ£çº¿={} ç»æ={}]", batchCode, qcLine, event.getStatus()); |
| | | seedQualityService.handleLineProcessResult(event.getId(), qcLine, batchCode, approved, event.getReason()); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.qc.seedquality.listener; |
| | | |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent; |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.qc.seedquality.MesQcSeedQualityService; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * ç»éåéè´§ç§åè´¨æ£éå®é¨å®¡æ¹ç¶æçå¬å¨ |
| | | * |
| | | * <p>æµç¨å®ä¹ keyï¼mes_seed_return_approveï¼businessKey 为éå®éè´§åç¼å·ã |
| | | */ |
| | | @Component |
| | | @Slf4j |
| | | public class MesQcSeedReturnStatusListener extends BpmProcessInstanceStatusEventListener { |
| | | |
| | | private static final String BPM_PROCESS_DEFINITION_KEY = "mes_seed_return_approve"; |
| | | |
| | | @Resource |
| | | private MesQcSeedQualityService seedQualityService; |
| | | |
| | | @Override |
| | | protected String getProcessDefinitionKey() { |
| | | return BPM_PROCESS_DEFINITION_KEY; |
| | | } |
| | | |
| | | @Override |
| | | protected void onEvent(BpmProcessInstanceStatusEvent event) { |
| | | if (!BpmProcessInstanceStatusEnum.isProcessEndStatus(event.getStatus()) |
| | | || BpmProcessInstanceStatusEnum.CANCEL.getStatus().equals(event.getStatus())) { |
| | | return; |
| | | } |
| | | Long returnSalesId; |
| | | try { |
| | | returnSalesId = Long.parseLong(event.getBusinessKey()); |
| | | } catch (NumberFormatException ex) { |
| | | log.warn("[onEvent][éè´§è´¨æ£å®¡æ¹ businessKey({}) 䏿¯ææçéå®éè´§åç¼å·]", event.getBusinessKey()); |
| | | return; |
| | | } |
| | | boolean approved = BpmProcessInstanceStatusEnum.APPROVE.getStatus().equals(event.getStatus()); |
| | | log.info("[onEvent][éè´§è´¨æ£å®¡æ¹å®æï¼éå®éè´§å={} ç»æ={}]", returnSalesId, event.getStatus()); |
| | | seedQualityService.handleReturnProcessResult(event.getId(), returnSalesId, approved, event.getReason()); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | private MesProTracePublicRespVO.QualityVO buildQuality(MesProBatchDO batch) { |
| | | List<MesQcSeedQualityDO> list = qcSeedQualityService.getQualityByBatchCode(batch.getCode()); |
| | | // å线质æ£ï¼åä¸è¢åå¨å å·¥é¨ãéå®é¨ä¸¤æ¡è®°å½ï¼å
¨é¨æ¾è¡æç®åæ ¼ï¼ä»»ä¸æ¡æ¥åºå³ä¸åæ ¼ |
| | | List<MesQcSeedQualityDO> list = qcSeedQualityService.getQualityByBatchCode(batch.getCode()).stream() |
| | | .filter(quality -> quality.getBizType() == null || Integer.valueOf(1).equals(quality.getBizType())) |
| | | .toList(); |
| | | if (CollUtil.isEmpty(list)) { |
| | | return null; |
| | | } |
| | | MesQcSeedQualityDO quality = list.get(0); |
| | | boolean released = list.stream().allMatch(item -> Integer.valueOf(40).equals(item.getWorkflowStatus())); |
| | | boolean scrapped = list.stream().anyMatch(item -> Integer.valueOf(70).equals(item.getWorkflowStatus())); |
| | | MesProTracePublicRespVO.QualityVO vo = new MesProTracePublicRespVO.QualityVO(); |
| | | Integer finalResult = Integer.valueOf(40).equals(quality.getWorkflowStatus()) ? 1 |
| | | : Integer.valueOf(70).equals(quality.getWorkflowStatus()) ? 2 : 0; |
| | | Integer finalResult = scrapped ? 2 : released ? 1 : 0; |
| | | String reason = list.stream().map(MesQcSeedQualityDO::getReason) |
| | | .filter(StrUtil::isNotBlank).findFirst().orElse(quality.getReason()); |
| | | vo.setResult(finalResult).setResultName(finalResult == 0 ? "夿£æå®¡æ¹ä¸" : qualityResultName(finalResult)) |
| | | .setReason(quality.getReason()).setInspectorName(userName(quality.getInspectorId())) |
| | | .setReason(reason).setInspectorName(userName(quality.getInspectorId())) |
| | | .setInspectedAt(quality.getInspectedAt()); |
| | | List<MesQcSeedQualityIndicatorDO> indicators = qcSeedQualityService.getIndicatorsByQualityId(quality.getId()); |
| | | vo.setIndicators(convertList(indicators, ind -> { |
| | |
| | | |
| | | @Override |
| | | public void checkAreaMixingRule(Long areaId, Long itemId, Long batchId) { |
| | | // 1.1 æ ¡éªåºä½å¿
é¡»åå¨ |
| | | // 1.1 æ ¡éªåºä½å¿
é¡»åå¨ï¼æªä½¿ç¨åºä½æ¶è·³è¿æ··æ¾æ ¡éªï¼ |
| | | MesWmWarehouseAreaDO area = areaService.validateWarehouseAreaExists(areaId); |
| | | if (area == null) { |
| | | return; |
| | | } |
| | | |
| | | // 2. æ¥è¯¢å½ååºä½ä¸å·²æçåºåè®°å½ |
| | | List<MesWmMaterialStockDO> stockList = getMaterialStockListByAreaId(areaId); |
| | |
| | | || ObjUtil.notEqual(stock.getBatchId(), batchId) |
| | | || (batchCode != null && ObjUtil.notEqual(stock.getBatchCode(), batchCode)) |
| | | || ObjUtil.notEqual(stock.getWarehouseId(), warehouseId) |
| | | || ObjUtil.notEqual(stock.getLocationId(), locationId) |
| | | || ObjUtil.notEqual(stock.getAreaId(), areaId)) { |
| | | || (locationId != null && ObjUtil.notEqual(stock.getLocationId(), locationId)) |
| | | || (areaId != null && ObjUtil.notEqual(stock.getAreaId(), areaId))) { |
| | | throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH); |
| | | } |
| | | // 3. æ ¡éªåºåè®°å½å¿
é¡»æ¯åæ ¼åºï¼æååºåæªè´¨æ£ï¼ä¸å¯ç¨äºåºåº/å ç¨ï¼ |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesLineDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesDetailMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstockbag.MesWmMaterialStockBagMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmProductSalesStatusEnum; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.materialstock.MesWmMaterialStockService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseAreaService; |
| | |
| | | private MesWmMaterialStockService materialStockService; |
| | | @Resource |
| | | private MesWmWarehouseAreaService warehouseAreaService; |
| | | @Resource |
| | | private MesWmMaterialStockBagMapper stockBagMapper; |
| | | @Resource |
| | | private MesWmProductSalesMapper productSalesMapper; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createProductSalesDetail(MesWmProductSalesDetailSaveReqVO createReqVO) { |
| | | lockPickingOrder(createReqVO.getSalesId()); |
| | | if (createReqVO.getRemark() != null && createReqVO.getRemark().matches("ç§»å¨ç«¯æ è¢ç æ£è´§ #[a-f0-9-]{36}")) { |
| | | var existing = productSalesDetailMapper.selectListBySalesId(createReqVO.getSalesId()).stream() |
| | | .filter(d -> createReqVO.getRemark().equals(d.getRemark())).findFirst().orElse(null); |
| | | if (existing != null) { |
| | | if (ObjUtil.notEqual(existing.getLineId(), createReqVO.getLineId()) |
| | | || ObjUtil.notEqual(existing.getMaterialStockId(), createReqVO.getMaterialStockId()) |
| | | || existing.getQuantity().compareTo(createReqVO.getQuantity()) != 0) |
| | | throw new IllegalArgumentException("æ£è´§è¯·æ±æ è¯å·²ç¨äºå
¶ä»æç»"); |
| | | return existing.getId(); |
| | | } |
| | | } |
| | | // æ ¡éªæ°æ® |
| | | validateProductSalesDetailSaveData(createReqVO); |
| | | |
| | |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateProductSalesDetail(MesWmProductSalesDetailSaveReqVO updateReqVO) { |
| | | // æ ¡éªåå¨ |
| | | MesWmProductSalesDetailDO existing = validateProductSalesDetailExists(updateReqVO.getId()); |
| | | lockPickingOrder(existing.getSalesId()); |
| | | if (existing.getShipmentId() != null) throw new IllegalArgumentException("å·²åé
åè´§è®°å½çæç»ä¸è½ä¿®æ¹"); |
| | | if (ObjUtil.notEqual(existing.getSalesId(), updateReqVO.getSalesId())) throw exception(WM_PRODUCT_SALES_DETAIL_LINE_NOT_MATCH); |
| | | if (existing.getBagCodeId() != null) throw new IllegalArgumentException("æ«ç è¢ç æç»ä¸è½æå·¥ä¿®æ¹æ°éæåºåï¼è¯·å é¤åéæ°æ«ç éæ©"); |
| | | // æ ¡éªæ°æ® |
| | | validateProductSalesDetailSaveData(updateReqVO); |
| | |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deleteProductSalesDetail(Long id) { |
| | | // æ ¡éªåå¨ |
| | | validateProductSalesDetailExists(id); |
| | | MesWmProductSalesDetailDO detail = validateProductSalesDetailExists(id); |
| | | lockPickingOrder(detail.getSalesId()); |
| | | if (detail.getShipmentId() != null) throw new IllegalArgumentException("å·²åé
åè´§è®°å½çæç»ä¸è½å é¤"); |
| | | // å é¤ |
| | | productSalesDetailMapper.deleteById(id); |
| | | } |
| | |
| | | return detail; |
| | | } |
| | | |
| | | private void lockPickingOrder(Long salesId) { |
| | | var sales = productSalesMapper.selectByIdForUpdate(salesId); |
| | | if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS); |
| | | if (!MesWmProductSalesStatusEnum.APPROVING.getStatus().equals(sales.getStatus())) { |
| | | throw exception(WM_PRODUCT_SALES_CANNOT_PICK); |
| | | } |
| | | } |
| | | |
| | | private void validateProductSalesDetailSaveData(MesWmProductSalesDetailSaveReqVO reqVO) { |
| | | if (reqVO.getQuantity() == null || reqVO.getQuantity().signum() <= 0) throw new IllegalArgumentException("æ£è´§æ°éå¿
须大äºé¶"); |
| | | if (reqVO.getMaterialStockId() != null && stockBagMapper.selectOneActiveByMaterialStockId(reqVO.getMaterialStockId()) != null) { |
| | | throw new IllegalArgumentException("该åºååå¨è¢ç æç»ï¼è¯·éæ©å®é
æ£åºçè¢ç "); |
| | | } |
| | | // æ ¡éªç¶æ°æ®ï¼è¡ï¼åå¨ |
| | | MesWmProductSalesLineDO line = productSalesLineService.getProductSalesLine(reqVO.getLineId()); |
| | | if (line == null) { |
| | |
| | | if (ObjUtil.notEqual(line.getSalesId(), reqVO.getSalesId())) { |
| | | throw exception(WM_PRODUCT_SALES_DETAIL_LINE_NOT_MATCH); |
| | | } |
| | | var picked = productSalesDetailMapper.selectListByLineId(line.getId()).stream() |
| | | .filter(d -> !java.util.Objects.equals(d.getId(), reqVO.getId())) |
| | | .map(MesWmProductSalesDetailDO::getQuantity).reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add); |
| | | if (line.getQuantity() == null || picked.add(reqVO.getQuantity()).compareTo(line.getQuantity()) > 0) |
| | | throw new IllegalArgumentException("累计æ£è´§æ°éä¸è½è¶
è¿åºåºåè¡è®¡åæ°é"); |
| | | // æ ¡éªç©æåå¨ |
| | | mdmItemService.validateItemExistsAndEnable(reqVO.getItemId()); |
| | | if (ObjUtil.notEqual(line.getItemId(), reqVO.getItemId())) { |
| | | throw exception(WM_PRODUCT_SALES_DETAIL_ITEM_MISMATCH); |
| | | } |
| | | if (line.getBatchCode() != null && !line.getBatchCode().isBlank() |
| | | && !java.util.Objects.equals(line.getBatchCode(), reqVO.getBatchCode())) |
| | | throw new IllegalArgumentException("æéåºåæ¹æ¬¡ä¸åºåºåè¡æ¹æ¬¡ä¸ä¸è´"); |
| | | // æ ¡éªåºä½åå¨ |
| | | warehouseAreaService.validateWarehouseAreaExists( |
| | | reqVO.getWarehouseId(), reqVO.getLocationId(), reqVO.getAreaId()); |
| | |
| | | */ |
| | | void stockProductSales(Long id); |
| | | |
| | | List<cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO> getShipments(Long id); |
| | | |
| | | MesWmProductSalesScanRespVO scanProductSales(@Valid MesWmProductSalesScanReqVO reqVO); |
| | | |
| | | MesWmProductSalesScanRespVO previewScan(String code); |
| | |
| | | */ |
| | | void cancelProductSales(Long id); |
| | | |
| | | void closeRemaining(Long id, String reason); |
| | | |
| | | void cancelShipment(Long id); |
| | | |
| | | /** |
| | | * OQC æ£éªå®æåï¼èªå¨æµè½¬ï¼å¾
æ£æµ â å¾
æ£è´§ï¼ |
| | | * |
| | |
| | | @Validated |
| | | @Slf4j |
| | | public class MesWmProductSalesServiceImpl implements MesWmProductSalesService { |
| | | @Resource private cn.iocoder.yudao.module.mes.dal.mysql.wm.productsales.MesWmProductSalesShipmentMapper shipmentMapper; |
| | | |
| | | @Resource |
| | | private MesWmProductSalesMapper productSalesMapper; |
| | |
| | | |
| | | @Override |
| | | public boolean checkProductSalesQuantity(Long id) { |
| | | // æ ¡éªæ¯è¡æç»æ°éä¹åæ¯å¦çäºè¡åºåºæ°é |
| | | // æ¬æ¬¡è³å°æ£ä¸æ¡ï¼ç´¯è®¡æ£è´§ä¸è¶
è¿è®¡åï¼å
许跨è¡ã忬¡åè´§ã |
| | | List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(id); |
| | | boolean hasPending = false; |
| | | for (MesWmProductSalesLineDO line : lines) { |
| | | List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListByLineId(line.getId()); |
| | | if (CollUtil.isEmpty(details)) { |
| | | return false; |
| | | } |
| | | if (details.stream().anyMatch(d -> d.getShipmentId() == null)) hasPending = true; |
| | | if (details.stream().anyMatch(d -> d.getQuantity() == null || d.getQuantity().signum() <= 0)) return false; |
| | | BigDecimal totalDetailQty = CollectionUtils.getSumValue(details, |
| | | MesWmProductSalesDetailDO::getQuantity, BigDecimal::add, BigDecimal.ZERO); |
| | | if (line.getQuantity() != null && totalDetailQty.compareTo(line.getQuantity()) != 0) { |
| | | if (line.getQuantity() == null || totalDetailQty.compareTo(line.getQuantity()) > 0) { |
| | | return false; |
| | | } |
| | | } |
| | | return true; |
| | | return hasPending; |
| | | } |
| | | |
| | | @Override |
| | | public List<cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO> getShipments(Long id) { |
| | | validateProductSalesExists(id); |
| | | return shipmentMapper.selectBySalesId(id); |
| | | } |
| | | |
| | | private record PickingScope(String type, Long id, MesProBatchDO batch, List<MesProBagCodeDO> bags) { } |
| | |
| | | List<MesWmProductSalesScanRespVO.BagOption> options = scope.bags().stream().map(b -> { |
| | | String reason = unavailable(b, stockBags.get(b.getId()), stocks, occupied, scope.batch()); |
| | | return new MesWmProductSalesScanRespVO.BagOption().setId(b.getId()).setCode(b.getCode()).setBusinessCode(b.getBusinessCode()) |
| | | .setPalletId(b.getPalletId()).setSelectable(reason == null).setUnavailableReason(reason); |
| | | .setPalletId(b.getPalletId()).setMaterialStockId(stockBags.get(b.getId()) == null ? null : stockBags.get(b.getId()).getMaterialStockId()) |
| | | .setSelectable(reason == null).setUnavailableReason(reason); |
| | | }).toList(); |
| | | return new MesWmProductSalesScanRespVO().setScanType(scope.type()).setCodeId(scope.id()).setScanContent(code) |
| | | .setBatchCode(scope.batch().getCode()).setItemId(scope.batch().getItemId()).setBagCount(options.size()).setBags(options) |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public MesWmProductSalesScanRespVO scanProductSales(MesWmProductSalesScanReqVO reqVO) { |
| | | if (Boolean.TRUE.equals(reqVO.getManual()) && (reqVO.getLineId() == null || reqVO.getMaterialStockId() == null)) |
| | | throw new IllegalArgumentException("æå·¥éæ©è¢ç å¿
é¡»æå®åºåºåè¡ååºåè®°å½"); |
| | | MesWmProductSalesDO sales = productSalesMapper.selectByIdForUpdate(reqVO.getSalesId()); |
| | | if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS); |
| | | if (!Integer.valueOf(2).equals(sales.getStatus())) throw exception(WM_PRODUCT_SALES_CANNOT_PICK); |
| | |
| | | List<MesWmProductSalesDetailDO> occupied = productSalesDetailMapper.selectPickingByBatchCodeForUpdate(scope.batch().getCode()); |
| | | List<MesWmProductSalesDetailDO> own = occupied.stream().filter(d -> Objects.equals(d.getSalesId(), sales.getId())).toList(); |
| | | long already = selected.stream().filter(b -> own.stream().anyMatch(d -> Objects.equals(d.getBagCodeId(), b.getId()))).count(); |
| | | if (already > 0 && own.stream().anyMatch(d -> selectedIds.contains(d.getBagCodeId()) |
| | | && (reqVO.getLineId() != null && !Objects.equals(reqVO.getLineId(), d.getLineId()) |
| | | || reqVO.getMaterialStockId() != null && !Objects.equals(reqVO.getMaterialStockId(), d.getMaterialStockId())))) |
| | | throw new IllegalArgumentException("æéè¢ç å·²å å
¥æ¬åå
¶ä»è¡æåºåè®°å½"); |
| | | if (already == selected.size()) return new MesWmProductSalesScanRespVO().setScanType(scope.type()).setCodeId(scope.id()) |
| | | .setScanContent(reqVO.getScanContent()).setSelectedBagIds(ids).setAlreadyScanned(true).setOutboundQuantity(quantity) |
| | | .setDetailId(own.stream().filter(d -> selectedIds.contains(d.getBagCodeId())).findFirst().orElseThrow().getId()).setMessage("æéè¢ç 已卿¬åæ£è´§æç»ä¸"); |
| | |
| | | for (MesProBagCodeDO bag : selected) { |
| | | String reason = unavailable(bag, stockBags.get(bag.getId()), stocks, occupied, scope.batch()); |
| | | if (reason != null) throw new IllegalArgumentException("è¢ç " + bag.getCode() + "ï¼" + reason); |
| | | if (reqVO.getMaterialStockId() != null && !Objects.equals(reqVO.getMaterialStockId(), stockBags.get(bag.getId()).getMaterialStockId())) |
| | | throw new IllegalArgumentException("æéè¢ç ä¸å±äºæå®åºåè®°å½"); |
| | | } |
| | | MesWmProductSalesLineDO line = productSalesLineService.getProductSalesLineListBySalesId(sales.getId()).stream() |
| | | .filter(l -> Objects.equals(l.getItemId(), scope.batch().getItemId())).findFirst().orElse(null); |
| | | .filter(l -> Objects.equals(l.getItemId(), scope.batch().getItemId()) |
| | | && (reqVO.getLineId() == null || Objects.equals(l.getId(), reqVO.getLineId())) |
| | | && (l.getBatchCode() == null || l.getBatchCode().isBlank() || Objects.equals(l.getBatchCode(), wmBatch.getCode()))) |
| | | .filter(l -> l.getQuantity() != null && productSalesDetailService.getProductSalesDetailListByLineId(l.getId()).stream() |
| | | .map(MesWmProductSalesDetailDO::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add) |
| | | .add(quantity).compareTo(l.getQuantity()) <= 0) |
| | | .findFirst().orElse(null); |
| | | if (line == null) throw exception(WM_PRODUCT_SALES_DETAIL_ITEM_MISMATCH); |
| | | BigDecimal scanned = productSalesDetailService.getProductSalesDetailListByLineId(line.getId()).stream() |
| | | .map(MesWmProductSalesDetailDO::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | |
| | | .setItemId(scope.batch().getItemId()).setQuantity(BigDecimal.ONE).setBagCodeId(bag.getId()) |
| | | .setPalletCodeId(bag.getPalletId()).setBatchId(wmBatch.getId()).setBatchCode(wmBatch.getCode()).setMaterialStockId(stock.getId()) |
| | | .setWarehouseId(stock.getWarehouseId()).setLocationId(stock.getLocationId()).setAreaId(stock.getAreaId()) |
| | | .setRemark("æ«ç ç±»å " + scope.type() + "ï¼æ¥æºç " + reqVO.getScanContent() + "ï¼è¢ç " + bag.getCode()); |
| | | .setRemark((Boolean.TRUE.equals(reqVO.getManual()) ? "æå·¥éæ©ï¼" : "") + "æ«ç ç±»å " + scope.type() + "ï¼" + switch (scope.type()) { |
| | | case "BATCH" -> "æ¥æºæ¹æ¬¡ç "; |
| | | case "PALLET" -> "æ¥æºæçç "; |
| | | default -> "æ¥æºè¢ç "; |
| | | } + reqVO.getScanContent() + "ï¼è¢ç " + bag.getCode()); |
| | | productSalesDetailMapper.insert(detail); |
| | | if (firstDetail == null) firstDetail = detail.getId(); |
| | | } |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void stockProductSales(Long id) { |
| | | // æ ¡éªåå¨ |
| | | MesWmProductSalesDO sales = validateProductSalesExists(id); |
| | | MesWmProductSalesDO sales = productSalesMapper.selectByIdForUpdate(id); |
| | | if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS); |
| | | if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.APPROVING.getStatus(), sales.getStatus())) { |
| | | throw exception(WM_PRODUCT_SALES_CANNOT_PICK); |
| | | } |
| | | if (!checkProductSalesQuantity(id)) throw new IllegalArgumentException("请æ£éæ¬æ¬¡åè´§æç»ï¼ä¸ç´¯è®¡æ°éä¸è½è¶
è¿è®¡åæ°é"); |
| | | var shipment = new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO(); |
| | | shipment.setSalesId(id); shipment.setStatus(10); shipmentMapper.insert(shipment); |
| | | // æ ¡éªæ¯è¡è³å°ææ£è´§æç»ï¼å¹¶æ ¡éªåºåå
è¶³ |
| | | List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(id); |
| | | for (MesWmProductSalesLineDO line : lines) { |
| | | List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListByLineId(line.getId()); |
| | | if (CollUtil.isEmpty(details)) { |
| | | throw exception(WM_PRODUCT_SALES_DETAILS_EMPTY); |
| | | } |
| | | // æ ¡éªåºåå
足并å建å ç¨ |
| | | for (MesWmProductSalesDetailDO detail : details) { |
| | | if (detail.getShipmentId() != null) continue; |
| | | materialStockService.validateSelectedStock( |
| | | detail.getMaterialStockId(), |
| | | detail.getItemId(), |
| | |
| | | detail.getItemId(), |
| | | detail.getQuantity() |
| | | ); |
| | | productSalesDetailMapper.updateById(new MesWmProductSalesDetailDO().setId(detail.getId()).setShipmentId(shipment.getId())); |
| | | } |
| | | } |
| | | |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void shippingProductSales(MesWmProductSalesShippingReqVO reqVO) { |
| | | // æ ¡éªåå¨ |
| | | MesWmProductSalesDO sales = validateProductSalesExists(reqVO.getId()); |
| | | MesWmProductSalesDO sales = productSalesMapper.selectByIdForUpdate(reqVO.getId()); |
| | | if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS); |
| | | if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.SHIPPING.getStatus(), sales.getStatus())) { |
| | | throw exception(WM_PRODUCT_SALES_CANNOT_SHIPPING); |
| | | } |
| | | |
| | | var shipment = ensureActiveShipment(sales, 10); |
| | | shipmentMapper.updateById(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO() |
| | | .setId(shipment.getId()).setStatus(20).setCarrier(reqVO.getCarrier()).setShippingNumber(reqVO.getShippingNumber())); |
| | | |
| | | // å¡«åè¿åï¼å¾
å¡«åè¿å â å¾
æ§è¡åºåºï¼ |
| | | productSalesMapper.updateById(new MesWmProductSalesDO() |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void finishProductSales(Long id) { |
| | | // 1. æ ¡éªåå¨ |
| | | MesWmProductSalesDO sales = validateProductSalesExists(id); |
| | | MesWmProductSalesDO sales = productSalesMapper.selectByIdForUpdate(id); |
| | | if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS); |
| | | if (ObjUtil.notEqual(MesWmProductSalesStatusEnum.APPROVED.getStatus(), sales.getStatus())) { |
| | | throw exception(WM_PRODUCT_SALES_CANNOT_FINISH); |
| | | } |
| | | var shipment = ensureActiveShipment(sales, 20); |
| | | List<MesWmProductSalesDetailDO> outDetails = productSalesDetailService.getProductSalesDetailListBySalesId(id).stream() |
| | | .filter(d -> Objects.equals(d.getShipmentId(), shipment.getId())).toList(); |
| | | if (outDetails.isEmpty()) throw new IllegalArgumentException("æ¬æ¬¡å货没æåºåºæç»"); |
| | | |
| | | // 2. 确认å ç¨è½¬åºåºï¼éæ¾å ç¨éï¼ |
| | | stockReserveService.confirmReserveToOutbound(MesWmStockReserveBizTypeEnum.PRODUCT_SALES.getType(), id); |
| | | |
| | | // 3. éåæææç»ï¼å建åºåäºå¡ï¼æ£ååºå + è®°å½æµæ°´ï¼ |
| | | createTransactionList(sales); |
| | | createTransactionList(sales, outDetails); |
| | | |
| | | shipmentMapper.updateById(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO() |
| | | .setId(shipment.getId()).setStatus(30).setOutboundTime(java.time.LocalDateTime.now()) |
| | | .setOutboundUserId(SecurityFrameworkUtils.getLoginUserId())); |
| | | boolean complete = productSalesLineService.getProductSalesLineListBySalesId(id).stream().allMatch(line -> { |
| | | BigDecimal total = productSalesDetailService.getProductSalesDetailListByLineId(line.getId()).stream() |
| | | .filter(d -> d.getShipmentId() != null).map(MesWmProductSalesDetailDO::getQuantity) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | return line.getQuantity() != null && total.compareTo(line.getQuantity()) == 0; |
| | | }); |
| | | |
| | | // 4. åºåç»éåä¸å®é
åºåºæä½ä¿¡æ¯ |
| | | CrmCustomerRespDTO customer = customerApi.getCustomer(sales.getClientId()); |
| | |
| | | .setClientName(customer != null ? customer.getName() : null) |
| | | .setOutboundTime(java.time.LocalDateTime.now()) |
| | | .setOutboundUserId(SecurityFrameworkUtils.getLoginUserId()) |
| | | .setStatus(MesWmProductSalesStatusEnum.FINISHED.getStatus())); |
| | | .setStatus(complete ? MesWmProductSalesStatusEnum.FINISHED.getStatus() : MesWmProductSalesStatusEnum.APPROVING.getStatus())); |
| | | |
| | | // 5. æ´æ°å
³èçåè´§éç¥ååºåºæ°éåç¶æ |
| | | if (sales.getNoticeId() != null) { |
| | | updateSalesNoticeOutCount(sales); |
| | | updateSalesNoticeOutCount(sales, outDetails); |
| | | } |
| | | |
| | | // 6. ååè¢ç /æçåºåºå»åï¼ä¾çªè´§ç¨½æ¥å®ä½ç»éåå°åºï¼ï¼å¹¶çæåºåºå®æ´æ§æè¦ |
| | | List<MesWmProductSalesDetailDO> outDetails = productSalesDetailService.getProductSalesDetailListBySalesId(id); |
| | | for (MesWmProductSalesDetailDO detail : outDetails) { |
| | | if (detail.getBagCodeId() != null) { |
| | | bagCodeService.markOutbound(detail.getBagCodeId(), id); |
| | |
| | | palletService.markOutbound(detail.getPalletCodeId(), id); |
| | | } |
| | | } |
| | | integrityService.recordSalesIntegrity(id); |
| | | if (complete) integrityService.recordSalesIntegrity(id); |
| | | } |
| | | |
| | | /** |
| | | * æ´æ°åè´§éç¥åçåºåºæ°éåç¶æ |
| | | */ |
| | | private void updateSalesNoticeOutCount(MesWmProductSalesDO sales) { |
| | | private void updateSalesNoticeOutCount(MesWmProductSalesDO sales, List<MesWmProductSalesDetailDO> outDetails) { |
| | | // è·ååºåºåè¡ |
| | | List<MesWmProductSalesLineDO> lines = productSalesLineService.getProductSalesLineListBySalesId(sales.getId()); |
| | | if (CollUtil.isEmpty(lines)) { |
| | |
| | | } |
| | | // éåè¡ï¼æ noticeLineId æ´æ°åºåºæ°é |
| | | for (MesWmProductSalesLineDO line : lines) { |
| | | if (line.getNoticeLineId() != null && line.getQuantity() != null) { |
| | | salesNoticeService.updateSalesNoticeLineOutCount(line.getNoticeLineId(), line.getQuantity()); |
| | | BigDecimal quantity = outDetails.stream().filter(d -> Objects.equals(d.getLineId(), line.getId())) |
| | | .map(MesWmProductSalesDetailDO::getQuantity).reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | if (line.getNoticeLineId() != null && quantity.signum() > 0) { |
| | | salesNoticeService.updateSalesNoticeLineOutCount(line.getNoticeLineId(), quantity); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void createTransactionList(MesWmProductSalesDO sales) { |
| | | List<MesWmProductSalesDetailDO> details = productSalesDetailService.getProductSalesDetailListBySalesId(sales.getId()); |
| | | private void createTransactionList(MesWmProductSalesDO sales, List<MesWmProductSalesDetailDO> details) { |
| | | Set<Long> bagIds = details.stream().map(MesWmProductSalesDetailDO::getBagCodeId).filter(Objects::nonNull).collect(Collectors.toSet()); |
| | | Map<Long, MesProBagCodeDO> bags = bagIds.isEmpty() ? Map.of() : pickingBagMapper.selectBatchIds(bagIds).stream() |
| | | .collect(Collectors.toMap(MesProBagCodeDO::getId, b -> b)); |
| | | wmTransactionService.createTransactionList(convertList(details, detail -> new MesWmTransactionSaveReqDTO() |
| | | .setType(MesWmTransactionTypeEnum.OUT.getType()).setItemId(detail.getItemId()) |
| | | .setQuantity(detail.getQuantity().negate()) // åºåºæ°éä¸ºè´æ° |
| | | .setBatchId(detail.getBatchId()).setBatchCode(detail.getBatchCode()) |
| | | .setBagCodeId(detail.getBagCodeId()) // æ°æ«ç æç»éè¢å
³èï¼æ§è¡åºåºéè¢æ´æ°åºåç¶æ |
| | | .setShipmentId(detail.getShipmentId()) |
| | | .setBagCode(detail.getBagCodeId() == null || !bags.containsKey(detail.getBagCodeId()) ? null : bags.get(detail.getBagCodeId()).getCode()) |
| | | .setWarehouseId(detail.getWarehouseId()).setLocationId(detail.getLocationId()).setAreaId(detail.getAreaId()) |
| | | .setBizType(MesBizTypeConstants.WM_PRODUCT_SALES).setBizId(sales.getId()) |
| | | .setBizCode(sales.getCode()).setBizLineId(detail.getLineId()) |
| | | .setMaterialStockId(detail.getMaterialStockId()))); // æå®åºåè®°å½IDï¼é¿å
éæ°å¹é
|
| | | } |
| | | |
| | | /** å
¼å®¹å级åå·²è¿å
¥å¾
è¿åæå¾
åºåºç¶æçæ§åæ®ãè°ç¨æ¹é¡»ææåºåºåè¡éã */ |
| | | private cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO ensureActiveShipment( |
| | | MesWmProductSalesDO sales, int expectedStatus) { |
| | | var shipment = shipmentMapper.selectActive(sales.getId()); |
| | | if (shipment != null) { |
| | | if (!Integer.valueOf(expectedStatus).equals(shipment.getStatus())) throw new IllegalArgumentException("åè´§è®°å½ç¶æå¼å¸¸ï¼è¯·å·æ°åéè¯"); |
| | | return shipment; |
| | | } |
| | | shipment = new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO(); |
| | | shipment.setSalesId(sales.getId()); shipment.setStatus(expectedStatus); |
| | | shipment.setCarrier(sales.getCarrier()); shipment.setShippingNumber(sales.getShippingNumber()); |
| | | shipmentMapper.insert(shipment); |
| | | for (MesWmProductSalesDetailDO detail : productSalesDetailService.getProductSalesDetailListBySalesId(sales.getId())) { |
| | | if (detail.getShipmentId() == null) |
| | | productSalesDetailMapper.updateById(new MesWmProductSalesDetailDO().setId(detail.getId()).setShipmentId(shipment.getId())); |
| | | } |
| | | return shipment; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void cancelProductSales(Long id) { |
| | | // æ ¡éªåå¨ |
| | | MesWmProductSalesDO sales = validateProductSalesExists(id); |
| | | MesWmProductSalesDO sales = productSalesMapper.selectByIdForUpdate(id); |
| | | if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS); |
| | | // 已宿å已忶ä¸å
è®¸åæ¶ |
| | | if (ObjectUtils.equalsAny(sales.getStatus(), |
| | | MesWmProductSalesStatusEnum.FINISHED.getStatus(), |
| | | MesWmProductSalesStatusEnum.CANCELED.getStatus())) { |
| | | MesWmProductSalesStatusEnum.CANCELED.getStatus(), MesWmProductSalesStatusEnum.CLOSED.getStatus())) { |
| | | throw exception(WM_PRODUCT_SALES_CANNOT_CANCEL); |
| | | } |
| | | if (shipmentMapper.selectBySalesId(id).stream().anyMatch(s -> Integer.valueOf(30).equals(s.getStatus()))) |
| | | throw new IllegalArgumentException("该åå·²æåºåºè®°å½ï¼è¯·å
³éå©ä½æ°éï¼ä¸è½åæ¶å·²åè´§åæ®"); |
| | | // éæ¾åºåå ç¨ |
| | | stockReserveService.releaseReserve(MesWmStockReserveBizTypeEnum.PRODUCT_SALES.getType(), id); |
| | | var shipment = shipmentMapper.selectActive(id); |
| | | if (shipment != null) shipmentMapper.updateById(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO() |
| | | .setId(shipment.getId()).setStatus(40)); |
| | | // åæ¶ |
| | | productSalesMapper.updateById(new MesWmProductSalesDO() |
| | | .setId(id).setStatus(MesWmProductSalesStatusEnum.CANCELED.getStatus())); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void closeRemaining(Long id, String reason) { |
| | | var sales = productSalesMapper.selectByIdForUpdate(id); |
| | | if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS); |
| | | if (!MesWmProductSalesStatusEnum.APPROVING.getStatus().equals(sales.getStatus())) |
| | | throw new IllegalArgumentException("ä»
å¾
æ£è´§ç¶æå¯å
³éå©ä½æ°é"); |
| | | if (reason == null || reason.isBlank() || reason.trim().length() > 500) |
| | | throw new IllegalArgumentException("请填åä¸è¶
è¿500åçå
³éåå "); |
| | | if (shipmentMapper.selectBySalesId(id).stream().noneMatch(s -> Integer.valueOf(30).equals(s.getStatus()))) |
| | | throw new IllegalArgumentException("å°æªåçåºåºï¼è¯·ä½¿ç¨åæ¶æä½"); |
| | | if (productSalesDetailService.getProductSalesDetailListBySalesId(id).stream().anyMatch(d -> d.getShipmentId() == null)) |
| | | throw new IllegalArgumentException("请å
å 餿¬æ¬¡æªåè´§çæ£è´§æç»"); |
| | | productSalesMapper.updateById(new MesWmProductSalesDO().setId(id) |
| | | .setStatus(MesWmProductSalesStatusEnum.CLOSED.getStatus()).setCloseReason(reason.trim())); |
| | | integrityService.recordSalesIntegrity(id); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void cancelShipment(Long id) { |
| | | var sales = productSalesMapper.selectByIdForUpdate(id); |
| | | if (sales == null) throw exception(WM_PRODUCT_SALES_NOT_EXISTS); |
| | | if (!ObjectUtils.equalsAny(sales.getStatus(), MesWmProductSalesStatusEnum.SHIPPING.getStatus(), MesWmProductSalesStatusEnum.APPROVED.getStatus())) |
| | | throw new IllegalArgumentException("ä»
å¾
è¿åæå¾
åºåºçåè´§è®°å½å¯åæ¶"); |
| | | int expected = MesWmProductSalesStatusEnum.SHIPPING.getStatus().equals(sales.getStatus()) ? 10 : 20; |
| | | var shipment = ensureActiveShipment(sales, expected); |
| | | stockReserveService.releaseReserve(MesWmStockReserveBizTypeEnum.PRODUCT_SALES.getType(), id); |
| | | productSalesDetailMapper.unassignShipment(shipment.getId()); |
| | | shipmentMapper.updateById(new cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesShipmentDO() |
| | | .setId(shipment.getId()).setStatus(40)); |
| | | productSalesMapper.updateById(new MesWmProductSalesDO().setId(id).setStatus(MesWmProductSalesStatusEnum.APPROVING.getStatus())); |
| | | } |
| | | |
| | | @Override |
| | | public void confirmProductSales(Long id) { |
| | | // æ ¡éªåå¨ + å¾
æ£æµç¶æ |
| | | MesWmProductSalesDO sales = validateProductSalesExists(id); |
| | |
| | | throw exception(WM_MATERIAL_STOCK_NOT_EXISTS); |
| | | } |
| | | // æ ¡éªç©æãæ¹æ¬¡ãä»åºç维度ä¸è´ï¼é²æ¢ä¼ å
¥é误ç materialStockIdï¼ |
| | | // åºåº/åºä½ä¸ºå¯éç»´åº¦ï¼æªæå®æ¶ä¸å䏿¯å¯¹ |
| | | 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())) { |
| | | || (reqDTO.getLocationId() != null && ObjUtil.notEqual(materialStock.getLocationId(), reqDTO.getLocationId())) |
| | | || (reqDTO.getAreaId() != null && ObjUtil.notEqual(materialStock.getAreaId(), reqDTO.getAreaId()))) { |
| | | throw exception(WM_MATERIAL_STOCK_SELECTION_MISMATCH); |
| | | } |
| | | } else if (isInbound) { |
| | |
| | | .type(reqDTO.getType()).quantity(reqDTO.getQuantity()).transactionTime(LocalDateTime.now()) |
| | | .itemId(reqDTO.getItemId()).batchId(reqDTO.getBatchId()).batchCode(reqDTO.getBatchCode()) |
| | | .bagCodeId(reqDTO.getBagCodeId()).bagCode(reqDTO.getBagCode()) |
| | | .shipmentId(reqDTO.getShipmentId()) |
| | | .warehouseId(reqDTO.getWarehouseId()).locationId(reqDTO.getLocationId()).areaId(reqDTO.getAreaId()) |
| | | .bizType(reqDTO.getBizType()).bizId(reqDTO.getBizId()).bizCode(reqDTO.getBizCode()).bizLineId(reqDTO.getBizLineId()) |
| | | .materialStockId(materialStock.getId()).relatedTransactionId(reqDTO.getRelatedTransactionId()) |
| | |
| | | if (Boolean.TRUE.equals(warehouse.getFrozen())) { |
| | | throw exception(WM_TRANSACTION_WAREHOUSE_FROZEN, warehouse.getName()); |
| | | } |
| | | // 1.2 æ£æ¥åºåºå»ç» |
| | | // 1.2 æ£æ¥åºåºå»ç»ï¼æªä½¿ç¨åºåºæ¶è·³è¿ï¼ |
| | | MesWmWarehouseLocationDO location = locationService.validateWarehouseLocationExists(reqDTO.getLocationId()); |
| | | if (Boolean.TRUE.equals(location.getFrozen())) { |
| | | if (location != null && Boolean.TRUE.equals(location.getFrozen())) { |
| | | throw exception(WM_TRANSACTION_LOCATION_FROZEN, location.getName()); |
| | | } |
| | | // 1.3 æ£æ¥åºä½å»ç» |
| | | // 1.3 æ£æ¥åºä½å»ç»ï¼æªä½¿ç¨åºä½æ¶è·³è¿ï¼ |
| | | MesWmWarehouseAreaDO area = areaService.validateWarehouseAreaExists(reqDTO.getAreaId()); |
| | | if (Boolean.TRUE.equals(area.getFrozen())) { |
| | | if (area != null && Boolean.TRUE.equals(area.getFrozen())) { |
| | | throw exception(WM_TRANSACTION_AREA_FROZEN, area.getName()); |
| | | } |
| | | |
| | | // 2. æ£æ¥åºåè®°å½å»ç» |
| | | if (Boolean.TRUE.equals(materialStock.getFrozen())) { |
| | | throw exception(WM_TRANSACTION_STOCK_FROZEN, warehouse.getName(), location.getName(), area.getName()); |
| | | throw exception(WM_TRANSACTION_STOCK_FROZEN, warehouse.getName(), |
| | | location != null ? location.getName() : "", area != null ? area.getName() : ""); |
| | | } |
| | | } |
| | | |
| | |
| | | private Long bagCodeId; |
| | | /** è¢ç */ |
| | | private String bagCode; |
| | | private Long shipmentId; |
| | | |
| | | // ===== åºåä½ç½® ===== |
| | | |
| | |
| | | @NotNull(message = "ä»åº ID ä¸è½ä¸ºç©º") |
| | | private Long warehouseId; |
| | | /** |
| | | * åºåº ID |
| | | * åºåº IDï¼å¯éï¼ |
| | | */ |
| | | @NotNull(message = "åºåº ID ä¸è½ä¸ºç©º") |
| | | private Long locationId; |
| | | /** |
| | | * åºä½ ID |
| | | * åºä½ IDï¼å¯éï¼ |
| | | */ |
| | | @NotNull(message = "åºä½ ID ä¸è½ä¸ºç©º") |
| | | private Long areaId; |
| | | |
| | | // ===== å°è´¦è¡¥å
åæ®µï¼å
¥åºæ¶ç¨äºåå§å MesWmMaterialStockDOï¼ ===== |
| | |
| | | |
| | | @Override |
| | | public MesWmWarehouseAreaDO validateWarehouseAreaExists(Long id) { |
| | | if (id == null) { |
| | | return null; |
| | | } |
| | | MesWmWarehouseAreaDO area = areaMapper.selectById(id); |
| | | if (area == null) { |
| | | throw exception(WM_WAREHOUSE_AREA_NOT_EXISTS); |
| | |
| | | |
| | | @Override |
| | | public MesWmWarehouseLocationDO validateWarehouseLocationExists(Long id) { |
| | | if (id == null) { |
| | | return null; |
| | | } |
| | | MesWmWarehouseLocationDO location = locationMapper.selectById(id); |
| | | if (location == null) { |
| | | throw exception(WM_WAREHOUSE_LOCATION_NOT_EXISTS); |
| | |
| | | private MesWmWarehouseMapper warehouseMapper; |
| | | |
| | | @Resource |
| | | private MesWmWarehouseLocationService locationService; |
| | | @Resource |
| | | @Lazy |
| | | private MesMdWorkstationService workstationService; |
| | | @Resource |
| | |
| | | MesWmWarehouseDO warehouse = validateWarehouseExists(id); |
| | | // æ ¡éªèæä»åºä¸å
许å é¤ |
| | | validateNotVirtual(warehouse.getCode()); |
| | | // æ ¡éªæ¯å¦æåºåº |
| | | if (locationService.getWarehouseLocationCountByWarehouseId(id) > 0) { |
| | | throw exception(WM_WAREHOUSE_HAS_LOCATION); |
| | | } |
| | | // æ ¡éªæ¯å¦è¢«å·¥ä½ç«å¼ç¨ |
| | | if (workstationService.getWorkstationCountByWarehouseId(id) > 0) { |
| | | throw exception(WM_WAREHOUSE_HAS_WORKSTATION); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" targetNamespace="http://www.flowable.org/processdef"> |
| | | <process id="mes_seed_quality_company_approve" name="ç§åå
¬å¸è´¨æ£å®¡æ¹" isExecutable="true"> |
| | | <startEvent id="start" /> |
| | | <sequenceFlow id="start_to_review" sourceRef="start" targetRef="review" /> |
| | | <userTask id="review" name="å
¬å¸è´¨æ£å®¡æ¹ï¼æç± admin å¤çï¼" flowable:candidateStrategy="30" flowable:candidateParam="1" /> |
| | | <sequenceFlow id="review_to_end" sourceRef="review" targetRef="end" /> |
| | | <endEvent id="end" /> |
| | | </process> |
| | | <bpmndi:BPMNDiagram id="BPMNDiagram_mes_seed_quality_company_approve"> |
| | | <bpmndi:BPMNPlane bpmnElement="mes_seed_quality_company_approve" id="BPMNPlane_mes_seed_quality_company_approve"> |
| | | <bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start"> |
| | | <omgdc:Bounds height="36.0" width="36.0" x="160.0" y="182.0"></omgdc:Bounds> |
| | | </bpmndi:BPMNShape> |
| | | <bpmndi:BPMNShape bpmnElement="review" id="BPMNShape_review"> |
| | | <omgdc:Bounds height="80.0" width="100.0" x="250.0" y="160.0"></omgdc:Bounds> |
| | | </bpmndi:BPMNShape> |
| | | <bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end"> |
| | | <omgdc:Bounds height="36.0" width="36.0" x="400.0" y="182.0"></omgdc:Bounds> |
| | | </bpmndi:BPMNShape> |
| | | <bpmndi:BPMNEdge bpmnElement="start_to_review" id="BPMNEdge_start_to_review"> |
| | | <omgdi:waypoint x="196.0" y="200.0"></omgdi:waypoint> |
| | | <omgdi:waypoint x="250.0" y="200.0"></omgdi:waypoint> |
| | | </bpmndi:BPMNEdge> |
| | | <bpmndi:BPMNEdge bpmnElement="review_to_end" id="BPMNEdge_review_to_end"> |
| | | <omgdi:waypoint x="350.0" y="200.0"></omgdi:waypoint> |
| | | <omgdi:waypoint x="400.0" y="200.0"></omgdi:waypoint> |
| | | </bpmndi:BPMNEdge> |
| | | </bpmndi:BPMNPlane> |
| | | </bpmndi:BPMNDiagram> |
| | | </definitions> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" targetNamespace="http://www.flowable.org/processdef"> |
| | | <process id="mes_seed_quality_line_approve" name="ç§åè´¨æ£é¨é¨å®¡æ¹" isExecutable="true"> |
| | | <startEvent id="start" /> |
| | | <sequenceFlow id="start_to_review" sourceRef="start" targetRef="review" /> |
| | | <userTask id="review" name="é¨é¨é¢å¯¼å®¡æ¹ï¼æç± admin å¤çï¼" flowable:candidateStrategy="30" flowable:candidateParam="1" /> |
| | | <sequenceFlow id="review_to_end" sourceRef="review" targetRef="end" /> |
| | | <endEvent id="end" /> |
| | | </process> |
| | | <bpmndi:BPMNDiagram id="BPMNDiagram_mes_seed_quality_line_approve"> |
| | | <bpmndi:BPMNPlane bpmnElement="mes_seed_quality_line_approve" id="BPMNPlane_mes_seed_quality_line_approve"> |
| | | <bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start"> |
| | | <omgdc:Bounds height="36.0" width="36.0" x="160.0" y="182.0"></omgdc:Bounds> |
| | | </bpmndi:BPMNShape> |
| | | <bpmndi:BPMNShape bpmnElement="review" id="BPMNShape_review"> |
| | | <omgdc:Bounds height="80.0" width="100.0" x="250.0" y="160.0"></omgdc:Bounds> |
| | | </bpmndi:BPMNShape> |
| | | <bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end"> |
| | | <omgdc:Bounds height="36.0" width="36.0" x="400.0" y="182.0"></omgdc:Bounds> |
| | | </bpmndi:BPMNShape> |
| | | <bpmndi:BPMNEdge bpmnElement="start_to_review" id="BPMNEdge_start_to_review"> |
| | | <omgdi:waypoint x="196.0" y="200.0"></omgdi:waypoint> |
| | | <omgdi:waypoint x="250.0" y="200.0"></omgdi:waypoint> |
| | | </bpmndi:BPMNEdge> |
| | | <bpmndi:BPMNEdge bpmnElement="review_to_end" id="BPMNEdge_review_to_end"> |
| | | <omgdi:waypoint x="350.0" y="200.0"></omgdi:waypoint> |
| | | <omgdi:waypoint x="400.0" y="200.0"></omgdi:waypoint> |
| | | </bpmndi:BPMNEdge> |
| | | </bpmndi:BPMNPlane> |
| | | </bpmndi:BPMNDiagram> |
| | | </definitions> |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:flowable="http://flowable.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" targetNamespace="http://www.flowable.org/processdef"> |
| | | <process id="mes_seed_return_approve" name="ç»éåéè´§ç§åè´¨æ£å®¡æ¹" isExecutable="true"> |
| | | <startEvent id="start" /> |
| | | <sequenceFlow id="start_to_review" sourceRef="start" targetRef="review" /> |
| | | <userTask id="review" name="éå®é¨é¢å¯¼å®¡æ¹ï¼æç± admin å¤çï¼" flowable:candidateStrategy="30" flowable:candidateParam="1" /> |
| | | <sequenceFlow id="review_to_end" sourceRef="review" targetRef="end" /> |
| | | <endEvent id="end" /> |
| | | </process> |
| | | <bpmndi:BPMNDiagram id="BPMNDiagram_mes_seed_return_approve"> |
| | | <bpmndi:BPMNPlane bpmnElement="mes_seed_return_approve" id="BPMNPlane_mes_seed_return_approve"> |
| | | <bpmndi:BPMNShape bpmnElement="start" id="BPMNShape_start"> |
| | | <omgdc:Bounds height="36.0" width="36.0" x="160.0" y="182.0"></omgdc:Bounds> |
| | | </bpmndi:BPMNShape> |
| | | <bpmndi:BPMNShape bpmnElement="review" id="BPMNShape_review"> |
| | | <omgdc:Bounds height="80.0" width="100.0" x="250.0" y="160.0"></omgdc:Bounds> |
| | | </bpmndi:BPMNShape> |
| | | <bpmndi:BPMNShape bpmnElement="end" id="BPMNShape_end"> |
| | | <omgdc:Bounds height="36.0" width="36.0" x="400.0" y="182.0"></omgdc:Bounds> |
| | | </bpmndi:BPMNShape> |
| | | <bpmndi:BPMNEdge bpmnElement="start_to_review" id="BPMNEdge_start_to_review"> |
| | | <omgdi:waypoint x="196.0" y="200.0"></omgdi:waypoint> |
| | | <omgdi:waypoint x="250.0" y="200.0"></omgdi:waypoint> |
| | | </bpmndi:BPMNEdge> |
| | | <bpmndi:BPMNEdge bpmnElement="review_to_end" id="BPMNEdge_review_to_end"> |
| | | <omgdi:waypoint x="350.0" y="200.0"></omgdi:waypoint> |
| | | <omgdi:waypoint x="400.0" y="200.0"></omgdi:waypoint> |
| | | </bpmndi:BPMNEdge> |
| | | </bpmndi:BPMNPlane> |
| | | </bpmndi:BPMNDiagram> |
| | | </definitions> |
| | |
| | | --> |
| | | <select id="selectSalesTargetList" parameterType="String" |
| | | resultType="cn.iocoder.yudao.module.mes.controller.admin.wm.batch.vo.MesWmBatchSalesTargetRespVO"> |
| | | SELECT s.code AS sales_code, s.sales_date, s.sales_order_code, s.client_id, s.client_name, s.contact_name, |
| | | oqc.code AS oqc_code, oqc.check_result AS check_result, |
| | | sl.batch_code, sl.item_id, sl.quantity, sl.quality_status |
| | | FROM mes_wm_product_sales_line sl |
| | | JOIN mes_wm_product_sales s ON s.id = sl.sales_id AND s.deleted = 0 |
| | | LEFT JOIN mes_qc_oqc oqc ON oqc.id = sl.oqc_id AND oqc.deleted = 0 |
| | | WHERE sl.batch_code = #{batchCode} AND sl.deleted = 0 AND s.status != 5 |
| | | AND NOT EXISTS (SELECT 1 FROM mes_wm_product_sales_detail d |
| | | WHERE d.line_id = sl.id AND d.batch_code = #{batchCode} AND d.deleted = 0) |
| | | UNION ALL |
| | | SELECT s.code AS sales_code, s.sales_date, s.sales_order_code, s.client_id, s.client_name, s.contact_name, |
| | | oqc.code AS oqc_code, oqc.check_result AS check_result, |
| | | d.batch_code, d.item_id, SUM(d.quantity) AS quantity, MAX(sl.quality_status) AS quality_status |
| | | FROM mes_wm_product_sales_detail d |
| | | JOIN mes_wm_product_sales s ON s.id = d.sales_id AND s.deleted = 0 |
| | | JOIN mes_wm_product_sales_line sl ON sl.id = d.line_id AND sl.deleted = 0 |
| | | LEFT JOIN mes_qc_oqc oqc ON oqc.id = sl.oqc_id AND oqc.deleted = 0 |
| | | WHERE d.batch_code = #{batchCode} AND d.deleted = 0 AND s.status != 5 |
| | | GROUP BY s.id, s.code, s.sales_date, s.sales_order_code, s.client_id, s.client_name, s.contact_name, |
| | | oqc.code, oqc.check_result, d.batch_code, d.item_id |
| | | SELECT s.code AS sales_code, tx.shipment_id, COALESCE(sh.outbound_time, MAX(tx.transaction_time)) AS sales_date, |
| | | s.sales_order_code, s.client_id, s.client_name, s.contact_name, |
| | | tx.batch_code, tx.item_id, -SUM(tx.quantity) AS quantity |
| | | FROM mes_wm_transaction tx |
| | | JOIN mes_wm_product_sales s ON s.id = tx.biz_id AND s.deleted = 0 |
| | | LEFT JOIN mes_wm_product_sales_shipment sh ON sh.id = tx.shipment_id AND sh.deleted = 0 |
| | | WHERE tx.batch_code = #{batchCode} AND tx.deleted = 0 AND tx.biz_type = 119 AND tx.type = 2 |
| | | GROUP BY s.id, s.code, tx.shipment_id, sh.outbound_time, s.sales_order_code, |
| | | s.client_id, s.client_name, s.contact_name, tx.batch_code, tx.item_id |
| | | ORDER BY sales_date |
| | | </select> |
| | | |
| | |
| | | package cn.iocoder.yudao.module.mes.service.qc.seedquality; |
| | | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedIndicatorRangeSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.mdm.MesMdmItemDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedIndicatorRangeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.qc.seedquality.MesQcSeedIndicatorRangeMapper; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.springframework.test.util.ReflectionTestUtils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | import static org.mockito.Mockito.*; |
| | | |
| | | class MesQcSeedIndicatorRangeServiceTest { |
| | | private final MesQcSeedIndicatorRangeService service = new MesQcSeedIndicatorRangeService(); |
| | | private final MesQcSeedIndicatorRangeMapper mapper = mock(MesQcSeedIndicatorRangeMapper.class); |
| | | private final MesMdmItemService itemService = mock(MesMdmItemService.class); |
| | | |
| | | @Test |
| | | void savesTemplateWithoutRestrictingActualInspection() { |
| | | void createsIndicatorRangeForProduct() { |
| | | ReflectionTestUtils.setField(service, "mapper", mapper); |
| | | ReflectionTestUtils.setField(service, "mdmItemService", itemService); |
| | | MesMdmItemDO item = new MesMdmItemDO(); |
| | | item.setId(1L); |
| | | when(itemService.getItem(1L)).thenReturn(item); |
| | | when(mapper.selectByItemIdAndCode(1L, "purity")).thenReturn(null); |
| | | MesQcSeedIndicatorRangeSaveReqVO request = new MesQcSeedIndicatorRangeSaveReqVO(); |
| | | request.setRanges(List.of(item("purity"), item("cleanliness"), item("germination_rate"), item("moisture"))); |
| | | service.saveRanges(request); |
| | | verify(mapper, times(4)).insert(any(MesQcSeedIndicatorRangeDO.class)); |
| | | } |
| | | |
| | | private MesQcSeedIndicatorRangeSaveReqVO.Item item(String code) { |
| | | MesQcSeedIndicatorRangeSaveReqVO.Item row = new MesQcSeedIndicatorRangeSaveReqVO.Item(); |
| | | row.setIndicatorCode(code); |
| | | row.setMinValue(new BigDecimal("90")); |
| | | row.setMaxValue(new BigDecimal("100")); |
| | | return row; |
| | | request.setItemId(1L); |
| | | request.setIndicatorCode("purity"); |
| | | request.setIndicatorName("纯度"); |
| | | request.setMinValue(new BigDecimal("90")); |
| | | request.setMaxValue(new BigDecimal("100")); |
| | | service.create(request); |
| | | verify(mapper, times(1)).insert(any(MesQcSeedIndicatorRangeDO.class)); |
| | | } |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.bagcode.MesProBagCodeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.batch.MesProBatchDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.pro.pallet.MesProPalletDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedIndicatorRangeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualityDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualityIndicatorDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.batch.MesWmBatchMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.wm.materialstock.MesWmMaterialStockMapper; |
| | | import cn.iocoder.yudao.module.mes.enums.wm.MesWmWarehouseTypeEnum; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import cn.iocoder.yudao.module.mes.service.pro.batch.MesProBatchService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.approver.MesQcApproverService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.warehouse.MesWmWarehouseService; |
| | |
| | | private final MesWmMaterialStockMapper stocks=mock(MesWmMaterialStockMapper.class); |
| | | private final MesWmWarehouseService warehouses=mock(MesWmWarehouseService.class); |
| | | private final MesQcApproverService approvers=mock(MesQcApproverService.class); |
| | | private final MesQcSeedIndicatorRangeService ranges=mock(MesQcSeedIndicatorRangeService.class); |
| | | private final MesMdmItemService items=mock(MesMdmItemService.class); |
| | | private final MesProBagCodeDO first=bag(1L),second=bag(2L),canceled=bag(3L).setStatus(30); |
| | | private MesQcSeedQualityGroupSubmitReqVO req; |
| | | private MesProBagCodeDO bag(Long id) {return new MesProBagCodeDO().setId(id).setCode("B"+id).setBatchId(10L).setPalletId(20L).setStatus(20).setInboundEventId(9L);} |
| | | private List<MesQcSeedQualitySubmitReqVO.Indicator> validIndicators() { |
| | | return List.of(indicator("purity"), indicator("cleanliness"), indicator("germination_rate"), indicator("moisture")); |
| | | } |
| | | private MesQcSeedIndicatorRangeDO range(String code) { |
| | | var range = new MesQcSeedIndicatorRangeDO(); |
| | | range.setIndicatorCode(code); range.setIndicatorName(code); range.setUnit("%"); |
| | | range.setMinValue(java.math.BigDecimal.ZERO); range.setMaxValue(java.math.BigDecimal.TEN); |
| | | return range; |
| | | } |
| | | private MesQcSeedQualitySubmitReqVO.Indicator indicator(String code) { |
| | | var item = new MesQcSeedQualitySubmitReqVO.Indicator(); item.setCode(code); |
| | |
| | | ReflectionTestUtils.setField(service,"proBatchService",batchService);ReflectionTestUtils.setField(service,"mesWmBatchMapper",wmBatches); |
| | | ReflectionTestUtils.setField(service,"materialStockMapper",stocks);ReflectionTestUtils.setField(service,"warehouseService",warehouses); |
| | | ReflectionTestUtils.setField(service,"approverService",approvers); |
| | | ReflectionTestUtils.setField(service,"rangeService",ranges); |
| | | ReflectionTestUtils.setField(service,"mdmItemService",items); |
| | | when(ranges.getEffectiveRanges(any())).thenReturn(List.of(range("purity"),range("cleanliness"),range("germination_rate"),range("moisture"))); |
| | | when(ranges.getEffectiveMap(any())).thenReturn(java.util.Map.of()); |
| | | when(items.getItem(any())).thenReturn(null); |
| | | var batch=new MesProBatchDO().setId(10L).setCode("BATCH").setBusinessCode("BATCH-BUS").setItemId(99L).setStatus(30); |
| | | when(batches.selectByScanCode(anyString())).thenReturn(batch);when(batches.selectByScanCodeForUpdate(anyString())).thenReturn(batch); |
| | | when(batchService.getBatch(10L)).thenReturn(batch); |
| | |
| | | java.util.Collection<Long> ids = invocation.getArgument(0); |
| | | return List.of(first, second).stream().filter(bag -> ids.contains(bag.getId())).toList(); |
| | | }); |
| | | req=new MesQcSeedQualityGroupSubmitReqVO();req.setCode("BATCH-BUS");req.setScanType("BATCH");req.setBagIds(List.of(1L,2L)); |
| | | req=new MesQcSeedQualityGroupSubmitReqVO();req.setCode("BATCH-BUS");req.setQcLine(1);req.setScanType("BATCH");req.setBagIds(List.of(1L,2L)); |
| | | req.setQualifiedWarehouseId(8L);req.setIndicators(validIndicators()); |
| | | } |
| | | @Test void batchPreviewCountsEffectiveInspectedAndPendingBags() { |
| | | when(qualities.selectListByCodes(any())).thenReturn(List.of(MesQcSeedQualityDO.builder().code("B1").build())); |
| | | when(qualities.selectListByCodes(any())).thenReturn(List.of(MesQcSeedQualityDO.builder().code("B1").qcLine(1).build())); |
| | | var result=service.resolveScan("BATCH-BUS","BATCH");assertEquals(2,result.get("totalCount")); |
| | | assertEquals(List.of(1L),result.get("completedBagIds"));assertEquals(List.of(2L),result.get("pendingBagIds")); |
| | | verify(qualities,never()).insertBatch(anyCollection(),anyInt()); |
| | | } |
| | | @Test void palletProcessesOnlyConfirmedUninspectedBags() { |
| | | req.setScanType("PALLET");req.setCode("PALLET-BUS");req.setBagIds(List.of(2L)); |
| | | when(qualities.selectListByCodes(any())).thenReturn(List.of(MesQcSeedQualityDO.builder().code("B1").build())); |
| | | when(qualities.selectListByCodes(any())).thenReturn(List.of(MesQcSeedQualityDO.builder().code("B1").qcLine(1).build())); |
| | | when(qualities.selectListByCodesForUpdate(List.of("B2"))).thenReturn(List.of()); |
| | | assertEquals(1,service.submitGroup(req).get("processedCount")); |
| | | verify(qualities).insertBatch(argThat(rows -> rows.size() == 1 && rows.iterator().next().getCode().equals("B2") |
| | | && rows.iterator().next().getQualifiedWarehouseId().equals(8L)),eq(100)); |
| | | verify(indicators).insertBatch(argThat(rows -> rows.size() == 4),eq(400)); |
| | | verify(actions).insertBatch(argThat(rows -> rows.size() == 1),eq(100)); |
| | | // 䏿¬¡æäº¤åæ¶çæå å·¥é¨ãéå®é¨ä¸¤æ¡è®°å½ï¼ææ ä¸ç»è®ºä¸è´ã |
| | | verify(qualities).insertBatch(argThat(rows -> rows.size() == 2 && rows.stream().allMatch(row -> row.getCode().equals("B2") |
| | | && row.getQualifiedWarehouseId().equals(8L)) |
| | | && rows.stream().map(MesQcSeedQualityDO::getQcLine).collect(java.util.stream.Collectors.toSet()) |
| | | .equals(java.util.Set.of(1, 2))),eq(100)); |
| | | verify(indicators).insertBatch(argThat(rows -> rows.size() == 8),eq(400)); |
| | | verify(actions).insertBatch(argThat(rows -> rows.size() == 2),eq(100)); |
| | | } |
| | | @Test void changedConfirmedListDoesNotSubmitAnyBag() { |
| | | req.setBagIds(List.of(1L));assertThrows(IllegalArgumentException.class,()->service.submitGroup(req));verify(qualities,never()).insertBatch(anyCollection(),anyInt()); |
| | |
| | | } |
| | | @Test void retestScopePreviewSeparatesWorkflowStages() { |
| | | when(qualities.selectListByCodes(any())).thenReturn(List.of( |
| | | MesQcSeedQualityDO.builder().id(11L).code("B1").workflowStatus(10).build(), |
| | | MesQcSeedQualityDO.builder().id(12L).code("B2").workflowStatus(50).build())); |
| | | MesQcSeedQualityDO.builder().id(11L).code("B1").qcLine(1).workflowStatus(10).build(), |
| | | MesQcSeedQualityDO.builder().id(12L).code("B2").qcLine(1).workflowStatus(50).build())); |
| | | var result = service.resolveRetestScope("PALLET-BUS", "PALLET"); |
| | | assertEquals(List.of(11L), result.get("retestIds")); |
| | | assertEquals(List.of(12L), result.get("initialRecheckIds")); |
| | |
| | | var third = bag(3L); var fourth = bag(4L); |
| | | when(bags.selectListByPalletId(20L)).thenReturn(List.of(first, second, third, fourth)); |
| | | when(qualities.selectListByCodes(any())).thenReturn(List.of( |
| | | MesQcSeedQualityDO.builder().id(11L).code("B1").workflowStatus(20).build(), |
| | | MesQcSeedQualityDO.builder().id(12L).code("B2").workflowStatus(25).build(), |
| | | MesQcSeedQualityDO.builder().id(13L).code("B3").workflowStatus(30).build(), |
| | | MesQcSeedQualityDO.builder().id(14L).code("B4").workflowStatus(60).build())); |
| | | MesQcSeedQualityDO.builder().id(11L).code("B1").qcLine(1).workflowStatus(20).build(), |
| | | MesQcSeedQualityDO.builder().id(12L).code("B2").qcLine(1).workflowStatus(25).build(), |
| | | MesQcSeedQualityDO.builder().id(13L).code("B3").qcLine(1).workflowStatus(30).build(), |
| | | MesQcSeedQualityDO.builder().id(14L).code("B4").qcLine(1).workflowStatus(60).build())); |
| | | var result = service.resolveRetestScope("PALLET-BUS", "PALLET"); |
| | | assertEquals(List.of(11L), result.get("pendingProcessIds")); |
| | | assertEquals(List.of(12L), result.get("pendingInitialFailIds")); |
| | |
| | | } |
| | | @Test void retestScopeRejectsStaleConfirmedList() { |
| | | when(qualities.selectListByCodes(any())).thenReturn(List.of( |
| | | MesQcSeedQualityDO.builder().id(11L).code("B1").workflowStatus(10).build(), |
| | | MesQcSeedQualityDO.builder().id(12L).code("B2").workflowStatus(10).build())); |
| | | MesQcSeedQualityDO.builder().id(11L).code("B1").qcLine(1).workflowStatus(10).build(), |
| | | MesQcSeedQualityDO.builder().id(12L).code("B2").qcLine(1).workflowStatus(10).build())); |
| | | var request = new MesQcSeedQualityScopeRetestReqVO(); |
| | | request.setCode("BATCH-BUS"); request.setScanType("BATCH"); request.setWorkflowStatus(10); |
| | | request.setCode("BATCH-BUS"); request.setScanType("BATCH"); request.setQcLine(1); request.setWorkflowStatus(10); |
| | | request.setQualityIds(List.of(11L)); request.setIndicators(validIndicators()); |
| | | assertThrows(IllegalArgumentException.class, () -> service.retestScope(request)); |
| | | verify(indicators, never()).updateBatch(anyCollection(), anyInt()); |
| | | } |
| | | @Test void retestScopeProcessesAllConfirmedBags() { |
| | | var pending = List.of(MesQcSeedQualityDO.builder().id(11L).code("B1").workflowStatus(10).build(), |
| | | MesQcSeedQualityDO.builder().id(12L).code("B2").workflowStatus(10).build()); |
| | | var pending = List.of(MesQcSeedQualityDO.builder().id(11L).code("B1").qcLine(1).workflowStatus(10).build(), |
| | | MesQcSeedQualityDO.builder().id(12L).code("B2").qcLine(1).workflowStatus(10).build()); |
| | | when(qualities.selectListByCodes(any())).thenReturn(pending); |
| | | when(qualities.selectListByIdsForUpdate(List.of(11L, 12L))).thenReturn(pending); |
| | | when(indicators.selectListByQualityIds(any())).thenReturn(storedIndicators(11L, 12L)); |
| | | var request = new MesQcSeedQualityScopeRetestReqVO(); |
| | | request.setCode("BATCH-BUS"); request.setScanType("BATCH"); request.setWorkflowStatus(10); |
| | | request.setCode("BATCH-BUS"); request.setScanType("BATCH"); request.setQcLine(1); request.setWorkflowStatus(10); |
| | | request.setQualityIds(List.of(11L, 12L)); request.setIndicators(validIndicators()); |
| | | assertEquals(2, service.retestScope(request).get("processedCount")); |
| | | verify(indicators).selectListByQualityIds(List.of(11L, 12L)); |
| | |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.qc.seedquality.MesQcSeedQualityDO; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.pro.bagcode.MesProBagCodeMapper; |
| | | import cn.iocoder.yudao.module.mes.dal.mysql.qc.seedquality.MesQcSeedQualityMapper; |
| | | import cn.iocoder.yudao.module.mes.service.mdm.MesMdmItemService; |
| | | import cn.iocoder.yudao.module.mes.service.qc.seedquality.MesQcSeedIndicatorRangeService; |
| | | import org.junit.jupiter.api.BeforeEach; |
| | | import org.junit.jupiter.api.Test; |
| | | import org.springframework.test.util.ReflectionTestUtils; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import static org.junit.jupiter.api.Assertions.*; |
| | | import static org.mockito.Mockito.*; |
| | | |
| | |
| | | private final MesQcSeedQualityServiceImpl service = new MesQcSeedQualityServiceImpl(); |
| | | private final MesProBagCodeMapper bags = mock(MesProBagCodeMapper.class); |
| | | private final MesQcSeedQualityMapper qualities = mock(MesQcSeedQualityMapper.class); |
| | | @BeforeEach void setup() { ReflectionTestUtils.setField(service,"bagCodeMapper",bags); ReflectionTestUtils.setField(service,"qualityMapper",qualities); } |
| | | private final MesMdmItemService items = mock(MesMdmItemService.class); |
| | | private final MesQcSeedIndicatorRangeService ranges = mock(MesQcSeedIndicatorRangeService.class); |
| | | @BeforeEach void setup() { |
| | | ReflectionTestUtils.setField(service,"bagCodeMapper",bags); |
| | | ReflectionTestUtils.setField(service,"qualityMapper",qualities); |
| | | ReflectionTestUtils.setField(service,"mdmItemService",items); |
| | | ReflectionTestUtils.setField(service,"rangeService",ranges); |
| | | when(ranges.getEffectiveRanges(any())).thenReturn(List.of()); |
| | | } |
| | | private MesProBagCodeDO bag() { return new MesProBagCodeDO().setId(1L).setCode("TRACE").setBusinessCode("BUSINESS").setStatus(20).setInboundEventId(9L).setBatchCode("BATCH"); } |
| | | @Test void businessScanReturnsCanonicalCodeAndPreviousResultWithoutWrites() { |
| | | when(bags.selectByScanCode("BUSINESS")).thenReturn(bag()); |
| | | when(qualities.selectByCode("TRACE")).thenReturn(MesQcSeedQualityDO.builder().id(2L).result(1).build()); |
| | | when(qualities.selectListByCode("TRACE")).thenReturn(List.of(MesQcSeedQualityDO.builder().id(2L).result(1).build())); |
| | | var result = service.resolveScan("BUSINESS"); |
| | | assertEquals("TRACE",result.get("code"));assertEquals(2L,result.get("qualityId"));assertEquals(true,result.get("inbound")); |
| | | verify(qualities,never()).insert(any(MesQcSeedQualityDO.class)); |
| | |
| | | } |
| | | @Test void duplicateQualityIsRejectedAfterLockingSameBagForBothCodes() { |
| | | when(bags.selectByScanCodeForUpdate("BUSINESS")).thenReturn(bag()); |
| | | var req = new MesQcSeedQualitySubmitReqVO();req.setCode("BUSINESS"); |
| | | when(qualities.selectByCodeForUpdate("TRACE")).thenReturn(MesQcSeedQualityDO.builder().id(2L).result(1).build()); |
| | | var req = new MesQcSeedQualitySubmitReqVO();req.setCode("BUSINESS");req.setQcLine(1); |
| | | when(qualities.selectListByCodeForUpdate("TRACE")).thenReturn(List.of(MesQcSeedQualityDO.builder().id(2L).result(1).build())); |
| | | req.setIndicators(Collections.nCopies(4,new MesQcSeedQualitySubmitReqVO.Indicator())); |
| | | assertThrows(IllegalArgumentException.class,()->service.submit(req)); |
| | | verify(qualities).selectByCodeForUpdate("TRACE");verify(qualities,never()).insert(any(MesQcSeedQualityDO.class)); |
| | | verify(qualities).selectListByCodeForUpdate("TRACE");verify(qualities,never()).insert(any(MesQcSeedQualityDO.class)); |
| | | } |
| | | @Test void unenteredAndCanceledBagsCannotSubmit() { |
| | | var req = new MesQcSeedQualitySubmitReqVO();req.setCode("BUSINESS");req.setIndicators(Collections.nCopies(4,new MesQcSeedQualitySubmitReqVO.Indicator())); |
| | | var req = new MesQcSeedQualitySubmitReqVO();req.setCode("BUSINESS");req.setQcLine(1);req.setIndicators(Collections.nCopies(4,new MesQcSeedQualitySubmitReqVO.Indicator())); |
| | | when(bags.selectByScanCodeForUpdate("BUSINESS")).thenReturn(bag().setInboundEventId(null)); |
| | | assertThrows(IllegalArgumentException.class,()->service.submit(req)); |
| | | when(bags.selectByScanCodeForUpdate("BUSINESS")).thenReturn(bag().setStatus(30)); |
| | |
| | | import org.junit.jupiter.api.Test; |
| | | import org.mockito.ArgumentCaptor; |
| | | import org.springframework.test.util.ReflectionTestUtils; |
| | | import org.springframework.transaction.support.TransactionSynchronizationManager; |
| | | import java.util.List; |
| | | import java.util.concurrent.atomic.AtomicBoolean; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.*; |
| | | import static org.mockito.Mockito.*; |
| | |
| | | } |
| | | |
| | | @Test |
| | | void initialApprovalOnlyQueuesRetestAndFailedRetestCanReturnToRetest() { |
| | | void failedProcessStartDoesNotPreventOtherStarts() { |
| | | AtomicBoolean secondStarted = new AtomicBoolean(); |
| | | TransactionSynchronizationManager.initSynchronization(); |
| | | try { |
| | | ReflectionTestUtils.invokeMethod(service, "afterCommit", (Runnable) () -> { |
| | | throw new IllegalStateException("BPM unavailable"); |
| | | }); |
| | | ReflectionTestUtils.invokeMethod(service, "afterCommit", (Runnable) () -> secondStarted.set(true)); |
| | | TransactionSynchronizationManager.getSynchronizations().forEach(sync -> sync.afterCommit()); |
| | | assertTrue(secondStarted.get()); |
| | | } finally { |
| | | TransactionSynchronizationManager.clearSynchronization(); |
| | | } |
| | | } |
| | | |
| | | @Test |
| | | void newDualLineRecordsRequireBpmApproval() { |
| | | when(qualityMapper.selectByIdForUpdate(1L)).thenReturn( |
| | | MesQcSeedQualityDO.builder().id(1L).code("B1").result(2).workflowStatus(20).build(), |
| | | MesQcSeedQualityDO.builder().id(1L).code("B1").result(2).workflowStatus(60).build()); |
| | | service.processApprove(1L, true, ""); |
| | | MesQcSeedQualityDO.builder().id(1L).code("B1").qcLine(1).result(2).workflowStatus(20).build()); |
| | | assertThrows(IllegalArgumentException.class, () -> service.processApprove(1L, true, "")); |
| | | verifyNoInteractions(transactionService); |
| | | service.companyApprove(1L, false, "夿£ä¸åæ ¼ï¼éæ°åæ ·"); |
| | | ArgumentCaptor<MesQcSeedQualityDO> update = ArgumentCaptor.forClass(MesQcSeedQualityDO.class); |
| | | verify(qualityMapper, times(2)).updateById(update.capture()); |
| | | assertEquals(10, update.getAllValues().get(0).getWorkflowStatus()); |
| | | assertEquals(10, update.getAllValues().get(1).getWorkflowStatus()); |
| | | verifyNoInteractions(transactionService); |
| | | verify(qualityMapper, never()).updateById(any(MesQcSeedQualityDO.class)); |
| | | } |
| | | |
| | | @Test |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.system.controller.admin.appearance; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.module.system.controller.admin.appearance.vo.SystemAppearanceRespVO; |
| | | import cn.iocoder.yudao.module.system.controller.admin.appearance.vo.SystemAppearanceSaveReqVO; |
| | | import cn.iocoder.yudao.module.system.service.appearance.SystemAppearanceService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.annotation.security.PermitAll; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestBody; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | /** |
| | | * 管çåå° - ç³»ç»å¤è§ï¼ç»å½èæ¯å¾ãä¼ä¸ Logoï¼ |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Tag(name = "管çåå° - ç³»ç»å¤è§") |
| | | @RestController |
| | | @RequestMapping("/system/appearance") |
| | | @Validated |
| | | public class SystemAppearanceController { |
| | | |
| | | @Resource |
| | | private SystemAppearanceService appearanceService; |
| | | |
| | | @GetMapping("/get") |
| | | @Operation(summary = "è·å¾ç³»ç»å¤è§") |
| | | @PermitAll |
| | | public CommonResult<SystemAppearanceRespVO> getAppearance() { |
| | | return success(appearanceService.getAppearance()); |
| | | } |
| | | |
| | | @PostMapping("/save") |
| | | @Operation(summary = "ä¿åç³»ç»å¤è§") |
| | | @PreAuthorize("@ss.hasPermission('system:appearance:update')") |
| | | public CommonResult<Boolean> saveAppearance(@Valid @RequestBody SystemAppearanceSaveReqVO reqVO) { |
| | | appearanceService.saveAppearance(reqVO); |
| | | return success(true); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.system.controller.admin.appearance.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "管çåå° - ç³»ç»å¤è§ Response VO") |
| | | @Data |
| | | public class SystemAppearanceRespVO { |
| | | |
| | | @Schema(description = "ä¼ä¸ Logo æä»¶ç¼å·") |
| | | private Long logoBlobId; |
| | | |
| | | @Schema(description = "ä¼ä¸ Logo 访é®å°å") |
| | | private String logoUrl; |
| | | |
| | | @Schema(description = "ç»å½èæ¯å¾æä»¶ç¼å·") |
| | | private Long loginBackgroundBlobId; |
| | | |
| | | @Schema(description = "ç»å½èæ¯å¾è®¿é®å°å") |
| | | private String loginBackgroundUrl; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.system.controller.admin.appearance.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Schema(description = "管çåå° - ç³»ç»å¤è§ä¿å Request VO") |
| | | @Data |
| | | public class SystemAppearanceSaveReqVO { |
| | | |
| | | @Schema(description = "ä¼ä¸ Logo æä»¶ç¼å·ï¼ä¼ 空表示æ¸
é¤") |
| | | private Long logoBlobId; |
| | | |
| | | @Schema(description = "ç»å½èæ¯å¾æä»¶ç¼å·ï¼ä¼ 空表示æ¸
é¤") |
| | | private Long loginBackgroundBlobId; |
| | | |
| | | } |
| | |
| | | |
| | | FILE("file", "éç¨æä»¶"), |
| | | IMAGE("image", "å¾ç"), |
| | | AVATAR("avatar", "头å"); |
| | | AVATAR("avatar", "头å"), |
| | | LOGO("logo", "ä¼ä¸Logo"), |
| | | LOGIN_BACKGROUND("login_background", "ç»å½èæ¯å¾"); |
| | | |
| | | private final String type; |
| | | private final String description; |
| | |
| | | // ===== 头å ===== |
| | | AVATAR("avatar", "头å"), |
| | | |
| | | // ===== ç³»ç»å¤è§ ===== |
| | | SYSTEM_APPEARANCE("system_appearance", "ç³»ç»å¤è§"), |
| | | |
| | | // ===== ERP éè´ ===== |
| | | ERP_PURCHASE_REQUEST("erp_purchase_request", "ERP éè´ç³è¯·"), |
| | | ERP_PURCHASE_ORDER("erp_purchase_order", "ERP éè´è®¢å"), |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.system.service.appearance; |
| | | |
| | | import cn.iocoder.yudao.module.system.controller.admin.appearance.vo.SystemAppearanceRespVO; |
| | | import cn.iocoder.yudao.module.system.controller.admin.appearance.vo.SystemAppearanceSaveReqVO; |
| | | |
| | | /** |
| | | * ç³»ç»å¤è§ Service æ¥å£ |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | public interface SystemAppearanceService { |
| | | |
| | | /** |
| | | * è·å¾å½åç³»ç»å¤è§ï¼ç»å½é¡µèæ¯å¾ãä¼ä¸ Logoï¼ |
| | | * |
| | | * @return ç³»ç»å¤è§ |
| | | */ |
| | | SystemAppearanceRespVO getAppearance(); |
| | | |
| | | /** |
| | | * ä¿åç³»ç»å¤è§ |
| | | * |
| | | * @param reqVO ä¿åä¿¡æ¯ |
| | | */ |
| | | void saveAppearance(SystemAppearanceSaveReqVO reqVO); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.system.service.appearance; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.module.system.controller.admin.appearance.vo.SystemAppearanceRespVO; |
| | | import cn.iocoder.yudao.module.system.controller.admin.appearance.vo.SystemAppearanceSaveReqVO; |
| | | import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageAttachmentListReqVO; |
| | | import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageAttachmentSaveReqVO; |
| | | import cn.iocoder.yudao.module.system.controller.admin.storage.vo.SystemStorageBlobRespVO; |
| | | import cn.iocoder.yudao.module.system.enums.storage.StorageApplicationTypeEnum; |
| | | import cn.iocoder.yudao.module.system.enums.storage.StorageRecordTypeEnum; |
| | | import cn.iocoder.yudao.module.system.service.storage.SystemStorageAttachmentService; |
| | | import cn.iocoder.yudao.module.system.util.storage.StorageFileUtil; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * ç³»ç»å¤è§ Service å®ç°ç±» |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Service |
| | | public class SystemAppearanceServiceImpl implements SystemAppearanceService { |
| | | |
| | | /** |
| | | * ç³»ç»å¤è§åºå®è®°å½ç¼å·ï¼å¤è§æ¯å
¨å±å¯ä¸é
ç½®ï¼ç¨ä¸æ¡éä»¶è®°å½æ¿è½½ |
| | | */ |
| | | private static final Long RECORD_ID = 1L; |
| | | |
| | | @Resource |
| | | private SystemStorageAttachmentService storageAttachmentService; |
| | | @Resource |
| | | private StorageFileUtil storageFileUtil; |
| | | |
| | | @Override |
| | | public SystemAppearanceRespVO getAppearance() { |
| | | SystemAppearanceRespVO respVO = new SystemAppearanceRespVO(); |
| | | SystemStorageBlobRespVO logo = getSingleBlob(StorageApplicationTypeEnum.LOGO); |
| | | if (logo != null) { |
| | | respVO.setLogoBlobId(logo.getId()); |
| | | respVO.setLogoUrl(buildPermanentUrl(logo)); |
| | | } |
| | | SystemStorageBlobRespVO background = getSingleBlob(StorageApplicationTypeEnum.LOGIN_BACKGROUND); |
| | | if (background != null) { |
| | | respVO.setLoginBackgroundBlobId(background.getId()); |
| | | respVO.setLoginBackgroundUrl(buildPermanentUrl(background)); |
| | | } |
| | | return respVO; |
| | | } |
| | | |
| | | @Override |
| | | public void saveAppearance(SystemAppearanceSaveReqVO reqVO) { |
| | | saveBlob(StorageApplicationTypeEnum.LOGO, reqVO.getLogoBlobId()); |
| | | saveBlob(StorageApplicationTypeEnum.LOGIN_BACKGROUND, reqVO.getLoginBackgroundBlobId()); |
| | | } |
| | | |
| | | private SystemStorageBlobRespVO getSingleBlob(StorageApplicationTypeEnum application) { |
| | | SystemStorageAttachmentListReqVO reqVO = new SystemStorageAttachmentListReqVO(); |
| | | reqVO.setApplication(application.getType()); |
| | | reqVO.setRecordType(StorageRecordTypeEnum.SYSTEM_APPEARANCE.getType()); |
| | | reqVO.setRecordId(RECORD_ID); |
| | | List<SystemStorageBlobRespVO> blobs = storageAttachmentService.listAttachments(reqVO); |
| | | return CollUtil.isEmpty(blobs) ? null : blobs.get(0); |
| | | } |
| | | |
| | | /** |
| | | * å¤è§å¾éè¦ç»å½åå°±è½è®¿é®ï¼å æ¤ä½¿ç¨æ°¸ä¹
å
¬å¼å°å |
| | | */ |
| | | private String buildPermanentUrl(SystemStorageBlobRespVO blob) { |
| | | return storageFileUtil.buildSignedUrl(blob, "/preview/", BigDecimal.valueOf(-1)); |
| | | } |
| | | |
| | | private void saveBlob(StorageApplicationTypeEnum application, Long blobId) { |
| | | SystemStorageAttachmentSaveReqVO reqVO = new SystemStorageAttachmentSaveReqVO(); |
| | | reqVO.setApplication(application.getType()); |
| | | reqVO.setRecordType(StorageRecordTypeEnum.SYSTEM_APPEARANCE.getType()); |
| | | reqVO.setRecordId(RECORD_ID); |
| | | reqVO.setBlobItems(blobId == null |
| | | ? Collections.emptyList() |
| | | : Collections.singletonList(buildBlobItem(blobId, application))); |
| | | storageAttachmentService.bindAttachments(reqVO); |
| | | } |
| | | |
| | | private SystemStorageAttachmentSaveReqVO.BlobItem buildBlobItem(Long blobId, StorageApplicationTypeEnum application) { |
| | | SystemStorageAttachmentSaveReqVO.BlobItem item = new SystemStorageAttachmentSaveReqVO.BlobItem(); |
| | | item.setBlobId(blobId); |
| | | item.setApplication(application.getType()); |
| | | return item; |
| | | } |
| | | |
| | | } |