From 01edd652ddfda92f1122957312bdda6f912c1903 Mon Sep 17 00:00:00 2001 From: yangk Date: Fri, 22 May 2026 14:55:06 +0800 Subject: [PATCH] =?UTF-8?q?feat(oa/erp):=20=E9=A2=84=E6=8A=95=E5=B7=A5?= =?UTF-8?q?=E6=97=B6=E5=88=86=E9=85=8D=E5=8A=9F=E8=83=BD=E7=94=B1=E5=91=98?= =?UTF-8?q?=E5=B7=A5=E7=BA=A7=E5=88=86=E9=85=8D=E6=94=B9=E4=B8=BA=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E7=BA=A7=E4=B8=94=E4=B8=8D=E5=9B=9E=E5=86=99=E6=9C=88?= =?UTF-8?q?=E6=B1=87=E6=80=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ErpTimesheetPreAllocController.java | 8 +- .../erp/domain/bo/ErpTimesheetPreAllocBo.java | 36 +- .../erp/domain/vo/ErpTimesheetPreAllocVo.java | 4 - .../oa/erp/domain/vo/PreAllocDetailVo.java | 13 +- .../mapper/ErpTimesheetPreAllocMapper.java | 29 + .../service/IErpTimesheetPreAllocService.java | 2 +- .../impl/ErpTimesheetPreAllocServiceImpl.java | 601 +++++------------- .../oa/erp/ErpTimesheetPreAllocMapper.xml | 41 ++ 8 files changed, 239 insertions(+), 495 deletions(-) diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetPreAllocController.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetPreAllocController.java index 530136ab..d07ba3ac 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetPreAllocController.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetPreAllocController.java @@ -75,10 +75,10 @@ public class ErpTimesheetPreAllocController extends BaseController { * 查询当前部门指定月份、来源预投项目的员工级可分配明细 */ @SaCheckPermission("oa/erp:timesheetPreAlloc:query") - @GetMapping("/getStaffAllocDetails") - public R getStaffAllocDetails(@NotBlank(message = "月份编码不能为空") String monthCode, - @NotNull(message = "来源预投项目不能为空") Long projectId) { - return R.ok(erpTimesheetPreAllocService.getStaffAllocDetails(monthCode, projectId)); + @GetMapping("/getAllocDetails") + public R getAllocDetails(@NotBlank(message = "月份编码不能为空") String monthCode, + @NotNull(message = "来源预投项目不能为空") Long projectId) { + return R.ok(erpTimesheetPreAllocService.getAllocDetails(monthCode, projectId)); } /** diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetPreAllocBo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetPreAllocBo.java index 64f6efbe..153e16aa 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetPreAllocBo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/bo/ErpTimesheetPreAllocBo.java @@ -1,13 +1,15 @@ package org.dromara.oa.erp.domain.bo; -import org.dromara.oa.erp.domain.ErpTimesheetPreAlloc; -import org.dromara.common.mybatis.core.domain.BaseEntity; -import org.dromara.common.core.validate.AddGroup; -import org.dromara.common.core.validate.EditGroup; import io.github.linpeilie.annotations.AutoMapper; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.EqualsAndHashCode; -import jakarta.validation.constraints.*; +import org.dromara.common.core.validate.AddGroup; +import org.dromara.common.core.validate.EditGroup; +import org.dromara.common.mybatis.core.domain.BaseEntity; +import org.dromara.oa.erp.domain.ErpTimesheetPreAlloc; + import java.math.BigDecimal; import java.util.Date; import java.util.List; @@ -37,11 +39,11 @@ public class ErpTimesheetPreAllocBo extends BaseEntity { /** * 月份编码(YYYYMM) */ - @NotBlank(message = "月份编码(YYYYMM)不能为空", groups = { AddGroup.class, EditGroup.class }) + @NotBlank(message = "月份编码不能为空", groups = { AddGroup.class, EditGroup.class }) private String monthCode; /** - * 关联月标准工时ID(erp_timesheet_standard_month) + * 关联月标准工时ID,预投分配独立后业务不再使用 */ private Long standardMonthId; @@ -52,22 +54,22 @@ public class ErpTimesheetPreAllocBo extends BaseEntity { private Long projectId; /** - * 预投项目编码(冗余) + * 预投项目编码 */ private String projectCode; /** - * 预投项目名称(冗余) + * 预投项目名称 */ private String projectName; /** - * 来源预投工时合计(天,带出汇总) + * 来源预投工时合计 */ private BigDecimal sourceTotalHours; /** - * 已分配合计(天) + * 已分配合计 */ private BigDecimal allocatedTotalHours; @@ -77,22 +79,22 @@ public class ErpTimesheetPreAllocBo extends BaseEntity { private Integer staffCount; /** - * 单据状态(0未分配 1部分分配 2已分配) + * 单据状态:0未分配、1部分分配、2已分配 */ private String allocStatus; /** - * 是否已回写月汇总(0否 1是) + * 旧回写标记,预投分配独立后业务不再使用 */ private String appliedFlag; /** - * 回写关联的月汇总主表ID + * 旧月汇总主表ID,预投分配独立后业务不再使用 */ private Long summaryId; /** - * 回写月汇总时间 + * 旧回写时间,预投分配独立后业务不再使用 */ private Date applyTime; @@ -102,8 +104,8 @@ public class ErpTimesheetPreAllocBo extends BaseEntity { private String remark; /** - * 员工级分配明细 + * 目标项目分配明细 */ - private List staffAllocList; + private List allocItems; } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetPreAllocVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetPreAllocVo.java index 79267b65..939e9a12 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetPreAllocVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ErpTimesheetPreAllocVo.java @@ -99,20 +99,16 @@ public class ErpTimesheetPreAllocVo implements Serializable { /** * 是否已回写月汇总(0否 1是) */ - @ExcelProperty(value = "是否已回写月汇总", converter = ExcelDictConvert.class) - @ExcelDictFormat(dictType = "applied_flag") private String appliedFlag; /** * 回写关联的月汇总主表ID */ - @ExcelProperty(value = "回写关联的月汇总主表ID") private Long summaryId; /** * 回写月汇总时间 */ - @ExcelProperty(value = "回写月汇总时间") private Date applyTime; /** diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/PreAllocDetailVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/PreAllocDetailVo.java index 1bb503a5..7d9006bb 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/PreAllocDetailVo.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/PreAllocDetailVo.java @@ -6,11 +6,10 @@ import java.io.Serial; import java.io.Serializable; import java.math.BigDecimal; import java.util.ArrayList; -import java.util.Date; import java.util.List; /** - * 预投工时分配员工级详情 + * 预投工时分配整单详情 * * @author Yangk * @date 2026-05-21 @@ -48,17 +47,11 @@ public class PreAllocDetailVo implements Serializable { private String allocStatus; - private String appliedFlag; - - private Long summaryId; - - private Date applyTime; - private String remark; /** - * 员工级分配明细 + * 目标项目分配明细 */ - private List staffAllocList = new ArrayList<>(); + private List allocItems = new ArrayList<>(); } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpTimesheetPreAllocMapper.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpTimesheetPreAllocMapper.java index c3ed9383..82c697a1 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpTimesheetPreAllocMapper.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpTimesheetPreAllocMapper.java @@ -1,13 +1,16 @@ package org.dromara.oa.erp.mapper; +import java.sql.Date; import java.util.List; import com.github.yulichang.wrapper.MPJLambdaWrapper; import com.baomidou.mybatisplus.core.toolkit.Constants; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.ibatis.annotations.Param; import org.dromara.oa.erp.domain.ErpTimesheetPreAlloc; +import org.dromara.oa.erp.domain.vo.ErpProjectInfoVo; import org.dromara.oa.erp.domain.vo.ErpTimesheetPreAllocVo; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; +import org.dromara.oa.erp.domain.vo.PreAllocSourceStatVo; /** * 预投工时分配Mapper接口 @@ -34,4 +37,30 @@ public interface ErpTimesheetPreAllocMapper extends BaseMapperPlus selectCustomErpTimesheetPreAllocVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper queryWrapper); + /** + * 查询当前部门指定月份有已审批填报工时的预投项目 + * + * @param deptId 当前部门ID + * @param startDate 月初 + * @param endDate 月末 + * @return 预投项目列表 + */ + List selectAvailableSourceProjects(@Param("deptId") Long deptId, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate); + + /** + * 统计当前部门指定月份、指定预投项目的已审批原始填报工时 + * + * @param deptId 当前部门ID + * @param startDate 月初 + * @param endDate 月末 + * @param projectId 来源预投项目ID + * @return 来源工时和涉及人数 + */ + PreAllocSourceStatVo selectSourceStat(@Param("deptId") Long deptId, + @Param("startDate") Date startDate, + @Param("endDate") Date endDate, + @Param("projectId") Long projectId); + } diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/IErpTimesheetPreAllocService.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/IErpTimesheetPreAllocService.java index b3e92ea1..17140c69 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/IErpTimesheetPreAllocService.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/IErpTimesheetPreAllocService.java @@ -58,7 +58,7 @@ public interface IErpTimesheetPreAllocService { * @param projectId 来源预投项目ID * @return 员工级可分配明细 */ - PreAllocDetailVo getStaffAllocDetails(String monthCode, Long projectId); + PreAllocDetailVo getAllocDetails(String monthCode, Long projectId); /** * 查询当前部门指定月份已填报工时的来源预投项目 diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetPreAllocServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetPreAllocServiceImpl.java index 512f625c..b28619ef 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetPreAllocServiceImpl.java +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetPreAllocServiceImpl.java @@ -1,9 +1,9 @@ package org.dromara.oa.erp.service.impl; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.github.yulichang.toolkit.JoinWrappers; import com.github.yulichang.wrapper.MPJLambdaWrapper; -import com.baomidou.mybatisplus.core.toolkit.Wrappers; import lombok.RequiredArgsConstructor; import org.apache.dubbo.config.annotation.DubboReference; import org.dromara.common.core.exception.ServiceException; @@ -14,40 +14,33 @@ import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.oa.erp.domain.ErpProjectInfo; import org.dromara.oa.erp.domain.ErpTimesheetPreAlloc; import org.dromara.oa.erp.domain.ErpTimesheetPreAllocDetail; -import org.dromara.oa.erp.domain.ErpTimesheetSummary; -import org.dromara.oa.erp.domain.ErpTimesheetSummaryDetail; import org.dromara.oa.erp.domain.bo.ErpTimesheetPreAllocBo; -import org.dromara.oa.erp.domain.bo.PreAllocStaffAllocBo; import org.dromara.oa.erp.domain.bo.PreAllocTargetBo; import org.dromara.oa.erp.domain.vo.ErpProjectInfoVo; import org.dromara.oa.erp.domain.vo.ErpTimesheetPreAllocVo; import org.dromara.oa.erp.domain.vo.PreAllocDetailVo; -import org.dromara.oa.erp.domain.vo.PreAllocStaffAllocVo; +import org.dromara.oa.erp.domain.vo.PreAllocSourceStatVo; import org.dromara.oa.erp.domain.vo.PreAllocTargetVo; import org.dromara.oa.erp.mapper.ErpProjectInfoMapper; import org.dromara.oa.erp.mapper.ErpTimesheetPreAllocDetailMapper; import org.dromara.oa.erp.mapper.ErpTimesheetPreAllocMapper; -import org.dromara.oa.erp.mapper.ErpTimesheetSummaryDetailMapper; -import org.dromara.oa.erp.mapper.ErpTimesheetSummaryMapper; import org.dromara.oa.erp.service.IErpTimesheetPreAllocService; import org.dromara.system.api.RemoteCodeRuleService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; +import java.sql.Date; +import java.time.YearMonth; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.Comparator; -import java.util.Date; -import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; -import java.util.stream.Collectors; /** * 预投工时分配Service业务层处理 @@ -60,20 +53,15 @@ import java.util.stream.Collectors; public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocService { private static final String DEL_FLAG_NORMAL = "0"; - private static final String PROJECT_WORK = "1"; - private static final String MANUAL_ROW = "0"; private static final String PRE_PROJECT_CATEGORY = "4"; private static final Set TARGET_PROJECT_CATEGORIES = Set.of("1", "2"); private static final String STATUS_NOT_ALLOCATED = "0"; private static final String STATUS_PART_ALLOCATED = "1"; private static final String STATUS_ALLOCATED = "2"; - private static final String APPLIED = "1"; private static final Pattern MONTH_CODE_PATTERN = Pattern.compile("^\\d{6}$"); private final ErpTimesheetPreAllocMapper baseMapper; private final ErpTimesheetPreAllocDetailMapper preAllocDetailMapper; - private final ErpTimesheetSummaryMapper summaryMapper; - private final ErpTimesheetSummaryDetailMapper summaryDetailMapper; private final ErpProjectInfoMapper projectInfoMapper; @DubboReference @@ -91,26 +79,22 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer } /** - * 查询预投工时分配员工级详情 + * 查询预投工时分配整单详情 * * @param allocId 主键 - * @return 预投工时分配员工级详情 + * @return 预投工时分配整单详情 */ @Override public PreAllocDetailVo queryDetailById(Long allocId) { ErpTimesheetPreAlloc alloc = baseMapper.selectById(allocId); - if (alloc == null) { + if (alloc == null || !DEL_FLAG_NORMAL.equals(alloc.getDelFlag())) { throw new ServiceException("预投工时分配单不存在"); } - Long deptId = LoginHelper.getDeptId(); + Long deptId = requireDeptId(); if (alloc.getCreateDept() != null && !Objects.equals(alloc.getCreateDept(), deptId)) { throw new ServiceException("只能查看当前登录部门的预投工时分配单"); } - ErpTimesheetSummary summary = summaryMapper.selectById(alloc.getSummaryId()); - if (summary != null && !Objects.equals(summary.getDeptId(), deptId)) { - throw new ServiceException("只能查看当前登录部门的预投工时分配单"); - } - return getStaffAllocDetails(alloc.getMonthCode(), alloc.getProjectId()); + return getAllocDetails(alloc.getMonthCode(), alloc.getProjectId()); } /** @@ -147,71 +131,54 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer .eq(deptId != null, ErpTimesheetPreAlloc::getCreateDept, deptId) .eq(StringUtils.isNotBlank(bo.getAllocCode()), ErpTimesheetPreAlloc::getAllocCode, bo.getAllocCode()) .eq(StringUtils.isNotBlank(bo.getMonthCode()), ErpTimesheetPreAlloc::getMonthCode, bo.getMonthCode()) - .eq(bo.getStandardMonthId() != null, ErpTimesheetPreAlloc::getStandardMonthId, bo.getStandardMonthId()) .eq(bo.getProjectId() != null, ErpTimesheetPreAlloc::getProjectId, bo.getProjectId()) .eq(StringUtils.isNotBlank(bo.getProjectCode()), ErpTimesheetPreAlloc::getProjectCode, bo.getProjectCode()) .like(StringUtils.isNotBlank(bo.getProjectName()), ErpTimesheetPreAlloc::getProjectName, bo.getProjectName()) .eq(StringUtils.isNotBlank(bo.getAllocStatus()), ErpTimesheetPreAlloc::getAllocStatus, bo.getAllocStatus()) - .eq(StringUtils.isNotBlank(bo.getAppliedFlag()), ErpTimesheetPreAlloc::getAppliedFlag, bo.getAppliedFlag()) - .eq(bo.getSummaryId() != null, ErpTimesheetPreAlloc::getSummaryId, bo.getSummaryId()) - .eq(bo.getApplyTime() != null, ErpTimesheetPreAlloc::getApplyTime, bo.getApplyTime()) - .orderByDesc(ErpTimesheetPreAlloc::getApplyTime) .orderByDesc(ErpTimesheetPreAlloc::getAllocId); } /** - * 查询当前部门指定月份、来源预投项目的员工级可分配明细 + * 查询当前部门指定月份、来源预投项目的整单分配详情 * * @param monthCode 月份编码(YYYYMM) * @param projectId 来源预投项目ID - * @return 员工级可分配明细 + * @return 整单分配详情 */ @Override - public PreAllocDetailVo getStaffAllocDetails(String monthCode, Long projectId) { + public PreAllocDetailVo getAllocDetails(String monthCode, Long projectId) { validateMonthCode(monthCode); if (projectId == null) { throw new ServiceException("来源预投项目不能为空"); } - Long deptId = LoginHelper.getDeptId(); - ErpTimesheetSummary summary = getSingleSummary(monthCode, deptId); + Long deptId = requireDeptId(); ErpProjectInfo sourceProject = getProject(projectId, "来源预投项目不存在"); validateProjectCategory(sourceProject, PRE_PROJECT_CATEGORY, "来源项目必须是预投项目"); - ErpTimesheetPreAlloc alloc = queryActiveAlloc(summary.getSummaryId(), projectId); - List staffSources = loadStaffSources(summary.getSummaryId(), sourceProject); - List staffVos = buildStaffVos(staffSources); + PreAllocSourceStatVo sourceStat = loadSourceStat(monthCode, deptId, projectId); + ErpTimesheetPreAlloc alloc = queryActiveAlloc(monthCode, projectId, deptId); + List allocItems = buildTargetVos(alloc == null ? null : alloc.getAllocId()); + BigDecimal allocatedTotal = allocItems.stream() + .map(item -> nvl(item.getAllocHours())) + .reduce(BigDecimal.ZERO, BigDecimal::add); + BigDecimal sourceTotal = nvl(sourceStat.getSourceTotalHours()); PreAllocDetailVo detailVo = new PreAllocDetailVo(); if (alloc != null) { detailVo.setAllocId(alloc.getAllocId()); detailVo.setAllocCode(alloc.getAllocCode()); - detailVo.setAppliedFlag(alloc.getAppliedFlag()); - detailVo.setApplyTime(alloc.getApplyTime()); detailVo.setRemark(alloc.getRemark()); - } else { - detailVo.setAppliedFlag("0"); } detailVo.setMonthCode(monthCode); - detailVo.setStandardMonthId(summary.getStandardMonthId()); - detailVo.setSummaryId(summary.getSummaryId()); detailVo.setProjectId(sourceProject.getProjectId()); detailVo.setProjectCode(sourceProject.getProjectCode()); detailVo.setProjectName(sourceProject.getProjectName()); - detailVo.setStaffAllocList(staffVos); - - BigDecimal sourceTotal = staffVos.stream() - .map(PreAllocStaffAllocVo::getSourceHours) - .reduce(BigDecimal.ZERO, BigDecimal::add); - BigDecimal allocatedTotal = staffVos.stream() - .map(PreAllocStaffAllocVo::getAllocatedHours) - .reduce(BigDecimal.ZERO, BigDecimal::add); detailVo.setSourceTotalHours(sourceTotal); detailVo.setAllocatedTotalHours(allocatedTotal); detailVo.setRemainingTotalHours(sourceTotal.subtract(allocatedTotal)); - detailVo.setStaffCount((int) staffVos.stream() - .filter(item -> item.getSourceHours().compareTo(BigDecimal.ZERO) > 0) - .count()); + detailVo.setStaffCount(sourceStat.getStaffCount()); detailVo.setAllocStatus(resolveAllocStatus(sourceTotal, allocatedTotal)); + detailVo.setAllocItems(allocItems); return detailVo; } @@ -224,34 +191,9 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer @Override public List listAvailableSourceProjects(String monthCode) { validateMonthCode(monthCode); - Long deptId = LoginHelper.getDeptId(); - ErpTimesheetSummary summary = getSingleSummaryOrNull(monthCode, deptId); - if (summary == null) { - return Collections.emptyList(); - } - - List detailList = summaryDetailMapper.selectList(Wrappers.lambdaQuery() - .select(ErpTimesheetSummaryDetail::getOriginalProjectId) - .eq(ErpTimesheetSummaryDetail::getSummaryId, summary.getSummaryId()) - .eq(ErpTimesheetSummaryDetail::getIsProject, PROJECT_WORK) - .eq(ErpTimesheetSummaryDetail::getDelFlag, DEL_FLAG_NORMAL) - .isNotNull(ErpTimesheetSummaryDetail::getOriginalProjectId) - .gt(ErpTimesheetSummaryDetail::getOriginalHours, BigDecimal.ZERO)); - List projectIds = detailList.stream() - .map(ErpTimesheetSummaryDetail::getOriginalProjectId) - .filter(Objects::nonNull) - .distinct() - .collect(Collectors.toList()); - if (projectIds.isEmpty()) { - return Collections.emptyList(); - } - - return projectInfoMapper.selectVoList(Wrappers.lambdaQuery() - .in(ErpProjectInfo::getProjectId, projectIds) - .eq(ErpProjectInfo::getProjectCategory, PRE_PROJECT_CATEGORY) - .eq(ErpProjectInfo::getDelFlag, DEL_FLAG_NORMAL) - .orderByAsc(ErpProjectInfo::getProjectCode) - .orderByAsc(ErpProjectInfo::getProjectName)); + Long deptId = requireDeptId(); + MonthRange range = monthRange(monthCode); + return baseMapper.selectAvailableSourceProjects(deptId, range.startDate(), range.endDate()); } /** @@ -277,7 +219,7 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer } /** - * 保存预投工时分配并回写月汇总明细 + * 保存预投工时分配,只维护分配主表和归集明细表 * * @param bo 预投工时分配 * @return 是否保存成功 @@ -290,26 +232,18 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer throw new ServiceException("来源预投项目不能为空"); } - Long deptId = LoginHelper.getDeptId(); - ErpTimesheetSummary summary = getSingleSummary(bo.getMonthCode(), deptId); - ErpTimesheetSummary lockedSummary = lockSummary(summary.getSummaryId()); - if (!Objects.equals(lockedSummary.getDeptId(), deptId)) { - throw new ServiceException("只能维护当前登录部门的预投工时分配"); - } - + Long deptId = requireDeptId(); ErpProjectInfo sourceProject = getProject(bo.getProjectId(), "来源预投项目不存在"); validateProjectCategory(sourceProject, PRE_PROJECT_CATEGORY, "来源项目必须是预投项目"); - List staffSources = loadStaffSources(lockedSummary.getSummaryId(), sourceProject); - BigDecimal sourceTotal = staffSources.stream() - .map(StaffSource::getSourceHours) - .reduce(BigDecimal.ZERO, BigDecimal::add); + + PreAllocSourceStatVo sourceStat = loadSourceStat(bo.getMonthCode(), deptId, sourceProject.getProjectId()); + BigDecimal sourceTotal = nvl(sourceStat.getSourceTotalHours()); if (sourceTotal.compareTo(BigDecimal.ZERO) <= 0) { throw new ServiceException("本月该预投项目没有可分配工时"); } - AllocationResult allocationResult = buildAllocationResult( - bo.getStaffAllocList(), staffSources, lockedSummary.getSummaryId(), sourceProject); - ErpTimesheetPreAlloc alloc = resolveAlloc(bo.getAllocId(), lockedSummary.getSummaryId(), sourceProject.getProjectId(), deptId); + AllocationResult allocationResult = buildAllocationResult(bo.getAllocItems(), sourceTotal); + ErpTimesheetPreAlloc alloc = resolveAlloc(bo.getAllocId(), bo.getMonthCode(), sourceProject.getProjectId(), deptId); boolean isNew = alloc == null; if (isNew) { alloc = new ErpTimesheetPreAlloc(); @@ -319,16 +253,14 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer alloc.setAllocCode(generateAllocCode()); } - fillAlloc(alloc, bo, lockedSummary, sourceProject, allocationResult); + fillAlloc(alloc, bo, sourceProject, sourceStat, allocationResult, deptId); if (isNew) { baseMapper.insert(alloc); } else { baseMapper.updateById(alloc); } - rewriteSummaryDetails(lockedSummary.getSummaryId(), sourceProject.getProjectId(), allocationResult.getSummaryDetails()); - rewritePreAllocDetails(alloc, sourceProject, allocationResult.getTargetAggregations()); - recalculateSummary(lockedSummary.getSummaryId()); + rewritePreAllocDetails(alloc, sourceProject, allocationResult.targetAllocations()); bo.setAllocId(alloc.getAllocId()); return true; } @@ -352,11 +284,8 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer if (allocList.size() != ids.size()) { throw new ServiceException("部分预投工时分配单不存在或已删除"); } - Long deptId = LoginHelper.getDeptId(); + Long deptId = requireDeptId(); for (ErpTimesheetPreAlloc alloc : allocList) { - if (APPLIED.equals(alloc.getAppliedFlag())) { - throw new ServiceException("已回写月汇总的预投工时分配单不允许删除"); - } if (alloc.getCreateDept() != null && !Objects.equals(alloc.getCreateDept(), deptId)) { throw new ServiceException("只能删除当前登录部门的预投工时分配单"); } @@ -370,308 +299,72 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer if (StringUtils.isBlank(monthCode) || !MONTH_CODE_PATTERN.matcher(monthCode).matches()) { throw new ServiceException("月份编码必须是YYYYMM格式"); } + int month = Integer.parseInt(monthCode.substring(4, 6)); + if (month < 1 || month > 12) { + throw new ServiceException("月份编码必须是有效月份"); + } } - private ErpTimesheetSummary getSingleSummary(String monthCode, Long deptId) { + private Long requireDeptId() { + Long deptId = LoginHelper.getDeptId(); if (deptId == null) { throw new ServiceException("当前登录用户未绑定部门,无法进行预投工时分配"); } - List summaries = summaryMapper.selectList(Wrappers.lambdaQuery() - .eq(ErpTimesheetSummary::getMonthCode, monthCode) - .eq(ErpTimesheetSummary::getDeptId, deptId) - .eq(ErpTimesheetSummary::getDelFlag, DEL_FLAG_NORMAL)); - if (summaries.isEmpty()) { - throw new ServiceException("当前部门该月份的月汇总工时不存在"); - } - if (summaries.size() > 1) { - throw new ServiceException("当前部门该月份存在多个月汇总工时,请先修正月汇总数据"); - } - return summaries.get(0); + return deptId; } - private ErpTimesheetSummary getSingleSummaryOrNull(String monthCode, Long deptId) { - if (deptId == null) { - throw new ServiceException("当前登录用户未绑定部门,无法进行预投工时分配"); - } - List summaries = summaryMapper.selectList(Wrappers.lambdaQuery() - .eq(ErpTimesheetSummary::getMonthCode, monthCode) - .eq(ErpTimesheetSummary::getDeptId, deptId) - .eq(ErpTimesheetSummary::getDelFlag, DEL_FLAG_NORMAL)); - if (summaries.isEmpty()) { - return null; - } - if (summaries.size() > 1) { - throw new ServiceException("当前部门该月份存在多个月汇总工时,请先修正月汇总数据"); - } - return summaries.get(0); + private MonthRange monthRange(String monthCode) { + YearMonth yearMonth = YearMonth.of( + Integer.parseInt(monthCode.substring(0, 4)), + Integer.parseInt(monthCode.substring(4, 6))); + return new MonthRange(Date.valueOf(yearMonth.atDay(1)), Date.valueOf(yearMonth.atEndOfMonth())); } - private ErpTimesheetSummary lockSummary(Long summaryId) { - ErpTimesheetSummary summary = summaryMapper.selectOne(Wrappers.lambdaQuery() - .eq(ErpTimesheetSummary::getSummaryId, summaryId) - .eq(ErpTimesheetSummary::getDelFlag, DEL_FLAG_NORMAL) - .last("FOR UPDATE")); - if (summary == null) { - throw new ServiceException("月汇总工时不存在或已删除"); + private PreAllocSourceStatVo loadSourceStat(String monthCode, Long deptId, Long projectId) { + MonthRange range = monthRange(monthCode); + PreAllocSourceStatVo sourceStat = baseMapper.selectSourceStat(deptId, range.startDate(), range.endDate(), projectId); + if (sourceStat == null) { + sourceStat = new PreAllocSourceStatVo(); } - return summary; + sourceStat.setSourceTotalHours(nvl(sourceStat.getSourceTotalHours())); + sourceStat.setStaffCount(sourceStat.getStaffCount() == null ? 0 : sourceStat.getStaffCount()); + return sourceStat; } - private ErpProjectInfo getProject(Long projectId, String message) { + private ErpProjectInfo getProject(Long projectId, String errorMessage) { ErpProjectInfo project = projectInfoMapper.selectById(projectId); if (project == null || !DEL_FLAG_NORMAL.equals(project.getDelFlag())) { - throw new ServiceException(message); + throw new ServiceException(errorMessage); } return project; } - private void validateProjectCategory(ErpProjectInfo project, String expectedCategory, String message) { + private void validateProjectCategory(ErpProjectInfo project, String expectedCategory, String errorMessage) { if (!expectedCategory.equals(project.getProjectCategory())) { - throw new ServiceException(message); + throw new ServiceException(errorMessage); } } private void validateTargetProject(ErpProjectInfo project) { if (!TARGET_PROJECT_CATEGORIES.contains(project.getProjectCategory())) { - throw new ServiceException("目标项目只能选择物流或备件项目"); + throw new ServiceException("目标项目必须是物流或备件项目"); } } - private ErpTimesheetPreAlloc queryActiveAlloc(Long summaryId, Long projectId) { + private ErpTimesheetPreAlloc queryActiveAlloc(String monthCode, Long projectId, Long deptId) { List allocList = baseMapper.selectList(Wrappers.lambdaQuery() - .eq(ErpTimesheetPreAlloc::getSummaryId, summaryId) + .eq(ErpTimesheetPreAlloc::getMonthCode, monthCode) .eq(ErpTimesheetPreAlloc::getProjectId, projectId) + .eq(ErpTimesheetPreAlloc::getCreateDept, deptId) .eq(ErpTimesheetPreAlloc::getDelFlag, DEL_FLAG_NORMAL)); if (allocList.size() > 1) { - throw new ServiceException("当前部门该月份该预投项目存在多张分配单,请先修正分配单数据"); + throw new ServiceException("当前部门该月份该预投项目存在多张有效分配单,请先修正数据"); } return allocList.isEmpty() ? null : allocList.get(0); } - private List loadStaffSources(Long summaryId, ErpProjectInfo sourceProject) { - List detailList = summaryDetailMapper.selectList(Wrappers.lambdaQuery() - .eq(ErpTimesheetSummaryDetail::getSummaryId, summaryId) - .eq(ErpTimesheetSummaryDetail::getOriginalProjectId, sourceProject.getProjectId()) - .eq(ErpTimesheetSummaryDetail::getIsProject, PROJECT_WORK) - .eq(ErpTimesheetSummaryDetail::getDelFlag, DEL_FLAG_NORMAL) - .orderByAsc(ErpTimesheetSummaryDetail::getSortOrder) - .orderByAsc(ErpTimesheetSummaryDetail::getSummaryDetailId)); - - Map> staffMap = detailList.stream() - .filter(detail -> detail.getStaffUserId() != null) - .collect(Collectors.groupingBy( - ErpTimesheetSummaryDetail::getStaffUserId, - LinkedHashMap::new, - Collectors.toList())); - - List result = new ArrayList<>(); - for (Map.Entry> entry : staffMap.entrySet()) { - List rows = entry.getValue(); - List positiveOriginalHours = rows.stream() - .map(item -> nvl(item.getOriginalHours())) - .filter(value -> value.compareTo(BigDecimal.ZERO) > 0) - .collect(Collectors.toList()); - long distinctPositiveCount = positiveOriginalHours.stream() - .map(this::decimalKey) - .distinct() - .count(); - if (distinctPositiveCount > 1) { - String staffName = rows.get(0).getStaffName(); - throw new ServiceException("员工[" + staffName + "]同一预投项目存在多个原始工时,请先修正月汇总数据"); - } - - BigDecimal sourceHours = positiveOriginalHours.stream() - .max(Comparator.naturalOrder()) - .orElse(BigDecimal.ZERO); - StaffSource staffSource = new StaffSource(); - staffSource.setStaffUserId(entry.getKey()); - staffSource.setStaffName(rows.get(0).getStaffName()); - staffSource.setSourceHours(sourceHours); - staffSource.setExistingRows(rows); - result.add(staffSource); - } - return result; - } - - private List buildStaffVos(List staffSources) { - List staffVos = new ArrayList<>(); - for (StaffSource source : staffSources) { - PreAllocStaffAllocVo staffVo = new PreAllocStaffAllocVo(); - staffVo.setStaffUserId(source.getStaffUserId()); - staffVo.setStaffName(source.getStaffName()); - staffVo.setSourceHours(source.getSourceHours()); - - Map targetMap = new LinkedHashMap<>(); - for (ErpTimesheetSummaryDetail row : source.getExistingRows()) { - BigDecimal adjustedHours = nvl(row.getAdjustedHours()); - if (adjustedHours.compareTo(BigDecimal.ZERO) <= 0) { - continue; - } - if (Objects.equals(row.getAdjustedProjectId(), row.getOriginalProjectId())) { - continue; - } - if (row.getAdjustedProjectId() == null) { - continue; - } - PreAllocTargetVo targetVo = targetMap.computeIfAbsent(row.getAdjustedProjectId(), targetProjectId -> { - PreAllocTargetVo vo = new PreAllocTargetVo(); - vo.setTargetProjectId(targetProjectId); - vo.setTargetProjectCode(row.getAdjustedProjectCode()); - vo.setTargetProjectName(row.getAdjustedProjectName()); - vo.setAllocHours(BigDecimal.ZERO); - return vo; - }); - targetVo.setAllocHours(targetVo.getAllocHours().add(adjustedHours)); - } - staffVo.setAllocItems(new ArrayList<>(targetMap.values())); - BigDecimal allocatedHours = staffVo.getAllocItems().stream() - .map(PreAllocTargetVo::getAllocHours) - .reduce(BigDecimal.ZERO, BigDecimal::add); - staffVo.setAllocatedHours(allocatedHours); - staffVo.setRemainingHours(staffVo.getSourceHours().subtract(allocatedHours)); - staffVos.add(staffVo); - } - return staffVos; - } - - private AllocationResult buildAllocationResult(List inputList, - List staffSources, - Long summaryId, - ErpProjectInfo sourceProject) { - Map sourceMap = staffSources.stream() - .filter(item -> item.getSourceHours().compareTo(BigDecimal.ZERO) > 0) - .collect(Collectors.toMap(StaffSource::getStaffUserId, item -> item, (a, b) -> a, LinkedHashMap::new)); - Map> inputMap = buildInputMap(inputList, sourceMap); - Map projectCache = new HashMap<>(); - List newSummaryDetails = new ArrayList<>(); - Map targetAggregations = new LinkedHashMap<>(); - - int sortOrder = 1; - BigDecimal allocatedTotal = BigDecimal.ZERO; - for (StaffSource source : sourceMap.values()) { - List targetInputs = inputMap.getOrDefault(source.getStaffUserId(), Collections.emptyList()); - BigDecimal staffAllocated = BigDecimal.ZERO; - List targetAllocations = new ArrayList<>(); - Set usedTargetIds = new java.util.HashSet<>(); - - for (PreAllocTargetBo targetInput : targetInputs) { - if (targetInput == null) { - throw new ServiceException("员工分配明细不能为空"); - } - Long targetProjectId = targetInput.getTargetProjectId(); - if (targetProjectId == null) { - throw new ServiceException("目标项目不能为空"); - } - if (!usedTargetIds.add(targetProjectId)) { - throw new ServiceException("员工[" + source.getStaffName() + "]不能重复选择同一目标项目"); - } - BigDecimal allocHours = nvl(targetInput.getAllocHours()); - if (allocHours.compareTo(BigDecimal.ZERO) <= 0) { - throw new ServiceException("员工[" + source.getStaffName() + "]分配工时必须大于0"); - } - ErpProjectInfo targetProject = projectCache.computeIfAbsent(targetProjectId, - id -> getProject(id, "目标项目不存在")); - validateTargetProject(targetProject); - - staffAllocated = staffAllocated.add(allocHours); - targetAllocations.add(new TargetAllocation(targetProject, allocHours)); - } - - if (staffAllocated.compareTo(source.getSourceHours()) > 0) { - throw new ServiceException("员工[" + source.getStaffName() + "]分配工时不能超过原预投工时"); - } - allocatedTotal = allocatedTotal.add(staffAllocated); - - boolean originalHoursWritten = false; - for (TargetAllocation allocation : targetAllocations) { - ErpTimesheetSummaryDetail detail = buildSummaryDetail( - summaryId, sortOrder++, sourceProject, source, allocation.getTargetProject(), allocation.getAllocHours(), - originalHoursWritten ? BigDecimal.ZERO : source.getSourceHours()); - originalHoursWritten = true; - newSummaryDetails.add(detail); - targetAggregations.computeIfAbsent(allocation.getTargetProject().getProjectId(), id -> { - TargetAggregation aggregation = new TargetAggregation(); - aggregation.setTargetProject(allocation.getTargetProject()); - aggregation.setAllocHours(BigDecimal.ZERO); - return aggregation; - }).addAllocHours(allocation.getAllocHours()); - } - - BigDecimal remainingHours = source.getSourceHours().subtract(staffAllocated); - if (remainingHours.compareTo(BigDecimal.ZERO) > 0) { - ErpTimesheetSummaryDetail detail = buildSummaryDetail( - summaryId, sortOrder++, sourceProject, source, sourceProject, remainingHours, - originalHoursWritten ? BigDecimal.ZERO : source.getSourceHours()); - newSummaryDetails.add(detail); - } - } - - AllocationResult result = new AllocationResult(); - result.setSourceTotal(sourceMap.values().stream() - .map(StaffSource::getSourceHours) - .reduce(BigDecimal.ZERO, BigDecimal::add)); - result.setAllocatedTotal(allocatedTotal); - result.setStaffCount(sourceMap.size()); - result.setSummaryDetails(newSummaryDetails); - result.setTargetAggregations(new ArrayList<>(targetAggregations.values())); - return result; - } - - private Map> buildInputMap(List inputList, - Map sourceMap) { - Map> inputMap = new HashMap<>(); - if (inputList == null) { - return inputMap; - } - Set seenStaffIds = new java.util.HashSet<>(); - for (PreAllocStaffAllocBo staffInput : inputList) { - if (staffInput == null || staffInput.getStaffUserId() == null) { - throw new ServiceException("员工分配信息不能为空"); - } - if (!seenStaffIds.add(staffInput.getStaffUserId())) { - throw new ServiceException("员工分配信息存在重复员工"); - } - StaffSource source = sourceMap.get(staffInput.getStaffUserId()); - if (source == null) { - throw new ServiceException("员工[" + staffInput.getStaffName() + "]不属于当前预投项目可分配范围"); - } - if (staffInput.getSourceHours() != null - && staffInput.getSourceHours().compareTo(source.getSourceHours()) != 0) { - throw new ServiceException("员工[" + source.getStaffName() + "]预投工时已变化,请重新查询后分配"); - } - inputMap.put(staffInput.getStaffUserId(), - staffInput.getAllocItems() == null ? Collections.emptyList() : staffInput.getAllocItems()); - } - return inputMap; - } - - private ErpTimesheetSummaryDetail buildSummaryDetail(Long summaryId, - int sortOrder, - ErpProjectInfo sourceProject, - StaffSource source, - ErpProjectInfo adjustedProject, - BigDecimal adjustedHours, - BigDecimal originalHours) { - ErpTimesheetSummaryDetail detail = new ErpTimesheetSummaryDetail(); - detail.setSummaryId(summaryId); - detail.setSortOrder(sortOrder); - detail.setStaffUserId(source.getStaffUserId()); - detail.setStaffName(source.getStaffName()); - detail.setIsProject(PROJECT_WORK); - detail.setOriginalProjectId(sourceProject.getProjectId()); - detail.setOriginalProjectCode(sourceProject.getProjectCode()); - detail.setOriginalProjectName(sourceProject.getProjectName()); - detail.setOriginalHours(originalHours); - detail.setAdjustedProjectId(adjustedProject.getProjectId()); - detail.setAdjustedProjectCode(adjustedProject.getProjectCode()); - detail.setAdjustedProjectName(adjustedProject.getProjectName()); - detail.setAdjustedHours(adjustedHours); - detail.setIsGenerated(MANUAL_ROW); - return detail; - } - - private ErpTimesheetPreAlloc resolveAlloc(Long allocId, Long summaryId, Long projectId, Long deptId) { - ErpTimesheetPreAlloc keyAlloc = queryActiveAlloc(summaryId, projectId); + private ErpTimesheetPreAlloc resolveAlloc(Long allocId, String monthCode, Long projectId, Long deptId) { + ErpTimesheetPreAlloc keyAlloc = queryActiveAlloc(monthCode, projectId, deptId); if (allocId == null) { return keyAlloc; } @@ -682,7 +375,7 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer if (keyAlloc != null && !Objects.equals(keyAlloc.getAllocId(), allocId)) { throw new ServiceException("当前部门该月份该预投项目已存在分配单"); } - if (!Objects.equals(idAlloc.getSummaryId(), summaryId) || !Objects.equals(idAlloc.getProjectId(), projectId)) { + if (!Objects.equals(idAlloc.getMonthCode(), monthCode) || !Objects.equals(idAlloc.getProjectId(), projectId)) { throw new ServiceException("不允许修改分配单的月份或来源预投项目"); } if (idAlloc.getCreateDept() != null && !Objects.equals(idAlloc.getCreateDept(), deptId)) { @@ -691,88 +384,106 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer return idAlloc; } + private AllocationResult buildAllocationResult(List inputList, BigDecimal sourceTotal) { + if (inputList == null || inputList.isEmpty()) { + return new AllocationResult(BigDecimal.ZERO, Collections.emptyList()); + } + + Map targetMap = new LinkedHashMap<>(); + BigDecimal allocatedTotal = BigDecimal.ZERO; + for (PreAllocTargetBo targetInput : inputList) { + if (targetInput == null) { + throw new ServiceException("分配明细不能为空"); + } + Long targetProjectId = targetInput.getTargetProjectId(); + if (targetProjectId == null) { + throw new ServiceException("目标项目不能为空"); + } + if (targetMap.containsKey(targetProjectId)) { + throw new ServiceException("同一张分配单不能重复选择同一目标项目"); + } + BigDecimal allocHours = nvl(targetInput.getAllocHours()); + if (allocHours.compareTo(BigDecimal.ZERO) <= 0) { + throw new ServiceException("分配工时必须大于0"); + } + ErpProjectInfo targetProject = getProject(targetProjectId, "目标项目不存在"); + validateTargetProject(targetProject); + + targetMap.put(targetProjectId, new TargetAllocation(targetProject, allocHours)); + allocatedTotal = allocatedTotal.add(allocHours); + } + + if (allocatedTotal.compareTo(sourceTotal) > 0) { + throw new ServiceException("分配工时合计不能超过来源预投工时"); + } + return new AllocationResult(allocatedTotal, new ArrayList<>(targetMap.values())); + } + private void fillAlloc(ErpTimesheetPreAlloc alloc, ErpTimesheetPreAllocBo bo, - ErpTimesheetSummary summary, ErpProjectInfo sourceProject, - AllocationResult allocationResult) { - alloc.setMonthCode(summary.getMonthCode()); - alloc.setStandardMonthId(summary.getStandardMonthId()); + PreAllocSourceStatVo sourceStat, + AllocationResult allocationResult, + Long deptId) { + BigDecimal sourceTotal = nvl(sourceStat.getSourceTotalHours()); + alloc.setMonthCode(bo.getMonthCode()); + alloc.setStandardMonthId(null); alloc.setProjectId(sourceProject.getProjectId()); alloc.setProjectCode(sourceProject.getProjectCode()); alloc.setProjectName(sourceProject.getProjectName()); - alloc.setSourceTotalHours(allocationResult.getSourceTotal()); - alloc.setAllocatedTotalHours(allocationResult.getAllocatedTotal()); - alloc.setStaffCount(allocationResult.getStaffCount()); - alloc.setAllocStatus(resolveAllocStatus(allocationResult.getSourceTotal(), allocationResult.getAllocatedTotal())); - alloc.setAppliedFlag(APPLIED); - alloc.setSummaryId(summary.getSummaryId()); - alloc.setApplyTime(new Date()); + alloc.setSourceTotalHours(sourceTotal); + alloc.setAllocatedTotalHours(allocationResult.allocatedTotal()); + alloc.setStaffCount(sourceStat.getStaffCount()); + alloc.setAllocStatus(resolveAllocStatus(sourceTotal, allocationResult.allocatedTotal())); alloc.setRemark(bo.getRemark()); - alloc.setCreateDept(summary.getDeptId()); - } - - private void rewriteSummaryDetails(Long summaryId, Long sourceProjectId, List details) { - summaryDetailMapper.delete(Wrappers.lambdaQuery() - .eq(ErpTimesheetSummaryDetail::getSummaryId, summaryId) - .eq(ErpTimesheetSummaryDetail::getOriginalProjectId, sourceProjectId) - .eq(ErpTimesheetSummaryDetail::getIsProject, PROJECT_WORK)); - if (!details.isEmpty()) { - summaryDetailMapper.insertBatch(details); - } + alloc.setCreateDept(deptId); } private void rewritePreAllocDetails(ErpTimesheetPreAlloc alloc, ErpProjectInfo sourceProject, - List aggregations) { + List targetAllocations) { preAllocDetailMapper.delete(Wrappers.lambdaQuery() .eq(ErpTimesheetPreAllocDetail::getAllocId, alloc.getAllocId())); - if (aggregations.isEmpty()) { + if (targetAllocations.isEmpty()) { return; } List details = new ArrayList<>(); int sortOrder = 1; - for (TargetAggregation aggregation : aggregations) { + for (TargetAllocation allocation : targetAllocations) { ErpTimesheetPreAllocDetail detail = new ErpTimesheetPreAllocDetail(); detail.setAllocId(alloc.getAllocId()); detail.setSortOrder(sortOrder++); detail.setOriginalProjectId(sourceProject.getProjectId()); detail.setOriginalProjectCode(sourceProject.getProjectCode()); detail.setOriginalProjectName(sourceProject.getProjectName()); - detail.setTargetProjectId(aggregation.getTargetProject().getProjectId()); - detail.setTargetProjectCode(aggregation.getTargetProject().getProjectCode()); - detail.setTargetProjectName(aggregation.getTargetProject().getProjectName()); - detail.setAllocHours(aggregation.getAllocHours()); + detail.setTargetProjectId(allocation.targetProject().getProjectId()); + detail.setTargetProjectCode(allocation.targetProject().getProjectCode()); + detail.setTargetProjectName(allocation.targetProject().getProjectName()); + detail.setAllocHours(allocation.allocHours()); details.add(detail); } preAllocDetailMapper.insertBatch(details); } - private void recalculateSummary(Long summaryId) { - List details = summaryDetailMapper.selectList(Wrappers.lambdaQuery() - .eq(ErpTimesheetSummaryDetail::getSummaryId, summaryId) - .eq(ErpTimesheetSummaryDetail::getDelFlag, DEL_FLAG_NORMAL)); - BigDecimal totalProjectHours = details.stream() - .filter(item -> PROJECT_WORK.equals(item.getIsProject())) - .map(item -> nvl(item.getAdjustedHours())) - .reduce(BigDecimal.ZERO, BigDecimal::add); - BigDecimal totalDeptHours = details.stream() - .filter(item -> !PROJECT_WORK.equals(item.getIsProject())) - .map(item -> nvl(item.getAdjustedHours())) - .reduce(BigDecimal.ZERO, BigDecimal::add); - long staffCount = details.stream() - .map(ErpTimesheetSummaryDetail::getStaffUserId) - .filter(Objects::nonNull) - .distinct() - .count(); - - ErpTimesheetSummary update = new ErpTimesheetSummary(); - update.setSummaryId(summaryId); - update.setTotalProjectHours(totalProjectHours); - update.setTotalDeptHours(totalDeptHours); - update.setTotalHours(totalProjectHours.add(totalDeptHours)); - update.setStaffCount((int) staffCount); - summaryMapper.updateById(update); + private List buildTargetVos(Long allocId) { + if (allocId == null) { + return Collections.emptyList(); + } + List detailList = preAllocDetailMapper.selectList(Wrappers.lambdaQuery() + .eq(ErpTimesheetPreAllocDetail::getAllocId, allocId) + .eq(ErpTimesheetPreAllocDetail::getDelFlag, DEL_FLAG_NORMAL) + .orderByAsc(ErpTimesheetPreAllocDetail::getSortOrder) + .orderByAsc(ErpTimesheetPreAllocDetail::getAllocDetailId)); + List targetVos = new ArrayList<>(); + for (ErpTimesheetPreAllocDetail detail : detailList) { + PreAllocTargetVo targetVo = new PreAllocTargetVo(); + targetVo.setTargetProjectId(detail.getTargetProjectId()); + targetVo.setTargetProjectCode(detail.getTargetProjectCode()); + targetVo.setTargetProjectName(detail.getTargetProjectName()); + targetVo.setAllocHours(nvl(detail.getAllocHours())); + targetVos.add(targetVo); + } + return targetVos; } private String resolveAllocStatus(BigDecimal sourceTotal, BigDecimal allocatedTotal) { @@ -795,40 +506,12 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer return value == null ? BigDecimal.ZERO : value; } - private String decimalKey(BigDecimal value) { - return nvl(value).stripTrailingZeros().toPlainString(); + private record MonthRange(Date startDate, Date endDate) { } - @lombok.Data - private static class StaffSource { - private Long staffUserId; - private String staffName; - private BigDecimal sourceHours = BigDecimal.ZERO; - private List existingRows = new ArrayList<>(); + private record TargetAllocation(ErpProjectInfo targetProject, BigDecimal allocHours) { } - @lombok.Data - private static class TargetAllocation { - private final ErpProjectInfo targetProject; - private final BigDecimal allocHours; - } - - @lombok.Data - private static class TargetAggregation { - private ErpProjectInfo targetProject; - private BigDecimal allocHours = BigDecimal.ZERO; - - private void addAllocHours(BigDecimal hours) { - this.allocHours = this.allocHours.add(hours); - } - } - - @lombok.Data - private static class AllocationResult { - private BigDecimal sourceTotal = BigDecimal.ZERO; - private BigDecimal allocatedTotal = BigDecimal.ZERO; - private Integer staffCount = 0; - private List summaryDetails = new ArrayList<>(); - private List targetAggregations = new ArrayList<>(); + private record AllocationResult(BigDecimal allocatedTotal, List targetAllocations) { } } diff --git a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpTimesheetPreAllocMapper.xml b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpTimesheetPreAllocMapper.xml index fbd8883b..63557af2 100644 --- a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpTimesheetPreAllocMapper.xml +++ b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpTimesheetPreAllocMapper.xml @@ -11,4 +11,45 @@ ${ew.getCustomSqlSegment} + + + +