feat(wms): 新增WMS报表管理功能

1.退库原因分析报表returnReasonAnalysis
统计各类退库(生产退库、销售退库等)的数量、占比及主要退库原因(如质量问题、订单变更),为改进生产、销售和质量管理提供数据支持。
2.库存变动趋势分析表inventoryTrendAnalysis
内容:以折线图/柱状图展示某物料或类别在一段时间内的库存数量变化(如近3个月每周结存),标注关键节点(如大额入库/出库)。
作用:识别库存波动规律(如季节性增减),预测未来库存需求,避免积压或短缺。
3.安全库存预警表safetyStockAlert
内容:对比物料当前库存与设定的“安全库存值”,列出“低于安全库存”(短缺预警)或“高于最高库存”(积压预警)的物料及差异量。
4.呆滞料库存报表stagnantInventory
内容:定义“呆滞标准”(如6个月未出库),统计符合标准的物料及数量、金额,标注呆滞原因(如订单取消/设计变更)。
作用:推动呆滞料处理(如折价处理、返工利用),减少资金占用和仓储成本。
5.库存差异报表inventoryDifference
内容:记录盘点后实际数量与系统账面数量的差异(差异量、差异率),标注差异物料及可能原因(如漏记、丢失、计数错误)。
作用:跟踪差异处理进度(如调账、追责),改进仓库操作规范(如加强入库扫码校验)。 
6定期生成库存周转报表inventoryTurnover
库存周转率=(销售数量/库存数量)x100%例如6月销售31台,期末库存65台库存周转率=(31/65)*100%=47.69%
库存周转率=(该期间的出库总金额/该期间的平均库存金额)x100%=该期间出库总金额x2/(期初库存金额+期末库存金额)x100%库存周转率=312*2/(490+589)=56.9%
hwmom-htk
zangch@mesnac.com 4 months ago
parent 4d14ea4268
commit cdd8502c11

@ -0,0 +1,90 @@
package org.dromara.mes.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* VO
*
* @author Yinq
* @date 2025-01-17
*/
@Data
@ExcelIgnoreUnannotated
public class WorkHourReportVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@ExcelProperty(value = "派工单")
private String dispatchCode;
/**
*
*/
@ExcelProperty(value = "班组")
private String teamName;
/**
*
*/
@ExcelProperty(value = "工位")
private String stationName;
/**
*
*/
@ExcelProperty(value = "物料名称")
private String materialName;
/**
*
*/
@ExcelProperty(value = "工序名称")
private String processName;
/**
*
*/
@ExcelProperty(value = "报工数量")
private BigDecimal completeAmount;
/**
* (h)
*/
@ExcelProperty(value = "标准工时(h)")
private BigDecimal standardWorkHour;
/**
* (h)
*/
@ExcelProperty(value = "报工工时(h)")
private BigDecimal reportWorkHour;
/**
*
*/
@ExcelProperty(value = "生产效率")
private BigDecimal productionEfficiency;
/**
*
*/
@ExcelProperty(value = "派工单状态")
private String planStatus;
/**
*
*/
@ExcelProperty(value = "备注")
private String remark;
}

