Subversion Repositories SmartDukaan

Rev

Rev 36402 | Rev 36975 | 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
34619 ranu 76
    @Qualifier(value = "googleMailSender")
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
 
34741 ranu 703
        LocalDateTime startOfToday;
704
        LocalDateTime previousDay;
34321 ranu 705
 
34741 ranu 706
        if (LocalDate.now().getDayOfMonth() == 1) {
707
            startOfToday = LocalDate.now().minusDays(1).atStartOfDay();
708
            previousDay = startOfToday.with(LocalTime.MAX);
709
        } else {
710
            startOfToday = LocalDate.now().atStartOfDay();
711
            previousDay = startOfToday.with(LocalTime.MAX).minusDays(1);
712
        }
713
 
35239 ranu 714
        Map<Integer, CustomRetailer> customRetailers = retailerService.getAllFofoRetailersInternalFalse();
34912 ranu 715
 
35239 ranu 716
        List<Integer> retailerIds = customRetailers.values().stream()
34903 ranu 717
                .filter(retailer -> {
36973 ranu 718
                    String storeCode = retailer.getCode();
34903 ranu 719
                    return !storeCode.equalsIgnoreCase("UPGBN640") && !storeCode.equalsIgnoreCase("HRYN039");
720
                })
721
                .map(CustomRetailer::getPartnerId)
35239 ranu 722
                .collect(Collectors.toList());
34606 ranu 723
 
36973 ranu 724
        Set<Integer> retailerIdSet = new HashSet<>(retailerIds);
34903 ranu 725
 
34641 ranu 726
        //partner daily investment
34729 amit.gupta 727
        List<Loan> defaultLoans = sdCreditService.getDefaultLoans();
34641 ranu 728
        Map<Integer,List<Loan>> defaultLoanMap = defaultLoans.stream().collect(Collectors.groupingBy(Loan::getFofoId));
34619 ranu 729
 
34641 ranu 730
        Map<Integer, PartnerDailyInvestment> partnerDailyInvestmentMap = new HashMap<>();
731
        List<PartnerDailyInvestment> partnerDailyInvestments = partnerDailyInvestmentRepository
34741 ranu 732
                .selectAll(new ArrayList<>(retailerIds), previousDay.toLocalDate());
34641 ranu 733
        if (!partnerDailyInvestments.isEmpty()) {
734
            partnerDailyInvestmentMap = partnerDailyInvestments.stream()
36973 ranu 735
                    .collect(Collectors.toMap(PartnerDailyInvestment::getFofoId, x -> x));
34641 ranu 736
        }
737
 
34741 ranu 738
        YearMonth currentMonth;
739
        LocalDateTime currentMonthStartDate;
740
        LocalDateTime currentMonthEndDate;
741
 
742
        if (LocalDate.now().getDayOfMonth() == 1) {
743
            currentMonth = YearMonth.now().minusMonths(1);
744
            currentMonthStartDate = currentMonth.atDay(1).atStartOfDay();
745
            currentMonthEndDate = currentMonth.atEndOfMonth().atTime(23, 59, 59);
746
        } else {
747
            currentMonth = YearMonth.now();
748
            currentMonthStartDate = currentMonth.atDay(1).atStartOfDay();
749
            currentMonthEndDate = LocalDate.now().minusDays(1).atTime(23, 59, 59);
750
        }
751
 
34619 ranu 752
        String currentMonthStringValue = String.valueOf(currentMonth);
34606 ranu 753
 
36973 ranu 754
        YearMonth lastMonth = currentMonth.minusMonths(1);
755
        String lastMonthStringValue = String.valueOf(lastMonth);
756
        LocalDateTime lastMontStartDate = lastMonth.atDay(1).atStartOfDay();
757
        LocalDateTime lastMonthEndDate = lastMonth.atEndOfMonth().atTime(23, 59, 59);
34606 ranu 758
 
36973 ranu 759
        YearMonth twoMonthsAgo = currentMonth.minusMonths(2);
760
        String twoMonthAgoStringValue = String.valueOf(twoMonthsAgo);
761
        LocalDateTime twoMonthsAgoStartDate = twoMonthsAgo.atDay(1).atStartOfDay();
762
        LocalDateTime twoMonthsAgoEndDate = twoMonthsAgo.atEndOfMonth().atTime(23, 59, 59);
34606 ranu 763
 
36973 ranu 764
        LocalDateTime dayBeforeStart = previousDay.toLocalDate().atStartOfDay().minusDays(1);
765
        LocalDateTime dayBeforeEnd = previousDay.minusDays(1);
766
        LocalDateTime yesterdayStart = previousDay.toLocalDate().atStartOfDay();
34749 ranu 767
 
36973 ranu 768
        // Returns / RTO — already batched
769
        Map<Integer, Long> currentMonthPartnerReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(currentMonthStartDate, currentMonthEndDate)
770
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
771
        Map<Integer, Long> currentMonthRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(currentMonthStartDate, currentMonthEndDate)
772
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
773
        Map<Integer, Long> yesterdayReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(yesterdayStart, previousDay)
774
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
775
        Map<Integer, Long> yesterdayRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(yesterdayStart, previousDay)
776
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
777
        Map<Integer, Long> dayBeforeYesterdayReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(dayBeforeStart, dayBeforeEnd)
778
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
779
        Map<Integer, Long> dayBeforeYesterdayRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(dayBeforeStart, dayBeforeEnd)
780
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
781
        Map<Integer, Long> lastMonthPartnerReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(lastMontStartDate, lastMonthEndDate)
782
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
783
        Map<Integer, Long> lastMonthRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(lastMontStartDate, lastMonthEndDate)
784
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
785
        Map<Integer, Long> twoMonthAgoPartnerReturnOrderInfoModelMap = returnOrderInfoRepository.selectAllByBetweenDate(twoMonthsAgoStartDate, twoMonthsAgoEndDate)
786
                .stream().collect(Collectors.groupingBy(ReturnOrderInfoModel::getRetailerId, Collectors.summingLong(x -> Math.round(x.getRefundAmount()))));
787
        Map<Integer, Long> twoMonthAgoRtoRefundOrderMap = orderRepository.selectAllRefundOrderDatesBetween(twoMonthsAgoStartDate, twoMonthsAgoEndDate)
788
                .stream().collect(Collectors.groupingBy(Order::getRetailerId, Collectors.summingLong(x -> Math.round(x.getTotalAmount()))));
34749 ranu 789
 
36973 ranu 790
        // ---- Batch pre-fetch everything else that was previously per-fofo ----
34749 ranu 791
 
36973 ranu 792
        // Auth hierarchy (BM / SalesManager / RBM L1+L2 fallback / ABM L1+L2+L3 fallback)
793
        Map<Integer, Integer> rbmL1Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_RBM, EscalationType.L1, retailerIdSet);
794
        Map<Integer, Integer> rbmL2Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_RBM, EscalationType.L2, retailerIdSet);
795
        Map<Integer, Integer> bmMap = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L2, retailerIdSet);
796
        Map<Integer, Integer> salesManagerMap = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_SALES, EscalationType.L1, retailerIdSet);
797
        Map<Integer, Integer> abmL1Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_ABM, EscalationType.L1, retailerIdSet);
798
        Map<Integer, Integer> abmL2Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_ABM, EscalationType.L2, retailerIdSet);
799
        Map<Integer, Integer> abmL3Map = csService.getAuthUserIdsWithoutTicketAssigneeByFofoIds(ProfitMandiConstants.TICKET_CATEGORY_ABM, EscalationType.L3, retailerIdSet);
34749 ranu 800
 
36973 ranu 801
        Set<Integer> allAuthUserIds = new HashSet<>();
802
        for (Map<Integer, Integer> m : Arrays.asList(rbmL1Map, rbmL2Map, bmMap, salesManagerMap, abmL1Map, abmL2Map, abmL3Map)) {
803
            m.values().stream().filter(v -> v != null && v != 0).forEach(allAuthUserIds::add);
804
        }
805
        Map<Integer, AuthUser> authUserMap = allAuthUserIds.isEmpty() ? new HashMap<>()
806
                : authRepository.selectByIds(new ArrayList<>(allAuthUserIds)).stream()
807
                    .collect(Collectors.toMap(AuthUser::getId, u -> u, (a, b) -> a));
34749 ranu 808
 
36973 ranu 809
        // AST batch
810
        Set<Integer> astIds = customRetailers.values().stream()
811
                .map(CustomRetailer::getAstId).filter(id -> id != null && id != 0).collect(Collectors.toSet());
812
        Map<Integer, AST> astMap = astIds.isEmpty() ? new HashMap<>()
813
                : astRepository.selectByIds(new ArrayList<>(astIds)).stream()
814
                    .collect(Collectors.toMap(AST::getId, a -> a, (a, b) -> a));
34749 ranu 815
 
36973 ranu 816
        // FofoStore batch (replaces the two duplicate per-fofo calls)
817
        Map<Integer, FofoStore> fofoStoreMap = fofoStoreRepository.selectByRetailerIds(retailerIds).stream()
