Subversion Repositories SmartDukaan

Rev

Rev 37225 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
34306 ranu 1
package com.smartdukaan.cron.scheduled;
2
 
3
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
34619 ranu 4
import com.spice.profitmandi.common.model.BrandStockPrice;
34606 ranu 5
import com.spice.profitmandi.common.model.CustomRetailer;
36973 ranu 6
import com.spice.profitmandi.common.model.IdAmountModel;
34321 ranu 7
import com.spice.profitmandi.common.model.ProfitMandiConstants;
34619 ranu 8
import com.spice.profitmandi.common.util.FormattingUtils;
9
import com.spice.profitmandi.common.util.Utils;
34450 ranu 10
import com.spice.profitmandi.dao.cart.SmartCartService;
34321 ranu 11
import com.spice.profitmandi.dao.entity.auth.AuthUser;
34758 ranu 12
import com.spice.profitmandi.dao.entity.catalog.TagListing;
34606 ranu 13
import com.spice.profitmandi.dao.entity.fofo.*;
14
import com.spice.profitmandi.dao.entity.logistics.AST;
15
import com.spice.profitmandi.dao.entity.logistics.ASTRepository;
34619 ranu 16
import com.spice.profitmandi.dao.entity.transaction.*;
34321 ranu 17
import com.spice.profitmandi.dao.entity.user.User;
18
import com.spice.profitmandi.dao.enumuration.cs.EscalationType;
34619 ranu 19
import com.spice.profitmandi.dao.enumuration.transaction.LoanReferenceType;
34641 ranu 20
import com.spice.profitmandi.dao.model.*;
34606 ranu 21
import com.spice.profitmandi.dao.repository.auth.AuthRepository;
34758 ranu 22
import com.spice.profitmandi.dao.repository.catalog.TagListingRepository;
34321 ranu 23
import com.spice.profitmandi.dao.repository.cs.CsService;
24
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
34606 ranu 25
import com.spice.profitmandi.dao.repository.fofo.*;
26
import com.spice.profitmandi.dao.repository.inventory.StateRepository;
34619 ranu 27
import com.spice.profitmandi.dao.repository.transaction.*;
34321 ranu 28
import com.spice.profitmandi.dao.repository.user.UserRepository;
35358 ranu 29
import com.spice.profitmandi.dao.service.solr.FofoSolr;
34655 ranu 30
import com.spice.profitmandi.service.PartnerStatsService;
34641 ranu 31
import com.spice.profitmandi.service.RbmTargetService;
34758 ranu 32
import com.spice.profitmandi.service.inventory.*;
34308 ranu 33
import com.spice.profitmandi.service.transaction.SDCreditService;
34606 ranu 34
import com.spice.profitmandi.service.user.RetailerService;
34619 ranu 35
import com.spice.profitmandi.service.wallet.WalletService;
36
import in.shop2020.model.v1.order.WalletReferenceType;
37
import org.apache.commons.io.output.ByteArrayOutputStream;
34306 ranu 38
import org.apache.logging.log4j.LogManager;
39
import org.apache.logging.log4j.Logger;
34715 ranu 40
import org.apache.poi.common.usermodel.HyperlinkType;
34641 ranu 41
import org.apache.poi.ss.usermodel.*;
34619 ranu 42
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
34306 ranu 43
import org.springframework.beans.factory.annotation.Autowired;
34619 ranu 44
import org.springframework.beans.factory.annotation.Qualifier;
45
import org.springframework.core.io.ByteArrayResource;
34321 ranu 46
import org.springframework.mail.javamail.JavaMailSender;
47
import org.springframework.mail.javamail.MimeMessageHelper;
34306 ranu 48
import org.springframework.stereotype.Component;
49
import org.springframework.transaction.annotation.Transactional;
50
 
34321 ranu 51
import javax.mail.MessagingException;
52
import javax.mail.internet.InternetAddress;
53
import javax.mail.internet.MimeMessage;
34619 ranu 54
import java.io.*;
55
import java.math.BigDecimal;
34641 ranu 56
import java.time.*;
34749 ranu 57
import java.time.format.DateTimeFormatter;
34306 ranu 58
import java.time.temporal.ChronoUnit;
36113 ranu 59
import java.time.temporal.TemporalAdjusters;
60
import java.time.DayOfWeek;
34306 ranu 61
import java.util.*;
34321 ranu 62
import java.util.stream.Collectors;
34306 ranu 63
 
34939 ranu 64
import static java.util.stream.Collectors.toList;
65
 
34306 ranu 66
@Component
67
@Transactional(rollbackFor = {Throwable.class, ProfitMandiBusinessException.class})
68
public class ScheduledTasksTest {
69
 
70
    private static final Logger LOGGER = LogManager.getLogger(ScheduledTasksTest.class);
71
 
72
    @Autowired
73
    TransactionRepository transactionRepository;
74
 
75
    @Autowired
36979 ranu 76
    @Qualifier(value = "googleMailSender")
34619 ranu 77
    private JavaMailSender googleMailSender;
78
 
79
    @Autowired
34306 ranu 80
    LoanRepository loanRepository;
81
 
34308 ranu 82
    @Autowired
83
    SDCreditService sdCreditService;
84
 
34321 ranu 85
    @Autowired
34939 ranu 86
    SmartCartSuggestionRepository smartCartSuggestionRepository;
87
 
88
    @Autowired
34321 ranu 89
    UserRepository userRepository;
90
 
91
    @Autowired
92
    CsService csService;
93
 
94
    @Autowired
95
    RbmRatingRepository rbmRatingRepository;
96
 
97
    @Autowired
36402 amit 98
    private JavaMailSender gmailRelaySender;
34321 ranu 99
 
100
    @Autowired
101
    SalesRatingRepository salesRatingRepository;
102
 
103
    @Autowired
104
    FofoStoreRepository fofoStoreRepository;
105
 
34450 ranu 106
    @Autowired
107
    SmartCartService smartCartService;
108
 
34606 ranu 109
    @Autowired
110
    RetailerService retailerService;
111
 
112
    @Autowired
113
    ASTRepository astRepository;
114
 
115
    @Autowired
116
    AuthRepository authRepository;
117
 
118
    @Autowired
119
    StateRepository stateRepository;
120
 
121
    @Autowired
122
    MonthlyTargetRepository monthlyTargetRepository;
123
 
124
    @Autowired
125
    PartnerTypeChangeService partnerTypeChangeService;
126
 
127
    @Autowired
128
    ReturnOrderInfoRepository returnOrderInfoRepository;
129
 
130
    @Autowired
131
    OrderRepository orderRepository;
132
 
34619 ranu 133
    @Autowired
134
    FofoOrderItemRepository fofoOrderItemRepository;
135
 
136
    @Autowired
137
    InventoryService inventoryService;
138
 
139
    @Autowired
140
    UserWalletRepository userWalletRepository;
141
 
142
    @Autowired
143
    LoanStatementRepository loanStatementRepository;
144
 
145
    @Autowired
34641 ranu 146
    ActivatedImeiRepository activatedImeiRepository;
147
 
148
    @Autowired
149
    PartnerDailyInvestmentRepository partnerDailyInvestmentRepository;
150
 
151
    @Autowired
34758 ranu 152
    SaholicInventoryService saholicInventoryService;
153
 
154
    @Autowired
34619 ranu 155
    WalletService walletService;
156
 
34641 ranu 157
    @Autowired
158
    RbmTargetService rbmTargetService;
159
 
34655 ranu 160
    @Autowired
161
    PartnerStatsService partnerStatsService;
162
 
34715 ranu 163
    @Autowired
34758 ranu 164
    AgeingService ageingService;
165
 
166
    @Autowired
167
    TagListingRepository tagListingRepository;
168
 
169
    @Autowired
34715 ranu 170
    UserWalletHistoryRepository userWalletHistoryRepository;
171
 
35358 ranu 172
    @Autowired
173
    FofoSolr fofoSolr;
174
 
34321 ranu 175
    public void test() throws Exception {
35358 ranu 176
 
177
        fofoSolr.populateTagItems();
178
 
34366 ranu 179
        System.out.println("test end");
34306 ranu 180
 
181
    }
182
 
34648 ranu 183
    public void generateBiReport() throws Exception {
34912 ranu 184
        this.generateBiReportExcel();
34648 ranu 185
    }
186
 
34308 ranu 187
    public void createLoanForBillingByTransactionIdAndInvoiceNumber(int transactionId, double invoiceAmount, String invoiceNumber) throws Exception {
188
        sdCreditService.createLoanForBilling(transactionId, invoiceAmount, invoiceNumber);
34306 ranu 189
 
34308 ranu 190
    }
34306 ranu 191
 
34619 ranu 192
    public void loanSettle() throws Exception {
193
        List<Integer> refrences = Arrays.asList(25807,36003,38938,39506,42219,45084);
194
        for(Integer ref : refrences){
195
            List<LoanStatement> loanStatements = loanStatementRepository.selectByLoanId(ref);
196
            double amountSum = loanStatements.stream().map(LoanStatement::getAmount).mapToDouble(BigDecimal::doubleValue).sum();
197
            if(amountSum > 0){
198
                walletService.addAmountToWallet(loanStatements.get(0).getFofoId(),ref, WalletReferenceType.CREDIT_LIMIT,"Amount reversal against credit limit deduction",(float) amountSum,LocalDateTime.now());
34308 ranu 199
 
34619 ranu 200
//                Loan statement entry
201
                    BigDecimal adjustAmount = BigDecimal.valueOf(amountSum).negate(); // or multiply by -1
202
                    LoanStatement loanStatement = new LoanStatement();
203
                    loanStatement.setAmount(adjustAmount);
204
                    loanStatement.setFofoId(loanStatements.get(0).getFofoId());
205
                    loanStatement.setLoanReferenceType(LoanReferenceType.PRINCIPAL);
206
                    loanStatement.setCreatedAt(LocalDateTime.now());
207
                    loanStatement.setDescription("Amount reversal due to access debit against limit");
208
                    loanStatement.setLoanId(ref);
209
                    loanStatement.setBusinessDate(LocalDateTime.now());
210
                    loanStatementRepository.persist(loanStatement);
211
 
212
                    Loan loan = loanRepository.selectByLoanId(ref);
213
                    loan.setPendingAmount(BigDecimal.valueOf(0));
214
                    loan.setSettledOn(LocalDateTime.now());
215
                }
216
 
217
 
218
        }
219
    }
220
 
221
 
222
 
34321 ranu 223
    private void sendMailHtmlFormat(String email[], String body, String cc[], String bcc[], String subject)
224
            throws MessagingException, ProfitMandiBusinessException, IOException {
36402 amit 225
        MimeMessage message = gmailRelaySender.createMimeMessage();
34321 ranu 226
        MimeMessageHelper helper = new MimeMessageHelper(message);
227
        helper.setSubject(subject);
228
        helper.setText(body, true);
229
        helper.setTo(email);
230
        if (cc != null) {
231
            helper.setCc(cc);
232
        }
233
        if (bcc != null) {
234
            helper.setBcc(bcc);
34308 ranu 235
 
34321 ranu 236
        }
237
 
238
        InternetAddress senderAddress = new InternetAddress("noreply@smartdukaan.com", "Smart Dukaan");
239
        helper.setFrom(senderAddress);
36402 amit 240
        gmailRelaySender.send(message);
34321 ranu 241
    }
242
 
34307 ranu 243
    public Map<Integer,Integer> findLoanTransactionMapingAccordingLoan(List<Integer> loanIds) throws ProfitMandiBusinessException {
34306 ranu 244
 
245
        Map<Integer, Integer> transactionLoanMap = new HashMap<>();
246
 
247
        for(int loanId : loanIds){
248
            Transaction transaction = null;
249
            Loan loan = loanRepository.selectByLoanId(loanId);
250
            List<Transaction> transactions = transactionRepository.selectByRetailerId(loan.getFofoId());
251
 
252
            LocalDateTime nearestDateTime = transactions.stream().map(x -> x.getCreateTimestamp())
253
                    .min(Comparator.comparingLong(x -> Math.abs(ChronoUnit.MILLIS.between(x, loan.getCreatedOn()))))
254
                    .orElse(null);
255
 
256
            if (nearestDateTime != null && loan.getCreatedOn().plusMinutes(2).isAfter(nearestDateTime) &&
257
                    loan.getCreatedOn().minusMinutes(1).isBefore(nearestDateTime)) {
258
                // Here transaction is still null
259
                transaction = transactions.stream()
260
                        .filter(x -> x.getCreateTimestamp().equals(nearestDateTime))
261
                        .findFirst().get();
262
                transactionLoanMap.put(transaction.getId(), loanId);
263
            }
264
 
265
        }
266
        LOGGER.info("transactionLoanMap {}",transactionLoanMap);
267
        return transactionLoanMap;
268
    }
34321 ranu 269
 
270
 
271
 
272
    public void sendRbmFeedbackSummaryEmail() throws MessagingException, ProfitMandiBusinessException, IOException {
36113 ranu 273
        // Weekly date range: Previous Monday to Sunday
274
        LocalDate today = LocalDate.now();
275
        LocalDate previousMonday = today.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));
276
        LocalDate previousSunday = previousMonday.plusDays(6);
277
        LocalDateTime startOfWeek = previousMonday.atStartOfDay();
278
        LocalDateTime endOfWeek = previousSunday.atTime(23, 59, 59);
279
 
34323 ranu 280
        String[] bcc = {"tarun.verma@smartdukaan.com"};
36113 ranu 281
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy");
34321 ranu 282
 
283
        // Get all RBM users
284
        List<AuthUser> authUsers = csService.getAuthUserIds(
285
                ProfitMandiConstants.TICKET_CATEGORY_RBM,
286
                Arrays.asList(EscalationType.L1)
287
        );
