Merge branch 'dev_v3.0.4' into dev_user0622
This commit is contained in:
+3
@@ -34,6 +34,9 @@ public class ReservationDeliveryDetailsLink extends BaseVOEntity {
|
||||
@ApiModelProperty(value = "客户编号")
|
||||
private String customerNumber;
|
||||
|
||||
@ApiModelProperty(value = "客户id")
|
||||
private Long customerId;
|
||||
|
||||
@ApiModelProperty(value = "配送名称")
|
||||
private String deliveryName;
|
||||
|
||||
|
||||
+2
-1
@@ -42,7 +42,8 @@ public class ReservationDeliveryDetailsLinkPO extends BaseVOEntity {
|
||||
|
||||
@ApiModelProperty(value = "客户编号")
|
||||
private String customerNumber;
|
||||
|
||||
@ApiModelProperty(value = "客户id")
|
||||
private Long customerId;
|
||||
@ApiModelProperty(value = "配送名称")
|
||||
private String deliveryName;
|
||||
|
||||
|
||||
+2
-1
@@ -41,7 +41,8 @@ public class ReservationDeliveryDetailsLinkDO extends BaseVOEntity {
|
||||
|
||||
@ApiModelProperty(value = "客户编号")
|
||||
private String customerNumber;
|
||||
|
||||
@ApiModelProperty(value = "客户id")
|
||||
private Long customerId;
|
||||
@ApiModelProperty(value = "配送名称")
|
||||
private String deliveryName;
|
||||
|
||||
|
||||
+4
@@ -63,6 +63,10 @@ public class ReservationInMaterialDetail extends BaseVOEntity {
|
||||
@Excel(name = "物料名称")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("物料名称")
|
||||
@Excel(name = "物料名称")
|
||||
private String reserveMaterialName;
|
||||
|
||||
@ApiModelProperty("物料条形码")
|
||||
@Excel(name = "物料条形码")
|
||||
private String barCode;
|
||||
|
||||
+4
@@ -62,6 +62,10 @@ public class ReservationInMaterialDetailPO extends ReservationStockInOrderBasePO
|
||||
@Excel(name = "物料名称")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("物料名称")
|
||||
@Excel(name = "物料名称")
|
||||
private String reserveMaterialName;
|
||||
|
||||
@ApiModelProperty("物料条形码")
|
||||
@Excel(name = "物料条形码")
|
||||
private String barCode;
|
||||
|
||||
+4
@@ -60,6 +60,10 @@ public class ReservationInMaterialDetailDO extends ReservationStockInOrderBaseDO
|
||||
@Excel(name = "物料名称")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("物料名称")
|
||||
@Excel(name = "物料名称")
|
||||
private String reserveMaterialName;
|
||||
|
||||
@ApiModelProperty("物料条形码")
|
||||
@Excel(name = "物料条形码")
|
||||
private String barCode;
|
||||
|
||||
+4
@@ -63,6 +63,10 @@ public class ReservationInMaterialDetailDTO extends BaseVOEntity {
|
||||
@Excel(name = "物料名称")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("物料名称")
|
||||
@Excel(name = "物料名称")
|
||||
private String reserveMaterialName;
|
||||
|
||||
@ApiModelProperty("物料条形码")
|
||||
@Excel(name = "物料条形码")
|
||||
private String barCode;
|
||||
|
||||
+327
-370
@@ -17,14 +17,13 @@ import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 维他奶出库Excel解析监听器(最终完整版)
|
||||
* 维他奶出库Excel解析监听器(新模板版)
|
||||
* 新模板特点:货品明细与客户配送明细左右分栏展示
|
||||
* 核心功能:
|
||||
* 1. 正确解析多单元格数据(解决发票张数/纪录合共读取错误)
|
||||
* 2. 支持动态行数的货品明细/客户配送明细解析
|
||||
* 1. 左侧栏:第1-3行解析表头(送货日期/送货线号/车次),第5行解析纪录合共,第7行起解析货品明细
|
||||
* 2. 右侧栏:第2行解析发票张数/小计,第4行起解析客户配送明细
|
||||
* 3. 自动处理文本型数字转换(String→Integer/BigDecimal)
|
||||
* 4. 完整的异常处理和日志记录
|
||||
*/
|
||||
@@ -32,83 +31,101 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
// 日志实例
|
||||
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标签匹配)
|
||||
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_CUSTOMER_DETAIL = "客户配送明细";
|
||||
private static final String KEYWORD_INVOICE_COUNT = "發票張數";
|
||||
private static final String KEYWORD_TOTAL_COUNT = "總計";
|
||||
private static final String KEYWORD_SUBTOTAL = "小計";
|
||||
// 支持的日期格式(String转Date)
|
||||
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 List<ProductDetail> productDetailList = new ArrayList<>();
|
||||
private List<CustomerDeliveryDetail> customerDeliveryDetailList = new ArrayList<>();
|
||||
private final DeliveryHeader deliveryHeader = new DeliveryHeader();
|
||||
private final List<ProductDetail> productDetailList = new ArrayList<>();
|
||||
private final List<CustomerDeliveryDetail> customerDeliveryDetailList = new ArrayList<>();
|
||||
|
||||
// ---------------------- 解析状态管理(动态切换) ----------------------
|
||||
public enum ParseStatus {
|
||||
IDLE, // 空闲状态(未进入任何表格)
|
||||
PRODUCT_DETAIL,// 货品明细解析状态
|
||||
CUSTOMER_DETAIL// 客户配送明细解析状态
|
||||
}
|
||||
private ParseStatus currentStatus = ParseStatus.IDLE;
|
||||
// 当前行号(1-based,与Excel行号一致)
|
||||
private int currentRowNum = 0;
|
||||
|
||||
// ---------------------- 核心解析方法(EasyExcel回调) ----------------------
|
||||
@Override
|
||||
public void invoke(Map<String, Object> data, AnalysisContext context) {
|
||||
currentRowNum = context.readRowHolder().getRowIndex() + 1;
|
||||
try {
|
||||
int rowNum = context.readRowHolder().getRowIndex() + 1;
|
||||
LOGGER.debug("第{}行:开始解析,所有单元格数据:{}", rowNum, data);
|
||||
LOGGER.debug("第{}行:开始解析,所有单元格数据:{}", currentRowNum, data);
|
||||
|
||||
// 1. 获取第一列数据(用于状态判断,标签通常在第一列)
|
||||
String firstCellValue = getFirstCellValue(data);
|
||||
LOGGER.debug("第{}行:第一列数据(状态判断依据):{}", rowNum, firstCellValue);
|
||||
// 左侧区域解析(A-E列)
|
||||
parseLeftSection(data);
|
||||
|
||||
// 2. 状态切换判断(传递完整data,支持多单元格解析)
|
||||
if (isStatusSwitchTrigger(firstCellValue)) {
|
||||
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;
|
||||
}
|
||||
// 右侧区域解析(F-M列)
|
||||
parseRightSection(data);
|
||||
|
||||
} catch (ExcelDataConvertException e) {
|
||||
// Excel内置转换异常(如日期格式错误)
|
||||
int rowNum = context.readRowHolder().getRowIndex() + 1;
|
||||
LOGGER.error("第{}行:Excel数据转换异常,列号:{},列名:{}",
|
||||
rowNum, e.getColumnIndex() + 1, getColumnNameByIndex(data, e.getColumnIndex()), e);
|
||||
throw new RuntimeException("Excel数据转换异常(行:" + rowNum + ",列:" + (e.getColumnIndex() + 1) + ")", e);
|
||||
currentRowNum, e.getColumnIndex() + 1, getColumnNameByIndex(data, e.getColumnIndex()), e);
|
||||
throw new RuntimeException("Excel数据转换异常(行:" + currentRowNum + ",列:" + (e.getColumnIndex() + 1) + ")", e);
|
||||
} catch (Exception e) {
|
||||
// 其他解析异常
|
||||
int rowNum = context.readRowHolder().getRowIndex() + 1;
|
||||
LOGGER.error("第{}行:Excel解析异常,所有单元格数据:{}", rowNum, data, e);
|
||||
throw new RuntimeException("Excel解析异常(行:" + rowNum + ")", e);
|
||||
LOGGER.error("第{}行:Excel解析异常,所有单元格数据:{}", currentRowNum, data, e);
|
||||
throw new RuntimeException("Excel解析异常(行:" + currentRowNum + ")", 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
|
||||
public void doAfterAllAnalysed(AnalysisContext context) {
|
||||
LOGGER.info("Excel解析全部完成!最终统计结果:");
|
||||
LOGGER.info("1. 表头信息:送货日期={},发票张数={},总记录数={},小计金额={}",
|
||||
LOGGER.info("1. 表头信息:送货日期={},送货线号={},车次={},发票张数={},总记录数={},小计金额={}",
|
||||
deliveryHeader.getDeliveryDate(),
|
||||
deliveryHeader.getDeliveryLineNo(),
|
||||
deliveryHeader.getTrainNumber(),
|
||||
deliveryHeader.getInvoiceCount(),
|
||||
deliveryHeader.getTotalRecordCount(),
|
||||
deliveryHeader.getSubtotalAmount());
|
||||
@@ -125,253 +144,185 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
LOGGER.info("3. 客户配送明细:共{}条记录", customerDeliveryDetailList.size());
|
||||
}
|
||||
|
||||
// ---------------------- 状态管理方法(传递完整data) ----------------------
|
||||
/**
|
||||
* 判断是否触发状态切换(根据第一列标签关键字)
|
||||
*/
|
||||
private boolean isStatusSwitchTrigger(String firstCellValue) {
|
||||
return firstCellValue.contains(KEYWORD_PRODUCT_DETAIL)
|
||||
|| firstCellValue.contains(KEYWORD_TOTAL_RECORD)
|
||||
|| 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 parseDeliveryDate(Map<String, Object> data) {
|
||||
Object value = getValueSafely(data, 1); // B列
|
||||
if (value != null) {
|
||||
Date deliveryDate = convertToDate(value);
|
||||
deliveryHeader.setDeliveryDate(deliveryDate);
|
||||
LOGGER.debug("第{}行:送货日期解析完成,值={}", currentRowNum, deliveryDate);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------- 数据有效性校验方法 ----------------------
|
||||
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) {
|
||||
// 有效条件:1.货品编号(第一列)不为空 2.不是表头行(表头第一列是"貨品編號")
|
||||
Object productCode = getValueSafely(data, 0);
|
||||
private boolean isValidProductDetailRow(Map<String, Object> data) {
|
||||
Object productCode = getValueSafely(data, 0); // A列
|
||||
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) {
|
||||
// 有效条件:1.类别(第一列)不为空 2.不是表头行(表头第一列是"類别")
|
||||
Object category = getValueSafely(data, 0);
|
||||
private boolean isValidCustomerDetailRow(Map<String, Object> data) {
|
||||
Object category = getValueSafely(data, 5); // F列
|
||||
return category != null
|
||||
&& !"類别".equals(category.toString().trim())
|
||||
&& !category.toString().trim().isEmpty();
|
||||
}
|
||||
|
||||
// ---------------------- 具体数据解析方法(全部支持多单元格) ----------------------
|
||||
/**
|
||||
* 解析表头信息(送货日期、送货线号、车次)
|
||||
* 列对应关系: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);
|
||||
}
|
||||
&& !category.toString().trim().isEmpty()
|
||||
&& !"類别".equals(category.toString().trim());
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析纪录合共(多单元格版)
|
||||
* 列对应关系:0=紀錄合共标签,1=总记录数值,4=總計标签,5=总箱数值,6=总支数值
|
||||
*/
|
||||
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=金額
|
||||
* 解析客户配送明细(第4行起)
|
||||
* 列对应关系:5=F列=類别,6=G列=發票編號,7=H列=客户編號,8=I列=名稱,
|
||||
* 9=J列=箱,10=K列=包,11=L列=金額
|
||||
*/
|
||||
private void parseCustomerDeliveryDetail(Map<String, Object> data, int rowNum) {
|
||||
try {
|
||||
CustomerDeliveryDetail customerDetail = new CustomerDeliveryDetail();
|
||||
// 读取对应列的多单元格数据
|
||||
String category = getValueSafely(data, 0, String.class); // 第1列:类别
|
||||
String invoiceNo = getValueSafely(data, 1, String.class); // 第2列:发票编号
|
||||
String customerCode = getValueSafely(data, 2, String.class); // 第3列:客户编号
|
||||
String customerName = getValueSafely(data, 3, String.class); // 第4列:客户名称
|
||||
Integer boxCount = getValueSafely(data, 4, Integer.class); // 第5列:箱数
|
||||
Integer packageCount = getValueSafely(data, 5, Integer.class); // 第6列:包数
|
||||
BigDecimal amount = getValueSafely(data, 6, BigDecimal.class); // 第7列:金额
|
||||
CustomerDeliveryDetail customerDetail = new CustomerDeliveryDetail();
|
||||
customerDetail.setCategory(convertToString(getValueSafely(data, 5)));
|
||||
customerDetail.setInvoiceNo(convertToString(getValueSafely(data, 6)));
|
||||
customerDetail.setCustomerCode(convertToString(getValueSafely(data, 7)));
|
||||
customerDetail.setCustomerName(convertToString(getValueSafely(data, 8)));
|
||||
customerDetail.setBoxCount(convertToInteger(getValueSafely(data, 9)));
|
||||
customerDetail.setPackageCount(convertToInteger(getValueSafely(data, 10)));
|
||||
customerDetail.setAmount(convertToBigDecimal(getValueSafely(data, 11)));
|
||||
|
||||
// 赋值(空值处理+去空格)
|
||||
customerDetail.setCategory(getDefaultValue(category, "").trim());
|
||||
customerDetail.setInvoiceNo(getDefaultValue(invoiceNo, "").trim());
|
||||
customerDetail.setCustomerCode(getDefaultValue(customerCode, "").trim());
|
||||
customerDetail.setCustomerName(getDefaultValue(customerName, "").trim());
|
||||
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);
|
||||
}
|
||||
customerDeliveryDetailList.add(customerDetail);
|
||||
LOGGER.debug("第{}行:解析客户配送明细完成,类别={},发票编号={},客户编号={},名称={},箱数={},包数={},金额={}",
|
||||
rowNum, customerDetail.getCategory(), customerDetail.getInvoiceNo(),
|
||||
customerDetail.getCustomerCode(), customerDetail.getCustomerName(),
|
||||
customerDetail.getBoxCount(), customerDetail.getPackageCount(), customerDetail.getAmount());
|
||||
}
|
||||
|
||||
// ---------------------- 核心工具方法(支持多单元格+类型转换) ----------------------
|
||||
/**
|
||||
* 获取第一列数据(用于状态判断)
|
||||
* 根据列索引安全获取值
|
||||
* 优先按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()) {
|
||||
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) {
|
||||
if (data == null || data.isEmpty()) {
|
||||
private Integer findKeywordIndex(Map<String, Object> data, String keyword) {
|
||||
if (data == null || data.isEmpty() || keyword == null) {
|
||||
return null;
|
||||
}
|
||||
int index = 0;
|
||||
for (Object value : data.values()) {
|
||||
if (index == columnIndex) {
|
||||
return value;
|
||||
for (Object keyObj : data.keySet()) {
|
||||
Object value = data.get(keyObj);
|
||||
if (value != null && value.toString().trim().contains(keyword)) {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 按列索引安全获取值(支持多类型转换,核心工具)
|
||||
* 功能:1. 直接类型匹配 2. String转数字/日期 3. 数字类型间转换
|
||||
* 查找关键字所在列的右侧相邻列值,并转为Integer
|
||||
*/
|
||||
private <T> T getValueSafely(Map<String, Object> data, int columnIndex, Class<T> expectedType) {
|
||||
Object value = getValueSafely(data, columnIndex);
|
||||
if (value == null) {
|
||||
LOGGER.debug("列索引{}:单元格值为null,返回null", columnIndex);
|
||||
private Integer findValueAfterKeyword(Map<String, Object> data, String keyword) {
|
||||
Integer keywordIndex = findKeywordIndex(data, keyword);
|
||||
if (keywordIndex == null) {
|
||||
LOGGER.warn("第{}行:未找到关键字'{}'", currentRowNum, keyword);
|
||||
return null;
|
||||
}
|
||||
return convertToInteger(getValueSafely(data, keywordIndex + 1));
|
||||
}
|
||||
|
||||
// 1. 直接类型匹配
|
||||
if (expectedType.isInstance(value)) {
|
||||
return expectedType.cast(value);
|
||||
/**
|
||||
* Object转String
|
||||
*/
|
||||
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) {
|
||||
String strValue = ((String) value).trim();
|
||||
if (strValue.isEmpty()) {
|
||||
LOGGER.debug("列索引{}:单元格值为空字符串,返回null", columnIndex);
|
||||
return null;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// String → Integer
|
||||
if (expectedType == Integer.class) {
|
||||
try {
|
||||
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;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(strValue);
|
||||
} catch (NumberFormatException e) {
|
||||
LOGGER.warn("第{}行:值转换为Integer失败,值:{},返回0", currentRowNum, value, e);
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 数字类型间转换(如Double→Integer,Long→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;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 空值处理:返回默认值
|
||||
* Object转BigDecimal(支持千分位符号,如"1,234.56")
|
||||
*/
|
||||
private <T> T getDefaultValue(T value, T defaultValue) {
|
||||
return value != null ? value : defaultValue;
|
||||
private BigDecimal convertToBigDecimal(Object value) {
|
||||
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层获取解析结果) ----------------------
|
||||
@@ -515,4 +472,4 @@ public class DynamicDeliveryExcelListener extends AnalysisEventListener<Map<Stri
|
||||
public List<CustomerDeliveryDetail> getCustomerDeliveryDetailList() {
|
||||
return customerDeliveryDetailList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+64
-22
@@ -17,7 +17,7 @@ import java.util.Map;
|
||||
|
||||
/**
|
||||
* 入库单Excel解析监听器
|
||||
* 功能:1. 解析第一行表头数据 2. 解析第二行开始的产品明细 3. 支持文本型数字转换
|
||||
* 功能:1. 解析第一行表头数据 2. 跳过第二行合计行、第三行列名行 3. 解析第四行开始的产品明细 4. 支持文本型数字转换
|
||||
*/
|
||||
public class StockInExcelListener extends AnalysisEventListener<Map<String, Object>> {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(StockInExcelListener.class);
|
||||
@@ -44,8 +44,16 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
if (currentRowNum == 1) {
|
||||
parseHeaderData(data);
|
||||
}
|
||||
// 2. 解析第二行及以后:产品明细(跳过表头行:第2行是表格列名)
|
||||
else if (currentRowNum >= 3) {
|
||||
// 2. 跳过第二行:合计行(Total: xxx)
|
||||
else if (currentRowNum == 2) {
|
||||
LOGGER.debug("第{}行是合计行,跳过解析", currentRowNum);
|
||||
}
|
||||
// 3. 跳过第三行:表格列名行(產品編號、產品名稱...)
|
||||
else if (currentRowNum == 3) {
|
||||
LOGGER.debug("第{}行是表格列名行,跳过解析", currentRowNum);
|
||||
}
|
||||
// 4. 解析第四行及以后:产品明细
|
||||
else if (currentRowNum >= 4) {
|
||||
if (isValidProductRow(data)) {
|
||||
StockInProductDetail detail = parseProductDetail(data);
|
||||
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) {
|
||||
// 入库日期(第2列,索引1)
|
||||
Object stockInDateObj = getValueSafely(data, 1);
|
||||
// 入库日期:查找包含"入库日期"标签的下一个值
|
||||
Object stockInDateObj = getValueAfterLabel(data, "入库日期");
|
||||
if (stockInDateObj != null) {
|
||||
Date stockInDate = convertToDate(stockInDateObj);
|
||||
stockInHeader.setStockInDate(stockInDate);
|
||||
LOGGER.debug("第1行:入库日期解析完成,值:{}", stockInDate);
|
||||
}
|
||||
|
||||
// OrderNO(第4列,索引3)
|
||||
Object orderNoObj = getValueSafely(data, 3);
|
||||
// OrderNO:查找包含"OrderNO"标签的下一个值
|
||||
Object orderNoObj = getValueAfterLabel(data, "OrderNO");
|
||||
if (orderNoObj != null) {
|
||||
String orderNo = orderNoObj.toString().trim();
|
||||
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:{}",
|
||||
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) {
|
||||
StockInProductDetail detail = new StockInProductDetail();
|
||||
|
||||
// 产品编号(第1列,索引0)- 支持文本型数字转换
|
||||
detail.setProductCode(convertToString(getValueSafely(data, 0)).toString());
|
||||
detail.setProductCode(convertToString(getValueSafely(data, 0)));
|
||||
// 产品名称(第2列,索引1)
|
||||
detail.setProductName(convertToString(getValueSafely(data, 1)));
|
||||
// 箱含量(第5列,索引4)
|
||||
detail.setBoxCapacity(convertToInteger(getValueSafely(data, 4)));
|
||||
// 件数(第6列,索引5)
|
||||
detail.setPieceCount(convertToInteger(getValueSafely(data, 5)));
|
||||
// 箱数(第7列,索引6)
|
||||
detail.setBoxCount(convertToInteger(getValueSafely(data, 6)));
|
||||
// 饮用日期(第8列,索引7)
|
||||
detail.setDrinkDate(convertToDate(getValueSafely(data, 7)));
|
||||
// 箱含量/规格(第3列,索引2)
|
||||
detail.setBoxCapacity(convertToInteger(getValueSafely(data, 2)));
|
||||
// 件数(第4列,索引3)
|
||||
detail.setPieceCount(convertToInteger(getValueSafely(data, 3)));
|
||||
// 箱数/Qty(cases)(第5列,索引4)
|
||||
detail.setBoxCount(convertToInteger(getValueSafely(data, 4)));
|
||||
// 饮用日期(第6列,索引5)
|
||||
detail.setDrinkDate(convertToDate(getValueSafely(data, 5)));
|
||||
|
||||
LOGGER.debug("第{}行产品明细解析完成,产品编号:{},产品名称:{}",
|
||||
currentRowNum, detail.getProductCode(), detail.getProductName());
|
||||
@@ -120,12 +129,11 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
* 校验产品行是否有效(排除空行和合计行)
|
||||
*/
|
||||
private boolean isValidProductRow(Map<String, Object> data) {
|
||||
// 有效条件:产品编号不为空 + 不是合计行(合计行产品编号为null,且包含"Total")
|
||||
// 有效条件:产品编号不为空 + 不是合计行
|
||||
Object productCodeObj = getValueSafely(data, 0);
|
||||
Object productNameObj = getValueSafely(data, 1);
|
||||
|
||||
// 排除合计行(如第7行:Total:68)
|
||||
if (productNameObj != null && productNameObj.toString().contains("Total")) {
|
||||
// 排除合计行(整行包含"Total"字样)
|
||||
if (containsAnyValue(data, "Total")) {
|
||||
LOGGER.debug("第{}行是合计行,跳过解析", currentRowNum);
|
||||
return false;
|
||||
}
|
||||
@@ -186,6 +194,40 @@ public class StockInExcelListener extends AnalysisEventListener<Map<String, Obje
|
||||
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
|
||||
*/
|
||||
|
||||
+31
-14
@@ -174,9 +174,21 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
// 封装DTO(逻辑不变)
|
||||
DeliveryDataDTO deliveryDataDTO = new DeliveryDataDTO();
|
||||
deliveryDataDTO.setDeliveryHeader(listener.getDeliveryHeader());
|
||||
deliveryDataDTO.setProductDetailList(listener.getProductDetailList());
|
||||
deliveryDataDTO.setCustomerDeliveryDetailList(listener.getCustomerDeliveryDetailList());
|
||||
List<ProductDetail> productDetailList = deliveryDataDTO.getProductDetailList();
|
||||
// 判空处理:货品明细和客户配送明细可能为空列表
|
||||
List<ProductDetail> productDetailList = listener.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<>();
|
||||
Set<Long> processedShipperIds = new HashSet<>();
|
||||
for (ProductDetail productDetail : productDetailList) {
|
||||
@@ -192,20 +204,25 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (shipperIds.isEmpty()) {
|
||||
throw new Exception("出库物料货主不存在!");
|
||||
}
|
||||
if (shipperIds.size() > 1) {
|
||||
throw new Exception("出库物料不是同一个货主!");
|
||||
// 货品明细不为空时,校验货主信息
|
||||
if (!productDetailList.isEmpty()) {
|
||||
if (shipperIds.isEmpty()) {
|
||||
throw new Exception("出库物料货主不存在!");
|
||||
}
|
||||
if (shipperIds.size() > 1) {
|
||||
throw new Exception("出库物料不是同一个货主!");
|
||||
}
|
||||
}
|
||||
|
||||
ReservationStockOutOrderDO reservationStockOutOrderDO = new ReservationStockOutOrderDO();
|
||||
reservationStockOutOrderDO.setOutOrderNumber(OrderSequence.getOrderCode("CK"));
|
||||
reservationStockOutOrderDO.setShipperId((Long) shipperIds.get(0).get("shipperId"));
|
||||
reservationStockOutOrderDO.setShipperName((String) shipperIds.get(0).get("shipperName"));
|
||||
reservationStockOutOrderDO.setShipperCode((String) shipperIds.get(0).get("shipperCode"));
|
||||
// 货主信息:有货品明细时从解析结果取,无货品明细时从登录用户取
|
||||
if (!shipperIds.isEmpty()) {
|
||||
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.setNeedDeclareFlag("1");
|
||||
reservationStockOutOrderDO.setOrganizationId(loginUser.getUserPo().getOrganizationId());
|
||||
@@ -267,7 +284,7 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
reservationStockOutOrderDO.setMaterialDetailList(reservationOutMaterialDetailDOList);
|
||||
reservationStockOutOrderApplicationService.insert(reservationStockOutOrderDO);
|
||||
|
||||
List<CustomerDeliveryDetail> customerDeliveryDetailList = deliveryDataDTO.getCustomerDeliveryDetailList();
|
||||
// 客户配送明细判空处理:为空时跳过关联保存
|
||||
for (CustomerDeliveryDetail customerDeliveryDetail : customerDeliveryDetailList) {
|
||||
ReservationDeliveryDetailsLink reservationDeliveryDetailsLink = new ReservationDeliveryDetailsLink();
|
||||
reservationDeliveryDetailsLink.setInvoiceNumber(customerDeliveryDetail.getInvoiceNo());
|
||||
@@ -388,7 +405,7 @@ public class ReservationStockInOrderImpl extends ServiceImpl<ReservationStockInO
|
||||
reservationInMaterialDetailDO.setCreateByName(loginUser.getUserPo().getUserName());
|
||||
reservationInMaterialDetailDO.setCreateTime(new Date());
|
||||
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) {
|
||||
reservationInMaterialDetailDO.setMaterialBaseInfoId(materialBaseInfoPO.getMaterialBaseInfoId());
|
||||
reservationInMaterialDetailDO.setMaterialName(materialBaseInfoPO.getMaterialName());
|
||||
|
||||
+39
-33
@@ -295,43 +295,45 @@ public class ReservationStockOutOrderApplicationService {
|
||||
{
|
||||
ReservationStockOutOrderPO stockOutOrderPO = stockOutOrderDomainService.getInfoChildren(outOrderId);
|
||||
String userNcCode = reservationStockInOrderMapper.getUserNcCode(stockOutOrderPO.getShipperId());
|
||||
if (stockOutOrderPO == null || CollectionUtils.isEmpty(stockOutOrderPO.getMaterialDetailList())) {
|
||||
if (stockOutOrderPO == null ) {
|
||||
return stockOutOrderPO;
|
||||
}
|
||||
stockOutOrderPO.setKhdm(userNcCode);
|
||||
List<ReservationOutMaterialDetailPO> materialDetailList = stockOutOrderPO.getMaterialDetailList();
|
||||
if (CollectionUtils.isEmpty(materialDetailList)) {
|
||||
for (ReservationOutMaterialDetailPO reservationOutMaterialDetailPO : materialDetailList) {
|
||||
reservationOutMaterialDetailPO.setKhdm(userNcCode);
|
||||
if (stockOutOrderPO.getMaterialDetailList() != null && stockOutOrderPO.getMaterialDetailList().size() > 0) {
|
||||
List<ReservationOutMaterialDetailPO> materialDetailList = stockOutOrderPO.getMaterialDetailList();
|
||||
if (CollectionUtils.isEmpty(materialDetailList)) {
|
||||
for (ReservationOutMaterialDetailPO reservationOutMaterialDetailPO : materialDetailList) {
|
||||
reservationOutMaterialDetailPO.setKhdm(userNcCode);
|
||||
}
|
||||
stockOutOrderPO.setMaterialDetailList(materialDetailList);
|
||||
}
|
||||
stockOutOrderPO.setMaterialDetailList(materialDetailList);
|
||||
}
|
||||
List<ReservationOutMaterialDetailPO> detailList = stockOutOrderPO.getMaterialDetailList();
|
||||
String outOrderNumber = stockOutOrderPO.getOutOrderNumber();
|
||||
for (ReservationOutMaterialDetailPO reservationOutMaterialDetailPO : detailList) {
|
||||
Long materialBaseInfoId = reservationOutMaterialDetailPO.getMaterialBaseInfoId();
|
||||
List<InventoryAdjustmentRecordPO> inventoryAdjustmentRecordPOS = reservationInMaterialDetailService.getkctzjl(materialBaseInfoId, outOrderNumber, "出库");
|
||||
if (inventoryAdjustmentRecordPOS != null && inventoryAdjustmentRecordPOS.size() > 0) {
|
||||
InventoryAdjustmentRecordPO inventoryAdjustmentRecordPO = inventoryAdjustmentRecordPOS.get(0);
|
||||
reservationOutMaterialDetailPO.setAllocationBeforeQuantity(inventoryAdjustmentRecordPO.getAllocationBeforeQuantity());
|
||||
reservationOutMaterialDetailPO.setAllocationAfterQuantity(inventoryAdjustmentRecordPO.getAllocationAfterQuantity());
|
||||
reservationOutMaterialDetailPO.setFreezeAfterQuantity(inventoryAdjustmentRecordPO.getFreezeAfterQuantity());
|
||||
reservationOutMaterialDetailPO.setFreezeBeforeQuantity(inventoryAdjustmentRecordPO.getFreezeBeforeQuantity());
|
||||
reservationOutMaterialDetailPO.setInventoryAfterQuantity(inventoryAdjustmentRecordPO.getInventoryAfterQuantity());
|
||||
reservationOutMaterialDetailPO.setInventoryBeforeQuantity(inventoryAdjustmentRecordPO.getInventoryBeforeQuantity());
|
||||
reservationOutMaterialDetailPO.setNetWeight(inventoryAdjustmentRecordPO.getNetWeight());
|
||||
reservationOutMaterialDetailPO.setGrossWeight(inventoryAdjustmentRecordPO.getGrossWeight());
|
||||
reservationOutMaterialDetailPO.setVolume(inventoryAdjustmentRecordPO.getVolume());
|
||||
reservationOutMaterialDetailPO.setArea(inventoryAdjustmentRecordPO.getArea());
|
||||
reservationOutMaterialDetailPO.setAdjustedNetWeight(inventoryAdjustmentRecordPO.getAdjustedNetWeight());
|
||||
reservationOutMaterialDetailPO.setAdjustedGrossWeight(inventoryAdjustmentRecordPO.getAdjustedGrossWeight());
|
||||
reservationOutMaterialDetailPO.setAdjustedVolume(inventoryAdjustmentRecordPO.getAdjustedVolume());
|
||||
reservationOutMaterialDetailPO.setAdjustedArea(inventoryAdjustmentRecordPO.getAdjustedArea());
|
||||
}
|
||||
if(materialBaseInfoId != null){
|
||||
MaterialBaseInfoPO materialBaseInfoPO = reservationStockInOrderMapper.getinfo(materialBaseInfoId);
|
||||
if(materialBaseInfoPO != null && materialBaseInfoPO.getUnitPrice() != null){
|
||||
reservationOutMaterialDetailPO.setUnitPrice(materialBaseInfoPO.getUnitPrice());
|
||||
List<ReservationOutMaterialDetailPO> detailList = stockOutOrderPO.getMaterialDetailList();
|
||||
String outOrderNumber = stockOutOrderPO.getOutOrderNumber();
|
||||
for (ReservationOutMaterialDetailPO reservationOutMaterialDetailPO : detailList) {
|
||||
Long materialBaseInfoId = reservationOutMaterialDetailPO.getMaterialBaseInfoId();
|
||||
List<InventoryAdjustmentRecordPO> inventoryAdjustmentRecordPOS = reservationInMaterialDetailService.getkctzjl(materialBaseInfoId, outOrderNumber, "出库");
|
||||
if (inventoryAdjustmentRecordPOS != null && inventoryAdjustmentRecordPOS.size() > 0) {
|
||||
InventoryAdjustmentRecordPO inventoryAdjustmentRecordPO = inventoryAdjustmentRecordPOS.get(0);
|
||||
reservationOutMaterialDetailPO.setAllocationBeforeQuantity(inventoryAdjustmentRecordPO.getAllocationBeforeQuantity());
|
||||
reservationOutMaterialDetailPO.setAllocationAfterQuantity(inventoryAdjustmentRecordPO.getAllocationAfterQuantity());
|
||||
reservationOutMaterialDetailPO.setFreezeAfterQuantity(inventoryAdjustmentRecordPO.getFreezeAfterQuantity());
|
||||
reservationOutMaterialDetailPO.setFreezeBeforeQuantity(inventoryAdjustmentRecordPO.getFreezeBeforeQuantity());
|
||||
reservationOutMaterialDetailPO.setInventoryAfterQuantity(inventoryAdjustmentRecordPO.getInventoryAfterQuantity());
|
||||
reservationOutMaterialDetailPO.setInventoryBeforeQuantity(inventoryAdjustmentRecordPO.getInventoryBeforeQuantity());
|
||||
reservationOutMaterialDetailPO.setNetWeight(inventoryAdjustmentRecordPO.getNetWeight());
|
||||
reservationOutMaterialDetailPO.setGrossWeight(inventoryAdjustmentRecordPO.getGrossWeight());
|
||||
reservationOutMaterialDetailPO.setVolume(inventoryAdjustmentRecordPO.getVolume());
|
||||
reservationOutMaterialDetailPO.setArea(inventoryAdjustmentRecordPO.getArea());
|
||||
reservationOutMaterialDetailPO.setAdjustedNetWeight(inventoryAdjustmentRecordPO.getAdjustedNetWeight());
|
||||
reservationOutMaterialDetailPO.setAdjustedGrossWeight(inventoryAdjustmentRecordPO.getAdjustedGrossWeight());
|
||||
reservationOutMaterialDetailPO.setAdjustedVolume(inventoryAdjustmentRecordPO.getAdjustedVolume());
|
||||
reservationOutMaterialDetailPO.setAdjustedArea(inventoryAdjustmentRecordPO.getAdjustedArea());
|
||||
}
|
||||
if(materialBaseInfoId != null){
|
||||
MaterialBaseInfoPO materialBaseInfoPO = reservationStockInOrderMapper.getinfo(materialBaseInfoId);
|
||||
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>()
|
||||
.eq(ReservationDeliveryDetailsLink::getOrderNumber, stockOutOrderPO.getOutOrderNumber()));
|
||||
stockOutOrderPO.setReservationDeliveryDetailsLinkList(reservationDeliveryDetailsLinks);
|
||||
Long shipperId = stockOutOrderPO.getShipperId();
|
||||
if (shipperId != null && shipperId == 0L) {
|
||||
stockOutOrderPO.setShipperId(null);
|
||||
}
|
||||
return stockOutOrderPO;
|
||||
}
|
||||
|
||||
|
||||
+6
-2
@@ -148,7 +148,11 @@ public class ReservationStockOutOrderDomainService {
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -979,7 +983,7 @@ public class ReservationStockOutOrderDomainService {
|
||||
reservationStockInOrderMapper.insert(reservationStockInOrder);
|
||||
reservationDeliveryDetailsLink.setReturnOrderNumber(reservationStockInOrder.getInOrderNumber());
|
||||
reservationDeliveryDetailsLinkService.updateById(reservationDeliveryDetailsLink);
|
||||
reservationDeliveryDetailsLinkService.updateW(reservationStockInOrder.getInOrderNumber(), reservationDeliveryDetailsLink.getOrderNumber());
|
||||
reservationDeliveryDetailsLinkService.updateW(reservationStockInOrder.getInOrderNumber(), reservationDeliveryDetailsLink.getInvoiceNumber());
|
||||
String returnOrderNumber = stockOutOrder.getReturnOrderNumber();
|
||||
if (returnOrderNumber == null) {
|
||||
stockOutOrder.setReturnOrderNumber(reservationStockInOrder.getInOrderNumber());
|
||||
|
||||
+4
@@ -63,6 +63,10 @@ public class ReserveInMaterialDetail extends BaseVOEntity {
|
||||
@Excel(name = "物料名称")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("物料名称")
|
||||
@Excel(name = "物料名称")
|
||||
private String reserveMaterialName;
|
||||
|
||||
@ApiModelProperty("物料条形码")
|
||||
@Excel(name = "物料条形码")
|
||||
private String barCode;
|
||||
|
||||
+4
@@ -63,6 +63,10 @@ public class ReserveInMaterialDetailPO extends ReserveStockInOrderBasePO {
|
||||
@Excel(name = "物料名称")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("物料名称")
|
||||
@Excel(name = "物料名称")
|
||||
private String reserveMaterialName;
|
||||
|
||||
@ApiModelProperty("物料条形码")
|
||||
@Excel(name = "物料条形码")
|
||||
private String barCode;
|
||||
|
||||
+4
@@ -61,6 +61,10 @@ public class ReserveInMaterialDetailDO extends ReserveStockInOrderBaseDO {
|
||||
@Excel(name = "物料名称")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("物料名称")
|
||||
@Excel(name = "物料名称")
|
||||
private String reserveMaterialName;
|
||||
|
||||
@ApiModelProperty("物料条形码")
|
||||
@Excel(name = "物料条形码")
|
||||
private String barCode;
|
||||
|
||||
+4
@@ -63,6 +63,10 @@ public class ReserveInMaterialDetailDTO extends BaseVOEntity {
|
||||
@Excel(name = "物料名称")
|
||||
private String materialName;
|
||||
|
||||
@ApiModelProperty("物料名称")
|
||||
@Excel(name = "物料名称")
|
||||
private String reserveMaterialName;
|
||||
|
||||
@ApiModelProperty("物料条形码")
|
||||
@Excel(name = "物料条形码")
|
||||
private String barCode;
|
||||
|
||||
+1
-1
@@ -670,7 +670,7 @@ public class ReserveStockInOrderDomainService {
|
||||
MaterialBaseInfoPO materialBaseInfoPO = reservationStockInOrderMapper.getinfo(inMaterialDetailDO.getMaterialBaseInfoId());
|
||||
//MaterialBaseInfoPO materialBaseInfoPO = materialBaseInfoService.getInfo(inMaterialDetailDO.getMaterialBaseInfoId());
|
||||
if (materialBaseInfoPO == null){
|
||||
throw new ServiceException("物料基础信息不存在");
|
||||
return;
|
||||
}
|
||||
inMaterialDetailDO.setMaterialCode(materialBaseInfoPO.getMaterialCode());
|
||||
inMaterialDetailDO.setMaterialName(materialBaseInfoPO.getMaterialName());
|
||||
|
||||
+10
-6
@@ -209,24 +209,28 @@ public class ReservationStockInOrderApi extends BaseController {
|
||||
|
||||
|
||||
/**
|
||||
* 上传并解析Excel文件
|
||||
* 上传并解析Excel文件(默认读取"出库导入"工作表)
|
||||
* @param file Excel文件
|
||||
* @param sheetName 工作表名称(出库导入/参考数据)
|
||||
* @return 解析后的出库数据
|
||||
*/
|
||||
@PostMapping("/parseExcel")
|
||||
public ResponseEntity<Map<String, Object>> parseExcel(
|
||||
@RequestParam("file") MultipartFile file) {
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
try {
|
||||
DeliveryDataDTO deliveryDataDTO = reservationStockInOrderService.parseExcelFile(file, "");
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("result", deliveryDataDTO);
|
||||
result.put("code",200);
|
||||
result.put("code", 200);
|
||||
result.put("msg", "解析成功");
|
||||
return new ResponseEntity<>(result, HttpStatus.OK);
|
||||
} 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) {
|
||||
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
result.put("code", 500);
|
||||
result.put("msg", e.getMessage());
|
||||
return new ResponseEntity<>(result, HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -61,6 +61,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
</select>
|
||||
|
||||
<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>
|
||||
</mapper>
|
||||
+2
-1
@@ -96,6 +96,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="reservePrice" column="reserve_price" />
|
||||
<result property="reserveUnitPrice" column="reserve_unit_price" />
|
||||
<result property="shelfLife" column="shelf_life" />
|
||||
<result property="reserveMaterialName" column="RESERVE_MATERIAL_NAME" />
|
||||
</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.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.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
|
||||
WHEN a.pack_detail_id IS NULL OR TRIM(a.pack_detail_id) = '' THEN NULL
|
||||
ELSE CAST(a.pack_detail_id AS BIGINT)
|
||||
|
||||
Reference in New Issue
Block a user