Subversion Repositories SmartDukaan

Rev

Rev 35714 | Rev 35721 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
33917 ranu 1
package com.spice.profitmandi.service;
2
 
35631 ranu 3
import com.spice.profitmandi.common.enumuration.ActivationType;
4
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
5
import com.spice.profitmandi.common.model.CustomRetailer;
33917 ranu 6
import com.spice.profitmandi.common.model.ProfitMandiConstants;
35631 ranu 7
import com.spice.profitmandi.dao.entity.auth.AuthUser;
8
import com.spice.profitmandi.dao.entity.auth.PartnerCollectionRemark;
9
import com.spice.profitmandi.dao.entity.auth.RbmCallSequenceLog;
10
import com.spice.profitmandi.dao.entity.cs.Position;
11
import com.spice.profitmandi.dao.entity.cs.Ticket;
12
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
34397 ranu 13
import com.spice.profitmandi.dao.entity.fofo.MonthlyTarget;
33985 ranu 14
import com.spice.profitmandi.dao.entity.inventory.RbmAchievements;
33926 ranu 15
import com.spice.profitmandi.dao.entity.inventory.RbmTargets;
35631 ranu 16
import com.spice.profitmandi.dao.enumuration.auth.CollectionRemark;
17
import com.spice.profitmandi.dao.enumuration.cs.EscalationType;
33917 ranu 18
import com.spice.profitmandi.dao.model.*;
35631 ranu 19
import com.spice.profitmandi.dao.repository.auth.AuthRepository;
20
import com.spice.profitmandi.dao.repository.auth.PartnerCollectionRemarkRepository;
21
import com.spice.profitmandi.dao.repository.auth.RbmCallSequenceLogRepository;
33985 ranu 22
import com.spice.profitmandi.dao.repository.catalog.RbmAchievementsRepository;
33926 ranu 23
import com.spice.profitmandi.dao.repository.catalog.RbmTargetsRepository;
35631 ranu 24
import com.spice.profitmandi.dao.repository.cs.CsService;
25
import com.spice.profitmandi.dao.repository.cs.PositionRepository;
26
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
34397 ranu 27
import com.spice.profitmandi.dao.repository.fofo.MonthlyTargetRepository;
34880 ranu 28
import com.spice.profitmandi.dao.repository.logistics.PublicHolidaysRepository;
35631 ranu 29
import com.spice.profitmandi.dao.repository.transaction.LoanRepository;
34397 ranu 30
import com.spice.profitmandi.dao.repository.transaction.OrderRepository;
35631 ranu 31
import com.spice.profitmandi.service.user.RetailerService;
33917 ranu 32
import org.apache.logging.log4j.LogManager;
33
import org.apache.logging.log4j.Logger;
34
import org.hibernate.Session;
35
import org.hibernate.SessionFactory;
36
import org.hibernate.query.NativeQuery;
37
import org.springframework.beans.factory.annotation.Autowired;
38
import org.springframework.stereotype.Component;
39
 
40
import javax.persistence.TypedQuery;
34880 ranu 41
import java.time.*;
35631 ranu 42
import java.time.format.DateTimeFormatter;
34880 ranu 43
import java.time.temporal.ChronoUnit;
35631 ranu 44
import java.util.*;
33997 ranu 45
import java.util.stream.Collectors;
33917 ranu 46
 
