Subversion Repositories SmartDukaan

Rev

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

Rev Author Line No. Line
33916 amit.gupta 1
package com.spice.profitmandi.service.catalog;
2
 
3
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
36960 amit 4
import com.spice.profitmandi.common.model.ComboCancellationResult;
5
import com.spice.profitmandi.common.model.ComboCancellationResult.ComboLinkedOrder;
33916 amit.gupta 6
import com.spice.profitmandi.dao.entity.catalog.*;
7
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
36960 amit 8
import com.spice.profitmandi.dao.entity.transaction.Order;
9
import in.shop2020.model.v1.order.OrderStatus;
33916 amit.gupta 10
import com.spice.profitmandi.dao.model.UserCart;
11
import com.spice.profitmandi.dao.repository.catalog.*;
12
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
36960 amit 13
import com.spice.profitmandi.dao.repository.transaction.OrderRepository;
33916 amit.gupta 14
import com.spice.profitmandi.dao.repository.user.CartLineRepository;
15
import com.spice.profitmandi.dao.repository.user.UserRepository;
16
import org.apache.logging.log4j.LogManager;
17
import org.apache.logging.log4j.Logger;
18
import org.springframework.beans.factory.annotation.Autowired;
19
import org.springframework.stereotype.Component;
20
 
21
import java.util.*;
22
import java.util.stream.Collectors;
23
 