818
                .collect(Collectors.toMap(FofoStore::getId, s -> s, (a, b) -> a));
34749 ranu 819
 
36973 ranu 820
        // Users + wallet-creation dates
821
        Map<Integer, User> userMap = userRepository.selectByIds(retailerIds).stream()
822
                .collect(Collectors.toMap(User::getId, u -> u, (a, b) -> a));
823
        Map<Integer, LocalDateTime> walletFirstCreatedMap = userWalletHistoryRepository.selectFirstCreatedDatesForFofoIds(retailerIds);
34606 ranu 824
 
36973 ranu 825
        // Partner type for today
826
        Map<Integer, PartnerType> partnerTypeMap = partnerTypeChangeService.getTypesForFofoIds(retailerIds, LocalDate.now());
34606 ranu 827
 
36973 ranu 828
        // Monthly targets
829
        Map<Integer, Double> currentTargetMap = monthlyTargetRepository.selectByDateAndFofoIds(currentMonth, retailerIds).stream()
830
                .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
831
        Map<Integer, Double> lastTargetMap = monthlyTargetRepository.selectByDateAndFofoIds(lastMonth, retailerIds).stream()
832
                .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
833
        Map<Integer, Double> twoMonthAgoTargetMap = monthlyTargetRepository.selectByDateAndFofoIds(twoMonthsAgo, retailerIds).stream()
834
                .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
34606 ranu 835
 
36973 ranu 836
        // DRR target: RbmTargetService.calculateFofoIdTodayTarget always uses YearMonth.now() internally, regardless of
837
        // the date arg. On the 1st this differs from currentMonth (previous month) — preserve original semantics.
838
        Map<Integer, Double> drrTargetMap = YearMonth.now().equals(currentMonth) ? currentTargetMap
839
                : monthlyTargetRepository.selectByDateAndFofoIds(YearMonth.now(), retailerIds).stream()
840
                    .collect(Collectors.toMap(MonthlyTarget::getFofoId, MonthlyTarget::getPurchaseTarget, (a, b) -> a));
34606 ranu 841
 
36973 ranu 842
        // Monthly secondary (order value) — batched
843
        Map<Integer, Double> mtdSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
844
                startOfToday.withDayOfMonth(1), previousDay).stream()
845
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
846
        Map<Integer, Double> yesterDaySecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
847
                yesterdayStart, previousDay).stream()
848
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
849
        Map<Integer, Double> dayBeforeSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
850
                dayBeforeStart, dayBeforeEnd).stream()
851
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
852
        Map<Integer, Double> lastMonthSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
853
                lastMontStartDate, lastMonthEndDate).stream()
854
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
855
        Map<Integer, Double> twoMonthAgoSecondaryMap = orderRepository.selectOrderValueBetweenBillingDatesGroupByFofoId(retailerIds,
856
                twoMonthsAgoStartDate, twoMonthsAgoEndDate).stream()
857
                .collect(Collectors.toMap(IdAmountModel::getId, IdAmountModel::getAmount, (a, b) -> a));
34606 ranu 858
 
36973 ranu 859
        // Tertiary MTD / last-month / two-months-ago (all-partners scan via fofoId=0)
860
        LocalDateTime now = LocalDateTime.now();
861
        Map<Integer, Double> mtdSaleTillYesterdayMap = fofoOrderItemRepository.selectSumMopGroupByRetailer(startOfToday.withDayOfMonth(1), previousDay, 0, false);
862
        Map<Integer, Double> lastMonthSaleMap = fofoOrderItemRepository.selectSumMopGroupByRetailer(lastMontStartDate, lastMonthEndDate, 0, false);
863
        Map<Integer, Double> twoMonthAgoSaleMap = fofoOrderItemRepository.selectSumMopGroupByRetailer(twoMonthsAgoStartDate, twoMonthsAgoEndDate, 0, false);
34749 ranu 864
 
36973 ranu 865
        // Monthly activated-but-not-billed
866
        Map<Integer, Map<YearMonth, PartnerWiseActivatedNotBilledTotal>> activatedNotBilledByFofo = new HashMap<>();
867
        for (PartnerWiseActivatedNotBilledTotal t : activatedImeiRepository.getTotalMonthlyActivatedNotBilledForFofoIds(retailerIds, twoMonthsAgoStartDate)) {
868
            activatedNotBilledByFofo.computeIfAbsent(t.getFofoId(), k -> new HashMap<>())
869
                    .put(YearMonth.parse(t.getYearMonth()), t);
870
        }
34606 ranu 871
 
36973 ranu 872
        // Brand-wise tertiary MTD (per fofo → brand → amount)
873
        Map<Integer, Map<String, Double>> brandTertiaryByFofo = fofoOrderItemRepository.selectSumAmountGroupByBrandForFofoIds(currentMonthStartDate, currentMonthEndDate, retailerIds);
34749 ranu 874
 
36973 ranu 875
        // Brand-wise secondary billed (MTD)
876
        Map<Integer, Map<String, Long>> brandBilledByFofo = new HashMap<>();
877
        for (BrandWiseModel m : orderRepository.selectAllBilledByCategoryOrderGroupByBrandFofoIds(retailerIds, currentMonthStartDate, currentMonthEndDate, Arrays.asList(10006, 10001))) {
878
            brandBilledByFofo.computeIfAbsent(m.getFofoId(), k -> new HashMap<>()).merge(m.getBrand(), m.getAmount(), Long::sum);
879
        }
880
        // Brand-wise returns + RTO returns (MTD)
881
        Map<Integer, Map<String, Double>> brandReturnByFofo = new HashMap<>();
882
        for (BrandWiseReturnInfo r : returnOrderInfoRepository.selectAllBrandWiseByBetweenDateForFofoIds(currentMonthStartDate, currentMonthEndDate.plusDays(1), retailerIds)) {
883
            brandReturnByFofo.computeIfAbsent(r.getRetailerId(), k -> new HashMap<>()).merge(r.getBrand(), r.getReturnAmount(), Double::sum);
884
        }
885
        Map<Integer, Map<String, Double>> brandRtoReturnByFofo = new HashMap<>();
886
        for (BrandWiseReturnInfo r : returnOrderInfoRepository.selectAllBrandWiseRTORefundByBetweenDateForFofoIds(currentMonthStartDate, currentMonthEndDate.plusDays(1), retailerIds)) {
887
            brandRtoReturnByFofo.computeIfAbsent(r.getRetailerId(), k -> new HashMap<>()).merge(r.getBrand(), r.getReturnAmount(), Double::sum);
888
        }
889
 
890
        // Active loans grouped per fofo, plus batched loanId → sum.
891
        // The single-fofo selectAllActiveLoan(fofoId) additionally filters pendingAmount > 0 — must preserve.
892
        Map<Integer, List<Loan>> activeLoansByFofo = loanRepository.selectAllActiveLoan().stream()
893
                .filter(l -> retailerIdSet.contains(l.getFofoId())
894
                        && l.getPendingAmount() != null
895
                        && l.getPendingAmount().doubleValue() > 0)
896
                .collect(Collectors.groupingBy(Loan::getFofoId));
897
        Set<Integer> loanIdsForSum = new HashSet<>();
898
        activeLoansByFofo.values().forEach(list -> list.forEach(l -> loanIdsForSum.add(l.getId())));
899
        defaultLoans.forEach(l -> loanIdsForSum.add(l.getId()));
900
        Map<Integer, Double> loanStatementSumByLoanId = loanStatementRepository.sumAmountByLoanIds(new ArrayList<>(loanIdsForSum));
901
 
902
        // Last order per fofo (batched)
903
        Map<Integer, Integer> lastOrderIdByFofo = orderRepository.getLastOrderByFofoIds(retailerIds);
904
        Set<Integer> lastOrderIds = lastOrderIdByFofo.values().stream().filter(id -> id != null && id != 0).collect(Collectors.toSet());
905
        Map<Integer, Order> lastOrderById = new HashMap<>();
906
        for (Integer oid : lastOrderIds) {
907
            Order o = orderRepository.selectById(oid);
908
            if (o != null) lastOrderById.put(oid, o);
909
        }
910
 
911
        // DRR precomputation — the two dates we ever call with
912
        long day1RemainingDays = rbmTargetService.getRemainingDaysInMonth(currentMonth.atDay(1));
913
        long todayRemainingDays = rbmTargetService.getRemainingDaysInMonth(startOfToday.toLocalDate());
914
 
34648 ranu 915
        Map<Integer , String> assessmentMap = new HashMap<>();
916
        Map<Integer , String> zeroBillingMap = new HashMap<>();
917
        Map<Integer , Float> billingNeededMap = new HashMap<>();
918
        Map<Integer , Integer> countAMap = new HashMap<>();
34619 ranu 919
        Map<Integer , BIRetailerModel> biRetailerModelMap = new HashMap<>();
34641 ranu 920
        Map<Integer , FofoInvestmentModel> biInvestmentModelMap = new HashMap<>();
34619 ranu 921
        Map<Integer, Map<YearMonth, BiSecondaryModel>> allRetailerMonthlyData = new HashMap<>();
