Subversion Repositories SmartDukaan

Rev

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

'''
Created on 05-Aug-2010

@author: ashish
'''
from shop2020.clients.CatalogClient import CatalogClient
from shop2020.clients.InventoryClient import InventoryClient
from shop2020.config.client.ConfigClient import ConfigClient
from shop2020.logistics.service.impl import DataAccessor, EcomExpressService
from shop2020.logistics.service.impl.Converters import to_t_awbupdate, \
    to_t_provider, to_t_pickup_store, to_t_bluedart_attribute
from shop2020.logistics.service.impl.DataAccessor import get_empty_AWB, \
    get_shipment_info, initialize, get_logistics_estimation, get_provider, \
    get_providers, close_session, get_free_awb_count, get_holidays, is_alive, \
    get_provider_for_pickup_type, get_pickup_store, get_all_pickup_stores, \
    get_pickup_store_by_hotspot_id, get_destination_code, update_pincode, \
    add_pincode, store_shipment_info, adjust_delivery_time, get_min_advance_amount, \
    add_new_awbs, run_Logistics_Location_Info_Update, \
    get_first_delivery_estimate_for_wh_location, \
    get_provider_limit_details_for_pincode, get_new_empty_awb, \
    get_logistics_locations, get_costing_and_delivery_estimate_for_pincode, \
    get_bluedart_attributes_for_logistics_txn_id, _DeliveryEstimateObject
from shop2020.logistics.service.impl.DataService import \
    ServiceableLocationDetails
from shop2020.thriftpy.logistics.ttypes import ItemText, LogisticsInfo, \
    LogisticsServiceException, DeliveryType, PickUpType
from shop2020.thriftpy.model.v1.catalog.ttypes import status
from shop2020.utils.Utils import to_java_date, to_py_date
import collections
import datetime
import math
import sys
from shop2020.utils.caching.SimpleCaching import memoized
# Start:- Added by Manish Sharma for Multiple Pincode Updation on 05-Jul-2013
# End:- Added by Manish Sharma for Multiple Pincode Updation on 05-Jul-2013


