Subversion Repositories SmartDukaan

Rev

Rev 37441 | 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("kamini.sharma@smartdukaan.com");
34321 ranu 445
 
446
        String[] emailRecipients = sendTo.toArray(new String[0]);
447
 
448
        this.sendMailHtmlFormat(emailRecipients, emailContent.toString(), null, bcc, subject);
449
 
36113 ranu 450
        LOGGER.info("Weekly RBM feedback summary email sent for week: {} to {}", previousMonday, previousSunday);
34321 ranu 451
    }
452
 
36113 ranu 453
    // Inner class to hold RBM weekly statistics
454
    private static class RbmWeeklyStats {
455
        private final int rbmId;
456
        private final String rbmName;
457
        private final double avgRating;
458
        private final int partnerCount;
459
        private double combinedScore;
460
        private int rank;
34321 ranu 461
 
36113 ranu 462
        public RbmWeeklyStats(int rbmId, String rbmName, double avgRating, int partnerCount) {
463
            this.rbmId = rbmId;
464
            this.rbmName = rbmName;
465
            this.avgRating = avgRating;
466
            this.partnerCount = partnerCount;
467
        }
468
 
469
        public int getRbmId() { return rbmId; }
470
        public String getRbmName() { return rbmName; }
471
        public double getAvgRating() { return avgRating; }
472
        public int getPartnerCount() { return partnerCount; }
473
        public double getCombinedScore() { return combinedScore; }
474
        public void setCombinedScore(double combinedScore) { this.combinedScore = combinedScore; }
475
        public int getRank() { return rank; }
476
        public void setRank(int rank) { this.rank = rank; }
477
    }
478
 
479
 
34321 ranu 480
    public void sendSalesFeedbackSummaryEmail() throws MessagingException, ProfitMandiBusinessException, IOException {
36113 ranu 481
        // Weekly date range: Previous Monday to Sunday
482
        LocalDate today = LocalDate.now();
483
        LocalDate previousMonday = today.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));
484
        LocalDate previousSunday = previousMonday.plusDays(6);
485
        LocalDateTime startOfWeek = previousMonday.atStartOfDay();
486
        LocalDateTime endOfWeek = previousSunday.atTime(23, 59, 59);
487
 
34323 ranu 488
        String[] bcc = {"tarun.verma@smartdukaan.com"};
36113 ranu 489
        DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("dd-MMM-yyyy");
34321 ranu 490
 
34903 ranu 491
        // Get all Sales users
34321 ranu 492
        List<AuthUser> authUsers = csService.getAuthUserIds(
493
                ProfitMandiConstants.TICKET_CATEGORY_SALES,
494
                Arrays.asList(EscalationType.L1)
495
        );
496
 
497
        if (authUsers.isEmpty()) {
498
            LOGGER.info("No sales person found.");
499
            return;
500
        }
501
 
502
        List<Integer> salesL1Ids = authUsers.stream().map(AuthUser::getId).collect(Collectors.toList());
503
 
36113 ranu 504
        // Fetch ratings for all Sales L1 for the week
505
        List<SalesRating> feedbackList = salesRatingRepository.selectBySalesL1IdsAndDateRange(salesL1Ids, startOfWeek, endOfWeek);
34321 ranu 506
 
507
        if (feedbackList.isEmpty()) {
36113 ranu 508
            LOGGER.info("No feedback entries found for Sales for the week.");
34321 ranu 509
            return;
510
        }
511
 
512
        // Sort feedback by createTimeStamp DESC
513
        feedbackList.sort((a, b) -> b.getCreateTimeStamp().compareTo(a.getCreateTimeStamp()));
514
 
515
        // Fetch and map FOFO (partner) names
516
        Map<Integer, String> fofoNameMap = new HashMap<>();
517
        for (SalesRating rating : feedbackList) {
518
            int fofoId = rating.getFofoId();
519
            if (!fofoNameMap.containsKey(fofoId)) {
520
                User fofoUser = userRepository.selectById(fofoId);
521
                FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(fofoId);
522
 
523
                String partnerName = fofoUser != null ? fofoUser.getName() : "Unknown Partner";
524
                String storeCode = fofoStore != null ? fofoStore.getCode() : "Unknown Code";
525
 
526
                String displayName = partnerName + " (" + storeCode + ")";
527
                fofoNameMap.put(fofoId, displayName);
528
            }
529
        }
530
 
36113 ranu 531
        // Map Sales L1 ID to name for quick lookup
34321 ranu 532
        Map<Integer, String> salesL1NameMap = authUsers.stream()
533
                .collect(Collectors.toMap(AuthUser::getId, AuthUser::getFullName));
534
 
36113 ranu 535
        // Calculate Sales L1 statistics: average rating and unique partner count
536
        Map<Integer, List<SalesRating>> feedbackBySales = feedbackList.stream()
537
                .collect(Collectors.groupingBy(SalesRating::getSalesL1Id));
538
 
539
        List<SalesWeeklyStats> salesStatsList = new ArrayList<>();
540
        for (Map.Entry<Integer, List<SalesRating>> entry : feedbackBySales.entrySet()) {
541
            int salesL1Id = entry.getKey();
542
            List<SalesRating> ratings = entry.getValue();
543
 
544
            double avgRating = ratings.stream()
545
                    .mapToInt(SalesRating::getRating)
546
                    .average()
547
                    .orElse(0.0);
548
 
549
            long uniquePartnerCount = ratings.stream()
550
                    .map(SalesRating::getFofoId)
551
                    .distinct()
552
                    .count();
553
 
554
            String salesL1Name = salesL1NameMap.getOrDefault(salesL1Id, "Unknown Sales Person");
555
            salesStatsList.add(new SalesWeeklyStats(salesL1Id, salesL1Name, avgRating, (int) uniquePartnerCount));
556
        }
557
 
558
        // Find max partner count for normalization
559
        int maxPartnerCount = salesStatsList.stream()
560
                .mapToInt(SalesWeeklyStats::getPartnerCount)
561
                .max()
562
                .orElse(1);
563
 
564
        // Calculate combined score: 65% partner count + 35% rating
565
        // Score = (0.65 × partnerCount/maxPartnerCount) + (0.35 × avgRating/5)
566
        for (SalesWeeklyStats stats : salesStatsList) {
567
            double partnerScore = (double) stats.getPartnerCount() / maxPartnerCount;
568
            double ratingScore = stats.getAvgRating() / 5.0;
569
            double combinedScore = (0.65 * partnerScore) + (0.35 * ratingScore);
570
            stats.setCombinedScore(combinedScore);
571
        }
572
 
573
        // Sort by combined score DESC and assign rank
574
        salesStatsList.sort((a, b) -> Double.compare(b.getCombinedScore(), a.getCombinedScore()));
575
        for (int i = 0; i < salesStatsList.size(); i++) {
576
            salesStatsList.get(i).setRank(i + 1);
577
        }
578
 
579
        // Create map for quick lookup of stats by Sales L1 ID
580
        Map<Integer, SalesWeeklyStats> salesStatsMap = salesStatsList.stream()
581
                .collect(Collectors.toMap(SalesWeeklyStats::getSalesL1Id, s -> s));
582
 
34321 ranu 583
        // Generate HTML content
584
        StringBuilder emailContent = new StringBuilder();
585
        emailContent.append("<html><body>");
586
        emailContent.append("<p>Dear Team,</p>");
36113 ranu 587
        emailContent.append("<p>Here is the <b>Weekly Sales L1 Rating and Feedback Summary</b> for the week: <b>")
588
                .append(previousMonday.format(dateFormatter))
589
                .append(" to ")
590
                .append(previousSunday.format(dateFormatter))
591
                .append("</b></p>");
34321 ranu 592
 
36113 ranu 593
        // Sales L1 Ranking Summary Table
594
        emailContent.append("<h3>Sales L1 Weekly Rankings</h3>");
595
        emailContent.append("<p style='font-size: 12px; color: #666;'>Rank = 65% Partner Count + 35% Avg Rating</p>");
596
        emailContent.append("<table border='1' cellspacing='0' cellpadding='5' style='border-collapse: collapse;'>");
597
        emailContent.append("<tr style='background-color: #4CAF50; color: white;'>")
598
                .append("<th>Rank</th>")
34321 ranu 599
                .append("<th>Sales L1 Name</th>")
36113 ranu 600
                .append("<th>Partner Count</th>")