922
        Map<Integer,Double> fofoTotalStockPriceMap = new HashMap<>();
923
        Map<Integer,Map<String, BrandStockPrice>> fofoBrandStockPriceMap = new HashMap<>();
34641 ranu 924
        Map<Integer,Long> fofoTotalMtdSecondaryMap = new HashMap<>();
34749 ranu 925
        Map<Integer,Long> fofoYesterdaySecondaryMap = new HashMap<>();
926
        Map<Integer,Long> fofoDayBeforeYesterdaySecondaryMap = new HashMap<>();
34641 ranu 927
        Map<Integer,Map<String, Long>> fofoBrandWiseMtdSecondaryMap = new HashMap<>();
928
        Map<Integer,Double> fofoTotalMtdTertiaryMap = new HashMap<>();
929
        Map<Integer,Map<String, Double>> fofoBrandMtdTertiaryMap = new HashMap<>();
930
 
36973 ranu 931
        for (Integer fofoId : retailerIds) {
932
            // resolve auth-user names from batched maps
34619 ranu 933
            String rbmName = "";
36973 ranu 934
            int rbmL1 = rbmL1Map.getOrDefault(fofoId, 0);
935
            if (rbmL1 != 0 && authUserMap.get(rbmL1) != null) {
936
                rbmName = authUserMap.get(rbmL1).getFullName();
937
            } else {
938
                int rbmL2 = rbmL2Map.getOrDefault(fofoId, 0);
939
                if (rbmL2 != 0 && authUserMap.get(rbmL2) != null) {
940
                    rbmName = authUserMap.get(rbmL2).getFullName();
34677 ranu 941
                }
34619 ranu 942
            }
36973 ranu 943
            String bmName = "";
944
            int bmId = bmMap.getOrDefault(fofoId, 0);
945
            if (bmId != 0 && authUserMap.get(bmId) != null) {
946
                bmName = authUserMap.get(bmId).getFullName();
34619 ranu 947
            }
36973 ranu 948
            int managerId = salesManagerMap.getOrDefault(fofoId, 0);
949
            String managerName;
950
            if (managerId != 0 && authUserMap.get(managerId) != null) {
951
                managerName = authUserMap.get(managerId).getFullName();
952
            } else {
34606 ranu 953
                managerName = bmName;
954
            }
36973 ranu 955
            String abmName = "";
956
            int abmId = abmL1Map.getOrDefault(fofoId, 0);
957
            int abmL2Id = abmL2Map.getOrDefault(fofoId, 0);
958
            int abmL3Id = abmL3Map.getOrDefault(fofoId, 0);
959
            if (abmId != 0 && authUserMap.get(abmId) != null) {
960
                abmName = authUserMap.get(abmId).getFullName();
961
            } else if (abmL2Id != 0 && authUserMap.get(abmL2Id) != null) {
962
                abmName = authUserMap.get(abmL2Id).getFullName();
963
            } else if (abmL3Id != 0 && authUserMap.get(abmL3Id) != null) {
964
                abmName = authUserMap.get(abmL3Id).getFullName();
34915 ranu 965
            }
34606 ranu 966
 
36973 ranu 967
            AST ast = astMap.get(customRetailers.get(fofoId).getAstId());
968
            PartnerType partnerTypeThisMonth = partnerTypeMap.get(fofoId);
34606 ranu 969
 
970
            BIRetailerModel biRetailerModel = new BIRetailerModel();
971
            biRetailerModel.setBmName(bmName);
972
            biRetailerModel.setCode(customRetailers.get(fofoId).getCode());
36973 ranu 973
            biRetailerModel.setArea(ast != null ? ast.getArea() : "-");
974
 
975
            FofoStore fofoStore = fofoStoreMap.get(fofoId);
976
            String retailerStatus;
977
            if (fofoStore == null) {
978
                retailerStatus = "-";
979
            } else if (!fofoStore.isActive()) {
34738 ranu 980
                retailerStatus = "INACTIVE";
36973 ranu 981
            } else {
982
                retailerStatus = String.valueOf(fofoStore.getActivationType());
34738 ranu 983
            }
34606 ranu 984
            biRetailerModel.setCity(customRetailers.get(fofoId).getAddress().getCity());
985
            biRetailerModel.setStoreName(customRetailers.get(fofoId).getBusinessName());
34738 ranu 986
            biRetailerModel.setStatus(retailerStatus);
34606 ranu 987
            biRetailerModel.setCategory(String.valueOf(partnerTypeThisMonth));
988
            biRetailerModel.setSalesManager(managerName);
989
            biRetailerModel.setRbm(rbmName);
34915 ranu 990
            biRetailerModel.setAbm(abmName);
36973 ranu 991
            biRetailerModelMap.put(fofoId, biRetailerModel);
34606 ranu 992
 
36973 ranu 993
            Map<YearMonth, PartnerWiseActivatedNotBilledTotal> partnerWiseActivatedNotBilledTotalMap =
994
                    activatedNotBilledByFofo.getOrDefault(fofoId, new HashMap<>());
34619 ranu 995
 
36973 ranu 996
            // Current-month secondary
997
            double currentSecondaryTarget = currentTargetMap.getOrDefault(fofoId, 0d);
998
            long currentMonthReturn = currentMonthPartnerReturnOrderInfoModelMap.getOrDefault(fofoId, 0L)
999
                    + currentMonthRtoRefundOrderMap.getOrDefault(fofoId, 0L);
34641 ranu 1000
 
36973 ranu 1001
            double dayBeforeYesterdayAfterReturnSecondary = dayBeforeSecondaryMap.getOrDefault(fofoId, 0d)
1002
                    - (dayBeforeYesterdayReturnOrderInfoModelMap.getOrDefault(fofoId, 0L) + dayBeforeYesterdayRtoRefundOrderMap.getOrDefault(fofoId, 0L));
34749 ranu 1003
            fofoDayBeforeYesterdaySecondaryMap.put(fofoId, (long) dayBeforeYesterdayAfterReturnSecondary);
1004
 
36973 ranu 1005
            double yesterDayAfterReturnSecondary = yesterDaySecondaryMap.getOrDefault(fofoId, 0d)
1006
                    - (yesterdayReturnOrderInfoModelMap.getOrDefault(fofoId, 0L) + yesterdayRtoRefundOrderMap.getOrDefault(fofoId, 0L));
34749 ranu 1007
            fofoYesterdaySecondaryMap.put(fofoId, (long) yesterDayAfterReturnSecondary);
1008
 
36973 ranu 1009
            double secondaryAchievedMtd = mtdSecondaryMap.getOrDefault(fofoId, 0.0);
34619 ranu 1010
            double currentMonthNetSecondary = secondaryAchievedMtd - currentMonthReturn;
36973 ranu 1011
            double currentMonthSecondaryPercent = currentSecondaryTarget == 0 ? 0.0
1012
                    : Math.round(Math.abs((secondaryAchievedMtd / currentSecondaryTarget) * 100));
1013
            double currentMonthUnbilled = partnerWiseActivatedNotBilledTotalMap.get(currentMonth) != null
1014
                    ? partnerWiseActivatedNotBilledTotalMap.get(currentMonth).getTotalUnbilledAmount() : 0d;
1015
            double mtdSale = mtdSaleTillYesterdayMap.getOrDefault(fofoId, 0d);
34606 ranu 1016
 
36973 ranu 1017
            // Last month secondary
1018
            double lastMonthSecondaryTarget = lastTargetMap.getOrDefault(fofoId, 0d);
1019
            long lastMonthReturn = lastMonthPartnerReturnOrderInfoModelMap.getOrDefault(fofoId, 0L)
1020
                    + lastMonthRtoRefundOrderMap.getOrDefault(fofoId, 0L);
1021
            double lastMonthSecondaryAchieved = lastMonthSecondaryMap.getOrDefault(fofoId, 0.0);
34619 ranu 1022
            double lastMonthNetSecondary = lastMonthSecondaryAchieved - lastMonthReturn;
36973 ranu 1023
            double lastMonthSecondaryPercent = lastMonthSecondaryTarget == 0 ? 0.0
1024
                    : Math.round(Math.abs((lastMonthSecondaryAchieved / lastMonthSecondaryTarget) * 100));
1025
            double lastMonthUnbilled = partnerWiseActivatedNotBilledTotalMap.get(lastMonth) != null
1026
                    ? partnerWiseActivatedNotBilledTotalMap.get(lastMonth).getTotalUnbilledAmount() : 0d;
1027
            double lastMonthSale = lastMonthSaleMap.getOrDefault(fofoId, 0d);
34606 ranu 1028
 
36973 ranu 1029
            // Two months ago secondary
1030
            double twoMonthAgoSecondaryTarget = twoMonthAgoTargetMap.getOrDefault(fofoId, 0d);
1031
            long twoMonthAgoReturn = twoMonthAgoPartnerReturnOrderInfoModelMap.getOrDefault(fofoId, 0L)
1032
                    + twoMonthAgoRtoRefundOrderMap.getOrDefault(fofoId, 0L);
1033
            double twoMonthAgoSecondaryAchieved = twoMonthAgoSecondaryMap.getOrDefault(fofoId, 0.0);
34619 ranu 1034
            double twoMonthAgoNetSecondary = twoMonthAgoSecondaryAchieved - twoMonthAgoReturn;
36973 ranu 1035
            double twoMonthAgoSecondaryPercent = twoMonthAgoSecondaryTarget == 0 ? 0.0
1036
                    : Math.round(Math.abs((twoMonthAgoSecondaryAchieved / twoMonthAgoSecondaryTarget) * 100));
1037
            double twoMonthAgoUnbilled = partnerWiseActivatedNotBilledTotalMap.get(twoMonthsAgo) != null
1038
                    ? partnerWiseActivatedNotBilledTotalMap.get(twoMonthsAgo).getTotalUnbilledAmount() : 0d;
1039
            double twoMonthAgoSale = twoMonthAgoSaleMap.getOrDefault(fofoId, 0d);
34606 ranu 1040
 
34619 ranu 1041
            Map<YearMonth, BiSecondaryModel> monthlySecondaryModels = new HashMap<>();
36973 ranu 1042
            monthlySecondaryModels.put(currentMonth, new BiSecondaryModel(
1043
                    currentSecondaryTarget, secondaryAchievedMtd, currentMonthReturn,
1044
                    currentMonthNetSecondary, currentMonthSecondaryPercent, mtdSale, currentMonthUnbilled));
1045
            monthlySecondaryModels.put(lastMonth, new BiSecondaryModel(
1046
                    lastMonthSecondaryTarget, lastMonthSecondaryAchieved, lastMonthReturn,
1047
                    lastMonthNetSecondary, lastMonthSecondaryPercent, lastMonthSale, lastMonthUnbilled));
1048
            monthlySecondaryModels.put(twoMonthsAgo, new BiSecondaryModel(
1049
                    twoMonthAgoSecondaryTarget, twoMonthAgoSecondaryAchieved, twoMonthAgoReturn,
1050
                    twoMonthAgoNetSecondary, twoMonthAgoSecondaryPercent, twoMonthAgoSale, twoMonthAgoUnbilled));
34619 ranu 1051
            allRetailerMonthlyData.put(fofoId, monthlySecondaryModels);
1052
 
36973 ranu 1053
            // Brand-wise stock value — still per-fofo (per-fofo pricing service, not batchable trivially)
34619 ranu 1054
            Map<String, BrandStockPrice> brandStockPriceMap = inventoryService.getBrandWiseStockValue(fofoId);
36973 ranu 1055
            fofoBrandStockPriceMap.put(fofoId, brandStockPriceMap);
1056
            fofoTotalStockPriceMap.put(fofoId, brandStockPriceMap.values().stream().mapToDouble(BrandStockPrice::getTotalValue).sum());
34619 ranu 1057
 
36973 ranu 1058
            Map<String, Double> brandMtdTertiaryAmount = brandTertiaryByFofo.getOrDefault(fofoId, new HashMap<>());
1059
            fofoBrandMtdTertiaryMap.put(fofoId, brandMtdTertiaryAmount);
1060
            fofoTotalMtdTertiaryMap.put(fofoId, brandMtdTertiaryAmount.values().stream().mapToDouble(Double::doubleValue).sum());
34619 ranu 1061
 
36973 ranu 1062
            Map<String, Long> brandWiseMtdSecondaryMap = brandBilledByFofo.getOrDefault(fofoId, new HashMap<>());
1063
            Map<String, Double> brandWiseReturnInfoMap = brandReturnByFofo.getOrDefault(fofoId, new HashMap<>());
1064
            Map<String, Double> brandWiseRTOReturnInfoMap = brandRtoReturnByFofo.getOrDefault(fofoId, new HashMap<>());
34619 ranu 1065
 
34730 ranu 1066
            Set<String> allBrands = new HashSet<>();
1067
            allBrands.addAll(brandWiseMtdSecondaryMap.keySet());
1068
            allBrands.addAll(brandWiseReturnInfoMap.keySet());
1069
            allBrands.addAll(brandWiseRTOReturnInfoMap.keySet());
1070
            Map<String, Long> brandWiseMtdNetSecondaryMap = new HashMap<>();
1071
            for (String brand : allBrands) {
36973 ranu 1072
                long billedAmount = brandWiseMtdSecondaryMap.getOrDefault(brand, 0L);
1073
                double returnAmount = brandWiseReturnInfoMap.getOrDefault(brand, 0d);
1074
                double rtoReturnAmount = brandWiseRTOReturnInfoMap.getOrDefault(brand, 0d);
1075
                brandWiseMtdNetSecondaryMap.put(brand, Math.round(billedAmount - (returnAmount + rtoReturnAmount)));
34730 ranu 1076
            }
36973 ranu 1077
            fofoBrandWiseMtdSecondaryMap.put(fofoId, brandWiseMtdNetSecondaryMap);
1078
            fofoTotalMtdSecondaryMap.put(fofoId, brandWiseMtdNetSecondaryMap.values().stream().mapToLong(Long::longValue).sum());
34730 ranu 1079
 
36973 ranu 1080
            // Investment info
1081
            PartnerDailyInvestment pdi = partnerDailyInvestmentMap.get(fofoId);
1082
            float shortInvestment = pdi != null ? pdi.getShortInvestment() : 0f;
1083
            float agreedInvestment = pdi != null ? pdi.getMinInvestment() : 0f;
1084
            float investmentLevel = pdi != null ? Math.abs(((shortInvestment - agreedInvestment) / agreedInvestment) * 100) : 0f;
34730 ranu 1085
 
36973 ranu 1086
            List<Loan> fofoDefaultLoans = defaultLoanMap.get(fofoId);
34641 ranu 1087
            float defaultLoanAmount = 0f;
36973 ranu 1088
            if (fofoDefaultLoans != null) {
1089
                for (Loan entry : fofoDefaultLoans) {
1090
                    double amount = loanStatementSumByLoanId.getOrDefault(entry.getId(), 0d);
1091
                    defaultLoanAmount += amount;
34641 ranu 1092
                }
1093
            }
36973 ranu 1094
            List<Loan> activeLoans = activeLoansByFofo.getOrDefault(fofoId, Collections.emptyList());
34743 ranu 1095
            float activeLoan = 0f;
1096
            for (Loan entry : activeLoans) {
36973 ranu 1097
                double pendingAmount = loanStatementSumByLoanId.getOrDefault(entry.getId(), 0d);
34743 ranu 1098
                activeLoan += pendingAmount;
1099
            }
1100
 
36973 ranu 1101
            float poValue = pdi != null ? pdi.getUnbilledAmount() : 0f;
34719 ranu 1102
            float poAndBilledValue = (float) (currentMonthNetSecondary + poValue);
34641 ranu 1103
 
36973 ranu 1104
            // DRR — inlined equivalent of RbmTargetService.calculateFofoIdTodayTarget.
1105
            // Note: original always looks up YearMonth.now() target (not currentMonth). On the 1st these differ.
1106
            double drrTarget = drrTargetMap.getOrDefault(fofoId, 0d);
1107
            double monthDay1Drr = 0d;
1108
            if (drrTarget != 0d) {
1109
                double remainingTarget = drrTarget;
1110
                monthDay1Drr = day1RemainingDays == 0 ? remainingTarget : (int) Math.ceil(remainingTarget / day1RemainingDays);
34897 ranu 1111
            }
36973 ranu 1112
            double todayRequiredDrr = 0d;
1113
            if (monthDay1Drr != 0d) {
1114
                double remainingTarget = drrTarget - currentMonthNetSecondary;
1115
                todayRequiredDrr = todayRemainingDays == 0 ? remainingTarget : (int) Math.ceil(remainingTarget / todayRemainingDays);
1116
            }
1117
            double gotDrrPercent = monthDay1Drr == 0 ? 0 : (todayRequiredDrr / monthDay1Drr) * 100;
34701 ranu 1118
            long drrPercentDisplay = Math.round(Math.abs(gotDrrPercent));
1119
 
36973 ranu 1120
            int orderId = lastOrderIdByFofo.getOrDefault(fofoId, 0);
34644 ranu 1121
            String alertLevel = "-";
1122
            int lastPurchaseDays = 0;
34641 ranu 1123
            if (orderId != 0) {
36973 ranu 1124
                Order order = lastOrderById.get(orderId);
1125
                if (order != null) {
1126
                    lastPurchaseDays = (int) Duration.between(order.getCreateTimestamp().plusDays(1), LocalDateTime.now()).toDays();
1127
                    if (lastPurchaseDays >= 11) alertLevel = "Alert for Management";
1128
                    else if (lastPurchaseDays >= 10) alertLevel = " Alert for RSM/SH";
1129
                    else if (lastPurchaseDays >= 7) alertLevel = "Must be Billed";
1130
                    else alertLevel = "OK";
34641 ranu 1131
                }
34644 ranu 1132
            }
34641 ranu 1133
 
34644 ranu 1134
            FofoInvestmentModel fofoInvestmentModel = new FofoInvestmentModel();
36973 ranu 1135
            fofoInvestmentModel.setCounterPotential(fofoStore != null ? fofoStore.getCounterPotential() : 0);
34644 ranu 1136
            fofoInvestmentModel.setShortInvestment(shortInvestment);
1137
            fofoInvestmentModel.setDefaultLoan(defaultLoanAmount);
1138
            fofoInvestmentModel.setInvestmentLevel(investmentLevel);
1139
            fofoInvestmentModel.setActiveLoan(activeLoan);
1140
            fofoInvestmentModel.setPoValue(poValue);
1141
            fofoInvestmentModel.setPoAndBilled(poAndBilledValue);
1142
            fofoInvestmentModel.setAgreedInvestment(agreedInvestment);
36973 ranu 1143
            fofoInvestmentModel.setWallet(pdi != null ? pdi.getWalletAmount() : 0);
34644 ranu 1144
            fofoInvestmentModel.setMonthBeginingDrr(monthDay1Drr);
1145
            fofoInvestmentModel.setRequiredDrr(todayRequiredDrr);
34701 ranu 1146
            fofoInvestmentModel.setDrrPercent(drrPercentDisplay);
34644 ranu 1147
            fofoInvestmentModel.setLastBillingDone(lastPurchaseDays);
1148
            fofoInvestmentModel.setSlab(alertLevel);
1149
            biInvestmentModelMap.put(fofoId, fofoInvestmentModel);
34641 ranu 1150
 
36973 ranu 1151
            String assessment;
1152
            if (defaultLoanAmount < 0) assessment = "Loan Default";
1153
            else if (investmentLevel <= 75 && defaultLoanAmount >= 0) assessment = "Low Invest";
1154
            else assessment = "-";
1155
            assessmentMap.put(fofoId, assessment);
1156
            zeroBillingMap.put(fofoId, currentMonthNetSecondary <= 100000 ? "Zero Billing" : "-");
1157
            billingNeededMap.put(fofoId, drrPercentDisplay >= 110 && todayRequiredDrr > 0 ? (float) todayRequiredDrr : 0f);
1158
            countAMap.put(fofoId, (defaultLoanAmount > 0 || investmentLevel <= 75 || currentMonthNetSecondary <= 100000 || drrPercentDisplay >= 110) ? 1 : 0);
34606 ranu 1159
        }
