| Line 622... |
Line 622... |
| 622 |
@Override
|
622 |
@Override
|
| 623 |
public List<RbmCallTargetModel> getRbmCallTargetModels() throws Exception {
|
623 |
public List<RbmCallTargetModel> getRbmCallTargetModels() throws Exception {
|
| 624 |
return getRbmCallTargetModels(LocalDate.now());
|
624 |
return getRbmCallTargetModels(LocalDate.now());
|
| 625 |
}
|
625 |
}
|
| 626 |
|
626 |
|
| - |
|
627 |
// Matches "No Answer", "NO_ANSWER", "no-answer", "noanswer", etc.
|
| - |
|
628 |
private static boolean isNoAnswerStatus(String status) {
|
| - |
|
629 |
if (status == null) return false;
|
| - |
|
630 |
String s = status.trim().toLowerCase().replace('_', ' ').replace('-', ' ').replaceAll("\\s+", " ");
|
| - |
|
631 |
return s.equals("no answer") || s.equals("noanswer");
|
| - |
|
632 |
}
|
| - |
|
633 |
|
| - |
|
634 |
@Override
|
| - |
|
635 |
public List<OutOfSequenceDetailModel> getOutOfSequenceDetails(int authId) {
|
| - |
|
636 |
|
| - |
|
637 |
LocalDate today = LocalDate.now();
|
| - |
|
638 |
LocalDateTime start = today.atStartOfDay();
|
| - |
|
639 |
LocalDateTime end = today.plusDays(1).atStartOfDay();
|
| - |
|
640 |
|
| - |
|
641 |
List<RbmCallSequenceLog> logs =
|
| - |
|
642 |
rbmCallSequenceLogRepository.selectByAuthIdAndDateRange(authId, start, end);
|
| - |
|
643 |
|
| - |
|
644 |
Map<Integer, RbmCallSequenceLog> uniqueOosLogsByFofoId = new LinkedHashMap<>();
|
| - |
|
645 |
|
| - |
|
646 |
for (RbmCallSequenceLog log : logs) {
|
| - |
|
647 |
if (log.isOutOfSequence()) {
|
| - |
|
648 |
// Keep only the first occurrence per fofoId (latest entry since ordered by id DESC)
|
| - |
|
649 |
uniqueOosLogsByFofoId.putIfAbsent(log.getFofoId(), log);
|
| - |
|
650 |
}
|
| - |
|
651 |
}
|
| - |
|
652 |
|
| - |
|
653 |
if (uniqueOosLogsByFofoId.isEmpty()) {
|
| - |
|
654 |
return Collections.emptyList();
|
| - |
|
655 |
}
|
| - |
|
656 |
|
| - |
|
657 |
Set<Integer> fofoIds = uniqueOosLogsByFofoId.keySet();
|
| - |
|
658 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
| - |
|
659 |
try {
|
| - |
|
660 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
|
| - |
|
661 |
} catch (ProfitMandiBusinessException e) {
|
| - |
|
662 |
LOGGER.error("Error fetching fofo stores", e);
|
| - |
|
663 |
}
|
| - |
|
664 |
|
| - |
|
665 |
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
|
| - |
|
666 |
List<OutOfSequenceDetailModel> result = new ArrayList<>();
|
| - |
|
667 |
|
| - |
|
668 |
for (RbmCallSequenceLog log : uniqueOosLogsByFofoId.values()) {
|
| - |
|
669 |
CustomRetailer retailer = retailerMap.get(log.getFofoId());
|
| - |
|
670 |
String partyName = retailer != null
|
| - |
|
671 |
? retailer.getBusinessName()
|
| - |
|
672 |
: "Unknown (" + log.getFofoId() + ")";
|
| - |
|
673 |
String code = retailer != null
|
| - |
|
674 |
? retailer.getCode()
|
| - |
|
675 |
: "-";
|
| - |
|
676 |
|
| - |
|
677 |
String time = log.getCreateTimestamp() != null
|
| - |
|
678 |
? log.getCreateTimestamp().format(timeFormatter)
|
| - |
|
679 |
: "-";
|
| - |
|
680 |
|
| - |
|
681 |
result.add(new OutOfSequenceDetailModel(partyName, code, time));
|
| - |
|
682 |
}
|
| - |
|
683 |
|
| - |
|
684 |
return result;
|
| - |
|
685 |
}
|
| - |
|
686 |
|
| - |
|
687 |
@Override
|
| - |
|
688 |
public List<CalledPartnerDetailModel> getCalledPartnerDetails(int authId) throws ProfitMandiBusinessException {
|
| - |
|
689 |
return getCalledPartnerDetails(authId, LocalDate.now());
|
| - |
|
690 |
}
|
| - |
|
691 |
|
| - |
|
692 |
@Override
|
| - |
|
693 |
public List<CalledPartnerDetailModel> getCalledPartnerDetails(int authId, LocalDate date) throws ProfitMandiBusinessException {
|
| - |
|
694 |
// Get all call logs for this auth user on this date
|
| - |
|
695 |
LOGGER.info("getCalledPartnerDetails: authId={}, date={}", authId, date);
|
| - |
|
696 |
List<AgentCallLog> callLogs = agentCallLogRepository.findByAuthIdAndDate(authId, date);
|
| - |
|
697 |
LOGGER.info("Found {} call logs for authId={} on date={}", callLogs.size(), authId, date);
|
| - |
|
698 |
|
| - |
|
699 |
if (callLogs.isEmpty()) {
|
| - |
|
700 |
return Collections.emptyList();
|
| - |
|
701 |
}
|
| - |
|
702 |
|
| - |
|
703 |
// Build a map of normalized customer number -> fofoId
|
| - |
|
704 |
Map<String, Integer> customerToFofoIdMap = new HashMap<>();
|
| - |
|
705 |
Set<String> normalizedNumbers = new HashSet<>();
|
| - |
|
706 |
|
| - |
|
707 |
for (AgentCallLog callLog : callLogs) {
|
| - |
|
708 |
String customerNumber = callLog.getCustomerNumber();
|
| - |
|
709 |
if (customerNumber != null) {
|
| - |
|
710 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
| - |
|
711 |
normalizedNumbers.add(normalized);
|
| - |
|
712 |
}
|
| - |
|
713 |
}
|
| - |
|
714 |
|
| - |
|
715 |
// For each normalized number, find fofoId from retailer_contact first, then address
|
| - |
|
716 |
for (String mobile : normalizedNumbers) {
|
| - |
|
717 |
Integer fofoId = findFofoIdByMobile(mobile);
|
| - |
|
718 |
if (fofoId != null) {
|
| - |
|
719 |
customerToFofoIdMap.put(mobile, fofoId);
|
| - |
|
720 |
}
|
| - |
|
721 |
}
|
| - |
|
722 |
|
| - |
|
723 |
// Get unique fofoIds for retailer lookup
|
| - |
|
724 |
Set<Integer> fofoIds = new HashSet<>(customerToFofoIdMap.values());
|
| - |
|
725 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
| - |
|
726 |
if (!fofoIds.isEmpty()) {
|
| - |
|
727 |
try {
|
| - |
|
728 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
|
| - |
|
729 |
} catch (ProfitMandiBusinessException e) {
|
| - |
|
730 |
LOGGER.error("Error fetching fofo stores", e);
|
| - |
|
731 |
}
|
| - |
|
732 |
}
|
| - |
|
733 |
|
| - |
|
734 |
// Get today's remarks for these fofoIds
|
| - |
|
735 |
Map<Integer, List<PartnerCollectionRemark>> fofoRemarkMap = new HashMap<>();
|
| - |
|
736 |
if (!fofoIds.isEmpty()) {
|
| - |
|
737 |
List<PartnerCollectionRemark> todayRemarks = partnerCollectionRemarkRepository
|
| - |
|
738 |
.selectAllByFofoIdsOnDate(new ArrayList<>(fofoIds), date);
|
| - |
|
739 |
for (PartnerCollectionRemark remark : todayRemarks) {
|
| - |
|
740 |
fofoRemarkMap.computeIfAbsent(remark.getFofoId(), k -> new ArrayList<>()).add(remark);
|
| - |
|
741 |
}
|
| - |
|
742 |
}
|
| - |
|
743 |
|
| - |
|
744 |
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
|
| - |
|
745 |
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm a");
|
| - |
|
746 |
List<CalledPartnerDetailModel> result = new ArrayList<>();
|
| - |
|
747 |
|
| - |
|
748 |
for (com.spice.profitmandi.dao.entity.cs.AgentCallLog callLog : callLogs) {
|
| - |
|
749 |
String customerNumber = callLog.getCustomerNumber();
|
| - |
|
750 |
if (customerNumber == null) {
|
| - |
|
751 |
continue;
|
| - |
|
752 |
}
|
| - |
|
753 |
|
| - |
|
754 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
| - |
|
755 |
Integer fofoId = customerToFofoIdMap.get(normalized);
|
| - |
|
756 |
|
| - |
|
757 |
String partyName = "Unknown";
|
| - |
|
758 |
String code = "-";
|
| - |
|
759 |
|
| - |
|
760 |
if (fofoId != null) {
|
| - |
|
761 |
CustomRetailer retailer = retailerMap.get(fofoId);
|
| - |
|
762 |
if (retailer != null) {
|
| - |
|
763 |
partyName = retailer.getBusinessName();
|
| - |
|
764 |
code = retailer.getCode();
|
| - |
|
765 |
} else {
|
| - |
|
766 |
partyName = "Unknown (" + fofoId + ")";
|
| - |
|
767 |
}
|
| - |
|
768 |
} else {
|
| - |
|
769 |
partyName = "Unknown (" + normalized + ")";
|
| - |
|
770 |
}
|
| - |
|
771 |
|
| - |
|
772 |
// Get remark if available
|
| - |
|
773 |
String remarkValue = "-";
|
| - |
|
774 |
String messageValue = "-";
|
| - |
|
775 |
String remarkTime = "-";
|
| - |
|
776 |
|
| - |
|
777 |
if (fofoId != null && fofoRemarkMap.containsKey(fofoId)) {
|
| - |
|
778 |
List<PartnerCollectionRemark> remarks = fofoRemarkMap.get(fofoId);
|
| - |
|
779 |
if (!remarks.isEmpty()) {
|
| - |
|
780 |
PartnerCollectionRemark remark = remarks.get(0);
|
| - |
|
781 |
remarkValue = remark.getRemark() != null ? remark.getRemark().getValue() : "-";
|
| - |
|
782 |
messageValue = remark.getMessage() != null ? remark.getMessage() : "-";
|
| - |
|
783 |
remarkTime = remark.getCreateTimestamp() != null ? remark.getCreateTimestamp().format(timeFormatter) : "-";
|
| - |
|
784 |
}
|
| - |
|
785 |
}
|
| - |
|
786 |
|
| - |
|
787 |
// Build call log data
|
| - |
|
788 |
String recordingUrl = callLog.getRecordingUrl();
|
| - |
|
789 |
String callStatus = callLog.getCallStatus();
|
| - |
|
790 |
String callDuration = callLog.getCallDuration();
|
| - |
|
791 |
String callDateTime = null;
|
| - |
|
792 |
if (callLog.getCallDate() != null && callLog.getCallTime() != null) {
|
| - |
|
793 |
LocalDateTime callDateTimeObj = LocalDateTime.of(callLog.getCallDate(), callLog.getCallTime());
|
| - |
|
794 |
callDateTime = callDateTimeObj.format(dateTimeFormatter);
|
| - |
|
795 |
}
|
| - |
|
796 |
|
| - |
|
797 |
result.add(new CalledPartnerDetailModel(partyName, code, remarkValue, messageValue, remarkTime,
|
| - |
|
798 |
recordingUrl, callStatus, callDuration, callDateTime));
|
| - |
|
799 |
}
|
| - |
|
800 |
|
| - |
|
801 |
return result;
|
| - |
|
802 |
}
|
| - |
|
803 |
|
| - |
|
804 |
private Integer findFofoIdByMobile(String mobile) {
|
| - |
|
805 |
// First check retailer_contact
|
| - |
|
806 |
List<RetailerContact> contacts = retailerContactRepository.selectByMobile(mobile);
|
| - |
|
807 |
if (contacts != null && !contacts.isEmpty()) {
|
| - |
|
808 |
return contacts.get(0).getFofoId();
|
| - |
|
809 |
}
|
| - |
|
810 |
|
| - |
|
811 |
// Fallback to user.address
|
| - |
|
812 |
List<Address> addresses = addressRepository.selectAllByPhoneNumber(mobile);
|
| - |
|
813 |
if (addresses != null && !addresses.isEmpty()) {
|
| - |
|
814 |
return addresses.get(0).getRetaierId();
|
| - |
|
815 |
}
|
| - |
|
816 |
|
| - |
|
817 |
return null;
|
| - |
|
818 |
}
|
| - |
|
819 |
|
| - |
|
820 |
private List<CalledPartnerDetailModel> buildCalledPartnerResult(List<PartnerCollectionRemark> allRemarks) {
|
| - |
|
821 |
if (allRemarks.isEmpty()) {
|
| - |
|
822 |
return Collections.emptyList();
|
| - |
|
823 |
}
|
| - |
|
824 |
|
| - |
|
825 |
// Get unique fofoIds for retailer lookup
|
| - |
|
826 |
Set<Integer> fofoIds = allRemarks.stream()
|
| - |
|
827 |
.map(PartnerCollectionRemark::getFofoId)
|
| - |
|
828 |
.collect(Collectors.toSet());
|
| - |
|
829 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
| - |
|
830 |
try {
|
| - |
|
831 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
|
| - |
|
832 |
} catch (ProfitMandiBusinessException e) {
|
| - |
|
833 |
LOGGER.error("Error fetching fofo stores", e);
|
| - |
|
834 |
}
|
| - |
|
835 |
|
| - |
|
836 |
// Fetch call logs for remarks that have agentCallLogId
|
| - |
|
837 |
Map<Long, com.spice.profitmandi.dao.entity.cs.AgentCallLog> callLogMap = new HashMap<>();
|
| - |
|
838 |
try {
|
| - |
|
839 |
List<Long> callLogIds = allRemarks.stream()
|
| - |
|
840 |
.filter(r -> r.getAgentCallLogId() > 0)
|
| - |
|
841 |
.map(PartnerCollectionRemark::getAgentCallLogId)
|
| - |
|
842 |
.collect(Collectors.toList());
|
| - |
|
843 |
|
| - |
|
844 |
if (!callLogIds.isEmpty()) {
|
| - |
|
845 |
List<com.spice.profitmandi.dao.entity.cs.AgentCallLog> callLogs = agentCallLogRepository.findByIds(callLogIds);
|
| - |
|
846 |
if (callLogs != null) {
|
| - |
|
847 |
callLogMap = callLogs.stream()
|
| - |
|
848 |
.collect(Collectors.toMap(com.spice.profitmandi.dao.entity.cs.AgentCallLog::getId, c -> c, (a, b) -> a));
|
| - |
|
849 |
}
|
| - |
|
850 |
}
|
| - |
|
851 |
} catch (Exception e) {
|
| - |
|
852 |
LOGGER.error("Error fetching call logs by ids", e);
|
| - |
|
853 |
}
|
| - |
|
854 |
|
| - |
|
855 |
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
|
| - |
|
856 |
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm a");
|
| - |
|
857 |
List<CalledPartnerDetailModel> result = new ArrayList<>();
|
| - |
|
858 |
|
| - |
|
859 |
for (PartnerCollectionRemark remark : allRemarks) {
|
| - |
|
860 |
CustomRetailer retailer = retailerMap.get(remark.getFofoId());
|
| - |
|
861 |
String partyName = retailer != null
|
| - |
|
862 |
? retailer.getBusinessName()
|
| - |
|
863 |
: "Unknown (" + remark.getFofoId() + ")";
|
| - |
|
864 |
String code = retailer != null
|
| - |
|
865 |
? retailer.getCode()
|
| - |
|
866 |
: "-";
|
| - |
|
867 |
|
| - |
|
868 |
String remarkValue = remark.getRemark() != null
|
| - |
|
869 |
? remark.getRemark().getValue()
|
| - |
|
870 |
: "-";
|
| - |
|
871 |
|
| - |
|
872 |
String messageValue = remark.getMessage() != null
|
| - |
|
873 |
? remark.getMessage()
|
| - |
|
874 |
: "-";
|
| - |
|
875 |
|
| - |
|
876 |
String time = remark.getCreateTimestamp() != null
|
| - |
|
877 |
? remark.getCreateTimestamp().format(timeFormatter)
|
| - |
|
878 |
: "-";
|
| - |
|
879 |
|
| - |
|
880 |
// Get call log data if available
|
| - |
|
881 |
String recordingUrl = null;
|
| - |
|
882 |
String callStatus = null;
|
| - |
|
883 |
String callDuration = null;
|
| - |
|
884 |
String callDateTime = null;
|
| - |
|
885 |
|
| - |
|
886 |
try {
|
| - |
|
887 |
if (remark.getAgentCallLogId() > 0 && callLogMap.containsKey(remark.getAgentCallLogId())) {
|
| - |
|
888 |
com.spice.profitmandi.dao.entity.cs.AgentCallLog callLog = callLogMap.get(remark.getAgentCallLogId());
|
| - |
|
889 |
recordingUrl = callLog.getRecordingUrl();
|
| - |
|
890 |
callStatus = callLog.getCallStatus();
|
| - |
|
891 |
callDuration = callLog.getCallDuration();
|
| - |
|
892 |
if (callLog.getCallDate() != null && callLog.getCallTime() != null) {
|
| - |
|
893 |
LocalDateTime callDateTimeObj = LocalDateTime.of(callLog.getCallDate(), callLog.getCallTime());
|
| - |
|
894 |
callDateTime = callDateTimeObj.format(dateTimeFormatter);
|
| - |
|
895 |
}
|
| - |
|
896 |
}
|
| - |
|
897 |
} catch (Exception e) {
|
| - |
|
898 |
LOGGER.error("Error processing call log for remark id: {}", remark.getId(), e);
|
| - |
|
899 |
}
|
| - |
|
900 |
|
| - |
|
901 |
result.add(new CalledPartnerDetailModel(partyName, code, remarkValue, messageValue, time,
|
| - |
|
902 |
recordingUrl, callStatus, callDuration, callDateTime));
|
| - |
|
903 |
}
|
| - |
|
904 |
|
| - |
|
905 |
return result;
|
| - |
|
906 |
}
|
| - |
|
907 |
|
| - |
|
908 |
@Override
|
| - |
|
909 |
public List<List<String>> getRbmCallTargetRawDataByAuthId(int authId) throws Exception {
|
| - |
|
910 |
List<List<String>> rows = new ArrayList<>();
|
| - |
|
911 |
|
| - |
|
912 |
// Get auth user
|
| - |
|
913 |
List<AuthUser> authUsers = authRepository.selectByIds(Collections.singletonList(authId));
|
| - |
|
914 |
if (authUsers.isEmpty()) {
|
| - |
|
915 |
return rows;
|
| - |
|
916 |
}
|
| - |
|
917 |
AuthUser authUser = authUsers.get(0);
|
| - |
|
918 |
|
| - |
|
919 |
// Get positions to determine if L2
|
| - |
|
920 |
List<Position> positions = positionRepository.selectPositionByAuthIds(Collections.singletonList(authId));
|
| - |
|
921 |
boolean isL2 = positions.stream()
|
| - |
|
922 |
.anyMatch(p -> ProfitMandiConstants.TICKET_CATEGORY_RBM == p.getCategoryId()
|
| - |
|
923 |
&& EscalationType.L2.equals(p.getEscalationType()));
|
| - |
|
924 |
|
| - |
|
925 |
LocalDateTime startDate = LocalDate.now().atStartOfDay();
|
| - |
|
926 |
LocalDate firstOfMonth = LocalDate.now().withDayOfMonth(1);
|
| - |
|
927 |
LocalDate endOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).plusDays(1);
|
| - |
|
928 |
|
| - |
|
929 |
// Get fofo IDs from mtdBillingData (same source as Partner Count in getRbmCallTargetModels)
|
| - |
|
930 |
List<RbmWeeklyBillingModel> mtdBillingData = getWeeklyBillingDataForMonth(firstOfMonth, endOfMonth);
|
| - |
|
931 |
|
| - |
|
932 |
List<Integer> fofoIdList;
|
| - |
|
933 |
if (isL2) {
|
| - |
|
934 |
// L2: get fofo IDs from escalated tickets (same as getRbmCallTargetModels)
|
| - |
|
935 |
List<Ticket> escalatedTickets = ticketRepository.selectOpenEscalatedTicketsByAuthIds(Collections.singletonList(authId));
|
| - |
|
936 |
fofoIdList = escalatedTickets.stream()
|
| - |
|
937 |
.filter(t -> t.getL2AuthUser() == authId
|
| - |
|
938 |
|| t.getL3AuthUser() == authId
|
| - |
|
939 |
|| t.getL4AuthUser() == authId
|
| - |
|
940 |
|| t.getL5AuthUser() == authId)
|
| - |
|
941 |
.map(Ticket::getFofoId)
|
| - |
|
942 |
.distinct()
|
| - |
|
943 |
.collect(Collectors.toList());
|
| - |
|
944 |
} else {
|
| - |
|
945 |
// L1: get fofo IDs from mtdBillingData with isTargetedPartner (same as Partner Count)
|
| - |
|
946 |
fofoIdList = mtdBillingData.stream()
|
| - |
|
947 |
.filter(RbmWeeklyBillingModel::isTargetedPartner)
|
| - |
|
948 |
.filter(m -> m.getAuthId() == authId)
|
| - |
|
949 |
.map(RbmWeeklyBillingModel::getFofoId)
|
| - |
|
950 |
.distinct()
|
| - |
|
951 |
.collect(Collectors.toList());
|
| - |
|
952 |
}
|
| - |
|
953 |
|
| - |
|
954 |
if (fofoIdList.isEmpty()) {
|
| - |
|
955 |
return rows;
|
| - |
|
956 |
}
|
| - |
|
957 |
|
| - |
|
958 |
// MTD billed fofoIds for zero billing check
|
| - |
|
959 |
Set<Integer> mtdBilledFofoIds = mtdBillingData.stream()
|
| - |
|
960 |
.filter(RbmWeeklyBillingModel::isMtdBilled)
|
| - |
|
961 |
.map(RbmWeeklyBillingModel::getFofoId)
|
| - |
|
962 |
.collect(Collectors.toSet());
|
| - |
|
963 |
|
| - |
|
964 |
// Collection rank map for status calculation
|
| - |
|
965 |
Map<Integer, Integer> collectionRankMap = new HashMap<>();
|
| - |
|
966 |
try {
|
| - |
|
967 |
collectionRankMap = partnerCollectionService.getCollectionRankMap(fofoIdList, startDate);
|
| - |
|
968 |
} catch (ProfitMandiBusinessException e) {
|
| - |
|
969 |
LOGGER.error("Error fetching collection rank map", e);
|
| - |
|
970 |
}
|
| - |
|
971 |
|
| - |
|
972 |
// Resolve partner names/codes
|
| - |
|
973 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
| - |
|
974 |
if (!fofoIdList.isEmpty()) {
|
| - |
|
975 |
try {
|
| - |
|
976 |
retailerMap = retailerService.getFofoRetailers(fofoIdList);
|
| - |
|
977 |
} catch (ProfitMandiBusinessException e) {
|
| - |
|
978 |
LOGGER.error("Error fetching fofo retailers for raw data", e);
|
| - |
|
979 |
}
|
| - |
|
980 |
}
|
| - |
|
981 |
|
| - |
|
982 |
String rbmName = authUser.getFullName() + (isL2 ? " (L2)" : "");
|
| - |
|
983 |
|
| - |
|
984 |
// Build rows for ALL partners (same count as Partner Count)
|
| - |
|
985 |
for (Integer fofoId : fofoIdList) {
|
| - |
|
986 |
// Default to rank 5 (Normal) for partners without collection plan
|
| - |
|
987 |
int rank = collectionRankMap.getOrDefault(fofoId, 5);
|
| - |
|
988 |
boolean hasZeroBilling = !mtdBilledFofoIds.contains(fofoId);
|
| - |
|
989 |
|
| - |
|
990 |
// Status assignment with same priority as getRbmCallTargetModels
|
| - |
|
991 |
String status;
|
| - |
|
992 |
if (rank == 1) {
|
| - |
|
993 |
status = "Plan Today";
|
| - |
|
994 |
} else if (rank == 2) {
|
| - |
|
995 |
status = "Carry Forward";
|
| - |
|
996 |
} else if (hasZeroBilling) {
|
| - |
|
997 |
status = "Zero Billing";
|
| - |
|
998 |
} else if (rank == 3) {
|
| - |
|
999 |
status = "Untouched";
|
| - |
|
1000 |
} else if (rank == 4) {
|
| - |
|
1001 |
status = "Future Plan";
|
| - |
|
1002 |
} else {
|
| - |
|
1003 |
status = "Normal";
|
| - |
|
1004 |
}
|
| - |
|
1005 |
|
| - |
|
1006 |
CustomRetailer retailer = retailerMap.get(fofoId);
|
| - |
|
1007 |
String partnerName = retailer != null ? retailer.getBusinessName() : "Unknown (" + fofoId + ")";
|
| - |
|
1008 |
String partnerCode = retailer != null ? retailer.getCode() : "-";
|
| - |
|
1009 |
|
| - |
|
1010 |
rows.add(Arrays.asList(partnerName, partnerCode, status, rbmName));
|
| - |
|
1011 |
}
|
| - |
|
1012 |
|
| - |
|
1013 |
return rows;
|
| - |
|
1014 |
}
|
| - |
|
1015 |
|
| - |
|
1016 |
@Override
|
| - |
|
1017 |
public List<List<String>> getAllRbmCallTargetRawData() throws Exception {
|
| - |
|
1018 |
List<List<String>> rows = new ArrayList<>();
|
| - |
|
1019 |
|
| - |
|
1020 |
// Get all L1 RBM positions (rows are emitted per L1 RBM).
|
| - |
|
1021 |
// Call-log lookup happens against ALL callers via findAllByDate — so an L2/L3 or admin call
|
| - |
|
1022 |
// still counts as "tried" for the partner.
|
| - |
|
1023 |
List<Position> allRbmPositions = positionRepository
|
| - |
|
1024 |
.selectPositionByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_RBM).stream()
|
| - |
|
1025 |
.filter(x -> EscalationType.L1.equals(x.getEscalationType()))
|
| - |
|
1026 |
.collect(Collectors.toList());
|
| - |
|
1027 |
|
| - |
|
1028 |
List<Integer> l1AuthIds = allRbmPositions.stream()
|
| - |
|
1029 |
.map(Position::getAuthUserId).distinct().collect(Collectors.toList());
|
| - |
|
1030 |
|
| - |
|
1031 |
if (l1AuthIds.isEmpty()) {
|
| - |
|
1032 |
return rows;
|
| - |
|
1033 |
}
|
| - |
|
1034 |
|
| - |
|
1035 |
// Get auth user map
|
| - |
|
1036 |
Map<Integer, AuthUser> authUserMap = authRepository.selectByIds(l1AuthIds).stream()
|
| - |
|
1037 |
.collect(Collectors.toMap(AuthUser::getId, au -> au));
|
| - |
|
1038 |
|
| - |
|
1039 |
// Positions per auth user (needed early to build L1 partner assignments below)
|
| - |
|
1040 |
Map<Integer, List<Position>> positionsByAuthId = positionRepository.selectPositionByAuthIds(l1AuthIds).stream()
|
| - |
|
1041 |
.collect(Collectors.groupingBy(Position::getAuthUserId));
|
| - |
|
1042 |
|
| - |
|
1043 |
// Build L1 partner assignments from partner_position — SAME source the UI summary uses for
|
| - |
|
1044 |
// Calling Target. storeGuyMap (email-based CS mapping) was returning smaller/different lists
|
| - |
|
1045 |
// for users who are also L2, causing summary=43 but download=3 for Ebadullah.
|
| - |
|
1046 |
Map<Integer, Integer> l1PositionIdToAuthId = new HashMap<>();
|
| - |
|
1047 |
for (int rbmAuthId : l1AuthIds) {
|
| - |
|
1048 |
List<Position> positions = positionsByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
| - |
|
1049 |
for (Position p : positions) {
|
| - |
|
1050 |
if (ProfitMandiConstants.TICKET_CATEGORY_RBM == p.getCategoryId()
|
| - |
|
1051 |
&& EscalationType.L1.equals(p.getEscalationType())) {
|
| - |
|
1052 |
l1PositionIdToAuthId.put(p.getId(), rbmAuthId);
|
| - |
|
1053 |
}
|
| - |
|
1054 |
}
|
| - |
|
1055 |
}
|
| - |
|
1056 |
|
| - |
|
1057 |
Map<Integer, List<Integer>> rbmToFofoIdsMap = new HashMap<>();
|
| - |
|
1058 |
Set<Integer> allFofoIds = new HashSet<>();
|
| - |
|
1059 |
if (!l1PositionIdToAuthId.isEmpty()) {
|
| - |
|
1060 |
List<com.spice.profitmandi.dao.entity.cs.PartnerPosition> allL1PPs =
|
| - |
|
1061 |
partnerPositionRepository.selectByPositionIds(new ArrayList<>(l1PositionIdToAuthId.keySet()));
|
| - |
|
1062 |
// Group by owning RBM; keep partners distinct within each RBM.
|
| - |
|
1063 |
Map<Integer, Set<Integer>> perRbmSets = new HashMap<>();
|
| - |
|
1064 |
for (com.spice.profitmandi.dao.entity.cs.PartnerPosition pp : allL1PPs) {
|
| - |
|
1065 |
Integer ownerAuthId = l1PositionIdToAuthId.get(pp.getPositionId());
|
| - |
|
1066 |
if (ownerAuthId == null) continue;
|
| - |
|
1067 |
perRbmSets.computeIfAbsent(ownerAuthId, k -> new HashSet<>()).add(pp.getFofoId());
|
| - |
|
1068 |
allFofoIds.add(pp.getFofoId());
|
| - |
|
1069 |
}
|
| - |
|
1070 |
for (Map.Entry<Integer, Set<Integer>> e : perRbmSets.entrySet()) {
|
| - |
|
1071 |
rbmToFofoIdsMap.put(e.getKey(), new ArrayList<>(e.getValue()));
|
| - |
|
1072 |
}
|
| - |
|
1073 |
}
|
| - |
|
1074 |
|
| - |
|
1075 |
if (allFofoIds.isEmpty()) {
|
| - |
|
1076 |
return rows;
|
| - |
|
1077 |
}
|
| - |
|
1078 |
|
| - |
|
1079 |
// Get fofo stores for filtering and name resolution
|
| - |
|
1080 |
Map<Integer, FofoStore> fofoStoresMap = new HashMap<>();
|
| - |
|
1081 |
try {
|
| - |
|
1082 |
fofoStoresMap = fofoStoreRepository.selectByRetailerIds(new ArrayList<>(allFofoIds)).stream()
|
| - |
|
1083 |
.collect(Collectors.toMap(FofoStore::getId, x -> x, (a, b) -> a));
|
| - |
|
1084 |
} catch (ProfitMandiBusinessException e) {
|
| - |
|
1085 |
LOGGER.error("Error fetching fofo stores for all raw data", e);
|
| - |
|
1086 |
}
|
| - |
|
1087 |
|
| - |
|
1088 |
// Batch fetch collection rank map
|
| - |
|
1089 |
LocalDateTime startDate = LocalDate.now().atStartOfDay();
|
| - |
|
1090 |
Map<Integer, Integer> allCollectionRankMap = new HashMap<>();
|
| - |
|
1091 |
try {
|
| - |
|
1092 |
allCollectionRankMap = partnerCollectionService.getCollectionRankMap(new ArrayList<>(allFofoIds), startDate);
|
| - |
|
1093 |
} catch (ProfitMandiBusinessException e) {
|
| - |
|
1094 |
LOGGER.error("Error fetching collection rank map for all raw data", e);
|
| - |
|
1095 |
}
|
| - |
|
1096 |
|
| - |
|
1097 |
// MTD billing data
|
| - |
|
1098 |
LocalDate firstOfMonth = LocalDate.now().withDayOfMonth(1);
|
| - |
|
1099 |
LocalDate endOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).plusDays(1);
|
| - |
|
1100 |
List<RbmWeeklyBillingModel> mtdBillingData = getWeeklyBillingDataForMonth(firstOfMonth, endOfMonth);
|
| - |
|
1101 |
Set<Integer> allMtdBilledFofoIds = mtdBillingData.stream()
|
| - |
|
1102 |
.filter(RbmWeeklyBillingModel::isMtdBilled)
|
| - |
|
1103 |
.map(RbmWeeklyBillingModel::getFofoId)
|
| - |
|
1104 |
.collect(Collectors.toSet());
|
| - |
|
1105 |
|
| - |
|
1106 |
// Batch fetch partner collection remarks for escalation filtering
|
| - |
|
1107 |
Map<Integer, PartnerCollectionRemark> allPartnerCollectionRemarks = new HashMap<>();
|
| - |
|
1108 |
if (!allFofoIds.isEmpty()) {
|
| - |
|
1109 |
List<Integer> allRemarkIds = partnerCollectionRemarkRepository.selectMaxRemarkId(new ArrayList<>(allFofoIds));
|
| - |
|
1110 |
if (!allRemarkIds.isEmpty()) {
|
| - |
|
1111 |
allPartnerCollectionRemarks = partnerCollectionRemarkRepository.selectByIds(allRemarkIds).stream()
|
| - |
|
1112 |
.collect(Collectors.toMap(PartnerCollectionRemark::getFofoId, x -> x, (a, b) -> a));
|
| - |
|
1113 |
}
|
| - |
|
1114 |
}
|
| - |
|
1115 |
|
| - |
|
1116 |
// Resolve partner names/codes in batch
|
| - |
|
1117 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
| - |
|
1118 |
try {
|
| - |
|
1119 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(allFofoIds));
|
| - |
|
1120 |
} catch (ProfitMandiBusinessException e) {
|
| - |
|
1121 |
LOGGER.error("Error fetching fofo retailers for all raw data", e);
|
| - |
|
1122 |
}
|
| - |
|
1123 |
|
| - |
|
1124 |
// Batch-fetch EVERY call log for today (across all callers, not just RBMs) and build a
|
| - |
|
1125 |
// fofoId -> latest AgentCallLog map. We use findAllByDate here so that calls placed by
|
| - |
|
1126 |
// admins, escalation agents, or anyone whose authId isn't in the RBM position table are
|
| - |
|
1127 |
// still surfaced. Downstream we only read this map for fofoIds we actually emit, so
|
| - |
|
1128 |
// extra entries are harmless.
|
| - |
|
1129 |
Map<Integer, AgentCallLog> latestCallByFofo = new HashMap<>();
|
| - |
|
1130 |
try {
|
| - |
|
1131 |
List<AgentCallLog> todayCallLogs = agentCallLogRepository.findAllByDate(LocalDate.now());
|
| - |
|
1132 |
Set<String> normalizedMobiles = new HashSet<>();
|
| - |
|
1133 |
for (AgentCallLog log : todayCallLogs) {
|
| - |
|
1134 |
if (log.getCustomerNumber() != null) {
|
| - |
|
1135 |
String n = log.getCustomerNumber();
|
| - |
|
1136 |
normalizedMobiles.add(n.startsWith("+91") ? n.substring(3) : n);
|
| - |
|
1137 |
}
|
| - |
|
1138 |
}
|
| - |
|
1139 |
Map<String, Integer> mobileToFofoIdMap = buildMobileToFofoIdMap(normalizedMobiles);
|
| - |
|
1140 |
for (AgentCallLog log : todayCallLogs) {
|
| - |
|
1141 |
if (log.getCustomerNumber() == null) continue;
|
| - |
|
1142 |
String n = log.getCustomerNumber();
|
| - |
|
1143 |
String normalized = n.startsWith("+91") ? n.substring(3) : n;
|
| - |
|
1144 |
Integer fofoId = mobileToFofoIdMap.get(normalized);
|
| - |
|
1145 |
if (fofoId == null) continue;
|
| - |
|
1146 |
AgentCallLog existing = latestCallByFofo.get(fofoId);
|
| - |
|
1147 |
if (existing == null || (log.getId() != null && existing.getId() != null && log.getId() > existing.getId())) {
|
| - |
|
1148 |
latestCallByFofo.put(fofoId, log);
|
| - |
|
1149 |
}
|
| - |
|
1150 |
}
|
| - |
|
1151 |
} catch (Exception e) {
|
| - |
|
1152 |
LOGGER.error("Error building latest call status map for all raw data", e);
|
| - |
|
1153 |
}
|
| - |
|
1154 |
|
| - |
|
1155 |
// Process each L1 RBM
|
| - |
|
1156 |
// Note: no cross-RBM dedup — a partner assigned to multiple RBMs appears once per RBM,
|
| - |
|
1157 |
// matching the UI summary counts so row totals per RBM equal that RBM's "Calling Target".
|
| - |
|
1158 |
for (int rbmAuthId : l1AuthIds) {
|
| - |
|
1159 |
AuthUser authUser = authUserMap.get(rbmAuthId);
|
| - |
|
1160 |
if (authUser == null) {
|
| - |
|
1161 |
continue;
|
| - |
|
1162 |
}
|
| - |
|
1163 |
|
| - |
|
1164 |
List<Integer> fofoIdList = rbmToFofoIdsMap.getOrDefault(rbmAuthId, Collections.emptyList());
|
| - |
|
1165 |
if (fofoIdList.isEmpty()) {
|
| - |
|
1166 |
continue; // No L1 partner assignments for this RBM
|
| - |
|
1167 |
}
|
| - |
|
1168 |
|
| - |
|
1169 |
// Filter escalated partners for L1 RBMs
|
| - |
|
1170 |
List<Position> positions = positionsByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
| - |
|
1171 |
boolean isRBMAndL1 = positions.stream()
|
| - |
|
1172 |
.anyMatch(position ->
|
| - |
|
1173 |
ProfitMandiConstants.TICKET_CATEGORY_RBM == position.getCategoryId()
|
| - |
|
1174 |
&& EscalationType.L1.equals(position.getEscalationType()));
|
| - |
|
1175 |
|
| - |
|
1176 |
List<Integer> fofoIds = fofoIdList;
|
| - |
|
1177 |
if (isRBMAndL1) {
|
| - |
|
1178 |
Map<Integer, PartnerCollectionRemark> partnerRemarks = new HashMap<>();
|
| - |
|
1179 |
for (Integer fofoId : fofoIdList) {
|
| - |
|
1180 |
if (allPartnerCollectionRemarks.containsKey(fofoId)) {
|
| - |
|
1181 |
partnerRemarks.put(fofoId, allPartnerCollectionRemarks.get(fofoId));
|
| - |
|
1182 |
}
|
| - |
|
1183 |
}
|
| - |
|
1184 |
Map<Integer, PartnerCollectionRemark> finalPartnerRemarks = partnerRemarks;
|
| - |
|
1185 |
fofoIds = fofoIdList.stream()
|
| - |
|
1186 |
.filter(fofoId -> {
|
| - |
|
1187 |
if (!finalPartnerRemarks.containsKey(fofoId)) return true;
|
| - |
|
1188 |
PartnerCollectionRemark pcr = finalPartnerRemarks.get(fofoId);
|
| - |
|
1189 |
return !(CollectionRemark.RBM_L2_ESCALATION.equals(pcr.getRemark())
|
| - |
|
1190 |
|| CollectionRemark.SALES_ESCALATION.equals(pcr.getRemark()));
|
| - |
|
1191 |
})
|
| - |
|
1192 |
.collect(Collectors.toList());
|
| - |
|
1193 |
}
|
| - |
|
1194 |
|
| - |
|
1195 |
// Filter to only external, ACTIVE or REVIVAL stores
|
| - |
|
1196 |
Map<Integer, FofoStore> finalFofoStoresMap = fofoStoresMap;
|
| - |
|
1197 |
List<Integer> validFofoIds = fofoIds.stream()
|
| - |
|
1198 |
.filter(fofoId -> {
|
| - |
|
1199 |
FofoStore store = finalFofoStoresMap.get(fofoId);
|
| - |
|
1200 |
if (store == null || store.isInternal()) return false;
|
| - |
|
1201 |
return ActivationType.ACTIVE.equals(store.getActivationType())
|
| - |
|
1202 |
|| ActivationType.REVIVAL.equals(store.getActivationType());
|
| - |
|
1203 |
})
|
| - |
|
1204 |
.collect(Collectors.toList());
|
| - |
|
1205 |
|
| - |
|
1206 |
String rbmName = authUser.getFullName();
|
| - |
|
1207 |
|
| - |
|
1208 |
// Categorize each partner and add a row per RBM assignment.
|
| - |
|
1209 |
for (Integer fofoId : validFofoIds) {
|
| - |
|
1210 |
int rank = allCollectionRankMap.getOrDefault(fofoId, 5);
|
| - |
|
1211 |
boolean hasZeroBilling = !allMtdBilledFofoIds.contains(fofoId);
|
| - |
|
1212 |
|
| - |
|
1213 |
String status;
|
| - |
|
1214 |
if (rank == 1) {
|
| - |
|
1215 |
status = "Plan Today";
|
| - |
|
1216 |
} else if (rank == 2) {
|
| - |
|
1217 |
status = "Carry Forward";
|
| - |
|
1218 |
} else if (hasZeroBilling) {
|
| - |
|
1219 |
status = "Zero Billing";
|
| - |
|
1220 |
} else if (rank == 3) {
|
| - |
|
1221 |
status = "Untouched";
|
| - |
|
1222 |
} else {
|
| - |
|
1223 |
continue; // Skip Future Plan and Normal — only include calling target parties
|
| - |
|
1224 |
}
|
| - |
|
1225 |
|
| - |
|
1226 |
CustomRetailer retailer = retailerMap.get(fofoId);
|
| - |
|
1227 |
String partnerName = retailer != null ? retailer.getBusinessName() : "Unknown (" + fofoId + ")";
|
| - |
|
1228 |
String partnerCode = retailer != null ? retailer.getCode() : "-";
|
| - |
|
1229 |
|
| - |
|
1230 |
// "Did Not Try" means the PARTNER has no call log today from anyone — not tied to this RBM.
|
| - |
|
1231 |
// If any caller reached this partner, show the actual status.
|
| - |
|
1232 |
String latestCallStatus = "Did Not Try";
|
| - |
|
1233 |
AgentCallLog latestLog = latestCallByFofo.get(fofoId);
|
| - |
|
1234 |
if (latestLog != null && latestLog.getCallStatus() != null && !latestLog.getCallStatus().isEmpty()) {
|
| - |
|
1235 |
latestCallStatus = latestLog.getCallStatus();
|
| - |
|
1236 |
}
|
| - |
|
1237 |
|
| - |
|
1238 |
rows.add(Arrays.asList(partnerName, partnerCode, status, rbmName, latestCallStatus));
|
| - |
|
1239 |
}
|
| - |
|
1240 |
}
|
| - |
|
1241 |
|
| - |
|
1242 |
return rows;
|
| - |
|
1243 |
}
|
| - |
|
1244 |
|
| - |
|
1245 |
/**
|
| - |
|
1246 |
* Get count of distinct partners called today based on call logs.
|
| - |
|
1247 |
* Maps customerNumber from call log to fofoId using retailer_contact and address.
|
| - |
|
1248 |
* If same fofoId is called multiple times, counts only once.
|
| - |
|
1249 |
* Numbers without fofoId mapping are also counted (by distinct customer number).
|
| - |
|
1250 |
*
|
| - |
|
1251 |
* @param authId the RBM auth ID
|
| - |
|
1252 |
* @return count of distinct partners/numbers called today
|
| - |
|
1253 |
*/
|
| - |
|
1254 |
public long getCalledCountFromCallLogs(long authId) {
|
| - |
|
1255 |
return getCalledCountFromCallLogs(authId, LocalDate.now());
|
| - |
|
1256 |
}
|
| - |
|
1257 |
|
| - |
|
1258 |
public long getCalledCountFromCallLogs(long authId, LocalDate date) {
|
| - |
|
1259 |
return getCallStats(authId, date)[0];
|
| - |
|
1260 |
}
|
| - |
|
1261 |
|
| - |
|
1262 |
/**
|
| - |
|
1263 |
* Batch-builds a mobile (normalized, +91 stripped) -> fofoId mapping for all the given mobiles
|
| - |
|
1264 |
* in just two queries (retailer_contact, then address fallback for unmapped numbers).
|
| - |
|
1265 |
* Replaces the previous per-call-log findFofoIdByMobile() N+1 lookups.
|
| - |
|
1266 |
*/
|
| - |
|
1267 |
private Map<String, Integer> buildMobileToFofoIdMap(Set<String> normalizedMobiles) {
|
| - |
|
1268 |
Map<String, Integer> result = new HashMap<>();
|
| - |
|
1269 |
if (normalizedMobiles == null || normalizedMobiles.isEmpty()) {
|
| - |
|
1270 |
return result;
|
| - |
|
1271 |
}
|
| - |
|
1272 |
List<String> mobilesList = new ArrayList<>(normalizedMobiles);
|
| - |
|
1273 |
|
| - |
|
1274 |
// First pass: retailer_contact
|
| - |
|
1275 |
List<RetailerContact> contacts = retailerContactRepository.selectByMobiles(mobilesList);
|
| - |
|
1276 |
for (RetailerContact rc : contacts) {
|
| - |
|
1277 |
// Keep the first fofoId we see per mobile (matches old single-mobile behavior of get(0))
|
| - |
|
1278 |
result.putIfAbsent(rc.getMobile(), rc.getFofoId());
|
| - |
|
1279 |
}
|
| - |
|
1280 |
|
| - |
|
1281 |
// Second pass: address fallback for mobiles not yet mapped
|
| - |
|
1282 |
List<String> unmapped = mobilesList.stream()
|
| - |
|
1283 |
.filter(m -> !result.containsKey(m))
|
| - |
|
1284 |
.collect(Collectors.toList());
|
| - |
|
1285 |
if (!unmapped.isEmpty()) {
|
| - |
|
1286 |
List<Address> addresses = addressRepository.selectAllByPhoneNumbers(unmapped);
|
| - |
|
1287 |
for (Address addr : addresses) {
|
| - |
|
1288 |
result.putIfAbsent(addr.getPhoneNumber(), addr.getRetaierId());
|
| - |
|
1289 |
}
|
| - |
|
1290 |
}
|
| - |
|
1291 |
return result;
|
| - |
|
1292 |
}
|
| - |
|
1293 |
|
| - |
|
1294 |
/**
|
| - |
|
1295 |
* In-memory variant of getCallStats() that uses pre-fetched call logs and mobile→fofoId mapping.
|
| - |
|
1296 |
* Used by the batch-fetched RBM Call Target loop to avoid N+1 query patterns.
|
| - |
|
1297 |
*/
|
| - |
|
1298 |
private long[] getCallStatsFromLogs(List<AgentCallLog> callLogs, Map<String, Integer> mobileToFofoIdMap) {
|
| - |
|
1299 |
if (callLogs == null || callLogs.isEmpty()) {
|
| - |
|
1300 |
return new long[]{0, 0, 0};
|
| - |
|
1301 |
}
|
| - |
|
1302 |
|
| - |
|
1303 |
Set<Integer> calledFofoIds = new HashSet<>();
|
| - |
|
1304 |
Set<String> calledNumbersWithoutFofoId = new HashSet<>();
|
| - |
|
1305 |
long totalRecordingCalls = 0;
|
| - |
|
1306 |
Set<String> uniqueRecordingNumbers = new HashSet<>();
|
| - |
|
1307 |
|
| - |
|
1308 |
for (AgentCallLog callLog : callLogs) {
|
| - |
|
1309 |
String customerNumber = callLog.getCustomerNumber();
|
| - |
|
1310 |
if (customerNumber != null) {
|
| - |
|
1311 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
| - |
|
1312 |
Integer fofoId = mobileToFofoIdMap.get(normalized);
|
| - |
|
1313 |
if (fofoId != null) {
|
| - |
|
1314 |
calledFofoIds.add(fofoId);
|
| - |
|
1315 |
} else {
|
| - |
|
1316 |
calledNumbersWithoutFofoId.add(normalized);
|
| - |
|
1317 |
}
|
| - |
|
1318 |
|
| - |
|
1319 |
if (callLog.getRecordingUrl() != null && !callLog.getRecordingUrl().isEmpty()
|
| - |
|
1320 |
&& !"None".equalsIgnoreCase(callLog.getRecordingUrl())) {
|
| - |
|
1321 |
totalRecordingCalls++;
|
| - |
|
1322 |
uniqueRecordingNumbers.add(normalized);
|
| - |
|
1323 |
}
|
| - |
|
1324 |
}
|
| - |
|
1325 |
}
|
| - |
|
1326 |
|
| - |
|
1327 |
long calledCount = calledFofoIds.size() + calledNumbersWithoutFofoId.size();
|
| - |
|
1328 |
return new long[]{calledCount, totalRecordingCalls, uniqueRecordingNumbers.size()};
|
| - |
|
1329 |
}
|
| - |
|
1330 |
|
| 627 |
public List<RbmCallTargetModel> getRbmCallTargetModels(LocalDate queryDate) throws Exception {
|
1331 |
public List<RbmCallTargetModel> getRbmCallTargetModels(LocalDate queryDate) throws Exception {
|
| 628 |
long methodStart = System.currentTimeMillis();
|
1332 |
long methodStart = System.currentTimeMillis();
|
| 629 |
List<RbmCallTargetModel> rbmCallTargetModels = new ArrayList<>();
|
1333 |
List<RbmCallTargetModel> rbmCallTargetModels = new ArrayList<>();
|
| 630 |
|
1334 |
|
| 631 |
// Get all RBM positions (L1 and L2)
|
1335 |
// Get all RBM positions (L1 and L2)
|
| Line 978... |
Line 1682... |
| 978 |
todayTargetPartners.addAll(carryForwardPartners);
|
1682 |
todayTargetPartners.addAll(carryForwardPartners);
|
| 979 |
todayTargetPartners.addAll(zeroBillingPartners);
|
1683 |
todayTargetPartners.addAll(zeroBillingPartners);
|
| 980 |
todayTargetPartners.addAll(untouchedPartners);
|
1684 |
todayTargetPartners.addAll(untouchedPartners);
|
| 981 |
|
1685 |
|
| 982 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
1686 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
| - |
|
1687 |
List<AgentCallLog> l1Logs = callLogsByAuthId.get((long) rbmAuthId);
|
| 983 |
long[] callStats = getCallStatsFromLogs(callLogsByAuthId.get((long) rbmAuthId), mobileToFofoIdMap);
|
1688 |
long[] callStats = getCallStatsFromLogs(l1Logs, mobileToFofoIdMap);
|
| 984 |
targetModel.setValueTargetAchieved(callStats[0]);
|
1689 |
targetModel.setValueTargetAchieved(callStats[0]);
|
| 985 |
targetModel.setTotalRecordingCalls(callStats[1]);
|
1690 |
targetModel.setTotalRecordingCalls(callStats[1]);
|
| 986 |
targetModel.setUniqueRecordingCalls(callStats[2]);
|
1691 |
targetModel.setUniqueRecordingCalls(callStats[2]);
|
| - |
|
1692 |
targetModel.setTriedUniqueCalls(computeUniqueTriedCount(l1Logs));
|
| 987 |
|
1693 |
|
| 988 |
// Keep todayRemarks for movedToFuture calculation
|
1694 |
// Keep todayRemarks for movedToFuture calculation
|
| 989 |
List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
1695 |
List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
| 990 |
|
1696 |
|
| 991 |
// Moved to Future = Partners in Future Plan category who have a remark today
|
1697 |
// Moved to Future = Partners in Future Plan category who have a remark today
|
| Line 1133... |
Line 1839... |
| 1133 |
// Pure L2 (not also L1) — target is only L2 escalation
|
1839 |
// Pure L2 (not also L1) — target is only L2 escalation
|
| 1134 |
l2Model.setTodayTargetOfCall(l2TargetFofoIds.size());
|
1840 |
l2Model.setTodayTargetOfCall(l2TargetFofoIds.size());
|
| 1135 |
}
|
1841 |
}
|
| 1136 |
|
1842 |
|
| 1137 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
1843 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
| - |
|
1844 |
List<AgentCallLog> l2Logs = callLogsByAuthId.get((long) l2AuthId);
|
| 1138 |
long[] l2CallStats = getCallStatsFromLogs(callLogsByAuthId.get((long) l2AuthId), mobileToFofoIdMap);
|
1845 |
long[] l2CallStats = getCallStatsFromLogs(l2Logs, mobileToFofoIdMap);
|
| 1139 |
l2Model.setValueTargetAchieved(l2CallStats[0]);
|
1846 |
l2Model.setValueTargetAchieved(l2CallStats[0]);
|
| 1140 |
l2Model.setTotalRecordingCalls(l2CallStats[1]);
|
1847 |
l2Model.setTotalRecordingCalls(l2CallStats[1]);
|
| 1141 |
l2Model.setUniqueRecordingCalls(l2CallStats[2]);
|
1848 |
l2Model.setUniqueRecordingCalls(l2CallStats[2]);
|
| - |
|
1849 |
l2Model.setTriedUniqueCalls(computeUniqueTriedCount(l2Logs));
|
| 1142 |
|
1850 |
|
| 1143 |
l2Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l2AuthId, 0L));
|
1851 |
l2Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l2AuthId, 0L));
|
| 1144 |
rbmCallTargetModels.add(l2Model);
|
1852 |
rbmCallTargetModels.add(l2Model);
|
| 1145 |
}
|
1853 |
}
|
| 1146 |
|
1854 |
|
| Line 1174... |
Line 1882... |
| 1174 |
|
1882 |
|
| 1175 |
// L3 Target = partners with RBM_L3_ESCALATION as latest remark
|
1883 |
// L3 Target = partners with RBM_L3_ESCALATION as latest remark
|
| 1176 |
l3Model.setTodayTargetOfCall(l3TargetFofoIds.size());
|
1884 |
l3Model.setTodayTargetOfCall(l3TargetFofoIds.size());
|
| 1177 |
|
1885 |
|
| 1178 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
1886 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
| - |
|
1887 |
List<AgentCallLog> l3Logs = callLogsByAuthId.get((long) l3AuthId);
|
| 1179 |
long[] l3CallStats = getCallStatsFromLogs(callLogsByAuthId.get((long) l3AuthId), mobileToFofoIdMap);
|
1888 |
long[] l3CallStats = getCallStatsFromLogs(l3Logs, mobileToFofoIdMap);
|
| 1180 |
l3Model.setValueTargetAchieved(l3CallStats[0]);
|
1889 |
l3Model.setValueTargetAchieved(l3CallStats[0]);
|
| 1181 |
l3Model.setTotalRecordingCalls(l3CallStats[1]);
|
1890 |
l3Model.setTotalRecordingCalls(l3CallStats[1]);
|
| 1182 |
l3Model.setUniqueRecordingCalls(l3CallStats[2]);
|
1891 |
l3Model.setUniqueRecordingCalls(l3CallStats[2]);
|
| - |
|
1892 |
l3Model.setTriedUniqueCalls(computeUniqueTriedCount(l3Logs));
|
| 1183 |
|
1893 |
|
| 1184 |
l3Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l3AuthId, 0L));
|
1894 |
l3Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l3AuthId, 0L));
|
| 1185 |
rbmCallTargetModels.add(l3Model);
|
1895 |
rbmCallTargetModels.add(l3Model);
|
| 1186 |
}
|
1896 |
}
|
| 1187 |
|
1897 |
|
| Line 1362... |
Line 2072... |
| 1362 |
|
2072 |
|
| 1363 |
LOGGER.info("RBM Call Target - TOTAL TIME: {}ms, RBM count: {}", System.currentTimeMillis() - methodStart, sortedModels.size());
|
2073 |
LOGGER.info("RBM Call Target - TOTAL TIME: {}ms, RBM count: {}", System.currentTimeMillis() - methodStart, sortedModels.size());
|
| 1364 |
return sortedModels;
|
2074 |
return sortedModels;
|
| 1365 |
}
|
2075 |
}
|
| 1366 |
|
2076 |
|
| 1367 |
@Override
|
- |
|
| 1368 |
public List<OutOfSequenceDetailModel> getOutOfSequenceDetails(int authId) {
|
- |
|
| 1369 |
|
- |
|
| 1370 |
LocalDate today = LocalDate.now();
|
- |
|
| 1371 |
LocalDateTime start = today.atStartOfDay();
|
- |
|
| 1372 |
LocalDateTime end = today.plusDays(1).atStartOfDay();
|
- |
|
| 1373 |
|
- |
|
| 1374 |
List<RbmCallSequenceLog> logs =
|
- |
|
| 1375 |
rbmCallSequenceLogRepository.selectByAuthIdAndDateRange(authId, start, end);
|
- |
|
| 1376 |
|
- |
|
| 1377 |
Map<Integer, RbmCallSequenceLog> uniqueOosLogsByFofoId = new LinkedHashMap<>();
|
- |
|
| 1378 |
|
- |
|
| 1379 |
for (RbmCallSequenceLog log : logs) {
|
- |
|
| 1380 |
if (log.isOutOfSequence()) {
|
- |
|
| 1381 |
// Keep only the first occurrence per fofoId (latest entry since ordered by id DESC)
|
- |
|
| 1382 |
uniqueOosLogsByFofoId.putIfAbsent(log.getFofoId(), log);
|
- |
|
| 1383 |
}
|
- |
|
| 1384 |
}
|
- |
|
| 1385 |
|
- |
|
| 1386 |
if (uniqueOosLogsByFofoId.isEmpty()) {
|
- |
|
| 1387 |
return Collections.emptyList();
|
- |
|
| 1388 |
}
|
- |
|
| 1389 |
|
- |
|
| 1390 |
Set<Integer> fofoIds = uniqueOosLogsByFofoId.keySet();
|
- |
|
| 1391 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
- |
|
| 1392 |
try {
|
- |
|
| 1393 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
|
- |
|
| 1394 |
} catch (ProfitMandiBusinessException e) {
|
- |
|
| 1395 |
LOGGER.error("Error fetching fofo stores", e);
|
- |
|
| 1396 |
}
|
- |
|
| 1397 |
|
- |
|
| 1398 |
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
|
- |
|
| 1399 |
List<OutOfSequenceDetailModel> result = new ArrayList<>();
|
- |
|
| 1400 |
|
- |
|
| 1401 |
for (RbmCallSequenceLog log : uniqueOosLogsByFofoId.values()) {
|
- |
|
| 1402 |
CustomRetailer retailer = retailerMap.get(log.getFofoId());
|
- |
|
| 1403 |
String partyName = retailer != null
|
- |
|
| 1404 |
? retailer.getBusinessName()
|
- |
|
| 1405 |
: "Unknown (" + log.getFofoId() + ")";
|
- |
|
| 1406 |
String code = retailer != null
|
- |
|
| 1407 |
? retailer.getCode()
|
- |
|
| 1408 |
: "-";
|
- |
|
| 1409 |
|
- |
|
| 1410 |
String time = log.getCreateTimestamp() != null
|
- |
|
| 1411 |
? log.getCreateTimestamp().format(timeFormatter)
|
- |
|
| 1412 |
: "-";
|
- |
|
| 1413 |
|
- |
|
| 1414 |
result.add(new OutOfSequenceDetailModel(partyName, code, time));
|
- |
|
| 1415 |
}
|
- |
|
| 1416 |
|
- |
|
| 1417 |
return result;
|
- |
|
| 1418 |
}
|
- |
|
| 1419 |
|
- |
|
| 1420 |
@Override
|
- |
|
| 1421 |
public List<CalledPartnerDetailModel> getCalledPartnerDetails(int authId) throws ProfitMandiBusinessException {
|
- |
|
| 1422 |
return getCalledPartnerDetails(authId, LocalDate.now());
|
- |
|
| 1423 |
}
|
- |
|
| 1424 |
|
- |
|
| 1425 |
@Override
|
- |
|
| 1426 |
public List<CalledPartnerDetailModel> getCalledPartnerDetails(int authId, LocalDate date) throws ProfitMandiBusinessException {
|
- |
|
| 1427 |
// Get all call logs for this auth user on this date
|
- |
|
| 1428 |
LOGGER.info("getCalledPartnerDetails: authId={}, date={}", authId, date);
|
- |
|
| 1429 |
List<AgentCallLog> callLogs = agentCallLogRepository.findByAuthIdAndDate(authId, date);
|
- |
|
| 1430 |
LOGGER.info("Found {} call logs for authId={} on date={}", callLogs.size(), authId, date);
|
- |
|
| 1431 |
|
- |
|
| 1432 |
if (callLogs.isEmpty()) {
|
- |
|
| 1433 |
return Collections.emptyList();
|
- |
|
| 1434 |
}
|
- |
|
| 1435 |
|
- |
|
| 1436 |
// Build a map of normalized customer number -> fofoId
|
- |
|
| 1437 |
Map<String, Integer> customerToFofoIdMap = new HashMap<>();
|
- |
|
| 1438 |
Set<String> normalizedNumbers = new HashSet<>();
|
- |
|
| 1439 |
|
- |
|
| 1440 |
for (AgentCallLog callLog : callLogs) {
|
- |
|
| 1441 |
String customerNumber = callLog.getCustomerNumber();
|
- |
|
| 1442 |
if (customerNumber != null) {
|
- |
|
| 1443 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
- |
|
| 1444 |
normalizedNumbers.add(normalized);
|
- |
|
| 1445 |
}
|
- |
|
| 1446 |
}
|
- |
|
| 1447 |
|
- |
|
| 1448 |
// For each normalized number, find fofoId from retailer_contact first, then address
|
- |
|
| 1449 |
for (String mobile : normalizedNumbers) {
|
- |
|
| 1450 |
Integer fofoId = findFofoIdByMobile(mobile);
|
- |
|
| 1451 |
if (fofoId != null) {
|
- |
|
| 1452 |
customerToFofoIdMap.put(mobile, fofoId);
|
- |
|
| 1453 |
}
|
- |
|
| 1454 |
}
|
- |
|
| 1455 |
|
- |
|
| 1456 |
// Get unique fofoIds for retailer lookup
|
- |
|
| 1457 |
Set<Integer> fofoIds = new HashSet<>(customerToFofoIdMap.values());
|
- |
|
| 1458 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
- |
|
| 1459 |
if (!fofoIds.isEmpty()) {
|
- |
|
| 1460 |
try {
|
- |
|
| 1461 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
|
- |
|
| 1462 |
} catch (ProfitMandiBusinessException e) {
|
- |
|
| 1463 |
LOGGER.error("Error fetching fofo stores", e);
|
- |
|
| 1464 |
}
|
- |
|
| 1465 |
}
|
- |
|
| 1466 |
|
- |
|
| 1467 |
// Get today's remarks for these fofoIds
|
- |
|
| 1468 |
Map<Integer, List<PartnerCollectionRemark>> fofoRemarkMap = new HashMap<>();
|
- |
|
| 1469 |
if (!fofoIds.isEmpty()) {
|
- |
|
| 1470 |
List<PartnerCollectionRemark> todayRemarks = partnerCollectionRemarkRepository
|
- |
|
| 1471 |
.selectAllByFofoIdsOnDate(new ArrayList<>(fofoIds), date);
|
- |
|
| 1472 |
for (PartnerCollectionRemark remark : todayRemarks) {
|
- |
|
| 1473 |
fofoRemarkMap.computeIfAbsent(remark.getFofoId(), k -> new ArrayList<>()).add(remark);
|
- |
|
| 1474 |
}
|
- |
|
| 1475 |
}
|
- |
|
| 1476 |
|
- |
|
| 1477 |
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
|
- |
|
| 1478 |
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm a");
|
- |
|
| 1479 |
List<CalledPartnerDetailModel> result = new ArrayList<>();
|
- |
|
| 1480 |
|
- |
|
| 1481 |
for (com.spice.profitmandi.dao.entity.cs.AgentCallLog callLog : callLogs) {
|
- |
|
| 1482 |
String customerNumber = callLog.getCustomerNumber();
|
- |
|
| 1483 |
if (customerNumber == null) {
|
- |
|
| 1484 |
continue;
|
- |
|
| 1485 |
}
|
- |
|
| 1486 |
|
- |
|
| 1487 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
- |
|
| 1488 |
Integer fofoId = customerToFofoIdMap.get(normalized);
|
- |
|
| 1489 |
|
- |
|
| 1490 |
String partyName = "Unknown";
|
- |
|
| 1491 |
String code = "-";
|
- |
|
| 1492 |
|
- |
|
| 1493 |
if (fofoId != null) {
|
- |
|
| 1494 |
CustomRetailer retailer = retailerMap.get(fofoId);
|
- |
|
| 1495 |
if (retailer != null) {
|
- |
|
| 1496 |
partyName = retailer.getBusinessName();
|
- |
|
| 1497 |
code = retailer.getCode();
|
- |
|
| 1498 |
} else {
|
- |
|
| 1499 |
partyName = "Unknown (" + fofoId + ")";
|
- |
|
| 1500 |
}
|
- |
|
| 1501 |
} else {
|
- |
|
| 1502 |
partyName = "Unknown (" + normalized + ")";
|
- |
|
| 1503 |
}
|
- |
|
| 1504 |
|
- |
|
| 1505 |
// Get remark if available
|
- |
|
| 1506 |
String remarkValue = "-";
|
- |
|
| 1507 |
String messageValue = "-";
|
- |
|
| 1508 |
String remarkTime = "-";
|
- |
|
| 1509 |
|
- |
|
| 1510 |
if (fofoId != null && fofoRemarkMap.containsKey(fofoId)) {
|
- |
|
| 1511 |
List<PartnerCollectionRemark> remarks = fofoRemarkMap.get(fofoId);
|
- |
|
| 1512 |
if (!remarks.isEmpty()) {
|
- |
|
| 1513 |
PartnerCollectionRemark remark = remarks.get(0);
|
- |
|
| 1514 |
remarkValue = remark.getRemark() != null ? remark.getRemark().getValue() : "-";
|
- |
|
| 1515 |
messageValue = remark.getMessage() != null ? remark.getMessage() : "-";
|
- |
|
| 1516 |
remarkTime = remark.getCreateTimestamp() != null ? remark.getCreateTimestamp().format(timeFormatter) : "-";
|
- |
|
| 1517 |
}
|
- |
|
| 1518 |
}
|
- |
|
| 1519 |
|
- |
|
| 1520 |
// Build call log data
|
- |
|
| 1521 |
String recordingUrl = callLog.getRecordingUrl();
|
- |
|
| 1522 |
String callStatus = callLog.getCallStatus();
|
- |
|
| 1523 |
String callDuration = callLog.getCallDuration();
|
- |
|
| 1524 |
String callDateTime = null;
|
- |
|
| 1525 |
if (callLog.getCallDate() != null && callLog.getCallTime() != null) {
|
- |
|
| 1526 |
LocalDateTime callDateTimeObj = LocalDateTime.of(callLog.getCallDate(), callLog.getCallTime());
|
- |
|
| 1527 |
callDateTime = callDateTimeObj.format(dateTimeFormatter);
|
- |
|
| 1528 |
}
|
- |
|
| 1529 |
|
- |
|
| 1530 |
result.add(new CalledPartnerDetailModel(partyName, code, remarkValue, messageValue, remarkTime,
|
- |
|
| 1531 |
recordingUrl, callStatus, callDuration, callDateTime));
|
- |
|
| 1532 |
}
|
- |
|
| 1533 |
|
- |
|
| 1534 |
return result;
|
- |
|
| 1535 |
}
|
- |
|
| 1536 |
|
- |
|
| 1537 |
private Integer findFofoIdByMobile(String mobile) {
|
- |
|
| 1538 |
// First check retailer_contact
|
- |
|
| 1539 |
List<RetailerContact> contacts = retailerContactRepository.selectByMobile(mobile);
|
- |
|
| 1540 |
if (contacts != null && !contacts.isEmpty()) {
|
- |
|
| 1541 |
return contacts.get(0).getFofoId();
|
- |
|
| 1542 |
}
|
- |
|
| 1543 |
|
- |
|
| 1544 |
// Fallback to user.address
|
- |
|
| 1545 |
List<Address> addresses = addressRepository.selectAllByPhoneNumber(mobile);
|
- |
|
| 1546 |
if (addresses != null && !addresses.isEmpty()) {
|
- |
|
| 1547 |
return addresses.get(0).getRetaierId();
|
- |
|
| 1548 |
}
|
- |
|
| 1549 |
|
- |
|
| 1550 |
return null;
|
- |
|
| 1551 |
}
|
- |
|
| 1552 |
|
- |
|
| 1553 |
private List<CalledPartnerDetailModel> buildCalledPartnerResult(List<PartnerCollectionRemark> allRemarks) {
|
- |
|
| 1554 |
if (allRemarks.isEmpty()) {
|
- |
|
| 1555 |
return Collections.emptyList();
|
- |
|
| 1556 |
}
|
- |
|
| 1557 |
|
- |
|
| 1558 |
// Get unique fofoIds for retailer lookup
|
- |
|
| 1559 |
Set<Integer> fofoIds = allRemarks.stream()
|
- |
|
| 1560 |
.map(PartnerCollectionRemark::getFofoId)
|
- |
|
| 1561 |
.collect(Collectors.toSet());
|
- |
|
| 1562 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
- |
|
| 1563 |
try {
|
- |
|
| 1564 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
|
- |
|
| 1565 |
} catch (ProfitMandiBusinessException e) {
|
- |
|
| 1566 |
LOGGER.error("Error fetching fofo stores", e);
|
- |
|
| 1567 |
}
|
- |
|
| 1568 |
|
- |
|
| 1569 |
// Fetch call logs for remarks that have agentCallLogId
|
- |
|
| 1570 |
Map<Long, com.spice.profitmandi.dao.entity.cs.AgentCallLog> callLogMap = new HashMap<>();
|
- |
|
| 1571 |
try {
|
- |
|
| 1572 |
List<Long> callLogIds = allRemarks.stream()
|
- |
|
| 1573 |
.filter(r -> r.getAgentCallLogId() > 0)
|
- |
|
| 1574 |
.map(PartnerCollectionRemark::getAgentCallLogId)
|
- |
|
| 1575 |
.collect(Collectors.toList());
|
- |
|
| 1576 |
|
- |
|
| 1577 |
if (!callLogIds.isEmpty()) {
|
- |
|
| 1578 |
List<com.spice.profitmandi.dao.entity.cs.AgentCallLog> callLogs = agentCallLogRepository.findByIds(callLogIds);
|
- |
|
| 1579 |
if (callLogs != null) {
|
- |
|
| 1580 |
callLogMap = callLogs.stream()
|
- |
|
| 1581 |
.collect(Collectors.toMap(com.spice.profitmandi.dao.entity.cs.AgentCallLog::getId, c -> c, (a, b) -> a));
|
- |
|
| 1582 |
}
|
- |
|
| 1583 |
}
|
- |
|
| 1584 |
} catch (Exception e) {
|
- |
|
| 1585 |
LOGGER.error("Error fetching call logs by ids", e);
|
- |
|
| 1586 |
}
|
- |
|
| 1587 |
|
- |
|
| 1588 |
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
|
- |
|
| 1589 |
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm a");
|
- |
|
| 1590 |
List<CalledPartnerDetailModel> result = new ArrayList<>();
|
- |
|
| 1591 |
|
- |
|
| 1592 |
for (PartnerCollectionRemark remark : allRemarks) {
|
- |
|
| 1593 |
CustomRetailer retailer = retailerMap.get(remark.getFofoId());
|
- |
|
| 1594 |
String partyName = retailer != null
|
- |
|
| 1595 |
? retailer.getBusinessName()
|
- |
|
| 1596 |
: "Unknown (" + remark.getFofoId() + ")";
|
- |
|
| 1597 |
String code = retailer != null
|
- |
|
| 1598 |
? retailer.getCode()
|
- |
|
| 1599 |
: "-";
|
- |
|
| 1600 |
|
- |
|
| 1601 |
String remarkValue = remark.getRemark() != null
|
- |
|
| 1602 |
? remark.getRemark().getValue()
|
- |
|
| 1603 |
: "-";
|
- |
|
| 1604 |
|
- |
|
| 1605 |
String messageValue = remark.getMessage() != null
|
- |
|
| 1606 |
? remark.getMessage()
|
- |
|
| 1607 |
: "-";
|
- |
|
| 1608 |
|
- |
|
| 1609 |
String time = remark.getCreateTimestamp() != null
|
- |
|
| 1610 |
? remark.getCreateTimestamp().format(timeFormatter)
|
- |
|
| 1611 |
: "-";
|
- |
|
| 1612 |
|
- |
|
| 1613 |
// Get call log data if available
|
- |
|
| 1614 |
String recordingUrl = null;
|
- |
|
| 1615 |
String callStatus = null;
|
- |
|
| 1616 |
String callDuration = null;
|
- |
|
| 1617 |
String callDateTime = null;
|
- |
|
| 1618 |
|
- |
|
| 1619 |
try {
|
- |
|
| 1620 |
if (remark.getAgentCallLogId() > 0 && callLogMap.containsKey(remark.getAgentCallLogId())) {
|
- |
|
| 1621 |
com.spice.profitmandi.dao.entity.cs.AgentCallLog callLog = callLogMap.get(remark.getAgentCallLogId());
|
- |
|
| 1622 |
recordingUrl = callLog.getRecordingUrl();
|
- |
|
| 1623 |
callStatus = callLog.getCallStatus();
|
- |
|
| 1624 |
callDuration = callLog.getCallDuration();
|
- |
|
| 1625 |
if (callLog.getCallDate() != null && callLog.getCallTime() != null) {
|
- |
|
| 1626 |
LocalDateTime callDateTimeObj = LocalDateTime.of(callLog.getCallDate(), callLog.getCallTime());
|
- |
|
| 1627 |
callDateTime = callDateTimeObj.format(dateTimeFormatter);
|
- |
|
| 1628 |
}
|
- |
|
| 1629 |
}
|
- |
|
| 1630 |
} catch (Exception e) {
|
- |
|
| 1631 |
LOGGER.error("Error processing call log for remark id: {}", remark.getId(), e);
|
- |
|
| 1632 |
}
|
- |
|
| 1633 |
|
- |
|
| 1634 |
result.add(new CalledPartnerDetailModel(partyName, code, remarkValue, messageValue, time,
|
- |
|
| 1635 |
recordingUrl, callStatus, callDuration, callDateTime));
|
- |
|
| 1636 |
}
|
- |
|
| 1637 |
|
- |
|
| 1638 |
return result;
|
- |
|
| 1639 |
}
|
- |
|
| 1640 |
|
- |
|
| 1641 |
@Override
|
- |
|
| 1642 |
public List<List<String>> getRbmCallTargetRawDataByAuthId(int authId) throws Exception {
|
- |
|
| 1643 |
List<List<String>> rows = new ArrayList<>();
|
- |
|
| 1644 |
|
- |
|
| 1645 |
// Get auth user
|
- |
|
| 1646 |
List<AuthUser> authUsers = authRepository.selectByIds(Collections.singletonList(authId));
|
- |
|
| 1647 |
if (authUsers.isEmpty()) {
|
- |
|
| 1648 |
return rows;
|
- |
|
| 1649 |
}
|
- |
|
| 1650 |
AuthUser authUser = authUsers.get(0);
|
- |
|
| 1651 |
|
- |
|
| 1652 |
// Get positions to determine if L2
|
- |
|
| 1653 |
List<Position> positions = positionRepository.selectPositionByAuthIds(Collections.singletonList(authId));
|
- |
|
| 1654 |
boolean isL2 = positions.stream()
|
- |
|
| 1655 |
.anyMatch(p -> ProfitMandiConstants.TICKET_CATEGORY_RBM == p.getCategoryId()
|
- |
|
| 1656 |
&& EscalationType.L2.equals(p.getEscalationType()));
|
- |
|
| 1657 |
|
- |
|
| 1658 |
LocalDateTime startDate = LocalDate.now().atStartOfDay();
|
- |
|
| 1659 |
LocalDate firstOfMonth = LocalDate.now().withDayOfMonth(1);
|
- |
|
| 1660 |
LocalDate endOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).plusDays(1);
|
- |
|
| 1661 |
|
- |
|
| 1662 |
// Get fofo IDs from mtdBillingData (same source as Partner Count in getRbmCallTargetModels)
|
- |
|
| 1663 |
List<RbmWeeklyBillingModel> mtdBillingData = getWeeklyBillingDataForMonth(firstOfMonth, endOfMonth);
|
- |
|
| 1664 |
|
- |
|
| 1665 |
List<Integer> fofoIdList;
|
- |
|
| 1666 |
if (isL2) {
|
- |
|
| 1667 |
// L2: get fofo IDs from escalated tickets (same as getRbmCallTargetModels)
|
- |
|
| 1668 |
List<Ticket> escalatedTickets = ticketRepository.selectOpenEscalatedTicketsByAuthIds(Collections.singletonList(authId));
|
- |
|
| 1669 |
fofoIdList = escalatedTickets.stream()
|
- |
|
| 1670 |
.filter(t -> t.getL2AuthUser() == authId
|
- |
|
| 1671 |
|| t.getL3AuthUser() == authId
|
- |
|
| 1672 |
|| t.getL4AuthUser() == authId
|
- |
|
| 1673 |
|| t.getL5AuthUser() == authId)
|
- |
|
| 1674 |
.map(Ticket::getFofoId)
|
- |
|
| 1675 |
.distinct()
|
- |
|
| 1676 |
.collect(Collectors.toList());
|
- |
|
| 1677 |
} else {
|
- |
|
| 1678 |
// L1: get fofo IDs from mtdBillingData with isTargetedPartner (same as Partner Count)
|
- |
|
| 1679 |
fofoIdList = mtdBillingData.stream()
|
- |
|
| 1680 |
.filter(RbmWeeklyBillingModel::isTargetedPartner)
|
- |
|
| 1681 |
.filter(m -> m.getAuthId() == authId)
|
- |
|
| 1682 |
.map(RbmWeeklyBillingModel::getFofoId)
|
- |
|
| 1683 |
.distinct()
|
- |
|
| 1684 |
.collect(Collectors.toList());
|
- |
|
| 1685 |
}
|
- |
|
| 1686 |
|
- |
|
| 1687 |
if (fofoIdList.isEmpty()) {
|
- |
|
| 1688 |
return rows;
|
- |
|
| 1689 |
}
|
- |
|
| 1690 |
|
- |
|
| 1691 |
// MTD billed fofoIds for zero billing check
|
- |
|
| 1692 |
Set<Integer> mtdBilledFofoIds = mtdBillingData.stream()
|
- |
|
| 1693 |
.filter(RbmWeeklyBillingModel::isMtdBilled)
|
- |
|
| 1694 |
.map(RbmWeeklyBillingModel::getFofoId)
|
- |
|
| 1695 |
.collect(Collectors.toSet());
|
- |
|
| 1696 |
|
- |
|
| 1697 |
// Collection rank map for status calculation
|
- |
|
| 1698 |
Map<Integer, Integer> collectionRankMap = new HashMap<>();
|
- |
|
| 1699 |
try {
|
- |
|
| 1700 |
collectionRankMap = partnerCollectionService.getCollectionRankMap(fofoIdList, startDate);
|
- |
|
| 1701 |
} catch (ProfitMandiBusinessException e) {
|
- |
|
| 1702 |
LOGGER.error("Error fetching collection rank map", e);
|
- |
|
| 1703 |
}
|
- |
|
| 1704 |
|
- |
|
| 1705 |
// Resolve partner names/codes
|
- |
|
| 1706 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
- |
|
| 1707 |
if (!fofoIdList.isEmpty()) {
|
- |
|
| 1708 |
try {
|
- |
|
| 1709 |
retailerMap = retailerService.getFofoRetailers(fofoIdList);
|
- |
|
| 1710 |
} catch (ProfitMandiBusinessException e) {
|
- |
|
| 1711 |
LOGGER.error("Error fetching fofo retailers for raw data", e);
|
- |
|
| 1712 |
}
|
- |
|
| 1713 |
}
|
- |
|
| 1714 |
|
- |
|
| 1715 |
String rbmName = authUser.getFullName() + (isL2 ? " (L2)" : "");
|
- |
|
| 1716 |
|
- |
|
| 1717 |
// Build rows for ALL partners (same count as Partner Count)
|
- |
|
| 1718 |
for (Integer fofoId : fofoIdList) {
|
- |
|
| 1719 |
// Default to rank 5 (Normal) for partners without collection plan
|
- |
|
| 1720 |
int rank = collectionRankMap.getOrDefault(fofoId, 5);
|
- |
|
| 1721 |
boolean hasZeroBilling = !mtdBilledFofoIds.contains(fofoId);
|
- |
|
| 1722 |
|
- |
|
| 1723 |
// Status assignment with same priority as getRbmCallTargetModels
|
- |
|
| 1724 |
String status;
|
- |
|
| 1725 |
if (rank == 1) {
|
- |
|
| 1726 |
status = "Plan Today";
|
- |
|
| 1727 |
} else if (rank == 2) {
|
- |
|
| 1728 |
status = "Carry Forward";
|
- |
|
| 1729 |
} else if (hasZeroBilling) {
|
- |
|
| 1730 |
status = "Zero Billing";
|
- |
|
| 1731 |
} else if (rank == 3) {
|
- |
|
| 1732 |
status = "Untouched";
|
- |
|
| 1733 |
} else if (rank == 4) {
|
- |
|
| 1734 |
status = "Future Plan";
|
- |
|
| 1735 |
} else {
|
- |
|
| 1736 |
status = "Normal";
|
- |
|
| 1737 |
}
|
- |
|
| 1738 |
|
- |
|
| 1739 |
CustomRetailer retailer = retailerMap.get(fofoId);
|
- |
|
| 1740 |
String partnerName = retailer != null ? retailer.getBusinessName() : "Unknown (" + fofoId + ")";
|
- |
|
| 1741 |
String partnerCode = retailer != null ? retailer.getCode() : "-";
|
- |
|
| 1742 |
|
- |
|
| 1743 |
rows.add(Arrays.asList(partnerName, partnerCode, status, rbmName));
|
- |
|
| 1744 |
}
|
- |
|
| 1745 |
|
- |
|
| 1746 |
return rows;
|
- |
|
| 1747 |
}
|
- |
|
| 1748 |
|
- |
|
| 1749 |
@Override
|
- |
|
| 1750 |
public List<List<String>> getAllRbmCallTargetRawData() throws Exception {
|
- |
|
| 1751 |
List<List<String>> rows = new ArrayList<>();
|
- |
|
| 1752 |
|
- |
|
| 1753 |
// Get all L1 RBM positions (rows are emitted per L1 RBM).
|
- |
|
| 1754 |
// Call-log lookup happens against ALL callers via findAllByDate — so an L2/L3 or admin call
|
- |
|
| 1755 |
// still counts as "tried" for the partner.
|
- |
|
| 1756 |
List<Position> allRbmPositions = positionRepository
|
- |
|
| 1757 |
.selectPositionByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_RBM).stream()
|
- |
|
| 1758 |
.filter(x -> EscalationType.L1.equals(x.getEscalationType()))
|
- |
|
| 1759 |
.collect(Collectors.toList());
|
- |
|
| 1760 |
|
- |
|
| 1761 |
List<Integer> l1AuthIds = allRbmPositions.stream()
|
- |
|
| 1762 |
.map(Position::getAuthUserId).distinct().collect(Collectors.toList());
|
- |
|
| 1763 |
|
- |
|
| 1764 |
if (l1AuthIds.isEmpty()) {
|
- |
|
| 1765 |
return rows;
|
- |
|
| 1766 |
}
|
- |
|
| 1767 |
|
- |
|
| 1768 |
// Get auth user map
|
- |
|
| 1769 |
Map<Integer, AuthUser> authUserMap = authRepository.selectByIds(l1AuthIds).stream()
|
- |
|
| 1770 |
.collect(Collectors.toMap(AuthUser::getId, au -> au));
|
- |
|
| 1771 |
|
- |
|
| 1772 |
// Positions per auth user (needed early to build L1 partner assignments below)
|
- |
|
| 1773 |
Map<Integer, List<Position>> positionsByAuthId = positionRepository.selectPositionByAuthIds(l1AuthIds).stream()
|
- |
|
| 1774 |
.collect(Collectors.groupingBy(Position::getAuthUserId));
|
- |
|
| 1775 |
|
- |
|
| 1776 |
// Build L1 partner assignments from partner_position — SAME source the UI summary uses for
|
- |
|
| 1777 |
// Calling Target. storeGuyMap (email-based CS mapping) was returning smaller/different lists
|
- |
|
| 1778 |
// for users who are also L2, causing summary=43 but download=3 for Ebadullah.
|
- |
|
| 1779 |
Map<Integer, Integer> l1PositionIdToAuthId = new HashMap<>();
|
- |
|
| 1780 |
for (int rbmAuthId : l1AuthIds) {
|
- |
|
| 1781 |
List<Position> positions = positionsByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
- |
|
| 1782 |
for (Position p : positions) {
|
- |
|
| 1783 |
if (ProfitMandiConstants.TICKET_CATEGORY_RBM == p.getCategoryId()
|
- |
|
| 1784 |
&& EscalationType.L1.equals(p.getEscalationType())) {
|
- |
|
| 1785 |
l1PositionIdToAuthId.put(p.getId(), rbmAuthId);
|
- |
|
| 1786 |
}
|
- |
|
| 1787 |
}
|
- |
|
| 1788 |
}
|
- |
|
| 1789 |
|
- |
|
| 1790 |
Map<Integer, List<Integer>> rbmToFofoIdsMap = new HashMap<>();
|
- |
|
| 1791 |
Set<Integer> allFofoIds = new HashSet<>();
|
- |
|
| 1792 |
if (!l1PositionIdToAuthId.isEmpty()) {
|
- |
|
| 1793 |
List<com.spice.profitmandi.dao.entity.cs.PartnerPosition> allL1PPs =
|
- |
|
| 1794 |
partnerPositionRepository.selectByPositionIds(new ArrayList<>(l1PositionIdToAuthId.keySet()));
|
- |
|
| 1795 |
// Group by owning RBM; keep partners distinct within each RBM.
|
- |
|
| 1796 |
Map<Integer, Set<Integer>> perRbmSets = new HashMap<>();
|
- |
|
| 1797 |
for (com.spice.profitmandi.dao.entity.cs.PartnerPosition pp : allL1PPs) {
|
- |
|
| 1798 |
Integer ownerAuthId = l1PositionIdToAuthId.get(pp.getPositionId());
|
- |
|
| 1799 |
if (ownerAuthId == null) continue;
|
- |
|
| 1800 |
perRbmSets.computeIfAbsent(ownerAuthId, k -> new HashSet<>()).add(pp.getFofoId());
|
- |
|
| 1801 |
allFofoIds.add(pp.getFofoId());
|
- |
|
| 1802 |
}
|
- |
|
| 1803 |
for (Map.Entry<Integer, Set<Integer>> e : perRbmSets.entrySet()) {
|
- |
|
| 1804 |
rbmToFofoIdsMap.put(e.getKey(), new ArrayList<>(e.getValue()));
|
- |
|
| 1805 |
}
|
- |
|
| 1806 |
}
|
- |
|
| 1807 |
|
- |
|
| 1808 |
if (allFofoIds.isEmpty()) {
|
- |
|
| 1809 |
return rows;
|
- |
|
| 1810 |
}
|
- |
|
| 1811 |
|
- |
|
| 1812 |
// Get fofo stores for filtering and name resolution
|
- |
|
| 1813 |
Map<Integer, FofoStore> fofoStoresMap = new HashMap<>();
|
- |
|
| 1814 |
try {
|
- |
|
| 1815 |
fofoStoresMap = fofoStoreRepository.selectByRetailerIds(new ArrayList<>(allFofoIds)).stream()
|
- |
|
| 1816 |
.collect(Collectors.toMap(FofoStore::getId, x -> x, (a, b) -> a));
|
- |
|
| 1817 |
} catch (ProfitMandiBusinessException e) {
|
- |
|
| 1818 |
LOGGER.error("Error fetching fofo stores for all raw data", e);
|
- |
|
| 1819 |
}
|
- |
|
| 1820 |
|
- |
|
| 1821 |
// Batch fetch collection rank map
|
- |
|
| 1822 |
LocalDateTime startDate = LocalDate.now().atStartOfDay();
|
- |
|
| 1823 |
Map<Integer, Integer> allCollectionRankMap = new HashMap<>();
|
- |
|
| 1824 |
try {
|
- |
|
| 1825 |
allCollectionRankMap = partnerCollectionService.getCollectionRankMap(new ArrayList<>(allFofoIds), startDate);
|
- |
|
| 1826 |
} catch (ProfitMandiBusinessException e) {
|
- |
|
| 1827 |
LOGGER.error("Error fetching collection rank map for all raw data", e);
|
- |
|
| 1828 |
}
|
- |
|
| 1829 |
|
- |
|
| 1830 |
// MTD billing data
|
- |
|
| 1831 |
LocalDate firstOfMonth = LocalDate.now().withDayOfMonth(1);
|
- |
|
| 1832 |
LocalDate endOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).plusDays(1);
|
- |
|
| 1833 |
List<RbmWeeklyBillingModel> mtdBillingData = getWeeklyBillingDataForMonth(firstOfMonth, endOfMonth);
|
- |
|
| 1834 |
Set<Integer> allMtdBilledFofoIds = mtdBillingData.stream()
|
- |
|
| 1835 |
.filter(RbmWeeklyBillingModel::isMtdBilled)
|
- |
|
| 1836 |
.map(RbmWeeklyBillingModel::getFofoId)
|
- |
|
| 1837 |
.collect(Collectors.toSet());
|
- |
|
| 1838 |
|
- |
|
| 1839 |
// Batch fetch partner collection remarks for escalation filtering
|
- |
|
| 1840 |
Map<Integer, PartnerCollectionRemark> allPartnerCollectionRemarks = new HashMap<>();
|
- |
|
| 1841 |
if (!allFofoIds.isEmpty()) {
|
- |
|
| 1842 |
List<Integer> allRemarkIds = partnerCollectionRemarkRepository.selectMaxRemarkId(new ArrayList<>(allFofoIds));
|
- |
|
| 1843 |
if (!allRemarkIds.isEmpty()) {
|
- |
|
| 1844 |
allPartnerCollectionRemarks = partnerCollectionRemarkRepository.selectByIds(allRemarkIds).stream()
|
- |
|
| 1845 |
.collect(Collectors.toMap(PartnerCollectionRemark::getFofoId, x -> x, (a, b) -> a));
|
- |
|
| 1846 |
}
|
- |
|
| 1847 |
}
|
- |
|
| 1848 |
|
- |
|
| 1849 |
// Resolve partner names/codes in batch
|
- |
|
| 1850 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
- |
|
| 1851 |
try {
|
- |
|
| 1852 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(allFofoIds));
|
- |
|
| 1853 |
} catch (ProfitMandiBusinessException e) {
|
- |
|
| 1854 |
LOGGER.error("Error fetching fofo retailers for all raw data", e);
|
- |
|
| 1855 |
}
|
- |
|
| 1856 |
|
- |
|
| 1857 |
// Batch-fetch EVERY call log for today (across all callers, not just RBMs) and build a
|
- |
|
| 1858 |
// fofoId -> latest AgentCallLog map. We use findAllByDate here so that calls placed by
|
- |
|
| 1859 |
// admins, escalation agents, or anyone whose authId isn't in the RBM position table are
|
- |
|
| 1860 |
// still surfaced. Downstream we only read this map for fofoIds we actually emit, so
|
- |
|
| 1861 |
// extra entries are harmless.
|
- |
|
| 1862 |
Map<Integer, AgentCallLog> latestCallByFofo = new HashMap<>();
|
- |
|
| 1863 |
try {
|
- |
|
| 1864 |
List<AgentCallLog> todayCallLogs = agentCallLogRepository.findAllByDate(LocalDate.now());
|
- |
|
| 1865 |
Set<String> normalizedMobiles = new HashSet<>();
|
- |
|
| 1866 |
for (AgentCallLog log : todayCallLogs) {
|
- |
|
| 1867 |
if (log.getCustomerNumber() != null) {
|
- |
|
| 1868 |
String n = log.getCustomerNumber();
|
- |
|
| 1869 |
normalizedMobiles.add(n.startsWith("+91") ? n.substring(3) : n);
|
- |
|
| 1870 |
}
|
- |
|
| 1871 |
}
|
- |
|
| 1872 |
Map<String, Integer> mobileToFofoIdMap = buildMobileToFofoIdMap(normalizedMobiles);
|
- |
|
| 1873 |
for (AgentCallLog log : todayCallLogs) {
|
- |
|
| 1874 |
if (log.getCustomerNumber() == null) continue;
|
- |
|
| 1875 |
String n = log.getCustomerNumber();
|
- |
|
| 1876 |
String normalized = n.startsWith("+91") ? n.substring(3) : n;
|
- |
|
| 1877 |
Integer fofoId = mobileToFofoIdMap.get(normalized);
|
- |
|
| 1878 |
if (fofoId == null) continue;
|
- |
|
| 1879 |
AgentCallLog existing = latestCallByFofo.get(fofoId);
|
- |
|
| 1880 |
if (existing == null || (log.getId() != null && existing.getId() != null && log.getId() > existing.getId())) {
|
- |
|
| 1881 |
latestCallByFofo.put(fofoId, log);
|
- |
|
| 1882 |
}
|
- |
|
| 1883 |
}
|
- |
|
| 1884 |
} catch (Exception e) {
|
- |
|
| 1885 |
LOGGER.error("Error building latest call status map for all raw data", e);
|
- |
|
| 1886 |
}
|
- |
|
| 1887 |
|
- |
|
| 1888 |
// Process each L1 RBM
|
- |
|
| 1889 |
// Note: no cross-RBM dedup — a partner assigned to multiple RBMs appears once per RBM,
|
- |
|
| 1890 |
// matching the UI summary counts so row totals per RBM equal that RBM's "Calling Target".
|
- |
|
| 1891 |
for (int rbmAuthId : l1AuthIds) {
|
- |
|
| 1892 |
AuthUser authUser = authUserMap.get(rbmAuthId);
|
- |
|
| 1893 |
if (authUser == null) {
|
- |
|
| 1894 |
continue;
|
- |
|
| 1895 |
}
|
- |
|
| 1896 |
|
- |
|
| 1897 |
List<Integer> fofoIdList = rbmToFofoIdsMap.getOrDefault(rbmAuthId, Collections.emptyList());
|
- |
|
| 1898 |
if (fofoIdList.isEmpty()) {
|
- |
|
| 1899 |
continue; // No L1 partner assignments for this RBM
|
- |
|
| 1900 |
}
|
- |
|
| 1901 |
|
- |
|
| 1902 |
// Filter escalated partners for L1 RBMs
|
- |
|
| 1903 |
List<Position> positions = positionsByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
- |
|
| 1904 |
boolean isRBMAndL1 = positions.stream()
|
- |
|
| 1905 |
.anyMatch(position ->
|
- |
|
| 1906 |
ProfitMandiConstants.TICKET_CATEGORY_RBM == position.getCategoryId()
|
- |
|
| 1907 |
&& EscalationType.L1.equals(position.getEscalationType()));
|
- |
|
| 1908 |
|
- |
|
| 1909 |
List<Integer> fofoIds = fofoIdList;
|
- |
|
| 1910 |
if (isRBMAndL1) {
|
- |
|
| 1911 |
Map<Integer, PartnerCollectionRemark> partnerRemarks = new HashMap<>();
|
- |
|
| 1912 |
for (Integer fofoId : fofoIdList) {
|
- |
|
| 1913 |
if (allPartnerCollectionRemarks.containsKey(fofoId)) {
|
- |
|
| 1914 |
partnerRemarks.put(fofoId, allPartnerCollectionRemarks.get(fofoId));
|
- |
|
| 1915 |
}
|
- |
|
| 1916 |
}
|
- |
|
| 1917 |
Map<Integer, PartnerCollectionRemark> finalPartnerRemarks = partnerRemarks;
|
- |
|
| 1918 |
fofoIds = fofoIdList.stream()
|
- |
|
| 1919 |
.filter(fofoId -> {
|
- |
|
| 1920 |
if (!finalPartnerRemarks.containsKey(fofoId)) return true;
|
- |
|
| 1921 |
PartnerCollectionRemark pcr = finalPartnerRemarks.get(fofoId);
|
- |
|
| 1922 |
return !(CollectionRemark.RBM_L2_ESCALATION.equals(pcr.getRemark())
|
- |
|
| 1923 |
|| CollectionRemark.SALES_ESCALATION.equals(pcr.getRemark()));
|
- |
|
| 1924 |
})
|
- |
|
| 1925 |
.collect(Collectors.toList());
|
- |
|
| 1926 |
}
|
- |
|
| 1927 |
|
- |
|
| 1928 |
// Filter to only external, ACTIVE or REVIVAL stores
|
- |
|
| 1929 |
Map<Integer, FofoStore> finalFofoStoresMap = fofoStoresMap;
|
- |
|
| 1930 |
List<Integer> validFofoIds = fofoIds.stream()
|
- |
|
| 1931 |
.filter(fofoId -> {
|
- |
|
| 1932 |
FofoStore store = finalFofoStoresMap.get(fofoId);
|
- |
|
| 1933 |
if (store == null || store.isInternal()) return false;
|
- |
|
| 1934 |
return ActivationType.ACTIVE.equals(store.getActivationType())
|
- |
|
| 1935 |
|| ActivationType.REVIVAL.equals(store.getActivationType());
|
- |
|
| 1936 |
})
|
- |
|
| 1937 |
.collect(Collectors.toList());
|
- |
|
| 1938 |
|
- |
|
| 1939 |
String rbmName = authUser.getFullName();
|
- |
|
| 1940 |
|
- |
|
| 1941 |
// Categorize each partner and add a row per RBM assignment.
|
- |
|
| 1942 |
for (Integer fofoId : validFofoIds) {
|
- |
|
| 1943 |
int rank = allCollectionRankMap.getOrDefault(fofoId, 5);
|
- |
|
| 1944 |
boolean hasZeroBilling = !allMtdBilledFofoIds.contains(fofoId);
|
- |
|
| 1945 |
|
- |
|
| 1946 |
String status;
|
- |
|
| 1947 |
if (rank == 1) {
|
- |
|
| 1948 |
status = "Plan Today";
|
- |
|
| 1949 |
} else if (rank == 2) {
|
- |
|
| 1950 |
status = "Carry Forward";
|
- |
|
| 1951 |
} else if (hasZeroBilling) {
|
- |
|
| 1952 |
status = "Zero Billing";
|
- |
|
| 1953 |
} else if (rank == 3) {
|
- |
|
| 1954 |
status = "Untouched";
|
- |
|
| 1955 |
} else {
|
- |
|
| 1956 |
continue; // Skip Future Plan and Normal — only include calling target parties
|
- |
|
| 1957 |
}
|
- |
|
| 1958 |
|
- |
|
| 1959 |
CustomRetailer retailer = retailerMap.get(fofoId);
|
- |
|
| 1960 |
String partnerName = retailer != null ? retailer.getBusinessName() : "Unknown (" + fofoId + ")";
|
- |
|
| 1961 |
String partnerCode = retailer != null ? retailer.getCode() : "-";
|
- |
|
| 1962 |
|
- |
|
| 1963 |
// "Did Not Try" means the PARTNER has no call log today from anyone — not tied to this RBM.
|
- |
|
| 1964 |
// If any caller reached this partner, show the actual status.
|
- |
|
| 1965 |
String latestCallStatus = "Did Not Try";
|
- |
|
| 1966 |
AgentCallLog latestLog = latestCallByFofo.get(fofoId);
|
- |
|
| 1967 |
if (latestLog != null && latestLog.getCallStatus() != null && !latestLog.getCallStatus().isEmpty()) {
|
- |
|
| 1968 |
latestCallStatus = latestLog.getCallStatus();
|
- |
|
| 1969 |
}
|
- |
|
| 1970 |
|
- |
|
| 1971 |
rows.add(Arrays.asList(partnerName, partnerCode, status, rbmName, latestCallStatus));
|
- |
|
| 1972 |
}
|
- |
|
| 1973 |
}
|
- |
|
| 1974 |
|
- |
|
| 1975 |
return rows;
|
- |
|
| 1976 |
}
|
- |
|
| 1977 |
|
- |
|
| 1978 |
/**
|
- |
|
| 1979 |
* Get count of distinct partners called today based on call logs.
|
- |
|
| 1980 |
* Maps customerNumber from call log to fofoId using retailer_contact and address.
|
- |
|
| 1981 |
* If same fofoId is called multiple times, counts only once.
|
- |
|
| 1982 |
* Numbers without fofoId mapping are also counted (by distinct customer number).
|
- |
|
| 1983 |
*
|
- |
|
| 1984 |
* @param authId the RBM auth ID
|
- |
|
| 1985 |
* @return count of distinct partners/numbers called today
|
- |
|
| 1986 |
*/
|
- |
|
| 1987 |
public long getCalledCountFromCallLogs(long authId) {
|
- |
|
| 1988 |
return getCalledCountFromCallLogs(authId, LocalDate.now());
|
- |
|
| 1989 |
}
|
- |
|
| 1990 |
|
- |
|
| 1991 |
public long getCalledCountFromCallLogs(long authId, LocalDate date) {
|
- |
|
| 1992 |
return getCallStats(authId, date)[0];
|
- |
|
| 1993 |
}
|
- |
|
| 1994 |
|
- |
|
| 1995 |
/**
|
- |
|
| 1996 |
* Batch-builds a mobile (normalized, +91 stripped) -> fofoId mapping for all the given mobiles
|
- |
|
| 1997 |
* in just two queries (retailer_contact, then address fallback for unmapped numbers).
|
- |
|
| 1998 |
* Replaces the previous per-call-log findFofoIdByMobile() N+1 lookups.
|
- |
|
| 1999 |
*/
|
- |
|
| 2000 |
private Map<String, Integer> buildMobileToFofoIdMap(Set<String> normalizedMobiles) {
|
- |
|
| 2001 |
Map<String, Integer> result = new HashMap<>();
|
- |
|
| 2002 |
if (normalizedMobiles == null || normalizedMobiles.isEmpty()) {
|
- |
|
| 2003 |
return result;
|
- |
|
| 2004 |
}
|
- |
|
| 2005 |
List<String> mobilesList = new ArrayList<>(normalizedMobiles);
|
- |
|
| 2006 |
|
- |
|
| 2007 |
// First pass: retailer_contact
|
- |
|
| 2008 |
List<RetailerContact> contacts = retailerContactRepository.selectByMobiles(mobilesList);
|
- |
|
| 2009 |
for (RetailerContact rc : contacts) {
|
- |
|
| 2010 |
// Keep the first fofoId we see per mobile (matches old single-mobile behavior of get(0))
|
- |
|
| 2011 |
result.putIfAbsent(rc.getMobile(), rc.getFofoId());
|
- |
|
| 2012 |
}
|
- |
|
| 2013 |
|
- |
|
| 2014 |
// Second pass: address fallback for mobiles not yet mapped
|
- |
|
| 2015 |
List<String> unmapped = mobilesList.stream()
|
- |
|
| 2016 |
.filter(m -> !result.containsKey(m))
|
- |
|
| 2017 |
.collect(Collectors.toList());
|
- |
|
| 2018 |
if (!unmapped.isEmpty()) {
|
- |
|
| 2019 |
List<Address> addresses = addressRepository.selectAllByPhoneNumbers(unmapped);
|
- |
|
| 2020 |
for (Address addr : addresses) {
|
- |
|
| 2021 |
result.putIfAbsent(addr.getPhoneNumber(), addr.getRetaierId());
|
- |
|
| 2022 |
}
|
- |
|
| 2023 |
}
|
- |
|
| 2024 |
return result;
|
- |
|
| 2025 |
}
|
- |
|
| 2026 |
|
- |
|
| 2027 |
/**
|
2077 |
/**
|
| 2028 |
* In-memory variant of getCallStats() that uses pre-fetched call logs and mobile→fofoId mapping.
|
2078 |
* Rule: count each distinct customer number this RBM attempted today, EXCEPT
|
| 2029 |
* Used by the batch-fetched RBM Call Target loop to avoid N+1 query patterns.
|
2079 |
* numbers whose every log entry has callStatus "No Answer" — those count only
|
| - |
|
2080 |
* if there were 3 or more attempts (persistence credit). Recording presence is
|
| - |
|
2081 |
* not required. null/empty callStatus is treated as "not no-answer" (favours
|
| - |
|
2082 |
* counting when the vendor didn't classify the outcome).
|
| 2030 |
*/
|
2083 |
*/
|
| 2031 |
private long[] getCallStatsFromLogs(List<AgentCallLog> callLogs, Map<String, Integer> mobileToFofoIdMap) {
|
2084 |
private long computeUniqueTriedCount(List<AgentCallLog> callLogs) {
|
| 2032 |
if (callLogs == null || callLogs.isEmpty()) {
|
2085 |
if (callLogs == null || callLogs.isEmpty()) return 0L;
|
| 2033 |
return new long[]{0, 0, 0};
|
- |
|
| 2034 |
}
|
- |
|
| 2035 |
|
- |
|
| 2036 |
Set<Integer> calledFofoIds = new HashSet<>();
|
- |
|
| 2037 |
Set<String> calledNumbersWithoutFofoId = new HashSet<>();
|
- |
|
| 2038 |
long totalRecordingCalls = 0;
|
- |
|
| 2039 |
Set<String> uniqueRecordingNumbers = new HashSet<>();
|
2086 |
Map<String, List<AgentCallLog>> byNumber = new HashMap<>();
|
| 2040 |
|
- |
|
| 2041 |
for (AgentCallLog callLog : callLogs) {
|
2087 |
for (AgentCallLog log : callLogs) {
|
| 2042 |
String customerNumber = callLog.getCustomerNumber();
|
2088 |
String num = log.getCustomerNumber();
|
| 2043 |
if (customerNumber != null) {
|
2089 |
if (num == null) continue;
|
| 2044 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
2090 |
String normalized = num.startsWith("+91") ? num.substring(3) : num;
|
| 2045 |
Integer fofoId = mobileToFofoIdMap.get(normalized);
|
2091 |
byNumber.computeIfAbsent(normalized, k -> new ArrayList<>()).add(log);
|
| 2046 |
if (fofoId != null) {
|
2092 |
}
|
| 2047 |
calledFofoIds.add(fofoId);
|
- |
|
| 2048 |
} else {
|
2093 |
long count = 0L;
|
| 2049 |
calledNumbersWithoutFofoId.add(normalized);
|
2094 |
for (List<AgentCallLog> logs : byNumber.values()) {
|
| 2050 |
}
|
2095 |
boolean hasNonNoAnswer = false;
|
| 2051 |
|
- |
|
| 2052 |
if (callLog.getRecordingUrl() != null && !callLog.getRecordingUrl().isEmpty()
|
2096 |
for (AgentCallLog l : logs) {
|
| 2053 |
&& !"None".equalsIgnoreCase(callLog.getRecordingUrl())) {
|
2097 |
if (!isNoAnswerStatus(l.getCallStatus())) {
|
| 2054 |
totalRecordingCalls++;
|
2098 |
hasNonNoAnswer = true;
|
| 2055 |
uniqueRecordingNumbers.add(normalized);
|
2099 |
break;
|
| 2056 |
}
|
2100 |
}
|
| 2057 |
}
|
2101 |
}
|
| - |
|
2102 |
if (hasNonNoAnswer) count++;
|
| - |
|
2103 |
else if (logs.size() >= 3) count++;
|
| 2058 |
}
|
2104 |
}
|
| 2059 |
|
- |
|
| 2060 |
long calledCount = calledFofoIds.size() + calledNumbersWithoutFofoId.size();
|
- |
|
| 2061 |
return new long[]{calledCount, totalRecordingCalls, uniqueRecordingNumbers.size()};
|
2105 |
return count;
|
| 2062 |
}
|
2106 |
}
|
| 2063 |
|
2107 |
|
| 2064 |
/**
|
2108 |
/**
|
| 2065 |
* Returns call stats: [0] = called count, [1] = total recording calls, [2] = unique recording calls
|
2109 |
* Returns call stats: [0] = called count, [1] = total recording calls, [2] = unique recording calls
|
| 2066 |
*/
|
2110 |
*/
|