| Line 1333... |
Line 1333... |
| 1333 |
|
1333 |
|
| 1334 |
long calledCount = calledFofoIds.size() + calledNumbersWithoutFofoId.size();
|
1334 |
long calledCount = calledFofoIds.size() + calledNumbersWithoutFofoId.size();
|
| 1335 |
return new long[]{calledCount, totalRecordingCalls, uniqueRecordingNumbers.size()};
|
1335 |
return new long[]{calledCount, totalRecordingCalls, uniqueRecordingNumbers.size()};
|
| 1336 |
}
|
1336 |
}
|
| 1337 |
|
1337 |
|
| - |
|
1338 |
// Same rule as computeUniqueTriedCount but restricted to fofoIds in the target set.
|
| - |
|
1339 |
// Used to compute "TGT Calls" — how many of THIS RBM's Calling Target partners were
|
| - |
|
1340 |
// actually engaged today. Off-target activity is silently excluded.
|
| - |
|
1341 |
private long computeUniqueTriedCountForTargets(List<AgentCallLog> callLogs,
|
| - |
|
1342 |
Map<String, Integer> mobileToFofoIdMap,
|
| - |
|
1343 |
Set<Integer> targetFofoIds) {
|
| - |
|
1344 |
if (callLogs == null || callLogs.isEmpty()) return 0L;
|
| - |
|
1345 |
if (targetFofoIds == null || targetFofoIds.isEmpty()) return 0L;
|
| - |
|
1346 |
Map<Integer, List<AgentCallLog>> byFofoId = new HashMap<>();
|
| - |
|
1347 |
for (AgentCallLog log : callLogs) {
|
| - |
|
1348 |
String num = log.getCustomerNumber();
|
| - |
|
1349 |
if (num == null) continue;
|
| - |
|
1350 |
String normalized = num.startsWith("+91") ? num.substring(3) : num;
|
| - |
|
1351 |
Integer fofoId = mobileToFofoIdMap != null ? mobileToFofoIdMap.get(normalized) : null;
|
| - |
|
1352 |
if (fofoId == null) continue;
|
| - |
|
1353 |
if (!targetFofoIds.contains(fofoId)) continue; // off-target, skip
|
| - |
|
1354 |
byFofoId.computeIfAbsent(fofoId, k -> new ArrayList<>()).add(log);
|
| - |
|
1355 |
}
|
| - |
|
1356 |
long count = 0L;
|
| - |
|
1357 |
for (List<AgentCallLog> logs : byFofoId.values()) {
|
| - |
|
1358 |
boolean hasNonMissed = false;
|
| - |
|
1359 |
for (AgentCallLog l : logs) {
|
| - |
|
1360 |
if (!isMissedCustomerStatus(l.getCustomerStatus())) {
|
| - |
|
1361 |
hasNonMissed = true;
|
| - |
|
1362 |
break;
|
| - |
|
1363 |
}
|
| - |
|
1364 |
}
|
| - |
|
1365 |
if (hasNonMissed) count++;
|
| - |
|
1366 |
else if (logs.size() >= 3) count++;
|
| - |
|
1367 |
}
|
| - |
|
1368 |
return count;
|
| - |
|
1369 |
}
|
| - |
|
1370 |
|
| 1338 |
// Matches "Missed", "MISSED", "No Answer", "NO_ANSWER", "no-answer" etc.
|
1371 |
// Matches "Missed", "MISSED", "No Answer", "NO_ANSWER", "no-answer" etc.
|
| 1339 |
// Kommuno currently emits "Missed" — kept liberal so we're covered if the tag changes.
|
1372 |
// Kommuno currently emits "Missed" — kept liberal so we're covered if the tag changes.
|
| 1340 |
private static boolean isMissedCustomerStatus(String status) {
|
1373 |
private static boolean isMissedCustomerStatus(String status) {
|
| 1341 |
if (status == null) return false;
|
1374 |
if (status == null) return false;
|
| 1342 |
String s = status.trim().toLowerCase().replace('_', ' ').replace('-', ' ').replaceAll("\\s+", " ");
|
1375 |
String s = status.trim().toLowerCase().replace('_', ' ').replace('-', ' ').replaceAll("\\s+", " ");
|
| Line 1703... |
Line 1736... |
| 1703 |
List<AgentCallLog> l1Logs = callLogsByAuthId.get((long) rbmAuthId);
|
1736 |
List<AgentCallLog> l1Logs = callLogsByAuthId.get((long) rbmAuthId);
|
| 1704 |
long[] callStats = getCallStatsFromLogs(l1Logs, mobileToFofoIdMap);
|
1737 |
long[] callStats = getCallStatsFromLogs(l1Logs, mobileToFofoIdMap);
|
| 1705 |
targetModel.setValueTargetAchieved(callStats[0]);
|
1738 |
targetModel.setValueTargetAchieved(callStats[0]);
|
| 1706 |
targetModel.setTotalRecordingCalls(callStats[1]);
|
1739 |
targetModel.setTotalRecordingCalls(callStats[1]);
|
| 1707 |
targetModel.setUniqueRecordingCalls(callStats[2]);
|
1740 |
targetModel.setUniqueRecordingCalls(callStats[2]);
|
| 1708 |
targetModel.setTriedUniqueCalls(computeUniqueTriedCount(l1Logs));
|
1741 |
targetModel.setTriedUniqueCalls(computeUniqueTriedCount(l1Logs, mobileToFofoIdMap));
|
| - |
|
1742 |
targetModel.setTgtCalls(computeUniqueTriedCountForTargets(l1Logs, mobileToFofoIdMap, todayTargetPartners));
|
| 1709 |
|
1743 |
|
| 1710 |
// Keep todayRemarks for movedToFuture calculation
|
1744 |
// Keep todayRemarks for movedToFuture calculation
|
| 1711 |
List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
1745 |
List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
| 1712 |
|
1746 |
|
| 1713 |
// Moved to Future = Partners in Future Plan category who have a remark today
|
1747 |
// Moved to Future = Partners in Future Plan category who have a remark today
|
| Line 1737... |
Line 1771... |
| 1737 |
List<Integer> l2FofoIdList = l2AuthIdToFofoIds.getOrDefault(l2AuthId, Collections.emptyList());
|
1771 |
List<Integer> l2FofoIdList = l2AuthIdToFofoIds.getOrDefault(l2AuthId, Collections.emptyList());
|
| 1738 |
|
1772 |
|
| 1739 |
// For L2, use unique fofoIds with RBM_L2_ESCALATION remark as target
|
1773 |
// For L2, use unique fofoIds with RBM_L2_ESCALATION remark as target
|
| 1740 |
Set<Integer> l2TargetFofoIds = new HashSet<>(l2FofoIdList);
|
1774 |
Set<Integer> l2TargetFofoIds = new HashSet<>(l2FofoIdList);
|
| 1741 |
|
1775 |
|
| - |
|
1776 |
// Collected from the L1-merge branch below when the user is also L1. Used later
|
| - |
|
1777 |
// for the TGT Calls scope: L2 escalation + L1 target buckets.
|
| - |
|
1778 |
Set<Integer> l1MergedTargetPartners = new HashSet<>();
|
| - |
|
1779 |
|
| 1742 |
RbmCallTargetModel l2Model = new RbmCallTargetModel();
|
1780 |
RbmCallTargetModel l2Model = new RbmCallTargetModel();
|
| 1743 |
l2Model.setAuthId(l2AuthId);
|
1781 |
l2Model.setAuthId(l2AuthId);
|
| 1744 |
l2Model.setRbmName(authUser.getFullName() + " (L2)");
|
1782 |
l2Model.setRbmName(authUser.getFullName() + " (L2)");
|
| 1745 |
l2Model.setL2Position(true);
|
1783 |
l2Model.setL2Position(true);
|
| 1746 |
l2Model.setL2CallingList(l2TargetFofoIds.size());
|
1784 |
l2Model.setL2CallingList(l2TargetFofoIds.size());
|
| Line 1837... |
Line 1875... |
| 1837 |
l2Model.setRevival(revivalPartners.size());
|
1875 |
l2Model.setRevival(revivalPartners.size());
|
| 1838 |
|
1876 |
|
| 1839 |
long l1OwnTarget = planTodayPartners.size() + carryForwardPartners.size()
|
1877 |
long l1OwnTarget = planTodayPartners.size() + carryForwardPartners.size()
|
| 1840 |
+ zeroBillingPartners.size() + untouchedPartners.size();
|
1878 |
+ zeroBillingPartners.size() + untouchedPartners.size();
|
| 1841 |
|
1879 |
|
| - |
|
1880 |
// Feed the L1 target sets up to the outer scope so TGT Calls can include them.
|
| - |
|
1881 |
l1MergedTargetPartners.addAll(planTodayPartners);
|
| - |
|
1882 |
l1MergedTargetPartners.addAll(carryForwardPartners);
|
| - |
|
1883 |
l1MergedTargetPartners.addAll(zeroBillingPartners);
|
| - |
|
1884 |
l1MergedTargetPartners.addAll(untouchedPartners);
|
| - |
|
1885 |
|
| 1842 |
// Today Target = own L1 target + L2 escalation
|
1886 |
// Today Target = own L1 target + L2 escalation
|
| 1843 |
l2Model.setTodayTargetOfCall(l2TargetFofoIds.size() + l1OwnTarget);
|
1887 |
l2Model.setTodayTargetOfCall(l2TargetFofoIds.size() + l1OwnTarget);
|
| 1844 |
|
1888 |
|
| 1845 |
// Moved to Future from L1 partners
|
1889 |
// Moved to Future from L1 partners
|
| 1846 |
List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(l2AuthId, Collections.emptyList());
|
1890 |
List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(l2AuthId, Collections.emptyList());
|
| Line 1860... |
Line 1904... |
| 1860 |
List<AgentCallLog> l2Logs = callLogsByAuthId.get((long) l2AuthId);
|
1904 |
List<AgentCallLog> l2Logs = callLogsByAuthId.get((long) l2AuthId);
|
| 1861 |
long[] l2CallStats = getCallStatsFromLogs(l2Logs, mobileToFofoIdMap);
|
1905 |
long[] l2CallStats = getCallStatsFromLogs(l2Logs, mobileToFofoIdMap);
|
| 1862 |
l2Model.setValueTargetAchieved(l2CallStats[0]);
|
1906 |
l2Model.setValueTargetAchieved(l2CallStats[0]);
|
| 1863 |
l2Model.setTotalRecordingCalls(l2CallStats[1]);
|
1907 |
l2Model.setTotalRecordingCalls(l2CallStats[1]);
|
| 1864 |
l2Model.setUniqueRecordingCalls(l2CallStats[2]);
|
1908 |
l2Model.setUniqueRecordingCalls(l2CallStats[2]);
|
| 1865 |
l2Model.setTriedUniqueCalls(computeUniqueTriedCount(l2Logs));
|
1909 |
l2Model.setTriedUniqueCalls(computeUniqueTriedCount(l2Logs, mobileToFofoIdMap));
|
| - |
|
1910 |
|
| - |
|
1911 |
// TGT Calls scope: L2 escalation + (if user is also L1) the L1 target buckets.
|
| - |
|
1912 |
Set<Integer> l2TgtSet = new HashSet<>(l2TargetFofoIds);
|
| - |
|
1913 |
l2TgtSet.addAll(l1MergedTargetPartners);
|
| - |
|
1914 |
l2Model.setTgtCalls(computeUniqueTriedCountForTargets(l2Logs, mobileToFofoIdMap, l2TgtSet));
|
| 1866 |
|
1915 |
|
| 1867 |
l2Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l2AuthId, 0L));
|
1916 |
l2Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l2AuthId, 0L));
|
| 1868 |
rbmCallTargetModels.add(l2Model);
|
1917 |
rbmCallTargetModels.add(l2Model);
|
| 1869 |
}
|
1918 |
}
|
| 1870 |
|
1919 |
|
| Line 1903... |
Line 1952... |
| 1903 |
List<AgentCallLog> l3Logs = callLogsByAuthId.get((long) l3AuthId);
|
1952 |
List<AgentCallLog> l3Logs = callLogsByAuthId.get((long) l3AuthId);
|
| 1904 |
long[] l3CallStats = getCallStatsFromLogs(l3Logs, mobileToFofoIdMap);
|
1953 |
long[] l3CallStats = getCallStatsFromLogs(l3Logs, mobileToFofoIdMap);
|
| 1905 |
l3Model.setValueTargetAchieved(l3CallStats[0]);
|
1954 |
l3Model.setValueTargetAchieved(l3CallStats[0]);
|
| 1906 |
l3Model.setTotalRecordingCalls(l3CallStats[1]);
|
1955 |
l3Model.setTotalRecordingCalls(l3CallStats[1]);
|
| 1907 |
l3Model.setUniqueRecordingCalls(l3CallStats[2]);
|
1956 |
l3Model.setUniqueRecordingCalls(l3CallStats[2]);
|
| 1908 |
l3Model.setTriedUniqueCalls(computeUniqueTriedCount(l3Logs));
|
1957 |
l3Model.setTriedUniqueCalls(computeUniqueTriedCount(l3Logs, mobileToFofoIdMap));
|
| - |
|
1958 |
// L3 TGT Calls scope: L3 escalation partners.
|
| - |
|
1959 |
l3Model.setTgtCalls(computeUniqueTriedCountForTargets(l3Logs, mobileToFofoIdMap, l3TargetFofoIds));
|
| 1909 |
|
1960 |
|
| 1910 |
l3Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l3AuthId, 0L));
|
1961 |
l3Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l3AuthId, 0L));
|
| 1911 |
rbmCallTargetModels.add(l3Model);
|
1962 |
rbmCallTargetModels.add(l3Model);
|
| 1912 |
}
|
1963 |
}
|
| 1913 |
|
1964 |
|
| Line 2098... |
Line 2149... |
| 2098 |
* - Else (every call to this number was Missed) → count only if attempts >= 3
|
2149 |
* - Else (every call to this number was Missed) → count only if attempts >= 3
|
| 2099 |
* (persistence credit).
|
2150 |
* (persistence credit).
|
| 2100 |
* null/empty customerStatus is treated as "not Missed" (favours counting when
|
2151 |
* null/empty customerStatus is treated as "not Missed" (favours counting when
|
| 2101 |
* the vendor didn't classify the outcome).
|
2152 |
* the vendor didn't classify the outcome).
|
| 2102 |
*/
|
2153 |
*/
|
| 2103 |
private long computeUniqueTriedCount(List<AgentCallLog> callLogs) {
|
2154 |
private long computeUniqueTriedCount(List<AgentCallLog> callLogs,
|
| - |
|
2155 |
Map<String, Integer> mobileToFofoIdMap) {
|
| 2104 |
if (callLogs == null || callLogs.isEmpty()) return 0L;
|
2156 |
if (callLogs == null || callLogs.isEmpty()) return 0L;
|
| - |
|
2157 |
// Group by fofoId (not raw number) — numbers that don't resolve to a partner via
|
| - |
|
2158 |
// retailer_contact/address are skipped. Once a number gets added to retailer_contact
|
| - |
|
2159 |
// it'll automatically resolve and start counting on the next page load.
|
| 2105 |
Map<String, List<AgentCallLog>> byNumber = new HashMap<>();
|
2160 |
Map<Integer, List<AgentCallLog>> byFofoId = new HashMap<>();
|
| 2106 |
for (AgentCallLog log : callLogs) {
|
2161 |
for (AgentCallLog log : callLogs) {
|
| 2107 |
String num = log.getCustomerNumber();
|
2162 |
String num = log.getCustomerNumber();
|
| 2108 |
if (num == null) continue;
|
2163 |
if (num == null) continue;
|
| 2109 |
String normalized = num.startsWith("+91") ? num.substring(3) : num;
|
2164 |
String normalized = num.startsWith("+91") ? num.substring(3) : num;
|
| - |
|
2165 |
Integer fofoId = mobileToFofoIdMap != null ? mobileToFofoIdMap.get(normalized) : null;
|
| - |
|
2166 |
if (fofoId == null) continue; // unknown number — excluded from Tried
|
| 2110 |
byNumber.computeIfAbsent(normalized, k -> new ArrayList<>()).add(log);
|
2167 |
byFofoId.computeIfAbsent(fofoId, k -> new ArrayList<>()).add(log);
|
| 2111 |
}
|
2168 |
}
|
| 2112 |
long count = 0L;
|
2169 |
long count = 0L;
|
| 2113 |
for (List<AgentCallLog> logs : byNumber.values()) {
|
2170 |
for (List<AgentCallLog> logs : byFofoId.values()) {
|
| 2114 |
boolean hasNonMissed = false;
|
2171 |
boolean hasNonMissed = false;
|
| 2115 |
for (AgentCallLog l : logs) {
|
2172 |
for (AgentCallLog l : logs) {
|
| 2116 |
if (!isMissedCustomerStatus(l.getCustomerStatus())) {
|
2173 |
if (!isMissedCustomerStatus(l.getCustomerStatus())) {
|
| 2117 |
hasNonMissed = true;
|
2174 |
hasNonMissed = true;
|
| 2118 |
break;
|
2175 |
break;
|