1160
 
34619 ranu 1161
        LOGGER.info("Total BI Retailers processed: {}", biRetailerModelMap.size());
34606 ranu 1162
 
34619 ranu 1163
        //generate excel and sent to mail
1164
        List<List<String>> headerGroup = new ArrayList<>();
34606 ranu 1165
 
34619 ranu 1166
        List<String> headers1 = Arrays.asList(
34641 ranu 1167
                "","","","",
34916 ranu 1168
                "Retailer Detail", "","", "", "", "", "", "", "", "","","","","",
34677 ranu 1169
 
1170
                twoMonthAgoStringValue, "", "", "", "", "", "",
1171
                lastMonthStringValue, "", "", "", "", "", "",
34619 ranu 1172
                currentMonthStringValue, "", "", "", "", "", "",
34641 ranu 1173
 
1174
                "","", "", "", "", "", "", "", "", "", "", "", "", "",
1175
 
1176
                "", "", "", "", "", "", "", "", "", "", "", "", "",
34749 ranu 1177
                "", "", "", "", "", "", "", "", "", "", "", "", "","",""
34641 ranu 1178
 
34619 ranu 1179
        );
34606 ranu 1180
 
34619 ranu 1181
        List<String> headers2 = Arrays.asList(
34641 ranu 1182
                "Assessment","Zero billing","Billing needed","Counta",
34916 ranu 1183
                "BM","Partner Id","Link","Wallet Date","Creation Date","Code","Area",  "City", "Store Name", "Status","Category","Sales Manager", "RBM","ABM",
34619 ranu 1184
                "Secondary Target", "Secondary Achieved", "Returns", "Net Secondary", "Secondary %",
1185
                "Tertiary Sale", "Unbilled",
1186
                "Secondary Target", "Secondary Achieved", "Returns", "Net Secondary", "Secondary %",
1187
                "Tertiary Sale", "Unbilled",
1188
                "Secondary Target", "Secondary Achieved", "Returns", "Net Secondary", "Secondary %",
1189
                "Tertiary Sale", "Unbilled",
34641 ranu 1190
                "Counter Potential", "Short investment", "Default", "INVESTMENT LEVEL", "Loan", "PO value", "Agreed investment",
1191
                "Wallet", "po+bill", "MONTH BEGINNING DRR", "REQ DRR", "Drr %", "Last billing Done", "Slab",
34606 ranu 1192
 
36193 ranu 1193
              "Total Stock",  "Apple","Xiaomi", "Vivo", "Tecno","Motorola", "Samsung", "Realme", "Oppo", "OnePlus", "POCO", "Lava", "Itel", "Almost New",
1194
              "Total Secondary", "Apple", "Xiaomi", "Vivo", "Tecno","Motorola", "Samsung", "Realme", "Oppo", "OnePlus", "POCO", "Lava", "Itel", "Almost New",
1195
              "Total Tertiary",  "Apple", "Xiaomi", "Vivo", "Tecno","Motorola", "Samsung", "Realme", "Oppo", "OnePlus", "POCO", "Lava", "Itel", "Almost New",
34749 ranu 1196
                "YesterDay Seconday","Day Before Yesterday Secondary"
34619 ranu 1197
        );