288
 
289
        if (authUsers.isEmpty()) {
290
            LOGGER.info("No RBMs found.");
291
            return;
292
        }
293
 
294
        List<Integer> rbmIds = authUsers.stream().map(AuthUser::getId).collect(Collectors.toList());
295
 
36113 ranu 296
        // Fetch ratings for all RBMs for the week
297
        List<RbmRating> feedbackList = rbmRatingRepository.selectByRbmIdsAndDateRange(rbmIds, startOfWeek, endOfWeek);
34321 ranu 298
 
299
        if (feedbackList.isEmpty()) {
36113 ranu 300
            LOGGER.info("No feedback entries found for RBMs for the week.");
34321 ranu 301
            return;
302
        }
303
 
304
        // Sort feedback by createTimeStamp DESC
305
        feedbackList.sort((a, b) -> b.getCreateTimeStamp().compareTo(a.getCreateTimeStamp()));
306
 
307
        // Fetch and map FOFO (partner) names
308
        Map<Integer, String> fofoNameMap = new HashMap<>();
309
        for (RbmRating rating : feedbackList) {
310
            int fofoId = rating.getFofoId();
311
            if (!fofoNameMap.containsKey(fofoId)) {
312
                User fofoUser = userRepository.selectById(fofoId);
313
                FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(fofoId);
314
 
315
                String partnerName = fofoUser != null ? fofoUser.getName() : "Unknown Partner";
316
                String storeCode = fofoStore != null ? fofoStore.getCode() : "Unknown Code";
317
 
318
                String displayName = partnerName + " (" + storeCode + ")";
319
                fofoNameMap.put(fofoId, displayName);
320
            }
321
        }
322
 
323
        // Map RBM ID to name for quick lookup
324
        Map<Integer, String> rbmNameMap = authUsers.stream()
325
                .collect(Collectors.toMap(AuthUser::getId, AuthUser::getFullName));
326
 
36113 ranu 327
        // Calculate RBM statistics: average rating and unique partner count
328
        Map<Integer, List<RbmRating>> feedbackByRbm = feedbackList.stream()
329
                .collect(Collectors.groupingBy(RbmRating::getRbmId));
330
 
331
        List<RbmWeeklyStats> rbmStatsList = new ArrayList<>();
332
        for (Map.Entry<Integer, List<RbmRating>> entry : feedbackByRbm.entrySet()) {
333
            int rbmId = entry.getKey();
334
            List<RbmRating> ratings = entry.getValue();
335
 
336
            double avgRating = ratings.stream()
337
                    .mapToInt(RbmRating::getRating)
338
                    .average()
339
                    .orElse(0.0);
340
 
341
            long uniquePartnerCount = ratings.stream()
342
                    .map(RbmRating::getFofoId)
343
                    .distinct()
344
                    .count();
345
 
346
            String rbmName = rbmNameMap.getOrDefault(rbmId, "Unknown RBM");
347
            rbmStatsList.add(new RbmWeeklyStats(rbmId, rbmName, avgRating, (int) uniquePartnerCount));
348
        }
349
 
350
        // Find max partner count for normalization
351
        int maxPartnerCount = rbmStatsList.stream()
352
                .mapToInt(RbmWeeklyStats::getPartnerCount)
353
                .max()
354
                .orElse(1);
355
 
356
        // Calculate combined score: 65% partner count + 35% rating
357
        // Score = (0.65 × partnerCount/maxPartnerCount) + (0.35 × avgRating/5)
358
        for (RbmWeeklyStats stats : rbmStatsList) {
359
            double partnerScore = (double) stats.getPartnerCount() / maxPartnerCount;
360
            double ratingScore = stats.getAvgRating() / 5.0;
361
            double combinedScore = (0.65 * partnerScore) + (0.35 * ratingScore);
362
            stats.setCombinedScore(combinedScore);
363
        }
364
 
365
        // Sort by combined score DESC and assign rank
366
        rbmStatsList.sort((a, b) -> Double.compare(b.getCombinedScore(), a.getCombinedScore()));
367
        for (int i = 0; i < rbmStatsList.size(); i++) {
368
            rbmStatsList.get(i).setRank(i + 1);
369
        }
370
 
371
        // Create map for quick lookup of stats by RBM ID
372
        Map<Integer, RbmWeeklyStats> rbmStatsMap = rbmStatsList.stream()
373
                .collect(Collectors.toMap(RbmWeeklyStats::getRbmId, s -> s));
374
 
34321 ranu 375
        // Generate HTML content
376
        StringBuilder emailContent = new StringBuilder();
377
        emailContent.append("<html><body>");
378
        emailContent.append("<p>Dear Team,</p>");
36113 ranu 379
        emailContent.append("<p>Here is the <b>Weekly RBM Rating and Feedback Summary</b> for the week: <b>")
380
                .append(previousMonday.format(dateFormatter))
381
                .append(" to ")
382
                .append(previousSunday.format(dateFormatter))
383
                .append("</b></p>");
34321 ranu 384
 
36113 ranu 385
        // RBM Ranking Summary Table
386
        emailContent.append("<h3>RBM Weekly Rankings</h3>");
387
        emailContent.append("<p style='font-size: 12px; color: #666;'>Rank = 65% Partner Count + 35% Avg Rating</p>");
388
        emailContent.append("<table border='1' cellspacing='0' cellpadding='5' style='border-collapse: collapse;'>");
389
        emailContent.append("<tr style='background-color: #4CAF50; color: white;'>")
390
                .append("<th>Rank</th>")
34321 ranu 391
                .append("<th>RBM Name</th>")
36113 ranu 392
                .append("<th>Partner Count</th>")
393
                .append("<th>Avg Rating</th>")
394
                .append("<th>Score</th>")
395
                .append("</tr>");
396
 
397
        // Already sorted by rank (combined score DESC)
398
        for (RbmWeeklyStats stats : rbmStatsList) {
399
            emailContent.append("<tr>")
400
                    .append("<td style='text-align: center; font-weight: bold;'>").append(stats.getRank()).append("</td>")
401
                    .append("<td>").append(stats.getRbmName()).append("</td>")
402
                    .append("<td style='text-align: center;'>").append(stats.getPartnerCount()).append("</td>")
403
                    .append("<td style='text-align: center;'>").append(String.format("%.2f", stats.getAvgRating())).append("</td>")
404
                    .append("<td style='text-align: center;'>").append(String.format("%.2f", stats.getCombinedScore())).append("</td>")
405
                    .append("</tr>");
406
        }
407
        emailContent.append("</table>");
408
 
409
        // Detailed Feedback Table
410
        emailContent.append("<br><h3>Detailed Feedback</h3>");
411
        emailContent.append("<table border='1' cellspacing='0' cellpadding='5' style='border-collapse: collapse;'>");
412
        emailContent.append("<tr style='background-color: #2196F3; color: white;'>")
413
                .append("<th>RBM Name</th>")
414
                .append("<th>Rank</th>")
34321 ranu 415
                .append("<th>Partner Name</th>")
416
                .append("<th>Rating</th>")
417
                .append("<th>Comment</th>")
418
                .append("<th>Date</th>")
419
                .append("</tr>");
420
 
421
        for (RbmRating rating : feedbackList) {
36113 ranu 422
            int rbmId = rating.getRbmId();
423
            String rbmName = rbmNameMap.getOrDefault(rbmId, "Unknown RBM");
34321 ranu 424
            String partnerName = fofoNameMap.getOrDefault(rating.getFofoId(), "Unknown Partner");
36113 ranu 425
            RbmWeeklyStats stats = rbmStatsMap.get(rbmId);
426
 
34321 ranu 427
            emailContent.append("<tr>")
428
                    .append("<td>").append(rbmName).append("</td>")
36113 ranu 429
                    .append("<td style='text-align: center; font-weight: bold;'>").append(stats != null ? stats.getRank() : "-").append("</td>")
34321 ranu 430
                    .append("<td>").append(partnerName).append("</td>")
36113 ranu 431
                    .append("<td style='text-align: center;'>").append(rating.getRating()).append("</td>")
34321 ranu 432
                    .append("<td>").append(rating.getComment() != null ? rating.getComment() : "-").append("</td>")
433
                    .append("<td>").append(rating.getCreateTimeStamp().toLocalDate()).append("</td>")
434
                    .append("</tr>");
435
        }
436
 
437
        emailContent.append("</table>");
438
        emailContent.append("<br><p>Regards,<br>Smart Dukaan Team</p>");
439
        emailContent.append("</body></html>");
440
 
36113 ranu 441
        String subject = "Weekly RBM Feedback Summary - " + previousMonday.format(dateFormatter) + " to " + previousSunday.format(dateFormatter);
34321 ranu 442
 
443
        List<String> sendTo = new ArrayList<>();
36113 ranu 444
        sendTo.add("sm@smartdukaan.com");
445
        sendTo.add("chiranjib.sarkar@smartdukaan.com");
446
        sendTo.add("kamini.sharma@smartdukaan.com");
34321 ranu 447
 
448
        String[] emailRecipients = sendTo.toArray(new String[0]);
449
 
450
        this.sendMailHtmlFormat(emailRecipients, emailContent.toString(), null, bcc, subject);
451
 
36113 ranu 452
        LOGGER.info("Weekly RBM feedback summary email sent for week: {} to {}", previousMonday, previousSunday);
34321 ranu 453
    }
454
 
36113 ranu 455
    // Inner class to hold RBM weekly statistics
456
    private static class RbmWeeklyStats {
457
        private final int rbmId;
458
        private final String rbmName;
459
        private final double avgRating;
460
        private final int partnerCount;
461
        private double combinedScore;
462
        private int rank;
34321 ranu 463
 
36113 ranu 464
        public RbmWeeklyStats(int rbmId, String rbmName, double avgRating, int partnerCount) {
465
            this.rbmId = rbmId;
466
            this.rbmName = rbmName;
467
            this.avgRating = avgRating;
468
            this.partnerCount = partnerCount;
469
        }
470
 
471
        public int getRbmId() { return rbmId; }
472
        public String getRbmName() { return rbmName; }
473
        public double getAvgRating() { return avgRating; }
474
        public int getPartnerCount() { return partnerCount; }
475
        public double getCombinedScore() { return combinedScore; }
476
        public void setCombinedScore(double combinedScore) { this.combinedScore = combinedScore; }
477
        public int getRank() { return rank; }
478
        public void setRank(int rank) { this.rank = rank; }
479
    }
480
 
481
 
34321 ranu 482
    public void sendSalesFeedbackSummaryEmail() throws MessagingException, ProfitMandiBusinessException, IOException {
36113 ranu 483
        // Weekly date range: Previous Monday to Sunday
484
        LocalDate today = LocalDate.now();
485
        LocalDate previousMonday = today.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));
486
        LocalDate previousSunday = previousMonday.plusDays(6);
487
        LocalDateTime startOfWeek = previousMonday.atStartOfDay();
488
        LocalDateTime endOfWeek = previousSunday.atTime(23, 59, 59);
489
 
34323 ranu 490
        String[] bcc = {"tarun.verma@smartdukaan.com"};
36113 ranu 491
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy");
34321 ranu 492
 
34903 ranu 493
        // Get all Sales users
34321 ranu 494
        List<AuthUser> authUsers = csService.getAuthUserIds(
495
                ProfitMandiConstants.TICKET_CATEGORY_SALES,
496
                Arrays.asList(EscalationType.L1)
497
        );
498
 
499
        if (authUsers.isEmpty()) {
500
            LOGGER.info("No sales person found.");
501
            return;
502
        }
503
 
504
        List<Integer> salesL1Ids = authUsers.stream().map(AuthUser::getId).collect(Collectors.toList());
505
 
36113 ranu 506
        // Fetch ratings for all Sales L1 for the week
507
        List<SalesRating> feedbackList = salesRatingRepository.selectBySalesL1IdsAndDateRange(salesL1Ids, startOfWeek, endOfWeek);
34321 ranu 508
 
509
        if (feedbackList.isEmpty()) {
36113 ranu 510
            LOGGER.info("No feedback entries found for Sales for the week.");
34321 ranu 511
            return;
512
        }
513
 
514
        // Sort feedback by createTimeStamp DESC
515
        feedbackList.sort((a, b) -> b.getCreateTimeStamp().compareTo(a.getCreateTimeStamp()));
516
 
517
        // Fetch and map FOFO (partner) names
518
        Map<Integer, String> fofoNameMap = new HashMap<>();
519
        for (SalesRating rating : feedbackList) {
520
            int fofoId = rating.getFofoId();
521
            if (!fofoNameMap.containsKey(fofoId)) {
522
                User fofoUser = userRepository.selectById(fofoId);
523
                FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(fofoId);
524
 
525
                String partnerName = fofoUser != null ? fofoUser.getName() : "Unknown Partner";
526
                String storeCode = fofoStore != null ? fofoStore.getCode() : "Unknown Code";
527
 
528
                String displayName = partnerName + " (" + storeCode + ")";
529
                fofoNameMap.put(fofoId, displayName);
530
            }
531
        }
532
 
36113 ranu 533
        // Map Sales L1 ID to name for quick lookup
34321 ranu 534
        Map<Integer, String> salesL1NameMap = authUsers.stream()
535
                .collect(Collectors.toMap(AuthUser::getId, AuthUser::getFullName));
536
 
36113 ranu 537
        // Calculate Sales L1 statistics: average rating and unique partner count
538
        Map<Integer, List<SalesRating>> feedbackBySales = feedbackList.stream()
539
                .collect(Collectors.groupingBy(SalesRating::getSalesL1Id));
540
 
541
        List<SalesWeeklyStats> salesStatsList = new ArrayList<>();
