Subversion Repositories SmartDukaan

Rev

Rev 12895 | Rev 19421 | Go to most recent revision | 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
from shop2020.logistics.service.impl.Converters import to_t_awbupdate, \
    to_t_provider, to_t_pickup_store
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
from shop2020.logistics.service.impl.DataService import \
    ServiceableLocationDetails
from shop2020.thriftpy.logistics.ttypes import LogisticsInfo, \
    LogisticsServiceException, DeliveryType, PickUpType
from shop2020.thriftpy.model.v1.catalog.ttypes import status
from shop2020.thriftpy.logistics.ttypes import ItemText
from shop2020.utils.Utils import to_java_date, to_py_date
import collections
import datetime
import math
import sys
#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):
        """
        Parameters:
         - destination_pincode
         - item_id
         - type
        """
        try:
            logistics_info = self.get_logistics_estimation_with_type(itemId, destination_pincode, type)
            if pickUp == PickUpType.RUNNER or pickUp == PickUpType.SELF:
                logistics_info.providerId = get_provider_for_pickup_type(pickUp)
            #logistics_info.airway_billno = get_empty_AWB(logistics_info.providerId, type)
            return logistics_info
        finally:
            close_session()
            
    def getEmptyAWB(self, providerId, type):
        """
        Parameters:
         - provider_id
         - type
        """
        try:
            return get_empty_AWB(providerId, type)
        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)
            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):
        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)
        except Exception as ex:
            raise LogisticsServiceException(103, "Unable to fetch inventory information about this item.")
        
        delivery_estimate = get_logistics_estimation(destination_pin, sellingPrice, weight, type, billingWarehouseId)
        if delivery_estimate is None:
            raise LogisticsServiceException(104, "Unable to fetch delivery estimate for this pincode.")
                
        
        ## 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
        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
        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):
        try:
            run_Logistics_Location_Info_Update(logisticsLocationInfoList, runCompleteUpdate)
        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()