This commit is contained in:
jhd
2026-06-02 13:52:06 +08:00
89 changed files with 7529 additions and 1892 deletions

View File

@@ -148,7 +148,10 @@ public class WmsDeliveryWaybillDetailController extends BaseController {
return new TableDataInfo<>();
}
bo.setCoilIds(boundCoilIds.stream().map(String::valueOf).collect(java.util.stream.Collectors.joining(",")));
bo.setStatusFirst(true); // 未发货的排在前面
bo.setStatusFirst(true);
if (planId == null) {
bo.setOrderByPlanDesc(true);
}
return iWmsMaterialCoilService.queryPageListWithBindInfo(bo, pageQuery);
}

View File

@@ -5,8 +5,6 @@ import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
import java.util.Set;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.stream.Collectors;
import java.util.HashMap;
@@ -33,7 +31,6 @@ import com.klp.common.core.validate.AddGroup;
import com.klp.common.core.validate.EditGroup;
import com.klp.common.enums.BusinessType;
import com.klp.common.utils.poi.ExcelUtil;
import com.klp.common.utils.StringUtils;
import com.klp.domain.bo.WmsMaterialCoilBo;
import com.klp.domain.bo.WmsMaterialCoilReportSummaryBo;
import com.klp.domain.vo.dashboard.CoilTrimStatisticsVo;
@@ -135,25 +132,6 @@ public class WmsMaterialCoilController extends BaseController {
ExcelUtil.exportExcel(list, "钢卷物料表", WmsMaterialCoilExportVo.class, response);
}
/**
* 个性化导出:前端传入要导出的字段名,仅导出选中列
* columns 参数为逗号分隔的 Java 字段名,如 "itemTypeDesc,enterCoilNo,netWeight"
* 不传 columns 时等同于 /exportAll 导出全部字段
*/
@Log(title = "钢卷物料表", businessType = BusinessType.EXPORT)
@PostMapping("/exportCustom")
public void exportCustom(WmsMaterialCoilBo bo,
@RequestParam(required = false) String columns,
HttpServletResponse response) {
List<WmsMaterialCoilAllExportVo> list = iWmsMaterialCoilService.queryExportListAll(bo);
if (StringUtils.isNotBlank(columns)) {
Set<String> includeFields = new HashSet<>(Arrays.asList(columns.split(",")));
ExcelUtil.exportExcel(list, "钢卷物料表", WmsMaterialCoilAllExportVo.class, includeFields, response);
} else {
ExcelUtil.exportExcel(list, "钢卷物料表", WmsMaterialCoilAllExportVo.class, response);
}
}
/**
* 获取可导出的列元数据(供前端列选择器使用)
* 返回 { "fieldName": "中文列名" } 的映射,基于完整导出字段
@@ -195,9 +173,41 @@ public class WmsMaterialCoilController extends BaseController {
columns.put("actualThickness", "实测厚度");
columns.put("transferType", "调拨类型");
columns.put("team", "班组");
columns.put("theoreticalThickness", "理论厚度");
columns.put("theoreticalLength", "理论长度");
columns.put("chromePlateCoilNo", "镀铬卷号");
return R.ok(columns);
}
/**
* 自定义导出(指定列顺序):前端传入按导出顺序排列的字段名
* columnsOrdered 参数为逗号分隔的有序字段名,如 "team,enterCoilNo,netWeight,remark"
*/
@Log(title = "钢卷物料表", businessType = BusinessType.EXPORT)
@PostMapping("/exportCustomOrdered")
public void exportCustomOrdered(WmsMaterialCoilBo bo,
@RequestParam String columnsOrdered,
HttpServletResponse response) {
List<WmsMaterialCoilAllExportVo> list = iWmsMaterialCoilService.queryExportListAll(bo);
List<String> orderedFields = Arrays.asList(columnsOrdered.split(","));
ExcelUtil.exportExcelOrdered(list, "钢卷物料表", orderedFields,
getAllExportFieldLabelMap(), response);
}
/**
* 从 WmsMaterialCoilAllExportVo 注解中提取字段名->中文列名映射
*/
private Map<String, String> getAllExportFieldLabelMap() {
Map<String, String> map = new LinkedHashMap<>();
for (java.lang.reflect.Field field : WmsMaterialCoilAllExportVo.class.getDeclaredFields()) {
com.alibaba.excel.annotation.ExcelProperty ep = field.getAnnotation(com.alibaba.excel.annotation.ExcelProperty.class);
if (ep != null && ep.value().length > 0) {
map.put(field.getName(), ep.value()[0]);
}
}
return map;
}
/**
* 导出钢卷物料表列表(完整字段版本)
* 导出全部字段

View File

@@ -213,5 +213,20 @@ public class WmsMaterialCoil extends BaseEntity {
private Long specId;
private Long versionId;
/**
* 理论厚度(单位:毫米)
*/
private BigDecimal theoreticalThickness;
/**
* 理论长度(单位:米)
*/
private BigDecimal theoreticalLength;
/**
* 镀铬卷号
*/
private String chromePlateCoilNo;
}

