项目成本测算新增项目成本预算、核算、结算,季度分配调整可子合约规划分配
This commit is contained in:
@@ -23,7 +23,10 @@ import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -35,7 +38,9 @@ import static cn.iocoder.lyzsys.framework.common.pojo.CommonResult.success;
|
||||
@Validated
|
||||
public class ProjectOutputSplitController {
|
||||
|
||||
private static final int AMOUNT_SCALE = 2;
|
||||
private static final int RATIO_SCALE = 4;
|
||||
private static final BigDecimal ZERO_AMOUNT = BigDecimal.ZERO.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
|
||||
private static final BigDecimal ZERO_RATIO = BigDecimal.ZERO.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
|
||||
|
||||
@Resource
|
||||
@@ -72,7 +77,7 @@ public class ProjectOutputSplitController {
|
||||
|
||||
List<ProjectPlanningQuarterDO> quarterList =
|
||||
projectPlanningQuarterService.getProjectPlanningQuarterListByPlanningId(planningId);
|
||||
respVO.setQuarters(BeanUtils.toBean(quarterList, ProjectPlanningQuarterRespVO.class));
|
||||
respVO.setQuarters(buildPlanningQuarterRespList(planning, quarterList));
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
@@ -94,4 +99,61 @@ public class ProjectOutputSplitController {
|
||||
.setScale(RATIO_SCALE, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
private List<ProjectPlanningQuarterRespVO> buildPlanningQuarterRespList(
|
||||
ProjectPlanningDO planning, List<ProjectPlanningQuarterDO> quarterList) {
|
||||
if (quarterList == null || quarterList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
boolean hasGuideDetailQuarter = quarterList.stream().anyMatch(item -> item.getGuideDetailId() != null);
|
||||
if (!hasGuideDetailQuarter) {
|
||||
return BeanUtils.toBean(quarterList, ProjectPlanningQuarterRespVO.class);
|
||||
}
|
||||
BigDecimal baseAmount = amount(planning.getAssessmentOutputValue())
|
||||
.multiply(ratio(planning.getTotalDistributionAmount()))
|
||||
.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
|
||||
Map<String, ProjectPlanningQuarterRespVO> aggregateMap = new LinkedHashMap<>();
|
||||
for (ProjectPlanningQuarterDO quarter : quarterList) {
|
||||
if (quarter.getGuideDetailId() == null) {
|
||||
continue;
|
||||
}
|
||||
String key = quarter.getDistributionYear() + "_" + quarter.getQuarterNo();
|
||||
ProjectPlanningQuarterRespVO aggregate = aggregateMap.computeIfAbsent(key, ignored -> {
|
||||
ProjectPlanningQuarterRespVO item = new ProjectPlanningQuarterRespVO();
|
||||
item.setPlanningId(planning.getId());
|
||||
item.setDistributionYear(quarter.getDistributionYear());
|
||||
item.setQuarterNo(quarter.getQuarterNo());
|
||||
item.setDistributionAmount(ZERO_AMOUNT);
|
||||
item.setDistributionRatio(ZERO_RATIO);
|
||||
return item;
|
||||
});
|
||||
BigDecimal distributionAmount = amount(aggregate.getDistributionAmount())
|
||||
.add(amount(quarter.getDistributionAmount()))
|
||||
.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
|
||||
aggregate.setDistributionAmount(distributionAmount);
|
||||
aggregate.setDistributionRatio(amountToDistributionRatio(distributionAmount, baseAmount));
|
||||
}
|
||||
List<ProjectPlanningQuarterRespVO> result = new ArrayList<>(aggregateMap.values());
|
||||
result.sort(Comparator
|
||||
.comparing(ProjectPlanningQuarterRespVO::getDistributionYear,
|
||||
Comparator.nullsLast(Integer::compareTo))
|
||||
.thenComparing(ProjectPlanningQuarterRespVO::getQuarterNo,
|
||||
Comparator.nullsLast(Integer::compareTo)));
|
||||
return result;
|
||||
}
|
||||
|
||||
private BigDecimal amountToDistributionRatio(BigDecimal amount, BigDecimal baseAmount) {
|
||||
if (baseAmount == null || baseAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return ZERO_RATIO;
|
||||
}
|
||||
return amount(amount).divide(baseAmount, RATIO_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private BigDecimal amount(BigDecimal value) {
|
||||
return value == null ? ZERO_AMOUNT : value.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private BigDecimal ratio(BigDecimal value) {
|
||||
return value == null ? ZERO_RATIO : value.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class ProjectPlanningRespVO {
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sortNo;
|
||||
private String sortNo;
|
||||
|
||||
@Schema(description = "归属类型")
|
||||
private String ownershipType;
|
||||
|
||||
@@ -21,8 +21,9 @@ public class ProjectPlanningSaveReqVO {
|
||||
@NotNull(message = "项目 ID 不能为空")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "排序", example = "0")
|
||||
private Integer sortNo;
|
||||
@Schema(description = "排序", example = "A-01")
|
||||
@Size(max = 50, message = "排序长度不能超过 50 个字符")
|
||||
private String sortNo;
|
||||
|
||||
@Schema(description = "归属类型:major/comprehensive/special_subcontract_major/special_subcontract_source_coop/special_subcontract_comprehensive", requiredMode = Schema.RequiredMode.REQUIRED, example = "major")
|
||||
@NotBlank(message = "归属类型不能为空")
|
||||
@@ -140,4 +141,8 @@ public class ProjectPlanningSaveReqVO {
|
||||
@DecimalMax(value = "1.0000", message = "calculationRatio must be <= 1")
|
||||
private BigDecimal calculationRatio;
|
||||
|
||||
@Schema(description = "合同单价(元/m²),为空时系统按分项合同产值 / 面积计算", example = "120.00")
|
||||
@DecimalMin(value = "0.0000", message = "contractUnitPrice must be >= 0")
|
||||
private BigDecimal contractUnitPrice;
|
||||
|
||||
}
|
||||
|
||||
@@ -3,12 +3,16 @@ package cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter;
|
||||
import cn.iocoder.lyzsys.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.planning.vo.ProjectPlanningRespVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter.vo.ProjectPlanningQuarterGuideDetailRespVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter.vo.ProjectPlanningQuarterPlanningDetailRespVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter.vo.ProjectPlanningQuarterRespVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter.vo.ProjectPlanningQuarterSaveReqVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planning.ProjectPlanningDO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningguidedetail.ProjectPlanningGuideDetailDO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningquarter.ProjectPlanningQuarterDO;
|
||||
import cn.iocoder.lyzsys.module.tjt.enums.ProjectPlanningBizTypeConstants;
|
||||
import cn.iocoder.lyzsys.module.tjt.service.planning.ProjectPlanningService;
|
||||
import cn.iocoder.lyzsys.module.tjt.service.planningguidedetail.ProjectPlanningGuideDetailService;
|
||||
import cn.iocoder.lyzsys.module.tjt.service.planningquarter.ProjectPlanningQuarterService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -21,9 +25,12 @@ import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.lyzsys.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@@ -40,6 +47,8 @@ public class ProjectPlanningQuarterController {
|
||||
private ProjectPlanningQuarterService projectPlanningQuarterService;
|
||||
@Resource
|
||||
private ProjectPlanningService projectPlanningService;
|
||||
@Resource
|
||||
private ProjectPlanningGuideDetailService projectPlanningGuideDetailService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建季度分配")
|
||||
@@ -111,10 +120,78 @@ public class ProjectPlanningQuarterController {
|
||||
projectPlanningService.getAllocatedAmountMap(Collections.singleton(planningId));
|
||||
respVO.setPlanning(BeanUtils.toBean(planning, ProjectPlanningRespVO.class,
|
||||
planningRespVO -> fillDistributionSummary(planningRespVO, allocatedAmountMap)));
|
||||
respVO.setQuarters(BeanUtils.toBean(quarterList, ProjectPlanningQuarterRespVO.class));
|
||||
List<ProjectPlanningQuarterDO> parentQuarterList = quarterList.stream()
|
||||
.filter(item -> item.getGuideDetailId() == null)
|
||||
.collect(Collectors.toList());
|
||||
List<ProjectPlanningQuarterDO> detailQuarterList = quarterList.stream()
|
||||
.filter(item -> item.getGuideDetailId() != null)
|
||||
.collect(Collectors.toList());
|
||||
boolean guideDetailMode = ProjectPlanningBizTypeConstants.isMajorGuidanceScene(
|
||||
planning.getOwnershipType(), planning.getCalculationMethod());
|
||||
respVO.setGuideDetailMode(guideDetailMode);
|
||||
respVO.setParentQuarters(BeanUtils.toBean(parentQuarterList, ProjectPlanningQuarterRespVO.class));
|
||||
respVO.setQuarters(guideDetailMode
|
||||
? respVO.getParentQuarters()
|
||||
: BeanUtils.toBean(quarterList, ProjectPlanningQuarterRespVO.class));
|
||||
if (guideDetailMode) {
|
||||
List<ProjectPlanningGuideDetailDO> guideDetailList =
|
||||
projectPlanningGuideDetailService.getProjectPlanningGuideDetailListByPlanningId(planningId);
|
||||
respVO.setGuideDetails(buildGuideDetailRespList(planning, guideDetailList, detailQuarterList));
|
||||
boolean historyParentMode = !parentQuarterList.isEmpty() && detailQuarterList.isEmpty();
|
||||
respVO.setHistoryParentMode(historyParentMode);
|
||||
if (historyParentMode) {
|
||||
respVO.setMessage("当前合约规划存在历史父级季度分配。请先清空历史父级分配后,再按指导价法明细维护。");
|
||||
}
|
||||
}
|
||||
return success(respVO);
|
||||
}
|
||||
|
||||
private List<ProjectPlanningQuarterGuideDetailRespVO> buildGuideDetailRespList(
|
||||
ProjectPlanningDO planning,
|
||||
List<ProjectPlanningGuideDetailDO> guideDetailList,
|
||||
List<ProjectPlanningQuarterDO> detailQuarterList) {
|
||||
if (guideDetailList == null || guideDetailList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<Long, List<ProjectPlanningQuarterDO>> quarterMap = detailQuarterList == null
|
||||
? Collections.emptyMap()
|
||||
: detailQuarterList.stream()
|
||||
.filter(item -> item.getGuideDetailId() != null)
|
||||
.collect(Collectors.groupingBy(ProjectPlanningQuarterDO::getGuideDetailId));
|
||||
List<ProjectPlanningQuarterGuideDetailRespVO> result = new ArrayList<>();
|
||||
for (ProjectPlanningGuideDetailDO guideDetail : guideDetailList) {
|
||||
ProjectPlanningQuarterGuideDetailRespVO respVO =
|
||||
BeanUtils.toBean(guideDetail, ProjectPlanningQuarterGuideDetailRespVO.class);
|
||||
List<ProjectPlanningQuarterDO> quarterList =
|
||||
quarterMap.getOrDefault(guideDetail.getId(), Collections.emptyList());
|
||||
respVO.setQuarters(BeanUtils.toBean(quarterList, ProjectPlanningQuarterRespVO.class));
|
||||
fillGuideDetailDistributionSummary(respVO, planning, quarterList);
|
||||
result.add(respVO);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void fillGuideDetailDistributionSummary(ProjectPlanningQuarterGuideDetailRespVO respVO,
|
||||
ProjectPlanningDO planning,
|
||||
List<ProjectPlanningQuarterDO> quarterList) {
|
||||
BigDecimal allocatedRatio = ZERO_RATIO;
|
||||
if (quarterList != null) {
|
||||
for (ProjectPlanningQuarterDO quarter : quarterList) {
|
||||
allocatedRatio = allocatedRatio.add(ratio(quarter.getDistributionRatio()))
|
||||
.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
}
|
||||
BigDecimal totalDistributionAmount = planning.getTotalDistributionAmount() == null
|
||||
? BigDecimal.ONE.setScale(RATIO_SCALE, RoundingMode.HALF_UP)
|
||||
: planning.getTotalDistributionAmount().setScale(RATIO_SCALE, RoundingMode.HALF_UP);
|
||||
BigDecimal allocatedAmount = totalDistributionAmount.multiply(allocatedRatio)
|
||||
.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
|
||||
respVO.setAllocatedAmount(allocatedAmount);
|
||||
respVO.setPendingAmount(totalDistributionAmount.subtract(allocatedAmount)
|
||||
.max(BigDecimal.ZERO)
|
||||
.setScale(RATIO_SCALE, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
private void fillDistributionSummary(ProjectPlanningRespVO respVO, Map<Long, BigDecimal> allocatedAmountMap) {
|
||||
BigDecimal allocatedRatio = allocatedAmountMap.getOrDefault(respVO.getId(), ZERO_RATIO);
|
||||
BigDecimal totalDistributionAmount = respVO.getTotalDistributionAmount() == null
|
||||
@@ -126,4 +203,8 @@ public class ProjectPlanningQuarterController {
|
||||
.setScale(RATIO_SCALE, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
private BigDecimal ratio(BigDecimal value) {
|
||||
return value == null ? ZERO_RATIO : value.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter.vo;
|
||||
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.planningguidedetail.vo.ProjectPlanningGuideDetailRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 指导价法明细季度分配 Response VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ProjectPlanningQuarterGuideDetailRespVO extends ProjectPlanningGuideDetailRespVO {
|
||||
|
||||
@Schema(description = "明细已分配比例")
|
||||
private BigDecimal allocatedAmount;
|
||||
|
||||
@Schema(description = "明细待分配比例")
|
||||
private BigDecimal pendingAmount;
|
||||
|
||||
@Schema(description = "明细季度分配列表")
|
||||
private List<ProjectPlanningQuarterRespVO> quarters = Collections.emptyList();
|
||||
|
||||
}
|
||||
@@ -17,4 +17,19 @@ public class ProjectPlanningQuarterPlanningDetailRespVO {
|
||||
@Schema(description = "季度分配列表")
|
||||
private List<ProjectPlanningQuarterRespVO> quarters = Collections.emptyList();
|
||||
|
||||
@Schema(description = "是否指导价法明细分配模式")
|
||||
private Boolean guideDetailMode = Boolean.FALSE;
|
||||
|
||||
@Schema(description = "是否存在历史父级季度分配")
|
||||
private Boolean historyParentMode = Boolean.FALSE;
|
||||
|
||||
@Schema(description = "提示信息")
|
||||
private String message;
|
||||
|
||||
@Schema(description = "父级季度分配列表")
|
||||
private List<ProjectPlanningQuarterRespVO> parentQuarters = Collections.emptyList();
|
||||
|
||||
@Schema(description = "指导价法明细及各自季度分配列表")
|
||||
private List<ProjectPlanningQuarterGuideDetailRespVO> guideDetails = Collections.emptyList();
|
||||
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@ public class ProjectPlanningQuarterRespVO {
|
||||
@Schema(description = "Planning ID", example = "1")
|
||||
private Long planningId;
|
||||
|
||||
@Schema(description = "Guide detail ID")
|
||||
private Long guideDetailId;
|
||||
|
||||
@Schema(description = "Guide detail sort number snapshot")
|
||||
private Integer guideDetailSortNo;
|
||||
|
||||
@Schema(description = "Distribution year")
|
||||
private Integer distributionYear;
|
||||
|
||||
|
||||
@@ -19,6 +19,12 @@ public class ProjectPlanningQuarterSaveReqVO {
|
||||
@NotNull(message = "planningId cannot be null")
|
||||
private Long planningId;
|
||||
|
||||
@Schema(description = "Guide detail ID, required only for major + guidance price planning", example = "1")
|
||||
private Long guideDetailId;
|
||||
|
||||
@Schema(description = "Guide detail sort number snapshot", example = "1")
|
||||
private Integer guideDetailSortNo;
|
||||
|
||||
@Schema(description = "Distribution year", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026")
|
||||
@NotNull(message = "distributionYear cannot be null")
|
||||
private Integer distributionYear;
|
||||
|
||||
@@ -4,6 +4,7 @@ import cn.iocoder.lyzsys.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitPageReqVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitRespVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitSettlementSaveReqVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.service.profit.ProjectProfitService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
@@ -11,6 +12,9 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
@@ -44,4 +48,27 @@ public class ProjectProfitController {
|
||||
return success(projectProfitService.getProjectProfitPage(pageReqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/lock-budget")
|
||||
@Operation(summary = "锁定项目成本预算测算")
|
||||
@Parameter(name = "projectId", description = "项目 ID", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('tjt:profit:query')")
|
||||
public CommonResult<ProjectProfitRespVO> lockBudgetSnapshot(@RequestParam("projectId") Long projectId) {
|
||||
return success(projectProfitService.lockBudgetSnapshot(projectId));
|
||||
}
|
||||
|
||||
@PostMapping("/lock-accounting")
|
||||
@Operation(summary = "锁定项目成本核算测算")
|
||||
@Parameter(name = "projectId", description = "项目 ID", required = true, example = "1")
|
||||
@PreAuthorize("@ss.hasPermission('tjt:profit:query')")
|
||||
public CommonResult<ProjectProfitRespVO> lockAccountingSnapshot(@RequestParam("projectId") Long projectId) {
|
||||
return success(projectProfitService.lockAccountingSnapshot(projectId));
|
||||
}
|
||||
|
||||
@PutMapping("/save-settlement")
|
||||
@Operation(summary = "保存项目成本结算测算")
|
||||
@PreAuthorize("@ss.hasPermission('tjt:profit:query')")
|
||||
public CommonResult<ProjectProfitRespVO> saveSettlementSnapshot(@Valid @RequestBody ProjectProfitSettlementSaveReqVO saveReqVO) {
|
||||
return success(projectProfitService.saveSettlementSnapshot(saveReqVO));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ public class ProjectProfitRespVO {
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "排序")
|
||||
private Integer sortNo;
|
||||
private String sortNo;
|
||||
|
||||
@Schema(description = "是否签订合同")
|
||||
private Boolean contractSignedFlag;
|
||||
@@ -28,7 +28,7 @@ public class ProjectProfitRespVO {
|
||||
@Schema(description = "结算合同总产值")
|
||||
private BigDecimal finalSettlementAmount;
|
||||
|
||||
@Schema(description = "项目预算产值总计,结算合同总产值大于 0 时取结算合同总产值,否则取合同总产值")
|
||||
@Schema(description = "项目预算产值总计,项目下所有合约规划的项目预算产值合计")
|
||||
private BigDecimal effectiveSettlementAmount;
|
||||
|
||||
@Schema(description = "综合所人工成本")
|
||||
@@ -73,4 +73,13 @@ public class ProjectProfitRespVO {
|
||||
@Schema(description = "创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "项目成本预算测算快照")
|
||||
private ProjectProfitSnapshotRespVO budgetSnapshot;
|
||||
|
||||
@Schema(description = "项目成本核算测算快照")
|
||||
private ProjectProfitSnapshotRespVO accountingSnapshot;
|
||||
|
||||
@Schema(description = "项目成本结算测算快照")
|
||||
private ProjectProfitSnapshotRespVO settlementSnapshot;
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Schema(description = "管理后台 - 项目成本结算测算保存 Request VO")
|
||||
@Data
|
||||
public class ProjectProfitSettlementSaveReqVO {
|
||||
|
||||
@Schema(description = "项目 ID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "项目 ID 不能为空")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "考核结果")
|
||||
@Size(max = 64, message = "考核结果长度不能超过 64 个字符")
|
||||
private String assessmentResult;
|
||||
|
||||
@Schema(description = "备注")
|
||||
@Size(max = 500, message = "备注长度不能超过 500 个字符")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 项目成本测算快照 Response VO")
|
||||
@Data
|
||||
public class ProjectProfitSnapshotRespVO {
|
||||
|
||||
@Schema(description = "快照 ID")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "项目 ID")
|
||||
private Long projectId;
|
||||
|
||||
@Schema(description = "快照类型:budget/accounting/settlement")
|
||||
private String snapshotType;
|
||||
|
||||
@Schema(description = "是否锁定")
|
||||
private Boolean lockedFlag;
|
||||
|
||||
@Schema(description = "操作人 ID")
|
||||
private Long actionUserId;
|
||||
|
||||
@Schema(description = "操作人姓名")
|
||||
private String actionUserName;
|
||||
|
||||
@Schema(description = "操作时间")
|
||||
private LocalDateTime actionTime;
|
||||
|
||||
@Schema(description = "合同总产值")
|
||||
private BigDecimal contractAmount;
|
||||
|
||||
@Schema(description = "结算合同总产值")
|
||||
private BigDecimal finalSettlementAmount;
|
||||
|
||||
@Schema(description = "项目预算产值总计,项目下所有合约规划的项目预算产值合计")
|
||||
private BigDecimal effectiveSettlementAmount;
|
||||
|
||||
@Schema(description = "综合所人工成本")
|
||||
private BigDecimal comprehensivePlanningAmount;
|
||||
|
||||
@Schema(description = "专项分包人工成本合计")
|
||||
private BigDecimal subcontractPlanningAmount;
|
||||
|
||||
@Schema(description = "专项分包-专业所人工成本")
|
||||
private BigDecimal specialSubcontractPlanningAmount;
|
||||
|
||||
@Schema(description = "专项分包-源头合作分包人工成本")
|
||||
private BigDecimal sourceCoopSubcontractPlanningAmount;
|
||||
|
||||
@Schema(description = "专项分包-综合所人工成本")
|
||||
private BigDecimal comprehensiveSubcontractPlanningAmount;
|
||||
|
||||
@Schema(description = "专业所考核产值")
|
||||
private BigDecimal majorOutputValue;
|
||||
|
||||
@Schema(description = "专业所人工成本")
|
||||
private BigDecimal majorExpectedPerformance;
|
||||
|
||||
@Schema(description = "科创产值比例")
|
||||
private BigDecimal innovationOutputRate;
|
||||
|
||||
@Schema(description = "科创产值")
|
||||
private BigDecimal innovationOutputValue;
|
||||
|
||||
@Schema(description = "其他成本")
|
||||
private BigDecimal otherCost;
|
||||
|
||||
@Schema(description = "预算盈亏值")
|
||||
private BigDecimal profitLossValue;
|
||||
|
||||
@Schema(description = "预算盈亏百分比")
|
||||
private BigDecimal profitLossRate;
|
||||
|
||||
@Schema(description = "考核结果")
|
||||
private String assessmentResult;
|
||||
|
||||
@Schema(description = "考核系数")
|
||||
private BigDecimal assessmentCoefficient;
|
||||
|
||||
@Schema(description = "综合所考核产值核算值")
|
||||
private BigDecimal comprehensiveAccountingOutputValue;
|
||||
|
||||
@Schema(description = "综合所考核产值结算值")
|
||||
private BigDecimal comprehensiveSettlementOutputValue;
|
||||
|
||||
@Schema(description = "专业所考核产值核算值")
|
||||
private BigDecimal majorAccountingOutputValue;
|
||||
|
||||
@Schema(description = "专业所考核产值结算值")
|
||||
private BigDecimal majorSettlementOutputValue;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class ProjectRespVO {
|
||||
|
||||
@Schema(description = "排序")
|
||||
@ExcelProperty("排序")
|
||||
private Integer sortNo;
|
||||
private String sortNo;
|
||||
|
||||
@Schema(description = "是否签订合同", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
@ExcelProperty("是否签订合同")
|
||||
|
||||
@@ -28,8 +28,9 @@ public class ProjectSaveReqVO {
|
||||
@Size(max = 200, message = "项目名称长度不能超过 200 个字符")
|
||||
private String projectName;
|
||||
|
||||
@Schema(description = "排序", example = "0")
|
||||
private Integer sortNo;
|
||||
@Schema(description = "排序", example = "A-01")
|
||||
@Size(max = 50, message = "排序长度不能超过 50 个字符")
|
||||
private String sortNo;
|
||||
|
||||
@Schema(description = "是否签订合同", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否签订合同不能为空")
|
||||
|
||||
@@ -25,7 +25,7 @@ public class ProjectPlanningDO extends TenantBaseDO {
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private Integer sortNo;
|
||||
private String sortNo;
|
||||
|
||||
private String ownershipType;
|
||||
|
||||
|
||||
@@ -25,6 +25,10 @@ public class ProjectPlanningQuarterDO extends TenantBaseDO {
|
||||
|
||||
private Long planningId;
|
||||
|
||||
private Long guideDetailId;
|
||||
|
||||
private Integer guideDetailSortNo;
|
||||
|
||||
private Integer distributionYear;
|
||||
|
||||
private Integer quarterNo;
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package cn.iocoder.lyzsys.module.tjt.dal.dataobject.profit;
|
||||
|
||||
import cn.iocoder.lyzsys.framework.tenant.core.db.TenantBaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 项目成本测算快照 DO
|
||||
*
|
||||
* @author Codex
|
||||
*/
|
||||
@TableName("tjt_project_cost_measure_snapshot")
|
||||
@KeySequence("tjt_project_cost_measure_snapshot_seq")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ProjectCostMeasureSnapshotDO extends TenantBaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String snapshotType;
|
||||
|
||||
private Boolean lockedFlag;
|
||||
|
||||
private Long actionUserId;
|
||||
|
||||
private String actionUserName;
|
||||
|
||||
private LocalDateTime actionTime;
|
||||
|
||||
private BigDecimal contractAmount;
|
||||
|
||||
private BigDecimal finalSettlementAmount;
|
||||
|
||||
private BigDecimal effectiveSettlementAmount;
|
||||
|
||||
private BigDecimal comprehensivePlanningAmount;
|
||||
|
||||
private BigDecimal subcontractPlanningAmount;
|
||||
|
||||
private BigDecimal specialSubcontractPlanningAmount;
|
||||
|
||||
private BigDecimal sourceCoopSubcontractPlanningAmount;
|
||||
|
||||
private BigDecimal comprehensiveSubcontractPlanningAmount;
|
||||
|
||||
private BigDecimal majorOutputValue;
|
||||
|
||||
private BigDecimal majorExpectedPerformance;
|
||||
|
||||
private BigDecimal innovationOutputRate;
|
||||
|
||||
private BigDecimal innovationOutputValue;
|
||||
|
||||
private BigDecimal otherCost;
|
||||
|
||||
private BigDecimal profitLossValue;
|
||||
|
||||
private BigDecimal profitLossRate;
|
||||
|
||||
private String assessmentResult;
|
||||
|
||||
private BigDecimal assessmentCoefficient;
|
||||
|
||||
private BigDecimal comprehensiveAccountingOutputValue;
|
||||
|
||||
private BigDecimal comprehensiveSettlementOutputValue;
|
||||
|
||||
private BigDecimal majorAccountingOutputValue;
|
||||
|
||||
private BigDecimal majorSettlementOutputValue;
|
||||
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public class ProjectDO extends TenantBaseDO {
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
private Integer sortNo;
|
||||
private String sortNo;
|
||||
/**
|
||||
* 是否签订合同
|
||||
*/
|
||||
|
||||
@@ -25,8 +25,7 @@ public interface ProjectPlanningMapper extends BaseMapperX<ProjectPlanningDO> {
|
||||
.likeIfPresent(ProjectPlanningDO::getPlanningContent, reqVO.getPlanningContent())
|
||||
.eqIfPresent(ProjectPlanningDO::getPlanningStartYear, reqVO.getPlanningStartYear())
|
||||
.betweenIfPresent(ProjectPlanningDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByAsc(ProjectPlanningDO::getSortNo)
|
||||
.orderByAsc(ProjectPlanningDO::getId));
|
||||
.last("ORDER BY CASE WHEN sort_no IS NULL OR sort_no = N'' THEN 1 ELSE 0 END, sort_no ASC, id ASC"));
|
||||
}
|
||||
|
||||
default List<ProjectPlanningDO> selectListByProjectId(Long projectId) {
|
||||
@@ -38,8 +37,7 @@ public interface ProjectPlanningMapper extends BaseMapperX<ProjectPlanningDO> {
|
||||
default List<ProjectPlanningDO> selectDisplayListByProjectId(Long projectId) {
|
||||
return selectList(new LambdaQueryWrapperX<ProjectPlanningDO>()
|
||||
.eq(ProjectPlanningDO::getProjectId, projectId)
|
||||
.orderByAsc(ProjectPlanningDO::getSortNo)
|
||||
.orderByAsc(ProjectPlanningDO::getId));
|
||||
.last("ORDER BY CASE WHEN sort_no IS NULL OR sort_no = N'' THEN 1 ELSE 0 END, sort_no ASC, id ASC"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,25 +20,102 @@ public interface ProjectPlanningQuarterMapper extends BaseMapperX<ProjectPlannin
|
||||
default ProjectPlanningQuarterDO selectByPlanningIdAndDistributionYearAndQuarter(Long planningId,
|
||||
Integer distributionYear,
|
||||
Integer quarterNo) {
|
||||
return selectOne(ProjectPlanningQuarterDO::getPlanningId, planningId,
|
||||
ProjectPlanningQuarterDO::getDistributionYear, distributionYear,
|
||||
ProjectPlanningQuarterDO::getQuarterNo, quarterNo);
|
||||
return selectParentByPlanningIdAndDistributionYearAndQuarter(planningId, distributionYear, quarterNo);
|
||||
}
|
||||
|
||||
default ProjectPlanningQuarterDO selectParentByPlanningIdAndDistributionYearAndQuarter(Long planningId,
|
||||
Integer distributionYear,
|
||||
Integer quarterNo) {
|
||||
return selectOne(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
|
||||
.eq(ProjectPlanningQuarterDO::getPlanningId, planningId)
|
||||
.eq(ProjectPlanningQuarterDO::getDistributionYear, distributionYear)
|
||||
.eq(ProjectPlanningQuarterDO::getQuarterNo, quarterNo)
|
||||
.isNull(ProjectPlanningQuarterDO::getGuideDetailId));
|
||||
}
|
||||
|
||||
default ProjectPlanningQuarterDO selectByPlanningIdAndGuideDetailIdAndDistributionYearAndQuarter(
|
||||
Long planningId, Long guideDetailId, Integer distributionYear, Integer quarterNo) {
|
||||
return selectOne(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
|
||||
.eq(ProjectPlanningQuarterDO::getPlanningId, planningId)
|
||||
.eq(ProjectPlanningQuarterDO::getGuideDetailId, guideDetailId)
|
||||
.eq(ProjectPlanningQuarterDO::getDistributionYear, distributionYear)
|
||||
.eq(ProjectPlanningQuarterDO::getQuarterNo, quarterNo));
|
||||
}
|
||||
|
||||
default List<ProjectPlanningQuarterDO> selectListByPlanningId(Long planningId) {
|
||||
return selectList(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
|
||||
.eq(ProjectPlanningQuarterDO::getPlanningId, planningId)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getGuideDetailSortNo)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getGuideDetailId)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getDistributionYear)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getQuarterNo)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getId));
|
||||
}
|
||||
|
||||
default List<ProjectPlanningQuarterDO> selectParentListByPlanningId(Long planningId) {
|
||||
return selectList(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
|
||||
.eq(ProjectPlanningQuarterDO::getPlanningId, planningId)
|
||||
.isNull(ProjectPlanningQuarterDO::getGuideDetailId)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getDistributionYear)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getQuarterNo)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getId));
|
||||
}
|
||||
|
||||
default List<ProjectPlanningQuarterDO> selectDetailListByPlanningId(Long planningId) {
|
||||
return selectList(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
|
||||
.eq(ProjectPlanningQuarterDO::getPlanningId, planningId)
|
||||
.isNotNull(ProjectPlanningQuarterDO::getGuideDetailId)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getGuideDetailSortNo)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getGuideDetailId)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getDistributionYear)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getQuarterNo)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getId));
|
||||
}
|
||||
|
||||
default List<ProjectPlanningQuarterDO> selectListByGuideDetailId(Long guideDetailId) {
|
||||
return selectList(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
|
||||
.eq(ProjectPlanningQuarterDO::getGuideDetailId, guideDetailId)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getDistributionYear)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getQuarterNo)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getId));
|
||||
}
|
||||
|
||||
default List<ProjectPlanningQuarterDO> selectListByGuideDetailIds(Collection<Long> guideDetailIds) {
|
||||
if (guideDetailIds == null || guideDetailIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return selectList(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
|
||||
.in(ProjectPlanningQuarterDO::getGuideDetailId, guideDetailIds)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getGuideDetailSortNo)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getGuideDetailId)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getDistributionYear)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getQuarterNo)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getId));
|
||||
}
|
||||
|
||||
default boolean existsParentQuarter(Long planningId, Long excludeId) {
|
||||
return selectCount(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
|
||||
.eq(ProjectPlanningQuarterDO::getPlanningId, planningId)
|
||||
.isNull(ProjectPlanningQuarterDO::getGuideDetailId)
|
||||
.ne(excludeId != null, ProjectPlanningQuarterDO::getId, excludeId)) > 0;
|
||||
}
|
||||
|
||||
default boolean existsGuideDetailQuarter(Long planningId, Long excludeId) {
|
||||
return selectCount(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
|
||||
.eq(ProjectPlanningQuarterDO::getPlanningId, planningId)
|
||||
.isNotNull(ProjectPlanningQuarterDO::getGuideDetailId)
|
||||
.ne(excludeId != null, ProjectPlanningQuarterDO::getId, excludeId)) > 0;
|
||||
}
|
||||
|
||||
default List<ProjectPlanningQuarterDO> selectListByPlanningIds(Collection<Long> planningIds) {
|
||||
if (planningIds == null || planningIds.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return selectList(new LambdaQueryWrapperX<ProjectPlanningQuarterDO>()
|
||||
.in(ProjectPlanningQuarterDO::getPlanningId, planningIds)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getPlanningId)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getGuideDetailSortNo)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getGuideDetailId)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getDistributionYear)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getQuarterNo)
|
||||
.orderByAsc(ProjectPlanningQuarterDO::getId));
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package cn.iocoder.lyzsys.module.tjt.dal.mysql.profit;
|
||||
|
||||
import cn.iocoder.lyzsys.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import cn.iocoder.lyzsys.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.profit.ProjectCostMeasureSnapshotDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface ProjectCostMeasureSnapshotMapper extends BaseMapperX<ProjectCostMeasureSnapshotDO> {
|
||||
|
||||
default ProjectCostMeasureSnapshotDO selectByProjectIdAndType(Long projectId, String snapshotType) {
|
||||
return selectOne(new LambdaQueryWrapperX<ProjectCostMeasureSnapshotDO>()
|
||||
.eq(ProjectCostMeasureSnapshotDO::getProjectId, projectId)
|
||||
.eq(ProjectCostMeasureSnapshotDO::getSnapshotType, snapshotType));
|
||||
}
|
||||
|
||||
default List<ProjectCostMeasureSnapshotDO> selectListByProjectId(Long projectId) {
|
||||
return selectList(new LambdaQueryWrapperX<ProjectCostMeasureSnapshotDO>()
|
||||
.eq(ProjectCostMeasureSnapshotDO::getProjectId, projectId));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -22,8 +22,7 @@ public interface ProjectMapper extends BaseMapperX<ProjectDO> {
|
||||
.eqIfPresent(ProjectDO::getProjectStartYear, reqVO.getProjectStartYear())
|
||||
.eqIfPresent(ProjectDO::getProjectStatus, reqVO.getProjectStatus())
|
||||
.betweenIfPresent(ProjectDO::getCreateTime, reqVO.getCreateTime())
|
||||
.orderByAsc(ProjectDO::getSortNo)
|
||||
.orderByAsc(ProjectDO::getId));
|
||||
.last("ORDER BY CASE WHEN sort_no IS NULL OR sort_no = N'' THEN 1 ELSE 0 END, sort_no ASC, id ASC"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,6 +32,10 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PROJECT_PLANNING_QUARTER_NOT_EXISTS = new ErrorCode(1_020_003_000, "季度分配明细不存在");
|
||||
ErrorCode PROJECT_PLANNING_QUARTER_DUPLICATE = new ErrorCode(1_020_003_001, "同一合约规划下该年度季度分配已存在");
|
||||
ErrorCode PROJECT_PLANNING_QUARTER_PLANNING_NOT_EXISTS = new ErrorCode(1_020_003_002, "关联合约规划不存在");
|
||||
ErrorCode PROJECT_PLANNING_QUARTER_GUIDE_DETAIL_REQUIRED = new ErrorCode(1_020_003_003, "专业所指导价法季度分配必须选择指导价法明细");
|
||||
ErrorCode PROJECT_PLANNING_QUARTER_GUIDE_DETAIL_INVALID = new ErrorCode(1_020_003_004, "指导价法明细不属于当前合约规划");
|
||||
ErrorCode PROJECT_PLANNING_QUARTER_GUIDE_DETAIL_NOT_ALLOWED = new ErrorCode(1_020_003_005, "非专业所指导价法季度分配不能选择指导价法明细");
|
||||
ErrorCode PROJECT_PLANNING_QUARTER_SCOPE_MIXED = new ErrorCode(1_020_003_006, "当前合约规划同时存在父级和明细季度分配,请先清理后再保存");
|
||||
|
||||
// ========== 页面 4 拆分管理 1-020-004-000 ==========
|
||||
ErrorCode PROJECT_OUTPUT_SPLIT_PLANNING_NOT_EXISTS = new ErrorCode(1_020_004_000, "关联合约规划不存在");
|
||||
@@ -67,4 +71,10 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode EMPLOYEE_YEAR_LEADER_OUTPUT_NOT_EXISTS = new ErrorCode(1_020_010_000, "年度所长考核产值记录不存在");
|
||||
ErrorCode EMPLOYEE_YEAR_LEADER_OUTPUT_DUPLICATE = new ErrorCode(1_020_010_001, "当前所长该年度考核产值已存在");
|
||||
|
||||
// ========== 项目成本测算快照 1-020-011-000 ==========
|
||||
ErrorCode PROJECT_COST_MEASURE_BUDGET_LOCKED = new ErrorCode(1_020_011_000, "目标责任书已下达,不能重复操作");
|
||||
ErrorCode PROJECT_COST_MEASURE_ACCOUNTING_LOCKED = new ErrorCode(1_020_011_001, "竣工验收已完成,不能重复操作");
|
||||
ErrorCode PROJECT_COST_MEASURE_ACCOUNTING_REQUIRED = new ErrorCode(1_020_011_002, "请先完成竣工验收,生成项目成本核算测算后再维护结算测算");
|
||||
ErrorCode PROJECT_COST_MEASURE_BUDGET_REQUIRED = new ErrorCode(1_020_011_003, "请先下达目标责任书,生成项目成本预算测算后再完成竣工验收");
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package cn.iocoder.lyzsys.module.tjt.enums;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 项目成本测算快照类型
|
||||
*
|
||||
* @author Codex
|
||||
*/
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public enum ProjectCostMeasureSnapshotTypeEnum {
|
||||
|
||||
BUDGET("budget", "项目成本预算测算"),
|
||||
ACCOUNTING("accounting", "项目成本核算测算"),
|
||||
SETTLEMENT("settlement", "项目成本结算测算");
|
||||
|
||||
private final String code;
|
||||
private final String label;
|
||||
|
||||
}
|
||||
@@ -157,18 +157,54 @@ public class ProjectPlanningServiceImpl implements ProjectPlanningService {
|
||||
if (planningIds == null || planningIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<ProjectPlanningQuarterDO> quarterList = projectPlanningQuarterMapper.selectListByPlanningIds(planningIds);
|
||||
List<Long> validPlanningIds = planningIds.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.collect(java.util.stream.Collectors.toList());
|
||||
if (validPlanningIds.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<Long, ProjectPlanningDO> planningMap = projectPlanningMapper.selectBatchIds(validPlanningIds).stream()
|
||||
.collect(java.util.stream.Collectors.toMap(ProjectPlanningDO::getId, item -> item, (a, b) -> a));
|
||||
List<ProjectPlanningQuarterDO> quarterList = projectPlanningQuarterMapper.selectListByPlanningIds(validPlanningIds);
|
||||
Map<Long, List<ProjectPlanningQuarterDO>> quarterMap = quarterList.stream()
|
||||
.collect(java.util.stream.Collectors.groupingBy(ProjectPlanningQuarterDO::getPlanningId));
|
||||
Map<Long, BigDecimal> allocatedAmountMap = new HashMap<>();
|
||||
for (Long planningId : planningIds) {
|
||||
allocatedAmountMap.put(planningId, ZERO_RATIO);
|
||||
for (Long planningId : validPlanningIds) {
|
||||
allocatedAmountMap.put(planningId, calculateAllocatedRatio(
|
||||
planningMap.get(planningId),
|
||||
quarterMap.getOrDefault(planningId, Collections.emptyList())));
|
||||
}
|
||||
for (ProjectPlanningQuarterDO quarter : quarterList) {
|
||||
allocatedAmountMap.merge(quarter.getPlanningId(), ratio(quarter.getDistributionRatio()), BigDecimal::add);
|
||||
}
|
||||
allocatedAmountMap.replaceAll((key, value) -> ratio(value));
|
||||
return allocatedAmountMap;
|
||||
}
|
||||
|
||||
private BigDecimal calculateAllocatedRatio(ProjectPlanningDO planning, List<ProjectPlanningQuarterDO> quarterList) {
|
||||
if (quarterList == null || quarterList.isEmpty()) {
|
||||
return ZERO_RATIO;
|
||||
}
|
||||
boolean hasGuideDetailQuarter = quarterList.stream().anyMatch(item -> item.getGuideDetailId() != null);
|
||||
if (!hasGuideDetailQuarter) {
|
||||
BigDecimal allocatedRatio = ZERO_RATIO;
|
||||
for (ProjectPlanningQuarterDO quarter : quarterList) {
|
||||
allocatedRatio = allocatedRatio.add(ratio(quarter.getDistributionRatio()))
|
||||
.setScale(RATIO_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
return allocatedRatio;
|
||||
}
|
||||
if (planning == null) {
|
||||
return ZERO_RATIO;
|
||||
}
|
||||
BigDecimal baseAmount = multiplyAmount(planning.getAssessmentOutputValue(), planning.getTotalDistributionAmount());
|
||||
if (baseAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return ZERO_RATIO;
|
||||
}
|
||||
BigDecimal allocatedAmount = ZERO_AMOUNT;
|
||||
for (ProjectPlanningQuarterDO quarter : quarterList) {
|
||||
allocatedAmount = allocatedAmount.add(amount(quarter.getDistributionAmount()))
|
||||
.setScale(AMOUNT_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
return allocatedAmount.divide(baseAmount, RATIO_SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private void validateProjectPlanningForSave(ProjectPlanningSaveReqVO reqVO, ProjectPlanningDO dbPlanning) {
|
||||
if (!ProjectPlanningBizTypeConstants.isValidOwnershipType(reqVO.getOwnershipType())) {
|
||||
throw exception(PROJECT_PLANNING_OWNERSHIP_TYPE_INVALID);
|
||||
@@ -203,9 +239,6 @@ public class ProjectPlanningServiceImpl implements ProjectPlanningService {
|
||||
}
|
||||
|
||||
private void prepareProjectPlanning(ProjectPlanningDO planning, ProjectDO project) {
|
||||
if (planning.getSortNo() == null) {
|
||||
planning.setSortNo(0);
|
||||
}
|
||||
if (planning.getPlanningStartYear() == null) {
|
||||
planning.setPlanningStartYear(project.getProjectStartYear());
|
||||
}
|
||||
@@ -250,6 +283,7 @@ public class ProjectPlanningServiceImpl implements ProjectPlanningService {
|
||||
planning.setWorkingDayUnitPrice(amountNullable(planning.getWorkingDayUnitPrice()));
|
||||
planning.setGuidanceUnitPrice(amountNullable(planning.getGuidanceUnitPrice()));
|
||||
planning.setGuidanceTotalPrice(amountNullable(planning.getGuidanceTotalPrice()));
|
||||
planning.setContractUnitPrice(amountNullable(planning.getContractUnitPrice()));
|
||||
}
|
||||
|
||||
private void calculateProjectPlanning(ProjectPlanningDO planning) {
|
||||
@@ -258,7 +292,9 @@ public class ProjectPlanningServiceImpl implements ProjectPlanningService {
|
||||
planning.setProjectBudgetOutputValue(amount(planning.getPlanningAmount()
|
||||
.subtract(planning.getManagementFee())
|
||||
.subtract(planning.getVatAmount())));
|
||||
planning.setContractUnitPrice(divideAmount(planning.getPlanningAmount(), planning.getPlanningArea()));
|
||||
if (planning.getContractUnitPrice() == null) {
|
||||
planning.setContractUnitPrice(divideAmount(planning.getPlanningAmount(), planning.getPlanningArea()));
|
||||
}
|
||||
planning.setTotalAdjustmentFactor(ZERO_RATIO);
|
||||
planning.setAssessmentArea(ZERO_AMOUNT);
|
||||
planning.setVirtualOutputValue(ZERO_AMOUNT);
|
||||
|
||||
@@ -84,6 +84,7 @@ public class ProjectPlanningGuideDetailServiceImpl implements ProjectPlanningGui
|
||||
.filter(id -> !retainedIds.contains(id))
|
||||
.collect(Collectors.toList());
|
||||
if (!deletedIds.isEmpty()) {
|
||||
projectPlanningQuarterMapper.deleteBatch(ProjectPlanningQuarterDO::getGuideDetailId, deletedIds);
|
||||
projectPlanningGuideDetailMapper.deleteBatchIds(deletedIds);
|
||||
}
|
||||
|
||||
@@ -96,6 +97,7 @@ public class ProjectPlanningGuideDetailServiceImpl implements ProjectPlanningGui
|
||||
if (detail == null) {
|
||||
throw exception(PROJECT_PLANNING_GUIDE_DETAIL_NOT_EXISTS);
|
||||
}
|
||||
projectPlanningQuarterMapper.delete(ProjectPlanningQuarterDO::getGuideDetailId, id);
|
||||
projectPlanningGuideDetailMapper.deleteById(id);
|
||||
recalculatePlanningSummary(detail.getPlanningId());
|
||||
}
|
||||
@@ -122,6 +124,7 @@ public class ProjectPlanningGuideDetailServiceImpl implements ProjectPlanningGui
|
||||
if (planningId == null) {
|
||||
return;
|
||||
}
|
||||
projectPlanningQuarterMapper.delete(ProjectPlanningQuarterDO::getPlanningId, planningId);
|
||||
projectPlanningGuideDetailMapper.delete(ProjectPlanningGuideDetailDO::getPlanningId, planningId);
|
||||
}
|
||||
|
||||
@@ -130,6 +133,7 @@ public class ProjectPlanningGuideDetailServiceImpl implements ProjectPlanningGui
|
||||
if (planningIds == null || planningIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
projectPlanningQuarterMapper.deleteBatch(ProjectPlanningQuarterDO::getPlanningId, planningIds);
|
||||
projectPlanningGuideDetailMapper.deleteBatch(ProjectPlanningGuideDetailDO::getPlanningId, planningIds);
|
||||
}
|
||||
|
||||
@@ -175,7 +179,6 @@ public class ProjectPlanningGuideDetailServiceImpl implements ProjectPlanningGui
|
||||
ProjectPlanningDO updateObj = new ProjectPlanningDO();
|
||||
updateObj.setId(planningId);
|
||||
updateObj.setPlanningArea(totalDesignArea);
|
||||
updateObj.setContractUnitPrice(divideAmount(planning.getPlanningAmount(), totalDesignArea));
|
||||
updateObj.setTotalAdjustmentFactor(ZERO_RATIO);
|
||||
updateObj.setAssessmentArea(totalAssessmentArea);
|
||||
updateObj.setVirtualOutputValue(ZERO_AMOUNT);
|
||||
@@ -241,9 +244,22 @@ public class ProjectPlanningGuideDetailServiceImpl implements ProjectPlanningGui
|
||||
return;
|
||||
}
|
||||
List<ProjectPlanningQuarterDO> quarterList = projectPlanningQuarterMapper.selectListByPlanningId(planning.getId());
|
||||
Map<Long, ProjectPlanningGuideDetailDO> guideDetailMap = Collections.emptyMap();
|
||||
if (isGuideDetailScene(planning)) {
|
||||
guideDetailMap = CollectionUtils.convertMap(
|
||||
projectPlanningGuideDetailMapper.selectListByPlanningId(planning.getId()),
|
||||
ProjectPlanningGuideDetailDO::getId);
|
||||
}
|
||||
for (ProjectPlanningQuarterDO quarter : quarterList) {
|
||||
ProjectPlanningGuideDetailDO guideDetail = quarter.getGuideDetailId() == null
|
||||
? null : guideDetailMap.get(quarter.getGuideDetailId());
|
||||
BigDecimal assessmentOutputValue = guideDetail == null
|
||||
? planning.getAssessmentOutputValue() : guideDetail.getAssessmentOutputValue();
|
||||
if (guideDetail != null) {
|
||||
quarter.setGuideDetailSortNo(guideDetail.getSortNo());
|
||||
}
|
||||
quarter.setDistributionAmount(multiplyAmount(
|
||||
planning.getAssessmentOutputValue(),
|
||||
assessmentOutputValue,
|
||||
planning.getTotalDistributionAmount(),
|
||||
ratio(quarter.getDistributionRatio())
|
||||
));
|
||||
|
||||
@@ -3,9 +3,12 @@ package cn.iocoder.lyzsys.module.tjt.service.planningquarter;
|
||||
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.planningquarter.vo.ProjectPlanningQuarterSaveReqVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planning.ProjectPlanningDO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningguidedetail.ProjectPlanningGuideDetailDO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningquarter.ProjectPlanningQuarterDO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.mysql.planning.ProjectPlanningMapper;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.mysql.planningguidedetail.ProjectPlanningGuideDetailMapper;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.mysql.planningquarter.ProjectPlanningQuarterMapper;
|
||||
import cn.iocoder.lyzsys.module.tjt.enums.ProjectPlanningBizTypeConstants;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
@@ -13,11 +16,16 @@ import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import static cn.iocoder.lyzsys.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_QUARTER_DUPLICATE;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_QUARTER_GUIDE_DETAIL_INVALID;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_QUARTER_GUIDE_DETAIL_NOT_ALLOWED;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_QUARTER_GUIDE_DETAIL_REQUIRED;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_QUARTER_NOT_EXISTS;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_QUARTER_PLANNING_NOT_EXISTS;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_PLANNING_QUARTER_SCOPE_MIXED;
|
||||
|
||||
/**
|
||||
* 季度分配 Service 实现类
|
||||
@@ -35,6 +43,8 @@ public class ProjectPlanningQuarterServiceImpl implements ProjectPlanningQuarter
|
||||
private ProjectPlanningQuarterMapper projectPlanningQuarterMapper;
|
||||
@Resource
|
||||
private ProjectPlanningMapper projectPlanningMapper;
|
||||
@Resource
|
||||
private ProjectPlanningGuideDetailMapper projectPlanningGuideDetailMapper;
|
||||
|
||||
@Override
|
||||
public Long createProjectPlanningQuarter(ProjectPlanningQuarterSaveReqVO createReqVO) {
|
||||
@@ -42,10 +52,12 @@ public class ProjectPlanningQuarterServiceImpl implements ProjectPlanningQuarter
|
||||
if (isZeroQuarter(createReqVO)) {
|
||||
return null;
|
||||
}
|
||||
validateQuarterUnique(null, createReqVO.getPlanningId(),
|
||||
ProjectPlanningGuideDetailDO guideDetail = validateGuideDetailScope(planning, createReqVO.getGuideDetailId());
|
||||
validateScopeNotMixed(null, createReqVO.getPlanningId(), guideDetail);
|
||||
validateQuarterUnique(null, createReqVO.getPlanningId(), createReqVO.getGuideDetailId(),
|
||||
createReqVO.getDistributionYear(), createReqVO.getQuarterNo());
|
||||
ProjectPlanningQuarterDO quarter = BeanUtils.toBean(createReqVO, ProjectPlanningQuarterDO.class);
|
||||
prepareQuarter(quarter, planning);
|
||||
prepareQuarter(quarter, planning, guideDetail);
|
||||
projectPlanningQuarterMapper.insert(quarter);
|
||||
return quarter.getId();
|
||||
}
|
||||
@@ -58,10 +70,12 @@ public class ProjectPlanningQuarterServiceImpl implements ProjectPlanningQuarter
|
||||
projectPlanningQuarterMapper.deleteById(dbQuarter.getId());
|
||||
return;
|
||||
}
|
||||
validateQuarterUnique(updateReqVO.getId(), updateReqVO.getPlanningId(),
|
||||
ProjectPlanningGuideDetailDO guideDetail = validateGuideDetailScope(planning, updateReqVO.getGuideDetailId());
|
||||
validateScopeNotMixed(updateReqVO.getId(), updateReqVO.getPlanningId(), guideDetail);
|
||||
validateQuarterUnique(updateReqVO.getId(), updateReqVO.getPlanningId(), updateReqVO.getGuideDetailId(),
|
||||
updateReqVO.getDistributionYear(), updateReqVO.getQuarterNo());
|
||||
ProjectPlanningQuarterDO updateObj = BeanUtils.toBean(updateReqVO, ProjectPlanningQuarterDO.class);
|
||||
prepareQuarter(updateObj, planning);
|
||||
prepareQuarter(updateObj, planning, guideDetail);
|
||||
projectPlanningQuarterMapper.updateById(updateObj);
|
||||
}
|
||||
|
||||
@@ -88,9 +102,13 @@ public class ProjectPlanningQuarterServiceImpl implements ProjectPlanningQuarter
|
||||
return projectPlanningQuarterMapper.selectListByPlanningId(planningId);
|
||||
}
|
||||
|
||||
private void validateQuarterUnique(Long id, Long planningId, Integer distributionYear, Integer quarterNo) {
|
||||
ProjectPlanningQuarterDO quarter = projectPlanningQuarterMapper.selectByPlanningIdAndDistributionYearAndQuarter(
|
||||
planningId, distributionYear, quarterNo);
|
||||
private void validateQuarterUnique(Long id, Long planningId, Long guideDetailId,
|
||||
Integer distributionYear, Integer quarterNo) {
|
||||
ProjectPlanningQuarterDO quarter = guideDetailId == null
|
||||
? projectPlanningQuarterMapper.selectParentByPlanningIdAndDistributionYearAndQuarter(
|
||||
planningId, distributionYear, quarterNo)
|
||||
: projectPlanningQuarterMapper.selectByPlanningIdAndGuideDetailIdAndDistributionYearAndQuarter(
|
||||
planningId, guideDetailId, distributionYear, quarterNo);
|
||||
if (quarter == null) {
|
||||
return;
|
||||
}
|
||||
@@ -115,14 +133,53 @@ public class ProjectPlanningQuarterServiceImpl implements ProjectPlanningQuarter
|
||||
return quarter;
|
||||
}
|
||||
|
||||
private ProjectPlanningGuideDetailDO validateGuideDetailScope(ProjectPlanningDO planning, Long guideDetailId) {
|
||||
boolean guideDetailMode = ProjectPlanningBizTypeConstants.isMajorGuidanceScene(
|
||||
planning.getOwnershipType(), planning.getCalculationMethod());
|
||||
if (!guideDetailMode) {
|
||||
if (guideDetailId != null) {
|
||||
throw exception(PROJECT_PLANNING_QUARTER_GUIDE_DETAIL_NOT_ALLOWED);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (guideDetailId == null) {
|
||||
throw exception(PROJECT_PLANNING_QUARTER_GUIDE_DETAIL_REQUIRED);
|
||||
}
|
||||
ProjectPlanningGuideDetailDO guideDetail = projectPlanningGuideDetailMapper.selectById(guideDetailId);
|
||||
if (guideDetail == null || !Objects.equals(guideDetail.getPlanningId(), planning.getId())) {
|
||||
throw exception(PROJECT_PLANNING_QUARTER_GUIDE_DETAIL_INVALID);
|
||||
}
|
||||
return guideDetail;
|
||||
}
|
||||
|
||||
private void validateScopeNotMixed(Long id, Long planningId, ProjectPlanningGuideDetailDO guideDetail) {
|
||||
boolean savingParentQuarter = guideDetail == null;
|
||||
boolean mixed = savingParentQuarter
|
||||
? projectPlanningQuarterMapper.existsGuideDetailQuarter(planningId, id)
|
||||
: projectPlanningQuarterMapper.existsParentQuarter(planningId, id);
|
||||
if (mixed) {
|
||||
throw exception(PROJECT_PLANNING_QUARTER_SCOPE_MIXED);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isZeroQuarter(ProjectPlanningQuarterSaveReqVO reqVO) {
|
||||
return ratio(reqVO.getDistributionRatio()).compareTo(BigDecimal.ZERO) == 0;
|
||||
}
|
||||
|
||||
private void prepareQuarter(ProjectPlanningQuarterDO quarter, ProjectPlanningDO planning) {
|
||||
private void prepareQuarter(ProjectPlanningQuarterDO quarter, ProjectPlanningDO planning,
|
||||
ProjectPlanningGuideDetailDO guideDetail) {
|
||||
quarter.setDistributionRatio(ratio(quarter.getDistributionRatio()));
|
||||
if (guideDetail == null) {
|
||||
quarter.setGuideDetailId(null);
|
||||
quarter.setGuideDetailSortNo(null);
|
||||
} else {
|
||||
quarter.setGuideDetailId(guideDetail.getId());
|
||||
quarter.setGuideDetailSortNo(guideDetail.getSortNo());
|
||||
}
|
||||
BigDecimal assessmentOutputValue = guideDetail == null
|
||||
? planning.getAssessmentOutputValue() : guideDetail.getAssessmentOutputValue();
|
||||
quarter.setDistributionAmount(multiplyAmount(
|
||||
planning.getAssessmentOutputValue(),
|
||||
assessmentOutputValue,
|
||||
planning.getTotalDistributionAmount(),
|
||||
quarter.getDistributionRatio()));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package cn.iocoder.lyzsys.module.tjt.service.profit;
|
||||
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitPageReqVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitRespVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitSettlementSaveReqVO;
|
||||
|
||||
/**
|
||||
* 项目盈亏 Service 接口
|
||||
@@ -15,4 +16,10 @@ public interface ProjectProfitService {
|
||||
|
||||
PageResult<ProjectProfitRespVO> getProjectProfitPage(ProjectProfitPageReqVO pageReqVO);
|
||||
|
||||
ProjectProfitRespVO lockBudgetSnapshot(Long projectId);
|
||||
|
||||
ProjectProfitRespVO lockAccountingSnapshot(Long projectId);
|
||||
|
||||
ProjectProfitRespVO saveSettlementSnapshot(ProjectProfitSettlementSaveReqVO saveReqVO);
|
||||
|
||||
}
|
||||
|
||||
@@ -2,17 +2,20 @@ package cn.iocoder.lyzsys.module.tjt.service.profit;
|
||||
|
||||
import cn.iocoder.lyzsys.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.lyzsys.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.lyzsys.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.lyzsys.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import cn.iocoder.lyzsys.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitPageReqVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitRespVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitSettlementSaveReqVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.controller.admin.profit.vo.ProjectProfitSnapshotRespVO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planning.ProjectPlanningDO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.planningquarter.ProjectPlanningQuarterDO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.profit.ProjectCostMeasureSnapshotDO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.project.ProjectDO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.dataobject.yearkvalue.YearKValueDO;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.mysql.planning.ProjectPlanningMapper;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.mysql.planningquarter.ProjectPlanningQuarterMapper;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.mysql.profit.ProjectCostMeasureSnapshotMapper;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.mysql.project.ProjectMapper;
|
||||
import cn.iocoder.lyzsys.module.tjt.dal.mysql.yearkvalue.YearKValueMapper;
|
||||
import cn.iocoder.lyzsys.module.tjt.enums.ProjectCostMeasureSnapshotTypeEnum;
|
||||
import cn.iocoder.lyzsys.module.tjt.enums.ProjectPlanningBizTypeConstants;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
@@ -20,7 +23,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
import javax.annotation.Resource;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Collection;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -28,6 +31,10 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static cn.iocoder.lyzsys.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_COST_MEASURE_ACCOUNTING_LOCKED;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_COST_MEASURE_ACCOUNTING_REQUIRED;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_COST_MEASURE_BUDGET_LOCKED;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_COST_MEASURE_BUDGET_REQUIRED;
|
||||
import static cn.iocoder.lyzsys.module.tjt.enums.ErrorCodeConstants.PROJECT_NOT_EXISTS;
|
||||
|
||||
@Service
|
||||
@@ -36,25 +43,23 @@ public class ProjectProfitServiceImpl implements ProjectProfitService {
|
||||
|
||||
private static final BigDecimal ZERO_AMOUNT = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP);
|
||||
private static final BigDecimal ZERO_RATIO = BigDecimal.ZERO.setScale(4, RoundingMode.HALF_UP);
|
||||
private static final BigDecimal ONE_RATIO = BigDecimal.ONE.setScale(4, RoundingMode.HALF_UP);
|
||||
private static final BigDecimal DEFAULT_INNOVATION_OUTPUT_RATE = new BigDecimal("0.0100");
|
||||
private static final BigDecimal DEFAULT_YEAR_K_VALUE = new BigDecimal("0.4000");
|
||||
private static final BigDecimal ASSESSMENT_COEFFICIENT_EXCELLENT = new BigDecimal("1.0200");
|
||||
private static final BigDecimal ASSESSMENT_COEFFICIENT_QUALIFIED = new BigDecimal("1.0000");
|
||||
private static final BigDecimal ASSESSMENT_COEFFICIENT_NEEDS_IMPROVEMENT = new BigDecimal("0.9500");
|
||||
|
||||
@Resource
|
||||
private ProjectMapper projectMapper;
|
||||
@Resource
|
||||
private ProjectPlanningMapper projectPlanningMapper;
|
||||
@Resource
|
||||
private ProjectPlanningQuarterMapper projectPlanningQuarterMapper;
|
||||
@Resource
|
||||
private YearKValueMapper yearKValueMapper;
|
||||
private ProjectCostMeasureSnapshotMapper projectCostMeasureSnapshotMapper;
|
||||
|
||||
@Override
|
||||
public ProjectProfitRespVO getProjectProfit(Long projectId) {
|
||||
ProjectDO project = validateProjectExists(projectId);
|
||||
List<ProjectPlanningDO> planningList = projectPlanningMapper.selectList(ProjectPlanningDO::getProjectId,
|
||||
Collections.singleton(projectId));
|
||||
return buildProjectProfit(project, planningList);
|
||||
ProjectProfitRespVO respVO = buildCurrentProjectProfit(projectId);
|
||||
fillSnapshots(respVO);
|
||||
return respVO;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,44 +83,129 @@ public class ProjectProfitServiceImpl implements ProjectProfitService {
|
||||
return new PageResult<>(list, pageResult.getTotal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectProfitRespVO lockBudgetSnapshot(Long projectId) {
|
||||
ProjectProfitRespVO currentProfit = buildCurrentProjectProfit(projectId);
|
||||
if (projectCostMeasureSnapshotMapper.selectByProjectIdAndType(projectId,
|
||||
ProjectCostMeasureSnapshotTypeEnum.BUDGET.getCode()) != null) {
|
||||
throw exception(PROJECT_COST_MEASURE_BUDGET_LOCKED);
|
||||
}
|
||||
ProjectCostMeasureSnapshotDO snapshot = buildSnapshot(currentProfit, ProjectCostMeasureSnapshotTypeEnum.BUDGET.getCode());
|
||||
snapshot.setLockedFlag(Boolean.TRUE);
|
||||
fillActionInfo(snapshot);
|
||||
projectCostMeasureSnapshotMapper.insert(snapshot);
|
||||
return getProjectProfit(projectId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectProfitRespVO lockAccountingSnapshot(Long projectId) {
|
||||
ProjectProfitRespVO currentProfit = buildCurrentProjectProfit(projectId);
|
||||
ProjectCostMeasureSnapshotDO budgetSnapshot = projectCostMeasureSnapshotMapper.selectByProjectIdAndType(projectId,
|
||||
ProjectCostMeasureSnapshotTypeEnum.BUDGET.getCode());
|
||||
if (budgetSnapshot == null || !Boolean.TRUE.equals(budgetSnapshot.getLockedFlag())) {
|
||||
throw exception(PROJECT_COST_MEASURE_BUDGET_REQUIRED);
|
||||
}
|
||||
if (projectCostMeasureSnapshotMapper.selectByProjectIdAndType(projectId,
|
||||
ProjectCostMeasureSnapshotTypeEnum.ACCOUNTING.getCode()) != null) {
|
||||
throw exception(PROJECT_COST_MEASURE_ACCOUNTING_LOCKED);
|
||||
}
|
||||
ProjectCostMeasureSnapshotDO snapshot = buildSnapshot(currentProfit, ProjectCostMeasureSnapshotTypeEnum.ACCOUNTING.getCode());
|
||||
snapshot.setLockedFlag(Boolean.TRUE);
|
||||
fillActionInfo(snapshot);
|
||||
projectCostMeasureSnapshotMapper.insert(snapshot);
|
||||
return getProjectProfit(projectId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectProfitRespVO saveSettlementSnapshot(ProjectProfitSettlementSaveReqVO saveReqVO) {
|
||||
validateProjectExists(saveReqVO.getProjectId());
|
||||
ProjectCostMeasureSnapshotDO accountingSnapshot = projectCostMeasureSnapshotMapper.selectByProjectIdAndType(
|
||||
saveReqVO.getProjectId(), ProjectCostMeasureSnapshotTypeEnum.ACCOUNTING.getCode());
|
||||
if (accountingSnapshot == null || !Boolean.TRUE.equals(accountingSnapshot.getLockedFlag())) {
|
||||
throw exception(PROJECT_COST_MEASURE_ACCOUNTING_REQUIRED);
|
||||
}
|
||||
|
||||
ProjectCostMeasureSnapshotDO settlementSnapshot = projectCostMeasureSnapshotMapper.selectByProjectIdAndType(
|
||||
saveReqVO.getProjectId(), ProjectCostMeasureSnapshotTypeEnum.SETTLEMENT.getCode());
|
||||
boolean create = settlementSnapshot == null;
|
||||
if (create) {
|
||||
settlementSnapshot = new ProjectCostMeasureSnapshotDO();
|
||||
}
|
||||
copySnapshotValues(accountingSnapshot, settlementSnapshot);
|
||||
settlementSnapshot.setProjectId(saveReqVO.getProjectId());
|
||||
settlementSnapshot.setSnapshotType(ProjectCostMeasureSnapshotTypeEnum.SETTLEMENT.getCode());
|
||||
settlementSnapshot.setLockedFlag(Boolean.FALSE);
|
||||
settlementSnapshot.setAssessmentResult(saveReqVO.getAssessmentResult());
|
||||
settlementSnapshot.setAssessmentCoefficient(getAssessmentCoefficient(saveReqVO.getAssessmentResult()));
|
||||
settlementSnapshot.setRemark(saveReqVO.getRemark());
|
||||
|
||||
BigDecimal comprehensiveAccountingOutputValue = amount(accountingSnapshot.getComprehensivePlanningAmount());
|
||||
BigDecimal majorAccountingOutputValue = amount(accountingSnapshot.getMajorOutputValue());
|
||||
settlementSnapshot.setComprehensiveAccountingOutputValue(comprehensiveAccountingOutputValue);
|
||||
settlementSnapshot.setMajorAccountingOutputValue(majorAccountingOutputValue);
|
||||
settlementSnapshot.setComprehensiveSettlementOutputValue(
|
||||
multiplyAmount(comprehensiveAccountingOutputValue, settlementSnapshot.getAssessmentCoefficient()));
|
||||
settlementSnapshot.setMajorSettlementOutputValue(
|
||||
multiplyAmount(majorAccountingOutputValue, settlementSnapshot.getAssessmentCoefficient()));
|
||||
fillActionInfo(settlementSnapshot);
|
||||
|
||||
if (create) {
|
||||
projectCostMeasureSnapshotMapper.insert(settlementSnapshot);
|
||||
} else {
|
||||
projectCostMeasureSnapshotMapper.updateById(settlementSnapshot);
|
||||
}
|
||||
return getProjectProfit(saveReqVO.getProjectId());
|
||||
}
|
||||
|
||||
private ProjectProfitRespVO buildCurrentProjectProfit(Long projectId) {
|
||||
ProjectDO project = validateProjectExists(projectId);
|
||||
List<ProjectPlanningDO> planningList = projectPlanningMapper.selectList(ProjectPlanningDO::getProjectId,
|
||||
Collections.singleton(projectId));
|
||||
return buildProjectProfit(project, planningList);
|
||||
}
|
||||
|
||||
private ProjectProfitRespVO buildProjectProfit(ProjectDO project, List<ProjectPlanningDO> planningList) {
|
||||
List<ProjectPlanningDO> safePlanningList = planningList == null ? Collections.emptyList() : planningList;
|
||||
BigDecimal projectBudgetOutputValue = ZERO_AMOUNT;
|
||||
BigDecimal comprehensivePlanningAmount = ZERO_AMOUNT;
|
||||
BigDecimal specialSubcontractPlanningAmount = ZERO_AMOUNT;
|
||||
BigDecimal sourceCoopSubcontractPlanningAmount = ZERO_AMOUNT;
|
||||
BigDecimal comprehensiveSubcontractPlanningAmount = ZERO_AMOUNT;
|
||||
BigDecimal majorExpectedPerformance = ZERO_AMOUNT;
|
||||
BigDecimal majorOutputValue = ZERO_AMOUNT;
|
||||
List<ProjectPlanningDO> majorPlanningList = safePlanningList.stream()
|
||||
.filter(planning -> ProjectPlanningBizTypeConstants.isMajor(planning.getOwnershipType()))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (ProjectPlanningDO planning : safePlanningList) {
|
||||
BigDecimal planningBudgetOutputValue = amount(planning.getProjectBudgetOutputValue());
|
||||
projectBudgetOutputValue = projectBudgetOutputValue.add(planningBudgetOutputValue);
|
||||
if (ProjectPlanningBizTypeConstants.isComprehensive(planning.getOwnershipType())) {
|
||||
comprehensivePlanningAmount = comprehensivePlanningAmount.add(amount(planning.getPlanningAmount()));
|
||||
comprehensivePlanningAmount = comprehensivePlanningAmount.add(planningBudgetOutputValue);
|
||||
continue;
|
||||
}
|
||||
if (ProjectPlanningBizTypeConstants.isSpecialSubcontract(planning.getOwnershipType())) {
|
||||
specialSubcontractPlanningAmount = specialSubcontractPlanningAmount.add(amount(planning.getPlanningAmount()));
|
||||
specialSubcontractPlanningAmount = specialSubcontractPlanningAmount.add(planningBudgetOutputValue);
|
||||
continue;
|
||||
}
|
||||
if (ProjectPlanningBizTypeConstants.isSourceCoopSubcontract(planning.getOwnershipType())) {
|
||||
sourceCoopSubcontractPlanningAmount = sourceCoopSubcontractPlanningAmount.add(amount(planning.getPlanningAmount()));
|
||||
sourceCoopSubcontractPlanningAmount = sourceCoopSubcontractPlanningAmount.add(planningBudgetOutputValue);
|
||||
continue;
|
||||
}
|
||||
if (ProjectPlanningBizTypeConstants.isComprehensiveSubcontract(planning.getOwnershipType())) {
|
||||
comprehensiveSubcontractPlanningAmount = comprehensiveSubcontractPlanningAmount.add(amount(planning.getPlanningAmount()));
|
||||
comprehensiveSubcontractPlanningAmount = comprehensiveSubcontractPlanningAmount.add(planningBudgetOutputValue);
|
||||
continue;
|
||||
}
|
||||
if (ProjectPlanningBizTypeConstants.isMajor(planning.getOwnershipType())) {
|
||||
majorExpectedPerformance = majorExpectedPerformance.add(planningBudgetOutputValue);
|
||||
majorOutputValue = majorOutputValue.add(amount(planning.getAssessmentOutputValue()));
|
||||
}
|
||||
}
|
||||
|
||||
BigDecimal majorExpectedPerformance = calculateMajorExpectedPerformance(majorPlanningList);
|
||||
BigDecimal contractAmount = amount(project.getContractAmount());
|
||||
BigDecimal finalSettlementAmount = amount(project.getFinalSettlementAmount());
|
||||
BigDecimal effectiveSettlementAmount = finalSettlementAmount.compareTo(BigDecimal.ZERO) > 0
|
||||
BigDecimal effectiveContractSettlementAmount = finalSettlementAmount.compareTo(BigDecimal.ZERO) > 0
|
||||
? finalSettlementAmount : contractAmount;
|
||||
BigDecimal taxExcludedSettlementAmount = effectiveContractSettlementAmount.compareTo(BigDecimal.ZERO) == 0
|
||||
? ZERO_AMOUNT
|
||||
: effectiveContractSettlementAmount.divide(new BigDecimal("1.06"), 2, RoundingMode.HALF_UP);
|
||||
BigDecimal innovationOutputRate = project.getInnovationOutputRate() == null
|
||||
? DEFAULT_INNOVATION_OUTPUT_RATE : ratio(project.getInnovationOutputRate());
|
||||
BigDecimal innovationOutputValue = contractAmount.compareTo(BigDecimal.ZERO) <= 0
|
||||
@@ -125,16 +215,16 @@ public class ProjectProfitServiceImpl implements ProjectProfitService {
|
||||
BigDecimal subcontractPlanningAmount = specialSubcontractPlanningAmount
|
||||
.add(sourceCoopSubcontractPlanningAmount)
|
||||
.add(comprehensiveSubcontractPlanningAmount);
|
||||
BigDecimal profitLossValue = effectiveSettlementAmount
|
||||
BigDecimal profitLossValue = taxExcludedSettlementAmount
|
||||
.subtract(comprehensivePlanningAmount)
|
||||
.subtract(subcontractPlanningAmount)
|
||||
.subtract(majorExpectedPerformance)
|
||||
.subtract(innovationOutputValue)
|
||||
.subtract(otherCost)
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
BigDecimal profitLossRate = effectiveSettlementAmount.compareTo(BigDecimal.ZERO) == 0
|
||||
BigDecimal profitLossRate = taxExcludedSettlementAmount.compareTo(BigDecimal.ZERO) == 0
|
||||
? ZERO_RATIO
|
||||
: profitLossValue.divide(effectiveSettlementAmount, 4, RoundingMode.HALF_UP);
|
||||
: profitLossValue.divide(taxExcludedSettlementAmount, 4, RoundingMode.HALF_UP);
|
||||
|
||||
ProjectProfitRespVO respVO = new ProjectProfitRespVO();
|
||||
respVO.setProjectId(project.getId());
|
||||
@@ -143,14 +233,15 @@ public class ProjectProfitServiceImpl implements ProjectProfitService {
|
||||
respVO.setContractSignedFlag(project.getContractSignedFlag());
|
||||
respVO.setContractAmount(contractAmount);
|
||||
respVO.setFinalSettlementAmount(finalSettlementAmount);
|
||||
respVO.setEffectiveSettlementAmount(effectiveSettlementAmount);
|
||||
// 历史字段名沿用 effectiveSettlementAmount,当前业务含义为“项目预算产值总计”。
|
||||
respVO.setEffectiveSettlementAmount(projectBudgetOutputValue.setScale(2, RoundingMode.HALF_UP));
|
||||
respVO.setComprehensivePlanningAmount(comprehensivePlanningAmount.setScale(2, RoundingMode.HALF_UP));
|
||||
respVO.setSubcontractPlanningAmount(subcontractPlanningAmount.setScale(2, RoundingMode.HALF_UP));
|
||||
respVO.setSpecialSubcontractPlanningAmount(specialSubcontractPlanningAmount.setScale(2, RoundingMode.HALF_UP));
|
||||
respVO.setSourceCoopSubcontractPlanningAmount(sourceCoopSubcontractPlanningAmount.setScale(2, RoundingMode.HALF_UP));
|
||||
respVO.setComprehensiveSubcontractPlanningAmount(comprehensiveSubcontractPlanningAmount.setScale(2, RoundingMode.HALF_UP));
|
||||
respVO.setMajorOutputValue(majorOutputValue.setScale(2, RoundingMode.HALF_UP));
|
||||
respVO.setMajorExpectedPerformance(majorExpectedPerformance);
|
||||
respVO.setMajorExpectedPerformance(majorExpectedPerformance.setScale(2, RoundingMode.HALF_UP));
|
||||
respVO.setInnovationOutputRate(innovationOutputRate);
|
||||
respVO.setInnovationOutputValue(innovationOutputValue);
|
||||
respVO.setOtherCost(otherCost);
|
||||
@@ -161,41 +252,66 @@ public class ProjectProfitServiceImpl implements ProjectProfitService {
|
||||
return respVO;
|
||||
}
|
||||
|
||||
private BigDecimal calculateMajorExpectedPerformance(List<ProjectPlanningDO> majorPlanningList) {
|
||||
if (majorPlanningList == null || majorPlanningList.isEmpty()) {
|
||||
return ZERO_AMOUNT;
|
||||
}
|
||||
List<Long> planningIds = majorPlanningList.stream().map(ProjectPlanningDO::getId).collect(Collectors.toList());
|
||||
Map<Long, List<ProjectPlanningQuarterDO>> quarterMap = CollectionUtils.convertMultiMap(
|
||||
projectPlanningQuarterMapper.selectListByPlanningIds(planningIds), ProjectPlanningQuarterDO::getPlanningId);
|
||||
Set<Integer> years = quarterMap.values().stream()
|
||||
.flatMap(Collection::stream)
|
||||
.map(ProjectPlanningQuarterDO::getDistributionYear)
|
||||
.collect(Collectors.toSet());
|
||||
Map<Integer, BigDecimal> yearKValueMap = yearKValueMapper.selectEnabledListByYears(years).stream()
|
||||
.collect(Collectors.toMap(YearKValueDO::getKYear, item -> ratio(item.getKValue()), (a, b) -> b));
|
||||
private void fillSnapshots(ProjectProfitRespVO respVO) {
|
||||
Map<String, ProjectCostMeasureSnapshotDO> snapshotMap = projectCostMeasureSnapshotMapper
|
||||
.selectListByProjectId(respVO.getProjectId())
|
||||
.stream()
|
||||
.collect(Collectors.toMap(ProjectCostMeasureSnapshotDO::getSnapshotType, item -> item, (a, b) -> b));
|
||||
respVO.setBudgetSnapshot(toSnapshotRespVO(snapshotMap.get(ProjectCostMeasureSnapshotTypeEnum.BUDGET.getCode())));
|
||||
respVO.setAccountingSnapshot(toSnapshotRespVO(snapshotMap.get(ProjectCostMeasureSnapshotTypeEnum.ACCOUNTING.getCode())));
|
||||
respVO.setSettlementSnapshot(toSnapshotRespVO(snapshotMap.get(ProjectCostMeasureSnapshotTypeEnum.SETTLEMENT.getCode())));
|
||||
}
|
||||
|
||||
BigDecimal total = ZERO_AMOUNT;
|
||||
for (ProjectPlanningDO planning : majorPlanningList) {
|
||||
BigDecimal assessmentOutputValue = amount(planning.getAssessmentOutputValue());
|
||||
Map<Integer, BigDecimal> yearRatioMap = quarterMap.getOrDefault(planning.getId(), Collections.emptyList()).stream()
|
||||
.collect(Collectors.groupingBy(ProjectPlanningQuarterDO::getDistributionYear,
|
||||
Collectors.reducing(ZERO_RATIO,
|
||||
item -> ratio(item.getDistributionRatio()),
|
||||
BigDecimal::add)));
|
||||
BigDecimal allocatedRatio = ZERO_RATIO;
|
||||
for (Map.Entry<Integer, BigDecimal> entry : yearRatioMap.entrySet()) {
|
||||
BigDecimal yearRatio = ratio(entry.getValue());
|
||||
allocatedRatio = allocatedRatio.add(yearRatio).setScale(4, RoundingMode.HALF_UP);
|
||||
BigDecimal kValue = yearKValueMap.getOrDefault(entry.getKey(), DEFAULT_YEAR_K_VALUE);
|
||||
total = total.add(multiplyAmount(assessmentOutputValue, yearRatio, kValue));
|
||||
}
|
||||
BigDecimal unallocatedRatio = ONE_RATIO.subtract(allocatedRatio).setScale(4, RoundingMode.HALF_UP);
|
||||
if (unallocatedRatio.compareTo(ZERO_RATIO) > 0) {
|
||||
total = total.add(multiplyAmount(assessmentOutputValue, unallocatedRatio, DEFAULT_YEAR_K_VALUE));
|
||||
}
|
||||
}
|
||||
return total.setScale(2, RoundingMode.HALF_UP);
|
||||
private ProjectCostMeasureSnapshotDO buildSnapshot(ProjectProfitRespVO profit, String snapshotType) {
|
||||
ProjectCostMeasureSnapshotDO snapshot = new ProjectCostMeasureSnapshotDO();
|
||||
snapshot.setProjectId(profit.getProjectId());
|
||||
snapshot.setSnapshotType(snapshotType);
|
||||
snapshot.setContractAmount(amount(profit.getContractAmount()));
|
||||
snapshot.setFinalSettlementAmount(amount(profit.getFinalSettlementAmount()));
|
||||
snapshot.setEffectiveSettlementAmount(amount(profit.getEffectiveSettlementAmount()));
|
||||
snapshot.setComprehensivePlanningAmount(amount(profit.getComprehensivePlanningAmount()));
|
||||
snapshot.setSubcontractPlanningAmount(amount(profit.getSubcontractPlanningAmount()));
|
||||
snapshot.setSpecialSubcontractPlanningAmount(amount(profit.getSpecialSubcontractPlanningAmount()));
|
||||
snapshot.setSourceCoopSubcontractPlanningAmount(amount(profit.getSourceCoopSubcontractPlanningAmount()));
|
||||
snapshot.setComprehensiveSubcontractPlanningAmount(amount(profit.getComprehensiveSubcontractPlanningAmount()));
|
||||
snapshot.setMajorOutputValue(amount(profit.getMajorOutputValue()));
|
||||
snapshot.setMajorExpectedPerformance(amount(profit.getMajorExpectedPerformance()));
|
||||
snapshot.setInnovationOutputRate(ratio(profit.getInnovationOutputRate()));
|
||||
snapshot.setInnovationOutputValue(amount(profit.getInnovationOutputValue()));
|
||||
snapshot.setOtherCost(amount(profit.getOtherCost()));
|
||||
snapshot.setProfitLossValue(amount(profit.getProfitLossValue()));
|
||||
snapshot.setProfitLossRate(ratio(profit.getProfitLossRate()));
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
private void copySnapshotValues(ProjectCostMeasureSnapshotDO source, ProjectCostMeasureSnapshotDO target) {
|
||||
target.setContractAmount(amount(source.getContractAmount()));
|
||||
target.setFinalSettlementAmount(amount(source.getFinalSettlementAmount()));
|
||||
target.setEffectiveSettlementAmount(amount(source.getEffectiveSettlementAmount()));
|
||||
target.setComprehensivePlanningAmount(amount(source.getComprehensivePlanningAmount()));
|
||||
target.setSubcontractPlanningAmount(amount(source.getSubcontractPlanningAmount()));
|
||||
target.setSpecialSubcontractPlanningAmount(amount(source.getSpecialSubcontractPlanningAmount()));
|
||||
target.setSourceCoopSubcontractPlanningAmount(amount(source.getSourceCoopSubcontractPlanningAmount()));
|
||||
target.setComprehensiveSubcontractPlanningAmount(amount(source.getComprehensiveSubcontractPlanningAmount()));
|
||||
target.setMajorOutputValue(amount(source.getMajorOutputValue()));
|
||||
target.setMajorExpectedPerformance(amount(source.getMajorExpectedPerformance()));
|
||||
target.setInnovationOutputRate(ratio(source.getInnovationOutputRate()));
|
||||
target.setInnovationOutputValue(amount(source.getInnovationOutputValue()));
|
||||
target.setOtherCost(amount(source.getOtherCost()));
|
||||
target.setProfitLossValue(amount(source.getProfitLossValue()));
|
||||
target.setProfitLossRate(ratio(source.getProfitLossRate()));
|
||||
}
|
||||
|
||||
private void fillActionInfo(ProjectCostMeasureSnapshotDO snapshot) {
|
||||
Long userId = SecurityFrameworkUtils.getLoginUserId();
|
||||
String nickname = SecurityFrameworkUtils.getLoginUserNickname();
|
||||
snapshot.setActionUserId(userId);
|
||||
snapshot.setActionUserName(nickname == null ? "" : nickname);
|
||||
snapshot.setActionTime(LocalDateTime.now());
|
||||
}
|
||||
|
||||
private ProjectProfitSnapshotRespVO toSnapshotRespVO(ProjectCostMeasureSnapshotDO snapshot) {
|
||||
return snapshot == null ? null : BeanUtils.toBean(snapshot, ProjectProfitSnapshotRespVO.class);
|
||||
}
|
||||
|
||||
private ProjectDO validateProjectExists(Long projectId) {
|
||||
@@ -214,6 +330,16 @@ public class ProjectProfitServiceImpl implements ProjectProfitService {
|
||||
return value == null ? ZERO_RATIO : value.setScale(4, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private BigDecimal getAssessmentCoefficient(String assessmentResult) {
|
||||
if ("优秀".equals(assessmentResult)) {
|
||||
return ASSESSMENT_COEFFICIENT_EXCELLENT;
|
||||
}
|
||||
if ("待改进".equals(assessmentResult)) {
|
||||
return ASSESSMENT_COEFFICIENT_NEEDS_IMPROVEMENT;
|
||||
}
|
||||
return ASSESSMENT_COEFFICIENT_QUALIFIED;
|
||||
}
|
||||
|
||||
private BigDecimal multiplyAmount(BigDecimal... values) {
|
||||
BigDecimal result = BigDecimal.ONE;
|
||||
for (BigDecimal value : values) {
|
||||
|
||||
@@ -151,8 +151,8 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
if (project.getArchiveTime() == null && dbProject != null) {
|
||||
project.setArchiveTime(dbProject.getArchiveTime());
|
||||
}
|
||||
if (project.getSortNo() == null) {
|
||||
project.setSortNo(dbProject == null || dbProject.getSortNo() == null ? 0 : dbProject.getSortNo());
|
||||
if (project.getSortNo() == null && dbProject != null) {
|
||||
project.setSortNo(dbProject.getSortNo());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,11 +269,8 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
Map<Long, List<ProjectPlanningQuarterDO>> quarterMap = CollectionUtils.convertMultiMap(
|
||||
projectPlanningQuarterMapper.selectListByPlanningIds(planningIds), ProjectPlanningQuarterDO::getPlanningId);
|
||||
for (ProjectPlanningDO planning : planningList) {
|
||||
java.math.BigDecimal allocatedRatio = quarterMap.getOrDefault(planning.getId(), Collections.emptyList()).stream()
|
||||
.map(ProjectPlanningQuarterDO::getDistributionRatio)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add)
|
||||
.setScale(4, java.math.RoundingMode.HALF_UP);
|
||||
java.math.BigDecimal allocatedRatio = resolveAllocatedRatio(
|
||||
planning, quarterMap.getOrDefault(planning.getId(), Collections.emptyList()));
|
||||
java.math.BigDecimal totalDistributionAmount = planning.getTotalDistributionAmount() == null
|
||||
? java.math.BigDecimal.ONE.setScale(4, java.math.RoundingMode.HALF_UP)
|
||||
: planning.getTotalDistributionAmount().setScale(4, java.math.RoundingMode.HALF_UP);
|
||||
@@ -284,6 +281,39 @@ public class ProjectServiceImpl implements ProjectService {
|
||||
return true;
|
||||
}
|
||||
|
||||
private java.math.BigDecimal resolveAllocatedRatio(ProjectPlanningDO planning, List<ProjectPlanningQuarterDO> quarterList) {
|
||||
if (quarterList == null || quarterList.isEmpty()) {
|
||||
return java.math.BigDecimal.ZERO.setScale(4, java.math.RoundingMode.HALF_UP);
|
||||
}
|
||||
boolean hasGuideDetailQuarter = quarterList.stream().anyMatch(item -> item.getGuideDetailId() != null);
|
||||
if (!hasGuideDetailQuarter) {
|
||||
return quarterList.stream()
|
||||
.map(ProjectPlanningQuarterDO::getDistributionRatio)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(java.math.BigDecimal.ZERO, java.math.BigDecimal::add)
|
||||
.setScale(4, java.math.RoundingMode.HALF_UP);
|
||||
}
|
||||
java.math.BigDecimal totalDistributionAmount = planning.getTotalDistributionAmount() == null
|
||||
? java.math.BigDecimal.ONE.setScale(4, java.math.RoundingMode.HALF_UP)
|
||||
: planning.getTotalDistributionAmount().setScale(4, java.math.RoundingMode.HALF_UP);
|
||||
java.math.BigDecimal baseAmount = amount(planning.getAssessmentOutputValue())
|
||||
.multiply(totalDistributionAmount)
|
||||
.setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
if (baseAmount.compareTo(java.math.BigDecimal.ZERO) == 0) {
|
||||
return java.math.BigDecimal.ZERO.setScale(4, java.math.RoundingMode.HALF_UP);
|
||||
}
|
||||
java.math.BigDecimal allocatedAmount = quarterList.stream()
|
||||
.map(ProjectPlanningQuarterDO::getDistributionAmount)
|
||||
.map(this::amount)
|
||||
.reduce(java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP), java.math.BigDecimal::add);
|
||||
return allocatedAmount.divide(baseAmount, 4, java.math.RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private java.math.BigDecimal amount(java.math.BigDecimal value) {
|
||||
return value == null ? java.math.BigDecimal.ZERO.setScale(2, java.math.RoundingMode.HALF_UP)
|
||||
: value.setScale(2, java.math.RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private void deleteProjectDependencies(Collection<Long> projectIds) {
|
||||
if (projectIds == null || projectIds.isEmpty()) {
|
||||
return;
|
||||
|
||||
@@ -180,11 +180,7 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic
|
||||
ProjectPlanningDO anchorPlanning = validatePlanningExists(reqVO.getPlanningId());
|
||||
ProjectDO project = validateProjectExists(anchorPlanning.getProjectId());
|
||||
Integer reportYear = resolveExportYear(reqVO.getYear());
|
||||
List<ProjectPlanningDO> planningList = projectPlanningMapper.selectListByProjectId(project.getId()).stream()
|
||||
.sorted(Comparator
|
||||
.comparingInt((ProjectPlanningDO item) -> projectLeadOutputTypeOrder(item.getOwnershipType()))
|
||||
.thenComparing(ProjectPlanningDO::getId))
|
||||
.collect(Collectors.toList());
|
||||
List<ProjectPlanningDO> planningList = sortPlanningList(projectPlanningMapper.selectListByProjectId(project.getId()));
|
||||
List<ProjectRolePersonDO> projectRolePersons = projectRolePersonMapper.selectListByProjectId(project.getId());
|
||||
Map<Long, List<ProjectPlanningQuarterDO>> quarterMap = getQuarterMap(planningList);
|
||||
Map<Long, ProjectOutputSplitDO> outputSplitMap = getOutputSplitMap(planningList);
|
||||
@@ -1265,7 +1261,6 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic
|
||||
}
|
||||
List<ProjectBudgetExcelBuilder.BudgetRow> rows = new ArrayList<>();
|
||||
planningList.stream()
|
||||
.sorted(Comparator.comparing(ProjectPlanningDO::getId))
|
||||
.forEach(planning -> {
|
||||
if (!ProjectPlanningBizTypeConstants.isMajorGuidanceScene(
|
||||
planning.getOwnershipType(), planning.getCalculationMethod())) {
|
||||
@@ -1552,11 +1547,37 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic
|
||||
}
|
||||
|
||||
private List<ProjectPlanningDO> sortPlanningList(List<ProjectPlanningDO> planningList) {
|
||||
if (planningList == null || planningList.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
Map<Long, ProjectDO> projectMap = getProjectMap(planningList);
|
||||
return planningList.stream()
|
||||
.sorted(Comparator.comparing(ProjectPlanningDO::getId))
|
||||
.sorted(Comparator
|
||||
.comparingInt((ProjectPlanningDO item) -> sortNoEmptyRank(projectSortNo(item, projectMap)))
|
||||
.thenComparing(item -> sortNoValue(projectSortNo(item, projectMap)))
|
||||
.thenComparing(ProjectPlanningDO::getProjectId, Comparator.nullsLast(Long::compareTo))
|
||||
.thenComparingInt(item -> sortNoEmptyRank(item.getSortNo()))
|
||||
.thenComparing(item -> sortNoValue(item.getSortNo()))
|
||||
.thenComparing(ProjectPlanningDO::getId, Comparator.nullsLast(Long::compareTo)))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String projectSortNo(ProjectPlanningDO planning, Map<Long, ProjectDO> projectMap) {
|
||||
if (planning == null || projectMap == null) {
|
||||
return null;
|
||||
}
|
||||
ProjectDO project = projectMap.get(planning.getProjectId());
|
||||
return project == null ? null : project.getSortNo();
|
||||
}
|
||||
|
||||
private int sortNoEmptyRank(String sortNo) {
|
||||
return sortNo == null || sortNo.trim().isEmpty() ? 1 : 0;
|
||||
}
|
||||
|
||||
private String sortNoValue(String sortNo) {
|
||||
return sortNo == null ? "" : sortNo.trim();
|
||||
}
|
||||
|
||||
private ProjectOutputSplitDO resolveQuarterOutputSplit(ProjectPlanningDO planning,
|
||||
Map<Long, ProjectOutputSplitDO> outputSplitMap) {
|
||||
if (planning == null) {
|
||||
@@ -1708,6 +1729,14 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic
|
||||
if (quarterList == null || quarterList.isEmpty()) {
|
||||
return summary;
|
||||
}
|
||||
boolean hasParentQuarter = quarterList.stream().anyMatch(item -> item.getGuideDetailId() == null);
|
||||
boolean hasGuideDetailQuarter = quarterList.stream().anyMatch(item -> item.getGuideDetailId() != null);
|
||||
if (hasParentQuarter && hasGuideDetailQuarter) {
|
||||
throw exception(PROJECT_PLANNING_QUARTER_SCOPE_MIXED);
|
||||
}
|
||||
if (hasGuideDetailQuarter) {
|
||||
return buildGuideDetailQuarterSummary(planning, quarterList, reportYear, summary);
|
||||
}
|
||||
|
||||
BigDecimal allocatedRatio = ZERO_RATIO;
|
||||
for (ProjectPlanningQuarterDO quarter : quarterList) {
|
||||
@@ -1750,6 +1779,56 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic
|
||||
return summary;
|
||||
}
|
||||
|
||||
private QuarterSummary buildGuideDetailQuarterSummary(ProjectPlanningDO planning,
|
||||
List<ProjectPlanningQuarterDO> quarterList,
|
||||
Integer reportYear,
|
||||
QuarterSummary summary) {
|
||||
BigDecimal baseAmount = multiplyAmount(planning == null ? null : planning.getAssessmentOutputValue(),
|
||||
defaultDistributionRatio(planning));
|
||||
BigDecimal allocatedAmount = ZERO_AMOUNT;
|
||||
BigDecimal historicalAmount = ZERO_AMOUNT;
|
||||
for (ProjectPlanningQuarterDO quarter : quarterList) {
|
||||
BigDecimal distributionAmount = amount(quarter.getDistributionAmount());
|
||||
allocatedAmount = allocatedAmount.add(distributionAmount).setScale(2, RoundingMode.HALF_UP);
|
||||
if (quarter.getDistributionYear() != null && quarter.getDistributionYear() < reportYear) {
|
||||
historicalAmount = historicalAmount.add(distributionAmount).setScale(2, RoundingMode.HALF_UP);
|
||||
continue;
|
||||
}
|
||||
if (!Objects.equals(quarter.getDistributionYear(), reportYear)) {
|
||||
continue;
|
||||
}
|
||||
if (Objects.equals(quarter.getQuarterNo(), 1)) {
|
||||
summary.setQuarterOneAmount(summary.getQuarterOneAmount().add(distributionAmount).setScale(2, RoundingMode.HALF_UP));
|
||||
} else if (Objects.equals(quarter.getQuarterNo(), 2)) {
|
||||
summary.setQuarterTwoAmount(summary.getQuarterTwoAmount().add(distributionAmount).setScale(2, RoundingMode.HALF_UP));
|
||||
} else if (Objects.equals(quarter.getQuarterNo(), 3)) {
|
||||
summary.setQuarterThreeAmount(summary.getQuarterThreeAmount().add(distributionAmount).setScale(2, RoundingMode.HALF_UP));
|
||||
} else if (Objects.equals(quarter.getQuarterNo(), 4)) {
|
||||
summary.setQuarterFourAmount(summary.getQuarterFourAmount().add(distributionAmount).setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
summary.setYearTotalAmount(summary.getYearTotalAmount().add(distributionAmount).setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
summary.setHistoricalIssuedRatio(amountToDistributionRatio(historicalAmount, baseAmount));
|
||||
summary.setQuarterOneRatio(amountToDistributionRatio(summary.getQuarterOneAmount(), baseAmount));
|
||||
summary.setQuarterTwoRatio(amountToDistributionRatio(summary.getQuarterTwoAmount(), baseAmount));
|
||||
summary.setQuarterThreeRatio(amountToDistributionRatio(summary.getQuarterThreeAmount(), baseAmount));
|
||||
summary.setQuarterFourRatio(amountToDistributionRatio(summary.getQuarterFourAmount(), baseAmount));
|
||||
summary.setCurrentYearRatio(amountToDistributionRatio(summary.getYearTotalAmount(), baseAmount));
|
||||
BigDecimal allocatedRatio = amountToDistributionRatio(allocatedAmount, baseAmount);
|
||||
BigDecimal pendingRatio = defaultDistributionRatio(planning).subtract(allocatedRatio)
|
||||
.setScale(4, RoundingMode.HALF_UP);
|
||||
summary.setPendingRatio(pendingRatio.compareTo(BigDecimal.ZERO) < 0 ? ZERO_RATIO : pendingRatio);
|
||||
return summary;
|
||||
}
|
||||
|
||||
private BigDecimal amountToDistributionRatio(BigDecimal amount, BigDecimal baseAmount) {
|
||||
if (baseAmount == null || baseAmount.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return ZERO_RATIO;
|
||||
}
|
||||
return amount(amount).divide(baseAmount, 4, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
private BigDecimal getCategoryRatio(ProjectOutputSplitDO outputSplit, String specialtyCode) {
|
||||
if (OutputSplitBizConstants.SPECIALTY_PROJECT_LEAD.equals(specialtyCode)) {
|
||||
return ratio(outputSplit.getProjectLeadRatio());
|
||||
@@ -1823,26 +1902,6 @@ public class ProjectOutputReportServiceImpl implements ProjectOutputReportServic
|
||||
return "其他产值";
|
||||
}
|
||||
|
||||
private int projectLeadOutputTypeOrder(String ownershipType) {
|
||||
String outputType = resolveBudgetCategoryLabel(ownershipType);
|
||||
if (Objects.equals(outputType, "六大专业考核产值")) {
|
||||
return 1;
|
||||
}
|
||||
if (Objects.equals(outputType, "专项分包-专业所产值")) {
|
||||
return 2;
|
||||
}
|
||||
if (Objects.equals(outputType, "专项分包-源头合作分包产值")) {
|
||||
return 3;
|
||||
}
|
||||
if (Objects.equals(outputType, "专项分包-综合所产值")) {
|
||||
return 4;
|
||||
}
|
||||
if (Objects.equals(outputType, "内部协作产值")) {
|
||||
return 5;
|
||||
}
|
||||
return 6;
|
||||
}
|
||||
|
||||
private String buildPlanningDisplayName(ProjectPlanningDO planning) {
|
||||
if (planning == null) {
|
||||
return "";
|
||||
|
||||
Reference in New Issue
Block a user