@ -0,0 +1,187 @@
package org.dromara.wms.controller;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.web.core.BaseController;
import org.dromara.wms.domain.vo.*;
import org.dromara.wms.service.IWmsReportService;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* WMS
* 访:/wms/report
*
* @author hwmom
* @date 2025-01-20
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/report")
public class WmsReportController extends BaseController {
private final IWmsReportService wmsReportService;
/**
* 退
*/
//@SaCheckPermission("wms:report:returnReason")
@GetMapping("/returnReasonAnalysis")
public R<List<WmsReturnReasonAnalysisVo>> getReturnReasonAnalysis(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId) {
List<WmsReturnReasonAnalysisVo> list = wmsReportService.getReturnReasonAnalysis(tenantId, materialCategoryId);
return R.ok(list);
}
/**
* 退
*/
//@SaCheckPermission("wms:report:returnReason")
@Log(title = "退库原因分析报表", businessType = BusinessType.EXPORT)
@PostMapping("/returnReasonAnalysis/export")
public void exportReturnReasonAnalysis(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId,
HttpServletResponse response) {
List<WmsReturnReasonAnalysisVo> list = wmsReportService.getReturnReasonAnalysis(tenantId, materialCategoryId);
ExcelUtil.exportExcel(list, "退库原因分析报表", WmsReturnReasonAnalysisVo.class, response);
}
/**
*
*/
//@SaCheckPermission("wms:report:inventoryTrend")
@GetMapping("/inventoryTrendAnalysis")
public R<List<WmsInventoryTrendAnalysisVo>> getInventoryTrendAnalysis(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId) {
List<WmsInventoryTrendAnalysisVo> list = wmsReportService.getInventoryTrendAnalysis(tenantId, materialCategoryId);
return R.ok(list);
}
/**
*
*/
//@SaCheckPermission("wms:report:inventoryTrend")
@Log(title = "库存变动趋势分析报表", businessType = BusinessType.EXPORT)
@PostMapping("/inventoryTrendAnalysis/export")
public void exportInventoryTrendAnalysis(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId,
HttpServletResponse response) {
List<WmsInventoryTrendAnalysisVo> list = wmsReportService.getInventoryTrendAnalysis(tenantId, materialCategoryId);
ExcelUtil.exportExcel(list, "库存变动趋势分析报表", WmsInventoryTrendAnalysisVo.class, response);
}
/**
*
*/
//@SaCheckPermission("wms:report:safetyStock")
@GetMapping("/safetyStockAlert")
public R<List<WmsSafetyStockAlertVo>> getSafetyStockAlert(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId) {
List<WmsSafetyStockAlertVo> list = wmsReportService.getSafetyStockAlert(tenantId, materialCategoryId);
return R.ok(list);
}
/**
*
*/
//@SaCheckPermission("wms:report:safetyStock")
@Log(title = "安全库存预警报表", businessType = BusinessType.EXPORT)
@PostMapping("/safetyStockAlert/export")
public void exportSafetyStockAlert(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId,
HttpServletResponse response) {
List<WmsSafetyStockAlertVo> list = wmsReportService.getSafetyStockAlert(tenantId, materialCategoryId);
ExcelUtil.exportExcel(list, "安全库存预警报表", WmsSafetyStockAlertVo.class, response);
}
/**
*
*/
//@SaCheckPermission("wms:report:stagnantInventory")
@GetMapping("/stagnantInventory")
public R<List<WmsStagnantInventoryVo>> getStagnantInventory(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId) {
List<WmsStagnantInventoryVo> list = wmsReportService.getStagnantInventory(tenantId, materialCategoryId);
return R.ok(list);
}
/**
*
*/
//@SaCheckPermission("wms:report:stagnantInventory")
@Log(title = "呆滞料库存报表", businessType = BusinessType.EXPORT)
@PostMapping("/stagnantInventory/export")
public void exportStagnantInventory(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId,
HttpServletResponse response) {
List<WmsStagnantInventoryVo> list = wmsReportService.getStagnantInventory(tenantId, materialCategoryId);
ExcelUtil.exportExcel(list, "呆滞料库存报表", WmsStagnantInventoryVo.class, response);
}
/**
*
*/
//@SaCheckPermission("wms:report:inventoryDifference")
@GetMapping("/inventoryDifference")
public R<List<WmsInventoryDifferenceVo>> getInventoryDifference(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId) {
List<WmsInventoryDifferenceVo> list = wmsReportService.getInventoryDifference(tenantId, materialCategoryId);
return R.ok(list);
}
/**
*
*/
//@SaCheckPermission("wms:report:inventoryDifference")
@Log(title = "库存差异报表", businessType = BusinessType.EXPORT)
@PostMapping("/inventoryDifference/export")
public void exportInventoryDifference(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId,
HttpServletResponse response) {
List<WmsInventoryDifferenceVo> list = wmsReportService.getInventoryDifference(tenantId, materialCategoryId);
ExcelUtil.exportExcel(list, "库存差异报表", WmsInventoryDifferenceVo.class, response);
}
/**
*
*/
//@SaCheckPermission("wms:report:inventoryTurnover")
@GetMapping("/inventoryTurnover")
public R<List<WmsInventoryTurnoverVo>> getInventoryTurnover(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId) {
List<WmsInventoryTurnoverVo> list = wmsReportService.getInventoryTurnover(tenantId, materialCategoryId);
return R.ok(list);
}
/**
*
*/
//@SaCheckPermission("wms:report:inventoryTurnover")
@Log(title = "库存周转报表", businessType = BusinessType.EXPORT)
@PostMapping("/inventoryTurnover/export")
public void exportInventoryTurnover(
@RequestParam(required = false) String tenantId,
@RequestParam(required = false) Long materialCategoryId,
HttpServletResponse response) {
List<WmsInventoryTurnoverVo> list = wmsReportService.getInventoryTurnover(tenantId, materialCategoryId);
ExcelUtil.exportExcel(list, "库存周转报表", WmsInventoryTurnoverVo.class, response);
}
}

@ -0,0 +1,126 @@
package org.dromara.wms.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* wms_inventory_difference
*
* @author hwmom
* @date 2025-01-20
*/
@Data
@ExcelIgnoreUnannotated
public class WmsInventoryDifferenceVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@ExcelProperty(value = "租户编号")
private String tenantId;
/**
*
*/
@ExcelProperty(value = "盘点单号")
private String checkCode;
/**
*
*/
@ExcelProperty(value = "盘点类型")
private String checkType;
/**
* ID
*/
@ExcelProperty(value = "仓库ID")
private Long warehouseId;
/**
*
*/
@ExcelProperty(value = "仓库名称")
private String warehouseName;
/**
* ID
*/
@ExcelProperty(value = "物料ID")
private Long materialId;
/**
*
*/
@ExcelProperty(value = "物料编码")
private String materialCode;
/**
*
*/
@ExcelProperty(value = "物料名称")
private String materialName;
/**
*
*/
@ExcelProperty(value = "物料大类")
private String materialCategoryName;
/**
*
*/
@ExcelProperty(value = "账面数量")
private BigDecimal bookInventoryQty;
/**
*
*/
@ExcelProperty(value = "实际盘点数量")
private BigDecimal actualInventoryQty;
/**
*
*/
@ExcelProperty(value = "差异量")
private BigDecimal differenceQty;
/**
*
*/
@ExcelProperty(value = "差异类型")
private String differenceType;
/**
*
*/
@ExcelProperty(value = "差异率(%)")
private BigDecimal differenceRate;
/**
*
*/
@ExcelProperty(value = "差异等级")
private String differenceLevel;
/**
*
*/
@ExcelProperty(value = "盘点时间")
private Date checkTime;
/**
*
*/
@ExcelProperty(value = "盘点人员")
private String checkBy;
}

