|
|
|
|
@ -1,24 +1,52 @@
|
|
|
|
|
package org.dromara.oa.erp.service.impl;
|
|
|
|
|
|
|
|
|
|
import org.dromara.common.core.utils.MapstructUtils;
|
|
|
|
|
import org.dromara.common.core.utils.StringUtils;
|
|
|
|
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
|
|
|
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
|
|
|
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
|
|
|
|
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.springframework.stereotype.Service;
|
|
|
|
|
import org.dromara.oa.erp.domain.bo.ErpTimesheetPreAllocBo;
|
|
|
|
|
import org.dromara.oa.erp.domain.vo.ErpTimesheetPreAllocVo;
|
|
|
|
|
import org.apache.dubbo.config.annotation.DubboReference;
|
|
|
|
|
import org.dromara.common.core.exception.ServiceException;
|
|
|
|
|
import org.dromara.common.core.utils.StringUtils;
|
|
|
|
|
import org.dromara.common.mybatis.core.page.PageQuery;
|
|
|
|
|
import org.dromara.common.mybatis.core.page.TableDataInfo;
|
|
|
|
|
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.ErpTimesheetPreAllocVo;
|
|
|
|
|
import org.dromara.oa.erp.domain.vo.PreAllocDetailVo;
|
|
|
|
|
import org.dromara.oa.erp.domain.vo.PreAllocStaffAllocVo;
|
|
|
|
|
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.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.Collection;
|
|
|
|
|
import java.util.Objects;
|
|
|
|
|
import java.util.Set;
|
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 预投工时分配Service业务层处理
|
|
|
|
|
@ -30,7 +58,25 @@ import java.util.Collection;
|
|
|
|
|
@Service
|
|
|
|
|
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<String> 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
|
|
|
|
|
private RemoteCodeRuleService remoteCodeRuleService;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 查询预投工时分配
|
|
|
|
|
@ -39,23 +85,46 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
|
|
|
|
|
* @return 预投工时分配
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public ErpTimesheetPreAllocVo queryById(Long allocId){
|
|
|
|
|
public ErpTimesheetPreAllocVo queryById(Long allocId) {
|
|
|
|
|
return baseMapper.selectVoById(allocId);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 分页查询预投工时分配列表
|
|
|
|
|
*
|
|
|
|
|
* @param bo 查询条件
|
|
|
|
|
* @param pageQuery 分页参数
|
|
|
|
|
* @return 预投工时分配分页列表
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public TableDataInfo<ErpTimesheetPreAllocVo> queryPageList(ErpTimesheetPreAllocBo bo, PageQuery pageQuery) {
|
|
|
|
|
MPJLambdaWrapper<ErpTimesheetPreAlloc> lqw = buildQueryWrapper(bo);
|
|
|
|
|
Page<ErpTimesheetPreAllocVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
|
|
|
|
return TableDataInfo.build(result);
|
|
|
|
|
/**
|
|
|
|
|
* 查询预投工时分配员工级详情
|
|
|
|
|
*
|
|
|
|
|
* @param allocId 主键
|
|
|
|
|
* @return 预投工时分配员工级详情
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public PreAllocDetailVo queryDetailById(Long allocId) {
|
|
|
|
|
ErpTimesheetPreAlloc alloc = baseMapper.selectById(allocId);
|
|
|
|
|
if (alloc == null) {
|
|
|
|
|
throw new ServiceException("预投工时分配单不存在");
|
|
|
|
|
}
|
|
|
|
|
Long deptId = LoginHelper.getDeptId();
|
|
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 分页查询预投工时分配列表
|
|
|
|
|
*
|
|
|
|
|
* @param bo 查询条件
|
|
|
|
|
* @param pageQuery 分页参数
|
|
|
|
|
* @return 预投工时分配分页列表
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public TableDataInfo<ErpTimesheetPreAllocVo> queryPageList(ErpTimesheetPreAllocBo bo, PageQuery pageQuery) {
|
|
|
|
|
MPJLambdaWrapper<ErpTimesheetPreAlloc> lqw = buildQueryWrapper(bo);
|
|
|
|
|
Page<ErpTimesheetPreAllocVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
|
|
|
|
|
return TableDataInfo.build(result);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 查询符合条件的预投工时分配列表
|
|
|
|
|
@ -70,25 +139,79 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private MPJLambdaWrapper<ErpTimesheetPreAlloc> buildQueryWrapper(ErpTimesheetPreAllocBo bo) {
|
|
|
|
|
Map<String, Object> params = bo.getParams();
|
|
|
|
|
MPJLambdaWrapper<ErpTimesheetPreAlloc> lqw = JoinWrappers.lambda(ErpTimesheetPreAlloc.class)
|
|
|
|
|
.selectAll(ErpTimesheetPreAlloc.class)
|
|
|
|
|
.eq(ErpTimesheetPreAlloc::getDelFlag, "0")
|
|
|
|
|
.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(bo.getSourceTotalHours() != null, ErpTimesheetPreAlloc::getSourceTotalHours, bo.getSourceTotalHours())
|
|
|
|
|
.eq(bo.getAllocatedTotalHours() != null, ErpTimesheetPreAlloc::getAllocatedTotalHours, bo.getAllocatedTotalHours())
|
|
|
|
|
.eq(bo.getStaffCount() != null, ErpTimesheetPreAlloc::getStaffCount, bo.getStaffCount())
|
|
|
|
|
.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())
|
|
|
|
|
;
|
|
|
|
|
return lqw;
|
|
|
|
|
Long deptId = LoginHelper.getDeptId();
|
|
|
|
|
return JoinWrappers.lambda(ErpTimesheetPreAlloc.class)
|
|
|
|
|
.selectAll(ErpTimesheetPreAlloc.class)
|
|
|
|
|
.eq(ErpTimesheetPreAlloc::getDelFlag, DEL_FLAG_NORMAL)
|
|
|
|
|
.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 员工级可分配明细
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public PreAllocDetailVo getStaffAllocDetails(String monthCode, Long projectId) {
|
|
|
|
|
validateMonthCode(monthCode);
|
|
|
|
|
if (projectId == null) {
|
|
|
|
|
throw new ServiceException("来源预投项目不能为空");
|
|
|
|
|
}
|
|
|
|
|
Long deptId = LoginHelper.getDeptId();
|
|
|
|
|
ErpTimesheetSummary summary = getSingleSummary(monthCode, deptId);
|
|
|
|
|
ErpProjectInfo sourceProject = getProject(projectId, "来源预投项目不存在");
|
|
|
|
|
validateProjectCategory(sourceProject, PRE_PROJECT_CATEGORY, "来源项目必须是预投项目");
|
|
|
|
|
|
|
|
|
|
ErpTimesheetPreAlloc alloc = queryActiveAlloc(summary.getSummaryId(), projectId);
|
|
|
|
|
List<StaffSource> staffSources = loadStaffSources(summary.getSummaryId(), sourceProject);
|
|
|
|
|
List<PreAllocStaffAllocVo> staffVos = buildStaffVos(staffSources);
|
|
|
|
|
|
|
|
|
|
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.setAllocStatus(resolveAllocStatus(sourceTotal, allocatedTotal));
|
|
|
|
|
return detailVo;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -99,13 +222,7 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public Boolean insertByBo(ErpTimesheetPreAllocBo bo) {
|
|
|
|
|
ErpTimesheetPreAlloc add = MapstructUtils.convert(bo, ErpTimesheetPreAlloc.class);
|
|
|
|
|
validEntityBeforeSave(add);
|
|
|
|
|
boolean flag = baseMapper.insert(add) > 0;
|
|
|
|
|
if (flag) {
|
|
|
|
|
bo.setAllocId(add.getAllocId());
|
|
|
|
|
}
|
|
|
|
|
return flag;
|
|
|
|
|
return saveAlloc(bo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -116,16 +233,64 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public Boolean updateByBo(ErpTimesheetPreAllocBo bo) {
|
|
|
|
|
ErpTimesheetPreAlloc update = MapstructUtils.convert(bo, ErpTimesheetPreAlloc.class);
|
|
|
|
|
validEntityBeforeSave(update);
|
|
|
|
|
return baseMapper.updateById(update) > 0;
|
|
|
|
|
return saveAlloc(bo);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 保存前的数据校验
|
|
|
|
|
* 保存预投工时分配并回写月汇总明细
|
|
|
|
|
*
|
|
|
|
|
* @param bo 预投工时分配
|
|
|
|
|
* @return 是否保存成功
|
|
|
|
|
*/
|
|
|
|
|
private void validEntityBeforeSave(ErpTimesheetPreAlloc entity){
|
|
|
|
|
//TODO 做一些数据校验,如唯一约束
|
|
|
|
|
@Override
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
public Boolean saveAlloc(ErpTimesheetPreAllocBo bo) {
|
|
|
|
|
validateMonthCode(bo.getMonthCode());
|
|
|
|
|
if (bo.getProjectId() == null) {
|
|
|
|
|
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("只能维护当前登录部门的预投工时分配");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ErpProjectInfo sourceProject = getProject(bo.getProjectId(), "来源预投项目不存在");
|
|
|
|
|
validateProjectCategory(sourceProject, PRE_PROJECT_CATEGORY, "来源项目必须是预投项目");
|
|
|
|
|
List<StaffSource> staffSources = loadStaffSources(lockedSummary.getSummaryId(), sourceProject);
|
|
|
|
|
BigDecimal sourceTotal = staffSources.stream()
|
|
|
|
|
.map(StaffSource::getSourceHours)
|
|
|
|
|
.reduce(BigDecimal.ZERO, BigDecimal::add);
|
|
|
|
|
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);
|
|
|
|
|
boolean isNew = alloc == null;
|
|
|
|
|
if (isNew) {
|
|
|
|
|
alloc = new ErpTimesheetPreAlloc();
|
|
|
|
|
alloc.setAllocCode(generateAllocCode());
|
|
|
|
|
alloc.setCreateDept(deptId);
|
|
|
|
|
} else if (StringUtils.isBlank(alloc.getAllocCode())) {
|
|
|
|
|
alloc.setAllocCode(generateAllocCode());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fillAlloc(alloc, bo, lockedSummary, sourceProject, allocationResult);
|
|
|
|
|
if (isNew) {
|
|
|
|
|
baseMapper.insert(alloc);
|
|
|
|
|
} else {
|
|
|
|
|
baseMapper.updateById(alloc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rewriteSummaryDetails(lockedSummary.getSummaryId(), sourceProject.getProjectId(), allocationResult.getSummaryDetails());
|
|
|
|
|
rewritePreAllocDetails(alloc, sourceProject, allocationResult.getTargetAggregations());
|
|
|
|
|
recalculateSummary(lockedSummary.getSummaryId());
|
|
|
|
|
bo.setAllocId(alloc.getAllocId());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@ -136,10 +301,477 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
|
|
|
|
|
* @return 是否删除成功
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
|
|
|
|
|
if(isValid){
|
|
|
|
|
//TODO 做一些业务上的校验,判断是否需要校验
|
|
|
|
|
if (ids == null || ids.isEmpty()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
List<ErpTimesheetPreAlloc> allocList = baseMapper.selectList(Wrappers.<ErpTimesheetPreAlloc>lambdaQuery()
|
|
|
|
|
.in(ErpTimesheetPreAlloc::getAllocId, ids)
|
|
|
|
|
.eq(ErpTimesheetPreAlloc::getDelFlag, DEL_FLAG_NORMAL));
|
|
|
|
|
if (allocList.size() != ids.size()) {
|
|
|
|
|
throw new ServiceException("部分预投工时分配单不存在或已删除");
|
|
|
|
|
}
|
|
|
|
|
Long deptId = LoginHelper.getDeptId();
|
|
|
|
|
for (ErpTimesheetPreAlloc alloc : allocList) {
|
|
|
|
|
if (APPLIED.equals(alloc.getAppliedFlag())) {
|
|
|
|
|
throw new ServiceException("已回写月汇总的预投工时分配单不允许删除");
|
|
|
|
|
}
|
|
|
|
|
if (alloc.getCreateDept() != null && !Objects.equals(alloc.getCreateDept(), deptId)) {
|
|
|
|
|
throw new ServiceException("只能删除当前登录部门的预投工时分配单");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
preAllocDetailMapper.delete(Wrappers.<ErpTimesheetPreAllocDetail>lambdaQuery()
|
|
|
|
|
.in(ErpTimesheetPreAllocDetail::getAllocId, ids));
|
|
|
|
|
return baseMapper.deleteByIds(ids) > 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void validateMonthCode(String monthCode) {
|
|
|
|
|
if (StringUtils.isBlank(monthCode) || !MONTH_CODE_PATTERN.matcher(monthCode).matches()) {
|
|
|
|
|
throw new ServiceException("月份编码必须是YYYYMM格式");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ErpTimesheetSummary getSingleSummary(String monthCode, Long deptId) {
|
|
|
|
|
if (deptId == null) {
|
|
|
|
|
throw new ServiceException("当前登录用户未绑定部门,无法进行预投工时分配");
|
|
|
|
|
}
|
|
|
|
|
List<ErpTimesheetSummary> summaries = summaryMapper.selectList(Wrappers.<ErpTimesheetSummary>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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ErpTimesheetSummary lockSummary(Long summaryId) {
|
|
|
|
|
ErpTimesheetSummary summary = summaryMapper.selectOne(Wrappers.<ErpTimesheetSummary>lambdaQuery()
|
|
|
|
|
.eq(ErpTimesheetSummary::getSummaryId, summaryId)
|
|
|
|
|
.eq(ErpTimesheetSummary::getDelFlag, DEL_FLAG_NORMAL)
|
|
|
|
|
.last("FOR UPDATE"));
|
|
|
|
|
if (summary == null) {
|
|
|
|
|
throw new ServiceException("月汇总工时不存在或已删除");
|
|
|
|
|
}
|
|
|
|
|
return summary;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ErpProjectInfo getProject(Long projectId, String message) {
|
|
|
|
|
ErpProjectInfo project = projectInfoMapper.selectById(projectId);
|
|
|
|
|
if (project == null || !DEL_FLAG_NORMAL.equals(project.getDelFlag())) {
|
|
|
|
|
throw new ServiceException(message);
|
|
|
|
|
}
|
|
|
|
|
return project;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void validateProjectCategory(ErpProjectInfo project, String expectedCategory, String message) {
|
|
|
|
|
if (!expectedCategory.equals(project.getProjectCategory())) {
|
|
|
|
|
throw new ServiceException(message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void validateTargetProject(ErpProjectInfo project) {
|
|
|
|
|
if (!TARGET_PROJECT_CATEGORIES.contains(project.getProjectCategory())) {
|
|
|
|
|
throw new ServiceException("目标项目只能选择物流或备件项目");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private ErpTimesheetPreAlloc queryActiveAlloc(Long summaryId, Long projectId) {
|
|
|
|
|
List<ErpTimesheetPreAlloc> allocList = baseMapper.selectList(Wrappers.<ErpTimesheetPreAlloc>lambdaQuery()
|
|
|
|
|
.eq(ErpTimesheetPreAlloc::getSummaryId, summaryId)
|
|
|
|
|
.eq(ErpTimesheetPreAlloc::getProjectId, projectId)
|
|
|
|
|
.eq(ErpTimesheetPreAlloc::getDelFlag, DEL_FLAG_NORMAL));
|
|
|
|
|
if (allocList.size() > 1) {
|
|
|
|
|
throw new ServiceException("当前部门该月份该预投项目存在多张分配单,请先修正分配单数据");
|
|
|
|
|
}
|
|
|
|
|
return allocList.isEmpty() ? null : allocList.get(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<StaffSource> loadStaffSources(Long summaryId, ErpProjectInfo sourceProject) {
|
|
|
|
|
List<ErpTimesheetSummaryDetail> detailList = summaryDetailMapper.selectList(Wrappers.<ErpTimesheetSummaryDetail>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<Long, List<ErpTimesheetSummaryDetail>> staffMap = detailList.stream()
|
|
|
|
|
.filter(detail -> detail.getStaffUserId() != null)
|
|
|
|
|
.collect(Collectors.groupingBy(
|
|
|
|
|
ErpTimesheetSummaryDetail::getStaffUserId,
|
|
|
|
|
LinkedHashMap::new,
|
|
|
|
|
Collectors.toList()));
|
|
|
|
|
|
|
|
|
|
List<StaffSource> result = new ArrayList<>();
|
|
|
|
|
for (Map.Entry<Long, List<ErpTimesheetSummaryDetail>> entry : staffMap.entrySet()) {
|
|
|
|
|
List<ErpTimesheetSummaryDetail> rows = entry.getValue();
|
|
|
|
|
List<BigDecimal> 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<PreAllocStaffAllocVo> buildStaffVos(List<StaffSource> staffSources) {
|
|
|
|
|
List<PreAllocStaffAllocVo> staffVos = new ArrayList<>();
|
|
|
|
|
for (StaffSource source : staffSources) {
|
|
|
|
|
PreAllocStaffAllocVo staffVo = new PreAllocStaffAllocVo();
|
|
|
|
|
staffVo.setStaffUserId(source.getStaffUserId());
|
|
|
|
|
staffVo.setStaffName(source.getStaffName());
|
|
|
|
|
staffVo.setSourceHours(source.getSourceHours());
|
|
|
|
|
|
|
|
|
|
Map<Long, PreAllocTargetVo> 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<PreAllocStaffAllocBo> inputList,
|
|
|
|
|
List<StaffSource> staffSources,
|
|
|
|
|
Long summaryId,
|
|
|
|
|
ErpProjectInfo sourceProject) {
|
|
|
|
|
Map<Long, StaffSource> sourceMap = staffSources.stream()
|
|
|
|
|
.filter(item -> item.getSourceHours().compareTo(BigDecimal.ZERO) > 0)
|
|
|
|
|
.collect(Collectors.toMap(StaffSource::getStaffUserId, item -> item, (a, b) -> a, LinkedHashMap::new));
|
|
|
|
|
Map<Long, List<PreAllocTargetBo>> inputMap = buildInputMap(inputList, sourceMap);
|
|
|
|
|
Map<Long, ErpProjectInfo> projectCache = new HashMap<>();
|
|
|
|
|
List<ErpTimesheetSummaryDetail> newSummaryDetails = new ArrayList<>();
|
|
|
|
|
Map<Long, TargetAggregation> targetAggregations = new LinkedHashMap<>();
|
|
|
|
|
|
|
|
|
|
int sortOrder = 1;
|
|
|
|
|
BigDecimal allocatedTotal = BigDecimal.ZERO;
|
|
|
|
|
for (StaffSource source : sourceMap.values()) {
|
|
|
|
|
List<PreAllocTargetBo> targetInputs = inputMap.getOrDefault(source.getStaffUserId(), Collections.emptyList());
|
|
|
|
|
BigDecimal staffAllocated = BigDecimal.ZERO;
|
|
|
|
|
List<TargetAllocation> targetAllocations = new ArrayList<>();
|
|
|
|
|
Set<Long> 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<Long, List<PreAllocTargetBo>> buildInputMap(List<PreAllocStaffAllocBo> inputList,
|
|
|
|
|
Map<Long, StaffSource> sourceMap) {
|
|
|
|
|
Map<Long, List<PreAllocTargetBo>> inputMap = new HashMap<>();
|
|
|
|
|
if (inputList == null) {
|
|
|
|
|
return inputMap;
|
|
|
|
|
}
|
|
|
|
|
Set<Long> 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);
|
|
|
|
|
if (allocId == null) {
|
|
|
|
|
return keyAlloc;
|
|
|
|
|
}
|
|
|
|
|
ErpTimesheetPreAlloc idAlloc = baseMapper.selectById(allocId);
|
|
|
|
|
if (idAlloc == null || !DEL_FLAG_NORMAL.equals(idAlloc.getDelFlag())) {
|
|
|
|
|
throw new ServiceException("预投工时分配单不存在");
|
|
|
|
|
}
|
|
|
|
|
if (keyAlloc != null && !Objects.equals(keyAlloc.getAllocId(), allocId)) {
|
|
|
|
|
throw new ServiceException("当前部门该月份该预投项目已存在分配单");
|
|
|
|
|
}
|
|
|
|
|
if (!Objects.equals(idAlloc.getSummaryId(), summaryId) || !Objects.equals(idAlloc.getProjectId(), projectId)) {
|
|
|
|
|
throw new ServiceException("不允许修改分配单的月份或来源预投项目");
|
|
|
|
|
}
|
|
|
|
|
if (idAlloc.getCreateDept() != null && !Objects.equals(idAlloc.getCreateDept(), deptId)) {
|
|
|
|
|
throw new ServiceException("只能维护当前登录部门的预投工时分配单");
|
|
|
|
|
}
|
|
|
|
|
return idAlloc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void fillAlloc(ErpTimesheetPreAlloc alloc,
|
|
|
|
|
ErpTimesheetPreAllocBo bo,
|
|
|
|
|
ErpTimesheetSummary summary,
|
|
|
|
|
ErpProjectInfo sourceProject,
|
|
|
|
|
AllocationResult allocationResult) {
|
|
|
|
|
alloc.setMonthCode(summary.getMonthCode());
|
|
|
|
|
alloc.setStandardMonthId(summary.getStandardMonthId());
|
|
|
|
|
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.setRemark(bo.getRemark());
|
|
|
|
|
alloc.setCreateDept(summary.getDeptId());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void rewriteSummaryDetails(Long summaryId, Long sourceProjectId, List<ErpTimesheetSummaryDetail> details) {
|
|
|
|
|
summaryDetailMapper.delete(Wrappers.<ErpTimesheetSummaryDetail>lambdaQuery()
|
|
|
|
|
.eq(ErpTimesheetSummaryDetail::getSummaryId, summaryId)
|
|
|
|
|
.eq(ErpTimesheetSummaryDetail::getOriginalProjectId, sourceProjectId)
|
|
|
|
|
.eq(ErpTimesheetSummaryDetail::getIsProject, PROJECT_WORK));
|
|
|
|
|
if (!details.isEmpty()) {
|
|
|
|
|
summaryDetailMapper.insertBatch(details);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void rewritePreAllocDetails(ErpTimesheetPreAlloc alloc,
|
|
|
|
|
ErpProjectInfo sourceProject,
|
|
|
|
|
List<TargetAggregation> aggregations) {
|
|
|
|
|
preAllocDetailMapper.delete(Wrappers.<ErpTimesheetPreAllocDetail>lambdaQuery()
|
|
|
|
|
.eq(ErpTimesheetPreAllocDetail::getAllocId, alloc.getAllocId()));
|
|
|
|
|
if (aggregations.isEmpty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
List<ErpTimesheetPreAllocDetail> details = new ArrayList<>();
|
|
|
|
|
int sortOrder = 1;
|
|
|
|
|
for (TargetAggregation aggregation : aggregations) {
|
|
|
|
|
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());
|
|
|
|
|
details.add(detail);
|
|
|
|
|
}
|
|
|
|
|
preAllocDetailMapper.insertBatch(details);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void recalculateSummary(Long summaryId) {
|
|
|
|
|
List<ErpTimesheetSummaryDetail> details = summaryDetailMapper.selectList(Wrappers.<ErpTimesheetSummaryDetail>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 String resolveAllocStatus(BigDecimal sourceTotal, BigDecimal allocatedTotal) {
|
|
|
|
|
BigDecimal safeSource = nvl(sourceTotal);
|
|
|
|
|
BigDecimal safeAllocated = nvl(allocatedTotal);
|
|
|
|
|
if (safeAllocated.compareTo(BigDecimal.ZERO) <= 0) {
|
|
|
|
|
return STATUS_NOT_ALLOCATED;
|
|
|
|
|
}
|
|
|
|
|
if (safeAllocated.compareTo(safeSource) < 0) {
|
|
|
|
|
return STATUS_PART_ALLOCATED;
|
|
|
|
|
}
|
|
|
|
|
return STATUS_ALLOCATED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String generateAllocCode() {
|
|
|
|
|
return remoteCodeRuleService.selectCodeRuleCode("1034");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private BigDecimal nvl(BigDecimal value) {
|
|
|
|
|
return value == null ? BigDecimal.ZERO : value;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String decimalKey(BigDecimal value) {
|
|
|
|
|
return nvl(value).stripTrailingZeros().toPlainString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@lombok.Data
|
|
|
|
|
private static class StaffSource {
|
|
|
|
|
private Long staffUserId;
|
|
|
|
|
private String staffName;
|
|
|
|
|
private BigDecimal sourceHours = BigDecimal.ZERO;
|
|
|
|
|
private List<ErpTimesheetSummaryDetail> existingRows = new ArrayList<>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@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<ErpTimesheetSummaryDetail> summaryDetails = new ArrayList<>();
|
|
|
|
|
private List<TargetAggregation> targetAggregations = new ArrayList<>();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|