47
@Component
48
public class RbmTargetServiceImpl implements RbmTargetService {
49
    private static final Logger LOGGER = LogManager.getLogger(RbmTargetServiceImpl.class);
50
 
51
    @Autowired
52
    SessionFactory sessionFactory;
53
 
33926 ranu 54
    @Autowired
55
    RbmTargetsRepository rbmTargetsRepository;
33917 ranu 56
 
33985 ranu 57
    @Autowired
58
    RbmAchievementsRepository rbmAchievementsRepository;
59
 
34397 ranu 60
    @Autowired
61
    MonthlyTargetRepository monthlyTargetRepository;
62
 
34880 ranu 63
    @Autowired
64
    PublicHolidaysRepository publicHolidaysRepository;
65
 
35631 ranu 66
    @Autowired
67
    RetailerService retailerService;
68
 
33917 ranu 69
    @Override
70
    public List<WarehouseRbmTargetModel> getWarehouseWiseRbmMonthlyTarget() {
71
        Session session = sessionFactory.getCurrentSession();
72
        final TypedQuery<WarehouseRbmTargetModel> typedQuerySimilar = session.createNamedQuery("RbmTarget.getWarehouseWiseMonthlyTarget", WarehouseRbmTargetModel.class);
73
 
74
        return typedQuerySimilar.getResultList();
75
 
76
    }
77
 
78
    @Override
79
    public List<MTDAchievedTargetModel> getDateWiseAchievedTargetOfRbm(LocalDate startDate, LocalDate endDate) {
80
        Session session = sessionFactory.getCurrentSession();
81
        final TypedQuery<MTDAchievedTargetModel> typedQuerySimilar = session.createNamedQuery("RbmTarget.getRbmAchievedMonthlyTarget", MTDAchievedTargetModel.class);
82
        typedQuerySimilar.setParameter("startDate", startDate);
83
        typedQuerySimilar.setParameter("endDate", endDate);
84
        return typedQuerySimilar.getResultList();
85
 
86
    }
87
 
88
    @Override
34055 ranu 89
    public List<TodayAchievedMovementModel> getMovementWiseAchievementByDate(LocalDate startDate, LocalDate endDate) {
33917 ranu 90
        LOGGER.info("start date {}, end date {}", startDate, endDate);
91
        Session session = sessionFactory.getCurrentSession();
34055 ranu 92
        final TypedQuery<TodayAchievedMovementModel> typedQuerySimilar = session.createNamedQuery("RBMTarget.TodayAchivementByMovement", TodayAchievedMovementModel.class);
33917 ranu 93
        typedQuerySimilar.setParameter("startDate", startDate);
94
        typedQuerySimilar.setParameter("endDate", endDate);
95
        return typedQuerySimilar.getResultList();
96
 
97
    }
98
 
99
    @Override
100
    public List<WarehouseMobileStockByMovementModel> getWarehouseMobileStockByMovement() {
101
        Session session = sessionFactory.getCurrentSession();
102
        final TypedQuery<WarehouseMobileStockByMovementModel> typedQuerySimilar = session.createNamedQuery("WarehouseStock.MovementWiseMobileStock", WarehouseMobileStockByMovementModel.class);
103
 
104
        return typedQuerySimilar.getResultList();
105
 
106
    }
107
 
33991 ranu 108
    @Override
109
    public List<SoldCatalogsReportModel> getCatalogSoldReport(LocalDate startDate, LocalDate endDate) {
110
        Session session = sessionFactory.getCurrentSession();
111
        final TypedQuery<SoldCatalogsReportModel> typedQuerySimilar = session.createNamedQuery("CatalogsReport.SoldCatalogsReport", SoldCatalogsReportModel.class);
112
        typedQuerySimilar.setParameter("startDate", startDate);
113
        typedQuerySimilar.setParameter("endDate", endDate);
114
        return typedQuerySimilar.getResultList();
33917 ranu 115
 
33991 ranu 116
    }
117
 
118
 
33917 ranu 119
    public int getWorkingDaysCount(LocalDate startDate) {
120
        Session session = sessionFactory.getCurrentSession();
121
 
122
        // Convert the LocalDate to a format MySQL can interpret
123
        String startDateString = startDate.toString();
124
 
125
        final NativeQuery<?> nativeQuery = session.createNativeQuery(
126
                "SELECT (DATEDIFF(LAST_DAY(:startDate), :startDate) + 1) " +
127
                        " - (FLOOR((DATEDIFF(LAST_DAY(:startDate), :startDate) + (WEEKDAY(:startDate) + 1)) / 7)) " +
128
                        " - (SELECT COUNT(*) " +
129
                        " FROM logistics.publicholidays " +
130
                        " WHERE date BETWEEN :startDate AND LAST_DAY(:startDate) " +
131
                        " AND WEEKDAY(date) != 6) AS working_days"
132
        );
133
 
134
        // Set the start date parameter for each placeholder
135
        nativeQuery.setParameter("startDate", startDateString);
136
 
137
        Object result = nativeQuery.getSingleResult();
138
        return result != null ? ((Number) result).intValue() : 0;
139
    }
140
 
141
 
35044 ranu 142
 
33917 ranu 143
    @Override
144
    public List<RbmArrViewModel> getRbmTodayArr() throws Exception {
145
        LocalDate todayDate = LocalDate.now();
146
        return getRbmTodayArr(todayDate);
147
    }
148
 
149
    @Override
33997 ranu 150
    public List<RbmTargetAndAchievementsModel> getRbmTargetsAndAchievemnts(LocalDate startDate, LocalDate endDate) {
151
 
34002 ranu 152
        List<RbmTargetsModel> rbmTargetsList = rbmTargetsRepository.selectTargetsModelListByDates(startDate.atStartOfDay(), endDate.atTime(LocalTime.MAX));
33997 ranu 153
 
154
        LOGGER.info("rbmTargetsList {}", rbmTargetsList);
155
        // Group Targtes by RBM and Warehouse
34002 ranu 156
        Map<String, RbmTargetsModel> targetsMap = rbmTargetsList.stream()
33997 ranu 157
                .collect(Collectors.toMap(
158
                        a -> a.getRbmAuthId() + "-" + a.getWarehouseId(),
159
                        a -> a,
160
                        (a1, a2) -> mergeTargets(a1, a2) // Handle duplicates by merging
161
                ));
162
 
163
 
34002 ranu 164
        List<RbmAchievementsModel> rbmAchievements = rbmAchievementsRepository.selectAchievementsModelListByDates(startDate.atStartOfDay(), endDate.atTime(LocalTime.MAX));
33997 ranu 165
        LOGGER.info("rbmTargetsList {}", rbmAchievements);
166
        // Group achievements by RBM and Warehouse
34002 ranu 167
        Map<String, RbmAchievementsModel> achievementMap = rbmAchievements.stream()
33997 ranu 168
                .collect(Collectors.toMap(
169
                        a -> a.getRbmAuthId() + "-" + a.getWarehouseId(),
170
                        a -> a,
171
                        (a1, a2) -> mergeAchievements(a1, a2) // Handle duplicates by merging
172
                ));
173
 
174
        return targetsMap.keySet().stream()
175
                .map(key -> {
176
                    String[] parts = key.split("-");
177
                    int rbmAuthId = Integer.parseInt(parts[0]);
178
                    int warehouseId = Integer.parseInt(parts[1]);
179
 
34002 ranu 180
                    RbmTargetsModel target = targetsMap.get(key);
181
                    RbmAchievementsModel achievement = achievementMap.getOrDefault(key, new RbmAchievementsModel());
33997 ranu 182
 
183
                    RbmTargetAndAchievementsModel model = new RbmTargetAndAchievementsModel();
184
                    model.setAuthId(rbmAuthId);
185
                    model.setRbmName(target.getRbmName());
186
                    model.setWarehouseName(ProfitMandiConstants.WAREHOUSE_MAP.getOrDefault(warehouseId, "Unknown"));
187
 
188
                    // Set target values
34006 ranu 189
                    model.setHidTarget((long) target.getHidTarget());
34098 ranu 190
                    model.setRunningTarget((long) target.getRunningTarget());
34006 ranu 191
                    model.setFastMovingTarget((long) target.getFastMovingTarget());
192
                    model.setSlowMovingTarget((long) target.getSlowMovingTarget());
193
                    model.setOtherMovingTarget((long) target.getOtherTarget());
33997 ranu 194
 
195
                    // Set achievement values
34006 ranu 196
                    model.setAchievedHid((long) achievement.getAchievedHidTarget());
34098 ranu 197
                    model.setAchievedRunning((long) achievement.getAchievedRunningTarget());
34006 ranu 198
                    model.setAchievedFastMoving((long) achievement.getAchievedFastMovingTarget());
199
                    model.setAchievedSlowMoving((long) achievement.getAchievedSlowMovingTarget());
200
                    model.setAchievedOtherMoving((long) achievement.getAchievedOtherTarget());
33997 ranu 201
 
202
                    model.setTotalTarget(
34006 ranu 203
                            (long) target.getHidTarget() +
34098 ranu 204
                                    (long) target.getRunningTarget() +
34006 ranu 205
                                    (long) target.getFastMovingTarget() +
206
                                    (long) target.getSlowMovingTarget() +
207
                                    (long) target.getOtherTarget()
33997 ranu 208
                    );
209
                    model.setTotalAchievemnt(
34006 ranu 210
                            (long) achievement.getAchievedHidTarget() +
34098 ranu 211
                                    (long) achievement.getAchievedRunningTarget() +
34006 ranu 212
                                    (long) achievement.getAchievedFastMovingTarget() +
213
                                    (long) achievement.getAchievedSlowMovingTarget() +
214
                                    (long) achievement.getAchievedOtherTarget()
33997 ranu 215
                    );
216
 
217
                    return model;
218
                })
219
                .collect(Collectors.toList());
220
 
221
    }
222
 
34002 ranu 223
    private RbmTargetsModel mergeTargets(RbmTargetsModel a1, RbmTargetsModel a2) {
33997 ranu 224
 
225
        // Merge logic for achievements (aggregate the target and achieved values)
34006 ranu 226
        a1.setHidTarget((a1.getHidTarget()) +
227
                (a2.getHidTarget()));
33997 ranu 228
 
34006 ranu 229
        a1.setFastMovingTarget((a1.getFastMovingTarget()) +
230
                (a2.getFastMovingTarget()));
33997 ranu 231
 
34006 ranu 232
        a1.setSlowMovingTarget((a1.getSlowMovingTarget()) +
233
                (a2.getSlowMovingTarget()));
33997 ranu 234
 
34098 ranu 235
        a1.setRunningTarget((a1.getRunningTarget()) +
236
                (a2.getRunningTarget()));
33997 ranu 237
 
34006 ranu 238
        a1.setOtherTarget((a1.getOtherTarget()) +
239
                (a2.getOtherTarget()));
33997 ranu 240
        return a1;
241
    }
242
 
34002 ranu 243
    private RbmAchievementsModel mergeAchievements(RbmAchievementsModel a1, RbmAchievementsModel a2) {
33997 ranu 244
        // Merge logic for achievements (aggregate the target and achieved values)
34006 ranu 245
        a1.setAchievedHidTarget((a1.getAchievedHidTarget()) +
246
                (a2.getAchievedHidTarget()));
33997 ranu 247
 
34098 ranu 248
        a1.setAchievedRunningTarget((a1.getAchievedRunningTarget()) +
249
                (a2.getAchievedRunningTarget()));
33997 ranu 250
 
34006 ranu 251
        a1.setAchievedFastMovingTarget((a1.getAchievedFastMovingTarget()) +
252
                (a2.getAchievedFastMovingTarget()));
33997 ranu 253
 
34006 ranu 254
        a1.setAchievedSlowMovingTarget((a1.getAchievedSlowMovingTarget()) +
255
                (a2.getAchievedSlowMovingTarget()));
33997 ranu 256
 
34006 ranu 257
        a1.setAchievedOtherTarget((a1.getAchievedOtherTarget()) +
258
                (a2.getAchievedOtherTarget()));
33997 ranu 259
 
260
        return a1;
261
    }
34002 ranu 262
 
33997 ranu 263
    @Override
33917 ranu 264
    public List<RbmArrViewModel> getRbmTodayArr(LocalDate todayDate) throws Exception {
265
 
266
        LocalDate startDateOfMonthDay1 = LocalDate.now().withDayOfMonth(1);
267
 
34289 ranu 268
        List<WarehouseRbmTargetModel> warehouseRbmTargetModelList = this.getWarehouseWiseRbmMonthlyTarget();
269
        List<WarehouseRbmTargetModel> warehouseRbmTargetModels = warehouseRbmTargetModelList.stream().filter(x -> x.getMonthlyTarget() > 0).collect(Collectors.toList());
270
        LOGGER.info("warehouseRbmTargetModels {}", warehouseRbmTargetModels);
271
        List<TodayAchievedMovementModel> todayAchievedMovementModels = getMovementWiseAchievementByDate(todayDate, todayDate.plusDays(1));
33917 ranu 272
 
273
        List<MTDAchievedTargetModel> mtdAchievedTargetModels = getDateWiseAchievedTargetOfRbm(startDateOfMonthDay1, todayDate);
274
 
35044 ranu 275
        int remainingWorkingDaysCount = (int) getRemainingDaysInMonth(todayDate);
33917 ranu 276
 
33926 ranu 277
        List<RbmTargets> todayRbmTargetsList = rbmTargetsRepository.selectTargetsByDates(todayDate.atStartOfDay(), todayDate.atTime(LocalTime.MAX));
33917 ranu 278
 
33926 ranu 279
        LOGGER.info("todayRbmTargetsList {}", todayRbmTargetsList);
33917 ranu 280
 
281
        List<RbmArrViewModel> rbmArrViewModels = new ArrayList<>();
282
 
34028 ranu 283
        if (!todayRbmTargetsList.isEmpty()) {
33917 ranu 284
 
35454 amit 285
            // OPTIMIZED: Pre-build maps for O(1) lookup instead of O(n) filter in each iteration
286
            // Map key: "authId-warehouseId"
287
            Map<String, Double> mtdAchievedMap = mtdAchievedTargetModels.stream()
288
                    .collect(Collectors.groupingBy(
289
                            x -> x.getAuthId() + "-" + x.getWarehouseId(),
290
                            Collectors.summingDouble(MTDAchievedTargetModel::getAcheivedMonthlyTarget)
291
                    ));
292
 
293
            Map<String, TodayAchievedMovementModel> todayAchievedMap = todayAchievedMovementModels.stream()
294
                    .collect(Collectors.toMap(
295
                            x -> x.getAuthId() + "-" + x.getWarehouseId(),
296
                            x -> x,
297
                            (a, b) -> a
298
                    ));
299
 
300
            Map<String, RbmTargets> todayRbmTargetsMap = todayRbmTargetsList.stream()
301
                    .collect(Collectors.toMap(
302
                            x -> x.getRbmAuthId() + "-" + x.getWarehouseId(),
303
                            x -> x,
304
                            (a, b) -> a
305
                    ));
306
 
34028 ranu 307
            for (WarehouseRbmTargetModel rbmTarget : warehouseRbmTargetModels) {
33917 ranu 308
 
35454 amit 309
                String lookupKey = rbmTarget.getAuthId() + "-" + rbmTarget.getWarehouseId();
310
 
34028 ranu 311
                float monthlyTarget = rbmTarget.getMonthlyTarget();
35454 amit 312
                float achievedSoFar = mtdAchievedMap.getOrDefault(lookupKey, 0.0).floatValue();
33917 ranu 313
 
34028 ranu 314
                float remainingTarget = monthlyTarget - achievedSoFar;
33953 ranu 315
 
34028 ranu 316
                float todayTarget = (remainingWorkingDaysCount > 0 && remainingTarget > 0) ? remainingTarget / remainingWorkingDaysCount : 0;
317
 
33926 ranu 318
                String warehouseName = ProfitMandiConstants.WAREHOUSE_MAP.getOrDefault(rbmTarget.getWarehouseId(), "Unknown");
33917 ranu 319
 
34288 ranu 320
                LOGGER.info("rbmTarget ==== {}", rbmTarget);
34281 ranu 321
 
35454 amit 322
                TodayAchievedMovementModel todayAchievedMovementModel = todayAchievedMap.get(lookupKey);
34283 ranu 323
 
35454 amit 324
                RbmTargets todayRbmTargets = todayRbmTargetsMap.get(lookupKey);
34285 ranu 325
 
35454 amit 326
                if (todayRbmTargets != null) {
34034 ranu 327
                    LOGGER.info("todayRbmTargets {}", todayRbmTargets);
328
                    RbmArrViewModel viewModel = new RbmArrViewModel();
33917 ranu 329
 
34034 ranu 330
                    viewModel.setAuthId(rbmTarget.getAuthId());
331
                    viewModel.setRbmName(rbmTarget.getRbmName());
332
                    viewModel.setWarehouseName(warehouseName);
333
                    viewModel.setTodayTarget(Math.round(todayTarget));
334
                    viewModel.setMonthlyTarget(Math.round(monthlyTarget));
335
                    viewModel.setMtdAchievedTarget(Math.round(achievedSoFar));
33926 ranu 336
 
34034 ranu 337
                    viewModel.setTodayHidTarget(Math.round((todayRbmTargets.getHidTarget())));
338
                    viewModel.setTodayFastMovingTarget(Math.round(todayRbmTargets.getFastMovingTarget()));
34098 ranu 339
                    viewModel.setTodaySlowMovingTarget(Math.round(todayRbmTargets.getSlowMovingtarget()));
340
                    viewModel.setTodayRunningTarget(Math.round(todayRbmTargets.getRunningtarget()));
34034 ranu 341
                    viewModel.setTodayOtherMovingTarget(Math.round(todayRbmTargets.getOtherTarget()));
33926 ranu 342
 
34283 ranu 343
                    if (todayAchievedMovementModel != null) {
344
                        viewModel.setTodayAchievedHidTarget(Math.round(todayAchievedMovementModel.getHidBilled()));
345
                        viewModel.setTodayAchievedFastMovingTarget(Math.round(todayAchievedMovementModel.getFastMovingBilled()));
346
                        viewModel.setTodayAchievedSlowMovingTarget(Math.round(todayAchievedMovementModel.getSlowMovinBilled()));
347
                        viewModel.setTodayAchievedRunningTarget(Math.round(todayAchievedMovementModel.getRunningBilled()));
348
                        viewModel.setTodayAchievedOtherMovingTarget(Math.round(todayAchievedMovementModel.getOtherBilled()));
349
                        viewModel.setTotalAchievedTarget(Math.round(todayAchievedMovementModel.getHidBilled() + todayAchievedMovementModel.getFastMovingBilled() + todayAchievedMovementModel.getSlowMovinBilled() + todayAchievedMovementModel.getRunningBilled() + todayAchievedMovementModel.getOtherBilled()));
350
                    } else {
351
                        viewModel.setTodayAchievedHidTarget(0);
352
                        viewModel.setTodayAchievedFastMovingTarget(0);
353
                        viewModel.setTodayAchievedSlowMovingTarget(0);
354
                        viewModel.setTodayAchievedRunningTarget(0);
355
                        viewModel.setTodayAchievedOtherMovingTarget(0);
356
                        viewModel.setTotalAchievedTarget(0);
357
                    }
34034 ranu 358
                    rbmArrViewModels.add(viewModel);
359
                } else {
360
                    LOGGER.info("No matching RbmTargets found for AuthId: {} and rbmname {} and WarehouseId: {}", rbmTarget.getAuthId(), rbmTarget.getRbmName(), rbmTarget.getWarehouseId());
361
                }
33917 ranu 362
 
34034 ranu 363
 
364
 
34028 ranu 365
            }
33917 ranu 366
        }
367
 
368
        LOGGER.info("rbmArrViewModels {}", rbmArrViewModels);
369
        return rbmArrViewModels;
370
    }
371
 
33926 ranu 372
    @Override
373
    public void setMovementWiseRbmTargets() {
374
        LocalDate todayDate = LocalDate.now();
33917 ranu 375
 
33926 ranu 376
        LocalDate startDateOfMonthDay1 = LocalDate.now().withDayOfMonth(1);
377
 
378
        List<WarehouseRbmTargetModel> warehouseRbmTargetModels = this.getWarehouseWiseRbmMonthlyTarget();
379
 
380
        List<MTDAchievedTargetModel> mtdAchievedTargetModels = getDateWiseAchievedTargetOfRbm(startDateOfMonthDay1, todayDate);
381
 
382
 
35044 ranu 383
        int remainingWorkingDaysCount = (int) getRemainingDaysInMonth(todayDate);
33926 ranu 384
 
385
        List<WarehouseMobileStockByMovementModel> warehouseMobileStockByMovementModels = getWarehouseMobileStockByMovement();
386
 
35454 amit 387
        // OPTIMIZED: Pre-build maps for O(1) lookup instead of O(n) filter in each iteration
388
        Map<String, Double> mtdAchievedMap = mtdAchievedTargetModels.stream()
389
                .collect(Collectors.groupingBy(
390
                        x -> x.getAuthId() + "-" + x.getWarehouseId(),
391
                        Collectors.summingDouble(MTDAchievedTargetModel::getAcheivedMonthlyTarget)
392
                ));
393
 
394
        Map<Integer, WarehouseMobileStockByMovementModel> warehouseStockMap = warehouseMobileStockByMovementModels.stream()
395
                .collect(Collectors.toMap(
396
                        WarehouseMobileStockByMovementModel::getWarehouseId,
397
                        x -> x,
398
                        (a, b) -> a
399
                ));
400
 
33926 ranu 401
        for (WarehouseRbmTargetModel rbmTarget : warehouseRbmTargetModels) {
402
 
35454 amit 403
            String lookupKey = rbmTarget.getAuthId() + "-" + rbmTarget.getWarehouseId();
404
 
33926 ranu 405
            float monthlyTarget = rbmTarget.getMonthlyTarget();
35454 amit 406
            float achievedSoFar = mtdAchievedMap.getOrDefault(lookupKey, 0.0).floatValue();
33926 ranu 407
 
408
 
409
            float remainingTarget = monthlyTarget - achievedSoFar;
33953 ranu 410
            LOGGER.info("remainingTarget {}", remainingTarget);
33926 ranu 411
 
33953 ranu 412
            float todayTarget = (remainingWorkingDaysCount > 0 && remainingTarget > 0) ? remainingTarget / remainingWorkingDaysCount : 0;
413
            LOGGER.info("todayTarget {}", todayTarget);
414
 
33926 ranu 415
            // Get the warehouse stock data
35454 amit 416
            WarehouseMobileStockByMovementModel warehouseMobileStockByMovementModel = warehouseStockMap.get(rbmTarget.getWarehouseId());
33926 ranu 417
 
418
 
419
            if (warehouseMobileStockByMovementModel != null) {
420
 
421
                // Total stock value for this warehouse
422
                float totalStockValue = warehouseMobileStockByMovementModel.getTotalAvailabilityPrice();
423
 
424
                // Calculate target allocation based on stock value proportion
425
                float hidTarget = (warehouseMobileStockByMovementModel.getTotalHidCatalogPrice() / totalStockValue) * todayTarget;
426
                float fastMovingTarget = (warehouseMobileStockByMovementModel.getTotalFastMovingCatalogPrice() / totalStockValue) * todayTarget;
427
                float slowMovingTarget = (warehouseMobileStockByMovementModel.getTotalSlowMovingCatalogPrice() / totalStockValue) * todayTarget;
34098 ranu 428
                float runningTarget = (warehouseMobileStockByMovementModel.getTotalRunningCatalogPrice() / totalStockValue) * todayTarget;
33926 ranu 429
                float otherTarget = (warehouseMobileStockByMovementModel.getTotalOtherCategoryCatalogPrice() / totalStockValue) * todayTarget;
430
 
431
                RbmTargets rbmTargets = new RbmTargets();
432
                rbmTargets.setWarehouseId(rbmTarget.getWarehouseId());
433
                rbmTargets.setRbmAuthId(rbmTarget.getAuthId());
434
                rbmTargets.setRbmName(rbmTarget.getRbmName());
34098 ranu 435
                rbmTargets.setRunningtarget(runningTarget);
33926 ranu 436
                rbmTargets.setHidTarget(hidTarget);
437
                rbmTargets.setFastMovingTarget(fastMovingTarget);
34098 ranu 438
                rbmTargets.setSlowMovingtarget(slowMovingTarget);
33926 ranu 439
                rbmTargets.setOtherTarget(otherTarget);
440
                rbmTargets.setCreateTimestamp(LocalDateTime.now());
441
 
442
                rbmTargetsRepository.persist(rbmTargets);
443
 
444
            }
445
        }
446
 
447
    }
448
 
449
 
33985 ranu 450
    @Override
451
    public void setMovementWiseRbmAchievement() {
452
        LocalDate todayDate = LocalDate.now();
453
 
34055 ranu 454
        List<TodayAchievedMovementModel> todayAchievedMovementModels = getMovementWiseAchievementByDate(todayDate, todayDate.plusDays(1));
33985 ranu 455
 
456
 
457
        for (TodayAchievedMovementModel achievement : todayAchievedMovementModels) {
458
 
459
            RbmAchievements rbmAchievements = new RbmAchievements();
460
 
461
            rbmAchievements.setRbmAuthId(achievement.getAuthId());
462
            rbmAchievements.setRbmName(achievement.getRbmName());
463
            rbmAchievements.setWarehouseId(achievement.getWarehouseId());
464
            rbmAchievements.setAchievedHidTarget(achievement.getHidBilled());
465
            rbmAchievements.setAchievedFastMovingTarget(achievement.getFastMovingBilled());
34098 ranu 466
            rbmAchievements.setAchievedSlowMovingTarget(achievement.getSlowMovinBilled());
467
            rbmAchievements.setAchievedRunningTarget(achievement.getRunningBilled());
34012 ranu 468
            rbmAchievements.setAchievedOtherTarget(achievement.getOtherBilled());
33985 ranu 469
            rbmAchievements.setCreateTimestamp(LocalDateTime.now());
470
 
471
            rbmAchievementsRepository.persist(rbmAchievements);
472
 
473
        }
474
 
475
    }
476
 
34055 ranu 477
    @Override
478
    public List<Sold15daysOldAgingModel> getAgingSale(LocalDate startDate, LocalDate endDate) {
479
        Session session = sessionFactory.getCurrentSession();
480
        final TypedQuery<Sold15daysOldAgingModel> typedQuerySimilar = session.createNamedQuery("Aging.SoldAgingModel", Sold15daysOldAgingModel.class);
34056 ranu 481
        typedQuerySimilar.setParameter("startDate", startDate);
482
        typedQuerySimilar.setParameter("endDate", endDate);
34055 ranu 483
        return typedQuerySimilar.getResultList();
33985 ranu 484
 
34055 ranu 485
    }
486
 
34103 ranu 487
    @Override
488
    public List<RbmBilledFofoIdsModel> getDateWiseBilledFofoIdByRbm(LocalDate startDate, LocalDate endDate) {
489
        Session session = sessionFactory.getCurrentSession();
490
        final TypedQuery<RbmBilledFofoIdsModel> typedQuerySimilar = session.createNamedQuery("RBM.RbmBilledFofoId", RbmBilledFofoIdsModel.class);
491
        typedQuerySimilar.setParameter("startDate", startDate);
492
        typedQuerySimilar.setParameter("endDate", endDate);
493
        return typedQuerySimilar.getResultList();
35453 amit 494
    }
34103 ranu 495
 
35453 amit 496
    @Override
497
    public List<RbmWeeklyBillingModel> getWeeklyBillingDataForMonth(LocalDate monthStart, LocalDate monthEnd) {
498
        Session session = sessionFactory.getCurrentSession();
499
        final TypedQuery<RbmWeeklyBillingModel> typedQuery = session.createNamedQuery("RBM.WeeklyBilling", RbmWeeklyBillingModel.class);
500
        typedQuery.setParameter("startDate", monthStart);
501
        typedQuery.setParameter("endDate", monthEnd);
502
        return typedQuery.getResultList();
34103 ranu 503
    }
504
 
34055 ranu 505
    public List<Our15DaysOldAgingStock> our15DaysAgingStock() {
506
        Session session = sessionFactory.getCurrentSession();
507
        final TypedQuery<Our15DaysOldAgingStock> typedQuerySimilar = session.createNamedQuery("Aging.15DaysOurStock", Our15DaysOldAgingStock.class);
508
        return typedQuerySimilar.getResultList();
509
 
510
    }
511
 
34397 ranu 512
    @Autowired
513
    OrderRepository orderRepository;
34055 ranu 514
 
34397 ranu 515
    @Override
34641 ranu 516
    public double calculateFofoIdTodayTarget(int fofoId, double secondryMtd,LocalDate date) {
34397 ranu 517
 
518
        MonthlyTarget monthlyTarget = monthlyTargetRepository.selectByDateAndFofoId(YearMonth.now(), fofoId);
34404 ranu 519
        if (monthlyTarget == null) {
520
            // Log or handle as needed
521
            return 0; // or -1 or some fallback
522
        }
34397 ranu 523
 
524
        double remainingTarget = monthlyTarget.getPurchaseTarget() - secondryMtd;
34880 ranu 525
//        double remainingWorkingDays = getWorkingDaysCount(date);
526
        double remainingWorkingDays = (double) getRemainingDaysInMonth(date);
34397 ranu 527
 
528
 
34716 ranu 529
 
34397 ranu 530
        if (remainingWorkingDays == 0) return remainingTarget; // Last day
34716 ranu 531
        LOGGER.info("remainingWorkingDays {}", remainingWorkingDays);
532
        LOGGER.info("remainingTarget {}", remainingTarget);
34397 ranu 533
 
534
        return (int) Math.ceil(remainingTarget / remainingWorkingDays);
535
    }
536
 
34880 ranu 537
    @Override
538
    public long getRemainingDaysInMonth(LocalDate date) {
539
        LocalDate lastDayOfMonth = YearMonth.from(date).atEndOfMonth();
34397 ranu 540
 
34880 ranu 541
        long totalDays = ChronoUnit.DAYS.between(date, lastDayOfMonth) + 1;
34397 ranu 542
 
34880 ranu 543
        // Count Sundays manually
544
        long sundayCount = 0;
545
        LocalDate current = date;
546
        while (!current.isAfter(lastDayOfMonth)) {
547
            if (current.getDayOfWeek() == DayOfWeek.SUNDAY) {
548
                sundayCount++;
549
            }
550
            current = current.plusDays(1);
551
        }
552
 
553
        // Public holidays in the range
554
        long publicHolidays = publicHolidaysRepository
555
                .selectAllBetweenDates(date, lastDayOfMonth)
556
                .size();
557
 
558
        long remainingDays = totalDays - sundayCount - publicHolidays;
559
 
560
        LOGGER.info("remainingDays {}", remainingDays);
561
        LOGGER.info("totalDays {}", totalDays);
562
        LOGGER.info("sundays {}", sundayCount);
563
        LOGGER.info("publicHolidays {}", publicHolidays);
564
 
565
        return remainingDays;
566
    }
567
 
35631 ranu 568
    @Autowired
569
    PositionRepository positionRepository;
34880 ranu 570
 
35631 ranu 571
    @Autowired
572
    CsService csService;
34880 ranu 573
 
35631 ranu 574
    @Autowired
575
    FofoStoreRepository fofoStoreRepository;
576
 
577
    @Autowired
578
    AuthRepository authRepository;
579
 
580
    @Autowired
581
    LoanRepository loanRepository;
582
 
583
    @Autowired
584
    PartnerCollectionService partnerCollectionService;
585
 
586
    @Autowired
587
    PartnerCollectionRemarkRepository partnerCollectionRemarkRepository;
588
 
589
    @Autowired
590
    RbmCallSequenceLogRepository rbmCallSequenceLogRepository;
591
 
592
    @Autowired
593
    com.spice.profitmandi.dao.repository.cs.TicketRepository ticketRepository;
594
 
35702 ranu 595
    @Autowired
596
    com.spice.profitmandi.dao.repository.cs.AgentCallLogRepository agentCallLogRepository;
597
 
35631 ranu 598
    @Override
599
    public List<RbmCallTargetModel> getRbmCallTargetModels() throws Exception {
600
        long methodStart = System.currentTimeMillis();
601
        List<RbmCallTargetModel> rbmCallTargetModels = new ArrayList<>();
602
 
603
        // Get all RBM positions (L1 and L2)
604
        long start = System.currentTimeMillis();
605
        List<Position> allRbmPositions = positionRepository
606
                .selectPositionByCategoryId(ProfitMandiConstants.TICKET_CATEGORY_RBM).stream()
607
                .filter(x -> Arrays.asList(EscalationType.L1, EscalationType.L2).contains(x.getEscalationType()))
608
                .collect(Collectors.toList());
609
 
610
        // Separate L1 and L2 auth IDs
611
        List<Integer> l1AuthIds = allRbmPositions.stream()
612
                .filter(p -> EscalationType.L1.equals(p.getEscalationType()))
613
                .map(Position::getAuthUserId).distinct().collect(Collectors.toList());
614
        List<Integer> l2AuthIds = allRbmPositions.stream()
615
                .filter(p -> EscalationType.L2.equals(p.getEscalationType()))
616
                .map(Position::getAuthUserId).distinct().collect(Collectors.toList());
617
 
618
        // Union of all auth IDs for batch fetching
619
        List<Integer> rbmPositionsAuthIds = allRbmPositions.stream()
620
                .map(Position::getAuthUserId).distinct().collect(Collectors.toList());
621
        LOGGER.info("RBM Call Target - RBM positions fetch: {}ms, L1: {}, L2: {}", System.currentTimeMillis() - start, l1AuthIds.size(), l2AuthIds.size());
622
 
623
        start = System.currentTimeMillis();
624
        Map<String, Set<Integer>> storeGuyMap = csService.getAuthUserPartnerIdMapping();
625
        LOGGER.info("RBM Call Target - StoreGuyMap fetch: {}ms", System.currentTimeMillis() - start);
626
 
627
        LocalDateTime startDate = LocalDate.now().atStartOfDay();
628
        LocalDate firstOfMonth = LocalDate.now().withDayOfMonth(1);
629
        LocalDate endOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).plusDays(1);
630
 
631
        // Get auth user map
632
        start = System.currentTimeMillis();
633
        Map<Integer, AuthUser> authUserMap = authRepository.selectByIds(rbmPositionsAuthIds).stream()
634
                .collect(Collectors.toMap(AuthUser::getId, au -> au));
635
        LOGGER.info("RBM Call Target - AuthUser fetch: {}ms", System.currentTimeMillis() - start);
636
 
637
        // Batch fetch positions by auth IDs (to check if RBM is L1)
638
        start = System.currentTimeMillis();
639
        Map<Integer, List<Position>> positionsByAuthId = positionRepository.selectPositionByAuthIds(rbmPositionsAuthIds).stream()
640
                .collect(Collectors.groupingBy(Position::getAuthUserId));
641
        LOGGER.info("RBM Call Target - Positions by AuthId fetch: {}ms", System.currentTimeMillis() - start);
642
 
643
        // Get all fofo IDs for all RBMs
644
        Set<Integer> allFofoIds = new HashSet<>();
645
        Map<Integer, List<Integer>> rbmToFofoIdsMap = new HashMap<>();
646
        for (int rbmAuthId : rbmPositionsAuthIds) {
647
            AuthUser au = authUserMap.get(rbmAuthId);
648
            if (au != null && storeGuyMap.containsKey(au.getEmailId())) {
649
                List<Integer> fofoIds = new ArrayList<>(storeGuyMap.get(au.getEmailId()));
650
                allFofoIds.addAll(fofoIds);
651
                rbmToFofoIdsMap.put(rbmAuthId, fofoIds);
652
            }
653
        }
654
        // Fetch escalated tickets for L2 early so their fofoIds are included in batch fetches
655
        List<Ticket> escalatedTickets = Collections.emptyList();
656
        Map<Integer, List<Integer>> l2AuthIdToFofoIds = new HashMap<>();
657
        if (!l2AuthIds.isEmpty()) {
658
            start = System.currentTimeMillis();
659
            escalatedTickets = ticketRepository.selectOpenEscalatedTicketsByAuthIds(l2AuthIds);
660
            LOGGER.info("RBM Call Target - Escalated tickets fetch: {}ms, count: {}", System.currentTimeMillis() - start, escalatedTickets.size());
661
 
662
            for (int l2AuthId : l2AuthIds) {
663
                List<Integer> l2FofoIds = escalatedTickets.stream()
664
                        .filter(t -> t.getL2AuthUser() == l2AuthId
665
                                || t.getL3AuthUser() == l2AuthId
666
                                || t.getL4AuthUser() == l2AuthId
667
                                || t.getL5AuthUser() == l2AuthId)
668
                        .map(Ticket::getFofoId)
669
                        .distinct()
670
                        .collect(Collectors.toList());
671
                l2AuthIdToFofoIds.put(l2AuthId, l2FofoIds);
672
                allFofoIds.addAll(l2FofoIds);
673
            }
674
        }
675
        LOGGER.info("RBM Call Target - Total fofo IDs to process: {}", allFofoIds.size());
676
 
677
        // Get only needed fofo stores (OPTIMIZED - was fetching ALL stores before)
678
        start = System.currentTimeMillis();
679
        Map<Integer, FofoStore> fofoStoresMap = new HashMap<>();
680
        if (!allFofoIds.isEmpty()) {
681
            try {
682
                fofoStoresMap = fofoStoreRepository.selectByRetailerIds(new ArrayList<>(allFofoIds)).stream()
683
                        .collect(Collectors.toMap(FofoStore::getId, x -> x, (a, b) -> a));
684
            } catch (ProfitMandiBusinessException e) {
685
                LOGGER.error("Error fetching fofo stores", e);
686
            }
687
        }
688
        LOGGER.info("RBM Call Target - FofoStores fetch (only needed): {}ms, count: {}", System.currentTimeMillis() - start, fofoStoresMap.size());
689
 
690
        // Batch fetch max remark ids for all fofoIds (for escalation filtering)
691
        start = System.currentTimeMillis();
692
        Map<Integer, PartnerCollectionRemark> allPartnerCollectionRemarks = new HashMap<>();
693
        if (!allFofoIds.isEmpty()) {
694
            List<Integer> allRemarkIds = partnerCollectionRemarkRepository.selectMaxRemarkId(new ArrayList<>(allFofoIds));
695
            if (!allRemarkIds.isEmpty()) {
696
                allPartnerCollectionRemarks = partnerCollectionRemarkRepository.selectByIds(allRemarkIds).stream()
697
                        .collect(Collectors.toMap(PartnerCollectionRemark::getFofoId, x -> x, (a, b) -> a));
698
            }
699
        }
700
        LOGGER.info("RBM Call Target - PartnerCollectionRemarks fetch: {}ms", System.currentTimeMillis() - start);
701
 
702
        // Batch fetch collection RANK map for all fofoIds (OPTIMIZED - only fetches rank, not full model)
703
        start = System.currentTimeMillis();
704
        Map<Integer, Integer> allCollectionRankMap = new HashMap<>();
705
        if (!allFofoIds.isEmpty()) {
706
            try {
707
                allCollectionRankMap = partnerCollectionService.getCollectionRankMap(new ArrayList<>(allFofoIds), startDate);
708
            } catch (ProfitMandiBusinessException e) {
709
                LOGGER.error("Error fetching collection rank map for all fofoIds", e);
710
            }
711
        }
712
        LOGGER.info("RBM Call Target - CollectionRankMap fetch (OPTIMIZED): {}ms", System.currentTimeMillis() - start);
713
 
35669 ranu 714
        // Get MTD billing data for zero billing calculation and partner counts
35631 ranu 715
        start = System.currentTimeMillis();
716
        List<RbmWeeklyBillingModel> mtdBillingData = getWeeklyBillingDataForMonth(firstOfMonth, endOfMonth);
717
        Set<Integer> allMtdBilledFofoIds = mtdBillingData.stream()
718
                .filter(RbmWeeklyBillingModel::isMtdBilled)
719
                .map(RbmWeeklyBillingModel::getFofoId)
720
                .collect(Collectors.toSet());
35669 ranu 721
        // Build partner count and fofoIds per RBM from mtdBillingData (same source as Today ARR page)
722
        Map<Integer, Set<Integer>> mtdFofoIdsByAuthId = mtdBillingData.stream()
723
                .filter(RbmWeeklyBillingModel::isTargetedPartner)
724
                .collect(Collectors.groupingBy(RbmWeeklyBillingModel::getAuthId,
725
                        Collectors.mapping(RbmWeeklyBillingModel::getFofoId, Collectors.toSet())));
35631 ranu 726
        LOGGER.info("RBM Call Target - MTD Billing fetch: {}ms", System.currentTimeMillis() - start);
727
 
728
        // Batch fetch today's remarks for all auth IDs (to calculate Value Achieved)
729
        start = System.currentTimeMillis();
730
        Map<Integer, List<PartnerCollectionRemark>> remarksByAuthId = partnerCollectionRemarkRepository
731
                .selectAllByAuthIdsOnDate(rbmPositionsAuthIds, LocalDate.now()).stream()
732
                .collect(Collectors.groupingBy(PartnerCollectionRemark::getAuthId));
733
        LOGGER.info("RBM Call Target - Today Remarks fetch: {}ms", System.currentTimeMillis() - start);
734
 
735
        // Batch fetch today's out-of-sequence logs for all RBMs
736
        start = System.currentTimeMillis();
737
        LocalDateTime todayStart = LocalDate.now().atStartOfDay();
738
        LocalDateTime todayEnd = LocalDate.now().plusDays(1).atStartOfDay();
739
        List<RbmCallSequenceLog> outOfSequenceLogs = rbmCallSequenceLogRepository.selectOutOfSequenceByDateRange(todayStart, todayEnd);
740
        Map<Integer, Long> outOfSequenceCountByAuthId = outOfSequenceLogs.stream()
35654 ranu 741
                .collect(Collectors.groupingBy(RbmCallSequenceLog::getAuthId,
742
                        Collectors.mapping(RbmCallSequenceLog::getFofoId, Collectors.collectingAndThen(Collectors.toSet(), s -> (long) s.size()))));
35631 ranu 743
        LOGGER.info("RBM Call Target - Out of Sequence fetch: {}ms", System.currentTimeMillis() - start);
744
 
745
        // Process L1 RBMs (existing logic)
746
        for (int rbmAuthId : l1AuthIds) {
747
            AuthUser authUser = authUserMap.get(rbmAuthId);
748
            if (authUser == null || !storeGuyMap.containsKey(authUser.getEmailId())) {
749
                continue;
750
            }
751
 
752
            List<Integer> fofoIdList = rbmToFofoIdsMap.getOrDefault(rbmAuthId, Collections.emptyList());
753
 
754
            // Check if RBM is L1 (same logic as getSummaryModel)
755
            List<Position> positions = positionsByAuthId.getOrDefault(authUser.getId(), Collections.emptyList());
756
            boolean isRBMAndL1 = positions.stream()
757
                    .anyMatch(position ->
758
                            ProfitMandiConstants.TICKET_CATEGORY_RBM == position.getCategoryId()
759
                                    && EscalationType.L1.equals(position.getEscalationType()));
760
 
761
            // Filter escalated partners for L1 RBMs (same logic as getSummaryModel)
762
            List<Integer> fofoIds = fofoIdList;
763
            if (isRBMAndL1) {
764
                Map<Integer, PartnerCollectionRemark> partnerCollectionRemarks = new HashMap<>();
765
                for (Integer fofoId : fofoIdList) {
766
                    if (allPartnerCollectionRemarks.containsKey(fofoId)) {
767
                        partnerCollectionRemarks.put(fofoId, allPartnerCollectionRemarks.get(fofoId));
768
                    }
769
                }
770
                fofoIds = partnerCollectionRemarks.entrySet().stream()
771
                        .filter(entry -> {
772
                            PartnerCollectionRemark pcrMap = entry.getValue();
773
                            return !(CollectionRemark.RBM_L2_ESCALATION.equals(pcrMap.getRemark())
774
                                    || CollectionRemark.SALES_ESCALATION.equals(pcrMap.getRemark()));
775
                        })
776
                        .map(Map.Entry::getKey)
777
                        .collect(Collectors.toList());
778
            }
779
 
35669 ranu 780
            // Filter to only external, ACTIVE stores (collection plan not required)
35631 ranu 781
            Map<Integer, Integer> finalAllCollectionRankMap = allCollectionRankMap;
782
            Map<Integer, FofoStore> finalFofoStoresMap = fofoStoresMap;
783
            List<Integer> validFofoIds = fofoIds.stream()
784
                    .filter(fofoId -> {
785
                        FofoStore store = finalFofoStoresMap.get(fofoId);
786
                        if (store == null || store.isInternal()) {
787
                            return false;
788
                        }
789
                        // Only include ACTIVE partners (not Low Sale, not Disputed, not Billing Pending)
35669 ranu 790
                        return ActivationType.ACTIVE.equals(store.getActivationType());
35631 ranu 791
                    })
792
                    .collect(Collectors.toList());
793
 
794
            if (validFofoIds.isEmpty()) {
795
                continue;
796
            }
797
 
798
            RbmCallTargetModel targetModel = new RbmCallTargetModel();
799
            targetModel.setAuthId(rbmAuthId);
800
            targetModel.setRbmName(authUser.getFullName());
35669 ranu 801
            // Use partner count from mtdBillingData (same source as Today ARR page)
802
            Set<Integer> mtdFofoIds = mtdFofoIdsByAuthId.getOrDefault(rbmAuthId, Collections.emptySet());
803
            targetModel.setPartnerCount(mtdFofoIds.size());
35631 ranu 804
 
805
            // Categorize each partner - each partner belongs to ONE category only
35665 ranu 806
            // Priority: PlanToday > CarryForward > ZeroBilling > Untouched > FuturePlan > Normal
35631 ranu 807
            Set<Integer> planTodayPartners = new HashSet<>();
808
            Set<Integer> carryForwardPartners = new HashSet<>();
809
            Set<Integer> untouchedPartners = new HashSet<>();
810
            Set<Integer> zeroBillingPartners = new HashSet<>();
811
            Set<Integer> futurePlanPartners = new HashSet<>();
812
            Set<Integer> normalPartners = new HashSet<>();
813
 
814
            for (Integer fofoId : validFofoIds) {
815
                // Get collection plan rank (from optimized rank map)
816
                int rank = allCollectionRankMap.getOrDefault(fofoId, 5); // default to Normal if no plan
817
 
818
                // Check if partner has zero billing in MTD
819
                boolean hasZeroBilling = !allMtdBilledFofoIds.contains(fofoId);
820
 
821
                // Assign to category based on priority
822
                if (rank == 1) {
823
                    planTodayPartners.add(fofoId);
824
                } else if (rank == 2) {
825
                    carryForwardPartners.add(fofoId);
35665 ranu 826
                } else if (hasZeroBilling) {
827
                    zeroBillingPartners.add(fofoId);
35631 ranu 828
                } else if (rank == 3) {
829
                    untouchedPartners.add(fofoId);
830
                } else if (rank == 4) {
831
                    futurePlanPartners.add(fofoId);
832
                } else {
833
                    normalPartners.add(fofoId);
834
                }
835
            }
836
 
837
            // Set counts
838
            targetModel.setCreditCollection(0); // Credit collection is handled in separate list
839
            targetModel.setPlanToday(planTodayPartners.size());
840
            targetModel.setCarryForward(carryForwardPartners.size());
841
            targetModel.setUntouched(untouchedPartners.size());
842
            targetModel.setZeroBilling(zeroBillingPartners.size());
843
            targetModel.setFuturePlan(futurePlanPartners.size());
844
            targetModel.setNormal(normalPartners.size());
845
 
846
            // Today Target = PlanToday + CarryForward + ZeroBilling + Untouched
847
            // These are mutually exclusive now, so we can sum them
848
            long todayTarget = planTodayPartners.size() +
849
                    carryForwardPartners.size() + zeroBillingPartners.size() + untouchedPartners.size();
850
            targetModel.setTodayTargetOfCall(todayTarget);
851
 
852
            // Create set of partners in Today Target categories
853
            Set<Integer> todayTargetPartners = new HashSet<>();
854
            todayTargetPartners.addAll(planTodayPartners);
855
            todayTargetPartners.addAll(carryForwardPartners);
856
            todayTargetPartners.addAll(zeroBillingPartners);
857
            todayTargetPartners.addAll(untouchedPartners);
858
 
859
            // Value Achieved = Partners from Today Target that have been contacted today (have remark today)
860
            List<PartnerCollectionRemark> todayRemarks = remarksByAuthId.getOrDefault(rbmAuthId, Collections.emptyList());
861
            long valueAchieved = todayRemarks.stream()
862
                    .map(PartnerCollectionRemark::getFofoId)
863
                    .filter(todayTargetPartners::contains)
864
                    .distinct()
865
                    .count();
866
            targetModel.setValueTargetAchieved(valueAchieved);
867
 
868
            // Moved to Future = Partners in Future Plan category who have a remark today
869
            // These are partners who were contacted today but moved to a future date
870
            Set<Integer> todayRemarkedFofoIds = todayRemarks.stream()
871
                    .map(PartnerCollectionRemark::getFofoId)
872
                    .collect(Collectors.toSet());
873
            long movedToFuture = futurePlanPartners.stream()
874
                    .filter(todayRemarkedFofoIds::contains)
875
                    .count();
876
            targetModel.setMovedToFuture(movedToFuture);
877
 
878
            // Set out of sequence count for this RBM
879
            targetModel.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(rbmAuthId, 0L));
880
 
881
            rbmCallTargetModels.add(targetModel);
882
        }