@ -0,0 +1,95 @@
package org.dromara.wms.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* wms_inventory_trend_analysis
*
* @author hwmom
* @date 2025-01-20
*/
@Data
@ExcelIgnoreUnannotated
public class WmsInventoryTrendAnalysisVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@ExcelProperty(value = "租户编号")
private String tenantId;
/**
* ID
*/
@ExcelProperty(value = "物料ID")
private Long materialId;
/**
*
*/
@ExcelProperty(value = "物料编码")
private String materialCode;
/**
*
*/
@ExcelProperty(value = "物料名称")
private String materialName;
/**
*
*/
@ExcelProperty(value = "物料大类")
private String materialCategoryName;
/**
*
*/
@ExcelProperty(value = "统计月份")
private String statisticsMonth;
/**
*
*/
@ExcelProperty(value = "统计周")
private Integer statisticsWeek;
/**
*
*/
@ExcelProperty(value = "当前库存数量")
private BigDecimal currentInventoryQty;
/**
*
*/
@ExcelProperty(value = "本周入库数量")
private BigDecimal weekInstockQty;
/**
*
*/
@ExcelProperty(value = "本周出库数量")
private BigDecimal weekOutstockQty;
/**
*
*/
@ExcelProperty(value = "上周结存数量")
private BigDecimal lastWeekInventoryQty;
/**
*
*/
@ExcelProperty(value = "关键节点标注")
private String keyNodeMark;
}

@ -0,0 +1,95 @@
package org.dromara.wms.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* wms_inventory_turnover
*
* @author hwmom
* @date 2025-01-20
*/
@Data
@ExcelIgnoreUnannotated
public class WmsInventoryTurnoverVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@ExcelProperty(value = "租户编号")
private String tenantId;
/**
* ID
*/
@ExcelProperty(value = "物料ID")
private Long materialId;
/**
*
*/
@ExcelProperty(value = "物料编码")
private String materialCode;
/**
*
*/
@ExcelProperty(value = "物料名称")
private String materialName;
/**
*
*/
@ExcelProperty(value = "物料大类")
private String materialCategoryName;
/**
*
*/
@ExcelProperty(value = "统计月份")
private String statisticsMonth;
/**
*
*/
@ExcelProperty(value = "月初库存数量")
private BigDecimal beginInventoryQty;
/**
*
*/
@ExcelProperty(value = "月末库存数量")
private BigDecimal endInventoryQty;
/**
*
*/
@ExcelProperty(value = "月出库数量")
private BigDecimal monthOutstockQty;
/**
*
*/
@ExcelProperty(value = "库存周转率(%)")
private BigDecimal inventoryTurnoverRate;
/**
*
*/
@ExcelProperty(value = "简化周转率(%)")
private BigDecimal simpleTurnoverRate;
/**
*
*/
@ExcelProperty(value = "周转评价")
private String turnoverEvaluation;
}

@ -0,0 +1,77 @@
package org.dromara.wms.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* 退 wms_return_reason_analysis
*
* @author hwmom
* @date 2025-01-20
*/
@Data
@ExcelIgnoreUnannotated
public class WmsReturnReasonAnalysisVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@ExcelProperty(value = "租户编号")
private String tenantId;
/**
* 退
*/
@ExcelProperty(value = "退库原因分类")
private String returnReasonCategory;
/**
* 退
*/
@ExcelProperty(value = "退库单数")
private Long returnOrderCount;
/**
* 退
*/
@ExcelProperty(value = "退库总数量")
private BigDecimal totalReturnAmount;
/**
* 退
*/
@ExcelProperty(value = "退库单数占比(%)")
private BigDecimal orderCountRatio;
/**
* 退
*/
@ExcelProperty(value = "退库数量占比(%)")
private BigDecimal amountRatio;
/**
*
*/
@ExcelProperty(value = "主要物料名称")
private String materialName;
/**
*
*/
@ExcelProperty(value = "物料大类")
private String materialCategoryName;
/**
*
*/
@ExcelProperty(value = "物料编码")
private String materialCode;
}

