Subversion Repositories SmartDukaan

Rev

Rev 24810 | Rev 24828 | Go to most recent revision | View as "text/plain" | Blame | Compare with Previous | Last modification | View Log | RSS feed

package com.smartdukaan.cron.scheduled;

import java.io.Serializable;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.stereotype.Component;

import com.spice.profitmandi.common.util.FileUtil;
import com.spice.profitmandi.common.util.FormattingUtils;
import com.spice.profitmandi.common.util.Utils;
import com.spice.profitmandi.dao.entity.catalog.Scheme;
import com.spice.profitmandi.dao.entity.fofo.FofoLineItem;
import com.spice.profitmandi.dao.entity.fofo.InventoryItem;
import com.spice.profitmandi.dao.entity.fofo.SchemeInOut;
import com.spice.profitmandi.dao.entity.transaction.Order;
import com.spice.profitmandi.dao.entity.transaction.ReturnOrder;
import com.spice.profitmandi.dao.entity.transaction.UserWallet;
import com.spice.profitmandi.dao.entity.transaction.UserWalletHistory;
import com.spice.profitmandi.dao.enumuration.catalog.SchemeType;
import com.spice.profitmandi.dao.repository.catalog.SchemeRepository;
import com.spice.profitmandi.dao.repository.dtr.FofoStoreRepository;
import com.spice.profitmandi.dao.repository.fofo.FofoLineItemRepository;
import com.spice.profitmandi.dao.repository.fofo.FofoOrderItemRepository;
import com.spice.profitmandi.dao.repository.fofo.InventoryItemRepository;
import com.spice.profitmandi.dao.repository.fofo.SchemeInOutRepository;
import com.spice.profitmandi.dao.repository.transaction.OrderRepository;
import com.spice.profitmandi.dao.repository.transaction.ReturnOrderRepository;
import com.spice.profitmandi.dao.repository.transaction.UserWalletHistoryRepository;
import com.spice.profitmandi.dao.repository.transaction.UserWalletRepository;
import com.spice.profitmandi.service.user.RetailerService;

import in.shop2020.model.v1.order.OrderStatus;
import in.shop2020.model.v1.order.WalletReferenceType;

@Component
public class Reconciliation {
        @Autowired
        private FofoStoreRepository fofoStoreRepository;
        @Autowired
        private FofoOrderItemRepository fofoOrderItemRepository;
        @Autowired
        private FofoLineItemRepository fofoLineItemRepository;
        @Autowired
        private UserWalletRepository userWalletRepository;
        @Autowired
        private UserWalletHistoryRepository userWalletHistoryRepository;
        @Autowired
        private SchemeInOutRepository schemeInOutRepository;
        @Autowired
        private SchemeRepository schemeRepository;
        @Autowired
        private InventoryItemRepository inventoryRepository;
        @Autowired
        private RetailerService retailerService;
        @Autowired
        private ReturnOrderRepository returnOrderRepository;
        @Autowired
        private OrderRepository orderRepository;
        @Autowired
        private JavaMailSender mailSender;