883
 
884
        // Process L2 RBMs (escalated ticket logic with categorization)
885
        for (int l2AuthId : l2AuthIds) {
886
            AuthUser authUser = authUserMap.get(l2AuthId);
887
            if (authUser == null) {
888
                continue;
889
            }
890
 
891
            List<Integer> l2FofoIdList = l2AuthIdToFofoIds.getOrDefault(l2AuthId, Collections.emptyList());
892
 
35662 ranu 893
            // For L2, use all escalated ticket fofoIds as target (no collection plan filter)
894
            Set<Integer> l2TargetFofoIds = new HashSet<>(l2FofoIdList);
35631 ranu 895
 
896
            RbmCallTargetModel l2Model = new RbmCallTargetModel();
897
            l2Model.setAuthId(l2AuthId);
898
            l2Model.setRbmName(authUser.getFullName() + " (L2)");
899
            l2Model.setL2Position(true);
900
            l2Model.setL2CallingList(l2FofoIdList.size());
901
            // Partner count = total assigned partners (same as L1 source)
902
            List<Integer> l2AssignedFofoIds = rbmToFofoIdsMap.getOrDefault(l2AuthId, Collections.emptyList());
903
            l2Model.setPartnerCount(l2AssignedFofoIds.size());
904
 
35662 ranu 905
            // L2 Target = all escalated ticket fofoIds
906
            l2Model.setTodayTargetOfCall(l2TargetFofoIds.size());
35631 ranu 907
 
35662 ranu 908
            // Value Achieved = distinct fofoIds from today's remarks that are in escalated tickets
909
            List<PartnerCollectionRemark> l2TodayRemarks = remarksByAuthId.getOrDefault(l2AuthId, Collections.emptyList());
910
            long l2ValueAchieved = l2TodayRemarks.stream()
911
                    .map(PartnerCollectionRemark::getFofoId)
912
                    .filter(l2TargetFofoIds::contains)
913
                    .distinct()
914
                    .count();
915
            l2Model.setValueTargetAchieved(l2ValueAchieved);
35631 ranu 916
 
917
            l2Model.setOutOfSequenceCount(outOfSequenceCountByAuthId.getOrDefault(l2AuthId, 0L));
918
            rbmCallTargetModels.add(l2Model);
919
        }