1198
 
1199
        headerGroup.add(headers1);
1200
        headerGroup.add(headers2);
1201
 
1202
 
1203
        List<List<?>> rows = new ArrayList<>();
36973 ranu 1204
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy");
34619 ranu 1205
        for (Map.Entry<Integer, BIRetailerModel> entry : biRetailerModelMap.entrySet()) {
1206
            Integer fofoId = entry.getKey();
36973 ranu 1207
            User user = userMap.get(fofoId);
1208
            LocalDateTime walletCreationDate = walletFirstCreatedMap.get(fofoId);
1209
            if (walletCreationDate == null && user != null) {
34758 ranu 1210
                walletCreationDate = user.getCreateTimestamp();
1211
            }
34619 ranu 1212
            BIRetailerModel retailer = entry.getValue();
34641 ranu 1213
 
34619 ranu 1214
            Map<YearMonth, BiSecondaryModel> monthlyData = allRetailerMonthlyData.get(fofoId);
1215
 
34741 ranu 1216
            BiSecondaryModel current = monthlyData.getOrDefault(currentMonth, new BiSecondaryModel(0,0,0,0,0,0,0));
1217
            BiSecondaryModel last = monthlyData.getOrDefault(currentMonth.minusMonths(1), new BiSecondaryModel(0,0,0,0,0,0,0));
1218
            BiSecondaryModel twoAgo = monthlyData.getOrDefault(currentMonth.minusMonths(2), new BiSecondaryModel(0,0,0,0,0,0,0));
34619 ranu 1219
 
1220
            List<Object> row = new ArrayList<>();
34758 ranu 1221
            LOGGER.info("fofoId-11 {}",fofoId);
1222
 
34619 ranu 1223
            row.addAll(Arrays.asList(
34758 ranu 1224
                    assessmentMap.get(fofoId),
1225
                    zeroBillingMap.get(fofoId),
1226
                    billingNeededMap.get(fofoId),
1227
                    countAMap.get(fofoId),
1228
                    retailer.getBmName(),
1229
                    fofoId ,
1230
                    "https://partners.smartdukaan.com/partnerPerformance?fofoId="+fofoId,
36973 ranu 1231
                    walletCreationDate != null ? walletCreationDate.format(formatter) : "-",
1232
                    (user != null && user.getCreateTimestamp() != null) ? user.getCreateTimestamp().format(formatter) : "-",
34758 ranu 1233
                    retailer.getCode(),
1234
                    retailer.getArea(),
1235
                    retailer.getCity(),
1236
                    retailer.getStoreName(),
1237
                    retailer.getStatus(),
1238
                    retailer.getCategory(),
1239
                    retailer.getSalesManager(),
34915 ranu 1240
                    retailer.getRbm(),
1241
                    retailer.getAbm()
34677 ranu 1242
 
34619 ranu 1243
            ));
1244
 
34677 ranu 1245
 
1246
            // Two Months Ago
34619 ranu 1247
            row.addAll(Arrays.asList(
34677 ranu 1248
                    twoAgo.getSecondaryTarget(),
1249
                    twoAgo.getSecondaryAchieved(),
1250
                    twoAgo.getSecondaryReturn(),
1251
                    twoAgo.getNetSecondary(),
34704 ranu 1252
                    twoAgo.getSecondaryAchievedPercent()+"%",
34677 ranu 1253
                    twoAgo.getTertiary(),
1254
                    twoAgo.getTertiaryUnBilled()
34619 ranu 1255
            ));
1256
 
1257
            // Last Month
1258
            row.addAll(Arrays.asList(
1259
                    last.getSecondaryTarget(),
1260
                    last.getSecondaryAchieved(),
1261
                    last.getSecondaryReturn(),
1262
                    last.getNetSecondary(),
34704 ranu 1263
                    last.getSecondaryAchievedPercent()+"%",
34619 ranu 1264
                    last.getTertiary(),
1265
                    last.getTertiaryUnBilled()
1266
            ));
1267
 
34677 ranu 1268
            // Current Month
34619 ranu 1269
            row.addAll(Arrays.asList(
34677 ranu 1270
                    current.getSecondaryTarget(),
1271
                    current.getSecondaryAchieved(),
1272
                    current.getSecondaryReturn(),
1273
                    current.getNetSecondary(),
34704 ranu 1274
                    current.getSecondaryAchievedPercent()+"%",
34677 ranu 1275
                    current.getTertiary(),
1276
                    current.getTertiaryUnBilled()
34619 ranu 1277
            ));
34677 ranu 1278
 
1279
 
1280
 
34641 ranu 1281
            FofoInvestmentModel fofoInvestmentModelValue = biInvestmentModelMap.get(fofoId);
1282
            if(fofoInvestmentModelValue != null){
1283
                row.addAll(Arrays.asList(
1284
                        fofoInvestmentModelValue.getCounterPotential(),
1285
                        fofoInvestmentModelValue.getShortInvestment(),
1286
                        fofoInvestmentModelValue.getDefaultLoan(),
34730 ranu 1287
                        fofoInvestmentModelValue.getInvestmentLevel() +"%",
34743 ranu 1288
                        fofoInvestmentModelValue.getActiveLoan(),
34641 ranu 1289
                        fofoInvestmentModelValue.getPoValue(),
1290
                        fofoInvestmentModelValue.getAgreedInvestment(),
1291
                        fofoInvestmentModelValue.getWallet(),
1292
                        fofoInvestmentModelValue.getPoAndBilled(),
1293
                        fofoInvestmentModelValue.getMonthBeginingDrr(),
1294
                        fofoInvestmentModelValue.getRequiredDrr(),
34704 ranu 1295
                        fofoInvestmentModelValue.getDrrPercent()+"%",
34641 ranu 1296
                        fofoInvestmentModelValue.getLastBillingDone(),
1297
                        fofoInvestmentModelValue.getSlab()
1298
                ));
1299
            }else {
1300
                row.addAll(Arrays.asList(
1301
                        "-","-","-","-","-","-","-","-","-","-","-",""
1302
                ));
1303
            }
1304
 
1305
            Map<String, BrandStockPrice> brandStockMap = fofoBrandStockPriceMap.get(fofoId);
34619 ranu 1306
            row.addAll(Arrays.asList(
1307
                    fofoTotalStockPriceMap.getOrDefault(fofoId, 0.0),
34641 ranu 1308
                    brandStockMap.get("Apple") != null ? brandStockMap.get("Apple").getTotalValue() : 0.0,
1309
                    brandStockMap.get("Xiaomi") != null ? brandStockMap.get("Xiaomi").getTotalValue() : 0.0,
1310
                    brandStockMap.get("Vivo") != null ? brandStockMap.get("Vivo").getTotalValue() : 0.0,
1311
                    brandStockMap.get("Tecno") != null ? brandStockMap.get("Tecno").getTotalValue() : 0.0,
36193 ranu 1312
                    brandStockMap.get("Motorola") != null ? brandStockMap.get("Motorola").getTotalValue() : 0.0,
34641 ranu 1313
                    brandStockMap.get("Samsung") != null ? brandStockMap.get("Samsung").getTotalValue() : 0.0,
1314
                    brandStockMap.get("Realme") != null ? brandStockMap.get("Realme").getTotalValue() : 0.0,
1315
                    brandStockMap.get("Oppo") != null ? brandStockMap.get("Oppo").getTotalValue() : 0.0,
1316
                    brandStockMap.get("OnePlus") != null ? brandStockMap.get("OnePlus").getTotalValue() : 0.0,
34721 ranu 1317
                    brandStockMap.get("POCO") != null ? brandStockMap.get("POCO").getTotalValue() : 0.0,
34641 ranu 1318
                    brandStockMap.get("Lava") != null ? brandStockMap.get("Lava").getTotalValue() : 0.0,
1319
                    brandStockMap.get("Itel") != null ? brandStockMap.get("Itel").getTotalValue() : 0.0,
1320
                    brandStockMap.get("Almost New") != null ? brandStockMap.get("Almost New").getTotalValue() : 0.0
34619 ranu 1321
            ));
1322
 
34641 ranu 1323
            Map<String, Long> brandSecondaryMap = fofoBrandWiseMtdSecondaryMap.get(fofoId);
1324
            row.addAll(Arrays.asList(
34648 ranu 1325
                    fofoTotalMtdSecondaryMap.get(fofoId),
34641 ranu 1326
                    brandSecondaryMap.getOrDefault("Apple", 0L),
1327
                    brandSecondaryMap.getOrDefault("Xiaomi", 0L),
1328
                    brandSecondaryMap.getOrDefault("Vivo", 0L),
1329
                    brandSecondaryMap.getOrDefault("Tecno", 0L),
36193 ranu 1330
                    brandSecondaryMap.getOrDefault("Motorola", 0L),
34641 ranu 1331
                    brandSecondaryMap.getOrDefault("Samsung", 0L),
1332
                    brandSecondaryMap.getOrDefault("Realme", 0L),
1333
                    brandSecondaryMap.getOrDefault("Oppo", 0L),
1334
                    brandSecondaryMap.getOrDefault("OnePlus", 0L),
34721 ranu 1335
                    brandSecondaryMap.getOrDefault("POCO", 0L),
34641 ranu 1336
                    brandSecondaryMap.getOrDefault("Lava", 0L),
1337
                    brandSecondaryMap.getOrDefault("Itel", 0L),
1338
                    brandSecondaryMap.getOrDefault("Almost New", 0L)
1339
            ));
1340
 
1341
            Map<String, Double> brandTertiaryMap = fofoBrandMtdTertiaryMap.get(fofoId);
1342
            row.addAll(Arrays.asList(
34648 ranu 1343
                    fofoTotalMtdTertiaryMap.get(fofoId),
34641 ranu 1344
                    brandTertiaryMap.getOrDefault("Apple", 0d),
1345
                    brandTertiaryMap.getOrDefault("Xiaomi", 0d),
1346
                    brandTertiaryMap.getOrDefault("Vivo", 0d),
1347
                    brandTertiaryMap.getOrDefault("Tecno", 0d),
36193 ranu 1348
                    brandTertiaryMap.getOrDefault("Motorola", 0d),
34641 ranu 1349
                    brandTertiaryMap.getOrDefault("Samsung", 0d),
1350
                    brandTertiaryMap.getOrDefault("Realme", 0d),
1351
                    brandTertiaryMap.getOrDefault("Oppo", 0d),
1352
                    brandTertiaryMap.getOrDefault("OnePlus", 0d),
34721 ranu 1353
                    brandTertiaryMap.getOrDefault("POCO", 0d),
34641 ranu 1354
                    brandTertiaryMap.getOrDefault("Lava", 0d),
1355
                    brandTertiaryMap.getOrDefault("Itel", 0d),
1356
                    brandTertiaryMap.getOrDefault("Almost New", 0d)
1357
            ));
34749 ranu 1358
 
1359
            row.addAll(Arrays.asList(
1360
                    fofoYesterdaySecondaryMap.get(fofoId),
1361
                    fofoDayBeforeYesterdaySecondaryMap.get(fofoId)
1362
            ));
34641 ranu 1363
            rows.add(row);
34619 ranu 1364
        }