601
                .append("<th>Avg Rating</th>")
602
                .append("<th>Score</th>")
603
                .append("</tr>");
604
 
605
        // Already sorted by rank (combined score DESC)
606
        for (SalesWeeklyStats stats : salesStatsList) {
607
            emailContent.append("<tr>")
608
                    .append("<td style='text-align: center; font-weight: bold;'>").append(stats.getRank()).append("</td>")
609
                    .append("<td>").append(stats.getSalesL1Name()).append("</td>")
610
                    .append("<td style='text-align: center;'>").append(stats.getPartnerCount()).append("</td>")
611
                    .append("<td style='text-align: center;'>").append(String.format("%.2f", stats.getAvgRating())).append("</td>")
612
                    .append("<td style='text-align: center;'>").append(String.format("%.2f", stats.getCombinedScore())).append("</td>")
613
                    .append("</tr>");
614
        }
615
        emailContent.append("</table>");
616
 
617
        // Detailed Feedback Table
618
        emailContent.append("<br><h3>Detailed Feedback</h3>");
619
        emailContent.append("<table border='1' cellspacing='0' cellpadding='5' style='border-collapse: collapse;'>");
620
        emailContent.append("<tr style='background-color: #2196F3; color: white;'>")
621
                .append("<th>Sales L1 Name</th>")
622
                .append("<th>Rank</th>")
34321 ranu 623
                .append("<th>Partner Name</th>")
34411 tejus.loha 624
                .append("<th>Partner Category</th>")
34321 ranu 625
                .append("<th>Rating</th>")
626
                .append("<th>Comment</th>")
627
                .append("<th>Date</th>")
628
                .append("</tr>");
629
 
630
        for (SalesRating rating : feedbackList) {
36113 ranu 631
            int salesL1Id = rating.getSalesL1Id();
632
            String salesL1Name = salesL1NameMap.getOrDefault(salesL1Id, "Unknown Sales Person");
34321 ranu 633
            String partnerName = fofoNameMap.getOrDefault(rating.getFofoId(), "Unknown Partner");
36113 ranu 634
            SalesWeeklyStats stats = salesStatsMap.get(salesL1Id);
34411 tejus.loha 635
            PartnerType partnerType = partnerTypeChangeService.getTypeOnDate(rating.getFofoId(), LocalDate.now());
36113 ranu 636
 
34321 ranu 637
            emailContent.append("<tr>")
36113 ranu 638
                    .append("<td>").append(salesL1Name).append("</td>")
639
                    .append("<td style='text-align: center; font-weight: bold;'>").append(stats != null ? stats.getRank() : "-").append("</td>")
34321 ranu 640
                    .append("<td>").append(partnerName).append("</td>")
34411 tejus.loha 641
                    .append("<td>").append(partnerType).append("</td>")
36113 ranu 642
                    .append("<td style='text-align: center;'>").append(rating.getRating()).append("</td>")
34321 ranu 643
                    .append("<td>").append(rating.getComment() != null ? rating.getComment() : "-").append("</td>")
644
                    .append("<td>").append(rating.getCreateTimeStamp().toLocalDate()).append("</td>")
645
                    .append("</tr>");
646
        }
647
 
648
        emailContent.append("</table>");
36113 ranu 649
        emailContent.append("<br><p>Regards,<br>Smart Dukaan Team</p>");
34321 ranu 650
        emailContent.append("</body></html>");
651
 
36113 ranu 652
        String subject = "Weekly Sales L1 Feedback Summary - " + previousMonday.format(dateFormatter) + " to " + previousSunday.format(dateFormatter);
34321 ranu 653
 
654
        List<String> sendTo = new ArrayList<>();
36113 ranu 655
        sendTo.add("kamini.sharma@smartdukaan.com");
34321 ranu 656
 
657
        String[] emailRecipients = sendTo.toArray(new String[0]);
658
 
659
        this.sendMailHtmlFormat(emailRecipients, emailContent.toString(), null, bcc, subject);
660
 
36113 ranu 661
        LOGGER.info("Weekly Sales L1 feedback summary email sent for week: {} to {}", previousMonday, previousSunday);
34321 ranu 662
    }
663
 
36113 ranu 664
    // Inner class to hold Sales L1 weekly statistics
665
    private static class SalesWeeklyStats {
666
        private final int salesL1Id;
667
        private final String salesL1Name;
668
        private final double avgRating;
669
        private final int partnerCount;
670
        private double combinedScore;
671
        private int rank;
672
 
673
        public SalesWeeklyStats(int salesL1Id, String salesL1Name, double avgRating, int partnerCount) {
674
            this.salesL1Id = salesL1Id;
675
            this.salesL1Name = salesL1Name;
676
            this.avgRating = avgRating;
677
            this.partnerCount = partnerCount;
678
        }
679
 
680
        public int getSalesL1Id() { return salesL1Id; }
681
        public String getSalesL1Name() { return salesL1Name; }
682
        public double getAvgRating() { return avgRating; }
683
        public int getPartnerCount() { return partnerCount; }
684
        public double getCombinedScore() { return combinedScore; }
685
        public void setCombinedScore(double combinedScore) { this.combinedScore = combinedScore; }
686
        public int getRank() { return rank; }
687
        public void setRank(int rank) { this.rank = rank; }
688
    }
689
 
34912 ranu 690
    public Map<String, Set<Integer>> generateBiReportHierarchyWise() throws Exception{
691
        List<Integer> categoryIds = Arrays.asList(ProfitMandiConstants.TICKET_CATEGORY_RBM, ProfitMandiConstants.TICKET_CATEGORY_SALES,ProfitMandiConstants.TICKET_CATEGORY_ABM,ProfitMandiConstants.TICKET_CATEGORY_BUSINESSINTELLIGENT);
692
        Map<String, Set<Integer>> storeGuyEntry = csService.getAuthUserPartnerIdMappingByCategoryIds(categoryIds, false);
693
        return storeGuyEntry;
34911 ranu 694
    }
695
 
696
 
697
 