920
 
921
        // Group L1 under their L2 manager using authUser.managerId
922
        Map<Integer, RbmCallTargetModel> l2ModelsByAuthId = new HashMap<>();
923
        Map<Integer, RbmCallTargetModel> l1ModelsByAuthId = new HashMap<>();
924
        for (RbmCallTargetModel m : rbmCallTargetModels) {
925
            if (m.isL2Position()) {
926
                l2ModelsByAuthId.put(m.getAuthId(), m);
927
            } else {
928
                l1ModelsByAuthId.put(m.getAuthId(), m);
929
            }
930
        }
931
 
932
        // Build L2 -> L1 team map using managerId from AuthUser
933
        Map<Integer, List<RbmCallTargetModel>> l2TeamMap = new LinkedHashMap<>();
934
        for (RbmCallTargetModel l2Model : l2ModelsByAuthId.values()) {
935
            l2TeamMap.put(l2Model.getAuthId(), new ArrayList<>());
936
        }
937
 
938
        Set<Integer> addedL1AuthIds = new HashSet<>();
939
        for (RbmCallTargetModel l1Model : l1ModelsByAuthId.values()) {
940
            AuthUser l1User = authUserMap.get(l1Model.getAuthId());
941
            if (l1User != null && l2TeamMap.containsKey(l1User.getManagerId())) {
942
                l2TeamMap.get(l1User.getManagerId()).add(l1Model);
943
                addedL1AuthIds.add(l1Model.getAuthId());
944
            }
945
        }