1365
 
34912 ranu 1366
        Map<String, Set<Integer>> storeGuyMap = this.generateBiReportHierarchyWise();
34619 ranu 1367
 
35239 ranu 1368
        for (Map.Entry<String, Set<Integer>> storeGuyEntry : storeGuyMap.entrySet()) {
34912 ranu 1369
            String storeGuyEmail = storeGuyEntry.getKey();
1370
            Set<Integer> fofoIds = storeGuyEntry.getValue();
1371
            String[] sendToArray = new String[]{storeGuyEmail};
34911 ranu 1372
 
34912 ranu 1373
            List<List<?>> filteredRows = rows.stream()
1374
                    .filter(row -> row.size() > 5 && fofoIds.contains((Integer) row.get(5)))
1375
                    .collect(Collectors.toList());
1376
            this.sendMailToUser(headerGroup,filteredRows,sendToArray);
35239 ranu 1377
        }
34912 ranu 1378
 
36193 ranu 1379
        this.sendMailToUser(
1380
                headerGroup,
1381
                rows,
1382
                new String[]{
1383
                        "ranu.rajput@smartdukaan.com",
1384
                        "niranjan.kala@smartdukaan.com",
36216 ranu 1385
                        "nivesh.mathur@smartdukaan.com",
36193 ranu 1386
                        "deena.nath@smartdukaan.com",
1387
                        "santosh.giri@smartdukaan.com"
1388
                }
1389
        );
34912 ranu 1390
 
1391
 
34911 ranu 1392
    }
1393
 