34912 ranu 698
    public void generateBiReportExcel() throws Exception {
34911 ranu 699
 
36975 ranu 700
        long __biReportStartMs = System.currentTimeMillis();
701
        LOGGER.info("[BI_REPORT] START batch-optimized generateBiReportExcel at {}", LocalDateTime.now());
702
 
34741 ranu 703
        LocalDateTime startOfToday;
704
        LocalDateTime previousDay;
34321 ranu 705
 
34741 ranu 706
        if (LocalDate.now().getDayOfMonth() == 1) {
707
            startOfToday = LocalDate.now().minusDays(1).atStartOfDay();
708
            previousDay = startOfToday.with(LocalTime.MAX);
709
        } else {
710
            startOfToday = LocalDate.now().atStartOfDay();
711
            previousDay = startOfToday.with(LocalTime.MAX).minusDays(1);
712
        }
713
 
35239 ranu 714
        Map<Integer, CustomRetailer> customRetailers = retailerService.getAllFofoRetailersInternalFalse();
34912 ranu 715
 
35239 ranu 716
        List<Integer> retailerIds = customRetailers.values().stream()
34903 ranu 717
                .filter(retailer -> {
36973 ranu 718
                    String storeCode = retailer.getCode();
34903 ranu 719
                    return !storeCode.equalsIgnoreCase("UPGBN640") && !storeCode.equalsIgnoreCase("HRYN039");
720
                })
721
                .map(CustomRetailer::getPartnerId)
35239 ranu 722
                .collect(Collectors.toList());
34606 ranu 723
 
36973 ranu 724
        Set<Integer> retailerIdSet = new HashSet<>(retailerIds);
34903 ranu 725
 
34641 ranu 726
        //partner daily investment
34729 amit.gupta 727
        List<Loan> defaultLoans = sdCreditService.getDefaultLoans();
34641 ranu 728
        Map<Integer,List<Loan>> defaultLoanMap = defaultLoans.stream().collect(Collectors.groupingBy(Loan::getFofoId));
34619 ranu 729
 
34641 ranu 730
        Map<Integer, PartnerDailyInvestment> partnerDailyInvestmentMap = new HashMap<>();
731
        List<PartnerDailyInvestment> partnerDailyInvestments = partnerDailyInvestmentRepository
34741 ranu 732
                .selectAll(new ArrayList<>(retailerIds), previousDay.toLocalDate());
34641 ranu 733
        if (!partnerDailyInvestments.isEmpty()) {
734
            partnerDailyInvestmentMap = partnerDailyInvestments.stream()
36973 ranu 735
                    .collect(Collectors.toMap(PartnerDailyInvestment::getFofoId, x -> x));
34641 ranu 736
        }
737
 
34741 ranu 738
        YearMonth currentMonth;
739
        LocalDateTime currentMonthStartDate;
740
        LocalDateTime currentMonthEndDate;
741
 
742
        if (LocalDate.now().getDayOfMonth() == 1) {
743
            currentMonth = YearMonth.now().minusMonths(1);
744
            currentMonthStartDate = currentMonth.atDay(1).atStartOfDay();
745
            currentMonthEndDate = currentMonth.atEndOfMonth().atTime(23, 59, 59);
746
        } else {
747
            currentMonth = YearMonth.now();
748
            currentMonthStartDate = currentMonth.atDay(1).atStartOfDay();
749
            currentMonthEndDate = LocalDate.now().minusDays(1).atTime(23, 59, 59);
750
        }
751
 
34619 ranu 752
        String currentMonthStringValue = String.valueOf(currentMonth);
34606 ranu 753
 
36973 ranu 754
        YearMonth lastMonth = currentMonth.minusMonths(1);
755
        String lastMonthStringValue = String.valueOf(lastMonth);
756
        LocalDateTime lastMontStartDate = lastMonth.atDay(1).atStartOfDay();
757
        LocalDateTime lastMonthEndDate = lastMonth.atEndOfMonth().atTime(23, 59, 59);
34606 ranu 758
 
36973 ranu 759
        YearMonth twoMonthsAgo = currentMonth.minusMonths(2);
760
        String twoMonthAgoStringValue = String.valueOf(twoMonthsAgo);
761
        LocalDateTime twoMonthsAgoStartDate = twoMonthsAgo.atDay(1).atStartOfDay();
762
        LocalDateTime twoMonthsAgoEndDate = twoMonthsAgo.atEndOfMonth().atTime(23, 59, 59);
34606 ranu 763
 
36973 ranu 764
        LocalDateTime dayBeforeStart = previousDay.toLocalDate().atStartOfDay().minusDays(1);
765
        LocalDateTime dayBeforeEnd = previousDay.minusDays(1);
766
        LocalDateTime yesterdayStart = previousDay.toLocalDate().atStartOfDay();
34749 ranu 767
 
36973 ranu 768
        // Returns / RTO — already batched
769
        Map<Integer, Long> currentMonthPartnerReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(currentMonthStartDate, currentMonthEndDate)
770
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
771
        Map<Integer, Long> currentMonthRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(currentMonthStartDate, currentMonthEndDate)
772
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
773
        Map<Integer, Long> yesterdayReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(yesterdayStart, previousDay)
774
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
775
        Map<Integer, Long> yesterdayRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(yesterdayStart, previousDay)
776
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
777
        Map<Integer, Long> dayBeforeYesterdayReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(dayBeforeStart, dayBeforeEnd)
778
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
779
        Map<Integer, Long> dayBeforeYesterdayRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(dayBeforeStart, dayBeforeEnd)
780
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
781
        Map<Integer, Long> lastMonthPartnerReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(lastMontStartDate, lastMonthEndDate)
782
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
783
        Map<Integer, Long> lastMonthRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(lastMontStartDate, lastMonthEndDate)
784
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
785
        Map<Integer, Long> twoMonthAgoPartnerReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(twoMonthsAgoStartDate, twoMonthsAgoEndDate)
786
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
787
        Map<Integer, Long> twoMonthAgoRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(twoMonthsAgoStartDate, twoMonthsAgoEndDate)
788
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
34749 ranu 789
 
36973 ranu 790
        // ---- Batch pre-fetch everything else that was previously per-fofo ----
34749 ranu 791
 
37225 ranu 792
        // Auth hierarchy:
793
        //   RBM  = TICKET_CATEGORY_RBM   → L1, fallback L2
37233 ranu 794
        //   BM   = TICKET_CATEGORY_SALES → L4
795
        //   Sales Manager = TICKET_CATEGORY_SALES → L1, fallback L2, L3, L4, L5
37225 ranu 796
        //   ABM  = removed from report
36973 ranu 797
        Map<Integer, Integer> rbmL1Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_RBM, EscalationType.L1, retailerIdSet);
798
        Map<Integer, Integer> rbmL2Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_RBM, EscalationType.L2, retailerIdSet);
37225 ranu 799
        Map<Integer, Integer> bmMap = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L4, retailerIdSet);
800
        Map<Integer, Integer> salesManagerL1Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L1, retailerIdSet);
801
        Map<Integer, Integer> salesManagerL2Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L2, retailerIdSet);
802
        Map<Integer, Integer> salesManagerL3Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L3, retailerIdSet);
37233 ranu 803
        Map<Integer, Integer> salesManagerL4Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L4, retailerIdSet);
804
        Map<Integer, Integer> salesManagerL5Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L5, retailerIdSet);
34749 ranu 805
 
36973 ranu 806
        Set<Integer> allAuthUserIds = new HashSet<>();
37233 ranu 807
        for (Map<Integer, Integer> m : Arrays.asList(rbmL1Map, rbmL2Map, bmMap, salesManagerL1Map, salesManagerL2Map, salesManagerL3Map, salesManagerL4Map, salesManagerL5Map)) {
36973 ranu 808
            m.values().stream().filter(v -> v != null && v != 0).forEach(allAuthUserIds::add);
809
        }
810
        Map<Integer, AuthUser> authUserMap = allAuthUserIds.isEmpty() ? new HashMap<>()
811
                : authRepository.selectByIds(new ArrayList<>(allAuthUserIds)).stream()
812
                    .collect(Collectors.toMap(AuthUser::getId, u -> u, (a, b) -> a));
34749 ranu 813
 
36973 ranu 814
        // AST batch
815
        Set<Integer> astIds = customRetailers.values().stream()
816
                .map(CustomRetailer::getAstId).filter(id -> id != null && id != 0).collect(Collectors.toSet());
817
        Map<Integer, AST> astMap = astIds.isEmpty() ? new HashMap<>()
818
                : astRepository.selectByIds(new ArrayList<>(astIds)).stream()
819
                    .collect(Collectors.toMap(AST::getId, a -> a, (a, b) -> a));
34749 ranu 820
 
36973 ranu 821
        // FofoStore batch (replaces the two duplicate per-fofo calls)
822
        Map<Integer, FofoStore> fofoStoreMap = fofoStoreRepository.selectByRetailerIds(retailerIds).stream()
823
                .collect(Collectors.toMap(FofoStore::getId, s -> s, (a, b) -> a));
34749 ranu 824
 
36973 ranu 825
        // Users + wallet-creation dates
826
        Map<Integer, User> userMap = userRepository.selectByIds(retailerIds).stream()
827
                .collect(Collectors.toMap(User::getId, u -> u, (a, b) -> a));
828
        Map<Integer, LocalDateTime> walletFirstCreatedMap = userWalletHistoryRepository.selectFirstCreatedDatesForFofoIds(retailerIds);
34606 ranu 829
 
36973 ranu 830
        // Partner type for today
831
        Map<Integer, PartnerType> partnerTypeMap = partnerTypeChangeService.getTypesForFofoIds(retailerIds, LocalDate.now());
34606 ranu 832
 
36973 ranu 833
        // Monthly targets
36980 ranu 834
        Map<Integer, Double> currentTargetMap = monthlyTargetRepository.selectByExactDateAndFofoIds(currentMonth, retailerIds).stream()
36973 ranu 835
                .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
36980 ranu 836
        Map<Integer, Double> lastTargetMap = monthlyTargetRepository.selectByExactDateAndFofoIds(lastMonth, retailerIds).stream()
36973 ranu 837
                .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
36980 ranu 838
        Map<Integer, Double> twoMonthAgoTargetMap = monthlyTargetRepository.selectByExactDateAndFofoIds(twoMonthsAgo, retailerIds).stream()
36973 ranu 839
                .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
34606 ranu 840
 
36973 ranu 841
        // DRR target: RbmTargetService.calculateFofoIdTodayTarget always uses YearMonth.now() internally, regardless of
842
        // the date arg. On the 1st this differs from currentMonth (previous month) — preserve original semantics.
843
        Map<Integer, Double> drrTargetMap = YearMonth.now().equals(currentMonth) ? currentTargetMap