946
 
947
        // Build sorted result: L2 row, then its L1 team (sorted by name)
948
        List<RbmCallTargetModel> sortedModels = new ArrayList<>();
949
 
950
        List<RbmCallTargetModel> l2Sorted = new ArrayList<>(l2ModelsByAuthId.values());
951
        l2Sorted.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
952
 
953
        for (RbmCallTargetModel l2Model : l2Sorted) {
954
            sortedModels.add(l2Model);
955
            List<RbmCallTargetModel> team = l2TeamMap.getOrDefault(l2Model.getAuthId(), Collections.emptyList());
956
            team.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
957
            sortedModels.addAll(team);
958
        }
959
 
960
        // Add any L1 RBMs not mapped to any L2 (sorted by name)
961
        List<RbmCallTargetModel> unmappedL1 = new ArrayList<>();
962
        for (RbmCallTargetModel m : l1ModelsByAuthId.values()) {
963
            if (!addedL1AuthIds.contains(m.getAuthId())) {
964
                unmappedL1.add(m);
965
            }
966
        }
967
        unmappedL1.sort(Comparator.comparing(RbmCallTargetModel::getRbmName));
968
        sortedModels.addAll(unmappedL1);
969
 
970
        LOGGER.info("RBM Call Target - TOTAL TIME: {}ms, RBM count: {}", System.currentTimeMillis() - methodStart, sortedModels.size());