542
        for (Map.Entry<Integer, List<SalesRating>> entry : feedbackBySales.entrySet()) {
543
            int salesL1Id = entry.getKey();
544
            List<SalesRating> ratings = entry.getValue();
545
 
546
            double avgRating = ratings.stream()
547
                    .mapToInt(SalesRating::getRating)
548
                    .average()
549
                    .orElse(0.0);
550
 
551
            long uniquePartnerCount = ratings.stream()
552
                    .map(SalesRating::getFofoId)
553
                    .distinct()
554
                    .count();
555
 
556
            String salesL1Name = salesL1NameMap.getOrDefault(salesL1Id, "Unknown Sales Person");
557
            salesStatsList.add(new SalesWeeklyStats(salesL1Id, salesL1Name, avgRating, (int) uniquePartnerCount));
558
        }
559
 
560
        // Find max partner count for normalization
561
        int maxPartnerCount = salesStatsList.stream()
562
                .mapToInt(SalesWeeklyStats::getPartnerCount)
563
                .max()
564
                .orElse(1);
565
 
566
        // Calculate combined score: 65% partner count + 35% rating
567
        // Score = (0.65 × partnerCount/maxPartnerCount) + (0.35 × avgRating/5)
568
        for (SalesWeeklyStats stats : salesStatsList) {
569
            double partnerScore = (double) stats.getPartnerCount() / maxPartnerCount;
570
            double ratingScore = stats.getAvgRating() / 5.0;
571
            double combinedScore = (0.65 * partnerScore) + (0.35 * ratingScore);
572
            stats.setCombinedScore(combinedScore);
573
        }
574
 
575
        // Sort by combined score DESC and assign rank
576
        salesStatsList.sort((a, b) -> Double.compare(b.getCombinedScore(), a.getCombinedScore()));
577
        for (int i = 0; i < salesStatsList.size(); i++) {
578
            salesStatsList.get(i).setRank(i + 1);
579
        }
580
 
581
        // Create map for quick lookup of stats by Sales L1 ID
582
        Map<Integer, SalesWeeklyStats> salesStatsMap = salesStatsList.stream()
583
                .collect(Collectors.toMap(SalesWeeklyStats::getSalesL1Id, s -> s));
584
 
34321 ranu 585
        // Generate HTML content
586
        StringBuilder emailContent = new StringBuilder();
587
        emailContent.append("<html><body>");
588
        emailContent.append("<p>Dear Team,</p>");
36113 ranu 589
        emailContent.append("<p>Here is the <b>Weekly Sales L1 Rating and Feedback Summary</b> for the week: <b>")
590
                .append(previousMonday.format(dateFormatter))
591
                .append(" to ")
592
                .append(previousSunday.format(dateFormatter))
593
                .append("</b></p>");
34321 ranu 594
 
36113 ranu 595
        // Sales L1 Ranking Summary Table
596
        emailContent.append("<h3>Sales L1 Weekly Rankings</h3>");
597
        emailContent.append("<p style='font-size: 12px; color: #666;'>Rank = 65% Partner Count + 35% Avg Rating</p>");
598
        emailContent.append("<table border='1' cellspacing='0' cellpadding='5' style='border-collapse: collapse;'>");
599
        emailContent.append("<tr style='background-color: #4CAF50; color: white;'>")
600
                .append("<th>Rank</th>")
34321 ranu 601
                .append("<th>Sales L1 Name</th>")
36113 ranu 602
                .append("<th>Partner Count</th>")
603
                .append("<th>Avg Rating</th>")
604
                .append("<th>Score</th>")
605
                .append("</tr>");
606
 
607
        // Already sorted by rank (combined score DESC)
608
        for (SalesWeeklyStats stats : salesStatsList) {
609
            emailContent.append("<tr>")
610
                    .append("<td style='text-align: center; font-weight: bold;'>").append(stats.getRank()).append("</td>")
611
                    .append("<td>").append(stats.getSalesL1Name()).append("</td>")
612
                    .append("<td style='text-align: center;'>").append(stats.getPartnerCount()).append("</td>")
613
                    .append("<td style='text-align: center;'>").append(String.format("%.2f", stats.getAvgRating())).append("</td>")
614
                    .append("<td style='text-align: center;'>").append(String.format("%.2f", stats.getCombinedScore())).append("</td>")
615
                    .append("</tr>");
616
        }
617
        emailContent.append("</table>");
618
 
619
        // Detailed Feedback Table
620
        emailContent.append("<br><h3>Detailed Feedback</h3>");
621
        emailContent.append("<table border='1' cellspacing='0' cellpadding='5' style='border-collapse: collapse;'>");
622
        emailContent.append("<tr style='background-color: #2196F3; color: white;'>")
623
                .append("<th>Sales L1 Name</th>")
624
                .append("<th>Rank</th>")
34321 ranu 625
                .append("<th>Partner Name</th>")
34411 tejus.loha 626
                .append("<th>Partner Category</th>")
34321 ranu 627
                .append("<th>Rating</th>")
628
                .append("<th>Comment</th>")
629
                .append("<th>Date</th>")
630
                .append("</tr>");
631
 
632
        for (SalesRating rating : feedbackList) {
36113 ranu 633
            int salesL1Id = rating.getSalesL1Id();
634
            String salesL1Name = salesL1NameMap.getOrDefault(salesL1Id, "Unknown Sales Person");
34321 ranu 635
            String partnerName = fofoNameMap.getOrDefault(rating.getFofoId(), "Unknown Partner");
36113 ranu 636
            SalesWeeklyStats stats = salesStatsMap.get(salesL1Id);
34411 tejus.loha 637
            PartnerType partnerType = partnerTypeChangeService.getTypeOnDate(rating.getFofoId(), LocalDate.now());
36113 ranu 638
 
34321 ranu 639
            emailContent.append("<tr>")
36113 ranu 640
                    .append("<td>").append(salesL1Name).append("</td>")
641
                    .append("<td style='text-align: center; font-weight: bold;'>").append(stats != null ? stats.getRank() : "-").append("</td>")
34321 ranu 642
                    .append("<td>").append(partnerName).append("</td>")
34411 tejus.loha 643
                    .append("<td>").append(partnerType).append("</td>")
36113 ranu 644
                    .append("<td style='text-align: center;'>").append(rating.getRating()).append("</td>")
34321 ranu 645
                    .append("<td>").append(rating.getComment() != null ? rating.getComment() : "-").append("</td>")
646
                    .append("<td>").append(rating.getCreateTimeStamp().toLocalDate()).append("</td>")
647
                    .append("</tr>");
648
        }
649
 
650
        emailContent.append("</table>");
36113 ranu 651
        emailContent.append("<br><p>Regards,<br>Smart Dukaan Team</p>");
34321 ranu 652
        emailContent.append("</body></html>");
653
 
36113 ranu 654
        String subject = "Weekly Sales L1 Feedback Summary - " + previousMonday.format(dateFormatter) + " to " + previousSunday.format(dateFormatter);
34321 ranu 655
 
656
        List<String> sendTo = new ArrayList<>();
36113 ranu 657
        sendTo.add("sm@smartdukaan.com");
658
        sendTo.add("kamini.sharma@smartdukaan.com");
34321 ranu 659
 
660
        String[] emailRecipients = sendTo.toArray(new String[0]);
661
 
662
        this.sendMailHtmlFormat(emailRecipients, emailContent.toString(), null, bcc, subject);
663
 
36113 ranu 664
        LOGGER.info("Weekly Sales L1 feedback summary email sent for week: {} to {}", previousMonday, previousSunday);
34321 ranu 665
    }
666
 
36113 ranu 667
    // Inner class to hold Sales L1 weekly statistics
668
    private static class SalesWeeklyStats {
669
        private final int salesL1Id;
670
        private final String salesL1Name;
671
        private final double avgRating;
672
        private final int partnerCount;
673
        private double combinedScore;
674
        private int rank;
675
 
676
        public SalesWeeklyStats(int salesL1Id, String salesL1Name, double avgRating, int partnerCount) {
677
            this.salesL1Id = salesL1Id;
678
            this.salesL1Name = salesL1Name;
679
            this.avgRating = avgRating;
680
            this.partnerCount = partnerCount;
681
        }
682
 
683
        public int getSalesL1Id() { return salesL1Id; }
684
        public String getSalesL1Name() { return salesL1Name; }
685
        public double getAvgRating() { return avgRating; }
686
        public int getPartnerCount() { return partnerCount; }
687
        public double getCombinedScore() { return combinedScore; }
688
        public void setCombinedScore(double combinedScore) { this.combinedScore = combinedScore; }
689
        public int getRank() { return rank; }
690
        public void setRank(int rank) { this.rank = rank; }
691
    }
692
 
34912 ranu 693
    public Map<String, Set<Integer>> generateBiReportHierarchyWise() throws Exception{
694
        List<Integer> categoryIds = Arrays.asList(ProfitMandiConstants.TICKET_CATEGORY_RBM, ProfitMandiConstants.TICKET_CATEGORY_SALES,ProfitMandiConstants.TICKET_CATEGORY_ABM,ProfitMandiConstants.TICKET_CATEGORY_BUSINESSINTELLIGENT);
695
        Map<String, Set<Integer>> storeGuyEntry = csService.getAuthUserPartnerIdMappingByCategoryIds(categoryIds, false);
696
        return storeGuyEntry;
34911 ranu 697
    }
698
 
699
 
700
 
34912 ranu 701
    public void generateBiReportExcel() throws Exception {
34911 ranu 702
 
36975 ranu 703
        long __biReportStartMs = System.currentTimeMillis();
704
        LOGGER.info("[BI_REPORT] START batch-optimized generateBiReportExcel at {}", LocalDateTime.now());
705
 
34741 ranu 706
        LocalDateTime startOfToday;
707
        LocalDateTime previousDay;
34321 ranu 708
 
34741 ranu 709
        if (LocalDate.now().getDayOfMonth() == 1) {
710
            startOfToday = LocalDate.now().minusDays(1).atStartOfDay();
711
            previousDay = startOfToday.with(LocalTime.MAX);
712
        } else {
713
            startOfToday = LocalDate.now().atStartOfDay();
714
            previousDay = startOfToday.with(LocalTime.MAX).minusDays(1);
715
        }
716
 
35239 ranu 717
        Map<Integer, CustomRetailer> customRetailers = retailerService.getAllFofoRetailersInternalFalse();
34912 ranu 718
 
35239 ranu 719
        List<Integer> retailerIds = customRetailers.values().stream()
34903 ranu 720
                .filter(retailer -> {
36973 ranu 721
                    String storeCode = retailer.getCode();
34903 ranu 722
                    return !storeCode.equalsIgnoreCase("UPGBN640") && !storeCode.equalsIgnoreCase("HRYN039");
723
                })
724
                .map(CustomRetailer::getPartnerId)
35239 ranu 725
                .collect(Collectors.toList());
34606 ranu 726
 
36973 ranu 727
        Set<Integer> retailerIdSet = new HashSet<>(retailerIds);
34903 ranu 728
 
34641 ranu 729
        //partner daily investment
34729 amit.gupta 730
        List<Loan> defaultLoans = sdCreditService.getDefaultLoans();
34641 ranu 731
        Map<Integer,List<Loan>> defaultLoanMap = defaultLoans.stream().collect(Collectors.groupingBy(Loan::getFofoId));
34619 ranu 732
 
34641 ranu 733
        Map<Integer, PartnerDailyInvestment> partnerDailyInvestmentMap = new HashMap<>();
734
        List<PartnerDailyInvestment> partnerDailyInvestments = partnerDailyInvestmentRepository
34741 ranu 735
                .selectAll(new ArrayList<>(retailerIds), previousDay.toLocalDate());
34641 ranu 736
        if (!partnerDailyInvestments.isEmpty()) {
737
            partnerDailyInvestmentMap = partnerDailyInvestments.stream()
36973 ranu 738
                    .collect(Collectors.toMap(PartnerDailyInvestment::getFofoId, x -> x));
34641 ranu 739
        }
740
 
34741 ranu 741
        YearMonth currentMonth;
742
        LocalDateTime currentMonthStartDate;
743
        LocalDateTime currentMonthEndDate;
744
 
745
        if (LocalDate.now().getDayOfMonth() == 1) {
746
            currentMonth = YearMonth.now().minusMonths(1);
747
            currentMonthStartDate = currentMonth.atDay(1).atStartOfDay();
748
            currentMonthEndDate = currentMonth.atEndOfMonth().atTime(23, 59, 59);
749
        } else {
750
            currentMonth = YearMonth.now();
751
            currentMonthStartDate = currentMonth.atDay(1).atStartOfDay();
752
            currentMonthEndDate = LocalDate.now().minusDays(1).atTime(23, 59, 59);
753
        }
754
 
34619 ranu 755
        String currentMonthStringValue = String.valueOf(currentMonth);
34606 ranu 756
 
36973 ranu 757
        YearMonth lastMonth = currentMonth.minusMonths(1);
758
        String lastMonthStringValue = String.valueOf(lastMonth);
759
        LocalDateTime lastMontStartDate = lastMonth.atDay(1).atStartOfDay();
760
        LocalDateTime lastMonthEndDate = lastMonth.atEndOfMonth().atTime(23, 59, 59);
34606 ranu 761
 
36973 ranu 762
        YearMonth twoMonthsAgo = currentMonth.minusMonths(2);
763
        String twoMonthAgoStringValue = String.valueOf(twoMonthsAgo);
764
        LocalDateTime twoMonthsAgoStartDate = twoMonthsAgo.atDay(1).atStartOfDay();
765
        LocalDateTime twoMonthsAgoEndDate = twoMonthsAgo.atEndOfMonth().atTime(23, 59, 59);
34606 ranu 766
 
36973 ranu 767
        LocalDateTime dayBeforeStart = previousDay.toLocalDate().atStartOfDay().minusDays(1);
768
        LocalDateTime dayBeforeEnd = previousDay.minusDays(1);
769
        LocalDateTime yesterdayStart = previousDay.toLocalDate().atStartOfDay();
34749 ranu 770
 
36973 ranu 771
        // Returns / RTO — already batched
772
        Map<Integer, Long> currentMonthPartnerReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(currentMonthStartDate, currentMonthEndDate)
773
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
774
        Map<Integer, Long> currentMonthRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(currentMonthStartDate, currentMonthEndDate)
775
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
776
        Map<Integer, Long> yesterdayReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(yesterdayStart, previousDay)
777
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
778
        Map<Integer, Long> yesterdayRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(yesterdayStart, previousDay)
779
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
780
        Map<Integer, Long> dayBeforeYesterdayReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(dayBeforeStart, dayBeforeEnd)
781
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
782
        Map<Integer, Long> dayBeforeYesterdayRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(dayBeforeStart, dayBeforeEnd)
783
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
784
        Map<Integer, Long> lastMonthPartnerReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(lastMontStartDate, lastMonthEndDate)
785
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
786
        Map<Integer, Long> lastMonthRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(lastMontStartDate, lastMonthEndDate)
787
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
788
        Map<Integer, Long> twoMonthAgoPartnerReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(twoMonthsAgoStartDate, twoMonthsAgoEndDate)
789
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
790
        Map<Integer, Long> twoMonthAgoRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(twoMonthsAgoStartDate, twoMonthsAgoEndDate)
791
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
34749 ranu 792
 
36973 ranu 793
        // ---- Batch pre-fetch everything else that was previously per-fofo ----
34749 ranu 794
 
37225 ranu 795
        // Auth hierarchy:
796
        //   RBM  = TICKET_CATEGORY_RBM   → L1, fallback L2
37233 ranu 797
        //   BM   = TICKET_CATEGORY_SALES → L4
798
        //   Sales Manager = TICKET_CATEGORY_SALES → L1, fallback L2, L3, L4, L5
37225 ranu 799
        //   ABM  = removed from report
36973 ranu 800
        Map<Integer, Integer> rbmL1Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_RBM, EscalationType.L1, retailerIdSet);
