Subversion Repositories SmartDukaan

Rev

Rev 27747 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
24783 amit.gupta 1
package com.smartdukaan.cron.scheduled;
2
 
24810 amit.gupta 3
import java.io.Serializable;
24783 amit.gupta 4
import java.time.LocalDate;
25503 amit.gupta 5
import java.time.LocalDateTime;
24783 amit.gupta 6
import java.util.ArrayList;
7
import java.util.Arrays;
24810 amit.gupta 8
import java.util.HashSet;
24783 amit.gupta 9
import java.util.List;
10
import java.util.Map;
24810 amit.gupta 11
import java.util.Set;
24783 amit.gupta 12
import java.util.stream.Collectors;
13
 
33256 amit.gupta 14
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
24783 amit.gupta 15
import org.apache.commons.io.output.ByteArrayOutputStream;
25076 amit.gupta 16
import org.apache.logging.log4j.LogManager;
17
import org.apache.logging.log4j.Logger;
24783 amit.gupta 18
import org.springframework.beans.factory.annotation.Autowired;
19
import org.springframework.core.io.ByteArrayResource;
20
import org.springframework.mail.javamail.JavaMailSender;
21
import org.springframework.stereotype.Component;
25072 amit.gupta 22
import org.springframework.transaction.annotation.Transactional;
24783 amit.gupta 23
 
25072 amit.gupta 24
import com.google.common.collect.Sets;
25076 amit.gupta 25
import com.smartdukaan.cron.Application;
24783 amit.gupta 26
import com.spice.profitmandi.common.util.FileUtil;
27
import com.spice.profitmandi.common.util.FormattingUtils;
28
import com.spice.profitmandi.common.util.Utils;
24810 amit.gupta 29
import com.spice.profitmandi.dao.entity.catalog.Scheme;
24826 amit.gupta 30
import com.spice.profitmandi.dao.entity.fofo.FofoLineItem;
25081 amit.gupta 31
import com.spice.profitmandi.dao.entity.fofo.FofoOrder;
24810 amit.gupta 32
import com.spice.profitmandi.dao.entity.fofo.InventoryItem;
25072 amit.gupta 33
import com.spice.profitmandi.dao.entity.fofo.Purchase;
34
import com.spice.profitmandi.dao.entity.fofo.ScanRecord;
24810 amit.gupta 35
import com.spice.profitmandi.dao.entity.fofo.SchemeInOut;
24783 amit.gupta 36
import com.spice.profitmandi.dao.entity.transaction.Order;
37
import com.spice.profitmandi.dao.entity.transaction.ReturnOrder;
38
import com.spice.profitmandi.dao.entity.transaction.UserWallet;
39
import com.spice.profitmandi.dao.entity.transaction.UserWalletHistory;
24810 amit.gupta 40
import com.spice.profitmandi.dao.enumuration.catalog.SchemeType;
25072 amit.gupta 41
import com.spice.profitmandi.dao.enumuration.fofo.ScanType;
24810 amit.gupta 42
import com.spice.profitmandi.dao.repository.catalog.SchemeRepository;
24783 amit.gupta 43
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
24826 amit.gupta 44
import com.spice.profitmandi.dao.repository.fofo.FofoLineItemRepository;
45
import com.spice.profitmandi.dao.repository.fofo.FofoOrderItemRepository;
25081 amit.gupta 46
import com.spice.profitmandi.dao.repository.fofo.FofoOrderRepository;
24810 amit.gupta 47
import com.spice.profitmandi.dao.repository.fofo.InventoryItemRepository;
25072 amit.gupta 48
import com.spice.profitmandi.dao.repository.fofo.PurchaseRepository;
49
import com.spice.profitmandi.dao.repository.fofo.ScanRecordRepository;
24810 amit.gupta 50
import com.spice.profitmandi.dao.repository.fofo.SchemeInOutRepository;
24783 amit.gupta 51
import com.spice.profitmandi.dao.repository.transaction.OrderRepository;
52
import com.spice.profitmandi.dao.repository.transaction.ReturnOrderRepository;
53
import com.spice.profitmandi.dao.repository.transaction.UserWalletHistoryRepository;
54
import com.spice.profitmandi.dao.repository.transaction.UserWalletRepository;
25083 amit.gupta 55
import com.spice.profitmandi.service.order.OrderService;
24783 amit.gupta 56
import com.spice.profitmandi.service.user.RetailerService;
57
 
