Merge branch '0.8.X' of http://49.232.154.205:10100/DeXun/klp-oa into 0.8.X
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出钢卷物料表列表(完整字段版本)
|
||||
* 导出全部字段
|
||||
|
||||
@@ -213,5 +213,20 @@ public class WmsMaterialCoil extends BaseEntity {
|
||||
|
||||
private Long specId;
|
||||
private Long versionId;
|
||||
|
||||
/**
|
||||
* 理论厚度(单位:毫米)
|
||||
*/
|
||||
private BigDecimal theoreticalThickness;
|
||||
|
||||
/**
|
||||
* 理论长度(单位:米)
|
||||
*/
|
||||
private BigDecimal theoreticalLength;
|
||||
|
||||
/**
|
||||
* 镀铬卷号
|
||||
*/
|
||||
private String chromePlateCoilNo;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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列表联查(钢卷 + 退火计划 + 退火计划钢卷关系)
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user