801
        Map<Integer, Integer> rbmL2Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_RBM, EscalationType.L2, retailerIdSet);
37225 ranu 802
        Map<Integer, Integer> bmMap = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L4, retailerIdSet);
803
        Map<Integer, Integer> salesManagerL1Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L1, retailerIdSet);
804
        Map<Integer, Integer> salesManagerL2Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L2, retailerIdSet);
805
        Map<Integer, Integer> salesManagerL3Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L3, retailerIdSet);
37233 ranu 806
        Map<Integer, Integer> salesManagerL4Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L4, retailerIdSet);
807
        Map<Integer, Integer> salesManagerL5Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L5, retailerIdSet);
34749 ranu 808
 
36973 ranu 809
        Set<Integer> allAuthUserIds = new HashSet<>();
37233 ranu 810
        for (Map<Integer, Integer> m : Arrays.asList(rbmL1Map, rbmL2Map, bmMap, salesManagerL1Map, salesManagerL2Map, salesManagerL3Map, salesManagerL4Map, salesManagerL5Map)) {
36973 ranu 811
            m.values().stream().filter(v -> v != null && v != 0).forEach(allAuthUserIds::add);
812
        }
813
        Map<Integer, AuthUser> authUserMap = allAuthUserIds.isEmpty() ? new HashMap<>()
814
                : authRepository.selectByIds(new ArrayList<>(allAuthUserIds)).stream()
815
                    .collect(Collectors.toMap(AuthUser::getId, u -> u, (a, b) -> a));
34749 ranu 816
 
36973 ranu 817
        // AST batch
818
        Set<Integer> astIds = customRetailers.values().stream()
819
                .map(CustomRetailer::getAstId).filter(id -> id != null && id != 0).collect(Collectors.toSet());
820
        Map<Integer, AST> astMap = astIds.isEmpty() ? new HashMap<>()
821
                : astRepository.selectByIds(new ArrayList<>(astIds)).stream()
822
                    .collect(Collectors.toMap(AST::getId, a -> a, (a, b) -> a));
34749 ranu 823
 
36973 ranu 824
        // FofoStore batch (replaces the two duplicate per-fofo calls)
825
        Map<Integer, FofoStore> fofoStoreMap = fofoStoreRepository.selectByRetailerIds(retailerIds).stream()
826
                .collect(Collectors.toMap(FofoStore::getId, s -> s, (a, b) -> a));
34749 ranu 827
 
36973 ranu 828
        // Users + wallet-creation dates
829
        Map<Integer, User> userMap = userRepository.selectByIds(retailerIds).stream()
830
                .collect(Collectors.toMap(User::getId, u -> u, (a, b) -> a));
831
        Map<Integer, LocalDateTime> walletFirstCreatedMap = userWalletHistoryRepository.selectFirstCreatedDatesForFofoIds(retailerIds);
34606 ranu 832
 
36973 ranu 833
        // Partner type for today
834
        Map<Integer, PartnerType> partnerTypeMap = partnerTypeChangeService.getTypesForFofoIds(retailerIds, LocalDate.now());
34606 ranu 835
 
36973 ranu 836
        // Monthly targets
36980 ranu 837
        Map<Integer, Double> currentTargetMap = monthlyTargetRepository.selectByExactDateAndFofoIds(currentMonth, retailerIds).stream()
36973 ranu 838
                .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
36980 ranu 839
        Map<Integer, Double> lastTargetMap = monthlyTargetRepository.selectByExactDateAndFofoIds(lastMonth, retailerIds).stream()
36973 ranu 840
                .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
36980 ranu 841
        Map<Integer, Double> twoMonthAgoTargetMap = monthlyTargetRepository.selectByExactDateAndFofoIds(twoMonthsAgo, retailerIds).stream()
36973 ranu 842
                .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
34606 ranu 843
 
36973 ranu 844
        // DRR target: RbmTargetService.calculateFofoIdTodayTarget always uses YearMonth.now() internally, regardless of
845
        // the date arg. On the 1st this differs from currentMonth (previous month) — preserve original semantics.
846
        Map<Integer, Double> drrTargetMap = YearMonth.now().equals(currentMonth) ? currentTargetMap
36980 ranu 847
                : monthlyTargetRepository.selectByExactDateAndFofoIds(YearMonth.now(), retailerIds).stream()
36973 ranu 848
                    .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
34606 ranu 849
 
36973 ranu 850
        // Monthly secondary (order value) — batched
851
        Map<Integer, Double> mtdSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
852
                startOfToday.withDayOfMonth(1), previousDay).stream()
853
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
854
        Map<Integer, Double> yesterDaySecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
855
                yesterdayStart, previousDay).stream()
856
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
857
        Map<Integer, Double> dayBeforeSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
858
                dayBeforeStart, dayBeforeEnd).stream()
859
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
860
        Map<Integer, Double> lastMonthSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
861
                lastMontStartDate, lastMonthEndDate).stream()
862
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
863
        Map<Integer, Double> twoMonthAgoSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
864
                twoMonthsAgoStartDate, twoMonthsAgoEndDate).stream()
865
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
34606 ranu 866
 
36973 ranu 867
        // Tertiary MTD / last-month / two-months-ago (all-partners scan via fofoId=0)
868
        LocalDateTime now = LocalDateTime.now();
869
        Map<Integer, Double> mtdSaleTillYesterdayMap = fofoOrderItemRepository.selectSumMopGroupByRetailer(startOfToday.withDayOfMonth(1), previousDay, 0, false);
870
        Map<Integer, Double> lastMonthSaleMap = fofoOrderItemRepository.selectSumMopGroupByRetailer(lastMontStartDate, lastMonthEndDate, 0, false);
871
        Map<Integer, Double> twoMonthAgoSaleMap = fofoOrderItemRepository.selectSumMopGroupByRetailer(twoMonthsAgoStartDate, twoMonthsAgoEndDate, 0, false);
34749 ranu 872
 
36973 ranu 873
        // Monthly activated-but-not-billed
874
        Map<Integer, Map<YearMonth, PartnerWiseActivatedNotBilledTotal>> activatedNotBilledByFofo = new HashMap<>();
875
        for (PartnerWiseActivatedNotBilledTotal t : activatedImeiRepository.getTotalMonthlyActivatedNotBilledForFofoIds(retailerIds, twoMonthsAgoStartDate)) {
876
            activatedNotBilledByFofo.computeIfAbsent(t.getFofoId(), k -> new HashMap<>())
877
                    .put(YearMonth.parse(t.getYearMonth()), t);
878
        }
34606 ranu 879
 
36973 ranu 880
        // Brand-wise tertiary MTD (per fofo → brand → amount)
881
        Map<Integer, Map<String, Double>> brandTertiaryByFofo = fofoOrderItemRepository.selectSumAmountGroupByBrandForFofoIds(currentMonthStartDate, currentMonthEndDate, retailerIds);
34749 ranu 882
 
36973 ranu 883
        // Brand-wise secondary billed (MTD)
884
        Map<Integer, Map<String, Long>> brandBilledByFofo = new HashMap<>();
885
        for (BrandWiseModel m : orderRepository.selectAllBilledByCategoryOrderGroupByBrandFofoIds(retailerIds, currentMonthStartDate, currentMonthEndDate, Arrays.asList(10006, 10001))) {
886
            brandBilledByFofo.computeIfAbsent(m.getFofoId(), k -> new HashMap<>()).merge(m.getBrand(), m.getAmount(), Long::sum);
887
        }
888
        // Brand-wise returns + RTO returns (MTD)
889
        Map<Integer, Map<String, Double>> brandReturnByFofo = new HashMap<>();
890
        for (BrandWiseReturnInfo r : returnOrderInfoRepository.selectAllBrandWiseByBetweenDateForFofoIds(currentMonthStartDate, currentMonthEndDate.plusDays(1), retailerIds)) {
891
            brandReturnByFofo.computeIfAbsent(r.getRetailerId(), k -> new HashMap<>()).merge(r.getBrand(), r.getReturnAmount(), Double::sum);
892
        }
893
        Map<Integer, Map<String, Double>> brandRtoReturnByFofo = new HashMap<>();
894
        for (BrandWiseReturnInfo r : returnOrderInfoRepository.selectAllBrandWiseRTORefundByBetweenDateForFofoIds(currentMonthStartDate, currentMonthEndDate.plusDays(1), retailerIds)) {
895
            brandRtoReturnByFofo.computeIfAbsent(r.getRetailerId(), k -> new HashMap<>()).merge(r.getBrand(), r.getReturnAmount(), Double::sum);
896
        }
897
 
898
        // Active loans grouped per fofo, plus batched loanId → sum.
899
        // The single-fofo selectAllActiveLoan(fofoId) additionally filters pendingAmount > 0 — must preserve.
900
        Map<Integer, List<Loan>> activeLoansByFofo = loanRepository.selectAllActiveLoan().stream()
901
                .filter(l -> retailerIdSet.contains(l.getFofoId())
902
                        && l.getPendingAmount() != null
903
                        && l.getPendingAmount().doubleValue() > 0)
904
                .collect(Collectors.groupingBy(Loan::getFofoId));
905
        Set<Integer> loanIdsForSum = new HashSet<>();
906
        activeLoansByFofo.values().forEach(list -> list.forEach(l -> loanIdsForSum.add(l.getId())));
907
        defaultLoans.forEach(l -> loanIdsForSum.add(l.getId()));
908
        Map<Integer, Double> loanStatementSumByLoanId = loanStatementRepository.sumAmountByLoanIds(new ArrayList<>(loanIdsForSum));
909
 
910
        // Last order per fofo (batched)
911
        Map<Integer, Integer> lastOrderIdByFofo = orderRepository.getLastOrderByFofoIds(retailerIds);
912
        Set<Integer> lastOrderIds = lastOrderIdByFofo.values().stream().filter(id -> id != null && id != 0).collect(Collectors.toSet());
913
        Map<Integer, Order> lastOrderById = new HashMap<>();
914
        for (Integer oid : lastOrderIds) {
915
            Order o = orderRepository.selectById(oid);
916
            if (o != null) lastOrderById.put(oid, o);
917
        }
918
 
919
        // DRR precomputation — the two dates we ever call with
920
        long day1RemainingDays = rbmTargetService.getRemainingDaysInMonth(currentMonth.atDay(1));
921
        long todayRemainingDays = rbmTargetService.getRemainingDaysInMonth(startOfToday.toLocalDate());
922
 
36975 ranu 923
        LOGGER.info("[BI_REPORT] batch pre-fetch complete in {}ms; retailers={}, entering per-fofo loop",
924
                System.currentTimeMillis() - __biReportStartMs, retailerIds.size());
925
        long __biReportLoopStartMs = System.currentTimeMillis();
926
 
34648 ranu 927
        Map<Integer , String> assessmentMap = new HashMap<>();
928
        Map<Integer , String> zeroBillingMap = new HashMap<>();
929
        Map<Integer , Float> billingNeededMap = new HashMap<>();
930
        Map<Integer , Integer> countAMap = new HashMap<>();
34619 ranu 931
        Map<Integer , BIRetailerModel> biRetailerModelMap = new HashMap<>();
34641 ranu 932
        Map<Integer , FofoInvestmentModel> biInvestmentModelMap = new HashMap<>();
34619 ranu 933
        Map<Integer, Map<YearMonth, BiSecondaryModel>> allRetailerMonthlyData = new HashMap<>();
934
        Map<Integer,Double> fofoTotalStockPriceMap = new HashMap<>();
