Subversion Repositories SmartDukaan

Rev

Rev 19247 | Rev 19419 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 19247 Rev 19413
Line 4... Line 4...
4
@author: ashish
4
@author: ashish
5
'''
5
'''
6
from elixir import *
6
from elixir import *
7
from functools import partial
7
from functools import partial
8
from shop2020.clients.CatalogClient import CatalogClient
8
from shop2020.clients.CatalogClient import CatalogClient
-
 
9
from shop2020.clients.LogisticsClient import LogisticsClient
9
from shop2020.clients.TransactionClient import TransactionClient
10
from shop2020.clients.TransactionClient import TransactionClient
10
from shop2020.model.v1.inventory.impl import DataService
11
from shop2020.model.v1.inventory.impl import DataService
11
from shop2020.model.v1.inventory.impl.Convertors import to_t_warehouse, \
12
from shop2020.model.v1.inventory.impl.Convertors import to_t_warehouse, \
12
    to_t_itemidwarehouseid, to_t_state
13
    to_t_itemidwarehouseid, to_t_state, to_t_item_location_availability
13
from shop2020.model.v1.inventory.impl.DataService import Warehouse, \
14
from shop2020.model.v1.inventory.impl.DataService import Warehouse, \
14
    ItemInventoryHistory, CurrentInventorySnapshot, VendorItemPricing, \
15
    ItemInventoryHistory, CurrentInventorySnapshot, VendorItemPricing, \
15
    VendorItemMapping, Vendor, MissedInventoryUpdate, BadInventorySnapshot, \
16
    VendorItemMapping, Vendor, MissedInventoryUpdate, BadInventorySnapshot, \
16
    VendorHolidays, ItemAvailabilityCache, \
17
    VendorHolidays, ItemAvailabilityCache, CurrentReservationSnapshot, \
17
    CurrentReservationSnapshot, IgnoredInventoryUpdateItems, ItemStockPurchaseParams, \
18
    IgnoredInventoryUpdateItems, ItemStockPurchaseParams, OOSStatus, \
-
 
19
    AmazonInventorySnapshot, StateMaster, HoldInventoryDetail, \
18
    OOSStatus, AmazonInventorySnapshot, StateMaster, HoldInventoryDetail, AmazonFbaInventorySnapshot, \
20
    AmazonFbaInventorySnapshot, SnapdealInventorySnapshot, FlipkartInventorySnapshot, \
19
    SnapdealInventorySnapshot, FlipkartInventorySnapshot, SnapdealStockAtEOD, FlipkartStockAtEOD, StockWeightedNlcInfo
21
    SnapdealStockAtEOD, FlipkartStockAtEOD, StockWeightedNlcInfo, \
-
 
22
    ItemLocationAvailabilityCache
20
from shop2020.thriftpy.model.v1.inventory.ttypes import \
23
from shop2020.thriftpy.model.v1.inventory.ttypes import \
21
    InventoryServiceException, HolidayType, InventoryType, WarehouseType
24
    InventoryServiceException, HolidayType, InventoryType, WarehouseType,\
-
 
25
    ItemLocationAvailability, ItemPincodeAvailability
22
from shop2020.thriftpy.model.v1.order.ttypes import AlertType
26
from shop2020.thriftpy.model.v1.order.ttypes import AlertType
23
from shop2020.thriftpy.purchase.ttypes import PurchaseServiceException
27
from shop2020.thriftpy.purchase.ttypes import PurchaseServiceException
24
from shop2020.utils import EmailAttachmentSender
28
from shop2020.utils import EmailAttachmentSender
25
from shop2020.utils.EmailAttachmentSender import mail
29
from shop2020.utils.EmailAttachmentSender import mail
26
from shop2020.utils.Utils import to_py_date, to_java_date
30
from shop2020.utils.Utils import to_py_date, to_java_date
Line 28... Line 32...
28
from sqlalchemy.sql import or_
32
from sqlalchemy.sql import or_
29
from sqlalchemy.sql.expression import and_, func, distinct, desc
33
from sqlalchemy.sql.expression import and_, func, distinct, desc
30
from sqlalchemy.sql.functions import count
34
from sqlalchemy.sql.functions import count
31
import calendar
35
import calendar
32
import datetime
36
import datetime
-
 
37
import math
33
import sys
38
import sys
34
import threading
39
import threading
35
import math
40
import json
-
 
41
from shop2020.thriftpy.logistics.ttypes import LocationInfo
36
 