@ -0,0 +1,96 @@
package org.dromara.wms.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* wms_safety_stock_alert
*
* @author hwmom
* @date 2025-01-20
*/
@Data
@ExcelIgnoreUnannotated
public class WmsSafetyStockAlertVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@ExcelProperty(value = "租户编号")
private String tenantId;
/**
* ID
*/
@ExcelProperty(value = "物料ID")
private Long materialId;
/**
*
*/
@ExcelProperty(value = "物料编码")
private String materialCode;
/**
*
*/
@ExcelProperty(value = "物料名称")
private String materialName;
/**
*
*/
@ExcelProperty(value = "物料大类")
private String materialCategoryName;
/**
*
*/
@ExcelProperty(value = "当前库存数量")
private BigDecimal currentInventoryQty;
/**
*
*/
@ExcelProperty(value = "安全库存值")
private BigDecimal safeStockAmount;
/**
*
*/
@ExcelProperty(value = "最小库存值")
private BigDecimal minStockAmount;
/**
*
*/
@ExcelProperty(value = "最大库存值")
private BigDecimal maxStockAmount;
/**
*
*/
@ExcelProperty(value = "预警状态")
private String alertStatus;
/**
*
*/
@ExcelProperty(value = "差异量")
private BigDecimal differenceAmount;
/**
*
*/
@ExcelProperty(value = "最后更新时间")
private Date lastUpdateTime;
}

@ -0,0 +1,102 @@
package org.dromara.wms.domain.vo;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
/**
* wms_stagnant_inventory
*
* @author hwmom
* @date 2025-01-20
*/
@Data
@ExcelIgnoreUnannotated
public class WmsStagnantInventoryVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@ExcelProperty(value = "租户编号")
private String tenantId;
/**
* ID
*/
@ExcelProperty(value = "物料ID")
private Long materialId;
/**
*
*/
@ExcelProperty(value = "物料编码")
private String materialCode;
/**
*
*/
@ExcelProperty(value = "物料名称")
private String materialName;
/**
*
*/
@ExcelProperty(value = "物料大类")
private String materialCategoryName;
/**
*
*/
@ExcelProperty(value = "呆滞库存数量")
private BigDecimal stagnantInventoryQty;
/**
*
*/
@ExcelProperty(value = "计量单位")
private String materialUnit;
/**
*
*/
@ExcelProperty(value = "最后出库时间")
private String lastOutstockTime;
/**
*
*/
@ExcelProperty(value = "呆滞天数")
private Integer stagnantDays;
/**
*
*/
@ExcelProperty(value = "呆滞原因")
private String stagnantReason;
/**
*
*/
@ExcelProperty(value = "物料规格")
private String materialSpec;
/**
*
*/
@ExcelProperty(value = "所在仓库")
private String warehouseName;
/**
*
*/
@ExcelProperty(value = "最后活动时间")
private String lastActivityTime;
}