View File

@@ -381,5 +381,26 @@ public class WmsMaterialCoilBo extends BaseEntity {
private Long specId;
private Long versionId;
/**
* 理论厚度(单位:毫米)
*/
private BigDecimal theoreticalThickness;
/**
* 理论长度(单位:米)
*/
private BigDecimal theoreticalLength;
/**
* 镀铬卷号
*/
private String chromePlateCoilNo;
/**
* 是否按计划创建时间倒序排序已绑定钢卷列表无planId时按计划新旧排列
*/
@TableField(exist = false)
private Boolean orderByPlanDesc;
}

View File

@@ -208,4 +208,22 @@ public class WmsMaterialCoilExportVo {
*/
@ExcelProperty(value = "调拨类型")
private String transferType;
/**
* 理论厚度(单位:毫米)
*/
@ExcelProperty(value = "理论厚度")
private BigDecimal theoreticalThickness;
/**
* 理论长度(单位:米)
*/
@ExcelProperty(value = "理论长度")
private BigDecimal theoreticalLength;
/**
* 镀铬卷号
*/
@ExcelProperty(value = "镀铬卷号")
private String chromePlateCoilNo;
}

View File

@@ -339,5 +339,20 @@ public class WmsMaterialCoilVo extends BaseEntity {
private Long versionId;
private String specCode;
private String versionCode;
/**
* 理论厚度(单位:毫米)
*/
private BigDecimal theoreticalThickness;
/**
* 理论长度(单位:米)
*/
private BigDecimal theoreticalLength;
/**
* 镀铬卷号
*/
private String chromePlateCoilNo;
}

View File