class LogisticsServiceHandler:
    
    def __init__(self, dbname='logistics', db_hostname='localhost'):
        initialize(dbname, db_hostname)
        try:
            config_client = ConfigClient()
            self.sourceId = int(config_client.get_property("sourceid"))
            self.cutoff_time = int(config_client.get_property('delivery_cutoff_time'))
            self.cod_cutoff_time = 24  # int(config_client.get_property('delivery_cutoff_time'))
            self.default_pincode = int(config_client.get_property('default_pincode'))
        except Exception as ex:
            print "[ERROR] Unexpected config error:", sys.exc_info()[0]
            self.sourceId = 1
            self.cutoff_time = 15
            self.cod_cutoff_time = 24
            self.default_pincode = "110001"
         
    def getProvider(self, providerId):
        """
        Returns a provider for a given provider ID. Throws an exception if none found.
        
        Parameters:
         - providerId
        """
        try:
            provider = get_provider(providerId)
            if provider:
                return to_t_provider(provider)
            else:
                raise LogisticsServiceException(101, "No Provider found for the given id")
        finally:
            close_session()
            
    def getAllProviders(self,):
        """
        Returns a list containing all the providers.
        """
        try:
            return [to_t_provider(provider) for provider in get_providers()]
        finally:
            close_session()
            
    def getLogisticsInfo(self, destination_pincode, itemId, type, pickUp, stateId):
        """
        Parameters:
         - destination_pincode
         - item_id
         - type
        """
        try:
            logistics_info = self.get_logistics_estimation_with_type(itemId, destination_pincode, type, pickUp, stateId)
            # logistics_info.airway_billno = get_empty_AWB(logistics_info.providerId, type)
            return logistics_info
        finally:
            close_session()
            
    def getEmptyAWB(self, providerId, logisticsTransactionId):
        """
        Parameters:
         - provider_id
         - type
        """
        try:
            return get_empty_AWB(providerId, logisticsTransactionId)
        finally:
            close_session()
            
    def getShipmentInfo(self, awbNumber, providerId):
        """
        Parameters:
         - awbNumber
         - providerId
        """
        try:
            awb_updates = get_shipment_info(awbNumber, providerId)
            t_updates = []
            for update in awb_updates:
                t_updates.append(to_t_awbupdate(update))
            return t_updates
        finally:
            close_session()
    
    def storeShipmentInfo(self, update):
        """
        Parameters:
         - update
        """
        try:
            store_shipment_info(update)
        finally:
            close_session()
    
    def getLogisticsEstimation(self, itemId, destination_pin, type):
        """
        Parameters:
         - itemId
         - destination_pin
         - type
        """
        try:
            return self.get_logistics_estimation_with_type(itemId, destination_pin, type)
        finally:
            close_session()

    def getLogisticsEstimationForStore(self, itemId, destination_pin, type):
        """
        Parameters:
         - itemId
         - destination_pin
         - type
        """
        try:
            todate = datetime.datetime.now()
            logistics_info = self.get_logistics_estimation_with_type(itemId, destination_pin, type, -1)
            logistics_info.deliveryTime = to_java_date(todate + datetime.timedelta(days=adjust_delivery_time(todate, logistics_info.deliveryTime)))
            logistics_info.shippingTime = to_java_date(todate + datetime.timedelta(days=adjust_delivery_time(todate, logistics_info.shippingTime)))
            minAdvanceAmount, logistics_info.codAllowed = get_min_advance_amount(itemId, destination_pin, logistics_info.providerId, logistics_info.codAllowed)
            # # Send minadvanceamount in providerId field 
            logistics_info.providerId = int(minAdvanceAmount)
            return logistics_info
        finally:
            close_session()
            
    def get_logistics_estimation_with_type(self, itemId, destination_pin, type, pickUp=PickUpType.COURIER, stateId=-1):
        try:
            # Get the id and location of actual warehouse that'll be used to fulfil this order.
            client = InventoryClient().get_client()
            fulfilmentWarehouseId, expected_delay, billingWarehouseId, sellingPrice, totalAvailability, weight = client.getItemAvailabilityAtLocation(itemId, self.sourceId, stateId)
            if totalAvailability <= 0:
                expected_delay = 0
                
        except Exception as ex:
            print "Unable to fetch inventory information about this item." + str(itemId), "source id", self.sourceId, stateId
            raise LogisticsServiceException(103, "Unable to fetch inventory information about this item." + str(itemId))
        
        if pickUp == PickUpType.COURIER:
            delivery_estimate = get_logistics_estimation(destination_pin, sellingPrice, weight, type, billingWarehouseId)
        else:
            delivery_estimate = _DeliveryEstimateObject(0, 0, get_provider_for_pickup_type(pickUp), False, False)
        if delivery_estimate is None:
            raise LogisticsServiceException(104, "Unable to fetch delivery estimate for this pincode.")
                
        print "destination", destination_pin, "stateId", stateId
        if stateId != -1:
            if destination_pin in ['132001', '136027', '132114', '131001', '132103']:
                delivery_estimate.provider_id = 48
        
        # # Commented below part as we have only Delhi as warehouse city. If we will add some more warehouses in different cities, this could be  useful. 
        # We are revising the estimates based on the actual warehouse that this order will be assigned to.
        # This warehouse may be located in a zone which is different from the one we allocated for this pincode.
        # delivery_estimate = get_logistics_estimation(destination_pin, item.sellingPrice, warehouse_loc, type)
        # if delivery_estimate is None:
        #    raise LogisticsServiceException(105, "Unable to fetch delivery estimate for pincode: " + destination_pin + " and revised location: " + str(warehouse_loc))
        
        delivery_time = 24 * (delivery_estimate.delivery_time + delivery_estimate.delivery_delay)
        
        '''
        We're now calculating the expected shipping delay which is independent of
        the courier agency and is completely within our control (well, almost).
        '''
        # Always add the expected delay
        # this is due to virtual inventory
        shipping_delay = 24 * expected_delay
        
        # Sometimes we set negative shipping delay just in case we know time to procure will be less than the default.
        # If we have received inventory and forgot to remove expected delay from item, it could lead to display negative shipping days. 
        if shipping_delay < 0:
            shipping_delay = 0
            
        # Further increase the estimate if it's late in the day
        current_hour = datetime.datetime.now().hour
        if type == DeliveryType.PREPAID and self.cutoff_time <= current_hour:
            shipping_delay = shipping_delay + 24
        
        # In case of COD,increase delay by one more day
        # As per deenanath why are doing this?
        if type == DeliveryType.COD:
            shipping_delay = shipping_delay + 24
            delivery_estimate.otgAvailable = False
            
        delivery_time = delivery_time + shipping_delay
        
        shipping_delay = int(math.ceil(shipping_delay / 24.0))
        delivery_time = int(math.ceil(delivery_time / 24.0))
        
        logistics_info = LogisticsInfo()
        logistics_info.deliveryTime = delivery_time
        logistics_info.providerId = delivery_estimate.provider_id
        logistics_info.warehouseId = billingWarehouseId
        logistics_info.fulfilmentWarehouseId = fulfilmentWarehouseId
        logistics_info.shippingTime = shipping_delay
        logistics_info.codAllowed = delivery_estimate.codAllowed 
        logistics_info.otgAvailable = delivery_estimate.otgAvailable
        logistics_info.deliveryDelay = delivery_estimate.delivery_delay
        
        try:
            return logistics_info
        finally:
            close_session()
        
    def getDestinationCode(self, providerId, pinCode):
        """
        Returns the short three letter code of a pincode for the given provider.
        Raises an exception if the pin code is not serviced by the given provider.
        
        Parameters:
         - providerId
         - pinCode
        """
        try:
            try:
                dest_code = DataAccessor.serviceable_location_cache[providerId][pinCode][0]
                return dest_code
            except:
                try:
                    dest_code = get_destination_code(providerId, pinCode) 
                    return dest_code
                except:
                    if providerId > 7:
                        return ""
                    raise LogisticsServiceException(101, "The pincode " + pinCode + " is not serviced by this provider: " + str(providerId))
        finally:
            close_session()

    def getFreeAwbCount(self, providerId, type):
        """
        Returns the number of unused AWB numbers for the given provider of the given type
        
        Parameters:
         - providerId
         - type
        """
        try:
            return get_free_awb_count(providerId, type)
        finally:
            close_session()

    def getHolidays(self, fromDate, toDate):
        """
        Returns list of Holiday dates between fromDate and toDate (both inclusive)
        fromDate should be passed as milliseconds corresponding to the start of the day.
        If fromDate is passed as -1, fromDate is not considered for filtering
        If toDate is passed as -1, toDate is not considered for filtering
        
        Parameters:
         - fromDate
         - toDate
        """
        try:
            return get_holidays(fromDate, toDate)
        finally:
            close_session()
            
    def getProviderForPickupType(self, pickUp):
        try:
            return get_provider_for_pickup_type(pickUp)
        finally:
            close_session()
    
    def closeSession(self,):
        close_session()

    def isAlive(self,):
        """
        For checking weather service is active alive or not. It also checks connectivity with database
        """
        try:
            return is_alive()
        finally:
            close_session()
    
    def getEntityLogisticsEstimation(self, catalogItemId, destination_pin, type):
        """
        Returns a LogisticsInfo structure w/o an airway bill number. Use this method during the estimation phase.
        Raises an exception if this pincode is not allocated to any warehouse zone or provider. Also, if the pincode
        is allocated to a warehouse zone but there are no actual warehouses in that zone, an exception is raised.
    
        Parameters:
         - catalogItemId
         - destination_pin
         - type
        """
        try:
            return self.get_entity_logistics_estimation_with_type(catalogItemId, destination_pin, type)
        finally:
            close_session()

    def get_entity_logistics_estimation_with_type(self, catalog_item_id, destination_pin, type):
        try:
            client = CatalogClient().get_client()
            items = client.getValidItemsByCatalogId(catalog_item_id)
        except Exception as ex:
            raise LogisticsServiceException(103, "Unable to fetch inventory information about this entity.")
        
        estimateList = []

        for item in items:
            try:
                estimationInfo = self.get_logistics_estimation_with_type(item.id, destination_pin, type)
            except Exception as ex:
                estimationInfo = LogisticsInfo()
                estimationInfo.deliveryTime = 0
            if item.itemStatus == status.ACTIVE:
                estimateList.append((0, estimationInfo.deliveryTime, "BUY NOW", item.id))
            elif item.itemStatus == status.PAUSED:
                estimateList.append((1, estimationInfo.deliveryTime, "NOTIFY ME", item.id))
            elif item.itemStatus == status.PAUSED_BY_RISK:
                estimateList.append((2, estimationInfo.deliveryTime, "NOTIFY ME", item.id))
            elif item.itemStatus == status.COMING_SOON:
                estimateList.append((3, estimationInfo.deliveryTime, "NOTIFY ME", item.id))

        estimateList.sort()
        try:
            return [ ItemText(estimate[-1], estimate[-2]) for estimate in estimateList]
        finally:
            close_session()
    
    def getAllPickupStores(self):
        try:
            return [to_t_pickup_store(pickup_store) for pickup_store in get_all_pickup_stores()]
        finally:
            close_session()

    def getPickupStore(self, storeId):
        """
        Parameters:
         - storeId
        """
        try:
            storeToReturn = to_t_pickup_store(get_pickup_store(storeId))
            return storeToReturn
        finally:
            close_session()
            
    def getPickupStoreByHotspotId(self, hotspotId):
        """
        Parameters:
         - hotspotId
        """
        try:
            storeToReturn = to_t_pickup_store(get_pickup_store_by_hotspot_id(hotspotId))
            return storeToReturn
        finally:
            close_session()

    def addPincode(self, providerId, pincode, destCode, exp, cod, stationType, otgAvailable):
        try:
            add_pincode(providerId, pincode, destCode, exp, cod, stationType, otgAvailable)
        finally:
            close_session()

    def updatePincode(self, providerId, pincode, exp, cod, otgAvailable):
        try:
            update_pincode(providerId, pincode, exp, cod, otgAvailable)
        finally:
            close_session()
    
    def addNewAwbs(self, providerId, isCod, awbs, awbUsedFor):
        try:
            return add_new_awbs(providerId, isCod, awbs, awbUsedFor)
        finally:
            close_session()
            
    def runLogisticsLocationInfoUpdate(self, logisticsLocationInfoList, runCompleteUpdate, providerId):
        try:
            run_Logistics_Location_Info_Update(logisticsLocationInfoList, runCompleteUpdate, providerId)
        finally:
            close_session()

    def adjustDeliveryDays(self, startDate, days):
        try:
            return adjust_delivery_time(to_py_date(startDate), days)
        finally:
            close_session()
            
    def getFirstDeliveryEstimateForWhLocation(self, pincode, whLocation):
        try:
            return get_first_delivery_estimate_for_wh_location(pincode, whLocation)
        finally:
            close_session()
            
    def getProviderLimitDetailsForPincode(self, providerId, pincode):
        try:
            return get_provider_limit_details_for_pincode(providerId, pincode)
        finally:
            close_session()
            
    def getNewEmptyAwb(self, providerId, type, orderQuantity):
        try:
            return get_new_empty_awb(providerId, type, orderQuantity)
        finally:
            close_session()
            
    def getLocationInfoMap(self, destinationPin, sellingPriceList):
        try:
            return get_logistics_locations(destinationPin, sellingPriceList)
        finally:
            close_session()
            
    def getCostingAndDeliveryEstimateForPincode(self, pincode, transactionAmount, isCod, weight, billingWarehouseId, isCompleteTxn):
        try:
            costingAndDeliveryEstimateObj = get_costing_and_delivery_estimate_for_pincode(pincode, transactionAmount, isCod, weight, billingWarehouseId, isCompleteTxn)
            delivery_time = 24 * (costingAndDeliveryEstimateObj.deliveryTime + costingAndDeliveryEstimateObj.delivery_delay)
            shipping_delay = 0
            
            # Further increase the estimate if it's late in the day
            current_hour = datetime.datetime.now().hour
            if not isCod and self.cutoff_time <= current_hour:
                shipping_delay = shipping_delay + 24
            
            # In case of COD,increase delay by one more day
            if isCod:
                shipping_delay = shipping_delay + 24
                costingAndDeliveryEstimateObj.otgAvailable = False
                
            delivery_time = delivery_time + shipping_delay
        
            shipping_delay = int(math.ceil(shipping_delay / 24.0))
            delivery_time = int(math.ceil(delivery_time / 24.0))
            
            costingAndDeliveryEstimateObj.deliveryTime = delivery_time
            costingAndDeliveryEstimateObj.shippingTime = shipping_delay
            return costingAndDeliveryEstimateObj
        finally:
            close_session()
    
    def getBluedartAttributesForLogisticsTxnId(self, logisticsTxnId, name):
        try:
            return to_t_bluedart_attribute(get_bluedart_attributes_for_logistics_txn_id(logisticsTxnId, name))
        finally:
            close_session()
            
    def pushCourierDetailsForEcomExpress(self, logisticsTransactionIds):
        try:
            for logisticsTransactionId in logisticsTransactionIds:
                EcomExpressService.forward_request(logisticsTransactionId)
            return True
        finally:
            close_session()
            
        return False