        public void dailyReconciliation() throws Exception {
                Map<SchemeType, Set<Integer>> schemeTypeMap = schemeRepository.selectAll().stream()
                                .collect(Collectors.groupingBy(Scheme::getType, Collectors.mapping(Scheme::getId, Collectors.toSet())));
                boolean reconciled = true;
                Map<Integer, String> stores = fofoStoreRepository.getStoresMap();
                List<List<? extends Serializable>> rows = new ArrayList<>();
                LocalDate yesterday = LocalDate.now().minusDays(1);
                Map<Integer, String> retailerNameMap = retailerService
                                .getAllFofoRetailerIdNameMap(new ArrayList<>(stores.keySet()));
                for (int partnerId : stores.keySet()) {
                        UserWallet uw = userWalletRepository.selectByRetailerId(partnerId);
                        List<UserWalletHistory> walletHistory = userWalletHistoryRepository.selectByWalletIdAndDate(uw.getId(),
                                        yesterday);
                        Map<WalletReferenceType, List<UserWalletHistory>> referenceWiseWalletHistory = walletHistory.stream()
                                        .collect(Collectors.groupingBy(x -> x.getReferenceType(), Collectors.toList()));

                        for (Map.Entry<WalletReferenceType, List<UserWalletHistory>> entry : referenceWiseWalletHistory
                                        .entrySet()) {
                                LocalDate dateToReconcile = yesterday;
                                List<UserWalletHistory> history = entry.getValue();
                                List<Serializable> reconciliation = Arrays.asList(partnerId, retailerNameMap.get(partnerId),
                                                dateToReconcile);
                                Map<Integer, Integer> referenceWalletMap = entry.getValue().stream().collect(
                                                Collectors.groupingBy(x -> x.getReference(), Collectors.summingInt(x -> x.getAmount())));
                                switch (entry.getKey()) {
                                case PURCHASE:
                                        reconciliation.addAll(
                                                        reconcileOrdersAndWallet(uw.getUserId(), dateToReconcile, referenceWalletMap, history));
                                        break;
                                case SCHEME_IN:
                                        reconciliation.addAll(reconcileSchemeInAndWallet(uw.getUserId(), dateToReconcile,
                                                        referenceWalletMap, history, schemeTypeMap.get(SchemeType.IN)));
                                        break;
                                case SCHEME_OUT:
                                        reconciliation.addAll(reconcileSchemeOutAndWallet(uw.getUserId(), dateToReconcile,
                                                        referenceWalletMap, history, schemeTypeMap.get(SchemeType.OUT)));
                                        break;
                                default:
                                        break;

                                }
                                reconciled = reconciled || Boolean.TRUE.equals(reconciliation.get(0));
                                rows.add(reconciliation);
                        }
                }
                ByteArrayOutputStream baos = FileUtil.getCSVByteStream(Arrays.asList("PartnerId", "Store Name",
                                "Reconciliation Date", "Purchase Reconciled", "Wallet amount consumed", "Ordered Total",
                                "Cancelled Total", "Refunded Total", "", "Scheme In to Wallet", "Scheme In disbursed",
                                "Scheme In rolledback", "Scheme Out to Wallet", "Scheme Out Disbursed", "Scheme Out Rolledback"), rows);

                Utils.sendMailWithAttachment(mailSender, new String[] { "amit.gupta@shop2020.in" }, new String[] {},
                                reconciled ? "Reconciled Successfully" : "Reconciliation failed", "Report attached",
                                String.format("reconciliation-%s.csv", FormattingUtils.formatDate(yesterday.atStartOfDay())),
                                new ByteArrayResource(baos.toByteArray()));
        }

        private List<? extends Serializable> reconcileOrdersAndWallet(int fofoId, LocalDate localDate,
                        Map<Integer, Integer> transactionsOnThatDate, List<UserWalletHistory> history) throws Exception {

                int totalWalletConsumed = 0;
                float cancelledAmount = 0;
                float returnedAmount = 0;
                float totalDeductedAmount = 0;
                for (int transactionId : transactionsOnThatDate.keySet()) {
                        List<Order> orders = orderRepository.selectAllByTransactionId(transactionId);
                        for (Order o : orders) {
                                if (o.getCreateTimestamp().toLocalDate().equals(localDate)) {
                                        if (Arrays.asList(OrderStatus.PAYMENT_PENDING, OrderStatus.PAYMENT_FAILED)
                                                        .contains(o.getStatus())) {
                                                cancelledAmount += o.getWalletAmount();
                                        } else if (o.getRefundTimestamp() != null
                                                        && o.getRefundTimestamp().toLocalDate().equals(localDate)) {
                                                ReturnOrder returnedOrder = returnOrderRepository.selectByOrderId(o.getId());
                                                if (returnedOrder == null) {
                                                        cancelledAmount += o.getWalletAmount();
                                                } else {
                                                        returnedAmount += returnedOrder.getTotalPrice();
                                                }
                                        }
                                        totalDeductedAmount += o.getWalletAmount();

                                } else if (o.getRefundTimestamp() != null && o.getRefundTimestamp().toLocalDate().equals(localDate)) {
                                        ReturnOrder returnedOrder = returnOrderRepository.selectByOrderId(o.getId());
                                        if (returnedOrder == null) {
                                                cancelledAmount += o.getWalletAmount();
                                        } else {
                                                returnedAmount += returnedOrder.getTotalPrice();
                                        }
                                }
                        }
                        totalWalletConsumed -= transactionsOnThatDate.get(transactionId);

                }
                boolean reconciled = Math
                                .abs(totalWalletConsumed - (totalDeductedAmount - cancelledAmount - returnedAmount)) < 2;

                return Arrays.asList(reconciled, totalWalletConsumed, totalDeductedAmount, cancelledAmount, returnedAmount, "");

        }