@@ -45,6 +45,11 @@ public interface WmsMaterialCoilMapper extends BaseMapperPlus<WmsMaterialCoilMap
*/
Page<WmsMaterialCoilVo> selectVoPagePlusOrderBy(Page<Object> build, @Param("ew") QueryWrapper<WmsMaterialCoil> lqw);
/**
* orderByPlanDesc=true 时使用:包含发货计划 join支持按计划创建时间排序
*/
Page<WmsMaterialCoilVo> selectVoPagePlusPlanOrder(Page<Object> build, @Param("ew") QueryWrapper<WmsMaterialCoil> lqw);
List<WmsMaterialCoilVo> selectVoListWithDynamicJoin(@Param("ew")QueryWrapper<WmsMaterialCoil> lqw);
Map<String, Object> selectCountForSpecSync(@Param("ew") QueryWrapper<WmsMaterialCoil> qw);
@@ -91,7 +96,7 @@ public interface WmsMaterialCoilMapper extends BaseMapperPlus<WmsMaterialCoilMap
* @param coilIds 钢卷ID集合
* @return 发货报表导出数据
*/
List<WmsMaterialCoilDeliveryExportVo> selectDeliveryExportListByCoilIds(@Param("coilIds") java.util.Collection<Long> coilIds);
List<WmsMaterialCoilDeliveryExportVo> selectDeliveryExportListByCoilIds(@Param("coilIds") java.util.Collection<Long> coilIds);
/**
* 退火报表导出按钢卷ID列表联查钢卷 + 退火计划 + 退火计划钢卷关系)

View File

@@ -49,7 +49,9 @@ import java.util.stream.Collectors;
public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService {
private static final List<String> STATUS_SEVERITY = java.util.Arrays.asList("normal", "late_warn", "early_warn",
"late_one", "early_one", "late_two", "early_two", "absent_half");
"late_one", "early_one", "late_two", "early_two", "absent_half", "missed_start", "missed_end", "missed");
private static final long LATE_EARLY_MAX_SECONDS = 120 * 60L;
private static final int BATCH_SIZE = 500;
@@ -386,18 +388,91 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
if (hasPeriod2) {
LocalTime p1End = toLocalTime(schedule.getShiftEndTime());
LocalTime p2Start = toLocalTime(schedule.getShiftStartTime2());
LocalTime split = LocalTime.of(
(p1End.getHour() + p2Start.getHour()) / 2,
(p1End.getMinute() + p2Start.getMinute()) / 2);
// 按理论时间范围分割p1End之前、中间区间、p2Start之后
List<AttendanceRecords> beforeGap = new ArrayList<>();
List<AttendanceRecords> inGap = new ArrayList<>();
List<AttendanceRecords> afterGap = new ArrayList<>();
for (AttendanceRecords r : records) {
LocalTime t = toLocalDateTime(r.getChecktime()).toLocalTime();
if (!t.isAfter(p1End)) {
beforeGap.add(r);
} else if (!t.isBefore(p2Start)) {
afterGap.add(r);
} else {
inGap.add(r);
}
}
List<AttendanceRecords> p1Records = new ArrayList<>();
List<AttendanceRecords> p2Records = new ArrayList<>();
for (AttendanceRecords r : records) {
LocalTime t = toLocalDateTime(r.getChecktime()).toLocalTime();
if (t.isBefore(split)) {
p1Records.add(r);
} else {
p2Records.add(r);
boolean lunchPatternFound = false;
// 检测[p1End, p2Start]区间内是否有午休打卡(取最远两条判断>10分钟按最大连续间隔切开
if (inGap.size() >= 2) {
LocalDateTime firstInGap = toLocalDateTime(inGap.get(0).getChecktime());
LocalDateTime lastInGap = toLocalDateTime(inGap.get(inGap.size() - 1).getChecktime());
if (Duration.between(firstInGap, lastInGap).getSeconds() > 600) {
int splitIdx = 0;
long maxGap = 0;
for (int i = 0; i < inGap.size() - 1; i++) {
LocalDateTime t1 = toLocalDateTime(inGap.get(i).getChecktime());
LocalDateTime t2 = toLocalDateTime(inGap.get(i + 1).getChecktime());
long gap = Duration.between(t1, t2).getSeconds();
if (gap > maxGap) {
maxGap = gap;
splitIdx = i;
}
if (gap > 600) {
break;
}
}
p1Records = new ArrayList<>(beforeGap);
p1Records.addAll(inGap.subList(0, splitIdx + 1));
p2Records = new ArrayList<>(inGap.subList(splitIdx + 1, inGap.size()));
p2Records.addAll(afterGap);
lunchPatternFound = true;
}
}
// 检测边界beforeGap最后一条 与 inGap第一条 间隔>10分钟提前下班+午饭回来)
if (!lunchPatternFound && !beforeGap.isEmpty() && !inGap.isEmpty()) {
LocalDateTime lastBefore = toLocalDateTime(beforeGap.get(beforeGap.size() - 1).getChecktime());
LocalDateTime firstIn = toLocalDateTime(inGap.get(0).getChecktime());
if (Duration.between(lastBefore, firstIn).getSeconds() > 600) {
p1Records = new ArrayList<>(beforeGap);
p2Records = new ArrayList<>(inGap);
p2Records.addAll(afterGap);
lunchPatternFound = true;
}
}
// 检测边界inGap最后一条 与 afterGap第一条 间隔>10分钟午休晚归
if (!lunchPatternFound && !inGap.isEmpty() && !afterGap.isEmpty()) {
LocalDateTime lastIn = toLocalDateTime(inGap.get(inGap.size() - 1).getChecktime());
LocalDateTime firstAfter = toLocalDateTime(afterGap.get(0).getChecktime());
if (Duration.between(lastIn, firstAfter).getSeconds() > 600) {
p1Records = new ArrayList<>(beforeGap);
p1Records.addAll(inGap);
p2Records = new ArrayList<>(afterGap);
lunchPatternFound = true;
}
}
if (!lunchPatternFound) {
// 没有午休打卡模式,回退原中值法
LocalTime split = LocalTime.of(
(p1End.getHour() + p2Start.getHour()) / 2,
(p1End.getMinute() + p2Start.getMinute()) / 2);
p1Records = new ArrayList<>();
p2Records = new ArrayList<>();
for (AttendanceRecords r : records) {
LocalTime t = toLocalDateTime(r.getChecktime()).toLocalTime();
if (t.isBefore(split)) {
p1Records.add(r);
} else {
p2Records.add(r);
}
}
}
@@ -457,6 +532,7 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
private void checkPeriod(WmsAttendanceCheck check, WmsAttendanceRule rule, int period,
List<AttendanceRecords> periodRecords, Date expectedStart, Date expectedEnd) {
// 旷工0条打卡记录
if (periodRecords.isEmpty()) {
if (period == 1) {
check.setP1Status("missed");
@@ -477,53 +553,72 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
int earlyMinutes = 0;
BigDecimal deduct = BigDecimal.ZERO;
String status = "normal";
boolean startMissed = false;
boolean endMissed = false;
if (expStart != null && firstCheck.isAfter(expStart)) {
lateMinutes = (int) Duration.between(expStart, firstCheck).toMinutes();
if (lateMinutes > rule.getAbsentHalfDay()) {
status = "absent_half";
} else if (lateMinutes > rule.getLateOne()) {
status = "late_two";
deduct = deduct.add(rule.getDeductTwo());
} else if (lateMinutes > rule.getLateWarn()) {
status = "late_one";
deduct = deduct.add(rule.getDeductOne());
} else {
status = "late_warn";
// 上班检测:晚于理论上班时间俩小时以上 → 漏打卡
if (expStart != null) {
long lateSecs = Duration.between(expStart, firstCheck).getSeconds();
if (lateSecs > LATE_EARLY_MAX_SECONDS) {
startMissed = true;
} else if (lateSecs > 0) {
lateMinutes = (int) (lateSecs / 60);
if (lateMinutes > rule.getAbsentHalfDay()) {
status = "absent_half";
} else if (lateMinutes > rule.getLateOne()) {
status = "late_two";
deduct = deduct.add(rule.getDeductTwo());
} else if (lateMinutes > rule.getLateWarn()) {
status = "late_one";
deduct = deduct.add(rule.getDeductOne());
} else {
status = "late_warn";
}
}
}
// 下班检测:早于理论下班时间俩小时以上 → 漏打卡
if (expEnd != null && lastCheck.isBefore(expEnd)) {
int min = (int) Duration.between(lastCheck, expEnd).toMinutes();
if (min > rule.getAbsentHalfDay()) {
status = maxSeverity(status, "absent_half");
earlyMinutes = min;
} else if (min > rule.getLateOne()) {
status = maxSeverity(status, "early_two");
deduct = deduct.add(rule.getDeductTwo());
earlyMinutes = min;
} else if (min > rule.getLateWarn()) {
status = maxSeverity(status, "early_one");
deduct = deduct.add(rule.getDeductOne());
earlyMinutes = min;
} else {
if ("normal".equals(status)) {
status = "early_warn";
long earlySecs = Duration.between(lastCheck, expEnd).getSeconds();
if (earlySecs > LATE_EARLY_MAX_SECONDS) {
endMissed = true;
} else if (earlySecs > 0) {
earlyMinutes = (int) (earlySecs / 60);
if (earlyMinutes > rule.getAbsentHalfDay()) {
status = maxSeverity(status, "absent_half");
} else if (earlyMinutes > rule.getLateOne()) {
status = maxSeverity(status, "early_two");
deduct = deduct.add(rule.getDeductTwo());
} else if (earlyMinutes > rule.getLateWarn()) {
status = maxSeverity(status, "early_one");
deduct = deduct.add(rule.getDeductOne());
} else {
if ("normal".equals(status)) {
status = "early_warn";
}
}
earlyMinutes = min;
}
}
// 上班漏打卡或下班漏打卡会覆盖原有状态
if (startMissed && endMissed) {
status = "missed";
} else if (startMissed) {
status = maxSeverity(status, "missed_start");
} else if (endMissed) {
status = maxSeverity(status, "missed_end");
}
if (period == 1) {
check.setP1FirstCheck(firstRec.getChecktime());
check.setP1LastCheck(lastRec.getChecktime());
check.setP1FirstCheck(startMissed ? null : firstRec.getChecktime());
check.setP1LastCheck(endMissed ? null : lastRec.getChecktime());
check.setP1LateMinutes(lateMinutes);
check.setP1EarlyMinutes(earlyMinutes);
check.setP1Status(status);
check.setP1Deduct(deduct);
} else {
check.setP2FirstCheck(firstRec.getChecktime());
check.setP2LastCheck(lastRec.getChecktime());
check.setP2FirstCheck(startMissed ? null : firstRec.getChecktime());
check.setP2LastCheck(endMissed ? null : lastRec.getChecktime());
check.setP2LateMinutes(lateMinutes);
check.setP2EarlyMinutes(earlyMinutes);
check.setP2Status(status);
@@ -549,19 +644,27 @@ public class WmsAttendanceCheckServiceImpl implements IWmsAttendanceCheckService
if ("absent_half".equals(check.getP1Status()) || "absent_half".equals(check.getP2Status())) {
hasAbsentHalf = true;
}
if ("missed".equals(check.getP1Status())) {
if (check.getP2StartTime() != null) {
if ("missed".equals(check.getP2Status())) {
check.setAbsentType("full_day");
check.setOverallStatus("absent_full");
return;
}
boolean p1Missed = "missed".equals(check.getP1Status());
boolean p2Missed = check.getP2Status() != null && "missed".equals(check.getP2Status());
boolean hasP2 = check.getP2StartTime() != null;
if (p1Missed) {
if (hasP2 && !p2Missed) {
check.setAbsentType("half_day");
check.setOverallStatus("absent_half");
return;
} else {
check.setAbsentType("full_day");
check.setOverallStatus("absent_full");
return;
}
}
if (p2Missed) {
check.setAbsentType("half_day");
check.setOverallStatus("absent_half");
return;
}
if (check.getP1StartTime() == null && check.getP2StartTime() == null) {
check.setAbsentType("full_day");
check.setOverallStatus("absent_full");

View File

@@ -41,6 +41,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
import java.util.stream.Collectors;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.io.IOException;
import java.net.URLEncoder;
@@ -486,7 +487,9 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
private Page<WmsMaterialCoilVo> queryMaterialCoilPage(WmsMaterialCoilBo bo, PageQuery pageQuery) {
QueryWrapper<WmsMaterialCoil> qw = buildQueryWrapperPlus(bo);
Page<WmsMaterialCoilVo> result;
if (Boolean.TRUE.equals(bo.getOrderBy())) {
if (Boolean.TRUE.equals(bo.getOrderByPlanDesc())) {
result = baseMapper.selectVoPagePlusPlanOrder(pageQuery.build(), qw);
} else if (Boolean.TRUE.equals(bo.getOrderBy())) {
result = baseMapper.selectVoPagePlusOrderBy(pageQuery.build(), qw);
} else {
result = baseMapper.selectVoPagePlus(pageQuery.build(), qw);
@@ -697,6 +700,12 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
qw.eq(bo.getActualWidth() != null, "mc.actual_width", bo.getActualWidth());
// 实测厚度
qw.eq(StringUtils.isNotBlank(bo.getActualThickness()), "mc.actual_thickness", bo.getActualThickness());
// 理论厚度
qw.eq(bo.getTheoreticalThickness() != null, "mc.theoretical_thickness", bo.getTheoreticalThickness());
// 理论长度
qw.eq(bo.getTheoreticalLength() != null, "mc.theoretical_length", bo.getTheoreticalLength());
// 镀铬卷号
qw.like(StringUtils.isNotBlank(bo.getChromePlateCoilNo()), "mc.chrome_plate_coil_no", bo.getChromePlateCoilNo());
// 生产开始时间
qw.eq(bo.getProductionStartTime() != null, "mc.production_start_time", bo.getProductionStartTime());
// 生产结束时间
@@ -926,6 +935,10 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
qw.apply("COALESCE(ca.abnormal_count, 0) >= {0}", bo.getMinAbnormalCount());
}
// 排序:
// 按计划创建时间倒序已绑定钢卷列表无planId时优先按计划排序
if (Boolean.TRUE.equals(bo.getOrderByPlanDesc())) {
qw.orderByDesc("pl.create_time");
}
// 已绑定钢卷列表中,未发货(status=0)的排在前面
if (Boolean.TRUE.equals(bo.getStatusFirst())) {
qw.orderByAsc("mc.status = 1");
@@ -1028,6 +1041,134 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
return '(' + String.join(" OR ", parts) + ')';
}
/**
* 根据净重(吨)、规格厚度和宽度自动计算理论厚度(mm)和理论长度(m)
* 理论厚度 = 净重(吨) × 1000 / 7.85 / 实测长度(mm) / 宽度(mm) * 1000
* 理论长度 = 净重(吨) × 1000 / 7.85 / 厚度(mm) / 宽度(mm) * 1000
*/
private void calculateTheoretical(WmsMaterialCoilBo bo) {
if (bo.getNetWeight() == null) {
return;
}
if (StringUtils.isBlank(bo.getItemType()) || bo.getItemId() == null) {
return;
}
String specThickness = null;
String specWidth = null;
try {
String specification = null;
if ("raw_material".equals(bo.getItemType())) {
WmsRawMaterial rm = rawMaterialMapper.selectById(bo.getItemId());
if (rm != null) {
specification = rm.getSpecification();
}
} else if ("product".equals(bo.getItemType())) {
WmsProduct p = productMapper.selectById(bo.getItemId());
if (p != null) {
specification = p.getSpecification();
}
}
if (StringUtils.isNotBlank(specification)) {
String[] parts = specification.split("\\*");
if (parts.length >= 2) {
specThickness = parts[0].trim();
specWidth = parts[parts.length - 1].trim();
}
}
} catch (Exception e) {
log.warn("查询物品规格失败, itemType: {}, itemId: {}", bo.getItemType(), bo.getItemId(), e);
return;
}
if (StringUtils.isBlank(specThickness) || StringUtils.isBlank(specWidth)) {
return;
}
try {
BigDecimal weight = bo.getNetWeight();
BigDecimal thickness = new BigDecimal(specThickness);
BigDecimal width = new BigDecimal(specWidth);
BigDecimal volume = weight.multiply(new BigDecimal("1000")).divide(new BigDecimal("7.85"), 10, RoundingMode.HALF_UP);
// 计算理论厚度(需要实测长度)
if (bo.getTheoreticalThickness() == null && bo.getActualLength() != null && bo.getActualWidth().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal length = new BigDecimal(bo.getActualLength());
BigDecimal theoreticalThickness = volume.divide(length, 10, RoundingMode.HALF_UP).divide(width, 10, RoundingMode.HALF_UP).multiply(new BigDecimal("1000"));
bo.setTheoreticalThickness(theoreticalThickness);
}
// 计算理论长度
if (bo.getTheoreticalLength() == null) {
BigDecimal theoreticalLength = volume.divide(thickness, 10, RoundingMode.HALF_UP).divide(width, 10, RoundingMode.HALF_UP).multiply(new BigDecimal("1000"));
bo.setTheoreticalLength(theoreticalLength);
}
} catch (Exception e) {
log.warn("计算理论厚度/长度失败", e);
}
}
/**
* 根据物品类型和ID获取规格中的宽度规格格式厚度*宽度,取 * 后面部分)
*/
private BigDecimal getSpecWidth(String itemType, Long itemId) {
if (StringUtils.isBlank(itemType) || itemId == null) {
return null;
}
try {
String specification = null;
if ("raw_material".equals(itemType)) {
WmsRawMaterial rm = rawMaterialMapper.selectById(itemId);
if (rm != null) {
specification = rm.getSpecification();
}
} else if ("product".equals(itemType)) {
WmsProduct p = productMapper.selectById(itemId);
if (p != null) {
specification = p.getSpecification();
}
}
if (StringUtils.isNotBlank(specification)) {
String[] parts = specification.split("\\*");
if (parts.length >= 2) {
return new BigDecimal(parts[parts.length - 1].trim());
}
}
} catch (Exception e) {
log.warn("获取规格宽度失败, itemType: {}, itemId: {}", itemType, itemId, e);
}
return null;
}
/**
* 根据物品类型和ID获取规格中的厚度规格格式厚度*宽度,取 * 前面部分)
*/
private BigDecimal getSpecThickness(String itemType, Long itemId) {
if (StringUtils.isBlank(itemType) || itemId == null) {
return null;
}
try {
String specification = null;
if ("raw_material".equals(itemType)) {
WmsRawMaterial rm = rawMaterialMapper.selectById(itemId);
if (rm != null) {
specification = rm.getSpecification();
}
} else if ("product".equals(itemType)) {
WmsProduct p = productMapper.selectById(itemId);
if (p != null) {
specification = p.getSpecification();
}
}
if (StringUtils.isNotBlank(specification)) {
String[] parts = specification.split("\\*");
if (parts.length >= 2) {
return new BigDecimal(parts[0].trim());
}
}
} catch (Exception e) {
log.warn("获取规格厚度失败, itemType: {}, itemId: {}", itemType, itemId, e);
}
return null;
}
/**
* 获取库位及其所有子库位的ID列表
* 逻辑参考 WmsActualWarehouseServiceImpl.queryList 中的拆分处理逻辑:
@@ -1333,6 +1474,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
bo.setActualWarehouseId(null);
}
// 3. 插入钢卷数据
calculateTheoretical(bo);
WmsMaterialCoil add = BeanUtil.toBean(bo, WmsMaterialCoil.class);
if(bo.getDataType() != null && bo.getDataType() == 10){
add.setDataType(10);
@@ -1548,6 +1690,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
}
}
validateCoilWeight(bo.getGrossWeight(), bo.getNetWeight());
calculateTheoretical(bo);
// 直接更新钢卷属性
WmsMaterialCoil updateCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class);
validEntityBeforeSave(updateCoil);
@@ -1641,6 +1784,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
baseMapper.update(null, updateWrapper);
// 2. 创建新记录
calculateTheoretical(bo);
WmsMaterialCoil newCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class);
newCoil.setCoilId(null); // 清空ID让数据库自动生成新ID
newCoil.setDataType(1); // 设置为当前数据
@@ -1888,6 +2032,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
? newCoilBo.getActualWarehouseId() : null;
validateActualWarehouseForAssign(newCoilBo.getActualWarehouseId(), ignoreOccupiedId);
}
calculateTheoretical(newCoilBo);
WmsMaterialCoil newCoil = BeanUtil.toBean(newCoilBo, WmsMaterialCoil.class);
newCoil.setCoilId(null);
newCoil.setDataType(1);
@@ -2016,6 +2161,7 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
if (bo.getActualWarehouseId() != null) {
validateActualWarehouseForAssign(bo.getActualWarehouseId(), null);
}
calculateTheoretical(bo);
WmsMaterialCoil newCoil = BeanUtil.toBean(bo, WmsMaterialCoil.class);
newCoil.setCoilId(null);
newCoil.setDataType(1);
@@ -4854,8 +5000,27 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
if (childCoilBo.getActualWarehouseId() != null) {
validateActualWarehouseForAssign(childCoilBo.getActualWarehouseId(), null);
}
if (pendingAction.getActionType() != 501 && childCoilBo.getActionType() != null) {
// 校验子卷净重不超过母卷
if (childCoilBo.getNetWeight() != null && parentCoil.getNetWeight() != null) {
if (childCoilBo.getNetWeight().compareTo(parentCoil.getNetWeight()) > 0) {
throw new RuntimeException("子卷净重[" + childCoilBo.getNetWeight() + "]不能超过母卷净重[" + parentCoil.getNetWeight() + "]");
}
}
// 校验子卷规格厚度不超过母卷
String childItemType = StringUtils.isNotBlank(childCoilBo.getItemType()) ? childCoilBo.getItemType() : parentCoil.getItemType();
Long childItemId = childCoilBo.getItemId() != null ? childCoilBo.getItemId() : parentCoil.getItemId();
BigDecimal parentSpecThickness = getSpecThickness(parentCoil.getItemType(), parentCoil.getItemId());
BigDecimal childSpecThickness = getSpecThickness(childItemType, childItemId);
if (parentSpecThickness != null && childSpecThickness != null) {
if (childSpecThickness.compareTo(parentSpecThickness) > 0) {
throw new RuntimeException("子卷规格厚度[" + childSpecThickness + "]不能超过母卷规格厚度[" + parentSpecThickness + "]");
}
}
}
// 5. 创建子钢卷(参考普通分卷逻辑,但不设置母卷为历史数据)
calculateTheoretical(childCoilBo);
WmsMaterialCoil childCoil = BeanUtil.toBean(childCoilBo, WmsMaterialCoil.class);
childCoil.setCoilId(null);
childCoil.setDataType(1); // 当前数据
@@ -4971,6 +5136,19 @@ public class WmsMaterialCoilServiceImpl implements IWmsMaterialCoilService {
.map(WmsMaterialCoil::getCurrentCoilNo)
.collect(Collectors.toList());
if (pendingAction.getActionType() != 501) {
// 校验所有子卷总重不超过母卷净重
if (parentCoil.getNetWeight() != null) {
BigDecimal totalChildWeight = childCoils.stream()
.map(WmsMaterialCoil::getNetWeight)
.filter(Objects::nonNull)
.reduce(BigDecimal.ZERO, BigDecimal::add);
if (totalChildWeight.compareTo(parentCoil.getNetWeight()) > 0) {
throw new RuntimeException("所有子卷总重[" + totalChildWeight + "]不能超过母卷净重[" + parentCoil.getNetWeight() + "]");
}
}
}
// 如果母卷即将成为历史卷 证明已经加工完成则释放库位
if (parentCoil.getActualWarehouseId() != null){
updateActualWarehouseEnableStatus(parentCoil.getActualWarehouseId(), null);