935
        Map<Integer,Map<String, BrandStockPrice>> fofoBrandStockPriceMap = new HashMap<>();
34641 ranu 936
        Map<Integer,Long> fofoTotalMtdSecondaryMap = new HashMap<>();
34749 ranu 937
        Map<Integer,Long> fofoYesterdaySecondaryMap = new HashMap<>();
938
        Map<Integer,Long> fofoDayBeforeYesterdaySecondaryMap = new HashMap<>();
34641 ranu 939
        Map<Integer,Map<String, Long>> fofoBrandWiseMtdSecondaryMap = new HashMap<>();
940
        Map<Integer,Double> fofoTotalMtdTertiaryMap = new HashMap<>();
941
        Map<Integer,Map<String, Double>> fofoBrandMtdTertiaryMap = new HashMap<>();
942
 
36973 ranu 943
        for (Integer fofoId : retailerIds) {
944
            // resolve auth-user names from batched maps
34619 ranu 945
            String rbmName = "";
36973 ranu 946
            int rbmL1 = rbmL1Map.getOrDefault(fofoId, 0);
947
            if (rbmL1 != 0 && authUserMap.get(rbmL1) != null) {
948
                rbmName = authUserMap.get(rbmL1).getFullName();
949
            } else {
950
                int rbmL2 = rbmL2Map.getOrDefault(fofoId, 0);
951
                if (rbmL2 != 0 && authUserMap.get(rbmL2) != null) {
952
                    rbmName = authUserMap.get(rbmL2).getFullName();
34677 ranu 953
                }
34619 ranu 954
            }
36973 ranu 955
            String bmName = "";
956
            int bmId = bmMap.getOrDefault(fofoId, 0);
957
            if (bmId != 0 && authUserMap.get(bmId) != null) {
958
                bmName = authUserMap.get(bmId).getFullName();
34619 ranu 959
            }
37233 ranu 960
            // Sales Manager: L1 → L2 → L3 → L4 → L5 fallback in SALES category
37225 ranu 961
            String managerName = "";
962
            int managerL1 = salesManagerL1Map.getOrDefault(fofoId, 0);
963
            int managerL2 = salesManagerL2Map.getOrDefault(fofoId, 0);
964
            int managerL3 = salesManagerL3Map.getOrDefault(fofoId, 0);
37233 ranu 965
            int managerL4 = salesManagerL4Map.getOrDefault(fofoId, 0);
966
            int managerL5 = salesManagerL5Map.getOrDefault(fofoId, 0);
37225 ranu 967
            if (managerL1 != 0 && authUserMap.get(managerL1) != null) {
968
                managerName = authUserMap.get(managerL1).getFullName();
969
            } else if (managerL2 != 0 && authUserMap.get(managerL2) != null) {
970
                managerName = authUserMap.get(managerL2).getFullName();
971
            } else if (managerL3 != 0 && authUserMap.get(managerL3) != null) {
972
                managerName = authUserMap.get(managerL3).getFullName();
37233 ranu 973
            } else if (managerL4 != 0 && authUserMap.get(managerL4) != null) {
974
                managerName = authUserMap.get(managerL4).getFullName();
975
            } else if (managerL5 != 0 && authUserMap.get(managerL5) != null) {
976
                managerName = authUserMap.get(managerL5).getFullName();
34606 ranu 977
            }
978
 
36973 ranu 979
            AST ast = astMap.get(customRetailers.get(fofoId).getAstId());
980
            PartnerType partnerTypeThisMonth = partnerTypeMap.get(fofoId);
34606 ranu 981
 
982
            BIRetailerModel biRetailerModel = new BIRetailerModel();
983
            biRetailerModel.setBmName(bmName);
984
            biRetailerModel.setCode(customRetailers.get(fofoId).getCode());
36973 ranu 985
            biRetailerModel.setArea(ast != null ? ast.getArea() : "-");
986
 
987
            FofoStore fofoStore = fofoStoreMap.get(fofoId);
988
            String retailerStatus;
989
            if (fofoStore == null) {
990
                retailerStatus = "-";
991
            } else if (!fofoStore.isActive()) {
34738 ranu 992
                retailerStatus = "INACTIVE";
36973 ranu 993
            } else {
994
                retailerStatus = String.valueOf(fofoStore.getActivationType());
34738 ranu 995
            }
34606 ranu 996
            biRetailerModel.setCity(customRetailers.get(fofoId).getAddress().getCity());
997
            biRetailerModel.setStoreName(customRetailers.get(fofoId).getBusinessName());
34738 ranu 998
            biRetailerModel.setStatus(retailerStatus);
34606 ranu 999
            biRetailerModel.setCategory(String.valueOf(partnerTypeThisMonth));
1000
            biRetailerModel.setSalesManager(managerName);
1001
            biRetailerModel.setRbm(rbmName);
36973 ranu 1002
            biRetailerModelMap.put(fofoId, biRetailerModel);
34606 ranu 1003
 
36973 ranu 1004
            Map<YearMonth, PartnerWiseActivatedNotBilledTotal> partnerWiseActivatedNotBilledTotalMap =
1005
                    activatedNotBilledByFofo.getOrDefault(fofoId, new HashMap<>());
34619 ranu 1006
 
36973 ranu 1007
            // Current-month secondary
1008
            double currentSecondaryTarget = currentTargetMap.getOrDefault(fofoId, 0d);
1009
            long currentMonthReturn = currentMonthPartnerReturnOrderInfoModelMap.getOrDefault(fofoId, 0L)
1010
                    + currentMonthRtoRefundOrderMap.getOrDefault(fofoId, 0L);
34641 ranu 1011
 
36973 ranu 1012
            double dayBeforeYesterdayAfterReturnSecondary = dayBeforeSecondaryMap.getOrDefault(fofoId, 0d)
1013
                    - (dayBeforeYesterdayReturnOrderInfoModelMap.getOrDefault(fofoId, 0L) + dayBeforeYesterdayRtoRefundOrderMap.getOrDefault(fofoId, 0L));
34749 ranu 1014
            fofoDayBeforeYesterdaySecondaryMap.put(fofoId, (long) dayBeforeYesterdayAfterReturnSecondary);
1015
 
36973 ranu 1016
            double yesterDayAfterReturnSecondary = yesterDaySecondaryMap.getOrDefault(fofoId, 0d)
1017
                    - (yesterdayReturnOrderInfoModelMap.getOrDefault(fofoId, 0L) + yesterdayRtoRefundOrderMap.getOrDefault(fofoId, 0L));
34749 ranu 1018
            fofoYesterdaySecondaryMap.put(fofoId, (long) yesterDayAfterReturnSecondary);
1019
 
36973 ranu 1020
            double secondaryAchievedMtd = mtdSecondaryMap.getOrDefault(fofoId, 0.0);
34619 ranu 1021
            double currentMonthNetSecondary = secondaryAchievedMtd - currentMonthReturn;
36973 ranu 1022
            double currentMonthSecondaryPercent = currentSecondaryTarget == 0 ? 0.0
1023
                    : Math.round(Math.abs((secondaryAchievedMtd / currentSecondaryTarget) * 100));
1024
            double currentMonthUnbilled = partnerWiseActivatedNotBilledTotalMap.get(currentMonth) != null
1025
                    ? partnerWiseActivatedNotBilledTotalMap.get(currentMonth).getTotalUnbilledAmount() : 0d;
1026
            double mtdSale = mtdSaleTillYesterdayMap.getOrDefault(fofoId, 0d);
34606 ranu 1027
 
36973 ranu 1028
            // Last month secondary
1029
            double lastMonthSecondaryTarget = lastTargetMap.getOrDefault(fofoId, 0d);
1030
            long lastMonthReturn = lastMonthPartnerReturnOrderInfoModelMap.getOrDefault(fofoId, 0L)
1031
                    + lastMonthRtoRefundOrderMap.getOrDefault(fofoId, 0L);
1032
            double lastMonthSecondaryAchieved = lastMonthSecondaryMap.getOrDefault(fofoId, 0.0);
34619 ranu 1033
            double lastMonthNetSecondary = lastMonthSecondaryAchieved - lastMonthReturn;
36973 ranu 1034
            double lastMonthSecondaryPercent = lastMonthSecondaryTarget == 0 ? 0.0
1035
                    : Math.round(Math.abs((lastMonthSecondaryAchieved / lastMonthSecondaryTarget) * 100));
1036
            double lastMonthUnbilled = partnerWiseActivatedNotBilledTotalMap.get(lastMonth) != null
1037
                    ? partnerWiseActivatedNotBilledTotalMap.get(lastMonth).getTotalUnbilledAmount() : 0d;
1038
            double lastMonthSale = lastMonthSaleMap.getOrDefault(fofoId, 0d);
34606 ranu 1039
 
36973 ranu 1040
            // Two months ago secondary
1041
            double twoMonthAgoSecondaryTarget = twoMonthAgoTargetMap.getOrDefault(fofoId, 0d);
1042
            long twoMonthAgoReturn = twoMonthAgoPartnerReturnOrderInfoModelMap.getOrDefault(fofoId, 0L)
1043
                    + twoMonthAgoRtoRefundOrderMap.getOrDefault(fofoId, 0L);
1044
            double twoMonthAgoSecondaryAchieved = twoMonthAgoSecondaryMap.getOrDefault(fofoId, 0.0);
34619 ranu 1045
            double twoMonthAgoNetSecondary = twoMonthAgoSecondaryAchieved - twoMonthAgoReturn;
36973 ranu 1046
            double twoMonthAgoSecondaryPercent = twoMonthAgoSecondaryTarget == 0 ? 0.0
1047
                    : Math.round(Math.abs((twoMonthAgoSecondaryAchieved / twoMonthAgoSecondaryTarget) * 100));
1048
            double twoMonthAgoUnbilled = partnerWiseActivatedNotBilledTotalMap.get(twoMonthsAgo) != null
1049
                    ? partnerWiseActivatedNotBilledTotalMap.get(twoMonthsAgo).getTotalUnbilledAmount() : 0d;
1050
            double twoMonthAgoSale = twoMonthAgoSaleMap.getOrDefault(fofoId, 0d);
34606 ranu 1051
 
34619 ranu 1052
            Map<YearMonth, BiSecondaryModel> monthlySecondaryModels = new HashMap<>();
36973 ranu 1053
            monthlySecondaryModels.put(currentMonth, new BiSecondaryModel(
1054
                    currentSecondaryTarget, secondaryAchievedMtd, currentMonthReturn,
1055
                    currentMonthNetSecondary, currentMonthSecondaryPercent, mtdSale, currentMonthUnbilled));
1056
            monthlySecondaryModels.put(lastMonth, new BiSecondaryModel(
1057
                    lastMonthSecondaryTarget, lastMonthSecondaryAchieved, lastMonthReturn,
1058
                    lastMonthNetSecondary, lastMonthSecondaryPercent, lastMonthSale, lastMonthUnbilled));
1059
            monthlySecondaryModels.put(twoMonthsAgo, new BiSecondaryModel(
1060
                    twoMonthAgoSecondaryTarget, twoMonthAgoSecondaryAchieved, twoMonthAgoReturn,
1061
                    twoMonthAgoNetSecondary, twoMonthAgoSecondaryPercent, twoMonthAgoSale, twoMonthAgoUnbilled));
34619 ranu 1062
            allRetailerMonthlyData.put(fofoId, monthlySecondaryModels);
1063
 
36973 ranu 1064
            // Brand-wise stock value — still per-fofo (per-fofo pricing service, not batchable trivially)
34619 ranu 1065
            Map<String, BrandStockPrice> brandStockPriceMap = inventoryService.getBrandWiseStockValue(fofoId);
36973 ranu 1066
            fofoBrandStockPriceMap.put(fofoId, brandStockPriceMap);
1067
            fofoTotalStockPriceMap.put(fofoId, brandStockPriceMap.values().stream().mapToDouble(BrandStockPrice::getTotalValue).sum());
34619 ranu 1068
 
36973 ranu 1069
            Map<String, Double> brandMtdTertiaryAmount = brandTertiaryByFofo.getOrDefault(fofoId, new HashMap<>());
1070
            fofoBrandMtdTertiaryMap.put(fofoId, brandMtdTertiaryAmount);
1071
            fofoTotalMtdTertiaryMap.put(fofoId, brandMtdTertiaryAmount.values().stream().mapToDouble(Double::doubleValue).sum());
34619 ranu 1072
 
36973 ranu 1073
            Map<String, Long> brandWiseMtdSecondaryMap = brandBilledByFofo.getOrDefault(fofoId, new HashMap<>());
1074
            Map<String, Double> brandWiseReturnInfoMap = brandReturnByFofo.getOrDefault(fofoId, new HashMap<>());
1075
            Map<String, Double> brandWiseRTOReturnInfoMap = brandRtoReturnByFofo.getOrDefault(fofoId, new HashMap<>());
34619 ranu 1076
 
34730 ranu 1077
            Set<String> allBrands = new HashSet<>();
1078
            allBrands.addAll(brandWiseMtdSecondaryMap.keySet());
1079
            allBrands.addAll(brandWiseReturnInfoMap.keySet());
1080
            allBrands.addAll(brandWiseRTOReturnInfoMap.keySet());
1081
            Map<String, Long> brandWiseMtdNetSecondaryMap = new HashMap<>();
1082
            for (String brand : allBrands) {
36973 ranu 1083
                long billedAmount = brandWiseMtdSecondaryMap.getOrDefault(brand, 0L);
1084
                double returnAmount = brandWiseReturnInfoMap.getOrDefault(brand, 0d);
1085
                double rtoReturnAmount = brandWiseRTOReturnInfoMap.getOrDefault(brand, 0d);
1086
                brandWiseMtdNetSecondaryMap.put(brand, Math.round(billedAmount - (returnAmount + rtoReturnAmount)));
34730 ranu 1087
            }
36973 ranu 1088
            fofoBrandWiseMtdSecondaryMap.put(fofoId, brandWiseMtdNetSecondaryMap);
1089
            fofoTotalMtdSecondaryMap.put(fofoId, brandWiseMtdNetSecondaryMap.values().stream().mapToLong(Long::longValue).sum());
34730 ranu 1090
 
36973 ranu 1091
            // Investment info
1092
            PartnerDailyInvestment pdi = partnerDailyInvestmentMap.get(fofoId);
1093
            float shortInvestment = pdi != null ? pdi.getShortInvestment() : 0f;
1094
            float agreedInvestment = pdi != null ? pdi.getMinInvestment() : 0f;
1095
            float investmentLevel = pdi != null ? Math.abs(((shortInvestment - agreedInvestment) / agreedInvestment) * 100) : 0f;
34730 ranu 1096
 
36973 ranu 1097
            List<Loan> fofoDefaultLoans = defaultLoanMap.get(fofoId);
34641 ranu 1098
            float defaultLoanAmount = 0f;
36973 ranu 1099
            if (fofoDefaultLoans != null) {
1100
                for (Loan entry : fofoDefaultLoans) {
1101
                    double amount = loanStatementSumByLoanId.getOrDefault(entry.getId(), 0d);
1102
                    defaultLoanAmount += amount;
34641 ranu 1103
                }
1104
            }
36973 ranu 1105
            List<Loan> activeLoans = activeLoansByFofo.getOrDefault(fofoId, Collections.emptyList());
34743 ranu 1106
            float activeLoan = 0f;
1107
            for (Loan entry : activeLoans) {
36973 ranu 1108
                double pendingAmount = loanStatementSumByLoanId.getOrDefault(entry.getId(), 0d);
34743 ranu 1109
                activeLoan += pendingAmount;
1110
            }
1111
 
36973 ranu 1112
            float poValue = pdi != null ? pdi.getUnbilledAmount() : 0f;
34719 ranu 1113
            float poAndBilledValue = (float) (currentMonthNetSecondary + poValue);
34641 ranu 1114
 
36973 ranu 1115
            // DRR — inlined equivalent of RbmTargetService.calculateFofoIdTodayTarget.
1116
            // Note: original always looks up YearMonth.now() target (not currentMonth). On the 1st these differ.
1117
            double drrTarget = drrTargetMap.getOrDefault(fofoId, 0d);
1118
            double monthDay1Drr = 0d;
1119
            if (drrTarget != 0d) {
1120
                double remainingTarget = drrTarget;
1121
                monthDay1Drr = day1RemainingDays == 0 ? remainingTarget : (int) Math.ceil(remainingTarget / day1RemainingDays);
34897 ranu 1122
            }
36973 ranu 1123
            double todayRequiredDrr = 0d;
1124
            if (monthDay1Drr != 0d) {
1125
                double remainingTarget = drrTarget - currentMonthNetSecondary;
1126
                todayRequiredDrr = todayRemainingDays == 0 ? remainingTarget : (int) Math.ceil(remainingTarget / todayRemainingDays);
1127
            }
1128
            double gotDrrPercent = monthDay1Drr == 0 ? 0 : (todayRequiredDrr / monthDay1Drr) * 100;
34701 ranu 1129
            long drrPercentDisplay = Math.round(Math.abs(gotDrrPercent));
1130
 
36973 ranu 1131
            int orderId = lastOrderIdByFofo.getOrDefault(fofoId, 0);
34644 ranu 1132
            String alertLevel = "-";
1133
            int lastPurchaseDays = 0;
34641 ranu 1134
            if (orderId != 0) {
36973 ranu 1135
                Order order = lastOrderById.get(orderId);
1136
                if (order != null) {
1137
                    lastPurchaseDays = (int) Duration.between(order.getCreateTimestamp().plusDays(1), LocalDateTime.now()).toDays();
1138
                    if (lastPurchaseDays >= 11) alertLevel = "Alert for Management";
1139
                    else if (lastPurchaseDays >= 10) alertLevel = " Alert for RSM/SH";
1140
                    else if (lastPurchaseDays >= 7) alertLevel = "Must be Billed";
1141
                    else alertLevel = "OK";
34641 ranu 1142
                }
34644 ranu 1143
            }
34641 ranu 1144
 
34644 ranu 1145
            FofoInvestmentModel fofoInvestmentModel = new FofoInvestmentModel();
36973 ranu 1146
            fofoInvestmentModel.setCounterPotential(fofoStore != null ? fofoStore.getCounterPotential() : 0);
34644 ranu 1147
            fofoInvestmentModel.setShortInvestment(shortInvestment);
1148
            fofoInvestmentModel.setDefaultLoan(defaultLoanAmount);
1149
            fofoInvestmentModel.setInvestmentLevel(investmentLevel);
1150
            fofoInvestmentModel.setActiveLoan(activeLoan);
1151
            fofoInvestmentModel.setPoValue(poValue);
1152
            fofoInvestmentModel.setPoAndBilled(poAndBilledValue);
1153
            fofoInvestmentModel.setAgreedInvestment(agreedInvestment);
36973 ranu 1154
            fofoInvestmentModel.setWallet(pdi != null ? pdi.getWalletAmount() : 0);
34644 ranu 1155
            fofoInvestmentModel.setMonthBeginingDrr(monthDay1Drr);
1156
            fofoInvestmentModel.setRequiredDrr(todayRequiredDrr);
34701 ranu 1157
            fofoInvestmentModel.setDrrPercent(drrPercentDisplay);
34644 ranu 1158
            fofoInvestmentModel.setLastBillingDone(lastPurchaseDays);
1159
            fofoInvestmentModel.setSlab(alertLevel);
1160
            biInvestmentModelMap.put(fofoId, fofoInvestmentModel);
34641 ranu 1161
 
36973 ranu 1162
            String assessment;
1163
            if (defaultLoanAmount < 0) assessment = "Loan Default";
1164
            else if (investmentLevel <= 75 && defaultLoanAmount >= 0) assessment = "Low Invest";
1165
            else assessment = "-";
1166
            assessmentMap.put(fofoId, assessment);
1167
            zeroBillingMap.put(fofoId, currentMonthNetSecondary <= 100000 ? "Zero Billing" : "-");
1168
            billingNeededMap.put(fofoId, drrPercentDisplay >= 110 && todayRequiredDrr > 0 ? (float) todayRequiredDrr : 0f);
1169
            countAMap.put(fofoId, (defaultLoanAmount > 0 || investmentLevel <= 75 || currentMonthNetSecondary <= 100000 || drrPercentDisplay >= 110) ? 1 : 0);
34606 ranu 1170
        }