        private List<? extends Serializable> reconcileSchemeInAndWallet(int fofoId, LocalDate localDate,
                        Map<Integer, Integer> transactionsOnThatDate, List<UserWalletHistory> history, Set<Integer> schemeIds) {

                int totalSchemeInWalletCredited = 0;
                float schemeInAmountAdded = 0;
                float schemeInAmountRolledBack = 0;
                for (int transactionId : transactionsOnThatDate.keySet()) {
                        List<InventoryItem> inventoryItems = inventoryRepository.selectByPurchaseId(transactionId).stream()
                                        .filter(x -> x.getSerialNumber() != null).collect(Collectors.toList());
                        Set<Integer> inventoryItemIds = inventoryItems.stream().map(x -> x.getId()).collect(Collectors.toSet());
                        List<SchemeInOut> sios = schemeInOutRepository.selectByInventoryItemIds(new HashSet<>(inventoryItemIds))
                                        .stream().filter(x -> schemeIds.contains(x.getSchemeId())).collect(Collectors.toList());
                        totalSchemeInWalletCredited += transactionsOnThatDate.get(transactionId);
                        for (SchemeInOut sio : sios) {
                                if (sio.getCreateTimestamp().toLocalDate().equals(localDate)) {
                                        schemeInAmountAdded += sio.getAmount();
                                }
                                if (sio.getRolledBackTimestamp().toLocalDate().equals(localDate)) {
                                        schemeInAmountRolledBack += sio.getAmount();
                                }
                        }

                }
                boolean reconciled = Math
                                .abs(totalSchemeInWalletCredited - (schemeInAmountAdded - schemeInAmountRolledBack)) < 5;

                return Arrays.asList(reconciled, totalSchemeInWalletCredited, schemeInAmountAdded, schemeInAmountRolledBack,
                                "");

        }

        private List<? extends Serializable> reconcileSchemeOutAndWallet(int fofoId, LocalDate localDate,
                        Map<Integer, Integer> transactionsOnThatDate, List<UserWalletHistory> history, Set<Integer> schemeIds) {
                int totalSchemeOutWalletCredited = 0;
                float schemeOutAmountAdded = 0;
                float schemeOutAmountRolledBack = 0;
                for (int fofoOrderId : transactionsOnThatDate.keySet()) {
                        Set<Integer> fofoOrderItemIds = fofoOrderItemRepository.selectByOrderId(fofoOrderId).stream()
                                        .map(x -> x.getId()).collect(Collectors.toSet());
                        List<FofoLineItem> fofoLineItems = fofoLineItemRepository.selectByFofoOrderItemIds(fofoOrderItemIds);
                        Set<Integer> inventoryItemIds = fofoLineItems.stream().map(x -> x.getInventoryItemId())
                                        .collect(Collectors.toSet());
                        List<SchemeInOut> sios = schemeInOutRepository.selectByInventoryItemIds(new HashSet<>(inventoryItemIds))
                                        .stream().filter(x -> schemeIds.contains(x.getSchemeId())).collect(Collectors.toList());
                        totalSchemeOutWalletCredited += transactionsOnThatDate.get(fofoOrderId);
                        for (SchemeInOut sio : sios) {
                                if (sio.getCreateTimestamp().toLocalDate().equals(localDate)) {
                                        schemeOutAmountAdded += sio.getAmount();
                                }
                                if (sio.getRolledBackTimestamp().toLocalDate().equals(localDate)) {
                                        schemeOutAmountRolledBack += sio.getAmount();
                                }
                        }
                }
                boolean reconciled = Math
                                .abs(totalSchemeOutWalletCredited - (schemeOutAmountAdded - schemeOutAmountRolledBack)) < 5;
                return Arrays.asList(reconciled, totalSchemeOutWalletCredited, schemeOutAmountAdded, schemeOutAmountRolledBack,
                                "");
        }
}