42
 
37
to_addresses = ["khushal.bhatia@shop2020.in", "chaitnaya.vats@shop2020.in", "chandan.kumar@shop2020.in",'manoj.kumar@shop2020.in']
43
to_addresses = ["khushal.bhatia@shop2020.in", "chaitnaya.vats@shop2020.in", "chandan.kumar@shop2020.in",'manoj.kumar@shop2020.in']
38
mail_user = "cnc.center@shop2020.in"
44
mail_user = "cnc.center@shop2020.in"
39
mail_password = "5h0p2o2o"
45
mail_password = "5h0p2o2o"
40
skippedItems = { 175 : [27, 2160, 2175, 2163, 2158, 7128, 26, 2154],
46
skippedItems = { 175 : [27, 2160, 2175, 2163, 2158, 7128, 26, 2154],
41
                 193 : [5839] }
47
                 193 : [5839] }
42
 
48
 
-
 
49
pincodePricingServiceabilityMap = {}
-
 
50
warehouseMap = {}
-
 
51
logisticsLocationWarehouseMap = {}
-
 
52
 
43
OOS_CALCULATION_TIME = 23
53
OOS_CALCULATION_TIME = 23
44
 
54
 
-
 
55
last_date = datetime.date.today()
-
 
56
adjusted_dates={}
-
 
57
 
45
def initialize(dbname='inventory', db_hostname="localhost"):
58
def initialize(dbname='inventory', db_hostname="localhost"):
46
    DataService.initialize(dbname, db_hostname)
59
    DataService.initialize(dbname, db_hostname)
-
 
60
    __populateWarehouseMap()
-
 
61
    
-
 
62
def __populateWarehouseMap():
-
 
63
    warehouses = Warehouse.query.filter(Warehouse.warehouseType.in_(['OURS', 'THIRD_PARTY'])).filter(Warehouse.inventoryType == 'GOOD').all()
-
 
64
    for warehouse in warehouses:
-
 
65
        warehouseMap[warehouse.id] = warehouse
-
 
66
        if not logisticsLocationWarehouseMap.has_key(warehouse.logisticsLocation):
-
 
67
            logisticsLocationWarehouseMap[warehouse.logisticsLocation] = []
-
 
68
        logisticsLocationWarehouseMap[warehouse.logisticsLocation].append(warehouse.id)
-
 
69
    
47
    
70
    
48
def get_Warehouse(warehouse_id):
71
def get_Warehouse(warehouse_id):
49
    return Warehouse.get_by(id=warehouse_id)
72
    return Warehouse.get_by(id=warehouse_id)
50
 
73
 
51
def get_vendor(vendorId):
74
def get_vendor(vendorId):
Line 462... Line 485...
462
    session.commit()
485
    session.commit()
463
    #**Update item availability cache**#
486
    #**Update item availability cache**#
464
    clear_item_availability_cache(item_id)
487
    clear_item_availability_cache(item_id)
465
    return True
488
    return True
466
 
489
 
-
 
490
def get_item_pincode_availability(pin_code, itemPricingMap):
-
 
491
    returnMap = {}
-
 
492
    missingItemPricingList = []
-
 
493
    for item_id, pricing in itemPricingMap.iteritems():
-
 
494
        if not pricing:
-
 
495
            missingItemPricingList.append(item_id)
-
 
496
        
-
 
497
    cc = CatalogClient().get_client()
-
 
498
    items = cc.getItems(missingItemPricingList)
-
 
499
    lc = LogisticsClient().get_client()
-
 
500
    #Consider selling price if its missing in itempricing
-
 
501
    for item in items:
-
 
502
        itemPricingMap[item.id] = item.sellingPrice
-
 
503
    pricingLocationInfoMap = __getLocationInfoMap(pin_code, itemPricingMap)    
-
 
504
    if pricingLocationInfoMap == -1:
-
 
505
        returnMap = {"pincode_serviceable":False}    
-
 
506
    else:
-
 
507
        #for item_id, sellingPrice in itemPricingMap.iteritems():
-
 
508
        allLocations = pricingLocationInfoMap[1].keys()    
-
 
509
        itemLocationAvailability = ItemLocationAvailabilityCache()
-
 
510
        itemLocationAvailabilityList = __get_item_location_availability_bulk(itemPricingMap.keys(), allLocations)
-
 
511
        pricingLocationsMap = {}
-
 
512
        for itemLocationAvailability in itemLocationAvailabilityList:
-
 
513
            sellingPrice = itemPricingMap[itemLocationAvailability.item_id]
-
 
514
            if pricingLocationInfoMap[sellingPrice] == -1:
-
 
515
                continue
-
 
516
            if not pricingLocationsMap.has_key(sellingPrice):
-
 
517
                pricingLocationsMap[sellingPrice] = pricingLocationInfoMap[sellingPrice].keys() 
-
 
518
            
-
 
519
            if itemLocationAvailability.location_id not in pricingLocationsMap[sellingPrice]:
-
 
520
                continue
-
 
521
            if not returnMap.has_key(itemLocationAvailability.item_id):
-
 
522
                returnMap[itemLocationAvailability.item_id] = ItemPincodeAvailability(vatQty = 0, totalQty = 0, minDeliveryDate=-1)
-
 
523
            itemPincodeAvailability = ItemPincodeAvailability()
-
 
524
            itemPincodeAvailability = returnMap[itemLocationAvailability.item_id]
-
 
525
            locationInfo = LocationInfo()
-
 
526
            locationInfo = pricingLocationInfoMap[sellingPrice][itemLocationAvailability.location_id]
-
 
527
            locationQty = itemLocationAvailability.virtual_availability + itemLocationAvailability.physical_availability
-
 
528
            if locationInfo.sameState:
-
 
529
                itemPincodeAvailability.vatQty += locationQty
-
 
530
            itemPincodeAvailability.totalQty += locationQty
-
 
531
            itemPincodeAvailability.isCod = itemPincodeAvailability.isCod or locationInfo.isCod 
-
 
532
            itemPincodeAvailability.isOtg = itemPincodeAvailability.isOtg or locationInfo.isOtg 
-
 
533
            if itemPincodeAvailability.minDeliveryDate == -1:
-
 
534
                itemPincodeAvailability.minDeliveryDate = itemLocationAvailability.min_transfer_delay + locationInfo.minDelay 
-
 
535
                itemPincodeAvailability.maxDeliveryDate = itemLocationAvailability.max_transfer_delay + locationInfo.maxDelay
-
 
536
            else:
-
 
537
                itemPincodeAvailability.minDeliveryDate = min(itemPincodeAvailability.minDeliveryDate, itemLocationAvailability.min_transfer_delay + locationInfo.minDelay) 
-
 
538
                itemPincodeAvailability.maxDeliveryDate = max(itemPincodeAvailability.maxDeliveryDate, itemLocationAvailability.max_transfer_delay + locationInfo.maxDelay)
-
 
539
        for itemId, itemPincodeAvailability in returnMap.iteritems():
-
 
540
            sellingPrice = itemPricingMap[itemId]
-
 
541
            locationsMap = pricingLocationsMap[sellingPrice]
-
 
542
            minDay = math.ceil(itemPincodeAvailability.minDeliveryDate)
-
 
543
            maxDay = math.ceil(itemPincodeAvailability.maxDeliveryDate)
-
 
544
            itemPincodeAvailability.minDeliveryDate, itemPincodeAvailability.maxDeliveryDate = __getDeliveryDate(minDay, itemPincodeAvailability.isCod, maxDay)
-
 
545
            
-
 
546
    return json.dumps(returnMap)
-
 
547
             
-
 
548
    
-
 
549
def __getLocationInfoMap(pin_code, itemPricingMap):
-
 
550
    priceList = list(set(itemPricingMap.values())) + [1]
-
 
551
    if not pincodePricingServiceabilityMap.has_key(pin_code):
-
 
552
        pincodePricingServiceabilityMap[pin_code] = {}
-
 
553
    pricingMap = pincodePricingServiceabilityMap[pin_code]
-
 
554
    if pricingMap != -1: 
-
 
555
        missingInMap = []
-
 
556
        for sellingPrice in priceList:
-
 
557
            if not pricingMap.has_key(sellingPrice):
-
 
558
                missingInMap.append(sellingPrice)
-
 
559
        
-
 
560
        lc = LogisticsClient().get_client()
-
 
561
        priceLocationInfoMap = lc.getLocationInfoMap(pin_code, missingInMap)
-
 
562
        if not priceLocationInfoMap:
-
 
563
            pricingMap = pincodePricingServiceabilityMap[pin_code] = -1
-
 
564
        else:
-
 
565
            for sellingPrice in missingInMap:
-
 
566
                if priceLocationInfoMap[sellingPrice] == {}:
-
 