1171
 
34619 ranu 1172
        LOGGER.info("Total BI Retailers processed: {}", biRetailerModelMap.size());
36975 ranu 1173
        LOGGER.info("[BI_REPORT] per-fofo loop finished in {}ms",
1174
                System.currentTimeMillis() - __biReportLoopStartMs);
34606 ranu 1175
 
34619 ranu 1176
        //generate excel and sent to mail
1177
        List<List<String>> headerGroup = new ArrayList<>();
34606 ranu 1178
 
34619 ranu 1179
        List<String> headers1 = Arrays.asList(
34641 ranu 1180
                "","","","",
34916 ranu 1181
                "Retailer Detail", "","", "", "", "", "", "", "", "","","","","",
34677 ranu 1182
 
1183
                twoMonthAgoStringValue, "", "", "", "", "", "",
1184
                lastMonthStringValue, "", "", "", "", "", "",
34619 ranu 1185
                currentMonthStringValue, "", "", "", "", "", "",
34641 ranu 1186
 
1187
                "","", "", "", "", "", "", "", "", "", "", "", "", "",
1188
 
1189
                "", "", "", "", "", "", "", "", "", "", "", "", "",
34749 ranu 1190
                "", "", "", "", "", "", "", "", "", "", "", "", "","",""
34641 ranu 1191
 
34619 ranu 1192
        );
34606 ranu 1193
 
34619 ranu 1194
        List<String> headers2 = Arrays.asList(
34641 ranu 1195
                "Assessment","Zero billing","Billing needed","Counta",
37225 ranu 1196
                "BM","Partner Id","Link","Wallet Date","Creation Date","Code","Area",  "City", "Store Name", "Status","Category","Sales Manager", "RBM",
34619 ranu 1197
                "Secondary Target", "Secondary Achieved", "Returns", "Net Secondary", "Secondary %",
1198
                "Tertiary Sale", "Unbilled",
1199
                "Secondary Target", "Secondary Achieved", "Returns", "Net Secondary", "Secondary %",
1200
                "Tertiary Sale", "Unbilled",
1201
                "Secondary Target", "Secondary Achieved", "Returns", "Net Secondary", "Secondary %",
1202
                "Tertiary Sale", "Unbilled",
34641 ranu 1203
                "Counter Potential", "Short investment", "Default", "INVESTMENT LEVEL", "Loan", "PO value", "Agreed investment",
1204
                "Wallet", "po+bill", "MONTH BEGINNING DRR", "REQ DRR", "Drr %", "Last billing Done", "Slab",
34606 ranu 1205
 
36193 ranu 1206
              "Total Stock",  "Apple","Xiaomi", "Vivo", "Tecno","Motorola", "Samsung", "Realme", "Oppo", "OnePlus", "POCO", "Lava", "Itel", "Almost New",
1207
              "Total Secondary", "Apple", "Xiaomi", "Vivo", "Tecno","Motorola", "Samsung", "Realme", "Oppo", "OnePlus", "POCO", "Lava", "Itel", "Almost New",
1208
              "Total Tertiary",  "Apple", "Xiaomi", "Vivo", "Tecno","Motorola", "Samsung", "Realme", "Oppo", "OnePlus", "POCO", "Lava", "Itel", "Almost New",
34749 ranu 1209
                "YesterDay Seconday","Day Before Yesterday Secondary"
34619 ranu 1210
        );
1211
 
1212
        headerGroup.add(headers1);
1213
        headerGroup.add(headers2);
1214
 
1215
 
1216
        List<List<?>> rows = new ArrayList<>();
