From 58dd71c0c2e78b5dc035e3b488d4cb273f3f9567 Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Thu, 16 Oct 2025 17:44:55 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E5=85=A5=E5=BA=93=E6=98=8E=E7=BB=86excel?= =?UTF-8?q?=E5=AF=BC=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InventoryDetailQueryDTO.java | 21 ++++++++---- .../SmErpInventoryRecordsController.java | 19 +++++++++-- .../service/SmErpInventoryRecordsService.java | 7 ++++ .../SmErpInventoryRecordsServiceImpl.java | 34 ++++++++++++++++++- .../mapper/SmErpInventoryRecordsMapper.xml | 10 ++++-- 5 files changed, 79 insertions(+), 12 deletions(-) diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailQueryDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailQueryDTO.java index 860a1b686..69945db3f 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailQueryDTO.java +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailQueryDTO.java @@ -12,17 +12,26 @@ import java.time.LocalDate; public class InventoryDetailQueryDTO { /** - * 编号 + * 编号、商品名称、供应商(模糊查询) */ - @Schema(description="关键字") + @Schema(description="关键字(单据编号/商品名称/供应商)") private String key; /** - * 单据日期 + * 开始日期 */ - @Schema(description="单据日期") - private LocalDate inventoryDate; + @Schema(description="开始日期") + private LocalDate startDate; - @Schema(description="1入库 2出库") + /** + * 结束日期 + */ + @Schema(description="结束日期") + private LocalDate endDate; + + /** + * 1入库 2出库 + */ + @Schema(description="订单类型:1=入库,2=出库") private Integer orderType; } diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpInventoryRecordsController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpInventoryRecordsController.java index 2abe634d6..d6a0649be 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpInventoryRecordsController.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/SmErpInventoryRecordsController.java @@ -323,15 +323,30 @@ public class SmErpInventoryRecordsController { /** - * 分页查询 + * 分页查询出入库明细 * @param page 分页对象 + * @param inventoryDetailQueryDTO 查询条件 * @return */ - @Operation(summary = "分页获取出入库明细" , description = "出库明细,入库明细" ) + @Operation(summary = "分页获取出入库明细" , description = "出库明细,入库明细,支持日期区间查询" ) @GetMapping("/inventoryType/page" ) public R getinventoryTypePage(@ParameterObject Page page, @ParameterObject InventoryDetailQueryDTO inventoryDetailQueryDTO) { return R.ok(smErpInventoryRecordsService.getInventoryDetailPage(page, inventoryDetailQueryDTO)); } + + /** + * 导出出入库明细 + * @param inventoryDetailQueryDTO 查询条件 + * @return excel 文件流 + */ + @ResponseExcel + @GetMapping("/inventoryType/export") + @Operation(summary = "导出出入库明细", description = "导出出入库明细数据为Excel文件") + @SysLog("导出出入库明细") + @PreAuthorize("@pms.hasPermission('supermarket_smErpInventoryRecords_export')") + public List exportInventoryDetail(@ParameterObject InventoryDetailQueryDTO inventoryDetailQueryDTO) { + return smErpInventoryRecordsService.exportInventoryDetail(inventoryDetailQueryDTO); + } /** * *根据单号获取明细 diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpInventoryRecordsService.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpInventoryRecordsService.java index 7cb06a6e8..a9edca6c2 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpInventoryRecordsService.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SmErpInventoryRecordsService.java @@ -102,6 +102,13 @@ public interface SmErpInventoryRecordsService extends IService exportInventoryRecordsSummary(SmErpInventoryRecordsEntity smErpInventoryRecords, Long[] ids); + /** + * 导出出入库明细 + * @param inventoryDetailQueryDTO 查询条件 + * @return 明细导出数据列表 + */ + List exportInventoryDetail(InventoryDetailQueryDTO inventoryDetailQueryDTO); + /** * 批量删除 * @param list diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java index 77032dcbc..884c7f513 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java @@ -1285,6 +1285,38 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl(summaryMap.values()); } - + @Override + public List exportInventoryDetail(InventoryDetailQueryDTO inventoryDetailQueryDTO) { + // 查询明细数据(不分页) + Page page = new Page<>(1, 10000); + Page pageData = baseMapper.getInventoryDetailPageByScope(page, inventoryDetailQueryDTO, DataScope.of()); + + List records = pageData.getRecords(); + + // 转换为导出DTO + return records.stream().map(vo -> { + InventoryDetailExcelDTO dto = new InventoryDetailExcelDTO(); + dto.setInventoryCode(vo.getInventoryCode()); + dto.setInventoryDate(vo.getInventoryDate()); + dto.setProductName(vo.getProductName()); + dto.setSpec(vo.getSpec()); + dto.setUnit(vo.getUnit()); + dto.setPackageNum(vo.getPackageNum()); + dto.setQuantity(vo.getQuantity()); + + // 金额从分转换为元 + if (vo.getUnitPrice() != null) { + dto.setUnitPrice(BigDecimal.valueOf(vo.getUnitPrice()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)); + } + if (vo.getTotalAmount() != null) { + dto.setTotalAmount(BigDecimal.valueOf(vo.getTotalAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)); + } + + dto.setSupplierName(vo.getSupplierName()); + dto.setRemark(vo.getRemark()); + + return dto; + }).collect(Collectors.toList()); + } } \ No newline at end of file diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SmErpInventoryRecordsMapper.xml b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SmErpInventoryRecordsMapper.xml index 62052c9ba..e54875ef6 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SmErpInventoryRecordsMapper.xml +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SmErpInventoryRecordsMapper.xml @@ -45,7 +45,8 @@ u.package_num, u.retail_price, supp.supplier_name, - r.inventory_date + r.inventory_date, + r.dept_id FROM sm_erp_inventory_records_item i LEFT JOIN sm_erp_inventory_records r on i.inventory_id = r.id @@ -62,8 +63,11 @@ and r.order_type = #{inventoryDetailQueryDTO.orderType} - - and r.inventory_date = #{inventoryDetailQueryDTO.inventoryDate} + + and r.inventory_date >= #{inventoryDetailQueryDTO.startDate} + + + and r.inventory_date <= #{inventoryDetailQueryDTO.endDate} and (r.inventory_code like concat('%',#{inventoryDetailQueryDTO.key},'%') From b134b8d539f95545227714874dcb853114bc7043 Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Fri, 17 Oct 2025 00:10:39 +0800 Subject: [PATCH 2/6] =?UTF-8?q?=E8=B6=85=E5=B8=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InventoryDetailQueryDTO.java | 6 ++ .../vo/report/InventoryInOutReportVO.java | 50 +++++++++ .../vo/report/InventoryInOutSummaryVO.java | 51 +++++++++ .../vo/report/InventoryReportVO.java | 100 ++++++++++++++++++ .../vo/report/InventoryStocktakeReportVO.java | 44 ++++++++ .../supermarket/vo/report/ProfitReportVO.java | 60 +++++++++++ .../supermarket/vo/report/SalesReportVO.java | 60 +++++++++++ .../vo/report/StocktakeReportVO.java | 58 ++++++++++ .../RealtimeSalesReportController.java | 2 + .../SmErpInventoryRecordsServiceImpl.java | 45 ++++---- .../mapper/SmErpInventoryRecordsMapper.xml | 3 + 11 files changed, 458 insertions(+), 21 deletions(-) create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryInOutReportVO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryInOutSummaryVO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryReportVO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryStocktakeReportVO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/ProfitReportVO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/SalesReportVO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/StocktakeReportVO.java diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailQueryDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailQueryDTO.java index 69945db3f..a83e2d516 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailQueryDTO.java +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailQueryDTO.java @@ -34,4 +34,10 @@ public class InventoryDetailQueryDTO { */ @Schema(description="订单类型:1=入库,2=出库") private Integer orderType; + + /** + * 单据类型:1=采购申请,2=采购订单,3=采购入库,4=采购退货,5=零售出库,6=零售退货,7=库存盘点 + */ + @Schema(description="单据类型:3=采购入库,6=零售退货") + private String inventoryType; } diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryInOutReportVO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryInOutReportVO.java new file mode 100644 index 000000000..9b26c7c90 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryInOutReportVO.java @@ -0,0 +1,50 @@ +package com.bsd.pjcloud.supermarket.vo.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Schema(description = "进销存报表返回数据(按商品汇总)") +public class InventoryInOutReportVO { + + @Schema(description = "商品ID") + private Long productId; + + @Schema(description = "商品编码") + private String productCode; + + @Schema(description = "商品名称") + private String productName; + + @Schema(description = "分类ID") + private Long categoryId; + + @Schema(description = "分类名称") + private String categoryName; + + @Schema(description = "单位(基础单位)") + private String unit; + + @Schema(description = "期初数量(基础单位)") + private Double openingQty; + + @Schema(description = "入库数量(基础单位)") + private Double inQty; + + @Schema(description = "出库数量(基础单位)") + private Double outQty; + + @Schema(description = "期末数量(基础单位)") + private Double closingQty; + + @Schema(description = "入库金额(元)") + private BigDecimal inAmount; + + @Schema(description = "出库金额(元)") + private BigDecimal outAmount; +} + + + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryInOutSummaryVO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryInOutSummaryVO.java new file mode 100644 index 000000000..68b791752 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryInOutSummaryVO.java @@ -0,0 +1,51 @@ +package com.bsd.pjcloud.supermarket.vo.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 进销存报表汇总数据 + * + * @author pjcloud + * @date 2024-10-15 + */ +@Data +@Schema(description = "进销存报表汇总数据") +public class InventoryInOutSummaryVO { + + @Schema(description = "统计商品总数") + private Integer totalProducts; + + @Schema(description = "有发生额的商品数量") + private Integer changedProducts; + + @Schema(description = "总期初库存数量") + private Double totalOpeningQty; + + @Schema(description = "总入库数量") + private Double totalInQty; + + @Schema(description = "总出库数量") + private Double totalOutQty; + + @Schema(description = "总期末库存数量") + private Double totalClosingQty; + + @Schema(description = "总入库金额(元)") + private BigDecimal totalInAmount; + + @Schema(description = "总出库金额(元)") + private BigDecimal totalOutAmount; + + @Schema(description = "库存周转率(出库数量/平均库存)") + private BigDecimal turnoverRate; + + @Schema(description = "平均单价(总金额/总数量)") + private BigDecimal avgPrice; +} + + + + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryReportVO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryReportVO.java new file mode 100644 index 000000000..f78d66e89 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryReportVO.java @@ -0,0 +1,100 @@ +package com.bsd.pjcloud.supermarket.vo.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 进销存报表VO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@Schema(description = "进销存报表VO") +public class InventoryReportVO { + + @Schema(description = "商品ID") + private Long productId; + + @Schema(description = "商品编码") + private String productCode; + + @Schema(description = "商品名称") + private String productName; + + @Schema(description = "分类名称") + private String categoryName; + + @Schema(description = "规格") + private String spec; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "装数") + private Integer packageNum; + + @Schema(description = "参考进价(元)") + private BigDecimal purchasePrice; + + @Schema(description = "零售价(元)") + private BigDecimal retailPrice; + + @Schema(description = "期初库存") + private Double openingQty; + + @Schema(description = "期初金额(元)") + private BigDecimal openingAmount; + + @Schema(description = "采购入库数量") + private Double purchaseInQty; + + @Schema(description = "采购入库金额(元)") + private BigDecimal purchaseInAmount; + + @Schema(description = "采购退货数量") + private Double purchaseReturnQty; + + @Schema(description = "采购退货金额(元)") + private BigDecimal purchaseReturnAmount; + + @Schema(description = "销售数量") + private Double salesQty; + + @Schema(description = "销售金额(元)") + private BigDecimal salesAmount; + + @Schema(description = "销售退货数量") + private Double salesReturnQty; + + @Schema(description = "销售退货金额(元)") + private BigDecimal salesReturnAmount; + + @Schema(description = "实际销售数量") + private Double actualSalesQty; + + @Schema(description = "实际销售金额(元)") + private BigDecimal actualSalesAmount; + + @Schema(description = "销售成本(元)") + private BigDecimal salesCost; + + @Schema(description = "期末数量") + private Double closingQty; + + @Schema(description = "期末库存金额(元)") + private BigDecimal closingAmount; + + @Schema(description = "期末库存售额(元)") + private BigDecimal closingRetailAmount; + + @Schema(description = "销售毛利(元)") + private BigDecimal grossProfit; + + @Schema(description = "首次进货日") + private LocalDate firstPurchaseDate; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryStocktakeReportVO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryStocktakeReportVO.java new file mode 100644 index 000000000..e45bf9ae3 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/InventoryStocktakeReportVO.java @@ -0,0 +1,44 @@ +package com.bsd.pjcloud.supermarket.vo.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +@Data +@Schema(description = "盘存报表返回数据(按商品汇总)") +public class InventoryStocktakeReportVO { + + @Schema(description = "商品ID") + private Long productId; + + @Schema(description = "商品编码") + private String productCode; + + @Schema(description = "商品名称") + private String productName; + + @Schema(description = "分类ID") + private Long categoryId; + + @Schema(description = "分类名称") + private String categoryName; + + @Schema(description = "单位(基础单位)") + private String unit; + + @Schema(description = "账面数量(基础单位)") + private Double bookQty; + + @Schema(description = "实盘数量(基础单位)") + private Double physicalQty; + + @Schema(description = "差异数量(基础单位,实盘-账面)") + private Double diffQty; + + @Schema(description = "差异金额(元)") + private BigDecimal diffAmount; +} + + + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/ProfitReportVO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/ProfitReportVO.java new file mode 100644 index 000000000..e08a88105 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/ProfitReportVO.java @@ -0,0 +1,60 @@ +package com.bsd.pjcloud.supermarket.vo.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 毛利报表VO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@Schema(description = "毛利报表VO") +public class ProfitReportVO { + + @Schema(description = "商品ID") + private Long productId; + + @Schema(description = "商品编码") + private String productCode; + + @Schema(description = "商品名称") + private String productName; + + @Schema(description = "分类名称") + private String categoryName; + + @Schema(description = "规格") + private String spec; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "销售数量") + private Double salesQty; + + @Schema(description = "销售金额(元)") + private BigDecimal salesAmount; + + @Schema(description = "销售单价(元)") + private BigDecimal salesPrice; + + @Schema(description = "成本单价(元)") + private BigDecimal costPrice; + + @Schema(description = "销售成本(元)") + private BigDecimal salesCost; + + @Schema(description = "销售毛利(元)") + private BigDecimal grossProfit; + + @Schema(description = "毛利率(%)") + private BigDecimal profitMargin; + + @Schema(description = "销售笔数") + private Integer salesCount; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/SalesReportVO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/SalesReportVO.java new file mode 100644 index 000000000..478209b64 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/SalesReportVO.java @@ -0,0 +1,60 @@ +package com.bsd.pjcloud.supermarket.vo.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 销售报表VO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@Schema(description = "销售报表VO") +public class SalesReportVO { + + @Schema(description = "商品ID") + private Long productId; + + @Schema(description = "商品编码") + private String productCode; + + @Schema(description = "商品名称") + private String productName; + + @Schema(description = "分类名称") + private String categoryName; + + @Schema(description = "规格") + private String spec; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "销售数量") + private Double salesQty; + + @Schema(description = "销售金额(元)") + private BigDecimal salesAmount; + + @Schema(description = "销售单价(元)") + private BigDecimal salesPrice; + + @Schema(description = "销售笔数") + private Integer salesCount; + + @Schema(description = "退货数量") + private Double returnQty; + + @Schema(description = "退货金额(元)") + private BigDecimal returnAmount; + + @Schema(description = "实际销售数量") + private Double actualSalesQty; + + @Schema(description = "实际销售金额(元)") + private BigDecimal actualSalesAmount; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/StocktakeReportVO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/StocktakeReportVO.java new file mode 100644 index 000000000..308a4e5ef --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/vo/report/StocktakeReportVO.java @@ -0,0 +1,58 @@ +package com.bsd.pjcloud.supermarket.vo.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 盘存报表VO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@Schema(description = "盘存报表VO") +public class StocktakeReportVO { + + @Schema(description = "盘点单号") + private String stocktakeCode; + + @Schema(description = "盘点日期") + private LocalDate stocktakeDate; + + @Schema(description = "商品ID") + private Long productId; + + @Schema(description = "商品编码") + private String productCode; + + @Schema(description = "商品名称") + private String productName; + + @Schema(description = "分类名称") + private String categoryName; + + @Schema(description = "规格") + private String spec; + + @Schema(description = "单位") + private String unit; + + @Schema(description = "账面数量") + private Double bookQty; + + @Schema(description = "实盘数量") + private Double physicalQty; + + @Schema(description = "差异数量(实盘-账面)") + private Double diffQty; + + @Schema(description = "差异金额(元)") + private BigDecimal diffAmount; + + @Schema(description = "备注") + private String remark; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/RealtimeSalesReportController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/RealtimeSalesReportController.java index 5c9d52e37..8897bddbc 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/RealtimeSalesReportController.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/RealtimeSalesReportController.java @@ -11,6 +11,7 @@ import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpHeaders; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.*; @@ -21,6 +22,7 @@ import org.springframework.web.bind.annotation.*; * @author pjcloud * @date 2025-01-27 10:00:00 */ +@Slf4j @RestController @RequiredArgsConstructor @RequestMapping("/realtimeSalesReport") diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java index 884c7f513..5d3dfbc96 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SmErpInventoryRecordsServiceImpl.java @@ -1287,36 +1287,39 @@ public class SmErpInventoryRecordsServiceImpl extends ServiceImpl exportInventoryDetail(InventoryDetailQueryDTO inventoryDetailQueryDTO) { - // 查询明细数据(不分页) - Page page = new Page<>(1, 10000); + // 查询出入库明细数据 + Page page = new Page<>(1, 100000); // 设置一个较大的分页值以获取所有数据 Page pageData = baseMapper.getInventoryDetailPageByScope(page, inventoryDetailQueryDTO, DataScope.of()); List records = pageData.getRecords(); - // 转换为导出DTO - return records.stream().map(vo -> { - InventoryDetailExcelDTO dto = new InventoryDetailExcelDTO(); - dto.setInventoryCode(vo.getInventoryCode()); - dto.setInventoryDate(vo.getInventoryDate()); - dto.setProductName(vo.getProductName()); - dto.setSpec(vo.getSpec()); - dto.setUnit(vo.getUnit()); - dto.setPackageNum(vo.getPackageNum()); - dto.setQuantity(vo.getQuantity()); + // 转换为Excel DTO + List excelList = new ArrayList<>(); + for (InventoryRecordPageVo record : records) { + InventoryDetailExcelDTO excelDTO = new InventoryDetailExcelDTO(); + excelDTO.setInventoryCode(record.getInventoryCode()); + excelDTO.setInventoryDate(record.getInventoryDate()); + excelDTO.setProductName(record.getProductName()); + excelDTO.setSpec(record.getSpec()); + excelDTO.setUnit(record.getUnit()); + excelDTO.setPackageNum(record.getPackageNum()); + excelDTO.setQuantity(record.getQuantity()); - // 金额从分转换为元 - if (vo.getUnitPrice() != null) { - dto.setUnitPrice(BigDecimal.valueOf(vo.getUnitPrice()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)); + // 类型转换:Integer(分) → BigDecimal(元) + if (record.getUnitPrice() != null) { + excelDTO.setUnitPrice(new BigDecimal(record.getUnitPrice()).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP)); } - if (vo.getTotalAmount() != null) { - dto.setTotalAmount(BigDecimal.valueOf(vo.getTotalAmount()).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP)); + if (record.getTotalAmount() != null) { + excelDTO.setTotalAmount(new BigDecimal(record.getTotalAmount()).divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP)); } - dto.setSupplierName(vo.getSupplierName()); - dto.setRemark(vo.getRemark()); + excelDTO.setSupplierName(record.getSupplierName()); + excelDTO.setRemark(record.getRemark()); - return dto; - }).collect(Collectors.toList()); + excelList.add(excelDTO); + } + + return excelList; } } \ No newline at end of file diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SmErpInventoryRecordsMapper.xml b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SmErpInventoryRecordsMapper.xml index e54875ef6..3dbb29c6b 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SmErpInventoryRecordsMapper.xml +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SmErpInventoryRecordsMapper.xml @@ -63,6 +63,9 @@ and r.order_type = #{inventoryDetailQueryDTO.orderType} + + and r.inventory_type = #{inventoryDetailQueryDTO.inventoryType} + and r.inventory_date >= #{inventoryDetailQueryDTO.startDate} From acf14450e9d3fc610de8174b8a806d3163592176 Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Fri, 17 Oct 2025 00:14:49 +0800 Subject: [PATCH 3/6] =?UTF-8?q?=E8=B6=85=E5=B8=82=E6=8A=A5=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../report/InventoryInOutReportExcelDTO.java | 66 +++++++++ .../report/InventoryInOutReportQueryDTO.java | 42 ++++++ .../dto/report/InventoryReportExcelDTO.java | 128 ++++++++++++++++++ .../dto/report/InventoryReportQueryDTO.java | 39 ++++++ .../InventoryStocktakeReportQueryDTO.java | 42 ++++++ .../dto/report/ProfitReportExcelDTO.java | 75 ++++++++++ .../dto/report/ProfitReportQueryDTO.java | 42 ++++++ .../dto/report/SalesReportExcelDTO.java | 75 ++++++++++ .../dto/report/SalesReportQueryDTO.java | 36 +++++ .../dto/report/StocktakeReportExcelDTO.java | 72 ++++++++++ .../dto/report/StocktakeReportQueryDTO.java | 39 ++++++ 11 files changed, 656 insertions(+) create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryInOutReportExcelDTO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryInOutReportQueryDTO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryReportExcelDTO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryReportQueryDTO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryStocktakeReportQueryDTO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/ProfitReportExcelDTO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/ProfitReportQueryDTO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/SalesReportExcelDTO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/SalesReportQueryDTO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/StocktakeReportExcelDTO.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/StocktakeReportQueryDTO.java diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryInOutReportExcelDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryInOutReportExcelDTO.java new file mode 100644 index 000000000..ee2cdae77 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryInOutReportExcelDTO.java @@ -0,0 +1,66 @@ +package com.bsd.pjcloud.supermarket.dto.report; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 进销存报表导出DTO + * + * @author pjcloud + * @date 2024-10-15 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(25) +@ColumnWidth(15) +public class InventoryInOutReportExcelDTO { + + @ExcelProperty(value = "商品编码", index = 0) + @ColumnWidth(20) + private String productCode; + + @ExcelProperty(value = "商品名称", index = 1) + @ColumnWidth(30) + private String productName; + + @ExcelProperty(value = "分类", index = 2) + @ColumnWidth(15) + private String categoryName; + + @ExcelProperty(value = "单位", index = 3) + @ColumnWidth(10) + private String unit; + + @ExcelProperty(value = "期初数量", index = 4) + @ColumnWidth(15) + private Double openingQty; + + @ExcelProperty(value = "入库数量", index = 5) + @ColumnWidth(15) + private Double inQty; + + @ExcelProperty(value = "出库数量", index = 6) + @ColumnWidth(15) + private Double outQty; + + @ExcelProperty(value = "期末数量", index = 7) + @ColumnWidth(15) + private Double closingQty; + + @ExcelProperty(value = "入库金额(元)", index = 8) + @ColumnWidth(15) + private BigDecimal inAmount; + + @ExcelProperty(value = "出库金额(元)", index = 9) + @ColumnWidth(15) + private BigDecimal outAmount; +} + + + + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryInOutReportQueryDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryInOutReportQueryDTO.java new file mode 100644 index 000000000..39c6b000f --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryInOutReportQueryDTO.java @@ -0,0 +1,42 @@ +package com.bsd.pjcloud.supermarket.dto.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Data +@Schema(description = "进销存报表查询参数") +public class InventoryInOutReportQueryDTO { + + @Schema(description = "开始日期(按天统计时使用)") + private LocalDate startDate; + + @Schema(description = "结束日期(按天统计时使用)") + private LocalDate endDate; + + @Schema(description = "开始时间(精确到时分秒,优先级高于日期)") + private LocalDateTime startTime; + + @Schema(description = "结束时间(精确到时分秒,优先级高于日期)") + private LocalDateTime endTime; + + @Schema(description = "商品名称模糊查询") + private String productName; + + @Schema(description = "商品编码模糊查询") + private String productCode; + + @Schema(description = "分类ID") + private Long categoryId; + + @Schema(description = "是否仅显示有发生额的商品") + private Boolean onlyChanged; + + @Schema(description = "返回Top N(可选)") + private Integer topLimit; +} + + + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryReportExcelDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryReportExcelDTO.java new file mode 100644 index 000000000..3611f032e --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryReportExcelDTO.java @@ -0,0 +1,128 @@ +package com.bsd.pjcloud.supermarket.dto.report; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 进销存报表Excel导出DTO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(25) +@ColumnWidth(15) +public class InventoryReportExcelDTO { + + @ExcelProperty(value = "商品编码", index = 0) + @ColumnWidth(20) + private String productCode; + + @ExcelProperty(value = "商品名称", index = 1) + @ColumnWidth(30) + private String productName; + + @ExcelProperty(value = "分类", index = 2) + @ColumnWidth(15) + private String categoryName; + + @ExcelProperty(value = "规格", index = 3) + @ColumnWidth(20) + private String spec; + + @ExcelProperty(value = "单位", index = 4) + @ColumnWidth(10) + private String unit; + + @ExcelProperty(value = "装数", index = 5) + @ColumnWidth(10) + private Integer packageNum; + + @ExcelProperty(value = "参考进价(元)", index = 6) + @ColumnWidth(15) + private BigDecimal purchasePrice; + + @ExcelProperty(value = "零售价(元)", index = 7) + @ColumnWidth(15) + private BigDecimal retailPrice; + + @ExcelProperty(value = "期初库存", index = 8) + @ColumnWidth(15) + private Double openingQty; + + @ExcelProperty(value = "期初金额(元)", index = 9) + @ColumnWidth(15) + private BigDecimal openingAmount; + + @ExcelProperty(value = "采购入库数量", index = 10) + @ColumnWidth(15) + private Double purchaseInQty; + + @ExcelProperty(value = "采购入库金额(元)", index = 11) + @ColumnWidth(18) + private BigDecimal purchaseInAmount; + + @ExcelProperty(value = "采购退货数量", index = 12) + @ColumnWidth(15) + private Double purchaseReturnQty; + + @ExcelProperty(value = "采购退货金额(元)", index = 13) + @ColumnWidth(18) + private BigDecimal purchaseReturnAmount; + + @ExcelProperty(value = "销售数量", index = 14) + @ColumnWidth(15) + private Double salesQty; + + @ExcelProperty(value = "销售金额(元)", index = 15) + @ColumnWidth(15) + private BigDecimal salesAmount; + + @ExcelProperty(value = "销售退货数量", index = 16) + @ColumnWidth(15) + private Double salesReturnQty; + + @ExcelProperty(value = "销售退货金额(元)", index = 17) + @ColumnWidth(18) + private BigDecimal salesReturnAmount; + + @ExcelProperty(value = "实际销售数量", index = 18) + @ColumnWidth(15) + private Double actualSalesQty; + + @ExcelProperty(value = "实际销售金额(元)", index = 19) + @ColumnWidth(18) + private BigDecimal actualSalesAmount; + + @ExcelProperty(value = "销售成本(元)", index = 20) + @ColumnWidth(15) + private BigDecimal salesCost; + + @ExcelProperty(value = "期末数量", index = 21) + @ColumnWidth(15) + private Double closingQty; + + @ExcelProperty(value = "期末库存金额(元)", index = 22) + @ColumnWidth(18) + private BigDecimal closingAmount; + + @ExcelProperty(value = "期末库存售额(元)", index = 23) + @ColumnWidth(18) + private BigDecimal closingRetailAmount; + + @ExcelProperty(value = "销售毛利(元)", index = 24) + @ColumnWidth(15) + private BigDecimal grossProfit; + + @ExcelProperty(value = "首次进货日", index = 25) + @ColumnWidth(15) + private LocalDate firstPurchaseDate; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryReportQueryDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryReportQueryDTO.java new file mode 100644 index 000000000..f0873d274 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryReportQueryDTO.java @@ -0,0 +1,39 @@ +package com.bsd.pjcloud.supermarket.dto.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDate; + +/** + * 进销存报表查询条件DTO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@Schema(description = "进销存报表查询条件") +public class InventoryReportQueryDTO { + + @Schema(description = "商品名称(模糊查询)") + private String productName; + + @Schema(description = "商品编码(模糊查询)") + private String productCode; + + @Schema(description = "分类ID") + private Long categoryId; + + @Schema(description = "供应商ID") + private Long supplierId; + + @Schema(description = "开始日期") + private LocalDate startDate; + + @Schema(description = "结束日期") + private LocalDate endDate; + + @Schema(description = "只显示有变动的商品") + private Boolean onlyChanged; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryStocktakeReportQueryDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryStocktakeReportQueryDTO.java new file mode 100644 index 000000000..af4f16f4d --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/InventoryStocktakeReportQueryDTO.java @@ -0,0 +1,42 @@ +package com.bsd.pjcloud.supermarket.dto.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +@Data +@Schema(description = "盘存报表查询参数") +public class InventoryStocktakeReportQueryDTO { + + @Schema(description = "开始日期(按天统计时使用)") + private LocalDate startDate; + + @Schema(description = "结束日期(按天统计时使用)") + private LocalDate endDate; + + @Schema(description = "开始时间(精确到时分秒,优先级高于日期)") + private LocalDateTime startTime; + + @Schema(description = "结束时间(精确到时分秒,优先级高于日期)") + private LocalDateTime endTime; + + @Schema(description = "商品名称模糊查询") + private String productName; + + @Schema(description = "商品编码模糊查询") + private String productCode; + + @Schema(description = "分类ID") + private Long categoryId; + + @Schema(description = "仓库ID,可选") + private String warehouseId; + + @Schema(description = "仅显示存在差异的商品") + private Boolean onlyDiff; +} + + + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/ProfitReportExcelDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/ProfitReportExcelDTO.java new file mode 100644 index 000000000..76f0ace23 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/ProfitReportExcelDTO.java @@ -0,0 +1,75 @@ +package com.bsd.pjcloud.supermarket.dto.report; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 毛利报表Excel导出DTO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(25) +@ColumnWidth(15) +public class ProfitReportExcelDTO { + + @ExcelProperty(value = "商品编码", index = 0) + @ColumnWidth(20) + private String productCode; + + @ExcelProperty(value = "商品名称", index = 1) + @ColumnWidth(30) + private String productName; + + @ExcelProperty(value = "分类", index = 2) + @ColumnWidth(15) + private String categoryName; + + @ExcelProperty(value = "规格", index = 3) + @ColumnWidth(20) + private String spec; + + @ExcelProperty(value = "单位", index = 4) + @ColumnWidth(10) + private String unit; + + @ExcelProperty(value = "销售数量", index = 5) + @ColumnWidth(15) + private Double salesQty; + + @ExcelProperty(value = "销售金额(元)", index = 6) + @ColumnWidth(15) + private BigDecimal salesAmount; + + @ExcelProperty(value = "销售单价(元)", index = 7) + @ColumnWidth(15) + private BigDecimal salesPrice; + + @ExcelProperty(value = "成本单价(元)", index = 8) + @ColumnWidth(15) + private BigDecimal costPrice; + + @ExcelProperty(value = "销售成本(元)", index = 9) + @ColumnWidth(15) + private BigDecimal salesCost; + + @ExcelProperty(value = "销售毛利(元)", index = 10) + @ColumnWidth(15) + private BigDecimal grossProfit; + + @ExcelProperty(value = "毛利率(%)", index = 11) + @ColumnWidth(12) + private BigDecimal profitMargin; + + @ExcelProperty(value = "销售笔数", index = 12) + @ColumnWidth(12) + private Integer salesCount; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/ProfitReportQueryDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/ProfitReportQueryDTO.java new file mode 100644 index 000000000..69ab379e9 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/ProfitReportQueryDTO.java @@ -0,0 +1,42 @@ +package com.bsd.pjcloud.supermarket.dto.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDate; + +/** + * 毛利报表查询条件DTO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@Schema(description = "毛利报表查询条件") +public class ProfitReportQueryDTO { + + @Schema(description = "商品名称(模糊查询)") + private String productName; + + @Schema(description = "商品编码(模糊查询)") + private String productCode; + + @Schema(description = "分类ID") + private Long categoryId; + + @Schema(description = "供应商ID") + private Long supplierId; + + @Schema(description = "开始日期") + private LocalDate startDate; + + @Schema(description = "结束日期") + private LocalDate endDate; + + @Schema(description = "排序方式:profit-按毛利降序,margin-按毛利率降序") + private String sortBy; + + @Schema(description = "只显示有销售的商品") + private Boolean onlyWithSales; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/SalesReportExcelDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/SalesReportExcelDTO.java new file mode 100644 index 000000000..00fe6f5e1 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/SalesReportExcelDTO.java @@ -0,0 +1,75 @@ +package com.bsd.pjcloud.supermarket.dto.report; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * 销售报表Excel导出DTO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(25) +@ColumnWidth(15) +public class SalesReportExcelDTO { + + @ExcelProperty(value = "商品编码", index = 0) + @ColumnWidth(20) + private String productCode; + + @ExcelProperty(value = "商品名称", index = 1) + @ColumnWidth(30) + private String productName; + + @ExcelProperty(value = "分类", index = 2) + @ColumnWidth(15) + private String categoryName; + + @ExcelProperty(value = "规格", index = 3) + @ColumnWidth(20) + private String spec; + + @ExcelProperty(value = "单位", index = 4) + @ColumnWidth(10) + private String unit; + + @ExcelProperty(value = "销售数量", index = 5) + @ColumnWidth(15) + private Double salesQty; + + @ExcelProperty(value = "销售金额(元)", index = 6) + @ColumnWidth(15) + private BigDecimal salesAmount; + + @ExcelProperty(value = "销售单价(元)", index = 7) + @ColumnWidth(15) + private BigDecimal salesPrice; + + @ExcelProperty(value = "销售笔数", index = 8) + @ColumnWidth(12) + private Integer salesCount; + + @ExcelProperty(value = "退货数量", index = 9) + @ColumnWidth(15) + private Double returnQty; + + @ExcelProperty(value = "退货金额(元)", index = 10) + @ColumnWidth(15) + private BigDecimal returnAmount; + + @ExcelProperty(value = "实际销售数量", index = 11) + @ColumnWidth(15) + private Double actualSalesQty; + + @ExcelProperty(value = "实际销售金额(元)", index = 12) + @ColumnWidth(18) + private BigDecimal actualSalesAmount; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/SalesReportQueryDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/SalesReportQueryDTO.java new file mode 100644 index 000000000..af77b71fb --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/SalesReportQueryDTO.java @@ -0,0 +1,36 @@ +package com.bsd.pjcloud.supermarket.dto.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDate; + +/** + * 销售报表查询条件DTO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@Schema(description = "销售报表查询条件") +public class SalesReportQueryDTO { + + @Schema(description = "商品名称(模糊查询)") + private String productName; + + @Schema(description = "商品编码(模糊查询)") + private String productCode; + + @Schema(description = "分类ID") + private Long categoryId; + + @Schema(description = "开始日期") + private LocalDate startDate; + + @Schema(description = "结束日期") + private LocalDate endDate; + + @Schema(description = "排序方式:amount-按销售额降序,qty-按销售数量降序") + private String sortBy; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/StocktakeReportExcelDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/StocktakeReportExcelDTO.java new file mode 100644 index 000000000..f055f1e9d --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/StocktakeReportExcelDTO.java @@ -0,0 +1,72 @@ +package com.bsd.pjcloud.supermarket.dto.report; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 盘存报表Excel导出DTO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(25) +@ColumnWidth(15) +public class StocktakeReportExcelDTO { + + @ExcelProperty(value = "盘点单号", index = 0) + @ColumnWidth(20) + private String stocktakeCode; + + @ExcelProperty(value = "盘点日期", index = 1) + @ColumnWidth(15) + private LocalDate stocktakeDate; + + @ExcelProperty(value = "商品编码", index = 2) + @ColumnWidth(20) + private String productCode; + + @ExcelProperty(value = "商品名称", index = 3) + @ColumnWidth(30) + private String productName; + + @ExcelProperty(value = "分类", index = 4) + @ColumnWidth(15) + private String categoryName; + + @ExcelProperty(value = "规格", index = 5) + @ColumnWidth(20) + private String spec; + + @ExcelProperty(value = "单位", index = 6) + @ColumnWidth(10) + private String unit; + + @ExcelProperty(value = "账面数量", index = 7) + @ColumnWidth(15) + private Double bookQty; + + @ExcelProperty(value = "实盘数量", index = 8) + @ColumnWidth(15) + private Double physicalQty; + + @ExcelProperty(value = "差异数量", index = 9) + @ColumnWidth(15) + private Double diffQty; + + @ExcelProperty(value = "差异金额(元)", index = 10) + @ColumnWidth(15) + private BigDecimal diffAmount; + + @ExcelProperty(value = "备注", index = 11) + @ColumnWidth(30) + private String remark; +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/StocktakeReportQueryDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/StocktakeReportQueryDTO.java new file mode 100644 index 000000000..4b06d5f31 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/report/StocktakeReportQueryDTO.java @@ -0,0 +1,39 @@ +package com.bsd.pjcloud.supermarket.dto.report; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDate; + +/** + * 盘存报表查询条件DTO + * + * @author pjcloud + * @date 2025-10-16 + */ +@Data +@Schema(description = "盘存报表查询条件") +public class StocktakeReportQueryDTO { + + @Schema(description = "商品名称(模糊查询)") + private String productName; + + @Schema(description = "商品编码(模糊查询)") + private String productCode; + + @Schema(description = "分类ID") + private Long categoryId; + + @Schema(description = "盘点单号") + private String stocktakeCode; + + @Schema(description = "开始日期") + private LocalDate startDate; + + @Schema(description = "结束日期") + private LocalDate endDate; + + @Schema(description = "差异类型:surplus-盘盈,shortage-盘亏,all-全部") + private String diffType; +} + From 2418e1884fa0b8079c749e74eb34bf5ddd6c196e Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Fri, 17 Oct 2025 00:19:09 +0800 Subject: [PATCH 4/6] =?UTF-8?q?=E8=B6=85=E5=B8=82=E6=8A=A5=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../docs/README_新增报表功能.md | 235 +++++++ .../docs/超市报表功能接口文档.md | 517 ++++++++++++++ .../docs/超市报表统计逻辑详解.md | 635 ++++++++++++++++++ .../docs/进货单退货单明细功能说明.md | 253 +++++++ .../controller/ReportController.java | 326 +++++++++ .../mapper/InventoryReportMapper.java | 40 ++ .../mapper/ProfitReportMapper.java | 40 ++ .../supermarket/mapper/SalesReportMapper.java | 40 ++ .../mapper/StocktakeReportMapper.java | 40 ++ .../service/InventoryReportService.java | 43 ++ .../service/ProfitReportService.java | 43 ++ .../service/SalesReportService.java | 43 ++ .../service/StocktakeReportService.java | 43 ++ .../impl/InventoryReportServiceImpl.java | 51 ++ .../service/impl/ProfitReportServiceImpl.java | 51 ++ .../service/impl/SalesReportServiceImpl.java | 51 ++ .../impl/StocktakeReportServiceImpl.java | 51 ++ .../mapper/InventoryReportMapper.xml | 277 ++++++++ .../resources/mapper/ProfitReportMapper.xml | 225 +++++++ .../resources/mapper/SalesReportMapper.xml | 161 +++++ .../mapper/StocktakeReportMapper.xml | 111 +++ 21 files changed, 3276 insertions(+) create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/docs/README_新增报表功能.md create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/docs/超市报表功能接口文档.md create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/docs/超市报表统计逻辑详解.md create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/docs/进货单退货单明细功能说明.md create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/ReportController.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/InventoryReportMapper.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/ProfitReportMapper.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/SalesReportMapper.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/StocktakeReportMapper.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/InventoryReportService.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/ProfitReportService.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SalesReportService.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/StocktakeReportService.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/InventoryReportServiceImpl.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/ProfitReportServiceImpl.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SalesReportServiceImpl.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/StocktakeReportServiceImpl.java create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/InventoryReportMapper.xml create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/ProfitReportMapper.xml create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SalesReportMapper.xml create mode 100644 pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/StocktakeReportMapper.xml diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/docs/README_新增报表功能.md b/pjcloud-supermarket/pjcloud-supermarket-biz/docs/README_新增报表功能.md new file mode 100644 index 000000000..cb2ffb180 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/docs/README_新增报表功能.md @@ -0,0 +1,235 @@ +# 超市报表功能开发说明 + +## 📋 需求来源 +根据 `超市报表需求/报表需求.md` 的要求,新增统一的报表管理功能 + +## ✅ 完成情况 + +所有需求已100%完成,包括接口开发、文档编写和测试准备。 + +--- + +## 🎯 新增功能清单 + +### 1. 进货单明细(需求1)✅ +- **路径**:`/superMarketReport/purchaseDetail/*` +- **功能**:查询inventory_type=3的采购入库明细 +- **接口**:分页查询 + Excel导出 + +### 2. 退货单明细(需求1)✅ +- **路径**:`/superMarketReport/returnDetail/*` +- **功能**:查询inventory_type=6的零售退货明细 +- **接口**:分页查询 + Excel导出 + +### 3. 进销存报表(需求2)✅ +- **路径**:`/superMarketReport/inventory/*` +- **功能**:完整的进销存统计,包含26个业务字段 +- **接口**:分页查询 + 列表查询 + Excel导出 +- **字段**:参考进价、零售价、期初库存、期初金额、采购入库、采购退货、销售、销售退货、实际销售、销售成本、期末库存、销售毛利、首次进货日等 + +### 4. 毛利报表(需求3)✅ +- **路径**:`/superMarketReport/profit/*` +- **功能**:商品毛利和毛利率分析 +- **接口**:分页查询 + 列表查询 + Excel导出 +- **字段**:销售数量/金额、成本、毛利、毛利率、销售笔数等 + +### 5. 销售报表(需求4)✅ +- **路径**:`/superMarketReport/sales/*` +- **功能**:商品销售数据统计 +- **接口**:分页查询 + 列表查询 + Excel导出 +- **字段**:销售、退货、实际销售、销售单价、销售笔数等 + +### 6. 盘存报表(需求5)✅ +- **路径**:`/superMarketReport/stocktake/*` +- **功能**:盘点差异统计 +- **接口**:分页查询 + 列表查询 + Excel导出 +- **字段**:账面数量、实盘数量、差异数量/金额、盘点日期等 + +--- + +## 📁 新增文件列表 + +### Controller层 +- ✅ `ReportController.java` - 统一的报表管理控制器 + +### Service层 +- ✅ `InventoryReportService.java` + `InventoryReportServiceImpl.java` +- ✅ `ProfitReportService.java` + `ProfitReportServiceImpl.java` +- ✅ `SalesReportService.java` + `SalesReportServiceImpl.java` +- ✅ `StocktakeReportService.java` + `StocktakeReportServiceImpl.java` + +### Mapper层 +- ✅ `InventoryReportMapper.java` + `InventoryReportMapper.xml` +- ✅ `ProfitReportMapper.java` + `ProfitReportMapper.xml` +- ✅ `SalesReportMapper.java` + `SalesReportMapper.xml` +- ✅ `StocktakeReportMapper.java` + `StocktakeReportMapper.xml` + +### DTO层(API模块) +- ✅ `InventoryReportQueryDTO.java` + `InventoryReportExcelDTO.java` +- ✅ `ProfitReportQueryDTO.java` + `ProfitReportExcelDTO.java` +- ✅ `SalesReportQueryDTO.java` + `SalesReportExcelDTO.java` +- ✅ `StocktakeReportQueryDTO.java` + `StocktakeReportExcelDTO.java` + +### VO层(API模块) +- ✅ `InventoryReportVO.java` +- ✅ `ProfitReportVO.java` +- ✅ `SalesReportVO.java` +- ✅ `StocktakeReportVO.java` + +### 修改的文件 +- ✅ `InventoryDetailQueryDTO.java` - 新增inventoryType字段 +- ✅ `SmErpInventoryRecordsMapper.xml` - 新增inventory_type筛选条件 + +### 文档 +- ✅ `进货单退货单明细功能说明.md` +- ✅ `超市报表功能接口文档.md` +- ✅ `超市报表统计逻辑详解.md` +- ✅ `README_新增报表功能.md`(本文件) + +--- + +## 🔗 接口路由表 + +| 功能 | 分页查询 | 列表查询 | Excel导出 | +|------|---------|---------|----------| +| 进货单明细 | GET /superMarketReport/purchaseDetail/page | - | GET /superMarketReport/purchaseDetail/export | +| 退货单明细 | GET /superMarketReport/returnDetail/page | - | GET /superMarketReport/returnDetail/export | +| 进销存报表 | GET /superMarketReport/inventory/page | GET /superMarketReport/inventory/list | GET /superMarketReport/inventory/export | +| 毛利报表 | GET /superMarketReport/profit/page | GET /superMarketReport/profit/list | GET /superMarketReport/profit/export | +| 销售报表 | GET /superMarketReport/sales/page | GET /superMarketReport/sales/list | GET /superMarketReport/sales/export | +| 盘存报表 | GET /superMarketReport/stocktake/page | GET /superMarketReport/stocktake/list | GET /superMarketReport/stocktake/export | + +--- + +## 🔐 权限配置 + +### 需要配置的权限 +```sql +-- 查询权限 +INSERT INTO sys_menu (permission, name) VALUES ('supermarket_report_view', '超市报表查询'); + +-- 导出权限 +INSERT INTO sys_menu (permission, name) VALUES ('supermarket_report_export', '超市报表导出'); +``` + +### 权限说明 +- **supermarket_report_view**:查询所有报表(page和list接口) +- **supermarket_report_export**:导出所有报表(export接口) + +--- + +## 📊 Excel导出字段汇总 + +| 报表类型 | 列数 | 主要字段 | +|---------|------|---------| +| 进货单明细 | 11 | 单据编号、商品、数量、单价、金额、供应商等 | +| 退货单明细 | 11 | 同进货单明细 | +| 进销存报表 | 26 | 期初、采购、销售、退货、期末、成本、毛利等 | +| 毛利报表 | 13 | 销售、成本、毛利、毛利率等 | +| 销售报表 | 13 | 销售、退货、实际销售、单价、笔数等 | +| 盘存报表 | 12 | 盘点单号、账面、实盘、差异等 | + +--- + +## 🧪 测试建议 + +### 功能测试 +1. **正常场景**:输入合法参数,验证返回数据 +2. **边界测试**:空数据、单条数据、大量数据 +3. **异常测试**:非法参数、权限不足、网络超时 + +### 数据准确性测试 +```sql +-- 使用文档中的SQL验证语句 +-- 1. 验证库存平衡 +-- 2. 验证金额计算 +-- 3. 验证毛利率计算 +-- 4. 验证盘点差异 +``` + +### 性能测试 +- 查询10万条数据的响应时间 +- 导出5万条数据的耗时 +- 并发100用户查询的系统负载 + +--- + +## 🚀 部署说明 + +### 数据库更新 +1. 无需执行额外的DDL +2. 现有表结构已满足需求 +3. 建议添加推荐的索引(见文档) + +### 应用部署 +1. 编译打包:`mvn clean package` +2. 重启服务:`docker-compose restart pjcloud-supermarket-biz` +3. 验证接口:访问Swagger文档检查接口 + +### 权限配置 +1. 在系统管理中添加权限菜单 +2. 分配权限给相应角色 +3. 验证权限控制是否生效 + +--- + +## 📖 相关文档 + +1. **接口文档**:`超市报表功能接口文档.md` + - 包含所有接口的详细说明 + - 查询参数、返回字段、权限要求 + +2. **统计逻辑文档**:`超市报表统计逻辑详解.md` + - 详细的计算公式 + - 数据核对方法 + - 前端集成指南 + +3. **进货单退货单文档**:`进货单退货单明细功能说明.md` + - 进货单和退货单的专项文档 + - 包含SQL查询逻辑和使用示例 + +--- + +## ⚠️ 重要说明 + +### 不要修改的代码 +- ❌ `InventoryInOutReportController.java` - 已有的进销存报表(不同于新需求) +- ❌ `ProfitMarginReportController.java` - 已有的毛利报表(不同于新需求) +- ❌ `RealtimeSalesReportController.java` - 已有的即时销售报表 +- ❌ `InventoryStocktakeReportController.java` - 已有的盘存报表 + +### 新增的代码 +- ✅ `ReportController.java` - 新的统一报表管理 +- ✅ 所有新的Service、Mapper、DTO、VO文件 + +### 区别说明 +- **旧的报表Controller**:已有功能,保持不变 +- **新的ReportController**:根据新需求开发的功能,统一管理 + +--- + +## 📞 联系方式 + +如有问题请参考: +1. 接口文档和统计逻辑文档 +2. 后台日志输出 +3. 数据库查询验证 + +--- + +## ✨ 特色功能 + +1. **统一管理**:所有报表接口在ReportController中统一管理 +2. **完整字段**:进销存报表包含26个业务字段,满足所有分析需求 +3. **灵活查询**:支持多维度筛选和排序 +4. **Excel导出**:所有报表都支持一键导出Excel +5. **数据权限**:自动应用DataScope权限过滤 +6. **单位换算**:自动处理各种包装单位的换算 +7. **金额转换**:自动处理分和元的转换 +8. **详细文档**:提供完整的技术文档和使用指南 + +--- + +**开发完成时间**:2025-10-16 +**状态**:✅ 已完成,可以交付使用 + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/docs/超市报表功能接口文档.md b/pjcloud-supermarket/pjcloud-supermarket-biz/docs/超市报表功能接口文档.md new file mode 100644 index 000000000..7b5df05c5 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/docs/超市报表功能接口文档.md @@ -0,0 +1,517 @@ +# 超市报表功能接口文档 + +## 总览 + +本文档详细说明了新的超市报表功能的所有接口、统计逻辑和使用方法。所有接口都在 `ReportController` 中统一管理。 + +**Controller路径**:`/superMarketReport` +**权限要求**: +- 查询权限:`supermarket_report_view` +- 导出权限:`supermarket_report_export` + +--- + +## 一、进货单明细 + +### 功能说明 +查询和导出进货单明细数据(inventory_type=3,采购入库) + +### 接口列表 + +#### 1.1 分页查询进货单明细 +- **路径**:`GET /superMarketReport/purchaseDetail/page` +- **功能**:分页查询进货单明细 +- **查询参数**: + - `key`:关键字(单据编号/商品名称/供应商) + - `startDate`:开始日期 + - `endDate`:结束日期 + - `orderType`:订单类型 +- **返回字段**:单据编号、单据日期、商品名称、规格、单位、装数、数量、单价、总金额、供应商、备注 + +#### 1.2 导出进货单明细 +- **路径**:`GET /superMarketReport/purchaseDetail/export` +- **功能**:导出进货单明细为Excel文件 + +### 统计逻辑 +1. 筛选条件:`r.inventory_type = '3'` +2. 关联表: + - sm_erp_inventory_records(出入库主表) + - sm_erp_inventory_records_item(明细表) + - sm_erp_spu(商品表) + - sm_erp_unit(单位表) + - sm_erp_supplier(供应商表) +3. 支持模糊查询和日期区间查询 + +--- + +## 二、退货单明细 + +### 功能说明 +查询和导出退货单明细数据(inventory_type=6,零售退货) + +### 接口列表 + +#### 2.1 分页查询退货单明细 +- **路径**:`GET /superMarketReport/returnDetail/page` +- **功能**:分页查询退货单明细 + +#### 2.2 导出退货单明细 +- **路径**:`GET /superMarketReport/returnDetail/export` +- **功能**:导出退货单明细为Excel文件 + +### 统计逻辑 +与进货单明细类似,区别在于筛选条件:`r.inventory_type = '6'` + +--- + +## 三、进销存报表 + +### 功能说明 +按商品汇总统计进销存数据,包含完整的业务字段和财务指标 + +### 接口列表 + +#### 3.1 分页查询进销存报表 +- **路径**:`GET /superMarketReport/inventory/page` +- **功能**:分页查询进销存数据 + +#### 3.2 列表查询进销存报表 +- **路径**:`GET /superMarketReport/inventory/list` +- **功能**:不分页查询所有数据 + +#### 3.3 导出进销存报表 +- **路径**:`GET /superMarketReport/inventory/export` +- **功能**:导出进销存报表为Excel文件(26列) + +### 查询参数(InventoryReportQueryDTO) +| 参数名 | 类型 | 说明 | +|--------|------|------| +| productName | String | 商品名称(模糊查询) | +| productCode | String | 商品编码(模糊查询) | +| categoryId | Long | 分类ID | +| supplierId | Long | 供应商ID | +| startDate | LocalDate | 开始日期 | +| endDate | LocalDate | 结束日期 | +| onlyChanged | Boolean | 只显示有变动的商品 | + +### 返回字段(共26个) +1. 基础信息:商品编码、商品名称、分类、规格、单位、装数 +2. 价格信息:参考进价、零售价 +3. 期初信息:期初库存、期初金额 +4. 采购信息:采购入库数量/金额、采购退货数量/金额 +5. 销售信息:销售数量/金额、销售退货数量/金额、实际销售数量/金额 +6. 成本毛利:销售成本、销售毛利 +7. 期末信息:期末数量、期末库存金额、期末库存售额 +8. 其他:首次进货日 + +### 统计逻辑 + +#### 采购统计 +```sql +采购入库数量 = SUM(CASE WHEN inventory_type = '3' THEN quantity END) +采购入库金额 = SUM(CASE WHEN inventory_type = '3' THEN total_amount END) / 100.0 + +采购退货数量 = SUM(CASE WHEN inventory_type = '4' THEN quantity END) +采购退货金额 = SUM(CASE WHEN inventory_type = '4' THEN total_amount END) / 100.0 +``` + +#### 销售统计 +```sql +销售数量 = SUM(CASE WHEN inventory_type = '5' THEN quantity END) +销售金额 = SUM(CASE WHEN inventory_type = '5' THEN total_amount END) / 100.0 + +销售退货数量 = SUM(CASE WHEN inventory_type = '6' THEN quantity END) +销售退货金额 = SUM(CASE WHEN inventory_type = '6' THEN total_amount END) / 100.0 + +实际销售数量 = 销售数量 - 销售退货数量 +实际销售金额 = 销售金额 - 销售退货金额 +``` + +#### 成本和毛利 +``` +销售成本 = 实际销售数量 × 参考进价 +销售毛利 = 实际销售金额 - 销售成本 +``` + +#### 期初期末 +``` +期末数量 = 当前实时库存 +期末金额 = 期末数量 × 参考进价 +期末售额 = 期末数量 × 零售价 + +期初数量 = 期末数量 - 采购入库 + 采购退货 + 销售 - 销售退货 +期初金额 = 期初数量 × 参考进价 +``` + +#### 首次进货日 +```sql +首次进货日 = MIN(CASE WHEN inventory_type = '3' THEN inventory_date END) +``` + +### 数据核对 +```sql +-- 验证库存平衡 +期末库存 = 期初库存 + 采购入库 - 采购退货 - 销售 + 销售退货 + +-- 验证金额计算 +销售成本 = 实际销售数量 × 参考进价 +销售毛利 = 实际销售金额 - 销售成本 +``` + +--- + +## 四、毛利报表 + +### 功能说明 +按商品统计销售毛利和毛利率,用于分析商品盈利能力 + +### 接口列表 + +#### 4.1 分页查询毛利报表 +- **路径**:`GET /superMarketReport/profit/page` +- **功能**:分页查询毛利数据 + +#### 4.2 列表查询毛利报表 +- **路径**:`GET /superMarketReport/profit/list` +- **功能**:不分页查询所有数据 + +#### 4.3 导出毛利报表 +- **路径**:`GET /superMarketReport/profit/export` +- **功能**:导出毛利报表为Excel文件(13列) + +### 查询参数(ProfitReportQueryDTO) +| 参数名 | 类型 | 说明 | +|--------|------|------| +| productName | String | 商品名称 | +| productCode | String | 商品编码 | +| categoryId | Long | 分类ID | +| supplierId | Long | 供应商ID | +| startDate | LocalDate | 开始日期 | +| endDate | LocalDate | 结束日期 | +| sortBy | String | 排序方式:profit-按毛利降序,margin-按毛利率降序 | +| onlyWithSales | Boolean | 只显示有销售的商品 | + +### 返回字段 +- 商品编码、商品名称、分类、规格、单位 +- 销售数量、销售金额、销售单价 +- 成本单价、销售成本 +- 销售毛利、毛利率 +- 销售笔数 + +### 统计逻辑 + +```sql +销售数量 = 销售出库(5) - 销售退货(6) +销售金额 = 销售出库金额(5) - 销售退货金额(6) +销售单价 = 销售金额 / 销售数量 +成本单价 = 商品参考进价 +销售成本 = 销售数量 × 成本单价 +销售毛利 = 销售金额 - 销售成本 +毛利率 = (销售毛利 / 销售金额) × 100% +``` + +### 排序规则 +- `sortBy=profit`:按销售毛利降序 +- `sortBy=margin`:按毛利率降序 +- 默认:按销售金额降序 + +--- + +## 五、销售报表 + +### 功能说明 +按商品统计销售数据,包含销售、退货、实际销售等信息 + +### 接口列表 + +#### 5.1 分页查询销售报表 +- **路径**:`GET /superMarketReport/sales/page` +- **功能**:分页查询销售数据 + +#### 5.2 列表查询销售报表 +- **路径**:`GET /superMarketReport/sales/list` +- **功能**:不分页查询所有数据 + +#### 5.3 导出销售报表 +- **路径**:`GET /superMarketReport/sales/export` +- **功能**:导出销售报表为Excel文件(13列) + +### 查询参数(SalesReportQueryDTO) +| 参数名 | 类型 | 说明 | +|--------|------|------| +| productName | String | 商品名称 | +| productCode | String | 商品编码 | +| categoryId | Long | 分类ID | +| startDate | LocalDate | 开始日期 | +| endDate | LocalDate | 结束日期 | +| sortBy | String | 排序方式:amount-按销售额降序,qty-按销售数量降序 | + +### 返回字段 +- 商品编码、商品名称、分类、规格、单位 +- 销售数量、销售金额、销售单价、销售笔数 +- 退货数量、退货金额 +- 实际销售数量、实际销售金额 + +### 统计逻辑 + +```sql +销售数量 = SUM(CASE WHEN inventory_type = '5' THEN quantity END) +销售金额 = SUM(CASE WHEN inventory_type = '5' THEN total_amount END) / 100.0 + +退货数量 = SUM(CASE WHEN inventory_type = '6' THEN quantity END) +退货金额 = SUM(CASE WHEN inventory_type = '6' THEN total_amount END) / 100.0 + +实际销售数量 = 销售数量 - 退货数量 +实际销售金额 = 销售金额 - 退货金额 + +销售单价 = 销售金额 / 销售数量 +销售笔数 = COUNT(DISTINCT 单据ID) +``` + +### 排序规则 +- `sortBy=qty`:按销售数量降序 +- 默认:按销售金额降序 + +--- + +## 六、盘存报表 + +### 功能说明 +查询盘点单明细,统计账面数量、实盘数量和差异情况 + +### 接口列表 + +#### 6.1 分页查询盘存报表 +- **路径**:`GET /superMarketReport/stocktake/page` +- **功能**:分页查询盘点数据 + +#### 6.2 列表查询盘存报表 +- **路径**:`GET /superMarketReport/stocktake/list` +- **功能**:不分页查询所有数据 + +#### 6.3 导出盘存报表 +- **路径**:`GET /superMarketReport/stocktake/export` +- **功能**:导出盘存报表为Excel文件(12列) + +### 查询参数(StocktakeReportQueryDTO) +| 参数名 | 类型 | 说明 | +|--------|------|------| +| productName | String | 商品名称 | +| productCode | String | 商品编码 | +| categoryId | Long | 分类ID | +| stocktakeCode | String | 盘点单号 | +| startDate | LocalDate | 开始日期 | +| endDate | LocalDate | 结束日期 | +| diffType | String | surplus-盘盈,shortage-盘亏,all-全部 | + +### 返回字段 +- 盘点单号、盘点日期 +- 商品编码、商品名称、分类、规格、单位 +- 账面数量、实盘数量 +- 差异数量、差异金额 +- 备注 + +### 统计逻辑 + +```sql +差异数量 = 实盘数量 - 账面数量 +差异金额 = 差异数量 × 成本价 + +盘盈:差异数量 > 0 +盘亏:差异数量 < 0 +盘平:差异数量 = 0 +``` + +### 筛选规则 +- `diffType=surplus`:只显示盘盈商品 +- `diffType=shortage`:只显示盘亏商品 +- `diffType=all` 或不传:显示所有商品 + +--- + +## 前端集成示例 + +### 进货单明细 +```javascript +// 查询 +const data = await axios.get('/superMarketReport/purchaseDetail/page', { + params: { current: 1, size: 20, key: '关键字', startDate: '2025-01-01', endDate: '2025-01-31' } +}); + +// 导出 +window.location.href = '/superMarketReport/purchaseDetail/export?startDate=2025-01-01&endDate=2025-01-31'; +``` + +### 进销存报表 +```javascript +// 查询 +const data = await axios.get('/superMarketReport/inventory/page', { + params: { current: 1, size: 20, productName: '可乐', categoryId: 100 } +}); + +// 导出 +window.location.href = '/superMarketReport/inventory/export?categoryId=100'; +``` + +### 毛利报表 +```javascript +// 查询 +const data = await axios.get('/superMarketReport/profit/page', { + params: { current: 1, size: 20, sortBy: 'profit', onlyWithSales: true } +}); + +// 导出 +window.location.href = '/superMarketReport/profit/export?sortBy=profit'; +``` + +### 销售报表 +```javascript +// 查询 +const data = await axios.get('/superMarketReport/sales/page', { + params: { current: 1, size: 20, sortBy: 'amount' } +}); + +// 导出 +window.location.href = '/superMarketReport/sales/export?sortBy=amount'; +``` + +### 盘存报表 +```javascript +// 查询 +const data = await axios.get('/superMarketReport/stocktake/page', { + params: { current: 1, size: 20, diffType: 'shortage' } +}); + +// 导出 +window.location.href = '/superMarketReport/stocktake/export?diffType=all'; +``` + +--- + +## 数据库表说明 + +### 核心表结构 + +#### sm_erp_inventory_records(出入库记录主表) +- `id`:主键 +- `inventory_code`:单据编号 +- `inventory_date`:单据日期 +- `inventory_type`:单据类型('3'=采购入库,'4'=采购退货,'5'=零售出库,'6'=零售退货,'7'=库存盘点) +- `order_type`:订单类型('1'=入库,'2'=出库) +- `supplier_id`:供应商ID +- `total_amount`:总金额(分) +- `remark`:备注 + +#### sm_erp_inventory_records_item(出入库记录明细表) +- `id`:主键 +- `inventory_id`:出入库记录ID +- `erp_spu_id`:商品ID +- `unit_id`:单位ID +- `quantity`:数量 +- `unit_price`:单价(分) +- `total_amount`:总金额(分) + +#### sm_erp_spu(商品表) +- `id`:主键 +- `product_code`:商品编码 +- `product_name`:商品名称 +- `category_id`:分类ID + +#### sm_erp_unit(包装单位表) +- `id`:主键 +- `spu_id`:商品ID +- `spec`:规格 +- `unit`:单位 +- `package_num`:装数 +- `purchase_price`:参考进价(分) +- `retail_price`:零售价(分) +- `is_base`:是否基础单位 + +#### sm_erp_stock(库存表) +- `id`:主键 +- `erp_spu_id`:商品ID +- `current_stock`:当前库存 + +#### sm_erp_category(分类表) +- `id`:主键 +- `name`:分类名称 + +#### sm_erp_supplier(供应商表) +- `supplier_id`:供应商ID +- `supplier_name`:供应商名称 + +--- + +## 单位换算说明 + +所有数量统一转换为基础单位进行统计: + +```sql +CASE + WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num -- 非基础单位需要乘以装数 + ELSE item.quantity -- 基础单位直接使用 +END +``` + +**示例**: +- 基础单位:瓶,装数=1 +- 包装单位:箱,装数=24 +- 如果销售1箱,则换算为24瓶进行统计 + +--- + +## 金额单位说明 + +数据库存储金额为**分**,显示时自动转换为**元**: + +```sql +total_amount / 100.0 -- 分转元 +``` + +--- + +## 性能优化建议 + +### 索引建议 +```sql +-- 出入库记录表 +CREATE INDEX idx_inventory_type_date ON sm_erp_inventory_records(inventory_type, inventory_date, del_flag); + +-- 明细表 +CREATE INDEX idx_inventory_spu ON sm_erp_inventory_records_item(inventory_id, erp_spu_id); + +-- 商品表 +CREATE INDEX idx_spu_category ON sm_erp_spu(category_id, del_flag); +CREATE INDEX idx_spu_code ON sm_erp_spu(product_code); +CREATE INDEX idx_spu_name ON sm_erp_spu(product_name); + +-- 单位表 +CREATE INDEX idx_unit_spu_base ON sm_erp_unit(spu_id, is_base); +``` + +### 查询优化 +1. **必须指定时间范围**:避免全表扫描 +2. **使用分页查询**:大数据量时必须分页 +3. **善用onlyChanged**:只查询有变动的商品,提高性能 +4. **合理使用排序**:根据业务需求选择合适的排序字段 + +--- + +## 注意事项 + +1. **数据权限**:所有查询都会应用 DataScope 数据权限过滤 +2. **时间范围**:建议查询时指定时间范围,避免查询所有历史数据 +3. **金额精度**:所有金额计算保留2位小数 +4. **删除标记**:查询会自动过滤 `del_flag = '0'` 的已删除数据 +5. **单位换算**:系统自动处理单位换算,统一为基础单位统计 + +--- + +## 版本历史 + +| 版本 | 日期 | 说明 | +|------|------|------| +| 1.0 | 2025-10-16 | 初始版本,实现所有核心报表功能 | + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/docs/超市报表统计逻辑详解.md b/pjcloud-supermarket/pjcloud-supermarket-biz/docs/超市报表统计逻辑详解.md new file mode 100644 index 000000000..cb18fe8bf --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/docs/超市报表统计逻辑详解.md @@ -0,0 +1,635 @@ +# 超市报表统计逻辑详解 + +## 文档说明 + +本文档详细说明每个报表功能的统计逻辑、计算公式和业务流程,方便后续核对数据准确性和前端AI自动生成代码。 + +--- + +## 一、进货单明细统计逻辑 + +### 业务流程 +``` +1. 供应商送货 → 2. 验收入库 → 3. 生成进货单(inventory_type=3) → 4. 更新库存 +``` + +### 数据来源 +- **主表**:sm_erp_inventory_records +- **明细表**:sm_erp_inventory_records_item +- **筛选条件**:`inventory_type = '3'` AND `del_flag = '0'` + +### 关键字段说明 +| 字段 | 计算方式 | 说明 | +|------|---------|------| +| 单据编号 | r.inventory_code | 系统自动生成 | +| 单据日期 | r.inventory_date | 入库日期 | +| 商品名称 | spu.product_name | 来自商品表 | +| 数量 | item.quantity | 明细表记录 | +| 单价 | item.unit_price / 100.0 | 分转元 | +| 总金额 | item.total_amount / 100.0 | 分转元 | +| 供应商 | supplier.supplier_name | 来自供应商表 | + +### 数据核对方法 +```sql +-- 1. 核对单据总金额 +SELECT inventory_code, + SUM(total_amount) / 100.0 AS item_sum, + total_amount / 100.0 AS record_total +FROM sm_erp_inventory_records_item i + JOIN sm_erp_inventory_records r ON r.id = i.inventory_id +WHERE r.inventory_type = '3' +GROUP BY inventory_code, r.total_amount; + +-- 2. 核对供应商关联 +SELECT COUNT(*) FROM sm_erp_inventory_records r +WHERE r.inventory_type = '3' AND r.supplier_id IS NULL; +``` + +--- + +## 二、退货单明细统计逻辑 + +### 业务流程 +``` +1. 客户退货 → 2. 检查商品 → 3. 生成退货单(inventory_type=6) → 4. 更新库存 +``` + +### 数据来源 +- **筛选条件**:`inventory_type = '6'` +- 其他逻辑同进货单明细 + +--- + +## 三、进销存报表统计逻辑 + +### 业务流程 +``` +期初库存 → 采购入库(+) → 采购退货(-) → 销售出库(-) → 销售退货(+) → 期末库存 +``` + +### 核心公式 + +#### 1. 期初库存计算 +``` +期初数量 = 期末数量 - 本期采购入库 + 本期采购退货 + 本期销售 - 本期销售退货 +期初金额 = 期初数量 × 参考进价 +``` + +**说明**:通过当前库存反推期初库存,确保数据一致性 + +#### 2. 采购统计 +```sql +-- 采购入库 (inventory_type = '3') +采购入库数量 = SUM(CASE WHEN r.inventory_type = '3' THEN + CASE WHEN 非基础单位 THEN quantity * package_num + ELSE quantity END + END) +采购入库金额 = SUM(CASE WHEN r.inventory_type = '3' THEN total_amount END) / 100.0 + +-- 采购退货 (inventory_type = '4') +采购退货数量 = SUM(CASE WHEN r.inventory_type = '4' THEN ... END) +采购退货金额 = SUM(CASE WHEN r.inventory_type = '4' THEN total_amount END) / 100.0 +``` + +#### 3. 销售统计 +```sql +-- 销售出库 (inventory_type = '5') +销售数量 = SUM(CASE WHEN r.inventory_type = '5' THEN ... END) +销售金额 = SUM(CASE WHEN r.inventory_type = '5' THEN total_amount END) / 100.0 + +-- 销售退货 (inventory_type = '6') +销售退货数量 = SUM(CASE WHEN r.inventory_type = '6' THEN ... END) +销售退货金额 = SUM(CASE WHEN r.inventory_type = '6' THEN total_amount END) / 100.0 + +-- 实际销售 +实际销售数量 = 销售数量 - 销售退货数量 +实际销售金额 = 销售金额 - 销售退货金额 +``` + +#### 4. 成本和毛利 +``` +销售成本 = 实际销售数量 × 参考进价 +销售毛利 = 实际销售金额 - 销售成本 +``` + +#### 5. 期末库存 +``` +期末数量 = stock.current_stock(当前实时库存) +期末库存金额 = 期末数量 × 参考进价 +期末库存售额 = 期末数量 × 零售价 +``` + +#### 6. 首次进货日 +```sql +首次进货日 = MIN(CASE WHEN r.inventory_type = '3' THEN r.inventory_date END) +``` + +### 数据核对方法 + +#### 库存平衡验证 +```sql +-- 验证:期末库存 = 期初库存 + 采购入库 - 采购退货 - 销售 + 销售退货 +SELECT + product_name, + opening_qty, + purchase_in_qty, + purchase_return_qty, + sales_qty, + sales_return_qty, + closing_qty, + (opening_qty + purchase_in_qty - purchase_return_qty - sales_qty + sales_return_qty) AS calculated_closing, + ABS(closing_qty - calculated_closing) AS diff +FROM inventory_report +WHERE ABS(closing_qty - calculated_closing) > 0.01; +``` + +#### 金额计算验证 +```sql +-- 验证销售成本 +SELECT + product_name, + actual_sales_qty, + purchase_price, + sales_cost, + (actual_sales_qty * purchase_price) AS calculated_cost, + ABS(sales_cost - calculated_cost) AS diff +FROM inventory_report +WHERE ABS(sales_cost - calculated_cost) > 0.01; + +-- 验证销售毛利 +SELECT + product_name, + actual_sales_amount, + sales_cost, + gross_profit, + (actual_sales_amount - sales_cost) AS calculated_profit, + ABS(gross_profit - calculated_profit) AS diff +FROM inventory_report +WHERE ABS(gross_profit - calculated_profit) > 0.01; +``` + +--- + +## 四、毛利报表统计逻辑 + +### 业务目的 +分析商品盈利能力,优化商品结构和定价策略 + +### 核心公式 + +#### 1. 销售数据统计 +```sql +销售数量 = 销售出库(5) - 销售退货(6) +销售金额 = 销售出库金额(5) - 销售退货金额(6) +``` + +#### 2. 单价计算 +``` +销售单价 = 销售金额 / 销售数量 +成本单价 = 商品参考进价(base.purchase_price / 100.0) +``` + +#### 3. 成本和毛利 +``` +销售成本 = 销售数量 × 成本单价 +销售毛利 = 销售金额 - 销售成本 +毛利率 = (销售毛利 / 销售金额) × 100% +``` + +#### 4. 销售笔数 +```sql +销售笔数 = COUNT(DISTINCT CASE WHEN r.inventory_type IN ('5', '6') THEN r.id END) +``` + +### 盈利分析 + +| 毛利率范围 | 评价 | 建议 | +|-----------|------|------| +| > 30% | 高毛利 | 保持或增加采购量 | +| 20% - 30% | 正常 | 维持现状 | +| 10% - 20% | 偏低 | 考虑调价或优化采购 | +| < 10% | 低毛利 | 需要重点关注 | +| < 0% | 亏损 | 必须调整策略 | + +### 数据核对方法 +```sql +-- 验证毛利率计算 +SELECT + product_name, + sales_amount, + sales_cost, + gross_profit, + profit_margin, + CASE WHEN sales_amount > 0 + THEN (gross_profit / sales_amount * 100) + ELSE 0 END AS calculated_margin, + ABS(profit_margin - calculated_margin) AS diff +FROM profit_report +WHERE sales_amount > 0 + AND ABS(profit_margin - calculated_margin) > 0.01; +``` + +--- + +## 五、销售报表统计逻辑 + +### 业务流程 +``` +客户下单 → 商品出库 → 生成销售单(5) → 客户退货(6) → 计算实际销售 +``` + +### 核心公式 + +#### 1. 销售数据 +```sql +销售数量 = SUM(CASE WHEN inventory_type = '5' THEN quantity END) +销售金额 = SUM(CASE WHEN inventory_type = '5' THEN total_amount END) / 100.0 +销售笔数 = COUNT(DISTINCT CASE WHEN inventory_type IN ('5', '6') THEN record_id END) +``` + +#### 2. 退货数据 +```sql +退货数量 = SUM(CASE WHEN inventory_type = '6' THEN quantity END) +退货金额 = SUM(CASE WHEN inventory_type = '6' THEN total_amount END) / 100.0 +``` + +#### 3. 实际销售 +``` +实际销售数量 = 销售数量 - 退货数量 +实际销售金额 = 销售金额 - 退货金额 +``` + +#### 4. 销售单价 +``` +销售单价 = 销售金额 / 销售数量 +``` + +### 销售分析指标 + +| 指标 | 计算方式 | 业务意义 | +|------|---------|---------| +| 销售额占比 | 商品销售额 / 总销售额 | 该商品对总销售的贡献 | +| 退货率 | 退货数量 / 销售数量 × 100% | 商品质量和客户满意度 | +| 平均单价 | 销售金额 / 销售数量 | 商品定价水平 | + +### 数据核对方法 +```sql +-- 验证实际销售计算 +SELECT + product_name, + sales_qty, + return_qty, + actual_sales_qty, + (sales_qty - return_qty) AS calculated_actual, + ABS(actual_sales_qty - calculated_actual) AS diff +FROM sales_report +WHERE ABS(actual_sales_qty - calculated_actual) > 0.01; + +-- 验证销售单价 +SELECT + product_name, + sales_amount, + sales_qty, + sales_price, + CASE WHEN sales_qty > 0 THEN sales_amount / sales_qty ELSE 0 END AS calculated_price, + ABS(sales_price - calculated_price) AS diff +FROM sales_report +WHERE sales_qty > 0 + AND ABS(sales_price - calculated_price) > 0.01; +``` + +--- + +## 六、盘存报表统计逻辑 + +### 业务流程 +``` +1. 制定盘点计划 → 2. 冻结库存 → 3. 实地盘点 → 4. 录入实盘数据 → 5. 生成盘点单(7) → 6. 分析差异 → 7. 调整库存 +``` + +### 核心公式 + +#### 1. 差异计算 +``` +差异数量 = 实盘数量 - 账面数量 +差异金额 = 差异数量 × 成本价 + +盘盈:差异数量 > 0(实际库存多于账面) +盘亏:差异数量 < 0(实际库存少于账面) +盘平:差异数量 = 0(实际与账面一致) +``` + +#### 2. 盘点准确率 +``` +盘点准确率 = (盘平商品数 / 盘点总商品数) × 100% +差异率 = |差异数量| / 账面数量 × 100% +``` + +### 差异原因分析 + +#### 盘盈常见原因 +1. 入库未及时录入系统 +2. 退货未登记 +3. 单位换算错误(录入错误) +4. 系统出库扣减错误 + +#### 盘亏常见原因 +1. 出库未及时录入系统 +2. 商品损耗未登记 +3. 被盗或丢失 +4. 赠品未扣减 +5. 称重误差 + +### 数据核对方法 +```sql +-- 验证差异数量计算 +SELECT + stocktake_code, + product_name, + book_qty, + physical_qty, + diff_qty, + (physical_qty - book_qty) AS calculated_diff, + ABS(diff_qty - calculated_diff) AS error +FROM stocktake_report +WHERE ABS(diff_qty - calculated_diff) > 0.01; + +-- 验证差异金额计算 +SELECT + product_name, + diff_qty, + cost_price, + diff_amount, + (diff_qty * cost_price) AS calculated_amount, + ABS(diff_amount - calculated_amount) AS error +FROM stocktake_report +WHERE ABS(diff_amount - calculated_amount) > 0.01; + +-- 检查重大差异 +SELECT + product_name, + book_qty, + physical_qty, + diff_qty, + ABS(diff_qty) / NULLIF(book_qty, 0) * 100 AS diff_rate +FROM stocktake_report +WHERE book_qty > 0 + AND ABS(diff_qty) / book_qty > 0.05 -- 差异率超过5% +ORDER BY diff_rate DESC; +``` + +--- + +## 七、单位换算统计逻辑 + +### 换算原理 +超市商品有多种包装单位(瓶、箱、包等),统计时需统一转换为基础单位 + +### 换算公式 +```sql +换算后数量 = CASE + WHEN 使用单位 != 基础单位 AND package_num > 0 + THEN item.quantity * u.package_num -- 乘以装数 + ELSE item.quantity -- 基础单位直接使用 +END +``` + +### 示例说明 + +#### 示例1:可乐销售 +- **基础单位**:瓶(is_base=TRUE,package_num=1) +- **包装单位**:箱(is_base=FALSE,package_num=24) + +**销售记录**: +- 记录1:销售2箱 → 换算为 2 × 24 = 48瓶 +- 记录2:销售10瓶 → 换算为 10瓶 + +**统计结果**:总销售数量 = 48 + 10 = 58瓶 + +#### 示例2:猪肉销售 +- **基础单位**:斤(is_base=TRUE,package_num=1) +- **其他单位**:公斤(is_base=FALSE,package_num=2) + +**销售记录**: +- 记录1:销售5公斤 → 换算为 5 × 2 = 10斤 +- 记录2:销售3斤 → 换算为 3斤 + +**统计结果**:总销售数量 = 10 + 3 = 13斤 + +### 关键SQL片段 +```sql +CASE + WHEN u.id IS NOT NULL -- 有单位信息 + AND base.id IS NOT NULL -- 有基础单位信息 + AND u.id != base.id -- 不是基础单位 + AND u.package_num IS NOT NULL + AND u.package_num > 0 -- 装数有效 + THEN item.quantity * u.package_num -- 换算 + ELSE item.quantity -- 不换算 +END +``` + +--- + +## 八、金额单位转换 + +### 存储规则 +数据库统一使用**分**作为金额存储单位,避免浮点数精度问题 + +### 转换规则 +```sql +-- 查询时:分转元 +SELECT total_amount / 100.0 AS amount_yuan FROM ... + +-- 插入时:元转分 +INSERT INTO ... (total_amount) VALUES (amount_yuan * 100) +``` + +### 示例 +- 存储:10050(分) +- 显示:100.50(元) +- Excel导出:100.50(元) + +--- + +## 九、数据权限过滤 + +### DataScope说明 +所有查询都会自动应用数据权限过滤: + +```java +inventoryReportMapper.selectInventoryReportPage(page, queryDTO, DataScope.of()); +``` + +### 权限SQL示例 +```sql +-- DataScope会自动添加以下条件 +WHERE ... + AND ( + dept_id IN (用户可见部门列表) + OR create_by = '当前用户' + ) +``` + +### 权限级别 +1. **全部数据**:super_admin角色 +2. **本部门及下级**:dept_admin角色 +3. **本部门**:dept_user角色 +4. **仅本人**:user角色 + +--- + +## 十、统计性能优化 + +### SQL优化要点 + +#### 1. 索引使用 +```sql +-- 确保以下索引存在 +CREATE INDEX idx_inventory_type_date ON sm_erp_inventory_records(inventory_type, inventory_date, del_flag); +CREATE INDEX idx_inventory_spu ON sm_erp_inventory_records_item(inventory_id, erp_spu_id); +CREATE INDEX idx_spu_category ON sm_erp_spu(category_id, del_flag); +CREATE INDEX idx_unit_spu_base ON sm_erp_unit(spu_id, is_base); +``` + +#### 2. 查询优化 +- **必须指定时间范围**:避免扫描所有历史数据 +- **使用分页查询**:大数据量必须分页 +- **避免SELECT \***:只查询需要的字段 +- **合理使用GROUP BY**:按商品分组统计 + +#### 3. 批量处理 +```java +// 不推荐:循环查询 +for (Long productId : productIds) { + ProductVO product = selectById(productId); // N+1查询问题 +} + +// 推荐:批量查询 +List products = selectByIds(productIds); // 一次查询 +``` + +### 执行计划查看 +```sql +EXPLAIN SELECT ... FROM inventory_report WHERE ...; +``` + +查看是否使用了索引: +- `type = ref` 或 `range`:使用索引 ✅ +- `type = ALL`:全表扫描 ❌ + +--- + +## 十一、前端使用指南 + +### 接口调用规范 + +#### 1. 分页查询 +```javascript +const pageQuery = { + current: 1, // 当前页码 + size: 20, // 每页数量 + ...otherParams // 其他查询条件 +}; + +const response = await axios.get('/report/inventory/page', { params: pageQuery }); +``` + +#### 2. 列表查询 +```javascript +// 用于小数据量或前端自行分页 +const response = await axios.get('/report/inventory/list', { + params: { categoryId: 100, startDate: '2025-01-01' } +}); +``` + +#### 3. Excel导出 +```javascript +// 方式1:直接下载 +window.location.href = `/report/inventory/export?categoryId=100&startDate=2025-01-01`; + +// 方式2:通过a标签 +const link = document.createElement('a'); +link.href = `/report/inventory/export?${new URLSearchParams(params)}`; +link.click(); +``` + +### 前端数据处理 + +#### 格式化金额 +```javascript +// 金额保留2位小数 +const formatAmount = (amount) => { + return amount ? amount.toFixed(2) : '0.00'; +}; +``` + +#### 格式化百分比 +```javascript +// 毛利率显示 +const formatPercent = (rate) => { + return rate ? rate.toFixed(2) + '%' : '0.00%'; +}; +``` + +#### 状态标识 +```javascript +// 根据毛利率显示颜色 +const getProfitColor = (margin) => { + if (margin >= 30) return 'green'; // 高毛利 + if (margin >= 20) return 'blue'; // 正常 + if (margin >= 10) return 'orange'; // 偏低 + if (margin > 0) return 'yellow'; // 低毛利 + return 'red'; // 亏损 +}; +``` + +--- + +## 十二、常见问题FAQ + +### Q1:为什么期初库存会出现负数? +**A**:可能原因: +1. 统计期之前有未记录的出库 +2. 历史数据不完整 +3. 期末库存小于本期入库减出库 + +**解决方法**: +- 扩大统计时间范围 +- 检查并补充历史数据 +- 核对库存表current_stock是否准确 + +### Q2:毛利率为什么显示0%? +**A**:可能原因: +1. 商品没有设置参考进价 +2. 销售金额为0 +3. 销售成本等于销售金额 + +**解决方法**:检查商品基础数据的purchase_price字段 + +### Q3:导出Excel为空? +**A**:可能原因: +1. 查询条件过滤了所有数据 +2. 时间范围内没有数据 +3. 权限限制看不到数据 + +**解决方法**: +- 放宽查询条件 +- 检查数据权限 +- 查看后台日志 + +### Q4:查询很慢怎么办? +**A**:优化方法: +1. 必须指定时间范围 +2. 使用分页查询 +3. 检查是否有索引 +4. 避免查询全部历史数据 + +--- + +## 版本历史 + +| 版本 | 日期 | 说明 | 作者 | +|------|------|------|------| +| 1.0 | 2025-10-16 | 初始版本,实现所有核心报表功能 | pjcloud | + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/docs/进货单退货单明细功能说明.md b/pjcloud-supermarket/pjcloud-supermarket-biz/docs/进货单退货单明细功能说明.md new file mode 100644 index 000000000..6acb0aafa --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/docs/进货单退货单明细功能说明.md @@ -0,0 +1,253 @@ +# 进货单和退货单明细功能说明 + +## 功能概述 + +本功能提供进货单明细和退货单明细的查询和导出功能,支持按单据编号、商品名称、供应商、日期区间等条件进行查询。 + +## 业务说明 + +### 单据类型 + +根据 `InventoryTypeEnum` 枚举定义: +- **进货单(inventory_type = 3)**:CKRK(采购入库) +- **退货单(inventory_type = 6)**:LSTH(零售退货) + +### 订单类型 + +根据 `OrderTypeEnum` 枚举定义: +- **入库(order_type = 1)**:RK +- **出库(order_type = 2)**:CK +- **无盈亏(order_type = 0)**:No + +## 功能实现 + +### 1. 进货单明细查询 + +#### 接口信息 +- **路径**:`GET /superMarketReport/purchaseDetail/page` +- **权限**:`supermarket_report_view` +- **功能**:分页查询进货单明细 + +#### 查询参数(InventoryDetailQueryDTO) +| 参数名 | 类型 | 必填 | 说明 | +|--------|------|------|------| +| key | String | 否 | 关键字(单据编号/商品名称/供应商) | +| startDate | LocalDate | 否 | 开始日期 | +| endDate | LocalDate | 否 | 结束日期 | +| orderType | Integer | 否 | 订单类型:1=入库,2=出库 | +| inventoryType | String | 自动设置 | 固定为"3"(进货单) | + +#### 返回字段(InventoryDetailExcelDTO) +| 字段名 | 类型 | 说明 | +|--------|------|------| +| inventoryCode | String | 单据编号 | +| inventoryDate | LocalDate | 单据日期 | +| productName | String | 商品名称 | +| spec | String | 规格 | +| unit | String | 单位 | +| packageNum | Integer | 装数 | +| quantity | Double | 数量 | +| unitPrice | BigDecimal | 单价(元) | +| totalAmount | BigDecimal | 总金额(元) | +| supplierName | String | 供应商 | +| remark | String | 备注 | + +#### 统计逻辑 +1. 从 `sm_erp_inventory_records` 主表关联 `sm_erp_inventory_records_item` 明细表 +2. 通过 `inventory_type = 3` 筛选进货单 +3. 支持通过单据编号、商品名称、供应商进行模糊查询 +4. 支持日期区间查询 +5. 关联商品表(sm_erp_spu)、单位表(sm_erp_unit)、供应商表(sm_erp_supplier)获取完整信息 + +### 2. 退货单明细查询 + +#### 接口信息 +- **路径**:`GET /superMarketReport/returnDetail/page` +- **权限**:`supermarket_report_view` +- **功能**:分页查询退货单明细 + +#### 查询参数 +与进货单明细相同,但 `inventoryType` 自动设置为"6"(退货单) + +#### 返回字段 +与进货单明细相同 + +#### 统计逻辑 +与进货单明细类似,区别在于通过 `inventory_type = 6` 筛选零售退货单 + +### 3. Excel导出功能 + +#### 进货单明细导出 +- **路径**:`GET /superMarketReport/purchaseDetail/export` +- **权限**:`supermarket_report_export` +- **功能**:导出进货单明细为Excel文件 + +#### 退货单明细导出 +- **路径**:`GET /superMarketReport/returnDetail/export` +- **权限**:`supermarket_report_export` +- **功能**:导出退货单明细为Excel文件 + +#### 导出字段顺序 +1. 单据编号(20字符宽) +2. 单据日期(15字符宽) +3. 商品名称(30字符宽) +4. 规格(20字符宽) +5. 单位(10字符宽) +6. 装数(10字符宽) +7. 数量(15字符宽) +8. 单价(15字符宽) +9. 总金额(15字符宽) +10. 供应商(25字符宽) +11. 备注(30字符宽) + +## 数据库设计 + +### 涉及表结构 + +#### sm_erp_inventory_records(出入库记录主表) +| 字段 | 类型 | 说明 | +|------|------|------| +| id | Long | 主键 | +| inventory_code | String | 单据编号 | +| inventory_date | LocalDate | 单据日期 | +| inventory_type | String | 单据类型 | +| order_type | String | 订单类型 | +| supplier_id | Long | 供应商ID | +| total_amount | Integer | 总金额(分) | +| remark | String | 备注 | + +#### sm_erp_inventory_records_item(出入库记录明细表) +| 字段 | 类型 | 说明 | +|------|------|------| +| id | Long | 主键 | +| inventory_id | Long | 出入库记录ID | +| erp_spu_id | Long | 商品ID | +| unit_id | Long | 单位ID | +| quantity | Double | 数量 | +| unit_price | Integer | 单价(分) | +| total_amount | Integer | 总金额(分) | +| order_type | String | 订单类型 | + +### SQL查询逻辑(Mapper XML) + +```xml + +``` + +## 使用流程 + +### 查询流程 +1. 前端发起分页查询请求 +2. Controller接收请求,设置固定的inventory_type(3或6) +3. Service调用Mapper执行SQL查询 +4. 关联查询商品、单位、供应商等信息 +5. 返回分页结果给前端 + +### 导出流程 +1. 前端发起导出请求 +2. Controller接收请求,设置固定的inventory_type +3. Service调用Mapper查询所有符合条件的数据(不分页) +4. 将VO对象转换为Excel DTO +5. 使用 `@ResponseExcel` 注解自动生成Excel文件并下载 + +## 核对与调试 + +### 数据准确性核对 + +1. **单据数量核对**:查询结果数量应与数据库实际记录数一致 + ```sql + SELECT COUNT(*) FROM sm_erp_inventory_records + WHERE inventory_type = '3' AND del_flag = '0'; + ``` + +2. **金额核对**:单据总金额 = 各明细项的总金额之和 + ```sql + SELECT inventory_code, + SUM(total_amount) as calc_total, + (SELECT total_amount FROM sm_erp_inventory_records WHERE id = i.inventory_id) as record_total + FROM sm_erp_inventory_records_item i + GROUP BY inventory_id; + ``` + +3. **供应商关联核对**:确保供应商信息正确关联 + +### 常见问题排查 + +1. **查询无数据**:检查inventory_type是否正确设置为3或6 +2. **金额显示错误**:数据库存储为分,显示需除以100 +3. **日期筛选不生效**:检查日期格式和SQL条件 + +## 前端集成建议 + +### 查询接口调用示例 +```javascript +// 进货单明细查询 +const queryPurchaseDetail = async (params) => { + const response = await axios.get('/superMarketReport/purchaseDetail/page', { + params: { + current: 1, + size: 20, + key: '搜索关键字', + startDate: '2025-01-01', + endDate: '2025-01-31' + } + }); + return response.data; +}; + +// 退货单明细查询 +const queryReturnDetail = async (params) => { + const response = await axios.get('/superMarketReport/returnDetail/page', { + params: { + current: 1, + size: 20, + key: '搜索关键字', + startDate: '2025-01-01', + endDate: '2025-01-31' + } + }); + return response.data; +}; +``` + +### 导出接口调用示例 +```javascript +// 进货单明细导出 +const exportPurchaseDetail = (params) => { + window.location.href = `/superMarketReport/purchaseDetail/export?${new URLSearchParams(params)}`; +}; + +// 退货单明细导出 +const exportReturnDetail = (params) => { + window.location.href = `/superMarketReport/returnDetail/export?${new URLSearchParams(params)}`; +}; +``` + +## 性能优化建议 + +1. **索引优化**:在 `inventory_type`、`inventory_date`、`del_flag` 字段上建立索引 +2. **分页查询**:避免一次性查询大量数据 +3. **缓存策略**:可对供应商、单位等基础数据进行缓存 +4. **异步导出**:对于大数据量导出,建议采用异步任务方式 + +## 版本历史 + +| 版本 | 日期 | 说明 | +|------|------|------| +| 1.0 | 2025-10-16 | 初始版本,实现基础查询和导出功能 | + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/ReportController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/ReportController.java new file mode 100644 index 000000000..5ec25a3ee --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/ReportController.java @@ -0,0 +1,326 @@ +package com.bsd.pjcloud.supermarket.controller; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.common.core.util.R; +import com.bsd.pjcloud.common.excel.annotation.ResponseExcel; +import com.bsd.pjcloud.common.log.annotation.SysLog; +import com.bsd.pjcloud.supermarket.dto.inventoryrecords.InventoryDetailExcelDTO; +import com.bsd.pjcloud.supermarket.dto.inventoryrecords.InventoryDetailQueryDTO; +import com.bsd.pjcloud.supermarket.dto.report.*; +import com.bsd.pjcloud.supermarket.service.InventoryReportService; +import com.bsd.pjcloud.supermarket.service.SmErpInventoryRecordsService; +import com.bsd.pjcloud.supermarket.vo.report.InventoryReportVO; +import com.bsd.pjcloud.supermarket.vo.report.ProfitReportVO; +import com.bsd.pjcloud.supermarket.vo.report.SalesReportVO; +import com.bsd.pjcloud.supermarket.vo.report.StocktakeReportVO; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.http.HttpHeaders; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +/** + * 超市报表统一管理控制器 + * 包含进货单明细、退货单明细、进销存报表、毛利报表、销售报表、盘存报表等功能 + * + * @author pjcloud + * @date 2025-10-16 + */ +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/superMarketReport") +@Tag(description = "report", name = "超市报表管理") +@SecurityRequirement(name = HttpHeaders.AUTHORIZATION) +public class ReportController { + + private final SmErpInventoryRecordsService smErpInventoryRecordsService; + private final InventoryReportService inventoryReportService; + private final com.bsd.pjcloud.supermarket.service.ProfitReportService profitReportService; + private final com.bsd.pjcloud.supermarket.service.SalesReportService salesReportService; + private final com.bsd.pjcloud.supermarket.service.StocktakeReportService stocktakeReportService; + + // ==================== 进货单明细 ==================== + + /** + * 分页查询进货单明细 + * @param page 分页对象 + * @param inventoryDetailQueryDTO 查询条件 + * @return 分页结果 + */ + @Operation(summary = "分页获取进货单明细", description = "进货单明细(inventory_type=3),支持日期区间查询") + @GetMapping("/purchaseDetail/page") + @PreAuthorize("@pms.hasPermission('supermarket_report_view')") + public R getPurchaseDetailPage(@ParameterObject Page page, @ParameterObject InventoryDetailQueryDTO inventoryDetailQueryDTO) { + // 固定查询进货单类型:inventory_type=3(采购入库) + inventoryDetailQueryDTO.setInventoryType("3"); + return R.ok(smErpInventoryRecordsService.getInventoryDetailPage(page, inventoryDetailQueryDTO)); + } + + /** + * 导出进货单明细 + * @param inventoryDetailQueryDTO 查询条件 + * @return excel 文件流 + */ + @ResponseExcel + @GetMapping("/purchaseDetail/export") + @Operation(summary = "导出进货单明细", description = "导出进货单明细数据为Excel文件") + @SysLog("导出进货单明细") + @PreAuthorize("@pms.hasPermission('supermarket_report_export')") + public List exportPurchaseDetail(@ParameterObject InventoryDetailQueryDTO inventoryDetailQueryDTO) { + // 固定查询进货单类型:inventory_type=3(采购入库) + inventoryDetailQueryDTO.setInventoryType("3"); + return smErpInventoryRecordsService.exportInventoryDetail(inventoryDetailQueryDTO); + } + + // ==================== 退货单明细 ==================== + + /** + * 分页查询退货单明细 + * @param page 分页对象 + * @param inventoryDetailQueryDTO 查询条件 + * @return 分页结果 + */ + @Operation(summary = "分页获取退货单明细", description = "退货单明细(inventory_type=6),支持日期区间查询") + @GetMapping("/returnDetail/page") + @PreAuthorize("@pms.hasPermission('supermarket_report_view')") + public R getReturnDetailPage(@ParameterObject Page page, @ParameterObject InventoryDetailQueryDTO inventoryDetailQueryDTO) { + // 固定查询退货单类型:inventory_type=6(零售退货) + inventoryDetailQueryDTO.setInventoryType("6"); + return R.ok(smErpInventoryRecordsService.getInventoryDetailPage(page, inventoryDetailQueryDTO)); + } + + /** + * 导出退货单明细 + * @param inventoryDetailQueryDTO 查询条件 + * @return excel 文件流 + */ + @ResponseExcel + @GetMapping("/returnDetail/export") + @Operation(summary = "导出退货单明细", description = "导出退货单明细数据为Excel文件") + @SysLog("导出退货单明细") + @PreAuthorize("@pms.hasPermission('supermarket_report_export')") + public List exportReturnDetail(@ParameterObject InventoryDetailQueryDTO inventoryDetailQueryDTO) { + // 固定查询退货单类型:inventory_type=6(零售退货) + inventoryDetailQueryDTO.setInventoryType("6"); + return smErpInventoryRecordsService.exportInventoryDetail(inventoryDetailQueryDTO); + } + + // ==================== 进销存报表 ==================== + + /** + * 分页查询进销存报表 + * @param page 分页对象 + * @param queryDTO 查询条件 + * @return 分页结果 + */ + @Operation(summary = "分页查询进销存报表", description = "包含所有扩展字段:参考进价、零售价、期初库存、销售成本、毛利等") + @GetMapping("/inventory/page") + @PreAuthorize("@pms.hasPermission('supermarket_report_view')") + public R> getInventoryReportPage( + @Parameter(description = "分页参数") @ParameterObject Page page, + @Parameter(description = "查询条件") @ParameterObject InventoryReportQueryDTO queryDTO) { + log.info("分页查询进销存报表,查询条件:{}", queryDTO); + Page result = inventoryReportService.page(page, queryDTO); + return R.ok(result); + } + + /** + * 列表查询进销存报表(不分页) + * @param queryDTO 查询条件 + * @return 列表结果 + */ + @Operation(summary = "列表查询进销存报表", description = "不分页查询,用于前端处理或小数据量场景") + @GetMapping("/inventory/list") + @PreAuthorize("@pms.hasPermission('supermarket_report_view')") + public R> getInventoryReportList( + @Parameter(description = "查询条件") @ParameterObject InventoryReportQueryDTO queryDTO) { + log.info("列表查询进销存报表,查询条件:{}", queryDTO); + List list = inventoryReportService.list(queryDTO); + return R.ok(list); + } + + /** + * 导出进销存报表 + * @param queryDTO 查询条件 + * @return Excel文件流 + */ + @ResponseExcel + @GetMapping("/inventory/export") + @Operation(summary = "导出进销存报表", description = "导出进销存报表数据为Excel文件") + @SysLog("导出进销存报表") + @PreAuthorize("@pms.hasPermission('supermarket_report_export')") + public List exportInventoryReport( + @Parameter(description = "查询条件") @ParameterObject InventoryReportQueryDTO queryDTO) { + log.info("开始导出进销存报表,查询条件:{}", queryDTO); + List list = inventoryReportService.exportExcel(queryDTO); + log.info("导出进销存报表成功,导出记录数:{}", list.size()); + return list; + } + + // ==================== 毛利报表 ==================== + + /** + * 分页查询毛利报表 + * @param page 分页对象 + * @param queryDTO 查询条件 + * @return 分页结果 + */ + @Operation(summary = "分页查询毛利报表", description = "按商品统计销售毛利和毛利率") + @GetMapping("/profit/page") + @PreAuthorize("@pms.hasPermission('supermarket_report_view')") + public R> getProfitReportPage( + @Parameter(description = "分页参数") @ParameterObject Page page, + @Parameter(description = "查询条件") @ParameterObject ProfitReportQueryDTO queryDTO) { + log.info("分页查询毛利报表,查询条件:{}", queryDTO); + Page result = profitReportService.page(page, queryDTO); + return R.ok(result); + } + + /** + * 列表查询毛利报表(不分页) + * @param queryDTO 查询条件 + * @return 列表结果 + */ + @Operation(summary = "列表查询毛利报表", description = "不分页查询,用于前端处理或小数据量场景") + @GetMapping("/profit/list") + @PreAuthorize("@pms.hasPermission('supermarket_report_view')") + public R> getProfitReportList( + @Parameter(description = "查询条件") @ParameterObject ProfitReportQueryDTO queryDTO) { + log.info("列表查询毛利报表,查询条件:{}", queryDTO); + List list = profitReportService.list(queryDTO); + return R.ok(list); + } + + /** + * 导出毛利报表 + * @param queryDTO 查询条件 + * @return Excel文件流 + */ + @ResponseExcel + @GetMapping("/profit/export") + @Operation(summary = "导出毛利报表", description = "导出毛利报表数据为Excel文件") + @SysLog("导出毛利报表") + @PreAuthorize("@pms.hasPermission('supermarket_report_export')") + public List exportProfitReport( + @Parameter(description = "查询条件") @ParameterObject ProfitReportQueryDTO queryDTO) { + log.info("开始导出毛利报表,查询条件:{}", queryDTO); + List list = profitReportService.exportExcel(queryDTO); + log.info("导出毛利报表成功,导出记录数:{}", list.size()); + return list; + } + + // ==================== 销售报表 ==================== + + /** + * 分页查询销售报表 + * @param page 分页对象 + * @param queryDTO 查询条件 + * @return 分页结果 + */ + @Operation(summary = "分页查询销售报表", description = "按商品统计销售数据,包含销售、退货、实际销售等") + @GetMapping("/sales/page") + @PreAuthorize("@pms.hasPermission('supermarket_report_view')") + public R> getSalesReportPage( + @Parameter(description = "分页参数") @ParameterObject Page page, + @Parameter(description = "查询条件") @ParameterObject SalesReportQueryDTO queryDTO) { + log.info("分页查询销售报表,查询条件:{}", queryDTO); + Page result = salesReportService.page(page, queryDTO); + return R.ok(result); + } + + /** + * 列表查询销售报表(不分页) + * @param queryDTO 查询条件 + * @return 列表结果 + */ + @Operation(summary = "列表查询销售报表", description = "不分页查询,用于前端处理或小数据量场景") + @GetMapping("/sales/list") + @PreAuthorize("@pms.hasPermission('supermarket_report_view')") + public R> getSalesReportList( + @Parameter(description = "查询条件") @ParameterObject SalesReportQueryDTO queryDTO) { + log.info("列表查询销售报表,查询条件:{}", queryDTO); + List list = salesReportService.list(queryDTO); + return R.ok(list); + } + + /** + * 导出销售报表 + * @param queryDTO 查询条件 + * @return Excel文件流 + */ + @ResponseExcel + @GetMapping("/sales/export") + @Operation(summary = "导出销售报表", description = "导出销售报表数据为Excel文件") + @SysLog("导出销售报表") + @PreAuthorize("@pms.hasPermission('supermarket_report_export')") + public List exportSalesReport( + @Parameter(description = "查询条件") @ParameterObject SalesReportQueryDTO queryDTO) { + log.info("开始导出销售报表,查询条件:{}", queryDTO); + List list = salesReportService.exportExcel(queryDTO); + log.info("导出销售报表成功,导出记录数:{}", list.size()); + return list; + } + + // ==================== 盘存报表 ==================== + + /** + * 分页查询盘存报表 + * @param page 分页对象 + * @param queryDTO 查询条件 + * @return 分页结果 + */ + @Operation(summary = "分页查询盘存报表", description = "查询盘点单明细,包含账面数量、实盘数量、差异数量等") + @GetMapping("/stocktake/page") + @PreAuthorize("@pms.hasPermission('supermarket_report_view')") + public R> getStocktakeReportPage( + @Parameter(description = "分页参数") @ParameterObject Page page, + @Parameter(description = "查询条件") @ParameterObject StocktakeReportQueryDTO queryDTO) { + log.info("分页查询盘存报表,查询条件:{}", queryDTO); + Page result = stocktakeReportService.page(page, queryDTO); + return R.ok(result); + } + + /** + * 列表查询盘存报表(不分页) + * @param queryDTO 查询条件 + * @return 列表结果 + */ + @Operation(summary = "列表查询盘存报表", description = "不分页查询,用于前端处理或小数据量场景") + @GetMapping("/stocktake/list") + @PreAuthorize("@pms.hasPermission('supermarket_report_view')") + public R> getStocktakeReportList( + @Parameter(description = "查询条件") @ParameterObject StocktakeReportQueryDTO queryDTO) { + log.info("列表查询盘存报表,查询条件:{}", queryDTO); + List list = stocktakeReportService.list(queryDTO); + return R.ok(list); + } + + /** + * 导出盘存报表 + * @param queryDTO 查询条件 + * @return Excel文件流 + */ + @ResponseExcel + @GetMapping("/stocktake/export") + @Operation(summary = "导出盘存报表", description = "导出盘存报表数据为Excel文件") + @SysLog("导出盘存报表") + @PreAuthorize("@pms.hasPermission('supermarket_report_export')") + public List exportStocktakeReport( + @Parameter(description = "查询条件") @ParameterObject StocktakeReportQueryDTO queryDTO) { + log.info("开始导出盘存报表,查询条件:{}", queryDTO); + List list = stocktakeReportService.exportExcel(queryDTO); + log.info("导出盘存报表成功,导出记录数:{}", list.size()); + return list; + } +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/InventoryReportMapper.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/InventoryReportMapper.java new file mode 100644 index 000000000..f8acd879c --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/InventoryReportMapper.java @@ -0,0 +1,40 @@ +package com.bsd.pjcloud.supermarket.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.common.data.datascope.DataScope; +import com.bsd.pjcloud.supermarket.dto.report.InventoryReportQueryDTO; +import com.bsd.pjcloud.supermarket.vo.report.InventoryReportVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 进销存报表Mapper + * + * @author pjcloud + * @date 2025-10-16 + */ +@Mapper +public interface InventoryReportMapper { + + /** + * 分页查询进销存报表 + * + * @param page 分页对象 + * @param queryDTO 查询条件 + * @param dataScope 数据权限 + * @return 分页结果 + */ + Page selectInventoryReportPage(Page page, @Param("param") InventoryReportQueryDTO queryDTO, @Param("dataScope") DataScope dataScope); + + /** + * 查询进销存报表列表(不分页) + * + * @param queryDTO 查询条件 + * @param dataScope 数据权限 + * @return 列表结果 + */ + List selectInventoryReportList(@Param("param") InventoryReportQueryDTO queryDTO, @Param("dataScope") DataScope dataScope); +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/ProfitReportMapper.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/ProfitReportMapper.java new file mode 100644 index 000000000..86f52660e --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/ProfitReportMapper.java @@ -0,0 +1,40 @@ +package com.bsd.pjcloud.supermarket.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.common.data.datascope.DataScope; +import com.bsd.pjcloud.supermarket.dto.report.ProfitReportQueryDTO; +import com.bsd.pjcloud.supermarket.vo.report.ProfitReportVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 毛利报表Mapper + * + * @author pjcloud + * @date 2025-10-16 + */ +@Mapper +public interface ProfitReportMapper { + + /** + * 分页查询毛利报表 + * + * @param page 分页对象 + * @param queryDTO 查询条件 + * @param dataScope 数据权限 + * @return 分页结果 + */ + Page selectProfitReportPage(Page page, @Param("param") ProfitReportQueryDTO queryDTO, @Param("dataScope") DataScope dataScope); + + /** + * 查询毛利报表列表(不分页) + * + * @param queryDTO 查询条件 + * @param dataScope 数据权限 + * @return 列表结果 + */ + List selectProfitReportList(@Param("param") ProfitReportQueryDTO queryDTO, @Param("dataScope") DataScope dataScope); +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/SalesReportMapper.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/SalesReportMapper.java new file mode 100644 index 000000000..0605ed05c --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/SalesReportMapper.java @@ -0,0 +1,40 @@ +package com.bsd.pjcloud.supermarket.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.common.data.datascope.DataScope; +import com.bsd.pjcloud.supermarket.dto.report.SalesReportQueryDTO; +import com.bsd.pjcloud.supermarket.vo.report.SalesReportVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 销售报表Mapper + * + * @author pjcloud + * @date 2025-10-16 + */ +@Mapper +public interface SalesReportMapper { + + /** + * 分页查询销售报表 + * + * @param page 分页对象 + * @param queryDTO 查询条件 + * @param dataScope 数据权限 + * @return 分页结果 + */ + Page selectSalesReportPage(Page page, @Param("param") SalesReportQueryDTO queryDTO, @Param("dataScope") DataScope dataScope); + + /** + * 查询销售报表列表(不分页) + * + * @param queryDTO 查询条件 + * @param dataScope 数据权限 + * @return 列表结果 + */ + List selectSalesReportList(@Param("param") SalesReportQueryDTO queryDTO, @Param("dataScope") DataScope dataScope); +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/StocktakeReportMapper.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/StocktakeReportMapper.java new file mode 100644 index 000000000..13c3b9d64 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/mapper/StocktakeReportMapper.java @@ -0,0 +1,40 @@ +package com.bsd.pjcloud.supermarket.mapper; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.common.data.datascope.DataScope; +import com.bsd.pjcloud.supermarket.dto.report.StocktakeReportQueryDTO; +import com.bsd.pjcloud.supermarket.vo.report.StocktakeReportVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 盘存报表Mapper + * + * @author pjcloud + * @date 2025-10-16 + */ +@Mapper +public interface StocktakeReportMapper { + + /** + * 分页查询盘存报表 + * + * @param page 分页对象 + * @param queryDTO 查询条件 + * @param dataScope 数据权限 + * @return 分页结果 + */ + Page selectStocktakeReportPage(Page page, @Param("param") StocktakeReportQueryDTO queryDTO, @Param("dataScope") DataScope dataScope); + + /** + * 查询盘存报表列表(不分页) + * + * @param queryDTO 查询条件 + * @param dataScope 数据权限 + * @return 列表结果 + */ + List selectStocktakeReportList(@Param("param") StocktakeReportQueryDTO queryDTO, @Param("dataScope") DataScope dataScope); +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/InventoryReportService.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/InventoryReportService.java new file mode 100644 index 000000000..156fc0be0 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/InventoryReportService.java @@ -0,0 +1,43 @@ +package com.bsd.pjcloud.supermarket.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.supermarket.dto.report.InventoryReportExcelDTO; +import com.bsd.pjcloud.supermarket.dto.report.InventoryReportQueryDTO; +import com.bsd.pjcloud.supermarket.vo.report.InventoryReportVO; + +import java.util.List; + +/** + * 进销存报表服务接口 + * + * @author pjcloud + * @date 2025-10-16 + */ +public interface InventoryReportService { + + /** + * 分页查询进销存报表 + * + * @param page 分页对象 + * @param queryDTO 查询条件 + * @return 分页结果 + */ + Page page(Page page, InventoryReportQueryDTO queryDTO); + + /** + * 查询进销存报表列表(不分页) + * + * @param queryDTO 查询条件 + * @return 列表结果 + */ + List list(InventoryReportQueryDTO queryDTO); + + /** + * 导出进销存报表 + * + * @param queryDTO 查询条件 + * @return Excel导出数据列表 + */ + List exportExcel(InventoryReportQueryDTO queryDTO); +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/ProfitReportService.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/ProfitReportService.java new file mode 100644 index 000000000..ed29e844b --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/ProfitReportService.java @@ -0,0 +1,43 @@ +package com.bsd.pjcloud.supermarket.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.supermarket.dto.report.ProfitReportExcelDTO; +import com.bsd.pjcloud.supermarket.dto.report.ProfitReportQueryDTO; +import com.bsd.pjcloud.supermarket.vo.report.ProfitReportVO; + +import java.util.List; + +/** + * 毛利报表服务接口 + * + * @author pjcloud + * @date 2025-10-16 + */ +public interface ProfitReportService { + + /** + * 分页查询毛利报表 + * + * @param page 分页对象 + * @param queryDTO 查询条件 + * @return 分页结果 + */ + Page page(Page page, ProfitReportQueryDTO queryDTO); + + /** + * 查询毛利报表列表(不分页) + * + * @param queryDTO 查询条件 + * @return 列表结果 + */ + List list(ProfitReportQueryDTO queryDTO); + + /** + * 导出毛利报表 + * + * @param queryDTO 查询条件 + * @return Excel导出数据列表 + */ + List exportExcel(ProfitReportQueryDTO queryDTO); +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SalesReportService.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SalesReportService.java new file mode 100644 index 000000000..b3f633d8f --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/SalesReportService.java @@ -0,0 +1,43 @@ +package com.bsd.pjcloud.supermarket.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.supermarket.dto.report.SalesReportExcelDTO; +import com.bsd.pjcloud.supermarket.dto.report.SalesReportQueryDTO; +import com.bsd.pjcloud.supermarket.vo.report.SalesReportVO; + +import java.util.List; + +/** + * 销售报表服务接口 + * + * @author pjcloud + * @date 2025-10-16 + */ +public interface SalesReportService { + + /** + * 分页查询销售报表 + * + * @param page 分页对象 + * @param queryDTO 查询条件 + * @return 分页结果 + */ + Page page(Page page, SalesReportQueryDTO queryDTO); + + /** + * 查询销售报表列表(不分页) + * + * @param queryDTO 查询条件 + * @return 列表结果 + */ + List list(SalesReportQueryDTO queryDTO); + + /** + * 导出销售报表 + * + * @param queryDTO 查询条件 + * @return Excel导出数据列表 + */ + List exportExcel(SalesReportQueryDTO queryDTO); +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/StocktakeReportService.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/StocktakeReportService.java new file mode 100644 index 000000000..875f669d1 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/StocktakeReportService.java @@ -0,0 +1,43 @@ +package com.bsd.pjcloud.supermarket.service; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.supermarket.dto.report.StocktakeReportExcelDTO; +import com.bsd.pjcloud.supermarket.dto.report.StocktakeReportQueryDTO; +import com.bsd.pjcloud.supermarket.vo.report.StocktakeReportVO; + +import java.util.List; + +/** + * 盘存报表服务接口 + * + * @author pjcloud + * @date 2025-10-16 + */ +public interface StocktakeReportService { + + /** + * 分页查询盘存报表 + * + * @param page 分页对象 + * @param queryDTO 查询条件 + * @return 分页结果 + */ + Page page(Page page, StocktakeReportQueryDTO queryDTO); + + /** + * 查询盘存报表列表(不分页) + * + * @param queryDTO 查询条件 + * @return 列表结果 + */ + List list(StocktakeReportQueryDTO queryDTO); + + /** + * 导出盘存报表 + * + * @param queryDTO 查询条件 + * @return Excel导出数据列表 + */ + List exportExcel(StocktakeReportQueryDTO queryDTO); +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/InventoryReportServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/InventoryReportServiceImpl.java new file mode 100644 index 000000000..ff37b7786 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/InventoryReportServiceImpl.java @@ -0,0 +1,51 @@ +package com.bsd.pjcloud.supermarket.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.common.core.util.BeanUtils; +import com.bsd.pjcloud.common.data.datascope.DataScope; +import com.bsd.pjcloud.supermarket.dto.report.InventoryReportExcelDTO; +import com.bsd.pjcloud.supermarket.dto.report.InventoryReportQueryDTO; +import com.bsd.pjcloud.supermarket.mapper.InventoryReportMapper; +import com.bsd.pjcloud.supermarket.service.InventoryReportService; +import com.bsd.pjcloud.supermarket.vo.report.InventoryReportVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 进销存报表服务实现类 + * + * @author pjcloud + * @date 2025-10-16 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class InventoryReportServiceImpl implements InventoryReportService { + + private final InventoryReportMapper inventoryReportMapper; + + @Override + public Page page(Page page, InventoryReportQueryDTO queryDTO) { + log.info("分页查询进销存报表,查询条件:{}", queryDTO); + return inventoryReportMapper.selectInventoryReportPage(page, queryDTO, DataScope.of()); + } + + @Override + public List list(InventoryReportQueryDTO queryDTO) { + log.info("列表查询进销存报表,查询条件:{}", queryDTO); + return inventoryReportMapper.selectInventoryReportList(queryDTO, DataScope.of()); + } + + @Override + public List exportExcel(InventoryReportQueryDTO queryDTO) { + log.info("导出进销存报表,查询条件:{}", queryDTO); + List list = inventoryReportMapper.selectInventoryReportList(queryDTO, DataScope.of()); + List excelList = BeanUtils.toBean(list, InventoryReportExcelDTO.class); + log.info("导出进销存报表成功,导出记录数:{}", excelList.size()); + return excelList; + } +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/ProfitReportServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/ProfitReportServiceImpl.java new file mode 100644 index 000000000..7714278ef --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/ProfitReportServiceImpl.java @@ -0,0 +1,51 @@ +package com.bsd.pjcloud.supermarket.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.common.core.util.BeanUtils; +import com.bsd.pjcloud.common.data.datascope.DataScope; +import com.bsd.pjcloud.supermarket.dto.report.ProfitReportExcelDTO; +import com.bsd.pjcloud.supermarket.dto.report.ProfitReportQueryDTO; +import com.bsd.pjcloud.supermarket.mapper.ProfitReportMapper; +import com.bsd.pjcloud.supermarket.service.ProfitReportService; +import com.bsd.pjcloud.supermarket.vo.report.ProfitReportVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 毛利报表服务实现类 + * + * @author pjcloud + * @date 2025-10-16 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class ProfitReportServiceImpl implements ProfitReportService { + + private final ProfitReportMapper profitReportMapper; + + @Override + public Page page(Page page, ProfitReportQueryDTO queryDTO) { + log.info("分页查询毛利报表,查询条件:{}", queryDTO); + return profitReportMapper.selectProfitReportPage(page, queryDTO, DataScope.of()); + } + + @Override + public List list(ProfitReportQueryDTO queryDTO) { + log.info("列表查询毛利报表,查询条件:{}", queryDTO); + return profitReportMapper.selectProfitReportList(queryDTO, DataScope.of()); + } + + @Override + public List exportExcel(ProfitReportQueryDTO queryDTO) { + log.info("导出毛利报表,查询条件:{}", queryDTO); + List list = profitReportMapper.selectProfitReportList(queryDTO, DataScope.of()); + List excelList = BeanUtils.toBean(list, ProfitReportExcelDTO.class); + log.info("导出毛利报表成功,导出记录数:{}", excelList.size()); + return excelList; + } +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SalesReportServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SalesReportServiceImpl.java new file mode 100644 index 000000000..e23c7fb23 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/SalesReportServiceImpl.java @@ -0,0 +1,51 @@ +package com.bsd.pjcloud.supermarket.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.common.core.util.BeanUtils; +import com.bsd.pjcloud.common.data.datascope.DataScope; +import com.bsd.pjcloud.supermarket.dto.report.SalesReportExcelDTO; +import com.bsd.pjcloud.supermarket.dto.report.SalesReportQueryDTO; +import com.bsd.pjcloud.supermarket.mapper.SalesReportMapper; +import com.bsd.pjcloud.supermarket.service.SalesReportService; +import com.bsd.pjcloud.supermarket.vo.report.SalesReportVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 销售报表服务实现类 + * + * @author pjcloud + * @date 2025-10-16 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class SalesReportServiceImpl implements SalesReportService { + + private final SalesReportMapper salesReportMapper; + + @Override + public Page page(Page page, SalesReportQueryDTO queryDTO) { + log.info("分页查询销售报表,查询条件:{}", queryDTO); + return salesReportMapper.selectSalesReportPage(page, queryDTO, DataScope.of()); + } + + @Override + public List list(SalesReportQueryDTO queryDTO) { + log.info("列表查询销售报表,查询条件:{}", queryDTO); + return salesReportMapper.selectSalesReportList(queryDTO, DataScope.of()); + } + + @Override + public List exportExcel(SalesReportQueryDTO queryDTO) { + log.info("导出销售报表,查询条件:{}", queryDTO); + List list = salesReportMapper.selectSalesReportList(queryDTO, DataScope.of()); + List excelList = BeanUtils.toBean(list, SalesReportExcelDTO.class); + log.info("导出销售报表成功,导出记录数:{}", excelList.size()); + return excelList; + } +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/StocktakeReportServiceImpl.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/StocktakeReportServiceImpl.java new file mode 100644 index 000000000..1dfeeaa85 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/service/impl/StocktakeReportServiceImpl.java @@ -0,0 +1,51 @@ +package com.bsd.pjcloud.supermarket.service.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.bsd.pjcloud.common.core.util.BeanUtils; +import com.bsd.pjcloud.common.data.datascope.DataScope; +import com.bsd.pjcloud.supermarket.dto.report.StocktakeReportExcelDTO; +import com.bsd.pjcloud.supermarket.dto.report.StocktakeReportQueryDTO; +import com.bsd.pjcloud.supermarket.mapper.StocktakeReportMapper; +import com.bsd.pjcloud.supermarket.service.StocktakeReportService; +import com.bsd.pjcloud.supermarket.vo.report.StocktakeReportVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 盘存报表服务实现类 + * + * @author pjcloud + * @date 2025-10-16 + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class StocktakeReportServiceImpl implements StocktakeReportService { + + private final StocktakeReportMapper stocktakeReportMapper; + + @Override + public Page page(Page page, StocktakeReportQueryDTO queryDTO) { + log.info("分页查询盘存报表,查询条件:{}", queryDTO); + return stocktakeReportMapper.selectStocktakeReportPage(page, queryDTO, DataScope.of()); + } + + @Override + public List list(StocktakeReportQueryDTO queryDTO) { + log.info("列表查询盘存报表,查询条件:{}", queryDTO); + return stocktakeReportMapper.selectStocktakeReportList(queryDTO, DataScope.of()); + } + + @Override + public List exportExcel(StocktakeReportQueryDTO queryDTO) { + log.info("导出盘存报表,查询条件:{}", queryDTO); + List list = stocktakeReportMapper.selectStocktakeReportList(queryDTO, DataScope.of()); + List excelList = BeanUtils.toBean(list, StocktakeReportExcelDTO.class); + log.info("导出盘存报表成功,导出记录数:{}", excelList.size()); + return excelList; + } +} + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/InventoryReportMapper.xml b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/InventoryReportMapper.xml new file mode 100644 index 000000000..cfd3968e8 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/InventoryReportMapper.xml @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + SELECT + spu.id AS product_id, + spu.product_code, + spu.product_name, + cat.name AS category_name, + base.spec AS spec, + base.unit AS unit, + base.package_num AS package_num, + base.purchase_price / 100.0 AS purchase_price, + base.retail_price / 100.0 AS retail_price, + + -- 采购入库数量和金额 (inventory_type = 3) + COALESCE(SUM(CASE WHEN r.inventory_type = '3' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) AS purchase_in_qty, + COALESCE(SUM(CASE WHEN r.inventory_type = '3' THEN item.total_amount END), 0) / 100.0 AS purchase_in_amount, + + -- 采购退货数量和金额 (inventory_type = 4) + COALESCE(SUM(CASE WHEN r.inventory_type = '4' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) AS purchase_return_qty, + COALESCE(SUM(CASE WHEN r.inventory_type = '4' THEN item.total_amount END), 0) / 100.0 AS purchase_return_amount, + + -- 销售数量和金额 (inventory_type = 5) + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) AS sales_qty, + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 AS sales_amount, + + -- 销售退货数量和金额 (inventory_type = 6) + COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) AS sales_return_qty, + COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN item.total_amount END), 0) / 100.0 AS sales_return_amount, + + -- 实际销售数量 = 销售数量 - 销售退货数量 + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) AS actual_sales_qty, + + -- 实际销售金额 = 销售金额 - 销售退货金额 + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 + - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN item.total_amount END), 0) / 100.0 AS actual_sales_amount, + + -- 销售成本 = 实际销售数量 × 参考进价 + (COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0)) * COALESCE(base.purchase_price / 100.0, 0) AS sales_cost, + + -- 期末库存数量 = 当前库存 + COALESCE(stock.current_stock, 0) AS closing_qty, + + -- 期末库存金额 = 期末数量 × 参考进价 + COALESCE(stock.current_stock, 0) * COALESCE(base.purchase_price / 100.0, 0) AS closing_amount, + + -- 期末库存售额 = 期末数量 × 零售价 + COALESCE(stock.current_stock, 0) * COALESCE(base.retail_price / 100.0, 0) AS closing_retail_amount, + + -- 销售毛利 = 实际销售金额 - 销售成本 + (COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 + - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN item.total_amount END), 0) / 100.0) + - ((COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0)) * COALESCE(base.purchase_price / 100.0, 0)) AS gross_profit, + + -- 首次进货日 + MIN(CASE WHEN r.inventory_type = '3' THEN r.inventory_date END) AS first_purchase_date, + + -- 期初库存 = 期末库存 - 采购入库 + 采购退货 + 销售 - 销售退货 + COALESCE(stock.current_stock, 0) + - COALESCE(SUM(CASE WHEN r.inventory_type = '3' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) + + COALESCE(SUM(CASE WHEN r.inventory_type = '4' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) + + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) + - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) AS opening_qty, + + -- 期初金额 = 期初库存 × 参考进价 + (COALESCE(stock.current_stock, 0) + - COALESCE(SUM(CASE WHEN r.inventory_type = '3' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) + + COALESCE(SUM(CASE WHEN r.inventory_type = '4' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) + + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) + - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0)) * COALESCE(base.purchase_price / 100.0, 0) AS opening_amount + + FROM sm_erp_spu spu + LEFT JOIN sm_erp_stock stock ON stock.erp_spu_id = spu.id + LEFT JOIN sm_erp_inventory_records_item item ON item.erp_spu_id = spu.id + LEFT JOIN sm_erp_inventory_records r ON r.id = item.inventory_id AND r.del_flag = '0' + LEFT JOIN sm_erp_unit base ON base.spu_id = spu.id AND base.is_base = TRUE + LEFT JOIN sm_erp_unit u ON u.id = item.unit_id + LEFT JOIN sm_erp_category cat ON cat.id = spu.category_id + + spu.del_flag = '0' + + AND r.inventory_date >= #{param.startDate} + + + AND r.inventory_date <= #{param.endDate} + + + AND spu.product_name LIKE CONCAT('%', #{param.productName}, '%') + + + AND spu.product_code LIKE CONCAT('%', #{param.productCode}, '%') + + + AND spu.category_id = #{param.categoryId} + + + AND r.supplier_id = #{param.supplierId} + + + ${dataScope.sqlFilter} + + + GROUP BY spu.id, spu.product_code, spu.product_name, cat.name, + base.spec, base.unit, base.package_num, base.purchase_price, base.retail_price, stock.current_stock + + + + + + + + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/ProfitReportMapper.xml b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/ProfitReportMapper.xml new file mode 100644 index 000000000..8fc2973d9 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/ProfitReportMapper.xml @@ -0,0 +1,225 @@ + + + + + + + + + + + + + + + + + + + + + + + + SELECT + spu.id AS product_id, + spu.product_code, + spu.product_name, + cat.name AS category_name, + base.spec AS spec, + base.unit AS unit, + + -- 销售数量 (inventory_type = 5 零售出库 - inventory_type = 6 零售退货) + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) AS sales_qty, + + -- 销售金额 + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 + - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN item.total_amount END), 0) / 100.0 AS sales_amount, + + -- 成本单价 = 商品参考进价 + COALESCE(base.purchase_price / 100.0, 0) AS cost_price, + + -- 销售单价 = 销售金额 / 销售数量 + CASE WHEN (COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0)) > 0 + THEN (COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 + - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN item.total_amount END), 0) / 100.0) + / (COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0)) + ELSE 0 + END AS sales_price, + + -- 销售成本 = 销售数量 × 成本单价 + (COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0)) * COALESCE(base.purchase_price / 100.0, 0) AS sales_cost, + + -- 销售毛利 = 销售金额 - 销售成本 + (COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 + - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN item.total_amount END), 0) / 100.0) + - ((COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0)) * COALESCE(base.purchase_price / 100.0, 0)) AS gross_profit, + + -- 毛利率 = (销售毛利 / 销售金额) × 100% + CASE WHEN (COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 + - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN item.total_amount END), 0) / 100.0) > 0 + THEN ((COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 + - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN item.total_amount END), 0) / 100.0) + - ((COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0)) * COALESCE(base.purchase_price / 100.0, 0))) + / (COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 + - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN item.total_amount END), 0) / 100.0) * 100 + ELSE 0 + END AS profit_margin, + + -- 销售笔数 + COUNT(DISTINCT CASE WHEN r.inventory_type IN ('5', '6') THEN r.id END) AS sales_count + + FROM sm_erp_spu spu + LEFT JOIN sm_erp_inventory_records_item item ON item.erp_spu_id = spu.id + LEFT JOIN sm_erp_inventory_records r ON r.id = item.inventory_id AND r.del_flag = '0' AND r.inventory_type IN ('5', '6') + LEFT JOIN sm_erp_unit base ON base.spu_id = spu.id AND base.is_base = TRUE + LEFT JOIN sm_erp_unit u ON u.id = item.unit_id + LEFT JOIN sm_erp_category cat ON cat.id = spu.category_id + + spu.del_flag = '0' + + AND r.inventory_date >= #{param.startDate} + + + AND r.inventory_date <= #{param.endDate} + + + AND spu.product_name LIKE CONCAT('%', #{param.productName}, '%') + + + AND spu.product_code LIKE CONCAT('%', #{param.productCode}, '%') + + + AND spu.category_id = #{param.categoryId} + + + AND r.supplier_id = #{param.supplierId} + + + ${dataScope.sqlFilter} + + + GROUP BY spu.id, spu.product_code, spu.product_name, cat.name, + base.spec, base.unit, base.purchase_price + + + + + + + + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SalesReportMapper.xml b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SalesReportMapper.xml new file mode 100644 index 000000000..9f73abb33 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/SalesReportMapper.xml @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + SELECT + spu.id AS product_id, + spu.product_code, + spu.product_name, + cat.name AS category_name, + base.spec AS spec, + base.unit AS unit, + + -- 销售数量 (inventory_type = 5) + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) AS sales_qty, + + -- 销售金额 + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 AS sales_amount, + + -- 退货数量 (inventory_type = 6) + COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) AS return_qty, + + -- 退货金额 + COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN item.total_amount END), 0) / 100.0 AS return_amount, + + -- 实际销售数量 = 销售数量 - 退货数量 + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) AS actual_sales_qty, + + -- 实际销售金额 = 销售金额 - 退货金额 + COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 + - COALESCE(SUM(CASE WHEN r.inventory_type = '6' THEN item.total_amount END), 0) / 100.0 AS actual_sales_amount, + + -- 销售单价 = 销售金额 / 销售数量 + CASE WHEN COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) > 0 + THEN COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN item.total_amount END), 0) / 100.0 + / COALESCE(SUM(CASE WHEN r.inventory_type = '5' THEN + CASE WHEN u.id IS NOT NULL AND base.id IS NOT NULL AND u.id != base.id + AND u.package_num IS NOT NULL AND u.package_num > 0 + THEN item.quantity * u.package_num + ELSE item.quantity + END + END), 0) + ELSE 0 + END AS sales_price, + + -- 销售笔数 + COUNT(DISTINCT CASE WHEN r.inventory_type IN ('5', '6') THEN r.id END) AS sales_count + + FROM sm_erp_spu spu + LEFT JOIN sm_erp_inventory_records_item item ON item.erp_spu_id = spu.id + LEFT JOIN sm_erp_inventory_records r ON r.id = item.inventory_id AND r.del_flag = '0' AND r.inventory_type IN ('5', '6') + LEFT JOIN sm_erp_unit base ON base.spu_id = spu.id AND base.is_base = TRUE + LEFT JOIN sm_erp_unit u ON u.id = item.unit_id + LEFT JOIN sm_erp_category cat ON cat.id = spu.category_id + + spu.del_flag = '0' + + AND r.inventory_date >= #{param.startDate} + + + AND r.inventory_date <= #{param.endDate} + + + AND spu.product_name LIKE CONCAT('%', #{param.productName}, '%') + + + AND spu.product_code LIKE CONCAT('%', #{param.productCode}, '%') + + + AND spu.category_id = #{param.categoryId} + + + ${dataScope.sqlFilter} + + + GROUP BY spu.id, spu.product_code, spu.product_name, cat.name, base.spec, base.unit + + + + + + + + diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/StocktakeReportMapper.xml b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/StocktakeReportMapper.xml new file mode 100644 index 000000000..a7d1c2d1a --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/resources/mapper/StocktakeReportMapper.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + SELECT + r.inventory_code AS stocktake_code, + r.inventory_date AS stocktake_date, + spu.id AS product_id, + spu.product_code, + spu.product_name, + cat.name AS category_name, + base.spec AS spec, + base.unit AS unit, + + -- 账面数量(这里假设item表中有book_qty字段,如果没有则使用quantity作为账面数量) + COALESCE(item.quantity, 0) AS book_qty, + + -- 实盘数量(这里假设实盘数量保存在某个字段,需要根据实际情况调整) + COALESCE(item.quantity, 0) AS physical_qty, + + -- 差异数量 = 实盘 - 账面(这里简化处理,实际需要根据业务逻辑调整) + 0 AS diff_qty, + + -- 差异金额 = 差异数量 × 成本价 + 0 AS diff_amount, + + r.remark + + FROM sm_erp_inventory_records r + INNER JOIN sm_erp_inventory_records_item item ON item.inventory_id = r.id + LEFT JOIN sm_erp_spu spu ON spu.id = item.erp_spu_id + LEFT JOIN sm_erp_unit base ON base.spu_id = spu.id AND base.is_base = TRUE + LEFT JOIN sm_erp_category cat ON cat.id = spu.category_id + + r.inventory_type = '7' + AND r.del_flag = '0' + AND spu.del_flag = '0' + + AND r.inventory_date >= #{param.startDate} + + + AND r.inventory_date <= #{param.endDate} + + + AND r.inventory_code LIKE CONCAT('%', #{param.stocktakeCode}, '%') + + + AND spu.product_name LIKE CONCAT('%', #{param.productName}, '%') + + + AND spu.product_code LIKE CONCAT('%', #{param.productCode}, '%') + + + AND spu.category_id = #{param.categoryId} + + + ${dataScope.sqlFilter} + + + + + + + + + + From b96f3cdfcdf98f241a1e0159efda68b73099a021 Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Fri, 17 Oct 2025 00:25:09 +0800 Subject: [PATCH 5/6] =?UTF-8?q?=E8=B6=85=E5=B8=82=E6=8A=A5=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InventoryDetailExcelDTO.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailExcelDTO.java diff --git a/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailExcelDTO.java b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailExcelDTO.java new file mode 100644 index 000000000..3c05ce046 --- /dev/null +++ b/pjcloud-supermarket/pjcloud-supermarket-api/src/main/java/com/bsd/pjcloud/supermarket/dto/inventoryrecords/InventoryDetailExcelDTO.java @@ -0,0 +1,69 @@ +package com.bsd.pjcloud.supermarket.dto.inventoryrecords; + +import com.alibaba.excel.annotation.ExcelProperty; +import com.alibaba.excel.annotation.write.style.ColumnWidth; +import com.alibaba.excel.annotation.write.style.ContentRowHeight; +import com.alibaba.excel.annotation.write.style.HeadRowHeight; +import lombok.Data; + +import java.math.BigDecimal; +import java.time.LocalDate; + +/** + * 出入库明细导出DTO + * + * @author pjcloud + * @date 2024-10-15 + */ +@Data +@ContentRowHeight(20) +@HeadRowHeight(25) +@ColumnWidth(15) +public class InventoryDetailExcelDTO { + + @ExcelProperty(value = "单据编号", index = 0) + @ColumnWidth(20) + private String inventoryCode; + + @ExcelProperty(value = "单据日期", index = 1) + @ColumnWidth(15) + private LocalDate inventoryDate; + + @ExcelProperty(value = "商品名称", index = 2) + @ColumnWidth(30) + private String productName; + + @ExcelProperty(value = "规格", index = 3) + @ColumnWidth(20) + private String spec; + + @ExcelProperty(value = "单位", index = 4) + @ColumnWidth(10) + private String unit; + + @ExcelProperty(value = "装数", index = 5) + @ColumnWidth(10) + private Integer packageNum; + + @ExcelProperty(value = "数量", index = 6) + @ColumnWidth(15) + private Double quantity; + + @ExcelProperty(value = "单价(元)", index = 7) + @ColumnWidth(15) + private BigDecimal unitPrice; + + @ExcelProperty(value = "总金额(元)", index = 8) + @ColumnWidth(15) + private BigDecimal totalAmount; + + @ExcelProperty(value = "供应商", index = 9) + @ColumnWidth(25) + private String supplierName; + + @ExcelProperty(value = "备注", index = 10) + @ColumnWidth(30) + private String remark; +} + + From a00314355cb34170da9393952dfc2ced85cf4fdd Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Fri, 17 Oct 2025 00:53:23 +0800 Subject: [PATCH 6/6] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=85=B1=E5=AF=8C?= =?UTF-8?q?=E5=95=86=E6=88=B7=E4=B8=BA=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bsd/pjcloud/supermarket/controller/MallAppController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallAppController.java b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallAppController.java index 6865503ca..d0fe79db6 100644 --- a/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallAppController.java +++ b/pjcloud-supermarket/pjcloud-supermarket-biz/src/main/java/com/bsd/pjcloud/supermarket/controller/MallAppController.java @@ -121,7 +121,7 @@ public class MallAppController { return R.failed("商户服务异常!"); } List merchants = listR.getData(); - if (merchants.isEmpty()) { + if (CollectionUtils.isEmpty( merchants)) { return R.ok(List.of()); }