24
@Component
25
public class ComboServiceImpl implements ComboService {
26
    @Autowired
27
    CartLineRepository cartLineRepository;
28
    @Autowired
29
    ItemRepository itemRepository;
30
    @Autowired
31
    CatalogRepository catalogRepository;
32
 
33
    @Autowired
34
    UserRepository userRepository;
35
 
36
    @Autowired
37
    FofoStoreRepository fofoStoreRepository;
38
 
39
    @Autowired
40
    ComboModelRepository comboModelRepository;
41
    @Autowired
42
    ComboOptionRepository comboOptionRepository;
43
    @Autowired
44
    ComboMappedModelRepository comboMappedModelRepository;
45
 
36960 amit 46
    @Autowired
47
    OrderRepository orderRepository;
48
 
33916 amit.gupta 49
    private static final Logger LOGGER = LogManager.getLogger(ComboServiceImpl.class);
50
 
51
    @Override
52
    public boolean validateCombo(UserCart userCart) throws ProfitMandiBusinessException {
53
        Map<Integer, Integer> itemQtyMap = cartLineRepository.selectAllByCart(userCart.getCartId()).stream().collect(Collectors.toMap(x -> x.getItemId(), x -> x.getQuantity()));
54
        LOGGER.info("itemQtyMap - {}", itemQtyMap);
55
        List<Item> items = itemRepository.selectByIds(itemQtyMap.keySet());
56
        Set<Integer> focModelSet = items.stream().filter(x -> x.getBrand().equals("FOC")).map(x -> x.getCatalogItemId()).collect(Collectors.toSet());
57
        Map<Integer, Integer> catalogIdCartQtyMap = items.stream().collect(Collectors.groupingBy(x -> x.getCatalogItemId(), Collectors.summingInt(x -> itemQtyMap.get(x.getId()))));
58
 
59
 
60
        FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(userCart.getUserId());
61
 
62
        List<ComboModel> comboModelList = comboModelRepository.selectByCatalogIdsAndWarehouseId(new ArrayList<>(catalogIdCartQtyMap.keySet()), fofoStore.getWarehouseId());
63
        if(comboModelList.size() == 0) {
64
            return false;
65
        }
66
        Map<Integer, List<ComboModel>> comboMap = comboModelList.stream().collect(Collectors.groupingBy(x -> x.getCatalogId()));
67
 
68
        for (Map.Entry<Integer, List<ComboModel>> comboEntry : comboMap.entrySet()) {
69
            int catalogId = comboEntry.getKey();
70
            List<ComboModel> comboModels = comboEntry.getValue();
71
            ComboModel filteredModel = comboModels.stream().filter(x -> x.getQty() == catalogIdCartQtyMap.get(catalogId)).findAny().orElse(null);
72
            if (filteredModel == null) {
73
                throw new ProfitMandiBusinessException("Combo Quantity mismatched", "Combo Quantity mismatched", "Combo Quantity mismatched");
74
            }
75
            List<ComboOption> comboOptions = comboOptionRepository.selectByComboId(filteredModel.getId());
76
            List<ComboMappedModel> comboMappedModels = comboMappedModelRepository.selectByComboOptionId(comboOptions.get(0).getId());
77
            for (ComboMappedModel comboMappedModel : comboMappedModels) {
78
                int secondaryCatalogId = comboMappedModel.getComboCatalogId();
79
                int requiredQty = comboMappedModel.getComboQty();
80
                LOGGER.info("catalogIdCartQtyMap.get(secondaryCatalogId) - {}", catalogIdCartQtyMap.get(secondaryCatalogId));
81
                if (!catalogIdCartQtyMap.containsKey(secondaryCatalogId) || requiredQty > catalogIdCartQtyMap.get(secondaryCatalogId)) {
82
                    Catalog catalog = catalogRepository.selectCatalogById(catalogId);
83
                    throw new ProfitMandiBusinessException("Missing required Qty for Combo", "", "Missing required qty for Combo - " + catalog.getDescription());
84
                } else {
85
                    catalogIdCartQtyMap.put(secondaryCatalogId, catalogIdCartQtyMap.get(secondaryCatalogId) - requiredQty);
86
                }
87
            }
88
        }
89
        for (int focModel : focModelSet) {
90
            int qtyLeft = catalogIdCartQtyMap.get(focModel);
91
            if (qtyLeft != 0) {
92
                throw new ProfitMandiBusinessException("FOC items cant be billed above Combo Qty", "FOC", "FOC items cant be billed above Combo Qty");
93
            }
94
        }
95
        return true;
96
    }
36960 amit 97
 
98
    // Combo cancellation rules (linkage resolved via the catalog combo definition, NOT just shared transaction):
99
    //   secondary is FOC      -> cancel main: auto-cancel the FOC (silent);  cancel FOC: standalone OK, main stays
100
    //   secondary is non-FOC  -> cancel main: standalone OK, secondary stays; cancel secondary: force-cancel main (confirm)
101
    @Override
102
    public ComboCancellationResult checkOrderCancellationForCombo(List<Order> orders) {
103
        ComboCancellationResult result = ComboCancellationResult.noCombo();
104
        if (orders == null || orders.isEmpty()) {
105
            return result;
106
        }
107
 
108
        Set<Integer> cancelledIds = orders.stream().map(Order::getId).collect(Collectors.toSet());
109
 
110
        Map<Integer, List<Order>> cancelledByTransaction = orders.stream()
111
                .filter(o -> o.getTransactionId() != null)
112
                .collect(Collectors.groupingBy(Order::getTransactionId));
113
 
114
        Set<Integer> autoCancelFocIds = new HashSet<>();
115
        Set<Integer> linkedMainIds = new HashSet<>();
116
 
117
        for (Map.Entry<Integer, List<Order>> entry : cancelledByTransaction.entrySet()) {
118
            List<Order> cancelledInTransaction = entry.getValue();
119
            List<Order> siblings = orderRepository.selectAllByTransactionId(entry.getKey());
120
            if (siblings == null || siblings.isEmpty()) {
121
                continue;
122
            }
123
 
124
            Integer warehouseId = resolveWarehouseId(siblings);
125
            if (warehouseId == null) {
126
                continue;
127
            }
128
 
129
            Map<Integer, Integer> orderIdToCatalogId = resolveOrderCatalogIds(siblings);
130
 
131
            // main-catalog -> secondary-catalogs, from the combo definition for this warehouse
132
            Map<Integer, Set<Integer>> mainToSecondaries =
133
                    buildComboCatalogMap(new ArrayList<>(new HashSet<>(orderIdToCatalogId.values())), warehouseId);
134
            if (mainToSecondaries.isEmpty()) {
135
                continue;
136
            }
137
 
138
            // inverse: secondary-catalog -> main-catalogs
139
            Map<Integer, Set<Integer>> secondaryToMains = new HashMap<>();
140
            for (Map.Entry<Integer, Set<Integer>> e : mainToSecondaries.entrySet()) {
141
                for (Integer secondaryCatalog : e.getValue()) {
142
                    secondaryToMains.computeIfAbsent(secondaryCatalog, k -> new HashSet<>()).add(e.getKey());
143
                }
144
            }
145
 
146
            // siblings indexed by their catalog id
147
            Map<Integer, List<Order>> siblingsByCatalog = new HashMap<>();
148
            for (Order sibling : siblings) {
149
                Integer catalogId = orderIdToCatalogId.get(sibling.getId());
150
                if (catalogId != null) {
151
                    siblingsByCatalog.computeIfAbsent(catalogId, k -> new ArrayList<>()).add(sibling);
152
                }
153
            }
154
 
155
            // mains that will be cancelled = user-selected mains + mains forced by a non-FOC secondary cancel
156
            Set<Integer> mainOrderIdsToCancel = new HashSet<>();
157
            for (Order cancelled : cancelledInTransaction) {
158
                Integer catalogId = orderIdToCatalogId.get(cancelled.getId());
159
                if (catalogId != null && mainToSecondaries.containsKey(catalogId)) {
160
                    mainOrderIdsToCancel.add(cancelled.getId());
161
                }
162
            }
163
 
164
            // Pass 1: a non-FOC secondary being cancelled mandatorily cancels its main(s) -> ask for confirmation
165
            for (Order cancelled : cancelledInTransaction) {
166
                Integer catalogId = orderIdToCatalogId.get(cancelled.getId());
167
                if (catalogId == null || !secondaryToMains.containsKey(catalogId) || isFoc(cancelled)) {
168
                    continue;
169
                }
170
                for (Integer mainCatalog : secondaryToMains.get(catalogId)) {
171
                    for (Order mainOrder : siblingsByCatalog.getOrDefault(mainCatalog, Collections.emptyList())) {
172
                        if (cancelledIds.contains(mainOrder.getId())
173
                                || !OrderStatus.SUBMITTED_FOR_PROCESSING.equals(mainOrder.getStatus())) {
174
                            continue;
175
                        }
176
                        mainOrderIdsToCancel.add(mainOrder.getId());
177
                        if (linkedMainIds.add(mainOrder.getId())) {
178
                            result.setConfirmationRequired(true);
179
                            result.getLinkedMainOrders().add(new ComboLinkedOrder(
180
                                    mainOrder.getId(),
181
                                    mainOrder.getLineItem().getBrand() + " " + mainOrder.getLineItem().getModelName() + " " + mainOrder.getLineItem().getModelNumber(),
182
                                    mainOrder.getLineItem().getQuantity()
183
                            ));
184
                        }
185
                    }
186
                }
187
            }
188
 
189
            // Pass 2: every main being cancelled auto-cancels its FOC secondaries (the free gift follows the main)
190
            for (Integer mainOrderId : mainOrderIdsToCancel) {
191
                Integer mainCatalog = orderIdToCatalogId.get(mainOrderId);
192
                if (mainCatalog == null) {
193
                    continue;
194
                }
195
                for (Integer secondaryCatalog : mainToSecondaries.getOrDefault(mainCatalog, Collections.emptySet())) {
196
                    for (Order secondary : siblingsByCatalog.getOrDefault(secondaryCatalog, Collections.emptyList())) {
197
                        if (!cancelledIds.contains(secondary.getId())
198
                                && OrderStatus.SUBMITTED_FOR_PROCESSING.equals(secondary.getStatus())
199
                                && isFoc(secondary)) {
200
                            autoCancelFocIds.add(secondary.getId());
201
                        }
202
                    }
203
                }
204
            }
205
        }
206
 
207
        result.getAutoCancelFocOrderIds().addAll(autoCancelFocIds);
208
        return result;
209
    }
210
 
211
    private boolean isFoc(Order order) {
212
        return order.getLineItem() != null && "FOC".equals(order.getLineItem().getBrand());
213
    }
214
 
215
    private Integer resolveWarehouseId(List<Order> siblings) {
216
        for (Order order : siblings) {
217
            if (order.getRetailerId() == null) {
218
                continue;
219
            }
220
            try {
221
                FofoStore store = fofoStoreRepository.selectByRetailerId(order.getRetailerId());
222
                if (store != null) {
223
                    return store.getWarehouseId();
224
                }
225
            } catch (Exception e) {
226
                LOGGER.warn("Combo cancel: unable to resolve warehouse for retailer {} - {}", order.getRetailerId(), e.getMessage());
227
            }
228
        }
229
        return null;
230
    }
231
 
232
    private Map<Integer, Integer> resolveOrderCatalogIds(List<Order> siblings) {
233
        Set<Integer> itemIds = siblings.stream()
234
                .filter(o -> o.getLineItem() != null && o.getLineItem().getItemId() != null)
235
                .map(o -> o.getLineItem().getItemId())
236
                .collect(Collectors.toSet());
237
 
238
        Map<Integer, Integer> itemIdToCatalogId = new HashMap<>();
239
        if (!itemIds.isEmpty()) {
240
            try {
241
                for (Item item : itemRepository.selectByIds(itemIds)) {
242
                    itemIdToCatalogId.put(item.getId(), item.getCatalogItemId());
243
                }
244
            } catch (Exception e) {
245
                LOGGER.warn("Combo cancel: unable to resolve items - {}", e.getMessage());
246
            }
247
        }
248
 
249
        Map<Integer, Integer> orderIdToCatalogId = new HashMap<>();
250
        for (Order order : siblings) {
251
            if (order.getLineItem() == null || order.getLineItem().getItemId() == null) {
252
                continue;
253
            }
254
            Integer catalogId = itemIdToCatalogId.get(order.getLineItem().getItemId());
255
            if (catalogId != null) {
256
                orderIdToCatalogId.put(order.getId(), catalogId);
257
            }
258
        }
259
        return orderIdToCatalogId;
260
    }
261
 
262
    private Map<Integer, Set<Integer>> buildComboCatalogMap(List<Integer> catalogIds, int warehouseId) {
263
        Map<Integer, Set<Integer>> mainToSecondaries = new HashMap<>();
264
        if (catalogIds.isEmpty()) {
265
            return mainToSecondaries;
266
        }
267
        List<ComboModel> comboModels = comboModelRepository.selectByCatalogIdsAndWarehouseId(catalogIds, warehouseId);
268
        for (ComboModel comboModel : comboModels) {
269
            for (ComboOption comboOption : comboOptionRepository.selectByComboId(comboModel.getId())) {
270
                for (ComboMappedModel mappedModel : comboMappedModelRepository.selectByComboOptionId(comboOption.getId())) {
271
                    mainToSecondaries
272
                            .computeIfAbsent(comboModel.getCatalogId(), k -> new HashSet<>())
273
                            .add(mappedModel.getComboCatalogId());
274
                }
275
            }
276
        }
277
        return mainToSecondaries;
278
    }
33916 amit.gupta 279
}