1394
    private  void sendMailToUser(List<List<String>> headerGroup,List<List<?>> rows, String[] sendToArray ) throws Exception {
34641 ranu 1395
        // Send to email
1396
//        ByteArrayOutputStream csvStream = FileUtil.getCSVByteStreamWithMultiHeaders(headerGroup, rows);
1397
        ByteArrayOutputStream csvStream = getExcelStreamWithMultiHeaders(headerGroup, rows);
1398
        String fileName = "BI-Retailer-Monthly-Report-" + FormattingUtils.formatDate(LocalDateTime.now()) + ".xlsx";
34619 ranu 1399
        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 1400
    }
34619 ranu 1401
 
1402
 
34641 ranu 1403
    public static ByteArrayOutputStream getExcelStreamWithMultiHeaders(List<List<String>> headerGroup, List<List<?>> rows) {
1404
        Workbook workbook = new XSSFWorkbook();
1405
        Sheet sheet = workbook.createSheet("BI Report");
34715 ranu 1406
        CreationHelper creationHelper = workbook.getCreationHelper();
34641 ranu 1407
        int rowIndex = 0;
34606 ranu 1408
 
34641 ranu 1409
        CellStyle centeredStyle = workbook.createCellStyle();
1410
        centeredStyle.setAlignment(HorizontalAlignment.CENTER); // Center horizontally
1411
        centeredStyle.setVerticalAlignment(VerticalAlignment.CENTER); // Center vertically
34606 ranu 1412
 
34641 ranu 1413
    // Optional: bold font
1414
        Font font1 = workbook.createFont();
1415
        font1.setBold(true);
1416
        centeredStyle.setFont(font1);
34606 ranu 1417
 
34619 ranu 1418
 
34641 ranu 1419
 
1420
        // Create styles
1421
        Map<String, CellStyle> headerStyles = new HashMap<>();
1422
 
1423
        // fontPurpleStyle
1424
        CellStyle purpleStyle = workbook.createCellStyle();
1425
        purpleStyle.setFillForegroundColor(IndexedColors.ROSE.getIndex());
1426
        purpleStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1427
        purpleStyle.setFont(font1);
1428
        headerStyles.put("Assessment", purpleStyle);
1429
        headerStyles.put("Zero billing", purpleStyle);
1430
        headerStyles.put("Billing needed", purpleStyle);
1431
        headerStyles.put("Counta", purpleStyle);
1432
        headerStyles.put("MONTH BEGINNING DRR", purpleStyle);
1433
        headerStyles.put("REQ DRR", purpleStyle);
1434
        headerStyles.put("Drr %", purpleStyle);
1435
 
1436
        // Light Blue
1437
        CellStyle blueStyle = workbook.createCellStyle();
1438
        blueStyle.setFillForegroundColor(IndexedColors.SKY_BLUE.getIndex());
1439
        blueStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1440
        blueStyle.setFont(font1);
1441
        headerStyles.put("Code", blueStyle);
1442
        headerStyles.put("Store Name", blueStyle);
1443
        headerStyles.put("City", blueStyle);
1444
        headerStyles.put("Area", blueStyle);
1445
        headerStyles.put("BM", blueStyle);
1446
        headerStyles.put("RBM", blueStyle);
1447
        headerStyles.put("Sales Manager", blueStyle);
1448
        headerStyles.put("Status", blueStyle);
1449
        headerStyles.put("Category", blueStyle);
34715 ranu 1450
        headerStyles.put("Wallet Date", blueStyle);
1451
        headerStyles.put("Creation Date", blueStyle);
1452
        headerStyles.put("Partner Id", blueStyle);
34641 ranu 1453
 
34715 ranu 1454
        //for link
1455
        // Create hyperlink style
1456
        CellStyle hyperlinkStyle = workbook.createCellStyle();
1457
        Font hlinkFont = workbook.createFont();
1458
        hlinkFont.setUnderline(Font.U_SINGLE);
1459
        hlinkFont.setColor(IndexedColors.BLUE.getIndex());
1460
        hyperlinkStyle.setFont(hlinkFont);
1461
 
1462
 
34641 ranu 1463
        // Light Yellow
1464
        CellStyle yellowStyle = workbook.createCellStyle();
1465
        yellowStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
1466
        yellowStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1467
        yellowStyle.setFont(font1);
1468
        headerStyles.put("Last billing Done", yellowStyle);
1469
        headerStyles.put("Total Stock", yellowStyle);
1470
 
1471
        // Light Orange
1472
        CellStyle orangeStyle = workbook.createCellStyle();
1473
        orangeStyle.setFillForegroundColor(IndexedColors.LIGHT_ORANGE.getIndex());
1474
        orangeStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1475
        orangeStyle.setFont(font1);
1476
        headerStyles.put("Total Tertiary", orangeStyle);
1477
        headerStyles.put("Total Secondary", orangeStyle);
1478
        headerStyles.put("Default", orangeStyle);
1479
 
1480
 
1481
        // Light green
1482
        CellStyle lightGreenStyle = workbook.createCellStyle();
1483
        lightGreenStyle.setFillForegroundColor(IndexedColors.LIGHT_GREEN.getIndex());
1484
        lightGreenStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1485
        lightGreenStyle.setFont(font1);
1486
        headerStyles.put("Short investment", lightGreenStyle);
1487
        headerStyles.put("INVESTMENT LEVEL", lightGreenStyle);
1488
        headerStyles.put("Loan", lightGreenStyle);
1489
        headerStyles.put("PO value", lightGreenStyle);
1490
        headerStyles.put("Agreed investment", lightGreenStyle);
1491
        headerStyles.put("Wallet", lightGreenStyle);
1492
        headerStyles.put("po+bill", lightGreenStyle);
1493
 
1494
        // Light Green
1495
        CellStyle secondary1 = createStyle(workbook, IndexedColors.LIGHT_GREEN);
1496
        CellStyle secondary2 = createStyle(workbook, IndexedColors.LIGHT_YELLOW);
1497
        CellStyle secondary3 = createStyle(workbook, IndexedColors.LIGHT_ORANGE);
1498
 
1499
        Map<String, CellStyle> brandStyles = new HashMap<>();
1500
        brandStyles.put("Apple", createStyle(workbook, IndexedColors.GREY_25_PERCENT));
1501
        brandStyles.put("Xiaomi", createStyle(workbook, IndexedColors.ORANGE));
1502
        brandStyles.put("Vivo", createStyle(workbook, IndexedColors.SKY_BLUE));
1503
        brandStyles.put("Tecno", createStyle(workbook, IndexedColors.LIGHT_BLUE));
36193 ranu 1504
        brandStyles.put("Motorola", createStyle(workbook, IndexedColors.LIGHT_GREEN));
34641 ranu 1505
        brandStyles.put("Samsung", createStyle(workbook, IndexedColors.ROYAL_BLUE));
1506
        brandStyles.put("Realme", createStyle(workbook, IndexedColors.YELLOW));
1507
        brandStyles.put("Oppo", createStyle(workbook, IndexedColors.LIGHT_GREEN));
1508
        brandStyles.put("OnePlus", createStyle(workbook, IndexedColors.RED));
34721 ranu 1509
        brandStyles.put("POCO", createStyle(workbook, IndexedColors.ORANGE));
34641 ranu 1510
        brandStyles.put("Lava", createStyle(workbook, IndexedColors.LIGHT_YELLOW));
1511
        brandStyles.put("Itel", createStyle(workbook, IndexedColors.LIGHT_YELLOW));
1512
        brandStyles.put("Almost New", createStyle(workbook, IndexedColors.WHITE));
1513
 
1514
 
1515
        CellStyle defaultHeaderStyle = workbook.createCellStyle();
1516
        defaultHeaderStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
1517
        defaultHeaderStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1518
        defaultHeaderStyle.setFont(font1);
1519
 
34749 ranu 1520
        CellStyle numberStyle = workbook.createCellStyle();
1521
        DataFormat format = workbook.createDataFormat();
1522
        numberStyle.setDataFormat(format.getFormat("#,##0")); // or "#,##0.00" for two decimals
34641 ranu 1523
 
34749 ranu 1524
 
1525
 
34641 ranu 1526
        Map<String, Integer> headerCount = new HashMap<>();
1527
 
1528
        for (int headerRowIndex = 0; headerRowIndex < headerGroup.size(); headerRowIndex++) {
1529
            List<String> headerRow = headerGroup.get(headerRowIndex);
1530
            Row row = sheet.createRow(rowIndex++);
1531
 
1532
            for (int i = 0; i < headerRow.size(); i++) {
1533
                String headerText = headerRow.get(i);
1534
                sheet.setColumnWidth(i, 25 * 256);
1535
                row.setHeightInPoints(20); // 25-point height
1536
                Cell cell = row.createCell(i);
1537
                cell.setCellValue(headerText);
1538
                cell.setCellStyle(centeredStyle);
1539
                // Count how many times this header has appeared
1540
                int count = headerCount.getOrDefault(headerText, 0) + 1;
1541
                headerCount.put(headerText, count);
1542
                // Apply special style for repeated headers
1543
                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")) {
1544
                    if (count == 1) {
1545
                        cell.setCellStyle(secondary1);
1546
                    } else if (count == 2) {
1547
                        cell.setCellStyle(secondary2);
1548
                    } else if (count == 3) {
1549
                        cell.setCellStyle(secondary3);
1550
                    }
1551
                }
1552
                // Brand header styling (apply only for the 2nd row of headers)
1553
                else if (headerRowIndex == 1 && brandStyles.containsKey(headerText)) {
1554
                    cell.setCellStyle(brandStyles.get(headerText));
1555
                }else if (headerStyles.containsKey(headerText)) {
1556
                    cell.setCellStyle(headerStyles.get(headerText));
1557
                } else {
1558
                    cell.setCellStyle(defaultHeaderStyle); // default style for others
1559
                }
1560
            }
1561
        }
