进销存报表

This commit is contained in:
zjf committed 2025-10-15 17:42:40 +08:00
1 parent adb2a52929
commit efc2ef5045
10 files changed
+709

No files matched your search

@@ -0,0 +1,128 @@
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.report.InventoryInOutReportExcelDTO;
import com.bsd.pjcloud.supermarket.dto.report.InventoryInOutReportQueryDTO;
import com.bsd.pjcloud.supermarket.service.InventoryInOutReportService;
import com.bsd.pjcloud.supermarket.vo.report.InventoryInOutReportVO;
import com.bsd.pjcloud.supermarket.vo.report.InventoryInOutSummaryVO;
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.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 2024-10-15
*/
@Slf4j
@RestController
@RequestMapping("/report/inventory-inout")
@Tag(name = "进销存报表", description = "进销存报表管理")
@SecurityRequirement(name = HttpHeaders.AUTHORIZATION)
@RequiredArgsConstructor
public class InventoryInOutReportController {
private final InventoryInOutReportService reportService;
/**
* 分页查询进销存报表
*
* @param page 分页参数
* @param queryDTO 查询条件
* @return 分页结果
*/
@GetMapping("/page")
@Operation(summary = "分页查询进销存报表", description = "支持按商品、分类、时间等条件分页查询进销存数据")
@PreAuthorize("@pms.hasPermission('supermarket_inventoryInOutReport_view')")
public R<Page<InventoryInOutReportVO>> page(
@Parameter(description = "分页参数") Page<?> page,
@Parameter(description = "查询条件") InventoryInOutReportQueryDTO queryDTO) {
try {
Page<InventoryInOutReportVO> result = reportService.page(page, queryDTO);
log.info("分页查询进销存报表成功,查询条件:{},返回记录数:{}", queryDTO, result.getRecords().size());
return R.ok(result);
} catch (Exception e) {
log.error("分页查询进销存报表失败", e);
return R.failed("查询失败:" + e.getMessage());
}
}
/**
* 列表查询进销存报表(用于前端直接导出)
*
* @param queryDTO 查询条件
* @return 列表数据
*/
@GetMapping("/list")
@Operation(summary = "列表查询进销存报表", description = "不分页查询所有符合条件的数据,用于前端直接导出")
@PreAuthorize("@pms.hasPermission('supermarket_inventoryInOutReport_view')")
public R<List<InventoryInOutReportVO>> list(
@Parameter(description = "查询条件") InventoryInOutReportQueryDTO queryDTO) {
try {
List<InventoryInOutReportVO> list = reportService.list(queryDTO);
log.info("列表查询进销存报表成功,查询条件:{},返回记录数:{}", queryDTO, list.size());
return R.ok(list);
} catch (Exception e) {
log.error("列表查询进销存报表失败", e);
return R.failed("查询失败:" + e.getMessage());
}
}
/**
* 获取进销存汇总统计
*
* @param queryDTO 查询条件
* @return 汇总数据
*/
@GetMapping("/summary")
@Operation(summary = "获取进销存汇总统计", description = "统计总入库、总出库、库存周转率等汇总数据")
@PreAuthorize("@pms.hasPermission('supermarket_inventoryInOutReport_view')")
public R<InventoryInOutSummaryVO> getSummary(
@Parameter(description = "查询条件") InventoryInOutReportQueryDTO queryDTO) {
try {
InventoryInOutSummaryVO summary = reportService.getSummary(queryDTO);
log.info("获取进销存汇总统计成功,统计商品数:{}", summary.getTotalProducts());
return R.ok(summary);
} catch (Exception e) {
log.error("获取进销存汇总统计失败", e);
return R.failed("统计失败:" + e.getMessage());
}
}
/**
* 导出进销存报表Excel
*
* @param queryDTO 查询条件
* @return Excel文件流
*/
@ResponseExcel
@GetMapping("/export")
@Operation(summary = "导出进销存报表", description = "导出进销存报表数据为Excel文件")
@SysLog("导出进销存报表")
@PreAuthorize("@pms.hasPermission('supermarket_inventoryInOutReport_export')")
public List<InventoryInOutReportExcelDTO> export(
@Parameter(description = "查询条件") InventoryInOutReportQueryDTO queryDTO) {
log.info("开始导出进销存报表,查询条件:{}", queryDTO);
List<InventoryInOutReportExcelDTO> list = reportService.exportExcel(queryDTO);
log.info("导出进销存报表成功,导出记录数:{}", list.size());
return list;
}
}
@@ -0,0 +1,46 @@
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.supermarket.dto.report.InventoryStocktakeReportQueryDTO;
import com.bsd.pjcloud.supermarket.service.InventoryStocktakeReportService;
import com.bsd.pjcloud.supermarket.vo.report.InventoryStocktakeReportVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
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;
@RestController
@RequestMapping("/report/stocktake")
@Tag(name = "盘存报表")
@RequiredArgsConstructor
public class InventoryStocktakeReportController {
private final InventoryStocktakeReportService reportService;
@GetMapping("/page")
@Operation(summary = "分页查询盘存报表")
public R<Page<InventoryStocktakeReportVO>> page(Page<?> page, InventoryStocktakeReportQueryDTO queryDTO) {
Page<InventoryStocktakeReportVO> result = reportService.page(page, queryDTO);
return R.ok(result);
}
@GetMapping("/list")
@Operation(summary = "列表查询盘存报表(用于导出)")
public R<List<InventoryStocktakeReportVO>> list(InventoryStocktakeReportQueryDTO queryDTO) {
List<InventoryStocktakeReportVO> list = reportService.list(queryDTO);
return R.ok(list);
}
}
@@ -0,0 +1,22 @@
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.InventoryInOutReportQueryDTO;
import com.bsd.pjcloud.supermarket.vo.report.InventoryInOutReportVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface InventoryInOutReportMapper {
List<InventoryInOutReportVO> selectInOutSummary(@Param("param") InventoryInOutReportQueryDTO queryDTO,
@Param("dataScope") DataScope dataScope);
Page<InventoryInOutReportVO> selectInOutSummaryPage(Page<?> page,
@Param("param") InventoryInOutReportQueryDTO queryDTO,
@Param("dataScope") DataScope dataScope);
}
@@ -0,0 +1,24 @@
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.InventoryStocktakeReportQueryDTO;
import com.bsd.pjcloud.supermarket.vo.report.InventoryStocktakeReportVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface InventoryStocktakeReportMapper {
List<InventoryStocktakeReportVO> selectStocktakeSummary(@Param("param") InventoryStocktakeReportQueryDTO queryDTO,
@Param("dataScope") DataScope dataScope);
Page<InventoryStocktakeReportVO> selectStocktakeSummaryPage(Page<?> page,
@Param("param") InventoryStocktakeReportQueryDTO queryDTO,
@Param("dataScope") DataScope dataScope);
}
@@ -0,0 +1,54 @@
package com.bsd.pjcloud.supermarket.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.supermarket.dto.report.InventoryInOutReportExcelDTO;
import com.bsd.pjcloud.supermarket.dto.report.InventoryInOutReportQueryDTO;
import com.bsd.pjcloud.supermarket.vo.report.InventoryInOutReportVO;
import com.bsd.pjcloud.supermarket.vo.report.InventoryInOutSummaryVO;
import java.util.List;
/**
* 进销存报表服务
*
* @author pjcloud
* @date 2024-10-15
*/
public interface InventoryInOutReportService {
/**
* 分页查询进销存报表
*
* @param page 分页参数
* @param queryDTO 查询条件
* @return 分页结果
*/
Page<InventoryInOutReportVO> page(Page<?> page, InventoryInOutReportQueryDTO queryDTO);
/**
* 列表查询进销存报表(用于导出)
*
* @param queryDTO 查询条件
* @return 列表数据
*/
List<InventoryInOutReportVO> list(InventoryInOutReportQueryDTO queryDTO);
/**
* 获取进销存汇总统计
*
* @param queryDTO 查询条件
* @return 汇总数据
*/
InventoryInOutSummaryVO getSummary(InventoryInOutReportQueryDTO queryDTO);
/**
* 导出进销存报表(Excel)
*
* @param queryDTO 查询条件
* @return Excel导出数据列表
*/
List<InventoryInOutReportExcelDTO> exportExcel(InventoryInOutReportQueryDTO queryDTO);
}
@@ -0,0 +1,24 @@
package com.bsd.pjcloud.supermarket.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.supermarket.dto.report.InventoryStocktakeReportQueryDTO;
import com.bsd.pjcloud.supermarket.vo.report.InventoryStocktakeReportVO;
import java.util.List;
public interface InventoryStocktakeReportService {
Page<InventoryStocktakeReportVO> page(Page<?> page, InventoryStocktakeReportQueryDTO queryDTO);
List<InventoryStocktakeReportVO> list(InventoryStocktakeReportQueryDTO queryDTO);
}
@@ -0,0 +1,127 @@
package com.bsd.pjcloud.supermarket.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.data.datascope.DataScope;
import com.bsd.pjcloud.supermarket.dto.report.InventoryInOutReportExcelDTO;
import com.bsd.pjcloud.supermarket.dto.report.InventoryInOutReportQueryDTO;
import com.bsd.pjcloud.supermarket.mapper.InventoryInOutReportMapper;
import com.bsd.pjcloud.supermarket.service.InventoryInOutReportService;
import com.bsd.pjcloud.supermarket.vo.report.InventoryInOutReportVO;
import com.bsd.pjcloud.supermarket.vo.report.InventoryInOutSummaryVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import java.util.stream.Collectors;
/**
* 进销存报表服务实现
*
* @author pjcloud
* @date 2024-10-15
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class InventoryInOutReportServiceImpl implements InventoryInOutReportService {
private final InventoryInOutReportMapper reportMapper;
@Override
public Page<InventoryInOutReportVO> page(Page<?> page, InventoryInOutReportQueryDTO queryDTO) {
return reportMapper.selectInOutSummaryPage(page, queryDTO, DataScope.of());
}
@Override
public List<InventoryInOutReportVO> list(InventoryInOutReportQueryDTO queryDTO) {
return reportMapper.selectInOutSummary(queryDTO, DataScope.of());
}
@Override
public InventoryInOutSummaryVO getSummary(InventoryInOutReportQueryDTO queryDTO) {
List<InventoryInOutReportVO> list = list(queryDTO);
InventoryInOutSummaryVO summary = new InventoryInOutSummaryVO();
// 统计商品总数
summary.setTotalProducts(list.size());
// 统计有发生额的商品数量
long changedCount = list.stream()
.filter(vo -> vo.getInQty() != null && vo.getInQty() > 0
|| vo.getOutQty() != null && vo.getOutQty() > 0)
.count();
summary.setChangedProducts((int) changedCount);
// 汇总数量和金额
double totalOpeningQty = list.stream()
.mapToDouble(vo -> vo.getOpeningQty() != null ? vo.getOpeningQty() : 0.0)
.sum();
double totalInQty = list.stream()
.mapToDouble(vo -> vo.getInQty() != null ? vo.getInQty() : 0.0)
.sum();
double totalOutQty = list.stream()
.mapToDouble(vo -> vo.getOutQty() != null ? vo.getOutQty() : 0.0)
.sum();
double totalClosingQty = list.stream()
.mapToDouble(vo -> vo.getClosingQty() != null ? vo.getClosingQty() : 0.0)
.sum();
BigDecimal totalInAmount = list.stream()
.map(vo -> vo.getInAmount() != null ? vo.getInAmount() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal totalOutAmount = list.stream()
.map(vo -> vo.getOutAmount() != null ? vo.getOutAmount() : BigDecimal.ZERO)
.reduce(BigDecimal.ZERO, BigDecimal::add);
summary.setTotalOpeningQty(totalOpeningQty);
summary.setTotalInQty(totalInQty);
summary.setTotalOutQty(totalOutQty);
summary.setTotalClosingQty(totalClosingQty);
summary.setTotalInAmount(totalInAmount.setScale(2, RoundingMode.HALF_UP));
summary.setTotalOutAmount(totalOutAmount.setScale(2, RoundingMode.HALF_UP));
// 计算库存周转率 = 出库数量 / 平均库存
// 平均库存 = (期初库存 + 期末库存) / 2
double avgStock = (totalOpeningQty + totalClosingQty) / 2.0;
if (avgStock > 0) {
BigDecimal turnoverRate = BigDecimal.valueOf(totalOutQty / avgStock)
.setScale(2, RoundingMode.HALF_UP);
summary.setTurnoverRate(turnoverRate);
} else {
summary.setTurnoverRate(BigDecimal.ZERO);
}
// 计算平均单价 = 总出库金额 / 总出库数量
if (totalOutQty > 0) {
BigDecimal avgPrice = totalOutAmount.divide(
BigDecimal.valueOf(totalOutQty), 2, RoundingMode.HALF_UP);
summary.setAvgPrice(avgPrice);
} else {
summary.setAvgPrice(BigDecimal.ZERO);
}
log.info("进销存报表汇总统计完成,总商品数:{},有发生额:{}",
summary.getTotalProducts(), summary.getChangedProducts());
return summary;
}
@Override
public List<InventoryInOutReportExcelDTO> exportExcel(InventoryInOutReportQueryDTO queryDTO) {
List<InventoryInOutReportVO> list = list(queryDTO);
return list.stream().map(vo -> {
InventoryInOutReportExcelDTO dto = new InventoryInOutReportExcelDTO();
BeanUtil.copyProperties(vo, dto);
return dto;
}).collect(Collectors.toList());
}
}
@@ -0,0 +1,39 @@
package com.bsd.pjcloud.supermarket.service.impl;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.bsd.pjcloud.common.data.datascope.DataScope;
import com.bsd.pjcloud.supermarket.dto.report.InventoryStocktakeReportQueryDTO;
import com.bsd.pjcloud.supermarket.mapper.InventoryStocktakeReportMapper;
import com.bsd.pjcloud.supermarket.service.InventoryStocktakeReportService;
import com.bsd.pjcloud.supermarket.vo.report.InventoryStocktakeReportVO;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
@RequiredArgsConstructor
public class InventoryStocktakeReportServiceImpl implements InventoryStocktakeReportService {
private final InventoryStocktakeReportMapper reportMapper;
@Override
public Page<InventoryStocktakeReportVO> page(Page<?> page, InventoryStocktakeReportQueryDTO queryDTO) {
return reportMapper.selectStocktakeSummaryPage(page, queryDTO, DataScope.of());
}
@Override
public List<InventoryStocktakeReportVO> list(InventoryStocktakeReportQueryDTO queryDTO) {
return reportMapper.selectStocktakeSummary(queryDTO, DataScope.of());
}
}
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsd.pjcloud.supermarket.mapper.InventoryInOutReportMapper">
<resultMap id="InOutResultMap" type="com.bsd.pjcloud.supermarket.vo.report.InventoryInOutReportVO">
<result property="productId" column="product_id"/>
<result property="productCode" column="product_code"/>
<result property="productName" column="product_name"/>
<result property="categoryId" column="category_id"/>
<result property="categoryName" column="category_name"/>
<result property="unit" column="unit"/>
<result property="openingQty" column="opening_qty"/>
<result property="inQty" column="in_qty"/>
<result property="outQty" column="out_qty"/>
<result property="closingQty" column="closing_qty"/>
<result property="inAmount" column="in_amount"/>
<result property="outAmount" column="out_amount"/>
</resultMap>
<!--
按商品聚合进销存报表
核心逻辑:
1. 期初库存 = 当前实时库存 - 统计期内入库 + 统计期内出库
2. 期末库存 = 当前实时库存
3. 所有数量统一转换为基础单位(使用package_num进行单位换算)
4. 金额单位为元(数据库存储为分,需除以100)
5. 时间条件通过WHERE子句过滤,避免JSqlParser解析错误
-->
<sql id="InOutCoreSql">
SELECT
spu.id AS product_id,
spu.product_code,
spu.product_name,
spu.category_id,
cat.name AS category_name,
base.unit AS unit,
COALESCE(SUM(CASE WHEN item.order_type = '1' 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 in_qty,
COALESCE(SUM(CASE WHEN item.order_type = '2' 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 out_qty,
COALESCE(SUM(CASE WHEN item.order_type = '1' THEN item.total_amount END), 0) / 100.0 AS in_amount,
COALESCE(SUM(CASE WHEN item.order_type = '2' THEN item.total_amount END), 0) / 100.0 AS out_amount,
COALESCE(stock.current_stock, 0) AS closing_qty,
COALESCE(stock.current_stock, 0)
- COALESCE(SUM(CASE WHEN item.order_type = '1' 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 item.order_type = '2' 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
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
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
<where>
spu.del_flag = '0'
<if test="param.startTime != null and param.endTime != null">
AND r.create_time &gt;= #{param.startTime}
AND r.create_time &lt;= #{param.endTime}
</if>
<if test="param.startTime == null and param.endTime == null and param.startDate != null and param.endDate != null">
AND r.inventory_date &gt;= #{param.startDate}
AND r.inventory_date &lt;= #{param.endDate}
</if>
<if test="param.productName != null and param.productName != ''">
AND spu.product_name LIKE CONCAT('%', #{param.productName}, '%')
</if>
<if test="param.productCode != null and param.productCode != ''">
AND spu.product_code LIKE CONCAT('%', #{param.productCode}, '%')
</if>
<if test="param.categoryId != null">
AND spu.category_id = #{param.categoryId}
</if>
<if test="dataScope != null">
${dataScope.sqlFilter}
</if>
</where>
GROUP BY spu.id, spu.product_code, spu.product_name, spu.category_id, cat.name, base.unit, stock.current_stock
</sql>
<select id="selectInOutSummary" resultMap="InOutResultMap">
<include refid="InOutCoreSql"/>
<if test="param.onlyChanged == true">
HAVING (in_qty != 0 OR out_qty != 0)
</if>
<if test="param.topLimit != null and param.topLimit &gt; 0">
LIMIT #{param.topLimit}
</if>
</select>
<select id="selectInOutSummaryPage" resultMap="InOutResultMap">
<include refid="InOutCoreSql"/>
<if test="param.onlyChanged == true">
HAVING (in_qty != 0 OR out_qty != 0)
</if>
ORDER BY spu.id DESC
</select>
</mapper>
@@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bsd.pjcloud.supermarket.mapper.InventoryStocktakeReportMapper">
<resultMap id="StocktakeResultMap" type="com.bsd.pjcloud.supermarket.vo.report.InventoryStocktakeReportVO">
<result property="productId" column="product_id"/>
<result property="productCode" column="product_code"/>
<result property="productName" column="product_name"/>
<result property="categoryId" column="category_id"/>
<result property="categoryName" column="category_name"/>
<result property="unit" column="unit"/>
<result property="bookQty" column="book_qty"/>
<result property="physicalQty" column="physical_qty"/>
<result property="diffQty" column="diff_qty"/>
<result property="diffAmount" column="diff_amount"/>
</resultMap>
<!-- 盘点明细字段:item.account_number(账面), item.real_number(实盘), item.real_price(实际单价-分) -->
<sql id="StocktakeCoreSql">
SELECT
spu.id AS product_id,
spu.product_code,
spu.product_name,
spu.category_id,
cat.name AS category_name,
base.unit AS unit,
-- 账面数量(折算基础单位)
COALESCE(SUM(
CASE WHEN item.account_number IS NOT NULL 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.account_number * u.package_num ELSE item.account_number END
END
), 0) AS book_qty,
-- 实盘数量(折算基础单位)
COALESCE(SUM(
CASE WHEN item.real_number IS NOT NULL 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.real_number * u.package_num ELSE item.real_number END
END
), 0) AS physical_qty,
-- 差异数量(实盘-账面)
COALESCE(SUM(
(CASE WHEN item.real_number IS NOT NULL 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.real_number * u.package_num ELSE item.real_number END
END)
-
(CASE WHEN item.account_number IS NOT NULL 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.account_number * u.package_num ELSE item.account_number END
END)
), 0) AS diff_qty,
-- 差异金额(元)= 差异数量 * 实际单价(元)
COALESCE(SUM(
(CASE WHEN item.real_price IS NOT NULL THEN item.real_price ELSE 0 END) *
(
(CASE WHEN item.real_number IS NOT NULL 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.real_number * u.package_num ELSE item.real_number END
END)
-
(CASE WHEN item.account_number IS NOT NULL 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.account_number * u.package_num ELSE item.account_number END
END)
)
), 0) / 100.0 AS diff_amount
FROM sm_erp_inventory_records_item item
LEFT JOIN sm_erp_inventory_records r ON r.id = item.inventory_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_unit u ON u.id = item.unit_id
LEFT JOIN sm_erp_category cat ON cat.id = spu.category_id
<where>
-- 仅盘点单据
r.inventory_type = '7'
<if test="param.startTime != null and param.endTime != null">
AND r.create_time &gt;= #{param.startTime}
AND r.create_time &lt;= #{param.endTime}
</if>
<if test="param.startTime == null and param.endTime == null and param.startDate != null and param.endDate != null">
AND r.inventory_date &gt;= #{param.startDate}
AND r.inventory_date &lt;= #{param.endDate}
</if>
<if test="param.productName != null and param.productName != ''">
AND spu.product_name like CONCAT('%', #{param.productName}, '%')
</if>
<if test="param.productCode != null and param.productCode != ''">
AND spu.product_code like CONCAT('%', #{param.productCode}, '%')
</if>
<if test="param.categoryId != null">
AND spu.category_id = #{param.categoryId}
</if>
<if test="param.warehouseId != null and param.warehouseId != ''">
AND r.warehouse_id = #{param.warehouseId}
</if>
<if test="dataScope != null">
${dataScope.sqlFilter}
</if>
</where>
GROUP BY spu.id, spu.product_code, spu.product_name, spu.category_id, cat.name, base.unit
</sql>
<select id="selectStocktakeSummary" resultMap="StocktakeResultMap">
<include refid="StocktakeCoreSql"/>
<if test="param.onlyDiff == true">
HAVING diff_qty != 0
</if>
</select>
<select id="selectStocktakeSummaryPage" resultMap="StocktakeResultMap">
<include refid="StocktakeCoreSql"/>
<if test="param.onlyDiff == true">
HAVING diff_qty != 0
</if>
ORDER BY spu.id DESC
</select>
</mapper>