Subversion Repositories SmartDukaan

Rev

Rev 34205 | Blame | Compare with Previous | Last modification | View Log | RSS feed

# -*- coding: utf-8 -*-

'''
Created on 29-Mar-2010

@author: Chandranshu
'''

from datetime import date, timedelta
from decimal import Decimal
from elixir import *
from expiringdict import ExpiringDict
from math import ceil
from random import randrange
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_JUSTIFY
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.units import inch, mm
from reportlab.pdfgen.canvas import Canvas
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, \
    BaseDocTemplate, Frame, PageTemplate, Table, TableStyle
from reportlab.rl_config import defaultPageSize
from shop2020.clients.AlertClient import AlertClient
from shop2020.clients.CRMClient import CRMClient
from shop2020.clients.CatalogClient import CatalogClient
from shop2020.clients.HelperClient import HelperClient
from shop2020.clients.InventoryClient import InventoryClient
from shop2020.clients.LogisticsClient import LogisticsClient
from shop2020.clients.PaymentClient import PaymentClient
from shop2020.clients.PromotionClient import PromotionClient
from shop2020.clients.UserClient import UserClient
from shop2020.clients.WarehouseClient import WarehouseClient
from shop2020.config.client.ConfigClient import ConfigClient
from shop2020.model.v1 import order
from shop2020.model.v1.order.impl import DataService, RechargeService
from shop2020.model.v1.order.impl.Convertors import to_t_emi_scheme, to_t_order, \
    cutoff_date, to_t_warehouse_address
from shop2020.model.v1.order.impl.DataService import Transaction, LineItem, \
    Order, BatchNoGenerator, InvoiceIDGenerator, TransactionRequiringExtraProcessing, \
    OrderInventory, Alert, PaymentSettlement, EBSSettlementSummary, \
    CodVerificationAgent, Attribute, RechargeVoucherTracker, EmiScheme, MiscCharges, \
    BlockedIpRange, DeniedIpAddress, InsuranceDetailForOrder, DocumentStore, \
    RechargeTransaction, HotspotStore, WalletForCompany, WalletHistoryForCompany, \
    FRC, OperatorSeries, SourceDetail, Company, EbayOrder, AmazonFbaSalesSnapshot, \
    AmazonOrder, StoreOrderDetail, EdcBank, StoreOrderCollection, \
    HotspotServiceMatrix, SnapdealOrder, FlipkartOrder, DataInsuranceDetailForOrder, \
    AmazonFbaOrderReturns, FlipkartAdvantageOrder, InvoiceCounterGenerator, \
    TransactionShipmentSequence, HsOrder, Creditor, UserSanction, CreditHistory, \
    LoanHistory, ShipmentLogisticsCostDetail, ReturnOrderInfo, ReturnTransaction, \
    ReturnPickupRequest, SellerWarehouse, Seller, Organisation, \
    WarehouseAddressMapping, WarehouseAddressMaster, PMSA, PMSA_Agents, \
    AdvancePayments, CodCourierPaymentRemittance, Price_Drop, Price_Drop_IMEI,\
    PendingRechargeCommissions, Line_Item_Imei
from shop2020.model.v1.order.impl.model.BaseOrder import BaseOrder
from shop2020.model.v1.order.impl.model.DTHRechargeOrder import DTHRechargeOrder
from shop2020.model.v1.order.impl.model.MobileRechargeOrder import \
    MobileRechargeOrder
from shop2020.model.v1.order.impl.model.RechargeDenomination import \
    RechargeDenomination
from shop2020.model.v1.order.impl.model.RechargeOrder import RechargeOrder
from shop2020.model.v1.order.impl.model.RechargePlan import RechargePlan
from shop2020.model.v1.order.impl.model.ReturnOrder import ReturnOrder
from shop2020.model.v1.order.impl.model.ServiceAvailability import \
    ServiceAvailability
from shop2020.model.v1.order.impl.model.ServiceProvider import ServiceProvider
from shop2020.model.v1.order.impl.model.TelecomCircle import TelecomCircle
from shop2020.model.v1.order.impl.model.UserWallet import UserWallet
from shop2020.model.v1.order.impl.model.UserWalletHistory import \
    UserWalletHistory
from shop2020.model.v1.user.impl.Converters import to_t_address
from shop2020.thriftpy.alert.ttypes import MonitoredEntity, EntityType
from shop2020.thriftpy.crm.ttypes import *
from shop2020.thriftpy.logistics.ttypes import DeliveryType, PickUpType
from shop2020.thriftpy.model.v1.catalog.ttypes import ItemType, ItemCondition
from shop2020.thriftpy.model.v1.inventory.ttypes import BillingType, \
    InventoryServiceException, WarehouseType, InventoryType, VatType, ItemInventory, \
    WarehouseLocation
from shop2020.thriftpy.model.v1.order.ttypes import TransactionServiceException, \
    TransactionStatus, OrderStatus, DelayReason, ExtraTransactionProcessingType, \
    HotspotAction, TimeoutSummary, OrderStatusGroups, OrderType, RechargeOrderStatus, \
    RechargeType, RechargeStatistics, DeviceNumberInfo, EmiChargeType, PayMethod, \
    OrderSource, StorePaymentStatus, ProductCondition, TaxType, \
    AmazonFCWarehouseLocation, RechargeMode, CreditTxnType, \
    CreditHistory as TCreditHistory, LoanHistory as TLoanHistory, \
    ShipmentLogisticsCostDetail as TShipmentLogisticsCostDetail, \
    ReturnTransactionStatus, ReturnAction, ReturnTxnResolutionStatus, \
    ReplacementShippingType, ReturnTxnPickupStatus, ReceivedReturnType, \
    ReturnPickupType, RefundType, SellerInfo, BuyerInfo, WalletReferenceType
from shop2020.thriftpy.model.v1.user.ttypes import VoucherType, CouponCategory, \
    User, Sex
from shop2020.thriftpy.payments.ttypes import PaymentException, PaymentStatus
from shop2020.thriftpy.utils.ttypes import UserSmsInfo, SmsType
from shop2020.thriftpy.warehouse.ttypes import ScanType, \
    WarehouseServiceException
from shop2020.utils.EmailAttachmentSender import mail, get_attachment_part, \
    mail_html
from shop2020.utils.Utils import to_py_date, to_java_date, \
    getSyncOperatorsForRecharge, generate_random_code
from sqlalchemy.orm import aliased, joinedload
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
from sqlalchemy.sql import func
from sqlalchemy.sql.expression import and_, or_, desc, not_, distinct, cast, \
    between
from string import Template
from suds.client import Client
from textwrap import dedent
from xml.dom.minidom import parseString
import MySQLdb
import base64
import collections
import datetime
import httplib
import logging
import math
import os
import re
import sys
import time
import traceback
import urllib
from shop2020.utils.caching.SimpleCaching import memoized



#Start:- Added By Manish Sharma for Creating a new Ticket: Category- RTO Refund on 21-Jun-2013
#End:- Added By Manish Sharma for Creating a new Ticket: Category- RTO Refund on 21-Jun-2013
#Start:- Added By Manish Sharma for Creating a new Ticket: Category- RTO Refund on 21-Jun-2013
#End:- Added By Manish Sharma for Creating a new Ticket: Category- RTO Refund on 21-Jun-2013
logging.basicConfig(level=logging.DEBUG)

sourceId = int(ConfigClient().get_property("sourceid"))
capitalFloatPayMethod = 456789 
SaholicHelpEmailId = "SmartDukaan <care@smartdukaan.com>"
mail_user = 'cnc.center@shop2020.in'
mail_password = '5h0p2o2o'
help_user = 'help@shop2020.in'
help_password = '5h0p2o2o'
source_url = 'www.saholic.com'
source_name = 'SmartDukaan'
hotspot_store_url ='http://125.19.98.100/loaddetect/service.asmx?WSDL'
aclient = None

PREPAID_SHIPPING_CUTOFF_TIME = 15
COD_SHIPPING_CUTOFF_TIME = 12

PAGE_HEIGHT=defaultPageSize[1]
PAGE_WIDTH=defaultPageSize[0]

billedOrdersColorMap = {}

ORDER_STATUS_TO_USER_TRUST_LEVEL_DELTA_DICT = {
        OrderStatus.RTO_RESHIPPED    :  3,
        OrderStatus.RTO_IN_TRANSIT   : -5,
        OrderStatus.DELIVERY_SUCCESS :  1,
        OrderStatus.DOA_CERT_INVALID : -5
}

creditTxnMultiplierMap = {
    CreditTxnType.BLOCKED : 1,
    CreditTxnType.BLOCKED_REVERSED : -1, 
    CreditTxnType.LOAN : -1,
    CreditTxnType.CORRECTION : 1                  
}

loanTxnMultplierMap = {
    CreditTxnType.LOAN : 1, 
    CreditTxnType.LOAN_CANCELLED : -1, 
    CreditTxnType.PAID : -1,
    CreditTxnType.CORRECTION : -1
}

refund_status_transition = { OrderStatus.LOST_IN_TRANSIT : OrderStatus.LOST_IN_TRANSIT_REFUNDED,
                     OrderStatus.RTO_RECEIVED_PRESTINE : OrderStatus.RTO_REFUNDED,
                     OrderStatus.RTO_RECEIVED_DAMAGED : OrderStatus.RTO_DAMAGED_REFUNDED,
                     OrderStatus.RTO_LOST_IN_TRANSIT : OrderStatus.RTO_LOST_IN_TRANSIT_REFUNDED,
                     OrderStatus.DOA_CERT_INVALID : OrderStatus.DOA_INVALID_REFUNDED,
                     OrderStatus.DOA_CERT_VALID : OrderStatus.DOA_VALID_REFUNDED,
                     OrderStatus.DOA_RECEIVED_DAMAGED : OrderStatus.DOA_REFUNDED_RCVD_DAMAGED,
                     OrderStatus.DOA_LOST_IN_TRANSIT : OrderStatus.DOA_REFUNDED_LOST_IN_TRANSIT,
                     OrderStatus.RET_PRODUCT_UNUSABLE : OrderStatus.RET_PRODUCT_UNUSABLE_REFUNDED,
                     OrderStatus.RET_PRODUCT_USABLE : OrderStatus.RET_PRODUCT_USABLE_REFUNDED,
                     OrderStatus.RET_RECEIVED_DAMAGED : OrderStatus.RET_REFUNDED_RCVD_DAMAGED,
                     OrderStatus.RET_LOST_IN_TRANSIT : OrderStatus.RET_REFUNDED_LOST_IN_TRANSIT,
                     OrderStatus.COD_VERIFICATION_PENDING : OrderStatus.COD_VERIFICATION_FAILED,
                     OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.INVENTORY_LOW : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.LOW_INV_PO_RAISED : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.LOW_INV_REVERSAL_IN_PROCESS : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.ACCEPTED : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.BILLED : OrderStatus.CANCELLED_ON_CUSTOMER_REQUEST,
                     OrderStatus.CANCEL_REQUEST_CONFIRMED : OrderStatus.CANCELLED_ON_CUSTOMER_REQUEST
                     }

creditorTicketSizeMap = {}
creditorDueDateMap = {}

stateIdMap = {}

gvGatewayId = 9
walletGatewayId = 8

HOURS=3600
CACHE_THREE_HOURS = ExpiringDict(max_len=10, max_age_seconds=3*HOURS)

dthRechargeCommission = 1.6
mobileRechargeCommission = 0.8


    
def fetchStateMaster():
    if not CACHE_THREE_HOURS.get("statemaster"):
        try:
            inventory_client = InventoryClient().get_client()
            CACHE_THREE_HOURS["statemaster"] = inventory_client.getStateMaster()
            
        except:
            print "Could not fetch"
    return CACHE_THREE_HOURS.get("statemaster")
            
def fetchCreditorTicketSizeMap():
    global creditorTicketSizeMap
    allCreditors = Creditor.query.all()
    for creditor in allCreditors:
        creditorTicketSizeMap[creditor.id] = creditor.ticket_size

def fetchCreditorDueDateMap():
    global creditorDueDateMap
    allCreditors = Creditor.query.all()
    for creditor in allCreditors:
        creditorDueDateMap[creditor.id] = creditor.credit_due_days


delhi_pincodes = ['110001','110002','110003','110004','110005','110006','110007','110008','110009','110010','110011','110012','110013','110014','110015',\
                '110016','110017','110018','110019','110020','110021','110022','110023','110024','110025','110026','110027','110028','110029','110030',\
                '110031','110032','110033','110034','110035','110037','110038','110041','110042','110044','110045','110046','110047','110048','110049',\
                '110051','110052','110053','110054','110055','110056','110057','110058','110059','110060','110061','110062','110063','110064','110065',\
                '110066','110067','110068','110070','110071','110074','110075','110076','110078','110081','110082','110083','110084','110085','110086',\
                '110087','110088','110089','110091','110092','110093','110094','110095','110096','110101','110103','110104','110105','110106','110107',\
                '110108','110109','110110','110112','110113','110114','110115','110116','110117','110118','110119','110120','110122','110124','110125',\
                '110301','110302','110501','110502','110503','110504','110505','110510','110511','110512','110601','110602','110603','110604','110605',\
                '110606','110607','110608','110609']

def getDbConnection(db_host, db_user, db_password, db_name):
    return MySQLdb.connect(db_host, db_user, db_password, db_name)
  
def closeConnection(conn):
    conn.close()
    
def get_store_account_client():
    global aclient
    if aclient is None:
        aclient = Client(hotspot_store_url, timeout=70)
    return aclient

def get_new_transaction():
    transaction = Transaction()
    transaction.createdOn = datetime.datetime.now()
    transaction.status = TransactionStatus.INIT
    transaction.status_message = "New transaction"
    return transaction

def create_order(t_order):
    order = Order()
    if t_order.warehouse_id:
        order.warehouse_id = t_order.warehouse_id
        order.fulfilmentWarehouseId = t_order.fulfilmentWarehouseId
    if t_order.logistics_provider_id:    
        order.logistics_provider_id = t_order.logistics_provider_id
        order.airwaybill_no = t_order.airwaybill_no
        order.tracking_id = t_order.tracking_id
    if t_order.expected_shipping_time:
        order.expected_shipping_time = to_py_date(t_order.expected_shipping_time).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
        order.promised_shipping_time = order.expected_shipping_time
    if t_order.expected_delivery_time:
        order.expected_delivery_time = to_py_date(t_order.expected_delivery_time)
        order.promised_delivery_time = order.expected_delivery_time
    if t_order.customer_id:    
        order.customer_id = t_order.customer_id
        order.customer_name = t_order.customer_name
        order.customer_city = t_order.customer_city
        order.customer_state = t_order.customer_state
        order.customer_mobilenumber = t_order.customer_mobilenumber
        order.customer_pincode = t_order.customer_pincode
        order.customer_address1 = t_order.customer_address1
        order.customer_address2 = t_order.customer_address2
        order.customer_email = t_order.customer_email
    #new status and status description to be added    
    order.status = t_order.status  
    order.statusDescription = t_order.statusDescription
    order.total_amount = t_order.total_amount
    order.gvAmount = t_order.gvAmount
    order.cod = t_order.cod
    order.total_weight = t_order.total_weight
    if t_order.created_timestamp:
        order.created_timestamp = to_py_date(t_order.created_timestamp)
    else:
        order.created_timestamp = datetime.datetime.now()
    order.pickupStoreId = t_order.pickupStoreId
    if t_order.insurer:
        order.insurer = t_order.insurer
        order.insuranceAmount = t_order.insuranceAmount
    if order.insurer > 0:
        insuranceDetail = InsuranceDetailForOrder()
        insuranceDetail.dob = t_order.dob
        insuranceDetail.guardianName = t_order.guardianName
        insuranceDetail.order = order
    if t_order.freebieItemId:
        order.freebieItemId = t_order.freebieItemId
    if OrderSource.MOBILESITE == t_order.source or OrderSource.WEBSITE == t_order.source:
        order.source =  OrderSource.WEBSITE
    else:
        order.source = t_order.source
    #Change this temporary
    if t_order.orderType is None or t_order.orderType ==OrderType.B2C:
        order.orderType = OrderType.B2C
    order.advanceAmount = t_order.advanceAmount
    order.storeId = t_order.storeId
    if t_order.productCondition:
        order.productCondition = t_order.productCondition
    if t_order.dataProtectionInsurer:
        order.dataProtectionInsurer = t_order.dataProtectionInsurer
        order.dataProtectionAmount = t_order.dataProtectionAmount
    if order.dataProtectionInsurer > 0:
        dataInsuranceDetail = DataInsuranceDetailForOrder()
        dataInsuranceDetail.order = order
    if t_order.taxType:
        order.taxType = t_order.taxType
    if t_order.shippingCost:
        order.shippingCost = t_order.shippingCost
    if t_order.codCharges:
        order.codCharges = t_order.codCharges
    order.wallet_amount = t_order.wallet_amount
    order.net_payable_amount = t_order.net_payable_amount
    return order
    
def create_transaction(t_transaction):
    t_orders = t_transaction.orders
    if not t_orders:
        raise TransactionServiceException(101, "Orders missing from the transaction")
    transaction = get_new_transaction()
    transaction.customer_id = t_transaction.customer_id 
    transaction.shopping_cart_id = t_transaction.shoppingCartid
    transaction.coupon_code = t_transaction.coupon_code
    transaction.session_source = t_transaction.sessionSource
    transaction.session_start_time = to_py_date(t_transaction.sessionStartTime)
    transaction.first_source = t_transaction.firstSource
    transaction.first_source_start_time = to_py_date(t_transaction.firstSourceTime)
    transaction.payment_option = t_transaction.payment_option
    if t_transaction.totalShippingCost:
        transaction.totalShippingCost = t_transaction.totalShippingCost
    if t_transaction.totalCodCharges:
        transaction.totalCodCharges = t_transaction.totalCodCharges
        
    user_client = UserClient().get_client()
    user_client.getCounterByUserId(t_transaction.customer_id)

    totalAmount = 0
    wallet_amount = 0.0
    for t_order in t_orders:
        order = create_order(t_order)
        totalAmount = totalAmount + order.total_amount
        wallet_amount = wallet_amount + order.wallet_amount
        order.transaction = transaction 
        for line_item in t_order.lineitems:
            litem = LineItem()
            litem.item_id = line_item.item_id
            litem.productGroup = line_item.productGroup
            litem.brand = line_item.brand
            litem.hsnCode = line_item.hsnCode
            if line_item.model_number:
                litem.model_number = line_item.model_number
            if line_item.model_name:    
                litem.model_name = line_item.model_name
            if line_item.color:
                litem.color = line_item.color
            if line_item.extra_info:
                litem.extra_info = line_item.extra_info
            litem.quantity = line_item.quantity
            litem.unit_price = line_item.unit_price
            if line_item.mrp:
                litem.mrp = line_item.mrp 
            litem.unit_weight = line_item.unit_weight
            litem.total_price = line_item.total_price
            litem.total_weight = line_item.total_weight
            try:
                litem.transfer_price = line_item.transfer_price
                litem.nlc = line_item.nlc
            except Exception as e:
                print "Exception while setting transfer price and nlc" + str(e.message)
                print sys.exc_info()[0]
            
            '''litem.transfer_price = line_item.transfer_price'''
            litem.dealText = line_item.dealText
            litem.warranty_expiry_timestamp = to_py_date(line_item.warrantry_expiry_timestamp)
            
            """
            if line_item.addedOn:
                litem.added_on = to_py_date(line_item.addedOn)
            else:
                litem.added_on = datetime.datetime.now()
            """
            litem.order = order
    session.commit()
    
    for order1 in Transaction.get_by(id=transaction.id).orders:
        if OrderSource.MOBILESITE == order.source or OrderSource.WEBSITE == order.source:
            attribute = Attribute()
            attribute.orderId = order1.id
            attribute.name = "OrderSource"
            if OrderSource.MOBILESITE == order1.source:
                attribute.value = "Mobile"
                order1.source =  OrderSource.WEBSITE
            else:
                attribute.value = "Desktop"
            session.commit()
    # EMI charges can not be charged by merchant as per RBI guidelines. 
    #Now we do not have any other charges, So commenting it out.
    #generateOtherCharges(transaction, t_transaction.emiSchemeId, totalAmount)
    
    
    """Update amount in wallet"""
    
    return transaction.id

def generateOtherCharges(transaction, emiSchemeId, totalAmount):
    emiScheme = EmiScheme.get_by(id=emiSchemeId)
    if not emiScheme:
        return
    
    if totalAmount < emiScheme.minAmount:
        return
    
    emiCharge = 0
    if emiScheme.chargeType == EmiChargeType.FIXED:
        emiCharge = ceil(emiScheme.chargeValue)
    if emiScheme.chargeType == EmiChargeType.PERCENTAGE:
        emiCharge = ceil(totalAmount*(emiScheme.chargeValue)/100)
    
    if transaction.coupon_code is not None and transaction.coupon_code != "":    
        try:
            promotion_client = PromotionClient().get_client()
            emis = promotion_client.getEmiDiscount(transaction.shopping_cart_id)
            if emis.has_key(emiSchemeId):
                discount = emis.get(emiSchemeId)
                if emiCharge > discount:
                    emiCharge -= discount
                else:
                    emiCharge = 0
        except:
            print "Could not get emi discounts from promotion"
    
    miscCharge = MiscCharges()
    miscCharge.transaction = transaction
    miscCharge.chargeType = 1 ##EMI charges
    miscCharge.chargeAmount = emiCharge
    session.commit()
    
def get_transaction(transaction_id):
    transaction = Transaction.get_by(id=transaction_id)
    if not transaction:
        raise TransactionServiceException(108, "no such transaction")
    
    return transaction

def get_transactions_for_customer(customer_id, from_date, to_date, status):
    if not customer_id:
        raise TransactionServiceException(101, "bad customer id")
    query = Transaction.query.filter(Transaction.customer_id == customer_id)
    if status is not None:
        query = query.filter(Transaction.status == status)
    if from_date:
        query = query.filter(Transaction.createdOn >from_date)
    if to_date:
        query = query.filter(Transaction.createdOn < to_date)
    return query.all()

def get_transactions_for_shopping_cart_id(shopping_cart_id):
    transactions = Transaction.query.filter_by(shopping_cart_id=shopping_cart_id).all()
    return transactions

def get_transaction_status(transaction_id):
    if not transaction_id:
        raise TransactionServiceException(101, "bad transaction id")
    
    transaction = get_transaction(transaction_id)
    
    return transaction.status

def change_transaction_status(transaction_id, new_status, description, pickUp, orderType, source):
    logging.info("########change transaction status called" + str(transaction_id))
    transaction = get_transaction(transaction_id)
    
    if new_status in (TransactionStatus.AUTHORIZED, TransactionStatus.FLAGGED, TransactionStatus.COD_IN_PROCESS):
        wallet_amount_used = 0.0
        for order in transaction.orders:
            wallet_amount_used = wallet_amount_used + order.wallet_amount
        if wallet_amount_used > 0:
            user_wallet = get_user_wallet(transaction.orders[0].customer_id)
            if user_wallet and user_wallet.amount < wallet_amount_used:
                if new_status == TransactionStatus.AUTHORIZED:
                    try:
                        captured_amount = __capture_txn(transaction_id)
                    except:
                        #TODO:
                        ##add_amount_in_wallet(transaction.customer_id, captured_amount, transaction_id, WalletReferenceType.PURCHASE, False, "Amount added against failed Purchase", commit_session=True)
                        pass
                return False
            payment_client = PaymentClient().get_client()
            payments = payment_client.getPaymentForTxnId(order.transaction_id)
            wallet_payment_exist = False
            for payment in payments:
                if payment.gatewayId == walletGatewayId:
                    wallet_payment_exist = True
                    logging.info("########Updating wallet")
                    payment_client.updatePaymentDetails(payment.paymentId, "", "", "SUCCESS", "Payment Received", "", "", "", "", PaymentStatus.SUCCESS, "", None);
            if not wallet_payment_exist:
                return False
            consume_wallet(transaction.orders[0].customer_id, wallet_amount_used, transaction_id, WalletReferenceType.PURCHASE, "Against Order purchase")
    
    
    transaction.status = new_status
    transaction.status_message = description
    expectedDelayMap = {}
    billingWarehouseIdMap = {}
    billingWarehouseTxnMap = {}
    
    
    
    if new_status == TransactionStatus.FAILED:
        for order in transaction.orders:
            order.status = OrderStatus.PAYMENT_FAILED
            order.statusDescription = "Payment Failed"
    elif new_status == TransactionStatus.AUTHORIZED or new_status == TransactionStatus.FLAGGED:                  
        user_client = UserClient().get_client()
        catalog_client = CatalogClient().get_client()
        
        pdu = user_client.getPrivateDealUser(transaction.orders[0].customer_id)
        print "pdu.isFofo----", pdu.isFofo
        fofoWarehousesMap = {} 
        if pdu.isFofo:
            fofoDealMap = catalog_client.getAllFofoDeals([order.lineitems[0].item_id], [4,7])
            if fofoDealMap:
                itemIds = []
                for order in transaction.orders:
                    itemIds.append(order.lineitems[0].item_id)
                inventory_client = InventoryClient().get_client()
                print "calling getFofoFulFillmentWarehouseMap" 
                fofoWarehousesMap = inventory_client.getFofoFulFillmentWarehouseMap(itemIds)
                print "called getFofoFulFillmentWarehouseMap" 
        

        expectedDelayMap = {}
        billingWarehouseIdMap = {}
        billingWarehouseTxnMap = {}
        subOrderAmountsMap = {}       
        for order in transaction.orders:
            if new_status == TransactionStatus.AUTHORIZED:
                order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
                order.statusDescription = "Submitted to warehouse"
            elif new_status == TransactionStatus.FLAGGED:
                order.status = OrderStatus.PAYMENT_FLAGGED
                order.statusDescription = "Payment flagged by gateway"
            order.cod = False
            #Order type cant be changed during authorization
            #if orderType is not None:
            #    order.orderType = orderType
            #After we got payment success, we will set logistics info also 
            logistics_client = LogisticsClient().get_client()
            #FIXME line item is only one now. If multiple will come, need to fix.
            item_id = order.lineitems[0].item_id
            if pickUp == PickUpType.RUNNER or pickUp == PickUpType.SELF:
                current_time = datetime.datetime.now()
                if pdu.isFofo:
                    stateMaster = fetchStateMaster()
                    stateId = -1
                    for k, v in stateMaster.iteritems():
                        if v.stateName == order.customer_state:
                            stateId = k
                            break
                    logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.PREPAID, pickUp, stateId)
                else:
                    logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.PREPAID, pickUp, -1)
                order.logistics_provider_id = logistics_info.providerId
                
                order.warehouse_id = logistics_info.warehouseId
                order.fulfilmentWarehouseId = logistics_info.fulfilmentWarehouseId
                
                logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
                logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
                logistics_info.deliveryDelay = adjust_delivery_time(current_time, (logistics_info.shippingTime+logistics_info.deliveryDelay))
                
                order.courier_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryDelay)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
                order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
                order.promised_shipping_time = order.expected_shipping_time
                order.expected_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
                order.promised_delivery_time = order.expected_delivery_time
                order.otg = False
                
                    
                inventory_client = InventoryClient().get_client()
                    
                if order.productCondition != ProductCondition.BAD:
                    inventory_client.reserveItemInWarehouse(item_id, order.fulfilmentWarehouseId, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), order.lineitems[0].quantity)

            elif order.source == OrderSource.WEBSITE:
                try:
                    #Get the id and location of actual warehouse that'll be used to fulfil this order.
                    inventory_client = InventoryClient().get_client()
                    fulfilmentWhId, expected_delay, billingWarehouseId, sellingPrice, totalAvailability, weight = inventory_client.getItemAvailabilityAtLocation(item_id, 1, -1)
                except Exception as ex:
                    raise TransactionServiceException(103, "Unable to fetch inventory information about this item.")
                
                expectedDelayMap[item_id] = expected_delay
                if billingWarehouseIdMap.has_key(billingWarehouseId):
                    ordersList = billingWarehouseIdMap.get(billingWarehouseId)
                    ordersList.append(order)
                    billingWarehouseIdMap[billingWarehouseId] = ordersList
                else:
                    ordersList = []
                    ordersList.append(order)
                    billingWarehouseIdMap[billingWarehouseId] = ordersList
                    
                if billingWarehouseTxnMap.has_key(billingWarehouseId):
                    transactionAmount , transactionWeight = billingWarehouseTxnMap.get(billingWarehouseId)
                    transactionAmount = transactionAmount+order.total_amount
                    transactionWeight = transactionWeight+order.total_weight
                    billingWarehouseTxnMap[billingWarehouseId] = transactionAmount , transactionWeight
                else:
                    billingWarehouseTxnMap[billingWarehouseId] = order.total_amount , order.total_weight
                    
                subOrderAmountsMap[long(order.lineitems[0].unit_price)] = order
                    
                order.fulfilmentWarehouseId = fulfilmentWhId
                order.warehouse_id = billingWarehouseId
                
                    
                if order.productCondition != ProductCondition.BAD:
                    inventory_client.reserveItemInWarehouse(item_id, order.fulfilmentWarehouseId, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), order.lineitems[0].quantity)
        
            try:
                item_pricing = inventory_client.getItemPricing(item_id, -1)
                order.lineitems[0].transfer_price = item_pricing.transferPrice
                order.lineitems[0].nlc = item_pricing.nlc
            except:
                print "Not able to get transfer price. Skipping"
    
    session.commit()
    return True

def __create_recharge_voucher_tracker(order, amount, voucherType):
    rvt = RechargeVoucherTracker()
    rvt.order = order
    rvt.amount = amount
    rvt.voucherIssued = False
    rvt.voucherType = voucherType
            
def __get_holidays(start_time=0L, to_time=-1L):
    '''
    Get the list of all public holidays since start time including the
    day of start time if it's a holiday
    '''
    midnightTime = start_time.replace(hour=0, minute=0, second=0, microsecond=0)
    fromDate = to_java_date(midnightTime)
    logistics_client = LogisticsClient().get_client()
    #TODO: This list should be cached after the first call.
    #Calling logistics server is also unnecessary.
    holidays = logistics_client.getHolidays(fromDate, to_time)
    holidays = [to_py_date(holiday).date() for holiday in holidays]
    return holidays

def adjust_delivery_time(start_time, delivery_days):
    '''
    Returns the actual no. of days which will pass while 'delivery_days'
    no. of business days will pass since 'order_time'. 
    '''
    start_date = start_time.date()
    end_date = start_date + datetime.timedelta(days = delivery_days)
    holidays = __get_holidays(start_time)
    
    while start_date <= end_date:
        if start_date.weekday() == 6 or start_date in holidays:
            delivery_days = delivery_days + 1
            end_date = end_date + datetime.timedelta(days = 1)
        start_date = start_date + datetime.timedelta(days = 1)
    
    return delivery_days


def adjust_shipping_time(delivery_date, delay):
    shipping_date=delivery_date - datetime.timedelta(days = delay)
    holidays = __get_holidays(shipping_date)
    while(True):
        if shipping_date.weekday() == 6 or shipping_date in holidays:
            shipping_date = shipping_date - datetime.timedelta(days = 1)
        else:
            return shipping_date        
    
    

def get_business_days_count(start_time, end_time):
    '''
    Returns business dates between two dates
    '''
    start_date = start_time.date()
    end_date  = end_time.date()
    diff = (end_date - start_date).days
    sundays = diff/7 + 1 if (start_date.weekday() + (diff%7) > 6) else diff/7 
    '''holidays = __get_holidays(start_time, end_time)'''
    logistics_client = LogisticsClient().get_client()
    #TODO: This list should be cached after the first call.
    #Calling logistics server is also unnecessary.
    holidays = logistics_client.getHolidays(to_java_date(start_time), to_java_date(end_time))
    non_business_days = len(holidays)
    for holiday in holidays:
        pyholiday = to_py_date(holiday).date()
        if pyholiday.weekday() == 6:
            sundays = sundays - 1
    return diff - (non_business_days + sundays)
            

def enqueue_transaction_info_email(transaction_id):
    transaction = get_transaction(transaction_id)
    
    #Dear Customer, thanks for placing order of Spice Stellar Mi-535 Black and 5 other items amounting Rs. 25000. Check your email for details.
    
    smsText = "Dear Customer, Thanks for placing order of "
    mobileNo = None
    customerId = 0
    sendSms = True
    transactionAmount = 0.0
    orderQuantity = 0 
    totalQuantity = 0
    itemQuantityMap = {}
    itemNameMap = {}  
    oneProduct = ""  
    
    subject = source_name + " - Order Details"
    orders = transaction.orders
    advance_amount = orders[0].advanceAmount
    saddress = ""
    
    for order in orders:
        if order.pickupStoreId:
            sendSms= False
        transactionAmount = transactionAmount + order.total_amount + order.shippingCost
        for lineitem in order.lineitems:
            if itemQuantityMap.has_key(lineitem.item_id):
                quantity = itemQuantityMap.get(lineitem.item_id)
                quantity = quantity + lineitem.quantity
                itemQuantityMap[lineitem.item_id] = quantity
            else:
                itemQuantityMap[lineitem.item_id] = lineitem.quantity
            
            if not itemNameMap.has_key(lineitem.item_id):
                itemNameMap[lineitem.item_id] = str(lineitem)
    
    if len(itemNameMap) == 1:
        for itemId, itemName in itemNameMap.iteritems():
            oneProduct = itemName
            orderQuantity = itemQuantityMap.get(itemId)
        if len(oneProduct) > 30:
            oneProduct = oneProduct[0:28]+"..."
        smsText = smsText + oneProduct +". Total "+ str(long(orderQuantity)) +"pc(s), amount Rs " + str(long(transactionAmount))
    
    if len(itemNameMap) >1: 
        for itemId, itemName in itemNameMap.iteritems():
            oneProduct = itemName
            totalQuantity = totalQuantity + itemQuantityMap.get(lineitem.item_id)
            itemQuantityMap.pop(lineitem.item_id)
            break
        for itemId, quantity in itemQuantityMap.iteritems():
            orderQuantity = orderQuantity + quantity
            totalQuantity = totalQuantity + quantity
            
        if len(oneProduct) > 30:
            oneProduct = oneProduct[0:28]+"..."
        
        smsText = smsText + oneProduct +" and "+str(long(orderQuantity))+ " other item(s). Total " + str(long(totalQuantity)) +"pcs, amount Rs "+ str(long(transactionAmount))
    
    smsText = smsText + ". Check your email for details." 
        
    bcc = []
    if advance_amount  > 0:
        store = get_hotspot_store(orders[0].storeId, "")
        bcc = bcc + store.approvalEmail.split(';')  + [store.email,  "amit.gupta@shop2020.in", "rajneesh.arora@shop2020.in", "amit.sirohi@shop2020.in"]
        saddress += " at our store " + __get_hotspot_store_address_html(store)
    otgLink = ""
    inOrderOtg = "<div>On Time Guarantee<br><span style=color:red>We pay if we delay</span></div>"
    if len(transaction.orders) == 1:
        inOrderOtg = ""
        if transaction.orders[0].otg :
            subject = "Your Order is confirmed with On Time Guarantee We Pay if we Delay"
            otgLink = ' Your Order is covered under the <a href="http://www.saholic.com/static/on-time-guarantee">On Time Guarantee, We pay If we Delay.</a>'
    
    customer_name = "" 
    html_header = """
<html>
<body>
<div>
    <p>
        Hello $customer_name,<br /><br />
        Thanks for placing order with us $saddress .$otgLink Following are the details of your order:
    </p>
    <div>
        Order Date: $order_date
    </div>
    <div>
        <table>
        <tr><td colspan="6"><hr /></td></tr>
        <tr><td colspan="6" align="left"><b>Order Details</b></td></tr>
        <tr><td colspan="6"><hr /></td></tr>
        <tr>
            <th width="100">Order No.</th>
            <th>Product</th>
            <th width="100">Estimated Delivery Date</th>
            <th width="100">Quantity</th>
            <th width="100">Unit Price</th>
            <th width="100">Amount</th>
        </tr>
    """
    user_email = ""
    total_amount = 0.0
    total_shipping = 0.0 
    html_table = ""
    order_date = datetime.datetime.now()

    html_pickup_text = ""
    random_text = ""
    if orders[0].pickupStoreId:
        random_text = str(randrange(100000, 999999)) 
    
    first = True    
    for order in orders:
        mobileNo = order.customer_mobilenumber
        customerId = order.customer_id
        
        customer_name = order.customer_name
        order_date = order.created_timestamp
        user_email = order.customer_email
        if first:
            first = False
            html_tr = "" 
        else:
            html_tr =  "<tr><td colspan=6>&nbsp;</td></tr>"
        html_tr += "<tr><td align='center'>" + str(order.id) + "</td>"

        sdt = datetime.datetime.strptime(str(orders[0].expected_delivery_time), "%Y-%m-%d %H:%M:%S")
        store_delivery_date = sdt.strftime("%d %B %Y")
        
        total_amount += order.shippingCost
        total_shipping += order.shippingCost
        if order.pickupStoreId:
            attribute = Attribute()
            attribute.orderId = order.id
            attribute.name = "SECRET_CODE"
            attribute.value = random_text
            session.commit()
        
        for lineitem in order.lineitems:
            lineitem_total_price = round(lineitem.total_price, 2)
            
            html_tr += "<td>" + str(lineitem) + "</td>"
            if order.otg:
                html_tr += "<td align='center'><div>" + store_delivery_date + "</div>" + inOrderOtg + "</td>"
            else:
                html_tr += "<td align='center'>" + store_delivery_date + "</td>"
            html_tr += "<td align='center'>" + ("%.0f" % lineitem.quantity) + "</td>"
            html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem.unit_price) + "</td>"
            html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem_total_price) + "</td></tr>"
            total_amount += lineitem_total_price
            html_table += html_tr
        if order.freebieItemId:
            catalog_client = CatalogClient().get_client()
            item = catalog_client.getItem(order.freebieItemId)
            html_table += "<tr><td align='center'></td><td style='font-style:italic;' colspan='5'> Free Item: "+item.brand + " " + item.modelName + " " + item.modelNumber+"</td></tr>"
        if order.insurer:
            html_table += "<tr><td colspan=6>&nbsp;</td></tr>"
            html_table += "<tr><td align='center'></td><td style='font-style:italic;'>Insured Against Theft for 1 Year. <a href='http://www.saholic.com/static/insurance-terms'>Know More</a></td><td align='center'></td><td align='center'></td><td align='center'></td>"
            html_table += "<td align='center'>" + str(order.insuranceAmount) + "</td></tr>"
            total_amount += order.insuranceAmount
            
    emi_amount = 0
    miscCharges = get_misc_charges(transaction_id)
    if miscCharges.get(1):
        emi_amount = miscCharges.get(1)
        
    html_footer = """
        <tr><td colspan=6>&nbsp;</td></tr>
        <tr><td colspan=6><hr /></td></tr>
        """
    if emi_amount > 0:
        total_amount += emi_amount
        html_footer = html_footer + "<tr>\
                        <td colspan=5>EMI Processing Charges</td>\
                        <td> Rs. $emi_amount</td>\
                    </tr>"
    
    amount_string = "Total Amount"
    if advance_amount > 0:
        amount_string = "Balance Amount"
        total_amount -= advance_amount
        html_footer = html_footer + "<tr>\
                        <td colspan=5>Advance Paid</td>\
                        <td> Rs. $advance_amount</td>\
                    </tr>"
    shipping_string = "Shipping Charges"
    if total_shipping > 0:
        html_footer = html_footer + "<tr>\
                            <td colspan=5>$shipping_string</td>\
                            <td> Rs. $total_shipping</td>\
                        </tr>"            
    html_footer = html_footer + "<tr>\
                <td colspan=5>$amount_string</td>\
                <td> Rs. $total_amount</td>\
                </tr>\
            </table>\
        </div>"
    
    #Commenting out fog message
    #html_footer = html_footer + "Note:- With the onset of dense fog in North India, it severely hits the operations of our courier partners. We would like to mention that our deliveries might be slightly delayed. We look forward to your support and understanding with regards to anticipated delays.<br>"
    
    store_address = ""
    if orders[0].pickupStoreId:
        store = LogisticsClient().get_client().getPickupStore(orders[0].pickupStoreId)
        store_address = __get_store_address_html(store)

        html_pickup_text = html_pickup_text + """
        <h3>Steps to Pickup In Store</h3>
        1) You will receive a confirmation email from us when your product(s) reaches the store. Once you receive the same, please visit the following store selected by you.<br>
        <br><i>$store_address</i><br>
        """

        if orders[0].cod:
            html_pickup_text = html_pickup_text + """
            2) Pay Rs. $net_amount, present a photo copy of valid Photo Identity Proof* and use your Secret Code <b><i>$secret_code</i></b> to claim your Product(s).<br><br>
            *Valid Photo Identity Proof are Driving License, Passport, PAN Card, Voter ID, Company Issued ID Card, College issued Student ID Card Or any other Government approved Photo Id.<br>
            <br>
            In case you have any questions please visit <a href="$source_url/static/buy-online-and-pickup-in-store">Details on Pickup in Store</a>. If you still have more questions feel free to contact us 24/7 via <a href="$source_url/contact-us">$source_url/contact-us</a>
            </br><hr />
            """
        else:
            html_pickup_text = html_pickup_text + """
            2) Present a photo copy of valid Photo Identity Proof* and use your Secret Code <b><i>$secret_code</i></b> to claim your Product(s).<br><br>
            *Valid Photo Identity Proof are Driving License, Passport, PAN Card, Voter ID, Company Issued ID Card, College issued Student ID Card Or any other Government approved Photo Id.<br>
            <br>
            In case you have any questions please visit <a href="$source_url/static/buy-online-and-pickup-in-store">Details on Pickup in Store</a>. If you still have more questions feel free to contact us 24/7 via <a href="$source_url/contact-us">$source_url/contact-us</a>
            </br><hr />
            """
            
    html_store_cust_address = ""
    if advance_amount > 0:
        html_store_cust_address += "Your order shall be delivered at " + __get_order_address_html(orders[0]) 
    html_footer = html_footer + html_pickup_text + html_store_cust_address + """
    <p>
        Best Wishes,<br />
        $source_name Team
    </p>
</div>
</body>
</html>
    """

    dt = datetime.datetime.strptime(str(order_date), "%Y-%m-%d %H:%M:%S")
    formated_order_date = dt.strftime("%A, %d. %B %Y %I:%M%p")

    sdt = datetime.datetime.strptime(str(orders[0].expected_delivery_time), "%Y-%m-%d %H:%M:%S")
    store_delivery_date = sdt.strftime("%A, %d %B %Y")

    email_header = Template(html_header).substitute(dict(order_date = formated_order_date, customer_name = customer_name, source_url = source_url, otgLink = otgLink, saddress = saddress))
    email_footer = Template(html_footer).substitute(dict(total_amount = "%.2f" % total_amount, emi_amount = "%.2f" % emi_amount, advance_amount = "%.2f" % advance_amount, net_amount = "%.2f" % (total_amount-order.gvAmount), secret_code = random_text, store_address = store_address, store_delivery_date = store_delivery_date, source_url = source_url, source_name = source_name, amount_string = amount_string, shipping_string = shipping_string, total_shipping = "%.2f" % total_shipping))

    try:
        helper_client = HelperClient().get_client()
        helper_client.saveUserEmailForSending([user_email], SaholicHelpEmailId, subject, email_header + html_table + email_footer, str(transaction_id), "TransactionInfo", [], bcc, orders[0].source)
        
        if sendSms:
            send_transaction_sms(customerId, mobileNo, smsText, SmsType.TRANSACTIONAL, False)
        
        return True
    except Exception as e:
        print e
        return False
    
def send_transaction_sms(customerId, mobileNo, smsText, smsType, useKey):
    if useKey:
        if mobileNo is not None:
            helper_client = HelperClient(host_key = "helper_service_server_host_prod").get_client()
            helper_client.saveUserSmsForSending(customerId, mobileNo, smsText, SmsType.TRANSACTIONAL)
            userSmsInfo = helper_client.getUserSmsInfo(customerId);
            if customerId == userSmsInfo.userId and mobileNo == userSmsInfo.mobileNo:
                userSmsInfo.dailyCount = userSmsInfo.dailyCount +1
                userSmsInfo.weeklyCount = userSmsInfo.weeklyCount +1
                helper_client.updateUserSmsInfo(userSmsInfo)
            elif customerId == userSmsInfo.userId and mobileNo != userSmsInfo.mobileNo:
                userSmsInfo.updateTimestamp = to_java_date(datetime.datetime.now())
                userSmsInfo.mobileNo = mobileNo
                userSmsInfo.dailyCount = 1
                userSmsInfo.weeklyCount = 1
                helper_client.updateUserSmsInfo(userSmsInfo)
            else:
                userSmsInfo = UserSmsInfo()
                userSmsInfo.userId = customerId
                userSmsInfo.mobileNo = mobileNo
                userSmsInfo.createdTimestamp = to_java_date(datetime.datetime.now())
                userSmsInfo.updateTimestamp = to_java_date(datetime.datetime.now())
                helper_client.addUserSmsInfo(userSmsInfo);
    else:
        if mobileNo is not None:
            helper_client = HelperClient().get_client()
            helper_client.saveUserSmsForSending(customerId, mobileNo, smsText, SmsType.TRANSACTIONAL)
            userSmsInfo = helper_client.getUserSmsInfo(customerId);
            if customerId == userSmsInfo.userId and mobileNo == userSmsInfo.mobileNo:
                userSmsInfo.dailyCount = userSmsInfo.dailyCount +1
                userSmsInfo.weeklyCount = userSmsInfo.weeklyCount +1
                helper_client.updateUserSmsInfo(userSmsInfo)
            elif customerId == userSmsInfo.userId and mobileNo != userSmsInfo.mobileNo:
                userSmsInfo.updateTimestamp = to_java_date(datetime.datetime.now())
                userSmsInfo.mobileNo = mobileNo
                userSmsInfo.dailyCount = 1
                userSmsInfo.weeklyCount = 1
                helper_client.updateUserSmsInfo(userSmsInfo)
            else:
                userSmsInfo = UserSmsInfo()
                userSmsInfo.userId = customerId
                userSmsInfo.mobileNo = mobileNo
                userSmsInfo.createdTimestamp = to_java_date(datetime.datetime.now())
                userSmsInfo.updateTimestamp = to_java_date(datetime.datetime.now())
                helper_client.addUserSmsInfo(userSmsInfo);

def enqueue_received_at_store_email(order):
    html_header = """
    <html>
    <body>
    <div>
        <p>
            Hello $customer_name,<br /><br />
            Your Product has reached the Store selected by you and is available for pickup. Please pick up your product on or before $order_expiry_date. Following are the details of your order and steps to pickup the same:
        </p>
        <div>
            Order Date: $order_date
        </div>
        <div>
            <table>
            <tr><td colspan="5"><hr /></td></tr>
            <tr><td colspan="5" align="left"><b>Order Details</b></td></tr>
            <tr><td colspan="5"><hr /></td></tr>
            <tr>
                <th width="100">Order No.</th>
                <th>Product</th>
                <th width="100">Quantity</th>
                <th width="100">Unit Price</th>
                <th width="100">Amount</th>
            </tr>
    """
    total_amount = 0.0
    html_table = ""

    html_pickup_text = ""
    random_text = __get_secret_code(order.id)

    customer_name = order.customer_name
    order_date = order.created_timestamp
    user_email = order.customer_email
    html_tr = "<tr><td align='center'>" + str(order.id) + "</td>"
    
    for lineitem in order.lineitems:
        lineitem_total_price = round(lineitem.total_price, 2)
        
        html_tr += "<td>" + str(lineitem) + "</td>"
        html_tr += "<td align='center'>" + ("%.0f" % lineitem.quantity) + "</td>"
        html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem.unit_price) + "</td>"
        html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem_total_price) + "</td></tr>"
        total_amount += lineitem_total_price
        html_table += html_tr
    
    html_footer = """
        <tr><td colspan=5>&nbsp;</td></tr>
        <tr><td colspan=5><hr /></td></tr>
        <tr>
            <td colspan=4>Total Amount</td>
            <td> Rs. $total_amount</td>
        </tr>
        </table>
    </div>
    """
    
    store_address = ""
    if order.pickupStoreId:
        store = LogisticsClient().get_client().getPickupStore(order.pickupStoreId)
        store_address = __get_store_address_html(store)

        html_pickup_text = html_pickup_text + """
        <h3>Steps to Pickup In Store</h3>
        1) Please visit the following store selected by you on or before $order_expiry_date<br>
        <br><i>$store_address</i><br>
        """

        if order.cod:
            html_pickup_text = html_pickup_text + """
            2) Pay Rs. $net_amount, present a valid Photo Identity Proof* and quote your Secret Code <b><i>$secret_code</i></b> to claim your Product.<br><br>
            *Valid Photo Identity Proof are Driving License, Passport, PAN Card, Voter ID, Company Issued ID Card, College issued Student ID Card Or any other Government approved Photo Id.<br>
            <br>
            In case you have any questions please visit <a href="$source_url/static/buy-online-and-pickup-in-store">Details on Pickup in Store</a>. If you still have more questions feel free to contact us 24/7 via <a href="$source_url/contact-us">$source_url/contact-us</a>
            </br><hr />
            """
        else:
            html_pickup_text = html_pickup_text + """
            2) Present a valid Photo Identity Proof* and use your Secret Code <b><i>$secret_code</i></b> to claim your Product(s).<br><br>
            *Valid Photo Identity Proof are Driving License, Passport, PAN Card, Voter ID, Company Issued ID Card, College issued Student ID Card Or any other Government approved Photo Id.<br>
            <br>
            In case you have any questions please visit <a href="$source_url/static/buy-online-and-pickup-in-store">Details on Pickup in Store</a>. If you still have more questions feel free to contact us 24/7 via <a href="$source_url/contact-us">$source_url/contact-us</a>
            </br><hr />
            """
            
        html_footer = html_footer + html_pickup_text + """
        <p>
            Best Wishes,<br />
            $source_name Team
        </p>
        </div>
        </body>
        </html>
        """

    subject = "Your order for " + str(order.lineitems[0]) + " is ready for Pickup in Store"
    dt = datetime.datetime.strptime(str(order_date), "%Y-%m-%d %H:%M:%S")
    formated_order_date = dt.strftime("%A, %d. %B %Y %I:%M%p")

    oetd = datetime.datetime.now() + datetime.timedelta(days=4)
    formated_order_expiry_date = oetd.strftime("%A, %d %B %Y")
    

    email_header = Template(html_header).substitute(dict(order_date = formated_order_date, customer_name = customer_name, order_expiry_date = formated_order_expiry_date, source_url = source_url, source_name = source_name))
    email_footer = Template(html_footer).substitute(dict(total_amount = "%.2f" % total_amount, net_amount = "%.2f" % (total_amount-order.netAmount), secret_code = random_text, store_address = store_address, order_expiry_date = formated_order_expiry_date, source_url = source_url, source_name = source_name))
    
    try:
        helper_client = HelperClient(host_key = "helper_service_server_host_prod").get_client()
        helper_client.saveUserEmailForSending([user_email], SaholicHelpEmailId, subject, email_header + html_table + email_footer, str(order.id), "ReceivedAtStore", [], [], order.source)
        return True
    except Exception as e:
        print e
        return False

def enqueue_delivery_success_mail(logisticsTxnId, orderList):
    
    order = orderList[0]
    user_client = UserClient().get_client()
    if user_client.getPrivateDealUser(order.customer_id).isFofo:
        return True
    html_header = """
    <html>
    <body>
    <div>
        <p>
            Dear $customer_name,<br /><br />
            </p>""" 
            
    html= """
            <div>
                Order Date: $order_date <br>
                $pickup_text
            </div>
            <div>
                <table>
                <tr><td colspan="8"><hr /></td></tr>
            <tr><td colspan="8" align="left"><b>Order Details</b></td></tr>
            <tr><td colspan="8"><hr /></td></tr>
            <tr>
                <th width="100">Sub Order Id</th>
                <th>Product Name</th>
                <th width="100">Quantity</th>
                <th width="100">Unit Price</th>
                <th width="100">Amount</th>
                <th width="100">Insurance Amount</th>
                <th width="100">OTG Covered</th>
                <th width="100">Freebie Item</th>
            </tr>"""
            
    total_amount = 0.0
    total_shipping = 0.0 
    advanceAmount = 0.0
    hasOtg = False
    otgCount = 0
    hasFreebie = False
    hasSplitOrder = False
    hasInsurer = False
    hasPickupStoreOrder = False
    splitOrdersMap = {}
    totalUnitPrice = 0.0
    for orderObj in orderList:
        lineitem = orderObj.lineitems[0]
        lineitem_total_price = round(lineitem.total_price, 2)
        total_amount += lineitem_total_price + orderObj.shippingCost
        advanceAmount += orderObj.advanceAmount 
        total_shipping += orderObj.shippingCost
        html += """
            <tr>
            <td align="center">"""+str(orderObj.id)+"""</td>
            <td>"""+str(lineitem)+"""</td>
            <td align="center">"""+("%.0f" % lineitem.quantity)+"""</td>
            <td align="center">"""+("%.2f" % lineitem.unit_price)+"""</td>
            <td align="center">"""+("%.2f" % lineitem_total_price)+"""</td>"""
        if orderObj.insurer > 0:
            hasInsurer = True
            total_amount += orderObj.insuranceAmount
            html += """<td align="center">"""+("%.2f" % orderObj.insuranceAmount) +"""</td>"""
        else:
            html += """<td align="center">0.00</td>"""
        if orderObj.otg:
            hasOtg = True
            otgCount += 1
            totalUnitPrice += lineitem.unit_price
            html += """<td align="center">Yes</td>"""
        else:
            html += """<td align="center">No</td>"""
        if orderObj.freebieItemId:
            hasFreebie = True
            catalog_client = CatalogClient().get_client()
            item = catalog_client.getItem(orderObj.freebieItemId)
            html += """<td align="center">"""+item.brand+" "+item.modelName+ " " + item.modelNumber +"""</td>"""
        else:
            html += """<td align="center"></td>"""
            freebieOrderId = get_order_attribute_value(orderObj.id, "freebieOrderId")
            if freebieOrderId != "":
                freebieOrder = get_order(freebieOrderId)
                hasSplitOrder = True
                splitOrdersMap[orderObj.id] = freebieOrder
        if orderObj.pickupStoreId:
            hasPickupStoreOrder = True
        
        html += """
                </tr>
            """
    html += """
        <tr><td colspan=8>&nbsp;</td></tr>
        <tr><td colspan=8><hr /></td></tr>"""
    amount_string = "Total Amount"
    if advanceAmount > 0:
        html += """<tr>
                   <td colspan="7">Advance Amount</td>
                   <td>Rs."""+("%.2f" % advanceAmount)+"""
                   </tr>
            """
        total_amount -= advanceAmount
        amount_string = "Balance Amount"
    
    shipping_string = "Shipping Charges"
    if total_shipping > 0:
        html = html + """<tr>\
                            <td colspan=7>"""+shipping_string+"""</td>\
                            <td> Rs. """+("%.2f" % total_shipping)+"""</td>\
                        </tr>"""
    
    html += """<tr>
               <td colspan="7">"""+amount_string+"""</td>
               <td>Rs."""+("%.2f" % total_amount)+"""
               </tr>
            """
            
    html += """
            </table>
            </div>
            $freebie_text
            $insurance_text
            </div>
            <p>
                Best Wishes,<br />
                $source_name Team
            </p>
            </div>
            </body>
            </html>
    """
    pickup_text =" "
    if hasPickupStoreOrder:
        pickup_text = "Pickup Date: " + order.delivery_timestamp.strftime("%A, %d. %B %Y %I:%M%p")
        thank_you_html = """
        Thank you for shopping with us and picking up your order in Store. 
    
        Following are the details of your order $master_order_id for which the pickup has happened. <br> If you have not picked up this product please <a href="$source_url/contact-us" target="_blank">contact us</a> immediately.<br>
        """
        #If you are happy with our services please join us on <a href="https://twitter.com/saholic">Twitter</a>, <a href="http://www.facebook.com/mysaholic">Facebook Page</a> and help us spread the word.<br/>
    else:
        if hasOtg:
            delayed = False
            fda = False
            
            if order.first_dlvyatmp_timestamp.date() < order.delivery_timestamp.date():
                fda = True
            
            fda_datetime = order.first_dlvyatmp_timestamp            
            fda_date = fda_datetime.date()
            
            if order.promised_delivery_time.date() < fda_date:
                delayed = True

            if not delayed and not fda:
                thank_you_html = """We are pleased to inform that the following items in your order $master_order_id have been delivered.<br/>As Promised order placed by you has been <b>Delivered On Time.</b><br/>
                                If you are happy with our services please join us on <a href="https://twitter.com/saholic">Twitter</a>, <a href="http://www.facebook.com/mysaholic">Facebook Page</a> and help us spread the word.<br/>
                                Delivered Item Details are:
                                """ 
            elif delayed:
                max_discount = 500*otgCount
                delayedDays = max(1,get_business_days_count(order.promised_delivery_time, fda_datetime))
                max_discount = min(max_discount, totalUnitPrice/10)
                discount = min(round(max_discount),delayedDays*50*otgCount)
                promotion_client = PromotionClient().get_client()
                endOn =  order.delivery_timestamp + datetime.timedelta(days = 30)
                expiry = endOn.strftime("%A, %d. %B %Y")
                endOn = to_java_date(endOn)
                arguments = "{\"endOn\":" + str(endOn) + ", \"emails\":[\"" + order.customer_email + "\"], \"couponType\":\"both\", \"usage_limit_for_user\":1, \"isCod\":False, \"discount\":" + str(discount) + "}"
                otgCoupon = promotion_client.createCoupon(28,CouponCategory.CUSTOMER_SATISFACTION , '', arguments, False, "otg")
                thank_you_html = """Following items in your order $master_order_id have been delivered. We apologise for a delay of """+str(delayedDays)+""" days<br/>
                                As per our On Time Guarantee Please accept a Gift Voucher worth Rs. """+str(discount)+""" as a token of our apology.
                                Your unique Gift Voucher Code is """+str(otgCoupon)+""" Gift Voucher Expiry Date: """+str(expiry)+"""<br/>
                                You can use the same to buy any Mobile, Camera, Laptop, Tablet, Accessory or Mobile/DTH Recharge from our website.</br/>
                                If you are happy with our services please join us on <a href="https://twitter.com/saholic">Twitter</a>, <a href="http://www.facebook.com/mysaholic">Facebook Page</a> and help us spread the word.<br/>
                                """
            elif fda:
                fdaFormattedString = order.first_dlvyatmp_timestamp.strftime("%A, %d. %B %Y")
                thank_you_html = """We are pleased to inform that the following items in your order $master_order_id have been delivered.<br/>  The First Delivery attempt was made on """+ fdaFormattedString +""" , within our commited estimated time for delivery.<br/>
                                If you are happy with our services please join us on <a href="https://twitter.com/saholic">Twitter</a>, <a href="http://www.facebook.com/mysaholic">Facebook Page</a> and help us spread the word.<br/>
                                Following Items are Delivered:
                                """ 
        else :
            thank_you_html = """We are pleased to inform that the following items in your order $master_order_id have been delivered.<br/>
                            Following Items are Delivered:
                            """ 
                            #If you are happy with our services please join us on <a href="https://twitter.com/saholic">Twitter</a>, <a href="http://www.facebook.com/mysaholic">Facebook Page</a> and help us spread the word.<br/>
        
    freebie_text = "<br/>"
    if hasFreebie and not hasSplitOrder:
        freebie_text = freebie_text + "We have also delivered your freebies with eligible products as promised<br/>"
    elif not hasFreebie and hasSplitOrder:
        freebie_text = freebie_text + "We wish to inform you that your freebie item(s): <br/>"
        for splitOrder in splitOrdersMap.values():
            freebieLineItem = get_line_items_for_order(splitOrder.id)[0]
            freebie_text = freebie_text + freebieLineItem.brand + " " + freebieLineItem.model_name + " " + freebieLineItem.model_number + " will be sent as a separate order with OrderId : " + str(splitOrder.id) + " and is expected to be delivered on " + splitOrder.expected_delivery_time.strftime("%A, %d %B %Y")+ "<br/>"+ "<br/>You can track the status of this order from My Orders section in saholic.com"
    elif hasFreebie and hasSplitOrder:
        freebie_text = freebie_text + "We have also delivered some of your freebies with eligible products as promised and rest freebie item(s) details are given below: <br/>"
        for splitOrder in splitOrdersMap.values():
            freebieLineItem = get_line_items_for_order(splitOrder.id)[0]
            freebie_text = freebie_text + freebieLineItem.brand + " " + freebieLineItem.model_name + " " + freebieLineItem.model_number + " will be sent as a separate order with OrderId : " + str(splitOrder.id) + " and is expected to be delivered on " + splitOrder.expected_delivery_time.strftime("%A, %d %B %Y")+ "<br/>" + "<br/>You can track the status of this order from My Orders section in saholic.com"
    else:
        freebie_text = ""        

    insuranceText = ""
    if hasInsurer:
        insuranceText = "Some item(s) are Insured Against Theft for 1 Year. <a href='http://www.saholic.com/static/insurance-terms'>Know More</a><br/>Please download and read attached policy document."
    
    customer_name = order.customer_name
    order_date = order.created_timestamp
    user_email = order.customer_email
       
    subject = "{0} - Your shipment {1} is delivered".format(source_name, logisticsTxnId)
    dt = datetime.datetime.strptime(str(order_date), "%Y-%m-%d %H:%M:%S")
    formated_order_date = dt.strftime("%A, %d. %B %Y %I:%M%p")
    
    complete_html = html_header + thank_you_html + html
    
    email_html = Template(complete_html).substitute(dict(master_order_id = logisticsTxnId, order_date = formated_order_date, customer_name = customer_name, pickup_text = pickup_text, source_url = source_url, source_name = source_name, freebie_text = freebie_text, insurance_text = insuranceText))
    
    try:
        helper_client = HelperClient(host_key = "helper_service_server_host_prod").get_client()
        helper_client.saveUserEmailForSending([user_email], SaholicHelpEmailId, subject, email_html, logisticsTxnId, "DeliverySuccess", [], [], order.source)
        
        if order.source == OrderSource.WEBSITE:
            send_transaction_sms(order.customer_id, order.customer_mobilenumber, "Dear Customer, Your order: " + logisticsTxnId + " has been delivered now. For any queries please call +918826894203." , SmsType.TRANSACTIONAL, True)
        
        return True
    except Exception as e:
        print e
        return False

def __get_secret_code(order_id):
    return get_order_attribute_value(order_id, 'SECRET_CODE')

def get_order_attribute_value(order_id, attribute_name):
    existingAttribute = Attribute.query.filter(Attribute.orderId == order_id).filter(Attribute.name == attribute_name).first()
    if existingAttribute:
        return existingAttribute.value
    return ""

def change_jacket_number(order_id, jacket_number):
    order = get_order(order_id)
    if order.status == OrderStatus.BILLED:
        order.jacket_number = jacket_number
        session.commit()
        return True
    return False

def mark_order_as_rto_in_transit(order_id):
    selectedOrder = get_order(order_id)
    if selectedOrder.logisticsTransactionId:
        orders = get_group_orders_by_logistics_txn_id(selectedOrder.logisticsTransactionId)
        for order in orders:
            if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.DELIVERED_AT_STORE, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY, OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE, OrderStatus.DELIVERY_SUCCESS]:
                return False
            order.status = OrderStatus.RTO_IN_TRANSIT
            order.delivery_timestamp = datetime.datetime.now()
            order.statusDescription = "Order Returned to Origin"
            try:
                alert_client = AlertClient().get_client()
                alert_client.endMonitoringEntity(EntityType.COURIER, "orderId = " + str(order.id))
            except Exception as e:
                print "Exception in ending alert in MarkOrderAsRTO method"
                print e
        update_trust_level(orders[0])
        session.commit()
    else:
        if selectedOrder == None or selectedOrder.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.DELIVERED_AT_STORE, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY, OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE, OrderStatus.DELIVERY_SUCCESS]:
            return False
        selectedOrder.status = OrderStatus.RTO_IN_TRANSIT
        selectedOrder.delivery_timestamp = datetime.datetime.now()
        selectedOrder.statusDescription = "Order Returned to Origin"
        update_trust_level(selectedOrder)
        session.commit()
        try:
            alert_client = AlertClient().get_client()
            alert_client.endMonitoringEntity(EntityType.COURIER, "orderId = " + str(selectedOrder.id))
        except Exception as e:
            print "Exception in ending alert in MarkOrderAsRTO method"
            print e
    return True
    
def get_order(order_id):
    order = Order.get_by(id=order_id)
    if not order:
        print "Not found order for orderId " + str(order_id)
        raise TransactionServiceException(108, "no such order")
    
    return order

def get_order_list(order_ids):
    orders = Order.query.filter(Order.id.in_(tuple(order_ids)))
    return orders

def get_order_list_for_vendor(order_ids, vendor_id):
    if vendor_id == -1:
        return get_order_list(order_ids)
    else:
        orders = Order.query.filter_by(vendorId = vendor_id).filter(Order.id.in_(tuple(order_ids)))
        return orders

def get_orders_for_customer(customer_id, from_date, to_date, statuses):
    if not customer_id:
        raise TransactionServiceException(101, "bad customer id")
    query = Order.query.filter(Order.customer_id == customer_id)
    if statuses:
        query = query.filter(Order.status.in_(tuple(statuses)))
    if from_date:
        query = query.filter(Order.created_timestamp >from_date)
    if to_date:
        query = query.filter(Order.created_timestamp < to_date)
    return query.all()

def get_order_for_customer(order_id, customer_id):
    if not customer_id:
        raise TransactionServiceException(101, "bad customer id")
    order = get_order(order_id)
    if order.customer_id != customer_id:
        raise TransactionServiceException(108, "order: " + str(order_id) + " does not belong to customer: " + str(customer_id))
    return order

def get_all_orders(statuses, from_date, to_date, warehouse_id):
    query = Order.query.options(joinedload('lineitems'))
    if not len(statuses):
        query = query.filter(Order.status >= OrderStatus.COD_VERIFICATION_PENDING)
    else:
        query = query.filter(Order.status.in_(statuses))
    if warehouse_id:
        query = query.filter(Order.warehouse_id == warehouse_id)
        #raise TransactionServiceException(101, "bad warehouse id")
    if from_date:
        if to_date:
            query = query.filter(or_(
                and_(Order.created_timestamp >from_date, Order.created_timestamp < to_date),
                and_(Order.refund_timestamp >from_date, Order.refund_timestamp < to_date)  
            ))
        else:
            query = query.filter(Order.created_timestamp >from_date)
    else:
        if to_date:
            query = query.filter(or_(Order.created_timestamp < to_date, Order.refund_timestamp < to_date))
    return query.all()

def get_orders_in_batch(statuses=[], offset=0, limit=0, warehouse_id=None, source=0):
    print "source ------------", source
    print "statuses  ------------", statuses
    query = Order.query.options(joinedload('lineitems'))
    if warehouse_id:
        query = query.filter(Order.warehouse_id == warehouse_id)
    if source:
        query = query.filter(Order.source == source)
    if statuses:
        query = query.filter(Order.status.in_(statuses))
    query = query.offset(offset)
    if limit:
        query = query.limit(limit)
    return query.all()

def get_orders_in_batch_as_promised_shipping(statuses=[], offset=0, limit=0, warehouse_id=None, source=0):
    print "source ------------", source
    print "statuses  ------------", statuses
    query = Order.query.options(joinedload('lineitems'))
    if warehouse_id:
        query = query.filter(Order.warehouse_id == warehouse_id)
    if source:
        query = query.filter(Order.source == source)
    if statuses:
        query = query.filter(Order.status.in_(statuses))
    query = query.order_by(Order.transaction_id)
    if limit>0:
        query = query.limit(limit)
    if offset>0 and limit>0: 
        query = query.offset(offset)
    return query.all()


def get_order_count(statuses=[], warehouse_id=None, source=0):
    #if not warehouse_id:
    #    raise TransactionServiceException(101, "bad warehouse id")
    query = session.query(func.count(Order.id))
    if warehouse_id:
        query = query.filter(Order.warehouse_id == warehouse_id)
    if source:
        query = query.filter(Order.source == source)
    if statuses:
        query = query.filter(Order.status.in_(statuses))
    return query.scalar()

def get_returnable_orders_for_customer(customer_id, limit = None):
    if not customer_id:
        raise TransactionServiceException(101, "bad customer id")
    
    query = Order.query.filter(Order.customer_id == customer_id)
    query = query.filter(Order.status != OrderStatus.PAYMENT_FAILED)
    query = query.filter(Order.status != OrderStatus.REJECTED)
    query = query.filter(Order.status > OrderStatus.SHIPPED_FROM_WH)
    query = query.filter(or_(Order.status < OrderStatus.DELIVERY_SUCCESS, Order.delivery_timestamp < datetime.datetime.now() + datetime.timedelta(hours = 48)))
    orders = query.all()
    order_ids = [order.id for order in orders]
    return order_ids

def get_cancellable_orders_for_customer(customer_id, limit = None):
    if not customer_id:
        raise TransactionServiceException(101, "bad customer id")
    cancellableOrderStatuses = OrderStatusGroups().codCancellable
    query = Order.query.filter(Order.customer_id == customer_id)
    query = query.filter(Order.status.in_(cancellableOrderStatuses))
    orders = query.all()
    return [order.id for order in orders]

def get_orders_by_billing_date(status, start_billing_date, end_billing_date, warehouse_id):
    if not warehouse_id:
        raise TransactionServiceException(101, "bad warehouse id")

    query = Order.query.filter(Order.warehouse_id == warehouse_id)
    
    if status:
        query = query.filter(Order.status == status)
    
    if start_billing_date:
        query = query.filter(Order.billing_timestamp >= start_billing_date)
    
    if end_billing_date:
        query = query.filter(Order.billing_timestamp <= end_billing_date)
    
    return query.all()

def get_orders_by_shipping_date(from_shipping_date, to_shipping_date, provider_id, warehouse_id, cod):
    query = Order.query.filter(Order.cod == cod)
    
    if warehouse_id and warehouse_id != -1:
        query = query.filter(Order.warehouse_id == warehouse_id)
    
    if provider_id and provider_id != -1:
        query = query.filter(Order.logistics_provider_id == provider_id)
    
    if from_shipping_date:
        query = query.filter(Order.shipping_timestamp >= from_shipping_date)
    
    if to_shipping_date:
        query = query.filter(Order.shipping_timestamp <= to_shipping_date)
    
    query = query.order_by(Order.airwaybill_no)
    
    return query.all()

def get_orders_for_transaction(transaction_id, customer_id):
    orders = Order.query.filter_by(transaction_id=transaction_id, customer_id=customer_id).all()
    
    if not orders:
        raise TransactionServiceException(101, "No order for the transaction")
    return orders

def get_undelivered_orders(provider_id, warehouse_id):
    query = Order.query
    if provider_id != -1:
        query = query.filter_by(Order.logistics_provider_id == provider_id)
    if warehouse_id != -1:
        query = query.filter(Order.warehouse_id == warehouse_id)
    query = query.filter(and_(Order.status != OrderStatus.DELIVERY_SUCCESS, Order.status != OrderStatus.FAILED, \
                              Order.status > OrderStatus.SHIPPED_FROM_WH))
    #Get last midnight time instead of current time as courier company would have sent the 
    #delivery records updated at least till midnight and not by the current time.
    time_format = "%Y-%m-%d %H:%M:%S.%f"
    t = time.strptime(str(datetime.datetime.now()), time_format)
    midnightTime = datetime.datetime(*t[:3])
    query = query.filter(Order.expected_delivery_time < midnightTime)
    orders = query.all()
    return orders

def get_line_items_for_order(order_id):
    query = LineItem.query.filter(LineItem.order_id == order_id)
    return query.all()

def change_order_status(self, orderId, status, description):
    order = get_order(orderId)
    order.status = status
    order.statusDescription = description
    session.commit()
    update_trust_level(order)
    return True

def update_trust_level(order):
    try:
        trust_level_delta = 0
        if order.cod == True:
            if order.status in [OrderStatus.RTO_RESHIPPED] and order.logisticsTransactionId:
                earlierMarkedReshipped = Order.query.filter(Order.logisticsTransactionId == order.logisticsTransactionId).filter(Order.status==OrderStatus.RTO_RESHIPPED).first()
                if earlierMarkedReshipped is None:
                    trust_level_delta = ORDER_STATUS_TO_USER_TRUST_LEVEL_DELTA_DICT.get(order.status, 0)
            else:
                trust_level_delta = ORDER_STATUS_TO_USER_TRUST_LEVEL_DELTA_DICT.get(order.status, 0)

        if (trust_level_delta != 0):
            user_client = UserClient().get_client()
            user_client.increaseTrustLevel(order.customer_id, trust_level_delta)
    except Exception as e:
        print e

    return True

def verify_order(orderId):
    logging.info("Verifying order no: " + str(orderId))
    order = get_order(orderId)
    if order.status == OrderStatus.COD_VERIFICATION_PENDING:
        order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
        order.statusDescription = "Submitted for processing"
        order.verification_timestamp = datetime.datetime.now()
        session.commit()
        
        if order.source == OrderSource.STORE:
            sod = StoreOrderDetail.get_by(orderId = order.id)
            __push_store_collection_to_hotspot(order, "advance", sod.cashAmount, sod.cardAmount)
        logging.info("Successfully verified order no: " + str(orderId))
        return True
    else:
        logging.warning("Verify called for order no." + str(orderId) +" which is not in verification pending state");
        return False
        
def unaccept_order(orderId):
    logging.info("UnAccepting order no: " + str(orderId))
    order = get_order(orderId)
    if order.status != OrderStatus.ACCEPTED:
        return False
    logisticsTxnId = order.logisticsTransactionId

    orders = [order]
    if logisticsTxnId is not None:
        orders = Order.query.filter(Order.logisticsTransactionId==logisticsTxnId).all()
    for o in orders:
        o.status = OrderStatus.SUBMITTED_FOR_PROCESSING 
        o.statusDescription = 'Submitted For Processing'
        o.accepted_timestamp = None
        o.logisticsTransactionId = None
        o.airwaybill_no = None
        o.tracking_id = None
        o.billing_timestamp = None
        o.invoice_number= None
        o.jacket_number = None
        o.refund_timestamp= None
        o.refunded_by= None 
        o.refund_reason= None
    session.commit()
    return True
    

def __capture_txn(txnId):
    logging.info("Capturing payment for merchant txn:" + str(txnId))
    captured_amount = 0
    try:
        payment_client = PaymentClient().get_client()
        payments = payment_client.getPaymentForTxnId(txnId)
        for payment in payments:
            if payment.gatewayId in (gvGatewayId, walletGatewayId):
                continue
            capture_result = payment_client.capturePayment(txnId, False)
            if capture_result:
                captured_amount = captured_amount + payment.amount
                logging.info("Successfully captured payment for merchant txn:" + str(txnId))
                #change_transaction_status(txnId, TransactionStatus.IN_PROCESS, "Payment received", PickUpType.COURIER, order.orderType, order.source)
            else:
                raise TransactionServiceException(115, "Payment capture failed.")
        return captured_amount
    except PaymentException as e:
        if e.error_code == 106:
            #order.status = OrderStatus.CAPTURE_IN_PROCESS
            #session.commit()
            raise TransactionServiceException(122, "Unable to capture payment due to connection issue.")
        else:
            raise TransactionServiceException(115, "Payment capture failed.")

def __amend_fulfilment_warehouse(order):
    itemId = order.lineitems[0].item_id
    inventoryClient = InventoryClient().get_client()
    inventory = inventoryClient.getItemInventoryByItemId(itemId)
    if not (inventory.availability.has_key(order.fulfilmentWarehouseId) and inventory.availability[order.fulfilmentWarehouseId]):
        for warehouse in inventoryClient.getWarehouses(WarehouseType.OURS, InventoryType.GOOD, 0, order.warehouse_id, 0):
            if inventory.availability.has_key(warehouse.id) and inventory.availability[warehouse.id] >= inventory.reserved[warehouse.id] + order.lineitems[0].quantity:
                inventoryClient.reduceReservationCount(itemId, order.fulfilmentWarehouseId, sourceId, order.id, order.lineitems[0].quantity)
                inventoryClient.reserveItemInWarehouse(itemId, warehouse.id, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), order.lineitems[0].quantity)
                order.fulfilmentWarehouseId = warehouse.id
                break

def __isFreebieOrderBillable(order):
    parentOrderId = get_order_attribute_value(order.id, "parentOrderIdForFreebie")
    parentOrder = Order.get_by(id = parentOrderId)
    if parentOrder.cod and parentOrder.status!=12:
        return False
    else:
        return True


def __getOrderTaxType(order, state='Delhi'):
    
    return TaxType.SGST if state == order.customer_state else TaxType.IGST
#    delhiPincodePrefix = "11";
#    maharashtraPincodePrefix = ["40", "41", "42", "43", "44"];
#    karnatakaPincodePrefix = ["56", "57", "58", "59"];
#    telenganaPincodes = ["500001","500002","500003","500004","500005","500006","500007","500008","500009","500010","500011","500012","500013","500014","500015","500016","500017","500018","500019","500020","500021","500022","500023","500024","500025","500026","500027","500028","500029","500030","500031","500032","500033","500034","500035","500036","500037","500038","500039","500040","500041","500042","500043","500044","500045","500046","500047","500048","500049","500050","500051","500052","500053","500054","500055","500056","500057","500058","500059","500060","500061","500062","500063","500064","500065","500066","500067","500068","500069","500070","500071","500072","500073","500074","500075","500076","500077","500078","500079","500080","500081","500082","500083","500084","500085","500086","500087","500088","500089","500090","500091","500092","500093","500094","500095","500096","500097","500098","500178","500409","501218","501301","501401","501510","501511","501512","502307","502319","517501","517502","517503","517505","517507","520001","520002","520003","520004","520005","520006","520007","520008","520009","520010","520011","520012","520013","520014","520015","521108","521225","522001","522002","522003","522004","522005","522006","522007","522019","522509","530001","530002","530003","530004","530005","530007","530008","530009","530010","530010","530011","530012","530013","530014","530015","530016","530017","530018","530020","530021","530022","530023","530024","530026","530027","530028","530029","530032","530035","530040","530041","530043","530044","530045","530046","531001","533101","533103","533104","533105"]
#    haryanaPincodes = ["133001","134003","123307","133205","125047","122005","131104","132111","121001","132038","134002","124411","132039","124505","124021","124521","132122","121105","133301","125021","132154","133005","123308","124105","125048","122101","124507","125028","131021","123413","124142","124110","125054"
#                       ,"134007","124416","121004","132040","126145","125061","125026","125078","133201","124101","124304","125045","134118","126010","126011","125042","123025","123501","125032","123034","132108","124201","124409","124401","124503","131022","125053","124415","122102","135106","126008","124310","132140","133101","135102","123026","125022","133021","135101","124302","134107","125027","123306","125101","135103","124504","132034","171208","124542","122002","126153","125036","125025","124109","124424","132020","123412","125106","122106","132112","133202","124107","125058","124202","124202","124528","125102","121007","121001","121002","121005","121006","124408","123506","125050","132042","122104","122001","131101","123505","132114","124301","125103","132035","125001","123504","124423","125033","124404","121107","121103","132115","125004","125011","125002","121106","134109","132041","122016","132129","132107","124103","126102","135003","135002","126001","131023","124535","124314","134201","132130","123310","131024","125030","126101","132036","132001","132118","132025","124412","125085","125029","132027","124113","143512","125201","133105","126117","126133","133302","124530","123027","132021","133102","125057","123502","125038","124305","124114","124402","135105","135021","123103","123101","124144","123102","131028","132022","132119","132132","124514","124108","132037","124111","132113","124203","125052","123021","125104","125056","124506","125105","123309","124510","124106","124112","125005","125088","123029","124022","134205","133203","125041","124306","131027","133103","123001","122108","124145","131103","125037","125039","134012","123023","123028","123414","134203","126116","132106","132145","124104","124513","124531","132117","132024","122107","125077","131029","132103","126007","123035","132043","124417","121102","132104","132105","134011","123503","125003","176050","132128","132128","124205","126113","134102","134101","132131","132026","121104","131102","124001","132133","134204","132044","151301","126110","125076","131030","125051","123401","125067","124429","125043","124403","125055","131001","133204","126112","133104","124146","132023","132046","132101","124501","124303","123020","133206","123024","125044","132033","125046","132135","176206","134202","126114","125060","125049","124407","124430","122103","125075","132153","132116","122105","132136","121101","125031","126119","125040","126115","132124","133207","126009","125083","132137","124406"]
#    
#    #Simplify either its IGST or SGST
#    if state == WarehouseLocation.Delhi: 
#        if order.customer_pincode.startswith(delhiPincodePrefix):
#            return TaxType.SGST
#        else:
#            return TaxType.IGST
#    #Maharashta
#    if state == WarehouseLocation.Mumbai:
#        for mahaPincodePrefix in maharashtraPincodePrefix:
#            if order.customer_pincode.startswith(mahaPincodePrefix):
#                return TaxType.SGST
#        return TaxType.IGST
#    if state == WarehouseLocation.Karnataka:
#        for knkPincodePrefix in karnatakaPincodePrefix:
#            if order.customer_pincode.startswith(knkPincodePrefix):
#                return TaxType.SGST
#        return 1
#    if state == WarehouseLocation.Telangana:
#        if order.customer_pincode in telenganaPincodes:
#            return TaxType.SGST
#        return TaxType.IGST
#    if state == WarehouseLocation.Haryana:
#        if order.customer_pincode in haryanaPincodes:
#            return TaxType.SGST
#        return TaxType.IGST
#    return TaxType.SGST

def add_billing_details(orderId, invoice_number, serialNumbers, itemNumbers, freebieWarehouseId, billedBy, jacketNumber, billingType, fulfilmentWarehouseId, authorize):
    if billedBy is None or billedBy.strip() == "":
        raise TransactionServiceException(110, "Invalid Biller")
    
    order = Order.get_by(id=orderId)
    hsnCode = order.lineitems[0].hsnCode
    if not order:
        raise TransactionServiceException(101, "No order found for the given order id" + str(orderId))
    
    newTaxType = __getOrderTaxType(order)
    order.taxType = newTaxType
    
    
    if jacketNumber is None or jacketNumber <= 0:
        if order.source == OrderSource.EBAY or order.source == OrderSource.SNAPDEAL or order.source == OrderSource.FLIPKART:
            print "Skipping Jacket Number field for OrderId " + str(orderId)
        else:
            raise TransactionServiceException(110, "Invalid jacket number")

    '''
    First checking whether freebie can be billed or not otherwise wont proceed.
    '''
    if order.freebieItemId:
        if billingType == BillingType.OURS or billingType == BillingType.OURS_EXTERNAL:
            if freebieWarehouseId:
                try:
                    inventory_client = InventoryClient().get_client()
                    warehouse = inventory_client.getWarehouse(freebieWarehouseId)
                    if warehouse.warehouseType!= WarehouseType.OURS or warehouse.inventoryType != InventoryType.GOOD:
                        raise TransactionServiceException(110,'Billing of Freebie is only allowed from OURS_GOOD warehouses ')
                    warehouse_client = WarehouseClient().get_client()
                    isItemAvailable = warehouse_client.isItemAvailableForSale(order.freebieItemId, "", freebieWarehouseId)
                    if isItemAvailable == False:
                        raise TransactionServiceException(110,'No Freebie Item available ')
                except Exception as e:
                    print e.message
                    raise TransactionServiceException(110,'Error in billing freebie for warehouseId ' + str(freebieWarehouseId))
            else:
                raise TransactionServiceException(110,'No warehouseId provided for billing of freebie ')
        else:
            raise TransactionServiceException(110,'Order with freebies associated with it cant be billed from an external warehouse ')
    lineitem = order.lineitems[0]
    item_id = lineitem.item_id

    '''
    Checking if order is a freebie split-order and if the order is cod and if the original order is marked as delivered.
    '''
    freebie_order_info_text = "Freebie Order for Order ID"
    if lineitem.extra_info and freebie_order_info_text in lineitem.extra_info:
        canbillOrder = __isFreebieOrderBillable(order)
        if canbillOrder == False:
            raise TransactionServiceException(110,'Parent order for this is COD and is still undelivered')
#    inventoryClient = InventoryClient().get_client()
#    if not inventoryClient.isOrderBillable(item_id, order.fulfilmentWarehouseId, sourceId, orderId):
#        raise TransactionServiceException(101, "There are other orders to be billed before this order id " + str(orderId))
    
    singleInvoiceAttr = Attribute.query.filter(Attribute.orderId == order.id).filter(Attribute.name == "Single Invoice").first()
    
    catalog_client = CatalogClient().get_client()
    item = catalog_client.getItem(item_id)
    if order.status == OrderStatus.ACCEPTED:
        order.jacket_number = jacketNumber

        if itemNumbers:
            lineitem.item_number = itemNumbers[0]   
        
        if serialNumbers:
            lineitem.serial_number = serialNumbers[0]                
            
        if order.productCondition == ProductCondition.BAD:
            if billingType != BillingType.OURS:
                raise TransactionServiceException(110, 'Bad inventory is not allowed to be sold from current warehouse')
        if billingType == BillingType.EXTERNAL:            
            order.invoice_number = invoice_number

        if billingType == BillingType.OURS or billingType == BillingType.OURS_EXTERNAL:
            if ItemType.SERIALIZED == item.type and not (serialNumbers and serialNumbers[0]):
                raise TransactionServiceException(110, "No Serial Number supplied")
            if singleInvoiceAttr:
                if singleInvoiceAttr.value == "true":
                    if serialNumbers:
                        finalSerialNo =''
                        for serialNumber in serialNumbers:
                            finalSerialNo = finalSerialNo +','+ serialNumber
                    
                        finalSerialNo = finalSerialNo[1:]
                        if lineitem.serial_number is None:
                            lineitem.serial_number = finalSerialNo
            else:
                if serialNumbers:
                    lineitem.serial_number = serialNumbers[0]
            
        
        order.status = OrderStatus.BILLED
        order.statusDescription = "Order Billed"
        order.billing_timestamp = datetime.datetime.now()
        order.billed_by = billedBy

        #Manish Sharma Invoice
        whStateId = None
        # Letting the billing process fail in cases where we are unable to 
        # fill in transfer price
        try:
            inventory_client = InventoryClient().get_client()
            warehouse = inventory_client.getWarehouse(fulfilmentWarehouseId)
            item_pricing = inventory_client.getItemPricing(item_id, warehouse.vendor.id)
            lineitem.transfer_price = item_pricing.transferPrice
            lineitem.nlc = item_pricing.nlc
        except InventoryServiceException as e:
            print sys.exc_info()[0]
            print e.message
            raise TransactionServiceException(110, 'Transfer price missing for itemId: ' + str(item_id) + ' and vendor: ' + str(warehouse.vendor.id))

        # For OUR warehouse, we need to scan out items for every billed order
        if billingType == BillingType.OURS:
            if order.productCondition == ProductCondition.GOOD: 
                for index, serialNumber in enumerate(serialNumbers):
                    try:
                        warehouse_client = WarehouseClient().get_client()
                        # Fetching GOOD w/h corresponding to the virtual one here
                        if not warehouse.billingWarehouseId:
                            warehouse = inventory_client.getWarehouses(None, InventoryType.GOOD, warehouse.vendor.id, order.warehouse_id, 0)[0]
    
                        if ItemType.SERIALIZED == item.type:
                            inventoryItem = warehouse_client.getInventoryItem(serialNumber)
                        else:
                            inventoryItem = warehouse_client.getNonSeralizedInventoryItem(itemNumbers[index], item_id, warehouse.id, warehouse.billingWarehouseId)
        
                        if (inventoryItem.itemId != item_id):
                            catalog_client = CatalogClient().get_client()
                            item = catalog_client.getItem(inventoryItem.itemId)
                            scanItemString = " ".join([str(item.brand), str(item.modelName), str(item.modelNumber), str(item.color)])
                            lineItemString = " ".join([str(lineitem.brand), str(lineitem.model_name), str(lineitem.model_number), str(lineitem.color)])
                            raise TransactionServiceException(110, 'Trying to scan ' + scanItemString + ' instead of ' + lineItemString)
        
                        if ItemType.SERIALIZED == item.type:
                            inventoryItem = warehouse_client.scanSerializedItemForOrder(serialNumber, ScanType.SALE, orderId, warehouse.id, 1, order.warehouse_id)
                        else:
                            inventoryItem = warehouse_client.scanForOrder(inventoryItem, ScanType.SALE, 1, order.id, warehouse.id, order.warehouse_id)
        
                        lineitem.transfer_price = inventoryItem.unitPrice
                        lineitem.nlc = inventoryItem.nlc
                        order.vendorId = inventoryItem.supplierId
                        catalog_client = CatalogClient().get_client()
                    except WarehouseServiceException as e:
                        print sys.exc_info()[0]
                        print 'Could not scan out orders due to: ' + e.message
                        raise TransactionServiceException(110, e.message)
            else:
                for index, serialNumber in enumerate(serialNumbers):
                    try:
                        warehouse_client = WarehouseClient().get_client()
                        
                        if ItemType.SERIALIZED == item.type:
                            inventoryItem = warehouse_client.scanForBadSale(serialNumber, itemNumbers[index], item_id, orderId, warehouse.id, 1, order.warehouse_id)
                        else:
                            inventoryItem = warehouse_client.scanForBadSale(serialNumber, itemNumbers[index], item_id, orderId, warehouse.id, lineitem.quantity, order.warehouse_id)
                        
                        lineitem.transfer_price = inventoryItem.unitPrice
                        lineitem.nlc = inventoryItem.nlc
                        order.vendorId = inventoryItem.supplierId
                        catalog_client = CatalogClient().get_client()
                    except WarehouseServiceException as e:
                        print sys.exc_info()[0]
                        print 'Could not scan out orders due to: ' + e.message
                        raise TransactionServiceException(110, e.message)
            
        elif billingType == BillingType.OURS_EXTERNAL:
            try:
                if ItemType.SERIALIZED == item.type:
                    serialNumber = serialNumbers[0]
                else:
                    serialNumber = ""
                warehouse_client = WarehouseClient().get_client()
                inventoryItem = warehouse_client.scanForOursExternalSale(lineitem.item_id, serialNumber, lineitem.item_number, invoice_number, order.fulfilmentWarehouseId, lineitem.transfer_price, lineitem.nlc, order.id)
                lineitem.transfer_price = inventoryItem.unitPrice
                lineitem.nlc = inventoryItem.nlc
                order.vendorId = inventoryItem.supplierId
            except WarehouseServiceException as e:
                print sys.exc_info()[0]
                print 'Could not scan out orders due to: ' + e.message
                raise TransactionServiceException(110, e.message)
        else:
            if not warehouse.isAvailabilityMonitored:
                inventory_client = InventoryClient().get_client()
                inventory_client.addInventory(item.id, warehouse.id, -1 * lineitem.quantity)
        
        if order.freebieItemId:
            try:
                inventory_client = InventoryClient().get_client()
                freebie_warehouse = inventory_client.getWarehouse(freebieWarehouseId)
                
                if freebie_warehouse.warehouseType!= WarehouseType.OURS or freebie_warehouse.inventoryType != InventoryType.GOOD:
                    raise TransactionServiceException(110,'Billing of Freebie is only allowed from OURS_GOOD warehouses ')
                
                warehouse_client = WarehouseClient().get_client()
                inventoryItem = warehouse_client.scanfreebie(orderId, order.freebieItemId, freebieWarehouseId, ScanType.SALE);
                
                attr = Attribute()
                attr.orderId = orderId
                attr.name = "freebie_tp"
                attr.value = str(inventoryItem.unitPrice)
                
                attr1 = Attribute()
                attr1.orderId = orderId
                attr1.name = "freebie_vendor"
                attr1.value = str(inventoryItem.supplierId)

                attr2 = Attribute()
                attr2.orderId = orderId
                attr2.name = "freebie_nlc"
                attr2.value = str(inventoryItem.nlc)
            except Exception as e:
                print e.message
                raise TransactionServiceException(110,'Error in billing freebie for warehouseId ' + str(freebieWarehouseId))
        if order.productCondition != ProductCondition.BAD:
            __update_inventory_reservation(order)
        order.fulfilmentWarehouseId = warehouse.id
        if singleInvoiceAttr:
            if singleInvoiceAttr.value == "true":
                session.commit()
                return True
        if billingType == BillingType.OURS or billingType == BillingType.OURS_EXTERNAL:
            #as of now only company 2 - SORPL, 3-New company is operating and cutoff config will decide the company to be used to bill
            #until further change - Amit Gupta
            seller_id = __get_seller(order.warehouse_id)
            order.invoice_number = get_next_invoice_counter(seller_id, order.warehouse_id, hsnCode)
            order.seller_id = seller_id
            whaddressmapping = WarehouseAddressMapping.query.filter_by(warehouse_id=order.warehouse_id).one()
            order.warehouse_address_id = whaddressmapping.address_id
        session.commit()
        return True
    else:
        return False

def add_invoice_number(orderId, invoiceNumber, color, serialNumber, itemNumber):
    order = Order.get_by(id=orderId)
    if not order:
        raise TransactionServiceException(101, "No order found for the given order id" + str(orderId))
    
    if order.status == OrderStatus.ACCEPTED:
        order.invoice_number = invoiceNumber
        if color:
            billedOrdersColorMap[orderId] = color
        if serialNumber:
            order.lineitems[0].serial_number = serialNumber.strip() 
        if itemNumber:
            order.lineitems[0].item_number = itemNumber.strip()
    session.commit()
    
def batch_orders(warehouseId):
    if not warehouseId:
        raise TransactionServiceException(101, "bad warehouse id")
    
    batchno_generator = BatchNoGenerator()
    session.commit()
    query = Order.query.filter_by(warehouse_id=warehouseId)
    query = query.filter(Order.status == OrderStatus.LOW_INV_PO_RAISED)
    query = query.order_by(Order.created_timestamp)
    pending_orders = query.all()
    
    serial_no = 1
    for order in pending_orders:
        order.batchNo = batchno_generator.id
        order.serialNo = serial_no
        serial_no += 1
    session.commit() 
    pending_orders = query.all()
    return pending_orders

def order_outofstock(orderId):
    '''
    Mark order as out of stock
    '''
    order = get_order(orderId)
    order.status = OrderStatus.INVENTORY_LOW
    order.statusDescription = "Low Inventory"
    order.outofstock_timestamp = datetime.datetime.now()
    session.commit()
    return True

def mark_orders_as_shipped_from_warehouse(warehouseId, providerId, cod, orderIds):
    try:
        current_timestamp = datetime.datetime.now()
        orders = Order.query.filter(Order.id.in_(orderIds)).filter_by(warehouse_id = warehouseId, logistics_provider_id = providerId, status = OrderStatus.BILLED).all()
        for order in orders:
            order.status = OrderStatus.SHIPPED_FROM_WH
            order.statusDescription = "Order shipped from warehouse"
            order.shipping_timestamp = current_timestamp
            '''
            try:
                print "Trying to schedule alert"
                monitoredEntity = MonitoredEntity();
                monitoredEntity.entityType=EntityType.COURIER;
                monitoredEntity.eventType=OrderStatus.SHIPPED_FROM_WH;
                monitoredEntity.entityIdentifier="orderId = " + str(order.id);
                adjustedDeliveryDays = adjust_delivery_time(datetime.datetime.now(), 1);
                sec = datetime.datetime.now()
                #Warn alert time is taken as 16 hrs
                warn_time = (sec + timedelta(days=(adjustedDeliveryDays-1)) + timedelta(hours=16))
                #Critical alert time is taken as 26 hrs                    
                critical_time = (sec + timedelta(days=(adjustedDeliveryDays-1)) + timedelta(hours=26))
                monitoredEntity.warnExpiryTime = int(warn_time.strftime("%s"))*1000
                monitoredEntity.criticalExpiryTime = int(critical_time.strftime("%s"))*1000
                monitoredEntity.description="warehouseId = " + str(warehouseId) + " providerId = " + str(providerId) + " orderId = "  + str(order.id);
                alert_client = AlertClient().get_client()
                alert_client.scheduleAlert(monitoredEntity)
            except Exception as e:
                print "Exception in scheduling alert in ShippedFromWarehouse method"
                print e
            '''
            session.commit()
        logisticsTxnIdOrdersMap = {}
        for order in orders:
            if order.logisticsTransactionId:
                if logisticsTxnIdOrdersMap.has_key(order.logisticsTransactionId):
                    ordersList = logisticsTxnIdOrdersMap.get(order.logisticsTransactionId)
                    ordersList.append(order)
                    logisticsTxnIdOrdersMap[order.logisticsTransactionId] = ordersList
                else:
                    ordersList = []
                    ordersList.append(order)
                    logisticsTxnIdOrdersMap[order.logisticsTransactionId] = ordersList
            else:
                ordersList = []
                ordersList.append(order)
                logisticsTxnIdOrdersMap[str(order.id)] = ordersList
        
        for logisticsTxnId, orderList in logisticsTxnIdOrdersMap.iteritems():
            enqueue_shipping_confirmation_email(logisticsTxnId, orderList)
        
        try:
            for order in orders:
                if order.pickupStoreId:
                    send_mails_to_bdms(order)
                '''
                else:
                    enqueue_shipping_confirmation_email(order)
                '''
        except Exception as e:
            print e
                            
        return True
    except:
        return False
    
def send_mails_to_bdms(order):
    logistics_client = LogisticsClient().get_client()
    store = logistics_client.getPickupStore(order.pickupStoreId)
    provider = logistics_client.getProvider(order.logistics_provider_id)
    subject = source_name + " order " + str(order.id) +" shipped to store" 
    raw_message = '''
    Dear Sir,

    Order mentioned below is being shipped to your store. Customer will pick it from your store. 

    Store Id: %(store_id)s
    Order Id: %(order_id)s 
    Product: %(product)s
    Delivery By: %(provider)s
    Payment Mode: %(payMode)s

    %(note)s

    Thanks and Regards
    Himanshu Pandey
    '''
    
    payMode = 'Prepaid'
    note = ''
    if order.cod:
        payMode = 'COD'
        note = 'Note: Please collect Rs. ' + str(order.total_amount-order.gvAmount) + ' from customer'

    message = dedent(raw_message) % { 'order_id' : str(order.id),
                                     'store_id' : str(store.hotspotId),
                                  'provider' : provider.name,
                                  'product' : str(order.lineitems[0]),
                                  'payMode' : payMode,
                                  'note'    : note
                                  }
    to_addresses = [store.email, store.bdmEmail, 'himanshu.pandey@shop2020.in', 'amit.sirohi@shop2020.in', 'ritesh.chauhan@shop2020.in']
    cc = ['khushal.bhatia@spiceretail.co.in']
    bcc = ['rajneesh.arora@spiceretail.co.in']
    mail(mail_user, mail_password, to_addresses, subject, message, [], cc, bcc)
        
def mark_orders_as_returned_from_store(providerId, orderIds, awbs):
    try:
        current_timestamp = datetime.datetime.now()
        orders = Order.query.filter(Order.id.in_(orderIds)).filter_by(logistics_provider_id = providerId, status = OrderStatus.RET_PICKUP_REQUEST_RAISED).all()
        for order in orders:
            order.status = OrderStatus.RTO_IN_TRANSIT
            order.statusDescription = "Order returned from store"
            order.doa_pickup_timestamp = current_timestamp
            #order.airwaybill_no = awbs[orderIds.index(order.id)]
            order.tracking_id = awbs[orderIds.index(order.id)]
        session.commit()
        
        return True
    except:
        return False    

def set_order_attributes(orderId, attributes):
    for attribute in attributes :
        existingAttribute = Attribute.query.filter(Attribute.orderId == orderId).filter(Attribute.name == attribute.name).first()
        if existingAttribute:
            existingAttribute.value = attribute.value
        else :                
            attr = Attribute()
            attr.orderId = orderId
            attr.name = attribute.name
            attr.value = attribute.value
    session.commit()
        
def mark_orders_as_picked_up(provider_id, pickup_details):
    for awb, pickup_timestamp in pickup_details.iteritems():
        orders = []
        orders = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).all()
        if orders == None or len(orders) ==0:
            continue
        for order in orders:
            if order.status != OrderStatus.SHIPPED_FROM_WH:
                continue
            order.status = OrderStatus.SHIPPED_TO_LOGST
            order.statusDescription = "Order picked up by Courier Company"
            order.pickup_timestamp = pickup_timestamp
            
            try:
                monitoredEntity = MonitoredEntity();
                monitoredEntity.entityType=EntityType.COURIER;
                monitoredEntity.eventType=OrderStatus.SHIPPED_TO_LOGST
                monitoredEntity.entityIdentifier="orderId = " + str(order.id);
                adjustedDeliveryDays = adjust_delivery_time(datetime.datetime.now(), 2);
                sec = datetime.datetime.now()
                #Warn alert time is taken as 26 hrs
                warn_time = (sec + timedelta(days=(adjustedDeliveryDays-2)) + timedelta(hours=26))
                #Critical alert time is taken as 36 hrs                    
                critical_time = (sec + timedelta(days=(adjustedDeliveryDays-1)) + timedelta(hours=36))
                monitoredEntity.warnExpiryTime = int(warn_time.strftime("%s"))*1000
                monitoredEntity.criticalExpiryTime = int(critical_time.strftime("%s"))*1000
                monitoredEntity.description="providerId = " + str(provider_id)
                alert_client = AlertClient().get_client()
                alert_client.updateMonitoredObject(monitoredEntity)
            except Exception as e:
                print "Exception in updating alert in MarkOrdersAsPickedUp method"
                print e
    session.commit()

def get_orders_not_picked_up(provider_id):
    current_time = datetime.datetime.now()
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
    orders_not_picked_up = Order.query.filter_by(logistics_provider_id = provider_id).filter_by(status=OrderStatus.SHIPPED_FROM_WH).filter(Order.shipping_timestamp <= to_datetime).all()
    return orders_not_picked_up

def mark_orders_as_delivered(provider_id, delivered_orders):
    logisticsTxnIdOrdersMap = {}
    for awb, detail in delivered_orders.iteritems():
        orders = []
        orders = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).all()
        if orders == None or len(orders) ==0:
            continue
        for order in orders:
            if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY, OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE]:
                continue
            if order.logisticsTransactionId:
                if logisticsTxnIdOrdersMap.has_key(order.logisticsTransactionId):
                    orderList = logisticsTxnIdOrdersMap.get(order.logisticsTransactionId)
                    orderList.append(order)
                    logisticsTxnIdOrdersMap[order.logisticsTransactionId] = orderList
                else:
                    orderList =[]
                    orderList.append(order)
                    logisticsTxnIdOrdersMap[order.logisticsTransactionId] = orderList
            else:
                orderList =[]
                orderList.append(order)
                logisticsTxnIdOrdersMap[str(order.id)] = orderList
            timestamp, receiver = detail.split('|')
            order.delivery_timestamp = datetime.datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S")
            if order.first_dlvyatmp_timestamp is None:
                order.first_dlvyatmp_timestamp = order.delivery_timestamp
            order.receiver = receiver
            if order.pickupStoreId:
                order.status = OrderStatus.DELIVERED_AT_STORE
                order.statusDescription = "Order delivered At Store"
                try:
                    monitoredEntity = MonitoredEntity();
                    monitoredEntity.entityType=EntityType.COURIER;
                    monitoredEntity.eventType=OrderStatus.DELIVERED_AT_STORE;
                    monitoredEntity.entityIdentifier="orderId = " + str(order.id);
                    #adjustedDeliveryDays = adjust_delivery_time(datetime.datetime.now(), 1);
                    sec = datetime.datetime.now()
                    #Critical alert time is taken as 26 hrs                    
                    critical_time = (sec + timedelta(hours=4))
                    monitoredEntity.criticalExpiryTime = int(critical_time.strftime("%s"))*1000
                    monitoredEntity.description="deliveryTimeAtStore = " + str(order.delivery_timestamp) + " orderId = "  + str(order.id);
                    alert_client = AlertClient().get_client()
                    alert_client.updateMonitoredObject(monitoredEntity)
                except Exception as e:
                    print e
            else:
                order.status = OrderStatus.DELIVERY_SUCCESS
                order.statusDescription = "Order delivered"
                if order.insuranceDetails:
                    try:
                        update_insurance_details(order)
                    except:
                        print "Error generating insurance file for order " + str(order.id)
                        session.rollback()
                        return
                    
                if order.dataInsuranceDetails:
                    order.dataInsuranceDetails[0].startDate = order.delivery_timestamp
                    order.dataInsuranceDetails[0].expiryDate = order.delivery_timestamp + timedelta(days = 90)
                        
                try:
                    alert_client = AlertClient().get_client()
                    alert_client.endMonitoringEntity(EntityType.COURIER, "orderId = " + str(order.id))
                except Exception as e:
                    print "Exception in scheduling alert in ShippedFromWarehouse method"
                    print e
        
    
    for logisticsTxnId, ordersList in logisticsTxnIdOrdersMap.iteritems():
        if enqueue_delivery_success_mail(logisticsTxnId, ordersList) :
            order = ordersList[0]
            update_trust_level(order)
            if order.transaction.payment_option == capitalFloatPayMethod:
                total_amount = 0
                for ordObj in ordersList:
                    total_amount = total_amount + ordObj.total_amount + ordObj.shippingCost - ordObj.gvAmount
                creditObj = __creditHistoryObj(order.customer_id, 1, order.transaction.id, total_amount, CreditTxnType.LOAN, order.logisticsTransactionId)
                creditTxns = []
                creditTxns.append(creditObj)
                try:
                    process_credit_transaction(order.transaction.id, order.customer_id, 1, creditTxns, order.invoice_number)
                except:
                    traceback.print_exc()
                    session.rollback()
                    return
        else :
            session.rollback()
    session.commit()              
    

def update_insurance_details(order):
    order.insuranceDetails[0].startDate = order.delivery_timestamp
    order.insuranceDetails[0].expiryDate = order.delivery_timestamp + timedelta(days = 365)
    filename = "/tmp/" + str(order.id) + "-insurance-policy.pdf"
    __generate_policy_doc(order, filename)
    file = open(filename, "rb")
    pdfFile = file.read()
    doc = DocumentStore()
    doc.docType = 1
    doc.docSource = order.id
    doc.document = pdfFile

def mark_order_as_delivered(orderId, deliveryTimestamp, receiver):
    singleOrder = Order.get_by(id=orderId)
    logisticsTxnIdOrdersMap = {}
    
    grouppedOrdersList = []
    if singleOrder.logisticsTransactionId:
        grouppedOrdersList = get_group_orders_by_logistics_txn_id(singleOrder.logisticsTransactionId)
        for order in grouppedOrdersList:
            if logisticsTxnIdOrdersMap.has_key(order.logisticsTransactionId):
                orderList = logisticsTxnIdOrdersMap.get(order.logisticsTransactionId)
                orderList.append(order)
                logisticsTxnIdOrdersMap[order.logisticsTransactionId]= orderList
            else:
                orderList=[]
                orderList.append(order)
                logisticsTxnIdOrdersMap[order.logisticsTransactionId]= orderList
    else:
        grouppedOrdersList.append(singleOrder)
        logisticsTxnIdOrdersMap[str(singleOrder.id)]= grouppedOrdersList
        
    for logisticsTxnId, ordersList in logisticsTxnIdOrdersMap.iteritems():
        order = ordersList[0]
        if order.transaction.payment_option == capitalFloatPayMethod:
            total_amount = 0
            for ordObj in ordersList:
                total_amount = total_amount + ordObj.total_amount + ordObj.shippingCost - ordObj.gvAmount
            creditObj = __creditHistoryObj(order.customer_id, 1, order.transaction.id, total_amount, CreditTxnType.LOAN, order.logisticsTransactionId)
            creditTxns = []
            creditTxns.append(creditObj)
            try:
                process_credit_transaction(order.transaction.id, order.customer_id, 1, creditTxns, order.invoice_number)
            except:
                traceback.print_exc()
                session.rollback()
                return
    
    itemIds = []
    ordersMap = {}
    orderItemsMap = {}        
    for order in grouppedOrdersList:
        if order.lineitems[0].item_id not in itemIds:
            itemIds.append(order.lineitems[0].item_id)
        ordersMap[order.id] = order
        orderItemsMap[order.id] = order.lineitems[0].item_id
    
       
    for order in grouppedOrdersList:            
        if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY, OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE, OrderStatus.DOA_PICKUP_REQUEST_RAISED, OrderStatus.DOA_PICKUP_CONFIRMED, OrderStatus.RET_REQUEST_RECEIVED, OrderStatus.RET_PICKUP_CONFIRMED, OrderStatus.RET_REQUEST_AUTHORIZED, OrderStatus.RET_PICKUP_REQUEST_RAISED, OrderStatus.RTO_IN_TRANSIT, OrderStatus.BILLED, OrderStatus.RECEIVED_AT_STORE]:
            raise TransactionServiceException(101, "Either wrong order id or invalid state " + str(orderId))
       
        # Provider is 4 is for self pickup and hardcoded. We should figure out a way to not to hard code.   
        if order.status == OrderStatus.BILLED and order.logistics_provider_id != 4:
            raise TransactionServiceException(101, "Order is not marked for self pickup: " + str(orderId))
             
        if order.status in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY, OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE, OrderStatus.RTO_IN_TRANSIT, OrderStatus.BILLED, OrderStatus.RECEIVED_AT_STORE]:
            order.delivery_timestamp = deliveryTimestamp
            if order.first_dlvyatmp_timestamp is None:
                order.first_dlvyatmp_timestamp = deliveryTimestamp
            order.receiver = receiver
        order.status = OrderStatus.DELIVERY_SUCCESS
        order.statusDescription = "Order delivered"
        if order.insuranceDetails:
            try:
                update_insurance_details(order)
            except:
                print "Error generating insurance file for order " + str(order.id)
                session.rollback()
                return
        if order.dataInsuranceDetails:
            order.dataInsuranceDetails[0].startDate = order.delivery_timestamp
            order.dataInsuranceDetails[0].expiryDate = order.delivery_timestamp + timedelta(days = 90)
        
        if order.source == 2:
            sod = StoreOrderDetail.get_by(orderId = order.id)
            sod.payStatus = StorePaymentStatus.FULL_PAY_RECEIVED
        
        if order.pickupStoreId and order.cod:
            __push_collection_to_hotspot(order, "SALE")
            
        for logisticsTxnId, ordersList in logisticsTxnIdOrdersMap.iteritems():
            enqueue_delivery_success_mail(logisticsTxnId, ordersList)
        
        
    #    if order.pickupStoreId:
    #        payment_client = PaymentClient().get_client()
    #        payment_client.createRefund(order.id, order.transaction.id, order.total_amount)
    #        payment_client = PaymentClient().get_client()
    #        payment_client.partiallyCapturePayment(order.transaction.id, order.total_amount, xferBy, xferTxnId, now())
    #        order.cod_reconciliation_timestamp = datetime.datetime.now()
    #        session.commit()
            
        try:
            alert_client = AlertClient().get_client()
            alert_client.endMonitoringEntity(EntityType.COURIER, "orderId = " + str(order.id))
        except Exception as e:
            print "Exception in ending alert in MarkOrderAsDelivered method"
            print e
                        
    update_trust_level(grouppedOrdersList[0])
    session.commit()

    
def mark_order_as_received_at_store(orderId, deliveryTimestamp):
    order = Order.get_by(id=orderId)
    if order != None or order.status in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY, OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE, OrderStatus.DELIVERED_AT_STORE]:
        order.status = OrderStatus.RECEIVED_AT_STORE
        order.statusDescription = "Order received at store"
        order.delivery_timestamp = deliveryTimestamp
    session.commit()
    try:
        alert_client = AlertClient().get_client()
        alert_client.endMonitoringEntity(EntityType.COURIER, "orderId = " + str(order.id))
    except Exception as e:
        print e
        
    try:
        enqueue_received_at_store_email(order)
    except Exception as e:
        print e


        
def mark_orders_as_rto(provider_id, returned_orders):
    for awb, detail in returned_orders.iteritems():
        orders = None
        orders = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).all()
        
        if orders == None or len(orders)==0:
            continue
        for order in orders:   
            if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.DELIVERED_AT_STORE, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY, OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE]:
                continue
                #raise TransactionServiceException(103, "No order found for the awb:" + awb)
            order.status = OrderStatus.RTO_IN_TRANSIT
            order.delivery_timestamp, reason = detail.split('|')
            order.statusDescription = "Order Returned to Origin:" + reason
            try:
                alert_client = AlertClient().get_client()
                alert_client.endMonitoringEntity(EntityType.COURIER, "orderId = " + str(order.id))
            except Exception as e:
                print "Exception in ending alert in MarkOrderAsRTO method"
                print e
        
        update_trust_level(orders[0])        
        session.commit()

def get_rto_orders(provider_id):
    current_time = datetime.datetime.now()
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
    rto_orders = Order.query.filter_by(logistics_provider_id = provider_id).filter_by(status=OrderStatus.RTO_IN_TRANSIT).filter(Order.delivery_timestamp <= to_datetime).all()
    return rto_orders

def get_receive_pending_orders(storeId):
    return Order.query.filter_by(pickupStoreId = storeId).filter(Order.status.in_((OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY, OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE, OrderStatus.DELIVERED_AT_STORE))).all()

def get_received_at_store_orders(storeId):
    return Order.query.filter_by(pickupStoreId = storeId).filter(Order.status.in_((OrderStatus.RECEIVED_AT_STORE, OrderStatus.RET_PICKUP_REQUEST_RAISED))).all()

def get_orders_collection_at_store(storeId, fromDate, toDate, onlyCod):
    query = Order.query.filter(Order.status == OrderStatus.DELIVERY_SUCCESS)
    if storeId:
        query = query.filter_by(pickupStoreId = storeId)
    else:
        query = query.filter(Order.pickupStoreId != 0)
    if onlyCod:
        query = query.filter_by(cod = onlyCod)
    if fromDate:
        query = query.filter(Order.delivery_timestamp >= fromDate)
    if toDate:
        query = query.filter(Order.delivery_timestamp <= toDate)
    return query.all()

def update_non_delivery_reason(provider_id, undelivered_orders):
    for awb, reason in undelivered_orders.iteritems():
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
        if order == None or order.status == OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE:
            continue
            #raise TransactionServiceException(103, "No order found for the awb:" + awb)
        order.statusDescription = reason
    session.commit()

def get_non_delivered_orders_by_courier(provider_id):
    current_time = datetime.datetime.now()
    today_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
    upto_datetime = today_datetime - datetime.timedelta(4)
    orders_not_delivered = Order.query.filter_by(logistics_provider_id = provider_id).filter(Order.status.in_((OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY, OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE))).filter(or_(Order.shipping_timestamp <= upto_datetime, Order.pickup_timestamp <= upto_datetime)).all()
    return orders_not_delivered

def mark_orders_as_local_connected(provider_id, local_connected_orders):
    for awb, timestamp in local_connected_orders.iteritems():
        orders = None
        orders = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).all()
        
        if orders == None or len(orders)==0:
            continue
        
        for order in orders:
            if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST]:
                #raise TransactionServiceException(102, "No order found for the awb: " + awb)
                continue
            order.status = OrderStatus.SHIPPED_TO_DESTINATION_CITY
            order.statusDescription = "Left Out of Origin City"
            current_time = datetime.datetime.now()
            order.local_connected_timestamp = datetime.datetime(current_time.year, current_time.month, current_time.day)
            try:
                monitoredEntity = MonitoredEntity();
                monitoredEntity.entityType=EntityType.COURIER;
                monitoredEntity.eventType=OrderStatus.SHIPPED_TO_DESTINATION_CITY
                monitoredEntity.entityIdentifier="orderId = " + str(order.id);
    
                sec = datetime.datetime.now()
                #Warn alert time is taken as 14 hrs less than expected delivery time
                warn_time1 = (order.expected_delivery_time-timedelta(hours = 14))
                warn_time2 = sec + timedelta(order.expected_delivery_time - order.expected_shipping_timestamp) - timedelta(sec - order.shipping_time)
                warn_time = max(warn_time1, warn_time2)
                
                #Critical alert time is taken as 13 hrs less than promised delivery time                    
                critical_time1 = (order.expected_delivery_time-timedelta(hours = 10))
                critical_time2  = sec + timedelta(order.expected_delivery_time - order.expected_shipping_timestamp) - timedelta(sec - order.shipping_time) + timedelta(hours = 5)
                critical_time = max(critical_time1, critical_time2)
                
                monitoredEntity.warnExpiryTime = int(warn_time.strftime("%s"))*1000
                monitoredEntity.criticalExpiryTime = int(critical_time.strftime("%s"))*1000
                monitoredEntity.description="providerId = " + str(provider_id) + " destination city " + order.customer_city 
                alert_client = AlertClient().get_client()
                alert_client.updateMonitoredObject(monitoredEntity)
            except Exception as e:
                print "Exception in updating alert in MarkOrderAsLocalConnected method"
                print e
            session.commit()

def get_orders_not_local_connected(provider_id):
    current_time = datetime.datetime.now()
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
    orders_pending_local_connection = Order.query.filter_by(logistics_provider_id = provider_id).filter(Order.status.in_((OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST))).filter(or_(Order.shipping_timestamp <= to_datetime, Order.pickup_timestamp <= to_datetime)).all()
    return orders_pending_local_connection

def mark_orders_as_destinationCityReached(provider_id, destination_city_reached_orders):
    for awb, timestamp in destination_city_reached_orders.iteritems():
        orders = None
        orders = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).all()
        
        if orders == None or len(orders)==0:
            continue
        
        for order in orders:
            if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY]:
                continue
                #raise TransactionServiceException(103, "No order found for the awb:" + awb)
            order.status = OrderStatus.REACHED_DESTINATION_CITY
            order.statusDescription = "Reached Destination City"
            order.reached_destination_timestamp = timestamp
            try:
                monitoredEntity = MonitoredEntity();
                monitoredEntity.entityType=EntityType.COURIER;
                monitoredEntity.eventType=OrderStatus.REACHED_DESTINATION_CITY
                monitoredEntity.entityIdentifier="orderId = " + str(order.id);
        
                sec = datetime.datetime.now()
                #Warn alert time is taken as expected delivery time
                warn_time1 = (order.expected_delivery_time)
                warn_time2 = sec + timedelta(order.expected_delivery_time - order.expected_shipping_time) - timedelta(sec - order.shipping_time)
                warn_time = max(warn_time1, warn_time2)
                
                #Critical alert time is taken as 13 hrs less than promised delivery time                    
                critical_time1 = (order.expected_delivery_time + timedelta(hours = 5))
                critical_time2  = sec + timedelta(order.expected_delivery_time - order.expected_shipping_time) - timedelta(sec - order.shipping_time) + timedelta(hours = 5)
                critical_time = max(critical_time1, critical_time2)
                
                monitoredEntity.warnExpiryTime = int(warn_time.strftime("%s"))*1000
                monitoredEntity.criticalExpiryTime = int(critical_time.strftime("%s"))*1000
                monitoredEntity.description="providerId = " + str(provider_id) + " destination city " + order.customer_city 
                alert_client = AlertClient().get_client()
                alert_client.updateMonitoredObject(monitoredEntity)
            except Exception as e:
                print "Exception in updating alert in MarkOrderAsDestCityReached method"
                print e
        session.commit()

def mark_orders_as_firstDeliveryAttempted(provider_id, first_atdl_orders):
    for awb, detail in first_atdl_orders.iteritems():
        orders = None
        orders = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).all()
        
        if orders == None or len(orders)==0:
            continue
        
        for order in orders:
            if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY]:
                continue
                #raise TransactionServiceException(103, "No order found for the awb:" + awb)
            order.status = OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE
            order.first_dlvyatmp_timestamp, reason = detail.split('|')
            order.statusDescription = reason
            try:
                monitoredEntity = MonitoredEntity();
                monitoredEntity.entityType=EntityType.COURIER;
                monitoredEntity.eventType=OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE
                monitoredEntity.entityIdentifier="orderId = " + str(order.id);
                adjustedDeliveryDaysfor1day = adjust_delivery_time(datetime.datetime.now(), 1);
                adjustedDeliveryDaysfor2days = adjust_delivery_time(datetime.datetime.now(), 2);
                sec = datetime.datetime.now()
                #Warn alert time is taken as 16 hrs
                warn_time = (sec + timedelta(days=(adjustedDeliveryDaysfor1day-1)) + timedelta(hours=24))
                #Critical alert time is taken as 26 hrs                    
                critical_time = (sec + timedelta(days=(adjustedDeliveryDaysfor2days-2)) + timedelta(hours=48))
                monitoredEntity.warnExpiryTime = int(warn_time.strftime("%s"))*1000
                monitoredEntity.criticalExpiryTime = int(critical_time.strftime("%s"))*1000
                monitoredEntity.description="providerId = " + str(provider_id) + " first attempt timestamp" + order.first_dlvyatmp_timestamp
                alert_client = AlertClient().get_client()
                alert_client.updateMonitoredObject(monitoredEntity)
            except Exception as e:
                print "Exception in updating alert in MarkOrdersAsFirstDeliveryAttempted method"
                print e
        session.commit()

def get_orders_not_met_expected_delivery_date():
    current_time = datetime.datetime.now()
    today_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
    orders_not_delivered = Order.query.filter(Order.status.in_((OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY))).filter(Order.expected_delivery_time <= today_datetime).all()
    return orders_not_delivered

def get_alerts(type, warehouseId, status, timestamp):
    query = Alert.query.filter(Alert.warehouseId == warehouseId)
    if type != -1 :
        query = query.filter(Alert.type == type)
    if status != -1 :
        query = query.filter(Alert.status == status)
    if timestamp:
        query = query.filter(Alert.timestamp >= to_py_date(timestamp))
    query = query.order_by(desc(Alert.timestamp))
    return query.all()

def add_alert(type, warehouseId, description):
    alert = Alert()
    alert.type = type
    alert.status = 1
    alert.description = description
    alert.timestamp = datetime.datetime.now()
    alert.warehouseId = warehouseId
    session.commit()

def mark_alerts_as_seen(warehouseId):
    query = Alert.query.filter(Alert.warehouseId == warehouseId).filter(Alert.status == 1)
    alerts = query.all()
    for alert in alerts:
        alert.status = 0    
    session.commit()
    
def get_valid_order_count():
    '''
    Returns the number of orders which we processed. The reshipped orders are not
    counted since there'll always be a new compensating order present.
    '''
    return Order.query.filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).filter(not_(Order.status.in_((OrderStatus.RTO_RESHIPPED, OrderStatus.DOA_INVALID_RESHIPPED, OrderStatus.DOA_VALID_RESHIPPED, OrderStatus.COD_VERIFICATION_FAILED)))).count()

def get_cust_count_with_successful_txn():
    '''
    Returns the number of distinct customers who have done successful transactions.
    It uses the sqlalchemy func module to construct an efficient query.
    '''
    return session.query(func.count(distinct(Order.customer_id))).filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).scalar()

def get_valid_orders_amount_range():
    '''
    Returns a list containing the minimum and maximum amount across all
    orders placed with us. No need to consider reshippped orders for min/max
    amount of orders.
    '''
    return session.query(func.min(Order.total_amount), func.max(Order.total_amount)).filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).one()

def get_valid_orders(limit, onlyStore):
    '''
    Returns the last limit number of valid orders placed with us. Ignored the
    limit if it's passed as 0. Raises an excpetion if the supplied limit is 
    less than 0. 
    '''
    if limit < 0:
        raise TransactionServiceException(101, "Invalid argument limit " + limit)
    query = Order.query.filter(Order.status >= OrderStatus.COD_VERIFICATION_PENDING)
    if onlyStore:
        query = query.filter(Order.pickupStoreId != 0)
    query = query.order_by(desc(Order.created_timestamp))
    if limit != 0:
        query = query.limit(limit)
    return query.all()

def get_next_invoice_number(orderType):
    if OrderType.B2Cbulk == orderType:
        orderType = OrderType.B2C
    entity_id = InvoiceIDGenerator.query.filter(InvoiceIDGenerator.orderType == orderType).with_lockmode("update").one()
    invoice_number = entity_id.id + 1
    entity_id.id = invoice_number
    return invoice_number

def get_next_invoice_counter(seller_id, warehouse_id, hsnCode):
    #Sequence would now be maintained through single order for seller
    #entity_id = InvoiceCounterGenerator.query.filter(InvoiceCounterGenerator.orderType == orderType).filter(InvoiceCounterGenerator.stateId == stateId).filter(InvoiceCounterGenerator.companyId==companyId).with_lockmode("update").one()
    entity_id = SellerWarehouse.query.filter(SellerWarehouse.warehouse_id == warehouse_id).filter(SellerWarehouse.seller_id==seller_id).with_lockmode("update").one()
    prefix = entity_id.prefix
    if hsnCode=="NOGST":
        document_number = entity_id.challan_id + 1
        entity_id.challan_id = document_number
        prefix = 'CHLN-' + prefix
    else:
        document_number = entity_id.id + 1
        entity_id.id = document_number 
    ret_invoiceNo = prefix + str(document_number) 
    return ret_invoiceNo

def toggle_doa_flag(order_id):
    order = get_order(order_id)
    if(order.doaFlag):
        order.doaFlag = False
    else:
        order.doaFlag = True
    session.commit()
    return order.doaFlag

def mark_order_doa_request_received(orderId):
    order = get_order(orderId)
    if order.status != OrderStatus.DELIVERY_SUCCESS:
        return False
    order.status = OrderStatus.DOA_REQUEST_RECEIVED
    order.statusDescription = "DOA Request Received"
    session.commit()
    return True

def mark_order_doa_request_authorized(orderId, isAuthorized, fromStore, isReship):
    order = get_order(orderId)
    if order.status != OrderStatus.DOA_REQUEST_RECEIVED:
        return False
    if isAuthorized:
        order.status = OrderStatus.DOA_REQUEST_AUTHORIZED
        order.statusDescription = "DOA Request Authorized"
        if order.source == 2:
            if fromStore:
                if isReship:
                    new_order = __clone_order(order, False, False)
                    order.reship_timestamp = datetime.datetime.now()
                    order.new_order_id = new_order.id
                else:
                    sod = StoreOrderDetail.get_by(orderId = order.id)
                    if sod.cashAmount > 0:
                        __push_store_collection_to_hotspot(order, "RefundFromStore", -sod.cashAmount, 0)
                        
                    if sod.cardAmount == 0:
                        sod.payStatus = StorePaymentStatus.ADV_REFUNDED
                    else:
                        sod.payStatus = StorePaymentStatus.REFUND_REQUESTED
                    
                    order.refund_timestamp = datetime.datetime.now()
                    order.refunded_by = "CRM Outbound Team"
                    sod.cashRefundAmount = sod.cashAmount
                    sod.cardRefundAmount = sod.cardAmount
    else:
        order.status = OrderStatus.DELIVERY_SUCCESS
        order.statusDescription = "Order delivered"
    session.commit()
    return True
    
def mark_order_return_request_received(orderId):
    order = get_order(orderId)
    if order.status != OrderStatus.DELIVERY_SUCCESS:
        return False
    order.status = OrderStatus.RET_REQUEST_RECEIVED
    order.statusDescription = "Return Request Received"
    session.commit()
    return True

def mark_order_return_request_authorized(orderId, isAuthorized, fromStore, isReship):
    order = get_order(orderId)
    if order.status != OrderStatus.RET_REQUEST_RECEIVED:
        return False
    
    if isAuthorized:
        order.status = OrderStatus.RET_REQUEST_AUTHORIZED
        order.statusDescription = "Return Request Authorized"
        if order.source == 2:
            if fromStore:
                if isReship:
                    new_order = __clone_order(order, False, False)
                    order.reship_timestamp = datetime.datetime.now()
                    order.new_order_id = new_order.id
                else:
                    sod = StoreOrderDetail.get_by(orderId = order.id)
                    if sod.cashAmount > 0:
                        __push_store_collection_to_hotspot(order, "RefundFromStore", -sod.cashAmount, 0)
                        
                    if sod.cardAmount == 0:
                        sod.payStatus = StorePaymentStatus.ADV_REFUNDED
                    else:
                        sod.payStatus = StorePaymentStatus.REFUND_REQUESTED
                        
                    order.refund_timestamp = datetime.datetime.now()
                    order.refunded_by = "CRM Outbound Team"
                    sod.cashRefundAmount = sod.cashAmount
                    sod.cardRefundAmount = sod.cardAmount
    else:
        order.status = OrderStatus.DELIVERY_SUCCESS
        order.statusDescription = "Order delivered"
    session.commit()
    return True
    
def request_pickup_number(order_id, providerId):
    order = get_order(order_id)
    if order.status != OrderStatus.DELIVERY_SUCCESS and order.status != OrderStatus.DOA_REQUEST_AUTHORIZED and order.status != OrderStatus.RET_REQUEST_AUTHORIZED:
        return False
    if order.status == OrderStatus.RET_REQUEST_AUTHORIZED:
        order.status = OrderStatus.RET_PICKUP_REQUEST_RAISED
        order.statusDescription = "Pick up requested for RETURN"
    else:
        order.status = OrderStatus.DOA_PICKUP_REQUEST_RAISED
        order.statusDescription = "Pick up requested for DOA"
    order.doa_logistics_provider_id = providerId
    session.commit()
    return True

def authorize_pickup(order_id, pickup_number, providerId):
    order = get_order(order_id)
    if order.status not in [OrderStatus.DOA_PICKUP_REQUEST_RAISED, OrderStatus.RET_PICKUP_REQUEST_RAISED]:
        return False
    if order.doa_logistics_provider_id is not None:
        providerId = order.doa_logistics_provider_id    #previously we are not storing this so data is taken from omdashboarduser
    subject = 'Pickup details'
    filename = "/tmp/" +  str(order.id) +".pdf" 
    try:
        inventory_client = InventoryClient().get_client()
        warehouse = inventory_client.getWarehouse(order.warehouse_id)
        
        logistics_client = LogisticsClient().get_client()
        provider = logistics_client.getProvider(providerId)
        provider_name = provider.name
        
        to_addr = order.customer_email
        today = date.today().strftime("%d-%B-%Y (%A)")

        raw_message = '''
        Dear %(customer_name)s,
        
        Would like to inform you that we have instructed %(provider_name)s to pick up the material from the below mentioned address on %(date)s. %(provider_name)s pickup request number is %(pickup_request_no)s.'
        
        Take a printout of the attachment in this mail which contains the delivery address and paste it on the packed parcel. Kindly keep the parcel ready.

        Pickup will happen from the below mentioned address:-

        %(customer_address)s

        Do let us know in case of any concerns

        Thanks & Regards
        %(source_name)s Team
        '''
        message = dedent(raw_message) % {'customer_name' : order.customer_name,
                                         'order_id' : order_id,
                                         'provider_name' : provider_name,
                                         'date': today,
                                         'pickup_request_no' : pickup_number,
                                         'customer_address' : __get_order_address(order),
                                         'source_name' : source_name}
        print message
        __generate_return_advice(order, warehouse, provider, filename)
        if order.source in [OrderSource.WEBSITE, OrderSource.AMAZON, OrderSource.JUNGLEE]:
            mail(help_user, help_password, [to_addr], subject, message, [get_attachment_part(filename)])
        order.pickupRequestNo = pickup_number
        if order.status == OrderStatus.RET_PICKUP_REQUEST_RAISED:
            order.status = OrderStatus.RET_PICKUP_CONFIRMED
            order.statusDescription = "RETURN pick up confirmed"
        else:
            order.status = OrderStatus.DOA_PICKUP_CONFIRMED
            order.statusDescription = "DOA pick up confirmed"
        order.doa_auth_timestamp = datetime.datetime.now()
        session.commit()
        return True
    except Exception as e:
        print sys.exc_info()
        traceback.print_tb(sys.exc_info()[2])
        return False
    finally:
        if os.path.exists(filename):
            os.remove(filename)

def mark_doas_as_picked_up(provider_id, pickup_details):
    for pickupNo, doa_pickup_timestamp in pickup_details.iteritems():
        order = Order.query.filter_by(pickupRequestNo=pickupNo, doa_logistics_provider_id = provider_id).first()
        if order == None or order.status != OrderStatus.DOA_PICKUP_CONFIRMED:
            #raise TransactionServiceException(102, "No order found for the awb: " + awb)
            continue
        order.status = OrderStatus.DOA_RETURN_IN_TRANSIT
        order.statusDescription = "DOA picked up by Courier Company"
        order.doa_pickup_timestamp = doa_pickup_timestamp
    session.commit()

def get_doas_not_picked_up(provider_id):
    current_time = datetime.datetime.now()
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
    orders_not_picked_up = Order.query.filter_by(doa_logistics_provider_id = provider_id).filter_by(status=OrderStatus.DOA_PICKUP_CONFIRMED).filter(Order.doa_auth_timestamp <= to_datetime).all()
    return orders_not_picked_up

def mark_return_orders_as_picked_up(provider_id, pickup_details):
    for pickupNo, return_order_pickup_timestamp in pickup_details.iteritems():
        order = Order.query.filter_by(pickupRequestNo=pickupNo, doa_logistics_provider_id = provider_id).first()
        if order == None or order.status != OrderStatus.RET_PICKUP_CONFIRMED:
            #raise TransactionServiceException(102, "No order found for the awb: " + awb)
            continue
        order.status = OrderStatus.RET_RETURN_IN_TRANSIT
        order.statusDescription = "Return order picked up by Courier Company"
        order.doa_pickup_timestamp = return_order_pickup_timestamp
    session.commit()

def get_return_orders_not_picked_up(provider_id):
    current_time = datetime.datetime.now()
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
    orders_not_picked_up = Order.query.filter_by(doa_logistics_provider_id = provider_id).filter_by(status=OrderStatus.RET_PICKUP_CONFIRMED).filter(Order.doa_auth_timestamp <= to_datetime).all()
    return orders_not_picked_up

def receive_return(order_id, receiveCondition, receiveFreebie, serialNumbers):
    order = get_order(order_id)
    scanFreebie = False
    grouppedOrdersList = []
    if order.logisticsTransactionId:
        grouppedOrdersList = get_group_orders_by_logistics_txn_id(order.logisticsTransactionId)
    else:
        grouppedOrdersList.append(order)
    orderCurrentStatus = order.status

    if order.status in [OrderStatus.DOA_PICKUP_CONFIRMED, OrderStatus.DOA_RETURN_IN_TRANSIT]:
        if receiveCondition == 0:
            order.status = OrderStatus.DOA_RECEIVED_PRESTINE
            order.statusDescription = "DOA package received"
            scanFreebie = receiveFreebie
        elif receiveCondition == 1:
            order.status = OrderStatus.DOA_RECEIVED_DAMAGED
            order.statusDescription = "DOA received damaged"
        elif receiveCondition == 2:
            order.status = OrderStatus.DOA_LOST_IN_TRANSIT
            order.statusDescription = "DOA lost in transit"
        order.received_return_timestamp = datetime.datetime.now()
    elif order.status in [OrderStatus.RET_PICKUP_CONFIRMED, OrderStatus.RET_RETURN_IN_TRANSIT]:
        if receiveCondition == 0:
            order.status = OrderStatus.RET_RECEIVED_PRESTINE
            order.statusDescription = "RETURN package received"
            scanFreebie = receiveFreebie
        elif receiveCondition == 1:
            order.status = OrderStatus.RET_RECEIVED_DAMAGED
            order.statusDescription = "RETURN received damaged"
        elif receiveCondition == 2:
            order.status = OrderStatus.RET_LOST_IN_TRANSIT
            order.statusDescription = "RETURN lost in transit"
        order.received_return_timestamp = datetime.datetime.now()
    elif order.status == OrderStatus.RTO_IN_TRANSIT :
        for orderObj in grouppedOrdersList:
            if receiveCondition == 0:
                orderObj.status = OrderStatus.RTO_RECEIVED_PRESTINE
                orderObj.statusDescription = "Returned to origin"
                if orderObj.freebieItemId:
                    scanFreebie = True
            elif receiveCondition == 1:
                orderObj.status = OrderStatus.RTO_RECEIVED_DAMAGED
                orderObj.statusDescription = "RTO received damaged"
            elif receiveCondition == 2:
                orderObj.status = OrderStatus.RTO_LOST_IN_TRANSIT
                orderObj.statusDescription = "RTO lost in transit"
            orderObj.received_return_timestamp = datetime.datetime.now()
       
    else:
        return False

    # For OUR warehouses, we need to scan in items for every return
    inventoryClient = InventoryClient().get_client()
    warehouse = inventoryClient.getWarehouse(order.warehouse_id)
    if warehouse.billingType == BillingType.OURS or warehouse.billingType == BillingType.OURS_EXTERNAL:
        scanMap = {
            OrderStatus.RTO_RECEIVED_PRESTINE : ScanType.SALE_RET,
            OrderStatus.RTO_RECEIVED_DAMAGED  : ScanType.SALE_RET_UNUSABLE,
            OrderStatus.RTO_LOST_IN_TRANSIT   : ScanType.LOST_IN_TRANSIT,
            OrderStatus.DOA_RECEIVED_PRESTINE : ScanType.DOA_IN,
            OrderStatus.DOA_RECEIVED_DAMAGED  : ScanType.DOA_IN,
            OrderStatus.DOA_LOST_IN_TRANSIT   : ScanType.LOST_IN_TRANSIT
        }
        if orderCurrentStatus == OrderStatus.RTO_IN_TRANSIT:
            order = grouppedOrdersList[0]
            
            if order.transaction.payment_option == capitalFloatPayMethod:
                total_amount = 0
                for ordObj in grouppedOrdersList:
                    total_amount = total_amount + ordObj.total_amount + ordObj.shippingCost - ordObj.gvAmount
                
                creditObj = __creditHistoryObj(order.customer_id, 1, order.transaction.id, total_amount, CreditTxnType.BLOCKED_REVERSED, order.logisticsTransactionId)
                creditTxns = []
                creditTxns.append(creditObj)
                try:
                    process_credit_transaction(order.transaction.id, order.customer_id, 1, creditTxns)
                except:
                    traceback.print_exc()
                    session.rollback()
                    return False
                
            for orderObj in grouppedOrdersList:
                if scanMap.has_key(orderObj.status):
                    scanType = scanMap[orderObj.status]
                    lineitem = orderObj.lineitems[0]
                    catalogClient = CatalogClient().get_client()
                    item = catalogClient.getItem(lineitem.item_id)
                    warehouseClient = WarehouseClient().get_client()
                    if warehouse.billingType == BillingType.OURS or scanType != ScanType.SALE_RET:
                        if item.type == ItemType.SERIALIZED:
                            if lineitem.quantity > 1:
                                serialNoList = lineitem.serial_number.split(',')
                                for serialNumber in serialNoList:
                                    warehouseClient.scanSerializedItemForOrder(serialNumber, scanType, orderObj.id, orderObj.fulfilmentWarehouseId, 1, orderObj.warehouse_id)
                            else:
                                warehouseClient.scanSerializedItemForOrder(lineitem.serial_number, scanType, orderObj.id, orderObj.fulfilmentWarehouseId, lineitem.quantity, orderObj.warehouse_id)
                        else:
                            warehouseClient.scanForOrder(None, scanType, lineitem.quantity, orderObj.id, orderObj.fulfilmentWarehouseId, orderObj.warehouse_id)
                    if warehouse.billingType == BillingType.OURS_EXTERNAL and scanType == ScanType.SALE_RET:
                        warehouseClient.scanForOursExternalSaleReturn(orderObj.id, lineitem.transfer_price)
                    if scanFreebie:
                        warehouseClient.scanfreebie(orderObj.id, orderObj.freebieItemId, 0, scanType)
        else:
            if scanMap.has_key(order.status):
                scanType = scanMap[order.status]
                lineitem = order.lineitems[0]
                catalogClient = CatalogClient().get_client()
                item = catalogClient.getItem(lineitem.item_id)
                warehouseClient = WarehouseClient().get_client()
                if warehouse.billingType == BillingType.OURS or scanType != ScanType.SALE_RET:
                    if item.type == ItemType.SERIALIZED:
                        if lineitem.quantity > 1:
                            if serialNumbers is None or len(serialNumbers)==0:
                                return False
                            else:
                                serialNoList = serialNumbers.split(',')
                                for serialNumber in serialNoList:
                                    warehouseClient.scanSerializedItemForOrder(serialNumber, scanType, order.id, order.fulfilmentWarehouseId, 1, order.warehouse_id)
                        else:
                            warehouseClient.scanSerializedItemForOrder(lineitem.serial_number, scanType, order.id, order.fulfilmentWarehouseId, lineitem.quantity, order.warehouse_id)
                    else:
                        warehouseClient.scanForOrder(None, scanType, lineitem.quantity, order.id, order.fulfilmentWarehouseId, order.warehouse_id)
                if warehouse.billingType == BillingType.OURS_EXTERNAL and scanType == ScanType.SALE_RET:
                    warehouseClient.scanForOursExternalSaleReturn(order.id, lineitem.transfer_price)
                if scanFreebie:
                    warehouseClient.scanfreebie(order.id, order.freebieItemId, 0, scanType)
    
    session.commit()  
    return True

def validate_doa(order_id, is_valid):
    order = get_order(order_id)
    if order.status != OrderStatus.DOA_RECEIVED_PRESTINE:
        return False
    if is_valid:
        order.status = OrderStatus.DOA_CERT_VALID
        order.statusDescription = "DOA Certificate Valid"
    else:
        order.status = OrderStatus.DOA_CERT_INVALID
        order.statusDescription = "DOA Certificate Invalid"
        update_trust_level(order)
    session.commit()
    return True

def validate_return_product(order_id, is_usable):
    order = get_order(order_id)
    if order.status != OrderStatus.RET_RECEIVED_PRESTINE:
        return False

    scanType = ScanType.SALE_RET_UNUSABLE
    if is_usable:
        order.status = OrderStatus.RET_PRODUCT_USABLE
        order.statusDescription = "Return product usable"
        scanType = ScanType.SALE_RET
    else:
        order.status = OrderStatus.RET_PRODUCT_UNUSABLE
        order.statusDescription = "Return product unusable"
        update_trust_level(order)
    session.commit()

    # For OUR warehouses, we need to scan in items for every return
    inventoryClient = InventoryClient().get_client()
    warehouse = inventoryClient.getWarehouse(order.warehouse_id)
    if warehouse.billingType == BillingType.OURS or warehouse.billingType == BillingType.OURS_EXTERNAL:
        lineitem = order.lineitems[0]
        catalogClient = CatalogClient().get_client()
        item = catalogClient.getItem(lineitem.item_id)
        warehouse_client = WarehouseClient().get_client()
        if warehouse.billingType == BillingType.OURS or scanType != ScanType.SALE_RET:
            if item.type == ItemType.SERIALIZED:
                warehouse_client.scanSerializedItemForOrder(lineitem.serial_number, scanType, order.id, order.fulfilmentWarehouseId, lineitem.quantity, order.warehouse_id)
            else:
                warehouse_client.scanForOrder(None, scanType, lineitem.quantity, order.id, order.fulfilmentWarehouseId, order.warehouse_id)
        if scanType == ScanType.SALE_RET and warehouse.billingType == BillingType.OURS_EXTERNAL:
            warehouse_client.scanForOursExternalSaleReturn(order.id, lineitem.transfer_price)        
    return True

def reship_order(order_id):
    """
    If the order is in RTO_RECEIVED_PRESTINE or DOA_CERT_INVALID state, it does the following:
        1. Creates a new order for processing in the BILLED state. All billing information is saved.
        2. Marks the current order as one of the final states RTO_RESHIPPED and DOA_INVALID_RESHIPPED depending on what state the order started in.
        
    If the order is in DOA_CERT_VALID state, it does the following:
        1. Creates a new order for processing in the SUBMITTED_FOR_PROCESSING state.
        2. Creates a return order for the warehouse executive to return the DOA material.
        3. Marks the current order as the final DOA_VALID_RESHIPPED state.
    
    Returns the id of the newly created order.
    
    Throws an exception if the order with the given id couldn't be found.
    
    Parameters:
     - orderId
    """
    order = get_order(order_id)
    alreadyReshipped = False
    
    #If this order was from store and it was already marked refunded then this order 
    #should not be reshipped.
    if order.source == 2:
        sod = StoreOrderDetail.get_by(orderId = order.id)
        if sod.payStatus in (StorePaymentStatus.ADV_REFUNDED , StorePaymentStatus.REFUND_INITIATED, \
                                          StorePaymentStatus.REFUND_REQUESTED, StorePaymentStatus.REFUNDED):
            raise TransactionServiceException(112, "This order should be refunded")
    
    #If this order was from store and a new order Id has already been set then it means that
    #a new order corresponding to this order has already been shipped.
    if order.source == 2 and order.new_order_id:
        alreadyReshipped = True
    if order.status == OrderStatus.RTO_RECEIVED_PRESTINE:
        order.status = OrderStatus.RTO_RESHIPPED
        order.statusDescription = "Order Reshipped"
        if not alreadyReshipped:
            new_order = __clone_order(order, True, order.cod)
        update_trust_level(order)
        __scan_for_reship_order(order_id, new_order)
    elif order.status == OrderStatus.RTO_RECEIVED_DAMAGED:
        order.status = OrderStatus.RTO_DAMAGED_RESHIPPED
        order.statusDescription = "Order Reshipped Received Damaged"
        if not alreadyReshipped:
            new_order = __clone_order(order, False, order.cod)
        ret_order = __create_return_order(order)
    elif order.status == OrderStatus.RTO_LOST_IN_TRANSIT:
        order.status = OrderStatus.RTO_LOST_IN_TRANSIT_RESHIPPED
        order.statusDescription = "Order Reshipped Lost In Transit"
        if not alreadyReshipped:
            new_order = __clone_order(order, False, order.cod)
    elif order.status == OrderStatus.LOST_IN_TRANSIT:
        order.status = OrderStatus.LOST_IN_TRANSIT_RESHIPPED
        order.statusDescription = "Order Reshipped Lost In Transit"
        if not alreadyReshipped:
            new_order = __clone_order(order, False, order.cod)
    elif order.status == OrderStatus.DOA_CERT_INVALID:
        order.status = OrderStatus.DOA_INVALID_RESHIPPED
        order.statusDescription = "Order Reshipped"
        if not alreadyReshipped:
            new_order = __clone_order(order, True, False)
        __scan_for_reship_order(order_id, new_order)
    elif order.status == OrderStatus.DOA_CERT_VALID:
        order.status = OrderStatus.DOA_VALID_RESHIPPED
        order.statusDescription = "Order Reshipped"
        if not alreadyReshipped:
            new_order = __clone_order(order, False, False)
        ret_order = __create_return_order(order)
    elif order.status == OrderStatus.DOA_RECEIVED_DAMAGED:
        order.status = OrderStatus.DOA_RESHIPPED_RCVD_DAMAGED
        order.statusDescription = "Order Reshipped Received Damaged"
        if not alreadyReshipped:
            new_order = __clone_order(order, False, False)
        ret_order = __create_return_order(order)
    elif order.status == OrderStatus.DOA_LOST_IN_TRANSIT:
        order.status = OrderStatus.DOA_RESHIPPED_LOST_IN_TRANSIT
        order.statusDescription = "Order Reshipped Lost In Transit"
        if not alreadyReshipped:
            new_order = __clone_order(order, False, False)
    elif order.status == OrderStatus.RET_PRODUCT_USABLE:
        order.status = OrderStatus.RET_PRODUCT_USABLE_RESHIPPED
        order.statusDescription = "Order Reshipped"
        if not alreadyReshipped:
            new_order = __clone_order(order, False, False)
    elif order.status == OrderStatus.RET_PRODUCT_UNUSABLE:
        order.status = OrderStatus.RET_PRODUCT_UNUSABLE_RESHIPPED
        order.statusDescription = "Order Reshipped"
        if not alreadyReshipped:
            new_order = __clone_order(order, False, False)
        ret_order = __create_return_order(order)
    elif order.status == OrderStatus.RET_RECEIVED_DAMAGED:
        order.status = OrderStatus.RET_RESHIPPED_RCVD_DAMAGED
        order.statusDescription = "Order Reshipped Received Damaged"
        if not alreadyReshipped:
            new_order = __clone_order(order, False, False)
        ret_order = __create_return_order(order)
    elif order.status == OrderStatus.RET_LOST_IN_TRANSIT:
        order.status = OrderStatus.RET_RESHIPPED_LOST_IN_TRANSIT
        order.statusDescription = "Order Reshipped Lost In Transit"
        if not alreadyReshipped:
            new_order = __clone_order(order, False, False)
    else:
        raise TransactionServiceException(112, "This order can't be reshipped")
    
    if not alreadyReshipped:
        order.reship_timestamp = datetime.datetime.now()
        order.new_order_id = new_order.id
        session.commit()
        return new_order.id
    else:
        session.commit()
        return order.new_order_id
    

def __scan_for_reship_order(order_id, new_order):
    inventoryClient = InventoryClient().get_client()
    warehouse = inventoryClient.getWarehouse(new_order.warehouse_id)
    if warehouse.billingType == BillingType.OURS:
        warehouseClient = WarehouseClient().get_client()
        inventoryItem = warehouseClient.getInventoryItemFromOrder(order_id)
        warehouseClient.scanForOrder(inventoryItem, ScanType.SALE, new_order.lineitems[0].quantity, new_order.id, new_order.fulfilmentWarehouseId, new_order.warehouse_id)
    if new_order.freebieItemId:
        warehouseClient = WarehouseClient().get_client()
        inventoryItem = warehouseClient.reshipfreebie(order_id, new_order.id, new_order.freebieItemId, ScanType.SALE)
    
def refund_order(order_id, refunded_by, reason):
    """
    If the order is in RTO_RECEIVED_PRESTINE, DOA_CERT_VALID or DOA_CERT_INVALID state, it does the following:
        1. Creates a refund request for batch processing.
        2. Creates a return order for the warehouse executive to return the shipped material.
        3. Marks the current order as RTO_REFUNDED, DOA_VALID_REFUNDED or DOA_INVALID_REFUNDED final states.
    
    If the order is in SUBMITTED_FOR_PROCESSING or INVENTORY_LOW state, it does the following:
        1. Creates a refund request for batch processing.
        2. Cancels the reservation of the item in the warehouse.
        3. Marks the current order as the REFUNDED final state.
    
    For all COD orders, if the order is in INIT, SUBMITTED_FOR_PROCESSING or INVENTORY_LOW state, it does the following:
        1. Cancels the reservation of the item in the warehouse.
        2. Marks the current order as CANCELED.
        
    In all cases, it updates the reason for cancellation or refund and the person who performed the action.
     
    Returns True if it is successful, False otherwise.
    
    Throws an exception if the order with the given id couldn't be found.
    
    Parameters:
     - order_id
     - refunded_by
     - reason
    """
    logging.info("Refunding order id: " + str(order_id))
    order = get_order(order_id)
    
    if order.cod:
        logging.info("Refunding COD order with status " + str(order.status))
        status_transition = refund_status_transition
        if order.status not in status_transition.keys():
            raise TransactionServiceException(114, "This order can't be refunded")
        
        if order.status in [OrderStatus.COD_VERIFICATION_PENDING, OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT, OrderStatus.ACCEPTED]:
            __update_inventory_reservation(order, refund=True)
            order.statusDescription = "Order Cancelled"
            #Shipment Id and Airway Bill No should be none in case of Cancellation
            order.logisticsTransactionId = None
            order.tracking_id = None
            order.airwaybill_no = None  
        elif order.status == OrderStatus.BILLED:
            __create_return_order(order)
            order.statusDescription = "Order Cancelled"
        elif order.status in [OrderStatus.RTO_RECEIVED_PRESTINE, OrderStatus.RTO_RECEIVED_DAMAGED, OrderStatus.RTO_LOST_IN_TRANSIT]:
            if order.status != OrderStatus.RTO_LOST_IN_TRANSIT:
                __create_return_order(order)
            order.statusDescription = "RTO Refunded"
        elif order.status in [OrderStatus.LOST_IN_TRANSIT]:
            #__create_return_order(order)
            order.statusDescription = "Lost in Transit Refunded"
        elif order.status in [OrderStatus.DOA_CERT_INVALID, OrderStatus.DOA_CERT_VALID, OrderStatus.DOA_RECEIVED_DAMAGED, OrderStatus.DOA_LOST_IN_TRANSIT] :
            if order.status != OrderStatus.DOA_LOST_IN_TRANSIT:
                __create_return_order(order)
            __create_refund(order, 0, 'Should be unreachable for now')
            order.statusDescription = "DOA Refunded"
        elif order.status in [OrderStatus.RET_PRODUCT_UNUSABLE, OrderStatus.RET_PRODUCT_USABLE, OrderStatus.RET_RECEIVED_DAMAGED, OrderStatus.RET_LOST_IN_TRANSIT] :
            if order.status != OrderStatus.RET_LOST_IN_TRANSIT:
                __create_return_order(order)
            __create_refund(order, 0, 'Should be unreachable for now')
            order.statusDescription = "Return Refunded"
        elif order.status == OrderStatus.CANCEL_REQUEST_CONFIRMED:
            if order.previousStatus in [OrderStatus.COD_VERIFICATION_PENDING, OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT, OrderStatus.ACCEPTED]:
                __update_inventory_reservation(order, refund=True)
                order.statusDescription = "Order Cancelled on customer request"
            elif order.previousStatus == OrderStatus.BILLED:
                __create_return_order(order)
                order.statusDescription = "Order Cancelled on customer request"
                order.received_return_timestamp = datetime.datetime.now()
    else:
        status_transition = {OrderStatus.LOST_IN_TRANSIT : OrderStatus.LOST_IN_TRANSIT_REFUNDED,
                     OrderStatus.RTO_RECEIVED_PRESTINE : OrderStatus.RTO_REFUNDED,
                     OrderStatus.RTO_RECEIVED_DAMAGED : OrderStatus.RTO_DAMAGED_REFUNDED,
                     OrderStatus.RTO_LOST_IN_TRANSIT : OrderStatus.RTO_LOST_IN_TRANSIT_REFUNDED,
                     OrderStatus.DOA_CERT_INVALID : OrderStatus.DOA_INVALID_REFUNDED,
                     OrderStatus.DOA_CERT_VALID : OrderStatus.DOA_VALID_REFUNDED,
                     OrderStatus.DOA_RECEIVED_DAMAGED : OrderStatus.DOA_REFUNDED_RCVD_DAMAGED,
                     OrderStatus.DOA_LOST_IN_TRANSIT : OrderStatus.DOA_REFUNDED_LOST_IN_TRANSIT,
                     OrderStatus.RET_PRODUCT_UNUSABLE : OrderStatus.RET_PRODUCT_UNUSABLE_REFUNDED,
                     OrderStatus.RET_PRODUCT_USABLE : OrderStatus.RET_PRODUCT_USABLE_REFUNDED,
                     OrderStatus.RET_RECEIVED_DAMAGED : OrderStatus.RET_REFUNDED_RCVD_DAMAGED,
                     OrderStatus.RET_LOST_IN_TRANSIT : OrderStatus.RET_REFUNDED_LOST_IN_TRANSIT,
                     OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.INVENTORY_LOW : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.LOW_INV_PO_RAISED : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.LOW_INV_REVERSAL_IN_PROCESS : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.ACCEPTED : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.BILLED : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
                     OrderStatus.CANCEL_REQUEST_CONFIRMED : OrderStatus.CANCELLED_ON_CUSTOMER_REQUEST,
                     OrderStatus.PAYMENT_FLAGGED : OrderStatus.PAYMENT_FLAGGED_DENIED
                     }
        if order.status not in status_transition.keys():
            raise TransactionServiceException(114, "This order can't be refunded")
    
        if order.status in [OrderStatus.RTO_RECEIVED_PRESTINE, OrderStatus.RTO_RECEIVED_DAMAGED, OrderStatus.RTO_LOST_IN_TRANSIT] :
            if order.status != OrderStatus.RTO_LOST_IN_TRANSIT:
                __create_return_order(order)
            __create_refund(order, order.wallet_amount, 'Order #{0} is RTO refunded'.format(order.id))
            order.statusDescription = "RTO Refunded"
            #Start:- Added By Manish Sharma for Creating a new Ticket: Category- RTO Refund on 21-Jun-2013
            try: 
                crmServiceClient = CRMClient().get_client()
                ticket =Ticket()
                activity = Activity()
                
                description = "Creating Ticket for " + order.statusDescription + " Order"
                ticket.creatorId = 1
                ticket.assigneeId = 34
                ticket.category = TicketCategory.RTO_REFUND
                ticket.priority = TicketPriority.MEDIUM
                ticket.status = TicketStatus.OPEN
                ticket.description = description
                ticket.orderId = order.id
                
                activity.creatorId = 1
                activity.ticketAssigneeId = ticket.assigneeId
                activity.type = ActivityType.OTHER
                activity.description = description
                activity.ticketCategory = ticket.category
                activity.ticketDescription = ticket.description
                activity.ticketPriority = ticket.priority
                activity.ticketStatus = ticket.status
                
                ticket.customerId= order.customer_id
                ticket.customerEmailId = order.customer_email
                ticket.customerMobileNumber = order.customer_mobilenumber
                ticket.customerName = order.customer_name
                activity.customerId = ticket.customerId
                activity.customerEmailId = order.customer_email
                activity.customerMobileNumber = order.customer_mobilenumber
                activity.customerName = order.customer_name
                
                crmServiceClient.insertTicket(ticket, activity)
                
            except:
                print "Ticket for RTO Refund is not created."
            #End:- Added By Manish Sharma for Creating a new Ticket: Category- RTO Refund on 21-Jun-2013
        elif order.status in [OrderStatus.LOST_IN_TRANSIT]:
            #__create_return_order(order)
            __create_refund(order, order.wallet_amount, 'Order #{0} is Lost in Transit'.format(order.id))
            order.statusDescription = "Lost in Transit Refunded"
        elif order.status in [OrderStatus.DOA_CERT_INVALID, OrderStatus.DOA_CERT_VALID, OrderStatus.DOA_RECEIVED_DAMAGED, OrderStatus.DOA_LOST_IN_TRANSIT] :
            if order.status != OrderStatus.DOA_LOST_IN_TRANSIT:
                __create_return_order(order)
            __create_refund(order, 0, 'This should be unreachable')
            order.statusDescription = "DOA Refunded"
        elif order.status in [OrderStatus.RET_PRODUCT_UNUSABLE, OrderStatus.RET_PRODUCT_USABLE, OrderStatus.RET_RECEIVED_DAMAGED, OrderStatus.RET_LOST_IN_TRANSIT] :
            if order.status != OrderStatus.RET_LOST_IN_TRANSIT:
                __create_return_order(order)
            __create_refund(order, 0, 'This should be unreachable')
            order.statusDescription = "Return Refunded"
        elif order.status in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT, OrderStatus.ACCEPTED]:
            __update_inventory_reservation(order, refund=True)
            order.statusDescription = "Order Refunded"
        elif order.status == OrderStatus.CANCEL_REQUEST_CONFIRMED:
            if order.previousStatus in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT, OrderStatus.PAYMENT_FLAGGED, OrderStatus.ACCEPTED]:
                __update_inventory_reservation(order, refund=True)
                order.statusDescription = "Order Cancelled on customer request"
            elif order.previousStatus == OrderStatus.BILLED:
                __create_refund(order, order.wallet_amount,  'Order #{0} Cancelled on customer request'.format(order.id))
                order.statusDescription = "Order Cancelled on customer request"
            
        elif order.status == OrderStatus.PAYMENT_FLAGGED:
            __update_inventory_reservation(order, refund=True)
            order.statusDescription = "Order Cancelled due to payment flagged"
            
    # For orders that are cancelled after being billed, we need to scan in the scanned out
    # inventory item and change availability accordingly
    inventoryClient = InventoryClient().get_client()
    warehouse = inventoryClient.getWarehouse(order.warehouse_id)
    if warehouse.billingType == BillingType.OURS or warehouse.billingType == BillingType.OURS_EXTERNAL:
        #Now BILLED orders can also be refunded directly with low inventory cancellations 
        if order.status in [OrderStatus.BILLED, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_FROM_WH]:
            __create_refund(order, order.wallet_amount, reason)
        if order.status in [OrderStatus.BILLED, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_FROM_WH] or (order.status == OrderStatus.CANCEL_REQUEST_CONFIRMED and order.previousStatus in [OrderStatus.BILLED, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_FROM_WH]):
            lineitem = order.lineitems[0]
            catalogClient = CatalogClient().get_client()
            item = catalogClient.getItem(lineitem.item_id)
            warehouseClient = WarehouseClient().get_client()
            if warehouse.billingType == BillingType.OURS:
                if ItemType.SERIALIZED == item.type:
                    for serial_number in str(lineitem.serial_number).split(','):
                        warehouseClient.scanSerializedItemForOrder(serial_number, ScanType.SALE_RET, order.id, order.fulfilmentWarehouseId, 1, order.warehouse_id)
                else:
                    warehouseClient.scanForOrder(None, ScanType.SALE_RET, lineitem.quantity, order.id, order.fulfilmentWarehouseId, order.warehouse_id)
            if warehouse.billingType == BillingType.OURS_EXTERNAL:
                warehouseClient.scanForOursExternalSaleReturn(order.id, lineitem.transfer_price)
            if order.freebieItemId:
                warehouseClient.scanfreebie(order.id, order.freebieItemId, 0, ScanType.SALE_RET)

    order.status = status_transition[order.status]
    order.statusDescription = OrderStatus._VALUES_TO_NAMES[order.status]
    order.refund_timestamp = datetime.datetime.now()
    order.refunded_by = refunded_by
    order.refund_reason = reason
    #to re evaluate the shipping charge if any order is being cancelled.
    #_revaluate_shiping(order_id)
    session.commit()
    return True

def _revaluate_shiping(orderId):
    orders = Order.get_by(id = orderId)
    totalCartVal=0
    totalshippingCost=0
    if orders:
        ordersList = get_orders_for_transaction(orders.transaction_id, orders.customer_id)
        for order in ordersList:
            if order.status == OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY or order.shipping_timestamp is not None:
                return
            
            if order.status not in [OrderStatus.COD_VERIFICATION_FAILED,OrderStatus.CANCELLED_ON_CUSTOMER_REQUEST,OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY]:
                for line in order.lineitems:
                    totalCartVal = totalCartVal+line.total_price
                
        if totalCartVal<1000:
            totalshippingCost = 50
            
        shippingCostInOrders=0
        shipUpdateOrder= None
        if totalshippingCost > 0:
            for order in ordersList:
                if order.status not in [OrderStatus.COD_VERIFICATION_FAILED,OrderStatus.CANCELLED_ON_CUSTOMER_REQUEST,OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY]:
                    for line in order.lineitems:
                        order.shippingCost = round((line.total_price*totalshippingCost)/totalCartVal, 0)
                    
                    shippingCostInOrders = shippingCostInOrders + order.shippingCost
                    if shipUpdateOrder is None:
                        shipUpdateOrder = order
                        
            if shipUpdateOrder is not None:
                diff = totalshippingCost - shippingCostInOrders
                if diff>0:
                    shipUpdateOrder.shippingCost = shipUpdateOrder.shippingCost + diff
            
            

def __update_inventory_reservation(order, refund=False):
    '''
    Reduce the reservation count for all line items of the given order.
    '''
    
    if refund:
        if order.wallet_amount > 0:
            refund_to_wallet(order.customer_id, order.wallet_amount, order.transaction.id, WalletReferenceType.PURCHASE, "Refunded angainst cancellation of Order Id -" + str(order.id))
    try:
        inventoryClient = InventoryClient().get_client()
        for lineitem in order.lineitems:
            inventoryClient.reduceReservationCount(lineitem.item_id, order.fulfilmentWarehouseId, sourceId, order.id, lineitem.quantity)
    except:
        print "Unable to reduce reservation count"

def __clone_order(order, should_copy_billing_info, is_cod, reserved=True):
    current_time = datetime.datetime.now()
    new_order = Order()
    new_order.customer_id = order.customer_id
    new_order.customer_name = order.customer_name
    new_order.customer_city = order.customer_city
    new_order.customer_state = order.customer_state
    new_order.customer_mobilenumber = order.customer_mobilenumber
    new_order.customer_pincode = order.customer_pincode
    new_order.customer_address1 = order.customer_address1
    new_order.customer_address2 = order.customer_address2
    new_order.customer_email = order.customer_email
    new_order.total_amount = order.total_amount
    new_order.gvAmount = order.gvAmount
    new_order.total_weight = order.total_weight
    new_order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
    new_order.statusDescription = 'Submitted for Processing'
    new_order.created_timestamp = current_time
    new_order.originalOrderId = order.originalOrderId if order.originalOrderId is not None else order.id
    new_order.shippingCost = order.shippingCost
    
    new_order.transaction = order.transaction
    new_order.cod = is_cod
    new_order.orderType = order.orderType
    for line_item in order.lineitems:
        litem = LineItem()
        litem.item_id = line_item.item_id
        litem.productGroup = line_item.productGroup
        litem.brand = line_item.brand
        if line_item.model_number:
            litem.model_number = line_item.model_number
        if line_item.model_name:    
            litem.model_name = line_item.model_name
        if line_item.color:
            litem.color = line_item.color
        if line_item.extra_info:
            litem.extra_info = line_item.extra_info
        litem.quantity = line_item.quantity
        litem.mrp = line_item.mrp
        litem.unit_price = line_item.unit_price
        litem.unit_weight = line_item.unit_weight
        litem.total_price = line_item.total_price
        litem.total_weight = line_item.total_weight
        litem.transfer_price = line_item.transfer_price
        litem.nlc = line_item.nlc
        litem.dealText = line_item.dealText
        litem.vatRate = line_item.vatRate
        litem.order = new_order
        
    if order.insurer > 0:
        new_order.insurer = order.insurer
        new_order.insuranceAmount = order.insuranceAmount
        newDetail = InsuranceDetailForOrder()
        oldDetail = InsuranceDetailForOrder.get_by(order_id = order.id)
        newDetail.dob = oldDetail.dob
        newDetail.guardianName = oldDetail.guardianName
        newDetail.order = new_order
        
    if order.dataProtectionInsurer > 0:
        new_order.dataProtectionInsurer = order.dataProtectionInsurer
        new_order.dataProtectionAmount = order.dataProtectionAmount
        dataInsuranceDetail = DataInsuranceDetailForOrder()
        dataInsuranceDetail.order = new_order
        
    logistics_client = LogisticsClient().get_client()
    item_id = new_order.lineitems[0].item_id
    logistics_info = logistics_client.getLogisticsInfo(new_order.customer_pincode, item_id, is_cod, PickUpType.COURIER) #TODO: We should be able to pass another flag to suggest ignoring the inventory situation.
    logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
    logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
    logistics_info.deliveryDelay = adjust_delivery_time(current_time, (logistics_info.shippingTime + logistics_info.deliveryDelay))
    new_order.warehouse_id = logistics_info.warehouseId
    new_order.fulfilmentWarehouseId = logistics_info.fulfilmentWarehouseId
    new_order.logistics_provider_id = logistics_info.providerId
    '''
    new_order.airwaybill_no = logistics_info.airway_billno
    new_order.tracking_id = new_order.airwaybill_no
    '''
    new_order.source = order.source
    order.courier_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryDelay)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
    new_order.promised_delivery_time = new_order.expected_delivery_time
    new_order.promised_shipping_time = order.promised_shipping_time
    
    if reserved:
        new_order.expected_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
        new_order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
    else:
        new_order.expected_delivery_time = order.expected_delivery_time
        new_order.expected_shipping_time = order.expected_shipping_time
        
    if should_copy_billing_info:
        new_order.vendorId = order.vendorId
        new_order.invoice_number = order.invoice_number
        new_order.billed_by = order.billed_by
        new_order.warehouse_id = order.warehouse_id
        new_order.fulfilmentWarehouseId = order.fulfilmentWarehouseId
        new_order.jacket_number = order.jacket_number
        new_order.lineitems[0].serial_number = order.lineitems[0].serial_number
        new_order.lineitems[0].item_number   = order.lineitems[0].item_number
        new_order.accepted_timestamp = current_time
        new_order.billing_timestamp = current_time    
        new_order.status = OrderStatus.BILLED
        new_order.statusDescription = 'Order Billed'
        
        if new_order.logistics_provider_id != 7 and new_order.airwaybill_no is None:
            if is_cod and order.total_amount > 1:
                new_order.airwaybill_no= logistics_client.getEmptyAWB(new_order.logistics_provider_id, DeliveryType.COD)
                new_order.tracking_id = new_order.airwaybill_no
            else:
                new_order.airwaybill_no= logistics_client.getEmptyAWB(new_order.logistics_provider_id, DeliveryType.PREPAID)
                new_order.tracking_id = new_order.airwaybill_no
                
        session.commit()
    else:
        session.commit()
        if reserved:       
            inventoryClient = InventoryClient().get_client()
            inventoryClient.reserveItemInWarehouse(item_id, logistics_info.fulfilmentWarehouseId, sourceId, new_order.id, to_java_date(new_order.created_timestamp), to_java_date(new_order.promised_shipping_time), new_order.lineitems[0].quantity)
    
    if order.source == 2:
        new_order.advanceAmount = order.advanceAmount
        new_order.storeId = order.storeId   
        oldSod = StoreOrderDetail.query.filter(StoreOrderDetail.orderId == order.id).filter(StoreOrderDetail.storeId == order.storeId).first()
        newSod = StoreOrderDetail()
        newSod.orderId = new_order.id
        newSod.storeId = oldSod.storeId
        newSod.advanceAmount = oldSod.advanceAmount
        newSod.approvalCode = oldSod.approvalCode
        newSod.cardAmount = oldSod.cardAmount
        newSod.cashAmount = oldSod.cashAmount
        newSod.cashRefundAmount = oldSod.cashRefundAmount
        newSod.cardRefundAmount = oldSod.cardRefundAmount
        newSod.edcBank = oldSod.edcBank
        newSod.payStatus = oldSod.payStatus
        newSod.cardType = oldSod.cardType 
        session.commit()
    if order.source == 3:
        old_amazon_order = AmazonOrder.get_by(orderId=order.id)
        amazon_order = AmazonOrder()
        amazon_order.orderId = new_order.id
        amazon_order.amazonOrderCode = old_amazon_order.amazonOrderCode
        amazon_order.amazonOrderItemCode = old_amazon_order.amazonOrderItemCode
        amazon_order.transactionId = old_amazon_order.transactionId
        amazon_order.item_id= old_amazon_order.item_id
        amazon_order.status= old_amazon_order.status
        amazon_order.purchaseDateOnAmazon= old_amazon_order.purchaseDateOnAmazon
        session.commit()
    if order.source == 6:
        old_ebay_order = EbayOrder.get_by(orderId=order.id)
        ebay_order = EbayOrder()
        ebay_order.orderId = new_order.id
        ebay_order.salesRecordNumber = old_ebay_order.salesRecordNumber
        ebay_order.paisaPayId = old_ebay_order.paisaPayId
        ebay_order.ebayListingId = old_ebay_order.ebayListingId
        ebay_order.subsidyAmount= old_ebay_order.subsidyAmount
        ebay_order.ebayTxnDate= old_ebay_order.ebayTxnDate
        ebay_order.transactionId= old_ebay_order.transactionId
        ebay_order.listingName= old_ebay_order.listingName
        session.commit()
    if order.source == 7:
        old_snapdeal_order = SnapdealOrder.get_by(orderId=order.id)
        snapdeal_order = SnapdealOrder()
        snapdeal_order.orderId = new_order.id
        snapdeal_order.subOrderId = old_snapdeal_order.subOrderId
        snapdeal_order.referenceCode = old_snapdeal_order.referenceCode
        snapdeal_order.productName = old_snapdeal_order.productName
        snapdeal_order.listingPrice = old_snapdeal_order.listingPrice
        snapdeal_order.snapdealTxnDate = old_snapdeal_order.snapdealTxnDate
        session.commit()
    if order.source == 8:
        old_flipkart_order = FlipkartOrder.get_by(orderId=order.id)
        flipkart_order = FlipkartOrder()
        flipkart_order.orderId = new_order.id
        flipkart_order.subOrderId = old_flipkart_order.subOrderId
        flipkart_order.referenceCode = old_flipkart_order.referenceCode
        flipkart_order.productName = old_flipkart_order.productName
        flipkart_order.listingPrice = old_flipkart_order.listingPrice
        flipkart_order.snapdealTxnDate = old_flipkart_order.snapdealTxnDate
        session.commit()
    
    attributes = Attribute.query.filter(Attribute.orderId == order.id).all()
    if attributes:
        for attribute in attributes:
            newattribute = Attribute()
            newattribute.orderId = new_order.id
            newattribute.name = attribute.name 
            newattribute.value = attribute.value
            session.commit()
    
    return new_order

def __create_return_order(order):
    ret_order = ReturnOrder(order)
    return ret_order

def __create_refund(order, refundAmount, reason="Order Refunded"):
    if order.wallet_amount > 0:
        refund_to_wallet(order.customer_id, refundAmount, order.transaction.id, WalletReferenceType.PURCHASE, reason)
    if order.net_payable_amount > 0:
        payment_client = PaymentClient().get_client()
        payment_client.createRefund(order.id, order.transaction.id, order.net_payable_amount)
    return

def __get_order_address(order):
    address = order.customer_name + "\n" + order.customer_address1 + "\n"
    if order.customer_address2:
        address = address + order.customer_address2 + "\n"
    address = address + order.customer_city + "\n"
    address = address + order.customer_state + "\n"
    address = address + "PIN " + order.customer_pincode + "\n"
    address = address + "Phone: " + order.customer_mobilenumber
    return address

def __get_order_address_html(order):
    address = order.customer_name + ", " + order.customer_address1 + ", "
    if order.customer_address2:
        address = address + order.customer_address2 + ", "
    address = address + order.customer_city + ", "
    address = address + order.customer_state + ", "
    address = address + "PIN " + order.customer_pincode + "."
    return address

def __get_store_address(store):
    address = store.name + "\n" + store.line1 + "\n"
    if store.line2:
        address = address + store.line2 + "\n"
    address = address + store.city + "\n"
    address = address + store.state + "\n"
    address = address + "PIN - " + store.pin + "\n"
    if store.phone:
        address = address + "Phone - " + store.phone + "\n"
    address = address +  "Store timings - 11AM to 8PM (7 days)\n"
    return address

def __get_store_address_html(store):
    address = store.name + "<br>" + store.line1 + "<br>"
    if store.line2:
        address = address + store.line2 + "<br>"
    address = address + store.city + "<br>"
    address = address + store.state + "<br>"
    address = address + "PIN - " + store.pin + "<br>"
    if store.phone:
        address = address + "Phone - " + store.phone + "<br>"
    address = address +  "Store timings - 7 days: 11AM to 8PM  (7 days)<br>"
    return address

def __get_hotspot_store_address_html(store):
    address = store.line1 + ", "
    if store.line2:
        address = address + store.line2 + ", "
    address = address + store.city + ", "
    address = address + store.state + ", "
    address = address + "PIN - " + store.pin
    return address

def __generate_return_advice(order, warehouse, provider, filename):
    if warehouse.id == 7:
        executive = 'Shiv Kumar'
    else:
        executive = 'Dinesh Kumar'
    pdf = Canvas(filename)
    pdf.setFont('Times-Bold',16)
    address_text = pdf.beginText(inch, PAGE_HEIGHT - inch)
    address_text.textLine("To")
    address_text.textLine(executive)
    for line in warehouse.location.split("\n"):
        address_text.textLine(line)
    address_text.textLine("PIN " + warehouse.pincode)
    pdf.drawText(address_text)
    
    pdf.setFont('Times-Roman',12)
    order_text = pdf.beginText(inch, PAGE_HEIGHT - 4 * inch)
    for detail in provider.details:
        if detail.deliveryType == DeliveryType.PREPAID and detail.logisticLocation == warehouse.logisticsLocation :
            order_text.textLine("Pickup CODE: " + detail.accountNo)
    lineitem = order.lineitems[0] 
    order_text.textLine("Content : " + lineitem.brand + " " + str(lineitem.model_number) + " " + str(lineitem.color))
    order_text.textLine("Declared Value: Rs. " + str(order.total_amount))
    order_text.textLine("Order ID : " + str(order.id))
    pdf.drawText(order_text)
    pdf.showPage()
    pdf.save()
    return filename

def __coord(self, x, y, unit=1):
        """
        # http://stackoverflow.com/questions/4726011/wrap-text-in-a-table-reportlab
        Helper class to help position flowables in Canvas objects
        """
        x, y = x * unit, PAGE_HEIGHT -  y * unit
        return x, y

def __generate_return_transaction_invoice(returnOrders, warehouse, provider, filename):
    ReturnPickupRequest
    if warehouse.id == 7:
        executive = 'Varun Patiyal'
    else:
        executive = 'Deepak Kumar'
    pdf = Canvas(filename)
    styles = getSampleStyleSheet()
    address = """ <font size="9">
        To<br/>
        <br/>
        %s<br/>"""%(executive)
    for line in warehouse.location.split("\n"):
        address = address +"""%s<br/>"""%(line)
    address = address +"""PIN %s<br>"""%(warehouse.pincode)
    address = address +"""</font>"""
    p = Paragraph(address, styles["Normal"])
    p.wrapOn(pdf, PAGE_WIDTH, PAGE_HEIGHT)
    p.drawOn(pdf, *__coord(18, 40, mm))
    returnPickupReq = ReturnPickupRequest.get_by(id=returnOrders[0].logisticsRequestId)
    
    order_number = '<font size="14"><b>Return Pickup Request No:- %s </b></font>' % returnPickupReq.pickupRequestNo
    p = Paragraph(order_number, styles["Normal"])
    p.wrapOn(pdf, PAGE_WIDTH, PAGE_HEIGHT)
    p.drawOn(pdf, *__coord(18, 50, mm))
        
    data = []
    data.append(["Content", "Quantity", "Declared Value: Rs.", "Order ID", "Return Ticket Id"])
    for returnOrder in returnOrders:
        lineitem = returnOrder.lineitem
        row = []
        row.append(str(lineitem))
        row.append(returnOrder.returnQuantity)
        row.append(returnOrder.returnQuantity*lineitem.unit_price)
        row.append(returnOrder.orderId)
        row.append(returnOrder.returnTransaction.id)
        data.append(row)
        
    t = Table(data, 1.5 * inch)
    t.setStyle(TableStyle([
        ('INNERGRID', (0,0), (-1,-1), 0.25, colors.black),
        ('BOX', (0,0), (-1,-1), 0.25, colors.black)
    ]))
    t.wrapOn(pdf, PAGE_WIDTH, PAGE_HEIGHT)
    t.drawOn(pdf, *__coord(18, 85, mm))
    
    pdf.showPage()
    pdf.save()
    return filename

def __push_store_collection_to_hotspot(order, ctype, cashamount, cardamount):
    ts = datetime.datetime.now()
    store =  get_hotspot_store(order.storeId, "")
        
    soc = StoreOrderCollection()
    soc.hotspotId = store.hotspotId
    soc.orderId = order.id
    soc.collectionType = ctype
    soc.advanceAmount = int(cashamount + cardamount)
    soc.productName =  str(order.lineitems[0]) 
    soc.addedAt = ts
    soc.pushedToOcr = False
    soc.cash = int(cashamount)
    soc.card = int(cardamount)
    session.commit()
    
    try:
        rcs = StoreOrderCollection.query.filter(StoreOrderCollection.pushedToOcr == False).all()
        for cs in rcs:
            #SaholicAdvanceTransfer(xs:string Store, xs:string OrderID, xs:string Product, xs:string CollectionType, xs:decimal AdvanceAmount, xs:decimal CashAmount, xs:decimal CardAmount, xs:string Time, xs:string Date, )
            resp = get_store_account_client().service.SaholicAdvanceTransfer(cs.hotspotId,str(cs.orderId),cs.productName,cs.collectionType,float(cs.advanceAmount),float(cs.cash),float(cs.card),cs.addedAt.strftime("%H%M%S"),cs.addedAt.strftime("%Y%m%d"))
            print resp
            if "Saved Successfully" in resp:
                cs.pushedToOcr = True
                cs.pushedAt = datetime.datetime.now()
                session.commit()
            elif "Error in Saving Data" in resp:
                mail_html("cnc.center@shop2020.in", "5h0p2o2o", ["kshitij.sood@shop2020.in", "rajneesh.arora@shop2020.in"], "Problem while pushing store collection to OCR", resp, [])
            else: 
                mail_html("cnc.center@shop2020.in", "5h0p2o2o", ["kshitij.sood@shop2020.in", "amit.gupta@shop2020.in", "rajneesh.arora@shop2020.in"], "Problem while pushing store collection to OCR", resp, [])
    except:
        print "Problem while pushing to ocr"

def __push_collection_to_hotspot(order, type, storeOrder = False):
    try:
        storeName = '109'
        if storeOrder:
            store =  get_hotspot_store(order.storeId, "")
            storeName = store.hotspotId
            storeName = '109'
        else:
            logistics_client = LogisticsClient().get_client()
            store = logistics_client.getPickupStore(order.pickupStoreId)
            storeName = store.hotspotId
        payment_mode = get_order_attribute_value(order.id, "PAYMENT_MODE")
        if type == "SALE":
            if storeOrder:
                amount = int(order.advanceAmount)
                ts = order.created_timestamp
            else:
                amount = int(order.total_amount-order.gvAmount)
                ts = order.delivery_timestamp
        if type == "SALE RETURN":
            if storeOrder:
                amount = -int(order.advanceAmount)
            else:
                amount = -int(order.total_amount-order.gvAmount)
            ts = order.refund_timestamp
        cash_int = 0
        card_int = 0
        if payment_mode == "Cash":
            cash_int = amount
            card_int = 0
        if payment_mode == "Card":
            cash_int = 0
            card_int = amount
        #SaholicDataTransfer(xs:string store, xs:string orderno, xs:string Type, xs:int cash, xs:int hdfc, xs:int date, xs:int time, )
        resp = get_store_account_client().service.SaholicDataTransfer(storeName,str(order.id),type,cash_int,card_int,int(ts.strftime("%Y%m%d")),int(ts.strftime("%H%M%S")))
        print resp
        if "Saved Successfully" in resp:
            return True
        elif "Error in Saving Data" in resp:
            return False
        else: 
            return False
    except Exception as e:
        print e
        return False

def get_return_orders(warehouse_id, from_date, to_date):
    query = ReturnOrder.query.filter_by(processedStatus = False)
    if warehouse_id != -1:
        query = query.filter_by(warehouseId = warehouse_id)
    if from_date:
        query = query.filter(ReturnOrder.createdAt >from_date)
    if to_date:
        query = query.filter(ReturnOrder.createdAt < to_date)
    return [return_order.to_thrift_object() for return_order in query.all()]

def get_all_return_orders(onlyNotProcessed, from_date, to_date):
    query = ReturnOrder.query
    if onlyNotProcessed:
        query = query.filter_by(processedStatus = False)
    if from_date:
        query = query.filter(ReturnOrder.createdAt >= from_date)
    if to_date:
        query = query.filter(ReturnOrder.createdAt <= to_date)
    return [return_order.to_thrift_object() for return_order in query.all()]

def get_return_order(id):
    ret_order = ReturnOrder.get_by(orderId = id)
    if ret_order is None:
        raise TransactionServiceException(116, "No return order found for the given order id")
    return ret_order.to_thrift_object()

def process_return(ret_order_id):
    ret_order = ReturnOrder.get_by(orderId = ret_order_id)
    if ret_order is None:
        raise TransactionServiceException(116, "No return order found for the given order id")
    ret_order.processedStatus = True
    ret_order.processedAt = datetime.datetime.now()
    session.commit()

def update_orders_as_PORaised(itemIdQuantityMap, purchaseOrderId, warehouseId):
    """
    Updates orders as PO raised. Also updates purchase order id in orders. Pass a map of items mapped to
    the quantities for which the PO is raised.

    Parameters:
     - itemIdQuantityMap
     - purchaseOrderId
     - warehouseId
    """
    query = Order.query.with_lockmode("update").filter_by(warehouse_id=warehouseId)
    query = query.filter(or_(Order.status == OrderStatus.SUBMITTED_FOR_PROCESSING, Order.status == OrderStatus.INVENTORY_LOW))
    query = query.filter_by(purchase_order_id=None)
    query = query.order_by(Order.created_timestamp)
    pending_orders = query.all()

    for order in pending_orders:
        lineitem = order.lineitems[0]
        if itemIdQuantityMap.has_key(lineitem.item_id):
            if itemIdQuantityMap[lineitem.item_id] >= lineitem.quantity:
                order.status = OrderStatus.LOW_INV_PO_RAISED
                order.statusDescription = 'In Process'
                order.purchase_order_id = purchaseOrderId
                itemIdQuantityMap[lineitem.item_id] -= lineitem.quantity

    session.commit()

def update_weight(order_id, weight):
    '''
    Update the weight of order
    '''
    order = get_order(order_id)
    order.total_weight = weight
    lineitem = order.lineitems[0]
    lineitem.total_weight = weight
    lineitem.unit_weight = weight
    
    session.commit()
    return order

def change_courier_provider(order_id, provider_id):
    order = get_order(order_id)
    if order.status not in [OrderStatus.COD_VERIFICATION_PENDING, OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.ACCEPTED, OrderStatus.BILLED]:
        raise TransactionServiceException(121, "This order has already been processed. Please seek help from engineering.")
    
    if order.logistics_provider_id == provider_id:
        raise TransactionServiceException(121, "Provider Id entered by you is same as provider assigned to order.")
    
    logistics_client = LogisticsClient().get_client()
    if provider_id != 7 and provider_id != 46:
        awb_number = logistics_client.getEmptyAWB(provider_id, order.cod)
    order.logistics_provider_id = provider_id
    if provider_id != 7 and provider_id != 46:
        order.airwaybill_no = awb_number
        order.tracking_id = awb_number
    session.commit()

    return order

def change_product(order_id, item_id):
    '''
    Ship a product of a different color to the customer.
    Do nothing
    order = get_order(order_id)
    if order.status not in [OrderStatus.COD_VERIFICATION_PENDING, OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.ACCEPTED, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT]:
        raise TransactionServiceException(120, "This order has already been processed. Please seek help from engineering.")
        
    lineitem = order.lineitems[0]
    
    catalog_client = CatalogClient().get_client()
    
    old_item = catalog_client.getItem(lineitem.item_id)
    item = catalog_client.getItem(item_id)
    if old_item.catalogItemId != item.catalogItemId:
        freebie_order_info_text = "Freebie Order for Order ID"
        if lineitem.extra_info and freebie_order_info_text in lineitem.extra_info:
            print "Allowing item change for split Freebie Order"
        else:    
            raise TransactionServiceException(121, "You can't ship a different item. You can only ship the same item in a different color.")
    
    inventoryClient = InventoryClient().get_client()
    if order.productCondition != ProductCondition.BAD:
        inventoryClient.updateReservationForOrder(item_id, order.fulfilmentWarehouseId, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), order.lineitems[0].quantity)
    #inventoryClient.reserveItemInWarehouse(item_id, order.fulfilmentWarehouseId, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), order.lineitems[0].quantity)
    #__update_inventory_reservation(order)
    #TODO: Check that this new item has the same price
        
    lineitem.item_id = item_id
    lineitem.brand = item.brand
    lineitem.model_name = item.modelName
    lineitem.model_number = item.modelNumber
    lineitem.color = item.color
    
    session.commit()
    move_orders_to_correct_warehouse()
    '''
    print 'Could not change product'
    order = get_order(order_id)
    return order


def change_warehouse(order_id, warehouse_id):
    '''
    Update the warehouse which will be used to fulfil this order.
    '''
    order = get_order(order_id)
    if order.status not in [OrderStatus.COD_VERIFICATION_PENDING, OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT, OrderStatus.ACCEPTED]:
        raise TransactionServiceException(117, "This order has already been processed. Please seek help from engineering.")
         
    if warehouse_id == order.warehouse_id:
        raise TransactionServiceException(118, "You have selected the current warehouse again. Nothing to do")

    lineitem = order.lineitems[0]
    userClient = UserClient().get_client()
    inventoryClient = InventoryClient().get_client()
    try:
        warehouse = inventoryClient.getWarehouse(warehouse_id)
    except:
        raise TransactionServiceException(119, "No warehouse with id" + str(warehouse_id))

    if not warehouse.billingWarehouseId:
        raise TransactionServiceException(119, "Shifting of warehouse allowed to warehouse with billing support only")

    # If One gives a billing warehouse for warehouse shift, we should assign order to the GOOD warehouse
    # present within it with maximum availability
    stateId = -1
    userClient, isFofo = __is_Fofo(userClient, order.customer_id)
    if isFofo:
        stateId = warehouse.stateId 
    if warehouse.id == warehouse.billingWarehouseId:
        itemAvailabilityLocation = inventoryClient.getItemAvailabilityAtLocation(lineitem.item_id, sourceId, stateId)
        if warehouse.billingWarehouseId == itemAvailabilityLocation[2]:
            billing_warehouse_id=itemAvailabilityLocation[2]
            vendor_warehouse_id=itemAvailabilityLocation[0]
        else:
            #In case movement happens to Lucknow Warehouse move the order to virtual warehouse first
            billing_warehouse_id = warehouse.billingWarehouseId
            vendor_warehouse_id = 8489
    else:
        vendor_warehouse_id = warehouse_id
        billing_warehouse_id=warehouse.billingWarehouseId
        
    #if vendorWarehouse is None or vendorWarehouse.billingWarehouseId != warehouse.id:
    #assign it to first vendorwarehouse available with 
    if order.productCondition != ProductCondition.BAD:
        inventoryClient.updateReservationForOrder(lineitem.item_id, vendor_warehouse_id, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), lineitem.quantity)
    #__update_inventory_reservation(order)
    if order.status == OrderStatus.ACCEPTED:
        __update_transfer_price(order, vendor_warehouse_id)
    order.warehouse_id = billing_warehouse_id
    order.fulfilmentWarehouseId = vendor_warehouse_id
    session.commit()
    return order

def __update_transfer_price(order, warehouse_id):
    lineitem = order.lineitems[0]
    inventory_client = InventoryClient().get_client()
    warehouse = inventory_client.getWarehouse(warehouse_id)
    item_pricing = inventory_client.getItemPricing(lineitem.item_id, warehouse.vendor.id)
    lineitem.transfer_price = item_pricing.transferPrice
    lineitem.nlc = item_pricing.nlc

def add_delay_reason(order_id, reason, further_delay, delayReasonText):
    '''
    Adds a delay reason to the order and updates the expected delivery time.
    '''
    order = get_order(order_id)
    try:
        order.delay_reason = DelayReason._VALUES_TO_NAMES[reason]
        order.delayReasonText = delayReasonText
        actual_delivery_delay = adjust_delivery_time(order.expected_delivery_time, further_delay)
        order.expected_delivery_time = order.expected_delivery_time + datetime.timedelta(days=actual_delivery_delay)
        
        actual_shipping_delay = adjust_delivery_time(order.expected_shipping_time, further_delay)
        order.expected_shipping_time = order.expected_shipping_time + datetime.timedelta(days=actual_shipping_delay)
        if order.courier_delivery_time:
            order.courier_delivery_time = order.courier_delivery_time + datetime.timedelta(days=adjust_delivery_time(order.courier_delivery_time, further_delay))
        else:
            order.courier_delivery_time = order.expected_delivery_time + datetime.timedelta(days=adjust_delivery_time(order.expected_delivery_time, further_delay))
        session.commit()
        
        exist = TransactionRequiringExtraProcessing.query.filter_by(transaction_id = order.transaction_id).first()
        
        if not exist:
            
            try:
                transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
                transaction_requiring_extra_processing.category = 'DELAYED_DELIVERY'
                transaction_requiring_extra_processing.transaction_id = order.transaction_id
                session.commit()
            except Exception as e:
                print 'Could not persist transaction id: ' + str(order.transaction_id) + ' for further processing due to: ' + str(e)
        return True
    except:
        print sys.exc_info()[0]
        return False

def reconcile_cod_collection(collected_amount_map, xferBy, xferTxnId, xferDate):
    unprocessed_awbs = {}
    for awb, amount in collected_amount_map.iteritems():
        try:
            orders = Order.query.filter_by(airwaybill_no=awb).all()
        except NoResultFound:
            unprocessed_awbs[awb] = "No order was found for the given AWB: " + awb
            continue
        order = orders[0]
        
        totalOrdersAmount = 0
        for order in orders:
            totalOrdersAmount = totalOrdersAmount + order.net_payable_amount
    
        if order.cod_reconciliation_timestamp:
            #This order has been processed already! This may be a re-run. Let's allow other orders to be processed.
            continue
        
        if order.status < OrderStatus.DELIVERY_SUCCESS:
            #Order has not been delivered yet. No way we can receive the payment.
            unprocessed_awbs[awb] = "Payment has been received for order: " + str(order.id) + " before it has been delivered"
            continue
        
        ##As per ticket #743 if amount difference less than 0.5 we should reconcile
        if abs(amount - totalOrdersAmount) > 0.5:
            #Received amount is less than or more than the order amount. Mustn't let the user proceed.
            unprocessed_awbs[awb] = "Payment of Rs. " + str(amount) + " has been received against the total value of Rs. " + str(totalOrdersAmount)
            continue
        
        try:
            payment_client = PaymentClient().get_client()
            payment_client.partiallyCapturePayment(order.transaction.id, amount, xferBy, xferTxnId, xferDate)
        except Exception:
            unprocessed_awbs[awb] = "We were unable to partially capture payment for order id " + str(order.id) + ", AWB: " + awb
            continue
        
        #Payment has been recorded now. We can update the order peacefully.
        remittance = CodCourierPaymentRemittance()
        remittance.airwayBillNo = awb
        remittance.collectionReference = xferTxnId
        remittance.amount = amount 
        remittance.settledAt = to_py_date(xferDate)
        remittance.provider_id = order.logistics_provider_id
        for order in orders:
            order.cod_reconciliation_timestamp = datetime.datetime.now()
        session.commit()
    
    return unprocessed_awbs

def get_transactions_requiring_extra_processing(category):
    """
    Returns the list of transaction ids that require some extra processing and
    which belong to a particular category. This is currently used by CRM
    application. If no such transaction ids are present, it returns an empty list.
    """
    query = TransactionRequiringExtraProcessing.query

    if category is not None:
        query = query.filter_by(category = ExtraTransactionProcessingType._VALUES_TO_NAMES.get(category))

    return [a.transaction_id for a in query.all()]

def mark_transaction_as_processed(transactionId, category):
    """
    Marks a particular transaction as processed for a particular cateogory.
    It essentially deletes the transaction id record for a particular
    processing type category (if present) from DB.
    This is currently used by CRM application.
    """
    TransactionRequiringExtraProcessing.query.filter_by(transaction_id = transactionId).filter_by(category = ExtraTransactionProcessingType._VALUES_TO_NAMES.get(category)).delete()
    session.commit()

def get_item_wise_risky_orders_count():
    """
    Returns a map containing the number of risky orders keyed by item id. A risky order
    is defined as one whose shipping date is about to expire.
    """
    item_wise_risky_orders_count = {}
    shipping_cutoff_time = (datetime.datetime.now() + datetime.timedelta(days = 1)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
    orders = Order.query.filter(Order.status.in_(tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.ACCEPTED]))).filter(Order.expected_shipping_time <= shipping_cutoff_time).all()
    for order in orders:
        for lineitem in order.lineitems:
            quantity = lineitem.quantity
            if item_wise_risky_orders_count.has_key(lineitem.item_id):
                quantity = quantity + item_wise_risky_orders_count[lineitem.item_id]
            
            item_wise_risky_orders_count[lineitem.item_id] = quantity
    return item_wise_risky_orders_count

def update_shipment_address(order_id, address_id):
    """
    Updates shipment address of an order. Delivery and shipping date estimates
    etc. are also updated here.

    Throws TransactionServiceException in case address change is not
    possible due to certain reasons such as new pincode in address is
    not serviceable etc.
    
    Parameters:
     - orderId
     - addressId
    """
    user_client = UserClient().get_client()
    address     = user_client.getAddressById(address_id)
    order       = get_order(order_id)

    type = DeliveryType.PREPAID
    if order.cod:
        type = DeliveryType.COD

    logistics_client = LogisticsClient().get_client()
    logistics_info = logistics_client.getLogisticsEstimation(order.lineitems[0].item_id, address.pin, type)

    # Check for new estimate. Raise exception if new address is not serviceable
    if logistics_info.deliveryTime == -1:
        raise TransactionServiceException(1, 'Location not serviceable')

    # COD services are not available everywhere
    if order.cod:
        if not logistics_info.codAllowed:
            raise TransactionServiceException(2, 'COD service not available')

    if logistics_info.providerId != order.logistics_provider_id:
        logistics_info = logistics_client.getLogisticsInfo(address.pin, order.lineitems[0].item_id, type, PickUpType.COURIER)
        #Start:- Added by Manish Sharma for FedEx Integration- Shipment Creation on 31-Jul-2013
        #order.tracking_id = logistics_info.airway_billno
        #order.airwaybill_no = logistics_info.airway_billno
        if logistics_info.providerId !=7 and logistics_info.providerId !=46:
            awb_number = logistics_client.getEmptyAWB(logistics_info.providerId, type)
            order.tracking_id = awb_number
            order.airwaybill_no = awb_number
        #End:- Added by Manish Sharma for FedEx Integration- Shipment Creation on 31-Jul-2013
        order.logistics_provider_id = logistics_info.providerId

    order.otg = logistics_info.otgAvailable
    current_time = datetime.datetime.now()
    logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
    logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
    logistics_info.deliveryDelay = adjust_delivery_time(current_time, (logistics_info.shippingTime+logistics_info.deliveryDelay))

    order.customer_name         = address.name
    order.customer_pincode      = address.pin
    order.customer_address1     = address.line1
    order.customer_address2     = address.line2
    order.customer_city         = address.city
    order.customer_state        = address.state
    order.customer_mobilenumber = address.phone

    if order.cod:
        order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0)
        order.expected_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryTime)).replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0)
        order.courier_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryDelay)).replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0)
    else:
        order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
        order.expected_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
        order.courier_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryDelay)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)

    order.promised_shipping_time = order.expected_shipping_time
    order.promised_delivery_time = order.expected_delivery_time 

    session.commit()

def convert_store_to_normal(orderId):
    """
    This method is used to convert a pickup in store order into a normal one.
    """
    order = get_order(orderId)
    
    deliveryType = DeliveryType.PREPAID
    if order.cod:
        deliveryType = DeliveryType.COD
        
    
    logistics_client = LogisticsClient().get_client()
    logistics_info = logistics_client.getLogisticsEstimation(order.lineitems[0].item_id, order.customer_pincode, deliveryType)
    print "******"
    print "got provider : " + str(logistics_info.providerId)
    print "order provider : " + str(order.logistics_provider_id)
    print "delivery time : " + str(logistics_info.deliveryTime)
    print "codAllowed : " + str(logistics_info.codAllowed)
    print "******"

    # Check for new estimate. Raise exception if new address is not serviceable
    if logistics_info.deliveryTime == -1:
        raise TransactionServiceException(1, 'Location not serviceable')

    # COD services are not available everywhere
    if order.cod:
        if not logistics_info.codAllowed:
            raise TransactionServiceException(2, 'COD service not available')

    logistics_info.airway_billno = logistics_client.getEmptyAWB(logistics_info.providerId, deliveryType)
    order.tracking_id = logistics_info.airway_billno
    order.airwaybill_no = logistics_info.airway_billno
    order.logistics_provider_id = logistics_info.providerId
    order.pickupStoreId = 0
    session.commit()
    return True

def mark_order_cancellation_request_received(orderId):
    """
    Mark order as cancellation request received. If customer sends request of cancellation of
    a particular order, this method will be called. It will just change status of the order
    depending on its current status. It also records the previous status, so that we can move
    back to that status if cancellation request is denied.

    Parameters:
     - orderId
    """
    status_transition = {OrderStatus.COD_VERIFICATION_PENDING : OrderStatus.CANCEL_REQUEST_RECEIVED,
             OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.CANCEL_REQUEST_RECEIVED,
             OrderStatus.INVENTORY_LOW : OrderStatus.CANCEL_REQUEST_RECEIVED,
             OrderStatus.LOW_INV_PO_RAISED : OrderStatus.CANCEL_REQUEST_RECEIVED,
             OrderStatus.LOW_INV_REVERSAL_IN_PROCESS : OrderStatus.CANCEL_REQUEST_RECEIVED,
             OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT : OrderStatus.CANCEL_REQUEST_RECEIVED,
             OrderStatus.ACCEPTED : OrderStatus.CANCEL_REQUEST_RECEIVED,
             OrderStatus.BILLED : OrderStatus.CANCEL_REQUEST_RECEIVED,
             OrderStatus.PAYMENT_FLAGGED : OrderStatus.CANCEL_REQUEST_RECEIVED
             }
    
    order = get_order(orderId)
    if order.status not in status_transition.keys():
        raise TransactionServiceException(114, "This order can't be considered for cancellation")

    
    order.previousStatus = order.status
    order.status = OrderStatus.CANCEL_REQUEST_RECEIVED
    order.statusDescription = "Cancellation request received from user"
    session.commit()
    
def mark_order_as_lost_in_transit(orderId):
    """
    Parameters:
     - orderId
    """
    order = get_order(orderId)
    if order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST, OrderStatus.SHIPPED_TO_DESTINATION_CITY, OrderStatus.REACHED_DESTINATION_CITY, OrderStatus.FIRST_DELIVERY_ATTEMPT_MADE]:
        raise TransactionServiceException(115, "This order can't be marked as lost in transit")
    order.status = OrderStatus.LOST_IN_TRANSIT
    order.statusDescription = "Lost in Transit"
    session.commit()
    
def mark_transaction_as_payment_flag_removed(transactionId):
    """
    If we and/or payment gateway has decided to accept the payment, this method needs to be called.
    Changed transaction and all orders status to payment accepted.

    Parameters:
     - transactionId
    """
    transaction = get_transaction(transactionId)
    transaction.status = TransactionStatus.AUTHORIZED
    for order in transaction.orders:
        order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
        order.statusDescription = "Submitted to warehouse"
    session.commit()
    
def refund_transaction(transactionId, refundedBy, reason):
    """
    This method is called when a flagged payment is deemed unserviceable and the corresponding orders
    need to be cancelled

    Parameters:
     - transactionId
    """
    transaction = get_transaction(transactionId)
    transaction.status = TransactionStatus.FAILED
    for order in transaction.orders:
        if order.status in refund_status_transition.keys():
            refund_order(order.id, refundedBy, reason)
    session.commit()
    
def mark_order_cancellation_request_denied(orderId):
    """
    If we decide to not to cancel order, we will move the order to previous status.
    
    Parameters:
     - orderId
    """
    order = get_order(orderId)
    order.status = order.previousStatus
    order.previousStatus = None
    order.statusDescription = "Cancellation request denied"
    session.commit()
    
def mark_order_cancellation_request_confirmed(orderId):
    """
    If we decide to to cancel order, CRM will call this method to move the status of order to
    cancellation request confirmed. After this OM will be able to cancel the order.

    Parameters:
     - orderId
    """
    order = get_order(orderId)
    order.status = OrderStatus.CANCEL_REQUEST_CONFIRMED
    order.statusDescription = "Cancellation request confirmed"
    #session.commit()
    refund_order(order.id, "crm-team", "As per Customer's Request")
    
@memoized(3600)
def __is_Fofo(userClient, user_id):
    if not userClient or not userClient.isAlive():
        userClient = UserClient().get_client()
    return userClient, userClient.getPrivateDealUser(user_id).isFofo
    
def move_orders_to_correct_warehouse():
    completedOrders = []
    pendingOrders = ""
    orders = Order.query.filter(Order.status.in_(tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS]))).order_by(Order.promised_shipping_time).all()
    inventoryClient = InventoryClient().get_client()
    userClient = UserClient().get_client()

    warehouses = inventoryClient.getWarehouses(WarehouseType.OURS, InventoryType.GOOD, None, None, None)
    warehousesMap = {}
    for warehouse in warehouses:
        warehousesMap[warehouse.id] = warehouse
    for order in orders:
        lineitem = order.lineitems[0]
        print "order.fulfilmentWarehouseId", order.fulfilmentWarehouseId
        fulFilmentWarehouse = warehousesMap.get(order.fulfilmentWarehouseId)
        if fulFilmentWarehouse is not None:
            continue
        try:
            itemInventory = inventoryClient.getItemInventoryByItemId(lineitem.item_id)
        except:
            inventoryClient = InventoryClient().get_client()
            itemInventory = inventoryClient.getItemInventoryByItemId(lineitem.item_id)
        try:
            fulFilmentWarehouse = inventoryClient.getWarehouse(order.fulfilmentWarehouseId) 
        except:
            inventoryClient = InventoryClient().get_client()
            fulFilmentWarehouse = inventoryClient.getWarehouse(order.fulfilmentWarehouseId) 
            
        for warehouseId, availability in itemInventory.availability.iteritems():
            warehouse = warehousesMap.get(warehouseId)
            if warehouse is None:
                continue
            if availability - itemInventory.reserved[warehouseId] >= lineitem.quantity and warehouse.stateId==fulFilmentWarehouse.stateId: 
                try:
                    print "++++" + str(order.id) + "=" + str(order.fulfilmentWarehouseId) + "->"+ str(warehouseId)
                    change_warehouse(order.id, warehouseId)
                    completedOrders.append(order.id)
                except:
                    traceback.print_exc()
                    print "++++" + str(order.id)
                    pendingOrders = pendingOrders + str(order.id) + "|"
                break
            
                
    if len(completedOrders)==0 and len(pendingOrders)==0:
        return "No Order To Move"
    elif len(completedOrders)>0 and len(completedOrders) == len(orders):
        return "Completed"
    elif len(pendingOrders)>0:
        return pendingOrders
    else:
        return "No Order To Move"
    
def get_slipped_sipping_date_orders():
    return Order.query.filter(Order.status.in_(tuple([OrderStatus.COD_VERIFICATION_PENDING, OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.ACCEPTED, OrderStatus.BILLED]))).filter(Order.promised_shipping_time <= datetime.datetime.now()).order_by(Order.promised_shipping_time).all()
    
def get_cancelled_orders(cancelDateFrom, cancelDateTo):
    return Order.query.filter(Order.status == OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY).filter(Order.refund_timestamp >= cancelDateFrom).filter(Order.refund_timestamp <= cancelDateTo).all()
        
def mark_orders_as_po_raised(vendorId, itemId, quantity, estimate, isReminder):
    if isReminder:
        statuses = tuple([OrderStatus.LOW_INV_PO_RAISED_TIMEOUT, OrderStatus.LOW_INV_REVERSAL_TIMEOUT])
    else:
        statuses = tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW])
        
    orders = Order.query.filter(Order.status.in_(statuses)).order_by(Order.expected_shipping_time).all()
    for order in orders:
        lineitem = order.lineitems[0]
        if quantity >= lineitem.quantity and itemId == lineitem.item_id:
            if estimate > 0:
                add_delay_reason(order.id, DelayReason.OTHERS, estimate)
            order.status = OrderStatus.LOW_INV_PO_RAISED
            order.statusDescription = "PO Raised"
            quantity = quantity - lineitem.quantity
            store_inventory_action(order, itemId, HotspotAction.PO_RAISED, estimate, lineitem.quantity)
            session.commit()
    
def mark_orders_as_reversal_initiated(vendorId, itemId, quantity, estimate, isReminder):
    if isReminder:
        statuses = tuple([OrderStatus.LOW_INV_PO_RAISED_TIMEOUT, OrderStatus.LOW_INV_REVERSAL_TIMEOUT])
    else:
        statuses = tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW])
    orders = Order.query.filter(Order.status.in_(statuses)).order_by(Order.expected_shipping_time).all()
    for order in orders:
        lineitem = order.lineitems[0]
        if quantity >= lineitem.quantity and itemId == lineitem.item_id:
            if estimate > 0:
                add_delay_reason(order.id, DelayReason.OTHERS, estimate)
            order.status = OrderStatus.LOW_INV_REVERSAL_IN_PROCESS
            order.statusDescription = "Reversal Initiated"
            quantity = quantity - lineitem.quantity
            store_inventory_action(order, itemId, HotspotAction.REVERSAL_INITIATED, estimate, lineitem.quantity)
            session.commit()
    
def mark_orders_as_not_available(vendorId, itemId, quantity, estimate, isReminder):
    if isReminder:
        statuses = tuple([OrderStatus.LOW_INV_PO_RAISED_TIMEOUT, OrderStatus.LOW_INV_REVERSAL_TIMEOUT])
    else:
        statuses = tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW])
        
    orders = Order.query.filter(Order.status.in_(statuses)).order_by(Order.expected_shipping_time).all()
    for order in orders:
        lineitem = order.lineitems[0]
        if quantity >= lineitem.quantity and itemId == lineitem.item_id:
            order.status = OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT
            order.statusDescription = "Not Available"
            quantity = quantity - lineitem.quantity
            store_inventory_action(order, itemId, HotspotAction.NOT_AVAILABLE, estimate, lineitem.quantity)
            session.commit()
    
def store_inventory_action(order, itemId, hotspotAction, estimate, quantity):
    orderInv = OrderInventory.get_by(order_id=order.id)
    if not orderInv:
        orderInv = OrderInventory()
        orderInv.order = order
    orderInv.hotspotAction = hotspotAction  
    orderInv.estimate = estimate
    orderInv.itemId = itemId
    orderInv.timestamp = datetime.datetime.now()
    orderInv.quantity = quantity
    
'''    
def mark_orders_as_timeout(vendorId):
    ret_timeouts = {}
    orderInvs = OrderInventory.query.all()
    for orderInv in orderInvs:
        if orderInv.timestamp +  timedelta(days=orderInv.estimate) < datetime.datetime.now():
            if not ret_timeouts.has_key(orderInv.itemId):
                timeout = TimeoutSummary()
                ret_timeouts[orderInv.itemId] = timeout
            else:
                timeout = ret_timeouts.get(orderInv.itemId)
            if orderInv.hotspotAction == HotspotAction.PO_RAISED and orderInv.order.status == OrderStatus.LOW_INV_PO_RAISED:
                if timeout.poRaised:
                    timeout.poRaised += 1
                else:
                    timeout.poRaised = 1
                    
                if timeout.poEstimate:
                    timeout.poEstimate += orderInv.estimate
                else:
                    timeout.poEstimate = orderInv.estimate
                orderInv.order.status = OrderStatus.LOW_INV_PO_RAISED_TIMEOUT
                orderInv.order.statusDescription = "PO Raised Timeout"
            elif orderInv.hotspotAction == HotspotAction.REVERSAL_INITIATED and orderInv.order.status == OrderStatus.LOW_INV_REVERSAL_IN_PROCESS:
                if timeout.reversalInitiated:
                    timeout.reversalInitiated += 1
                else:
                    timeout.reversalInitiated = 1
                    
                if timeout.reversalEstimate:
                    timeout.reversalEstimate += orderInv.estimate
                else:
                    timeout.reversalEstimate = orderInv.estimate
                orderInv.order.status = OrderStatus.LOW_INV_REVERSAL_TIMEOUT
                orderInv.order.statusDescription = "Reversal Initiated Timeout"
            else:
                orderInv.delete()
    session.commit()
    return ret_timeouts
'''
    
    
def mark_orders_as_timeout(vendorId):
    ret_timeouts = {}
    orderInvs = OrderInventory.query.all()
    for orderInv in orderInvs:
        if not ret_timeouts.has_key(orderInv.itemId):
            timeout = TimeoutSummary()
            ret_timeouts[orderInv.itemId] = timeout
        else:
            timeout = ret_timeouts.get(orderInv.itemId)
        if orderInv.hotspotAction == HotspotAction.PO_RAISED and orderInv.order.status == OrderStatus.LOW_INV_PO_RAISED:
            if timeout.poRaised:
                timeout.poRaised += 1
            else:
                timeout.poRaised = 1
                
            if timeout.poEstimate:
                timeout.poEstimate = max(orderInv.estimate, timeout.poEstimate)
            else:
                timeout.poEstimate = orderInv.estimate
        elif orderInv.hotspotAction == HotspotAction.REVERSAL_INITIATED and orderInv.order.status == OrderStatus.LOW_INV_REVERSAL_IN_PROCESS:
            if timeout.reversalInitiated:
                timeout.reversalInitiated += 1
            else:
                timeout.reversalInitiated = 1
                
            if timeout.reversalEstimate:
                timeout.reversalEstimate = max(orderInv.estimate, timeout.reversalEstimate)
            else:
                timeout.reversalEstimate = orderInv.estimate
        orderInv.order.status = OrderStatus.INVENTORY_LOW
        orderInv.order.statusDescription = "Low Inventory"
        orderInv.delete()
    session.commit()
    return ret_timeouts
         
def get_order_for_awb(awb):
    orders = Order.query.filter(Order.airwaybill_no==awb).all()
    if not orders:
        orders = []
    
    return orders

def get_orders_for_provider_for_status(provider_id, order_status_list):
    if not provider_id or not order_status_list:
        raise TransactionServiceException(101, "bad provider id or order status")
    doa_provider = False
    for order_status in order_status_list:
        if order_status in [OrderStatus.DOA_PICKUP_CONFIRMED, OrderStatus.RET_PICKUP_CONFIRMED]:
            doa_provider = True
    if doa_provider:
        query = Order.query.filter_by(doa_logistics_provider_id = provider_id).filter(Order.status.in_(order_status_list))
    else:
        query = Order.query.filter_by(logistics_provider_id = provider_id).filter(Order.status.in_(order_status_list))
    return query.all()

def get_billed_orders_for_vendor(vendor_id, billing_date_from, billing_date_to):
    query = Order.query.filter(Order.status != OrderStatus.PAYMENT_PENDING)

    if vendor_id:
        query = query.filter_by(vendorId = vendor_id)

    if billing_date_from:
        query = query.filter(Order.billing_timestamp >= billing_date_from)

    if billing_date_to:
        query = query.filter(Order.billing_timestamp <= billing_date_to)

    return query.all()

def get_billed_orders(vendor_id, onlyVendorNotPaid, billing_date_from, billing_date_to):
    query = Order.query.filter(Order.billing_timestamp != None)
    
    if onlyVendorNotPaid:
        query = query.filter_by(vendor_paid = False)
    
    if vendor_id != -1:
        query = query.filter_by(vendorId = vendor_id)
    
    if billing_date_from:
        query = query.filter(Order.billing_timestamp >= billing_date_from)
    
    if billing_date_to:
        query = query.filter(Order.billing_timestamp <= billing_date_to)
    
    return query.all()

def save_bluedart_settlements(map_awb_and_amount):
    payment_client = PaymentClient().get_client()
    
    for awb, amount in map_awb_and_amount.iteritems():
        order = Order.get_by(airwaybill_no = awb)
        
        payment = payment_client.getSuccessfulPaymentForTxnId(order.transaction_id)
        settlement = PaymentSettlement()
        settlement.paymentId = payment.paymentId
        settlement.paymentGatewayId = payment.gatewayId
        settlement.netCollection = amount
        session.commit()

def save_payment_settlements(settlementDate, paymentGatewayId, referenceId, serviceTax, otherCharges, netCollection):
    originalOrderId = None
    if paymentGatewayId == 4:
        cod_order = Order.get_by(id = referenceId)
        if cod_order.originalOrderId:
            originalOrderId = cod_order.originalOrderId
        else:
            originalOrderId = referenceId
    settlement = PaymentSettlement()
    settlement.settlementDate = settlementDate
    settlement.paymentGatewayId = paymentGatewayId
    if originalOrderId is not None:
        settlement.originalOrderId = originalOrderId
    settlement.referenceId = referenceId
    settlement.serviceTax = serviceTax
    settlement.otherCharges = otherCharges
    settlement.netCollection = netCollection
    session.commit()

def get_settlement_for_Prepaid(reference_id, is_refund = False):
    query = PaymentSettlement.query.filter_by(referenceId = reference_id).filter(PaymentSettlement.paymentGatewayId != 4)
    
    if is_refund:
        query = query.filter(PaymentSettlement.netCollection < 0)
    else:
        query = query.filter(PaymentSettlement.netCollection > 0)
    
    return query.first()

def get_settlement_for_Cod(orderId, is_refund = False):
    query = PaymentSettlement.query.filter_by(originalOrderId = orderId)
    
    if is_refund:
        query = query.filter(PaymentSettlement.netCollection < 0)
    else:
        query = query.filter(PaymentSettlement.netCollection > 0)
    
    return query.first()

def save_ebs_settlement_summary(settlement_id, settlement_date, transaction_date_from, transaction_date_to, amount):
    summary = EBSSettlementSummary.query.filter_by(settlementId = settlement_id).first()
    if summary is None:
        summary = EBSSettlementSummary()
        summary.settlementId = settlement_id
        summary.settlementDate = settlement_date
        summary.transactionDateFrom = transaction_date_from
        summary.transactionDateTo = transaction_date_to
        summary.amount = amount
        summary.detailsUploaded = False
        session.commit()

def get_ebs_settlement_summaries():
    summaries = EBSSettlementSummary.query.filter_by(detailsUploaded = False).order_by(EBSSettlementSummary.transactionDateFrom).all()
    summary_map = {}
    
    for summary in summaries:
        transactionDateFrom = str(summary.transactionDateFrom.year) + '-' + str(summary.transactionDateFrom.month) + '-' + str(summary.transactionDateFrom.day)
        transactionDateTo = str(summary.transactionDateTo.year) + '-' + str(summary.transactionDateTo.month) + '-' + str(summary.transactionDateTo.day)
        
        summary_map[summary.settlementId] = "%s to %s" %(transactionDateFrom, transactionDateTo)
    
    return summary_map

def get_ebs_settlement_date(settlement_id):
    summary = EBSSettlementSummary.get_by(settlementId = settlement_id)
    
    if summary:
        return summary.settlementDate

def mark_ebs_settlement_uploaded(settlement_id):
    summary = EBSSettlementSummary.get_by(settlementId = settlement_id)
    
    if summary:
        summary.detailsUploaded = True
        session.commit()

def get_settlements_by_date(settlement_date_from, settlement_date_to, is_refund):
    query = PaymentSettlement.query
    
    if settlement_date_from:
        query = query.filter(PaymentSettlement.settlementDate >= settlement_date_from)
        
    if settlement_date_to:
        query = query.filter(PaymentSettlement.settlementDate <= settlement_date_to)
    
    if is_refund is True:
        query = query.filter(PaymentSettlement.netCollection < 0.0)
    else:
        query = query.filter(PaymentSettlement.netCollection > 0.0)
    
    return query.all()

def get_reshipped_order_ids(order_ids):
    reshipped_order_ids = []
    return [order.new_order_id for order in Order.query.filter(Order.new_order_id.in_(order_ids)).all()]

def get_shipping_confirmation_email_body(logisticsTxnId, orderList):
    order = orderList[0]
    html = """
<html>
<body>
<div>
    <p>Dear Customer,</p>
    <p>
        We are pleased to inform that the following items in your order: $master_order_id have been Shipped.
        Details of your shipment:
        <br>
        <br>
        <div>
            <table>
            <tr><td colspan="8"><hr /></td></tr>
            <tr><td colspan="8" align="left"><b>Order Details</b></td></tr>
            <tr><td colspan="8"><hr /></td></tr>
            <tr>
                <th width="100">Sub Order Id</th>
                <th>Product Name</th>
                <th width="100">Quantity</th>
                <th width="100">Unit Price</th>
                <th width="100">Amount</th>
                <th width="100">Insurance Amount</th>
                <th width="100">OTG Covered</th>
                <th width="100">Freebie Item</th>
            </tr>"""
            
    total_amount = 0.0
    total_shipping = 0.0
    advanceAmount = 0.0
    hasOtg = False
    hasFreebie = False
    hasSplitOrder = False
    hasInsurer = False
    splitOrdersMap = {}
    for orderObj in orderList:
        lineitem = orderObj.lineitems[0]
        lineitem_total_price = round(lineitem.total_price, 2)
        total_amount += lineitem_total_price + orderObj.shippingCost
        total_shipping += orderObj.shippingCost
        advanceAmount += orderObj.advanceAmount
        html += """
            <tr>
            <td align="center">"""+str(orderObj.id)+"""</td>
            <td>"""+str(lineitem)+"""</td>
            <td align="center">"""+("%.0f" % lineitem.quantity)+"""</td>
            <td align="center">"""+("%.2f" % lineitem.unit_price)+"""</td>
            <td align="center">"""+("%.2f" % lineitem_total_price)+"""</td>"""
        if orderObj.insurer > 0:
            hasInsurer = True
            total_amount += orderObj.insuranceAmount
            html += """<td align="center">"""+("%.2f" % orderObj.insuranceAmount) +"""</td>"""
        else:
            html += """<td align="center">0.00</td>"""
        
        if orderObj.freebieItemId:
            hasFreebie = True
            catalog_client = CatalogClient().get_client()
            item = catalog_client.getItem(orderObj.freebieItemId)
            html += """<td align="center">"""+item.brand+" "+item.modelName+ " " + item.modelNumber +"""</td>"""
        else:
            html += """<td align="center"></td>"""
            freebieOrderId = get_order_attribute_value(orderObj.id, "freebieOrderId")
            if freebieOrderId != "":
                freebieOrder = get_order(freebieOrderId)
                hasSplitOrder = True
                splitOrdersMap[orderObj.id] = freebieOrder
        
        html += """
                </tr>
            """
    
    html += """
        <tr><td colspan=8>&nbsp;</td></tr>
        <tr><td colspan=8><hr /></td></tr>"""
            
    amount_string = "Total Amount"
    if advanceAmount > 0:
        html += """<tr>
                   <td colspan="7">Advance Amount</td>
                   <td>Rs."""+("%.2f" % advanceAmount)+"""
                   </tr>
            """
        total_amount -= advanceAmount
        amount_string = "Balance Amount"
    shipping_string = "Shipping Charges"
    if total_shipping > 0:
        html = html + """<tr>\
                            <td colspan=7>"""+shipping_string+"""</td>\
                            <td> Rs. """+("%.2f" % total_shipping)+"""</td>\
                        </tr>"""        
    html += """<tr>
               <td colspan="7">"""+amount_string+"""</td>
               <td>Rs."""+("%.2f" % total_amount)+"""
               </tr>
            """
    
    
    html += """          
            </table>
       </div>
        The shipment will be delivered to you via $courier_name Courier Service vide AWB# $awb.
        
    </p>
    <p>
        Expected delivery date for your order is $expected_delivery_date.
        <br/>
        $otg_text
        $freebie_text
        <br/> 
        The product can be delivered to you between business hours of 10am to 6pm.
        You can Check the current shipment updates for your product online at <a href="www.saholic.com/myaccount">Open Orders</a>.
    </p>
    <p>
        For scheduling the delivery of your product at some specific timings, we recommend you to contact the helpline for $courier_name with your complete Airway Bill Number.
    </p>
    <p>$courier_name: $courier_phone</p>
    <p>
        Warm Regards,<br />
        $source_name Team
    </p>
</div>
</body>
</html>
    """
    logistics_providers = {1: {'name': 'BlueDart', 'phone': '011-66111234'}, 6: {'name': 'RedExpress', 'phone': '8373915813'}, 3: {'name': 'Delhivery', 'phone' : '0124- 4212200'}, 7: {'name': 'FedEx', 'phone' : '0120-4354176'}}
    provider_name = logistics_providers[order.logistics_provider_id]['name']
    provider_phone = logistics_providers[order.logistics_provider_id]['phone']
    
    lclient = LogisticsClient().get_client()
    logistics_info = lclient.getLogisticsEstimation(order.lineitems[0].item_id, order.customer_pincode, DeliveryType.PREPAID)
    dt = datetime.datetime.strptime(str(order.shipping_timestamp), "%Y-%m-%d %H:%M:%S")
    shipping_date = dt.strftime("%A, %d %B %Y %I:%M%p")
    new_expected_delivery_timestamp = datetime.datetime.now() + timedelta(adjust_delivery_time(datetime.datetime.now(), (logistics_info.deliveryTime - logistics_info.shippingTime)))
    if order.source ==6:
        new_expected_delivery_timestamp = datetime.datetime.now() + timedelta(adjust_delivery_time(datetime.datetime.now(), 3))
    new_courier_delivery_time = datetime.datetime.now() + timedelta(adjust_delivery_time(datetime.datetime.now(), (logistics_info.deliveryTime - logistics_info.shippingTime - logistics_info.deliveryDelay)))
    #update new expected timestamp
    
    for orderObj in orderList:
        if orderObj.cod:
            new_expected_delivery_timestamp = new_expected_delivery_timestamp.replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0, microsecond=0)
            orderObj.courier_delivery_time = new_courier_delivery_time.replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0, microsecond=0)
        else:
            new_expected_delivery_timestamp = new_expected_delivery_timestamp.replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0, microsecond=0)
            orderObj.courier_delivery_time = new_courier_delivery_time.replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0, microsecond=0)
        orderObj.expected_delivery_time = new_expected_delivery_timestamp
    session.commit()
    
    new_expected_delivery_date = new_expected_delivery_timestamp.strftime("%A, %d %B %Y")
    otg_text = ""
    if hasOtg:
        promised_delivery_date = order.promised_delivery_time.strftime("%A, %d %B %Y")
        if new_expected_delivery_timestamp > order.promised_delivery_time:
            otg_text = 'Your order is covered under our <a href="http://www.saholic.com/static/on-time-guarantee">On Time Guarantee; We Pay if we Delay.</a> We apologize in advance for the expected delay from our initial promised delivery date of ' + promised_delivery_date + '. Your will receive a Gift Voucher Code at time of delivery based on the actual delay. You can use the same to buy any Mobile, Camera, Laptop, Tablet, Accessory or Mobile/DTH Recharge from our website.'
        elif new_expected_delivery_timestamp < order.promised_delivery_time:
            otg_text = 'We are happy to inform you that we expect to deliver your order before time. Your order is covered under our <a href="http://www.saholic.com/static/on-time-guarantee">On Time Guarantee; We Pay if we Delay.</a> Please note, compensation for delay (If Any) will be computed from the initial promised delivery date of ' + promised_delivery_date + '.'
        else:            
            otg_text = 'Your order is covered under our <a href="http://www.saholic.com/static/on-time-guarantee">On Time Guarantee; We Pay if we Delay.</a> We are committed to deliver your order by our initial promised delivery date of ' + promised_delivery_date + '.'
    
    freebie_text = "<br/>"
    if hasFreebie and not hasSplitOrder:
        freebie_text = freebie_text + "We have also shipped your freebies with eligible products as promised<br/>"
    elif not hasFreebie and hasSplitOrder:
        freebie_text = freebie_text + "We wish to inform you that your freebie item(s): <br/>"
        for splitOrder in splitOrdersMap.values():
            freebieLineItem = get_line_items_for_order(splitOrder.id)[0]
            freebie_text = freebie_text + freebieLineItem.brand + " " + freebieLineItem.model_name + " " + freebieLineItem.model_number + " will be sent as a separate order with OrderId : " + str(splitOrder.id) + " and is expected to be delivered on " + splitOrder.expected_delivery_time.strftime("%A, %d %B %Y")+ "<br/>"
    elif hasFreebie and hasSplitOrder:
        freebie_text = freebie_text + "We have also shipped some of your freebies with eligible products as promised and rest freebie item(s) details are given below: <br/>"
        for splitOrder in splitOrdersMap.values():
            freebieLineItem = get_line_items_for_order(splitOrder.id)[0]
            freebie_text = freebie_text + freebieLineItem.brand + " " + freebieLineItem.model_name + " " + freebieLineItem.model_number + " will be sent as a separate order with OrderId : " + str(splitOrder.id) + " and is expected to be delivered on " + splitOrder.expected_delivery_time.strftime("%A, %d %B %Y")+ "<br/>"
    else:
        freebie_text = ""
                   
    info = dict(master_order_id = logisticsTxnId, shipping_datetime = shipping_date, awb = order.airwaybill_no, expected_delivery_date = new_expected_delivery_date, courier_name = provider_name, courier_phone = provider_phone, source_name = source_name, otg_text = otg_text, freebie_text = freebie_text)
    return Template(html).substitute(info)

def enqueue_shipping_confirmation_email(logisticsTxnId, orderList):
    # Calling helper service on production
    order = orderList[0]
    helperClient = HelperClient(host_key = "helper_service_server_host_prod").get_client()
    emailSubject = 'Shipping Details for Master Order ID: ' + logisticsTxnId
    emailBody = get_shipping_confirmation_email_body(logisticsTxnId, orderList)
    helperClient.saveUserEmailForSending([order.customer_email], SaholicHelpEmailId, emailSubject, emailBody, logisticsTxnId, 'ShippingConfirmation', [], [], order.source)
    
    logistics_providers = {1: {'name': 'BlueDart', 'phone': '011-66111234'}, 6: {'name': 'RedExpress', 'phone': '8373915813'}, 3: {'name': 'Delhivery', 'phone' : '0124- 4212200'}, 7: {'name': 'FedEx', 'phone' : '0120-4354176'}}
    provider_name = logistics_providers[order.logistics_provider_id]['name']
    if order.source == OrderSource.WEBSITE :
        send_transaction_sms(order.customer_id, order.customer_mobilenumber, "Dear Customer, We have shipped your order: " + logisticsTxnId + " through "+provider_name + " AWB No. " + order.airwaybill_no, SmsType.TRANSACTIONAL, True)    
    
    
def get_order_distribution_by_status(start_date, end_date):
    query = session.query(Order.status, func.count(Order.id)).group_by(Order.status)
    query = query.filter(Order.created_timestamp >= start_date)
    query = query.filter(Order.created_timestamp <= end_date)
    return query.all()

def get_order_ids_for_status(status, start_datetime, end_datetime):
    query = Order.query.filter(Order.status == status)
    query = query.filter(Order.created_timestamp >= start_datetime)
    query = query.filter(Order.created_timestamp <= end_datetime)
    return query.all()

def update_orders_as_paid_to_vendor(order_id):
    paid_order = Order.get_by(id = order_id)
    
    if paid_order.originalOrderId is None:
        paid_order.vendor_paid = True
        session.commit()
        
        for order in Order.query.filter(Order.originalOrderId == order_id).all():
            order.vendor_paid = True
            session.commit()
    else:
        original_order_id = paid_order.originalOrderId
        
        for order in Order.query.filter(Order.originalOrderId == original_order_id).all():
            order.vendor_paid = True
            session.commit()
        
        original_order = Order.get_by(id = original_order_id)
        if original_order:  original_order.vendor_paid = True
        session.commit()
        
def update_COD_agent(agentEmailId, orderId):
    order = Order.get_by(id=orderId)
    if not order:
        raise TransactionServiceException(101, "No order found for the given order id" + str(orderId))
    agent = CodVerificationAgent()
    agent.orderId = orderId
    agent.verificationAgent = agentEmailId
    session.commit()
    

def update_order_only_as_paid_to_vendor(order_id):
    paid_order = Order.get_by(id = order_id)
    if paid_order is None:
        raise TransactionServiceException(116, "No order found for the given order id")
    paid_order.vendor_paid = True
    session.commit()

def get_refunded_orders_marked_paid():
    orderStatusGroup = OrderStatusGroups()
    return Order.query.filter(Order.vendor_paid == 1).filter(Order.status.in_(orderStatusGroup.refundedOrders)).all()

def get_all_verification_agents(minOrderId, maxOrderId):
    return CodVerificationAgent.query.filter(CodVerificationAgent.orderId >= minOrderId).filter(CodVerificationAgent.orderId <= maxOrderId).all()

def get_all_attributes_for_order_id(orderId):
    return Attribute.query.filter(Attribute.orderId == orderId).all()

def set_order_attribute_for_transaction(transactionId, t_attribute):
    for order in Order.query.filter_by(transaction_id = transactionId).all():
        attribute = Attribute()
        attribute.orderId = order.id
        attribute.name = t_attribute.name
        attribute.value = t_attribute.value
        session.commit()

def accept_order_for_item(itemId, quantity, fulfilmentWarehouseId, billingWarehouseId):
    inventoryClient = InventoryClient().get_client()
    inventory = inventoryClient.getItemInventoryByItemId(itemId)
    orders = Order.query.filter(Order.fulfilmentWarehouseId != fulfilmentWarehouseId).filter(Order.warehouse_id == billingWarehouseId).filter(Order.status.in_(tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.COD_VERIFICATION_PENDING, OrderStatus.ACCEPTED, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS]))).order_by(Order.expected_shipping_time).all()
    for order in orders:
        if order.source ==6:
            continue
        lineitem = order.lineitems[0]
        if lineitem.item_id == itemId and lineitem.quantity <= quantity:
            warehouse = inventoryClient.getWarehouse(order.fulfilmentWarehouseId)
            if not warehouse.billingWarehouseId and warehouse.warehouseType == WarehouseType.THIRD_PARTY:
                if (inventory.availability[fulfilmentWarehouseId] >= inventory.reserved[fulfilmentWarehouseId] + lineitem.quantity) and inventory.availability[order.fulfilmentWarehouseId] < inventory.reserved[order.fulfilmentWarehouseId]:
                    inventoryClient.reduceReservationCount(itemId, order.fulfilmentWarehouseId, sourceId, order.id, lineitem.quantity)
                    inventoryClient.reserveItemInWarehouse(itemId, fulfilmentWarehouseId, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), lineitem.quantity)
                    order.fulfilmentWarehouseId = fulfilmentWarehouseId
                    quantity -= lineitem.quantity
                    session.commit()
                    if not quantity:
                        break

def get_recharge_order(rechargeOrderId):
    return RechargeOrder.get_by(id = rechargeOrderId)

def get_recharge_orders(userId, offset=None, limit=None):
    if offset > 0 and limit > 0:
        return RechargeOrder.query.filter_by(userId = userId).order_by(RechargeOrder.id.desc()).offset(offset).limit(limit).all()
    return RechargeOrder.query.filter_by(userId = userId).all()

def update_recharge_order_status(rechargeOrderId, rechargeOrderStatus):
    recharge_order = get_recharge_order(rechargeOrderId)
    if rechargeOrderStatus == RechargeOrderStatus.PAYMENT_FAILED:
        recharge_order.status = RechargeOrderStatus.PAYMENT_FAILED
        session.commit()
        return True
    
    if rechargeOrderStatus == RechargeOrderStatus.RECHARGE_FAILED:
        if recharge_order.status == RechargeOrderStatus.RECHARGE_FAILED:
            return True
        recharge_order.status = RechargeOrderStatus.RECHARGE_FAILED
        recharge_order.responseTimestamp = datetime.datetime.now()
        session.commit()
        #update_amount_in_wallet(recharge_order.userId, recharge_order.walletAmount, recharge_order.id, 1)
        reverse_money_to_wallet(recharge_order.userId, recharge_order.walletAmount, recharge_order.id, WalletReferenceType.RECHARGE)
        #update_amount_in_wallet(recharge_order.userId, recharge_order.totalAmount - recharge_order.walletAmount - recharge_order.couponAmount, recharge_order.id, 0)
        add_amount_in_wallet(recharge_order.userId, recharge_order.totalAmount - recharge_order.walletAmount - recharge_order.couponAmount, recharge_order.id, 
                             WalletReferenceType.RECHARGE, False, "Recharge failed, Refunded", commit_session=True)
        enqueue_recharge_info_email(recharge_order)
        return True
    
    if rechargeOrderStatus == RechargeOrderStatus.RECHARGE_SUCCESSFUL:
        if recharge_order.status == RechargeOrderStatus.RECHARGE_SUCCESSFUL:
            return True
        recharge_order.status = RechargeOrderStatus.RECHARGE_SUCCESSFUL
        recharge_order.responseTimestamp = datetime.datetime.now()
        recharge_order.invoiceNumber = get_next_invoice_number(OrderType.RECHARGE)
        #Add commissions as well
        pendingRechargeCommissions = PendingRechargeCommissions.query.filter(PendingRechargeCommissions.user_id == recharge_order.userId).first()
        if not pendingRechargeCommissions:
            pendingRechargeCommissions = PendingRechargeCommissions()
            pendingRechargeCommissions.user_id = recharge_order.userId
            pendingRechargeCommissions.approvedAmountInPaise = 0 
        pendingRechargeCommissions.lastUpdated = recharge_order.responseTimestamp
        if isinstance(recharge_order, MobileRechargeOrder):
            commissions = mobileRechargeCommission
        if isinstance(recharge_order, DTHRechargeOrder):
            commissions = dthRechargeCommission
        pendingRechargeCommissions.approvedAmountInPaise += int(round(recharge_order.totalAmount*commissions))
        #Add amount to wallet if PendingRechargeCommissions are greater than Rs. 10
        if pendingRechargeCommissions.approvedAmountInPaise > 1000:
            commission_amount = int(pendingRechargeCommissions.approvedAmountInPaise/100)
            pendingRechargeCommissions.approvedAmountInPaise = pendingRechargeCommissions.approvedAmountInPaise%100
            add_amount_in_wallet(recharge_order.userId, commission_amount, str(datetime.datetime.now()), WalletReferenceType.RECHARGE_COMMISSIONS, True, "Cashback for Recharge commissions", False)
        session.commit()
        gvs = allocate_gift_voucher(recharge_order)
        enqueue_recharge_info_email(recharge_order, gvs)
        return True
    
    if rechargeOrderStatus == RechargeOrderStatus.RECHARGE_FAILED_REFUNDED:
        if recharge_order.status == RechargeOrderStatus.RECHARGE_FAILED_REFUNDED:
            return True
        recharge_order.status = RechargeOrderStatus.RECHARGE_FAILED_REFUNDED
        recharge_order.responseTimestamp = datetime.datetime.now()
        session.commit()
        #update_amount_in_wallet(recharge_order.userId, recharge_order.walletAmount, recharge_order.id, 1)
        #update_amount_in_wallet(recharge_order.userId, recharge_order.totalAmount - recharge_order.walletAmount - recharge_order.couponAmount, recharge_order.id, 0)
        reverse_money_to_wallet(recharge_order.userId, recharge_order.walletAmount, recharge_order.id, WalletReferenceType.RECHARGE)
        add_amount_in_wallet(recharge_order.userId, recharge_order.totalAmount - recharge_order.walletAmount - recharge_order.couponAmount, recharge_order.id, 
                             WalletReferenceType.RECHARGE, False, "Recharge failed, Refunded", commit_session=True)
        enqueue_recharge_info_email(recharge_order)
        return True
            
    if rechargeOrderStatus == RechargeOrderStatus.PAYMENT_SUCCESSFUL:
        gvs = {}
        recharge_order.status = RechargeOrderStatus.PAYMENT_SUCCESSFUL
        session.commit()
        if recharge_order.walletAmount > 0:
            wallet = get_user_wallet(recharge_order.userId)
            if wallet and wallet.amount >= recharge_order.walletAmount:
                #update_amount_in_wallet(recharge_order.userId, -recharge_order.walletAmount, recharge_order.id, 1)
                consume_wallet(recharge_order.userId, recharge_order.walletAmount, recharge_order.id, WalletReferenceType.RECHARGE, "Against recharge")
            else:
                ## User is trying to play smart. Let us first mark his recharge failed and then credit payment amount to his wallet.
                recharge_order.status = RechargeOrderStatus.RECHARGE_FAILED
                recharge_order.responseTimestamp = datetime.datetime.now()
                session.commit()
                #update_amount_in_wallet(recharge_order.userId, recharge_order.totalAmount - recharge_order.walletAmount - recharge_order.couponAmount, recharge_order.id, 0)
                add_amount_in_wallet(recharge_order.userId, recharge_order.totalAmount - recharge_order.walletAmount - recharge_order.couponAmount, recharge_order.id, 
                                     WalletReferenceType.RECHARGE, False, "Refunded against failed recharge", commit_session=True)
                raise TransactionServiceException(103, 'Wallet amount has already been used')
    
        try:
            if activate_recharge_txn(recharge_order):
                recharge_order.invoiceNumber = get_next_invoice_number(OrderType.RECHARGE)
                session.commit()
                gvs = allocate_gift_voucher(recharge_order)
                return True
            else:
                #update_amount_in_wallet(recharge_order.userId, recharge_order.walletAmount, recharge_order.id, 1)
                #update_amount_in_wallet(recharge_order.userId, recharge_order.totalAmount - recharge_order.walletAmount - recharge_order.couponAmount, recharge_order.id, 0)
                reverse_money_to_wallet(recharge_order.userId, recharge_order.walletAmount, recharge_order.id, WalletReferenceType.RECHARGE)
                add_amount_in_wallet(recharge_order.userId, recharge_order.totalAmount - recharge_order.walletAmount - recharge_order.couponAmount, recharge_order.id, 
                                     WalletReferenceType.RECHARGE, False, commit_session=True)
                session.commit()
                return False
        except:
            if isinstance(recharge_order, MobileRechargeOrder):
                if recharge_order.mobileOperatorId not in (getSyncOperatorsForRecharge()):
                    print "I Am returned from here", recharge_order.walletAmount
                    return True 
            if isinstance(recharge_order, DTHRechargeOrder):
                if recharge_order.dthOperatorId not in (getSyncOperatorsForRecharge()):
                    return True
            transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
            transaction_requiring_extra_processing.category = 'RECHARGE_UNKNOWN'
            transaction_requiring_extra_processing.transaction_id = recharge_order.transaction_id
            session.commit()
            return False
        finally:
            enqueue_recharge_info_email(recharge_order, gvs)
            
def disburse_recharge_commissions():
    pass
    #get the minimum amount that can be disbursed
    
            

def allocate_gift_voucher(recharge_order):            
    totalAmount = recharge_order.totalAmount
    gvs = {}
    return gvs
    if totalAmount >= 100:
        totalAmount=500 if totalAmount >= 500 else totalAmount
        try:
            pc = PromotionClient().get_client()
            enddate = datetime.datetime.now() + datetime.timedelta(days=45)
            while totalAmount > 0:
                gvAmount = totalAmount if totalAmount <= 250 else 250
                totalAmount = totalAmount - gvAmount
                gv = pc.createCoupon(26,CouponCategory.CUSTOMER_SATISFACTION , to_java_date(enddate), recharge_order.userEmailId, gvAmount, False, 1, None)
                gvs[gv] = gvAmount  
        except:
            print "Not able to create gift voucher for user " + recharge_order.userEmailId
    return gvs     

def activate_recharge_txn(recharge_order):
    if isinstance(recharge_order, MobileRechargeOrder):
        rechargeType = 'MTP'
        deviceNumber = recharge_order.mobileNumber
        strProviderCode = get_provider_code(recharge_order.mobileOperatorId)
        plan = recharge_order.rechargePlan
        if recharge_order.mobileOperatorId not in (getSyncOperatorsForRecharge()):
            __activate_recharge_txn_asynchronously(recharge_order,rechargeType,deviceNumber,strProviderCode,plan)
    if isinstance(recharge_order, DTHRechargeOrder):
        rechargeType = 'RCH'
        deviceNumber = recharge_order.dthAccountNumber
        strProviderCode = get_provider_code(recharge_order.dthOperatorId)
        plan = ''
        if recharge_order.dthOperatorId not in (getSyncOperatorsForRecharge()):
            __activate_recharge_txn_asynchronously(recharge_order,rechargeType,deviceNumber,strProviderCode,plan)
    retStatus, retCode, spiceTID, description, aggTID, providerTID = RechargeService.rechargeDevice(recharge_order.id, rechargeType, strProviderCode, deviceNumber, recharge_order.totalAmount, plan, '' ,RechargeMode._NAMES_TO_VALUES.get('SYNC'))
    recharge_order.spiceTID = spiceTID
    recharge_order.responseTimestamp = datetime.datetime.now()
    recharge_order.description = description
    session.commit()
    if retCode == '00' and retStatus == 'S':
        recharge_order.status = RechargeOrderStatus.RECHARGE_SUCCESSFUL
        session.commit()
        if recharge_order.couponAmount > 0 :
            couponCode = recharge_order.couponCode
            if couponCode :
                try:
                    promotionServiceClient = PromotionClient()
                    promotionServiceClient.get_client().trackCouponUsage(couponCode, recharge_order.id, recharge_order.userId)
                except Exception as e:
                    print 'Unable to track the usage of coupon - ' + recharge_order.couponCode + ' and spiceTID - ' + str(spiceTID)
                    print e
        return True
    elif retCode in ('STO', 'US', 'UP', 'STP') and retStatus == 'S':
        if recharge_order.couponAmount > 0 :
            couponCode = recharge_order.couponCode
            if couponCode :
                try:
                    promotionServiceClient = PromotionClient()
                    promotionServiceClient.get_client().trackCouponUsage(couponCode, recharge_order.id, recharge_order.userId)
                except Exception as e:
                    print 'Unable to track the usage of coupon - ' + recharge_order.couponCode + ' and spiceTID - ' + str(spiceTID)
                    print e
        raise TransactionServiceException(111, "No response from operator within specified time")
    else:
        recharge_order.status = RechargeOrderStatus.RECHARGE_FAILED
        if retCode in ('SVAI', 'SVIA', 'AI', 'IA', 'EAI', 'SVEAI'):
            recharge_order.description = "Invalid Amount"
        elif retCode in ('ECL', 'SVECL', 'SCL'):
            recharge_order.description = "Customer Exceeded Daily Limit"
        elif retCode in ('CI', 'ECI', 'ESP', 'SVCI', 'SVECI', 'SVESP', 'SV_ECI', 'SV ECI'):
            recharge_order.description = "Invalid Device Number"
        session.commit()
        return False

def __activate_recharge_txn_asynchronously(recharge_order, rechargeType, deviceNumber, strProviderCode, plan):
    if recharge_order.couponAmount > 0 :
        couponCode = recharge_order.couponCode
        if couponCode :
            try:
                promotionServiceClient = PromotionClient()
                promotionServiceClient.get_client().trackCouponUsage(couponCode, recharge_order.id, recharge_order.userId, recharge_order.couponAmount, True)
            except Exception as e:
                print 'Unable to track the usage of coupon - ' + recharge_order.couponCode + ' and rechargeOrderId - ' + str(recharge_order.id)
                raise(TransactionServiceException(111, "Unable to track coupon usage"))
    retStatus, retCode, spiceTID, description, aggTID, providerTID = RechargeService.rechargeDevice(recharge_order.id, rechargeType, strProviderCode, deviceNumber, recharge_order.totalAmount, plan, '' ,RechargeMode._NAMES_TO_VALUES.get('ASYNC'))
    recharge_order.spiceTID = spiceTID
    recharge_order.responseTimestamp = datetime.datetime.now()
    recharge_order.status = RechargeOrderStatus.RECHARGE_UNKNOWN
    recharge_order.description = 'Recharge In Process'
    session.commit()
    raise(TransactionServiceException(111, "Explicitly raising exception, will try to fetch final status"))


def get_user_wallet(userId):
    wallet = UserWallet.get_by(userId = userId)
    if not wallet:
        wallet = UserWallet()
        wallet.amount = 0
        wallet.userId = userId
        wallet.refundable_amount = 0
    return wallet

def get_user_wallet_history(userId, offset=0, limit=50):
    wallet = get_user_wallet(userId)
    if wallet.id:
        return UserWalletHistory.query.filter_by(wallet = wallet).order_by(desc(UserWalletHistory.id)).offset(offset).limit(limit).all()
    else:
        return []

def get_provider(providerId):
    return ServiceProvider.query.filter_by(id = providerId).one()

def get_provider_code(providerId):
    return ServiceProvider.query.filter_by(id = providerId).one().code

def get_provider_by_code(providerCode):
    return ServiceProvider.query.filter_by(code = providerCode).one()

def get_service_providers(recharge_type, only_active):
    query = ServiceProvider.query.filter_by(type = recharge_type)
    if only_active:
        query = query.filter_by(status = only_active)
    providers = query.all()
    serviceProviders = {}
    for provider in providers:
        serviceProviders[provider.id] = provider.name
    return serviceProviders

def get_service_provider_for_device(rechargeType, deviceNumber):
    #FIXME : This method does not take care of MNP cases.
    if rechargeType == RechargeType.MOBILE:
        serviceType = 'MTP'
    if rechargeType == RechargeType.DTH:
        serviceType = 'RCH'
    
    provider = None
    try:
        series = OperatorSeries.get_by(series = int(deviceNumber))
        if series:
            providerId = series.operatorId
            tc = TelecomCircle.get_by(id = series.circleId)
            operatorCircle = tc.code
            provider = get_provider(providerId)
        else:
            l = len(deviceNumber)
            if l < 10:
                deviceNumber = deviceNumber + "0" * (10-l) 
            providerCode, operatorCircle = RechargeService.checkServiceProviderApi(serviceType, deviceNumber)
            if providerCode == 'SNS' or providerCode == '':
                providerId = 0
            else:
                provider = get_provider_by_code(providerCode)
                providerId = provider.id
    except:
        providerId = -1
        operatorCircle = ""
        
    deviceNumberInfo = DeviceNumberInfo()
    deviceNumberInfo.circleCode = operatorCircle
    deviceNumberInfo.operatorId = providerId
    if provider:
        deviceNumberInfo.operatorName = provider.name
    return deviceNumberInfo

def validate_recharge(rechargeType, deviceNumber, userSelectedProviderId, clientAddress):
    addressComponents = clientAddress.split('.')
    n = 3
    addressNumber = 0
    for addressComponent in addressComponents :
        addressNumber = addressNumber + (int(addressComponent) * pow(256, n))
        n = n-1
    matchingRow = BlockedIpRange.query.filter(BlockedIpRange.start <= addressNumber).filter(BlockedIpRange.end >= addressNumber).first()
    #if a match is found then check if date has expired.
    if matchingRow :
        #if expiredOn is None that means this range is permanently blocked.
        if matchingRow.expiredOn is None:
            deniedIpAddress = DeniedIpAddress()
            deniedIpAddress.ip = clientAddress
            deniedIpAddress.deniedOn = datetime.datetime.now()
            deniedIpAddress.deviceNumber = deviceNumber
            deniedIpAddress.rechargeType = rechargeType
            session.commit()
            return "Oops! There seems to be a problem. Please try after some time."
        else :
            #if expired is not None and the blockage is still not expired then block the ip.
            if matchingRow.expiredOn > datetime.datetime.now():
                deniedIpAddress = DeniedIpAddress()
                deniedIpAddress.ip = clientAddress
                deniedIpAddress.deniedOn = datetime.datetime.now()
                deniedIpAddress.deviceNumber = deviceNumber
                deniedIpAddress.rechargeType = rechargeType
                session.commit()
                return "Oops! There seems to be a problem. Please try after some time."
        
    #FIXME : MNP cases are not being handled.
    deviceNumberInfo = get_service_provider_for_device(rechargeType, deviceNumber)
    if deviceNumberInfo.operatorId == -1:
        return "Oops! There seems to be a problem. Please try after some time."
    circle = TelecomCircle.query.filter_by(code = deviceNumberInfo.circleCode).first()
    if circle:
        serviceAvailability = ServiceAvailability.query.filter_by(circleId = circle.id).filter_by(operatorId = userSelectedProviderId).first()
        if serviceAvailability.permIsServiceAvailable == False or serviceAvailability.tempIsServiceAvailable == False :
            
            if deviceNumberInfo.operatorName :
                if serviceAvailability.permIsServiceAvailable == False:
                    return "We currently do not provide service for " + deviceNumberInfo.operatorName + " in " + circle.name + ". We regret the inconvenience."
                else :
                    return "We are facing technical issues with " + deviceNumberInfo.operatorName + " in " + circle.name + " currently. Please try later." 
            else :
                if serviceAvailability.permIsServiceAvailable == False:
                    return "We currently do not provide service for this operator in " + circle.name + ". We regret the inconvenience."
                else:
                    return "We are facing technical issues with this operator in " + circle.name + " currently. Please try later." 
    totalSuccessfulRechargeAmount = get_previous_transactions(clientAddress)
    if totalSuccessfulRechargeAmount >= 3000 :
        return "You have exceeded your daily recharge limit. Please try after 24 hours."
    return "SUCCESS"
    
def get_previous_transactions(clientAddress):
    limittime = datetime.datetime.now() - datetime.timedelta(hours=24)
    totalSuccessfulRechargeAmount = session.query(func.sum(RechargeOrder.totalAmount)).filter(RechargeOrder.status == 5).filter(RechargeOrder.ipAddress == clientAddress).filter(RechargeOrder.creationTimestamp  > limittime).scalar()
    return totalSuccessfulRechargeAmount




def refund_to_wallet(userId, amount, reference, reference_type, description='Refunded to wallet', commit_session=False):
    """
    Commit to be decided by caller
    """
    if amount <= 0 or WalletReferenceType._VALUES_TO_NAMES.get(reference_type) is None:
        return False
    
    all_entries = UserWalletHistory.query.filter_by(reference = reference).filter_by(reference_type = WalletReferenceType._VALUES_TO_NAMES.get(reference_type)).all()
    creditable_amount_for_reference = 0
    refundable_amount_for_reference = 0
    for history in all_entries:
        creditable_amount_for_reference -= history.amount
        refundable_amount_for_reference -= history.refundable_amount
    #Refundable amount for reference should be greater than the refund amount
    if creditable_amount_for_reference < amount:
        print 'Cant be credited back to wallet as most of it has been already credited'
        return False
    
    wallet = get_user_wallet(userId)
    wallet.amount = wallet.amount + amount
    refundable_amount = 0
    if creditable_amount_for_reference - refundable_amount_for_reference < amount:
        refundable_amount = amount - (creditable_amount_for_reference - refundable_amount_for_reference)
    wallet.refundable_amount = wallet.refundable_amount + refundable_amount

    history = UserWalletHistory()
    history.amount = amount
    history.refundable_amount = refundable_amount
    history.reference = reference
    history.reference_type = WalletReferenceType._VALUES_TO_NAMES.get(reference_type) 
    history.wallet = wallet
    history.timestamp = datetime.datetime.now()
    history.description = description
    if commit_session:
        session.commit()

def add_amount_in_wallet(userId, amount, reference, reference_type, isPromotional, description=None, commit_session=False):
    """
    Commit to be decided by caller
    """
    if not amount > 0 or WalletReferenceType._VALUES_TO_NAMES.get(reference_type) is None:
        return 
    wallet = get_user_wallet(userId)
    wallet.amount = wallet.amount + amount
    history = UserWalletHistory()
    history.refundable_amount = 0
    if not isPromotional:
        wallet.refundable_amount = wallet.refundable_amount + amount
        history.refundable_amount = amount
    history.amount = amount
    history.reference = reference
    history.reference_type = WalletReferenceType._VALUES_TO_NAMES.get(reference_type)
    history.wallet = wallet
    history.description = description
    history.timestamp = datetime.datetime.now()
    if commit_session:
        session.commit()
    
def consume_wallet(userId, amount, reference, reference_type, description):
    if amount <= 0 or WalletReferenceType._VALUES_TO_NAMES.get(reference_type) is None:
        return
    wallet = get_user_wallet(userId)
    refundable_amount = wallet.refundable_amount
    promotional_amount = wallet.amount - wallet.refundable_amount
    refundable_amount_used = 0
    
    if promotional_amount < amount:
        refundable_amount = refundable_amount - (amount - promotional_amount)
        refundable_amount_used = amount - promotional_amount
        promotional_amount = 0
    else:
        promotional_amount = promotional_amount - amount
    
    wallet.amount = promotional_amount + refundable_amount
    wallet.refundable_amount = refundable_amount
    history = UserWalletHistory()
    history.amount = -amount
    history.refundable_amount = -refundable_amount_used
    history.reference = reference
    history.reference_type = WalletReferenceType._VALUES_TO_NAMES.get(reference_type)
    history.wallet = wallet
    history.description = description
    history.timestamp = datetime.datetime.now()
    session.commit()

def refund_amount_from_wallet(userId, amount, reference, reference_type, description="Refunded to customer"):
    if amount <= 0 or WalletReferenceType._VALUES_TO_NAMES.get(reference_type) is None:
        return
    wallet = get_user_wallet(userId)
    
    wallet.amount = wallet.amount - amount
    wallet.refundable_amount = wallet.refundable_amount - amount
    if wallet.refundable_amount < 0:
        return
    history = UserWalletHistory()
    history.amount = -amount
    history.refundable_amount = -amount
    history.reference = reference
    history.reference_type = WalletReferenceType._VALUES_TO_NAMES.get(reference_type)
    history.wallet = wallet
    history.description = description
    history.timestamp = datetime.datetime.now()
    session.commit()

def reverse_money_to_wallet(userId, amount, reference, reference_type):
    
    """To only reverse wallet payments, check add_amount_in_wallet to add new amount"""
    
    if amount <= 0 or WalletReferenceType._VALUES_TO_NAMES.get(reference_type) is None:
        return
    
    previous_debit_history = None
    if reference_type==WalletReferenceType.RECHARGE:
        previous_debit_history = UserWalletHistory.query.filter_by(reference = reference).filter_by(reference_type = WalletReferenceType._VALUES_TO_NAMES.get(reference_type)).filter_by(amount = -amount).order_by(UserWalletHistory.id.desc()).first()
        if previous_debit_history is None:
            print 'No amount was debited from wallet for recharge Id : ' + str(reference)
            return
    
    elif reference_type==WalletReferenceType.PURCHASE:
        previous_debit_history = UserWalletHistory.query.filter_by(reference = reference).filter_by(reference_type = WalletReferenceType._VALUES_TO_NAMES.get(reference_type)).filter_by(amount = -amount).order_by(UserWalletHistory.id.desc()).first()
        if previous_debit_history is None:
            print 'No amount was debited from wallet for purchase : ' + str(reference)
            return
    
    else:
        print "Reference is not valid"
        return
    
    
    wallet = get_user_wallet(userId)
    wallet.amount = wallet.amount + amount
    wallet.refundable_amount = wallet.refundable_amount -previous_debit_history.refundable_amount
    
    history = UserWalletHistory()
    history.amount = amount
    history.refundable_amount = -previous_debit_history.refundable_amount
    history.reference = reference
    history.reference_type = WalletReferenceType._VALUES_TO_NAMES.get(reference_type) 
    history.wallet = wallet
    history.timestamp = datetime.datetime.now()
    history.description = "Wallet amount reversed"
    session.commit()
    
    
#def update_amount_in_wallet(userId, amount, orderId, updateType):
#    return
#    """
#    UpdateType 1 is for wallet amounts
#    and 0 is for payment amounts
#    """
#    if amount == 0:
#        return
#    
#    history = None
#    if updateType == 1 and amount > 0 :
#        history = UserWalletHistory.query.filter_by(orderId = orderId).filter_by(amount = -amount).order_by(UserWalletHistory.id.desc()).first()
#        if history is None:
#            print 'No amount was debited from wallet for recharge Id : ' + str(orderId)
#            return
#
#    """
#    TODO check if successful payment available or Not
#    elif updateType == 0 :
#        baseorder = BaseOrder.query.filter_by(id = orderId).first()
#        if baseorder :
#            try :
#                pc = PaymentClient().get_client()
#                payments = pc.getPaymentForRechargeTxnId(baseorder.transaction.id)
#            except Exception as e:
#                print e
#                return
#            for payment in payments :
#                if payment.status == 2 :
#                    break
#        else :
#            return
#      """  
#    wallet = get_user_wallet(userId)
#    wallet.amount = wallet.amount + amount
#    
#    history = UserWalletHistory()
#    history.amount = amount
#    history.orderId = orderId
#    history.wallet = wallet
#    history.timestamp = datetime.datetime.now()
#    session.commit()

def get_recharge_orders_for_device(deviceNumber):
    orders = MobileRechargeOrder.query.filter_by(mobileNumber = deviceNumber).all()
    if orders is None:
        orders = DTHRechargeOrder.query.filter_by(dthAccountNumber = deviceNumber).all()
    return orders

def get_recharge_orders_for_status(status):
    if status == -1:
        orders = RechargeOrder.query.order_by(desc(RechargeOrder.id)).all()
    else:
        orders = RechargeOrder.query.order_by(desc(RechargeOrder.id)).filter_by(status = status).all()
    return orders

def get_recharge_statistics():
    rs = RechargeStatistics()
    statusCounts = {}
    operatorCounts = {}
    currentOrders = {}
    amounts = {}
    
    sorders = session.query(RechargeOrder.status, func.count(RechargeOrder.status)).group_by(RechargeOrder.status).all()
    for sorder in sorders:
        statusCounts[sorder[0]] = sorder[1]
    
    oorders = session.query(MobileRechargeOrder.mobileOperatorId, func.count(MobileRechargeOrder.mobileOperatorId)).group_by(MobileRechargeOrder.mobileOperatorId).all()
    for oorder in oorders:
        operatorCounts[oorder[0]] = oorder[1]

    oorders = session.query(DTHRechargeOrder.dthOperatorId, func.count(DTHRechargeOrder.dthOperatorId)).group_by(DTHRechargeOrder.dthOperatorId).all()
    for oorder in oorders:
        operatorCounts[oorder[0]] = oorder[1]

    forders = RechargeOrder.query.order_by(desc(RechargeOrder.id)).filter_by(status = RechargeOrderStatus.RECHARGE_FAILED).limit(20).all()
    currentOrders[RechargeOrderStatus._VALUES_TO_NAMES.get(RechargeOrderStatus.RECHARGE_FAILED)] = [order.to_thrift_object() for order in forders]
    sorders = RechargeOrder.query.order_by(desc(RechargeOrder.id)).filter_by(status = RechargeOrderStatus.RECHARGE_SUCCESSFUL).limit(20).all()
    currentOrders[RechargeOrderStatus._VALUES_TO_NAMES.get(RechargeOrderStatus.RECHARGE_SUCCESSFUL)] = [order.to_thrift_object() for order in sorders]
    porders = RechargeOrder.query.order_by(desc(RechargeOrder.id)).filter_by(status = RechargeOrderStatus.PAYMENT_SUCCESSFUL).limit(20).all()
    currentOrders[RechargeOrderStatus._VALUES_TO_NAMES.get(RechargeOrderStatus.PAYMENT_SUCCESSFUL)] = [order.to_thrift_object() for order in porders]
    rorders = RechargeOrder.query.order_by(desc(RechargeOrder.id)).filter_by(status = RechargeOrderStatus.RECHARGE_FAILED_REFUNDED).limit(20).all()
    currentOrders[RechargeOrderStatus._VALUES_TO_NAMES.get(RechargeOrderStatus.RECHARGE_FAILED_REFUNDED)] = [order.to_thrift_object() for order in rorders]
    
    twa = session.query(func.sum(UserWallet.amount)).one()
    tra = session.query(func.sum(RechargeOrder.totalAmount)).filter(RechargeOrder.status == RechargeOrderStatus.RECHARGE_SUCCESSFUL).one()
    gwa = session.query(func.sum(UserWalletHistory.amount)).filter(UserWalletHistory.orderId > 100000).one()
    dit2 = session.query(func.sum(WalletHistoryForCompany.amount)).filter(WalletHistoryForCompany.walletId == 2).filter(WalletHistoryForCompany.amount >= 100000).one()
    tpa = session.query(func.sum(RechargeOrder.totalAmount-RechargeOrder.walletAmount-RechargeOrder.couponAmount)).filter(RechargeOrder.status.in_((RechargeOrderStatus.RECHARGE_SUCCESSFUL, RechargeOrderStatus.RECHARGE_FAILED, RechargeOrderStatus.PAYMENT_SUCCESSFUL, RechargeOrderStatus.RECHARGE_FAILED_REFUNDED))).one()
    tda = session.query(func.sum(RechargeOrder.couponAmount)).filter(RechargeOrder.status == RechargeOrderStatus.RECHARGE_SUCCESSFUL).one()
        
    balance = RechargeService.getBalance()
    amounts['(a) Total Payment Amount'] = tpa[0]
    amounts['(b) Gift Wallet Amount'] = gwa[0]
    amounts['(c) Total Wallet Amount'] = twa[0]
    amounts['(d) Total Recharge Amount'] = tra[0]
    amounts['(e) Discount Balance'] = tda[0]
    amounts['Saholic Mismatch = (a+b+f-c-d)'] = tpa[0] + gwa[0] + tda[0] - tra[0] - twa[0] + 57607
    
    '''
    if amounts['SpiceDeck Mismatch = (d-e)'] != -739:
        mail(mail_user, mail_password, ['rajneesh.arora@shop2020.in', 'rajveer.singh@shop2020.in', 'kshitij.sood@shop2020.in'], "SpiceDeck Account mismatch of amount " + str(amounts['SpiceDeck Mismatch = (d-e)']), "", [], [], [])
    if amounts['Saholic Mismatch = (a+b-c-d)'] != -1919:
        mail(mail_user, mail_password, ['rajneesh.arora@shop2020.in', 'rajveer.singh@shop2020.in', 'kshitij.sood@shop2020.in'], "Saholic Recharge Account mismatch of amount " + str(amounts['Saholic Mismatch = (a+b-c-d)']), "", [], [], [])
    '''
                
    rs.statusCounts = statusCounts
    rs.operatorCounts = operatorCounts    
    rs.currentOrders = currentOrders
    rs.amounts = amounts
    
    return rs


def get_plans_for_operator(operator_id):
    return RechargePlan.query.filter_by(operatorId = operator_id).all()

def get_recharge_orders_for_transaction(txId):
    return RechargeOrder.query.filter_by(transaction_id = txId).first()

def enqueue_recharge_info_email(recharge_order, gvs={}):
    status = ""
    message = ""
    html_header = """

    <html>
    <body>
    <div>
        <p>
            Hello $email,<br /><br />
            Thanks for placing recharge order with us. Following are the details of your recharge order:
        </p>
        <div>
            Order Id : $order_id <br/>
            Order Date : $order_date <br/>
            Order Amount : Rs. $order_amount <br/>
            Order Status : $order_status <br/>
            Device Number : $device_number <br/>
            Service Provider : $service_provider <br/>
        </div>
    <br />
    """

    FailureReasonMap = {}
    FailureReasonMap["Invalid Amount"] = "the amount you tried seems to be invalid. Please try another amount."
    FailureReasonMap["Customer Exceeded Daily Limit"] = "you have exceeded daily recharge limit for this number. Please try after 12 hours."
    FailureReasonMap["Invalid Device Number"] = "it seems that your device number is not being recognized by selected operator. Please make sure the number you tried is correct."
    
    
    FailureReasonMapSms = {}
    FailureReasonMapSms["Invalid Amount"] = " invalid amount."
    FailureReasonMapSms["Customer Exceeded Daily Limit"] = " daily limit exceeded. Please try after 12Hrs."
    FailureReasonMapSms["Invalid Device Number"] = " invalid device number."
    
    failureReason = FailureReasonMap.get(recharge_order.description)
    
    failureReasonSms = FailureReasonMapSms.get(recharge_order.description)
    
    if failureReasonSms is None:
        failureReasonSms = " network failure."
    
    if failureReason is None:
        failureReason = "of network failure."
    if recharge_order.status == RechargeOrderStatus.RECHARGE_SUCCESSFUL:
        status = "Recharge Successful"
        message = ""
        if recharge_order.couponAmount > 0:
            message = message + "<b>You have used coupon " + recharge_order.couponCode + " and got discount of Rs. " + str(recharge_order.couponAmount) + ".</b>" 

    elif recharge_order.status == RechargeOrderStatus.RECHARGE_FAILED:
        status = "Recharge Failed"
        message = """
                We could not process your recharge because $failureReason We have credited the amount to your recharge wallet. You can use wallet amount to recharge again. <a href="$source_url/static/recharge-faq">Learn More</a>
                """
    elif recharge_order.status == RechargeOrderStatus.PAYMENT_SUCCESSFUL:
        status = "Recharge Under Process"
        message = """
                Your Payment was successful but due to some internal error with the operator's system we are not sure if the recharge was successful. In case your recharge is not processed, we will credit amount to your recharge wallet. You can use wallet amount to recharge again. <a href="$source_url/static/recharge-faq">Learn More</a>
                """
    elif recharge_order.status == RechargeOrderStatus.RECHARGE_FAILED_REFUNDED:
        status = "Recharge Failed, Amount Refunded"
        message = """
                It seems operator could not process your recharge request. We are crediting the refund amount to your recharge wallet. You can use wallet amount to recharge again. <a href="$source_url/static/recharge-faq">Learn More</a>
                """
    if len(gvs) > 0:
        message = message  + '<br />  You have been awarded with following Gift Voucher(s). You can redeem Gift Voucher(s) to purchase any product for Rs. 5000 and above using credit/debit card and net banking. <a href="$source_url/static/giftvoucher-offer">More details</a>'
        message = message + " <br />  <b>Voucher Code &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Amount</b>"
        for gv in gvs.keys():
            gvAmount = gvs.get(gv)
            message = message + " <br />  <b>" + gv + "&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Rs. "  + str(gvAmount) + "</b>"
    
    message = message  + '<br />  In case of any problem, please  <a href="$source_url/contact-us">contact us</a>'
    
    html_header = html_header + message
    html_footer = """
    <p>
        Best Wishes,<br />
        $source_name Team
    </p>
    </div>
    </body>
    </html>
    """

    if isinstance(recharge_order, MobileRechargeOrder):
        deviceNumber = recharge_order.mobileNumber
        providerName = get_provider(recharge_order.mobileOperatorId).name
    if isinstance(recharge_order, DTHRechargeOrder):
        deviceNumber = recharge_order.dthAccountNumber
        providerName = get_provider(recharge_order.dthOperatorId).name
        
    display_order_id = recharge_order.idPrefix + str(recharge_order.id)
    email = recharge_order.userEmailId
    dt = datetime.datetime.strptime(str(recharge_order.creationTimestamp), "%Y-%m-%d %H:%M:%S")
    formated_order_date = dt.strftime("%A, %d. %B %Y %I:%M%p")

    email_header = Template(html_header).substitute(dict(order_id = display_order_id, order_date = formated_order_date, email = email, source_url = source_url, order_amount = recharge_order.totalAmount, order_status = status, device_number = deviceNumber, service_provider = providerName, failureReason = failureReason))
    email_footer = Template(html_footer).substitute(dict(source_url = source_url, source_name = source_name))
    
    
    if isinstance(recharge_order, MobileRechargeOrder):
        smsText = ""
        
        if status == "Recharge Failed" and recharge_order.description == "Invalid Device Number" :
            print "Do send Sms"
            '''
            try:
                helper_client = HelperClient().get_client()
                helper_client.saveUserEmailForSending([email], "", source_name + " - Recharge Order Details", email_header + email_footer, str(recharge_order.id), "RechargeInfo", [], [], 1)
            except Exception as e:
                print e
            '''
        elif status == "Recharge Failed" and recharge_order.description != "Invalid Device Number" :
            smsText = "Dear Customer, We could not process your recharge due to" + failureReasonSms + " We have credited the amount to your recharge wallet. You can use wallet amount to recharge again. "
        elif status == "Recharge Successful" :
            smsText = "Dear Customer, Thanks for recharging your mobile for Rs. "+ str(recharge_order.totalAmount) + " from SmartDukaan. Order Id is "+ display_order_id + ". Any Query? please call 8826894203"
        elif status == "Recharge Under Process" :
            smsText = "Dear Customer, Your Payment was successful but due to some internal error with the operator's system we are not sure if the recharge was successful. In case your recharge is not processed, we will credit amount to your recharge wallet."
            #Dear Customer, Your Payment was successful but due to some internal error with the operator's system we are not sure if the recharge was successful. In case your recharge is not processed, we will credit amount to your recharge wallet.
        elif status == "Recharge Failed, Amount Refunded" :
            smsText = "Dear Customer, It seems operator could not process your recharge request. We are crediting the refund amount to your recharge wallet. You can use wallet amount to recharge again."
        
        if smsText is not None and len(smsText)>0 and recharge_order.status!=RechargeOrderStatus.RECHARGE_UNKNOWN:
            try:
                send_transaction_sms(recharge_order.userId, deviceNumber, smsText, SmsType.TRANSACTIONAL, False)
            except Exception as e:
                print e
                pass
        
    if recharge_order.status == RechargeOrderStatus.RECHARGE_UNKNOWN:
        return True
    try:
        helper_client = HelperClient().get_client()
        helper_client.saveUserEmailForSending([email], SaholicHelpEmailId, source_name + " - Recharge Order Details", email_header + email_footer, str(recharge_order.id), "RechargeInfo", [], [], 1)
        return True
    except Exception as e:
        print e
        return False

def get_recharge_denominations(operator_id, circleCode, denomination_type):
    circle_id = TelecomCircle.query.filter_by(code = circleCode).first().id
    return RechargeDenomination.query.filter_by(operatorId = operator_id).filter_by(circleId = circle_id).filter_by(denominationType = denomination_type).all()
    
def update_availability_status(operatorId, circleId, isAvailable):
    serviceAvailability = ServiceAvailability.query.filter_by(circleId = circleId).filter_by(operatorId = operatorId).first()
    serviceAvailability.tempIsServiceAvailable = isAvailable
    session.commit()
    
def get_misc_charges(transactionId):
    retCharges = {}
    miscCharges = MiscCharges.get_by(transaction_id = transactionId)
    if miscCharges:
        retCharges[1] = miscCharges.chargeAmount 
    return retCharges
    
def get_available_emi_schemes():
    t_emi_schemes = []
    emi_schemes = EmiScheme.query.filter(EmiScheme.minAmount < 9999999).all()
    if emi_schemes:
        t_emi_schemes = [to_t_emi_scheme(emi_scheme) for emi_scheme in emi_schemes]
    return t_emi_schemes

def refund_recharge_order(rechargeOrderId):
    try :
        rechargeOrder = RechargeOrder.query.filter_by(id = rechargeOrderId).first()
        wallet = UserWallet.query.filter_by(userId = rechargeOrder.userId).first()
    except:
        return False
    
    paidAmount = rechargeOrder.totalAmount - rechargeOrder.walletAmount - rechargeOrder.couponAmount
    amountToRefund = 0
    status = None
    if (paidAmount <= wallet.refundable_amount) :
        amountToRefund = paidAmount
        status = RechargeOrderStatus.REFUNDED
    else :
        amountToRefund = wallet.refundable_amount
        status = RechargeOrderStatus.PARTIALLY_REFUNDED
        
    #update_amount_in_wallet(rechargeOrder.userId, -amountToRefund, rechargeOrderId, 1)
    refund_amount_from_wallet(rechargeOrder.userId, amountToRefund, rechargeOrderId, WalletReferenceType.RECHARGE)
    
    try:    
        pc = PaymentClient().get_client()
        if pc.refundPayment(rechargeOrder.transaction_id, amountToRefund, True) :
            rechargeOrder.status = status
            rechargeOrder.refundTimestamp = datetime.datetime.now()
            session.commit()
            return True
        else :
            #update_amount_in_wallet(rechargeOrder.userId, amountToRefund, rechargeOrderId, 1)
            add_amount_in_wallet(rechargeOrder.userId, amountToRefund, rechargeOrderId, WalletReferenceType.RECHARGE, False, "Reversed while refunding to gateway", commit_session=True)
            
    except:
        #update_amount_in_wallet(rechargeOrder.userId, amountToRefund, rechargeOrderId, 1)
        add_amount_in_wallet(rechargeOrder.userId, amountToRefund, rechargeOrderId, WalletReferenceType.RECHARGE, False, "Reversed while refunding to gateway", commit_session=True)
        
    return False 
    
def get_physical_orders(from_date, to_date):
    query = Order.query.filter(Order.created_timestamp >from_date).filter(Order.created_timestamp < to_date).filter(~Order.status.in_([0,1,6,11,14,15,18,19,31,34,28,53,54,55,56,20,21,77,79,78]))
    return query.all()
        

def close_session():
    if session.is_active:
        print "session is active. closing it."
        session.close()

def is_alive():
    try:
        session.query(Transaction.id).limit(1).one()
        return True
    except:
        return False

def get_document(docType, docSource):
    document = DocumentStore.get_by(docType = docType, docSource = docSource)
    if document:
        return document.document
    else:
        return bin(0)

def retrieve_invoice(orderId, userId):
    order = get_order(orderId)
    if order.customer_id != userId:
        return bin(0)
    strOrderId = '0'*(10-len(str(orderId)))+str(orderId)
    filename = "/SaholicInvoices/" + strOrderId[0:2] + "/" + strOrderId[2:4] + "/" + strOrderId[4:6] + "/" + str(orderId) + ".pdf"  
    file = open(filename, "rb")
    pdfFile = file.read()
    return pdfFile

def myFirstPage(canvas, doc):
    canvas.saveState()
    canvas.setTitle("Theft-Insurance-Policy")
    canvas.restoreState()


def __generate_policy_doc(order, filename):
    PAGE_HEIGHT=defaultPageSize[1]; PAGE_WIDTH=defaultPageSize[0]
    styles = getSampleStyleSheet()
    styles.add(ParagraphStyle(name='left', leftIndent=12,fontSize=11), alias='li')
    styles.add(ParagraphStyle(name='bullet', bulletIndent=24, leftIndent=24), alias='bull')
    styles.add(ParagraphStyle(name='insurerName', alignment=TA_CENTER, fontName="Times-Bold", fontSize=16), alias='iN')
    styles.add(ParagraphStyle(name='insurerAdd', alignment=TA_CENTER, fontName="Helvetica",leading=15, fontSize=12), alias='iA')
    styles.add(ParagraphStyle(name='subHeading', alignment=TA_CENTER, fontName="Helvetica", fontSize=13), alias='sH')
    
    invoiceNumber = str(order.invoice_number)
    billing_timestamp = str(order.billing_timestamp.date()) 
    
    doc = SimpleDocTemplate(filename)
    Story = [Spacer(0, 0)]
    style = styles["Normal"]
    
    
    file_name = os.path.dirname(os.path.realpath(__file__)) + '/../resources/nia-logo.jpg'
    image = Image(file_name, width=100, height=100)
    Story.append(image)
    Story.append(Spacer(1,0.2*inch))
    
    text = ("""<u>The New India Assurance Company Limited</u>""") 
    p = Paragraph(text, styles['insurerName'])
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    
    text = ("""DO-IX, 35/1, Fortune Park, 1st Floor, Park Road, Tasker Town, Shivajinagar, Bangalore  560051""") 
    p = Paragraph(text, styles['insurerAdd'])
    Story.append(p)
    Story.append(Spacer(1,0.3*inch))
    
    text = ("""MOBILE HAND SET INSURANCE – THEFT & BURGLARY RISKS ONLY""")
    p = Paragraph(text, styles['subHeading'])
    Story.append(p)
    Story.append(Spacer(1,1*inch))
    
    text = ("""<u><b>Subject Matter Of Insurance</b></u> : <i>New Mobile Handsets</i> alone and <i>Tablets with 'Voice Calls'</i> only. 
    Sold 'online' by <b>Spice Online Retail Private Limited, New Delhi</b>, to its customers.""") 
    p = Paragraph(text, style)
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    text = ("""Insurer will indemnify the Insured in the event of Theft/Burglary of <i>Mobile Handsets</i> 
    alone and <i>Tablets with 'Voice Calls'</i> only sold as per Invoice No. %s
    dated %s and insured with THE NEW INDIA ASSURANCE COMPANY LIMITED, BANGALORE, as per Policy No. 67020046142400000013,  
    for a period of 12 months from the date and time of delivery of Mobile Handset to the customer.""" % (invoiceNumber, billing_timestamp)) 
    p = Paragraph(text, style)
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    text = ("""<b>WARRANTY</b> -  Warranted that the Mobile Handset sold during the Policy Period of 25/02/2015 to 24/02/2016
             are only covered irrespective of date of delivery to the customer""")
    p = Paragraph(text, style)
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    text = ("<b>INSURER</b> -The New India Assurance company limited.")
    p = Paragraph(text, style)
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    text = ("<b>INSURED</b> - Purchaser of the Mobile Handset OR his/her spouse or children. If the Purchaser is a ‘Company’, user is the duly authorised person.") 
    p = Paragraph(text, style)
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    text = ("<b>GEOGRAPHICAL LIMIT</b> – World-wide") 
    p = Paragraph(text, style)
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    text = ("<b>RISK COVERAGE</b> – Theft and/or burglary only") 
    p = Paragraph(text, style)
    Story.append(p)
    Story.append(Spacer(1,0.4*inch))
    text = ("Terms and Conditions of Insurance:") 
    p = Paragraph(text, styles["h2"])
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("""Insurance Premium paid will not be refunded.""")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("""Claims will be settled by UB Insurance Associates on behalf of The New India Assurance Co. Ltd.<br></br>
            Saholic.com will not be responsible for claim approval and disbursement.""")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("""Insurance will be invalidated for returned products.""")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.3*inch))
    text = ("Exclusions:") 
    p = Paragraph(text, styles["h2"])
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("Insurer is not liable") 
    p = Paragraph(text, styles["left"])
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("If the handset is not connected to Mobile Service Provider Network")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("Caused by incorrect storage, poor care and maintenance, careless use, negligence.")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("Due to mysterious disappearance, forgotten or misplaced or Lost or if handset is left unattended at any point of time.")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("Caused by war, war like operation, act of foreign enemy, civil war, rebellion, civil commotion, military usurped power, seizure, capture, confiscation, arrests, restrains and detainment by order of any Government or any other Authority.")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("Caused whilst engaging in criminal acts.")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("Caused by consequential loss or damage of whatsoever nature or any costs incurred in connection with claim under this policy, including postage, transit costs and reconnection cost.")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("Loss of mobile handset due to theft from an unattended vehicle unless the vehicle is of  fully enclosed saloon type having at time all the doors/windows and other opening securely locked and properly fastened.")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("Loss of mobile handset due to theft from the building unless there is visible forcible entry/exit into/from the building.")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("If the documents are found to be tampered; non-disclosure of material fact, fraud/misrepresentation.")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("The policy is not transferable.")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    
    text = ("Claim Procedure:") 
    p = Paragraph(text, styles["h2"])
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("""For Theft or Burglary of the handset immediate intimation to be given
     to the police within 48 hours of occurrence of incident and proper documents to be obtained 
     and inform the service provider within 48 hours about theft of handset in order to block the SIM services.""")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("""To intimate Insurance Company about the loss of Mobile Handset within 48 hours 
     through Call Centre No.080-41258886 OR through e-mail <u>hkruniversal@gmail.com</u> OR through 
     written communication to <i>UB Insurance Associates (Insurance Consultants and Advisors), No.S-204-205, Suraj Plaza, 196/8, 25th Cross, 8th F Main, 3rd Block, Jayanagar, Bangalore – 560011, TEL - 08041258886; Mobile - +919448595917</i>.""")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("""Claim documents can be downloaded through SPICE ONLINE RETAIL Website <u>http://www.saholic.com/claiminfo.html</u>
    <br></br>To know or track your claim please contact Call Centre No. 022-49107910 of Universal Insurance brokers (UIB). """)
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("""All the completed claim documents to reach UB Insurance Associates (Insurance Consultants and Advisors), No.S-204-205, Suraj Plaza, 196/8, 25th Cross, 8th F Main, 3rd Block, Jayanagar, Bangalore – 560011, TEL - 08041258886; <b>within 21 days</b>.""")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    text = ("""Claim proceeds will be credited to customer’s bank account.""")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    

    text = ("Excess(Minimum Deduction):") 
    p = Paragraph(text, styles["h2"])
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    text = ("""The excess will be 5% of the value of each claim subject to a minimum of Rs.500/-""")
    p = Paragraph(text, styles["left"])
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    
    text = ("Depreciation:") 
    p = Paragraph(text, styles["h2"])
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    
    text = ("For 0 – 60 days, 15%")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    
    text = ("61-150 days, 30%")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.1*inch))
    
    text = ("151 – 365 days, 45%")
    p = Paragraph(text, styles["bullet"], bulletText='\xe2\x80\xa2')
    Story.append(p)
    Story.append(Spacer(1,0.4*inch))
    
    text = ("<i>Insurance is a subject matter of solicitation.</i>")
    p = Paragraph(text, style)
    Story.append(p)
    Story.append(Spacer(1,0.2*inch))
    
    doc.build(Story, onFirstPage=myFirstPage)
    
def drawLine(Story, style):
    Story.append(Paragraph('-'*40, style))

def generateHotSpotInvoice():
    
    ##########TO BE PASSED TO THIS METHOD####################################
    #########################################################################
    filename = "/home/anupam/test.pdf"                             ##########
    itemCode = "SPICEMI450WHT"                                     ##########
    lotNumber = "12345678901234"                                   ##########
    itemName = "Spice Stellar Craze Mi-450 White"                  ##########
    qty = 1                                                        ##########
    unitPrice = 3099.50                                            ##########
    vat = 310.23                                                   ##########
    billingTime = datetime.datetime.now()                          ##########
    invNum = "885182994"                                           ##########
    counter = 1                                                    ##########
    ourInvoiceNum = "209809"                                       ##########
    customerId = "12345678909"                                     ##########
    customerName = "ANUPAM PRATAP SINGH"                           ##########
    telNum = ""                                                    ##########
    #########################################################################
    
    PAGE_HEIGHT=defaultPageSize[1];
    styles = getSampleStyleSheet()
    styles.add(ParagraphStyle(name='prologue', alignment=TA_CENTER, fontName="Courier", rightIndent = 5, fontSize=10), alias='sH')
    styles.add(ParagraphStyle(name='text', alignment=TA_JUSTIFY, fontName="Courier", fontSize=10), alias='text')
    styles.add(ParagraphStyle(name='line', alignment=TA_CENTER, fontName="Courier", fontSize=10), alias='line')
    styles.add(ParagraphStyle(name='epilogue', alignment=TA_CENTER, fontName="Courier", leftIndent = 30, rightIndent=36, fontSize=10), alias='ep')
    
    Story = []
    prologueStyle = styles["prologue"]
    lineStyle = styles["line"]
    textStyle = styles["text"]
    epilogueStyle = styles["epilogue"]

    doc = BaseDocTemplate(filename,showBoundary=0)

    #Two Columns
    frame1 = Frame(58, doc.bottomMargin, doc.width/2 + 25, doc.height + 7, id='col1')
    
    

    Story.append(Paragraph("""Spice Retail Limited<br></br>
        C/O,PIBCO LIMITED,Basement,Punjsons<br></br>
        2,Kalkaji Industrial Area,New Delhi<br></br>
          .<br></br>
        TIN NO. 07430284979<br></br>
        Service Tax No:AAACM0294PST001<br></br>
        D U P L I C A T E<br></br>
        I N V O I C E<br></br>""", prologueStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph('{0:7s}{1:>11s}{2:  %d/%m/%Y  %H:%M:%S}'.format("INV NO:", invNum, billingTime).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:8s}{1:>5d}{2:>11s}{3:>11s}".format('COUNTER:', counter, "CASHIER:", "SUPERADMIN").replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("MN INV NO&amp;DT: {0:<13s}{1:%d/%m/%Y}   ".format(ourInvoiceNum, billingTime).replace(" ", "&nbsp;"), textStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph("{0:<13s}{1:<27s}".format("CUSTOMER ID:", customerId).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<6s}{1:<34s}".format("NAME:", customerName + " " + ourInvoiceNum).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<9s}{1:<31s}".format("TEL NO.:", telNum).replace(" ", "&nbsp;"), textStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph("ITEM" + "&nbsp;"*12 + "LOT NUMBER<br></br>" + \
                            "&nbsp;ITEM NAME<br></br>" + \
                            "&nbsp;"*5 + "QTY" + "&nbsp;"*21 + "AMOUNT(INR)", textStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph(('{0:<16s}{1:24s}').format(itemCode, lotNumber).replace(' ', '&nbsp;'),textStyle))
    Story.append(Paragraph((' {0:<39s}').format(itemName).replace(' ', '&nbsp;'),textStyle))
    Story.append(Paragraph(('{0:<3d}{1:>10s}{2:13.2f}{3:14.2f}').format(qty, 'PCS', unitPrice, qty*unitPrice).replace(' ', '&nbsp;'),textStyle))
    Story.append(Paragraph(('{0:<3s}{1:37.2f}').format("VAT", vat).replace(' ', '&nbsp;'),textStyle))
    Story.append(Paragraph(('{0:<10s}{1:30.2f}').format("ITEM TOTAL", vat + unitPrice*qty).replace(' ', '&nbsp;'),textStyle))
    
    drawLine(Story, lineStyle)
    Story.append(Paragraph(('{0:13s}{1:>12s}{2:15.2f}').format("GROSS TOTAL:","INR", vat + unitPrice*qty).replace(' ', '&nbsp;'),textStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph(('{0:13s}{1:>12s}{2:15.2f}').format("NET TOTAL:","INR", vat + unitPrice*qty).replace(' ', '&nbsp;'),textStyle))
    Story.append(Paragraph(('{0:13s}{1:>12s}{2:15.2f}').format("NET VAT:","INR", vat).replace(' ', '&nbsp;'),textStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph(('{0:13s}{1:>12s}{2:15.2f}').format("SPICE ONLINE:","INR", vat + unitPrice*qty).replace(' ', '&nbsp;'),textStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph(('{0:16s}{1:>4d}').format("NUMBER OF ITEMS:",qty,).replace(' ', '&nbsp;'),textStyle))
    Story.append(Paragraph(('{0:16s}{1:>9.6f}').format("TOTAL ITEMS    :",qty,).replace(' ', '&nbsp;'),textStyle))
    
    Story.append(Paragraph("Goods once Sold Will not be taken back.<br></br>All Disputes subject to Delhi Jurisdiction",epilogueStyle))

    
    doc.addPageTemplates([PageTemplate(id='TwoCol',frames=[frame1]), ])

    #start the construction of the pdf
    doc.build(Story)
   
def change_shipping_address(orderId, line1, line2, city, state, pin):
    order = Order.get_by(id = orderId)
    if order:
        ordersList = get_orders_for_transaction(order.transaction_id, order.customer_id)
        logistics_client = LogisticsClient().get_client()
        for order in ordersList:
            if order.logisticsTransactionId is None:
                order.customer_address1 = line1
                order.customer_address2 = line2
                order.customer_city = city
                order.customer_state = state
        
                # Start:- Added/Modified by Manish Sharma on 13 May 2013 for Change Shipping Address functionality
                if order.customer_pincode != pin :
                    type = DeliveryType.PREPAID
                    if order.cod:
                        type = DeliveryType.COD
                        
                    if not logistics_client.isAlive():
                        logistics_client = LogisticsClient().get_client()
                        
                    logistics_info = logistics_client.getLogisticsEstimation(order.lineitems[0].item_id, pin, type)
                    
                    # Check for new estimate. Raise exception if new address is not serviceable
                    if logistics_info.deliveryTime == -1:
                        raise TransactionServiceException(1, 'Location not serviceable')
                
                    # COD services are not available everywhere
                    if order.cod:
                        if not logistics_info.codAllowed:
                            raise TransactionServiceException(2, 'COD service not available')
        
                    logistics_info_new = logistics_client.getLogisticsInfo(pin, order.lineitems[0].item_id, type, PickUpType.COURIER)
                    #Start:- Added by Manish Sharma for FedEx Integration- Shipment Creation on 31-Jul-2013
                    #order.tracking_id = logistics_info_new.airway_billno
                    #order.airwaybill_no = logistics_info_new.airway_billno
                    '''
                    if logistics_info_new.providerId !=7:
                        awb_number = logistics_client.getEmptyAWB(logistics_info_new.providerId, type)
                        order.tracking_id = awb_number
                        order.airwaybill_no = awb_number
                    '''            
                    #End:- Added by Manish Sharma for FedEx Integration- Shipment Creation on 31-Jul-2013
                    order.logistics_provider_id = logistics_info_new.providerId
                    current_time = datetime.datetime.now()
                    
                    logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
                    logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
                    logistics_info.deliveryDelay = adjust_delivery_time(current_time, (logistics_info.shippingTime+logistics_info.deliveryDelay))
                    new_expected_shipping_time_cod = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0)
                    new_expected_shipping_time_prepaid = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
                    
                    if order.cod:
                        if order.expected_shipping_time.date() == new_expected_shipping_time_cod.date() :
                            if order.otg :
                                order.otg = logistics_info.otgAvailable 
                        else:
                            if order.otg:
                                order.otg = False
                        order.expected_shipping_time = new_expected_shipping_time_cod
                        order.expected_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryTime)).replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0)
                        order.courier_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryDelay)).replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0)
                    else:
                        if order.expected_shipping_time.date() == new_expected_shipping_time_prepaid.date() :
                            if order.otg :
                                order.otg = logistics_info.otgAvailable 
                        else:
                            if order.otg:
                                order.otg = False
                        order.expected_shipping_time = new_expected_shipping_time_prepaid
                        order.expected_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
                        order.courier_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryDelay)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
        
                order.customer_pincode = pin
        # End:- Added/Modified by Manish Sharma on 13 May 2013 for Change Shipping Address functionality
        session.commit()
        return True
    return False


def get_recharge_transactions(storeId):
    query = RechargeTransaction.query.filter(RechargeTransaction.storeId == storeId)
    return query.all()

def get_recharge_trans(storeId, startdate,enddate, status):
    if startdate == -1:
        startdate = None
    if enddate == -1:
        enddate = None
    if storeId== 0:
        storeId = None
    if status == 0:
        status = None    
        
    query = RechargeTransaction.query
    if storeId is not None:
        query = query.filter(RechargeTransaction.storeId == storeId)
    if status is not None:
        query = query.filter(RechargeTransaction.status == status)
        
    if status == RechargeOrderStatus.RECHARGE_FAILED_REFUNDED :
        query.filter(cast(RechargeTransaction.responseTime, DATE) == enddate)
    
    else :
        if startdate is not None and enddate is not None:
            startdate = to_py_date(startdate).date()
            enddate = to_py_date(enddate).date()
            if startdate == enddate:
                query = query.filter(cast(RechargeTransaction.transactionTime, DATE) == enddate)
            elif enddate < startdate:
                query = query.filter(between(cast(RechargeTransaction.transactionTime, DATE), enddate, startdate))
            else:
                query = query.filter(between(cast(RechargeTransaction.transactionTime, DATE), startdate, enddate))
        elif startdate is not None:
            startdate = to_py_date(startdate).date()
            query = query.filter(cast(RechargeTransaction.transactionTime, DATE) >= startdate)
        elif enddate is not None:
            enddate = to_py_date(enddate).date()
            query = query.filter(cast(RechargeTransaction.transactionTime, DATE) <= enddate)

    return query.all()    

def update_recharge_transaction_status(id, status):
    rechargeTransaction = RechargeTransaction.get_by(id=id)
    if rechargeTransaction.status == status:
        print "Same updation of recharge transaction status"
        return
    if status in (RechargeOrderStatus.RECHARGE_FAILED_REFUNDED, RechargeOrderStatus.RECHARGE_FAILED):
        rechargeTransaction.status = status
        rechargeTransaction.responseTime = datetime.datetime.now() 
        
        store = HotspotStore.get_by(id=rechargeTransaction.storeId)
        
        wallet = WalletForCompany.query.filter(WalletForCompany.companyId==store.companyId).with_lockmode("update").one()
        wh = WalletHistoryForCompany()
        wh.walletId = wallet.id
        wh.amount = rechargeTransaction.amount
        wh.transactionTime = rechargeTransaction.responseTime
        wh.openingBal = wallet.amount
        wh.closingBal = wallet.amount + wh.amount
        wh.referenceNumber = rechargeTransaction.id 
        wh.description = "Credited amount"
        
        wallet.amount = wallet.amount + rechargeTransaction.amount
        store.availableLimit = store.availableLimit + rechargeTransaction.amount
        store.collectedAmount = store.collectedAmount - rechargeTransaction.amount + rechargeTransaction.discount
        session.commit()

    if status == RechargeOrderStatus.RECHARGE_SUCCESSFUL:
        rechargeTransaction.status = RechargeOrderStatus.RECHARGE_SUCCESSFUL
        rechargeTransaction.responseTime = datetime.datetime.now() 
        rechargeTransaction.invoiceNumber = get_next_invoice_number(OrderType.RCH4HOTSPOT)
        session.commit()


def create_recharge_transaction(t_recharge):
    dt = datetime.datetime.now() - datetime.timedelta(minutes=10)
    txns = RechargeTransaction.query.filter(RechargeTransaction.deviceNum == t_recharge.deviceNum).filter(RechargeTransaction.transactionTime > dt).all()
    if txns:
        raise TransactionServiceException(898, "In last 10 minutes you had already recharged this mobile number. Please try after sometime.")
    rechargeTransaction = RechargeTransaction()
    rechargeTransaction.amount = t_recharge.amount
    rechargeTransaction.circleId = t_recharge.circleId
    rechargeTransaction.deviceNum = t_recharge.deviceNum
    rechargeTransaction.deviceType = t_recharge.deviceType
    rechargeTransaction.discount = t_recharge.discount
    rechargeTransaction.email = t_recharge.email
    rechargeTransaction.name = t_recharge.name
    rechargeTransaction.cafNum = t_recharge.cafNum
    rechargeTransaction.simNum = t_recharge.simNum
    rechargeTransaction.isFrc = t_recharge.isFrc
    rechargeTransaction.status = RechargeOrderStatus.INIT
    rechargeTransaction.storeId = t_recharge.storeId
    rechargeTransaction.transactionTime = datetime.datetime.now()
    rechargeTransaction.operatorId = t_recharge.operatorId
    rechargeTransaction.plan = t_recharge.plan
    rechargeTransaction.ipAddress = t_recharge.ipAddress
    rechargeTransaction.paymentAmount = t_recharge.paymentAmount
    rechargeTransaction.payMethod = t_recharge.payMethod
    
    store = HotspotStore.get_by(id=rechargeTransaction.storeId)
    if not rechargeTransaction.circleId:
        rechargeTransaction.circleId = store.circleId
            
    wallet = WalletForCompany.query.filter(WalletForCompany.companyId==store.companyId).with_lockmode("update").one()
    if store.availableLimit < rechargeTransaction.amount or wallet.amount < rechargeTransaction.amount:
        raise TransactionServiceException(898, "Either wallet amount for company or available limit for store is less than recharge amount.")
    
    wh = WalletHistoryForCompany()
    wh.walletId = wallet.id
    wh.amount = -rechargeTransaction.amount
    wh.transactionTime = rechargeTransaction.transactionTime
    wh.openingBal = wallet.amount
    wh.closingBal = wallet.amount + wh.amount
    wh.description = "Debited amount"
    
    wallet.amount = wallet.amount - rechargeTransaction.amount
    store.availableLimit = store.availableLimit - rechargeTransaction.amount
    store.collectedAmount = store.collectedAmount + rechargeTransaction.amount - rechargeTransaction.discount 
    session.commit()

    wh.referenceNumber = rechargeTransaction.id
    session.commit()
    

    strProviderCode = get_provider_code(rechargeTransaction.operatorId)
    if rechargeTransaction.deviceType == 1:
        rtype = 'MTP'
    else:
        rtype = 'RCH'
    if rechargeTransaction.operatorId not in (getSyncOperatorsForRecharge()):
        rechargeMode = RechargeMode._NAMES_TO_VALUES.get('ASYNC')
    else:
        rechargeMode = RechargeMode._NAMES_TO_VALUES.get('SYNC')
    retStatus, retCode, spiceTID, description, aggTID, providerTID = RechargeService.rechargeDevice(rechargeTransaction.id, rtype, strProviderCode, rechargeTransaction.deviceNum, rechargeTransaction.amount, rechargeTransaction.plan, store.hotspotId, rechargeMode)
    rechargeTransaction.spiceTID = spiceTID
    rechargeTransaction.aggTID = aggTID
    rechargeTransaction.providerTID = providerTID
    rechargeTransaction.responseTime = datetime.datetime.now()
    rechargeTransaction.description = description
    if rechargeMode == RechargeMode._NAMES_TO_VALUES.get('SYNC'):
        if retCode == '00' and retStatus == 'S':
            rechargeTransaction.status = RechargeOrderStatus.RECHARGE_SUCCESSFUL
            rechargeTransaction.invoiceNumber = get_next_invoice_number(OrderType.RCH4HOTSPOT)
    
        elif retCode in ('STO', 'US', 'UP', 'STP') and retStatus == 'S':
            rechargeTransaction.status = RechargeOrderStatus.RECHARGE_UNKNOWN
        else:
            rechargeTransaction.status = RechargeOrderStatus.RECHARGE_FAILED
            if retCode in ('SVAI', 'SVIA', 'AI', 'IA', 'EAI', 'SVEAI'):
                rechargeTransaction.description = "Invalid Amount"
            elif retCode in ('ECL', 'SVECL', 'SCL'):
                rechargeTransaction.description = "Customer Exceeded Daily Limit"
            elif retCode in ('CI', 'ECI', 'ESP', 'SVCI', 'SVECI', 'SVESP', 'SV_ECI', 'SV ECI'):
                rechargeTransaction.description = "Invalid Device Number"
    
            store = HotspotStore.get_by(id=rechargeTransaction.storeId)
            wallet = WalletForCompany.query.filter(WalletForCompany.companyId==store.companyId).with_lockmode("update").one()
            wh = WalletHistoryForCompany()
            wh.walletId = wallet.id
            wh.amount = rechargeTransaction.amount
            wh.transactionTime = rechargeTransaction.responseTime
            wh.openingBal = wallet.amount
            wh.closingBal = wallet.amount + wh.amount
            wh.referenceNumber = rechargeTransaction.id 
            wh.description = "Credited amount"
            
            wallet.amount = wallet.amount + rechargeTransaction.amount
            store.availableLimit = store.availableLimit + rechargeTransaction.amount
            store.collectedAmount = store.collectedAmount - rechargeTransaction.amount + rechargeTransaction.discount
        
    else:
        rechargeTransaction.status = RechargeOrderStatus.RECHARGE_IN_PROCESS
        rechargeTransaction.description = 'Recharge In Process'
    session.commit()
    return rechargeTransaction

def get_frcs(circleId, operatorId):
    query = FRC.query.filter(FRC.circleId == circleId)
    if(operatorId > 0):
        query = query.filter(FRC.operatorId == operatorId)
        
    frcs = query.all()
    
    if frcs is None:
        return []
    return frcs

def get_recharge_transaction(rechargeId):
    recharge = RechargeTransaction.get_by(id = rechargeId)
    if not recharge:
        raise TransactionServiceException(108, "no such transaction")
    return recharge

def get_hotspot_store(id, hotspotid):
    store = None
    if id > 0:
        store = HotspotStore.query.filter(HotspotStore.id == id).first()
    elif len(hotspotid) > 0:
        store = HotspotStore.query.filter(HotspotStore.hotspotId == hotspotid).first()
    else:
        raise TransactionServiceException(119, 'No such store')
    if store:
        return store
    else:
        raise TransactionServiceException(119, 'No such store')

def get_circle(id, code):
    circle = None
    if id > 0:
        circle = TelecomCircle.get_by(id = id)
    elif len(code) > 0:
        circle = TelecomCircle.get_by(code = code)
    else:
        raise TransactionServiceException(119, 'No such circle')
    if circle:
        return circle
    else:
        raise TransactionServiceException(119, 'No such circle')
    
def retrieve_hotspot_recharge_invoice(id):
    recharge = RechargeTransaction.get_by(id = id)
    filename = "/tmp/recharge" + str(recharge.invoiceNumber) + ".pdf"
    operator = ServiceProvider.get_by(id = recharge.operatorId).name
    amount = recharge.amount
    telNum = "+91 " + str(recharge.deviceNum)
    customerName = recharge.name
    invNum = str(recharge.invoiceNumber)
    billingTime = recharge.responseTime
    payMethod = recharge.payMethod
    return __printRechargeInvoice(filename, operator, amount, telNum, customerName, invNum, billingTime, payMethod)

def split_freebie_order(order_id, splitReason, shippingDate):
    order = get_order(order_id)
    freebie_order = Order()
    
    freebie_order.transaction = order.transaction
    
    freebie_order.customer_id = order.customer_id
    freebie_order.customer_email = order.customer_email
    freebie_order.customer_name = order.customer_name
    freebie_order.customer_pincode = order.customer_pincode
    freebie_order.customer_address1 = order.customer_address1
    freebie_order.customer_address2 = order.customer_address2
    freebie_order.customer_city = order.customer_city
    freebie_order.customer_state = order.customer_state
    freebie_order.customer_mobilenumber = order.customer_mobilenumber
    freebie_order.total_weight = order.total_weight
    freebie_order.total_amount = 0.01
    freebie_order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
    freebie_order.statusDescription = "In Process";
    freebie_order.created_timestamp = datetime.datetime.now()
    freebie_order.cod = 0
    freebie_order.orderType = OrderType.B2C
    freebie_order.pickupStoreId = 0
    freebie_order.otg = 0
    freebie_order.insurer = 0
    freebie_order.insuranceAmount = 0
    freebie_order.source = order.source
    
    logistics_client = LogisticsClient().get_client()
    logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, order.freebieItemId, DeliveryType.PREPAID, PickUpType.COURIER)
    freebie_order.logistics_provider_id = logistics_info.providerId;
    freebie_order.tracking_id = logistics_info.airway_billno
    freebie_order.airwaybill_no = logistics_info.airway_billno
    new_shipping_date = max(to_py_date(shippingDate),order.expected_delivery_time+datetime.timedelta(hours=72))
    #freebie_order.expected_delivery_time = (to_py_date(shippingDate)+datetime.timedelta(days=logistics_info.deliveryTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
    freebie_order.expected_delivery_time = (new_shipping_date+datetime.timedelta(days=logistics_info.deliveryTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)  
    freebie_order.promised_delivery_time = order.promised_delivery_time
    freebie_order.expected_shipping_time = new_shipping_date
    freebie_order.promised_shipping_time = order.promised_shipping_time

    inventory_client = InventoryClient().get_client()
    item_availability = inventory_client.getItemAvailabilityAtLocation(order.freebieItemId,1)
    freebie_order.warehouse_id = item_availability[2]
    freebie_order.fulfilmentWarehouseId = item_availability[0]

    catalog_client = CatalogClient().get_client()
    item = catalog_client.getItem(order.freebieItemId)
    freebie_order.total_weight = item.weight
    
    litem = LineItem()
    litem.item_id = item.id
    litem.productGroup = item.productGroup
    litem.brand = item.brand
    litem.model_number = item.modelNumber
    litem.model_name = item.modelName
    litem.color = item.color
    litem.extra_info = "Freebie Order for Order ID " + str(order.id)
    litem.quantity = 1
    litem.unit_price = 0.01
    litem.unit_weight = item.weight
    litem.total_price = 0.01
    litem.total_weight = item.weight
    litem.vatRate = 5.0
    
    freebie_order.lineitems.append(litem)
    order.freebieItemId = 0
    session.commit()
    order.lineitems[0].extra_info = "Parent Order for Order ID " + str(freebie_order.id)
   
    inventory_client = InventoryClient().get_client()   
    inventory_client.reserveItemInWarehouse(freebie_order.lineitems[0].item_id, item_availability[0], sourceId, freebie_order.id, to_java_date(freebie_order.created_timestamp), to_java_date(freebie_order.promised_shipping_time), 1)
    parentOrderAttribute = Attribute()
    parentOrderAttribute.orderId = freebie_order.id
    parentOrderAttribute.name = "parentOrderIdForFreebie"
    parentOrderAttribute.value = str(order.id)
    
    freebieOrderAttribute = Attribute()
    freebieOrderAttribute.orderId = order.id
    freebieOrderAttribute.name = "freebieOrderId"
    freebieOrderAttribute.value = str(freebie_order.id)
    session.commit()
    
    '''
    crmServiceClient = CRMClient().get_client()
    ticket =Ticket()
    activity = Activity()
    description = "Creating Ticket for " + splitReason + " Order"
    ticket.creatorId = 1
    ticket.assigneeId = 34
    ticket.category = TicketCategory.OTHER
    ticket.priority = TicketPriority.HIGH
    ticket.status = TicketStatus.OPEN
    ticket.description = description
    ticket.orderId = order_id
    activity.creatorId = 1
    activity.ticketAssigneeId = ticket.assigneeId
    activity.type = ActivityType.OTHER
    activity.description = description
    activity.ticketCategory = ticket.category
    activity.ticketDescription = ticket.description
    activity.ticketPriority = ticket.priority
    activity.ticketStatus = ticket.status
    ticket.customerId= order.customer_id
    ticket.customerEmailId = order.customer_email
    ticket.customerMobileNumber = order.customer_mobilenumber
    ticket.customerName = order.customer_name
    activity.customerId = ticket.customerId
    activity.customerEmailId = order.customer_email
    activity.customerMobileNumber = order.customer_mobilenumber
    activity.customerName = order.customer_name
    crmServiceClient.insertTicket(ticket, activity)
    '''
    return to_t_order(freebie_order)

def __printRechargeInvoice(filename, operator, amount, telNum, customerName, invNum, billingTime, payMethod): 
    PAGE_HEIGHT=defaultPageSize[1];
    styles = getSampleStyleSheet()
    styles.add(ParagraphStyle(name='prologue', alignment=TA_CENTER, fontName="Courier", rightIndent = 5, fontSize=10), alias='sH')
    styles.add(ParagraphStyle(name='text', alignment=TA_JUSTIFY, fontName="Courier", fontSize=10), alias='text')
    styles.add(ParagraphStyle(name='line', alignment=TA_CENTER, fontName="Courier", fontSize=10), alias='line')
    styles.add(ParagraphStyle(name='epilogue', alignment=TA_CENTER, fontName="Courier", leftIndent = 30, rightIndent=36, fontSize=10), alias='ep')
    
    Story = []
    prologueStyle = styles["prologue"]
    lineStyle = styles["line"]
    textStyle = styles["text"]
    epilogueStyle = styles["epilogue"]

    doc = BaseDocTemplate(filename,showBoundary=0)

    #Two Columns
    frame1 = Frame(58, doc.bottomMargin, doc.width/2 + 25, doc.height + 7, id='col1')
    
    

    Story.append(Paragraph("""Spice Retail Limited<br></br>
        C/O,PIBCO LIMITED,Basement,Punjsons<br></br>
        2,Kalkaji Industrial Area,New Delhi<br></br>
          .<br></br>
        TIN NO. 07430284979<br></br>
        Service Tax No:AAACM0294PST001<br></br><br></br><br></br>""", prologueStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph('{0:7s}{1:>11s}{2:  %d/%m/%Y  %H:%M:%S}'.format("INV NO:", invNum, billingTime).replace(" ", "&nbsp;"), textStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph("{0:<6s}{1:<34s}".format("NAME:", customerName).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<9s}{1:<31s}".format("TEL NO.:", telNum).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<9s}{1:<31s}".format("PAYMENT MODE:", PayMethod._VALUES_TO_NAMES[payMethod]).replace(" ", "&nbsp;"), textStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph(('{0:<16s}{1:>13s}{2:11.2f}').format(operator, "RECHARGE", amount).replace(' ', '&nbsp;'),textStyle))
    drawLine(Story, lineStyle)
    doc.addPageTemplates([PageTemplate(id='TwoCol',frames=[frame1]), ])

    #start the construction of the pdf
    doc.build(Story)
    
    try:
        fileToSend = open(filename, "rb")
        pdfFile = fileToSend.read()
    except:
        return bin(0)
    
    return pdfFile

def get_recharge_transactions_by_number(number, storeId):
    query = RechargeTransaction.query.filter(RechargeTransaction.deviceNum == number)
    query = query.filter(RechargeTransaction.storeId == storeId)
    return query.all()

def update_hotspot_store_password(storeId, password):
    store = None
    if password is None:
        return False
    if storeId:
        store = HotspotStore.query.filter(HotspotStore.id == storeId).first()
    if store:
        store.password = password
        session.commit()
        return True
    return False
    
def get_source_detail(source):
    detail = SourceDetail.get_by(id=source)
    if not detail:
        raise TransactionServiceException(108, "no such source")
    return detail

def get_all_circles():
    circles = TelecomCircle.query.all();
    if not circles:
        circles = []
    return circles 

def delete_frcs(frcIdsToDelete):
    toReturn = False
    if frcIdsToDelete:
        for frcId in frcIdsToDelete:
            frc = FRC.get_by(id = frcId)
            if frc:
                frc.delete()
                toReturn = True
        session.commit()
        return toReturn
    else:
        return toReturn

def topup_company_wallet(companyId, amount):
    wallet = WalletForCompany.query.filter(WalletForCompany.id == companyId).with_lockmode("update").one()
    company = Company.get_by(id = companyId)
    wh = WalletHistoryForCompany()
    wh.walletId = wallet.id
    wh.openingBal = wallet.amount
    wh.closingBal = wallet.amount +  amount
    wh.amount = amount
    wh.transactionTime = datetime.datetime.now()
    wh.referenceNumber =  get_next_invoice_number(OrderType.WALLETCREDIT)
    wh.description = "Wallet Credited"
    
    wallet.amount += amount
    session.commit()
    mail("cnc.center@shop2020.in", "5h0p2o2o", [ "kshitij.sood@shop2020.in","pardeep.panwar@spiceretail.co.in","adarsh.verma@spiceretail.co.in","j.p.gupta@shop2020.in", "rajneesh.arora@shop2020.in", "amit.tyagi@spiceretail.co.in"] , company.name + " wallet topped up by " +  str(amount) + " rupees.", "", [], [], [])
    return wallet.amount

def get_wallet_balance_for_company(companyId):
    return WalletForCompany.get_by(companyId = companyId).amount

def add_amazon_order(amazonOrder):
    amazon_order = AmazonOrder()
    amazon_order.orderId = amazonOrder.orderId
    amazon_order.amazonOrderCode = amazonOrder.amazonOrderCode
    amazon_order.amazonOrderItemCode = amazonOrder.amazonOrderItemCode
    amazon_order.transactionId = amazonOrder.transactionId
    amazon_order.item_id= amazonOrder.item_id
    amazon_order.status= amazonOrder.status
    amazon_order.purchaseDateOnAmazon= to_py_date(amazonOrder.purchaseDateOnAmazon);
    session.commit()
    
def update_amazon_order_status(order_id,status):
    row = AmazonOrder.get_by(orderId=order_id)
    row.status=status
    session.commit()
    return True

def get_amazon_orders_marked_sent():
    return AmazonOrder.query.filter(AmazonOrder.status=='Order-Payment-Success').all()

def get_amazon_orders_shipped():
    amazonShippedOrders = []
    ###toDate = datetime.datetime.now()
    ###fromDate = toDate-timedelta(days = interval-1, hours=toDate.hour, minutes=toDate.minute, seconds=toDate.second)
    orders = get_amazon_orders_marked_sent()
    if not orders:
        return amazonShippedOrders
        
    for order in orders:
        row = Order.query.filter(Order.id == order.orderId).first()
        if row.shipping_timestamp:
            amazonShippedOrders.append(to_t_order(row))
    return amazonShippedOrders

def get_amazon_orders_cancelled(interval):
    amazonCancelledOrders = []
    toDate = datetime.datetime.now()
    fromDate = toDate-timedelta(days = interval-1, hours=toDate.hour, minutes=toDate.minute, seconds=toDate.second)
    orders = get_amazon_orders_marked_sent()
    for order in orders:
        row = Order.query.filter(Order.refund_timestamp.between (fromDate,toDate)).filter(Order.id==order.orderId).first()
        if row is None:
            continue
        if ( row.status == 15 or row.status == 18 or row.status == 34 ):
            amazonCancelledOrders.append(to_t_order(row))
    return amazonCancelledOrders        

def get_amazon_order(order_id):
    return AmazonOrder.get_by(orderId=order_id)

def get_amazon_order_by_amazonorderid(amazonOrderId):
    return AmazonOrder.query.filter(AmazonOrder.amazonOrderCode==amazonOrderId)


def get_amazon_orders_to_acknowledge(source):
    orderstatus = []
    orderstatus.append('Order-Payment-Success')
    orderstatus.append('Order-Payment-Failure')
    return session.query(AmazonOrder.amazonOrderCode,AmazonOrder.amazonOrderItemCode,AmazonOrder.status,AmazonOrder.transactionId,func.count(AmazonOrder.orderId))\
    .join((Order, Order.id==AmazonOrder.orderId)).filter(AmazonOrder.status.in_(orderstatus)).filter(Order.source==source).group_by(AmazonOrder.amazonOrderItemCode,AmazonOrder.status).all()

def change_amazon_order_status(amazonOrderCode,status):
    for order in AmazonOrder.query.filter(AmazonOrder.amazonOrderCode==amazonOrderCode):
        order.status = status
    session.commit()   
    
def get_orders_for_store(orderId, storeId, startDate, endDate, statuses):
    orders = []
    query = Order.query
    if storeId < 1:
        return orders
    elif orderId > 0:
        query = query.filter(Order.id == orderId)
    else :
        if startDate:
            query = query.filter(cast(Order.created_timestamp, DATE) >= startDate.date())
        
        if endDate:
            query = query.filter(cast(Order.created_timestamp, DATE) <= endDate.date())
        
        if len(statuses) > 0:
            query = query.filter(Order.status.in_(statuses))
                
    orders = query.filter(Order.storeId == storeId).all()
    return orders

def get_store_order_advance_invoice(orderId, storeId):
    order = Order.get_by(id = orderId)
    storeOrderDetail = StoreOrderDetail.query.filter(StoreOrderDetail.orderId == orderId).filter(StoreOrderDetail.storeId == storeId).first()
    store = HotspotStore.get_by(id = storeId)
    
    modelName = "&nbsp;"
    modelNumber = "&nbsp;"
    color = "&nbsp;"
    if order.lineitems[0].model_name:
        modelName = modelName + order.lineitems[0].model_name
    if order.lineitems[0].model_number:
        modelNumber = modelNumber + order.lineitems[0].model_number
    if order.lineitems[0].color:
        color = color + order.lineitems[0].color
    
    product = order.lineitems[0].brand +  modelName + modelNumber + color
    advanceAmount = order.advanceAmount
    totalAmount = order.total_amount
    delivery = order.expected_delivery_time
    
    customerName = order.customer_name
    customerAdd1 = order.customer_address1
    customerAdd2 = order.customer_address2
    customerCity = order.customer_city
    customerState = order.customer_state
    customerMobile = order.customer_mobilenumber
    customerPin = order.customer_pincode
    inCash = storeOrderDetail.cashAmount
    byCard = storeOrderDetail.cardAmount
    
    if order.status in (15, 34) and (storeOrderDetail.cashRefundAmount > 0 or storeOrderDetail.cashRefundAmount > 0):
        filename = "/tmp/order-refund-" + str(order.id) + ".pdf"
        inCash = storeOrderDetail.cashRefundAmount
        byCard = storeOrderDetail.cardRefundAmount
        return __printStoreOrderCancellationReceipt(store, order.id, filename, customerName, customerAdd1, customerAdd2, customerCity, customerState, customerMobile, customerPin, product, advanceAmount,inCash, byCard, order.created_timestamp.date(), order.refund_timestamp.date())
    else:
        filename = "/tmp/order-" + str(order.id) + ".pdf"
    #payMethod = recharge.payMethod
#    if order.status == 15 or order.status == 34:
#        return __printStoreOrderAdvanceInvoice(filename, product, advanceAmount, totalAmount, customerName, delivery, mode, customerAdd1, customerAdd2, customerCity, customerState, customerMobile, customerPin)
    return __printStoreOrderAdvanceInvoice(store, order.id, filename, product, advanceAmount, totalAmount, customerName, delivery, inCash, byCard, customerAdd1, customerAdd2, customerCity, customerState, customerMobile, customerPin, order.created_timestamp.date())

def __printStoreOrderCancellationReceipt(store, orderId, filename, customerName, customerAdd1, customerAdd2, customerCity, customerState, customerMobile, customerPin, product, advanceAmount,inCash, byCard, createdDate, cancelDate): 
    PAGE_HEIGHT=defaultPageSize[1];
    styles = getSampleStyleSheet()
    styles.add(ParagraphStyle(name='prologue', alignment=TA_CENTER, fontName="Courier", rightIndent = 5, fontSize=10), alias='sH')
    styles.add(ParagraphStyle(name='text', alignment=TA_JUSTIFY, fontName="Courier", fontSize=10), alias='text')
    styles.add(ParagraphStyle(name='line', alignment=TA_CENTER, fontName="Courier", fontSize=10), alias='line')
    styles.add(ParagraphStyle(name='epilogue', alignment=TA_CENTER, fontName="Courier", leftIndent = 30, rightIndent=36, fontSize=10), alias='ep')
    
    Story = []
    prologueStyle = styles["prologue"]
    lineStyle = styles["line"]
    textStyle = styles["text"]
    epilogueStyle = styles["epilogue"]

    doc = BaseDocTemplate(filename,showBoundary=0)

    #Two Columns
    frame1 = Frame(58, doc.bottomMargin, doc.width/2 + 25, doc.height + 7, id='col1')
    
    
    Story.append(Paragraph(store.line1, prologueStyle))
    Story.append(Paragraph(store.line2 + ', ' + store.city, prologueStyle))
    Story.append(Paragraph(store.state + ' - ' + store.pin, prologueStyle))
    
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph("{0:^39s}".format("*** ADVANCE REFUND RECEIPT ***").replace(" ", "&nbsp;"), textStyle))
    
    drawLine(Story, lineStyle)
   
    Story.append(Paragraph("{0:<6s}{1:<34s}".format("NAME:", customerName).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<10s}{1:<29s}".format("MOBILE:", customerMobile).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<10s}{1:<29s}".format("ORDER ID:", str(orderId)).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<20s}{1:  %d/%m/%Y}".format("CURRENT DATE:", datetime.datetime.now().date()).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<20s}{1:  %d/%m/%Y}".format("ORDER DATE:", createdDate).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<20s}{1:  %d/%m/%Y}".format("CANCELLATION DATE:", cancelDate).replace(" ", "&nbsp;"), textStyle))
    
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph("{0:^39s}".format("DELIVERY ADDRESS").replace(" ", "&nbsp;"), textStyle))
    
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph(customerAdd1, textStyle))
    if len(customerAdd2) > 0:
        Story.append(Paragraph(customerAdd2, textStyle))
    Story.append(Paragraph(customerCity, textStyle))
    Story.append(Paragraph(customerState + " - " + customerPin, textStyle))
    
    drawLine(Story, lineStyle)

    Story.append(Paragraph("{0:^39s}".format("PRODUCT NAME").replace(" ", "&nbsp;"), textStyle))
    
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph(product, textStyle))
    
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph(('{0:<20s}{1:>19.2f}').format("ADVANCE AMOUNT: ", advanceAmount).replace(' ', '&nbsp;'),textStyle))
    Story.append(Paragraph(('{0:<30s}{1:>9.2f}').format("LESS-CANCELLATION CHARGES: ", advanceAmount-inCash-byCard).replace(' ', '&nbsp;'),textStyle))
    Story.append(Paragraph(('{0:<30s}{1:>9.2f}').format("AMOUNT TO BE REFUNDED: ", inCash+byCard).replace(' ', '&nbsp;'),textStyle))
    if inCash > 0:
        Story.append(Paragraph(('{0:>20s}{1:>19.2f}').format("REFUND IN CASH: ", inCash).replace(' ', '&nbsp;'),textStyle))
    if byCard > 0:
        Story.append(Paragraph(('{0:>20s}{1:>19.2f}').format("REFUND BY CARD: ", byCard).replace(' ', '&nbsp;'),textStyle))
        
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph("CUSTOMER SIGNATURE :", textStyle))
    Story.append(Paragraph("&nbsp;", textStyle))
    
    drawLine(Story, lineStyle)
    
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph("Terms & Conditions:", textStyle))                                        
    Story.append(Paragraph("* Original copy of 'Advance Receipt' Shall be provided  by Customer to get the refund",textStyle))
    Story.append(Paragraph("* All disputes subject to Delhi jusrisdiction",textStyle))                                        
    
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph(('{0:<20s}{1:>19s}').format("Customer care : ", "0120-6744700").replace(' ', '&nbsp;'),textStyle))
    
    drawLine(Story, lineStyle)
    
    doc.addPageTemplates([PageTemplate(id='TwoCol',frames=[frame1]), ])
    #start the construction of the pdf
    doc.build(Story)
    try:
        fileToSend = open(filename, "rb")
        pdfFile = fileToSend.read()
    except:
        return bin(0)
    
    return pdfFile
    
    
    
    
def __printStoreOrderAdvanceInvoice(store, orderId, filename, product, advanceAmount, totalAmount, customerName, delivery, inCash, byCard, customerAdd1, customerAdd2, customerCity, customerState, customerMobile, customerPin, createdDate): 
    PAGE_HEIGHT=defaultPageSize[1];
    styles = getSampleStyleSheet()
    styles.add(ParagraphStyle(name='prologue', alignment=TA_CENTER, fontName="Courier", rightIndent = 5, fontSize=10), alias='sH')
    styles.add(ParagraphStyle(name='text', alignment=TA_JUSTIFY, fontName="Courier", fontSize=10), alias='text')
    styles.add(ParagraphStyle(name='line', alignment=TA_CENTER, fontName="Courier", fontSize=10), alias='line')
    styles.add(ParagraphStyle(name='epilogue', alignment=TA_CENTER, fontName="Courier", leftIndent = 30, rightIndent=36, fontSize=10), alias='ep')
    
    Story = []
    prologueStyle = styles["prologue"]
    lineStyle = styles["line"]
    textStyle = styles["text"]
    epilogueStyle = styles["epilogue"]

    doc = BaseDocTemplate(filename,showBoundary=0)

    #Two Columns
    frame1 = Frame(58, doc.bottomMargin, doc.width/2 + 25, doc.height + 7, id='col1')
    
    
    Story.append(Paragraph(store.line1, prologueStyle))
    Story.append(Paragraph(store.line2 + ', ' + store.city, prologueStyle))
    Story.append(Paragraph(store.state + ' - ' + store.pin, prologueStyle))
    
    
    drawLine(Story, lineStyle)
    Story.append(Paragraph("{0:^39s}".format("*** ADVANCE RECEIPT ***").replace(" ", "&nbsp;"), textStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph("{0:^39s}".format("DELIVERY ADDRESS").replace(" ", "&nbsp;"), textStyle))
    drawLine(Story, lineStyle)
    Story.append(Paragraph(customerAdd1, textStyle))
    if len(customerAdd2) > 0:
        Story.append(Paragraph(customerAdd2, textStyle))
        
    Story.append(Paragraph(customerCity, textStyle))
    Story.append(Paragraph(customerState + " - " + customerPin, textStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph("{0:<6s}{1:<34s}".format("NAME:", customerName).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<10s}{1:<29s}".format("MOBILE:", customerMobile).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<10s}{1:<12s}{2:7s}{3:  %d/%m/%Y}".format("ORDER ID:", str(orderId), "DATE:", createdDate).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<20s}{1:  %d/%m/%Y}".format("CURRENT DATE:", datetime.datetime.now().date()).replace(" ", "&nbsp;"), textStyle))
    Story.append(Paragraph("{0:<20s}{1:  %d/%m/%Y}".format("EXPECTED DELIVERY:", delivery).replace(" ", "&nbsp;"), textStyle))
    
    drawLine(Story, lineStyle)
    Story.append(Paragraph("{0:^39s}".format("PRODUCT NAME").replace(" ", "&nbsp;"), textStyle))
    drawLine(Story, lineStyle)
    Story.append(Paragraph("{0:<39s}".format(product).replace(" ", "&nbsp;"), textStyle))
    drawLine(Story, lineStyle)
    
    Story.append(Paragraph(('{0:<20s}{1:>19.2f}').format("TOTAL PRICE: ", totalAmount).replace(' ', '&nbsp;'),textStyle))
    Story.append(Paragraph(('{0:<20s}{1:>19.2f}').format("ADVANCE AMOUNT: ", advanceAmount).replace(' ', '&nbsp;'),textStyle))
    if inCash > 0:
        Story.append(Paragraph(('{0:>13s}{1:>26.2f}').format("IN CASH: ", inCash).replace(' ', '&nbsp;'),textStyle))
    if byCard > 0:
        Story.append(Paragraph(('{0:>13s}{1:>26.2f}').format("BY CARD: ", byCard).replace(' ', '&nbsp;'),textStyle))
    Story.append(Paragraph(('{0:<20s}{1:>19.2f}').format("BALANCE TO BE PAID: ", (totalAmount - advanceAmount)).replace(' ', '&nbsp;'),textStyle))
    
    drawLine(Story, lineStyle)
    Story.append(Paragraph("Terms & Conditions:", textStyle))                                        
    Story.append(Paragraph("* In case of cancellation of order on customer request, 10% of product amount subject to minimum Rs 500/- shall be deducted as cancellation charges. Customer shall provide original copy of 'Advance Receipt' to get the balance amount as refund",textStyle))                                        
    Story.append(Paragraph("* Balance amount as mentioned above to be paid at the time of delivery of product to courier agency. Refusal to accept the product delivered through courier shall be deemed as cancellation of order and cancellation charges shall become applicable.",textStyle))                                         
    Story.append(Paragraph("* The product as mentioned above shall be made available to the customer, subject to its availability from the manufacturer",textStyle))
    Story.append(Paragraph("* All disputes subject to Delhi Jurisdiction",textStyle))                                        
    drawLine(Story, lineStyle)
    Story.append(Paragraph(('{0:<20s}{1:>19s}').format("Customer care : ", "0120-6744700").replace(' ', '&nbsp;'),textStyle))
    drawLine(Story, lineStyle)
    
    doc.addPageTemplates([PageTemplate(id='TwoCol',frames=[frame1]), ])

    #start the construction of the pdf
    doc.build(Story)
    
    try:
        fileToSend = open(filename, "rb")
        pdfFile = fileToSend.read()
    except:
        return bin(0)
    
    return pdfFile

def add_frc(circle, operatorId, denomination, maxDiscount):
    frc = FRC.query.filter(FRC.circleId == circle).filter(FRC.operatorId == operatorId).filter(FRC.denomination == denomination).first()
    if frc:
        frc.maxDiscount = maxDiscount
    else:
        frc = FRC()
        frc.circleId = circle
        frc.denomination = denomination
        frc.maxDiscount = maxDiscount
        frc.operatorId = operatorId
    session.commit()
    return True 

def add_series(circle, operatorId, series):
    operatorSeriesObject = OperatorSeries.query.filter(OperatorSeries.series == series).first()
    if operatorSeriesObject:
        circleName = TelecomCircle.get_by(id = operatorSeriesObject.circleId).name
        operatorName = ServiceProvider.get_by(id = operatorSeriesObject.operatorId).name
        return "Series already exists for : " + operatorName + " in circle : " + circleName
    else :
        operatorSeriesObject = OperatorSeries()
        operatorSeriesObject.series = series
        operatorSeriesObject.circleId = circle
        operatorSeriesObject.operatorId = operatorId
        session.commit()
        return "Successfully saved"
    
         
def save_store_order_detail(thrift_storeOrderDetail):
    txnId = thrift_storeOrderDetail.orderId
    txn = Transaction.get_by(id=txnId)
    oid = txn.orders[0].id
    ds_storeOrderDetail = StoreOrderDetail()
    ds_storeOrderDetail.orderId = oid
    ds_storeOrderDetail.advanceAmount = thrift_storeOrderDetail.advanceAmount
    ds_storeOrderDetail.cardAmount = thrift_storeOrderDetail.cardAmount
    ds_storeOrderDetail.cashAmount = thrift_storeOrderDetail.cashAmount
    ds_storeOrderDetail.storeId = thrift_storeOrderDetail.storeId
    ds_storeOrderDetail.payStatus = thrift_storeOrderDetail.payStatus
    ds_storeOrderDetail.cardRefundAmount = thrift_storeOrderDetail.cardRefundAmount
    ds_storeOrderDetail.cashRefundAmount = thrift_storeOrderDetail.cashRefundAmount
    ds_storeOrderDetail.edcBank = thrift_storeOrderDetail.edcBank
    ds_storeOrderDetail.approvalCode = thrift_storeOrderDetail.approvalCode
    ds_storeOrderDetail.cardType = thrift_storeOrderDetail.cardType
    session.commit()
    return True
    
def get_store_order_detail(orderId, storeId):
    storeOrderDetail = StoreOrderDetail.query.filter(StoreOrderDetail.orderId == orderId).filter(StoreOrderDetail.storeId == storeId).first()
    if storeOrderDetail:
        return storeOrderDetail
    else:
        return StoreOrderDetail()
    
def get_all_edc_banks():
    allBankNames = []
    edcBanks = EdcBank.query.all();
    for bank in edcBanks:
        allBankNames.append(bank.name)
    return allBankNames

def save_refund_amounts_for_store_order(orderId, storeId, cashRefundAmount, cardRefundAmount):
    storeOrderDetail = StoreOrderDetail.query.filter(StoreOrderDetail.orderId == orderId).filter(StoreOrderDetail.storeId == storeId).first()
    if storeOrderDetail:
        storeOrderDetail.cashRefundAmount = cashRefundAmount
        storeOrderDetail.cardRefundAmount = cardRefundAmount
        session.commit()
        return True
    else:
        return False

def get_collections_for_store(storeId, startdate, enddate):
    if startdate == -1:
        startdate = None
    if enddate == -1:
        enddate = None
    if storeId== 0:
        storeId = None
        
    if storeId:
        store = HotspotStore.get_by(id = storeId)
        
    hotspotId = store.hotspotId
        
    query = StoreOrderCollection.query
    if storeId is not None:
        query = query.filter(StoreOrderCollection.hotspotId == hotspotId)
    else:
        return []
        
    if startdate is not None and enddate is not None:
        startdate = to_py_date(startdate).date()
        enddate = to_py_date(enddate).date()
        if startdate == enddate:
            query = query.filter(cast(StoreOrderCollection.pushedAt, DATE) == enddate)
        elif enddate < startdate:
            query = query.filter(between(cast(StoreOrderCollection.pushedAt, DATE), enddate, startdate))
        else:
            query = query.filter(between(cast(StoreOrderCollection.pushedAt, DATE), startdate, enddate))
    elif startdate is not None:
        startdate = to_py_date(startdate).date()
        query = query.filter(cast(StoreOrderCollection.pushedAt, DATE) >= startdate)
    elif enddate is not None:
        enddate = to_py_date(enddate).date()
        query = query.filter(cast(StoreOrderCollection.pushedAt, DATE) <= enddate)

    return query.filter(StoreOrderCollection.pushedToOcr == 1).all()    

def update_timestamp_for_amazon_order(orderDeliveryMap):
    for orderId,dateMap in orderDeliveryMap.iteritems():
        order = get_order(orderId)
        order.expected_delivery_time = datetime.datetime.strptime(dateMap.values()[0]+' 15:00:00', '%d-%m-%Y %H:%M:%S') 
        order.promised_delivery_time = datetime.datetime.strptime(dateMap.values()[0]+' 15:00:00', '%d-%m-%Y %H:%M:%S')
        promisedShipping = datetime.datetime.strptime(dateMap.keys()[0]+' 15:00:00', '%d-%m-%Y %H:%M:%S')
        order.expected_shipping_time = promisedShipping
        order.promised_shipping_time = promisedShipping
        session.commit()
    return True

def update_source_detail_timestamp(id,lastUpdatedOn):
    detail = get_source_detail(id)
    detail.lastUpdatedOn = to_py_date(lastUpdatedOn)
    session.commit()
    return True

def get_orders_by_mobile_number(mobileNumber):
    return Order.query.filter(Order.customer_mobilenumber == mobileNumber).all()

def get_orders_by_amazon_id(amazonId):
    orderIds = []
    amazonOrders = AmazonOrder.query.filter(AmazonOrder.amazonOrderCode == amazonId).all()
    for amazonOrder in amazonOrders:
        orderIds.append(amazonOrder.orderId)
    return get_order_list(orderIds)
    
def update_freebie_item(orderId, newFreebieItemId):
    order = Order.get_by(id = orderId)
    order.freebieItemId = newFreebieItemId
    session.commit()
    return order

#Start:- Added by Manish Sharma for FedEx Integration- Shipment Creation on 31-Jul-2013
def update_order_AWB(orderId, airwayBillNo):
    order = Order.get_by(id = orderId)
    order.airwaybill_no=airwayBillNo
    order.tracking_id=airwayBillNo
    #order.logisticsTransactionId = str(order.transaction_id)+airwayBillNo
    session.commit()
    shipmentLogisticsCostObj = TShipmentLogisticsCostDetail()
    shipmentLogisticsCostObj.airwayBillNo = airwayBillNo
    shipmentLogisticsCostObj.logisticsTransactionId = order.logisticsTransactionId
    shipmentLogisticsCostObj.shipmentAmount = order.total_amount
    shipmentLogisticsCostObj.shipmentCodCollectionCharges = order.lineitems[0].codCollectionCharges
    shipmentLogisticsCostObj.shipmentLogsiticsCost = order.lineitems[0].logisticsCost
    shipmentLogisticsCostObj.shipmentWeight = order.total_weight
    
    shipmentLogisticsCostDetails = []
    shipmentLogisticsCostDetails.append(shipmentLogisticsCostObj)
    add_or_update_shipment_logistics_cost_details(shipmentLogisticsCostDetails)
    return order
#End:- Added by Manish Sharma for FedEx Integration- Shipment Creation on 31-Jul-2013

def get_hotspot_service_matrices():
    hotspotServiceMatrices = HotspotServiceMatrix.query.all()
    return hotspotServiceMatrices

def get_orders_by_vendor(vendors, statuses):
    if not len(statuses):
        raise TransactionServiceException(101, "Status list is empty")
    if not len(vendors):
        raise TransactionServiceException(101, "Vendor list is empty")
    warehouse_ids = []
    inventory_client = InventoryClient().get_client()
    for vendor in vendors:
        warehouses = inventory_client.getWarehouses(None, None, vendor, 0, 0)
        for warehouse in warehouses:
            warehouse_ids.append(warehouse.id)
    query = Order.query.filter(Order.status.in_(statuses))
    query = query.filter(Order.warehouse_id.in_(warehouse_ids))
        #raise TransactionServiceException(101, "bad warehouse id")
    return query.all()    

def __update_tax_type(order):
    print "updating tax type"

def create_ebay_order(ebay_order):
    ebayOrder = EbayOrder()
    ebayOrder.orderId =  ebay_order.orderId
    ebayOrder.paisaPayId =  ebay_order.paisaPayId
    ebayOrder.salesRecordNumber = ebay_order.salesRecordNumber
    ebayOrder.ebayListingId =  ebay_order.ebayListingId
    ebayOrder.subsidyAmount =  ebay_order.subsidyAmount
    ebayOrder.ebayTxnDate =  to_py_date(ebay_order.ebayTxnDate)
    ebayOrder.transactionId = ebay_order.transactionId
    ebayOrder.listingName = ebay_order.listingName
    ebayOrder.listingPrice = ebay_order.listingPrice
    session.commit()

def get_ebay_order_by_sales_rec_number(sales_rec_number):
    return EbayOrder.query.filter(EbayOrder.salesRecordNumber == sales_rec_number).one()

def get_ebay_order(sales_rec_number, listingId, paisa_pay_id):
    query = EbayOrder.query
    if sales_rec_number:
        query = query.filter(EbayOrder.salesRecordNumber == sales_rec_number)
    if listingId:
        query = query.filter(EbayOrder.ebayListingId == listingId)
    if paisa_pay_id:
        query = query.filter(EbayOrder.paisaPayId == paisa_pay_id)
    return query.all()
    #return EbayOrder.query.filter(EbayOrder.salesRecordNumber == sales_rec_number).filter(EbayOrder.ebayListingId == listingId).one()

def get_ebay_order_by_orderId(order_id):
    return EbayOrder.query.filter(EbayOrder.orderId == order_id).one()

def update_ebay_order(ebay_order):
    ebayOrder = get_ebay_order_by_orderId(ebay_order.orderId)
    ebayOrder.subsidyAmount = ebay_order.subsidyAmount
    ebayOrder.transactionId = ebay_order.transactionId
    ebayOrder.ebayTxnDate = to_py_date(ebay_order.ebayTxnDate)
    ebayOrder.bluedartPaisaPayRef = ebay_order.bluedartPaisaPayRef
    ebayOrder.listingPrice = ebay_order.listingPrice
    session.commit()

def ebay_order_exists(sales_rec_number, ebay_listing_id):
    try:
        ebayOrder = get_ebay_order_by_sales_rec_number(sales_rec_number)
        if ebayOrder.ebayListingId == ebay_listing_id:
            return True
        else:
            return False
    except Exception as e:
        return False
def update_order_for_ebay(t_order):
    order = get_order(t_order.id)
    order.customer_id = t_order.customer_id
    order.customer_name = t_order.customer_name
    order.customer_city = t_order.customer_city
    order.customer_state = t_order.customer_state
    order.customer_mobilenumber = t_order.customer_mobilenumber
    order.customer_pincode = t_order.customer_pincode
    order.customer_address1 = t_order.customer_address1
    order.customer_address2 = t_order.customer_address2
    order.customer_email = t_order.customer_email
    order.logistics_provider_id = t_order.logistics_provider_id
    order.airwaybill_no = t_order.airwaybill_no
    order.tracking_id = t_order.tracking_id
    order.status = t_order.status  
    order.statusDescription = t_order.statusDescription
    order.cod = 0
    order.otg = False
    order.freebieItemId = 0
    order.orderType = OrderType.B2C
    order.source = 6
    
    transaction = get_transaction(order.transaction_id)
    transaction.status = TransactionStatus.AUTHORIZED
    transaction.customer_id = t_order.customer_id
    session.commit()
    
def split_ebay_order(order_id, split_order_qty, split_order_item_id, use_power_ship):
    #In case of split now order is always assigned based on the website logic...Also itemId won't work for now
    order = get_order(order_id)
    if split_order_qty >=order.lineitems[0].quantity:
        raise TransactionServiceException(108, "Split Quantity is greater than or equal to order quantity")
    if order.status not in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT, OrderStatus.CAPTURE_IN_PROCESS, OrderStatus.REJECTED, OrderStatus.ACCEPTED]:
        raise TransactionServiceException(108, "Order not allowed to be split")
    new_order = __clone_order(order, False, False)
    new_order.expected_delivery_time = order.expected_delivery_time
    new_order.promised_delivery_time = order.promised_delivery_time
    new_order.expected_shipping_time = order.expected_shipping_time
    new_order.promised_shipping_time = order.promised_shipping_time
    if use_power_ship:
        new_order.logistics_provider_id = order.logistics_provider_id
        new_order.airwaybill_no = order.airwaybill_no
        new_order.tracking_id = order.tracking_id
    
    new_order.lineitems[0].quantity = split_order_qty
    new_order.lineitems[0].total_price = new_order.lineitems[0].unit_price*split_order_qty
    new_order.total_amount = new_order.lineitems[0].total_price
    
    order.lineitems[0].quantity = order.lineitems[0].quantity - split_order_qty
    order.lineitems[0].total_price = order.lineitems[0].unit_price * order.lineitems[0].quantity
    order.total_amount = order.lineitems[0].total_price
    
    session.commit()
    return new_order

def add_or_update_amazon_fba_sales_snapshot(amazonfbasalessnapshot):
    salesnapshotfordate = AmazonFbaSalesSnapshot.get_by(item_id = amazonfbasalessnapshot.item_id,dateOfSale=to_py_date(amazonfbasalessnapshot.dateOfSale).date(),fcLocation=amazonfbasalessnapshot.fcLocation)
    if salesnapshotfordate is None:
        amazon_fba_sales_snapshot = AmazonFbaSalesSnapshot()
        amazon_fba_sales_snapshot.dateOfSale = to_py_date(amazonfbasalessnapshot.dateOfSale).date()
        amazon_fba_sales_snapshot.item_id = amazonfbasalessnapshot.item_id
        amazon_fba_sales_snapshot.totalOrderCount = amazonfbasalessnapshot.totalOrderCount
        amazon_fba_sales_snapshot.amazonFbaInventory = amazonfbasalessnapshot.amazonFbaInventory
        amazon_fba_sales_snapshot.isOutOfStock = amazonfbasalessnapshot.isOutOfStock
        amazon_fba_sales_snapshot.promotionOrderCount = amazonfbasalessnapshot.promotionOrderCount
        amazon_fba_sales_snapshot.totalSale = amazonfbasalessnapshot.totalSale
        amazon_fba_sales_snapshot.promotionSale = amazonfbasalessnapshot.promotionSale
        #if amazonfbasalessnapshot.ourPrice is not None:
        amazon_fba_sales_snapshot.ourPriceSnapshotDate = to_py_date(amazonfbasalessnapshot.ourPriceSnapshotDate)
        amazon_fba_sales_snapshot.ourPrice = amazonfbasalessnapshot.ourPrice
        #if amazonfbasalessnapshot.salePrice is not None:
        amazon_fba_sales_snapshot.salePriceSnapshotDate = to_py_date(amazonfbasalessnapshot.salePriceSnapshotDate)
        amazon_fba_sales_snapshot.salePrice = amazonfbasalessnapshot.salePrice
        #if amazonfbasalessnapshot.minFbaPrice is not None:
        amazon_fba_sales_snapshot.minFbaPriceSnapshotDate = to_py_date(amazonfbasalessnapshot.minFbaPriceSnapshotDate)
        amazon_fba_sales_snapshot.minFbaPrice = amazonfbasalessnapshot.minFbaPrice
        #if amazonfbasalessnapshot.minMfnPrice is not None:
        amazon_fba_sales_snapshot.minMfnPriceSnapshotDate = to_py_date(amazonfbasalessnapshot.minMfnPriceSnapshotDate)
        amazon_fba_sales_snapshot.minMfnPrice = amazonfbasalessnapshot.minMfnPrice
        amazon_fba_sales_snapshot.fcLocation = amazonfbasalessnapshot.fcLocation 
    
    else:
        if(amazonfbasalessnapshot.totalOrderCount > 0):
            salesnapshotfordate.isOutOfStock = 0
        salesnapshotfordate.promotionOrderCount = amazonfbasalessnapshot.promotionOrderCount
        salesnapshotfordate.totalSale = amazonfbasalessnapshot.totalSale
        salesnapshotfordate.promotionSale = amazonfbasalessnapshot.promotionSale
        salesnapshotfordate.totalOrderCount = amazonfbasalessnapshot.totalOrderCount
        salesnapshotfordate.fcLocation = amazonfbasalessnapshot.fcLocation
    session.commit()    
    
def get_amazon_fba_sales_snapshot_for_days(interval):
    query = session.query(Order.status, func.count(Order.id)).group_by(Order.status)
    date = datetime.datetime.now()
    toDate = date-timedelta(days = 1)
    fromDate = toDate-timedelta(days = interval)
    fbasalessnapshot = AmazonFbaSalesSnapshot.query.filter(AmazonFbaSalesSnapshot.dateOfSale.between (fromDate.date(),toDate.date())).order_by(AmazonFbaSalesSnapshot.dateOfSale).all()
    return fbasalessnapshot        
    
def create_snapdeal_order(t_snapdeal_order):
    snapdealOrder = SnapdealOrder()
    snapdealOrder.orderId = t_snapdeal_order.orderId
    snapdealOrder.subOrderId = t_snapdeal_order.subOrderId
    snapdealOrder.referenceCode = t_snapdeal_order.referenceCode
    snapdealOrder.productName = t_snapdeal_order.productName
    snapdealOrder.listingPrice = t_snapdeal_order.listingPrice
    snapdealOrder.snapdealTxnDate = to_py_date(t_snapdeal_order.snapdealTxnDate)
    snapdealOrder.maxNlc = t_snapdeal_order.maxNlc
    order = Order.get_by(id=t_snapdeal_order.orderId)
    order.status = OrderStatus.ACCEPTED
    user = None
    try:
        user_client = UserClient().get_client()
        user_to_add = User()
        user_to_add.email = 'SD.'+ str(t_snapdeal_order.orderId) + '@mailinator.com'
        user_to_add.password = 'hSp5gvCsrrhZcceE6mzzOQ'
        user_to_add.communicationEmail = user_to_add.email
        user_to_add.sourceStartTime = int(round(time.time() * 1000))
        user_to_add.sourceId = OrderSource.SNAPDEAL
        user_to_add.sex = Sex.WONT_SAY
        user_to_add = user_client.createUser(user_to_add)
        user = user_to_add
    except:
        pass
    
    if user:
        order.customer_id = user.userId
        order.customer_email = user.email
    session.commit()
    
def get_snapdeal_order(order_id, reference_code, subOrder_id):
    query = SnapdealOrder.query
    if order_id:
        query = query.filter(SnapdealOrder.orderId == order_id)
    elif reference_code==subOrder_id:
        if reference_code is None:
            reference_code=''
        query = query.filter(or_(SnapdealOrder.referenceCode == reference_code, SnapdealOrder.subOrderId== subOrder_id))
    else:
        query = query.filter(and_(SnapdealOrder.referenceCode == reference_code, SnapdealOrder.subOrderId== subOrder_id))
        return query.one()
    return query.all()

def snapdeal_order_exists(suborder_id, reference_code):
    exists = SnapdealOrder.query.filter_by(subOrderId=suborder_id,referenceCode=reference_code).first()
    if exists is not None:
        return True
    else:
        return False
         
def get_amazon_fba_sales_latest_snapshot_for_item(item_id):
    return AmazonFbaSalesSnapshot.query.filter(AmazonFbaSalesSnapshot.item_id==item_id).filter(AmazonFbaSalesSnapshot.dateOfSale!=datetime.datetime.now().date()).order_by(desc(AmazonFbaSalesSnapshot.dateOfSale)).first()

def update_latest_fba_prices_for_item(fbaitemprices):
    fbasalessnapshot = AmazonFbaSalesSnapshot.query.filter(AmazonFbaSalesSnapshot.item_id==fbaitemprices.item_id).order_by(desc(AmazonFbaSalesSnapshot.dateOfSale)).first()
    if(fbasalessnapshot.ourPriceSnapshotDate < to_java_date(fbaitemprices.ourPriceSnapshotDate) and fbaitemprices.ourPrice is not None):
        fbasalessnapshot.ourPriceSnapshotDate = to_py_date(fbaitemprices.ourPriceSnapshotDate)
        fbasalessnapshot.ourPrice = fbaitemprices.ourPrice
    if(fbasalessnapshot.salePriceSnapshotDate < to_java_date(fbaitemprices.salePriceSnapshotDate) and fbaitemprices.salePrice is not None):
            fbasalessnapshot.salePriceSnapshotDate = to_py_date(fbaitemprices.salePriceSnapshotDate)
            fbasalessnapshot.salePrice = fbaitemprices.salePrice
    if(fbasalessnapshot.minFbaPriceSnapshotDate < to_java_date(fbaitemprices.minFbaPriceSnapshotDate) and fbaitemprices.minFbaPrice is not None):
            fbasalessnapshot.minFbaPriceSnapshotDate = to_py_date(fbaitemprices.minFbaPriceSnapshotDate)
            fbasalessnapshot.minFbaPrice = fbaitemprices.minFbaPrice
    if(fbasalessnapshot.minMfnPriceSnapshotDate < to_java_date(fbaitemprices.minMfnPriceSnapshotDate) and fbaitemprices.minMfnPrice is not None):
        fbasalessnapshot.minMfnPriceSnapshotDate = to_py_date(fbaitemprices.minMfnPriceSnapshotDate)
        fbasalessnapshot.minMfnPrice = fbaitemprices.minMfnPrice
    session.commit()
    
def flipkart_order_exists(orderid,suborderid):
    exists = FlipkartOrder.query.filter_by(flipkartOrderId=orderid,flipkartSubOrderId=suborderid).first()
    if exists is not None:
        return True
    else:
        return False
    
def create_flipkart_order(flipkartorder):
    order = FlipkartOrder()
    order.orderId = flipkartorder.orderId
    order.flipkartSubOrderId = flipkartorder.flipkartSubOrderId
    order.flipkartOrderId = flipkartorder.flipkartOrderId
    order.flipkartTxnDate = to_py_date(flipkartorder.flipkartTxnDate)
    order.shippingPrice = flipkartorder.shippingPrice 
    order.octroiFee = flipkartorder.octroiFee
    order.emiFee = flipkartorder.emiFee
    order.maxNlc = flipkartorder.maxNlc
    s_order = Order.get_by(id=flipkartorder.orderId)
    if s_order.cod:
        s_order.status = OrderStatus.COD_VERIFICATION_PENDING
    else:
        s_order.status = OrderStatus.ACCEPTED
        s_order.accepted_timetsmap = datetime.datetime.now()
    user = None
    try:
        user_client = UserClient().get_client()
        user_to_add = User()
        user_to_add.email = 'FK.'+ str(flipkartorder.orderId) + '@mailinator.com'
        user_to_add.password = 'gR9zF-Ish2im6tbYFNivgA'
        user_to_add.communicationEmail = user_to_add.email
        user_to_add.sourceStartTime = int(round(time.time() * 1000))
        user_to_add.sourceId = OrderSource.FLIPKART
        user_to_add.sex = Sex.WONT_SAY
        user_to_add = user_client.createUser(user_to_add)
        user = user_to_add
    except:
        pass
    
    if user:
        s_order.customer_id = user.userId
        s_order.customer_email = user.email
        
    session.commit()

def get_flipkart_order(orderid):
    flipkartorder = FlipkartOrder.query.filter_by(orderId=orderid).first()
    if not flipkartorder:
        print "No found order for orderid " + str(orderid)
        raise TransactionServiceException(108, "no such order")
    return flipkartorder    

def get_flipkart_order_by_subOrderId(flipkartOrderItemId):
    flipkartorder = FlipkartOrder.query.filter_by(flipkartSubOrderId=flipkartOrderItemId).first()
    if not flipkartorder:
        raise TransactionServiceException(108, "no such flipkart order " + str(flipkartOrderItemId))
    return flipkartorder

def update_flipkart_order_dates_and_awb(orderid,suborderid,date,awb):
    flipkartorder = FlipkartOrder.query.filter_by(flipkartOrderId=orderid,flipkartSubOrderId=suborderid).first()
    print flipkartorder 
    order = get_order(flipkartorder.orderId)
    if not order:
        print "No found order for orderid " + str(orderid)
        raise TransactionServiceException(108, "no such order")
    order.tracking_id = awb
    order.expected_delivery_time = to_py_date(date + 4*24*60*60*1000)
    order.promised_delivery_time = to_py_date(date + 4*24*60*60*1000)
    order.expected_shipping_time = to_py_date(date)
    order.promised_shipping_time = to_py_date(date)
    session.commit()
    
def get_order_for_airwayBillNo(airwaybillNo):
    orders = Order.query.filter(Order.airwaybill_no==airwaybillNo).filter(Order.logisticsTransactionId != None).all()
    if not orders:
        orders = []
    
    return orders
    
    
def get_orders_count_created_after_timestamp_for_source(timestamp,source):
    item_ordercount = dict()
    status=[15,18,34,0,1]
    items =session.query(LineItem.item_id,func.count(LineItem.id)).join((Order,LineItem.order_id==Order.id)).filter(Order.source==source).filter(Order.created_timestamp >=to_py_date(timestamp)).filter(not_(Order.status.in_(status))).group_by(LineItem.item_id).all()
    print items
    for item in items:
        print item[0]
        print item[1]
        item_ordercount[item[0]] = item[1]
    return item_ordercount    

    
def get_min_created_timestamp_undelivered_orders_for_source(source):
    order = order = Order.query.filter(Order.source== source).filter(Order.delivery_timestamp == None).filter(Order.status == OrderStatus.SHIPPED_FROM_WH).limit(1).one()
    print to_java_date(order.created_timestamp)
    return to_java_date(order.created_timestamp)

def is_private_deal_transaction(transaction_id):
    result = False
    transaction = Transaction.get_by(id=transaction_id)
    if transaction:
        allItems = []
        for order in transaction.orders:
            allItems.append(order.lineitems[0].item_id)
        
        catalog_client = CatalogClient().get_client()
        return len(catalog_client.getAllActivePrivateDeals(allItems, 0)) > 0 
    
            
    return result

def update_snapdeal_orders_status(orders):
    for statusString, orderList in orders.iteritems():
        if statusString == "Cancelled":
            try:
                for cancel_order in orderList:
                    ref_code = cancel_order[0]
                    sub_order_code = cancel_order[1]
                    snapdeal_order = None 
                    try:
                        snapdeal_order = get_snapdeal_order(None, ref_code, sub_order_code)
                    except Exception as e:
                        print 'Caught in Updation of Cancelled Orders Getting Snapdeal Order'
                        pass
                    if snapdeal_order:
                        order = get_order(snapdeal_order.orderId)
                        if order.status == OrderStatus.SHIPPED_FROM_WH:
                            order.status = OrderStatus.SHIPPED_TO_LOGST
                            session.commit()
            except Exception as e:
                print 'Caught in Updation of Cancelled Orders'
                print e
                print sys.exc_info()
                
        
        if statusString == "Delivered":
            try:
                for del_order in orderList:
                    ref_code = del_order[0]
                    sub_order_code = del_order[1]
                    dlvry_timestamp = del_order[2]
                    snapdeal_order = None                    
                    try:
                        snapdeal_order = get_snapdeal_order(None, ref_code, sub_order_code)
                    except Exception as e:
                        print 'Caught in Updation of Delivered Orders Getting Snapdeal Order'
                        pass
                    if snapdeal_order:
                        order = get_order(snapdeal_order.orderId)
                        if order.status == OrderStatus.SHIPPED_FROM_WH:
                            order.delivery_timestamp = datetime.datetime.strptime(dlvry_timestamp, "%Y-%m-%d %H:%M:%S")
                            order.receiver = order.customer_name
                            order.status = OrderStatus.DELIVERY_SUCCESS
                            order.statusDescription = "Order delivered"
                            session.commit()
            except Exception as e:
                print 'Caught in Updation of Delivered Orders'
                print e
                print sys.exc_info()
    '''
    try:
        for del_order in delivered_orders:
            ref_code = del_order[0]
            sub_order_code = del_order[1]
            dlvry_timestamp = del_order[2]
            
            if snapdeal_order_exists(sub_order_code, ref_code):
                snapdeal_order = get_snapdeal_order(None, None, sub_order_code)
                if snapdeal_order:
                    order = get_order(snapdeal_order.orderId)
                    if order.status == OrderStatus.SHIPPED_FROM_WH:
                        order.delivery_timestamp = datetime.datetime.strptime(dlvry_timestamp, "%Y-%m-%d %H:%M:%S")
                        order.receiver = order.customer_name
                        order.status = OrderStatus.DELIVERY_SUCCESS
                        order.statusDescription = "Order delivered"
                        session.commit()
    except Exception as e:
        print e
        print sys.exc_info()
    '''
            
def update_flipkart_orders_status(delivered_orders):
    try:
        for del_order in delivered_orders:
            flipkart_order_id = del_order[0]
            sub_order_code = del_order[1]
            dlvry_timestamp = del_order[2]  
            
            if flipkart_order_exists(flipkart_order_id,sub_order_code):
                flipkart_order = get_flipkart_order_by_subOrderId(sub_order_code)
                if flipkart_order:
                    order = get_order(flipkart_order.orderId)
                    if order.status == OrderStatus.SHIPPED_FROM_WH:
                        order.delivery_timestamp = datetime.datetime.strptime(dlvry_timestamp, "%Y-%m-%d %H:%M:%S")
                        order.receiver = order.customer_name
                        order.status = OrderStatus.DELIVERY_SUCCESS
                        order.statusDescription = "Order delivered"
                        session.commit()
    except Exception as e:
        print e
        print sys.exc_info()
            
    '''    
    order_ids = delivered_orders.keys()
    for orderId in order_ids:
        order = Order.query.filter(Order.id == orderId).first()
        order.status = OrderStatus.DELIVERY_SUCCESS
        order.statusDescription = "Order delivered"
        order.delivery_timestamp = datetime.datetime.strptime(delivered_orders.get(orderId), "%Y-%m-%d %H:%M:%S")
        session.commit()
    '''

def bulk_add_or_update_amazon_fba_sales_snapshot(amazonfbasalessnapshotlist):
    for amazonfbasalessnapshot in amazonfbasalessnapshotlist:
        salesnapshotfordate = AmazonFbaSalesSnapshot.get_by(item_id = amazonfbasalessnapshot.item_id,dateOfSale=to_py_date(amazonfbasalessnapshot.dateOfSale).date(),fcLocation=amazonfbasalessnapshot.fcLocation)
        if salesnapshotfordate is None:
            amazon_fba_sales_snapshot = AmazonFbaSalesSnapshot()
            amazon_fba_sales_snapshot.dateOfSale = to_py_date(amazonfbasalessnapshot.dateOfSale).date()
            amazon_fba_sales_snapshot.item_id = amazonfbasalessnapshot.item_id
            amazon_fba_sales_snapshot.totalOrderCount = amazonfbasalessnapshot.totalOrderCount
            amazon_fba_sales_snapshot.amazonFbaInventory = amazonfbasalessnapshot.amazonFbaInventory
            amazon_fba_sales_snapshot.isOutOfStock = amazonfbasalessnapshot.isOutOfStock
            amazon_fba_sales_snapshot.promotionOrderCount = amazonfbasalessnapshot.promotionOrderCount
            amazon_fba_sales_snapshot.totalSale = amazonfbasalessnapshot.totalSale
            amazon_fba_sales_snapshot.promotionSale = amazonfbasalessnapshot.promotionSale
            #if amazonfbasalessnapshot.ourPrice is not None:
            amazon_fba_sales_snapshot.ourPriceSnapshotDate = to_py_date(amazonfbasalessnapshot.ourPriceSnapshotDate)
            amazon_fba_sales_snapshot.ourPrice = amazonfbasalessnapshot.ourPrice
            #if amazonfbasalessnapshot.salePrice is not None:
            amazon_fba_sales_snapshot.salePriceSnapshotDate = to_py_date(amazonfbasalessnapshot.salePriceSnapshotDate)
            amazon_fba_sales_snapshot.salePrice = amazonfbasalessnapshot.salePrice
            #if amazonfbasalessnapshot.minFbaPrice is not None:
            amazon_fba_sales_snapshot.minFbaPriceSnapshotDate = to_py_date(amazonfbasalessnapshot.minFbaPriceSnapshotDate)
            amazon_fba_sales_snapshot.minFbaPrice = amazonfbasalessnapshot.minFbaPrice
            #if amazonfbasalessnapshot.minMfnPrice is not None:
            amazon_fba_sales_snapshot.minMfnPriceSnapshotDate = to_py_date(amazonfbasalessnapshot.minMfnPriceSnapshotDate)
            amazon_fba_sales_snapshot.minMfnPrice = amazonfbasalessnapshot.minMfnPrice
            amazon_fba_sales_snapshot.fcLocation = amazonfbasalessnapshot.fcLocation
        else:
            if(amazonfbasalessnapshot.totalOrderCount > 0):
                salesnapshotfordate.isOutOfStock = 0
            salesnapshotfordate.promotionOrderCount = amazonfbasalessnapshot.promotionOrderCount
            salesnapshotfordate.totalSale = amazonfbasalessnapshot.totalSale
            salesnapshotfordate.promotionSale = amazonfbasalessnapshot.promotionSale
            salesnapshotfordate.totalOrderCount = amazonfbasalessnapshot.totalOrderCount
            salesnapshotfordate.fcLocation = amazonfbasalessnapshot.fcLocation
    session.commit()

def get_created_orders_for_flipkart(flipkartorderids):
    item_ordercount = dict()
    orders = []
    flipkart_orders = session.query(FlipkartOrder).filter(FlipkartOrder.flipkartOrderId.in_(tuple(flipkartorderids))).all()
    print flipkart_orders 
    if len(flipkart_orders)!=0:
        for order in flipkart_orders:
            print order.orderId 
            orders.append(order.orderId)
        items =session.query(LineItem.item_id,func.count(LineItem.quantity)).join((Order,LineItem.order_id==Order.id)).filter(Order.id.in_(tuple(orders))).filter(Order.status >=3).filter(Order.shipping_timestamp==None).group_by(LineItem.item_id).all()    
    else:
        return item_ordercount
    print "items map after query"     
    print items
    for item in items:
        print "item id"
        print item[0]
        print "order count"
        print item[1]
        item_ordercount[item[0]] = item[1]
    print item_ordercount    
    return item_ordercount    

def change_easyship_mfn_order_txn_status(transaction_id, new_status, description, pickUp, orderType, source, shipTimestamp, deliveryTimeStamp):
    transaction = get_transaction(transaction_id)
    transaction.status = new_status
    transaction.status_message = description
    ## Assign runner in case of delhi pincodes
    if transaction.orders[0].pickupStoreId:
        logistics_client = LogisticsClient().get_client() 
        store = logistics_client.getPickupStore(transaction.orders[0].pickupStoreId)
        if delhi_pincodes.__contains__(store.pin):
            pickUp = PickUpType.RUNNER  
        
    if new_status == TransactionStatus.FAILED:
        for order in transaction.orders:
            order.status = OrderStatus.PAYMENT_FAILED
            order.statusDescription = "Payment Failed"
    elif new_status == TransactionStatus.AUTHORIZED or new_status == TransactionStatus.FLAGGED:
        for order in transaction.orders:
            if new_status == TransactionStatus.AUTHORIZED:
                order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
                order.statusDescription = "Submitted to warehouse"
            elif new_status == TransactionStatus.FLAGGED:
                order.status = OrderStatus.PAYMENT_FLAGGED
                order.statusDescription = "Payment flagged by gateway"
            order.cod = False
            order.orderType = orderType
            #After we got payment success, we will set logistics info also 
            logistics_client = LogisticsClient().get_client()
            #FIXME line item is only one now. If multiple will come, need to fix.
            item_id = order.lineitems[0].item_id
            logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.PREPAID, pickUp)
            
            #current_time = datetime.datetime.now()
            
            #logistics_info.deliveryTime = adjust_delivery_time(to_py_date(deliveryTimeStamp), 0)
            #logistics_info.shippingTime = adjust_delivery_time(to_py_date(shipTimestamp), 0)
            #logistics_info.deliveryDelay = 0
            
            
            order.otg = logistics_info.otgAvailable
            order.warehouse_id = logistics_info.warehouseId
            order.fulfilmentWarehouseId = logistics_info.fulfilmentWarehouseId
            order.logistics_provider_id = 45
            #Start:- Added by Manish Sharma for FedEx Integration - Shipment Creation on 31-Jul-2013
            #order.airwaybill_no = logistics_info.airway_billno
            #order.tracking_id = order.airwaybill_no
            #End:- Added by Manish Sharma for FedEx Integration- Shipment Creation on 31-Jul-2013
            
            order.courier_delivery_time = to_py_date(deliveryTimeStamp).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
            order.expected_shipping_time = to_py_date(shipTimestamp).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
            order.promised_shipping_time = order.expected_shipping_time
            order.expected_delivery_time = to_py_date(deliveryTimeStamp).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
            order.promised_delivery_time = order.expected_delivery_time 
            
            if order.pickupStoreId:
                order.otg = False
                
            inventory_client = InventoryClient().get_client()
            if order.productCondition != ProductCondition.BAD:
                inventory_client.reserveItemInWarehouse(item_id, logistics_info.fulfilmentWarehouseId, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), order.lineitems[0].quantity)

            try:
                item_pricing = inventory_client.getItemPricing(item_id, -1)
                order.lineitems[0].transfer_price = item_pricing.transferPrice
                order.lineitems[0].nlc = item_pricing.nlc
            except:
                print "Not able to get transfer price. Skipping"
            
            catalog_client = CatalogClient().get_client() 
            voucherAmount = catalog_client.getVoucherAmount(item_id, VoucherType.SPICEDECK_MOBILE)
            if voucherAmount:
                __create_recharge_voucher_tracker(order, voucherAmount, VoucherType.SPICEDECK_MOBILE)
            #Note 2 Buy back Offer
            if item_id in (7974, 7558) and transaction.coupon_code is not None and transaction.coupon_code.lower() == "note2buyback":    
                order.status = OrderStatus.COD_VERIFICATION_PENDING
                order.statusDescription = "Note 2 Buy Back Offer - Approval Pending from CRM Team"
                order.otg = False
                                
    elif new_status == TransactionStatus.COD_IN_PROCESS:
        for order in transaction.orders:
            order.status = OrderStatus.COD_VERIFICATION_PENDING
            order.statusDescription = "Verification Pending"
            order.cod = True
            order.orderType = orderType
            #After we got payment success, we will set logistics info also 
            logistics_client = LogisticsClient().get_client()
            #FIXME line item is only one now. If multiple will come, need to fix.
            item_id = order.lineitems[0].item_id
            if order.pickupStoreId:
                # Need to send prepaid awb number for store pick up orders
                logistics_info = logistics_client.getLogisticsEstimation(item_id, order.customer_pincode, DeliveryType.COD)
                #//Start:- Added by Manish Sharma for FedEx Integration- Shipment Creation on 31-Jul-2013
                #prepaid_logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.PREPAID, pickUp)
                #logistics_info.airway_billno = prepaid_logistics_info.airway_billno
                #logistics_info.providerId = prepaid_logistics_info.providerId
                #End:- Added by Manish Sharma for FedEx Integration- Shipment Creation on 31-Jul-2013
                # Will not provide OTG in pickup store
                order.otg = False
            else:
                logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.COD, pickUp)
                order.otg = logistics_info.otgAvailable
            
               
            #current_time = datetime.datetime.now()
            #logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
            #logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
            #logistics_info.deliveryDelay = adjust_delivery_time(current_time, (logistics_info.shippingTime+logistics_info.deliveryDelay))
            
                
            order.warehouse_id = logistics_info.warehouseId
            order.fulfilmentWarehouseId = logistics_info.fulfilmentWarehouseId
            order.logistics_provider_id = 45
            #Start:- Added by Manish Sharma for FedEx Integration- Shipment Creation on 31-Jul-2013
            #order.airwaybill_no = logistics_info.airway_billno
            #order.tracking_id = order.airwaybill_no
            #End:- Added by Manish Sharma for FedEx Integration- Shipment Creation on 31-Jul-2013
            order.courier_delivery_time = to_py_date(deliveryTimeStamp).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
            order.expected_shipping_time = to_py_date(shipTimestamp).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
            order.promised_shipping_time = order.expected_shipping_time
            order.expected_delivery_time = to_py_date(deliveryTimeStamp).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
            order.promised_delivery_time = order.expected_delivery_time
            
            inventory_client = InventoryClient().get_client()
            if order.productCondition != ProductCondition.BAD:
                inventory_client.reserveItemInWarehouse(item_id, logistics_info.fulfilmentWarehouseId, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), order.lineitems[0].quantity)

            try:
                item_pricing = inventory_client.getItemPricing(item_id, -1)
                order.lineitems[0].transfer_price = item_pricing.transferPrice
                order.lineitems[0].nlc = item_pricing.nlc        
            except:
                print "Not able to get transfer price. Skipping"
            
            catalog_client = CatalogClient().get_client()
            voucherAmount = catalog_client.getVoucherAmount(item_id, VoucherType.SPICEDECK_MOBILE)
            if voucherAmount:
                __create_recharge_voucher_tracker(order, voucherAmount, VoucherType.SPICEDECK_MOBILE)

                                    
    session.commit()

    if new_status in (TransactionStatus.AUTHORIZED, TransactionStatus.FLAGGED, TransactionStatus.COD_IN_PROCESS):
        try:
            if order.source == OrderSource.WEBSITE:    
                if new_status == TransactionStatus.COD_IN_PROCESS:
                    transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
                    transaction_requiring_extra_processing.category = 'COD_VERIFICATION'
                    transaction_requiring_extra_processing.transaction_id = transaction_id
                    session.commit()
                elif new_status == TransactionStatus.FLAGGED:
                    transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
                    transaction_requiring_extra_processing.category = 'PAYMENT_FLAGGED'
                    transaction_requiring_extra_processing.transaction_id = transaction_id
                    session.commit()
            elif order.source == OrderSource.STORE:
                order.otg = False
                session.commit()
                catalog_client = CatalogClient().get_client() 
                sourcePricing = catalog_client.getStorePricing(order.lineitems[0].item_id)
                if order.total_amount >= sourcePricing.minPrice:
                    order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
                    order.statusDescription = "Order Accepted"
                    session.commit()
                    sod = StoreOrderDetail.get_by(orderId = order.id)
                    __push_store_collection_to_hotspot(order, "advance", sod.cashAmount, sod.cardAmount)
                    
                else: 
                    order.status = OrderStatus.COD_VERIFICATION_PENDING
                    order.statusDescription = "Approval Pending"
                    content = "<html><body>"
                    content += "<h4>Order Id:  " + str(order.id) + "</h4>"
                    content += "<h4>Product:  " + str(order.lineitems[0]) + "</h4>"
                    content += "<h4>Min Price: " +  str(sourcePricing.minPrice) + "</h4>"
                    content += "<h4>Recommended Price: " +  str(sourcePricing.recommendedPrice) + "</h4>"
                    content += "<h2>Approval Requested for Price: " +  str(order.total_amount) + "</h2>"
                    content += "<h2>Note 1: If this discount price is approved then reply 'Approved' otherwise 'Not Approved' without changing anythig in the Mail.</h2>"  
                    content += "<h2>Note 2: For Quick approval using browser <a href='http://support.shop2020.in:8080/Support/price-approval?xszbg=" + base64.b64encode(str(order.id))  + "'>Click Here</a></h2>"
                    content += "<br><br></body></html>"
                    store = get_hotspot_store(order.storeId, "")
                    helper_client = HelperClient().get_client()
                    helper_client.saveUserEmailForSending(store.approvalEmail.split(';'), SaholicHelpEmailId, "Price approval requested for " + str(order.lineitems[0]) + " order Id " + str(order.id) + " in store " + store.hotspotId , content, str(transaction_id), "PriceApproval", [], [], order.source)
                    session.commit()
            else:
                order.otg = False
                session.commit()
        except Exception as e:
            print "Error inserting transaction Id: " + str(transaction_id) + " due to " + str(e)
            
    return True

def update_amazon_fba_order_returns(fbaOrderReturns):
    for order_return in fbaOrderReturns:
        orderReturn = AmazonFbaOrderReturns.query.filter(AmazonFbaOrderReturns.amazonOrderId==order_return.amazonOrderId).filter(AmazonFbaOrderReturns.insertionTimestamp == to_py_date(order_return.insertionTimestamp)).filter(AmazonFbaOrderReturns.sku==order_return.sku).first()
        if orderReturn:
            orderReturn.sellableReturnQuantity = order_return.sellableReturnQuantity
            orderReturn.nonSellableReturnQuantity = order_return.nonSellableReturnQuantity
            session.commit()
        else:
            orderReturn = AmazonFbaOrderReturns()
            orderReturn.amazonOrderId = order_return.amazonOrderId
            orderReturn.insertionTimestamp = to_py_date(order_return.insertionTimestamp)
            orderReturn.sku = order_return.sku
            orderReturn.creationTimestamp = to_py_date(order_return.creationTimestamp)
            orderReturn.shippedQuantity = order_return.shippedQuantity
            orderReturn.sellableReturnQuantity = order_return.sellableReturnQuantity
            orderReturn.nonSellableReturnQuantity = order_return.nonSellableReturnQuantity
            session.commit()
        
def get_all_amazon_fba_order_returns(insertionTimestamp):
    returns = AmazonFbaOrderReturns.query.filter(AmazonFbaOrderReturns.insertionTimestamp==to_py_date(insertionTimestamp)).all()
    if not returns:
        returns = []
    return returns  

def get_total_sale_returns_fba_skus_curent_time(insertionTimestamp):  
    allFbaSkuDetails = session.query(AmazonFbaOrderReturns.sku, func.sum(AmazonFbaOrderReturns.shippedQuantity), func.sum(AmazonFbaOrderReturns.sellableReturnQuantity), func.sum(AmazonFbaOrderReturns.nonSellableReturnQuantity)).group_by(AmazonFbaOrderReturns.sku).filter(AmazonFbaOrderReturns.insertionTimestamp == to_py_date(insertionTimestamp)).all()
    
    returnAllFbaSkuDetails = {}
    
    for fbaSkuDetail in allFbaSkuDetails:
        saleReturnDetail = {}
        saleReturnDetail['Sale'] = fbaSkuDetail[1]
        saleReturnDetail['SaleableReturn'] = fbaSkuDetail[2]
        saleReturnDetail['NonSaleableReturn'] = fbaSkuDetail[3]
        returnAllFbaSkuDetails[fbaSkuDetail[0]]= saleReturnDetail
    
    return returnAllFbaSkuDetails

def get_amazon_fba_sales_latest_snapshot_for_item_location_wise(item_id,location):
    return AmazonFbaSalesSnapshot.query.filter(AmazonFbaSalesSnapshot.item_id==item_id).filter(AmazonFbaSalesSnapshot.fcLocation==location).filter(AmazonFbaSalesSnapshot.dateOfSale!=datetime.datetime.now().date()).order_by(desc(AmazonFbaSalesSnapshot.dateOfSale)).first()

def get_verification_pending_orders_fk():
    retOrders = Order.query.filter(Order.source==OrderSource.FLIPKART).filter(Order.status==OrderStatus.COD_VERIFICATION_PENDING).all()
    retFkOrders = []
    order_ids = []
    if not retOrders:
        return retFkOrders
    else:
        for order in retOrders:
            order_ids.append(order.id)
        retFkOrders = FlipkartOrder.query.filter(FlipkartOrder.orderId.in_(tuple(order_ids)))
        if not retFkOrders:
            retFkOrders = []
    return retFkOrders

def add_invoice_details_to_orders(transactionId, customerId):
    orders = Order.query.filter_by(transaction_id=transactionId, customer_id=customerId).all()
    if not orders:
        raise TransactionServiceException(101, "No order for the transaction Unable to Print Invoice")
    else:
        invoiceNo = get_next_invoice_number(orders[0].orderType)
        for order in orders:
            order.invoice_number = invoiceNo
            session.commit()
               
def get_fa_order_by_fk_order_id(fk_OrderId,fk_OrderItemId):
    faOrder = FlipkartAdvantageOrder.query.filter_by(fkOrderId=fk_OrderId,fkOrderItemId=fk_OrderItemId).first()
    if not faOrder:
        print "Not found order for orderId " + str(fk_OrderId)
        raise TransactionServiceException(108, "no such order")
    
    return faOrder

def flipkart_fa_order_exists(fk_OrderId,fk_OrderItemId):
    faOrder = FlipkartAdvantageOrder.query.filter_by(fkOrderId=fk_OrderId,fkOrderItemId=fk_OrderItemId).first()
    if not faOrder:
        return False
    else:
        return True

def get_all_fa_orders_list(status):
    faOrders = None
    if status == 'all':
        faOrders = FlipkartAdvantageOrder.query.all()
    else:
        faOrders = FlipkartAdvantageOrder.query.filter(FlipkartAdvantageOrder.status==status).all()
    if not faOrders:
        faOrders = []   
    return faOrders

def add_update_fa_orders_bulk(faOrdersList):
    for faOrder in faOrdersList:
        if faOrder.status == 'approved':
            fa_Order = FlipkartAdvantageOrder()
            fa_Order.fkOrderId = faOrder.fkOrderId
            fa_Order.fkOrderItemId = faOrder.fkOrderItemId
            fa_Order.sku = faOrder.sku
            fa_Order.creationTimestamp = to_py_date(faOrder.creationTimestamp)
            fa_Order.customerName = faOrder.customerName
            fa_Order.customerAddress = faOrder.customerAddress
            fa_Order.pincode = faOrder.pincode
            fa_Order.customerCity = faOrder.customerCity
            fa_Order.customerState = faOrder.customerState
            fa_Order.customerPhone = faOrder.customerPhone
            fa_Order.status = faOrder.status
            fa_Order.quantity = faOrder.quantity
            fa_Order.totalPrice = faOrder.totalPrice
            fa_Order.listPrice = faOrder.listPrice
            fa_Order.modifiedDate = to_py_date(faOrder.modifiedDate)
            fa_Order.listingId = faOrder.listingId
            fa_Order.cancelReason = faOrder.cancelReason
            fa_Order.returnReason = faOrder.returnReason
            fa_Order.freebieItemId = faOrder.freebieItemId
            fa_Order.productTitle = faOrder.productTitle
        else:
            fa_Order = FlipkartAdvantageOrder.query.filter_by(fkOrderId=faOrder.fkOrderId,fkOrderItemId=faOrder.fkOrderItemId).first()
            if fa_Order:
                if fa_Order.status !=faOrder.status:
                    fa_Order.status = faOrder.status
                    fa_Order.cancelReason = faOrder.cancelReason
                    fa_Order.returnReason = faOrder.returnReason
                    fa_Order.modifiedDate = to_py_date(faOrder.modifiedDate)
            else:
                fa_Order = FlipkartAdvantageOrder()
                fa_Order.fkOrderId = faOrder.fkOrderId
                fa_Order.fkOrderItemId = faOrder.fkOrderItemId
                fa_Order.sku = faOrder.sku
                fa_Order.creationTimestamp = to_py_date(faOrder.creationTimestamp)
                fa_Order.customerName = faOrder.customerName
                fa_Order.customerAddress = faOrder.customerAddress
                fa_Order.pincode = faOrder.pincode
                fa_Order.customerCity = faOrder.customerCity
                fa_Order.customerState = faOrder.customerState
                fa_Order.customerPhone = faOrder.customerPhone
                fa_Order.status = faOrder.status
                fa_Order.quantity = faOrder.quantity
                fa_Order.totalPrice = faOrder.totalPrice
                fa_Order.listPrice = faOrder.listPrice
                fa_Order.modifiedDate = to_py_date(faOrder.modifiedDate)
                fa_Order.listingId = faOrder.listingId
                fa_Order.cancelReason = faOrder.cancelReason
                fa_Order.returnReason = faOrder.returnReason
                fa_Order.freebieItemId = faOrder.freebieItemId
                fa_Order.productTitle = faOrder.productTitle
    session.commit()

def get_recharge_order_status(rechargeOrderId, final):
    d_rechargeOrder = get_recharge_order(rechargeOrderId)
    if d_rechargeOrder.status == RechargeOrderStatus.RECHARGE_UNKNOWN:
        try:
            status, description = RechargeService.checkTransactionStatus('', str(rechargeOrderId))
            print status, description
            if status:
                update_recharge_order_status(rechargeOrderId, RechargeOrderStatus.RECHARGE_SUCCESSFUL)
            else:
                update_recharge_order_status(rechargeOrderId, RechargeOrderStatus.RECHARGE_FAILED)
        except:
            if final:
                d_rechargeOrder.status = RechargeOrderStatus.PAYMENT_SUCCESSFUL
                d_rechargeOrder.responseTimestamp = datetime.datetime.now()
                session.commit()
        finally:
            return get_recharge_order(rechargeOrderId)
    else:
        return d_rechargeOrder

def get_recharge_transaction_status(rechargeTransactionId, final):
    d_rechargeTransaction = get_recharge_transaction(rechargeTransactionId)
    if d_rechargeTransaction.status == RechargeOrderStatus.RECHARGE_IN_PROCESS:
        try:
            status, description = RechargeService.checkTransactionStatus('', str(rechargeTransactionId))
            d_rechargeTransaction.description = description
            print status, description
            if status:
                update_recharge_transaction_status(rechargeTransactionId, RechargeOrderStatus.RECHARGE_SUCCESSFUL)
            else:
                update_recharge_transaction_status(rechargeTransactionId, RechargeOrderStatus.RECHARGE_FAILED)
        except:
            if final:
                d_rechargeTransaction.status = RechargeOrderStatus.RECHARGE_UNKNOWN
                d_rechargeTransaction.responseTime = datetime.datetime.now()
                session.commit()
        finally:
            return get_recharge_transaction(rechargeTransactionId)
    else:
        return d_rechargeTransaction
    
def accept_package_orders(orders):
    totalWeight = 0.0
    totalOrdersAmount = 0.0
    ordersList = get_order_list(orders)
    totalOrderQuantity = 0
    
    for orderObj in ordersList:
        totalOrderQuantity = totalOrderQuantity + long(orderObj.lineitems[0].quantity)
        
    print orders
    
    updatedOrders = logisticsProviderFinalCheck(ordersList)
        
    user_client = UserClient().get_client()
    isPrivateDealUser = user_client.isPrivateDealUser(updatedOrders[0].customer_id)
    privateDealUser = None
    if isPrivateDealUser:
        privateDealUser = user_client.getPrivateDealUser(updatedOrders[0].customer_id)
    logistics_client = LogisticsClient().get_client()
    provider = logistics_client.getProvider(updatedOrders[0].logistics_provider_id)
    providerLimits = logistics_client.getProviderLimitDetailsForPincode(provider.id, updatedOrders[0].customer_pincode)
    print 'Accpet Order Process Started'
    
    '''
    Check For Pre Conditions
    '''
    for order in updatedOrders:
        if order.total_weight is None or order.total_weight ==0:
            raise TransactionServiceException(208, "Weight Value Not Defined for order:- "+ str(order.id)+". Contact Category Team.")
        totalWeight = totalWeight + order.total_weight
        #if order.cod:
        totalOrdersAmount = totalOrdersAmount + order.total_amount
        
        if order.cod:
            if isPrivateDealUser and privateDealUser.bulkShipmentAmountLimit:
                if totalOrdersAmount > privateDealUser.bulkShipmentAmountLimit:
                    raise TransactionServiceException(208, "Private Deal Counter Group Shipment Amount Limit Violated i.e. "+ str(privateDealUser.bulkShipmentAmountLimit))
            else:
                if totalOrdersAmount > 50000:
                    raise TransactionServiceException(208, "Private Deal Counter Group Shipment Amount Limit Violated i.e. "+ str(50000))
        
        if totalWeight > provider.bundleWeightLimit:
            raise TransactionServiceException(210, "Logistics Partner "+provider.name+ " Group Shipment Weight Limit Violated i.e. "+str(provider.bundleWeightLimit))
        
        if order.cod:
            if totalOrdersAmount > provider.maxCodLimit:
                raise TransactionServiceException(210, "Logistics Partner "+provider.name+ " Max Cod Amount Collection Limit Violated i.e. "+ str(provider.maxCodLimit))
        
        if order.logistics_provider_id not in [4, 48] :
        
            providerCodLimit = float(providerLimits.get("providerCodLimit"))
            providerPrepaidLimit = float(providerLimits.get("providerPrepaidLimit"))
            
            if order.cod:
                if totalOrdersAmount > providerCodLimit:
                    raise TransactionServiceException(212, "Provider Maximum COD Limit Violated for Customer Pincode i.e. "+str(providerCodLimit))
            
            if not order.cod:
                if totalOrdersAmount > providerPrepaidLimit:
                    raise TransactionServiceException(212, "Logistics Partner "+provider.name+ " Max Prepaid Amount Limit Violated Rs." +str(providerPrepaidLimit))
    
    for order in updatedOrders:
        if order.status in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT, OrderStatus.CAPTURE_IN_PROCESS]:
            if not order.cod:
                if order.transaction.status == TransactionStatus.AUTHORIZED and order.transaction.payment_option != capitalFloatPayMethod:
                    __capture_txn(order.transaction_id)
                    change_transaction_status(order.transaction_id, TransactionStatus.IN_PROCESS, "Payment received", PickUpType.COURIER, order.orderType, order.source)
                    
        break
    ''' Change IP using Config Client and Handle Exception '''
    try:
        inventoryDbHost = str(ConfigClient().get_property("inventory_service_db_hostname"))
        conn = getDbConnection(inventoryDbHost,"root", "shop2020", "inventory")
    except:
        raise TransactionServiceException(212, "Unable to connect to Inventory System")
    #conn = getDbConnection("localhost","root","shop2020","inventory")
    print 'Connected to Inventory DB'
    
    for order in updatedOrders:
        if order.status in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT, OrderStatus.CAPTURE_IN_PROCESS]:
            order.status = OrderStatus.ACCEPTED
            order.statusDescription = "Order Accepted"
            order.accepted_timestamp = datetime.datetime.now()
            if order.source == 6 or order.source == 7 or order.source == 8:
                if order.source == 8 and order.cod:
                    order.cod = False
                continue
            
            itemId = order.lineitems[0].item_id
            
            selectSql = "select warehouse_id, availability, reserved, held from currentinventorysnapshot where item_id = %d"%(itemId)             
            avalibilityResult = None
                        
            try:
                cursor = conn.cursor()
                cursor.execute(selectSql)
                avalibilityResult = cursor.fetchall()
            except Exception as e:
                print "Error: unable to fetch data"
            
            wrongFulFillmentWh = False
            
            itemAvailibility = ItemInventory()
            availability = {}
            reserved = {}
            held = {}
            
            if avalibilityResult is not None:
                for avalibilityRecord in avalibilityResult:
                    if order.fulfilmentWarehouseId == avalibilityRecord[0]:
                        wrongFulFillmentWh = False
                        break
                    else:
                        wrongFulFillmentWh = True
                        availability[avalibilityRecord[0]] = avalibilityRecord[1]
                        reserved[avalibilityRecord[0]] = avalibilityRecord[2]
                        held[avalibilityRecord[0]] = avalibilityRecord[3]
                        continue
            
            itemAvailibility.id = itemId
            itemAvailibility.availability = availability
            itemAvailibility.reserved = reserved
            itemAvailibility.held = held
                    
            warehousesResult = None
            if wrongFulFillmentWh:
                selectSql = "SELECT * from warehouse where warehouseType ='OURS' and inventoryType='GOOD' and billingWarehouseId=%d"%(order.warehouse_id)
                try:
                    cursor = conn.cursor()
                    cursor.execute(selectSql)
                    warehousesResult = cursor.fetchall()
                except Exception as e:
                    print "Error: unable to fetch data 1"
            
            newFulfillWh = None
            if warehousesResult is not None:
                for warehouse in warehousesResult:
                    if itemAvailibility.availability.has_key(warehouse[0]) and itemAvailibility.availability[warehouse[0]] >= itemAvailibility.reserved[warehouse[0]] + order.lineitems[0].quantity:
                        sql_update = "update currentinventorysnapshot set reserved = reserved -%d where item_id = %d and warehouse_id = %d"%(int(order.lineitems[0].quantity),itemId,order.fulfilmentWarehouseId)
                        sql_delete = "delete from currentreservationsnapshot where order_id = %d and item_id = %d and warehouse_id = %d and source_id= %d"%(order.id,itemId,order.fulfilmentWarehouseId,sourceId)
                        
                        print sql_update
                        try:
                            cursor = conn.cursor()
                            cursor.execute(sql_update)
                            cursor.execute(sql_delete)
                        except:
                            conn.rollback()
                            
                        sql_get = "select * from currentinventorysnapshot where item_id=%d and warehouse_id=%d"%(itemId,warehouse[0])
                        
                        try:
                            cursor = conn.cursor()
                            cursor.execute(sql_get)
                            invsnapshot = cursor.fetchone()
                            if invsnapshot is not None:
                                sql_update = "update currentinventorysnapshot set reserved = reserved +%d where item_id = %d and warehouse_id = %d"%(int(order.lineitems[0].quantity),itemId,warehouse[0])
                                cursor.execute(sql_update)
                            else:
                                sql_insert = "insert into currentinventorysnapshot values(%d,%d,%d,%d,%d)"%(itemId,warehouse[0],0,int(order.lineitems[0].quantity),0)
                                cursor.execute(sql_insert)
                                sql_res_insert = "insert into currentreservationsnapshot values(%d,%d,%d,%d,'%s','%s',%d)"%(itemId,warehouse[0],sourceId,order.id,str(order.created_timestamp), str(order.promised_shipping_time), int(order.lineitems[0].quantity))
                                cursor.execute(sql_res_insert)
                        except:
                            conn.rollback()
                        order.fulfilmentWarehouseId = warehouse[0]
                        newFulfillWh = warehouse[0]
                    break        
            
            orderFulfillmentWarehouse = None
            try:
                cursor = conn.cursor()
                if wrongFulFillmentWh:
                    selectSql = "select id, displayName, vendor_id from warehouse where id = %d"%(newFulfillWh)
                    print "SelectSql", selectSql 
                    cursor.execute(selectSql)
                else:
                    selectSql = "select id, displayName, vendor_id from warehouse where id = %d"%(order.fulfilmentWarehouseId)
                    cursor.execute(selectSql)
                orderFulfillmentWarehouse = cursor.fetchone()
            except Exception as e:
                print "Error: unable to fetch data 2"
            
            
            if order.productCondition != ProductCondition.BAD:
                if orderFulfillmentWarehouse is not None:
                    itemPricing = None
                    selectSql = "select * from vendoritempricing where item_id = %d and vendor_id = %d"%(itemId,orderFulfillmentWarehouse[2])
                    try:
                        cursor = conn.cursor()
                        cursor.execute(selectSql)
                        itemPricing = cursor.fetchone()
                    except Exception as e:
                        print "Error: unable to fetch data 3"
                        
                    if itemPricing is None:
                        raise TransactionServiceException(214, "Vendor Item Pricing Missing For Item Id "+str(itemId)+ " and Fulfillment Warehouse "+ str(orderFulfillmentWarehouse[1]))
                    else:
                        order.lineitems[0].transfer_price = itemPricing[4]
                        order.lineitems[0].nlc = itemPricing[5]
        else:
            conn.close()
            raise TransactionServiceException(216, "Order in Unacceptable Status.. "+str(order.id))
    try:
        conn.commit()
    except:
        conn.close()
        
    conn.close()
    
    txnSeqRequired = 0
    txnShipSeq = TransactionShipmentSequence.query.filter(TransactionShipmentSequence.transactionId==updatedOrders[0].transaction_id).order_by(desc(TransactionShipmentSequence.id)).first()
    if txnShipSeq is None:
        txnShipSeq = TransactionShipmentSequence()
        txnShipSeq.transactionId = updatedOrders[0].transaction_id
        txnShipSeq.createdTimestamp = datetime.datetime.now()
        txnShipSeq.sequence = 1
        txnSeqRequired = 1
    else:
        txnShipSeqNew = TransactionShipmentSequence()
        txnShipSeqNew.transactionId = txnShipSeq.transactionId
        txnShipSeqNew.createdTimestamp = datetime.datetime.now()
        txnShipSeqNew.sequence = txnShipSeq.sequence + 1
        txnSeqRequired = txnShipSeq.sequence + 1    
    
    for order in updatedOrders:
        order.logisticsTransactionId = str(order.transaction_id)+"-"+str(txnSeqRequired)
                
    session.commit()
    return True

def get_group_orders_by_logistics_txn_id(logisticsTxnId):
    orders = Order.query.filter(Order.logisticsTransactionId==logisticsTxnId).all()
    if not orders:
        orders = []   
    return orders

def add_billing_details_for_groupped_orders(orderIds, invoice_number, itemNumbersMap, serialNumbersMap, freebieWarehouseIdMap, billed_by, jacketNumber, billingType, authorize, invoiceType):
#    print 'orderIds',orderIds
#    print 'invoice_number', invoice_number
#    print 'itemNumbersMap', itemNumbersMap
#    print 'serialNumbersMap', serialNumbersMap
#    print 'freebieWarehouseIdMap', freebieWarehouseIdMap
#    print 'billed_by', billed_by
#    print 'jacketNumber', jacketNumber
#    print 'billingType', billingType
#    print 'authorize', authorize 
#    print 'invoiceType', invoiceType
    ''' Check for Biller'''
    if billed_by is None or billed_by.strip() == "":
        raise TransactionServiceException(110, "Invalid Biller")
    
    ordersList = []
    
    ''' Check for Order'''
    for orderId in orderIds:
        order = Order.get_by(id=orderId)
        if not order:
            raise TransactionServiceException(301, "No order found for the given order id" + str(orderId))
        else:
            ordersList.append(order)
            
    singleOrder = ordersList[0]
    hsnCode = singleOrder.lineitems[0].hsnCode
    if singleOrder.logisticsTransactionId is not None:
        grouppedOrders = get_group_orders_by_logistics_txn_id(singleOrder.logisticsTransactionId)
        
        errorString = ""
        missingString = "Billing Details Missing for following Orders:- "
        
        for ordObj in grouppedOrders:
            if ordObj.id not in orderIds and ordObj.status==OrderStatus.ACCEPTED :
                errorString = errorString + "Order Id:- " +str(ordObj.id) +" "
        
        if len(errorString)>0:
            raise TransactionServiceException(301, missingString + errorString)
                
    #inventoryDbConnection = getDbConnection("192.168.190.114","root", "shop2020", "inventory") 
    try:
        warehouseDbConnection = getDbConnection("192.168.190.114","root", "shop2020", "warehouse")
    except:
        raise TransactionServiceException(302, "Unable to connect to Warehouse System")
    inventory_client = InventoryClient().get_client()
    warehouse_client = WarehouseClient().get_client()
    catalog_client = CatalogClient().get_client()
    lineItemSize = 0
    individualInvoice = True
    warehouse = inventory_client.getWarehouse(ordersList[0].warehouse_id)
    whState = fetchStateMaster().get(warehouse.stateId)
     
    taxType = __getOrderTaxType(ordersList[0], whState.stateName)
    
    if not catalog_client.isAlive():
        catalog_client = CatalogClient().get_client()
    
    itemIds = []
    
    for order in ordersList:
        itemIds.append(order.lineitems[0].item_id)
        
    if taxType == TaxType.IGST:
        gstRatesMap = catalog_client.getStateTaxRate(itemIds, -1)
    else:
        gstRatesMap = catalog_client.getStateTaxRate(itemIds, whState.id)
    
        
    orderscansMap = {}
    orderInventoryItemMap = {}
    orderFulfilmentWarehouseMap = {}
    nonSerializedUsedInventoryMap = {}
    for order in ordersList:
        scanList = []
        inventoryItemList = []
        order.taxType = taxType
        if jacketNumber is None or jacketNumber <= 0:
            if order.source == OrderSource.EBAY or order.source == OrderSource.SNAPDEAL or order.source == OrderSource.FLIPKART:
                print "Skipping Jacket Number field for OrderId " + str(orderId)
            else:
                raise TransactionServiceException(303, "Invalid jacket number")
        
        '''
        First checking whether freebie can be billed or not otherwise wont proceed.
        '''
        if order.freebieItemId:
            if billingType == BillingType.OURS or billingType == BillingType.OURS_EXTERNAL:
                freebieWarehouseId = freebieWarehouseIdMap.get(order.id)[0]
                if freebieWarehouseId:
                    if not inventory_client.isAlive():
                        inventory_client = InventoryClient().get_client()
                    warehouse = inventory_client.getWarehouse(freebieWarehouseId)
                    if warehouse.warehouseType!= WarehouseType.OURS or warehouse.inventoryType != InventoryType.GOOD:
                        raise TransactionServiceException(304,'Billing of Freebie is only allowed from OURS_GOOD warehouses. Order Id:- '+str(order.id))
                    if not warehouse_client.isAlive():
                        warehouse_client = WarehouseClient().get_client()
                    isItemAvailable = warehouse_client.isItemAvailableForSale(order.freebieItemId, "", freebieWarehouseId)
                    if isItemAvailable == False:
                        raise TransactionServiceException(305,'No Freebie Item available. Order Id- '+str(order.id))
                else:
                    raise TransactionServiceException(306,'No warehouseId provided for billing of freebie. Order Id-'+str(order.id))
        
        lineitem = order.lineitems[0]
        item_id = lineitem.item_id

        '''
        Checking if order is a freebie split-order and if the order is cod and if the original order is marked as delivered.
        '''
        freebie_order_info_text = "Freebie Order for Order ID"
        if lineitem.extra_info and freebie_order_info_text in lineitem.extra_info:
            canbillOrder = __isFreebieOrderBillable(order)
            if canbillOrder == False:
                raise TransactionServiceException(307,'Parent order for this is COD and is still undelivered')
        
        if not catalog_client.isAlive():
            catalog_client = CatalogClient().get_client()
        item = catalog_client.getItem(item_id)
        
        if ItemType.SERIALIZED == item.type and lineitem.quantity>2:
            individualInvoice=False
            
        if order.orderType== OrderType.B2B:
            individualInvoice=False
        
        lineItemSize = lineItemSize+1
        
        if order.status == OrderStatus.ACCEPTED:
            if order.source == 6:
                order.jacket_number = "600"+str(orderId)
            elif order.source == 7:
                order.jacket_number = "700"+str(orderId)
            elif order.source == 8:
                order.jacket_number = "800"+str(orderId)
            else:
                order.jacket_number = jacketNumber
            
            itemNumbers = itemNumbersMap.get(order.id)
            
            lineitem.item_number = itemNumbers[0]
            serialNumbers = serialNumbersMap.get(order.id)
            
            if serialNumbers:
                if lineitem.serial_number:
                    serialNumbers.insert(0, lineitem.serial_number)
                    
                #dupSerials =  [ser for ser, count in collections.Counter(serialNumbers).items() if count > 1]
                dupSerials = set([x for x in serialNumbers if serialNumbers.count(x) > 1])
                
                if dupSerials:
                    logging.info("*********Duplicates are being scanned***********\n" + ",".join(dupSerials))
                    raise TransactionServiceException(110, 'Found repeated serialNumbers in: ' + ",".join(dupSerials))
                lineitem.serial_number = ','.join(serialNumbers)
            for serialNumber in serialNumbers:
                lineItemImei = Line_Item_Imei()
                lineItemImei.serial_number = serialNumber
                lineItemImei.line_item_id = lineitem.id
            order.status = OrderStatus.BILLED
            order.statusDescription = "Order Billed"
            order.billing_timestamp = datetime.datetime.now()
            order.billed_by = billed_by
            
            # Letting the billing process fail in cases where we are unable to 
            # fill in transfer price
            
            try:
                if not inventory_client.isAlive():
                    inventory_client = InventoryClient().get_client()
                warehouse = inventory_client.getWarehouse(order.fulfilmentWarehouseId)
                if order.productCondition != ProductCondition.BAD:
                    item_pricing = inventory_client.getItemPricing(item_id, warehouse.vendor.id)
                    lineitem.transfer_price = item_pricing.transferPrice
                    lineitem.nlc = item_pricing.nlc
                #Now onwards vatRates are obsolete, we will be using fields igst, sgst and cgst
                gstRates = gstRatesMap.get(lineitem.item_id)
                lineitem.igstRate = gstRates.igstRate
                lineitem.cgstRate = gstRates.cgstRate
                lineitem.sgstRate = gstRates.sgstRate
                    
                    
                order.vendorId = warehouse.vendor.id
            except InventoryServiceException as e:
                print sys.exc_info()[0]
                print e.message
                if warehouseDbConnection.open:
                    warehouseDbConnection.close()
                raise TransactionServiceException(110, 'Transfer price missing for itemId: ' + str(item_id) + ' and vendor: ' + str(warehouse.vendor.id))
            
#            if order.orderType == OrderType.B2B:
#                tinNumber = Attribute.query.filter(Attribute.orderId == order.id).filter(Attribute.name == "tinNumber").first()
#                if tinNumber is None:
#                    if warehouseDbConnection.open:
#                        warehouseDbConnection.close()
#                    raise TransactionServiceException(308, "Tin Number is Missing for B2B Order. Please contact engineering Team" + str(orderId))
#                if newTaxType != 0:
#                    order.orderType = OrderType.B2C
            
            if billingType == BillingType.OURS:
                if order.productCondition == ProductCondition.GOOD:
                    # Fetching GOOD w/h corresponding to the virtual one here
                    if not warehouse.billingWarehouseId:
                        if not inventory_client.isAlive():
                            inventory_client = InventoryClient().get_client()
                        warehouse = inventory_client.getWarehouses(None, InventoryType.GOOD, warehouse.vendor.id, order.warehouse_id, 0)[0]
                    
                    whCursor = warehouseDbConnection.cursor()
                    ''' Good Serialized'''
                    if ItemType.SERIALIZED == item.type:
                        for serialNumber in serialNumbers: 
                            serialNumber = serialNumber.strip()
                            if len(serialNumber) ==0:
                                raise TransactionServiceException(315, "Serial Number not Specified. Order Id:- "+str(order.id))
                            invItemSql = "select i.id, i.itemId, i.itemNumber, i.serialNumber, i.initialQuantity, i.currentQuantity, i.purchaseId, i.purchaseReturnId, i.currentWarehouseId, i.lastScanType, i.transferStatus, physicalWarehouseId, po.supplierId, l.unitPrice, l.nlc from inventoryItem i join purchase p on i.purchaseId = p.id join purchaseorder po on p.purchaseOrder_id = po.id join lineitem l on (i.itemId = l.itemId and po.id =l.purchaseOrder_id) where i.serialNumber ='%s' order by i.id desc limit 1"%(serialNumber)
                            whCursor.execute(invItemSql)
                            invItem = whCursor.fetchone()
                            if invItem is None:
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(309, "No Item in the inventory with this serial number " + serialNumber+" and Order Id:- "+str(order.id))
                            if item.id!=invItem[1]:
                                if not catalog_client.isAlive():
                                    catalog_client = CatalogClient().get_client()
                                sritem = catalog_client.getItem(invItem[1])
                                scanItemString = " ".join([str(sritem.brand), str(sritem.modelName), str(sritem.modelNumber), str(sritem.color)])
                                lineItemString = " ".join([str(lineitem.brand), str(lineitem.model_name), str(lineitem.model_number), str(lineitem.color)])
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(310, "Trying to scan " + scanItemString + " instead of " + lineItemString+" Order Id- "+str(order.id))
                            if order.warehouse_id!= invItem[11]:
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(311, "No Item residing in Billing Warehouse with this serial number " + serialNumber+" and Order Id:- "+str(order.id))
                            if warehouse.id!= invItem[8]:
                                warehouseDbConnection.rollback()
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(311, "No Item residing in Billing Warehouse with this serial number " + serialNumber+" in the vendor warehouse Id"+str(order.fulfilmentWarehouseId)+"and Order Id:- "+str(order.id))
                            if invItem[10] is not None and invItem[10]=='IN_TRANSIT':
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(312, "Trying to Scan In-Transit Inventory " + serialNumber+" and Order Id:- "+str(order.id))
                            if invItem[9]=='MARKED_BAD' or invItem[9]=='DOA_IN' or invItem[9]=='DOA_OUT' or invItem[9]=='DOA_REJECTED' or invItem[9]=='SALE_RET_UNUSABLE' or invItem[9]=='BAD_SALE':
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(313, "Trying to Scan Bad Inventory Serial Number:- " + serialNumber+" and Order Id:- "+str(order.id))
                            scanSql = "select * from scanNew where inventoryItemId =%d order by id"%(invItem[0])
                            whCursor.execute(scanSql)
                            scans = whCursor.fetchall()
                            if scans is None:
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(314, "Item with Serial Number:- " + serialNumber+" never scanned in the system")
                            lastScan = scans[len(scans)-1]
                            if lastScan[3]=='SALE':
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(315, "Inventory Item with serial number- "+ serialNumber+" already scanned against order id- "+str(lastScan[6]))
                            if warehouse.id!=invItem[8]:
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(316, "Inventory Item scanning from vendor warehouse "+str(invItem[8])+" instead of order fulfillment warehouse "+str(warehouse.id)+" order id- "+str(order.id))
                                
                            current_time = datetime.datetime.now()
                            insertScanSql = "insert into scanNew(inventoryItemId, warehouseId, type, scannedAt, quantity, orderId) values(%d,%d,'%s','%s',%d,%d)"%(invItem[0],warehouse.id,'SALE',current_time.strftime('%Y-%m-%d %H:%M:%S'),1,order.id)
                            scanList.append(insertScanSql)
                            #whCursor.execute(insertScanSql)
                            updateInvItemSql = "update inventoryItem set currentQuantity = currentQuantity-1, lastScanType='SALE' where id=%d and serialnumber ='%s'"%(invItem[0],serialNumber)
                            inventoryItemList.append(updateInvItemSql)
                            #whCursor.execute(updateInvItemSql)
                            
        
                            lineitem.transfer_price = invItem[13]
                            lineitem.nlc = invItem[14]
                            order.vendorId = invItem[12]
    
                    else:
                        invItemSql = "select i.id, i.itemId, i.itemNumber, i.serialNumber, i.initialQuantity, i.currentQuantity, i.purchaseId, i.purchaseReturnId, i.currentWarehouseId, i.lastScanType, i.transferStatus, physicalWarehouseId, po.supplierId, l.unitPrice, l.nlc from inventoryItem i join purchase p on i.purchaseId = p.id join purchaseorder po on p.purchaseOrder_id = po.id join lineitem l on (i.itemId = l.itemId and po.id =l.purchaseOrder_id) where i.itemId = %d AND i.currentQuantity > 0 AND i.currentWarehouseId = %d AND i.physicalWarehouseId = %d AND i.lastScanType not in ('MARKED_BAD','DOA_IN','DOA_REJECTED','DOA_REPLACED') AND (i.transferStatus is NULL or i.transferStatus != 'IN_TRANSIT')"%(item.id,warehouse.id,order.warehouse_id)
                        whCursor.execute(invItemSql)
                        invItems = whCursor.fetchall()
                        if invItems is None:
                            raise TransactionServiceException(317, "No Item in the inventory with this item number " + itemNumbers[0]+" in Vendor Warehouse Id- "+str(warehouse.id)+" Order Id:- "+str(order.id))
                            if warehouseDbConnection.open:
                                warehouseDbConnection.close()
                        totalCurrentQuantity =0
                        for invItem in invItems:
                            totalCurrentQuantity = totalCurrentQuantity + invItem[5]
                        
                        orderQuantity = lineitem.quantity
                        if totalCurrentQuantity < lineitem.quantity:
                            if warehouseDbConnection.open:
                                warehouseDbConnection.close()
                            raise TransactionServiceException(318, "Unsufficient Quantity with this item number " + itemNumbers[0]+" in Vendor Warehouse Id- "+str(warehouse.id)+" Order Id:- "+str(order.id))
                        
                        usedInventory = 0
                        if nonSerializedUsedInventoryMap.has_key(str(item.id)+"_"+str(warehouse.id)):
                            usedInventory = nonSerializedUsedInventoryMap.get(str(item.id)+"_"+str(warehouse.id))
                            usedInventory = usedInventory+orderQuantity
                            nonSerializedUsedInventoryMap[str(item.id)+"_"+str(warehouse.id)] = usedInventory
                        else:
                            usedInventory = orderQuantity
                            nonSerializedUsedInventoryMap[str(item.id)+"_"+str(warehouse.id)] = usedInventory
                        
                        if usedInventory > totalCurrentQuantity:
                            if warehouseDbConnection.open:
                                warehouseDbConnection.close()
                            raise TransactionServiceException(318, "Unsufficient Quantity with this item number " + itemNumbers[0]+" in Vendor Warehouse Id- "+str(warehouse.id)+" Order Id:- "+str(order.id))
                        
                        for invItem in invItems:
                            scanSql = "select * from scanNew where inventoryItemId =%d order by id"%(invItem[0])
                            whCursor.execute(scanSql)
                            scans = whCursor.fetchall()
                            
                            if scans is None:
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(319, "Item with Item Number:- " + itemNumbers[0]+" never scanned in the system")
                            
                            
                            currentQuantity = invItem[5]
                            scanQuantity = 0
                            if orderQuantity >0:
                                if currentQuantity < orderQuantity:
                                    scanQuantity = currentQuantity
                                    orderQuantity = orderQuantity - currentQuantity
                                elif currentQuantity > orderQuantity:
                                    scanQuantity = orderQuantity
                                    orderQuantity = 0
                                else:
                                    scanQuantity = currentQuantity
                                    orderQuantity = 0
                            else:
                                break
                            
                            current_time = datetime.datetime.now()
                            insertScanSql = "insert into scanNew(inventoryItemId, warehouseId, type, scannedAt, quantity, orderId) values(%d,%d,'%s','%s',%d,%d)"%(invItem[0],warehouse.id,'SALE',current_time.strftime('%Y-%m-%d %H:%M:%S'),scanQuantity,order.id)
                            print insertScanSql
                            scanList.append(insertScanSql)
                            #whCursor.execute(insertScanSql)
                            updateInvItemSql = "update inventoryItem set currentQuantity = currentQuantity-%d, lastScanType='SALE' where id=%d "%(scanQuantity,invItem[0])
                            inventoryItemList.append(updateInvItemSql)
                            #whCursor.execute(updateInvItemSql)
            
                            lineitem.transfer_price = invItem[13]
                            lineitem.nlc = invItem[14]
                            order.vendorId = invItem[12]
                            
                else:
                    ''' Bad Serialized '''
                    if not warehouse.billingWarehouseId:
                        if not inventory_client.isAlive():
                            inventory_client = InventoryClient().get_client()
                        warehouse = inventory_client.getWarehouses(None, InventoryType.GOOD, warehouse.vendor.id, order.warehouse_id, 0)[0]
                    
                    whCursor = warehouseDbConnection.cursor()
                    if ItemType.SERIALIZED == item.type:
                        for serialNumber in serialNumbers:
                            serialNumber = serialNumber.strip()
                            if len(serialNumber) ==0:
                                raise TransactionServiceException(315, "Serial Number not Specified. Order Id:- "+str(order.id)) 
                            invItemSql = "select i.id, i.itemId, i.itemNumber, i.serialNumber, i.initialQuantity, i.currentQuantity, i.purchaseId, i.purchaseReturnId, i.currentWarehouseId, i.lastScanType, i.transferStatus, physicalWarehouseId, po.supplierId, l.unitPrice, l.nlc from inventoryItem i join purchase p on i.purchaseId = p.id join purchaseorder po on p.purchaseOrder_id = po.id join lineitem l on (i.itemId = l.itemId and po.id =l.purchaseOrder_id) where i.serialNumber ='%s' order by i.id desc limit 1"%(serialNumber)
                            whCursor.execute(invItemSql)
                            invItem = whCursor.fetchone()
                            if invItem is None:
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(110, "No Item in the inventory with this serial number " + serialNumber+" and Order Id:- "+str(order.id))
                            if item.id!=invItem[1]:
                                if not catalog_client.isAlive():
                                    catalog_client = CatalogClient().get_client()
                                sritem = catalog_client.getItem(invItem[1])
                                scanItemString = " ".join([str(sritem.brand), str(sritem.modelName), str(sritem.modelNumber), str(sritem.color)])
                                lineItemString = " ".join([str(lineitem.brand), str(lineitem.model_name), str(lineitem.model_number), str(lineitem.color)])
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(110, "Trying to scan " + scanItemString + " instead of " + lineItemString+" Order Id- "+str(order.id))
                            if order.warehouse_id!= invItem[11]:
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(110, "No Item residing in Billing Warehouse with this serial number " + serialNumber+" and Order Id:- "+str(order.id))
                            if order.fulfilmentWarehouseId!= invItem[8]:
                                warehouseDbConnection.rollback()
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(311, "No Item residing in Billing Warehouse with this serial number " + serialNumber+" in the vendor warehouse Id"+str(order.fulfilmentWarehouseId)+"and Order Id:- "+str(order.id))
                            if invItem[10] is not None and invItem[10]=='IN_TRANSIT':
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(110, "Trying to Scan In-Transit Inventory " + serialNumber+" and Order Id:- "+str(order.id))
                            if invItem[9]!='MARKED_BAD' and invItem[9]!='DOA_IN' and invItem[9]!='DOA_REJECTED' and invItem[9]!='SALE_RET_UNUSABLE':
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(110, "Trying to Scan Good Inventory Serial Number:- " + serialNumber+" and Order Id:- "+str(order.id))
                                
                            scanSql = "select * from scanNew where inventoryItemId =%d order by id"%(invItem[0])
                            whCursor.execute(scanSql)
                            scans = whCursor.fetchall()
                            if scans is None:
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(110, "Item with Serial Number:- " + serialNumber+" never scanned in the system")
                            lastScan = scans[len(scans)-1]
                            if lastScan[3]=='BAD_SALE':
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(110, "Inventory Item with serial number- "+ serialNumber+" already scanned against order id- "+str(lastScan[6]))
                            if warehouse.id!=invItem[8]:
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(110, "Inventory Item scanning from vendor warehouse "+str(invItem[8])+" instead of order fulfillment warehouse "+str(warehouse.id)+" order id- "+str(order.id))
                            current_time = datetime.datetime.now()
                            insertScanSql = "insert into scanNew(inventoryItemId, warehouseId, type, scannedAt, quantity, orderId) values(%d,%d,'%s','%s',%d,%d)"%(invItem[0],warehouse.id,'BAD_SALE',current_time.strftime('%Y-%m-%d %H:%M:%S'),1,order.id)
                            scanList.append(insertScanSql)
                            #whCursor.execute(insertScanSql)
                            updateInvItemSql = "update inventoryItem set currentQuantity = currentQuantity-1, lastScanType='BAD_SALE' where id=%d and serialnumber ='%s'"%(invItem[0],serialNumber)
                            inventoryItemList.append(updateInvItemSql)
                            #whCursor.execute(updateInvItemSql)
                            
        
                            lineitem.transfer_price = invItem[13]
                            lineitem.nlc = invItem[14]
                            order.vendorId = invItem[12]
                    else:
                        invItemSql = "select i.id, i.itemId, i.itemNumber, i.serialNumber, i.initialQuantity, i.currentQuantity, i.purchaseId, i.purchaseReturnId, i.currentWarehouseId, i.lastScanType, i.transferStatus, physicalWarehouseId, po.supplierId, l.unitPrice, l.nlc from inventoryItem i join purchase p on i.purchaseId = p.id join purchaseorder po on p.purchaseOrder_id = po.id join lineitem l on (i.itemId = l.itemId and po.id =l.purchaseOrder_id) where i.itemId = %d AND i.physicalWarehouseId = %d AND lastScanType in ('BAD_SALE', 'MARKED_BAD', 'SALE_RET_UNUSABLE', 'DOA_REJECTED', 'DOA_IN') AND i.currentQuantity >0 AND (i.transferStatus is NULL or i.transferStatus != 'IN_TRANSIT')"%(item.id,order.warehouse_id)
                        whCursor.execute(invItemSql)
                        invItems = whCursor.fetchall()
                        if invItems is None:
                            if warehouseDbConnection.open:
                                warehouseDbConnection.close()
                            raise TransactionServiceException(110, "No Item in the inventory with this item number " + itemNumbers[0]+" in Vendor Warehouse Id- "+str(warehouse.id)+" Order Id:- "+str(order.id))
    
                        totalCurrentQuantity =0
                        for invItem in invItems:
                            totalCurrentQuantity = totalCurrentQuantity + invItem[5]
                        
                        orderQuantity = lineitem.quantity
                        if totalCurrentQuantity < lineitem.quantity:
                            if warehouseDbConnection.open:
                                warehouseDbConnection.close()
                            raise TransactionServiceException(110, "Unsufficient Quantity with this item number " + itemNumbers[0]+" in Vendor Warehouse Id- "+str(warehouse.id)+" Order Id:- "+str(order.id))
                        
                        usedInventory = 0
                        if nonSerializedUsedInventoryMap.has_key(str(item.id)+"_"+str(warehouse.id)):
                            usedInventory = nonSerializedUsedInventoryMap.get(str(item.id)+"_"+str(warehouse.id))
                            usedInventory = usedInventory+orderQuantity
                            nonSerializedUsedInventoryMap[str(item.id)+"_"+str(warehouse.id)] = usedInventory
                        else:
                            usedInventory = orderQuantity
                            nonSerializedUsedInventoryMap[str(item.id)+"_"+str(warehouse.id)] = usedInventory
                        
                        if usedInventory > totalCurrentQuantity:
                            if warehouseDbConnection.open:
                                warehouseDbConnection.close()
                            raise TransactionServiceException(318, "Unsufficient Quantity with this item number " + itemNumbers[0]+" in Vendor Warehouse Id- "+str(warehouse.id)+" Order Id:- "+str(order.id))
                        
                        for invItem in invItems:
                            scanSql = "select * from scanNew where inventoryItemId =%d order by id"%(invItem[0])
                            whCursor.execute(scanSql)
                            scans = whCursor.fetchall()
                            
                            if scans is None:
                                if warehouseDbConnection.open:
                                    warehouseDbConnection.close()
                                raise TransactionServiceException(110, "Item with Item Number:- " + itemNumbers[0]+" never scanned in the system")
                            
                            currentQuantity = invItem[5]
                            scanQuantity = 0
                            if orderQuantity >0:
                                if currentQuantity < orderQuantity:
                                    scanQuantity = currentQuantity
                                    orderQuantity = orderQuantity - currentQuantity
                                elif currentQuantity > orderQuantity:
                                    scanQuantity = orderQuantity
                                    orderQuantity = 0
                                else:
                                    scanQuantity = currentQuantity
                                    orderQuantity = 0
                            else:
                                break
                            
                            current_time = datetime.datetime.now()
                            insertScanSql = "insert into scanNew(inventoryItemId, warehouseId, type, scannedAt, quantity, orderId) values(%d,%d,'%s','%s',%d,%d)"%(invItem[0],invItem[8],'BAD_SALE',current_time.strftime('%Y-%m-%d %H:%M:%S'),scanQuantity,order.id)
                            scanList.append(insertScanSql)
                            #whCursor.execute(insertScanSql)
                            updateInvItemSql = "update inventoryItem set currentQuantity = currentQuantity-%d, lastScanType='BAD_SALE' where id=%d "%(scanQuantity,invItem[0])
                            inventoryItemList.append(updateInvItemSql)
                            #whCursor.execute(updateInvItemSql)                            
            
                            lineitem.transfer_price = invItem[13]
                            lineitem.nlc = invItem[14]
                            order.vendorId = invItem[12]
                            order.fulfilmentWarehouseId = invItem[9]
            
            elif billingType == BillingType.OURS_EXTERNAL:
                try:
                    if ItemType.SERIALIZED == item.type:
                        serialNumber = serialNumbers[0]
                    else:
                        serialNumber = ""
                    if not warehouse_client.isAlive():
                        warehouse_client = WarehouseClient().get_client()
                    inventoryItem = warehouse_client.scanForOursExternalSale(lineitem.item_id, serialNumber, lineitem.item_number, invoice_number, order.fulfilmentWarehouseId, lineitem.transfer_price, lineitem.nlc, order.id)
                    lineitem.transfer_price = inventoryItem.unitPrice
                    lineitem.nlc = inventoryItem.nlc
                    order.vendorId = inventoryItem.supplierId
                except WarehouseServiceException as e:
                    print sys.exc_info()[0]
                    print 'Could not scan out orders due to: ' + e.message
                    raise TransactionServiceException(110, e.message)
            else:
                if not warehouse.isAvailabilityMonitored:
                    if not inventory_client.isAlive():
                        inventory_client = InventoryClient().get_client()
                    inventory_client.addInventory(item.id, warehouse.id, -1 * lineitem.quantity)
            
            
            orderscansMap[order.id] = scanList
            orderInventoryItemMap[order.id] = inventoryItemList
            orderFulfilmentWarehouseMap[order.id] = warehouse
            
            
    for order in ordersList:
        if billingType == BillingType.OURS:
            scanList = orderscansMap.get(order.id)
            #print order.id
            #print scanList
            inventoryItemList = orderInventoryItemMap.get(order.id)
            fulfillmentWarehouse = orderFulfilmentWarehouseMap.get(order.id)
            lineItem = order.lineitems[0]
            
            whCursor = warehouseDbConnection.cursor()
            
            for scan in scanList:
                whCursor.execute(scan)
            for invItem in inventoryItemList:
                whCursor.execute(invItem)
            
            if order.productCondition != ProductCondition.BAD:  
                if fulfillmentWarehouse.billingType == BillingType.OURS:
                    if not inventory_client.isAlive():
                        inventory_client = InventoryClient().get_client()
                    inventory_client.addInventory(lineItem.item_id, fulfillmentWarehouse.id, long(-1.0*lineItem.quantity))
        if order.freebieItemId:
            inventoryItem = None
            for freebieWhId in freebieWarehouseIdMap.get(order.id):
                try:                        
                    warehouse_client = WarehouseClient().get_client()
                    inventoryItem = warehouse_client.scanfreebie(orderId, order.freebieItemId, freebieWhId, ScanType.SALE);
                except Exception as e:
                    print e.message
                    raise TransactionServiceException(110,'Error in billing freebie for warehouseId ' + str(freebieWarehouseId))
                
            attr = Attribute()
            attr.orderId = orderId
            attr.name = "freebie_tp"
            attr.value = str(inventoryItem.unitPrice)
            
            attr1 = Attribute()
            attr1.orderId = orderId
            attr1.name = "freebie_vendor"
            attr1.value = str(inventoryItem.supplierId)

            attr2 = Attribute()
            attr2.orderId = orderId
            attr2.name = "freebie_nlc"
            attr2.value = str(inventoryItem.nlc)
        if order.productCondition != ProductCondition.BAD:
            '''
            Reduce the reservation count for all line items of the given order.
            '''
            try:
                if not inventory_client.isAlive():
                    inventory_client = InventoryClient().get_client()
                for lineitem in order.lineitems:
                    inventory_client.reduceReservationCount(lineitem.item_id, order.fulfilmentWarehouseId, sourceId, order.id, lineitem.quantity)
            except:
                print "Unable to reduce reservation count"
        
    
    warehouseDbConnection.commit()
    if warehouseDbConnection.open:
        warehouseDbConnection.close()
    session.commit()
    
    if billingType == BillingType.OURS or billingType == BillingType.OURS_EXTERNAL:
        #get seller for current warehouse id
        seller_id  = __get_seller(order.warehouse_id)
        whaddressmapping = WarehouseAddressMapping.query.filter_by(warehouse_id=ordersList[0].warehouse_id).one()
        invoiceNumber = get_next_invoice_counter(seller_id, ordersList[0].warehouse_id, hsnCode)
            
            
    
    invoiceTypeVal = 1
    if not individualInvoice:
        invoiceTypeVal = 2
    if lineItemSize > 1:
        invoiceTypeVal = 2
    if lineItemSize <= 1 and invoiceType =="BulkInvoice" :
        invoiceTypeVal = 2
    if ordersList[0].logisticsTransactionId:
        transactionShipSeqValues = ordersList[0].logisticsTransactionId.split('-')
        
        txnShipSeq = TransactionShipmentSequence.query.filter(TransactionShipmentSequence.transactionId==int(transactionShipSeqValues[0])).filter(TransactionShipmentSequence.sequence==int(transactionShipSeqValues[1])).order_by(desc(TransactionShipmentSequence.id)).first()
        txnShipSeq.invoiceFormat = invoiceTypeVal

    for order in ordersList:
        order.invoice_number = invoiceNumber
        order.seller_id = seller_id
        order.warehouse_address_id = whaddressmapping.address_id
    
#    logisticCostDetail= ShipmentLogisticsCostDetail.get_by(logisticsTransactionId=ordersList[0].logisticsTransactionId)
#    logisticCostDetail.packageDimensions = packageDimensions
        
    session.commit()
                            
    return True

def get_invoice_format_logistics_txn_id(transactionId, shipementSeq):
    txnShipSeq = TransactionShipmentSequence.query.filter(TransactionShipmentSequence.transactionId==transactionId).filter(TransactionShipmentSequence.sequence==shipementSeq).order_by(desc(TransactionShipmentSequence.id)).first()
    if txnShipSeq.invoiceFormat == 2:
        return 'Bulk'
    else:
        return 'Individual'
    
def create_homeshop_order(hsOrder):
    hs_Order = HsOrder()
    hs_Order.orderId = hsOrder.orderId
    hs_Order.catalogueName = hsOrder.catalogueName
    hs_Order.courierName = hsOrder.courierName
    hs_Order.hsItemId = hsOrder.hsItemId
    hs_Order.hsOrderDate = to_py_date(hsOrder.hsOrderDate)
    hs_Order.hsOrderNo = hsOrder.hsOrderNo
    hs_Order.hsProductId = hsOrder.hsProductId
    hs_Order.hsSubOrderNo = hsOrder.hsSubOrderNo
    hs_Order.paymentMode = hsOrder.paymentMode
    hs_Order.sellerSku = hsOrder.sellerSku
    hs_Order.slaDays = hsOrder.slaDays
    hs_Order.shippingName = hsOrder.shippingName
    order = Order.get_by(id=hsOrder.orderId)
    order.status = OrderStatus.ACCEPTED
    order.accepted_timestamp = datetime.datetime.now()
    user = None
    try:
        user_client = UserClient().get_client()
        user_to_add = User()
        user_to_add.email = 'HS.'+ str(hsOrder.orderId) + '@mailinator.com'
        user_to_add.password = 'ouBPONyDYWfeAFtXeaYRkQ'
        user_to_add.communicationEmail = user_to_add.email
        user_to_add.sourceStartTime = int(round(time.time() * 1000))
        user_to_add.sourceId = OrderSource.HOMESHOP18
        user_to_add.sex = Sex.WONT_SAY
        user_to_add = user_client.createUser(user_to_add)
        user = user_to_add
    except:
        pass
    
    if user:
        order.customer_id = user.userId
        order.customer_email = user.email
    session.commit()
    
def get_homeshop_order(order_id, hsOrderNo, hsSubOrderNo):
    query = HsOrder.query
    if order_id:
        query = query.filter(HsOrder.orderId == order_id)
    if hsOrderNo:
        query = query.filter(HsOrder.hsOrderNo == hsOrderNo)
    if hsSubOrderNo:
        query = query.filter(HsOrder.hsSubOrderNo == hsSubOrderNo)
    return query.all()

def homeshop_order_exists(hs_OrderNo, hs_SubOrderNo):
    exists = HsOrder.query.filter_by(hsOrderNo=hs_OrderNo,hsSubOrderNo=hs_SubOrderNo).first()
    if exists is not None:
        return True
    else:
        return False
    
def split_bulk_order(order_id, split_order_quantity):
    order = get_order(order_id)
    if split_order_quantity >=order.lineitems[0].quantity:
        raise TransactionServiceException(115, "Split Quantity is greater than or equal to order quantity")
    if order.status not in [OrderStatus.SUBMITTED_FOR_PROCESSING, 
                            OrderStatus.INVENTORY_LOW, 
                            OrderStatus.LOW_INV_PO_RAISED, 
                            OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, 
                            OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT, 
                            OrderStatus.CAPTURE_IN_PROCESS, OrderStatus.REJECTED]:
        raise TransactionServiceException(115, "Order not allowed to be split")
    
    new_order = __copy_order(order)
    new_order.originalOrderId=order.id
    
    new_order.lineitems[0].quantity = split_order_quantity
    new_order.logistics_provider_id = order.logistics_provider_id
    new_order.lineitems[0].logisticsCost = round((order.lineitems[0].logisticsCost/order.lineitems[0].quantity)*split_order_quantity,2)
    order.lineitems[0].logisticsCost = order.lineitems[0].logisticsCost - new_order.lineitems[0].logisticsCost
    if order.cod:
        new_order.lineitems[0].codCollectionCharges = round((order.lineitems[0].codCollectionCharges/order.lineitems[0].quantity)*split_order_quantity,2)
        order.lineitems[0].codCollectionCharges = order.lineitems[0].codCollectionCharges - new_order.lineitems[0].codCollectionCharges

    new_order.lineitems[0].total_price = new_order.lineitems[0].unit_price*split_order_quantity
    new_order.lineitems[0].total_weight = new_order.lineitems[0].unit_weight*split_order_quantity
    new_order.total_amount = new_order.lineitems[0].total_price
    new_order.total_weight = new_order.lineitems[0].total_weight
    new_order.shippingCost = round((order.shippingCost/order.lineitems[0].quantity)*split_order_quantity,0)
    new_order.shippingRefund = round((order.shippingRefund/order.lineitems[0].quantity)*split_order_quantity,0)
    order.lineitems[0].quantity = order.lineitems[0].quantity - split_order_quantity
    order.lineitems[0].total_price = order.lineitems[0].unit_price * order.lineitems[0].quantity
    order.lineitems[0].total_weight = order.lineitems[0].unit_weight*order.lineitems[0].quantity
    order.total_amount = order.lineitems[0].total_price
    order.total_weight = order.lineitems[0].total_weight
    order.shippingCost = order.shippingCost - new_order.shippingCost
    
    new_order.net_payable_amount = new_order.total_amount + new_order.shippingCost
    order.net_payable_amount = order.total_amount + order.shippingCost
    new_order.wallet_amount = 0
    
    if order.wallet_amount > 0:
        wallet_amount = order.wallet_amount
        first_order = new_order
        second_order = order
        if new_order.net_payable_amount > order.net_payable_amount:
            first_order = order
            second_order = new_order
        
        first_order.wallet_amount = min(first_order.total_amount + first_order.shippingCost, wallet_amount)
        first_order.net_payable_amount = first_order.total_amount + first_order.shippingCost - first_order.wallet_amount
        second_order.wallet_amount = wallet_amount - first_order.wallet_amount
        second_order.net_payable_amount = second_order.total_amount + second_order.shippingCost - second_order.wallet_amount
    #Add attributes during split    
    attributes = Attribute.query.filter(Attribute.orderId == order.id).all()
    if attributes:
        for attribute in attributes:
            newattribute = Attribute()
            newattribute.orderId = new_order.id
            newattribute.name = attribute.name 
            newattribute.value = attribute.value
        
    session.commit()
    inventoryClient = InventoryClient().get_client()
    inventoryClient.reserveItemInWarehouse(new_order.lineitems[0].item_id, new_order.fulfilmentWarehouseId, sourceId, new_order.id, to_java_date(new_order.created_timestamp), to_java_date(new_order.promised_shipping_time), new_order.lineitems[0].quantity)
    inventoryClient.reduceReservationCount(order.lineitems[0].item_id, order.fulfilmentWarehouseId, sourceId, order.id, new_order.lineitems[0].quantity)

    html = """
    <html>
    <body>
    <div>
        <p>Dear Customer,</p>
        <p>
            Your order: """+str(order.id)+""" have been split into following orders:-
            <br>
            <br>
            <div>
                <table>
                <tr><td colspan="8"><hr /></td></tr>
                <tr><td colspan="8" align="left"><b>Order Details</b></td></tr>
                <tr><td colspan="8"><hr /></td></tr>
                <tr>
                    <th width="100">Order Id</th>
                    <th>Product Name</th>
                    <th width="100">Quantity</th>
                    <th width="100">Unit Price</th>
                    <th width="100">Amount</th>
                </tr>"""
    html += """
            <tr>
            <td align="center">"""+str(order.id)+"""</td>
            <td>"""+str(order.lineitems[0])+"""</td>
            <td align="center">"""+("%.0f" % order.lineitems[0].quantity)+"""</td>
            <td align="center">"""+("%.2f" % order.lineitems[0].unit_price)+"""</td>
            <td align="center">"""+("%.2f" % round(order.lineitems[0].total_price,2))+"""</td>
            </tr>"""
    html += """
            <tr>
            <td align="center">"""+str(new_order.id)+"""</td>
            <td>"""+str(new_order.lineitems[0])+"""</td>
            <td align="center">"""+("%.0f" % new_order.lineitems[0].quantity)+"""</td>
            <td align="center">"""+("%.2f" % new_order.lineitems[0].unit_price)+"""</td>
            <td align="center">"""+("%.2f" % round(new_order.lineitems[0].total_price,2))+"""</td>
            </tr>"""
            
    html += """
            </table>
            <p>
                For any queries please call +918826894203.
            </p>
            <p>
                Warm Regards,<br />
                SmartDukaan Team
            </p>
            </div>
            </body>
            </html>
            """
    subject = "Your Smart Dukaan Order: "+str(order.id) + " have been split"
    bcc = ["backup@saholic.com"]
    try:
        helper_client = HelperClient(host_key = "helper_service_server_host_prod").get_client()
        helper_client.saveUserEmailForSending([order.customer_email], SaholicHelpEmailId, subject, html, str(order.id), "TransactionInfo", [], bcc, order.source)
        #if order.customer_mobilenumber is not None:
        #    smsText = "Dear Customer, Your Order "+ str(order.id) + " have been split into two orders. Please check your account or email. For any queries please call +918826894203."
        #    send_transaction_sms(order.customer_id, order.customer_mobilenumber, smsText, SmsType.TRANSACTIONAL, False)
            
    except Exception as e:
        print e
    
    return new_order

def __verify_order(order):
    logging.info("Verifying order no: " + str(order.id))
    if order.status == OrderStatus.COD_VERIFICATION_PENDING:
        order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
        order.statusDescription = "Submitted for processing"
        order.verification_timestamp = datetime.datetime.now()
        session.commit()
        
        if order.source == OrderSource.STORE:
            sod = StoreOrderDetail.get_by(orderId = order.id)
            __push_store_collection_to_hotspot(order, "advance", sod.cashAmount, sod.cardAmount)
        logging.info("Successfully verified order no: " + str(order.id))
        return True
    else:
        logging.warning("Verify called for order no." + str(order.id) +" which is not in verification pending state");
        return False

def verify_orders_for_transaction(transactionId):
    transaction = get_transaction(transactionId)
    for order in transaction.orders:
        __verify_order(order)
    return True

def get_creditor_info(creditorId):
    return Creditor.get_by(id=creditorId)

def update_creditor_info(creditor):
    t_creditor = get_creditor_info(creditor.id)
    t_creditor.active = creditor.active
    if t_creditor.name != creditor.name:
        t_creditor.name = creditor.name
    session.commit()
    return True

def get_user_sanction_details(userId, creditorId):
    us_query = UserSanction.query
    if userId:
        us_query = us_query.filter(UserSanction.user_id == userId)
    if creditorId:
        us_query = us_query.filter(UserSanction.creditor_id == creditorId)
    us_query = us_query.filter(UserSanction.active == True)   
    allSanctions = us_query.all()
    returnMap = {}
    for sanction in allSanctions:
        ticket_size = -1
        if creditorTicketSizeMap.has_key(sanction.creditor_id):
            ticket_size = creditorTicketSizeMap.get(sanction.creditor_id)
        else:
            fetchCreditorTicketSizeMap()
            ticket_size = creditorTicketSizeMap.get(sanction.creditor_id)
        
        if returnMap.has_key(sanction.creditor_id):
            creditorMap = returnMap.get(sanction.creditor_id)
            creditorMap['TicketSize'] = ticket_size
            if creditorMap.has_key('Sanctions'):
                sanctionsList = creditorMap.get('Sanctions')
                sanctionsList.append(sanction)
                creditorMap['Sanctions'] = sanctionsList
            else:
                sanctionsList =[]
                sanctionsList.append(sanction)
                creditorMap['Sanctions'] = sanctionsList
            returnMap[sanction.creditor_id] = creditorMap
        else:
            creditorMap = {}
            creditorMap['TicketSize'] = ticket_size
            sanctionsList =[]
            sanctionsList.append(sanction)
            creditorMap['Sanctions'] = sanctionsList
            returnMap[sanction.creditor_id] = creditorMap
                    
    return returnMap

def get_user_sanction_details_paginated(userId, creditorId, limit=0, offset=0,sort="Default"):
    totalCount = UserSanction.query.filter(UserSanction.active == True).count()
    hasMore = False
    if limit >0 and (limit+offset) < totalCount:
        hasMore = True 
    us_query = UserSanction.query
    if userId:
        us_query = us_query.filter(UserSanction.user_id == userId)
    if creditorId:
        us_query = us_query.filter(UserSanction.creditor_id == creditorId)
    us_query = us_query.filter(UserSanction.active == True)
    if sort == 'loan':
        us_query = us_query.order_by(desc(UserSanction.loan))
    if limit>0:
        us_query = us_query.limit(limit)
    if offset>0 and limit >0:
        us_query = us_query.offset(offset)
    allSanctions = us_query.all()
    returnMap = {}
    for sanction in allSanctions:
        ticket_size = -1
        if creditorTicketSizeMap.has_key(sanction.creditor_id):
            ticket_size = creditorTicketSizeMap.get(sanction.creditor_id)
        else:
            fetchCreditorTicketSizeMap()
            ticket_size = creditorTicketSizeMap.get(sanction.creditor_id)
        
        if returnMap.has_key(sanction.creditor_id):
            creditorMap = returnMap.get(sanction.creditor_id)
            creditorMap['TicketSize'] = ticket_size
            if creditorMap.has_key('Sanctions'):
                sanctionsList = creditorMap.get('Sanctions')
                sanctionsList.append(sanction)
                creditorMap['Sanctions'] = sanctionsList
            else:
                sanctionsList =[]
                sanctionsList.append(sanction)
                creditorMap['Sanctions'] = sanctionsList
            returnMap[sanction.creditor_id] = creditorMap
        else:
            creditorMap = {}
            creditorMap['TicketSize'] = ticket_size
            sanctionsList =[]
            sanctionsList.append(sanction)
            creditorMap['Sanctions'] = sanctionsList
            returnMap[sanction.creditor_id] = creditorMap
                    
    return hasMore, totalCount, returnMap

def update_user_sanction(userSanction):
    userS = UserSanction.get_by(id=userSanction.id)
    if userS is None:
        return False
    else:
        userS.credit_blocked = userSanction.credit_blocked
        userS.loan = userSanction.loan
        userS.active = userSanction.active
        session.commit()
        return True
    
def __get_user_sanction(sanctionId):
    return UserSanction.get_by(id=sanctionId)

def get_credit_history_records(paymentId, userId, creditorId, creditTxnType, limit=0, offset=0):
    credit_hs_query = CreditHistory.query
    if paymentId and paymentId>0:
        credit_hs_query = credit_hs_query.filter(CreditHistory.payment_id==paymentId)
    if userId and userId>0:
        credit_hs_query = credit_hs_query.filter(CreditHistory.user_id==userId)
    if creditorId and creditorId>0:
        credit_hs_query = credit_hs_query.filter(CreditHistory.creditor_id==creditorId)
    if creditTxnType:
        credit_hs_query = credit_hs_query.filter(CreditHistory.credit_type==CreditTxnType._VALUES_TO_NAMES[creditTxnType])
    credit_hs_query = credit_hs_query.order_by(desc(CreditHistory.created))
    if limit>0:
        credit_hs_query = credit_hs_query.limit(limit)
    if offset>0 and limit >0:
        credit_hs_query = credit_hs_query.offset(offset)
    return credit_hs_query.all()

def get_credit_history_records_paginated(paymentId, userId, creditorId, creditTxnType, limit=0, offset=0):
    totalCount = CreditHistory.query.count()
    hasMore = False
    if limit >0 and (limit+offset) < totalCount:
        hasMore = True
    credit_hs_query = CreditHistory.query
    if paymentId and paymentId>0:
        credit_hs_query = credit_hs_query.filter(CreditHistory.payment_id==paymentId)
    if userId and userId>0:
        credit_hs_query = credit_hs_query.filter(CreditHistory.user_id==userId)
    if creditorId and creditorId>0:
        credit_hs_query = credit_hs_query.filter(CreditHistory.creditor_id==creditorId)
    if creditTxnType:
        credit_hs_query = credit_hs_query.filter(CreditHistory.credit_type==CreditTxnType._VALUES_TO_NAMES[creditTxnType])
    credit_hs_query = credit_hs_query.order_by(desc(CreditHistory.created))
    if limit>0:
        credit_hs_query = credit_hs_query.limit(limit)
    if offset>0 and limit >0:
        credit_hs_query = credit_hs_query.offset(offset)
    return hasMore, totalCount, credit_hs_query.all()

def process_credit_transaction(paymentId, userId, creditorId, creditTxns, invoiceNumber=None):
    us_query = UserSanction.query
    if userId:
        us_query = us_query.filter(UserSanction.user_id == userId)
    if creditorId:
        us_query = us_query.filter(UserSanction.creditor_id == creditorId)
        
    userSanctions = us_query.all()
    if userSanctions is None or len(userSanctions)==0:
        logging.info("No Credit Sanction Details Availble. UserId:- "+str(userId)+ " Creditor Id:- "+ str(creditorId))
        raise TransactionServiceException(221, "No Credit Sanction Details Availble. UserId:- "+str(userId)+ " Creditor Id:- "+ str(creditorId))
    
    userSanction = userSanctions[0]
    availableCredit =  userSanction.credit_limit - (userSanction.credit_blocked + userSanction.loan)
        
    amountToReversed = 0
    amountToLoan = 0
    amountToBlocked = 0
    
    creditHistoryTotal = 0
    
    allCreditTxns = get_credit_history_records(paymentId, userId, creditorId, None)
    for credit_txn in allCreditTxns:
        creditHistoryTotal = creditHistoryTotal + credit_txn.amount
        
    if creditHistoryTotal < 0:
        logging.info("Error: Credit History is Negative. Payment Id:- "+str(paymentId))
        raise TransactionServiceException(222, "Error: Credit History is Negative. Payment Id:- "+str(paymentId))
    
    for creditTxn in creditTxns:
        if creditTxn.credit_type in [CreditTxnType.BLOCKED_REVERSED]:
            amountToReversed = amountToReversed + creditTxn.amount
        if creditTxn.credit_type in [CreditTxnType.LOAN]:
            amountToLoan = amountToLoan + creditTxn.amount
        if creditTxn.credit_type in [CreditTxnType.BLOCKED]:
            amountToBlocked = amountToBlocked + creditTxn.amount   
    
    if amountToLoan > userSanction.credit_blocked:
        logStr = "Error: Not Enough Credit to Process For Loan. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(creditTxns[0].shipping_id)+" Amount to Loan:- "+str(amountToLoan)+ " Credit Blocked:- "+str(userSanction.credit_blocked)
        logging.info(logStr)
        raise TransactionServiceException(224, "Error: Not Enough Credit to Process For Loan. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(creditTxns[0].shipping_id)+ " Amount to Loan:- "+str(amountToLoan)+ " Credit Blocked:- "+str(userSanction.credit_blocked))
    
    if amountToReversed > userSanction.credit_blocked:
        logStr= "Error: Not Enough Credit to Process For Reversal. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(creditTxns[0].shipping_id)+ " Amount to Reverse:- "+str(amountToReversed)+ " Credit Blocked:- "+str(userSanction.credit_blocked)
        logging.info(logStr)
        raise TransactionServiceException(225, "Error: Not Enough Credit to Process For Reversal. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(creditTxns[0].shipping_id)+ " Amount to Reverse:- "+str(amountToReversed)+ " Credit Blocked:- "+str(userSanction.credit_blocked))
    
    if amountToBlocked > availableCredit:
        logStr = "Error: Not Enough Credit to be Blocked. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(creditTxns[0].shipping_id)+" Amount to Block:- "+str(amountToBlocked)+ " Credit Blocked:- "+str(userSanction.credit_blocked)
        logging.info(logStr)
        raise TransactionServiceException(226, "Error: Not Enough Credit to be Blocked. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(creditTxns[0].shipping_id)+" Amount to Block:- "+str(amountToBlocked)+ " Credit Blocked:- "+str(userSanction.credit_blocked))
    
    for creditTxn in creditTxns:
        creditHistoryRec = CreditHistory.get_by(payment_id=paymentId,shipping_id=creditTxn.shipping_id)
        if creditHistoryRec is None:
            creditHistoryRec = CreditHistory()
            creditHistoryRec.user_id = userId
            creditHistoryRec.creditor_id = creditorId
            creditHistoryRec.payment_id = paymentId
            creditHistoryRec.amount = creditTxn.amount * creditTxnMultiplierMap.get(creditTxn.credit_type) 
            creditHistoryRec.credit_type = CreditTxnType._VALUES_TO_NAMES[creditTxn.credit_type]
            creditHistoryRec.shipping_id = creditTxn.shipping_id
            userSanction.credit_blocked = userSanction.credit_blocked + (creditTxn.amount * creditTxnMultiplierMap.get(creditTxn.credit_type))
            if creditTxn.credit_type in [CreditTxnType.LOAN]:
                userSanction.loan = userSanction.loan + creditTxn.amount
                loanHistoryRec = LoanHistory()
                loanHistoryRec.user_id = userId
                loanHistoryRec.creditor_id = creditorId
                loanHistoryRec.payment_id = paymentId
                loanHistoryRec.amount = creditTxn.amount * loanTxnMultplierMap.get(creditTxn.credit_type)
                loanHistoryRec.credit_type = CreditTxnType._VALUES_TO_NAMES[creditTxn.credit_type]
                loanHistoryRec.loan_id = creditTxn.shipping_id
                if invoiceNumber is not None:
                    loanHistoryRec.invoiceNumber = invoiceNumber
                if creditorDueDateMap.has_key(creditorId):
                    due_days = creditorDueDateMap.get(creditorId)
                    loanHistoryRec.due_date = datetime.datetime.now()+timedelta(days=due_days)
                else:
                    fetchCreditorDueDateMap()
                    due_days = creditorDueDateMap.get(creditorId)
                    loanHistoryRec.due_date = datetime.datetime.now()+timedelta(days=due_days)
        else:
            logging.info("Error: Entry already existed for "+str(paymentId) + " Shipping Id:- "+str(creditTxns[0].shipping_id) +" Credit Txn Type:- "+CreditTxnType._VALUES_TO_NAMES[creditTxn.credit_type]+ " in Credit History ")
            raise TransactionServiceException(227, "Error: Entry already existed for "+str(paymentId) + " Shipping Id:- "+str(creditTxns[0].shipping_id) +" Credit Txn Type:- "+CreditTxnType._VALUES_TO_NAMES[creditTxn.credit_type]+ " in Credit History ")
    return True
            
def get_loan_payable_for_user_to_creditor(userId, creditorId, dueDate):
    loanhistoryRecords = LoanHistory.query.filter(and_(LoanHistory.user_id==userId, LoanHistory.creditor_id==creditorId, LoanHistory.due_date<to_py_date(dueDate), LoanHistory.updated is None)).all()
    loanAmount = 0
    for loanHistory in loanhistoryRecords:
        loanAmount = loanAmount + loanHistory.amount
    return loanAmount

def get_loan_history_records(paymentId, userId, creditorId, creditTxnType, limit=0, offset=0):
    loan_hs_query = LoanHistory.query
    if paymentId and paymentId>0:
        loan_hs_query = loan_hs_query.filter(LoanHistory.payment_id==paymentId)
    if userId and userId>0:
        loan_hs_query = loan_hs_query.filter(LoanHistory.user_id==userId)
    if creditorId and creditorId>0:
        loan_hs_query = loan_hs_query.filter(LoanHistory.creditor_id==creditorId)
    if creditTxnType:
        loan_hs_query = loan_hs_query.filter(LoanHistory.credit_type==CreditTxnType._VALUES_TO_NAMES[creditTxnType])
    loan_hs_query = loan_hs_query.order_by(desc(LoanHistory.created))
    if limit>0:
        loan_hs_query = loan_hs_query.limit(limit)
    if offset>0 and limit>0:
        loan_hs_query = loan_hs_query.offset(offset)
    
    return loan_hs_query.all()

def get_loan_history_records_paginated(paymentId, userId, creditorId, creditTxnType, limit=0, offset=0):
    totalCount = LoanHistory.query.count()
    hasMore = False
    if limit >0 and (limit+offset) < totalCount:
        hasMore = True
    loan_hs_query = LoanHistory.query
    if paymentId and paymentId>0:
        loan_hs_query = loan_hs_query.filter(LoanHistory.payment_id==paymentId)
    if userId and userId>0:
        loan_hs_query = loan_hs_query.filter(LoanHistory.user_id==userId)
    if creditorId and creditorId>0:
        loan_hs_query = loan_hs_query.filter(LoanHistory.creditor_id==creditorId)
    if creditTxnType:
        loan_hs_query = loan_hs_query.filter(LoanHistory.credit_type==CreditTxnType._VALUES_TO_NAMES[creditTxnType])
    loan_hs_query = loan_hs_query.order_by(desc(LoanHistory.created))
    if limit>0:
        loan_hs_query = loan_hs_query.limit(limit)
    if offset>0 and limit>0:
        loan_hs_query = loan_hs_query.offset(offset)
    
    return hasMore, totalCount, loan_hs_query.all()

def process_loan_transaction(paymentId, userId, creditorId, loantxns):
    us_query = UserSanction.query
    if userId:
        us_query = us_query.filter(UserSanction.user_id == userId)
    if creditorId:
        us_query = us_query.filter(UserSanction.creditor_id == creditorId)
        
    userSanctions = us_query.all()
    if userSanctions is None or len(userSanctions)==0:
        logging.info("No Credit Sanction Details Availble. UserId:- "+str(userId)+ " Creditor Id:- "+ str(creditorId))
        raise TransactionServiceException(227, "No Credit Sanction Details Availble. UserId:- "+str(userId)+ " Creditor Id:- "+ str(creditorId))
    
    userSanction = userSanctions[0]
        
    amountToLoan = 0
    amountToLoanPaid = 0
    amountToCorrected = 0
    
    loanHistoryTotal = 0

    allLoanTxns = get_loan_history_records(paymentId, userId, creditorId, None)
    for loan_txn in allLoanTxns:
        loanHistoryTotal = loanHistoryTotal + loan_txn.amount
        
    if loanHistoryTotal < 0:
        logging.info("Error: Loan History is Negative. Payment Id:- "+str(paymentId))
        raise TransactionServiceException(228, "Error: Loan History is Negative. Payment Id:- "+str(paymentId))
    
    for loanTxn in loantxns:
        if loanTxn.credit_type in [CreditTxnType.LOAN]:
            amountToLoan = amountToLoan + loanTxn.amount
        if loanTxn.credit_type in [CreditTxnType.PAID]:
            amountToLoanPaid = amountToLoanPaid + loanTxn.amount
        if loanTxn.credit_type in [CreditTxnType.CORRECTION]:
            amountToCorrected = amountToCorrected + loanTxn.amount
    
    if amountToLoan > userSanction.loan:
        logging.info("Error: Loan required is more than specified. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(loantxns[0].loan_id))
        raise TransactionServiceException(229, "Error: Loan required is more than specified. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(loantxns[0].loan_id))
    
    if amountToLoanPaid > userSanction.loan:
        logging.info("Error: Can't Pay more than Loan Amount. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(loantxns[0].loan_id))
        raise TransactionServiceException(230, "Error: Can't Pay more than Loan Amount. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(loantxns[0].loan_id))
    
    if amountToCorrected > userSanction.loan:
        logging.info("Error: Correction Amount More than loan Amount. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(loantxns[0].loan_id))
        raise TransactionServiceException(231, "Error: Correction Amount More than loan Amount. Payment Id:- "+str(paymentId) + " Shipping Id:- "+str(loantxns[0].loan_id))    
    
    for loanTxn in loantxns:
        if loanTxn.credit_type in [CreditTxnType.PAID]:
            loanHistoryRecords = LoanHistory.query.filter(LoanHistory.loan_id==loanTxn.loan_id).filter(LoanHistory.payment_id==paymentId).all()
            loanTotalHistory = 0
            for loanHistoryRec in loanHistoryRecords:
                loanTotalHistory = loanTotalHistory + loanHistoryRec.amount
            if loanTotalHistory <= 0:
                logging.info("Error: Loan Already Paid or Closed for "+str(paymentId) + " Loan Id:- "+str(loanTxn.loan_id) +" Loan Txn Type:- "+CreditTxnType._VALUES_TO_NAMES[loanTxn.credit_type])
                raise TransactionServiceException(227, "Error: Loan Already Paid or Closed for "+str(paymentId) + " Loan Id:- "+str(loanTxn.loan_id) +" Loan Txn Type:- "+CreditTxnType._VALUES_TO_NAMES[loanTxn.credit_type])
            else:
                loanHistoryRec = LoanHistory()
                loanHistoryRec.user_id = userId
                loanHistoryRec.creditor_id = creditorId
                loanHistoryRec.payment_id = paymentId
                loanHistoryRec.amount = loanTxn.amount * loanTxnMultplierMap.get(loanTxn.credit_type)
                loanHistoryRec.credit_type = CreditTxnType._VALUES_TO_NAMES[loanTxn.credit_type]
                loanHistoryRec.loan_id = loanTxn.loan_id
                loanHistoryRec.invoiceNumber = loanTxn.invoiceNumber
                if loanTxn.value_date:
                    loanHistoryRec.value_date = to_py_date(loanTxn.value_date)
                userSanction.loan = userSanction.loan + (loanTxn.amount * loanTxnMultplierMap.get(loanTxn.credit_type))
        else:
            loanHistoryRec = LoanHistory.get_by(payment_id=paymentId,loan_id=loanTxn.loan_id, credit_type=CreditTxnType._VALUES_TO_NAMES[loanTxn.credit_type])
            if loanHistoryRec is None:
                loanHistoryRec = LoanHistory()
                loanHistoryRec.user_id = userId
                loanHistoryRec.creditor_id = creditorId
                loanHistoryRec.payment_id = paymentId
                loanHistoryRec.amount = loanTxn.amount * loanTxnMultplierMap.get(loanTxn.credit_type)
                loanHistoryRec.credit_type = CreditTxnType._VALUES_TO_NAMES[loanTxn.credit_type]
                loanHistoryRec.loan_id = loanTxn.loan_id
                loanHistoryRec.invoiceNumber = loanTxn.invoiceNumber
                if loanTxn.value_date:
                    loanHistoryRec.value_date = to_py_date(loanTxn.value_date)
                if loanTxn.credit_type in [CreditTxnType.LOAN]:
                    if creditorDueDateMap.has_key(creditorId):
                        due_days = creditorDueDateMap.get(creditorId)
                        loanHistoryRec.due_date = datetime.datetime.now()+timedelta(days=due_days)
                    else:
                        fetchCreditorDueDateMap()
                        due_days = creditorDueDateMap.get(creditorId)
                        loanHistoryRec.due_date = datetime.datetime.now()+timedelta(days=due_days)
                if loanTxn.credit_type in [CreditTxnType.CORRECTION]:
                    userSanction.loan = userSanction.loan + (loanTxn.amount * loanTxnMultplierMap.get(loanTxn.credit_type))
            else:
                logging.info("Error: Entry already existed for "+str(paymentId) + " Loan Id:- "+str(loanTxn.loan_id) +" Loan Txn Type:- "+CreditTxnType._VALUES_TO_NAMES[loanTxn.credit_type])
                raise TransactionServiceException(227, "Error: Entry already existed for "+str(paymentId) + " Loan Id:- "+str(loanTxn.loan_id) +" Loan Txn Type:- "+CreditTxnType._VALUES_TO_NAMES[loanTxn.credit_type])
    session.commit()   
    return True
    
#paymentId is transaction id
def __loanHistoryObj(user_id, creditor_id, paymentId, amount, credit_type, loan_id, value_date=None, invoiceNumber=None):
    loanobj = TLoanHistory()
    loanobj.user_id = user_id
    loanobj.creditor_id = creditor_id    
    loanobj.payment_id = paymentId
    loanobj.amount = amount
    loanobj.credit_type = credit_type
    loanobj.loan_id = loan_id
    if value_date is not None:
        loanobj.value_date = to_java_date(value_date)
    if invoiceNumber is not None:
        loanobj.invoiceNumber = invoiceNumber
    return loanobj

def __creditHistoryObj(user_id, creditor_id, paymentId, amount, credit_type, shipping_id):
    creditObj = TCreditHistory()
    creditObj.user_id = user_id
    creditObj.creditor_id = creditor_id
    creditObj.payment_id = paymentId
    creditObj.amount = amount
    creditObj.credit_type = credit_type
    creditObj.shipping_id = shipping_id
    return creditObj

def get_outstanding_payments(fetchType,userId=0,limit=100):
    if fetchType =='loan':
        return fetch_outstanding_loans(userId,limit)
    elif fetchType =='credit':
        return fetch_outstanding_credits(userId,limit)
    else:
        return None

def fetch_outstanding_loans(userId,limit):
    userCounterMap={}
    try:
        loan_os_query = session.query(LoanHistory.user_id,LoanHistory.creditor_id,LoanHistory.payment_id,func.sum(LoanHistory.amount).label('totalAmount'),func.max(LoanHistory.due_date).label('createdDate'))
        user_client = UserClient().get_client()
        if userId>0:
            loan_os_query = loan_os_query.filter(LoanHistory.user_id==userId)
        loan_os_result = loan_os_query.group_by(LoanHistory.payment_id).having(func.sum(LoanHistory.amount)>0).order_by(func.max(LoanHistory.id)).all()
        if loan_os_result is not None or len(loan_os_result)>0:
            userIds=[]
            for r in loan_os_result:
                userIds.append(r.user_id)
            userCounterMap = user_client.getCounterName(userIds)
        return userCounterMap, loan_os_result 
    except Exception as e:
        print e
        
def fetch_outstanding_credits(userId,limit):
    userCounterMap={}
    try:
        credit_os_query = session.query(CreditHistory.user_id,CreditHistory.creditor_id,CreditHistory.payment_id,func.sum(CreditHistory.amount).label('totalAmount'),(CreditHistory.created).label('createdDate'))
        user_client = UserClient().get_client()
        if userId>0:
            credit_os_query = credit_os_query.filter(CreditHistory.user_id==userId)
        credit_os_result = credit_os_query.group_by(CreditHistory.payment_id).having(func.sum(CreditHistory.amount)>0).order_by(func.max(CreditHistory.id)).all()
        if credit_os_result is not None or len(credit_os_result)>0:
            userIds=[]
            for r in credit_os_result:
                userIds.append(r.user_id)
            userCounterMap = user_client.getCounterName(userIds)
        return userCounterMap, credit_os_result 
    except Exception as e:
        print e

def mark_payment_settled(userId,paymentId,totalAmount,repaymentDate):
    try:
        pendingSettlements = session.query(LoanHistory).filter_by(user_id=userId,payment_id=paymentId).all()
        if pendingSettlements is not None:
            amount=0
            for pendingSettlement in pendingSettlements:
                amount += pendingSettlement.amount
            if amount!=totalAmount:
                return False,"Amount mismatch for the particular payment id"
            else:
                loanObjs = []
                for pendingSettlement in pendingSettlements:
                    loanObj = __loanHistoryObj(pendingSettlement.user_id, 1, pendingSettlement.payment_id, pendingSettlement.amount, CreditTxnType.PAID, pendingSettlement.loan_id,to_py_date(repaymentDate), pendingSettlement.invoiceNumber)
                    loanObjs.append(loanObj)
                if process_loan_transaction(paymentId, userId, 1, loanObjs):
                    return True,"Successfully settled payment"
        else:
            return False,"No payment to be settled."    
    except:
        print traceback.print_exc()
        return False,"Some exception at the backend"
        
        
def mark_payment_settled_partial(userId,paymentId,totalAmount,repaymentDate):
    try:
        pendingSettlements = session.query(LoanHistory).filter_by(user_id=userId,payment_id=paymentId).all()
        if pendingSettlements is not None:
            amount=0
            for pendingSettlement in pendingSettlements:
                amount += pendingSettlement.amount
            if amount!=totalAmount:
                return False,"Amount mismatch for the particular payment id"
            else:
                loanObjs = []
                for pendingSettlement in pendingSettlements:
                    loanObj = __loanHistoryObj(pendingSettlement.user_id, 1, pendingSettlement.payment_id, pendingSettlement.amount, CreditTxnType.PAID, pendingSettlement.loan_id,datetime.datetime.strptime(repaymentDate, '%Y-%m-%d'), pendingSettlement.invoiceNumber)
                    loanObjs.append(loanObj)
                process_loan_transaction(paymentId, userId, 1, loanObjs)
        else:
            return False,"No payment to be settled."    
    except:
        print traceback.print_exc()
        
def set_order_attribute_for_master_order_id(logisticsTransactionId, attributes):
    orders = get_group_orders_by_logistics_txn_id(logisticsTransactionId)
    for order in orders:
        for attribute in attributes :
            existingAttribute = Attribute.query.filter(Attribute.orderId == order.id).filter(Attribute.name == attribute.name).first()
            if existingAttribute:
                existingAttribute.value = attribute.value
            else :                
                attr = Attribute()
                attr.orderId = order.id
                attr.name = attribute.name
                attr.value = attribute.value
    session.commit()

def update_master_order_awb(logisticsTransactionId, airwaybill_no):
    orders = get_group_orders_by_logistics_txn_id(logisticsTransactionId)
    shipmentAmount = 0
    shipmentWeight = 0
    shipmentLogisticsCost = 0
    shipmentCodCollectionCharges = 0
    for order in orders:
        order.airwaybill_no = airwaybill_no
        order.tracking_id = airwaybill_no
        shipmentAmount = shipmentAmount + order.total_amount
        shipmentWeight = shipmentWeight + order.total_weight
        shipmentLogisticsCost = shipmentLogisticsCost + order.lineitems[0].logisticsCost
        shipmentCodCollectionCharges = shipmentCodCollectionCharges + order.lineitems[0].codCollectionCharges

    shipmentLogisticsCostObj = TShipmentLogisticsCostDetail()
    shipmentLogisticsCostObj.airwayBillNo = airwaybill_no
    shipmentLogisticsCostObj.logisticsTransactionId = logisticsTransactionId
    shipmentLogisticsCostObj.shipmentAmount = shipmentAmount
    shipmentLogisticsCostObj.shipmentCodCollectionCharges = shipmentCodCollectionCharges
    shipmentLogisticsCostObj.shipmentLogsiticsCost = shipmentLogisticsCost
    shipmentLogisticsCostObj.shipmentWeight = shipmentWeight
    
    shipmentLogisticsCostDetails = []
    shipmentLogisticsCostDetails.append(shipmentLogisticsCostObj)
    add_or_update_shipment_logistics_cost_details(shipmentLogisticsCostDetails)
    
    return True

def add_or_update_shipment_logistics_cost_details(shipmentLogisticsCostDetails):
    currentTime = datetime.datetime.now()
    for shipmentLogisticsCostDetail in shipmentLogisticsCostDetails:
        shipment_logisticsCostDetail = ShipmentLogisticsCostDetail.get_by(logisticsTransactionId=shipmentLogisticsCostDetail.logisticsTransactionId)
        if shipment_logisticsCostDetail:
            shipment_logisticsCostDetail.shipmentAmount = round(shipmentLogisticsCostDetail.shipmentAmount,2)
            shipment_logisticsCostDetail.shipmentWeight = round(shipmentLogisticsCostDetail.shipmentWeight,3)
            shipment_logisticsCostDetail.shipmentLogsiticsCost = round(shipmentLogisticsCostDetail.shipmentLogsiticsCost,2)
            shipment_logisticsCostDetail.shipmentCodCollectionCharges = round(shipmentLogisticsCostDetail.shipmentCodCollectionCharges,2)
            shipment_logisticsCostDetail.updatedAt = currentTime
            shipment_logisticsCostDetail.airwayBillNo = shipmentLogisticsCostDetail.airwayBillNo 
        else:
            raise RuntimeError("Unable to add ShipmentLogisticsCostDetail.")
    session.commit()
    return True

def logisticsProviderFinalCheck(ordersList):
    return ordersList

def __create_return_order_info(order, status, quantity=0):
    returnOrderInfo = ReturnOrderInfo()
    returnOrderInfo.orderId = order.id
    returnOrderInfo.lineItemId = order.lineitems[0].id
    if quantity>0:
        returnOrderInfo.returnQuantity = quantity
    else:
        returnOrderInfo.returnQuantity = order.lineitems[0].quantity
    returnOrderInfo.createdAt = datetime.datetime.now()
    returnOrderInfo.returnStatus = status
    returnOrderInfo.masterOrderId = order.logisticsTransactionId
    returnOrderInfo.warehouse_id = order.warehouse_id
    session.commit()
    
    return returnOrderInfo

def create_return_order_info(returnInfo, order=None, itemCondition="DAMAGED"):
    returnOrderInfo = ReturnOrderInfo()
    returnOrderInfo.orderId = returnInfo.orderId
    returnOrderInfo.lineItemId = order.lineitems[0].id
    returnOrderInfo.returnQuantity = returnInfo.returnQuantity
    returnOrderInfo.createdAt = datetime.datetime.now()
    if itemCondition=="DAMAGED":
        returnOrderInfo.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_REQUEST_RECEIVED]
    else:
        returnOrderInfo.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_REQUEST_RECEIVED]
    returnOrderInfo.masterOrderId = order.logisticsTransactionId
    returnOrderInfo.warehouse_id = order.warehouse_id
    returnOrderInfo.returnAction = returnInfo.returnAction
    returnOrderInfo.freebieItemId = order.freebieItemId
    returnOrderInfo.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.PENDING]
    session.commit()
    
    return returnOrderInfo

def get_return_order_info(returnOrderId):
    returnOrder = ReturnOrderInfo.get_by(id=returnOrderId)
    if not returnOrder:
        return None
    return returnOrder

def refund_return_order(returnOrderInfo, attrList):
    returnOrder = ReturnOrderInfo.get_by(id=returnOrderInfo.id)
    if returnOrder.returnStatus not in (OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_CERT_VALID], OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_CERT_INVALID], OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_LOST_IN_TRANSIT],OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RECEIVED_DAMAGED] ,OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_LOST_IN_TRANSIT], OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_UNUSABLE], OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_USABLE],OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_RECEIVED_DAMAGED]):
        print "************returnig false staus check"
        return False
    if returnOrder.returnAction == ReturnAction.REFUND:
        returnOrder.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.REFUND_IN_PROCESS]
    else:
        returnOrder.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.REPLACEMENT_ORDER_CREATED]
    detailsMap = {}
    if returnOrder.refundedAt or returnOrder.replacementOrderId:
        raise TransactionServiceException(145, "Refund/Replacemet already in Process for this return order")
    if returnOrder.receivedQuantity != returnOrder.returnQuantity:
        raise TransactionServiceException(145, "Quantity mismatch Return vs Received")
    detailsMap['RefundReason'] = returnOrderInfo.refundReason
    detailsMap['RefundType'] = returnOrderInfo.refundType
    detailsMap['RefundedBy'] = returnOrderInfo.refundedBy
    detailsMap['RefundAmount'] = returnOrderInfo.refundAmount
    detailsMap['RefundDescription'] = returnOrderInfo.refundDescription
    print detailsMap
    print "*************************8"
    if(not __process_return_order(returnOrder, detailsMap)):
        raise TransactionServiceException(145, "Issue in __process_return_order")
    returnOrder.processedAt = datetime.datetime.now()
    __markReturnTransactionAsProcessed(returnOrder.returnTransaction_id)
    set_order_attributes(returnOrder.orderId, attrList)
    return True

def __markReturnTransactionAsProcessed(returnTransactionId):
    returnTransaction = ReturnTransaction.get_by(id=returnTransactionId)
    if returnTransaction is None:
        raise TransactionServiceException(101, "No Return Transaction Found")
    unprocessedOrders = []
    for retOrder in returnTransaction.returnOrders:
        if retOrder.reshippedAt is None and retOrder.refundedAt is None:
            unprocessedOrders.append(retOrder)
    if len(unprocessedOrders) == 0:
        returnTransaction.processedAt = datetime.datetime.now()
        returnTransaction.status = ReturnTransactionStatus._VALUES_TO_NAMES[ReturnTransactionStatus.PROCESSED]
        session.commit()


def get_return_orders_info_map(order_ids=None, statuses=None):
    returnOrdersInfoQuery = ReturnOrderInfo.query
    if order_ids is not None and len(order_ids)>0:
        returnOrdersInfoQuery = returnOrdersInfoQuery.filter(ReturnOrderInfo.orderId.in_(tuple(order_ids)))
    if statuses is not None:
        returnOrdersInfoQuery = returnOrdersInfoQuery.filter(ReturnOrderInfo.returnStatus.in_(tuple(statuses)))
    allReturnOrdersList = returnOrdersInfoQuery.all()
    returnOrdersInfoMap = {}
    for returnOrder in allReturnOrdersList:
        if returnOrdersInfoMap.has_key(returnOrder.orderId):
            returnList = returnOrdersInfoMap.get(returnOrder.orderId)
            returnList.append(returnOrder)
            returnOrdersInfoMap[returnOrder.orderId] = returnList
        else:
            returnList = []
            returnList.append(returnOrder)
            returnOrdersInfoMap[returnOrder.orderId] = returnList
            
    return returnOrdersInfoMap

def update_return_order_info(returnInfo):
    try:
        returnOrderInfo = get_return_order_info(returnInfo.id)
        returnOrderInfo.returnStatus = returnInfo.returnStatus
        if returnInfo.processedAt:
            returnOrderInfo.processedAt = to_py_date(returnInfo.processedAt)
        if returnInfo.reshippedAt:
            returnOrderInfo.reshippedAt = to_py_date(returnInfo.reshippedAt)
        if returnInfo.receivedAt:
            returnOrderInfo.receivedAt = to_py_date(returnInfo.receivedAt)
        if returnInfo.authorizedAt:
            returnOrderInfo.authorizedAt = to_py_date(returnInfo.authorizedAt)
        if returnInfo.refundAmount:
            returnOrderInfo.refundAmount = returnInfo.refundAmount
        if returnInfo.refundReason:
            returnOrderInfo.refundReason = returnInfo.refundReason
        if returnInfo.refundedAt:
            returnOrderInfo.refundedAt = to_py_date(returnInfo.refundedAt)
        if returnInfo.refundedBy:
            returnOrderInfo.refundedBy = returnInfo.refundedBy
        if returnInfo.refundType:
            returnOrderInfo.refundType = returnInfo.refundType
        if returnInfo.refundDescription:
            returnOrderInfo.refundDescription = returnInfo.refundDescription
        if returnInfo.logisticsRequestId:
            returnOrderInfo.logisticsRequestId = returnInfo.logisticsRequestId 
        if returnOrderInfo.shippingType != returnInfo.shippingType:
            returnOrderInfo.shippingType = returnInfo.shippingType
            if returnOrderInfo.replacementOrderId and returnInfo.shippingType:
                replacementorder = get_order(returnOrderInfo.replacementOrderId)
                replacementorder.promised_shipping_time = None
                replacementorder.promised_delivery_time = None
                replacementorder.transaction = None
                replacementorder.expected_delivery_time = None
                replacementorder.expected_shipping_time = None
        if returnInfo.replacementOrderId:
            returnOrderInfo.replacementOrderId = returnInfo.replacementOrderId       
        if returnOrderInfo.returnPickupType != returnInfo.returnPickupType:
            if returnOrderInfo.logisticsRequestId:
                returnpickupRequest = ReturnPickupRequest.get_by(id=returnOrderInfo.logisticsRequestId)
                if returnpickupRequest.pickupStatus in ['PICKUP_REQUESTED','PENDING']:
                    returnOrderInfo.returnPickupType = returnInfo.returnPickupType
            else:
                returnOrderInfo.returnPickupType = returnInfo.returnPickupType
        else:
            return False
        if returnOrderInfo.returnAction != returnInfo.returnAction:
            returnOrderInfo.returnAction = returnInfo.returnAction
        session.commit()
        return True
    except:
        return False
    
def bulk_update_return_order_info(orderReturnInfosMap):
    responseMap = {}
    for orderId, returnOrdersList in orderReturnInfosMap.items():
        for returnOrder in returnOrdersList:
            response = update_return_order_info(returnOrder)
            responseMap[orderId] = response
    return responseMap

def get_return_orders_as_per_warehouse(warehouseId):
    all_return_orders_as_per_warehouse = ReturnOrderInfo.query.filter(ReturnOrderInfo.processedAt==None).filter(ReturnOrderInfo.warehouse_id <= warehouseId).all()
    if all_return_orders_as_per_warehouse is None:
        all_return_orders_as_per_warehouse = []
    return all_return_orders_as_per_warehouse

def create_return_transaction(returnTransaction, itemCondition, overrideWarranty):
    t_orders = returnTransaction.returnOrders
    if not t_orders:
        raise TransactionServiceException(101, "Return Orders missing from the transaction")
    
    returnOrdersMap = {}
    for tOrder in t_orders:
        returnOrdersMap[tOrder.orderId] = tOrder.returnQuantity
    
    if validate_return_transaction(returnTransaction.customer_id, returnOrdersMap, itemCondition, overrideWarranty):
        returnTxn = ReturnTransaction()
        returnTxn.customer_id = returnTransaction.customer_id
        returnTxn.status = ReturnTransactionStatus._VALUES_TO_NAMES[ReturnTransactionStatus.INITIATED]
        returnTxn.customer_email = returnTransaction.customer_email
        returnTxn.customer_name = returnTransaction.customer_name
        returnTxn.createdAt = datetime.datetime.now()
        returnTxn.address_id = returnTransaction.address_id
        returnTxn.customerAddress = returnTransaction.customerAddress
        returnTxn.customerCity = returnTransaction.customerCity
        returnTxn.customerPhone = returnTransaction.customerPhone
        returnTxn.customerState = returnTransaction.customerState
        returnTxn.pincode = returnTransaction.pincode
        for t_order in t_orders:
            order = get_order(t_order.orderId)
            returnOrder = create_return_order_info(t_order, order, itemCondition)
            returnOrder.returnTransaction = returnTxn
            lineitem = order.lineitems[0]
            lineitem.returnQty = lineitem.returnQty + returnOrder.returnQuantity
            order.status = OrderStatus.PARTIAL_RETURN_IN_PROCESS
            order.statusDescription = "Partial Return In Process"
        
            crmServiceClient = CRMClient().get_client()
            ticket =Ticket()
            activity = Activity()
            
            description = "Creating Ticket for return requested by customer:- " + str(returnTransaction.customer_name)  + " Return Transaction Id:- "+ str(returnTxn.id)
            ticket.creatorId = 1
            ticket.assigneeId = 34
            ticket.category = TicketCategory.RETURN_FORM
            ticket.priority = TicketPriority.HIGH
            ticket.status = TicketStatus.OPEN
            ticket.description = description
            ticket.orderId = order.id
            
            activity.creatorId = 1
            activity.ticketAssigneeId = ticket.assigneeId
            activity.type = ActivityType.OTHER
            activity.description = description
            activity.ticketCategory = ticket.category
            activity.ticketDescription = ticket.description
            activity.ticketPriority = ticket.priority
            activity.ticketStatus = ticket.status
            
            ticket.customerId= returnTransaction.customer_id
            ticket.customerEmailId = returnTransaction.customer_email
            ticket.customerMobileNumber = returnTransaction.customerPhone
            ticket.customerName = returnTransaction.customer_name
            activity.customerId = ticket.customerId
            activity.customerEmailId = returnTransaction.customer_email
            activity.customerMobileNumber = returnTransaction.customerPhone
            activity.customerName = returnTransaction.customer_name
            
            returnTxn.ticketId = crmServiceClient.insertTicket(ticket, activity)

        session.commit()
        return returnTxn
    return ReturnTransaction()

def get_return_transactions_for_customer(statusList, customerMobile, customerEmail, returnTransactionId, customer_id):
    query = ReturnTransaction.query
    if statusList is not None and len(statusList) >0:
        statuses = []
        for status in statusList:
            statuses.append(ReturnTransactionStatus._VALUES_TO_NAMES[status])
        query = query.filter(ReturnTransaction.status.in_(tuple(statuses)))
    if customer_id:
        query = query.filter(ReturnTransaction.customer_id == customer_id)
    if customerMobile:
        query = query.filter(ReturnTransaction.customerPhone == customerMobile)
    if customerEmail:
        query = query.filter(ReturnTransaction.customer_email == customerEmail)
    return query.all()

def get_return_transaction(transaction_id):
    transaction = ReturnTransaction.get_by(id=transaction_id)
    if not transaction:
        raise TransactionServiceException(108, "no such transaction")
    return transaction

def get_return_orders_for_return_transaction(returnTransactionId):
    returnOrders = ReturnOrderInfo.query.filter_by(returnTransactionId=returnTransactionId).all()
    
    if not returnOrders:
        raise TransactionServiceException(101, "No return order for the transaction")
    return returnOrders

def __process_return_order(returnOrder, returnOrderAttrMap):
    if returnOrder.returnAction:
        if returnOrder.refundedAt:
            raise TransactionServiceException(145, "Refund already in Process for this return order")
        refundReason = returnOrderAttrMap.get('RefundReason')
        refundType = returnOrderAttrMap.get('RefundType')
        refundedBy = returnOrderAttrMap.get('RefundedBy')
        refundAmount = returnOrderAttrMap.get('RefundAmount')
        order = get_order(returnOrder.orderId)
        status_transition = {}
        if order.cod:
            logging.info("Refunding COD order with status " + str(returnOrder.returnStatus))

        else:
            logging.info("Refunding Prepaid order with status " + str(returnOrder.returnStatus))
        status_transition = {"LOST_IN_TRANSIT" : "LOST_IN_TRANSIT_REFUNDED",
                     "DOA_CERT_INVALID" : "DOA_INVALID_REFUNDED",
                     "DOA_CERT_VALID" : "DOA_VALID_REFUNDED",
                     "DOA_RECEIVED_DAMAGED" : "DOA_REFUNDED_RCVD_DAMAGED",
                     "DOA_LOST_IN_TRANSIT" : "DOA_REFUNDED_LOST_IN_TRANSIT",
                     "RET_PRODUCT_UNUSABLE" : "RET_PRODUCT_UNUSABLE_REFUNDED",
                     "RET_PRODUCT_USABLE" : "RET_PRODUCT_USABLE_REFUNDED",
                     "RET_RECEIVED_DAMAGED" : "RET_REFUNDED_RCVD_DAMAGED",
                     "RET_LOST_IN_TRANSIT" : "RET_REFUNDED_LOST_IN_TRANSIT"
                     }
        
        if returnOrder.returnStatus in status_transition.keys():
            returnOrder.returnStatus = status_transition[returnOrder.returnStatus] 
        elif returnOrder.returnPickupType in [ReturnPickupType.LATER, ReturnPickupType.NOT_REQUIRED]:
            if returnOrder.returnPickupType == ReturnPickupType.LATER:
                returnOrder.refundDescription = "Pickup will be processed Later. Refund in Process. It takes 7-10 working days to be in actual credit."
            else:
                returnOrder.refundDescription = "No Pickup Required for this item. Refund in Process. It takes 7-10 working days to be in actual credit."
        else:
            raise TransactionServiceException(114, "This order can't be refunded")
        
        __create_refund(order, refundAmount, 'Refunded against #{0},  {1}pc(s)'.format(order.id, returnOrder.receivedQuantity))
                
        returnOrder.refundedAt = datetime.datetime.now()
        returnOrder.refundedBy = refundedBy
        returnOrder.refundReason = refundReason
        returnOrder.refundAmount = refundAmount
        returnOrder.refundType = refundType
        session.commit()
        return True      
        
    else:
        alreadyReshipped = False
        if returnOrder.replacementOrderId:
            alreadyReshipped = True
        if returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.LOST_IN_TRANSIT]:
            returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.LOST_IN_TRANSIT_RESHIPPED]
            if not alreadyReshipped:
                order = get_order(returnOrder.orderId)
                new_order = __clone_order(order, False, order.cod)
        elif returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_CERT_INVALID]:
            returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_INVALID_RESHIPPED]
            if not alreadyReshipped:
                order = get_order(returnOrder.orderId)
                new_order = __clone_order(order, True, False)
            __scan_for_reship_order(returnOrder.orderId, new_order, returnOrder)
        elif returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_CERT_VALID]:
            returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_VALID_RESHIPPED]
            if not alreadyReshipped:
                order = get_order(returnOrder.orderId)
                new_order = __clone_order(order, False, False)
        elif returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RECEIVED_DAMAGED]:
            returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RESHIPPED_RCVD_DAMAGED]
            if not alreadyReshipped:
                order = get_order(returnOrder.orderId)
                new_order = __clone_order(order, False, False)
        elif returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_LOST_IN_TRANSIT]:
            returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RESHIPPED_LOST_IN_TRANSIT]
            if not alreadyReshipped:
                order = get_order(returnOrder.orderId)
                new_order = __clone_order(order, False, False)
        elif returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_USABLE]:
            returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_USABLE_RESHIPPED]
            if not alreadyReshipped:
                order = get_order(returnOrder.orderId)
                new_order = __clone_order(order, False, False)
        elif returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_UNUSABLE]:
            returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_UNUSABLE_RESHIPPED]
            if not alreadyReshipped:
                order = get_order(returnOrder.orderId)
                new_order = __clone_order(order, False, False)
        elif returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_RECEIVED_DAMAGED]:
            returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_RESHIPPED_RCVD_DAMAGED]
            if not alreadyReshipped:
                order = get_order(returnOrder.orderId)
                new_order = __clone_order(order, False, False)
        elif returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_LOST_IN_TRANSIT]:
            returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_RESHIPPED_LOST_IN_TRANSIT]
            if not alreadyReshipped:
                order = get_order(returnOrder.orderId)
                new_order = __clone_order(order, False, False)
        elif returnOrder.returnPickupType in [ReturnPickupType.LATER, ReturnPickupType.NOT_REQUIRED]:
            if not alreadyReshipped:
                order = get_order(returnOrder.orderId)
                new_order = __clone_order(order, False, False)
                returnOrder.refundDescription = "Replacement Order has been created."
        else:
            return False
        
        if not alreadyReshipped:
            returnOrder.replacementOrderId = new_order.id
            returnOrder.reshippedAt = datetime.datetime.now()
            orderQty = returnOrder.returnQuantity 
            if returnOrder.returnPickupType not in [ReturnPickupType.LATER, ReturnPickupType.NOT_REQUIRED]:
                orderQty = returnOrder.receivedQuantity
            new_order.lineitems[0].quantity = orderQty
            new_order.lineitems[0].total_price = new_order.lineitems[0].unit_price * orderQty
            new_order.lineitems[0].total_weight = new_order.lineitems[0].unit_weight * orderQty
            new_order.total_amount = new_order.lineitems[0].unit_price * orderQty
            new_order.total_weight = new_order.lineitems[0].unit_weight * orderQty
            if returnOrder.shippingType:
                new_order.transaction = None
                new_order.promised_shipping_time = None
                new_order.expected_shipping_time = None
                new_order.promised_delivery_time = None
                new_order.expected_delivery_time = None
                
            session.commit()
            return True
        else:
            session.commit()
            return True

def communicate_pickup_details_to_customer(returnPickupRequest, returnOrders):
    returnTxn = returnOrders[0].returnTransaction
    subject = 'Pickup details for '+str(returnTxn.id)
    filename = "/PickupDetails/" +  str(returnPickupRequest.pickupRequestNo) +".pdf" 
    
    try:
        inventory_client = InventoryClient().get_client()
        warehouse = inventory_client.getWarehouse(returnOrders[0].warehouse_id)
        
        logistics_client = LogisticsClient().get_client()
        provider = logistics_client.getProvider(returnPickupRequest.logisticsProviderId)
        provider_name = provider.name
        
        to_addr = returnTxn.customer_email
        promised_pickup_time = returnPickupRequest.promised_pickup_timestamp.date().strftime("%d-%B-%Y (%A)")
        customer_address = returnTxn.customer_name +"\n" + returnTxn.customerAddress +"\n"
        customer_address = customer_address + returnTxn.customerCity + "\n"
        customer_address = customer_address + returnTxn.customerState + "\n"
        customer_address = customer_address + "PIN: "+returnTxn.pincode + "\n"
        customer_address = customer_address + "Phone: "+returnTxn.customerPhone
        
        raw_message = '''
        Dear %(customer_name)s,
        
        Would like to inform you that we have instructed %(provider_name)s to pick up the material from the below mentioned address on or before %(date)s. %(provider_name)s pickup request number is %(pickup_request_no)s.'
        
        Take a printout of the attachment in this mail which contains the delivery address and paste it on the packed parcel. Kindly keep the parcel ready.

        Pickup will happen from the below mentioned address:-

        %(customer_address)s

        Do let us know in case of any concerns

        Thanks & Regards
        %(source_name)s Team
        '''
        message = dedent(raw_message) % {'customer_name' : returnTxn.customer_name,
                                         'order_id' : returnTxn.id,
                                         'provider_name' : provider_name,
                                         'date': promised_pickup_time,
                                         'pickup_request_no' : returnPickupRequest.pickupRequestNo,
                                         'customer_address' : customer_address,
                                         'source_name' : source_name}
        print message
        __generate_return_transaction_invoice(returnOrders, warehouse, provider, filename)
        mail(help_user, help_password, [to_addr], subject, message, [get_attachment_part(filename)])
        session.commit()
        return True
    except Exception as e:
        print sys.exc_info()
        traceback.print_tb(sys.exc_info()[2])
        return False
    finally:
        if os.path.exists(filename):
            os.remove(filename)    
        
def __communicate_customer_for_closed_transaction(returnTransaction):
    pass

def change_return_transaction_status(returnTransactionId, new_status, returnOrdersIds=None, returnOrdersAttrMap=None):
    returnTransaction = ReturnTransaction.get_by(id=returnTransactionId)
    if returnTransaction is None:
        raise TransactionServiceException(101, "No Return Transaction Found")
    if new_status == ReturnTransactionStatus.AUTHORIZED:
        returnTransaction.authorizedAt = datetime.datetime.now()
        for returnOrder in returnTransaction.returnOrders:
            if returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_REQUEST_RECEIVED]:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_REQUEST_AUTHORIZED]
            if returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_REQUEST_RECEIVED]:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_REQUEST_AUTHORIZED]
        returnTransaction.status = ReturnTransactionStatus._VALUES_TO_NAMES[new_status]
    elif new_status == ReturnTransactionStatus.INPROCESS:
        for returnOrder in returnTransaction.returnOrders:
            returnOrder.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.AWAITING_PICKUP]
            if returnOrder.returnPickupType == ReturnPickupType.NOT_REQUIRED:
                pass
            else:
                if returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_REQUEST_AUTHORIZED]:
                    returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PICKUP_REQUEST_RAISED]
                if returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_REQUEST_AUTHORIZED]:
                    returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_PICKUP_REQUEST_RAISED]
            returnTransaction.status = ReturnTransactionStatus._VALUES_TO_NAMES[new_status] 
        returnTransaction.status = ReturnTransactionStatus._VALUES_TO_NAMES[new_status]
    elif new_status == ReturnTransactionStatus.REJECTED:
        returnTransaction.processedAt = datetime.datetime.now()
        for returnOrder in returnTransaction.returnOrders:
            returnOrder.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.CLOSED]
            if returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_REQUEST_RECEIVED]:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.REJECTED]
            if returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_REQUEST_RECEIVED]:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.REJECTED]
        returnTransaction.status = ReturnTransactionStatus._VALUES_TO_NAMES[new_status] 
    elif new_status == ReturnTransactionStatus.PROCESSED:
        if returnOrdersIds and len(returnOrdersIds)>0:
            unprocessedOrders = []   
            for retOrder in returnTransaction.returnOrders:
                if retOrder.reshippedAt is None or retOrder.refundedAt is None:
                    unprocessedOrders.append(retOrder)
            if len(returnOrdersIds) < len(returnTransaction.returnOrders):
                if len(returnOrdersIds) == len(unprocessedOrders):
                    for ret_Order in unprocessedOrders:
                        if ret_Order.returnAction == ReturnAction.REFUND:
                            ret_Order.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.REFUND_IN_PROCESS]
                        else:
                            ret_Order.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.REPLACEMENT_ORDER_CREATED]
                    returnTransaction.processedAt = datetime.datetime.now()
                    returnTransaction.status = ReturnTransactionStatus._VALUES_TO_NAMES[new_status] 
            else:
                for ret_Order in returnTransaction.returnOrders:
                    if ret_Order.returnAction == ReturnAction.REFUND:
                        ret_Order.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.REFUND_IN_PROCESS]
                    else:
                        ret_Order.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.REPLACEMENT_ORDER_CREATED]
                returnTransaction.processedAt = datetime.datetime.now()
                returnTransaction.status = ReturnTransactionStatus._VALUES_TO_NAMES[new_status]
            for returnOrderId in returnOrdersIds:
                __process_return_order(get_return_order_info(returnOrderId), returnOrdersAttrMap.get(returnOrderId))
                
    elif new_status == ReturnTransactionStatus.COMPLETED:
        mark_return_transaction_complete(returnTransaction.id)            
     
    elif new_status == ReturnTransactionStatus.CLOSED:
        if not returnTransaction.status == ReturnTransactionStatus._VALUES_TO_NAMES[ReturnTransactionStatus.COMPLETED]:
            raise
        returnTransaction.closedAt = datetime.datetime.now()
        for ret_Order in returnTransaction.returnOrders:
            ret_Order.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.CLOSED]
        __communicate_customer_for_closed_transaction(returnTransaction)
        returnTransaction.status = ReturnTransactionStatus._VALUES_TO_NAMES[new_status] 
     
    session.commit()
    return True

    
def create_return_pickup_request(returnOrderIds):
    returnPickupRequest = ReturnPickupRequest()
    returnPickupRequest.pickupStatus = ReturnTxnPickupStatus._VALUES_TO_NAMES[ReturnTxnPickupStatus.PENDING]

    if len(returnOrderIds) > 0:
        for returnOrderId in returnOrderIds:
            returnOrder = get_return_order_info(returnOrderId)
            returnOrder.logisticsRequestId = returnPickupRequest.id
        session.commit()
        return returnPickupRequest.id

    return 0

def update_return_pickup_request(returnPickupRequest):
    print returnPickupRequest
    try:
        returnPickupReq = ReturnPickupRequest.get_by(id=returnPickupRequest.id)
        if returnPickupRequest.pickupStatus == ReturnTxnPickupStatus.PICKUP_REQUESTED:
            returnPickupReq.pickupStatus = ReturnTxnPickupStatus._VALUES_TO_NAMES[returnPickupRequest.pickupStatus]
        
        if returnPickupRequest.pickupStatus == ReturnTxnPickupStatus.PICKUP_CONFIRMED:
            returnPickupReq.logisticsProviderId = returnPickupRequest.logisticsProviderId
            returnPickupReq.pickupRequestNo = returnPickupRequest.pickupRequestNo
            returnPickupReq.confirmedAt = datetime.datetime.now()
            returnPickupReq.pickupStatus = ReturnTxnPickupStatus._VALUES_TO_NAMES[returnPickupRequest.pickupStatus]
            for returnOrder in ReturnOrderInfo.query.filter(ReturnOrderInfo.logisticsRequestId==returnPickupRequest.id).all():
                if returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_PICKUP_REQUEST_RAISED]:
                    returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_PICKUP_CONFIRMED]
                else:
                    returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PICKUP_CONFIRMED]
        if returnPickupRequest.pickupStatus == ReturnTxnPickupStatus.PICKUP_SCHEDULED:
            returnPickupReq.pickupStatus = ReturnTxnPickupStatus._VALUES_TO_NAMES[returnPickupRequest.pickupStatus]
            returnPickupReq.promised_pickup_timestamp = to_py_date(returnPickupRequest.promised_pickup_timestamp)
            returnOrdersMap = {}
            returnOrders = ReturnOrderInfo.query.filter(ReturnOrderInfo.logisticsRequestId==returnPickupRequest.id).all()
            for returnOrder in returnOrders:
                returnTxn = returnOrders[0].returnTransaction
                if returnOrdersMap.has_key(returnTxn.address_id):
                    retOrders = returnOrdersMap.get(returnTxn.address_id)
                    retOrders.append(returnOrder)
                    returnOrdersMap[returnTxn.address_id] = retOrders
                else:
                    retOrders = []
                    retOrders.append(returnOrder)
                    returnOrdersMap[returnTxn.address_id] = retOrders
            for returnOrdersList in returnOrdersMap.values():
                try:
                    communicate_pickup_details_to_customer(returnPickupReq, returnOrdersList)
                except:
                    print "Customer Communication Failed for the pickup Information"
        if returnPickupRequest.pickupStatus == ReturnTxnPickupStatus.PICKUP_IN_TRANSIT:
            returnPickupReq.pickedUpAt = to_py_date(returnPickupRequest.pickedUpAt)
            returnPickupReq.pickupStatus = ReturnTxnPickupStatus._VALUES_TO_NAMES[ReturnTxnPickupStatus.PICKUP_IN_TRANSIT]
            for returnOrder in ReturnOrderInfo.query.filter(ReturnOrderInfo.logisticsRequestId==returnPickupRequest.id).all():
                if returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_PICKUP_CONFIRMED]:
                    returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RETURN_IN_TRANSIT]
                else:
                    returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_RETURN_IN_TRANSIT]
        if returnPickupRequest.pickupStatus == ReturnTxnPickupStatus.PICKUP_RECEIVED:
            returnPickupReq.pickupStatus = ReturnTxnPickupStatus._VALUES_TO_NAMES[ReturnTxnPickupStatus.PICKUP_RECEIVED]
            for returnOrder in ReturnOrderInfo.query.filter(ReturnOrderInfo.logisticsRequestId==returnPickupRequest.id).all():
                if returnOrder.returnStatus not in (OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_CERT_VALID], OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_CERT_INVALID], OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_LOST_IN_TRANSIT], OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RECEIVED_DAMAGED],OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_CERT_VALID],OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_LOST_IN_TRANSIT], OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_UNUSABLE], OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_USABLE], OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_RECEIVED_DAMAGED]):
                    return False
                returnOrder.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.QUALITY_CHECK]
        session.commit()
        return True
    except:
        traceback.print_exc()
        return False 

def receive_return_pickup(returnOrdersMap, req_id):
    #returnPickupReq = ReturnPickupRequest.get_by(id=req_id)
    #returnOrders = ReturnOrderInfo.query.filter(ReturnOrderInfo.logisticsRequestId==req_id).all()
    #if len(returnOrders)!=len(returnOrdersMap.keys()):
    #    raise TransactionServiceException(101, "All Returns Orders  Information Missing. Please Fill All Orders Info")
    print "returnOrdersMap", returnOrdersMap
    for returnOrderId in returnOrdersMap.iterkeys():
        returnOrder = ReturnOrderInfo.get_by(id=returnOrderId)
        if returnOrder is None:
            raise TransactionServiceException(101, "Return order missing")
        returnOrderDetails = returnOrdersMap.get(returnOrder.id)
        receiveCondition = ReceivedReturnType._NAMES_TO_VALUES[returnOrderDetails.get('ReceiveCondition')]
        quantity = int(returnOrderDetails.get('Quantity'))
        returnOrder.receivedReturnType = receiveCondition 
        returnOrder.receivedReturnType = receiveCondition 
        if returnOrder.returnStatus in [OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_PICKUP_CONFIRMED], OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RETURN_IN_TRANSIT]]:
            if receiveCondition == ReceivedReturnType.PRESTINE:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RECEIVED_PRESTINE]
            elif receiveCondition == ReceivedReturnType.DAMAGED:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RECEIVED_DAMAGED]
            elif receiveCondition == ReceivedReturnType.LOST_IN_TRANSIT:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_LOST_IN_TRANSIT]
            returnOrder.receivedAt = datetime.datetime.now()
            returnOrder.receivedQuantity = quantity
        elif returnOrder.returnStatus in [OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PICKUP_CONFIRMED], OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_RETURN_IN_TRANSIT]]:
            if receiveCondition == ReceivedReturnType.PRESTINE:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_RECEIVED_PRESTINE]
            elif receiveCondition == ReceivedReturnType.DAMAGED:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_RECEIVED_DAMAGED]
            elif receiveCondition == ReceivedReturnType.LOST_IN_TRANSIT:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_LOST_IN_TRANSIT]
            returnOrder.receivedAt = datetime.datetime.now()
            returnOrder.receivedQuantity = quantity
        else:
            return False
    
    scanMap = {
            "RET_RECEIVED_PRESTINE" : ScanType.SALE_RET,
            "RET_RECEIVED_DAMAGED"  : ScanType.SALE_RET_UNUSABLE,
            "RET_LOST_IN_TRANSIT"   : ScanType.LOST_IN_TRANSIT,
            "DOA_RECEIVED_PRESTINE" : ScanType.DOA_IN,
            "DOA_RECEIVED_DAMAGED"  : ScanType.DOA_IN,
            "DOA_LOST_IN_TRANSIT"   : ScanType.LOST_IN_TRANSIT
        }
    inventoryClient = InventoryClient().get_client()
    warehouse = inventoryClient.getWarehouse(returnOrder.warehouse_id)
    warehouseClient = WarehouseClient().get_client()

    for returnOrderId in returnOrdersMap.iterkeys():
        returnOrder = ReturnOrderInfo.get_by(id=returnOrderId)    
        returnOrderDetails = returnOrdersMap.get(returnOrder.id)
        warehouse = inventoryClient.getWarehouse(returnOrder.warehouse_id) 
        
        serialNumbers = []
        if returnOrderDetails.has_key('SerialNumbers'):  
            serialNumbersSplitted = returnOrderDetails.get('SerialNumbers').split(',')
            serialNumbers = []
            for t in serialNumbersSplitted:
                if t.strip() !="":
                    serialNumbers.append(t.strip())
                    
        scanFreebie = False
        if returnOrderDetails.has_key('ScanFreebie') and returnOrderDetails.get('ScanFreebie')=='yes':
            scanFreebie = True
        quantity = int(returnOrderDetails.get('Quantity'))
           
        if warehouse is not None and warehouse.billingType == BillingType.OURS or warehouse.billingType == BillingType.OURS_EXTERNAL:
            if scanMap.has_key(returnOrder.returnStatus):
                scanType = scanMap[returnOrder.returnStatus]
                lineitem = returnOrder.lineitem
                return_qty = lineitem.returnQty - returnOrder.returnQuantity + quantity
                lineitem.returnQty = lineitem.returnQty - returnOrder.returnQuantity + quantity
                order = get_order(returnOrder.orderId)
                if return_qty < order.lineitems[0].quantity:
                    order.status = OrderStatus.PARTIAL_RETURN
                if return_qty == order.lineitems[0].quantity:
                    order.status = OrderStatus.COMPLETE_RETURN
                if warehouse.billingType == BillingType.OURS or scanType != ScanType.SALE_RET:
                    if lineitem.serial_number is not None:
                        if serialNumbers is None or len(serialNumbers)==0 or len(serialNumbers)<quantity:
                            return False
                        else:
                            for serialNumber in serialNumbers:
                                try:
                                    warehouseClient.scanSerializedItemForOrder(serialNumber, scanType, order.id, order.fulfilmentWarehouseId, 1, order.warehouse_id)
                                except:
                                    warehouseClient = WarehouseClient().get_client()
                                    warehouseClient.scanSerializedItemForOrder(serialNumber, scanType, order.id, order.fulfilmentWarehouseId, 1, order.warehouse_id)
                    else:
                        try:
                            warehouseClient.scanForOrder(None, scanType, quantity, order.id, order.fulfilmentWarehouseId, order.warehouse_id)
                        except:
                            warehouseClient = WarehouseClient().get_client()
                            warehouseClient.scanForOrder(None, scanType, quantity, order.id, order.fulfilmentWarehouseId, order.warehouse_id)
                if warehouse.billingType == BillingType.OURS_EXTERNAL and scanType == ScanType.SALE_RET:
                    try:
                        warehouseClient.scanForOursExternalSaleReturn(order.id, lineitem.transfer_price)
                    except:
                        warehouseClient = WarehouseClient().get_client()
                        warehouseClient.scanForOursExternalSaleReturn(order.id, lineitem.transfer_price)
                if scanFreebie:
                    try:
                        warehouseClient.scanfreebie(order.id, order.freebieItemId, 0, scanType)
                    except:
                        warehouseClient = WarehouseClient().get_client()
                        warehouseClient.scanfreebie(order.id, order.freebieItemId, 0, scanType)
     
    
        isValid = returnOrderDetails.get('productUsable')
        if returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RECEIVED_PRESTINE]:
            if isValid == "true":
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_CERT_VALID]
            else:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_CERT_INVALID]
                
        elif returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_RECEIVED_PRESTINE]:
            if isValid == "true":
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_USABLE]
            else:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_UNUSABLE]
    session.commit()  
    return True
    

def validate_return_pickup(returnPickupId, returnOrdersMap):
    returnOrders = ReturnOrderInfo.query.filter(ReturnOrderInfo.logisticsRequestId==returnPickupId).all()
    if len(returnOrders)!=len(returnOrdersMap.keys()):
        raise TransactionServiceException(101, "All Returns Orders Information Missing. Please Fill All Orders Info")
    
    for returnOrder in returnOrders:        
        if returnOrder.returnStatus != OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RECEIVED_PRESTINE] and returnOrder.returnStatus != OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_RECEIVED_PRESTINE]:
            return False
    
    for returnOrder in returnOrders:
        isValid = returnOrdersMap.get(returnOrder.id)
        if returnOrder.returnStatus == OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_RECEIVED_PRESTINE]:
            if isValid:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_CERT_VALID]
            else:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_CERT_INVALID]
        else:
            if isValid:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_USABLE]
            else:
                returnOrder.returnStatus = OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PRODUCT_UNUSABLE]
    session.commit()
               
    return True

def process_return_pickup(returnPickupId, returnOrdersMap):
    returnOrders = ReturnOrderInfo.query.filter(ReturnOrderInfo.logisticsRequestId==returnPickupId).all()
    returnTxnIdOrdersMap = {} 
    if len(returnOrders)!=len(returnOrdersMap.keys()):
        raise TransactionServiceException(101, "All Returns Orders Information Missing. Please Fill All Orders Info")
    
    for returnOrder in returnOrders:
        if returnTxnIdOrdersMap.has_key(returnOrder.returnTransaction.id):
            ordersList = returnTxnIdOrdersMap.get(returnOrder.returnTransaction.id)
            ordersList.append(returnOrder.id)
            returnTxnIdOrdersMap[returnOrder.returnTransaction.id] = ordersList
        else:
            ordersList = []
            ordersList.append(returnOrder.id)
            returnTxnIdOrdersMap[returnOrder.returnTransaction.id] = ordersList
        
    for returnTransactionId, returnOrdersIds in returnTxnIdOrdersMap.items():
        change_return_transaction_status(returnTransactionId, ReturnTransactionStatus.PROCESSED, returnOrdersIds, returnOrdersMap)
        
    session.commit()
    return True

def mark_return_transaction_complete(returnTransactionId):
    returnTransaction = get_return_transaction(returnTransactionId)
    if returnTransaction.status == ReturnTransactionStatus._VALUES_TO_NAMES[ReturnTransactionStatus.COMPLETED]:
        raise TransactionServiceException(101, "Transaction Already completed")
    
    returnString = ''
    for returnOrder in returnTransaction.returnOrders:
        if returnOrder.processedAt is None:
            returnString = returnString + str(returnOrder.id)+":"+str(returnOrder.lineitem)+"|"
    
    if len(returnString) >0 :
        raise TransactionServiceException(140, "Unprocessed orders are: "+returnString)
    
    returnTransaction.status = ReturnTransactionStatus._VALUES_TO_NAMES[ReturnTransactionStatus.COMPLETED]
        
    session.commit()
    return True

def refund_return_transaction_payment(returnOrdersMap, returnTransactionId):
    for returnOrderId, returnOrderAttrMap in returnOrdersMap.items():
        returnOrder = get_return_order_info(returnOrderId)
        if returnOrder.returnAction:
            returnOrder.processedAt = datetime.datetime.now()
            returnOrder.refundDescription = returnOrder.refundDescription + returnOrderAttrMap.get("RefundInfo")
            returnOrder.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.REFUND_DONE]
        else:
            returnOrder.processedAt = datetime.datetime.now()
            returnOrder.refundDescription = str(returnOrder.replacementOrderId) + " has been delivered now"
            returnOrder.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.REPLACEMENT_DONE]
    session.commit()
    
def get_eligible_orders_for_return(customerId, itemCondition, overrideWarranty):
    query = session.query(Order).join((LineItem,Order.id==LineItem.order_id)).filter(Order.customer_id==customerId).filter(Order.delivery_timestamp != None).filter(Order.status.in_(tuple([OrderStatus.DELIVERY_SUCCESS,OrderStatus.PARTIAL_RETURN,OrderStatus.PARTIAL_RETURN_IN_PROCESS]))).filter(LineItem.quantity-LineItem.returnQty>0)
    if not overrideWarranty and itemCondition is not None:
        if itemCondition == 'DAMAGED':
            query = query.filter(LineItem.damaged_expiry_timestamp > datetime.datetime.now())
        elif itemCondition == 'DEFECTIVE':
            query = query.filter(LineItem.warranty_expiry_timestamp > datetime.datetime.now())

    orders = query.order_by(desc(Order.delivery_timestamp)).all()
    return orders
        
def get_eligible_return_orders_for_pickup(customerId):
    return session.query(ReturnOrderInfo).join((ReturnTransaction,ReturnOrderInfo.returnTransaction_id==ReturnTransaction.id)).filter(ReturnTransaction.customer_id==customerId).filter(or_(ReturnOrderInfo.returnStatus==OrderStatus._VALUES_TO_NAMES[OrderStatus.RET_PICKUP_REQUEST_RAISED], ReturnOrderInfo.returnStatus==OrderStatus._VALUES_TO_NAMES[OrderStatus.DOA_PICKUP_REQUEST_RAISED])).filter(ReturnOrderInfo.logisticsRequestId==None).all()

def validate_return_transaction(customerId, returnOrdersMap, itemCondition, overrideWarranty):
    eligibleOrders = get_eligible_orders_for_return(customerId, itemCondition, overrideWarranty)

    ordersMap = {}
    for order in eligibleOrders:
        ordersMap[order.id] = order.lineitems[0].quantity - order.lineitems[0].returnQty

    for orderId, returnOrderQty in returnOrdersMap.items():
        if returnOrderQty > ordersMap.get(orderId):
            return False
    return True

def get_return_pickup_request(returnPickupId):
    return ReturnPickupRequest.get_by(id=returnPickupId)

def mark_return_not_required_orders_as_processed(t_returnOrderInfo):
    returnOrderInfo = ReturnOrderInfo.get_by(id=t_returnOrderInfo.id)
    if returnOrderInfo is None:
        raise TransactionServiceException(101, "No Return Order Found")

    if returnOrderInfo.returnPickupType == ReturnPickupType.NOT_REQUIRED:
        if returnOrderInfo.returnAction == ReturnAction.REPLACEMENT  or t_returnOrderInfo.refundType!= RefundType.WALLET:
            raise TransactionServiceException(101, "Refund Type not Defined for return order:- "+str(returnOrderInfo.id))

    detailsMap = {}
    if returnOrderInfo.refundedAt :
        raise TransactionServiceException(145, "Refund already in Process for this return order")
    detailsMap['RefundReason'] = "As per Customer Request"
    detailsMap['RefundType'] = t_returnOrderInfo.refundType
    detailsMap['RefundedBy'] = t_returnOrderInfo.refundedBy
    detailsMap['RefundAmount'] = t_returnOrderInfo.refundAmount
    returnOrderInfo.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.REFUND_IN_PROCESS]
    if(not __process_return_order(returnOrderInfo, detailsMap)):
        raise TransactionServiceException(145, "Issue in __process_return_order")
    returnOrderInfo.processedAt = datetime.datetime.now()
    returnOrderInfo.returnTxnResolutionStatus = ReturnTxnResolutionStatus._VALUES_TO_NAMES[ReturnTxnResolutionStatus.REFUND_DONE]
    session.commit()
    __markReturnTransactionAsProcessed(returnOrderInfo.returnTransaction_id)
    return True


def get_all_return_orders_for_return_pickup_request(logisticsRequestId):
    return ReturnOrderInfo.query.filter(ReturnOrderInfo.logisticsRequestId==logisticsRequestId).all()

def __clone_return_order(returnOrder, splitOrderQty):
    if returnOrder.returnQuantity <= splitOrderQty or splitOrderQty<=0:
        raise TransactionServiceException(101, "SplitQty canot be equal to or greater than parent return order returnQty")
    returnOrder.returnQuantity = returnOrder.returnQuantity - splitOrderQty 
    new_return_order = ReturnOrderInfo()
    new_return_order.returnTransaction = returnOrder.returnTransaction
    new_return_order.masterOrderId = returnOrder.masterOrderId
    new_return_order.orderId = returnOrder.orderId
    new_return_order.lineItemId = returnOrder.lineItemId
    new_return_order.logisticsRequestId = returnOrder.logisticsRequestId
    new_return_order.returnQuantity = splitOrderQty
    new_return_order.receivedQuantity = returnOrder.receivedQuantity
    new_return_order.createdAt = returnOrder.createdAt
    new_return_order.processedAt = returnOrder.processedAt
    new_return_order.returnStatus = returnOrder.returnStatus
    new_return_order.reshippedAt = returnOrder.reshippedAt
    new_return_order.receivedAt = returnOrder.receivedAt
    new_return_order.refundReason = returnOrder.refundReason
    new_return_order.refundedBy = returnOrder.refundedBy
    new_return_order.refundedAt = returnOrder.refundedAt
    new_return_order.warehouse_id = returnOrder.warehouse_id
    new_return_order.refundAmount = returnOrder.refundAmount
    new_return_order.refundType = returnOrder.refundType
    new_return_order.refundDescription = returnOrder.refundDescription
    new_return_order.returnPickupType = returnOrder.returnPickupType
    new_return_order.shippingType = returnOrder.shippingType
    new_return_order.replacementOrderId = returnOrder.replacementOrderId
    new_return_order.receivedReturnType = returnOrder.receivedReturnType
    new_return_order.freebieItemId = returnOrder.freebieItemId
    new_return_order.returnAction = returnOrder.returnAction
    new_return_order.returnTxnResolutionStatus = returnOrder.returnTxnResolutionStatus
    new_return_order.lineitem = returnOrder.lineitem
    new_return_order.returnPickUpRequest = returnOrder.returnPickUpRequest
    return new_return_order

    
def get_seller_info(sellerId):
    sellerInfo = SellerInfo()
    seller = Seller.get_by(id=sellerId)
    if seller is None:
        return None
    else:
        sellerInfo.tin = seller.tin
        sellerInfo.gstin = seller.gstin
        sellerInfo.stateId = seller.state_id 
        org = Organisation.get_by(id=seller.organisation_id)
        if org is None:
            return None
        else:
            sellerInfo.organisationName = org.name
            sellerInfo.regId = org.registered_id
            sellerInfo.registeredAddress =  org.address
            if org.type == 'company':
                sellerInfo.cinNumber = org.registered_id
            return sellerInfo

def get_warehouse_address(addressId):
    address = WarehouseAddressMaster.get_by(id=addressId)
    return to_t_warehouse_address(address)

def split_return_order_info(returnOrderId, splitOrderQty):
    returnOrder = ReturnOrderInfo.get_by(id=returnOrderId)
    if returnOrder is None:
        raise TransactionServiceException(101, "No Return Order is there with this Order Id")
    new_return_order = __clone_return_order(returnOrder, splitOrderQty)
    session.commit()
    return new_return_order

#Seller would be used to get tin number
def __get_seller(warehouse_id):
        #As of now we have hardcoded the company and 
        #seller would be returned on basis warehouse and company
        #Seller changed to HSSPL. Effective Aug-1-2016 - Amit Gupta
        #New Organisation has been added to it.
        (sellerId,) = session.query(Seller.id).join((SellerWarehouse, Seller.id==SellerWarehouse.seller_id)).filter(SellerWarehouse.warehouse_id==warehouse_id).first()
        return sellerId

def add_shipment_delay(shipmentDelayDetail):
    returnList = []
    for delayObj in shipmentDelayDetail:
        order = get_order(delayObj.orderId)
        if order.status not in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.ACCEPTED]:
            returnList.append(delayObj.orderId)
            continue
        try:
            result = add_delay_reason(delayObj.orderId, delayObj.delayReason, delayObj.delay, delayObj.delayReasonText)
        except:
            returnList.append(delayObj.orderId)
            continue
        if not result:
            returnList.append(delayObj.orderId)
    return returnList

#Buyer can be anyone based on logic
def __get_buyer(warehouse_id):
        #As of now we have hardcoded the company and 
        #seller would be returned on basis warehouse and company
        buyerId=None
        result = session.query(Seller.id).join((SellerWarehouse, Seller.id==SellerWarehouse.seller_id)).filter(SellerWarehouse.warehouse_id==warehouse_id).first()
        if result:
            buyerId = result[0]
        return buyerId
    
def get_buyer_by_warehouse(warehouse_id):
        buyerId = __get_buyer(warehouse_id)
        if buyerId is None:
            return None
        sellerInfo = get_seller_info(buyerId)
        buyerInfo = BuyerInfo()
        buyerInfo.cinNumber = sellerInfo.cinNumber
        buyerInfo.organisationName = sellerInfo.organisationName
        buyerInfo.regId = sellerInfo.regId
        buyerInfo.tin = sellerInfo.tin
        buyerInfo.gstin = sellerInfo.gstin 
        buyerInfo.registeredAddress = sellerInfo.registeredAddress
        buyerInfo.buyerId = buyerId
        buyerInfo.stateId = sellerInfo.stateId 
        addressMapping = session.query(WarehouseAddressMaster).join((WarehouseAddressMapping,WarehouseAddressMapping.address_id==WarehouseAddressMaster.id)).filter(WarehouseAddressMapping.warehouse_id==warehouse_id).first()
        if addressMapping is not None:
            buyerInfo.addressId = addressMapping.id
            buyerInfo.buyerAddress = to_t_warehouse_address(addressMapping)
        else:
            return None
        print buyerInfo
        return buyerInfo


def get_cost_detail_for_logistics_txn_id(logisticsTxnId):
    logisticCostDetail= ShipmentLogisticsCostDetail.get_by(logisticsTransactionId=logisticsTxnId)
    if logisticCostDetail is None:
        raise
    return logisticCostDetail

##Shipment Logistics cost should be added only during billing
def add_shipment_logistic_detail(shipmentLogisticsCostDetail):
    if shipmentLogisticsCostDetail.logisticsTransactionId is None or shipmentLogisticsCostDetail.packageDimensions is None:
        raise ValueError("Null fields logisticsTransactionId/packageDimensions")
    
    logisticCostDetail= ShipmentLogisticsCostDetail.get_by(logisticsTransactionId=shipmentLogisticsCostDetail.logisticsTransactionId)
    if logisticCostDetail is not None:
        logisticCostDetail.packageDimensions = shipmentLogisticsCostDetail.packageDimensions
    else:
        d_ShipmentLogisticsCostDetail = ShipmentLogisticsCostDetail()
        d_ShipmentLogisticsCostDetail.logisticsTransactionId = shipmentLogisticsCostDetail.logisticsTransactionId
        d_ShipmentLogisticsCostDetail.packageDimensions = shipmentLogisticsCostDetail.packageDimensions
    session.commit()


def create_payment(userId, txnId, gatewayId):
    total_payable_amount = calculate_payment_amount(txnId)
    gv_amount = 0.0
    wallet_amount = 0.0
    transaction = Transaction.get_by(id=txnId)
    for order in transaction.orders:
        gv_amount = gv_amount + order.gvAmount
        wallet_amount = wallet_amount + order.wallet_amount
    payment_client = PaymentClient().get_client()
    if gv_amount > 0:
        paymentId = payment_client.createPayment(userId, gv_amount, gvGatewayId, txnId, False);
        payment_client.updatePaymentDetails(paymentId, "", "", "SUCCESS", "Payment Received", "", "", "", "", PaymentStatus.SUCCESS, "", None);
    if wallet_amount > 0:
        paymentId = payment_client.createPayment(userId, wallet_amount, walletGatewayId, txnId, False);
    if total_payable_amount > 0:
        paymentId = payment_client.createPayment(userId, total_payable_amount, gatewayId, txnId, False);
    return paymentId
    

def calculate_payment_amount(txnId):
    transaction = Transaction.get_by(id=txnId)
    miscCharges = MiscCharges.get_by(transaction_id = transaction.id)
    total_amount = 0.0
    for order in transaction.orders:
        if order.net_payable_amount is None:
            order.net_payable_amount = 0
        total_amount = total_amount + order.net_payable_amount
    if miscCharges and miscCharges.get(1) is not None:
        total_amount = total_amount + miscCharges.get(1)
    return total_amount

def get_billed_orders_for_manifest_gen(warehouse_id, logistics_provider_id, cod):
    orders =  Order.query.filter(Order.warehouse_id == warehouse_id)\
    .filter(Order.status == OrderStatus.BILLED)\
    .filter(Order.logistics_provider_id == logistics_provider_id).all()
    shipmentMap = {}
    p_returnMap = {}
    c_returnMap = {}
    for order in orders:
        if shipmentMap.has_key(order.logisticsTransactionId):
            shipmentMap.get(order.logisticsTransactionId).append(order)
        else:
            shipmentMap[order.logisticsTransactionId] = [order]
            
    for k,v in shipmentMap.iteritems():
        validShipment = True
        for order in v:
            if order.cod and order.net_payable_amount > 0:
                validShipment = False
                break
        if validShipment:
            p_returnMap[k] = shipmentMap.get(k)
        else:
            c_returnMap[k] = shipmentMap.get(k)
    
    if cod:
        return c_returnMap.values()
    else:
        return p_returnMap.values()

def __copy_order(order):
    copy = Order()
    copy.from_dict(order.to_dict())
    copy.id = None

    lineitem = LineItem()
    lineitem.from_dict(order.lineitems[0].to_dict())
    lineitem.id = None
    copy.lineitems = [lineitem]
    
    return copy

def register_rsa(userId, activation_code):
    pmsa = PMSA.get_by(code=activation_code)
    if pmsa is None:
        return False
    pmsa_agents = PMSA_Agents.get_by(userId = userId)
    if pmsa_agents is None:
        pmsa_agents = PMSA_Agents()
        pmsa_agents.userId = userId
        pmsa_agents.pmsa_id = pmsa.id
        session.commit()
    return True

def add_sales_associate(pmsa, referrerEmail, l1_userEmail):
    d_pmsa = PMSA.query.filter(or_(PMSA.emailId == pmsa.emailId, PMSA.phone == pmsa.phone)).all()
    if d_pmsa:
        return "User with same phone or email already exists"
    referrer = PMSA.get_by(emailId = referrerEmail, activated=True)
    d_pmsa = PMSA()
    d_pmsa.name = pmsa.name
    d_pmsa.phone = pmsa.phone 
    d_pmsa.emailId = pmsa.emailId
    d_pmsa.address = pmsa.address
    d_pmsa.state = pmsa.state
    d_pmsa.level = pmsa.level
    d_pmsa.activated = pmsa.activated
    d_pmsa.pin = pmsa.pin
    if referrerEmail != l1_userEmail:
        if referrer is None or referrer.level != 'L2':
            return "Not valid referrer"
        l1_user = PMSA.get_by(id = referrer.l1_id)
        if l1_user.emailId != l1_userEmail:
            return "You are not authorized to refer"
        d_pmsa.l2_id = referrer.id
    else:
        l1_user = referrer
    d_pmsa.l1_id = l1_user.id
    for i in range(0,5):
        activation_code = "SA"+str(generate_random_code(8))
        code_exists = PMSA.get_by(code=activation_code)
        if not code_exists:
            break
        if i==4:
            return "Activation code generation failed"
    d_pmsa.code = activation_code
    session.commit()
    return "User added successfully.Activation Code %s"%(activation_code)

def search_pmsa(pmsaSearchFilter, associateEmail):
    associate = PMSA.get_by(emailId = associateEmail)
    query = session.query(PMSA).filter(PMSA.l1_id == associate.id)
    if pmsaSearchFilter.name:
        query = query.filter(PMSA.name == pmsaSearchFilter.name)
    if pmsaSearchFilter.emailId:
        query = query.filter(PMSA.emailId == pmsaSearchFilter.emailId)
    if pmsaSearchFilter.phone:
        query = query.filter(PMSA.phone == pmsaSearchFilter.phone)
    if pmsaSearchFilter.code:
        query = query.filter(PMSA.code == pmsaSearchFilter.code)
    
    if pmsaSearchFilter.l2_email:
        referrer = PMSA.get_by(emailId = pmsaSearchFilter.l2_email)
        if referrer:
            query = query.filter(PMSA.l2_id == referrer.id)
        else:
            return []
    return query.all()

def get_pmsa_user(id, associateEmail):
    associate = PMSA.get_by(emailId = associateEmail)
    if id is None:
        return associate
    pmsa = PMSA.get_by(id=id)
    if pmsa.l1_id != associate.id:
        return None 
    l2_user_email = ""
    if pmsa.l2_id:
        l2_user = PMSA.get_by(id=pmsa.l2_id)
        l2_user_email = l2_user.emailId
    return pmsa, associate.emailId, l2_user_email

def update_pmsa_user(pmsa, associateEmail):
    d_pmsa = PMSA.query.filter(or_(PMSA.emailId == pmsa.emailId, PMSA.phone == pmsa.phone)).all()
    if not d_pmsa or (len(d_pmsa)==1 and d_pmsa[0].id == pmsa.id):
        n_pmsa = PMSA.get_by(id=pmsa.id)
        associate = PMSA.get_by(emailId = associateEmail)
        if n_pmsa.l1_id != associate.id:
            return "You dont have permission to edit this user"
        n_pmsa.name = pmsa.name
        n_pmsa.phone = pmsa.phone 
        n_pmsa.emailId = pmsa.emailId
        n_pmsa.address = pmsa.address
        n_pmsa.state = pmsa.state
        n_pmsa.activated = pmsa.activated
        n_pmsa.pin = pmsa.pin
        session.commit()
        return "User updated successfully"
    return "User with same email or phone exists"

def get_pending_pmsa(associateEmail):
    associate = PMSA.get_by(emailId = associateEmail)
    query = session.query(PMSA).filter(PMSA.l1_id == associate.id).filter(PMSA.activated == False)
    return query.all()

def get_pmsa_users(associateEmail):
    associate = PMSA.get_by(emailId = associateEmail)
    query = session.query(PMSA).filter(PMSA.l1_id == associate.id).order_by(desc(PMSA.createdAt))
    return query.all()
    
def get_stats_for_associates(associateEmail):
    a = PMSA.get_by(emailId = associateEmail)
    if a is None:
        return [0,0,0,0,0]
    l2 = session.query(PMSA.id).filter(PMSA.l1_id == a.id).filter(PMSA.level == "L2").count()
    l3 = session.query(PMSA.id).filter(PMSA.l1_id == a.id).filter(PMSA.level == "L3").count()
    not_activated = session.query(PMSA.id).filter(PMSA.l1_id == a.id).filter(PMSA.activated == False).count()
    total = session.query(PMSA.id).filter(PMSA.l1_id == a.id).count()
    return [l2,l3,total,not_activated,total-not_activated]

def credit_user_wallet(userId, amount, cash_back, shortDesc):
    user_client = UserClient().get_client()
    t_user = user_client.getUserById(userId)
    if t_user.userId == -1:
        raise
    if amount <0 :
        reference = [int(s) for s in shortDesc.split() if s.isdigit()][0]
        consume_wallet(userId, -amount, reference, WalletReferenceType.ADVANCE_REVERSAL, shortDesc)
        return True
    ap = AdvancePayments()
    ap.userId = userId
    ap.amount = amount
    ap.cash_back = cash_back
    ap.cash_back_type = "PERCENTAGE"
    ap.cash_back_amount = int(math.floor(amount * (cash_back/100)))
    session.commit()
    if not shortDesc:
        shortDesc = "Paid in advance"
    add_amount_in_wallet(userId, ap.amount + ap.cash_back_amount, ap.id, WalletReferenceType.ADVANCE_AMOUNT, True, shortDesc, commit_session=True)
    return True    

#refund complete shipping if type is list else as per map
def __refund_shipping(orders, commit=True):
    if type(orders) is list:
        for order in orders:
            if order.shippingRefund < order.shippingCost:
                diff = order.shippingCost - order.shippingRefund
                order.shippingRefund = order.shippingCost
                add_amount_in_wallet(order.customer_id, diff, order.transaction_id, WalletReferenceType.SHIPPING_REFUND, False, "Refunded shipping charges", commit_session=False)
        if commit:
            session.commit()
            
#def mark_order_for_selfpickup(transaction_ids):
#    for transaction_id in transaction_ids:
#        transaction = get_transaction(transaction_id)      
#        user_client = UserClient().get_client()
#        counter = user_client.getCounterByUserId(transaction.customer_id)
#        address = user_client.getAddressById(counter.address)
#        for order in transaction.orders:
#            oa = Attribute()
#            oa.value = counter.tin
#            oa.name='tinNumber'
#            oa.orderId = order.id
#    
#            order.customer_name         = address.name
#            order.customer_pincode      = address.pin
#            order.customer_address1     = address.line1
#            order.customer_address2     = address.line2
#            order.customer_city         = address.city
#            order.customer_state        = address.state
#            order.orderType = OrderType.B2B
#            #Self Pickup
#            order.logistics_provider_id = 4 
#        __refund_shipping(list(transaction.orders), False)    
#        session.commit()
#        session.close()
            
#
#This should be called before order processing
#Billing address is set using this for invoicing purposes
def mark_order_for_registered_gst_invoice(transaction_ids):
    try:
        for transaction_id in transaction_ids:
            transaction = get_transaction(transaction_id)      
            user_client = UserClient().get_client()
            counter = user_client.getCounterByUserId(transaction.customer_id)
            address = user_client.getAddressById(counter.address)
            for order in transaction.orders:
                order.customer_name         = address.name
                order.customer_pincode      = address.pin
                order.customer_address1     = address.line1
                order.customer_address2     = address.line2
                order.customer_city         = address.city
                order.customer_state        = address.state
                order.orderType = OrderType.B2B
            session.commit()
            return True
    except:
        logging.info("Tried accessing counter for user id {0} - {1}".format(transaction.customer_id, transaction.orders[0].customer_email))
        return False
    finally:
        session.close()


def get_in_transit_orders():
    try:
        warehouseDbConnection = getDbConnection("192.168.190.114","root", "shop2020", "warehouse")
    except:
        raise TransactionServiceException(302, "Unable to connect to Warehouse System")
    whCursor = warehouseDbConnection.cursor()
    


def is_shipment_cod(logisticsTransactionId):
    orders = Order.query.filter_by(logisticsTransactionId=logisticsTransactionId).all()
    net_payable = 0
    for order in orders:
        if order.cod == 0:
            return False
        else:
            net_payable += order.net_payable_amount
    
    return True if net_payable > 0 else False

def get_intransit_orders_on_date_by_item_id(closingDate, itemId):
    closingDateInclusive = to_py_date(closingDate) + datetime.timedelta(days=1)
    orders = Order.query.options(joinedload('lineitems')).filter(Order.lineitems.any(item_id=itemId)).filter(Order.billing_timestamp < closingDateInclusive).filter(or_(Order.refund_timestamp >= closingDateInclusive, Order.delivery_timestamp >= closingDateInclusive)).all()
    print len(orders)
    t_orders = []
    for order in orders:
        if order.lineitems[0].item_id==itemId:
            t_orders.append(to_t_order(order))
    return t_orders

def get_intransit_orders_on_date(closingDate):
    closingDateInclusive = to_py_date(closingDate) + datetime.timedelta(days=1)
    orders = Order.query.options(joinedload('lineitems')).filter(Order.billing_timestamp < closingDateInclusive).filter(or_(Order.refund_timestamp >= closingDateInclusive, Order.delivery_timestamp >= closingDateInclusive)).all()
    return [to_t_order(order) for order in orders]

def add_price_drop(item_id, imeis, amount, affected_on):
    priceDrop = Price_Drop()
    priceDrop.amount = amount
    priceDrop.affected_on = to_py_date(affected_on)
    priceDrop.created_on = datetime.datetime.now()
    priceDrop.item_id = item_id
    for imei in imeis: 
        pdImei = Price_Drop_IMEI()
        pdImei.imei = imei
        pdImei.price_drop = priceDrop
        
    session.commit()
    
    return True
    



def process_failed_payment(new_status, transaction_id, description, sourceId, pickUp):
    logging.info("########change transaction status called" + str(transaction_id))
    transaction = get_transaction(transaction_id)
    
    transaction.status = new_status
    transaction.status_message = description
    order = transaction.orders[0]
    expectedDelayMap = {}
    billingWarehouseIdMap = {}
    billingWarehouseTxnMap = {}
    
    
    
    if new_status == TransactionStatus.FAILED:
        for order in transaction.orders:
            order.status = OrderStatus.PAYMENT_FAILED
            order.statusDescription = "Payment Failed"
    elif new_status == TransactionStatus.AUTHORIZED or new_status == TransactionStatus.FLAGGED:                  
        user_client = UserClient().get_client()
        catalog_client = CatalogClient().get_client()
        
        pdu = user_client.getPrivateDealUser(transaction.orders[0].customer_id)
        print "pdu.isFofo----", pdu.isFofo
        fofoWarehousesMap = {} 
        if pdu.isFofo:
            fofoDealMap = catalog_client.getAllFofoDeals([order.lineitems[0].item_id], [4,7])
            if fofoDealMap:
                itemIds = []
                for order in transaction.orders:
                    itemIds.append(order.lineitems[0].item_id)
                inventory_client = InventoryClient().get_client()
                print "calling getFofoFulFillmentWarehouseMap" 
                fofoWarehousesMap = inventory_client.getFofoFulFillmentWarehouseMap(itemIds)
                print "called getFofoFulFillmentWarehouseMap" 
        

        expectedDelayMap = {}
        billingWarehouseIdMap = {}
        billingWarehouseTxnMap = {}
        subOrderAmountsMap = {}       
        for order in transaction.orders:
            if new_status == TransactionStatus.AUTHORIZED:
                order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
                order.statusDescription = "Submitted to warehouse"
            elif new_status == TransactionStatus.FLAGGED:
                order.status = OrderStatus.PAYMENT_FLAGGED
                order.statusDescription = "Payment flagged by gateway"
            order.cod = False
            #Order type cant be changed during authorization
            #if orderType is not None:
            #    order.orderType = orderType
            #After we got payment success, we will set logistics info also 
            logistics_client = LogisticsClient().get_client()
            #FIXME line item is only one now. If multiple will come, need to fix.
            item_id = order.lineitems[0].item_id
            if pickUp == PickUpType.RUNNER or pickUp == PickUpType.SELF:
                current_time = datetime.datetime.now()
                if pdu.isFofo:
                    stateMaster = fetchStateMaster()
                    stateId = -1
                    for k, v in stateMaster.iteritems():
                        if v.stateName == order.customer_state:
                            stateId = k
                            break
                    logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.PREPAID, pickUp, stateId)
                else:
                    logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.PREPAID, pickUp, -1)
                order.logistics_provider_id = logistics_info.providerId
                
                order.warehouse_id = logistics_info.warehouseId
                order.fulfilmentWarehouseId = logistics_info.fulfilmentWarehouseId
                
                logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
                logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
                logistics_info.deliveryDelay = adjust_delivery_time(current_time, (logistics_info.shippingTime+logistics_info.deliveryDelay))
                
                order.courier_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryDelay)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
                order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
                order.promised_shipping_time = order.expected_shipping_time
                order.expected_delivery_time = (current_time + datetime.timedelta(days=logistics_info.deliveryTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
                order.promised_delivery_time = order.expected_delivery_time
                order.otg = False
                
                    
                inventory_client = InventoryClient().get_client()
                    
                if order.productCondition != ProductCondition.BAD:
                    inventory_client.reserveItemInWarehouse(item_id, order.fulfilmentWarehouseId, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), order.lineitems[0].quantity)

            elif order.source == OrderSource.WEBSITE:
                try:
                    #Get the id and location of actual warehouse that'll be used to fulfil this order.
                    inventory_client = InventoryClient().get_client()
                    fulfilmentWhId, expected_delay, billingWarehouseId, sellingPrice, totalAvailability, weight = inventory_client.getItemAvailabilityAtLocation(item_id, 1, -1)
                except Exception as ex:
                    raise TransactionServiceException(103, "Unable to fetch inventory information about this item.")
                
                expectedDelayMap[item_id] = expected_delay
                if billingWarehouseIdMap.has_key(billingWarehouseId):
                    ordersList = billingWarehouseIdMap.get(billingWarehouseId)
                    ordersList.append(order)
                    billingWarehouseIdMap[billingWarehouseId] = ordersList
                else:
                    ordersList = []
                    ordersList.append(order)
                    billingWarehouseIdMap[billingWarehouseId] = ordersList
                    
                if billingWarehouseTxnMap.has_key(billingWarehouseId):
                    transactionAmount , transactionWeight = billingWarehouseTxnMap.get(billingWarehouseId)
                    transactionAmount = transactionAmount+order.total_amount
                    transactionWeight = transactionWeight+order.total_weight
                    billingWarehouseTxnMap[billingWarehouseId] = transactionAmount , transactionWeight
                else:
                    billingWarehouseTxnMap[billingWarehouseId] = order.total_amount , order.total_weight
                    
                subOrderAmountsMap[long(order.lineitems[0].unit_price)] = order
                    
                order.fulfilmentWarehouseId = fulfilmentWhId
                order.warehouse_id = billingWarehouseId
                
                    
                if order.productCondition != ProductCondition.BAD:
                    inventory_client.reserveItemInWarehouse(item_id, order.fulfilmentWarehouseId, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), order.lineitems[0].quantity)
        
            try:
                item_pricing = inventory_client.getItemPricing(item_id, -1)
                order.lineitems[0].transfer_price = item_pricing.transferPrice
                order.lineitems[0].nlc = item_pricing.nlc
            except:
                print "Not able to get transfer price. Skipping"
            
    elif new_status == TransactionStatus.COD_IN_PROCESS:
        expectedDelayMap = {}
        billingWarehouseIdMap = {}
        billingWarehouseTxnMap = {}
        subOrderAmountsMap = {}
        for order in transaction.orders:
            order.status = OrderStatus.COD_VERIFICATION_PENDING
            order.statusDescription = "Verification Pending"
            order.cod = True
            #OrderType cant be changed
            #order.orderType = orderType
            #After we got payment success, we will set logistics info also 
            logistics_client = LogisticsClient().get_client()
            #FIXME line item is only one now. If multiple will come, need to fix.
            item_id = order.lineitems[0].item_id
                        
            try:
                #Get the id and location of actual warehouse that'll be used to fulfil this order.
                inventory_client = InventoryClient().get_client()
                fulfilmentWhId, expected_delay, billingWarehouseId, sellingPrice, totalAvailability, weight = inventory_client.getItemAvailabilityAtLocation(item_id, 1, -1)
            except Exception as ex:
                raise TransactionServiceException(103, "Unable to fetch inventory information about this item.")
            
            expectedDelayMap[item_id] = expected_delay
            if billingWarehouseIdMap.has_key(billingWarehouseId):
                ordersList = billingWarehouseIdMap.get(billingWarehouseId)
                ordersList.append(order)
                billingWarehouseIdMap[billingWarehouseId] = ordersList
            else:
                ordersList = []
                ordersList.append(order)
                billingWarehouseIdMap[billingWarehouseId] = ordersList
                
            if billingWarehouseTxnMap.has_key(billingWarehouseId):
                transactionAmount , transactionWeight = billingWarehouseTxnMap.get(billingWarehouseId)
                transactionAmount = transactionAmount+order.total_amount
                transactionWeight = transactionWeight+order.total_weight
                billingWarehouseTxnMap[billingWarehouseId] = transactionAmount , transactionWeight
            else:
                billingWarehouseTxnMap[billingWarehouseId] = order.total_amount , order.total_weight
                
            subOrderAmountsMap[long(order.lineitems[0].unit_price)] = order
                
            order.fulfilmentWarehouseId = fulfilmentWhId
            order.warehouse_id = billingWarehouseId
                            
            if order.productCondition != ProductCondition.BAD:
                inventory_client.reserveItemInWarehouse(item_id, order.fulfilmentWarehouseId, sourceId, order.id, to_java_date(order.created_timestamp), to_java_date(order.promised_shipping_time), order.lineitems[0].quantity)
                                
            try:
                item_pricing = inventory_client.getItemPricing(item_id, -1)
                order.lineitems[0].transfer_price = item_pricing.transferPrice
                order.lineitems[0].nlc = item_pricing.nlc        
            except:
                print "Not able to get transfer price. Skipping"
            
    session.commit()

    if new_status in (TransactionStatus.AUTHORIZED, TransactionStatus.FLAGGED, TransactionStatus.COD_IN_PROCESS):
        try:
            if order.source == OrderSource.WEBSITE:    
                if new_status == TransactionStatus.COD_IN_PROCESS:
                    transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
                    transaction_requiring_extra_processing.category = 'COD_VERIFICATION'
                    transaction_requiring_extra_processing.transaction_id = transaction_id
                    session.commit()
                elif new_status == TransactionStatus.FLAGGED:
                    transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
                    transaction_requiring_extra_processing.category = 'PAYMENT_FLAGGED'
                    transaction_requiring_extra_processing.transaction_id = transaction_id
                    session.commit()
            else:
                order.otg = False
                session.commit()
        except Exception as e:
            print "Error inserting transaction Id: " + str(transaction_id) + " due to " + str(e)

    return True

    



if __name__ == '__main__':
    DataService.initialize(db_hostname="192.168.190.112")
    #transaction_ids=[1030084,1030087,1030090,1030093,1030096,1030099,1030102,1030105,1030108,1030111,1030114,1030117,1030120,1030123,1030144,1030147,1030153]
    #transaction_ids=[1043579, 1043580, 1043581, 1043582, 1043584, 1043588, 1043589]
    #transaction_ids=[1043577]
    #for transaction_id in transaction_ids:
    #    process_failed_payment(TransactionStatus.AUTHORIZED, transaction_id, "Transaction Authorised", 1, PickUpType.SELF)
    #
    ##################################################################################################################################
    refund_order(1588409, "backend-team", "As per Customer's Request")
    
       
    
#    mark_order_for_selfpickup_temp(transaction_ids)
#    get_billed_orders_for_manifest_gen(7441, 2, True)
#    ordersmap = {'911319502886601':1544266,'911319502890314':1544266,'x911319502903141':1544269,'x911319502916655':1544269,'x911319502950985':1544269,'x911319502836309':1544269,'x911319502972369':1544269,'x911319502950860':1544269,'x911319502918313':1544269,
#                 'x911319502943436':1544269,'x911319502928056':1544269,'x911319502838263':1544269,'911319502794144':1544269,'x911319502821616':1544269,'x911319502974746':1544269,'x911319502821442':1544269,'x911319502903984':1544269,'x911319502820980':1544269,
#                 'x911319502973102':1544269,'x911319502913009':1544269,'x911319502973979':1544269,'x911319502842729':1544269,'x911319502824784':1544269,'x911319502847249':1544269,'x911319502848387':1544269,'x911319502887021':1544269,'x911319502962634':1544269,
#                 'x911319502826268':1544269,'x911319502785761':1544269,'x911319502956750':1544269,'x911319502825195':1544269,'x911319502828298':1544269,'x911319502972054':1544269,'x911319502902044':1544269,'x911319502846498':1544269,'x911319502824305':1544269,
#                 'x911319502891478':1544269,'x911319502962451':1544269,'x911319502929732':1544269,'x911319502834304':1544269,'x911319502830575':1544269,'x911319502889456':1544269,'x911319502842521':1544269,'x911319502900998':1544269,'x911319502890462':1544269,'x911319502881222':1544269,'x911319503523252':1544269}
#    
#    invoice_number=''
#    itemNumbersMap={}
#    serialNumbersMap={}
#    from shop2020.model.v1.order.impl import DataService
#    from shop2020.model.v1.order.impl.DataAccessors import add_billing_details_for_groupped_orders
#    DataService.initialize('transaction','192.168.190.114')
#    for k,v in ordersmap.iteritems():
#        if not serialNumbersMap.has_key(v):
#            serialNumbersMap[v]=[]
#        arr = serialNumbersMap.get(v)
#        arr.append(k)    
#    order_ids=serialNumbersMap.keys()
#    for order_id in order_ids:
#        itemNumbersMap[order_id]=['1']
#    add_billing_details_for_groupped_orders(order_ids, invoice_number, itemNumbersMap, serialNumbersMap, {}, 'mp-mmx-admin', '1', 0, False, 'Individual')
#from shop2020.clients.LogisticsClient import LogisticsClient
#pincodes = [302006,263139,781001,834001,380051,380001,110074,400095,560037,125055,122001,700009,400709,462022,700023,400015]
#logistics_client = LogisticsClient().get_client()
#for pincode in pincodes:
#    print pincode, logistics_client.getLogisticsEstimation(26385, str(pincode), 1).providerId

    #add_price_drop(213, ['1','2'], 20, 1513686198574)