36980 ranu 844
                : monthlyTargetRepository.selectByExactDateAndFofoIds(YearMonth.now(), retailerIds).stream()
36973 ranu 845
                    .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
34606 ranu 846
 
36973 ranu 847
        // Monthly secondary (order value) — batched
848
        Map<Integer, Double> mtdSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
849
                startOfToday.withDayOfMonth(1), previousDay).stream()
850
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
851
        Map<Integer, Double> yesterDaySecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
852
                yesterdayStart, previousDay).stream()
853
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
854
        Map<Integer, Double> dayBeforeSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
855
                dayBeforeStart, dayBeforeEnd).stream()
856
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
857
        Map<Integer, Double> lastMonthSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
858
                lastMontStartDate, lastMonthEndDate).stream()
859
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
860
        Map<Integer, Double> twoMonthAgoSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
861
                twoMonthsAgoStartDate, twoMonthsAgoEndDate).stream()
862
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
34606 ranu 863
 
36973 ranu 864
        // Tertiary MTD / last-month / two-months-ago (all-partners scan via fofoId=0)
865
        LocalDateTime now = LocalDateTime.now();
866
        Map<Integer, Double> mtdSaleTillYesterdayMap = fofoOrderItemRepository.selectSumMopGroupByRetailer(startOfToday.withDayOfMonth(1), previousDay, 0, false);
867
        Map<Integer, Double> lastMonthSaleMap = fofoOrderItemRepository.selectSumMopGroupByRetailer(lastMontStartDate, lastMonthEndDate, 0, false);
868
        Map<Integer, Double> twoMonthAgoSaleMap = fofoOrderItemRepository.selectSumMopGroupByRetailer(twoMonthsAgoStartDate, twoMonthsAgoEndDate, 0, false);
34749 ranu 869
 
36973 ranu 870
        // Monthly activated-but-not-billed
871
        Map<Integer, Map<YearMonth, PartnerWiseActivatedNotBilledTotal>> activatedNotBilledByFofo = new HashMap<>();
872
        for (PartnerWiseActivatedNotBilledTotal t : activatedImeiRepository.getTotalMonthlyActivatedNotBilledForFofoIds(retailerIds, twoMonthsAgoStartDate)) {
873
            activatedNotBilledByFofo.computeIfAbsent(t.getFofoId(), k -> new HashMap<>())
874
                    .put(YearMonth.parse(t.getYearMonth()), t);
875
        }
34606 ranu 876
 
36973 ranu 877
        // Brand-wise tertiary MTD (per fofo → brand → amount)
878
        Map<Integer, Map<String, Double>> brandTertiaryByFofo = fofoOrderItemRepository.selectSumAmountGroupByBrandForFofoIds(currentMonthStartDate, currentMonthEndDate, retailerIds);
34749 ranu 879
 
36973 ranu 880
        // Brand-wise secondary billed (MTD)
881
        Map<Integer, Map<String, Long>> brandBilledByFofo = new HashMap<>();
882
        for (BrandWiseModel m : orderRepository.selectAllBilledByCategoryOrderGroupByBrandFofoIds(retailerIds, currentMonthStartDate, currentMonthEndDate, Arrays.asList(10006, 10001))) {
883
            brandBilledByFofo.computeIfAbsent(m.getFofoId(), k -> new HashMap<>()).merge(m.getBrand(), m.getAmount(), Long::sum);
884
        }
885
        // Brand-wise returns + RTO returns (MTD)
886
        Map<Integer, Map<String, Double>> brandReturnByFofo = new HashMap<>();
887
        for (BrandWiseReturnInfo r : returnOrderInfoRepository.selectAllBrandWiseByBetweenDateForFofoIds(currentMonthStartDate, currentMonthEndDate.plusDays(1), retailerIds)) {
888
            brandReturnByFofo.computeIfAbsent(r.getRetailerId(), k -> new HashMap<>()).merge(r.getBrand(), r.getReturnAmount(), Double::sum);
889
        }
890
        Map<Integer, Map<String, Double>> brandRtoReturnByFofo = new HashMap<>();
891
        for (BrandWiseReturnInfo r : returnOrderInfoRepository.selectAllBrandWiseRTORefundByBetweenDateForFofoIds(currentMonthStartDate, currentMonthEndDate.plusDays(1), retailerIds)) {
892
            brandRtoReturnByFofo.computeIfAbsent(r.getRetailerId(), k -> new HashMap<>()).merge(r.getBrand(), r.getReturnAmount(), Double::sum);
893
        }
894
 
895
        // Active loans grouped per fofo, plus batched loanId → sum.
896
        // The single-fofo selectAllActiveLoan(fofoId) additionally filters pendingAmount > 0 — must preserve.
897
        Map<Integer, List<Loan>> activeLoansByFofo = loanRepository.selectAllActiveLoan().stream()
898
                .filter(l -> retailerIdSet.contains(l.getFofoId())
899
                        && l.getPendingAmount() != null
900
                        && l.getPendingAmount().doubleValue() > 0)
901
                .collect(Collectors.groupingBy(Loan::getFofoId));
902
        Set<Integer> loanIdsForSum = new HashSet<>();
903
        activeLoansByFofo.values().forEach(list -> list.forEach(l -> loanIdsForSum.add(l.getId())));
904
        defaultLoans.forEach(l -> loanIdsForSum.add(l.getId()));
905
        Map<Integer, Double> loanStatementSumByLoanId = loanStatementRepository.sumAmountByLoanIds(new ArrayList<>(loanIdsForSum));
906
 
907
        // Last order per fofo (batched)
908
        Map<Integer, Integer> lastOrderIdByFofo = orderRepository.getLastOrderByFofoIds(retailerIds);
909
        Set<Integer> lastOrderIds = lastOrderIdByFofo.values().stream().filter(id -> id != null && id != 0).collect(Collectors.toSet());
910
        Map<Integer, Order> lastOrderById = new HashMap<>();
911
        for (Integer oid : lastOrderIds) {
912
            Order o = orderRepository.selectById(oid);
913
            if (o != null) lastOrderById.put(oid, o);
914
        }
915
 
916
        // DRR precomputation — the two dates we ever call with
917
        long day1RemainingDays = rbmTargetService.getRemainingDaysInMonth(currentMonth.atDay(1));
918
        long todayRemainingDays = rbmTargetService.getRemainingDaysInMonth(startOfToday.toLocalDate());
919
 
36975 ranu 920
        LOGGER.info("[BI_REPORT] batch pre-fetch complete in {}ms; retailers={}, entering per-fofo loop",
921
                System.currentTimeMillis() - __biReportStartMs, retailerIds.size());
922
        long __biReportLoopStartMs = System.currentTimeMillis();
923
 
34648 ranu 924
        Map<Integer , String> assessmentMap = new HashMap<>();
925
        Map<Integer , String> zeroBillingMap = new HashMap<>();
926
        Map<Integer , Float> billingNeededMap = new HashMap<>();
927
        Map<Integer , Integer> countAMap = new HashMap<>();
34619 ranu 928
        Map<Integer , BIRetailerModel> biRetailerModelMap = new HashMap<>();
34641 ranu 929
        Map<Integer , FofoInvestmentModel> biInvestmentModelMap = new HashMap<>();
34619 ranu 930
        Map<Integer, Map<YearMonth, BiSecondaryModel>> allRetailerMonthlyData = new HashMap<>();
931
        Map<Integer,Double> fofoTotalStockPriceMap = new HashMap<>();
932
        Map<Integer,Map<String, BrandStockPrice>> fofoBrandStockPriceMap = new HashMap<>();
34641 ranu 933
        Map<Integer,Long> fofoTotalMtdSecondaryMap = new HashMap<>();
34749 ranu 934
        Map<Integer,Long> fofoYesterdaySecondaryMap = new HashMap<>();
935
        Map<Integer,Long> fofoDayBeforeYesterdaySecondaryMap = new HashMap<>();
34641 ranu 936
        Map<Integer,Map<String, Long>> fofoBrandWiseMtdSecondaryMap = new HashMap<>();
937
        Map<Integer,Double> fofoTotalMtdTertiaryMap = new HashMap<>();
938
        Map<Integer,Map<String, Double>> fofoBrandMtdTertiaryMap = new HashMap<>();
939
 
