diff --git a/src/main/java/com/qs/serve/modules/tbs/controller/TbsActivityController.java b/src/main/java/com/qs/serve/modules/tbs/controller/TbsActivityController.java index afad7c5e..b8256e50 100644 --- a/src/main/java/com/qs/serve/modules/tbs/controller/TbsActivityController.java +++ b/src/main/java/com/qs/serve/modules/tbs/controller/TbsActivityController.java @@ -60,6 +60,8 @@ public class TbsActivityController { private final VtbVerificationService verificationService; + private final TbsActivityApplicationService tbsActivityApplicationService; + /** * 列表 * @param param @@ -101,6 +103,7 @@ public class TbsActivityController { return R.byPageHelperList(list); } + /** * 费用详情列表 * @param id @@ -178,6 +181,18 @@ public class TbsActivityController { return R.ok(activity); } + /** + * 对比预算 + * @param param + * @return + */ + @PostMapping("/matchBudget") + @SysLog(module = SystemModule.Budget, title = "费用活动", biz = BizType.UPDATE) + public R matchBudget(@RequestBody @Valid TbsActivityBo param){ + return R.ok(tbsActivityApplicationService.matchBudgetCostResult(false,param)); + } + + /** * 更新 * @param param diff --git a/src/main/java/com/qs/serve/modules/tbs/entity/bo/TbsActivityBo.java b/src/main/java/com/qs/serve/modules/tbs/entity/bo/TbsActivityBo.java index 17a51091..22b770de 100644 --- a/src/main/java/com/qs/serve/modules/tbs/entity/bo/TbsActivityBo.java +++ b/src/main/java/com/qs/serve/modules/tbs/entity/bo/TbsActivityBo.java @@ -23,6 +23,9 @@ public class TbsActivityBo implements Serializable { /** id */ private Long id; + /** 供应商id */ + private Long supplierId; + /** 费用申请id */ @NotNull(message = "费用申请id不能为空") private Long costApplyId; diff --git a/src/main/java/com/qs/serve/modules/tbs/entity/dto/TbsBudgetCostResult.java b/src/main/java/com/qs/serve/modules/tbs/entity/dto/TbsBudgetCostResult.java index c08e76c8..7e09d9e8 100644 --- a/src/main/java/com/qs/serve/modules/tbs/entity/dto/TbsBudgetCostResult.java +++ b/src/main/java/com/qs/serve/modules/tbs/entity/dto/TbsBudgetCostResult.java @@ -31,4 +31,7 @@ public class TbsBudgetCostResult { /** 表数据封装 */ private TbsBudgetTableVo tableData; + /** 匹配状态:0-无匹配的预算;1-有匹配的预算 */ + private Integer state; + } diff --git a/src/main/java/com/qs/serve/modules/tbs/service/TbsActivityApplicationService.java b/src/main/java/com/qs/serve/modules/tbs/service/TbsActivityApplicationService.java new file mode 100644 index 00000000..d70586c6 --- /dev/null +++ b/src/main/java/com/qs/serve/modules/tbs/service/TbsActivityApplicationService.java @@ -0,0 +1,362 @@ +package com.qs.serve.modules.tbs.service; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.qs.serve.common.model.consts.SysConfigKey; +import com.qs.serve.common.util.Assert; +import com.qs.serve.common.util.CollectionUtil; +import com.qs.serve.modules.bms.entity.BmsSubject; +import com.qs.serve.modules.bms.entity.BmsSupplier; +import com.qs.serve.modules.bms.service.BmsSupplierService; +import com.qs.serve.modules.goods.entity.dto.TbsCenterDto; +import com.qs.serve.modules.sys.service.SysConfigService; +import com.qs.serve.modules.tbs.common.TbsGoodsType; +import com.qs.serve.modules.tbs.common.util.TbsBudgetCostUtil; +import com.qs.serve.modules.tbs.entity.*; +import com.qs.serve.modules.tbs.entity.bo.TbsActivityBo; +import com.qs.serve.modules.tbs.entity.dto.TbsBudgetCostResult; +import com.qs.serve.modules.tbs.entity.vo.TbsBudgetTableVo; +import com.qs.serve.modules.tbs.mapper.TbsActivityGoodsMapper; +import com.qs.serve.modules.tbs.mapper.TbsActivityMapper; +import com.qs.serve.modules.tbs.mapper.TbsBudgetMapper; +import com.qs.serve.modules.tbs.service.impl.TbsActivityServiceImpl; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.checkerframework.checker.units.qual.A; +import org.jetbrains.annotations.NotNull; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +/** + * 预算应用层服务 + * @author YenHex + * @since 2022/12/6 + */ +@Slf4j +@Service +@AllArgsConstructor +public class TbsActivityApplicationService { + + private TbsScheduleItemBudgetService tbsScheduleItemBudgetService; + private TbsBudgetMapper tbsBudgetMapper; + private TbsBudgetConditionService tbsBudgetConditionService; + private TbsBudgetCostItemService tbsBudgetCostItemService; + private SysConfigService configService; + private TbsActivityServiceImpl activityService; + private final BmsSupplierService supplierService; + + /** + * 对比预算 + * @param throwEx + * @param activityBo + * @return + */ + public TbsBudgetCostResult matchBudgetCostResult(Boolean throwEx, TbsActivityBo activityBo){ + BmsSupplier supplier = supplierService.getById(activityBo.getSupplierId()); + //允许超出预算标识 + boolean overspend = configService.getByKey(SysConfigKey.TbsBudgetOverspend).getConfigValue().equals("1"); + //考核期有关的预算id,判断逻辑为活动需要允许在 + List budgetIds = new ArrayList<>(); + //所有满足条件的考核期,用于加载历史核销费用 + List scheduleItemList = this.loadScheduleBudgetAndSetting(activityBo, budgetIds); + if(budgetIds.size()<1&&throwEx){ + Assert.throwEx("无匹配的预算"); + } + if(budgetIds.size()<1){ + TbsBudgetCostResult result = new TbsBudgetCostResult(); + result.setState(0); + return result; + } + //加载预算占用历史 + List scheduleItemBudgetIds = scheduleItemList.stream().map(TbsScheduleItemBudget::getId).collect(Collectors.toList()); + List hisBudgetCostItemList = tbsBudgetCostItemService.listByScheduleItemBudgetIds(scheduleItemBudgetIds,null); + Map> hisCostGroupByItemBudget = hisBudgetCostItemList.stream() + .collect(Collectors.groupingBy(TbsBudgetCostItem::getScheduleItemBudgetId)); + List activityGoodsList = activityService.buildActGoodsList(activityBo,null,null); + //构建预算费用占用明细 + Map subjectMap = new HashMap<>(); + List activitySubjects = new ArrayList<>(); + List activityCenterList = new ArrayList<>(); + Map centerDtoMap = new HashMap<>(); + activityService.initSubjectAndCenter(activityBo,subjectMap,centerDtoMap,activitySubjects,activityCenterList,null,null); + Map checkAmountMap = new HashMap<>(); + List activityCenterGoodsList = new ArrayList<>(); + activityService.initCenterGoods(activityBo,subjectMap,centerDtoMap,checkAmountMap,activityGoodsList,activityCenterGoodsList,null,new TbsActivity(),supplier); + // 加载所有条件 + LambdaQueryWrapper budgetLqw = new LambdaQueryWrapper<>(); + budgetLqw.in(TbsBudget::getId,budgetIds); + budgetLqw.eq(TbsBudget::getBudgetState,1); + List budgetList = tbsBudgetMapper.selectList(budgetLqw); + + List noConditionBudgetList = new ArrayList<>(); + boolean budgetNoCondition = this.loadConditionByBudgetsAndMatch(budgetList, noConditionBudgetList); + //预算条件需包含活动条件, Map结构:活动id->满足的预算id列表 + List activityAllowBudgetIds = this.buildActivityBudgetMap(activityCenterGoodsList, budgetList, noConditionBudgetList); + //活动拦截 + this.handleNoBudgetActivity(throwEx, overspend, budgetNoCondition,activityAllowBudgetIds); + //统计当前活动前置项占用预算 + Map counterMap = new HashMap<>(); + //匹配预算主要方法 + final List actMatchList = new ArrayList<>(); + final List actUnMatchList = new ArrayList<>(); + for (TbsActivityCenterGoods activityCostItem : activityCenterGoodsList) { + this.matchActivityMain(activityCostItem,throwEx, overspend, scheduleItemList, hisCostGroupByItemBudget, budgetList, + noConditionBudgetList, counterMap, actMatchList, actUnMatchList,activityAllowBudgetIds); + } + List budgetMatchList = actMatchList.stream() + .map(TbsActivityCenterGoods::toBudgetCostItem).collect(Collectors.toList()); + List budgetUnMatchList = actUnMatchList.stream() + .map(TbsActivityCenterGoods::toBudgetCostItem).collect(Collectors.toList()); + TbsBudgetCostResult result = new TbsBudgetCostResult(); + result.setState(1); + result.setBudgetMatchList(budgetMatchList); + result.setBudgetUnMatchList(budgetUnMatchList); + return result; + } + + /** + * 预算条件需包含活动条件, Map结构:活动id->满足的预算id列表 + * @param centerGoodsList + * @param budgetList + * @param noConditionBudgetList + * @return + */ + private List buildActivityBudgetMap(List centerGoodsList, + List budgetList, + List noConditionBudgetList) { + List budgetIdsOfActivity = new ArrayList<>(); + if(CollectionUtil.isEmpty(noConditionBudgetList)){ + budgetIdsOfActivity.addAll(noConditionBudgetList.stream().map(TbsBudget::getId).collect(Collectors.toList())); + } + Map> activityCenterGoodsListMap = centerGoodsList.stream().collect(Collectors.groupingBy(a->a.getTargetType()+":"+a.getTargetId())); + for (TbsBudget budget : budgetList) { + if(budget.getConditionFlag().equals(1)){ + List budgetConditionList = budget.getBudgetConditionList(); + boolean hasNoMatch = false; + for (String key : activityCenterGoodsListMap.keySet()) { + List centerGoods = activityCenterGoodsListMap.get(key); + TbsActivityCenterGoods activityCenterGoods = centerGoods.get(0); + boolean matchGoods = false; + for (TbsBudgetCondition budgetCondition : budgetConditionList) { + if(activityCenterGoods.getTargetLevelPathIds().contains(budgetCondition.getTargetLevelPathIds())){ + matchGoods = true; + break; + } + } + if(!matchGoods){ + hasNoMatch = true; + break; + } + } + if(!hasNoMatch){ + budgetIdsOfActivity.add(budget.getId()); + } + } + } + return budgetIdsOfActivity; + } + + + + /** + * 匹配的主要方法 + * @param activityCostItem 活动项 + * @param throwEx 预算不够时抛出异常 + * @param overspend 超值标识 + * @param scheduleItemBudgets 时间区间条件 + * @param hisCostGroupByItemBudget 历史费用组 + * @param allBudgetList 所有预算 + * @param noConditionBudgetList 没有条件的预算列表 + * @param counterMap 历史预算占用 + * @param actMatchList 匹配预算占用的费用(含超支预算) + * @param actUnMatchList 没有匹配预算的费用 + * @param activityAllowBudgetIds 满足当前活动项的费用idMap + */ + private void matchActivityMain(TbsActivityCenterGoods activityCostItem, + Boolean throwEx, + boolean overspend, + List scheduleItemBudgets, + Map> hisCostGroupByItemBudget, + final List allBudgetList, + List noConditionBudgetList, + Map counterMap, + final List actMatchList, + final List actUnMatchList, + final List activityAllowBudgetIds) { + //过滤满全条件的预算 + List allowBudgetIds = activityAllowBudgetIds; + List budgetList = allBudgetList.stream().filter(obj->allowBudgetIds.contains(obj.getId())).collect(Collectors.toList()); + //PS:排序规则:优先为时间条件,其次匹配品牌条件 + //按品类条件,提取可用预算(列表已按小维度到大维度排列) + List currentItemBudgetList = this.filterMatchGoodsCondition(budgetList, activityCostItem); + //关联无条件预算 + currentItemBudgetList.addAll(noConditionBudgetList); + //提取可用预算的考核期 + List currentScheduleItemBudgets = new ArrayList<>(); + for (TbsBudget budget : currentItemBudgetList) { + for (TbsScheduleItemBudget scheduleItemBudget : scheduleItemBudgets) { + if(scheduleItemBudget.getBudgetId().equals(budget.getId())){ + currentScheduleItemBudgets.add(scheduleItemBudget); + } + } + } + //检测是否有是否足够预算 + BigDecimal goodsAmount = activityCostItem.getCenterGoodsAmount(); + if(CollectionUtil.isNotEmpty(currentScheduleItemBudgets)){ + boolean isMatch = false; + for (TbsScheduleItemBudget itemBudget : currentScheduleItemBudgets) { + //历史费用 + BigDecimal totalUsed = TbsBudgetCostUtil.totalHisCost(hisCostGroupByItemBudget.get(itemBudget.getId())); + //当前项费用 + BigDecimal budgetAmount = itemBudget.getBudgetAmount(); + //前置费用 + BigDecimal lastAmount = counterMap.get(itemBudget.getId()); + if(lastAmount==null){ + lastAmount = BigDecimal.ZERO; + } + if(budgetAmount.subtract(lastAmount).subtract(totalUsed).compareTo(goodsAmount)>0){ + activityCostItem.setBudgetId(itemBudget.getBudgetId()); + activityCostItem.setScheduleId(itemBudget.getScheduleId()); + activityCostItem.setScheduleItemId(itemBudget.getScheduleItemId()); + activityCostItem.setScheduleItemBudgetId(itemBudget.getId()); + isMatch = true; + //记录当前费用使用考核期费用 + lastAmount = lastAmount.add(goodsAmount); + counterMap.put(itemBudget.getId(),lastAmount); + break; + } + } + //无匹配的预算 + if(!isMatch){ + if(overspend){ + TbsScheduleItemBudget itemBudget = currentScheduleItemBudgets.get(0); + activityCostItem.setBudgetId(itemBudget.getBudgetId()); + activityCostItem.setScheduleId(itemBudget.getScheduleId()); + activityCostItem.setScheduleItemId(itemBudget.getScheduleItemId()); + activityCostItem.setScheduleItemBudgetId(itemBudget.getId()); + //记录当前费用使用考核期费用 + BigDecimal lastAmount = counterMap.get(itemBudget.getId()); + if(lastAmount==null){ + lastAmount = BigDecimal.ZERO; + } + lastAmount = lastAmount.add(goodsAmount); + counterMap.put(itemBudget.getId(),lastAmount); + }else if (throwEx){ + Assert.throwEx("品类["+ activityCostItem.getTargetName()+"]预算不足"); + } + activityCostItem.setBudgetId(0L); + activityCostItem.setScheduleId(0L); + activityCostItem.setScheduleItemId(0L); + activityCostItem.setScheduleItemBudgetId(0L); + } + actMatchList.add(activityCostItem); + }else { + if(throwEx){ + Assert.throwEx("品类["+ activityCostItem.getTargetName()+"]无可用预算"); + } + if(overspend){ + //添加到预算超支表 + activityCostItem.setBudgetId(0L); + activityCostItem.setScheduleId(0L); + activityCostItem.setScheduleItemId(0L); + activityCostItem.setScheduleItemBudgetId(0L); + actUnMatchList.add(activityCostItem); + } + } + } + + /** + * 按品类条件,提取可用预算 + * @param budgetList + * @param activityCostItem + * @return + */ + @NotNull + private List filterMatchGoodsCondition(List budgetList,TbsActivityCenterGoods activityCostItem) { + String levelPath = activityCostItem.getTargetLevelPathIds(); + Set levelPathSet = new LinkedHashSet<>(); + levelPathSet.add(levelPath); + TbsBudgetCostUtil.buildPaths(levelPath,levelPathSet); + List currentItemBudgetList = new ArrayList<>(); + for (TbsBudget budget : budgetList) { + if(budget.getConditionFlag().equals(1)){ + List budgetConditionListByBudget = budget.getBudgetConditionList(); + boolean unMatch = true; + for (String conditionString : levelPathSet) { + if(unMatch){ + for (TbsBudgetCondition budgetCondition : budgetConditionListByBudget) { + if(budgetCondition.getTargetLevelPathIds().contains(conditionString)){ + currentItemBudgetList.add(budget); + unMatch = false; + break; + } + } + } + } + } + } + return currentItemBudgetList; + } + + /** + * 1.加载所有的条件 + * 2.匹配初无条件预算 + * 3.预算条件关联到预算对象中 + * @param budgetList + * @param noConditionBudgetList + * @return 含有无条件预算标识 + */ + private boolean loadConditionByBudgetsAndMatch( List budgetList, List noConditionBudgetList) { + boolean budgetNoCondition = false; + for (TbsBudget budget : budgetList) { + if(budget.getConditionFlag().equals(1)){ + List budgetConditionListByBudgetId = tbsBudgetConditionService.getByBudgetId(budget.getId()); + budget.setBudgetConditionList(budgetConditionListByBudgetId); + }else { + budgetNoCondition = true; + noConditionBudgetList.add(budget); + } + } + return budgetNoCondition; + } + + /** + * 拦截没有预算的活动 + * @param throwEx 抛出异常 + * @param overspend 超支 + * @param budgetNoCondition 标识,没有条件的预算 + */ + private void handleNoBudgetActivity(Boolean throwEx, boolean overspend, + boolean budgetNoCondition, + List activityAllowBudgetIds) { + if(!overspend && !budgetNoCondition && throwEx ){ + Assert.throwEx("活动无可用预算"); + } + if(throwEx){ + if(activityAllowBudgetIds.size()<1){ + Assert.throwEx("活动无可用预算"); + } + } + } + + /** + * 1.通过活动加载所有满足条件的考核期(用于加载历史核销费用), + * 2.设置匹配的考核期列表到活动中 + * @param budgetIds + */ + public List loadScheduleBudgetAndSetting(TbsActivityBo activityBo, + List budgetIds) { + Set budgetIdsSet = new HashSet<>(); + List budgetItemList = tbsScheduleItemBudgetService + .betweenDateList(activityBo.getPreStartDate(),activityBo.getPreEndDate()); + if(CollectionUtil.isNotEmpty(budgetItemList)){ + for (TbsScheduleItemBudget item : budgetItemList) { + budgetIdsSet.add(item.getBudgetId()); + } + } + budgetIds.addAll(budgetIdsSet); + return budgetItemList; + } +} diff --git a/src/main/java/com/qs/serve/modules/tbs/service/impl/TbsActivityServiceImpl.java b/src/main/java/com/qs/serve/modules/tbs/service/impl/TbsActivityServiceImpl.java index ea4f1196..a2f69f34 100644 --- a/src/main/java/com/qs/serve/modules/tbs/service/impl/TbsActivityServiceImpl.java +++ b/src/main/java/com/qs/serve/modules/tbs/service/impl/TbsActivityServiceImpl.java @@ -127,17 +127,17 @@ public class TbsActivityServiceImpl extends ServiceImpl activityGoodsList = this.buildActGoodsList(activityBo,costApply,activity); + List activityGoodsList = this.buildActGoodsList(activityBo,costApply.getId(),activity.getId()); //费用详情 Map subjectMap = new HashMap<>(); List activitySubjects = new ArrayList<>(); List activityCenterList = new ArrayList<>(); Map centerDtoMap = new HashMap<>(); - this.initSubjectAndCenter(activityBo,subjectMap,centerDtoMap,activitySubjects,activityCenterList,costApply,activity); + this.initSubjectAndCenter(activityBo,subjectMap,centerDtoMap,activitySubjects,activityCenterList,costApply.getId(),activity.getId()); //产品成本配占比 Map checkAmountMap = new HashMap<>(); List activityCenterGoodsList = new ArrayList<>(); - this.initCenterGoods(activityBo,subjectMap,centerDtoMap,checkAmountMap,activityGoodsList,activityCenterGoodsList,costApply,activity,supplier); + this.initCenterGoods(activityBo,subjectMap,centerDtoMap,checkAmountMap,activityGoodsList,activityCenterGoodsList,costApply.getId(),activity,supplier); //校验金额和比率 Map> actCenterGoodsMap = activityCenterGoodsList.stream().collect(Collectors.groupingBy(TbsActivityCenterGoods::getSubjectId)); for (Long subjectId : actCenterGoodsMap.keySet()) { @@ -236,16 +236,16 @@ public class TbsActivityServiceImpl extends ServiceImpl subjectMap, Map centerDtoMap, Map checkAmountMap, List activityGoodsList, List activityCenterGoodsList, - TbsCostApply costApply,TbsActivity activity,BmsSupplier supplier){ + Long costApplyId,TbsActivity activity,BmsSupplier supplier){ for (int i = 0; i < activityBo.getActivityCenterGoodsList().size(); i++) { TbsActivityCenterGoodsBo centerGoodsBo = activityBo.getActivityCenterGoodsList().get(i); BmsSubject subject = subjectMap.get(centerGoodsBo.getSubjectId()); @@ -259,7 +259,7 @@ public class TbsActivityServiceImpl extends ServiceImpl subjectMap, Map centerDtoMap, List activitySubjects, List activityCenterList, - TbsCostApply costApply,TbsActivity activity){ + Long costApplyId,Long activityId){ for (TbsActivitySubjectBo subjectBo : activityBo.getActivitySubjectList()) { BmsSubject subject = subjectService.getById(subjectBo.getSubjectId()); if(subject==null){ @@ -339,8 +339,8 @@ public class TbsActivityServiceImpl extends ServiceImpl buildActGoodsList(TbsActivityBo activityBo,TbsCostApply costApply,TbsActivity activity){ + public List buildActGoodsList(TbsActivityBo activityBo,Long costApplyId,Long activityId){ List activityGoodsList = new ArrayList<>(); String goodsType = activityBo.getActivityGoodsType(); if(goodsType.equals(TbsGoodsType.spu.name())){ @@ -406,8 +406,8 @@ public class TbsActivityServiceImpl extends ServiceImpl costLqw = new LambdaQueryWrapper<>(); costLqw.in(TbsBudgetCostItem::getScheduleItemBudgetId,ids); - costLqw.ne(TbsBudgetCostItem::getCostApplyId,neCostApplyId); + if(neCostApplyId!=null){ + costLqw.ne(TbsBudgetCostItem::getCostApplyId,neCostApplyId); + } return this.list(costLqw); } return new ArrayList<>();