567
                    pricingMap[sellingPrice] = -1
-
 
568
                else:
-
 
569
                    pricingMap[sellingPrice] = priceLocationInfoMap[sellingPrice]
-
 
570
        
-
 
571
    return pricingMap
-
 
572
    
-
 
573
def __getDeliveryDate(minDays, isCod, maxDays=None):
-
 
574
    curTime = datetime.datetime.now()
-
 
575
    if maxDays is None:
-
 
576
        maxDays = minDays 
-
 
577
    if isCod and curTime.hour < 15:
-
 
578
        maxDays = maxDays + 1
-
 
579
    return __getAdjustedDate(minDays), __getAdjustedDate(maxDays) 
-
 
580
 
-
 
581
def __getAdjustedDate(days):
-
 
582
    curDate = datetime.date.today()
-
 
583
    if curDate == last_date:
-
 
584
        if adjusted_dates.has_key(days):
-
 
585
            return adjusted_dates[days]
-
 
586
    else:
-
 
587
        adjusted_dates = {}
-
 
588
    lc = LogisticsClient().get_client()
-
 
589
    adjusted_day = lc.adjustDeliveryDays(datetime.datetime.now(), to_java_date(days))
-
 
590
    
-
 
591
    adjusted_date = to_java_date(datetime.datetime.combine(curDate, datetime.datetime.min.time()) + datetime.timedelta(days=adjusted_day))
-
 
592
    adjusted_dates[days] = adjusted_date
-
 
593
    return adjusted_date
467
 
594
 
468
def reduce_reservation_count(item_id, warehouse_id, source_id, order_id, quantity):
595
def reduce_reservation_count(item_id, warehouse_id, source_id, order_id, quantity):
469
    if not warehouse_id:
596
    if not warehouse_id:
470
        raise InventoryServiceException(101, "bad warehouse_id")
597
        raise InventoryServiceException(101, "bad warehouse_id")
471
        
598
        
Line 500... Line 627...
500
        __update_item_availability_cache(item_id, source_id)
627
        __update_item_availability_cache(item_id, source_id)
501
            ##Check risky status for the source
628
            ##Check risky status for the source
502
        __check_risky_item(item_id, source_id)
629
        __check_risky_item(item_id, source_id)
503
        return get_item_availability_for_location(item_id, source_id)
630
        return get_item_availability_for_location(item_id, source_id)
504
    
631
    
-
 
632
def get_item_location_availability(item_id, locations=[]):
-
 
633
    if locations:
-
 
634
        newLocations = locations + [-1]
-
 
635
        itemisedItemLocationAvailability = ItemLocationAvailabilityCache.filter_by(itemId=item_id).filter(ItemLocationAvailability.locationId.in_(newLocations)).all()
-
 
636
    else:
-
 
637
        itemisedItemLocationAvailability = ItemLocationAvailabilityCache.filter_by(itemId=item_id).all()
-
 
638
    if not itemisedItemLocationAvailability:
-
 
639
        __update_item_location(item_id)
-
 
640
        return get_item_location_availability(item_id, locations)
-
 
641
    else:
-
 
642
        thriftList = []
-
 
643
        for itemLocationAvailability in itemisedItemLocationAvailability:
-
 
644
            if itemLocationAvailability.location_id == -1:
-
 
645
                itemisedItemLocationAvailability
-
 
646
            else:
-
 
647
                thriftList.append(to_t_item_location_availability(itemLocationAvailability))
-
 
648
        return thriftList
-
 
649
 
-
 
650
def __get_item_location_availability_bulk(item_ids, locations=[]):
-
 
651
    
-
 
652
    query = ItemLocationAvailabilityCache.query.filter(ItemLocationAvailabilityCache.item_id.in_(item_ids))
-
 
653
    allPopulated = query.filter(ItemLocationAvailabilityCache.location_id==-1).all()
-
 
654
    if len(item_ids) > len(allPopulated):
-
 
655
        for item_id in allPopulated:
-
 
656
            if item_id not in item_ids:
-
 
657
                __update_item_location(item_id)
-
 
658
    if locations:
-
 
659
        query.filter(ItemLocationAvailabilityCache.location_id.in_(locations)).all()
-
 
660
    return query.all()
-
 
661
    
-
 
662
 
-
 
663
def clear_item_location_availability_cache(item_id, locations=[]):
-
 
664
    if type(item_id)==list:
-
 
