Rev 34174 | Rev 34222 | Go to most recent revision | View as "text/plain" | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.spice.profitmandi.service.scheme;import com.spice.profitmandi.common.enumuration.ItemType;import com.spice.profitmandi.common.enumuration.MessageType;import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;import com.spice.profitmandi.common.model.ProfitMandiConstants;import com.spice.profitmandi.common.model.SchemeModel;import com.spice.profitmandi.common.model.SendNotificationModel;import com.spice.profitmandi.common.services.ReporticoService;import com.spice.profitmandi.common.util.FormattingUtils;import com.spice.profitmandi.common.util.StringUtils;import com.spice.profitmandi.common.util.Utils;import com.spice.profitmandi.dao.entity.catalog.*;import com.spice.profitmandi.dao.entity.fofo.*;import com.spice.profitmandi.dao.entity.inventory.PartnerAgeingModel;import com.spice.profitmandi.dao.entity.transaction.PriceDrop;import com.spice.profitmandi.dao.entity.transaction.UserWallet;import com.spice.profitmandi.dao.entity.transaction.UserWalletHistory;import com.spice.profitmandi.dao.enumuration.catalog.AmountType;import com.spice.profitmandi.dao.enumuration.catalog.SchemeType;import com.spice.profitmandi.dao.enumuration.fofo.ScanType;import com.spice.profitmandi.dao.enumuration.transaction.SchemePayoutStatus;import com.spice.profitmandi.dao.model.AmountModel;import com.spice.profitmandi.dao.model.CreateSchemeRequest;import com.spice.profitmandi.dao.model.DateRangeModel;import com.spice.profitmandi.dao.repository.catalog.*;import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;import com.spice.profitmandi.dao.repository.dtr.RetailerRepository;import com.spice.profitmandi.dao.repository.fofo.*;import com.spice.profitmandi.dao.repository.transaction.PriceDropRepository;import com.spice.profitmandi.dao.repository.transaction.UserWalletHistoryRepository;import com.spice.profitmandi.dao.repository.transaction.UserWalletRepository;import com.spice.profitmandi.dao.repository.warehouse.AgeingSummaryModel;import com.spice.profitmandi.dao.repository.warehouse.WarehouseInventoryItemRepository;import com.spice.profitmandi.service.NotificationService;import com.spice.profitmandi.service.authentication.RoleManager;import com.spice.profitmandi.service.inventory.AgeingService;import com.spice.profitmandi.service.inventory.PurchaseService;import com.spice.profitmandi.service.pricecircular.CatalogSummaryModel;import com.spice.profitmandi.service.pricecircular.PriceCircularService;import com.spice.profitmandi.service.pricecircular.SchemeSummaryModel;import com.spice.profitmandi.service.wallet.WalletService;import in.shop2020.model.v1.order.WalletReferenceType;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.query.Query;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.cache.annotation.Cacheable;import org.springframework.stereotype.Component;import javax.persistence.TypedQuery;import javax.persistence.criteria.CriteriaBuilder;import javax.persistence.criteria.CriteriaQuery;import javax.persistence.criteria.Predicate;import javax.persistence.criteria.Root;import java.text.MessageFormat;import java.time.LocalDate;import java.time.LocalDateTime;import java.time.LocalTime;import java.time.YearMonth;import java.util.*;import java.util.stream.Collectors;@Componentpublic class SchemeServiceImpl implements SchemeService {private static final Logger LOGGER = LogManager.getLogger(SchemeServiceImpl.class);private static final Set<Integer> tagIds = new HashSet<>(Arrays.asList(4));private static final List<String> BLOCKED_IMEIS = Arrays.asList("864883056397593", "864883054606656", "864883056567815", "861950056518271", "869175055649511", "861362058924574", "866009066803036", "866009066816699", "866009066816137", "866009066815873", "866009066805536", "866009066803010", "866009066821939", "866009066802756", "866009066820592", "866009066820311", "866009066816491", "866009066816376", "866009066815899", "866009066815774", "866009066817937", "866009066819859", "866009066817655", "866009066820691", "866009066820832", "866009066803291", "866009066820733", "866009066814496", "866009066820451", "866009066820659", "866009066804976", "866009066820717", "866009066816095", "861362054898434", "869599051117852", "869599056695332", "869599056695894", "864883057389656", "862661052418692", "860118051929254", "862888051664998", "862680054625831", "862888051666316", "860118051738895", "868066050447970", "868066052424399", "865084051552576", "865084050755097", "865084050755295", "865084050754819", "864883057487278", "864883057389599", "864883057437455", "864883057388278", "862680058278058", "869599056810139", "862200053994193", "861932057188916", "861175050581774", "863933065909093", "863933065635391", "861362054889177", "864004062055154", "864004062069239", "862661050221676", "862661052416993", "866812058631475", "869599051118173", "869599051504273", "868066052729250", "864883057701397", "864883054123033", "864883054947316", "864883056235694", "868066052727692", "866030052139896", "866030052140175", "860588051522053", "860588051513193", "861932056969779", "869599056171995", "865594061074932", "863935059410491", "866088059072718", "869599055375894", "869599054306916", "863782054006472", "863782054012371", "860588053486992", "868066052726835", "868066052726694", "860688053876430", "860688053869674", "868494054682394", "869599053512357"//Imeis provided by deena, "863132060568430", "357737717299925", "863132060568471", "869663062661835", "869663062659458", "863132061326994", "863132061326796", "863132060906770", "869663064230795", "869663064231876", "869663064230670", "869663064231819", "865303063431276", "865303063431391", "865303064031315", "865303064031273", "865303064030994", "865303064031091", "865303063689113", "865303063689576", "865303063688636", "865303063689055", "868386069690414", "868386069688533", "864288066323771", "864288066330156", "869657061613797", "864288065002178", "867791065108199", "867791065069839", "867791065069474", "867791065114395", "867791065113918", "867791065088318", "862814069767037", "867791063493114", "861433069356815", "861433069357359", "861433066654055", "861433066748675", "861433065712136", "861433065712656", "867791062605650", "867791062593559", "867791062593112", "867791062804436", "867791062798455", "867791062805177", "862965069680636", "862807060752412", "862807060752214", "865626066329117", "861433069357011", "861433066749319", "861433068904698", "861433068906057", "861433065714017", "861433065712177", "867791062804279", "866670064793915", "868386063007672", "865626064418557", "861433069357110", "864179066094829", "866382065031176", "864562072802477", "864562072801651", "864562072801917", "864288066786274", "864562070789478", "864562070785930", "864562070788330", "864179066116929", "864470060972569", "867492065820095", "867940069618279", "863718065007331", "863718065001417", "860948069657616", "860948069562733", "860948069563939", "860948068569838", "860948068569598", "860948067443092", "860948067442912", "860948067442573", "860948069561750", "860948069561479", "860948069561974", "860948069562550", "860948069560679", "860948069560190", "860948069562691", "860948069735859");private static final List<SchemeType> ACTIVATION_SCHEME_TYPES = Arrays.asList(SchemeType.ACTIVATION, SchemeType.SPECIAL_SUPPORT, SchemeType.SELLOUT);@AutowiredSchemeBlockedImeiRepository schemeBlockedImeiRepository;@AutowiredStateGstRateRepository stateGstRateRepository;@AutowiredNotificationService notificationService;@Autowired@Qualifier("fofoInventoryItemRepository")private InventoryItemRepository inventoryItemRepository;@Autowiredprivate ActivatedImeiRepository activatedImeiRepository;@Autowiredprivate PartnerTypeChangeService partnerTypeChangeService;@Autowiredprivate PurchaseService purchaseService;@Autowiredprivate ScanRecordRepository scanRecordRepository;@Autowiredprivate SessionFactory sessionFactory;@Autowiredprivate SchemeRepository schemeRepository;@Autowiredprivate PriceDropRepository priceDropRepository;@Autowiredprivate RoleManager roleManager;@Autowiredprivate RetailerRepository retailerRepository;@Autowiredprivate ReporticoService reporticoService;@Autowiredprivate TagListingRepository tagListingRepository;@Autowiredprivate SchemeInOutRepository schemeInOutRepository;@Autowired@Qualifier("catalogItemRepository")private ItemRepository itemRepository;@Autowiredprivate SchemeItemRepository schemeItemRepository;@Autowiredprivate SchemeRegionRepository schemeRegionRepository;@Autowiredprivate WalletService walletService;@Autowiredprivate PurchaseRepository purchaseRepository;@Autowiredprivate FofoOrderRepository fofoOrderRepository;@Autowiredprivate PriceCircularService priceCircularService;@Autowiredprivate SamsungExceptionRepository samsungExceptionRepository;@Overridepublic List<String> getBlockedImeis() {List<SchemeBlockedImei> schemeBlockedImeis = schemeBlockedImeiRepository.selectByDateRange(DateRangeModel.of(LocalDateTime.now().minusMonths(6), LocalDateTime.now()));return schemeBlockedImeis.stream().filter(x -> x.isBlocked()).map(x -> x.getImei()).collect(Collectors.toList());}@Overridepublic void saveScheme(int creatorId, CreateSchemeRequest createSchemeRequest) throws ProfitMandiBusinessException {this.validateCreateSchemeRequest(createSchemeRequest);Scheme scheme = this.toScheme(creatorId, createSchemeRequest);if (scheme.getStartDateTime().isAfter(scheme.getEndDateTime())) {throw new ProfitMandiBusinessException(ProfitMandiConstants.START_DATE + ", " + ProfitMandiConstants.END_DATE,scheme.getStartDateTime() + ", " + scheme.getEndDateTime(), "SCHM_VE_1005");}// this.validateItemIds(createSchemeRequest);schemeRepository.persist(scheme);for (int catalogId : createSchemeRequest.getCatalogIds()) {SchemeItem schemeItem = new SchemeItem();schemeItem.setSchemeId(scheme.getId());schemeItem.setCatalogId(catalogId);schemeItemRepository.persist(schemeItem);}for (int regionId : createSchemeRequest.getRegionIds()) {SchemeRegion schemeRegion = new SchemeRegion();schemeRegion.setSchemeId(scheme.getId());schemeRegion.setRegionId(regionId);schemeRegionRepository.persist(schemeRegion);}}private void validateCreateSchemeRequest(CreateSchemeRequest createSchemeRequest)throws ProfitMandiBusinessException {if (createSchemeRequest.getName() == null || createSchemeRequest.getName().isEmpty()) {throw new ProfitMandiBusinessException(ProfitMandiConstants.NAME, createSchemeRequest.getName(),"SCHM_VE_1000");}if (createSchemeRequest.getAmount() <= 0) {throw new ProfitMandiBusinessException(ProfitMandiConstants.AMOUNT, createSchemeRequest.getAmount(),"SCHM_VE_1001");}if (createSchemeRequest.getAmountType().equals(AmountType.PERCENTAGE)&& createSchemeRequest.getAmount() > 100) {throw new ProfitMandiBusinessException(ProfitMandiConstants.AMOUNT, createSchemeRequest.getAmount(),"SCHM_VE_1002");}if (createSchemeRequest.getStartDate() == null) {throw new ProfitMandiBusinessException(ProfitMandiConstants.START_DATE, createSchemeRequest.getStartDate(),"SCHM_VE_1003");}if (createSchemeRequest.getEndDate() == null) {throw new ProfitMandiBusinessException(ProfitMandiConstants.END_DATE, createSchemeRequest.getEndDate(),"SCHM_VE_1004");}}private void validateItemIds(CreateSchemeRequest createSchemeRequest) throws ProfitMandiBusinessException {if (createSchemeRequest.getCatalogIds() == null || createSchemeRequest.getCatalogIds().isEmpty()) {throw new ProfitMandiBusinessException(ProfitMandiConstants.ITEM_ID, createSchemeRequest.getCatalogIds(),"SCHM_1003");}List<Integer> foundItemIds = itemRepository.selectIdsByIdsAndType(createSchemeRequest.getCatalogIds(),ItemType.SERIALIZED);if (foundItemIds.size() != createSchemeRequest.getCatalogIds().size()) {createSchemeRequest.getCatalogIds().removeAll(foundItemIds);throw new ProfitMandiBusinessException(ProfitMandiConstants.ITEM_ID, createSchemeRequest.getCatalogIds(),"SCHM_1004");}}@Overridepublic Scheme getSchemeById(int schemeId) throws ProfitMandiBusinessException {Scheme scheme = schemeRepository.selectById(schemeId);List<Integer> catalogIds = schemeItemRepository.selectCatalogIdsBySchemeId(scheme.getId());if (catalogIds.size() > 0) {List<Item> items = itemRepository.selectAllByCatalogIds(new HashSet<>(catalogIds));scheme.setCatalogStringMap(this.toCatalogStringMap(items));}return scheme;}public Map<Integer, String> toCatalogStringMap(List<Item> items) {Map<Integer, String> catalogMap = new HashMap<>();for (Item item : items) {if (!catalogMap.containsKey(item.getCatalogItemId())) {catalogMap.put(item.getCatalogItemId(), item.getItemDescriptionNoColor());}}return catalogMap;}private Set<Integer> schemeItemsToCatalogIds(List<SchemeItem> schemeItems) {Set<Integer> catalogId = new HashSet<>();for (SchemeItem schemeItem : schemeItems) {catalogId.add(schemeItem.getCatalogId());}return catalogId;}@Overridepublic List<SchemeModel> getAllSchemeModels(LocalDateTime startDateTime, LocalDateTime endDateTime) throws ProfitMandiBusinessException {List<Scheme> schemes = schemeRepository.selectAllBetweenCreateTimestamp(startDateTime, endDateTime);Map<Integer, Scheme> schemeIdSchemeMap = schemes.stream().collect(Collectors.toMap(x -> x.getId(), x -> x));List<SchemeItem> schemeItems = schemeItemRepository.selectBySchemeIds(schemeIdSchemeMap.keySet());Set<Integer> catalogIds = schemeItems.stream().map(x -> x.getCatalogId()).collect(Collectors.toSet());List<Item> items = itemRepository.selectAllByCatalogIds(catalogIds);Map<Integer, String> catalogStringMap = this.toCatalogStringMap(items);this.addCatalogIdsToSchemes(schemeItems, schemeIdSchemeMap, catalogStringMap);return this.toSchemeModels(schemeIdSchemeMap);}private List<SchemeModel> toSchemeModels(Map<Integer, Scheme> schemeIdSchemeMap) {List<SchemeModel> schemeModels = new ArrayList<>();for (Map.Entry<Integer, Scheme> schemeIdSchemeEntry : schemeIdSchemeMap.entrySet()) {schemeModels.add(this.toSchemeModel(schemeIdSchemeEntry.getValue()));}return schemeModels;}private SchemeModel toSchemeModel(Scheme scheme) {SchemeModel schemeModel = new SchemeModel();schemeModel.setSchemeId(scheme.getId());schemeModel.setName(scheme.getName());schemeModel.setDescription(scheme.getDescription());schemeModel.setSchemeType(scheme.getType().toString());schemeModel.setAmountType(scheme.getAmountType().toString());schemeModel.setAmount(scheme.getAmount());schemeModel.setStartDateTime(StringUtils.toString(scheme.getStartDateTime()));schemeModel.setEndDateTime(StringUtils.toString(scheme.getEndDateTime()));schemeModel.setCreateTimestamp(StringUtils.toString(scheme.getCreateTimestamp()));schemeModel.setActiveTimestamp(StringUtils.toString(scheme.getActiveTimestamp()));schemeModel.setExpireTimestamp(StringUtils.toString(scheme.getExpireTimestamp()));schemeModel.setCreatedBy(scheme.getCreatedBy());schemeModel.setCatalogStringMap(scheme.getCatalogStringMap());schemeModel.setRetailerIds(scheme.getRetailerIds());return schemeModel;}private void addCatalogIdsToSchemes(List<SchemeItem> schemeItems, Map<Integer, Scheme> schemeIdSchemeMap,Map<Integer, String> catalogStringMap) {for (SchemeItem schemeItem : schemeItems) {Scheme scheme = schemeIdSchemeMap.get(schemeItem.getSchemeId());scheme.getCatalogStringMap().put(schemeItem.getCatalogId(), catalogStringMap.get(schemeItem.getCatalogId()));}}@Overridepublic void activeSchemeById(int schemeId) throws ProfitMandiBusinessException {Scheme scheme = schemeRepository.selectById(schemeId);if (scheme.getActiveTimestamp() != null) {throw new ProfitMandiBusinessException(ProfitMandiConstants.ACTIVE_TIMESTAMP, scheme.getActiveTimestamp(),"SCHM_1005");}if (scheme.getExpireTimestamp() != null) {throw new ProfitMandiBusinessException(ProfitMandiConstants.EXPIRE_TIMESTAMP, scheme.getExpireTimestamp(),"SCHM_1006");}scheme.setActiveTimestamp(LocalDateTime.now());this.sendSchemeNotification(scheme);/** if (scheme.getType() == SchemeType.IN) {* this.processPreviousPurchases(scheme); } else if (scheme.getType() ==* SchemeType.OUT) { this.processPreviousSales(scheme); }*/}private void sendSchemeNotification(Scheme scheme) throws ProfitMandiBusinessException {if (ACTIVATION_SCHEME_TYPES.contains(scheme.getType())) {SendNotificationModel sendNotificationModel = new SendNotificationModel();List<SchemeItem> schemeItems = schemeItemRepository.selectBySchemeIds(Collections.singleton(scheme.getId()));Set<Integer> catalogIds = schemeItems.stream().map(x -> x.getCatalogId()).collect(Collectors.toSet());List<String> itemDescriptions = itemRepository.selectAllByCatalogIds(catalogIds).stream().filter(Utils.distinctByKey(Item::getCatalogItemId)).map(x -> x.getItemDescriptionNoColor()).collect(Collectors.toList());String titleTemplate = "%s of Rs.%s for %s";String schemeString = "Activation scheme";if (scheme.getType().equals(SchemeType.SPECIAL_SUPPORT)) {schemeString = "Special Support";}String message = "Duration from - " + FormattingUtils.formatDateMonth(scheme.getStartDateTime()) + " - " + FormattingUtils.formatDateMonth(scheme.getEndDateTime());sendNotificationModel.setCampaignName("activationscheme");sendNotificationModel.setUrl("https://app.smartdukaan.com/pages/home/notifications");sendNotificationModel.setExpiresat(LocalDateTime.now().plusDays(1));sendNotificationModel.setMessage(message);sendNotificationModel.setTitle(String.format(titleTemplate, schemeString, FormattingUtils.formatDecimal(scheme.getAmount()), org.apache.commons.lang3.StringUtils.abbreviate(String.join(", ", itemDescriptions), 25)));sendNotificationModel.setType("url");sendNotificationModel.setMessageType(MessageType.scheme);notificationService.sendNotificationToAll(sendNotificationModel);}}@Overridepublic void expireSchemeById(int schemeId, LocalDateTime expiryTime) throws ProfitMandiBusinessException {Scheme scheme = schemeRepository.selectById(schemeId);if (scheme == null || scheme.getActiveTimestamp() == null) {throw new ProfitMandiBusinessException(ProfitMandiConstants.ACTIVE_TIMESTAMP, scheme.getActiveTimestamp(),"SCHM_1007");}if (scheme.getExpireTimestamp() != null) {throw new ProfitMandiBusinessException(ProfitMandiConstants.EXPIRE_TIMESTAMP, scheme.getExpireTimestamp(),"SCHM_1008");}scheme.setExpireTimestamp(LocalDateTime.now());if (expiryTime.isAfter(scheme.getEndDateTime())) {throw new ProfitMandiBusinessException(ProfitMandiConstants.EXPIRE_TIMESTAMP, scheme.getExpireTimestamp(),"End Date cant be extended during expiry");}scheme.setEndDateTime(expiryTime);schemeRepository.persist(scheme);}private Map<Integer, Scheme> toSchemeIdSchemeMap(List<Scheme> schemes) {Map<Integer, Scheme> schemeIdSchemeMap = new HashMap<>();for (Scheme scheme : schemes) {schemeIdSchemeMap.put(scheme.getId(), scheme);}return schemeIdSchemeMap;}private Map<Integer, Set<Scheme>> toCatalogIdSchemesMap(List<SchemeItem> schemeItems, List<Scheme> schemes) {Map<Integer, Scheme> schemesMap = schemes.stream().collect(Collectors.toMap(x -> x.getId(), x -> x));Map<Integer, Set<Scheme>> catalogSchemesMap = new HashMap<>();for (SchemeItem schemeItem : schemeItems) {if (!catalogSchemesMap.containsKey(schemeItem.getCatalogId())) {catalogSchemesMap.put(schemeItem.getCatalogId(), new HashSet<>());}Set<Scheme> schemesSet = catalogSchemesMap.get(schemeItem.getCatalogId());schemesSet.add(schemesMap.get(schemeItem.getSchemeId()));}return catalogSchemesMap;}private Map<InventoryItem, Set<Scheme>> toInventoryItemSchemesMap(List<Scheme> schemes,List<InventoryItem> inventoryItems) throws ProfitMandiBusinessException {Set<Integer> schemeIds = schemes.stream().map(x -> x.getId()).collect(Collectors.toSet());Set<Integer> itemIds = inventoryItems.stream().map(x -> x.getItemId()).collect(Collectors.toSet());Set<Integer> catalogIds = itemRepository.selectByIds(itemIds).stream().map(x -> x.getCatalogItemId()).collect(Collectors.toSet());List<SchemeItem> schemeItems = schemeItemRepository.selectBySchemeIdsAndCatalogIds(schemeIds, catalogIds);Map<Integer, Set<Scheme>> catalogIdSchemesMap = this.toCatalogIdSchemesMap(schemeItems, schemes);Map<InventoryItem, Set<Scheme>> inventoryItemSchemesMap = new HashMap<>();for (InventoryItem inventoryItem : inventoryItems) {LOGGER.info("inventoryItem {}", inventoryItem);LOGGER.info("inventoryItem.getItem() {}", inventoryItem.getItem());LOGGER.info("catalogIdSchemesMap {}", catalogIdSchemesMap);if (catalogIdSchemesMap.containsKey(inventoryItem.getItem().getCatalogItemId())) {inventoryItemSchemesMap.put(inventoryItem, catalogIdSchemesMap.get(inventoryItem.getItem().getCatalogItemId()));}}return inventoryItemSchemesMap;}@AutowiredOfferTargetSlabRepository offerTargetSlabRepository;private Scheme toScheme(int creatorId, CreateSchemeRequest createSchemeRequest) {Scheme scheme = new Scheme();scheme.setName(createSchemeRequest.getName());scheme.setDescription(createSchemeRequest.getDescription());scheme.setType(createSchemeRequest.getType());scheme.setAmountType(createSchemeRequest.getAmountType());scheme.setAmount(createSchemeRequest.getAmount());scheme.setPartnerType(createSchemeRequest.getPartnerType());scheme.setStartDateTime(createSchemeRequest.getStartDate());scheme.setEndDateTime(createSchemeRequest.getEndDate());scheme.setCreatedBy(creatorId);scheme.setCashback(createSchemeRequest.isCashback());return scheme;}@AutowiredOfferRepository offerRepository;@AutowiredOfferPayoutRepository offerPayoutRepository;@AutowiredAgeingService ageingService;public void processSchemeIn(int purchaseId, int retailerId) throws ProfitMandiBusinessException {LOGGER.info("Processing scheme in for purchaseId - {}", purchaseId);Purchase purchase = purchaseRepository.selectByIdAndFofoId(purchaseId, retailerId);List<InventoryItem> inventoryItems = inventoryItemRepository.selectByPurchaseId(purchaseId);processSchemeIn(purchase, inventoryItems);}private void processSchemeIn(Purchase purchase, List<InventoryItem> inventoryItems) throws ProfitMandiBusinessException {if (inventoryItems.stream().filter(x -> x.getPurchaseId() != purchase.getId()).collect(Collectors.toList()).size() > 0) {throw new ProfitMandiBusinessException("", "Invalid purchase id for inventory item", "SCHM_1009");}PartnerType partnerType = partnerTypeChangeService.getTypeOnMonth(purchase.getFofoId(),YearMonth.from(purchase.getCreateTimestamp()));//Remove imeis from blocked imeis listList<String> blocked_imeis = this.getBlockedImeis();inventoryItems = inventoryItems.stream().filter(inventoryItem -> !blocked_imeis.contains(inventoryItem.getSerialNumber())).collect(Collectors.toList());if (inventoryItems.size() == 0) return;Set<Integer> itemIds = inventoryItems.stream().map(x -> x.getItemId()).collect(Collectors.toSet());Map<Integer, Item> itemsMap = itemRepository.selectByIds(itemIds).stream().collect(Collectors.toMap(x -> x.getId(), x -> x));inventoryItems.stream().forEach(x -> x.setItem(itemsMap.get(x.getItemId())));LocalDateTime billingDate = purchaseService.getBillingDateOfPurchase(purchase.getId());Set<Integer> itemIdsSet = tagListingRepository.selectByItemIdsAndTagIds(itemIds, tagIds).stream().filter(x -> x.getEolDate() == null || x.getEolDate().isAfter(billingDate)).map(x -> x.getItemId()).collect(Collectors.toSet());// Only consider inventory items that were not returned and not eolinventoryItems = inventoryItems.stream().filter(x -> itemIdsSet.contains(x.getItemId())).filter(x -> !x.getLastScanType().equals(ScanType.PURCHASE_RET_BAD)).filter(x -> !x.getLastScanType().equals(ScanType.PURCHASE_RET)).collect(Collectors.toList());if (inventoryItems.size() == 0) return;Map<Integer, List<InventoryItem>> catalogInventoryItemMap = inventoryItems.stream().collect(Collectors.groupingBy(x -> x.getItem().getCatalogItemId()));Map<CatalogSummaryModel, List<SchemeSummaryModel>> catalogSchemeSummaryMap = tagListingRepository.getModelSchemesByCatalogIdsAndType(purchase.getFofoId(),partnerType, new ArrayList<>(catalogInventoryItemMap.keySet()), billingDate);int itemsCount = 0;float totalCashback = 0;for (Map.Entry<CatalogSummaryModel, List<SchemeSummaryModel>> catalogSummaryModelListEntry : catalogSchemeSummaryMap.entrySet()) {CatalogSummaryModel catalogSummaryModel = catalogSummaryModelListEntry.getKey();List<SchemeSummaryModel> schemeSummaryModels = catalogSummaryModelListEntry.getValue().stream().filter(x -> x != null).collect(Collectors.toList());schemeSummaryModels.stream().filter(x -> x != null && x.getSchemeType().equals(SchemeType.IN)).forEach(x -> x.setProcess(true));if (schemeSummaryModels.stream().filter(x -> x.isProcess()).count() == 0) continue;List<InventoryItem> modelInventoryItems = catalogInventoryItemMap.get(catalogSummaryModel.getCatalogId());for (InventoryItem inventoryItem : modelInventoryItems) {float inventoryItemCashback = this.createSchemeInOut(schemeSummaryModels, inventoryItem);if (inventoryItemCashback > 0) {itemsCount++;totalCashback += inventoryItemCashback;}}}LOGGER.info("Items count for purchase id {} is {}", purchase.getId(), itemsCount);if (itemsCount > 0) {walletService.addAmountToWallet(purchase.getFofoId(), purchase.getId(), WalletReferenceType.SCHEME_IN, "Added for SCHEME IN against invoice "+ purchase.getPurchaseReference() + " (total " + itemsCount + " pcs)",totalCashback, purchase.getCreateTimestamp());LOGGER.info("Added Rs.{} for SCHEME IN against invoice {} total pcs({}) {}", totalCashback,purchase.getPurchaseReference(), itemsCount);}}// We are maintaining price drop after grnprivate float getAmount(InventoryItem inventoryItem, Scheme scheme) throws ProfitMandiBusinessException {if (this.getBlockedImeis().contains(inventoryItem.getSerialNumber())) {return 0;}float amount = 0;float dpForCalc = 0;float taxableSellingPrice = 0;//float totalTaxRate = stateGstRateRepository.getTotalTaxRate(inventoryItem.getItemId());if (scheme.getAmountType().equals(AmountType.PERCENTAGE)) {if (scheme.getType().equals(SchemeType.IN)) {dpForCalc = inventoryItem.getUnitPrice() - inventoryItem.getPriceDropAmount();} else {try {dpForCalc = Math.min(inventoryItem.getUnitPrice() - inventoryItem.getPriceDropAmount(),tagListingRepository.selectByItemId(inventoryItem.getItemId()).getSellingPrice());} catch (Exception e) {LOGGER.info("Could not find tag Listing entry in {}", inventoryItem.getItemId());e.printStackTrace();}}//TODO:Should be calculated on unit price//taxableSellingPrice = dpForCalc / (1 + totalTaxRate / 100);//amount = taxableSellingPrice * scheme.getAmount() / 100;amount = dpForCalc * scheme.getAmount() / 100;System.out.println(String.format("%d\t%s\t%d\t%d\t%s\t%s\t%s\t%s\t%f\t%f\t%f\t%f", inventoryItem.getId(),inventoryItem.getSerialNumber(), inventoryItem.getItemId(), scheme.getId(), scheme.getName(),scheme.getType(), scheme.getAmountType(), scheme.getPartnerType(), dpForCalc, taxableSellingPrice,scheme.getAmount(), amount));} else if (scheme.getType().equals(SchemeType.IN)) {amount = scheme.getAmount();}return amount;}//Only in and activation margins are allowed to be rolled out more than twiceprivate float createSchemeInOut(List<SchemeSummaryModel> schemeSummaryModels, InventoryItem inventoryItem) throws ProfitMandiBusinessException {LOGGER.info("schemeSummaryModels - {}", schemeSummaryModels);InventoryPayoutModel inventoryPayoutModel = priceCircularService.getPayouts(inventoryItem);LOGGER.info("inventoryPayoutModel - {}", inventoryPayoutModel);//Get all schemesList<SchemeSummaryModel> inventoryPayoutModelToProcess = schemeSummaryModels.stream().filter(x -> x.isProcess()).collect(Collectors.toList());List<SchemeInOut> paidSios = inventoryPayoutModel.getPaidSios();List<SchemeInOut> pendingSios = inventoryPayoutModel.getPendingSios();Map<SchemeType, List<SchemeInOut>> paidSchemeTypesMap = inventoryPayoutModel.getPaidSios().stream().collect(Collectors.groupingBy(x -> x.getScheme().getType()));Map<Integer, SchemeInOut> paidSchemesMap = paidSios.stream().collect(Collectors.toMap(x -> x.getSchemeId(), x -> x));Map<Integer, SchemeInOut> pendingSchemesMap = pendingSios.stream().collect(Collectors.toMap(x -> x.getSchemeId(), x -> x));Map<SchemeType, Float> schemeTypeCancelledAmountMap = new HashMap<>();double percentageToPay = 0d;double fixedToPay = 0d;Map<SchemeSummaryModel, AmountModel> payoutSchemeSummaryModelMap = new HashMap<>();for (SchemeSummaryModel schemeSummaryModelToProcess : inventoryPayoutModelToProcess) {if (paidSchemesMap.containsKey(schemeSummaryModelToProcess.getSchemeId()) || pendingSchemesMap.containsKey(schemeSummaryModelToProcess.getSchemeId()))continue;//If different type but on higher side provide the higher marginif (Arrays.asList(SchemeType.ACTIVATION, SchemeType.SPECIAL_SUPPORT).contains(schemeSummaryModelToProcess.getSchemeType())) {Scheme scheme = schemeRepository.selectById(schemeSummaryModelToProcess.getSchemeId());//Create only if the activation if the activation date is not knownActivatedImei activatedImei = activatedImeiRepository.selectBySerialNumber(inventoryItem.getSerialNumber());if (activatedImei == null || activatedImei.getActivationTimestamp() == null) {SchemeInOut sio = new SchemeInOut();sio.setAmount(0);sio.setInventoryItemId(inventoryItem.getId());sio.setSchemeId(schemeSummaryModelToProcess.getSchemeId());sio.setStatusDescription("Activation pending for IMEI#" + inventoryItem.getSerialNumber());sio.setStatus(SchemePayoutStatus.PENDING);schemeInOutRepository.persist(sio);}} else if (!SchemeType.IN.equals(schemeSummaryModelToProcess.getSchemeType())) {//We have got non repeating scheme typeif (paidSchemeTypesMap.containsKey(schemeSummaryModelToProcess.getSchemeType())) {SchemeInOut sio = paidSchemeTypesMap.get(schemeSummaryModelToProcess.getSchemeType()).get(0);Scheme paidScheme = sio.getScheme();//Don't entertain if amount types do not match for same typeif (paidScheme.getAmountType().equals(schemeSummaryModelToProcess.getAmountType()) &&schemeSummaryModelToProcess.getAmount() > paidScheme.getAmount() + Utils.DOUBLE_EPSILON) {sio.setRolledBackTimestamp(LocalDateTime.now());sio.setStatus(SchemePayoutStatus.REJECTED);sio.setStatusDescription("Eligible for higher margin for " + schemeSummaryModelToProcess.getSchemeType() + ", new entry added");schemeTypeCancelledAmountMap.put(schemeSummaryModelToProcess.getSchemeType(), sio.getAmount());if (paidScheme.getAmountType().equals(AmountType.PERCENTAGE)) {percentageToPay += schemeSummaryModelToProcess.getAmount() - paidScheme.getAmount();} else {fixedToPay += schemeSummaryModelToProcess.getAmount() - paidScheme.getAmount();}payoutSchemeSummaryModelMap.put(schemeSummaryModelToProcess, new AmountModel(schemeSummaryModelToProcess.getAmount() - paidScheme.getAmount(), schemeSummaryModelToProcess.getAmountType()));}} else {if (schemeSummaryModelToProcess.getAmountType().equals(AmountType.PERCENTAGE)) {percentageToPay += schemeSummaryModelToProcess.getAmount();} else {fixedToPay += schemeSummaryModelToProcess.getAmount();}payoutSchemeSummaryModelMap.put(schemeSummaryModelToProcess, new AmountModel(schemeSummaryModelToProcess.getAmount(), schemeSummaryModelToProcess.getAmountType()));}} else {if (schemeSummaryModelToProcess.getAmountType().equals(AmountType.PERCENTAGE)) {percentageToPay += schemeSummaryModelToProcess.getAmount();} else {fixedToPay += schemeSummaryModelToProcess.getAmount();}payoutSchemeSummaryModelMap.put(schemeSummaryModelToProcess, new AmountModel(schemeSummaryModelToProcess.getAmount(), schemeSummaryModelToProcess.getAmountType()));}}double walletCredit = 0d;if (fixedToPay > 0) {double fixedRollout = fixedToPay * (100 / (100 + inventoryPayoutModel.getPercentageAmount()));for (Map.Entry<SchemeSummaryModel, AmountModel> schemeSummaryModelAmountModelEntry : payoutSchemeSummaryModelMap.entrySet()) {SchemeSummaryModel schemeSummaryModel = schemeSummaryModelAmountModelEntry.getKey();AmountModel amountModel = schemeSummaryModelAmountModelEntry.getValue();if (amountModel.getAmountType().equals(AmountType.FIXED)) {SchemeInOut sio = new SchemeInOut();sio.setSchemeId(schemeSummaryModel.getSchemeId());sio.setInventoryItemId(inventoryItem.getId());sio.setStatus(SchemePayoutStatus.CREDITED);sio.setCreditTimestamp(LocalDateTime.now());sio.setAmount((float) (fixedRollout * amountModel.getAmount() / fixedToPay) + schemeTypeCancelledAmountMap.getOrDefault(schemeSummaryModel.getSchemeType(), 0f));if (schemeSummaryModel.getSchemeType().equals(SchemeType.IN))sio.setStatusDescription("Credited for GRN of IMEI-" + inventoryItem.getSerialNumber());elsesio.setStatusDescription("Credited for Sale of IMEI-" + inventoryItem.getSerialNumber());schemeInOutRepository.persist(sio);}}walletCredit += fixedRollout;}if (percentageToPay > 0) {LOGGER.info("inventoryPayoutModel.getFixedAmount() ----> {}", inventoryPayoutModel.getFixedAmount());double effectiveDP = inventoryPayoutModel.getDp() - (inventoryPayoutModel.getFixedAmount() + fixedToPay);double totalPercentage = inventoryPayoutModel.getPercentageAmount() + percentageToPay;double percentageRollout = effectiveDP * (totalPercentage / (100 + totalPercentage) - (inventoryPayoutModel.getPercentageAmount() / (100 + inventoryPayoutModel.getPercentageAmount())));for (Map.Entry<SchemeSummaryModel, AmountModel> schemeSummaryModelAmountModelEntry : payoutSchemeSummaryModelMap.entrySet()) {SchemeSummaryModel schemeSummaryModel = schemeSummaryModelAmountModelEntry.getKey();AmountModel amountModel = schemeSummaryModelAmountModelEntry.getValue();if (amountModel.getAmountType().equals(AmountType.PERCENTAGE)) {SchemeInOut sio = new SchemeInOut();sio.setInventoryItemId(inventoryItem.getId());sio.setSchemeId(schemeSummaryModel.getSchemeId());sio.setStatus(SchemePayoutStatus.CREDITED);sio.setCreditTimestamp(LocalDateTime.now());sio.setAmount((float) (percentageRollout * amountModel.getAmount() / percentageToPay) +schemeTypeCancelledAmountMap.getOrDefault(schemeSummaryModel.getSchemeType(), 0f));if (schemeSummaryModel.getSchemeType().equals(SchemeType.IN))sio.setStatusDescription("Credited for GRN of IMEI-" + inventoryItem.getSerialNumber());elsesio.setStatusDescription("Credited for Sale of IMEI-" + inventoryItem.getSerialNumber());schemeInOutRepository.persist(sio);}}walletCredit += percentageRollout;}return (float) walletCredit;}private Set<Integer> filterImeisByAgeing(Set<Integer> inventoryItemIds, FofoOrder fofoOrder) {Set<Integer> filteredInventoryIds = new HashSet<>();List<PartnerAgeingModel> partnerAgeingModels = ageingService.filterAgedInventory(inventoryItemIds);for (PartnerAgeingModel partnerAgeingModel : partnerAgeingModels) {LOGGER.info("Serial Number - {}", partnerAgeingModel.getSerialNumber());if (partnerAgeingModel.getBrand().equalsIgnoreCase("Samsung")) {ActivatedImei activatedImei = activatedImeiRepository.selectBySerialNumber(partnerAgeingModel.getSerialNumber());if (activatedImei != null && activatedImei.getActivationTimestamp().toLocalDate().isBefore(partnerAgeingModel.getBillingDate().plusDays(partnerAgeingModel.getMaxAgeingDays()))) {//Lets give money if activation is there and is before the ageing limit of that brand.filteredInventoryIds.add(partnerAgeingModel.getInventoryItemId());} else {//If billing happens before ageing expiryif (fofoOrder.getCreateTimestamp().toLocalDate().isBefore(partnerAgeingModel.getBillingDate().plusDays(partnerAgeingModel.getMaxAgeingDays()))) {filteredInventoryIds.add(partnerAgeingModel.getInventoryItemId());}}} else {filteredInventoryIds.add(partnerAgeingModel.getInventoryItemId());}}return filteredInventoryIds;}@AutowiredWarehouseInventoryItemRepository warehouseInventoryItemRepository;@Overridepublic float processSchemeOut(int fofoOrderId, int retailerId) throws ProfitMandiBusinessException {float totalCashback = 0;FofoOrder fofoOrder = fofoOrderRepository.selectByFofoIdAndOrderId(retailerId, fofoOrderId);// Process only if order is not cancelledif (fofoOrder.getCancelledTimestamp() == null) {// PartnerType partnerType = partnerTypeChangeService.getTypeOnDate(retailerId,// fofoOrder.getCreateTimestamp().toLocalDate());// TODO - SCHEMEPartnerType partnerType = partnerTypeChangeService.getTypeOnMonth(retailerId,YearMonth.from(fofoOrder.getCreateTimestamp()));List<ScanRecord> scanRecords = scanRecordRepository.selectAllByOrderId(fofoOrderId);if (scanRecords.size() == 0) return 0;Set<Integer> inventoryItemIds = scanRecords.stream().map(x -> x.getInventoryItemId()).collect(Collectors.toSet());//Check for ageing//inventoryItemIds = this.filterImeisByAgeing(inventoryItemIds, fofoOrder);//ageingService.filterAgedInventory(inventoryItemIds);LOGGER.info("fofoOrderId --- {}", fofoOrderId);LOGGER.info("scanRecords --- {}", scanRecords);LOGGER.info("inventoryItemIds --- {}", inventoryItemIds);if (inventoryItemIds.size() == 0) return 0;Set<InventoryItem> inventoryItems = inventoryItemRepository.selectByIds(inventoryItemIds).stream().filter(x -> x.getSerialNumber() != null && !x.getSerialNumber().equals("")).collect(Collectors.toSet());inventoryItems = inventoryItems.stream().filter(inventoryItem -> !this.getBlockedImeis().contains(inventoryItem.getSerialNumber())).collect(Collectors.toSet());//Do not consider imei above 90 days for samsungList<String> samsungSerialNumbers = inventoryItems.stream().filter(x -> x.getItem().getBrand().equalsIgnoreCase("samsung")).map(x -> x.getSerialNumber()).collect(Collectors.toList());if (samsungSerialNumbers.size() > 0) {List<AgeingSummaryModel> billedImeiModels = warehouseInventoryItemRepository.findStockAgeingByFofoIdSerialNumbers(retailerId, samsungSerialNumbers);List<String> agedSerialNumbers = billedImeiModels.stream().filter(x -> x.isAgedAbove(365)).map(x -> x.getSerialNumber()).collect(Collectors.toList());if (agedSerialNumbers.size() > 0) {List<String> samsungExceptionsSerialNumbers = samsungExceptionRepository.selectAllBySerialNumber(agedSerialNumbers).stream().map(x -> x.getSerialNumber()).collect(Collectors.toList());agedSerialNumbers.removeAll(samsungExceptionsSerialNumbers);}inventoryItems = inventoryItems.stream().filter(x -> !agedSerialNumbers.contains(x.getSerialNumber())).collect(Collectors.toSet());}if (inventoryItems.size() == 0) return 0;Map<Integer, List<InventoryItem>> catalogInventoryItemMap = inventoryItems.stream().collect(Collectors.groupingBy(x -> x.getItem().getCatalogItemId()));Map<CatalogSummaryModel, List<SchemeSummaryModel>> catalogSchemeSummaryMap = tagListingRepository.getModelSchemesByCatalogIdsAndType(retailerId,partnerType, new ArrayList<>(catalogInventoryItemMap.keySet()), fofoOrder.getCreateTimestamp());int count = 0;for (Map.Entry<CatalogSummaryModel, List<SchemeSummaryModel>> catalogSummaryModelListEntry : catalogSchemeSummaryMap.entrySet()) {CatalogSummaryModel catalogSummaryModel = catalogSummaryModelListEntry.getKey();List<SchemeSummaryModel> schemeSummaryModels = catalogSummaryModelListEntry.getValue().stream().filter(x -> x != null).collect(Collectors.toList());List<SchemeType> allOutSchemeTypes = new ArrayList<>();allOutSchemeTypes.addAll(Arrays.asList(SchemeType.ACTIVATION, SchemeType.INVESTMENT, SchemeType.SPECIAL_SUPPORT, SchemeType.SELLOUT));allOutSchemeTypes.addAll(OUT_SCHEME_TYPES);schemeSummaryModels.stream().filter(x -> allOutSchemeTypes.contains(x.getSchemeType())).forEach(x -> x.setProcess(true));if (schemeSummaryModels.stream().filter(x -> x.isProcess()).count() == 0) continue;List<InventoryItem> modelInventoryItems = catalogInventoryItemMap.get(catalogSummaryModel.getCatalogId());for (InventoryItem inventoryItem : modelInventoryItems) {float inventoryItemCashback = this.createSchemeInOut(schemeSummaryModels, inventoryItem);if (inventoryItemCashback > 0) {count++;totalCashback += inventoryItemCashback;}}}if (count > 0) {walletService.addAmountToWallet(retailerId, fofoOrderId, WalletReferenceType.SCHEME_OUT, "Sales margin for invoice number "+ fofoOrder.getInvoiceNumber() + ". Total " + count + " pc(s)",totalCashback, fofoOrder.getCreateTimestamp());fofoOrder.setCashback(totalCashback + fofoOrder.getCashback());}}return totalCashback;}@Override//Tax rate has been passed to 0 to ensure no tax deductionpublic float getSpecialSupportAmount(float supportAmount, PartnerType partnerType, LocalDate onDate,int catalogId) throws ProfitMandiBusinessException {//int itemId = itemRepository.selectAllByCatalogItemId(catalogId).stream().findAny().get().getId();//float totalTaxRate = stateGstRateRepository.getTotalTaxRate(itemId);return this.getSpecialSupportAmount(supportAmount, partnerType, onDate, catalogId, 0);}@Overridepublic float getSpecialSupportAmount(float supportAmount, PartnerType partnerType, LocalDate onDate,int catalogId, float taxRate) throws ProfitMandiBusinessException {float totalMargin = this.selectPercentageScheme(partnerType, onDate, catalogId, false, 0, 0).stream().collect(Collectors.summingDouble(x -> x.getAmount())).floatValue();float amountToCredit = supportAmount * (1 - (totalMargin / (100 + taxRate)));return amountToCredit;}@Overridepublic void rollbackSchemes(List<Integer> inventoryItemIds, String rollbackReason)throws Exception {List<InventoryItem> inventoryItems = inventoryItemRepository.selectAllByIds(inventoryItemIds);Map<Integer, InventoryItem> inventoryItemMap = inventoryItems.stream().collect(Collectors.toMap(x -> x.getId(), x -> x));Map<Integer, Integer> purchasePartnerMap = inventoryItems.stream().collect(Collectors.toMap(x -> x.getPurchaseId(), x -> x.getFofoId(), (u, v) -> u));LOGGER.info("inventoryItemIds - {}", inventoryItemIds);List<SchemeInOut> schemeInOuts = schemeInOutRepository.selectByInventoryItemIds(new HashSet<>(inventoryItemIds));List<Integer> schemeIds = schemeInOuts.stream().map(x -> x.getSchemeId()).distinct().collect(Collectors.toList());if (schemeIds.size() == 0) return;List<Scheme> schemes = schemeRepository.selectBySchemeIds(schemeIds);Map<Integer, Float> inSchemesMap = new HashMap<>();Map<Integer, Float> outSchemesMap = new HashMap<>();Map<Integer, Scheme> schemesMap = schemes.stream().collect(Collectors.toMap(x -> x.getId(), x -> x));for (SchemeInOut schemeInOut : schemeInOuts) {Map<Integer, Float> schemePayoutMap;int inventoryItemId = schemeInOut.getInventoryItemId();if (schemeInOut.getRolledBackTimestamp() == null) {schemeInOut.setRolledBackTimestamp(LocalDateTime.now());if (schemeInOut.getStatus() == null || schemeInOut.getStatus().equals(SchemePayoutStatus.CREDITED)) {Scheme scheme = schemesMap.get(schemeInOut.getSchemeId());if (scheme.getType().equals(SchemeType.IN)) {schemePayoutMap = inSchemesMap;} else {schemePayoutMap = outSchemesMap;}if (!schemePayoutMap.containsKey(inventoryItemId)) {schemePayoutMap.put(inventoryItemId, 0f);}schemePayoutMap.put(inventoryItemId, inSchemesMap.get(inventoryItemId) == null ? 0 : inSchemesMap.get(inventoryItemId) + schemeInOut.getAmount());}schemeInOut.setStatus(SchemePayoutStatus.REJECTED);schemeInOut.setStatusDescription(rollbackReason);}}Map<Integer, Double> purchaseRollbackAmountMap = inSchemesMap.entrySet().stream().collect(Collectors.groupingBy(x -> inventoryItemMap.get(x.getKey()).getPurchaseId(), Collectors.summingDouble(x -> x.getValue())));for (Map.Entry<Integer, Double> purchaseRollbackAmountEntry : purchaseRollbackAmountMap.entrySet()) {int purchaseId = purchaseRollbackAmountEntry.getKey();Double amountToRollback = purchaseRollbackAmountEntry.getValue();if (amountToRollback != null && amountToRollback > 0) {walletService.rollbackAmountFromWallet(purchasePartnerMap.get(purchaseId), amountToRollback.floatValue(), purchaseId,WalletReferenceType.SCHEME_IN, rollbackReason, LocalDateTime.now());}}}@Overridepublic Map<String, Object> getSchemes(Set<Integer> roleIds, int offset, int limit)throws ProfitMandiBusinessException {Map<String, Object> map = new HashMap<>();List<Scheme> schemes = null;long size = 0;if (roleManager.isAdmin(roleIds)) {schemes = schemeRepository.selectAll(offset, limit);size = schemeRepository.selectAllCount();} else {schemes = schemeRepository.selectActiveAll(offset, limit);size = schemeRepository.selectAllActiveCount();}map.put("schemes", schemes);map.put("start", offset + 1);map.put("size", size);if (schemes.size() < limit) {map.put("end", offset + schemes.size());} else {map.put("end", offset + limit);}return map;}@Overridepublic List<Scheme> getPaginatedSchemes(Set<Integer> roleIds, int offset, int limit)throws ProfitMandiBusinessException {LOGGER.info("requested offset=[{}], limit = [{}]", offset, limit);List<Scheme> schemes = null;if (roleManager.isAdmin(roleIds)) {schemes = schemeRepository.selectAll(offset, limit);} else {schemes = schemeRepository.selectActiveAll(offset, limit);}return schemes;}@Override// This is being called to reverse schemes while processing price Droppublic void reverseSchemes(List<InventoryItem> inventoryItems, int priceDropId, String reversalReason)throws ProfitMandiBusinessException {PriceDrop priceDrop = priceDropRepository.selectById(priceDropId);Map<Integer, List<InventoryItem>> purchaseInventoryListMap = inventoryItems.stream().collect(Collectors.groupingBy(InventoryItem::getPurchaseId, Collectors.toList()));for (Map.Entry<Integer, List<InventoryItem>> purchaseEntry : purchaseInventoryListMap.entrySet()) {float amountToCredit = 0;float amountToDebit = 0;int purchaseId = purchaseEntry.getKey();List<InventoryItem> purchaseInventoryItemList = purchaseEntry.getValue();Map<Integer, InventoryItem> inventoryItemsMap = purchaseInventoryItemList.stream().collect(Collectors.toMap(x -> x.getId(), x -> x));List<SchemeInOut> schemeInOuts = schemeInOutRepository.selectByInventoryItemIds(inventoryItemsMap.keySet());LOGGER.info("Scheme InOuts , {}", schemeInOuts);if (schemeInOuts.size() == 0) {continue;}List<Integer> schemeIds = schemeInOuts.stream().map(x -> x.getSchemeId()).collect(Collectors.toList());Map<Integer, Scheme> schemesMap = schemeRepository.selectBySchemeIds(schemeIds, 0, schemeIds.size()).stream().collect(Collectors.toMap(x -> x.getId(), x -> x));for (SchemeInOut schemeInOut : schemeInOuts) {InventoryItem ii = inventoryItemsMap.get(schemeInOut.getInventoryItemId());Scheme scheme = schemesMap.get(schemeInOut.getSchemeId());if (scheme.getAmountType().equals(AmountType.FIXED)) {continue;}if (scheme.getType().equals(SchemeType.IN) && schemeInOut.getRolledBackTimestamp() == null) {schemeInOut.setRolledBackTimestamp(LocalDateTime.now());schemeInOut.setStatus(SchemePayoutStatus.REJECTED);schemeInOut.setStatusDescription("Margin reversed due to price drop");// IF not credited then dont consider any credit/debit for that sio entryif (schemeInOut.getCreditTimestamp() != null) {amountToDebit += schemeInOut.getAmount();}}}int fofoId = inventoryItems.get(0).getFofoId();if (amountToDebit > 0) {walletService.addAmountToWallet(fofoId, purchaseId, WalletReferenceType.SCHEME_IN,MessageFormat.format(reversalReason, purchaseInventoryItemList.size()), -amountToDebit,priceDrop.getAffectedOn());}}}@AutowiredUserWalletRepository userWalletRepository;@AutowiredUserWalletHistoryRepository userWalletHistoryRepository;@Override// Always being called from cancel order/bad return means no SCHEME IN is consideredpublic void reverseSchemes(List<InventoryItem> inventoryItems, int reversalReference, String reversalReason,List<SchemeType> schemeTypes) throws ProfitMandiBusinessException {Map<Integer, InventoryItem> inventoryItemsMap = inventoryItems.stream().collect(Collectors.toMap(x -> x.getId(), x -> x));LOGGER.info("inventoryItems" + inventoryItems);Map<SchemeType, SchemeInOut> schemeTypeMap = new HashMap<>();List<SchemeInOut> schemeInOuts = schemeInOutRepository.selectByInventoryItemIds(inventoryItemsMap.keySet());List<SchemeInOut> rolledBacks = schemeInOuts.stream().filter(x -> x.getStatusDescription().equals(reversalReason)).collect(Collectors.toList());float amountToRollback = 0;if (!schemeInOuts.isEmpty()) {List<Integer> schemeIds = schemeInOuts.stream().map(x -> x.getSchemeId()).collect(Collectors.toList());LOGGER.info("schemeIds" + schemeIds);Map<Integer, Scheme> schemesMap = schemeRepository.selectBySchemeIds(schemeIds, 0, schemeIds.size()).stream().collect(Collectors.toMap(x -> x.getId(), x -> x));if (rolledBacks.size() > 0) {for (SchemeInOut schemeInOut : rolledBacks) {Scheme scheme = schemesMap.get(schemeInOut.getSchemeId());if (schemeTypes.contains(scheme.getType())) {schemeTypeMap.put(scheme.getType(), schemeInOut);if (schemeInOut.getCreditTimestamp() != null) {amountToRollback += schemeInOut.getAmount();}}}} else {for (SchemeInOut schemeInOut : schemeInOuts) {Scheme scheme = schemesMap.get(schemeInOut.getSchemeId());if (schemeTypes.contains(scheme.getType())) {if (schemeInOut.getRolledBackTimestamp() == null) {schemeInOut.setRolledBackTimestamp(LocalDateTime.now());if (schemeInOut.getStatus().equals(SchemePayoutStatus.CREDITED)) {amountToRollback += schemeInOut.getAmount();}schemeInOut.setStatus(SchemePayoutStatus.REJECTED);schemeInOut.setStatusDescription(reversalReason);}}}}}int fofoId = inventoryItems.get(0).getFofoId();WalletReferenceType walletReferenceType = schemeTypes.containsAll(SchemeService.OUT_SCHEME_TYPES) ? WalletReferenceType.SCHEME_OUT: schemeTypes.contains(SchemeType.ACTIVATION) ? WalletReferenceType.ACTIVATION_SCHEME: schemeTypes.contains(SchemeType.SPECIAL_SUPPORT) ? WalletReferenceType.SPECIAL_SUPPORT: schemeTypes.contains(SchemeType.INVESTMENT) ? WalletReferenceType.SCHEME_OUT : null;List<UserWalletHistory> userWalletHistoryList = null;if (amountToRollback > 0 && walletReferenceType != null) {// Mark appropriate reference of rollback investment marginif (schemeTypes.contains(SchemeType.INVESTMENT) && schemeTypeMap.containsKey(SchemeType.INVESTMENT)) {LocalDateTime creditTime = schemeTypeMap.get(SchemeType.INVESTMENT).getCreditTimestamp();if (creditTime == null) return;int investmentReversalReference = Integer.parseInt(FormattingUtils.getYearMonth(creditTime.minusMonths(1)));UserWallet userWallet = userWalletRepository.selectByRetailerId(fofoId);userWalletHistoryList = userWalletHistoryRepository.selectAllByreferenceIdandreferenceType(userWallet.getUserId(), investmentReversalReference, WalletReferenceType.INVESTMENT_PAYOUT);if (userWalletHistoryList.size() > 0) {walletReferenceType = WalletReferenceType.INVESTMENT_PAYOUT;} else {userWalletHistoryList = null;}}if (userWalletHistoryList == null) {userWalletHistoryList = userWalletHistoryRepository.selectAllByreferenceIdandreferenceType(reversalReference, walletReferenceType);}if (userWalletHistoryList.size() > 0) {int maxDeductible = userWalletHistoryList.stream().collect(Collectors.summingInt(x -> x.getAmount()));if (maxDeductible > 0) {LOGGER.info("----------> maxDeductible {}, amountToRollback {}, reversalReference {}, walletReferenceType {} ", maxDeductible, amountToRollback, reversalReference, walletReferenceType);walletService.rollbackAmountFromWallet(fofoId, Math.min(maxDeductible, amountToRollback), reversalReference, walletReferenceType,reversalReason, LocalDateTime.now());}}}}@Overridepublic double getTotalMargin(int itemId, PartnerType partnerType, LocalDateTime dateTime) {Session session = sessionFactory.getCurrentSession();CriteriaBuilder cb = session.getCriteriaBuilder();CriteriaQuery<Double> criteriaQuery = cb.createQuery(Double.class);Root<SchemeItem> schemeItem = criteriaQuery.from(SchemeItem.class);Root<Scheme> scheme = criteriaQuery.from(Scheme.class);Predicate schemePredicate = cb.equal(scheme.get(ProfitMandiConstants.AMOUNT_TYPE), AmountType.PERCENTAGE);Predicate lessThanPredicate = cb.lessThanOrEqualTo(scheme.get(ProfitMandiConstants.END_DATE_TIME), dateTime);Predicate greaterThanPredicate = cb.greaterThanOrEqualTo(scheme.get(ProfitMandiConstants.START_DATE_TIME),dateTime);Predicate joinPredicate = cb.equal(scheme.get("id"), schemeItem.get("schemeId"));Predicate schemeItemPredicate = cb.equal(schemeItem.get(ProfitMandiConstants.ITEM_ID), itemId);criteriaQuery.select(cb.sum(scheme.get(ProfitMandiConstants.AMOUNT))).where(schemePredicate, lessThanPredicate,greaterThanPredicate, schemeItemPredicate, joinPredicate);Query<Double> query = session.createQuery(criteriaQuery);return query.getSingleResult() + ProfitMandiConstants.SCHEME_INVESTMENT_MARGIN;}@Overridepublic Map<Integer, Float> getCatalogSchemeCashBack(int fofoId, List<Integer> catalogIds) throwsProfitMandiBusinessException {PartnerType partnerType = partnerTypeChangeService.getTypeOnDate(fofoId, LocalDate.now());Map<CatalogSummaryModel, List<SchemeSummaryModel>> catalogModelMap = tagListingRepository.getModelSchemesByCatalogIdsAndType(fofoId, partnerType, catalogIds, LocalDate.now().atStartOfDay());Map<Integer, Float> catalogCashbackMap = new HashMap<>();for (Map.Entry<CatalogSummaryModel, List<SchemeSummaryModel>> catalogSummaryModelListEntry : catalogModelMap.entrySet()) {int catalogItemId = catalogSummaryModelListEntry.getKey().getCatalogId();List<SchemeSummaryModel> schemeSummaryModels = catalogSummaryModelListEntry.getValue();float totalCashback = schemeSummaryModels.stream().filter(x -> Arrays.asList(SchemeType.ACTIVATION, SchemeType.SPECIAL_SUPPORT, SchemeType.SELLOUT).contains(x.getSchemeType())&& x.getAmountType().equals(AmountType.FIXED)).collect(Collectors.summingDouble(x -> x.getAmount())).floatValue();catalogCashbackMap.put(catalogItemId, totalCashback);}return catalogCashbackMap;}@Overridepublic List<Scheme> selectSchemeByPartnerTypeFofoId(PartnerType partnerType, LocalDate onDate, int catalogId,int fofoId, int offset, int limit) throws ProfitMandiBusinessException {Session session = sessionFactory.getCurrentSession();final TypedQuery<Scheme> typedQuery = session.createNamedQuery("Scheme.selectSchemeByModelsPartnerTypeFofoId", Scheme.class);typedQuery.setParameter("catalogIds", Arrays.asList(catalogId));typedQuery.setParameter("fofoIds", Arrays.asList(fofoId, 0));typedQuery.setParameter("onDate", onDate.atStartOfDay());typedQuery.setParameter("partnerTypes", Arrays.asList(partnerType, partnerType.ALL));typedQuery.setFirstResult(offset);if (limit != 0) {typedQuery.setMaxResults(limit);}return typedQuery.getResultList();}@Overridepublic void processSchemeIn(List<InventoryItem> inventoryItems) throws ProfitMandiBusinessException {Map<Integer, List<InventoryItem>> purchaseIdInventoryItemsMap = inventoryItems.stream().collect(Collectors.groupingBy(x -> x.getPurchaseId()));for (Map.Entry<Integer, List<InventoryItem>> purchaseIdInventoryItemEntry : purchaseIdInventoryItemsMap.entrySet()) {Purchase purchase = purchaseRepository.selectById(purchaseIdInventoryItemEntry.getKey());this.processSchemeIn(purchase, purchaseIdInventoryItemEntry.getValue());}}@AutowiredFofoStoreRepository fofoStoreRepository;@Override@Cacheable(value = "staticscheme", cacheManager = "oneDayCacheManager")public Scheme getStaticScheme(int fofoId) throws ProfitMandiBusinessException {FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(fofoId);Scheme scheme = null;if (fofoStore.getTarget() > 0) {scheme = new Scheme();scheme.setName("Super Retailer - Club 3");scheme.setStartDateTime(LocalDate.of(2024, 8, 1).atStartOfDay());scheme.setEndDateTime(LocalDate.of(2024, 11, 30).atTime(LocalTime.MAX));scheme.setTarget(fofoStore.getTarget());if (scheme.getEndDateTime().plusDays(5).isBefore(LocalDateTime.now())) return null;}return scheme;}@Overridepublic List<Scheme> selectSchemeByPartnerType(PartnerType partnerType, LocalDate onDate, int catalogId,boolean isAdmin, int offset, int limit) throws ProfitMandiBusinessException {Session session = sessionFactory.getCurrentSession();List<Predicate> andPredicates = new ArrayList<>();CriteriaBuilder cb = session.getCriteriaBuilder();CriteriaQuery<Scheme> query = cb.createQuery(Scheme.class);Root<Scheme> scheme = query.from(Scheme.class);if (!partnerType.equals(PartnerType.ALL)) {List<PartnerType> pt = new ArrayList<>();pt.add(PartnerType.ALL);pt.add(partnerType);andPredicates.add(cb.in(scheme.get("partnerType")).value(pt));}cb.desc(cb.isNull(scheme.get("expireTimestamp")));if (catalogId > 0) {List<Integer> schemeIds = schemeItemRepository.selectSchemeIdByCatalogId(catalogId);LOGGER.info("schemeId" + schemeIds);if (schemeIds.isEmpty()) {return new ArrayList<>();}andPredicates.add(cb.in(scheme.get("id")).value(schemeIds));if (onDate != null) {andPredicates.add(cb.greaterThan(scheme.get("endDateTime"), onDate.atStartOfDay()));andPredicates.add(cb.lessThanOrEqualTo(scheme.get("startDateTime"), onDate.atStartOfDay()));}}if (!isAdmin) {andPredicates.add(cb.isNotNull(scheme.get("activeTimestamp")));}query.where(cb.and(andPredicates.toArray(new Predicate[0])));query.orderBy(cb.desc(cb.function("isnull", Boolean.class, scheme.get("expireTimestamp"))));if (limit == 0) {return session.createQuery(query).setFirstResult(offset).getResultList();}return session.createQuery(query).setFirstResult(offset).setMaxResults(limit).getResultList();}@Overridepublic List<Scheme> selectPercentageScheme(PartnerType partnerType, LocalDate onDate, int catalogId,boolean isAdmin, int offset, int limit) throws ProfitMandiBusinessException {List<Scheme> schemes = this.selectSchemeByPartnerType(partnerType, onDate, catalogId, isAdmin, offset, limit);return schemes.stream().filter(x -> x.getAmountType().equals(AmountType.PERCENTAGE)).collect(Collectors.toList());}@Overridepublic void processActivation() throws ProfitMandiBusinessException {List<SchemeInOut> pendingPayouts = schemeInOutRepository.selectAllPending();List<Integer> schemeIds = new ArrayList<>();Set<Integer> inventoryIds = new HashSet<>();for (SchemeInOut pendingPayout : pendingPayouts) {schemeIds.add(pendingPayout.getSchemeId());}Map<Integer, Scheme> schemesMap = schemeRepository.selectBySchemeIds(schemeIds, 0, 0).stream().filter(x -> x.getType().equals(SchemeType.ACTIVATION) || x.getType().equals(SchemeType.SPECIAL_SUPPORT)).collect(Collectors.toMap(x -> x.getId(), x -> x));pendingPayouts = pendingPayouts.stream().filter(x -> schemesMap.get(x.getSchemeId()) != null).collect(Collectors.toList());for (SchemeInOut pendingPayout : pendingPayouts) {inventoryIds.add(pendingPayout.getInventoryItemId());}Map<Integer, InventoryItem> inventoryItemMap = inventoryItemRepository.selectByIds(inventoryIds).stream().collect(Collectors.toMap(x -> x.getId(), x -> x));Map<String, InventoryItem> serialNumberMap = inventoryItemMap.values().stream().collect(Collectors.toMap(x -> x.getSerialNumber(), x -> x));List<ActivatedImei> activatedImeis = activatedImeiRepository.selectBySerialNumbers(new ArrayList<>(serialNumberMap.keySet())).stream().collect(Collectors.toList());Map<String, ActivatedImei> activatedImeiMap = activatedImeis.stream().collect(Collectors.toMap(x -> x.getSerialNumber().toLowerCase(), x -> x));for (SchemeInOut pendingPayout : pendingPayouts) {Scheme scheme = schemesMap.get(pendingPayout.getSchemeId());InventoryItem ii = inventoryItemMap.get(pendingPayout.getInventoryItemId());String serialNumber = ii.getSerialNumber().toLowerCase();ActivatedImei activatedImei = activatedImeiMap.get(serialNumber);if (activatedImei == null) {continue;}if (scheme.isWithinRange(activatedImei.getActivationTimestamp())) {int fofoId = ii.getFofoId();// Get latest order Idint orderId = scanRecordRepository.selectByInventoryItemId(ii.getId()).stream().filter(x -> x.getOrderId() > 0).sorted(Comparator.comparing(ScanRecord::getCreateTimestamp).reversed()).findFirst().get().getOrderId();FofoOrder fofoOrder = fofoOrderRepository.selectByOrderId(orderId);InventoryPayoutModel inventoryPayoutModel = priceCircularService.getPayouts(ii);AmountModel amountModel = new AmountModel();amountModel.setAmount(scheme.getAmount());amountModel.setAmountType(scheme.getAmountType());double amountToRollout = inventoryPayoutModel.getRolloutAmount(amountModel);pendingPayout.setAmount((float) amountToRollout);if (scheme.getType().equals(SchemeType.ACTIVATION)) {walletService.addAmountToWallet(fofoId, orderId, WalletReferenceType.ACTIVATION_SCHEME,"Activation margin for " + ii.getItem().getItemDescriptionNoColor() + ", Imei - " + serialNumber, (float) amountToRollout,fofoOrder.getCreateTimestamp());pendingPayout.setStatusDescription("Activation margin credited, activated on " + FormattingUtils.formatDate(activatedImei.getActivationTimestamp()));} else {walletService.addAmountToWallet(fofoId, orderId, WalletReferenceType.SPECIAL_SUPPORT,"Special Support for " + ii.getItem().getItemDescriptionNoColor() + ", Imei - " + serialNumber, (float) amountToRollout,fofoOrder.getCreateTimestamp());pendingPayout.setStatusDescription("Special support credited, activated on " + FormattingUtils.formatDate(activatedImei.getActivationTimestamp()));}pendingPayout.setCreditTimestamp(LocalDateTime.now());pendingPayout.setStatus(SchemePayoutStatus.CREDITED);} else {pendingPayout.setStatus(SchemePayoutStatus.REJECTED);pendingPayout.setRolledBackTimestamp(LocalDateTime.now());;pendingPayout.setStatusDescription("Rejected, activated on " + FormattingUtils.formatDate(activatedImei.getActivationTimestamp()));}}}@Overridepublic void processActivatedImeisForSchemes() throws ProfitMandiBusinessException {List<SchemesImeisModel> schemesImeisModels = schemeRepository.selectSelectUnpaidSchemes();LOGGER.info("Total Size - " + schemesImeisModels.size());List<Integer> orderIds = schemesImeisModels.stream().map(x -> x.getOrderId()).collect(Collectors.toList());List<FofoOrder> fofoOrders = fofoOrderRepository.selectAllByOrderIds(orderIds);Map<Integer, FofoOrder> validOrdersMap = fofoOrders.stream().filter(x -> x.getCancelledTimestamp() == null).collect(Collectors.toMap(x -> x.getId(), x -> x));Map<String, List<SchemesImeisModel>> validImeiSchemesModelMap = schemesImeisModels.stream().filter(x -> validOrdersMap.containsKey(x.getOrderId())).collect(Collectors.groupingBy(x -> x.getImei()));for (Map.Entry<String, List<SchemesImeisModel>> imeiListEntry : validImeiSchemesModelMap.entrySet()) {SchemesImeisModel schemesImeisModel = imeiListEntry.getValue().get(0);List<Integer> schemeIds = imeiListEntry.getValue().stream().map(x -> x.getSchemeId()).collect(Collectors.toList());LOGGER.info("Serial Number - {}, Scheme IDs - {}", schemesImeisModel.getImei(), schemeIds);InventoryItem inventoryItem = inventoryItemRepository.selectById(schemesImeisModel.getInventoryItemId());List<Scheme> schemes = schemeRepository.selectBySchemeIds(schemeIds);List<Scheme> supportSchemes = schemes.stream().filter(x -> Arrays.asList(SchemeType.SPECIAL_SUPPORT, SchemeType.ACTIVATION).contains(x.getType())).collect(Collectors.toList());if (supportSchemes.size() > 0) {for (Scheme scheme : supportSchemes) {List<SchemeInOut> schemeInOuts = schemeInOutRepository.selectByScheme(scheme.getId(), inventoryItem.getId());if (schemeInOuts.stream().filter(x -> Arrays.asList(SchemePayoutStatus.CREDITED, SchemePayoutStatus.PENDING).contains(x.getStatus())).count() > 0) {continue;}SchemeInOut sio = new SchemeInOut();sio.setAmount(0);sio.setInventoryItemId(inventoryItem.getId());sio.setSchemeId(scheme.getId());sio.setStatusDescription("Activation pending for IMEI#" + inventoryItem.getSerialNumber());sio.setStatus(SchemePayoutStatus.PENDING);schemeInOutRepository.persist(sio);}}}}}