|
|
|
|
@ -5,18 +5,41 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
|
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
|
|
|
|
import com.zbkj.modules.autogencode.entity.SysDictData;
|
|
|
|
|
import com.zbkj.modules.autogencode.service.SysDictDataService;
|
|
|
|
|
import org.apache.poi.hssf.usermodel.HSSFSheet;
|
|
|
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
import com.zbkj.modules.autogencode.dao.CkStockChangeDao;
|
|
|
|
|
import com.zbkj.modules.autogencode.entity.CkStockChange;
|
|
|
|
|
import com.zbkj.modules.autogencode.dao.CmCustDao;
|
|
|
|
|
import com.zbkj.modules.autogencode.entity.CmCust;
|
|
|
|
|
import com.zbkj.modules.autogencode.service.CkStockChangeService;
|
|
|
|
|
import com.zbkj.common.page.CommonPage;
|
|
|
|
|
import com.zbkj.common.request.PageParamRequest;
|
|
|
|
|
import com.zbkj.common.request.InventoryRequest;
|
|
|
|
|
import com.zbkj.common.response.InventoryResponse;
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
import javax.servlet.http.HttpServletResponse;
|
|
|
|
|
import java.io.IOException;
|
|
|
|
|
import java.net.URLEncoder;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.util.HashMap;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
|
|
|
|
import org.apache.poi.ss.usermodel.*;
|
|
|
|
|
import org.apache.poi.ss.util.CellRangeAddress;
|
|
|
|
|
import org.springframework.web.context.request.RequestContextHolder;
|
|
|
|
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
|
|
|
|
|
|
|
|
|
import java.util.Objects;
|
|
|
|
|
import com.zbkj.modules.autogencode.entity.CmCustProduct;
|
|
|
|
|
import com.zbkj.modules.autogencode.dao.CmCustProductDao;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Service("ckStockChangeService")
|
|
|
|
|
@ -26,7 +49,16 @@ public class CkStockChangeServiceImpl extends ServiceImpl<CkStockChangeDao, CkSt
|
|
|
|
|
@Resource
|
|
|
|
|
private CkStockChangeDao dao;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
private CmCustProductDao cmCustProductDao;
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
private SysDictDataService sysDictDataService;
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
private CmCustDao cmCustDao;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 带分页参数的列表查询实现
|
|
|
|
|
*/
|
|
|
|
|
@ -37,6 +69,590 @@ public class CkStockChangeServiceImpl extends ServiceImpl<CkStockChangeDao, CkSt
|
|
|
|
|
|
|
|
|
|
return dao.selectList(queryWrapper);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 查询物资盘点表数据
|
|
|
|
|
* 上期结存的重量就是最后一条的after_wt,其价值就是after_value
|
|
|
|
|
* 本期入库的重量就是查询时间段中的所有changeType为入库的变动重量的合计,价值就是查询时间段中的所有changeType为入库的变动价值的合计
|
|
|
|
|
* 本期出库的重量就是查询时间段中的所有changeType为出库的变动重量的合计,价值就是查询时间段中的所有changeType为出库的变动价值的合计
|
|
|
|
|
* 累计结算就是以上期结存的重量和价值作为初始值,然后根据本期的入库出库进行加减
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public List<InventoryResponse> getInventoryList(InventoryRequest request) {
|
|
|
|
|
List<InventoryResponse> result = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
// 构建查询条件
|
|
|
|
|
LambdaQueryWrapper<CkStockChange> wrapper = new LambdaQueryWrapper<>();
|
|
|
|
|
|
|
|
|
|
if (request.getCustId() != null) {
|
|
|
|
|
wrapper.eq(CkStockChange::getCustId, request.getCustId());
|
|
|
|
|
}
|
|
|
|
|
if (request.getCargoId() != null) {
|
|
|
|
|
wrapper.eq(CkStockChange::getCargoId, request.getCargoId());
|
|
|
|
|
}
|
|
|
|
|
if (request.getCargoName() != null) {
|
|
|
|
|
wrapper.eq(CkStockChange::getCargoName, request.getCargoName());
|
|
|
|
|
}
|
|
|
|
|
if (request.getStockId() != null) {
|
|
|
|
|
wrapper.eq(CkStockChange::getStockId, request.getStockId());
|
|
|
|
|
}
|
|
|
|
|
if (request.getStockName() != null) {
|
|
|
|
|
wrapper.eq(CkStockChange::getStockName, request.getStockName());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 按时间升序排序,便于取最后一条记录
|
|
|
|
|
wrapper.orderByAsc(CkStockChange::getChangeDate);
|
|
|
|
|
|
|
|
|
|
// 查询所有库存变动记录
|
|
|
|
|
List<CkStockChange> allChanges = dao.selectList(wrapper);
|
|
|
|
|
|
|
|
|
|
// 1. 同bill_id + cargoId去重,取每组最后一条记录
|
|
|
|
|
Map<String, CkStockChange> uniqueByBillAndCargo = new HashMap<>();
|
|
|
|
|
for (CkStockChange change : allChanges) {
|
|
|
|
|
String key = change.getBillId() + "_" + change.getCargoId();
|
|
|
|
|
// 由于已经按时间升序排序,后面的记录会覆盖前面的,最终保留最后一条
|
|
|
|
|
uniqueByBillAndCargo.put(key, change);
|
|
|
|
|
}
|
|
|
|
|
List<CkStockChange> uniqueChanges = new ArrayList<>(uniqueByBillAndCargo.values());
|
|
|
|
|
|
|
|
|
|
// 2. 按 cargoName + hsCode + custId + stockId 分组
|
|
|
|
|
Map<String, List<CkStockChange>> groupedByProduct = uniqueChanges.stream()
|
|
|
|
|
.collect(Collectors.groupingBy(change ->
|
|
|
|
|
change.getCargoName() + "_" + change.getHsCode() + "_" + change.getCustId() + "_" + change.getStockId()));
|
|
|
|
|
|
|
|
|
|
// 3. 处理物品类别过滤
|
|
|
|
|
if (request.getCargoCategory() != null && !request.getCargoCategory().isEmpty()) {
|
|
|
|
|
// 获取所有商品ID
|
|
|
|
|
List<Long> cargoIds = uniqueChanges.stream()
|
|
|
|
|
.map(CkStockChange::getCargoId)
|
|
|
|
|
.filter(Objects::nonNull)
|
|
|
|
|
.distinct()
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
|
|
|
|
if (!cargoIds.isEmpty()) {
|
|
|
|
|
// 查询商品分类信息
|
|
|
|
|
List<CmCustProduct> products = cmCustProductDao.selectList(new LambdaQueryWrapper<CmCustProduct>()
|
|
|
|
|
.in(CmCustProduct::getId, cargoIds));
|
|
|
|
|
|
|
|
|
|
// 构建商品ID到分类ID的映射
|
|
|
|
|
Map<Long, Long> cargoClassMap = products.stream()
|
|
|
|
|
.collect(Collectors.toMap(CmCustProduct::getId, CmCustProduct::getClassId, (a, b) -> b));
|
|
|
|
|
|
|
|
|
|
// 构建商品ID到单位的映射
|
|
|
|
|
Map<Long, String> cargoUnitMap = products.stream()
|
|
|
|
|
.collect(Collectors.toMap(CmCustProduct::getId, CmCustProduct::getUnit, (a, b) -> b));
|
|
|
|
|
|
|
|
|
|
// 过滤分组,只保留符合分类条件的商品
|
|
|
|
|
Map<String, List<CkStockChange>> filteredGroups = new HashMap<>();
|
|
|
|
|
for (Map.Entry<String, List<CkStockChange>> entry : groupedByProduct.entrySet()) {
|
|
|
|
|
List<CkStockChange> changes = entry.getValue();
|
|
|
|
|
if (!changes.isEmpty()) {
|
|
|
|
|
CkStockChange firstChange = changes.get(0);
|
|
|
|
|
Long classId = cargoClassMap.get(firstChange.getCargoId());
|
|
|
|
|
if (classId != null && request.getCargoCategory().contains(classId.toString())) {
|
|
|
|
|
filteredGroups.put(entry.getKey(), entry.getValue());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
groupedByProduct = filteredGroups;
|
|
|
|
|
} else {
|
|
|
|
|
// 没有商品ID,清空分组
|
|
|
|
|
groupedByProduct.clear();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 合计数据
|
|
|
|
|
BigDecimal totalLastCargoNum = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal totalLastCargoValue = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal totalInCargoNum = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal totalInCargoValue = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal totalOutCargoNum = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal totalOutCargoValue = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal totalCargoNum = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal totalCargoValue = BigDecimal.ZERO;
|
|
|
|
|
|
|
|
|
|
// 查询所有商品的单位信息(通过字典翻译)
|
|
|
|
|
Map<Long, String> cargoUnitMap = new HashMap<>();
|
|
|
|
|
List<Long> allCargoIds = groupedByProduct.values().stream()
|
|
|
|
|
.flatMap(list -> list.stream())
|
|
|
|
|
.map(CkStockChange::getCargoId)
|
|
|
|
|
.filter(Objects::nonNull)
|
|
|
|
|
.distinct()
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
if (!allCargoIds.isEmpty()) {
|
|
|
|
|
List<CmCustProduct> products = cmCustProductDao.selectList(new LambdaQueryWrapper<CmCustProduct>()
|
|
|
|
|
.in(CmCustProduct::getId, allCargoIds));
|
|
|
|
|
|
|
|
|
|
// 通过字典表 bm_measuring_unit 翻译单位
|
|
|
|
|
List<SysDictData> unitDictList = sysDictDataService.list(new LambdaQueryWrapper<SysDictData>()
|
|
|
|
|
.eq(SysDictData::getDictType, "bm_measuring_unit")
|
|
|
|
|
.eq(SysDictData::getStatus, "0"));
|
|
|
|
|
Map<String, String> unitTranslationMap = unitDictList.stream()
|
|
|
|
|
.collect(Collectors.toMap(SysDictData::getDictValue, SysDictData::getDictLabel, (a, b) -> b));
|
|
|
|
|
|
|
|
|
|
// 设置翻译后的单位
|
|
|
|
|
for (CmCustProduct product : products) {
|
|
|
|
|
String unitCode = product.getUnit();
|
|
|
|
|
String translatedUnit = unitTranslationMap.getOrDefault(unitCode, unitCode != null ? unitCode : "吨");
|
|
|
|
|
cargoUnitMap.put(product.getId(), translatedUnit);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 遍历每个商品组
|
|
|
|
|
int index = 1;
|
|
|
|
|
for (Map.Entry<String, List<CkStockChange>> entry : groupedByProduct.entrySet()) {
|
|
|
|
|
List<CkStockChange> productChanges = entry.getValue();
|
|
|
|
|
InventoryResponse response = new InventoryResponse();
|
|
|
|
|
|
|
|
|
|
// 获取商品基本信息(使用第一个记录的数据)
|
|
|
|
|
CkStockChange firstChange = productChanges.get(0);
|
|
|
|
|
response.setIndex(index++);
|
|
|
|
|
response.setCargoName(firstChange.getCargoName());
|
|
|
|
|
response.setCargoSpec(firstChange.getCargoSpec());
|
|
|
|
|
// 设置单位
|
|
|
|
|
response.setUnit(cargoUnitMap.getOrDefault(firstChange.getCargoId(), "吨"));
|
|
|
|
|
// 构建品名及规格
|
|
|
|
|
String cargoNameWithSpec = firstChange.getCargoName();
|
|
|
|
|
if (firstChange.getCargoSpec() != null && !firstChange.getCargoSpec().isEmpty()) {
|
|
|
|
|
cargoNameWithSpec += "\n" + firstChange.getCargoSpec();
|
|
|
|
|
}
|
|
|
|
|
response.setCargoNameWithSpec(cargoNameWithSpec);
|
|
|
|
|
|
|
|
|
|
// 上期结存变量
|
|
|
|
|
BigDecimal lastCargoNum = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal lastCargoValue = BigDecimal.ZERO;
|
|
|
|
|
// 本期入库变量
|
|
|
|
|
BigDecimal inCargoNum = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal inCargoValue = BigDecimal.ZERO;
|
|
|
|
|
// 本期出库变量
|
|
|
|
|
BigDecimal outCargoNum = BigDecimal.ZERO;
|
|
|
|
|
BigDecimal outCargoValue = BigDecimal.ZERO;
|
|
|
|
|
|
|
|
|
|
// 遍历变动记录,计算上期结存和本期入库出库
|
|
|
|
|
for (CkStockChange change : productChanges) {
|
|
|
|
|
if (request.getStartDate() != null) {
|
|
|
|
|
if (change.getChangeDate().before(request.getStartDate())) {
|
|
|
|
|
// 上期变动记录,用于计算上期结存
|
|
|
|
|
if ("1".equals(change.getChangeType())) {
|
|
|
|
|
// 上期入库
|
|
|
|
|
lastCargoNum = lastCargoNum.add(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO);
|
|
|
|
|
lastCargoValue = lastCargoValue.add(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO);
|
|
|
|
|
} else if ("2".equals(change.getChangeType())) {
|
|
|
|
|
// 上期出库
|
|
|
|
|
lastCargoNum = lastCargoNum.subtract(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO);
|
|
|
|
|
lastCargoValue = lastCargoValue.subtract(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO);
|
|
|
|
|
}
|
|
|
|
|
} else if (request.getEndDate() != null && !change.getChangeDate().after(request.getEndDate())) {
|
|
|
|
|
// 本期变动记录
|
|
|
|
|
if ("1".equals(change.getChangeType())) {
|
|
|
|
|
// 本期入库
|
|
|
|
|
inCargoNum = inCargoNum.add(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO);
|
|
|
|
|
inCargoValue = inCargoValue.add(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO);
|
|
|
|
|
} else if ("2".equals(change.getChangeType())) {
|
|
|
|
|
// 本期出库
|
|
|
|
|
outCargoNum = outCargoNum.add(change.getChangeWt() != null ? change.getChangeWt() : BigDecimal.ZERO);
|
|
|
|
|
outCargoValue = outCargoValue.add(change.getChangeValue() != null ? change.getChangeValue() : BigDecimal.ZERO);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 确保结存不为负数
|
|
|
|
|
if (lastCargoNum.compareTo(BigDecimal.ZERO) < 0) {
|
|
|
|
|
lastCargoNum = BigDecimal.ZERO;
|
|
|
|
|
}
|
|
|
|
|
if (lastCargoValue.compareTo(BigDecimal.ZERO) < 0) {
|
|
|
|
|
lastCargoValue = BigDecimal.ZERO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 设置响应数据
|
|
|
|
|
response.setLastCargoNum(lastCargoNum);
|
|
|
|
|
response.setLastCargoValue(lastCargoValue);
|
|
|
|
|
response.setInCargoNum(inCargoNum);
|
|
|
|
|
response.setInCargoValue(inCargoValue);
|
|
|
|
|
response.setOutCargoNum(outCargoNum);
|
|
|
|
|
response.setOutCargoValue(outCargoValue);
|
|
|
|
|
|
|
|
|
|
// 累计结存 = 上期结存 + 本期入库 - 本期出库
|
|
|
|
|
BigDecimal totalNum = lastCargoNum.add(inCargoNum).subtract(outCargoNum);
|
|
|
|
|
BigDecimal totalValue = lastCargoValue.add(inCargoValue).subtract(outCargoValue);
|
|
|
|
|
response.setCargoNum(totalNum);
|
|
|
|
|
response.setCargoValue(totalValue);
|
|
|
|
|
|
|
|
|
|
// 累加合计
|
|
|
|
|
totalLastCargoNum = totalLastCargoNum.add(lastCargoNum);
|
|
|
|
|
totalLastCargoValue = totalLastCargoValue.add(lastCargoValue);
|
|
|
|
|
totalInCargoNum = totalInCargoNum.add(inCargoNum);
|
|
|
|
|
totalInCargoValue = totalInCargoValue.add(inCargoValue);
|
|
|
|
|
totalOutCargoNum = totalOutCargoNum.add(outCargoNum);
|
|
|
|
|
totalOutCargoValue = totalOutCargoValue.add(outCargoValue);
|
|
|
|
|
totalCargoNum = totalCargoNum.add(totalNum);
|
|
|
|
|
totalCargoValue = totalCargoValue.add(totalValue);
|
|
|
|
|
|
|
|
|
|
result.add(response);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加合计行
|
|
|
|
|
if (!result.isEmpty()) {
|
|
|
|
|
InventoryResponse totalResponse = new InventoryResponse();
|
|
|
|
|
totalResponse.setCargoName("合计");
|
|
|
|
|
totalResponse.setCargoNameWithSpec("合计");
|
|
|
|
|
totalResponse.setLastCargoNum(totalLastCargoNum);
|
|
|
|
|
totalResponse.setLastCargoValue(totalLastCargoValue);
|
|
|
|
|
totalResponse.setInCargoNum(totalInCargoNum);
|
|
|
|
|
totalResponse.setInCargoValue(totalInCargoValue);
|
|
|
|
|
totalResponse.setOutCargoNum(totalOutCargoNum);
|
|
|
|
|
totalResponse.setOutCargoValue(totalOutCargoValue);
|
|
|
|
|
totalResponse.setCargoNum(totalCargoNum);
|
|
|
|
|
totalResponse.setCargoValue(totalCargoValue);
|
|
|
|
|
result.add(totalResponse);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 标题样式:加粗、居中、14号
|
|
|
|
|
private CellStyle getTitleStyle(Workbook wb) {
|
|
|
|
|
CellStyle style = wb.createCellStyle();
|
|
|
|
|
Font font = wb.createFont();
|
|
|
|
|
font.setFontName("微软雅黑");
|
|
|
|
|
font.setFontHeightInPoints((short) 14);
|
|
|
|
|
font.setBold(true);
|
|
|
|
|
style.setFont(font);
|
|
|
|
|
style.setAlignment(HorizontalAlignment.CENTER);
|
|
|
|
|
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 表头样式:加粗、居中、11号
|
|
|
|
|
private CellStyle getHeaderStyle(Workbook wb) {
|
|
|
|
|
CellStyle style = wb.createCellStyle();
|
|
|
|
|
Font font = wb.createFont();
|
|
|
|
|
font.setFontName("微软雅黑");
|
|
|
|
|
font.setFontHeightInPoints((short) 11);
|
|
|
|
|
font.setBold(true);
|
|
|
|
|
style.setFont(font);
|
|
|
|
|
style.setAlignment(HorizontalAlignment.CENTER);
|
|
|
|
|
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
|
|
style.setBorderTop(BorderStyle.THIN);
|
|
|
|
|
style.setBorderBottom(BorderStyle.THIN);
|
|
|
|
|
style.setBorderLeft(BorderStyle.THIN);
|
|
|
|
|
style.setBorderRight(BorderStyle.THIN);
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 数据样式:居中、细边框
|
|
|
|
|
private CellStyle getDataStyle(Workbook wb) {
|
|
|
|
|
CellStyle style = wb.createCellStyle();
|
|
|
|
|
Font font = wb.createFont();
|
|
|
|
|
font.setFontName("微软雅黑");
|
|
|
|
|
font.setFontHeightInPoints((short) 10);
|
|
|
|
|
style.setFont(font);
|
|
|
|
|
style.setAlignment(HorizontalAlignment.CENTER);
|
|
|
|
|
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
|
|
style.setBorderTop(BorderStyle.THIN);
|
|
|
|
|
style.setBorderBottom(BorderStyle.THIN);
|
|
|
|
|
style.setBorderLeft(BorderStyle.THIN);
|
|
|
|
|
style.setBorderRight(BorderStyle.THIN);
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 合计金额样式:右对齐、加粗、细边框
|
|
|
|
|
private CellStyle getTotalValueStyle(Workbook wb) {
|
|
|
|
|
CellStyle style = wb.createCellStyle();
|
|
|
|
|
Font font = wb.createFont();
|
|
|
|
|
font.setFontName("微软雅黑");
|
|
|
|
|
font.setFontHeightInPoints((short) 11);
|
|
|
|
|
font.setBold(true);
|
|
|
|
|
style.setFont(font);
|
|
|
|
|
style.setAlignment(HorizontalAlignment.RIGHT);
|
|
|
|
|
style.setVerticalAlignment(VerticalAlignment.CENTER);
|
|
|
|
|
style.setBorderTop(BorderStyle.THIN);
|
|
|
|
|
style.setBorderBottom(BorderStyle.THIN);
|
|
|
|
|
style.setBorderLeft(BorderStyle.THIN);
|
|
|
|
|
style.setBorderRight(BorderStyle.THIN);
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 底部签字样式
|
|
|
|
|
private CellStyle getSignStyle(Workbook wb) {
|
|
|
|
|
CellStyle style = wb.createCellStyle();
|
|
|
|
|
Font font = wb.createFont();
|
|
|
|
|
font.setFontName("微软雅黑");
|
|
|
|
|
font.setFontHeightInPoints((short) 10);
|
|
|
|
|
style.setFont(font);
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void exportInventory(InventoryRequest request) {
|
|
|
|
|
// 获取盘点数据
|
|
|
|
|
List<InventoryResponse> inventoryList = getInventoryList(request);
|
|
|
|
|
|
|
|
|
|
// 获取响应对象
|
|
|
|
|
HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
|
|
|
|
|
if (response == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
// 1. 创建工作簿
|
|
|
|
|
HSSFWorkbook wb = new HSSFWorkbook();
|
|
|
|
|
HSSFSheet sheet = wb.createSheet("物资盘点表");
|
|
|
|
|
|
|
|
|
|
// 2. 行高设置
|
|
|
|
|
sheet.createRow(0).setHeight((short) 600); // 标题
|
|
|
|
|
sheet.createRow(1).setHeight((short) 350); // 类别/供应商/日期
|
|
|
|
|
sheet.createRow(2).setHeight((short) 380); // 表头1
|
|
|
|
|
sheet.createRow(3).setHeight((short) 380); // 表头2
|
|
|
|
|
|
|
|
|
|
// ====================== 第1行:标题 ======================
|
|
|
|
|
Row titleRow = sheet.getRow(0);
|
|
|
|
|
Cell titleCell = titleRow.createCell(0);
|
|
|
|
|
titleCell.setCellValue("物资盘点表");
|
|
|
|
|
titleCell.setCellStyle(getTitleStyle(wb));
|
|
|
|
|
// 合并 A1:N1
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, 13));
|
|
|
|
|
|
|
|
|
|
// ====================== 第2行:类别、供应商、日期 ======================
|
|
|
|
|
Row infoRow = sheet.getRow(1);
|
|
|
|
|
infoRow.createCell(0).setCellValue("类别:所有类别");
|
|
|
|
|
// 根据custId查询供应商名称
|
|
|
|
|
String supplierName = "";
|
|
|
|
|
if (request.getCustId() != null) {
|
|
|
|
|
CmCust cmCust = cmCustDao.selectById(request.getCustId());
|
|
|
|
|
if (cmCust != null) {
|
|
|
|
|
supplierName = cmCust.getCustName();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
infoRow.createCell(6).setCellValue("供应商:" + supplierName);
|
|
|
|
|
// 使用传参中的结束日期
|
|
|
|
|
String endDateStr = "";
|
|
|
|
|
if (request.getEndDate() != null) {
|
|
|
|
|
endDateStr = new java.text.SimpleDateFormat("yyyy-MM-dd").format(request.getEndDate());
|
|
|
|
|
}
|
|
|
|
|
infoRow.createCell(11).setCellValue("日期:" + endDateStr);
|
|
|
|
|
|
|
|
|
|
// ====================== 第3-4行:双行表头 ======================
|
|
|
|
|
Row headerRow1 = sheet.getRow(2);
|
|
|
|
|
Row headerRow2 = sheet.getRow(3);
|
|
|
|
|
CellStyle hs = getHeaderStyle(wb);
|
|
|
|
|
|
|
|
|
|
// 双行表头结构(严格对齐你的表)
|
|
|
|
|
String[] h1 = {
|
|
|
|
|
"序号", "品名及规格", "上期结存", "", "", "本期入库", "", "", "本期出库", "", "", "累计结存", "", ""
|
|
|
|
|
};
|
|
|
|
|
String[] h2 = {
|
|
|
|
|
"", "", "单位", "数量", "金额", "单位", "数量", "金额", "单位", "数量", "金额", "单位", "数量", "金额"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < h1.length; i++) {
|
|
|
|
|
Cell c1 = headerRow1.createCell(i);
|
|
|
|
|
Cell c2 = headerRow2.createCell(i);
|
|
|
|
|
c1.setCellValue(h1[i]);
|
|
|
|
|
c2.setCellValue(h2[i]);
|
|
|
|
|
c1.setCellStyle(hs);
|
|
|
|
|
c2.setCellStyle(hs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 合并表头(上期/入库/出库/结存)
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(2, 2, 2, 4)); // 上期
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(2, 2, 5, 7)); // 入库
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(2, 2, 8, 10)); // 出库
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(2, 2, 11, 13)); // 结存
|
|
|
|
|
|
|
|
|
|
// ====================== 数据行 ======================
|
|
|
|
|
CellStyle ds = getDataStyle(wb);
|
|
|
|
|
int dataRowIndex = 4;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < inventoryList.size(); i++) {
|
|
|
|
|
InventoryResponse item = inventoryList.get(i);
|
|
|
|
|
Row dataRow = sheet.createRow(dataRowIndex);
|
|
|
|
|
dataRow.setHeight((short) 300);
|
|
|
|
|
|
|
|
|
|
// 判断是否为合计行(最后一行)
|
|
|
|
|
boolean isTotalRow = (i == inventoryList.size() - 1) && "合计".equals(item.getCargoNameWithSpec());
|
|
|
|
|
CellStyle currentStyle = isTotalRow ? hs : ds;
|
|
|
|
|
|
|
|
|
|
if (!isTotalRow) {
|
|
|
|
|
// 数据行:显示完整内容
|
|
|
|
|
// 序号
|
|
|
|
|
Cell indexCell = dataRow.createCell(0);
|
|
|
|
|
indexCell.setCellValue(item.getIndex() != null ? item.getIndex() : 0);
|
|
|
|
|
indexCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
// 品名及规格
|
|
|
|
|
Cell nameCell = dataRow.createCell(1);
|
|
|
|
|
nameCell.setCellValue(item.getCargoNameWithSpec() != null ? item.getCargoNameWithSpec() : "");
|
|
|
|
|
nameCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
// 上期结存
|
|
|
|
|
String unit = item.getUnit() != null ? item.getUnit() : "吨";
|
|
|
|
|
Cell lastUnitCell = dataRow.createCell(2);
|
|
|
|
|
lastUnitCell.setCellValue(unit);
|
|
|
|
|
lastUnitCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
Cell lastNumCell = dataRow.createCell(3);
|
|
|
|
|
BigDecimal lastNum = item.getLastCargoNum() != null ? item.getLastCargoNum() : BigDecimal.ZERO;
|
|
|
|
|
lastNumCell.setCellValue(lastNum.doubleValue());
|
|
|
|
|
lastNumCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
Cell lastValueCell = dataRow.createCell(4);
|
|
|
|
|
BigDecimal lastValue = item.getLastCargoValue() != null ? item.getLastCargoValue() : BigDecimal.ZERO;
|
|
|
|
|
lastValueCell.setCellValue(lastValue.doubleValue());
|
|
|
|
|
lastValueCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
// 本期入库
|
|
|
|
|
Cell inUnitCell = dataRow.createCell(5);
|
|
|
|
|
inUnitCell.setCellValue(unit);
|
|
|
|
|
inUnitCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
Cell inNumCell = dataRow.createCell(6);
|
|
|
|
|
BigDecimal inNum = item.getInCargoNum() != null ? item.getInCargoNum() : BigDecimal.ZERO;
|
|
|
|
|
inNumCell.setCellValue(inNum.doubleValue());
|
|
|
|
|
inNumCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
Cell inValueCell = dataRow.createCell(7);
|
|
|
|
|
BigDecimal inValue = item.getInCargoValue() != null ? item.getInCargoValue() : BigDecimal.ZERO;
|
|
|
|
|
inValueCell.setCellValue(inValue.doubleValue());
|
|
|
|
|
inValueCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
// 本期出库
|
|
|
|
|
Cell outUnitCell = dataRow.createCell(8);
|
|
|
|
|
outUnitCell.setCellValue(unit);
|
|
|
|
|
outUnitCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
Cell outNumCell = dataRow.createCell(9);
|
|
|
|
|
BigDecimal outNum = item.getOutCargoNum() != null ? item.getOutCargoNum() : BigDecimal.ZERO;
|
|
|
|
|
outNumCell.setCellValue(outNum.doubleValue());
|
|
|
|
|
outNumCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
Cell outValueCell = dataRow.createCell(10);
|
|
|
|
|
BigDecimal outValue = item.getOutCargoValue() != null ? item.getOutCargoValue() : BigDecimal.ZERO;
|
|
|
|
|
outValueCell.setCellValue(outValue.doubleValue());
|
|
|
|
|
outValueCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
// 累计结存
|
|
|
|
|
Cell cargoUnitCell = dataRow.createCell(11);
|
|
|
|
|
cargoUnitCell.setCellValue(unit);
|
|
|
|
|
cargoUnitCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
Cell cargoNumCell = dataRow.createCell(12);
|
|
|
|
|
BigDecimal cargoNum = item.getCargoNum() != null ? item.getCargoNum() : BigDecimal.ZERO;
|
|
|
|
|
cargoNumCell.setCellValue(cargoNum.doubleValue());
|
|
|
|
|
cargoNumCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
Cell cargoValueCell = dataRow.createCell(13);
|
|
|
|
|
BigDecimal cargoValue = item.getCargoValue() != null ? item.getCargoValue() : BigDecimal.ZERO;
|
|
|
|
|
cargoValueCell.setCellValue(cargoValue.doubleValue());
|
|
|
|
|
cargoValueCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
dataRowIndex++;
|
|
|
|
|
} else {
|
|
|
|
|
// 合计行:每个大列内部合并单位、数量和金额列,只显示金额(右对齐)
|
|
|
|
|
CellStyle totalValueStyle = getTotalValueStyle(wb);
|
|
|
|
|
|
|
|
|
|
// 序号(空)
|
|
|
|
|
Cell indexCell = dataRow.createCell(0);
|
|
|
|
|
indexCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
// 品名及规格:显示"合计"
|
|
|
|
|
Cell nameCell = dataRow.createCell(1);
|
|
|
|
|
nameCell.setCellValue("合计");
|
|
|
|
|
nameCell.setCellStyle(currentStyle);
|
|
|
|
|
|
|
|
|
|
// 上期结存:合并单位(2)、数量(3)和金额(4)列,只显示金额(右对齐)
|
|
|
|
|
for (int col = 2; col <= 4; col++) {
|
|
|
|
|
Cell cell = dataRow.createCell(col);
|
|
|
|
|
cell.setCellStyle(totalValueStyle);
|
|
|
|
|
}
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 2, 4));
|
|
|
|
|
Cell lastValueCell = dataRow.getCell(2);
|
|
|
|
|
BigDecimal lastValue = item.getLastCargoValue() != null ? item.getLastCargoValue() : BigDecimal.ZERO;
|
|
|
|
|
lastValueCell.setCellValue(lastValue.doubleValue());
|
|
|
|
|
|
|
|
|
|
// 本期入库:合并单位(5)、数量(6)和金额(7)列,只显示金额(右对齐)
|
|
|
|
|
for (int col = 5; col <= 7; col++) {
|
|
|
|
|
Cell cell = dataRow.createCell(col);
|
|
|
|
|
cell.setCellStyle(totalValueStyle);
|
|
|
|
|
}
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 5, 7));
|
|
|
|
|
Cell inValueCell = dataRow.getCell(5);
|
|
|
|
|
BigDecimal inValue = item.getInCargoValue() != null ? item.getInCargoValue() : BigDecimal.ZERO;
|
|
|
|
|
inValueCell.setCellValue(inValue.doubleValue());
|
|
|
|
|
|
|
|
|
|
// 本期出库:合并单位(8)、数量(9)和金额(10)列,只显示金额(右对齐)
|
|
|
|
|
for (int col = 8; col <= 10; col++) {
|
|
|
|
|
Cell cell = dataRow.createCell(col);
|
|
|
|
|
cell.setCellStyle(totalValueStyle);
|
|
|
|
|
}
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 8, 10));
|
|
|
|
|
Cell outValueCell = dataRow.getCell(8);
|
|
|
|
|
BigDecimal outValue = item.getOutCargoValue() != null ? item.getOutCargoValue() : BigDecimal.ZERO;
|
|
|
|
|
outValueCell.setCellValue(outValue.doubleValue());
|
|
|
|
|
|
|
|
|
|
// 累计结存:合并单位(11)、数量(12)和金额(13)列,只显示金额(右对齐)
|
|
|
|
|
for (int col = 11; col <= 13; col++) {
|
|
|
|
|
Cell cell = dataRow.createCell(col);
|
|
|
|
|
cell.setCellStyle(totalValueStyle);
|
|
|
|
|
}
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex, dataRowIndex, 11, 13));
|
|
|
|
|
Cell cargoValueCell = dataRow.getCell(11);
|
|
|
|
|
BigDecimal cargoValue = item.getCargoValue() != null ? item.getCargoValue() : BigDecimal.ZERO;
|
|
|
|
|
cargoValueCell.setCellValue(cargoValue.doubleValue());
|
|
|
|
|
|
|
|
|
|
dataRowIndex++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ====================== 底部签字 ======================
|
|
|
|
|
Row signRow = sheet.createRow(dataRowIndex++);
|
|
|
|
|
signRow.setHeight((short) 300);
|
|
|
|
|
CellStyle ss = getSignStyle(wb);
|
|
|
|
|
|
|
|
|
|
// 审核人:合并列0-4
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex - 1, dataRowIndex - 1, 0, 4));
|
|
|
|
|
Cell auditorCell = signRow.createCell(0);
|
|
|
|
|
auditorCell.setCellValue("审核人:");
|
|
|
|
|
auditorCell.setCellStyle(ss);
|
|
|
|
|
|
|
|
|
|
// 仓管员:合并列5-9
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex - 1, dataRowIndex - 1, 5, 9));
|
|
|
|
|
Cell warehouseCell = signRow.createCell(5);
|
|
|
|
|
warehouseCell.setCellValue("仓管员:");
|
|
|
|
|
warehouseCell.setCellStyle(ss);
|
|
|
|
|
|
|
|
|
|
// 领料人:合并列10-13
|
|
|
|
|
sheet.addMergedRegion(new CellRangeAddress(dataRowIndex - 1, dataRowIndex - 1, 10, 13));
|
|
|
|
|
Cell requesterCell = signRow.createCell(10);
|
|
|
|
|
requesterCell.setCellValue("领料人:");
|
|
|
|
|
requesterCell.setCellStyle(ss);
|
|
|
|
|
|
|
|
|
|
// 设置所有单元格样式
|
|
|
|
|
for (int i = 0; i < 14; i++) {
|
|
|
|
|
Cell c = signRow.getCell(i);
|
|
|
|
|
if (c == null) {
|
|
|
|
|
c = signRow.createCell(i);
|
|
|
|
|
}
|
|
|
|
|
c.setCellStyle(ss);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ====================== 列宽 ======================
|
|
|
|
|
int[] widths = {6, 24, 6, 10, 10, 6, 10, 10, 6, 10, 10, 6, 10, 10};
|
|
|
|
|
for (int i = 0; i < widths.length; i++) {
|
|
|
|
|
sheet.setColumnWidth(i, widths[i] * 256);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ====================== 输出 ======================
|
|
|
|
|
response.setContentType("application/vnd.ms-excel");
|
|
|
|
|
String fileName = URLEncoder.encode("物资盘点表", "UTF-8").replaceAll("\\+", "%");
|
|
|
|
|
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + fileName + ".xls");
|
|
|
|
|
wb.write(response.getOutputStream());
|
|
|
|
|
wb.close();
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|