Subversion Repositories SmartDukaan

Rev

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

Rev Author Line No. Line
27391 tejbeer 1
package com.spice.profitmandi.web.controller;
2
 
35923 aman 3
import com.google.gson.Gson;
29943 amit.gupta 4
import com.jcraft.jsch.*;
29900 amit.gupta 5
import com.spice.profitmandi.common.enumuration.MessageType;
27391 tejbeer 6
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
7
import com.spice.profitmandi.common.model.CustomRetailer;
27876 amit.gupta 8
import com.spice.profitmandi.common.model.ProfitMandiConstants;
29900 amit.gupta 9
import com.spice.profitmandi.common.model.SendNotificationModel;
29904 amit.gupta 10
import com.spice.profitmandi.common.util.FormattingUtils;
27876 amit.gupta 11
import com.spice.profitmandi.common.web.util.ResponseSender;
35501 ranu 12
import com.spice.profitmandi.dao.entity.catalog.BrandCatalog;
27391 tejbeer 13
import com.spice.profitmandi.dao.entity.catalog.Offer;
14
import com.spice.profitmandi.dao.entity.fofo.PartnerType;
15
import com.spice.profitmandi.dao.enumuration.catalog.ItemCriteriaType;
30651 amit.gupta 16
import com.spice.profitmandi.dao.enumuration.catalog.OfferSchemeType;
27391 tejbeer 17
import com.spice.profitmandi.dao.model.CreateOfferRequest;
34176 tejus.loha 18
import com.spice.profitmandi.dao.model.ItemCriteriaPayout;
35501 ranu 19
import com.spice.profitmandi.dao.model.TodayOfferModel;
35923 aman 20
import com.spice.profitmandi.dao.repository.catalog.CatalogRepository;
21
import com.spice.profitmandi.dao.repository.catalog.ItemRepository;
22
import com.spice.profitmandi.dao.repository.catalog.OfferMarginRepository;
23
import com.spice.profitmandi.dao.repository.catalog.OfferRepository;
27391 tejbeer 24
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
29926 amit.gupta 25
import com.spice.profitmandi.dao.repository.dtr.UserAccountRepository;
29900 amit.gupta 26
import com.spice.profitmandi.service.NotificationService;
29785 amit.gupta 27
import com.spice.profitmandi.service.authentication.RoleManager;
33043 amit.gupta 28
import com.spice.profitmandi.service.catalog.BrandsService;
34176 tejus.loha 29
import com.spice.profitmandi.service.offers.ItemCriteria;
36344 amit 30
import com.spice.profitmandi.service.offers.OfferBatchService;
27876 amit.gupta 31
import com.spice.profitmandi.service.offers.OfferService;
35923 aman 32
import com.spice.profitmandi.service.offers.PartnerCriteria;
35501 ranu 33
import com.spice.profitmandi.service.offers.TodayOfferService;
27391 tejbeer 34
import com.spice.profitmandi.service.user.RetailerService;
35
import com.spice.profitmandi.web.model.LoginDetails;
36
import com.spice.profitmandi.web.util.CookiesProcessor;
37
import com.spice.profitmandi.web.util.MVCResponseSender;
29943 amit.gupta 38
import org.apache.commons.io.FileUtils;
39
import org.apache.commons.io.output.ByteArrayOutputStream;
40
import org.apache.logging.log4j.LogManager;
41
import org.apache.logging.log4j.Logger;
42
import org.springframework.beans.factory.annotation.Autowired;
32868 amit.gupta 43
import org.springframework.beans.factory.annotation.Value;
29943 amit.gupta 44
import org.springframework.core.io.InputStreamResource;
45
import org.springframework.http.HttpHeaders;
46
import org.springframework.http.HttpStatus;
47
import org.springframework.http.ResponseEntity;
32204 amit.gupta 48
import org.springframework.mock.web.MockHttpServletRequest;
49
import org.springframework.mock.web.MockHttpServletResponse;
29943 amit.gupta 50
import org.springframework.stereotype.Controller;
35501 ranu 51
import org.springframework.transaction.annotation.Transactional;
29943 amit.gupta 52
import org.springframework.ui.Model;
53
import org.springframework.web.bind.annotation.*;
54
import org.springframework.web.multipart.MultipartFile;
32204 amit.gupta 55
import org.springframework.web.servlet.View;
56
import org.springframework.web.servlet.ViewResolver;
29943 amit.gupta 57
import org.xhtmlrenderer.swing.Java2DRenderer;
27391 tejbeer 58
 
29943 amit.gupta 59
import javax.imageio.ImageIO;
60
import javax.servlet.http.HttpServletRequest;
61
import java.awt.*;
62
import java.awt.image.BufferedImage;
63
import java.io.ByteArrayInputStream;
64
import java.io.File;
65
import java.io.FileNotFoundException;
66
import java.io.InputStream;
33713 tejus.loha 67
import java.time.Instant;
68
import java.time.LocalDate;
69
import java.time.LocalDateTime;
70
import java.time.YearMonth;
35923 aman 71
import java.util.*;
35501 ranu 72
import java.util.List;
29943 amit.gupta 73
import java.util.stream.Collectors;
74
 