36973 ranu 940
        for (Integer fofoId : retailerIds) {
941
            // resolve auth-user names from batched maps
34619 ranu 942
            String rbmName = "";
36973 ranu 943
            int rbmL1 = rbmL1Map.getOrDefault(fofoId, 0);
944
            if (rbmL1 != 0 && authUserMap.get(rbmL1) != null) {
945
                rbmName = authUserMap.get(rbmL1).getFullName();
946
            } else {
947
                int rbmL2 = rbmL2Map.getOrDefault(fofoId, 0);
948
                if (rbmL2 != 0 && authUserMap.get(rbmL2) != null) {
949
                    rbmName = authUserMap.get(rbmL2).getFullName();
34677 ranu 950
                }
34619 ranu 951
            }
36973 ranu 952
            String bmName = "";
953
            int bmId = bmMap.getOrDefault(fofoId, 0);
954
            if (bmId != 0 && authUserMap.get(bmId) != null) {
955
                bmName = authUserMap.get(bmId).getFullName();
34619 ranu 956
            }
37233 ranu 957
            // Sales Manager: L1 → L2 → L3 → L4 → L5 fallback in SALES category
37225 ranu 958
            String managerName = "";
959
            int managerL1 = salesManagerL1Map.getOrDefault(fofoId, 0);
960
            int managerL2 = salesManagerL2Map.getOrDefault(fofoId, 0);
961
            int managerL3 = salesManagerL3Map.getOrDefault(fofoId, 0);
37233 ranu 962
            int managerL4 = salesManagerL4Map.getOrDefault(fofoId, 0);
963
            int managerL5 = salesManagerL5Map.getOrDefault(fofoId, 0);
37351 ranu 964
            String salesL3Name = (managerL3 != 0 && authUserMap.get(managerL3) != null)
965
                    ? authUserMap.get(managerL3).getFullName() : "";
37225 ranu 966
            if (managerL1 != 0 && authUserMap.get(managerL1) != null) {
967
                managerName = authUserMap.get(managerL1).getFullName();
968
            } else if (managerL2 != 0 && authUserMap.get(managerL2) != null) {
969
                managerName = authUserMap.get(managerL2).getFullName();
970
            } else if (managerL3 != 0 && authUserMap.get(managerL3) != null) {
971
                managerName = authUserMap.get(managerL3).getFullName();
37233 ranu 972
            } else if (managerL4 != 0 && authUserMap.get(managerL4) != null) {
973
                managerName = authUserMap.get(managerL4).getFullName();
974
            } else if (managerL5 != 0 && authUserMap.get(managerL5) != null) {
975
                managerName = authUserMap.get(managerL5).getFullName();
34606 ranu 976
            }
977
 
36973 ranu 978
            AST ast = astMap.get(customRetailers.get(fofoId).getAstId());
979
            PartnerType partnerTypeThisMonth = partnerTypeMap.get(fofoId);
34606 ranu 980
 
981
            BIRetailerModel biRetailerModel = new BIRetailerModel();
982
            biRetailerModel.setBmName(bmName);
983
            biRetailerModel.setCode(customRetailers.get(fofoId).getCode());
36973 ranu 984
            biRetailerModel.setArea(ast != null ? ast.getArea() : "-");
985
 
986
            FofoStore fofoStore = fofoStoreMap.get(fofoId);
987
            String retailerStatus;
988
            if (fofoStore == null) {
989
                retailerStatus = "-";
990
            } else if (!fofoStore.isActive()) {
34738 ranu 991
                retailerStatus = "INACTIVE";
36973 ranu 992
            } else {
993
                retailerStatus = String.valueOf(fofoStore.getActivationType());
34738 ranu 994
            }
34606 ranu 995
            biRetailerModel.setCity(customRetailers.get(fofoId).getAddress().getCity());
996
            biRetailerModel.setStoreName(customRetailers.get(fofoId).getBusinessName());
34738 ranu 997
            biRetailerModel.setStatus(retailerStatus);
34606 ranu 998
            biRetailerModel.setCategory(String.valueOf(partnerTypeThisMonth));
999
            biRetailerModel.setSalesManager(managerName);
37351 ranu 1000
            biRetailerModel.setSalesL3Manager(salesL3Name);
34606 ranu 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
                "","","","",
37351 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",
37351 ranu 1196
                "BM","Sales L3 Manager","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(),
37351 ranu 1242
                    retailer.getSalesL3Manager(),
34758 ranu 1243
                    fofoId ,
1244
                    "https://partners.smartdukaan.com/partnerPerformance?fofoId="+fofoId,
36973 ranu 1245
                    walletCreationDate != null ? walletCreationDate.format(formatter) : "-",
1246
                    (user != null && user.getCreateTimestamp() != null) ? user.getCreateTimestamp().format(formatter) : "-",
34758 ranu 1247
                    retailer.getCode(),
1248
                    retailer.getArea(),
1249
                    retailer.getCity(),
1250
                    retailer.getStoreName(),
1251
                    retailer.getStatus(),
1252
                    retailer.getCategory(),
1253
                    retailer.getSalesManager(),
37225 ranu 1254
                    retailer.getRbm()
34677 ranu 1255
 
34619 ranu 1256
            ));
1257
 
34677 ranu 1258
 
1259
            // Two Months Ago
34619 ranu 1260
            row.addAll(Arrays.asList(
34677 ranu 1261
                    twoAgo.getSecondaryTarget(),
1262
                    twoAgo.getSecondaryAchieved(),
1263
                    twoAgo.getSecondaryReturn(),
1264
                    twoAgo.getNetSecondary(),
34704 ranu 1265
                    twoAgo.getSecondaryAchievedPercent()+"%",
34677 ranu 1266
                    twoAgo.getTertiary(),
1267
                    twoAgo.getTertiaryUnBilled()
34619 ranu 1268
            ));
1269
 
1270
            // Last Month
1271
            row.addAll(Arrays.asList(
1272
                    last.getSecondaryTarget(),
1273
                    last.getSecondaryAchieved(),
1274
                    last.getSecondaryReturn(),
1275
                    last.getNetSecondary(),
34704 ranu 1276
                    last.getSecondaryAchievedPercent()+"%",
34619 ranu 1277
                    last.getTertiary(),
1278
                    last.getTertiaryUnBilled()
1279
            ));
1280
 
34677 ranu 1281
            // Current Month
34619 ranu 1282
            row.addAll(Arrays.asList(
34677 ranu 1283
                    current.getSecondaryTarget(),
1284
                    current.getSecondaryAchieved(),
1285
                    current.getSecondaryReturn(),
1286
                    current.getNetSecondary(),
34704 ranu 1287
                    current.getSecondaryAchievedPercent()+"%",
34677 ranu 1288
                    current.getTertiary(),
1289
                    current.getTertiaryUnBilled()
34619 ranu 1290
            ));
34677 ranu 1291
 
1292
 
1293
 
34641 ranu 1294
            FofoInvestmentModel fofoInvestmentModelValue = biInvestmentModelMap.get(fofoId);
1295
            if(fofoInvestmentModelValue != null){
1296
                row.addAll(Arrays.asList(
1297
                        fofoInvestmentModelValue.getCounterPotential(),
1298
                        fofoInvestmentModelValue.getShortInvestment(),
1299
                        fofoInvestmentModelValue.getDefaultLoan(),
34730 ranu 1300
                        fofoInvestmentModelValue.getInvestmentLevel() +"%",
34743 ranu 1301
                        fofoInvestmentModelValue.getActiveLoan(),
34641 ranu 1302
                        fofoInvestmentModelValue.getPoValue(),
1303
                        fofoInvestmentModelValue.getAgreedInvestment(),
1304
                        fofoInvestmentModelValue.getWallet(),
1305
                        fofoInvestmentModelValue.getPoAndBilled(),
1306
                        fofoInvestmentModelValue.getMonthBeginingDrr(),
1307
                        fofoInvestmentModelValue.getRequiredDrr(),
34704 ranu 1308
                        fofoInvestmentModelValue.getDrrPercent()+"%",
34641 ranu 1309
                        fofoInvestmentModelValue.getLastBillingDone(),
1310
                        fofoInvestmentModelValue.getSlab()
1311
                ));
1312
            }else {
1313
                row.addAll(Arrays.asList(
1314
                        "-","-","-","-","-","-","-","-","-","-","-",""
1315
                ));
1316
            }
1317
 
1318
            Map<String, BrandStockPrice> brandStockMap = fofoBrandStockPriceMap.get(fofoId);
