Commit e878b011f2815130bb78057b9c7c18144f67bf80
1 parent
60b81195
feat(order): 优化内部生产净利润分析表
- 新增生产科单价字段 - 优化内部生产提成计算逻辑 - 修正内部生产净利润率计算方式 - 调整固定成本计算方法 - 优化 Excel 导出数据
Showing
5 changed files
with
105 additions
and
26 deletions
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<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<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<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<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<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<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<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<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<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<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<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<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<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<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<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<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, "内部生产净利润"); | ... | ... |