Subversion Repositories SmartDukaan

Rev

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

package com.spice.profitmandi.service.order;

import com.spice.profitmandi.common.enumuration.ItemType;
import com.spice.profitmandi.common.enumuration.SearchType;
import com.spice.profitmandi.common.exception.ProfitMandiBusinessException;
import com.spice.profitmandi.common.model.*;
import com.spice.profitmandi.common.util.FormattingUtils;
import com.spice.profitmandi.common.util.StringUtils;
import com.spice.profitmandi.common.util.Utils;
import com.spice.profitmandi.common.web.client.RestClient;
import com.spice.profitmandi.dao.entity.catalog.Item;
import com.spice.profitmandi.dao.entity.catalog.TagListing;
import com.spice.profitmandi.dao.entity.dtr.*;
import com.spice.profitmandi.dao.entity.fofo.*;
import com.spice.profitmandi.dao.entity.inventory.State;
import com.spice.profitmandi.dao.entity.transaction.Order;
import com.spice.profitmandi.dao.entity.user.Address;
import com.spice.profitmandi.dao.entity.user.Counter;
import com.spice.profitmandi.dao.entity.user.PrivateDealUser;
import com.spice.profitmandi.dao.entity.warehouse.WarehouseInventoryItem;
import com.spice.profitmandi.dao.enumuration.catalog.SchemeType;
import com.spice.profitmandi.dao.enumuration.dtr.PaymentOptionReferenceType;
import com.spice.profitmandi.dao.enumuration.fofo.ReturnType;
import com.spice.profitmandi.dao.enumuration.fofo.ScanType;
import com.spice.profitmandi.dao.enumuration.fofo.SettlementType;
import com.spice.profitmandi.dao.enumuration.inventory.ScratchedGift;
import com.spice.profitmandi.dao.enumuration.transaction.OrderStatus;
import com.spice.profitmandi.dao.repository.catalog.ItemRepository;
import com.spice.profitmandi.dao.repository.catalog.StateGstRateRepository;
import com.spice.profitmandi.dao.repository.catalog.TagListingRepository;
import com.spice.profitmandi.dao.repository.dtr.*;
import com.spice.profitmandi.dao.repository.fofo.*;
import com.spice.profitmandi.dao.repository.inventory.StateRepository;
import com.spice.profitmandi.dao.repository.transaction.OrderRepository;
import com.spice.profitmandi.dao.repository.user.AddressRepository;
import com.spice.profitmandi.dao.repository.user.CounterRepository;
import com.spice.profitmandi.dao.repository.user.PrivateDealUserRepository;
import com.spice.profitmandi.dao.repository.warehouse.WarehouseInventoryItemRepository;
import com.spice.profitmandi.service.integrations.zest.InsuranceService;
import com.spice.profitmandi.service.integrations.zest.MobileInsurancePlan;
import com.spice.profitmandi.service.inventory.InventoryService;
import com.spice.profitmandi.service.inventory.PurchaseReturnService;
import com.spice.profitmandi.service.inventory.SaholicInventoryService;
import com.spice.profitmandi.service.offers.ItemCriteria;
import com.spice.profitmandi.service.pricing.PricingService;
import com.spice.profitmandi.service.scheme.SchemeService;
import com.spice.profitmandi.service.user.RetailerService;
import com.spice.profitmandi.service.wallet.WalletService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.AbstractMap.SimpleEntry;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Component
public class OrderServiceImpl implements OrderService {

    private static final Logger LOGGER = LogManager.getLogger(OrderServiceImpl.class);

    private static Map<String, Integer> serialNumberOrderIdMap = new HashMap<>();

    static {
        serialNumberOrderIdMap.put("862897055749275", 67228);
    }

    @Autowired
    @Qualifier("fofoInventoryItemRepository")
    private InventoryItemRepository inventoryItemRepository;

    @Autowired
    private StateGstRateRepository stateGstRateRepository;

    @Autowired
    private SaholicInventoryService saholicInventoryService;

    @Autowired
    private LiveDemoBillingRespository liveDemoBillingRespository;

    @Autowired
    private InsuranceService insuranceService;

    @Autowired
    private WalletService walletService;

    @Autowired
    @Qualifier("fofoCurrentInventorySnapshotRepository")
    private CurrentInventorySnapshotRepository currentInventorySnapshotRepository;

    @Autowired
    private InvoiceNumberGenerationSequenceRepository invoiceNumberGenerationSequenceRepository;

    @Autowired
    private PurchaseReturnService purchaseReturnService;

    @Autowired
    private RetailerService retailerService;

    @Autowired
    private CustomerRepository customerRepository;

    @Autowired
    private PurchaseReturnItemRepository purchaseReturnItemRepository;

    @Autowired
    private AddressRepository addressRepository;

    @Autowired
    private FofoLineItemRepository fofoLineItemRepository;

    @Autowired
    private WarehouseInventoryItemRepository warehouseInventoryItemRepository;

    @Autowired
    private FofoOrderItemRepository fofoOrderItemRepository;

    @Autowired
    private PaymentOptionRepository paymentOptionRepository;

    @Autowired
    private CustomerReturnItemRepository customerReturnItemRepository;

    @Autowired
    @Qualifier("fofoScanRecordRepository")
    private ScanRecordRepository scanRecordRepository;

    @Autowired
    private FofoOrderRepository fofoOrderRepository;

    @Autowired
    private RetailerRepository retailerRepository;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private UserAccountRepository userAccountRepository;

    @Autowired
    private RetailerRegisteredAddressRepository retailerRegisteredAddressRepository;

    @Autowired
    private CustomerAddressRepository customerAddressRepository;

    @Autowired
    @Qualifier("catalogItemRepository")
    private ItemRepository itemRepository;

    @Autowired
    private InsuranceProviderRepository insuranceProviderRepository;

    @Autowired
    private InsurancePolicyRepository insurancePolicyRepository;

    @Autowired
    private StateRepository stateRepository;

    @Autowired
    private PolicyNumberGenerationSequenceRepository policyNumberGenerationSequenceRepository;

    @Autowired
    private PricingService pricingService;

    @Autowired
    private PrivateDealUserRepository privateDealUserRepository;

    @Autowired
    private TagListingRepository tagListingRepository;

    @Autowired
    private CounterRepository counterRepository;

    @Autowired
    private FofoStoreRepository fofoStoreRepository;

    @Autowired
    private PaymentOptionTransactionRepository paymentOptionTransactionRepository;

    @Autowired
    private SchemeService schemeService;

    private static final List<Integer> orderIdsConsumed = new ArrayList<>();

    @Autowired
    @Qualifier("fofoInventoryService")
    private InventoryService inventoryService;

    @Autowired
    private CustomerCreditNoteRepository customerCreditNoteRepository;

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private HygieneDataRepository hygieneDataRepository;

    @Autowired
    private SessionFactory sessionFactory;

    @Autowired
    private Mongo mongoClient;

    @Autowired
    private PendingOrderRepository pendingOrderRepository;

    @Autowired
    private PendingOrderItemRepository pendingOrderItemRepository;

    @Autowired
    private ScratchOfferRepository scratchOfferRepository;

    @Autowired
    RestClient restClient;

    @Value("${prod}")
    private boolean prodEnv;

    private static final String SMS_GATEWAY = "http://api.pinnacle.in/index.php/sms/send";
    private static final String SENDER = "SMTDKN";

    public static final String APP_DOWNLOAD_BILLING_TEMPLATE_ID = "1507163542403945677";

    public static final String APP_DOWNLOAD_BILLING_OFFER = "Dear Customer, Thank you for purchasing from SmartDukaan pls click %s to download our app to see you invoice and special offers. SmartDukaan";

