进销存报表
This commit is contained in:
1 parent
adb2a52929
commit
efc2ef5045
10 files changed
+709
No files matched your search
+128
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+46
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+22
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+24
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+54
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
+24
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+127
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+39
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+123
@@ -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 >= #{param.startTime}
|
||||
AND r.create_time <= #{param.endTime}
|
||||
</if>
|
||||
<if test="param.startTime == null and param.endTime == null and param.startDate != null and param.endDate != null">
|
||||
AND r.inventory_date >= #{param.startDate}
|
||||
AND r.inventory_date <= #{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 > 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>
|
||||
|
||||
|
||||
|
||||
+122
@@ -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 >= #{param.startTime}
|
||||
AND r.create_time <= #{param.endTime}
|
||||
</if>
|
||||
<if test="param.startTime == null and param.endTime == null and param.startDate != null and param.endDate != null">
|
||||
AND r.inventory_date >= #{param.startDate}
|
||||
AND r.inventory_date <= #{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>
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user