971
        return sortedModels;
972
    }
973
 
974
    @Override
975
    public List<OutOfSequenceDetailModel> getOutOfSequenceDetails(int authId) {
976
 
977
        LocalDate today = LocalDate.now();
978
        LocalDateTime start = today.atStartOfDay();
979
        LocalDateTime end = today.plusDays(1).atStartOfDay();
980
 
981
        List<RbmCallSequenceLog> logs =
982
                rbmCallSequenceLogRepository.selectByAuthIdAndDateRange(authId, start, end);
983
 
35654 ranu 984
        Map<Integer, RbmCallSequenceLog> uniqueOosLogsByFofoId = new LinkedHashMap<>();
35631 ranu 985
 
986
        for (RbmCallSequenceLog log : logs) {
987
            if (log.isOutOfSequence()) {
35654 ranu 988
                // Keep only the first occurrence per fofoId (latest entry since ordered by id DESC)
989
                uniqueOosLogsByFofoId.putIfAbsent(log.getFofoId(), log);
35631 ranu 990
            }
991
        }
992
 
35654 ranu 993
        if (uniqueOosLogsByFofoId.isEmpty()) {
35631 ranu 994
            return Collections.emptyList();
995
        }
996
 
35654 ranu 997
        Set<Integer> fofoIds = uniqueOosLogsByFofoId.keySet();
35631 ranu 998
        Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
999
        try {
1000
            retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
1001
        } catch (ProfitMandiBusinessException e) {
1002
            LOGGER.error("Error fetching fofo stores", e);
1003
        }
1004
 
1005
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
1006
        List<OutOfSequenceDetailModel> result = new ArrayList<>();
1007
 
35654 ranu 1008
        for (RbmCallSequenceLog log : uniqueOosLogsByFofoId.values()) {
35631 ranu 1009
            CustomRetailer retailer = retailerMap.get(log.getFofoId());
1010
            String partyName = retailer != null
1011
                    ? retailer.getBusinessName()
1012
                    : "Unknown (" + log.getFofoId() + ")";
1013
            String code = retailer != null
1014
                    ? retailer.getCode()
1015
                    : "-";
1016
 
1017
            String time = log.getCreateTimestamp() != null
1018
                    ? log.getCreateTimestamp().format(timeFormatter)
1019
                    : "-";
1020
 
1021
            result.add(new OutOfSequenceDetailModel(partyName, code, time));
1022
        }
1023
 
1024
        return result;
1025
    }
