| Line 1326... |
Line 1326... |
| 1326 |
|
1326 |
|
| 1327 |
long calledCount = calledFofoIds.size() + calledNumbersWithoutFofoId.size();
|
1327 |
long calledCount = calledFofoIds.size() + calledNumbersWithoutFofoId.size();
|
| 1328 |
return new long[]{calledCount, totalRecordingCalls, uniqueRecordingNumbers.size()};
|
1328 |
return new long[]{calledCount, totalRecordingCalls, uniqueRecordingNumbers.size()};
|
| 1329 |
}
|
1329 |
}
|
| 1330 |
|
1330 |
|
| - |
|
1331 |
// Matches "Missed", "MISSED", "No Answer", "NO_ANSWER", "no-answer" etc.
|
| - |
|
1332 |
// Kommuno currently emits "Missed" — kept liberal so we're covered if the tag changes.
|
| - |
|
1333 |
private static boolean isMissedCustomerStatus(String status) {
|
| - |
|
1334 |
if (status == null) return false;
|
| - |
|
1335 |
String s = status.trim().toLowerCase().replace('_', ' ').replace('-', ' ').replaceAll("\\s+", " ");
|
| - |
|
1336 |
return s.equals("missed") || s.equals("no answer") || s.equals("noanswer");
|
| - |
|
1337 |
}
|
| - |
|
1338 |
|
| 1331 |
public List<RbmCallTargetModel> getRbmCallTargetModels(LocalDate queryDate) throws Exception {
|
1339 |
public List<RbmCallTargetModel> getRbmCallTargetModels(LocalDate queryDate) throws Exception {
|
| 1332 |
long methodStart = System.currentTimeMillis();
|
1340 |
long methodStart = System.currentTimeMillis();
|
| 1333 |
List<RbmCallTargetModel> rbmCallTargetModels = new ArrayList<>();
|
1341 |
List<RbmCallTargetModel> rbmCallTargetModels = new ArrayList<>();
|
| 1334 |
|
1342 |
|
| 1335 |
// Get all RBM positions (L1 and L2)
|
1343 |
// Get all RBM positions (L1 and L2)
|
| Line 1687... |
Line 1695... |
| 1687 |
List<AgentCallLog> l1Logs = callLogsByAuthId.get((long) rbmAuthId);
|
1695 |
List<AgentCallLog> l1Logs = callLogsByAuthId.get((long) rbmAuthId);
|
| 1688 |
long[] callStats = getCallStatsFromLogs(l1Logs, mobileToFofoIdMap);
|
1696 |
long[] callStats = getCallStatsFromLogs(l1Logs, mobileToFofoIdMap);
|
| 1689 |
targetModel.setValueTargetAchieved(callStats[0]);
|
1697 |
targetModel.setValueTargetAchieved(callStats[0]);
|
| 1690 |
targetModel.setTotalRecordingCalls(callStats[1]);
|
1698 |
targetModel.setTotalRecordingCalls(callStats[1]);
|
| 1691 |
targetModel.setUniqueRecordingCalls(callStats[2]);
|
1699 |
targetModel.setUniqueRecordingCalls(callStats[2]);
|
| - |
|
1700 |
targetModel.setTriedUniqueCalls(computeUniqueTriedCount(l1Logs));
|
| 1692 |
|
1701 |
|
| 1693 |
// Keep todayRemarks for movedToFuture calculation AND for Tried calculation
|
1702 |
// Keep todayRemarks for movedToFuture calculation
|
| 1694 |
List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
1703 |
List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
| 1695 |
targetModel.setTriedUniqueCalls(computeUniqueTriedCount(l1Logs, mobileToFofoIdMap, todayRemarks));
|
- |
|
| 1696 |
|
1704 |
|
| 1697 |
// Moved to Future = Partners in Future Plan category who have a remark today
|
1705 |
// Moved to Future = Partners in Future Plan category who have a remark today
|
| 1698 |
// These are partners who were contacted today but moved to a future date
|
1706 |
// These are partners who were contacted today but moved to a future date
|
| 1699 |
Set<Integer> todayRemarkedFofoIds = todayRemarks.stream()
|
1707 |
Set<Integer> todayRemarkedFofoIds = todayRemarks.stream()
|
| 1700 |
.map(PartnerCollectionRemark::getFofoId)
|
1708 |
.map(PartnerCollectionRemark::getFofoId)
|
| Line 1844... |
Line 1852... |
| 1844 |
List<AgentCallLog> l2Logs = callLogsByAuthId.get((long) l2AuthId);
|
1852 |
List<AgentCallLog> l2Logs = callLogsByAuthId.get((long) l2AuthId);
|
| 1845 |
long[] l2CallStats = getCallStatsFromLogs(l2Logs, mobileToFofoIdMap);
|
1853 |
long[] l2CallStats = getCallStatsFromLogs(l2Logs, mobileToFofoIdMap);
|
| 1846 |
l2Model.setValueTargetAchieved(l2CallStats[0]);
|
1854 |
l2Model.setValueTargetAchieved(l2CallStats[0]);
|
| 1847 |
l2Model.setTotalRecordingCalls(l2CallStats[1]);
|
1855 |
l2Model.setTotalRecordingCalls(l2CallStats[1]);
|
| 1848 |
l2Model.setUniqueRecordingCalls(l2CallStats[2]);
|
1856 |
l2Model.setUniqueRecordingCalls(l2CallStats[2]);
|
| 1849 |
l2Model.setTriedUniqueCalls(computeUniqueTriedCount(l2Logs, mobileToFofoIdMap,
|
1857 |
l2Model.setTriedUniqueCalls(computeUniqueTriedCount(l2Logs));
|
| 1850 |
remarksByAuthId.getOrDefault(l2AuthId, Collections.emptyList())));
|
- |
|
| 1851 |
|
1858 |
|
| 1852 |
l2Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l2AuthId, 0L));
|
1859 |
l2Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l2AuthId, 0L));
|
| 1853 |
rbmCallTargetModels.add(l2Model);
|
1860 |
rbmCallTargetModels.add(l2Model);
|
| 1854 |
}
|
1861 |
}
|
| 1855 |
|
1862 |
|
| Line 1888... |
Line 1895... |
| 1888 |
List<AgentCallLog> l3Logs = callLogsByAuthId.get((long) l3AuthId);
|
1895 |
List<AgentCallLog> l3Logs = callLogsByAuthId.get((long) l3AuthId);
|
| 1889 |
long[] l3CallStats = getCallStatsFromLogs(l3Logs, mobileToFofoIdMap);
|
1896 |
long[] l3CallStats = getCallStatsFromLogs(l3Logs, mobileToFofoIdMap);
|
| 1890 |
l3Model.setValueTargetAchieved(l3CallStats[0]);
|
1897 |
l3Model.setValueTargetAchieved(l3CallStats[0]);
|
| 1891 |
l3Model.setTotalRecordingCalls(l3CallStats[1]);
|
1898 |
l3Model.setTotalRecordingCalls(l3CallStats[1]);
|
| 1892 |
l3Model.setUniqueRecordingCalls(l3CallStats[2]);
|
1899 |
l3Model.setUniqueRecordingCalls(l3CallStats[2]);
|
| 1893 |
l3Model.setTriedUniqueCalls(computeUniqueTriedCount(l3Logs, mobileToFofoIdMap,
|
1900 |
l3Model.setTriedUniqueCalls(computeUniqueTriedCount(l3Logs));
|
| 1894 |
remarksByAuthId.getOrDefault(l3AuthId, Collections.emptyList())));
|
- |
|
| 1895 |
|
1901 |
|
| 1896 |
l3Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l3AuthId, 0L));
|
1902 |
l3Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l3AuthId, 0L));
|
| 1897 |
rbmCallTargetModels.add(l3Model);
|
1903 |
rbmCallTargetModels.add(l3Model);
|
| 1898 |
}
|
1904 |
}
|
| 1899 |
|
1905 |
|
| Line 2076... |
Line 2082... |
| 2076 |
return sortedModels;
|
2082 |
return sortedModels;
|
| 2077 |
}
|
2083 |
}
|
| 2078 |
|
2084 |
|
| 2079 |
/**
|
2085 |
/**
|
| 2080 |
* Rule: count each distinct customer number this RBM attempted today, based on
|
2086 |
* Rule: count each distinct customer number this RBM attempted today, based on
|
| 2081 |
* post-call REMARKS (PartnerCollectionRemark), not the vendor callStatus:
|
2087 |
* the vendor-reported customer-side outcome (AgentCallLog.customerStatus):
|
| 2082 |
* - If the RBM logged ANY remark other than NO_ANSWER for this partner today
|
2088 |
* - If ANY call to this number has customerStatus that is not "Missed"
|
| 2083 |
* → count as 1 (customer was reached / tagged with a real outcome).
|
2089 |
* → count as 1 (customer was reached at some point).
|
| 2084 |
* - Else if the RBM has NO remark row at all for this partner today → count
|
- |
|
| 2085 |
* as 1 (treat missing remark as "not tagged no-answer" — matches the "-"
|
- |
|
| 2086 |
* rows shown in the Called Partners modal which count as tried).
|
- |
|
| 2087 |
* - Else (every remark is NO_ANSWER) → count only if attempts >= 3
|
2090 |
* - Else (every call to this number was Missed) → count only if attempts >= 3
|
| 2088 |
* (persistence credit).
|
2091 |
* (persistence credit).
|
| 2089 |
* Unknown numbers (no fofoId mapping) always count as 1 — no remark to check.
|
2092 |
* null/empty customerStatus is treated as "not Missed" (favours counting when
|
| - |
|
2093 |
* the vendor didn't classify the outcome).
|
| 2090 |
*/
|
2094 |
*/
|
| 2091 |
private long computeUniqueTriedCount(List<AgentCallLog> callLogs,
|
2095 |
private long computeUniqueTriedCount(List<AgentCallLog> callLogs) {
|
| 2092 |
Map<String, Integer> mobileToFofoIdMap,
|
- |
|
| 2093 |
List<PartnerCollectionRemark> rbmRemarksToday) {
|
- |
|
| 2094 |
if (callLogs == null || callLogs.isEmpty()) return 0L;
|
2096 |
if (callLogs == null || callLogs.isEmpty()) return 0L;
|
| 2095 |
|
- |
|
| 2096 |
// Build fofoId -> remarks (only the current RBM's remarks today are in scope).
|
- |
|
| 2097 |
Map<Integer, List<PartnerCollectionRemark>> remarksByFofoId = new HashMap<>();
|
- |
|
| 2098 |
if (rbmRemarksToday != null) {
|
- |
|
| 2099 |
for (PartnerCollectionRemark r : rbmRemarksToday) {
|
- |
|
| 2100 |
remarksByFofoId.computeIfAbsent(r.getFofoId(), k -> new ArrayList<>()).add(r);
|
- |
|
| 2101 |
}
|
- |
|
| 2102 |
}
|
- |
|
| 2103 |
|
- |
|
| 2104 |
// Attempts (call count) per fofoId, or per raw number if unresolved.
|
- |
|
| 2105 |
Map<Integer, Integer> attemptsByFofo = new HashMap<>();
|
2097 |
Map<String, List<AgentCallLog>> byNumber = new HashMap<>();
|
| 2106 |
Set<String> unresolvedNumbers = new HashSet<>();
|
- |
|
| 2107 |
for (AgentCallLog log : callLogs) {
|
2098 |
for (AgentCallLog log : callLogs) {
|
| 2108 |
String num = log.getCustomerNumber();
|
2099 |
String num = log.getCustomerNumber();
|
| 2109 |
if (num == null) continue;
|
2100 |
if (num == null) continue;
|
| 2110 |
String normalized = num.startsWith("+91") ? num.substring(3) : num;
|
2101 |
String normalized = num.startsWith("+91") ? num.substring(3) : num;
|
| 2111 |
Integer fofoId = mobileToFofoIdMap != null ? mobileToFofoIdMap.get(normalized) : null;
|
2102 |
byNumber.computeIfAbsent(normalized, k -> new ArrayList<>()).add(log);
|
| 2112 |
if (fofoId != null) {
|
- |
|
| 2113 |
attemptsByFofo.merge(fofoId, 1, Integer::sum);
|
- |
|
| 2114 |
} else {
|
- |
|
| 2115 |
unresolvedNumbers.add(normalized);
|
- |
|
| 2116 |
}
|
- |
|
| 2117 |
}
|
2103 |
}
|
| 2118 |
|
- |
|
| 2119 |
long count = 0L;
|
2104 |
long count = 0L;
|
| 2120 |
for (Map.Entry<Integer, Integer> e : attemptsByFofo.entrySet()) {
|
2105 |
for (List<AgentCallLog> logs : byNumber.values()) {
|
| 2121 |
int fofoId = e.getKey();
|
- |
|
| 2122 |
int attempts = e.getValue();
|
- |
|
| 2123 |
List<PartnerCollectionRemark> remarks = remarksByFofoId.getOrDefault(fofoId, Collections.emptyList());
|
- |
|
| 2124 |
if (remarks.isEmpty()) {
|
- |
|
| 2125 |
// No remark logged today for this fofoId — counts.
|
- |
|
| 2126 |
count++;
|
- |
|
| 2127 |
continue;
|
- |
|
| 2128 |
}
|
- |
|
| 2129 |
boolean hasNonNoAnswer = false;
|
2106 |
boolean hasNonMissed = false;
|
| 2130 |
for (PartnerCollectionRemark r : remarks) {
|
2107 |
for (AgentCallLog l : logs) {
|
| 2131 |
if (r.getRemark() != CollectionRemark.NO_ANSWER) {
|
2108 |
if (!isMissedCustomerStatus(l.getCustomerStatus())) {
|
| 2132 |
hasNonNoAnswer = true;
|
2109 |
hasNonMissed = true;
|
| 2133 |
break;
|
2110 |
break;
|
| 2134 |
}
|
2111 |
}
|
| 2135 |
}
|
2112 |
}
|
| 2136 |
if (hasNonNoAnswer) count++;
|
2113 |
if (hasNonMissed) count++;
|
| 2137 |
else if (attempts >= 3) count++;
|
2114 |
else if (logs.size() >= 3) count++;
|
| 2138 |
}
|
2115 |
}
|
| 2139 |
// Unresolved numbers — count each as 1.
|
- |
|
| 2140 |
count += unresolvedNumbers.size();
|
- |
|
| 2141 |
return count;
|
2116 |
return count;
|
| 2142 |
}
|
2117 |
}
|
| 2143 |
|
2118 |
|
| 2144 |
/**
|
2119 |
/**
|
| 2145 |
* Returns call stats: [0] = called count, [1] = total recording calls, [2] = unique recording calls
|
2120 |
* Returns call stats: [0] = called count, [1] = total recording calls, [2] = unique recording calls
|