| 33917 |
ranu |
1 |
package com.spice.profitmandi.service;
|
|
|
2 |
|
| 37845 |
amit |
3 |
import com.spice.profitmandi.service.warehouse.BillingWarehouseService;
|
|
|
4 |
|
| 35631 |
ranu |
5 |
import com.spice.profitmandi.common.enumuration.ActivationType;
|
|
|
6 |
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
|
|
|
7 |
import com.spice.profitmandi.common.model.CustomRetailer;
|
| 33917 |
ranu |
8 |
import com.spice.profitmandi.common.model.ProfitMandiConstants;
|
| 35631 |
ranu |
9 |
import com.spice.profitmandi.dao.entity.auth.AuthUser;
|
|
|
10 |
import com.spice.profitmandi.dao.entity.auth.PartnerCollectionRemark;
|
|
|
11 |
import com.spice.profitmandi.dao.entity.auth.RbmCallSequenceLog;
|
| 35761 |
ranu |
12 |
import com.spice.profitmandi.dao.entity.cs.AgentCallLog;
|
| 35631 |
ranu |
13 |
import com.spice.profitmandi.dao.entity.cs.Position;
|
|
|
14 |
import com.spice.profitmandi.dao.entity.cs.Ticket;
|
|
|
15 |
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
|
| 34397 |
ranu |
16 |
import com.spice.profitmandi.dao.entity.fofo.MonthlyTarget;
|
| 35759 |
ranu |
17 |
import com.spice.profitmandi.dao.entity.fofo.RetailerContact;
|
| 33985 |
ranu |
18 |
import com.spice.profitmandi.dao.entity.inventory.RbmAchievements;
|
| 33926 |
ranu |
19 |
import com.spice.profitmandi.dao.entity.inventory.RbmTargets;
|
| 37497 |
ranu |
20 |
import com.spice.profitmandi.dao.entity.inventory.SalesAchievements;
|
|
|
21 |
import com.spice.profitmandi.dao.entity.inventory.SalesTargets;
|
| 35759 |
ranu |
22 |
import com.spice.profitmandi.dao.entity.user.Address;
|
| 35631 |
ranu |
23 |
import com.spice.profitmandi.dao.enumuration.auth.CollectionRemark;
|
|
|
24 |
import com.spice.profitmandi.dao.enumuration.cs.EscalationType;
|
| 33917 |
ranu |
25 |
import com.spice.profitmandi.dao.model.*;
|
| 35631 |
ranu |
26 |
import com.spice.profitmandi.dao.repository.auth.AuthRepository;
|
|
|
27 |
import com.spice.profitmandi.dao.repository.auth.PartnerCollectionRemarkRepository;
|
|
|
28 |
import com.spice.profitmandi.dao.repository.auth.RbmCallSequenceLogRepository;
|
| 33985 |
ranu |
29 |
import com.spice.profitmandi.dao.repository.catalog.RbmAchievementsRepository;
|
| 33926 |
ranu |
30 |
import com.spice.profitmandi.dao.repository.catalog.RbmTargetsRepository;
|
| 37497 |
ranu |
31 |
import com.spice.profitmandi.dao.repository.catalog.SalesAchievementsRepository;
|
|
|
32 |
import com.spice.profitmandi.dao.repository.catalog.SalesTargetsRepository;
|
| 35631 |
ranu |
33 |
import com.spice.profitmandi.dao.repository.cs.CsService;
|
|
|
34 |
import com.spice.profitmandi.dao.repository.cs.PositionRepository;
|
|
|
35 |
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
|
| 35759 |
ranu |
36 |
import com.spice.profitmandi.dao.repository.dtr.RetailerContactRepository;
|
| 34397 |
ranu |
37 |
import com.spice.profitmandi.dao.repository.fofo.MonthlyTargetRepository;
|
| 34880 |
ranu |
38 |
import com.spice.profitmandi.dao.repository.logistics.PublicHolidaysRepository;
|
| 35631 |
ranu |
39 |
import com.spice.profitmandi.dao.repository.transaction.LoanRepository;
|
| 34397 |
ranu |
40 |
import com.spice.profitmandi.dao.repository.transaction.OrderRepository;
|
| 35759 |
ranu |
41 |
import com.spice.profitmandi.dao.repository.user.AddressRepository;
|
| 35631 |
ranu |
42 |
import com.spice.profitmandi.service.user.RetailerService;
|
| 33917 |
ranu |
43 |
import org.apache.logging.log4j.LogManager;
|
|
|
44 |
import org.apache.logging.log4j.Logger;
|
|
|
45 |
import org.hibernate.Session;
|
|
|
46 |
import org.hibernate.SessionFactory;
|
|
|
47 |
import org.hibernate.query.NativeQuery;
|
|
|
48 |
import org.springframework.beans.factory.annotation.Autowired;
|
| 36334 |
ranu |
49 |
import org.springframework.cache.annotation.Cacheable;
|
| 33917 |
ranu |
50 |
import org.springframework.stereotype.Component;
|
|
|
51 |
|
|
|
52 |
import javax.persistence.TypedQuery;
|
| 34880 |
ranu |
53 |
import java.time.*;
|
| 35631 |
ranu |
54 |
import java.time.format.DateTimeFormatter;
|
| 34880 |
ranu |
55 |
import java.time.temporal.ChronoUnit;
|
| 35631 |
ranu |
56 |
import java.util.*;
|
| 33997 |
ranu |
57 |
import java.util.stream.Collectors;
|
| 33917 |
ranu |
58 |
|
|
|
59 |
@Component
|
|
|
60 |
public class RbmTargetServiceImpl implements RbmTargetService {
|
| 37845 |
amit |
61 |
|
|
|
62 |
@Autowired
|
|
|
63 |
private BillingWarehouseService billingWarehouseService;
|
|
|
64 |
|
| 33917 |
ranu |
65 |
private static final Logger LOGGER = LogManager.getLogger(RbmTargetServiceImpl.class);
|
|
|
66 |
|
|
|
67 |
@Autowired
|
|
|
68 |
SessionFactory sessionFactory;
|
|
|
69 |
|
| 33926 |
ranu |
70 |
@Autowired
|
|
|
71 |
RbmTargetsRepository rbmTargetsRepository;
|
| 33917 |
ranu |
72 |
|
| 33985 |
ranu |
73 |
@Autowired
|
| 37497 |
ranu |
74 |
SalesTargetsRepository salesTargetsRepository;
|
|
|
75 |
|
|
|
76 |
@Autowired
|
|
|
77 |
SalesAchievementsRepository salesAchievementsRepository;
|
|
|
78 |
|
|
|
79 |
@Autowired
|
|
|
80 |
AuthService authService;
|
|
|
81 |
|
|
|
82 |
@Autowired
|
| 33985 |
ranu |
83 |
RbmAchievementsRepository rbmAchievementsRepository;
|
|
|
84 |
|
| 34397 |
ranu |
85 |
@Autowired
|
|
|
86 |
MonthlyTargetRepository monthlyTargetRepository;
|
|
|
87 |
|
| 34880 |
ranu |
88 |
@Autowired
|
|
|
89 |
PublicHolidaysRepository publicHolidaysRepository;
|
|
|
90 |
|
| 35631 |
ranu |
91 |
@Autowired
|
|
|
92 |
RetailerService retailerService;
|
|
|
93 |
|
| 33917 |
ranu |
94 |
@Override
|
|
|
95 |
public List<WarehouseRbmTargetModel> getWarehouseWiseRbmMonthlyTarget() {
|
|
|
96 |
Session session = sessionFactory.getCurrentSession();
|
|
|
97 |
final TypedQuery<WarehouseRbmTargetModel> typedQuerySimilar = session.createNamedQuery("RbmTarget.getWarehouseWiseMonthlyTarget", WarehouseRbmTargetModel.class);
|
|
|
98 |
|
|
|
99 |
return typedQuerySimilar.getResultList();
|
|
|
100 |
|
|
|
101 |
}
|
|
|
102 |
|
|
|
103 |
@Override
|
|
|
104 |
public List<MTDAchievedTargetModel> getDateWiseAchievedTargetOfRbm(LocalDate startDate, LocalDate endDate) {
|
|
|
105 |
Session session = sessionFactory.getCurrentSession();
|
|
|
106 |
final TypedQuery<MTDAchievedTargetModel> typedQuerySimilar = session.createNamedQuery("RbmTarget.getRbmAchievedMonthlyTarget", MTDAchievedTargetModel.class);
|
|
|
107 |
typedQuerySimilar.setParameter("startDate", startDate);
|
|
|
108 |
typedQuerySimilar.setParameter("endDate", endDate);
|
|
|
109 |
return typedQuerySimilar.getResultList();
|
|
|
110 |
|
|
|
111 |
}
|
|
|
112 |
|
|
|
113 |
@Override
|
| 34055 |
ranu |
114 |
public List<TodayAchievedMovementModel> getMovementWiseAchievementByDate(LocalDate startDate, LocalDate endDate) {
|
| 33917 |
ranu |
115 |
LOGGER.info("start date {}, end date {}", startDate, endDate);
|
|
|
116 |
Session session = sessionFactory.getCurrentSession();
|
| 34055 |
ranu |
117 |
final TypedQuery<TodayAchievedMovementModel> typedQuerySimilar = session.createNamedQuery("RBMTarget.TodayAchivementByMovement", TodayAchievedMovementModel.class);
|
| 33917 |
ranu |
118 |
typedQuerySimilar.setParameter("startDate", startDate);
|
|
|
119 |
typedQuerySimilar.setParameter("endDate", endDate);
|
|
|
120 |
return typedQuerySimilar.getResultList();
|
|
|
121 |
|
|
|
122 |
}
|
|
|
123 |
|
|
|
124 |
@Override
|
| 37442 |
ranu |
125 |
public List<RbmBrandSoldModel> getBrandsSoldByRbm(int authId, int warehouseId, LocalDate startDate, LocalDate endDate) {
|
|
|
126 |
Session session = sessionFactory.getCurrentSession();
|
|
|
127 |
final TypedQuery<RbmBrandSoldModel> q = session.createNamedQuery("RbmDrr.BrandSoldByRbm", RbmBrandSoldModel.class);
|
|
|
128 |
q.setParameter("authId", authId);
|
|
|
129 |
q.setParameter("warehouseId", warehouseId);
|
|
|
130 |
q.setParameter("startDate", startDate);
|
|
|
131 |
q.setParameter("endDate", endDate);
|
|
|
132 |
return q.getResultList();
|
|
|
133 |
}
|
|
|
134 |
|
|
|
135 |
@Override
|
| 37460 |
ranu |
136 |
public List<RbmBrandPartnerCountModel> getBrandPartnerCountByRbm(int authId, int warehouseId, LocalDate startDate, LocalDate endDate) {
|
|
|
137 |
Session session = sessionFactory.getCurrentSession();
|
|
|
138 |
final TypedQuery<RbmBrandPartnerCountModel> q = session.createNamedQuery("RbmDrr.BrandPartnerCountByRbm", RbmBrandPartnerCountModel.class);
|
|
|
139 |
q.setParameter("authId", authId);
|
|
|
140 |
q.setParameter("warehouseId", warehouseId);
|
|
|
141 |
q.setParameter("startDate", startDate);
|
|
|
142 |
q.setParameter("endDate", endDate);
|
|
|
143 |
return q.getResultList();
|
|
|
144 |
}
|
|
|
145 |
|
|
|
146 |
@Override
|
| 37487 |
ranu |
147 |
public List<WarehouseBuyingByMovementModel> getWarehouseBuyingByMovement(int warehouseId, LocalDate startDate, LocalDate endDate) {
|
|
|
148 |
Session session = sessionFactory.getCurrentSession();
|
|
|
149 |
final TypedQuery<WarehouseBuyingByMovementModel> q = session.createNamedQuery("RbmDrr.WarehouseBuyingByMovement", WarehouseBuyingByMovementModel.class);
|
|
|
150 |
q.setParameter("warehouseId", warehouseId);
|
|
|
151 |
q.setParameter("startDate", startDate);
|
|
|
152 |
q.setParameter("endDate", endDate);
|
|
|
153 |
return q.getResultList();
|
|
|
154 |
}
|
|
|
155 |
|
|
|
156 |
@Override
|
| 37586 |
ranu |
157 |
public List<WarehouseOpeningByMovementModel> getWarehouseOpeningByMovement(int warehouseId, LocalDate asOfDate) {
|
|
|
158 |
Session session = sessionFactory.getCurrentSession();
|
|
|
159 |
final TypedQuery<WarehouseOpeningByMovementModel> q = session.createNamedQuery(
|
|
|
160 |
"RbmDrr.WarehouseOpeningByMovement", WarehouseOpeningByMovementModel.class);
|
|
|
161 |
q.setParameter("warehouseId", warehouseId);
|
|
|
162 |
q.setParameter("asOfDate", asOfDate);
|
|
|
163 |
return q.getResultList();
|
|
|
164 |
}
|
|
|
165 |
|
|
|
166 |
@Override
|
| 33917 |
ranu |
167 |
public List<WarehouseMobileStockByMovementModel> getWarehouseMobileStockByMovement() {
|
|
|
168 |
Session session = sessionFactory.getCurrentSession();
|
|
|
169 |
final TypedQuery<WarehouseMobileStockByMovementModel> typedQuerySimilar = session.createNamedQuery("WarehouseStock.MovementWiseMobileStock", WarehouseMobileStockByMovementModel.class);
|
|
|
170 |
|
|
|
171 |
return typedQuerySimilar.getResultList();
|
|
|
172 |
|
|
|
173 |
}
|
|
|
174 |
|
| 33991 |
ranu |
175 |
@Override
|
|
|
176 |
public List<SoldCatalogsReportModel> getCatalogSoldReport(LocalDate startDate, LocalDate endDate) {
|
|
|
177 |
Session session = sessionFactory.getCurrentSession();
|
|
|
178 |
final TypedQuery<SoldCatalogsReportModel> typedQuerySimilar = session.createNamedQuery("CatalogsReport.SoldCatalogsReport", SoldCatalogsReportModel.class);
|
|
|
179 |
typedQuerySimilar.setParameter("startDate", startDate);
|
|
|
180 |
typedQuerySimilar.setParameter("endDate", endDate);
|
|
|
181 |
return typedQuerySimilar.getResultList();
|
| 33917 |
ranu |
182 |
|
| 33991 |
ranu |
183 |
}
|
|
|
184 |
|
|
|
185 |
|
| 33917 |
ranu |
186 |
public int getWorkingDaysCount(LocalDate startDate) {
|
|
|
187 |
Session session = sessionFactory.getCurrentSession();
|
|
|
188 |
|
|
|
189 |
// Convert the LocalDate to a format MySQL can interpret
|
|
|
190 |
String startDateString = startDate.toString();
|
|
|
191 |
|
|
|
192 |
final NativeQuery<?> nativeQuery = session.createNativeQuery(
|
|
|
193 |
"SELECT (DATEDIFF(LAST_DAY(:startDate), :startDate) + 1) " +
|
|
|
194 |
" - (FLOOR((DATEDIFF(LAST_DAY(:startDate), :startDate) + (WEEKDAY(:startDate) + 1)) / 7)) " +
|
|
|
195 |
" - (SELECT COUNT(*) " +
|
|
|
196 |
" FROM logistics.publicholidays " +
|
|
|
197 |
" WHERE date BETWEEN :startDate AND LAST_DAY(:startDate) " +
|
|
|
198 |
" AND WEEKDAY(date) != 6) AS working_days"
|
|
|
199 |
);
|
|
|
200 |
|
|
|
201 |
// Set the start date parameter for each placeholder
|
|
|
202 |
nativeQuery.setParameter("startDate", startDateString);
|
|
|
203 |
|
|
|
204 |
Object result = nativeQuery.getSingleResult();
|
|
|
205 |
return result != null ? ((Number) result).intValue() : 0;
|
|
|
206 |
}
|
|
|
207 |
|
|
|
208 |
|
| 35044 |
ranu |
209 |
|
| 33917 |
ranu |
210 |
@Override
|
|
|
211 |
public List<RbmArrViewModel> getRbmTodayArr() throws Exception {
|
|
|
212 |
LocalDate todayDate = LocalDate.now();
|
|
|
213 |
return getRbmTodayArr(todayDate);
|
|
|
214 |
}
|
|
|
215 |
|
|
|
216 |
@Override
|
| 33997 |
ranu |
217 |
public List<RbmTargetAndAchievementsModel> getRbmTargetsAndAchievemnts(LocalDate startDate, LocalDate endDate) {
|
|
|
218 |
|
| 34002 |
ranu |
219 |
List<RbmTargetsModel> rbmTargetsList = rbmTargetsRepository.selectTargetsModelListByDates(startDate.atStartOfDay(), endDate.atTime(LocalTime.MAX));
|
| 33997 |
ranu |
220 |
|
|
|
221 |
LOGGER.info("rbmTargetsList {}", rbmTargetsList);
|
|
|
222 |
// Group Targtes by RBM and Warehouse
|
| 34002 |
ranu |
223 |
Map<String, RbmTargetsModel> targetsMap = rbmTargetsList.stream()
|
| 33997 |
ranu |
224 |
.collect(Collectors.toMap(
|
|
|
225 |
a -> a.getRbmAuthId() + "-" + a.getWarehouseId(),
|
|
|
226 |
a -> a,
|
|
|
227 |
(a1, a2) -> mergeTargets(a1, a2) // Handle duplicates by merging
|
|
|
228 |
));
|
|
|
229 |
|
|
|
230 |
|
| 34002 |
ranu |
231 |
List<RbmAchievementsModel> rbmAchievements = rbmAchievementsRepository.selectAchievementsModelListByDates(startDate.atStartOfDay(), endDate.atTime(LocalTime.MAX));
|
| 33997 |
ranu |
232 |
LOGGER.info("rbmTargetsList {}", rbmAchievements);
|
|
|
233 |
// Group achievements by RBM and Warehouse
|
| 34002 |
ranu |
234 |
Map<String, RbmAchievementsModel> achievementMap = rbmAchievements.stream()
|
| 33997 |
ranu |
235 |
.collect(Collectors.toMap(
|
|
|
236 |
a -> a.getRbmAuthId() + "-" + a.getWarehouseId(),
|
|
|
237 |
a -> a,
|
|
|
238 |
(a1, a2) -> mergeAchievements(a1, a2) // Handle duplicates by merging
|
|
|
239 |
));
|
|
|
240 |
|
|
|
241 |
return targetsMap.keySet().stream()
|
|
|
242 |
.map(key -> {
|
|
|
243 |
String[] parts = key.split("-");
|
|
|
244 |
int rbmAuthId = Integer.parseInt(parts[0]);
|
|
|
245 |
int warehouseId = Integer.parseInt(parts[1]);
|
|
|
246 |
|
| 34002 |
ranu |
247 |
RbmTargetsModel target = targetsMap.get(key);
|
|
|
248 |
RbmAchievementsModel achievement = achievementMap.getOrDefault(key, new RbmAchievementsModel());
|
| 33997 |
ranu |
249 |
|
|
|
250 |
RbmTargetAndAchievementsModel model = new RbmTargetAndAchievementsModel();
|
|
|
251 |
model.setAuthId(rbmAuthId);
|
|
|
252 |
model.setRbmName(target.getRbmName());
|
| 37845 |
amit |
253 |
model.setWarehouseName(billingWarehouseService.getName(warehouseId, "Unknown"));
|
| 33997 |
ranu |
254 |
|
|
|
255 |
// Set target values
|
| 34006 |
ranu |
256 |
model.setHidTarget((long) target.getHidTarget());
|
| 34098 |
ranu |
257 |
model.setRunningTarget((long) target.getRunningTarget());
|
| 34006 |
ranu |
258 |
model.setFastMovingTarget((long) target.getFastMovingTarget());
|
|
|
259 |
model.setSlowMovingTarget((long) target.getSlowMovingTarget());
|
|
|
260 |
model.setOtherMovingTarget((long) target.getOtherTarget());
|
| 33997 |
ranu |
261 |
|
|
|
262 |
// Set achievement values
|
| 34006 |
ranu |
263 |
model.setAchievedHid((long) achievement.getAchievedHidTarget());
|
| 34098 |
ranu |
264 |
model.setAchievedRunning((long) achievement.getAchievedRunningTarget());
|
| 34006 |
ranu |
265 |
model.setAchievedFastMoving((long) achievement.getAchievedFastMovingTarget());
|
|
|
266 |
model.setAchievedSlowMoving((long) achievement.getAchievedSlowMovingTarget());
|
|
|
267 |
model.setAchievedOtherMoving((long) achievement.getAchievedOtherTarget());
|
| 33997 |
ranu |
268 |
|
|
|
269 |
model.setTotalTarget(
|
| 34006 |
ranu |
270 |
(long) target.getHidTarget() +
|
| 34098 |
ranu |
271 |
(long) target.getRunningTarget() +
|
| 34006 |
ranu |
272 |
(long) target.getFastMovingTarget() +
|
|
|
273 |
(long) target.getSlowMovingTarget() +
|
|
|
274 |
(long) target.getOtherTarget()
|
| 33997 |
ranu |
275 |
);
|
|
|
276 |
model.setTotalAchievemnt(
|
| 34006 |
ranu |
277 |
(long) achievement.getAchievedHidTarget() +
|
| 34098 |
ranu |
278 |
(long) achievement.getAchievedRunningTarget() +
|
| 34006 |
ranu |
279 |
(long) achievement.getAchievedFastMovingTarget() +
|
|
|
280 |
(long) achievement.getAchievedSlowMovingTarget() +
|
|
|
281 |
(long) achievement.getAchievedOtherTarget()
|
| 33997 |
ranu |
282 |
);
|
|
|
283 |
|
|
|
284 |
return model;
|
|
|
285 |
})
|
|
|
286 |
.collect(Collectors.toList());
|
|
|
287 |
|
|
|
288 |
}
|
|
|
289 |
|
| 34002 |
ranu |
290 |
private RbmTargetsModel mergeTargets(RbmTargetsModel a1, RbmTargetsModel a2) {
|
| 33997 |
ranu |
291 |
|
|
|
292 |
// Merge logic for achievements (aggregate the target and achieved values)
|
| 34006 |
ranu |
293 |
a1.setHidTarget((a1.getHidTarget()) +
|
|
|
294 |
(a2.getHidTarget()));
|
| 33997 |
ranu |
295 |
|
| 34006 |
ranu |
296 |
a1.setFastMovingTarget((a1.getFastMovingTarget()) +
|
|
|
297 |
(a2.getFastMovingTarget()));
|
| 33997 |
ranu |
298 |
|
| 34006 |
ranu |
299 |
a1.setSlowMovingTarget((a1.getSlowMovingTarget()) +
|
|
|
300 |
(a2.getSlowMovingTarget()));
|
| 33997 |
ranu |
301 |
|
| 34098 |
ranu |
302 |
a1.setRunningTarget((a1.getRunningTarget()) +
|
|
|
303 |
(a2.getRunningTarget()));
|
| 33997 |
ranu |
304 |
|
| 34006 |
ranu |
305 |
a1.setOtherTarget((a1.getOtherTarget()) +
|
|
|
306 |
(a2.getOtherTarget()));
|
| 33997 |
ranu |
307 |
return a1;
|
|
|
308 |
}
|
|
|
309 |
|
| 34002 |
ranu |
310 |
private RbmAchievementsModel mergeAchievements(RbmAchievementsModel a1, RbmAchievementsModel a2) {
|
| 33997 |
ranu |
311 |
// Merge logic for achievements (aggregate the target and achieved values)
|
| 34006 |
ranu |
312 |
a1.setAchievedHidTarget((a1.getAchievedHidTarget()) +
|
|
|
313 |
(a2.getAchievedHidTarget()));
|
| 33997 |
ranu |
314 |
|
| 34098 |
ranu |
315 |
a1.setAchievedRunningTarget((a1.getAchievedRunningTarget()) +
|
|
|
316 |
(a2.getAchievedRunningTarget()));
|
| 33997 |
ranu |
317 |
|
| 34006 |
ranu |
318 |
a1.setAchievedFastMovingTarget((a1.getAchievedFastMovingTarget()) +
|
|
|
319 |
(a2.getAchievedFastMovingTarget()));
|
| 33997 |
ranu |
320 |
|
| 34006 |
ranu |
321 |
a1.setAchievedSlowMovingTarget((a1.getAchievedSlowMovingTarget()) +
|
|
|
322 |
(a2.getAchievedSlowMovingTarget()));
|
| 33997 |
ranu |
323 |
|
| 34006 |
ranu |
324 |
a1.setAchievedOtherTarget((a1.getAchievedOtherTarget()) +
|
|
|
325 |
(a2.getAchievedOtherTarget()));
|
| 33997 |
ranu |
326 |
|
|
|
327 |
return a1;
|
|
|
328 |
}
|
| 34002 |
ranu |
329 |
|
| 33997 |
ranu |
330 |
@Override
|
| 33917 |
ranu |
331 |
public List<RbmArrViewModel> getRbmTodayArr(LocalDate todayDate) throws Exception {
|
|
|
332 |
|
|
|
333 |
LocalDate startDateOfMonthDay1 = LocalDate.now().withDayOfMonth(1);
|
|
|
334 |
|
| 34289 |
ranu |
335 |
List<WarehouseRbmTargetModel> warehouseRbmTargetModelList = this.getWarehouseWiseRbmMonthlyTarget();
|
|
|
336 |
List<WarehouseRbmTargetModel> warehouseRbmTargetModels = warehouseRbmTargetModelList.stream().filter(x -> x.getMonthlyTarget() > 0).collect(Collectors.toList());
|
|
|
337 |
LOGGER.info("warehouseRbmTargetModels {}", warehouseRbmTargetModels);
|
|
|
338 |
List<TodayAchievedMovementModel> todayAchievedMovementModels = getMovementWiseAchievementByDate(todayDate, todayDate.plusDays(1));
|
| 33917 |
ranu |
339 |
|
|
|
340 |
List<MTDAchievedTargetModel> mtdAchievedTargetModels = getDateWiseAchievedTargetOfRbm(startDateOfMonthDay1, todayDate);
|
|
|
341 |
|
| 35044 |
ranu |
342 |
int remainingWorkingDaysCount = (int) getRemainingDaysInMonth(todayDate);
|
| 33917 |
ranu |
343 |
|
| 33926 |
ranu |
344 |
List<RbmTargets> todayRbmTargetsList = rbmTargetsRepository.selectTargetsByDates(todayDate.atStartOfDay(), todayDate.atTime(LocalTime.MAX));
|
| 33917 |
ranu |
345 |
|
| 33926 |
ranu |
346 |
LOGGER.info("todayRbmTargetsList {}", todayRbmTargetsList);
|
| 33917 |
ranu |
347 |
|
|
|
348 |
List<RbmArrViewModel> rbmArrViewModels = new ArrayList<>();
|
|
|
349 |
|
| 34028 |
ranu |
350 |
if (!todayRbmTargetsList.isEmpty()) {
|
| 33917 |
ranu |
351 |
|
| 35454 |
amit |
352 |
// OPTIMIZED: Pre-build maps for O(1) lookup instead of O(n) filter in each iteration
|
|
|
353 |
// Map key: "authId-warehouseId"
|
|
|
354 |
Map<String, Double> mtdAchievedMap = mtdAchievedTargetModels.stream()
|
|
|
355 |
.collect(Collectors.groupingBy(
|
|
|
356 |
x -> x.getAuthId() + "-" + x.getWarehouseId(),
|
|
|
357 |
Collectors.summingDouble(MTDAchievedTargetModel::getAcheivedMonthlyTarget)
|
|
|
358 |
));
|
|
|
359 |
|
|
|
360 |
Map<String, TodayAchievedMovementModel> todayAchievedMap = todayAchievedMovementModels.stream()
|
|
|
361 |
.collect(Collectors.toMap(
|
|
|
362 |
x -> x.getAuthId() + "-" + x.getWarehouseId(),
|
|
|
363 |
x -> x,
|
|
|
364 |
(a, b) -> a
|
|
|
365 |
));
|
|
|
366 |
|
|
|
367 |
Map<String, RbmTargets> todayRbmTargetsMap = todayRbmTargetsList.stream()
|
|
|
368 |
.collect(Collectors.toMap(
|
|
|
369 |
x -> x.getRbmAuthId() + "-" + x.getWarehouseId(),
|
|
|
370 |
x -> x,
|
|
|
371 |
(a, b) -> a
|
|
|
372 |
));
|
|
|
373 |
|
| 34028 |
ranu |
374 |
for (WarehouseRbmTargetModel rbmTarget : warehouseRbmTargetModels) {
|
| 33917 |
ranu |
375 |
|
| 35454 |
amit |
376 |
String lookupKey = rbmTarget.getAuthId() + "-" + rbmTarget.getWarehouseId();
|
|
|
377 |
|
| 34028 |
ranu |
378 |
float monthlyTarget = rbmTarget.getMonthlyTarget();
|
| 35454 |
amit |
379 |
float achievedSoFar = mtdAchievedMap.getOrDefault(lookupKey, 0.0).floatValue();
|
| 33917 |
ranu |
380 |
|
| 34028 |
ranu |
381 |
float remainingTarget = monthlyTarget - achievedSoFar;
|
| 33953 |
ranu |
382 |
|
| 34028 |
ranu |
383 |
float todayTarget = (remainingWorkingDaysCount > 0 && remainingTarget > 0) ? remainingTarget / remainingWorkingDaysCount : 0;
|
|
|
384 |
|
| 37845 |
amit |
385 |
String warehouseName = billingWarehouseService.getName(rbmTarget.getWarehouseId(), "Unknown");
|
| 33917 |
ranu |
386 |
|
| 34288 |
ranu |
387 |
LOGGER.info("rbmTarget ==== {}", rbmTarget);
|
| 34281 |
ranu |
388 |
|
| 35454 |
amit |
389 |
TodayAchievedMovementModel todayAchievedMovementModel = todayAchievedMap.get(lookupKey);
|
| 34283 |
ranu |
390 |
|
| 35454 |
amit |
391 |
RbmTargets todayRbmTargets = todayRbmTargetsMap.get(lookupKey);
|
| 34285 |
ranu |
392 |
|
| 35454 |
amit |
393 |
if (todayRbmTargets != null) {
|
| 34034 |
ranu |
394 |
LOGGER.info("todayRbmTargets {}", todayRbmTargets);
|
|
|
395 |
RbmArrViewModel viewModel = new RbmArrViewModel();
|
| 33917 |
ranu |
396 |
|
| 34034 |
ranu |
397 |
viewModel.setAuthId(rbmTarget.getAuthId());
|
|
|
398 |
viewModel.setRbmName(rbmTarget.getRbmName());
|
|
|
399 |
viewModel.setWarehouseName(warehouseName);
|
|
|
400 |
viewModel.setTodayTarget(Math.round(todayTarget));
|
|
|
401 |
viewModel.setMonthlyTarget(Math.round(monthlyTarget));
|
|
|
402 |
viewModel.setMtdAchievedTarget(Math.round(achievedSoFar));
|
| 33926 |
ranu |
403 |
|
| 34034 |
ranu |
404 |
viewModel.setTodayHidTarget(Math.round((todayRbmTargets.getHidTarget())));
|
|
|
405 |
viewModel.setTodayFastMovingTarget(Math.round(todayRbmTargets.getFastMovingTarget()));
|
| 34098 |
ranu |
406 |
viewModel.setTodaySlowMovingTarget(Math.round(todayRbmTargets.getSlowMovingtarget()));
|
|
|
407 |
viewModel.setTodayRunningTarget(Math.round(todayRbmTargets.getRunningtarget()));
|
| 34034 |
ranu |
408 |
viewModel.setTodayOtherMovingTarget(Math.round(todayRbmTargets.getOtherTarget()));
|
| 33926 |
ranu |
409 |
|
| 34283 |
ranu |
410 |
if (todayAchievedMovementModel != null) {
|
|
|
411 |
viewModel.setTodayAchievedHidTarget(Math.round(todayAchievedMovementModel.getHidBilled()));
|
|
|
412 |
viewModel.setTodayAchievedFastMovingTarget(Math.round(todayAchievedMovementModel.getFastMovingBilled()));
|
|
|
413 |
viewModel.setTodayAchievedSlowMovingTarget(Math.round(todayAchievedMovementModel.getSlowMovinBilled()));
|
|
|
414 |
viewModel.setTodayAchievedRunningTarget(Math.round(todayAchievedMovementModel.getRunningBilled()));
|
|
|
415 |
viewModel.setTodayAchievedOtherMovingTarget(Math.round(todayAchievedMovementModel.getOtherBilled()));
|
|
|
416 |
viewModel.setTotalAchievedTarget(Math.round(todayAchievedMovementModel.getHidBilled() + todayAchievedMovementModel.getFastMovingBilled() + todayAchievedMovementModel.getSlowMovinBilled() + todayAchievedMovementModel.getRunningBilled() + todayAchievedMovementModel.getOtherBilled()));
|
|
|
417 |
} else {
|
|
|
418 |
viewModel.setTodayAchievedHidTarget(0);
|
|
|
419 |
viewModel.setTodayAchievedFastMovingTarget(0);
|
|
|
420 |
viewModel.setTodayAchievedSlowMovingTarget(0);
|
|
|
421 |
viewModel.setTodayAchievedRunningTarget(0);
|
|
|
422 |
viewModel.setTodayAchievedOtherMovingTarget(0);
|
|
|
423 |
viewModel.setTotalAchievedTarget(0);
|
|
|
424 |
}
|
| 34034 |
ranu |
425 |
rbmArrViewModels.add(viewModel);
|
|
|
426 |
} else {
|
|
|
427 |
LOGGER.info("No matching RbmTargets found for AuthId: {} and rbmname {} and WarehouseId: {}", rbmTarget.getAuthId(), rbmTarget.getRbmName(), rbmTarget.getWarehouseId());
|
|
|
428 |
}
|
| 33917 |
ranu |
429 |
|
| 34034 |
ranu |
430 |
|
|
|
431 |
|
| 34028 |
ranu |
432 |
}
|
| 33917 |
ranu |
433 |
}
|
|
|
434 |
|
|
|
435 |
LOGGER.info("rbmArrViewModels {}", rbmArrViewModels);
|
|
|
436 |
return rbmArrViewModels;
|
|
|
437 |
}
|
|
|
438 |
|
| 33926 |
ranu |
439 |
@Override
|
|
|
440 |
public void setMovementWiseRbmTargets() {
|
|
|
441 |
LocalDate todayDate = LocalDate.now();
|
| 33917 |
ranu |
442 |
|
| 33926 |
ranu |
443 |
LocalDate startDateOfMonthDay1 = LocalDate.now().withDayOfMonth(1);
|
|
|
444 |
|
|
|
445 |
List<WarehouseRbmTargetModel> warehouseRbmTargetModels = this.getWarehouseWiseRbmMonthlyTarget();
|
|
|
446 |
|
|
|
447 |
List<MTDAchievedTargetModel> mtdAchievedTargetModels = getDateWiseAchievedTargetOfRbm(startDateOfMonthDay1, todayDate);
|
|
|
448 |
|
|
|
449 |
|
| 35044 |
ranu |
450 |
int remainingWorkingDaysCount = (int) getRemainingDaysInMonth(todayDate);
|
| 33926 |
ranu |
451 |
|
|
|
452 |
List<WarehouseMobileStockByMovementModel> warehouseMobileStockByMovementModels = getWarehouseMobileStockByMovement();
|
|
|
453 |
|
| 35454 |
amit |
454 |
// OPTIMIZED: Pre-build maps for O(1) lookup instead of O(n) filter in each iteration
|
|
|
455 |
Map<String, Double> mtdAchievedMap = mtdAchievedTargetModels.stream()
|
|
|
456 |
.collect(Collectors.groupingBy(
|
|
|
457 |
x -> x.getAuthId() + "-" + x.getWarehouseId(),
|
|
|
458 |
Collectors.summingDouble(MTDAchievedTargetModel::getAcheivedMonthlyTarget)
|
|
|
459 |
));
|
|
|
460 |
|
|
|
461 |
Map<Integer, WarehouseMobileStockByMovementModel> warehouseStockMap = warehouseMobileStockByMovementModels.stream()
|
|
|
462 |
.collect(Collectors.toMap(
|
|
|
463 |
WarehouseMobileStockByMovementModel::getWarehouseId,
|
|
|
464 |
x -> x,
|
|
|
465 |
(a, b) -> a
|
|
|
466 |
));
|
|
|
467 |
|
| 33926 |
ranu |
468 |
for (WarehouseRbmTargetModel rbmTarget : warehouseRbmTargetModels) {
|
|
|
469 |
|
| 35454 |
amit |
470 |
String lookupKey = rbmTarget.getAuthId() + "-" + rbmTarget.getWarehouseId();
|
|
|
471 |
|
| 33926 |
ranu |
472 |
float monthlyTarget = rbmTarget.getMonthlyTarget();
|
| 35454 |
amit |
473 |
float achievedSoFar = mtdAchievedMap.getOrDefault(lookupKey, 0.0).floatValue();
|
| 33926 |
ranu |
474 |
|
|
|
475 |
|
|
|
476 |
float remainingTarget = monthlyTarget - achievedSoFar;
|
| 33953 |
ranu |
477 |
LOGGER.info("remainingTarget {}", remainingTarget);
|
| 33926 |
ranu |
478 |
|
| 33953 |
ranu |
479 |
float todayTarget = (remainingWorkingDaysCount > 0 && remainingTarget > 0) ? remainingTarget / remainingWorkingDaysCount : 0;
|
|
|
480 |
LOGGER.info("todayTarget {}", todayTarget);
|
|
|
481 |
|
| 33926 |
ranu |
482 |
// Get the warehouse stock data
|
| 35454 |
amit |
483 |
WarehouseMobileStockByMovementModel warehouseMobileStockByMovementModel = warehouseStockMap.get(rbmTarget.getWarehouseId());
|
| 33926 |
ranu |
484 |
|
|
|
485 |
|
|
|
486 |
if (warehouseMobileStockByMovementModel != null) {
|
|
|
487 |
|
|
|
488 |
// Total stock value for this warehouse
|
|
|
489 |
float totalStockValue = warehouseMobileStockByMovementModel.getTotalAvailabilityPrice();
|
|
|
490 |
|
|
|
491 |
// Calculate target allocation based on stock value proportion
|
|
|
492 |
float hidTarget = (warehouseMobileStockByMovementModel.getTotalHidCatalogPrice() / totalStockValue) * todayTarget;
|
|
|
493 |
float fastMovingTarget = (warehouseMobileStockByMovementModel.getTotalFastMovingCatalogPrice() / totalStockValue) * todayTarget;
|
|
|
494 |
float slowMovingTarget = (warehouseMobileStockByMovementModel.getTotalSlowMovingCatalogPrice() / totalStockValue) * todayTarget;
|
| 34098 |
ranu |
495 |
float runningTarget = (warehouseMobileStockByMovementModel.getTotalRunningCatalogPrice() / totalStockValue) * todayTarget;
|
| 33926 |
ranu |
496 |
float otherTarget = (warehouseMobileStockByMovementModel.getTotalOtherCategoryCatalogPrice() / totalStockValue) * todayTarget;
|
|
|
497 |
|
|
|
498 |
RbmTargets rbmTargets = new RbmTargets();
|
|
|
499 |
rbmTargets.setWarehouseId(rbmTarget.getWarehouseId());
|
|
|
500 |
rbmTargets.setRbmAuthId(rbmTarget.getAuthId());
|
|
|
501 |
rbmTargets.setRbmName(rbmTarget.getRbmName());
|
| 34098 |
ranu |
502 |
rbmTargets.setRunningtarget(runningTarget);
|
| 33926 |
ranu |
503 |
rbmTargets.setHidTarget(hidTarget);
|
|
|
504 |
rbmTargets.setFastMovingTarget(fastMovingTarget);
|
| 34098 |
ranu |
505 |
rbmTargets.setSlowMovingtarget(slowMovingTarget);
|
| 33926 |
ranu |
506 |
rbmTargets.setOtherTarget(otherTarget);
|
|
|
507 |
rbmTargets.setCreateTimestamp(LocalDateTime.now());
|
|
|
508 |
|
|
|
509 |
rbmTargetsRepository.persist(rbmTargets);
|
|
|
510 |
|
|
|
511 |
}
|
|
|
512 |
}
|
|
|
513 |
|
|
|
514 |
}
|
|
|
515 |
|
|
|
516 |
|
| 37497 |
ranu |
517 |
/**
|
|
|
518 |
* Daily writer for Sales-hierarchy targets. Rolls up today's persisted
|
|
|
519 |
* inventory.rbm_targets snapshots (produced by {@link #setMovementWiseRbmTargets})
|
|
|
520 |
* to every active Sales person whose downline includes those RBMs, and
|
|
|
521 |
* inserts one row per (sales, warehouse) into inventory.sales_targets.
|
|
|
522 |
*
|
|
|
523 |
* <p>Chain order matters: run this AFTER pushRbmTargets so today's RBM
|
|
|
524 |
* snapshot rows exist when we roll up.
|
|
|
525 |
*/
|
| 33985 |
ranu |
526 |
@Override
|
| 37497 |
ranu |
527 |
public void setSalesTargets() {
|
|
|
528 |
setSalesTargetsForDate(LocalDate.now());
|
|
|
529 |
}
|
|
|
530 |
|
|
|
531 |
@Override
|
|
|
532 |
public void setSalesTargetsForDate(LocalDate date) {
|
| 37505 |
ranu |
533 |
// Sales target for a person = (own_monthly_target − MTD_own_achievement) /
|
|
|
534 |
// remainingDays, then split by warehouse stock mix — exactly like RBM, but
|
|
|
535 |
// scoped to the Sales person's OWN assigned partners (no RBM roll-up).
|
|
|
536 |
LocalDate monthStart = date.withDayOfMonth(1);
|
|
|
537 |
int remainingWorkingDays = (int) getRemainingDaysInMonth(date);
|
| 37497 |
ranu |
538 |
|
| 37505 |
ranu |
539 |
// Warehouse-level stock mix (same source used by setMovementWiseRbmTargets).
|
|
|
540 |
Map<Integer, WarehouseMobileStockByMovementModel> whStockByWh = new HashMap<>();
|
|
|
541 |
for (WarehouseMobileStockByMovementModel s : getWarehouseMobileStockByMovement()) {
|
|
|
542 |
whStockByWh.put(s.getWarehouseId(), s);
|
| 37497 |
ranu |
543 |
}
|
|
|
544 |
|
|
|
545 |
String[][] roles = {
|
|
|
546 |
{"ASM", "4", "L1"}, {"CM", "4", "L2"}, {"DM", "4", "L3"},
|
|
|
547 |
{"BM", "4", "L4"}, {"RSM", "4", "L5"}, {"NSM", "4", "L6"}
|
|
|
548 |
};
|
|
|
549 |
|
|
|
550 |
LocalDate today = LocalDate.now();
|
|
|
551 |
LocalDateTime stamp = date.equals(today) ? LocalDateTime.now() : date.atTime(java.time.LocalTime.NOON);
|
| 37505 |
ranu |
552 |
int written = 0, zeroTargetSkipped = 0;
|
|
|
553 |
|
| 37519 |
ranu |
554 |
// Cache monthly targets + MTD ACH per level so the RSM/NSM roll-up
|
|
|
555 |
// pass can reuse them without repeating queries.
|
|
|
556 |
Map<String, List<SalesHierarchyMonthlyTargetModel>> mtByLevel = new HashMap<>();
|
|
|
557 |
Map<String, Map<String, Double>> mtdByLevelKey = new HashMap<>();
|
|
|
558 |
|
| 37497 |
ranu |
559 |
for (String[] r : roles) {
|
|
|
560 |
String role = r[0];
|
|
|
561 |
int catId = Integer.parseInt(r[1]);
|
| 37505 |
ranu |
562 |
String levelStr = r[2];
|
| 37497 |
ranu |
563 |
|
| 37505 |
ranu |
564 |
List<SalesHierarchyMonthlyTargetModel> monthlyTgts = getSalesMonthlyTargetByRole(catId, levelStr, date);
|
| 37519 |
ranu |
565 |
mtByLevel.put(role, monthlyTgts);
|
| 37497 |
ranu |
566 |
|
| 37505 |
ranu |
567 |
Map<String, Double> mtdByKey = new HashMap<>();
|
|
|
568 |
for (SalesHierarchyAchievedByMovementModel a :
|
|
|
569 |
getSalesAchievementByRole(catId, levelStr, monthStart, date.plusDays(1))) {
|
|
|
570 |
double totBilled =
|
|
|
571 |
a.getHidBilled()
|
|
|
572 |
+ a.getFastMovingBilled()
|
|
|
573 |
+ (a.getRunningBilled() != null ? a.getRunningBilled() : 0)
|
|
|
574 |
+ (a.getSlowMovingBilled() != null ? a.getSlowMovingBilled() : 0)
|
|
|
575 |
+ (a.getOtherBilled() != null ? a.getOtherBilled() : 0);
|
|
|
576 |
mtdByKey.merge(a.getAuthId() + "-" + a.getWarehouseId(), totBilled, Double::sum);
|
|
|
577 |
}
|
| 37519 |
ranu |
578 |
mtdByLevelKey.put(role, mtdByKey);
|
| 37505 |
ranu |
579 |
|
| 37519 |
ranu |
580 |
// Direct-role writer — one row per (authId × warehouse) at this role.
|
|
|
581 |
if (monthlyTgts.isEmpty()) continue;
|
| 37505 |
ranu |
582 |
for (SalesHierarchyMonthlyTargetModel mt : monthlyTgts) {
|
|
|
583 |
if (mt.getMonthlyTarget() <= 0) {
|
|
|
584 |
zeroTargetSkipped++;
|
|
|
585 |
continue;
|
| 37497 |
ranu |
586 |
}
|
| 37505 |
ranu |
587 |
double achieved = mtdByKey.getOrDefault(mt.getAuthId() + "-" + mt.getWarehouseId(), 0.0);
|
|
|
588 |
double remaining = mt.getMonthlyTarget() - achieved;
|
|
|
589 |
double todayTarget = (remainingWorkingDays > 0 && remaining > 0)
|
|
|
590 |
? remaining / remainingWorkingDays : 0;
|
| 37497 |
ranu |
591 |
|
| 37505 |
ranu |
592 |
WarehouseMobileStockByMovementModel stock = whStockByWh.get(mt.getWarehouseId());
|
|
|
593 |
if (stock == null || stock.getTotalAvailabilityPrice() <= 0) continue;
|
|
|
594 |
float total = stock.getTotalAvailabilityPrice();
|
|
|
595 |
|
|
|
596 |
SalesTargets st = new SalesTargets();
|
|
|
597 |
st.setSalesAuthId(mt.getAuthId());
|
|
|
598 |
st.setSalesName(mt.getName());
|
|
|
599 |
st.setRole(role);
|
|
|
600 |
st.setWarehouseId(mt.getWarehouseId());
|
|
|
601 |
st.setHidTarget((float) ((stock.getTotalHidCatalogPrice() / total) * todayTarget));
|
|
|
602 |
st.setFastMovingTarget((float) ((stock.getTotalFastMovingCatalogPrice() / total) * todayTarget));
|
|
|
603 |
st.setSlowMovingTarget((float) ((stock.getTotalSlowMovingCatalogPrice() / total) * todayTarget));
|
|
|
604 |
st.setRunningTarget((float) ((stock.getTotalRunningCatalogPrice() / total) * todayTarget));
|
|
|
605 |
st.setOtherTarget((float) ((stock.getTotalOtherCategoryCatalogPrice() / total) * todayTarget));
|
|
|
606 |
st.setCreateTimestamp(stamp);
|
|
|
607 |
salesTargetsRepository.persist(st);
|
|
|
608 |
written++;
|
| 37497 |
ranu |
609 |
}
|
|
|
610 |
}
|
| 37519 |
ranu |
611 |
|
|
|
612 |
// Roll-up pass for RSM (L5) and NSM (L6) — uses distinct-partner dedup
|
|
|
613 |
// queries so partners assigned to multiple positions in the downline
|
|
|
614 |
// aren't counted more than once.
|
|
|
615 |
String[] rollUpRoles = {"RSM", "NSM"};
|
|
|
616 |
for (String rollUpRole : rollUpRoles) {
|
|
|
617 |
String levelStr = "RSM".equals(rollUpRole) ? "L5" : "L6";
|
|
|
618 |
for (AuthUser top : csService.getAuthUserByCategoryId(4, EscalationType.valueOf(levelStr))) {
|
|
|
619 |
if (!top.isActive()) continue;
|
|
|
620 |
Set<Integer> tree = new HashSet<>();
|
|
|
621 |
tree.add(top.getId());
|
|
|
622 |
List<Integer> reportees = authService.getAllReportees(top.getId());
|
|
|
623 |
if (reportees != null) tree.addAll(reportees);
|
|
|
624 |
if (tree.isEmpty()) continue;
|
|
|
625 |
|
|
|
626 |
// Distinct-partner monthly TGT per warehouse for the downline.
|
|
|
627 |
Map<Integer, Double> monthlyByWh = new LinkedHashMap<>();
|
|
|
628 |
for (com.spice.profitmandi.dao.model.SalesDownlineMonthlyTargetModel d :
|
|
|
629 |
getSalesDownlineMonthlyTarget(tree, date)) {
|
|
|
630 |
monthlyByWh.put(d.getWarehouseId(), (double) d.getMonthlyTarget());
|
|
|
631 |
}
|
|
|
632 |
// Distinct-partner MTD ACH per warehouse for the downline.
|
|
|
633 |
Map<Integer, Double> mtdByWh = new HashMap<>();
|
|
|
634 |
for (com.spice.profitmandi.dao.model.SalesDownlineAchievedByMovementModel d :
|
|
|
635 |
getSalesDownlineAchievement(tree, monthStart, date.plusDays(1))) {
|
|
|
636 |
double tot = d.getHidBilled() + d.getFastMovingBilled()
|
|
|
637 |
+ (d.getRunningBilled() != null ? d.getRunningBilled() : 0)
|
|
|
638 |
+ (d.getSlowMovingBilled() != null ? d.getSlowMovingBilled() : 0)
|
|
|
639 |
+ (d.getOtherBilled() != null ? d.getOtherBilled() : 0);
|
|
|
640 |
mtdByWh.put(d.getWarehouseId(), tot);
|
|
|
641 |
}
|
|
|
642 |
|
|
|
643 |
for (Map.Entry<Integer, Double> e : monthlyByWh.entrySet()) {
|
|
|
644 |
int whId = e.getKey();
|
|
|
645 |
double monthlyTgt = e.getValue();
|
|
|
646 |
if (monthlyTgt <= 0) {
|
|
|
647 |
zeroTargetSkipped++;
|
|
|
648 |
continue;
|
|
|
649 |
}
|
|
|
650 |
double achieved = mtdByWh.getOrDefault(whId, 0.0);
|
|
|
651 |
double remaining = monthlyTgt - achieved;
|
|
|
652 |
double todayTarget = (remainingWorkingDays > 0 && remaining > 0)
|
|
|
653 |
? remaining / remainingWorkingDays : 0;
|
|
|
654 |
|
|
|
655 |
WarehouseMobileStockByMovementModel stock = whStockByWh.get(whId);
|
|
|
656 |
if (stock == null || stock.getTotalAvailabilityPrice() <= 0) continue;
|
|
|
657 |
float total = stock.getTotalAvailabilityPrice();
|
|
|
658 |
|
|
|
659 |
SalesTargets st = new SalesTargets();
|
|
|
660 |
st.setSalesAuthId(top.getId());
|
|
|
661 |
st.setSalesName(top.getFullName());
|
|
|
662 |
st.setRole(rollUpRole);
|
|
|
663 |
st.setWarehouseId(whId);
|
|
|
664 |
st.setHidTarget((float) ((stock.getTotalHidCatalogPrice() / total) * todayTarget));
|
|
|
665 |
st.setFastMovingTarget((float) ((stock.getTotalFastMovingCatalogPrice() / total) * todayTarget));
|
|
|
666 |
st.setSlowMovingTarget((float) ((stock.getTotalSlowMovingCatalogPrice() / total) * todayTarget));
|
|
|
667 |
st.setRunningTarget((float) ((stock.getTotalRunningCatalogPrice() / total) * todayTarget));
|
|
|
668 |
st.setOtherTarget((float) ((stock.getTotalOtherCategoryCatalogPrice() / total) * todayTarget));
|
|
|
669 |
st.setCreateTimestamp(stamp);
|
|
|
670 |
salesTargetsRepository.persist(st);
|
|
|
671 |
written++;
|
|
|
672 |
}
|
|
|
673 |
}
|
|
|
674 |
}
|
| 37505 |
ranu |
675 |
LOGGER.info("setSalesTargetsForDate: wrote {} sales_targets rows for {} ({} rows skipped due to zero monthly target)",
|
|
|
676 |
written, date, zeroTargetSkipped);
|
| 37497 |
ranu |
677 |
}
|
|
|
678 |
|
|
|
679 |
@Override
|
|
|
680 |
public List<SalesTargets> getSalesTargetsByDates(LocalDate startDate, LocalDate endDate) {
|
|
|
681 |
return salesTargetsRepository.selectTargetsByDates(startDate.atStartOfDay(), endDate.atStartOfDay());
|
|
|
682 |
}
|
|
|
683 |
|
|
|
684 |
@Override
|
| 37505 |
ranu |
685 |
public List<SalesHierarchyAchievedByMovementModel> getSalesAchievementByRole(
|
|
|
686 |
int categoryId, String escalationType, LocalDate startDate, LocalDate endDate) {
|
|
|
687 |
Session session = sessionFactory.getCurrentSession();
|
|
|
688 |
TypedQuery<SalesHierarchyAchievedByMovementModel> q = session.createNamedQuery(
|
|
|
689 |
"RbmDrr.SalesHierarchyAchievedByMovement", SalesHierarchyAchievedByMovementModel.class);
|
|
|
690 |
q.setParameter("categoryId", categoryId);
|
|
|
691 |
q.setParameter("escalationType", escalationType);
|
|
|
692 |
q.setParameter("startDate", startDate);
|
|
|
693 |
q.setParameter("endDate", endDate);
|
|
|
694 |
return q.getResultList();
|
|
|
695 |
}
|
|
|
696 |
|
|
|
697 |
@Override
|
|
|
698 |
public List<SalesHierarchyMonthlyTargetModel> getSalesMonthlyTargetByRole(
|
|
|
699 |
int categoryId, String escalationType, LocalDate refDate) {
|
|
|
700 |
Session session = sessionFactory.getCurrentSession();
|
|
|
701 |
TypedQuery<SalesHierarchyMonthlyTargetModel> q = session.createNamedQuery(
|
|
|
702 |
"RbmDrr.SalesHierarchyMonthlyTarget", SalesHierarchyMonthlyTargetModel.class);
|
|
|
703 |
q.setParameter("categoryId", categoryId);
|
|
|
704 |
q.setParameter("escalationType", escalationType);
|
|
|
705 |
q.setParameter("refDate", refDate);
|
|
|
706 |
return q.getResultList();
|
|
|
707 |
}
|
|
|
708 |
|
|
|
709 |
@Override
|
| 37519 |
ranu |
710 |
public List<com.spice.profitmandi.dao.model.SalesDownlineAchievedByMovementModel>
|
|
|
711 |
getSalesDownlineAchievement(Collection<Integer> authIds, LocalDate startDate, LocalDate endDate) {
|
|
|
712 |
if (authIds == null || authIds.isEmpty()) return new ArrayList<>();
|
|
|
713 |
Session session = sessionFactory.getCurrentSession();
|
|
|
714 |
TypedQuery<com.spice.profitmandi.dao.model.SalesDownlineAchievedByMovementModel> q =
|
|
|
715 |
session.createNamedQuery("RbmDrr.SalesDownlineAchievedByMovement",
|
|
|
716 |
com.spice.profitmandi.dao.model.SalesDownlineAchievedByMovementModel.class);
|
|
|
717 |
q.setParameter("authIds", authIds);
|
|
|
718 |
q.setParameter("startDate", startDate);
|
|
|
719 |
q.setParameter("endDate", endDate);
|
|
|
720 |
return q.getResultList();
|
|
|
721 |
}
|
|
|
722 |
|
|
|
723 |
@Override
|
|
|
724 |
public List<com.spice.profitmandi.dao.model.SalesDownlineMonthlyTargetModel>
|
|
|
725 |
getSalesDownlineMonthlyTarget(Collection<Integer> authIds, LocalDate refDate) {
|
|
|
726 |
if (authIds == null || authIds.isEmpty()) return new ArrayList<>();
|
|
|
727 |
Session session = sessionFactory.getCurrentSession();
|
|
|
728 |
TypedQuery<com.spice.profitmandi.dao.model.SalesDownlineMonthlyTargetModel> q =
|
|
|
729 |
session.createNamedQuery("RbmDrr.SalesDownlineMonthlyTarget",
|
|
|
730 |
com.spice.profitmandi.dao.model.SalesDownlineMonthlyTargetModel.class);
|
|
|
731 |
q.setParameter("authIds", authIds);
|
|
|
732 |
q.setParameter("refDate", refDate);
|
|
|
733 |
return q.getResultList();
|
|
|
734 |
}
|
|
|
735 |
|
|
|
736 |
@Override
|
| 37533 |
ranu |
737 |
public List<com.spice.profitmandi.dao.model.SalesHierarchyBrandSoldModel>
|
|
|
738 |
getSalesBrandsSold(Collection<Integer> authIds, int warehouseId, LocalDate startDate, LocalDate endDate) {
|
|
|
739 |
if (authIds == null || authIds.isEmpty()) return new ArrayList<>();
|
|
|
740 |
Session session = sessionFactory.getCurrentSession();
|
|
|
741 |
TypedQuery<com.spice.profitmandi.dao.model.SalesHierarchyBrandSoldModel> q =
|
|
|
742 |
session.createNamedQuery("RbmDrr.SalesBrandSold",
|
|
|
743 |
com.spice.profitmandi.dao.model.SalesHierarchyBrandSoldModel.class);
|
|
|
744 |
q.setParameter("authIds", authIds);
|
|
|
745 |
q.setParameter("warehouseId", warehouseId);
|
|
|
746 |
q.setParameter("startDate", startDate);
|
|
|
747 |
q.setParameter("endDate", endDate);
|
|
|
748 |
return q.getResultList();
|
|
|
749 |
}
|
|
|
750 |
|
|
|
751 |
@Override
|
|
|
752 |
public List<com.spice.profitmandi.dao.model.SalesHierarchyBrandPartnerCountModel>
|
|
|
753 |
getSalesBrandPartnerCount(Collection<Integer> authIds, int warehouseId, LocalDate startDate, LocalDate endDate) {
|
|
|
754 |
if (authIds == null || authIds.isEmpty()) return new ArrayList<>();
|
|
|
755 |
Session session = sessionFactory.getCurrentSession();
|
|
|
756 |
TypedQuery<com.spice.profitmandi.dao.model.SalesHierarchyBrandPartnerCountModel> q =
|
|
|
757 |
session.createNamedQuery("RbmDrr.SalesBrandPartnerCount",
|
|
|
758 |
com.spice.profitmandi.dao.model.SalesHierarchyBrandPartnerCountModel.class);
|
|
|
759 |
q.setParameter("authIds", authIds);
|
|
|
760 |
q.setParameter("warehouseId", warehouseId);
|
|
|
761 |
q.setParameter("startDate", startDate);
|
|
|
762 |
q.setParameter("endDate", endDate);
|
|
|
763 |
return q.getResultList();
|
|
|
764 |
}
|
|
|
765 |
|
|
|
766 |
@Override
|
| 37497 |
ranu |
767 |
public void setSalesAchievements() {
|
|
|
768 |
setSalesAchievementsForDate(LocalDate.now());
|
|
|
769 |
}
|
|
|
770 |
|
|
|
771 |
@Override
|
|
|
772 |
public void setSalesAchievementsForDate(LocalDate date) {
|
| 37505 |
ranu |
773 |
// Sales ACH per (auth × warehouse) — direct query scoped to each Sales
|
|
|
774 |
// person's OWN partners (no RBM roll-up). Same source-of-truth orders SQL.
|
| 37497 |
ranu |
775 |
String[][] roles = {
|
|
|
776 |
{"ASM", "4", "L1"}, {"CM", "4", "L2"}, {"DM", "4", "L3"},
|
|
|
777 |
{"BM", "4", "L4"}, {"RSM", "4", "L5"}, {"NSM", "4", "L6"}
|
|
|
778 |
};
|
|
|
779 |
|
|
|
780 |
LocalDate today = LocalDate.now();
|
|
|
781 |
LocalDateTime stamp = date.equals(today) ? LocalDateTime.now() : date.atTime(java.time.LocalTime.NOON);
|
| 37505 |
ranu |
782 |
int written = 0;
|
| 37497 |
ranu |
783 |
|
| 37519 |
ranu |
784 |
// Cache per-level results so the RSM/NSM roll-up pass reuses them.
|
|
|
785 |
Map<String, List<SalesHierarchyAchievedByMovementModel>> byLevel = new HashMap<>();
|
| 37497 |
ranu |
786 |
for (String[] r : roles) {
|
|
|
787 |
String role = r[0];
|
|
|
788 |
int catId = Integer.parseInt(r[1]);
|
| 37505 |
ranu |
789 |
String levelStr = r[2];
|
| 37497 |
ranu |
790 |
|
| 37519 |
ranu |
791 |
List<SalesHierarchyAchievedByMovementModel> list =
|
|
|
792 |
getSalesAchievementByRole(catId, levelStr, date, date.plusDays(1));
|
|
|
793 |
byLevel.put(role, list);
|
|
|
794 |
for (SalesHierarchyAchievedByMovementModel a : list) {
|
| 37505 |
ranu |
795 |
SalesAchievements sa = new SalesAchievements();
|
|
|
796 |
sa.setSalesAuthId(a.getAuthId());
|
|
|
797 |
sa.setSalesName(a.getName());
|
|
|
798 |
sa.setRole(role);
|
|
|
799 |
sa.setWarehouseId(a.getWarehouseId());
|
|
|
800 |
sa.setAchievedHidTarget(a.getHidBilled());
|
|
|
801 |
sa.setAchievedFastMovingTarget(a.getFastMovingBilled());
|
|
|
802 |
sa.setAchievedRunningTarget(a.getRunningBilled() != null ? a.getRunningBilled() : 0f);
|
|
|
803 |
sa.setAchievedSlowMovingTarget(a.getSlowMovingBilled() != null ? a.getSlowMovingBilled() : 0f);
|
|
|
804 |
sa.setAchievedOtherTarget(a.getOtherBilled() != null ? a.getOtherBilled() : 0f);
|
|
|
805 |
sa.setCreateTimestamp(stamp);
|
|
|
806 |
salesAchievementsRepository.persist(sa);
|
|
|
807 |
written++;
|
| 37497 |
ranu |
808 |
}
|
|
|
809 |
}
|
| 37519 |
ranu |
810 |
|
|
|
811 |
// Roll-up pass for RSM (L5) and NSM (L6) — uses distinct-partner dedup
|
|
|
812 |
// query so partners assigned to multiple positions in the downline
|
|
|
813 |
// don't have their orders counted more than once.
|
|
|
814 |
String[] rollUpRoles = {"RSM", "NSM"};
|
|
|
815 |
for (String rollUpRole : rollUpRoles) {
|
|
|
816 |
String levelStr = "RSM".equals(rollUpRole) ? "L5" : "L6";
|
|
|
817 |
for (AuthUser top : csService.getAuthUserByCategoryId(4, EscalationType.valueOf(levelStr))) {
|
|
|
818 |
if (!top.isActive()) continue;
|
|
|
819 |
Set<Integer> tree = new HashSet<>();
|
|
|
820 |
tree.add(top.getId());
|
|
|
821 |
List<Integer> reportees = authService.getAllReportees(top.getId());
|
|
|
822 |
if (reportees != null) tree.addAll(reportees);
|
|
|
823 |
if (tree.isEmpty()) continue;
|
|
|
824 |
|
|
|
825 |
for (com.spice.profitmandi.dao.model.SalesDownlineAchievedByMovementModel d :
|
|
|
826 |
getSalesDownlineAchievement(tree, date, date.plusDays(1))) {
|
|
|
827 |
SalesAchievements sa = new SalesAchievements();
|
|
|
828 |
sa.setSalesAuthId(top.getId());
|
|
|
829 |
sa.setSalesName(top.getFullName());
|
|
|
830 |
sa.setRole(rollUpRole);
|
|
|
831 |
sa.setWarehouseId(d.getWarehouseId());
|
|
|
832 |
sa.setAchievedHidTarget(d.getHidBilled());
|
|
|
833 |
sa.setAchievedFastMovingTarget(d.getFastMovingBilled());
|
|
|
834 |
sa.setAchievedRunningTarget(d.getRunningBilled() != null ? d.getRunningBilled() : 0f);
|
|
|
835 |
sa.setAchievedSlowMovingTarget(d.getSlowMovingBilled() != null ? d.getSlowMovingBilled() : 0f);
|
|
|
836 |
sa.setAchievedOtherTarget(d.getOtherBilled() != null ? d.getOtherBilled() : 0f);
|
|
|
837 |
sa.setCreateTimestamp(stamp);
|
|
|
838 |
salesAchievementsRepository.persist(sa);
|
|
|
839 |
written++;
|
|
|
840 |
}
|
|
|
841 |
}
|
|
|
842 |
}
|
| 37497 |
ranu |
843 |
LOGGER.info("setSalesAchievementsForDate: wrote {} sales_achievements rows for {}", written, date);
|
|
|
844 |
}
|
|
|
845 |
|
|
|
846 |
@Override
|
| 33985 |
ranu |
847 |
public void setMovementWiseRbmAchievement() {
|
|
|
848 |
LocalDate todayDate = LocalDate.now();
|
|
|
849 |
|
| 34055 |
ranu |
850 |
List<TodayAchievedMovementModel> todayAchievedMovementModels = getMovementWiseAchievementByDate(todayDate, todayDate.plusDays(1));
|
| 33985 |
ranu |
851 |
|
|
|
852 |
|
|
|
853 |
for (TodayAchievedMovementModel achievement : todayAchievedMovementModels) {
|
|
|
854 |
|
|
|
855 |
RbmAchievements rbmAchievements = new RbmAchievements();
|
|
|
856 |
|
|
|
857 |
rbmAchievements.setRbmAuthId(achievement.getAuthId());
|
|
|
858 |
rbmAchievements.setRbmName(achievement.getRbmName());
|
|
|
859 |
rbmAchievements.setWarehouseId(achievement.getWarehouseId());
|
|
|
860 |
rbmAchievements.setAchievedHidTarget(achievement.getHidBilled());
|
|
|
861 |
rbmAchievements.setAchievedFastMovingTarget(achievement.getFastMovingBilled());
|
| 34098 |
ranu |
862 |
rbmAchievements.setAchievedSlowMovingTarget(achievement.getSlowMovinBilled());
|
|
|
863 |
rbmAchievements.setAchievedRunningTarget(achievement.getRunningBilled());
|
| 34012 |
ranu |
864 |
rbmAchievements.setAchievedOtherTarget(achievement.getOtherBilled());
|
| 33985 |
ranu |
865 |
rbmAchievements.setCreateTimestamp(LocalDateTime.now());
|
|
|
866 |
|
|
|
867 |
rbmAchievementsRepository.persist(rbmAchievements);
|
|
|
868 |
|
|
|
869 |
}
|
|
|
870 |
|
|
|
871 |
}
|
|
|
872 |
|
| 34055 |
ranu |
873 |
@Override
|
|
|
874 |
public List<Sold15daysOldAgingModel> getAgingSale(LocalDate startDate, LocalDate endDate) {
|
|
|
875 |
Session session = sessionFactory.getCurrentSession();
|
|
|
876 |
final TypedQuery<Sold15daysOldAgingModel> typedQuerySimilar = session.createNamedQuery("Aging.SoldAgingModel", Sold15daysOldAgingModel.class);
|
| 34056 |
ranu |
877 |
typedQuerySimilar.setParameter("startDate", startDate);
|
|
|
878 |
typedQuerySimilar.setParameter("endDate", endDate);
|
| 34055 |
ranu |
879 |
return typedQuerySimilar.getResultList();
|
| 33985 |
ranu |
880 |
|
| 34055 |
ranu |
881 |
}
|
|
|
882 |
|
| 34103 |
ranu |
883 |
@Override
|
|
|
884 |
public List<RbmBilledFofoIdsModel> getDateWiseBilledFofoIdByRbm(LocalDate startDate, LocalDate endDate) {
|
|
|
885 |
Session session = sessionFactory.getCurrentSession();
|
|
|
886 |
final TypedQuery<RbmBilledFofoIdsModel> typedQuerySimilar = session.createNamedQuery("RBM.RbmBilledFofoId", RbmBilledFofoIdsModel.class);
|
|
|
887 |
typedQuerySimilar.setParameter("startDate", startDate);
|
|
|
888 |
typedQuerySimilar.setParameter("endDate", endDate);
|
|
|
889 |
return typedQuerySimilar.getResultList();
|
| 35453 |
amit |
890 |
}
|
| 34103 |
ranu |
891 |
|
| 35453 |
amit |
892 |
@Override
|
| 36296 |
amit |
893 |
@Cacheable(value = "rbmWeeklyBilling",
|
| 36329 |
amit |
894 |
cacheManager = "fiveMintimeoutCacheManager",
|
|
|
895 |
sync = true)
|
| 35453 |
amit |
896 |
public List<RbmWeeklyBillingModel> getWeeklyBillingDataForMonth(LocalDate monthStart, LocalDate monthEnd) {
|
|
|
897 |
Session session = sessionFactory.getCurrentSession();
|
|
|
898 |
final TypedQuery<RbmWeeklyBillingModel> typedQuery = session.createNamedQuery("RBM.WeeklyBilling", RbmWeeklyBillingModel.class);
|
|
|
899 |
typedQuery.setParameter("startDate", monthStart);
|
|
|
900 |
typedQuery.setParameter("endDate", monthEnd);
|
|
|
901 |
return typedQuery.getResultList();
|
| 34103 |
ranu |
902 |
}
|
|
|
903 |
|
| 34055 |
ranu |
904 |
public List<Our15DaysOldAgingStock> our15DaysAgingStock() {
|
|
|
905 |
Session session = sessionFactory.getCurrentSession();
|
|
|
906 |
final TypedQuery<Our15DaysOldAgingStock> typedQuerySimilar = session.createNamedQuery("Aging.15DaysOurStock", Our15DaysOldAgingStock.class);
|
|
|
907 |
return typedQuerySimilar.getResultList();
|
| 36334 |
ranu |
908 |
}
|
| 34055 |
ranu |
909 |
|
| 36334 |
ranu |
910 |
@Override
|
|
|
911 |
public List<WarehouseAgingStockModel> getWarehouseWiseAgingStock() {
|
|
|
912 |
Session session = sessionFactory.getCurrentSession();
|
|
|
913 |
final TypedQuery<WarehouseAgingStockModel> typedQuery = session.createNamedQuery("Aging.15DaysWarehouseWiseStock", WarehouseAgingStockModel.class);
|
|
|
914 |
return typedQuery.getResultList();
|
| 34055 |
ranu |
915 |
}
|
|
|
916 |
|
| 34397 |
ranu |
917 |
@Autowired
|
|
|
918 |
OrderRepository orderRepository;
|
| 34055 |
ranu |
919 |
|
| 34397 |
ranu |
920 |
@Override
|
| 34641 |
ranu |
921 |
public double calculateFofoIdTodayTarget(int fofoId, double secondryMtd,LocalDate date) {
|
| 34397 |
ranu |
922 |
|
|
|
923 |
MonthlyTarget monthlyTarget = monthlyTargetRepository.selectByDateAndFofoId(YearMonth.now(), fofoId);
|
| 34404 |
ranu |
924 |
if (monthlyTarget == null) {
|
|
|
925 |
// Log or handle as needed
|
|
|
926 |
return 0; // or -1 or some fallback
|
|
|
927 |
}
|
| 34397 |
ranu |
928 |
|
|
|
929 |
double remainingTarget = monthlyTarget.getPurchaseTarget() - secondryMtd;
|
| 34880 |
ranu |
930 |
// double remainingWorkingDays = getWorkingDaysCount(date);
|
|
|
931 |
double remainingWorkingDays = (double) getRemainingDaysInMonth(date);
|
| 34397 |
ranu |
932 |
|
|
|
933 |
|
| 34716 |
ranu |
934 |
|
| 34397 |
ranu |
935 |
if (remainingWorkingDays == 0) return remainingTarget; // Last day
|
| 34716 |
ranu |
936 |
LOGGER.info("remainingWorkingDays {}", remainingWorkingDays);
|
|
|
937 |
LOGGER.info("remainingTarget {}", remainingTarget);
|
| 34397 |
ranu |
938 |
|
|
|
939 |
return (int) Math.ceil(remainingTarget / remainingWorkingDays);
|
|
|
940 |
}
|
|
|
941 |
|
| 34880 |
ranu |
942 |
@Override
|
|
|
943 |
public long getRemainingDaysInMonth(LocalDate date) {
|
|
|
944 |
LocalDate lastDayOfMonth = YearMonth.from(date).atEndOfMonth();
|
| 34397 |
ranu |
945 |
|
| 34880 |
ranu |
946 |
long totalDays = ChronoUnit.DAYS.between(date, lastDayOfMonth) + 1;
|
| 34397 |
ranu |
947 |
|
| 34880 |
ranu |
948 |
// Count Sundays manually
|
|
|
949 |
long sundayCount = 0;
|
|
|
950 |
LocalDate current = date;
|
|
|
951 |
while (!current.isAfter(lastDayOfMonth)) {
|
|
|
952 |
if (current.getDayOfWeek() == DayOfWeek.SUNDAY) {
|
|
|
953 |
sundayCount++;
|
|
|
954 |
}
|
|
|
955 |
current = current.plusDays(1);
|
|
|
956 |
}
|
|
|
957 |
|
|
|
958 |
// Public holidays in the range
|
|
|
959 |
long publicHolidays = publicHolidaysRepository
|
|
|
960 |
.selectAllBetweenDates(date, lastDayOfMonth)
|
|
|
961 |
.size();
|
|
|
962 |
|
|
|
963 |
long remainingDays = totalDays - sundayCount - publicHolidays;
|
|
|
964 |
|
|
|
965 |
LOGGER.info("remainingDays {}", remainingDays);
|
|
|
966 |
LOGGER.info("totalDays {}", totalDays);
|
|
|
967 |
LOGGER.info("sundays {}", sundayCount);
|
|
|
968 |
LOGGER.info("publicHolidays {}", publicHolidays);
|
|
|
969 |
|
|
|
970 |
return remainingDays;
|
|
|
971 |
}
|
|
|
972 |
|
| 35631 |
ranu |
973 |
@Autowired
|
|
|
974 |
PositionRepository positionRepository;
|
| 34880 |
ranu |
975 |
|
| 35631 |
ranu |
976 |
@Autowired
|
|
|
977 |
CsService csService;
|
| 34880 |
ranu |
978 |
|
| 35631 |
ranu |
979 |
@Autowired
|
|
|
980 |
FofoStoreRepository fofoStoreRepository;
|
|
|
981 |
|
|
|
982 |
@Autowired
|
|
|
983 |
AuthRepository authRepository;
|
|
|
984 |
|
|
|
985 |
@Autowired
|
|
|
986 |
LoanRepository loanRepository;
|
|
|
987 |
|
|
|
988 |
@Autowired
|
|
|
989 |
PartnerCollectionService partnerCollectionService;
|
|
|
990 |
|
|
|
991 |
@Autowired
|
|
|
992 |
PartnerCollectionRemarkRepository partnerCollectionRemarkRepository;
|
|
|
993 |
|
|
|
994 |
@Autowired
|
|
|
995 |
RbmCallSequenceLogRepository rbmCallSequenceLogRepository;
|
|
|
996 |
|
|
|
997 |
@Autowired
|
|
|
998 |
com.spice.profitmandi.dao.repository.cs.TicketRepository ticketRepository;
|
|
|
999 |
|
| 35702 |
ranu |
1000 |
@Autowired
|
|
|
1001 |
com.spice.profitmandi.dao.repository.cs.AgentCallLogRepository agentCallLogRepository;
|
|
|
1002 |
|
| 35759 |
ranu |
1003 |
@Autowired
|
|
|
1004 |
RetailerContactRepository retailerContactRepository;
|
|
|
1005 |
|
|
|
1006 |
@Autowired
|
|
|
1007 |
AddressRepository addressRepository;
|
|
|
1008 |
|
| 36225 |
ranu |
1009 |
@Autowired
|
|
|
1010 |
com.spice.profitmandi.dao.repository.cs.PartnerPositionRepository partnerPositionRepository;
|
|
|
1011 |
|
| 35631 |
ranu |
1012 |
@Override
|
|
|
1013 |
public List<RbmCallTargetModel> getRbmCallTargetModels() throws Exception {
|
| 36234 |
ranu |
1014 |
return getRbmCallTargetModels(LocalDate.now());
|
|
|
1015 |
}
|
|
|
1016 |
|
| 37010 |
ranu |
1017 |
// Matches "No Answer", "NO_ANSWER", "no-answer", "noanswer", etc.
|
|
|
1018 |
private static boolean isNoAnswerStatus(String status) {
|
|
|
1019 |
if (status == null) return false;
|
|
|
1020 |
String s = status.trim().toLowerCase().replace('_', ' ').replace('-', ' ').replaceAll("\\s+", " ");
|
|
|
1021 |
return s.equals("no answer") || s.equals("noanswer");
|
|
|
1022 |
}
|
|
|
1023 |
|
|
|
1024 |
@Override
|
|
|
1025 |
public List<OutOfSequenceDetailModel> getOutOfSequenceDetails(int authId) {
|
|
|
1026 |
|
|
|
1027 |
LocalDate today = LocalDate.now();
|
|
|
1028 |
LocalDateTime start = today.atStartOfDay();
|
|
|
1029 |
LocalDateTime end = today.plusDays(1).atStartOfDay();
|
|
|
1030 |
|
|
|
1031 |
List<RbmCallSequenceLog> logs =
|
|
|
1032 |
rbmCallSequenceLogRepository.selectByAuthIdAndDateRange(authId, start, end);
|
|
|
1033 |
|
|
|
1034 |
Map<Integer, RbmCallSequenceLog> uniqueOosLogsByFofoId = new LinkedHashMap<>();
|
|
|
1035 |
|
|
|
1036 |
for (RbmCallSequenceLog log : logs) {
|
|
|
1037 |
if (log.isOutOfSequence()) {
|
|
|
1038 |
// Keep only the first occurrence per fofoId (latest entry since ordered by id DESC)
|
|
|
1039 |
uniqueOosLogsByFofoId.putIfAbsent(log.getFofoId(), log);
|
|
|
1040 |
}
|
|
|
1041 |
}
|
|
|
1042 |
|
|
|
1043 |
if (uniqueOosLogsByFofoId.isEmpty()) {
|
|
|
1044 |
return Collections.emptyList();
|
|
|
1045 |
}
|
|
|
1046 |
|
|
|
1047 |
Set<Integer> fofoIds = uniqueOosLogsByFofoId.keySet();
|
|
|
1048 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
|
|
1049 |
try {
|
|
|
1050 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
|
|
|
1051 |
} catch (ProfitMandiBusinessException e) {
|
|
|
1052 |
LOGGER.error("Error fetching fofo stores", e);
|
|
|
1053 |
}
|
|
|
1054 |
|
|
|
1055 |
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
|
|
|
1056 |
List<OutOfSequenceDetailModel> result = new ArrayList<>();
|
|
|
1057 |
|
|
|
1058 |
for (RbmCallSequenceLog log : uniqueOosLogsByFofoId.values()) {
|
|
|
1059 |
CustomRetailer retailer = retailerMap.get(log.getFofoId());
|
|
|
1060 |
String partyName = retailer != null
|
|
|
1061 |
? retailer.getBusinessName()
|
|
|
1062 |
: "Unknown (" + log.getFofoId() + ")";
|
|
|
1063 |
String code = retailer != null
|
|
|
1064 |
? retailer.getCode()
|
|
|
1065 |
: "-";
|
|
|
1066 |
|
|
|
1067 |
String time = log.getCreateTimestamp() != null
|
|
|
1068 |
? log.getCreateTimestamp().format(timeFormatter)
|
|
|
1069 |
: "-";
|
|
|
1070 |
|
|
|
1071 |
result.add(new OutOfSequenceDetailModel(partyName, code, time));
|
|
|
1072 |
}
|
|
|
1073 |
|
|
|
1074 |
return result;
|
|
|
1075 |
}
|
|
|
1076 |
|
|
|
1077 |
@Override
|
|
|
1078 |
public List<CalledPartnerDetailModel> getCalledPartnerDetails(int authId) throws ProfitMandiBusinessException {
|
|
|
1079 |
return getCalledPartnerDetails(authId, LocalDate.now());
|
|
|
1080 |
}
|
|
|
1081 |
|
|
|
1082 |
@Override
|
|
|
1083 |
public List<CalledPartnerDetailModel> getCalledPartnerDetails(int authId, LocalDate date) throws ProfitMandiBusinessException {
|
|
|
1084 |
// Get all call logs for this auth user on this date
|
|
|
1085 |
LOGGER.info("getCalledPartnerDetails: authId={}, date={}", authId, date);
|
|
|
1086 |
List<AgentCallLog> callLogs = agentCallLogRepository.findByAuthIdAndDate(authId, date);
|
|
|
1087 |
LOGGER.info("Found {} call logs for authId={} on date={}", callLogs.size(), authId, date);
|
|
|
1088 |
|
|
|
1089 |
if (callLogs.isEmpty()) {
|
|
|
1090 |
return Collections.emptyList();
|
|
|
1091 |
}
|
|
|
1092 |
|
|
|
1093 |
// Build a map of normalized customer number -> fofoId
|
|
|
1094 |
Map<String, Integer> customerToFofoIdMap = new HashMap<>();
|
|
|
1095 |
Set<String> normalizedNumbers = new HashSet<>();
|
|
|
1096 |
|
|
|
1097 |
for (AgentCallLog callLog : callLogs) {
|
|
|
1098 |
String customerNumber = callLog.getCustomerNumber();
|
|
|
1099 |
if (customerNumber != null) {
|
|
|
1100 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
|
|
1101 |
normalizedNumbers.add(normalized);
|
|
|
1102 |
}
|
|
|
1103 |
}
|
|
|
1104 |
|
|
|
1105 |
// For each normalized number, find fofoId from retailer_contact first, then address
|
|
|
1106 |
for (String mobile : normalizedNumbers) {
|
|
|
1107 |
Integer fofoId = findFofoIdByMobile(mobile);
|
|
|
1108 |
if (fofoId != null) {
|
|
|
1109 |
customerToFofoIdMap.put(mobile, fofoId);
|
|
|
1110 |
}
|
|
|
1111 |
}
|
|
|
1112 |
|
|
|
1113 |
// Get unique fofoIds for retailer lookup
|
|
|
1114 |
Set<Integer> fofoIds = new HashSet<>(customerToFofoIdMap.values());
|
|
|
1115 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
|
|
1116 |
if (!fofoIds.isEmpty()) {
|
|
|
1117 |
try {
|
|
|
1118 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
|
|
|
1119 |
} catch (ProfitMandiBusinessException e) {
|
|
|
1120 |
LOGGER.error("Error fetching fofo stores", e);
|
|
|
1121 |
}
|
|
|
1122 |
}
|
|
|
1123 |
|
|
|
1124 |
// Get today's remarks for these fofoIds
|
|
|
1125 |
Map<Integer, List<PartnerCollectionRemark>> fofoRemarkMap = new HashMap<>();
|
|
|
1126 |
if (!fofoIds.isEmpty()) {
|
|
|
1127 |
List<PartnerCollectionRemark> todayRemarks = partnerCollectionRemarkRepository
|
|
|
1128 |
.selectAllByFofoIdsOnDate(new ArrayList<>(fofoIds), date);
|
|
|
1129 |
for (PartnerCollectionRemark remark : todayRemarks) {
|
|
|
1130 |
fofoRemarkMap.computeIfAbsent(remark.getFofoId(), k -> new ArrayList<>()).add(remark);
|
|
|
1131 |
}
|
|
|
1132 |
}
|
|
|
1133 |
|
|
|
1134 |
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
|
|
|
1135 |
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm a");
|
|
|
1136 |
List<CalledPartnerDetailModel> result = new ArrayList<>();
|
|
|
1137 |
|
|
|
1138 |
for (com.spice.profitmandi.dao.entity.cs.AgentCallLog callLog : callLogs) {
|
|
|
1139 |
String customerNumber = callLog.getCustomerNumber();
|
|
|
1140 |
if (customerNumber == null) {
|
|
|
1141 |
continue;
|
|
|
1142 |
}
|
|
|
1143 |
|
|
|
1144 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
|
|
1145 |
Integer fofoId = customerToFofoIdMap.get(normalized);
|
|
|
1146 |
|
|
|
1147 |
String partyName = "Unknown";
|
|
|
1148 |
String code = "-";
|
|
|
1149 |
|
|
|
1150 |
if (fofoId != null) {
|
|
|
1151 |
CustomRetailer retailer = retailerMap.get(fofoId);
|
|
|
1152 |
if (retailer != null) {
|
|
|
1153 |
partyName = retailer.getBusinessName();
|
|
|
1154 |
code = retailer.getCode();
|
|
|
1155 |
} else {
|
|
|
1156 |
partyName = "Unknown (" + fofoId + ")";
|
|
|
1157 |
}
|
|
|
1158 |
} else {
|
|
|
1159 |
partyName = "Unknown (" + normalized + ")";
|
|
|
1160 |
}
|
|
|
1161 |
|
|
|
1162 |
// Get remark if available
|
|
|
1163 |
String remarkValue = "-";
|
|
|
1164 |
String messageValue = "-";
|
|
|
1165 |
String remarkTime = "-";
|
|
|
1166 |
|
|
|
1167 |
if (fofoId != null && fofoRemarkMap.containsKey(fofoId)) {
|
|
|
1168 |
List<PartnerCollectionRemark> remarks = fofoRemarkMap.get(fofoId);
|
|
|
1169 |
if (!remarks.isEmpty()) {
|
|
|
1170 |
PartnerCollectionRemark remark = remarks.get(0);
|
|
|
1171 |
remarkValue = remark.getRemark() != null ? remark.getRemark().getValue() : "-";
|
|
|
1172 |
messageValue = remark.getMessage() != null ? remark.getMessage() : "-";
|
|
|
1173 |
remarkTime = remark.getCreateTimestamp() != null ? remark.getCreateTimestamp().format(timeFormatter) : "-";
|
|
|
1174 |
}
|
|
|
1175 |
}
|
|
|
1176 |
|
|
|
1177 |
// Build call log data
|
|
|
1178 |
String recordingUrl = callLog.getRecordingUrl();
|
|
|
1179 |
String callStatus = callLog.getCallStatus();
|
|
|
1180 |
String callDuration = callLog.getCallDuration();
|
|
|
1181 |
String callDateTime = null;
|
|
|
1182 |
if (callLog.getCallDate() != null && callLog.getCallTime() != null) {
|
|
|
1183 |
LocalDateTime callDateTimeObj = LocalDateTime.of(callLog.getCallDate(), callLog.getCallTime());
|
|
|
1184 |
callDateTime = callDateTimeObj.format(dateTimeFormatter);
|
|
|
1185 |
}
|
|
|
1186 |
|
|
|
1187 |
result.add(new CalledPartnerDetailModel(partyName, code, remarkValue, messageValue, remarkTime,
|
|
|
1188 |
recordingUrl, callStatus, callDuration, callDateTime));
|
|
|
1189 |
}
|
|
|
1190 |
|
|
|
1191 |
return result;
|
|
|
1192 |
}
|
|
|
1193 |
|
|
|
1194 |
private Integer findFofoIdByMobile(String mobile) {
|
|
|
1195 |
// First check retailer_contact
|
|
|
1196 |
List<RetailerContact> contacts = retailerContactRepository.selectByMobile(mobile);
|
|
|
1197 |
if (contacts != null && !contacts.isEmpty()) {
|
|
|
1198 |
return contacts.get(0).getFofoId();
|
|
|
1199 |
}
|
|
|
1200 |
|
|
|
1201 |
// Fallback to user.address
|
|
|
1202 |
List<Address> addresses = addressRepository.selectAllByPhoneNumber(mobile);
|
|
|
1203 |
if (addresses != null && !addresses.isEmpty()) {
|
|
|
1204 |
return addresses.get(0).getRetaierId();
|
|
|
1205 |
}
|
|
|
1206 |
|
|
|
1207 |
return null;
|
|
|
1208 |
}
|
|
|
1209 |
|
|
|
1210 |
private List<CalledPartnerDetailModel> buildCalledPartnerResult(List<PartnerCollectionRemark> allRemarks) {
|
|
|
1211 |
if (allRemarks.isEmpty()) {
|
|
|
1212 |
return Collections.emptyList();
|
|
|
1213 |
}
|
|
|
1214 |
|
|
|
1215 |
// Get unique fofoIds for retailer lookup
|
|
|
1216 |
Set<Integer> fofoIds = allRemarks.stream()
|
|
|
1217 |
.map(PartnerCollectionRemark::getFofoId)
|
|
|
1218 |
.collect(Collectors.toSet());
|
|
|
1219 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
|
|
1220 |
try {
|
|
|
1221 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
|
|
|
1222 |
} catch (ProfitMandiBusinessException e) {
|
|
|
1223 |
LOGGER.error("Error fetching fofo stores", e);
|
|
|
1224 |
}
|
|
|
1225 |
|
|
|
1226 |
// Fetch call logs for remarks that have agentCallLogId
|
|
|
1227 |
Map<Long, com.spice.profitmandi.dao.entity.cs.AgentCallLog> callLogMap = new HashMap<>();
|
|
|
1228 |
try {
|
|
|
1229 |
List<Long> callLogIds = allRemarks.stream()
|
|
|
1230 |
.filter(r -> r.getAgentCallLogId() > 0)
|
|
|
1231 |
.map(PartnerCollectionRemark::getAgentCallLogId)
|
|
|
1232 |
.collect(Collectors.toList());
|
|
|
1233 |
|
|
|
1234 |
if (!callLogIds.isEmpty()) {
|
|
|
1235 |
List<com.spice.profitmandi.dao.entity.cs.AgentCallLog> callLogs = agentCallLogRepository.findByIds(callLogIds);
|
|
|
1236 |
if (callLogs != null) {
|
|
|
1237 |
callLogMap = callLogs.stream()
|
|
|
1238 |
.collect(Collectors.toMap(com.spice.profitmandi.dao.entity.cs.AgentCallLog::getId, c -> c, (a, b) -> a));
|
|
|
1239 |
}
|
|
|
1240 |
}
|
|
|
1241 |
} catch (Exception e) {
|
|
|
1242 |
LOGGER.error("Error fetching call logs by ids", e);
|
|
|
1243 |
}
|
|
|
1244 |
|
|
|
1245 |
DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
|
|
|
1246 |
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm a");
|
|
|
1247 |
List<CalledPartnerDetailModel> result = new ArrayList<>();
|
|
|
1248 |
|
|
|
1249 |
for (PartnerCollectionRemark remark : allRemarks) {
|
|
|
1250 |
CustomRetailer retailer = retailerMap.get(remark.getFofoId());
|
|
|
1251 |
String partyName = retailer != null
|
|
|
1252 |
? retailer.getBusinessName()
|
|
|
1253 |
: "Unknown (" + remark.getFofoId() + ")";
|
|
|
1254 |
String code = retailer != null
|
|
|
1255 |
? retailer.getCode()
|
|
|
1256 |
: "-";
|
|
|
1257 |
|
|
|
1258 |
String remarkValue = remark.getRemark() != null
|
|
|
1259 |
? remark.getRemark().getValue()
|
|
|
1260 |
: "-";
|
|
|
1261 |
|
|
|
1262 |
String messageValue = remark.getMessage() != null
|
|
|
1263 |
? remark.getMessage()
|
|
|
1264 |
: "-";
|
|
|
1265 |
|
|
|
1266 |
String time = remark.getCreateTimestamp() != null
|
|
|
1267 |
? remark.getCreateTimestamp().format(timeFormatter)
|
|
|
1268 |
: "-";
|
|
|
1269 |
|
|
|
1270 |
// Get call log data if available
|
|
|
1271 |
String recordingUrl = null;
|
|
|
1272 |
String callStatus = null;
|
|
|
1273 |
String callDuration = null;
|
|
|
1274 |
String callDateTime = null;
|
|
|
1275 |
|
|
|
1276 |
try {
|
|
|
1277 |
if (remark.getAgentCallLogId() > 0 && callLogMap.containsKey(remark.getAgentCallLogId())) {
|
|
|
1278 |
com.spice.profitmandi.dao.entity.cs.AgentCallLog callLog = callLogMap.get(remark.getAgentCallLogId());
|
|
|
1279 |
recordingUrl = callLog.getRecordingUrl();
|
|
|
1280 |
callStatus = callLog.getCallStatus();
|
|
|
1281 |
callDuration = callLog.getCallDuration();
|
|
|
1282 |
if (callLog.getCallDate() != null && callLog.getCallTime() != null) {
|
|
|
1283 |
LocalDateTime callDateTimeObj = LocalDateTime.of(callLog.getCallDate(), callLog.getCallTime());
|
|
|
1284 |
callDateTime = callDateTimeObj.format(dateTimeFormatter);
|
|
|
1285 |
}
|
|
|
1286 |
}
|
|
|
1287 |
} catch (Exception e) {
|
|
|
1288 |
LOGGER.error("Error processing call log for remark id: {}", remark.getId(), e);
|
|
|
1289 |
}
|
|
|
1290 |
|
|
|
1291 |
result.add(new CalledPartnerDetailModel(partyName, code, remarkValue, messageValue, time,
|
|
|
1292 |
recordingUrl, callStatus, callDuration, callDateTime));
|
|
|
1293 |
}
|
|
|
1294 |
|
|
|
1295 |
return result;
|
|
|
1296 |
}
|
|
|
1297 |
|
|
|
1298 |
@Override
|
|
|
1299 |
public List<List<String>> getRbmCallTargetRawDataByAuthId(int authId) throws Exception {
|
|
|
1300 |
List<List<String>> rows = new ArrayList<>();
|
|
|
1301 |
|
|
|
1302 |
// Get auth user
|
|
|
1303 |
List<AuthUser> authUsers = authRepository.selectByIds(Collections.singletonList(authId));
|
|
|
1304 |
if (authUsers.isEmpty()) {
|
|
|
1305 |
return rows;
|
|
|
1306 |
}
|
|
|
1307 |
AuthUser authUser = authUsers.get(0);
|
|
|
1308 |
|
|
|
1309 |
// Get positions to determine if L2
|
|
|
1310 |
List<Position> positions = positionRepository.selectPositionByAuthIds(Collections.singletonList(authId));
|
|
|
1311 |
boolean isL2 = positions.stream()
|
|
|
1312 |
.anyMatch(p -> ProfitMandiConstants.TICKET_CATEGORY_RBM == p.getCategoryId()
|
|
|
1313 |
&& EscalationType.L2.equals(p.getEscalationType()));
|
|
|
1314 |
|
|
|
1315 |
LocalDateTime startDate = LocalDate.now().atStartOfDay();
|
|
|
1316 |
LocalDate firstOfMonth = LocalDate.now().withDayOfMonth(1);
|
|
|
1317 |
LocalDate endOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).plusDays(1);
|
|
|
1318 |
|
|
|
1319 |
// Get fofo IDs from mtdBillingData (same source as Partner Count in getRbmCallTargetModels)
|
|
|
1320 |
List<RbmWeeklyBillingModel> mtdBillingData = getWeeklyBillingDataForMonth(firstOfMonth, endOfMonth);
|
|
|
1321 |
|
|
|
1322 |
List<Integer> fofoIdList;
|
|
|
1323 |
if (isL2) {
|
| 37145 |
ranu |
1324 |
// L2: get fofo IDs from escalated tickets (same as getRbmCallTargetModels).
|
|
|
1325 |
// selectOpenEscalatedTicketsByAuthIds already filters to tickets where authId is the
|
|
|
1326 |
// current assignee AND the ticket has moved past L1 — no post-filter needed.
|
| 37010 |
ranu |
1327 |
List<Ticket> escalatedTickets = ticketRepository.selectOpenEscalatedTicketsByAuthIds(Collections.singletonList(authId));
|
|
|
1328 |
fofoIdList = escalatedTickets.stream()
|
|
|
1329 |
.map(Ticket::getFofoId)
|
|
|
1330 |
.distinct()
|
|
|
1331 |
.collect(Collectors.toList());
|
|
|
1332 |
} else {
|
|
|
1333 |
// L1: get fofo IDs from mtdBillingData with isTargetedPartner (same as Partner Count)
|
|
|
1334 |
fofoIdList = mtdBillingData.stream()
|
|
|
1335 |
.filter(RbmWeeklyBillingModel::isTargetedPartner)
|
|
|
1336 |
.filter(m -> m.getAuthId() == authId)
|
|
|
1337 |
.map(RbmWeeklyBillingModel::getFofoId)
|
|
|
1338 |
.distinct()
|
|
|
1339 |
.collect(Collectors.toList());
|
|
|
1340 |
}
|
|
|
1341 |
|
|
|
1342 |
if (fofoIdList.isEmpty()) {
|
|
|
1343 |
return rows;
|
|
|
1344 |
}
|
|
|
1345 |
|
|
|
1346 |
// MTD billed fofoIds for zero billing check
|
|
|
1347 |
Set<Integer> mtdBilledFofoIds = mtdBillingData.stream()
|
|
|
1348 |
.filter(RbmWeeklyBillingModel::isMtdBilled)
|
|
|
1349 |
.map(RbmWeeklyBillingModel::getFofoId)
|
|
|
1350 |
.collect(Collectors.toSet());
|
|
|
1351 |
|
|
|
1352 |
// Collection rank map for status calculation
|
|
|
1353 |
Map<Integer, Integer> collectionRankMap = new HashMap<>();
|
|
|
1354 |
try {
|
|
|
1355 |
collectionRankMap = partnerCollectionService.getCollectionRankMap(fofoIdList, startDate);
|
|
|
1356 |
} catch (ProfitMandiBusinessException e) {
|
|
|
1357 |
LOGGER.error("Error fetching collection rank map", e);
|
|
|
1358 |
}
|
|
|
1359 |
|
|
|
1360 |
// Resolve partner names/codes
|
|
|
1361 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
|
|
1362 |
if (!fofoIdList.isEmpty()) {
|
|
|
1363 |
try {
|
|
|
1364 |
retailerMap = retailerService.getFofoRetailers(fofoIdList);
|
|
|
1365 |
} catch (ProfitMandiBusinessException e) {
|
|
|
1366 |
LOGGER.error("Error fetching fofo retailers for raw data", e);
|
|
|
1367 |
}
|
|
|
1368 |
}
|
|
|
1369 |
|
|
|
1370 |
String rbmName = authUser.getFullName() + (isL2 ? " (L2)" : "");
|
|
|
1371 |
|
|
|
1372 |
// Build rows for ALL partners (same count as Partner Count)
|
|
|
1373 |
for (Integer fofoId : fofoIdList) {
|
|
|
1374 |
// Default to rank 5 (Normal) for partners without collection plan
|
|
|
1375 |
int rank = collectionRankMap.getOrDefault(fofoId, 5);
|
|
|
1376 |
boolean hasZeroBilling = !mtdBilledFofoIds.contains(fofoId);
|
|
|
1377 |
|
|
|
1378 |
// Status assignment with same priority as getRbmCallTargetModels
|
|
|
1379 |
String status;
|
|
|
1380 |
if (rank == 1) {
|
|
|
1381 |
status = "Plan Today";
|
|
|
1382 |
} else if (rank == 2) {
|
|
|
1383 |
status = "Carry Forward";
|
|
|
1384 |
} else if (hasZeroBilling) {
|
|
|
1385 |
status = "Zero Billing";
|
|
|
1386 |
} else if (rank == 3) {
|
|
|
1387 |
status = "Untouched";
|
|
|
1388 |
} else if (rank == 4) {
|
|
|
1389 |
status = "Future Plan";
|
|
|
1390 |
} else {
|
|
|
1391 |
status = "Normal";
|
|
|
1392 |
}
|
|
|
1393 |
|
|
|
1394 |
CustomRetailer retailer = retailerMap.get(fofoId);
|
|
|
1395 |
String partnerName = retailer != null ? retailer.getBusinessName() : "Unknown (" + fofoId + ")";
|
|
|
1396 |
String partnerCode = retailer != null ? retailer.getCode() : "-";
|
|
|
1397 |
|
|
|
1398 |
rows.add(Arrays.asList(partnerName, partnerCode, status, rbmName));
|
|
|
1399 |
}
|
|
|
1400 |
|
|
|
1401 |
return rows;
|
|
|
1402 |
}
|
|
|
1403 |
|
|
|
1404 |
@Override
|
|
|
1405 |
public List<List<String>> getAllRbmCallTargetRawData() throws Exception {
|
|
|
1406 |
List<List<String>> rows = new ArrayList<>();
|
|
|
1407 |
|
|
|
1408 |
// Get all L1 RBM positions (rows are emitted per L1 RBM).
|
|
|
1409 |
// Call-log lookup happens against ALL callers via findAllByDate — so an L2/L3 or admin call
|
|
|
1410 |
// still counts as "tried" for the partner.
|
|
|
1411 |
List<Position> allRbmPositions = positionRepository
|
|
|
1412 |
.selectPositionByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_RBM).stream()
|
|
|
1413 |
.filter(x -> EscalationType.L1.equals(x.getEscalationType()))
|
|
|
1414 |
.collect(Collectors.toList());
|
|
|
1415 |
|
|
|
1416 |
List<Integer> l1AuthIds = allRbmPositions.stream()
|
|
|
1417 |
.map(Position::getAuthUserId).distinct().collect(Collectors.toList());
|
|
|
1418 |
|
|
|
1419 |
if (l1AuthIds.isEmpty()) {
|
|
|
1420 |
return rows;
|
|
|
1421 |
}
|
|
|
1422 |
|
|
|
1423 |
// Get auth user map
|
|
|
1424 |
Map<Integer, AuthUser> authUserMap = authRepository.selectByIds(l1AuthIds).stream()
|
|
|
1425 |
.collect(Collectors.toMap(AuthUser::getId, au -> au));
|
|
|
1426 |
|
|
|
1427 |
// Positions per auth user (needed early to build L1 partner assignments below)
|
|
|
1428 |
Map<Integer, List<Position>> positionsByAuthId = positionRepository.selectPositionByAuthIds(l1AuthIds).stream()
|
|
|
1429 |
.collect(Collectors.groupingBy(Position::getAuthUserId));
|
|
|
1430 |
|
|
|
1431 |
// Build L1 partner assignments from partner_position — SAME source the UI summary uses for
|
|
|
1432 |
// Calling Target. storeGuyMap (email-based CS mapping) was returning smaller/different lists
|
|
|
1433 |
// for users who are also L2, causing summary=43 but download=3 for Ebadullah.
|
|
|
1434 |
Map<Integer, Integer> l1PositionIdToAuthId = new HashMap<>();
|
|
|
1435 |
for (int rbmAuthId : l1AuthIds) {
|
|
|
1436 |
List<Position> positions = positionsByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
|
|
1437 |
for (Position p : positions) {
|
|
|
1438 |
if (ProfitMandiConstants.TICKET_CATEGORY_RBM == p.getCategoryId()
|
|
|
1439 |
&& EscalationType.L1.equals(p.getEscalationType())) {
|
|
|
1440 |
l1PositionIdToAuthId.put(p.getId(), rbmAuthId);
|
|
|
1441 |
}
|
|
|
1442 |
}
|
|
|
1443 |
}
|
|
|
1444 |
|
|
|
1445 |
Map<Integer, List<Integer>> rbmToFofoIdsMap = new HashMap<>();
|
|
|
1446 |
Set<Integer> allFofoIds = new HashSet<>();
|
|
|
1447 |
if (!l1PositionIdToAuthId.isEmpty()) {
|
|
|
1448 |
List<com.spice.profitmandi.dao.entity.cs.PartnerPosition> allL1PPs =
|
|
|
1449 |
partnerPositionRepository.selectByPositionIds(new ArrayList<>(l1PositionIdToAuthId.keySet()));
|
|
|
1450 |
// Group by owning RBM; keep partners distinct within each RBM.
|
|
|
1451 |
Map<Integer, Set<Integer>> perRbmSets = new HashMap<>();
|
|
|
1452 |
for (com.spice.profitmandi.dao.entity.cs.PartnerPosition pp : allL1PPs) {
|
|
|
1453 |
Integer ownerAuthId = l1PositionIdToAuthId.get(pp.getPositionId());
|
|
|
1454 |
if (ownerAuthId == null) continue;
|
|
|
1455 |
perRbmSets.computeIfAbsent(ownerAuthId, k -> new HashSet<>()).add(pp.getFofoId());
|
|
|
1456 |
allFofoIds.add(pp.getFofoId());
|
|
|
1457 |
}
|
|
|
1458 |
for (Map.Entry<Integer, Set<Integer>> e : perRbmSets.entrySet()) {
|
|
|
1459 |
rbmToFofoIdsMap.put(e.getKey(), new ArrayList<>(e.getValue()));
|
|
|
1460 |
}
|
|
|
1461 |
}
|
|
|
1462 |
|
|
|
1463 |
if (allFofoIds.isEmpty()) {
|
|
|
1464 |
return rows;
|
|
|
1465 |
}
|
|
|
1466 |
|
|
|
1467 |
// Get fofo stores for filtering and name resolution
|
|
|
1468 |
Map<Integer, FofoStore> fofoStoresMap = new HashMap<>();
|
|
|
1469 |
try {
|
|
|
1470 |
fofoStoresMap = fofoStoreRepository.selectByRetailerIds(new ArrayList<>(allFofoIds)).stream()
|
|
|
1471 |
.collect(Collectors.toMap(FofoStore::getId, x -> x, (a, b) -> a));
|
|
|
1472 |
} catch (ProfitMandiBusinessException e) {
|
|
|
1473 |
LOGGER.error("Error fetching fofo stores for all raw data", e);
|
|
|
1474 |
}
|
|
|
1475 |
|
|
|
1476 |
// Batch fetch collection rank map
|
|
|
1477 |
LocalDateTime startDate = LocalDate.now().atStartOfDay();
|
|
|
1478 |
Map<Integer, Integer> allCollectionRankMap = new HashMap<>();
|
|
|
1479 |
try {
|
|
|
1480 |
allCollectionRankMap = partnerCollectionService.getCollectionRankMap(new ArrayList<>(allFofoIds), startDate);
|
|
|
1481 |
} catch (ProfitMandiBusinessException e) {
|
|
|
1482 |
LOGGER.error("Error fetching collection rank map for all raw data", e);
|
|
|
1483 |
}
|
|
|
1484 |
|
|
|
1485 |
// MTD billing data
|
|
|
1486 |
LocalDate firstOfMonth = LocalDate.now().withDayOfMonth(1);
|
|
|
1487 |
LocalDate endOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).plusDays(1);
|
|
|
1488 |
List<RbmWeeklyBillingModel> mtdBillingData = getWeeklyBillingDataForMonth(firstOfMonth, endOfMonth);
|
|
|
1489 |
Set<Integer> allMtdBilledFofoIds = mtdBillingData.stream()
|
|
|
1490 |
.filter(RbmWeeklyBillingModel::isMtdBilled)
|
|
|
1491 |
.map(RbmWeeklyBillingModel::getFofoId)
|
|
|
1492 |
.collect(Collectors.toSet());
|
|
|
1493 |
|
|
|
1494 |
// Batch fetch partner collection remarks for escalation filtering
|
|
|
1495 |
Map<Integer, PartnerCollectionRemark> allPartnerCollectionRemarks = new HashMap<>();
|
|
|
1496 |
if (!allFofoIds.isEmpty()) {
|
|
|
1497 |
List<Integer> allRemarkIds = partnerCollectionRemarkRepository.selectMaxRemarkId(new ArrayList<>(allFofoIds));
|
|
|
1498 |
if (!allRemarkIds.isEmpty()) {
|
|
|
1499 |
allPartnerCollectionRemarks = partnerCollectionRemarkRepository.selectByIds(allRemarkIds).stream()
|
|
|
1500 |
.collect(Collectors.toMap(PartnerCollectionRemark::getFofoId, x -> x, (a, b) -> a));
|
|
|
1501 |
}
|
|
|
1502 |
}
|
|
|
1503 |
|
|
|
1504 |
// Resolve partner names/codes in batch
|
|
|
1505 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
|
|
1506 |
try {
|
|
|
1507 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(allFofoIds));
|
|
|
1508 |
} catch (ProfitMandiBusinessException e) {
|
|
|
1509 |
LOGGER.error("Error fetching fofo retailers for all raw data", e);
|
|
|
1510 |
}
|
|
|
1511 |
|
|
|
1512 |
// Batch-fetch EVERY call log for today (across all callers, not just RBMs) and build a
|
|
|
1513 |
// fofoId -> latest AgentCallLog map. We use findAllByDate here so that calls placed by
|
|
|
1514 |
// admins, escalation agents, or anyone whose authId isn't in the RBM position table are
|
|
|
1515 |
// still surfaced. Downstream we only read this map for fofoIds we actually emit, so
|
|
|
1516 |
// extra entries are harmless.
|
| 37013 |
ranu |
1517 |
// Group ALL today's call logs by fofoId so we can apply the Tried rule per partner
|
|
|
1518 |
// (uses customerStatus + persistence, same as the summary's Tried metric).
|
|
|
1519 |
Map<Integer, List<AgentCallLog>> callsByFofoId = new HashMap<>();
|
| 37010 |
ranu |
1520 |
try {
|
|
|
1521 |
List<AgentCallLog> todayCallLogs = agentCallLogRepository.findAllByDate(LocalDate.now());
|
|
|
1522 |
Set<String> normalizedMobiles = new HashSet<>();
|
|
|
1523 |
for (AgentCallLog log : todayCallLogs) {
|
|
|
1524 |
if (log.getCustomerNumber() != null) {
|
|
|
1525 |
String n = log.getCustomerNumber();
|
|
|
1526 |
normalizedMobiles.add(n.startsWith("+91") ? n.substring(3) : n);
|
|
|
1527 |
}
|
|
|
1528 |
}
|
|
|
1529 |
Map<String, Integer> mobileToFofoIdMap = buildMobileToFofoIdMap(normalizedMobiles);
|
|
|
1530 |
for (AgentCallLog log : todayCallLogs) {
|
|
|
1531 |
if (log.getCustomerNumber() == null) continue;
|
|
|
1532 |
String n = log.getCustomerNumber();
|
|
|
1533 |
String normalized = n.startsWith("+91") ? n.substring(3) : n;
|
|
|
1534 |
Integer fofoId = mobileToFofoIdMap.get(normalized);
|
|
|
1535 |
if (fofoId == null) continue;
|
| 37013 |
ranu |
1536 |
callsByFofoId.computeIfAbsent(fofoId, k -> new ArrayList<>()).add(log);
|
| 37010 |
ranu |
1537 |
}
|
|
|
1538 |
} catch (Exception e) {
|
| 37013 |
ranu |
1539 |
LOGGER.error("Error building call logs map for all raw data", e);
|
| 37010 |
ranu |
1540 |
}
|
|
|
1541 |
|
|
|
1542 |
// Process each L1 RBM
|
|
|
1543 |
// Note: no cross-RBM dedup — a partner assigned to multiple RBMs appears once per RBM,
|
|
|
1544 |
// matching the UI summary counts so row totals per RBM equal that RBM's "Calling Target".
|
|
|
1545 |
for (int rbmAuthId : l1AuthIds) {
|
|
|
1546 |
AuthUser authUser = authUserMap.get(rbmAuthId);
|
|
|
1547 |
if (authUser == null) {
|
|
|
1548 |
continue;
|
|
|
1549 |
}
|
|
|
1550 |
|
|
|
1551 |
List<Integer> fofoIdList = rbmToFofoIdsMap.getOrDefault(rbmAuthId, Collections.emptyList());
|
|
|
1552 |
if (fofoIdList.isEmpty()) {
|
|
|
1553 |
continue; // No L1 partner assignments for this RBM
|
|
|
1554 |
}
|
|
|
1555 |
|
|
|
1556 |
// Filter escalated partners for L1 RBMs
|
|
|
1557 |
List<Position> positions = positionsByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
|
|
1558 |
boolean isRBMAndL1 = positions.stream()
|
|
|
1559 |
.anyMatch(position ->
|
|
|
1560 |
ProfitMandiConstants.TICKET_CATEGORY_RBM == position.getCategoryId()
|
|
|
1561 |
&& EscalationType.L1.equals(position.getEscalationType()));
|
|
|
1562 |
|
|
|
1563 |
List<Integer> fofoIds = fofoIdList;
|
|
|
1564 |
if (isRBMAndL1) {
|
|
|
1565 |
Map<Integer, PartnerCollectionRemark> partnerRemarks = new HashMap<>();
|
|
|
1566 |
for (Integer fofoId : fofoIdList) {
|
|
|
1567 |
if (allPartnerCollectionRemarks.containsKey(fofoId)) {
|
|
|
1568 |
partnerRemarks.put(fofoId, allPartnerCollectionRemarks.get(fofoId));
|
|
|
1569 |
}
|
|
|
1570 |
}
|
|
|
1571 |
Map<Integer, PartnerCollectionRemark> finalPartnerRemarks = partnerRemarks;
|
|
|
1572 |
fofoIds = fofoIdList.stream()
|
|
|
1573 |
.filter(fofoId -> {
|
|
|
1574 |
if (!finalPartnerRemarks.containsKey(fofoId)) return true;
|
|
|
1575 |
PartnerCollectionRemark pcr = finalPartnerRemarks.get(fofoId);
|
|
|
1576 |
return !(CollectionRemark.RBM_L2_ESCALATION.equals(pcr.getRemark())
|
|
|
1577 |
|| CollectionRemark.SALES_ESCALATION.equals(pcr.getRemark()));
|
|
|
1578 |
})
|
|
|
1579 |
.collect(Collectors.toList());
|
|
|
1580 |
}
|
|
|
1581 |
|
|
|
1582 |
// Filter to only external, ACTIVE or REVIVAL stores
|
|
|
1583 |
Map<Integer, FofoStore> finalFofoStoresMap = fofoStoresMap;
|
|
|
1584 |
List<Integer> validFofoIds = fofoIds.stream()
|
|
|
1585 |
.filter(fofoId -> {
|
|
|
1586 |
FofoStore store = finalFofoStoresMap.get(fofoId);
|
|
|
1587 |
if (store == null || store.isInternal()) return false;
|
|
|
1588 |
return ActivationType.ACTIVE.equals(store.getActivationType())
|
|
|
1589 |
|| ActivationType.REVIVAL.equals(store.getActivationType());
|
|
|
1590 |
})
|
|
|
1591 |
.collect(Collectors.toList());
|
|
|
1592 |
|
|
|
1593 |
String rbmName = authUser.getFullName();
|
|
|
1594 |
|
|
|
1595 |
// Categorize each partner and add a row per RBM assignment.
|
|
|
1596 |
for (Integer fofoId : validFofoIds) {
|
|
|
1597 |
int rank = allCollectionRankMap.getOrDefault(fofoId, 5);
|
|
|
1598 |
boolean hasZeroBilling = !allMtdBilledFofoIds.contains(fofoId);
|
|
|
1599 |
|
|
|
1600 |
String status;
|
|
|
1601 |
if (rank == 1) {
|
|
|
1602 |
status = "Plan Today";
|
|
|
1603 |
} else if (rank == 2) {
|
|
|
1604 |
status = "Carry Forward";
|
|
|
1605 |
} else if (hasZeroBilling) {
|
|
|
1606 |
status = "Zero Billing";
|
|
|
1607 |
} else if (rank == 3) {
|
|
|
1608 |
status = "Untouched";
|
|
|
1609 |
} else {
|
|
|
1610 |
continue; // Skip Future Plan and Normal — only include calling target parties
|
|
|
1611 |
}
|
|
|
1612 |
|
|
|
1613 |
CustomRetailer retailer = retailerMap.get(fofoId);
|
|
|
1614 |
String partnerName = retailer != null ? retailer.getBusinessName() : "Unknown (" + fofoId + ")";
|
|
|
1615 |
String partnerCode = retailer != null ? retailer.getCode() : "-";
|
|
|
1616 |
|
| 37013 |
ranu |
1617 |
// Show the latest customerStatus from today's call logs for this partner.
|
|
|
1618 |
// "Did Not Try" only when there is no call log at all today.
|
|
|
1619 |
List<AgentCallLog> partnerCalls = callsByFofoId.getOrDefault(fofoId, Collections.emptyList());
|
| 37010 |
ranu |
1620 |
String latestCallStatus = "Did Not Try";
|
| 37013 |
ranu |
1621 |
if (!partnerCalls.isEmpty()) {
|
|
|
1622 |
AgentCallLog latest = null;
|
|
|
1623 |
for (AgentCallLog l : partnerCalls) {
|
|
|
1624 |
if (latest == null || (l.getId() != null && latest.getId() != null && l.getId() > latest.getId())) {
|
|
|
1625 |
latest = l;
|
|
|
1626 |
}
|
|
|
1627 |
}
|
|
|
1628 |
if (latest != null && latest.getCustomerStatus() != null && !latest.getCustomerStatus().isEmpty()) {
|
|
|
1629 |
latestCallStatus = latest.getCustomerStatus();
|
|
|
1630 |
}
|
| 37010 |
ranu |
1631 |
}
|
|
|
1632 |
|
|
|
1633 |
rows.add(Arrays.asList(partnerName, partnerCode, status, rbmName, latestCallStatus));
|
|
|
1634 |
}
|
|
|
1635 |
}
|
|
|
1636 |
|
|
|
1637 |
return rows;
|
|
|
1638 |
}
|
|
|
1639 |
|
|
|
1640 |
/**
|
|
|
1641 |
* Get count of distinct partners called today based on call logs.
|
|
|
1642 |
* Maps customerNumber from call log to fofoId using retailer_contact and address.
|
|
|
1643 |
* If same fofoId is called multiple times, counts only once.
|
|
|
1644 |
* Numbers without fofoId mapping are also counted (by distinct customer number).
|
|
|
1645 |
*
|
|
|
1646 |
* @param authId the RBM auth ID
|
|
|
1647 |
* @return count of distinct partners/numbers called today
|
|
|
1648 |
*/
|
|
|
1649 |
public long getCalledCountFromCallLogs(long authId) {
|
|
|
1650 |
return getCalledCountFromCallLogs(authId, LocalDate.now());
|
|
|
1651 |
}
|
|
|
1652 |
|
|
|
1653 |
public long getCalledCountFromCallLogs(long authId, LocalDate date) {
|
|
|
1654 |
return getCallStats(authId, date)[0];
|
|
|
1655 |
}
|
|
|
1656 |
|
|
|
1657 |
/**
|
|
|
1658 |
* Batch-builds a mobile (normalized, +91 stripped) -> fofoId mapping for all the given mobiles
|
|
|
1659 |
* in just two queries (retailer_contact, then address fallback for unmapped numbers).
|
|
|
1660 |
* Replaces the previous per-call-log findFofoIdByMobile() N+1 lookups.
|
|
|
1661 |
*/
|
|
|
1662 |
private Map<String, Integer> buildMobileToFofoIdMap(Set<String> normalizedMobiles) {
|
|
|
1663 |
Map<String, Integer> result = new HashMap<>();
|
|
|
1664 |
if (normalizedMobiles == null || normalizedMobiles.isEmpty()) {
|
|
|
1665 |
return result;
|
|
|
1666 |
}
|
|
|
1667 |
List<String> mobilesList = new ArrayList<>(normalizedMobiles);
|
|
|
1668 |
|
|
|
1669 |
// First pass: retailer_contact
|
|
|
1670 |
List<RetailerContact> contacts = retailerContactRepository.selectByMobiles(mobilesList);
|
|
|
1671 |
for (RetailerContact rc : contacts) {
|
|
|
1672 |
// Keep the first fofoId we see per mobile (matches old single-mobile behavior of get(0))
|
|
|
1673 |
result.putIfAbsent(rc.getMobile(), rc.getFofoId());
|
|
|
1674 |
}
|
|
|
1675 |
|
|
|
1676 |
// Second pass: address fallback for mobiles not yet mapped
|
|
|
1677 |
List<String> unmapped = mobilesList.stream()
|
|
|
1678 |
.filter(m -> !result.containsKey(m))
|
|
|
1679 |
.collect(Collectors.toList());
|
|
|
1680 |
if (!unmapped.isEmpty()) {
|
|
|
1681 |
List<Address> addresses = addressRepository.selectAllByPhoneNumbers(unmapped);
|
|
|
1682 |
for (Address addr : addresses) {
|
|
|
1683 |
result.putIfAbsent(addr.getPhoneNumber(), addr.getRetaierId());
|
|
|
1684 |
}
|
|
|
1685 |
}
|
|
|
1686 |
return result;
|
|
|
1687 |
}
|
|
|
1688 |
|
|
|
1689 |
/**
|
|
|
1690 |
* In-memory variant of getCallStats() that uses pre-fetched call logs and mobile→fofoId mapping.
|
|
|
1691 |
* Used by the batch-fetched RBM Call Target loop to avoid N+1 query patterns.
|
|
|
1692 |
*/
|
|
|
1693 |
private long[] getCallStatsFromLogs(List<AgentCallLog> callLogs, Map<String, Integer> mobileToFofoIdMap) {
|
|
|
1694 |
if (callLogs == null || callLogs.isEmpty()) {
|
|
|
1695 |
return new long[]{0, 0, 0};
|
|
|
1696 |
}
|
|
|
1697 |
|
|
|
1698 |
Set<Integer> calledFofoIds = new HashSet<>();
|
|
|
1699 |
Set<String> calledNumbersWithoutFofoId = new HashSet<>();
|
|
|
1700 |
long totalRecordingCalls = 0;
|
|
|
1701 |
Set<String> uniqueRecordingNumbers = new HashSet<>();
|
|
|
1702 |
|
|
|
1703 |
for (AgentCallLog callLog : callLogs) {
|
|
|
1704 |
String customerNumber = callLog.getCustomerNumber();
|
|
|
1705 |
if (customerNumber != null) {
|
|
|
1706 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
|
|
1707 |
Integer fofoId = mobileToFofoIdMap.get(normalized);
|
|
|
1708 |
if (fofoId != null) {
|
|
|
1709 |
calledFofoIds.add(fofoId);
|
|
|
1710 |
} else {
|
|
|
1711 |
calledNumbersWithoutFofoId.add(normalized);
|
|
|
1712 |
}
|
|
|
1713 |
|
|
|
1714 |
if (callLog.getRecordingUrl() != null && !callLog.getRecordingUrl().isEmpty()
|
|
|
1715 |
&& !"None".equalsIgnoreCase(callLog.getRecordingUrl())) {
|
|
|
1716 |
totalRecordingCalls++;
|
|
|
1717 |
uniqueRecordingNumbers.add(normalized);
|
|
|
1718 |
}
|
|
|
1719 |
}
|
|
|
1720 |
}
|
|
|
1721 |
|
|
|
1722 |
long calledCount = calledFofoIds.size() + calledNumbersWithoutFofoId.size();
|
|
|
1723 |
return new long[]{calledCount, totalRecordingCalls, uniqueRecordingNumbers.size()};
|
|
|
1724 |
}
|
|
|
1725 |
|
| 37022 |
ranu |
1726 |
// Same rule as computeUniqueTriedCount but restricted to fofoIds in the target set.
|
|
|
1727 |
// Used to compute "TGT Calls" — how many of THIS RBM's Calling Target partners were
|
|
|
1728 |
// actually engaged today. Off-target activity is silently excluded.
|
|
|
1729 |
private long computeUniqueTriedCountForTargets(List<AgentCallLog> callLogs,
|
|
|
1730 |
Map<String, Integer> mobileToFofoIdMap,
|
|
|
1731 |
Set<Integer> targetFofoIds) {
|
|
|
1732 |
if (callLogs == null || callLogs.isEmpty()) return 0L;
|
|
|
1733 |
if (targetFofoIds == null || targetFofoIds.isEmpty()) return 0L;
|
|
|
1734 |
Map<Integer, List<AgentCallLog>> byFofoId = new HashMap<>();
|
|
|
1735 |
for (AgentCallLog log : callLogs) {
|
|
|
1736 |
String num = log.getCustomerNumber();
|
|
|
1737 |
if (num == null) continue;
|
|
|
1738 |
String normalized = num.startsWith("+91") ? num.substring(3) : num;
|
|
|
1739 |
Integer fofoId = mobileToFofoIdMap != null ? mobileToFofoIdMap.get(normalized) : null;
|
|
|
1740 |
if (fofoId == null) continue;
|
|
|
1741 |
if (!targetFofoIds.contains(fofoId)) continue; // off-target, skip
|
|
|
1742 |
byFofoId.computeIfAbsent(fofoId, k -> new ArrayList<>()).add(log);
|
|
|
1743 |
}
|
|
|
1744 |
long count = 0L;
|
|
|
1745 |
for (List<AgentCallLog> logs : byFofoId.values()) {
|
|
|
1746 |
boolean hasNonMissed = false;
|
|
|
1747 |
for (AgentCallLog l : logs) {
|
|
|
1748 |
if (!isMissedCustomerStatus(l.getCustomerStatus())) {
|
|
|
1749 |
hasNonMissed = true;
|
|
|
1750 |
break;
|
|
|
1751 |
}
|
|
|
1752 |
}
|
|
|
1753 |
if (hasNonMissed) count++;
|
|
|
1754 |
else if (logs.size() >= 3) count++;
|
|
|
1755 |
}
|
|
|
1756 |
return count;
|
|
|
1757 |
}
|
|
|
1758 |
|
| 37012 |
ranu |
1759 |
// Matches "Missed", "MISSED", "No Answer", "NO_ANSWER", "no-answer" etc.
|
|
|
1760 |
// Kommuno currently emits "Missed" — kept liberal so we're covered if the tag changes.
|
|
|
1761 |
private static boolean isMissedCustomerStatus(String status) {
|
|
|
1762 |
if (status == null) return false;
|
|
|
1763 |
String s = status.trim().toLowerCase().replace('_', ' ').replace('-', ' ').replaceAll("\\s+", " ");
|
|
|
1764 |
return s.equals("missed") || s.equals("no answer") || s.equals("noanswer");
|
|
|
1765 |
}
|
|
|
1766 |
|
| 37013 |
ranu |
1767 |
|
| 36234 |
ranu |
1768 |
public List<RbmCallTargetModel> getRbmCallTargetModels(LocalDate queryDate) throws Exception {
|
| 35631 |
ranu |
1769 |
long methodStart = System.currentTimeMillis();
|
|
|
1770 |
List<RbmCallTargetModel> rbmCallTargetModels = new ArrayList<>();
|
|
|
1771 |
|
|
|
1772 |
// Get all RBM positions (L1 and L2)
|
|
|
1773 |
long start = System.currentTimeMillis();
|
|
|
1774 |
List<Position> allRbmPositions = positionRepository
|
|
|
1775 |
.selectPositionByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_RBM).stream()
|
| 36210 |
ranu |
1776 |
.filter(x -> Arrays.asList(EscalationType.L1, EscalationType.L2, EscalationType.L3).contains(x.getEscalationType()))
|
| 35631 |
ranu |
1777 |
.collect(Collectors.toList());
|
|
|
1778 |
|
| 36210 |
ranu |
1779 |
// Separate L1, L2 and L3 auth IDs
|
| 35631 |
ranu |
1780 |
List<Integer> l1AuthIds = allRbmPositions.stream()
|
|
|
1781 |
.filter(p -> EscalationType.L1.equals(p.getEscalationType()))
|
|
|
1782 |
.map(Position::getAuthUserId).distinct().collect(Collectors.toList());
|
|
|
1783 |
List<Integer> l2AuthIds = allRbmPositions.stream()
|
|
|
1784 |
.filter(p -> EscalationType.L2.equals(p.getEscalationType()))
|
|
|
1785 |
.map(Position::getAuthUserId).distinct().collect(Collectors.toList());
|
| 36210 |
ranu |
1786 |
List<Integer> l3AuthIds = allRbmPositions.stream()
|
|
|
1787 |
.filter(p -> EscalationType.L3.equals(p.getEscalationType()))
|
|
|
1788 |
.map(Position::getAuthUserId).distinct().collect(Collectors.toList());
|
| 35631 |
ranu |
1789 |
|
|
|
1790 |
// Union of all auth IDs for batch fetching
|
|
|
1791 |
List<Integer> rbmPositionsAuthIds = allRbmPositions.stream()
|
|
|
1792 |
.map(Position::getAuthUserId).distinct().collect(Collectors.toList());
|
| 36210 |
ranu |
1793 |
LOGGER.info("RBM Call Target - RBM positions fetch: {}ms, L1: {}, L2: {}, L3: {}", System.currentTimeMillis() - start, l1AuthIds.size(), l2AuthIds.size(), l3AuthIds.size());
|
| 35631 |
ranu |
1794 |
|
|
|
1795 |
start = System.currentTimeMillis();
|
|
|
1796 |
Map<String, Set<Integer>> storeGuyMap = csService.getAuthUserPartnerIdMapping();
|
|
|
1797 |
LOGGER.info("RBM Call Target - StoreGuyMap fetch: {}ms", System.currentTimeMillis() - start);
|
|
|
1798 |
|
| 36234 |
ranu |
1799 |
LocalDateTime startDate = queryDate.atStartOfDay();
|
|
|
1800 |
LocalDate firstOfMonth = queryDate.withDayOfMonth(1);
|
|
|
1801 |
LocalDate endOfMonth = queryDate.withDayOfMonth(queryDate.lengthOfMonth()).plusDays(1);
|
| 35631 |
ranu |
1802 |
|
|
|
1803 |
// Get auth user map
|
|
|
1804 |
start = System.currentTimeMillis();
|
|
|
1805 |
Map<Integer, AuthUser> authUserMap = authRepository.selectByIds(rbmPositionsAuthIds).stream()
|
|
|
1806 |
.collect(Collectors.toMap(AuthUser::getId, au -> au));
|
|
|
1807 |
LOGGER.info("RBM Call Target - AuthUser fetch: {}ms", System.currentTimeMillis() - start);
|
|
|
1808 |
|
|
|
1809 |
// Batch fetch positions by auth IDs (to check if RBM is L1)
|
|
|
1810 |
start = System.currentTimeMillis();
|
|
|
1811 |
Map<Integer, List<Position>> positionsByAuthId = positionRepository.selectPositionByAuthIds(rbmPositionsAuthIds).stream()
|
|
|
1812 |
.collect(Collectors.groupingBy(Position::getAuthUserId));
|
|
|
1813 |
LOGGER.info("RBM Call Target - Positions by AuthId fetch: {}ms", System.currentTimeMillis() - start);
|
|
|
1814 |
|
|
|
1815 |
// Get all fofo IDs for all RBMs
|
|
|
1816 |
Set<Integer> allFofoIds = new HashSet<>();
|
|
|
1817 |
Map<Integer, List<Integer>> rbmToFofoIdsMap = new HashMap<>();
|
|
|
1818 |
for (int rbmAuthId : rbmPositionsAuthIds) {
|
|
|
1819 |
AuthUser au = authUserMap.get(rbmAuthId);
|
|
|
1820 |
if (au != null && storeGuyMap.containsKey(au.getEmailId())) {
|
|
|
1821 |
List<Integer> fofoIds = new ArrayList<>(storeGuyMap.get(au.getEmailId()));
|
|
|
1822 |
allFofoIds.addAll(fofoIds);
|
|
|
1823 |
rbmToFofoIdsMap.put(rbmAuthId, fofoIds);
|
|
|
1824 |
}
|
|
|
1825 |
}
|
| 35816 |
ranu |
1826 |
// Initialize L2 calling list map - will be populated after fetching remarks
|
| 35631 |
ranu |
1827 |
Map<Integer, List<Integer>> l2AuthIdToFofoIds = new HashMap<>();
|
| 35816 |
ranu |
1828 |
for (int l2AuthId : l2AuthIds) {
|
|
|
1829 |
l2AuthIdToFofoIds.put(l2AuthId, new ArrayList<>());
|
| 35631 |
ranu |
1830 |
}
|
| 36210 |
ranu |
1831 |
// Initialize L3 calling list map - will be populated after fetching remarks
|
|
|
1832 |
Map<Integer, List<Integer>> l3AuthIdToFofoIds = new HashMap<>();
|
|
|
1833 |
for (int l3AuthId : l3AuthIds) {
|
|
|
1834 |
l3AuthIdToFofoIds.put(l3AuthId, new ArrayList<>());
|
|
|
1835 |
}
|
| 35631 |
ranu |
1836 |
LOGGER.info("RBM Call Target - Total fofo IDs to process: {}", allFofoIds.size());
|
|
|
1837 |
|
|
|
1838 |
// Get only needed fofo stores (OPTIMIZED - was fetching ALL stores before)
|
|
|
1839 |
start = System.currentTimeMillis();
|
|
|
1840 |
Map<Integer, FofoStore> fofoStoresMap = new HashMap<>();
|
|
|
1841 |
if (!allFofoIds.isEmpty()) {
|
|
|
1842 |
try {
|
|
|
1843 |
fofoStoresMap = fofoStoreRepository.selectByRetailerIds(new ArrayList<>(allFofoIds)).stream()
|
|
|
1844 |
.collect(Collectors.toMap(FofoStore::getId, x -> x, (a, b) -> a));
|
|
|
1845 |
} catch (ProfitMandiBusinessException e) {
|
|
|
1846 |
LOGGER.error("Error fetching fofo stores", e);
|
|
|
1847 |
}
|
|
|
1848 |
}
|
|
|
1849 |
LOGGER.info("RBM Call Target - FofoStores fetch (only needed): {}ms, count: {}", System.currentTimeMillis() - start, fofoStoresMap.size());
|
|
|
1850 |
|
|
|
1851 |
// Batch fetch max remark ids for all fofoIds (for escalation filtering)
|
|
|
1852 |
start = System.currentTimeMillis();
|
|
|
1853 |
Map<Integer, PartnerCollectionRemark> allPartnerCollectionRemarks = new HashMap<>();
|
|
|
1854 |
if (!allFofoIds.isEmpty()) {
|
|
|
1855 |
List<Integer> allRemarkIds = partnerCollectionRemarkRepository.selectMaxRemarkId(new ArrayList<>(allFofoIds));
|
|
|
1856 |
if (!allRemarkIds.isEmpty()) {
|
|
|
1857 |
allPartnerCollectionRemarks = partnerCollectionRemarkRepository.selectByIds(allRemarkIds).stream()
|
|
|
1858 |
.collect(Collectors.toMap(PartnerCollectionRemark::getFofoId, x -> x, (a, b) -> a));
|
|
|
1859 |
}
|
|
|
1860 |
}
|
|
|
1861 |
LOGGER.info("RBM Call Target - PartnerCollectionRemarks fetch: {}ms", System.currentTimeMillis() - start);
|
|
|
1862 |
|
| 35816 |
ranu |
1863 |
// Populate L2 calling list based on partners whose latest remark is RBM_L2_ESCALATION
|
|
|
1864 |
// Find the L1 who has the partner and add to that L1's manager (L2) calling list
|
|
|
1865 |
for (Map.Entry<Integer, PartnerCollectionRemark> entry : allPartnerCollectionRemarks.entrySet()) {
|
|
|
1866 |
Integer fofoId = entry.getKey();
|
|
|
1867 |
PartnerCollectionRemark remark = entry.getValue();
|
|
|
1868 |
|
|
|
1869 |
if (CollectionRemark.RBM_L2_ESCALATION.equals(remark.getRemark())) {
|
|
|
1870 |
// Find which L1 RBM has this partner assigned
|
|
|
1871 |
for (int l1AuthId : l1AuthIds) {
|
|
|
1872 |
List<Integer> l1FofoIds = rbmToFofoIdsMap.getOrDefault(l1AuthId, Collections.emptyList());
|
|
|
1873 |
if (l1FofoIds.contains(fofoId)) {
|
|
|
1874 |
// Get L1's manager (L2)
|
|
|
1875 |
AuthUser l1User = authUserMap.get(l1AuthId);
|
|
|
1876 |
if (l1User != null && l2AuthIdToFofoIds.containsKey(l1User.getManagerId())) {
|
|
|
1877 |
int l2ManagerId = l1User.getManagerId();
|
|
|
1878 |
l2AuthIdToFofoIds.get(l2ManagerId).add(fofoId);
|
|
|
1879 |
}
|
|
|
1880 |
break; // Found the L1 for this fofoId
|
|
|
1881 |
}
|
|
|
1882 |
}
|
|
|
1883 |
}
|
|
|
1884 |
}
|
|
|
1885 |
LOGGER.info("RBM Call Target - L2 calling lists populated from RBM_L2_ESCALATION remarks");
|
|
|
1886 |
|
| 36210 |
ranu |
1887 |
// Populate L3 calling list based on partners whose latest remark is RBM_L3_ESCALATION
|
| 36212 |
ranu |
1888 |
// Find the L1 who originally has the partner, then:
|
|
|
1889 |
// Case 1: L1 -> L3 directly (if L1's manager IS L3)
|
|
|
1890 |
// Case 2: L1 -> L2 -> L3 (if L1's manager is L2, then L2's manager is L3)
|
| 36210 |
ranu |
1891 |
for (Map.Entry<Integer, PartnerCollectionRemark> entry : allPartnerCollectionRemarks.entrySet()) {
|
|
|
1892 |
Integer fofoId = entry.getKey();
|
|
|
1893 |
PartnerCollectionRemark remark = entry.getValue();
|
|
|
1894 |
|
|
|
1895 |
if (CollectionRemark.RBM_L3_ESCALATION.equals(remark.getRemark())) {
|
|
|
1896 |
// Find which L1 RBM originally has this partner assigned
|
|
|
1897 |
for (int l1AuthId : l1AuthIds) {
|
|
|
1898 |
List<Integer> l1FofoIds = rbmToFofoIdsMap.getOrDefault(l1AuthId, Collections.emptyList());
|
|
|
1899 |
if (l1FofoIds.contains(fofoId)) {
|
|
|
1900 |
AuthUser l1User = authUserMap.get(l1AuthId);
|
|
|
1901 |
if (l1User != null) {
|
| 36212 |
ranu |
1902 |
int l1ManagerId = l1User.getManagerId();
|
|
|
1903 |
// Case 1: L1's manager IS L3 directly (L1 → L3, no L2 in between)
|
|
|
1904 |
if (l3AuthIdToFofoIds.containsKey(l1ManagerId)) {
|
|
|
1905 |
l3AuthIdToFofoIds.get(l1ManagerId).add(fofoId);
|
|
|
1906 |
LOGGER.debug("L3 Calling List (direct): fofoId={} -> L1={} -> L3={}",
|
|
|
1907 |
fofoId, l1AuthId, l1ManagerId);
|
|
|
1908 |
} else {
|
|
|
1909 |
// Case 2: L1 -> L2 -> L3
|
|
|
1910 |
AuthUser l2User = authUserMap.get(l1ManagerId);
|
|
|
1911 |
if (l2User != null && l3AuthIdToFofoIds.containsKey(l2User.getManagerId())) {
|
|
|
1912 |
int l3ManagerId = l2User.getManagerId();
|
|
|
1913 |
l3AuthIdToFofoIds.get(l3ManagerId).add(fofoId);
|
|
|
1914 |
LOGGER.debug("L3 Calling List: fofoId={} -> L1={} -> L2={} -> L3={}",
|
|
|
1915 |
fofoId, l1AuthId, l1ManagerId, l3ManagerId);
|
|
|
1916 |
}
|
| 36210 |
ranu |
1917 |
}
|
|
|
1918 |
}
|
|
|
1919 |
break; // Found the L1 for this fofoId
|
|
|
1920 |
}
|
|
|
1921 |
}
|
|
|
1922 |
}
|
|
|
1923 |
}
|
|
|
1924 |
LOGGER.info("RBM Call Target - L3 calling lists populated from RBM_L3_ESCALATION remarks");
|
|
|
1925 |
|
| 35631 |
ranu |
1926 |
// Batch fetch collection RANK map for all fofoIds (OPTIMIZED - only fetches rank, not full model)
|
|
|
1927 |
start = System.currentTimeMillis();
|
|
|
1928 |
Map<Integer, Integer> allCollectionRankMap = new HashMap<>();
|
|
|
1929 |
if (!allFofoIds.isEmpty()) {
|
|
|
1930 |
try {
|
|
|
1931 |
allCollectionRankMap = partnerCollectionService.getCollectionRankMap(new ArrayList<>(allFofoIds), startDate);
|
|
|
1932 |
} catch (ProfitMandiBusinessException e) {
|
|
|
1933 |
LOGGER.error("Error fetching collection rank map for all fofoIds", e);
|
|
|
1934 |
}
|
|
|
1935 |
}
|
|
|
1936 |
LOGGER.info("RBM Call Target - CollectionRankMap fetch (OPTIMIZED): {}ms", System.currentTimeMillis() - start);
|
|
|
1937 |
|
| 35669 |
ranu |
1938 |
// Get MTD billing data for zero billing calculation and partner counts
|
| 35631 |
ranu |
1939 |
start = System.currentTimeMillis();
|
|
|
1940 |
List<RbmWeeklyBillingModel> mtdBillingData = getWeeklyBillingDataForMonth(firstOfMonth, endOfMonth);
|
|
|
1941 |
Set<Integer> allMtdBilledFofoIds = mtdBillingData.stream()
|
|
|
1942 |
.filter(RbmWeeklyBillingModel::isMtdBilled)
|
|
|
1943 |
.map(RbmWeeklyBillingModel::getFofoId)
|
|
|
1944 |
.collect(Collectors.toSet());
|
| 35669 |
ranu |
1945 |
// Build partner count and fofoIds per RBM from mtdBillingData (same source as Today ARR page)
|
|
|
1946 |
Map<Integer, Set<Integer>> mtdFofoIdsByAuthId = mtdBillingData.stream()
|
|
|
1947 |
.filter(RbmWeeklyBillingModel::isTargetedPartner)
|
|
|
1948 |
.collect(Collectors.groupingBy(RbmWeeklyBillingModel::getAuthId,
|
|
|
1949 |
Collectors.mapping(RbmWeeklyBillingModel::getFofoId, Collectors.toSet())));
|
| 35631 |
ranu |
1950 |
LOGGER.info("RBM Call Target - MTD Billing fetch: {}ms", System.currentTimeMillis() - start);
|
|
|
1951 |
|
|
|
1952 |
// Batch fetch today's remarks for all auth IDs (to calculate Value Achieved)
|
|
|
1953 |
start = System.currentTimeMillis();
|
|
|
1954 |
Map<Integer, List<PartnerCollectionRemark>> remarksByAuthId = partnerCollectionRemarkRepository
|
|
|
1955 |
.selectAllByAuthIdsOnDate(rbmPositionsAuthIds, LocalDate.now()).stream()
|
|
|
1956 |
.collect(Collectors.groupingBy(PartnerCollectionRemark::getAuthId));
|
|
|
1957 |
LOGGER.info("RBM Call Target - Today Remarks fetch: {}ms", System.currentTimeMillis() - start);
|
|
|
1958 |
|
|
|
1959 |
// Batch fetch today's out-of-sequence logs for all RBMs
|
|
|
1960 |
start = System.currentTimeMillis();
|
|
|
1961 |
LocalDateTime todayStart = LocalDate.now().atStartOfDay();
|
|
|
1962 |
LocalDateTime todayEnd = LocalDate.now().plusDays(1).atStartOfDay();
|
|
|
1963 |
List<RbmCallSequenceLog> outOfSequenceLogs = rbmCallSequenceLogRepository.selectOutOfSequenceByDateRange(todayStart, todayEnd);
|
|
|
1964 |
Map<Integer, Long> outOfSequenceCountByAuthId = outOfSequenceLogs.stream()
|
| 35654 |
ranu |
1965 |
.collect(Collectors.groupingBy(RbmCallSequenceLog::getAuthId,
|
|
|
1966 |
Collectors.mapping(RbmCallSequenceLog::getFofoId, Collectors.collectingAndThen(Collectors.toSet(), s -> (long) s.size()))));
|
| 35631 |
ranu |
1967 |
LOGGER.info("RBM Call Target - Out of Sequence fetch: {}ms", System.currentTimeMillis() - start);
|
|
|
1968 |
|
| 36284 |
ranu |
1969 |
// BATCH FETCH: All call logs for all RBMs (L1 + L2 + L3) in a single query.
|
|
|
1970 |
// Replaces the previous N+1 pattern where getCallStats() was called per RBM.
|
|
|
1971 |
start = System.currentTimeMillis();
|
|
|
1972 |
List<AgentCallLog> allCallLogs = agentCallLogRepository.findByAuthIdsAndDate(rbmPositionsAuthIds, queryDate);
|
|
|
1973 |
Map<Long, List<AgentCallLog>> callLogsByAuthId = allCallLogs.stream()
|
|
|
1974 |
.collect(Collectors.groupingBy(AgentCallLog::getAuthId));
|
|
|
1975 |
LOGGER.info("RBM Call Target - Call logs batch fetch: {}ms ({} logs across {} RBMs)",
|
|
|
1976 |
System.currentTimeMillis() - start, allCallLogs.size(), callLogsByAuthId.size());
|
|
|
1977 |
|
|
|
1978 |
// BATCH FETCH: Build a single mobile -> fofoId map from all unique customer numbers across all call logs.
|
|
|
1979 |
// Replaces the previous N+1 pattern where findFofoIdByMobile() was called per call log entry.
|
|
|
1980 |
start = System.currentTimeMillis();
|
|
|
1981 |
Set<String> allNormalizedMobiles = new HashSet<>();
|
|
|
1982 |
for (AgentCallLog callLog : allCallLogs) {
|
|
|
1983 |
String customerNumber = callLog.getCustomerNumber();
|
|
|
1984 |
if (customerNumber != null) {
|
|
|
1985 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
|
|
1986 |
allNormalizedMobiles.add(normalized);
|
|
|
1987 |
}
|
|
|
1988 |
}
|
|
|
1989 |
Map<String, Integer> mobileToFofoIdMap = buildMobileToFofoIdMap(allNormalizedMobiles);
|
|
|
1990 |
LOGGER.info("RBM Call Target - Mobile→FofoId batch fetch: {}ms ({} unique mobiles, {} mapped)",
|
|
|
1991 |
System.currentTimeMillis() - start, allNormalizedMobiles.size(), mobileToFofoIdMap.size());
|
|
|
1992 |
|
| 36225 |
ranu |
1993 |
// Identify users who are both L1 and L2 — they will be shown only as L2
|
|
|
1994 |
Set<Integer> l2AuthIdSet = new HashSet<>(l2AuthIds);
|
|
|
1995 |
|
|
|
1996 |
// Process L1 RBMs (skip users who are also L2 — their data will be merged into L2 model)
|
| 35631 |
ranu |
1997 |
for (int rbmAuthId : l1AuthIds) {
|
| 36225 |
ranu |
1998 |
if (l2AuthIdSet.contains(rbmAuthId)) {
|
|
|
1999 |
continue; // Will be handled in L2 processing with merged L1 data
|
|
|
2000 |
}
|
| 35631 |
ranu |
2001 |
AuthUser authUser = authUserMap.get(rbmAuthId);
|
|
|
2002 |
if (authUser == null || !storeGuyMap.containsKey(authUser.getEmailId())) {
|
|
|
2003 |
continue;
|
|
|
2004 |
}
|
|
|
2005 |
|
|
|
2006 |
List<Integer> fofoIdList = rbmToFofoIdsMap.getOrDefault(rbmAuthId, Collections.emptyList());
|
|
|
2007 |
|
|
|
2008 |
// Check if RBM is L1 (same logic as getSummaryModel)
|
|
|
2009 |
List<Position> positions = positionsByAuthId.getOrDefault(authUser.getId(), Collections.emptyList());
|
|
|
2010 |
boolean isRBMAndL1 = positions.stream()
|
|
|
2011 |
.anyMatch(position ->
|
|
|
2012 |
ProfitMandiConstants.TICKET_CATEGORY_RBM == position.getCategoryId()
|
|
|
2013 |
&& EscalationType.L1.equals(position.getEscalationType()));
|
|
|
2014 |
|
|
|
2015 |
// Filter escalated partners for L1 RBMs (same logic as getSummaryModel)
|
|
|
2016 |
List<Integer> fofoIds = fofoIdList;
|
|
|
2017 |
if (isRBMAndL1) {
|
|
|
2018 |
Map<Integer, PartnerCollectionRemark> partnerCollectionRemarks = new HashMap<>();
|
|
|
2019 |
for (Integer fofoId : fofoIdList) {
|
|
|
2020 |
if (allPartnerCollectionRemarks.containsKey(fofoId)) {
|
|
|
2021 |
partnerCollectionRemarks.put(fofoId, allPartnerCollectionRemarks.get(fofoId));
|
|
|
2022 |
}
|
|
|
2023 |
}
|
|
|
2024 |
fofoIds = partnerCollectionRemarks.entrySet().stream()
|
|
|
2025 |
.filter(entry -> {
|
|
|
2026 |
PartnerCollectionRemark pcrMap = entry.getValue();
|
|
|
2027 |
return !(CollectionRemark.RBM_L2_ESCALATION.equals(pcrMap.getRemark())
|
|
|
2028 |
|| CollectionRemark.SALES_ESCALATION.equals(pcrMap.getRemark()));
|
|
|
2029 |
})
|
|
|
2030 |
.map(Map.Entry::getKey)
|
|
|
2031 |
.collect(Collectors.toList());
|
|
|
2032 |
}
|
|
|
2033 |
|
| 36181 |
ranu |
2034 |
// Filter to only external, ACTIVE or REVIVAL stores (collection plan not required)
|
| 35631 |
ranu |
2035 |
Map<Integer, Integer> finalAllCollectionRankMap = allCollectionRankMap;
|
|
|
2036 |
Map<Integer, FofoStore> finalFofoStoresMap = fofoStoresMap;
|
|
|
2037 |
List<Integer> validFofoIds = fofoIds.stream()
|
|
|
2038 |
.filter(fofoId -> {
|
|
|
2039 |
FofoStore store = finalFofoStoresMap.get(fofoId);
|
|
|
2040 |
if (store == null || store.isInternal()) {
|
|
|
2041 |
return false;
|
|
|
2042 |
}
|
| 36181 |
ranu |
2043 |
// Only include ACTIVE or REVIVAL partners (not Low Sale, not Disputed, not Billing Pending)
|
|
|
2044 |
return ActivationType.ACTIVE.equals(store.getActivationType())
|
|
|
2045 |
|| ActivationType.REVIVAL.equals(store.getActivationType());
|
| 35631 |
ranu |
2046 |
})
|
|
|
2047 |
.collect(Collectors.toList());
|
|
|
2048 |
|
|
|
2049 |
if (validFofoIds.isEmpty()) {
|
|
|
2050 |
continue;
|
|
|
2051 |
}
|
|
|
2052 |
|
|
|
2053 |
RbmCallTargetModel targetModel = new RbmCallTargetModel();
|
|
|
2054 |
targetModel.setAuthId(rbmAuthId);
|
|
|
2055 |
targetModel.setRbmName(authUser.getFullName());
|
| 35669 |
ranu |
2056 |
// Use partner count from mtdBillingData (same source as Today ARR page)
|
|
|
2057 |
Set<Integer> mtdFofoIds = mtdFofoIdsByAuthId.getOrDefault(rbmAuthId, Collections.emptySet());
|
|
|
2058 |
targetModel.setPartnerCount(mtdFofoIds.size());
|
| 35631 |
ranu |
2059 |
|
|
|
2060 |
// Categorize each partner - each partner belongs to ONE category only
|
| 35665 |
ranu |
2061 |
// Priority: PlanToday > CarryForward > ZeroBilling > Untouched > FuturePlan > Normal
|
| 36181 |
ranu |
2062 |
// Revival is counted separately (just for display, doesn't affect categorization)
|
| 35631 |
ranu |
2063 |
Set<Integer> planTodayPartners = new HashSet<>();
|
|
|
2064 |
Set<Integer> carryForwardPartners = new HashSet<>();
|
|
|
2065 |
Set<Integer> untouchedPartners = new HashSet<>();
|
|
|
2066 |
Set<Integer> zeroBillingPartners = new HashSet<>();
|
|
|
2067 |
Set<Integer> futurePlanPartners = new HashSet<>();
|
|
|
2068 |
Set<Integer> normalPartners = new HashSet<>();
|
| 36181 |
ranu |
2069 |
Set<Integer> revivalPartners = new HashSet<>();
|
| 35631 |
ranu |
2070 |
|
|
|
2071 |
for (Integer fofoId : validFofoIds) {
|
|
|
2072 |
// Get collection plan rank (from optimized rank map)
|
|
|
2073 |
int rank = allCollectionRankMap.getOrDefault(fofoId, 5); // default to Normal if no plan
|
|
|
2074 |
|
|
|
2075 |
// Check if partner has zero billing in MTD
|
|
|
2076 |
boolean hasZeroBilling = !allMtdBilledFofoIds.contains(fofoId);
|
|
|
2077 |
|
| 36181 |
ranu |
2078 |
// Count REVIVAL partners separately (just for display, doesn't affect categorization)
|
|
|
2079 |
FofoStore store = finalFofoStoresMap.get(fofoId);
|
|
|
2080 |
if (store != null && ActivationType.REVIVAL.equals(store.getActivationType())) {
|
|
|
2081 |
revivalPartners.add(fofoId);
|
|
|
2082 |
}
|
|
|
2083 |
|
| 35631 |
ranu |
2084 |
// Assign to category based on priority
|
|
|
2085 |
if (rank == 1) {
|
|
|
2086 |
planTodayPartners.add(fofoId);
|
|
|
2087 |
} else if (rank == 2) {
|
|
|
2088 |
carryForwardPartners.add(fofoId);
|
| 35665 |
ranu |
2089 |
} else if (hasZeroBilling) {
|
|
|
2090 |
zeroBillingPartners.add(fofoId);
|
| 35631 |
ranu |
2091 |
} else if (rank == 3) {
|
|
|
2092 |
untouchedPartners.add(fofoId);
|
|
|
2093 |
} else if (rank == 4) {
|
|
|
2094 |
futurePlanPartners.add(fofoId);
|
|
|
2095 |
} else {
|
|
|
2096 |
normalPartners.add(fofoId);
|
|
|
2097 |
}
|
|
|
2098 |
}
|
|
|
2099 |
|
|
|
2100 |
// Set counts
|
|
|
2101 |
targetModel.setCreditCollection(0); // Credit collection is handled in separate list
|
|
|
2102 |
targetModel.setPlanToday(planTodayPartners.size());
|
|
|
2103 |
targetModel.setCarryForward(carryForwardPartners.size());
|
|
|
2104 |
targetModel.setUntouched(untouchedPartners.size());
|
|
|
2105 |
targetModel.setZeroBilling(zeroBillingPartners.size());
|
|
|
2106 |
targetModel.setFuturePlan(futurePlanPartners.size());
|
|
|
2107 |
targetModel.setNormal(normalPartners.size());
|
| 36181 |
ranu |
2108 |
targetModel.setRevival(revivalPartners.size());
|
| 35631 |
ranu |
2109 |
|
|
|
2110 |
// Today Target = PlanToday + CarryForward + ZeroBilling + Untouched
|
|
|
2111 |
// These are mutually exclusive now, so we can sum them
|
|
|
2112 |
long todayTarget = planTodayPartners.size() +
|
|
|
2113 |
carryForwardPartners.size() + zeroBillingPartners.size() + untouchedPartners.size();
|
|
|
2114 |
targetModel.setTodayTargetOfCall(todayTarget);
|
|
|
2115 |
|
|
|
2116 |
// Create set of partners in Today Target categories
|
|
|
2117 |
Set<Integer> todayTargetPartners = new HashSet<>();
|
|
|
2118 |
todayTargetPartners.addAll(planTodayPartners);
|
|
|
2119 |
todayTargetPartners.addAll(carryForwardPartners);
|
|
|
2120 |
todayTargetPartners.addAll(zeroBillingPartners);
|
|
|
2121 |
todayTargetPartners.addAll(untouchedPartners);
|
|
|
2122 |
|
| 36284 |
ranu |
2123 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
| 37010 |
ranu |
2124 |
List<AgentCallLog> l1Logs = callLogsByAuthId.get((long) rbmAuthId);
|
|
|
2125 |
long[] callStats = getCallStatsFromLogs(l1Logs, mobileToFofoIdMap);
|
| 36276 |
ranu |
2126 |
targetModel.setValueTargetAchieved(callStats[0]);
|
|
|
2127 |
targetModel.setTotalRecordingCalls(callStats[1]);
|
|
|
2128 |
targetModel.setUniqueRecordingCalls(callStats[2]);
|
| 37022 |
ranu |
2129 |
targetModel.setTriedUniqueCalls(computeUniqueTriedCount(l1Logs, mobileToFofoIdMap));
|
|
|
2130 |
targetModel.setTgtCalls(computeUniqueTriedCountForTargets(l1Logs, mobileToFofoIdMap, todayTargetPartners));
|
| 35631 |
ranu |
2131 |
|
| 37012 |
ranu |
2132 |
// Keep todayRemarks for movedToFuture calculation
|
| 35843 |
ranu |
2133 |
List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
|
|
|
2134 |
|
| 35631 |
ranu |
2135 |
// Moved to Future = Partners in Future Plan category who have a remark today
|
|
|
2136 |
// These are partners who were contacted today but moved to a future date
|
|
|
2137 |
Set<Integer> todayRemarkedFofoIds = todayRemarks.stream()
|
|
|
2138 |
.map(PartnerCollectionRemark::getFofoId)
|
|
|
2139 |
.collect(Collectors.toSet());
|
|
|
2140 |
long movedToFuture = futurePlanPartners.stream()
|
|
|
2141 |
.filter(todayRemarkedFofoIds::contains)
|
|
|
2142 |
.count();
|
|
|
2143 |
targetModel.setMovedToFuture(movedToFuture);
|
|
|
2144 |
|
|
|
2145 |
// Set out of sequence count for this RBM
|
|
|
2146 |
targetModel.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(rbmAuthId, 0L));
|
|
|
2147 |
|
|
|
2148 |
rbmCallTargetModels.add(targetModel);
|
|
|
2149 |
}
|
|
|
2150 |
|
|
|
2151 |
// Process L2 RBMs (escalated ticket logic with categorization)
|
| 36225 |
ranu |
2152 |
// For users who are both L1 and L2, merge their L1 calling target into L2 model
|
| 35631 |
ranu |
2153 |
for (int l2AuthId : l2AuthIds) {
|
|
|
2154 |
AuthUser authUser = authUserMap.get(l2AuthId);
|
|
|
2155 |
if (authUser == null) {
|
|
|
2156 |
continue;
|
|
|
2157 |
}
|
|
|
2158 |
|
|
|
2159 |
List<Integer> l2FofoIdList = l2AuthIdToFofoIds.getOrDefault(l2AuthId, Collections.emptyList());
|
|
|
2160 |
|
| 35816 |
ranu |
2161 |
// For L2, use unique fofoIds with RBM_L2_ESCALATION remark as target
|
| 35662 |
ranu |
2162 |
Set<Integer> l2TargetFofoIds = new HashSet<>(l2FofoIdList);
|
| 35631 |
ranu |
2163 |
|
| 37022 |
ranu |
2164 |
// Collected from the L1-merge branch below when the user is also L1. Used later
|
|
|
2165 |
// for the TGT Calls scope: L2 escalation + L1 target buckets.
|
|
|
2166 |
Set<Integer> l1MergedTargetPartners = new HashSet<>();
|
|
|
2167 |
|
| 35631 |
ranu |
2168 |
RbmCallTargetModel l2Model = new RbmCallTargetModel();
|
|
|
2169 |
l2Model.setAuthId(l2AuthId);
|
|
|
2170 |
l2Model.setRbmName(authUser.getFullName() + " (L2)");
|
|
|
2171 |
l2Model.setL2Position(true);
|
| 35816 |
ranu |
2172 |
l2Model.setL2CallingList(l2TargetFofoIds.size());
|
| 36228 |
ranu |
2173 |
// Partner count: if user is also L1, use L1 partner count (MTD targeted partners)
|
|
|
2174 |
if (l1AuthIds.contains(l2AuthId)) {
|
|
|
2175 |
Set<Integer> mtdFofoIds = mtdFofoIdsByAuthId.getOrDefault(l2AuthId, Collections.emptySet());
|
|
|
2176 |
l2Model.setPartnerCount(mtdFofoIds.size());
|
|
|
2177 |
} else {
|
|
|
2178 |
List<Integer> l2AssignedFofoIds = rbmToFofoIdsMap.getOrDefault(l2AuthId, Collections.emptyList());
|
|
|
2179 |
l2Model.setPartnerCount(l2AssignedFofoIds.size());
|
|
|
2180 |
}
|
| 35631 |
ranu |
2181 |
|
| 36229 |
ranu |
2182 |
// If user is also L1, calculate full L1 breakdown and merge into L2 model
|
| 36225 |
ranu |
2183 |
if (l1AuthIds.contains(l2AuthId) && storeGuyMap.containsKey(authUser.getEmailId())) {
|
| 36229 |
ranu |
2184 |
// Get only L1 RBM position partners (not L2 partners) from partner_position
|
| 36225 |
ranu |
2185 |
List<Position> positions = positionsByAuthId.getOrDefault(l2AuthId, Collections.emptyList());
|
| 36229 |
ranu |
2186 |
Set<Integer> l1RbmPositionIds = positions.stream()
|
|
|
2187 |
.filter(p -> ProfitMandiConstants.TICKET_CATEGORY_RBM == p.getCategoryId()
|
|
|
2188 |
&& EscalationType.L1.equals(p.getEscalationType()))
|
|
|
2189 |
.map(Position::getId)
|
|
|
2190 |
.collect(Collectors.toSet());
|
|
|
2191 |
// Fetch partner_position for only L1 RBM positions of this user
|
|
|
2192 |
List<Integer> fofoIdList = partnerPositionRepository
|
|
|
2193 |
.selectByPositionIds(new ArrayList<>(l1RbmPositionIds)).stream()
|
|
|
2194 |
.map(pp -> pp.getFofoId())
|
|
|
2195 |
.distinct()
|
|
|
2196 |
.collect(Collectors.toList());
|
|
|
2197 |
boolean isRBMAndL1 = !l1RbmPositionIds.isEmpty();
|
| 36225 |
ranu |
2198 |
List<Integer> l1FofoIds = new ArrayList<>(fofoIdList);
|
|
|
2199 |
if (isRBMAndL1) {
|
|
|
2200 |
Map<Integer, PartnerCollectionRemark> partnerCollectionRemarks = new HashMap<>();
|
|
|
2201 |
for (Integer fofoId : fofoIdList) {
|
|
|
2202 |
if (allPartnerCollectionRemarks.containsKey(fofoId)) {
|
|
|
2203 |
partnerCollectionRemarks.put(fofoId, allPartnerCollectionRemarks.get(fofoId));
|
|
|
2204 |
}
|
|
|
2205 |
}
|
|
|
2206 |
l1FofoIds = partnerCollectionRemarks.entrySet().stream()
|
|
|
2207 |
.filter(entry -> {
|
|
|
2208 |
PartnerCollectionRemark pcrMap = entry.getValue();
|
|
|
2209 |
return !(CollectionRemark.RBM_L2_ESCALATION.equals(pcrMap.getRemark())
|
|
|
2210 |
|| CollectionRemark.SALES_ESCALATION.equals(pcrMap.getRemark()));
|
|
|
2211 |
})
|
|
|
2212 |
.map(Map.Entry::getKey)
|
|
|
2213 |
.collect(Collectors.toList());
|
|
|
2214 |
}
|
|
|
2215 |
Map<Integer, FofoStore> finalFofoStoresMap2 = fofoStoresMap;
|
|
|
2216 |
List<Integer> validL1FofoIds = l1FofoIds.stream()
|
|
|
2217 |
.filter(fofoId -> {
|
|
|
2218 |
FofoStore store = finalFofoStoresMap2.get(fofoId);
|
|
|
2219 |
if (store == null || store.isInternal()) return false;
|
|
|
2220 |
return ActivationType.ACTIVE.equals(store.getActivationType())
|
|
|
2221 |
|| ActivationType.REVIVAL.equals(store.getActivationType());
|
|
|
2222 |
})
|
|
|
2223 |
.collect(Collectors.toList());
|
| 35631 |
ranu |
2224 |
|
| 36229 |
ranu |
2225 |
// Categorize L1 partners — same logic as L1 processing
|
|
|
2226 |
Set<Integer> planTodayPartners = new HashSet<>();
|
|
|
2227 |
Set<Integer> carryForwardPartners = new HashSet<>();
|
|
|
2228 |
Set<Integer> untouchedPartners = new HashSet<>();
|
|
|
2229 |
Set<Integer> zeroBillingPartners = new HashSet<>();
|
|
|
2230 |
Set<Integer> futurePlanPartners = new HashSet<>();
|
|
|
2231 |
Set<Integer> normalPartners = new HashSet<>();
|
|
|
2232 |
Set<Integer> revivalPartners = new HashSet<>();
|
|
|
2233 |
|
| 36225 |
ranu |
2234 |
for (Integer fofoId : validL1FofoIds) {
|
|
|
2235 |
int rank = allCollectionRankMap.getOrDefault(fofoId, 5);
|
|
|
2236 |
boolean hasZeroBilling = !allMtdBilledFofoIds.contains(fofoId);
|
| 36229 |
ranu |
2237 |
FofoStore store = finalFofoStoresMap2.get(fofoId);
|
|
|
2238 |
if (store != null && ActivationType.REVIVAL.equals(store.getActivationType())) {
|
|
|
2239 |
revivalPartners.add(fofoId);
|
| 36225 |
ranu |
2240 |
}
|
| 36229 |
ranu |
2241 |
if (rank == 1) {
|
|
|
2242 |
planTodayPartners.add(fofoId);
|
|
|
2243 |
} else if (rank == 2) {
|
|
|
2244 |
carryForwardPartners.add(fofoId);
|
|
|
2245 |
} else if (hasZeroBilling) {
|
|
|
2246 |
zeroBillingPartners.add(fofoId);
|
|
|
2247 |
} else if (rank == 3) {
|
|
|
2248 |
untouchedPartners.add(fofoId);
|
|
|
2249 |
} else if (rank == 4) {
|
|
|
2250 |
futurePlanPartners.add(fofoId);
|
|
|
2251 |
} else {
|
|
|
2252 |
normalPartners.add(fofoId);
|
|
|
2253 |
}
|
| 36225 |
ranu |
2254 |
}
|
| 36229 |
ranu |
2255 |
|
|
|
2256 |
// Set L1 breakdown fields on L2 model
|
|
|
2257 |
l2Model.setPlanToday(planTodayPartners.size());
|
|
|
2258 |
l2Model.setCarryForward(carryForwardPartners.size());
|
|
|
2259 |
l2Model.setZeroBilling(zeroBillingPartners.size());
|
|
|
2260 |
l2Model.setUntouched(untouchedPartners.size());
|
|
|
2261 |
l2Model.setFuturePlan(futurePlanPartners.size());
|
|
|
2262 |
l2Model.setNormal(normalPartners.size());
|
|
|
2263 |
l2Model.setRevival(revivalPartners.size());
|
|
|
2264 |
|
|
|
2265 |
long l1OwnTarget = planTodayPartners.size() + carryForwardPartners.size()
|
|
|
2266 |
+ zeroBillingPartners.size() + untouchedPartners.size();
|
|
|
2267 |
|
| 37022 |
ranu |
2268 |
// Feed the L1 target sets up to the outer scope so TGT Calls can include them.
|
|
|
2269 |
l1MergedTargetPartners.addAll(planTodayPartners);
|
|
|
2270 |
l1MergedTargetPartners.addAll(carryForwardPartners);
|
|
|
2271 |
l1MergedTargetPartners.addAll(zeroBillingPartners);
|
|
|
2272 |
l1MergedTargetPartners.addAll(untouchedPartners);
|
|
|
2273 |
|
| 36229 |
ranu |
2274 |
// Today Target = own L1 target + L2 escalation
|
|
|
2275 |
l2Model.setTodayTargetOfCall(l2TargetFofoIds.size() + l1OwnTarget);
|
|
|
2276 |
|
|
|
2277 |
// Moved to Future from L1 partners
|
|
|
2278 |
List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(l2AuthId, Collections.emptyList());
|
|
|
2279 |
Set<Integer> todayRemarkedFofoIds = todayRemarks.stream()
|
|
|
2280 |
.map(PartnerCollectionRemark::getFofoId)
|
|
|
2281 |
.collect(Collectors.toSet());
|
|
|
2282 |
long movedToFuture = futurePlanPartners.stream()
|
|
|
2283 |
.filter(todayRemarkedFofoIds::contains)
|
|
|
2284 |
.count();
|
|
|
2285 |
l2Model.setMovedToFuture(movedToFuture);
|
|
|
2286 |
} else {
|
|
|
2287 |
// Pure L2 (not also L1) — target is only L2 escalation
|
|
|
2288 |
l2Model.setTodayTargetOfCall(l2TargetFofoIds.size());
|
| 36225 |
ranu |
2289 |
}
|
|
|
2290 |
|
| 36284 |
ranu |
2291 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
| 37010 |
ranu |
2292 |
List<AgentCallLog> l2Logs = callLogsByAuthId.get((long) l2AuthId);
|
|
|
2293 |
long[] l2CallStats = getCallStatsFromLogs(l2Logs, mobileToFofoIdMap);
|
| 36276 |
ranu |
2294 |
l2Model.setValueTargetAchieved(l2CallStats[0]);
|
|
|
2295 |
l2Model.setTotalRecordingCalls(l2CallStats[1]);
|
|
|
2296 |
l2Model.setUniqueRecordingCalls(l2CallStats[2]);
|
| 37022 |
ranu |
2297 |
l2Model.setTriedUniqueCalls(computeUniqueTriedCount(l2Logs, mobileToFofoIdMap));
|
| 35631 |
ranu |
2298 |
|
| 37022 |
ranu |
2299 |
// TGT Calls scope: L2 escalation + (if user is also L1) the L1 target buckets.
|
|
|
2300 |
Set<Integer> l2TgtSet = new HashSet<>(l2TargetFofoIds);
|
|
|
2301 |
l2TgtSet.addAll(l1MergedTargetPartners);
|
|
|
2302 |
l2Model.setTgtCalls(computeUniqueTriedCountForTargets(l2Logs, mobileToFofoIdMap, l2TgtSet));
|
|
|
2303 |
|
| 35631 |
ranu |
2304 |
l2Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l2AuthId, 0L));
|
|
|
2305 |
rbmCallTargetModels.add(l2Model);
|
|
|
2306 |
}
|
|
|
2307 |
|
| 36210 |
ranu |
2308 |
// Process L3 RBMs (escalated ticket logic with categorization)
|
|
|
2309 |
for (int l3AuthId : l3AuthIds) {
|
|
|
2310 |
AuthUser authUser = authUserMap.get(l3AuthId);
|
|
|
2311 |
if (authUser == null) {
|
|
|
2312 |
continue;
|
|
|
2313 |
}
|
|
|
2314 |
|
|
|
2315 |
List<Integer> l3FofoIdList = l3AuthIdToFofoIds.getOrDefault(l3AuthId, Collections.emptyList());
|
|
|
2316 |
|
|
|
2317 |
// For L3, use unique fofoIds with RBM_L3_ESCALATION remark as target
|
|
|
2318 |
Set<Integer> l3TargetFofoIds = new HashSet<>(l3FofoIdList);
|
|
|
2319 |
|
|
|
2320 |
RbmCallTargetModel l3Model = new RbmCallTargetModel();
|
|
|
2321 |
l3Model.setAuthId(l3AuthId);
|
|
|
2322 |
l3Model.setRbmName(authUser.getFullName() + " (L3)");
|
|
|
2323 |
l3Model.setL3Position(true);
|
|
|
2324 |
l3Model.setL3CallingList(l3TargetFofoIds.size());
|
| 36359 |
ranu |
2325 |
// Partner count = total assigned partners (excluding internal)
|
|
|
2326 |
Map<Integer, FofoStore> finalFofoStoresMapL3 = fofoStoresMap;
|
| 36210 |
ranu |
2327 |
List<Integer> l3AssignedFofoIds = rbmToFofoIdsMap.getOrDefault(l3AuthId, Collections.emptyList());
|
| 36359 |
ranu |
2328 |
long l3ExternalPartnerCount = l3AssignedFofoIds.stream()
|
|
|
2329 |
.filter(fofoId -> {
|
|
|
2330 |
FofoStore store = finalFofoStoresMapL3.get(fofoId);
|
|
|
2331 |
return store != null && !store.isInternal();
|
|
|
2332 |
})
|
|
|
2333 |
.count();
|
|
|
2334 |
l3Model.setPartnerCount(l3ExternalPartnerCount);
|
| 36210 |
ranu |
2335 |
|
|
|
2336 |
// L3 Target = partners with RBM_L3_ESCALATION as latest remark
|
|
|
2337 |
l3Model.setTodayTargetOfCall(l3TargetFofoIds.size());
|
|
|
2338 |
|
| 36284 |
ranu |
2339 |
// Value Achieved = All distinct partners called today (from pre-fetched call logs)
|
| 37010 |
ranu |
2340 |
List<AgentCallLog> l3Logs = callLogsByAuthId.get((long) l3AuthId);
|
|
|
2341 |
long[] l3CallStats = getCallStatsFromLogs(l3Logs, mobileToFofoIdMap);
|
| 36276 |
ranu |
2342 |
l3Model.setValueTargetAchieved(l3CallStats[0]);
|
|
|
2343 |
l3Model.setTotalRecordingCalls(l3CallStats[1]);
|
|
|
2344 |
l3Model.setUniqueRecordingCalls(l3CallStats[2]);
|
| 37022 |
ranu |
2345 |
l3Model.setTriedUniqueCalls(computeUniqueTriedCount(l3Logs, mobileToFofoIdMap));
|
|
|
2346 |
// L3 TGT Calls scope: L3 escalation partners.
|
|
|
2347 |
l3Model.setTgtCalls(computeUniqueTriedCountForTargets(l3Logs, mobileToFofoIdMap, l3TargetFofoIds));
|
| 36210 |
ranu |
2348 |
|
|
|
2349 |
l3Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l3AuthId, 0L));
|
|
|
2350 |
rbmCallTargetModels.add(l3Model);
|
|
|
2351 |
}
|
|
|
2352 |
|
| 36225 |
ranu |
2353 |
// Group models by escalation level
|
| 36210 |
ranu |
2354 |
Map<Integer, RbmCallTargetModel> l3ModelsByAuthId = new HashMap<>();
|
| 35631 |
ranu |
2355 |
Map<Integer, RbmCallTargetModel> l2ModelsByAuthId = new HashMap<>();
|
|
|
2356 |
Map<Integer, RbmCallTargetModel> l1ModelsByAuthId = new HashMap<>();
|
|
|
2357 |
for (RbmCallTargetModel m : rbmCallTargetModels) {
|
| 36210 |
ranu |
2358 |
if (m.isL3Position()) {
|
|
|
2359 |
l3ModelsByAuthId.put(m.getAuthId(), m);
|
|
|
2360 |
} else if (m.isL2Position()) {
|
| 35631 |
ranu |
2361 |
l2ModelsByAuthId.put(m.getAuthId(), m);
|
|
|
2362 |
} else {
|
|
|
2363 |
l1ModelsByAuthId.put(m.getAuthId(), m);
|
|
|
2364 |
}
|
|
|
2365 |
}
|
|
|
2366 |
|
| 36225 |
ranu |
2367 |
// Build partner-based hierarchy using partner_position table
|
|
|
2368 |
// Map positionId -> Position for quick lookup
|
|
|
2369 |
Map<Integer, Position> positionByIdMap = allRbmPositions.stream()
|
|
|
2370 |
.collect(Collectors.toMap(Position::getId, p -> p, (a, b) -> a));
|
|
|
2371 |
|
|
|
2372 |
// Get all RBM position IDs and fetch their partner assignments
|
|
|
2373 |
List<Integer> allRbmPositionIds = allRbmPositions.stream()
|
|
|
2374 |
.map(Position::getId).collect(Collectors.toList());
|
|
|
2375 |
List<com.spice.profitmandi.dao.entity.cs.PartnerPosition> allPartnerPositions =
|
|
|
2376 |
partnerPositionRepository.selectByPositionIds(allRbmPositionIds);
|
|
|
2377 |
|
|
|
2378 |
// Build partnerId -> map of escalationType -> Set<authUserIds>
|
|
|
2379 |
// This tells us for each partner, who is their L1, L2, L3
|
|
|
2380 |
Map<Integer, Map<EscalationType, Set<Integer>>> partnerToAuthByLevel = new HashMap<>();
|
|
|
2381 |
for (com.spice.profitmandi.dao.entity.cs.PartnerPosition pp : allPartnerPositions) {
|
|
|
2382 |
Position pos = positionByIdMap.get(pp.getPositionId());
|
|
|
2383 |
if (pos != null && pos.getEscalationType() != null) {
|
|
|
2384 |
partnerToAuthByLevel
|
|
|
2385 |
.computeIfAbsent(pp.getFofoId(), k -> new HashMap<>())
|
|
|
2386 |
.computeIfAbsent(pos.getEscalationType(), k -> new HashSet<>())
|
|
|
2387 |
.add(pos.getAuthUserId());
|
|
|
2388 |
}
|
|
|
2389 |
}
|
|
|
2390 |
|
|
|
2391 |
// Build L2 -> L1 team map based on shared partners
|
|
|
2392 |
// If an L1 and L2 share partners (L1 at L1 level, L2 at L2 level), that L1 belongs under that L2
|
|
|
2393 |
Map<Integer, List<RbmCallTargetModel>> l2TeamMap = new LinkedHashMap<>();
|
|
|
2394 |
for (RbmCallTargetModel l2Model : l2ModelsByAuthId.values()) {
|
|
|
2395 |
l2TeamMap.put(l2Model.getAuthId(), new ArrayList<>());
|
|
|
2396 |
}
|
|
|
2397 |
|
|
|
2398 |
// Build L3 -> L2 team map based on shared partners
|
| 36210 |
ranu |
2399 |
Map<Integer, List<RbmCallTargetModel>> l3TeamMap = new LinkedHashMap<>();
|
|
|
2400 |
for (RbmCallTargetModel l3Model : l3ModelsByAuthId.values()) {
|
|
|
2401 |
l3TeamMap.put(l3Model.getAuthId(), new ArrayList<>());
|
|
|
2402 |
}
|
|
|
2403 |
|
| 36225 |
ranu |
2404 |
// For each L1, find which L2 shares the most partners -> that's their L2
|
|
|
2405 |
Set<Integer> addedL1AuthIds = new HashSet<>();
|
|
|
2406 |
for (RbmCallTargetModel l1Model : l1ModelsByAuthId.values()) {
|
|
|
2407 |
Map<Integer, Integer> l2SharedCount = new HashMap<>(); // l2AuthId -> shared partner count
|
|
|
2408 |
for (Map.Entry<Integer, Map<EscalationType, Set<Integer>>> entry : partnerToAuthByLevel.entrySet()) {
|
|
|
2409 |
Map<EscalationType, Set<Integer>> levelMap = entry.getValue();
|
|
|
2410 |
Set<Integer> l1IdsForPartner = levelMap.getOrDefault(EscalationType.L1, Collections.emptySet());
|
|
|
2411 |
Set<Integer> l2AuthIdsForPartner = levelMap.getOrDefault(EscalationType.L2, Collections.emptySet());
|
|
|
2412 |
if (l1IdsForPartner.contains(l1Model.getAuthId())) {
|
|
|
2413 |
for (int l2Id : l2AuthIdsForPartner) {
|
|
|
2414 |
if (l2ModelsByAuthId.containsKey(l2Id) && l2Id != l1Model.getAuthId()) {
|
|
|
2415 |
l2SharedCount.merge(l2Id, 1, Integer::sum);
|
|
|
2416 |
}
|
|
|
2417 |
}
|
|
|
2418 |
}
|
|
|
2419 |
}
|
|
|
2420 |
// Assign L1 to the L2 with most shared partners
|
|
|
2421 |
if (!l2SharedCount.isEmpty()) {
|
|
|
2422 |
int bestL2 = l2SharedCount.entrySet().stream()
|
|
|
2423 |
.max(Map.Entry.comparingByValue()).get().getKey();
|
|
|
2424 |
l2TeamMap.get(bestL2).add(l1Model);
|
|
|
2425 |
addedL1AuthIds.add(l1Model.getAuthId());
|
|
|
2426 |
}
|
|
|
2427 |
}
|
|
|
2428 |
|
|
|
2429 |
// For each L2, find which L3 shares the most partners -> that's their L3
|
| 36210 |
ranu |
2430 |
Set<Integer> addedL2AuthIds = new HashSet<>();
|
|
|
2431 |
for (RbmCallTargetModel l2Model : l2ModelsByAuthId.values()) {
|
| 36225 |
ranu |
2432 |
Map<Integer, Integer> l3SharedCount = new HashMap<>();
|
|
|
2433 |
for (Map.Entry<Integer, Map<EscalationType, Set<Integer>>> entry : partnerToAuthByLevel.entrySet()) {
|
|
|
2434 |
Map<EscalationType, Set<Integer>> levelMap = entry.getValue();
|
|
|
2435 |
Set<Integer> l2AuthIdsForPartner = levelMap.getOrDefault(EscalationType.L2, Collections.emptySet());
|
|
|
2436 |
Set<Integer> l3AuthIdsForPartner = levelMap.getOrDefault(EscalationType.L3, Collections.emptySet());
|
|
|
2437 |
if (l2AuthIdsForPartner.contains(l2Model.getAuthId())) {
|
|
|
2438 |
for (int l3Id : l3AuthIdsForPartner) {
|
|
|
2439 |
if (l3ModelsByAuthId.containsKey(l3Id)) {
|
|
|
2440 |
l3SharedCount.merge(l3Id, 1, Integer::sum);
|
|
|
2441 |
}
|
|
|
2442 |
}
|
|
|
2443 |
}
|
|
|
2444 |
}
|
|
|
2445 |
if (!l3SharedCount.isEmpty()) {
|
|
|
2446 |
int bestL3 = l3SharedCount.entrySet().stream()
|
|
|
2447 |
.max(Map.Entry.comparingByValue()).get().getKey();
|
|
|
2448 |
l3TeamMap.get(bestL3).add(l2Model);
|
| 36210 |
ranu |
2449 |
addedL2AuthIds.add(l2Model.getAuthId());
|
|
|
2450 |
}
|
|
|
2451 |
}
|
|
|
2452 |
|
| 36225 |
ranu |
2453 |
// For L1s not mapped to any L2, check if they map directly to an L3 via shared partners
|
|
|
2454 |
Map<Integer, List<RbmCallTargetModel>> l3DirectL1Map = new LinkedHashMap<>();
|
|
|
2455 |
for (RbmCallTargetModel l3Model : l3ModelsByAuthId.values()) {
|
|
|
2456 |
l3DirectL1Map.put(l3Model.getAuthId(), new ArrayList<>());
|
| 35631 |
ranu |
2457 |
}
|
|
|
2458 |
for (RbmCallTargetModel l1Model : l1ModelsByAuthId.values()) {
|
| 36225 |
ranu |
2459 |
if (addedL1AuthIds.contains(l1Model.getAuthId())) continue;
|
|
|
2460 |
Map<Integer, Integer> l3SharedCount = new HashMap<>();
|
|
|
2461 |
for (Map.Entry<Integer, Map<EscalationType, Set<Integer>>> entry : partnerToAuthByLevel.entrySet()) {
|
|
|
2462 |
Map<EscalationType, Set<Integer>> levelMap = entry.getValue();
|
|
|
2463 |
Set<Integer> l1IdsForPartner = levelMap.getOrDefault(EscalationType.L1, Collections.emptySet());
|
|
|
2464 |
Set<Integer> l3AuthIdsForPartner = levelMap.getOrDefault(EscalationType.L3, Collections.emptySet());
|
|
|
2465 |
if (l1IdsForPartner.contains(l1Model.getAuthId())) {
|
|
|
2466 |
for (int l3Id : l3AuthIdsForPartner) {
|
|
|
2467 |
if (l3ModelsByAuthId.containsKey(l3Id)) {
|
|
|
2468 |
l3SharedCount.merge(l3Id, 1, Integer::sum);
|
|
|
2469 |
}
|
|
|
2470 |
}
|
|
|
2471 |
}
|
|
|
2472 |
}
|
|
|
2473 |
if (!l3SharedCount.isEmpty()) {
|
|
|
2474 |
int bestL3 = l3SharedCount.entrySet().stream()
|
|
|
2475 |
.max(Map.Entry.comparingByValue()).get().getKey();
|
|
|
2476 |
l3DirectL1Map.get(bestL3).add(l1Model);
|
| 35631 |
ranu |
2477 |
addedL1AuthIds.add(l1Model.getAuthId());
|
|
|
2478 |
}
|
|
|
2479 |
}
|
|
|
2480 |
|
| 36225 |
ranu |
2481 |
// Build sorted result: L3 -> L2 -> L1 (with direct L1s under L3 if no L2)
|
| 35631 |
ranu |
2482 |
List<RbmCallTargetModel> sortedModels = new ArrayList<>();
|
|
|
2483 |
|
| 36210 |
ranu |
2484 |
List<RbmCallTargetModel> l3Sorted = new ArrayList<>(l3ModelsByAuthId.values());
|
|
|
2485 |
l3Sorted.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
|
| 35631 |
ranu |
2486 |
|
| 36210 |
ranu |
2487 |
for (RbmCallTargetModel l3Model : l3Sorted) {
|
|
|
2488 |
sortedModels.add(l3Model);
|
|
|
2489 |
List<RbmCallTargetModel> l2Team = l3TeamMap.getOrDefault(l3Model.getAuthId(), Collections.emptyList());
|
|
|
2490 |
l2Team.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
|
|
|
2491 |
for (RbmCallTargetModel l2Model : l2Team) {
|
|
|
2492 |
sortedModels.add(l2Model);
|
|
|
2493 |
List<RbmCallTargetModel> l1Team = l2TeamMap.getOrDefault(l2Model.getAuthId(), Collections.emptyList());
|
|
|
2494 |
l1Team.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
|
|
|
2495 |
sortedModels.addAll(l1Team);
|
|
|
2496 |
}
|
| 36225 |
ranu |
2497 |
// Add L1s that report directly to this L3 (no L2 in between)
|
|
|
2498 |
List<RbmCallTargetModel> directL1Team = l3DirectL1Map.getOrDefault(l3Model.getAuthId(), Collections.emptyList());
|
|
|
2499 |
directL1Team.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
|
|
|
2500 |
sortedModels.addAll(directL1Team);
|
| 36210 |
ranu |
2501 |
}
|
|
|
2502 |
|
| 36225 |
ranu |
2503 |
// Add L2s not mapped to any L3
|
| 36210 |
ranu |
2504 |
List<RbmCallTargetModel> unmappedL2 = new ArrayList<>();
|
|
|
2505 |
for (RbmCallTargetModel l2Model : l2ModelsByAuthId.values()) {
|
|
|
2506 |
if (!addedL2AuthIds.contains(l2Model.getAuthId())) {
|
|
|
2507 |
unmappedL2.add(l2Model);
|
|
|
2508 |
}
|
|
|
2509 |
}
|
|
|
2510 |
unmappedL2.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
|
|
|
2511 |
for (RbmCallTargetModel l2Model : unmappedL2) {
|
| 35631 |
ranu |
2512 |
sortedModels.add(l2Model);
|
|
|
2513 |
List<RbmCallTargetModel> team = l2TeamMap.getOrDefault(l2Model.getAuthId(), Collections.emptyList());
|
|
|
2514 |
team.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
|
|
|
2515 |
sortedModels.addAll(team);
|
|
|
2516 |
}
|
|
|
2517 |
|
| 36225 |
ranu |
2518 |
// Add any L1s not mapped to any L2 or L3
|
| 35631 |
ranu |
2519 |
List<RbmCallTargetModel> unmappedL1 = new ArrayList<>();
|
|
|
2520 |
for (RbmCallTargetModel m : l1ModelsByAuthId.values()) {
|
|
|
2521 |
if (!addedL1AuthIds.contains(m.getAuthId())) {
|
|
|
2522 |
unmappedL1.add(m);
|
|
|
2523 |
}
|
|
|
2524 |
}
|
|
|
2525 |
unmappedL1.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
|
|
|
2526 |
sortedModels.addAll(unmappedL1);
|
|
|
2527 |
|
|
|
2528 |
LOGGER.info("RBM Call Target - TOTAL TIME: {}ms, RBM count: {}", System.currentTimeMillis() - methodStart, sortedModels.size());
|
|
|
2529 |
return sortedModels;
|
|
|
2530 |
}
|
|
|
2531 |
|
| 35843 |
ranu |
2532 |
/**
|
| 37011 |
ranu |
2533 |
* Rule: count each distinct customer number this RBM attempted today, based on
|
| 37012 |
ranu |
2534 |
* the vendor-reported customer-side outcome (AgentCallLog.customerStatus):
|
|
|
2535 |
* - If ANY call to this number has customerStatus that is not "Missed"
|
|
|
2536 |
* → count as 1 (customer was reached at some point).
|
|
|
2537 |
* - Else (every call to this number was Missed) → count only if attempts >= 3
|
| 37011 |
ranu |
2538 |
* (persistence credit).
|
| 37012 |
ranu |
2539 |
* null/empty customerStatus is treated as "not Missed" (favours counting when
|
|
|
2540 |
* the vendor didn't classify the outcome).
|
| 35843 |
ranu |
2541 |
*/
|
| 37022 |
ranu |
2542 |
private long computeUniqueTriedCount(List<AgentCallLog> callLogs,
|
|
|
2543 |
Map<String, Integer> mobileToFofoIdMap) {
|
| 37010 |
ranu |
2544 |
if (callLogs == null || callLogs.isEmpty()) return 0L;
|
| 37022 |
ranu |
2545 |
// Group by fofoId (not raw number) — numbers that don't resolve to a partner via
|
|
|
2546 |
// retailer_contact/address are skipped. Once a number gets added to retailer_contact
|
|
|
2547 |
// it'll automatically resolve and start counting on the next page load.
|
|
|
2548 |
Map<Integer, List<AgentCallLog>> byFofoId = new HashMap<>();
|
| 37010 |
ranu |
2549 |
for (AgentCallLog log : callLogs) {
|
|
|
2550 |
String num = log.getCustomerNumber();
|
|
|
2551 |
if (num == null) continue;
|
|
|
2552 |
String normalized = num.startsWith("+91") ? num.substring(3) : num;
|
| 37022 |
ranu |
2553 |
Integer fofoId = mobileToFofoIdMap != null ? mobileToFofoIdMap.get(normalized) : null;
|
|
|
2554 |
if (fofoId == null) continue; // unknown number — excluded from Tried
|
|
|
2555 |
byFofoId.computeIfAbsent(fofoId, k -> new ArrayList<>()).add(log);
|
| 36284 |
ranu |
2556 |
}
|
| 37010 |
ranu |
2557 |
long count = 0L;
|
| 37022 |
ranu |
2558 |
for (List<AgentCallLog> logs : byFofoId.values()) {
|
| 37012 |
ranu |
2559 |
boolean hasNonMissed = false;
|
|
|
2560 |
for (AgentCallLog l : logs) {
|
|
|
2561 |
if (!isMissedCustomerStatus(l.getCustomerStatus())) {
|
|
|
2562 |
hasNonMissed = true;
|
| 37010 |
ranu |
2563 |
break;
|
| 36284 |
ranu |
2564 |
}
|
|
|
2565 |
}
|
| 37012 |
ranu |
2566 |
if (hasNonMissed) count++;
|
|
|
2567 |
else if (logs.size() >= 3) count++;
|
| 36284 |
ranu |
2568 |
}
|
| 37010 |
ranu |
2569 |
return count;
|
| 36284 |
ranu |
2570 |
}
|
|
|
2571 |
|
|
|
2572 |
/**
|
| 36276 |
ranu |
2573 |
* Returns call stats: [0] = called count, [1] = total recording calls, [2] = unique recording calls
|
|
|
2574 |
*/
|
|
|
2575 |
public long[] getCallStats(long authId, LocalDate date) {
|
| 36234 |
ranu |
2576 |
List<AgentCallLog> callLogs = agentCallLogRepository.findByAuthIdAndDate(authId, date);
|
|
|
2577 |
|
| 35843 |
ranu |
2578 |
if (callLogs == null || callLogs.isEmpty()) {
|
| 36276 |
ranu |
2579 |
return new long[]{0, 0, 0};
|
| 35843 |
ranu |
2580 |
}
|
|
|
2581 |
|
|
|
2582 |
Set<Integer> calledFofoIds = new HashSet<>();
|
|
|
2583 |
Set<String> calledNumbersWithoutFofoId = new HashSet<>();
|
| 36276 |
ranu |
2584 |
long totalRecordingCalls = 0;
|
|
|
2585 |
Set<String> uniqueRecordingNumbers = new HashSet<>();
|
| 35843 |
ranu |
2586 |
|
|
|
2587 |
for (AgentCallLog callLog : callLogs) {
|
|
|
2588 |
String customerNumber = callLog.getCustomerNumber();
|
|
|
2589 |
if (customerNumber != null) {
|
|
|
2590 |
// Normalize the phone number (remove +91 prefix if present)
|
|
|
2591 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
|
|
2592 |
|
|
|
2593 |
// Find fofoId from retailer_contact or address
|
|
|
2594 |
Integer fofoId = findFofoIdByMobile(normalized);
|
|
|
2595 |
if (fofoId != null) {
|
|
|
2596 |
calledFofoIds.add(fofoId);
|
|
|
2597 |
} else {
|
|
|
2598 |
// Number not found in retailer_contact or address, count by distinct number
|
|
|
2599 |
calledNumbersWithoutFofoId.add(normalized);
|
|
|
2600 |
}
|
| 36276 |
ranu |
2601 |
|
|
|
2602 |
// Count calls with recordings
|
|
|
2603 |
if (callLog.getRecordingUrl() != null && !callLog.getRecordingUrl().isEmpty()
|
|
|
2604 |
&& !"None".equalsIgnoreCase(callLog.getRecordingUrl())) {
|
|
|
2605 |
totalRecordingCalls++;
|
|
|
2606 |
uniqueRecordingNumbers.add(normalized);
|
|
|
2607 |
}
|
| 35843 |
ranu |
2608 |
}
|
|
|
2609 |
}
|
|
|
2610 |
|
|
|
2611 |
// Total called = distinct fofoIds + distinct numbers without fofoId mapping
|
| 36276 |
ranu |
2612 |
long calledCount = calledFofoIds.size() + calledNumbersWithoutFofoId.size();
|
|
|
2613 |
return new long[]{calledCount, totalRecordingCalls, uniqueRecordingNumbers.size()};
|
| 35843 |
ranu |
2614 |
}
|
|
|
2615 |
|
| 35852 |
ranu |
2616 |
@Override
|
|
|
2617 |
public List<List<String>> getAllCallDataByDate(LocalDate date) throws Exception {
|
|
|
2618 |
List<List<String>> rows = new ArrayList<>();
|
|
|
2619 |
|
|
|
2620 |
// Add header row
|
|
|
2621 |
rows.add(Arrays.asList("RBM Name", "Partner Name", "Code", "Remark", "Call Status", "Call Duration", "Call Date Time", "Recording URL"));
|
|
|
2622 |
|
|
|
2623 |
// Get all call logs for the date
|
|
|
2624 |
List<AgentCallLog> allCallLogs = agentCallLogRepository.findAllByDate(date);
|
|
|
2625 |
LOGGER.info("getAllCallDataByDate: Found {} call logs for date {}", allCallLogs.size(), date);
|
|
|
2626 |
|
|
|
2627 |
if (allCallLogs.isEmpty()) {
|
|
|
2628 |
return rows;
|
|
|
2629 |
}
|
|
|
2630 |
|
|
|
2631 |
// Get unique authIds from call logs
|
|
|
2632 |
Set<Long> authIds = allCallLogs.stream()
|
|
|
2633 |
.map(AgentCallLog::getAuthId)
|
|
|
2634 |
.collect(Collectors.toSet());
|
|
|
2635 |
|
|
|
2636 |
// Get auth users for RBM names
|
|
|
2637 |
List<Integer> authIdInts = authIds.stream().map(Long::intValue).collect(Collectors.toList());
|
|
|
2638 |
Map<Integer, AuthUser> authUserMap = authRepository.selectByIds(authIdInts).stream()
|
|
|
2639 |
.collect(Collectors.toMap(AuthUser::getId, au -> au, (a, b) -> a));
|
|
|
2640 |
|
|
|
2641 |
// Build a map of normalized customer number -> fofoId
|
|
|
2642 |
Map<String, Integer> customerToFofoIdMap = new HashMap<>();
|
|
|
2643 |
Set<String> normalizedNumbers = new HashSet<>();
|
|
|
2644 |
|
|
|
2645 |
for (AgentCallLog callLog : allCallLogs) {
|
|
|
2646 |
String customerNumber = callLog.getCustomerNumber();
|
|
|
2647 |
if (customerNumber != null) {
|
|
|
2648 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
|
|
2649 |
normalizedNumbers.add(normalized);
|
|
|
2650 |
}
|
|
|
2651 |
}
|
|
|
2652 |
|
|
|
2653 |
// For each normalized number, find fofoId
|
|
|
2654 |
for (String mobile : normalizedNumbers) {
|
|
|
2655 |
Integer fofoId = findFofoIdByMobile(mobile);
|
|
|
2656 |
if (fofoId != null) {
|
|
|
2657 |
customerToFofoIdMap.put(mobile, fofoId);
|
|
|
2658 |
}
|
|
|
2659 |
}
|
|
|
2660 |
|
|
|
2661 |
// Get unique fofoIds for retailer lookup
|
|
|
2662 |
Set<Integer> fofoIds = new HashSet<>(customerToFofoIdMap.values());
|
|
|
2663 |
Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
|
|
|
2664 |
if (!fofoIds.isEmpty()) {
|
|
|
2665 |
try {
|
|
|
2666 |
retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
|
|
|
2667 |
} catch (ProfitMandiBusinessException e) {
|
|
|
2668 |
LOGGER.error("Error fetching fofo stores", e);
|
|
|
2669 |
}
|
|
|
2670 |
}
|
|
|
2671 |
|
|
|
2672 |
// Get remarks for these fofoIds on this date
|
|
|
2673 |
Map<Integer, List<PartnerCollectionRemark>> fofoRemarkMap = new HashMap<>();
|
|
|
2674 |
if (!fofoIds.isEmpty()) {
|
|
|
2675 |
List<PartnerCollectionRemark> dateRemarks = partnerCollectionRemarkRepository
|
|
|
2676 |
.selectAllByFofoIdsOnDate(new ArrayList<>(fofoIds), date);
|
|
|
2677 |
for (PartnerCollectionRemark remark : dateRemarks) {
|
|
|
2678 |
fofoRemarkMap.computeIfAbsent(remark.getFofoId(), k -> new ArrayList<>()).add(remark);
|
|
|
2679 |
}
|
|
|
2680 |
}
|
|
|
2681 |
|
|
|
2682 |
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm a");
|
|
|
2683 |
|
|
|
2684 |
// Build rows
|
|
|
2685 |
for (AgentCallLog callLog : allCallLogs) {
|
|
|
2686 |
String customerNumber = callLog.getCustomerNumber();
|
|
|
2687 |
if (customerNumber == null) {
|
|
|
2688 |
continue;
|
|
|
2689 |
}
|
|
|
2690 |
|
|
|
2691 |
// Get RBM Name
|
|
|
2692 |
AuthUser authUser = authUserMap.get((int) callLog.getAuthId());
|
|
|
2693 |
String rbmName = authUser != null ? authUser.getFullName() : "Unknown";
|
|
|
2694 |
|
|
|
2695 |
String normalized = customerNumber.startsWith("+91") ? customerNumber.substring(3) : customerNumber;
|
|
|
2696 |
Integer fofoId = customerToFofoIdMap.get(normalized);
|
|
|
2697 |
|
|
|
2698 |
String partyName = "Unknown";
|
|
|
2699 |
String code = "-";
|
|
|
2700 |
|
|
|
2701 |
if (fofoId != null) {
|
|
|
2702 |
CustomRetailer retailer = retailerMap.get(fofoId);
|
|
|
2703 |
if (retailer != null) {
|
|
|
2704 |
partyName = retailer.getBusinessName();
|
|
|
2705 |
code = retailer.getCode();
|
|
|
2706 |
} else {
|
|
|
2707 |
partyName = "Unknown (" + fofoId + ")";
|
|
|
2708 |
}
|
|
|
2709 |
} else {
|
|
|
2710 |
partyName = "Unknown (" + normalized + ")";
|
|
|
2711 |
}
|
|
|
2712 |
|
|
|
2713 |
// Get remark if available
|
|
|
2714 |
String remarkValue = "-";
|
|
|
2715 |
if (fofoId != null && fofoRemarkMap.containsKey(fofoId)) {
|
|
|
2716 |
List<PartnerCollectionRemark> remarks = fofoRemarkMap.get(fofoId);
|
|
|
2717 |
if (!remarks.isEmpty()) {
|
|
|
2718 |
PartnerCollectionRemark remark = remarks.get(0);
|
|
|
2719 |
remarkValue = remark.getRemark() != null ? remark.getRemark().getValue() : "-";
|
|
|
2720 |
if (remark.getMessage() != null && !remark.getMessage().isEmpty()) {
|
|
|
2721 |
remarkValue = remarkValue + " - " + remark.getMessage();
|
|
|
2722 |
}
|
|
|
2723 |
}
|
|
|
2724 |
}
|
|
|
2725 |
|
|
|
2726 |
// Call status from customerStatus field
|
|
|
2727 |
String callStatus = callLog.getCustomerStatus() != null ? callLog.getCustomerStatus() : "-";
|
|
|
2728 |
String callDuration = callLog.getCallDuration() != null ? callLog.getCallDuration() : "-";
|
|
|
2729 |
|
|
|
2730 |
String callDateTime = "-";
|
|
|
2731 |
if (callLog.getCallDate() != null && callLog.getCallTime() != null) {
|
|
|
2732 |
LocalDateTime callDateTimeObj = LocalDateTime.of(callLog.getCallDate(), callLog.getCallTime());
|
|
|
2733 |
callDateTime = callDateTimeObj.format(dateTimeFormatter);
|
|
|
2734 |
}
|
|
|
2735 |
|
|
|
2736 |
String recordingUrl = callLog.getRecordingUrl() != null ? callLog.getRecordingUrl() : "-";
|
|
|
2737 |
|
|
|
2738 |
rows.add(Arrays.asList(rbmName, partyName, code, remarkValue, callStatus, callDuration, callDateTime, recordingUrl));
|
|
|
2739 |
}
|
|
|
2740 |
|
|
|
2741 |
return rows;
|
|
|
2742 |
}
|
|
|
2743 |
|
| 33917 |
ranu |
2744 |
}
|