feat(erp/mes): 新增采购订单/销售订单/计量器具 Excel 导入,含导入菜单权限 SQL
- 采购订单导入:模板/导入接口、明细分组、供应商按名称匹配、状态可置已审核
- 销售订单导入:模板/导入接口、客户按名称匹配(扩展 CrmCustomerApi.getCustomerSimpleList)
- 计量器具导入:模板/导入接口、部门/责任人按名称匹配、字典转换、编码已删可恢复
- 菜单权限:新增 erp:purchase-order:import、erp:sale-order:import、mes:dv-metering:import、hrm:employee-contract:import
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- éè´è®¢å - 导å
¥æé®æéï¼ç¶èåï¼éè´è®¢å id=2666ï¼ |
| | | -- ä¿®æ£è¯´æï¼åèæ¬åæ» id=602511ï¼ä¸âç产计å-æ°å¢(mes:pro-mps:create)âæè½¦å¯¼è´ INSERT 被跳è¿ã |
| | | -- ç°æ¹ä¸ºå¨æå system_menu æå¤§ id + 1ï¼å¹¶ç¨åéé¢å¤ï¼å¯é夿§è¡ï¼å¹çï¼ã |
| | | SET @perm_exists := (SELECT COUNT(*) FROM system_menu WHERE permission = 'erp:purchase-order:import'); |
| | | SET @import_menu_id := IFNULL((SELECT MAX(id) FROM system_menu), 0) + 1; |
| | | |
| | | -- 1) æ°å¢âéè´è®¢å导å
¥âæé®æéï¼å·²åå¨åä¸æï¼ |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, |
| | | component_name, status, visible, keep_alive, always_show, |
| | | creator, create_time, updater, update_time, deleted) |
| | | SELECT @import_menu_id, 'éè´è®¢å导å
¥', 'erp:purchase-order:import', 3, 8, 2666, '', '#', '', NULL, |
| | | 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL |
| | | WHERE @perm_exists = 0; |
| | | |
| | | -- 2) æäºè¶
级管çåè§è²ï¼role_id=1ï¼è¯¥æé®æéï¼å·²ææåä¸æï¼ |
| | | SET @menu_id := (SELECT id FROM system_menu WHERE permission = 'erp:purchase-order:import'); |
| | | SET @grant_exists := (SELECT COUNT(*) FROM system_role_menu WHERE role_id = 1 AND menu_id = @menu_id); |
| | | |
| | | INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted) |
| | | SELECT 1, @menu_id, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL |
| | | WHERE @menu_id IS NOT NULL AND @grant_exists = 0; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- éå®è®¢å - 导å
¥æé®æéï¼ç¶èåï¼éå®è®¢å id=2638ï¼ |
| | | -- ç¨å¨æ idï¼MAX(id)+1ï¼ï¼é¿å
䏿¢æèåæè½¦ï¼å¯é夿§è¡ï¼å¹çï¼ã |
| | | SET @perm_exists := (SELECT COUNT(*) FROM system_menu WHERE permission = 'erp:sale-order:import'); |
| | | SET @import_menu_id := IFNULL((SELECT MAX(id) FROM system_menu), 0) + 1; |
| | | |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, |
| | | component_name, status, visible, keep_alive, always_show, |
| | | creator, create_time, updater, update_time, deleted) |
| | | SELECT @import_menu_id, 'éå®è®¢å导å
¥', 'erp:sale-order:import', 3, 8, 2638, '', '#', '', NULL, |
| | | 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL |
| | | WHERE @perm_exists = 0; |
| | | |
| | | SET @menu_id := (SELECT id FROM system_menu WHERE permission = 'erp:sale-order:import'); |
| | | SET @grant_exists := (SELECT COUNT(*) FROM system_role_menu WHERE role_id = 1 AND menu_id = @menu_id); |
| | | |
| | | INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted) |
| | | SELECT 1, @menu_id, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL |
| | | WHERE @menu_id IS NOT NULL AND @grant_exists = 0; |
| | |
| | | -- åå·¥åå - 导å
¥/导å
¥æ¨¡æ¿ æé®æé |
| | | -- ç¶èåï¼åå·¥ååï¼parent_id = 1063105ï¼ |
| | | -- åç
§ï¼ååæ°å¢æé® id=1063107ï¼type=3, permission=hrm:employee-contract:createï¼ |
| | | -- åå·¥åå - 导å
¥æé®æéï¼ç¶èåï¼åå·¥åå id=1063105ï¼ |
| | | -- 卿 idï¼MAX(id)+1ï¼ï¼é¿å
䏿¢æèåæè½¦ï¼å¯é夿§è¡ï¼å¹çï¼ã |
| | | SET @perm_exists := (SELECT COUNT(*) FROM system_menu WHERE permission = 'hrm:employee-contract:import'); |
| | | SET @import_menu_id := IFNULL((SELECT MAX(id) FROM system_menu), 0) + 1; |
| | | |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, |
| | | component_name, status, visible, keep_alive, always_show, |
| | | creator, create_time, updater, update_time, deleted) |
| | | VALUES (1063300, 'åå导å
¥', 'hrm:employee-contract:import', 3, 5, 1063105, '', '#', '', NULL, |
| | | 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0); |
| | | SELECT @import_menu_id, 'åå导å
¥', 'hrm:employee-contract:import', 3, 5, 1063105, '', '#', '', NULL, |
| | | 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL |
| | | WHERE @perm_exists = 0; |
| | | |
| | | -- æäºè¶
级管çåè§è²ï¼role_id=1ï¼è¯¥æé®æéï¼å¹çï¼ |
| | | SET @menu_id := (SELECT id FROM system_menu WHERE permission = 'hrm:employee-contract:import'); |
| | | SET @grant_exists := (SELECT COUNT(*) FROM system_role_menu WHERE role_id = 1 AND menu_id = @menu_id); |
| | | |
| | | INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted) |
| | | SELECT 1, 1063300, '1', NOW(), '1', NOW(), 0 FROM DUAL |
| | | WHERE NOT EXISTS (SELECT 1 FROM system_role_menu WHERE role_id = 1 AND menu_id = 1063300); |
| | | SELECT 1, @menu_id, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL |
| | | WHERE @menu_id IS NOT NULL AND @grant_exists = 0; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- ===================================================================== |
| | | -- 忍¡åâ导å
¥âæé®æé åå¹¶è¡¥ä¸ |
| | | -- 1) éè´è®¢å导å
¥ erp:purchase-order:import |
| | | -- 2) éå®è®¢å导å
¥ erp:sale-order:import |
| | | -- 3) 计éå¨å
·å¯¼å
¥ mes:dv-metering:import |
| | | -- 4) åå·¥åå导å
¥ hrm:employee-contract:import |
| | | -- |
| | | -- ç¨æ³ï¼å¨ãç®æ æ°æ®åºã䏿´ä½æ§è¡ä¸æ¬¡å³å¯ï¼æ¬å°å¼ååº / è¿ç¨æµè¯åº / ç产åºï¼ã |
| | | -- ç¹ç¹ï¼ |
| | | -- * 卿å system_menu æå¤§ id + 1ï¼ä¸ä¼ä¸æ¢æèå id æè½¦ï¼ |
| | | -- * æ permission 夿ï¼å¹çï¼å¯é夿§è¡ï¼ |
| | | -- * ä»
ææâè¶
级管çåâè§è² role_id=1ï¼å¦è¦è®©å
¶å®è§è²ä¹è½å¯¼å
¥ï¼ |
| | | -- 请å¨ãç³»ç»ç®¡ç â è§è²ç®¡çãéå¾é对åºâXX导å
¥âæé®ã |
| | | -- 注æï¼ |
| | | -- * parent_id ä¸ºåæ¨¡åèå idï¼éè´è®¢å=2666ãéå®è®¢å=2638ã计éå¨å
·=5380ãåå·¥åå=1063105ï¼ã |
| | | -- è¥ç®æ åºèåç»æè¢«æ¹è¿ï¼è¯·å
æ ¸å¯¹è¿å 个ç¶èå id åæ§è¡ã |
| | | -- ===================================================================== |
| | | |
| | | -- ------------- 1) éè´è®¢å导å
¥ ------------- |
| | | SET @perm_exists := (SELECT COUNT(*) FROM system_menu WHERE permission = 'erp:purchase-order:import'); |
| | | SET @new_id := IFNULL((SELECT MAX(id) FROM system_menu), 0) + 1; |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, |
| | | component_name, status, visible, keep_alive, always_show, |
| | | creator, create_time, updater, update_time, deleted) |
| | | SELECT @new_id, 'éè´è®¢å导å
¥', 'erp:purchase-order:import', 3, 8, 2666, '', '#', '', NULL, |
| | | 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL WHERE @perm_exists = 0; |
| | | SET @menu_id := (SELECT id FROM system_menu WHERE permission = 'erp:purchase-order:import'); |
| | | SET @grant_exists := (SELECT COUNT(*) FROM system_role_menu WHERE role_id = 1 AND menu_id = @menu_id); |
| | | INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted) |
| | | SELECT 1, @menu_id, '1', NOW(), '1', NOW(), 0 FROM DUAL |
| | | WHERE @menu_id IS NOT NULL AND @grant_exists = 0; |
| | | |
| | | -- ------------- 2) éå®è®¢å导å
¥ ------------- |
| | | SET @perm_exists := (SELECT COUNT(*) FROM system_menu WHERE permission = 'erp:sale-order:import'); |
| | | SET @new_id := IFNULL((SELECT MAX(id) FROM system_menu), 0) + 1; |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, |
| | | component_name, status, visible, keep_alive, always_show, |
| | | creator, create_time, updater, update_time, deleted) |
| | | SELECT @new_id, 'éå®è®¢å导å
¥', 'erp:sale-order:import', 3, 8, 2638, '', '#', '', NULL, |
| | | 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL WHERE @perm_exists = 0; |
| | | SET @menu_id := (SELECT id FROM system_menu WHERE permission = 'erp:sale-order:import'); |
| | | SET @grant_exists := (SELECT COUNT(*) FROM system_role_menu WHERE role_id = 1 AND menu_id = @menu_id); |
| | | INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted) |
| | | SELECT 1, @menu_id, '1', NOW(), '1', NOW(), 0 FROM DUAL |
| | | WHERE @menu_id IS NOT NULL AND @grant_exists = 0; |
| | | |
| | | -- ------------- 3) 计éå¨å
·å¯¼å
¥ ------------- |
| | | SET @perm_exists := (SELECT COUNT(*) FROM system_menu WHERE permission = 'mes:dv-metering:import'); |
| | | SET @new_id := IFNULL((SELECT MAX(id) FROM system_menu), 0) + 1; |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, |
| | | component_name, status, visible, keep_alive, always_show, |
| | | creator, create_time, updater, update_time, deleted) |
| | | SELECT @new_id, '计éå¨å
·å¯¼å
¥', 'mes:dv-metering:import', 3, 8, 5380, '', '#', '', NULL, |
| | | 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL WHERE @perm_exists = 0; |
| | | SET @menu_id := (SELECT id FROM system_menu WHERE permission = 'mes:dv-metering:import'); |
| | | SET @grant_exists := (SELECT COUNT(*) FROM system_role_menu WHERE role_id = 1 AND menu_id = @menu_id); |
| | | INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted) |
| | | SELECT 1, @menu_id, '1', NOW(), '1', NOW(), 0 FROM DUAL |
| | | WHERE @menu_id IS NOT NULL AND @grant_exists = 0; |
| | | |
| | | -- ------------- 4) åå·¥åå导å
¥ ------------- |
| | | SET @perm_exists := (SELECT COUNT(*) FROM system_menu WHERE permission = 'hrm:employee-contract:import'); |
| | | SET @new_id := IFNULL((SELECT MAX(id) FROM system_menu), 0) + 1; |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, |
| | | component_name, status, visible, keep_alive, always_show, |
| | | creator, create_time, updater, update_time, deleted) |
| | | SELECT @new_id, 'åå导å
¥', 'hrm:employee-contract:import', 3, 5, 1063105, '', '#', '', NULL, |
| | | 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL WHERE @perm_exists = 0; |
| | | SET @menu_id := (SELECT id FROM system_menu WHERE permission = 'hrm:employee-contract:import'); |
| | | SET @grant_exists := (SELECT COUNT(*) FROM system_role_menu WHERE role_id = 1 AND menu_id = @menu_id); |
| | | INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted) |
| | | SELECT 1, @menu_id, '1', NOW(), '1', NOW(), 0 FROM DUAL |
| | | WHERE @menu_id IS NOT NULL AND @grant_exists = 0; |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | -- 计éå¨å
· - 导å
¥æé®æéï¼ç¶èåï¼è®¡éå¨å
· id=5380ï¼ |
| | | -- 卿 idï¼MAX(id)+1ï¼ï¼é¿å
䏿¢æèåæè½¦ï¼å¯é夿§è¡ï¼å¹çï¼ã |
| | | SET @perm_exists := (SELECT COUNT(*) FROM system_menu WHERE permission = 'mes:dv-metering:import'); |
| | | SET @import_menu_id := IFNULL((SELECT MAX(id) FROM system_menu), 0) + 1; |
| | | |
| | | INSERT INTO system_menu (id, name, permission, type, sort, parent_id, path, icon, component, |
| | | component_name, status, visible, keep_alive, always_show, |
| | | creator, create_time, updater, update_time, deleted) |
| | | SELECT @import_menu_id, '计éå¨å
·å¯¼å
¥', 'mes:dv-metering:import', 3, 8, 5380, '', '#', '', NULL, |
| | | 0, 1, 0, 1, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL |
| | | WHERE @perm_exists = 0; |
| | | |
| | | SET @menu_id := (SELECT id FROM system_menu WHERE permission = 'mes:dv-metering:import'); |
| | | SET @grant_exists := (SELECT COUNT(*) FROM system_role_menu WHERE role_id = 1 AND menu_id = @menu_id); |
| | | |
| | | INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted) |
| | | SELECT 1, @menu_id, '1', NOW(), '1', NOW(), 0 |
| | | FROM DUAL |
| | | WHERE @menu_id IS NOT NULL AND @grant_exists = 0; |
| | |
| | | */ |
| | | List<Long> getPermittedCustomerIds(); |
| | | |
| | | /** |
| | | * è·å¾å®¢æ·ç®åå表ï¼ç¼å· + åç§°ï¼ |
| | | * |
| | | * ç¨äºå
¶å®æ¨¡åï¼å¦ ERP éå®è®¢å导å
¥ï¼æå®¢æ·åç§°å¹é
客æ·ç¼å· |
| | | * |
| | | * @return 客æ·ç®åå表 |
| | | */ |
| | | List<CrmCustomerRespDTO> getCustomerSimpleList(); |
| | | |
| | | } |
| | |
| | | */ |
| | | List<Long> getPermittedCustomerIds(); |
| | | |
| | | /** |
| | | * è·å¾å®¢æ·ç®åå表ï¼ç¼å· + åç§°ï¼ |
| | | * |
| | | * ç¨äºå
¶å®æ¨¡åï¼å¦ ERP éå®è®¢å导å
¥ï¼æå®¢æ·åç§°å¹é
客æ·ç¼å· |
| | | * |
| | | * @return 客æ·ç®åå表 |
| | | */ |
| | | List<CrmCustomerRespDTO> getCustomerSimpleList(); |
| | | |
| | | } |
| | |
| | | return new ArrayList<>(permittedIds); |
| | | } |
| | | |
| | | @Override |
| | | public List<CrmCustomerRespDTO> getCustomerSimpleList() { |
| | | List<CrmCustomerDO> customers = customerService.getCustomerList(); |
| | | return BeanUtils.toBean(customers, CrmCustomerRespDTO.class); |
| | | } |
| | | |
| | | /** |
| | | * è¿æ»¤æç¨æ·æ²¡ææéçå®¢æ· |
| | | * <p> |
| | |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderRespVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderImportExcelVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderImportRespVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO; |
| | | import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; |
| | |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | import java.util.Arrays; |
| | | import java.util.Map; |
| | | |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| | |
| | | ExcelUtils.write(response, "éè´è®¢å.xls", "æ°æ®", ErpPurchaseOrderRespVO.class, list); |
| | | } |
| | | |
| | | @GetMapping("/get-import-template") |
| | | @Operation(summary = "è·å¾å¯¼å
¥éè´è®¢å模æ¿") |
| | | @PreAuthorize("@ss.hasPermission('erp:purchase-order:import')") |
| | | public void importTemplate(HttpServletResponse response) throws IOException { |
| | | List<ErpPurchaseOrderImportExcelVO> list = Arrays.asList( |
| | | ErpPurchaseOrderImportExcelVO.builder() |
| | | .no("CGDD20260101000001").supplierName("示ä¾ä¾åºå") |
| | | .orderTime("2026-01-01 10:00:00").productCode("ITEM001").productName("示ä¾äº§åA") |
| | | .count(new BigDecimal("100")).productPrice(new BigDecimal("10.00")) |
| | | .taxPercent(new BigDecimal("13")).qcCheckFlag("å¦").remark("示ä¾").build()); |
| | | ExcelUtils.write(response, "éè´è®¢å导å
¥æ¨¡æ¿.xls", "éè´è®¢å", ErpPurchaseOrderImportExcelVO.class, list); |
| | | } |
| | | |
| | | @PostMapping("/import") |
| | | @Operation(summary = "导å
¥éè´è®¢å") |
| | | @PreAuthorize("@ss.hasPermission('erp:purchase-order:import')") |
| | | public CommonResult<ErpPurchaseOrderImportRespVO> importPurchaseOrderExcel( |
| | | @RequestParam("file") MultipartFile file, |
| | | @RequestParam(value = "directApprove", required = false, defaultValue = "false") Boolean directApprove) throws Exception { |
| | | List<ErpPurchaseOrderImportExcelVO> list = ExcelUtils.read(file, ErpPurchaseOrderImportExcelVO.class); |
| | | return success(purchaseOrderService.importPurchaseOrderList(list, directApprove)); |
| | | } |
| | | |
| | | @PostMapping("/confirm-receipt") |
| | | @Operation(summary = "确认æ¶è´§") |
| | | @Parameter(name = "id", description = "éè´è®¢åç¼å·", required = true) |
| | |
| | | .getPurchaseInvoicePriceSumMapByOrderIds(convertSet(pageResult.getList(), ErpPurchaseOrderDO::getId)); |
| | | // 2. å¼å§æ¼æ¥ |
| | | return BeanUtils.toBean(pageResult, ErpPurchaseOrderRespVO.class, purchaseOrder -> { |
| | | purchaseOrder.setItems(BeanUtils.toBean(purchaseOrderItemMap.get(purchaseOrder.getId()), ErpPurchaseOrderRespVO.Item.class, |
| | | purchaseOrder.setItems(BeanUtils.toBean( |
| | | purchaseOrderItemMap.getOrDefault(purchaseOrder.getId(), java.util.Collections.<ErpPurchaseOrderItemDO>emptyList()), |
| | | ErpPurchaseOrderRespVO.Item.class, |
| | | item -> { |
| | | MapUtils.findAndThen(itemMap, item.getProductId(), mdmItem -> item.setProductName(mdmItem.getName()) |
| | | .setProductBarCode(mdmItem.getBarCode())); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order; |
| | | |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * 管çåå° - éè´è®¢å导å
¥ Excel VO |
| | | * |
| | | * 说æï¼ä¸è¡ = 䏿¡äº§åæç»ï¼ç¸åâéè´åç¼å·âçå¤è¡ä¼å并为ä¸å¼ 订åï¼ä¸»è¡¨å段å该ç»é¦è¡ï¼ã |
| | | * âéè´åç¼å·âç空æ¶ï¼æ¯ä¸è¡ä¼åèªçæä¸å¼ æ°è®¢åï¼åæ¡æç»ï¼ã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - éè´è®¢å导å
¥ Excel VO") |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class ErpPurchaseOrderImportExcelVO { |
| | | |
| | | @ExcelProperty("éè´åç¼å·") |
| | | @Schema(description = "éè´åç¼å·ï¼ç空åèªå¨çæï¼ç¸åç¼å·çå¤è¡å并为åä¸å¼ 订å") |
| | | private String no; |
| | | |
| | | @ExcelProperty("ä¾åºååç§°") |
| | | @Schema(description = "ä¾åºååç§°ï¼éä¸ç³»ç»å
ä¾åºååç§°ï¼æç¼ç ï¼ä¸è´", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private String supplierName; |
| | | |
| | | @ExcelProperty("éè´æ¶é´") |
| | | @Schema(description = "éè´æ¶é´ï¼æ ¼å¼å¦ 2026-01-01 10:00:00ï¼ç空åå½åæ¶é´") |
| | | private String orderTime; |
| | | |
| | | @ExcelProperty("产åç¼ç ") |
| | | @Schema(description = "产åï¼ç©æï¼ç¼ç ", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private String productCode; |
| | | |
| | | @ExcelProperty("产ååç§°") |
| | | @Schema(description = "产ååç§°ï¼ä»
ä¾å¯¹ç
§ï¼å¹é
ä»ä»¥äº§åç¼ç 为åï¼") |
| | | private String productName; |
| | | |
| | | @ExcelProperty("æ°é") |
| | | @Schema(description = "äº§åæ°é", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private BigDecimal count; |
| | | |
| | | @ExcelProperty("åä»·") |
| | | @Schema(description = "产ååä»·") |
| | | private BigDecimal productPrice; |
| | | |
| | | @ExcelProperty("ç¨ç(%)") |
| | | @Schema(description = "ç¨çï¼ç¾åæ¯ï¼ä¾å¦ 13 表示 13%") |
| | | private BigDecimal taxPercent; |
| | | |
| | | @ExcelProperty("æ¯å¦æ¥ææ£éª") |
| | | @Schema(description = "æ¯å¦éè¦æ¥ææ£éªï¼å¡«âæ¯â为éè¦ï¼å
¶å®ä¸ºä¸éè¦") |
| | | private String qcCheckFlag; |
| | | |
| | | @ExcelProperty("夿³¨") |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 管çåå° - éè´è®¢å导å
¥ Response VO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - éè´è®¢å导å
¥ Response VO") |
| | | @Data |
| | | @Builder |
| | | public class ErpPurchaseOrderImportRespVO { |
| | | |
| | | @Schema(description = "å建æåçéè´åç¼å·æ°ç»", example = "CGDD20260101000001") |
| | | private List<String> createList; |
| | | |
| | | @Schema(description = "æ´æ°æåçéè´åç¼å·æ°ç»", example = "CGDD20260101000002") |
| | | private List<String> updateList; |
| | | |
| | | @Schema(description = "导å
¥å¤±è´¥éåï¼key 为éè´åç¼å·æè¡å·ï¼value 为失败åå ") |
| | | private Map<String, String> failureList; |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderRespVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderImportExcelVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderImportRespVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO; |
| | | import cn.iocoder.yudao.module.erp.service.sale.ErpSaleOrderService; |
| | |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.IOException; |
| | | import java.math.BigDecimal; |
| | | import java.util.List; |
| | | import java.util.Arrays; |
| | | import java.util.Map; |
| | | |
| | | import static cn.iocoder.yudao.framework.apilog.core.enums.OperateTypeEnum.EXPORT; |
| | |
| | | ExcelUtils.write(response, "éå®è®¢å.xls", "æ°æ®", ErpSaleOrderRespVO.class, list); |
| | | } |
| | | |
| | | @GetMapping("/get-import-template") |
| | | @Operation(summary = "è·å¾å¯¼å
¥éå®è®¢å模æ¿") |
| | | @PreAuthorize("@ss.hasPermission('erp:sale-order:import')") |
| | | public void importTemplate(HttpServletResponse response) throws IOException { |
| | | List<ErpSaleOrderImportExcelVO> list = Arrays.asList( |
| | | ErpSaleOrderImportExcelVO.builder() |
| | | .no("XSDD20260101000001").customerName("示ä¾å®¢æ·").contractNo("HT20260101") |
| | | .orderTime("2026-01-01 10:00:00").productCode("ITEM001").productName("示ä¾äº§åA") |
| | | .count(new BigDecimal("100")).productPrice(new BigDecimal("20.00")) |
| | | .taxPercent(new BigDecimal("13")).needProduction("æ¯").remark("示ä¾").build()); |
| | | ExcelUtils.write(response, "éå®è®¢å导å
¥æ¨¡æ¿.xls", "éå®è®¢å", ErpSaleOrderImportExcelVO.class, list); |
| | | } |
| | | |
| | | @PostMapping("/import") |
| | | @Operation(summary = "导å
¥éå®è®¢å") |
| | | @PreAuthorize("@ss.hasPermission('erp:sale-order:import')") |
| | | public CommonResult<ErpSaleOrderImportRespVO> importSaleOrderExcel( |
| | | @RequestParam("file") MultipartFile file) throws Exception { |
| | | List<ErpSaleOrderImportExcelVO> list = ExcelUtils.read(file, ErpSaleOrderImportExcelVO.class); |
| | | return success(saleOrderService.importSaleOrderList(list)); |
| | | } |
| | | |
| | | private PageResult<ErpSaleOrderRespVO> buildSaleOrderVOPageResult(PageResult<ErpSaleOrderDO> pageResult) { |
| | | if (CollUtil.isEmpty(pageResult.getList())) { |
| | | return PageResult.empty(pageResult.getTotal()); |
| | |
| | | } |
| | | // 2. å¼å§æ¼æ¥ |
| | | return BeanUtils.toBean(pageResult, ErpSaleOrderRespVO.class, saleOrder -> { |
| | | saleOrder.setItems(BeanUtils.toBean(saleOrderItemMap.get(saleOrder.getId()), ErpSaleOrderRespVO.Item.class, |
| | | saleOrder.setItems(BeanUtils.toBean( |
| | | saleOrderItemMap.getOrDefault(saleOrder.getId(), java.util.Collections.<ErpSaleOrderItemDO>emptyList()), |
| | | ErpSaleOrderRespVO.Item.class, |
| | | item -> { |
| | | fillItemPrice(item); |
| | | item.setBatchCode(batchCodeMap.get(item.getId())); |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order; |
| | | |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | /** |
| | | * 管çåå° - éå®è®¢å导å
¥ Excel VO |
| | | * |
| | | * 说æï¼ä¸è¡ = 䏿¡äº§åæç»ï¼ç¸åâéå®åç¼å·âçå¤è¡ä¼å并为ä¸å¼ 订åï¼ä¸»è¡¨å段å该ç»é¦è¡ï¼ã |
| | | * âéå®åç¼å·âç空æ¶ï¼æ¯ä¸è¡ä¼åèªçæä¸å¼ æ°è®¢åï¼åæ¡æç»ï¼ã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - éå®è®¢å导å
¥ Excel VO") |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class ErpSaleOrderImportExcelVO { |
| | | |
| | | @ExcelProperty("éå®åç¼å·") |
| | | @Schema(description = "éå®åç¼å·ï¼ç空åèªå¨çæï¼ç¸åç¼å·çå¤è¡å并为åä¸å¼ 订å") |
| | | private String no; |
| | | |
| | | @ExcelProperty("客æ·åç§°") |
| | | @Schema(description = "客æ·åç§°ï¼éä¸ CRM ä¸ç客æ·åç§°ä¸è´", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private String customerName; |
| | | |
| | | @ExcelProperty("åååå·") |
| | | @Schema(description = "å
³èåååå·ï¼å¯ç©º") |
| | | private String contractNo; |
| | | |
| | | @ExcelProperty("ä¸åæ¶é´") |
| | | @Schema(description = "ä¸åæ¶é´ï¼æ ¼å¼å¦ 2026-01-01 10:00:00ï¼ç空åå½åæ¶é´") |
| | | private String orderTime; |
| | | |
| | | @ExcelProperty("产åç¼ç ") |
| | | @Schema(description = "产åï¼ç©æï¼ç¼ç ", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private String productCode; |
| | | |
| | | @ExcelProperty("产ååç§°") |
| | | @Schema(description = "产ååç§°ï¼ä»
ä¾å¯¹ç
§ï¼å¹é
ä»ä»¥äº§åç¼ç 为åï¼") |
| | | private String productName; |
| | | |
| | | @ExcelProperty("æ°é") |
| | | @Schema(description = "äº§åæ°é", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private BigDecimal count; |
| | | |
| | | @ExcelProperty("åä»·") |
| | | @Schema(description = "产ååä»·ï¼ç空ååè¯¥ç©æçéå®ä»·") |
| | | private BigDecimal productPrice; |
| | | |
| | | @ExcelProperty("ç¨ç(%)") |
| | | @Schema(description = "ç¨çï¼ç¾åæ¯ï¼ä¾å¦ 13 表示 13%") |
| | | private BigDecimal taxPercent; |
| | | |
| | | @ExcelProperty("æ¯å¦éè¦ç产") |
| | | @Schema(description = "æ¯å¦éè¦ç产ï¼å¡«âæ¯/å¦âï¼ç空é»è®¤âæ¯â") |
| | | private String needProduction; |
| | | |
| | | @ExcelProperty("夿³¨") |
| | | @Schema(description = "夿³¨ï¼åä¸å¼ 订ååé¦è¡å¤æ³¨ä½ä¸ºæ´å夿³¨") |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 管çåå° - éå®è®¢å导å
¥ Response VO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - éå®è®¢å导å
¥ Response VO") |
| | | @Data |
| | | @Builder |
| | | public class ErpSaleOrderImportRespVO { |
| | | |
| | | @Schema(description = "å建æåçéå®åç¼å·æ°ç»", example = "XSDD20260101000001") |
| | | private List<String> createList; |
| | | |
| | | @Schema(description = "æ´æ°æåçéå®åç¼å·æ°ç»", example = "XSDD20260101000002") |
| | | private List<String> updateList; |
| | | |
| | | @Schema(description = "导å
¥å¤±è´¥éåï¼key 为éå®åç¼å·æè¡å·ï¼value 为失败åå ") |
| | | private Map<String, String> failureList; |
| | | |
| | | } |
| | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderImportExcelVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderImportRespVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO; |
| | | import jakarta.validation.Valid; |
| | |
| | | Long createPurchaseOrder(@Valid ErpPurchaseOrderSaveReqVO createReqVO); |
| | | |
| | | /** |
| | | * 导å
¥éè´è®¢å |
| | | * |
| | | * @param importList 导å
¥æ°æ®ï¼æ¯ä¸è¡ = 䏿¡æç»ï¼ç¸åéè´åç¼å·å并为ä¸å¼ 订åï¼ |
| | | * @param directApprove æ¯å¦å°å¯¼å
¥ç订åç´æ¥ç½®ä¸ºâå·²å®¡æ ¸âï¼true=å·²å®¡æ ¸(20)ï¼false=æªå®¡æ ¸(10) |
| | | * @return 导å
¥ç»æ |
| | | */ |
| | | ErpPurchaseOrderImportRespVO importPurchaseOrderList(List<ErpPurchaseOrderImportExcelVO> importList, Boolean directApprove); |
| | | |
| | | /** |
| | | * æ´æ°éè´è®¢å |
| | | * |
| | | * @param updateReqVO æ´æ°ä¿¡æ¯ |
| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | |
| | | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderImportExcelVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderImportRespVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseOrderItemDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.purchase.ErpPurchaseRequestDO; |
| | |
| | | import cn.iocoder.yudao.module.erp.enums.ErpAuditStatus; |
| | | import cn.iocoder.yudao.module.erp.enums.ErpPurchaseOrderInStatusEnum; |
| | | import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService; |
| | | import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.SrmSupplierApi; |
| | | import cn.iocoder.yudao.module.srm.api.supplier.dto.SrmSupplierRespDTO; |
| | | import cn.iocoder.yudao.module.mes.api.arrivalnotice.MesArrivalNoticeApi; |
| | | import cn.iocoder.yudao.module.system.api.storage.StorageAttachmentApi; |
| | | import jakarta.annotation.Resource; |
| | |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.*; |
| | | |
| | | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| | |
| | | |
| | | @Resource |
| | | private StorageAttachmentApi storageAttachmentApi; |
| | | |
| | | @Resource |
| | | private SrmSupplierApi srmSupplierApi; |
| | | @Resource |
| | | private MdmItemApi mdmItemApi; |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | |
| | | storageAttachmentApi.updateAttachments("file", "erp_purchase_order", updateReqVO.getId(), updateReqVO.getBlobIds()); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public ErpPurchaseOrderImportRespVO importPurchaseOrderList(List<ErpPurchaseOrderImportExcelVO> importList, |
| | | Boolean directApprove) { |
| | | ErpPurchaseOrderImportRespVO respVO = ErpPurchaseOrderImportRespVO.builder() |
| | | .createList(new ArrayList<>()) |
| | | .updateList(new ArrayList<>()) |
| | | .failureList(new LinkedHashMap<>()) |
| | | .build(); |
| | | if (CollUtil.isEmpty(importList)) { |
| | | return respVO; |
| | | } |
| | | |
| | | // 1. é¢çâä¾åºååç§°/ç¼ç -> ä¾åºåç¼å·âæ å°ï¼ERP ä¾åºåä¸»æ°æ®å¨ SRM 模åï¼ |
| | | Map<String, Long> supplierIdMap = new HashMap<>(); |
| | | CommonResult<List<SrmSupplierRespDTO>> supplierResult = srmSupplierApi.getSupplierSimpleList(); |
| | | if (supplierResult != null && CollUtil.isNotEmpty(supplierResult.getData())) { |
| | | supplierResult.getData().forEach(supplier -> { |
| | | if (StrUtil.isNotBlank(supplier.getName())) { |
| | | supplierIdMap.put(supplier.getName().trim(), supplier.getId()); |
| | | } |
| | | if (StrUtil.isNotBlank(supplier.getCode())) { |
| | | supplierIdMap.putIfAbsent(supplier.getCode().trim(), supplier.getId()); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 2. æâéè´åç¼å·âåç»ï¼åç¼å·å¤è¡ = ä¸å¼ 订åç夿¡æç»ï¼ç¼å·ä¸ºç©ºåæ¯è¡ç¬ç«æå |
| | | Map<String, List<ErpPurchaseOrderImportExcelVO>> orderGroups = new LinkedHashMap<>(); |
| | | for (int i = 0; i < importList.size(); i++) { |
| | | ErpPurchaseOrderImportExcelVO row = importList.get(i); |
| | | if (row == null || (StrUtil.isBlank(row.getSupplierName()) && StrUtil.isBlank(row.getProductCode()) |
| | | && StrUtil.isBlank(row.getNo()))) { |
| | | continue; // è·³è¿ç©ºè¡ |
| | | } |
| | | String key = StrUtil.isNotBlank(row.getNo()) ? row.getNo().trim() : "__row_" + (i + 2); |
| | | orderGroups.computeIfAbsent(key, k -> new ArrayList<>()).add(row); |
| | | } |
| | | |
| | | // 3. é个订å导å
¥ï¼åå¼ è®¢å失败ä¸å½±åå
¶å®ï¼ |
| | | for (Map.Entry<String, List<ErpPurchaseOrderImportExcelVO>> entry : orderGroups.entrySet()) { |
| | | List<ErpPurchaseOrderImportExcelVO> rows = entry.getValue(); |
| | | String label = StrUtil.isNotBlank(rows.get(0).getNo()) ? rows.get(0).getNo().trim() |
| | | : "第 " + entry.getKey().substring("__row_".length()) + " è¡"; |
| | | try { |
| | | ErpPurchaseOrderDO purchaseOrder = createPurchaseOrderByImport(rows, supplierIdMap, directApprove); |
| | | respVO.getCreateList().add(purchaseOrder.getNo()); |
| | | } catch (Exception ex) { |
| | | respVO.getFailureList().put(label, resolveImportErrorMessage(ex)); |
| | | } |
| | | } |
| | | return respVO; |
| | | } |
| | | |
| | | /** |
| | | * 便®å¯¼å
¥æ°æ®å建ä¸å¼ éè´è®¢åï¼å«æç»ï¼ã |
| | | * |
| | | * 注æï¼æ¤å¤ä¸è°ç¨ submitPurchaseOrderï¼å æ¤ä¸ä¼å建 BPM 工使µï¼è®¢åç¶æç± directApprove ç´æ¥å³å®ï¼ |
| | | * å¾éâæ¯å¦å®¡æ ¸â => å·²å®¡æ ¸(20)ï¼æªå¾é => æªå®¡æ ¸(10)ã |
| | | */ |
| | | private ErpPurchaseOrderDO createPurchaseOrderByImport(List<ErpPurchaseOrderImportExcelVO> rows, |
| | | Map<String, Long> supplierIdMap, Boolean directApprove) { |
| | | // 1. ä¾åºåï¼å该ç»é¦è¡ï¼ |
| | | ErpPurchaseOrderImportExcelVO head = rows.get(0); |
| | | if (StrUtil.isBlank(head.getSupplierName())) { |
| | | throw new IllegalArgumentException("ä¾åºååç§°ä¸è½ä¸ºç©º"); |
| | | } |
| | | Long supplierId = supplierIdMap.get(head.getSupplierName().trim()); |
| | | if (supplierId == null) { |
| | | throw new IllegalArgumentException("ä¾åºåä¸åå¨ï¼" + head.getSupplierName()); |
| | | } |
| | | supplierService.validateSupplier(supplierId); |
| | | |
| | | // 2. æç» |
| | | List<ErpPurchaseOrderSaveReqVO.Item> items = new ArrayList<>(); |
| | | for (ErpPurchaseOrderImportExcelVO row : rows) { |
| | | if (StrUtil.isBlank(row.getProductCode())) { |
| | | throw new IllegalArgumentException("产åç¼ç ä¸è½ä¸ºç©º"); |
| | | } |
| | | CommonResult<MdmItemRespDTO> itemResult = mdmItemApi.getItemByCode(row.getProductCode().trim()); |
| | | MdmItemRespDTO item = itemResult == null ? null : itemResult.getData(); |
| | | if (item == null) { |
| | | throw new IllegalArgumentException("产åä¸åå¨ï¼" + row.getProductCode()); |
| | | } |
| | | if (row.getCount() == null) { |
| | | throw new IllegalArgumentException("产å[" + row.getProductCode() + "]çæ°éä¸è½ä¸ºç©º"); |
| | | } |
| | | if (item.getUnitMeasureId() == null) { |
| | | throw new IllegalArgumentException("产å[" + row.getProductCode() + "]æªé
置计éåä½ï¼è¯·å
å¨ç©æä¸ç»´æ¤"); |
| | | } |
| | | ErpPurchaseOrderSaveReqVO.Item itemVO = new ErpPurchaseOrderSaveReqVO.Item(); |
| | | itemVO.setProductId(item.getId()); |
| | | itemVO.setProductUnitId(item.getUnitMeasureId()); |
| | | itemVO.setProductPrice(row.getProductPrice() == null ? BigDecimal.ZERO : row.getProductPrice()); |
| | | itemVO.setCount(row.getCount()); |
| | | itemVO.setTaxPercent(row.getTaxPercent()); |
| | | itemVO.setRemark(row.getRemark()); |
| | | itemVO.setQcCheckFlag("æ¯".equals(StrUtil.trim(row.getQcCheckFlag()))); |
| | | items.add(itemVO); |
| | | } |
| | | |
| | | // 3. åå·ï¼å¡«äºå°±ç¨ï¼æ ¡éªå¯ä¸ï¼ï¼æ²¡å¡«èªå¨çæ |
| | | String no = StrUtil.isNotBlank(head.getNo()) ? head.getNo().trim() |
| | | : noRedisDAO.generate(ErpNoRedisDAO.PURCHASE_ORDER_NO_PREFIX); |
| | | if (purchaseOrderMapper.selectByNo(no) != null) { |
| | | throw new IllegalArgumentException("éè´åç¼å·å·²åå¨ï¼" + no); |
| | | } |
| | | |
| | | // 4. ç»è£
主表ï¼å¾éâæ¯å¦å®¡æ ¸â => å·²å®¡æ ¸(20)ï¼å¦åæªå®¡æ ¸(10) |
| | | ErpPurchaseOrderDO purchaseOrder = new ErpPurchaseOrderDO(); |
| | | purchaseOrder.setNo(no); |
| | | purchaseOrder.setSupplierId(supplierId); |
| | | purchaseOrder.setOrderTime(parseImportOrderTime(head.getOrderTime())); |
| | | purchaseOrder.setRemark(head.getRemark()); |
| | | purchaseOrder.setStatus(Boolean.TRUE.equals(directApprove) |
| | | ? ErpAuditStatus.APPROVE.getStatus() : ErpAuditStatus.PROCESS.getStatus()); |
| | | purchaseOrder.setDepositPrice(BigDecimal.ZERO); // deposit_price 为 NOT NULL 䏿 é»è®¤å¼ï¼å¿
é¡»æ¾å¼èµå¼ |
| | | purchaseOrder.setInCount(BigDecimal.ZERO); |
| | | purchaseOrder.setInStatus(ErpPurchaseOrderInStatusEnum.NOT_IN.getStatus()); |
| | | |
| | | List<ErpPurchaseOrderItemDO> itemDOs = validatePurchaseOrderItems(items); |
| | | calculateTotalPrice(purchaseOrder, itemDOs); |
| | | purchaseOrderMapper.insert(purchaseOrder); |
| | | itemDOs.forEach(item -> item.setOrderId(purchaseOrder.getId())); |
| | | purchaseOrderItemMapper.insertBatch(itemDOs); |
| | | return purchaseOrder; |
| | | } |
| | | |
| | | /** |
| | | * è§£æå¯¼å
¥çéè´æ¶é´ï¼å
¼å®¹å¤ç§å¸¸è§æ ¼å¼ï¼ä¸ºç©ºåå½åæ¶é´ |
| | | */ |
| | | private LocalDateTime parseImportOrderTime(String text) { |
| | | if (StrUtil.isBlank(text)) { |
| | | return LocalDateTime.now(); |
| | | } |
| | | String value = text.trim(); |
| | | for (String pattern : new String[]{"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd HH:mm:ss"}) { |
| | | try { |
| | | return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(pattern)); |
| | | } catch (Exception ignored) { |
| | | // ç»§ç»å°è¯ä¸ä¸ä¸ªæ ¼å¼ |
| | | } |
| | | } |
| | | for (String pattern : new String[]{"yyyy-MM-dd", "yyyy/M/d"}) { |
| | | try { |
| | | return LocalDate.parse(value, DateTimeFormatter.ofPattern(pattern)).atStartOfDay(); |
| | | } catch (Exception ignored) { |
| | | // ç»§ç»å°è¯ä¸ä¸ä¸ªæ ¼å¼ |
| | | } |
| | | } |
| | | throw new IllegalArgumentException("éè´æ¶é´æ ¼å¼ä¸æ£ç¡®ï¼" + text + "ï¼ç¤ºä¾ 2026-01-01 10:00:00"); |
| | | } |
| | | |
| | | /** |
| | | * æå导å
¥å¤±è´¥åå ï¼ServiceException åä¸å¡æç¤ºï¼ |
| | | */ |
| | | private String resolveImportErrorMessage(Exception ex) { |
| | | if (ex instanceof cn.iocoder.yudao.framework.common.exception.ServiceException serviceException) { |
| | | return serviceException.getMessage(); |
| | | } |
| | | return StrUtil.blankToDefault(ex.getMessage(), "导å
¥å¤±è´¥"); |
| | | } |
| | | |
| | | private void calculateTotalPrice(ErpPurchaseOrderDO purchaseOrder, List<ErpPurchaseOrderItemDO> purchaseOrderItems) { |
| | | purchaseOrder.setTotalCount(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getCount, BigDecimal::add)); |
| | | purchaseOrder.setTotalProductPrice(getSumValue(purchaseOrderItems, ErpPurchaseOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)); |
| | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderImportExcelVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderImportRespVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO; |
| | | import jakarta.validation.Valid; |
| | |
| | | Long createSaleOrder(@Valid ErpSaleOrderSaveReqVO createReqVO); |
| | | |
| | | /** |
| | | * 导å
¥éå®è®¢åï¼å¯¼å
¥åä¸ºâæªæäº¤âç¶æï¼å¯ç»§ç»ç¼è¾ææäº¤å®¡æ¹ï¼ |
| | | * |
| | | * @param importList 导å
¥æ°æ®ï¼æ¯ä¸è¡ = 䏿¡æç»ï¼ç¸åéå®åç¼å·å并为ä¸å¼ 订åï¼ |
| | | * @return 导å
¥ç»æ |
| | | */ |
| | | ErpSaleOrderImportRespVO importSaleOrderList(List<ErpSaleOrderImportExcelVO> importList); |
| | | |
| | | /** |
| | | * æ´æ°éå®è®¢å |
| | | * |
| | | * @param updateReqVO æ´æ°ä¿¡æ¯ |
| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjectUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.number.MoneyUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | |
| | | import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService; |
| | | import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; |
| | | import cn.iocoder.yudao.module.crm.api.customer.CrmCustomerApi; |
| | | import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO; |
| | | import cn.iocoder.yudao.module.crm.api.contract.CrmContractApi; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderImportExcelVO; |
| | | import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderImportRespVO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderDO; |
| | | import cn.iocoder.yudao.module.erp.dal.dataobject.sale.ErpSaleOrderItemDO; |
| | | import cn.iocoder.yudao.module.erp.dal.mysql.sale.ErpSaleOrderItemMapper; |
| | |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalDate; |
| | | import java.time.LocalDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.ArrayList; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | |
| | | storageAttachmentApi.updateAttachments("file", "erp_sale_order", updateReqVO.getId(), updateReqVO.getBlobIds()); |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public ErpSaleOrderImportRespVO importSaleOrderList(List<ErpSaleOrderImportExcelVO> importList) { |
| | | ErpSaleOrderImportRespVO respVO = ErpSaleOrderImportRespVO.builder() |
| | | .createList(new ArrayList<>()) |
| | | .updateList(new ArrayList<>()) |
| | | .failureList(new LinkedHashMap<>()) |
| | | .build(); |
| | | if (CollUtil.isEmpty(importList)) { |
| | | return respVO; |
| | | } |
| | | |
| | | // 1. é¢çâ客æ·åç§° -> 客æ·ç¼å·âæ å°ï¼å®¢æ·ä¸»æ°æ®å¨ CRM 模åï¼ |
| | | Map<String, Long> customerIdMap = new HashMap<>(); |
| | | List<CrmCustomerRespDTO> customers = customerApi.getCustomerSimpleList(); |
| | | if (CollUtil.isNotEmpty(customers)) { |
| | | customers.forEach(customer -> { |
| | | if (StrUtil.isNotBlank(customer.getName())) { |
| | | customerIdMap.putIfAbsent(customer.getName().trim(), customer.getId()); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 2. æâéå®åç¼å·âåç»ï¼åç¼å·å¤è¡ = ä¸å¼ 订åç夿¡æç»ï¼ç¼å·ä¸ºç©ºåæ¯è¡ç¬ç«æå |
| | | Map<String, List<ErpSaleOrderImportExcelVO>> orderGroups = new LinkedHashMap<>(); |
| | | for (int i = 0; i < importList.size(); i++) { |
| | | ErpSaleOrderImportExcelVO row = importList.get(i); |
| | | if (row == null || (StrUtil.isBlank(row.getCustomerName()) && StrUtil.isBlank(row.getProductCode()) |
| | | && StrUtil.isBlank(row.getNo()))) { |
| | | continue; // è·³è¿ç©ºè¡ |
| | | } |
| | | String key = StrUtil.isNotBlank(row.getNo()) ? row.getNo().trim() : "__row_" + (i + 2); |
| | | orderGroups.computeIfAbsent(key, k -> new ArrayList<>()).add(row); |
| | | } |
| | | |
| | | // 3. é个订å导å
¥ï¼åå¼ è®¢å失败ä¸å½±åå
¶å®ï¼ |
| | | for (Map.Entry<String, List<ErpSaleOrderImportExcelVO>> entry : orderGroups.entrySet()) { |
| | | List<ErpSaleOrderImportExcelVO> rows = entry.getValue(); |
| | | String label = StrUtil.isNotBlank(rows.get(0).getNo()) ? rows.get(0).getNo().trim() |
| | | : "第 " + entry.getKey().substring("__row_".length()) + " è¡"; |
| | | try { |
| | | ErpSaleOrderDO saleOrder = createSaleOrderByImport(rows, customerIdMap); |
| | | respVO.getCreateList().add(saleOrder.getNo()); |
| | | } catch (Exception ex) { |
| | | respVO.getFailureList().put(label, resolveImportErrorMessage(ex)); |
| | | } |
| | | } |
| | | return respVO; |
| | | } |
| | | |
| | | /** |
| | | * 便®å¯¼å
¥æ°æ®å建ä¸å¼ éå®è®¢åï¼å«æç»ï¼ã |
| | | * |
| | | * 注æï¼æ¤å¤ä¸è°ç¨ submitSaleOrderï¼å æ¤ä¸ä¼å建 BPM 工使µï¼è®¢åç¶æåºå®ä¸ºâæªæäº¤(0)âï¼ |
| | | * å¯å¨å表ä¸ç»§ç»ç¼è¾ææäº¤å®¡æ¹ã |
| | | */ |
| | | private ErpSaleOrderDO createSaleOrderByImport(List<ErpSaleOrderImportExcelVO> rows, |
| | | Map<String, Long> customerIdMap) { |
| | | // 1. 客æ·ï¼å该ç»é¦è¡ï¼ |
| | | ErpSaleOrderImportExcelVO head = rows.get(0); |
| | | if (StrUtil.isBlank(head.getCustomerName())) { |
| | | throw new IllegalArgumentException("客æ·åç§°ä¸è½ä¸ºç©º"); |
| | | } |
| | | Long customerId = customerIdMap.get(head.getCustomerName().trim()); |
| | | if (customerId == null) { |
| | | throw new IllegalArgumentException("客æ·ä¸åå¨ï¼" + head.getCustomerName()); |
| | | } |
| | | customerApi.validateCustomer(customerId); |
| | | |
| | | // 2. æç» |
| | | List<ErpSaleOrderSaveReqVO.Item> items = new ArrayList<>(); |
| | | for (ErpSaleOrderImportExcelVO row : rows) { |
| | | if (StrUtil.isBlank(row.getProductCode())) { |
| | | throw new IllegalArgumentException("产åç¼ç ä¸è½ä¸ºç©º"); |
| | | } |
| | | CommonResult<MdmItemRespDTO> itemResult = mdmItemApi.getItemByCode(row.getProductCode().trim()); |
| | | MdmItemRespDTO item = itemResult == null ? null : itemResult.getData(); |
| | | if (item == null) { |
| | | throw new IllegalArgumentException("产åä¸åå¨ï¼" + row.getProductCode()); |
| | | } |
| | | if (row.getCount() == null) { |
| | | throw new IllegalArgumentException("产å[" + row.getProductCode() + "]çæ°éä¸è½ä¸ºç©º"); |
| | | } |
| | | if (item.getUnitMeasureId() == null) { |
| | | throw new IllegalArgumentException("产å[" + row.getProductCode() + "]æªé
置计éåä½ï¼è¯·å
å¨ç©æä¸ç»´æ¤"); |
| | | } |
| | | ErpSaleOrderSaveReqVO.Item itemVO = new ErpSaleOrderSaveReqVO.Item(); |
| | | itemVO.setProductId(item.getId()); |
| | | itemVO.setProductUnitId(item.getUnitMeasureId()); |
| | | // åä»·ï¼æªå¡«åæ¶åç©æçéå®ä»·ï¼ä¸é¡µé¢é产ååçé»è®¤å¼ä¸è´ï¼ |
| | | itemVO.setProductPrice(row.getProductPrice() != null ? row.getProductPrice() |
| | | : ObjectUtil.defaultIfNull(item.getSalesPrice(), BigDecimal.ZERO)); |
| | | itemVO.setCount(row.getCount()); |
| | | itemVO.setTaxPercent(row.getTaxPercent()); |
| | | itemVO.setNeedProduction(parseNeedProduction(row.getNeedProduction())); |
| | | itemVO.setRemark(row.getRemark()); |
| | | items.add(itemVO); |
| | | } |
| | | |
| | | // 3. åå·ï¼å¡«äºå°±ç¨ï¼æ ¡éªå¯ä¸ï¼ï¼æ²¡å¡«èªå¨çæ |
| | | String no = StrUtil.isNotBlank(head.getNo()) ? head.getNo().trim() |
| | | : noRedisDAO.generate(ErpNoRedisDAO.SALE_ORDER_NO_PREFIX); |
| | | if (saleOrderMapper.selectByNo(no) != null) { |
| | | throw new IllegalArgumentException("éå®åç¼å·å·²åå¨ï¼" + no); |
| | | } |
| | | |
| | | // 4. ç»è£
主表ï¼å¯¼å
¥ååºå®ä¸ºâæªæäº¤(0)âç¶æ |
| | | ErpSaleOrderDO saleOrder = new ErpSaleOrderDO(); |
| | | saleOrder.setNo(no); |
| | | saleOrder.setCustomerId(customerId); |
| | | saleOrder.setContractNo(head.getContractNo()); |
| | | saleOrder.setOrderTime(parseImportOrderTime(head.getOrderTime())); |
| | | saleOrder.setRemark(head.getRemark()); |
| | | saleOrder.setStatus(ErpAuditStatus.DRAFT.getStatus()); |
| | | saleOrder.setDepositPrice(BigDecimal.ZERO); |
| | | saleOrder.setOutCount(BigDecimal.ZERO); |
| | | saleOrder.setOutStatus(ErpSaleOrderOutStatusEnum.NOT_OUT.getStatus()); |
| | | |
| | | List<ErpSaleOrderItemDO> itemDOs = validateSaleOrderItems(items); |
| | | calculateTotalPrice(saleOrder, itemDOs); |
| | | saleOrderMapper.insert(saleOrder); |
| | | itemDOs.forEach(item -> item.setOrderId(saleOrder.getId())); |
| | | saleOrderItemMapper.insertBatch(itemDOs); |
| | | return saleOrder; |
| | | } |
| | | |
| | | /** |
| | | * è§£æâæ¯å¦éè¦ç产âï¼ç空é»è®¤âæ¯(1)âï¼å¡«âå¦/0/ä¸éè¦âå为âå¦(0)â |
| | | */ |
| | | private Integer parseNeedProduction(String text) { |
| | | if (StrUtil.isBlank(text)) { |
| | | return 1; // ä¸é¡µé¢æ°å¢æç»çé»è®¤å¼ä¸è´ |
| | | } |
| | | String value = text.trim(); |
| | | boolean no = "å¦".equals(value) || "0".equals(value) || "ä¸éè¦".equals(value) |
| | | || "false".equalsIgnoreCase(value) || "no".equalsIgnoreCase(value); |
| | | return no ? 0 : 1; |
| | | } |
| | | |
| | | /** |
| | | * è§£æå¯¼å
¥çä¸åæ¶é´ï¼å
¼å®¹å¤ç§å¸¸è§æ ¼å¼ï¼ä¸ºç©ºåå½åæ¶é´ |
| | | */ |
| | | private LocalDateTime parseImportOrderTime(String text) { |
| | | if (StrUtil.isBlank(text)) { |
| | | return LocalDateTime.now(); |
| | | } |
| | | String value = text.trim(); |
| | | for (String pattern : new String[]{"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "yyyy/MM/dd HH:mm:ss"}) { |
| | | try { |
| | | return LocalDateTime.parse(value, DateTimeFormatter.ofPattern(pattern)); |
| | | } catch (Exception ignored) { |
| | | // ç»§ç»å°è¯ä¸ä¸ä¸ªæ ¼å¼ |
| | | } |
| | | } |
| | | for (String pattern : new String[]{"yyyy-MM-dd", "yyyy/M/d"}) { |
| | | try { |
| | | return LocalDate.parse(value, DateTimeFormatter.ofPattern(pattern)).atStartOfDay(); |
| | | } catch (Exception ignored) { |
| | | // ç»§ç»å°è¯ä¸ä¸ä¸ªæ ¼å¼ |
| | | } |
| | | } |
| | | throw new IllegalArgumentException("ä¸åæ¶é´æ ¼å¼ä¸æ£ç¡®ï¼" + text + "ï¼ç¤ºä¾ 2026-01-01 10:00:00"); |
| | | } |
| | | |
| | | /** |
| | | * æå导å
¥å¤±è´¥åå ï¼ServiceException åä¸å¡æç¤ºï¼ |
| | | */ |
| | | private String resolveImportErrorMessage(Exception ex) { |
| | | if (ex instanceof cn.iocoder.yudao.framework.common.exception.ServiceException serviceException) { |
| | | return serviceException.getMessage(); |
| | | } |
| | | return StrUtil.blankToDefault(ex.getMessage(), "导å
¥å¤±è´¥"); |
| | | } |
| | | |
| | | private void calculateTotalPrice(ErpSaleOrderDO saleOrder, List<ErpSaleOrderItemDO> saleOrderItems) { |
| | | saleOrder.setTotalCount(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getCount, BigDecimal::add)); |
| | | saleOrder.setTotalProductPrice(getSumValue(saleOrderItems, ErpSaleOrderItemDO::getTotalPrice, BigDecimal::add, BigDecimal.ZERO)); |
| | |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringImportExcelVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringImportRespVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.dv.metering.MesDvMeteringDO; |
| | | import cn.iocoder.yudao.module.mes.enums.dv.MesDvMeteringExpireStatusEnum; |
| | | import cn.iocoder.yudao.module.mes.service.dv.metering.MesDvMeteringService; |
| | |
| | | import org.springframework.security.access.prepost.PreAuthorize; |
| | | import org.springframework.validation.annotation.Validated; |
| | | import org.springframework.web.bind.annotation.*; |
| | | import org.springframework.web.multipart.MultipartFile; |
| | | |
| | | import java.io.IOException; |
| | | import java.time.LocalDate; |
| | | import java.time.temporal.ChronoUnit; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | buildMeteringRespVOList(list)); |
| | | } |
| | | |
| | | @GetMapping("/get-import-template") |
| | | @Operation(summary = "è·å¾è®¡éå¨å
·å¯¼å
¥æ¨¡æ¿") |
| | | @PreAuthorize("@ss.hasPermission('mes:dv-metering:import')") |
| | | public void importTemplate(HttpServletResponse response) throws IOException { |
| | | List<MesDvMeteringImportExcelVO> list = Collections.singletonList( |
| | | MesDvMeteringImportExcelVO.builder() |
| | | .code("JL-001").name("ç¤ºä¾æ¸¸æ å¡å°º").category(1).specification("0-150mm") |
| | | .brand("示ä¾åç").precision("0.02mm").range("0-150mm") |
| | | .deptName("ç åé¨é¨").chargeUserName("è¶
级管çå") |
| | | .checkCycleCount(12).useDate("2026-01-01").status(1).remark("ç¤ºä¾æ°æ®ï¼å¯¼å
¥åè¯·æ¿æ¢") |
| | | .build()); |
| | | ExcelUtils.write(response, "计éå¨å
·å¯¼å
¥æ¨¡æ¿.xls", "计éå¨å
·", MesDvMeteringImportExcelVO.class, list); |
| | | } |
| | | |
| | | @PostMapping("/import") |
| | | @Operation(summary = "导å
¥è®¡éå¨å
·") |
| | | @PreAuthorize("@ss.hasPermission('mes:dv-metering:import')") |
| | | public CommonResult<MesDvMeteringImportRespVO> importMeteringExcel( |
| | | @RequestParam("file") MultipartFile file) throws Exception { |
| | | List<MesDvMeteringImportExcelVO> list = ExcelUtils.read(file, MesDvMeteringImportExcelVO.class); |
| | | return success(meteringService.importMeteringList(list)); |
| | | } |
| | | |
| | | @GetMapping("/simple-list") |
| | | @Operation(summary = "è·å¾è®¡éå¨å
·ç²¾ç®å表", description = "主è¦ç¨äºå端ç䏿é项") |
| | | @PreAuthorize("@ss.hasPermission('mes:dv-metering:query')") |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo; |
| | | |
| | | import cn.idev.excel.annotation.ExcelProperty; |
| | | import cn.iocoder.yudao.framework.excel.core.annotations.DictFormat; |
| | | import cn.iocoder.yudao.framework.excel.core.convert.DictConvert; |
| | | import cn.iocoder.yudao.module.mes.enums.DictTypeConstants; |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.AllArgsConstructor; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | import lombok.NoArgsConstructor; |
| | | |
| | | /** |
| | | * 管çåå° - 计éå¨å
·å¯¼å
¥ Excel VO |
| | | * |
| | | * 说æï¼ä¸å¯¼åºåä¿æä¸è´ï¼åé¤ç³»ç»åï¼ç¼å·ã䏿¬¡/䏿¬¡æ£æµæ¥æãå°æç¶æãå©ä½å¤©æ°ãå建æ¶é´ï¼ã |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - 计éå¨å
·å¯¼å
¥ Excel VO") |
| | | @Data |
| | | @Builder |
| | | @AllArgsConstructor |
| | | @NoArgsConstructor |
| | | public class MesDvMeteringImportExcelVO { |
| | | |
| | | @ExcelProperty("计éå¨å
·ç¼ç ") |
| | | @Schema(description = "计éå¨å
·ç¼ç ï¼å¯ä¸", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private String code; |
| | | |
| | | @ExcelProperty("计éå¨å
·åç§°") |
| | | @Schema(description = "计éå¨å
·åç§°", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private String name; |
| | | |
| | | @ExcelProperty(value = "å¨å
·ç±»å«", converter = DictConvert.class) |
| | | @DictFormat(DictTypeConstants.MES_DV_METERING_CATEGORY) |
| | | @Schema(description = "å¨å
·ç±»å«ï¼å¡«åå
¸ä¸æï¼å¦ å¡å°º/å
¶ä»ï¼", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private Integer category; |
| | | |
| | | @ExcelProperty("è§æ ¼åå·") |
| | | @Schema(description = "è§æ ¼åå·") |
| | | private String specification; |
| | | |
| | | @ExcelProperty("åç/ç产åå®¶") |
| | | @Schema(description = "åç/ç产åå®¶") |
| | | private String brand; |
| | | |
| | | @ExcelProperty("精度") |
| | | @Schema(description = "精度") |
| | | private String precision; |
| | | |
| | | @ExcelProperty("éç¨") |
| | | @Schema(description = "éç¨") |
| | | private String range; |
| | | |
| | | @ExcelProperty("使ç¨é¨é¨") |
| | | @Schema(description = "使ç¨é¨é¨åç§°ï¼éä¸ç³»ç»é¨é¨åç§°ä¸è´ï¼") |
| | | private String deptName; |
| | | |
| | | @ExcelProperty("责任人") |
| | | @Schema(description = "责任人æµç§°ï¼éä¸ç³»ç»ç¨æ·æµç§°ä¸è´ï¼") |
| | | private String chargeUserName; |
| | | |
| | | @ExcelProperty("æ ¡å卿(æ)") |
| | | @Schema(description = "æ ¡åå¨æï¼æï¼ï¼ç空é»è®¤ 12", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private Integer checkCycleCount; |
| | | |
| | | @ExcelProperty("æå
¥ä½¿ç¨æ¥æ") |
| | | @Schema(description = "æå
¥ä½¿ç¨æ¥æï¼æ ¼å¼ yyyy-MM-dd") |
| | | private String useDate; |
| | | |
| | | @ExcelProperty(value = "ç¶æ", converter = DictConvert.class) |
| | | @DictFormat(DictTypeConstants.MES_DV_METERING_STATUS) |
| | | @Schema(description = "ç¶æï¼å¡«åå
¸ä¸æï¼å¦ å¨ç¨/åç¨/æ¥åºï¼", requiredMode = Schema.RequiredMode.REQUIRED) |
| | | private Integer status; |
| | | |
| | | @ExcelProperty("夿³¨") |
| | | @Schema(description = "夿³¨") |
| | | private String remark; |
| | | |
| | | } |
| ¶Ô±ÈÐÂÎļþ |
| | |
| | | package cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo; |
| | | |
| | | import io.swagger.v3.oas.annotations.media.Schema; |
| | | import lombok.Builder; |
| | | import lombok.Data; |
| | | |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | | /** |
| | | * 管çåå° - 计éå¨å
·å¯¼å
¥ Response VO |
| | | * |
| | | * @author è¶
级管çå |
| | | */ |
| | | @Schema(description = "管çåå° - 计éå¨å
·å¯¼å
¥ Response VO") |
| | | @Data |
| | | @Builder |
| | | public class MesDvMeteringImportRespVO { |
| | | |
| | | @Schema(description = "å建æåç计éå¨å
·ç¼ç æ°ç»", example = "JL-001") |
| | | private List<String> createList; |
| | | |
| | | @Schema(description = "æ´æ°æåç计éå¨å
·ç¼ç æ°ç»") |
| | | private List<String> updateList; |
| | | |
| | | @Schema(description = "导å
¥å¤±è´¥éåï¼key 为计éå¨å
·ç¼ç æè¡å·ï¼value 为失败åå ") |
| | | private Map<String, String> failureList; |
| | | |
| | | } |
| | |
| | | package cn.iocoder.yudao.module.mes.dal.mysql.dv.metering; |
| | | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import com.baomidou.mybatisplus.annotation.InterceptorIgnore; |
| | | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| | | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.dv.metering.MesDvMeteringDO; |
| | | import org.apache.ibatis.annotations.Mapper; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.apache.ibatis.annotations.Select; |
| | | import org.apache.ibatis.annotations.Update; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.List; |
| | |
| | | return selectOne(MesDvMeteringDO::getCode, code); |
| | | } |
| | | |
| | | /** |
| | | * æç¼ç æ¥è¯¢ï¼å«â已被é»è¾å é¤âçè®°å½ï¼ââç¨äºå¯¼å
¥æ¶å¤é |
| | | */ |
| | | @InterceptorIgnore(tenantLine = "true", dataPermission = "true") |
| | | @Select("SELECT * FROM mes_dv_metering WHERE code = #{code} ORDER BY id DESC LIMIT 1") |
| | | MesDvMeteringDO selectByCodeIncludeDeleted(@Param("code") String code); |
| | | |
| | | /** |
| | | * æ¢å¤è¢«é»è¾å é¤çè®°å½ï¼æ deleted ç½®å 0ï¼ |
| | | */ |
| | | @InterceptorIgnore(tenantLine = "true", dataPermission = "true") |
| | | @Update("UPDATE mes_dv_metering SET deleted = 0 WHERE id = #{id}") |
| | | int restoreDeletedById(@Param("id") Long id); |
| | | |
| | | default List<MesDvMeteringDO> selectListByNextCheckDateBetween(LocalDate begin, LocalDate end) { |
| | | return selectList(new LambdaQueryWrapperX<MesDvMeteringDO>() |
| | | .ge(MesDvMeteringDO::getNextCheckDate, begin) |
| | |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringImportExcelVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringImportRespVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.dv.metering.MesDvMeteringDO; |
| | | import jakarta.validation.Valid; |
| | | |
| | |
| | | Long createMetering(@Valid MesDvMeteringSaveReqVO createReqVO); |
| | | |
| | | /** |
| | | * 导å
¥è®¡éå¨å
·ï¼å¯¼å
¥å为æ£å¸¸æ°æ®ï¼ç¼ç å·²åå¨åè®°å
¥å¤±è´¥åè¡¨ï¼ |
| | | * |
| | | * @param importList 导å
¥æ°æ® |
| | | * @return 导å
¥ç»æ |
| | | */ |
| | | MesDvMeteringImportRespVO importMeteringList(List<MesDvMeteringImportExcelVO> importList); |
| | | |
| | | /** |
| | | * æ´æ°è®¡éå¨å
· |
| | | * |
| | | * @param updateReqVO æ´æ°ä¿¡æ¯ |
| | |
| | | package cn.iocoder.yudao.module.mes.service.dv.metering; |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringImportExcelVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.dv.metering.vo.MesDvMeteringImportRespVO; |
| | | 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.user.AdminUserApi; |
| | | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | |
| | | import jakarta.annotation.Resource; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.validation.annotation.Validated; |
| | | |
| | | import java.time.LocalDate; |
| | | import java.util.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.Map; |
| | | import java.util.Collection; |
| | | import java.util.Collections; |
| | | import java.util.List; |
| | |
| | | @Resource |
| | | private ConfigApi configApi; |
| | | |
| | | @Resource |
| | | private DeptApi deptApi; |
| | | @Resource |
| | | private AdminUserApi adminUserApi; |
| | | |
| | | @Override |
| | | public Long createMetering(MesDvMeteringSaveReqVO createReqVO) { |
| | | // æ ¡éªç¼ç å¯ä¸ |
| | |
| | | } |
| | | |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public MesDvMeteringImportRespVO importMeteringList(List<MesDvMeteringImportExcelVO> importList) { |
| | | MesDvMeteringImportRespVO respVO = MesDvMeteringImportRespVO.builder() |
| | | .createList(new ArrayList<>()) |
| | | .updateList(new ArrayList<>()) |
| | | .failureList(new LinkedHashMap<>()) |
| | | .build(); |
| | | if (CollUtil.isEmpty(importList)) { |
| | | return respVO; |
| | | } |
| | | |
| | | // 1. é¢çâé¨é¨åç§° -> é¨é¨ç¼å·âæ å°ï¼é¨é¨/责任人为éå¿
å¡«ï¼å¹é
ä¸å°è¯¥è¡å¤±è´¥ï¼ |
| | | Map<String, Long> deptIdMap = new HashMap<>(); |
| | | List<DeptRespDTO> depts = deptApi.getDeptList(); |
| | | if (CollUtil.isNotEmpty(depts)) { |
| | | depts.forEach(dept -> { |
| | | if (StrUtil.isNotBlank(dept.getName())) { |
| | | deptIdMap.putIfAbsent(dept.getName().trim(), dept.getId()); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 2. éè¡å¯¼å
¥ï¼åè¡å¤±è´¥ä¸å½±åå
¶å®è¡ï¼ |
| | | for (int i = 0; i < importList.size(); i++) { |
| | | MesDvMeteringImportExcelVO row = importList.get(i); |
| | | if (row == null || (StrUtil.isBlank(row.getCode()) && StrUtil.isBlank(row.getName()))) { |
| | | continue; // è·³è¿ç©ºè¡ |
| | | } |
| | | String label = StrUtil.isNotBlank(row.getCode()) ? row.getCode().trim() : "第 " + (i + 2) + " è¡"; |
| | | try { |
| | | boolean restored = createMeteringByImport(row, deptIdMap); |
| | | if (restored) { |
| | | respVO.getUpdateList().add(row.getCode().trim()); // æ¢å¤/è¦çæ´æ° |
| | | } else { |
| | | respVO.getCreateList().add(row.getCode().trim()); |
| | | } |
| | | } catch (Exception ex) { |
| | | respVO.getFailureList().put(label, resolveImportErrorMessage(ex)); |
| | | } |
| | | } |
| | | return respVO; |
| | | } |
| | | |
| | | /** |
| | | * 便®å¯¼å
¥è¡åå»ºä¸æ¡è®¡éå¨å
·è®°å½ |
| | | */ |
| | | private boolean createMeteringByImport(MesDvMeteringImportExcelVO row, Map<String, Long> deptIdMap) { |
| | | // 1. å¿
å¡«æ ¡éª |
| | | if (StrUtil.isBlank(row.getCode())) { |
| | | throw new IllegalArgumentException("计éå¨å
·ç¼ç ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (StrUtil.isBlank(row.getName())) { |
| | | throw new IllegalArgumentException("计éå¨å
·åç§°ä¸è½ä¸ºç©º"); |
| | | } |
| | | if (row.getCategory() == null) { |
| | | throw new IllegalArgumentException("å¨å
·ç±»å«ä¸è½ä¸ºç©ºï¼è¯·å¡«åå
¸ä¸æï¼å¦ å¡å°º/å
¶ä»ï¼"); |
| | | } |
| | | if (row.getStatus() == null) { |
| | | throw new IllegalArgumentException("ç¶æä¸è½ä¸ºç©ºï¼è¯·å¡«åå
¸ä¸æï¼å¦ å¨ç¨/åç¨/æ¥åºï¼"); |
| | | } |
| | | // 2. ç¼ç å¤éï¼å«â已被é»è¾å é¤âçè®°å½ï¼ï¼æªå é¤ => æ¥éï¼å·²å é¤ => ç¨åæ¢å¤å¹¶æ´æ° |
| | | MesDvMeteringDO exist = meteringMapper.selectByCodeIncludeDeleted(row.getCode().trim()); |
| | | if (exist != null && !Boolean.TRUE.equals(exist.getDeleted())) { |
| | | throw new IllegalArgumentException("计éå¨å
·ç¼ç å·²åå¨ï¼" + row.getCode()); |
| | | } |
| | | |
| | | // 3. ç»è£
|
| | | MesDvMeteringSaveReqVO reqVO = new MesDvMeteringSaveReqVO(); |
| | | reqVO.setCode(row.getCode().trim()); |
| | | reqVO.setName(row.getName().trim()); |
| | | reqVO.setCategory(row.getCategory()); |
| | | reqVO.setSpecification(row.getSpecification()); |
| | | reqVO.setBrand(row.getBrand()); |
| | | reqVO.setPrecision(row.getPrecision()); |
| | | reqVO.setRange(row.getRange()); |
| | | reqVO.setCheckCycleCount(row.getCheckCycleCount() == null ? 12 : row.getCheckCycleCount()); |
| | | reqVO.setUseDate(parseImportUseDate(row.getUseDate())); |
| | | reqVO.setStatus(row.getStatus()); |
| | | reqVO.setRemark(row.getRemark()); |
| | | // 使ç¨é¨é¨ï¼æåç§°å¹é
|
| | | if (StrUtil.isNotBlank(row.getDeptName())) { |
| | | Long deptId = deptIdMap.get(row.getDeptName().trim()); |
| | | if (deptId == null) { |
| | | throw new IllegalArgumentException("使ç¨é¨é¨ä¸åå¨ï¼" + row.getDeptName()); |
| | | } |
| | | reqVO.setDeptId(deptId); |
| | | } |
| | | // è´£ä»»äººï¼ææµç§°å¹é
|
| | | if (StrUtil.isNotBlank(row.getChargeUserName())) { |
| | | Long userId = matchUserIdByNickname(row.getChargeUserName().trim()); |
| | | if (userId == null) { |
| | | throw new IllegalArgumentException("责任人ä¸åå¨ï¼" + row.getChargeUserName()); |
| | | } |
| | | reqVO.setChargeUserId(userId); |
| | | } |
| | | |
| | | // 4. è½åºï¼å½ä¸âå·²å é¤âçåç¼ç => å
æ¢å¤åæ´æ°ï¼å¦åæ°å¢ |
| | | if (exist != null) { |
| | | meteringMapper.restoreDeletedById(exist.getId()); |
| | | reqVO.setId(exist.getId()); |
| | | updateMetering(reqVO); |
| | | return true; // è¡¨ç¤ºâæ¢å¤/æ´æ°â |
| | | } |
| | | createMetering(reqVO); |
| | | return false; |
| | | } |
| | | |
| | | /** |
| | | * ææµç§°å¹é
ç¨æ·ç¼å·ï¼ç²¾ç¡®å¹é
ä¼å
ï¼åä¸å°åç¨æ¨¡ç³ç»æç¬¬ä¸æ¡ï¼ |
| | | */ |
| | | private Long matchUserIdByNickname(String nickname) { |
| | | List<AdminUserRespDTO> users = adminUserApi.getUserListByNickname(nickname); |
| | | if (CollUtil.isEmpty(users)) { |
| | | return null; |
| | | } |
| | | return users.stream() |
| | | .filter(user -> nickname.equals(user.getNickname())) |
| | | .map(AdminUserRespDTO::getId) |
| | | .findFirst() |
| | | .orElseGet(() -> users.get(0).getId()); |
| | | } |
| | | |
| | | /** |
| | | * è§£ææå
¥ä½¿ç¨æ¥æï¼yyyy-MM-dd / yyyy/M/dï¼ï¼ä¸ºç©ºè¿å null |
| | | */ |
| | | private LocalDate parseImportUseDate(String text) { |
| | | if (StrUtil.isBlank(text)) { |
| | | return null; |
| | | } |
| | | String value = text.trim(); |
| | | for (String pattern : new String[]{"yyyy-MM-dd", "yyyy/M/d"}) { |
| | | try { |
| | | return LocalDate.parse(value, java.time.format.DateTimeFormatter.ofPattern(pattern)); |
| | | } catch (Exception ignored) { |
| | | // ç»§ç»å°è¯ä¸ä¸ä¸ªæ ¼å¼ |
| | | } |
| | | } |
| | | try { |
| | | return java.time.LocalDateTime.parse(value, |
| | | java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")).toLocalDate(); |
| | | } catch (Exception ignored) { |
| | | // å¿½ç¥ |
| | | } |
| | | throw new IllegalArgumentException("æå
¥ä½¿ç¨æ¥ææ ¼å¼ä¸æ£ç¡®ï¼" + text + "ï¼ç¤ºä¾ 2026-01-01"); |
| | | } |
| | | |
| | | /** |
| | | * æå导å
¥å¤±è´¥åå ï¼ServiceException åä¸å¡æç¤ºï¼ |
| | | */ |
| | | private String resolveImportErrorMessage(Exception ex) { |
| | | if (ex instanceof cn.iocoder.yudao.framework.common.exception.ServiceException serviceException) { |
| | | return serviceException.getMessage(); |
| | | } |
| | | return StrUtil.blankToDefault(ex.getMessage(), "导å
¥å¤±è´¥"); |
| | | } |
| | | |
| | | @Override |
| | | public void updateMetering(MesDvMeteringSaveReqVO updateReqVO) { |
| | | // æ ¡éªåå¨ |
| | | validateMeteringExists(updateReqVO.getId()); |