From d21636425e2c593adca56d6c0c4763ad64bddbb3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=A9=AC=E4=B8=89=E7=82=AE?= <15856818120@163.com>
Date: Thu, 22 Jan 2026 18:25:33 +0800
Subject: [PATCH] =?UTF-8?q?=E6=81=A2=E5=A4=8D=E8=A2=AB=E8=A6=86=E7=9B=96?=
=?UTF-8?q?=E7=9A=84=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
resources/mybatis/cost/ProjectCostMapper.xml | 196 ++++----
.../cost/beans/ProjectLeaseCostDetail.java | 92 +++-
.../cost/service/ProjectCostServiceImpl.java | 460 +++++++++++++++---
3 files changed, 588 insertions(+), 160 deletions(-)
diff --git a/resources/mybatis/cost/ProjectCostMapper.xml b/resources/mybatis/cost/ProjectCostMapper.xml
index ba90d0e..236d4b8 100644
--- a/resources/mybatis/cost/ProjectCostMapper.xml
+++ b/resources/mybatis/cost/ProjectCostMapper.xml
@@ -4,23 +4,25 @@
+
insert into wf_project_settlement_details(
- ID,
- SETTLEMENT_ID,
- OPERATE_TYPE,
- MACHINE_TYPE_ID,
- MACHINE_TYPE_NAME,
- MACHINE_CODE,
- MACHINE_MODEL,
- MACHINE_UNIT,
- PRICE,
- LEASE_NUM,
- RETURN_NUM,
- LEASE_UNIT,
- OPERATE_PERSON_NAME,
- OPERATE_DATE,
- TASK_CODE
+ ID,
+ SETTLEMENT_ID,
+ OPERATE_TYPE,
+ MACHINE_TYPE_ID,
+ MACHINE_TYPE_NAME,
+ MACHINE_CODE,
+ MACHINE_MODEL,
+ MACHINE_UNIT,
+ PRICE,
+ LEASE_NUM,
+ RETURN_NUM,
+ LEASE_UNIT,
+ OPERATE_PERSON_NAME,
+ OPERATE_DATE,
+ TASK_CODE
) values
-
- (
- #{item.id},
- #{item.settlementId},
- #{item.operateType},
- #{item.machineTypeId},
- #{item.machineTypeName},
- #{item.machineCode},
- #{item.machineModel},
- #{item.machineUnit},
- #{item.price},
- #{item.leaseNum},
- #{item.returnNum},
- #{item.leaseUnit},
- #{item.operatePersonName},
- #{item.operateDate},
- #{item.taskCode}
- )
-
+
+ (
+ #{item.id},
+ #{item.settlementId},
+ #{item.operateType},
+ #{item.machineTypeId},
+ #{item.machineTypeName},
+ #{item.machineCode},
+ #{item.machineModel},
+ #{item.machineUnit},
+ #{item.price},
+ #{item.leaseNum},
+ #{item.returnNum},
+ #{item.leaseUnit},
+ #{item.operatePersonName},
+ #{item.operateDate},
+ #{item.taskCode}
+ )
+
-
\ No newline at end of file
+
diff --git a/src/com/bonus/cost/beans/ProjectLeaseCostDetail.java b/src/com/bonus/cost/beans/ProjectLeaseCostDetail.java
index dc040aa..33e7777 100644
--- a/src/com/bonus/cost/beans/ProjectLeaseCostDetail.java
+++ b/src/com/bonus/cost/beans/ProjectLeaseCostDetail.java
@@ -2,11 +2,12 @@ package com.bonus.cost.beans;
import com.fasterxml.jackson.annotation.JsonFormat;
+import java.math.BigDecimal;
import java.util.Map;
/**
* 工程领退物资明细实体类
- *
+ *
* @author syruan
*/
public class ProjectLeaseCostDetail {
@@ -21,6 +22,11 @@ public class ProjectLeaseCostDetail {
*/
private String machineName;
+ /**
+ * 机具类别
+ */
+ private String machineCodeName;
+
/**
* 机具类型名称
*/
@@ -51,6 +57,8 @@ public class ProjectLeaseCostDetail {
*/
private String machineCode;
+ private String oldMachineCode;
+
/**
* 备注
*/
@@ -106,6 +114,21 @@ public class ProjectLeaseCostDetail {
*/
private String operatePersonName;
+ /**
+ * 累计领料数量
+ */
+ private BigDecimal totalLeaseQuantity;
+
+ /**
+ * 累计退料数量
+ */
+ private BigDecimal totalReturnQuantity;
+
+ /**
+ * 领退料差额
+ */
+ private BigDecimal differenceQuantity;
+
/**
* 操作日期
*/
@@ -128,6 +151,16 @@ public class ProjectLeaseCostDetail {
*/
private String endTime;
+ /**
+ * 租赁单位
+ */
+ private String bsName;
+
+ /**
+ * 序号(第几次领料)
+ */
+ private Integer sequence;
+
/**
* 查询关键字
*/
@@ -324,4 +357,61 @@ public class ProjectLeaseCostDetail {
public void setMachineTypeName(String machineTypeName) {
this.machineTypeName = machineTypeName;
}
+
+ public String getOldMachineCode() {
+ return oldMachineCode;
+ }
+
+ public void setOldMachineCode(String oldMachineCode) {
+ this.oldMachineCode = oldMachineCode;
+ }
+
+ public String getBsName() {
+ return bsName;
+ }
+
+ public void setBsName(String bsName) {
+ this.bsName = bsName;
+ }
+
+ public String getMachineCodeName() {
+ return machineCodeName;
+ }
+
+ public void setMachineCodeName(String machineCodeName) {
+ this.machineCodeName = machineCodeName;
+ }
+
+ public Integer getSequence() {
+ return sequence;
+ }
+
+ public void setSequence(Integer sequence) {
+ this.sequence = sequence;
+ }
+
+ public BigDecimal getTotalLeaseQuantity() {
+ return totalLeaseQuantity;
+ }
+
+ public void setTotalLeaseQuantity(BigDecimal totalLeaseQuantity) {
+ this.totalLeaseQuantity = totalLeaseQuantity;
+ }
+
+ public BigDecimal getTotalReturnQuantity() {
+ return totalReturnQuantity;
+ }
+
+ public void setTotalReturnQuantity(BigDecimal totalReturnQuantity) {
+ this.totalReturnQuantity = totalReturnQuantity;
+ }
+
+ public BigDecimal getDifferenceQuantity() {
+ return differenceQuantity;
+ }
+
+ public void setDifferenceQuantity(BigDecimal differenceQuantity) {
+ this.differenceQuantity = differenceQuantity;
+ }
}
+
diff --git a/src/com/bonus/cost/service/ProjectCostServiceImpl.java b/src/com/bonus/cost/service/ProjectCostServiceImpl.java
index ab26ba0..89399f7 100644
--- a/src/com/bonus/cost/service/ProjectCostServiceImpl.java
+++ b/src/com/bonus/cost/service/ProjectCostServiceImpl.java
@@ -25,6 +25,7 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.OutputStream;
+import java.math.BigDecimal;
import java.net.URLEncoder;
import java.time.LocalDate;
import java.time.LocalDateTime;
@@ -44,20 +45,20 @@ public class ProjectCostServiceImpl implements ProjectCostService {
@Autowired
private ProjectCostDao projectCostDao;
-
+
@Autowired
private NewSettlementService newSettlementService;
@Autowired
private PlanApplicationDao planApplicationDao;
-
+
// 定义两个格式化器:输入格式(带时分秒)、输出格式(仅年月日)
private static final DateTimeFormatter INPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
private static final DateTimeFormatter OUTPUT_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
/**
* 安全转换为Double类型
- *
+ *
* @param obj 要转换的对象
* @param defaultValue 默认值
* @return 转换后的Double值
@@ -91,7 +92,7 @@ public class ProjectCostServiceImpl implements ProjectCostService {
/**
* 安全转换为Integer类型
- *
+ *
* @param obj 要转换的对象
* @param defaultValue 默认值
* @return 转换后的Integer值
@@ -351,18 +352,18 @@ public class ProjectCostServiceImpl implements ProjectCostService {
LocalDateTime endtime = LocalDateTime.parse("2025-05-26 14:21:00", formatter);
long daysBetween = java.time.Duration.between(operateTime, endtime).toDays();
System.err.println(daysBetween);
-
+
}
@Override
public Map calculateSettlement(ProjectLeaseCostDetail o) {
return newSettlementService.getNewSettlement(o);
}
-
-
-
+
+
+
public Map calculateSettlement2(ProjectLeaseCostDetail o) {
-
-
+
+
// 获取领料明细
List leaseDetails = queryProjectLeaseDetails(o);
// 获取退料明细
@@ -440,7 +441,7 @@ public class ProjectCostServiceImpl implements ProjectCostService {
startDate = LocalDateTime.now().withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0);
endDate = LocalDateTime.now().withHour(23).withMinute(59).withSecond(59);
}
-
+
// 对每种物资类型进行计算
for (Map.Entry> entry : groupedByMachineType.entrySet()) {
boolean add=true;
@@ -450,7 +451,7 @@ public class ProjectCostServiceImpl implements ProjectCostService {
if("3627".equals(machineTypeId)) {
System.err.println(machineTypeId);
}
-
+
// 按操作时间排序
items.sort(Comparator.comparing(ProjectLeaseCostDetail::getOperateTime,
Comparator.nullsFirst(Comparator.naturalOrder())));
@@ -473,7 +474,7 @@ public class ProjectCostServiceImpl implements ProjectCostService {
// 第一步:处理统计期间开始之前的所有操作,计算期初在用数量
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
-
+
DateTimeFormatter formatter_day = DateTimeFormatter.ofPattern("yyyy-MM-dd");
for (ProjectLeaseCostDetail item : items) {
if (item == null || item.getOperateTime() == null) {
@@ -498,7 +499,7 @@ public class ProjectCostServiceImpl implements ProjectCostService {
}
System.out.println("物资ID=" + machineTypeId + " 期初在用数量: " + currentCount);
-
+
// 如果期初在用数量大于0,需要计算从统计开始时间到第一个操作时间(或统计结束时间)的费用
if (currentCount > 0) {
// 找到统计期间内的第一个操作时间
@@ -519,8 +520,8 @@ public class ProjectCostServiceImpl implements ProjectCostService {
onlyAdd=false;
}
}
-
-
+
+
if (!operateTime.isBefore(startDate) && !operateTime.isAfter(endDate)) {
if (firstOperateTimeInPeriod == null || operateTime.isBefore(firstOperateTimeInPeriod)) {
firstOperateTimeInPeriod = operateTime;
@@ -531,8 +532,8 @@ public class ProjectCostServiceImpl implements ProjectCostService {
// 如果统计期间内有操作,计算从开始时间到第一个操作时间的费用
// 如果统计期间内没有操作,计算从开始时间到结束时间的费用
LocalDateTime endTimeForInitialSegment = firstOperateTimeInPeriod != null ? firstOperateTimeInPeriod : endDate;
-
-
+
+
long daysBetween = getDay(startDate, endTimeForInitialSegment);
LocalDateTime startTimes = LocalDateTime.parse(o.getStartTime()+" 00:00:01", formatter);
if(!onlyAdd) {
@@ -541,7 +542,7 @@ public class ProjectCostServiceImpl implements ProjectCostService {
add=false;
}
}
-
+
LocalDateTime lastTimes = LocalDateTime.parse(o.getEndTime()+" 23:59:59", formatter);
String parsedDateOnly = formatLocalDateTimeToDate(lastTimes);
String end_times = formatLocalDateTimeToDate(endTimeForInitialSegment);
@@ -551,14 +552,14 @@ public class ProjectCostServiceImpl implements ProjectCostService {
}else {
ProjectLeaseCostDetail item=items.get(items.size()-1);
LocalDateTime operateTime = LocalDateTime.parse(item.getOperateTime(), formatter);
-
-
+
+
//判断当前数据是最后一条数据 =====
if(2==item.getOperateType() && operateTime.equals(endTimeForInitialSegment)) {
daysBetween+=1;
}
-
-
+
+
}
}
if (daysBetween > 0) {
@@ -607,7 +608,7 @@ public class ProjectCostServiceImpl implements ProjectCostService {
// 判断操作类型:1-领料,2-退料
boolean isLease = (item.getOperateType() == null || item.getOperateType() != 2);
int quantity = 0;
-
+
// 计算上一个时间点到当前操作时间的租赁费用
if (currentCount > 0 && !previousTime.equals(operateTime)) {
if("3627".equals(machineTypeId)) {
@@ -633,25 +634,25 @@ public class ProjectCostServiceImpl implements ProjectCostService {
if (daysBetween < 1) {
daysBetween = 1;
}
-
+
if(add) {
if(parsedDateOnly.equals(end_times)) {
daysBetween+=1;
}else {
//计算--
-
+
if(i==items.size()-1 && 2==item.getOperateType()) {
long datadelNum = item.getReturnNum() != null ? item.getReturnNum() : 0;
if(currentCount==datadelNum) {
daysBetween+=1;
}
-
+
}
-
+
}
}
-//
-
+//
+
// 计算该时间段的租赁费用
double segmentAmount = currentCount * unitPrice * daysBetween;
totalItemAmount += segmentAmount;
@@ -702,7 +703,7 @@ public class ProjectCostServiceImpl implements ProjectCostService {
if (currentCount > 0 && !previousTime.equals(endDate)) {
// 计算两个时间点之间的天数
long daysBetween =getDay(previousTime, endDate);
-
+
LocalDateTime lastTimes = LocalDateTime.parse(o.getEndTime()+" 23:59:59", formatter);
String parsedDateOnly = formatLocalDateTimeToDate(lastTimes);
String end_times = formatLocalDateTimeToDate(previousTime);
@@ -726,8 +727,8 @@ public class ProjectCostServiceImpl implements ProjectCostService {
if(add) {
daysBetween+=1;
}
-
-
+
+
}
// 计算该时间段的租赁费用
@@ -814,21 +815,21 @@ public class ProjectCostServiceImpl implements ProjectCostService {
result.put("totalAmount", totalAmount); // 总金额
return result;
}
-
+
/**
* 新的结算逻辑
- *
- *
+ *
+ *
*/
public Map getNewSettlement() {
Map result = new HashMap<>();
-
+
return result;
-
+
}
-
-
-
+
+
+
/**
* 核心方法:将LocalDateTime格式化为yyyy-MM-dd字符串
* @param dateTime 待格式化的LocalDateTime对象
@@ -844,9 +845,9 @@ public class ProjectCostServiceImpl implements ProjectCostService {
LocalDate startDate = operateTime.toLocalDate();
LocalDate endDate = endtime.toLocalDate();
long naturalDays = ChronoUnit.DAYS.between(startDate, endDate);
- System.out.println("自然日天数差(正确结果):" + naturalDays);
+ System.out.println("自然日天数差(正确结果):" + naturalDays);
return naturalDays;
-
+
}
@Override
@@ -982,6 +983,13 @@ public class ProjectCostServiceImpl implements ProjectCostService {
List leaseDetails = queryProjectLeaseDetails(queryParam);
List returnDetails = queryProjectReturnDetails(queryParam);
+ //同一天的统一类型的机具进行合并
+ leaseDetails = mergeByDay(leaseDetails,1);
+ returnDetails = mergeByDay(returnDetails,2);
+
+ //计算差额
+ leaseDetails =calculateMaterialDifference(leaseDetails, returnDetails);
+
List allDetails = new ArrayList<>();
if (leaseDetails != null) {
allDetails.addAll(leaseDetails);
@@ -1064,6 +1072,142 @@ public class ProjectCostServiceImpl implements ProjectCostService {
return calculation;
}
+ /**
+ * 计算领退料差额
+ * @param leaseDetails 领料明细列表
+ * @param returnDetails 退料明细列表
+ * @return 计算后的领料明细列表(包含差额)
+ */
+ public List calculateMaterialDifference(
+ List leaseDetails,
+ List returnDetails) {
+
+ // 1. 按物料类型分组领料数据
+ Map> leaseByMaterial =
+ leaseDetails.stream()
+ .sorted(Comparator.comparing(ProjectLeaseCostDetail::getOperateTime))
+ .collect(Collectors.groupingBy(
+ ProjectLeaseCostDetail::getMachineTypeId,
+ LinkedHashMap::new, // 保持顺序
+ Collectors.toList()
+ ));
+
+ // 2. 按物料类型分组退料数据
+ Map> returnByMaterial =
+ returnDetails.stream()
+ .sorted(Comparator.comparing(ProjectLeaseCostDetail::getOperateTime))
+ .collect(Collectors.groupingBy(
+ ProjectLeaseCostDetail::getMachineTypeId,
+ LinkedHashMap::new,
+ Collectors.toList()
+ ));
+
+ // 3. 处理每个物料类型
+ List result = new ArrayList<>();
+
+ for (Map.Entry> entry : leaseByMaterial.entrySet()) {
+ Integer materialType = entry.getKey();
+ List materialLeases = entry.getValue();
+ List materialReturns =
+ returnByMaterial.getOrDefault(materialType, new ArrayList<>());
+
+ // 计算该物料的累计差额
+ List calculated =
+ calculateForSingleMaterial(materialLeases, materialReturns);
+
+ result.addAll(calculated);
+ }
+
+ return result;
+ }
+
+ /**
+ * 计算单个物料的领退料差额
+ */
+ private List calculateForSingleMaterial(
+ List leases,
+ List returns) {
+
+ List result = new ArrayList<>();
+
+ // 初始化累计量
+ BigDecimal totalReturn = BigDecimal.ZERO;
+
+ // 累计退料
+ for (ProjectLeaseCostDetail detail : returns) {
+ BigDecimal returnQty = detail.getReturnNum() != null ?
+ BigDecimal.valueOf(detail.getReturnNum()) : BigDecimal.ZERO;
+ totalReturn = totalReturn.add(returnQty);
+ }
+
+ // 按时间顺序处理每次领料
+ for (ProjectLeaseCostDetail lease : leases) {
+
+ //比较当前退料数量和领料数量
+ if(BigDecimal.valueOf(lease.getLeaseNum()).compareTo(totalReturn)>=0){
+ lease.setDifferenceQuantity(BigDecimal.valueOf(lease.getLeaseNum()).subtract(totalReturn));
+ totalReturn = BigDecimal.ZERO;
+ }else {
+ lease.setDifferenceQuantity(BigDecimal.ZERO);
+ //获取剩余的退料数量
+ totalReturn = totalReturn.subtract(BigDecimal.valueOf(lease.getLeaseNum()));
+ }
+
+ result.add(lease);
+ }
+
+ return result;
+ }
+
+ /**
+ * 根据 operateDate 按天合并,累加 leaseNum
+ */
+ public List mergeByDay(List leases, int i) {
+ List leasesNew = new ArrayList<>();
+
+ // 使用 Map 按日期分组,同一天的合并一起
+ Map mergedMap = new LinkedHashMap<>();
+
+ for (ProjectLeaseCostDetail returnDetail : leases) {
+ if ("牵张设备".equals(returnDetail.getMachineCodeName()) ||"施工机械".equals(returnDetail.getMachineCodeName()) ||"仪器设备".equals(returnDetail.getMachineCodeName())){
+ leasesNew.add(returnDetail);
+ }else {
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ String dateOnly = returnDetail.getOperateTime().substring(0, 10);
+ LocalDate dateKey = LocalDate.parse(dateOnly,
+ formatter);
+ if (mergedMap.containsKey(dateKey)) {
+ // 已存在该日期的记录,合并数量
+ ProjectLeaseCostDetail existing = mergedMap.get(dateKey);
+ if (i==1){
+ // 累加 leaseNum
+ existing.setLeaseNum(
+ (existing.getLeaseNum() != null ? existing.getLeaseNum() : 0) +
+ (returnDetail.getLeaseNum() != null ? returnDetail.getLeaseNum() : 0)
+ );
+ }else {
+ // 累加 leaseNum
+ existing.setReturnNum(
+ (existing.getReturnNum() != null ? existing.getReturnNum() : 0) +
+ (returnDetail.getReturnNum() != null ? returnDetail.getReturnNum() : 0)
+ );
+ }
+ } else {
+ // 新日期的记录,创建副本(避免修改原对象)
+ mergedMap.put(dateKey, returnDetail);
+ }
+ }
+
+ }
+
+ //保存合并后的数据
+ for (Map.Entry entry : mergedMap.entrySet()) {
+ ProjectLeaseCostDetail materialLeases = entry.getValue();
+ leasesNew.add(materialLeases);
+ }
+ return leasesNew;
+ }
+
@Override
public boolean deleteCalculation(Integer id) {
return projectCostDao.deleteCalculation(id) > 0;
@@ -1071,7 +1215,7 @@ public class ProjectCostServiceImpl implements ProjectCostService {
/**
* 封装导出Excel的方法,适用于只有HttpServletResponse的情况
- *
+ *
* @param fileName 文件名
* @param sheetName sheet名称
* @param headers 表头
@@ -1914,46 +2058,234 @@ public class ProjectCostServiceImpl implements ProjectCostService {
// 准备Excel数据
String fileName = "领退记录表_" + calculation.getProjectName() + "_" + calculation.getId() + ".xls";
- String sheetName = "领退记录表";
+ String sheetName = "领用、差缺明细表";
+ String sheetName2 = "退料明细表";
// 表头 - 增加规格型号字段
- String[] headers = { "序号", "物资类型", "规格型号", "操作类型", "操作时间", "数量", "单位", "操作人", "任务编号" };
+ /*String[] headers = { "序号", "物资类型", "规格型号", "操作类型", "操作时间", "数量", "单位", "操作人", "任务编号" };*/
+ String[] headers = { "领料日期", "工程名称", "库房", "类别", "机具设备名称", "规格型号", "编码",
+ "新编码", "单位", "数量", "差额", "租赁单位", "备注" };
+ String[] headers2 = { "退料工程名称", "退料单位", "退料日期", "库房", "类别", "机具设备名称", "规格",
+ "编号", "单位", "数量", "备注" };
// 列宽 - 添加规格型号列宽
- int[] widths = { 256 * 5, 256 * 15, 256 * 15, 256 * 10, 256 * 18, 256 * 10, 256 * 10, 256 * 10, 256 * 15 };
+ int[] widths = { 256 * 15,256 * 30,256 * 15,256 * 15,256 * 20,256 * 20,256 * 20,256 * 10,256 * 10, 256 * 10, 256 * 10, 256 * 20, 256 * 10 };
+ int[] widths2 = { 256 * 30,256 * 15,256 * 15,256 * 15,256 * 18,256 * 20,256 * 20,256 * 10,256 * 10, 256 * 10, 256 * 10 };
// 数据格式(1:String left; 2:String center; 3:String right; 4:int right; 5:float
// right)
- int[] formats = { 4, 2, 2, 2, 2, 4, 2, 2, 2 };
-
+ int[] formats = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2, 2, 2 };
+ int[] formats2 = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 4, 2 };
// 准备数据
List