1026
 
35645 ranu 1027
    @Override
35672 ranu 1028
    public List<CalledPartnerDetailModel> getCalledPartnerDetails(int authId) throws ProfitMandiBusinessException {
35670 ranu 1029
        LocalDate today = LocalDate.now();
35672 ranu 1030
        LocalDateTime startDate = today.atStartOfDay();
1031
        LocalDate firstOfMonth = today.withDayOfMonth(1);
1032
        LocalDate endOfMonth = today.withDayOfMonth(today.lengthOfMonth()).plusDays(1);
35670 ranu 1033
 
35672 ranu 1034
        // Get auth user
1035
        List<AuthUser> authUsers = authRepository.selectByIds(Collections.singletonList(authId));
1036
        if (authUsers.isEmpty()) {
1037
            return Collections.emptyList();
1038
        }
1039
        AuthUser authUser = authUsers.get(0);
35670 ranu 1040
 
35672 ranu 1041
        // Check if L1 or L2
1042
        List<Position> positions = positionRepository.selectPositionByAuthIds(Collections.singletonList(authId));
1043
        boolean isL2 = positions.stream()
1044
                .anyMatch(p -> ProfitMandiConstants.TICKET_CATEGORY_RBM == p.getCategoryId()
1045
                        && EscalationType.L2.equals(p.getEscalationType()));
1046
 
1047
        // Get fofo IDs for this RBM
1048
        List<Integer> fofoIdList;
1049
        if (isL2) {
1050
            // L2: get fofo IDs from escalated tickets (all escalated tickets are target)
1051
            List<Ticket> escalatedTickets = ticketRepository.selectOpenEscalatedTicketsByAuthIds(Collections.singletonList(authId));
1052
            fofoIdList = escalatedTickets.stream()
1053
                    .filter(t -> t.getL2AuthUser() == authId
1054
                            || t.getL3AuthUser() == authId
1055
                            || t.getL4AuthUser() == authId
1056
                            || t.getL5AuthUser() == authId)
1057
                    .map(Ticket::getFofoId)
1058
                    .distinct()
1059
                    .collect(Collectors.toList());
1060
 
35720 ranu 1061
            // For L2, all escalated tickets are target
1062
            Set<Integer> targetFofoIds = new HashSet<>(fofoIdList);
1063
 
1064
            // Get today's remarks
35672 ranu 1065
            List<PartnerCollectionRemark> todayRemarks = partnerCollectionRemarkRepository
1066
                    .selectAllByAuthIdsOnDate(Collections.singletonList(authId), today);
1067
 
35720 ranu 1068
            // Filter to only target partners and deduplicate
35672 ranu 1069
            Map<Integer, PartnerCollectionRemark> uniqueRemarksByFofoId = new LinkedHashMap<>();
1070
            for (PartnerCollectionRemark remark : todayRemarks) {
35720 ranu 1071
                if (targetFofoIds.contains(remark.getFofoId())) {
1072
                    uniqueRemarksByFofoId.putIfAbsent(remark.getFofoId(), remark);
1073
                }
35672 ranu 1074
            }
1075
 
1076
            return buildCalledPartnerResult(uniqueRemarksByFofoId);
1077
        }
1078
 
1079
        // L1 Logic
1080
        Map<String, Set<Integer>> storeGuyMap;
1081
        try {
1082
            storeGuyMap = csService.getAuthUserPartnerIdMapping();
1083
        } catch (ProfitMandiBusinessException e) {
1084
            LOGGER.error("Error fetching store guy map", e);
35670 ranu 1085
            return Collections.emptyList();
1086
        }
1087
 
35672 ranu 1088
        if (!storeGuyMap.containsKey(authUser.getEmailId())) {
1089
            return Collections.emptyList();
1090
        }
1091
        fofoIdList = new ArrayList<>(storeGuyMap.get(authUser.getEmailId()));
1092
 
1093
        if (fofoIdList.isEmpty()) {
1094
            return Collections.emptyList();
1095
        }
1096
 
1097
        // Get fofo stores for filtering
1098
        Map<Integer, FofoStore> fofoStoresMap;
1099
        try {
1100
            fofoStoresMap = fofoStoreRepository.selectByRetailerIds(fofoIdList).stream()
1101
                    .collect(Collectors.toMap(FofoStore::getId, x -> x, (a, b) -> a));
1102
        } catch (ProfitMandiBusinessException e) {
1103
            LOGGER.error("Error fetching fofo stores", e);
1104
            return Collections.emptyList();
1105
        }
1106
 
1107
        // Filter escalated partners for L1
1108
        List<Integer> allRemarkIds = partnerCollectionRemarkRepository.selectMaxRemarkId(fofoIdList);
1109
        if (!allRemarkIds.isEmpty()) {
1110
            Map<Integer, PartnerCollectionRemark> partnerCollectionRemarks = partnerCollectionRemarkRepository.selectByIds(allRemarkIds).stream()
1111
                    .collect(Collectors.toMap(PartnerCollectionRemark::getFofoId, x -> x, (a, b) -> a));
1112
            fofoIdList = partnerCollectionRemarks.entrySet().stream()
1113
                    .filter(entry -> {
1114
                        PartnerCollectionRemark pcrMap = entry.getValue();
1115
                        return !(CollectionRemark.RBM_L2_ESCALATION.equals(pcrMap.getRemark())
1116
                                || CollectionRemark.SALES_ESCALATION.equals(pcrMap.getRemark()));
1117
                    })
1118
                    .map(Map.Entry::getKey)
1119
                    .collect(Collectors.toList());
1120
        }
1121
 
1122
        // Filter to only external, ACTIVE stores
1123
        List<Integer> validFofoIds = fofoIdList.stream()
1124
                .filter(fofoId -> {
1125
                    FofoStore store = fofoStoresMap.get(fofoId);
1126
                    if (store == null || store.isInternal()) {
1127
                        return false;
1128
                    }
1129
                    return ActivationType.ACTIVE.equals(store.getActivationType());
1130
                })
1131
                .collect(Collectors.toList());
1132
 
1133
        if (validFofoIds.isEmpty()) {
1134
            return Collections.emptyList();
1135
        }
1136
 
1137
        // Get collection rank map
1138
        Map<Integer, Integer> collectionRankMap;
1139
        try {
1140
            collectionRankMap = partnerCollectionService.getCollectionRankMap(validFofoIds, startDate);
1141
        } catch (ProfitMandiBusinessException e) {
1142
            LOGGER.error("Error fetching collection rank map", e);
1143
            collectionRankMap = new HashMap<>();
1144
        }
1145
 
1146
        // Get MTD billing data for zero billing check
1147
        List<RbmWeeklyBillingModel> mtdBillingData = getWeeklyBillingDataForMonth(firstOfMonth, endOfMonth);
1148
        Set<Integer> mtdBilledFofoIds = mtdBillingData.stream()
1149
                .filter(RbmWeeklyBillingModel::isMtdBilled)
1150
                .map(RbmWeeklyBillingModel::getFofoId)
1151
                .collect(Collectors.toSet());
1152
 
1153
        // Build target partner set (PlanToday + CarryForward + ZeroBilling + Untouched)
1154
        Set<Integer> targetFofoIds = new HashSet<>();
1155
        for (Integer fofoId : validFofoIds) {
1156
            int rank = collectionRankMap.getOrDefault(fofoId, 5);
1157
            boolean hasZeroBilling = !mtdBilledFofoIds.contains(fofoId);
1158
 
1159
            // Same priority logic as in getRbmCallTargetModels
1160
            if (rank == 1 || rank == 2 || hasZeroBilling || rank == 3) {
1161
                targetFofoIds.add(fofoId);
1162
            }
1163
            // rank 4 (FuturePlan) and rank 5 (Normal) are NOT in target
1164
        }
1165
 
35720 ranu 1166
        // Get today's remarks
35672 ranu 1167
        List<PartnerCollectionRemark> todayRemarks = partnerCollectionRemarkRepository
1168
                .selectAllByAuthIdsOnDate(Collections.singletonList(authId), today);
1169
 
35720 ranu 1170
        // Filter to only target partners and deduplicate
35670 ranu 1171
        Map<Integer, PartnerCollectionRemark> uniqueRemarksByFofoId = new LinkedHashMap<>();
1172
        for (PartnerCollectionRemark remark : todayRemarks) {
35720 ranu 1173
            if (targetFofoIds.contains(remark.getFofoId())) {
1174
                uniqueRemarksByFofoId.putIfAbsent(remark.getFofoId(), remark);
1175
            }
35670 ranu 1176
        }
1177
 
35672 ranu 1178
        return buildCalledPartnerResult(uniqueRemarksByFofoId);
1179
    }
1180
 
