Commit e878b011f2815130bb78057b9c7c18144f67bf80

Authored by 曾国涛
1 parent 60b81195

feat(order): 优化内部生产净利润分析表

- 新增生产科单价字段
- 优化内部生产提成计算逻辑
- 修正内部生产净利润率计算方式
- 调整固定成本计算方法
- 优化 Excel 导出数据
src/main/java/com/order/erp/controller/ProjectController.java
... ... @@ -66,7 +66,6 @@ public class ProjectController {
66 66 @PostMapping("/BusinessProfitInfo/listByPage")
67 67 @ApiOperation("业务研发净利润分析表")
68 68 public ServerResult<Page<BusinessProfitInfoVO>> listBusinessProfitInfosByPage(@RequestBody @Validated OrderBaseInfoQueryVO queryVO) {
69   - queryVO.setProductionDepartment(Collections.singletonList("内部"));
70 69 return projectBaseInfoService.listBusinessProfitInfoByPage(queryVO);
71 70 }
72 71  
... ...
src/main/java/com/order/erp/domain/dto/order/ProjectBaseInfoDO.java
... ... @@ -42,6 +42,10 @@ public class ProjectBaseInfoDO extends BaseDO implements Serializable {
42 42  
43 43 private LocalDateTime projectEndTime;
44 44  
  45 + private LocalDateTime projectInnerProfitInfoStartTime;
  46 +
  47 + private LocalDateTime projectInnerProfitInfoEndTime;
  48 +
45 49  
46 50 private BigDecimal spainPaidRmbCommission;
47 51  
... ... @@ -49,5 +53,4 @@ public class ProjectBaseInfoDO extends BaseDO implements Serializable {
49 53  
50 54 private BigDecimal actualExchangeRate;
51 55  
52   -
53 56 }
... ...
src/main/java/com/order/erp/domain/vo/order/InnerProfitInfoVO.java
... ... @@ -59,6 +59,12 @@ public class InnerProfitInfoVO implements Serializable {
59 59 /**
60 60 * 生产科总价合计
61 61 */
  62 + @ApiModelProperty(value = "生产科单价")
  63 + private Double productionDepartmentUnitPrice;
  64 +
  65 + /**
  66 + * 生产科总价合计
  67 + */
62 68 @ApiModelProperty(value = "生产科总价")
63 69 private Double productionDepartmentTotalPrice;
64 70  
... ...
src/main/java/com/order/erp/domain/vo/order/ProjectBaseInfoVO.java
... ... @@ -40,6 +40,9 @@ public class ProjectBaseInfoVO implements Serializable {
40 40  
41 41 private LocalDateTime projectEndTime;
42 42  
  43 + private LocalDateTime projectInnerProfitInfoStartTime;
  44 +
  45 + private LocalDateTime projectInnerProfitInfoEndTime;
43 46  
44 47 private BigDecimal spainPaidRmbCommission;
45 48  
... ...
src/main/java/com/order/erp/service/order/impl/ProjectBaseInfoServiceImpl.java
... ... @@ -20,7 +20,6 @@ import com.order.erp.domain.ProjectApplyTypeEnum;
20 20 import com.order.erp.domain.dto.BaseDO;
21 21 import com.order.erp.domain.dto.SystemSettingDO;
22 22 import com.order.erp.domain.dto.order.OrderBaseInfoDO;
23   -import com.order.erp.domain.dto.order.OrderCostInfoDO;
24 23 import com.order.erp.domain.dto.order.ProjectBaseInfoDO;
25 24 import com.order.erp.domain.model.ProjectApplyDO;
26 25 import com.order.erp.domain.model.ProjectFieldLockRecord;
... ... @@ -177,7 +176,7 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
177 176  
178 177 @Override
179 178 public ServerResult<Page<InnerProfitInfoVO>> listInnerProfitInfoByPage(OrderBaseInfoQueryVO queryVO) {
180   - Page<OrderBaseInfoDO> page = pageProjectNoPrefix(queryVO);
  179 + Page<OrderBaseInfoDO> page = pageProjectNo(queryVO);
181 180 List<OrderInfoResultVO> orderInfoResultVOS = getOrderInfoResultVOS(page, queryVO);
182 181 List<InnerProfitInfoVO> innerProfitInfoVOs = buildInnerProfitInfos(orderInfoResultVOS);
183 182 return buildPageResult(page, innerProfitInfoVOs);
... ... @@ -186,7 +185,7 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
186 185 @Override
187 186 public ServerResult<Page<BusinessProfitInfoVO>> listBusinessProfitInfoByPage(OrderBaseInfoQueryVO queryVO) {
188 187 Page<OrderBaseInfoDO> page = pageProjectNoPrefix(queryVO);
189   - List<OrderInfoResultVO> orderInfoResultVOS = getOrderInfoResultVOS(page, queryVO);
  188 + List<OrderInfoResultVO> orderInfoResultVOS = getOrderInfoResultVOSByProNoPrefix(page, queryVO);
190 189 List<BusinessProfitInfoVO> businessProfitInfoVOs = buildBusinessProfitInfos(orderInfoResultVOS);
191 190 return buildPageResult(page, businessProfitInfoVOs);
192 191 }
... ... @@ -194,6 +193,26 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
194 193 /**
195 194 * 分页查询项目号前8位
196 195 */
  196 + private Page<OrderBaseInfoDO> pageProjectNo(OrderBaseInfoQueryVO queryVO) {
  197 + LambdaQueryWrapper<OrderBaseInfoDO> orderBaseInfoDOLambdaQueryWrapper = orderBaseInfoService.buildQueryByParam(queryVO).select(OrderBaseInfoDO::getId);
  198 + return orderBaseInfoService.page(new Page<>(queryVO.getPage(), queryVO.getPageSize()),
  199 + new LambdaQueryWrapper<OrderBaseInfoDO>()
  200 + .select(OrderBaseInfoDO::getProjectNo)
  201 + .func(!orderBaseInfoDOLambdaQueryWrapper.isEmptyOfWhere(), queryWrapper -> {
  202 + List<OrderBaseInfoDO> list = orderBaseInfoService.list(orderBaseInfoDOLambdaQueryWrapper);
  203 + List<Long> ids = list.stream().map(OrderBaseInfoDO::getId).collect(Collectors.toList());
  204 + if (CollUtil.isNotEmpty(ids)) {
  205 + queryWrapper.in(OrderBaseInfoDO::getId, ids);
  206 + } else {
  207 + queryWrapper.apply("1!=1 ");
  208 + }
  209 + })
  210 + .groupBy(OrderBaseInfoDO::getProjectNo));
  211 + }
  212 +
  213 + /**
  214 + * 分页查询项目号前8位
  215 + */
197 216 private Page<OrderBaseInfoDO> pageProjectNoPrefix(OrderBaseInfoQueryVO queryVO) {
198 217 LambdaQueryWrapper<OrderBaseInfoDO> orderBaseInfoDOLambdaQueryWrapper = orderBaseInfoService.buildQueryByParam(queryVO).select(OrderBaseInfoDO::getId);
199 218 return orderBaseInfoService.page(new Page<>(queryVO.getPage(), queryVO.getPageSize()),
... ... @@ -211,12 +230,35 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
211 230 .groupBy("LEFT(project_no, 8)"));
212 231 }
213 232  
  233 +
  234 +
214 235 /**
215 236 * 查询订单列表
216 237 */
217 238 private List<OrderInfoResultVO> getOrderInfoResultVOS(Page<OrderBaseInfoDO> page, OrderBaseInfoQueryVO queryVO) {
218 239 List<String> projectNoPrefix = page.getRecords().stream()
219 240 .map(OrderBaseInfoDO::getProjectNo)
  241 + //.map(x -> x.substring(0, 8))
  242 + .collect(Collectors.toList());
  243 +
  244 + List<OrderBaseInfoDO> orderBaseInfoDOS = orderBaseInfoService.list(orderBaseInfoService.buildQueryByParam(queryVO)
  245 + .func(query -> {
  246 + if (CollUtil.isNotEmpty(projectNoPrefix)) {
  247 + query.in(OrderBaseInfoDO::getProjectNo, projectNoPrefix);
  248 + } else {
  249 + query.apply("1!=1");
  250 + }
  251 + }));
  252 +
  253 + return orderBaseInfoService.wrapperOrderResultList(true, orderBaseInfoDOS);
  254 + }
  255 +
  256 + /**
  257 + * 查询订单列表
  258 + */
  259 + private List<OrderInfoResultVO> getOrderInfoResultVOSByProNoPrefix(Page<OrderBaseInfoDO> page, OrderBaseInfoQueryVO queryVO) {
  260 + List<String> projectNoPrefix = page.getRecords().stream()
  261 + .map(OrderBaseInfoDO::getProjectNo)
220 262 .map(x -> x.substring(0, 8))
221 263 .collect(Collectors.toList());
222 264  
... ... @@ -249,8 +291,23 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
249 291 }
250 292 Map<String, List<OrderInfoResultVO>> orderBaseInfoDOSGroupByProjectNoPre = orderInfoResultVOS.stream()
251 293 .collect(Collectors.groupingBy(
252   - order -> order.getProjectNo().substring(0, 8)
  294 + OrderBaseInfoVO::getProjectNo
253 295 ));
  296 + List<String> projectNoPrefix = orderInfoResultVOS.stream()
  297 + .map(OrderInfoResultVO::getProjectNo)
  298 + //.map(projectNo -> projectNo.substring(0, 8))
  299 + .distinct()
  300 + .collect(Collectors.toList());
  301 + List<ProjectBaseInfoDO> list = this.lambdaQuery()
  302 + .func(query -> {
  303 + if (CollUtil.isNotEmpty(projectNoPrefix)) {
  304 + query.in(ProjectBaseInfoDO::getProjectNoPrefix, projectNoPrefix);
  305 + } else {
  306 + query.apply("1!=1");
  307 + }
  308 + })
  309 + .list();
  310 + Map<String, ProjectBaseInfoDO> no2ProjectInfoMap = list.stream().collect(Collectors.toMap(ProjectBaseInfoDO::getProjectNoPrefix, Function.identity()));
254 311 Set<String> collect = orderInfoResultVOS.stream()
255 312 .map(OrderBaseInfoVO::getCustomerCode)
256 313 .collect(Collectors.toSet());
... ... @@ -261,7 +318,7 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
261 318 .list();
262 319 Map<String, SystemSettingDO> systemSettingDOSMap = systemSettingDOS.stream()
263 320 .collect(Collectors.toMap(SystemSettingDO::getSettingValue, Function.identity(), (x, y) -> x));
264   - List<InnerProfitInfoVO> innerProfitInfoVOs = orderBaseInfoDOSGroupByProjectNoPre.entrySet().stream().map(entry -> {
  321 + return orderBaseInfoDOSGroupByProjectNoPre.entrySet().stream().map(entry -> {
265 322 List<OrderInfoResultVO> details = entry.getValue();
266 323 InnerProfitInfoVO innerProfitInfoVO = InnerProfitInfoVO.builder()
267 324 .customerCode(details.get(0).getCustomerCode())
... ... @@ -276,6 +333,18 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
276 333 .reduce(Integer::sum)
277 334 .ifPresent(innerProfitInfoVO::setOrderCount);
278 335  
  336 + //生产科单价
  337 + details.stream()
  338 + .map(OrderInfoResultVO::getProfitAnalysisInfo)
  339 + .filter(Objects::nonNull)
  340 + .map(OrderProfitAnalysisVO::getCustomerRmbPrice)
  341 + .filter(Objects::nonNull)
  342 + .map(BigDecimal::valueOf)
  343 + .reduce(BigDecimal::add)
  344 + .ifPresent(price -> {
  345 + innerProfitInfoVO.setProductionDepartmentUnitPrice(price.doubleValue());
  346 + });
  347 +
279 348 //生产科总价
280 349 details.stream()
281 350 .map(OrderInfoResultVO::getProfitAnalysisInfo)
... ... @@ -334,16 +403,8 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
334 403 }
335 404  
336 405 //hod时间差
337   - LocalDateTime earliest = details.stream()
338   - .map(OrderInfoResultVO::getOrderHodTime)
339   - .map(hodTime -> LocalDateTime.parse(hodTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
340   - .min(Comparator.naturalOrder())
341   - .orElse(null);
342   - LocalDateTime latest = details.stream()
343   - .map(OrderInfoResultVO::getOrderHodTime)
344   - .map(hodTime -> LocalDateTime.parse(hodTime, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
345   - .max(Comparator.naturalOrder())
346   - .orElse(null);
  406 + LocalDateTime earliest = Optional.ofNullable(no2ProjectInfoMap.get(entry.getKey())).map(ProjectBaseInfoDO::getProjectInnerProfitInfoStartTime).orElse(null);
  407 + LocalDateTime latest = Optional.ofNullable(no2ProjectInfoMap.get(entry.getKey())).map(ProjectBaseInfoDO::getProjectInnerProfitInfoEndTime).orElse(null);;
347 408 innerProfitInfoVO.setProduceStartTime(earliest);
348 409 innerProfitInfoVO.setProduceEndTime(latest);
349 410 SystemSettingDO systemSettingDO = systemSettingDOSMap.get(innerProfitInfoVO.getCustomerCode());
... ... @@ -369,13 +430,18 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
369 430 && Objects.nonNull(latest)) {
370 431 Duration duration = Duration.between(earliest, latest);
371 432 //内部生产固定成本¥
372   - innerProfitInfoVO.setInnerProductionFixedCost(BigDecimal.valueOf(duration.toDays() * Double.parseDouble(fixCostValue)));
  433 + innerProfitInfoVO.setInnerProductionFixedCost(BigDecimal.valueOf((duration.toDays()+1) * Double.parseDouble(fixCostValue)));
373 434 }
374 435 if (com.baomidou.mybatisplus.core.toolkit.StringUtils.isNotBlank(ratioValue)
375 436 && Objects.nonNull(innerProfitInfoVO.getPredictRatio())
376 437 && innerProfitInfoVO.getPredictRatio().compareTo(BigDecimal.valueOf(1.05)) < 0) {
377 438 //内部生产提成¥
378   - innerProfitInfoVO.setInnerProductionCommission(BigDecimal.valueOf(details.size()).multiply(BigDecimal.valueOf(Double.parseDouble(ratioValue))));
  439 + innerProfitInfoVO.setInnerProductionCommission(
  440 + details.stream()
  441 + .map(detail -> BigDecimal.valueOf(detail.getOrderCount())
  442 + .multiply(BigDecimal.valueOf(Optional.ofNullable(detail.getProfitAnalysisInfo()).map(OrderProfitAnalysisVO::getCustomerRmbPrice).orElse(0.0))))
  443 + .reduce(BigDecimal.ZERO, BigDecimal::add)
  444 + );
379 445 }
380 446 //内部生产净利润 = 总价-实际费用-固定成本-提成
381 447 if (Objects.nonNull(innerProfitInfoVO.getProductionDepartmentTotalPrice())
... ... @@ -398,7 +464,6 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
398 464 }
399 465 return innerProfitInfoVO;
400 466 }).collect(Collectors.toList());
401   - return innerProfitInfoVOs;
402 467 }
403 468  
404 469 private List<BusinessProfitInfoVO> buildBusinessProfitInfos(List<OrderInfoResultVO> orderInfoResultVOS) {
... ... @@ -460,7 +525,7 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
460 525 }
461 526 }));
462 527 BigDecimal exchangeRate = systemSettingService.getExchangeRate();
463   - List<BusinessProfitInfoVO> businessProfitInfoVOs = orderBaseInfoDOSGroupByProjectNoPre.entrySet().stream().map(entry -> {
  528 + return orderBaseInfoDOSGroupByProjectNoPre.entrySet().stream().map(entry -> {
464 529 List<OrderInfoResultVO> details = entry.getValue();
465 530 ProjectBaseInfoDO projectBaseInfoDO = noPrefix2ProjectProfitAnalysisDOMap.get(entry.getKey());
466 531 BusinessProfitInfoVO businessProfitInfoVO = BusinessProfitInfoVO.builder()
... ... @@ -604,7 +669,7 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
604 669 }
605 670 }
606 671 //固定成本
607   - businessProfitInfoVO.setFixedCost(BigDecimal.valueOf(between * Double.parseDouble(fixCostValue)));
  672 + businessProfitInfoVO.setFixedCost(BigDecimal.valueOf((between+1) * Double.parseDouble(fixCostValue)));
608 673 if (Objects.nonNull(spainRatio)
609 674 && Objects.nonNull(businessProfitInfoVO.getCustomerRmbTotalPrice())) {
610 675 //西班牙提成
... ... @@ -670,7 +735,6 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
670 735 .add(Optional.ofNullable(businessProfitInfoVO.getActualOrderRmbPrice()).orElse(BigDecimal.ZERO)));
671 736 return businessProfitInfoVO;
672 737 }).collect(Collectors.toList());
673   - return businessProfitInfoVOs;
674 738 }
675 739  
676 740 @Override
... ... @@ -774,7 +838,11 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
774 838 createMergedRow(sheet, cellStyle, 46, 47, 5, 6, Optional.ofNullable(businessProfitInfoVO.getPacketProfitRmbPrice()).map(price -> "¥" + price).orElse("-"));
775 839 createMergedRow(sheet, cellStyle, 46, 47, 7, 9, "");
776 840 createMergedRow(sheet, cellStyle, 48, 49, 0, 4, "包装费用净利润率");
777   - createMergedRow(sheet, cellStyle, 48, 49, 5, 6, Optional.ofNullable(businessProfitInfoVO.getPacketProfitRate()).map(rate -> rate.multiply(new BigDecimal(100)) + "%").orElse("-"));
  841 + createMergedRow(sheet, cellStyle, 48, 49, 5, 6,
  842 + Optional.ofNullable(businessProfitInfoVO.getPacketRmbTotalPrice())
  843 + .map(totalPrice -> Optional.ofNullable(businessProfitInfoVO.getPacketProfitRmbPrice())
  844 + .orElse(BigDecimal.valueOf(0))
  845 + .divide(BigDecimal.valueOf(totalPrice), 4, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(100)) + "%").orElse("-"));
778 846 createMergedRow(sheet, cellStyle, 48, 49, 7, 9, "");
779 847 createMergedRow(sheet, cellStyle, 50, 51, 0, 4, "实际汇率");
780 848 createMergedRow(sheet, cellStyle, 50, 51, 5, 6, Optional.ofNullable(businessProfitInfoVO.getActualExchangeRate()).map(String::valueOf).orElse("-"));
... ... @@ -798,7 +866,7 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
798 866  
799 867 @Override
800 868 public void exportInnerProfitInfo(HttpServletResponse response, String projectNoPrefix) throws Exception {
801   - ServerResult serverResult = listInnerProfitInfoByPage(OrderBaseInfoQueryVO.builder().page(1).pageSize(1).projectNoLikeRight(projectNoPrefix).build());
  869 + ServerResult serverResult = listInnerProfitInfoByPage(OrderBaseInfoQueryVO.builder().page(1).pageSize(1).productionDepartment(Collections.singletonList("内部")).projectNoLikeRight(projectNoPrefix).build());
802 870 Page<InnerProfitInfoVO> page = (Page<InnerProfitInfoVO>) serverResult.getData();
803 871 List<InnerProfitInfoVO> profitInfoVOS = page.getRecords();
804 872 InnerProfitInfoVO ProfitInfoVO = profitInfoVOS.get(0);
... ... @@ -886,7 +954,7 @@ public class ProjectBaseInfoServiceImpl extends ServiceImpl&lt;ProjectBaseInfoMappe
886 954 // 第八部分:提成与净利润
887 955 createMergedRow(sheet, cellStyle, 20, 21, 0, 3, "内部生产提成");
888 956 createMergedRow(sheet, cellStyle, 20, 21, 4, 5, "¥" + Optional.ofNullable(vo.getInnerProductionCommission()).orElse(BigDecimal.ZERO));
889   - createMergedRow(sheet, cellStyle, 20, 21, 6, 7, "净利润");
  957 + createMergedRow(sheet, cellStyle, 20, 21, 6, 7, "净利润");
890 958 createMergedRow(sheet, cellStyle, 20, 21, 8, 9, "");
891 959  
892 960 createMergedRow(sheet, cellStyle, 22, 23, 0, 3, "内部生产净利润");
... ...