feat(crm): 增强合同开票与回款功能
- 集成开票服务到合同控制器,支持获取已开票金额和未开票金额
- 在合同响应对象中新增已开票金额和未开票金额字段
- 增强发票控制器功能,添加简单列表接口供回款创建时选择关联发票
- 更新发票金额统计逻辑,包含审批通过和审批中的发票状态
- 强化回款验证机制,增加发票关联验证和金额超限检查
- 在回款实体中新增关联发票编号字段
- 添加按发票ID查询回款金额映射的方法
- 实现回款与发票的双向关联显示功能
- 优化开票提交时的金额超限验证逻辑
- 增加发票附件上传状态标记和剩余可回款金额计算
- 在ERP模块中新增采购来票业务类型枚举值
- 实现ERP付款单与采购发票的关联功能
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # ååç®¡çæ°å¢"å·²å¼ç¥¨éé¢/æªå¼ç¥¨éé¢"åæ®µ - å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - åå管çå表页 |
| | | - åå详æ
页 |
| | | - å建å¼ç¥¨ / åå»ºåæ¬¾æ¶éæ©ååç䏿ï¼simple-listï¼ |
| | | |
| | | ## API |
| | | |
| | | æ¬æ¬¡**䏿¹å¨ä»»ä½æ¥å£è·¯å¾ãæ¹æ³ã请æ±åæ°**ï¼ä»
ååç¸å
³æ¥å£ç**ååºç»ææ°å¢ä¸¤ä¸ªå段**ã |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | /crm/contract/page | ååå页åè¡¨ï¼æ°å¢åæ®µï¼ | |
| | | | GET | /crm/contract/get | åå详æ
ï¼æ°å¢åæ®µï¼ | |
| | | | GET | /crm/contract/page-by-customer | æå®¢æ·åé¡µï¼æ°å¢åæ®µï¼ | |
| | | | GET | /crm/contract/page-by-business | æåæºåé¡µï¼æ°å¢åæ®µï¼ | |
| | | | GET | /crm/contract/simple-list | ååä¸æï¼æ°å¢åæ®µï¼ | |
| | | |
| | | **ååºå段æ°å¢ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | totalInvoicePrice | BigDecimal | å·²å¼ç¥¨éé¢ï¼å
ï¼ï¼ç»è®¡**审æ¹éè¿ + 审æ¹ä¸**çå¼ç¥¨åéé¢å计 | |
| | | | unInvoicePrice | BigDecimal | æªå¼ç¥¨éé¢ï¼å
ï¼ï¼çäº `totalPrice - totalInvoicePrice`ï¼ä»
å表/详æ
æ¥å£è¿åï¼simple-list ä¸è¿å | |
| | | |
| | | **ååºç¤ºä¾ï¼å表/详æ
ï¼ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": { |
| | | "totalPrice": 1000000.00, |
| | | "totalReceivablePrice": 400000.00, |
| | | "totalInvoicePrice": 1000000.00, |
| | | "unInvoicePrice": 0.00 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |------|----------|------| |
| | | | å·²å¼ç¥¨éé¢ï¼totalInvoicePriceï¼ | åè¡¨è¡¨æ ¼åã详æ
页éé¢åºå | å«å®¡æ¹ä¸çå¼ç¥¨åéé¢ï¼ä¸å端é²è¶
æ ¡éªå£å¾ä¸è´ | |
| | | | æªå¼ç¥¨éé¢ï¼unInvoicePriceï¼ | åè¡¨è¡¨æ ¼åã详æ
页éé¢åºå | ååéé¢ - å·²å¼ç¥¨éé¢ï¼å¯åæ¶ç¨äºå±ç¤º"å©ä½å¯å¼ç¥¨éé¢" | |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | | åºæ¯ | è§å | |
| | | |------|------| |
| | | | 审æ¹ä¸çå¼ç¥¨å | 计å
¥"å·²å¼ç¥¨éé¢"ï¼å ç¨ååé¢åº¦ï¼ä¸å端æäº¤å¼ç¥¨æ¶çæ ¡éªä¸è´ï¼å端å±ç¤ºçå©ä½é¢åº¦æåç¡®ï¼ | |
| | | | 审æ¹è¢«é©³å / åæ¶çå¼ç¥¨å | ä¸è®¡å
¥"å·²å¼ç¥¨éé¢"ï¼é¢åº¦èªå¨éæ¾ | |
| | | | æªå¼ç¥¨éé¢ | è¥ä¸ºè´æ°è¯´æå·²è¶
å¼ï¼æ£å¸¸æµç¨åç«¯å·²æ¦æªï¼ä¸ä¼åºç°ï¼ | |
| | | | åå䏿ï¼simple-listï¼ | è¿å totalInvoicePriceï¼å¯ç¨äºå¨å建å¼ç¥¨/忬¾æ¶æç¤ºå©ä½å¯å¼éé¢ | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - åæ®µè¯ä¹æ¯"å«å®¡æ¹ä¸"ï¼ä¸æ¯"å®é
å·²å¼åºç¥¨"éé¢ï¼å端å±ç¤ºæ¶ææ¡å»ºè®®ç¨"å·²å¼ç¥¨éé¢ï¼å«å®¡æ¹ä¸ï¼"é¿å
æ§ä¹ã |
| | | - æ°å¢å段ä»
ç¨äºå±ç¤ºï¼å端**ä¸è¦**æäº¤è¿ä¸¤ä¸ªå段ï¼åç«¯ä¸æ¥æ¶ï¼å±åªè¯»æ±æ»å段ï¼ã |
| | | - Excel å¯¼åºæ¬æ¬¡æªæ°å¢è¿ä¸¤åï¼å¦é导åºå¯å¦è¡ç³è¯·ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # åå·¥åå管ç - å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - æ°å¢ãåå·¥ååã管ç页ï¼`views/hrm/employee/contract/index.vue` |
| | | - æ°å¢ãåå·¥ååãèåï¼å·²é
ç½®å端èåï¼æè½½å¨ HRM â 人åèµæº ä¸ãä¸ãå工管çã平级ï¼component = `hrm/employee/contract/index`ï¼ä¸è¦æå°ãå工管çãä¸ï¼å¦å该èåä¼åæç®å½å¯¼è´åå·¥åè¡¨é¡µæ æ³ç´è¾¾ï¼ |
| | | - éæ©å工䏿ï¼å¤ç¨ç°æå工精ç®å表æ¥å£ |
| | | - å工详æ
é¡µï¼æ°å¢ãåå·¥ååãTabï¼å±ç¤ºè¯¥åå·¥ç¾è®¢çå
¨é¨ååï¼å¤ç¨ `/hrm/employee-contract/page`ï¼æ `employeeId` è¿æ»¤ï¼ |
| | | |
| | | ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å
¥ |
| | | |
| | | 1. å建ååï¼åå·¥ä» `/hrm/employee/simple-list` 䏿鿩 â èªå¨å¸¦å
¥åå·¥ï¼æ éåéé¨é¨/å²ä½ï¼ |
| | | 2. ç»ç¾ååï¼ååå表æä½åãç»ç¾ãæé® â èªå¨å¸¦å
¥è¯¥åå·¥ + ä¸ä»½ååçç±»å/æé/ç¾çº¦ä¸»ä½/å·¥èµï¼æ¥æééæ°å¡«åï¼æäº¤æ¶æ¾å¼ä¼ `parentId` æå被ç»ç¾åå |
| | | 3. åååè¡¨ï¼æååç¼å·ãåå·¥ãååç±»åãæéç±»åãååç¶æãå°æç±»åçé |
| | | 3. è§£é¤/ç»æ¢ï¼ä»
对"å¾
çæ/çæä¸/å³å°å°æ"çåå坿ä½ï¼å¼¹åºç¡®è®¤æ¡å¡«åè§£é¤æ¥æä¸åå |
| | | 4. å°ææéï¼åè¡¨æ¯æãå³å°å°æ / 已尿ãå¿«æ·çéï¼HRM é¦é¡µå¯è°ç¨ `expiring-count` å±ç¤ºå³å°å°æååæ°é |
| | | |
| | | ## API |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | POST | /hrm/employee-contract/create | æ°å¢åå | |
| | | | PUT | /hrm/employee-contract/update | ä¿®æ¹åå | |
| | | | PUT | /hrm/employee-contract/terminate | è§£é¤/ç»æ¢åå | |
| | | | DELETE | /hrm/employee-contract/delete | å é¤åå | |
| | | | GET | /hrm/employee-contract/get | åå详æ
ï¼å«éä»¶ï¼ | |
| | | | GET | /hrm/employee-contract/page | ååå页 | |
| | | | GET | /hrm/employee-contract/expiring-count | å³å°å°æååæ°éï¼ä¾å¾
å/é¦é¡µæéï¼ | |
| | | |
| | | ### å建/ä¿®æ¹åå |
| | | |
| | | **请æ±åæ°ï¼create/update å
±ç¨ï¼ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | id | Long | å¦ | ååIDï¼ä¿®æ¹æ¶å¿
ä¼ ï¼ | |
| | | | employeeId | Long | æ¯ | åå·¥ID | |
| | | | parentId | Long | å¦ | ç»ç¾ä¸ä¸ä»½ååIDã**ç»ç¾æé®**ç¹å»æ¶å端ä¼ä¼ ï¼æå被ç»ç¾çååï¼ï¼æ®éæ°å¢ä¸ä¼ ï¼å端èªå¨å
³è该åå·¥æè¿ä¸ä»½åå | |
| | | | contractType | Integer | æ¯ | ååç±»åï¼åå
¸ hrm_contract_typeï¼1-å³å¨åå 2-å³å¡åå 3-å®ä¹ åè®® 4-å³å¡æ´¾é£ 5-å
¶ä» | |
| | | | contractTermType | Integer | æ¯ | æéç±»åï¼åå
¸ hrm_contract_term_typeï¼1-åºå®æé 2-æ åºå®æé 3-以宿ä¸å®å·¥ä½ä»»å¡ä¸ºæé | |
| | | | signCompany | String | å¦ | ç¾çº¦ä¸»ä½ï¼å
¬å¸å
¨ç§°ï¼ | |
| | | | signDate | date | å¦ | ç¾è®¢æ¥æ | |
| | | | startDate | date | æ¯ | ååå¼å§æ¥æ | |
| | | | endDate | date | å¦ | ååç»ææ¥æï¼æ åºå®æéå¯ä¸ºç©ºï¼ | |
| | | | probationStartDate | date | å¦ | è¯ç¨æå¼å§æ¥æ | |
| | | | probationEndDate | date | å¦ | è¯ç¨æç»ææ¥æ | |
| | | | probationSalary | BigDecimal | å¦ | è¯ç¨æå·¥èµ | |
| | | | regularSalary | BigDecimal | å¦ | 转æ£å·¥èµ | |
| | | | remark | String | å¦ | 夿³¨ | |
| | | | blobIds | Array | å¦ | ååéä»¶ blobId å表 | |
| | | |
| | | **æ ¡éªè§åï¼å端强å¶ï¼ï¼** |
| | | - æéç±»å â æ åºå®æé(2) æ¶ï¼endDate å¿
å¡« |
| | | - startDate ä¸è½æäº endDateï¼è¯ç¨æå¼å§ä¸è½æäºè¯ç¨æç»æ |
| | | - ååç¼å·ç±å端èªå¨çæï¼HT + æ¥æ + åºå·ï¼ï¼å端æ éå¡«åãä¸å¯ä¿®æ¹ |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": 1 |
| | | } |
| | | ``` |
| | | |
| | | ### è§£é¤/ç»æ¢åå |
| | | |
| | | **请æ±åæ°ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | id | Long | æ¯ | ååID | |
| | | | terminateStatus | Integer | æ¯ | 1-å·²è§£é¤ 2-å·²ç»æ¢ | |
| | | | terminateDate | date | æ¯ | è§£é¤/ç»æ¢æ¥æ | |
| | | | terminateReason | String | æ¯ | è§£é¤/ç»æ¢åå | |
| | | |
| | | **ååºï¼** `{ "code": 0, "data": true }` |
| | | |
| | | ### ååå页 |
| | | |
| | | **请æ±åæ°ï¼çéï¼ï¼** |
| | | |
| | | | åæ° | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | pageNo / pageSize | Integer | å页忰 | |
| | | | contractNo | String | ååç¼å·ï¼æ¨¡ç³å¹é
ï¼ | |
| | | | employeeId | Long | åå·¥ID | |
| | | | contractType | Integer | ååç±»å | |
| | | | contractTermType | Integer | æéç±»å | |
| | | | terminateStatus | Integer | 0-æ£å¸¸ 1-å·²è§£é¤ 2-å·²ç»æ¢ | |
| | | | status | Integer | ååç¶æï¼å¨æè®¡ç®ï¼ï¼1-å¾
çæ 2-çæä¸ 3-å³å°å°æ 4-已尿 5-å·²è§£é¤ 6-å·²ç»æ¢ | |
| | | | expiryType | Integer | å°æç±»åï¼1-å³å°å°æ 2-已尿ï¼å¾
åæéç¨ï¼ä¸ status äºæ¥ï¼ | |
| | | |
| | | **ååºå段æ°å¢ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | contractNo | String | ååç¼å·ï¼èªå¨çæï¼ | |
| | | | parentId | Long | ç»ç¾ä¸ä¸ä»½ååID | |
| | | | parentNo | String | ç»ç¾ä¸ä¸ä»½ååç¼å·ï¼å端å±ç¤º"ç»ç¾èª"ï¼ | |
| | | | isCurrent | Boolean | æ¯å¦åå·¥å½åçæååï¼true æ¶åç«¯ææ ï¼ | |
| | | | employeeName | String | åå·¥å§åï¼èªå¨å¡«å
ï¼ | |
| | | | employeeNo | String | å工工å·ï¼èªå¨å¡«å
ï¼ | |
| | | | deptName | String | é¨é¨åç§°ï¼èªå¨å¡«å
ï¼ | |
| | | | status | Integer | ååç¶æï¼å端æå½åæ¥æå¨æè®¡ç®ï¼ | |
| | | | attachmentList | Array | éä»¶å表ï¼ä»
详æ
æ¥å£è¿åï¼ | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": { |
| | | "list": [ |
| | | { |
| | | "id": 1, |
| | | "contractNo": "HT202608100001", |
| | | "employeeId": 1, |
| | | "employeeName": "å¼ ä¸", |
| | | "employeeNo": "EMP202401010001", |
| | | "deptName": "ç åé¨", |
| | | "contractType": 1, |
| | | "contractTermType": 1, |
| | | "startDate": "2026-01-01", |
| | | "endDate": "2027-01-01", |
| | | "status": 2, |
| | | "terminateStatus": 0 |
| | | } |
| | | ], |
| | | "total": 1 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å表 | 表å | 详æ
| 说æ | |
| | | |------|------|------|------|------| |
| | | | ååç¼å· | â
| åªè¯»èªå¨çæ | â
| HT + æ¥æ + åºå· | |
| | | | åå·¥å§å | â
| 䏿鿩 | â
| å¤ç¨å工精ç®å表 | |
| | | | é¨é¨åç§° | â
| èªå¨å¸¦å
¥ | â
| éæ©åå·¥åå±ç¤º | |
| | | | ååç±»å | â
| 䏿 | â
| åå
¸ hrm_contract_type | |
| | | | æéç±»å | â
| 䏿 | â
| åå
¸ hrm_contract_term_type | |
| | | | ç¾è®¢æ¥æ | â
| æ¥æ | â
| | |
| | | | å¼å§/ç»ææ¥æ | â
| æ¥æ | â
| æ åºå®æéç»ææ¥æå¯ç©º | |
| | | | è¯ç¨æèµ·æ¢ | â
| æ¥æ | â
| | |
| | | | è¯ç¨æ/转æ£å·¥èµ | â
| æ°å | â
| | |
| | | | ååç¶æ | â
æ ç¾ | - | â
| å¨æè®¡ç®ï¼æé¢è²åºåï¼çæä¸(绿)/å³å°å°æ(æ©)/已尿(ç°)/已解é¤Â·å·²ç»æ¢(红)/å¾
çæ(è) | |
| | | | éä»¶ | - | ä¸ä¼ | â
| èµ°ç³»ç»éä»¶ä¸ä¼ | |
| | | | æä½ | â
| - | - | ç¼è¾ / è§£é¤/ç»æ¢ / å é¤ | |
| | | |
| | | ## éä»¶å¯¹æ¥æµç¨ |
| | | |
| | | 1. ä¸ä¼ ï¼è°ç¨ `POST /system/storage-blob/upload` è·å blobId |
| | | 2. ä¿åååï¼è¡¨å `blobIds` åæ®µä¼ blobId æ°ç»ï¼å端ç»å® |
| | | 3. æ¥è¯¢ï¼è¯¦æ
æ¥å£è¿å `attachmentList`ï¼å表页å¦éåç¬å·æ°éä»¶ï¼å¯è°ç¨ `GET /system/storage-attachment/list?recordType=hrm_employee_contract&recordId=xxx` |
| | | 4. ä¸å¡è¡¨ä¸åæä»¶å°åï¼ç¦æ¢å¨å端è°ç¨ `/infra/file/*` æ§æ¥å£ |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | > **å¤åå设计**ï¼ä¸ä¸ªåå·¥å¯ç¾è®¢å¤æ¬¡ååï¼ç»ç¾/æ¢ç¾ï¼ãæ°æ®è¡¨éè¿ `parent_id`ï¼ç»ç¾ä¸ä¸ä»½ååIDï¼å½¢æç»ç¾é¾ï¼éè¿ `is_current`ï¼æ¯å¦å½åçæååï¼æ è¯åå·¥å½åååï¼åä¸åå·¥ä»»ææ¶å»ä»
ä¸ä»½ `is_current=true` çååã |
| | | |
| | | | åºæ¯ | è§å | |
| | | |------|------| |
| | | | ç¶æè®¡ç® | 已解é¤(5)/å·²ç»æ¢(6) > 已尿(4) > å¾
çæ(1) > å³å°å°æ(3) > çæä¸(2)ï¼å端æå½åæ¥æå¨æè®¡ç®ï¼å端æ éç»´æ¤ | |
| | | | å³å°å°æ | ç»ææ¥æå¨å½å¤©èµ·ç 30 天å
ï¼å«å½å¤©ï¼ä¸æªè§£é¤/ç»æ¢ | |
| | | | ç»ç¾å
³è | æä½åãç»ç¾ãæé®ç¹å»æ¶å端æ¾å¼ä¼ `parent_id` æå被ç»ç¾ååï¼æ®éæ°å¢ä¸ä¼ ï¼å端èªå¨æè¯¥åå·¥æè¿ä¸ä»½ååä½ä¸º `parent_id`ï¼ç»ç¾é¾ï¼ãå端å表/详æ
å±ç¤º"ç»ç¾èª xxx" | |
| | | | å½ååå | å建æ°ååæ¶ï¼å端èªå¨æ¸
é¤è¯¥åå·¥æ§ååç `is_current` å¹¶ç½®æ°åå为 trueï¼è§£é¤/ç»æ¢å½åååæ¶èªå¨æ¸
餿 è®° | |
| | | | æ¶é´éå | åç«¯å¼ºå¶æ ¡éªï¼åä¸åå·¥ä¸è½å卿¶é´éå çæªè§£é¤/æªç»æ¢ååï¼æ åºå®æéè§ä¸ºæ éé¿ï¼ï¼æ¥é"该å工卿鿶鴿®µå·²åå¨çæåå"ï¼éå
è§£é¤/ç»æ¢æ§ååæè°æ´æ¥æ | |
| | | | è§£é¤/ç»æ¢ | ä»
æªè§£é¤/æªç»æ¢çåå坿ä½ï¼å·²è§£é¤/å·²ç»æ¢ååä¸å¯é夿ä½ï¼æä½åç¶æåºå®ä¸ºå·²è§£é¤/å·²ç»æ¢ | |
| | | | ç¼è¾éå¶ | 已解é¤/å·²ç»æ¢çåå建议åªè¯»å±ç¤ºï¼ä¸å
许修æ¹èµ·æ¢æ¥æï¼å端æªå¼ºå¶ï¼å端æç¤ºå³å¯ï¼ | |
| | | | å é¤ | ç´æ¥å é¤è®°å½å¹¶çº§èå é¤éä»¶ | |
| | | | å°ææé | å表ãå³å°å°æãçé = status=3 æ expiryType=1ï¼é¦é¡µå¯éè¿ expiring-count å±ç¤ºæ°é | |
| | | | å工维度 | åå页æåå·¥çé坿¥çæåå·¥å
¨é¨ååï¼å工详æ
页ãåå·¥ååãTab å±ç¤ºè¯¥åå·¥å
¨é¨ååå¹¶æ è®°å½ååå | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - ååç¼å·å¯ä¸ï¼ç±å端çæï¼å端ä¸è½ä¿®æ¹ |
| | | - æ°å¢/ç¼è¾ååçæ ¡éªï¼åºå®æéå¿
é¡»å¡«ç»ææ¥æãæ¥æå
åï¼ç±å端è¿åæç¡®é误æç¤ºï¼åç«¯è¡¨åæ ¡éªå¯åæ¥å ä¸ |
| | | - åè¡¨ç¶æåéæ ¹æ® status 弿 å°åå
¸ hrm_contract_status å±ç¤ºä¸æä¸é¢è² |
| | | - è§£é¤/ç»æ¢æä½éäºæ¬¡ç¡®è®¤ï¼é¿å
误æä½ |
| | | - å页æ¥è¯¢éä»¶å表为空æ¶ä¸è¿åè¯¥åæ®µï¼å端éå空å¼å
¼å®¹ |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # éè´æ¥ç¥¨ + 仿¬¾å
³èæ¥ç¥¨ - å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - éè´æ¥ç¥¨ç®¡ç页ï¼**æ°å»ºé¡µé¢**ï¼åèéè´ç³è¯·/éè´è®¢å页é¢ç»æï¼ |
| | | - éè´è®¢å详æ
页ï¼å±ç¤ºè¯¥è®¢åä¸å·²å
³èçæ¥ç¥¨ä¿¡æ¯ï¼ |
| | | - 仿¬¾å管çé¡µï¼æ°å»º/ç¼è¾å¼¹çªãå表ã详æ
ï¼ |
| | | - 仿¬¾å详æ
页ï¼å±ç¤ºå
³èçæ¥ç¥¨ä¿¡æ¯ï¼ |
| | | |
| | | ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å
¥ |
| | | |
| | | 1. éè´è®¢åï¼å®¡æ ¸éè¿ï¼-> å建æ¥ç¥¨ï¼`purchaseOrderId` å¿
éï¼ä¾åºåèªå¨å¸¦åºï¼ä»¥éè´è®¢åçä¾åºå为åï¼å端æ éä¼ `supplierId`ï¼ç±å端æéè´è®¢åæ¥è¯¢è®¾ç½®ï¼ã |
| | | 2. æ¥ç¥¨å建å -> ä¸ä¼ å票éä»¶ï¼èµ° system 模å Storage APIï¼ã |
| | | 3. æ¥ç¥¨æäº¤å®¡æ¹ -> 审æ¹éè¿ -> æå¯ä½ä¸ºä»æ¬¾å
³è对象ã |
| | | 4. æ°å»ºä»æ¬¾å -> éæ©å
³èæ¥ç¥¨ï¼`invoiceId` å¿
å¡«ï¼ï¼ä» `GET /erp/purchase-invoice/simple-list?supplierId=&purchaseOrderId=` éæ©ï¼ä»
æ¾ç¤º"审æ¹éè¿ + å·²ä¸ä¼ éä»¶ + å©ä½å¯ä»éé¢ > 0"çæ¥ç¥¨ï¼éæ©å `remainingInvoicePrice` å¯ç¨äºé¢å¡«ä»æ¬¾éé¢ä¸éã |
| | | |
| | | 对åºå
³ç³»ï¼**éè´è®¢å(1) -> æ¥ç¥¨(N) -> 仿¬¾(M)**ã |
| | | |
| | | ## API |
| | | |
| | | ### 1. æ¥ç¥¨ç®¡çï¼æ°ï¼ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | POST | /erp/purchase-invoice/create | å建æ¥ç¥¨ | |
| | | | PUT | /erp/purchase-invoice/update | æ´æ°æ¥ç¥¨ | |
| | | | DELETE | /erp/purchase-invoice/delete?id= | å 餿¥ç¥¨ | |
| | | | GET | /erp/purchase-invoice/get?id= | è·å¾æ¥ç¥¨è¯¦æ
| |
| | | | GET | /erp/purchase-invoice/page | æ¥ç¥¨å页 | |
| | | | GET | /erp/purchase-invoice/export-excel | å¯¼åºæ¥ç¥¨ Excel | |
| | | | PUT | /erp/purchase-invoice/submit?id= | æäº¤æ¥ç¥¨å®¡æ¹ | |
| | | | GET | /erp/purchase-invoice/audit-count | å¾
å®¡æ ¸æ¥ç¥¨æ°é | |
| | | | GET | /erp/purchase-invoice/simple-list?supplierId=&purchaseOrderId= | ç²¾ç®å表ï¼ä»æ¬¾è¡¨åéæ¥ç¥¨ï¼ | |
| | | |
| | | **å建/æ´æ°æ¥ç¥¨è¯·æ±åæ°ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | id | Long | å¦ | æ´æ°æ¶å¿
å¡« | |
| | | | purchaseOrderId | Long | æ¯ | éè´è®¢åç¼å·ï¼å¿
é | |
| | | | supplierId | Long | å¦ | ä¾åºåç¼å·ï¼**ä¸ä¼ **ï¼ç±å端æéè´è®¢åæ¥è¯¢è®¾ç½® | |
| | | | invoiceNo | String | å¦ | å票å·ç | |
| | | | invoiceTitle | String | å¦ | å票æ¬å¤´ | |
| | | | price | BigDecimal | æ¯ | æ¥ç¥¨éé¢ï¼å
ï¼ | |
| | | | invoiceTime | LocalDateTime | æ¯ | æ¥ç¥¨æ¥æ | |
| | | | remark | String | å¦ | 夿³¨ | |
| | | | blobIds | List\<Long\> | å¦ | å票éä»¶ blobId å表 | |
| | | |
| | | **æ¥ç¥¨å页/详æ
ååºæ°å¢å段ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | no | String | æ¥ç¥¨ç¼å·ï¼å端çæï¼åç¼ FPDï¼ | |
| | | | supplierName | String | ä¾åºååç§° | |
| | | | order | Object | éè´è®¢åä¿¡æ¯ï¼åµå¥ï¼ | |
| | | | auditStatus | Integer | 审æ¹ç¶æï¼0æªæäº¤/10审æ¹ä¸/20å®¡æ ¸éè¿/30å®¡æ ¸ä¸éè¿/40å·²åæ¶ï¼ | |
| | | | attachmentList | List | å票éä»¶å表 | |
| | | | hasAttachment | Boolean | æ¯å¦å·²ä¸ä¼ å票éä»¶ | |
| | | | remainingInvoicePrice | BigDecimal | å©ä½å¯ä»éé¢ = æ¥ç¥¨éé¢ - 该æ¥ç¥¨å·²ä»æ¬¾éé¢ | |
| | | |
| | | **simple-list ååºï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | id | Long | æ¥ç¥¨ç¼å· | |
| | | | no | String | æ¥ç¥¨ç¼å· | |
| | | | invoiceNo | String | å票å·ç | |
| | | | invoiceTitle | String | å票æ¬å¤´ | |
| | | | price | BigDecimal | æ¥ç¥¨éé¢ | |
| | | | supplierId | Long | ä¾åºåç¼å· | |
| | | | purchaseOrderId | Long | éè´è®¢åç¼å· | |
| | | | auditStatus | Integer | 审æ¹ç¶æ | |
| | | | hasAttachment | Boolean | æ¯å¦å·²ä¸ä¼ éä»¶ï¼æä¸º trueï¼ | |
| | | | remainingInvoicePrice | BigDecimal | å©ä½å¯ä»éé¢ | |
| | | |
| | | ### 2. 仿¬¾åï¼æ¥å£åæ´ï¼ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | POST | /erp/finance-payment/create | åå»ºä»æ¬¾åï¼è¯·æ±ä½æ°å¢ `invoiceId`ï¼å¿
å¡«ï¼ | |
| | | | PUT | /erp/finance-payment/update | æ´æ°ä»æ¬¾åï¼è¯·æ±ä½æ°å¢ `invoiceId`ï¼å¿
å¡«ï¼ | |
| | | | GET | /erp/finance-payment/get | ååºæ°å¢ `invoiceId`ã`invoice` åµå¥å¯¹è±¡ | |
| | | | GET | /erp/finance-payment/page | ååºæ°å¢ `invoiceId`ã`invoice` åµå¥å¯¹è±¡ | |
| | | |
| | | **请æ±åæ°æ°å¢ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | invoiceId | Long | æ¯ | å
³èçæ¥ç¥¨ç¼å·ï¼æ°å»ºä»æ¬¾åå¿
å¡«ï¼ | |
| | | |
| | | **ååºå段æ°å¢ï¼** |
| | | |
| | | | åæ®µ | ç±»å | 说æ | |
| | | |------|------|------| |
| | | | invoiceId | Long | å
³èçæ¥ç¥¨ç¼å· | |
| | | | invoice | Object | å
³èçæ¥ç¥¨ä¿¡æ¯ï¼å« noãinvoiceNoãinvoiceTitleãpriceãauditStatus çï¼ | |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": { |
| | | "id": 1, |
| | | "no": "FKD20260810000001", |
| | | "invoiceId": 100, |
| | | "invoice": { |
| | | "id": 100, |
| | | "no": "FPD20260810000001", |
| | | "invoiceNo": "02564178", |
| | | "invoiceTitle": "XXç§ææéå
¬å¸", |
| | | "price": 9000.00, |
| | | "auditStatus": 20 |
| | | }, |
| | | "paymentPrice": 5000.00 |
| | | } |
| | | } |
| | | ``` |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |------|----------|------| |
| | | | æ¥ç¥¨ç¼å· | æ¥ç¥¨å表ã详æ
ã仿¬¾è¯¦æ
| åç¼ FPD | |
| | | | ä¾åºå | æ¥ç¥¨å表ã详æ
| æéè´è®¢åå¸¦åº | |
| | | | éè´è®¢å | æ¥ç¥¨å表ã详æ
| å±ç¤ºè®¢åç¼å·/ä¿¡æ¯ | |
| | | | å票å·ç /æ¬å¤´ | æ¥ç¥¨å表ã详æ
| å½å
¥å段 | |
| | | | æ¥ç¥¨éé¢ | æ¥ç¥¨å表ã详æ
ã仿¬¾è¡¨å | å
| |
| | | | æ¥ç¥¨æ¥æ | æ¥ç¥¨å表ã详æ
| | |
| | | | 审æ¹ç¶æ | æ¥ç¥¨å表ã详æ
| åå
¸å±ç¤º | |
| | | | å票éä»¶ | æ¥ç¥¨è¯¦æ
ãæ¥ç¥¨å表 | ä¸ä¼ /é¢è§/ä¸è½½ | |
| | | | å©ä½å¯ä»éé¢ | æ¥ç¥¨å表ã仿¬¾è¡¨å | æ¥ç¥¨éé¢ - 已仿¬¾éé¢ | |
| | | | å
³èæ¥ç¥¨ | 仿¬¾å表ã仿¬¾è¯¦æ
| å±ç¤º invoice.no / invoice.invoiceNo / invoice.price | |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | | åºæ¯ | è§å | |
| | | |------|------| |
| | | | æ¥ç¥¨å建 | éè´è®¢åå¿
é¡»å®¡æ ¸éè¿ï¼æ¥ç¥¨ä¾åºåä¸éè´è®¢åä¸è´ï¼æ¥ç¥¨éé¢ä¸è¶
è¿è®¢åå©ä½å¯å¼ç¥¨éé¢ | |
| | | | æ¥ç¥¨ç¼è¾ | ä»
"æªæäº¤"ç¶æå¯ç¼è¾ | |
| | | | æ¥ç¥¨å é¤ | 审æ¹éè¿åä¸å¯å é¤ | |
| | | | æ¥ç¥¨æäº¤å®¡æ¹ | ä»
"æªæäº¤"ç¶æå¯æäº¤ï¼æäº¤åè¿å
¥å®¡æ¹ä¸ | |
| | | | 仿¬¾éæ©æ¥ç¥¨ | ä»
审æ¹éè¿ + å·²ä¸ä¼ å票éä»¶çæ¥ç¥¨å¯é | |
| | | | 仿¬¾éé¢ä¸é | 仿¬¾éé¢ <= 该æ¥ç¥¨éé¢ - 该æ¥ç¥¨å·²ä»æ¬¾éé¢ï¼æªå®¡æ ¸+审æ¹ä¸+å·²å®¡æ ¸ä»æ¬¾ï¼æé¤ä½åºï¼ | |
| | | | ä¾åºåä¸è´æ§ | 仿¬¾çä¾åºåå¿
é¡»ä¸å
³èæ¥ç¥¨çä¾åºåä¸è´ | |
| | | | åé仿¬¾å | invoiceId 为空çåé仿¬¾åï¼ç¼è¾æ¶ééæ°éæ©åæ ¼æ¥ç¥¨ | |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - æ¥ç¥¨ä¾åºåä¸ä»åç«¯ä¼ å
¥ï¼å建/æ´æ°æ¶ç±åç«¯æ ¹æ®éè´è®¢åèªå¨è®¾ç½®ï¼å端表ååªå±ç¤ºã |
| | | - 仿¬¾è¡¨åéæ©æ¥ç¥¨æ¶ï¼å
æä¾åºåè¿æ»¤ï¼`supplierId`ï¼ï¼å¯éè®¢åæ¶ä¼ `purchaseOrderId` è¿ä¸æ¥è¿æ»¤ã |
| | | - `remainingInvoicePrice` ä¸ºè´æ°æ 0 çæ¥ç¥¨ä¸ä¼åºç°å¨ simple-list ä¸ï¼å端å¯ç´æ¥æè¿åå表渲æã |
| | | - 仿¬¾é颿 ¡éªå¨å端ï¼å端å¯å°æ¥ç¥¨ç `remainingInvoicePrice` ä½ä¸ºä»æ¬¾éé¢è¾å
¥ä¸éæç¤ºã |
| | | - æ¥ç¥¨å®¡æ¹ç¶æååéè¿ BPM åè°æ´æ°ï¼å端æ éå¤çï¼å页æ¥å£ç´æ¥è¿åææ°ç¶æã |
| | | - æ¥ç¥¨å票éä»¶ä¸ä¼ ä½¿ç¨ system 模å Storage APIï¼upload -> bindï¼ï¼ä¸è¦ä½¿ç¨æ§ç /infra/file æ¥å£ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | # å¼ç¥¨ä¸å款å
³èï¼å
å¼ç¥¨å忬¾ï¼ - å端èè°æ¹æ¡ |
| | | |
| | | ## æ¶åé¡µé¢ |
| | | |
| | | - 忬¾ç®¡çå表页 |
| | | - 忬¾å建 / ç¼è¾è¡¨å |
| | | - 忬¾è¯¦æ
页 |
| | | - å¼ç¥¨ç®¡çå表页ãå¼ç¥¨è¯¦æ
页ï¼**å端ç®å缺失ï¼éæ°å»º**ï¼ |
| | | - åå管çå表页ãåå详æ
页ï¼ä¸ä¸ªä»»å¡å·²æ¥å
¥"å·²å¼ç¥¨éé¢/æªå¼ç¥¨éé¢"ï¼æ¬æ¬¡ä¸æ¶åï¼ |
| | | |
| | | ## ä¸å¡æµç¨ä¸æ°æ®å¸¦å
¥ |
| | | |
| | | 1. **å¼ç¥¨ â 忬¾**ï¼å¼ç¥¨å审æ¹éè¿ + ä¸ä¼ å票éä»¶åï¼æä¼åºç°å¨å款å建表åç"å
³èå¼ç¥¨"䏿ä¸ã |
| | | 2. **忬¾å建**ï¼éæ©å®¢æ· â éæ©åå â éæ©å
³èå¼ç¥¨ï¼èªå¨å¸¦å
¥å®¢æ·ãååï¼âï¼å¯éï¼éæ©å款计å â 填忬¾éé¢ï¼å¯é¢å¡«ä¸ºè¯¥å¼ç¥¨çå©ä½å¯å款éé¢ï¼ã |
| | | 3. **åå䏿ï¼simple-listï¼**ï¼å·²è¿å `totalInvoicePrice`ï¼å·²å¼ç¥¨éé¢ï¼å«å®¡æ¹ä¸ï¼ï¼ç¨äºå±ç¤ºååå©ä½å¯å¼é¢åº¦ã |
| | | 4. **å¼ç¥¨ä¸æï¼simple-listï¼**ï¼è¿å `remainingInvoicePrice`ï¼è¯¥å¼ç¥¨å©ä½å¯å款éé¢ï¼ï¼ç¨äºåå»ºåæ¬¾æ¶é¢å¡«éé¢ã |
| | | |
| | | ## API |
| | | |
| | | ### 1. æ°å¢æ¥å£ï¼æååæ¥è¯¢å¯éå¼ç¥¨ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | GET | `/crm/invoice/simple-list?contractId=` | 忬¾å建æ¶éæ©å
³èå¼ç¥¨ï¼åªè¿ååæ ¼å¼ç¥¨ | |
| | | |
| | | **çéè§åï¼å端已å®ç°ï¼**ï¼å®¡æ¹éè¿ + å·²ä¸ä¼ å票éä»¶ + å©ä½å¯å款éé¢ > 0ã |
| | | |
| | | **ååºç¤ºä¾ï¼** |
| | | |
| | | ```json |
| | | { |
| | | "code": 0, |
| | | "data": [ |
| | | { |
| | | "id": 1, |
| | | "no": "KP20260810000001", |
| | | "invoiceNo": "02564178", |
| | | "invoiceTitle": "XXç§ææéå
¬å¸", |
| | | "price": 1090.00, |
| | | "customerId": 1, |
| | | "contractId": 31, |
| | | "auditStatus": 20, |
| | | "hasAttachment": true, |
| | | "remainingInvoicePrice": 1090.00 |
| | | } |
| | | ] |
| | | } |
| | | ``` |
| | | |
| | | ### 2. åæ´æ¥å£ï¼å款å建 / æ´æ° |
| | | |
| | | | æ¹æ³ | è·¯å¾ | 说æ | |
| | | |------|------|------| |
| | | | POST | `/crm/receivable/create` | 请æ±ä½æ°å¢ `invoiceId`ï¼**å¿
å¡«**ï¼ | |
| | | | PUT | `/crm/receivable/update` | 请æ±ä½æ°å¢ `invoiceId`ï¼å¯ä¿®æ¹ï¼ | |
| | | |
| | | **请æ±åæ°æ°å¢ï¼** |
| | | |
| | | | åæ° | ç±»å | å¿
å¡« | 说æ | |
| | | |------|------|------|------| |
| | | | invoiceId | Long | æ¯ | å
³èçå¼ç¥¨ç¼å·ï¼å¿
须为审æ¹éè¿ä¸å·²ä¸ä¼ éä»¶çå¼ç¥¨ | |
| | | |
| | | ### 3. åæ´æ¥å£ï¼å款æ¥è¯¢ï¼ååºæ°å¢åæ®µï¼ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | æ°å¢ååºå段 | |
| | | |------|------|--------------| |
| | | | GET | `/crm/receivable/page` | `invoiceId`ã`invoice`ï¼åµå¥å¯¹è±¡ï¼ | |
| | | | GET | `/crm/receivable/get` | `invoiceId`ã`invoice`ï¼åµå¥å¯¹è±¡ï¼ | |
| | | | GET | `/crm/receivable/page-by-customer` | `invoiceId`ã`invoice`ï¼åµå¥å¯¹è±¡ï¼ | |
| | | |
| | | **`invoice` åµå¥å¯¹è±¡å
³é®å段ï¼** `id`ã`no`ï¼å¼ç¥¨ç¼å·ï¼ã`invoiceNo`ï¼å票å·ç ï¼ã`invoiceTitle`ï¼å票æ¬å¤´ï¼ã`price`ï¼å¼ç¥¨éé¢ï¼ã |
| | | |
| | | ### 4. åæ´æ¥å£ï¼å¼ç¥¨æ¥è¯¢ï¼ååºæ°å¢åæ®µï¼ |
| | | |
| | | | æ¹æ³ | è·¯å¾ | æ°å¢ååºå段 | |
| | | |------|------|--------------| |
| | | | GET | `/crm/invoice/page` | `hasAttachment`ã`remainingInvoicePrice` | |
| | | | GET | `/crm/invoice/get` | `hasAttachment`ã`remainingInvoicePrice` | |
| | | |
| | | **说æï¼** |
| | | - `hasAttachment`ï¼Booleanï¼ï¼æ¯å¦å·²ä¸ä¼ å票éä»¶ï¼ç¨äºå表快é夿åªäºå¼ç¥¨å¯è¢«å款å
³èã |
| | | - `remainingInvoicePrice`ï¼BigDecimalï¼ï¼å©ä½å¯å款éé¢ = å¼ç¥¨éé¢ - 该å¼ç¥¨å·²å款éé¢ï¼ç»è®¡è稿 + 审æ¹ä¸ + 审æ¹éè¿ç忬¾ï¼ã |
| | | |
| | | ## åæ®µå±ç¤ºè§å |
| | | |
| | | | åæ®µ | å±ç¤ºä½ç½® | 说æ | |
| | | |------|----------|------| |
| | | | å
³èå¼ç¥¨ | 忬¾å表åã忬¾è¯¦æ
页 | å±ç¤º `invoice.no` / `invoice.invoiceNo` / `invoice.price` | |
| | | | å
³èå¼ç¥¨éæ©å¨ | 忬¾å建/ç¼è¾è¡¨å | éæ©å带å
¥å®¢æ·ãååãé¢å¡«å款éé¢ä¸º `remainingInvoicePrice` | |
| | | | å©ä½å¯å款éé¢ | å¼ç¥¨å表åãå¼ç¥¨è¯¦æ
页 | å¼ç¥¨éé¢ - 该å¼ç¥¨å·²å款éé¢ | |
| | | | æ¯å¦å·²ä¸ä¼ éä»¶ | å¼ç¥¨å表å | è¾
å©å¤æå¼ç¥¨æ¯å¦å¯å款ï¼é审æ¹éè¿ + å·²ä¸ä¼ éä»¶ï¼ | |
| | | |
| | | ## ä¸å¡è§å说æ |
| | | |
| | | | åºæ¯ | è§å | |
| | | |------|------| |
| | | | 审æ¹ä¸çå¼ç¥¨ | ä¸å¯éä¸ºåæ¬¾ä¾æ®ï¼åç«¯æ¦æªï¼å
³èå¼ç¥¨ä¸æ¯å®¡æ¹éè¿ç¶æï¼ | |
| | | | æªä¸ä¼ å票éä»¶çå¼ç¥¨ | ä¸å¯éä¸ºåæ¬¾ä¾æ®ï¼åç«¯æ¦æªï¼å
³èå¼ç¥¨æªä¸ä¼ å票éä»¶ï¼ | |
| | | | å·²å
¨é¨å款çå¼ç¥¨ | ä»"å
³èå¼ç¥¨"ä¸æè¿æ»¤ï¼å©ä½å¯å款 ⤠0ï¼ | |
| | | | 忬¾éé¢è¶
é | åç«¯æ¦æªï¼å款éé¢è¶
åºè¯¥å¼ç¥¨å©ä½å¯å款éé¢ | |
| | | | å¼ç¥¨ä¸ååä¸å¹é
| åç«¯æ¦æªï¼æéå¼ç¥¨ä¸å款ååä¸ä¸è´ | |
| | | | ç¼è¾åæ¬¾æ¶æ¹å¼ç¥¨ | è稿/审æ¹ä¸ç¶æå¯ä¿®æ¹å
³èå¼ç¥¨ï¼æ¹åéæ°ææ°å¼ç¥¨æ ¡éªéé¢ | |
| | | |
| | | ## å端缺å£ï¼éæ°å»ºï¼ |
| | | |
| | | > å端å¼ç¥¨æ¨¡å已宿´ï¼å«å®¡æ¹æµç¨ãéä»¶ä¸ä¼ ï¼ï¼ä½åç«¯å°æ å¼ç¥¨é¡µé¢ä¸ APIï¼éå端æ°å»ºã |
| | | |
| | | 1. **å¼ç¥¨ç®¡ç页**ï¼`views/crm/invoice/`ï¼å表ãå建/ç¼è¾ãå®¡æ¹æäº¤ãå票éä»¶ä¸ä¼ ä¸å±ç¤ºï¼ã |
| | | 2. **å¼ç¥¨ API**ï¼`api/crm/invoice/`ï¼page / get / create / update / delete / submit / audit-count / export-excel / simple-listï¼ã |
| | | 3. **忬¾è¡¨åå¼ç¥¨éæ©å¨**ï¼éåååè°ç¨ `/crm/invoice/simple-list?contractId=`ï¼å±ç¤º"å¼ç¥¨ç¼å· / å票å·ç / å¼ç¥¨éé¢ / å©ä½å¯å款"ï¼éä¸åé¢å¡«éé¢ã |
| | | 4. **èå**ï¼å·²ç±å端 SQL é
ç½®å®æï¼è¥é管ç -> å¼ç¥¨ç®¡çï¼`component = crm/invoice/index`ï¼ï¼å端建好页é¢åèåå³å¯ç¨ã |
| | | |
| | | ## 注æäºé¡¹ |
| | | |
| | | - `invoiceId` 为å¿
å¡«ï¼å款å建/æ´æ°**å¿
é¡»**æºå¸¦ï¼å¦åè¿ååæ°æ ¡éªé误ã |
| | | - `invoice`ã`remainingInvoicePrice`ã`hasAttachment` å为å端计ç®çåªè¯»å段ï¼å端**ä¸è¦**æäº¤ã |
| | | - 忬¾éé¢å»ºè®®å¨éä¸å¼ç¥¨åé¢å¡«ä¸º `remainingInvoicePrice`ï¼é¿å
è¶
é被åç«¯æ¦æªã |
| | | - åé忬¾æ°æ®è¥æªå
³èå¼ç¥¨ï¼`invoiceId` 为空ï¼ï¼ç¼è¾æ¶ä¼è¢«è¦æ±éæ°éæ©åæ ¼å¼ç¥¨ã |
| | | - å¼ç¥¨å表å±ç¤º"å©ä½å¯å款éé¢"æ¶ï¼ææ¡å»ºè®®ç¨"å©ä½å¯å款éé¢ï¼å«å®¡æ¹ä¸ï¼"é¿å
æ§ä¹ï¼ä¸åå"å·²å¼ç¥¨éé¢ï¼å«å®¡æ¹ä¸ï¼"å£å¾ä¸è´ã |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ============================================= |
| | | -- HRM åå·¥åå管ç SQL åå§åèæ¬ |
| | | -- å
å«ï¼å»ºè¡¨ + æ°æ®åå
¸ + èå |
| | | -- ============================================= |
| | | |
| | | -- ============================================= |
| | | -- 1. åå·¥åå表 |
| | | -- ============================================= |
| | | CREATE TABLE IF NOT EXISTS `hrm_employee_contract` ( |
| | | `id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ååID', |
| | | `parent_id` bigint DEFAULT NULL COMMENT 'ç»ç¾ä¸ä¸ä»½ååIDï¼ä¸ä¸ªåå·¥å¯ç¾è®¢å¤æ¬¡ååï¼éè¿è¯¥å段形æç»ç¾é¾ï¼', |
| | | `is_current` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å½åçæååï¼åå·¥ææ°ç¾è®¢çæªè§£é¤/æªç»æ¢ååï¼', |
| | | `contract_no` varchar(64) NOT NULL COMMENT 'ååç¼å·', |
| | | `employee_id` bigint NOT NULL COMMENT 'åå·¥ID', |
| | | `contract_type` tinyint NOT NULL COMMENT 'ååç±»åï¼1-å³å¨ååï¼2-å³å¡ååï¼3-å®ä¹ åè®®ï¼4-å³å¡æ´¾é£ï¼5-å
¶ä»', |
| | | `contract_term_type` tinyint NOT NULL COMMENT 'ååæéç±»åï¼1-åºå®æéï¼2-æ åºå®æéï¼3-以宿ä¸å®å·¥ä½ä»»å¡ä¸ºæé', |
| | | `sign_company` varchar(128) DEFAULT NULL COMMENT 'ç¾çº¦ä¸»ä½ï¼å
¬å¸å
¨ç§°ï¼', |
| | | `sign_date` date DEFAULT NULL COMMENT 'ç¾è®¢æ¥æ', |
| | | `start_date` date NOT NULL COMMENT 'ååå¼å§æ¥æ', |
| | | `end_date` date DEFAULT NULL COMMENT 'ååç»ææ¥æï¼æ åºå®æéå¯ä¸ºç©ºï¼', |
| | | `probation_start_date` date DEFAULT NULL COMMENT 'è¯ç¨æå¼å§æ¥æ', |
| | | `probation_end_date` date DEFAULT NULL COMMENT 'è¯ç¨æç»ææ¥æ', |
| | | `probation_salary` decimal(10,2) DEFAULT NULL COMMENT 'è¯ç¨æå·¥èµ', |
| | | `regular_salary` decimal(10,2) DEFAULT NULL COMMENT '转æ£å·¥èµ', |
| | | `terminate_status` tinyint NOT NULL DEFAULT 0 COMMENT 'è§£é¤/ç»æ¢ç¶æï¼0-æ£å¸¸ï¼1-已解é¤ï¼2-å·²ç»æ¢', |
| | | `terminate_date` date DEFAULT NULL COMMENT 'è§£é¤/ç»æ¢æ¥æ', |
| | | `terminate_reason` varchar(255) DEFAULT NULL COMMENT 'è§£é¤/ç»æ¢åå ', |
| | | `remark` varchar(255) DEFAULT NULL COMMENT '夿³¨', |
| | | `creator` varchar(64) DEFAULT '' COMMENT 'å建è
', |
| | | `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT 'å建æ¶é´', |
| | | `updater` varchar(64) DEFAULT '' COMMENT 'æ´æ°è
', |
| | | `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT 'æ´æ°æ¶é´', |
| | | `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å é¤', |
| | | PRIMARY KEY (`id`), |
| | | UNIQUE KEY `uk_contract_no` (`contract_no`), |
| | | KEY `idx_employee_id` (`employee_id`), |
| | | KEY `idx_end_date` (`end_date`), |
| | | KEY `idx_terminate_status` (`terminate_status`), |
| | | KEY `idx_parent_id` (`parent_id`) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='HRMåå·¥åå表'; |
| | | |
| | | -- ============================================= |
| | | -- 1.1 å¢éå级ï¼é对已åå¨ç表æ§è¡ï¼æ³¨æ ADD COLUMN 䏿¯æ IF NOT EXISTSï¼é确认åä¸åå¨ï¼ |
| | | -- ============================================= |
| | | ALTER TABLE `hrm_employee_contract` |
| | | ADD COLUMN `parent_id` bigint DEFAULT NULL COMMENT 'ç»ç¾ä¸ä¸ä»½ååID' AFTER `id`, |
| | | ADD COLUMN `is_current` bit(1) NOT NULL DEFAULT b'0' COMMENT 'æ¯å¦å½åçæåå' AFTER `parent_id`, |
| | | ADD INDEX `idx_parent_id` (`parent_id`); |
| | | |
| | | -- ============================================= |
| | | -- 2. æ°æ®åå
¸ç±»å |
| | | -- ============================================= |
| | | INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES |
| | | (1061123, 'ååç±»å', 'hrm_contract_type', 0, NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061124, 'ååæéç±»å', 'hrm_contract_term_type', 0, NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061125, 'ååç¶æ', 'hrm_contract_status', 0, NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061126, 'è§£é¤ç»æ¢ç¶æ', 'hrm_contract_terminate_status', 0, NULL, '', NOW(), '', NOW(), b'0'); |
| | | |
| | | -- ============================================= |
| | | -- 3. æ°æ®åå
¸æ°æ® |
| | | -- ============================================= |
| | | -- 3.1 ååç±»å hrm_contract_type |
| | | INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES |
| | | (1061231, 1, 'å³å¨åå', '1', 'hrm_contract_type', 0, 'primary', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061232, 2, 'å³å¡åå', '2', 'hrm_contract_type', 0, 'success', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061233, 3, 'å®ä¹ åè®®', '3', 'hrm_contract_type', 0, 'warning', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061234, 4, 'å³å¡æ´¾é£', '4', 'hrm_contract_type', 0, 'info', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061235, 5, 'å
¶ä»', '5', 'hrm_contract_type', 0, 'default', NULL, '', NOW(), '', NOW(), b'0'); |
| | | |
| | | -- 3.2 ååæéç±»å hrm_contract_term_type |
| | | INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES |
| | | (1061236, 1, 'åºå®æé', '1', 'hrm_contract_term_type', 0, 'primary', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061237, 2, 'æ åºå®æé', '2', 'hrm_contract_term_type', 0, 'success', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061238, 3, '以宿ä¸å®å·¥ä½ä»»å¡ä¸ºæé', '3', 'hrm_contract_term_type', 0, 'warning', NULL, '', NOW(), '', NOW(), b'0'); |
| | | |
| | | -- 3.3 ååç¶æ hrm_contract_statusï¼å¨æè®¡ç®å±ç¤ºç¨ï¼ |
| | | INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES |
| | | (1061239, 1, 'å¾
çæ', '1', 'hrm_contract_status', 0, 'info', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061240, 2, 'çæä¸', '2', 'hrm_contract_status', 0, 'success', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061241, 3, 'å³å°å°æ', '3', 'hrm_contract_status', 0, 'warning', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061242, 4, '已尿', '4', 'hrm_contract_status', 0, 'default', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061243, 5, '已解é¤', '5', 'hrm_contract_status', 0, 'danger', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061244, 6, 'å·²ç»æ¢', '6', 'hrm_contract_status', 0, 'danger', NULL, '', NOW(), '', NOW(), b'0'); |
| | | |
| | | -- 3.4 è§£é¤ç»æ¢ç¶æ hrm_contract_terminate_status |
| | | INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES |
| | | (1061245, 1, 'æ£å¸¸', '0', 'hrm_contract_terminate_status', 0, 'success', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061246, 2, '已解é¤', '1', 'hrm_contract_terminate_status', 0, 'danger', NULL, '', NOW(), '', NOW(), b'0'), |
| | | (1061247, 3, 'å·²ç»æ¢', '2', 'hrm_contract_terminate_status', 0, 'warning', NULL, '', NOW(), '', NOW(), b'0'); |
| | | |
| | | -- ============================================= |
| | | -- 4. èåï¼æè½½å° HRM 人åèµæº 6001 ä¸ï¼ä¸å工管ç 6006 å¹³çº§ï¼ |
| | | -- 注æï¼ä¸è½æå¨"å工管ç"ä¸ï¼å¦å 6006 ä¼åæå¸¦åèåçç®å½ï¼å¯¼è´åå·¥åè¡¨é¡µæ æ³ç´è¾¾ |
| | | -- ============================================= |
| | | INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `path`, `component`, `parent_id`, `icon`, `sort`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES |
| | | (1063105, 'åå·¥åå', '', 2, 'employee-contract', 'hrm/employee/contract/index', 6001, 'ep:files', 0, 0, b'1', b'1', b'1', '', NOW(), '', NOW(), b'0'), |
| | | (1063106, 'ååæ¥è¯¢', 'hrm:employee-contract:query', 3, '', '', 1063105, '#', 1, 0, b'1', b'1', b'1', '', NOW(), '', NOW(), b'0'), |
| | | (1063107, 'ååæ°å¢', 'hrm:employee-contract:create', 3, '', '', 1063105, '#', 2, 0, b'1', b'1', b'1', '', NOW(), '', NOW(), b'0'), |
| | | (1063108, 'ååä¿®æ¹', 'hrm:employee-contract:update', 3, '', '', 1063105, '#', 3, 0, b'1', b'1', b'1', '', NOW(), '', NOW(), b'0'), |
| | | (1063109, 'ååå é¤', 'hrm:employee-contract:delete', 3, '', '', 1063105, '#', 4, 0, b'1', b'1', b'1', '', NOW(), '', NOW(), b'0'); |
| | |
| | | import cn.iocoder.yudao.module.crm.service.contact.CrmContactService; |
| | | import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; |
| | | import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; |
| | | import cn.iocoder.yudao.module.crm.service.invoice.CrmInvoiceService; |
| | | import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService; |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| | |
| | | private CrmBusinessService businessService; |
| | | @Resource |
| | | private CrmReceivableService receivableService; |
| | | @Resource |
| | | private CrmInvoiceService invoiceService; |
| | | |
| | | @Resource |
| | | private MdmItemApi mdmItemApi; |
| | |
| | | // 1.5 è·å¾å·²å款éé¢ |
| | | Map<Long, BigDecimal> receivablePriceMap = receivableService.getReceivablePriceMapByContractId( |
| | | convertSet(contractList, CrmContractDO::getId)); |
| | | // 1.6 è·å¾å·²å¼ç¥¨éé¢ï¼å®¡æ¹éè¿ + 审æ¹ä¸ï¼ä¸å¼ç¥¨é²è¶
æ ¡éªå£å¾ä¸è´ï¼ |
| | | Map<Long, BigDecimal> invoicePriceMap = invoiceService.getInvoicePriceMapByContractId( |
| | | convertSet(contractList, CrmContractDO::getId)); |
| | | // 2. æ¼æ¥æ°æ® |
| | | return BeanUtils.toBean(contractList, CrmContractRespVO.class, contractVO -> { |
| | | // 2.1 设置客æ·ä¿¡æ¯ |
| | |
| | | findAndThen(businessMap, contractVO.getBusinessId(), business -> contractVO.setBusinessName(business.getName())); |
| | | // 2.5 è®¾ç½®å·²åæ¬¾éé¢ |
| | | contractVO.setTotalReceivablePrice(receivablePriceMap.getOrDefault(contractVO.getId(), BigDecimal.ZERO)); |
| | | // 2.6 设置已å¼ç¥¨éé¢ |
| | | contractVO.setTotalInvoicePrice(invoicePriceMap.getOrDefault(contractVO.getId(), BigDecimal.ZERO)); |
| | | // 2.7 设置æªå¼ç¥¨éé¢ |
| | | if (contractVO.getTotalPrice() != null && contractVO.getTotalInvoicePrice() != null) { |
| | | contractVO.setUnInvoicePrice(contractVO.getTotalPrice().subtract(contractVO.getTotalInvoicePrice())); |
| | | } |
| | | }); |
| | | } |
| | | |
| | |
| | | // æ¼æ¥æ°æ® |
| | | Map<Long, BigDecimal> receivablePriceMap = receivableService.getReceivablePriceMapByContractId( |
| | | convertSet(pageResult.getList(), CrmContractDO::getId)); |
| | | Map<Long, BigDecimal> invoicePriceMap = invoiceService.getInvoicePriceMapByContractId( |
| | | convertSet(pageResult.getList(), CrmContractDO::getId)); |
| | | return success(convertList(pageResult.getList(), contract -> new CrmContractRespVO() // åªè¿å idãname çç²¾ç®å段 |
| | | .setId(contract.getId()).setName(contract.getName()).setAuditStatus(contract.getAuditStatus()) |
| | | .setTotalPrice(contract.getTotalPrice()) |
| | | .setTotalReceivablePrice(receivablePriceMap.getOrDefault(contract.getId(), BigDecimal.ZERO)))); |
| | | .setTotalReceivablePrice(receivablePriceMap.getOrDefault(contract.getId(), BigDecimal.ZERO)) |
| | | .setTotalInvoicePrice(invoicePriceMap.getOrDefault(contract.getId(), BigDecimal.ZERO)))); |
| | | } |
| | | |
| | | @PostMapping("/generate-sale-order") |
| | |
| | | @ExcelProperty(value = "æªå款éé¢ï¼å
ï¼", index = 13) |
| | | private BigDecimal unReceivablePrice; |
| | | |
| | | @Schema(description = "å·²å¼ç¥¨éé¢ï¼å«å®¡æ¹ä¸ï¼ï¼ç»è®¡å®¡æ¹éè¿ + 审æ¹ä¸çå¼ç¥¨", example = "5617") |
| | | private BigDecimal totalInvoicePrice; |
| | | |
| | | @Schema(description = "æªå¼ç¥¨éé¢ï¼çäº ååéé¢ - å·²å¼ç¥¨éé¢", example = "1000.00") |
| | | private BigDecimal unInvoicePrice; |
| | | |
| | | @Schema(description = "å
³è ERP éå®è®¢åç¼å·", example = "12345") |
| | | private Long orderId; |
| | | @Schema(description = "ERP éå®è®¢ååå·", example = "SO001") |
| | |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.invoice.CrmInvoiceDO; |
| | | import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; |
| | | import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; |
| | | import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; |
| | | import cn.iocoder.yudao.module.crm.service.invoice.CrmInvoiceService; |
| | | import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService; |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | | import cn.iocoder.yudao.module.system.enums.storage.StorageRecordTypeEnum; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | private CrmContractService contractService; |
| | | @Resource |
| | | private CrmCustomerService customerService; |
| | | @Resource |
| | | private CrmReceivableService receivableService; |
| | | |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | |
| | | // 1.3 è·å¾ååå表 |
| | | Map<Long, CrmContractDO> contractMap = contractService.getContractMap( |
| | | convertSet(invoiceList, CrmInvoiceDO::getContractId)); |
| | | // 1.4 è·å¾æ¯å¼ å¼ç¥¨å·²å款éé¢ï¼è稿 + 审æ¹ä¸ + 审æ¹éè¿ï¼ |
| | | Map<Long, BigDecimal> receivablePriceMap = receivableService.getReceivablePriceMapByInvoiceId( |
| | | convertSet(invoiceList, CrmInvoiceDO::getId)); |
| | | // 2. æ¼æ¥ç»æ |
| | | return BeanUtils.toBean(invoiceList, CrmInvoiceRespVO.class, (invoiceVO) -> { |
| | | // 2.1 æ¼æ¥å®¢æ·åç§° |
| | |
| | | findAndThen(contractMap, invoiceVO.getContractId(), contract -> |
| | | invoiceVO.setContract(BeanUtils.toBean(contract, CrmContractRespVO.class))); |
| | | // 2.4 æ¼æ¥å票éä»¶ |
| | | invoiceVO.setAttachmentList(storageAttachmentApi.listAttachments("crm_invoice", invoiceVO.getId())); |
| | | invoiceVO.setAttachmentList(storageAttachmentApi.listAttachments( |
| | | StorageRecordTypeEnum.CRM_INVOICE.getType(), invoiceVO.getId())); |
| | | // 2.5 æ¼æ¥æ¯å¦å·²ä¸ä¼ éä»¶ |
| | | invoiceVO.setHasAttachment(CollUtil.isNotEmpty(invoiceVO.getAttachmentList())); |
| | | // 2.6 æ¼æ¥å©ä½å¯å款éé¢ |
| | | invoiceVO.setRemainingInvoicePrice(invoiceVO.getPrice().subtract( |
| | | receivablePriceMap.getOrDefault(invoiceVO.getId(), BigDecimal.ZERO))); |
| | | }); |
| | | } |
| | | |
| | |
| | | return success(invoiceService.getAuditInvoiceCount(getLoginUserId())); |
| | | } |
| | | |
| | | @GetMapping("/simple-list") |
| | | @Operation(summary = "è·å¾å¼ç¥¨ç²¾ç®å表", description = "åªå
å«å®¡æ ¸éè¿ä¸å·²ä¸ä¼ å票éä»¶çå¼ç¥¨ï¼ä¸»è¦ç¨äºå款å建æ¶éæ©å
³èå¼ç¥¨") |
| | | @Parameter(name = "contractId", description = "ååç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('crm:invoice:query')") |
| | | public CommonResult<List<CrmInvoiceRespVO>> getInvoiceSimpleList(@RequestParam("contractId") Long contractId) { |
| | | List<CrmInvoiceDO> invoices = invoiceService.getInvoiceListByContractIdAndStatus(contractId, |
| | | List.of(CrmAuditStatusEnum.APPROVE.getStatus())); |
| | | if (CollUtil.isEmpty(invoices)) { |
| | | return success(Collections.emptyList()); |
| | | } |
| | | // è¿æ»¤ï¼å·²ä¸ä¼ å票éä»¶ |
| | | invoices.removeIf(invoice -> CollUtil.isEmpty(storageAttachmentApi.listAttachments( |
| | | StorageRecordTypeEnum.CRM_INVOICE.getType(), invoice.getId()))); |
| | | if (CollUtil.isEmpty(invoices)) { |
| | | return success(Collections.emptyList()); |
| | | } |
| | | // è®¡ç®æ¯å¼ å¼ç¥¨å·²å款éé¢ï¼è稿 + 审æ¹ä¸ + 审æ¹éè¿ï¼å£å¾ä¸åå»ºåæ¬¾æ ¡éªä¸è´ï¼ |
| | | Map<Long, BigDecimal> receivablePriceMap = receivableService.getReceivablePriceMapByInvoiceId( |
| | | convertSet(invoices, CrmInvoiceDO::getId)); |
| | | // è¿æ»¤ï¼å©ä½å¯å款éé¢ > 0 æå¯é |
| | | List<CrmInvoiceRespVO> result = new ArrayList<>(); |
| | | for (CrmInvoiceDO invoice : invoices) { |
| | | BigDecimal remainingPrice = invoice.getPrice() |
| | | .subtract(receivablePriceMap.getOrDefault(invoice.getId(), BigDecimal.ZERO)); |
| | | if (remainingPrice.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | result.add(new CrmInvoiceRespVO() // åªè¿åç²¾ç®å段 |
| | | .setId(invoice.getId()).setNo(invoice.getNo()).setInvoiceNo(invoice.getInvoiceNo()) |
| | | .setInvoiceTitle(invoice.getInvoiceTitle()).setPrice(invoice.getPrice()) |
| | | .setCustomerId(invoice.getCustomerId()).setContractId(invoice.getContractId()) |
| | | .setAuditStatus(invoice.getAuditStatus()).setHasAttachment(true) |
| | | .setRemainingInvoicePrice(remainingPrice)); |
| | | } |
| | | return success(result); |
| | | } |
| | | |
| | | } |
| | |
| | | @Schema(description = "å票éä»¶å表") |
| | | private List<StorageBlobRespDTO> attachmentList; |
| | | |
| | | @Schema(description = "æ¯å¦å·²ä¸ä¼ å票éä»¶", example = "true") |
| | | private Boolean hasAttachment; |
| | | |
| | | @Schema(description = "å©ä½å¯å款éé¢ï¼çäº å¼ç¥¨éé¢ - 该å¼ç¥¨å·²å款éé¢ï¼è稿+审æ¹ä¸+审æ¹éè¿ï¼", example = "1000.00") |
| | | private BigDecimal remainingInvoicePrice; |
| | | |
| | | @Schema(description = "å建æ¶é´", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @ExcelProperty("å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceRespVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivablePageReqVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableRespVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.receivable.vo.receivable.CrmReceivableSaveReqVO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.invoice.CrmInvoiceDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.receivable.CrmReceivableDO; |
| | | import cn.iocoder.yudao.module.crm.service.contract.CrmContractService; |
| | | import cn.iocoder.yudao.module.crm.service.customer.CrmCustomerService; |
| | | import cn.iocoder.yudao.module.crm.service.invoice.CrmInvoiceService; |
| | | import cn.iocoder.yudao.module.crm.service.receivable.CrmReceivableService; |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| | |
| | | private CrmContractService contractService; |
| | | @Resource |
| | | private CrmCustomerService customerService; |
| | | @Resource |
| | | private CrmInvoiceService invoiceService; |
| | | |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | |
| | | // 1.3 è·å¾ååå表 |
| | | Map<Long, CrmContractDO> contractMap = contractService.getContractMap( |
| | | convertSet(receivableList, CrmReceivableDO::getContractId)); |
| | | // 1.4 è·å¾å
³èå¼ç¥¨å表 |
| | | Map<Long, CrmInvoiceDO> invoiceMap = invoiceService.getInvoiceMap( |
| | | convertSet(receivableList, CrmReceivableDO::getInvoiceId)); |
| | | // 2. æ¼æ¥ç»æ |
| | | return BeanUtils.toBean(receivableList, CrmReceivableRespVO.class, (receivableVO) -> { |
| | | // 2.1 æ¼æ¥å®¢æ·åç§° |
| | |
| | | // 2.3 æ¼æ¥ååä¿¡æ¯ |
| | | findAndThen(contractMap, receivableVO.getContractId(), contract -> |
| | | receivableVO.setContract(BeanUtils.toBean(contract, CrmContractRespVO.class))); |
| | | // 2.4 æ¼æ¥å
³èå¼ç¥¨ä¿¡æ¯ |
| | | findAndThen(invoiceMap, receivableVO.getInvoiceId(), invoice -> |
| | | receivableVO.setInvoice(BeanUtils.toBean(invoice, CrmInvoiceRespVO.class))); |
| | | }); |
| | | } |
| | | |
| | |
| | | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
| | | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.contract.vo.contract.CrmContractRespVO; |
| | | import cn.iocoder.yudao.module.crm.controller.admin.invoice.vo.invoice.CrmInvoiceRespVO; |
| | | import cn.iocoder.yudao.module.crm.enums.DictTypeConstants; |
| | | import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | |
| | | @Schema(description = "ååä¿¡æ¯") |
| | | private CrmContractRespVO contract; |
| | | |
| | | @Schema(description = "å
³èçå¼ç¥¨ç¼å·", example = "25787") |
| | | @ExcelProperty("å
³èå¼ç¥¨ç¼å·") |
| | | private Long invoiceId; |
| | | @Schema(description = "å
³èå¼ç¥¨ä¿¡æ¯") |
| | | private CrmInvoiceRespVO invoice; |
| | | |
| | | @Schema(description = "è´è´£äººçç¨æ·ç¼å·", example = "25682") |
| | | private Long ownerUserId; |
| | | @Schema(description = "è´è´£äººåå", example = "25682") |
| | |
| | | @NotNull(message = "ååç¼å·ä¸è½ä¸ºç©º") |
| | | private Long contractId; |
| | | |
| | | @Schema(description = "å
³èçå¼ç¥¨ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "25787") |
| | | @DiffLogField(name = "å
³èå¼ç¥¨", function = CrmInvoiceParseFunction.NAME) |
| | | @NotNull(message = "å
³èå¼ç¥¨ç¼å·ä¸è½ä¸ºç©º") |
| | | private Long invoiceId; |
| | | |
| | | @Schema(description = "忬¾è®¡åç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024") |
| | | @DiffLogField(name = "åå", function = CrmReceivablePlanParseFunction.NAME) |
| | | private Long planId; |
| | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.contract.CrmContractDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerDO; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.invoice.CrmInvoiceDO; |
| | | import cn.iocoder.yudao.module.crm.enums.common.CrmAuditStatusEnum; |
| | | import cn.iocoder.yudao.module.crm.enums.receivable.CrmReceivableReturnTypeEnum; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | |
| | | */ |
| | | private Long contractId; |
| | | /** |
| | | * å
³èçå¼ç¥¨ç¼å· |
| | | * |
| | | * å
³è {@link CrmInvoiceDO#getId()}ï¼åªæå®¡æ¹éè¿ä¸å·²ä¸ä¼ å票éä»¶çå¼ç¥¨æå¯å
³è |
| | | */ |
| | | private Long invoiceId; |
| | | /** |
| | | * è´è´£äººç¼å·ï¼å
³è {@link AdminUserRespDTO#getId()} |
| | | */ |
| | | private Long ownerUserId; |
| | |
| | | if (CollUtil.isEmpty(contractIds)) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | // SQL sum æ¥è¯¢ |
| | | // SQL sum æ¥è¯¢ï¼å®¡æ¹éè¿ + 审æ¹ä¸ï¼ä¸å¼ç¥¨é²è¶
æ ¡éªå£å¾ä¸è´ï¼ |
| | | List<Map<String, Object>> result = selectMaps(new QueryWrapper<CrmInvoiceDO>() |
| | | .select("contract_id, SUM(price) AS total_price") |
| | | .eq("audit_status", CrmAuditStatusEnum.APPROVE.getStatus()) |
| | | .in("audit_status", CrmAuditStatusEnum.APPROVE.getStatus(), |
| | | CrmAuditStatusEnum.PROCESS.getStatus()) |
| | | .groupBy("contract_id") |
| | | .in("contract_id", contractIds)); |
| | | // è·å¾éé¢ |
| | |
| | | .in(CrmReceivableDO::getAuditStatus, auditStatuses)); |
| | | } |
| | | |
| | | default List<CrmReceivableDO> selectListByInvoiceIdAndStatus(Long invoiceId, Collection<Integer> auditStatuses) { |
| | | return selectList(new LambdaQueryWrapperX<CrmReceivableDO>() |
| | | .eq(CrmReceivableDO::getInvoiceId, invoiceId) |
| | | .in(CrmReceivableDO::getAuditStatus, auditStatuses)); |
| | | } |
| | | |
| | | default Map<Long, BigDecimal> selectReceivablePriceMapByInvoiceId(Collection<Long> invoiceIds) { |
| | | if (CollUtil.isEmpty(invoiceIds)) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | // SQL sum æ¥è¯¢ |
| | | List<Map<String, Object>> result = selectMaps(new QueryWrapper<CrmReceivableDO>() |
| | | .select("invoice_id, SUM(price) AS total_price") |
| | | .in("audit_status", CrmAuditStatusEnum.DRAFT.getStatus(), // è稿 + 审æ¹ä¸ + 审æ¹éè¿ |
| | | CrmAuditStatusEnum.PROCESS.getStatus(), CrmAuditStatusEnum.APPROVE.getStatus()) |
| | | .groupBy("invoice_id") |
| | | .in("invoice_id", invoiceIds)); |
| | | // è·å¾éé¢ |
| | | return convertMap(result, obj -> (Long) obj.get("invoice_id"), obj -> (BigDecimal) obj.get("total_price")); |
| | | } |
| | | |
| | | default Map<Long, BigDecimal> selectReceivablePriceMapByContractId(Collection<Long> contractIds) { |
| | | if (CollUtil.isEmpty(contractIds)) { |
| | | return Collections.emptyMap(); |
| | |
| | | ErrorCode INVOICE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_016_005, "æ´æ°å¼ç¥¨å®¡æ ¸ç¶æå¤±è´¥ï¼åå ï¼å¼ç¥¨ä¸æ¯å®¡æ ¸ä¸ç¶æ"); |
| | | ErrorCode INVOICE_CREATE_FAIL_CONTRACT_NOT_APPROVE = new ErrorCode(1_020_016_006, "å建å¼ç¥¨å¤±è´¥ï¼åå ï¼åå䏿¯å®¡æ ¸éè¿ç¶æ"); |
| | | ErrorCode INVOICE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT = new ErrorCode(1_020_016_007, "å建å¼ç¥¨å¤±è´¥ï¼åå ï¼å¼ç¥¨éé¢è¶
åºååéé¢ï¼ç®åå¯å¼ç¥¨ï¼{} å
"); |
| | | ErrorCode RECEIVABLE_CREATE_FAIL_INVOICE_NOT_ENOUGH = new ErrorCode(1_020_016_008, "åå»ºåæ¬¾å¤±è´¥ï¼åå ï¼åå累计å¼ç¥¨éé¢ä¸è¶³ï¼ç®åå¯å款ï¼{} å
"); |
| | | ErrorCode RECEIVABLE_CREATE_FAIL_INVOICE_NOT_EXISTS = new ErrorCode(1_020_016_008, "åå»ºåæ¬¾å¤±è´¥ï¼åå ï¼å
³èçå¼ç¥¨ä¸åå¨"); |
| | | ErrorCode RECEIVABLE_CREATE_FAIL_INVOICE_NOT_APPROVE = new ErrorCode(1_020_016_009, "åå»ºåæ¬¾å¤±è´¥ï¼åå ï¼å
³èçå¼ç¥¨ä¸æ¯å®¡æ¹éè¿ç¶æ"); |
| | | ErrorCode RECEIVABLE_CREATE_FAIL_INVOICE_NOT_ATTACHMENT = new ErrorCode(1_020_016_010, "åå»ºåæ¬¾å¤±è´¥ï¼åå ï¼å
³èçå¼ç¥¨æªä¸ä¼ å票éä»¶"); |
| | | ErrorCode RECEIVABLE_CREATE_FAIL_INVOICE_CONTRACT_MISMATCH = new ErrorCode(1_020_016_011, "åå»ºåæ¬¾å¤±è´¥ï¼åå ï¼å
³èçå¼ç¥¨ä¸æéååä¸å¹é
"); |
| | | ErrorCode RECEIVABLE_CREATE_FAIL_INVOICE_PRICE_EXCEEDS = new ErrorCode(1_020_016_012, "åå»ºåæ¬¾å¤±è´¥ï¼åå ï¼å款éé¢è¶
åºè¯¥å¼ç¥¨å©ä½å¯å款éé¢ï¼ç®åå¯å款ï¼{} å
"); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.crm.framework.operatelog.core; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.module.crm.dal.dataobject.invoice.CrmInvoiceDO; |
| | | import cn.iocoder.yudao.module.crm.service.invoice.CrmInvoiceService; |
| | | import com.mzt.logapi.service.IParseFunction; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * CRM å¼ç¥¨ç {@link IParseFunction} å®ç°ç±» |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Component |
| | | @Slf4j |
| | | public class CrmInvoiceParseFunction implements IParseFunction { |
| | | |
| | | public static final String NAME = "getInvoiceById"; |
| | | |
| | | @Resource |
| | | private CrmInvoiceService invoiceService; |
| | | |
| | | @Override |
| | | public boolean executeBefore() { |
| | | return false; // ä¸å¨ _DIFF ä¸ä½¿ç¨ï¼æ éæåæ±å¼ |
| | | } |
| | | |
| | | @Override |
| | | public String functionName() { |
| | | return NAME; |
| | | } |
| | | |
| | | @Override |
| | | public String apply(Object value) { |
| | | if (StrUtil.isEmptyIfStr(value)) { |
| | | return ""; |
| | | } |
| | | CrmInvoiceDO invoice = invoiceService.getInvoice(Long.parseLong(value.toString())); |
| | | return invoice == null ? "" : invoice.getNo(); |
| | | } |
| | | |
| | | } |
| | |
| | | List<CrmInvoiceDO> getInvoiceListByContractIdAndStatus(Long contractId, Collection<Integer> auditStatuses); |
| | | |
| | | /** |
| | | * è·å¾ååå·²å¼ç¥¨éé¢ Mapï¼ä»
审æ¹éè¿ï¼ |
| | | * è·å¾ååå·²å¼ç¥¨éé¢ Mapï¼å®¡æ¹éè¿ + 审æ¹ä¸ï¼ä¸å¼ç¥¨é²è¶
æ ¡éªå£å¾ä¸è´ï¼ |
| | | * |
| | | * @param contractIds ååç¼å· |
| | | * @return å¼ç¥¨éé¢ Map |
| | |
| | | } |
| | | |
| | | private void validateInvoicePriceExceedsLimit(CrmInvoiceSaveReqVO reqVO) { |
| | | // 1. 计ç®å©ä½å¯å¼ç¥¨éé¢ï¼ä¸å
æ¬ reqVO èªèº« |
| | | // 1. 计ç®å©ä½å¯å¼ç¥¨éé¢ï¼ä¸å
æ¬ reqVO èªèº«ï¼å®¡æ¹ä¸ä¹å ç¨é¢åº¦ï¼é²æ¢ç´¯è®¡å¼ç¥¨è¶
è¿ååéé¢ï¼ |
| | | CrmContractDO contract = contractService.validateContract(reqVO.getContractId()); |
| | | List<CrmInvoiceDO> invoices = invoiceMapper.selectListByContractIdAndStatus(reqVO.getContractId(), |
| | | List.of(CrmAuditStatusEnum.APPROVE.getStatus())); |
| | | List.of(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus())); |
| | | if (reqVO.getId() != null) { |
| | | invoices.removeIf(invoice -> ObjectUtil.equal(invoice.getId(), reqVO.getId())); |
| | | } |
| | |
| | | throw exception(INVOICE_SUBMIT_FAIL_NOT_DRAFT); |
| | | } |
| | | |
| | | // 1.1 éæ°æ ¡éªå¯å¼ç¥¨éé¢ï¼é²æ¢ç´¯è®¡å¼ç¥¨è¶
è¿ååéé¢ï¼è稿æåå¯è½æå
¶ä»åè¿å
¥å®¡æ¹ä¸ï¼ |
| | | validateInvoicePriceExceedsLimit(new CrmInvoiceSaveReqVO() |
| | | .setId(invoice.getId()).setContractId(invoice.getContractId()).setPrice(invoice.getPrice())); |
| | | |
| | | // 2. å建å¼ç¥¨å®¡æ¹æµç¨å®ä¾ |
| | | String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id))); |
| | |
| | | Map<Long, BigDecimal> getReceivablePriceMapByContractId(Collection<Long> contractIds); |
| | | |
| | | /** |
| | | * è·å¾å¼ç¥¨å·²å款éé¢ Mapï¼è稿 + 审æ¹ä¸ + 审æ¹éè¿ï¼ |
| | | * |
| | | * @param invoiceIds å¼ç¥¨ç¼å· |
| | | * @return 已忬¾éé¢ Map |
| | | */ |
| | | Map<Long, BigDecimal> getReceivablePriceMapByInvoiceId(Collection<Long> invoiceIds); |
| | | |
| | | /** |
| | | * æ ¹æ®ååç¼å·æ¥è¯¢å款æ°é |
| | | * |
| | | * @param contractId ååç¼å· |
| | |
| | | @LogRecord(type = CRM_RECEIVABLE_TYPE, subType = CRM_RECEIVABLE_CREATE_SUB_TYPE, bizNo = "{{#receivable.id}}", |
| | | success = CRM_RECEIVABLE_CREATE_SUCCESS) |
| | | public Long createReceivable(CrmReceivableSaveReqVO createReqVO) { |
| | | // 1.1 æ ¡éªå¯å款éé¢è¶
è¿ä¸é |
| | | validateReceivablePriceExceedsLimit(createReqVO); |
| | | // 1.2 æ ¡éªå¯å款éé¢ä¸è¶
è¿ç´¯è®¡å·²å¼ç¥¨éé¢ï¼å
å¼ç¥¨å忬¾ï¼ |
| | | validateReceivablePriceExceedsInvoiceLimit(createReqVO); |
| | | // 1.3 æ ¡éªå
³èæ°æ®åå¨ |
| | | // 1.1 æ ¡éªå
³èæ°æ®åå¨ |
| | | validateRelationDataExists(createReqVO); |
| | | // 1.4 çæå款ç¼å· |
| | | // 1.2 æ ¡éªå
³èå¼ç¥¨åæ ¼ï¼å®¡æ¹éè¿ + å·²ä¸ä¼ å票éä»¶ + ä¸ååå¹é
ï¼ |
| | | validateInvoiceForReceivable(createReqVO); |
| | | // 1.3 æ ¡éªå¯å款éé¢ä¸è¶
è¿ååå©ä½å¯å款éé¢ |
| | | validateReceivablePriceExceedsLimit(createReqVO); |
| | | // 1.4 æ ¡éªå¯å款éé¢ä¸è¶
è¿è¯¥å¼ç¥¨å©ä½å¯å款éé¢ï¼å
å¼ç¥¨å忬¾ï¼ |
| | | validateReceivablePriceExceedsInvoiceRemaining(createReqVO); |
| | | // 1.5 çæå款ç¼å· |
| | | String no = noRedisDAO.generate(CrmNoRedisDAO.RECEIVABLE_PREFIX); |
| | | if (receivableMapper.selectByNo(no) != null) { |
| | | throw exception(RECEIVABLE_NO_EXISTS); |
| | |
| | | private void validateReceivablePriceExceedsLimit(CrmReceivableSaveReqVO reqVO) { |
| | | // 1. 计ç®å©ä½å¯é款éé¢ï¼ä¸å
æ¬ reqVO èªèº« |
| | | CrmContractDO contract = contractService.validateContract(reqVO.getContractId()); |
| | | // ç»è®¡å£å¾ï¼è稿 + 审æ¹ä¸ + 审æ¹éè¿ï¼ä¸å¼ç¥¨çº§æ ¡éªãç»è®¡æ¹æ³ selectReceivablePriceMapByContractId ä¿æä¸è´ï¼ |
| | | List<CrmReceivableDO> receivables = receivableMapper.selectListByContractIdAndStatus(reqVO.getContractId(), |
| | | Arrays.asList(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus())); |
| | | Arrays.asList(CrmAuditStatusEnum.DRAFT.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus(), |
| | | CrmAuditStatusEnum.APPROVE.getStatus())); |
| | | if (reqVO.getId() != null) { |
| | | receivables.removeIf(receivable -> ObjectUtil.equal(receivable.getId(), reqVO.getId())); |
| | | } |
| | |
| | | } |
| | | |
| | | /** |
| | | * æ ¡éªç´¯è®¡å款éé¢ä¸è¶
è¿ç´¯è®¡å·²å¼ç¥¨ä¸å·²ä¸ä¼ å票éä»¶çéé¢ï¼å
å¼ç¥¨å忬¾ï¼ |
| | | * æ ¡éªå
³èå¼ç¥¨åæ ¼ï¼åå¨ã审æ¹éè¿ãå·²ä¸ä¼ å票éä»¶ã䏿éååä¸è´ |
| | | */ |
| | | private void validateReceivablePriceExceedsInvoiceLimit(CrmReceivableSaveReqVO reqVO) { |
| | | // 1. 计ç®ååç´¯è®¡åæ¬¾éé¢ï¼ä¸å«æ¬æ¬¡ï¼ç»è®¡å£å¾åååé颿 ¡éªï¼å®¡æ¹éè¿ + 审æ¹ä¸ï¼ |
| | | List<CrmReceivableDO> receivables = receivableMapper.selectListByContractIdAndStatus(reqVO.getContractId(), |
| | | Arrays.asList(CrmAuditStatusEnum.APPROVE.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus())); |
| | | private void validateInvoiceForReceivable(CrmReceivableSaveReqVO reqVO) { |
| | | // 1. æ ¡éªå¼ç¥¨åå¨ |
| | | CrmInvoiceDO invoice = invoiceService.getInvoice(reqVO.getInvoiceId()); |
| | | if (invoice == null) { |
| | | throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_EXISTS); |
| | | } |
| | | // 2. æ ¡éªå¼ç¥¨å®¡æ¹éè¿ |
| | | if (ObjectUtil.notEqual(invoice.getAuditStatus(), CrmAuditStatusEnum.APPROVE.getStatus())) { |
| | | throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_APPROVE); |
| | | } |
| | | // 3. æ ¡éªå¼ç¥¨å·²ä¸ä¼ å票éä»¶ |
| | | if (CollUtil.isEmpty(storageAttachmentApi.listAttachments( |
| | | StorageRecordTypeEnum.CRM_INVOICE.getType(), invoice.getId()))) { |
| | | throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_ATTACHMENT); |
| | | } |
| | | // 4. æ ¡éªå¼ç¥¨ä¸æéååä¸è´ |
| | | if (ObjectUtil.notEqual(invoice.getContractId(), reqVO.getContractId())) { |
| | | throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_CONTRACT_MISMATCH); |
| | | } |
| | | // 5. æ ¡éªå¼ç¥¨ä¸æéåå客æ·ä¸è´ï¼é²å¾¡èæ°æ®ï¼åå-å¼ç¥¨-忬¾ä¸å¯¹å¤å¯¹å¤ï¼å¼ç¥¨å¿
é¡»å±äºè¯¥åå客æ·ï¼ |
| | | if (reqVO.getCustomerId() != null && ObjectUtil.notEqual(invoice.getCustomerId(), reqVO.getCustomerId())) { |
| | | throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_CONTRACT_MISMATCH); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ ¡éªç´¯è®¡å款éé¢ä¸è¶
è¿è¯¥å¼ç¥¨å©ä½å¯å款éé¢ï¼å
å¼ç¥¨å忬¾ï¼ |
| | | */ |
| | | private void validateReceivablePriceExceedsInvoiceRemaining(CrmReceivableSaveReqVO reqVO) { |
| | | // 1. 计ç®è¯¥å¼ç¥¨ç´¯è®¡å款éé¢ï¼ä¸å«æ¬æ¬¡ï¼ç»è®¡å£å¾åååé颿 ¡éªï¼è稿 + 审æ¹ä¸ + 审æ¹éè¿ï¼ |
| | | CrmInvoiceDO invoice = invoiceService.getInvoice(reqVO.getInvoiceId()); |
| | | List<CrmReceivableDO> receivables = receivableMapper.selectListByInvoiceIdAndStatus(reqVO.getInvoiceId(), |
| | | Arrays.asList(CrmAuditStatusEnum.DRAFT.getStatus(), CrmAuditStatusEnum.PROCESS.getStatus(), |
| | | CrmAuditStatusEnum.APPROVE.getStatus())); |
| | | if (reqVO.getId() != null) { |
| | | receivables.removeIf(receivable -> ObjectUtil.equal(receivable.getId(), reqVO.getId())); |
| | | } |
| | | BigDecimal receivablePriceSum = CollectionUtils.getSumValue(receivables, CrmReceivableDO::getPrice, |
| | | BigDecimal::add, BigDecimal.ZERO); |
| | | |
| | | // 2. 计ç®ååä¸ã审æ¹éè¿ä¸å·²ä¸ä¼ å票éä»¶ãç累计å¼ç¥¨éé¢ |
| | | List<CrmInvoiceDO> invoices = invoiceService.getInvoiceListByContractIdAndStatus(reqVO.getContractId(), |
| | | List.of(CrmAuditStatusEnum.APPROVE.getStatus())); |
| | | BigDecimal invoicePriceSum = BigDecimal.ZERO; |
| | | for (CrmInvoiceDO invoice : invoices) { |
| | | if (CollUtil.isNotEmpty(storageAttachmentApi.listAttachments( |
| | | StorageRecordTypeEnum.CRM_INVOICE.getType(), invoice.getId()))) { |
| | | invoicePriceSum = invoicePriceSum.add(invoice.getPrice()); |
| | | } |
| | | } |
| | | |
| | | // 3. æ ¡éªæ¬æ¬¡å款éé¢ä¸è¶
è¿å©ä½å¯å款éé¢ï¼å·²å¼ç¥¨éé¢ - 已忬¾éé¢ï¼ |
| | | BigDecimal availablePrice = invoicePriceSum.subtract(receivablePriceSum); |
| | | // 2. æ ¡éªæ¬æ¬¡å款éé¢ä¸è¶
è¿è¯¥å¼ç¥¨å©ä½å¯å款éé¢ï¼å¼ç¥¨éé¢ - 已忬¾éé¢ï¼ |
| | | BigDecimal availablePrice = invoice.getPrice().subtract(receivablePriceSum); |
| | | if (reqVO.getPrice().compareTo(availablePrice) > 0) { |
| | | throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_NOT_ENOUGH, availablePrice); |
| | | throw exception(RECEIVABLE_CREATE_FAIL_INVOICE_PRICE_EXCEEDS, availablePrice); |
| | | } |
| | | } |
| | | |
| | |
| | | @CrmPermission(bizType = CrmBizTypeEnum.CRM_RECEIVABLE, bizId = "#updateReqVO.id", level = CrmPermissionLevelEnum.WRITE) |
| | | public void updateReceivable(CrmReceivableSaveReqVO updateReqVO) { |
| | | Assert.notNull(updateReqVO.getId(), "忬¾ç¼å·ä¸è½ä¸ºç©º"); |
| | | updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // ä¸å
许修æ¹çåæ®µ |
| | | updateReqVO.setOwnerUserId(null).setCustomerId(null).setContractId(null).setPlanId(null); // ä¸å
许修æ¹çåæ®µï¼invoiceId å
许修æ¹ï¼ |
| | | // 1.1 æ ¡éªåå¨ |
| | | CrmReceivableDO oldReceivable = validateReceivableExists(updateReqVO.getId()); |
| | | updateReqVO.setOwnerUserId(oldReceivable.getOwnerUserId()).setCustomerId(oldReceivable.getCustomerId()) |
| | | .setContractId(oldReceivable.getContractId()).setPlanId(oldReceivable.getPlanId()); // 设置已åå¨çå¼ |
| | | // 1.2 æ ¡éªå¯å款éé¢è¶
è¿ä¸é |
| | | // 1.2 è¥æªä¼ éå
³èå¼ç¥¨ï¼æ²¿ç¨æ§å¼ |
| | | if (updateReqVO.getInvoiceId() == null) { |
| | | updateReqVO.setInvoiceId(oldReceivable.getInvoiceId()); |
| | | } |
| | | // 1.3 æ ¡éªå¯å款éé¢ä¸è¶
è¿ååå©ä½å¯å款éé¢ |
| | | validateReceivablePriceExceedsLimit(updateReqVO); |
| | | // 1.3 æ ¡éªå¯å款éé¢ä¸è¶
è¿ç´¯è®¡å·²å¼ç¥¨éé¢ï¼å
å¼ç¥¨å忬¾ï¼ |
| | | validateReceivablePriceExceedsInvoiceLimit(updateReqVO); |
| | | // 1.4 æ ¡éªå
³èå¼ç¥¨åæ ¼ |
| | | validateInvoiceForReceivable(updateReqVO); |
| | | // 1.5 æ ¡éªå¯å款éé¢ä¸è¶
è¿è¯¥å¼ç¥¨å©ä½å¯å款éé¢ |
| | | validateReceivablePriceExceedsInvoiceRemaining(updateReqVO); |
| | | |
| | | // 1.4 åªæè稿ã审æ¹ä¸ï¼å¯ä»¥ç¼è¾ï¼ |
| | | // 1.6 åªæè稿ã审æ¹ä¸ï¼å¯ä»¥ç¼è¾ï¼ |
| | | if (!ObjectUtils.equalsAny(oldReceivable.getAuditStatus(), CrmAuditStatusEnum.DRAFT.getStatus(), |
| | | CrmAuditStatusEnum.PROCESS.getStatus())) { |
| | | throw exception(RECEIVABLE_UPDATE_FAIL_EDITING_PROHIBITED); |
| | |
| | | } |
| | | |
| | | @Override |
| | | public Map<Long, BigDecimal> getReceivablePriceMapByInvoiceId(Collection<Long> invoiceIds) { |
| | | return receivableMapper.selectReceivablePriceMapByInvoiceId(invoiceIds); |
| | | } |
| | | |
| | | @Override |
| | | public Long getReceivableCountByContractId(Long contractId) { |
| | | return receivableMapper.selectCountByContractId(contractId); |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentRespVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoiceRespVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpAccountDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO; |
| | | import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; |
| | | import cn.iocoder.yudao.module.erp.service.finance.ErpFinancePaymentService; |
| | | import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInvoiceService; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | |
| | | private SrmSupplierApi srmSupplierApi; |
| | | @Resource |
| | | private ErpAccountService accountService; |
| | | @Resource |
| | | private ErpPurchaseInvoiceService invoiceService; |
| | | |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | |
| | | return success(null); |
| | | } |
| | | List<ErpFinancePaymentItemDO> paymentItemList = financePaymentService.getFinancePaymentItemListByPaymentId(id); |
| | | return success(BeanUtils.toBean(payment, ErpFinancePaymentRespVO.class, financePaymentVO -> |
| | | financePaymentVO.setItems(BeanUtils.toBean(paymentItemList, ErpFinancePaymentRespVO.Item.class)) |
| | | .setAttachmentList(storageAttachmentApi.listAttachments("erp_finance_payment", id)))); |
| | | return success(buildFinancePaymentVO(payment, paymentItemList)); |
| | | } |
| | | |
| | | private ErpFinancePaymentRespVO buildFinancePaymentVO(ErpFinancePaymentDO payment, |
| | | List<ErpFinancePaymentItemDO> paymentItemList) { |
| | | ErpPurchaseInvoiceRespVO invoiceVO = null; |
| | | if (payment.getInvoiceId() != null) { |
| | | ErpPurchaseInvoiceDO invoice = invoiceService.getPurchaseInvoice(payment.getInvoiceId()); |
| | | if (invoice != null) { |
| | | invoiceVO = BeanUtils.toBean(invoice, ErpPurchaseInvoiceRespVO.class); |
| | | } |
| | | } |
| | | ErpPurchaseInvoiceRespVO invoiceFinal = invoiceVO; |
| | | return BeanUtils.toBean(payment, ErpFinancePaymentRespVO.class, financePaymentVO -> |
| | | financePaymentVO.setInvoice(invoiceFinal) |
| | | .setItems(BeanUtils.toBean(paymentItemList, ErpFinancePaymentRespVO.Item.class)) |
| | | .setAttachmentList(storageAttachmentApi.listAttachments("erp_finance_payment", payment.getId()))); |
| | | } |
| | | |
| | | @GetMapping("/page") |
| | |
| | | // 1.4 管çåä¿¡æ¯ |
| | | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertListByFlatMap(pageResult.getList(), |
| | | contact -> Stream.of(NumberUtils.parseLong(contact.getCreator()), contact.getFinanceUserId()))); |
| | | // 1.5 æ¥ç¥¨ä¿¡æ¯ |
| | | Map<Long, ErpPurchaseInvoiceDO> invoiceMap = invoiceService.getPurchaseInvoiceMap( |
| | | convertSet(pageResult.getList(), ErpFinancePaymentDO::getInvoiceId)); |
| | | // 2. å¼å§æ¼æ¥ |
| | | return BeanUtils.toBean(pageResult, ErpFinancePaymentRespVO.class, payment -> { |
| | | payment.setItems(BeanUtils.toBean(financePaymentItemMap.get(payment.getId()), ErpFinancePaymentRespVO.Item.class)); |
| | |
| | | MapUtils.findAndThen(accountMap, payment.getAccountId(), account -> payment.setAccountName(account.getName())); |
| | | MapUtils.findAndThen(userMap, Long.parseLong(payment.getCreator()), user -> payment.setCreatorName(user.getNickname())); |
| | | MapUtils.findAndThen(userMap, payment.getFinanceUserId(), user -> payment.setFinanceUserName(user.getNickname())); |
| | | MapUtils.findAndThen(invoiceMap, payment.getInvoiceId(), invoice -> |
| | | payment.setInvoice(BeanUtils.toBean(invoice, ErpPurchaseInvoiceRespVO.class))); |
| | | }); |
| | | } |
| | | |
| | |
| | | |
| | | import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoiceRespVO; |
| | | import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotNull; |
| | |
| | | @Schema(description = "ä¾åºååç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "å°çªèå
¬å¸") |
| | | private String supplierName; |
| | | |
| | | @Schema(description = "å
³èçæ¥ç¥¨ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "25787") |
| | | private Long invoiceId; |
| | | @Schema(description = "å
³èçæ¥ç¥¨ä¿¡æ¯") |
| | | private ErpPurchaseInvoiceRespVO invoice; |
| | | |
| | | @Schema(description = "仿¬¾è´¦æ·ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "28989") |
| | | private Long accountId; |
| | | @Schema(description = "仿¬¾è´¦æ·åç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "å¼ ä¸") |
| | |
| | | @NotNull(message = "ä¾åºåç¼å·ä¸è½ä¸ºç©º") |
| | | private Long supplierId; |
| | | |
| | | @Schema(description = "å
³èçæ¥ç¥¨ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "25787") |
| | | @NotNull(message = "å
³èæ¥ç¥¨ç¼å·ä¸è½ä¸ºç©º") |
| | | private Long invoiceId; |
| | | |
| | | @Schema(description = "仿¬¾è´¦æ·ç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "28989") |
| | | @NotNull(message = "仿¬¾è´¦æ·ç¼å·ä¸è½ä¸ºç©º") |
| | | private Long accountId; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.controller.admin.purchase; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.collection.MapUtils; |
| | | import cn.iocoder.yudao.framework.common.util.number.NumberUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoicePageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoiceRespVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoiceSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderRespVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; |
| | | import cn.iocoder.yudao.module.erp.enums.ErpPurchaseInvoiceAuditStatusEnum; |
| | | import cn.iocoder.yudao.module.erp.service.finance.ErpFinancePaymentService; |
| | | import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInvoiceService; |
| | | import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInvoiceServiceImpl; |
| | | import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseOrderService; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.servlet.http.HttpServletResponse; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | import static cn.iocoder.yudao.framework.common.pojo.PageParam.PAGE_SIZE_NONE; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen; |
| | | import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId; |
| | | |
| | | @Tag(name = "管çåå° - ERP éè´æ¥ç¥¨") |
| | | @RestController |
| | | @RequestMapping("/erp/purchase-invoice") |
| | | @Validated |
| | | public class ErpPurchaseInvoiceController { |
| | | |
| | | @Resource |
| | | private ErpPurchaseInvoiceService invoiceService; |
| | | @Resource |
| | | private ErpPurchaseOrderService purchaseOrderService; |
| | | @Resource |
| | | private ErpFinancePaymentService financePaymentService; |
| | | @Resource |
| | | private SrmSupplierApi srmSupplierApi; |
| | | |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | | @Resource |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建æ¥ç¥¨") |
| | | @PreAuthorize("@ss.hasPermission('erp:purchase-invoice:create')") |
| | | public CommonResult<Long> createPurchaseInvoice(@Valid @RequestBody ErpPurchaseInvoiceSaveReqVO createReqVO) { |
| | | return success(invoiceService.createPurchaseInvoice(createReqVO)); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "æ´æ°æ¥ç¥¨") |
| | | @PreAuthorize("@ss.hasPermission('erp:purchase-invoice:update')") |
| | | public CommonResult<Boolean> updatePurchaseInvoice(@Valid @RequestBody ErpPurchaseInvoiceSaveReqVO updateReqVO) { |
| | | invoiceService.updatePurchaseInvoice(updateReqVO); |
| | | return success(true); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Operation(summary = "å 餿¥ç¥¨") |
| | | @Parameter(name = "id", description = "ç¼å·", required = true) |
| | | @PreAuthorize("@ss.hasPermission('erp:purchase-invoice:delete')") |
| | | public CommonResult<Boolean> deletePurchaseInvoice(@RequestParam("id") Long id) { |
| | | invoiceService.deletePurchaseInvoice(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/get") |
| | | @Operation(summary = "è·å¾æ¥ç¥¨") |
| | | @Parameter(name = "id", description = "ç¼å·", required = true, example = "1024") |
| | | @PreAuthorize("@ss.hasPermission('erp:purchase-invoice:query')") |
| | | public CommonResult<ErpPurchaseInvoiceRespVO> getPurchaseInvoice(@RequestParam("id") Long id) { |
| | | ErpPurchaseInvoiceDO invoice = invoiceService.getPurchaseInvoice(id); |
| | | return success(buildInvoiceDetail(invoice)); |
| | | } |
| | | |
| | | private ErpPurchaseInvoiceRespVO buildInvoiceDetail(ErpPurchaseInvoiceDO invoice) { |
| | | if (invoice == null) { |
| | | return null; |
| | | } |
| | | return buildInvoiceDetailList(Collections.singletonList(invoice)).get(0); |
| | | } |
| | | |
| | | @GetMapping("/page") |
| | | @Operation(summary = "è·å¾æ¥ç¥¨å页") |
| | | @PreAuthorize("@ss.hasPermission('erp:purchase-invoice:query')") |
| | | public CommonResult<PageResult<ErpPurchaseInvoiceRespVO>> getPurchaseInvoicePage(@Valid ErpPurchaseInvoicePageReqVO pageReqVO) { |
| | | PageResult<ErpPurchaseInvoiceDO> pageResult = invoiceService.getPurchaseInvoicePage(pageReqVO); |
| | | return success(new PageResult<>(buildInvoiceDetailList(pageResult.getList()), pageResult.getTotal())); |
| | | } |
| | | |
| | | @GetMapping("/export-excel") |
| | | @Operation(summary = "å¯¼åºæ¥ç¥¨ Excel") |
| | | @PreAuthorize("@ss.hasPermission('erp:purchase-invoice:export')") |
| | | @ApiAccessLog(operateType = EXPORT) |
| | | public void exportPurchaseInvoiceExcel(@Valid ErpPurchaseInvoicePageReqVO pageReqVO, |
| | | HttpServletResponse response) throws IOException { |
| | | pageReqVO.setPageSize(PAGE_SIZE_NONE); |
| | | List<ErpPurchaseInvoiceDO> list = invoiceService.getPurchaseInvoicePage(pageReqVO).getList(); |
| | | // å¯¼åº Excel |
| | | ExcelUtils.write(response, "æ¥ç¥¨.xls", "æ°æ®", ErpPurchaseInvoiceRespVO.class, |
| | | buildInvoiceDetailList(list)); |
| | | } |
| | | |
| | | private List<ErpPurchaseInvoiceRespVO> buildInvoiceDetailList(List<ErpPurchaseInvoiceDO> invoiceList) { |
| | | if (CollUtil.isEmpty(invoiceList)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | // 1.1 è·åä¾åºåå表 |
| | | Map<Long, SrmSupplierRespDTO> supplierMap = srmSupplierApi.getSupplierList( |
| | | convertSet(invoiceList, ErpPurchaseInvoiceDO::getSupplierId)).getCheckedData().stream() |
| | | .collect(java.util.stream.Collectors.toMap(SrmSupplierRespDTO::getId, v -> v, (a, b) -> a)); |
| | | // 1.2 è·åéè´è®¢åå表 |
| | | Map<Long, ErpPurchaseOrderDO> orderMap = convertMap( |
| | | purchaseOrderService.getPurchaseOrderList(convertList(invoiceList, ErpPurchaseInvoiceDO::getPurchaseOrderId)), |
| | | ErpPurchaseOrderDO::getId); |
| | | // 1.3 è·åå建人å表 |
| | | Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap( |
| | | convertSet(invoiceList, invoice -> NumberUtils.parseLong(invoice.getCreator()))); |
| | | // 1.4 è·å¾æ¯å¼ æ¥ç¥¨å·²ä»æ¬¾éé¢ï¼æªå®¡æ ¸ + 审æ¹ä¸ + å·²å®¡æ ¸ï¼ |
| | | Map<Long, BigDecimal> paymentPriceMap = financePaymentService.getPaymentPriceMapByInvoiceId( |
| | | convertSet(invoiceList, ErpPurchaseInvoiceDO::getId)); |
| | | // 2. æ¼æ¥ç»æ |
| | | return BeanUtils.toBean(invoiceList, ErpPurchaseInvoiceRespVO.class, (invoiceVO) -> { |
| | | // 2.1 æ¼æ¥ä¾åºååç§° |
| | | findAndThen(supplierMap, invoiceVO.getSupplierId(), supplier -> invoiceVO.setSupplierName(supplier.getName())); |
| | | // 2.2 æ¼æ¥éè´è®¢åä¿¡æ¯ |
| | | findAndThen(orderMap, invoiceVO.getPurchaseOrderId(), order -> |
| | | invoiceVO.setOrder(BeanUtils.toBean(order, ErpPurchaseOrderRespVO.class))); |
| | | // 2.3 æ¼æ¥å建人åç§° |
| | | MapUtils.findAndThen(userMap, NumberUtils.parseLong(invoiceVO.getCreator()), |
| | | user -> invoiceVO.setCreatorName(user.getNickname())); |
| | | // 2.4 æ¼æ¥å票éä»¶ + æ¯å¦å·²ä¸ä¼ éä»¶ |
| | | invoiceVO.setAttachmentList(storageAttachmentApi.listAttachments( |
| | | ErpPurchaseInvoiceServiceImpl.PURCHASE_INVOICE_RECORD_TYPE, invoiceVO.getId())); |
| | | invoiceVO.setHasAttachment(CollUtil.isNotEmpty(invoiceVO.getAttachmentList())); |
| | | // 2.5 æ¼æ¥å©ä½å¯ä»éé¢ |
| | | invoiceVO.setRemainingInvoicePrice(invoiceVO.getPrice().subtract( |
| | | paymentPriceMap.getOrDefault(invoiceVO.getId(), BigDecimal.ZERO))); |
| | | }); |
| | | } |
| | | |
| | | @PutMapping("/submit") |
| | | @Operation(summary = "æäº¤æ¥ç¥¨å®¡æ¹") |
| | | @PreAuthorize("@ss.hasPermission('erp:purchase-invoice:update')") |
| | | public CommonResult<Boolean> submitPurchaseInvoice(@RequestParam("id") Long id) { |
| | | invoiceService.submitPurchaseInvoice(id, getLoginUserId()); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/audit-count") |
| | | @Operation(summary = "è·å¾å¾
å®¡æ ¸æ¥ç¥¨æ°é") |
| | | @PreAuthorize("@ss.hasPermission('erp:purchase-invoice:query')") |
| | | public CommonResult<Long> getAuditInvoiceCount() { |
| | | return success(invoiceService.getAuditInvoiceCount()); |
| | | } |
| | | |
| | | @GetMapping("/simple-list") |
| | | @Operation(summary = "è·å¾æ¥ç¥¨ç²¾ç®å表", description = "åªå
å«å®¡æ ¸éè¿ä¸å·²ä¸ä¼ å票éä»¶çæ¥ç¥¨ï¼ä¸»è¦ç¨äºä»æ¬¾å建æ¶éæ©å
³èæ¥ç¥¨") |
| | | @Parameter(name = "supplierId", description = "ä¾åºåç¼å·", required = true) |
| | | @Parameter(name = "purchaseOrderId", description = "éè´è®¢åç¼å·ï¼å¯éï¼ç¨äºæè®¢åè¿æ»¤ï¼") |
| | | @PreAuthorize("@ss.hasPermission('erp:purchase-invoice:query')") |
| | | public CommonResult<List<ErpPurchaseInvoiceRespVO>> getInvoiceSimpleList( |
| | | @RequestParam("supplierId") Long supplierId, |
| | | @RequestParam(value = "purchaseOrderId", required = false) Long purchaseOrderId) { |
| | | List<ErpPurchaseInvoiceDO> invoices; |
| | | if (purchaseOrderId != null) { |
| | | invoices = invoiceService.getPurchaseInvoiceListByOrderIdAndStatus(purchaseOrderId, |
| | | List.of(ErpPurchaseInvoiceAuditStatusEnum.APPROVE.getStatus())); |
| | | } else { |
| | | invoices = invoiceService.getPurchaseInvoiceListBySupplierIdAndStatus(supplierId, |
| | | List.of(ErpPurchaseInvoiceAuditStatusEnum.APPROVE.getStatus())); |
| | | } |
| | | if (CollUtil.isEmpty(invoices)) { |
| | | return success(Collections.emptyList()); |
| | | } |
| | | // è¿æ»¤ï¼å·²ä¸ä¼ å票éä»¶ |
| | | invoices.removeIf(invoice -> CollUtil.isEmpty(storageAttachmentApi.listAttachments( |
| | | ErpPurchaseInvoiceServiceImpl.PURCHASE_INVOICE_RECORD_TYPE, invoice.getId()))); |
| | | if (CollUtil.isEmpty(invoices)) { |
| | | return success(Collections.emptyList()); |
| | | } |
| | | // è®¡ç®æ¯å¼ æ¥ç¥¨å·²ä»æ¬¾éé¢ï¼æªå®¡æ ¸ + 审æ¹ä¸ + å·²å®¡æ ¸ï¼å£å¾ä¸åå»ºä»æ¬¾æ ¡éªä¸è´ï¼ |
| | | Map<Long, BigDecimal> paymentPriceMap = financePaymentService.getPaymentPriceMapByInvoiceId( |
| | | convertSet(invoices, ErpPurchaseInvoiceDO::getId)); |
| | | // è¿æ»¤ï¼å©ä½å¯ä»éé¢ > 0 æå¯é |
| | | List<ErpPurchaseInvoiceRespVO> result = new ArrayList<>(); |
| | | for (ErpPurchaseInvoiceDO invoice : invoices) { |
| | | BigDecimal remainingPrice = invoice.getPrice() |
| | | .subtract(paymentPriceMap.getOrDefault(invoice.getId(), BigDecimal.ZERO)); |
| | | if (remainingPrice.compareTo(BigDecimal.ZERO) <= 0) { |
| | | continue; |
| | | } |
| | | result.add(new ErpPurchaseInvoiceRespVO() // åªè¿åç²¾ç®å段 |
| | | .setId(invoice.getId()).setNo(invoice.getNo()).setInvoiceNo(invoice.getInvoiceNo()) |
| | | .setInvoiceTitle(invoice.getInvoiceTitle()).setPrice(invoice.getPrice()) |
| | | .setSupplierId(invoice.getSupplierId()).setPurchaseOrderId(invoice.getPurchaseOrderId()) |
| | | .setAuditStatus(invoice.getAuditStatus()).setHasAttachment(true) |
| | | .setRemainingInvoicePrice(remainingPrice)); |
| | | } |
| | | return success(result); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import cn.iocoder.yudao.framework.common.validation.InEnum; |
| | | import cn.iocoder.yudao.module.erp.enums.ErpPurchaseInvoiceAuditStatusEnum; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | import lombok.ToString; |
| | | |
| | | @Schema(description = "管çåå° - ERP éè´æ¥ç¥¨å页 Request VO") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | public class ErpPurchaseInvoicePageReqVO extends PageParam { |
| | | |
| | | @Schema(description = "æ¥ç¥¨ç¼å·") |
| | | private String no; |
| | | |
| | | @Schema(description = "ä¾åºåç¼å·", example = "29399") |
| | | private Long supplierId; |
| | | |
| | | @Schema(description = "éè´è®¢åç¼å·", example = "17386") |
| | | private Long purchaseOrderId; |
| | | |
| | | @Schema(description = "审æ¹ç¶æ", example = "20") |
| | | @InEnum(ErpPurchaseInvoiceAuditStatusEnum.class) |
| | | private Integer auditStatus; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice; |
| | | |
| | | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
| | | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderRespVO; |
| | | import cn.iocoder.yudao.module.erp.enums.DictTypeConstants; |
| | | import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO; |
| | | import cn.idev.excel.annotation.ExcelIgnoreUnannotated; |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - ERP éè´æ¥ç¥¨ Response VO") |
| | | @Data |
| | | @ExcelIgnoreUnannotated |
| | | public class ErpPurchaseInvoiceRespVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "25787") |
| | | @ExcelProperty("ç¼å·") |
| | | private Long id; |
| | | |
| | | @Schema(description = "æ¥ç¥¨ç¼å·", example = "FPD888") |
| | | @ExcelProperty("æ¥ç¥¨ç¼å·") |
| | | private String no; |
| | | |
| | | @Schema(description = "ä¾åºåç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "29399") |
| | | private Long supplierId; |
| | | @Schema(description = "ä¾åºååç§°", requiredMode = Schema.RequiredMode.REQUIRED, example = "å°çªèå
¬å¸") |
| | | @ExcelProperty("ä¾åºååç§°") |
| | | private String supplierName; |
| | | |
| | | @Schema(description = "éè´è®¢åç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") |
| | | @ExcelProperty("éè´è®¢åç¼å·") |
| | | private Long purchaseOrderId; |
| | | @Schema(description = "éè´è®¢åä¿¡æ¯") |
| | | private ErpPurchaseOrderRespVO order; |
| | | |
| | | @Schema(description = "å票å·ç ", example = "02564178") |
| | | @ExcelProperty("å票å·ç ") |
| | | private String invoiceNo; |
| | | |
| | | @Schema(description = "å票æ¬å¤´", example = "XXç§ææéå
¬å¸") |
| | | @ExcelProperty("å票æ¬å¤´") |
| | | private String invoiceTitle; |
| | | |
| | | @Schema(description = "æ¥ç¥¨éé¢ï¼åä½ï¼å
", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000") |
| | | @ExcelProperty("æ¥ç¥¨éé¢") |
| | | private BigDecimal price; |
| | | |
| | | @Schema(description = "æ¥ç¥¨æ¥æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02") |
| | | @ExcelProperty("æ¥ç¥¨æ¥æ") |
| | | private LocalDateTime invoiceTime; |
| | | |
| | | @Schema(description = "审æ¹ç¶æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "0") |
| | | @ExcelProperty(value = "审æ¹ç¶æ", converter = DictConvert.class) |
| | | @DictFormat(DictTypeConstants.AUDIT_STATUS) |
| | | private Integer auditStatus; |
| | | |
| | | @Schema(description = "工使µç¼å·", example = "1043") |
| | | @ExcelProperty("工使µç¼å·") |
| | | private String processInstanceId; |
| | | |
| | | @Schema(description = "夿³¨", example = "夿³¨") |
| | | @ExcelProperty("夿³¨") |
| | | private String remark; |
| | | |
| | | @Schema(description = "å票éä»¶å表") |
| | | private List<StorageBlobRespDTO> attachmentList; |
| | | |
| | | @Schema(description = "æ¯å¦å·²ä¸ä¼ å票éä»¶", example = "true") |
| | | private Boolean hasAttachment; |
| | | |
| | | @Schema(description = "å©ä½å¯ä»éé¢ï¼çäº æ¥ç¥¨éé¢ - 该æ¥ç¥¨å·²ä»æ¬¾éé¢ï¼æªå®¡æ ¸+审æ¹ä¸+å·²å®¡æ ¸ï¼", example = "1000.00") |
| | | private BigDecimal remainingInvoicePrice; |
| | | |
| | | @Schema(description = "å建人", example = "25682") |
| | | private String creator; |
| | | @Schema(description = "å建人åç§°", example = "test") |
| | | @ExcelProperty("å建人åç§°") |
| | | private String creatorName; |
| | | |
| | | @Schema(description = "å建æ¶é´", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @ExcelProperty("å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | @Schema(description = "æ´æ°æ¶é´", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @ExcelProperty("æ´æ°æ¶é´") |
| | | private LocalDateTime updateTime; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - ERP éè´æ¥ç¥¨æ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class ErpPurchaseInvoiceSaveReqVO { |
| | | |
| | | @Schema(description = "ç¼å·", example = "25787") |
| | | private Long id; |
| | | |
| | | @Schema(description = "ä¾åºåç¼å·", example = "29399") |
| | | private Long supplierId; // è¯¥åæ®µä¸éè¿åç«¯ä¼ éï¼èæ¯ purchaseOrderId æ¥è¯¢åºæ¥è®¾ç½®è¿å» |
| | | |
| | | @Schema(description = "éè´è®¢åç¼å·", requiredMode = Schema.RequiredMode.REQUIRED, example = "17386") |
| | | @NotNull(message = "éè´è®¢åç¼å·ä¸è½ä¸ºç©º") |
| | | private Long purchaseOrderId; |
| | | |
| | | @Schema(description = "å票å·ç ", example = "02564178") |
| | | private String invoiceNo; |
| | | |
| | | @Schema(description = "å票æ¬å¤´", example = "XXç§ææéå
¬å¸") |
| | | private String invoiceTitle; |
| | | |
| | | @Schema(description = "æ¥ç¥¨éé¢ï¼åä½ï¼å
", requiredMode = Schema.RequiredMode.REQUIRED, example = "9000") |
| | | @NotNull(message = "æ¥ç¥¨éé¢ä¸è½ä¸ºç©º") |
| | | private BigDecimal price; |
| | | |
| | | @Schema(description = "æ¥ç¥¨æ¥æ", requiredMode = Schema.RequiredMode.REQUIRED, example = "2024-02-02") |
| | | @NotNull(message = "æ¥ç¥¨æ¥æä¸è½ä¸ºç©º") |
| | | private LocalDateTime invoiceTime; |
| | | |
| | | @Schema(description = "夿³¨", example = "夿³¨") |
| | | private String remark; |
| | | |
| | | @Schema(description = "å票éä»¶ BlobId å表", example = "[1, 2, 3]") |
| | | private List<Long> blobIds; |
| | | |
| | | } |
| | |
| | | package cn.iocoder.yudao.module.erp.dal.dataobject.finance; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpSupplierDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | */ |
| | | private Long supplierId; |
| | | /** |
| | | * å
³èçæ¥ç¥¨ç¼å· |
| | | * |
| | | * å
³è {@link ErpPurchaseInvoiceDO#getId()} |
| | | */ |
| | | private Long invoiceId; |
| | | /** |
| | | * 仿¬¾è´¦æ·ç¼å· |
| | | * |
| | | * å
³è {@link ErpAccountDO#getId()} |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.dal.dataobject.purchase; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import cn.iocoder.yudao.module.erp.enums.ErpPurchaseInvoiceAuditStatusEnum; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDateTime; |
| | | |
| | | /** |
| | | * ERP éè´æ¥ç¥¨ DO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @TableName("erp_purchase_invoice") |
| | | @KeySequence("erp_purchase_invoice_seq") // ç¨äº OracleãPostgreSQLãKingbaseãDB2ãH2 æ°æ®åºç主é®èªå¢ãå¦ææ¯ MySQL çæ°æ®åºï¼å¯ä¸åã |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class ErpPurchaseInvoiceDO extends BaseDO { |
| | | |
| | | /** |
| | | * ç¼å· |
| | | */ |
| | | @TableId |
| | | private Long id; |
| | | /** |
| | | * æ¥ç¥¨ç¼å· |
| | | */ |
| | | private String no; |
| | | /** |
| | | * ä¾åºåç¼å· |
| | | * |
| | | * å
³è {@link ErpSupplierDO#getId()} |
| | | */ |
| | | private Long supplierId; |
| | | /** |
| | | * éè´è®¢åç¼å· |
| | | * |
| | | * å
³è {@link ErpPurchaseOrderDO#getId()} |
| | | */ |
| | | private Long purchaseOrderId; |
| | | /** |
| | | * å票å·ç |
| | | */ |
| | | private String invoiceNo; |
| | | /** |
| | | * å票æ¬å¤´ |
| | | */ |
| | | private String invoiceTitle; |
| | | /** |
| | | * æ¥ç¥¨éé¢ï¼åä½ï¼å
|
| | | */ |
| | | private BigDecimal price; |
| | | /** |
| | | * æ¥ç¥¨æ¥æ |
| | | */ |
| | | private LocalDateTime invoiceTime; |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | | private String remark; |
| | | /** |
| | | * 工使µç¼å· |
| | | * |
| | | * å
³è ProcessInstance ç id 屿§ |
| | | */ |
| | | private String processInstanceId; |
| | | /** |
| | | * 审æ¹ç¶æ |
| | | * |
| | | * æä¸¾ {@link ErpPurchaseInvoiceAuditStatusEnum} |
| | | */ |
| | | private Integer auditStatus; |
| | | |
| | | } |
| | |
| | | package cn.iocoder.yudao.module.erp.dal.mysql.finance; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.MPJLambdaWrapperX; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO; |
| | | import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; |
| | | |
| | | /** |
| | | * ERP 仿¬¾å Mapper |
| | |
| | | return selectOne(ErpFinancePaymentDO::getNo, no); |
| | | } |
| | | |
| | | default List<ErpFinancePaymentDO> selectListByInvoiceIdAndStatus(Long invoiceId, Collection<Integer> statuses) { |
| | | return selectList(new LambdaQueryWrapperX<ErpFinancePaymentDO>() |
| | | .eq(ErpFinancePaymentDO::getInvoiceId, invoiceId) |
| | | .in(ErpFinancePaymentDO::getStatus, statuses)); |
| | | } |
| | | |
| | | /** |
| | | * ç»è®¡æ¯å¼ æ¥ç¥¨å·²ä»æ¬¾éé¢ï¼æªå®¡æ ¸ + 审æ¹ä¸ + å·²å®¡æ ¸ï¼æé¤ä½åºï¼ï¼ç¨äºæ ¡éªå©ä½å¯ä»éé¢ |
| | | * |
| | | * @param invoiceIds æ¥ç¥¨ç¼å·å表 |
| | | * @return key 为æ¥ç¥¨ç¼å·ï¼value ä¸ºå·²ä»æ¬¾éé¢ |
| | | */ |
| | | default Map<Long, BigDecimal> selectPaymentPriceMapByInvoiceId(Collection<Long> invoiceIds) { |
| | | if (CollUtil.isEmpty(invoiceIds)) { |
| | | return Collections.emptyMap(); |
| | | } |
| | | // SQL sum æ¥è¯¢ |
| | | List<Map<String, Object>> result = selectMaps(new QueryWrapper<ErpFinancePaymentDO>() |
| | | .select("invoice_id, SUM(payment_price) AS total_payment_price") |
| | | .in("status", ErpAuditStatus.PROCESS.getStatus(), // æªå®¡æ ¸ + 审æ¹ä¸ + å·²å®¡æ ¸ |
| | | ErpAuditStatus.APPROVING.getStatus(), ErpAuditStatus.APPROVE.getStatus()) |
| | | .groupBy("invoice_id") |
| | | .in("invoice_id", invoiceIds)); |
| | | // è·å¾éé¢ |
| | | return convertMap(result, obj -> (Long) obj.get("invoice_id"), obj -> (BigDecimal) obj.get("total_payment_price")); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.dal.mysql.purchase; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoicePageReqVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO; |
| | | import cn.iocoder.yudao.module.erp.enums.ErpPurchaseInvoiceAuditStatusEnum; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * ERP éè´æ¥ç¥¨ Mapper |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Mapper |
| | | public interface ErpPurchaseInvoiceMapper extends BaseMapperX<ErpPurchaseInvoiceDO> { |
| | | |
| | | default ErpPurchaseInvoiceDO selectByNo(String no) { |
| | | return selectOne(ErpPurchaseInvoiceDO::getNo, no); |
| | | } |
| | | |
| | | default PageResult<ErpPurchaseInvoiceDO> selectPage(ErpPurchaseInvoicePageReqVO reqVO) { |
| | | return selectPage(reqVO, new LambdaQueryWrapperX<ErpPurchaseInvoiceDO>() |
| | | .likeIfPresent(ErpPurchaseInvoiceDO::getNo, reqVO.getNo()) |
| | | .eqIfPresent(ErpPurchaseInvoiceDO::getSupplierId, reqVO.getSupplierId()) |
| | | .eqIfPresent(ErpPurchaseInvoiceDO::getPurchaseOrderId, reqVO.getPurchaseOrderId()) |
| | | .eqIfPresent(ErpPurchaseInvoiceDO::getAuditStatus, reqVO.getAuditStatus()) |
| | | .orderByDesc(ErpPurchaseInvoiceDO::getId)); |
| | | } |
| | | |
| | | default List<ErpPurchaseInvoiceDO> selectListByOrderIdAndStatus(Long orderId, Collection<Integer> auditStatuses) { |
| | | return selectList(new LambdaQueryWrapperX<ErpPurchaseInvoiceDO>() |
| | | .eq(ErpPurchaseInvoiceDO::getPurchaseOrderId, orderId) |
| | | .in(ErpPurchaseInvoiceDO::getAuditStatus, auditStatuses)); |
| | | } |
| | | |
| | | default List<ErpPurchaseInvoiceDO> selectListBySupplierIdAndStatus(Long supplierId, Collection<Integer> auditStatuses) { |
| | | return selectList(new LambdaQueryWrapperX<ErpPurchaseInvoiceDO>() |
| | | .eq(ErpPurchaseInvoiceDO::getSupplierId, supplierId) |
| | | .in(ErpPurchaseInvoiceDO::getAuditStatus, auditStatuses)); |
| | | } |
| | | |
| | | default Long selectCountByAudit() { |
| | | return selectCount(ErpPurchaseInvoiceDO::getAuditStatus, ErpPurchaseInvoiceAuditStatusEnum.PROCESS.getStatus()); |
| | | } |
| | | |
| | | } |
| | |
| | | public static final String PURCHASE_REQUEST_NO_PREFIX = "CGSQ"; |
| | | |
| | | /** |
| | | * éè´æ¥ç¥¨ {@link cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO} |
| | | */ |
| | | public static final String PURCHASE_INVOICE_NO_PREFIX = "FPD"; |
| | | |
| | | /** |
| | | * 仿¬¾å {@link cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO} |
| | | */ |
| | | public static final String FINANCE_PAYMENT_NO_PREFIX = "FKD"; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.enums; |
| | | |
| | | import cn.iocoder.yudao.framework.common.core.ArrayValuable; |
| | | import lombok.Getter; |
| | | import lombok.RequiredArgsConstructor; |
| | | |
| | | import java.util.Arrays; |
| | | |
| | | /** |
| | | * ERP éè´æ¥ç¥¨å®¡æ¹ç¶ææä¸¾ |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @RequiredArgsConstructor |
| | | @Getter |
| | | public enum ErpPurchaseInvoiceAuditStatusEnum implements ArrayValuable<Integer> { |
| | | |
| | | DRAFT(0, "æªæäº¤"), |
| | | PROCESS(10, "审æ¹ä¸"), |
| | | APPROVE(20, "å®¡æ ¸éè¿"), |
| | | REJECT(30, "å®¡æ ¸ä¸éè¿"), |
| | | CANCEL(40, "已忶"); |
| | | |
| | | public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpPurchaseInvoiceAuditStatusEnum::getStatus).toArray(Integer[]::new); |
| | | |
| | | private final Integer status; |
| | | private final String name; |
| | | |
| | | @Override |
| | | public Integer[] array() { |
| | | return ARRAYS; |
| | | } |
| | | |
| | | } |
| | |
| | | ErrorCode PURCHASE_REQUEST_BPM_CATEGORY_NOT_EXISTS = new ErrorCode(1_030_104_008, "éè´ç³è¯·å®¡æ¹åç±»ä¸åå¨ï¼è¯·å
å¨BPM模åå建éè´ç³è¯·å®¡æ¹æµç¨åç±»"); |
| | | ErrorCode PURCHASE_REQUEST_BPM_PROCESS_DEFINITION_NOT_EXISTS = new ErrorCode(1_030_104_009, "éè´ç³è¯·å®¡æ¹åç±»ä¸æ²¡æå¯ç¨çæµç¨æ¨¡åï¼è¯·å
å建并å叿µç¨"); |
| | | |
| | | // ========== ERP éè´æ¥ç¥¨ï¼1-030-105-000ï¼ ========== |
| | | ErrorCode PURCHASE_INVOICE_NOT_EXISTS = new ErrorCode(1_030_105_000, "æ¥ç¥¨ä¸åå¨"); |
| | | ErrorCode PURCHASE_INVOICE_NO_EXISTS = new ErrorCode(1_030_105_001, "çææ¥ç¥¨ç¼å·å¤±è´¥ï¼è¯·éæ°æäº¤"); |
| | | ErrorCode PURCHASE_INVOICE_UPDATE_FAIL_EDITING_PROHIBITED = new ErrorCode(1_030_105_002, "æ¥ç¥¨({})䏿¯èç¨¿ç¶æï¼æ æ³ä¿®æ¹"); |
| | | ErrorCode PURCHASE_INVOICE_DELETE_FAIL_IS_APPROVE = new ErrorCode(1_030_105_003, "å 餿¥ç¥¨å¤±è´¥ï¼åå ï¼æ¥ç¥¨å®¡æ¹å·²éè¿"); |
| | | ErrorCode PURCHASE_INVOICE_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_030_105_004, "æ¥ç¥¨æäº¤å®¡æ ¸å¤±è´¥ï¼åå ï¼æ¥ç¥¨æ²¡å¤å¨æªæäº¤ç¶æ"); |
| | | ErrorCode PURCHASE_INVOICE_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_030_105_005, "æ´æ°æ¥ç¥¨å®¡æ ¸ç¶æå¤±è´¥ï¼åå ï¼æ¥ç¥¨ä¸æ¯å®¡æ ¸ä¸ç¶æ"); |
| | | ErrorCode PURCHASE_INVOICE_CREATE_FAIL_ORDER_NOT_APPROVE = new ErrorCode(1_030_105_006, "å建æ¥ç¥¨å¤±è´¥ï¼åå ï¼éè´è®¢å䏿¯å®¡æ ¸éè¿ç¶æ"); |
| | | ErrorCode PURCHASE_INVOICE_CREATE_FAIL_SUPPLIER_MISMATCH = new ErrorCode(1_030_105_007, "å建æ¥ç¥¨å¤±è´¥ï¼åå ï¼æ¥ç¥¨ä¾åºåä¸éè´è®¢åä¸ä¸è´"); |
| | | ErrorCode PURCHASE_INVOICE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT = new ErrorCode(1_030_105_008, "å建æ¥ç¥¨å¤±è´¥ï¼åå ï¼æ¥ç¥¨éé¢è¶
åºéè´è®¢åéé¢ï¼ç®åå¯å¼ç¥¨ï¼{} å
"); |
| | | |
| | | // ========== ERP éè´éè´§ï¼1-030-103-000ï¼ ========== |
| | | ErrorCode PURCHASE_RETURN_NOT_EXISTS = new ErrorCode(1_030_103_000, "éè´éè´§åä¸åå¨"); |
| | | ErrorCode PURCHASE_RETURN_DELETE_FAIL_APPROVE = new ErrorCode(1_030_103_001, "éè´éè´§å({})å·²å®¡æ ¸ï¼æ æ³å é¤"); |
| | |
| | | ErrorCode FINANCE_PAYMENT_APPROVE_FAIL = new ErrorCode(1_030_601_003, "å®¡æ ¸å¤±è´¥ï¼åªææªå®¡æ ¸ç仿¬¾åæè½å®¡æ ¸"); |
| | | ErrorCode FINANCE_PAYMENT_NO_EXISTS = new ErrorCode(1_030_601_004, "çæä»æ¬¾åå·å¤±è´¥ï¼è¯·éæ°æäº¤"); |
| | | ErrorCode FINANCE_PAYMENT_UPDATE_FAIL_APPROVE = new ErrorCode(1_030_601_005, "仿¬¾å({})å·²å®¡æ ¸ï¼æ æ³ä¿®æ¹"); |
| | | ErrorCode FINANCE_PAYMENT_CREATE_FAIL_INVOICE_NOT_EXISTS = new ErrorCode(1_030_601_006, "åå»ºä»æ¬¾å失败ï¼åå ï¼å
³èçæ¥ç¥¨ä¸åå¨"); |
| | | ErrorCode FINANCE_PAYMENT_CREATE_FAIL_INVOICE_NOT_APPROVE = new ErrorCode(1_030_601_007, "åå»ºä»æ¬¾å失败ï¼åå ï¼å
³èçæ¥ç¥¨ä¸æ¯å®¡æ¹éè¿ç¶æ"); |
| | | ErrorCode FINANCE_PAYMENT_CREATE_FAIL_INVOICE_NOT_ATTACHMENT = new ErrorCode(1_030_601_008, "åå»ºä»æ¬¾å失败ï¼åå ï¼å
³èçæ¥ç¥¨æªä¸ä¼ å票éä»¶"); |
| | | ErrorCode FINANCE_PAYMENT_CREATE_FAIL_INVOICE_SUPPLIER_MISMATCH = new ErrorCode(1_030_601_009, "åå»ºä»æ¬¾å失败ï¼åå ï¼å
³èçæ¥ç¥¨ä¸æéä¾åºåä¸å¹é
"); |
| | | ErrorCode FINANCE_PAYMENT_CREATE_FAIL_INVOICE_PRICE_EXCEEDS = new ErrorCode(1_030_601_010, "åå»ºä»æ¬¾å失败ï¼åå ï¼ä»æ¬¾éé¢è¶
åºè¯¥æ¥ç¥¨å©ä½å¯ä»éé¢ï¼ç®åå¯ä»ï¼{} å
"); |
| | | |
| | | // ========== ERP æ¶æ¬¾å 1-030-602-000 ========== |
| | | ErrorCode FINANCE_RECEIPT_NOT_EXISTS = new ErrorCode(1_030_602_000, "æ¶æ¬¾åä¸åå¨"); |
| | |
| | | PURCHASE_ORDER(10, "éè´è®¢å"), |
| | | PURCHASE_IN(11, "éè´å
¥åº"), |
| | | PURCHASE_RETURN(12, "éè´éè´§"), |
| | | PURCHASE_INVOICE(13, "éè´æ¥ç¥¨"), |
| | | |
| | | SALE_ORDER(20, "éå®è®¢å"), |
| | | SALE_OUT(21, "éå®åºåº"), |
| | |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO; |
| | | import jakarta.validation.Valid; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * ERP 仿¬¾å Service æ¥å£ |
| | |
| | | */ |
| | | PageResult<ErpFinancePaymentDO> getFinancePaymentPage(ErpFinancePaymentPageReqVO pageReqVO); |
| | | |
| | | /** |
| | | * ç»è®¡æ¯å¼ æ¥ç¥¨å·²ä»æ¬¾éé¢ï¼æªå®¡æ ¸ + 审æ¹ä¸ + å·²å®¡æ ¸ï¼æé¤ä½åºï¼ |
| | | * |
| | | * @param invoiceIds æ¥ç¥¨ç¼å·å表 |
| | | * @return key 为æ¥ç¥¨ç¼å·ï¼value ä¸ºå·²ä»æ¬¾éé¢ |
| | | */ |
| | | Map<Long, BigDecimal> getPaymentPriceMapByInvoiceId(Collection<Long> invoiceIds); |
| | | |
| | | // ==================== 仿¬¾å项 ==================== |
| | | |
| | | /** |
| | |
| | | package cn.iocoder.yudao.module.erp.service.finance; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentPageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.finance.vo.payment.ErpFinancePaymentSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.finance.ErpFinancePaymentItemDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO; |
| | | import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinancePaymentItemMapper; |
| | | import cn.iocoder.yudao.module.erp.dal.mysql.finance.ErpFinancePaymentMapper; |
| | | import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; |
| | | import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; |
| | | import cn.iocoder.yudao.module.erp.enums.ErpPurchaseInvoiceAuditStatusEnum; |
| | | import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInvoiceService; |
| | | import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInvoiceServiceImpl; |
| | | import cn.iocoder.yudao.module.erp.service.purchase.ErpSupplierService; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import cn.iocoder.yudao.module.system.api.user.AdminUserApi; |
| | |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; |
| | |
| | | private ErpSupplierService supplierService; |
| | | @Resource |
| | | private ErpAccountService accountService; |
| | | @Resource |
| | | private ErpPurchaseInvoiceService invoiceService; |
| | | |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | |
| | | if (createReqVO.getFinanceUserId() != null) { |
| | | adminUserApi.validateUser(createReqVO.getFinanceUserId()); |
| | | } |
| | | // 1.5 çæä»æ¬¾åå·ï¼å¹¶æ ¡éªå¯ä¸æ§ |
| | | // 1.5 æ ¡éªå
³èæ¥ç¥¨ |
| | | validateInvoiceForPayment(createReqVO.getInvoiceId(), createReqVO.getSupplierId()); |
| | | // 1.6 æ ¡éªä»æ¬¾éé¢ä¸è¶
è¿è¯¥æ¥ç¥¨å©ä½å¯ä»éé¢ |
| | | validatePaymentPriceExceedsInvoiceRemaining(createReqVO.getInvoiceId(), |
| | | getSumValue(paymentItems, ErpFinancePaymentItemDO::getPaymentPrice, BigDecimal::add, BigDecimal.ZERO) |
| | | .subtract(createReqVO.getDiscountPrice()), null); |
| | | // 1.7 çæä»æ¬¾åå·ï¼å¹¶æ ¡éªå¯ä¸æ§ |
| | | String no = noRedisDAO.generate(ErpNoRedisDAO.FINANCE_PAYMENT_NO_PREFIX); |
| | | if (financePaymentMapper.selectByNo(no) != null) { |
| | | throw exception(FINANCE_PAYMENT_NO_EXISTS); |
| | |
| | | // 1.5 æ ¡éªä»æ¬¾åé¡¹çæææ§ |
| | | List<ErpFinancePaymentItemDO> paymentItems = validateFinancePaymentItems( |
| | | updateReqVO.getSupplierId(), updateReqVO.getItems()); |
| | | // 1.6 æ ¡éªå
³èæ¥ç¥¨ |
| | | validateInvoiceForPayment(updateReqVO.getInvoiceId(), updateReqVO.getSupplierId()); |
| | | // 1.7 æ ¡éªä»æ¬¾éé¢ä¸è¶
è¿è¯¥æ¥ç¥¨å©ä½å¯ä»éé¢ï¼æé¤è¯¥ä»æ¬¾åèªèº«å½åå·²ä»éé¢ï¼ |
| | | validatePaymentPriceExceedsInvoiceRemaining(updateReqVO.getInvoiceId(), |
| | | getSumValue(paymentItems, ErpFinancePaymentItemDO::getPaymentPrice, BigDecimal::add, BigDecimal.ZERO) |
| | | .subtract(updateReqVO.getDiscountPrice()), payment.getPaymentPrice()); |
| | | |
| | | // 2.1 æ´æ°ä»æ¬¾å |
| | | ErpFinancePaymentDO updateObj = BeanUtils.toBean(updateReqVO, ErpFinancePaymentDO.class); |
| | |
| | | return payment; |
| | | } |
| | | |
| | | /** |
| | | * æ ¡éªä»æ¬¾åå
³èçæ¥ç¥¨ï¼åå¨ã审æ¹éè¿ãå·²ä¸ä¼ å票éä»¶ãä¾åºå䏿¥ç¥¨ä¸è´ |
| | | * |
| | | * @param invoiceId æ¥ç¥¨ç¼å· |
| | | * @param supplierId ä¾åºåç¼å· |
| | | */ |
| | | private void validateInvoiceForPayment(Long invoiceId, Long supplierId) { |
| | | // 1. æ ¡éªæ¥ç¥¨åå¨ |
| | | ErpPurchaseInvoiceDO invoice = invoiceService.getPurchaseInvoice(invoiceId); |
| | | if (invoice == null) { |
| | | throw exception(FINANCE_PAYMENT_CREATE_FAIL_INVOICE_NOT_EXISTS); |
| | | } |
| | | // 2. æ ¡éªæ¥ç¥¨å®¡æ¹éè¿ |
| | | if (ObjectUtil.notEqual(invoice.getAuditStatus(), ErpPurchaseInvoiceAuditStatusEnum.APPROVE.getStatus())) { |
| | | throw exception(FINANCE_PAYMENT_CREATE_FAIL_INVOICE_NOT_APPROVE); |
| | | } |
| | | // 3. æ ¡éªå·²ä¸ä¼ å票éä»¶ |
| | | if (CollUtil.isEmpty(storageAttachmentApi.listAttachments( |
| | | ErpPurchaseInvoiceServiceImpl.PURCHASE_INVOICE_RECORD_TYPE, invoiceId))) { |
| | | throw exception(FINANCE_PAYMENT_CREATE_FAIL_INVOICE_NOT_ATTACHMENT); |
| | | } |
| | | // 4. æ ¡éªä¾åºå䏿¥ç¥¨ä¸è´ï¼é²èæ°æ®ï¼ |
| | | if (ObjectUtil.notEqual(invoice.getSupplierId(), supplierId)) { |
| | | throw exception(FINANCE_PAYMENT_CREATE_FAIL_INVOICE_SUPPLIER_MISMATCH); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ ¡éªä»æ¬¾éé¢ä¸è¶
è¿è¯¥æ¥ç¥¨å©ä½å¯ä»éé¢ï¼æ¥ç¥¨éé¢ - 该æ¥ç¥¨å·²ä»æ¬¾éé¢ï¼ |
| | | * |
| | | * @param invoiceId æ¥ç¥¨ç¼å· |
| | | * @param paymentPrice æ¬æ¬¡ä»æ¬¾éé¢ |
| | | * @param oldPaymentPrice æ´æ°åºæ¯ä¸ï¼è¯¥ä»æ¬¾åèªèº«å½åç仿¬¾éé¢ï¼ç¨äºæé¤èªèº«ï¼ï¼åå»ºåºæ¯ä¼ null |
| | | */ |
| | | private void validatePaymentPriceExceedsInvoiceRemaining(Long invoiceId, BigDecimal paymentPrice, BigDecimal oldPaymentPrice) { |
| | | ErpPurchaseInvoiceDO invoice = invoiceService.getPurchaseInvoice(invoiceId); |
| | | BigDecimal paidPrice = financePaymentMapper.selectPaymentPriceMapByInvoiceId(List.of(invoiceId)) |
| | | .getOrDefault(invoiceId, BigDecimal.ZERO); |
| | | if (oldPaymentPrice != null) { |
| | | paidPrice = paidPrice.subtract(oldPaymentPrice); // æé¤è¯¥ä»æ¬¾åèªèº«å½åå·²ä»éé¢ |
| | | } |
| | | BigDecimal remainingPrice = invoice.getPrice().subtract(paidPrice); |
| | | if (paymentPrice.compareTo(remainingPrice) > 0) { |
| | | throw exception(FINANCE_PAYMENT_CREATE_FAIL_INVOICE_PRICE_EXCEEDS, remainingPrice); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public ErpFinancePaymentDO getFinancePayment(Long id) { |
| | | return financePaymentMapper.selectById(id); |
| | |
| | | return financePaymentMapper.selectPage(pageReqVO); |
| | | } |
| | | |
| | | @Override |
| | | public Map<Long, BigDecimal> getPaymentPriceMapByInvoiceId(Collection<Long> invoiceIds) { |
| | | return financePaymentMapper.selectPaymentPriceMapByInvoiceId(invoiceIds); |
| | | } |
| | | |
| | | // ==================== 仿¬¾å项 ==================== |
| | | |
| | | @Override |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.service.purchase; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoicePageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoiceSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO; |
| | | import jakarta.validation.Valid; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; |
| | | |
| | | /** |
| | | * ERP éè´æ¥ç¥¨ Service æ¥å£ |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | public interface ErpPurchaseInvoiceService { |
| | | |
| | | /** |
| | | * å建æ¥ç¥¨ |
| | | * |
| | | * @param createReqVO åå»ºä¿¡æ¯ |
| | | * @return ç¼å· |
| | | */ |
| | | Long createPurchaseInvoice(@Valid ErpPurchaseInvoiceSaveReqVO createReqVO); |
| | | |
| | | /** |
| | | * æ´æ°æ¥ç¥¨ |
| | | * |
| | | * @param updateReqVO æ´æ°ä¿¡æ¯ |
| | | */ |
| | | void updatePurchaseInvoice(@Valid ErpPurchaseInvoiceSaveReqVO updateReqVO); |
| | | |
| | | /** |
| | | * å 餿¥ç¥¨ |
| | | * |
| | | * @param id ç¼å· |
| | | */ |
| | | void deletePurchaseInvoice(Long id); |
| | | |
| | | /** |
| | | * æäº¤æ¥ç¥¨å®¡æ¹ |
| | | * |
| | | * @param id ç¼å· |
| | | * @param userId ç¨æ·ç¼å· |
| | | */ |
| | | void submitPurchaseInvoice(Long id, Long userId); |
| | | |
| | | /** |
| | | * æ´æ°æ¥ç¥¨å®¡æ¹ç»æï¼BPM åè°ï¼ |
| | | * |
| | | * @param id ç¼å· |
| | | * @param bpmResult BPM 审æ¹ç»æ |
| | | */ |
| | | void updatePurchaseInvoiceAuditStatus(Long id, Integer bpmResult); |
| | | |
| | | /** |
| | | * è·å¾æ¥ç¥¨ |
| | | * |
| | | * @param id ç¼å· |
| | | * @return æ¥ç¥¨ |
| | | */ |
| | | ErpPurchaseInvoiceDO getPurchaseInvoice(Long id); |
| | | |
| | | /** |
| | | * è·å¾æ¥ç¥¨å表 |
| | | * |
| | | * @param ids ç¼å·å表 |
| | | * @return æ¥ç¥¨å表 |
| | | */ |
| | | List<ErpPurchaseInvoiceDO> getPurchaseInvoiceList(Collection<Long> ids); |
| | | |
| | | /** |
| | | * è·å¾æ¥ç¥¨ Map |
| | | * |
| | | * @param ids ç¼å·å表 |
| | | * @return æ¥ç¥¨ Map |
| | | */ |
| | | default Map<Long, ErpPurchaseInvoiceDO> getPurchaseInvoiceMap(Collection<Long> ids) { |
| | | return convertMap(getPurchaseInvoiceList(ids), ErpPurchaseInvoiceDO::getId); |
| | | } |
| | | |
| | | /** |
| | | * è·å¾æ¥ç¥¨å页 |
| | | * |
| | | * @param pageReqVO å页æ¥è¯¢ |
| | | * @return æ¥ç¥¨å页 |
| | | */ |
| | | PageResult<ErpPurchaseInvoiceDO> getPurchaseInvoicePage(ErpPurchaseInvoicePageReqVO pageReqVO); |
| | | |
| | | /** |
| | | * æ ¹æ®éè´è®¢åç¼å·å审æ¹ç¶æè·å¾æ¥ç¥¨å表 |
| | | * |
| | | * @param orderId éè´è®¢åç¼å· |
| | | * @param auditStatuses 审æ¹ç¶æéå |
| | | * @return æ¥ç¥¨å表 |
| | | */ |
| | | List<ErpPurchaseInvoiceDO> getPurchaseInvoiceListByOrderIdAndStatus(Long orderId, Collection<Integer> auditStatuses); |
| | | |
| | | /** |
| | | * æ ¹æ®ä¾åºåç¼å·å审æ¹ç¶æè·å¾æ¥ç¥¨å表 |
| | | * |
| | | * @param supplierId ä¾åºåç¼å· |
| | | * @param auditStatuses 审æ¹ç¶æéå |
| | | * @return æ¥ç¥¨å表 |
| | | */ |
| | | List<ErpPurchaseInvoiceDO> getPurchaseInvoiceListBySupplierIdAndStatus(Long supplierId, Collection<Integer> auditStatuses); |
| | | |
| | | /** |
| | | * è·å¾å¾
å®¡æ ¸æ¥ç¥¨æ°é |
| | | * |
| | | * @return æ°é |
| | | */ |
| | | Long getAuditInvoiceCount(); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.service.purchase; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.lang.Assert; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; |
| | | import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; |
| | | import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoicePageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.invoice.ErpPurchaseInvoiceSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseInvoiceDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; |
| | | import cn.iocoder.yudao.module.erp.dal.mysql.purchase.ErpPurchaseInvoiceMapper; |
| | | import cn.iocoder.yudao.module.erp.dal.redis.no.ErpNoRedisDAO; |
| | | import cn.iocoder.yudao.module.erp.enums.ErpPurchaseInvoiceAuditStatusEnum; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.util.Collection; |
| | | import java.util.List; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue; |
| | | import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*; |
| | | |
| | | /** |
| | | * ERP éè´æ¥ç¥¨ Service å®ç°ç±» |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Service |
| | | @Validated |
| | | @Slf4j |
| | | public class ErpPurchaseInvoiceServiceImpl implements ErpPurchaseInvoiceService { |
| | | |
| | | /** |
| | | * BPM æ¥ç¥¨å®¡æ¹æµç¨æ è¯ |
| | | */ |
| | | public static final String BPM_PROCESS_DEFINITION_KEY = "erp-purchase-invoice-audit"; |
| | | |
| | | /** |
| | | * æ¥ç¥¨éä»¶çä¸å¡è®°å½ç±»åï¼å¯¹åº {@link cn.iocoder.yudao.module.system.enums.storage.StorageRecordTypeEnum#ERP_PURCHASE_INVOICE} |
| | | */ |
| | | public static final String PURCHASE_INVOICE_RECORD_TYPE = "erp_purchase_invoice"; |
| | | |
| | | @Resource |
| | | private ErpPurchaseInvoiceMapper invoiceMapper; |
| | | |
| | | @Resource |
| | | private ErpNoRedisDAO noRedisDAO; |
| | | |
| | | @Resource |
| | | private ErpPurchaseOrderService purchaseOrderService; |
| | | |
| | | @Resource |
| | | private BpmProcessInstanceApi bpmProcessInstanceApi; |
| | | @Resource |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createPurchaseInvoice(ErpPurchaseInvoiceSaveReqVO createReqVO) { |
| | | // 1.1 æ ¡éªå
³èæ°æ®åå¨ |
| | | validateRelationDataExists(createReqVO); |
| | | // 1.2 æ ¡éªå¯å¼ç¥¨éé¢è¶
è¿ä¸é |
| | | validatePurchaseInvoicePriceExceedsLimit(createReqVO); |
| | | // 1.3 çææ¥ç¥¨ç¼å· |
| | | String no = noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_INVOICE_NO_PREFIX); |
| | | if (invoiceMapper.selectByNo(no) != null) { |
| | | throw exception(PURCHASE_INVOICE_NO_EXISTS); |
| | | } |
| | | |
| | | // 2.1 æå
¥æ¥ç¥¨ |
| | | ErpPurchaseInvoiceDO invoice = BeanUtils.toBean(createReqVO, ErpPurchaseInvoiceDO.class) |
| | | .setNo(no).setAuditStatus(ErpPurchaseInvoiceAuditStatusEnum.DRAFT.getStatus()); |
| | | invoiceMapper.insert(invoice); |
| | | |
| | | // 2.2 ç»å®å票éä»¶ |
| | | if (CollUtil.isNotEmpty(createReqVO.getBlobIds())) { |
| | | storageAttachmentApi.bindAttachments("file", PURCHASE_INVOICE_RECORD_TYPE, invoice.getId(), createReqVO.getBlobIds()); |
| | | } |
| | | return invoice.getId(); |
| | | } |
| | | |
| | | private void validateRelationDataExists(ErpPurchaseInvoiceSaveReqVO reqVO) { |
| | | // æ ¡éªéè´è®¢åï¼å¿
é¡»å®¡æ ¸éè¿ |
| | | ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.validatePurchaseOrder(reqVO.getPurchaseOrderId()); |
| | | // æ ¡éªä¾åºåä¸éè´è®¢åä¸è´ï¼é²å¾¡èæ°æ®ï¼æ¥ç¥¨å¿
é¡»å±äºè¯¥éè´è®¢åçä¾åºåï¼ |
| | | if (reqVO.getSupplierId() != null && ObjectUtil.notEqual(purchaseOrder.getSupplierId(), reqVO.getSupplierId())) { |
| | | throw exception(PURCHASE_INVOICE_CREATE_FAIL_SUPPLIER_MISMATCH); |
| | | } |
| | | reqVO.setSupplierId(purchaseOrder.getSupplierId()); // ä¾åºå以éè´è®¢å为å |
| | | } |
| | | |
| | | private void validatePurchaseInvoicePriceExceedsLimit(ErpPurchaseInvoiceSaveReqVO reqVO) { |
| | | // 1. 计ç®å©ä½å¯å¼ç¥¨éé¢ï¼ä¸å
æ¬ reqVO èªèº«ï¼å®¡æ¹ä¸ä¹å ç¨é¢åº¦ï¼é²æ¢ç´¯è®¡æ¥ç¥¨è¶
è¿éè´è®¢åéé¢ï¼ |
| | | ErpPurchaseOrderDO purchaseOrder = purchaseOrderService.getPurchaseOrder(reqVO.getPurchaseOrderId()); |
| | | List<ErpPurchaseInvoiceDO> invoices = invoiceMapper.selectListByOrderIdAndStatus(reqVO.getPurchaseOrderId(), |
| | | List.of(ErpPurchaseInvoiceAuditStatusEnum.DRAFT.getStatus(), |
| | | ErpPurchaseInvoiceAuditStatusEnum.PROCESS.getStatus(), |
| | | ErpPurchaseInvoiceAuditStatusEnum.APPROVE.getStatus())); |
| | | if (reqVO.getId() != null) { |
| | | invoices.removeIf(invoice -> ObjectUtil.equal(invoice.getId(), reqVO.getId())); |
| | | } |
| | | BigDecimal remainPrice = purchaseOrder.getTotalPrice().subtract( |
| | | getSumValue(invoices, ErpPurchaseInvoiceDO::getPrice, BigDecimal::add, BigDecimal.ZERO)); |
| | | // 2. æ ¡éªé颿¯å¦è¶
è¿ |
| | | if (reqVO.getPrice().compareTo(remainPrice) > 0) { |
| | | throw exception(PURCHASE_INVOICE_CREATE_FAIL_PRICE_EXCEEDS_LIMIT, remainPrice); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updatePurchaseInvoice(ErpPurchaseInvoiceSaveReqVO updateReqVO) { |
| | | Assert.notNull(updateReqVO.getId(), "æ¥ç¥¨ç¼å·ä¸è½ä¸ºç©º"); |
| | | // 1.1 æ ¡éªåå¨ |
| | | ErpPurchaseInvoiceDO oldInvoice = validatePurchaseInvoiceExists(updateReqVO.getId()); |
| | | // 1.2 åªæè稿å¯ä»¥ç¼è¾ |
| | | if (ObjectUtil.notEqual(oldInvoice.getAuditStatus(), ErpPurchaseInvoiceAuditStatusEnum.DRAFT.getStatus())) { |
| | | throw exception(PURCHASE_INVOICE_UPDATE_FAIL_EDITING_PROHIBITED, oldInvoice.getNo()); |
| | | } |
| | | // 1.3 æ ¡éªå
³èæ°æ® + å¯å¼ç¥¨éé¢ |
| | | validateRelationDataExists(updateReqVO); |
| | | validatePurchaseInvoicePriceExceedsLimit(updateReqVO); |
| | | |
| | | // 2. æ´æ°æ¥ç¥¨ |
| | | ErpPurchaseInvoiceDO updateObj = BeanUtils.toBean(updateReqVO, ErpPurchaseInvoiceDO.class); |
| | | invoiceMapper.updateById(updateObj); |
| | | |
| | | // 3. å¢éæ´æ°å票éä»¶ |
| | | storageAttachmentApi.updateAttachments("file", PURCHASE_INVOICE_RECORD_TYPE, updateReqVO.getId(), updateReqVO.getBlobIds()); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deletePurchaseInvoice(Long id) { |
| | | // 1.1 æ ¡éªåå¨ |
| | | ErpPurchaseInvoiceDO invoice = validatePurchaseInvoiceExists(id); |
| | | // 1.2 审æ¹éè¿æ¶ï¼ä¸å
许å é¤ |
| | | if (ObjectUtil.equal(invoice.getAuditStatus(), ErpPurchaseInvoiceAuditStatusEnum.APPROVE.getStatus())) { |
| | | throw exception(PURCHASE_INVOICE_DELETE_FAIL_IS_APPROVE); |
| | | } |
| | | |
| | | // 2.1 å é¤å票éä»¶ |
| | | storageAttachmentApi.deleteAttachmentsByRecord(PURCHASE_INVOICE_RECORD_TYPE, id); |
| | | // 2.2 å 餿¥ç¥¨ |
| | | invoiceMapper.deleteById(id); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void submitPurchaseInvoice(Long id, Long userId) { |
| | | // 1. æ ¡éªæ¥ç¥¨æ¯å¦å¨å®¡æ¹ |
| | | ErpPurchaseInvoiceDO invoice = validatePurchaseInvoiceExists(id); |
| | | if (ObjectUtil.notEqual(invoice.getAuditStatus(), ErpPurchaseInvoiceAuditStatusEnum.DRAFT.getStatus())) { |
| | | throw exception(PURCHASE_INVOICE_SUBMIT_FAIL_NOT_DRAFT); |
| | | } |
| | | |
| | | // 1.1 éæ°æ ¡éªå¯å¼ç¥¨éé¢ï¼é²æ¢ç´¯è®¡æ¥ç¥¨è¶
è¿éè´è®¢åéé¢ï¼è稿æåå¯è½æå
¶ä»åè¿å
¥å®¡æ¹ä¸ï¼ |
| | | validatePurchaseInvoicePriceExceedsLimit(new ErpPurchaseInvoiceSaveReqVO() |
| | | .setId(invoice.getId()).setPurchaseOrderId(invoice.getPurchaseOrderId()) |
| | | .setSupplierId(invoice.getSupplierId()).setPrice(invoice.getPrice())); |
| | | |
| | | // 2. å建æ¥ç¥¨å®¡æ¹æµç¨å®ä¾ |
| | | String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() |
| | | .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY).setBusinessKey(String.valueOf(id))); |
| | | |
| | | // 3. æ´æ°æ¥ç¥¨å·¥ä½æµç¼å· |
| | | invoiceMapper.updateById(new ErpPurchaseInvoiceDO().setId(id).setProcessInstanceId(processInstanceId) |
| | | .setAuditStatus(ErpPurchaseInvoiceAuditStatusEnum.PROCESS.getStatus())); |
| | | } |
| | | |
| | | @Override |
| | | public void updatePurchaseInvoiceAuditStatus(Long id, Integer bpmResult) { |
| | | // 1. æ ¡éªåå¨ + 审æ¹ä¸ç¶æ |
| | | ErpPurchaseInvoiceDO invoice = validatePurchaseInvoiceExists(id); |
| | | if (ObjectUtil.notEqual(invoice.getAuditStatus(), ErpPurchaseInvoiceAuditStatusEnum.PROCESS.getStatus())) { |
| | | log.warn("[updatePurchaseInvoiceAuditStatus][æ¥ç¥¨({}) ä¸å¤äºå®¡æ¹ä¸ï¼æ æ³æ´æ°å®¡æ¹ç»æ({})]", id, bpmResult); |
| | | return; |
| | | } |
| | | // 2. æ ¹æ®å®¡æ¹ç»ææ´æ°ç¶æ |
| | | Integer auditStatus = convertBpmResultToAuditStatus(bpmResult); |
| | | if (auditStatus != null) { |
| | | invoiceMapper.updateById(new ErpPurchaseInvoiceDO().setId(id).setAuditStatus(auditStatus)); |
| | | } |
| | | } |
| | | |
| | | private Integer convertBpmResultToAuditStatus(Integer bpmResult) { |
| | | if (BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult)) { |
| | | return ErpPurchaseInvoiceAuditStatusEnum.APPROVE.getStatus(); |
| | | } else if (BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult)) { |
| | | return ErpPurchaseInvoiceAuditStatusEnum.REJECT.getStatus(); |
| | | } else if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)) { |
| | | return ErpPurchaseInvoiceAuditStatusEnum.CANCEL.getStatus(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | private ErpPurchaseInvoiceDO validatePurchaseInvoiceExists(Long id) { |
| | | ErpPurchaseInvoiceDO invoice = invoiceMapper.selectById(id); |
| | | if (invoice == null) { |
| | | throw exception(PURCHASE_INVOICE_NOT_EXISTS); |
| | | } |
| | | return invoice; |
| | | } |
| | | |
| | | @Override |
| | | public ErpPurchaseInvoiceDO getPurchaseInvoice(Long id) { |
| | | return invoiceMapper.selectById(id); |
| | | } |
| | | |
| | | @Override |
| | | public List<ErpPurchaseInvoiceDO> getPurchaseInvoiceList(Collection<Long> ids) { |
| | | if (CollUtil.isEmpty(ids)) { |
| | | return List.of(); |
| | | } |
| | | return invoiceMapper.selectByIds(ids); |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<ErpPurchaseInvoiceDO> getPurchaseInvoicePage(ErpPurchaseInvoicePageReqVO pageReqVO) { |
| | | return invoiceMapper.selectPage(pageReqVO); |
| | | } |
| | | |
| | | @Override |
| | | public List<ErpPurchaseInvoiceDO> getPurchaseInvoiceListByOrderIdAndStatus(Long orderId, Collection<Integer> auditStatuses) { |
| | | return invoiceMapper.selectListByOrderIdAndStatus(orderId, auditStatuses); |
| | | } |
| | | |
| | | @Override |
| | | public List<ErpPurchaseInvoiceDO> getPurchaseInvoiceListBySupplierIdAndStatus(Long supplierId, Collection<Integer> auditStatuses) { |
| | | return invoiceMapper.selectListBySupplierIdAndStatus(supplierId, auditStatuses); |
| | | } |
| | | |
| | | @Override |
| | | public Long getAuditInvoiceCount() { |
| | | return invoiceMapper.selectCountByAudit(); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.service.purchase.listener; |
| | | |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent; |
| | | import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEventListener; |
| | | import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInvoiceService; |
| | | import cn.iocoder.yudao.module.erp.service.purchase.ErpPurchaseInvoiceServiceImpl; |
| | | import jakarta.annotation.Resource; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.stereotype.Component; |
| | | |
| | | /** |
| | | * éè´æ¥ç¥¨å®¡æ¹ç¶æçå¬å¨ |
| | | * |
| | | * çå¬ BPM æµç¨å®ä¾ç¶æåæ´äºä»¶ï¼æ´æ°æ¥ç¥¨ç审æ¹ç¶æ |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Component |
| | | @Slf4j |
| | | public class ErpPurchaseInvoiceStatusListener extends BpmProcessInstanceStatusEventListener { |
| | | |
| | | @Resource |
| | | private ErpPurchaseInvoiceService invoiceService; |
| | | |
| | | @Override |
| | | protected String getProcessDefinitionKey() { |
| | | return ErpPurchaseInvoiceServiceImpl.BPM_PROCESS_DEFINITION_KEY; |
| | | } |
| | | |
| | | @Override |
| | | protected void onEvent(BpmProcessInstanceStatusEvent event) { |
| | | Long invoiceId; |
| | | try { |
| | | invoiceId = Long.parseLong(event.getBusinessKey()); |
| | | } catch (NumberFormatException e) { |
| | | log.warn("[onEvent] businessKey({}) 䏿¯ææçæ¥ç¥¨ç¼å·", event.getBusinessKey()); |
| | | return; |
| | | } |
| | | log.info("[onEvent] æ¶å°BPM审æ¹äºä»¶ï¼invoiceId={}, status={}", invoiceId, event.getStatus()); |
| | | invoiceService.updatePurchaseInvoiceAuditStatus(invoiceId, event.getStatus()); |
| | | } |
| | | |
| | | } |
| | |
| | | ErrorCode EMPLOYEE_POST_NAME_NOT_EXISTS = new ErrorCode(1_030_000_008, "å²ä½ã{}ãä¸åå¨"); |
| | | ErrorCode EMPLOYEE_ROLE_NAME_NOT_EXISTS = new ErrorCode(1_030_000_009, "è§è²ã{}ãä¸åå¨"); |
| | | ErrorCode EMPLOYEE_IMPORT_FIELD_REQUIRED = new ErrorCode(1_030_000_010, "导å
¥å段ã{}ãä¸è½ä¸ºç©º"); |
| | | ErrorCode EMPLOYEE_CONTRACT_NOT_EXISTS = new ErrorCode(1_030_000_011, "åå·¥ååä¸åå¨"); |
| | | ErrorCode EMPLOYEE_CONTRACT_TERMINATE_REQUIRE_DATE_REASON = new ErrorCode(1_030_000_012, "è§£é¤/ç»æ¢ååå¿
须填忥æååå "); |
| | | ErrorCode EMPLOYEE_CONTRACT_END_DATE_REQUIRE_FOR_FIXED = new ErrorCode(1_030_000_013, "åºå®æéååå¿
须填åç»ææ¥æ"); |
| | | ErrorCode EMPLOYEE_CONTRACT_DATE_INVALID = new ErrorCode(1_030_000_014, "å忥æä¸åæ³ï¼å¼å§æ¥æä¸è½æäºç»ææ¥æ"); |
| | | ErrorCode EMPLOYEE_CONTRACT_DATE_OVERLAP = new ErrorCode(1_030_000_015, "该å工卿鿶鴿®µå·²å卿ªè§£é¤/æªç»æ¢çååï¼è¯·å
è§£é¤/ç»æ¢ååç¾è®¢"); |
| | | |
| | | // ========== 请åç³è¯· 1-030-001-000 ========== |
| | | ErrorCode LEAVE_APPLICATION_NOT_EXISTS = new ErrorCode(1_030_001_000, "请åç³è¯·ä¸åå¨"); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.controller.admin.employee; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractPageReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractTerminateReqVO; |
| | | import cn.iocoder.yudao.module.hrm.service.employee.HrmEmployeeContractService; |
| | | import io.swagger.v3.oas.annotations.Operation; |
| | | import io.swagger.v3.oas.annotations.Parameter; |
| | | import io.swagger.v3.oas.annotations.tags.Tag; |
| | | import jakarta.annotation.Resource; |
| | | import jakarta.validation.Valid; |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| | | |
| | | @Tag(name = "管çåå° - HRM åå·¥åå") |
| | | @RestController |
| | | @RequestMapping("/hrm/employee-contract") |
| | | @Validated |
| | | public class HrmEmployeeContractController { |
| | | |
| | | @Resource |
| | | private HrmEmployeeContractService contractService; |
| | | |
| | | @PostMapping("/create") |
| | | @Operation(summary = "å建åå·¥åå") |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee-contract:create')") |
| | | public CommonResult<Long> createContract(@Valid @RequestBody HrmEmployeeContractSaveReqVO createReqVO) { |
| | | return success(contractService.createContract(createReqVO)); |
| | | } |
| | | |
| | | @PutMapping("/update") |
| | | @Operation(summary = "æ´æ°åå·¥åå") |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee-contract:update')") |
| | | public CommonResult<Boolean> updateContract(@Valid @RequestBody HrmEmployeeContractSaveReqVO updateReqVO) { |
| | | contractService.updateContract(updateReqVO); |
| | | return success(true); |
| | | } |
| | | |
| | | @PutMapping("/terminate") |
| | | @Operation(summary = "è§£é¤/ç»æ¢åå·¥åå") |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee-contract:update')") |
| | | public CommonResult<Boolean> terminateContract(@Valid @RequestBody HrmEmployeeContractTerminateReqVO reqVO) { |
| | | contractService.terminateContract(reqVO); |
| | | return success(true); |
| | | } |
| | | |
| | | @DeleteMapping("/delete") |
| | | @Operation(summary = "å é¤åå·¥åå") |
| | | @Parameter(name = "id", description = "ååID", required = true) |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee-contract:delete')") |
| | | public CommonResult<Boolean> deleteContract(@RequestParam("id") Long id) { |
| | | contractService.deleteContract(id); |
| | | return success(true); |
| | | } |
| | | |
| | | @GetMapping("/get") |
| | | @Operation(summary = "è·å¾åå·¥åå详æ
") |
| | | @Parameter(name = "id", description = "ååID", required = true) |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee-contract:query')") |
| | | public CommonResult<HrmEmployeeContractRespVO> getContract(@RequestParam("id") Long id) { |
| | | return success(contractService.getContract(id)); |
| | | } |
| | | |
| | | @GetMapping("/page") |
| | | @Operation(summary = "è·å¾åå·¥ååå页") |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee-contract:query')") |
| | | public CommonResult<PageResult<HrmEmployeeContractRespVO>> getContractPage(@Valid HrmEmployeeContractPageReqVO pageReqVO) { |
| | | return success(contractService.getContractPage(pageReqVO)); |
| | | } |
| | | |
| | | @GetMapping("/expiring-count") |
| | | @Operation(summary = "è·å¾å³å°å°æååæ°é", description = "ä¾ HRM å¾
å/é¦é¡µæé使ç¨") |
| | | @PreAuthorize("@ss.hasPermission('hrm:employee-contract:query')") |
| | | public CommonResult<Long> getExpiringCount() { |
| | | return success(contractService.getExpiringCount()); |
| | | } |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | @Schema(description = "管çåå° - åå·¥ååå页 Request VO") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | public class HrmEmployeeContractPageReqVO extends PageParam { |
| | | |
| | | /** |
| | | * ååç¶æ - å¾
çæ |
| | | */ |
| | | public static final Integer STATUS_PENDING = 1; |
| | | /** |
| | | * ååç¶æ - çæä¸ |
| | | */ |
| | | public static final Integer STATUS_ACTIVE = 2; |
| | | /** |
| | | * ååç¶æ - å³å°å°æ |
| | | */ |
| | | public static final Integer STATUS_EXPIRING_SOON = 3; |
| | | /** |
| | | * ååç¶æ - 已尿 |
| | | */ |
| | | public static final Integer STATUS_EXPIRED = 4; |
| | | /** |
| | | * ååç¶æ - å·²è§£é¤ |
| | | */ |
| | | public static final Integer STATUS_CANCELED = 5; |
| | | /** |
| | | * ååç¶æ - å·²ç»æ¢ |
| | | */ |
| | | public static final Integer STATUS_TERMINATED = 6; |
| | | |
| | | /** |
| | | * å°æç±»å - å³å°å°æ |
| | | */ |
| | | public static final Integer EXPIRY_TYPE_ABOUT_TO_EXPIRE = 1; |
| | | /** |
| | | * å°æç±»å - 已尿 |
| | | */ |
| | | public static final Integer EXPIRY_TYPE_EXPIRED = 2; |
| | | |
| | | @Schema(description = "ååç¼å·", example = "HT202608100001") |
| | | private String contractNo; |
| | | |
| | | @Schema(description = "åå·¥ID", example = "1") |
| | | private Long employeeId; |
| | | |
| | | @Schema(description = "ååç±»å", example = "1") |
| | | private Integer contractType; |
| | | |
| | | @Schema(description = "ååæéç±»å", example = "1") |
| | | private Integer contractTermType; |
| | | |
| | | @Schema(description = "è§£é¤/ç»æ¢ç¶æï¼0-æ£å¸¸ 1-å·²è§£é¤ 2-å·²ç»æ¢", example = "0") |
| | | private Integer terminateStatus; |
| | | |
| | | @Schema(description = "ååç¶æï¼1-å¾
çæ 2-çæä¸ 3-å³å°å°æ 4-已尿 5-å·²è§£é¤ 6-å·²ç»æ¢ï¼å¨æè®¡ç®ï¼", example = "3") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "å°æç±»åï¼1-å³å°å°æ 2-已尿ï¼å¾
åæéç¨ï¼", example = "1") |
| | | private Integer expiryType; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo; |
| | | |
| | | import cn.iocoder.yudao.module.system.api.storage.dto.StorageBlobRespDTO; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - åå·¥åå Response VO") |
| | | @Data |
| | | public class HrmEmployeeContractRespVO { |
| | | |
| | | @Schema(description = "ååID", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "ç»ç¾ä¸ä¸ä»½ååID", example = "1") |
| | | private Long parentId; |
| | | |
| | | @Schema(description = "ç»ç¾ä¸ä¸ä»½ååç¼å·", example = "HT202608100001") |
| | | private String parentNo; |
| | | |
| | | @Schema(description = "æ¯å¦å½åçæåå", example = "true") |
| | | private Boolean isCurrent; |
| | | |
| | | @Schema(description = "ååç¼å·", example = "HT202608100001") |
| | | private String contractNo; |
| | | |
| | | @Schema(description = "åå·¥ID", example = "1") |
| | | private Long employeeId; |
| | | |
| | | @Schema(description = "åå·¥å§å", example = "å¼ ä¸") |
| | | private String employeeName; |
| | | |
| | | @Schema(description = "å工工å·", example = "EMP202401010001") |
| | | private String employeeNo; |
| | | |
| | | @Schema(description = "é¨é¨åç§°", example = "ç åé¨") |
| | | private String deptName; |
| | | |
| | | @Schema(description = "ååç±»å", example = "1") |
| | | private Integer contractType; |
| | | |
| | | @Schema(description = "ååæéç±»å", example = "1") |
| | | private Integer contractTermType; |
| | | |
| | | @Schema(description = "ç¾çº¦ä¸»ä½ï¼å
¬å¸å
¨ç§°ï¼", example = "åéXXç§ææéå
¬å¸") |
| | | private String signCompany; |
| | | |
| | | @Schema(description = "ç¾è®¢æ¥æ") |
| | | private LocalDate signDate; |
| | | |
| | | @Schema(description = "ååå¼å§æ¥æ") |
| | | private LocalDate startDate; |
| | | |
| | | @Schema(description = "ååç»ææ¥æï¼æ åºå®æéå¯ä¸ºç©ºï¼") |
| | | private LocalDate endDate; |
| | | |
| | | @Schema(description = "è¯ç¨æå¼å§æ¥æ") |
| | | private LocalDate probationStartDate; |
| | | |
| | | @Schema(description = "è¯ç¨æç»ææ¥æ") |
| | | private LocalDate probationEndDate; |
| | | |
| | | @Schema(description = "è¯ç¨æå·¥èµ", example = "8000.00") |
| | | private BigDecimal probationSalary; |
| | | |
| | | @Schema(description = "转æ£å·¥èµ", example = "10000.00") |
| | | private BigDecimal regularSalary; |
| | | |
| | | @Schema(description = "ååç¶æï¼1-å¾
çæ 2-çæä¸ 3-å³å°å°æ 4-已尿 5-å·²è§£é¤ 6-å·²ç»æ¢ï¼å¨æè®¡ç®ï¼", example = "2") |
| | | private Integer status; |
| | | |
| | | @Schema(description = "è§£é¤/ç»æ¢ç¶æï¼0-æ£å¸¸ 1-å·²è§£é¤ 2-å·²ç»æ¢", example = "0") |
| | | private Integer terminateStatus; |
| | | |
| | | @Schema(description = "è§£é¤/ç»æ¢æ¥æ") |
| | | private LocalDate terminateDate; |
| | | |
| | | @Schema(description = "è§£é¤/ç»æ¢åå ", example = "ååå°æä¸ç»ç¾") |
| | | private String terminateReason; |
| | | |
| | | @Schema(description = "夿³¨", example = "ç¬¬ä¸æ¬¡ç»ç¾") |
| | | private String remark; |
| | | |
| | | @Schema(description = "éä»¶å表") |
| | | private List<StorageBlobRespDTO> attachmentList; |
| | | |
| | | @Schema(description = "å建æ¶é´") |
| | | private LocalDateTime createTime; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.util.List; |
| | | |
| | | @Schema(description = "管çåå° - åå·¥ååæ°å¢/ä¿®æ¹ Request VO") |
| | | @Data |
| | | public class HrmEmployeeContractSaveReqVO { |
| | | |
| | | @Schema(description = "ååID", example = "1") |
| | | private Long id; |
| | | |
| | | @Schema(description = "ç»ç¾ä¸ä¸ä»½ååIDï¼ç»ç¾æ¶æå®ï¼ä¸è¬ä¸ä¼ ï¼å端èªå¨å
³è该åå·¥æè¿ä¸ä»½ååï¼", example = "1") |
| | | private Long parentId; |
| | | |
| | | @Schema(description = "åå·¥ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "åå·¥ä¸è½ä¸ºç©º") |
| | | private Long employeeId; |
| | | |
| | | @Schema(description = "ååç±»å", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "ååç±»åä¸è½ä¸ºç©º") |
| | | private Integer contractType; |
| | | |
| | | @Schema(description = "ååæéç±»å", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "ååæéç±»åä¸è½ä¸ºç©º") |
| | | private Integer contractTermType; |
| | | |
| | | @Schema(description = "ç¾çº¦ä¸»ä½ï¼å
¬å¸å
¨ç§°ï¼", example = "åéXXç§ææéå
¬å¸") |
| | | private String signCompany; |
| | | |
| | | @Schema(description = "ç¾è®¢æ¥æ") |
| | | private LocalDate signDate; |
| | | |
| | | @Schema(description = "ååå¼å§æ¥æ", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @NotNull(message = "ååå¼å§æ¥æä¸è½ä¸ºç©º") |
| | | private LocalDate startDate; |
| | | |
| | | @Schema(description = "ååç»ææ¥æï¼æ åºå®æéå¯ä¸ºç©ºï¼") |
| | | private LocalDate endDate; |
| | | |
| | | @Schema(description = "è¯ç¨æå¼å§æ¥æ") |
| | | private LocalDate probationStartDate; |
| | | |
| | | @Schema(description = "è¯ç¨æç»ææ¥æ") |
| | | private LocalDate probationEndDate; |
| | | |
| | | @Schema(description = "è¯ç¨æå·¥èµ", example = "8000.00") |
| | | private BigDecimal probationSalary; |
| | | |
| | | @Schema(description = "转æ£å·¥èµ", example = "10000.00") |
| | | private BigDecimal regularSalary; |
| | | |
| | | @Schema(description = "è§£é¤/ç»æ¢ç¶æï¼0-æ£å¸¸ 1-å·²è§£é¤ 2-å·²ç»æ¢", example = "0") |
| | | private Integer terminateStatus; |
| | | |
| | | @Schema(description = "è§£é¤/ç»æ¢æ¥æ") |
| | | private LocalDate terminateDate; |
| | | |
| | | @Schema(description = "è§£é¤/ç»æ¢åå ", example = "ååå°æä¸ç»ç¾") |
| | | private String terminateReason; |
| | | |
| | | @Schema(description = "夿³¨", example = "ç¬¬ä¸æ¬¡ç»ç¾") |
| | | private String remark; |
| | | |
| | | @Schema(description = "éä»¶ blobId å表", example = "[1, 2]") |
| | | private List<Long> blobIds; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.controller.admin.employee.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import jakarta.validation.constraints.NotNull; |
| | | import lombok.Data; |
| | | |
| | | import java.time.LocalDate; |
| | | |
| | | @Schema(description = "管çåå° - åå·¥ååè§£é¤/ç»æ¢ Request VO") |
| | | @Data |
| | | public class HrmEmployeeContractTerminateReqVO { |
| | | |
| | | @Schema(description = "ååID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "ååIDä¸è½ä¸ºç©º") |
| | | private Long id; |
| | | |
| | | @Schema(description = "è§£é¤/ç»æ¢ç¶æï¼1-å·²è§£é¤ 2-å·²ç»æ¢", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") |
| | | @NotNull(message = "è§£é¤/ç»æ¢ç¶æä¸è½ä¸ºç©º") |
| | | private Integer terminateStatus; |
| | | |
| | | @Schema(description = "è§£é¤/ç»æ¢æ¥æ", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | @NotNull(message = "è§£é¤/ç»æ¢æ¥æä¸è½ä¸ºç©º") |
| | | private LocalDate terminateDate; |
| | | |
| | | @Schema(description = "è§£é¤/ç»æ¢åå ", requiredMode = Schema.RequiredMode.REQUIRED, example = "ååä¸è´è§£é¤") |
| | | private String terminateReason; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.dal.dataobject.employee; |
| | | |
| | | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| | | import com.baomidou.mybatisplus.annotation.KeySequence; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import lombok.*; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | |
| | | /** |
| | | * åå·¥åå DO |
| | | * |
| | | * è®°å½åå·¥ä¸å
¬å¸ç¾è®¢çå³å¨åå/ç»ç¾ååçï¼ä¸ä¸ªå工坿å¤ä»½ååï¼åå²åå + å½åååï¼ |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @TableName("hrm_employee_contract") |
| | | @KeySequence("hrm_employee_contract_seq") |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = true) |
| | | @ToString(callSuper = true) |
| | | @Builder |
| | | @NoArgsConstructor |
| | | @AllArgsConstructor |
| | | public class HrmEmployeeContractDO extends BaseDO { |
| | | |
| | | /** |
| | | * ååID |
| | | */ |
| | | @TableId |
| | | private Long id; |
| | | |
| | | /** |
| | | * ç»ç¾ä¸ä¸ä»½ååID |
| | | * |
| | | * ä¸ä¸ªåå·¥å¯ç¾è®¢å¤æ¬¡ååï¼éè¿è¯¥å段形æç»ç¾é¾ï¼ä¾¿äºè¿½æº¯åååå² |
| | | */ |
| | | private Long parentId; |
| | | |
| | | /** |
| | | * æ¯å¦å½åçæåå |
| | | * |
| | | * åå·¥ææ°ç¾è®¢çãæªè§£é¤/æªç»æ¢çååæ è®°ä¸ºå½åååï¼åä¸åå·¥ä»
ä¸ä»½ |
| | | */ |
| | | private Boolean isCurrent; |
| | | |
| | | /** |
| | | * ååç¼å· |
| | | * |
| | | * èªå¨çæï¼HT + yyyyMMdd + 4ä½èªå¢ |
| | | */ |
| | | private String contractNo; |
| | | |
| | | /** |
| | | * åå·¥ID |
| | | * |
| | | * å
³è HrmEmployeeDO#getId() |
| | | */ |
| | | private Long employeeId; |
| | | |
| | | /** |
| | | * ååç±»å |
| | | * |
| | | * åå
¸ hrm_contract_typeï¼1-å³å¨åå 2-å³å¡åå 3-å®ä¹ åè®® 4-å³å¡æ´¾é£ 5-å
¶ä» |
| | | */ |
| | | private Integer contractType; |
| | | |
| | | /** |
| | | * ååæéç±»å |
| | | * |
| | | * åå
¸ hrm_contract_term_typeï¼1-åºå®æé 2-æ åºå®æé 3-以宿ä¸å®å·¥ä½ä»»å¡ä¸ºæé |
| | | */ |
| | | private Integer contractTermType; |
| | | |
| | | /** |
| | | * ç¾çº¦ä¸»ä½ï¼å
¬å¸å
¨ç§°ï¼ |
| | | */ |
| | | private String signCompany; |
| | | |
| | | /** |
| | | * ç¾è®¢æ¥æ |
| | | */ |
| | | private LocalDate signDate; |
| | | |
| | | /** |
| | | * ååå¼å§æ¥æ |
| | | */ |
| | | private LocalDate startDate; |
| | | |
| | | /** |
| | | * ååç»ææ¥æ |
| | | * |
| | | * æ åºå®æéååå¯ä¸ºç©º |
| | | */ |
| | | private LocalDate endDate; |
| | | |
| | | /** |
| | | * è¯ç¨æå¼å§æ¥æ |
| | | */ |
| | | private LocalDate probationStartDate; |
| | | |
| | | /** |
| | | * è¯ç¨æç»ææ¥æ |
| | | */ |
| | | private LocalDate probationEndDate; |
| | | |
| | | /** |
| | | * è¯ç¨æå·¥èµ |
| | | */ |
| | | private BigDecimal probationSalary; |
| | | |
| | | /** |
| | | * 转æ£å·¥èµ |
| | | */ |
| | | private BigDecimal regularSalary; |
| | | |
| | | /** |
| | | * è§£é¤/ç»æ¢ç¶æ |
| | | * |
| | | * 0-æ£å¸¸ 1-å·²è§£é¤ 2-å·²ç»æ¢ |
| | | */ |
| | | private Integer terminateStatus; |
| | | |
| | | /** |
| | | * è§£é¤/ç»æ¢æ¥æ |
| | | */ |
| | | private LocalDate terminateDate; |
| | | |
| | | /** |
| | | * è§£é¤/ç»æ¢åå |
| | | */ |
| | | private String terminateReason; |
| | | |
| | | /** |
| | | * 夿³¨ |
| | | */ |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.dal.mysql.employee; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractPageReqVO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeContractDO; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.List; |
| | | |
| | | /** |
| | | * åå·¥åå Mapper |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Mapper |
| | | public interface HrmEmployeeContractMapper extends BaseMapperX<HrmEmployeeContractDO> { |
| | | |
| | | /** |
| | | * å°ææéå¤©æ° |
| | | */ |
| | | int REMIND_DAYS = 30; |
| | | |
| | | /** |
| | | * ååç¶æï¼å¾
çæ |
| | | */ |
| | | int STATUS_PENDING = 1; |
| | | /** |
| | | * ååç¶æï¼çæä¸ |
| | | */ |
| | | int STATUS_ACTIVE = 2; |
| | | /** |
| | | * ååç¶æï¼å³å°å°æ |
| | | */ |
| | | int STATUS_EXPIRING_SOON = 3; |
| | | /** |
| | | * ååç¶æï¼å·²å°æ |
| | | */ |
| | | int STATUS_EXPIRED = 4; |
| | | |
| | | /** |
| | | * è§£é¤ç»æ¢ç¶æï¼æ£å¸¸ |
| | | */ |
| | | int TERMINATE_NORMAL = 0; |
| | | /** |
| | | * è§£é¤ç»æ¢ç¶æï¼å·²è§£é¤ |
| | | */ |
| | | int TERMINATE_CANCELED = 1; |
| | | /** |
| | | * è§£é¤ç»æ¢ç¶æï¼å·²ç»æ¢ |
| | | */ |
| | | int TERMINATE_TERMINATED = 2; |
| | | |
| | | default PageResult<HrmEmployeeContractDO> selectPage(HrmEmployeeContractPageReqVO reqVO, LocalDate today) { |
| | | LambdaQueryWrapperX<HrmEmployeeContractDO> query = new LambdaQueryWrapperX<HrmEmployeeContractDO>() |
| | | .likeIfPresent(HrmEmployeeContractDO::getContractNo, reqVO.getContractNo()) |
| | | .eqIfPresent(HrmEmployeeContractDO::getEmployeeId, reqVO.getEmployeeId()) |
| | | .eqIfPresent(HrmEmployeeContractDO::getContractType, reqVO.getContractType()) |
| | | .eqIfPresent(HrmEmployeeContractDO::getContractTermType, reqVO.getContractTermType()) |
| | | .eqIfPresent(HrmEmployeeContractDO::getTerminateStatus, reqVO.getTerminateStatus()) |
| | | .orderByDesc(HrmEmployeeContractDO::getId); |
| | | // å¨æç¶æçéï¼ä¸ terminateStatus äºæ¥ï¼ä¼å
æç¶æçéï¼ |
| | | if (reqVO.getStatus() != null) { |
| | | appendStatusCondition(query, reqVO.getStatus(), today); |
| | | } else if (reqVO.getExpiryType() != null) { |
| | | appendExpiryCondition(query, reqVO.getExpiryType(), today); |
| | | } |
| | | return selectPage(reqVO, query); |
| | | } |
| | | |
| | | /** |
| | | * æ¼æ¥å¨æç¶æç鿡件 |
| | | * |
| | | * @param status ååç¶æï¼1-å¾
çæ 2-çæä¸ 3-å³å°å°æ 4-已尿 5-å·²è§£é¤ 6-å·²ç»æ¢ |
| | | */ |
| | | default void appendStatusCondition(LambdaQueryWrapperX<HrmEmployeeContractDO> query, Integer status, LocalDate today) { |
| | | switch (status) { |
| | | case STATUS_PENDING: // å¾
çæ |
| | | query.eq(HrmEmployeeContractDO::getTerminateStatus, TERMINATE_NORMAL) |
| | | .gt(HrmEmployeeContractDO::getStartDate, today); |
| | | break; |
| | | case STATUS_ACTIVE: // çæä¸ï¼æªè§£é¤/ç»æ¢ï¼å·²å¼å§ï¼ä¸æªç»æï¼å«æ åºå®æéï¼ |
| | | query.eq(HrmEmployeeContractDO::getTerminateStatus, TERMINATE_NORMAL) |
| | | .le(HrmEmployeeContractDO::getStartDate, today) |
| | | .and(w -> w.isNull(HrmEmployeeContractDO::getEndDate) |
| | | .or().ge(HrmEmployeeContractDO::getEndDate, today)); |
| | | break; |
| | | case STATUS_EXPIRING_SOON: // å³å°å°æ |
| | | query.eq(HrmEmployeeContractDO::getTerminateStatus, TERMINATE_NORMAL) |
| | | .le(HrmEmployeeContractDO::getStartDate, today) |
| | | .ge(HrmEmployeeContractDO::getEndDate, today) |
| | | .le(HrmEmployeeContractDO::getEndDate, today.plusDays(REMIND_DAYS)); |
| | | break; |
| | | case STATUS_EXPIRED: // 已尿 |
| | | query.eq(HrmEmployeeContractDO::getTerminateStatus, TERMINATE_NORMAL) |
| | | .isNotNull(HrmEmployeeContractDO::getEndDate) |
| | | .lt(HrmEmployeeContractDO::getEndDate, today); |
| | | break; |
| | | case 5: // å·²è§£é¤ |
| | | query.eq(HrmEmployeeContractDO::getTerminateStatus, TERMINATE_CANCELED); |
| | | break; |
| | | case 6: // å·²ç»æ¢ |
| | | query.eq(HrmEmployeeContractDO::getTerminateStatus, TERMINATE_TERMINATED); |
| | | break; |
| | | default: |
| | | break; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ¼æ¥å°æç±»åç鿡件ï¼å¾
åæéç¨ï¼ |
| | | * |
| | | * @param expiryType å°æç±»åï¼1-å³å°å°æ 2-已尿 |
| | | */ |
| | | default void appendExpiryCondition(LambdaQueryWrapperX<HrmEmployeeContractDO> query, Integer expiryType, LocalDate today) { |
| | | if (expiryType == 1) { // å³å°å°æ |
| | | query.eq(HrmEmployeeContractDO::getTerminateStatus, TERMINATE_NORMAL) |
| | | .le(HrmEmployeeContractDO::getStartDate, today) |
| | | .between(HrmEmployeeContractDO::getEndDate, today, today.plusDays(REMIND_DAYS)); |
| | | } else if (expiryType == 2) { // 已尿 |
| | | query.eq(HrmEmployeeContractDO::getTerminateStatus, TERMINATE_NORMAL) |
| | | .isNotNull(HrmEmployeeContractDO::getEndDate) |
| | | .lt(HrmEmployeeContractDO::getEndDate, today); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * ç»è®¡å³å°å°æçååæ°éï¼ä¾å¾
å/é¦é¡µæéï¼ |
| | | */ |
| | | default Long selectCountByExpiring(LocalDate today) { |
| | | return selectCount(new LambdaQueryWrapperX<HrmEmployeeContractDO>() |
| | | .eq(HrmEmployeeContractDO::getTerminateStatus, TERMINATE_NORMAL) |
| | | .le(HrmEmployeeContractDO::getStartDate, today) |
| | | .between(HrmEmployeeContractDO::getEndDate, today, today.plusDays(REMIND_DAYS))); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢åä¸åå·¥æ¶é´éå çæªè§£é¤/æªç»æ¢åå |
| | | * |
| | | * @param employeeId åå·¥ID |
| | | * @param startDate æ°ååå¼å§æ¥æ |
| | | * @param endDate æ°ååç»ææ¥æï¼æ åºå®æé为 nullï¼è§ä¸ºæ éé¿ï¼ |
| | | * @param excludeId éè¦æé¤çååIDï¼æ´æ°æ¶ä¼ èªèº« idï¼å建æ¶ä¼ nullï¼ |
| | | * @return éå çååå表 |
| | | */ |
| | | default List<HrmEmployeeContractDO> selectOverlapping(Long employeeId, LocalDate startDate, |
| | | LocalDate endDate, Long excludeId) { |
| | | LambdaQueryWrapperX<HrmEmployeeContractDO> query = new LambdaQueryWrapperX<HrmEmployeeContractDO>(); |
| | | query.eq(HrmEmployeeContractDO::getEmployeeId, employeeId); |
| | | query.eq(HrmEmployeeContractDO::getTerminateStatus, TERMINATE_NORMAL); |
| | | if (excludeId != null) { |
| | | query.ne(HrmEmployeeContractDO::getId, excludeId); |
| | | } |
| | | if (endDate != null) { |
| | | // æ°ååæç»ææ¥æï¼ä¸å·²åå¨ååç [startDate, endDate] åºé´åå¨äº¤éå³å²çª |
| | | query.lt(HrmEmployeeContractDO::getStartDate, endDate); |
| | | query.and(w -> w.isNull(HrmEmployeeContractDO::getEndDate) |
| | | .or().gt(HrmEmployeeContractDO::getEndDate, startDate)); |
| | | } |
| | | // æ°ååæ åºå®æéï¼endDate 为 nullï¼ï¼åå·¥åå¨ä»»ææªè§£é¤/ç»æ¢ååå³å²çª |
| | | return selectList(query); |
| | | } |
| | | |
| | | /** |
| | | * æ¸
é¤è¯¥åå·¥å
¨é¨ååç"å½ååå"æ è®° |
| | | * |
| | | * @param employeeId åå·¥ID |
| | | */ |
| | | default void clearCurrentByEmployee(Long employeeId) { |
| | | update(null, new LambdaUpdateWrapper<HrmEmployeeContractDO>() |
| | | .eq(HrmEmployeeContractDO::getEmployeeId, employeeId) |
| | | .set(HrmEmployeeContractDO::getIsCurrent, false)); |
| | | } |
| | | |
| | | /** |
| | | * æ¥è¯¢åå·¥çå
¨é¨ååï¼æå建æ¶é´ååºï¼ |
| | | * |
| | | * @param employeeId åå·¥ID |
| | | * @return ååå表 |
| | | */ |
| | | default List<HrmEmployeeContractDO> selectListByEmployee(Long employeeId) { |
| | | return selectList(new LambdaQueryWrapperX<HrmEmployeeContractDO>() |
| | | .eq(HrmEmployeeContractDO::getEmployeeId, employeeId) |
| | | .orderByDesc(HrmEmployeeContractDO::getId)); |
| | | } |
| | | |
| | | } |
| | |
| | | */ |
| | | public static final String RESIGNATION_APPLICATION_NO_PREFIX = "RA"; |
| | | |
| | | /** |
| | | * åå·¥ååç¼å·åç¼ |
| | | */ |
| | | public static final String EMPLOYEE_CONTRACT_NO_PREFIX = "HT"; |
| | | |
| | | @Resource |
| | | private StringRedisTemplate stringRedisTemplate; |
| | | |
| | |
| | | } |
| | | |
| | | /** |
| | | * çæåå·¥ååç¼å·ï¼æ ¼å¼ï¼HT + yyyyMMdd + 4ä½èªå¢ |
| | | * ä¾å¦ï¼HT202401010001 |
| | | * |
| | | * @return åå·¥ååç¼å· |
| | | */ |
| | | public String generateEmployeeContractNo() { |
| | | return generate(EMPLOYEE_CONTRACT_NO_PREFIX, 4); |
| | | } |
| | | |
| | | /** |
| | | * çæèªé
¬æ ¸ç®ç¼å·ï¼æ ¼å¼ï¼SC + yyyyMMdd + 4ä½èªå¢ |
| | | * ä¾å¦ï¼SC202401010001 |
| | | * |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.service.employee; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractPageReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractTerminateReqVO; |
| | | |
| | | /** |
| | | * åå·¥åå Service æ¥å£ |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | public interface HrmEmployeeContractService { |
| | | |
| | | /** |
| | | * å建åå·¥åå |
| | | * |
| | | * @param createReqVO åå»ºè¯·æ± |
| | | * @return ååID |
| | | */ |
| | | Long createContract(HrmEmployeeContractSaveReqVO createReqVO); |
| | | |
| | | /** |
| | | * æ´æ°åå·¥åå |
| | | * |
| | | * @param updateReqVO æ´æ°è¯·æ± |
| | | */ |
| | | void updateContract(HrmEmployeeContractSaveReqVO updateReqVO); |
| | | |
| | | /** |
| | | * å é¤åå·¥åå |
| | | * |
| | | * @param id ååID |
| | | */ |
| | | void deleteContract(Long id); |
| | | |
| | | /** |
| | | * è§£é¤/ç»æ¢åå·¥åå |
| | | * |
| | | * @param reqVO è§£é¤/ç»æ¢è¯·æ± |
| | | */ |
| | | void terminateContract(HrmEmployeeContractTerminateReqVO reqVO); |
| | | |
| | | /** |
| | | * è·å¾åå·¥åå详æ
|
| | | * |
| | | * @param id ååID |
| | | * @return åå详æ
ï¼å«éä»¶ãå工信æ¯ãå¨æç¶æï¼ |
| | | */ |
| | | HrmEmployeeContractRespVO getContract(Long id); |
| | | |
| | | /** |
| | | * è·å¾åå·¥ååå页 |
| | | * |
| | | * @param pageReqVO åé¡µè¯·æ± |
| | | * @return åååé¡µç»æï¼å«å工信æ¯ãå¨æç¶æï¼ |
| | | */ |
| | | PageResult<HrmEmployeeContractRespVO> getContractPage(HrmEmployeeContractPageReqVO pageReqVO); |
| | | |
| | | /** |
| | | * ç»è®¡å³å°å°æçååæ°éï¼ä¾å¾
å/é¦é¡µæéï¼ |
| | | * |
| | | * @return å³å°å°æååæ°é |
| | | */ |
| | | Long getExpiringCount(); |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.hrm.service.employee; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractPageReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractRespVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractSaveReqVO; |
| | | import cn.iocoder.yudao.module.hrm.controller.admin.employee.vo.HrmEmployeeContractTerminateReqVO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeContractDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.dataobject.employee.HrmEmployeeDO; |
| | | import cn.iocoder.yudao.module.hrm.dal.mysql.employee.HrmEmployeeContractMapper; |
| | | import cn.iocoder.yudao.module.hrm.dal.redis.HrmNoRedisDAO; |
| | | import cn.iocoder.yudao.module.system.api.dept.DeptApi; |
| | | import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.Set; |
| | | import java.util.stream.Collectors; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | | import static cn.iocoder.yudao.module.hrm.enums.ErrorCodeConstants.*; |
| | | |
| | | /** |
| | | * åå·¥åå Service å®ç°ç±» |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Service |
| | | @Validated |
| | | public class HrmEmployeeContractServiceImpl implements HrmEmployeeContractService { |
| | | |
| | | /** |
| | | * éä»¶ä¸å¡è®°å½ç±»å |
| | | */ |
| | | private static final String RECORD_TYPE = "hrm_employee_contract"; |
| | | |
| | | @Resource |
| | | private HrmEmployeeContractMapper contractMapper; |
| | | @Resource |
| | | private HrmEmployeeService employeeService; |
| | | @Resource |
| | | private DeptApi deptApi; |
| | | @Resource |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | @Resource |
| | | private HrmNoRedisDAO noRedisDAO; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public Long createContract(HrmEmployeeContractSaveReqVO createReqVO) { |
| | | // 1. æ ¡éªåå·¥åå¨ |
| | | employeeService.validateEmployee(createReqVO.getEmployeeId()); |
| | | // 2. æ ¡éªå忥æ |
| | | validateContractDate(createReqVO); |
| | | // 3. æ ¡éªåä¸åå·¥æ¶é´éå çåå |
| | | validateNoOverlap(createReqVO.getEmployeeId(), createReqVO.getStartDate(), |
| | | createReqVO.getEndDate(), null); |
| | | // 4. çæååç¼å· |
| | | String contractNo = noRedisDAO.generateEmployeeContractNo(); |
| | | // 5. æ¥è¯¢è¯¥å工已æååï¼ç¡®å®ç»ç¾å
³ç³» |
| | | List<HrmEmployeeContractDO> existing = contractMapper.selectListByEmployee(createReqVO.getEmployeeId()); |
| | | // 6. æå
¥ååè®°å½ï¼æ°ç¾ååå³åå·¥å½åååï¼ |
| | | HrmEmployeeContractDO contract = BeanUtils.toBean(createReqVO, HrmEmployeeContractDO.class); |
| | | contract.setContractNo(contractNo); |
| | | contract.setIsCurrent(true); |
| | | // ç»ç¾ï¼ä¼å
使ç¨å端æå®ç parentIdï¼ç¨æ·ç¹å»çé£ä»½ååï¼ï¼å¦åèªå¨å
³èåå·¥æè¿ä¸ä»½åå |
| | | if (createReqVO.getParentId() == null) { |
| | | contract.setParentId(CollUtil.isEmpty(existing) ? null : existing.get(0).getId()); |
| | | } |
| | | if (contract.getTerminateStatus() == null) { |
| | | contract.setTerminateStatus(HrmEmployeeContractMapper.TERMINATE_NORMAL); |
| | | } |
| | | // 7. å
æ¸
餿§ååç"å½ååå"æ è®°ï¼åæå
¥æ°åå |
| | | contractMapper.clearCurrentByEmployee(createReqVO.getEmployeeId()); |
| | | contractMapper.insert(contract); |
| | | // 8. ç»å®éä»¶ |
| | | if (CollUtil.isNotEmpty(createReqVO.getBlobIds())) { |
| | | storageAttachmentApi.bindAttachments("file", RECORD_TYPE, contract.getId(), createReqVO.getBlobIds()); |
| | | } |
| | | return contract.getId(); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateContract(HrmEmployeeContractSaveReqVO updateReqVO) { |
| | | // 1. æ ¡éªåå¨ |
| | | validateContractExists(updateReqVO.getId()); |
| | | // 2. æ ¡éªåå·¥åå¨ |
| | | employeeService.validateEmployee(updateReqVO.getEmployeeId()); |
| | | // 3. æ ¡éªå忥æ |
| | | validateContractDate(updateReqVO); |
| | | // 4. æ ¡éªåä¸åå·¥æ¶é´éå çååï¼æé¤èªèº«ï¼ |
| | | validateNoOverlap(updateReqVO.getEmployeeId(), updateReqVO.getStartDate(), |
| | | updateReqVO.getEndDate(), updateReqVO.getId()); |
| | | // 5. æ´æ°ååè®°å½ |
| | | HrmEmployeeContractDO updateObj = BeanUtils.toBean(updateReqVO, HrmEmployeeContractDO.class); |
| | | contractMapper.updateById(updateObj); |
| | | // 6. æ´æ°éä»¶ |
| | | storageAttachmentApi.updateAttachments("file", RECORD_TYPE, updateReqVO.getId(), updateReqVO.getBlobIds()); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void deleteContract(Long id) { |
| | | // 1. æ ¡éªåå¨ |
| | | validateContractExists(id); |
| | | // 2. å é¤åå |
| | | contractMapper.deleteById(id); |
| | | // 3. å é¤éä»¶ |
| | | storageAttachmentApi.deleteAttachmentsByRecord(RECORD_TYPE, id); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void terminateContract(HrmEmployeeContractTerminateReqVO reqVO) { |
| | | // 1. æ ¡éªåå¨ |
| | | HrmEmployeeContractDO contract = validateContractExists(reqVO.getId()); |
| | | // 2. æ ¡éªè§£é¤/ç»æ¢åæ° |
| | | Integer terminateStatus = reqVO.getTerminateStatus(); |
| | | if (terminateStatus == null |
| | | || (terminateStatus != HrmEmployeeContractMapper.TERMINATE_CANCELED |
| | | && terminateStatus != HrmEmployeeContractMapper.TERMINATE_TERMINATED) |
| | | || reqVO.getTerminateDate() == null || StrUtil.isBlank(reqVO.getTerminateReason())) { |
| | | throw exception(EMPLOYEE_CONTRACT_TERMINATE_REQUIRE_DATE_REASON); |
| | | } |
| | | // 3. 已解é¤/å·²ç»æ¢çååä¸å
许éå¤æä½ |
| | | if (contract.getTerminateStatus() != null |
| | | && contract.getTerminateStatus() != HrmEmployeeContractMapper.TERMINATE_NORMAL) { |
| | | throw exception(EMPLOYEE_CONTRACT_TERMINATE_REQUIRE_DATE_REASON); |
| | | } |
| | | // 4. æ´æ°è§£é¤/ç»æ¢ä¿¡æ¯ |
| | | HrmEmployeeContractDO updateObj = new HrmEmployeeContractDO(); |
| | | updateObj.setId(contract.getId()); |
| | | updateObj.setTerminateStatus(terminateStatus); |
| | | updateObj.setTerminateDate(reqVO.getTerminateDate()); |
| | | updateObj.setTerminateReason(reqVO.getTerminateReason()); |
| | | // 5. è¥è§£é¤/ç»æ¢çæ¯åå·¥å½åååï¼æ¸
é¤å½åæ è®° |
| | | if (Boolean.TRUE.equals(contract.getIsCurrent())) { |
| | | updateObj.setIsCurrent(false); |
| | | } |
| | | contractMapper.updateById(updateObj); |
| | | } |
| | | |
| | | @Override |
| | | public HrmEmployeeContractRespVO getContract(Long id) { |
| | | HrmEmployeeContractDO contract = validateContractExists(id); |
| | | HrmEmployeeContractRespVO respVO = BeanUtils.toBean(contract, HrmEmployeeContractRespVO.class); |
| | | // å¡«å
åå·¥ä¿¡æ¯ |
| | | HrmEmployeeDO employee = employeeService.getEmployee(contract.getEmployeeId()); |
| | | if (employee != null) { |
| | | Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(Collections.singleton(employee.getDeptId())); |
| | | fillEmployeeInfo(respVO, employee, deptMap); |
| | | } |
| | | // å¡«å
ç»ç¾ä¸ä¸ä»½ååç¼å· |
| | | fillParentNo(respVO, contract.getParentId()); |
| | | // å¡«å
éä»¶ |
| | | respVO.setAttachmentList(storageAttachmentApi.listAttachments(RECORD_TYPE, id)); |
| | | // å¨æè®¡ç®ç¶æ |
| | | respVO.setStatus(calculateStatus(contract, LocalDate.now())); |
| | | return respVO; |
| | | } |
| | | |
| | | @Override |
| | | public PageResult<HrmEmployeeContractRespVO> getContractPage(HrmEmployeeContractPageReqVO pageReqVO) { |
| | | LocalDate today = LocalDate.now(); |
| | | PageResult<HrmEmployeeContractDO> pageResult = contractMapper.selectPage(pageReqVO, today); |
| | | if (CollUtil.isEmpty(pageResult.getList())) { |
| | | return PageResult.empty(); |
| | | } |
| | | // æ¹éè·ååå·¥ä¿¡æ¯ |
| | | Set<Long> employeeIds = pageResult.getList().stream() |
| | | .map(HrmEmployeeContractDO::getEmployeeId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Map<Long, HrmEmployeeDO> employeeMap = CollUtil.isEmpty(employeeIds) ? Collections.emptyMap() : |
| | | employeeService.getEmployeeList(employeeIds).stream() |
| | | .collect(Collectors.toMap(HrmEmployeeDO::getId, e -> e)); |
| | | // æ¹éè·åé¨é¨ä¿¡æ¯ |
| | | Set<Long> deptIds = employeeMap.values().stream() |
| | | .map(HrmEmployeeDO::getDeptId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Map<Long, DeptRespDTO> deptMap = CollUtil.isEmpty(deptIds) ? Collections.emptyMap() : deptApi.getDeptMap(deptIds); |
| | | // æ¹éè·åç»ç¾ä¸ä¸ä»½ååä¿¡æ¯ |
| | | Set<Long> parentIds = pageResult.getList().stream() |
| | | .map(HrmEmployeeContractDO::getParentId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | Map<Long, HrmEmployeeContractDO> parentMap = CollUtil.isEmpty(parentIds) ? Collections.emptyMap() : |
| | | contractMapper.selectList(HrmEmployeeContractDO::getId, parentIds).stream() |
| | | .collect(Collectors.toMap(HrmEmployeeContractDO::getId, c -> c)); |
| | | // ç»è£
VO |
| | | List<HrmEmployeeContractRespVO> list = pageResult.getList().stream().map(contract -> { |
| | | HrmEmployeeContractRespVO respVO = BeanUtils.toBean(contract, HrmEmployeeContractRespVO.class); |
| | | fillEmployeeInfo(respVO, employeeMap.get(contract.getEmployeeId()), deptMap); |
| | | fillParentNo(respVO, contract.getParentId(), parentMap); |
| | | respVO.setStatus(calculateStatus(contract, today)); |
| | | return respVO; |
| | | }).collect(Collectors.toList()); |
| | | return new PageResult<>(list, pageResult.getTotal()); |
| | | } |
| | | |
| | | @Override |
| | | public Long getExpiringCount() { |
| | | return contractMapper.selectCountByExpiring(LocalDate.now()); |
| | | } |
| | | |
| | | private HrmEmployeeContractDO validateContractExists(Long id) { |
| | | HrmEmployeeContractDO contract = contractMapper.selectById(id); |
| | | if (contract == null) { |
| | | throw exception(EMPLOYEE_CONTRACT_NOT_EXISTS); |
| | | } |
| | | return contract; |
| | | } |
| | | |
| | | /** |
| | | * æ ¡éªåä¸åå·¥æ¶é´éå çååï¼é¿å
ä¸ä¸ªå工忶åå¨å¤ä»½æªè§£é¤/æªç»æ¢ççæåå |
| | | */ |
| | | private void validateNoOverlap(Long employeeId, LocalDate startDate, LocalDate endDate, Long excludeId) { |
| | | List<HrmEmployeeContractDO> overlaps = contractMapper.selectOverlapping(employeeId, startDate, endDate, excludeId); |
| | | if (CollUtil.isNotEmpty(overlaps)) { |
| | | throw exception(EMPLOYEE_CONTRACT_DATE_OVERLAP); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å¡«å
ç»ç¾ä¸ä¸ä»½ååç¼å· |
| | | */ |
| | | private void fillParentNo(HrmEmployeeContractRespVO respVO, Long parentId) { |
| | | if (parentId != null) { |
| | | HrmEmployeeContractDO parent = contractMapper.selectById(parentId); |
| | | if (parent != null) { |
| | | respVO.setParentNo(parent.getContractNo()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * æ¹éå¡«å
ç»ç¾ä¸ä¸ä»½ååç¼å· |
| | | */ |
| | | private void fillParentNo(HrmEmployeeContractRespVO respVO, Long parentId, Map<Long, HrmEmployeeContractDO> parentMap) { |
| | | if (parentId != null) { |
| | | HrmEmployeeContractDO parent = parentMap.get(parentId); |
| | | if (parent != null) { |
| | | respVO.setParentNo(parent.getContractNo()); |
| | | } |
| | | } |
| | | } |
| | | |
| | | private void validateContractDate(HrmEmployeeContractSaveReqVO reqVO) { |
| | | // éæ åºå®æéååï¼æéç±»å != 2ï¼å¿
须填åç»ææ¥æ |
| | | if (!Integer.valueOf(2).equals(reqVO.getContractTermType()) && reqVO.getEndDate() == null) { |
| | | throw exception(EMPLOYEE_CONTRACT_END_DATE_REQUIRE_FOR_FIXED); |
| | | } |
| | | // å¼å§æ¥æä¸è½æäºç»ææ¥æ |
| | | if (reqVO.getStartDate() != null && reqVO.getEndDate() != null |
| | | && reqVO.getStartDate().isAfter(reqVO.getEndDate())) { |
| | | throw exception(EMPLOYEE_CONTRACT_DATE_INVALID); |
| | | } |
| | | // è¯ç¨æå¼å§æ¥æä¸è½æäºè¯ç¨æç»ææ¥æ |
| | | if (reqVO.getProbationStartDate() != null && reqVO.getProbationEndDate() != null |
| | | && reqVO.getProbationStartDate().isAfter(reqVO.getProbationEndDate())) { |
| | | throw exception(EMPLOYEE_CONTRACT_DATE_INVALID); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * å¨æè®¡ç®ååç¶æ |
| | | * |
| | | * @param contract ååè®°å½ |
| | | * @param today å½åæ¥æ |
| | | * @return ç¶æï¼1-å¾
çæ 2-çæä¸ 3-å³å°å°æ 4-已尿 5-å·²è§£é¤ 6-å·²ç»æ¢ |
| | | */ |
| | | private Integer calculateStatus(HrmEmployeeContractDO contract, LocalDate today) { |
| | | Integer terminateStatus = contract.getTerminateStatus(); |
| | | if (terminateStatus != null && terminateStatus == HrmEmployeeContractMapper.TERMINATE_CANCELED) { |
| | | return HrmEmployeeContractPageReqVO.STATUS_CANCELED; |
| | | } |
| | | if (terminateStatus != null && terminateStatus == HrmEmployeeContractMapper.TERMINATE_TERMINATED) { |
| | | return HrmEmployeeContractPageReqVO.STATUS_TERMINATED; |
| | | } |
| | | LocalDate endDate = contract.getEndDate(); |
| | | if (endDate != null && endDate.isBefore(today)) { |
| | | return HrmEmployeeContractPageReqVO.STATUS_EXPIRED; |
| | | } |
| | | LocalDate startDate = contract.getStartDate(); |
| | | if (startDate != null && startDate.isAfter(today)) { |
| | | return HrmEmployeeContractPageReqVO.STATUS_PENDING; |
| | | } |
| | | if (endDate != null && !endDate.isAfter(today.plusDays(HrmEmployeeContractMapper.REMIND_DAYS))) { |
| | | return HrmEmployeeContractPageReqVO.STATUS_EXPIRING_SOON; |
| | | } |
| | | return HrmEmployeeContractPageReqVO.STATUS_ACTIVE; |
| | | } |
| | | |
| | | private void fillEmployeeInfo(HrmEmployeeContractRespVO respVO, HrmEmployeeDO employee, |
| | | Map<Long, DeptRespDTO> deptMap) { |
| | | if (employee != null) { |
| | | respVO.setEmployeeName(employee.getName()); |
| | | respVO.setEmployeeNo(employee.getEmployeeNo()); |
| | | if (employee.getDeptId() != null && deptMap != null) { |
| | | DeptRespDTO dept = deptMap.get(employee.getDeptId()); |
| | | if (dept != null) { |
| | | respVO.setDeptName(dept.getName()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | // ===== ERP éè´ ===== |
| | | ERP_PURCHASE_REQUEST("erp_purchase_request", "ERP éè´ç³è¯·"), |
| | | ERP_PURCHASE_ORDER("erp_purchase_order", "ERP éè´è®¢å"), |
| | | ERP_PURCHASE_INVOICE("erp_purchase_invoice", "ERP éè´æ¥ç¥¨"), |
| | | |
| | | // ===== ERP éå® ===== |
| | | ERP_SALE_ORDER("erp_sale_order", "ERP éå®è®¢å"), |