36973 ranu 1217
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
34619 ranu 1218
        for (Map.Entry<Integer, BIRetailerModel> entry : biRetailerModelMap.entrySet()) {
1219
            Integer fofoId = entry.getKey();
36973 ranu 1220
            User user = userMap.get(fofoId);
1221
            LocalDateTime walletCreationDate = walletFirstCreatedMap.get(fofoId);
1222
            if (walletCreationDate == null && user != null) {
34758 ranu 1223
                walletCreationDate = user.getCreateTimestamp();
1224
            }
34619 ranu 1225
            BIRetailerModel retailer = entry.getValue();
34641 ranu 1226
 
34619 ranu 1227
            Map<YearMonth, BiSecondaryModel> monthlyData = allRetailerMonthlyData.get(fofoId);
1228
 
34741 ranu 1229
            BiSecondaryModel current = monthlyData.getOrDefault(currentMonth, new BiSecondaryModel(0,0,0,0,0,0,0));
1230
            BiSecondaryModel last = monthlyData.getOrDefault(currentMonth.minusMonths(1), new BiSecondaryModel(0,0,0,0,0,0,0));
1231
            BiSecondaryModel twoAgo = monthlyData.getOrDefault(currentMonth.minusMonths(2), new BiSecondaryModel(0,0,0,0,0,0,0));
34619 ranu 1232
 
1233
            List<Object> row = new ArrayList<>();
34758 ranu 1234
            LOGGER.info("fofoId-11 {}",fofoId);
1235
 
34619 ranu 1236
            row.addAll(Arrays.asList(
34758 ranu 1237
                    assessmentMap.get(fofoId),
1238
                    zeroBillingMap.get(fofoId),
1239
                    billingNeededMap.get(fofoId),
1240
                    countAMap.get(fofoId),
1241
                    retailer.getBmName(),
1242
                    fofoId ,
1243
                    "https://partners.smartdukaan.com/partnerPerformance?fofoId="+fofoId,
36973 ranu 1244
                    walletCreationDate != null ? walletCreationDate.format(formatter) : "-",
1245
                    (user != null && user.getCreateTimestamp() != null) ? user.getCreateTimestamp().format(formatter) : "-",
34758 ranu 1246
                    retailer.getCode(),
1247
                    retailer.getArea(),
1248
                    retailer.getCity(),
1249
                    retailer.getStoreName(),
1250
                    retailer.getStatus(),
1251
                    retailer.getCategory(),
1252
                    retailer.getSalesManager(),
37225 ranu 1253
                    retailer.getRbm()
34677 ranu 1254
 
34619 ranu 1255
            ));
1256
 
34677 ranu 1257
 
1258
            // Two Months Ago
34619 ranu 1259
            row.addAll(Arrays.asList(
34677 ranu 1260
                    twoAgo.getSecondaryTarget(),
1261
                    twoAgo.getSecondaryAchieved(),
1262
                    twoAgo.getSecondaryReturn(),
1263
                    twoAgo.getNetSecondary(),
34704 ranu 1264
                    twoAgo.getSecondaryAchievedPercent()+"%",
34677 ranu 1265
                    twoAgo.getTertiary(),
1266
                    twoAgo.getTertiaryUnBilled()
34619 ranu 1267
            ));
1268
 
1269
            // Last Month
1270
            row.addAll(Arrays.asList(
1271
                    last.getSecondaryTarget(),
1272
                    last.getSecondaryAchieved(),
1273
                    last.getSecondaryReturn(),
1274
                    last.getNetSecondary(),
34704 ranu 1275
                    last.getSecondaryAchievedPercent()+"%",
34619 ranu 1276
                    last.getTertiary(),
1277
                    last.getTertiaryUnBilled()
1278
            ));
1279
 
34677 ranu 1280
            // Current Month
34619 ranu 1281
            row.addAll(Arrays.asList(
34677 ranu 1282
                    current.getSecondaryTarget(),
1283
                    current.getSecondaryAchieved(),
1284
                    current.getSecondaryReturn(),
1285
                    current.getNetSecondary(),
34704 ranu 1286
                    current.getSecondaryAchievedPercent()+"%",
34677 ranu 1287
                    current.getTertiary(),
1288
                    current.getTertiaryUnBilled()
34619 ranu 1289
            ));
34677 ranu 1290
 
1291
 
1292
 
34641 ranu 1293
            FofoInvestmentModel fofoInvestmentModelValue = biInvestmentModelMap.get(fofoId);
1294
            if(fofoInvestmentModelValue != null){
1295
                row.addAll(Arrays.asList(
1296
                        fofoInvestmentModelValue.getCounterPotential(),
1297
                        fofoInvestmentModelValue.getShortInvestment(),
1298
                        fofoInvestmentModelValue.getDefaultLoan(),
34730 ranu 1299
                        fofoInvestmentModelValue.getInvestmentLevel() +"%",
34743 ranu 1300
                        fofoInvestmentModelValue.getActiveLoan(),
34641 ranu 1301
                        fofoInvestmentModelValue.getPoValue(),
1302
                        fofoInvestmentModelValue.getAgreedInvestment(),
1303
                        fofoInvestmentModelValue.getWallet(),
1304
                        fofoInvestmentModelValue.getPoAndBilled(),
1305
                        fofoInvestmentModelValue.getMonthBeginingDrr(),
1306
                        fofoInvestmentModelValue.getRequiredDrr(),
34704 ranu 1307
                        fofoInvestmentModelValue.getDrrPercent()+"%",
34641 ranu 1308
                        fofoInvestmentModelValue.getLastBillingDone(),
1309
                        fofoInvestmentModelValue.getSlab()
1310
                ));
1311
            }else {
1312
                row.addAll(Arrays.asList(
1313
                        "-","-","-","-","-","-","-","-","-","-","-",""
1314
                ));
1315
            }
1316
 
1317
            Map<String, BrandStockPrice> brandStockMap = fofoBrandStockPriceMap.get(fofoId);
34619 ranu 1318
            row.addAll(Arrays.asList(
1319
                    fofoTotalStockPriceMap.getOrDefault(fofoId, 0.0),
34641 ranu 1320
                    brandStockMap.get("Apple") != null ? brandStockMap.get("Apple").getTotalValue() : 0.0,
1321
                    brandStockMap.get("Xiaomi") != null ? brandStockMap.get("Xiaomi").getTotalValue() : 0.0,
1322
                    brandStockMap.get("Vivo") != null ? brandStockMap.get("Vivo").getTotalValue() : 0.0,
1323
                    brandStockMap.get("Tecno") != null ? brandStockMap.get("Tecno").getTotalValue() : 0.0,
36193 ranu 1324
                    brandStockMap.get("Motorola") != null ? brandStockMap.get("Motorola").getTotalValue() : 0.0,
34641 ranu 1325
                    brandStockMap.get("Samsung") != null ? brandStockMap.get("Samsung").getTotalValue() : 0.0,
1326
                    brandStockMap.get("Realme") != null ? brandStockMap.get("Realme").getTotalValue() : 0.0,
1327
                    brandStockMap.get("Oppo") != null ? brandStockMap.get("Oppo").getTotalValue() : 0.0,
1328
                    brandStockMap.get("OnePlus") != null ? brandStockMap.get("OnePlus").getTotalValue() : 0.0,
34721 ranu 1329
                    brandStockMap.get("POCO") != null ? brandStockMap.get("POCO").getTotalValue() : 0.0,
34641 ranu 1330
                    brandStockMap.get("Lava") != null ? brandStockMap.get("Lava").getTotalValue() : 0.0,
1331
                    brandStockMap.get("Itel") != null ? brandStockMap.get("Itel").getTotalValue() : 0.0,
1332
                    brandStockMap.get("Almost New") != null ? brandStockMap.get("Almost New").getTotalValue() : 0.0
34619 ranu 1333
            ));
1334
 
34641 ranu 1335
            Map<String, Long> brandSecondaryMap = fofoBrandWiseMtdSecondaryMap.get(fofoId);
1336
            row.addAll(Arrays.asList(
34648 ranu 1337
                    fofoTotalMtdSecondaryMap.get(fofoId),
34641 ranu 1338
                    brandSecondaryMap.getOrDefault("Apple", 0L),
1339
                    brandSecondaryMap.getOrDefault("Xiaomi", 0L),
1340
                    brandSecondaryMap.getOrDefault("Vivo", 0L),
1341
                    brandSecondaryMap.getOrDefault("Tecno", 0L),
36193 ranu 1342
                    brandSecondaryMap.getOrDefault("Motorola", 0L),
34641 ranu 1343
                    brandSecondaryMap.getOrDefault("Samsung", 0L),
1344
                    brandSecondaryMap.getOrDefault("Realme", 0L),
1345
                    brandSecondaryMap.getOrDefault("Oppo", 0L),
1346
                    brandSecondaryMap.getOrDefault("OnePlus", 0L),
34721 ranu 1347
                    brandSecondaryMap.getOrDefault("POCO", 0L),
34641 ranu 1348
                    brandSecondaryMap.getOrDefault("Lava", 0L),
1349
                    brandSecondaryMap.getOrDefault("Itel", 0L),
1350
                    brandSecondaryMap.getOrDefault("Almost New", 0L)
1351
            ));
1352
 
1353
            Map<String, Double> brandTertiaryMap = fofoBrandMtdTertiaryMap.get(fofoId);
1354
            row.addAll(Arrays.asList(
34648 ranu 1355
                    fofoTotalMtdTertiaryMap.get(fofoId),
34641 ranu 1356
                    brandTertiaryMap.getOrDefault("Apple", 0d),
1357
                    brandTertiaryMap.getOrDefault("Xiaomi", 0d),
1358
                    brandTertiaryMap.getOrDefault("Vivo", 0d),
1359
                    brandTertiaryMap.getOrDefault("Tecno", 0d),
36193 ranu 1360
                    brandTertiaryMap.getOrDefault("Motorola", 0d),
34641 ranu 1361
                    brandTertiaryMap.getOrDefault("Samsung", 0d),
1362
                    brandTertiaryMap.getOrDefault("Realme", 0d),
1363
                    brandTertiaryMap.getOrDefault("Oppo", 0d),
1364
                    brandTertiaryMap.getOrDefault("OnePlus", 0d),
34721 ranu 1365
                    brandTertiaryMap.getOrDefault("POCO", 0d),
34641 ranu 1366
                    brandTertiaryMap.getOrDefault("Lava", 0d),
1367
                    brandTertiaryMap.getOrDefault("Itel", 0d),
1368
                    brandTertiaryMap.getOrDefault("Almost New", 0d)
1369
            ));
34749 ranu 1370
 
1371
            row.addAll(Arrays.asList(
1372
                    fofoYesterdaySecondaryMap.get(fofoId),
1373
                    fofoDayBeforeYesterdaySecondaryMap.get(fofoId)
1374
            ));
34641 ranu 1375
            rows.add(row);
34619 ranu 1376
        }
1377
 
36987 ranu 1378
        Map<String, Set<Integer>> storeGuyMap = this.generateBiReportHierarchyWise();
34619 ranu 1379
 
35239 ranu 1380
        for (Map.Entry<String, Set<Integer>> storeGuyEntry : storeGuyMap.entrySet()) {
34912 ranu 1381
            String storeGuyEmail = storeGuyEntry.getKey();
1382
            Set<Integer> fofoIds = storeGuyEntry.getValue();
1383
            String[] sendToArray = new String[]{storeGuyEmail};
34911 ranu 1384
 
34912 ranu 1385
            List<List<?>> filteredRows = rows.stream()
1386
                    .filter(row -> row.size() > 5 && fofoIds.contains((Integer) row.get(5)))
1387
                    .collect(Collectors.toList());
1388
            this.sendMailToUser(headerGroup,filteredRows,sendToArray);
36987 ranu 1389
        }
34912 ranu 1390
 
36193 ranu 1391
        this.sendMailToUser(
1392
                headerGroup,
1393
                rows,
1394
                new String[]{
36987 ranu 1395
                        "ranu.rajput@smartdukaan.com",
1396
                        "niranjan.kala@smartdukaan.com",
36216 ranu 1397
                        "nivesh.mathur@smartdukaan.com",
36193 ranu 1398
                        "deena.nath@smartdukaan.com",
36987 ranu 1399
                        "santosh.giri@smartdukaan.com"
36193 ranu 1400
                }
1401
        );
34912 ranu 1402
 
36975 ranu 1403
        LOGGER.info("[BI_REPORT] DONE batch-optimized generateBiReportExcel; totalMs={}, retailers={}, rows={}",
1404
                System.currentTimeMillis() - __biReportStartMs, retailerIds.size(), rows.size());
34912 ranu 1405
 
36975 ranu 1406
 
34911 ranu 1407
    }
1408
 
1409
    private  void sendMailToUser(List<List<String>> headerGroup,List<List<?>> rows, String[] sendToArray ) throws Exception {
34641 ranu 1410
        // Send to email
1411
//        ByteArrayOutputStream csvStream = FileUtil.getCSVByteStreamWithMultiHeaders(headerGroup, rows);
1412
        ByteArrayOutputStream csvStream = getExcelStreamWithMultiHeaders(headerGroup, rows);
1413
        String fileName = "BI-Retailer-Monthly-Report-" + FormattingUtils.formatDate(LocalDateTime.now()) + ".xlsx";
34619 ranu 1414
        Utils.sendMailWithAttachment(googleMailSender, sendToArray, new String[]{}, "BI Retailer Monthly Report", "Please find attached the BI retailer secondary/tertiary monthly report.", fileName, new ByteArrayResource(csvStream.toByteArray()));
34911 ranu 1415
    }
34619 ranu 1416
 
1417
 
34641 ranu 1418
    public static ByteArrayOutputStream getExcelStreamWithMultiHeaders(List<List<String>> headerGroup, List<List<?>> rows) {
1419
        Workbook workbook = new XSSFWorkbook();
1420
        Sheet sheet = workbook.createSheet("BI Report");
34715 ranu 1421
        CreationHelper creationHelper = workbook.getCreationHelper();
34641 ranu 1422
        int rowIndex = 0;
34606 ranu 1423
 
34641 ranu 1424
        CellStyle centeredStyle = workbook.createCellStyle();
1425
        centeredStyle.setAlignment(HorizontalAlignment.CENTER); // Center horizontally
1426
        centeredStyle.setVerticalAlignment(VerticalAlignment.CENTER); // Center vertically
34606 ranu 1427
 
34641 ranu 1428
    // Optional: bold font
1429
        Font font1 = workbook.createFont();
1430
        font1.setBold(true);
1431
        centeredStyle.setFont(font1);
34606 ranu 1432
 
34619 ranu 1433
 
34641 ranu 1434
 
1435
        // Create styles
1436
        Map<String, CellStyle> headerStyles = new HashMap<>();
1437
 
1438
        // fontPurpleStyle
1439
        CellStyle purpleStyle = workbook.createCellStyle();
1440
        purpleStyle.setFillForegroundColor(IndexedColors.ROSE.getIndex());
1441
        purpleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1442
        purpleStyle.setFont(font1);
1443
        headerStyles.put("Assessment", purpleStyle);
1444
        headerStyles.put("Zero billing", purpleStyle);
1445
        headerStyles.put("Billing needed", purpleStyle);
1446
        headerStyles.put("Counta", purpleStyle);
1447
        headerStyles.put("MONTH BEGINNING DRR", purpleStyle);
1448
        headerStyles.put("REQ DRR", purpleStyle);
1449
        headerStyles.put("Drr %", purpleStyle);
1450
 
1451
        // Light Blue
1452
        CellStyle blueStyle = workbook.createCellStyle();
1453
        blueStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
1454
        blueStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1455
        blueStyle.setFont(font1);
1456
        headerStyles.put("Code", blueStyle);
1457
        headerStyles.put("Store Name", blueStyle);
1458
        headerStyles.put("City", blueStyle);
1459
        headerStyles.put("Area", blueStyle);
1460
        headerStyles.put("BM", blueStyle);
1461
        headerStyles.put("RBM", blueStyle);
1462
        headerStyles.put("Sales Manager", blueStyle);
1463
        headerStyles.put("Status", blueStyle);
1464
        headerStyles.put("Category", blueStyle);
34715 ranu 1465
        headerStyles.put("Wallet Date", blueStyle);
1466
        headerStyles.put("Creation Date", blueStyle);
1467
        headerStyles.put("Partner Id", blueStyle);
34641 ranu 1468
 
34715 ranu 1469
        //for link
1470
        // Create hyperlink style
1471
        CellStyle hyperlinkStyle = workbook.createCellStyle();
1472
        Font hlinkFont = workbook.createFont();
1473
        hlinkFont.setUnderline(Font.U_SINGLE);
1474
        hlinkFont.setColor(IndexedColors.BLUE.getIndex());
1475
        hyperlinkStyle.setFont(hlinkFont);
1476
 
1477
 
34641 ranu 1478
        // Light Yellow
1479
        CellStyle yellowStyle = workbook.createCellStyle();
1480
        yellowStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
1481
        yellowStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1482
        yellowStyle.setFont(font1);
1483
        headerStyles.put("Last billing Done", yellowStyle);
1484
        headerStyles.put("Total Stock", yellowStyle);
1485
 
1486
        // Light Orange
1487
        CellStyle orangeStyle = workbook.createCellStyle();
1488
        orangeStyle.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex());