58
import in.shop2020.model.v1.order.OrderStatus;
59
import in.shop2020.model.v1.order.WalletReferenceType;
60
 
61
@Component
25072 amit.gupta 62
@Transactional(rollbackFor = Throwable.class)
24783 amit.gupta 63
public class Reconciliation {
25503 amit.gupta 64
 
33256 amit.gupta 65
    private static final Logger LOGGER = LogManager.getLogger(Application.class);
25503 amit.gupta 66
 
33256 amit.gupta 67
    @Autowired
68
    private FofoStoreRepository fofoStoreRepository;
25072 amit.gupta 69
 
33256 amit.gupta 70
    private static final List<WalletReferenceType> reconciliationReferenceTypes = Arrays
71
            .asList(WalletReferenceType.PURCHASE, WalletReferenceType.SCHEME_IN, WalletReferenceType.SCHEME_OUT);
27745 amit.gupta 72
 
33256 amit.gupta 73
    @Autowired
74
    private PurchaseRepository purchaseRepository;
25503 amit.gupta 75
 
33256 amit.gupta 76
    @Autowired
77
    private OrderService orderService;
25503 amit.gupta 78
 
33256 amit.gupta 79
    @Autowired
80
    private FofoOrderItemRepository fofoOrderItemRepository;
81
    @Autowired
82
    private FofoLineItemRepository fofoLineItemRepository;
83
    @Autowired
84
    private UserWalletRepository userWalletRepository;
25072 amit.gupta 85
 
33256 amit.gupta 86
    @Autowired
87
    private InventoryItemRepository inventoryItemRepository;
25072 amit.gupta 88
 
33256 amit.gupta 89
    @Autowired
90
    private ScanRecordRepository scanRecordRepository;
91
    @Autowired
92
    private UserWalletHistoryRepository userWalletHistoryRepository;
93
    @Autowired
94
    private SchemeInOutRepository schemeInOutRepository;
95
    @Autowired
96
    private SchemeRepository schemeRepository;
25081 amit.gupta 97
 
33256 amit.gupta 98
    @Autowired
99
    private RetailerService retailerService;
100
    @Autowired
101
    private ReturnOrderRepository returnOrderRepository;
102
    @Autowired
103
    private OrderRepository orderRepository;
104
    @Autowired
105
    private FofoOrderRepository fofoOrderRepository;
24783 amit.gupta 106
 
33256 amit.gupta 107
    @Autowired
108
    private JavaMailSender mailSender;
25072 amit.gupta 109
 
33256 amit.gupta 110
    public void dailyReconciliation() throws Exception {
111
        LocalDate date = LocalDate.now().minusDays(1);
112
        dailyReconciliation(date);
113
    }
24810 amit.gupta 114
 
33256 amit.gupta 115
    public void dailyReconciliation(LocalDate localDate) throws Exception {
116
        Map<SchemeType, Set<Integer>> schemeTypeMap = schemeRepository.selectAll().stream()
117
                .collect(Collectors.groupingBy(Scheme::getType, Collectors.mapping(Scheme::getId, Collectors.toSet())));
118
        boolean reconciled = true;
119
        Map<Integer, String> stores = fofoStoreRepository.getStoresMap();
120
        List<List<?>> rows = new ArrayList<>();
121
        LocalDate yesterday = LocalDate.now().minusDays(1);
122
        Map<Integer, String> retailerNameMap = retailerService
123
                .getAllFofoRetailerIdNameMap(new ArrayList<>(stores.keySet()));
124
        for (int partnerId : stores.keySet()) {
125
            UserWallet uw = userWalletRepository.selectByRetailerId(partnerId);
126
            List<UserWalletHistory> walletHistory = userWalletHistoryRepository.selectByWalletIdAndDate(uw.getId(),
127
                    yesterday);
128
            Map<WalletReferenceType, List<UserWalletHistory>> referenceWiseWalletHistory = walletHistory.stream()
129
                    .collect(Collectors.groupingBy(x -> x.getReferenceType(), Collectors.toList()));
130
            reconciliationReferenceTypes.forEach(x -> {
131
                if (!referenceWiseWalletHistory.containsKey(x)) {
132
                    referenceWiseWalletHistory.put(x, new ArrayList<>());
133
                }
134
            });
24831 amit.gupta 135
 
33256 amit.gupta 136
            List<Serializable> reconciliation = new ArrayList<>();
137
            LocalDate dateToReconcile = yesterday;
138
            // "PartnerId", "Partner Name", "Reconciliation Date"
139
            reconciliation.addAll(Arrays.asList(partnerId, retailerNameMap.get(partnerId), dateToReconcile));
24810 amit.gupta 140
 
33256 amit.gupta 141
            for (WalletReferenceType walletReferenceType : reconciliationReferenceTypes) {
142
                List<UserWalletHistory> history = referenceWiseWalletHistory.get(walletReferenceType);
143
                Map<Integer, Integer> referenceWalletMap = history.stream().collect(
144
                        Collectors.groupingBy(x -> x.getReference(), Collectors.summingInt(x -> x.getAmount())));
145
                switch (walletReferenceType) {
146
                    case PURCHASE:
147
                        reconciliation.addAll(reconcileOrdersAndWallet(dateToReconcile, referenceWalletMap, history));
148
                        break;
149
                    case SCHEME_IN:
150
                        reconciliation.addAll(reconcileSchemeInAndWallet(dateToReconcile, referenceWalletMap, history,
151
                                schemeTypeMap.get(SchemeType.IN)));
152
                        break;
153
                    case SCHEME_OUT:
154
                        reconciliation.addAll(reconcileSchemeOutAndWallet(dateToReconcile, referenceWalletMap, history,
155
                                schemeTypeMap.get(SchemeType.OUT)));
156
                        break;
157
                    default:
158
                        break;
24783 amit.gupta 159
 
33256 amit.gupta 160
                }
161
            }
162
            reconciled = reconciled || Boolean.TRUE.equals(reconciliation.get(0));
163
            rows.add(reconciliation);
164
        }
165
        ByteArrayOutputStream baos = FileUtil.getCSVByteStream(Arrays.asList("PartnerId", "Partner Name",
166
                "Reconciliation Date", "Purchase Reconciled", "Wallet amount consumed", "Ordered Total",
167
                "Cancelled Total", "Refunded Total", "Scheme In Reconciled", "Scheme In to Wallet",
168
                "Scheme In disbursed", "Scheme In rolledback", "SchemeOut Reconciled", "Scheme Out to Wallet",
169
                "Scheme Out Disbursed", "Scheme Out Rolledback"), rows);
24783 amit.gupta 170
 
33256 amit.gupta 171
        Utils.sendMailWithAttachment(mailSender,
172
                new String[]{"amit.gupta@shop2020.in", "neeraj.gupta@smartdukaan.com"}, new String[]{},
173
                reconciled ? "Reconciled Successfully" : "Reconciliation failed", "Report attached",
174
                String.format("reconciliation-%s.csv", FormattingUtils.formatDate(yesterday.atStartOfDay())),
175
                new ByteArrayResource(baos.toByteArray()));
176
    }
24783 amit.gupta 177
 
33256 amit.gupta 178
    private List<? extends Serializable> reconcileOrdersAndWallet(LocalDate localDate,
179
                                                                  Map<Integer, Integer> transactionsOnThatDate, List<UserWalletHistory> history) throws Exception {
24783 amit.gupta 180
 
33256 amit.gupta 181
        int totalWalletConsumed = 0;
182
        float cancelledAmount = 0;
183
        float returnedAmount = 0;
184
        float totalDeductedAmount = 0;
185
        for (int transactionId : transactionsOnThatDate.keySet()) {
186
            List<Order> orders = orderRepository.selectAllByTransactionId(transactionId);
187
            for (Order o : orders) {
188
                if (o.getCreateTimestamp().toLocalDate().equals(localDate)) {
189
                    if (Arrays.asList(OrderStatus.PAYMENT_PENDING, OrderStatus.PAYMENT_FAILED)
190
                            .contains(o.getStatus())) {
191
                        cancelledAmount += o.getWalletAmount();
192
                    } else if (o.getRefundTimestamp() != null
193
                            && o.getRefundTimestamp().toLocalDate().equals(localDate)) {
194
                        ReturnOrder returnedOrder = returnOrderRepository.selectByOrderId(o.getId());
195
                        if (returnedOrder == null) {
196
                            cancelledAmount += o.getWalletAmount();
197
                        } else {
198
                            returnedAmount += returnedOrder.getTotalPrice();
199
                        }
200
                    }
201
                    totalDeductedAmount += o.getWalletAmount();
24810 amit.gupta 202
 
33256 amit.gupta 203
                } else if (o.getRefundTimestamp() != null && o.getRefundTimestamp().toLocalDate().equals(localDate)) {
204
                    ReturnOrder returnedOrder = returnOrderRepository.selectByOrderId(o.getId());
205
                    if (returnedOrder == null) {
206
                        cancelledAmount += o.getWalletAmount();
207
                    } else {
208
                        returnedAmount += returnedOrder.getTotalPrice();
209
                    }
210
                }
211
            }
212
            totalWalletConsumed -= transactionsOnThatDate.get(transactionId);
24783 amit.gupta 213
 
33256 amit.gupta 214
        }
215
        boolean reconciled = Math
216
                .abs(totalWalletConsumed - (totalDeductedAmount - cancelledAmount - returnedAmount)) < 2;
24783 amit.gupta 217
 
33256 amit.gupta 218
        return Arrays.asList(reconciled, totalWalletConsumed, totalDeductedAmount, cancelledAmount, returnedAmount, "");
24783 amit.gupta 219
 
33256 amit.gupta 220
    }
24783 amit.gupta 221
 
33256 amit.gupta 222
    private List<? extends Serializable> reconcileSchemeInAndWallet(LocalDate localDate,
223
                                                                    Map<Integer, Integer> transactionsOnThatDate, List<UserWalletHistory> history, Set<Integer> schemeIds) throws ProfitMandiBusinessException {
24810 amit.gupta 224
 
33256 amit.gupta 225
        int totalSchemeInWalletCredited = 0;
226
        float schemeInAmountAdded = 0;
227
        float schemeInAmountRolledBack = 0;
228
        for (int transactionId : transactionsOnThatDate.keySet()) {
229
            List<InventoryItem> inventoryItems = inventoryItemRepository.selectByPurchaseId(transactionId).stream()
230
                    .filter(x -> x.getSerialNumber() != null).collect(Collectors.toList());
231
            Set<Integer> inventoryItemIds = inventoryItems.stream().map(x -> x.getId()).collect(Collectors.toSet());
232
            if (inventoryItemIds.size() == 0) {
233
                continue;
234
            }
235
            List<SchemeInOut> sios = schemeInOutRepository.selectByInventoryItemIds(new HashSet<>(inventoryItemIds))
236
                    .stream().filter(x -> schemeIds.contains(x.getSchemeId())).collect(Collectors.toList());
237
            totalSchemeInWalletCredited += transactionsOnThatDate.get(transactionId);
238
            for (SchemeInOut sio : sios) {
239
                if (sio.getCreateTimestamp().toLocalDate().equals(localDate)) {
240
                    schemeInAmountAdded += sio.getAmount();
241
                }
242
                if (sio.getRolledBackTimestamp() != null
243
                        && sio.getRolledBackTimestamp().toLocalDate().equals(localDate)) {
244
                    schemeInAmountRolledBack += sio.getAmount();
245
                }
246
            }
24810 amit.gupta 247
 
33256 amit.gupta 248
        }
249
        boolean reconciled = Math
250
                .abs(totalSchemeInWalletCredited - (schemeInAmountAdded - schemeInAmountRolledBack)) < 5;
24810 amit.gupta 251
 
33256 amit.gupta 252
        return Arrays.asList(reconciled, totalSchemeInWalletCredited, schemeInAmountAdded, schemeInAmountRolledBack,
253
                "");
24783 amit.gupta 254
 
33256 amit.gupta 255
    }
25072 amit.gupta 256
 
33256 amit.gupta 257
    private List<? extends Serializable> reconcileSchemeOutAndWallet(LocalDate localDate,
258
                                                                     Map<Integer, Integer> transactionsOnThatDate, List<UserWalletHistory> history, Set<Integer> schemeIds) throws ProfitMandiBusinessException {
259
        int totalSchemeOutWalletCredited = 0;
260
        float schemeOutAmountAdded = 0;
261
        float schemeOutAmountRolledBack = 0;
262
        for (int fofoOrderId : transactionsOnThatDate.keySet()) {
263
            Set<Integer> fofoOrderItemIds = fofoOrderItemRepository.selectByOrderId(fofoOrderId).stream()
264
                    .map(x -> x.getId()).collect(Collectors.toSet());
265
            List<FofoLineItem> fofoLineItems = fofoLineItemRepository.selectByFofoOrderItemIds(fofoOrderItemIds);
266
            Set<Integer> inventoryItemIds = fofoLineItems.stream().map(x -> x.getInventoryItemId())
267
                    .collect(Collectors.toSet());
268
            List<SchemeInOut> sios = schemeInOutRepository.selectByInventoryItemIds(new HashSet<>(inventoryItemIds))
269
                    .stream().filter(x -> schemeIds.contains(x.getSchemeId())).collect(Collectors.toList());
270
            totalSchemeOutWalletCredited += transactionsOnThatDate.get(fofoOrderId);
271
            for (SchemeInOut sio : sios) {
272
                if (sio.getCreateTimestamp().toLocalDate().equals(localDate)) {
273
                    schemeOutAmountAdded += sio.getAmount();
274
                }
275
                if (sio.getRolledBackTimestamp() != null
276
                        && sio.getRolledBackTimestamp().toLocalDate().equals(localDate)) {
277
                    schemeOutAmountRolledBack += sio.getAmount();
278
                }
279
            }
280
        }
281
        boolean reconciled = Math
282
                .abs(totalSchemeOutWalletCredited - (schemeOutAmountAdded - schemeOutAmountRolledBack)) < 5;
283
        return Arrays.asList(reconciled, totalSchemeOutWalletCredited, schemeOutAmountAdded, schemeOutAmountRolledBack,
284
                "");
285
    }
25072 amit.gupta 286
 
33256 amit.gupta 287
    public void reconcileExpiredFixedSchemes() throws Exception {
288
        List<Scheme> allSchemes = schemeRepository.selectAll();
289
        // .stream().filter(x ->
290
        // x.getAmountType().equals(AmountType.FIXED)).collect(Collectors.toList());
291
        allSchemes = allSchemes.stream()
292
                .filter(x -> x.getEndDateTime().isAfter(LocalDate.of(2019, 3, 31).atStartOfDay()))
293
                .collect(Collectors.toList());
294
        System.out.println(
295
                "InventoryId\tSerialNumber\tItem Id\tScheme Id\tScheme Name\tScheme Type\tAmount Type\tScheme Amount\tAmount Paid\tReference\tTransaction Time\tScheme Start\tScheme End\tScheme Expiry");
296
        for (Scheme scheme : allSchemes) {
297
            if (scheme.getExpireTimestamp() != null) {
298
                if (scheme.getExpireTimestamp().isBefore(scheme.getEndDateTime())) {
299
                    List<SchemeInOut> inOuts = schemeInOutRepository.selectBySchemeIds(Sets.newHashSet(scheme.getId()));
300
                    if (scheme.getType().equals(SchemeType.IN)) {
301
                        for (SchemeInOut schemeInOut : inOuts) {
302
                            ScanRecord inRecord = scanRecordRepository
303
                                    .selectByInventoryItemId(schemeInOut.getInventoryItemId()).stream()
304
                                    .filter(x -> x.getType().equals(ScanType.PURCHASE)).collect(Collectors.toList())
305
                                    .get(0);
306
                            if (inRecord.getCreateTimestamp().isAfter(scheme.getExpireTimestamp())) {
307
                                InventoryItem ii = inventoryItemRepository.selectById(inRecord.getInventoryItemId());
308
                                Purchase purchase = purchaseRepository.selectByIdAndFofoId(ii.getPurchaseId(),
309
                                        ii.getFofoId());
310
                                System.out.println(String.format("%d\t%s\t%d\t%d\t%s\t%s\t%f\t%f\t%d\t%s\t%s\t%s\t%s",
311
                                        ii.getId(), ii.getSerialNumber(), ii.getItemId(), schemeInOut.getSchemeId(),
312
                                        scheme.getName(), scheme.getType(), scheme.getAmount(), schemeInOut.getAmount(),
313
                                        purchase.getId(), inRecord.getCreateTimestamp(), scheme.getStartDateTime(),
314
                                        scheme.getEndDateTime(), scheme.getExpireTimestamp()));
315
                            }
25503 amit.gupta 316
 
33256 amit.gupta 317
                        }
318
                    }
319
                    if (scheme.getType().equals(SchemeType.OUT)) {
320
                        for (SchemeInOut schemeInOut : inOuts) {
321
                            ScanRecord outRecord = scanRecordRepository
322
                                    .selectByInventoryItemId(schemeInOut.getInventoryItemId()).stream()
323
                                    .filter(x -> x.getType().equals(ScanType.SALE))
324
                                    .sorted((x1, x2) -> x1.getId() - x2.getId()).collect(Collectors.toList()).get(0);
325
                            if (outRecord.getCreateTimestamp().isAfter(scheme.getExpireTimestamp())) {
326
                                InventoryItem ii = inventoryItemRepository.selectById(outRecord.getInventoryItemId());
327
                                FofoOrder fofoOrder;
328
                                if (outRecord.getOrderId() == 0) {
329
                                    fofoOrder = orderService.getOrderByInventoryItemId(ii.getId());
330
                                } else {
331
                                    fofoOrder = fofoOrderRepository.selectByOrderId(outRecord.getOrderId());
332
                                    if (fofoOrder == null) {
333
                                        LOGGER.info("Order id does not exits - {}", outRecord.getOrderId());
334
                                        continue;
335
                                    }
336
                                }
337
                                System.out.println(String.format("%d\t%s\t%d\t%d\t%s\t%s\t%f\t%f\t%d\t%s\t%s\t%s\t%s",
338
                                        ii.getId(), ii.getSerialNumber(), ii.getItemId(), schemeInOut.getSchemeId(),
339
                                        scheme.getName(), scheme.getType(), scheme.getAmount(), schemeInOut.getAmount(),
340
                                        fofoOrder.getId(), outRecord.getCreateTimestamp(), scheme.getStartDateTime(),
341
                                        scheme.getEndDateTime(), scheme.getExpireTimestamp()));
25072 amit.gupta 342
 
33256 amit.gupta 343
                            }
25072 amit.gupta 344
 
33256 amit.gupta 345
                        }
346
                    }
347
                }
348
            }
349
        }
25503 amit.gupta 350
 
33256 amit.gupta 351
    }
25503 amit.gupta 352
 
33256 amit.gupta 353
    private void checkForDblClickIssues() {
354
        // For all schmes from April onwards
355
    }
25503 amit.gupta 356
 
33256 amit.gupta 357
    private void dbClickPurchase() {
358
        List<Purchase> purchases = purchaseRepository
359
                .selectAllBetweenPurchaseDate(LocalDate.of(2019, 4, 1).atStartOfDay(), LocalDateTime.now());
360
    }
25503 amit.gupta 361
 
33256 amit.gupta 362
    private void dbClickSale() {
363
        List<FofoOrder> sales = fofoOrderRepository.selectBetweenSaleDate(LocalDate.of(2019, 4, 1).atStartOfDay(),
364
                LocalDateTime.now());
365
 
366
    }
24783 amit.gupta 367
}