1562
 
1563
        // Write data rows
1564
        for (List<?> dataRow : rows) {
1565
            Row row = sheet.createRow(rowIndex++);
1566
            for (int i = 0; i < dataRow.size(); i++) {
1567
                Cell cell = row.createCell(i);
1568
                Object value = dataRow.get(i);
34715 ranu 1569
 
1570
                if (i == 6 && value != null) { // Assuming column 6 is "Link"
1571
                    Hyperlink hyperlink = creationHelper.createHyperlink(HyperlinkType.URL);
1572
                    hyperlink.setAddress(value.toString());
34719 ranu 1573
                    cell.setCellValue("View Link"); // Display text
34715 ranu 1574
                    cell.setHyperlink(hyperlink);
1575
                    cell.setCellStyle(hyperlinkStyle);
34719 ranu 1576
                } else if (value instanceof Number) {
34749 ranu 1577
                    double numeric = ((Number) value).doubleValue();
1578
                    cell.setCellValue(Math.round(numeric));
1579
                    cell.setCellStyle(numberStyle);
34715 ranu 1580
                } else {
1581
                    cell.setCellValue(value != null ? value.toString() : "");
1582
                }
34641 ranu 1583
            }
34719 ranu 1584
 
34641 ranu 1585
        }
1586
 
1587
        // Auto-size columns
1588
        if (!rows.isEmpty()) {
1589
            for (int i = 0; i < rows.get(0).size(); i++) {
1590
                sheet.autoSizeColumn(i);
1591
            }
1592
        }
1593
 
1594
        // Output as ByteArray
1595
        try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
1596
            workbook.write(outputStream);
1597
            workbook.close();
1598
            return outputStream;
1599
        } catch (IOException e) {
1600
            throw new RuntimeException("Failed to generate Excel file", e);
1601
        }
1602
    }
1603
 
1604
 
1605
    private static CellStyle createStyle(Workbook workbook, IndexedColors color) {
1606
        CellStyle style = workbook.createCellStyle();
1607
        style.setFillForegroundColor(color.getIndex());
1608
        style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
1609
        Font font = workbook.createFont();
1610
        font.setBold(true);
1611
        style.setFont(font);
1612
        return style;
1613
    }
1614
 
1615
 
34758 ranu 1616
    public void stockAlertMailToRetailer() throws Exception {
1617
 
1618
        Map<Integer, CustomRetailer> customRetailers = retailerService.getFofoRetailers(true);
1619
 
1620
        List<Integer> retailerIds = customRetailers.values().stream().map(CustomRetailer::getPartnerId).collect(Collectors.toList());
1621
 
1622
        for(Integer fofoId : retailerIds){
1623
            List<String> statusOrder = Arrays.asList("HID", "FASTMOVING", "RUNNING", "SLOWMOVING", "OTHER");
1624
            FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(fofoId);
1625
            List<PartnerWarehouseStockSummaryModel> partnerWarehouseStockSummaryModels = saholicInventoryService.getSaholicAndPartnerStock(fofoId, fofoStore.getWarehouseId());
1626
 
1627
            List<PartnerWarehouseStockAgingSummaryModel> partnerWarehouseStockAgingSummaryModelList = new ArrayList<>();
1628
 
1629
            Set<Integer> catalogIds = partnerWarehouseStockSummaryModels.stream().map(x -> x.getCatalogId()).collect(Collectors.toSet());
1630
 
1631
            List<Integer> catalogsList = new ArrayList<>(catalogIds);
1632
 
1633
            Map<Integer, TagListing> tagListingsMap = tagListingRepository.selectAllByCatalogIds(catalogsList);
1634
 
1635
            List<CatalogAgingModel> catalogAgingModels = ageingService.getCatalogsAgingByWarehouse(catalogIds, fofoStore.getWarehouseId());
1636
 
1637
            Map<Integer, CatalogAgingModel> catalogAgingModelMap = catalogAgingModels.stream().collect(Collectors.toMap(x -> x.getCatalogId(), x -> x));
1638
 
1639
            for (PartnerWarehouseStockSummaryModel stockSummary : partnerWarehouseStockSummaryModels) {
1640
 
1641
                PartnerWarehouseStockAgingSummaryModel partnerWarehouseStockAgingSummaryModel = new PartnerWarehouseStockAgingSummaryModel();
1642
                partnerWarehouseStockAgingSummaryModel.setCatalogId(stockSummary.getCatalogId());
1643
                partnerWarehouseStockAgingSummaryModel.setBrand(stockSummary.getBrand());
1644
                partnerWarehouseStockAgingSummaryModel.setModelNumber(stockSummary.getModelNumber());
1645
                partnerWarehouseStockAgingSummaryModel.setNetAvailability(stockSummary.getShaholicNetAvailability());
1646
                partnerWarehouseStockAgingSummaryModel.setPartnerStockAvailability(stockSummary.getPartnerFullFilledQty());
1647
                partnerWarehouseStockAgingSummaryModel.setPartnerCurrentAvailability(stockSummary.getPartnerCurrentQty());
1648
                partnerWarehouseStockAgingSummaryModel.setPartnerShortageStock(stockSummary.getPartnerShortageQty());
1649
                if (catalogAgingModelMap.get(stockSummary.getCatalogId()) != null) {
1650
                    partnerWarehouseStockAgingSummaryModel.setExceedDays(catalogAgingModelMap.get(stockSummary.getCatalogId()).getExceedDays());
1651
                } else {
1652
                    partnerWarehouseStockAgingSummaryModel.setExceedDays(0);
1653
 
1654
                }
1655
                partnerWarehouseStockAgingSummaryModel.setStatus(stockSummary.getStatus());
1656
 
1657
                partnerWarehouseStockAgingSummaryModelList.add(partnerWarehouseStockAgingSummaryModel);
1658
            }
1659
 
1660
            Set<Integer> existingCatalogIdsInAgingSummaryList = partnerWarehouseStockAgingSummaryModelList.stream()
1661
                    .map(PartnerWarehouseStockAgingSummaryModel::getCatalogId)
1662
                    .collect(Collectors.toSet());
1663
        }
1664
 
1665
    }
1666
 
34939 ranu 1667
    public void createFofoSmartCartSuggestion(){
34758 ranu 1668
 
34939 ranu 1669
        List<Integer> fofoIds = fofoStoreRepository.selectActiveStores().stream().map(x->x.getId()).collect(toList());
1670
        LocalDateTime todayDate = LocalDate.now().atStartOfDay();
1671
        LocalDateTime fortyFiveAgoDate = todayDate.minusDays(45).with(LocalTime.MAX);
1672
        for(Integer fofoId :fofoIds){
1673
            smartCartSuggestionRepository.deleteByFofoId(fofoId);
1674
            List<SoldAllCatalogitemQtyByPartnerModel> soldAllCatalogitemQtyByPartnerModels = smartCartService.getAllSoldCatalogItemByPartner(fofoId,fortyFiveAgoDate,todayDate);
1675
            for(SoldAllCatalogitemQtyByPartnerModel soldAllCatalogitemQtyByPartnerModel : soldAllCatalogitemQtyByPartnerModels){
1676
               SmartCartSuggestion smartCartSuggestion = new SmartCartSuggestion();
34941 ranu 1677
 
1678
                // weekly average = total sold qty / 6 weeks
1679
                long avgWeeklyQty = Math.round((float) soldAllCatalogitemQtyByPartnerModel.getSoldQty() / 6);
1680
 
1681
                // ensure minimum 2
1682
                long suggestedQty = Math.max(1, avgWeeklyQty);
1683
 
34939 ranu 1684
               smartCartSuggestion.setCatalogId(soldAllCatalogitemQtyByPartnerModel.getCatalogId());
1685
               smartCartSuggestion.setFofoId(fofoId);
1686
               smartCartSuggestion.setSoldQty(soldAllCatalogitemQtyByPartnerModel.getSoldQty());
34941 ranu 1687
               smartCartSuggestion.setSuggestedQty(suggestedQty);
34939 ranu 1688
               smartCartSuggestion.setCreationDate(LocalDate.now());
1689
               smartCartSuggestionRepository.persist(smartCartSuggestion);
1690
            }
1691
        }
1692
 
1693
    }
1694
 
1695
 
34306 ranu 1696
}