| 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 |
}
|