sql/mysql/after_sale_cancellation_order_link_20261107.sql
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,14 @@ -- éååç忝æå ³è ERP éå®è®¢åï¼å®¡æ¹éè¿èå¨ä½åº SET @c := (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='after_sale_cancellation' AND COLUMN_NAME='sale_order_id'); SET @s := IF(@c=0, 'ALTER TABLE after_sale_cancellation ADD COLUMN sale_order_id BIGINT DEFAULT NULL COMMENT ''ERP éå®è®¢åç¼å·'' AFTER contract_id', 'SELECT 1'); PREPARE st FROM @s; EXECUTE st; DEALLOCATE PREPARE st; SET @i := (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME='after_sale_cancellation' AND INDEX_NAME='idx_after_sale_cancellation_order'); SET @s2 := IF(@i=0, 'ALTER TABLE after_sale_cancellation ADD KEY idx_after_sale_cancellation_order (sale_order_id, deleted)', 'SELECT 1'); PREPARE st2 FROM @s2; EXECUTE st2; DEALLOCATE PREPARE st2; sql/mysql/erp_menus_restore_20261107.sql
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,28 @@ -- ERP å®é å·²å¯ç¨ï¼éå®/éè´/仿¬¾è®¢åæ¥å£åå¯ç¨ï¼ï¼æ¤éæ¤åç ERP åç¨ä¸èåéèï¼ -- å¹¶æãéè´ææ¬åæãæ¹ä¸º ERP éè´è®¢åå£å¾ç页é¢ã -- 1) æ¢å¤éè´ç®¡çä¸ ERP åèåå¯ç¨ï¼æ¤é误å¤åç¨ï¼ UPDATE system_menu SET status = 0 WHERE CAST(deleted AS UNSIGNED)=0 AND parent_id = 2602 AND component LIKE 'erp/%'; -- 2) æ¢å¤ä»åºå¤§å±ï¼çº¿ä¸ä¸ºå¯ç¨ç¶æï¼ UPDATE system_menu SET status = 0, visible = b'1' WHERE CAST(deleted AS UNSIGNED)=0 AND component = 'bi/warehouse/index'; -- 3) éè´ææ¬åææ¹æå ERP 页é¢ï¼å¹¶ä¸æå°ãéè´ç®¡çãä¸ï¼åæå¨ SRM ä¸ï¼ UPDATE system_menu SET component = 'erp/purchase/statistics/price-analysis', component_name = 'ErpPurchasePriceAnalysis', name = 'éè´ææ¬åæ', parent_id = 2602, sort = 10, status = 0, visible = b'1' WHERE CAST(deleted AS UNSIGNED)=0 AND component IN ('srm/statistics/cost-analysis','erp/purchase/statistics/price-analysis'); -- 4) 确认èåææ INSERT INTO system_role_menu (role_id, menu_id, creator, create_time, updater, update_time, deleted) SELECT rm.role_id, m.id, '1', NOW(), '1', NOW(), b'0' FROM (SELECT DISTINCT role_id FROM system_role_menu WHERE menu_id = 602524 AND CAST(deleted AS UNSIGNED)=0) rm CROSS JOIN (SELECT id FROM system_menu WHERE component='erp/purchase/statistics/price-analysis' AND CAST(deleted AS UNSIGNED)=0) m WHERE NOT EXISTS (SELECT 1 FROM system_role_menu x WHERE x.role_id=rm.role_id AND x.menu_id=m.id AND CAST(x.deleted AS UNSIGNED)=0); yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/controller/admin/cancellation/vo/AfterSaleCancellationSaveReqVO.java
@@ -27,7 +27,11 @@ @Schema(description = "CRM ååç¼å·") private Long contractId; @Schema(description = "éå®è®¢åå·ï¼æ ERP æ¶å¯ä¸ºç©ºï¼") @Schema(description = "ERP éå®è®¢åç¼å·ï¼å¡«åå审æ¹éè¿ä¼èå¨ä½åº") @Positive private Long saleOrderId; @Schema(description = "éå®è®¢åå·ï¼ä» å±ç¤ºï¼éå¡«ï¼") @Size(max = 64) private String saleOrderNo; yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/dal/dataobject/cancellation/AfterSaleCancellationDO.java
@@ -32,6 +32,8 @@ private Long ticketId; private Long customerId; private Long contractId; /** ERP éå®è®¢åç¼å·ï¼å¡«åå审æ¹éè¿ä¼èå¨ä½åºï¼å MPS è®¡åæ¦æªçº¦æï¼ */ private Long saleOrderId; private String saleOrderNo; /** éè´§ç±»åï¼1 鿬¾éè´§ 2 æ¢è´§ 3 ä» éæ¬¾ */ private Integer returnType; yudao-module-aftersales/src/main/java/cn/iocoder/yudao/module/aftersales/service/cancellation/AfterSaleCancellationService.java
@@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.aftersales.dal.redis.no.AfterSaleNoRedisDAO; import cn.iocoder.yudao.module.aftersales.service.returnobj.AfterSaleReturnService; import cn.iocoder.yudao.module.crm.api.contract.CrmContractApi; import cn.iocoder.yudao.module.erp.api.sale.ErpSaleOrderApi; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.dao.DuplicateKeyException; @@ -22,6 +23,7 @@ import java.util.Objects; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; import static cn.iocoder.yudao.module.aftersales.enums.ErrorCodeConstants.*; /** @@ -53,6 +55,7 @@ @Resource private AfterSaleNoRedisDAO noRedisDAO; @Resource private AfterSaleReturnService returnService; @Resource private CrmContractApi crmContractApi; @Resource private ErpSaleOrderApi erpSaleOrderApi; public PageResult<AfterSaleCancellationDO> page(PageParam page, String code, String type, Integer status, Long customerId) { @@ -133,7 +136,10 @@ } /** * 审æ¹ï¼éè¿åæ§è¡ä¸æ¸¸å¨ä½ï¼é©³åå¿ é¡»å¡«æè§ã * 审æ¹ï¼é©³åå¿ é¡»å¡«æè§ï¼éè¿æ¶å æ§è¡ä¸æ¸¸å¨ä½ï¼å ¨é¨æåæè½å®¡æ¹ç»è®ºã * * <p>䏿¸¸å¤±è´¥ï¼å¦éå®è®¢åä»æå ³èç产计åï¼ä¼æ´ä½åæ»ï¼åçåä¿æãå¾ å®¡æ¹ãï¼ * ç±è°ç¨æ¹çå°å ·ä½åå ï¼é¿å åºç°ã审æ¹éè¿äºä½ä¸å¡æ²¡æ§è¡ãçä¸ä¸è´ç¶æã * * @return éè¿æ¶çæçéè´§åç¼å·ï¼ééè´§ç±»å为 nullï¼ */ @@ -146,27 +152,47 @@ AfterSaleCancellationDO task = get(req.getId()); if (!Objects.equals(task.getStatus(), ST_PENDING)) throw exception(CANCELLATION_APPROVE_FAIL_STATUS); int target = Boolean.TRUE.equals(req.getApproved()) ? ST_APPROVED : ST_REJECTED; // 驳åï¼åªæ¹ç¶æï¼ä¸æ§è¡ä¸æ¸¸ if (!Boolean.TRUE.equals(req.getApproved())) { if (mapper.update(null, new LambdaUpdateWrapper<AfterSaleCancellationDO>() .eq(AfterSaleCancellationDO::getId, task.getId()) .eq(AfterSaleCancellationDO::getStatus, ST_PENDING) .set(AfterSaleCancellationDO::getStatus, ST_REJECTED) .set(AfterSaleCancellationDO::getApproverId, userId) .set(AfterSaleCancellationDO::getApprovalTime, LocalDateTime.now()) .set(AfterSaleCancellationDO::getApprovalRemark, req.getRemark())) != 1) { throw exception(CANCELLATION_APPROVE_FAIL_STATUS); } return null; } // éè¿ï¼å æ§è¡ä¸æ¸¸å¨ä½ï¼å¼å¸¸ç´æ¥æåºï¼äºå¡åæ»ï¼åæ®ä»ä¸ºå¾ 审æ¹ï¼ Long returnId = executeDownstream(task, userId); // 䏿¸¸æåååè½å®¡æ¹ç»è®º if (mapper.update(null, new LambdaUpdateWrapper<AfterSaleCancellationDO>() .eq(AfterSaleCancellationDO::getId, task.getId()) .eq(AfterSaleCancellationDO::getStatus, ST_PENDING) .set(AfterSaleCancellationDO::getStatus, target) .set(AfterSaleCancellationDO::getStatus, ST_APPROVED) .set(AfterSaleCancellationDO::getApproverId, userId) .set(AfterSaleCancellationDO::getApprovalTime, LocalDateTime.now()) .set(AfterSaleCancellationDO::getApprovalRemark, req.getRemark())) != 1) { .set(AfterSaleCancellationDO::getApprovalRemark, req.getRemark()) .set(AfterSaleCancellationDO::getExecuted, 1)) != 1) { throw exception(CANCELLATION_APPROVE_FAIL_STATUS); } if (!Boolean.TRUE.equals(req.getApproved())) return null; return executeDownstream(task, userId); return returnId; } /** 审æ¹éè¿åç䏿¸¸å¨ä½ï¼å¤±è´¥æ¶è®°å½åå ä½ä¸åæ»å®¡æ¹ç»è®ºï¼ä¾¿äºäººå·¥è¡¥åã */ /** * 审æ¹éè¿åç䏿¸¸å¨ä½ã * * <p>ä¸åå¼å¸¸ï¼ä»»ä¸æ¥å¤±è´¥ç´æ¥æåºï¼ç± {@link #approve} çäºå¡æ´ä½åæ»ï¼ * ä¿è¯ã审æ¹éè¿ãä¸ã䏿¸¸å·²æ§è¡ãæ°¸è¿ä¸è´ã */ private Long executeDownstream(AfterSaleCancellationDO task, Long userId) { Long returnId = null; String remark; boolean ok = true; try { { switch (task.getType()) { case "RETURN" -> { var vo = new AfterSaleReturnSaveReqVO(); @@ -175,23 +201,31 @@ vo.setReturnType(task.getReturnType() == null ? 1 : task.getReturnType()); vo.setReturnReason(task.getReason()); returnId = returnService.createReturn(vo, userId); remark = "å·²çæéè´§å " + returnId; if (task.getSaleOrderId() != null) { erpSaleOrderApi.cancelSaleOrder(task.getSaleOrderId()).getCheckedData(); remark = "å·²çæéè´§å " + returnId + "ï¼å¹¶ä½åºéå®è®¢å " + task.getSaleOrderId(); } else { remark = "å·²çæéè´§å " + returnId; } } case "CONTRACT_TERMINATE" -> { crmContractApi.updateContractStatus(task.getContractId(), CRM_CONTRACT_INVALID).getCheckedData(); remark = "å·²ä½åº CRM åå " + task.getContractId(); // è¥å ³è ERP éå®è®¢åï¼ä¸å¹¶ä½åºï¼ä¼è¢«æªåæ¶ç MPS è®¡åæ¦æªï¼ if (task.getSaleOrderId() != null) { erpSaleOrderApi.cancelSaleOrder(task.getSaleOrderId()).getCheckedData(); remark = "å·²ä½åº CRM åå " + task.getContractId() + "ï¼å¹¶ä½åºéå®è®¢å " + task.getSaleOrderId(); } else { remark = "å·²ä½åº CRM åå " + task.getContractId(); } } case "CONTRACT_CHANGE" -> remark = "åæ´åçå·²éè¿ï¼CRM ååå 容请æéä»¶åæ¥ç»´æ¤"; default -> { ok = false; remark = "æªç¥åçç±»åï¼" + task.getType(); } default -> throw invalidParamException("æªç¥åçç±»åï¼" + task.getType()); } } catch (Exception e) { ok = false; remark = "䏿¸¸æ§è¡å¤±è´¥ï¼" + e.getMessage(); log.warn("éååç[{}]䏿¸¸å¨ä½å¤±è´¥ï¼{}", task.getCode(), e.getMessage()); } var update = new AfterSaleCancellationDO(); update.setId(task.getId()); update.setExecuted(ok ? 1 : 0); update.setExecuteRemark(remark); if (returnId != null) update.setReturnId(returnId); mapper.updateById(update); yudao-module-erp-api/src/main/java/cn/iocoder/yudao/module/erp/api/sale/ErpSaleOrderApi.java
@@ -83,4 +83,13 @@ */ CommonResult<Boolean> updateOrderOutStatusTo(Long orderId, Integer outStatus); /** * ä½åºéå®è®¢åï¼ä¾å®å/éåæµç¨èå¨ï¼ * * <p>订åå·²å卿ªåæ¶ç MPS è®¡åæ¶ä¼æåºä¸å¡å¼å¸¸ï¼ç±è°ç¨æ¹æç¤ºç¨æ·å å¤ç计åã * * @param orderId 订åç¼å· */ CommonResult<Boolean> cancelSaleOrder(Long orderId); } yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/api/sale/ErpSaleOrderApiImpl.java
@@ -138,4 +138,14 @@ return success(true); } @Override public CommonResult<Boolean> cancelSaleOrder(Long orderId) { if (orderId == null) { return success(false); } // å¤ç¨ Service çä½åºé»è¾ï¼å«ç¶ææ ¡éªãåºåºåæ ¡éªãæªåæ¶ MPS è®¡åæ¦æª saleOrderService.cancelSaleOrder(orderId); return success(true); } } yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/controller/admin/statistics/SrmStatisticsController.java
@@ -1,13 +1,8 @@ package cn.iocoder.yudao.module.srm.controller.admin.statistics; 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.module.srm.controller.admin.statistics.vo.SrmCostAnalysisRespVO; import cn.iocoder.yudao.module.srm.controller.admin.statistics.vo.SrmTenderStatisticsRespVO; import cn.iocoder.yudao.module.srm.service.statistics.SrmCostAnalysisService; import cn.iocoder.yudao.module.srm.service.statistics.SrmStatisticsService; import jakarta.validation.Valid; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; @@ -25,8 +20,6 @@ @Resource private SrmStatisticsService statisticsService; @Resource private SrmCostAnalysisService costAnalysisService; @GetMapping("/tender") @Operation(summary = "è·åææ ç»è®¡æ°æ®") @@ -35,22 +28,4 @@ return success(statisticsService.getTenderStatistics(year)); } @GetMapping("/cost-analysis") @Operation(summary = "éè´ææ¬åæ", description = "åºäº SRM ææ éæ±éä¸ä¾åºåæ¥ä»·èåï¼éè´éãå æåä»·ãæä½/æé«ä»·ãä»·æ ¼åå¨ï¼ä¸ä¾èµ ERP") @PreAuthorize("@ss.hasPermission('srm:statistics:query')") public CommonResult<PageResult<SrmCostAnalysisRespVO>> getCostAnalysis( @Valid PageParam page, @RequestParam(value = "productName", required = false) String productName) { return success(costAnalysisService.page(page, productName)); } @GetMapping("/cost-trend") @Operation(summary = "ç©æä»·æ ¼è¶å¿", description = "æææ±æ»è¯¥ç©æç平忥价") @PreAuthorize("@ss.hasPermission('srm:statistics:query')") public CommonResult<java.util.List<SrmCostAnalysisRespVO.TrendPoint>> getCostTrend( @RequestParam("productId") Long productId) { return success(costAnalysisService.trend(productId)); } } } yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/controller/admin/statistics/vo/SrmCostAnalysisRespVO.java
ÎļþÒÑɾ³ý yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/statistics/SrmCostAnalysisService.java
ÎļþÒÑɾ³ý