Subversion Repositories SmartDukaan

Rev

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