批次规则bug修改
This commit is contained in:
+10
-9
@@ -1616,23 +1616,20 @@ public class StockInOrderApplicationService {
|
||||
// 如果编号不匹配,再根据名称匹配(支持去除空格后匹配)
|
||||
if (!matched && StringUtils.isNotEmpty(normalizedShipperName) && userName != null) {
|
||||
String normalizedUserName = String.valueOf(userName).trim();
|
||||
// 精确匹配
|
||||
// 精确匹配(优先)
|
||||
if (normalizedShipperName.equals(normalizedUserName)) {
|
||||
matched = true;
|
||||
log.debug("通过名称精确匹配成功,货主名称:{},货主ID:{}", normalizedShipperName, userId);
|
||||
}
|
||||
// 如果精确匹配失败,尝试包含匹配(支持部分匹配)
|
||||
// 如果精确匹配失败,尝试包含匹配(列表中的名称包含导入的名称,例如列表中是"珠海百思科鞋材有限公司",导入的是"百思科")
|
||||
// 注意:这里不使用反向包含匹配(导入的名称包含列表中的名称),因为这样会误匹配(例如导入"珠海百思科鞋材有限公司"会匹配到"百思科")
|
||||
if (!matched && normalizedUserName.contains(normalizedShipperName)) {
|
||||
matched = true;
|
||||
log.debug("通过名称包含匹配成功,货主名称:{},列表中的名称:{},货主ID:{}",
|
||||
normalizedShipperName, normalizedUserName, userId);
|
||||
}
|
||||
// 如果包含匹配也失败,尝试反向包含匹配
|
||||
if (!matched && normalizedShipperName.contains(normalizedUserName)) {
|
||||
matched = true;
|
||||
log.debug("通过名称反向包含匹配成功,货主名称:{},列表中的名称:{},货主ID:{}",
|
||||
normalizedShipperName, normalizedUserName, userId);
|
||||
}
|
||||
// 不再使用反向包含匹配,避免误匹配
|
||||
// 例如:导入"珠海百思科鞋材有限公司"不应该匹配到列表中的"百思科"
|
||||
}
|
||||
|
||||
if (matched && userId != null) {
|
||||
@@ -1698,7 +1695,11 @@ public class StockInOrderApplicationService {
|
||||
throw new ServiceException("获取货主信息失败:返回数据为空(货主ID: " + shipperId + ")");
|
||||
}
|
||||
stockInOrderDO.setShipperCode(userPo.getUserMemberCode());
|
||||
stockInOrderDO.setShipperName(userPo.getUserName());
|
||||
// 如果原始shipperName不为空,保留原始值(导入时读取的客户名称应该保留)
|
||||
// 只有在原始shipperName为空时,才使用查询到的userName
|
||||
if (StringUtils.isBlank(stockInOrderDO.getShipperName())) {
|
||||
stockInOrderDO.setShipperName(userPo.getUserName());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+362
-7
@@ -43,12 +43,17 @@ import org.springframework.stereotype.Service;
|
||||
import com.mhd.common.core.utils.StringUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -136,23 +141,52 @@ public class StockReceiptOrderApplicationService {
|
||||
receiptMaterialDetailDO.setReceiptOrderNumber(stockReceiptOrderPO.getReceiptOrderNumber());
|
||||
List<ReceiptMaterialDetailPO> receiptMaterialDetailPOList = receiptMaterialDetailDomainService.queryListChildren(receiptMaterialDetailDO);
|
||||
|
||||
// 为每个物料明细设置更多属性(根据物料绑定的批次属性,过滤isDisplay=1的属性)
|
||||
receiptMaterialDetailPOList.forEach(receiptMaterialDetailPO -> {
|
||||
List<MaterialMoreDetailPO> materialMoreDetailList =
|
||||
getMaterialMoreDetailByBatch(receiptMaterialDetailPO.getMaterialBaseInfoId());
|
||||
receiptMaterialDetailPO.setMaterialMoreDetailList(materialMoreDetailList);
|
||||
});
|
||||
// 性能优化:批量获取批次属性,避免N+1查询问题
|
||||
// 1. 收集所有唯一的materialBaseInfoId
|
||||
Set<Long> materialBaseInfoIdSet = receiptMaterialDetailPOList.stream()
|
||||
.map(ReceiptMaterialDetailPO::getMaterialBaseInfoId)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
// 2. 批量查询批次属性(使用Map缓存,避免重复查询相同的materialBaseInfoId)
|
||||
Map<Long, List<BatchDetailFeignPO>> batchDetailMap = new HashMap<>();
|
||||
for (Long materialBaseInfoId : materialBaseInfoIdSet) {
|
||||
try {
|
||||
AjaxResult ajaxResult = systemServiceFeign.getBatchDetailListByMaterialBaseInfoId(materialBaseInfoId);
|
||||
if (ajaxResult != null && "200".equals(String.valueOf(ajaxResult.get("code")))) {
|
||||
List<BatchDetailFeignPO> batchDetailFeignPOList = JSON.parseArray(
|
||||
JSONObject.toJSONString(ajaxResult.get("data")), BatchDetailFeignPO.class);
|
||||
if (!CollectionUtils.isEmpty(batchDetailFeignPOList)) {
|
||||
// 过滤isDisplay=1的属性
|
||||
List<BatchDetailFeignPO> filteredList = batchDetailFeignPOList.stream()
|
||||
.filter(batchDetail -> batchDetail.getIsDisplay() != null && batchDetail.getIsDisplay() == 1)
|
||||
.collect(Collectors.toList());
|
||||
batchDetailMap.put(materialBaseInfoId, filteredList);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("获取物料批次属性失败,物料基础信息ID:{},错误:{}", materialBaseInfoId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 为每个物料明细设置更多属性(从缓存中获取),递归处理所有层级
|
||||
setMaterialMoreDetailListRecursively(receiptMaterialDetailPOList, batchDetailMap);
|
||||
|
||||
stockReceiptOrderPO.setMaterialDetailList(receiptMaterialDetailPOList);
|
||||
log.info("返回收货单信息,receiptOrderId={}, materialDetailList大小={}",
|
||||
stockReceiptOrderPO.getReceiptOrderId(),
|
||||
receiptMaterialDetailPOList != null ? receiptMaterialDetailPOList.size() : 0);
|
||||
return stockReceiptOrderPO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据物料基础信息ID获取批次属性(更多属性),只返回isDisplay=1的属性
|
||||
* 并根据fieldName从物料明细中获取对应的字段值
|
||||
* @param materialBaseInfoId 物料基础信息ID
|
||||
* @param receiptMaterialDetailPO 收货物料明细对象,用于根据fieldName获取字段值
|
||||
* @return 更多属性列表
|
||||
*/
|
||||
private List<MaterialMoreDetailPO> getMaterialMoreDetailByBatch(Long materialBaseInfoId) {
|
||||
private List<MaterialMoreDetailPO> getMaterialMoreDetailByBatch(Long materialBaseInfoId, ReceiptMaterialDetailPO receiptMaterialDetailPO) {
|
||||
List<MaterialMoreDetailPO> materialMoreDetailPOList = new ArrayList<>();
|
||||
if (materialBaseInfoId == null) {
|
||||
return materialMoreDetailPOList;
|
||||
@@ -178,6 +212,32 @@ public class StockReceiptOrderApplicationService {
|
||||
materialMoreDetailPO.setAttributeFormat(batchDetailFeignPO.getAttributeFormat());
|
||||
materialMoreDetailPO.setAttributeOption(batchDetailFeignPO.getAttributeOption());
|
||||
materialMoreDetailPO.setRemark(batchDetailFeignPO.getRemark());
|
||||
|
||||
// 根据fieldName或batchLabels从物料明细中获取对应的字段值
|
||||
if (receiptMaterialDetailPO != null) {
|
||||
String fieldValue = null;
|
||||
String usedFieldName = null;
|
||||
// 优先使用fieldName(如果存在)
|
||||
if (StringUtils.isNotBlank(batchDetailFeignPO.getFieldName())) {
|
||||
usedFieldName = batchDetailFeignPO.getFieldName();
|
||||
fieldValue = getFieldValueByFieldName(receiptMaterialDetailPO, usedFieldName);
|
||||
log.debug("批次属性 batchDetailId={}, fieldName={}, 获取到的值={}",
|
||||
batchDetailFeignPO.getBatchDetailId(), usedFieldName, fieldValue);
|
||||
}
|
||||
// 如果fieldName为空,尝试根据batchLabels映射到字段名
|
||||
else if (StringUtils.isNotBlank(batchDetailFeignPO.getBatchLabels())) {
|
||||
String fieldNameByLabel = getFieldNameByBatchLabel(batchDetailFeignPO.getBatchLabels());
|
||||
if (StringUtils.isNotBlank(fieldNameByLabel)) {
|
||||
usedFieldName = fieldNameByLabel;
|
||||
fieldValue = getFieldValueByFieldName(receiptMaterialDetailPO, usedFieldName);
|
||||
log.debug("批次属性 batchDetailId={}, batchLabels={}, 映射字段名={}, 获取到的值={}",
|
||||
batchDetailFeignPO.getBatchDetailId(), batchDetailFeignPO.getBatchLabels(),
|
||||
usedFieldName, fieldValue);
|
||||
}
|
||||
}
|
||||
materialMoreDetailPO.setAttributeValue(fieldValue);
|
||||
}
|
||||
|
||||
materialMoreDetailPOList.add(materialMoreDetailPO);
|
||||
});
|
||||
}
|
||||
@@ -188,6 +248,300 @@ public class StockReceiptOrderApplicationService {
|
||||
|
||||
return materialMoreDetailPOList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 递归为物料明细设置更多属性
|
||||
* @param receiptMaterialDetailPOList 物料明细列表
|
||||
* @param batchDetailMap 批次属性Map
|
||||
*/
|
||||
private void setMaterialMoreDetailListRecursively(List<ReceiptMaterialDetailPO> receiptMaterialDetailPOList,
|
||||
Map<Long, List<BatchDetailFeignPO>> batchDetailMap) {
|
||||
if (CollectionUtils.isEmpty(receiptMaterialDetailPOList)) {
|
||||
return;
|
||||
}
|
||||
|
||||
receiptMaterialDetailPOList.forEach(receiptMaterialDetailPO -> {
|
||||
Long materialBaseInfoId = receiptMaterialDetailPO.getMaterialBaseInfoId();
|
||||
|
||||
// 只保留isDisplay=1的批次属性(从batchDetailMap中获取,已经过滤过了)
|
||||
// 创建新的materialMoreDetailList,只包含isDisplay=1的属性
|
||||
List<MaterialMoreDetailPO> materialMoreDetailList = new ArrayList<>();
|
||||
|
||||
// 创建batchDetailId到MaterialMoreDetailPO的映射,用于快速查找已有的属性值
|
||||
Map<Long, MaterialMoreDetailPO> batchDetailIdToMaterialMoreDetailMap = new HashMap<>();
|
||||
List<MaterialMoreDetailPO> existingList = receiptMaterialDetailPO.getMaterialMoreDetailList();
|
||||
if (!CollectionUtils.isEmpty(existingList)) {
|
||||
for (MaterialMoreDetailPO existing : existingList) {
|
||||
if (existing.getBatchDetailId() != null) {
|
||||
batchDetailIdToMaterialMoreDetailMap.put(existing.getBatchDetailId(), existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (materialBaseInfoId != null && batchDetailMap.containsKey(materialBaseInfoId)) {
|
||||
List<BatchDetailFeignPO> batchDetailFeignPOList = batchDetailMap.get(materialBaseInfoId);
|
||||
// 用于跟踪已添加到列表中的batchDetailId
|
||||
Set<Long> addedBatchDetailIds = new HashSet<>();
|
||||
|
||||
for (BatchDetailFeignPO batchDetailFeignPO : batchDetailFeignPOList) {
|
||||
MaterialMoreDetailPO materialMoreDetailPO = batchDetailIdToMaterialMoreDetailMap.get(batchDetailFeignPO.getBatchDetailId());
|
||||
|
||||
// 如果不存在,创建新的
|
||||
if (materialMoreDetailPO == null) {
|
||||
materialMoreDetailPO = new MaterialMoreDetailPO();
|
||||
materialMoreDetailPO.setBatchId(batchDetailFeignPO.getBatchId());
|
||||
materialMoreDetailPO.setBatchDetailId(batchDetailFeignPO.getBatchDetailId());
|
||||
materialMoreDetailPO.setBatchLabels(batchDetailFeignPO.getBatchLabels());
|
||||
materialMoreDetailPO.setInputControl(batchDetailFeignPO.getInputControl());
|
||||
materialMoreDetailPO.setAttributeFormat(batchDetailFeignPO.getAttributeFormat());
|
||||
materialMoreDetailPO.setAttributeOption(batchDetailFeignPO.getAttributeOption());
|
||||
materialMoreDetailPO.setRemark(batchDetailFeignPO.getRemark());
|
||||
} else {
|
||||
// 如果已存在,更新属性(保留已有的attributeValue)
|
||||
materialMoreDetailPO.setBatchId(batchDetailFeignPO.getBatchId());
|
||||
materialMoreDetailPO.setBatchLabels(batchDetailFeignPO.getBatchLabels());
|
||||
materialMoreDetailPO.setInputControl(batchDetailFeignPO.getInputControl());
|
||||
materialMoreDetailPO.setAttributeFormat(batchDetailFeignPO.getAttributeFormat());
|
||||
materialMoreDetailPO.setAttributeOption(batchDetailFeignPO.getAttributeOption());
|
||||
materialMoreDetailPO.setRemark(batchDetailFeignPO.getRemark());
|
||||
}
|
||||
|
||||
// 确保添加到列表中(使用batchDetailId来避免重复)
|
||||
Long batchDetailId = batchDetailFeignPO.getBatchDetailId();
|
||||
if (batchDetailId != null && !addedBatchDetailIds.contains(batchDetailId)) {
|
||||
materialMoreDetailList.add(materialMoreDetailPO);
|
||||
addedBatchDetailIds.add(batchDetailId);
|
||||
}
|
||||
|
||||
// 根据fieldName或batchLabels从物料明细中获取对应的字段值
|
||||
if (receiptMaterialDetailPO != null) {
|
||||
String fieldValue = null;
|
||||
String usedFieldName = null;
|
||||
// 优先使用fieldName(如果存在)
|
||||
if (StringUtils.isNotBlank(batchDetailFeignPO.getFieldName())) {
|
||||
usedFieldName = batchDetailFeignPO.getFieldName();
|
||||
fieldValue = getFieldValueByFieldName(receiptMaterialDetailPO, usedFieldName);
|
||||
log.info("批次属性 batchDetailId={}, fieldName={}, 获取到的值={}",
|
||||
batchDetailFeignPO.getBatchDetailId(), usedFieldName, fieldValue);
|
||||
}
|
||||
// 如果fieldName为空,尝试根据batchLabels映射到字段名
|
||||
else if (StringUtils.isNotBlank(batchDetailFeignPO.getBatchLabels())) {
|
||||
String fieldNameByLabel = getFieldNameByBatchLabel(batchDetailFeignPO.getBatchLabels());
|
||||
if (StringUtils.isNotBlank(fieldNameByLabel)) {
|
||||
usedFieldName = fieldNameByLabel;
|
||||
fieldValue = getFieldValueByFieldName(receiptMaterialDetailPO, usedFieldName);
|
||||
log.info("批次属性 batchDetailId={}, batchLabels={}, 映射字段名={}, 获取到的值={}",
|
||||
batchDetailFeignPO.getBatchDetailId(), batchDetailFeignPO.getBatchLabels(),
|
||||
usedFieldName, fieldValue);
|
||||
}
|
||||
}
|
||||
// 只有当获取到值时才更新attributeValue(避免覆盖已有的值)
|
||||
if (fieldValue != null) {
|
||||
materialMoreDetailPO.setAttributeValue(fieldValue);
|
||||
log.info("设置更多属性:batchDetailId={}, batchLabels={}, attributeValue={}",
|
||||
materialMoreDetailPO.getBatchDetailId(), materialMoreDetailPO.getBatchLabels(),
|
||||
materialMoreDetailPO.getAttributeValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
receiptMaterialDetailPO.setMaterialMoreDetailList(materialMoreDetailList);
|
||||
log.info("物料明细 uniqueId={}, materialBaseInfoId={}, materialMoreDetailList大小={}",
|
||||
receiptMaterialDetailPO.getUniqueId(), receiptMaterialDetailPO.getMaterialBaseInfoId(),
|
||||
materialMoreDetailList != null ? materialMoreDetailList.size() : 0);
|
||||
|
||||
// 递归处理子集
|
||||
if (!CollectionUtils.isEmpty(receiptMaterialDetailPO.getChildren())) {
|
||||
setMaterialMoreDetailListRecursively(receiptMaterialDetailPO.getChildren(), batchDetailMap);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据批次标签(batchLabels)映射到字段名
|
||||
* @param batchLabel 批次标签
|
||||
* @return 字段名,如果找不到映射则返回null
|
||||
*/
|
||||
private String getFieldNameByBatchLabel(String batchLabel) {
|
||||
if (StringUtils.isBlank(batchLabel)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 批次标签到字段名的映射关系
|
||||
// 可以根据实际业务需求扩展这个映射表
|
||||
Map<String, String> labelToFieldNameMap = new HashMap<>();
|
||||
labelToFieldNameMap.put("Invoice No.", "invoiceNo");
|
||||
labelToFieldNameMap.put("Invoice No", "invoiceNo");
|
||||
labelToFieldNameMap.put("发票号", "invoiceNo");
|
||||
labelToFieldNameMap.put("Batch Ref NO's", "batchRefNo");
|
||||
labelToFieldNameMap.put("Batch Ref NO", "batchRefNo");
|
||||
labelToFieldNameMap.put("批次参考号", "batchRefNo");
|
||||
labelToFieldNameMap.put("Sheet Ref NO's", "sheetRefNo");
|
||||
labelToFieldNameMap.put("Sheet Ref NO", "sheetRefNo");
|
||||
labelToFieldNameMap.put("单据参考号", "sheetRefNo");
|
||||
labelToFieldNameMap.put("箱号/卡板号", "boxPalletNo");
|
||||
labelToFieldNameMap.put("箱号", "boxPalletNo");
|
||||
labelToFieldNameMap.put("卡板号", "boxPalletNo");
|
||||
labelToFieldNameMap.put("申报数量", "declaredQuantity");
|
||||
labelToFieldNameMap.put("申报单位", "declarationUnit");
|
||||
labelToFieldNameMap.put("原产国", "originCountry");
|
||||
labelToFieldNameMap.put("币制", "currency");
|
||||
labelToFieldNameMap.put("单位", "unit");
|
||||
labelToFieldNameMap.put("升", "liter");
|
||||
labelToFieldNameMap.put("尺寸", "dimensions");
|
||||
labelToFieldNameMap.put("箱数", "boxCount");
|
||||
labelToFieldNameMap.put("件数", "pieceCount");
|
||||
labelToFieldNameMap.put("入库数量", "inboundQuantity");
|
||||
labelToFieldNameMap.put("总净重", "totalNetWeight");
|
||||
labelToFieldNameMap.put("总毛重", "totalGrossWeight");
|
||||
labelToFieldNameMap.put("总体积", "totalVolume");
|
||||
labelToFieldNameMap.put("总面积", "totalArea");
|
||||
labelToFieldNameMap.put("总价", "totalPrice");
|
||||
|
||||
// 精确匹配
|
||||
String fieldName = labelToFieldNameMap.get(batchLabel.trim());
|
||||
if (StringUtils.isNotBlank(fieldName)) {
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
// 模糊匹配(包含关系)
|
||||
for (Map.Entry<String, String> entry : labelToFieldNameMap.entrySet()) {
|
||||
if (batchLabel.contains(entry.getKey()) || entry.getKey().contains(batchLabel)) {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
// 如果找不到映射,尝试将批次标签转换为驼峰命名(简单处理)
|
||||
// 例如:"Invoice No." -> "invoiceNo"
|
||||
return convertLabelToFieldName(batchLabel);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将批次标签转换为字段名(驼峰命名)
|
||||
* @param label 批次标签
|
||||
* @return 字段名
|
||||
*/
|
||||
private String convertLabelToFieldName(String label) {
|
||||
if (StringUtils.isBlank(label)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 移除特殊字符,转换为小写,用空格或特殊字符分割
|
||||
String cleaned = label.replaceAll("[^a-zA-Z0-9\\u4e00-\\u9fa5\\s]", " ")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
|
||||
// 如果是中文,无法自动转换,返回null
|
||||
if (cleaned.matches(".*[\\u4e00-\\u9fa5].*")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 将空格分割的单词转换为驼峰命名
|
||||
String[] words = cleaned.split("\\s+");
|
||||
if (words.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
StringBuilder fieldName = new StringBuilder(words[0]);
|
||||
for (int i = 1; i < words.length; i++) {
|
||||
if (words[i].length() > 0) {
|
||||
fieldName.append(words[i].substring(0, 1).toUpperCase())
|
||||
.append(words[i].substring(1));
|
||||
}
|
||||
}
|
||||
|
||||
return fieldName.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据字段名从对象中获取字段值(使用反射)
|
||||
* @param obj 对象
|
||||
* @param fieldName 字段名
|
||||
* @return 字段值(转换为字符串)
|
||||
*/
|
||||
private String getFieldValueByFieldName(Object obj, String fieldName) {
|
||||
if (obj == null || StringUtils.isBlank(fieldName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
// 兼容:如果传的是下划线命名(数据库列名风格),先转成驼峰字段名再取
|
||||
// 例如:box_pallet_no -> boxPalletNo
|
||||
String normalizedFieldName = fieldName;
|
||||
if (normalizedFieldName.contains("_")) {
|
||||
normalizedFieldName = snakeToCamel(normalizedFieldName);
|
||||
}
|
||||
|
||||
// 获取对象的Class
|
||||
Class<?> clazz = obj.getClass();
|
||||
|
||||
// 尝试获取字段(包括父类字段)
|
||||
Field field = null;
|
||||
Class<?> currentClass = clazz;
|
||||
while (currentClass != null && field == null) {
|
||||
try {
|
||||
field = currentClass.getDeclaredField(normalizedFieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
currentClass = currentClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
if (field == null) {
|
||||
log.warn("字段不存在:{}(normalized:{}),对象类型:{}", fieldName, normalizedFieldName, clazz.getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
// 设置可访问
|
||||
field.setAccessible(true);
|
||||
|
||||
// 获取字段值
|
||||
Object value = field.get(obj);
|
||||
|
||||
// 转换为字符串
|
||||
if (value == null) {
|
||||
log.info("字段 {} 的值为 null,对象类型:{}", normalizedFieldName, clazz.getName());
|
||||
return null;
|
||||
}
|
||||
|
||||
String result;
|
||||
// 处理日期类型字段
|
||||
if (value instanceof Date) {
|
||||
result = DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD, (Date) value);
|
||||
} else {
|
||||
result = value.toString();
|
||||
}
|
||||
|
||||
log.info("通过反射获取字段值成功,字段名:{}(原始:{}),值:{},对象类型:{}",
|
||||
normalizedFieldName, fieldName, result, clazz.getName());
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
log.warn("通过反射获取字段值失败,字段名:{},对象类型:{},错误:{}", fieldName, obj.getClass().getName(), e.getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String snakeToCamel(String snake) {
|
||||
if (StringUtils.isBlank(snake)) {
|
||||
return null;
|
||||
}
|
||||
String s = snake.trim().toLowerCase();
|
||||
StringBuilder sb = new StringBuilder();
|
||||
boolean upperNext = false;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
if (c == '_') {
|
||||
upperNext = true;
|
||||
continue;
|
||||
}
|
||||
if (upperNext) {
|
||||
sb.append(Character.toUpperCase(c));
|
||||
upperNext = false;
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取收货单详细信息
|
||||
@@ -836,3 +1190,4 @@ public class StockReceiptOrderApplicationService {
|
||||
receiptMaterialDetailDO.setDistributeRuleName(materialGoodsRulePO.getRuleName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+33
@@ -10,6 +10,8 @@ import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
|
||||
/**
|
||||
@@ -348,4 +350,35 @@ public class InMaterialDetailPO extends StockInOrderBasePO {
|
||||
@Excel(name = "总价")
|
||||
private BigDecimal totalPrice;
|
||||
|
||||
@ApiModelProperty("生产日期")
|
||||
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date productionDate;
|
||||
|
||||
@ApiModelProperty("过期日期")
|
||||
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date expiryDate;
|
||||
|
||||
@ApiModelProperty("存货日期")
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("扩展属性1")
|
||||
@Excel(name = "扩展属性1")
|
||||
private String extAttr1;
|
||||
|
||||
@ApiModelProperty("扩展属性2")
|
||||
@Excel(name = "扩展属性2")
|
||||
private String extAttr2;
|
||||
|
||||
@ApiModelProperty("扩展属性3")
|
||||
@Excel(name = "扩展属性3")
|
||||
private String extAttr3;
|
||||
|
||||
@ApiModelProperty("扩展属性4")
|
||||
@Excel(name = "扩展属性4")
|
||||
private String extAttr4;
|
||||
|
||||
}
|
||||
|
||||
+68
@@ -214,5 +214,73 @@ public class ReceiptMaterialDetail extends BaseVOEntity {
|
||||
@Excel(name = "备注")
|
||||
private String remark;
|
||||
|
||||
@ApiModelProperty("总净重(KG)")
|
||||
@Excel(name = "总净重(KG)")
|
||||
private BigDecimal totalNetWeight;
|
||||
|
||||
@ApiModelProperty("总毛重(KG)")
|
||||
@Excel(name = "总毛重(KG)")
|
||||
private BigDecimal totalGrossWeight;
|
||||
|
||||
@ApiModelProperty("总体积(CBM)")
|
||||
@Excel(name = "总体积(CBM)")
|
||||
private BigDecimal totalVolume;
|
||||
|
||||
@ApiModelProperty("总面积(SQM)")
|
||||
@Excel(name = "总面积(SQM)")
|
||||
private BigDecimal totalArea;
|
||||
|
||||
@ApiModelProperty("Invoice No.")
|
||||
@Excel(name = "Invoice No.")
|
||||
private String invoiceNo;
|
||||
|
||||
@ApiModelProperty("Batch Ref NO's")
|
||||
@Excel(name = "Batch Ref NO's")
|
||||
private String batchRefNo;
|
||||
|
||||
@ApiModelProperty("Sheet Ref NO's")
|
||||
@Excel(name = "Sheet Ref NO's")
|
||||
private String sheetRefNo;
|
||||
|
||||
@ApiModelProperty("箱号/卡板号")
|
||||
@Excel(name = "箱号/卡板号")
|
||||
private String boxPalletNo;
|
||||
|
||||
@ApiModelProperty("申报数量")
|
||||
@Excel(name = "申报数量")
|
||||
private BigDecimal declaredQuantity;
|
||||
|
||||
@ApiModelProperty("申报单位")
|
||||
@Excel(name = "申报单位")
|
||||
private String declarationUnit;
|
||||
|
||||
@ApiModelProperty("原产国")
|
||||
@Excel(name = "原产国")
|
||||
private String originCountry;
|
||||
|
||||
@ApiModelProperty("币制")
|
||||
@Excel(name = "币制")
|
||||
private String currency;
|
||||
|
||||
@ApiModelProperty("单位")
|
||||
@Excel(name = "单位")
|
||||
private String unit;
|
||||
|
||||
@ApiModelProperty("升")
|
||||
@Excel(name = "升")
|
||||
private BigDecimal liter;
|
||||
|
||||
@ApiModelProperty("尺寸")
|
||||
@Excel(name = "尺寸")
|
||||
private String dimensions;
|
||||
|
||||
@ApiModelProperty("箱数")
|
||||
@Excel(name = "箱数")
|
||||
private BigDecimal boxCount;
|
||||
|
||||
@ApiModelProperty("件数")
|
||||
@Excel(name = "件数")
|
||||
private BigDecimal pieceCount;
|
||||
|
||||
|
||||
}
|
||||
|
||||
+158
-31
@@ -13,6 +13,7 @@ import com.mhd.common.core.utils.uuid.IdGenerator;
|
||||
import com.mhd.common.core.web.domain.AjaxResult;
|
||||
import com.mhd.common.security.utils.SecurityUtils;
|
||||
import com.mhd.system.api.SystemServiceFeign;
|
||||
import com.mhd.system.api.domain.BatchDetailFeignPO;
|
||||
import com.mhd.system.api.model.LoginUser;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.mhd.wms.domain.inMaterialDetail.entity.InMaterialDetail;
|
||||
@@ -43,11 +44,15 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import com.mhd.common.core.utils.StringUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -56,6 +61,7 @@ import java.util.stream.Collectors;
|
||||
* @author gen
|
||||
* @date 2024-06-17
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetailMapper, ReceiptMaterialDetail> implements IReceiptMaterialDetailService{
|
||||
@Autowired
|
||||
@@ -291,7 +297,7 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
||||
if (!CollectionUtils.isEmpty(materialDetailSerialNumberList)){
|
||||
inMaterialDetailSerialNumberService.saveBatch(materialDetailSerialNumberList);
|
||||
}
|
||||
//从materialMoreDetailList中根据attributeOption提取字段值并更新入库单明细
|
||||
//从materialMoreDetailList中提取字段值并更新入库单明细(优先使用batchDetailId->fieldName映射,兼容attributeOption)
|
||||
updateInMaterialDetailFromAttributeOption(receiptMaterialDetailDOList);
|
||||
}
|
||||
|
||||
@@ -306,64 +312,185 @@ public class ReceiptMaterialDetailImpl extends ServiceImpl<ReceiptMaterialDetail
|
||||
if (CollectionUtils.isEmpty(receiptMaterialDetailDOList)) {
|
||||
return;
|
||||
}
|
||||
// 缓存:materialBaseInfoId -> (batchDetailId -> fieldName)
|
||||
Map<Long, Map<Long, String>> batchFieldNameCache = new HashMap<>();
|
||||
updateInMaterialDetailFromAttributeOption(receiptMaterialDetailDOList, batchFieldNameCache);
|
||||
}
|
||||
|
||||
private void updateInMaterialDetailFromAttributeOption(List<ReceiptMaterialDetailDO> receiptMaterialDetailDOList,
|
||||
Map<Long, Map<Long, String>> batchFieldNameCache) {
|
||||
if (CollectionUtils.isEmpty(receiptMaterialDetailDOList)) {
|
||||
return;
|
||||
}
|
||||
LoginUser loginUser = SecurityUtils.getLoginUser();
|
||||
for (ReceiptMaterialDetailDO receiptMaterialDetailDO : receiptMaterialDetailDOList) {
|
||||
Long inUniqueId = receiptMaterialDetailDO.getInUniqueId();
|
||||
if (inUniqueId == null) {
|
||||
// 处理子项
|
||||
List<ReceiptMaterialDetailDO> children = receiptMaterialDetailDO.getChildren();
|
||||
if (!CollectionUtils.isEmpty(children)) {
|
||||
updateInMaterialDetailFromAttributeOption(children, batchFieldNameCache);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
List<MaterialMoreDetailDO> materialMoreDetailList = receiptMaterialDetailDO.getMaterialMoreDetailList();
|
||||
if (CollectionUtils.isEmpty(materialMoreDetailList)) {
|
||||
// 处理子项
|
||||
List<ReceiptMaterialDetailDO> children = receiptMaterialDetailDO.getChildren();
|
||||
if (!CollectionUtils.isEmpty(children)) {
|
||||
updateInMaterialDetailFromAttributeOption(children, batchFieldNameCache);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// 构建UpdateWrapper
|
||||
|
||||
// 构建UpdateWrapper(用列名动态set)
|
||||
UpdateWrapper<InMaterialDetail> updateWrapper = new UpdateWrapper<>();
|
||||
updateWrapper.lambda().eq(InMaterialDetail::getUniqueId, inUniqueId);
|
||||
updateWrapper.eq("unique_id", inUniqueId);
|
||||
boolean hasUpdate = false;
|
||||
|
||||
// 遍历materialMoreDetailList,根据attributeOption映射到对应字段
|
||||
|
||||
// 构建 batchDetailId -> fieldName 映射(按物料维度缓存)
|
||||
Map<Long, String> batchDetailIdToFieldName = null;
|
||||
Long materialBaseInfoId = receiptMaterialDetailDO.getMaterialBaseInfoId();
|
||||
if (materialBaseInfoId != null) {
|
||||
batchDetailIdToFieldName = batchFieldNameCache.get(materialBaseInfoId);
|
||||
if (batchDetailIdToFieldName == null) {
|
||||
batchDetailIdToFieldName = loadBatchDetailIdToFieldName(materialBaseInfoId);
|
||||
batchFieldNameCache.put(materialBaseInfoId, batchDetailIdToFieldName);
|
||||
}
|
||||
}
|
||||
|
||||
for (MaterialMoreDetailDO materialMoreDetail : materialMoreDetailList) {
|
||||
String attributeOption = materialMoreDetail.getAttributeOption();
|
||||
String attributeValue = materialMoreDetail.getAttributeValue();
|
||||
|
||||
// 如果attributeValue为空,跳过
|
||||
if (attributeValue == null || attributeValue.trim().isEmpty()) {
|
||||
if (StringUtils.isBlank(attributeValue)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据attributeOption映射到入库单明细表的字段
|
||||
if ("InvoiceNo".equalsIgnoreCase(attributeOption)) {
|
||||
updateWrapper.lambda().set(InMaterialDetail::getInvoiceNo, attributeValue);
|
||||
hasUpdate = true;
|
||||
} else if ("BatchRefNo".equalsIgnoreCase(attributeOption)) {
|
||||
updateWrapper.lambda().set(InMaterialDetail::getBatchRefNo, attributeValue);
|
||||
hasUpdate = true;
|
||||
} else if ("SheetRefNo".equalsIgnoreCase(attributeOption)) {
|
||||
updateWrapper.lambda().set(InMaterialDetail::getSheetRefNo, attributeValue);
|
||||
hasUpdate = true;
|
||||
} else if ("BoxPalletNo".equalsIgnoreCase(attributeOption)) {
|
||||
updateWrapper.lambda().set(InMaterialDetail::getBoxPalletNo, attributeValue);
|
||||
hasUpdate = true;
|
||||
|
||||
String fieldName = null;
|
||||
Long batchDetailId = materialMoreDetail.getBatchDetailId();
|
||||
if (batchDetailId != null && batchDetailIdToFieldName != null) {
|
||||
fieldName = batchDetailIdToFieldName.get(batchDetailId);
|
||||
}
|
||||
|
||||
// 兼容旧逻辑:fieldName取不到时用attributeOption(例如 InvoiceNo -> invoiceNo)
|
||||
if (StringUtils.isBlank(fieldName)) {
|
||||
String attributeOption = materialMoreDetail.getAttributeOption();
|
||||
fieldName = lowerFirst(attributeOption);
|
||||
}
|
||||
|
||||
if (StringUtils.isBlank(fieldName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// camelCase -> snake_case 列名
|
||||
String columnName = camelToSnake(fieldName);
|
||||
if (StringUtils.isBlank(columnName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// 数字字段尝试转BigDecimal,其它按String
|
||||
Object val = coerceInMaterialDetailValue(fieldName, attributeValue);
|
||||
updateWrapper.set(columnName, val);
|
||||
hasUpdate = true;
|
||||
}
|
||||
|
||||
// 如果有字段需要更新,则执行更新
|
||||
|
||||
if (hasUpdate) {
|
||||
updateWrapper.lambda()
|
||||
.set(InMaterialDetail::getUpdateBy, loginUser.getUserid())
|
||||
.set(InMaterialDetail::getUpdateByName, loginUser.getUsername())
|
||||
.set(InMaterialDetail::getUpdateTime, new Date());
|
||||
updateWrapper
|
||||
.set("update_by", loginUser.getUserid())
|
||||
.set("update_by_name", loginUser.getUsername())
|
||||
.set("update_time", new Date());
|
||||
iInMaterialDetailService.update(updateWrapper);
|
||||
}
|
||||
|
||||
|
||||
// 处理子项
|
||||
List<ReceiptMaterialDetailDO> children = receiptMaterialDetailDO.getChildren();
|
||||
if (!CollectionUtils.isEmpty(children)) {
|
||||
updateInMaterialDetailFromAttributeOption(children);
|
||||
updateInMaterialDetailFromAttributeOption(children, batchFieldNameCache);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Long, String> loadBatchDetailIdToFieldName(Long materialBaseInfoId) {
|
||||
Map<Long, String> result = new HashMap<>();
|
||||
try {
|
||||
AjaxResult ajaxResult = systemServiceFeign.getBatchDetailListByMaterialBaseInfoId(materialBaseInfoId);
|
||||
if (ajaxResult == null || !"200".equals(String.valueOf(ajaxResult.get("code")))) {
|
||||
return result;
|
||||
}
|
||||
List<BatchDetailFeignPO> list = JSON.parseArray(JSON.toJSONString(ajaxResult.get("data")), BatchDetailFeignPO.class);
|
||||
if (CollectionUtils.isEmpty(list)) {
|
||||
return result;
|
||||
}
|
||||
for (BatchDetailFeignPO po : list) {
|
||||
if (po.getBatchDetailId() == null) {
|
||||
continue;
|
||||
}
|
||||
String fieldName = po.getFieldName();
|
||||
if (StringUtils.isBlank(fieldName)) {
|
||||
// 兼容:fieldName为空时尝试用attributeOption
|
||||
fieldName = lowerFirst(po.getAttributeOption());
|
||||
}
|
||||
if (StringUtils.isNotBlank(fieldName)) {
|
||||
result.put(po.getBatchDetailId(), fieldName);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("加载批次属性fieldName失败,materialBaseInfoId:{},错误:{}", materialBaseInfoId, e.getMessage());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String lowerFirst(String s) {
|
||||
if (StringUtils.isBlank(s)) {
|
||||
return null;
|
||||
}
|
||||
if (s.length() == 1) {
|
||||
return s.toLowerCase();
|
||||
}
|
||||
return s.substring(0, 1).toLowerCase() + s.substring(1);
|
||||
}
|
||||
|
||||
private String camelToSnake(String camel) {
|
||||
if (StringUtils.isBlank(camel)) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < camel.length(); i++) {
|
||||
char c = camel.charAt(i);
|
||||
if (Character.isUpperCase(c)) {
|
||||
if (i > 0) {
|
||||
sb.append('_');
|
||||
}
|
||||
sb.append(Character.toLowerCase(c));
|
||||
} else {
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private Object coerceInMaterialDetailValue(String fieldName, String raw) {
|
||||
if (raw == null) {
|
||||
return null;
|
||||
}
|
||||
// 这些字段在库里通常是NUMBER
|
||||
if ("liter".equals(fieldName)
|
||||
|| "declaredQuantity".equals(fieldName)
|
||||
|| "boxCount".equals(fieldName)
|
||||
|| "pieceCount".equals(fieldName)
|
||||
|| "totalNetWeight".equals(fieldName)
|
||||
|| "totalGrossWeight".equals(fieldName)
|
||||
|| "totalVolume".equals(fieldName)
|
||||
|| "totalArea".equals(fieldName)) {
|
||||
try {
|
||||
return new BigDecimal(raw.trim());
|
||||
} catch (Exception ignored) {
|
||||
return raw;
|
||||
}
|
||||
}
|
||||
return raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 从入库单明细表中获取InvoiceNo、BatchRefNo、SheetRefNo、BoxPalletNo字段的值并填充到materialMoreDetailList的attributeValue中
|
||||
* @author Auto
|
||||
|
||||
+33
@@ -12,6 +12,8 @@ import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
|
||||
/**
|
||||
@@ -252,6 +254,37 @@ public class ReceiptMaterialDetailPO extends BaseVOEntity {
|
||||
@Excel(name = "箱号/卡板号")
|
||||
private String boxPalletNo;
|
||||
|
||||
@ApiModelProperty("生产日期")
|
||||
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date productionDate;
|
||||
|
||||
@ApiModelProperty("过期日期")
|
||||
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date expiryDate;
|
||||
|
||||
@ApiModelProperty("存货日期")
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("扩展属性1")
|
||||
@Excel(name = "扩展属性1")
|
||||
private String extAttr1;
|
||||
|
||||
@ApiModelProperty("扩展属性2")
|
||||
@Excel(name = "扩展属性2")
|
||||
private String extAttr2;
|
||||
|
||||
@ApiModelProperty("扩展属性3")
|
||||
@Excel(name = "扩展属性3")
|
||||
private String extAttr3;
|
||||
|
||||
@ApiModelProperty("扩展属性4")
|
||||
@Excel(name = "扩展属性4")
|
||||
private String extAttr4;
|
||||
|
||||
@ApiModelProperty("物料明细子集")
|
||||
private List<ReceiptMaterialDetailPO> children;
|
||||
|
||||
|
||||
+65
@@ -11,6 +11,8 @@ import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Date;
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
|
||||
|
||||
/**
|
||||
@@ -218,6 +220,69 @@ public class ReceiptMaterialDetailDO extends BaseVOEntity {
|
||||
@Excel(name = "是否生成上架单: 1-是 2-否")
|
||||
private Integer genStockShelf;
|
||||
|
||||
@ApiModelProperty("总净重(KG)")
|
||||
@Excel(name = "总净重(KG)")
|
||||
private BigDecimal totalNetWeight;
|
||||
|
||||
@ApiModelProperty("总毛重(KG)")
|
||||
@Excel(name = "总毛重(KG)")
|
||||
private BigDecimal totalGrossWeight;
|
||||
|
||||
@ApiModelProperty("总体积(CBM)")
|
||||
@Excel(name = "总体积(CBM)")
|
||||
private BigDecimal totalVolume;
|
||||
|
||||
@ApiModelProperty("总面积(SQM)")
|
||||
@Excel(name = "总面积(SQM)")
|
||||
private BigDecimal totalArea;
|
||||
|
||||
@ApiModelProperty("Invoice No.")
|
||||
@Excel(name = "Invoice No.")
|
||||
private String invoiceNo;
|
||||
|
||||
@ApiModelProperty("Batch Ref NO's")
|
||||
@Excel(name = "Batch Ref NO's")
|
||||
private String batchRefNo;
|
||||
|
||||
@ApiModelProperty("Sheet Ref NO's")
|
||||
@Excel(name = "Sheet Ref NO's")
|
||||
private String sheetRefNo;
|
||||
|
||||
@ApiModelProperty("箱号/卡板号")
|
||||
@Excel(name = "箱号/卡板号")
|
||||
private String boxPalletNo;
|
||||
|
||||
@ApiModelProperty("生产日期")
|
||||
@Excel(name = "生产日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date productionDate;
|
||||
|
||||
@ApiModelProperty("过期日期")
|
||||
@Excel(name = "过期日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date expiryDate;
|
||||
|
||||
@ApiModelProperty("存货日期")
|
||||
@Excel(name = "存货日期", width = 30, dateFormat = "yyyy-MM-dd")
|
||||
@JsonFormat(pattern = "yyyy-MM-dd")
|
||||
private Date inventoryDate;
|
||||
|
||||
@ApiModelProperty("扩展属性1")
|
||||
@Excel(name = "扩展属性1")
|
||||
private String extAttr1;
|
||||
|
||||
@ApiModelProperty("扩展属性2")
|
||||
@Excel(name = "扩展属性2")
|
||||
private String extAttr2;
|
||||
|
||||
@ApiModelProperty("扩展属性3")
|
||||
@Excel(name = "扩展属性3")
|
||||
private String extAttr3;
|
||||
|
||||
@ApiModelProperty("扩展属性4")
|
||||
@Excel(name = "扩展属性4")
|
||||
private String extAttr4;
|
||||
|
||||
@ApiModelProperty(name = "组织ID集合")
|
||||
private List<Long> organizationIdList;
|
||||
|
||||
|
||||
+198
@@ -40,6 +40,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -297,6 +298,11 @@ public class StockInOrderDomainService {
|
||||
ReceiptMaterialDetailDO receiptMaterialDetailDO = new ReceiptMaterialDetailDO();
|
||||
BeanUtils.copyProperties(inMaterialDetailPO, receiptMaterialDetailDO);
|
||||
receiptMaterialDetailDO.setInUniqueId(inMaterialDetailPO.getUniqueId());
|
||||
|
||||
// 复制特定字段:生产日期、过期日期、存货日期、批次参考号、单据参考号、箱号/卡板号、扩展属性1-4
|
||||
// 这些字段可能使用驼峰命名或下划线命名,使用反射获取和设置
|
||||
copySpecialFields(inMaterialDetailPO, receiptMaterialDetailDO);
|
||||
|
||||
receiptMaterialDetailDOList.add(receiptMaterialDetailDO);
|
||||
});
|
||||
stockReceiptOrderDO.setMaterialDetailList(receiptMaterialDetailDOList);
|
||||
@@ -452,4 +458,196 @@ public class StockInOrderDomainService {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 复制特定字段从入库单明细到收货明细
|
||||
* 包括:生产日期、过期日期、存货日期、批次参考号、单据参考号、箱号/卡板号、扩展属性1-4
|
||||
* @author gen
|
||||
* @date 2026/1/29
|
||||
* @param source 源对象(InMaterialDetailPO)
|
||||
* @param target 目标对象(ReceiptMaterialDetailDO)
|
||||
*/
|
||||
private void copySpecialFields(InMaterialDetailPO source, ReceiptMaterialDetailDO target) {
|
||||
if (source == null || target == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 字段映射:源字段名 -> 目标字段名(支持驼峰和下划线两种命名方式)
|
||||
Map<String, String> fieldMappings = new HashMap<>();
|
||||
fieldMappings.put("productionDate", "productionDate");
|
||||
fieldMappings.put("production_date", "productionDate");
|
||||
fieldMappings.put("expiryDate", "expiryDate");
|
||||
fieldMappings.put("expiry_date", "expiryDate");
|
||||
fieldMappings.put("inventoryDate", "inventoryDate");
|
||||
fieldMappings.put("inventory_date", "inventoryDate");
|
||||
fieldMappings.put("batchRefNo", "batchRefNo");
|
||||
fieldMappings.put("batch_ref_no", "batchRefNo");
|
||||
fieldMappings.put("sheetRefNo", "sheetRefNo");
|
||||
fieldMappings.put("sheet_ref_no", "sheetRefNo");
|
||||
fieldMappings.put("boxPalletNo", "boxPalletNo");
|
||||
fieldMappings.put("box_pallet_no", "boxPalletNo");
|
||||
fieldMappings.put("extAttr1", "extAttr1");
|
||||
fieldMappings.put("ext_attr_1", "extAttr1");
|
||||
fieldMappings.put("extAttr2", "extAttr2");
|
||||
fieldMappings.put("ext_attr_2", "extAttr2");
|
||||
fieldMappings.put("extAttr3", "extAttr3");
|
||||
fieldMappings.put("ext_attr_3", "extAttr3");
|
||||
fieldMappings.put("extAttr4", "extAttr4");
|
||||
fieldMappings.put("ext_attr_4", "extAttr4");
|
||||
|
||||
// 使用反射复制字段值
|
||||
for (Map.Entry<String, String> entry : fieldMappings.entrySet()) {
|
||||
String sourceFieldName = entry.getKey();
|
||||
String targetFieldName = entry.getValue();
|
||||
|
||||
// 尝试从源对象获取字段值(支持驼峰和下划线命名)
|
||||
Object value = getFieldValue(source, sourceFieldName);
|
||||
if (value != null) {
|
||||
// 设置到目标对象(使用驼峰命名)
|
||||
setFieldValue(target, targetFieldName, value);
|
||||
log.info("复制字段成功:{} -> {},值:{}", sourceFieldName, targetFieldName, value);
|
||||
} else {
|
||||
log.debug("源对象字段 {} 的值为 null,跳过复制", sourceFieldName);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.warn("复制特定字段时发生异常,源对象:{},目标对象:{},错误:{}",
|
||||
source.getClass().getSimpleName(), target.getClass().getSimpleName(), e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 使用反射获取字段值(支持驼峰和下划线命名)
|
||||
*/
|
||||
private Object getFieldValue(Object obj, String fieldName) {
|
||||
if (obj == null || StringUtils.isBlank(fieldName)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> clazz = obj.getClass();
|
||||
Field field = null;
|
||||
|
||||
// 先尝试直接获取字段(驼峰命名)
|
||||
try {
|
||||
field = clazz.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
// 如果失败,尝试转换为驼峰命名(如果是下划线命名)
|
||||
if (fieldName.contains("_")) {
|
||||
String camelCaseName = snakeToCamel(fieldName);
|
||||
try {
|
||||
field = clazz.getDeclaredField(camelCaseName);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
// 继续查找父类
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 如果当前类找不到,查找父类
|
||||
if (field == null) {
|
||||
Class<?> currentClass = clazz.getSuperclass();
|
||||
while (currentClass != null && field == null) {
|
||||
try {
|
||||
field = currentClass.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
if (fieldName.contains("_")) {
|
||||
String camelCaseName = snakeToCamel(fieldName);
|
||||
try {
|
||||
field = currentClass.getDeclaredField(camelCaseName);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
// 继续查找父类
|
||||
}
|
||||
}
|
||||
}
|
||||
currentClass = currentClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
if (field != null) {
|
||||
field.setAccessible(true);
|
||||
return field.get(obj);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("获取字段值失败,对象:{},字段名:{},错误:{}",
|
||||
obj.getClass().getSimpleName(), fieldName, e.getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 使用反射设置字段值
|
||||
*/
|
||||
private void setFieldValue(Object obj, String fieldName, Object value) {
|
||||
if (obj == null || StringUtils.isBlank(fieldName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> clazz = obj.getClass();
|
||||
Field field = null;
|
||||
|
||||
// 先尝试直接获取字段(驼峰命名)
|
||||
try {
|
||||
field = clazz.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException e) {
|
||||
// 如果失败,查找父类
|
||||
Class<?> currentClass = clazz.getSuperclass();
|
||||
while (currentClass != null && field == null) {
|
||||
try {
|
||||
field = currentClass.getDeclaredField(fieldName);
|
||||
} catch (NoSuchFieldException ex) {
|
||||
// 继续查找父类
|
||||
}
|
||||
currentClass = currentClass.getSuperclass();
|
||||
}
|
||||
}
|
||||
|
||||
if (field != null) {
|
||||
field.setAccessible(true);
|
||||
// 类型转换:如果值类型不匹配,尝试转换
|
||||
Class<?> fieldType = field.getType();
|
||||
if (value != null && !fieldType.isAssignableFrom(value.getClass())) {
|
||||
// 尝试类型转换
|
||||
if (fieldType == Date.class && value instanceof String) {
|
||||
// 字符串转日期(简单处理,实际可能需要更复杂的解析)
|
||||
value = null; // 暂时不转换,避免解析错误
|
||||
} else if (fieldType == String.class) {
|
||||
value = String.valueOf(value);
|
||||
}
|
||||
}
|
||||
if (value != null && fieldType.isAssignableFrom(value.getClass())) {
|
||||
field.set(obj, value);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.debug("设置字段值失败,对象:{},字段名:{},值:{},错误:{}",
|
||||
obj.getClass().getSimpleName(), fieldName, value, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 下划线命名转驼峰命名
|
||||
*/
|
||||
private String snakeToCamel(String snakeCase) {
|
||||
if (StringUtils.isBlank(snakeCase)) {
|
||||
return snakeCase;
|
||||
}
|
||||
|
||||
String[] parts = snakeCase.split("_");
|
||||
if (parts.length == 0) {
|
||||
return snakeCase;
|
||||
}
|
||||
|
||||
StringBuilder camelCase = new StringBuilder(parts[0].toLowerCase());
|
||||
for (int i = 1; i < parts.length; i++) {
|
||||
if (parts[i].length() > 0) {
|
||||
camelCase.append(parts[i].substring(0, 1).toUpperCase())
|
||||
.append(parts[i].substring(1).toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
return camelCase.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -73,6 +73,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="totalVolume" column="TOTAL_VOLUME" />
|
||||
<result property="totalArea" column="TOTAL_AREA" />
|
||||
<result property="totalPrice" column="TOTAL_PRICE" />
|
||||
<result property="productionDate" column="PRODUCTION_DATE" />
|
||||
<result property="expiryDate" column="EXPIRY_DATE" />
|
||||
<result property="inventoryDate" column="INVENTORY_DATE" />
|
||||
<result property="extAttr1" column="EXT_ATTR_1" />
|
||||
<result property="extAttr2" column="EXT_ATTR_2" />
|
||||
<result property="extAttr3" column="EXT_ATTR_3" />
|
||||
<result property="extAttr4" column="EXT_ATTR_4" />
|
||||
</resultMap>
|
||||
|
||||
|
||||
@@ -91,7 +98,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
a.level, a.parent_unique_id, a.remark, a.create_time, a.create_by,a.create_by_name, a.update_time, a.update_by, a.update_by_name, a.del_flag, a.over_stock_id, a.over_stock_status, a.over_stock_type,
|
||||
a.SPECIFICATION_MODEL, a.SKU_CODE, a.INVOICE_NO, a.LITER, a.DIMENSIONS, a.DECLARATION_UNIT, a.BATCH_REF_NO, a.SHEET_REF_NO,
|
||||
a.ORIGIN_COUNTRY, a.BOX_PALLET_NO, a.CURRENCY, a.UNIT, a.DECLARED_QUANTITY, a.BOX_COUNT, a.PIECE_COUNT, a.INBOUND_QUANTITY,
|
||||
a.TOTAL_NET_WEIGHT, a.TOTAL_GROSS_WEIGHT, a.TOTAL_VOLUME, a.TOTAL_AREA, a.TOTAL_PRICE
|
||||
a.TOTAL_NET_WEIGHT, a.TOTAL_GROSS_WEIGHT, a.TOTAL_VOLUME, a.TOTAL_AREA, a.TOTAL_PRICE,
|
||||
a.PRODUCTION_DATE, a.EXPIRY_DATE, a.INVENTORY_DATE, a.EXT_ATTR_1, a.EXT_ATTR_2, a.EXT_ATTR_3, a.EXT_ATTR_4
|
||||
,CASE
|
||||
WHEN (a.quantity - a.receipt_quantity) < 0 THEN 0
|
||||
ELSE (a.quantity - a.receipt_quantity)
|
||||
|
||||
+26
-5
@@ -62,10 +62,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
<result property="totalGrossWeight" column="total_gross_weight" />
|
||||
<result property="totalVolume" column="total_volume" />
|
||||
<result property="totalArea" column="total_area" />
|
||||
<result property="invoiceNo" column="invoice_no" />
|
||||
<result property="batchRefNo" column="batch_ref_no" />
|
||||
<result property="sheetRefNo" column="sheet_ref_no" />
|
||||
<result property="boxPalletNo" column="box_pallet_no" />
|
||||
<result property="invoiceNo" column="INVOICE_NO" />
|
||||
<result property="batchRefNo" column="BATCH_REF_NO" />
|
||||
<result property="sheetRefNo" column="SHEET_REF_NO" />
|
||||
<result property="boxPalletNo" column="BOX_PALLET_NO" />
|
||||
<result property="productionDate" column="PRODUCTION_DATE" />
|
||||
<result property="expiryDate" column="EXPIRY_DATE" />
|
||||
<result property="inventoryDate" column="INVENTORY_DATE" />
|
||||
<result property="extAttr1" column="EXT_ATTR_1" />
|
||||
<result property="extAttr2" column="EXT_ATTR_2" />
|
||||
<result property="extAttr3" column="EXT_ATTR_3" />
|
||||
<result property="extAttr4" column="EXT_ATTR_4" />
|
||||
</resultMap>
|
||||
|
||||
|
||||
@@ -76,7 +83,21 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
||||
a.BAR_CODE,
|
||||
c.SPECIFICATION_MODEL,
|
||||
a.QUANTITY, a.ALREADY_RECEIPT_QUANTITY, a.RECEIPT_QUANTITY, a.RECEIPT_STATUS, a.PACK_ID, a.PACK_CODE, a.PACK_NAME, a.PACK_DETAIL_ID, a.UNIT_CODE, a.UNIT_NAME, a.UNIT_NUMBER, a.MATERIAL_WAREHOUSE_CONTROL_ID, a.SERIAL_NUMBER_MANAGE, a.QUALITY_INSPECTION_MANAGE, a.QUALITY_INSPECTION_STAGE, a.QUALITY_INSPECTION_RULE, a.QUALITY_INSPECTION_RATIO, a.QUALITY_INSPECTION_TERM, a.QUALITY_INSPECTION_RESULTS, a.ALLOW_OVERCHARGE, a.OVERCHARGE_RATIO, a.BATCH_NUMBER, a.MATERIAL_STATUS_CODE, a.MATERIAL_STATUS_NAME, a.DISTRIBUTE_RULE_ID, a.DISTRIBUTE_RULE_CODE, a.DISTRIBUTE_RULE_NAME, a.CONTAINER_ID, a.CONTAINER_CODE, a.CONTAINER_TYPE, a.CONTAINER_TYPE_NAME, a.LEVEL, a.PARENT_UNIQUE_ID, a.IN_UNIQUE_ID, a.ALLOW_MODIFY, a.REMARK, a.CREATE_TIME, a.CREATE_BY, a.CREATE_BY_NAME, a.UPDATE_TIME, a.UPDATE_BY, a.UPDATE_BY_NAME, a.GEN_STOCK_SHELF, a.DEL_FLAG,
|
||||
b.TOTAL_NET_WEIGHT, b.TOTAL_GROSS_WEIGHT, b.TOTAL_VOLUME, b.TOTAL_AREA, b.INVOICE_NO, b.BATCH_REF_NO, b.SHEET_REF_NO, b.BOX_PALLET_NO
|
||||
ifnull(a.TOTAL_NET_WEIGHT, b.TOTAL_NET_WEIGHT) TOTAL_NET_WEIGHT,
|
||||
ifnull(a.TOTAL_GROSS_WEIGHT, b.TOTAL_GROSS_WEIGHT) TOTAL_GROSS_WEIGHT,
|
||||
ifnull(a.TOTAL_VOLUME, b.TOTAL_VOLUME) TOTAL_VOLUME,
|
||||
ifnull(a.TOTAL_AREA, b.TOTAL_AREA) TOTAL_AREA,
|
||||
ifnull(a.INVOICE_NO, b.INVOICE_NO) INVOICE_NO,
|
||||
ifnull(a.BATCH_REF_NO, b.BATCH_REF_NO) BATCH_REF_NO,
|
||||
ifnull(a.SHEET_REF_NO, b.SHEET_REF_NO) SHEET_REF_NO,
|
||||
ifnull(a.BOX_PALLET_NO, b.BOX_PALLET_NO) BOX_PALLET_NO,
|
||||
ifnull(a.PRODUCTION_DATE, b.PRODUCTION_DATE) PRODUCTION_DATE,
|
||||
ifnull(a.EXPIRY_DATE, b.EXPIRY_DATE) EXPIRY_DATE,
|
||||
ifnull(a.INVENTORY_DATE, b.INVENTORY_DATE) INVENTORY_DATE,
|
||||
ifnull(a.EXT_ATTR_1, b.EXT_ATTR_1) EXT_ATTR_1,
|
||||
ifnull(a.EXT_ATTR_2, b.EXT_ATTR_2) EXT_ATTR_2,
|
||||
ifnull(a.EXT_ATTR_3, b.EXT_ATTR_3) EXT_ATTR_3,
|
||||
ifnull(a.EXT_ATTR_4, b.EXT_ATTR_4) EXT_ATTR_4
|
||||
from RECEIPT_MATERIAL_DETAIL a
|
||||
LEFT JOIN IN_MATERIAL_DETAIL b ON a.IN_UNIQUE_ID = b.UNIQUE_ID AND b.DEL_FLAG = 1
|
||||
LEFT JOIN MATERIAL_BASE_INFO c ON a.MATERIAL_BASE_INFO_ID = c.MATERIAL_BASE_INFO_ID AND c.DEL_FLAG = 1
|
||||
|
||||
Reference in New Issue
Block a user