27391 tejbeer 75
@Controller
35458 amit 76
@Transactional(rollbackFor = Throwable.class)
27391 tejbeer 77
public class OfferController {
32505 amit.gupta 78
    private static final Logger LOGGER = LogManager.getLogger(OfferController.class);
79
    private static final String IMAGE_REMOTE_DIR = "/var/www/dtrdashboard/uploads/campaigns/";
80
    private static final String IMAGE_STATIC_SERVER_URL = "https://images.smartdukaan.com/uploads/campaigns";
81
    @Autowired
82
    UserAccountRepository userAccountRepository;
83
    @Autowired
84
    RoleManager roleManager;
85
    @Autowired
86
    private OfferRepository offerRepository;
87
    @Autowired
88
    private OfferMarginRepository offerMarginRepository;
89
    @Autowired
90
    private FofoStoreRepository fofoStoreRepository;
91
    @Autowired
92
    private ResponseSender responseSender;
93
    @Autowired
94
    private ViewResolver viewResolver;
95
    @Autowired
96
    private ItemRepository itemRepository;
97
    @Autowired
98
    private MVCResponseSender mvcResponseSender;
99
    @Autowired
100
    private RetailerService retailerService;
101
    @Autowired
102
    private NotificationService notificationService;
103
    @Autowired
104
    private CookiesProcessor cookiesProcessor;
105
    @Autowired
106
    private OfferService offerService;
27391 tejbeer 107
 
33043 amit.gupta 108
    @Autowired
36344 amit 109
    private OfferBatchService offerBatchService;
110
 
111
    @Autowired
35893 amit 112
    private Gson gson;
113
 
114
    @Autowired
33043 amit.gupta 115
    BrandsService brandsService;
34553 amit.gupta 116
 
34552 amit.gupta 117
    @Autowired
118
    private CatalogRepository catalogRepository;
33043 amit.gupta 119
 
35501 ranu 120
    @Autowired
121
    TodayOfferService todayOfferService;
122
 
32505 amit.gupta 123
    @RequestMapping(value = "/getCreateOffer", method = RequestMethod.GET)
124
    public String getCreateOffer(HttpServletRequest request, Model model) throws ProfitMandiBusinessException {
125
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
126
        List<Integer> fofoIds = fofoStoreRepository.selectActiveStores().stream().map(x -> x.getId())
127
                .collect(Collectors.toList());
27391 tejbeer 128
 
33713 tejus.loha 129
        Set<String> brands = brandsService.getBrandsToDisplay(3).stream().map(x -> x.getName()).collect(Collectors.toSet());
32505 amit.gupta 130
        brands.addAll(itemRepository.selectAllBrands(ProfitMandiConstants.LED_CATEGORY_ID));
33615 amit.gupta 131
        brands.addAll(itemRepository.selectAllBrands(ProfitMandiConstants.SMART_WATCH_CATEGORY_ID));
32505 amit.gupta 132
        //Lets allow demo
133
        brands.add("Live Demo");
27391 tejbeer 134
 
32505 amit.gupta 135
        Map<Integer, CustomRetailer> customRetailerMap = retailerService.getAllFofoRetailers();
27876 amit.gupta 136
 
32505 amit.gupta 137
        Map<Integer, CustomRetailer> customRetailersMap = fofoIds.stream().map(x -> customRetailerMap.get(x))
138
                .filter(x -> x != null).collect(Collectors.toList()).stream()
139
                .collect(Collectors.toMap(x -> x.getPartnerId(), x -> x));
32204 amit.gupta 140
 
32505 amit.gupta 141
        model.addAttribute("customRetailersMap", customRetailersMap);
142
        model.addAttribute("itemCriteriaType", ItemCriteriaType.values());
143
        model.addAttribute("brands", brands);
144
        model.addAttribute("partnerCategories", PartnerType.values());
145
        model.addAttribute("warehouseRegion", ProfitMandiConstants.WAREHOUSE_MAP);
146
        return "scheme_offer";
27391 tejbeer 147
 
32505 amit.gupta 148
    }
29926 amit.gupta 149
 
32505 amit.gupta 150
    @RequestMapping(value = "/createOffer", method = RequestMethod.POST)
151
    public String createOffer(HttpServletRequest request, @RequestBody CreateOfferRequest createOfferRequest,
152
                              Model model) throws Exception {
153
        LOGGER.info("createOfferRequest [{}]", createOfferRequest);
154
        offerService.addOfferService(createOfferRequest);
36050 amit 155
        offerService.evictOfferCaches(createOfferRequest.getId());
32505 amit.gupta 156
        model.addAttribute("response1", mvcResponseSender.createResponseString(true));
157
        return "response";
27391 tejbeer 158
 
32505 amit.gupta 159
    }
27391 tejbeer 160
 
32505 amit.gupta 161
    @RequestMapping(value = "/offers/published", method = RequestMethod.GET)
162
    public String getPublishedOffers(HttpServletRequest request, @RequestParam int fofoId, Model model)
163
            throws Exception {
164
        LOGGER.info("Published");
165
        offerService.getPublishedOffers(fofoId, YearMonth.from(LocalDateTime.now()));
166
        return "scheme_offer/published";
27391 tejbeer 167
 
32505 amit.gupta 168
    }
27391 tejbeer 169
 
32868 amit.gupta 170
    @Value("${prod}")
171
    private boolean isProd;
172
 
32505 amit.gupta 173
    @RequestMapping(value = "/offer/active/{offerId}", method = RequestMethod.GET)
174
    public String activateOffer(HttpServletRequest request, @PathVariable(name = "offerId") String offerIdsString,
175
                                Model model, @RequestParam(defaultValue = "true") boolean active)
176
            throws ProfitMandiBusinessException, Exception {
177
        List<Integer> offerIds = Arrays.stream(offerIdsString.split(",")).map(x -> Integer.parseInt(x))
178
                .collect(Collectors.toList());
32868 amit.gupta 179
 
36634 amit 180
        // REQUIRES_NEW: commits active flag to DB before cache eviction
181
        List<Offer> offers = offerService.activateOffers(offerIds, active);
32868 amit.gupta 182
 
32505 amit.gupta 183
        for (Offer offer : offers) {
36050 amit 184
            offerService.evictOfferCaches(offer.getId());
32505 amit.gupta 185
        }
32868 amit.gupta 186
        if (active) {
187
            for (Offer offer : offers) {
36634 amit 188
                try {
189
                    this.sendNotification(offer);
190
                } catch (Exception e) {
191
                    LOGGER.error("Notification failed for offer {}", offer.getId(), e);
192
                }
32505 amit.gupta 193
            }
194
        }
32868 amit.gupta 195
 
32505 amit.gupta 196
        model.addAttribute("response1", mvcResponseSender.createResponseString(true));
197
        return "response";
198
    }
27391 tejbeer 199
 
35857 amit 200
    @RequestMapping(value = "/offers/publishAll", method = RequestMethod.POST)
201
    public ResponseEntity<?> publishAllUnpublished(@RequestParam YearMonth yearMonth)
202
            throws ProfitMandiBusinessException, Exception {
203
        List<Offer> published = offerService.publishAllUnpublished(yearMonth);
204
        if (!published.isEmpty()) {
205
            for (Offer offer : published) {
36050 amit 206
                offerService.evictOfferCaches(offer.getId());
36634 amit 207
                try {
208
                    this.sendNotification(offer);
209
                } catch (Exception e) {
210
                    LOGGER.error("Notification failed for offer {}", offer.getId(), e);
211
                }
35857 amit 212
            }
213
        }
214
        return responseSender.ok(published.size() + " offers published");
215
    }
216
 
36703 amit 217
    /**
218
     * Evicts month-level offer caches so newly created/modified offers appear in listings.
219
     * Clears: monthOfferIds (admin list), catalog.published_yearmonth (partner view), todayOffers.
220
     */
221
    @RequestMapping(value = "/offer/refreshCache", method = RequestMethod.GET)
222
    public ResponseEntity<?> refreshOfferCache(@RequestParam YearMonth yearMonth) {
223
        offerService.evictMonthCaches(yearMonth);
224
        return responseSender.ok("Offer cache refreshed for " + yearMonth);
225
    }
226
 
35857 amit 227
    @RequestMapping(value = "/offer/delete/{offerId}", method = RequestMethod.DELETE)
228
    public ResponseEntity<?> deleteOffer(@PathVariable int offerId) throws ProfitMandiBusinessException {
229
        offerService.deleteOffer(offerId);
230
        return responseSender.ok(true);
231
    }
232
 
32505 amit.gupta 233
    @RequestMapping(value = "/offer/testimage/{offerId}", method = RequestMethod.GET)
234
    public String testOffer(HttpServletRequest request, @PathVariable int offerId, Model model,
235
                            @RequestParam(defaultValue = "true") boolean active) throws ProfitMandiBusinessException, Exception {
236
        Offer offer = offerRepository.selectById(offerId);
237
        // model.addAttribute("response1", mvcResponseSender.createResponseString(true));
238
        // return "response";
239
        CreateOfferRequest createOfferRequest = offerService.getCreateOfferRequest(offer);
240
        Map<String, Object> model1 = new HashMap<>();
241
        model1.put("offer", createOfferRequest);
242
        model1.put("lessThan", "<");
243
        String htmlContent = this.getContentFromTemplate("offer_margin_detail_notify", model1);
244
        model.addAttribute("response1", htmlContent);
245
        return "response";
246
    }
29900 amit.gupta 247
 
32505 amit.gupta 248
    private void sendNotification(Offer offer) throws Exception {
249
        if (!YearMonth.from(offer.getStartDate()).equals(YearMonth.now())) {
250
            return;
251
        }
252
        String fileName = "offer-" + offer.getId() + ".png";
32868 amit.gupta 253
        //String htmlFileName = fileName.replace("png", "html");
32505 amit.gupta 254
        CreateOfferRequest createOfferRequest = offerService.getCreateOfferRequest(offer);
34620 amit.gupta 255
        boolean isLiveDemo = createOfferRequest.getTargetSlabs().stream()
256
                .map(x -> x.getItemCriteriaPayouts())
257
                .flatMap(List::stream)
258
                .map(ItemCriteriaPayout::getItemCriteria)
259
                .map(ItemCriteria::getCatalogIds)
260
                .flatMap(List::stream)
261
                .anyMatch(catalogId -> catalogRepository.selectCatalogById(catalogId).getBrand().equals("Live Demo"));
262
        if (!isLiveDemo) {
263
            SendNotificationModel sendNotificationModel = new SendNotificationModel();
264
            sendNotificationModel.setCampaignName("SchemeOffer");
265
            sendNotificationModel.setTitle(offer.getName());
266
            sendNotificationModel.setMessage(createOfferRequest.getSchemeType().name() + " of select models, "
267
                    + FormattingUtils.formatDateMonth(offer.getStartDate()) + " to "
268
                    + FormattingUtils.formatDateMonth(offer.getEndDate()));
269
            sendNotificationModel.setType("url");
270
            String imageUrl = IMAGE_STATIC_SERVER_URL + "/" + "image" + LocalDate.now() + "/" + fileName;
271
            sendNotificationModel.setImageUrl(imageUrl);
272
            sendNotificationModel.setUrl("https://app.smartdukaan.com/pages/home/notifications");
273
            sendNotificationModel.setExpiresat(LocalDateTime.now().plusDays(1));
274
            sendNotificationModel.setMessageType(MessageType.scheme);
275
            //Map<Integer, List<Offer>> offersMap = offerRepository.selectAllPublishedMapByPartner(YearMonth.now());
29900 amit.gupta 276
 
34620 amit.gupta 277
            Map<String, InputStream> fileStreamsMap = new HashMap<>();
278
            Map<String, Object> model = new HashMap<>();
279
            model.put("offer", createOfferRequest);
280
            String htmlContent = this.getContentFromTemplate("offer_margin_detail_notify", model);
281
            LOGGER.info("this.getContentFromTemplate {}", htmlContent);
282
            fileStreamsMap.put(fileName, this.getImageBuffer(htmlContent));
283
            // fileStreamsMap.put(htmlFileName, new
284
            // ByteArrayInputStream(htmlContent.getBytes()));
285
            List<Integer> fofoIds = null;
286
            if (isProd) {
287
                this.uploadFile(fileStreamsMap);
288
            }
289
 
290
            List<Integer> fofoIdSet = new ArrayList<>(offerRepository.getEligibleFofoIds(offer));
291
            //LOGGER.info(fofoIdSet);
292
            List<Integer> userIds = userAccountRepository.selectUserIdsByRetailerIds(new ArrayList<>(fofoIdSet));
293
            sendNotificationModel.setUserIds(userIds);
294
            notificationService.sendNotification(sendNotificationModel);
295
            sendWhatsapp(offer, fofoIds, imageUrl);
32868 amit.gupta 296
        }
32505 amit.gupta 297
    }
27876 amit.gupta 298
 
32505 amit.gupta 299
    private void sendWhatsapp(Offer offer, List<Integer> fofoIds, String imageUrl) throws Exception {
35205 amit 300
        offerService.sendWhatsapp(offer, fofoIds, imageUrl);
32505 amit.gupta 301
    }
27391 tejbeer 302
 
32505 amit.gupta 303
    private InputStream asInputStream(BufferedImage bi) throws Exception {
304
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
305
        ImageIO.write(bi, "png", baos);
306
        return new ByteArrayInputStream(baos.toByteArray());
27391 tejbeer 307
 
32505 amit.gupta 308
    }
27391 tejbeer 309
 
32505 amit.gupta 310
    private ChannelSftp setupJsch() throws JSchException {
311
        JSch jsch = new JSch();
33471 amit.gupta 312
        Session jschSession = jsch.getSession("root", "172.105.58.16");
32505 amit.gupta 313
        // Session jschSession = jsch.getSession("root", "173.255.254.24");
314
        LOGGER.info("getClass().getResource(\"id_rsa\") {}",
315
                getClass().getClassLoader().getResource("id_rsa").getPath());
316
        jsch.addIdentity(getClass().getClassLoader().getResource("id_rsa").getPath());
317
        // jschSession.setPassword("spic@2015static0");
318
        jschSession.setConfig("StrictHostKeyChecking", "no");
319
        jschSession.connect();
320
        return (ChannelSftp) jschSession.openChannel("sftp");
321
    }
30426 tejbeer 322
 
32505 amit.gupta 323
    private void fileUpload(ChannelSftp channelSftp, Map<String, InputStream> streamsFileMap, String destinationPath)
324
            throws SftpException, FileNotFoundException {
27391 tejbeer 325
 
32505 amit.gupta 326
        channelSftp.cd(destinationPath);
327
        String folderName = "image" + LocalDate.now();
27391 tejbeer 328
 
32505 amit.gupta 329
        channelSftp.cd(destinationPath);
330
        SftpATTRS attrs = null;
27391 tejbeer 331
 
32505 amit.gupta 332
        // check if the directory is already existing
333
        try {
334
            attrs = channelSftp.stat(folderName);
335
        } catch (Exception e) {
336
            System.out.println(destinationPath + "/" + folderName + " not found");
337
        }
27391 tejbeer 338
 
32505 amit.gupta 339
        // else create a directory
340
        if (attrs == null) {
341
            channelSftp.mkdir(folderName);
342
            channelSftp.chmod(0755, ".");
343
        }
344
        channelSftp.cd(folderName);
27391 tejbeer 345
 
32505 amit.gupta 346
        for (Map.Entry<String, InputStream> streamsFileEntry : streamsFileMap.entrySet()) {
347
            channelSftp.put(streamsFileEntry.getValue(), streamsFileEntry.getKey(), ChannelSftp.OVERWRITE);
348
        }
27391 tejbeer 349
 
32505 amit.gupta 350
    }
29926 amit.gupta 351
 
32505 amit.gupta 352
    private void uploadFile(Map<String, InputStream> fileStreamsMap) throws Exception {
353
        ChannelSftp channelSftp = setupJsch();
354
        channelSftp.connect();
355
        this.fileUpload(channelSftp, fileStreamsMap, IMAGE_REMOTE_DIR + "");
356
        channelSftp.exit();
357
    }
27391 tejbeer 358
 
32505 amit.gupta 359
    private InputStream getImageBuffer(String html) throws Exception {
35923 aman 360
        // Sanitize HTML to valid XHTML for Flying Saucer (Java2DRenderer requires well-formed XML)
361
        org.jsoup.nodes.Document doc = org.jsoup.Jsoup.parse(html);
362
        doc.outputSettings().syntax(org.jsoup.nodes.Document.OutputSettings.Syntax.xml);
363
        doc.outputSettings().escapeMode(org.jsoup.nodes.Entities.EscapeMode.xhtml);
364
        doc.outputSettings().charset("UTF-8");
365
        html = doc.html();
29900 amit.gupta 366
 
32505 amit.gupta 367
        String fileName = "/tmp/" + Instant.now().toEpochMilli();
368
        FileUtils.writeStringToFile(new File(fileName), html, "UTF-8");
369
        String address = "file:" + fileName;
370
        Java2DRenderer renderer = new Java2DRenderer(address, 400);
371
        RenderingHints hints = new RenderingHints(RenderingHints.KEY_COLOR_RENDERING,
372
                RenderingHints.VALUE_COLOR_RENDER_QUALITY);
373
        hints.add(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
374
        hints.add(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
375
        hints.add(new RenderingHints(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC));
376
        renderer.setRenderingHints(hints);
377
        BufferedImage img = renderer.getImage();
378
        ByteArrayOutputStream os = new ByteArrayOutputStream();
379
        ImageIO.write(img, "png", os);
380
        return new ByteArrayInputStream(os.toByteArray());
381
    }
29926 amit.gupta 382
 
32505 amit.gupta 383
    private String getContentFromTemplate(String template, Map<String, Object> model) throws Exception {
384
        View resolvedView = viewResolver.resolveViewName(template, Locale.US);
385
        MockHttpServletResponse mockResp = new MockHttpServletResponse();
386
        MockHttpServletRequest req = new MockHttpServletRequest();
387
        LOGGER.info("Resolved view ->  {}, {}, {}, {}", resolvedView, model, req, mockResp);
388
        resolvedView.render(model, req, mockResp);
389
        return mockResp.getContentAsString();
390
    }
29926 amit.gupta 391
 
32505 amit.gupta 392
    @RequestMapping(value = "/offerHistory", method = RequestMethod.GET)
393
    public String getPaginatedOffers(HttpServletRequest request, @RequestParam YearMonth yearMonth, Model model)
394
            throws ProfitMandiBusinessException {
30017 amit.gupta 395
 
32505 amit.gupta 396
        List<CreateOfferRequest> publishedOffers = offerService.getAllOffers(yearMonth).values().stream()
397
                .sorted(Comparator.comparing(CreateOfferRequest::getId).reversed()).collect(Collectors.toList());
398
        model.addAttribute("offers", publishedOffers);
399
        model.addAttribute("yearMonth", yearMonth);
400
        model.addAttribute("currentMonth", yearMonth.equals(YearMonth.now()));
29926 amit.gupta 401
 
32505 amit.gupta 402
        return "offer_history";
403
    }
30723 amit.gupta 404
 
32505 amit.gupta 405
    @RequestMapping(value = "/offer-details", method = RequestMethod.GET)
406
    public String schemeDetails(HttpServletRequest request, @RequestParam int offerId, Model model)
407
            throws ProfitMandiBusinessException {
408
        CreateOfferRequest createOfferRequest = offerService.getOffer(0, offerId);
29900 amit.gupta 409
 
32505 amit.gupta 410
        model.addAttribute("offer", createOfferRequest);
411
        return "offer-details";
412
    }
29926 amit.gupta 413
 
32505 amit.gupta 414
    @RequestMapping(value = "/offer/process/{offerId}", method = RequestMethod.GET)
415
    public ResponseEntity<?> processOfferRequest(HttpServletRequest request, @PathVariable int offerId, Model model)
416
            throws Exception {
417
        CreateOfferRequest createOfferRequest = offerService.getOffer(0, offerId);
418
        if (!createOfferRequest.isActive()) {
419
            throw new ProfitMandiBusinessException("Offer not active", "Offer not active", "Offer not active");
420
        }
36487 amit 421
        if (offerBatchService.hasUnfinishedBatch(offerId)) {
422
            throw new ProfitMandiBusinessException("Reprocessing not allowed",
423
                    "Existing batch for this offer is not fully processed yet",
424
                    "Existing batch for this offer is not fully processed yet");
425
        }
426
        Offer offer = offerRepository.selectById(offerId);
427
        offer.setProcessedTimestamp(LocalDateTime.now());
36521 amit 428
        offerService.evictOfferDefinitionCache(offerId);
36348 amit 429
        String message = offerBatchService.submitBatchAsync(offerId);
430
        return responseSender.ok(message);
32505 amit.gupta 431
    }
29926 amit.gupta 432
 
32505 amit.gupta 433
    @RequestMapping(value = "/offerDownload", method = RequestMethod.GET)
434
    public ResponseEntity<?> dowloadOfferSummary(HttpServletRequest request, @RequestParam int offerId, Model model)
435
            throws Exception {
436
        final HttpHeaders headers = new HttpHeaders();
437
        headers.set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
438
        headers.set("Content-disposition", "inline; filename=offer-" + offerId + ".csv");
439
        CreateOfferRequest createOfferRequest = offerService.getOffer(0, offerId);
33999 tejus.loha 440
        ByteArrayOutputStream baos = offerService.createCSVOfferReport(createOfferRequest);
32505 amit.gupta 441
        final InputStream inputStream = new ByteArrayInputStream(baos.toByteArray());
442
        final InputStreamResource inputStreamResource = new InputStreamResource(inputStream);
443
        return new ResponseEntity<>(inputStreamResource, headers, HttpStatus.OK);
444
    }
29900 amit.gupta 445
 
32505 amit.gupta 446
    @RequestMapping(value = "/offerById", method = RequestMethod.GET)
447
    public String offerById(HttpServletRequest request, int offerId, Model model) throws ProfitMandiBusinessException {
448
        Offer offer = offerRepository.selectById(offerId);
449
        model.addAttribute("offer", offer);
450
        return "offer-edit";
29900 amit.gupta 451
 
32505 amit.gupta 452
    }
29900 amit.gupta 453
 
34176 tejus.loha 454
    @RequestMapping(value = "/published-offers", method = RequestMethod.GET)
455
    public String publishedOffersOnMonthBefore(HttpServletRequest request, @RequestParam int yearMonth, @RequestParam(required = false, defaultValue = "") String brandFilter, Model model)
32505 amit.gupta 456
            throws ProfitMandiBusinessException {
34176 tejus.loha 457
        LOGGER.info("publishedOffersCalled");
32505 amit.gupta 458
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
459
        int fofoId = loginDetails.getFofoId();
460
        List<CreateOfferRequest> createOffers = offerService.getPublishedOffers(fofoId,
461
                YearMonth.from(LocalDate.now()).minusMonths(yearMonth));
29900 amit.gupta 462
 
34617 amit.gupta 463
        List<CreateOfferRequest> publishedOffers = null;
464
        if (!brandFilter.isEmpty()) {
465
            publishedOffers = createOffers.stream()
34176 tejus.loha 466
                    .filter(createOffer -> createOffer.getTargetSlabs().stream()
467
                            .map(x -> x.getItemCriteriaPayouts())
468
                            .flatMap(List::stream)
469
                            .map(ItemCriteriaPayout::getItemCriteria)
470
                            .map(ItemCriteria::getBrands)
471
                            .flatMap(List::stream)
472
                            .anyMatch(brand -> brand.equals(brandFilter)))
473
                    .collect(Collectors.toList());
34617 amit.gupta 474
        } else {
475
            publishedOffers = createOffers.stream().filter(createOffer -> createOffer.getTargetSlabs().stream()
34557 amit.gupta 476
                    .map(x -> x.getItemCriteriaPayouts())
477
                    .flatMap(List::stream)
478
                    .map(ItemCriteriaPayout::getItemCriteria)
34559 amit.gupta 479
                    .map(ItemCriteria::getCatalogIds)
34557 amit.gupta 480
                    .flatMap(List::stream)
34559 amit.gupta 481
                    .noneMatch(catalogId -> catalogRepository.selectCatalogById(catalogId).getBrand().equals("Live Demo"))).collect(Collectors.toList());
34176 tejus.loha 482
        }
29926 amit.gupta 483
 
34176 tejus.loha 484
        model.addAttribute("publishedOffers", publishedOffers);
485
 
32505 amit.gupta 486
        return "published-offers";
487
    }
29926 amit.gupta 488
 
32505 amit.gupta 489
    @PostMapping(value = "/offers/upload")
490
    public String uploadOffers(HttpServletRequest request, @RequestPart("file") MultipartFile targetFile, Model model)
491
            throws Exception {
492
        offerService.createOffers(targetFile.getInputStream());
493
        model.addAttribute("response1", true);
494
        return "response";
495
    }
29926 amit.gupta 496
 
32505 amit.gupta 497
    @RequestMapping(value = "/getOfferMargins", method = RequestMethod.GET)
498
    public String getOfferMargins(HttpServletRequest request,
499
                                  @RequestParam(name = "offerId", defaultValue = "0") int offerId, Model model) throws Exception {
500
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
501
        boolean isAdmin = roleManager.isAdmin(loginDetails.getRoleIds());
502
        CreateOfferRequest createOfferRequest = offerService.getOffer(isAdmin ? 0 : loginDetails.getFofoId(), offerId);
29900 amit.gupta 503
 
32505 amit.gupta 504
        model.addAttribute("offer", createOfferRequest);
36501 amit 505
        model.addAttribute("isAdmin", isAdmin);
36492 amit 506
        model.addAttribute("isFinanceTeam", isAdmin && this.hasCategory(this.getUserPositions(loginDetails), ProfitMandiConstants.TICKET_CATEGORY_ACCOUNTS));
29900 amit.gupta 507
 
32505 amit.gupta 508
        return "offer_margin_detail_partner";
30470 amit.gupta 509
 
32505 amit.gupta 510
    }
29900 amit.gupta 511
 
35501 ranu 512
    @RequestMapping(value = "/todayOffer")
513
    public String todayOffer(HttpServletRequest request, Model model, @RequestParam(name = "fofoId", defaultValue = "0") int fofoId) throws ProfitMandiBusinessException {
514
 
515
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
516
        if (fofoId == 0) {
517
            fofoId = loginDetails.getFofoId();
518
        }
519
        List<BrandCatalog> allBrands = brandsService.getBrandsToDisplay(3);
520
 
521
        // 1. IDs to exclude entirely
522
        List<Integer> excludedIds = Arrays.asList(132, 133, 28, 17, 125);
523
 
524
        // 2. Brands that must come first (in this specific order)
525
        List<String> priorityOrder = Arrays.asList("Samsung", "Oppo", "Vivo", "Xiaomi", "Realme");
526
 
527
        List<BrandCatalog> sortedBrands = allBrands.stream()
528
                .filter(brand -> !excludedIds.contains(brand.getId())) // Remove excluded
529
                .sorted((b1, b2) -> {
530
                    // Get the index of the brand name in our priority list
531
                    int index1 = priorityOrder.indexOf(b1.getName());
532
                    int index2 = priorityOrder.indexOf(b2.getName());
533
 
534
                    // If brand is NOT in priority list, give it a high index (move to bottom)
535
                    int p1 = (index1 != -1) ? index1 : Integer.MAX_VALUE;
536
                    int p2 = (index2 != -1) ? index2 : Integer.MAX_VALUE;
537
 
538
                    if (p1 != p2) {
539
                        return Integer.compare(p1, p2); // Sort by priority first
540
                    }
541
 
542
                    // If both are "Others", sort them alphabetically
543
                    return b1.getName().compareToIgnoreCase(b2.getName());
544
                })
545
                .collect(Collectors.toList());
546
 
547
        model.addAttribute("brands", sortedBrands);
548
        model.addAttribute("fofoId", fofoId);
549
        model.addAttribute("date", FormattingUtils.format(LocalDateTime.now()));
550
 
551
        return "today-offer";
552
    }
553
 
36492 amit 554
    @Autowired
555
    com.spice.profitmandi.dao.repository.cs.PositionRepository positionRepository;
556
 
557
    @Autowired
558
    com.spice.profitmandi.dao.repository.auth.AuthRepository authRepository;
559
 
560
    private List<com.spice.profitmandi.dao.entity.cs.Position> getUserPositions(LoginDetails loginDetails) throws ProfitMandiBusinessException {
561
        com.spice.profitmandi.dao.entity.auth.AuthUser authUser = authRepository.selectByEmailOrMobile(loginDetails.getEmailId());
562
        return positionRepository.selectAllByAuthUserId(authUser.getId());
563
    }
564
 
565
    private boolean hasCategory(List<com.spice.profitmandi.dao.entity.cs.Position> positions, int categoryId) {
566
        return positions.stream().anyMatch(x -> x.getCategoryId() == categoryId);
567
    }
568
 
569
    private boolean hasCategoryL2Plus(List<com.spice.profitmandi.dao.entity.cs.Position> positions, int categoryId) {
570
        return positions.stream()
571
                .filter(x -> x.getCategoryId() == categoryId)
572
                .anyMatch(x -> com.spice.profitmandi.dao.enumuration.cs.EscalationType.L2.isGreaterThanEqualTo(x.getEscalationType()));
573
    }
574
 
575
    private boolean canReceive(List<com.spice.profitmandi.dao.entity.cs.Position> positions) {
576
        // Warehouse/Logistics team OR Finance L2+ can receive
577
        return hasCategory(positions, ProfitMandiConstants.TICKET_CATEGORY_WAREHOUSE)
578
                || hasCategory(positions, ProfitMandiConstants.TICKET_CATEGORY_LOGISTICS)
579
                || hasCategoryL2Plus(positions, ProfitMandiConstants.TICKET_CATEGORY_ACCOUNTS);
580
    }
581
 
35501 ranu 582
    @RequestMapping(value = "/todayOfferList")
583
    public String todayOfferList(HttpServletRequest request, Model model, @RequestParam String brand, @RequestParam(defaultValue = "0", required = false) int fofoId) throws ProfitMandiBusinessException {
584
 
585
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
586
        if (fofoId == 0) {
587
            fofoId = loginDetails.getFofoId();
588
        }
589
 
590
        List<String> brands = brandsService.getBrandsToDisplay(3).stream().map(x -> x.getName()).collect(Collectors.toList());
591
 
592
        List<TodayOfferModel> todayOfferModels = todayOfferService.findAllTodayOffer(brand, fofoId);
593
 
594
        List<TodayOfferModel> groupedOffers = todayOfferService.groupSameOffers(todayOfferModels);
595
        model.addAttribute("brands", brands);
35505 ranu 596
        model.addAttribute("fofoId", fofoId);
35501 ranu 597
        model.addAttribute("todayOfferModels", todayOfferModels);
598
        model.addAttribute("groupedOffers", groupedOffers);
599
 
600
 
601
        return "today-offer-list";
602
    }
603
 
604
    @RequestMapping(value = "/todayFofoOffer")
605
    public String todayFofoOffer(HttpServletRequest request, Model model, @RequestParam(name = "fofoId", defaultValue = "0") int fofoId) throws ProfitMandiBusinessException {
606
 
607
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
608
        if (fofoId == 0) {
609
            fofoId = loginDetails.getFofoId();
610
        }
611
        List<BrandCatalog> allBrands = brandsService.getBrandsToDisplay(3);
612
 
613
        // 1. IDs to exclude entirely
614
        List<Integer> excludedIds = Arrays.asList(132, 133, 28, 17, 125);
615
 
616
        // 2. Brands that must come first (in this specific order)
617
        List<String> priorityOrder = Arrays.asList("Samsung", "Oppo", "Vivo", "Xiaomi", "Realme");
618
 
619
        List<BrandCatalog> sortedBrands = allBrands.stream()
620
                .filter(brand -> !excludedIds.contains(brand.getId())) // Remove excluded
621
                .sorted((b1, b2) -> {
622
                    // Get the index of the brand name in our priority list
623
                    int index1 = priorityOrder.indexOf(b1.getName());
624
                    int index2 = priorityOrder.indexOf(b2.getName());
625
 
626
                    // If brand is NOT in priority list, give it a high index (move to bottom)
627
                    int p1 = (index1 != -1) ? index1 : Integer.MAX_VALUE;
628
                    int p2 = (index2 != -1) ? index2 : Integer.MAX_VALUE;
629
 
630
                    if (p1 != p2) {
631
                        return Integer.compare(p1, p2); // Sort by priority first
632
                    }
633
 
634
                    // If both are "Others", sort them alphabetically
635
                    return b1.getName().compareToIgnoreCase(b2.getName());
636
                })
637
                .collect(Collectors.toList());
638
 
639
        model.addAttribute("brands", sortedBrands);
640
        model.addAttribute("fofoId", fofoId);
641
        model.addAttribute("date", FormattingUtils.format(LocalDateTime.now()));
642
 
643
        return "today-fofo-offer";
644
    }
645
 
35886 amit 646
    // ===== Offer Partner & Target Management (Admin Only) =====
647
 
648
    @RequestMapping(value = "/offer/partners", method = RequestMethod.GET)
649
    public String getOfferPartners(HttpServletRequest request, @RequestParam int offerId, Model model) throws Exception {
650
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
651
        if (!roleManager.isAdmin(loginDetails.getRoleIds())) {
652
            throw new ProfitMandiBusinessException("Unauthorized", "Unauthorized", "");
653
        }
654
        CreateOfferRequest offer = offerService.getOffer(0, offerId);
655
        Map<Integer, CustomRetailer> customRetailerMap = retailerService.getAllFofoRetailers();
656
 
35888 amit 657
        // Partners are stored in partner_criteria JSON, not in offer_partners table
658
        List<Integer> partnerFofoIds = offer.getPartnerCriteria() != null
659
                ? offer.getPartnerCriteria().getFofoIds() : new ArrayList<>();
660
        if (partnerFofoIds == null) partnerFofoIds = new ArrayList<>();
661
 
35886 amit 662
        List<Integer> allFofoIds = fofoStoreRepository.selectActiveStores().stream()
663
                .map(x -> x.getId()).collect(Collectors.toList());
664
        Map<Integer, CustomRetailer> allRetailersMap = allFofoIds.stream()
665
                .map(id -> customRetailerMap.get(id))
666
                .filter(x -> x != null)
667
                .collect(Collectors.toMap(CustomRetailer::getPartnerId, x -> x));
668
 
669
        model.addAttribute("offer", offer);
670
        model.addAttribute("offerId", offerId);
35888 amit 671
        model.addAttribute("partnerFofoIds", partnerFofoIds);
35886 amit 672
        model.addAttribute("customRetailerMap", customRetailerMap);
673
        model.addAttribute("allRetailersMap", allRetailersMap);
674
        return "offer_partners";
675
    }
676
 
677
    @RequestMapping(value = "/offer/removePartners", method = RequestMethod.POST)
678
    public ResponseEntity<?> removePartnersFromOffer(HttpServletRequest request,
679
            @RequestParam int offerId, @RequestParam List<Integer> fofoIds,
680
            @RequestParam(required = false, defaultValue = "false") boolean createNewOffer,
681
            @RequestParam(required = false) List<Integer> targets) throws Exception {
682
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
683
        if (!roleManager.isAdmin(loginDetails.getRoleIds())) {
684
            throw new ProfitMandiBusinessException("Unauthorized", "Unauthorized", "");
685
        }
686
        Offer offer = offerRepository.selectById(offerId);
687
        YearMonth ym = YearMonth.from(offer.getStartDate());
688
 
689
        offerService.removePartnersFromOffer(offerId, fofoIds);
690
 
691
        Integer newOfferId = null;
692
        String message;
693
        if (createNewOffer && targets != null && !targets.isEmpty()) {
694
            newOfferId = offerService.cloneOfferForPartners(offerId, fofoIds, targets);
695
            message = "Partner(s) removed from Offer #" + offerId + ". New Offer #" + newOfferId + " created (Unpublished).";
696
        } else {
697
            message = "Partner(s) removed from Offer #" + offerId + ".";
698
        }
699
 
36050 amit 700
        offerService.evictOfferCaches(offerId);
35892 amit 701
 
35886 amit 702
        Map<String, Object> response = new HashMap<>();
703
        response.put("message", message);
704
        response.put("newOfferId", newOfferId);
705
        response.put("yearMonth", ym.toString());
706
        return responseSender.ok(response);
707
    }
708
 
709
    @RequestMapping(value = "/offer/addPartners", method = RequestMethod.POST)
710
    public ResponseEntity<?> addPartnersToOffer(HttpServletRequest request,
711
            @RequestParam int offerId, @RequestParam List<Integer> fofoIds) throws Exception {
712
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
713
        if (!roleManager.isAdmin(loginDetails.getRoleIds())) {
714
            throw new ProfitMandiBusinessException("Unauthorized", "Unauthorized", "");
715
        }
716
        offerService.addPartnersToOffer(offerId, fofoIds);
36050 amit 717
        offerService.evictOfferCaches(offerId);
35886 amit 718
 
719
        Offer offer = offerRepository.selectById(offerId);
720
        YearMonth ym = YearMonth.from(offer.getStartDate());
721
        Map<String, Object> response = new HashMap<>();
722
        response.put("message", "Partner(s) added to Offer #" + offerId + ".");
723
        response.put("yearMonth", ym.toString());
724
        return responseSender.ok(response);
725
    }
726
 
727
    @RequestMapping(value = "/offer/updateTargets", method = RequestMethod.POST)
728
    public ResponseEntity<?> updateOfferTargets(HttpServletRequest request,
729
            @RequestParam int offerId, @RequestParam List<Integer> targets) throws Exception {
730
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
731
        if (!roleManager.isAdmin(loginDetails.getRoleIds())) {
732
            throw new ProfitMandiBusinessException("Unauthorized", "Unauthorized", "");
733
        }
734
        offerService.updateOfferTargets(offerId, targets);
36050 amit 735
        offerService.evictOfferCaches(offerId);
35925 amit 736
        return responseSender.ok("Targets updated for Offer #" + offerId);
737
    }
35886 amit 738
 
35925 amit 739
    @RequestMapping(value = "/offer/updateSlabs", method = RequestMethod.POST, consumes = "application/json")
740
    public ResponseEntity<?> updateOfferSlabs(HttpServletRequest request,
741
            @RequestBody com.spice.profitmandi.dao.model.UpdateOfferSlabsRequest updateRequest) throws Exception {
742
        LoginDetails loginDetails = cookiesProcessor.getCookiesObject(request);
743
        if (!roleManager.isAdmin(loginDetails.getRoleIds())) {
744
            throw new ProfitMandiBusinessException("Unauthorized", "Unauthorized", "");
745
        }
746
        offerService.updateOfferSlabs(updateRequest);
36050 amit 747
        offerService.evictOfferCaches(updateRequest.getOfferId());
35925 amit 748
        return responseSender.ok("Slabs updated for Offer #" + updateRequest.getOfferId());
749
    }
750
 
35886 amit 751
 
27895 amit.gupta 752
}