    @Override
    public int createOrder(CreateOrderRequest createOrderRequest, int fofoId, boolean accessoriesDeals) throws ProfitMandiBusinessException {
        LOGGER.info("fofoId -- {} Order Request -- {}", fofoId, createOrderRequest);
        CustomCustomer customCustomer = createOrderRequest.getCustomer();
        Customer customer = customerRepository.selectById(customCustomer.getCustomerId());

        if (!StringUtils.isValidGstNumber(customCustomer.getGstNumber())) {
            LOGGER.error("invalid customer gstNumber {} ", customCustomer.getGstNumber());
            throw new ProfitMandiBusinessException(ProfitMandiConstants.CUSTOMER_GST_NUMBER, customCustomer.getGstNumber(), "VE_1072");
        }

        Map<Integer, Integer> itemIdQuantity = new HashMap<>(); // this is for error
        Map<Integer, CustomFofoOrderItem> itemIdCustomFofoOrderItemMap = new HashMap<>();
        Map<Integer, Float> lineItemPrice = new HashMap<>(); // this is for pricing error

        float totalAmount = 0;
        boolean noGST = false;
        for (CustomFofoOrderItem customFofoOrderItem : createOrderRequest.getFofoOrderItems()) {
            // itemIds.add(customFofoOrderItem.getItemId());
            Set<String> serialNumbers = this.serialNumberDetailsToSerialNumbers(customFofoOrderItem.getSerialNumberDetails());
            if (!serialNumbers.isEmpty() && customFofoOrderItem.getQuantity() != serialNumbers.size()) {
                itemIdQuantity.put(customFofoOrderItem.getItemId(), customFofoOrderItem.getQuantity());
            }
            if (!(customFofoOrderItem.getSellingPrice() > 0)) {
                lineItemPrice.put(customFofoOrderItem.getItemId(), customFofoOrderItem.getSellingPrice());
            } else {
                totalAmount = totalAmount + customFofoOrderItem.getSellingPrice() * customFofoOrderItem.getQuantity();
                for (SerialNumberDetail serialNumberDetail : customFofoOrderItem.getSerialNumberDetails()) {
                    if (serialNumberDetail.getAmount() > 0) {
                        totalAmount = totalAmount + serialNumberDetail.getAmount();
                    }
                }
            }

            itemIdCustomFofoOrderItemMap.put(customFofoOrderItem.getItemId(), customFofoOrderItem);
        }
        if (!itemIdQuantity.isEmpty()) {
            // if item quantity does not match with given serialnumbers size
            LOGGER.error("itemId's quantity should be equal to given serialnumber size {} ", itemIdQuantity);
            throw new ProfitMandiBusinessException("itemIdQuantity", itemIdQuantity, "FFORDR_1001");
            // return "error";
        }

        this.validatePaymentOptionsAndTotalAmount(createOrderRequest.getPaymentOptions(), totalAmount);

        if (!lineItemPrice.isEmpty()) {
            // given fofo line item price must be greater than zero
            LOGGER.error("requested itemId's selling price must greater than 0");
            throw new ProfitMandiBusinessException(ProfitMandiConstants.PRICE, lineItemPrice, "FFORDR_1002");
        }

        List<CurrentInventorySnapshot> currentInventorySnapshots = currentInventorySnapshotRepository.selectByFofoItemIds(fofoId, itemIdCustomFofoOrderItemMap.keySet());

        this.validateCurrentInventorySnapshotQuantities(currentInventorySnapshots, itemIdCustomFofoOrderItemMap);

        List<Item> items = itemRepository.selectByIds(itemIdCustomFofoOrderItemMap.keySet());
        if (items.size() != itemIdCustomFofoOrderItemMap.keySet().size()) {
            LOGGER.error("Requested ItemIds not found in catalog");
            // invalid itemIds
            throw new ProfitMandiBusinessException("invalidItemIds", itemIdCustomFofoOrderItemMap.keySet(), "FFORDR_1003");
        }

        Map<Integer, Item> itemMap = this.toItemMap(items);

        Set<Integer> nonSerializedItemIds = new HashSet<>();
        Set<String> serialNumbers = new HashSet<>();
        List<InsuranceModel> insuredModels = new ArrayList<>();
        for (CustomFofoOrderItem customFofoOrderItem : createOrderRequest.getFofoOrderItems()) {
            Item item = itemMap.get(customFofoOrderItem.getItemId());
            noGST = item.getHsnCode().equals("NOGST");
            if (item.getType().equals(ItemType.SERIALIZED)) {
                for (SerialNumberDetail serialNumberDetail : customFofoOrderItem.getSerialNumberDetails()) {
                    serialNumbers.add(serialNumberDetail.getSerialNumber());
                    if (serialNumberDetail.getAmount() > 0) {
                        if (customer.getEmailId() == null || customer.getEmailId().equals("")) {
                            throw new ProfitMandiBusinessException("Email Id is required for insurance", "Email Id is required for insurance", "Email Id is required for insurance");
                        }
                        InsuranceModel im = new InsuranceModel();
                        im.setBrand(item.getBrand());
                        im.setColor(item.getColor());
                        im.setModelName(item.getModelName() + item.getModelNumber());
                        im.setInsuranceAmount(serialNumberDetail.getAmount());
                        im.setDeviceSellingPrice(customFofoOrderItem.getSellingPrice());
                        im.setInsuranceId(serialNumberDetail.getInsurance());
                        im.setSerialNumber(serialNumberDetail.getSerialNumber());
                        im.setMemory(serialNumberDetail.getMemory());
                        im.setRam(serialNumberDetail.getRam());
                        im.setMfgDate(serialNumberDetail.getMfgDate());
                        insuredModels.add(im);
                        // Check for free insurance code
                        try {
                            Map<String, List<MobileInsurancePlan>> mobileInsurancePlanMap = insuranceService.getAllPlans(item.getId(), im.getDeviceSellingPrice());
                            MobileInsurancePlan mobileInsurancePlan = mobileInsurancePlanMap.entrySet().stream().flatMap(x -> x.getValue().stream()).filter(x -> x.getProductId().equals(serialNumberDetail.getInsurance())).findFirst().get();
                            LOGGER.info("OneAssist Plan - {}", mobileInsurancePlanMap);
                            LOGGER.info("SerialNumber Detqail InsuranceId - {}", serialNumberDetail.getInsurance());
                            LOGGER.info("product description - {}", mobileInsurancePlan);
                            if (mobileInsurancePlan.getPlanName().equals("OneAssist Damage Protection Plan")) {
                                MobileInsurancePlan freePlan = mobileInsurancePlanMap.get("Prolong Extendended Warranty(SmartDukaan Special Price)").get(0);
                                InsuranceModel imFree = new InsuranceModel();
                                imFree.setBrand(item.getBrand());
                                imFree.setColor(item.getColor());
                                imFree.setModelName(item.getModelName() + item.getModelNumber());
                                imFree.setInsuranceAmount(0);
                                imFree.setDeviceSellingPrice(customFofoOrderItem.getSellingPrice());
                                imFree.setInsuranceId(freePlan.getProductId());
                                imFree.setSerialNumber(serialNumberDetail.getSerialNumber());
                                imFree.setMemory(serialNumberDetail.getMemory());
                                imFree.setRam(serialNumberDetail.getRam());
                                imFree.setMfgDate(serialNumberDetail.getMfgDate());
                                insuredModels.add(imFree);
                            }
                        } catch (Exception e) {
                            LOGGER.error("Exception - {}", e);
                            throw new ProfitMandiBusinessException("problem fetching plans", "problem fetching plans", "problem fetching plans");
                        }
                    }

                }
            } else {
                nonSerializedItemIds.add(customFofoOrderItem.getItemId());
            }
        }

        Map<Integer, Set<InventoryItem>> serializedInventoryItemMap = new HashMap<>();
        Map<Integer, Set<InventoryItem>> nonSerializedInventoryItemMap = new HashMap<>();
        // Map<String, Float> serialNumberItemPrice = new HashMap<>();

        if (!serialNumbers.isEmpty()) {
            List<InventoryItem> serializedInventoryItems = inventoryItemRepository.selectByFofoIdSerialNumbers(fofoId, serialNumbers, false);
            LOGGER.info("serializedInventoryItems {}", serializedInventoryItems);
            for (InventoryItem inventoryItem : serializedInventoryItems) {
                if (inventoryItem.getGoodQuantity() == 1) {
                    if (serializedInventoryItemMap.containsKey(inventoryItem.getItemId())) {
                        serializedInventoryItemMap.get(inventoryItem.getItemId()).add(inventoryItem);
                    } else {
                        Set<InventoryItem> itemIdInventoryItems = new HashSet<>();
                        itemIdInventoryItems.add(inventoryItem);
                        serializedInventoryItemMap.put(inventoryItem.getItemId(), itemIdInventoryItems);
                    }
                }
            }
        }

        if (!nonSerializedItemIds.isEmpty()) {
            List<InventoryItem> nonSerializedInventoryItems = inventoryItemRepository.selectByFofoIdItemIds(fofoId, nonSerializedItemIds);
            LOGGER.info("nonSerializedInventoryItems {}", nonSerializedInventoryItems);
            for (InventoryItem it : nonSerializedInventoryItems) {
                if (it.getGoodQuantity() > 0) {
                    if (nonSerializedInventoryItemMap.containsKey(it.getItemId())) {
                        nonSerializedInventoryItemMap.get(it.getItemId()).add(it);
                    } else {
                        Set<InventoryItem> tmp = new HashSet<>();
                        tmp.add(it);
                        nonSerializedInventoryItemMap.put(it.getItemId(), tmp);
                    }
                }
            }
        }

        this.validateItemsSerializedNonSerialized(items, itemIdCustomFofoOrderItemMap);

        Map<Integer, Set<InventoryItem>> inventoryItemsToBill = new HashMap<>();
        Map<Integer, Integer> inventoryItemIdQuantityUsed = new HashMap<>(); // to keep track of inventoryitem quanity
        // used for scan records insertion

        LOGGER.info("itemMap keys {}", itemMap.keySet());
        // Lets reduce quantity and decide what inventory items to use.
        for (Item item : items) {
            if (item.getType().equals(ItemType.SERIALIZED)) {
                // TODO:handle null
                if (serializedInventoryItemMap.get(item.getId()) == null || itemIdCustomFofoOrderItemMap.get(item.getId()).getSerialNumberDetails().size() != serializedInventoryItemMap.get(item.getId()).size()) {

                    List<String> invalidSerialNumbers = itemIdCustomFofoOrderItemMap.get(item.getId()).getSerialNumberDetails().stream().map(x -> x.getSerialNumber()).collect(Collectors.toList());
                    throw new ProfitMandiBusinessException("invalidSerialNumbers", invalidSerialNumbers, "FFORDR_1004");
                }
                List<String> serialNumberList = liveDemoBillingRespository.selectAllSerialNumber();

                Set<InventoryItem> inventoryItemsSerializedserialized = serializedInventoryItemMap.get(item.getId());
                for (InventoryItem inventoryItem : inventoryItemsSerializedserialized) {
                    inventoryItem.setGoodQuantity(0);
                    inventoryItemIdQuantityUsed.put(inventoryItem.getId(), 1);
                    if (serialNumberList.contains(inventoryItem.getSerialNumber())) {
                        LiveDemoSerialNumber liveDemoSerialNumber = liveDemoBillingRespository.selectBySerialNumber(inventoryItem.getSerialNumber());
                        liveDemoBillingRespository.delete(liveDemoSerialNumber);
                    }
                }
                inventoryItemsToBill.put(item.getId(), inventoryItemsSerializedserialized);
            } else {
                Set<InventoryItem> inventoryItemsNonSerialized = nonSerializedInventoryItemMap.get(item.getId());
                int quantityToBill = itemIdCustomFofoOrderItemMap.get(item.getId()).getQuantity();
                int totalLeft = quantityToBill;
                Set<InventoryItem> inventoryItemsNonSerializedUsed = new HashSet<>();
                if (inventoryItemsNonSerialized != null) {
                    for (InventoryItem inventoryItem : inventoryItemsNonSerialized) {
                        if (totalLeft > 0) {
                            int toUse = Math.min(totalLeft, inventoryItem.getGoodQuantity());
                            inventoryItemIdQuantityUsed.put(inventoryItem.getId(), toUse);
                            inventoryItem.setGoodQuantity(inventoryItem.getGoodQuantity() - toUse);
                            totalLeft = totalLeft - toUse;
                            inventoryItemsNonSerializedUsed.add(inventoryItem);
                        }
                    }
                }

                if (totalLeft > 0) {
                    // not enough quanity for non-serialized
                    LOGGER.error("not enough quanity for non-serialized");
                    throw new ProfitMandiBusinessException("notEnoughQuantityForNonSerialized", totalLeft, "FFORDR_1005");
                }
                inventoryItemsToBill.put(item.getId(), inventoryItemsNonSerializedUsed);
            }
        }

        Map<Integer, PriceModel> itemIdMopPriceMap = pricingService.getPurchasePriceMopPriceNotFound(itemIdCustomFofoOrderItemMap.keySet(), fofoId);
        LOGGER.info("itemIdMopMap {}", itemIdMopPriceMap);
        if (accessoriesDeals) {
            this.validateDpPrice(itemIdMopPriceMap, itemIdCustomFofoOrderItemMap);
        } else {
            this.validateMopPrice(itemIdMopPriceMap, itemIdCustomFofoOrderItemMap);
        }

        String fofoStoreCode = this.getFofoStoreCode(fofoId);
        String documentNumber = null;
        if (noGST) {
            documentNumber = this.getSecurityDepositNumber(fofoId, fofoStoreCode);

        } else {
            documentNumber = this.getInvoiceNumber(fofoId, fofoStoreCode);
        }

        CustomerAddress customerAddress = customer.getCustomerAddress().stream().filter(x -> x.getId() == customCustomer.getCustomerAddressId()).findFirst().get();

        FofoOrder fofoOrder = this.createAndGetFofoOrder(customer.getId(), customCustomer.getGstNumber(), fofoId, documentNumber, totalAmount, customerAddress.getId());

        this.createPaymentOptions(fofoOrder, createOrderRequest.getPaymentOptions());

        int retailerAddressId = retailerRegisteredAddressRepository.selectAddressIdByRetailerId(fofoId);

        Address retailerAddress = addressRepository.selectById(retailerAddressId);

        Integer stateId = null;
        if (customerAddress.getState().equals(retailerAddress.getState())) {
            try {
                State state = stateRepository.selectByName(customerAddress.getState());
                stateId = Long.valueOf(state.getId()).intValue();
            } catch (Exception e) {
                LOGGER.error("Unable to get state rates");
            }
        }

        for (CustomFofoOrderItem customFofoOrderItem : createOrderRequest.getFofoOrderItems()) {
            FofoOrderItem fofoOrderItem = this.createAndGetFofoOrderItem(customFofoOrderItem, fofoOrder.getId(), itemMap, inventoryItemsToBill.get(customFofoOrderItem.getItemId()), stateId);

            Set<InventoryItem> inventoryItems = inventoryItemsToBill.get(customFofoOrderItem.getItemId());

            this.createFofoLineItem(fofoOrderItem.getId(), inventoryItems, inventoryItemIdQuantityUsed);

            this.updateCurrentInventorySnapshot(currentInventorySnapshots, fofoId, customFofoOrderItem.getItemId(), customFofoOrderItem.getQuantity());

            this.updateInventoryItemsAndScanRecord(inventoryItems, fofoId, inventoryItemIdQuantityUsed, fofoOrder.getId());
        }

        List<FofoOrderItem> fofoItems = fofoOrderItemRepository.selectByOrderId(fofoOrder.getId());

        boolean smartPhone = false;
        for (FofoOrderItem fofoItem : fofoItems) {
            Item orderItem = itemRepository.selectById(fofoItem.getItemId());
            if (orderItem.isSmartPhone()) {
                smartPhone = true;
            }

        }

        if (smartPhone) {
            this.createAndGetHygieneData(fofoOrder.getId(), fofoOrder.getFofoId());
        }
        // insurance calculation is insurance flag is enabled
        //
        if (insuredModels.size() > 0) {
            LOGGER.info("Processing insurane for serialNumbers");
            LOGGER.info("InsuranceModels {}", insuredModels);
            LocalDate customerDateOfBirth = LocalDate.from(createOrderRequest.getCustomer().getDateOfBirth());
            fofoOrder.setDateOfBirth(customerDateOfBirth);
            for (InsuranceModel insuranceModel : insuredModels) {
                LOGGER.info("Creating insurance for {}", insuranceModel.getInsuranceId());
                insuranceService.createInsurance(fofoOrder, insuranceModel);
            }
        }

        schemeService.processSchemeOut(fofoOrder.getId(), fofoId);

        if (createOrderRequest.getPoId() != 0) {
            PendingOrder po = pendingOrderRepository.selectById(createOrderRequest.getPoId());
            po.setBilledAmount(po.getBilledAmount() + totalAmount);
            PendingOrderItem poi = pendingOrderItemRepository.selectById(createOrderRequest.getPoItemId());
            poi.setStatus(OrderStatus.BILLED);
            poi.setBilledTimestamp(LocalDateTime.now());
        }

        LocalDate startDate = LocalDate.of(2023, 01, 13);
        LocalDate endDate = LocalDate.of(2023, 01, 27);

        if (smartPhone) {
            if (LocalDateTime.now().isAfter(startDate.atStartOfDay()) && LocalDateTime.now().isBefore(endDate.atStartOfDay())) {

                try {
                    this.sendAppDownloadBillingOffer(customer.getMobileNumber());
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                this.createScratchOffer(fofoOrder.getInvoiceNumber(), fofoOrder.getCustomerId());
            }
        }
        return fofoOrder.getId();
    }

    public void sendAppDownloadBillingOffer(String mobileNumber) throws Exception {
        // In case of Cant receive SMS?
        // String mailMessage = java.text.MessageFormat.format(text, otp.getOtp());
        String sdurl = "http://surl.li/anhfn";
        try {
            if (prodEnv) {
                this.sendSms(APP_DOWNLOAD_BILLING_TEMPLATE_ID, String.format(APP_DOWNLOAD_BILLING_OFFER, sdurl), mobileNumber);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void sendSms(String dltTemplateId, String message, String mobileNumber) throws Exception {
        Map<String, String> map = new HashMap<>();

        map.put("sender", SENDER);
        map.put("messagetype", "TXT");
        map.put("apikey", "b866f7-c6c483-682ff5-054420-ad9e2c");

        map.put("numbers", "91" + mobileNumber);
        LOGGER.info("Message {}", message);
        // OTP Message Template
        map.put("message", message);
        map.put("dlttempid", dltTemplateId);

        String response = restClient.post(SMS_GATEWAY, map, new HashMap<>());
        LOGGER.info(response);

    }

    private void createScratchOffer(String invoiceNumber, int customerId) {

        /*
         * ScratchOffer so = new ScratchOffer(); so.setInvoiceNumber(invoiceNumber);
         * so.setOfferName(ScratchedGift.EW); so.setScratched(false);
         * so.setCreatedTimestamp(LocalDateTime.now());
         * so.setExpiredTimestamp(so.getCreatedTimestamp().plusDays(1));
         * so.setUnlockedAt(LocalDateTime.now()); so.setCustomerId(customerId);
         * scratchOfferRepository.persist(so);
         */
        LocalDateTime endDate = LocalDateTime.of(LocalDate.now().getYear(), LocalDate.now().getMonth(), 27, 21, 00);

        ScratchOffer so2 = new ScratchOffer();
        so2.setInvoiceNumber(invoiceNumber);
        so2.setScratched(false);
        so2.setCreatedTimestamp(LocalDateTime.now());
        so2.setExpiredTimestamp(endDate.plusDays(1).toLocalDate().atTime(LocalTime.MAX));
        so2.setOfferName(ScratchedGift.BLNT);
        so2.setCustomerId(customerId);

        LocalDate date = LocalDate.now();
        LocalDateTime seven = LocalDateTime.of(date.getYear(), date.getMonth(), date.getDayOfMonth(), 19, 0);
        LocalDateTime nine = LocalDateTime.of(date.getYear(), date.getMonth(), date.getDayOfMonth(), 21, 0);

        if (LocalDateTime.now().isAfter(seven)) {
            so2.setUnlockedAt(nine.plusDays(1));
        } else {
            so2.setUnlockedAt(nine);
        }

        scratchOfferRepository.persist(so2);

    }

    private HygieneData createAndGetHygieneData(int id, int fofoId) {
        HygieneData hygieneData = new HygieneData();
        hygieneData.setOrderId(id);
        hygieneData.setFofoId(fofoId);
        hygieneData.setCreatedTimestamp(LocalDateTime.now());
        hygieneDataRepository.persist(hygieneData);

        return hygieneData;
    }

    @Override
    public String getInvoiceNumber(int fofoId, String fofoStoreCode) {
        InvoiceNumberGenerationSequence invoiceNumberGenerationSequence = null;
        try {
            invoiceNumberGenerationSequence = invoiceNumberGenerationSequenceRepository.selectByFofoId(fofoId);
            invoiceNumberGenerationSequence.setSequence(invoiceNumberGenerationSequence.getSequence() + 1);
        } catch (ProfitMandiBusinessException profitMandiBusinessException) {
            invoiceNumberGenerationSequence = new InvoiceNumberGenerationSequence();
            invoiceNumberGenerationSequence.setFofoId(fofoId);
            invoiceNumberGenerationSequence.setPrefix(fofoStoreCode);
            invoiceNumberGenerationSequence.setSequence(1);
        }
        invoiceNumberGenerationSequenceRepository.persist(invoiceNumberGenerationSequence);
        return invoiceNumberGenerationSequence.getPrefix() + "/" + invoiceNumberGenerationSequence.getSequence();
    }

    private String getSecurityDepositNumber(int fofoId, String fofoStoreCode) {
        InvoiceNumberGenerationSequence invoiceNumberGenerationSequence = null;
        try {
            invoiceNumberGenerationSequence = invoiceNumberGenerationSequenceRepository.selectByFofoId(fofoId);
            invoiceNumberGenerationSequence.setChallanNumberSequence(invoiceNumberGenerationSequence.getChallanNumberSequence() + 1);
        } catch (ProfitMandiBusinessException profitMandiBusinessException) {
            invoiceNumberGenerationSequence = new InvoiceNumberGenerationSequence();
            invoiceNumberGenerationSequence.setFofoId(fofoId);
            invoiceNumberGenerationSequence.setPrefix(fofoStoreCode);
            invoiceNumberGenerationSequence.setChallanNumberSequence(1);
        }
        invoiceNumberGenerationSequenceRepository.persist(invoiceNumberGenerationSequence);
        return invoiceNumberGenerationSequence.getPrefix() + "/SEC" + invoiceNumberGenerationSequence.getChallanNumberSequence();
    }

    private Set<String> serialNumberDetailsToSerialNumbers(Set<SerialNumberDetail> serialNumberDetails) {
        Set<String> serialNumbers = new HashSet<>();
        for (SerialNumberDetail serialNumberDetail : serialNumberDetails) {
            if (serialNumberDetail.getSerialNumber() != null && !serialNumberDetail.getSerialNumber().isEmpty()) {
                serialNumbers.add(serialNumberDetail.getSerialNumber());
            }
        }
        return serialNumbers;
    }

    @Override
    public InvoicePdfModel getInvoicePdfModel(int orderId) throws ProfitMandiBusinessException {
        FofoOrder fofoOrder = fofoOrderRepository.selectByOrderId(orderId);
        return this.getInvoicePdfModel(fofoOrder);
    }

    @Override
    @Cacheable(value = "order.dummymodel", cacheManager = "oneDayCacheManager")
    public InvoicePdfModel getDummyPdfModel(String serialNumber) throws ProfitMandiBusinessException {
        List<WarehouseInventoryItem> warehouseInventoryItems = warehouseInventoryItemRepository.selectWarehouseInventoryItemBySerailNumbers(Arrays.asList(serialNumber));
        if (warehouseInventoryItems.size() > 0) {
            WarehouseInventoryItem warehouseInventoryItem = warehouseInventoryItems.get(0);
            int currentQuantity = warehouseInventoryItems.get(0).getCurrentQuantity();
            if (currentQuantity > 0) {
                throw new ProfitMandiBusinessException("Serial Number", serialNumber, "Serial Number exist in our warehouse");
            } else {
                try {
                    InventoryItem inventoryItem = inventoryItemRepository.selectBySerialNumber(serialNumber);
                    if (inventoryItem.getGoodQuantity() > 0) {
                        throw new ProfitMandiBusinessException("Serial Number", serialNumber, "Serial Number is not yet billed by the partner");
                    } else {
                        List<ScanRecord> scanRecords = scanRecordRepository.selectByInventoryItemId(inventoryItem.getId());
                        Optional<ScanRecord> scanRecord = scanRecords.stream().filter(x -> x.getOrderId() != 0).findFirst();
                        if (scanRecord.isPresent()) {
                            FofoOrder fofoOrder = fofoOrderRepository.selectByOrderId(scanRecord.get().getOrderId());
                            orderIdsConsumed.add(fofoOrder.getId());
                            return this.getInvoicePdfModel(fofoOrder);
                        } else {
                            throw new ProfitMandiBusinessException("Serial Number", serialNumber, "Serial Number returned by partner, but in transit");
                        }
                    }
                } catch (Exception e) {
                    int itemId = warehouseInventoryItem.getItemId();
                    if (serialNumberOrderIdMap.containsKey(serialNumber)) {
                        FofoOrder fofoOrder = fofoOrderRepository.selectByOrderId(serialNumberOrderIdMap.get(serialNumber));
                        InvoicePdfModel pdfModel = this.getInvoicePdfModel(fofoOrder.getId());
                        this.modifyDummyModel(fofoOrder, pdfModel, itemId, serialNumber);
                        return pdfModel;
                    }
                    // Map this serialNumber for dummy billing
                    LocalDateTime grnDate = warehouseInventoryItem.getCreated();
                    Random random = new Random();
                    int randomDays = random.ints(2, 15).findFirst().getAsInt();
                    LocalDateTime saleDate = grnDate.plusDays(randomDays);
                    if (saleDate.isAfter(LocalDate.now().atStartOfDay())) {
                        saleDate = LocalDateTime.now().minusDays(2);
                    }
                    Random offsetRandom = new Random();
                    int offset = offsetRandom.ints(2, 100).findFirst().getAsInt();
                    FofoOrder fofoOrder = fofoOrderRepository.selectFirstOrderAfterDate(saleDate, offset);
                    while (orderIdsConsumed.contains(fofoOrder.getId())) {
                        Random offsetRandom2 = new Random();
                        int offset2 = offsetRandom2.ints(2, 100).findFirst().getAsInt();
                        FofoOrder fofoOrder2 = fofoOrderRepository.selectFirstOrderAfterDate(saleDate, offset2);
                        if (fofoOrder2 != null) {
                            fofoOrder = fofoOrder2;
                        }
                    }
                    InvoicePdfModel pdfModel = this.getInvoicePdfModel(fofoOrder.getId());
                    orderIdsConsumed.add(fofoOrder.getId());
                    this.modifyDummyModel(fofoOrder, pdfModel, itemId, serialNumber);
                    return pdfModel;

                }
            }
        } else {
            throw new ProfitMandiBusinessException("Serial Number", serialNumber, "Serial Number does not exist in our warehouse");
        }
    }

    void modifyDummyModel(FofoOrder fofoOrder, InvoicePdfModel pdfModel, int itemId, String serialNumber) throws ProfitMandiBusinessException {

        int retailerAddressId = retailerRegisteredAddressRepository.selectAddressIdByRetailerId(fofoOrder.getFofoId());

        Address retailerAddress = addressRepository.selectById(retailerAddressId);
        Customer customer = customerRepository.selectById(fofoOrder.getCustomerId());

        CustomerAddress customerAddress = customer.getCustomerAddress().stream().filter(x -> x.getId() == fofoOrder.getCustomerAddressId()).findFirst().get();

        Integer stateId = null;
        if (customerAddress.getState().equals(retailerAddress.getState())) {
            try {
                // stateId =
                // Long.valueOf(Utils.getStateInfo(customerAddress.getState()).getId()).intValue();

                stateId = Long.valueOf(stateRepository.selectByName(customerAddress.getState()).getId()).intValue();
            } catch (Exception e) {
                LOGGER.error("Unable to get state rates");
            }
        }
        CustomOrderItem cli = pdfModel.getOrderItems().stream().findFirst().get();
        List<FofoOrderItem> fofoOrderItems = Arrays.asList(this.getDummyFofoOrderItem(itemId, fofoOrder.getId(), serialNumber, stateId));
        pdfModel.setPaymentOptions(pdfModel.getPaymentOptions().stream().limit(1).collect(Collectors.toList()));
        CustomPaymentOption paymentOption = pdfModel.getPaymentOptions().get(0);
        paymentOption.setAmount(fofoOrderItems.get(0).getMop());
        Set<CustomOrderItem> customerFofoOrderItems = new HashSet<>();
        for (FofoOrderItem fofoOrderItem : fofoOrderItems) {
            CustomOrderItem customFofoOrderItem = new CustomOrderItem();
            float totalTaxRate = fofoOrderItem.getIgstRate() + fofoOrderItem.getSgstRate() + fofoOrderItem.getCgstRate();
            float taxableSellingPrice = fofoOrderItem.getSellingPrice() / (1 + totalTaxRate / 100);
            float taxableDiscountPrice = fofoOrderItem.getDiscount() / (1 + totalTaxRate / 100);

            customFofoOrderItem.setAmount(fofoOrderItem.getQuantity() * (taxableSellingPrice - taxableDiscountPrice));
            customFofoOrderItem.setDescription(fofoOrderItem.getBrand() + " " + fofoOrderItem.getModelName() + " " + fofoOrderItem.getModelNumber() + "-" + fofoOrderItem.getColor());
            Set<String> serialNumbers = this.toSerialNumbers(fofoOrderItem.getFofoLineItems());
            // LOGGER.info("serialNumbers {}", serialNumbers);
            // LOGGER.info("serialNumbers is empty {}", serialNumbers.isEmpty());
            if (!serialNumbers.isEmpty()) {
                customFofoOrderItem.setDescription(
                        customFofoOrderItem.getDescription() + "\n IMEIS - " + String.join(", ", serialNumbers));
            }
            customFofoOrderItem.setRate(taxableSellingPrice);
            customFofoOrderItem.setDiscount(taxableDiscountPrice);
            customFofoOrderItem.setQuantity(fofoOrderItem.getQuantity());
            customFofoOrderItem.setNetAmount(
                    (fofoOrderItem.getSellingPrice() - fofoOrderItem.getDiscount()) * fofoOrderItem.getQuantity());
            float igstAmount = (customFofoOrderItem.getAmount() * fofoOrderItem.getIgstRate()) / 100;
            float cgstAmount = (customFofoOrderItem.getAmount() * fofoOrderItem.getCgstRate()) / 100;
            float sgstAmount = (customFofoOrderItem.getAmount() * fofoOrderItem.getSgstRate()) / 100;
            customFofoOrderItem.setIgstRate(fofoOrderItem.getIgstRate());
            customFofoOrderItem.setIgstAmount(igstAmount);
            customFofoOrderItem.setCgstRate(fofoOrderItem.getCgstRate());
            customFofoOrderItem.setCgstAmount(cgstAmount);
            customFofoOrderItem.setSgstRate(fofoOrderItem.getSgstRate());
            customFofoOrderItem.setSgstAmount(sgstAmount);
            customFofoOrderItem.setHsnCode(fofoOrderItem.getHsnCode());
            customerFofoOrderItems.add(customFofoOrderItem);
        }
        pdfModel.setTotalAmount(paymentOption.getAmount());
        pdfModel.setOrderItems(customerFofoOrderItems);

    }

    private InvoicePdfModel getInvoicePdfModel(FofoOrder fofoOrder) throws ProfitMandiBusinessException {

        List<PaymentOptionTransaction> paymentOptionTransactions = paymentOptionTransactionRepository.selectByReferenceIdAndTypes(fofoOrder.getId(), Arrays.asList(PaymentOptionReferenceType.ORDER, PaymentOptionReferenceType.INSURANCE));

        List<CustomPaymentOption> paymentOptions = new ArrayList<>();

        InvoicePdfModel pdfModel = new InvoicePdfModel();
        for (PaymentOptionTransaction paymentOptionTransaction : paymentOptionTransactions) {
            CustomPaymentOption cpi = new CustomPaymentOption();
            cpi.setAmount(paymentOptionTransaction.getAmount());
            cpi.setPaymentOption(
                    paymentOptionRepository.selectById(paymentOptionTransaction.getPaymentOptionId()).getName());
            paymentOptions.add(cpi);
        }
        List<FofoOrderItem> fofoOrderItems = this.getByOrderId(fofoOrder.getId());

        pdfModel.setTitle("Retailer Invoice");
        Optional<FofoOrderItem> fofoOrderItemOptional = fofoOrderItems.stream().findAny();
        if (fofoOrderItemOptional.isPresent() && fofoOrderItemOptional.get().equals("NOGST")) {
            pdfModel.setTitle("Security Deposit Receipt");
        }
        pdfModel.setPaymentOptions(paymentOptions);
        pdfModel.setAuther("SmartDukaan");
        pdfModel.setInvoiceDate(FormattingUtils.formatDate(fofoOrder.getCreateTimestamp()));

        // insurance calculation
        List<InsurancePolicy> insurancePolicies = insurancePolicyRepository.selectByRetailerIdInvoiceNumber(fofoOrder.getInvoiceNumber());
        Set<CustomInsurancePolicy> customInsurancePolicies = new HashSet<>();
        final float totalInsuranceTaxRate = 18;
        for (InsurancePolicy insurancePolicy : insurancePolicies) {
            float taxableInsurancePrice = insurancePolicy.getSaleAmount() / (1 + totalInsuranceTaxRate / 100);
            CustomInsurancePolicy customInsurancePolicy = new CustomInsurancePolicy();
            customInsurancePolicy.setDescription(insurancePolicy.getPolicyPlan() + " for Device #" + insurancePolicy.getSerialNumber() + "\n Plan Reference - " + insurancePolicy.getPolicyNumber());
            customInsurancePolicy.setHsnCode("998716");
            customInsurancePolicy.setRate(taxableInsurancePrice);
            customInsurancePolicy.setIgstRate(18);
            customInsurancePolicy.setIgstAmount(taxableInsurancePrice * 18 / 100);
            customInsurancePolicy.setCgstRate(9);
            customInsurancePolicy.setCgstAmount(taxableInsurancePrice * 9 / 100);
            customInsurancePolicy.setSgstRate(9);
            customInsurancePolicy.setSgstAmount(taxableInsurancePrice * 9 / 100);
            customInsurancePolicy.setNetAmount(insurancePolicy.getSaleAmount());
            customInsurancePolicies.add(customInsurancePolicy);
        }
        pdfModel.setInsurancePolicies(customInsurancePolicies);

        pdfModel.setCustomer(getCustomCustomer(fofoOrder));
        pdfModel.setInvoiceNumber(fofoOrder.getInvoiceNumber());
        pdfModel.setTotalAmount(fofoOrder.getTotalAmount());

        Retailer retailer = retailerRepository.selectById(fofoOrder.getFofoId());
        PrivateDealUser privateDealUser = null;
        try {
            privateDealUser = privateDealUserRepository.selectById(retailer.getId());
        } catch (ProfitMandiBusinessException profitMandiBusinessException) {
            LOGGER.error("Private Deal User not found : ", profitMandiBusinessException);
        }

        User user = userRepository.selectById(userAccountRepository.selectUserIdByRetailerId(retailer.getId()));
        CustomRetailer customRetailer = new CustomRetailer();
        customRetailer.setBusinessName(retailer.getName());
        customRetailer.setMobileNumber(user.getMobileNumber());
        // customRetailer.setTinNumber(retailer.getNumber());
        if (privateDealUser == null) {
            customRetailer.setGstNumber(null);
        } else {
            if (null != privateDealUser.getCounterId()) {
                Counter counter = counterRepository.selectById(privateDealUser.getCounterId());
                customRetailer.setGstNumber(counter.getGstin());
            } else {
                customRetailer.setGstNumber(null);
            }
        }
        Address retailerAddress = addressRepository.selectById(retailerRegisteredAddressRepository.selectAddressIdByRetailerId(retailer.getId()));
        customRetailer.setAddress(this.createCustomAddress(retailerAddress));
        pdfModel.setRetailer(customRetailer);

        Set<CustomOrderItem> customerFofoOrderItems = new HashSet<>();
        for (FofoOrderItem fofoOrderItem : fofoOrderItems) {
            float discount = fofoOrderItem.getDiscount();
            CustomOrderItem customFofoOrderItem = new CustomOrderItem();
            float totalTaxRate = fofoOrderItem.getIgstRate() + fofoOrderItem.getSgstRate() + fofoOrderItem.getCgstRate();
            float taxableSellingPrice = (fofoOrderItem.getSellingPrice() + discount) / (1 + totalTaxRate / 100);
            float taxableDiscountPrice = discount / (1 + totalTaxRate / 100);

            customFofoOrderItem.setAmount(fofoOrderItem.getQuantity() * (taxableSellingPrice - taxableDiscountPrice));
            customFofoOrderItem.setDescription(fofoOrderItem.getBrand() + " " + fofoOrderItem.getModelName() + " " + fofoOrderItem.getModelNumber() + "-" + fofoOrderItem.getColor());
            Set<String> serialNumbers = this.toSerialNumbers(fofoOrderItem.getFofoLineItems());
            // LOGGER.info("serialNumbers {}", serialNumbers);
            // LOGGER.info("serialNumbers is empty {}", serialNumbers.isEmpty());
            if (!serialNumbers.isEmpty()) {
                customFofoOrderItem.setDescription(
                        customFofoOrderItem.getDescription() + "\n IMEIS - " + String.join(", ", serialNumbers));
            }
            customFofoOrderItem.setRate(taxableSellingPrice);
            customFofoOrderItem.setDiscount(taxableDiscountPrice);
            customFofoOrderItem.setQuantity(fofoOrderItem.getQuantity());
            customFofoOrderItem.setNetAmount(fofoOrderItem.getSellingPrice() * fofoOrderItem.getQuantity());
            float igstAmount = (customFofoOrderItem.getAmount() * fofoOrderItem.getIgstRate()) / 100;
            float cgstAmount = (customFofoOrderItem.getAmount() * fofoOrderItem.getCgstRate()) / 100;
            float sgstAmount = (customFofoOrderItem.getAmount() * fofoOrderItem.getSgstRate()) / 100;
            customFofoOrderItem.setIgstRate(fofoOrderItem.getIgstRate());
            customFofoOrderItem.setIgstAmount(igstAmount);
            customFofoOrderItem.setCgstRate(fofoOrderItem.getCgstRate());
            customFofoOrderItem.setCgstAmount(cgstAmount);
            customFofoOrderItem.setSgstRate(fofoOrderItem.getSgstRate());
            customFofoOrderItem.setSgstAmount(sgstAmount);
            customFofoOrderItem.setHsnCode(fofoOrderItem.getHsnCode());
            customerFofoOrderItems.add(customFofoOrderItem);
        }
        pdfModel.setOrderItems(customerFofoOrderItems);
        String partnerAddressStateCode = stateRepository.selectByName(pdfModel.getRetailer().getAddress().getState()).getCode();
        String customerAddressStateCode = stateRepository.selectByName(pdfModel.getCustomer().getAddress().getState()).getCode();
        pdfModel.setPartnerAddressStateCode(partnerAddressStateCode);
        pdfModel.setCustomerAddressStateCode(customerAddressStateCode);
        pdfModel.setCancelled(fofoOrder.getCancelledTimestamp() != null);
        List<String> tncs = new ArrayList<>();
        tncs.add("I agree that goods received are in good working condition");
        tncs.add("Goods once sold cannot be exchanged or taken back");
        tncs.add("Warranty for the goods received by me is the responsibility of the manufacturer only.");
        tncs.add("Customer needs to activate the handset at the time of delivery to be eligible for the discount");
        tncs.add(
                "Tempered Glass Replacement will be done only for the Mobile Phone which was purchased from SmartDukaan");
        tncs.add(
                "Customers requesting Tempered Glass Replacement will have to bring the broken tempered glass, either pasted on the phone or along with the phone");
        tncs.add("Service fee of Rs.20 will be chargeable for each Tempered Glass Replacement");
        if (pdfModel.getInsurancePolicies() != null && pdfModel.getInsurancePolicies().size() > 0) {
            tncs.add("Damage protection provided is the responisibility of Protection Provider only");
        }
        pdfModel.setTncs(tncs);
        return pdfModel;

    }

    private CustomCustomer getCustomCustomer(FofoOrder fofoOrder) throws ProfitMandiBusinessException {
        Customer customer = customerRepository.selectById(fofoOrder.getCustomerId());
        CustomCustomer customCustomer = new CustomCustomer();
        customCustomer.setFirstName(customer.getFirstName());
        customCustomer.setLastName(customer.getLastName());
        customCustomer.setEmailId(customer.getEmailId());
        customCustomer.setMobileNumber(customer.getMobileNumber());
        customCustomer.setGstNumber(fofoOrder.getCustomerGstNumber());
        CustomerAddress customerAddress = customerAddressRepository.selectById(fofoOrder.getCustomerAddressId());
        customCustomer.setAddress(this.createCustomAddress(customerAddress));
        return customCustomer;
    }

    @Override
    public InvoicePdfModel getInvoicePdfModel(int fofoId, int orderId) throws ProfitMandiBusinessException {
        FofoOrder fofoOrder = fofoOrderRepository.selectByFofoIdAndOrderId(fofoId, orderId);
        return this.getInvoicePdfModel(fofoOrder);
    }

    public String getBillingAddress(CustomerAddress customerAddress) {
        StringBuilder address = new StringBuilder();
        if ((customerAddress.getLine1() != null) && (!customerAddress.getLine1().isEmpty())) {
            address.append(customerAddress.getLine1());
            address.append(", ");
        }

        if ((customerAddress.getLine2() != null) && (!customerAddress.getLine2().isEmpty())) {
            address.append(customerAddress.getLine2());
            address.append(", ");
        }

        if ((customerAddress.getLandmark() != null) && (!customerAddress.getLandmark().isEmpty())) {
            address.append(customerAddress.getLandmark());
            address.append(", ");
        }

        if ((customerAddress.getCity() != null) && (!customerAddress.getCity().isEmpty())) {
            address.append(customerAddress.getCity());
            address.append(", ");
        }

        if ((customerAddress.getState() != null) && (!customerAddress.getState().isEmpty())) {
            address.append(customerAddress.getState());
        }

        if ((customerAddress.getPinCode() != null) && (!customerAddress.getPinCode().isEmpty())) {
            address.append("- ");
            address.append(customerAddress.getPinCode());
        }

        return address.toString();
    }

    @Override
    public List<CartFofo> cartCheckout(String cartJson) throws ProfitMandiBusinessException {
        try {
            JSONObject cartObject = new JSONObject(cartJson);
            Iterator<?> keys = cartObject.keys();

            Set<Integer> itemIds = new HashSet<>();
            List<CartFofo> cartItems = new ArrayList<CartFofo>();

            while (keys.hasNext()) {
                String key = (String) keys.next();
                if (cartObject.get(key) instanceof JSONObject) {
                    LOGGER.info(cartObject.get(key).toString());
                }
                CartFofo cf = new CartFofo();
                cf.setItemId(cartObject.getJSONObject(key).getInt("itemId"));
                cf.setQuantity(cartObject.getJSONObject(key).getInt("quantity"));
                if (cartObject.getJSONObject(key).has("poId")) {

                    cf.setPoId(cartObject.getJSONObject(key).getInt("poId"));
                    cf.setPoItemId(cartObject.getJSONObject(key).getInt("poItemId"));
                }
                if (cf.getQuantity() <= 0) {
                    continue;
                }
                cartItems.add(cf);
                itemIds.add(cartObject.getJSONObject(key).getInt("itemId"));
            }
            Map<Integer, Item> itemMap = new HashMap<Integer, Item>();
            if (itemIds.size() > 0) {
                List<Item> items = itemRepository.selectByIds(itemIds);
                for (Item i : items) {
                    itemMap.put(i.getId(), i);
                }

            }
            for (CartFofo cf : cartItems) {
                Item i = itemMap.get(cf.getItemId());
                if (i == null) {
                    continue;
                }
                cf.setDisplayName(getValidName(i.getBrand()) + " " + getValidName(i.getModelName()) + " " + getValidName(i.getModelNumber()) + " " + getValidName(i.getColor()).replaceAll("\\s+", " "));
                cf.setItemType(i.getType());
            }
            return cartItems;
        } catch (Exception e) {
            LOGGER.error("Unable to Prepare cart to place order...", e);
            throw new ProfitMandiBusinessException("cartData", cartJson, "FFORDR_1006");
        }
    }

    @Override
    public Map<String, Object> getSaleHistory(int fofoId, SearchType searchType, String searchValue, LocalDateTime startDate, LocalDateTime endDate, int offset, int limit) throws ProfitMandiBusinessException {
        long countItems = 0;
        List<FofoOrder> fofoOrders = new ArrayList<>();

        if (searchType == SearchType.CUSTOMER_MOBILE_NUMBER && !searchValue.isEmpty()) {
            fofoOrders = fofoOrderRepository.selectByFofoIdAndCustomerMobileNumber(fofoId, searchValue, null, null, offset, limit);
            countItems = fofoOrderRepository.selectCountByCustomerMobileNumber(fofoId, searchValue, null, null);
        } else if (searchType == SearchType.CUSTOMER_NAME && !searchValue.isEmpty()) {
            fofoOrders = fofoOrderRepository.selectByFofoIdAndCustomerName(fofoId, searchValue, null, null, offset, limit);
            countItems = fofoOrderRepository.selectCountByCustomerName(fofoId, searchValue, null, null);
        } else if (searchType == SearchType.IMEI && !searchValue.isEmpty()) {
            fofoOrders = fofoOrderRepository.selectByFofoIdAndSerialNumber(fofoId, searchValue, null, null, offset, limit);
            countItems = fofoOrderRepository.selectCountBySerialNumber(fofoId, searchValue, null, null);
        } else if (searchType == SearchType.ITEM_NAME && !searchValue.isEmpty()) {
            fofoOrders = fofoOrderRepository.selectByFofoIdAndItemName(fofoId, searchValue, null, null, offset, limit);
            countItems = fofoOrderRepository.selectCountByItemName(fofoId, searchValue, null, null);
        } else if (searchType == SearchType.INVOICE_NUMBER && !searchValue.isEmpty()) {
            fofoOrders = Arrays.asList(fofoOrderRepository.selectByFofoIdAndInvoiceNumber(fofoId, searchValue));
            countItems = fofoOrders.size();
        } else if (searchType == SearchType.DATE_RANGE) {
            fofoOrders = fofoOrderRepository.selectByFofoId(fofoId, startDate, endDate, offset, limit);
            countItems = fofoOrderRepository.selectCountByFofoId(fofoId, startDate, endDate);
        }
        Map<String, Object> map = new HashMap<>();

        map.put("saleHistories", fofoOrders);
        map.put("start", offset + 1);
        map.put("size", countItems);
        map.put("searchType", searchType);
        map.put("searchTypes", SearchType.values());
        map.put("startDate", startDate);
        map.put("searchValue", searchValue);
        map.put(ProfitMandiConstants.END_TIME, endDate);
        if (fofoOrders.size() < limit) {
            map.put("end", offset + fofoOrders.size());
        } else {
            map.put("end", offset + limit);
        }
        return map;
    }

    public ResponseEntity<?> downloadReportInCsv(org.apache.commons.io.output.ByteArrayOutputStream baos, List<List<?>> rows, String fileName) {
        final HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "text/csv");

        headers.set("Content-disposition", "inline; filename=" + fileName + ".csv");
        headers.setContentLength(baos.toByteArray().length);

        final InputStream inputStream = new ByteArrayInputStream(baos.toByteArray());
        final InputStreamResource inputStreamResource = new InputStreamResource(inputStream);

        return new ResponseEntity<InputStreamResource>(inputStreamResource, headers, HttpStatus.OK);
    }

    @Override
    public Map<String, Object> getSaleHistoryPaginated(int fofoId, SearchType searchType, String searchValue, LocalDateTime startDate, LocalDateTime endDate, int offset, int limit) throws ProfitMandiBusinessException {
        List<FofoOrder> fofoOrders = new ArrayList<>();

        if (searchType == SearchType.CUSTOMER_MOBILE_NUMBER && !searchValue.isEmpty()) {
            fofoOrders = fofoOrderRepository.selectByFofoIdAndCustomerMobileNumber(fofoId, searchValue, startDate, endDate, offset, limit);
        } else if (searchType == SearchType.CUSTOMER_NAME && !searchValue.isEmpty()) {
            fofoOrders = fofoOrderRepository.selectByFofoIdAndCustomerName(fofoId, searchValue, startDate, endDate, offset, limit);
        } else if (searchType == SearchType.IMEI && !searchValue.isEmpty()) {
            fofoOrders = fofoOrderRepository.selectByFofoIdAndSerialNumber(fofoId, searchValue, startDate, endDate, offset, limit);
        } else if (searchType == SearchType.ITEM_NAME && !searchValue.isEmpty()) {
            fofoOrders = fofoOrderRepository.selectByFofoIdAndItemName(fofoId, searchValue, startDate, endDate, offset, limit);

        } else if (searchType == SearchType.DATE_RANGE) {
            fofoOrders = fofoOrderRepository.selectByFofoId(fofoId, startDate, endDate, offset, limit);
        }
        Map<String, Object> map = new HashMap<>();
        map.put("saleHistories", fofoOrders);
        map.put("searchType", searchType);
        map.put("searchTypes", SearchType.values());
        map.put("startDate", startDate);
        map.put("searchValue", searchValue);
        map.put(ProfitMandiConstants.END_TIME, endDate);
        return map;
    }

    private String getFofoStoreCode(int fofoId) throws ProfitMandiBusinessException {
        FofoStore fofoStore = fofoStoreRepository.selectByRetailerId(fofoId);
        return fofoStore.getCode();
    }

    private String getValidName(String name) {
        return name != null ? name : "";
    }

    private Set<String> toSerialNumbers(Set<FofoLineItem> fofoLineItems) {
        Set<String> serialNumbers = new HashSet<>();
        for (FofoLineItem fofoLineItem : fofoLineItems) {
            if (fofoLineItem.getSerialNumber() != null && !fofoLineItem.getSerialNumber().isEmpty()) {
                serialNumbers.add(fofoLineItem.getSerialNumber());
            }
        }
        return serialNumbers;
    }

    private void validateDpPrice(Map<Integer, PriceModel> itemIdMopPriceMap, Map<Integer, CustomFofoOrderItem> itemIdCustomFofoLineItemMap) throws ProfitMandiBusinessException {
        for (Map.Entry<Integer, CustomFofoOrderItem> entry : itemIdCustomFofoLineItemMap.entrySet()) {
            int itemId = entry.getKey();
            CustomFofoOrderItem customFofoOrderItem = entry.getValue();
            LOGGER.info("CustomFofoOrderItem -- {}", customFofoOrderItem);
            PriceModel priceModel = itemIdMopPriceMap.get(itemId);
            Item item = itemRepository.selectById(itemId);
            if (!item.getBrand().equals("Live Demo") && (item.getCategoryId() == ProfitMandiConstants.MOBILE_CATEGORY_ID || item.getCategoryId() == ProfitMandiConstants.TABLET_CATEGORY_ID || item.getCategoryId() == ProfitMandiConstants.LED_CATEGORY_ID) && customFofoOrderItem.getSerialNumberDetails().stream().filter(x -> org.apache.commons.lang.StringUtils.isNotEmpty(x.getSerialNumber())).collect(Collectors.toList()).size() > 0) {
                if (Utils.compareFloat(priceModel.getPrice(), customFofoOrderItem.getSellingPrice() + customFofoOrderItem.getDiscountAmount()) > 0) {
                    throw new ProfitMandiBusinessException("Selling Price for ", item.getItemDescription(), "FFORDR_1010");
                }
            } else {
                if (!item.getBrand().equals("Live Demo") && priceModel.getPurchasePrice() > customFofoOrderItem.getSellingPrice()) {
                    throw new ProfitMandiBusinessException("Selling Price", itemRepository.selectById(itemId).getItemDescription(), "Selling Price should not be less than DP");
                }
            }
        }
    }

    private void validateMopPrice(Map<Integer, PriceModel> itemIdMopPriceMap, Map<Integer, CustomFofoOrderItem> itemIdCustomFofoLineItemMap) throws ProfitMandiBusinessException {
        Map<Integer, Float> invalidMopItemIdPriceMap = new HashMap<>();
        for (Map.Entry<Integer, PriceModel> entry : itemIdMopPriceMap.entrySet()) {
            CustomFofoOrderItem customFofoOrderItem = itemIdCustomFofoLineItemMap.get(entry.getKey());
            Item item = itemRepository.selectById(customFofoOrderItem.getItemId());
            if (!(item.getBrand().equals("Live Demo") || item.getCategoryId() != ProfitMandiConstants.MOBILE_CATEGORY_ID || item.getCategoryId() != ProfitMandiConstants.TABLET_CATEGORY_ID) && customFofoOrderItem.getSellingPrice() + customFofoOrderItem.getDiscountAmount() < entry.getValue().getPrice()) {
                invalidMopItemIdPriceMap.put(entry.getKey(), customFofoOrderItem.getSellingPrice());
            }
        }

        if (!invalidMopItemIdPriceMap.isEmpty()) {
            LOGGER.error("Invalid itemIds selling prices{} should be greater than mop prices {}", invalidMopItemIdPriceMap, itemIdMopPriceMap);
            throw new ProfitMandiBusinessException("invalidMopItemIdPrice", invalidMopItemIdPriceMap, "FFORDR_1010");
        }

    }

    private void updateInventoryItemsAndScanRecord(Set<InventoryItem> inventoryItems, int fofoId, Map<Integer, Integer> inventoryItemQuantityUsed, int fofoOrderId) {
        for (InventoryItem inventoryItem : inventoryItems) {
            inventoryItem.setLastScanType(ScanType.SALE);
            ScanRecord scanRecord = new ScanRecord();
            scanRecord.setInventoryItemId(inventoryItem.getId());
            scanRecord.setFofoId(fofoId);
            scanRecord.setOrderId(fofoOrderId);
            // correct this
            scanRecord.setQuantity(inventoryItemQuantityUsed.get(inventoryItem.getId()));
            scanRecord.setType(ScanType.SALE);
            scanRecordRepository.persist(scanRecord);
            purchaseReturnItemRepository.deleteById(inventoryItem.getId());

        }
    }

    private void createFofoLineItem(int fofoOrderItemId, Set<InventoryItem> inventoryItems, Map<Integer, Integer> inventoryItemIdQuantityUsed) {
        for (InventoryItem inventoryItem : inventoryItems) {
            FofoLineItem fofoLineItem = new FofoLineItem();
            fofoLineItem.setFofoOrderItemId(fofoOrderItemId);
            fofoLineItem.setSerialNumber(inventoryItem.getSerialNumber());
            fofoLineItem.setInventoryItemId(inventoryItem.getId());
            fofoLineItem.setQuantity(inventoryItemIdQuantityUsed.get(inventoryItem.getId()));
            fofoLineItemRepository.persist(fofoLineItem);
        }
    }

    private FofoOrderItem createAndGetFofoOrderItem(CustomFofoOrderItem customFofoOrderItem, int fofoOrderId, Map<Integer, Item> itemMap, Set<InventoryItem> inventoryItems, Integer stateId) throws ProfitMandiBusinessException {
        FofoOrderItem fofoOrderItem = new FofoOrderItem();
        fofoOrderItem.setItemId(customFofoOrderItem.getItemId());
        fofoOrderItem.setQuantity(customFofoOrderItem.getQuantity());
        fofoOrderItem.setSellingPrice(customFofoOrderItem.getSellingPrice());
        fofoOrderItem.setOrderId(fofoOrderId);
        TagListing tl = tagListingRepository.selectByItemId(customFofoOrderItem.getItemId());
        // In case listing gets removed rebill it using the selling price
        if (tl != null) {
            fofoOrderItem.setDp(tl.getSellingPrice());
            fofoOrderItem.setMop(tl.getMop());
        } else {
            fofoOrderItem.setDp(customFofoOrderItem.getSellingPrice());
            fofoOrderItem.setMop(customFofoOrderItem.getSellingPrice());
        }
        fofoOrderItem.setDiscount(customFofoOrderItem.getDiscountAmount());

        Item item = itemMap.get(customFofoOrderItem.getItemId());
        Map<Integer, GstRate> itemIdStateTaxRateMap = null;
        if (stateId != null) {
            itemIdStateTaxRateMap = stateGstRateRepository.getStateTaxRate(new ArrayList<>(itemMap.keySet()), stateId);
        } else {
            itemIdStateTaxRateMap = stateGstRateRepository.getIgstTaxRate(new ArrayList<>(itemMap.keySet()));
        }
        for (InventoryItem inventoryItem : inventoryItems) {

            fofoOrderItem.setIgstRate(itemIdStateTaxRateMap.get(inventoryItem.getItemId()).getIgstRate());

            fofoOrderItem.setCgstRate(itemIdStateTaxRateMap.get(inventoryItem.getItemId()).getCgstRate());
            fofoOrderItem.setSgstRate(itemIdStateTaxRateMap.get(inventoryItem.getItemId()).getSgstRate());


            fofoOrderItem.setHsnCode(inventoryItem.getHsnCode());
            break;
        }
        fofoOrderItem.setBrand(item.getBrand());
        fofoOrderItem.setModelName(item.getModelName());
        fofoOrderItem.setModelNumber(item.getModelNumber());
        fofoOrderItem.setColor(item.getColor());
        fofoOrderItemRepository.persist(fofoOrderItem);
        return fofoOrderItem;
    }

    private FofoOrderItem getDummyFofoOrderItem(int itemId, int fofoOrderId, String serialNumber, Integer stateId) throws ProfitMandiBusinessException {
        Item item = itemRepository.selectById(itemId);
        TagListing tl = tagListingRepository.selectByItemId(itemId);
        FofoOrderItem fofoOrderItem = new FofoOrderItem();
        fofoOrderItem.setItemId(itemId);
        fofoOrderItem.setQuantity(1);
        fofoOrderItem.setSellingPrice(tl.getMop());
        fofoOrderItem.setOrderId(fofoOrderId);
        // In case listing gets removed rebill it using the selling price
        fofoOrderItem.setDp(tl.getSellingPrice());
        fofoOrderItem.setMop(tl.getMop());
        fofoOrderItem.setDiscount(0);

        Map<Integer, GstRate> itemIdStateTaxRateMap = null;
        if (stateId != null) {
            itemIdStateTaxRateMap = stateGstRateRepository.getStateTaxRate(Arrays.asList(itemId), stateId);
        } else {
            itemIdStateTaxRateMap = stateGstRateRepository.getIgstTaxRate(Arrays.asList(itemId));
        }

        fofoOrderItem.setIgstRate(itemIdStateTaxRateMap.get(itemId).getIgstRate());

        fofoOrderItem.setCgstRate(itemIdStateTaxRateMap.get(itemId).getCgstRate());
        fofoOrderItem.setSgstRate(itemIdStateTaxRateMap.get(itemId).getSgstRate());


        fofoOrderItem.setHsnCode(item.getHsnCode());
        fofoOrderItem.setBrand(item.getBrand());
        fofoOrderItem.setModelName(item.getModelName());
        fofoOrderItem.setModelNumber(item.getModelNumber());
        fofoOrderItem.setColor(item.getColor());

        Set<FofoLineItem> fofoLineItems = new HashSet<>();
        FofoLineItem fli = new FofoLineItem();
        fli.setQuantity(1);
        fli.setSerialNumber(serialNumber);
        fofoLineItems.add(fli);
        fofoOrderItem.setFofoLineItems(fofoLineItems);

        return fofoOrderItem;
    }

    private void updateCurrentInventorySnapshot(List<CurrentInventorySnapshot> currentInventorySnapshots, int fofoId, int itemId, int quantity) throws ProfitMandiBusinessException {
        for (CurrentInventorySnapshot currentInventorySnapshot : currentInventorySnapshots) {
            if (currentInventorySnapshot.getItemId() == itemId && currentInventorySnapshot.getFofoId() == fofoId) {
                currentInventorySnapshotRepository.updateAvailabilityByItemIdAndFofoId(itemId, fofoId, currentInventorySnapshot.getAvailability() - quantity);
            }
        }
    }

    private void createPaymentOptions(FofoOrder fofoOrder, Set<CustomPaymentOption> customPaymentOptions) throws ProfitMandiBusinessException {
        for (CustomPaymentOption customPaymentOption : customPaymentOptions) {
            if (customPaymentOption.getAmount() > 0) {
                PaymentOptionTransaction paymentOptionTransaction = new PaymentOptionTransaction();
                paymentOptionTransaction.setReferenceId(fofoOrder.getId());
                paymentOptionTransaction.setPaymentOptionId(customPaymentOption.getPaymentOptionId());
                paymentOptionTransaction.setReferenceType(PaymentOptionReferenceType.ORDER);
                paymentOptionTransaction.setAmount(customPaymentOption.getAmount());
                paymentOptionTransaction.setFofoId(fofoOrder.getFofoId());
                paymentOptionTransactionRepository.persist(paymentOptionTransaction);
            }
        }
    }

    private FofoOrder createAndGetFofoOrder(int customerId, String customerGstNumber, int fofoId, String documentNumber, float totalAmount, int customerAddressId) {
        FofoOrder fofoOrder = new FofoOrder();
        fofoOrder.setCustomerGstNumber(customerGstNumber);
        fofoOrder.setCustomerId(customerId);
        fofoOrder.setFofoId(fofoId);
        fofoOrder.setInvoiceNumber(documentNumber);
        fofoOrder.setTotalAmount(totalAmount);
        fofoOrder.setCustomerAddressId(customerAddressId);
        fofoOrderRepository.persist(fofoOrder);
        return fofoOrder;
    }

    private void validateItemsSerializedNonSerialized(List<Item> items, Map<Integer, CustomFofoOrderItem> customFofoOrderItemMap) throws ProfitMandiBusinessException {
        List<Integer> invalidItemIdSerialNumbers = new ArrayList<Integer>();
        List<Integer> itemIdNonSerializedSerialNumbers = new ArrayList<Integer>();
        for (Item i : items) {
            CustomFofoOrderItem customFofoOrderItem = customFofoOrderItemMap.get(i.getId());
            if (i.getType().equals(ItemType.SERIALIZED)) {
                if (customFofoOrderItem == null || customFofoOrderItem.getSerialNumberDetails().isEmpty()) {
                    invalidItemIdSerialNumbers.add(i.getId());
                }
            } else {
                Set<String> serialNumbers = this.serialNumberDetailsToSerialNumbers(customFofoOrderItem.getSerialNumberDetails());
                if (customFofoOrderItem == null || !serialNumbers.isEmpty()) {
                    itemIdNonSerializedSerialNumbers.add(i.getId());
                }
            }
        }

        if (!invalidItemIdSerialNumbers.isEmpty()) {
            LOGGER.error("Invalid itemId's serialNumbers for serialized{}", invalidItemIdSerialNumbers);
            // itemId's are serialized you are saying these are not serialized
            throw new ProfitMandiBusinessException("invalidItemIdSerialNumbers", invalidItemIdSerialNumbers, "FFORDR_1013");
        }

        if (!itemIdNonSerializedSerialNumbers.isEmpty()) {
            LOGGER.error("Invalid itemId's serialNumbers for non serialized{}", itemIdNonSerializedSerialNumbers);
            // itemId's are non serialized you are saying these are serialized
            throw new ProfitMandiBusinessException("itemIdNonSerializedSerialNumbers", itemIdNonSerializedSerialNumbers, "FFORDR_1014");
        }
    }

    private void validateCurrentInventorySnapshotQuantities(List<CurrentInventorySnapshot> currentInventorySnapshots, Map<Integer, CustomFofoOrderItem> itemIdCustomFofoOrderItemMap) throws ProfitMandiBusinessException {
        if (itemIdCustomFofoOrderItemMap.keySet().size() != currentInventorySnapshots.size()) {
            throw new ProfitMandiBusinessException("quantiiesSize", currentInventorySnapshots.size(), "");
        }
        List<ItemIdQuantityAvailability> itemIdQuantityAvailabilities = new ArrayList<>(); // this is for error
        LOGGER.info("currentInventorySnapshots " + currentInventorySnapshots);
        LOGGER.info("CustomFofoLineItemMap {}", itemIdCustomFofoOrderItemMap);
        for (CurrentInventorySnapshot currentInventorySnapshot : currentInventorySnapshots) {
            CustomFofoOrderItem customFofoOrderItem = itemIdCustomFofoOrderItemMap.get(currentInventorySnapshot.getItemId());
            LOGGER.info("customFofoOrderItem {}", customFofoOrderItem);
            if (customFofoOrderItem.getQuantity() > currentInventorySnapshot.getAvailability()) {
                ItemIdQuantityAvailability itemIdQuantityAvailability = new ItemIdQuantityAvailability();
                itemIdQuantityAvailability.setItemId(customFofoOrderItem.getItemId());
                Quantity quantity = new Quantity();
                quantity.setAvailable(currentInventorySnapshot.getAvailability());
                quantity.setRequested(customFofoOrderItem.getQuantity());
                itemIdQuantityAvailability.setQuantity(quantity);
                itemIdQuantityAvailabilities.add(itemIdQuantityAvailability);
            }
        }

        if (!itemIdQuantityAvailabilities.isEmpty()) {
            // itemIdQuantity request is not valid
            LOGGER.error("Requested quantities should not be greater than currently available quantities {}", itemIdQuantityAvailabilities);
            throw new ProfitMandiBusinessException("itemIdQuantityAvailabilities", itemIdQuantityAvailabilities, "FFORDR_1015");
        }
    }

    private int getItemIdFromSerialNumber(Map<Integer, CustomFofoOrderItem> itemIdCustomFofoOrderItemMap, String serialNumber) {
        int itemId = 0;
        for (Map.Entry<Integer, CustomFofoOrderItem> entry : itemIdCustomFofoOrderItemMap.entrySet()) {
            Set<SerialNumberDetail> serialNumberDetails = entry.getValue().getSerialNumberDetails();
            for (SerialNumberDetail serialNumberDetail : serialNumberDetails) {
                if (serialNumberDetail.getSerialNumber().equals(serialNumber)) {
                    itemId = entry.getKey();
                    break;
                }
            }
        }
        return itemId;
    }

    private Map<Integer, Item> toItemMap(List<Item> items) {
        Function<Item, Integer> itemIdFunction = new Function<Item, Integer>() {
            @Override
            public Integer apply(Item item) {
                return item.getId();
            }
        };
        Function<Item, Item> itemFunction = new Function<Item, Item>() {
            @Override
            public Item apply(Item item) {
                return item;
            }
        };
        return items.stream().collect(Collectors.toMap(itemIdFunction, itemFunction));
    }

    private void setCustomerAddress(CustomerAddress customerAddress, CustomAddress customAddress) {
        customerAddress.setName(customAddress.getName());
        customerAddress.setLastName(customAddress.getLastName());
        customerAddress.setLine1(customAddress.getLine1());
        customerAddress.setLine2(customAddress.getLine2());
        customerAddress.setLandmark(customAddress.getLandmark());
        customerAddress.setCity(customAddress.getCity());
        customerAddress.setPinCode(customAddress.getPinCode());
        customerAddress.setState(customAddress.getState());
        customerAddress.setCountry(customAddress.getCountry());
        customerAddress.setPhoneNumber(customAddress.getPhoneNumber());
    }

    private CustomAddress createCustomAddress(Address address) {
        CustomAddress customAddress = new CustomAddress();
        customAddress.setName(address.getName());
        customAddress.setLine1(address.getLine1());
        customAddress.setLine2(address.getLine2());
        customAddress.setLandmark(address.getLandmark());
        customAddress.setCity(address.getCity());
        customAddress.setPinCode(address.getPinCode());
        customAddress.setState(address.getState());
        customAddress.setCountry(address.getCountry());
        customAddress.setPhoneNumber(address.getPhoneNumber());
        return customAddress;
    }

    private CustomAddress createCustomAddress(CustomerAddress customerAddress) {
        CustomAddress customAddress = new CustomAddress();
        customAddress.setName(customerAddress.getName());
        customAddress.setLastName(customerAddress.getLastName());
        customAddress.setLine1(customerAddress.getLine1());
        customAddress.setLine2(customerAddress.getLine2());
        customAddress.setLandmark(customerAddress.getLandmark());
        customAddress.setCity(customerAddress.getCity());
        customAddress.setPinCode(customerAddress.getPinCode());
        customAddress.setState(customerAddress.getState());
        customAddress.setCountry(customerAddress.getCountry());
        customAddress.setPhoneNumber(customerAddress.getPhoneNumber());
        return customAddress;
    }

    private void validatePaymentOptionsAndTotalAmount(Set<CustomPaymentOption> customPaymentOptions, float totalAmount) throws ProfitMandiBusinessException {
        Set<Integer> paymentOptionIds = new HashSet<>();

        float calculatedAmount = 0;
        for (CustomPaymentOption customPaymentOption : customPaymentOptions) {
            paymentOptionIds.add(customPaymentOption.getPaymentOptionId());
            calculatedAmount = calculatedAmount + customPaymentOption.getAmount();
        }
        if (calculatedAmount != totalAmount) {
            LOGGER.warn("Error occured while validating payment options amount - {} != TotalAmount {}", calculatedAmount, totalAmount);
            throw new ProfitMandiBusinessException(ProfitMandiConstants.PAYMENT_OPTION_CALCULATED_AMOUNT, calculatedAmount, "FFORDR_1016");
        }

        List<Integer> foundPaymentOptionIds = paymentOptionRepository.selectIdsByIds(paymentOptionIds);
        if (foundPaymentOptionIds.size() != paymentOptionIds.size()) {
            paymentOptionIds.removeAll(foundPaymentOptionIds);
            throw new ProfitMandiBusinessException(ProfitMandiConstants.PAYMENT_OPTION_ID, paymentOptionIds, "FFORDR_1017");
        }
    }

    @Override
    public List<FofoOrderItem> getByOrderId(int orderId) throws ProfitMandiBusinessException {
        List<FofoOrderItem> fofoOrderItems = fofoOrderItemRepository.selectByOrderId(orderId);
        if (!fofoOrderItems.isEmpty()) {
            List<FofoOrderItem> newFofoOrderItems = new ArrayList<>();
            Map<Integer, Set<FofoLineItem>> fofoOrderItemIdFofoLineItemsMap = this.toFofoOrderItemIdFofoLineItems(fofoOrderItems);
            Iterator<FofoOrderItem> fofoOrderItemsIterator = fofoOrderItems.iterator();
            while (fofoOrderItemsIterator.hasNext()) {
                FofoOrderItem fofoOrderItem = fofoOrderItemsIterator.next();
                fofoOrderItem.setFofoLineItems(fofoOrderItemIdFofoLineItemsMap.get(fofoOrderItem.getId()));
                newFofoOrderItems.add(fofoOrderItem);
                fofoOrderItemsIterator.remove();
            }
            fofoOrderItems = newFofoOrderItems;
        }
        return fofoOrderItems;
    }

    private Set<Integer> toFofoOrderItemIds(List<FofoOrderItem> fofoOrderItems) {
        Function<FofoOrderItem, Integer> fofoOrderItemToFofoOrderItemIdFunction = new Function<FofoOrderItem, Integer>() {
            @Override
            public Integer apply(FofoOrderItem fofoOrderItem) {
                return fofoOrderItem.getId();
            }
        };
        return fofoOrderItems.stream().map(fofoOrderItemToFofoOrderItemIdFunction).collect(Collectors.toSet());
    }

    private Map<Integer, Set<FofoLineItem>> toFofoOrderItemIdFofoLineItems(List<FofoOrderItem> fofoOrderItems) {
        Set<Integer> fofoOrderItemIds = this.toFofoOrderItemIds(fofoOrderItems);
        List<FofoLineItem> fofoLineItems = fofoLineItemRepository.selectByFofoOrderItemIds(fofoOrderItemIds);
        Map<Integer, Set<FofoLineItem>> fofoOrderItemIdFofoLineItemsMap = new HashMap<>();
        for (FofoLineItem fofoLineItem : fofoLineItems) {
            if (!fofoOrderItemIdFofoLineItemsMap.containsKey(fofoLineItem.getFofoOrderItemId())) {
                Set<FofoLineItem> fofoLineItems2 = new HashSet<>();
                fofoLineItems2.add(fofoLineItem);
                fofoOrderItemIdFofoLineItemsMap.put(fofoLineItem.getFofoOrderItemId(), fofoLineItems2);
            } else {
                fofoOrderItemIdFofoLineItemsMap.get(fofoLineItem.getFofoOrderItemId()).add(fofoLineItem);
            }
        }
        return fofoOrderItemIdFofoLineItemsMap;
    }

    @Override
    public void updateCustomerDetails(CustomCustomer customCustomer, String invoiceNumber) throws ProfitMandiBusinessException {
        FofoOrder fofoOrder = fofoOrderRepository.selectByInvoiceNumber(invoiceNumber);
        Customer customer = customerRepository.selectById(fofoOrder.getCustomerId());
        customer.setFirstName(customCustomer.getFirstName());
        customer.setLastName(customCustomer.getLastName());
        customer.setMobileNumber(customCustomer.getMobileNumber());
        customer.setEmailId(customCustomer.getEmailId());
        customerRepository.persist(customer);
        CustomerAddress customerAddress = customerAddressRepository.selectById(fofoOrder.getCustomerAddressId());
        if (!customerAddress.getState().equalsIgnoreCase(customCustomer.getAddress().getState())) {
            List<FofoOrderItem> fofoOrderItems = fofoOrderItemRepository.selectByOrderId(fofoOrder.getId());
            resetTaxation(fofoOrder.getFofoId(), customerAddress, fofoOrderItems);
        }
        this.setCustomerAddress(customerAddress, customCustomer.getAddress());
        customerAddressRepository.persist(customerAddress);
        fofoOrder.setCustomerGstNumber(customCustomer.getGstNumber());
    }

    private void resetTaxation(int fofoId, CustomerAddress customerAddress, List<FofoOrderItem> fofoOrderItems) throws ProfitMandiBusinessException {
        int retailerAddressId = retailerRegisteredAddressRepository.selectAddressIdByRetailerId(fofoId);

        Address retailerAddress = addressRepository.selectById(retailerAddressId);

        Integer stateId = null;
        if (customerAddress.getState().equalsIgnoreCase(retailerAddress.getState())) {
            try {
                stateId = Long.valueOf(stateRepository.selectByName(customerAddress.getState()).getId()).intValue();
            } catch (Exception e) {
                LOGGER.error("Unable to get state rates");
            }
        }
        List<Integer> itemIds = fofoOrderItems.stream().map(x -> x.getItemId()).collect(Collectors.toList());
        final Map<Integer, GstRate> gstRates;
        if (stateId != null) {
            gstRates = stateGstRateRepository.getStateTaxRate(itemIds, stateId);
        } else {
            gstRates = stateGstRateRepository.getIgstTaxRate(itemIds);
        }
        for (FofoOrderItem fofoOrderItem : fofoOrderItems) {
            GstRate rate = gstRates.get(fofoOrderItem.getItemId());
            fofoOrderItem.setCgstRate(rate.getCgstRate());
            fofoOrderItem.setSgstRate(rate.getSgstRate());
            fofoOrderItem.setIgstRate(rate.getIgstRate());
        }
    }

    @Override
    public CustomerCreditNote badReturn(int fofoId, FoiBadReturnRequest foiBadReturnRequest) throws ProfitMandiBusinessException {
        FofoOrderItem foi = fofoOrderItemRepository.selectById(foiBadReturnRequest.getFofoOrderItemId());
        FofoOrder fofoOrder = fofoOrderRepository.selectByOrderId(foi.getOrderId());
        if (fofoOrder.getFofoId() != fofoId) {
            throw new ProfitMandiBusinessException("Partner Auth", "", "Invalid Order");
        }
        int billedQty = foi.getQuantity() - customerReturnItemRepository.selectAllByOrderItemId(foi.getId()).size();
        if (foiBadReturnRequest.getMarkedBadArr().size() > billedQty) {
            throw new ProfitMandiBusinessException("Cant bad return more than what is billed", "", "Invalid Quantity");
        }
        List<CustomerReturnItem> customerReturnItems = new ArrayList<>();
        for (BadReturnRequest badReturnRequest : foiBadReturnRequest.getMarkedBadArr()) {
            CustomerReturnItem customerReturnItem = new CustomerReturnItem();
            customerReturnItem.setFofoId(fofoId);
            customerReturnItem.setFofoOrderItemId(foiBadReturnRequest.getFofoOrderItemId());
            customerReturnItem.setFofoOrderId(fofoOrder.getId());
            customerReturnItem.setRemarks(badReturnRequest.getRemarks());
            customerReturnItem.setInventoryItemId(badReturnRequest.getInventoryItemId());
            customerReturnItem.setQuantity(1);
            customerReturnItem.setType(ReturnType.BAD);
            // customerReturnItemRepository.persist(customerReturnItem);
            inventoryService.saleReturnInventoryItem(customerReturnItem);
            customerReturnItems.add(customerReturnItem);
        }
        CustomerCreditNote creditNote = generateCreditNote(fofoOrder, customerReturnItems);
        for (CustomerReturnItem customerReturnItem : customerReturnItems) {
            purchaseReturnService.returnInventoryItem(fofoId, false, customerReturnItem.getInventoryItemId(), ReturnType.BAD);
        }
        // This should cancel the order
        fofoOrder.setCancelledTimestamp(LocalDateTime.now());
        this.reverseScheme(fofoOrder);
        return creditNote;
    }

    private CustomerCreditNote generateCreditNote(FofoOrder fofoOrder, List<CustomerReturnItem> customerReturnItems) throws ProfitMandiBusinessException {

        InvoiceNumberGenerationSequence sequence = invoiceNumberGenerationSequenceRepository.selectByFofoId(fofoOrder.getFofoId());
        sequence.setCreditNoteSequence(sequence.getCreditNoteSequence() + 1);
        invoiceNumberGenerationSequenceRepository.persist(sequence);

        String creditNoteNumber = sequence.getPrefix() + "/" + sequence.getCreditNoteSequence();
        CustomerCreditNote creditNote = new CustomerCreditNote();
        creditNote.setCreditNoteNumber(creditNoteNumber);
        creditNote.setFofoId(fofoOrder.getFofoId());
        creditNote.setFofoOrderId(fofoOrder.getId());
        creditNote.setFofoOrderItemId(customerReturnItems.get(0).getFofoOrderItemId());
        creditNote.setSettlementType(SettlementType.UNSETTLED);
        customerCreditNoteRepository.persist(creditNote);

        for (CustomerReturnItem customerReturnItem : customerReturnItems) {
            customerReturnItem.setCreditNoteId(creditNote.getId());
            customerReturnItemRepository.persist(customerReturnItem);
        }
        // this.returnInventoryItems(inventoryItems, debitNote);

        return creditNote;
    }

    @Override
    public CreditNotePdfModel getCreditNotePdfModel(int customerCreditNoteId) throws ProfitMandiBusinessException {
        CustomerCreditNote creditNote = customerCreditNoteRepository.selectById(customerCreditNoteId);
        return getCreditNotePdfModel(creditNote);
    }

    private CreditNotePdfModel getCreditNotePdfModel(CustomerCreditNote creditNote) throws ProfitMandiBusinessException {
        FofoOrder fofoOrder = fofoOrderRepository.selectByOrderId(creditNote.getFofoOrderId());
        List<CustomerReturnItem> customerReturnItems = customerReturnItemRepository.selectAllByCreditNoteId(creditNote.getId());
        CustomCustomer customCustomer = getCustomCustomer(fofoOrder);

        Set<CustomOrderItem> customerFofoOrderItems = new HashSet<>();
        CustomRetailer customRetailer = retailerService.getFofoRetailer(fofoOrder.getFofoId());

        FofoOrderItem fofoOrderItem = fofoOrderItemRepository.selectById(creditNote.getFofoOrderItemId());
        float totalTaxRate = fofoOrderItem.getIgstRate() + fofoOrderItem.getSgstRate() + fofoOrderItem.getCgstRate();
        float taxableSellingPrice = fofoOrderItem.getSellingPrice() / (1 + totalTaxRate / 100);
        float taxableDiscountPrice = fofoOrderItem.getDiscount() / (1 + totalTaxRate / 100);

        CustomOrderItem customFofoOrderItem = new CustomOrderItem();
        customFofoOrderItem.setAmount(customerReturnItems.size() * (taxableSellingPrice - taxableDiscountPrice));
        customFofoOrderItem.setDescription(fofoOrderItem.getBrand() + " " + fofoOrderItem.getModelName() + " " + fofoOrderItem.getModelNumber() + "-" + fofoOrderItem.getColor());

        if (ItemType.SERIALIZED.equals(itemRepository.selectById(fofoOrderItem.getItemId()).getType())) {
            Set<Integer> inventoryItemIds = customerReturnItems.stream().map(x -> x.getInventoryItemId()).collect(Collectors.toSet());
            List<String> serialNumbers = inventoryItemRepository.selectByIds(inventoryItemIds).stream().map(x -> x.getSerialNumber()).collect(Collectors.toList());
            customFofoOrderItem.setDescription(
                    customFofoOrderItem.getDescription() + "\n IMEIS - " + String.join(", ", serialNumbers));
        }

        customFofoOrderItem.setRate(taxableSellingPrice);
        customFofoOrderItem.setDiscount(taxableDiscountPrice);
        customFofoOrderItem.setQuantity(customerReturnItems.size());
        customFofoOrderItem.setNetAmount(
                (fofoOrderItem.getSellingPrice() - fofoOrderItem.getDiscount()) * customFofoOrderItem.getQuantity());

        float igstAmount = (customFofoOrderItem.getAmount() * fofoOrderItem.getIgstRate()) / 100;
        float cgstAmount = (customFofoOrderItem.getAmount() * fofoOrderItem.getCgstRate()) / 100;
        float sgstAmount = (customFofoOrderItem.getAmount() * fofoOrderItem.getSgstRate()) / 100;
        LOGGER.info("fofoOrderItem - {}", fofoOrderItem);
        customFofoOrderItem.setIgstRate(fofoOrderItem.getIgstRate());
        customFofoOrderItem.setIgstAmount(igstAmount);
        customFofoOrderItem.setCgstRate(fofoOrderItem.getCgstRate());
        customFofoOrderItem.setCgstAmount(cgstAmount);
        customFofoOrderItem.setSgstRate(fofoOrderItem.getSgstRate());
        customFofoOrderItem.setSgstAmount(sgstAmount);
        customFofoOrderItem.setHsnCode(fofoOrderItem.getHsnCode());
        customFofoOrderItem.setOrderId(1);
        customerFofoOrderItems.add(customFofoOrderItem);

        InvoicePdfModel pdfModel = new InvoicePdfModel();
        pdfModel.setAuther("NSSPL");
        pdfModel.setCustomer(customCustomer);
        pdfModel.setInvoiceNumber(fofoOrder.getInvoiceNumber());
        pdfModel.setInvoiceDate(FormattingUtils.formatDate(fofoOrder.getCreateTimestamp()));
        pdfModel.setTitle("Credit Note");
        pdfModel.setRetailer(customRetailer);
        pdfModel.setTotalAmount(customFofoOrderItem.getNetAmount());
        pdfModel.setOrderItems(customerFofoOrderItems);

        CreditNotePdfModel creditNotePdfModel = new CreditNotePdfModel();
        creditNotePdfModel.setCreditNoteDate(FormattingUtils.formatDate(creditNote.getCreateTimestamp()));
        creditNotePdfModel.setCreditNoteNumber(creditNote.getCreditNoteNumber());
        creditNotePdfModel.setPdfModel(pdfModel);
        return creditNotePdfModel;
    }

    // This will remove the order and maintain order record and reverse inventory
    // and scheme
    @Override
    public void cancelOrder(List<String> invoiceNumbers) throws ProfitMandiBusinessException {
        for (String invoiceNumber : invoiceNumbers) {
            // Cancel only when not cancelled
            FofoOrder fofoOrder = fofoOrderRepository.selectByInvoiceNumber(invoiceNumber);
            if (fofoOrder.getCancelledTimestamp() == null) {
                fofoOrder.setCancelledTimestamp(LocalDateTime.now());
                PaymentOptionTransaction paymentTransaction = new PaymentOptionTransaction();
                paymentTransaction.setAmount(-fofoOrder.getTotalAmount());
                paymentTransaction.setFofoId(fofoOrder.getFofoId());
                paymentTransaction.setReferenceId(fofoOrder.getId());
                paymentTransaction.setReferenceType(PaymentOptionReferenceType.ORDER);
                paymentTransaction.setPaymentOptionId(1);
                paymentOptionTransactionRepository.persist(paymentTransaction);

                List<FofoOrderItem> fois = fofoOrderItemRepository.selectByOrderId(fofoOrder.getId());
                if (fois.size() > 0) {
                    List<InventoryItem> inventoryItems = new ArrayList<>();
                    fois.stream().forEach(x -> {
                        x.getFofoLineItems().stream().forEach(y -> {
                            inventoryService.rollbackInventory(y.getInventoryItemId(), y.getQuantity(), fofoOrder.getFofoId());
                            inventoryItems.add(inventoryItemRepository.selectById(y.getInventoryItemId()));
                        });
                    });
                    // if(invoice)
                    this.reverseScheme(fofoOrder);
                }
                insuranceService.cancelInsurance(fofoOrder);
            }
        }
    }

    @Override
    public void reverseScheme(FofoOrder fofoOrder) throws ProfitMandiBusinessException {
        String reversalReason = "Order Rolledback/Cancelled/Returned for Invoice #" + fofoOrder.getInvoiceNumber();
        List<FofoOrderItem> fois = fofoOrderItemRepository.selectByOrderId(fofoOrder.getId());
        Set<Integer> inventoryItemIds = fois.stream().flatMap(x -> x.getFofoLineItems().stream().map(y -> y.getInventoryItemId())).collect(Collectors.toSet());
        List<InventoryItem> inventoryItems = inventoryItemRepository.selectByIds(inventoryItemIds);
        schemeService.reverseSchemes(inventoryItems, fofoOrder.getId(), reversalReason, SchemeService.OUT_SCHEME_TYPES);
        schemeService.reverseSchemes(inventoryItems, fofoOrder.getId(), reversalReason, Arrays.asList(SchemeType.INVESTMENT));
        schemeService.reverseSchemes(inventoryItems, fofoOrder.getId(), reversalReason, Arrays.asList(SchemeType.ACTIVATION));
        schemeService.reverseSchemes(inventoryItems, fofoOrder.getId(), reversalReason, Arrays.asList(SchemeType.SPECIAL_SUPPORT));

    }

    @Override
    public void reverseActivationScheme(List<Integer> inventoryItemIds) throws ProfitMandiBusinessException {
        List<InventoryItem> inventoryItems = inventoryItemRepository.selectAllByIds(inventoryItemIds);
        for (InventoryItem inventoryItem : inventoryItems) {
            List<FofoLineItem> fofoLineItems = fofoLineItemRepository.selectByInventoryItemId(inventoryItem.getId());
            FofoLineItem fofoLineItem = fofoLineItems.get(0);
            FofoOrderItem fofoOrderItem = fofoOrderItemRepository.selectById(fofoLineItem.getFofoOrderItemId());
            FofoOrder fofoOrder = fofoOrderRepository.selectByOrderId(fofoOrderItem.getOrderId());
            String reversalReason = "Scheme rolled back as activation date is invalid for imei " + inventoryItem.getSerialNumber();
            schemeService.reverseSchemes(Arrays.asList(inventoryItem), fofoOrder.getId(), reversalReason, Arrays.asList(SchemeType.ACTIVATION));
            schemeService.reverseSchemes(Arrays.asList(inventoryItem), fofoOrder.getId(), reversalReason, Arrays.asList(SchemeType.SPECIAL_SUPPORT));

        }

    }

    @Override
    public float getSales(int fofoId, LocalDateTime startDate, LocalDateTime endDate) {
        Float sales = fofoOrderRepository.selectSaleSumGroupByFofoIds(startDate, endDate).get(fofoId);
        return sales == null ? 0f : sales;
    }

    @Override
    public LocalDateTime getMaxSalesDate(int fofoId, LocalDateTime startDate, LocalDateTime endDate) {
        LocalDateTime dateTime = fofoOrderRepository.selectMaxSaleDateGroupByFofoIds(startDate, endDate).get(fofoId);
        return dateTime;
    }

    @Override
    // Only being used internally
    public float getSales(int fofoId, LocalDate onDate) {
        LocalDateTime startTime = LocalDateTime.of(onDate, LocalTime.MIDNIGHT);
        LocalDateTime endTime = LocalDateTime.of(onDate, LocalTime.MIDNIGHT).plusDays(1);
        return this.getSales(fofoId, startTime, endTime);
    }

    @Override
    public float getSales(LocalDateTime onDate) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public float getSales(LocalDateTime startDate, LocalDateTime endDate) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public boolean notifyColorChange(int orderId, int itemId) throws ProfitMandiBusinessException {
        Order order = orderRepository.selectById(orderId);
        saholicInventoryService.reservationCountByColor(itemId, order);

        order.getLineItem().setItemId(itemId);
        Item item = itemRepository.selectById(itemId);
        order.getLineItem().setColor(item.getColor());
        return true;
    }

    @Override
    public FofoOrder getOrderByInventoryItemId(int inventoryItemId) throws Exception {
        List<FofoLineItem> lineItems = fofoLineItemRepository.selectByInventoryItemId(inventoryItemId);
        if (lineItems.size() > 0) {
            FofoOrderItem fofoOrderItem = fofoOrderItemRepository.selectById(lineItems.get(0).getFofoOrderItemId());
            fofoOrderItem.setFofoLineItems(new HashSet<>(lineItems));
            FofoOrder fofoOrder = fofoOrderRepository.selectByOrderId(fofoOrderItem.getOrderId());
            fofoOrder.setOrderItem(fofoOrderItem);
            return fofoOrder;
        } else {
            throw new Exception(String.format("Could not find inventoryItemId - %s", inventoryItemId));
        }
    }

    @Override
    public Map<Integer, Long> carryBagCreditCount(int fofoId) throws ProfitMandiBusinessException {

        FofoStore fs = fofoStoreRepository.selectByRetailerId(fofoId);
        LocalDateTime lastCredit = fs.getBagsLastCredited();
        /*
         * long carryBagCount = 0; List<FofoOrder> fofoOrders =
         * fofoOrderRepository.selectByFofoIdBetweenCreatedTimeStamp(fofoId,
         * lastCredit.atStartOfDay(), LocalDate.now().plusDays(1).atStartOfDay()); for
         * (FofoOrder fo : fofoOrders) { carryBagCount +=
         * fofoOrderItemRepository.selectByOrderId(fo.getId()).stream() .filter(x ->
         * x.getSellingPrice() >= 12000).count();
         *
         * }
         */

        Session session = sessionFactory.getCurrentSession();
        CriteriaBuilder cb = session.getCriteriaBuilder();

        CriteriaQuery<SimpleEntry> query = cb.createQuery(SimpleEntry.class);
        Root<FofoOrder> fofoOrder = query.from(FofoOrder.class);
        Root<FofoOrderItem> fofoOrderItem = query.from(FofoOrderItem.class);
        Root<TagListing> tagListingRoot = query.from(TagListing.class);
        Root<Item> itemRoot = query.from(Item.class);

        Predicate p2 = cb.between(fofoOrder.get(ProfitMandiConstants.CREATE_TIMESTAMP), lastCredit, LocalDate.now().atStartOfDay());
        Predicate p3 = cb.isNull(fofoOrder.get("cancelledTimestamp"));
        Predicate joinPredicate = cb.and(
                cb.equal(fofoOrder.get(ProfitMandiConstants.ID), fofoOrderItem.get(ProfitMandiConstants.ORDER_ID)), cb.equal(fofoOrderItem.get("itemId"), tagListingRoot.get("itemId")), cb.equal(itemRoot.get("id"), tagListingRoot.get("itemId")), cb.equal(fofoOrder.get(ProfitMandiConstants.FOFO_ID), fofoId));
        ItemCriteria itemCriteria = new ItemCriteria();
        itemCriteria.setBrands(mongoClient.getMongoBrands(fofoId, null, 3).stream().map(x -> (String) x.get("name")).collect(Collectors.toList()));
        float startValue = 12000;
        itemCriteria.setStartPrice(startValue);
        itemCriteria.setEndPrice(0);
        itemCriteria.setFeaturedPhone(false);
        itemCriteria.setSmartPhone(true);
        itemCriteria.setCatalogIds(new ArrayList<>());
        itemCriteria.setExcludeCatalogIds(new ArrayList<>());
        Predicate itemPredicate = itemRepository.getItemPredicate(itemCriteria, cb, itemRoot, tagListingRoot.get("itemId"), tagListingRoot.get("sellingPrice"));
        Predicate finalPredicate = cb.and(itemPredicate, p2, p3, joinPredicate);
        query = query.multiselect(fofoOrder.get(ProfitMandiConstants.FOFO_ID), cb.count(fofoOrder)).where(finalPredicate).groupBy(fofoOrder.get(ProfitMandiConstants.FOFO_ID));
        List<SimpleEntry> simpleEntries = session.createQuery(query).getResultList();
        Map<Integer, Long> returnMap = new HashMap<>();

        for (SimpleEntry simpleEntry : simpleEntries) {
            returnMap.put((Integer) simpleEntry.getKey(), (Long) simpleEntry.getValue());
        }
        return returnMap;

    }
}