@ -0,0 +1,341 @@
package org.dromara.wms.mapper;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.dromara.wms.domain.vo.*;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* WMSMapper
*
* @author hwmom
* @date 2025-01-20
*/
@Repository
@Mapper
public interface WmsReportMapper {
/**
* 退
*/
@Select("SELECT " +
" ro.tenant_id AS tenantId, " +
" CASE " +
" WHEN ro.return_reason LIKE '%质量%' OR ro.return_reason LIKE '%不合格%' THEN '质量问题' " +
" WHEN ro.return_reason LIKE '%订单%' OR ro.return_reason LIKE '%变更%' THEN '订单变更' " +
" WHEN ro.return_reason LIKE '%损坏%' OR ro.return_reason LIKE '%破损%' THEN '物料损坏' " +
" WHEN ro.return_reason LIKE '%过期%' OR ro.return_reason LIKE '%超期%' THEN '过期物料' " +
" ELSE '其他原因' " +
" END AS returnReasonCategory, " +
" COUNT(*) AS returnOrderCount, " +
" SUM(ro.return_amount) AS totalReturnAmount, " +
" CAST(COUNT(*) * 100.0 / (SELECT COUNT(*) FROM wms_return_order WHERE tenant_id = ro.tenant_id AND order_status = '1') AS DECIMAL(10,2)) AS orderCountRatio, " +
" CAST(SUM(ro.return_amount) * 100.0 / (SELECT SUM(return_amount) FROM wms_return_order WHERE tenant_id = ro.tenant_id AND order_status = '1') AS DECIMAL(10,2)) AS amountRatio, " +
" mi.material_name AS materialName, " +
" mc.material_category_name AS materialCategoryName, " +
" mi.material_code AS materialCode " +
"FROM wms_return_order ro " +
"INNER JOIN base_material_info_copy1 mi ON ro.material_id = mi.material_id " +
"INNER JOIN base_material_category mc ON mi.material_category_id = mc.material_category_id " +
"WHERE ro.order_status = '1' " +
" AND (#{tenantId} IS NULL OR ro.tenant_id = #{tenantId}) " +
" AND (#{materialCategoryId} IS NULL OR mc.material_category_id = #{materialCategoryId}) " +
"GROUP BY ro.tenant_id, " +
" CASE " +
" WHEN ro.return_reason LIKE '%质量%' OR ro.return_reason LIKE '%不合格%' THEN '质量问题' " +
" WHEN ro.return_reason LIKE '%订单%' OR ro.return_reason LIKE '%变更%' THEN '订单变更' " +
" WHEN ro.return_reason LIKE '%损坏%' OR ro.return_reason LIKE '%破损%' THEN '物料损坏' " +
" WHEN ro.return_reason LIKE '%过期%' OR ro.return_reason LIKE '%超期%' THEN '过期物料' " +
" ELSE '其他原因' " +
" END, " +
" mi.material_name, " +
" mc.material_category_name, " +
" mi.material_code " +
"ORDER BY ro.tenant_id, totalReturnAmount DESC")
List<WmsReturnReasonAnalysisVo> selectReturnReasonAnalysis(@Param("tenantId") String tenantId,
@Param("materialCategoryId") Long materialCategoryId);
/**
*
*/
@Select("SELECT " +
" inv.tenant_id AS tenantId, " +
" inv.material_id AS materialId, " +
" mi.material_code AS materialCode, " +
" mi.material_name AS materialName, " +
" mc.material_category_name AS materialCategoryName, " +
" CONVERT(VARCHAR(7), GETDATE(), 126) AS statisticsMonth, " +
" DATEPART(WEEK, GETDATE()) AS statisticsWeek, " +
" SUM(inv.inventory_qty) AS currentInventoryQty, " +
" ISNULL(instock_data.weekInstockQty, 0) AS weekInstockQty, " +
" ISNULL(outstock_data.weekOutstockQty, 0) AS weekOutstockQty, " +
" (SUM(inv.inventory_qty) + ISNULL(outstock_data.weekOutstockQty, 0) - ISNULL(instock_data.weekInstockQty, 0)) AS lastWeekInventoryQty, " +
" CASE " +
" WHEN ISNULL(instock_data.weekInstockQty, 0) > 1000 THEN '大额入库' " +
" WHEN ISNULL(outstock_data.weekOutstockQty, 0) > 1000 THEN '大额出库' " +
" ELSE '正常变动' " +
" END AS keyNodeMark " +
"FROM wms_inventory inv " +
"INNER JOIN base_material_info_copy1 mi ON inv.material_id = mi.material_id " +
"INNER JOIN base_material_category mc ON mi.material_category_id = mc.material_category_id " +
"LEFT JOIN ( " +
" SELECT material_id, tenant_id, SUM(instock_qty) AS weekInstockQty " +
" FROM wms_instock_record " +
" WHERE create_time >= DATEADD(DAY, -7, GETDATE()) " +
" GROUP BY material_id, tenant_id " +
") instock_data ON inv.material_id = instock_data.material_id AND inv.tenant_id = instock_data.tenant_id " +
"LEFT JOIN ( " +
" SELECT material_id, tenant_id, SUM(outstock_qty) AS weekOutstockQty " +
" FROM wms_outstock_record " +
" WHERE create_time >= DATEADD(DAY, -7, GETDATE()) " +
" GROUP BY material_id, tenant_id " +
") outstock_data ON inv.material_id = outstock_data.material_id AND inv.tenant_id = outstock_data.tenant_id " +
"WHERE inv.inventory_status = '1' " +
" AND (#{tenantId} IS NULL OR inv.tenant_id = #{tenantId}) " +
" AND (#{materialCategoryId} IS NULL OR mc.material_category_id = #{materialCategoryId}) " +
"GROUP BY inv.tenant_id, inv.material_id, mi.material_code, mi.material_name, mc.material_category_name, " +
" instock_data.weekInstockQty, outstock_data.weekOutstockQty " +
"ORDER BY inv.tenant_id, currentInventoryQty DESC")
List<WmsInventoryTrendAnalysisVo> selectInventoryTrendAnalysis(@Param("tenantId") String tenantId,
@Param("materialCategoryId") Long materialCategoryId);
/**
*
*/
@Select("SELECT " +
" inv.tenant_id AS tenantId, " +
" inv.material_id AS materialId, " +
" mi.material_code AS materialCode, " +
" mi.material_name AS materialName, " +
" mc.material_category_name AS materialCategoryName, " +
" SUM(inv.inventory_qty) AS currentInventoryQty, " +
" mi.safe_stock_amount AS safeStockAmount, " +
" mi.min_stock_amount AS minStockAmount, " +
" mi.max_stock_amount AS maxStockAmount, " +
" CASE " +
" WHEN SUM(inv.inventory_qty) < mi.min_stock_amount THEN '短缺预警' " +
" WHEN SUM(inv.inventory_qty) > mi.max_stock_amount THEN '积压预警' " +
" WHEN SUM(inv.inventory_qty) < mi.safe_stock_amount THEN '低于安全库存' " +
" ELSE '正常' " +
" END AS alertStatus, " +
" CASE " +
" WHEN SUM(inv.inventory_qty) < mi.min_stock_amount THEN mi.min_stock_amount - SUM(inv.inventory_qty) " +
" WHEN SUM(inv.inventory_qty) > mi.max_stock_amount THEN SUM(inv.inventory_qty) - mi.max_stock_amount " +
" WHEN SUM(inv.inventory_qty) < mi.safe_stock_amount THEN mi.safe_stock_amount - SUM(inv.inventory_qty) " +
" ELSE 0 " +
" END AS differenceAmount, " +
" inv.update_time AS lastUpdateTime " +
"FROM wms_inventory inv " +
"INNER JOIN base_material_info_copy1 mi ON inv.material_id = mi.material_id " +
"INNER JOIN base_material_category mc ON mi.material_category_id = mc.material_category_id " +
"WHERE inv.inventory_status = '1' " +
" AND mi.active_flag = '1' " +
" AND (mi.safe_stock_amount IS NOT NULL OR mi.min_stock_amount IS NOT NULL OR mi.max_stock_amount IS NOT NULL) " +
" AND (#{tenantId} IS NULL OR inv.tenant_id = #{tenantId}) " +
" AND (#{materialCategoryId} IS NULL OR mc.material_category_id = #{materialCategoryId}) " +
"GROUP BY inv.tenant_id, inv.material_id, mi.material_code, mi.material_name, mc.material_category_name, " +
" mi.safe_stock_amount, mi.min_stock_amount, mi.max_stock_amount, inv.update_time " +
"HAVING SUM(inv.inventory_qty) < mi.safe_stock_amount " +
" OR SUM(inv.inventory_qty) < mi.min_stock_amount " +
" OR SUM(inv.inventory_qty) > mi.max_stock_amount " +
"ORDER BY inv.tenant_id, alertStatus, differenceAmount DESC")
List<WmsSafetyStockAlertVo> selectSafetyStockAlert(@Param("tenantId") String tenantId,
@Param("materialCategoryId") Long materialCategoryId);
/**
*
*/
@Select("SELECT " +
" inv.tenant_id AS tenantId, " +
" inv.material_id AS materialId, " +
" mi.material_code AS materialCode, " +
" mi.material_name AS materialName, " +
" mc.material_category_name AS materialCategoryName, " +
" SUM(inv.inventory_qty) AS stagnantInventoryQty, " +
" mi.material_unit AS materialUnit, " +
" CASE " +
" WHEN last_out.lastOutstockTime IS NULL THEN '从未出库' " +
" ELSE CONVERT(VARCHAR(19), last_out.lastOutstockTime, 120) " +
" END AS lastOutstockTime, " +
" CASE " +
" WHEN last_out.lastOutstockTime IS NULL THEN DATEDIFF(DAY, first_in.firstInstockTime, GETDATE()) " +
" ELSE DATEDIFF(DAY, last_out.lastOutstockTime, GETDATE()) " +
" END AS stagnantDays, " +
" CASE " +
" WHEN last_out.lastOutstockTime IS NULL THEN '从未出库' " +
" WHEN DATEDIFF(DAY, last_out.lastOutstockTime, GETDATE()) > 180 THEN '超过6个月未出库' " +
" ELSE '正常' " +
" END AS stagnantReason, " +
" mi.material_spec AS materialSpec, " +
" w.warehouse_name AS warehouseName, " +
" CASE " +
" WHEN last_out.lastOutstockTime IS NULL THEN CONVERT(VARCHAR(19), first_in.firstInstockTime, 120) " +
" ELSE CONVERT(VARCHAR(19), last_out.lastOutstockTime, 120) " +
" END AS lastActivityTime " +
"FROM wms_inventory inv " +
"INNER JOIN base_material_info_copy1 mi ON inv.material_id = mi.material_id " +
"INNER JOIN base_material_category mc ON mi.material_category_id = mc.material_category_id " +
"INNER JOIN wms_base_warehouse w ON inv.warehouse_id = w.warehouse_id " +
"LEFT JOIN ( " +
" SELECT " +
" material_id, " +
" tenant_id, " +
" MIN(create_time) AS firstInstockTime " +
" FROM wms_instock_record " +
" WHERE create_time IS NOT NULL " +
" GROUP BY material_id, tenant_id " +
") first_in ON inv.material_id = first_in.material_id AND inv.tenant_id = first_in.tenant_id " +
"LEFT JOIN ( " +
" SELECT " +
" material_id, " +
" tenant_id, " +
" MAX(create_time) AS lastOutstockTime " +
" FROM wms_outstock_record " +
" WHERE create_time IS NOT NULL " +
" GROUP BY material_id, tenant_id " +
") last_out ON inv.material_id = last_out.material_id AND inv.tenant_id = last_out.tenant_id " +
"WHERE inv.inventory_status = '1' " +
" AND inv.inventory_qty > 0 " +
" AND mi.active_flag = '1' " +
" AND mi.del_flag = '0' " +
" AND ( " +
" last_out.lastOutstockTime IS NULL " +
" OR DATEDIFF(DAY, last_out.lastOutstockTime, GETDATE()) >= 180 " +
" ) " +
" AND (#{tenantId} IS NULL OR inv.tenant_id = #{tenantId}) " +
" AND (#{materialCategoryId} IS NULL OR mc.material_category_id = #{materialCategoryId}) " +
"GROUP BY " +
" inv.tenant_id, " +
" inv.material_id, " +
" mi.material_code, " +
" mi.material_name, " +
" mc.material_category_name, " +
" mi.material_unit, " +
" last_out.lastOutstockTime, " +
" mi.material_spec, " +
" w.warehouse_name, " +
" first_in.firstInstockTime " +
"ORDER BY inv.tenant_id, stagnantDays DESC, stagnantInventoryQty DESC")
List<WmsStagnantInventoryVo> selectStagnantInventory(@Param("tenantId") String tenantId,
@Param("materialCategoryId") Long materialCategoryId);
/**
*
*/
@Select("SELECT " +
" ic.tenant_id AS tenantId, " +
" ic.check_code AS checkCode, " +
" CASE ic.check_type " +
" WHEN '0' THEN '抽检' " +
" WHEN '1' THEN '盘点' " +
" WHEN '2' THEN '库位/货架盘点' " +
" ELSE '未知' " +
" END AS checkType, " +
" ic.plan_warehouse_id AS warehouseId, " +
" w.warehouse_name AS warehouseName, " +
" icr.material_id AS materialId, " +
" mi.material_code AS materialCode, " +
" mi.material_name AS materialName, " +
" mc.material_category_name AS materialCategoryName, " +
" icr.inventory_qty AS bookInventoryQty, " +
" icr.check_qty AS actualInventoryQty, " +
" (icr.check_qty - icr.inventory_qty) AS differenceQty, " +
" CASE " +
" WHEN ABS(icr.check_qty - icr.inventory_qty) = 0 THEN '无差异' " +
" WHEN (icr.check_qty - icr.inventory_qty) > 0 THEN '盘盈' " +
" ELSE '盘亏' " +
" END AS differenceType, " +
" CASE " +
" WHEN icr.inventory_qty > 0 THEN " +
" ABS((icr.check_qty - icr.inventory_qty) / icr.inventory_qty * 100) " +
" ELSE 0 " +
" END AS differenceRate, " +
" CASE " +
" WHEN ABS(icr.check_qty - icr.inventory_qty) > 10 THEN '重大差异' " +
" WHEN ABS(icr.check_qty - icr.inventory_qty) > 5 THEN '一般差异' " +
" WHEN ABS(icr.check_qty - icr.inventory_qty) > 0 THEN '轻微差异' " +
" ELSE '无差异' " +
" END AS differenceLevel, " +
" ic.create_time AS checkTime, " +
" ic.create_by AS checkBy " +
"FROM wms_inventory_check ic " +
"INNER JOIN wms_base_warehouse w ON ic.plan_warehouse_id = w.warehouse_id " +
"INNER JOIN wms_inventory_check_record icr ON icr.check_code = ic.check_code AND icr.tenant_id = ic.tenant_id " +
"INNER JOIN base_material_info_copy1 mi ON icr.material_id = mi.material_id " +
"INNER JOIN base_material_category mc ON mi.material_category_id = mc.material_category_id " +
"WHERE ic.check_status = '3' " +
" AND (#{tenantId} IS NULL OR ic.tenant_id = #{tenantId}) " +
" AND (#{materialCategoryId} IS NULL OR mc.material_category_id = #{materialCategoryId}) " +
"ORDER BY ic.tenant_id, ic.check_code, differenceRate DESC")
List<WmsInventoryDifferenceVo> selectInventoryDifference(@Param("tenantId") String tenantId,
@Param("materialCategoryId") Long materialCategoryId);
/**
*
*/
@Select("SELECT " +
" base_data.tenantId, " +
" base_data.materialId, " +
" base_data.materialCode, " +
" base_data.materialName, " +
" base_data.materialCategoryName, " +
" base_data.statisticsMonth, " +
" base_data.beginInventoryQty, " +
" base_data.endInventoryQty, " +
" base_data.monthOutstockQty, " +
" CASE " +
" WHEN (base_data.beginInventoryQty + base_data.endInventoryQty) > 0 THEN " +
" CAST(base_data.monthOutstockQty * 2.0 / (base_data.beginInventoryQty + base_data.endInventoryQty) * 100 AS DECIMAL(10,2)) " +
" ELSE 0 " +
" END AS inventoryTurnoverRate, " +
" CASE " +
" WHEN base_data.endInventoryQty > 0 THEN " +
" CAST(base_data.monthOutstockQty / base_data.endInventoryQty * 100 AS DECIMAL(10,2)) " +
" ELSE 0 " +
" END AS simpleTurnoverRate, " +
" CASE " +
" WHEN base_data.monthOutstockQty = 0 THEN '无流动' " +
" WHEN CAST(base_data.monthOutstockQty * 2.0 / (base_data.beginInventoryQty + base_data.endInventoryQty) * 100 AS DECIMAL(10,2)) >= 100 THEN '快速周转' " +
" WHEN CAST(base_data.monthOutstockQty * 2.0 / (base_data.beginInventoryQty + base_data.endInventoryQty) * 100 AS DECIMAL(10,2)) >= 50 THEN '正常周转' " +
" ELSE '缓慢周转' " +
" END AS turnoverEvaluation " +
"FROM ( " +
" SELECT " +
" inv.tenant_id AS tenantId, " +
" inv.material_id AS materialId, " +
" mi.material_code AS materialCode, " +
" mi.material_name AS materialName, " +
" mc.material_category_name AS materialCategoryName, " +
" CONVERT(VARCHAR(7), GETDATE(), 126) AS statisticsMonth, " +
" SUM(inv.inventory_qty) AS endInventoryQty, " +
" SUM(inv.inventory_qty) + ISNULL(month_out.monthOutstockQty, 0) - ISNULL(month_in.monthInstockQty, 0) AS beginInventoryQty, " +
" ISNULL(month_out.monthOutstockQty, 0) AS monthOutstockQty " +
" FROM wms_inventory inv " +
" INNER JOIN base_material_info_copy1 mi ON inv.material_id = mi.material_id " +
" INNER JOIN base_material_category mc ON mi.material_category_id = mc.material_category_id " +
" LEFT JOIN ( " +
" SELECT material_id, tenant_id, SUM(outstock_qty) AS monthOutstockQty " +
" FROM wms_outstock_record " +
" WHERE create_time >= DATEADD(MONTH, -1, GETDATE()) " +
" GROUP BY material_id, tenant_id " +
" ) month_out ON inv.material_id = month_out.material_id AND inv.tenant_id = month_out.tenant_id " +
" LEFT JOIN ( " +
" SELECT material_id, tenant_id, SUM(instock_qty) AS monthInstockQty " +
" FROM wms_instock_record " +
" WHERE create_time >= DATEADD(MONTH, -1, GETDATE()) " +
" GROUP BY material_id, tenant_id " +
" ) month_in ON inv.material_id = month_in.material_id AND inv.tenant_id = month_in.tenant_id " +
" WHERE inv.inventory_status = '1' " +
" AND (#{tenantId} IS NULL OR inv.tenant_id = #{tenantId}) " +
" AND (#{materialCategoryId} IS NULL OR mc.material_category_id = #{materialCategoryId}) " +
" GROUP BY inv.tenant_id, inv.material_id, mi.material_code, mi.material_name, mc.material_category_name, " +
" month_out.monthOutstockQty, month_in.monthInstockQty " +
") base_data " +
"WHERE base_data.beginInventoryQty > 0 OR base_data.endInventoryQty > 0 OR base_data.monthOutstockQty > 0 " +
"ORDER BY base_data.tenantId, inventoryTurnoverRate DESC")
List<WmsInventoryTurnoverVo> selectInventoryTurnover(@Param("tenantId") String tenantId,
@Param("materialCategoryId") Long materialCategoryId);
}

