Merge branch 'dev_New_pro' into dev_宁夏_英泽防锈
已添加20个文件
已重命名9个文件
已修改98个文件
已删除6个文件
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # éè´å¤æä»¶åæç¡®è®¤æ¥å£ä¼ åç±»å约æï¼`purchase_ledger`ï¼ |
| | | |
| | | ## 1. éç¨æ¥å£ |
| | | |
| | | - `POST /purchase-ai/analyze-files/confirm` |
| | | - `businessType = purchase_ledger` |
| | | |
| | | > æ¬æç¨äºçº¦æå端æäº¤å°ç¡®è®¤æ¥å£ç `payload` ç±»åï¼é¿å
`Cannot deserialize ...` è¿ç±»ååºååå¼å¸¸ã |
| | | |
| | | ## 2. é¡¶å±è¯·æ±ä½ |
| | | |
| | | ```json |
| | | { |
| | | "businessType": "purchase_ledger", |
| | | "payload": { |
| | | "purchaseLedgers": [] |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ## 3. payload ç»æ |
| | | |
| | | æ¨èç»ä¸ä½¿ç¨æ¹éç»æï¼å³ä½¿åªæ 1 æ¡ï¼ï¼ |
| | | |
| | | ```json |
| | | { |
| | | "purchaseLedgers": [ |
| | | { |
| | | "purchaseContractNumber": "CG-2026-001", |
| | | "supplierId": 10001, |
| | | "entryDate": "2026-05-07", |
| | | "type": 2, |
| | | "approvalStatus": 1, |
| | | "productData": [ |
| | | { |
| | | "productCategory": "颿", |
| | | "specificationModel": "Q235-A", |
| | | "unit": "å¨", |
| | | "quantity": 10, |
| | | "taxInclusiveUnitPrice": 1200, |
| | | "taxInclusiveTotalPrice": 12000, |
| | | "taxRate": 13, |
| | | "type": 2 |
| | | } |
| | | ] |
| | | } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | å端ä¹å
¼å®¹âåæ¡ç´ä¼ âï¼`payload` ç´æ¥æ¯ä¸æ¡ `PurchaseLedgerDto`ï¼ï¼ä½ä¸å»ºè®®æ°å端继ç»ä½¿ç¨ã |
| | | |
| | | ## 4. Java ç±»åå° JSON ç±»åæ å°è§å |
| | | |
| | | - `Long` / `Integer`ï¼ä¼ **number**ï¼æ´æ°ï¼ï¼ä¸è¦ä¼ `"pending"`ã`"1级"` è¿ç±»å符串ã |
| | | - `BigDecimal`ï¼ä¼ **number**ï¼å¯å°æ°ï¼ï¼ä¸è¦ä¼ 带éå·ãåä½çå符串ï¼å¦ `"12,000å
"`ï¼ã |
| | | - `Date`ï¼ä¼ **string**ï¼æ ¼å¼åºå® `yyyy-MM-dd`ã |
| | | - `Boolean`ï¼ä¼ `true/false`ï¼ä¸è¦ä¼ `"true"`ã`"æ¯"`ã |
| | | - `List<T>`ï¼ä¼ æ°ç» `[]`ã |
| | | |
| | | ## 5. `PurchaseLedgerDto` åæ®µç±»å约æï¼ç¡®è®¤æ¥å£å¯è¯å«åæ®µï¼ |
| | | |
| | | | åæ®µ | Java ç±»å | JSON ç±»å | 说æ | |
| | | | --- | --- | --- | --- | |
| | | | entryDateStart | String | string | æ¥è¯¢åºé´å¼å§æ¥æï¼`yyyy-MM-dd`ï¼ | |
| | | | entryDateEnd | String | string | æ¥è¯¢åºé´ç»ææ¥æï¼`yyyy-MM-dd`ï¼ | |
| | | | id | Long | number(integer) | å°è´¦ID | |
| | | | purchaseContractNumber | String | string | **å¿
å¡«**ï¼ä¸»ä¸å¡åå·ï¼ | |
| | | | supplierId | Long | number(integer) | `supplierId` ä¸ `supplierName` äºéä¸å¿
å¡« | |
| | | | supplierName | String | string | `supplierId` ä¸ `supplierName` äºéä¸å¿
å¡« | |
| | | | recorderId | Long | number(integer) | å½å
¥äººID | |
| | | | recorderName | String | string | å½å
¥äººåç§° | |
| | | | salesContractNo | String | string | éå®ååå· | |
| | | | salesContractNoId | Long | number(integer) | éå®ååID | |
| | | | projectName | String | string | 项ç®åç§° | |
| | | | entryDate | Date | string(`yyyy-MM-dd`) | å½å
¥æ¥æï¼ç¼ºçæ¶å端ä¼è¡¥å½å¤© | |
| | | | executionDate | Date | string(`yyyy-MM-dd`) | ç¾è®¢æ¥æ | |
| | | | remarks | String | string | 夿³¨ | |
| | | | attachmentMaterials | String | string | é件说æ/è·¯å¾ | |
| | | | createdAt | Date | string(`yyyy-MM-dd`) | åå»ºæ¥æ | |
| | | | updatedAt | Date | string(`yyyy-MM-dd`) | æ´æ°æ¥æ | |
| | | | salesLedgerId | Long | number(integer) | éå®å°è´¦ID | |
| | | | hasChildren | Boolean | boolean | æ¯å¦æå项 | |
| | | | Type | Integer | number(integer) | åå²å段ï¼ä¸æ¨èæ°å端使ç¨ï¼ | |
| | | | productData | List<SalesLedgerProduct> | array | 产åæç»ï¼è§ä¸è | |
| | | | tempFileIds | List<String> | array[string] | ä¸´æ¶æä»¶IDå表 | |
| | | | SalesLedgerFiles | List<CommonFile> | array[object] | åå²å
¼å®¹å段 | |
| | | | phoneNumber | String | string | ä¸å¡åææºå· | |
| | | | businessPersonId | Long | number(integer) | ä¸å¡åID | |
| | | | productId | Long | number(integer) | 产åID | |
| | | | productModelId | Long | number(integer) | 产åè§æ ¼ID | |
| | | | invoiceNumber | String | string | åç¥¨å· | |
| | | | invoiceAmount | BigDecimal | number | å票éé¢ | |
| | | | ticketRegistrationId | Long | number(integer) | æ¥ç¥¨ç»è®°ID | |
| | | | contractAmount | BigDecimal | number | ååéé¢ | |
| | | | receiptPaymentAmount | BigDecimal | number | æ¥ç¥¨éé¢ | |
| | | | unReceiptPaymentAmount | BigDecimal | number | æªæ¥ç¥¨éé¢ | |
| | | | type | Integer | number(integer) | å°è´¦ç±»åï¼éè´åºå® `2`ï¼ç¼ºçæ¶å端补 `2`ï¼ | |
| | | | paymentMethod | String | string | 仿¬¾æ¹å¼ | |
| | | | approvalStatus | Integer | number(integer) | 审æ¹ç¶æï¼**严ç¦ä¼ å符串**ï¼å¦ `"pending"`ï¼ | |
| | | | templateName | String | string | 模æ¿åç§° | |
| | | |
| | | ### 审æ¹ç¶æå»ºè®®å¼ï¼`approvalStatus`ï¼ |
| | | |
| | | å»ºè®®ææ°åä¼ å¼ï¼ |
| | | |
| | | - `1`ï¼å¾
å®¡æ ¸ |
| | | - `2`ï¼å®¡æ ¸ä¸ |
| | | - `3`ï¼å®¡æ ¸éè¿ |
| | | - `4`ï¼å®¡æ ¸æç»/失败 |
| | | - `5`ï¼æ¨¡æ¿æ°æ®ï¼åå²å®ä¹ï¼ |
| | | |
| | | ## 6. `productData`ï¼`SalesLedgerProduct`ï¼å»ºè®®å段åç±»å |
| | | |
| | | | åæ®µ | Java ç±»å | JSON ç±»å | 说æ | |
| | | | --- | --- | --- | --- | |
| | | | productCategory | String | string | **å¿
å¡«**ï¼äº§å大类/åç§° | |
| | | | specificationModel | String | string | **å¿
å¡«**ï¼è§æ ¼åå· | |
| | | | unit | String | string | **å¿
å¡«**ï¼åä½ | |
| | | | quantity | BigDecimal | number | **å¿
å¡«**ï¼æ°é | |
| | | | taxRate | BigDecimal | number | ç¨çï¼å¦ `13`ï¼ | |
| | | | taxInclusiveUnitPrice | BigDecimal | number | **å¿
å¡«**ï¼å«ç¨åä»· | |
| | | | taxInclusiveTotalPrice | BigDecimal | number | **å¿
å¡«**ï¼å«ç¨æ»ä»· | |
| | | | taxExclusiveTotalPrice | BigDecimal | number | ä¸å«ç¨æ»ä»·ï¼å¯ä¸ä¼ ï¼åç«¯å¯æ¨å¯¼ï¼ | |
| | | | invoiceType | String | string | å票类å | |
| | | | productId | Long | number(integer) | 产åID | |
| | | | productModelId | Long | number(integer) | 产ååå·ID | |
| | | | isChecked | Boolean | boolean | æ¯å¦è´¨æ£ | |
| | | | type | Integer | number(integer) | éè´äº§ååºå® `2`ï¼å»ºè®®ä¼ `2`ï¼ | |
| | | |
| | | ## 7. å¿
å¡«ä¸å端é»è®¤è¡ä¸º |
| | | |
| | | - å°è´¦ä¸»è¡¨å¿
å¡«ï¼`purchaseContractNumber`ï¼ä»¥å `supplierId` / `supplierName` äºéä¸ã |
| | | - 产åæç»è¥ä¼ äº `productData`ï¼åæ¯æ¡äº§åå¿
å¡«ï¼`productCategory`ã`specificationModel`ã`unit`ã`quantity`ã`taxInclusiveUnitPrice`ã`taxInclusiveTotalPrice`ã |
| | | - `entryDate` 为空æ¶ï¼å端补å½å¤©æ¥æã |
| | | - `type` 为空æ¶ï¼å端补 `2`ã |
| | | |
| | | ## 8. å端é«é¢éè¯¯ç¤ºä¾ |
| | | |
| | | é误ï¼ä¼è§¦åååºååå¼å¸¸ï¼ï¼ |
| | | |
| | | ```json |
| | | { |
| | | "approvalStatus": "pending", |
| | | "type": "éè´", |
| | | "supplierId": "ä¾åºåA" |
| | | } |
| | | ``` |
| | | |
| | | æ£ç¡®ï¼ |
| | | |
| | | ```json |
| | | { |
| | | "approvalStatus": 1, |
| | | "type": 2, |
| | | "supplierId": 10001 |
| | | } |
| | | ``` |
| | | |
| | | ## 9. æäº¤åèªæ£æ¸
å |
| | | |
| | | 1. ææ `Long/Integer/BigDecimal` åæ®µé½ä¸ºæ°åï¼ä¸æ¯ä¸å¡è¯å符串ã |
| | | 2. æææ¥æåæ®µé½æ¯ `yyyy-MM-dd`ã |
| | | 3. `approvalStatus` ä»
ä¼ æ°åç¶æç ã |
| | | 4. `supplierId` ä¸ `supplierName` è³å°æä¸ä¸ªææå¼ã |
| | | 5. `productData` ä¸å¿
å¡«åé½å
¨ä¸ä¸ºæ£ç¡®ç±»åã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # éè´å¤æä»¶åæéä»¶åå¨ä¸åå²åæ¾èè°è¯´æ |
| | | |
| | | > æ´æ°æ¶é´ï¼2026-05-08 |
| | | > éç¨èå´ï¼éè´æºè½ä½å¤æä»¶åæ + åå²ä¼è¯éä»¶åæ¾ |
| | | |
| | | ## 1. åæ´èæ¯ |
| | | |
| | | å端已补é½ä»¥ä¸è½åï¼ |
| | | |
| | | 1. `POST /purchase-ai/analyze-files` ä¸ä¼ æ¶å
åéä»¶å°æå¡å¨ï¼å
Œ
±è®¿é®ï¼ã |
| | | 2. ææä»¶ç±»åè¿åå¯è®¿é®è·¯å¾ï¼ |
| | | - å¾ç / PDFï¼ä¼å
é¢è§è·¯å¾ |
| | | - å
¶å®æä»¶ï¼ä¼å
ä¸è½½è·¯å¾ |
| | | 3. ç¨æ·æé®ä¸éä»¶è·¯å¾åå¼åå
¥ Mongoã |
| | | 4. å岿¶æ¯æ¥å£å¯æâæ¶æ¯ç»´åº¦âåä¼ éä»¶è·¯å¾ï¼å端å¯ç´æ¥åæ¾éä»¶å¡çã |
| | | |
| | | --- |
| | | |
| | | ## 2. æ¥å£è¡ä¸º |
| | | |
| | | ### 2.1 夿件忿¥å£ |
| | | |
| | | ```http |
| | | POST /purchase-ai/analyze-files |
| | | Content-Type: multipart/form-data |
| | | ``` |
| | | |
| | | 请æ±åæ°ï¼ |
| | | |
| | | - `files`: `MultipartFile[]`ï¼å¿
å¡«ï¼ |
| | | - `message`: `string`ï¼å¯éï¼ |
| | | - `memoryId`: `string`ï¼å¯éï¼ |
| | | |
| | | å端å¤çæµç¨ï¼ |
| | | |
| | | 1. è°ç¨ `StorageBlobService.upload(files, true)` ä¸ä¼ å¹¶ä¿åéä»¶ã |
| | | 2. çæé件访é®è·¯å¾ï¼é¢è§ / ä¸è½½ï¼ã |
| | | 3. å°âæ¬æ¬¡æé® + æ¬æ¬¡éä»¶è·¯å¾å表âåå
¥ Mongoã |
| | | 4. ç»§ç»æ§è¡åææä»¶è§£æå AI åææµç¨ã |
| | | |
| | | å¯è½éè¯¯ï¼æµå¼ææ¬ï¼ï¼ |
| | | |
| | | - `æä»¶ä¸ä¼ 失败` |
| | | - `ä¼è¯æä»¶ä¿¡æ¯ä¿å失败` |
| | | |
| | | ### 2.2 å岿¶æ¯æ¥å£ï¼å端éç¹ï¼ |
| | | |
| | | ```http |
| | | GET /purchase-ai/history/messages/{memoryId} |
| | | ``` |
| | | |
| | | æ¶æ¯å¯¹è±¡æ°å¢å¯éåæ®µ `filePaths`ï¼ä»
ç¨æ·æ¶æ¯å¯è½æå¼ï¼ï¼ |
| | | |
| | | ```json |
| | | { |
| | | "role": "user | assistant | system | tool | unknown", |
| | | "content": "æ¶æ¯ææ¬", |
| | | "filePaths": [ |
| | | "/common/preview/xxx?publicKey=...", |
| | | "/common/download/yyy?publicKey=..." |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | 说æï¼ |
| | | |
| | | 1. `filePaths` å¯è½ç¼ºå¤±æä¸ºç©ºï¼èä¼è¯ / æ®é对è¯ï¼ã |
| | | 2. åæ¡ç¨æ·æ¶æ¯å¯è½å
å«å¤ä¸ªéä»¶è·¯å¾ã |
| | | 3. è¯¥åæ®µå·²ææ¶æ¯ç»´åº¦å¯¹é½ï¼å¯ç´æ¥ç¨äºåå²åæ¾ã |
| | | |
| | | --- |
| | | |
| | | ## 3. å端æ¹é å»ºè®®ï¼æå½åå®ç°è½å°ï¼ |
| | | |
| | | ### 3.1 åå²ååºæ¨¡å |
| | | |
| | | 建议å¨å岿¶æ¯åå§ç»æä¸ä¿ç `filePaths`ï¼ |
| | | |
| | | ```ts |
| | | type AiHistoryMessage = { |
| | | role: string; |
| | | content: string; |
| | | filePaths?: string[]; |
| | | }; |
| | | ``` |
| | | |
| | | ### 3.2 UI æ¶æ¯æ¨¡åæ å° |
| | | |
| | | è¥å端页é¢ç¨çæ¯ `localUploadFiles` 渲æéä»¶å¡çï¼å¦ `AIChatSidebar` å½åå®ç°ï¼ï¼å岿¶æ¯éå䏿¬¡æ å°ï¼ |
| | | |
| | | ```ts |
| | | type LocalUploadFileItem = { |
| | | previewId: string; |
| | | name: string; |
| | | size: number; |
| | | type: string; |
| | | isImage: boolean; |
| | | previewUrl: string; |
| | | rawFile: null; |
| | | }; |
| | | ``` |
| | | |
| | | æ å°è§åå»ºè®®ï¼ |
| | | |
| | | 1. ä»
`role === 'user'` ä¸ `filePaths?.length > 0` æ¶çæ `localUploadFiles`ã |
| | | 2. `previewId` ç¨ `${memoryId}-${messageIndex}-${fileIndex}` çæç¨³å®å¼ã |
| | | 3. `name` å¯ä» URL è·¯å¾è§£æï¼è§£æå¤±è´¥ç¨ `file-{n}`ã |
| | | 4. `isImage` 坿æ©å±å夿ï¼`png/jpg/jpeg/gif/webp/bmp/svg`ï¼ã |
| | | 5. å¾çç `previewUrl` ç´æ¥ä½¿ç¨è·¯å¾ï¼éå¾çå¯ç½®ç©ºå¹¶èµ°å¾æ å±ç¤ºã |
| | | |
| | | ### 3.3 æ¶æ¯æ¸²æ |
| | | |
| | | 对äºç¨æ·æ¶æ¯ï¼ |
| | | |
| | | 1. æ£å¸¸æ¸²æ `content`ã |
| | | 2. è¥åå¨ `localUploadFiles`ï¼æç´æ¥ä½¿ç¨ `filePaths`ï¼ï¼å¨æ¶æ¯ä¸æ¹æ¸²æéä»¶å表/å¡çã |
| | | 3. 龿¥ç´æ¥ä½¿ç¨å端è¿åè·¯å¾ï¼ä¸åæ¼æ¥æäºæ¬¡æ¹åã |
| | | |
| | | > åç«¯å·²å®æâé¢è§/ä¸è½½è·¯å¾âéæ©ï¼å端åªè´è´£å±ç¤ºä¸æå¼ã |
| | | |
| | | ### 3.4 å
¼å®¹è¦æ± |
| | | |
| | | - `filePaths` 缺失ï¼ä¸æ¥éï¼ä¸æ¸²æéä»¶åºåã |
| | | - èä¼è¯ï¼ç»§ç»æ `role/content` å±ç¤ºï¼ä¸å½±ååå²è®°å½æ¥çã |
| | | - å¤éä»¶ï¼ä¿æé¡ºåºå±ç¤ºï¼é¿å
æä¹±ç¨æ·ä¸ä¼ 顺åºã |
| | | |
| | | --- |
| | | |
| | | ## 4. Mongo åæ®µè¯´æï¼å端已å®ç°ï¼ |
| | | |
| | | `chat_messages` ææ¡£æ°å¢/使ç¨åæ®µï¼ |
| | | |
| | | - `analyzeUserQuestions: string[]` |
| | | - `analyzeFilePaths: string[]`ï¼å
¼å®¹æ§åæ®µï¼ |
| | | - `analyzeFilePathGroups: string[][]`ï¼æ¨èï¼ææé®åç»ï¼ |
| | | |
| | | å岿¶æ¯åä¼ æ¶ï¼å端ä¼å
读å `analyzeFilePathGroups`ï¼å¹¶å
¼å®¹ `analyzeFilePaths`ã |
| | | |
| | | --- |
| | | |
| | | ## 5. èè°éªæ¶æ¸
å |
| | | |
| | | 1. æ°å»ºä¼è¯ï¼ä¸ä¼ 1 å¼ å¾ç + 1 个 Excelï¼åéåæè¯·æ±ã |
| | | 2. è°ç¨ `GET /purchase-ai/history/messages/{memoryId}`ï¼ç¡®è®¤ç¨æ·æ¶æ¯å« `filePaths` 䏿°éæ£ç¡®ã |
| | | 3. å·æ°é¡µé¢åéæ°è¿å
¥åä¸ä¼è¯ï¼ç¡®è®¤éä»¶å¡çå¯åæ¾ã |
| | | 4. åå«éªè¯ï¼ |
| | | - å¾ç/PDF å¯é¢è§è®¿é® |
| | | - å
¶å®æä»¶å¯ä¸è½½è®¿é® |
| | | 5. éªè¯èä¼è¯ï¼æ `filePaths`ï¼å¯æ£å¸¸å±ç¤ºï¼é¡µé¢ä¸æ¥éã |
| ÎļþÃû´Ó src/main/java/com/ruoyi/account/dto/AccountDto.java ÐÞ¸Ä |
| | |
| | | package com.ruoyi.account.dto; |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.ruoyi.account.pojo.AccountExpense; |
| | | import com.ruoyi.account.pojo.AccountIncome; |
| | | import com.ruoyi.dto.DateQueryDto; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Excel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import jakarta.validation.constraints.NotBlank; |
| | | import java.io.Serializable; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * è´¢å¡ç®¡ç--è´¢å¡æ¥è¡¨ |
| ÎļþÃû´Ó src/main/java/com/ruoyi/account/dto/AccountDto2.java ÐÞ¸Ä |
| | |
| | | package com.ruoyi.account.dto; |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import com.ruoyi.account.pojo.AccountExpense; |
| | | import com.ruoyi.account.pojo.AccountIncome; |
| | | import lombok.Data; |
| | | |
| | | import java.io.Serializable; |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * è´¢å¡ç®¡ç--è´¢å¡æ¥è¡¨(ç±»å) |
| ÎļþÃû´Ó src/main/java/com/ruoyi/account/dto/AccountDto3.java ÐÞ¸Ä |
| | |
| | | package com.ruoyi.account.dto; |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import com.ruoyi.account.pojo.AccountSubject; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @Data |
| | | public class AccountSubjectDto extends AccountSubject { |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Excel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class AccountSubjectImportDto { |
| | | |
| | | @Schema(description = "ç§ç®ç¼ç ") |
| | | @Excel(name = "ç§ç®ç¼ç ") |
| | | private String subjectCode; |
| | | |
| | | @Schema(description = "ç§ç®åç§°") |
| | | @Excel(name = "ç§ç®åç§°") |
| | | private String subjectName; |
| | | |
| | | @Schema(description = "ç§ç®ç±»å") |
| | | @Excel(name = "ç§ç®ç±»å") |
| | | private String subjectType; |
| | | |
| | | @Schema(description = "ä½é¢æ¹å") |
| | | @Excel(name = "ä½é¢æ¹å") |
| | | private String balanceDirection; |
| | | |
| | | /** |
| | | * ç¶æ 0å¯ç¨ 1ç¦ç¨ |
| | | */ |
| | | @Schema(description = "ç¶æ") |
| | | @Excel(name = "ç¶æ",readConverterExp = "0=å¯ç¨,1=ç¦ç¨") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "夿³¨") |
| | | @Excel(name = "夿³¨") |
| | | private String remark; |
| | | } |
| ÎļþÃû´Ó src/main/java/com/ruoyi/account/dto/DeviceTypeDetail.java ÐÞ¸Ä |
| | |
| | | package com.ruoyi.account.dto; |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| ÎļþÃû´Ó src/main/java/com/ruoyi/account/dto/DeviceTypeDistributionVO.java ÐÞ¸Ä |
| | |
| | | package com.ruoyi.account.dto; |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| ÎļþÃû´Ó src/main/java/com/ruoyi/account/dto/ReportDateDto.java ÐÞ¸Ä |
| | |
| | | package com.ruoyi.account.dto; |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.Date; |
| | | |
| | | /** |
| | | * @author :yys |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | @Data |
| | | @Schema(name = "SalesOutboundDto", description = "è´¢å¡ç®¡ç--éå®åºåºå°è´¦(ä¼ å)") |
| | | public class SalesOutboundDto { |
| | | |
| | | @Schema(description = "åºåºåå·") |
| | | private String outboundBatches; |
| | | |
| | | @Schema(description = "客æ·åç§°") |
| | | private String customerName; |
| | | |
| | | @Schema(description = "å¼å§æ¥æ") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | private Date startDate; |
| | | |
| | | @Schema(description = "ç»ææ¥æ") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | private Date endDate; |
| | | } |
| ÎļþÃû´Ó src/main/java/com/ruoyi/account/dto/SalesReceiptReturnDto.java ÐÞ¸Ä |
| | |
| | | package com.ruoyi.account.dto; |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import com.ruoyi.account.pojo.SalesReceiptReturn; |
| | | import lombok.Data; |
| ÎļþÃû´Ó src/main/java/com/ruoyi/account/dto/SalesRefundAmountOrderDto.java ÐÞ¸Ä |
| | |
| | | package com.ruoyi.account.dto; |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import com.ruoyi.account.pojo.SalesRefundAmountOrder; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.bean.dto; |
| | | |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.util.Date; |
| | | |
| | | @Data |
| | | @Schema(name = "SalesReturnDto", description = "è´¢å¡ç®¡ç--éå®éè´§å°è´¦(ä¼ å)") |
| | | public class SalesReturnDto { |
| | | |
| | | @Schema(description = "éè´§åå·") |
| | | private String returnNo; |
| | | |
| | | @Schema(description = "客æ·åç§°") |
| | | private String customerName; |
| | | |
| | | @Schema(description = "å¼å§æ¥æ") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | private Date startDate; |
| | | |
| | | @Schema(description = "ç»ææ¥æ") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | private Date endDate; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.bean.vo; |
| | | |
| | | import com.ruoyi.account.pojo.AccountSubject; |
| | | import lombok.Data; |
| | | |
| | | @Data |
| | | public class AccountSubjectVo extends AccountSubject { |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.bean.vo; |
| | | |
| | | import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Excel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Date; |
| | | |
| | | @Data |
| | | @Schema(name = "SalesOutboundVo", description = "è´¢å¡ç®¡ç--éå®åºåºå°è´¦(è¿å)") |
| | | @ExcelIgnoreUnannotated |
| | | public class SalesOutboundVo { |
| | | |
| | | @Schema(description = "åºåºåid") |
| | | private Long id; |
| | | |
| | | @Schema(description = "åºåºåå·") |
| | | @Excel(name = "åºåºåå·") |
| | | private String outboundBatches; |
| | | |
| | | @Schema(description = "客æ·åç§°") |
| | | @Excel(name = "客æ·åç§°") |
| | | private String customerName; |
| | | |
| | | @Schema(description = "åºåºæ¥æ") |
| | | @Excel(name = "åºåºæ¥æ") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | private Date shippingDate; |
| | | |
| | | @Schema(description = "产ååç§°") |
| | | @Excel(name = "产ååç§°") |
| | | private String productName; |
| | | |
| | | @Schema(description = "产åè§æ ¼") |
| | | @Excel(name = "产åè§æ ¼") |
| | | private String specificationModel; |
| | | |
| | | @Schema(description = "åºåºæ°é") |
| | | @Excel(name = "åºåºæ°é") |
| | | private BigDecimal stockOutNum; |
| | | |
| | | @Schema(description = "åè´§ç¼å·") |
| | | @Excel(name = "åè´§ç¼å·") |
| | | private String shippingNo; |
| | | |
| | | @Schema(description = "éå®è®¢åå·") |
| | | @Excel(name = "éå®è®¢åå·") |
| | | private String salesContractNo; |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.bean.vo; |
| | | |
| | | import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Excel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | | @Schema(name = "SalesReturnVo", description = "è´¢å¡ç®¡ç--éå®éè´§å°è´¦(è¿å)") |
| | | @ExcelIgnoreUnannotated |
| | | public class SalesReturnVo { |
| | | |
| | | @Schema(description = "éè´§åid") |
| | | private Long id; |
| | | |
| | | @Excel(name = "éè´§åå·") |
| | | @Schema(description = "éè´§åå·") |
| | | private String returnNo; |
| | | |
| | | @Schema(description = "客æ·åç§°") |
| | | @Excel(name = "客æ·åç§°") |
| | | private String customerName; |
| | | |
| | | @Schema(description = "å
³èåè´§åå·") |
| | | @Excel(name = "å
³èåè´§åå·") |
| | | private String shippingNo; |
| | | |
| | | @Schema(description = "éè´§æ¥æ") |
| | | @Excel(name = "éè´§æ¥æ") |
| | | @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") |
| | | private LocalDateTime makeTime; |
| | | |
| | | @Schema(description = "鿬¾æ»é¢") |
| | | @Excel(name = "鿬¾æ»é¢") |
| | | private BigDecimal refundAmount; |
| | | |
| | | @Schema(description = "éè´§åå ") |
| | | @Excel(name = "éè´§åå ") |
| | | private String returnReason; |
| | | |
| | | @Schema(description = "éå®è®¢åå·") |
| | | @Excel(name = "éå®è®¢åå·") |
| | | private String salesContractNo; |
| | | } |
| | |
| | | package com.ruoyi.account.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.dto.ReportDateDto; |
| | | import com.ruoyi.account.bean.dto.ReportDateDto; |
| | | import com.ruoyi.account.pojo.AccountExpense; |
| | | import com.ruoyi.account.service.AccountExpenseService; |
| | | import com.ruoyi.account.service.AccountIncomeService; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.controller; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.bean.dto.SalesOutboundDto; |
| | | import com.ruoyi.account.bean.dto.SalesReturnDto; |
| | | import com.ruoyi.account.bean.vo.SalesOutboundVo; |
| | | import com.ruoyi.account.bean.vo.SalesReturnVo; |
| | | import com.ruoyi.account.service.AccountSalesService; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PostMapping; |
| | | import org.springframework.web.bind.annotation.RequestMapping; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | |
| | | /** |
| | | * <p> |
| | | * è´¢å¡ç®¡ççéå®é¨å å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-07 04:45:30 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/accountSales") |
| | | @RequiredArgsConstructor |
| | | @Tag(name = "è´¢å¡ç®¡ççéå®é¨å") |
| | | public class AccountSalesController { |
| | | |
| | | private final AccountSalesService accountSalesService; |
| | | |
| | | @GetMapping("/listPageByOutbound") |
| | | @Log(title = "éå®åºåºå°è´¦", businessType = BusinessType.OTHER) |
| | | @Operation(summary = "è´¢å¡ç®¡ç--éå®åºåºå°è´¦") |
| | | public R<IPage<SalesOutboundVo>> listPageByOutbound(Page page, SalesOutboundDto salesOutboundDto) { |
| | | IPage<SalesOutboundVo> listPage = accountSalesService.listPageByOutbound(page,salesOutboundDto); |
| | | return R.ok(listPage); |
| | | } |
| | | |
| | | @PostMapping("/exportAccountSalesOutbound") |
| | | @Operation(summary = "导åºéå®åºåºæä»¶") |
| | | @Log(title = "导åºéå®åºåºæä»¶", businessType = BusinessType.EXPORT) |
| | | public void exportAccountSalesOutbound(HttpServletResponse response,SalesOutboundDto salesOutboundDto) { |
| | | accountSalesService.exportAccountSalesOutbound(response,salesOutboundDto); |
| | | } |
| | | |
| | | @GetMapping("/listPageByReturn") |
| | | @Log(title = "éå®éè´§å°è´¦", businessType = BusinessType.OTHER) |
| | | @Operation(summary = "è´¢å¡ç®¡ç--éå®éè´§å°è´¦") |
| | | public R<IPage<SalesReturnVo>> listPageBySalesReturn(Page page, SalesReturnDto salesReturnDto) { |
| | | IPage<SalesReturnVo> listPage = accountSalesService.listPageBySalesReturn(page,salesReturnDto); |
| | | return R.ok(listPage); |
| | | } |
| | | |
| | | @PostMapping("/exportAccountSalesReturn") |
| | | @Operation(summary = "导åºéå®éè´§æä»¶") |
| | | @Log(title = "导åºéå®éè´§æä»¶", businessType = BusinessType.EXPORT) |
| | | public void exportAccountSalesReturn(HttpServletResponse response,SalesReturnDto salesReturnDto) { |
| | | accountSalesService.exportAccountSalesReturn(response,salesReturnDto); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.controller; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.bean.dto.AccountSubjectDto; |
| | | import com.ruoyi.account.bean.vo.AccountSubjectVo; |
| | | import com.ruoyi.account.service.AccountSubjectService; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ»è´¦ç§ç®è¡¨ å端æ§å¶å¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-07 04:45:30 |
| | | */ |
| | | @RestController |
| | | @RequestMapping("/accountSubject") |
| | | @RequiredArgsConstructor |
| | | @Tag(name = "æ»è´¦ç§ç®") |
| | | public class AccountSubjectController { |
| | | private final AccountSubjectService accountSubjectService; |
| | | |
| | | @GetMapping("/list") |
| | | @Log(title = "æ»è´¦ç§ç®æ°æ®éå", businessType = BusinessType.OTHER) |
| | | @Operation(summary = "æ»è´¦ç§ç®å页æ¥è¯¢") |
| | | public R<IPage<AccountSubjectVo>> AccountSubjectDtoList(Page<AccountSubjectDto> page, AccountSubjectDto accountSubjectDto) { |
| | | IPage<AccountSubjectVo> paramList = accountSubjectService.baseList(page, accountSubjectDto); |
| | | return R.ok(paramList); |
| | | } |
| | | |
| | | @PostMapping("/add") |
| | | @Log(title = "æ°å¢æ»è´¦ç§ç®", businessType = BusinessType.INSERT) |
| | | @Operation(summary = "æ°å¢æ»è´¦ç§ç®") |
| | | public R AccountSubjectDtoAdd(@RequestBody AccountSubjectDto accountSubjectDto) { |
| | | return R.ok(accountSubjectService.save(accountSubjectDto)); |
| | | } |
| | | |
| | | @PutMapping("/edit") |
| | | @Log(title = "ä¿®æ¹æ»è´¦ç§ç®", businessType = BusinessType.UPDATE) |
| | | @Operation(summary = "ä¿®æ¹æ»è´¦ç§ç®") |
| | | public R AccountSubjectDtoEdit(@RequestBody AccountSubjectDto accountSubjectDto) { |
| | | return R.ok(accountSubjectService.updateById(accountSubjectDto)); |
| | | } |
| | | |
| | | @DeleteMapping("/remove/{ids}") |
| | | @Log(title = "å 餿»è´¦ç§ç®", businessType = BusinessType.DELETE) |
| | | @Operation(summary = "å 餿»è´¦ç§ç®") |
| | | public R AccountSubjectDtooRemove(@PathVariable Long[] ids) { |
| | | return R.ok(accountSubjectService.removeBatchByIds(Arrays.asList(ids))); |
| | | } |
| | | |
| | | @PostMapping("/export") |
| | | @Operation(summary = "å¯¼åºæ»è´¦ç§ç®æä»¶") |
| | | @Log(title = "å¯¼åºæ»è´¦ç§ç®æä»¶", businessType = BusinessType.EXPORT) |
| | | public void exportAccountSubject(HttpServletResponse response) { |
| | | accountSubjectService.exportAccountSubject(response); |
| | | } |
| | | |
| | | } |
| | |
| | | package com.ruoyi.account.controller; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.ruoyi.account.dto.SalesReceiptReturnDto; |
| | | import com.ruoyi.account.bean.dto.SalesReceiptReturnDto; |
| | | import com.ruoyi.account.pojo.SalesReceiptReturn; |
| | | import com.ruoyi.account.service.SalesReceiptReturnService; |
| | | import com.ruoyi.account.service.impl.SalesReceiptReturnServiceImpl; |
| | |
| | | package com.ruoyi.account.controller; |
| | | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.dto.SalesRefundAmountOrderDto; |
| | | import com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto; |
| | | import com.ruoyi.account.pojo.SalesRefundAmountOrder; |
| | | import com.ruoyi.account.service.SalesRefundAmountOrderService; |
| | | import com.ruoyi.framework.web.domain.R; |
| | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.dto.AccountDto; |
| | | import com.ruoyi.account.dto.AccountDto2; |
| | | import com.ruoyi.account.bean.dto.AccountDto; |
| | | import com.ruoyi.account.bean.dto.AccountDto2; |
| | | import com.ruoyi.account.pojo.AccountExpense; |
| | | import com.ruoyi.account.pojo.AccountIncome; |
| | | import com.ruoyi.dto.DateQueryDto; |
| | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.dto.AccountDto2; |
| | | import com.ruoyi.account.bean.dto.AccountDto2; |
| | | import com.ruoyi.account.pojo.AccountFile; |
| | | import com.ruoyi.account.pojo.AccountIncome; |
| | | import com.ruoyi.dto.DateQueryDto; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.mapper; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.account.pojo.AccountSubject; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ»è´¦ç§ç®è¡¨ Mapper æ¥å£ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-07 04:45:30 |
| | | */ |
| | | @Mapper |
| | | public interface AccountSubjectMapper extends BaseMapper<AccountSubject> { |
| | | |
| | | } |
| | |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.dto.SalesRefundAmountOrderDto; |
| | | import com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto; |
| | | import com.ruoyi.account.pojo.SalesRefundAmountOrder; |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import org.apache.ibatis.annotations.Mapper; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.pojo; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.FieldFill; |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableField; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Getter; |
| | | import lombok.Setter; |
| | | import lombok.ToString; |
| | | |
| | | import java.io.Serializable; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ»è´¦ç§ç®è¡¨ |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-07 04:45:30 |
| | | */ |
| | | @Getter |
| | | @Setter |
| | | @ToString |
| | | @TableName("account_subject") |
| | | @ApiModel(value = "AccountSubject对象", description = "æ»è´¦ç§ç®è¡¨") |
| | | public class AccountSubject implements Serializable { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | /** |
| | | * 主é®ID |
| | | */ |
| | | @ApiModelProperty("主é®ID") |
| | | @TableId(value = "id", type = IdType.AUTO) |
| | | private Long id; |
| | | |
| | | /** |
| | | * ç§ç®ç¼ç (å¯ä¸æ è¯) |
| | | */ |
| | | @ApiModelProperty("ç§ç®ç¼ç (å¯ä¸æ è¯)") |
| | | private String subjectCode; |
| | | |
| | | /** |
| | | * ç§ç®åç§° |
| | | */ |
| | | @ApiModelProperty("ç§ç®åç§°") |
| | | private String subjectName; |
| | | |
| | | /** |
| | | * ç§ç®ç±»å |
| | | */ |
| | | @ApiModelProperty("ç§ç®ç±»å") |
| | | private String subjectType; |
| | | |
| | | /** |
| | | * ä½é¢æ¹å |
| | | */ |
| | | @ApiModelProperty("ä½é¢æ¹å") |
| | | private String balanceDirection; |
| | | |
| | | /** |
| | | * ç¶æ 0å¯ç¨ 1ç¦ç¨ |
| | | */ |
| | | @ApiModelProperty("ç¶æ 0å¯ç¨ 1ç¦ç¨") |
| | | private Integer status; |
| | | |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | | @ApiModelProperty("夿³¨") |
| | | private String remark; |
| | | |
| | | /** |
| | | * å建人 |
| | | */ |
| | | @ApiModelProperty("å建人") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private String createUser; |
| | | |
| | | /** |
| | | * å建æ¶é´ |
| | | */ |
| | | @ApiModelProperty("å建æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private LocalDateTime createTime; |
| | | |
| | | /** |
| | | * ä¿®æ¹äºº |
| | | */ |
| | | @ApiModelProperty("ä¿®æ¹äºº") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private String updateUser; |
| | | |
| | | /** |
| | | * ä¿®æ¹æ¶é´ |
| | | */ |
| | | @ApiModelProperty("ä¿®æ¹æ¶é´") |
| | | @TableField(fill = FieldFill.INSERT_UPDATE) |
| | | private LocalDateTime updateTime; |
| | | |
| | | /** |
| | | * é¨é¨ID |
| | | */ |
| | | @ApiModelProperty("é¨é¨ID") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.account.dto.AccountDto; |
| | | import com.ruoyi.account.dto.AccountDto2; |
| | | import com.ruoyi.account.dto.AccountDto3; |
| | | import com.ruoyi.account.dto.ReportDateDto; |
| | | import com.ruoyi.account.bean.dto.AccountDto; |
| | | import com.ruoyi.account.bean.dto.AccountDto2; |
| | | import com.ruoyi.account.bean.dto.AccountDto3; |
| | | import com.ruoyi.account.bean.dto.ReportDateDto; |
| | | import com.ruoyi.account.pojo.AccountExpense; |
| | | import com.ruoyi.account.pojo.AccountIncome; |
| | | import com.ruoyi.dto.DateQueryDto; |
| | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.account.dto.AccountDto2; |
| | | import com.ruoyi.account.dto.AccountDto3; |
| | | import com.ruoyi.account.dto.ReportDateDto; |
| | | import com.ruoyi.account.bean.dto.AccountDto2; |
| | | import com.ruoyi.account.bean.dto.AccountDto3; |
| | | import com.ruoyi.account.bean.dto.ReportDateDto; |
| | | import com.ruoyi.account.pojo.AccountIncome; |
| | | |
| | | import jakarta.servlet.http.HttpServletResponse; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.bean.dto.SalesOutboundDto; |
| | | import com.ruoyi.account.bean.dto.SalesReturnDto; |
| | | import com.ruoyi.account.bean.vo.SalesOutboundVo; |
| | | import com.ruoyi.account.bean.vo.SalesReturnVo; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | |
| | | /** |
| | | * <p> |
| | | * è´¢å¡ç®¡ççéå®é¨å æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-07 04:45:30 |
| | | */ |
| | | public interface AccountSalesService { |
| | | |
| | | IPage<SalesOutboundVo> listPageByOutbound(Page page, SalesOutboundDto salesOutboundDto); |
| | | |
| | | void exportAccountSalesOutbound(HttpServletResponse response, SalesOutboundDto salesOutboundDto); |
| | | |
| | | IPage<SalesReturnVo> listPageBySalesReturn(Page page, SalesReturnDto salesReturnDto); |
| | | |
| | | void exportAccountSalesReturn(HttpServletResponse response, SalesReturnDto salesReturnDto); |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.bean.dto.AccountSubjectDto; |
| | | import com.ruoyi.account.bean.vo.AccountSubjectVo; |
| | | import com.ruoyi.account.pojo.AccountSubject; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ»è´¦ç§ç®è¡¨ æå¡ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-07 04:45:30 |
| | | */ |
| | | public interface AccountSubjectService extends IService<AccountSubject> { |
| | | |
| | | IPage<AccountSubjectVo> baseList(Page<AccountSubjectDto> page, AccountSubjectDto accountSubjectDto); |
| | | |
| | | void exportAccountSubject(HttpServletResponse response); |
| | | } |
| | |
| | | package com.ruoyi.account.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.ruoyi.account.dto.SalesReceiptReturnDto; |
| | | import com.ruoyi.account.bean.dto.SalesReceiptReturnDto; |
| | | import com.ruoyi.account.pojo.SalesReceiptReturn; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.dto.SalesRefundAmountOrderDto; |
| | | import com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto; |
| | | import com.ruoyi.account.pojo.SalesRefundAmountOrder; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | |
| | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.account.dto.AccountDto; |
| | | import com.ruoyi.account.dto.AccountDto2; |
| | | import com.ruoyi.account.dto.AccountDto3; |
| | | import com.ruoyi.account.dto.ReportDateDto; |
| | | import com.ruoyi.account.bean.dto.AccountDto; |
| | | import com.ruoyi.account.bean.dto.AccountDto2; |
| | | import com.ruoyi.account.bean.dto.AccountDto3; |
| | | import com.ruoyi.account.bean.dto.ReportDateDto; |
| | | import com.ruoyi.account.mapper.AccountExpenseMapper; |
| | | import com.ruoyi.account.mapper.AccountIncomeMapper; |
| | | import com.ruoyi.account.pojo.AccountExpense; |
| | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.account.dto.AccountDto3; |
| | | import com.ruoyi.account.dto.ReportDateDto; |
| | | import com.ruoyi.account.bean.dto.AccountDto3; |
| | | import com.ruoyi.account.bean.dto.ReportDateDto; |
| | | import com.ruoyi.account.mapper.AccountIncomeMapper; |
| | | import com.ruoyi.account.pojo.AccountIncome; |
| | | import com.ruoyi.account.service.AccountIncomeService; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.bean.dto.SalesOutboundDto; |
| | | import com.ruoyi.account.bean.dto.SalesReturnDto; |
| | | import com.ruoyi.account.bean.vo.SalesOutboundVo; |
| | | import com.ruoyi.account.bean.vo.SalesReturnVo; |
| | | import com.ruoyi.account.service.AccountSalesService; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.procurementrecord.mapper.ReturnManagementMapper; |
| | | import com.ruoyi.sales.mapper.ShippingInfoMapper; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * <p> |
| | | * è´¢å¡ç®¡ççéå®é¨å æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-07 04:45:30 |
| | | */ |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class AccountSalesServiceImpl implements AccountSalesService { |
| | | |
| | | private final ShippingInfoMapper shippingInfoMapper; |
| | | |
| | | private final ReturnManagementMapper returnManagementMapper; |
| | | |
| | | @Override |
| | | public IPage<SalesOutboundVo> listPageByOutbound(Page page, SalesOutboundDto salesOutboundDto) { |
| | | return shippingInfoMapper.listPageByOutbound(page,salesOutboundDto); |
| | | } |
| | | |
| | | @Override |
| | | public void exportAccountSalesOutbound(HttpServletResponse response, SalesOutboundDto salesOutboundDto) { |
| | | List<SalesOutboundVo> list = shippingInfoMapper.listPageByOutbound(new Page(1,-1),salesOutboundDto).getRecords(); |
| | | ExcelUtil<SalesOutboundVo> util = new ExcelUtil<>(SalesOutboundVo.class); |
| | | util.exportExcel(response, list , "éå®åºåº"); |
| | | } |
| | | |
| | | @Override |
| | | public IPage<SalesReturnVo> listPageBySalesReturn(Page page, SalesReturnDto salesReturnDto) { |
| | | return returnManagementMapper.listPageBySalesReturn(page,salesReturnDto); |
| | | } |
| | | |
| | | @Override |
| | | public void exportAccountSalesReturn(HttpServletResponse response, SalesReturnDto salesReturnDto) { |
| | | List<SalesReturnVo> list = returnManagementMapper.listPageBySalesReturn(new Page(1,-1),salesReturnDto).getRecords(); |
| | | ExcelUtil<SalesReturnVo> util = new ExcelUtil<>(SalesReturnVo.class); |
| | | util.exportExcel(response, list , "éå®éè´§"); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.account.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.account.bean.dto.AccountSubjectDto; |
| | | import com.ruoyi.account.bean.dto.AccountSubjectImportDto; |
| | | import com.ruoyi.account.bean.vo.AccountSubjectVo; |
| | | import com.ruoyi.account.mapper.AccountSubjectMapper; |
| | | import com.ruoyi.account.pojo.AccountSubject; |
| | | import com.ruoyi.account.service.AccountSubjectService; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * <p> |
| | | * æ»è´¦ç§ç®è¡¨ æå¡å®ç°ç±» |
| | | * </p> |
| | | * |
| | | * @author è¯å¯¼è½¯ä»¶ï¼æ±èï¼æéå
¬å¸ |
| | | * @since 2026-05-07 04:45:30 |
| | | */ |
| | | @Service |
| | | @RequiredArgsConstructor |
| | | public class AccountSubjectServiceImpl extends ServiceImpl<AccountSubjectMapper, AccountSubject> implements AccountSubjectService { |
| | | |
| | | private final AccountSubjectMapper accountSubjectMapper; |
| | | |
| | | @Override |
| | | public IPage<AccountSubjectVo> baseList(Page<AccountSubjectDto> page, AccountSubjectDto accountSubjectDto) { |
| | | LambdaQueryWrapper<AccountSubject> queryWrapper = new LambdaQueryWrapper<>(); |
| | | if (accountSubjectDto != null && StringUtils.isNotEmpty(accountSubjectDto.getSubjectCode())) { |
| | | queryWrapper.like(AccountSubject::getSubjectCode, accountSubjectDto.getSubjectCode()); |
| | | } |
| | | if (accountSubjectDto != null && StringUtils.isNotEmpty(accountSubjectDto.getSubjectName())) { |
| | | queryWrapper.like(AccountSubject::getSubjectName, accountSubjectDto.getSubjectName()); |
| | | } |
| | | if (accountSubjectDto != null && StringUtils.isNotEmpty(accountSubjectDto.getSubjectType())) { |
| | | queryWrapper.eq(AccountSubject::getSubjectType, accountSubjectDto.getSubjectType()); |
| | | } |
| | | queryWrapper.orderByDesc(AccountSubject::getId); |
| | | |
| | | Page<AccountSubject> entityPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); |
| | | Page<AccountSubject> paramPage = page(entityPage, queryWrapper); |
| | | |
| | | Page<AccountSubjectVo> resultPage = new Page<>(paramPage.getCurrent(), paramPage.getSize(), paramPage.getTotal()); |
| | | List<AccountSubjectVo> records = new ArrayList<>(paramPage.getRecords().size()); |
| | | for (AccountSubject item : paramPage.getRecords()) { |
| | | AccountSubjectVo vo = new AccountSubjectVo(); |
| | | BeanUtils.copyProperties(item, vo); |
| | | records.add(vo); |
| | | } |
| | | resultPage.setRecords(records); |
| | | return resultPage; |
| | | } |
| | | |
| | | @Override |
| | | public void exportAccountSubject(HttpServletResponse response) { |
| | | List<AccountSubject> list = accountSubjectMapper.selectList(null); |
| | | List<AccountSubjectImportDto> importDtos = list.stream().map(accountSubject -> { |
| | | AccountSubjectImportDto accountSubjectImportDto = new AccountSubjectImportDto(); |
| | | BeanUtils.copyProperties(accountSubject, accountSubjectImportDto); |
| | | return accountSubjectImportDto; |
| | | }).collect(Collectors.toList()); |
| | | ExcelUtil<AccountSubjectImportDto> util = new ExcelUtil<>(AccountSubjectImportDto.class); |
| | | util.exportExcel(response, importDtos , "æ»è´¦ç§ç®"); |
| | | } |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.dto.DeviceTypeDetail; |
| | | import com.ruoyi.account.dto.DeviceTypeDistributionVO; |
| | | import com.ruoyi.account.bean.dto.DeviceTypeDetail; |
| | | import com.ruoyi.account.bean.dto.DeviceTypeDistributionVO; |
| | | import com.ruoyi.account.mapper.BorrowInfoMapper; |
| | | import com.ruoyi.account.pojo.BorrowInfo; |
| | | import com.ruoyi.device.mapper.DeviceLedgerMapper; |
| | |
| | | package com.ruoyi.account.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.ruoyi.account.dto.SalesReceiptReturnDto; |
| | | import com.ruoyi.account.bean.dto.SalesReceiptReturnDto; |
| | | import com.ruoyi.account.pojo.SalesReceiptReturn; |
| | | import com.ruoyi.account.mapper.SalesReceiptReturnMapper; |
| | | import com.ruoyi.account.service.SalesReceiptReturnService; |
| | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.account.dto.SalesRefundAmountOrderDto; |
| | | import com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto; |
| | | import com.ruoyi.account.mapper.SalesRefundAmountOrderMapper; |
| | | import com.ruoyi.account.pojo.SalesRefundAmountOrder; |
| | | import com.ruoyi.account.service.SalesRefundAmountOrderService; |
| | |
| | | package com.ruoyi.ai.controller; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.ruoyi.ai.assistant.PurchaseAgent; |
| | | import com.ruoyi.ai.assistant.PurchaseIntentExecutor; |
| | | import com.ruoyi.ai.bean.ChatForm; |
| | | import com.ruoyi.ai.bean.PurchaseAiConfirmRequest; |
| | | import com.ruoyi.ai.context.AiSessionUserContext; |
| | | import com.ruoyi.ai.service.AiChatSessionService; |
| | | import com.ruoyi.ai.service.AiFileTextExtractor; |
| | | import com.ruoyi.ai.store.MongoChatMemoryStore; |
| | | import com.ruoyi.basic.mapper.SupplierManageMapper; |
| | | import com.ruoyi.basic.pojo.SupplierManage; |
| | | import com.ruoyi.ai.service.PurchaseAiService; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.framework.web.controller.BaseController; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.purchase.dto.PurchaseLedgerDto; |
| | | import com.ruoyi.purchase.dto.PurchaseReturnOrderDto; |
| | | import com.ruoyi.purchase.pojo.PaymentRegistration; |
| | | import com.ruoyi.purchase.service.IPaymentRegistrationService; |
| | | import com.ruoyi.purchase.service.IPurchaseLedgerService; |
| | | import com.ruoyi.purchase.service.PurchaseReturnOrdersService; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import dev.langchain4j.data.image.Image; |
| | | import dev.langchain4j.data.message.AiMessage; |
| | | import dev.langchain4j.data.message.ChatMessage; |
| | | import dev.langchain4j.data.message.Content; |
| | | import dev.langchain4j.data.message.ImageContent; |
| | | import dev.langchain4j.data.message.SystemMessage; |
| | | import dev.langchain4j.data.message.TextContent; |
| | | import dev.langchain4j.data.message.UserMessage; |
| | | import dev.langchain4j.model.chat.StreamingChatLanguageModel; |
| | | import dev.langchain4j.model.chat.response.ChatResponse; |
| | | import dev.langchain4j.model.chat.response.StreamingChatResponseHandler; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.web.bind.annotation.RequestParam; |
| | | import org.springframework.web.bind.annotation.DeleteMapping; |
| | | import org.springframework.web.bind.annotation.GetMapping; |
| | | import org.springframework.web.bind.annotation.PathVariable; |
| | | 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.RequestParam; |
| | | import org.springframework.web.bind.annotation.RestController; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | import reactor.core.publisher.Flux; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.Base64; |
| | | import java.time.LocalDate; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.time.format.DateTimeParseException; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.NoSuchElementException; |
| | | import java.util.UUID; |
| | | |
| | | @Tag(name = "éè´æºè½ä½") |
| | | @RestController |
| | | @RequestMapping("/purchase-ai") |
| | | public class PurchaseAiController extends BaseController { |
| | | |
| | | private static final String PURCHASE_FILE_ANALYZE_MEMORY_PREFIX = "purchase-file-analyze::"; |
| | | private static final int MAX_FILE_COUNT = 10; |
| | | private static final int MAX_SINGLE_FILE_TEXT_LENGTH = 8000; |
| | | private static final int MAX_TOTAL_FILE_TEXT_LENGTH = 30000; |
| | | private final PurchaseAiService purchaseAiService; |
| | | |
| | | private final PurchaseAgent purchaseAgent; |
| | | private final PurchaseIntentExecutor purchaseIntentExecutor; |
| | | private final AiSessionUserContext aiSessionUserContext; |
| | | private final MongoChatMemoryStore mongoChatMemoryStore; |
| | | private final AiChatSessionService aiChatSessionService; |
| | | private final AiFileTextExtractor aiFileTextExtractor; |
| | | private final ObjectMapper objectMapper; |
| | | private final IPurchaseLedgerService purchaseLedgerService; |
| | | private final IPaymentRegistrationService paymentRegistrationService; |
| | | private final PurchaseReturnOrdersService purchaseReturnOrdersService; |
| | | private final SupplierManageMapper supplierManageMapper; |
| | | private final StreamingChatLanguageModel purchaseVisionStreamingChatModel; |
| | | |
| | | public PurchaseAiController(PurchaseAgent purchaseAgent, |
| | | PurchaseIntentExecutor purchaseIntentExecutor, |
| | | AiSessionUserContext aiSessionUserContext, |
| | | MongoChatMemoryStore mongoChatMemoryStore, |
| | | AiChatSessionService aiChatSessionService, |
| | | AiFileTextExtractor aiFileTextExtractor, |
| | | ObjectMapper objectMapper, |
| | | IPurchaseLedgerService purchaseLedgerService, |
| | | IPaymentRegistrationService paymentRegistrationService, |
| | | PurchaseReturnOrdersService purchaseReturnOrdersService, |
| | | SupplierManageMapper supplierManageMapper, |
| | | @Qualifier("purchaseVisionStreamingChatModel") StreamingChatLanguageModel purchaseVisionStreamingChatModel) { |
| | | this.purchaseAgent = purchaseAgent; |
| | | this.purchaseIntentExecutor = purchaseIntentExecutor; |
| | | this.aiSessionUserContext = aiSessionUserContext; |
| | | this.mongoChatMemoryStore = mongoChatMemoryStore; |
| | | this.aiChatSessionService = aiChatSessionService; |
| | | this.aiFileTextExtractor = aiFileTextExtractor; |
| | | this.objectMapper = objectMapper; |
| | | this.purchaseLedgerService = purchaseLedgerService; |
| | | this.paymentRegistrationService = paymentRegistrationService; |
| | | this.purchaseReturnOrdersService = purchaseReturnOrdersService; |
| | | this.supplierManageMapper = supplierManageMapper; |
| | | this.purchaseVisionStreamingChatModel = purchaseVisionStreamingChatModel; |
| | | public PurchaseAiController(PurchaseAiService purchaseAiService) { |
| | | this.purchaseAiService = purchaseAiService; |
| | | } |
| | | |
| | | @Operation(summary = "éè´å¯¹è¯") |
| | | @PostMapping(value = "/chat", produces = "text/stream;charset=utf-8") |
| | | public Flux<String> chat(@RequestBody ChatForm chatForm) { |
| | | if (!StringUtils.hasText(chatForm.getMemoryId())) { |
| | | return Flux.just("memoryIdä¸è½ä¸ºç©º"); |
| | | } |
| | | if (!StringUtils.hasText(chatForm.getMessage())) { |
| | | return Flux.just("messageä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | String memoryId = chatForm.getMemoryId(); |
| | | String userMessage = chatForm.getMessage(); |
| | | |
| | | aiSessionUserContext.bind(memoryId, loginUser); |
| | | aiChatSessionService.touchSession(memoryId, loginUser, userMessage); |
| | | |
| | | String directResponse = purchaseIntentExecutor.tryExecute(memoryId, userMessage); |
| | | if (StringUtils.isNotEmpty(directResponse)) { |
| | | mongoChatMemoryStore.appendMessages( |
| | | memoryId, |
| | | List.of(UserMessage.from(userMessage), AiMessage.from(directResponse)) |
| | | ); |
| | | aiChatSessionService.refreshSessionStats(memoryId, loginUser); |
| | | return Flux.just(directResponse); |
| | | } |
| | | |
| | | return purchaseAgent.chat(memoryId, userMessage) |
| | | .doOnComplete(() -> aiChatSessionService.refreshSessionStats(memoryId, loginUser)) |
| | | .doOnError(ex -> aiChatSessionService.refreshSessionStats(memoryId, loginUser)); |
| | | return purchaseAiService.chat(chatForm, loginUser); |
| | | } |
| | | |
| | | @Operation(summary = "éè´å¤æä»¶åæ") |
| | |
| | | public Flux<String> analyzeFiles(@RequestParam("files") MultipartFile[] files, |
| | | @RequestParam(value = "message", required = false) String message, |
| | | @RequestParam(value = "memoryId", required = false) String memoryId) { |
| | | if (files == null || files.length == 0) { |
| | | return Flux.just("filesä¸è½ä¸ºç©º"); |
| | | } |
| | | if (files.length > MAX_FILE_COUNT) { |
| | | return Flux.just("䏿¬¡æå¤åæ" + MAX_FILE_COUNT + "个æä»¶"); |
| | | } |
| | | |
| | | String rawMemoryId = StringUtils.hasText(memoryId) ? memoryId : UUID.randomUUID().toString(); |
| | | String finalMemoryId = rawMemoryId.startsWith(PURCHASE_FILE_ANALYZE_MEMORY_PREFIX) |
| | | ? rawMemoryId |
| | | : PURCHASE_FILE_ANALYZE_MEMORY_PREFIX + rawMemoryId; |
| | | |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | aiSessionUserContext.bind(finalMemoryId, loginUser); |
| | | |
| | | String finalMessage = StringUtils.hasText(message) |
| | | ? message |
| | | : "请åæè¿äºéè´æä»¶ï¼æåå¯ç¨äºä¸å¡å¤ççæ°æ®ï¼å¹¶æ´çæå¾
客æ·ç¡®è®¤çæ ¼å¼"; |
| | | |
| | | String fileContent; |
| | | try { |
| | | fileContent = buildMultiFileContent(files); |
| | | } catch (IllegalArgumentException ex) { |
| | | return Flux.just(ex.getMessage()); |
| | | } catch (IOException ex) { |
| | | return Flux.just("æä»¶è¯»å失败"); |
| | | } |
| | | |
| | | if (!StringUtils.hasText(fileContent)) { |
| | | return Flux.just("æªæåå°æææä»¶å
容"); |
| | | } |
| | | |
| | | String userPrompt = buildPurchaseFileAnalyzePrompt(finalMessage, fileContent); |
| | | aiChatSessionService.touchSession(finalMemoryId, loginUser, "éè´å¤æä»¶åæ: " + finalMessage); |
| | | |
| | | if (containsImageFile(files)) { |
| | | return chatWithPurchaseVisionModel(finalMemoryId, userPrompt, files) |
| | | .doOnComplete(() -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)) |
| | | .doOnError(ex -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)); |
| | | } |
| | | |
| | | return Flux.defer(() -> purchaseAgent.chat(finalMemoryId, userPrompt)) |
| | | .onErrorResume(NoSuchElementException.class, ex -> { |
| | | mongoChatMemoryStore.deleteMessages(finalMemoryId); |
| | | return purchaseAgent.chat(finalMemoryId, userPrompt); |
| | | }) |
| | | .doOnComplete(() -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)) |
| | | .doOnError(ex -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)); |
| | | return purchaseAiService.analyzeFiles(files, message, memoryId, loginUser); |
| | | } |
| | | |
| | | @Operation(summary = "éè´å¤æä»¶åæç¡®è®¤å¤ç") |
| | | @PostMapping("/analyze-files/confirm") |
| | | public AjaxResult confirmAnalyzeResult(@RequestBody PurchaseAiConfirmRequest request) { |
| | | if (request == null || !StringUtils.hasText(request.getBusinessType())) { |
| | | return AjaxResult.error("businessTypeä¸è½ä¸ºç©º"); |
| | | } |
| | | if (request.getPayload() == null || request.getPayload().isEmpty()) { |
| | | return AjaxResult.error("payloadä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | try { |
| | | String businessType = request.getBusinessType().trim(); |
| | | return switch (businessType) { |
| | | case "purchase_ledger" -> processPurchaseLedger(request.getPayload()); |
| | | case "payment_registration" -> processPaymentRegistration(request.getPayload()); |
| | | case "purchase_return_order" -> processPurchaseReturnOrder(request.getPayload()); |
| | | default -> AjaxResult.error("æä¸æ¯æè¯¥ä¸å¡ç±»å: " + businessType); |
| | | }; |
| | | } catch (Exception ex) { |
| | | return AjaxResult.error(toCustomerMessage(ex)); |
| | | } |
| | | return purchaseAiService.confirmAnalyzeResult(request); |
| | | } |
| | | |
| | | @Operation(summary = "éè´ä¼è¯å表") |
| | | @GetMapping("/history/sessions") |
| | | public AjaxResult listSessions() { |
| | | return success(aiChatSessionService.listCurrentUserSessions(SecurityUtils.getLoginUser())); |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | return success(purchaseAiService.listSessions(loginUser)); |
| | | } |
| | | |
| | | @Operation(summary = "éè´ä¼è¯æ¶æ¯") |
| | | @GetMapping("/history/messages/{memoryId}") |
| | | public AjaxResult listMessages(@PathVariable String memoryId) { |
| | | return success(aiChatSessionService.listCurrentUserMessages(memoryId, SecurityUtils.getLoginUser())); |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | return success(purchaseAiService.listMessages(memoryId, loginUser)); |
| | | } |
| | | |
| | | @Operation(summary = "å é¤éè´ä¼è¯") |
| | | @DeleteMapping("/history/{memoryId}") |
| | | public AjaxResult deleteSession(@PathVariable String memoryId) { |
| | | aiSessionUserContext.remove(memoryId); |
| | | return toAjax(aiChatSessionService.deleteCurrentUserSession(memoryId, SecurityUtils.getLoginUser())); |
| | | } |
| | | |
| | | private String buildMultiFileContent(MultipartFile[] files) throws IOException { |
| | | StringBuilder builder = new StringBuilder(); |
| | | int totalLength = 0; |
| | | for (MultipartFile file : files) { |
| | | String text = aiFileTextExtractor.extractText(file); |
| | | if (!StringUtils.hasText(text)) { |
| | | continue; |
| | | } |
| | | String limitedText = text.length() > MAX_SINGLE_FILE_TEXT_LENGTH |
| | | ? text.substring(0, MAX_SINGLE_FILE_TEXT_LENGTH) |
| | | : text; |
| | | if (totalLength + limitedText.length() > MAX_TOTAL_FILE_TEXT_LENGTH) { |
| | | int remain = MAX_TOTAL_FILE_TEXT_LENGTH - totalLength; |
| | | if (remain <= 0) { |
| | | break; |
| | | } |
| | | limitedText = limitedText.substring(0, remain); |
| | | } |
| | | builder.append("\n--- æä»¶: ") |
| | | .append(file.getOriginalFilename()) |
| | | .append(" ---\n") |
| | | .append(limitedText) |
| | | .append('\n'); |
| | | totalLength += limitedText.length(); |
| | | } |
| | | return builder.toString(); |
| | | } |
| | | |
| | | private boolean containsImageFile(MultipartFile[] files) { |
| | | for (MultipartFile file : files) { |
| | | if (aiFileTextExtractor.isImageFile(file)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private Flux<String> chatWithPurchaseVisionModel(String memoryId, String userPrompt, MultipartFile[] files) { |
| | | return Flux.create(sink -> { |
| | | try { |
| | | List<Content> contents = new ArrayList<>(); |
| | | contents.add(TextContent.from(userPrompt)); |
| | | for (MultipartFile file : files) { |
| | | if (!aiFileTextExtractor.isImageFile(file)) { |
| | | continue; |
| | | } |
| | | contents.add(TextContent.from("ä¸é¢è¿å¼ å¾çæä»¶åï¼" + file.getOriginalFilename())); |
| | | contents.add(ImageContent.from(Image.builder() |
| | | .base64Data(Base64.getEncoder().encodeToString(file.getBytes())) |
| | | .mimeType(resolveImageMimeType(file)) |
| | | .build())); |
| | | } |
| | | |
| | | List<ChatMessage> messages = List.of( |
| | | SystemMessage.from("ä½ æ¯éè´ä¸å¡æä»¶åæå©æãè¯·ä»ææ¬åå¾çä¸è¯å«éè´å°è´¦ãéè´äº§åæç»ã仿¬¾æéè´§ä¿¡æ¯ï¼åªè¾åºåæ³ JSONã"), |
| | | UserMessage.from(contents) |
| | | ); |
| | | purchaseVisionStreamingChatModel.chat(messages, new StreamingChatResponseHandler() { |
| | | @Override |
| | | public void onPartialResponse(String partialResponse) { |
| | | sink.next(partialResponse); |
| | | } |
| | | |
| | | @Override |
| | | public void onCompleteResponse(ChatResponse completeResponse) { |
| | | sink.complete(); |
| | | } |
| | | |
| | | @Override |
| | | public void onError(Throwable error) { |
| | | sink.error(error); |
| | | } |
| | | }); |
| | | } catch (Exception ex) { |
| | | sink.next("å¾çæä»¶è¯»å失败ï¼è¯·ç¡®è®¤å¾çæ ¼å¼ä¸º pngãjpgãjpegãwebp æ bmpï¼ä¸å¤§å°ä¸è¶
è¿10MB"); |
| | | sink.complete(); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private String resolveImageMimeType(MultipartFile file) { |
| | | String contentType = file.getContentType(); |
| | | if (StringUtils.hasText(contentType) && contentType.startsWith("image/")) { |
| | | return contentType; |
| | | } |
| | | String filename = file.getOriginalFilename(); |
| | | String ext = ""; |
| | | if (StringUtils.hasText(filename) && filename.contains(".")) { |
| | | ext = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase(); |
| | | } |
| | | return switch (ext) { |
| | | case "jpg", "jpeg" -> "image/jpeg"; |
| | | case "webp" -> "image/webp"; |
| | | case "bmp" -> "image/bmp"; |
| | | default -> "image/png"; |
| | | }; |
| | | } |
| | | |
| | | private String buildPurchaseFileAnalyzePrompt(String message, String fileContent) { |
| | | return """ |
| | | ä½ æ¯éè´ä¸å¡æä»¶åæå©æãè¯·ä¸¥æ ¼æ ¹æ®ç¨æ·ä¸ä¼ çå¤ä¸ªæä»¶åç¨æ·è¦æ±æåéè´ä¸å¡æ°æ®ã |
| | | |
| | | ç¨æ·è¦æ±: |
| | | %s |
| | | |
| | | è¾åºè¦æ±: |
| | | 1. åªè¾åºåæ³ JSONï¼ä¸è¦ Markdownï¼ä¸è¦é¢å¤è§£éã |
| | | 2. JSON é¡¶å±å段åºå®ä¸º: |
| | | - success: boolean |
| | | - businessType: purchase_ledger | payment_registration | purchase_return_order | unknown |
| | | - action: confirm_required |
| | | - description: ä¸æè¯´æ |
| | | - confidence: 0å°1çå°æ° |
| | | - missingFields: ç¼ºå¤±åæ®µä¸æåç§°æ°ç»ï¼é¢å客æ·å±ç¤ºï¼ä¸è¦è¾åºè±æå段å |
| | | - warnings: é£é©æç¤ºæ°ç» |
| | | - payload: å¾
客æ·ç¡®è®¤çæ°æ®ï¼å段åå¿
须使ç¨å端 DTO åæ®µå |
| | | - preview: ç»å®¢æ·ç¡®è®¤ç¨ç䏿æè¦æ°ç» |
| | | 3. 妿å¯å¤æä¸ºéè´å°è´¦ï¼businessType ä½¿ç¨ purchase_ledgerï¼payload.purchaseLedgers 为éè´è®¢å/éè´å°è´¦æ°ç»: |
| | | - purchaseLedgers: éè´è®¢å/éè´å°è´¦æ°ç»ï¼æ¯æ¡è®°å½å段åå¿
é¡»ä¸ PurchaseLedgerDto ä¿æä¸è´ |
| | | - 产åæç»å¿
é¡»æ¾å¨æ¯æ¡éè´å°è´¦è®°å½ç productData åæ®µä¸ï¼productData ç±»å为 List<SalesLedgerProduct> |
| | | - ä¸è¦ä¼å
ä½¿ç¨ payload é¡¶å± productDataï¼é¡¶å± productData ä»
ä½ä¸ºæ§æ ¼å¼å
¼å®¹ |
| | | - æä»¶éçâéè´åå·âå°±æ¯âéè´ååå·âï¼ç»ä¸æ å°ä¸º purchaseContractNumber |
| | | - æä»¶éçâéå®åå·âå°±æ¯âéå®ååå·âï¼ç»ä¸æ å°ä¸º salesContractNo |
| | | - æææ¥æå段å¿
é¡»ä½¿ç¨ yyyy-MM-ddï¼ä¾å¦ 2026-04-30ï¼ä¸è¦è¾åº 4/30/26ã2026/4/30ã2026å¹´4æ30æ¥ æå¸¦æ¶åç§çæ ¼å¼ |
| | | - éè´å°è´¦ä¸éè¦å¨ payload ä¸ä¼ 审æ¹äººï¼ä¸è¦è¾åº approveUserIdsãapproverId |
| | | - missingFields åªå¡«åä¸å¡å¿
填使 æ³è¯å«çåæ®µï¼ä¸è¦æ PurchaseLedgerDto çææç©ºåæ®µé½å为缺失ï¼ç¼ºå¤±é¡¹å¿
é¡»å䏿ï¼ä¾å¦âä¾åºååç§°ââå«ç¨åä»·âï¼ä¸è¦å supplierIdãtaxInclusiveUnitPrice |
| | | - éè´å°è´¦ä¸»è¡¨å¿
å¡«åæ®µä»
æè¿äºå¤æ: purchaseContractNumberãsupplierName æ supplierId |
| | | - productData æ¯æ¡äº§åå¿
å¡«åæ®µ: productCategoryãspecificationModelãunitãquantityãtaxInclusiveUnitPrice æ taxInclusiveTotalPriceï¼å¦æåªæå«ç¨æ»ä»·åæ°éï¼å¿
é¡»è®¡ç® taxInclusiveUnitPriceï¼å¦æåªæå«ç¨åä»·åæ°éï¼å¿
é¡»è®¡ç® taxInclusiveTotalPrice |
| | | - 产ååæ®µæéè´å¯¼å
¥æ¥å£ PurchaseLedgerProductImportDto 对é½: éè´åå·ã产å大类ãè§æ ¼åå·ãåä½ãæ°éãç¨çãå«ç¨åä»·ãå«ç¨æ»ä»·ãå票类åãæ¯å¦è´¨æ£ |
| | | - éè´äº§å type åºå®ä¸º 2 |
| | | - purchaseLedgers æ¯æ¡è®°å½åªä½¿ç¨è¿äº PurchaseLedgerDto åæ®µå: |
| | | entryDateStart, entryDateEnd, id, purchaseContractNumber, supplierId, supplierName, isWhite, recorderId, recorderName, salesContractNo, salesContractNoId, projectName, entryDate, executionDate, remarks, attachmentMaterials, createdAt, updatedAt, salesLedgerId, hasChildren, Type, productData, tempFileIds, SalesLedgerFiles, phoneNumber, businessPersonId, productId, productModelId, invoiceNumber, invoiceAmount, ticketRegistrationId, contractAmount, receiptPaymentAmount, unReceiptPaymentAmount, type, paymentMethod, approvalStatus, templateName |
| | | - productData æ¯æ¡äº§ååªä½¿ç¨è¿äº SalesLedgerProduct åæ®µå: |
| | | productCategory, specificationModel, unit, quantity, taxRate, taxInclusiveUnitPrice, taxInclusiveTotalPrice, taxExclusiveTotalPrice, invoiceType, productId, productModelId, isChecked, type |
| | | 4. 妿å¯å¤æä¸ºä»æ¬¾ç»è®°ï¼businessType ä½¿ç¨ payment_registrationï¼payload.records ä¸ºä»æ¬¾ç»è®°æ°ç»ï¼å段尽éå
å« purchaseLedgerIdãsalesLedgerProductIdãcurrentPaymentAmountãpaymentMethodãpaymentDateã |
| | | 5. 妿å¯å¤æä¸ºéè´éè´§ï¼businessType ä½¿ç¨ purchase_return_orderï¼payload æ PurchaseReturnOrderDto ç»ç»ï¼æç»æ¾ purchaseReturnOrderProductsDtosã |
| | | 6. 缺å°ä¸å¡å¤çå¿
须忮µæ¶ï¼ä¸è¦ç¼é IDï¼æåæ®µæ¾å
¥ missingFieldsï¼å¹¶ä»è¿åå¯ç¡®è®¤çèç¨¿æ°æ®ã |
| | | 7. ææä¸æå
å®¹ç´æ¥ä¿çï¼ä¸è¦è½¬ä¹æ Unicodeã |
| | | |
| | | æä»¶å
容: |
| | | %s |
| | | """.formatted(message, fileContent); |
| | | } |
| | | |
| | | private AjaxResult processPurchaseLedger(Map<String, Object> payload) throws Exception { |
| | | if (payload.containsKey("purchaseLedgers")) { |
| | | return processPurchaseLedgerBatch(payload); |
| | | } |
| | | |
| | | Map<String, Object> normalizedPayload = normalizePurchaseLedgerMap(payload); |
| | | PurchaseLedgerDto dto = objectMapper.convertValue(normalizedPayload, PurchaseLedgerDto.class); |
| | | AjaxResult ledgerResult = validatePurchaseLedger(dto, 0); |
| | | if (ledgerResult != null) { |
| | | return ledgerResult; |
| | | } |
| | | AjaxResult supplierResult = fillSupplierIdByName(dto); |
| | | if (supplierResult != null) { |
| | | return supplierResult; |
| | | } |
| | | AjaxResult productResult = validatePurchaseProducts(dto.getProductData(), 0); |
| | | if (productResult != null) { |
| | | return productResult; |
| | | } |
| | | int result = purchaseLedgerService.addOrEditPurchase(dto); |
| | | return AjaxResult.success("éè´å°è´¦å·²å¤ç", result); |
| | | } |
| | | |
| | | private AjaxResult processPurchaseLedgerBatch(Map<String, Object> payload) throws Exception { |
| | | List<Map<String, Object>> purchaseLedgers = toMapList(payload.get("purchaseLedgers")); |
| | | if (purchaseLedgers.isEmpty()) { |
| | | return AjaxResult.error("purchaseLedgersä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | List<Map<String, Object>> topLevelProductData = toMapList(payload.get("productData")); |
| | | List<Map<String, Object>> results = new ArrayList<>(); |
| | | for (int i = 0; i < purchaseLedgers.size(); i++) { |
| | | Map<String, Object> ledgerMap = normalizePurchaseLedgerMap(purchaseLedgers.get(i)); |
| | | PurchaseLedgerDto dto = objectMapper.convertValue(ledgerMap, PurchaseLedgerDto.class); |
| | | AjaxResult ledgerResult = validatePurchaseLedger(dto, i); |
| | | if (ledgerResult != null) { |
| | | return ledgerResult; |
| | | } |
| | | AjaxResult supplierResult = fillSupplierIdByName(dto); |
| | | if (supplierResult != null) { |
| | | return supplierResult; |
| | | } |
| | | |
| | | List<SalesLedgerProduct> products = dto.getProductData(); |
| | | if (products == null || products.isEmpty()) { |
| | | products = matchProductsForLedger(ledgerMap, dto, topLevelProductData, purchaseLedgers.size() == 1); |
| | | dto.setProductData(products); |
| | | } |
| | | AjaxResult productResult = validatePurchaseProducts(products, i); |
| | | if (productResult != null) { |
| | | return productResult; |
| | | } |
| | | int result = purchaseLedgerService.addOrEditPurchase(dto); |
| | | |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("index", i); |
| | | item.put("purchaseContractNumber", dto.getPurchaseContractNumber()); |
| | | item.put("supplierId", dto.getSupplierId()); |
| | | item.put("supplierName", dto.getSupplierName()); |
| | | item.put("productCount", products.size()); |
| | | item.put("result", result); |
| | | results.add(item); |
| | | } |
| | | return AjaxResult.success("éè´å°è´¦å·²æ¹éå¤ç", results); |
| | | } |
| | | |
| | | private List<SalesLedgerProduct> matchProductsForLedger(Map<String, Object> ledgerMap, |
| | | PurchaseLedgerDto dto, |
| | | List<Map<String, Object>> productData, |
| | | boolean onlyOneLedger) { |
| | | List<SalesLedgerProduct> products = new ArrayList<>(); |
| | | for (Map<String, Object> productMap : productData) { |
| | | if (onlyOneLedger || productBelongsToLedger(productMap, ledgerMap, dto)) { |
| | | products.add(objectMapper.convertValue(normalizeSalesLedgerProductMap(productMap), SalesLedgerProduct.class)); |
| | | } |
| | | } |
| | | return products; |
| | | } |
| | | |
| | | private boolean productBelongsToLedger(Map<String, Object> productMap, Map<String, Object> ledgerMap, PurchaseLedgerDto dto) { |
| | | Long productPurchaseLedgerId = longValue(productMap, "purchaseLedgerId", "purchaseId", "éè´è®¢åid", "éè´å°è´¦id"); |
| | | if (productPurchaseLedgerId != null && dto.getId() != null && productPurchaseLedgerId.equals(dto.getId())) { |
| | | return true; |
| | | } |
| | | |
| | | Long productSalesLedgerId = longValue(productMap, "salesLedgerId"); |
| | | if (productSalesLedgerId != null && dto.getId() != null && productSalesLedgerId.equals(dto.getId())) { |
| | | return true; |
| | | } |
| | | |
| | | String productContractNo = stringValue(productMap, "purchaseContractNumber", "purchaseContractNo", "éè´ååå·", "éè´åå·", "éè´è®¢åå·"); |
| | | if (StringUtils.hasText(productContractNo) |
| | | && StringUtils.hasText(dto.getPurchaseContractNumber()) |
| | | && productContractNo.trim().equals(dto.getPurchaseContractNumber().trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String ledgerContractNo = stringValue(ledgerMap, "purchaseContractNumber", "purchaseContractNo", "éè´ååå·", "éè´åå·", "éè´è®¢åå·"); |
| | | if (StringUtils.hasText(productContractNo) |
| | | && StringUtils.hasText(ledgerContractNo) |
| | | && productContractNo.trim().equals(ledgerContractNo.trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String productSalesContractNo = stringValue(productMap, "salesContractNo", "salesContractNumber", "éå®ååå·", "éå®åå·", "éå®è®¢åå·"); |
| | | if (StringUtils.hasText(productSalesContractNo) |
| | | && StringUtils.hasText(dto.getSalesContractNo()) |
| | | && productSalesContractNo.trim().equals(dto.getSalesContractNo().trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String ledgerSalesContractNo = stringValue(ledgerMap, "salesContractNo", "salesContractNumber", "éå®ååå·", "éå®åå·", "éå®è®¢åå·"); |
| | | if (StringUtils.hasText(productSalesContractNo) |
| | | && StringUtils.hasText(ledgerSalesContractNo) |
| | | && productSalesContractNo.trim().equals(ledgerSalesContractNo.trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String productSupplierName = stringValue(productMap, "supplierName", "ä¾åºååç§°"); |
| | | return StringUtils.hasText(productSupplierName) |
| | | && StringUtils.hasText(dto.getSupplierName()) |
| | | && productSupplierName.trim().equals(dto.getSupplierName().trim()); |
| | | } |
| | | |
| | | private Map<String, Object> normalizePurchaseLedgerMap(Map<String, Object> source) { |
| | | Map<String, Object> target = new LinkedHashMap<>(); |
| | | copyPurchaseLedgerDtoFields(source, target); |
| | | putDtoFieldIfPresent(source, target, "entryDateStart", "å½å
¥å¼å§æ¥æ", "å½å
¥æ¥æå¼å§"); |
| | | putDtoFieldIfPresent(source, target, "entryDateEnd", "å½å
¥ç»ææ¥æ", "å½å
¥æ¥æç»æ"); |
| | | putDtoFieldIfPresent(source, target, "id", "éè´å°è´¦id", "éè´è®¢åid", "主é®"); |
| | | putDtoFieldIfPresent(source, target, "purchaseContractNumber", "purchaseContractNo", "éè´ååå·", "éè´åå·", "éè´è®¢åå·"); |
| | | putDtoFieldIfPresent(source, target, "supplierId", "ä¾åºåid", "ä¾åºåID", "ä¾åºååç§°id", "ä¾åºååç§°ID"); |
| | | putDtoFieldIfPresent(source, target, "supplierName", "ä¾åºå", "ä¾åºååç§°"); |
| | | putDtoFieldIfPresent(source, target, "isWhite", "æ¯å¦ç½åå"); |
| | | putDtoFieldIfPresent(source, target, "recorderId", "å½å
¥äººid", "å½å
¥äººID", "å½å
¥äººå§åid", "å½å
¥äººå§åID"); |
| | | putDtoFieldIfPresent(source, target, "recorderName", "å½å
¥äºº", "å½å
¥äººå§å"); |
| | | putDtoFieldIfPresent(source, target, "salesContractNo", "salesContractNumber", "éå®ååå·", "éå®åå·", "éå®è®¢åå·"); |
| | | putDtoFieldIfPresent(source, target, "salesContractNoId", "éå®ååå·id", "éå®ååå·ID", "éå®åå·id", "éå®åå·ID"); |
| | | putDtoFieldIfPresent(source, target, "projectName", "项ç®", "项ç®åç§°"); |
| | | putDtoFieldIfPresent(source, target, "entryDate", "å½å
¥æ¥æ"); |
| | | putDtoFieldIfPresent(source, target, "executionDate", "ç¾è®¢æ¥æ", "ååç¾è®¢æ¥æ"); |
| | | putDtoFieldIfPresent(source, target, "remarks", "夿³¨", "说æ"); |
| | | putDtoFieldIfPresent(source, target, "attachmentMaterials", "éä»¶ææ", "éä»¶ææè·¯å¾æåç§°"); |
| | | putDtoFieldIfPresent(source, target, "createdAt", "å建æ¶é´", "è®°å½å建æ¶é´"); |
| | | putDtoFieldIfPresent(source, target, "updatedAt", "æ´æ°æ¶é´", "è®°å½æåæ´æ°æ¶é´"); |
| | | putDtoFieldIfPresent(source, target, "salesLedgerId", "éå®å°è´¦id", "éå®å°è´¦ID", "å
³èéå®å°è´¦ä¸»è¡¨ä¸»é®"); |
| | | putDtoFieldIfPresent(source, target, "hasChildren", "æ¯å¦æå级", "æ¯å¦ææç»"); |
| | | putDtoFieldIfPresent(source, target, "Type", "å°è´¦ç±»å", "ä¸å¡ç±»å"); |
| | | putDtoFieldIfPresent(source, target, "productData", "products", "产åæç»", "éè´äº§åæç»"); |
| | | putDtoFieldIfPresent(source, target, "tempFileIds", "ä¸´æ¶æä»¶id", "ä¸´æ¶æä»¶ID", "ä¸´æ¶æä»¶ids"); |
| | | putDtoFieldIfPresent(source, target, "SalesLedgerFiles", "éä»¶å表", "éå®å°è´¦éä»¶"); |
| | | putDtoFieldIfPresent(source, target, "phoneNumber", "ä¸å¡åææºå·", "ææºå·"); |
| | | putDtoFieldIfPresent(source, target, "businessPersonId", "ä¸å¡åid", "ä¸å¡åID"); |
| | | putDtoFieldIfPresent(source, target, "productId", "产åid", "产åID"); |
| | | putDtoFieldIfPresent(source, target, "productModelId", "产åè§æ ¼id", "产åè§æ ¼ID"); |
| | | putDtoFieldIfPresent(source, target, "invoiceNumber", "å票å·", "å票å·ç "); |
| | | putDtoFieldIfPresent(source, target, "invoiceAmount", "å票éé¢", "å票éé¢ï¼å
ï¼"); |
| | | putDtoFieldIfPresent(source, target, "ticketRegistrationId", "æ¥ç¥¨ç»è®°id", "æ¥ç¥¨ç»è®°ID"); |
| | | putDtoFieldIfPresent(source, target, "contractAmount", "ååéé¢", "ååéé¢ï¼äº§åå«ç¨æ»ä»·ï¼"); |
| | | putDtoFieldIfPresent(source, target, "receiptPaymentAmount", "æ¥ç¥¨éé¢", "å·²æ¥ç¥¨éé¢", "å·²æ¥ç¥¨éé¢(å
)"); |
| | | putDtoFieldIfPresent(source, target, "unReceiptPaymentAmount", "æªæ¥ç¥¨éé¢", "æªæ¥ç¥¨éé¢(å
)"); |
| | | putDtoFieldIfPresent(source, target, "type", "æä»¶ç±»å"); |
| | | putDtoFieldIfPresent(source, target, "paymentMethod", "仿¬¾æ¹å¼"); |
| | | putDtoFieldIfPresent(source, target, "approvalStatus", "审æ¹ç¶æ"); |
| | | putDtoFieldIfPresent(source, target, "templateName", "模æ¿åç§°"); |
| | | target.remove("approveUserIds"); |
| | | target.remove("approverId"); |
| | | normalizeNestedProductData(target); |
| | | attachImportStyleProductData(source, target); |
| | | if (target.get("type") == null) { |
| | | target.put("type", 2); |
| | | } |
| | | target.putIfAbsent("entryDate", LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)); |
| | | normalizePurchaseLedgerDateFields(target); |
| | | return target; |
| | | } |
| | | |
| | | private void attachImportStyleProductData(Map<String, Object> source, Map<String, Object> target) { |
| | | if (target.get("productData") != null) { |
| | | return; |
| | | } |
| | | Map<String, Object> productMap = normalizeSalesLedgerProductMap(source); |
| | | if (hasImportStyleProductData(productMap)) { |
| | | target.put("productData", List.of(productMap)); |
| | | } |
| | | } |
| | | |
| | | private boolean hasImportStyleProductData(Map<String, Object> productMap) { |
| | | return hasMapText(productMap, "productCategory") |
| | | || hasMapText(productMap, "specificationModel") |
| | | || productMap.get("quantity") != null |
| | | || productMap.get("taxInclusiveUnitPrice") != null |
| | | || productMap.get("taxInclusiveTotalPrice") != null; |
| | | } |
| | | |
| | | private boolean hasMapText(Map<String, Object> map, String key) { |
| | | Object value = map.get(key); |
| | | return value != null && StringUtils.hasText(String.valueOf(value)); |
| | | } |
| | | |
| | | private void normalizeNestedProductData(Map<String, Object> target) { |
| | | Object productDataValue = target.get("productData"); |
| | | if (productDataValue == null) { |
| | | return; |
| | | } |
| | | List<Map<String, Object>> productMaps = toMapList(productDataValue); |
| | | List<Map<String, Object>> normalizedProducts = new ArrayList<>(); |
| | | for (Map<String, Object> productMap : productMaps) { |
| | | normalizedProducts.add(normalizeSalesLedgerProductMap(productMap)); |
| | | } |
| | | target.put("productData", normalizedProducts); |
| | | } |
| | | |
| | | private Map<String, Object> normalizeSalesLedgerProductMap(Map<String, Object> source) { |
| | | Map<String, Object> target = new LinkedHashMap<>(); |
| | | copySalesLedgerProductFields(source, target); |
| | | putDtoFieldIfPresent(source, target, "productCategory", "产å大类", "产ååç§°", "产å", "åå", "ç©æåç§°"); |
| | | putDtoFieldIfPresent(source, target, "specificationModel", "è§æ ¼åå·", "åå·", "è§æ ¼", "产åè§æ ¼"); |
| | | putDtoFieldIfPresent(source, target, "unit", "åä½"); |
| | | putDtoFieldIfPresent(source, target, "quantity", "æ°é", "éè´æ°é"); |
| | | putDtoFieldIfPresent(source, target, "taxRate", "ç¨ç"); |
| | | putDtoFieldIfPresent(source, target, "taxInclusiveUnitPrice", "å«ç¨åä»·", "åä»·", "éè´åä»·", "å«ç¨ä»·æ ¼"); |
| | | putDtoFieldIfPresent(source, target, "taxInclusiveTotalPrice", "å«ç¨æ»ä»·", "æ»ä»·", "éè´éé¢", "éé¢", "ååéé¢"); |
| | | putDtoFieldIfPresent(source, target, "taxExclusiveTotalPrice", "ä¸å«ç¨æ»ä»·"); |
| | | putDtoFieldIfPresent(source, target, "invoiceType", "å票类å", "å票类å«"); |
| | | putDtoFieldIfPresent(source, target, "productId", "产åid", "产åID"); |
| | | putDtoFieldIfPresent(source, target, "productModelId", "产åè§æ ¼id", "产åè§æ ¼ID", "åå·id", "åå·ID"); |
| | | putDtoFieldIfPresent(source, target, "isChecked", "æ¯å¦è´¨æ£", "æ¯å¦è´¨æ£éª", "è´¨æ£"); |
| | | putDtoFieldIfPresent(source, target, "type", "å°è´¦ç±»å"); |
| | | normalizeProductAmounts(target); |
| | | target.putIfAbsent("type", 2); |
| | | return target; |
| | | } |
| | | |
| | | private void copySalesLedgerProductFields(Map<String, Object> source, Map<String, Object> target) { |
| | | String[] productFields = { |
| | | "id", "salesLedgerId", "warnNum", "productCategory", "specificationModel", "unit", |
| | | "speculativeTradingName", "quantity", "minStock", "taxRate", "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", "invoiceType", "type", "ticketsNum", |
| | | "ticketsAmount", "futureTickets", "futureTicketsAmount", "invoiceNum", "noInvoiceNum", |
| | | "invoiceAmount", "noInvoiceAmount", "productId", "productModelId", "register", "registerDate", |
| | | "approveStatus", "pendingInvoiceTotal", "invoiceTotal", "pendingTicketsTotal", "ticketsTotal", |
| | | "isChecked", "isProduction" |
| | | }; |
| | | for (String field : productFields) { |
| | | if (source.containsKey(field)) { |
| | | target.put(field, source.get(field)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void normalizeProductAmounts(Map<String, Object> target) { |
| | | BigDecimal quantity = decimalValue(target.get("quantity")); |
| | | BigDecimal unitPrice = decimalValue(target.get("taxInclusiveUnitPrice")); |
| | | BigDecimal totalPrice = decimalValue(target.get("taxInclusiveTotalPrice")); |
| | | if (unitPrice == null && totalPrice != null && quantity != null && quantity.compareTo(BigDecimal.ZERO) != 0) { |
| | | target.put("taxInclusiveUnitPrice", totalPrice.divide(quantity, 6, RoundingMode.HALF_UP)); |
| | | } |
| | | if (totalPrice == null && unitPrice != null && quantity != null) { |
| | | target.put("taxInclusiveTotalPrice", unitPrice.multiply(quantity)); |
| | | } |
| | | BigDecimal taxRate = decimalValue(target.get("taxRate")); |
| | | totalPrice = decimalValue(target.get("taxInclusiveTotalPrice")); |
| | | if (target.get("taxExclusiveTotalPrice") == null && totalPrice != null && taxRate != null) { |
| | | BigDecimal divisor = BigDecimal.ONE.add(taxRate.divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP)); |
| | | target.put("taxExclusiveTotalPrice", totalPrice.divide(divisor, 2, RoundingMode.HALF_UP)); |
| | | } |
| | | } |
| | | |
| | | private AjaxResult validatePurchaseProducts(List<SalesLedgerProduct> products, int ledgerIndex) { |
| | | if (products == null || products.isEmpty()) { |
| | | return null; |
| | | } |
| | | for (int i = 0; i < products.size(); i++) { |
| | | SalesLedgerProduct product = products.get(i); |
| | | String prefix = "第" + (ledgerIndex + 1) + "个éè´å°è´¦ç第" + (i + 1) + "æ¡äº§å"; |
| | | if (!StringUtils.hasText(product.getProductCategory())) { |
| | | return AjaxResult.error(prefix + "缺å°äº§ååç§°ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (!StringUtils.hasText(product.getSpecificationModel())) { |
| | | return AjaxResult.error(prefix + "缺å°è§æ ¼åå·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (!StringUtils.hasText(product.getUnit())) { |
| | | return AjaxResult.error(prefix + "缺å°åä½ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (product.getQuantity() == null) { |
| | | return AjaxResult.error(prefix + "ç¼ºå°æ°é"); |
| | | } |
| | | if (product.getTaxInclusiveUnitPrice() == null) { |
| | | return AjaxResult.error(prefix + "缺å°å«ç¨åä»·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (product.getTaxInclusiveTotalPrice() == null) { |
| | | return AjaxResult.error(prefix + "缺å°å«ç¨æ»ä»·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private AjaxResult validatePurchaseLedger(PurchaseLedgerDto dto, int ledgerIndex) { |
| | | String prefix = "第" + (ledgerIndex + 1) + "个éè´å°è´¦"; |
| | | if (!StringUtils.hasText(dto.getPurchaseContractNumber())) { |
| | | return AjaxResult.error(prefix + "缺å°éè´ååå·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (dto.getSupplierId() == null && !StringUtils.hasText(dto.getSupplierName())) { |
| | | return AjaxResult.error(prefix + "缺å°ä¾åºååç§°ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private void normalizePurchaseLedgerDateFields(Map<String, Object> target) { |
| | | normalizeDateField(target, "entryDate"); |
| | | normalizeDateField(target, "executionDate"); |
| | | normalizeDateField(target, "createdAt"); |
| | | normalizeDateField(target, "updatedAt"); |
| | | } |
| | | |
| | | private void normalizeDateField(Map<String, Object> target, String fieldName) { |
| | | Object value = target.get(fieldName); |
| | | if (value == null) { |
| | | return; |
| | | } |
| | | String normalizedDate = normalizeDateValue(value); |
| | | if (StringUtils.hasText(normalizedDate)) { |
| | | target.put(fieldName, normalizedDate); |
| | | } |
| | | } |
| | | |
| | | private String normalizeDateValue(Object value) { |
| | | if (value instanceof Date date) { |
| | | return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | } |
| | | if (value instanceof Number number) { |
| | | return LocalDate.of(1899, 12, 30) |
| | | .plusDays(number.longValue()) |
| | | .format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | } |
| | | |
| | | String text = String.valueOf(value).trim(); |
| | | if (!StringUtils.hasText(text)) { |
| | | return null; |
| | | } |
| | | if (text.length() >= 10 && text.charAt(4) == '-' && text.charAt(7) == '-') { |
| | | return text.substring(0, 10); |
| | | } |
| | | |
| | | String normalizedText = text.replace("å¹´", "-") |
| | | .replace("æ", "-") |
| | | .replace("æ¥", "") |
| | | .replace(".", "-") |
| | | .replace("/", "-") |
| | | .trim(); |
| | | DateTimeFormatter[] formatters = { |
| | | DateTimeFormatter.ofPattern("yyyy-M-d"), |
| | | DateTimeFormatter.ofPattern("M-d-yyyy"), |
| | | DateTimeFormatter.ofPattern("M-d-yy") |
| | | }; |
| | | for (DateTimeFormatter formatter : formatters) { |
| | | try { |
| | | return LocalDate.parse(normalizedText, formatter).format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | } catch (DateTimeParseException ignored) { |
| | | // Try the next supported input pattern. |
| | | } |
| | | } |
| | | return text; |
| | | } |
| | | |
| | | private void copyPurchaseLedgerDtoFields(Map<String, Object> source, Map<String, Object> target) { |
| | | String[] dtoFields = { |
| | | "entryDateStart", "entryDateEnd", "id", "purchaseContractNumber", |
| | | "supplierId", "supplierName", "isWhite", "recorderId", "recorderName", "salesContractNo", |
| | | "salesContractNoId", "projectName", "entryDate", "executionDate", "remarks", "attachmentMaterials", |
| | | "createdAt", "updatedAt", "salesLedgerId", "hasChildren", "Type", "productData", "tempFileIds", |
| | | "SalesLedgerFiles", "phoneNumber", "businessPersonId", "productId", "productModelId", "invoiceNumber", |
| | | "invoiceAmount", "ticketRegistrationId", "contractAmount", "receiptPaymentAmount", |
| | | "unReceiptPaymentAmount", "type", "paymentMethod", "approvalStatus", "templateName" |
| | | }; |
| | | for (String field : dtoFields) { |
| | | if (source.containsKey(field)) { |
| | | target.put(field, source.get(field)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void putDtoFieldIfPresent(Map<String, Object> source, Map<String, Object> target, String dtoField, String... aliases) { |
| | | if (target.containsKey(dtoField) && target.get(dtoField) != null) { |
| | | return; |
| | | } |
| | | for (String alias : aliases) { |
| | | Object value = source.get(alias); |
| | | if (value != null && StringUtils.hasText(String.valueOf(value))) { |
| | | target.put(dtoField, value); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | |
| | | private List<Map<String, Object>> toMapList(Object value) { |
| | | if (value == null) { |
| | | return List.of(); |
| | | } |
| | | return objectMapper.convertValue(value, new TypeReference<List<Map<String, Object>>>() { |
| | | }); |
| | | } |
| | | |
| | | private String stringValue(Map<String, Object> map, String... keys) { |
| | | for (String key : keys) { |
| | | Object value = map.get(key); |
| | | if (value != null && StringUtils.hasText(String.valueOf(value))) { |
| | | return String.valueOf(value); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private Long longValue(Map<String, Object> map, String... keys) { |
| | | String value = stringValue(map, keys); |
| | | if (!StringUtils.hasText(value)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return Long.parseLong(value.trim()); |
| | | } catch (NumberFormatException ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private BigDecimal decimalValue(Object value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | if (value instanceof BigDecimal decimal) { |
| | | return decimal; |
| | | } |
| | | if (value instanceof Number number) { |
| | | return new BigDecimal(String.valueOf(number)); |
| | | } |
| | | String text = String.valueOf(value) |
| | | .replace(",", "") |
| | | .replace("ï¼", "") |
| | | .replace("å
", "") |
| | | .replace("ï¿¥", "") |
| | | .trim(); |
| | | if (!StringUtils.hasText(text)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return new BigDecimal(text); |
| | | } catch (NumberFormatException ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private String toCustomerMessage(Exception ex) { |
| | | String message = ex.getMessage(); |
| | | if (!StringUtils.hasText(message)) { |
| | | return "å¤ç失败ï¼è¯·æ£æ¥ç¡®è®¤æ°æ®åéè¯"; |
| | | } |
| | | if (message.contains("tax_inclusive_unit_price")) { |
| | | return "å¤ç失败ï¼äº§åæç»ç¼ºå°å«ç¨åä»·ï¼è¯·è¡¥å
åå确认"; |
| | | } |
| | | if (message.contains("tax_inclusive_total_price")) { |
| | | return "å¤ç失败ï¼äº§åæç»ç¼ºå°å«ç¨æ»ä»·ï¼è¯·è¡¥å
åå确认"; |
| | | } |
| | | if (message.contains("entryDate")) { |
| | | return "å¤ç失败ï¼å½å
¥æ¥ææ ¼å¼ä¸æ£ç¡®ï¼è¯·ä½¿ç¨ yyyy-MM-ddï¼ä¾å¦ 2026-04-30"; |
| | | } |
| | | if (message.contains("supplier")) { |
| | | return "å¤ç失败ï¼ä¾åºåä¿¡æ¯ä¸å®æ´ï¼è¯·ç¡®è®¤ä¾åºååç§°æä¾åºåID"; |
| | | } |
| | | if (message.contains("SQL") || message.contains("java.") || message.contains("Exception")) { |
| | | return "å¤ç失败ï¼ç¡®è®¤æ°æ®ä¸å®æ´ææ ¼å¼ä¸æ£ç¡®ï¼è¯·æ£æ¥å¿
å¡«åæ®µåéè¯"; |
| | | } |
| | | return "å¤ç失败ï¼" + message; |
| | | } |
| | | |
| | | private AjaxResult fillSupplierIdByName(PurchaseLedgerDto dto) { |
| | | if (dto.getSupplierId() != null) { |
| | | return null; |
| | | } |
| | | if (!StringUtils.hasText(dto.getSupplierName())) { |
| | | return AjaxResult.error("ä¾åºåIDä¸è½ä¸ºç©ºï¼æªè¯å«å°ä¾åºååç§°ï¼æ æ³èªå¨å¹é
ä¾åºåID"); |
| | | } |
| | | |
| | | SupplierManage supplier = supplierManageMapper.selectOne(new LambdaQueryWrapper<SupplierManage>() |
| | | .eq(SupplierManage::getSupplierName, dto.getSupplierName().trim()) |
| | | .last("limit 1")); |
| | | if (supplier == null) { |
| | | return AjaxResult.error("æªæ¾å°ä¾åºåï¼" + dto.getSupplierName() + "ï¼è¯·å
ç»´æ¤ä¾åºåææå¨éæ©ä¾åºåID"); |
| | | } |
| | | dto.setSupplierId(supplier.getId()); |
| | | return null; |
| | | } |
| | | |
| | | private AjaxResult processPaymentRegistration(Map<String, Object> payload) { |
| | | Object recordsValue = payload.get("records"); |
| | | List<PaymentRegistration> records; |
| | | if (recordsValue == null) { |
| | | records = Collections.singletonList(objectMapper.convertValue(payload, PaymentRegistration.class)); |
| | | } else { |
| | | records = objectMapper.convertValue(recordsValue, new TypeReference<List<PaymentRegistration>>() { |
| | | }); |
| | | } |
| | | int result = paymentRegistrationService.insertPaymentRegistration(records); |
| | | return AjaxResult.success("仿¬¾ç»è®°å·²å¤ç", result); |
| | | } |
| | | |
| | | private AjaxResult processPurchaseReturnOrder(Map<String, Object> payload) { |
| | | PurchaseReturnOrderDto dto = objectMapper.convertValue(payload, PurchaseReturnOrderDto.class); |
| | | Boolean result = purchaseReturnOrdersService.add(dto); |
| | | return AjaxResult.success("éè´éè´§åå·²å¤ç", result); |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | return toAjax(purchaseAiService.deleteSession(memoryId, loginUser)); |
| | | } |
| | | } |
| | |
| | | package com.ruoyi.ai.dto; |
| | | |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class AiChatMessageDto { |
| | | |
| | | private String role; |
| | | |
| | | private String content; |
| | | |
| | | private List<String> filePaths; |
| | | |
| | | public AiChatMessageDto(String role, String content) { |
| | | this.role = role; |
| | | this.content = content; |
| | | } |
| | | |
| | | public AiChatMessageDto(String role, String content, List<String> filePaths) { |
| | | this.role = role; |
| | | this.content = content; |
| | | this.filePaths = filePaths; |
| | | } |
| | | } |
| | |
| | | import org.springframework.data.mongodb.core.mapping.Document; |
| | | |
| | | import java.util.Date; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | | @AllArgsConstructor |
| | |
| | | |
| | | private String content; |
| | | |
| | | /** |
| | | * 夿件åæç¨æ·æé®ä¿¡æ¯ï¼ä¸æä»¶è·¯å¾åå¼åå¨ï¼ |
| | | */ |
| | | private List<String> analyzeUserQuestions; |
| | | |
| | | /** |
| | | * 夿件åæä¸ä¼ æä»¶è·¯å¾ï¼å¾çå pdf 使ç¨é¢è§å°åï¼å
¶ä»ä½¿ç¨ä¸è½½å°åï¼ |
| | | */ |
| | | private List<String> analyzeFilePaths; |
| | | |
| | | /** |
| | | * 夿件忿¯æ¬¡æé®å¯¹åºçæä»¶è·¯å¾åç» |
| | | */ |
| | | private List<List<String>> analyzeFilePathGroups; |
| | | |
| | | private Date createTime; |
| | | |
| | | private Date updateTime; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.ai.service; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.fasterxml.jackson.core.type.TypeReference; |
| | | import com.fasterxml.jackson.databind.ObjectMapper; |
| | | import com.ruoyi.ai.assistant.PurchaseAgent; |
| | | import com.ruoyi.ai.assistant.PurchaseIntentExecutor; |
| | | import com.ruoyi.ai.bean.ChatForm; |
| | | import com.ruoyi.ai.bean.PurchaseAiConfirmRequest; |
| | | import com.ruoyi.ai.context.AiSessionUserContext; |
| | | import com.ruoyi.ai.dto.AiChatMessageDto; |
| | | import com.ruoyi.ai.dto.AiChatSessionDto; |
| | | import com.ruoyi.ai.store.MongoChatMemoryStore; |
| | | import com.ruoyi.basic.dto.StorageBlobVO; |
| | | import com.ruoyi.basic.mapper.SupplierManageMapper; |
| | | import com.ruoyi.basic.pojo.SupplierManage; |
| | | import com.ruoyi.basic.service.StorageBlobService; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.purchase.dto.PurchaseLedgerDto; |
| | | import com.ruoyi.purchase.dto.PurchaseReturnOrderDto; |
| | | import com.ruoyi.purchase.pojo.PaymentRegistration; |
| | | import com.ruoyi.purchase.service.IPaymentRegistrationService; |
| | | import com.ruoyi.purchase.service.IPurchaseLedgerService; |
| | | import com.ruoyi.purchase.service.PurchaseReturnOrdersService; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import dev.langchain4j.data.image.Image; |
| | | import dev.langchain4j.data.message.AiMessage; |
| | | import dev.langchain4j.data.message.ChatMessage; |
| | | import dev.langchain4j.data.message.Content; |
| | | import dev.langchain4j.data.message.ImageContent; |
| | | import dev.langchain4j.data.message.SystemMessage; |
| | | import dev.langchain4j.data.message.TextContent; |
| | | import dev.langchain4j.data.message.UserMessage; |
| | | import dev.langchain4j.model.chat.StreamingChatLanguageModel; |
| | | import dev.langchain4j.model.chat.response.ChatResponse; |
| | | import dev.langchain4j.model.chat.response.StreamingChatResponseHandler; |
| | | import org.springframework.beans.factory.annotation.Qualifier; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | import reactor.core.publisher.Flux; |
| | | |
| | | import java.io.ByteArrayInputStream; |
| | | import java.io.File; |
| | | import java.io.IOException; |
| | | import java.io.InputStream; |
| | | import java.math.BigDecimal; |
| | | import java.math.RoundingMode; |
| | | import java.util.Base64; |
| | | import java.util.Arrays; |
| | | import java.time.LocalDate; |
| | | import java.time.ZoneId; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.time.format.DateTimeParseException; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Locale; |
| | | import java.util.Map; |
| | | import java.util.NoSuchElementException; |
| | | import java.util.UUID; |
| | | import java.nio.file.Files; |
| | | |
| | | @Service |
| | | public class PurchaseAiService { |
| | | |
| | | private static final String PURCHASE_FILE_ANALYZE_MEMORY_PREFIX = "purchase-file-analyze::"; |
| | | private static final int MAX_FILE_COUNT = 10; |
| | | private static final int MAX_SINGLE_FILE_TEXT_LENGTH = 8000; |
| | | private static final int MAX_TOTAL_FILE_TEXT_LENGTH = 30000; |
| | | |
| | | private final PurchaseAgent purchaseAgent; |
| | | private final PurchaseIntentExecutor purchaseIntentExecutor; |
| | | private final AiSessionUserContext aiSessionUserContext; |
| | | private final MongoChatMemoryStore mongoChatMemoryStore; |
| | | private final AiChatSessionService aiChatSessionService; |
| | | private final AiFileTextExtractor aiFileTextExtractor; |
| | | private final ObjectMapper objectMapper; |
| | | private final IPurchaseLedgerService purchaseLedgerService; |
| | | private final IPaymentRegistrationService paymentRegistrationService; |
| | | private final PurchaseReturnOrdersService purchaseReturnOrdersService; |
| | | private final StorageBlobService storageBlobService; |
| | | private final SupplierManageMapper supplierManageMapper; |
| | | private final StreamingChatLanguageModel purchaseVisionStreamingChatModel; |
| | | |
| | | public PurchaseAiService(PurchaseAgent purchaseAgent, |
| | | PurchaseIntentExecutor purchaseIntentExecutor, |
| | | AiSessionUserContext aiSessionUserContext, |
| | | MongoChatMemoryStore mongoChatMemoryStore, |
| | | AiChatSessionService aiChatSessionService, |
| | | AiFileTextExtractor aiFileTextExtractor, |
| | | ObjectMapper objectMapper, |
| | | IPurchaseLedgerService purchaseLedgerService, |
| | | IPaymentRegistrationService paymentRegistrationService, |
| | | PurchaseReturnOrdersService purchaseReturnOrdersService, |
| | | StorageBlobService storageBlobService, |
| | | SupplierManageMapper supplierManageMapper, |
| | | @Qualifier("purchaseVisionStreamingChatModel") StreamingChatLanguageModel purchaseVisionStreamingChatModel) { |
| | | this.purchaseAgent = purchaseAgent; |
| | | this.purchaseIntentExecutor = purchaseIntentExecutor; |
| | | this.aiSessionUserContext = aiSessionUserContext; |
| | | this.mongoChatMemoryStore = mongoChatMemoryStore; |
| | | this.aiChatSessionService = aiChatSessionService; |
| | | this.aiFileTextExtractor = aiFileTextExtractor; |
| | | this.objectMapper = objectMapper; |
| | | this.purchaseLedgerService = purchaseLedgerService; |
| | | this.paymentRegistrationService = paymentRegistrationService; |
| | | this.purchaseReturnOrdersService = purchaseReturnOrdersService; |
| | | this.storageBlobService = storageBlobService; |
| | | this.supplierManageMapper = supplierManageMapper; |
| | | this.purchaseVisionStreamingChatModel = purchaseVisionStreamingChatModel; |
| | | } |
| | | |
| | | public Flux<String> chat(ChatForm chatForm, LoginUser loginUser) { |
| | | if (!StringUtils.hasText(chatForm.getMemoryId())) { |
| | | return Flux.just("memoryIdä¸è½ä¸ºç©º"); |
| | | } |
| | | if (!StringUtils.hasText(chatForm.getMessage())) { |
| | | return Flux.just("messageä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | String memoryId = chatForm.getMemoryId(); |
| | | String userMessage = chatForm.getMessage(); |
| | | |
| | | aiSessionUserContext.bind(memoryId, loginUser); |
| | | aiChatSessionService.touchSession(memoryId, loginUser, userMessage); |
| | | |
| | | String directResponse = purchaseIntentExecutor.tryExecute(memoryId, userMessage); |
| | | if (StringUtils.isNotEmpty(directResponse)) { |
| | | mongoChatMemoryStore.appendMessages( |
| | | memoryId, |
| | | List.of(UserMessage.from(userMessage), AiMessage.from(directResponse)) |
| | | ); |
| | | aiChatSessionService.refreshSessionStats(memoryId, loginUser); |
| | | return Flux.just(directResponse); |
| | | } |
| | | |
| | | return purchaseAgent.chat(memoryId, userMessage) |
| | | .doOnComplete(() -> aiChatSessionService.refreshSessionStats(memoryId, loginUser)) |
| | | .doOnError(ex -> aiChatSessionService.refreshSessionStats(memoryId, loginUser)); |
| | | } |
| | | |
| | | public Flux<String> analyzeFiles(MultipartFile[] files, |
| | | String message, |
| | | String memoryId, |
| | | LoginUser loginUser) { |
| | | if (files == null || files.length == 0) { |
| | | return Flux.just("filesä¸è½ä¸ºç©º"); |
| | | } |
| | | if (files.length > MAX_FILE_COUNT) { |
| | | return Flux.just("䏿¬¡æå¤åæ" + MAX_FILE_COUNT + "个æä»¶"); |
| | | } |
| | | |
| | | String rawMemoryId = StringUtils.hasText(memoryId) ? memoryId : UUID.randomUUID().toString(); |
| | | String finalMemoryId = rawMemoryId.startsWith(PURCHASE_FILE_ANALYZE_MEMORY_PREFIX) |
| | | ? rawMemoryId |
| | | : PURCHASE_FILE_ANALYZE_MEMORY_PREFIX + rawMemoryId; |
| | | |
| | | aiSessionUserContext.bind(finalMemoryId, loginUser); |
| | | |
| | | String finalMessage = StringUtils.hasText(message) |
| | | ? message |
| | | : "请åæè¿äºéè´æä»¶ï¼æåå¯ç¨äºä¸å¡å¤ççæ°æ®ï¼å¹¶æ´çæå¾
客æ·ç¡®è®¤çæ ¼å¼"; |
| | | |
| | | List<String> filePaths; |
| | | try { |
| | | List<StorageBlobVO> uploadedFiles = storageBlobService.upload(copyFilesForUpload(files), true); |
| | | filePaths = resolveFileAccessPaths(uploadedFiles); |
| | | } catch (Exception ex) { |
| | | return Flux.just("æä»¶ä¸ä¼ 失败"); |
| | | } |
| | | try { |
| | | mongoChatMemoryStore.appendAnalyzeFileContext(finalMemoryId, finalMessage, filePaths); |
| | | } catch (Exception ex) { |
| | | return Flux.just("ä¼è¯æä»¶ä¿¡æ¯ä¿å失败"); |
| | | } |
| | | |
| | | String fileContent; |
| | | try { |
| | | fileContent = buildMultiFileContent(files); |
| | | } catch (IllegalArgumentException ex) { |
| | | return Flux.just(ex.getMessage()); |
| | | } catch (IOException ex) { |
| | | return Flux.just("æä»¶è¯»å失败"); |
| | | } |
| | | |
| | | if (!StringUtils.hasText(fileContent)) { |
| | | return Flux.just("æªæåå°æææä»¶å
容"); |
| | | } |
| | | |
| | | String userPrompt = buildPurchaseFileAnalyzePrompt(finalMessage, fileContent); |
| | | aiChatSessionService.touchSession(finalMemoryId, loginUser, "éè´å¤æä»¶åæ: " + finalMessage); |
| | | |
| | | if (containsImageFile(files)) { |
| | | return chatWithPurchaseVisionModel(finalMemoryId, finalMessage, userPrompt, files) |
| | | .doOnComplete(() -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)) |
| | | .doOnError(ex -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)); |
| | | } |
| | | |
| | | return Flux.defer(() -> purchaseAgent.chat(finalMemoryId, userPrompt)) |
| | | .onErrorResume(NoSuchElementException.class, ex -> { |
| | | mongoChatMemoryStore.deleteMessages(finalMemoryId); |
| | | return purchaseAgent.chat(finalMemoryId, userPrompt); |
| | | }) |
| | | .doOnComplete(() -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)) |
| | | .doOnError(ex -> aiChatSessionService.refreshSessionStats(finalMemoryId, loginUser)); |
| | | } |
| | | |
| | | public AjaxResult confirmAnalyzeResult(PurchaseAiConfirmRequest request) { |
| | | if (request == null || !StringUtils.hasText(request.getBusinessType())) { |
| | | return AjaxResult.error("businessTypeä¸è½ä¸ºç©º"); |
| | | } |
| | | if (request.getPayload() == null || request.getPayload().isEmpty()) { |
| | | return AjaxResult.error("payloadä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | try { |
| | | String businessType = request.getBusinessType().trim(); |
| | | return switch (businessType) { |
| | | case "purchase_ledger" -> processPurchaseLedger(request.getPayload()); |
| | | case "payment_registration" -> processPaymentRegistration(request.getPayload()); |
| | | case "purchase_return_order" -> processPurchaseReturnOrder(request.getPayload()); |
| | | default -> AjaxResult.error("æä¸æ¯æè¯¥ä¸å¡ç±»å: " + businessType); |
| | | }; |
| | | } catch (Exception ex) { |
| | | return AjaxResult.error(toCustomerMessage(ex)); |
| | | } |
| | | } |
| | | |
| | | public List<AiChatSessionDto> listSessions(LoginUser loginUser) { |
| | | return aiChatSessionService.listCurrentUserSessions(loginUser); |
| | | } |
| | | |
| | | public List<AiChatMessageDto> listMessages(String memoryId, LoginUser loginUser) { |
| | | return aiChatSessionService.listCurrentUserMessages(memoryId, loginUser); |
| | | } |
| | | |
| | | public boolean deleteSession(String memoryId, LoginUser loginUser) { |
| | | aiSessionUserContext.remove(memoryId); |
| | | return aiChatSessionService.deleteCurrentUserSession(memoryId, loginUser); |
| | | } |
| | | |
| | | private String buildMultiFileContent(MultipartFile[] files) throws IOException { |
| | | StringBuilder builder = new StringBuilder(); |
| | | int totalLength = 0; |
| | | for (MultipartFile file : files) { |
| | | String text = aiFileTextExtractor.extractText(file); |
| | | if (!StringUtils.hasText(text)) { |
| | | continue; |
| | | } |
| | | String limitedText = text.length() > MAX_SINGLE_FILE_TEXT_LENGTH |
| | | ? text.substring(0, MAX_SINGLE_FILE_TEXT_LENGTH) |
| | | : text; |
| | | if (totalLength + limitedText.length() > MAX_TOTAL_FILE_TEXT_LENGTH) { |
| | | int remain = MAX_TOTAL_FILE_TEXT_LENGTH - totalLength; |
| | | if (remain <= 0) { |
| | | break; |
| | | } |
| | | limitedText = limitedText.substring(0, remain); |
| | | } |
| | | builder.append("\n--- æä»¶: ") |
| | | .append(file.getOriginalFilename()) |
| | | .append(" ---\n") |
| | | .append(limitedText) |
| | | .append('\n'); |
| | | totalLength += limitedText.length(); |
| | | } |
| | | return builder.toString(); |
| | | } |
| | | |
| | | private boolean containsImageFile(MultipartFile[] files) { |
| | | for (MultipartFile file : files) { |
| | | if (aiFileTextExtractor.isImageFile(file)) { |
| | | return true; |
| | | } |
| | | } |
| | | return false; |
| | | } |
| | | |
| | | private List<String> resolveFileAccessPaths(List<StorageBlobVO> uploadedFiles) { |
| | | if (StringUtils.isEmpty(uploadedFiles)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<String> filePaths = new ArrayList<>(); |
| | | for (StorageBlobVO uploadedFile : uploadedFiles) { |
| | | if (uploadedFile == null) { |
| | | continue; |
| | | } |
| | | String selectedPath; |
| | | if (shouldUsePreviewPath(uploadedFile)) { |
| | | selectedPath = StringUtils.hasText(uploadedFile.getPreviewURL()) |
| | | ? uploadedFile.getPreviewURL() |
| | | : uploadedFile.getDownloadURL(); |
| | | } else { |
| | | selectedPath = StringUtils.hasText(uploadedFile.getDownloadURL()) |
| | | ? uploadedFile.getDownloadURL() |
| | | : uploadedFile.getPreviewURL(); |
| | | } |
| | | if (StringUtils.hasText(selectedPath)) { |
| | | filePaths.add(selectedPath); |
| | | } |
| | | } |
| | | return filePaths; |
| | | } |
| | | |
| | | private boolean shouldUsePreviewPath(StorageBlobVO uploadedFile) { |
| | | String contentType = uploadedFile.getContentType(); |
| | | if (StringUtils.hasText(contentType)) { |
| | | String normalized = contentType.toLowerCase(Locale.ROOT); |
| | | if (normalized.startsWith("image/") || "application/pdf".equals(normalized)) { |
| | | return true; |
| | | } |
| | | } |
| | | String filename = uploadedFile.getOriginalFilename(); |
| | | if (!StringUtils.hasText(filename) || !filename.contains(".")) { |
| | | return false; |
| | | } |
| | | String ext = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase(Locale.ROOT); |
| | | return StringUtils.inStringIgnoreCase(ext, "png", "jpg", "jpeg", "webp", "bmp", "pdf"); |
| | | } |
| | | |
| | | private List<MultipartFile> copyFilesForUpload(MultipartFile[] files) throws IOException { |
| | | List<MultipartFile> copies = new ArrayList<>(); |
| | | for (MultipartFile file : files) { |
| | | copies.add(new InMemoryMultipartFile( |
| | | file.getName(), |
| | | file.getOriginalFilename(), |
| | | file.getContentType(), |
| | | file.getBytes() |
| | | )); |
| | | } |
| | | return copies; |
| | | } |
| | | |
| | | private static final class InMemoryMultipartFile implements MultipartFile { |
| | | private final String name; |
| | | private final String originalFilename; |
| | | private final String contentType; |
| | | private final byte[] bytes; |
| | | |
| | | private InMemoryMultipartFile(String name, String originalFilename, String contentType, byte[] bytes) { |
| | | this.name = name; |
| | | this.originalFilename = originalFilename; |
| | | this.contentType = contentType; |
| | | this.bytes = bytes == null ? new byte[0] : bytes; |
| | | } |
| | | |
| | | @Override |
| | | public String getName() { |
| | | return name; |
| | | } |
| | | |
| | | @Override |
| | | public String getOriginalFilename() { |
| | | return originalFilename; |
| | | } |
| | | |
| | | @Override |
| | | public String getContentType() { |
| | | return contentType; |
| | | } |
| | | |
| | | @Override |
| | | public boolean isEmpty() { |
| | | return bytes.length == 0; |
| | | } |
| | | |
| | | @Override |
| | | public long getSize() { |
| | | return bytes.length; |
| | | } |
| | | |
| | | @Override |
| | | public byte[] getBytes() { |
| | | return bytes.clone(); |
| | | } |
| | | |
| | | @Override |
| | | public InputStream getInputStream() { |
| | | return new ByteArrayInputStream(bytes); |
| | | } |
| | | |
| | | @Override |
| | | public void transferTo(File dest) throws IOException, IllegalStateException { |
| | | Files.write(dest.toPath(), bytes); |
| | | } |
| | | } |
| | | |
| | | private Flux<String> chatWithPurchaseVisionModel(String memoryId, |
| | | String userMessage, |
| | | String userPrompt, |
| | | MultipartFile[] files) { |
| | | return Flux.create(sink -> { |
| | | StringBuilder assistantReply = new StringBuilder(); |
| | | try { |
| | | List<Content> contents = new ArrayList<>(); |
| | | contents.add(TextContent.from(userPrompt)); |
| | | for (MultipartFile file : files) { |
| | | if (!aiFileTextExtractor.isImageFile(file)) { |
| | | continue; |
| | | } |
| | | contents.add(TextContent.from("ä¸é¢è¿å¼ å¾çæä»¶åï¼" + file.getOriginalFilename())); |
| | | contents.add(ImageContent.from(Image.builder() |
| | | .base64Data(Base64.getEncoder().encodeToString(file.getBytes())) |
| | | .mimeType(resolveImageMimeType(file)) |
| | | .build())); |
| | | } |
| | | |
| | | List<ChatMessage> messages = List.of( |
| | | SystemMessage.from("ä½ æ¯éè´ä¸å¡æä»¶åæå©æãè¯·ä»ææ¬åå¾çä¸è¯å«éè´å°è´¦ãéè´äº§åæç»ã仿¬¾æéè´§ä¿¡æ¯ï¼åªè¾åºåæ³ JSONã"), |
| | | UserMessage.from(contents) |
| | | ); |
| | | safeAppendMessages(memoryId, List.of(UserMessage.from("éè´å¤æä»¶åæ: " + userMessage))); |
| | | purchaseVisionStreamingChatModel.chat(messages, new StreamingChatResponseHandler() { |
| | | @Override |
| | | public void onPartialResponse(String partialResponse) { |
| | | if (partialResponse != null) { |
| | | assistantReply.append(partialResponse); |
| | | sink.next(partialResponse); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void onCompleteResponse(ChatResponse completeResponse) { |
| | | if (StringUtils.hasText(assistantReply.toString())) { |
| | | safeAppendMessages(memoryId, List.of(AiMessage.from(assistantReply.toString()))); |
| | | } |
| | | sink.complete(); |
| | | } |
| | | |
| | | @Override |
| | | public void onError(Throwable error) { |
| | | sink.error(error); |
| | | } |
| | | }); |
| | | } catch (Exception ex) { |
| | | sink.next("å¾çæä»¶è¯»å失败ï¼è¯·ç¡®è®¤å¾çæ ¼å¼ä¸º pngãjpgãjpegãwebp æ bmpï¼ä¸å¤§å°ä¸è¶
è¿10MB"); |
| | | sink.complete(); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | private void safeAppendMessages(String memoryId, List<ChatMessage> messages) { |
| | | if (!StringUtils.hasText(memoryId) || StringUtils.isEmpty(messages)) { |
| | | return; |
| | | } |
| | | try { |
| | | mongoChatMemoryStore.appendMessages(memoryId, messages); |
| | | } catch (Exception ignored) { |
| | | } |
| | | } |
| | | |
| | | private String resolveImageMimeType(MultipartFile file) { |
| | | String contentType = file.getContentType(); |
| | | if (StringUtils.hasText(contentType) && contentType.startsWith("image/")) { |
| | | return contentType; |
| | | } |
| | | String filename = file.getOriginalFilename(); |
| | | String ext = ""; |
| | | if (StringUtils.hasText(filename) && filename.contains(".")) { |
| | | ext = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase(); |
| | | } |
| | | return switch (ext) { |
| | | case "jpg", "jpeg" -> "image/jpeg"; |
| | | case "webp" -> "image/webp"; |
| | | case "bmp" -> "image/bmp"; |
| | | default -> "image/png"; |
| | | }; |
| | | } |
| | | |
| | | private String buildPurchaseFileAnalyzePrompt(String message, String fileContent) { |
| | | return """ |
| | | ä½ æ¯éè´ä¸å¡æä»¶åæå©æãè¯·ä¸¥æ ¼æ ¹æ®ç¨æ·ä¸ä¼ çå¤ä¸ªæä»¶åç¨æ·è¦æ±æåéè´ä¸å¡æ°æ®ã |
| | | |
| | | ç¨æ·è¦æ±: |
| | | %s |
| | | |
| | | è¾åºè¦æ±: |
| | | 1. åªè¾åºåæ³ JSONï¼ä¸è¦ Markdownï¼ä¸è¦é¢å¤è§£éã |
| | | 2. JSON é¡¶å±å段åºå®ä¸º: |
| | | - success: boolean |
| | | - businessType: purchase_ledger | payment_registration | purchase_return_order | unknown |
| | | - action: confirm_required |
| | | - description: ä¸æè¯´æ |
| | | - confidence: 0å°1çå°æ° |
| | | - missingFields: ç¼ºå¤±åæ®µä¸æåç§°æ°ç»ï¼é¢å客æ·å±ç¤ºï¼ä¸è¦è¾åºè±æå段å |
| | | - warnings: é£é©æç¤ºæ°ç» |
| | | - payload: å¾
客æ·ç¡®è®¤çæ°æ®ï¼å段åå¿
须使ç¨å端 DTO åæ®µå |
| | | - preview: ç»å®¢æ·ç¡®è®¤ç¨ç䏿æè¦æ°ç» |
| | | 3. 妿å¯å¤æä¸ºéè´å°è´¦ï¼businessType ä½¿ç¨ purchase_ledgerï¼payload.purchaseLedgers 为éè´è®¢å/éè´å°è´¦æ°ç»: |
| | | - purchaseLedgers: éè´è®¢å/éè´å°è´¦æ°ç»ï¼æ¯æ¡è®°å½å段åå¿
é¡»ä¸ PurchaseLedgerDto ä¿æä¸è´ |
| | | - 产åæç»å¿
é¡»æ¾å¨æ¯æ¡éè´å°è´¦è®°å½ç productData åæ®µä¸ï¼productData ç±»å为 List<SalesLedgerProduct> |
| | | - ä¸è¦ä¼å
ä½¿ç¨ payload é¡¶å± productDataï¼é¡¶å± productData ä»
ä½ä¸ºæ§æ ¼å¼å
¼å®¹ |
| | | - æä»¶éçâéè´åå·âå°±æ¯âéè´ååå·âï¼ç»ä¸æ å°ä¸º purchaseContractNumber |
| | | - æä»¶éçâéå®åå·âå°±æ¯âéå®ååå·âï¼ç»ä¸æ å°ä¸º salesContractNo |
| | | - æææ¥æå段å¿
é¡»ä½¿ç¨ yyyy-MM-ddï¼ä¾å¦ 2026-04-30ï¼ä¸è¦è¾åº 4/30/26ã2026/4/30ã2026å¹´4æ30æ¥ æå¸¦æ¶åç§çæ ¼å¼ |
| | | - éè´å°è´¦ä¸éè¦å¨ payload ä¸ä¼ 审æ¹äººï¼ä¸è¦è¾åº approveUserIdsãapproverId |
| | | - missingFields åªå¡«åä¸å¡å¿
填使 æ³è¯å«çåæ®µï¼ä¸è¦æ PurchaseLedgerDto çææç©ºåæ®µé½å为缺失ï¼ç¼ºå¤±é¡¹å¿
é¡»å䏿ï¼ä¾å¦âä¾åºååç§°ââå«ç¨åä»·âï¼ä¸è¦å supplierIdãtaxInclusiveUnitPrice |
| | | - éè´å°è´¦ä¸»è¡¨å¿
å¡«åæ®µä»
æè¿äºå¤æ: purchaseContractNumberãsupplierName æ supplierId |
| | | - productData æ¯æ¡äº§åå¿
å¡«åæ®µ: productCategoryãspecificationModelãunitãquantityãtaxInclusiveUnitPrice æ taxInclusiveTotalPriceï¼å¦æåªæå«ç¨æ»ä»·åæ°éï¼å¿
é¡»è®¡ç® taxInclusiveUnitPriceï¼å¦æåªæå«ç¨åä»·åæ°éï¼å¿
é¡»è®¡ç® taxInclusiveTotalPrice |
| | | - 产ååæ®µæéè´å¯¼å
¥æ¥å£ PurchaseLedgerProductImportDto 对é½: éè´åå·ã产å大类ãè§æ ¼åå·ãåä½ãæ°éãç¨çãå«ç¨åä»·ãå«ç¨æ»ä»·ãå票类åãæ¯å¦è´¨æ£ |
| | | - éè´äº§å type åºå®ä¸º 2 |
| | | - purchaseLedgers æ¯æ¡è®°å½åªä½¿ç¨è¿äº PurchaseLedgerDto åæ®µå: |
| | | entryDateStart, entryDateEnd, id, purchaseContractNumber, supplierId, supplierName, isWhite, recorderId, recorderName, salesContractNo, salesContractNoId, projectName, entryDate, executionDate, remarks, attachmentMaterials, createdAt, updatedAt, salesLedgerId, hasChildren, Type, productData, tempFileIds, SalesLedgerFiles, phoneNumber, businessPersonId, productId, productModelId, invoiceNumber, invoiceAmount, ticketRegistrationId, contractAmount, receiptPaymentAmount, unReceiptPaymentAmount, type, paymentMethod, approvalStatus, templateName |
| | | - productData æ¯æ¡äº§ååªä½¿ç¨è¿äº SalesLedgerProduct åæ®µå: |
| | | productCategory, specificationModel, unit, quantity, taxRate, taxInclusiveUnitPrice, taxInclusiveTotalPrice, taxExclusiveTotalPrice, invoiceType, productId, productModelId, isChecked, type |
| | | 4. 妿å¯å¤æä¸ºä»æ¬¾ç»è®°ï¼businessType ä½¿ç¨ payment_registrationï¼payload.records ä¸ºä»æ¬¾ç»è®°æ°ç»ï¼å段尽éå
å« purchaseLedgerIdãsalesLedgerProductIdãcurrentPaymentAmountãpaymentMethodãpaymentDateã |
| | | 5. 妿å¯å¤æä¸ºéè´éè´§ï¼businessType ä½¿ç¨ purchase_return_orderï¼payload æ PurchaseReturnOrderDto ç»ç»ï¼æç»æ¾ purchaseReturnOrderProductsDtosã |
| | | 6. 缺å°ä¸å¡å¤çå¿
须忮µæ¶ï¼ä¸è¦ç¼é IDï¼æåæ®µæ¾å
¥ missingFieldsï¼å¹¶ä»è¿åå¯ç¡®è®¤çèç¨¿æ°æ®ã |
| | | 7. ææä¸æå
å®¹ç´æ¥ä¿çï¼ä¸è¦è½¬ä¹æ Unicodeã |
| | | |
| | | æä»¶å
容: |
| | | %s |
| | | """.formatted(message, fileContent); |
| | | } |
| | | |
| | | private AjaxResult processPurchaseLedger(Map<String, Object> payload) throws Exception { |
| | | if (payload.containsKey("purchaseLedgers")) { |
| | | return processPurchaseLedgerBatch(payload); |
| | | } |
| | | |
| | | Map<String, Object> normalizedPayload = normalizePurchaseLedgerMap(payload); |
| | | PurchaseLedgerDto dto = objectMapper.convertValue(normalizedPayload, PurchaseLedgerDto.class); |
| | | AjaxResult ledgerResult = validatePurchaseLedger(dto, 0); |
| | | if (ledgerResult != null) { |
| | | return ledgerResult; |
| | | } |
| | | AjaxResult supplierResult = fillSupplierIdByName(dto); |
| | | if (supplierResult != null) { |
| | | return supplierResult; |
| | | } |
| | | AjaxResult productResult = validatePurchaseProducts(dto.getProductData(), 0); |
| | | if (productResult != null) { |
| | | return productResult; |
| | | } |
| | | int result = purchaseLedgerService.addOrEditPurchase(dto); |
| | | return AjaxResult.success("éè´å°è´¦å·²å¤ç", result); |
| | | } |
| | | |
| | | private AjaxResult processPurchaseLedgerBatch(Map<String, Object> payload) throws Exception { |
| | | List<Map<String, Object>> purchaseLedgers = toMapList(payload.get("purchaseLedgers")); |
| | | if (purchaseLedgers.isEmpty()) { |
| | | return AjaxResult.error("purchaseLedgersä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | List<Map<String, Object>> topLevelProductData = toMapList(payload.get("productData")); |
| | | List<Map<String, Object>> results = new ArrayList<>(); |
| | | for (int i = 0; i < purchaseLedgers.size(); i++) { |
| | | Map<String, Object> ledgerMap = normalizePurchaseLedgerMap(purchaseLedgers.get(i)); |
| | | PurchaseLedgerDto dto = objectMapper.convertValue(ledgerMap, PurchaseLedgerDto.class); |
| | | AjaxResult ledgerResult = validatePurchaseLedger(dto, i); |
| | | if (ledgerResult != null) { |
| | | return ledgerResult; |
| | | } |
| | | AjaxResult supplierResult = fillSupplierIdByName(dto); |
| | | if (supplierResult != null) { |
| | | return supplierResult; |
| | | } |
| | | |
| | | List<SalesLedgerProduct> products = dto.getProductData(); |
| | | if (products == null || products.isEmpty()) { |
| | | products = matchProductsForLedger(ledgerMap, dto, topLevelProductData, purchaseLedgers.size() == 1); |
| | | dto.setProductData(products); |
| | | } |
| | | AjaxResult productResult = validatePurchaseProducts(products, i); |
| | | if (productResult != null) { |
| | | return productResult; |
| | | } |
| | | int result = purchaseLedgerService.addOrEditPurchase(dto); |
| | | |
| | | Map<String, Object> item = new LinkedHashMap<>(); |
| | | item.put("index", i); |
| | | item.put("purchaseContractNumber", dto.getPurchaseContractNumber()); |
| | | item.put("supplierId", dto.getSupplierId()); |
| | | item.put("supplierName", dto.getSupplierName()); |
| | | item.put("productCount", products.size()); |
| | | item.put("result", result); |
| | | results.add(item); |
| | | } |
| | | return AjaxResult.success("éè´å°è´¦å·²æ¹éå¤ç", results); |
| | | } |
| | | |
| | | private List<SalesLedgerProduct> matchProductsForLedger(Map<String, Object> ledgerMap, |
| | | PurchaseLedgerDto dto, |
| | | List<Map<String, Object>> productData, |
| | | boolean onlyOneLedger) { |
| | | List<SalesLedgerProduct> products = new ArrayList<>(); |
| | | for (Map<String, Object> productMap : productData) { |
| | | if (onlyOneLedger || productBelongsToLedger(productMap, ledgerMap, dto)) { |
| | | products.add(objectMapper.convertValue(normalizeSalesLedgerProductMap(productMap), SalesLedgerProduct.class)); |
| | | } |
| | | } |
| | | return products; |
| | | } |
| | | |
| | | private boolean productBelongsToLedger(Map<String, Object> productMap, Map<String, Object> ledgerMap, PurchaseLedgerDto dto) { |
| | | Long productPurchaseLedgerId = longValue(productMap, "purchaseLedgerId", "purchaseId", "éè´è®¢åid", "éè´å°è´¦id"); |
| | | if (productPurchaseLedgerId != null && dto.getId() != null && productPurchaseLedgerId.equals(dto.getId())) { |
| | | return true; |
| | | } |
| | | |
| | | Long productSalesLedgerId = longValue(productMap, "salesLedgerId"); |
| | | if (productSalesLedgerId != null && dto.getId() != null && productSalesLedgerId.equals(dto.getId())) { |
| | | return true; |
| | | } |
| | | |
| | | String productContractNo = stringValue(productMap, "purchaseContractNumber", "purchaseContractNo", "éè´ååå·", "éè´åå·", "éè´è®¢åå·"); |
| | | if (StringUtils.hasText(productContractNo) |
| | | && StringUtils.hasText(dto.getPurchaseContractNumber()) |
| | | && productContractNo.trim().equals(dto.getPurchaseContractNumber().trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String ledgerContractNo = stringValue(ledgerMap, "purchaseContractNumber", "purchaseContractNo", "éè´ååå·", "éè´åå·", "éè´è®¢åå·"); |
| | | if (StringUtils.hasText(productContractNo) |
| | | && StringUtils.hasText(ledgerContractNo) |
| | | && productContractNo.trim().equals(ledgerContractNo.trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String productSalesContractNo = stringValue(productMap, "salesContractNo", "salesContractNumber", "éå®ååå·", "éå®åå·", "éå®è®¢åå·"); |
| | | if (StringUtils.hasText(productSalesContractNo) |
| | | && StringUtils.hasText(dto.getSalesContractNo()) |
| | | && productSalesContractNo.trim().equals(dto.getSalesContractNo().trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String ledgerSalesContractNo = stringValue(ledgerMap, "salesContractNo", "salesContractNumber", "éå®ååå·", "éå®åå·", "éå®è®¢åå·"); |
| | | if (StringUtils.hasText(productSalesContractNo) |
| | | && StringUtils.hasText(ledgerSalesContractNo) |
| | | && productSalesContractNo.trim().equals(ledgerSalesContractNo.trim())) { |
| | | return true; |
| | | } |
| | | |
| | | String productSupplierName = stringValue(productMap, "supplierName", "ä¾åºååç§°"); |
| | | return StringUtils.hasText(productSupplierName) |
| | | && StringUtils.hasText(dto.getSupplierName()) |
| | | && productSupplierName.trim().equals(dto.getSupplierName().trim()); |
| | | } |
| | | |
| | | private Map<String, Object> normalizePurchaseLedgerMap(Map<String, Object> source) { |
| | | Map<String, Object> target = new LinkedHashMap<>(); |
| | | copyPurchaseLedgerDtoFields(source, target); |
| | | putDtoFieldIfPresent(source, target, "entryDateStart", "å½å
¥å¼å§æ¥æ", "å½å
¥æ¥æå¼å§"); |
| | | putDtoFieldIfPresent(source, target, "entryDateEnd", "å½å
¥ç»ææ¥æ", "å½å
¥æ¥æç»æ"); |
| | | putDtoFieldIfPresent(source, target, "id", "éè´å°è´¦id", "éè´è®¢åid", "主é®"); |
| | | putDtoFieldIfPresent(source, target, "purchaseContractNumber", "purchaseContractNo", "éè´ååå·", "éè´åå·", "éè´è®¢åå·"); |
| | | putDtoFieldIfPresent(source, target, "supplierId", "ä¾åºåid", "ä¾åºåID", "ä¾åºååç§°id", "ä¾åºååç§°ID"); |
| | | putDtoFieldIfPresent(source, target, "supplierName", "ä¾åºå", "ä¾åºååç§°"); |
| | | putDtoFieldIfPresent(source, target, "isWhite", "æ¯å¦ç½åå"); |
| | | putDtoFieldIfPresent(source, target, "recorderId", "å½å
¥äººid", "å½å
¥äººID", "å½å
¥äººå§åid", "å½å
¥äººå§åID"); |
| | | putDtoFieldIfPresent(source, target, "recorderName", "å½å
¥äºº", "å½å
¥äººå§å"); |
| | | putDtoFieldIfPresent(source, target, "salesContractNo", "salesContractNumber", "éå®ååå·", "éå®åå·", "éå®è®¢åå·"); |
| | | putDtoFieldIfPresent(source, target, "salesContractNoId", "éå®ååå·id", "éå®ååå·ID", "éå®åå·id", "éå®åå·ID"); |
| | | putDtoFieldIfPresent(source, target, "projectName", "项ç®", "项ç®åç§°"); |
| | | putDtoFieldIfPresent(source, target, "entryDate", "å½å
¥æ¥æ"); |
| | | putDtoFieldIfPresent(source, target, "executionDate", "ç¾è®¢æ¥æ", "ååç¾è®¢æ¥æ"); |
| | | putDtoFieldIfPresent(source, target, "remarks", "夿³¨", "说æ"); |
| | | putDtoFieldIfPresent(source, target, "attachmentMaterials", "éä»¶ææ", "éä»¶ææè·¯å¾æåç§°"); |
| | | putDtoFieldIfPresent(source, target, "createdAt", "å建æ¶é´", "è®°å½å建æ¶é´"); |
| | | putDtoFieldIfPresent(source, target, "updatedAt", "æ´æ°æ¶é´", "è®°å½æåæ´æ°æ¶é´"); |
| | | putDtoFieldIfPresent(source, target, "salesLedgerId", "éå®å°è´¦id", "éå®å°è´¦ID", "å
³èéå®å°è´¦ä¸»è¡¨ä¸»é®"); |
| | | putDtoFieldIfPresent(source, target, "hasChildren", "æ¯å¦æå级", "æ¯å¦ææç»"); |
| | | putDtoFieldIfPresent(source, target, "Type", "å°è´¦ç±»å", "ä¸å¡ç±»å"); |
| | | putDtoFieldIfPresent(source, target, "productData", "products", "产åæç»", "éè´äº§åæç»"); |
| | | putDtoFieldIfPresent(source, target, "tempFileIds", "ä¸´æ¶æä»¶id", "ä¸´æ¶æä»¶ID", "ä¸´æ¶æä»¶ids"); |
| | | putDtoFieldIfPresent(source, target, "SalesLedgerFiles", "éä»¶å表", "éå®å°è´¦éä»¶"); |
| | | putDtoFieldIfPresent(source, target, "phoneNumber", "ä¸å¡åææºå·", "ææºå·"); |
| | | putDtoFieldIfPresent(source, target, "businessPersonId", "ä¸å¡åid", "ä¸å¡åID"); |
| | | putDtoFieldIfPresent(source, target, "productId", "产åid", "产åID"); |
| | | putDtoFieldIfPresent(source, target, "productModelId", "产åè§æ ¼id", "产åè§æ ¼ID"); |
| | | putDtoFieldIfPresent(source, target, "invoiceNumber", "å票å·", "å票å·ç "); |
| | | putDtoFieldIfPresent(source, target, "invoiceAmount", "å票éé¢", "å票éé¢ï¼å
ï¼"); |
| | | putDtoFieldIfPresent(source, target, "ticketRegistrationId", "æ¥ç¥¨ç»è®°id", "æ¥ç¥¨ç»è®°ID"); |
| | | putDtoFieldIfPresent(source, target, "contractAmount", "ååéé¢", "ååéé¢ï¼äº§åå«ç¨æ»ä»·ï¼"); |
| | | putDtoFieldIfPresent(source, target, "receiptPaymentAmount", "æ¥ç¥¨éé¢", "å·²æ¥ç¥¨éé¢", "å·²æ¥ç¥¨éé¢(å
)"); |
| | | putDtoFieldIfPresent(source, target, "unReceiptPaymentAmount", "æªæ¥ç¥¨éé¢", "æªæ¥ç¥¨éé¢(å
)"); |
| | | putDtoFieldIfPresent(source, target, "type", "æä»¶ç±»å"); |
| | | putDtoFieldIfPresent(source, target, "paymentMethod", "仿¬¾æ¹å¼"); |
| | | putDtoFieldIfPresent(source, target, "approvalStatus", "审æ¹ç¶æ"); |
| | | putDtoFieldIfPresent(source, target, "templateName", "模æ¿åç§°"); |
| | | target.remove("approveUserIds"); |
| | | target.remove("approverId"); |
| | | normalizeNestedProductData(target); |
| | | attachImportStyleProductData(source, target); |
| | | if (target.get("type") == null) { |
| | | target.put("type", 2); |
| | | } |
| | | target.putIfAbsent("entryDate", LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)); |
| | | normalizePurchaseLedgerDateFields(target); |
| | | return target; |
| | | } |
| | | |
| | | private void attachImportStyleProductData(Map<String, Object> source, Map<String, Object> target) { |
| | | if (target.get("productData") != null) { |
| | | return; |
| | | } |
| | | Map<String, Object> productMap = normalizeSalesLedgerProductMap(source); |
| | | if (hasImportStyleProductData(productMap)) { |
| | | target.put("productData", List.of(productMap)); |
| | | } |
| | | } |
| | | |
| | | private boolean hasImportStyleProductData(Map<String, Object> productMap) { |
| | | return hasMapText(productMap, "productCategory") |
| | | || hasMapText(productMap, "specificationModel") |
| | | || productMap.get("quantity") != null |
| | | || productMap.get("taxInclusiveUnitPrice") != null |
| | | || productMap.get("taxInclusiveTotalPrice") != null; |
| | | } |
| | | |
| | | private boolean hasMapText(Map<String, Object> map, String key) { |
| | | Object value = map.get(key); |
| | | return value != null && StringUtils.hasText(String.valueOf(value)); |
| | | } |
| | | |
| | | private void normalizeNestedProductData(Map<String, Object> target) { |
| | | Object productDataValue = target.get("productData"); |
| | | if (productDataValue == null) { |
| | | return; |
| | | } |
| | | List<Map<String, Object>> productMaps = toMapList(productDataValue); |
| | | List<Map<String, Object>> normalizedProducts = new ArrayList<>(); |
| | | for (Map<String, Object> productMap : productMaps) { |
| | | normalizedProducts.add(normalizeSalesLedgerProductMap(productMap)); |
| | | } |
| | | target.put("productData", normalizedProducts); |
| | | } |
| | | |
| | | private Map<String, Object> normalizeSalesLedgerProductMap(Map<String, Object> source) { |
| | | Map<String, Object> target = new LinkedHashMap<>(); |
| | | copySalesLedgerProductFields(source, target); |
| | | putDtoFieldIfPresent(source, target, "productCategory", "产å大类", "产ååç§°", "产å", "åå", "ç©æåç§°"); |
| | | putDtoFieldIfPresent(source, target, "specificationModel", "è§æ ¼åå·", "åå·", "è§æ ¼", "产åè§æ ¼"); |
| | | putDtoFieldIfPresent(source, target, "unit", "åä½"); |
| | | putDtoFieldIfPresent(source, target, "quantity", "æ°é", "éè´æ°é"); |
| | | putDtoFieldIfPresent(source, target, "taxRate", "ç¨ç"); |
| | | putDtoFieldIfPresent(source, target, "taxInclusiveUnitPrice", "å«ç¨åä»·", "åä»·", "éè´åä»·", "å«ç¨ä»·æ ¼"); |
| | | putDtoFieldIfPresent(source, target, "taxInclusiveTotalPrice", "å«ç¨æ»ä»·", "æ»ä»·", "éè´éé¢", "éé¢", "ååéé¢"); |
| | | putDtoFieldIfPresent(source, target, "taxExclusiveTotalPrice", "ä¸å«ç¨æ»ä»·"); |
| | | putDtoFieldIfPresent(source, target, "invoiceType", "å票类å", "å票类å«"); |
| | | putDtoFieldIfPresent(source, target, "productId", "产åid", "产åID"); |
| | | putDtoFieldIfPresent(source, target, "productModelId", "产åè§æ ¼id", "产åè§æ ¼ID", "åå·id", "åå·ID"); |
| | | putDtoFieldIfPresent(source, target, "isChecked", "æ¯å¦è´¨æ£", "æ¯å¦è´¨æ£éª", "è´¨æ£"); |
| | | putDtoFieldIfPresent(source, target, "type", "å°è´¦ç±»å"); |
| | | normalizeProductAmounts(target); |
| | | target.putIfAbsent("type", 2); |
| | | return target; |
| | | } |
| | | |
| | | private void copySalesLedgerProductFields(Map<String, Object> source, Map<String, Object> target) { |
| | | String[] productFields = { |
| | | "id", "salesLedgerId", "warnNum", "productCategory", "specificationModel", "unit", |
| | | "speculativeTradingName", "quantity", "minStock", "taxRate", "taxInclusiveUnitPrice", |
| | | "taxInclusiveTotalPrice", "taxExclusiveTotalPrice", "invoiceType", "type", "ticketsNum", |
| | | "ticketsAmount", "futureTickets", "futureTicketsAmount", "invoiceNum", "noInvoiceNum", |
| | | "invoiceAmount", "noInvoiceAmount", "productId", "productModelId", "register", "registerDate", |
| | | "approveStatus", "pendingInvoiceTotal", "invoiceTotal", "pendingTicketsTotal", "ticketsTotal", |
| | | "isChecked", "isProduction" |
| | | }; |
| | | for (String field : productFields) { |
| | | if (source.containsKey(field)) { |
| | | target.put(field, source.get(field)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void normalizeProductAmounts(Map<String, Object> target) { |
| | | BigDecimal quantity = decimalValue(target.get("quantity")); |
| | | BigDecimal unitPrice = decimalValue(target.get("taxInclusiveUnitPrice")); |
| | | BigDecimal totalPrice = decimalValue(target.get("taxInclusiveTotalPrice")); |
| | | if (unitPrice == null && totalPrice != null && quantity != null && quantity.compareTo(BigDecimal.ZERO) != 0) { |
| | | target.put("taxInclusiveUnitPrice", totalPrice.divide(quantity, 6, RoundingMode.HALF_UP)); |
| | | } |
| | | if (totalPrice == null && unitPrice != null && quantity != null) { |
| | | target.put("taxInclusiveTotalPrice", unitPrice.multiply(quantity)); |
| | | } |
| | | BigDecimal taxRate = decimalValue(target.get("taxRate")); |
| | | totalPrice = decimalValue(target.get("taxInclusiveTotalPrice")); |
| | | if (target.get("taxExclusiveTotalPrice") == null && totalPrice != null && taxRate != null) { |
| | | BigDecimal divisor = BigDecimal.ONE.add(taxRate.divide(new BigDecimal("100"), 6, RoundingMode.HALF_UP)); |
| | | target.put("taxExclusiveTotalPrice", totalPrice.divide(divisor, 2, RoundingMode.HALF_UP)); |
| | | } |
| | | } |
| | | |
| | | private AjaxResult validatePurchaseProducts(List<SalesLedgerProduct> products, int ledgerIndex) { |
| | | if (products == null || products.isEmpty()) { |
| | | return null; |
| | | } |
| | | for (int i = 0; i < products.size(); i++) { |
| | | SalesLedgerProduct product = products.get(i); |
| | | String prefix = "第" + (ledgerIndex + 1) + "个éè´å°è´¦ç第" + (i + 1) + "æ¡äº§å"; |
| | | if (!StringUtils.hasText(product.getProductCategory())) { |
| | | return AjaxResult.error(prefix + "缺å°äº§ååç§°ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (!StringUtils.hasText(product.getSpecificationModel())) { |
| | | return AjaxResult.error(prefix + "缺å°è§æ ¼åå·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (!StringUtils.hasText(product.getUnit())) { |
| | | return AjaxResult.error(prefix + "缺å°åä½ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (product.getQuantity() == null) { |
| | | return AjaxResult.error(prefix + "ç¼ºå°æ°é"); |
| | | } |
| | | if (product.getTaxInclusiveUnitPrice() == null) { |
| | | return AjaxResult.error(prefix + "缺å°å«ç¨åä»·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (product.getTaxInclusiveTotalPrice() == null) { |
| | | return AjaxResult.error(prefix + "缺å°å«ç¨æ»ä»·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private AjaxResult validatePurchaseLedger(PurchaseLedgerDto dto, int ledgerIndex) { |
| | | String prefix = "第" + (ledgerIndex + 1) + "个éè´å°è´¦"; |
| | | if (!StringUtils.hasText(dto.getPurchaseContractNumber())) { |
| | | return AjaxResult.error(prefix + "缺å°éè´ååå·ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | if (dto.getSupplierId() == null && !StringUtils.hasText(dto.getSupplierName())) { |
| | | return AjaxResult.error(prefix + "缺å°ä¾åºååç§°ï¼è¯·è¡¥å
åå确认"); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private void normalizePurchaseLedgerDateFields(Map<String, Object> target) { |
| | | normalizeDateField(target, "entryDate"); |
| | | normalizeDateField(target, "executionDate"); |
| | | normalizeDateField(target, "createdAt"); |
| | | normalizeDateField(target, "updatedAt"); |
| | | } |
| | | |
| | | private void normalizeDateField(Map<String, Object> target, String fieldName) { |
| | | Object value = target.get(fieldName); |
| | | if (value == null) { |
| | | return; |
| | | } |
| | | String normalizedDate = normalizeDateValue(value); |
| | | if (StringUtils.hasText(normalizedDate)) { |
| | | target.put(fieldName, normalizedDate); |
| | | } |
| | | } |
| | | |
| | | private String normalizeDateValue(Object value) { |
| | | if (value instanceof Date date) { |
| | | return date.toInstant().atZone(ZoneId.systemDefault()).toLocalDate().format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | } |
| | | if (value instanceof Number number) { |
| | | return LocalDate.of(1899, 12, 30) |
| | | .plusDays(number.longValue()) |
| | | .format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | } |
| | | |
| | | String text = String.valueOf(value).trim(); |
| | | if (!StringUtils.hasText(text)) { |
| | | return null; |
| | | } |
| | | if (text.length() >= 10 && text.charAt(4) == '-' && text.charAt(7) == '-') { |
| | | return text.substring(0, 10); |
| | | } |
| | | |
| | | String normalizedText = text.replace("å¹´", "-") |
| | | .replace("æ", "-") |
| | | .replace("æ¥", "") |
| | | .replace(".", "-") |
| | | .replace("/", "-") |
| | | .trim(); |
| | | DateTimeFormatter[] formatters = { |
| | | DateTimeFormatter.ofPattern("yyyy-M-d"), |
| | | DateTimeFormatter.ofPattern("M-d-yyyy"), |
| | | DateTimeFormatter.ofPattern("M-d-yy") |
| | | }; |
| | | for (DateTimeFormatter formatter : formatters) { |
| | | try { |
| | | return LocalDate.parse(normalizedText, formatter).format(DateTimeFormatter.ISO_LOCAL_DATE); |
| | | } catch (DateTimeParseException ignored) { |
| | | // Try the next supported input pattern. |
| | | } |
| | | } |
| | | return text; |
| | | } |
| | | |
| | | private void copyPurchaseLedgerDtoFields(Map<String, Object> source, Map<String, Object> target) { |
| | | String[] dtoFields = { |
| | | "entryDateStart", "entryDateEnd", "id", "purchaseContractNumber", |
| | | "supplierId", "supplierName", "isWhite", "recorderId", "recorderName", "salesContractNo", |
| | | "salesContractNoId", "projectName", "entryDate", "executionDate", "remarks", "attachmentMaterials", |
| | | "createdAt", "updatedAt", "salesLedgerId", "hasChildren", "Type", "productData", "tempFileIds", |
| | | "SalesLedgerFiles", "phoneNumber", "businessPersonId", "productId", "productModelId", "invoiceNumber", |
| | | "invoiceAmount", "ticketRegistrationId", "contractAmount", "receiptPaymentAmount", |
| | | "unReceiptPaymentAmount", "type", "paymentMethod", "approvalStatus", "templateName" |
| | | }; |
| | | for (String field : dtoFields) { |
| | | if (source.containsKey(field)) { |
| | | target.put(field, source.get(field)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void putDtoFieldIfPresent(Map<String, Object> source, Map<String, Object> target, String dtoField, String... aliases) { |
| | | if (target.containsKey(dtoField) && target.get(dtoField) != null) { |
| | | return; |
| | | } |
| | | for (String alias : aliases) { |
| | | Object value = source.get(alias); |
| | | if (value != null && StringUtils.hasText(String.valueOf(value))) { |
| | | target.put(dtoField, value); |
| | | return; |
| | | } |
| | | } |
| | | } |
| | | |
| | | private List<Map<String, Object>> toMapList(Object value) { |
| | | if (value == null) { |
| | | return List.of(); |
| | | } |
| | | return objectMapper.convertValue(value, new TypeReference<List<Map<String, Object>>>() { |
| | | }); |
| | | } |
| | | |
| | | private String stringValue(Map<String, Object> map, String... keys) { |
| | | for (String key : keys) { |
| | | Object value = map.get(key); |
| | | if (value != null && StringUtils.hasText(String.valueOf(value))) { |
| | | return String.valueOf(value); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private Long longValue(Map<String, Object> map, String... keys) { |
| | | String value = stringValue(map, keys); |
| | | if (!StringUtils.hasText(value)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return Long.parseLong(value.trim()); |
| | | } catch (NumberFormatException ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private BigDecimal decimalValue(Object value) { |
| | | if (value == null) { |
| | | return null; |
| | | } |
| | | if (value instanceof BigDecimal decimal) { |
| | | return decimal; |
| | | } |
| | | if (value instanceof Number number) { |
| | | return new BigDecimal(String.valueOf(number)); |
| | | } |
| | | String text = String.valueOf(value) |
| | | .replace(",", "") |
| | | .replace("ï¼", "") |
| | | .replace("å
", "") |
| | | .replace("ï¿¥", "") |
| | | .trim(); |
| | | if (!StringUtils.hasText(text)) { |
| | | return null; |
| | | } |
| | | try { |
| | | return new BigDecimal(text); |
| | | } catch (NumberFormatException ignored) { |
| | | return null; |
| | | } |
| | | } |
| | | |
| | | private String toCustomerMessage(Exception ex) { |
| | | String message = ex.getMessage(); |
| | | if (!StringUtils.hasText(message)) { |
| | | return "å¤ç失败ï¼è¯·æ£æ¥ç¡®è®¤æ°æ®åéè¯"; |
| | | } |
| | | if (message.contains("tax_inclusive_unit_price")) { |
| | | return "å¤ç失败ï¼äº§åæç»ç¼ºå°å«ç¨åä»·ï¼è¯·è¡¥å
åå确认"; |
| | | } |
| | | if (message.contains("tax_inclusive_total_price")) { |
| | | return "å¤ç失败ï¼äº§åæç»ç¼ºå°å«ç¨æ»ä»·ï¼è¯·è¡¥å
åå确认"; |
| | | } |
| | | if (message.contains("entryDate")) { |
| | | return "å¤ç失败ï¼å½å
¥æ¥ææ ¼å¼ä¸æ£ç¡®ï¼è¯·ä½¿ç¨ yyyy-MM-ddï¼ä¾å¦ 2026-04-30"; |
| | | } |
| | | if (message.contains("supplier")) { |
| | | return "å¤ç失败ï¼ä¾åºåä¿¡æ¯ä¸å®æ´ï¼è¯·ç¡®è®¤ä¾åºååç§°æä¾åºåID"; |
| | | } |
| | | if (message.contains("SQL") || message.contains("java.") || message.contains("Exception")) { |
| | | return "å¤ç失败ï¼ç¡®è®¤æ°æ®ä¸å®æ´ææ ¼å¼ä¸æ£ç¡®ï¼è¯·æ£æ¥å¿
å¡«åæ®µåéè¯"; |
| | | } |
| | | return "å¤ç失败ï¼" + message; |
| | | } |
| | | |
| | | private AjaxResult fillSupplierIdByName(PurchaseLedgerDto dto) { |
| | | if (dto.getSupplierId() != null) { |
| | | return null; |
| | | } |
| | | if (!StringUtils.hasText(dto.getSupplierName())) { |
| | | return AjaxResult.error("ä¾åºåIDä¸è½ä¸ºç©ºï¼æªè¯å«å°ä¾åºååç§°ï¼æ æ³èªå¨å¹é
ä¾åºåID"); |
| | | } |
| | | |
| | | SupplierManage supplier = supplierManageMapper.selectOne(new LambdaQueryWrapper<SupplierManage>() |
| | | .eq(SupplierManage::getSupplierName, dto.getSupplierName().trim()) |
| | | .last("limit 1")); |
| | | if (supplier == null) { |
| | | return AjaxResult.error("æªæ¾å°ä¾åºåï¼" + dto.getSupplierName() + "ï¼è¯·å
ç»´æ¤ä¾åºåææå¨éæ©ä¾åºåID"); |
| | | } |
| | | dto.setSupplierId(supplier.getId()); |
| | | return null; |
| | | } |
| | | |
| | | private AjaxResult processPaymentRegistration(Map<String, Object> payload) { |
| | | Object recordsValue = payload.get("records"); |
| | | List<PaymentRegistration> records; |
| | | if (recordsValue == null) { |
| | | records = Collections.singletonList(objectMapper.convertValue(payload, PaymentRegistration.class)); |
| | | } else { |
| | | records = objectMapper.convertValue(recordsValue, new TypeReference<List<PaymentRegistration>>() { |
| | | }); |
| | | } |
| | | int result = paymentRegistrationService.insertPaymentRegistration(records); |
| | | return AjaxResult.success("仿¬¾ç»è®°å·²å¤ç", result); |
| | | } |
| | | |
| | | private AjaxResult processPurchaseReturnOrder(Map<String, Object> payload) { |
| | | PurchaseReturnOrderDto dto = objectMapper.convertValue(payload, PurchaseReturnOrderDto.class); |
| | | Boolean result = purchaseReturnOrdersService.add(dto); |
| | | return AjaxResult.success("éè´éè´§åå·²å¤ç", result); |
| | | } |
| | | } |
| | |
| | | return new LinkedList<>(); |
| | | } |
| | | List<ChatMessage> messages = mongoChatMemoryStore.getMessages(memoryId); |
| | | return messages.stream().map(this::convertMessage).collect(Collectors.toList()); |
| | | List<AiChatMessageDto> messageDtos = messages.stream().map(this::convertMessage).collect(Collectors.toList()); |
| | | List<List<String>> analyzeFilePathGroups = mongoChatMemoryStore.getAnalyzeFilePathGroups(memoryId); |
| | | attachAnalyzeFilePaths(messageDtos, analyzeFilePathGroups); |
| | | return messageDtos; |
| | | } |
| | | |
| | | @Override |
| | |
| | | } |
| | | return new AiChatMessageDto("unknown", String.valueOf(message)); |
| | | } |
| | | |
| | | private void attachAnalyzeFilePaths(List<AiChatMessageDto> messages, |
| | | List<List<String>> analyzeFilePathGroups) { |
| | | if (StringUtils.isEmpty(messages) || StringUtils.isEmpty(analyzeFilePathGroups)) { |
| | | return; |
| | | } |
| | | int analyzeIndex = 0; |
| | | for (AiChatMessageDto message : messages) { |
| | | if (!"user".equals(message.getRole()) || analyzeIndex >= analyzeFilePathGroups.size()) { |
| | | continue; |
| | | } |
| | | List<String> filePaths = analyzeFilePathGroups.get(analyzeIndex); |
| | | if (!StringUtils.isEmpty(filePaths)) { |
| | | message.setFilePaths(filePaths); |
| | | } |
| | | analyzeIndex++; |
| | | } |
| | | } |
| | | } |
| | |
| | | import org.springframework.data.mongodb.core.query.Query; |
| | | import org.springframework.data.mongodb.core.query.Update; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.StringUtils; |
| | | |
| | | import java.util.Date; |
| | | import java.util.LinkedList; |
| | |
| | | |
| | | @Override |
| | | public List<ChatMessage> getMessages(Object memoryId) { |
| | | Query query = Query.query(Criteria.where("memoryId").is(memoryIdString(memoryId))); |
| | | ChatMessages chatMessages = mongoTemplate.findOne(query, ChatMessages.class); |
| | | ChatMessages chatMessages = findChatMessages(memoryId); |
| | | if (chatMessages == null || chatMessages.getContent() == null) { |
| | | return new LinkedList<>(); |
| | | } |
| | |
| | | updateMessages(memoryId, messages); |
| | | } |
| | | |
| | | public void appendAnalyzeFileContext(Object memoryId, String userQuestion, List<String> filePaths) { |
| | | String memoryIdValue = memoryIdString(memoryId); |
| | | if (!StringUtils.hasText(memoryIdValue)) { |
| | | return; |
| | | } |
| | | List<String> validFilePaths = new LinkedList<>(); |
| | | if (!CollectionUtils.isEmpty(filePaths)) { |
| | | for (String filePath : filePaths) { |
| | | if (StringUtils.hasText(filePath)) { |
| | | validFilePaths.add(filePath); |
| | | } |
| | | } |
| | | } |
| | | if (!StringUtils.hasText(userQuestion) && validFilePaths.isEmpty()) { |
| | | return; |
| | | } |
| | | Query query = Query.query(Criteria.where("memoryId").is(memoryIdValue)); |
| | | Update update = new Update(); |
| | | update.set("memoryId", memoryIdValue); |
| | | update.set("updateTime", new Date()); |
| | | update.setOnInsert("createTime", new Date()); |
| | | if (StringUtils.hasText(userQuestion)) { |
| | | update.push("analyzeUserQuestions", userQuestion); |
| | | } |
| | | if (!validFilePaths.isEmpty()) { |
| | | update.push("analyzeFilePaths").each(validFilePaths.toArray()); |
| | | update.push("analyzeFilePathGroups", validFilePaths); |
| | | } |
| | | mongoTemplate.upsert(query, update, ChatMessages.class); |
| | | } |
| | | |
| | | public List<String> getAnalyzeUserQuestions(Object memoryId) { |
| | | ChatMessages chatMessages = findChatMessages(memoryId); |
| | | if (chatMessages == null || CollectionUtils.isEmpty(chatMessages.getAnalyzeUserQuestions())) { |
| | | return new LinkedList<>(); |
| | | } |
| | | return new LinkedList<>(chatMessages.getAnalyzeUserQuestions()); |
| | | } |
| | | |
| | | public List<List<String>> getAnalyzeFilePathGroups(Object memoryId) { |
| | | ChatMessages chatMessages = findChatMessages(memoryId); |
| | | if (chatMessages == null) { |
| | | return new LinkedList<>(); |
| | | } |
| | | if (CollectionUtils.isEmpty(chatMessages.getAnalyzeFilePathGroups())) { |
| | | if (CollectionUtils.isEmpty(chatMessages.getAnalyzeFilePaths())) { |
| | | return new LinkedList<>(); |
| | | } |
| | | List<List<String>> fallback = new LinkedList<>(); |
| | | fallback.add(new LinkedList<>(chatMessages.getAnalyzeFilePaths())); |
| | | return fallback; |
| | | } |
| | | List<List<String>> groups = new LinkedList<>(); |
| | | for (List<String> group : chatMessages.getAnalyzeFilePathGroups()) { |
| | | if (CollectionUtils.isEmpty(group)) { |
| | | groups.add(new LinkedList<>()); |
| | | } else { |
| | | groups.add(new LinkedList<>(group)); |
| | | } |
| | | } |
| | | return groups; |
| | | } |
| | | |
| | | private String memoryIdString(Object memoryId) { |
| | | return memoryId == null ? "" : memoryId.toString(); |
| | | } |
| | | |
| | | private ChatMessages findChatMessages(Object memoryId) { |
| | | Query query = Query.query(Criteria.where("memoryId").is(memoryIdString(memoryId))); |
| | | return mongoTemplate.findOne(query, ChatMessages.class); |
| | | } |
| | | } |
| | |
| | | addQualityInspect(purchaseLedger, salesLedgerProduct); |
| | | } else { |
| | | //ç´æ¥å
¥åº |
| | | stockUtils.addStock(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), purchaseLedger.getId()); |
| | | stockUtils.addStockWithBatchNo(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), purchaseLedger.getId(),purchaseLedger.getPurchaseContractNumber()+"-"+salesLedgerProduct.getId()); |
| | | } |
| | | } |
| | | } else if (status.equals(3)) { |
| | |
| | | } |
| | | salesQuotationMapper.updateById(salesQuote); |
| | | } |
| | | // åºåºå®¡æ¹ä¿®æ¹ |
| | | // åºåºå®¡æ¹ä¿®æ¹=åè´§å®¡æ¹ |
| | | if (approveProcess.getApproveType().equals(7)) { |
| | | String[] split = approveProcess.getApproveReason().split(":"); |
| | | ShippingInfo shippingInfo = shippingInfoMapper.selectOne(new LambdaQueryWrapper<ShippingInfo>() |
| | | .eq(ShippingInfo::getShippingNo, split[1]) |
| | | .eq(ShippingInfo::getShippingNo, approveProcess.getApproveReason()) |
| | | .orderByDesc(ShippingInfo::getCreateTime) |
| | | .last("limit 1")); |
| | | if (shippingInfo != null) { |
| | |
| | | } |
| | | shippingInfoMapper.updateById(shippingInfo); |
| | | } |
| | | //åºåæ£å |
| | | |
| | | } |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.APPROVE_NODE, approveNode.getId(), approveNode.getStorageBlobDTOS()); |
| | |
| | | package com.ruoyi.approve.service.impl; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.common.enums.FileNameType; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.utils.OrderUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.project.system.domain.SysDept; |
| | | import com.ruoyi.project.system.domain.SysNotice; |
| | | import com.ruoyi.project.system.domain.SysUser; |
| | |
| | | import com.ruoyi.purchase.mapper.PurchaseLedgerMapper; |
| | | import com.ruoyi.purchase.pojo.PurchaseLedger; |
| | | import com.ruoyi.sales.mapper.CommonFileMapper; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.sales.mapper.ShippingInfoMapper; |
| | | import com.ruoyi.sales.pojo.CommonFile; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.sales.pojo.ShippingInfo; |
| | | import com.ruoyi.sales.service.impl.CommonFileServiceImpl; |
| | | import lombok.RequiredArgsConstructor; |
| | |
| | | private final CommonFileServiceImpl commonFileService; |
| | | private final ISysNoticeService sysNoticeService; |
| | | private final PurchaseLedgerMapper purchaseLedgerMapper; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private final StockUtils stockUtils; |
| | | private final ShippingInfoMapper shippingInfoMapper; |
| | | private final ApproveNodeMapper approveNodeMapper; |
| | | private final ApproveProcessConfigNodeService approveProcessConfigNodeService; |
| | |
| | | .map(ApproveProcessConfigNodeVo::getApproverId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toList()); |
| | | if (list.isEmpty()) { |
| | | throw new RuntimeException("æµç¨ä¸åå¨"); |
| | | } |
| | | // æ å®¡æ ¸äººé»è¾æ·»å |
| | | if (CollectionUtils.isEmpty(nodeIds)) { |
| | | autoPassPurchaseApproveIfNoApprover(approveProcessVO); |
| | | autoPassPurchaseApproveIfNoApprover(approveProcessVO); // éè´åæ å®¡æ ¸äººé»è¾ |
| | | return; |
| | | } |
| | | List<SysUser> sysUsers = sysUserMapper.selectUserByIds(nodeIds); |
| | | if (CollectionUtils.isEmpty(sysUsers)) throw new RuntimeException("å®¡æ ¸ç¨æ·ä¸åå¨"); |
| | | if (sysDept == null) throw new RuntimeException("é¨é¨ä¸åå¨"); |
| | | if (sysUser == null) throw new RuntimeException("ç³è¯·äººä¸åå¨"); |
| | | // String today = LocalDate.now().format(DATE_FORMAT); |
| | | // Long approveId = dailyRedisCounter.incrementAndGetByDb(); |
| | | // String formattedCount = String.format("%03d", approveId); |
| | | // //æµç¨ ID |
| | | // String approveID = today + formattedCount; |
| | | SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); |
| | | ApproveProcess approveProcess = new ApproveProcess(); |
| | | String no = OrderUtils.countTodayByCreateTime(approveProcessMapper, "", "approve_id"); |
| | |
| | | || !StringUtils.hasText(approveProcessVO.getApproveReason())) { |
| | | throw new RuntimeException("å®¡æ ¸ç¨æ·ä¸åå¨"); |
| | | } |
| | | |
| | | purchaseLedgerMapper.update(null, new LambdaUpdateWrapper<PurchaseLedger>() |
| | | .eq(PurchaseLedger::getPurchaseContractNumber, approveProcessVO.getApproveReason()) |
| | | .set(PurchaseLedger::getApprovalStatus, 3)); |
| | | //éè´å
¥åº |
| | | PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectOne(new LambdaQueryWrapper<PurchaseLedger>() |
| | | .eq(PurchaseLedger::getPurchaseContractNumber, approveProcessVO.getApproveReason()) |
| | | .last("limit 1")); |
| | | List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectList(new QueryWrapper<SalesLedgerProduct>() |
| | | .lambda().eq(SalesLedgerProduct::getSalesLedgerId, purchaseLedger.getId()).eq(SalesLedgerProduct::getType, 2)); |
| | | for (SalesLedgerProduct salesLedgerProduct : salesLedgerProducts) { |
| | | stockUtils.addStockWithBatchNo(salesLedgerProduct.getProductModelId(), salesLedgerProduct.getQuantity(), StockInQualifiedRecordTypeEnum.PURCHASE_STOCK_IN.getCode(), purchaseLedger.getId(),purchaseLedger.getPurchaseContractNumber()+"-"+salesLedgerProduct.getId()); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | |
| | | @Data |
| | | public class ProductModelExportDto { |
| | | |
| | | @Excel(name = "产åç¼ç ") |
| | | private String productCode; |
| | | |
| | | @Excel(name = "è§æ ¼åå·") |
| | | private String model; |
| | | |
| | |
| | | ProductModel item = productModelList.get(i); |
| | | int rowNum = i + 2; |
| | | |
| | | if (StringUtils.isEmpty(item.getProductCode())) { |
| | | return AjaxResult.error("第 " + rowNum + " è¡å¯¼å
¥å¤±è´¥: [产åç¼ç ] ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (StringUtils.isEmpty(item.getModel())) { |
| | | return AjaxResult.error("第 " + rowNum + " è¡å¯¼å
¥å¤±è´¥: [è§æ ¼åå·] ä¸è½ä¸ºç©º"); |
| | | } |
| | |
| | | SALE_SHIP_STOCK_OUT("13", "éå®-åè´§åºåº"), |
| | | RETURN_HE_IN("14", "éå®éè´§-åæ ¼å
¥åº"), |
| | | RETURN_UNSTOCK_IN("15", "éå®éè´§-ä¸åæ ¼å
¥åº"), |
| | | PICK_RETURN_IN("20", "éå®éè´§-åæ ¼å
¥åº"), |
| | | PURCHASE_RETURN_STOCK_OUT("21", "éè´éè´§"); |
| | | PICK_RETURN_IN("20", "颿éæ-åæ ¼å
¥åº"), |
| | | PURCHASE_RETURN_STOCK_OUT("21", "éè´éè´§"), |
| | | FEED_RETURN_IN("22", "ç产éæ-åæ ¼å
¥åº"); |
| | | |
| | | |
| | | |
| | |
| | | PRODUCTION_REPORT_STOCK_OUT("3", "ç产æ¥å·¥-åºåº"), |
| | | SALE_STOCK_OUT("8", "éå®-åºåº"), |
| | | PURCHASE_RETURN_STOCK_OUT("9", "éè´éè´§"), |
| | | SALE_SHIP_STOCK_OUT("13", "éå®-åè´§åºåº"); |
| | | SALE_SHIP_STOCK_OUT("13", "éå®-åè´§åºåº"), |
| | | PICK_STOCK_OUT("14", "çäº§é¢æåºåº"), |
| | | FEED_STOCK_OUT("15", "ç产补æåºåº"); |
| | | |
| | | private final String code; |
| | | private final String value; |
| | |
| | | val = DateUtil.getJavaDate((Double) val);
|
| | | }
|
| | | }
|
| | | else if (LocalDate.class == fieldType) |
| | | { |
| | | if (val instanceof String) |
| | | { |
| | | Date date = DateUtils.parseDate(val); |
| | | val = StringUtils.isNull(date) ? null : DateUtils.toLocalDate(date); |
| | | } |
| | | else if (val instanceof Date) |
| | | { |
| | | val = DateUtils.toLocalDate((Date) val); |
| | | } |
| | | else if (val instanceof Double) |
| | | { |
| | | val = DateUtils.toLocalDate(DateUtil.getJavaDate((Double) val)); |
| | | } |
| | | } |
| | | else if (Boolean.TYPE == fieldType || Boolean.class == fieldType)
|
| | | {
|
| | | val = Convert.toBool(val, false);
|
| | |
| | | } else if (val instanceof Double) {
|
| | | val = DateUtil.getJavaDate((Double) val);
|
| | | }
|
| | | } else if (LocalDate.class == fieldType) { |
| | | if (val instanceof String) { |
| | | Date date = DateUtils.parseDate(val); |
| | | val = StringUtils.isNull(date) ? null : DateUtils.toLocalDate(date); |
| | | } else if (val instanceof Date) { |
| | | val = DateUtils.toLocalDate((Date) val); |
| | | } else if (val instanceof Double) { |
| | | val = DateUtils.toLocalDate(DateUtil.getJavaDate((Double) val)); |
| | | } |
| | | } else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) {
|
| | | val = Convert.toBool(val, false);
|
| | | }
|
| | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.dto.DeviceTypeDetail; |
| | | import com.ruoyi.account.dto.DeviceTypeDistributionVO; |
| | | import com.ruoyi.account.bean.dto.DeviceTypeDetail; |
| | | import com.ruoyi.account.bean.dto.DeviceTypeDistributionVO; |
| | | import com.ruoyi.device.dto.DeviceLedgerDto; |
| | | import com.ruoyi.device.execl.DeviceLedgerExeclDto; |
| | | import com.ruoyi.device.pojo.DeviceLedger; |
| | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.github.xiaoymin.knife4j.core.util.CollectionUtils; |
| | | import com.ruoyi.basic.enums.ApplicationTypeEnum; |
| | | import com.ruoyi.basic.enums.RecordTypeEnum; |
| | | import com.ruoyi.basic.utils.FileUtil; |
| | | import com.ruoyi.common.utils.bean.BeanUtils; |
| | |
| | | }); |
| | | } |
| | | // å¤çå¾çä¸ä¼ |
| | | if (deviceRepairDto.getStorageBlobDTOs() != null) { |
| | | fileUtil.saveStorageAttachmentByRecordTypeAndRecordId("file", RecordTypeEnum.DEVICE_REPAIR, id, deviceRepairDto.getStorageBlobDTOs()); |
| | | } |
| | | return AjaxResult.success(); |
| | | } |
| | | return AjaxResult.error(); |
| | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.bean.dto.SalesReturnDto; |
| | | import com.ruoyi.account.bean.vo.SalesReturnVo; |
| | | import com.ruoyi.procurementrecord.dto.ReturnManagementDto; |
| | | import com.ruoyi.procurementrecord.pojo.ReturnManagement; |
| | | import org.apache.ibatis.annotations.Param; |
| | |
| | | IPage<ReturnManagementDto> listPage(Page page, @Param("req") ReturnManagementDto returnManagement); |
| | | |
| | | ReturnManagementDto getReturnManagementDtoById(Long id); |
| | | |
| | | IPage<SalesReturnVo> listPageBySalesReturn(Page page, @Param("req") SalesReturnDto salesReturnDto); |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.account.dto.SalesRefundAmountOrderDto; |
| | | import com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto; |
| | | import com.ruoyi.account.mapper.AccountExpenseMapper; |
| | | import com.ruoyi.account.pojo.AccountExpense; |
| | | import com.ruoyi.account.service.SalesRefundAmountOrderService; |
| | |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.util.ObjectUtils; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | |
| | | |
| | | @Override |
| | | public boolean addReturnManagementDto(ReturnManagementDto returnManagementDto) { |
| | | if (ObjectUtils.isEmpty(returnManagementDto.getReturnNo())){ |
| | | String rt = OrderUtils.countTodayByCreateTime(returnManagementMapper, "RT","return_no"); |
| | | returnManagementDto.setReturnNo(rt); |
| | | } |
| | | save(returnManagementDto); |
| | | for (ReturnSaleProduct returnSaleProduct : returnManagementDto.getReturnSaleProducts()) { |
| | | returnSaleProduct.setReturnManagementId(returnManagementDto.getId()); |
| | |
| | | } |
| | | |
| | | /** |
| | | * åæ ¼å
¥åºå¸¦æ¹æ¬¡å· |
| | | * @param productModelId |
| | | * @param quantity |
| | | * @param recordType |
| | | * @param recordId |
| | | */ |
| | | public void addStockWithBatchNo(Long productModelId, BigDecimal quantity, String recordType, Long recordId, String batchNo) { |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordId(recordId); |
| | | stockInventoryDto.setRecordType(String.valueOf(recordType)); |
| | | stockInventoryDto.setQualitity(quantity); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | } |
| | | |
| | | /** |
| | | * åæ ¼åºåº |
| | | * |
| | | * @param productModelId |
| | |
| | | import java.time.LocalDate; |
| | | |
| | | @Data |
| | | @Schema(name = "ProductionAccountDto", description = "production account query dto") |
| | | @Schema(name = "ProductionAccountDto", description = "çäº§æ ¸ç®æ¥è¯¢åæ°") |
| | | public class ProductionAccountDto extends ProductionAccount { |
| | | |
| | | @Schema(description = "sales contract no") |
| | | @Schema(description = "éå®ååå·") |
| | | private String salesContractNo; |
| | | |
| | | @Schema(description = "customer contract no") |
| | | @Schema(description = "客æ·ååå·") |
| | | private String customerContractNo; |
| | | |
| | | @Schema(description = "project name") |
| | | @Schema(description = "项ç®åç§°") |
| | | private String projectName; |
| | | |
| | | @Schema(description = "customer name") |
| | | @Schema(description = "客æ·åç§°") |
| | | private String customerName; |
| | | |
| | | @Schema(description = "product category") |
| | | @Schema(description = "产åç±»å«") |
| | | private String productCategory; |
| | | |
| | | @Schema(description = "specification model") |
| | | @Schema(description = "è§æ ¼åå·") |
| | | private String specificationModel; |
| | | |
| | | @Schema(description = "scheduling user id") |
| | | @Schema(description = "æäº§äººåID") |
| | | private Long schedulingUserId; |
| | | |
| | | @Schema(description = "scheduling user name") |
| | | @Schema(description = "æäº§äººååç§°") |
| | | private String schedulingUserName; |
| | | |
| | | @Schema(description = "process") |
| | | @Schema(description = "å·¥åº") |
| | | private String process; |
| | | |
| | | @Schema(description = "date type(day/month)") |
| | | @Schema(description = "æ¥æç±»åï¼æå¤©/ææï¼") |
| | | private String dateType; |
| | | |
| | | @Schema(description = "day query date") |
| | | @Schema(description = "æå¤©æ¥è¯¢æ¥æ") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate entryDate; |
| | | |
| | | @Schema(description = "date range") |
| | | @Schema(description = "æ¥æèå´") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate[] dateRange; |
| | | |
| | | @Schema(description = "start date") |
| | | @Schema(description = "å¼å§æ¥æ") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate entryDateStart; |
| | | |
| | | @Schema(description = "end date") |
| | | @Schema(description = "ç»ææ¥æ") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate entryDateEnd; |
| | |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate requiredDateEnd; |
| | | |
| | | @Schema(description = "éå®ååå·") |
| | | private String salesContractNo; |
| | | |
| | | } |
| | |
| | | |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @Data |
| | | @Schema(name = "ProductionProductMainDto", description = "production report query dto") |
| | | @Schema(name = "ProductionProductMainDto", description = "ç产æ¥å·¥æ¥è¯¢åæ°") |
| | | public class ProductionProductMainDto extends ProductionProductMain { |
| | | |
| | | @Schema(description = "product process route item id") |
| | | @Schema(description = "产åå·¥èºè·¯çº¿å·¥åºID") |
| | | private Long productProcessRouteItemId; |
| | | |
| | | @Schema(description = "production report id") |
| | | @Schema(description = "æ¥å·¥ID") |
| | | private Long productMainId; |
| | | |
| | | @Schema(description = "tenant id") |
| | | @Schema(description = "ç§æ·ID") |
| | | private Long tenantId; |
| | | |
| | | @Schema(description = "work order no") |
| | | @Schema(description = "å·¥åç¼å·") |
| | | private String workOrderNo; |
| | | |
| | | @Schema(description = "work order status") |
| | | @Schema(description = "å·¥åç¶æ") |
| | | private String workOrderStatus; |
| | | |
| | | @Schema(description = "nick name") |
| | | @Schema(description = "æµç§°") |
| | | private String nickName; |
| | | |
| | | @Schema(description = "quantity") |
| | | @Schema(description = "æ°é") |
| | | private BigDecimal quantity; |
| | | |
| | | @Schema(description = "scrap quantity") |
| | | @Schema(description = "æ¥åºæ°é") |
| | | private BigDecimal scrapQty; |
| | | |
| | | @Schema(description = "product name") |
| | | @Schema(description = "产ååç§°") |
| | | private String productName; |
| | | |
| | | @Schema(description = "product model name") |
| | | @Schema(description = "产åè§æ ¼åå·") |
| | | private String productModelName; |
| | | |
| | | @Schema(description = "unit") |
| | | @Schema(description = "åä½") |
| | | private String unit; |
| | | |
| | | @Schema(description = "sales contract no") |
| | | @Schema(description = "éå®ååå·") |
| | | private String salesContractNo; |
| | | |
| | | @Schema(description = "scheduling date") |
| | | @Schema(description = "æäº§æ¥æ") |
| | | private LocalDate schedulingDate; |
| | | |
| | | @Schema(description = "scheduling user name") |
| | | @Schema(description = "æäº§äººååç§°") |
| | | private String schedulingUserName; |
| | | |
| | | @Schema(description = "customer name") |
| | | @Schema(description = "客æ·åç§°") |
| | | private String customerName; |
| | | |
| | | @Schema(description = "process") |
| | | @Schema(description = "å·¥åº") |
| | | private String process; |
| | | |
| | | @Schema(description = "salary quota") |
| | | @Schema(description = "å·¥èµå®é¢") |
| | | private BigDecimal workHours; |
| | | |
| | | @Schema(description = "wages") |
| | | @Schema(description = "å·¥èµ") |
| | | private BigDecimal wages; |
| | | |
| | | @Schema(description = "operation param list") |
| | | @Schema(description = "å·¥åºåæ°å表") |
| | | private List<ProductionOrderRoutingOperationParam> productionOperationParamList; |
| | | } |
| | |
| | | import java.time.LocalDate; |
| | | |
| | | @Data |
| | | @Schema(name = "ProductionAccountVo", description = "production account page result") |
| | | @Schema(name = "ProductionAccountVo", description = "çäº§æ ¸ç®åé¡µç»æ") |
| | | public class ProductionAccountVo { |
| | | |
| | | @Schema(description = "customer contract no") |
| | | @Schema(description = "客æ·ååå·") |
| | | private String customerContractNo; |
| | | |
| | | @Schema(description = "project name") |
| | | @Schema(description = "项ç®åç§°") |
| | | private String projectName; |
| | | |
| | | @Schema(description = "customer name") |
| | | @Schema(description = "客æ·åç§°") |
| | | private String customerName; |
| | | |
| | | @Schema(description = "product category") |
| | | @Schema(description = "产åç±»å«") |
| | | private String productCategory; |
| | | |
| | | @Schema(description = "specification model") |
| | | @Schema(description = "è§æ ¼åå·") |
| | | private String specificationModel; |
| | | |
| | | @Schema(description = "unit") |
| | | @Schema(description = "åä½") |
| | | private String unit; |
| | | |
| | | @Schema(description = "scheduling user id") |
| | | @Schema(description = "æäº§äººåID") |
| | | private Long schedulingUserId; |
| | | |
| | | @Schema(description = "scheduling user name") |
| | | @Schema(description = "æäº§äººååç§°") |
| | | private String schedulingUserName; |
| | | |
| | | @Schema(description = "wages") |
| | | @Schema(description = "å·¥èµ") |
| | | private BigDecimal wages; |
| | | |
| | | @Schema(description = "finished quantity") |
| | | @Schema(description = "宿æ°é") |
| | | private BigDecimal finishedNum; |
| | | |
| | | @Schema(description = "salary quota") |
| | | @Schema(description = "å·¥èµå®é¢") |
| | | private BigDecimal workHours; |
| | | |
| | | @Schema(description = "output rate") |
| | | @Schema(description = "å·¥æ¶") |
| | | private BigDecimal workHour; |
| | | |
| | | @Schema(description = "产åºç") |
| | | private String outputRate; |
| | | |
| | | @Schema(description = "process") |
| | | @Schema(description = "å·¥åº") |
| | | private String process; |
| | | |
| | | @Schema(description = "scheduling date") |
| | | @Schema(description = "æäº§æ¥æ") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | private LocalDate schedulingDate; |
| | | |
| | | @Schema(description = "scheduling month(yyyy-MM)") |
| | | @Schema(description = "æäº§æä»½(yyyy-MM)") |
| | | private String schedulingMonth; |
| | | } |
| | |
| | | |
| | | @Schema(description = "æ¯å¦ç»æï¼") |
| | | private Boolean endOrder; |
| | | |
| | | @Schema(description = "ç±»å åºå计æ¶å计件(0计æ¶1计件)") |
| | | private Integer type; |
| | | } |
| | |
| | | package com.ruoyi.production.bean.vo; |
| | | |
| | | import com.ruoyi.basic.dto.StorageBlobVO; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Excel; |
| | | import com.ruoyi.production.pojo.ProductionOrder; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | |
| | | private String customerName; |
| | | |
| | | @Schema(description = "产ååç§°") |
| | | @Excel(name = "产ååç§°",sort = 2) |
| | | private String productName; |
| | | |
| | | @Schema(description = "è§æ ¼åå·") |
| | | @Excel(name = "è§æ ¼",sort = 3) |
| | | private String model; |
| | | |
| | | @Schema(description = "å·¥èºè·¯çº¿ç¼ç ") |
| | | @Excel(name = "å·¥èºè·¯çº¿ç¼å·",sort = 4) |
| | | private String processRouteCode; |
| | | |
| | | @Schema(description = "产åå¾ç") |
| | |
| | | private String bomNo; |
| | | |
| | | @Schema(description = "宿è¿åº¦") |
| | | @Excel(name = "宿è¿åº¦",sort = 7) |
| | | private BigDecimal completionStatus; |
| | | |
| | | @Schema(description = "æ¯å¦å·²éæ") |
| | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | |
| | | @Data |
| | |
| | | @Schema(description = "æ¥å·¥ä¸»ä¿¡æ¯") |
| | | private ProductionProductMain reportMain; |
| | | |
| | | @Schema(description = "å·¥æ¶") |
| | | private BigDecimal workHour; |
| | | |
| | | @Schema(description = "æ¥å·¥äº§åºæç»") |
| | | private List<ProductionProductOutput> reportOutputList; |
| | | |
| | |
| | | @Schema(description = "æ¥å·¥ä¸»ä¿¡æ¯") |
| | | private ProductionProductMain reportMain; |
| | | |
| | | @Schema(description = "å·¥æ¶") |
| | | private BigDecimal workHour; |
| | | |
| | | @Schema(description = "è´¨æ£ä¸»ä¿¡æ¯") |
| | | private QualityInspect inspect; |
| | | |
| | |
| | | package com.ruoyi.production.controller; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.common.utils.poi.ExcelUtil; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Log; |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.domain.R; |
| | | import com.ruoyi.production.bean.dto.ProductionOrderDto; |
| | | import com.ruoyi.production.bean.vo.ProductionOrderPickVo; |
| | |
| | | import com.ruoyi.production.bean.vo.ProductionOrderWorkOrderDetailVo; |
| | | import com.ruoyi.production.pojo.ProductionOrder; |
| | | import com.ruoyi.production.service.ProductionOrderService; |
| | | import com.ruoyi.sales.dto.SalesLedgerDto; |
| | | import com.ruoyi.sales.vo.SalesLedgerVo; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.media.Content; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | |
| | | @RestController |
| | |
| | | public R updateOrder(@RequestBody ProductionOrderDto productionOrderDto) { |
| | | return R.ok(productionOrderService.updateOrder(productionOrderDto)); |
| | | } |
| | | |
| | | |
| | | @Log(title = "ç产订å导åº", businessType = BusinessType.EXPORT) |
| | | @PostMapping("/export") |
| | | public void export(HttpServletResponse response, ProductionOrderDto dto) { |
| | | IPage<ProductionOrderVo> productionOrderVoIPage = productionOrderService.pageProductionOrder(new Page<>(-1, -1), dto); |
| | | List<ProductionOrderVo> records = productionOrderVoIPage.getRecords(); |
| | | ExcelUtil<ProductionOrderVo> util = new ExcelUtil<>(ProductionOrderVo.class); |
| | | util.exportExcel(response, records, "çäº§è®¢åæ°æ®"); |
| | | } |
| | | } |
| | |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.http.MediaType; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | |
| | | excelUtil.importTemplateExcel(response, "主ç产计å导å
¥æ¨¡æ¿"); |
| | | } |
| | | |
| | | @PostMapping("/import") |
| | | @PostMapping(value = "/import", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) |
| | | @Operation(summary = "主çäº§è®¡åæ°æ®å¯¼å
¥") |
| | | @Log(title = "主çäº§è®¡åæ°æ®å¯¼å
¥", businessType = BusinessType.IMPORT) |
| | | public R importProdData(@RequestParam("file") MultipartFile file) { |
| | | public R importProdData(@RequestPart("file") MultipartFile file) { |
| | | productionPlanService.importProdData(file); |
| | | return R.ok("导å
¥æå"); |
| | | } |
| | |
| | | @PostMapping("/export") |
| | | @Operation(summary = "主çäº§è®¡åæ°æ®å¯¼åº") |
| | | @Log(title = "主çäº§è®¡åæ°æ®å¯¼åº", businessType = BusinessType.EXPORT) |
| | | public void exportProdData(HttpServletResponse response, @RequestBody(required = false) List<Long> ids) { |
| | | productionPlanService.exportProdData(response, ids); |
| | | public void exportProdData(HttpServletResponse response, @RequestBody(required = false) ProductionPlanDto requestDto) { |
| | | productionPlanService.exportProdData(response, requestDto); |
| | | } |
| | | |
| | | } |
| | |
| | | |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | import com.fasterxml.jackson.annotation.JsonFormat; |
| | | import com.ruoyi.framework.aspectj.lang.annotation.Excel; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | |
| | | private Long productModelId; |
| | | |
| | | @Schema(description = "ç产订åå·") |
| | | @Excel(name = "ç产订å",sort = 0) |
| | | private String npsNo; |
| | | |
| | | @Schema(description = "å½å
¥æ¶é´") |
| | |
| | | private Long technologyRoutingId; |
| | | |
| | | @Schema(description = "éæ±æ°éãæå¨æ°å¢æ¶å¿
å¡«ä¸å¿
é¡»å¤§äº 0ï¼å¦æä¼ äº productionPlanIdsï¼åå¯ç±ç³»ç»èªå¨å¸¦åºã") |
| | | @Excel(name = "éæ±æ°é",sort = 5) |
| | | private BigDecimal quantity; |
| | | |
| | | @Schema(description = "宿æ°é") |
| | | @Excel(name = "宿æ°é",sort = 6) |
| | | private BigDecimal completeQuantity; |
| | | |
| | | @Schema(description = "å¼å§æ¥æ") |
| | | @Excel(name = "å¼å§æ¥æ",sort = 8,dateFormat = "yyyy-MM-dd") |
| | | private LocalDateTime startTime; |
| | | |
| | | @Schema(description = "ç»ææ¥æ") |
| | | @Excel(name = "ç»ææ¥æ",sort = 9,dateFormat = "yyyy-MM-dd") |
| | | private LocalDateTime endTime; |
| | | |
| | | @Schema(description = "å建人ID") |
| | |
| | | @Schema(description = "计å宿æ¶é´") |
| | | @JsonFormat(pattern = "yyyy-MM-dd") |
| | | @DateTimeFormat(pattern = "yyyy-MM-dd") |
| | | @Excel(name = "计å宿æ¶é´",sort = 10,dateFormat = "yyyy-MM-dd") |
| | | private LocalDate planCompleteTime; |
| | | |
| | | @Schema(description = "ç¶æï¼1.å¾
å¼å§ 2.è¿è¡ä¸ 3.已宿 4.已忶 5.å·²ç»æï¼") |
| | | @Excel(name = "ç¶æ",sort = 1,readConverterExp = "1=å¾
å¼å§,2=è¿è¡ä¸,3=已宿,4=已忶,5=å·²ç»æ") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "æ¯å¦ç»æï¼") |
| | |
| | | |
| | | @Schema(description = "å·¥åºè¡¨id") |
| | | private Long technologyOperationId; |
| | | |
| | | @Schema(description = "ç±»å åºå计æ¶å计件ï¼0计æ¶ï¼1计件") |
| | | private Integer type; |
| | | } |
| | |
| | | import lombok.Data; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | @Data |
| | |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | |
| | | @Schema(description = "å·¥æ¶") |
| | | private BigDecimal workHour; |
| | | |
| | | } |
| | |
| | | /** |
| | | * å¯¼åºæ°æ® |
| | | */ |
| | | void exportProdData(HttpServletResponse response, List<Long> ids); |
| | | void exportProdData(HttpServletResponse response, ProductionPlanDto requestDto); |
| | | |
| | | } |
| | |
| | | |
| | | @Override |
| | | public IPage<ProductionAccountVo> listPage(Page<ProductionAccountDto> page, ProductionAccountDto dto) { |
| | | // å页æ¥è¯¢çäº§æ ¸ç®æ°æ® |
| | | ProductionAccountDto queryDto = normalizeDateQuery(dto); |
| | | return baseMapper.listPage(page, queryDto); |
| | | } |
| | | |
| | | @Override |
| | | public IPage<ProductionProductMainDto> listProductionDetails(ProductionAccountDto dto, Page page) { |
| | | // æ¥è¯¢çäº§æ ¸ç®æç» |
| | | return productionProductMainMapper.listProductionDetails(normalizeDateQuery(dto), page); |
| | | } |
| | | |
| | | private ProductionAccountDto normalizeDateQuery(ProductionAccountDto dto) { |
| | | // è§èæ¥ææ¥è¯¢èå´ï¼è¡¥é½ç¼ºå¤±çå¼å§æç»ææ¶é´ |
| | | if (dto == null) { |
| | | return new ProductionAccountDto(); |
| | | } |
| | |
| | | */ |
| | | @Override |
| | | public List<ProductionBomStructureVo> listByBomId(Long bomId) { |
| | | // æBOMIDæ¥è¯¢çäº§ç»ææ°æ® |
| | | List<ProductionBomStructureVo> list = productionBomStructureMapper.listByBomId(bomId); |
| | | Map<Long, ProductionBomStructureVo> map = new HashMap<>(); |
| | | for (ProductionBomStructureVo node : list) { |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean addProductionBomStructure(ProductionBomStructureDto dto) { |
| | | // æ°å¢ç产BOMç»æ |
| | | // 读åå½å订åBOM主é®ï¼å¹¶æå端æ ç»ææå¹³æå表 |
| | | Long orderBomId = dto.getProductionOrderBomId(); |
| | | List<ProductionBomStructureDto> flatDtoList = new ArrayList<>(); |
| | | flattenTree(dto.getChildren(), flatDtoList); |
| | | |
| | | // æ¥è¯¢æ°æ®åºå·²æç»æï¼ç¨äºåç»åå¢å æ¹å¯¹æ¯ |
| | | List<ProductionBomStructure> dbList = this.list(new LambdaQueryWrapper<ProductionBomStructure>() |
| | | .eq(ProductionBomStructure::getProductionOrderBomId, orderBomId)); |
| | | |
| | | // æ¶éå端ä»ç¶åå¨çèç¹ID |
| | | Set<Long> frontendIds = new HashSet<>(); |
| | | for (ProductionBomStructureDto item : flatDtoList) { |
| | | if (item.getId() != null) { |
| | |
| | | } |
| | | } |
| | | |
| | | // 计ç®éè¦å é¤çèç¹ï¼æ°æ®åºæãå端已å é¤ï¼ |
| | | Set<Long> deleteIds = new HashSet<>(); |
| | | for (ProductionBomStructure dbItem : dbList) { |
| | | if (!frontendIds.contains(dbItem.getId())) { |
| | | deleteIds.add(dbItem.getId()); |
| | | } |
| | | } |
| | | // å
å æå端已ç»ç§»é¤çèç¹ |
| | | if (!deleteIds.isEmpty()) { |
| | | this.removeByIds(deleteIds); |
| | | } |
| | | |
| | | // ææ¯å¦æIDæå为æ°å¢åæ´æ°ï¼åæ¶ç¼åæ°å¢èç¹ç临æ¶IDæ å° |
| | | List<ProductionBomStructure> insertList = new ArrayList<>(); |
| | | List<ProductionBomStructure> updateList = new ArrayList<>(); |
| | | Map<String, ProductionBomStructure> tempEntityMap = new HashMap<>(); |
| | |
| | | } |
| | | } |
| | | |
| | | // æ¹éæ°å¢ï¼æ¿å°æ°æ®åºçæççå®ID |
| | | if (!insertList.isEmpty()) { |
| | | this.saveBatch(insertList); |
| | | } |
| | | |
| | | // æ°å¢èç¹äºæ¬¡ååç¶IDï¼åç«¯ä¼ çæ¯ä¸´æ¶ç¶IDï¼ |
| | | List<ProductionBomStructure> parentFixList = new ArrayList<>(); |
| | | for (ProductionBomStructureDto item : flatDtoList) { |
| | | if (item.getId() == null && item.getParentTempId() != null) { |
| | |
| | | continue; |
| | | } |
| | | ProductionBomStructure parent = tempEntityMap.get(item.getParentTempId()); |
| | | // ç¶èç¹æ¯æ¬æ¬¡æ°å¢æ¶ï¼ç´æ¥ç¨æ°å¢åççå®IDï¼å¦ååé为åç«¯ä¼ å
¥ç¶ID |
| | | Long realParentId = parent != null ? parent.getId() : Long.valueOf(item.getParentTempId()); |
| | | child.setParentId(realParentId); |
| | | parentFixList.add(child); |
| | | } |
| | | } |
| | | |
| | | // ååæ°å¢èç¹çç¶åå
³ç³» |
| | | if (!parentFixList.isEmpty()) { |
| | | this.updateBatchById(parentFixList); |
| | | } |
| | | // æ¹éæ´æ°å·²æèç¹ |
| | | if (!updateList.isEmpty()) { |
| | | this.updateBatchById(updateList); |
| | | } |
| | |
| | | * å°æ å½¢ç»ææå¹³æå表ï¼ä¾¿äºç»ä¸ä¿åã |
| | | */ |
| | | private void flattenTree(List<ProductionBomStructureDto> source, List<ProductionBomStructureDto> result) { |
| | | // æå¹³åå¤çæ |
| | | if (source == null) { |
| | | return; |
| | | } |
| | |
| | | |
| | | @Override |
| | | public IPage<ProductionOperationTaskVo> pageProductionOperationTask(Page<ProductionOperationTaskDto> page, ProductionOperationTaskDto dto) { |
| | | // å页æ¥è¯¢ç产工åºä»»å¡ |
| | | Page<ProductionOperationTaskVo> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal()); |
| | | IPage<ProductionOperationTaskVo> result = baseMapper.pageProductionOperationTask(voPage, dto); |
| | | fillUserNames(result.getRecords()); |
| | |
| | | |
| | | @Override |
| | | public List<ProductionOperationTaskVo> listProductionOperationTask(ProductionOperationTaskDto dto) { |
| | | // æ¥è¯¢å·¥åºä»»å¡å表 |
| | | List<ProductionOperationTaskVo> result = BeanUtil.copyToList(this.list(buildQueryWrapper(dto)), ProductionOperationTaskVo.class); |
| | | fillUserNames(result); |
| | | return result; |
| | |
| | | |
| | | @Override |
| | | public ProductionOperationTaskVo getProductionOperationTaskInfo(Long id) { |
| | | // è·åç产工åºä»»å¡è¯¦æ
|
| | | ProductionOperationTask item = this.getById(id); |
| | | if (item == null) { |
| | | return null; |
| | |
| | | |
| | | @Override |
| | | public boolean saveProductionOperationTask(ProductionOperationTask productionOperationTask) { |
| | | // ä¿åç产工åºä»»å¡ |
| | | return this.saveOrUpdate(productionOperationTask); |
| | | } |
| | | |
| | | @Override |
| | | public boolean removeProductionOperationTask(List<Long> ids) { |
| | | // å é¤ç产工åºä»»å¡ |
| | | return ids != null && !ids.isEmpty() && this.removeByIds(ids); |
| | | } |
| | | |
| | | @Override |
| | | public int updateProductWorkOrder(ProductionOperationTaskDto dto) { |
| | | // æ´æ°å·¥åºä»»å¡å¯¹åºçå·¥åä¿¡æ¯ |
| | | return baseMapper.updateById(dto); |
| | | } |
| | | |
| | | @Override |
| | | public boolean assign(ProductionOperationTaskDto dto) { |
| | | // åé
å·¥åºä»»å¡æ§è¡äºº |
| | | if (dto == null || dto.getId() == null) { |
| | | throw new ServiceException("å·¥åIDä¸è½ä¸ºç©º"); |
| | | } |
| | |
| | | } |
| | | |
| | | private LambdaQueryWrapper<ProductionOperationTask> buildQueryWrapper(ProductionOperationTaskDto dto) { |
| | | // ææ¡ä»¶å¨ææå»ºæ°æ®åºæ¥è¯¢æ¡ä»¶ |
| | | ProductionOperationTask query = dto == null ? new ProductionOperationTask() : dto; |
| | | return Wrappers.<ProductionOperationTask>lambdaQuery() |
| | | .eq(query.getId() != null, ProductionOperationTask::getId, query.getId()) |
| | |
| | | } |
| | | |
| | | private void fillUserNames(List<ProductionOperationTaskVo> voList) { |
| | | // å¡«å
ç¨æ·åç§° |
| | | if (voList == null || voList.isEmpty()) { |
| | | return; |
| | | } |
| | | Set<Long> userIdSet = new LinkedHashSet<>(); |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | for (ProductionOperationTaskVo vo : voList) { |
| | | if (vo == null) { |
| | | continue; |
| | |
| | | } |
| | | |
| | | private List<Long> parseUserIdList(String userIds, boolean strict) { |
| | | // è§£æå¹¶æ ¡éªç¨æ·IDæ°ç»å符串 |
| | | if (StringUtils.isBlank(userIds)) { |
| | | if (strict) { |
| | | throw new ServiceException("userIdsæ ¼å¼ä¸æ£ç¡®ï¼å¿
须为JSONæ°åæ°ç»"); |
| | |
| | | |
| | | @Override |
| | | public void down(HttpServletResponse response, ProductionOperationTaskDto dto) { |
| | | // 导åºå·¥åºä»»å¡æ°æ® |
| | | if (dto == null || dto.getId() == null) { |
| | | throw new ServiceException("å·¥åIDä¸è½ä¸ºç©º"); |
| | | } |
| | |
| | | } |
| | | |
| | | private List<Map<String, Object>> buildTaskAttachmentImages(Long taskId) { |
| | | // ç»è£
ä»»å¡éä»¶å¾çæ°æ®ç¨äºå¯¼åº |
| | | List<Map<String, Object>> images = new ArrayList<>(); |
| | | StorageAttachmentDTO storageAttachmentDTO = new StorageAttachmentDTO(); |
| | | storageAttachmentDTO.setRecordType(RecordTypeEnum.PRODUCTION_OPERATION_TASK.getType()); |
| | | storageAttachmentDTO.setRecordId(taskId); |
| | | List<StorageBlobVO> taskWorkOrderFiles = |
| | | fileUtil.getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(storageAttachmentDTO); |
| | | // åæ°ä¸åç½®æ¡ä»¶æ ¡éª |
| | | if (CollectionUtils.isEmpty(taskWorkOrderFiles)) { |
| | | return images; |
| | | } |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | for (StorageBlobVO blobVO : taskWorkOrderFiles) { |
| | | if (blobVO == null) { |
| | | continue; |
| | |
| | | } |
| | | |
| | | private File resolveImageFile(StorageBlobVO blobVO) { |
| | | // å°éä»¶ä¿¡æ¯è§£æä¸ºæ¬å°å¾çæä»¶å¯¹è±¡ |
| | | if (blobVO == null || StringUtils.isBlank(blobVO.getUidFilename())) { |
| | | return null; |
| | | } |
| | |
| | | } |
| | | |
| | | private PictureType resolvePictureType(StorageBlobVO blobVO) { |
| | | // ææä»¶åæå
容类åè¯å«å¾çæ ¼å¼ |
| | | if (blobVO == null) { |
| | | return null; |
| | | } |
| | |
| | | } |
| | | |
| | | private PictureType parsePictureTypeByFileName(String fileName) { |
| | | // æ ¹æ®æä»¶åç¼è§£æå¾çæ ¼å¼ |
| | | if (StringUtils.isBlank(fileName) || !fileName.contains(".")) { |
| | | return null; |
| | | } |
| | |
| | | } |
| | | |
| | | private PictureType parsePictureTypeByContentType(String contentType) { |
| | | // æ ¹æ®Content-Typeè§£æå¾çæ ¼å¼ |
| | | if (StringUtils.isBlank(contentType)) { |
| | | return null; |
| | | } |
| | |
| | | |
| | | @Override |
| | | public List<ProductionOperationTaskVo> getOperation(ProductionOperationTaskDto dto) { |
| | | // æ¥è¯¢å·¥åºä»»å¡å表 |
| | | return baseMapper.getOperation(dto); |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | public List<ProductionOrderPickRecordVo> listFeedingRecord(ProductionOrderPickRecordDto dto) { |
| | | // æ¥è¯¢ææè®°å½ |
| | | if (dto == null || dto.getProductionOrderId() == null || dto.getPickId() == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.enums.ReviewStatusEnum; |
| | | import com.ruoyi.common.enums.StockInQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.production.bean.dto.ProductionOrderPickDto; |
| | |
| | | import com.ruoyi.production.service.ProductionOrderPickService; |
| | | import com.ruoyi.stock.dto.StockInventoryDto; |
| | | import com.ruoyi.stock.mapper.StockInventoryMapper; |
| | | import com.ruoyi.stock.pojo.StockInRecord; |
| | | import com.ruoyi.stock.pojo.StockInventory; |
| | | import com.ruoyi.stock.pojo.StockOutRecord; |
| | | import com.ruoyi.stock.service.StockInventoryService; |
| | | import com.ruoyi.stock.service.StockInRecordService; |
| | | import com.ruoyi.stock.service.StockOutRecordService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * <p> |
| | | * çã å´æ£°åæ¡ç»¾è¯ç«æµ ?éå¶
å§ç¹çµå¹ç»«? |
| | | * </p> |
| | | * |
| | | * @author éºîî±æîæ¬¢éå çé»å¿¥ç´é夿ªºéîå¾ |
| | | * @since 2026-04-21 03:55:52 |
| | | * ç产订å颿æå¡å®ç°ã |
| | | * è´è´£é¢ææ°å¢ãæ´æ°ãè¡¥æãéæååºåèå¨ã |
| | | */ |
| | | @Service |
| | | @RequiredArgsConstructor |
| | |
| | | |
| | | private static final byte PICK_TYPE_NORMAL = 1; |
| | | private static final byte PICK_TYPE_FEEDING = 2; |
| | | private static final String PICK_STOCK_OUT_RECORD_TYPE = StockOutQualifiedRecordTypeEnum.PICK_STOCK_OUT.getCode(); |
| | | private static final String FEED_STOCK_OUT_RECORD_TYPE = StockOutQualifiedRecordTypeEnum.FEED_STOCK_OUT.getCode(); |
| | | private static final String PICK_RETURN_IN_RECORD_TYPE = StockInQualifiedRecordTypeEnum.PICK_RETURN_IN.getCode(); |
| | | private static final String FEED_RETURN_IN_RECORD_TYPE = StockInQualifiedRecordTypeEnum.FEED_RETURN_IN.getCode(); |
| | | |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | private final ProductionOperationTaskMapper productionOperationTaskMapper; |
| | | private final ProductionOrderPickRecordMapper productionOrderPickRecordMapper; |
| | | private final StockInventoryMapper stockInventoryMapper; |
| | | private final StockInventoryService stockInventoryService; |
| | | private final StockInRecordService stockInRecordService; |
| | | private final StockOutRecordService stockOutRecordService; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean savePick(ProductionOrderPickDto dto) { |
| | | // 颿æ°å¢æ»æµç¨ï¼ |
| | | // 1) è§£æåç«¯è¡æ°æ®å¹¶éè¡åå¹¶åæ°ï¼ |
| | | // 2) æ ¡éªåæ°ä¸æ¹æ¬¡ï¼ |
| | | // 3) å
ä¿åé¢æä¸»è®°å½ï¼ |
| | | // 4) åèµ°âåºåºç³è¯· + 审æ¹éè¿â宿åºåæ£åï¼ |
| | | // 5) åå
¥é¢ææµæ°´ï¼è®°å½æ°éåå轨迹ã |
| | | List<ProductionOrderPickDto> pickItems = resolvePickItems(dto); |
| | | // éè¡å¤çé¢ææ°æ®ï¼è¡å·ç¨äºæ¼è£
ç²¾ç¡®çæ¥éä¿¡æ¯ã |
| | | for (int i = 0; i < pickItems.size(); i++) { |
| | | int rowNo = i + 1; |
| | | ProductionOrderPickDto resolvedDto = mergeDto(dto, pickItems.get(i)); |
| | | // æ¯è¡é½å宿´æ ¡éªï¼å¼å¸¸ä¿¡æ¯å¸¦è¡å·ã |
| | | validatePickParam(resolvedDto, rowNo); |
| | | |
| | | // ç»ä¸å¤çæ¹æ¬¡ï¼æ¯æåæ¹æ¬¡/夿¹æ¬¡ï¼ã |
| | | List<String> batchNoList = resolveBatchNoList(resolvedDto); |
| | | String inventoryBatchNo = pickInventoryBatchNo(batchNoList); |
| | | String storedBatchNo = formatBatchNoStorage(batchNoList); |
| | | subtractInventory(resolvedDto.getProductModelId(), storedBatchNo, resolvedDto.getPickQuantity(), rowNo); |
| | | |
| | | // ä¿åé¢æä¸»è®°å½å¿«ç
§ã |
| | | ProductionOrderPick orderPick = new ProductionOrderPick(); |
| | | orderPick.setProductionOrderId(resolvedDto.getProductionOrderId()); |
| | | orderPick.setProductModelId(resolvedDto.getProductModelId()); |
| | |
| | | orderPick.setDemandedQuantity(resolvedDto.getDemandedQuantity()); |
| | | orderPick.setBom(resolvedDto.getBom()); |
| | | orderPick.setReturned(false); |
| | | // æ°å¢ä¸»è®°å½ã |
| | | baseMapper.insert(orderPick); |
| | | |
| | | // å
æ°å¢åºåºç³è¯·ï¼å审æ¹éè¿ï¼å®æåºåæ£åã |
| | | subtractInventory(orderPick.getId(), resolvedDto.getProductModelId(), storedBatchNo, resolvedDto.getPickQuantity(), rowNo, PICK_STOCK_OUT_RECORD_TYPE); |
| | | |
| | | // è®°å½æ¬æ¬¡é¢ææµæ°´ï¼before=0ï¼after=æ¬æ¬¡é¢æéï¼ã |
| | | insertPickRecord(orderPick.getId(), |
| | | resolvedDto.getProductionOrderId(), |
| | | resolvedDto.getProductionOperationTaskId(), |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Boolean updatePick(ProductionOrderPickDto dto) { |
| | | // é¢ææ´æ°å
¥å£ï¼åæ¥å£å
¼å®¹ä¸ç±»ä¸å¡ï¼ï¼ |
| | | // 1) æ®é颿æ¹é/å¢å ï¼ |
| | | // 2) è¡¥æï¼pickType=2ï¼ï¼ |
| | | // 3) éæï¼returned=trueï¼ã |
| | | if (dto == null) { |
| | | throw new ServiceException("åæ´åæ°ä¸è½ä¸ºç©º"); |
| | | throw new ServiceException("åæ°ä¸è½ä¸ºç©º"); |
| | | } |
| | | Long productionOrderId = resolveProductionOrderId(dto); |
| | | if (productionOrderId == null) { |
| | |
| | | throw new ServiceException("ç产订åä¸åå¨"); |
| | | } |
| | | |
| | | // æ¥è¯¢è®¢åä¸ç°æé¢æè®°å½å¹¶æå»ºIDç´¢å¼ã |
| | | List<ProductionOrderPick> existingPickList = baseMapper.selectList( |
| | | Wrappers.<ProductionOrderPick>lambdaQuery() |
| | | .eq(ProductionOrderPick::getProductionOrderId, productionOrderId)); |
| | | // 转æMap便äºåç»æIDå¿«éæ ¡éªä¸æ´æ°ã |
| | | Map<Long, ProductionOrderPick> existingPickMap = existingPickList.stream() |
| | | .filter(item -> item.getId() != null) |
| | | .collect(Collectors.toMap(ProductionOrderPick::getId, Function.identity(), (a, b) -> a)); |
| | | |
| | | // è¡¥æè¯·æ±åç¬èµ°è¡¥æåæ¯ã |
| | | if (isFeedingRequest(dto)) { |
| | | processFeedingPickItems(dto, existingPickMap, productionOrderId); |
| | | return true; |
| | | } |
| | | // éæè¯·æ±åç¬èµ°éæåæ¯ã |
| | | if (isReturnRequest(dto)) { |
| | | processReturnPickItems(dto, existingPickMap, productionOrderId); |
| | | return true; |
| | | } |
| | | |
| | | // æ®éæ´æ°åºæ¯å
å¤çæ¾å¼å é¤ã |
| | | processDeletePickIds(dto, existingPickMap, productionOrderId); |
| | | |
| | | List<ProductionOrderPickDto> pickItems = resolveUpdateItems(dto); |
| | | Set<Long> keepPickIdSet = new HashSet<>(); |
| | | // keepPickIdSet ç¨äºæ è®°æ¬æ¬¡å端ä»ç¶ä¿ççæ§è®°å½ï¼åç»ç¨äºè¯å«âæªåä¼ å³å é¤âçè¡ã |
| | | for (int i = 0; i < pickItems.size(); i++) { |
| | | int rowNo = i + 1; |
| | | ProductionOrderPickDto resolvedDto = mergeDto(dto, pickItems.get(i)); |
| | |
| | | keepPickIdSet.add(resolvedDto.getId()); |
| | | updateExistingPick(resolvedDto, rowNo, existingPickMap); |
| | | } |
| | | // æ¸
çå端æªåä¼ æ§è¡å¹¶åè¡¥åºåã |
| | | processMissingPickItems(dto, existingPickMap, productionOrderId, keepPickIdSet); |
| | | return true; |
| | | } |
| | | |
| | | @Override |
| | | public List<ProductionOrderPickVo> listPickedDetail(Long productionOrderId) { |
| | | // æ¥è¯¢è®¢å颿æç»ï¼å¹¶è¡¥é½æ¹æ¬¡å±ç¤ºå段ã |
| | | if (productionOrderId == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | |
| | | private void processDeletePickIds(ProductionOrderPickDto rootDto, |
| | | Map<Long, ProductionOrderPick> existingPickMap, |
| | | Long productionOrderId) { |
| | | // å¤çå端æ¾å¼å é¤IDï¼ |
| | | // 1) æ ¡éªå é¤ç®æ æ¯å¦å±äºå½å订åï¼ |
| | | // 2) åè¡¥åºåï¼ |
| | | // 3) å é¤ä¸»è®°å½ï¼ |
| | | // 4) è®°å½å 餿µæ°´ã |
| | | if (rootDto.getDeletePickIds() == null || rootDto.getDeletePickIds().isEmpty()) { |
| | | return; |
| | | } |
| | |
| | | } |
| | | ProductionOrderPick existingPick = existingPickMap.get(deleteId); |
| | | if (existingPick == null || !Objects.equals(existingPick.getProductionOrderId(), productionOrderId)) { |
| | | throw new ServiceException("è¦å é¤çé¢æè®°å½ä¸å卿ä¸å±äºå½å订åï¼ID=" + deleteId); |
| | | throw new ServiceException("å é¤å¤±è´¥ï¼é¢æè®°å½ä¸å卿ä¸å±äºå½å订åï¼ID=" + deleteId); |
| | | } |
| | | String oldBatchNo = resolveInventoryBatchNoFromStored(existingPick.getBatchNo()); |
| | | BigDecimal oldQuantity = defaultDecimal(existingPick.getQuantity()); |
| | | addInventory(existingPick.getProductModelId(), oldBatchNo, oldQuantity); |
| | | addInventory(existingPick.getId(), existingPick.getProductModelId(), oldBatchNo, oldQuantity, PICK_RETURN_IN_RECORD_TYPE); |
| | | // å é¤å
³èé¢ææµæ°´ï¼é¿å
éçæ ä¸»è®°å½ã |
| | | productionOrderPickRecordMapper.delete( |
| | | Wrappers.<ProductionOrderPickRecord>lambdaQuery() |
| | | .eq(ProductionOrderPickRecord::getPickId, existingPick.getId()) |
| | | ); |
| | | int affected = baseMapper.deleteById(deleteId); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("å é¤é¢æå¤±è´¥ï¼ID=" + deleteId); |
| | | throw new ServiceException("å é¤é¢æè®°å½å¤±è´¥ï¼ID=" + deleteId); |
| | | } |
| | | insertPickRecord(existingPick.getId(), |
| | | existingPick.getProductionOrderId(), |
| | |
| | | Map<Long, ProductionOrderPick> existingPickMap, |
| | | Long productionOrderId, |
| | | Set<Long> keepPickIdSet) { |
| | | // å¤çâå端æªåä¼ âçæ§è¡ï¼ |
| | | // 对åºåºæ¯æ¯ç¨æ·å¨å端å é¤è¡ä½æªæ¾å
¥ deletePickIdsã |
| | | // è¿éå
åºè¯å«å¹¶æ§è¡åè¡¥åºå + å é¤ä¸»è®°å½ + åæµæ°´ã |
| | | if (rootDto.getPickList() == null) { |
| | | return; |
| | | } |
| | |
| | | for (ProductionOrderPick missingPick : missingPickList) { |
| | | String oldBatchNo = resolveInventoryBatchNoFromStored(missingPick.getBatchNo()); |
| | | BigDecimal oldQuantity = defaultDecimal(missingPick.getQuantity()); |
| | | addInventory(missingPick.getProductModelId(), oldBatchNo, oldQuantity); |
| | | addInventory(missingPick.getId(), missingPick.getProductModelId(), oldBatchNo, oldQuantity, PICK_RETURN_IN_RECORD_TYPE); |
| | | // å é¤å
³èé¢ææµæ°´ï¼é¿å
éçæ ä¸»è®°å½ã |
| | | productionOrderPickRecordMapper.delete( |
| | | Wrappers.<ProductionOrderPickRecord>lambdaQuery() |
| | | .eq(ProductionOrderPickRecord::getPickId, missingPick.getId()) |
| | | ); |
| | | int affected = baseMapper.deleteById(missingPick.getId()); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("å é¤é¢æå¤±è´¥ï¼ID=" + missingPick.getId()); |
| | | throw new ServiceException("å 餿ªåä¼ é¢æè®°å½å¤±è´¥ï¼ID=" + missingPick.getId()); |
| | | } |
| | | insertPickRecord(missingPick.getId(), |
| | | missingPick.getProductionOrderId(), |
| | |
| | | } |
| | | |
| | | private void addNewPickInUpdate(ProductionOrderPickDto dto, int rowNo) { |
| | | // æ´æ°åºæ¯ä¸æ°å¢ä¸æ¡é¢æï¼ |
| | | // æ°å¢ä¸»è®°å½ -> åºåºç³è¯·å¹¶å®¡æ¹ -> åæµæ°´ã |
| | | List<String> batchNoList = resolveBatchNoList(dto); |
| | | String inventoryBatchNo = pickInventoryBatchNo(batchNoList); |
| | | String storedBatchNo = formatBatchNoStorage(batchNoList); |
| | | subtractInventory(dto.getProductModelId(), storedBatchNo, dto.getPickQuantity(), rowNo); |
| | | |
| | | ProductionOrderPick orderPick = new ProductionOrderPick(); |
| | | orderPick.setProductionOrderId(dto.getProductionOrderId()); |
| | |
| | | orderPick.setBom(dto.getBom()); |
| | | orderPick.setReturned(false); |
| | | baseMapper.insert(orderPick); |
| | | |
| | | // å
æ°å¢åºåºç³è¯·ï¼å审æ¹éè¿ï¼å®æåºåæ£åã |
| | | subtractInventory(orderPick.getId(), dto.getProductModelId(), storedBatchNo, dto.getPickQuantity(), rowNo, PICK_STOCK_OUT_RECORD_TYPE); |
| | | |
| | | insertPickRecord(orderPick.getId(), |
| | | dto.getProductionOrderId(), |
| | |
| | | private void processFeedingPickItems(ProductionOrderPickDto rootDto, |
| | | Map<Long, ProductionOrderPick> existingPickMap, |
| | | Long productionOrderId) { |
| | | // è¡¥ææµç¨å
¥å£ï¼ |
| | | // éè¡æ ¡éªè¡¥æåæ°ï¼æ ¡éªå颿å½å±ï¼åæ§è¡è¡¥æåºåæ£åå主记å½ååã |
| | | List<ProductionOrderPickDto> pickItems = resolveUpdateItems(rootDto); |
| | | for (int i = 0; i < pickItems.size(); i++) { |
| | | int rowNo = i + 1; |
| | |
| | | continue; |
| | | } |
| | | if (!isFeedingPick(resolvedDto)) { |
| | | throw new ServiceException("è¡¥æè¯·æ±ä¸çé¢æç±»åå¿
é¡»å
¨é¨ä¸º2"); |
| | | throw new ServiceException("è¡¥æè¯·æ±ä¸åå¨éè¡¥æç±»åæ°æ®"); |
| | | } |
| | | if (resolvedDto.getProductionOrderId() == null) { |
| | | resolvedDto.setProductionOrderId(productionOrderId); |
| | |
| | | |
| | | ProductionOrderPick oldPick = existingPickMap.get(resolvedDto.getId()); |
| | | if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), productionOrderId)) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢æè®°å½ä¸å卿ä¸å±äºå½å订å"); |
| | | throw new ServiceException("第" + rowNo + "è¡è¡¥æå¤±è´¥ï¼æªæ¾å°å¯¹åºçé¢æè®°å½"); |
| | | } |
| | | addFeedingPick(resolvedDto, oldPick, rowNo); |
| | | } |
| | | } |
| | | |
| | | private void addFeedingPick(ProductionOrderPickDto dto, ProductionOrderPick oldPick, int rowNo) { |
| | | // è¡¥ææ ¸å¿ï¼ |
| | | // 1) æ ¡éªè§æ ¼ä¸è´ï¼ |
| | | // 2) æ£åè¡¥æåºåï¼ |
| | | // 3) åè¡¥ææµæ°´ï¼ |
| | | // 4) åå主å累计补æéåå®é
éã |
| | | if (dto.getProductModelId() != null && !Objects.equals(dto.getProductModelId(), oldPick.getProductModelId())) { |
| | | throw new ServiceException("第" + rowNo + "æ¡è¡¥æäº§åè§æ ¼ä¸é¢æè®°å½ä¸ä¸è´"); |
| | | throw new ServiceException("第" + rowNo + "è¡è¡¥æå¤±è´¥ï¼äº§åè§æ ¼ä¸åé¢æè®°å½ä¸ä¸è´"); |
| | | } |
| | | Long productModelId = oldPick.getProductModelId(); |
| | | List<String> batchNoList = resolveBatchNoList(dto); |
| | |
| | | : formatBatchNoStorage(batchNoList); |
| | | BigDecimal feedingQuantity = dto.getFeedingQuantity(); |
| | | |
| | | subtractInventory(productModelId, inventoryBatchNo, feedingQuantity, rowNo); |
| | | subtractInventory(oldPick.getId(), productModelId, inventoryBatchNo, feedingQuantity, rowNo, FEED_STOCK_OUT_RECORD_TYPE); |
| | | |
| | | // 计ç®è¡¥æååæ°éå¹¶åè¡¥ææµæ°´ã |
| | | BigDecimal beforeFeedingQty = sumFeedingQuantity(dto.getProductionOrderId(), oldPick.getId()); |
| | | BigDecimal afterFeedingQty = beforeFeedingQty.add(feedingQuantity); |
| | | insertPickRecord(oldPick.getId(), |
| | |
| | | updatePick.setId(oldPick.getId()); |
| | | updatePick.setFeedingQty(afterFeedingQty); |
| | | updatePick.setActualQty(calculateActualQty(oldPick, afterFeedingQty)); |
| | | // åå主记å½çè¡¥æç´¯è®¡å¼ä¸å®é
ç¨éã |
| | | int affected = baseMapper.updateById(updatePick); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡è¡¥ææ»éæ´æ°å¤±è´¥"); |
| | | throw new ServiceException("第" + rowNo + "è¡è¡¥æå¤±è´¥ï¼æ´æ°é¢æä¸»è®°å½å¤±è´¥"); |
| | | } |
| | | oldPick.setFeedingQty(afterFeedingQty); |
| | | oldPick.setActualQty(updatePick.getActualQty()); |
| | |
| | | private void processReturnPickItems(ProductionOrderPickDto rootDto, |
| | | Map<Long, ProductionOrderPick> existingPickMap, |
| | | Long productionOrderId) { |
| | | // éææµç¨å
¥å£ï¼ |
| | | // éè¡æ ¡éªéæåæ°ä¸é¢æå½å±ï¼åæ´æ°éæéä¸å®é
éåæ®µã |
| | | List<ProductionOrderPickDto> pickItems = resolveUpdateItems(rootDto); |
| | | for (int i = 0; i < pickItems.size(); i++) { |
| | | int rowNo = i + 1; |
| | |
| | | |
| | | ProductionOrderPick oldPick = existingPickMap.get(resolvedDto.getId()); |
| | | if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), productionOrderId)) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢æè®°å½ä¸å卿ä¸å±äºå½å订å"); |
| | | throw new ServiceException("第" + rowNo + "è¡éæå¤±è´¥ï¼æªæ¾å°å¯¹åºçé¢æè®°å½"); |
| | | } |
| | | updateReturnPick(resolvedDto, oldPick, rowNo); |
| | | } |
| | | } |
| | | |
| | | private void updateReturnPick(ProductionOrderPickDto dto, ProductionOrderPick oldPick, int rowNo) { |
| | | // éææ´æ°ï¼ |
| | | // 1) returnQty æâæ¬æ¬¡éæéâå¤çï¼ |
| | | // 2) æ¬æ¬¡éæéåè¡¥å°âç产éæå
¥åºâï¼ |
| | | // 3) ç´¯å 主记å½éææ»éå¹¶éç®å®é
éã |
| | | BigDecimal oldReturnQty = defaultDecimal(oldPick.getReturnQty()); |
| | | BigDecimal currentReturnQty = defaultDecimal(dto.getReturnQty()); |
| | | BigDecimal totalReturnQty = oldReturnQty.add(currentReturnQty); |
| | | if (currentReturnQty.compareTo(BigDecimal.ZERO) > 0) { |
| | | String returnBatchNo = resolveInventoryBatchNoFromStored(oldPick.getBatchNo()); |
| | | addInventoryRecordOnly(oldPick.getId(), oldPick.getProductModelId(), returnBatchNo, currentReturnQty, FEED_RETURN_IN_RECORD_TYPE); |
| | | } |
| | | |
| | | BigDecimal actualQty = defaultDecimal(oldPick.getQuantity()) |
| | | .add(defaultDecimal(oldPick.getFeedingQty())) |
| | | .subtract(totalReturnQty); |
| | | if (actualQty.compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("第" + rowNo + "è¡éæå¤±è´¥ï¼ç´¯è®¡éææ°éä¸è½å¤§äºå¯ç¨æ°é"); |
| | | } |
| | | ProductionOrderPick updatePick = new ProductionOrderPick(); |
| | | updatePick.setId(oldPick.getId()); |
| | | updatePick.setReturnQty(dto.getReturnQty()); |
| | | updatePick.setActualQty(dto.getActualQty()); |
| | | updatePick.setReturned(true); |
| | | updatePick.setReturnQty(totalReturnQty); |
| | | updatePick.setActualQty(actualQty); |
| | | updatePick.setReturned(totalReturnQty.compareTo(BigDecimal.ZERO) > 0); |
| | | int affected = baseMapper.updateById(updatePick); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡éæä¿¡æ¯æ´æ°å¤±è´¥"); |
| | | throw new ServiceException("第" + rowNo + "è¡éæå¤±è´¥ï¼æ´æ°é¢æä¸»è®°å½å¤±è´¥"); |
| | | } |
| | | oldPick.setReturnQty(updatePick.getReturnQty()); |
| | | oldPick.setActualQty(updatePick.getActualQty()); |
| | | oldPick.setReturned(true); |
| | | oldPick.setReturned(updatePick.getReturned()); |
| | | } |
| | | |
| | | private void updateExistingPick(ProductionOrderPickDto dto, |
| | | int rowNo, |
| | | Map<Long, ProductionOrderPick> existingPickMap) { |
| | | // æ®éæ´æ°åè¡æ ¸å¿æµç¨ï¼ |
| | | // 1) æ ¡éªæ§è®°å½åå¨ä¸å±äºå½å订åï¼ |
| | | // 2) æ¯è¾æ°æ§âè§æ ¼+æ¹æ¬¡âï¼å³å®åºåå¤ççç¥ï¼ |
| | | // 3) æ´æ°ä¸»è®°å½ï¼ |
| | | // 4) ååæ´æµæ°´ï¼è®°å½ååæ°éååï¼ã |
| | | ProductionOrderPick oldPick = existingPickMap.get(dto.getId()); |
| | | if (oldPick == null || !Objects.equals(oldPick.getProductionOrderId(), dto.getProductionOrderId())) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢æè®°å½ä¸å卿ä¸å±äºå½å订å"); |
| | | throw new ServiceException("第" + rowNo + "è¡æ´æ°å¤±è´¥ï¼æªæ¾å°å¯¹åºçé¢æè®°å½"); |
| | | } |
| | | |
| | | Long oldProductModelId = oldPick.getProductModelId(); |
| | |
| | | String newStoredBatchNo = formatBatchNoStorage(newBatchNoList); |
| | | BigDecimal newQuantity = dto.getPickQuantity(); |
| | | |
| | | // å¤æè§æ ¼+æ¹æ¬¡ææ°éæ¯å¦ååï¼å¹¶æåºæ¯å¤çåºåï¼ |
| | | // 1) åè§æ ¼åæ¹æ¬¡ï¼æå·®å¼å¤çï¼å¢éæ£å / åéåéï¼ï¼ |
| | | // 2) è§æ ¼ææ¹æ¬¡ååï¼å鿧颿ååéææ°é¢æã |
| | | boolean sameStockKey = Objects.equals(oldProductModelId, newProductModelId) |
| | | && Objects.equals(oldBatchNo, newBatchNo); |
| | | boolean quantityChanged = oldQuantity.compareTo(newQuantity) != 0; |
| | | boolean needReissuePickRecord = !sameStockKey || quantityChanged; |
| | | if (sameStockKey) { |
| | | BigDecimal delta = newQuantity.subtract(oldQuantity); |
| | | if (delta.compareTo(BigDecimal.ZERO) > 0) { |
| | | subtractInventory(newProductModelId, newStoredBatchNo, delta, rowNo); |
| | | } else if (delta.compareTo(BigDecimal.ZERO) < 0) { |
| | | addInventory(oldProductModelId, oldBatchNo, delta.abs()); |
| | | BigDecimal deltaQuantity = newQuantity.subtract(oldQuantity); |
| | | if (deltaQuantity.compareTo(BigDecimal.ZERO) > 0) { |
| | | // æ°éå¢å ï¼åªæ£åæ°å¢é¨åã |
| | | subtractInventory(oldPick.getId(), newProductModelId, newStoredBatchNo, deltaQuantity, rowNo, PICK_STOCK_OUT_RECORD_TYPE); |
| | | } else if (deltaQuantity.compareTo(BigDecimal.ZERO) < 0) { |
| | | // æ°éåå°ï¼åªåéå·®å¼é¨åã |
| | | addInventory(oldPick.getId(), oldProductModelId, oldBatchNo, deltaQuantity.abs(), PICK_RETURN_IN_RECORD_TYPE); |
| | | } |
| | | } else { |
| | | addInventory(oldProductModelId, oldBatchNo, oldQuantity); |
| | | subtractInventory(newProductModelId, newStoredBatchNo, newQuantity, rowNo); |
| | | // è§æ ¼ææ¹æ¬¡ååï¼å
å
¨éå鿧颿ï¼åå
¨éæ£åæ°é¢æã |
| | | addInventory(oldPick.getId(), oldProductModelId, oldBatchNo, oldQuantity, PICK_RETURN_IN_RECORD_TYPE); |
| | | subtractInventory(oldPick.getId(), newProductModelId, newStoredBatchNo, newQuantity, rowNo, PICK_STOCK_OUT_RECORD_TYPE); |
| | | } |
| | | if (needReissuePickRecord) { |
| | | // æ£å¸¸é¢ææµæ°´æâææ°é¢æéâé建ï¼é¿å
ä¿çå岿§å¼ã |
| | | deleteNormalPickRecord(oldPick.getId()); |
| | | } |
| | | |
| | | oldPick.setProductModelId(newProductModelId); |
| | |
| | | oldPick.setRemark(dto.getRemark()); |
| | | oldPick.setOperationName(dto.getOperationName()); |
| | | oldPick.setTechnologyOperationId(dto.getTechnologyOperationId()); |
| | | // æ®éæ´æ°ä¹è¦åæ¥éç®å®é
ç¨éï¼é¿å
æ²¿ç¨æ§å¼ã |
| | | // è§åï¼å®é
ç¨é = 颿æ°é + è¡¥ææ°é - éææ°éã |
| | | oldPick.setActualQty(calculateActualQty(oldPick, oldPick.getFeedingQty())); |
| | | if (dto.getDemandedQuantity() != null) { |
| | | oldPick.setDemandedQuantity(dto.getDemandedQuantity()); |
| | | } |
| | |
| | | } |
| | | int affected = baseMapper.updateById(oldPick); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢ææ´æ°å¤±è´¥"); |
| | | throw new ServiceException("第" + rowNo + "è¡æ´æ°å¤±è´¥ï¼æ´æ°é¢æè®°å½å¤±è´¥"); |
| | | } |
| | | |
| | | BigDecimal recordQuantity = sameStockKey ? oldQuantity.subtract(newQuantity).abs() : newQuantity; |
| | | if (recordQuantity.compareTo(BigDecimal.ZERO) > 0 || oldQuantity.compareTo(newQuantity) != 0 || !sameStockKey) { |
| | | // 妿åç颿éæï¼è¡¥å䏿¡æ°çæ£å¸¸é¢ææµæ°´ã |
| | | if (needReissuePickRecord) { |
| | | insertPickRecord(oldPick.getId(), |
| | | dto.getProductionOrderId(), |
| | | dto.getProductionOperationTaskId(), |
| | | newProductModelId, |
| | | newBatchNo, |
| | | recordQuantity, |
| | | oldQuantity, |
| | | newQuantity, |
| | | BigDecimal.ZERO, |
| | | newQuantity, |
| | | dto.getPickType(), |
| | | dto.getRemark(), |
| | |
| | | Byte pickType, |
| | | String remark, |
| | | String feedingReason) { |
| | | // åé¢ææµæ°´è®°å½ï¼ç»ä¸è®°å½é¢æ/è¡¥æ/éææ°éåå轨迹ã |
| | | ProductionOrderPickRecord pickRecord = new ProductionOrderPickRecord(); |
| | | pickRecord.setPickId(pickId); |
| | | pickRecord.setProductionOrderId(productionOrderId); |
| | |
| | | productionOrderPickRecordMapper.insert(pickRecord); |
| | | } |
| | | |
| | | private void subtractInventory(Long productModelId, String batchNo, BigDecimal quantity, int rowNo) { |
| | | private void deleteNormalPickRecord(Long pickId) { |
| | | // å é¤è¯¥é¢æååå²ä¸çâæ£å¸¸é¢æâæµæ°´ï¼ä¿çè¡¥æ/éææµæ°´ã |
| | | if (pickId == null) { |
| | | return; |
| | | } |
| | | productionOrderPickRecordMapper.delete( |
| | | Wrappers.<ProductionOrderPickRecord>lambdaQuery() |
| | | .eq(ProductionOrderPickRecord::getPickId, pickId) |
| | | .eq(ProductionOrderPickRecord::getPickType, PICK_TYPE_NORMAL) |
| | | ); |
| | | } |
| | | |
| | | private void subtractInventory(Long recordId, |
| | | Long productModelId, |
| | | String batchNo, |
| | | BigDecimal quantity, |
| | | int rowNo, |
| | | String stockOutRecordType) { |
| | | // æ£ååºåæ»æµç¨ï¼ |
| | | // 1) è§£ææ¹æ¬¡åè¡¨ï¼ |
| | | // 2) è®¡ç®æ¯ä¸ªæ¹æ¬¡å¯ç¨é䏿»å¯ç¨éï¼ |
| | | // 3) ææ¹æ¬¡é¡ºåºéç¬âæ°å¢åºåºè®°å½å¹¶å®¡æ¹éè¿âï¼ç´å°æ£å®ç®æ æ°éï¼ |
| | | // 4) 任䏿¥å¤±è´¥å³æéå¹¶åæ»äºå¡ã |
| | | BigDecimal deductQuantity = defaultDecimal(quantity); |
| | | // 颿æ°éå°äºçäº0æ¶ï¼ä¸éè¦æ§è¡åºåæ£åã |
| | | if (deductQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | |
| | | batchNoList = Collections.singletonList(null); |
| | | } |
| | | |
| | | // å
计ç®åæ¹æ¬¡å¯ç¨éï¼é¿å
è¾¹æ£è¾¹ç®å¯¼è´å¤æä¸ä¸è´ã |
| | | Map<String, BigDecimal> availableQuantityMap = new LinkedHashMap<>(); |
| | | BigDecimal totalAvailableQuantity = BigDecimal.ZERO; |
| | | // éåæ¹æ¬¡ï¼è®¡ç®æ¯ä¸ªæ¹æ¬¡å¯ç¨åºåã |
| | | for (String currentBatchNo : batchNoList) { |
| | | // æ¥è¯¢å½åè§æ ¼+æ¹æ¬¡çåºåè®°å½ã |
| | | StockInventory stockInventory = stockInventoryMapper.selectOne(buildStockWrapper(productModelId, currentBatchNo)); |
| | | BigDecimal availableQuantity = BigDecimal.ZERO; |
| | | if (stockInventory != null) { |
| | |
| | | |
| | | if (deductQuantity.compareTo(totalAvailableQuantity) > 0) { |
| | | BigDecimal shortQuantity = deductQuantity.subtract(totalAvailableQuantity); |
| | | throw new ServiceException("颿å¯ç¨åºåä¸è¶³ï¼å¯ç¨åºå为" + formatQuantity(totalAvailableQuantity) |
| | | + "ï¼è¿å·®" + formatQuantity(shortQuantity)); |
| | | throw new ServiceException("第" + rowNo + "è¡æ£ååºå失败ï¼å¯ç¨åºåä¸è¶³ï¼å½åå¯ç¨" |
| | | + formatQuantity(totalAvailableQuantity) + "ï¼ä»ç¼ºå°" + formatQuantity(shortQuantity)); |
| | | } |
| | | |
| | | // ææ¹æ¬¡é¡ºåºéç¬æ£ååºåã |
| | | BigDecimal remainingQuantity = deductQuantity; |
| | | for (Map.Entry<String, BigDecimal> entry : availableQuantityMap.entrySet()) { |
| | | if (remainingQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | |
| | | continue; |
| | | } |
| | | BigDecimal currentDeductQuantity = remainingQuantity.min(availableQuantity); |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(entry.getKey()); |
| | | stockInventoryDto.setQualitity(currentDeductQuantity); |
| | | int affected = stockInventoryMapper.updateSubtractStockInventory(stockInventoryDto); |
| | | if (affected <= 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢ææ£ååºå失败"); |
| | | } |
| | | createAndApproveStockOutRecord(recordId, productModelId, entry.getKey(), currentDeductQuantity, rowNo, stockOutRecordType); |
| | | remainingQuantity = remainingQuantity.subtract(currentDeductQuantity); |
| | | } |
| | | |
| | | if (remainingQuantity.compareTo(BigDecimal.ZERO) > 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢ææ£ååºå失败ï¼å©ä½å¾
æ£åæ°é为" + formatQuantity(remainingQuantity)); |
| | | throw new ServiceException("第" + rowNo + "è¡æ£ååºå失败ï¼ä»ææªæ£åæ°é" + formatQuantity(remainingQuantity)); |
| | | } |
| | | } |
| | | |
| | | private void addInventory(Long productModelId, String batchNo, BigDecimal quantity) { |
| | | private void createAndApproveStockOutRecord(Long recordId, |
| | | Long productModelId, |
| | | String batchNo, |
| | | BigDecimal quantity, |
| | | int rowNo, |
| | | String stockOutRecordType) { |
| | | // åºåæ£åæ¹ä¸ºä¸¤æ¥ï¼ |
| | | // 1) å
è°ç¨ addStockOutRecordOnly æ°å¢å¾
审æ¹åºåºè®°å½ï¼ |
| | | // 2) åè°ç¨åºåºå®¡æ¹ï¼å®¡æ¹ç¶æåºå®ä¼ 1ï¼éè¿ï¼ã |
| | | try { |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setRecordId(recordId == null ? 0L : recordId); |
| | | stockInventoryDto.setRecordType(stockOutRecordType); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | stockInventoryDto.setQualitity(quantity); |
| | | stockInventoryService.addStockOutRecordOnly(stockInventoryDto); |
| | | |
| | | LambdaQueryWrapper<StockOutRecord> recordWrapper = Wrappers.<StockOutRecord>lambdaQuery() |
| | | .eq(StockOutRecord::getRecordId, stockInventoryDto.getRecordId()) |
| | | .eq(StockOutRecord::getRecordType, stockOutRecordType) |
| | | .eq(StockOutRecord::getProductModelId, productModelId) |
| | | .eq(StockOutRecord::getType, "0") |
| | | .orderByDesc(StockOutRecord::getId) |
| | | .last("limit 1"); |
| | | if (StringUtils.isEmpty(batchNo)) { |
| | | recordWrapper.isNull(StockOutRecord::getBatchNo); |
| | | } else { |
| | | recordWrapper.eq(StockOutRecord::getBatchNo, batchNo); |
| | | } |
| | | StockOutRecord stockOutRecord = stockOutRecordService.getOne(recordWrapper, false); |
| | | if (stockOutRecord == null || stockOutRecord.getId() == null) { |
| | | throw new ServiceException("第" + rowNo + "è¡æ£ååºåå¤±è´¥ï¼æªæ¾å°å¯¹åºåºåºç³è¯·è®°å½"); |
| | | } |
| | | stockOutRecordService.batchApprove( |
| | | Collections.singletonList(stockOutRecord.getId()), |
| | | ReviewStatusEnum.APPROVED.getCode() |
| | | ); |
| | | } catch (ServiceException ex) { |
| | | throw ex; |
| | | } catch (Exception ex) { |
| | | throw new ServiceException("第" + rowNo + "è¡æ£ååºå失败ï¼" + ex.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void addInventory(Long recordId, |
| | | Long productModelId, |
| | | String batchNo, |
| | | BigDecimal quantity, |
| | | String stockInRecordType) { |
| | | // åè¡¥åºåæ¹ä¸ºä¸¤æ¥ï¼ |
| | | // 1) å
æ°å¢å
¥åºç³è¯·ï¼ |
| | | // 2) å审æ¹éè¿ï¼ç¡®ä¿åºåç«å»åè¡¥çæã |
| | | BigDecimal addQuantity = defaultDecimal(quantity); |
| | | if (addQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | try { |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | stockInventoryDto.setQualitity(addQuantity); |
| | | stockInventoryDto.setRecordType(String.valueOf(StockInQualifiedRecordTypeEnum.PICK_RETURN_IN.getCode())); |
| | | stockInventoryDto.setRecordId(0L); |
| | | stockInventoryDto.setRecordType(stockInRecordType); |
| | | stockInventoryDto.setRecordId(recordId == null ? 0L : recordId); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | |
| | | LambdaQueryWrapper<StockInRecord> recordWrapper = Wrappers.<StockInRecord>lambdaQuery() |
| | | .eq(StockInRecord::getRecordId, stockInventoryDto.getRecordId()) |
| | | .eq(StockInRecord::getRecordType, stockInventoryDto.getRecordType()) |
| | | .eq(StockInRecord::getProductModelId, productModelId) |
| | | .eq(StockInRecord::getType, "0") |
| | | .orderByDesc(StockInRecord::getId) |
| | | .last("limit 1"); |
| | | if (StringUtils.isEmpty(batchNo)) { |
| | | recordWrapper.isNull(StockInRecord::getBatchNo); |
| | | } else { |
| | | recordWrapper.eq(StockInRecord::getBatchNo, batchNo); |
| | | } |
| | | StockInRecord stockInRecord = stockInRecordService.getOne(recordWrapper, false); |
| | | if (stockInRecord == null || stockInRecord.getId() == null) { |
| | | throw new ServiceException("åè¡¥åºåå¤±è´¥ï¼æªæ¾å°å¯¹åºå
¥åºç³è¯·è®°å½"); |
| | | } |
| | | stockInRecordService.batchApprove( |
| | | Collections.singletonList(stockInRecord.getId()), |
| | | ReviewStatusEnum.APPROVED.getCode() |
| | | ); |
| | | } catch (ServiceException ex) { |
| | | throw ex; |
| | | } catch (Exception ex) { |
| | | throw new ServiceException("åè¡¥åºå失败ï¼" + ex.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private void addInventoryRecordOnly(Long recordId, |
| | | Long productModelId, |
| | | String batchNo, |
| | | BigDecimal quantity, |
| | | String stockInRecordType) { |
| | | // ä»
è®°å½å
¥åºç³è¯·ï¼ä¸åå®¡æ ¸éè¿ã |
| | | BigDecimal addQuantity = defaultDecimal(quantity); |
| | | if (addQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return; |
| | | } |
| | | try { |
| | | StockInventoryDto stockInventoryDto = new StockInventoryDto(); |
| | | stockInventoryDto.setProductModelId(productModelId); |
| | | stockInventoryDto.setBatchNo(batchNo); |
| | | stockInventoryDto.setQualitity(addQuantity); |
| | | stockInventoryDto.setRecordType(stockInRecordType); |
| | | stockInventoryDto.setRecordId(recordId == null ? 0L : recordId); |
| | | stockInventoryService.addStockInRecordOnly(stockInventoryDto); |
| | | } catch (ServiceException ex) { |
| | | throw ex; |
| | | } catch (Exception ex) { |
| | | throw new ServiceException("éæå
¥åºè®°å½ä¿å失败ï¼" + ex.getMessage()); |
| | | } |
| | | } |
| | | |
| | | private List<ProductionOrderPickDto> resolvePickItems(ProductionOrderPickDto dto) { |
| | | // è§£ææ°å¢åºæ¯ç颿æç»éåã |
| | | if (dto == null) { |
| | | throw new ServiceException("颿忰ä¸è½ä¸ºç©º"); |
| | | throw new ServiceException("åæ°ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (dto.getPickList() != null && !dto.getPickList().isEmpty()) { |
| | | return dto.getPickList(); |
| | |
| | | } |
| | | |
| | | private List<ProductionOrderPickDto> resolveUpdateItems(ProductionOrderPickDto dto) { |
| | | // è§£ææ´æ°åºæ¯ç颿æç»éåã |
| | | if (dto.getPickList() != null) { |
| | | return dto.getPickList(); |
| | | } |
| | |
| | | } |
| | | |
| | | private boolean isEmptyUpdateItem(ProductionOrderPickDto dto) { |
| | | // å¤ææ´æ°è¡æ¯å¦ä¸ºç©ºç½å ä½è¡ã |
| | | return dto.getId() == null |
| | | && dto.getProductModelId() == null |
| | | && dto.getPickQuantity() == null |
| | |
| | | } |
| | | |
| | | private Long resolveProductionOrderId(ProductionOrderPickDto dto) { |
| | | // ä¼å
ä»ä¸»DTOè§£æè®¢åIDï¼ä¸å卿¶åä»å项ä¸å鿥æ¾ã |
| | | if (dto.getProductionOrderId() != null) { |
| | | return dto.getProductionOrderId(); |
| | | } |
| | |
| | | } |
| | | |
| | | private ProductionOrderPickDto mergeDto(ProductionOrderPickDto rootDto, ProductionOrderPickDto itemDto) { |
| | | // åå¹¶è§åï¼ |
| | | // - itemDto ä¼å
æ¿è½½è¡çº§è¾å
¥ï¼ |
| | | // - itemDto ç¼ºå¤±åæ®µä» rootDto å
åºç»§æ¿ï¼ |
| | | // - è¾åº merged ä½ä¸ºç»ä¸ä¸å¡å
¥åã |
| | | ProductionOrderPickDto merged = new ProductionOrderPickDto(); |
| | | // å
æ·è´è¡çº§å段ã |
| | | if (itemDto != null) { |
| | | merged.setId(itemDto.getId()); |
| | | merged.setProductionOrderId(itemDto.getProductionOrderId()); |
| | |
| | | } |
| | | |
| | | private void validatePickParam(ProductionOrderPickDto dto, int rowNo) { |
| | | // æ ¡éªæ®é颿忰ï¼è®¢åãè§æ ¼ãæ°éãç±»åï¼ã |
| | | if (dto.getProductionOrderId() == null) { |
| | | throw new ServiceException("第" + rowNo + "æ¡ç产订åIDä¸è½ä¸ºç©º"); |
| | | throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼ç产订åIDä¸è½ä¸ºç©º"); |
| | | } |
| | | if (dto.getProductModelId() == null) { |
| | | throw new ServiceException("第" + rowNo + "æ¡äº§åè§æ ¼IDä¸è½ä¸ºç©º"); |
| | | throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼äº§åè§æ ¼ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (dto.getPickQuantity() == null || dto.getPickQuantity().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢ææ°éä¸è½å°äº0"); |
| | | throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼é¢ææ°éä¸è½å°äº0"); |
| | | } |
| | | if (dto.getPickType() != null && dto.getPickType() != PICK_TYPE_NORMAL && dto.getPickType() != PICK_TYPE_FEEDING) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢æç±»ååªè½æ¯1æ2"); |
| | | throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼é¢æç±»åä»
æ¯æ1(颿)æ2(è¡¥æ)"); |
| | | } |
| | | } |
| | | |
| | | private void validateFeedingParam(ProductionOrderPickDto dto, int rowNo) { |
| | | // æ ¡éªè¡¥æåæ°ï¼è®¢åã颿IDãè¡¥ææ°éãç±»åï¼ã |
| | | if (dto.getProductionOrderId() == null) { |
| | | throw new ServiceException("第" + rowNo + "æ¡ç产订åIDä¸è½ä¸ºç©º"); |
| | | throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼ç产订åIDä¸è½ä¸ºç©º"); |
| | | } |
| | | if (dto.getId() == null) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢æIDä¸è½ä¸ºç©º"); |
| | | throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼é¢æè®°å½IDä¸è½ä¸ºç©º"); |
| | | } |
| | | if (dto.getFeedingQuantity() == null || dto.getFeedingQuantity().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡æ¬æ¬¡è¡¥ææ°éä¸è½å°äº0"); |
| | | throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼è¡¥ææ°éä¸è½å°äº0"); |
| | | } |
| | | if (!isFeedingPick(dto)) { |
| | | throw new ServiceException("第" + rowNo + "æ¡è¡¥æç±»åå¿
须为2"); |
| | | throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼è¡¥æåºæ¯ä¸é¢æç±»åå¿
须为2"); |
| | | } |
| | | } |
| | | |
| | | private void validateReturnParam(ProductionOrderPickDto dto, int rowNo) { |
| | | // æ ¡éªéæåæ°ï¼è®¢åã颿IDãéæéï¼ã |
| | | if (dto.getProductionOrderId() == null) { |
| | | throw new ServiceException("第" + rowNo + "æ¡ç产订åIDä¸è½ä¸ºç©º"); |
| | | throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼ç产订åIDä¸è½ä¸ºç©º"); |
| | | } |
| | | if (dto.getId() == null) { |
| | | throw new ServiceException("第" + rowNo + "æ¡é¢æIDä¸è½ä¸ºç©º"); |
| | | throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼é¢æè®°å½IDä¸è½ä¸ºç©º"); |
| | | } |
| | | if (dto.getReturnQty() == null || dto.getReturnQty().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡éææ°éä¸è½ä¸ºç©ºä¸ä¸è½å°äº0"); |
| | | } |
| | | if (dto.getActualQty() == null || dto.getActualQty().compareTo(BigDecimal.ZERO) < 0) { |
| | | throw new ServiceException("第" + rowNo + "æ¡å®é
æ°éä¸è½ä¸ºç©ºä¸ä¸è½å°äº0"); |
| | | throw new ServiceException("第" + rowNo + "è¡åæ°é误ï¼éææ°éä¸è½å°äº0"); |
| | | } |
| | | } |
| | | |
| | | private boolean isFeedingRequest(ProductionOrderPickDto dto) { |
| | | // 夿å½åè¯·æ±æ¯å¦å±äºè¡¥ææµç¨ã |
| | | if (isFeedingPick(dto)) { |
| | | return true; |
| | | } |
| | |
| | | } |
| | | |
| | | private boolean isFeedingPick(ProductionOrderPickDto dto) { |
| | | // 夿å½åè¡æ¯å¦ä¸ºè¡¥æç±»åã |
| | | return dto != null && Objects.equals(dto.getPickType(), PICK_TYPE_FEEDING); |
| | | } |
| | | |
| | | private boolean isReturnRequest(ProductionOrderPickDto dto) { |
| | | // 夿å½åè¯·æ±æ¯å¦å±äºéææµç¨ã |
| | | if (isReturnPick(dto)) { |
| | | return true; |
| | | } |
| | |
| | | } |
| | | |
| | | private boolean isReturnPick(ProductionOrderPickDto dto) { |
| | | // 夿å½åè¡æ¯å¦ä¸ºéæç±»åã |
| | | return dto != null && Boolean.TRUE.equals(dto.getReturned()); |
| | | } |
| | | |
| | | private BigDecimal sumFeedingQuantity(Long productionOrderId, Long pickId) { |
| | | // æ±æ»æå®é¢æåçåå²è¡¥ææ»éã |
| | | List<ProductionOrderPickRecord> feedingRecords = productionOrderPickRecordMapper.selectList( |
| | | Wrappers.<ProductionOrderPickRecord>lambdaQuery() |
| | | .eq(ProductionOrderPickRecord::getProductionOrderId, productionOrderId) |
| | |
| | | } |
| | | |
| | | private BigDecimal calculateActualQty(ProductionOrderPick pick, BigDecimal feedingQty) { |
| | | // æâ颿+è¡¥æ-éæâ计ç®å®é
ç¨éã |
| | | return defaultDecimal(pick.getQuantity()) |
| | | .add(defaultDecimal(feedingQty)) |
| | | .subtract(defaultDecimal(pick.getReturnQty())); |
| | | } |
| | | |
| | | private String normalizeBatchNo(String batchNo) { |
| | | // æ ååæ¹æ¬¡å·ï¼å»ç©ºç½ã空串转nullï¼ã |
| | | if (StringUtils.isEmpty(batchNo)) { |
| | | return null; |
| | | } |
| | |
| | | return trimBatchNo.isEmpty() ? null : trimBatchNo; |
| | | } |
| | | private List<String> resolveBatchNoList(ProductionOrderPickDto dto) { |
| | | // ä¼å
è§£æ batchNoListï¼ç©ºååéè§£æ batchNo å符串ã |
| | | List<String> normalizedBatchNoList = normalizeBatchNoList(dto.getBatchNoList()); |
| | | if (!normalizedBatchNoList.isEmpty()) { |
| | | return normalizedBatchNoList; |
| | |
| | | } |
| | | |
| | | private String pickInventoryBatchNo(List<String> batchNoList) { |
| | | // 仿¹æ¬¡éåä¸ååºåæ£å使ç¨çæ¹æ¬¡ã |
| | | if (batchNoList == null || batchNoList.isEmpty()) { |
| | | return null; |
| | | } |
| | |
| | | } |
| | | |
| | | private String resolveInventoryBatchNoFromStored(String storedBatchNo) { |
| | | // 仿°æ®åºå卿¹æ¬¡å段ä¸åè§£å¯ç¨æ¹æ¬¡ã |
| | | return pickInventoryBatchNo(parseBatchNoValue(storedBatchNo)); |
| | | } |
| | | |
| | | private String formatBatchNoStorage(List<String> batchNoList) { |
| | | // å°æ¹æ¬¡éåæ ¼å¼åä¸ºæ°æ®åºåå¨å¼ã |
| | | if (batchNoList == null || batchNoList.isEmpty()) { |
| | | return null; |
| | | } |
| | |
| | | } |
| | | |
| | | private List<String> normalizeBatchNoList(List<String> batchNoList) { |
| | | // æ¹éæ ååæ¹æ¬¡å·å¹¶å»éã |
| | | if (batchNoList == null || batchNoList.isEmpty()) { |
| | | return Collections.emptyList(); |
| | | } |
| | |
| | | } |
| | | |
| | | private void fillBatchNoList(List<ProductionOrderPickVo> detailList) { |
| | | // å°å订å+åè§æ ¼+åå·¥åºçæ°æ®æç»èåæ¹æ¬¡ï¼ä¾¿äºå端ç»ä¸å±ç¤ºã |
| | | if (detailList == null || detailList.isEmpty()) { |
| | | return; |
| | | } |
| | |
| | | } |
| | | |
| | | private void fillSelectableBatchNoList(List<ProductionOrderPickVo> detailList) { |
| | | // åå¹¶âå·²éæ¹æ¬¡âåâåºåå¯éæ¹æ¬¡âï¼ç¨äºåç«¯ä¸æã |
| | | if (detailList == null || detailList.isEmpty()) { |
| | | return; |
| | | } |
| | | // å
æ¶éæç»ä¸æ¶åçè§æ ¼IDï¼æ¹éæ¥è¯¢åºåæ¹æ¬¡ã |
| | | Set<Long> productModelIdSet = detailList.stream() |
| | | .map(ProductionOrderPickVo::getProductModelId) |
| | | .filter(Objects::nonNull) |
| | |
| | | } |
| | | |
| | | private String buildBatchNoGroupKey(ProductionOrderPickVo detail) { |
| | | return String.valueOf(detail.getProductionOrderId()) + "|" |
| | | + String.valueOf(detail.getProductModelId()) + "|" |
| | | + String.valueOf(detail.getTechnologyOperationId()) + "|" |
| | | + String.valueOf(detail.getOperationName()); |
| | | // æå»ºæ¹æ¬¡èååç»é®ã |
| | | return detail.getProductionOrderId() + "|" |
| | | + detail.getProductModelId() + "|" |
| | | + detail.getTechnologyOperationId() + "|" |
| | | + detail.getOperationName(); |
| | | } |
| | | |
| | | private List<String> parseBatchNoValue(String rawBatchNoValue) { |
| | | // æ¹æ¬¡è§£æå
¼å®¹ä¸ç§æ ¼å¼ï¼ |
| | | // 1) åå¼ï¼A001 |
| | | // 2) éå·åéï¼A001,A002 |
| | | // 3) ç±»JSONæ°ç»å符串ï¼["A001","A002"] |
| | | String normalizedValue = normalizeBatchNo(rawBatchNoValue); |
| | | if (StringUtils.isEmpty(normalizedValue)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | if (normalizedValue.startsWith("[") && normalizedValue.endsWith("]")) { |
| | | if (normalizedValue != null && normalizedValue.startsWith("[") && normalizedValue.endsWith("]")) { |
| | | String value = normalizedValue.substring(1, normalizedValue.length() - 1); |
| | | if (StringUtils.isEmpty(value)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | List<String> parsed = Arrays.stream(value.split(",")) |
| | | .map(item -> item == null ? null : item.trim().replace("\"", "").replace("'", "")) |
| | | .map(item -> item.trim().replace("\"", "").replace("'", "")) |
| | | .collect(Collectors.toList()); |
| | | return normalizeBatchNoList(parsed); |
| | | } |
| | | if (normalizedValue.contains(",")) { |
| | | if (normalizedValue != null && normalizedValue.contains(",")) { |
| | | List<String> parsed = Arrays.stream(normalizedValue.split(",")) |
| | | .map(item -> item == null ? null : item.trim()) |
| | | .map(item -> item.trim()) |
| | | .collect(Collectors.toList()); |
| | | return normalizeBatchNoList(parsed); |
| | | } |
| | |
| | | } |
| | | |
| | | private LambdaQueryWrapper<StockInventory> buildStockWrapper(Long productModelId, String batchNo) { |
| | | // æå»ºåºåæ¥è¯¢æ¡ä»¶ï¼è§æ ¼ + æ¹æ¬¡ï¼ã |
| | | LambdaQueryWrapper<StockInventory> wrapper = Wrappers.<StockInventory>lambdaQuery() |
| | | .eq(StockInventory::getProductModelId, productModelId); |
| | | if (StringUtils.isEmpty(batchNo)) { |
| | |
| | | } |
| | | |
| | | private BigDecimal defaultDecimal(BigDecimal value) { |
| | | // BigDecimal 空å¼å
åºï¼ç»ä¸æ0å¤çã |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private String formatQuantity(BigDecimal value) { |
| | | // æ°éæ ¼å¼åè¾åºï¼å»é¤æ«å°¾æ æ0ï¼ã |
| | | return defaultDecimal(value).stripTrailingZeros().toPlainString(); |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | public List<ProductionOrderRoutingOperationParamVo> listProductionOrderRoutingOperationParam(ProductionOrderRoutingOperationParamDto dto) { |
| | | // æ¥è¯¢ç产订åå·¥èºè·¯çº¿å·¥åºåæ° |
| | | return BeanUtil.copyToList(this.list(buildQueryWrapper(dto)), ProductionOrderRoutingOperationParamVo.class); |
| | | } |
| | | |
| | | @Override |
| | | public ProductionOrderRoutingOperationParamVo getProductionOrderRoutingOperationParamInfo(Long id) { |
| | | // è·åç产订åå·¥èºè·¯çº¿å·¥åºåæ°è¯¦æ
|
| | | ProductionOrderRoutingOperationParam item = this.getById(id); |
| | | return item == null ? null : BeanUtil.copyProperties(item, ProductionOrderRoutingOperationParamVo.class); |
| | | } |
| | | |
| | | @Override |
| | | public boolean saveProductionOrderRoutingOperationParam(ProductionOrderRoutingOperationParam item) { |
| | | // ä¿åç产订åå·¥èºè·¯çº¿å·¥åºåæ° |
| | | ProductionOrderRoutingOperation routingOperation = getRoutingOperation(item.getProductionOrderRoutingOperationId()); |
| | | fillFromSourceParam(item, routingOperation); |
| | | validateManualFields(item); |
| | |
| | | |
| | | @Override |
| | | public boolean removeProductionOrderRoutingOperationParam(Long id) { |
| | | // å é¤ç产订åå·¥èºè·¯çº¿å·¥åºåæ° |
| | | return this.removeById(id); |
| | | } |
| | | |
| | | private LambdaQueryWrapper<ProductionOrderRoutingOperationParam> buildQueryWrapper(ProductionOrderRoutingOperationParamDto dto) { |
| | | // ææ¡ä»¶å¨ææå»ºæ°æ®åºæ¥è¯¢æ¡ä»¶ |
| | | ProductionOrderRoutingOperationParam query = dto == null ? new ProductionOrderRoutingOperationParam() : dto; |
| | | return Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery() |
| | | .eq(query.getId() != null, ProductionOrderRoutingOperationParam::getId, query.getId()) |
| | |
| | | } |
| | | |
| | | private ProductionOrderRoutingOperation getRoutingOperation(Long productionOrderRoutingOperationId) { |
| | | // è·åå·¥èºè·¯çº¿å·¥åº |
| | | if (productionOrderRoutingOperationId == null) { |
| | | throw new ServiceException("productionOrderRoutingOperationId is required"); |
| | | throw new ServiceException("ç产订åå·¥èºè·¯çº¿å·¥åºIDä¸è½ä¸ºç©º"); |
| | | } |
| | | ProductionOrderRoutingOperation routingOperation = productionOrderRoutingOperationMapper.selectById(productionOrderRoutingOperationId); |
| | | if (routingOperation == null) { |
| | | throw new ServiceException("Production order routing operation not found"); |
| | | throw new ServiceException("ç产订åå·¥èºè·¯çº¿å·¥åºä¸åå¨"); |
| | | } |
| | | return routingOperation; |
| | | } |
| | | |
| | | private void fillFromSourceParam(ProductionOrderRoutingOperationParam item, ProductionOrderRoutingOperation routingOperation) { |
| | | // 仿¥æºåæ°åå¡«å½ååæ°é»è®¤å¼ |
| | | item.setProductionOrderId(routingOperation.getProductionOrderId()); |
| | | item.setProductionOrderRoutingOperationId(routingOperation.getId()); |
| | | ProductionOrder productionOrder = productionOrderMapper.selectById(routingOperation.getProductionOrderId()); |
| | | if (productionOrder == null) { |
| | | throw new ServiceException("Production order not found"); |
| | | throw new ServiceException("ç产订åä¸åå¨"); |
| | | } |
| | | if (item.getParamId() == null) { |
| | | return; |
| | | } |
| | | TechnologyParam sourceParam = technologyParamMapper.selectById(item.getParamId()); |
| | | if (sourceParam == null) { |
| | | throw new ServiceException("Technology param not found"); |
| | | throw new ServiceException("å·¥èºåæ°ä¸åå¨"); |
| | | } |
| | | if (item.getTechnologyOperationParamId() != null) { |
| | | TechnologyRoutingOperationParam sourceRoutingOperationParam = technologyRoutingOperationParamMapper.selectById(item.getTechnologyOperationParamId()); |
| | |
| | | } |
| | | |
| | | private void validateManualFields(ProductionOrderRoutingOperationParam item) { |
| | | // æ ¡éªæå·¥å½å
¥å段çå¿
填䏿 ¼å¼ |
| | | if (item.getParamCode() == null || item.getParamCode().trim().isEmpty()) { |
| | | throw new ServiceException("paramCode is required"); |
| | | throw new ServiceException("åæ°ç¼ç ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (item.getParamName() == null || item.getParamName().trim().isEmpty()) { |
| | | throw new ServiceException("paramName is required"); |
| | | throw new ServiceException("åæ°åç§°ä¸è½ä¸ºç©º"); |
| | | } |
| | | } |
| | | |
| | | private void checkDuplicate(ProductionOrderRoutingOperationParam item) { |
| | | // æ£æ¥æ°æ®æ¯å¦éå¤ï¼é¿å
éå¤ä¿å |
| | | boolean duplicate = productionOrderRoutingOperationParamMapper.selectCount( |
| | | Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery() |
| | | .isNull(ProductionOrderRoutingOperationParam::getProductionProductMainId) |
| | |
| | | .ne(item.getId() != null, ProductionOrderRoutingOperationParam::getId, item.getId()) |
| | | ) > 0; |
| | | if (duplicate) { |
| | | throw new ServiceException("Duplicate production order routing operation param"); |
| | | throw new ServiceException("ç产订åå·¥èºè·¯çº¿å·¥åºåæ°éå¤"); |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | public R addRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation) { |
| | | // æ°å¢å·¥èºè·¯çº¿ |
| | | int insert = productionOrderRoutingOperationMapper.insert(productionOrderRoutingOperation); |
| | | //å·¥åºå
³èçåæ°éè¦åæ¥æ°å¢ |
| | | List<TechnologyOperationParam> technologyOperationParams = technologyOperationParamMapper.selectList(Wrappers.<TechnologyOperationParam>lambdaQuery() |
| | | .eq(TechnologyOperationParam::getTechnologyOperationId, productionOrderRoutingOperation.getTechnologyOperationId())); |
| | | // åæ°ä¸åç½®æ¡ä»¶æ ¡éª |
| | | if (CollectionUtils.isNotEmpty(technologyOperationParams)){ |
| | | ArrayList<ProductionOrderRoutingOperationParam> productionOrderRoutingOperationParams = new ArrayList<>(); |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | for (TechnologyOperationParam technologyOperationParam : technologyOperationParams) { |
| | | TechnologyParam technologyParam = technologyParamMapper.selectById(technologyOperationParam.getTechnologyParamId()); |
| | | ProductionOrderRoutingOperationParam productionOrderRoutingOperationParam = new ProductionOrderRoutingOperationParam(); |
| | |
| | | |
| | | @Override |
| | | public R deleteRouteItem(Long id) { |
| | | // å é¤å·¥èºè·¯çº¿ |
| | | try { |
| | | // æ¥è¯¢å¹¶åå¤ä¸å¡æ°æ® |
| | | ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectOne( |
| | | new LambdaQueryWrapper<ProductionOperationTask>() |
| | | .eq(ProductionOperationTask::getProductionOrderRoutingOperationId, id) |
| | | .last("limit 1")); |
| | | // åæ°ä¸åç½®æ¡ä»¶æ ¡éª |
| | | if (productionOperationTask == null) { |
| | | throw new RuntimeException("å é¤å¤±è´¥ï¼æªæ¾å°å
³èçç产工å"); |
| | | } |
| | |
| | | List<ProductionProductMain> productionProductMains = productionProductMainMapper.selectList( |
| | | new LambdaQueryWrapper<ProductionProductMain>() |
| | | .eq(ProductionProductMain::getProductionOperationTaskId, productionOperationTask.getId())); |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | for (ProductionProductMain main : productionProductMains) { |
| | | productionProductMainService.removeProductMain(main.getId()); |
| | | } |
| | |
| | | ProductionOrderRoutingOperation item = operationList.get(i); |
| | | if (!Integer.valueOf(i + 1).equals(item.getDragSort())) { |
| | | item.setDragSort(i + 1); |
| | | // æä¹
åæè¾åºå¤çç»æ |
| | | productionOrderRoutingOperationMapper.updateById(item); |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | public int sortRouteItem(ProductionOrderRoutingOperation productionOrderRoutingOperation) { |
| | | // æåºå·¥èºè·¯çº¿ |
| | | ProductionOrderRoutingOperation oldItem = productionOrderRoutingOperationMapper.selectById(productionOrderRoutingOperation.getId()); |
| | | List<ProductionOrderRoutingOperation> operationList = productionOrderRoutingOperationMapper.selectList( |
| | | Wrappers.<ProductionOrderRoutingOperation>lambdaQuery() |
| | |
| | | |
| | | @Override |
| | | public ProductionOrderRouting listMain(Long orderId) { |
| | | // æ¥è¯¢ä¸»è¡¨IDéå |
| | | return productionOrderRoutingMapper.selectOne( |
| | | Wrappers.<ProductionOrderRouting>lambdaQuery() |
| | | .eq(ProductionOrderRouting::getProductionOrderId, orderId) |
| | |
| | | |
| | | @Override |
| | | public List<ProductionOrderRoutingOperationVo> listItem(Long orderId) { |
| | | // æ¥è¯¢å·¥èºè·¯çº¿å·¥åºæç» |
| | | return productionOrderRoutingOperationMapper.selectVoListByOrderId(orderId); |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | public IPage<ProductionOrderVo> pageProductionOrder(Page<ProductionOrderDto> page, ProductionOrderDto dto) { |
| | | // å页æ¥è¯¢ç产订å |
| | | Page<ProductionOrderVo> result = (Page<ProductionOrderVo>) baseMapper.pageProductionOrder(page, dto); |
| | | fillProductImages(result.getRecords()); |
| | | return result; |
| | |
| | | |
| | | @Override |
| | | public List<ProductionOrderVo> listProductionOrder(ProductionOrderDto dto) { |
| | | // æ¥è¯¢ç产订åå表 |
| | | List<ProductionOrderVo> records = baseMapper.listProductionOrder(dto); |
| | | fillProductImages(records); |
| | | return records; |
| | |
| | | |
| | | @Override |
| | | public ProductionOrderVo getProductionOrderInfo(Long id) { |
| | | // è·åç产订å详æ
|
| | | ProductionOrderVo item = baseMapper.getProductionOrderInfo(id); |
| | | if (item == null) { |
| | | return null; |
| | |
| | | |
| | | @Override |
| | | public boolean saveProductionOrder(ProductionOrder productionOrder) { |
| | | // ä¿åç产订å |
| | | ProductionOrder oldOrder = productionOrder.getId() == null ? null : this.getById(productionOrder.getId()); |
| | | // ä¸åå
¥å£ç»ä¸è¡¥é½æ¥æºåæ®ã计ååå·¥èºä¿¡æ¯ï¼é¿å
å端åå«ä¼ å¤å¥å段ã |
| | | validateAndFillOrder(productionOrder, oldOrder); |
| | |
| | | |
| | | @Override |
| | | public boolean removeProductionOrder(List<Long> ids) { |
| | | // å é¤ç产订å |
| | | if (ids == null || ids.isEmpty()) { |
| | | return false; |
| | | } |
| | |
| | | |
| | | @Override |
| | | public Integer bindingRoute(ProductionOrderDto productionOrderDto) { |
| | | // 为订åç»å®å·¥èºè·¯çº¿ |
| | | if (productionOrderDto == null || productionOrderDto.getId() == null) { |
| | | throw new ServiceException("ç产订åIDä¸è½ä¸ºç©º"); |
| | | } |
| | |
| | | |
| | | @Override |
| | | public List<ProductionPlanVo> getSource(Long id) { |
| | | // æ¥è¯¢è®¢åå
³èæ¥æºè®¡å |
| | | ProductionOrder productionOrder = baseMapper.selectById(id); |
| | | if (productionOrder != null && productionOrder.getProductionPlanIds() != null) { |
| | | List<Long> planIds = parsePlanIds(productionOrder.getProductionPlanIds()); |
| | |
| | | |
| | | @Override |
| | | public int syncProductionOrderSnapshot(Long productionOrderId) { |
| | | // åæ¥è®¢åå·¥èºãå·¥åºãåæ°åBOMå¿«ç
§ |
| | | ProductionOrder productionOrder = this.getById(productionOrderId); |
| | | // åæ°ä¸åç½®æ¡ä»¶æ ¡éª |
| | | if (productionOrder == null) { |
| | | throw new ServiceException("ç产订åä¸åå¨"); |
| | | } |
| | |
| | | clearProductionSnapshot(productionOrderId); |
| | | ProductionOrderBom orderBom = syncProductionOrderBomSnapshot(productionOrder, technologyRouting); |
| | | |
| | | //ç产订åå·¥èºè·¯çº¿è¡¨ |
| | | ProductionOrderRouting orderRouting = new ProductionOrderRouting(); |
| | | orderRouting.setProductionOrderId(productionOrder.getId()); |
| | | orderRouting.setTechnologyRoutingId(technologyRouting.getId()); |
| | |
| | | productionOrderRoutingMapper.insert(orderRouting); |
| | | |
| | | int syncedParamCount = 0; |
| | | // æ¥è¯¢å¹¶åå¤ä¸å¡æ°æ® |
| | | List<TechnologyRoutingOperation> routingOperations = technologyRoutingOperationMapper.selectList( |
| | | Wrappers.<TechnologyRoutingOperation>lambdaQuery() |
| | | .eq(TechnologyRoutingOperation::getTechnologyRoutingId, technologyRouting.getId()) |
| | | .orderByDesc(TechnologyRoutingOperation::getDragSort) |
| | | .orderByDesc(TechnologyRoutingOperation::getId)); |
| | | Map<Long, String> operationNameMap = technologyOperationMapper.selectBatchIds( |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | routingOperations.stream() |
| | | .map(TechnologyRoutingOperation::getTechnologyOperationId) |
| | | .filter(Objects::nonNull) |
| | |
| | | targetOperation.setIsQuality(sourceOperation.getIsQuality()); |
| | | targetOperation.setOperationName(operationNameMap.get(sourceOperation.getTechnologyOperationId())); |
| | | targetOperation.setTechnologyOperationId(sourceOperation.getTechnologyOperationId()); |
| | | targetOperation.setType(sourceOperation.getType()); |
| | | productionOrderRoutingOperationMapper.insert(targetOperation); |
| | | |
| | | boolean isLastOperation = lastDragSort != null && Objects.equals(sourceOperation.getDragSort(), lastDragSort); |
| | |
| | | } |
| | | |
| | | private ProductionOrderBom syncProductionOrderBomSnapshot(ProductionOrder productionOrder, TechnologyRouting technologyRouting) { |
| | | // åæ¥è®¢åBOMå¿«ç
§ç»æ |
| | | if (technologyRouting.getBomId() == null) { |
| | | return null; |
| | | } |
| | |
| | | if (technologyBom == null) { |
| | | throw new ServiceException("å·¥èºBOMä¸åå¨"); |
| | | } |
| | | // æ¥è¯¢å¹¶åå¤ä¸å¡æ°æ® |
| | | List<TechnologyBomStructure> structureList = technologyBomStructureMapper.selectList( |
| | | Wrappers.<TechnologyBomStructure>lambdaQuery() |
| | | .eq(TechnologyBomStructure::getBomId, technologyBom.getId()) |
| | | .orderByAsc(TechnologyBomStructure::getId)); |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | TechnologyBomStructure root = structureList.stream().filter(item -> item.getParentId() == null).findFirst().orElse(null); |
| | | BigDecimal orderQuantity = defaultDecimal(productionOrder.getQuantity()); |
| | | |
| | |
| | | orderBom.setRemark(technologyBom.getRemark()); |
| | | orderBom.setBomNo(technologyBom.getBomNo()); |
| | | orderBom.setVersion(technologyBom.getVersion()); |
| | | // æä¹
åæè¾åºå¤çç»æ |
| | | productionOrderBomMapper.insert(orderBom); |
| | | |
| | | Map<Long, Long> idMap = new HashMap<>(); |
| | |
| | | } |
| | | |
| | | private void clearProductionSnapshot(Long productionOrderId) { |
| | | // 已产çé¢æè®°å½åç¦æ¢é建ï¼é¿å
夿/ææä¾æ®ä¸è®¢åå¿«ç
§è±èã |
| | | // æ¸
ç订åå·²çæçå·¥èºä¸BOMå¿«ç
§æ°æ® |
| | | boolean hasPickRecord = productionOrderPickRecordMapper.selectCount( |
| | | // æ¥è¯¢å¹¶åå¤ä¸å¡æ°æ® |
| | | Wrappers.<ProductionOrderPickRecord>lambdaQuery() |
| | | .eq(ProductionOrderPickRecord::getProductionOrderId, productionOrderId)) > 0; |
| | | // åæ°ä¸åç½®æ¡ä»¶æ ¡éª |
| | | if (hasPickRecord) { |
| | | throw new ServiceException("ç产订åå·²åå¨é¢æè®°å½ï¼ä¸è½éæ°çæå¿«ç
§"); |
| | | } |
| | | List<Long> taskIds = productionOperationTaskMapper.selectList( |
| | | Wrappers.<ProductionOperationTask>lambdaQuery() |
| | | .eq(ProductionOperationTask::getProductionOrderId, productionOrderId)) |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | .stream().map(ProductionOperationTask::getId).collect(Collectors.toList()); |
| | | if (!taskIds.isEmpty()) { |
| | | // å·²ææ¥å·¥è®°å½è¯´æè®¢åå·²å¼å·¥ï¼æ¤æ¶ä¸å
许åé建快ç
§ã |
| | |
| | | } |
| | | |
| | | private LambdaQueryWrapper<ProductionOrder> buildQueryWrapper(ProductionOrderDto dto) { |
| | | // ææ¡ä»¶å¨ææå»ºæ°æ®åºæ¥è¯¢æ¡ä»¶ |
| | | ProductionOrder query = dto == null ? new ProductionOrder() : dto; |
| | | return Wrappers.<ProductionOrder>lambdaQuery() |
| | | .eq(query.getId() != null, ProductionOrder::getId, query.getId()) |
| | |
| | | } |
| | | |
| | | private String generateNextOrderNo() { |
| | | // çæä¸ä¸ä¸ªç产订åå· |
| | | String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | String prefix = "SC" + datePrefix; |
| | | ProductionOrder latestOrder = this.getOne(Wrappers.<ProductionOrder>lambdaQuery() |
| | |
| | | } |
| | | |
| | | private String generateNextTaskNo() { |
| | | // çæä¸ä¸ä¸ªç产工åå· |
| | | String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | String prefix = "GD" + datePrefix; |
| | | ProductionOperationTask lastTask = productionOperationTaskMapper.selectOne( |
| | |
| | | } |
| | | |
| | | private BigDecimal defaultDecimal(BigDecimal value) { |
| | | // å°ç©ºæ°éå
åºä¸º0ï¼é¿å
空æéå¼å¸¸ |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private void validateAndFillOrder(ProductionOrder productionOrder, ProductionOrder oldOrder) { |
| | | // æ ¡éªè®¢ååæ°å¹¶è¡¥é½é»è®¤å¼ |
| | | if (productionOrder == null) { |
| | | throw new ServiceException("ç产订åä¸è½ä¸ºç©º"); |
| | | } |
| | |
| | | } |
| | | |
| | | private void fillFromProductionPlans(ProductionOrder productionOrder) { |
| | | // ä»å
³èç产计åå填订åå
³é®å段 |
| | | List<Long> planIds = parsePlanIds(productionOrder.getProductionPlanIds()); |
| | | // åæ°ä¸åç½®æ¡ä»¶æ ¡éª |
| | | if (planIds.isEmpty()) { |
| | | return; |
| | | } |
| | |
| | | if (productionPlans.size() != planIds.size()) { |
| | | throw new ServiceException("é¨åç产计åä¸åå¨"); |
| | | } |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | Map<Long, ProductionPlan> planMap = productionPlans.stream() |
| | | .collect(Collectors.toMap(ProductionPlan::getId, item -> item, (left, right) -> left)); |
| | | ProductionPlan mainPlan = planMap.get(planIds.get(0)); |
| | |
| | | } |
| | | |
| | | private void releaseProductionPlanIssueStatus(ProductionOrder productionOrder) { |
| | | // åéç产计åä¸åç¶æ |
| | | if (productionOrder == null) { |
| | | return; |
| | | } |
| | |
| | | |
| | | //ç产订åå é¤ï¼ç产计åçå·²ä¸åæ°é对åºåæ´ |
| | | private void updatePlanIssuedFlag(List<Long> planIds, BigDecimal remainingAssignedQuantity) { |
| | | // æ´æ°è®¡åä¸åæ è®°åä¸åæ°é |
| | | if (planIds == null || planIds.isEmpty()) { |
| | | return; |
| | | } |
| | |
| | | } |
| | | |
| | | private BigDecimal resolveRemainingQuantity(ProductionPlan plan) { |
| | | // 计ç®å½å计åæè®°å½çå©ä½æ°é |
| | | if (plan == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | |
| | | } |
| | | |
| | | private int resolvePlanStatus(BigDecimal requiredQuantity, BigDecimal issuedQuantity) { |
| | | // æ ¹æ®éæ±éåä¸åéæ¨å¯¼è®¡åç¶æ |
| | | if (requiredQuantity == null || requiredQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return 0; |
| | | } |
| | |
| | | } |
| | | |
| | | private List<Long> parsePlanIds(String productionPlanIds) { |
| | | // å°è®¡åIDå符串解æä¸ºLongå表 |
| | | if (productionPlanIds == null || productionPlanIds.trim().isEmpty()) { |
| | | return new ArrayList<>(); |
| | | } |
| | |
| | | } |
| | | |
| | | private String formatPlanIds(List<Long> planIds) { |
| | | // å°è®¡åIDéåæ ¼å¼å为[1,2,3]å符串 |
| | | if (planIds == null || planIds.isEmpty()) { |
| | | return null; |
| | | } |
| | |
| | | } |
| | | |
| | | private LocalDate resolvePlanCompleteDate(ProductionPlan productionPlan) { |
| | | // è§£æè®¡åå®ææ¥æ |
| | | if (productionPlan == null) { |
| | | return null; |
| | | } |
| | |
| | | } |
| | | |
| | | private int compareDecimal(BigDecimal left, BigDecimal right) { |
| | | // å®å
¨æ¯è¾ä¸¤ä¸ªæ°éå¼å¤§å° |
| | | return defaultDecimal(left).compareTo(defaultDecimal(right)); |
| | | } |
| | | |
| | | private void fillProductImages(List<ProductionOrderVo> records) { |
| | | // å¡«å
产åå¾ç |
| | | if (records == null || records.isEmpty()) { |
| | | return; |
| | | } |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | List<Long> productModelIds = records.stream() |
| | | .map(ProductionOrderVo::getProductModelId) |
| | | .filter(Objects::nonNull) |
| | |
| | | return; |
| | | } |
| | | |
| | | // æ¥è¯¢å¹¶åå¤ä¸å¡æ°æ® |
| | | List<StorageAttachment> attachments = storageAttachmentMapper.selectList( |
| | | Wrappers.<StorageAttachment>lambdaQuery() |
| | | .in(StorageAttachment::getRecordId, productModelIds) |
| | |
| | | } |
| | | |
| | | private StorageBlobVO toStorageBlobVO(StorageBlob blob) { |
| | | // å°åå¨æä»¶å¯¹è±¡è½¬æ¢ä¸ºVO |
| | | StorageBlobVO vo = BeanUtil.copyProperties(blob, StorageBlobVO.class); |
| | | vo.setPreviewURL(fileUtil.buildSignedPreviewUrl(vo)); |
| | | vo.setDownloadURL(fileUtil.buildSignedDownloadUrl(vo)); |
| | |
| | | |
| | | @Override |
| | | public ProductionOrderWorkOrderDetailVo getWorkOrderReportInspectDetail(ProductionOrderDto dto) { |
| | | // è·åå·¥åè®¢åæ¥å·¥è´¨æ£æç» |
| | | Long productionOrderId = resolveProductionOrderId(dto); |
| | | ProductionOrderVo orderInfo = getProductionOrderInfo(productionOrderId); |
| | | // åæ°ä¸åç½®æ¡ä»¶æ ¡éª |
| | | if (orderInfo == null) { |
| | | throw new ServiceException("ç产订åä¸åå¨"); |
| | | } |
| | |
| | | new Page<ProductionOperationTaskVo>(1, -1), taskQuery); |
| | | List<ProductionOperationTaskVo> workOrderList = workOrderPage == null || workOrderPage.getRecords() == null |
| | | ? Collections.emptyList() |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | : workOrderPage.getRecords().stream() |
| | | .filter(Objects::nonNull) |
| | | .sorted(Comparator.comparing(ProductionOperationTaskVo::getId, Comparator.nullsLast(Comparator.naturalOrder()))) |
| | | .collect(Collectors.toList()); |
| | | if (workOrderList == null || workOrderList.isEmpty()) { |
| | | .toList(); |
| | | if (workOrderList.isEmpty()) { |
| | | detailVo.setWorkOrderList(Collections.emptyList()); |
| | | return detailVo; |
| | | } |
| | |
| | | .collect(Collectors.toList()); |
| | | List<ProductionProductMain> reportMainList = workOrderIdList.isEmpty() |
| | | ? Collections.emptyList() |
| | | // æ¥è¯¢å¹¶åå¤ä¸å¡æ°æ® |
| | | : productionProductMainMapper.selectList( |
| | | Wrappers.<ProductionProductMain>lambdaQuery() |
| | | .in(ProductionProductMain::getProductionOperationTaskId, workOrderIdList) |
| | |
| | | |
| | | ProductionOrderWorkOrderDetailVo.ReportDetail reportDetail = new ProductionOrderWorkOrderDetailVo.ReportDetail(); |
| | | reportDetail.setReportMain(reportMain); |
| | | reportDetail.setWorkHour(reportMain.getWorkHour()); |
| | | reportDetail.setReportOutputList(reportOutputMap.getOrDefault(reportMainId, Collections.emptyList())); |
| | | reportDetail.setReportParamList(reportParamMap.getOrDefault(reportMainId, Collections.emptyList())); |
| | | reportDetailList.add(reportDetail); |
| | |
| | | inspectDetail.setReportId(reportMainId); |
| | | inspectDetail.setReportNo(reportMain.getProductNo()); |
| | | inspectDetail.setReportMain(reportMain); |
| | | inspectDetail.setWorkHour(reportMain.getWorkHour()); |
| | | inspectDetail.setInspect(inspect); |
| | | inspectDetail.setInspectParamList(inspectParamMap.getOrDefault(inspect.getId(), Collections.emptyList())); |
| | | inspectDetail.setInspectFileList(inspectFileMap.getOrDefault(inspect.getId(), Collections.emptyList())); |
| | |
| | | } |
| | | |
| | | private Long resolveProductionOrderId(ProductionOrderDto dto) { |
| | | // ä»å
¥åä¸è§£æç产订åIDå¹¶æ ¡éª |
| | | if (dto == null) { |
| | | throw new ServiceException("è¯·ä¼ å
¥ç产订åIDæç产订åå·"); |
| | | } |
| | |
| | | |
| | | @Override |
| | | public List<ProductionOrderPickVo> pick(Long productionOrderId) { |
| | | // æ¥è¯¢è®¢åé¢æãææä¸éææç» |
| | | if (productionOrderId == null) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | // æ¥è¯¢å¹¶åå¤ä¸å¡æ°æ® |
| | | ProductionOrderBom orderBom = productionOrderBomMapper.selectOne( |
| | | Wrappers.<ProductionOrderBom>lambdaQuery() |
| | | .eq(ProductionOrderBom::getProductionOrderId, productionOrderId) |
| | |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | List<Long> productModelIds = bomStructureList.stream() |
| | | .map(ProductionBomStructureVo::getProductModelId) |
| | | .filter(Objects::nonNull) |
| | |
| | | |
| | | @Override |
| | | public int updateOrder(ProductionOrderDto productionOrderDto) { |
| | | // æ´æ°ç产订åä¸»æ°æ® |
| | | productionOrderDto.setStatus(5); |
| | | return baseMapper.updateById(productionOrderDto); |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.basic.mapper.ProductMapper; |
| | | import com.ruoyi.basic.mapper.ProductModelMapper; |
| | | import com.ruoyi.basic.pojo.Product; |
| | | import com.ruoyi.basic.pojo.ProductModel; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.common.exception.base.BaseException; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | |
| | | private final ProductionPlanMapper productionPlanMapper; |
| | | private final ProductionOrderMapper productionOrderMapper; |
| | | private final ProductionOrderService productionOrderService; |
| | | private final ProductModelMapper productModelMapper; |
| | | private final ProductMapper productMapper; |
| | | |
| | | @Override |
| | | public IPage<ProductionPlanVo> listPage(Page<ProductionPlanDto> page, ProductionPlanDto productionPlanDto) { |
| | | // å页æ¥è¯¢ä¸»ç产计åå表 |
| | | return productionPlanMapper.listPage(page, productionPlanDto); |
| | | } |
| | | |
| | | /** |
| | | * åå¹¶ç产计åå¹¶ä¸åç产订åã |
| | | * ä¸å¡çº¦æï¼ |
| | | * 约æï¼ |
| | | * 1. ä»
å
许åä¸äº§ååå·ç计ååå¹¶ï¼ |
| | | * 2. å·²ä¸åæé¨åä¸åç计åç¦æ¢å次åå¹¶ï¼ |
| | | * 3. ä¸åæ°éä¸è½å¤§äºæé计åéæ±æ»éï¼ |
| | | * 4. 订åå建ç»ä¸å¤ç¨ ProductionOrderService.saveProductionOrderï¼ç¡®ä¿å·¥èº/BOM/颿䏻åçåç»é»è¾ä¸è´ã |
| | | * 2. å·²ä¸åæé¨åä¸åç计åä¸å
è®¸åæ¬¡åå¹¶ï¼ |
| | | * 3. ä¸åæ°éä¸è½å¤§äºæé计åå©ä½éæ±æ»éï¼ |
| | | * 4. ä¸åæ¶ç»ä¸è°ç¨ ProductionOrderService.saveProductionOrderï¼ç¡®ä¿åç»å·¥èº/BOM/颿é»è¾ä¸è´ã |
| | | */ |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public boolean combine(ProductionPlanDto productionPlanDto) { |
| | | // åºç¡å
¥åæ ¡éªï¼æ²¡æå¯ä¸å计ååç´æ¥è¿å false |
| | | if (productionPlanDto == null || productionPlanDto.getIds() == null || productionPlanDto.getIds().isEmpty()) { |
| | | return false; |
| | | } |
| | | |
| | | // å»ç©ºãå»éï¼å¾å°æ¬æ¬¡åä¸åå¹¶ç计å ID |
| | | List<Long> planIds = productionPlanDto.getIds().stream() |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | |
| | | throw new ServiceException("ä¸åå¤±è´¥ï¼æªéæ©ç产计å"); |
| | | } |
| | | |
| | | // æ¥è¯¢å¹¶æ ¡éªè®¡åæ¯å¦é½åå¨ |
| | | List<ProductionPlanDto> planLists = productionPlanMapper.selectWithMaterialByIds(planIds); |
| | | if (planLists == null || planLists.isEmpty() || planLists.size() != planIds.size()) { |
| | | throw new ServiceException("ä¸å失败ï¼ç产计åä¸åå¨æå·²è¢«å é¤"); |
| | | } |
| | | |
| | | // ä»¥ç¬¬ä¸æ¡è®¡åä½ä¸ºåå·åºå |
| | | ProductionPlanDto firstPlan = planLists.getFirst(); |
| | | if (firstPlan.getProductModelId() == null) { |
| | | throw new ServiceException("ä¸å失败ï¼ç产计å缺å°äº§ååå·"); |
| | | } |
| | | |
| | | // ä»
å
许ååå·è®¡ååå¹¶ä¸å |
| | | boolean hasDifferentModel = planLists.stream() |
| | | .anyMatch(item -> !Objects.equals(item.getProductModelId(), firstPlan.getProductModelId())); |
| | | if (hasDifferentModel) { |
| | | throw new BaseException("åå¹¶å¤±è´¥ï¼æéç产计åç产ååå·ä¸ä¸è´"); |
| | | } |
| | | |
| | | boolean hasIssuedPlan = planLists.stream() |
| | | .anyMatch(item -> item.getStatus() != null && item.getStatus() == PLAN_STATUS_ISSUED); |
| | | if (hasIssuedPlan) { |
| | | throw new BaseException("åå¹¶å¤±è´¥ï¼æéç产计ååå¨å·²ä¸åæé¨åä¸åæ°æ®"); |
| | | // ä»
âå·²ä¸åâ计åä¸å
è®¸åæ¬¡åä¸åå¹¶ä¸åï¼ |
| | | // âå¾
ä¸å/é¨åä¸åâå
许继ç»ä¸åå©ä½æ°éã |
| | | boolean hasFullyIssuedPlan = planLists.stream() |
| | | .anyMatch(item -> item.getStatus() != null |
| | | && item.getStatus() == PLAN_STATUS_ISSUED); |
| | | if (hasFullyIssuedPlan) { |
| | | throw new BaseException("åå¹¶å¤±è´¥ï¼æéç产计ååå¨å·²ä¸åçæ°æ®"); |
| | | } |
| | | |
| | | // è®¡ç®æ¬æ¬¡å¯ä¸åçå©ä½éæ±æ»é |
| | | BigDecimal totalRequiredQuantity = planLists.stream() |
| | | .map(this::resolveRemainingQuantity) |
| | | .reduce(BigDecimal.ZERO, BigDecimal::add); |
| | | if (totalRequiredQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("ä¸åå¤±è´¥ï¼æéç产计åéæ±æ»éå¿
须大äº0"); |
| | | throw new ServiceException("ä¸åå¤±è´¥ï¼æéç产计åå©ä½éæ±æ»éå¿
须大äº0"); |
| | | } |
| | | |
| | | // æ ¡éªä¸åæ°é |
| | | BigDecimal assignedQuantity = productionPlanDto.getTotalAssignedQuantity(); |
| | | if (assignedQuantity == null || assignedQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("ä¸å失败ï¼ä¸åæ°éå¿
须大äº0"); |
| | | } |
| | | if (assignedQuantity.compareTo(totalRequiredQuantity) > 0) { |
| | | throw new ServiceException("ä¸å失败ï¼ä¸åæ°éä¸è½å¤§äºè®¡åéæ±æ»é"); |
| | | throw new ServiceException("ä¸å失败ï¼ä¸åæ°éä¸è½å¤§äºè®¡åå©ä½éæ±æ»é"); |
| | | } |
| | | |
| | | // æè®¡å顺åºåæä¸åæ°éï¼æ¶éå®é
åä¸ä¸åç计å ID |
| | | BigDecimal remainingForOrderBind = assignedQuantity; |
| | | List<Long> issuedPlanIds = new ArrayList<>(); |
| | | for (ProductionPlanDto plan : planLists) { |
| | |
| | | } |
| | | } |
| | | if (issuedPlanIds.isEmpty()) { |
| | | throw new ServiceException("Issue failed, no quantity available for dispatch"); |
| | | throw new ServiceException("ä¸åå¤±è´¥ï¼æ å¯ä¸åæ°é"); |
| | | } |
| | | |
| | | // çæç产订å主åï¼å¹¶ç»å®æ¬æ¬¡ä¸åå
³èç计å |
| | | ProductionOrder productionOrder = new ProductionOrder(); |
| | | productionOrder.setProductionPlanIds(formatPlanIds(issuedPlanIds)); |
| | | productionOrder.setProductModelId(firstPlan.getProductModelId()); |
| | | productionOrder.setQuantity(assignedQuantity); |
| | | productionOrder.setPlanCompleteTime(productionPlanDto.getPlanCompleteTime()); |
| | | |
| | | boolean saved = productionOrderService.saveProductionOrder(productionOrder); |
| | | if (!saved) { |
| | | if (!productionOrderService.saveProductionOrder(productionOrder)) { |
| | | throw new ServiceException("ä¸å失败ï¼ç产订åä¿å失败"); |
| | | } |
| | | |
| | | //å·²ä¸åæ°é |
| | | // å忝æ¡è®¡åç累计ä¸åéåç¶æ |
| | | BigDecimal remainingAssignedQuantity = assignedQuantity; |
| | | List<ProductionPlan> updates = new ArrayList<>(); |
| | | for (ProductionPlanDto plan : planLists) { |
| | |
| | | updates.add(update); |
| | | } |
| | | if (!updates.isEmpty()) { |
| | | // æ¹éæ´æ°è®¡åç¶æä¸æ°é |
| | | this.updateBatchById(updates); |
| | | } |
| | | return true; |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public boolean add(ProductionPlanDto dto) { |
| | | // æ°å¢ä¸»ç产计å |
| | | if (StringUtils.isBlank(dto.getMpsNo())) { |
| | | dto.setMpsNo(generateNextPlanNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")))); |
| | | }else checkMpsNoUnique(dto.getMpsNo(), null); |
| | | String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | dto.setMpsNo(buildPlanNo(datePrefix, resolveNextPlanSequence(datePrefix))); |
| | | } else { |
| | | checkMpsNoUnique(dto.getMpsNo(), null); |
| | | } |
| | | dto.setStatus(PLAN_STATUS_WAIT); |
| | | if (StringUtils.isBlank(dto.getSource())) { |
| | | dto.setSource("å
é¨"); |
| | | } |
| | | return productionPlanMapper.insert(dto) > 0; |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public boolean update(ProductionPlanDto dto) { |
| | | // æ´æ°ä¸»ç产计å |
| | | if (dto == null || dto.getId() == null) { |
| | | throw new ServiceException("ç¼è¾å¤±è´¥ï¼æ°æ®ä¸è½ä¸ºç©º"); |
| | | } |
| | |
| | | return productionPlanMapper.updateById(dto) > 0; |
| | | } |
| | | |
| | | private void checkMpsNoUnique(String mpsNo, Long excludeId) { |
| | | LambdaQueryWrapper<ProductionPlan> wrapper = Wrappers.lambdaQuery(); |
| | | wrapper.eq(ProductionPlan::getMpsNo, mpsNo); |
| | | if (excludeId != null) { |
| | | wrapper.ne(ProductionPlan::getId, excludeId); |
| | | } |
| | | if (productionPlanMapper.selectCount(wrapper) > 0) { |
| | | throw new ServiceException("ç产计åå· " + mpsNo + " å·²åå¨"); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public boolean delete(List<Long> ids) { |
| | | // å é¤ä¸»ç产计å |
| | | if (productionPlanMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery().in(ProductionPlan::getId, ids)) |
| | | .stream() |
| | | .anyMatch(p -> p.getStatus() == PLAN_STATUS_PARTIAL || p.getStatus() == PLAN_STATUS_ISSUED)) { |
| | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void importProdData(MultipartFile file) { |
| | | // åæ°ä¸åç½®æ¡ä»¶æ ¡éª |
| | | if (file == null || file.isEmpty()) { |
| | | throw new ServiceException("导å
¥æ°æ®ä¸è½ä¸ºç©º"); |
| | | } |
| | |
| | | if (list == null || list.isEmpty()) { |
| | | throw new ServiceException("Excelæ²¡ææ°æ®"); |
| | | } |
| | | |
| | | String datePrefix = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd")); |
| | | int nextSequence = resolveNextPlanSequence(datePrefix); |
| | | Set<String> mpsNos = new HashSet<>(); |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | for (int i = 0; i < list.size(); i++) { |
| | | ProductionPlanImportDto dto = list.get(i); |
| | | String mpsNo = dto.getMpsNo(); |
| | | String mpsNo = StringUtils.trim(dto.getMpsNo()); |
| | | if (StringUtils.isEmpty(mpsNo)) { |
| | | generateNextPlanNo(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))); |
| | | mpsNo = buildPlanNo(datePrefix, nextSequence++); |
| | | } |
| | | dto.setMpsNo(mpsNo); |
| | | if (!mpsNos.add(mpsNo)) { |
| | | throw new ServiceException("导å
¥å¤±è´¥ï¼Excel ä¸åå¨éå¤çç³è¯·åç¼å· " + mpsNo); |
| | | throw new ServiceException("导å
¥å¤±è´¥ï¼Excelä¸åå¨éå¤ç主ç产计åå· " + mpsNo); |
| | | } |
| | | if (dto.getQtyRequired() == null || dto.getQtyRequired().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new ServiceException("导å
¥å¤±è´¥ï¼ç¬¬" + (i + 2) + "è¡éæ±æ°éå¿
须大äº0"); |
| | | } |
| | | } |
| | | Long existApplyNoCount = baseMapper.selectCount(Wrappers.<ProductionPlan>lambdaQuery() |
| | | .in(ProductionPlan::getMpsNo, mpsNos)); |
| | | |
| | | // æ¥è¯¢å¹¶åå¤ä¸å¡æ°æ® |
| | | Long existApplyNoCount = baseMapper.selectCount(Wrappers.<ProductionPlan>lambdaQuery().in(ProductionPlan::getMpsNo, mpsNos)); |
| | | if (existApplyNoCount > 0) { |
| | | List<String> existMpsNos = baseMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery() |
| | | .in(ProductionPlan::getMpsNo, mpsNos)) |
| | | List<String> existMpsNos = baseMapper.selectList(Wrappers.<ProductionPlan>lambdaQuery().in(ProductionPlan::getMpsNo, mpsNos)) |
| | | .stream() |
| | | .map(ProductionPlan::getMpsNo) |
| | | .collect(Collectors.toList()); |
| | | throw new ServiceException("导å
¥å¤±è´¥ï¼ç产计åå·å·²åå¨: " + String.join(", ", existMpsNos)); |
| | | throw new ServiceException("导å
¥å¤±è´¥ï¼ä¸»ç产计åå·å·²åå¨: " + String.join(", ", existMpsNos)); |
| | | } |
| | | |
| | | List<ProductModel> allModels = productModelMapper.selectList(Wrappers.<ProductModel>lambdaQuery()); |
| | | Set<Long> productIds = allModels.stream() |
| | | .map(ProductModel::getProductId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Map<Long, String> productNameById = productIds.isEmpty() |
| | | ? Collections.emptyMap() |
| | | : productMapper.selectBatchIds(productIds).stream() |
| | | .collect(Collectors.toMap(Product::getId, Product::getProductName, (a, b) -> a)); |
| | | |
| | | LocalDateTime now = LocalDateTime.now(); |
| | | List<ProductionPlan> entityList = list.stream().map(dto -> { |
| | | List<ProductionPlan> entityList = new ArrayList<>(); |
| | | for (int i = 0; i < list.size(); i++) { |
| | | ProductionPlanImportDto dto = list.get(i); |
| | | ProductionPlan entity = new ProductionPlan(); |
| | | BeanUtils.copyProperties(dto, entity); |
| | | entity.setProductModelId(resolveProductModelId(dto, i + 2, allModels, productNameById)); |
| | | entity.setStatus(PLAN_STATUS_WAIT); |
| | | entity.setSource("å
é¨"); |
| | | entity.setSource(StringUtils.isNotEmpty(dto.getSource()) ? StringUtils.trim(dto.getSource()) : "å
é¨"); |
| | | entity.setQuantityIssued(BigDecimal.ZERO); |
| | | entity.setCreateTime(now); |
| | | entity.setUpdateTime(now); |
| | | return entity; |
| | | }).collect(Collectors.toList()); |
| | | this.saveBatch(entityList); |
| | | entityList.add(entity); |
| | | } |
| | | // æä¹
åæè¾åºå¤çç»æ |
| | | if (!this.saveBatch(entityList)) { |
| | | throw new ServiceException("导å
¥å¤±è´¥ï¼ä¿åçäº§è®¡åæ°æ®å¤±è´¥"); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void exportProdData(HttpServletResponse response, List<Long> ids) { |
| | | List<ProductionPlanDto> list = productionPlanMapper.selectWithMaterialByIds(ids); |
| | | public void exportProdData(HttpServletResponse response, ProductionPlanDto requestDto) { |
| | | // 导åºä¸»çäº§è®¡åæ°æ® |
| | | List<Long> ids = requestDto == null || requestDto.getIds() == null |
| | | ? Collections.emptyList() |
| | | : requestDto.getIds().stream().filter(Objects::nonNull).distinct().collect(Collectors.toList()); |
| | | |
| | | List<ProductionPlanImportDto> exportList = new ArrayList<>(); |
| | | for (ProductionPlanDto entity : list) { |
| | | if (!ids.isEmpty()) { |
| | | List<ProductionPlanDto> list = productionPlanMapper.selectWithMaterialByIds(ids); |
| | | for (ProductionPlanDto item : list) { |
| | | ProductionPlanImportDto dto = new ProductionPlanImportDto(); |
| | | BeanUtils.copyProperties(entity, dto); |
| | | BeanUtils.copyProperties(item, dto); |
| | | dto.setAssignedQuantity(item.getQuantityIssued()); |
| | | exportList.add(dto); |
| | | } |
| | | } else { |
| | | ProductionPlanDto query = new ProductionPlanDto(); |
| | | if (requestDto != null) { |
| | | BeanUtils.copyProperties(requestDto, query); |
| | | } |
| | | IPage<ProductionPlanVo> page = productionPlanMapper.listPage(new Page<>(1, -1), query); |
| | | if (page != null && page.getRecords() != null) { |
| | | for (ProductionPlanVo item : page.getRecords()) { |
| | | ProductionPlanImportDto dto = new ProductionPlanImportDto(); |
| | | BeanUtils.copyProperties(item, dto); |
| | | dto.setAssignedQuantity(item.getQuantityIssued()); |
| | | exportList.add(dto); |
| | | } |
| | | } |
| | | } |
| | | |
| | | ExcelUtil<ProductionPlanImportDto> util = new ExcelUtil<>(ProductionPlanImportDto.class); |
| | | util.exportExcel(response, exportList, "主ç产计å"); |
| | | } |
| | | |
| | | private BigDecimal resolveRemainingQuantity(ProductionPlan plan) { |
| | | if (plan == null) { |
| | | return BigDecimal.ZERO; |
| | | /** |
| | | * æ ¡éªä¸»ç产计åå·å¯ä¸æ§ï¼å¯éè¿ excludeId æé¤å½åè®°å½ã |
| | | */ |
| | | private void checkMpsNoUnique(String mpsNo, Long excludeId) { |
| | | // æä¸»ç产计åå·æ¥è¯¢éå¤è®°å½ |
| | | LambdaQueryWrapper<ProductionPlan> wrapper = Wrappers.lambdaQuery(); |
| | | wrapper.eq(ProductionPlan::getMpsNo, mpsNo); |
| | | if (excludeId != null) { |
| | | // æ´æ°æ¶æé¤å½åè®°å½æ¬èº« |
| | | wrapper.ne(ProductionPlan::getId, excludeId); |
| | | } |
| | | BigDecimal requiredQuantity = Optional.ofNullable(plan.getQtyRequired()).orElse(BigDecimal.ZERO); |
| | | if (requiredQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | if (productionPlanMapper.selectCount(wrapper) > 0) { |
| | | // åå¨éå¤è®¡åå·ï¼ç´æ¥æ¦æª |
| | | throw new ServiceException("ç产计åå· " + mpsNo + " å·²åå¨"); |
| | | } |
| | | BigDecimal issuedQuantity = Optional.ofNullable(plan.getQuantityIssued()).orElse(BigDecimal.ZERO); |
| | | if (issuedQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return requiredQuantity; |
| | | } |
| | | if (issuedQuantity.compareTo(requiredQuantity) >= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | return requiredQuantity.subtract(issuedQuantity); |
| | | } |
| | | |
| | | private int resolvePlanStatus(BigDecimal requiredQuantity, BigDecimal issuedQuantity) { |
| | | if (requiredQuantity == null || requiredQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return PLAN_STATUS_WAIT; |
| | | } |
| | | if (issuedQuantity == null || issuedQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return PLAN_STATUS_WAIT; |
| | | } |
| | | return issuedQuantity.compareTo(requiredQuantity) < 0 ? PLAN_STATUS_PARTIAL : PLAN_STATUS_ISSUED; |
| | | /** |
| | | * æ ¹æ®å¯¼å
¥è¡çåå·ã产ååç§°ãåä½å®ä½å¯ä¸ç产ååå· IDã |
| | | */ |
| | | private Long resolveProductModelId(ProductionPlanImportDto dto, int rowNo, List<ProductModel> allModels, |
| | | Map<Long, String> productNameById) { |
| | | // å
æè§æ ¼åå·å第ä¸è½®è¿æ»¤ |
| | | String model = StringUtils.trim(dto.getModel()); |
| | | if (StringUtils.isEmpty(model)) { |
| | | throw new ServiceException("导å
¥å¤±è´¥ï¼ç¬¬" + rowNo + "è¡è§æ ¼åå·ä¸è½ä¸ºç©º"); |
| | | } |
| | | |
| | | private String formatPlanIds(List<Long> planIds) { |
| | | return planIds.stream() |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .map(String::valueOf) |
| | | .collect(Collectors.joining(",", "[", "]")); |
| | | List<ProductModel> candidates = allModels.stream() |
| | | .filter(item -> model.equals(StringUtils.trim(item.getModel()))) |
| | | .collect(Collectors.toList()); |
| | | if (candidates.isEmpty()) { |
| | | throw new ServiceException("导å
¥å¤±è´¥ï¼ç¬¬" + rowNo + "è¡è§æ ¼åå·ä¸åå¨ï¼åå·ï¼" + model); |
| | | } |
| | | |
| | | private String generateNextPlanNo(String datePrefix) { |
| | | // è¥ä¼ äºäº§ååç§°ï¼åå第äºè½®è¿æ»¤ |
| | | String productName = StringUtils.trim(dto.getProductName()); |
| | | if (StringUtils.isNotEmpty(productName)) { |
| | | candidates = candidates.stream() |
| | | .filter(item -> productName.equals(StringUtils.trim(productNameById.get(item.getProductId())))) |
| | | .collect(Collectors.toList()); |
| | | if (candidates.isEmpty()) { |
| | | throw new ServiceException("导å
¥å¤±è´¥ï¼ç¬¬" + rowNo + "è¡äº§ååç§°ä¸è§æ ¼åå·ä¸å¹é
"); |
| | | } |
| | | } |
| | | |
| | | // è¥ä¼ äºåä½ï¼åå第ä¸è½®è¿æ»¤ |
| | | String unit = StringUtils.trim(dto.getUnit()); |
| | | if (StringUtils.isNotEmpty(unit)) { |
| | | candidates = candidates.stream() |
| | | .filter(item -> unit.equals(StringUtils.trim(item.getUnit()))) |
| | | .collect(Collectors.toList()); |
| | | if (candidates.isEmpty()) { |
| | | throw new ServiceException("导å
¥å¤±è´¥ï¼ç¬¬" + rowNo + "è¡åä½ä¸è§æ ¼åå·ä¸å¹é
"); |
| | | } |
| | | } |
| | | |
| | | // ä»ç¶å¤æ¡è¯´æä¿¡æ¯ä¸è¶³ä»¥å¯ä¸å®ä½ |
| | | if (candidates.size() > 1) { |
| | | throw new ServiceException("导å
¥å¤±è´¥ï¼ç¬¬" + rowNo + "è¡è§æ ¼åå·å¹é
å°å¤ä¸ªäº§åï¼è¯·è¡¥å
产ååç§°æåä½"); |
| | | } |
| | | return candidates.get(0).getId(); |
| | | } |
| | | |
| | | /** |
| | | * çæä¸»ç产计åå·ï¼æ ¼å¼ï¼JH + yyyyMMdd + 4使µæ°´å·ã |
| | | */ |
| | | private String buildPlanNo(String datePrefix, int sequence) { |
| | | // ç»ä¸è®¡åå·æ ¼å¼ï¼JH + æ¥æ + 4使µæ°´å· |
| | | return "JH" + datePrefix + String.format("%04d", sequence); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢å½æ¥å·²åå¨çæå¤§æµæ°´å·ï¼å¹¶è¿åä¸ä¸ä¸ªå¯ç¨æµæ°´å·ã |
| | | */ |
| | | private int resolveNextPlanSequence(String datePrefix) { |
| | | // æ¥è¯¢å½æ¥ææ°ä¸æ¡è®¡åå· |
| | | QueryWrapper<ProductionPlan> queryWrapper = new QueryWrapper<>(); |
| | | queryWrapper.likeRight("mps_no", "JH" + datePrefix); |
| | | queryWrapper.orderByDesc("mps_no"); |
| | | queryWrapper.last("LIMIT 1"); |
| | | ProductionPlan latestPlan = productionPlanMapper.selectOne(queryWrapper); |
| | | |
| | | // é»è®¤ä» 0001 å¼å§ |
| | | int sequence = 1; |
| | | if (latestPlan != null && latestPlan.getMpsNo() != null && !latestPlan.getMpsNo().isEmpty()) { |
| | | if (latestPlan != null && StringUtils.isNotEmpty(latestPlan.getMpsNo())) { |
| | | // æªåæ«å°¾æµæ°´å·å¹¶éå¢ |
| | | String sequenceStr = latestPlan.getMpsNo().substring(("JH" + datePrefix).length()); |
| | | try { |
| | | sequence = Integer.parseInt(sequenceStr) + 1; |
| | | } catch (NumberFormatException e) { |
| | | } catch (NumberFormatException ignored) { |
| | | // å岿°æ®æ ¼å¼å¼å¸¸æ¶åéå° 0001 |
| | | sequence = 1; |
| | | } |
| | | } |
| | | return "JH" + datePrefix + String.format("%04d", sequence); |
| | | return sequence; |
| | | } |
| | | |
| | | /** |
| | | * 计ç®ç产计åçå©ä½æªä¸åæ°éï¼éæ±é - å·²ä¸åéï¼æå°ä¸º 0ï¼ã |
| | | */ |
| | | private BigDecimal resolveRemainingQuantity(ProductionPlan plan) { |
| | | // 空对象æ 0 å¤ç |
| | | if (plan == null) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | // éæ±é为空æå°äºçäº 0ï¼è§ä¸ºæ å©ä½ |
| | | BigDecimal requiredQuantity = Optional.ofNullable(plan.getQtyRequired()).orElse(BigDecimal.ZERO); |
| | | if (requiredQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | // å·²ä¸åé为空æå°äºçäº 0ï¼å©ä½å³éæ±é |
| | | BigDecimal issuedQuantity = Optional.ofNullable(plan.getQuantityIssued()).orElse(BigDecimal.ZERO); |
| | | if (issuedQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return requiredQuantity; |
| | | } |
| | | // å·²ä¸åé大äºçäºéæ±éï¼å©ä½å½é¶ |
| | | if (issuedQuantity.compareTo(requiredQuantity) >= 0) { |
| | | return BigDecimal.ZERO; |
| | | } |
| | | // æ£å¸¸åºæ¯è¿åå·®å¼ |
| | | return requiredQuantity.subtract(issuedQuantity); |
| | | } |
| | | |
| | | /** |
| | | * æéæ±éä¸ç´¯è®¡ä¸åéæ¨å¯¼è®¡åç¶æã |
| | | */ |
| | | private int resolvePlanStatus(BigDecimal requiredQuantity, BigDecimal issuedQuantity) { |
| | | // æ ææéæ±éæ¶ï¼ç¶æä¿æå¾
ä¸å |
| | | if (requiredQuantity == null || requiredQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return PLAN_STATUS_WAIT; |
| | | } |
| | | // æéæ±ä½æªä¸åï¼ç¶æä»ä¸ºå¾
ä¸å |
| | | if (issuedQuantity == null || issuedQuantity.compareTo(BigDecimal.ZERO) <= 0) { |
| | | return PLAN_STATUS_WAIT; |
| | | } |
| | | // å·²ä¸åéå°äºéæ±é为é¨åä¸åï¼å¦å为已ä¸å |
| | | return issuedQuantity.compareTo(requiredQuantity) < 0 ? PLAN_STATUS_PARTIAL : PLAN_STATUS_ISSUED; |
| | | } |
| | | |
| | | /** |
| | | * å°è®¡å ID éå转æ [1,2,3] å½¢å¼ï¼åå
¥ç产订åå
³èåæ®µã |
| | | */ |
| | | private String formatPlanIds(List<Long> planIds) { |
| | | // å»éå¹¶æ¼æ¥ä¸º [1,2,3] å½¢å¼çå符串 |
| | | return planIds.stream() |
| | | .filter(Objects::nonNull) |
| | | .distinct() |
| | | .map(String::valueOf) |
| | | .collect(Collectors.joining(",", "[", "]")); |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | public IPage<ProductionProductInputDto> listPageProductionProductInputDto(Page page, ProductionProductInputDto productionProductInputDto) { |
| | | // å页æ¥è¯¢ç产产åå
¥åº |
| | | return productionProductInputMapper.listPageProductionProductInputDto(page, productionProductInputDto); |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | public IPage<ProductionProductMainDto> listPageProductionProductMainDto(Page page, ProductionProductMainDto productionProductMainDto) { |
| | | // å页æ¥è¯¢ç产æ¥å·¥ä¸»è¡¨ |
| | | IPage<ProductionProductMainDto> result = productionProductMainMapper.listPageProductionProductMainDto(page, productionProductMainDto); |
| | | fillOperationParamList(result.getRecords()); |
| | | return result; |
| | |
| | | |
| | | @Override |
| | | public IPage<ProductionProductMainDto> pageProductionProductMain(Page page, ProductionProductMainDto productionProductMainDto) { |
| | | // å页æ¥è¯¢ç产æ¥å·¥ä¸»è¡¨ |
| | | return listPageProductionProductMainDto(page, productionProductMainDto); |
| | | } |
| | | |
| | | @Override |
| | | public ProductionProductMainDto getProductionProductMainInfo(Long id) { |
| | | // è·åç产产å主表详æ
|
| | | return listPageProductionProductMainDto(new Page<>(1, 1), new ProductionProductMainDto() {{ |
| | | setId(id); |
| | | }}).getRecords().stream().findFirst().orElse(null); |
| | | } |
| | | |
| | | private void fillOperationParamList(List<ProductionProductMainDto> recordList) { |
| | | // å¡«å
å·¥åºåæ°å表 |
| | | if (recordList == null || recordList.isEmpty()) { |
| | | return; |
| | | } |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | Set<Long> mainIdSet = recordList.stream() |
| | | .map(ProductionProductMainDto::getId) |
| | | .filter(Objects::nonNull) |
| | |
| | | return; |
| | | } |
| | | |
| | | // æ¥è¯¢å¹¶åå¤ä¸å¡æ°æ® |
| | | List<ProductionOrderRoutingOperationParam> paramList = productionOrderRoutingOperationParamMapper.selectList( |
| | | Wrappers.<ProductionOrderRoutingOperationParam>lambdaQuery() |
| | | .in(ProductionOrderRoutingOperationParam::getProductionProductMainId, mainIdSet) |
| | |
| | | |
| | | @Override |
| | | public Boolean addProductMain(ProductionProductMainDto dto) { |
| | | // æ°å¢ç产æ¥å·¥ä¸»è®°å½ |
| | | Long taskId = resolveTaskId(dto); |
| | | if (taskId == null) { |
| | | throw new ServiceException("è¯·ä¼ å
¥ç产工åID"); |
| | |
| | | |
| | | @Override |
| | | public Boolean saveProductionProductMain(ProductionProductMainDto productionProductMainDto) { |
| | | // ä¿åç产æ¥å·¥ä¸»è®°å½ |
| | | return addProductMain(productionProductMainDto); |
| | | } |
| | | |
| | | @Override |
| | | public Boolean removeProductMain(Long id) { |
| | | // å é¤ç产æ¥å·¥ä¸»è®°å½ |
| | | ProductionProductMain currentMain = productionProductMainMapper.selectById(id); |
| | | if (currentMain == null) { |
| | | return true; |
| | |
| | | } |
| | | |
| | | private Boolean addProductMainByProductionTask(ProductionProductMainDto dto) { |
| | | // æ¥å·¥ä»¥è®¢åå·¥åºå¿«ç
§ä¸ºåï¼é¿å
å·¥èºä¸»æ°æ®åæ´åå½±ååå²å·¥åæ§è¡ã |
| | | // æçäº§ä»»å¡æ°å¢æ¥å·¥ä¸»è®°å½ |
| | | Long taskId = resolveTaskId(dto); |
| | | if (taskId == null) { |
| | | throw new ServiceException("productionOperationTaskId can not be null"); |
| | | throw new ServiceException("ç产工åIDä¸è½ä¸ºç©º"); |
| | | } |
| | | SysUser user = userMapper.selectUserById(dto.getUserId()); |
| | | ProductionOperationTask productionOperationTask = productionOperationTaskMapper.selectById(taskId); |
| | |
| | | productionProductMain.setUserName(user == null ? dto.getUserName() : user.getNickName()); |
| | | productionProductMain.setProductionOperationTaskId(taskId); |
| | | productionProductMain.setStatus(0); |
| | | productionProductMain.setWorkHour(dto.getWorkHour()); |
| | | productionProductMainMapper.insert(productionProductMain); |
| | | syncOperationParamInputValue(dto, routingOperation.getId(), productionProductMain.getId()); |
| | | |
| | |
| | | } |
| | | |
| | | private Boolean removeProductMainByProductionTask(ProductionProductMain productionProductMain) { |
| | | // å 餿¥å·¥éè¦åæ¥åæ»è´¨æ£ãåºåãå·¥æ¶æ ¸ç®å订å/å·¥åè¿åº¦ã |
| | | // æç产任å¡åæ»å¹¶å 餿¥å·¥ä¸»è®°å½ |
| | | List<QualityInspect> qualityInspects = qualityInspectMapper.selectList( |
| | | Wrappers.<QualityInspect>lambdaQuery().eq(QualityInspect::getProductMainId, productionProductMain.getId())); |
| | | // åæ°ä¸åç½®æ¡ä»¶æ ¡éª |
| | | if (qualityInspects.size() > 0) { |
| | | List<QualityUnqualified> qualityUnqualifieds = qualityUnqualifiedMapper.selectList( |
| | | Wrappers.<QualityUnqualified>lambdaQuery() |
| | | // éåå¤çæ°æ®å¹¶ç»è£
ç»æ |
| | | .in(QualityUnqualified::getInspectId, qualityInspects.stream().map(QualityInspect::getId).collect(Collectors.toList()))); |
| | | if (qualityUnqualifieds.size() > 0 && qualityUnqualifieds.get(0).getInspectState() == 1) { |
| | | throw new ServiceException("è¯¥æ¡æ¥å·¥å·²ç»ä¸åæ ¼å¤çäºï¼ä¸å
许å é¤"); |
| | |
| | | } else { |
| | | productionOperationTask.setStatus(3); |
| | | } |
| | | // æä¹
åæè¾åºå¤çç»æ |
| | | productionOperationTaskMapper.updateById(productionOperationTask); |
| | | |
| | | ProductionOrder productionOrder = productionOrderMapper.selectById(productionOperationTask.getProductionOrderId()); |
| | |
| | | } |
| | | |
| | | private String generateProductNo() { |
| | | // çæä¸ä¸ä¸ªç产产åç¼å· |
| | | String datePrefix = "BG" + LocalDate.now().format(DateTimeFormatter.ofPattern("yyMMdd")); |
| | | QueryWrapper<ProductionProductMain> queryWrapper = new QueryWrapper<>(); |
| | | queryWrapper.select("MAX(product_no) as maxNo").likeRight("product_no", datePrefix); |
| | |
| | | } |
| | | |
| | | private BigDecimal defaultDecimal(BigDecimal value) { |
| | | // å°ç©ºæ°éå
åºä¸º0ï¼é¿å
空æéå¼å¸¸ |
| | | return value == null ? BigDecimal.ZERO : value; |
| | | } |
| | | |
| | | private Long resolveTaskId(ProductionProductMainDto dto) { |
| | | // ä»å
¥åä¸è§£æç产工åIDå¹¶æ ¡éª |
| | | if (dto == null) { |
| | | return null; |
| | | } |
| | |
| | | |
| | | @Override |
| | | public ArrayList<Long> listMain(List<Long> idList) { |
| | | // æ¥è¯¢ä¸»è¡¨IDéå |
| | | return productionProductMainMapper.listMain(idList); |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | public IPage<ProductionProductOutputDto> listPageProductionProductOutputDto(Page page, ProductionProductOutputDto productionProductOutputDto) { |
| | | // å页æ¥è¯¢ç产产ååºåº |
| | | return productionProductOutputMapper.listPageProductionProductOutputDto(page, productionProductOutputDto); |
| | | } |
| | | } |
| | |
| | | |
| | | @Override |
| | | public UserAccountDto getByUserId(UserProductionAccountingDto dto) { |
| | | // æç¨æ·æ¥è¯¢çäº§æ ¸ç®ä¿¡æ¯ |
| | | if (dto == null || dto.getUserId() == null || dto.getDate() == null || dto.getDate().trim().isEmpty()) { |
| | | return new UserAccountDto(); |
| | | } |
| | |
| | | */ |
| | | @Excel(name = "ä¾åºååç§°") |
| | | private String supplierName; |
| | | /** |
| | | * æ¯å¦ç½åå |
| | | */ |
| | | @Excel(name = "æ¯å¦ç½åå") |
| | | private Integer isWhite; |
| | | // /** |
| | | // * æ¯å¦ç½åå |
| | | // */ |
| | | // @Excel(name = "æ¯å¦ç½åå") |
| | | // private Integer isWhite; |
| | | |
| | | /** |
| | | * å½å
¥äººå§åid |
| ÎļþÃû´Ó src/main/java/com/ruoyi/purchase/vo/PurchaseReturnOrderVo.java ÐÞ¸Ä |
| | |
| | | package com.ruoyi.purchase.vo; |
| | | package com.ruoyi.purchase.dto; |
| | | |
| | | import com.ruoyi.purchase.pojo.PurchaseReturnOrders; |
| | | import lombok.AllArgsConstructor; |
| | |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class PurchaseReturnOrderVo extends PurchaseReturnOrders { |
| | | public class PurchaseReturnOrderHasAllInfoDto extends PurchaseReturnOrders { |
| | | //ä¾åºååç§° |
| | | private String supplierName; |
| | | |
| | |
| | | import com.ruoyi.purchase.dto.PurchaseReturnOrderDto; |
| | | import com.ruoyi.purchase.pojo.PurchaseReturnOrders; |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.ruoyi.purchase.vo.PurchaseReturnOrderVo; |
| | | import com.ruoyi.purchase.dto.PurchaseReturnOrderHasAllInfoDto; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | |
| | | */ |
| | | @Mapper |
| | | public interface PurchaseReturnOrdersMapper extends BaseMapper<PurchaseReturnOrders> { |
| | | IPage<PurchaseReturnOrderVo> listPage(Page page, @Param("params") PurchaseReturnOrderDto purchaseReturnOrder); |
| | | IPage<PurchaseReturnOrderHasAllInfoDto> listPage(Page page, @Param("params") PurchaseReturnOrderDto purchaseReturnOrder); |
| | | |
| | | PurchaseReturnOrderHasAllInfoDto getPurchaseReturnOrderHasAllInfoById(@Param("id") @NotNull Long id); |
| | | } |
| | |
| | | import com.ruoyi.purchase.pojo.PurchaseReturnOrders; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.purchase.vo.PurchaseReturnDetailsVo; |
| | | import com.ruoyi.purchase.vo.PurchaseReturnOrderVo; |
| | | import com.ruoyi.purchase.dto.PurchaseReturnOrderHasAllInfoDto; |
| | | |
| | | import jakarta.validation.constraints.NotNull; |
| | | |
| | |
| | | * @since 2026-03-06 11:44:38 |
| | | */ |
| | | public interface PurchaseReturnOrdersService extends IService<PurchaseReturnOrders> { |
| | | IPage<PurchaseReturnOrderVo> listPage(Page page, PurchaseReturnOrderDto purchaseReturnOrderDto); |
| | | IPage<PurchaseReturnOrderHasAllInfoDto> listPage(Page page, PurchaseReturnOrderDto purchaseReturnOrderDto); |
| | | |
| | | Boolean add(PurchaseReturnOrderDto purchaseReturnOrderDto); |
| | | |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public int addOrEditPurchase(PurchaseLedgerDto purchaseLedgerDto) throws Exception { |
| | | PurchaseLedger purchaseLedger = new PurchaseLedger(); |
| | | // DTO转Entity |
| | | BeanUtils.copyProperties(purchaseLedgerDto, purchaseLedger); |
| | | SalesLedger salesLedger = salesLedgerMapper.selectById(purchaseLedgerDto.getSalesLedgerId()); |
| | | //å½å
¥äºº |
| | | SysUser sysUser = userMapper.selectUserById(purchaseLedgerDto.getRecorderId()); |
| | |
| | | |
| | | SupplierManage supplierManage = supplierManageMapper.selectById(purchaseLedgerDto.getSupplierId()); |
| | | |
| | | // DTO转Entity |
| | | |
| | | BeanUtils.copyProperties(purchaseLedgerDto, purchaseLedger); |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | if (ObjectUtils.isNotEmpty(loginUser) && null != loginUser.getTenantId()) { |
| | | purchaseLedger.setTenantId(loginUser.getTenantId()); |
| | |
| | | } |
| | | purchaseLedgerMapper.updateById(purchaseLedger); |
| | | } |
| | | // 6.éè´å®¡æ ¸æ°å¢ï¼å®¡æ¹ç®¡çæªé
ç½®éè´å®¡æ¹äººæ¶ï¼å®¡æ¹æå¡ä¼èªå¨ç½®ä¸ºå®¡æ¹éè¿ã |
| | | addApproveByPurchase(loginUser, purchaseLedger); |
| | | |
| | | // 4. å¤çåè¡¨æ°æ® |
| | | List<SalesLedgerProduct> productList = purchaseLedgerDto.getProductData(); |
| | | if (productList != null && !productList.isEmpty()) { |
| | | handleSalesLedgerProducts(purchaseLedger.getId(), productList, purchaseLedgerDto.getType()); |
| | | } |
| | | //æ°å¢åæææ£éª 审æ¹ä¹åæçææ£éª |
| | | // if (productList != null) { |
| | | // for (SalesLedgerProduct saleProduct : productList) { |
| | | // //æ¯å¦æ¨éè´¨æ£ï¼å¦ætrue就添å |
| | | // if (saleProduct.getIsChecked()) { |
| | | // addQualityInspect(purchaseLedger, saleProduct); |
| | | // } |
| | | // } |
| | | // } |
| | | // 6.éè´å®¡æ ¸æ°å¢ï¼å®¡æ¹ç®¡çæªé
ç½®éè´å®¡æ¹äººæ¶ï¼å®¡æ¹æå¡ä¼èªå¨ç½®ä¸ºå®¡æ¹éè¿ã |
| | | addApproveByPurchase(loginUser, purchaseLedger); |
| | | // 5. è¿ç§»ä¸´æ¶æä»¶å°æ£å¼ç®å½ |
| | | fileUtil.saveStorageAttachment(ApplicationTypeEnum.FILE, RecordTypeEnum.PURCHASE_LEDGER, purchaseLedger.getId(), purchaseLedgerDto.getStorageBlobDTOS()); |
| | | return 1; |
| | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.account.pojo.AccountIncome; |
| | | import com.ruoyi.account.service.AccountIncomeService; |
| | | import com.ruoyi.common.enums.SaleEnum; |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.common.utils.DateUtils; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.framework.security.LoginUser; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.purchase.dto.PurchaseReturnOrderDto; |
| | | import com.ruoyi.purchase.dto.PurchaseReturnOrderProductsDto; |
| | | import com.ruoyi.purchase.mapper.PurchaseLedgerMapper; |
| | | import com.ruoyi.purchase.mapper.PurchaseReturnOrderProductsMapper; |
| | | import com.ruoyi.purchase.mapper.PurchaseReturnOrdersMapper; |
| | | import com.ruoyi.purchase.pojo.PurchaseLedger; |
| | | import com.ruoyi.purchase.pojo.PurchaseReturnOrderProducts; |
| | | import com.ruoyi.purchase.pojo.PurchaseReturnOrders; |
| | | import com.ruoyi.purchase.service.PurchaseReturnOrdersService; |
| | | import com.ruoyi.purchase.vo.PurchaseReturnDetailsVo; |
| | | import com.ruoyi.purchase.vo.PurchaseReturnOrderVo; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | | import com.ruoyi.purchase.dto.PurchaseReturnOrderHasAllInfoDto; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import com.ruoyi.sales.service.ISalesLedgerService; |
| | | import com.ruoyi.stock.mapper.StockOutRecordMapper; |
| | | import com.ruoyi.stock.pojo.StockOutRecord; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | private final PurchaseReturnOrderProductsMapper purchaseReturnOrderProductsMapper; |
| | | private final ISalesLedgerService salesLedgerService; |
| | | private final AccountIncomeService accountIncomeService; |
| | | private final StockUtils stockUtils; |
| | | private final SalesLedgerProductMapper salesLedgerProductMapper; |
| | | private final PurchaseLedgerMapper purchaseLedgerMapper; |
| | | private final StockOutRecordMapper stockOutRecordMapper; |
| | | |
| | | @Override |
| | | public IPage<PurchaseReturnOrderVo> listPage(Page page, PurchaseReturnOrderDto purchaseReturnOrderDto) { |
| | | public IPage<PurchaseReturnOrderHasAllInfoDto> listPage(Page page, PurchaseReturnOrderDto purchaseReturnOrderDto) { |
| | | return purchaseReturnOrdersMapper.listPage(page, purchaseReturnOrderDto); |
| | | } |
| | | |
| | |
| | | // è¿é为æ°å¢å æ¤id为null |
| | | purchaseReturnOrderProductsDto.setId(null); |
| | | purchaseReturnOrderProductsMapper.insert(purchaseReturnOrderProductsDto); |
| | | //åºåéè¦åºåº(éè´éè´§) |
| | | PurchaseLedger purchaseLedger = purchaseLedgerMapper.selectById(purchaseReturnOrderDto.getPurchaseLedgerId()); |
| | | SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(purchaseReturnOrderProductsDto.getSalesLedgerProductId()); |
| | | stockUtils.substractStock(salesLedgerProduct.getProductModelId(), purchaseReturnOrderProductsDto.getReturnQuantity(), StockOutQualifiedRecordTypeEnum.PURCHASE_RETURN_STOCK_OUT.getCode(), purchaseReturnOrderDto.getId(), purchaseLedger.getPurchaseContractNumber()+"-"+salesLedgerProduct.getId()); |
| | | } |
| | | }else { |
| | | throw new RuntimeException("è¯·éæ©éè´§åå"); |
| | |
| | | |
| | | @Override |
| | | public PurchaseReturnDetailsVo getPurchaseReturnOrderDtoById(Long id) { |
| | | PurchaseReturnOrders purchaseReturnOrders = purchaseReturnOrdersMapper.selectById(id); |
| | | PurchaseReturnOrderHasAllInfoDto purchaseReturnOrders = purchaseReturnOrdersMapper.getPurchaseReturnOrderHasAllInfoById(id); |
| | | PurchaseReturnDetailsVo purchaseReturnOrderDto = BeanUtil.copyProperties(purchaseReturnOrders, PurchaseReturnDetailsVo.class); |
| | | // æ¥è¯¢åºä»å
·ä½å¯¹åºçéè´§ |
| | | LambdaQueryWrapper<PurchaseReturnOrderProducts> queryWrapper = new LambdaQueryWrapper<>(); |
| | |
| | | LambdaUpdateWrapper<PurchaseReturnOrderProducts> updateWrapper = new LambdaUpdateWrapper<>(); |
| | | updateWrapper.eq(PurchaseReturnOrderProducts::getPurchaseReturnOrderId, id); |
| | | purchaseReturnOrderProductsMapper.delete(updateWrapper); |
| | | |
| | | //(éè´éè´§çæ°æ®éè¦å æ) |
| | | stockOutRecordMapper.delete(Wrappers.<StockOutRecord>lambdaQuery() |
| | | .eq(StockOutRecord::getRecordType,StockOutQualifiedRecordTypeEnum.PURCHASE_RETURN_STOCK_OUT.getCode()) |
| | | .eq(StockOutRecord::getRecordId, id)); |
| | | // è´¢å¡ |
| | | LambdaUpdateWrapper<AccountIncome> updateWrapperAccountIncome = new LambdaUpdateWrapper<>(); |
| | | updateWrapperAccountIncome.eq(AccountIncome::getBusinessId, id); |
| | |
| | | package com.ruoyi.purchase.vo; |
| | | |
| | | import com.ruoyi.purchase.pojo.PurchaseReturnOrders; |
| | | import com.ruoyi.purchase.dto.PurchaseReturnOrderHasAllInfoDto; |
| | | import com.ruoyi.sales.pojo.SalesLedgerProduct; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Data; |
| | |
| | | @Data |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class PurchaseReturnDetailsVo extends PurchaseReturnOrders implements Serializable { |
| | | public class PurchaseReturnDetailsVo extends PurchaseReturnOrderHasAllInfoDto implements Serializable { |
| | | |
| | | private List<PurchaseReturnOrderProductsDetailVo> purchaseReturnOrderProductsDetailVoList; |
| | | |
| | |
| | | if (CollUtil.isEmpty(list)) { |
| | | return AjaxResult.success(list); |
| | | } |
| | | // |
| | | List<Long> productIds = list.stream().map(SalesLedgerProduct::getId).collect(Collectors.toList()); |
| | | List<SimpleReturnOrderGroupDto> groupListByProductIds = purchaseReturnOrderProductsMapper.getReturnOrderGroupListByProductIds(productIds); |
| | | Map<Long, BigDecimal> returnOrderGroupDtoMap = groupListByProductIds.stream().collect(Collectors.toMap(SimpleReturnOrderGroupDto::getSalesLedgerProductId, item -> item.getSumReturnQuantity())); |
| | |
| | | if (item.getFutureTicketsAmount().compareTo(BigDecimal.ZERO) == 0) { |
| | | item.setFutureTicketsAmount(BigDecimal.ZERO); |
| | | } |
| | | // ProcurementPageDto procurementDto = new ProcurementPageDto(); |
| | | // procurementDto.setSalesLedgerProductId(item.getId()); |
| | | // procurementDto.setProductCategory(item.getProductCategory()); |
| | | // IPage<ProcurementPageDtoCopy> result = procurementRecordService.listPageCopyByProduction(new Page<>(1,-1), procurementDto); |
| | | // BigDecimal stockQuantity = stockUtils.getStockQuantity(item.getProductModelId()).get("stockQuantity"); |
| | | |
| | | // ProcurementPageDtoCopy procurementDtoCopy = result.getRecords().get(0); |
| | | if (item.getApproveStatus() != 2) { |
| | | if (item.getHasSufficientStock() == 0) { |
| | | item.setApproveStatus(0); |
| | |
| | | ApproveProcessVO approveProcessVO = new ApproveProcessVO(); |
| | | approveProcessVO.setApproveType(7); |
| | | approveProcessVO.setApproveDeptId(loginUser.getCurrentDeptId()); |
| | | approveProcessVO.setApproveReason(req.getType() + ":" +sh); |
| | | approveProcessVO.setApproveReason(sh);//åè´§ç¼å· |
| | | approveProcessVO.setApproveUserIds(req.getApproveUserIds()); |
| | | approveProcessVO.setApproveUser(loginUser.getUserId()); |
| | | approveProcessVO.setApproveTime(LocalDate.now().toString()); |
| | |
| | | } |
| | | |
| | | @GetMapping("/getDateil/{id}") |
| | | @Operation(summary = "éè¿idæ¥è¯¢è¯¦æ
") |
| | | public R getDateil(@PathVariable("id") Long id) { |
| | | return R.ok(shippingInfoService.getDetail(id)); |
| | | } |
| | | |
| | | @GetMapping("/getDateilByShippingNo") |
| | | @Operation(summary = "éè¿åè´§åå·æ¥è¯¢è¯¦æ
") |
| | | public R getDateilByShippingNo(String shippingNo) { |
| | | return R.ok(shippingInfoService.getDateilByShippingNo(shippingNo)); |
| | | } |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package com.ruoyi.sales.dto; |
| | | |
| | | import com.ruoyi.sales.pojo.ShippingInfo; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | |
| | | //åè´§å®¡æ¹æ¥ç详æ
|
| | | @Data |
| | | public class ShippingApproveDto { |
| | | |
| | | private ShippingInfo shippingInfo; |
| | | |
| | | private List<ShippingProductDetailDto> shippingProductDetailDtoList; |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.ruoyi.account.bean.dto.SalesOutboundDto; |
| | | import com.ruoyi.account.bean.vo.SalesOutboundVo; |
| | | import com.ruoyi.sales.dto.SalesLedgerProductDto; |
| | | import com.ruoyi.sales.dto.ShippingInfoDto; |
| | | import com.ruoyi.sales.pojo.ShippingInfo; |
| | | import com.ruoyi.sales.pojo.ShippingProductDetail; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | import java.util.List; |
| | |
| | | |
| | | List<ShippingInfo> getShippingInfoByCustomerName(String customerName); |
| | | |
| | | List<ShippingProductDetail> getDateil(Long id); |
| | | IPage<SalesOutboundVo> listPageByOutbound(Page page, @Param("req") SalesOutboundDto salesOutboundDto); |
| | | } |
| | |
| | | import com.ruoyi.sales.dto.ShippingProductDetailDto; |
| | | import com.ruoyi.sales.pojo.ShippingProductDetail; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | |
| | | import java.util.List; |
| | | |
| | |
| | | public interface ShippingProductDetailMapper extends BaseMapper<ShippingProductDetail> { |
| | | |
| | | List<ShippingProductDetailDto> getDetail(Long id); |
| | | |
| | | List<ShippingProductDetailDto> getDateilByShippingNo(@Param("shippingNo") String shippingNo); |
| | | } |
| | |
| | | private Boolean isProduction; |
| | | |
| | | @TableField(exist = false) |
| | | @Schema(description = "å¾
åè´§æ°é") |
| | | private BigDecimal noQuantity; |
| | | } |
| | |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.IService; |
| | | import com.ruoyi.sales.dto.SalesLedgerProductDto; |
| | | import com.ruoyi.sales.dto.ShippingApproveDto; |
| | | import com.ruoyi.sales.dto.ShippingInfoDto; |
| | | import com.ruoyi.sales.dto.ShippingProductDetailDto; |
| | | import com.ruoyi.sales.pojo.ShippingInfo; |
| | |
| | | boolean add(ShippingInfoDto req); |
| | | |
| | | List<ShippingProductDetailDto> getDetail(Long id); |
| | | |
| | | ShippingApproveDto getDateilByShippingNo(String shippingNo); |
| | | } |
| | |
| | | |
| | | @Override |
| | | public List<SalesLedgerProduct> selectSalesLedgerProductList(SalesLedgerProduct salesLedgerProduct) { |
| | | // LambdaQueryWrapper<SalesLedgerProduct> queryWrapper = new LambdaQueryWrapper<>(); |
| | | // queryWrapper.eq(SalesLedgerProduct::getSalesLedgerId, salesLedgerProduct.getSalesLedgerId()) |
| | | // .eq(SalesLedgerProduct::getType, salesLedgerProduct.getType()); |
| | | List<SalesLedgerProduct> salesLedgerProducts = salesLedgerProductMapper.selectSalesLedgerProductList(salesLedgerProduct); |
| | | if(!CollectionUtils.isEmpty(salesLedgerProducts)){ |
| | | salesLedgerProducts.forEach(item -> { |
| | |
| | | public boolean add(SalesQuotationDto salesQuotationDto) { |
| | | LoginUser loginUser = SecurityUtils.getLoginUser(); |
| | | SalesQuotation salesQuotation = new SalesQuotation(); |
| | | BeanUtils.copyProperties(salesQuotationDto, salesQuotation); |
| | | salesQuotation.setId(null); |
| | | Customer customer = customerMapper.selectById(Long.valueOf(salesQuotationDto.getCustomerId())); |
| | | if (ObjectUtils.isNotEmpty(customer)) { |
| | | salesQuotation.setCustomer(customer.getCustomerName()); |
| | |
| | | import com.ruoyi.common.enums.StockOutQualifiedRecordTypeEnum; |
| | | import com.ruoyi.procurementrecord.utils.StockUtils; |
| | | import com.ruoyi.sales.dto.SalesLedgerProductDto; |
| | | import com.ruoyi.sales.dto.ShippingApproveDto; |
| | | import com.ruoyi.sales.dto.ShippingInfoDto; |
| | | import com.ruoyi.sales.dto.ShippingProductDetailDto; |
| | | import com.ruoyi.sales.mapper.SalesLedgerProductMapper; |
| | |
| | | } |
| | | //æ£ååºå |
| | | if(!"å·²åè´§".equals(byId.getStatus())){ |
| | | // SalesLedgerProduct salesLedgerProduct = salesLedgerProductMapper.selectById(byId.getSalesLedgerProductId()); |
| | | List<ShippingProductDetail> shippingProductDetails = shippingProductDetailMapper.selectList(new LambdaQueryWrapper<ShippingProductDetail>().eq(ShippingProductDetail::getShippingInfoId, req.getId())); |
| | | if (CollectionUtils.isEmpty(shippingProductDetails)) { |
| | | throw new RuntimeException("åè´§ä¿¡æ¯ä¸åå¨"); |
| | |
| | | public List<ShippingProductDetailDto> getDetail(Long id) { |
| | | return shippingProductDetailMapper.getDetail(id); |
| | | } |
| | | |
| | | @Override |
| | | public ShippingApproveDto getDateilByShippingNo(String shippingNo) { |
| | | ShippingApproveDto shippingApproveDto = new ShippingApproveDto(); |
| | | ShippingInfo shippingInfo = new ShippingInfo(); |
| | | shippingInfo.setShippingNo(shippingNo); |
| | | shippingApproveDto.setShippingInfo(shippingInfoMapper.listPage(new Page(1, -1),shippingInfo).getRecords().get(0)); |
| | | List<ShippingProductDetailDto> dateilByShippingNo = shippingProductDetailMapper.getDateilByShippingNo(shippingNo); |
| | | shippingApproveDto.setShippingProductDetailDtoList(dateilByShippingNo); |
| | | return shippingApproveDto; |
| | | } |
| | | } |
| | |
| | | import com.ruoyi.framework.aspectj.lang.enums.BusinessType; |
| | | import com.ruoyi.framework.web.domain.AjaxResult; |
| | | import com.ruoyi.staff.dto.StaffOnJobDto; |
| | | import com.ruoyi.staff.dto.StaffOnJobExcelDto; |
| | | import com.ruoyi.staff.pojo.StaffContract; |
| | | import com.ruoyi.staff.pojo.StaffOnJob; |
| | | import com.ruoyi.staff.service.IStaffOnJobService; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import org.springframework.util.CollectionUtils; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | import com.ruoyi.staff.dto.StaffOnJobExcelDto; |
| | | |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import jakarta.validation.Valid; |
| | | import java.util.List; |
| | | |
| | | /** |
| | |
| | | * @return |
| | | */ |
| | | @PostMapping("/renewContract/{id}") |
| | | public AjaxResult renewContract(@PathVariable("id") Long id, @RequestBody StaffContract staffContract) { |
| | | public AjaxResult renewContract(@PathVariable Long id, @RequestBody StaffContract staffContract) { |
| | | return AjaxResult.success(staffOnJobService.renewContract(id, staffContract)); |
| | | } |
| | | |
| | |
| | | @Schema(description = "æ¯å¦è´¨æ£") |
| | | private Boolean isQuality; |
| | | |
| | | @Schema(description = "ç±»å åºå计æ¶å计件") |
| | | @Schema(description = "ç±»å åºå计æ¶å计件ï¼0计æ¶ï¼1计件") |
| | | private Integer type; |
| | | |
| | | @Schema(description = "设å¤id") |
| | |
| | | @Schema(description = "é¨é¨ID") |
| | | @TableField(fill = FieldFill.INSERT) |
| | | private Long deptId; |
| | | |
| | | @Schema(description = "ç±»å åºå计æ¶å计件ï¼0计æ¶ï¼1计件") |
| | | private Integer type; |
| | | } |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public R update(TechnologyBom technologyBom) { |
| | | if (technologyBom.getId() == null) { |
| | | throw new ServiceException("BOM id is required"); |
| | | throw new ServiceException("BOM IDä¸è½ä¸ºç©º"); |
| | | } |
| | | validateProductModel(technologyBom.getProductModelId()); |
| | | TechnologyBom oldBom = technologyBomMapper.selectById(technologyBom.getId()); |
| | | if (oldBom == null) { |
| | | throw new ServiceException("BOM not found"); |
| | | throw new ServiceException("BOMä¸åå¨"); |
| | | } |
| | | if (oldBom.getProductModelId() != null && !oldBom.getProductModelId().equals(technologyBom.getProductModelId())) { |
| | | technologyRoutingMapper.updateProductModelByBomId(technologyBom.getProductModelId(), technologyBom.getId().longValue()); |
| | |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public boolean batchDelete(List<Long> ids) { |
| | | if (ids == null || ids.isEmpty()) { |
| | | throw new ServiceException("Select at least one BOM"); |
| | | throw new ServiceException("请è³å°éæ©ä¸ä¸ªBOM"); |
| | | } |
| | | List<TechnologyRouting> list = technologyRoutingMapper.selectList(Wrappers.<TechnologyRouting>lambdaQuery() |
| | | .in(TechnologyRouting::getBomId, ids)); |
| | | if (!list.isEmpty()) { |
| | | throw new ServiceException("BOM is referenced by routing"); |
| | | throw new ServiceException("BOM已被工èºè·¯çº¿å¼ç¨ï¼ä¸è½å é¤"); |
| | | } |
| | | technologyBomStructureService.remove(Wrappers.<TechnologyBomStructure>lambdaQuery() |
| | | .in(TechnologyBomStructure::getBomId, ids)); |
| | |
| | | */ |
| | | private void validateProductModel(Long productModelId) { |
| | | if (productModelId == null) { |
| | | throw new ServiceException("Product model is required"); |
| | | throw new ServiceException("产åè§æ ¼IDä¸è½ä¸ºç©º"); |
| | | } |
| | | ProductModel productModel = productModelService.getById(productModelId); |
| | | if (productModel == null) { |
| | | throw new ServiceException("Product model not found"); |
| | | throw new ServiceException("产åè§æ ¼ä¸åå¨"); |
| | | } |
| | | } |
| | | |
| | |
| | | public boolean saveTechnologyOperationParam(TechnologyOperationParam technologyOperationParam) { |
| | | if (technologyOperationParam.getTechnologyOperationId() == null |
| | | || technologyOperationMapper.selectById(technologyOperationParam.getTechnologyOperationId()) == null) { |
| | | throw new ServiceException("Operation not found"); |
| | | throw new ServiceException("å·¥åºä¸åå¨"); |
| | | } |
| | | if (technologyOperationParam.getTechnologyParamId() == null) { |
| | | throw new ServiceException("Param is required"); |
| | | throw new ServiceException("åæ°IDä¸è½ä¸ºç©º"); |
| | | } |
| | | TechnologyParam technologyParam = technologyParamMapper.selectById(technologyOperationParam.getTechnologyParamId()); |
| | | if (technologyParam == null) { |
| | | throw new ServiceException("Param not found"); |
| | | throw new ServiceException("åæ°ä¸åå¨"); |
| | | } |
| | | boolean duplicate = technologyOperationParamMapper.selectCount(Wrappers.<TechnologyOperationParam>lambdaQuery() |
| | | .eq(TechnologyOperationParam::getTechnologyOperationId, technologyOperationParam.getTechnologyOperationId()) |
| | | .eq(TechnologyOperationParam::getTechnologyParamId, technologyOperationParam.getTechnologyParamId()) |
| | | .ne(technologyOperationParam.getId() != null, TechnologyOperationParam::getId, technologyOperationParam.getId())) > 0; |
| | | if (duplicate) { |
| | | throw new ServiceException("Duplicate param in operation"); |
| | | throw new ServiceException("å·¥åºåæ°éå¤"); |
| | | } |
| | | return this.saveOrUpdate(technologyOperationParam); |
| | | } |
| | |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.ruoyi.common.utils.SecurityUtils; |
| | | import com.ruoyi.common.utils.StringUtils; |
| | | import com.ruoyi.technology.bean.dto.TechnologyParamDto; |
| | | import com.ruoyi.technology.bean.vo.TechnologyParamVo; |
| | | import com.ruoyi.technology.mapper.TechnologyOperationParamMapper; |
| | | import com.ruoyi.technology.mapper.TechnologyParamMapper; |
| | | import com.ruoyi.technology.pojo.TechnologyOperationParam; |
| | | import com.ruoyi.technology.pojo.TechnologyParam; |
| | | import com.ruoyi.technology.service.TechnologyParamService; |
| | | import lombok.RequiredArgsConstructor; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | |
| | | private static final List<Integer> VALID_PARAM_TYPES = Arrays.asList(1, 2, 3, 4); |
| | | private static final String PARAM_CODE_PREFIX = "PARAM_"; |
| | | private static final Byte DATE_PARAM_TYPE = (byte) 4; |
| | | private final TechnologyOperationParamMapper technologyOperationParamMapper; |
| | | |
| | | /** |
| | | * å页æ¥è¯¢åºç¡åæ°å¹¶æ ¼å¼åæ¥æç±»åå±ç¤ºã |
| | |
| | | * æ¹éå é¤åºç¡åæ°ã |
| | | */ |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public int deleteBaseParamByIds(Long[] ids) { |
| | | if (ids == null || ids.length == 0) { |
| | | throw new RuntimeException("å é¤IDä¸è½ä¸ºç©º"); |
| | | } |
| | | return baseMapper.deleteBatchIds(Arrays.asList(ids)); |
| | | technologyOperationParamMapper.delete(Wrappers.<TechnologyOperationParam>lambdaQuery() |
| | | .in(TechnologyOperationParam::getTechnologyParamId, Arrays.asList(ids))); |
| | | return baseMapper.deleteByIds(Arrays.asList(ids)); |
| | | } |
| | | } |
| | |
| | | routingOperation.setProductModelId(resolveOutputProductModelId(bomStructure, structureById, technologyRouting.getProductModelId())); |
| | | routingOperation.setTechnologyOperationId(bomStructure.getOperationId()); |
| | | routingOperation.setDragSort(dragSort++); |
| | | routingOperation.setIsQuality(getOperationQuality(bomStructure.getOperationId())); |
| | | TechnologyOperation technologyOperation = getOperation(bomStructure.getOperationId()); |
| | | routingOperation.setIsQuality(technologyOperation != null ? technologyOperation.getIsQuality() : null); |
| | | routingOperation.setIsProduction(technologyOperation != null ? technologyOperation.getIsProduction() : null); |
| | | routingOperation.setType(technologyOperation != null ? technologyOperation.getType() : null); |
| | | technologyRoutingOperationMapper.insert(routingOperation); |
| | | syncRoutingOperationParams(routingOperation.getId(), bomStructure.getOperationId()); |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * è´¨æ£æ è¯ä»¥å·¥åºåºç¡è¡¨å®ä¹ä¸ºåã |
| | | */ |
| | | private Boolean getOperationQuality(Long operationId) { |
| | | TechnologyOperation technologyOperation = technologyOperationMapper.selectById(operationId); |
| | | return technologyOperation != null ? technologyOperation.getIsQuality() : null; |
| | | private TechnologyOperation getOperation(Long operationId) { |
| | | if (operationId == null) { |
| | | return null; |
| | | } |
| | | return technologyOperationMapper.selectById(operationId); |
| | | } |
| | | |
| | | private String buildProcessRouteCode(Long id) { |
| | |
| | | # æ¯å¦å
è®¸çææä»¶è¦çå°æ¬å°ï¼èªå®ä¹è·¯å¾ï¼ï¼é»è®¤ä¸å
许 |
| | | allowOverwrite: false |
| | | |
| | | # æä»¶ä¸ä¼ é
ç½® |
| | | file: |
| | | temp-dir: D:/ruoyi/temp/uploads # 临æ¶ç®å½ |
| | | upload-dir: D:/ruoyi/prod/uploads # æ£å¼ç®å½ |
| | | temp-dir: D:/ruoyi/temp/uploads # 临æ¶ç®å½ åæå é¤ |
| | | upload-dir: D:/ruoyi/prod/uploads # æ£å¼ç®å½ åæå é¤ |
| | | path: D:/ruoyi/prod/uploads # ä¸ä¼ ç®å½ |
| | | urlPrefix: /common # 龿¥åç¼ |
| | | domain: http://127.0.0.1:7005 # åååç¼ |
| | | expired: 120 # è¿ææ¶é´(åä½:åé) |
| | | useLimit: 10 # ä½¿ç¨æ¬¡æ° |
| | | compress: true # æ¯å¦å缩 |
| | | needCompressSize: 10MB # å缩éå¼ |
| | | compressQuality: 0.5 # å缩质é(0.0-1.0) |
| | | knowledge: |
| | | one: D:\æ°ç大ç½ç´ ä¼ä¸äº§åä½ç³»è¯´æææ¡£.md |
| | | |
| | |
| | | AND expense_method = #{accountExpense.expenseMethod} |
| | | </if> |
| | | </select> |
| | | <select id="report" resultType="com.ruoyi.account.dto.AccountDto2"> |
| | | <select id="report" resultType="com.ruoyi.account.bean.dto.AccountDto2"> |
| | | SELECT |
| | | sdd.dict_label typeName, |
| | | sum(expense_money) account |
| | |
| | | AND income_method = #{accountIncome.incomeMethod} |
| | | </if> |
| | | </select> |
| | | <select id="report" resultType="com.ruoyi.account.dto.AccountDto2"> |
| | | <select id="report" resultType="com.ruoyi.account.bean.dto.AccountDto2"> |
| | | SELECT |
| | | sdd.dict_label typeName, |
| | | ifnull(sum(income_money),0) account |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | <?xml version="1.0" encoding="UTF-8"?> |
| | | <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
| | | <mapper namespace="com.ruoyi.account.mapper.AccountSubjectMapper"> |
| | | |
| | | <!-- éç¨æ¥è¯¢æ å°ç»æ --> |
| | | <resultMap id="BaseResultMap" type="com.ruoyi.account.pojo.AccountSubject"> |
| | | <id column="id" property="id" /> |
| | | <result column="subject_code" property="subjectCode" /> |
| | | <result column="subject_name" property="subjectName" /> |
| | | <result column="subject_type" property="subjectType" /> |
| | | <result column="balance_direction" property="balanceDirection" /> |
| | | <result column="status" property="status" /> |
| | | <result column="remark" property="remark" /> |
| | | <result column="create_user" property="createUser" /> |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_user" property="updateUser" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | <result column="dept_id" property="deptId" /> |
| | | </resultMap> |
| | | |
| | | </mapper> |
| | |
| | | <result column="create_user_id" property="createUserId" /> |
| | | <result column="update_user_id" property="updateUserId" /> |
| | | </resultMap> |
| | | <select id="pageSalesRefundAmountOrderDto" resultType="com.ruoyi.account.dto.SalesRefundAmountOrderDto"> |
| | | <select id="pageSalesRefundAmountOrderDto" resultType="com.ruoyi.account.bean.dto.SalesRefundAmountOrderDto"> |
| | | select sl.sales_contract_no, |
| | | sl.customer_contract_no, |
| | | slp.specification_model, |
| | |
| | | where id = #{id} |
| | | </select> |
| | | <select id="getDeviceTypeDistributionByYear" |
| | | resultType="com.ruoyi.account.dto.DeviceTypeDetail" |
| | | resultType="com.ruoyi.account.bean.dto.DeviceTypeDetail" |
| | | parameterType="java.lang.Integer"> |
| | | SELECT |
| | | `type`, |
| | |
| | | left join sales_ledger sl on si.sales_ledger_id = sl.id |
| | | where rm.id = #{id} |
| | | </select> |
| | | <select id="listPageBySalesReturn" resultType="com.ruoyi.account.bean.vo.SalesReturnVo"> |
| | | select rm.id, |
| | | rm.return_no, |
| | | c.customer_name, |
| | | si.shipping_no, |
| | | rm.make_time, |
| | | rm.refund_amount, |
| | | rm.return_reason, |
| | | rm.make_time, |
| | | sl.sales_contract_no |
| | | from return_management rm |
| | | left join shipping_info si on rm.shipping_id = si.id |
| | | left join customer c on rm.customer_id = c.id |
| | | left join sales_ledger sl on si.sales_ledger_id = sl.id |
| | | where rm.status=1 |
| | | <if test="req.returnNo != null and req.returnNo != ''"> |
| | | and rm.return_no like concat('%',#{req.returnNo},'%') |
| | | </if> |
| | | <if test="req.customerName != null and req.customerName != ''"> |
| | | and c.customer_name like concat('%',#{req.customerName},'%') |
| | | </if> |
| | | <if test="req.startDate != null and req.endDate != null"> |
| | | AND DATE_FORMAT(rm.make_time, '%Y-%m-%d') BETWEEN #{startDate} AND #{endDate} |
| | | </if> |
| | | order by rm.id DESC |
| | | </select> |
| | | </mapper> |
| | |
| | | pa.scheduling_user_id as schedulingUserId, |
| | | pa.scheduling_user_name as schedulingUserName, |
| | | cast(sum( |
| | | ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) * |
| | | case |
| | | when poro.type = 0 then ifnull(pa.work_hours, 0) * ifnull(ppm.work_hour, 0) |
| | | else ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) * |
| | | case |
| | | when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$' |
| | | then cast(substring_index(pm.model, '*', -1) as decimal(18,4)) |
| | | else 1 |
| | | end |
| | | end |
| | | ) as decimal(18,4)) as wages, |
| | | cast(sum(ifnull(pa.finished_num, 0)) as decimal(18,4)) as finishedNum, |
| | | cast(sum(ifnull(pa.work_hours, 0)) as decimal(18,4)) as workHours, |
| | | cast(sum(ifnull(ppm.work_hour, 0)) as decimal(18,4)) as workHour, |
| | | case |
| | | when sum(ifnull(ppo.quantity, 0) + ifnull(ppo.scrapQty, 0)) = 0 then '0%' |
| | | else concat( |
| | |
| | | pm.model as model, |
| | | pm.unit as unit, |
| | | poro.operation_name as operationName, |
| | | poro.type as type, |
| | | IFNULL(scrapStat.scrapQty, 0) AS scrapQty, |
| | | ROUND(IFNULL(pot.complete_quantity, 0) / NULLIF(pot.plan_quantity, 0) * 100, 2) AS completionStatus, |
| | | CASE |
| | |
| | | <result column="update_time" property="updateTime" /> |
| | | <result column="drag_sort" property="dragSort" /> |
| | | <result column="is_quality" property="isQuality" /> |
| | | <result column="type" property="type" /> |
| | | <result column="create_user" property="createUser" /> |
| | | <result column="dept_id" property="deptId" /> |
| | | </resultMap> |
| | |
| | | <if test="c.requiredDateStart != null and c.requiredDateEnd != null"> |
| | | and pp.required_date between #{c.requiredDateStart} and #{c.requiredDateEnd} |
| | | </if> |
| | | <if test="c.salesContractNo != null and c.salesContractNo != ''"> |
| | | and sl.sales_contract_no like concat('%', #{c.salesContractNo}, '%') |
| | | </if> |
| | | </if> |
| | | </where> |
| | | ORDER BY COALESCE(pp.id) DESC |
| | |
| | | ifnull(ppo.scrap_qty, 0) as scrapQty, |
| | | date(pa.scheduling_date) as schedulingDate, |
| | | pa.scheduling_user_name as schedulingUserName, |
| | | cast(ifnull(ppm.work_hour, 0) as decimal(18,4)) as workHour, |
| | | cast(ifnull(pa.work_hours, 0) as decimal(18,4)) as workHours, |
| | | cast( |
| | | ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) * |
| | | case |
| | | when poro.type = 0 then ifnull(pa.work_hours, 0) * ifnull(ppm.work_hour, 0) |
| | | else ifnull(pa.work_hours, 0) * ifnull(pa.finished_num, 0) * |
| | | case |
| | | when substring_index(pm.model, '*', -1) regexp '^[0-9]+(\\.[0-9]+)?$' |
| | | then cast(substring_index(pm.model, '*', -1) as decimal(18,4)) |
| | | else 1 |
| | | end |
| | | end |
| | | as decimal(18,4) |
| | | ) as wages |
| | | from production_account pa |
| | |
| | | <result column="create_time" property="createTime" /> |
| | | <result column="update_time" property="updateTime" /> |
| | | </resultMap> |
| | | |
| | | <select id="listPage" resultType="com.ruoyi.purchase.vo.PurchaseReturnOrderVo"> |
| | | <sql id="getPurchaseReturnOrderHasAllInfoFormAndColumn"> |
| | | SELECT |
| | | pro.*, |
| | | sm.supplier_name as supplierName, |
| | | pl.purchase_contract_number as purchaseContractNumber |
| | | sm.supplier_name as supplier_name, |
| | | pl.purchase_contract_number as purchase_contract_number |
| | | FROM purchase_return_orders pro |
| | | LEFT JOIN supplier_manage sm ON pro.supplier_id = sm.id |
| | | LEFT JOIN purchase_ledger pl ON pl.id = pro.purchase_ledger_id |
| | | where 1=1 |
| | | </sql> |
| | | <select id="listPage" resultType="com.ruoyi.purchase.dto.PurchaseReturnOrderHasAllInfoDto"> |
| | | <include refid="getPurchaseReturnOrderHasAllInfoFormAndColumn"/> |
| | | <where> |
| | | <if test="params.no != null and params.no != '' "> |
| | | AND pro.no LIKE CONCAT('%',#{params.no},'%') |
| | | </if> |
| | |
| | | <if test="params.createUser != null"> |
| | | AND pro.create_user = #{params.createUser} |
| | | </if> |
| | | </where> |
| | | ORDER BY pro.create_time DESC |
| | | </select> |
| | | <select id="getPurchaseReturnOrderHasAllInfoById" |
| | | resultType="com.ruoyi.purchase.dto.PurchaseReturnOrderHasAllInfoDto"> |
| | | <include refid="getPurchaseReturnOrderHasAllInfoFormAndColumn"/> |
| | | where pro.id = #{id} |
| | | </select> |
| | | </mapper> |
| | |
| | | AND T3.invoice_date = #{invoiceRegistrationProductDto.invoiceDate} |
| | | </if> |
| | | </where> |
| | | ORDER BY T1.create_time DESC |
| | | ORDER BY T1.create_time DESC, T1.id DESC |
| | | </select> |
| | | |
| | | <select id="invoiceRegistrationProductPage" resultType="com.ruoyi.sales.dto.InvoiceRegistrationProductDto"> |
| | |
| | | %H:%i:%s')+interval 1 day |
| | | </if> |
| | | </where> |
| | | ORDER BY T1.create_time DESC |
| | | ORDER BY T1.create_time DESC, T1.id DESC |
| | | </select> |
| | | </mapper> |
| | |
| | | left join sales_ledger sl on si.sales_ledger_id = sl.id |
| | | where si.status = 'å·²åè´§' and sl.customer_name = #{customerName} |
| | | </select> |
| | | <select id="listPageByOutbound" resultType="com.ruoyi.account.bean.vo.SalesOutboundVo"> |
| | | SELECT |
| | | sor.id, |
| | | sor.outbound_batches, |
| | | sl.customer_name, |
| | | s.shipping_date, |
| | | p.product_name, |
| | | slp.specification_model, |
| | | slp.stock_out_num, |
| | | s.shipping_no, |
| | | sl.sales_contract_no |
| | | FROM shipping_info s |
| | | LEFT JOIN sales_ledger sl ON s.sales_ledger_id = sl.id |
| | | LEFT JOIN sales_ledger_product slp ON s.sales_ledger_product_id = slp.id and slp.type = 1 |
| | | left join product_model pm on slp.product_model_id = pm.id |
| | | left join product p on pm.product_id = p.id |
| | | left join stock_out_record sor on sor.record_id = s.id and sor.record_type='13' |
| | | WHERE s.status='å·²åè´§' |
| | | <if test="req.outboundBatches != null and req.outboundBatches != ''"> |
| | | AND sor.outbound_batches LIKE CONCAT('%',#{req.outboundBatches},'%') |
| | | </if> |
| | | <if test="req.customerName != null and req.customerName != ''"> |
| | | AND sl.customer_name LIKE CONCAT('%',#{req.customerName},'%') |
| | | </if> |
| | | <if test="req.startDate != null and req.endDate != null"> |
| | | AND s.shipping_date BETWEEN #{startDate} AND #{endDate} |
| | | </if> |
| | | order by sor.id DESC |
| | | </select> |
| | | </mapper> |
| | |
| | | left join product p on p.id = pm.product_id |
| | | where spd.shipping_info_id = #{id} |
| | | </select> |
| | | <select id="getDateilByShippingNo" resultType="com.ruoyi.sales.dto.ShippingProductDetailDto"> |
| | | select si.batch_no, pm.model as specification_model, p.product_name, spd.quantity as delivery_quantity |
| | | from shipping_product_detail spd |
| | | left join shipping_info sp on sp.id = spd.shipping_info_id |
| | | left join stock_inventory si on si.id = spd.stock_inventory_id |
| | | left join product_model pm on pm.id = si.product_model_id |
| | | left join product p on p.id = pm.product_id |
| | | where sp.shipping_no = #{shippingNo} |
| | | </select> |
| | | |
| | | </mapper> |
| | |
| | | staff_on_job.*, |
| | | sp.post_name as postName, |
| | | sd.dept_name as deptName, |
| | | t1.contract_start_time |
| | | MIN(t1.contract_start_time) as contract_start_time, -- åææ©ååå¼å§æ¶é´ |
| | | MAX(t1.contract_end_time) as contract_end_time |
| | | FROM staff_on_job |
| | | LEFT JOIN |
| | | sys_post sp ON sp.post_id = staff_on_job.sys_post_id |
| | | LEFT JOIN |
| | | sys_dept sd ON sd.dept_id = staff_on_job.sys_dept_id |
| | | LEFT JOIN sys_post sp ON sp.post_id = staff_on_job.sys_post_id |
| | | LEFT JOIN sys_dept sd ON sd.dept_id = staff_on_job.sys_dept_id |
| | | LEFT JOIN staff_contract as t1 ON t1.staff_on_job_id = staff_on_job.id |
| | | where 1=1 |
| | | WHERE 1=1 |
| | | <if test="staffOnJob.staffState != null"> |
| | | AND staff_state = #{staffOnJob.staffState} |
| | | </if> |
| | |
| | | <if test="staffOnJob.entryDateEnd != null and staffOnJob.entryDateEnd != '' "> |
| | | AND contract_expire_time <= DATE_FORMAT(#{staffOnJob.entryDateEnd},'%Y-%m-%d') |
| | | </if> |
| | | GROUP BY staff_on_job.id |
| | | </select> |
| | | <select id="staffOnJobList" resultType="com.ruoyi.staff.dto.StaffOnJobDto"> |
| | | SELECT |
| | |
| | | <result column="update_time" property="updateTime" /> |
| | | <result column="drag_sort" property="dragSort" /> |
| | | <result column="is_quality" property="isQuality" /> |
| | | <result column="type" property="type" /> |
| | | <result column="create_user" property="createUser" /> |
| | | <result column="dept_id" property="deptId" /> |
| | | </resultMap> |