Merge branch 'dev' into dev_v3.0.5

This commit is contained in:
王奎兴
2026-07-02 19:07:55 +08:00
20 changed files with 520 additions and 452 deletions
@@ -34,6 +34,9 @@ public class ReservationDeliveryDetailsLink extends BaseVOEntity {
@ApiModelProperty(value = "客户编号") @ApiModelProperty(value = "客户编号")
private String customerNumber; private String customerNumber;
@ApiModelProperty(value = "客户id")
private Long customerId;
@ApiModelProperty(value = "配送名称") @ApiModelProperty(value = "配送名称")
private String deliveryName; private String deliveryName;
@@ -42,7 +42,8 @@ public class ReservationDeliveryDetailsLinkPO extends BaseVOEntity {
@ApiModelProperty(value = "客户编号") @ApiModelProperty(value = "客户编号")
private String customerNumber; private String customerNumber;
@ApiModelProperty(value = "客户id")
private Long customerId;
@ApiModelProperty(value = "配送名称") @ApiModelProperty(value = "配送名称")
private String deliveryName; private String deliveryName;
@@ -41,7 +41,8 @@ public class ReservationDeliveryDetailsLinkDO extends BaseVOEntity {
@ApiModelProperty(value = "客户编号") @ApiModelProperty(value = "客户编号")
private String customerNumber; private String customerNumber;
@ApiModelProperty(value = "客户id")
private Long customerId;
@ApiModelProperty(value = "配送名称") @ApiModelProperty(value = "配送名称")
private String deliveryName; private String deliveryName;
@@ -63,6 +63,10 @@ public class ReservationInMaterialDetail extends BaseVOEntity {
@Excel(name = "物料名称") @Excel(name = "物料名称")
private String materialName; private String materialName;
@ApiModelProperty("物料名称")
@Excel(name = "物料名称")
private String reserveMaterialName;
@ApiModelProperty("物料条形码") @ApiModelProperty("物料条形码")
@Excel(name = "物料条形码") @Excel(name = "物料条形码")
private String barCode; private String barCode;
@@ -62,6 +62,10 @@ public class ReservationInMaterialDetailPO extends ReservationStockInOrderBasePO
@Excel(name = "物料名称") @Excel(name = "物料名称")
private String materialName; private String materialName;
@ApiModelProperty("物料名称")
@Excel(name = "物料名称")
private String reserveMaterialName;
@ApiModelProperty("物料条形码") @ApiModelProperty("物料条形码")
@Excel(name = "物料条形码") @Excel(name = "物料条形码")
private String barCode; private String barCode;
@@ -60,6 +60,10 @@ public class ReservationInMaterialDetailDO extends ReservationStockInOrderBaseDO
@Excel(name = "物料名称") @Excel(name = "物料名称")
private String materialName; private String materialName;
@ApiModelProperty("物料名称")
@Excel(name = "物料名称")
private String reserveMaterialName;
@ApiModelProperty("物料条形码") @ApiModelProperty("物料条形码")
@Excel(name = "物料条形码") @Excel(name = "物料条形码")
private String barCode; private String barCode;
@@ -63,6 +63,10 @@ public class ReservationInMaterialDetailDTO extends BaseVOEntity {
@Excel(name = "物料名称") @Excel(name = "物料名称")
private String materialName; private String materialName;
@ApiModelProperty("物料名称")
@Excel(name = "物料名称")
private String reserveMaterialName;
@ApiModelProperty("物料条形码") @ApiModelProperty("物料条形码")
@Excel(name = "物料条形码") @Excel(name = "物料条形码")
private String barCode; private String barCode;
@@ -17,14 +17,13 @@ import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/** /**
* 维他奶出库Excel解析监听器(最终完整版) * 维他奶出库Excel解析监听器(新模板版)
* 新模板特点:货品明细与客户配送明细左右分栏展示
* 核心功能: * 核心功能:
* 1. 正确解析多单元格数据(解决发票张数/纪录合共读取错误) * 1. 左侧栏:第1-3行解析表头(送货日期/送货线号/车次),第5行解析纪录合共,第7行起解析货品明细
* 2. 支持动态行数的货品明细/客户配送明细解析 * 2. 右侧栏:第2行解析发票张数/小计,第4行起解析客户配送明细
* 3. 自动处理文本型数字转换(String→Integer/BigDecimal * 3. 自动处理文本型数字转换(String→Integer/BigDecimal
* 4. 完整的异常处理和日志记录 * 4. 完整的异常处理和日志记录
*/ */
@@ -32,83 +31,101 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
// 日志实例 // 日志实例
private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDeliveryExcelListener.class); private static final Logger LOGGER = LoggerFactory.getLogger(DynamicDeliveryExcelListener.class);
// ---------------------- 常量定义(便于维护) ----------------------
// 正则表达式:匹配整数(支持正负数)
private static final Pattern INTEGER_PATTERN = Pattern.compile("-?\\d+");
// 正则表达式:匹配小数(支持正负数和整数形式)
private static final Pattern DECIMAL_PATTERN = Pattern.compile("-?\\d+(\\.\\d+)?");
// 关键字定义(Excel标签匹配) // 关键字定义(Excel标签匹配)
private static final String KEYWORD_DELIVERY_DATE = "送貨日期";
private static final String KEYWORD_PRODUCT_DETAIL = "货品明细";
private static final String KEYWORD_TOTAL_RECORD = "紀錄合共"; private static final String KEYWORD_TOTAL_RECORD = "紀錄合共";
private static final String KEYWORD_CUSTOMER_DETAIL = "客户配送明细";
private static final String KEYWORD_INVOICE_COUNT = "發票張數"; private static final String KEYWORD_INVOICE_COUNT = "發票張數";
private static final String KEYWORD_TOTAL_COUNT = "總計";
private static final String KEYWORD_SUBTOTAL = "小計"; private static final String KEYWORD_SUBTOTAL = "小計";
// 支持的日期格式(String转Date) // 支持的日期格式(String转Date)
private static final String[] SUPPORTED_DATE_FORMATS = {"yyyy-MM-dd", "yyyy/MM/dd", "yyyyMMdd", "yyyy-MM-dd HH:mm:ss"}; private static final String[] SUPPORTED_DATE_FORMATS = {"yyyy-MM-dd", "yyyy/MM/dd", "yyyyMMdd", "yyyy-MM-dd HH:mm:ss"};
// ---------------------- 数据存储(解析结果) ---------------------- // ---------------------- 数据存储(解析结果) ----------------------
private DeliveryHeader deliveryHeader = new DeliveryHeader(); private final DeliveryHeader deliveryHeader = new DeliveryHeader();
private List<ProductDetail> productDetailList = new ArrayList<>(); private final List<ProductDetail> productDetailList = new ArrayList<>();
private List<CustomerDeliveryDetail> customerDeliveryDetailList = new ArrayList<>(); private final List<CustomerDeliveryDetail> customerDeliveryDetailList = new ArrayList<>();
// ---------------------- 解析状态管理(动态切换) ---------------------- // 当前行号(1-based,与Excel行号一致)
public enum ParseStatus { private int currentRowNum = 0;
IDLE, // 空闲状态(未进入任何表格)
PRODUCT_DETAIL,// 货品明细解析状态
CUSTOMER_DETAIL// 客户配送明细解析状态
}
private ParseStatus currentStatus = ParseStatus.IDLE;
// ---------------------- 核心解析方法(EasyExcel回调) ---------------------- // ---------------------- 核心解析方法(EasyExcel回调) ----------------------
@Override @Override
public void invoke(Map<String, Object> data, AnalysisContext context) { public void invoke(Map<String, Object> data, AnalysisContext context) {
currentRowNum = context.readRowHolder().getRowIndex() + 1;
try { try {
int rowNum = context.readRowHolder().getRowIndex() + 1; LOGGER.debug("第{}行:开始解析,所有单元格数据:{}", currentRowNum, data);
LOGGER.debug("第{}行:开始解析,所有单元格数据:{}", rowNum, data);
// 1. 获取第一列数据(用于状态判断,标签通常在第一列) // 左侧区域解析(A-E列)
String firstCellValue = getFirstCellValue(data); parseLeftSection(data);
LOGGER.debug("第{}行:第一列数据(状态判断依据):{}", rowNum, firstCellValue);
// 2. 状态切换判断(传递完整data,支持多单元格解析 // 右侧区域解析(F-M列
if (isStatusSwitchTrigger(firstCellValue)) { parseRightSection(data);
switchStatus(firstCellValue, rowNum, data);
return;
}
// 3. 根据当前状态解析对应表格数据
switch (currentStatus) {
case PRODUCT_DETAIL:
if (isValidProductDetailRow(data, firstCellValue)) {
parseProductDetail(data, rowNum);
}
break;
case CUSTOMER_DETAIL:
if (isValidCustomerDetailRow(data, firstCellValue)) {
parseCustomerDeliveryDetail(data, rowNum);
}
break;
case IDLE:
// 空闲状态:解析表头信息(送货日期行,多单元格数据)
if (firstCellValue.contains(KEYWORD_DELIVERY_DATE)) {
parseHeaderInfo(data, rowNum);
}
break;
}
} catch (ExcelDataConvertException e) { } catch (ExcelDataConvertException e) {
// Excel内置转换异常(如日期格式错误)
int rowNum = context.readRowHolder().getRowIndex() + 1;
LOGGER.error("第{}行:Excel数据转换异常,列号:{},列名:{}", LOGGER.error("第{}行:Excel数据转换异常,列号:{},列名:{}",
rowNum, e.getColumnIndex() + 1, getColumnNameByIndex(data, e.getColumnIndex()), e); currentRowNum, e.getColumnIndex() + 1, getColumnNameByIndex(data, e.getColumnIndex()), e);
throw new RuntimeException("Excel数据转换异常(行:" + rowNum + ",列:" + (e.getColumnIndex() + 1) + "", e); throw new RuntimeException("Excel数据转换异常(行:" + currentRowNum + ",列:" + (e.getColumnIndex() + 1) + "", e);
} catch (Exception e) { } catch (Exception e) {
// 其他解析异常 LOGGER.error("第{}行:Excel解析异常,所有单元格数据:{}", currentRowNum, data, e);
int rowNum = context.readRowHolder().getRowIndex() + 1; throw new RuntimeException("Excel解析异常(行:" + currentRowNum + "", e);
LOGGER.error("第{}行:Excel解析异常,所有单元格数据:{}", rowNum, data, e); }
throw new RuntimeException("Excel解析异常(行:" + rowNum + "", e); }
// ---------------------- 左侧区域解析(货品相关信息) ----------------------
private void parseLeftSection(Map<String, Object> data) {
switch (currentRowNum) {
case 1:
// 第1行:送货日期(A列=标签,B列=值)
parseDeliveryDate(data);
break;
case 2:
// 第2行:送货线号(A列=标签,B列=值)
parseDeliveryLineNo(data);
break;
case 3:
// 第3行:车次(A列=标签,B列=值)
parseTrainNumber(data);
break;
case 4:
// 第4行:货品明细标题行,无需解析
LOGGER.debug("第{}行:货品明细标题行,跳过", currentRowNum);
break;
case 5:
// 第5行:纪录合共(A列=标签,B列=总记录数,D列=总箱数,E列=总支数)
parseTotalRecord(data);
break;
case 6:
// 第6行:货品明细列标题(貨品編號/簡稱/箱/支),跳过
LOGGER.debug("第{}行:货品明细列标题行,跳过", currentRowNum);
break;
default:
// 第7行起:货品明细数据行(遇到空行自动结束)
if (currentRowNum >= 7 && isValidProductDetailRow(data)) {
parseProductDetail(data, currentRowNum);
}
break;
}
}
// ---------------------- 右侧区域解析(客户配送相关信息) ----------------------
private void parseRightSection(Map<String, Object> data) {
switch (currentRowNum) {
case 1:
// 第1行:客户配送明细标题行,无需解析
LOGGER.debug("第{}行:客户配送明细标题行,跳过", currentRowNum);
break;
case 2:
// 第2行:发票张数/小计(F列=發票張數标签,G列=数值;J列=小計标签,K/L/M列=数值)
parseInvoiceCount(data);
break;
case 3:
// 第3行:客户配送明细列标题(類别/發票編號/客户編號/名稱/箱/包/金額),跳过
LOGGER.debug("第{}行:客户配送明细列标题行,跳过", currentRowNum);
break;
default:
// 第4行起:客户配送明细数据行(遇到空行自动结束)
if (currentRowNum >= 4 && isValidCustomerDetailRow(data)) {
parseCustomerDeliveryDetail(data, currentRowNum);
}
break;
} }
} }
@@ -116,8 +133,10 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
@Override @Override
public void doAfterAllAnalysed(AnalysisContext context) { public void doAfterAllAnalysed(AnalysisContext context) {
LOGGER.info("Excel解析全部完成!最终统计结果:"); LOGGER.info("Excel解析全部完成!最终统计结果:");
LOGGER.info("1. 表头信息:送货日期={},发票张数={},总记录数={},小计金额={}", LOGGER.info("1. 表头信息:送货日期={}送货线号={},车次={}发票张数={},总记录数={},小计金额={}",
deliveryHeader.getDeliveryDate(), deliveryHeader.getDeliveryDate(),
deliveryHeader.getDeliveryLineNo(),
deliveryHeader.getTrainNumber(),
deliveryHeader.getInvoiceCount(), deliveryHeader.getInvoiceCount(),
deliveryHeader.getTotalRecordCount(), deliveryHeader.getTotalRecordCount(),
deliveryHeader.getSubtotalAmount()); deliveryHeader.getSubtotalAmount());
@@ -125,253 +144,185 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
LOGGER.info("3. 客户配送明细:共{}条记录", customerDeliveryDetailList.size()); LOGGER.info("3. 客户配送明细:共{}条记录", customerDeliveryDetailList.size());
} }
// ---------------------- 状态管理方法(传递完整data ---------------------- // ---------------------- 左侧:表头信息解析 ----------------------
/** private void parseDeliveryDate(Map<String, Object> data) {
* 判断是否触发状态切换(根据第一列标签关键字) Object value = getValueSafely(data, 1); // B列
*/ if (value != null) {
private boolean isStatusSwitchTrigger(String firstCellValue) { Date deliveryDate = convertToDate(value);
return firstCellValue.contains(KEYWORD_PRODUCT_DETAIL) deliveryHeader.setDeliveryDate(deliveryDate);
|| firstCellValue.contains(KEYWORD_TOTAL_RECORD) LOGGER.debug("第{}行:送货日期解析完成,值={}", currentRowNum, deliveryDate);
|| firstCellValue.contains(KEYWORD_CUSTOMER_DETAIL)
|| firstCellValue.contains(KEYWORD_INVOICE_COUNT);
}
/**
* 切换解析状态(核心:传递完整data,支持多单元格解析)
*/
private void switchStatus(String firstCellValue, int rowNum, Map<String, Object> data) {
if (firstCellValue.contains(KEYWORD_PRODUCT_DETAIL)) {
currentStatus = ParseStatus.PRODUCT_DETAIL;
LOGGER.info("第{}行:检测到【{}】标签,切换到【货品明细】解析状态", rowNum, KEYWORD_PRODUCT_DETAIL);
} else if (firstCellValue.contains(KEYWORD_TOTAL_RECORD)) {
currentStatus = ParseStatus.IDLE;
parseTotalRecord(data, rowNum); // 解析纪录合共(多单元格)
LOGGER.info("第{}行:检测到【{}】标签,切换到【空闲】状态,已解析货品明细{}条",
rowNum, KEYWORD_TOTAL_RECORD, productDetailList.size());
} else if (firstCellValue.contains(KEYWORD_CUSTOMER_DETAIL)) {
currentStatus = ParseStatus.CUSTOMER_DETAIL;
LOGGER.info("第{}行:检测到【{}】标签,切换到【客户配送明细】解析状态", rowNum, KEYWORD_CUSTOMER_DETAIL);
} else if (firstCellValue.contains(KEYWORD_INVOICE_COUNT)) {
currentStatus = ParseStatus.IDLE;
parseInvoiceCount(data, rowNum); // 解析发票张数(多单元格,核心修复)
LOGGER.info("第{}行:检测到【{}】标签,切换到【空闲】状态,已解析客户配送明细{}条",
rowNum, KEYWORD_INVOICE_COUNT, customerDeliveryDetailList.size());
} }
} }
// ---------------------- 数据有效性校验方法 ---------------------- private void parseDeliveryLineNo(Map<String, Object> data) {
Object value = getValueSafely(data, 1); // B列
if (value != null) {
String deliveryLineNo = value.toString().trim();
deliveryHeader.setDeliveryLineNo(deliveryLineNo);
LOGGER.debug("第{}行:送货线号解析完成,值={}", currentRowNum, deliveryLineNo);
}
}
private void parseTrainNumber(Map<String, Object> data) {
Object value = getValueSafely(data, 1); // B列
Integer trainNumber = convertToInteger(value);
deliveryHeader.setTrainNumber(trainNumber);
LOGGER.debug("第{}行:车次解析完成,值={}", currentRowNum, trainNumber);
}
// ---------------------- 左侧:纪录合共解析 ----------------------
/**
* 第5行:纪录合共
* 列对应关系:0=A列=紀錄合共标签,1=B列=总记录数,3=D列=总箱数,4=E列=总支数
*/
private void parseTotalRecord(Map<String, Object> data) {
Object totalRecordLabel = getValueSafely(data, 0);
if (totalRecordLabel == null || !totalRecordLabel.toString().trim().contains(KEYWORD_TOTAL_RECORD)) {
LOGGER.warn("第{}行:不是纪录合共行,跳过解析,第一列内容:{}", currentRowNum, totalRecordLabel);
return;
}
Integer totalRecordCount = convertToInteger(getValueSafely(data, 1)); // B列
Integer totalBoxCount = convertToInteger(getValueSafely(data, 3)); // D列
Integer totalPieceCount = convertToInteger(getValueSafely(data, 4)); // E列
deliveryHeader.setTotalRecordCount(totalRecordCount);
deliveryHeader.setTotalBoxCount(totalBoxCount);
deliveryHeader.setTotalPieceCount(totalPieceCount);
LOGGER.debug("第{}行:解析纪录合共完成,总记录数={},总箱数={},总支数={}",
currentRowNum, totalRecordCount, totalBoxCount, totalPieceCount);
}
// ---------------------- 左侧:货品明细解析 ----------------------
/** /**
* 校验货品明细行是否有效(排除表头和空行) * 校验货品明细行是否有效(排除表头和空行)
*/ */
private boolean isValidProductDetailRow(Map<String, Object> data, String firstCellValue) { private boolean isValidProductDetailRow(Map<String, Object> data) {
// 有效条件:1.货品编号(第一列)不为空 2.不是表头行(表头第一列是"貨品編號") Object productCode = getValueSafely(data, 0); // A列
Object productCode = getValueSafely(data, 0);
return productCode != null return productCode != null
&& !"貨品編號".equals(productCode.toString().trim()) && !productCode.toString().trim().isEmpty()
&& !productCode.toString().trim().isEmpty(); && !"貨品編號".equals(productCode.toString().trim());
} }
/**
* 解析货品明细(第7行起)
* 列对应关系:0=A列=貨品編號,1=B列=簡稱,2=C列=箱,3=D列=支
*/
private void parseProductDetail(Map<String, Object> data, int rowNum) {
ProductDetail productDetail = new ProductDetail();
productDetail.setProductCode(convertToString(getValueSafely(data, 0)));
productDetail.setProductName(convertToString(getValueSafely(data, 1)));
productDetail.setBoxCount(convertToInteger(getValueSafely(data, 2)));
productDetail.setPieceCount(convertToInteger(getValueSafely(data, 3)));
productDetailList.add(productDetail);
LOGGER.debug("第{}行:解析货品明细完成,货品编号={},名称={},箱数={},支数={}",
rowNum, productDetail.getProductCode(), productDetail.getProductName(),
productDetail.getBoxCount(), productDetail.getPieceCount());
}
// ---------------------- 右侧:发票张数与小计解析 ----------------------
/**
* 第2行:发票张数与小计
* 通过关键字定位标签,再读取右侧相邻列,避免空列导致索引偏移
*/
private void parseInvoiceCount(Map<String, Object> data) {
// 1. 定位并解析发票张数
Integer invoiceCount = findValueAfterKeyword(data, KEYWORD_INVOICE_COUNT);
if (invoiceCount != null) {
deliveryHeader.setInvoiceCount(invoiceCount);
}
// 2. 定位并解析小计(标签右侧第1列=箱,第2列=支,第3列=金额)
Integer subtotalStartIndex = findKeywordIndex(data, KEYWORD_SUBTOTAL);
if (subtotalStartIndex != null && subtotalStartIndex >= 0) {
Integer subtotalBoxCount = convertToInteger(getValueSafely(data, subtotalStartIndex + 1));
Integer subtotalPieceCount = convertToInteger(getValueSafely(data, subtotalStartIndex + 2));
BigDecimal subtotalAmount = convertToBigDecimal(getValueSafely(data, subtotalStartIndex + 3));
deliveryHeader.setSubtotalBoxCount(subtotalBoxCount);
deliveryHeader.setSubtotalPieceCount(subtotalPieceCount);
deliveryHeader.setSubtotalAmount(subtotalAmount);
LOGGER.debug("第{}行:解析小计完成,箱数={},支数={},金额={}",
currentRowNum, subtotalBoxCount, subtotalPieceCount, subtotalAmount);
} else {
LOGGER.warn("第{}行:未找到小计标签,小计信息置空", currentRowNum);
deliveryHeader.setSubtotalBoxCount(0);
deliveryHeader.setSubtotalPieceCount(0);
deliveryHeader.setSubtotalAmount(BigDecimal.ZERO);
}
LOGGER.debug("第{}行:解析发票张数行完成,发票张数={},小计金额={}",
currentRowNum, deliveryHeader.getInvoiceCount(), deliveryHeader.getSubtotalAmount());
}
// ---------------------- 右侧:客户配送明细解析 ----------------------
/** /**
* 校验客户配送明细行是否有效(排除表头和空行) * 校验客户配送明细行是否有效(排除表头和空行)
*/ */
private boolean isValidCustomerDetailRow(Map<String, Object> data, String firstCellValue) { private boolean isValidCustomerDetailRow(Map<String, Object> data) {
// 有效条件:1.类别(第一列)不为空 2.不是表头行(表头第一列是"類别") Object category = getValueSafely(data, 5); // F列
Object category = getValueSafely(data, 0);
return category != null return category != null
&& !"類别".equals(category.toString().trim()) && !category.toString().trim().isEmpty()
&& !category.toString().trim().isEmpty(); && !"類别".equals(category.toString().trim());
}
// ---------------------- 具体数据解析方法(全部支持多单元格) ----------------------
/**
* 解析表头信息(送货日期、送货线号、车次)
* 列对应关系:0=送貨日期标签,1=送貨日期值,2=送貨線號标签,3=送貨線號值,4=車次标签,5=車次值
*/
private void parseHeaderInfo(Map<String, Object> data, int rowNum) {
try {
// 读取多单元格数据(标签在左,数值在右)
Date deliveryDate = getValueSafely(data, 1, Date.class); // 第2列:送货日期值
String deliveryLineNo = getValueSafely(data, 3, String.class); // 第4列:送货线号值
Integer trainNumber = getValueSafely(data, 5, Integer.class); // 第6列:车次值
// 赋值(空值处理)
if (deliveryDate != null) {
deliveryHeader.setDeliveryDate(deliveryDate);
}
deliveryHeader.setDeliveryLineNo(deliveryLineNo);
deliveryHeader.setTrainNumber(getDefaultValue(trainNumber, 0));
LOGGER.debug("第{}行:解析表头信息完成,送货日期={},送货线号={},车次={}",
rowNum, deliveryHeader.getDeliveryDate(),
deliveryHeader.getDeliveryLineNo(), deliveryHeader.getTrainNumber());
} catch (Exception e) {
LOGGER.error("第{}行:解析表头信息失败,所有单元格数据:{}", rowNum, data, e);
throw new RuntimeException("解析表头信息失败(行:" + rowNum + "", e);
}
} }
/** /**
* 解析纪录合共(多单元格版 * 解析客户配送明细(第4行起
* 列对应关系:0=紀錄合共标签,1=总记录数值,4=總計标签,5=总箱数值,6=总支数值 * 列对应关系:5=F列=類别,6=G列=發票編號,7=H列=客户編號,8=I列=名稱,
*/ * 9=J列=箱,10=K列=包,11=L列=金額
private void parseTotalRecord(Map<String, Object> data, int rowNum) {
try {
// 1. 验证是否为纪录合共行(第一列包含标签)
Object totalRecordLabel = getValueSafely(data, 0, String.class);
if (totalRecordLabel == null || !totalRecordLabel.toString().trim().contains(KEYWORD_TOTAL_RECORD)) {
LOGGER.warn("第{}行:不是纪录合共行,跳过解析,第一列内容:{}", rowNum, totalRecordLabel);
return;
}
// 2. 读取多单元格数值(标签右侧对应列)
Integer totalRecordCount = getValueSafely(data, 1, Integer.class); // 第2列:总记录数
Integer totalBoxCount = getValueSafely(data, 5, Integer.class); // 第6列:总箱数
Integer totalPieceCount = getValueSafely(data, 6, Integer.class); // 第7列:总支数
// 3. 赋值与日志
deliveryHeader.setTotalRecordCount(getDefaultValue(totalRecordCount, 0));
deliveryHeader.setTotalBoxCount(getDefaultValue(totalBoxCount, 0));
deliveryHeader.setTotalPieceCount(getDefaultValue(totalPieceCount, 0));
LOGGER.debug("第{}行:解析纪录合共完成,总记录数={},总箱数={},总支数={}",
rowNum, deliveryHeader.getTotalRecordCount(),
deliveryHeader.getTotalBoxCount(), deliveryHeader.getTotalPieceCount());
} catch (Exception e) {
LOGGER.error("第{}行:解析纪录合共失败,所有单元格数据:{}", rowNum, data, e);
throw new RuntimeException("解析纪录合共失败(行:" + rowNum + "", e);
}
}
/**
* 核心修复:解析发票张数(多单元格版,正确读取右侧单元格)
* 列对应关系:
* - 0=發票張數标签列(A列),1=发票张数数值列(B列)
* - 4=小計标签列(E列),5=小计箱数列(F列),6=小计支数列(G列),7=小计金额列(H列)
*/
private void parseInvoiceCount(Map<String, Object> data, int rowNum) {
try {
// 1. 验证是否为发票张数行(第一列包含标签)
Object invoiceLabel = getValueSafely(data, 0, String.class);
if (invoiceLabel == null || !invoiceLabel.toString().trim().contains(KEYWORD_INVOICE_COUNT)) {
LOGGER.warn("第{}行:不是发票张数行,跳过解析,第一列内容:{}", rowNum, invoiceLabel);
return;
}
// 2. 解析发票张数(标签右侧第1列:B列,索引1)
Integer invoiceCount = getValueSafely(data, 1, Integer.class);
deliveryHeader.setInvoiceCount(getDefaultValue(invoiceCount, 0));
LOGGER.debug("第{}行:解析发票张数,标签列(A列):{},数值列(B列):{}",
rowNum, invoiceLabel, deliveryHeader.getInvoiceCount());
// 3. 解析小计信息(标签在E列,数值在F/G/H列)
Object subtotalLabel = getValueSafely(data, 3, String.class); // E列:小計标签
if (subtotalLabel != null && subtotalLabel.toString().trim().contains(KEYWORD_SUBTOTAL)) {
Integer subtotalBoxCount = getValueSafely(data, 4, Integer.class); // F列:小计箱数
Integer subtotalPieceCount = getValueSafely(data, 5, Integer.class); // G列:小计支数
BigDecimal subtotalAmount = getValueSafely(data, 6, BigDecimal.class); // H列:小计金额
// 赋值(空值处理)
deliveryHeader.setSubtotalBoxCount(getDefaultValue(subtotalBoxCount, 0));
deliveryHeader.setSubtotalPieceCount(getDefaultValue(subtotalPieceCount, 0));
deliveryHeader.setSubtotalAmount(getDefaultValue(subtotalAmount, BigDecimal.ZERO));
LOGGER.debug("第{}行:解析小计信息,标签列(E列):{},箱数列(F列):{},支数列(G列):{},金额列(H列):{}",
rowNum, subtotalLabel,
deliveryHeader.getSubtotalBoxCount(),
deliveryHeader.getSubtotalPieceCount(),
deliveryHeader.getSubtotalAmount());
} else {
LOGGER.warn("第{}行:未找到小计标签(E列),或标签内容不匹配,跳过小计解析", rowNum);
deliveryHeader.setSubtotalBoxCount(0);
deliveryHeader.setSubtotalPieceCount(0);
deliveryHeader.setSubtotalAmount(BigDecimal.ZERO);
}
// 4. 解析完成日志
LOGGER.debug("第{}行:解析发票张数行完成,最终结果:发票张数={},小计金额={}",
rowNum, deliveryHeader.getInvoiceCount(), deliveryHeader.getSubtotalAmount());
} catch (Exception e) {
LOGGER.error("第{}行:解析发票张数失败,所有单元格数据:{}", rowNum, data, e);
throw new RuntimeException("解析发票张数失败(行:" + rowNum + "),请检查Excel列对应关系", e);
}
}
/**
* 解析货品明细(多单元格,动态行数适配)
* 列对应关系:0=貨品編號,1=簡稱,4=箱,5=支
*/
private void parseProductDetail(Map<String, Object> data, int rowNum) {
try {
ProductDetail productDetail = new ProductDetail();
// 读取对应列的多单元格数据
String productCode = getValueSafely(data, 0, String.class); // 第1列:货品编号
String productName = getValueSafely(data, 1, String.class); // 第2列:货品简称
Integer boxCount = getValueSafely(data, 4, Integer.class); // 第5列:箱数
Integer pieceCount = getValueSafely(data, 5, Integer.class); // 第6列:支数
// 赋值(空值处理+去空格)
productDetail.setProductCode(getDefaultValue(productCode, "").trim());
productDetail.setProductName(getDefaultValue(productName, "").trim());
productDetail.setBoxCount(getDefaultValue(boxCount, 0));
productDetail.setPieceCount(getDefaultValue(pieceCount, 0));
// 添加到列表
productDetailList.add(productDetail);
LOGGER.debug("第{}行:解析货品明细完成,货品编号={},名称={},箱数={},支数={}",
rowNum, productDetail.getProductCode(), productDetail.getProductName(),
productDetail.getBoxCount(), productDetail.getPieceCount());
} catch (Exception e) {
LOGGER.error("第{}行:解析货品明细失败,所有单元格数据:{}", rowNum, data, e);
throw new RuntimeException("解析货品明细失败(行:" + rowNum + "", e);
}
}
/**
* 解析客户配送明细(多单元格,动态行数适配)
* 列对应关系:0=類别,1=發票編號,2=客户編號,3=名稱,4=箱,5=包,6=金額
*/ */
private void parseCustomerDeliveryDetail(Map<String, Object> data, int rowNum) { private void parseCustomerDeliveryDetail(Map<String, Object> data, int rowNum) {
try { CustomerDeliveryDetail customerDetail = new CustomerDeliveryDetail();
CustomerDeliveryDetail customerDetail = new CustomerDeliveryDetail(); customerDetail.setCategory(convertToString(getValueSafely(data, 5)));
// 读取对应列的多单元格数据 customerDetail.setInvoiceNo(convertToString(getValueSafely(data, 6)));
String category = getValueSafely(data, 0, String.class); // 第1列:类别 customerDetail.setCustomerCode(convertToString(getValueSafely(data, 7)));
String invoiceNo = getValueSafely(data, 1, String.class); // 第2列:发票编号 customerDetail.setCustomerName(convertToString(getValueSafely(data, 8)));
String customerCode = getValueSafely(data, 2, String.class); // 第3列:客户编号 customerDetail.setBoxCount(convertToInteger(getValueSafely(data, 9)));
String customerName = getValueSafely(data, 3, String.class); // 第4列:客户名称 customerDetail.setPackageCount(convertToInteger(getValueSafely(data, 10)));
Integer boxCount = getValueSafely(data, 4, Integer.class); // 第5列:箱数 customerDetail.setAmount(convertToBigDecimal(getValueSafely(data, 11)));
Integer packageCount = getValueSafely(data, 5, Integer.class); // 第6列:包数
BigDecimal amount = getValueSafely(data, 6, BigDecimal.class); // 第7列:金额
// 赋值(空值处理+去空格) customerDeliveryDetailList.add(customerDetail);
customerDetail.setCategory(getDefaultValue(category, "").trim()); LOGGER.debug("第{}行:解析客户配送明细完成,类别={},发票编号={},客户编号={},名称={},箱数={},包数={},金额={}",
customerDetail.setInvoiceNo(getDefaultValue(invoiceNo, "").trim()); rowNum, customerDetail.getCategory(), customerDetail.getInvoiceNo(),
customerDetail.setCustomerCode(getDefaultValue(customerCode, "").trim()); customerDetail.getCustomerCode(), customerDetail.getCustomerName(),
customerDetail.setCustomerName(getDefaultValue(customerName, "").trim()); customerDetail.getBoxCount(), customerDetail.getPackageCount(), customerDetail.getAmount());
customerDetail.setBoxCount(getDefaultValue(boxCount, 0));
customerDetail.setPackageCount(getDefaultValue(packageCount, 0));
customerDetail.setAmount(getDefaultValue(amount, BigDecimal.ZERO));
// 添加到列表
customerDeliveryDetailList.add(customerDetail);
LOGGER.debug("第{}行:解析客户配送明细完成,客户编号={},名称={},金额={}",
rowNum, customerDetail.getCustomerCode(), customerDetail.getCustomerName(),
customerDetail.getAmount());
} catch (Exception e) {
LOGGER.error("第{}行:解析客户配送明细失败,所有单元格数据:{}", rowNum, data, e);
throw new RuntimeException("解析客户配送明细失败(行:" + rowNum + "", e);
}
} }
// ---------------------- 核心工具方法(支持多单元格+类型转换) ---------------------- // ---------------------- 核心工具方法(支持多单元格+类型转换) ----------------------
/** /**
* 获取第一列数据(用于状态判断) * 根据列索引安全获取值
* 优先按Map的key直接查找(适配EasyExcel以列号为key的情况),找不到再按位置顺序查找
* 注意:EasyExcel在某些情况下(如合并单元格、headRowNumber=0)可能将表头文本作为key,
* 此时key不是数字字符串,需跳过这些key,走位置兜底逻辑
*/ */
private String getFirstCellValue(Map<String, Object> data) { private Object getValueSafely(Map<String, Object> data, int columnIndex) {
if (data == null || data.isEmpty()) { if (data == null || data.isEmpty()) {
return ""; return null;
} }
Object firstValue = data.values().iterator().next();
return firstValue != null ? firstValue.toString().trim() : ""; String strIndex = String.valueOf(columnIndex);
// 1. 直接按key查找(兼容key为String或Integer的情况,跳过非数字key)
for (Object keyObj : data.keySet()) {
if (keyObj != null && strIndex.equals(keyObj.toString())) {
return data.get(keyObj);
}
}
// 2. 兜底:按顺序位置查找(保持与原逻辑兼容)
int index = 0;
for (Object value : data.values()) {
if (index == columnIndex) {
return value;
}
index++;
}
LOGGER.warn("第{}行列索引{}超出当前行单元格数量({}列),返回null",
currentRowNum, columnIndex, data.size());
return null;
} }
/** /**
@@ -392,115 +343,121 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
} }
/** /**
* 按列索引安全获取原始值(不做类型转换) * 在指定行中查找关键字所在的列索引
* 注意:Map的key可能是非数字字符串(如表头文本"客户配送明细"),此时跳过该key,不抛出异常
*/ */
private Object getValueSafely(Map<String, Object> data, int columnIndex) { private Integer findKeywordIndex(Map<String, Object> data, String keyword) {
if (data == null || data.isEmpty()) { if (data == null || data.isEmpty() || keyword == null) {
return null; return null;
} }
int index = 0; for (Object keyObj : data.keySet()) {
for (Object value : data.values()) { Object value = data.get(keyObj);
if (index == columnIndex) { if (value != null && value.toString().trim().contains(keyword)) {
return value; try {
return Integer.parseInt(keyObj.toString());
} catch (NumberFormatException e) {
// key不是数字(可能是表头文本如"客户配送明细"),跳过,不打印警告避免日志刷屏
LOGGER.debug("第{}行:关键字'{}'所在列key不是数字,key={},跳过",
currentRowNum, keyword, keyObj);
}
} }
index++;
} }
LOGGER.warn("列索引{}超出当前行单元格数量({}列),返回null", columnIndex, data.size());
return null; return null;
} }
/** /**
* 按列索引安全获取值(支持多类型转换,核心工具) * 查找关键字所在列的右侧相邻列值,并转为Integer
* 功能:1. 直接类型匹配 2. String转数字/日期 3. 数字类型间转换
*/ */
private <T> T getValueSafely(Map<String, Object> data, int columnIndex, Class<T> expectedType) { private Integer findValueAfterKeyword(Map<String, Object> data, String keyword) {
Object value = getValueSafely(data, columnIndex); Integer keywordIndex = findKeywordIndex(data, keyword);
if (value == null) { if (keywordIndex == null) {
LOGGER.debug("列索引{}:单元格值为null,返回null", columnIndex); LOGGER.warn("第{}行:未找到关键字'{}'", currentRowNum, keyword);
return null; return null;
} }
return convertToInteger(getValueSafely(data, keywordIndex + 1));
}
// 1. 直接类型匹配 /**
if (expectedType.isInstance(value)) { * Object转String
return expectedType.cast(value); */
private String convertToString(Object value) {
return value == null ? "" : value.toString().trim();
}
/**
* Object转Integer(支持文本型数字、Number类型)
*/
private Integer convertToInteger(Object value) {
if (value == null) {
return 0;
}
if (value instanceof Number) {
return ((Number) value).intValue();
} }
// 2. String类型转目标类型(核心:处理文本型数字)
if (value instanceof String) { if (value instanceof String) {
String strValue = ((String) value).trim(); String strValue = ((String) value).trim();
if (strValue.isEmpty()) { if (strValue.isEmpty()) {
LOGGER.debug("列索引{}:单元格值为空字符串,返回null", columnIndex); return 0;
return null;
} }
try {
// String → Integer return Integer.parseInt(strValue);
if (expectedType == Integer.class) { } catch (NumberFormatException e) {
try { LOGGER.warn("第{}行:值转换为Integer失败,值:{},返回0", currentRowNum, value, e);
return expectedType.cast(Integer.parseInt(strValue));
} catch (NumberFormatException e) {
LOGGER.warn("列索引{}String转Integer失败,原始值:{}", columnIndex, strValue, e);
return null;
}
}
// String → Long
if (expectedType == Long.class) {
try {
return expectedType.cast(Long.parseLong(strValue));
} catch (NumberFormatException e) {
LOGGER.warn("列索引{}String转Long失败,原始值:{}", columnIndex, strValue, e);
return null;
}
}
// String → BigDecimal(支持千分位符号,如"1,234.56"
if (expectedType == BigDecimal.class) {
try {
String cleanValue = strValue.replace(",", ""); // 去除千分位逗号
return expectedType.cast(new BigDecimal(cleanValue));
} catch (NumberFormatException e) {
LOGGER.warn("列索引{}String转BigDecimal失败,原始值:{}", columnIndex, strValue, e);
return null;
}
}
// String → Date(支持多种日期格式)
if (expectedType == Date.class) {
try {
return expectedType.cast(DateUtils.parseDate(strValue, SUPPORTED_DATE_FORMATS));
} catch (ParseException e) {
LOGGER.warn("列索引{}String转Date失败,原始值:{},支持格式:{}",
columnIndex, strValue, Arrays.toString(SUPPORTED_DATE_FORMATS), e);
return null;
}
} }
} }
return 0;
// 3. 数字类型间转换(如Double→IntegerLong→BigDecimal
if (value instanceof Number) {
Number numValue = (Number) value;
if (expectedType == Integer.class) {
return expectedType.cast(numValue.intValue());
}
if (expectedType == Long.class) {
return expectedType.cast(numValue.longValue());
}
if (expectedType == BigDecimal.class) {
return expectedType.cast(new BigDecimal(numValue.toString()));
}
}
// 4. 不支持的转换
LOGGER.warn("列索引{}:不支持的类型转换,期望类型:{},实际类型:{},原始值:{}",
columnIndex, expectedType.getSimpleName(), value.getClass().getSimpleName(), value);
return null;
} }
/** /**
* 空值处理:返回默认值 * Object转BigDecimal(支持千分位符号,如"1,234.56"
*/ */
private <T> T getDefaultValue(T value, T defaultValue) { private BigDecimal convertToBigDecimal(Object value) {
return value != null ? value : defaultValue; if (value == null) {
return BigDecimal.ZERO;
}
if (value instanceof BigDecimal) {
return (BigDecimal) value;
}
if (value instanceof Number) {
return new BigDecimal(value.toString());
}
if (value instanceof String) {
String strValue = ((String) value).trim().replace(",", "");
if (strValue.isEmpty()) {
return BigDecimal.ZERO;
}
try {
return new BigDecimal(strValue);
} catch (NumberFormatException e) {
LOGGER.warn("第{}行:值转换为BigDecimal失败,值:{},返回0", currentRowNum, value, e);
}
}
return BigDecimal.ZERO;
}
/**
* Object转Date(支持Date类型、String类型)
*/
private Date convertToDate(Object value) {
if (value == null) {
return null;
}
if (value instanceof Date) {
return (Date) value;
}
if (value instanceof String) {
String strValue = ((String) value).trim();
if (strValue.isEmpty()) {
return null;
}
try {
return DateUtils.parseDate(strValue, SUPPORTED_DATE_FORMATS);
} catch (ParseException e) {
LOGGER.warn("第{}行:值转换为Date失败,值:{},支持格式:{}",
currentRowNum, strValue, Arrays.toString(SUPPORTED_DATE_FORMATS), e);
}
}
return null;
} }
// ---------------------- Getter方法(供Service层获取解析结果) ---------------------- // ---------------------- Getter方法(供Service层获取解析结果) ----------------------
@@ -515,4 +472,4 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
public List<CustomerDeliveryDetail> getCustomerDeliveryDetailList() { public List<CustomerDeliveryDetail> getCustomerDeliveryDetailList() {
return customerDeliveryDetailList; return customerDeliveryDetailList;
} }
} }
@@ -17,7 +17,7 @@ import java.util.Map;
/** /**
* 入库单Excel解析监听器 * 入库单Excel解析监听器
* 功能:1. 解析第一行表头数据 2. 解析第行开始的产品明细 3. 支持文本型数字转换 * 功能:1. 解析第一行表头数据 2. 跳过第二行合计行、第三行列名行 3. 解析第行开始的产品明细 4. 支持文本型数字转换
*/ */
public class StockInExcelListener extends AnalysisEventListener<Map<String, Object>> { public class StockInExcelListener extends AnalysisEventListener<Map<String, Object>> {
private static final Logger LOGGER = LoggerFactory.getLogger(StockInExcelListener.class); private static final Logger LOGGER = LoggerFactory.getLogger(StockInExcelListener.class);
@@ -44,8 +44,16 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
if (currentRowNum == 1) { if (currentRowNum == 1) {
parseHeaderData(data); parseHeaderData(data);
} }
// 2. 解析第二行及以后:产品明细(跳过表头行:第2行是表格列名 // 2. 跳过第二行:合计行(Total: xxx
else if (currentRowNum >= 3) { else if (currentRowNum == 2) {
LOGGER.debug("第{}行是合计行,跳过解析", currentRowNum);
}
// 3. 跳过第三行:表格列名行(產品編號、產品名稱...)
else if (currentRowNum == 3) {
LOGGER.debug("第{}行是表格列名行,跳过解析", currentRowNum);
}
// 4. 解析第四行及以后:产品明细
else if (currentRowNum >= 4) {
if (isValidProductRow(data)) { if (isValidProductRow(data)) {
StockInProductDetail detail = parseProductDetail(data); StockInProductDetail detail = parseProductDetail(data);
productDetailList.add(detail); productDetailList.add(detail);
@@ -66,19 +74,19 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
/** /**
* 解析第一行表头数据 * 解析第一行表头数据
* 列对应关系:0=入库日期标签,1=入库日期值,2=OrderNO标签3=OrderNO值 * 模板中标签与值相邻,分别搜索"入库日期"和"OrderNO"标签后一列的值,兼容位置调整
*/ */
private void parseHeaderData(Map<String, Object> data) { private void parseHeaderData(Map<String, Object> data) {
// 入库日期(第2列,索引1 // 入库日期:查找包含"入库日期"标签的下一个值
Object stockInDateObj = getValueSafely(data, 1); Object stockInDateObj = getValueAfterLabel(data, "入库日期");
if (stockInDateObj != null) { if (stockInDateObj != null) {
Date stockInDate = convertToDate(stockInDateObj); Date stockInDate = convertToDate(stockInDateObj);
stockInHeader.setStockInDate(stockInDate); stockInHeader.setStockInDate(stockInDate);
LOGGER.debug("第1行:入库日期解析完成,值:{}", stockInDate); LOGGER.debug("第1行:入库日期解析完成,值:{}", stockInDate);
} }
// OrderNO(第4列,索引3 // OrderNO:查找包含"OrderNO"标签的下一个值
Object orderNoObj = getValueSafely(data, 3); Object orderNoObj = getValueAfterLabel(data, "OrderNO");
if (orderNoObj != null) { if (orderNoObj != null) {
String orderNo = orderNoObj.toString().trim(); String orderNo = orderNoObj.toString().trim();
stockInHeader.setOrderNo(orderNo); stockInHeader.setOrderNo(orderNo);
@@ -86,7 +94,7 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
} }
// 校验表头数据完整性 // 校验表头数据完整性
if (stockInHeader.getStockInDate() == null || stockInHeader.getOrderNo().isEmpty()) { if (stockInHeader.getStockInDate() == null || stockInHeader.getOrderNo() == null || stockInHeader.getOrderNo().isEmpty()) {
LOGGER.warn("第1行表头数据不完整,入库日期:{},OrderNO:{}", LOGGER.warn("第1行表头数据不完整,入库日期:{},OrderNO:{}",
stockInHeader.getStockInDate(), stockInHeader.getOrderNo()); stockInHeader.getStockInDate(), stockInHeader.getOrderNo());
} }
@@ -94,22 +102,23 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
/** /**
* 解析产品明细数据(一行对应一个产品) * 解析产品明细数据(一行对应一个产品)
* 新模板列顺序:0=产品编号,1=产品名称,2=规格(箱含量),3=件数,4=Qty(cases)(箱数),5=饮用日期
*/ */
private StockInProductDetail parseProductDetail(Map<String, Object> data) { private StockInProductDetail parseProductDetail(Map<String, Object> data) {
StockInProductDetail detail = new StockInProductDetail(); StockInProductDetail detail = new StockInProductDetail();
// 产品编号(第1列,索引0)- 支持文本型数字转换 // 产品编号(第1列,索引0)- 支持文本型数字转换
detail.setProductCode(convertToString(getValueSafely(data, 0)).toString()); detail.setProductCode(convertToString(getValueSafely(data, 0)));
// 产品名称(第2列,索引1 // 产品名称(第2列,索引1
detail.setProductName(convertToString(getValueSafely(data, 1))); detail.setProductName(convertToString(getValueSafely(data, 1)));
// 箱含量(第5列,索引4 // 箱含量/规格(第3列,索引2
detail.setBoxCapacity(convertToInteger(getValueSafely(data, 4))); detail.setBoxCapacity(convertToInteger(getValueSafely(data, 2)));
// 件数(第6列,索引5 // 件数(第4列,索引3
detail.setPieceCount(convertToInteger(getValueSafely(data, 5))); detail.setPieceCount(convertToInteger(getValueSafely(data, 3)));
// 箱数(第7列,索引6 // 箱数/Qty(cases)(第5列,索引4
detail.setBoxCount(convertToInteger(getValueSafely(data, 6))); detail.setBoxCount(convertToInteger(getValueSafely(data, 4)));
// 饮用日期(第8列,索引7 // 饮用日期(第6列,索引5
detail.setDrinkDate(convertToDate(getValueSafely(data, 7))); detail.setDrinkDate(convertToDate(getValueSafely(data, 5)));
LOGGER.debug("第{}行产品明细解析完成,产品编号:{},产品名称:{}", LOGGER.debug("第{}行产品明细解析完成,产品编号:{},产品名称:{}",
currentRowNum, detail.getProductCode(), detail.getProductName()); currentRowNum, detail.getProductCode(), detail.getProductName());
@@ -120,12 +129,11 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
* 校验产品行是否有效(排除空行和合计行) * 校验产品行是否有效(排除空行和合计行)
*/ */
private boolean isValidProductRow(Map<String, Object> data) { private boolean isValidProductRow(Map<String, Object> data) {
// 有效条件:产品编号不为空 + 不是合计行(合计行产品编号为null,且包含"Total" // 有效条件:产品编号不为空 + 不是合计行
Object productCodeObj = getValueSafely(data, 0); Object productCodeObj = getValueSafely(data, 0);
Object productNameObj = getValueSafely(data, 1);
// 排除合计行(如第7行:Total68 // 排除合计行(整行包含"Total"字样
if (productNameObj != null && productNameObj.toString().contains("Total")) { if (containsAnyValue(data, "Total")) {
LOGGER.debug("第{}行是合计行,跳过解析", currentRowNum); LOGGER.debug("第{}行是合计行,跳过解析", currentRowNum);
return false; return false;
} }
@@ -186,6 +194,40 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
return "" + (columnIndex + 1) + ""; return "" + (columnIndex + 1) + "";
} }
/**
* 根据标签查找相邻的下一个值(用于第一行表头,兼容标签位置调整)
*/
private Object getValueAfterLabel(Map<String, Object> data, String label) {
if (data == null || data.isEmpty()) {
return null;
}
Object prevValue = null;
for (Object value : data.values()) {
if (prevValue != null && prevValue.toString().contains(label)) {
return value;
}
prevValue = value;
}
return null;
}
/**
* 判断当前行是否有任意单元格包含指定文本
*/
private boolean containsAnyValue(Map<String, Object> data, String text) {
if (data == null || data.isEmpty()) {
return false;
}
for (Object value : data.values()) {
if (value != null && value.toString().contains(text)) {
return true;
}
}
return false;
}
/** /**
* 通用转换:Object转String * 通用转换:Object转String
*/ */
@@ -174,9 +174,21 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
// 封装DTO(逻辑不变) // 封装DTO(逻辑不变)
DeliveryDataDTO deliveryDataDTO = new DeliveryDataDTO(); DeliveryDataDTO deliveryDataDTO = new DeliveryDataDTO();
deliveryDataDTO.setDeliveryHeader(listener.getDeliveryHeader()); deliveryDataDTO.setDeliveryHeader(listener.getDeliveryHeader());
deliveryDataDTO.setProductDetailList(listener.getProductDetailList()); // 判空处理:货品明细和客户配送明细可能为空列表
deliveryDataDTO.setCustomerDeliveryDetailList(listener.getCustomerDeliveryDetailList()); List<ProductDetail> productDetailList = listener.getProductDetailList();
List<ProductDetail> productDetailList = deliveryDataDTO.getProductDetailList(); if (productDetailList == null) {
productDetailList = new ArrayList<>();
}
deliveryDataDTO.setProductDetailList(productDetailList);
List<CustomerDeliveryDetail> customerDeliveryDetailList = listener.getCustomerDeliveryDetailList();
if (customerDeliveryDetailList == null) {
customerDeliveryDetailList = new ArrayList<>();
}
deliveryDataDTO.setCustomerDeliveryDetailList(customerDeliveryDetailList);
LOGGER.info("Excel解析完成,货品明细{}条,客户配送明细{}条",
productDetailList.size(), customerDeliveryDetailList.size());
List<Map<String,Object>> shipperIds = new ArrayList<>(); List<Map<String,Object>> shipperIds = new ArrayList<>();
Set<Long> processedShipperIds = new HashSet<>(); Set<Long> processedShipperIds = new HashSet<>();
for (ProductDetail productDetail : productDetailList) { for (ProductDetail productDetail : productDetailList) {
@@ -192,20 +204,25 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
} }
} }
} }
if (shipperIds.isEmpty()) { // 货品明细不为空时,校验货主信息
throw new Exception("出库物料货主不存在!"); if (!productDetailList.isEmpty()) {
} if (shipperIds.isEmpty()) {
if (shipperIds.size() > 1) { throw new Exception("出库物料货主不存在!");
throw new Exception("出库物料不是同一个货主!"); }
if (shipperIds.size() > 1) {
throw new Exception("出库物料不是同一个货主!");
}
} }
ReservationStockOutOrderDO reservationStockOutOrderDO = new ReservationStockOutOrderDO(); ReservationStockOutOrderDO reservationStockOutOrderDO = new ReservationStockOutOrderDO();
reservationStockOutOrderDO.setOutOrderNumber(OrderSequence.getOrderCode("CK")); reservationStockOutOrderDO.setOutOrderNumber(OrderSequence.getOrderCode("CK"));
reservationStockOutOrderDO.setShipperId((Long) shipperIds.get(0).get("shipperId")); // 货主信息:有货品明细时从解析结果取,无货品明细时从登录用户取
reservationStockOutOrderDO.setShipperName((String) shipperIds.get(0).get("shipperName")); if (!shipperIds.isEmpty()) {
reservationStockOutOrderDO.setShipperCode((String) shipperIds.get(0).get("shipperCode")); reservationStockOutOrderDO.setShipperId((Long) shipperIds.get(0).get("shipperId"));
reservationStockOutOrderDO.setShipperName((String) shipperIds.get(0).get("shipperName"));
reservationStockOutOrderDO.setShipperCode((String) shipperIds.get(0).get("shipperCode"));
}
reservationStockOutOrderDO.setNeedTransportFlag("1"); reservationStockOutOrderDO.setNeedTransportFlag("1");
reservationStockOutOrderDO.setNeedDeclareFlag("1"); reservationStockOutOrderDO.setNeedDeclareFlag("1");
reservationStockOutOrderDO.setOrganizationId(loginUser.getUserPo().getOrganizationId()); reservationStockOutOrderDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
@@ -267,7 +284,7 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
reservationStockOutOrderDO.setMaterialDetailList(reservationOutMaterialDetailDOList); reservationStockOutOrderDO.setMaterialDetailList(reservationOutMaterialDetailDOList);
reservationStockOutOrderApplicationService.insert(reservationStockOutOrderDO); reservationStockOutOrderApplicationService.insert(reservationStockOutOrderDO);
List<CustomerDeliveryDetail> customerDeliveryDetailList = deliveryDataDTO.getCustomerDeliveryDetailList(); // 客户配送明细判空处理:为空时跳过关联保存
for (CustomerDeliveryDetail customerDeliveryDetail : customerDeliveryDetailList) { for (CustomerDeliveryDetail customerDeliveryDetail : customerDeliveryDetailList) {
ReservationDeliveryDetailsLink reservationDeliveryDetailsLink = new ReservationDeliveryDetailsLink(); ReservationDeliveryDetailsLink reservationDeliveryDetailsLink = new ReservationDeliveryDetailsLink();
reservationDeliveryDetailsLink.setInvoiceNumber(customerDeliveryDetail.getInvoiceNo()); reservationDeliveryDetailsLink.setInvoiceNumber(customerDeliveryDetail.getInvoiceNo());
@@ -388,7 +405,7 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
reservationInMaterialDetailDO.setCreateByName(loginUser.getUserPo().getUserName()); reservationInMaterialDetailDO.setCreateByName(loginUser.getUserPo().getUserName());
reservationInMaterialDetailDO.setCreateTime(new Date()); reservationInMaterialDetailDO.setCreateTime(new Date());
MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByCode(stockInProductDetail.getProductCode()); MaterialBaseInfoPO materialBaseInfoPO = stockInOrderMapper.getByCode(stockInProductDetail.getProductCode());
reservationInMaterialDetailDO.setQuantity(new BigDecimal(stockInProductDetail.getPieceCount()).setScale(2, BigDecimal.ROUND_HALF_UP)); reservationInMaterialDetailDO.setQuantity(new BigDecimal(stockInProductDetail.getBoxCount()).setScale(2, BigDecimal.ROUND_HALF_UP));
if (materialBaseInfoPO != null) { if (materialBaseInfoPO != null) {
reservationInMaterialDetailDO.setMaterialBaseInfoId(materialBaseInfoPO.getMaterialBaseInfoId()); reservationInMaterialDetailDO.setMaterialBaseInfoId(materialBaseInfoPO.getMaterialBaseInfoId());
reservationInMaterialDetailDO.setMaterialName(materialBaseInfoPO.getMaterialName()); reservationInMaterialDetailDO.setMaterialName(materialBaseInfoPO.getMaterialName());
@@ -295,43 +295,45 @@ public class ReservationStockOutOrderApplicationService {
{ {
ReservationStockOutOrderPO stockOutOrderPO = stockOutOrderDomainService.getInfoChildren(outOrderId); ReservationStockOutOrderPO stockOutOrderPO = stockOutOrderDomainService.getInfoChildren(outOrderId);
String userNcCode = reservationStockInOrderMapper.getUserNcCode(stockOutOrderPO.getShipperId()); String userNcCode = reservationStockInOrderMapper.getUserNcCode(stockOutOrderPO.getShipperId());
if (stockOutOrderPO == null || CollectionUtils.isEmpty(stockOutOrderPO.getMaterialDetailList())) { if (stockOutOrderPO == null ) {
return stockOutOrderPO; return stockOutOrderPO;
} }
stockOutOrderPO.setKhdm(userNcCode); stockOutOrderPO.setKhdm(userNcCode);
List<ReservationOutMaterialDetailPO> materialDetailList = stockOutOrderPO.getMaterialDetailList(); if (stockOutOrderPO.getMaterialDetailList() != null && stockOutOrderPO.getMaterialDetailList().size() > 0) {
if (CollectionUtils.isEmpty(materialDetailList)) { List<ReservationOutMaterialDetailPO> materialDetailList = stockOutOrderPO.getMaterialDetailList();
for (ReservationOutMaterialDetailPO reservationOutMaterialDetailPO : materialDetailList) { if (CollectionUtils.isEmpty(materialDetailList)) {
reservationOutMaterialDetailPO.setKhdm(userNcCode); for (ReservationOutMaterialDetailPO reservationOutMaterialDetailPO : materialDetailList) {
reservationOutMaterialDetailPO.setKhdm(userNcCode);
}
stockOutOrderPO.setMaterialDetailList(materialDetailList);
} }
stockOutOrderPO.setMaterialDetailList(materialDetailList); List<ReservationOutMaterialDetailPO> detailList = stockOutOrderPO.getMaterialDetailList();
} String outOrderNumber = stockOutOrderPO.getOutOrderNumber();
List<ReservationOutMaterialDetailPO> detailList = stockOutOrderPO.getMaterialDetailList(); for (ReservationOutMaterialDetailPO reservationOutMaterialDetailPO : detailList) {
String outOrderNumber = stockOutOrderPO.getOutOrderNumber(); Long materialBaseInfoId = reservationOutMaterialDetailPO.getMaterialBaseInfoId();
for (ReservationOutMaterialDetailPO reservationOutMaterialDetailPO : detailList) { List<InventoryAdjustmentRecordPO> inventoryAdjustmentRecordPOS = reservationInMaterialDetailService.getkctzjl(materialBaseInfoId, outOrderNumber, "出库");
Long materialBaseInfoId = reservationOutMaterialDetailPO.getMaterialBaseInfoId(); if (inventoryAdjustmentRecordPOS != null && inventoryAdjustmentRecordPOS.size() > 0) {
List<InventoryAdjustmentRecordPO> inventoryAdjustmentRecordPOS = reservationInMaterialDetailService.getkctzjl(materialBaseInfoId, outOrderNumber, "出库"); InventoryAdjustmentRecordPO inventoryAdjustmentRecordPO = inventoryAdjustmentRecordPOS.get(0);
if (inventoryAdjustmentRecordPOS != null && inventoryAdjustmentRecordPOS.size() > 0) { reservationOutMaterialDetailPO.setAllocationBeforeQuantity(inventoryAdjustmentRecordPO.getAllocationBeforeQuantity());
InventoryAdjustmentRecordPO inventoryAdjustmentRecordPO = inventoryAdjustmentRecordPOS.get(0); reservationOutMaterialDetailPO.setAllocationAfterQuantity(inventoryAdjustmentRecordPO.getAllocationAfterQuantity());
reservationOutMaterialDetailPO.setAllocationBeforeQuantity(inventoryAdjustmentRecordPO.getAllocationBeforeQuantity()); reservationOutMaterialDetailPO.setFreezeAfterQuantity(inventoryAdjustmentRecordPO.getFreezeAfterQuantity());
reservationOutMaterialDetailPO.setAllocationAfterQuantity(inventoryAdjustmentRecordPO.getAllocationAfterQuantity()); reservationOutMaterialDetailPO.setFreezeBeforeQuantity(inventoryAdjustmentRecordPO.getFreezeBeforeQuantity());
reservationOutMaterialDetailPO.setFreezeAfterQuantity(inventoryAdjustmentRecordPO.getFreezeAfterQuantity()); reservationOutMaterialDetailPO.setInventoryAfterQuantity(inventoryAdjustmentRecordPO.getInventoryAfterQuantity());
reservationOutMaterialDetailPO.setFreezeBeforeQuantity(inventoryAdjustmentRecordPO.getFreezeBeforeQuantity()); reservationOutMaterialDetailPO.setInventoryBeforeQuantity(inventoryAdjustmentRecordPO.getInventoryBeforeQuantity());
reservationOutMaterialDetailPO.setInventoryAfterQuantity(inventoryAdjustmentRecordPO.getInventoryAfterQuantity()); reservationOutMaterialDetailPO.setNetWeight(inventoryAdjustmentRecordPO.getNetWeight());
reservationOutMaterialDetailPO.setInventoryBeforeQuantity(inventoryAdjustmentRecordPO.getInventoryBeforeQuantity()); reservationOutMaterialDetailPO.setGrossWeight(inventoryAdjustmentRecordPO.getGrossWeight());
reservationOutMaterialDetailPO.setNetWeight(inventoryAdjustmentRecordPO.getNetWeight()); reservationOutMaterialDetailPO.setVolume(inventoryAdjustmentRecordPO.getVolume());
reservationOutMaterialDetailPO.setGrossWeight(inventoryAdjustmentRecordPO.getGrossWeight()); reservationOutMaterialDetailPO.setArea(inventoryAdjustmentRecordPO.getArea());
reservationOutMaterialDetailPO.setVolume(inventoryAdjustmentRecordPO.getVolume()); reservationOutMaterialDetailPO.setAdjustedNetWeight(inventoryAdjustmentRecordPO.getAdjustedNetWeight());
reservationOutMaterialDetailPO.setArea(inventoryAdjustmentRecordPO.getArea()); reservationOutMaterialDetailPO.setAdjustedGrossWeight(inventoryAdjustmentRecordPO.getAdjustedGrossWeight());
reservationOutMaterialDetailPO.setAdjustedNetWeight(inventoryAdjustmentRecordPO.getAdjustedNetWeight()); reservationOutMaterialDetailPO.setAdjustedVolume(inventoryAdjustmentRecordPO.getAdjustedVolume());
reservationOutMaterialDetailPO.setAdjustedGrossWeight(inventoryAdjustmentRecordPO.getAdjustedGrossWeight()); reservationOutMaterialDetailPO.setAdjustedArea(inventoryAdjustmentRecordPO.getAdjustedArea());
reservationOutMaterialDetailPO.setAdjustedVolume(inventoryAdjustmentRecordPO.getAdjustedVolume()); }
reservationOutMaterialDetailPO.setAdjustedArea(inventoryAdjustmentRecordPO.getAdjustedArea()); if(materialBaseInfoId != null){
} MaterialBaseInfoPO materialBaseInfoPO = reservationStockInOrderMapper.getinfo(materialBaseInfoId);
if(materialBaseInfoId != null){ if(materialBaseInfoPO != null && materialBaseInfoPO.getUnitPrice() != null){
MaterialBaseInfoPO materialBaseInfoPO = reservationStockInOrderMapper.getinfo(materialBaseInfoId); reservationOutMaterialDetailPO.setUnitPrice(materialBaseInfoPO.getUnitPrice());
if(materialBaseInfoPO != null && materialBaseInfoPO.getUnitPrice() != null){ }
reservationOutMaterialDetailPO.setUnitPrice(materialBaseInfoPO.getUnitPrice());
} }
} }
} }
@@ -377,6 +379,10 @@ public class ReservationStockOutOrderApplicationService {
List<ReservationDeliveryDetailsLink> reservationDeliveryDetailsLinks = reservationDeliveryDetailsLinkMapper.selectList(new LambdaQueryWrapper<ReservationDeliveryDetailsLink>() List<ReservationDeliveryDetailsLink> reservationDeliveryDetailsLinks = reservationDeliveryDetailsLinkMapper.selectList(new LambdaQueryWrapper<ReservationDeliveryDetailsLink>()
.eq(ReservationDeliveryDetailsLink::getOrderNumber, stockOutOrderPO.getOutOrderNumber())); .eq(ReservationDeliveryDetailsLink::getOrderNumber, stockOutOrderPO.getOutOrderNumber()));
stockOutOrderPO.setReservationDeliveryDetailsLinkList(reservationDeliveryDetailsLinks); stockOutOrderPO.setReservationDeliveryDetailsLinkList(reservationDeliveryDetailsLinks);
Long shipperId = stockOutOrderPO.getShipperId();
if (shipperId != null && shipperId == 0L) {
stockOutOrderPO.setShipperId(null);
}
return stockOutOrderPO; return stockOutOrderPO;
} }
@@ -134,7 +134,11 @@ public class ReservationStockOutOrderDomainService {
reservationDeliveryDetailsLinkService.saveBatch(reservationDeliveryDetailsLinkList); reservationDeliveryDetailsLinkService.saveBatch(reservationDeliveryDetailsLinkList);
} }
} }
return outMaterialDetailService.batchInsert(stockOutOrderDO.getOutOrderNumber(), stockOutOrderDO.getMaterialDetailList()); List<ReservationOutMaterialDetailDO> materialDetailList = stockOutOrderDO.getMaterialDetailList();
if (materialDetailList != null && !materialDetailList.isEmpty()) {
return outMaterialDetailService.batchInsert(stockOutOrderDO.getOutOrderNumber(), stockOutOrderDO.getMaterialDetailList());
}
return true;
} }
/** /**
@@ -810,7 +814,7 @@ public class ReservationStockOutOrderDomainService {
reservationStockInOrderMapper.insert(reservationStockInOrder); reservationStockInOrderMapper.insert(reservationStockInOrder);
reservationDeliveryDetailsLink.setReturnOrderNumber(reservationStockInOrder.getInOrderNumber()); reservationDeliveryDetailsLink.setReturnOrderNumber(reservationStockInOrder.getInOrderNumber());
reservationDeliveryDetailsLinkService.updateById(reservationDeliveryDetailsLink); reservationDeliveryDetailsLinkService.updateById(reservationDeliveryDetailsLink);
reservationDeliveryDetailsLinkService.updateW(reservationStockInOrder.getInOrderNumber(), reservationDeliveryDetailsLink.getOrderNumber()); reservationDeliveryDetailsLinkService.updateW(reservationStockInOrder.getInOrderNumber(), reservationDeliveryDetailsLink.getInvoiceNumber());
String returnOrderNumber = stockOutOrder.getReturnOrderNumber(); String returnOrderNumber = stockOutOrder.getReturnOrderNumber();
if (returnOrderNumber == null) { if (returnOrderNumber == null) {
stockOutOrder.setReturnOrderNumber(reservationStockInOrder.getInOrderNumber()); stockOutOrder.setReturnOrderNumber(reservationStockInOrder.getInOrderNumber());
@@ -63,6 +63,10 @@ public class ReserveInMaterialDetail extends BaseVOEntity {
@Excel(name = "物料名称") @Excel(name = "物料名称")
private String materialName; private String materialName;
@ApiModelProperty("物料名称")
@Excel(name = "物料名称")
private String reserveMaterialName;
@ApiModelProperty("物料条形码") @ApiModelProperty("物料条形码")
@Excel(name = "物料条形码") @Excel(name = "物料条形码")
private String barCode; private String barCode;
@@ -63,6 +63,10 @@ public class ReserveInMaterialDetailPO extends ReserveStockInOrderBasePO {
@Excel(name = "物料名称") @Excel(name = "物料名称")
private String materialName; private String materialName;
@ApiModelProperty("物料名称")
@Excel(name = "物料名称")
private String reserveMaterialName;
@ApiModelProperty("物料条形码") @ApiModelProperty("物料条形码")
@Excel(name = "物料条形码") @Excel(name = "物料条形码")
private String barCode; private String barCode;
@@ -61,6 +61,10 @@ public class ReserveInMaterialDetailDO extends ReserveStockInOrderBaseDO {
@Excel(name = "物料名称") @Excel(name = "物料名称")
private String materialName; private String materialName;
@ApiModelProperty("物料名称")
@Excel(name = "物料名称")
private String reserveMaterialName;
@ApiModelProperty("物料条形码") @ApiModelProperty("物料条形码")
@Excel(name = "物料条形码") @Excel(name = "物料条形码")
private String barCode; private String barCode;
@@ -63,6 +63,10 @@ public class ReserveInMaterialDetailDTO extends BaseVOEntity {
@Excel(name = "物料名称") @Excel(name = "物料名称")
private String materialName; private String materialName;
@ApiModelProperty("物料名称")
@Excel(name = "物料名称")
private String reserveMaterialName;
@ApiModelProperty("物料条形码") @ApiModelProperty("物料条形码")
@Excel(name = "物料条形码") @Excel(name = "物料条形码")
private String barCode; private String barCode;
@@ -667,7 +667,7 @@ public class ReserveStockInOrderDomainService {
MaterialBaseInfoPO materialBaseInfoPO = reservationStockInOrderMapper.getinfo(inMaterialDetailDO.getMaterialBaseInfoId()); MaterialBaseInfoPO materialBaseInfoPO = reservationStockInOrderMapper.getinfo(inMaterialDetailDO.getMaterialBaseInfoId());
//MaterialBaseInfoPO materialBaseInfoPO = materialBaseInfoService.getInfo(inMaterialDetailDO.getMaterialBaseInfoId()); //MaterialBaseInfoPO materialBaseInfoPO = materialBaseInfoService.getInfo(inMaterialDetailDO.getMaterialBaseInfoId());
if (materialBaseInfoPO == null){ if (materialBaseInfoPO == null){
throw new ServiceException("物料基础信息不存在"); return;
} }
inMaterialDetailDO.setMaterialCode(materialBaseInfoPO.getMaterialCode()); inMaterialDetailDO.setMaterialCode(materialBaseInfoPO.getMaterialCode());
inMaterialDetailDO.setMaterialName(materialBaseInfoPO.getMaterialName()); inMaterialDetailDO.setMaterialName(materialBaseInfoPO.getMaterialName());
@@ -192,24 +192,28 @@ public class ReservationStockInOrderApi extends BaseController {
/** /**
* 上传并解析Excel文件 * 上传并解析Excel文件(默认读取"出库导入"工作表)
* @param file Excel文件 * @param file Excel文件
* @param sheetName 工作表名称(出库导入/参考数据)
* @return 解析后的出库数据 * @return 解析后的出库数据
*/ */
@PostMapping("/parseExcel") @PostMapping("/parseExcel")
public ResponseEntity<Map<String, Object>> parseExcel( public ResponseEntity<Map<String, Object>> parseExcel(
@RequestParam("file") MultipartFile file) { @RequestParam("file") MultipartFile file) {
Map<String, Object> result = new HashMap<>();
try { try {
DeliveryDataDTO deliveryDataDTO = reservationStockInOrderService.parseExcelFile(file, ""); DeliveryDataDTO deliveryDataDTO = reservationStockInOrderService.parseExcelFile(file, "");
Map<String, Object> result = new HashMap<>();
result.put("result", deliveryDataDTO); result.put("result", deliveryDataDTO);
result.put("code",200); result.put("code", 200);
result.put("msg", "解析成功");
return new ResponseEntity<>(result, HttpStatus.OK); return new ResponseEntity<>(result, HttpStatus.OK);
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
return new ResponseEntity<>(HttpStatus.BAD_REQUEST); result.put("code", 400);
result.put("msg", e.getMessage());
return new ResponseEntity<>(result, HttpStatus.BAD_REQUEST);
} catch (Exception e) { } catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR); result.put("code", 500);
result.put("msg", e.getMessage());
return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
} }
} }
@@ -61,6 +61,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select> </select>
<update id="updateW"> <update id="updateW">
update NGWL_TEST_WMS.DELIVERY_DETAILS_LINK set RETURN_ORDER_NUMBER = #{returnOrderNumber} where ORDER_NUMBER = #{orderNumber} update NGWL_TEST_WMS.DELIVERY_DETAILS_LINK set RETURN_ORDER_NUMBER = #{returnOrderNumber} where INVOICE_NUMBER = #{orderNumber}
</update> </update>
</mapper> </mapper>
@@ -96,6 +96,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="reservePrice" column="reserve_price" /> <result property="reservePrice" column="reserve_price" />
<result property="reserveUnitPrice" column="reserve_unit_price" /> <result property="reserveUnitPrice" column="reserve_unit_price" />
<result property="shelfLife" column="shelf_life" /> <result property="shelfLife" column="shelf_life" />
<result property="reserveMaterialName" column="RESERVE_MATERIAL_NAME" />
</resultMap> </resultMap>
@@ -109,7 +110,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
a.lot_no ,a.reserve_quantity,a.reserve_gross_weight,a.reserve_net_weight,a.reserve_volume,a.reserve_area,a.reserve_price,a.reserve_unit_price,a.shelf_life, a.lot_no ,a.reserve_quantity,a.reserve_gross_weight,a.reserve_net_weight,a.reserve_volume,a.reserve_area,a.reserve_price,a.reserve_unit_price,a.shelf_life,
a.material_detail_id, a.organization_id, a.organization_name, a.top_organization_id, a.unique_id, a.in_order_number, a.material_base_info_id, a.material_detail_id, a.organization_id, a.organization_name, a.top_organization_id, a.unique_id, a.in_order_number, a.material_base_info_id,
a.material_code, a.material_name, a.bar_code, a.quantity, a.receipt_quantity, a.shelves_quantity, a.material_code, a.material_name, a.bar_code, a.quantity, a.receipt_quantity, a.shelves_quantity,
a.receipt_status, a.shelves_status, a.pack_id, a.pack_code, a.pack_name, a.receipt_status, a.shelves_status, a.pack_id, a.pack_code, a.pack_name,a.RESERVE_MATERIAL_NAME
CASE CASE
WHEN a.pack_detail_id IS NULL OR TRIM(a.pack_detail_id) = '' THEN NULL WHEN a.pack_detail_id IS NULL OR TRIM(a.pack_detail_id) = '' THEN NULL
ELSE CAST(a.pack_detail_id AS BIGINT) ELSE CAST(a.pack_detail_id AS BIGINT)