| Line 1... |
Line 1... |
| 1 |
package com.spice.profitmandi.service.catalog;
|
1 |
package com.spice.profitmandi.service.catalog;
|
| 2 |
|
2 |
|
| 3 |
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
|
3 |
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
|
| 4 |
import com.spice.profitmandi.common.web.client.RestClient;
|
4 |
import com.spice.profitmandi.common.model.ComboCancellationResult;
|
| - |
|
5 |
import com.spice.profitmandi.common.model.ComboCancellationResult.ComboLinkedOrder;
|
| 5 |
import com.spice.profitmandi.dao.entity.catalog.*;
|
6 |
import com.spice.profitmandi.dao.entity.catalog.*;
|
| 6 |
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
|
7 |
import com.spice.profitmandi.dao.entity.fofo.FofoStore;
|
| - |
|
8 |
import com.spice.profitmandi.dao.entity.transaction.Order;
|
| - |
|
9 |
import in.shop2020.model.v1.order.OrderStatus;
|
| 7 |
import com.spice.profitmandi.dao.model.UserCart;
|
10 |
import com.spice.profitmandi.dao.model.UserCart;
|
| 8 |
import com.spice.profitmandi.dao.repository.catalog.*;
|
11 |
import com.spice.profitmandi.dao.repository.catalog.*;
|
| 9 |
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
|
12 |
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
|
| - |
|
13 |
import com.spice.profitmandi.dao.repository.transaction.OrderRepository;
|
| 10 |
import com.spice.profitmandi.dao.repository.user.CartLineRepository;
|
14 |
import com.spice.profitmandi.dao.repository.user.CartLineRepository;
|
| 11 |
import com.spice.profitmandi.dao.repository.user.UserRepository;
|
15 |
import com.spice.profitmandi.dao.repository.user.UserRepository;
|
| 12 |
import org.apache.logging.log4j.LogManager;
|
16 |
import org.apache.logging.log4j.LogManager;
|
| 13 |
import org.apache.logging.log4j.Logger;
|
17 |
import org.apache.logging.log4j.Logger;
|
| 14 |
import org.springframework.beans.factory.annotation.Autowired;
|
18 |
import org.springframework.beans.factory.annotation.Autowired;
|
| Line 37... |
Line 41... |
| 37 |
@Autowired
|
41 |
@Autowired
|
| 38 |
ComboOptionRepository comboOptionRepository;
|
42 |
ComboOptionRepository comboOptionRepository;
|
| 39 |
@Autowired
|
43 |
@Autowired
|
| 40 |
ComboMappedModelRepository comboMappedModelRepository;
|
44 |
ComboMappedModelRepository comboMappedModelRepository;
|
| 41 |
|
45 |
|
| - |
|
46 |
@Autowired
|
| - |
|
47 |
OrderRepository orderRepository;
|
| - |
|
48 |
|
| 42 |
private static final Logger LOGGER = LogManager.getLogger(ComboServiceImpl.class);
|
49 |
private static final Logger LOGGER = LogManager.getLogger(ComboServiceImpl.class);
|
| 43 |
|
50 |
|
| 44 |
@Override
|
51 |
@Override
|
| 45 |
public boolean validateCombo(UserCart userCart) throws ProfitMandiBusinessException {
|
52 |
public boolean validateCombo(UserCart userCart) throws ProfitMandiBusinessException {
|
| 46 |
Map<Integer, Integer> itemQtyMap = cartLineRepository.selectAllByCart(userCart.getCartId()).stream().collect(Collectors.toMap(x -> x.getItemId(), x -> x.getQuantity()));
|
53 |
Map<Integer, Integer> itemQtyMap = cartLineRepository.selectAllByCart(userCart.getCartId()).stream().collect(Collectors.toMap(x -> x.getItemId(), x -> x.getQuantity()));
|
| Line 85... |
Line 92... |
| 85 |
throw new ProfitMandiBusinessException("FOC items cant be billed above Combo Qty", "FOC", "FOC items cant be billed above Combo Qty");
|
92 |
throw new ProfitMandiBusinessException("FOC items cant be billed above Combo Qty", "FOC", "FOC items cant be billed above Combo Qty");
|
| 86 |
}
|
93 |
}
|
| 87 |
}
|
94 |
}
|
| 88 |
return true;
|
95 |
return true;
|
| 89 |
}
|
96 |
}
|
| - |
|
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 |
}
|
| 90 |
}
|
279 |
}
|