34619 ranu 1319
            row.addAll(Arrays.asList(
1320
                    fofoTotalStockPriceMap.getOrDefault(fofoId, 0.0),
34641 ranu 1321
                    brandStockMap.get("Apple") != null ? brandStockMap.get("Apple").getTotalValue() : 0.0,
1322
                    brandStockMap.get("Xiaomi") != null ? brandStockMap.get("Xiaomi").getTotalValue() : 0.0,
1323
                    brandStockMap.get("Vivo") != null ? brandStockMap.get("Vivo").getTotalValue() : 0.0,
1324
                    brandStockMap.get("Tecno") != null ? brandStockMap.get("Tecno").getTotalValue() : 0.0,
36193 ranu 1325
                    brandStockMap.get("Motorola") != null ? brandStockMap.get("Motorola").getTotalValue() : 0.0,
34641 ranu 1326
                    brandStockMap.get("Samsung") != null ? brandStockMap.get("Samsung").getTotalValue() : 0.0,
1327
                    brandStockMap.get("Realme") != null ? brandStockMap.get("Realme").getTotalValue() : 0.0,
1328
                    brandStockMap.get("Oppo") != null ? brandStockMap.get("Oppo").getTotalValue() : 0.0,
1329
                    brandStockMap.get("OnePlus") != null ? brandStockMap.get("OnePlus").getTotalValue() : 0.0,
34721 ranu 1330
                    brandStockMap.get("POCO") != null ? brandStockMap.get("POCO").getTotalValue() : 0.0,
34641 ranu 1331
                    brandStockMap.get("Lava") != null ? brandStockMap.get("Lava").getTotalValue() : 0.0,
1332
                    brandStockMap.get("Itel") != null ? brandStockMap.get("Itel").getTotalValue() : 0.0,
1333
                    brandStockMap.get("Almost New") != null ? brandStockMap.get("Almost New").getTotalValue() : 0.0
34619 ranu 1334
            ));
1335
 
34641 ranu 1336
            Map<String, Long> brandSecondaryMap = fofoBrandWiseMtdSecondaryMap.get(fofoId);
1337
            row.addAll(Arrays.asList(
34648 ranu 1338
                    fofoTotalMtdSecondaryMap.get(fofoId),
34641 ranu 1339
                    brandSecondaryMap.getOrDefault("Apple", 0L),
1340
                    brandSecondaryMap.getOrDefault("Xiaomi", 0L),
1341
                    brandSecondaryMap.getOrDefault("Vivo", 0L),
1342
                    brandSecondaryMap.getOrDefault("Tecno", 0L),
36193 ranu 1343
                    brandSecondaryMap.getOrDefault("Motorola", 0L),
34641 ranu 1344
                    brandSecondaryMap.getOrDefault("Samsung", 0L),
1345
                    brandSecondaryMap.getOrDefault("Realme", 0L),
1346
                    brandSecondaryMap.getOrDefault("Oppo", 0L),
1347
                    brandSecondaryMap.getOrDefault("OnePlus", 0L),
34721 ranu 1348
                    brandSecondaryMap.getOrDefault("POCO", 0L),
34641 ranu 1349
                    brandSecondaryMap.getOrDefault("Lava", 0L),
1350
                    brandSecondaryMap.getOrDefault("Itel", 0L),
1351
                    brandSecondaryMap.getOrDefault("Almost New", 0L)
1352
            ));
1353
 
1354
            Map<String, Double> brandTertiaryMap = fofoBrandMtdTertiaryMap.get(fofoId);
1355
            row.addAll(Arrays.asList(
34648 ranu 1356
                    fofoTotalMtdTertiaryMap.get(fofoId),
34641 ranu 1357
                    brandTertiaryMap.getOrDefault("Apple", 0d),
1358
                    brandTertiaryMap.getOrDefault("Xiaomi", 0d),
1359
                    brandTertiaryMap.getOrDefault("Vivo", 0d),
1360
                    brandTertiaryMap.getOrDefault("Tecno", 0d),
36193 ranu 1361
                    brandTertiaryMap.getOrDefault("Motorola", 0d),
34641 ranu 1362
                    brandTertiaryMap.getOrDefault("Samsung", 0d),
1363
                    brandTertiaryMap.getOrDefault("Realme", 0d),
1364
                    brandTertiaryMap.getOrDefault("Oppo", 0d),
1365
                    brandTertiaryMap.getOrDefault("OnePlus", 0d),
34721 ranu 1366
                    brandTertiaryMap.getOrDefault("POCO", 0d),
34641 ranu 1367
                    brandTertiaryMap.getOrDefault("Lava", 0d),
1368
                    brandTertiaryMap.getOrDefault("Itel", 0d),
1369
                    brandTertiaryMap.getOrDefault("Almost New", 0d)
1370
            ));
34749 ranu 1371
 
1372
            row.addAll(Arrays.asList(
1373
                    fofoYesterdaySecondaryMap.get(fofoId),
1374
                    fofoDayBeforeYesterdaySecondaryMap.get(fofoId)
1375
            ));
34641 ranu 1376
            rows.add(row);
34619 ranu 1377
        }
1378
 
36987 ranu 1379
        Map<String, Set<Integer>> storeGuyMap = this.generateBiReportHierarchyWise();
34619 ranu 1380
 
35239 ranu 1381
        for (Map.Entry<String, Set<Integer>> storeGuyEntry : storeGuyMap.entrySet()) {
34912 ranu 1382
            String storeGuyEmail = storeGuyEntry.getKey();
1383
            Set<Integer> fofoIds = storeGuyEntry.getValue();
1384
            String[] sendToArray = new String[]{storeGuyEmail};
34911 ranu 1385
 
34912 ranu 1386
            List<List<?>> filteredRows = rows.stream()
37351 ranu 1387
                    .filter(row -> row.size() > 6 && fofoIds.contains((Integer) row.get(6)))
34912 ranu 1388
                    .collect(Collectors.toList());
1389
            this.sendMailToUser(headerGroup,filteredRows,sendToArray);
36987 ranu 1390
        }
34912 ranu 1391
 
36193 ranu 1392
        this.sendMailToUser(
1393
                headerGroup,
1394
                rows,
1395
                new String[]{
36987 ranu 1396
                        "ranu.rajput@smartdukaan.com",
1397
                        "niranjan.kala@smartdukaan.com",
37441 ranu 1398
                        "gaurav.mathur1@smartdukaan.com",
36216 ranu 1399
                        "nivesh.mathur@smartdukaan.com",
36193 ranu 1400
                        "deena.nath@smartdukaan.com",
36987 ranu 1401
                        "santosh.giri@smartdukaan.com"
36193 ranu 1402
                }
1403
        );
34912 ranu 1404
 
36975 ranu 1405
        LOGGER.info("[BI_REPORT] DONE batch-optimized generateBiReportExcel; totalMs={}, retailers={}, rows={}",
1406
                System.currentTimeMillis() - __biReportStartMs, retailerIds.size(), rows.size());
34912 ranu 1407
 
36975 ranu 1408
 
34911 ranu 1409
    }
1410
 
1411
    private  void sendMailToUser(List<List<String>> headerGroup,List<List<?>> rows, String[] sendToArray ) throws Exception {
34641 ranu 1412
        // Send to email
1413
//        ByteArrayOutputStream csvStream = FileUtil.getCSVByteStreamWithMultiHeaders(headerGroup, rows);
1414
        ByteArrayOutputStream csvStream = getExcelStreamWithMultiHeaders(headerGroup, rows);
1415
        String fileName = "BI-Retailer-Monthly-Report-" + FormattingUtils.formatDate(LocalDateTime.now()) + ".xlsx";
34619 ranu 1416
        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 1417
    }
34619 ranu 1418
 
1419
 