@ -0,0 +1,68 @@
package org.dromara.wms.service;
import org.dromara.wms.domain.vo.*;
import java.util.List;
/**
* WMSService
*
* @author hwmom
* @date 2025-01-20
*/
public interface IWmsReportService {
/**
* 退
*
* @param tenantId
* @param materialCategoryId ID
* @return 退
*/
List<WmsReturnReasonAnalysisVo> getReturnReasonAnalysis(String tenantId, Long materialCategoryId);
/**
*
*
* @param tenantId
* @param materialCategoryId ID
* @return
*/
List<WmsInventoryTrendAnalysisVo> getInventoryTrendAnalysis(String tenantId, Long materialCategoryId);
/**
*
*
* @param tenantId
* @param materialCategoryId ID
* @return
*/
List<WmsSafetyStockAlertVo> getSafetyStockAlert(String tenantId, Long materialCategoryId);
/**
*
*
* @param tenantId
* @param materialCategoryId ID
* @return
*/
List<WmsStagnantInventoryVo> getStagnantInventory(String tenantId, Long materialCategoryId);
/**
*
*
* @param tenantId
* @param materialCategoryId ID
* @return
*/
List<WmsInventoryDifferenceVo> getInventoryDifference(String tenantId, Long materialCategoryId);
/**
*
*
* @param tenantId
* @param materialCategoryId ID
* @return
*/
List<WmsInventoryTurnoverVo> getInventoryTurnover(String tenantId, Long materialCategoryId);
}