1489
        orangeStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1490
        orangeStyle.setFont(font1);
1491
        headerStyles.put("Total Tertiary", orangeStyle);
1492
        headerStyles.put("Total Secondary", orangeStyle);
1493
        headerStyles.put("Default", orangeStyle);
1494
 
1495
 
1496
        // Light green
1497
        CellStyle lightGreenStyle = workbook.createCellStyle();
1498
        lightGreenStyle.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());
1499
        lightGreenStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1500
        lightGreenStyle.setFont(font1);
1501
        headerStyles.put("Short investment", lightGreenStyle);
1502
        headerStyles.put("INVESTMENT LEVEL", lightGreenStyle);
1503
        headerStyles.put("Loan", lightGreenStyle);
1504
        headerStyles.put("PO value", lightGreenStyle);
1505
        headerStyles.put("Agreed investment", lightGreenStyle);
1506
        headerStyles.put("Wallet", lightGreenStyle);
1507
        headerStyles.put("po+bill", lightGreenStyle);
1508
 
1509
        // Light Green
1510
        CellStyle secondary1 = createStyle(workbook, IndexedColors.LIGHT_GREEN);
1511
        CellStyle secondary2 = createStyle(workbook, IndexedColors.LIGHT_YELLOW);
1512
        CellStyle secondary3 = createStyle(workbook, IndexedColors.LIGHT_ORANGE);
1513
 
1514
        Map<String, CellStyle> brandStyles = new HashMap<>();
1515
        brandStyles.put("Apple", createStyle(workbook, IndexedColors.GREY_25_PERCENT));
1516
        brandStyles.put("Xiaomi", createStyle(workbook, IndexedColors.ORANGE));
1517
        brandStyles.put("Vivo", createStyle(workbook, IndexedColors.SKY_BLUE));
1518
        brandStyles.put("Tecno", createStyle(workbook, IndexedColors.LIGHT_BLUE));
36193 ranu 1519
        brandStyles.put("Motorola", createStyle(workbook, IndexedColors.LIGHT_GREEN));
34641 ranu 1520
        brandStyles.put("Samsung", createStyle(workbook, IndexedColors.ROYAL_BLUE));
1521
        brandStyles.put("Realme", createStyle(workbook, IndexedColors.YELLOW));
1522
        brandStyles.put("Oppo", createStyle(workbook, IndexedColors.LIGHT_GREEN));
1523
        brandStyles.put("OnePlus", createStyle(workbook, IndexedColors.RED));
34721 ranu 1524
        brandStyles.put("POCO", createStyle(workbook, IndexedColors.ORANGE));
34641 ranu 1525
        brandStyles.put("Lava", createStyle(workbook, IndexedColors.LIGHT_YELLOW));
1526
        brandStyles.put("Itel", createStyle(workbook, IndexedColors.LIGHT_YELLOW));
1527
        brandStyles.put("Almost New", createStyle(workbook, IndexedColors.WHITE));
1528
 
1529
 
1530
        CellStyle defaultHeaderStyle = workbook.createCellStyle();
1531
        defaultHeaderStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
1532
        defaultHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1533
        defaultHeaderStyle.setFont(font1);
1534
 
34749 ranu 1535
        CellStyle numberStyle = workbook.createCellStyle();
1536
        DataFormat format = workbook.createDataFormat();
1537
        numberStyle.setDataFormat(format.getFormat("#,##0")); // or "#,##0.00" for two decimals
34641 ranu 1538
 
34749 ranu 1539
 
1540
 
34641 ranu 1541
        Map<String, Integer> headerCount = new HashMap<>();
1542
 
1543
        for (int headerRowIndex = 0; headerRowIndex < headerGroup.size(); headerRowIndex++) {
1544
            List<String> headerRow = headerGroup.get(headerRowIndex);
1545
            Row row = sheet.createRow(rowIndex++);
1546
 
1547
            for (int i = 0; i < headerRow.size(); i++) {
1548
                String headerText = headerRow.get(i);
1549
                sheet.setColumnWidth(i, 25 * 256);
1550
                row.setHeightInPoints(20); // 25-point height
1551
                Cell cell = row.createCell(i);
1552
                cell.setCellValue(headerText);
1553
                cell.setCellStyle(centeredStyle);
1554
                // Count how many times this header has appeared
1555
                int count = headerCount.getOrDefault(headerText, 0) + 1;
1556
                headerCount.put(headerText, count);
1557
                // Apply special style for repeated headers
1558
                if (headerText.equals("Secondary Target") || headerText.equals("Secondary Achieved") || headerText.equals("Returns") || headerText.equals("Net Secondary") || headerText.equals("Secondary %") || headerText.equals("Tertiary Sale") || headerText.equals("Unbilled")) {
1559
                    if (count == 1) {
1560
                        cell.setCellStyle(secondary1);
1561
                    } else if (count == 2) {
1562
                        cell.setCellStyle(secondary2);
1563
                    } else if (count == 3) {
1564
                        cell.setCellStyle(secondary3);
1565
                    }
1566
                }
1567
                // Brand header styling (apply only for the 2nd row of headers)
1568
                else if (headerRowIndex == 1 && brandStyles.containsKey(headerText)) {
1569
                    cell.setCellStyle(brandStyles.get(headerText));
1570
                }else if (headerStyles.containsKey(headerText)) {
1571
                    cell.setCellStyle(headerStyles.get(headerText));
1572
                } else {
1573
                    cell.setCellStyle(defaultHeaderStyle); // default style for others
1574
                }
1575
            }
1576
        }
1577
 
1578
        // Write data rows
1579
        for (List<?> dataRow : rows) {
1580
            Row row = sheet.createRow(rowIndex++);
1581
            for (int i = 0; i < dataRow.size(); i++) {
1582
                Cell cell = row.createCell(i);
1583
                Object value = dataRow.get(i);
34715 ranu 1584
 
1585
                if (i == 6 && value != null) { // Assuming column 6 is "Link"
1586
                    Hyperlink hyperlink = creationHelper.createHyperlink(HyperlinkType.URL);
1587
                    hyperlink.setAddress(value.toString());
34719 ranu 1588
                    cell.setCellValue("View Link"); // Display text
34715 ranu 1589
                    cell.setHyperlink(hyperlink);
1590
                    cell.setCellStyle(hyperlinkStyle);
34719 ranu 1591
                } else if (value instanceof Number) {
34749 ranu 1592
                    double numeric = ((Number) value).doubleValue();
1593
                    cell.setCellValue(Math.round(numeric));
1594
                    cell.setCellStyle(numberStyle);
34715 ranu 1595
                } else {
1596
                    cell.setCellValue(value != null ? value.toString() : "");
1597
                }
34641 ranu 1598
            }
34719 ranu 1599
 
34641 ranu 1600
        }
1601
 
1602
        // Auto-size columns
1603
        if (!rows.isEmpty()) {
1604
            for (int i = 0; i < rows.get(0).size(); i++) {
1605
                sheet.autoSizeColumn(i);
1606
            }
1607
        }
1608
 
1609
        // Output as ByteArray
1610
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
1611
            workbook.write(outputStream);
1612
            workbook.close();
1613
            return outputStream;
1614
        } catch (IOException e) {
1615
            throw new RuntimeException("Failed to generate Excel file", e);
1616
        }
1617
    }
1618
 
1619
 
1620
    private static CellStyle createStyle(Workbook workbook, IndexedColors color) {
1621
        CellStyle style = workbook.createCellStyle();
1622
        style.setFillForegroundColor(color.getIndex());
1623
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1624
        Font font = workbook.createFont();
1625
        font.setBold(true);
1626
        style.setFont(font);
1627
        return style;
1628
    }
1629
 
1630
 
34758 ranu 1631
    public void stockAlertMailToRetailer() throws Exception {
1632
 
1633
        Map<Integer, CustomRetailer> customRetailers = retailerService.getFofoRetailers(true);
1634
 
1635
        List<Integer> retailerIds = customRetailers.values().stream().map(CustomRetailer::getPartnerId).collect(Collectors.toList());
1636
 
1637
        for(Integer fofoId : retailerIds){
1638
            List<String> statusOrder = Arrays.asList("HID", "FASTMOVING", "RUNNING", "SLOWMOVING", "OTHER");
1639
            FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(fofoId);
1640
            List<PartnerWarehouseStockSummaryModel> partnerWarehouseStockSummaryModels = saholicInventoryService.getSaholicAndPartnerStock(fofoId, fofoStore.getWarehouseId());
1641
 
1642
            List<PartnerWarehouseStockAgingSummaryModel> partnerWarehouseStockAgingSummaryModelList = new ArrayList<>();
1643
 
1644
            Set<Integer> catalogIds = partnerWarehouseStockSummaryModels.stream().map(x -> x.getCatalogId()).collect(Collectors.toSet());
1645
 
1646
            List<Integer> catalogsList = new ArrayList<>(catalogIds);
1647
 
1648
            Map<Integer, TagListing> tagListingsMap = tagListingRepository.selectAllByCatalogIds(catalogsList);
1649
 
1650
            List<CatalogAgingModel> catalogAgingModels = ageingService.getCatalogsAgingByWarehouse(catalogIds, fofoStore.getWarehouseId());
1651
 
1652
            Map<Integer, CatalogAgingModel> catalogAgingModelMap = catalogAgingModels.stream().collect(Collectors.toMap(x -> x.getCatalogId(), x -> x));
1653
 
1654
            for (PartnerWarehouseStockSummaryModel stockSummary : partnerWarehouseStockSummaryModels) {
1655
 
1656
                PartnerWarehouseStockAgingSummaryModel partnerWarehouseStockAgingSummaryModel = new PartnerWarehouseStockAgingSummaryModel();
1657
                partnerWarehouseStockAgingSummaryModel.setCatalogId(stockSummary.getCatalogId());
1658
                partnerWarehouseStockAgingSummaryModel.setBrand(stockSummary.getBrand());
1659
                partnerWarehouseStockAgingSummaryModel.setModelNumber(stockSummary.getModelNumber());
1660
                partnerWarehouseStockAgingSummaryModel.setNetAvailability(stockSummary.getShaholicNetAvailability());
1661
                partnerWarehouseStockAgingSummaryModel.setPartnerStockAvailability(stockSummary.getPartnerFullFilledQty());
1662
                partnerWarehouseStockAgingSummaryModel.setPartnerCurrentAvailability(stockSummary.getPartnerCurrentQty());
1663
                partnerWarehouseStockAgingSummaryModel.setPartnerShortageStock(stockSummary.getPartnerShortageQty());
1664
                if (catalogAgingModelMap.get(stockSummary.getCatalogId()) != null) {
1665
                    partnerWarehouseStockAgingSummaryModel.setExceedDays(catalogAgingModelMap.get(stockSummary.getCatalogId()).getExceedDays());
1666
                } else {
1667
                    partnerWarehouseStockAgingSummaryModel.setExceedDays(0);
1668
 
1669
                }
1670
                partnerWarehouseStockAgingSummaryModel.setStatus(stockSummary.getStatus());
1671
 
1672
                partnerWarehouseStockAgingSummaryModelList.add(partnerWarehouseStockAgingSummaryModel);
1673
            }
1674
 
1675
            Set<Integer> existingCatalogIdsInAgingSummaryList = partnerWarehouseStockAgingSummaryModelList.stream()
1676
                    .map(PartnerWarehouseStockAgingSummaryModel::getCatalogId)
1677
                    .collect(Collectors.toSet());
1678
        }
1679
 
1680
    }
1681
 
34939 ranu 1682
    public void createFofoSmartCartSuggestion(){
34758 ranu 1683
 
34939 ranu 1684
        List<Integer> fofoIds = fofoStoreRepository.selectActiveStores().stream().map(x->x.getId()).collect(toList());
1685
        LocalDateTime todayDate = LocalDate.now().atStartOfDay();
1686
        LocalDateTime fortyFiveAgoDate = todayDate.minusDays(45).with(LocalTime.MAX);
1687
        for(Integer fofoId :fofoIds){
1688
            smartCartSuggestionRepository.deleteByFofoId(fofoId);
1689
            List<SoldAllCatalogitemQtyByPartnerModel> soldAllCatalogitemQtyByPartnerModels = smartCartService.getAllSoldCatalogItemByPartner(fofoId,fortyFiveAgoDate,todayDate);
1690
            for(SoldAllCatalogitemQtyByPartnerModel soldAllCatalogitemQtyByPartnerModel : soldAllCatalogitemQtyByPartnerModels){
1691
               SmartCartSuggestion smartCartSuggestion = new SmartCartSuggestion();
34941 ranu 1692
 
1693
                // weekly average = total sold qty / 6 weeks
1694
                long avgWeeklyQty = Math.round((float) soldAllCatalogitemQtyByPartnerModel.getSoldQty() / 6);
1695
 
1696
                // ensure minimum 2
1697
                long suggestedQty = Math.max(1, avgWeeklyQty);
1698
 
34939 ranu 1699
               smartCartSuggestion.setCatalogId(soldAllCatalogitemQtyByPartnerModel.getCatalogId());
1700
               smartCartSuggestion.setFofoId(fofoId);
1701
               smartCartSuggestion.setSoldQty(soldAllCatalogitemQtyByPartnerModel.getSoldQty());
34941 ranu 1702
               smartCartSuggestion.setSuggestedQty(suggestedQty);
34939 ranu 1703
               smartCartSuggestion.setCreationDate(LocalDate.now());
1704
               smartCartSuggestionRepository.persist(smartCartSuggestion);
1705
            }
1706
        }
1707
 
1708
    }
1709
 
1710
 
34306 ranu 1711
}