665
        ItemLocationAvailabilityCache.query.filter(ItemLocationAvailabilityCache.itemId.in_(item_id)).delete(synchronize_session='fetch')
-
 
666
        session.commit()
-
 
667
        t = threading.Thread(target=_task_update_item_availability_cache, args=(item_id,))
-
 
668
        t.start()
-
 
669
    elif item_id:
-
 
670
        ItemLocationAvailabilityCache.query.filter_by(itemId = item_id).delete()
-
 
671
    session.commit()
-
 
672
 
505
def clear_item_availability_cache(item_id = None):
673
def clear_item_availability_cache(item_id = None):
506
    print item_id
674
    print item_id
507
    if type(item_id)==list:
675
    if type(item_id)==list:
508
        ItemAvailabilityCache.query.filter(ItemAvailabilityCache.itemId.in_(item_id)).delete(synchronize_session='fetch')
676
        ItemAvailabilityCache.query.filter(ItemAvailabilityCache.itemId.in_(item_id)).delete(synchronize_session='fetch')
509
        session.commit()
677
        session.commit()
Line 526... Line 694...
526
            except:
694
            except:
527
                print "Could not update cache for "
695
                print "Could not update cache for "
528
                continue
696
                continue
529
    return True   
697
    return True   
530
 
698
 
-
 
699
def __update_item_location(item_id):
-
 
700
    ignoredWhs = get_ignored_warehouses(item_id)
-
 
701
        
-
 
702
    itemsnapshot = CurrentInventorySnapshot.query.filter_by(item_id = item_id).all()
-
 
703
    locationsMap = {}
-
 
704
    for row in itemsnapshot:
-
 
705
        warehouse = warehouseMap[row.warehouse_id]
-
 
706
        if row.warehouse_id in ignoredWhs:
-
 
707
            continue 
-
 
708
 
-
 
709
        location = warehouse.logisticsLocation
-
 
710
        if not locationsMap.has_key(location):
-
 
711
            locationsMap[location] = {"physicalQty":0, "virtualQty":0,    "minTransferDelay":100, "maxTransferDelay":0}
-
 
712
        locationMap = locationsMap[location]
-
 
713
        if warehouse.type == 'THIRD_PARTY':
-
 
714
            locationMap["virtualQty"] += max(0, row.availability - row.reserverd - row.held)
-
 
715
            locationMap["minTransferDelay"] = min(locationMap["minTransferDelay"], row.transferDelayInHours/24)
-
 
716
            locationMap["maxTransferDelay"] = max(locationMap["maxTransferDelay"], row.transferDelayInHours/24)
-
 
717
        else:
-
 
718
            locationMap["physicalQty"] += max(0, row.availability - row.reserverd - row.held)
-
 
719
            locationMap["minTransferDelay"] = 0
-
 
720
    for location, locationMap in locationsMap.iteritems():
-
 
721
        if locationMap["virtualQty"] > 0 or locationMap["physicalQty"] > 0: 
-
 
722
            itemLocationAvailability = ItemLocationAvailabilityCache()
-
 
723
            itemLocationAvailability.item_id = item_id
-
 
724
            itemLocationAvailability.location_id = location
-
 
725
            itemLocationAvailability.max_transfer_delay = locationMap["maxTransferDelay"]
-
 
726
            itemLocationAvailability.min_transfer_delay = locationMap["minTransferDelay"]
-
 
727
            itemLocationAvailability.virtual_availability = locationMap["virtualQty"]
-
 
728
            itemLocationAvailability.physical_availability = locationMap["physicalQty"]
-
 
729
    #Add location -1 for each item
-
 
730
    itemLocationAvailability = ItemLocationAvailabilityCache()
-
 
731
    itemLocationAvailability.item_id = item_id
-
 
732
    itemLocationAvailability.location_id = -1
-
 
733
    session.commit()
-
 
734
            
-
 
735
 
-
 
736
 
-
 
737
 
-
 
738
 
531
def __update_item_availability_cache(item_id, source_id, item=None):
739
def __update_item_availability_cache(item_id, source_id, item=None):
532
    """
740
    """
533
    Determines the warehouse that should be used to fulfil an order for the given item.
741
    Determines the warehouse that should be used to fulfil an order for the given item.
534
    Algorithm explained at https://sites.google.com/a/shop2020.in/virtual-w-h-and-inventory/technical-details
742
    Algorithm explained at https://sites.google.com/a/shop2020.in/virtual-w-h-and-inventory/technical-details
535
 
743