34641 ranu 1420
    public static ByteArrayOutputStream getExcelStreamWithMultiHeaders(List<List<String>> headerGroup, List<List<?>> rows) {
1421
        Workbook workbook = new XSSFWorkbook();
1422
        Sheet sheet = workbook.createSheet("BI Report");
34715 ranu 1423
        CreationHelper creationHelper = workbook.getCreationHelper();
34641 ranu 1424
        int rowIndex = 0;
34606 ranu 1425
 
34641 ranu 1426
        CellStyle centeredStyle = workbook.createCellStyle();
1427
        centeredStyle.setAlignment(HorizontalAlignment.CENTER); // Center horizontally
1428
        centeredStyle.setVerticalAlignment(VerticalAlignment.CENTER); // Center vertically
34606 ranu 1429
 
34641 ranu 1430
    // Optional: bold font
1431
        Font font1 = workbook.createFont();
1432
        font1.setBold(true);
1433
        centeredStyle.setFont(font1);
34606 ranu 1434
 
34619 ranu 1435
 
34641 ranu 1436
 
1437
        // Create styles
1438
        Map<String, CellStyle> headerStyles = new HashMap<>();
1439
 
1440
        // fontPurpleStyle
1441
        CellStyle purpleStyle = workbook.createCellStyle();
1442
        purpleStyle.setFillForegroundColor(IndexedColors.ROSE.getIndex());
1443
        purpleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1444
        purpleStyle.setFont(font1);
1445
        headerStyles.put("Assessment", purpleStyle);
1446
        headerStyles.put("Zero billing", purpleStyle);
1447
        headerStyles.put("Billing needed", purpleStyle);
1448
        headerStyles.put("Counta", purpleStyle);
1449
        headerStyles.put("MONTH BEGINNING DRR", purpleStyle);
1450
        headerStyles.put("REQ DRR", purpleStyle);
1451
        headerStyles.put("Drr %", purpleStyle);
1452
 
1453
        // Light Blue
1454
        CellStyle blueStyle = workbook.createCellStyle();
1455
        blueStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
1456
        blueStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1457
        blueStyle.setFont(font1);
1458
        headerStyles.put("Code", blueStyle);
1459
        headerStyles.put("Store Name", blueStyle);
1460
        headerStyles.put("City", blueStyle);
1461
        headerStyles.put("Area", blueStyle);
1462
        headerStyles.put("BM", blueStyle);
1463
        headerStyles.put("RBM", blueStyle);
1464
        headerStyles.put("Sales Manager", blueStyle);
37351 ranu 1465
        headerStyles.put("Sales L3 Manager", blueStyle);
34641 ranu 1466
        headerStyles.put("Status", blueStyle);
1467
        headerStyles.put("Category", blueStyle);
34715 ranu 1468
        headerStyles.put("Wallet Date", blueStyle);
1469
        headerStyles.put("Creation Date", blueStyle);
1470
        headerStyles.put("Partner Id", blueStyle);
34641 ranu 1471
 
34715 ranu 1472
        //for link
1473
        // Create hyperlink style
1474
        CellStyle hyperlinkStyle = workbook.createCellStyle();
1475
        Font hlinkFont = workbook.createFont();
1476
        hlinkFont.setUnderline(Font.U_SINGLE);
1477
        hlinkFont.setColor(IndexedColors.BLUE.getIndex());
1478
        hyperlinkStyle.setFont(hlinkFont);
1479
 
1480
 
34641 ranu 1481
        // Light Yellow
1482
        CellStyle yellowStyle = workbook.createCellStyle();
1483
        yellowStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
1484
        yellowStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1485
        yellowStyle.setFont(font1);
1486
        headerStyles.put("Last billing Done", yellowStyle);
1487
        headerStyles.put("Total Stock", yellowStyle);
1488
 
1489
        // Light Orange
1490
        CellStyle orangeStyle = workbook.createCellStyle();
1491
        orangeStyle.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex());
1492
        orangeStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1493
        orangeStyle.setFont(font1);
1494
        headerStyles.put("Total Tertiary", orangeStyle);
1495
        headerStyles.put("Total Secondary", orangeStyle);
1496
        headerStyles.put("Default", orangeStyle);
1497
 
1498
 
1499
        // Light green
1500
        CellStyle lightGreenStyle = workbook.createCellStyle();
1501
        lightGreenStyle.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());
1502
        lightGreenStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1503
        lightGreenStyle.setFont(font1);
1504
        headerStyles.put("Short investment", lightGreenStyle);
1505
        headerStyles.put("INVESTMENT LEVEL", lightGreenStyle);
1506
        headerStyles.put("Loan", lightGreenStyle);
1507
        headerStyles.put("PO value", lightGreenStyle);
1508
        headerStyles.put("Agreed investment", lightGreenStyle);
1509
        headerStyles.put("Wallet", lightGreenStyle);
1510
        headerStyles.put("po+bill", lightGreenStyle);
1511
 
1512
        // Light Green
1513
        CellStyle secondary1 = createStyle(workbook, IndexedColors.LIGHT_GREEN);
1514
        CellStyle secondary2 = createStyle(workbook, IndexedColors.LIGHT_YELLOW);
1515
        CellStyle secondary3 = createStyle(workbook, IndexedColors.LIGHT_ORANGE);
1516
 
1517
        Map<String, CellStyle> brandStyles = new HashMap<>();
1518
        brandStyles.put("Apple", createStyle(workbook, IndexedColors.GREY_25_PERCENT));
1519
        brandStyles.put("Xiaomi", createStyle(workbook, IndexedColors.ORANGE));
1520
        brandStyles.put("Vivo", createStyle(workbook, IndexedColors.SKY_BLUE));
1521
        brandStyles.put("Tecno", createStyle(workbook, IndexedColors.LIGHT_BLUE));
36193 ranu 1522
        brandStyles.put("Motorola", createStyle(workbook, IndexedColors.LIGHT_GREEN));
34641 ranu 1523
        brandStyles.put("Samsung", createStyle(workbook, IndexedColors.ROYAL_BLUE));
1524
        brandStyles.put("Realme", createStyle(workbook, IndexedColors.YELLOW));
1525
        brandStyles.put("Oppo", createStyle(workbook, IndexedColors.LIGHT_GREEN));
1526
        brandStyles.put("OnePlus", createStyle(workbook, IndexedColors.RED));
34721 ranu 1527
        brandStyles.put("POCO", createStyle(workbook, IndexedColors.ORANGE));
34641 ranu 1528
        brandStyles.put("Lava", createStyle(workbook, IndexedColors.LIGHT_YELLOW));
1529
        brandStyles.put("Itel", createStyle(workbook, IndexedColors.LIGHT_YELLOW));
1530
        brandStyles.put("Almost New", createStyle(workbook, IndexedColors.WHITE));
1531
 
1532
 
1533
        CellStyle defaultHeaderStyle = workbook.createCellStyle();
1534
        defaultHeaderStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
1535
        defaultHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1536
        defaultHeaderStyle.setFont(font1);
1537
 
34749 ranu 1538
        CellStyle numberStyle = workbook.createCellStyle();
1539
        DataFormat format = workbook.createDataFormat();
1540
        numberStyle.setDataFormat(format.getFormat("#,##0")); // or "#,##0.00" for two decimals
34641 ranu 1541
 
34749 ranu 1542
 
1543
 
34641 ranu 1544
        Map<String, Integer> headerCount = new HashMap<>();
1545
 
1546
        for (int headerRowIndex = 0; headerRowIndex < headerGroup.size(); headerRowIndex++) {
1547
            List<String> headerRow = headerGroup.get(headerRowIndex);
1548
            Row row = sheet.createRow(rowIndex++);
1549
 
1550
            for (int i = 0; i < headerRow.size(); i++) {
1551
                String headerText = headerRow.get(i);
1552
                sheet.setColumnWidth(i, 25 * 256);
1553
                row.setHeightInPoints(20); // 25-point height
1554
                Cell cell = row.createCell(i);
1555
                cell.setCellValue(headerText);
1556
                cell.setCellStyle(centeredStyle);
1557
                // Count how many times this header has appeared
1558
                int count = headerCount.getOrDefault(headerText, 0) + 1;
1559
                headerCount.put(headerText, count);
1560
                // Apply special style for repeated headers
1561
                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")) {
1562
                    if (count == 1) {
1563
                        cell.setCellStyle(secondary1);
1564
                    } else if (count == 2) {
1565
                        cell.setCellStyle(secondary2);
1566
                    } else if (count == 3) {
1567
                        cell.setCellStyle(secondary3);
1568
                    }
1569
                }
1570
                // Brand header styling (apply only for the 2nd row of headers)
1571
                else if (headerRowIndex == 1 && brandStyles.containsKey(headerText)) {
1572
                    cell.setCellStyle(brandStyles.get(headerText));
1573
                }else if (headerStyles.containsKey(headerText)) {
1574
                    cell.setCellStyle(headerStyles.get(headerText));
1575
                } else {
1576
                    cell.setCellStyle(defaultHeaderStyle); // default style for others
1577
                }
1578
            }
