| Line 1... |
Line 1... |
| 1 |
package com.spice.profitmandi.service;
|
1 |
package com.spice.profitmandi.service;
|
| 2 |
|
2 |
|
| - |
|
3 |
import com.spice.profitmandi.common.enumuration.ActivationType;
|
| - |
|
4 |
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
|
| - |
|
5 |
import com.spice.profitmandi.common.model.CustomRetailer;
|
| 3 |
import com.spice.profitmandi.common.model.ProfitMandiConstants;
|
6 |
import com.spice.profitmandi.common.model.ProfitMandiConstants;
|
| - |
|
7 |
import com.spice.profitmandi.dao.entity.auth.AuthUser;
|
| - |
|
8 |
import com.spice.profitmandi.dao.entity.auth.PartnerCollectionRemark;
|
| - |
|
9 |
import com.spice.profitmandi.dao.entity.auth.RbmCallSequenceLog;
|
| - |
|
10 |
import com.spice.profitmandi.dao.entity.cs.Position;
|
| - |
|
11 |
import com.spice.profitmandi.dao.entity.cs.Ticket;
|
| - |
|
12 |
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
|
| 4 |
import com.spice.profitmandi.dao.entity.fofo.MonthlyTarget;
|
13 |
import com.spice.profitmandi.dao.entity.fofo.MonthlyTarget;
|
| 5 |
import com.spice.profitmandi.dao.entity.inventory.RbmAchievements;
|
14 |
import com.spice.profitmandi.dao.entity.inventory.RbmAchievements;
|
| 6 |
import com.spice.profitmandi.dao.entity.inventory.RbmTargets;
|
15 |
import com.spice.profitmandi.dao.entity.inventory.RbmTargets;
|
| - |
|
16 |
import com.spice.profitmandi.dao.enumuration.auth.CollectionRemark;
|
| - |
|
17 |
import com.spice.profitmandi.dao.enumuration.cs.EscalationType;
|
| 7 |
import com.spice.profitmandi.dao.model.*;
|
18 |
import com.spice.profitmandi.dao.model.*;
|
| - |
|
19 |
import com.spice.profitmandi.dao.repository.auth.AuthRepository;
|
| - |
|
20 |
import com.spice.profitmandi.dao.repository.auth.PartnerCollectionRemarkRepository;
|
| - |
|
21 |
import com.spice.profitmandi.dao.repository.auth.RbmCallSequenceLogRepository;
|
| 8 |
import com.spice.profitmandi.dao.repository.catalog.RbmAchievementsRepository;
|
22 |
import com.spice.profitmandi.dao.repository.catalog.RbmAchievementsRepository;
|
| 9 |
import com.spice.profitmandi.dao.repository.catalog.RbmTargetsRepository;
|
23 |
import com.spice.profitmandi.dao.repository.catalog.RbmTargetsRepository;
|
| - |
|
24 |
import com.spice.profitmandi.dao.repository.cs.CsService;
|
| - |
|
25 |
import com.spice.profitmandi.dao.repository.cs.PositionRepository;
|
| - |
|
26 |
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
|
| 10 |
import com.spice.profitmandi.dao.repository.fofo.MonthlyTargetRepository;
|
27 |
import com.spice.profitmandi.dao.repository.fofo.MonthlyTargetRepository;
|
| 11 |
import com.spice.profitmandi.dao.repository.logistics.PublicHolidaysRepository;
|
28 |
import com.spice.profitmandi.dao.repository.logistics.PublicHolidaysRepository;
|
| - |
|
29 |
import com.spice.profitmandi.dao.repository.transaction.LoanRepository;
|
| 12 |
import com.spice.profitmandi.dao.repository.transaction.OrderRepository;
|
30 |
import com.spice.profitmandi.dao.repository.transaction.OrderRepository;
|
| - |
|
31 |
import com.spice.profitmandi.service.user.RetailerService;
|
| 13 |
import org.apache.logging.log4j.LogManager;
|
32 |
import org.apache.logging.log4j.LogManager;
|
| 14 |
import org.apache.logging.log4j.Logger;
|
33 |
import org.apache.logging.log4j.Logger;
|
| 15 |
import org.hibernate.Session;
|
34 |
import org.hibernate.Session;
|
| 16 |
import org.hibernate.SessionFactory;
|
35 |
import org.hibernate.SessionFactory;
|
| 17 |
import org.hibernate.query.NativeQuery;
|
36 |
import org.hibernate.query.NativeQuery;
|
| 18 |
import org.springframework.beans.factory.annotation.Autowired;
|
37 |
import org.springframework.beans.factory.annotation.Autowired;
|
| 19 |
import org.springframework.stereotype.Component;
|
38 |
import org.springframework.stereotype.Component;
|
| 20 |
|
39 |
|
| 21 |
import javax.persistence.TypedQuery;
|
40 |
import javax.persistence.TypedQuery;
|
| 22 |
import java.time.*;
|
41 |
import java.time.*;
|
| - |
|
42 |
import java.time.format.DateTimeFormatter;
|
| 23 |
import java.time.temporal.ChronoUnit;
|
43 |
import java.time.temporal.ChronoUnit;
|
| 24 |
import java.util.ArrayList;
|
- |
|
| 25 |
import java.util.List;
|
- |
|
| 26 |
import java.util.Map;
|
44 |
import java.util.*;
|
| 27 |
import java.util.Optional;
|
- |
|
| 28 |
import java.util.stream.Collectors;
|
45 |
import java.util.stream.Collectors;
|
| 29 |
|
46 |
|
| 30 |
@Component
|
47 |
@Component
|
| 31 |
public class RbmTargetServiceImpl implements RbmTargetService {
|
48 |
public class RbmTargetServiceImpl implements RbmTargetService {
|
| 32 |
private static final Logger LOGGER = LogManager.getLogger(RbmTargetServiceImpl.class);
|
49 |
private static final Logger LOGGER = LogManager.getLogger(RbmTargetServiceImpl.class);
|
| Line 44... |
Line 61... |
| 44 |
MonthlyTargetRepository monthlyTargetRepository;
|
61 |
MonthlyTargetRepository monthlyTargetRepository;
|
| 45 |
|
62 |
|
| 46 |
@Autowired
|
63 |
@Autowired
|
| 47 |
PublicHolidaysRepository publicHolidaysRepository;
|
64 |
PublicHolidaysRepository publicHolidaysRepository;
|
| 48 |
|
65 |
|
| - |
|
66 |
@Autowired
|
| - |
|
67 |
RetailerService retailerService;
|
| - |
|
68 |
|
| 49 |
@Override
|
69 |
@Override
|
| 50 |
public List<WarehouseRbmTargetModel> getWarehouseWiseRbmMonthlyTarget() {
|
70 |
public List<WarehouseRbmTargetModel> getWarehouseWiseRbmMonthlyTarget() {
|
| 51 |
Session session = sessionFactory.getCurrentSession();
|
71 |
Session session = sessionFactory.getCurrentSession();
|
| 52 |
final TypedQuery<WarehouseRbmTargetModel> typedQuerySimilar = session.createNamedQuery("RbmTarget.getWarehouseWiseMonthlyTarget", WarehouseRbmTargetModel.class);
|
72 |
final TypedQuery<WarehouseRbmTargetModel> typedQuerySimilar = session.createNamedQuery("RbmTarget.getWarehouseWiseMonthlyTarget", WarehouseRbmTargetModel.class);
|
| 53 |
|
73 |
|
| Line 543... |
Line 563... |
| 543 |
LOGGER.info("publicHolidays {}", publicHolidays);
|
563 |
LOGGER.info("publicHolidays {}", publicHolidays);
|
| 544 |
|
564 |
|
| 545 |
return remainingDays;
|
565 |
return remainingDays;
|
| 546 |
}
|
566 |
}
|
| 547 |
|
567 |
|
| - |
|
568 |
@Autowired
|
| - |
|
569 |
PositionRepository positionRepository;
|
| - |
|
570 |
|
| - |
|
571 |
@Autowired
|
| - |
|
572 |
CsService csService;
|
| - |
|
573 |
|
| - |
|
574 |
@Autowired
|
| - |
|
575 |
FofoStoreRepository fofoStoreRepository;
|
| - |
|
576 |
|
| - |
|
577 |
@Autowired
|
| - |
|
578 |
AuthRepository authRepository;
|
| - |
|
579 |
|
| - |
|
580 |
@Autowired
|
| - |
|
581 |
LoanRepository loanRepository;
|
| - |
|
582 |
|
| - |
|
583 |
@Autowired
|
| - |
|
584 |
PartnerCollectionService partnerCollectionService;
|
| - |
|
585 |
|
| - |
|
586 |
@Autowired
|
| - |
|
587 |
PartnerCollectionRemarkRepository partnerCollectionRemarkRepository;
|
| - |
|
588 |
|
| - |
|
589 |
@Autowired
|
| - |
|
590 |
RbmCallSequenceLogRepository rbmCallSequenceLogRepository;
|
| - |
|
591 |
|
| - |
|
592 |
@Autowired
|
| - |
|
593 |
com.spice.profitmandi.dao.repository.cs.TicketRepository ticketRepository;
|
| - |
|
594 |
|
| - |
|
595 |
@Override
|
| - |
|
596 |
public List<RbmCallTargetModel> getRbmCallTargetModels() throws Exception {
|
| - |
|
597 |
long methodStart = System.currentTimeMillis();
|
| - |
|
598 |
List<RbmCallTargetModel> rbmCallTargetModels = new ArrayList<>();
|
| - |
|
599 |
|
| - |
|
600 |
// Get all RBM positions (L1 and L2)
|
| - |
|
601 |
long start = System.currentTimeMillis();
|
| - |
|
602 |
List<Position> allRbmPositions = positionRepository
|
| - |
|
603 |
.selectPositionByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_RBM).stream()
|
| - |
|
604 |
.filter(x -> Arrays.asList(EscalationType.L1, EscalationType.L2).contains(x.getEscalationType()))
|
| - |
|
605 |
.collect(Collectors.toList());
|
| - |
|
606 |
|
| - |
|
607 |
// Separate L1 and L2 auth IDs
|
| - |
|
608 |
List<Integer> l1AuthIds = allRbmPositions.stream()
|
| - |
|
609 |
.filter(p -> EscalationType.L1.equals(p.getEscalationType()))
|
| - |
|
610 |
.map(Position::getAuthUserId).distinct().collect(Collectors.toList());
|
| - |
|
611 |
List<Integer> l2AuthIds = allRbmPositions.stream()
|
| - |
|
612 |
.filter(p -> EscalationType.L2.equals(p.getEscalationType()))
|
| - |
|
613 |
.map(Position::getAuthUserId).distinct().collect(Collectors.toList());
|
| - |
|
614 |
|
| - |
|
615 |
// Union of all auth IDs for batch fetching
|
| - |
|
616 |
List<Integer> rbmPositionsAuthIds = allRbmPositions.stream()
|
| - |
|
617 |
.map(Position::getAuthUserId).distinct().collect(Collectors.toList());
|
| - |
|
618 |
LOGGER.info("RBM Call Target - RBM positions fetch: {}ms, L1: {}, L2: {}", System.currentTimeMillis() - start, l1AuthIds.size(), l2AuthIds.size());
|
| - |
|
619 |
|
| - |
|
620 |
start = System.currentTimeMillis();
|
| - |
|
621 |
Map<String, Set<Integer>> storeGuyMap = csService.getAuthUserPartnerIdMapping();
|
| - |
|
622 |
LOGGER.info("RBM Call Target - StoreGuyMap fetch: {}ms", System.currentTimeMillis() - start);
|
| - |
|
623 |
|
| - |
|
624 |
LocalDateTime startDate = LocalDate.now().atStartOfDay();
|
| - |
|
625 |
LocalDate firstOfMonth = LocalDate.now().withDayOfMonth(1);
|
| - |
|
626 |
LocalDate endOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).plusDays(1);
|
| - |
|
627 |
|
| - |
|
628 |
// Get auth user map
|
| - |
|
629 |
start = System.currentTimeMillis();
|
| - |
|
630 |
Map<Integer, AuthUser> authUserMap = authRepository.selectByIds(rbmPositionsAuthIds).stream()
|
| - |
|
631 |
.collect(Collectors.toMap(AuthUser::getId, au -> au));
|
| - |
|
632 |
LOGGER.info("RBM Call Target - AuthUser fetch: {}ms", System.currentTimeMillis() - start);
|
| - |
|
633 |
|
| - |
|
634 |
// Batch fetch positions by auth IDs (to check if RBM is L1)
|
| - |
|
635 |
start = System.currentTimeMillis();
|
| - |
|
636 |
Map<Integer, List<Position>> positionsByAuthId = positionRepository.selectPositionByAuthIds(rbmPositionsAuthIds).stream()
|
| - |
|
637 |
.collect(Collectors.groupingBy(Position::getAuthUserId));
|
| - |
|
638 |
LOGGER.info("RBM Call Target - Positions by AuthId fetch: {}ms", System.currentTimeMillis() - start);
|
| - |
|
639 |
|
| - |
|
640 |
// Get all fofo IDs for all RBMs
|
| - |
|
641 |
Set<Integer> allFofoIds = new HashSet<>();
|
| - |
|
642 |
Map<Integer, List<Integer>> rbmToFofoIdsMap = new HashMap<>();
|
| - |
|
643 |
for (int rbmAuthId : rbmPositionsAuthIds) {
|
| - |
|
644 |
AuthUser au = authUserMap.get(rbmAuthId);
|
| - |
|
645 |
if (au != null && storeGuyMap.containsKey(au.getEmailId())) {
|
| - |
|
646 |
List<Integer> fofoIds = new ArrayList<>(storeGuyMap.get(au.getEmailId()));
|
| - |
|
647 |
allFofoIds.addAll(fofoIds);
|
| - |
|
648 |
rbmToFofoIdsMap.put(rbmAuthId, fofoIds);
|
| - |
|
649 |
}
|
| - |
|
650 |
}
|
| - |
|
651 |
// Fetch escalated tickets for L2 early so their fofoIds are included in batch fetches
|
| - |
|
652 |
List<Ticket> escalatedTickets = Collections.emptyList();
|
| - |
|
653 |
Map<Integer, List<Integer>> l2AuthIdToFofoIds = new HashMap<>();
|
| - |
|
654 |
if (!l2AuthIds.isEmpty()) {
|
| - |
|
655 |
start = System.currentTimeMillis();
|
| - |
|
656 |
escalatedTickets = ticketRepository.selectOpenEscalatedTicketsByAuthIds(l2AuthIds);
|
| - |
|
657 |
LOGGER.info("RBM Call Target - Escalated tickets fetch: {}ms, count: {}", System.currentTimeMillis() - start, escalatedTickets.size());
|
| - |
|
658 |
|
| - |
|
659 |
for (int l2AuthId : l2AuthIds) {
|
| - |
|
660 |
List<Integer> l2FofoIds = escalatedTickets.stream()
|
| - |
|
661 |
.filter(t -> t.getL2AuthUser() == l2AuthId
|
| - |
|
662 |
|| t.getL3AuthUser() == l2AuthId
|
| - |
|
663 |
|| t.getL4AuthUser() == l2AuthId
|
| - |
|
664 |
|| t.getL5AuthUser() == l2AuthId)
|
| - |
|
665 |
.map(Ticket::getFofoId)
|
| - |
|
666 |
.distinct()
|
| - |
|
667 |
.collect(Collectors.toList());
|
| - |
|
668 |
l2AuthIdToFofoIds.put(l2AuthId, l2FofoIds);
|
| - |
|
669 |
allFofoIds.addAll(l2FofoIds);
|
| - |
|
670 |
}
|
| - |
|
671 |
}
|
| - |
|
672 |
LOGGER.info("RBM Call Target - Total fofo IDs to process: {}", allFofoIds.size());
|
| - |
|
673 |
|
| - |
|
674 |
// Get only needed fofo stores (OPTIMIZED - was fetching ALL stores before)
|
| - |
|
675 |
start = System.currentTimeMillis();
|
| - |
|
676 |
Map<Integer, FofoStore> fofoStoresMap = new HashMap<>();
|
| - |
|
677 |
if (!allFofoIds.isEmpty()) {
|
| - |
|
678 |
try {
|
| - |
|
679 |
fofoStoresMap = fofoStoreRepository.selectByRetailerIds(new ArrayList<>(allFofoIds)).stream()
|
| - |
|
680 |
.collect(Collectors.toMap(FofoStore::getId, x -> x, (a, b) -> a));
|
| - |
|
681 |
} catch (ProfitMandiBusinessException e) {
|
| - |
|
682 |
LOGGER.error("Error fetching fofo stores", e);
|
| - |
|
683 |
}
|
| - |
|
684 |
}
|
| - |
|
685 |
LOGGER.info("RBM Call Target - FofoStores fetch (only needed): {}ms, count: {}", System.currentTimeMillis() - start, fofoStoresMap.size());
|
| - |
|
686 |
|
| - |
|
687 |
// Batch fetch max remark ids for all fofoIds (for escalation filtering)
|
| - |
|
688 |
start = System.currentTimeMillis();
|
| - |
|
689 |
Map<Integer, PartnerCollectionRemark> allPartnerCollectionRemarks = new HashMap<>();
|
| - |
|
690 |
if (!allFofoIds.isEmpty()) {
|
| - |
|
691 |
List<Integer> allRemarkIds = partnerCollectionRemarkRepository.selectMaxRemarkId(new ArrayList<>(allFofoIds));
|
| - |
|
692 |
if (!allRemarkIds.isEmpty()) {
|
| - |
|
693 |
allPartnerCollectionRemarks = partnerCollectionRemarkRepository.selectByIds(allRemarkIds).stream()
|
| - |
|
694 |
.collect(Collectors.toMap(PartnerCollectionRemark::getFofoId, x -> x, (a, b) -> a));
|
| - |
|
695 |
}
|
| - |
|
696 |
}
|
| - |
|
697 |
LOGGER.info("RBM Call Target - PartnerCollectionRemarks fetch: {}ms", System.currentTimeMillis() - start);
|
| - |
|
698 |
|
| - |
|
699 |
// Batch fetch collection RANK map for all fofoIds (OPTIMIZED - only fetches rank, not full model)
|
| - |
|
700 |
start = System.currentTimeMillis();
|
| - |
|
701 |
Map<Integer, Integer> allCollectionRankMap = new HashMap<>();
|
| - |
|
702 |
if (!allFofoIds.isEmpty()) {
|
| - |
|
703 |
try {
|
| - |
|
704 |
allCollectionRankMap = partnerCollectionService.getCollectionRankMap(new ArrayList<>(allFofoIds), startDate);
|
| - |
|
705 |
} catch (ProfitMandiBusinessException e) {
|
| - |
|
706 |
LOGGER.error("Error fetching collection rank map for all fofoIds", e);
|
| - |
|
707 |
}
|
| - |
|
708 |
}
|
| - |
|
709 |
LOGGER.info("RBM Call Target - CollectionRankMap fetch (OPTIMIZED): {}ms", System.currentTimeMillis() - start);
|
| - |
|
710 |
|
| - |
|
711 |
// Get MTD billing data for zero billing calculation
|
| - |
|
712 |
start = System.currentTimeMillis();
|
| - |
|
713 |
List<RbmWeeklyBillingModel> mtdBillingData = getWeeklyBillingDataForMonth(firstOfMonth, endOfMonth);
|
| - |
|
714 |
Set<Integer> allMtdBilledFofoIds = mtdBillingData.stream()
|
| - |
|
715 |
.filter(RbmWeeklyBillingModel::isMtdBilled)
|
| - |
|
716 |
.map(RbmWeeklyBillingModel::getFofoId)
|
| - |
|
717 |
.collect(Collectors.toSet());
|
| - |
|
718 |
LOGGER.info("RBM Call Target - MTD Billing fetch: {}ms", System.currentTimeMillis() - start);
|
| - |
|
719 |
|
| - |
|
720 |
// Batch fetch today's remarks for all auth IDs (to calculate Value Achieved)
|
| - |
|
721 |
start = System.currentTimeMillis();
|
| - |
|
722 |
Map<Integer, List<PartnerCollectionRemark>> remarksByAuthId = partnerCollectionRemarkRepository
|
| - |
|
723 |
.selectAllByAuthIdsOnDate(rbmPositionsAuthIds, LocalDate.now()).stream()
|
| - |
|
724 |
.collect(Collectors.groupingBy(PartnerCollectionRemark::getAuthId));
|
| - |
|
725 |
LOGGER.info("RBM Call Target - Today Remarks fetch: {}ms", System.currentTimeMillis() - start);
|
| - |
|
726 |
|
| - |
|
727 |
// Batch fetch today's out-of-sequence logs for all RBMs
|
| - |
|
728 |
start = System.currentTimeMillis();
|
| - |
|
729 |
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
|
| - |
|
730 |
LocalDateTime todayEnd = LocalDate.now().plusDays(1).atStartOfDay();
|
| - |
|
731 |
List<RbmCallSequenceLog> outOfSequenceLogs = rbmCallSequenceLogRepository.selectOutOfSequenceByDateRange(todayStart, todayEnd);
|
| - |
|
732 |
Map<Integer, Long> outOfSequenceCountByAuthId = outOfSequenceLogs.stream()
|
| - |
|
733 |
.collect(Collectors.groupingBy(RbmCallSequenceLog::getAuthId, Collectors.counting()));
|
| - |
|
734 |
LOGGER.info("RBM Call Target - Out of Sequence fetch: {}ms", System.currentTimeMillis() - start);
|
| - |
|
735 |
|
| - |
|
736 |
// Process L1 RBMs (existing logic)
|
| - |
|
737 |
for (int rbmAuthId : l1AuthIds) {
|
| - |
|
738 |
AuthUser authUser = authUserMap.get(rbmAuthId);
|
| - |
|
739 |
if (authUser == null || !storeGuyMap.containsKey(authUser.getEmailId())) {
|
| - |
|
740 |
continue;
|
| - |
|
741 |
}
|
| - |
|
742 |
|
| - |
|
743 |
List<Integer> fofoIdList = rbmToFofoIdsMap.getOrDefault(rbmAuthId, Collections.emptyList());
|
| - |
|
744 |
|
| - |
|
745 |
// Check if RBM is L1 (same logic as getSummaryModel)
|
| - |
|
746 |
List<Position> positions = positionsByAuthId.getOrDefault(authUser.getId(), Collections.emptyList());
|
| - |
|
747 |
boolean isRBMAndL1 = positions.stream()
|
| - |
|
748 |
.anyMatch(position ->
|
| - |
|
749 |
ProfitMandiConstants.TICKET_CATEGORY_RBM == position.getCategoryId()
|
| - |
|
750 |
&& EscalationType.L1.equals(position.getEscalationType()));
|
| - |
|
751 |
|
| - |
|
752 |
// Filter escalated partners for L1 RBMs (same logic as getSummaryModel)
|
| - |
|
753 |
List<Integer> fofoIds = fofoIdList;
|
| - |
|
754 |
if (isRBMAndL1) {
|
| - |
|
755 |
Map<Integer, PartnerCollectionRemark> partnerCollectionRemarks = new HashMap<>();
|
| - |
|
756 |
for (Integer fofoId : fofoIdList) {
|
| - |
|
757 |
if (allPartnerCollectionRemarks.containsKey(fofoId)) {
|
| - |
|
758 |
partnerCollectionRemarks.put(fofoId, allPartnerCollectionRemarks.get(fofoId));
|
| - |
|
759 |
}
|
| - |
|
760 |
}
|
| - |
|
761 |
fofoIds = partnerCollectionRemarks.entrySet().stream()
|
| - |
|
762 |
.filter(entry -> {
|
| - |
|
763 |
PartnerCollectionRemark pcrMap = entry.getValue();
|
| - |
|
764 |
return !(CollectionRemark.RBM_L2_ESCALATION.equals(pcrMap.getRemark())
|
| - |
|
765 |
|| CollectionRemark.SALES_ESCALATION.equals(pcrMap.getRemark()));
|
| - |
|
766 |
})
|
| - |
|
767 |
.map(Map.Entry::getKey)
|
| - |
|
768 |
.collect(Collectors.toList());
|
| - |
|
769 |
}
|
| - |
|
770 |
|
| - |
|
771 |
// Filter to only external, ACTIVE stores with collection plan (like today_target Active section)
|
| - |
|
772 |
Map<Integer, Integer> finalAllCollectionRankMap = allCollectionRankMap;
|
| - |
|
773 |
Map<Integer, FofoStore> finalFofoStoresMap = fofoStoresMap;
|
| - |
|
774 |
List<Integer> validFofoIds = fofoIds.stream()
|
| - |
|
775 |
.filter(fofoId -> {
|
| - |
|
776 |
FofoStore store = finalFofoStoresMap.get(fofoId);
|
| - |
|
777 |
if (store == null || store.isInternal()) {
|
| - |
|
778 |
return false;
|
| - |
|
779 |
}
|
| - |
|
780 |
// Only include ACTIVE partners (not Low Sale, not Disputed, not Billing Pending)
|
| - |
|
781 |
if (!ActivationType.ACTIVE.equals(store.getActivationType())) {
|
| - |
|
782 |
return false;
|
| - |
|
783 |
}
|
| - |
|
784 |
// Only include partners who have a collection plan (to match today_target Active section)
|
| - |
|
785 |
return finalAllCollectionRankMap.containsKey(fofoId);
|
| - |
|
786 |
})
|
| - |
|
787 |
.collect(Collectors.toList());
|
| - |
|
788 |
|
| - |
|
789 |
if (validFofoIds.isEmpty()) {
|
| - |
|
790 |
continue;
|
| - |
|
791 |
}
|
| - |
|
792 |
|
| - |
|
793 |
RbmCallTargetModel targetModel = new RbmCallTargetModel();
|
| - |
|
794 |
targetModel.setAuthId(rbmAuthId);
|
| - |
|
795 |
targetModel.setRbmName(authUser.getFullName());
|
| - |
|
796 |
targetModel.setPartnerCount(validFofoIds.size());
|
| - |
|
797 |
|
| - |
|
798 |
// Categorize each partner - each partner belongs to ONE category only
|
| - |
|
799 |
// Priority: PlanToday > CarryForward > Untouched > ZeroBilling > FuturePlan > Normal
|
| - |
|
800 |
Set<Integer> planTodayPartners = new HashSet<>();
|
| - |
|
801 |
Set<Integer> carryForwardPartners = new HashSet<>();
|
| - |
|
802 |
Set<Integer> untouchedPartners = new HashSet<>();
|
| - |
|
803 |
Set<Integer> zeroBillingPartners = new HashSet<>();
|
| - |
|
804 |
Set<Integer> futurePlanPartners = new HashSet<>();
|
| - |
|
805 |
Set<Integer> normalPartners = new HashSet<>();
|
| - |
|
806 |
|
| - |
|
807 |
for (Integer fofoId : validFofoIds) {
|
| - |
|
808 |
// Get collection plan rank (from optimized rank map)
|
| - |
|
809 |
int rank = allCollectionRankMap.getOrDefault(fofoId, 5); // default to Normal if no plan
|
| - |
|
810 |
|
| - |
|
811 |
// Check if partner has zero billing in MTD
|
| - |
|
812 |
boolean hasZeroBilling = !allMtdBilledFofoIds.contains(fofoId);
|
| - |
|
813 |
|
| - |
|
814 |
// Assign to category based on priority
|
| - |
|
815 |
if (rank == 1) {
|
| - |
|
816 |
planTodayPartners.add(fofoId);
|
| - |
|
817 |
} else if (rank == 2) {
|
| - |
|
818 |
carryForwardPartners.add(fofoId);
|
| - |
|
819 |
} else if (rank == 3) {
|
| - |
|
820 |
untouchedPartners.add(fofoId);
|
| - |
|
821 |
} else if (hasZeroBilling) {
|
| - |
|
822 |
zeroBillingPartners.add(fofoId);
|
| - |
|
823 |
} else if (rank == 4) {
|
| - |
|
824 |
futurePlanPartners.add(fofoId);
|
| - |
|
825 |
} else {
|
| - |
|
826 |
normalPartners.add(fofoId);
|
| - |
|
827 |
}
|
| - |
|
828 |
}
|
| - |
|
829 |
|
| - |
|
830 |
// Set counts
|
| - |
|
831 |
targetModel.setCreditCollection(0); // Credit collection is handled in separate list
|
| - |
|
832 |
targetModel.setPlanToday(planTodayPartners.size());
|
| - |
|
833 |
targetModel.setCarryForward(carryForwardPartners.size());
|
| - |
|
834 |
targetModel.setUntouched(untouchedPartners.size());
|
| - |
|
835 |
targetModel.setZeroBilling(zeroBillingPartners.size());
|
| - |
|
836 |
targetModel.setFuturePlan(futurePlanPartners.size());
|
| - |
|
837 |
targetModel.setNormal(normalPartners.size());
|
| - |
|
838 |
|
| - |
|
839 |
// Today Target = PlanToday + CarryForward + ZeroBilling + Untouched
|
| - |
|
840 |
// These are mutually exclusive now, so we can sum them
|
| - |
|
841 |
long todayTarget = planTodayPartners.size() +
|
| - |
|
842 |
carryForwardPartners.size() + zeroBillingPartners.size() + untouchedPartners.size();
|
| - |
|
843 |
targetModel.setTodayTargetOfCall(todayTarget);
|
| - |
|
844 |
|
| - |
|
845 |
// Create set of partners in Today Target categories
|
| - |
|
846 |
Set<Integer> todayTargetPartners = new HashSet<>();
|
| - |
|
847 |
todayTargetPartners.addAll(planTodayPartners);
|
| - |
|
848 |
todayTargetPartners.addAll(carryForwardPartners);
|
| - |
|
849 |
todayTargetPartners.addAll(zeroBillingPartners);
|
| - |
|
850 |
todayTargetPartners.addAll(untouchedPartners);
|
| - |
|
851 |
|
| - |
|
852 |
// Value Achieved = Partners from Today Target that have been contacted today (have remark today)
|
| - |
|
853 |
List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
| - |
|
854 |
long valueAchieved = todayRemarks.stream()
|
| - |
|
855 |
.map(PartnerCollectionRemark::getFofoId)
|
| - |
|
856 |
.filter(todayTargetPartners::contains)
|
| - |
|
857 |
.distinct()
|
| - |
|
858 |
.count();
|
| - |
|
859 |
targetModel.setValueTargetAchieved(valueAchieved);
|
| - |
|
860 |
|
| - |
|
861 |
// Moved to Future = Partners in Future Plan category who have a remark today
|
| - |
|
862 |
// These are partners who were contacted today but moved to a future date
|
| - |
|
863 |
Set<Integer> todayRemarkedFofoIds = todayRemarks.stream()
|
| - |
|
864 |
.map(PartnerCollectionRemark::getFofoId)
|
| - |
|
865 |
.collect(Collectors.toSet());
|
| - |
|
866 |
long movedToFuture = futurePlanPartners.stream()
|
| - |
|
867 |
.filter(todayRemarkedFofoIds::contains)
|
| - |
|
868 |
.count();
|
| - |
|
869 |
targetModel.setMovedToFuture(movedToFuture);
|
| - |
|
870 |
|
| - |
|
871 |
// Set out of sequence count for this RBM
|
| - |
|
872 |
targetModel.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(rbmAuthId, 0L));
|
| - |
|
873 |
|
| - |
|
874 |
rbmCallTargetModels.add(targetModel);
|
| - |
|
875 |
}
|
| - |
|
876 |
|
| - |
|
877 |
// Process L2 RBMs (escalated ticket logic with categorization)
|
| - |
|
878 |
for (int l2AuthId : l2AuthIds) {
|
| - |
|
879 |
AuthUser authUser = authUserMap.get(l2AuthId);
|
| - |
|
880 |
if (authUser == null) {
|
| - |
|
881 |
continue;
|
| - |
|
882 |
}
|
| - |
|
883 |
|
| - |
|
884 |
List<Integer> l2FofoIdList = l2AuthIdToFofoIds.getOrDefault(l2AuthId, Collections.emptyList());
|
| - |
|
885 |
if (l2FofoIdList.isEmpty()) {
|
| - |
|
886 |
continue;
|
| - |
|
887 |
}
|
| - |
|
888 |
|
| - |
|
889 |
// Filter to only external, ACTIVE stores with collection plan
|
| - |
|
890 |
Map<Integer, Integer> finalAllCollectionRankMap2 = allCollectionRankMap;
|
| - |
|
891 |
Map<Integer, FofoStore> finalFofoStoresMap2 = fofoStoresMap;
|
| - |
|
892 |
List<Integer> validL2FofoIds = l2FofoIdList.stream()
|
| - |
|
893 |
.filter(fofoId -> {
|
| - |
|
894 |
FofoStore store = finalFofoStoresMap2.get(fofoId);
|
| - |
|
895 |
if (store == null || store.isInternal()) {
|
| - |
|
896 |
return false;
|
| - |
|
897 |
}
|
| - |
|
898 |
if (!ActivationType.ACTIVE.equals(store.getActivationType())) {
|
| - |
|
899 |
return false;
|
| - |
|
900 |
}
|
| - |
|
901 |
return finalAllCollectionRankMap2.containsKey(fofoId);
|
| - |
|
902 |
})
|
| - |
|
903 |
.collect(Collectors.toList());
|
| - |
|
904 |
|
| - |
|
905 |
RbmCallTargetModel l2Model = new RbmCallTargetModel();
|
| - |
|
906 |
l2Model.setAuthId(l2AuthId);
|
| - |
|
907 |
l2Model.setRbmName(authUser.getFullName() + " (L2)");
|
| - |
|
908 |
l2Model.setL2Position(true);
|
| - |
|
909 |
l2Model.setL2CallingList(l2FofoIdList.size());
|
| - |
|
910 |
// Partner count = total assigned partners (same as L1 source)
|
| - |
|
911 |
List<Integer> l2AssignedFofoIds = rbmToFofoIdsMap.getOrDefault(l2AuthId, Collections.emptyList());
|
| - |
|
912 |
l2Model.setPartnerCount(l2AssignedFofoIds.size());
|
| - |
|
913 |
|
| - |
|
914 |
if (!validL2FofoIds.isEmpty()) {
|
| - |
|
915 |
// Categorize using same logic as L1
|
| - |
|
916 |
Set<Integer> l2PlanToday = new HashSet<>();
|
| - |
|
917 |
Set<Integer> l2CarryForward = new HashSet<>();
|
| - |
|
918 |
Set<Integer> l2Untouched = new HashSet<>();
|
| - |
|
919 |
Set<Integer> l2ZeroBilling = new HashSet<>();
|
| - |
|
920 |
Set<Integer> l2FuturePlan = new HashSet<>();
|
| - |
|
921 |
Set<Integer> l2Normal = new HashSet<>();
|
| - |
|
922 |
|
| - |
|
923 |
for (Integer fofoId : validL2FofoIds) {
|
| - |
|
924 |
int rank = allCollectionRankMap.getOrDefault(fofoId, 5);
|
| - |
|
925 |
boolean hasZeroBilling = !allMtdBilledFofoIds.contains(fofoId);
|
| - |
|
926 |
|
| - |
|
927 |
if (rank == 1) {
|
| - |
|
928 |
l2PlanToday.add(fofoId);
|
| - |
|
929 |
} else if (rank == 2) {
|
| - |
|
930 |
l2CarryForward.add(fofoId);
|
| - |
|
931 |
} else if (rank == 3) {
|
| - |
|
932 |
l2Untouched.add(fofoId);
|
| - |
|
933 |
} else if (hasZeroBilling) {
|
| - |
|
934 |
l2ZeroBilling.add(fofoId);
|
| - |
|
935 |
} else if (rank == 4) {
|
| - |
|
936 |
l2FuturePlan.add(fofoId);
|
| - |
|
937 |
} else {
|
| - |
|
938 |
l2Normal.add(fofoId);
|
| - |
|
939 |
}
|
| - |
|
940 |
}
|
| - |
|
941 |
|
| - |
|
942 |
l2Model.setPlanToday(l2PlanToday.size());
|
| - |
|
943 |
l2Model.setCarryForward(l2CarryForward.size());
|
| - |
|
944 |
l2Model.setUntouched(l2Untouched.size());
|
| - |
|
945 |
l2Model.setZeroBilling(l2ZeroBilling.size());
|
| - |
|
946 |
l2Model.setFuturePlan(l2FuturePlan.size());
|
| - |
|
947 |
l2Model.setNormal(l2Normal.size());
|
| - |
|
948 |
|
| - |
|
949 |
long l2TodayTarget = l2PlanToday.size() + l2CarryForward.size()
|
| - |
|
950 |
+ l2ZeroBilling.size() + l2Untouched.size();
|
| - |
|
951 |
l2Model.setTodayTargetOfCall(l2TodayTarget);
|
| - |
|
952 |
|
| - |
|
953 |
// Value Achieved
|
| - |
|
954 |
Set<Integer> l2TodayTargetPartners = new HashSet<>();
|
| - |
|
955 |
l2TodayTargetPartners.addAll(l2PlanToday);
|
| - |
|
956 |
l2TodayTargetPartners.addAll(l2CarryForward);
|
| - |
|
957 |
l2TodayTargetPartners.addAll(l2ZeroBilling);
|
| - |
|
958 |
l2TodayTargetPartners.addAll(l2Untouched);
|
| - |
|
959 |
|
| - |
|
960 |
List<PartnerCollectionRemark> l2TodayRemarks = remarksByAuthId.getOrDefault(l2AuthId, Collections.emptyList());
|
| - |
|
961 |
long l2ValueAchieved = l2TodayRemarks.stream()
|
| - |
|
962 |
.map(PartnerCollectionRemark::getFofoId)
|
| - |
|
963 |
.filter(l2TodayTargetPartners::contains)
|
| - |
|
964 |
.distinct()
|
| - |
|
965 |
.count();
|
| - |
|
966 |
l2Model.setValueTargetAchieved(l2ValueAchieved);
|
| - |
|
967 |
|
| - |
|
968 |
// Moved to Future
|
| - |
|
969 |
Set<Integer> l2TodayRemarkedFofoIds = l2TodayRemarks.stream()
|
| - |
|
970 |
.map(PartnerCollectionRemark::getFofoId)
|
| - |
|
971 |
.collect(Collectors.toSet());
|
| - |
|
972 |
long l2MovedToFuture = l2FuturePlan.stream()
|
| - |
|
973 |
.filter(l2TodayRemarkedFofoIds::contains)
|
| - |
|
974 |
.count();
|
| - |
|
975 |
l2Model.setMovedToFuture(l2MovedToFuture);
|
| - |
|
976 |
}
|
| - |
|
977 |
|
| - |
|
978 |
l2Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l2AuthId, 0L));
|
| - |
|
979 |
rbmCallTargetModels.add(l2Model);
|
| - |
|
980 |
}
|
| - |
|
981 |
|
| - |
|
982 |
// Group L1 under their L2 manager using authUser.managerId
|
| - |
|
983 |
Map<Integer, RbmCallTargetModel> l2ModelsByAuthId = new HashMap<>();
|
| - |
|
984 |
Map<Integer, RbmCallTargetModel> l1ModelsByAuthId = new HashMap<>();
|
| - |
|
985 |
for (RbmCallTargetModel m : rbmCallTargetModels) {
|
| - |
|
986 |
if (m.isL2Position()) {
|
| - |
|
987 |
l2ModelsByAuthId.put(m.getAuthId(), m);
|
| - |
|
988 |
} else {
|
| - |
|
989 |
l1ModelsByAuthId.put(m.getAuthId(), m);
|
| - |
|
990 |
}
|
| - |
|
991 |
}
|
| - |
|
992 |
|
| - |
|
993 |
// Build L2 -> L1 team map using managerId from AuthUser
|
| - |
|
994 |
Map<Integer, List<RbmCallTargetModel>> l2TeamMap = new LinkedHashMap<>();
|
| - |
|
995 |
for (RbmCallTargetModel l2Model : l2ModelsByAuthId.values()) {
|
| - |
|
996 |
l2TeamMap.put(l2Model.getAuthId(), new ArrayList<>());
|
| - |
|
997 |
}
|
| - |
|
998 |
|
| - |
|
999 |
Set<Integer> addedL1AuthIds = new HashSet<>();
|
| - |
|
1000 |
for (RbmCallTargetModel l1Model : l1ModelsByAuthId.values()) {
|
| - |
|
1001 |
AuthUser l1User = authUserMap.get(l1Model.getAuthId());
|
| - |
|
1002 |
if (l1User != null && l2TeamMap.containsKey(l1User.getManagerId())) {
|
| - |
|
1003 |
l2TeamMap.get(l1User.getManagerId()).add(l1Model);
|
| - |
|
1004 |
addedL1AuthIds.add(l1Model.getAuthId());
|
| - |
|
1005 |
}
|
| - |
|
1006 |
}
|
| - |
|
1007 |
|
| - |
|
1008 |
// Build sorted result: L2 row, then its L1 team (sorted by name)
|
| - |
|
1009 |
List<RbmCallTargetModel> sortedModels = new ArrayList<>();
|
| - |
|
1010 |
|
| - |
|
1011 |
List<RbmCallTargetModel> l2Sorted = new ArrayList<>(l2ModelsByAuthId.values());
|
| - |
|
1012 |
l2Sorted.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
|
| - |
|
1013 |
|
| - |
|
1014 |
for (RbmCallTargetModel l2Model : l2Sorted) {
|
| - |
|
1015 |
sortedModels.add(l2Model);
|
| - |
|
1016 |
List<RbmCallTargetModel> team = l2TeamMap.getOrDefault(l2Model.getAuthId(), Collections.emptyList());
|
| - |
|
1017 |
team.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
|
| - |
|
1018 |
sortedModels.addAll(team);
|
| - |
|
1019 |
}
|
| - |
|
1020 |
|
| - |
|
1021 |
// Add any L1 RBMs not mapped to any L2 (sorted by name)
|
| - |
|
1022 |
List<RbmCallTargetModel> unmappedL1 = new ArrayList<>();
|
| - |
|
1023 |
for (RbmCallTargetModel m : l1ModelsByAuthId.values()) {
|
| - |
|
1024 |
if (!addedL1AuthIds.contains(m.getAuthId())) {
|
| - |
|
1025 |
unmappedL1.add(m);
|
| - |
|
1026 |
}
|
| - |
|
1027 |
}
|
| - |
|
1028 |
unmappedL1.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
|
| - |
|
1029 |
sortedModels.addAll(unmappedL1);
|
| - |
|
1030 |
|
| - |
|
1031 |
LOGGER.info("RBM Call Target - TOTAL TIME: {}ms, RBM count: {}", System.currentTimeMillis() - methodStart, sortedModels.size());
|
| - |
|
1032 |
return sortedModels;
|
| - |
|
1033 |
}
|
| - |
|
1034 |
|
| - |
|
1035 |
@Override
|
| - |
|
1036 |
public List<OutOfSequenceDetailModel> getOutOfSequenceDetails(int authId) {
|
| - |
|
1037 |
|
| - |
|
1038 |
LocalDate today = LocalDate.now();
|
| - |
|
1039 |
LocalDateTime start = today.atStartOfDay();
|
| - |
|
1040 |
LocalDateTime end = today.plusDays(1).atStartOfDay();
|
| - |
|
1041 |
|
| - |
|
1042 |
List<RbmCallSequenceLog> logs =
|
| - |
|
1043 |
rbmCallSequenceLogRepository.selectByAuthIdAndDateRange(authId, start, end);
|
| - |
|
1044 |
|
| - |
|
1045 |
Set<Integer> fofoIds = new HashSet<>();
|
| - |
|
1046 |
List<RbmCallSequenceLog> oosLogs = new ArrayList<>();
|
| - |
|
1047 |
|
| - |
|
1048 |
for (RbmCallSequenceLog log : logs) {
|
| - |
|
1049 |
if (log.isOutOfSequence()) {
|
| - |
|
1050 |
oosLogs.add(log);
|
| - |
|
1051 |
fofoIds.add(log.getFofoId());
|
| - |
|
1052 |
}
|
| - |
|
1053 |
}
|
| - |
|
1054 |
|
| - |
|
1055 |
if (oosLogs.isEmpty()) {
|
| - |
|
1056 |
return Collections.emptyList();
|
| - |
|
1057 |
}
|
| - |
|
1058 |
|
| - |
|
1059 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
| - |
|
1060 |
try {
|
| - |
|
1061 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
|
| - |
|
1062 |
} catch (ProfitMandiBusinessException e) {
|
| - |
|
1063 |
LOGGER.error("Error fetching fofo stores", e);
|
| - |
|
1064 |
}
|
| - |
|
1065 |
|
| - |
|
1066 |
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
|
| - |
|
1067 |
List<OutOfSequenceDetailModel> result = new ArrayList<>();
|
| - |
|
1068 |
|
| - |
|
1069 |
for (RbmCallSequenceLog log : oosLogs) {
|
| - |
|
1070 |
CustomRetailer retailer = retailerMap.get(log.getFofoId());
|
| - |
|
1071 |
String partyName = retailer != null
|
| - |
|
1072 |
? retailer.getBusinessName()
|
| - |
|
1073 |
: "Unknown (" + log.getFofoId() + ")";
|
| - |
|
1074 |
String code = retailer != null
|
| - |
|
1075 |
? retailer.getCode()
|
| - |
|
1076 |
: "-";
|
| - |
|
1077 |
|
| - |
|
1078 |
String time = log.getCreateTimestamp() != null
|
| - |
|
1079 |
? log.getCreateTimestamp().format(timeFormatter)
|
| - |
|
1080 |
: "-";
|
| - |
|
1081 |
|
| - |
|
1082 |
result.add(new OutOfSequenceDetailModel(partyName, code, time));
|
| - |
|
1083 |
}
|
| - |
|
1084 |
|
| - |
|
1085 |
return result;
|
| - |
|
1086 |
}
|
| 548 |
|
1087 |
|
| 549 |
|
1088 |
|
| 550 |
}
|
1089 |
}
|