Subversion Repositories SmartDukaan

Rev

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

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