| | |
| | | |
| | | import cn.hutool.core.collection.CollUtil; |
| | | import cn.hutool.core.util.ObjUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| | | import cn.iocoder.yudao.framework.common.exception.ServiceException; |
| | | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| | | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| | | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| | | import cn.iocoder.yudao.module.crm.api.customer.dto.CrmCustomerRespDTO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesImportExcelVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesImportRespVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesPageReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.MesWmProductSalesShippingReqVO; |
| | | import cn.iocoder.yudao.module.mes.controller.admin.wm.productsales.vo.line.MesWmProductSalesLineSaveReqVO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.batch.MesWmBatchDO; |
| | | import cn.iocoder.yudao.module.mes.enums.md.autocode.MesMdAutoCodeRuleCodeEnum; |
| | | import cn.iocoder.yudao.module.mes.service.md.autocode.MesMdAutoCodeRecordService; |
| | | import cn.iocoder.yudao.module.mes.service.wm.batch.MesWmBatchService; |
| | | import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi; |
| | | import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.salesnotice.MesWmSalesNoticeDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDO; |
| | | import cn.iocoder.yudao.module.mes.dal.dataobject.wm.productsales.MesWmProductSalesDetailDO; |
| | |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.PlatformTransactionManager; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.transaction.support.TransactionTemplate; |
| | | 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.ArrayList; |
| | | import java.util.HashMap; |
| | | import java.util.LinkedHashMap; |
| | | 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.convertList; |
| | |
| | | |
| | | @Resource |
| | | private ErpSaleOrderApi saleOrderApi; |
| | | |
| | | @Resource |
| | | private MdmItemApi mdmItemApi; |
| | | @Resource |
| | | private MesWmBatchService batchService; |
| | | @Resource |
| | | private MesMdAutoCodeRecordService autoCodeRecordService; |
| | | @Resource |
| | | private PlatformTransactionManager transactionManager; |
| | | |
| | | @Override |
| | | public Long createProductSales(MesWmProductSalesSaveReqVO createReqVO) { |
| | |
| | | productSalesMapper.updateById(new MesWmProductSalesDO().setId(id).setStatus(status)); |
| | | } |
| | | |
| | | // ==================== 导å
¥ ==================== |
| | | |
| | | @Override |
| | | public MesWmProductSalesImportRespVO importProductSalesList(List<MesWmProductSalesImportExcelVO> importList) { |
| | | MesWmProductSalesImportRespVO respVO = MesWmProductSalesImportRespVO.builder() |
| | | .createList(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 (customer != null && StrUtil.isNotBlank(customer.getName())) { |
| | | customerIdMap.putIfAbsent(customer.getName().trim(), customer.getId()); |
| | | } |
| | | }); |
| | | } |
| | | |
| | | // 2. æâåºåºåå·âåç»ï¼ååå·å¤è¡ = ä¸å¼ åºåºåç夿¡æç»ï¼åå·ä¸ºç©ºåæ¯è¡ç¬ç«æå |
| | | Map<String, List<ImportRow>> orderGroups = new LinkedHashMap<>(); |
| | | for (int i = 0; i < importList.size(); i++) { |
| | | MesWmProductSalesImportExcelVO row = importList.get(i); |
| | | if (row == null || isEmptyImportRow(row)) { |
| | | continue; // è·³è¿ç©ºè¡ |
| | | } |
| | | String key = StrUtil.isNotBlank(row.getCode()) ? row.getCode().trim() : "__row_" + (i + 2); |
| | | orderGroups.computeIfAbsent(key, k -> new ArrayList<>()).add(new ImportRow(row, i + 2)); |
| | | } |
| | | |
| | | // 3. éå导å
¥ï¼æ¯å¼ åç¬ç«äºå¡ï¼å¤±è´¥çåæ´ååæ»ï¼ä¸å½±åå
¶å®å |
| | | TransactionTemplate importTxTemplate = new TransactionTemplate(transactionManager); |
| | | for (Map.Entry<String, List<ImportRow>> entry : orderGroups.entrySet()) { |
| | | List<ImportRow> rows = entry.getValue(); |
| | | String label = StrUtil.isNotBlank(rows.get(0).data().getCode()) |
| | | ? rows.get(0).data().getCode().trim() : "第 " + rows.get(0).rowNum() + " è¡"; |
| | | try { |
| | | String code = importTxTemplate.execute(status -> createProductSalesByImport(rows, customerIdMap)); |
| | | respVO.getCreateList().add(code); |
| | | } catch (Exception ex) { |
| | | respVO.getFailureList().put(label, resolveImportErrorMessage(ex)); |
| | | } |
| | | } |
| | | return respVO; |
| | | } |
| | | |
| | | /** |
| | | * 便®åä¸åºåºåå·çå¤è¡å¯¼å
¥æ°æ®ï¼å建ä¸å¼ éå®åºåºåï¼å头 + æç»ï¼ |
| | | * |
| | | * @return åºåºåå· |
| | | */ |
| | | private String createProductSalesByImport(List<ImportRow> rows, Map<String, Long> customerIdMap) { |
| | | ImportRow headRow = rows.get(0); |
| | | MesWmProductSalesImportExcelVO head = headRow.data(); |
| | | String rowPrefix = "第 " + headRow.rowNum() + " è¡ï¼"; |
| | | |
| | | // 1. 客æ·ï¼å该ç»é¦è¡ï¼ |
| | | if (StrUtil.isBlank(head.getClientName())) { |
| | | throw new IllegalArgumentException(rowPrefix + "客æ·åç§°ä¸è½ä¸ºç©º"); |
| | | } |
| | | String clientName = head.getClientName().trim(); |
| | | Long clientId = customerIdMap.get(clientName); |
| | | if (clientId == null) { |
| | | throw new IllegalArgumentException(rowPrefix + "客æ·åç§°[" + clientName |
| | | + "]å¨ CRM ä¸ä¸åå¨ï¼è¯·æ ¸å¯¹åç§°æ¯å¦ä¸ç³»ç»å®¢æ·å®å
¨ä¸è´ï¼å«å
¨åè§ãç©ºæ ¼ï¼"); |
| | | } |
| | | |
| | | // 2. åºåºæ¥æï¼ç空åå½å¤©ï¼ |
| | | LocalDateTime salesDate = parseImportSalesDate(head.getSalesDate(), rowPrefix); |
| | | |
| | | // 3. åºåºåå·ï¼å¡«äºå°±ç¨ï¼æ ¡éªå¯ä¸ï¼ï¼æ²¡å¡«èªå¨çæ |
| | | String code = StrUtil.isNotBlank(head.getCode()) ? head.getCode().trim() |
| | | : autoCodeRecordService.generateAutoCode(MesMdAutoCodeRuleCodeEnum.WM_PRODUCT_SALES_CODE.getCode()); |
| | | if (StrUtil.isBlank(code)) { |
| | | throw new IllegalArgumentException(rowPrefix |
| | | + "åºåºåå·èªå¨çæå¤±è´¥ï¼è¯·ç¨åéè¯ï¼æå¨è¯¥åæå·¥å¡«ååºåºåå·"); |
| | | } |
| | | if (productSalesMapper.selectByCode(code) != null) { |
| | | throw new IllegalArgumentException(rowPrefix + "åºåºåå·[" + code |
| | | + "]å·²åå¨ï¼è¯·ä¿®æ¹è¯¥åææ¸
空该åç±ç³»ç»èªå¨çæ"); |
| | | } |
| | | |
| | | // 4. å建å头ï¼èç¨¿ç¶æï¼ä¸æäº¤ãä¸å ç¨åºåï¼ |
| | | MesWmProductSalesSaveReqVO saveReqVO = new MesWmProductSalesSaveReqVO(); |
| | | saveReqVO.setCode(code); |
| | | saveReqVO.setName(StrUtil.isNotBlank(head.getName()) ? head.getName().trim() |
| | | : clientName + "-" + salesDate.toLocalDate() + " åºåº"); |
| | | saveReqVO.setClientId(clientId); |
| | | saveReqVO.setSalesDate(salesDate); |
| | | saveReqVO.setSalesOrderCode(StrUtil.trimToNull(head.getSalesOrderCode())); |
| | | saveReqVO.setContactName(StrUtil.trimToNull(head.getContactName())); |
| | | saveReqVO.setContactTelephone(StrUtil.trimToNull(head.getContactTelephone())); |
| | | saveReqVO.setContactAddress(StrUtil.trimToNull(head.getContactAddress())); |
| | | saveReqVO.setRemark(StrUtil.trimToNull(head.getRemark())); |
| | | Long salesId = createProductSales(saveReqVO); |
| | | |
| | | // 5. éè¡å建æç» |
| | | for (ImportRow row : rows) { |
| | | createProductSalesLineByImport(salesId, row); |
| | | } |
| | | return code; |
| | | } |
| | | |
| | | /** |
| | | * 便®å¯¼å
¥è¡åå»ºä¸æ¡éå®åºåºåè¡ |
| | | */ |
| | | private void createProductSalesLineByImport(Long salesId, ImportRow row) { |
| | | MesWmProductSalesImportExcelVO data = row.data(); |
| | | String rowPrefix = "第 " + row.rowNum() + " è¡ï¼"; |
| | | |
| | | // 1. ç©æç¼ç ï¼å¿
å¡«ï¼ä¸å¿
é¡»åå¨ãå¯ç¨ï¼ |
| | | if (StrUtil.isBlank(data.getItemCode())) { |
| | | throw new IllegalArgumentException(rowPrefix + "ç©æç¼ç ä¸è½ä¸ºç©º"); |
| | | } |
| | | String itemCode = data.getItemCode().trim(); |
| | | MdmItemRespDTO item = mdmItemApi.getItemByCode(itemCode).getCheckedData(); |
| | | if (item == null) { |
| | | throw new IllegalArgumentException(rowPrefix + "ç©æç¼ç [" + itemCode |
| | | + "]ä¸åå¨ï¼è¯·å
å°ãåºç¡æ°æ®-ç©æä¸»æ°æ®ãä¸ç»´æ¤è¯¥ç©æ"); |
| | | } |
| | | if (!CommonStatusEnum.ENABLE.getStatus().equals(item.getStatus())) { |
| | | throw new IllegalArgumentException(rowPrefix + "ç©æç¼ç [" + itemCode |
| | | + "-" + item.getName() + "]å·²åç¨ï¼è¯·å
å¨ç©æä¸»æ°æ®ä¸å¯ç¨åå导å
¥"); |
| | | } |
| | | |
| | | // 2. åºåºæ°éï¼å¿
å¡«ï¼ä¸»åä½å£å¾ï¼ |
| | | if (data.getQuantity() == null) { |
| | | throw new IllegalArgumentException(rowPrefix + "åºåºæ°éä¸è½ä¸ºç©ºï¼ç©æ[" + itemCode + "]ï¼"); |
| | | } |
| | | if (data.getQuantity().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new IllegalArgumentException(rowPrefix + "åºåºæ°éå¿
é¡»å¤§äº 0ï¼å½åå¡«å为[" |
| | | + data.getQuantity().toPlainString() + "]ï¼ç©æ[" + itemCode + "]ï¼"); |
| | | } |
| | | |
| | | // 3. æ¹æ¬¡å·ï¼å¯ç©ºï¼å¡«äºå¿
é¡»æ¯å·²åå¨ä¸å½å±è¯¥ç©æçæ¹æ¬¡ï¼ |
| | | String batchCode = StrUtil.trimToNull(data.getBatchCode()); |
| | | if (batchCode != null) { |
| | | MesWmBatchDO batch = batchService.getBatchByCode(batchCode); |
| | | if (batch == null) { |
| | | throw new IllegalArgumentException(rowPrefix + "æ¹æ¬¡å·[" + batchCode |
| | | + "]ä¸åå¨ï¼è¯·å
å¨ãæ¹æ¬¡ç®¡çãä¸åå»ºï¼ææ¸
空该è¡çæ¹æ¬¡å·"); |
| | | } |
| | | if (batch.getItemId() != null && !ObjUtil.equal(batch.getItemId(), item.getId())) { |
| | | String batchItemText = resolveItemText(batch.getItemId()); |
| | | throw new IllegalArgumentException(rowPrefix + "æ¹æ¬¡å·[" + batchCode + "]å±äºç©æ[" |
| | | + batchItemText + "]ï¼ä¸å½åè¡ç©æ[" + itemCode + "-" + item.getName() |
| | | + "]ä¸ä¸è´ï¼è¯·æ ¸å¯¹æ¹æ¬¡å·æç©æç¼ç "); |
| | | } |
| | | } |
| | | |
| | | // 4. å建è¡ï¼èç¨¿ç¶æï¼ç©æå卿§/å¯ç¨ãæ¹æ¬¡è§£æçæ ¡éªç±è¡æå¡ç»ä¸å®æï¼ |
| | | MesWmProductSalesLineSaveReqVO lineVO = new MesWmProductSalesLineSaveReqVO(); |
| | | lineVO.setSalesId(salesId); |
| | | lineVO.setItemId(item.getId()); |
| | | lineVO.setQuantity(data.getQuantity()); |
| | | lineVO.setBatchCode(batchCode); |
| | | lineVO.setOqcCheckFlag(parseImportYesNo(data.getOqcCheckFlag())); |
| | | lineVO.setRemark(StrUtil.trimToNull(data.getRemark())); |
| | | productSalesLineService.createProductSalesLine(lineVO); |
| | | } |
| | | |
| | | private boolean isEmptyImportRow(MesWmProductSalesImportExcelVO row) { |
| | | return StrUtil.isBlank(row.getCode()) && StrUtil.isBlank(row.getClientName()) |
| | | && StrUtil.isBlank(row.getItemCode()) && row.getQuantity() == null |
| | | && StrUtil.isBlank(row.getName()) && StrUtil.isBlank(row.getSalesDate()); |
| | | } |
| | | |
| | | private LocalDateTime parseImportSalesDate(String text, String rowPrefix) { |
| | | if (StrUtil.isBlank(text)) { |
| | | return LocalDate.now().atStartOfDay(); |
| | | } |
| | | String value = text.trim(); |
| | | for (String pattern : new String[]{"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm"}) { |
| | | 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(rowPrefix + "åºåºæ¥æ[" + text |
| | | + "]æ ¼å¼ä¸æ£ç¡®ï¼è¯·ä½¿ç¨ yyyy-MM-ddï¼å¦ 2026-10-31"); |
| | | } |
| | | |
| | | private Boolean parseImportYesNo(String text) { |
| | | if (StrUtil.isBlank(text)) { |
| | | return false; |
| | | } |
| | | String value = text.trim(); |
| | | return "æ¯".equals(value) || "1".equals(value) || "true".equalsIgnoreCase(value) |
| | | || "yes".equalsIgnoreCase(value) || "éè¦".equals(value); |
| | | } |
| | | |
| | | private String resolveItemText(Long itemId) { |
| | | try { |
| | | MdmItemRespDTO item = mdmItemApi.getItem(itemId).getCheckedData(); |
| | | if (item != null) { |
| | | return item.getCode() + "-" + item.getName(); |
| | | } |
| | | } catch (Exception ex) { |
| | | log.warn("[resolveItemText][ç©æ({}) åç§°è§£æå¤±è´¥]", itemId, ex); |
| | | } |
| | | return "ID=" + itemId; |
| | | } |
| | | |
| | | private String resolveImportErrorMessage(Exception ex) { |
| | | if (ex instanceof ServiceException serviceException) { |
| | | return serviceException.getMessage(); |
| | | } |
| | | return StrUtil.blankToDefault(ex.getMessage(), "导å
¥å¤±è´¥"); |
| | | } |
| | | |
| | | /** |
| | | * 导å
¥è¡ï¼ä¿ç Excel è¡å·ï¼ä¾¿äºæç¤ºâ第 N è¡âï¼ |
| | | */ |
| | | private record ImportRow(MesWmProductSalesImportExcelVO data, int rowNum) { |
| | | } |
| | | |
| | | } |