1181
    private List<CalledPartnerDetailModel> buildCalledPartnerResult(Map<Integer, PartnerCollectionRemark> uniqueRemarksByFofoId) {
1182
        if (uniqueRemarksByFofoId.isEmpty()) {
1183
            return Collections.emptyList();
1184
        }
1185
 
35670 ranu 1186
        Set<Integer> fofoIds = uniqueRemarksByFofoId.keySet();
1187
        Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
1188
        try {
1189
            retailerMap = retailerService.getFofoRetailers(new ArrayList<>(fofoIds));
1190
        } catch (ProfitMandiBusinessException e) {
1191
            LOGGER.error("Error fetching fofo stores", e);
1192
        }
1193
 
35702 ranu 1194
        // Fetch call logs for remarks that have agentCallLogId
1195
        List<Long> callLogIds = uniqueRemarksByFofoId.values().stream()
1196
                .filter(r -> r.getAgentCallLogId() > 0)
1197
                .map(PartnerCollectionRemark::getAgentCallLogId)
1198
                .collect(Collectors.toList());
1199
 
1200
        Map<Long, com.spice.profitmandi.dao.entity.cs.AgentCallLog> callLogMap = new HashMap<>();
1201
        if (!callLogIds.isEmpty()) {
35720 ranu 1202
            try {
1203
                List<com.spice.profitmandi.dao.entity.cs.AgentCallLog> callLogs = agentCallLogRepository.findByIds(callLogIds);
1204
                callLogMap = callLogs.stream()
1205
                        .collect(Collectors.toMap(com.spice.profitmandi.dao.entity.cs.AgentCallLog::getId, c -> c, (a, b) -> a));
1206
            } catch (Exception e) {
1207
                LOGGER.error("Error fetching call logs by ids", e);
1208
            }
35702 ranu 1209
        }
1210
 
35670 ranu 1211
        DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("hh:mm a");
35702 ranu 1212
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("dd-MM-yyyy hh:mm a");
35670 ranu 1213
        List<CalledPartnerDetailModel> result = new ArrayList<>();
1214
 
1215
        for (PartnerCollectionRemark remark : uniqueRemarksByFofoId.values()) {
1216
            CustomRetailer retailer = retailerMap.get(remark.getFofoId());
1217
            String partyName = retailer != null
1218
                    ? retailer.getBusinessName()
1219
                    : "Unknown (" + remark.getFofoId() + ")";
1220
            String code = retailer != null
1221
                    ? retailer.getCode()
1222
                    : "-";
1223
 
1224
            String remarkValue = remark.getRemark() != null
1225
                    ? remark.getRemark().getValue()
1226
                    : "-";
1227
 
35677 ranu 1228
            String messageValue = remark.getMessage() != null
1229
                    ? remark.getMessage()
1230
                    : "-";
1231
 
35670 ranu 1232
            String time = remark.getCreateTimestamp() != null
1233
                    ? remark.getCreateTimestamp().format(timeFormatter)
1234
                    : "-";
1235
 
35702 ranu 1236
            // Get call log data if available
1237
            String recordingUrl = null;
1238
            String callStatus = null;
1239
            String callDuration = null;
1240
            String callDateTime = null;
1241
 
1242
            if (remark.getAgentCallLogId() > 0 && callLogMap.containsKey(remark.getAgentCallLogId())) {
1243
                com.spice.profitmandi.dao.entity.cs.AgentCallLog callLog = callLogMap.get(remark.getAgentCallLogId());
1244
                recordingUrl = callLog.getRecordingUrl();
1245
                callStatus = callLog.getCallStatus();
1246
                callDuration = callLog.getCallDuration();
1247
                if (callLog.getCallDate() != null && callLog.getCallTime() != null) {
1248
                    LocalDateTime callDateTimeObj = LocalDateTime.of(callLog.getCallDate(), callLog.getCallTime());
1249
                    callDateTime = callDateTimeObj.format(dateTimeFormatter);
1250
                }
1251
            }
1252
 
1253
            result.add(new CalledPartnerDetailModel(partyName, code, remarkValue, messageValue, time,
1254
                    recordingUrl, callStatus, callDuration, callDateTime));
35670 ranu 1255
        }
1256
 
1257
        return result;
1258
    }
1259
 
1260
    @Override
35645 ranu 1261
    public List<List<String>> getRbmCallTargetRawDataByAuthId(int authId) throws Exception {
1262
        List<List<String>> rows = new ArrayList<>();
35631 ranu 1263
 
35645 ranu 1264
        // Get auth user
1265
        List<AuthUser> authUsers = authRepository.selectByIds(Collections.singletonList(authId));
1266
        if (authUsers.isEmpty()) {
1267
            return rows;
1268
        }
1269
        AuthUser authUser = authUsers.get(0);
1270
 
1271
        // Get positions to determine if L1 or L2
1272
        List<Position> positions = positionRepository.selectPositionByAuthIds(Collections.singletonList(authId));
1273
        boolean isL1 = positions.stream()
1274
                .anyMatch(p -> ProfitMandiConstants.TICKET_CATEGORY_RBM == p.getCategoryId()
1275
                        && EscalationType.L1.equals(p.getEscalationType()));
1276
        boolean isL2 = positions.stream()
1277
                .anyMatch(p -> ProfitMandiConstants.TICKET_CATEGORY_RBM == p.getCategoryId()
1278
                        && EscalationType.L2.equals(p.getEscalationType()));
1279
 
1280
        // Get fofo IDs for this RBM
1281
        Map<String, Set<Integer>> storeGuyMap = csService.getAuthUserPartnerIdMapping();
1282
        List<Integer> fofoIdList = new ArrayList<>();
1283
 
1284
        if (isL2) {
1285
            // L2: get fofo IDs from escalated tickets
1286
            List<Ticket> escalatedTickets = ticketRepository.selectOpenEscalatedTicketsByAuthIds(Collections.singletonList(authId));
1287
            fofoIdList = escalatedTickets.stream()
1288
                    .filter(t -> t.getL2AuthUser() == authId
1289
                            || t.getL3AuthUser() == authId
1290
                            || t.getL4AuthUser() == authId
1291
                            || t.getL5AuthUser() == authId)
1292
                    .map(Ticket::getFofoId)
1293
                    .distinct()
1294
                    .collect(Collectors.toList());
1295
        } else if (storeGuyMap.containsKey(authUser.getEmailId())) {
1296
            fofoIdList = new ArrayList<>(storeGuyMap.get(authUser.getEmailId()));
1297
        }
1298
 
1299
        if (fofoIdList.isEmpty()) {
1300
            return rows;
1301
        }
1302
 
1303
        LocalDateTime startDate = LocalDate.now().atStartOfDay();
1304
        LocalDate firstOfMonth = LocalDate.now().withDayOfMonth(1);
1305
        LocalDate endOfMonth = LocalDate.now().withDayOfMonth(LocalDate.now().lengthOfMonth()).plusDays(1);
1306
 
1307
        // Get fofo stores
1308
        Map<Integer, FofoStore> fofoStoresMap = new HashMap<>();
1309
        try {
1310
            fofoStoresMap = fofoStoreRepository.selectByRetailerIds(fofoIdList).stream()
1311
                    .collect(Collectors.toMap(FofoStore::getId, x -> x, (a, b) -> a));
1312
        } catch (ProfitMandiBusinessException e) {
1313
            LOGGER.error("Error fetching fofo stores", e);
1314
        }
1315
 
1316
        // For L1 RBMs, filter escalated partners
1317
        if (isL1) {
1318
            List<Integer> allRemarkIds = partnerCollectionRemarkRepository.selectMaxRemarkId(fofoIdList);
1319
            if (!allRemarkIds.isEmpty()) {
1320
                Map<Integer, PartnerCollectionRemark> partnerCollectionRemarks = partnerCollectionRemarkRepository.selectByIds(allRemarkIds).stream()
1321
                        .collect(Collectors.toMap(PartnerCollectionRemark::getFofoId, x -> x, (a, b) -> a));
1322
                fofoIdList = partnerCollectionRemarks.entrySet().stream()
1323
                        .filter(entry -> {
1324
                            PartnerCollectionRemark pcrMap = entry.getValue();
1325
                            return !(CollectionRemark.RBM_L2_ESCALATION.equals(pcrMap.getRemark())
1326
                                    || CollectionRemark.SALES_ESCALATION.equals(pcrMap.getRemark()));
1327
                        })
1328
                        .map(Map.Entry::getKey)
1329
                        .collect(Collectors.toList());
1330
            }
1331
        }
1332
 
1333
        // Collection rank map
1334
        Map<Integer, Integer> collectionRankMap = new HashMap<>();
1335
        try {
1336
            collectionRankMap = partnerCollectionService.getCollectionRankMap(fofoIdList, startDate);
1337
        } catch (ProfitMandiBusinessException e) {
1338
            LOGGER.error("Error fetching collection rank map", e);
1339
        }
1340
 
1341
        // MTD billing data
1342
        List<RbmWeeklyBillingModel> mtdBillingData = getWeeklyBillingDataForMonth(firstOfMonth, endOfMonth);
1343
        Set<Integer> mtdBilledFofoIds = mtdBillingData.stream()
1344
                .filter(RbmWeeklyBillingModel::isMtdBilled)
1345
                .map(RbmWeeklyBillingModel::getFofoId)
1346
                .collect(Collectors.toSet());
1347
 
1348
        // Filter to valid fofo IDs (external, ACTIVE, has collection plan)
1349
        Map<Integer, Integer> finalCollectionRankMap = collectionRankMap;
1350
        Map<Integer, FofoStore> finalFofoStoresMap = fofoStoresMap;
1351
        List<Integer> validFofoIds = fofoIdList.stream()
1352
                .filter(fofoId -> {
1353
                    FofoStore store = finalFofoStoresMap.get(fofoId);
1354
                    if (store == null || store.isInternal()) return false;
1355
                    if (!ActivationType.ACTIVE.equals(store.getActivationType())) return false;
1356
                    return finalCollectionRankMap.containsKey(fofoId);
1357
                })
1358
                .collect(Collectors.toList());
1359
 
1360
        // Resolve partner names/codes
1361
        Map<Integer, CustomRetailer> retailerMap = Collections.emptyMap();
1362
        if (!validFofoIds.isEmpty()) {
1363
            try {
1364
                retailerMap = retailerService.getFofoRetailers(validFofoIds);
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
1373
        for (Integer fofoId : validFofoIds) {
1374
            int rank = collectionRankMap.getOrDefault(fofoId, 5);
1375
            boolean hasZeroBilling = !mtdBilledFofoIds.contains(fofoId);
1376
 
1377
            String status;
1378
            if (rank == 1) {
1379
                status = "Plan Today";
1380
            } else if (rank == 2) {
1381
                status = "Carry Forward";
1382
            } else if (rank == 3) {
1383
                status = "Untouched";
1384
            } else if (hasZeroBilling) {
1385
                status = "Zero Billing";
1386
            } else if (rank == 4) {
1387
                status = "Future Plan";
1388
            } else {
1389
                status = "Normal";
1390
            }
1391
 
1392
            CustomRetailer retailer = retailerMap.get(fofoId);
1393
            String partnerName = retailer != null ? retailer.getBusinessName() : "Unknown (" + fofoId + ")";
1394
            String partnerCode = retailer != null ? retailer.getCode() : "-";
1395
 
1396
            rows.add(Arrays.asList(partnerName, partnerCode, status, rbmName));
1397
        }
1398
 
1399
        return rows;
1400
    }
1401
 
33917 ranu 1402
}