Rev 33916 | Blame | Compare with Previous | Last modification | View Log | RSS feed
package com.spice.profitmandi.service.catalog;import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;import com.spice.profitmandi.common.model.ComboCancellationResult;import com.spice.profitmandi.common.model.ComboCancellationResult.ComboLinkedOrder;import com.spice.profitmandi.dao.entity.catalog.*;import com.spice.profitmandi.dao.entity.fofo.FofoStore;import com.spice.profitmandi.dao.entity.transaction.Order;import in.shop2020.model.v1.order.OrderStatus;import com.spice.profitmandi.dao.model.UserCart;import com.spice.profitmandi.dao.repository.catalog.*;import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;import com.spice.profitmandi.dao.repository.transaction.OrderRepository;import com.spice.profitmandi.dao.repository.user.CartLineRepository;import com.spice.profitmandi.dao.repository.user.UserRepository;import org.apache.logging.log4j.LogManager;import org.apache.logging.log4j.Logger;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import java.util.*;import java.util.stream.Collectors;@Componentpublic class ComboServiceImpl implements ComboService {@AutowiredCartLineRepository cartLineRepository;@AutowiredItemRepository itemRepository;@AutowiredCatalogRepository catalogRepository;@AutowiredUserRepository userRepository;@AutowiredFofoStoreRepository fofoStoreRepository;@AutowiredComboModelRepository comboModelRepository;@AutowiredComboOptionRepository comboOptionRepository;@AutowiredComboMappedModelRepository comboMappedModelRepository;@AutowiredOrderRepository orderRepository;private static final Logger LOGGER = LogManager.getLogger(ComboServiceImpl.class);@Overridepublic boolean validateCombo(UserCart userCart) throws ProfitMandiBusinessException {Map<Integer, Integer> itemQtyMap = cartLineRepository.selectAllByCart(userCart.getCartId()).stream().collect(Collectors.toMap(x -> x.getItemId(), x -> x.getQuantity()));LOGGER.info("itemQtyMap - {}", itemQtyMap);List<Item> items = itemRepository.selectByIds(itemQtyMap.keySet());Set<Integer> focModelSet = items.stream().filter(x -> x.getBrand().equals("FOC")).map(x -> x.getCatalogItemId()).collect(Collectors.toSet());Map<Integer, Integer> catalogIdCartQtyMap = items.stream().collect(Collectors.groupingBy(x -> x.getCatalogItemId(), Collectors.summingInt(x -> itemQtyMap.get(x.getId()))));FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(userCart.getUserId());List<ComboModel> comboModelList = comboModelRepository.selectByCatalogIdsAndWarehouseId(new ArrayList<>(catalogIdCartQtyMap.keySet()), fofoStore.getWarehouseId());if(comboModelList.size() == 0) {return false;}Map<Integer, List<ComboModel>> comboMap = comboModelList.stream().collect(Collectors.groupingBy(x -> x.getCatalogId()));for (Map.Entry<Integer, List<ComboModel>> comboEntry : comboMap.entrySet()) {int catalogId = comboEntry.getKey();List<ComboModel> comboModels = comboEntry.getValue();ComboModel filteredModel = comboModels.stream().filter(x -> x.getQty() == catalogIdCartQtyMap.get(catalogId)).findAny().orElse(null);if (filteredModel == null) {throw new ProfitMandiBusinessException("Combo Quantity mismatched", "Combo Quantity mismatched", "Combo Quantity mismatched");}List<ComboOption> comboOptions = comboOptionRepository.selectByComboId(filteredModel.getId());List<ComboMappedModel> comboMappedModels = comboMappedModelRepository.selectByComboOptionId(comboOptions.get(0).getId());for (ComboMappedModel comboMappedModel : comboMappedModels) {int secondaryCatalogId = comboMappedModel.getComboCatalogId();int requiredQty = comboMappedModel.getComboQty();LOGGER.info("catalogIdCartQtyMap.get(secondaryCatalogId) - {}", catalogIdCartQtyMap.get(secondaryCatalogId));if (!catalogIdCartQtyMap.containsKey(secondaryCatalogId) || requiredQty > catalogIdCartQtyMap.get(secondaryCatalogId)) {Catalog catalog = catalogRepository.selectCatalogById(catalogId);throw new ProfitMandiBusinessException("Missing required Qty for Combo", "", "Missing required qty for Combo - " + catalog.getDescription());} else {catalogIdCartQtyMap.put(secondaryCatalogId, catalogIdCartQtyMap.get(secondaryCatalogId) - requiredQty);}}}for (int focModel : focModelSet) {int qtyLeft = catalogIdCartQtyMap.get(focModel);if (qtyLeft != 0) {throw new ProfitMandiBusinessException("FOC items cant be billed above Combo Qty", "FOC", "FOC items cant be billed above Combo Qty");}}return true;}// Combo cancellation rules (linkage resolved via the catalog combo definition, NOT just shared transaction):// secondary is FOC -> cancel main: auto-cancel the FOC (silent); cancel FOC: standalone OK, main stays// secondary is non-FOC -> cancel main: standalone OK, secondary stays; cancel secondary: force-cancel main (confirm)@Overridepublic ComboCancellationResult checkOrderCancellationForCombo(List<Order> orders) {ComboCancellationResult result = ComboCancellationResult.noCombo();if (orders == null || orders.isEmpty()) {return result;}Set<Integer> cancelledIds = orders.stream().map(Order::getId).collect(Collectors.toSet());Map<Integer, List<Order>> cancelledByTransaction = orders.stream().filter(o -> o.getTransactionId() != null).collect(Collectors.groupingBy(Order::getTransactionId));Set<Integer> autoCancelFocIds = new HashSet<>();Set<Integer> linkedMainIds = new HashSet<>();for (Map.Entry<Integer, List<Order>> entry : cancelledByTransaction.entrySet()) {List<Order> cancelledInTransaction = entry.getValue();List<Order> siblings = orderRepository.selectAllByTransactionId(entry.getKey());if (siblings == null || siblings.isEmpty()) {continue;}Integer warehouseId = resolveWarehouseId(siblings);if (warehouseId == null) {continue;}Map<Integer, Integer> orderIdToCatalogId = resolveOrderCatalogIds(siblings);// main-catalog -> secondary-catalogs, from the combo definition for this warehouseMap<Integer, Set<Integer>> mainToSecondaries =buildComboCatalogMap(new ArrayList<>(new HashSet<>(orderIdToCatalogId.values())), warehouseId);if (mainToSecondaries.isEmpty()) {continue;}// inverse: secondary-catalog -> main-catalogsMap<Integer, Set<Integer>> secondaryToMains = new HashMap<>();for (Map.Entry<Integer, Set<Integer>> e : mainToSecondaries.entrySet()) {for (Integer secondaryCatalog : e.getValue()) {secondaryToMains.computeIfAbsent(secondaryCatalog, k -> new HashSet<>()).add(e.getKey());}}// siblings indexed by their catalog idMap<Integer, List<Order>> siblingsByCatalog = new HashMap<>();for (Order sibling : siblings) {Integer catalogId = orderIdToCatalogId.get(sibling.getId());if (catalogId != null) {siblingsByCatalog.computeIfAbsent(catalogId, k -> new ArrayList<>()).add(sibling);}}// mains that will be cancelled = user-selected mains + mains forced by a non-FOC secondary cancelSet<Integer> mainOrderIdsToCancel = new HashSet<>();for (Order cancelled : cancelledInTransaction) {Integer catalogId = orderIdToCatalogId.get(cancelled.getId());if (catalogId != null && mainToSecondaries.containsKey(catalogId)) {mainOrderIdsToCancel.add(cancelled.getId());}}// Pass 1: a non-FOC secondary being cancelled mandatorily cancels its main(s) -> ask for confirmationfor (Order cancelled : cancelledInTransaction) {Integer catalogId = orderIdToCatalogId.get(cancelled.getId());if (catalogId == null || !secondaryToMains.containsKey(catalogId) || isFoc(cancelled)) {continue;}for (Integer mainCatalog : secondaryToMains.get(catalogId)) {for (Order mainOrder : siblingsByCatalog.getOrDefault(mainCatalog, Collections.emptyList())) {if (cancelledIds.contains(mainOrder.getId())|| !OrderStatus.SUBMITTED_FOR_PROCESSING.equals(mainOrder.getStatus())) {continue;}mainOrderIdsToCancel.add(mainOrder.getId());if (linkedMainIds.add(mainOrder.getId())) {result.setConfirmationRequired(true);result.getLinkedMainOrders().add(new ComboLinkedOrder(mainOrder.getId(),mainOrder.getLineItem().getBrand() + " " + mainOrder.getLineItem().getModelName() + " " + mainOrder.getLineItem().getModelNumber(),mainOrder.getLineItem().getQuantity()));}}}}// Pass 2: every main being cancelled auto-cancels its FOC secondaries (the free gift follows the main)for (Integer mainOrderId : mainOrderIdsToCancel) {Integer mainCatalog = orderIdToCatalogId.get(mainOrderId);if (mainCatalog == null) {continue;}for (Integer secondaryCatalog : mainToSecondaries.getOrDefault(mainCatalog, Collections.emptySet())) {for (Order secondary : siblingsByCatalog.getOrDefault(secondaryCatalog, Collections.emptyList())) {if (!cancelledIds.contains(secondary.getId())&& OrderStatus.SUBMITTED_FOR_PROCESSING.equals(secondary.getStatus())&& isFoc(secondary)) {autoCancelFocIds.add(secondary.getId());}}}}}result.getAutoCancelFocOrderIds().addAll(autoCancelFocIds);return result;}private boolean isFoc(Order order) {return order.getLineItem() != null && "FOC".equals(order.getLineItem().getBrand());}private Integer resolveWarehouseId(List<Order> siblings) {for (Order order : siblings) {if (order.getRetailerId() == null) {continue;}try {FofoStore store = fofoStoreRepository.selectByRetailerId(order.getRetailerId());if (store != null) {return store.getWarehouseId();}} catch (Exception e) {LOGGER.warn("Combo cancel: unable to resolve warehouse for retailer {} - {}", order.getRetailerId(), e.getMessage());}}return null;}private Map<Integer, Integer> resolveOrderCatalogIds(List<Order> siblings) {Set<Integer> itemIds = siblings.stream().filter(o -> o.getLineItem() != null && o.getLineItem().getItemId() != null).map(o -> o.getLineItem().getItemId()).collect(Collectors.toSet());Map<Integer, Integer> itemIdToCatalogId = new HashMap<>();if (!itemIds.isEmpty()) {try {for (Item item : itemRepository.selectByIds(itemIds)) {itemIdToCatalogId.put(item.getId(), item.getCatalogItemId());}} catch (Exception e) {LOGGER.warn("Combo cancel: unable to resolve items - {}", e.getMessage());}}Map<Integer, Integer> orderIdToCatalogId = new HashMap<>();for (Order order : siblings) {if (order.getLineItem() == null || order.getLineItem().getItemId() == null) {continue;}Integer catalogId = itemIdToCatalogId.get(order.getLineItem().getItemId());if (catalogId != null) {orderIdToCatalogId.put(order.getId(), catalogId);}}return orderIdToCatalogId;}private Map<Integer, Set<Integer>> buildComboCatalogMap(List<Integer> catalogIds, int warehouseId) {Map<Integer, Set<Integer>> mainToSecondaries = new HashMap<>();if (catalogIds.isEmpty()) {return mainToSecondaries;}List<ComboModel> comboModels = comboModelRepository.selectByCatalogIdsAndWarehouseId(catalogIds, warehouseId);for (ComboModel comboModel : comboModels) {for (ComboOption comboOption : comboOptionRepository.selectByComboId(comboModel.getId())) {for (ComboMappedModel mappedModel : comboMappedModelRepository.selectByComboOptionId(comboOption.getId())) {mainToSecondaries.computeIfAbsent(comboModel.getCatalogId(), k -> new HashSet<>()).add(mappedModel.getComboCatalogId());}}}return mainToSecondaries;}}