1579
        }
1580
 
1581
        // Write data rows
1582
        for (List<?> dataRow : rows) {
1583
            Row row = sheet.createRow(rowIndex++);
1584
            for (int i = 0; i < dataRow.size(); i++) {
1585
                Cell cell = row.createCell(i);
1586
                Object value = dataRow.get(i);
34715 ranu 1587
 
37351 ranu 1588
                if (i == 7 && value != null) { // Assuming column 7 is "Link"
34715 ranu 1589
                    Hyperlink hyperlink = creationHelper.createHyperlink(HyperlinkType.URL);
1590
                    hyperlink.setAddress(value.toString());
34719 ranu 1591
                    cell.setCellValue("View Link"); // Display text
34715 ranu 1592
                    cell.setHyperlink(hyperlink);
1593
                    cell.setCellStyle(hyperlinkStyle);
34719 ranu 1594
                } else if (value instanceof Number) {
34749 ranu 1595
                    double numeric = ((Number) value).doubleValue();
1596
                    cell.setCellValue(Math.round(numeric));
1597
                    cell.setCellStyle(numberStyle);
34715 ranu 1598
                } else {
1599
                    cell.setCellValue(value != null ? value.toString() : "");
1600
                }
34641 ranu 1601
            }
34719 ranu 1602
 
34641 ranu 1603
        }
1604
 
1605
        // Auto-size columns
1606
        if (!rows.isEmpty()) {
1607
            for (int i = 0; i < rows.get(0).size(); i++) {
1608
                sheet.autoSizeColumn(i);
1609
            }
1610
        }
1611
 
1612
        // Output as ByteArray
1613
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
1614
            workbook.write(outputStream);
1615
            workbook.close();
1616
            return outputStream;
1617
        } catch (IOException e) {
1618
            throw new RuntimeException("Failed to generate Excel file", e);
1619
        }
1620
    }
1621
 
1622
 
1623
    private static CellStyle createStyle(Workbook workbook, IndexedColors color) {
1624
        CellStyle style = workbook.createCellStyle();
1625
        style.setFillForegroundColor(color.getIndex());
1626
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1627
        Font font = workbook.createFont();
1628
        font.setBold(true);
1629
        style.setFont(font);
1630
        return style;
1631
    }
1632
 
1633
 
34758 ranu 1634
    public void stockAlertMailToRetailer() throws Exception {
1635
 
1636
        Map<Integer, CustomRetailer> customRetailers = retailerService.getFofoRetailers(true);
1637
 
1638
        List<Integer> retailerIds = customRetailers.values().stream().map(CustomRetailer::getPartnerId).collect(Collectors.toList());
1639
 
1640
        for(Integer fofoId : retailerIds){
1641
            List<String> statusOrder = Arrays.asList("HID", "FASTMOVING", "RUNNING", "SLOWMOVING", "OTHER");
1642
            FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(fofoId);
1643
            List<PartnerWarehouseStockSummaryModel> partnerWarehouseStockSummaryModels = saholicInventoryService.getSaholicAndPartnerStock(fofoId, fofoStore.getWarehouseId());
1644
 
1645
            List<PartnerWarehouseStockAgingSummaryModel> partnerWarehouseStockAgingSummaryModelList = new ArrayList<>();
1646
 
1647
            Set<Integer> catalogIds = partnerWarehouseStockSummaryModels.stream().map(x -> x.getCatalogId()).collect(Collectors.toSet());
1648
 
1649
            List<Integer> catalogsList = new ArrayList<>(catalogIds);
1650
 
1651
            Map<Integer, TagListing> tagListingsMap = tagListingRepository.selectAllByCatalogIds(catalogsList);
1652
 
1653
            List<CatalogAgingModel> catalogAgingModels = ageingService.getCatalogsAgingByWarehouse(catalogIds, fofoStore.getWarehouseId());
1654
 
1655
            Map<Integer, CatalogAgingModel> catalogAgingModelMap = catalogAgingModels.stream().collect(Collectors.toMap(x -> x.getCatalogId(), x -> x));
1656
 
1657
            for (PartnerWarehouseStockSummaryModel stockSummary : partnerWarehouseStockSummaryModels) {
1658
 
1659
                PartnerWarehouseStockAgingSummaryModel partnerWarehouseStockAgingSummaryModel = new PartnerWarehouseStockAgingSummaryModel();
1660
                partnerWarehouseStockAgingSummaryModel.setCatalogId(stockSummary.getCatalogId());
1661
                partnerWarehouseStockAgingSummaryModel.setBrand(stockSummary.getBrand());
1662
                partnerWarehouseStockAgingSummaryModel.setModelNumber(stockSummary.getModelNumber());
1663
                partnerWarehouseStockAgingSummaryModel.setNetAvailability(stockSummary.getShaholicNetAvailability());
1664
                partnerWarehouseStockAgingSummaryModel.setPartnerStockAvailability(stockSummary.getPartnerFullFilledQty());
1665
                partnerWarehouseStockAgingSummaryModel.setPartnerCurrentAvailability(stockSummary.getPartnerCurrentQty());
1666
                partnerWarehouseStockAgingSummaryModel.setPartnerShortageStock(stockSummary.getPartnerShortageQty());
1667
                if (catalogAgingModelMap.get(stockSummary.getCatalogId()) != null) {
1668
                    partnerWarehouseStockAgingSummaryModel.setExceedDays(catalogAgingModelMap.get(stockSummary.getCatalogId()).getExceedDays());
1669
                } else {
1670
                    partnerWarehouseStockAgingSummaryModel.setExceedDays(0);
1671
 
1672
                }
1673
                partnerWarehouseStockAgingSummaryModel.setStatus(stockSummary.getStatus());
1674
 
1675
                partnerWarehouseStockAgingSummaryModelList.add(partnerWarehouseStockAgingSummaryModel);
1676
            }
1677
 
1678
            Set<Integer> existingCatalogIdsInAgingSummaryList = partnerWarehouseStockAgingSummaryModelList.stream()
1679
                    .map(PartnerWarehouseStockAgingSummaryModel::getCatalogId)
1680
                    .collect(Collectors.toSet());
1681
        }
1682
 
1683
    }
1684
 
34939 ranu 1685
    public void createFofoSmartCartSuggestion(){
34758 ranu 1686
 
34939 ranu 1687
        List<Integer> fofoIds = fofoStoreRepository.selectActiveStores().stream().map(x->x.getId()).collect(toList());
1688
        LocalDateTime todayDate = LocalDate.now().atStartOfDay();
1689
        LocalDateTime fortyFiveAgoDate = todayDate.minusDays(45).with(LocalTime.MAX);
1690
        for(Integer fofoId :fofoIds){
1691
            smartCartSuggestionRepository.deleteByFofoId(fofoId);
1692
            List<SoldAllCatalogitemQtyByPartnerModel> soldAllCatalogitemQtyByPartnerModels = smartCartService.getAllSoldCatalogItemByPartner(fofoId,fortyFiveAgoDate,todayDate);
1693
            for(SoldAllCatalogitemQtyByPartnerModel soldAllCatalogitemQtyByPartnerModel : soldAllCatalogitemQtyByPartnerModels){
1694
               SmartCartSuggestion smartCartSuggestion = new SmartCartSuggestion();
34941 ranu 1695
 
1696
                // weekly average = total sold qty / 6 weeks
1697
                long avgWeeklyQty = Math.round((float) soldAllCatalogitemQtyByPartnerModel.getSoldQty() / 6);
1698
 
1699
                // ensure minimum 2
1700
                long suggestedQty = Math.max(1, avgWeeklyQty);
1701
 
34939 ranu 1702
               smartCartSuggestion.setCatalogId(soldAllCatalogitemQtyByPartnerModel.getCatalogId());
1703
               smartCartSuggestion.setFofoId(fofoId);
1704
               smartCartSuggestion.setSoldQty(soldAllCatalogitemQtyByPartnerModel.getSoldQty());
34941 ranu 1705
               smartCartSuggestion.setSuggestedQty(suggestedQty);
34939 ranu 1706
               smartCartSuggestion.setCreationDate(LocalDate.now());
1707
               smartCartSuggestionRepository.persist(smartCartSuggestion);
1708
            }
1709
        }
1710
 
1711
    }
1712
 
1713
 
34306 ranu 1714
}