@ -0,0 +1,70 @@
package org.dromara.wms.service.impl;
import lombok.RequiredArgsConstructor;
import org.dromara.wms.domain.vo.*;
import org.dromara.wms.mapper.WmsReportMapper;
import org.dromara.wms.service.IWmsReportService;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* WMSService
*
* @author hwmom
* @date 2025-01-20
*/
@RequiredArgsConstructor
@Service
public class WmsReportServiceImpl implements IWmsReportService {
private final WmsReportMapper wmsReportMapper;
/**
* 退
*/
@Override
public List<WmsReturnReasonAnalysisVo> getReturnReasonAnalysis(String tenantId, Long materialCategoryId) {
return wmsReportMapper.selectReturnReasonAnalysis(tenantId, materialCategoryId);
}
/**
*
*/
@Override
public List<WmsInventoryTrendAnalysisVo> getInventoryTrendAnalysis(String tenantId, Long materialCategoryId) {
return wmsReportMapper.selectInventoryTrendAnalysis(tenantId, materialCategoryId);
}
/**
*
*/
@Override
public List<WmsSafetyStockAlertVo> getSafetyStockAlert(String tenantId, Long materialCategoryId) {
return wmsReportMapper.selectSafetyStockAlert(tenantId, materialCategoryId);
}
/**
*
*/
@Override
public List<WmsStagnantInventoryVo> getStagnantInventory(String tenantId, Long materialCategoryId) {
return wmsReportMapper.selectStagnantInventory(tenantId, materialCategoryId);
}
/**
*
*/
@Override
public List<WmsInventoryDifferenceVo> getInventoryDifference(String tenantId, Long materialCategoryId) {
return wmsReportMapper.selectInventoryDifference(tenantId, materialCategoryId);
}
/**
*
*/
@Override
public List<WmsInventoryTurnoverVo> getInventoryTurnover(String tenantId, Long materialCategoryId) {
return wmsReportMapper.selectInventoryTurnover(tenantId, materialCategoryId);
}
}
Loading…
Cancel
Save