Rev 16029 | Rev 17990 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed
'''Created on 23-Mar-2010@author: ashish'''from elixir import *from functools import partialfrom shop2020.clients.CatalogClient import CatalogClientfrom shop2020.clients.TransactionClient import TransactionClientfrom shop2020.model.v1.inventory.impl import DataServicefrom shop2020.model.v1.inventory.impl.Convertors import to_t_warehouse, \to_t_itemidwarehouseid, to_t_statefrom shop2020.model.v1.inventory.impl.DataService import Warehouse, \ItemInventoryHistory, CurrentInventorySnapshot, VendorItemPricing, \VendorItemMapping, Vendor, MissedInventoryUpdate, BadInventorySnapshot, \VendorHolidays, ItemAvailabilityCache, \CurrentReservationSnapshot, IgnoredInventoryUpdateItems, ItemStockPurchaseParams, \OOSStatus, AmazonInventorySnapshot, StateMaster, HoldInventoryDetail, AmazonFbaInventorySnapshot, \SnapdealInventorySnapshot, FlipkartInventorySnapshot, SnapdealStockAtEOD, FlipkartStockAtEOD, StockWeightedNlcInfofrom shop2020.thriftpy.model.v1.inventory.ttypes import \InventoryServiceException, HolidayType, InventoryType, WarehouseTypefrom shop2020.thriftpy.model.v1.order.ttypes import AlertTypefrom shop2020.thriftpy.purchase.ttypes import PurchaseServiceExceptionfrom shop2020.utils import EmailAttachmentSenderfrom shop2020.utils.EmailAttachmentSender import mailfrom shop2020.utils.Utils import to_py_date, to_java_datefrom sqlalchemy.orm.exc import MultipleResultsFound, NoResultFoundfrom sqlalchemy.sql import or_from sqlalchemy.sql.expression import and_, func, distinct, descfrom sqlalchemy.sql.functions import countimport calendarimport datetimeimport sysimport threadingimport mathto_addresses = ["khushal.bhatia@shop2020.in", "chaitnaya.vats@shop2020.in", "chandan.kumar@shop2020.in",'manoj.kumar@shop2020.in']mail_user = "cnc.center@shop2020.in"mail_password = "5h0p2o2o"skippedItems = { 175 : [27, 2160, 2175, 2163, 2158, 7128, 26, 2154],193 : [5839] }OOS_CALCULATION_TIME = 23def initialize(dbname='inventory', db_hostname="localhost"):DataService.initialize(dbname, db_hostname)def get_Warehouse(warehouse_id):return Warehouse.get_by(id=warehouse_id)def get_vendor(vendorId):return Vendor.get_by(id=vendorId)def get_state(stateId):return StateMaster.get_by(id=stateId)def get_all_warehouses_by_status(status):return Warehouse.query.all()def get_all_items_for_warehouse(warehouse_id):warehouse = get_Warehouse(warehouse_id)if not warehouse:raise InventoryServiceException(108, "bad warehouse")return warehouse.all_itemsdef add_warehouse(warehouse):if not warehouse:raise InventoryServiceException(108, "Bad warehouse")if get_Warehouse(warehouse.id):#warehouse is already present.raise InventoryServiceException(101, "Warehouse already present")ds_warehouse = Warehouse()ds_warehouse.location = warehouse.locationds_warehouse.status = 3ds_warehouse.addedOn = datetime.datetime.now()ds_warehouse.lastCheckedOn = datetime.datetime.now()ds_warehouse.tinNumber = warehouse.tinNumberds_warehouse.pincode = warehouse.pincodeds_warehouse.billingType = warehouse.billingTypeds_warehouse.billingWarehouseId = warehouse.billingWarehouseIdds_warehouse.displayName = warehouse.displayNameds_warehouse.inventoryType = InventoryType._VALUES_TO_NAMES[warehouse.inventoryType]ds_warehouse.isAvailabilityMonitored = warehouse.isAvailabilityMonitoredds_warehouse.logisticsLocation = warehouse.logisticsLocationds_warehouse.shippingWarehouseId = warehouse.shippingWarehouseIdds_warehouse.transferDelayInHours = warehouse.transferDelayInHoursds_warehouse.vendor = get_vendor(warehouse.vendor.id)ds_warehouse.state = get_state(warehouse.stateId)ds_warehouse.warehouseType = WarehouseType._VALUES_TO_NAMES[warehouse.warehouseType]if warehouse.vendorString:ds_warehouse.vendorString = warehouse.vendorStringsession.commit()return ds_warehouse.iddef get_ignored_items(warehouse_id):Ignored_inventory_items = IgnoredInventoryUpdateItems.query.filter_by(warehouse_id=warehouse_id).all()negativeItems = []for Ignored_inventory_item in Ignored_inventory_items:try:item_id = Ignored_inventory_item.item_idnegativeItems.append(item_id)except:raise InventoryServiceException(108, "Some unforeseen error while updating inventory")return negativeItemsdef get_ignored_warehouses(item_id):Ignored_inventory_items = IgnoredInventoryUpdateItems.query.filter_by(item_id=item_id).all()warehouses = []for Ignored_inventory_item in Ignored_inventory_items:warehouses.append(Ignored_inventory_item.warehouse_id)return warehousesdef update_inventory_history(warehouse_id, timestamp, availability):warehouse = get_Warehouse(warehouse_id)if not warehouse:raise InventoryServiceException(107, "Warehouse? Where?")vendor = warehouse.vendortime = datetime.datetime.now()for item_key, quantity in availability.iteritems():try:vendor_item_mapping = VendorItemMapping.query.filter_by(vendor=vendor, item_key=item_key).one();item_id = vendor_item_mapping.item_idexcept:continuetry:item_inventory_history = ItemInventoryHistory()item_inventory_history.warehouse = warehouseitem_inventory_history.item_id = item_iditem_inventory_history.timestamp = timeitem_inventory_history.availability = quantityexcept:raise InventoryServiceException(108, "Some unforeseen error while updating inventory")session.commit()def update_inventory(warehouse_id, timestamp, availability):warehouse = get_Warehouse(warehouse_id)if not warehouse:raise InventoryServiceException(107, "Warehouse? Where?")time = datetime.datetime.now()warehouse.lastCheckedOn = timewarehouse.vendorString = timestampvendor = warehouse.vendoritem_ids = []for item_key, quantity in availability.iteritems():try:vendor_item_mapping = VendorItemMapping.query.filter_by(vendor=vendor, item_key=item_key).one();item_id = vendor_item_mapping.item_iditem_ids.append(item_id)except:print 'Skipping update for ' + item_key + ' quantity ' + str(quantity) + ' warehouse id: ' + str(warehouse_id)__send_mail_for_missing_key(item_key, quantity, warehouse_id)continuetry:current_inventory_snapshot = CurrentInventorySnapshot.get_by(item_id=item_id, warehouse=warehouse)if not current_inventory_snapshot:current_inventory_snapshot = CurrentInventorySnapshot()current_inventory_snapshot.item_id = item_idcurrent_inventory_snapshot.warehouse = warehousecurrent_inventory_snapshot.availability = 0current_inventory_snapshot.reserved = 0current_inventory_snapshot.held = 0# added the difference in the current inventorycurrent_inventory_snapshot.availability = current_inventory_snapshot.availability + quantityitem = __get_item_from_master(item_id)try:if quantity > 0 and __get_item_reserved(item_id) > 0:cl = TransactionClient().get_client()#FIXME hardcoding for warehouse idcl.addAlert(AlertType.NEW_INVENTORY_ALERT, 5, "Inventory received for item " + item.brand + " " + item.modelName + " " + item.modelNumber + " " + item.color)except:print "Not able to raise alert for incoming inventory"if current_inventory_snapshot.availability < 0:__send_alert_for_negative_availability(item, current_inventory_snapshot.availability, warehouse)except:print "Some unforeseen error while updating inventory:", sys.exc_info()[0]raise InventoryServiceException(108, "Some unforeseen error while updating inventory")session.commit()#**Update item availability cache**#for item_id in item_ids:clear_item_availability_cache(item_id)def __send_alert_for_negative_reserved(item, reserved, warehouse):itemName = " ".join([str(item.id), str(item.brand), str(item.modelName), str(item.modelNumber), str(item.color)])EmailAttachmentSender.mail(mail_user, mail_password, 'manish.sharma@shop2020.in', 'Negative reserved: ' + str(reserved) + ' for Item Id: ' + itemName + ' warehouse id: ' + str(warehouse.id), None)def __send_alert_for_negative_availability(item, availability, warehouse):itemName = " ".join([str(item.id), str(item.brand), str(item.modelName), str(item.modelNumber), str(item.color)])# EmailAttachmentSender.mail('cnc.center@shop2020.in', '5h0p2o2o', 'amar.kumar@shop2020.in', 'Negative availability ' + str(availability) + ' for Item id: ' + itemName + ' warehouse id: ' + str(warehouse.id), None)def __send_mail_for_missing_key(item_key, quantity, warehouse_id):missedInventoryUpdate = MissedInventoryUpdate.get_by(itemKey = item_key, warehouseId = warehouse_id)# One email per product key mismatchif not missedInventoryUpdate:missedInventoryUpdate = MissedInventoryUpdate()missedInventoryUpdate.itemKey = item_keymissedInventoryUpdate.quantity = quantitymissedInventoryUpdate.isIgnored = 1missedInventoryUpdate.timestamp = datetime.datetime.now()missedInventoryUpdate.warehouseId = warehouse_idsession.commit()try:EmailAttachmentSender.mail(mail_user, mail_password, ['chaitnaya.vats@shop2020.in', 'chandan.kumar@shop2020.in', 'khushal.bhatia@shop2020.in', 'manoj.kumar@shop2020.in'], 'Skipped inventory update for ' + item_key + ' quantity ' + str(quantity) + ' warehouse id: ' + str(warehouse_id), None)except:print "Not able to send email. No issues, we can continue with updates."else:missedInventoryUpdate.quantity += quantitysession.commit()def add_inventory(itemId, warehouseId, quantity):current_inventory_snapshot = CurrentInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)if not current_inventory_snapshot:current_inventory_snapshot = CurrentInventorySnapshot()current_inventory_snapshot.item_id = itemIdcurrent_inventory_snapshot.warehouse_id = warehouseIdcurrent_inventory_snapshot.availability = 0current_inventory_snapshot.reserved = 0current_inventory_snapshot.held = 0# added the difference in the current inventorycurrent_inventory_snapshot.availability = current_inventory_snapshot.availability + quantitysession.commit()#**Update item availability cache**#clear_item_availability_cache(itemId)if current_inventory_snapshot.availability < 0:item = __get_item_from_master(itemId)__send_alert_for_negative_availability(item, current_inventory_snapshot.availability, get_Warehouse(warehouseId))def add_bad_inventory(itemId, warehouseId, quantity):bad_inventory_snapshot = BadInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)if not bad_inventory_snapshot:bad_inventory_snapshot = BadInventorySnapshot()bad_inventory_snapshot.item_id = itemIdbad_inventory_snapshot.warehouse_id = warehouseIdbad_inventory_snapshot.availability = 0# added the difference in the current inventorybad_inventory_snapshot.availability += quantitysession.commit()if bad_inventory_snapshot.availability < 0:item = __get_item_from_master(itemId)__send_alert_for_negative_availability(item, bad_inventory_snapshot.availability, get_Warehouse(warehouseId))def get_item_inventory_by_item_id(item_id):return CurrentInventorySnapshot.query.filter_by(item_id=item_id).all()def retire_warehouse(warehouse_id):if not warehouse_id:raise InventoryServiceException(101, "Bad warehouse id")warehouse = get_Warehouse(warehouse_id)if not warehouse:raise InventoryServiceException(108, "warehouse id not present")warehouse.status = 0;session.commit()def get_item_availability_for_warehouse(warehouse_id, item_id):ignore = IgnoredInventoryUpdateItems.query.filter_by(item_id=item_id).filter_by(warehouse_id = warehouse_id).all()if ignore:return 0try:current_inventory_snapshot = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id).filter_by(item_id = item_id).one()return current_inventory_snapshot.availability - current_inventory_snapshot.reserved - current_inventory_snapshot.heldexcept:return 0def get_item_availability_for_our_warehouses(item_ids):our_warehouses = Warehouse.query.filter_by(warehouseType = 'OURS', inventoryType = 'GOOD').all()our_thirdparty_warehouses = Warehouse.query.filter_by(warehouseType = 'OURS_THIRDPARTY').all()warehouse_ids = []for warehouse in our_warehouses :warehouse_ids.append(warehouse.id)#for warehouse in our_thirdparty_warehouses :# warehouse_ids.append(warehouse.id)availability_map = dict()try :for item_id in item_ids :total_availability = 0for current_inventory_snapshot in CurrentInventorySnapshot.query.filter(CurrentInventorySnapshot.warehouse_id.in_(warehouse_ids)).filter_by(item_id = item_id).all():total_availability += current_inventory_snapshot.availabilityif total_availability >0:availability_map[item_id] = total_availabilityexcept Exception as e:print eraise PurchaseServiceException(101, 'Exception while fetching availability of items in our warehouses')return availability_map'''This method returns quantity of a particular item across all warehouses whose ids is providedif warehouse_ids is null it checks for inventory in all warehouses.'''def __get_item_availability(item, warehouse_ids):if warehouse_ids is None:all_inventory = CurrentInventorySnapshot.query.filter_by(item = item).all()availability = 0reserved = 0for currInv in all_inventory:availability = availability + currInv.availabilityreserved = reserved + currInv.reservedreturn availability - reservedelse:total_availability = 0for current_inventory_snapshot in CurrentInventorySnapshot.query.filter(CurrentInventorySnapshot.warehouse_id.in_(warehouse_ids)).filter_by(item_id = item.id).all():total_availability += current_inventory_snapshot.availability - current_inventory_snapshot.reservedreturn total_availabilitydef __get_item_reserved(item_id):all_inventory = CurrentInventorySnapshot.query.filter_by(item_id = item_id).all()reserved = 0for currInv in all_inventory:reserved = reserved + currInv.reservedreturn reserveddef __get_item_availability_at_warehouse(warehouse_id, item_id):inventory = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id).one()return inventory.availabilitydef is_order_billable(item_id, warehouse_id, source_id, order_id):reservations = CurrentReservationSnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id).order_by(CurrentReservationSnapshot.promised_shipping_timestamp).order_by(CurrentReservationSnapshot.created_timestamp).all()availability = __get_item_availability_at_warehouse(warehouse_id, item_id)for reservation in reservations:availability = availability - reservation.reservedif reservation.order_id == order_id and reservation.source_id == source_id:breakif availability < 0:return Falsereturn Truedef reserve_item_in_warehouse(item_id, warehouse_id, source_id, order_id, created_timestamp, promised_shipping_timestamp, quantity):if not warehouse_id:raise InventoryServiceException(101, "bad warehouse_id")query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)try:current_inventory_snapshot = query.one()except:current_inventory_snapshot = CurrentInventorySnapshot()current_inventory_snapshot.warehouse_id = warehouse_idcurrent_inventory_snapshot.item_id = item_idcurrent_inventory_snapshot.availability = 0current_inventory_snapshot.reserved = 0current_inventory_snapshot.held = 0current_inventory_snapshot.reserved = current_inventory_snapshot.reserved + quantityreservation = CurrentReservationSnapshot()reservation.item_id = item_idreservation.warehouse_id = warehouse_idreservation.source_id = source_idreservation.order_id = order_idreservation.created_timestamp = to_py_date(created_timestamp)reservation.promised_shipping_timestamp = to_py_date(promised_shipping_timestamp)reservation.reserved = quantitysession.commit()try:order_client = TransactionClient().get_client()order = order_client.getOrder(order_id)if order.source:holdInventoryDetail = HoldInventoryDetail.query.filter_by(item_id = item_id, warehouse_id = warehouse_id, source = order.source).first()if holdInventoryDetail is None or holdInventoryDetail.held<=0:holdInventoryDetails = HoldInventoryDetail.query.filter_by(item_id = item_id, source = order.source).all()if holdInventoryDetails:for hID in holdInventoryDetails:if hID.held>0:holdInventoryDetail = hIDif holdInventoryDetail is not None and holdInventoryDetail.held>0:previousHeld = holdInventoryDetail.heldholdInventoryDetail.held = max(0, holdInventoryDetail.held -quantity)diff = previousHeld-holdInventoryDetail.heldcurrent_inventory_snapshot = CurrentInventorySnapshot.get_by(item_id=item_id, warehouse_id=holdInventoryDetail.warehouse_id)if current_inventory_snapshot is not None:current_inventory_snapshot.held = max(0, current_inventory_snapshot.held - diff)session.commit()except:print "Unable to release hold Inventory for item_id " + str(item_id) + " warehouse_id " + str(warehouse_id) + " source " + str(source_id)#session.commit()#**Update item availability cache**#clear_item_availability_cache(item_id)return Truedef update_reservation_for_order(item_id, warehouse_id, source_id, order_id, created_timestamp, promised_shipping_timestamp, quantity):if not warehouse_id:raise InventoryServiceException(101, "bad warehouse_id")warehouse = get_Warehouse(warehouse_id)item_pricing = get_item_pricing(item_id, warehouse.vendor.id)if not item_pricing:raise InventoryServiceException(101, "No Pricing Info found for vendor and Item")query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)try:new_current_inventory_snapshot = query.one()except:new_current_inventory_snapshot = CurrentInventorySnapshot()new_current_inventory_snapshot.warehouse_id = warehouse_idnew_current_inventory_snapshot.item_id = item_idnew_current_inventory_snapshot.availability = 0new_current_inventory_snapshot.reserved = 0new_current_inventory_snapshot.reserved = new_current_inventory_snapshot.reserved + quantitynew_reservation = CurrentReservationSnapshot()new_reservation.item_id = item_idnew_reservation.warehouse_id = warehouse_idnew_reservation.source_id = source_idnew_reservation.order_id = order_idnew_reservation.created_timestamp = to_py_date(created_timestamp)new_reservation.promised_shipping_timestamp = to_py_date(promised_shipping_timestamp)new_reservation.reserved = quantitytry:order_client = TransactionClient().get_client()order = order_client.getOrder(order_id)if order.source:holdInventoryDetail = HoldInventoryDetail.query.filter_by(item_id = item_id, warehouse_id = warehouse_id, source = order.source).first()if holdInventoryDetail is None or holdInventoryDetail.held<=0:holdInventoryDetails = HoldInventoryDetail.query.filter_by(item_id = item_id, source = order.source).all()if holdInventoryDetails:for hID in holdInventoryDetails:if hID.held>0:holdInventoryDetail = hIDif holdInventoryDetail is not None and holdInventoryDetail.held>0:previousHeld = holdInventoryDetail.heldholdInventoryDetail.held = max(0, holdInventoryDetail.held -quantity)diff = previousHeld-holdInventoryDetail.heldcurrent_inventory_snapshot = CurrentInventorySnapshot.get_by(item_id=item_id, warehouse_id=holdInventoryDetail.warehouse_id)if current_inventory_snapshot is not None:current_inventory_snapshot.held = max(0, current_inventory_snapshot.held - diff)session.commit()except:print "Unable to release hold Inventory for item_id " + str(item_id) + " warehouse_id " + str(warehouse_id) + " source " + str(source_id)order_client = TransactionClient().get_client()order = order_client.getOrder(order_id)for lineitem in order.lineitems:query = CurrentInventorySnapshot.query.filter_by(warehouse_id = order.fulfilmentWarehouseId, item_id = lineitem.item_id)try:current_inventory_snapshot = query.one()current_inventory_snapshot.reserved = current_inventory_snapshot.reserved - quantityreservation = CurrentReservationSnapshot.query.filter_by(warehouse_id = order.fulfilmentWarehouseId, item_id = lineitem.item_id, source_id = source_id, order_id = order_id).one()if reservation.reserved == quantity:reservation.delete()else:reservation.reserved -= quantityclear_item_availability_cache(lineitem.item_id)session.commit()try:if current_inventory_snapshot.reserved < 0:item = __get_item_from_master(lineitem.item_id)__send_alert_for_negative_reserved(item, current_inventory_snapshot.reserved, get_Warehouse(order.fulfilmentWarehouseId))except:print "Error in sending negative reserved alert:", sys.exc_info()[0]return Falseexcept:print "Error in reducing reservation for item:", sys.exc_info()[0]return Falsesession.commit()#**Update item availability cache**#clear_item_availability_cache(item_id)return Truedef reduce_reservation_count(item_id, warehouse_id, source_id, order_id, quantity):if not warehouse_id:raise InventoryServiceException(101, "bad warehouse_id")query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)try:current_inventory_snapshot = query.one()current_inventory_snapshot.reserved = current_inventory_snapshot.reserved - quantityreservation = CurrentReservationSnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id, source_id = source_id, order_id = order_id).one()if reservation.reserved == quantity:reservation.delete()else:reservation.reserved -= quantitysession.commit()#**Update item availability cache**#clear_item_availability_cache(item_id)if current_inventory_snapshot.reserved < 0:item = __get_item_from_master(item_id)__send_alert_for_negative_reserved(item, current_inventory_snapshot.reserved, get_Warehouse(warehouse_id))return Trueexcept:print "Unexpected error:", sys.exc_info()[0]return False#This is not the source as in snapdeal or website, this source is to incorporate different pricing depending upon source id.#i.e. if user visiting to our site has specific source he would se pricing on that souce basis. Default source is 1.def get_item_availability_for_location(item_id, source_id):item_availability = ItemAvailabilityCache.get_by(itemId=item_id, sourceId = source_id)if item_availability:return [item_availability.warehouseId, item_availability.expectedDelay, item_availability.billingWarehouseId, item_availability.sellingPrice, item_availability.totalAvailability, item_availability.weight]else:__update_item_availability_cache(item_id, source_id)##Check risky status for the source__check_risky_item(item_id, source_id)return get_item_availability_for_location(item_id, source_id)def clear_item_availability_cache(item_id = None):print item_idif type(item_id)==list:ItemAvailabilityCache.query.filter(ItemAvailabilityCache.itemId.in_(item_id)).delete(synchronize_session='fetch')session.commit()t = threading.Thread(target=_task_update_item_availability_cache, args=(item_id,))t.start()elif item_id:ItemAvailabilityCache.query.filter_by(itemId = item_id).delete()session.commit()def _task_update_item_availability_cache(item_ids):client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()items = client.getItems(item_ids)for item_id in item_ids:item_availability = ItemAvailabilityCache.get_by(itemId=item_id, sourceId=1)if not item_availability:try:__update_item_availability_cache(item_id, 1, items[id])__check_risky_item(id, 1)except:print "Could not update cache for "continuereturndef __update_item_availability_cache(item_id, source_id, item=None):"""Determines the warehouse that should be used to fulfil an order for the given item.Algorithm explained at https://sites.google.com/a/shop2020.in/virtual-w-h-and-inventory/technical-detailsIt will be ensured that every item has either a preferred vendor specified or at least for one vendor its transfer price should be defined.This is needed to associate an item with at least one vendor so that in default case when its available no where, we know from where to procure it.if item available at any OUR-GOOD warehouse// OUR-GOOD warehouses have inventory risk; So, we empty them first!// We can start with minimum transfer price criterion but down the line we can also bring in Inventory ageassign OUR-GOOD warehouse with minimum transfer priceelseif Preferred vendor is specified and marked Sticky// Always purchase from Preferred if its marked stickyassign preferred vendor's THIRDPARTY GOOD/VIRTUAL warehouseelseif item available in a THIRDPARTY GOOD/VIRTUAL warehouseassign THIRDPARTY GOOD/VIRTUAL warehouse where item is available with minimal transfer delay followed by minimum transfer priceelse// Item not available at any warehouse, OURS or THIRDPARTYIf Preferred vendor is specifiedassign preferred vendor's THIRDPARTY GOOD/VIRTUAL warehouseelseassign THIRDPARTY GOOD/VIRTUAL warehouse with minimum transfer priceReturns an ordered list of size 4 with following elements in the given order:1. Logistics location of the warehouse which was finally picked up to ship the order.2. Expected delay added by the category manager.3. Id of the warehouse which was finally picked up.Parameters:- itemId"""if item is None:item = __get_item_from_source(item_id, source_id)item_pricing = {}for vendorItemPricing in VendorItemPricing.query.filter_by(item_id=item_id).all():item_pricing[vendorItemPricing.vendor_id] = vendorItemPricingignoredWhs = get_ignored_warehouses(item_id)warehouses = {}ourGoodWarehouses = {}thirdpartyWarehouses = {}preferredThirdpartyWarehouses = {}for warehouse in Warehouse.query.all():if (warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.BAD] or warehouse.warehouseType == WarehouseType._VALUES_TO_NAMES[WarehouseType.OURS_THIRDPARTY]):continuewarehouses[warehouse.id] = warehouseif warehouse.warehouseType == WarehouseType._VALUES_TO_NAMES[WarehouseType.OURS]:if warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]:ourGoodWarehouses[warehouse.id] = warehouseelse:thirdpartyWarehouses[warehouse.id] = warehouseif item.preferredVendor == warehouse.vendor_id and warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]:preferredThirdpartyWarehouses[warehouse.id] = warehousewarehouse_retid = -1total_availability = 0[warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_price(ourGoodWarehouses, ignoredWhs, item_id, item_pricing, False)if warehouse_retid == -1:if item.preferredVendor and item.isWarehousePreferenceSticky:[warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_delay(preferredThirdpartyWarehouses, ignoredWhs, item_id, item_pricing)if warehouse_retid == -1:warehouse_retid = preferredThirdpartyWarehouses.keys()[0]else:[warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_delay(thirdpartyWarehouses, ignoredWhs, item_id, item_pricing)if warehouse_retid == -1:if item.preferredVendor:warehouse_retid = preferredThirdpartyWarehouses.keys()[0]else:[warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_price(thirdpartyWarehouses, ignoredWhs, item_id, item_pricing, True)warehouse = warehouses[warehouse_retid]billingWarehouseId = warehouse.billingWarehouseId# Fetching billing warehouse of a Good billable warehouse corresponding to the virtual oneif not warehouse.billingWarehouseId:for w in Warehouse.query.filter_by(vendor_id = warehouse.vendor_id, inventoryType = InventoryType._VALUES_TO_NAMES[InventoryType.GOOD], logisticsLocation=warehouse.logisticsLocation).all():if w.billingWarehouseId:billingWarehouseId = w.billingWarehouseIdbreakexpectedDelay = item.expectedDelayif expectedDelay is None:print 'expectedDelay field for this item was Null. Resetting it to 0'expectedDelay = 0else:expectedDelay = int(item.expectedDelay)if total_availability <= 0:if item.preferredVendor in [1, 5]:expectedDelay = expectedDelay + 3else:expectedDelay = expectedDelay + 2else:if warehouse.transferDelayInHours:expectedDelay = expectedDelay + warehouse.transferDelayInHours / 24if warehouse.warehouseType == WarehouseType.THIRD_PARTY:expectedDelay = expectedDelay + __get_vendor_holiday_delay(warehouse.vendor_id, expectedDelay)total_availability = 0for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():if entry.warehouse_id not in ignoredWhs:if entry.warehouse_id not in ourGoodWarehouses and entry.warehouse_id not in thirdpartyWarehouses:continueif entry.warehouse_id in ourGoodWarehouses and ourGoodWarehouses[entry.warehouse_id].billingWarehouseId == billingWarehouseId:total_availability += entry.availability - entry.reserved - entry.heldelif entry.warehouse_id in thirdpartyWarehouses:vendorId = thirdpartyWarehouses[entry.warehouse_id].vendor_idfor goodWarehouse in ourGoodWarehouses.values():if goodWarehouse.vendor_id==vendorId and goodWarehouse.billingWarehouseId == billingWarehouseId and warehouse.logisticsLocation==goodWarehouse.logisticsLocation:total_availability += entry.availability - entry.reserved - entry.heldbreakitem_availability_cache = ItemAvailabilityCache.get_by(itemId=item_id, sourceId=source_id)if item_availability_cache is None:item_availability_cache = ItemAvailabilityCache()item_availability_cache.itemId = item_iditem_availability_cache.sourceId = source_iditem_availability_cache.warehouseId = int(warehouse_retid)item_availability_cache.expectedDelay = expectedDelayitem_availability_cache.billingWarehouseId = billingWarehouseIditem_availability_cache.sellingPrice = item.sellingPriceitem_availability_cache.totalAvailability = total_availability#item_availability_cache.location = warehouse.logisticsLocationitem_availability_cache.weight = 1000*item.weight if item.weight else 300session.commit()def __get_warehouse_with_min_transfer_price(warehouses, ignoredWhs, item_id, item_pricing, ignoreAvailability):warehouse_retid = -1minTransferPrice = Nonetotal_availability = 0availabilityForBillingWarehouses = {}warehousesAvailability = {}availability = 0billing_warehouse_retid = Noneif not ignoreAvailability:for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():entry.reserved = max(entry.reserved, 0)entry.held = max(entry.held, 0)#if entry.availability > entry.reserved:warehousesAvailability[entry.warehouse_id] = [entry.availability, entry.reserved, entry.held]if len(ignoredWhs) > 0:for whid in ignoredWhs:if warehousesAvailability.has_key(whid):warehousesAvailability[whid][0] = 0warehousesAvailability[whid][1] = 0warehousesAvailability[whid][2] = 0for warehouse in warehouses.values():if not ignoreAvailability:#TODO Mistake no entry for this warehouse.id in warehouseswithAvailabif warehouse.id not in warehousesAvailability:continueentry = warehousesAvailability[warehouse.id]if warehouse.billingWarehouseId in availabilityForBillingWarehouses:if warehouse.billingWarehouseId is not None or warehouse.billingWarehouseId != 0:availabilityForBillingWarehouses[warehouse.billingWarehouseId] = availabilityForBillingWarehouses[warehouse.billingWarehouseId] + entry[0] - entry[1] - entry[2]else:if warehouse.billingWarehouseId is not None or warehouse.billingWarehouseId != 0:availabilityForBillingWarehouses[warehouse.billingWarehouseId] = entry[0] - entry[1] - entry[2]if entry[0] <= (entry[1] + entry[2]):continuetotal_availability += entry[0] - entry[1] - entry[2]# Missing transfer price cases should not impact warehouse assignmenttransferPrice = Noneif item_pricing.has_key(warehouse.vendor_id):transferPrice = item_pricing[warehouse.vendor_id].nlcif minTransferPrice is None or (transferPrice and minTransferPrice > transferPrice):warehouse_retid = warehouse.idbilling_warehouse_retid = warehouse.billingWarehouseIdminTransferPrice = transferPriceif billing_warehouse_retid in availabilityForBillingWarehouses:availability = availabilityForBillingWarehouses[billing_warehouse_retid]else:availability = total_availabilityreturn [warehouse_retid, availability]def __get_warehouse_with_min_transfer_delay(warehouses, ignoredWhs, item_id, item_pricing):minTransferDelay = NoneminTransferDelayWarehouses = {}total_availability = 0for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():entry.reserved = max(entry.reserved, 0)entry.held = max(entry.held, 0)if warehouses.has_key(entry.warehouse_id):warehouse = warehouses[entry.warehouse_id]#if entry.availability > entry.reserved:if entry.warehouse_id not in ignoredWhs:total_availability += entry.availability - entry.reserved - entry.heldif entry.availability - entry.reserved - entry.held <= 0:continuetransferDelay = warehouse.transferDelayInHoursif minTransferDelay is None or minTransferDelay >= transferDelay:if minTransferDelay != transferDelay:minTransferDelayWarehouses = {}minTransferDelayWarehouses[warehouse.id] = warehouseminTransferDelay = transferDelayreturn [__get_warehouse_with_min_transfer_price(minTransferDelayWarehouses, ignoredWhs, item_id, item_pricing, False)[0], total_availability]def __get_warehouse_with_max_availability(warehouse_ids, item_id):warehouse_retid = -1max_availability = 0total_availability = 0for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():entry.reserved = max(entry.reserved, 0)entry.held = max(entry.held, 0)if entry.warehouse_id in warehouse_ids:availability = entry.availability - entry.reservedif availability > max_availability:warehouse_retid = entry.warehouse_idmax_availability = availabilitytotal_availability += availabilityreturn [warehouse_retid, total_availability]def __get_vendor_holiday_delay(vendor_id, expectedDelay):## If vendor is closed two days continuouslyholidayDelay = 0currentDate = datetime.date.today()expectedDate = currentDate + datetime.timedelta(days = expectedDelay)holidays = VendorHolidays.query.filter(VendorHolidays.vendor_id == vendor_id).filter(VendorHolidays.date.between(currentDate, expectedDate)).all()if holidays:holidayDelay = holidayDelay + len(holidays)return holidayDelaydef get_item_pricing(item_id, vendorId):'''if vendor id is -1 then we calculate an average transfer price to be populatedat the time of order creation. This will be later updated with actual transfer priceat the time of billing.'''if(vendorId == -1):tp_total = 0nlc_total = 0try:item_pricings = []item = __get_item_from_master(item_id)if item.preferredVendor is not None:item_pricing = VendorItemPricing.query.filter_by(item_id=item_id, vendor_id=item.preferredVendor).first()if item_pricing:item_pricings.append(item_pricing)else :item_pricings = VendorItemPricing.query.filter_by(item_id=item_id).all()if item_pricings:for item_pricing in item_pricings:tp_total += item_pricing.transfer_pricenlc_total += item_pricing.nlctp_avg = tp_total / len(item_pricings)nlc_avg = nlc_total / len(item_pricings)item_pricing.transfer_price = tp_avgitem_pricing.nlc = nlc_avgelse:item_pricing = VendorItemPricing()item_pricing.transfer_price = item.sellingPriceitem_pricing.nlc = item.sellingPricevendor = Vendor()vendor.id = vendorIditem_pricing.vendor = vendoritem_pricing.item_id = item_idreturn item_pricingexcept:raise InventoryServiceException(101, "Item pricing not found ")vendor = Vendor.get_by(id=vendorId)try:item_pricing = VendorItemPricing.query.filter_by(vendor=vendor, item_id=item_id).one()return item_pricingexcept MultipleResultsFound:raise InventoryServiceException(110, "Multiple pricing information present for Vendor: " + vendor.name + " and Item: " + str(item_id))except NoResultFound:raise InventoryServiceException(111, "Missing pricing information for Vendor: " + vendor.name + " and Item: " + str(item_id))def get_all_item_pricing(item_id):item_pricing = VendorItemPricing.query.filter_by(item_id=item_id).all()return item_pricingdef get_all_vendor_item_pricing(item_id, vendor_id):query = VendorItemPricing.queryif item_id:query = query.filter_by(item_id = item_id)if item_id:query = query.filter_by(vendor_id = vendor_id)item_pricing = query.all()return item_pricingdef get_item_mappings(item_id):item_mappings = VendorItemMapping.query.filter_by(item_id=item_id).all()return item_mappingsdef add_vendor_pricing(vendorItemPricing):if not vendorItemPricing:raise InventoryServiceException(108, "Bad vendorItemPricing in request")vendorId = vendorItemPricing.vendorIditemId = vendorItemPricing.itemIdtry:vendor = Vendor.query.filter_by(id=vendorId).one()except:raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))try:item = __get_item_from_master(itemId)except:raise InventoryServiceException(101, "Item not found for itemId " + str(itemId))validate_vendor_prices(item, vendorItemPricing)try:ds_vendorItemPricing = VendorItemPricing.query.filter(and_(VendorItemPricing.vendor==vendor, VendorItemPricing.item_id==itemId)).one()except:ds_vendorItemPricing = VendorItemPricing()ds_vendorItemPricing.vendor = vendords_vendorItemPricing.item_id = itemIdsubject = ""message = ""if vendorItemPricing.mop:ds_vendorItemPricing.mop = vendorItemPricing.mopif vendorItemPricing.dealerPrice:ds_vendorItemPricing.dealerPrice = vendorItemPricing.dealerPriceif vendorItemPricing.transferPrice:if vendorItemPricing.transferPrice != ds_vendorItemPricing.transfer_price:client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()item = client.getItem(itemId)message = "Transfer price for Item {0} {1} {2} {3} \nand Vendor:{4} is changed from {5} to {6}.".format(item.brand, item.modelName, item.modelNumber, item.color, vendor.name, ds_vendorItemPricing.transfer_price, vendorItemPricing.transferPrice)subject = "Alert:Change in Transfer Price {0} {1} {2} {3} {4}".format(item.brand, item.modelName, item.modelNumber, item.color, itemId)ds_vendorItemPricing.transfer_price = vendorItemPricing.transferPriceif vendorItemPricing.nlc:if vendorItemPricing.nlc != ds_vendorItemPricing.nlc:client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()item = client.getItem(itemId)message = message + "\nNLC for Item {0} {1} {2} {3} \nand Vendor:{4} is changed from {5} to {6}.".format(item.brand, item.modelName, item.modelNumber, item.color, vendor.name, ds_vendorItemPricing.nlc, vendorItemPricing.nlc)subject = "Alert:Change in NLC {0} {1} {2} {3} {4}".format(item.brand, item.modelName, item.modelNumber, item.color, itemId)ds_vendorItemPricing.nlc = vendorItemPricing.nlcsession.commit()client = CatalogClient("catalog_service_server_host_staging", "catalog_service_server_port").get_client()client.updateNlcAtMarketplaces(itemId,vendorId,ds_vendorItemPricing.nlc)if subject:__send_mail(subject, message)returndef add_vendor_item_mapping(key, vendorItemMapping):if not vendorItemMapping:raise InventoryServiceException(108, "Bad vendorItemMapping in request")vendorId = vendorItemMapping.vendorIditemId = vendorItemMapping.itemIdtry:vendor = Vendor.query.filter_by(id=vendorId).one()except:raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))try:ds_vendorItemMapping = VendorItemMapping.query.filter(and_(VendorItemMapping.vendor==vendor, VendorItemMapping.item_id==itemId, VendorItemMapping.item_key==key)).one()except:ds_vendorItemMapping = VendorItemMapping()ds_vendorItemMapping.vendor = vendords_vendorItemMapping.item_id = itemIdds_vendorItemMapping.item_key = vendorItemMapping.itemKeysession.commit()# Marking the missed inventory as not ignored as the catalog dashboard user has updated their keyfor missedInventoryUpdate in MissedInventoryUpdate.query.filter_by(itemKey = vendorItemMapping.itemKey).all():missedInventoryUpdate.isIgnored = 0session.commit()returndef validate_vendor_prices(item, vendorPrices):if item.mrp != None and item.mrp != "" and vendorPrices.mop != "" and item.mrp < vendorPrices.mop:print "[BAD MRP and MOP:] for {0} {1} {2} {3}. MRP={4}. MOP={5}, Vendor={6}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(item.mrp), str(vendorPrices.mop), str(vendorPrices.vendorId))raise InventoryServiceException(101, "[BAD MRP and MOP:] for {0} {1} {2} {3}. MRP={4}. MOP={5}, Vendor={6}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(item.mrp), str(vendorPrices.mop), str(vendorPrices.vendorId)))if vendorPrices.mop != "" and vendorPrices.transferPrice != "" and vendorPrices.transferPrice > vendorPrices.mop:print "[BAD MOP and TP:] for {0} {1} {2} {3}. TP={4}. MOP={5}, Vendor={6}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(vendorPrices.transferPrice), str(vendorPrices.mop), str(vendorPrices.vendorId))raise InventoryServiceException(101, "[BAD MOP and TP:] for {0} {1} {2} {3}. TP={4}. MOP={5}, Vendor={6}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(vendorPrices.transferPrice), str(vendorPrices.mop), str(vendorPrices.vendorId)))returndef get_all_vendors():return Vendor.query.all()def get_pending_orders_inventory(vendor_id=1):"""Returns a list of inventory stock for items for which there are pending orders."""warehouse_ids = [warehouse.id for warehouse in Warehouse.query.filter_by(vendor_id = vendor_id)]pending_items_inventory = []if warehouse_ids:pending_items_inventory = session.query(CurrentInventorySnapshot.item_id, func.sum(CurrentInventorySnapshot.availability), func.sum(CurrentInventorySnapshot.reserved)).filter(CurrentInventorySnapshot.warehouse_id.in_(warehouse_ids)).group_by(CurrentInventorySnapshot.item_id).having(func.sum(CurrentInventorySnapshot.reserved) > 0).all()return pending_items_inventorydef get_billable_inventory_and_pending_orders():"""Returns a list of inventory Availability and Reserved Count for items which either have real inventoryor have pending orders."""warehouse_ids = [warehouse.id for warehouse in Warehouse.query.filter(Warehouse.isAvailabilityMonitored == 1).filter(or_(Warehouse.inventoryType == 'GOOD', Warehouse.warehouseType == 'OURS'))]items_inventory = []reserved_items_inventory = []available_items_inventory = []if warehouse_ids:reserved_items_inventory = session.query(CurrentInventorySnapshot.item_id, func.sum(CurrentInventorySnapshot.availability), func.sum(CurrentInventorySnapshot.reserved)).filter(CurrentInventorySnapshot.warehouse_id.in_(warehouse_ids)).group_by(CurrentInventorySnapshot.item_id).having(func.sum(CurrentInventorySnapshot.reserved) > 0).all()available_items_inventory = session.query(CurrentInventorySnapshot.item_id, func.sum(CurrentInventorySnapshot.availability), func.sum(CurrentInventorySnapshot.reserved)).filter(CurrentInventorySnapshot.warehouse_id.in_(warehouse_ids)).group_by(CurrentInventorySnapshot.item_id).having(func.sum(CurrentInventorySnapshot.availability) > 0).all()items_inventory.extend(reserved_items_inventory)items_inventory.extend(available_items_inventory)return items_inventorydef close_session():if session.is_active:print "session is active. closing it."session.close()def is_alive():try:session.query(Vendor.id).limit(1).one()return Trueexcept:return Falsedef add_vendor(vendor):if not vendor:raise InventoryServiceException(108, "Bad vendor")if get_vendor(vendor.id):#vendor is already present.raise InventoryServiceException(101, "Vendor already present")ds_vendor = Vendor()ds_vendor.id = vendor.idds_vendor.name = vendor.namesession.commit()return ds_vendor.iddef add_warehouse_vendor_mapping(warehouse_id, VendorId):return Truedef mark_missed_inventory_updates_as_processed(itemKey, warehouseId):MissedInventoryUpdate.query.filter_by(itemKey = itemKey, warehouseId = warehouseId).delete()session.commit()def get_item_keys_to_be_processed(warehouseId):return [i.itemKey for i in MissedInventoryUpdate.query.filter_by(warehouseId = warehouseId, isIgnored = 0)]def reset_availability(itemKey, vendorId, quantity, warehouseId):vendorItemMapping = VendorItemMapping.get_by(vendor_id = vendorId, item_key = itemKey)if vendorItemMapping:itemId = vendorItemMapping.item_idif skippedItems.has_key(warehouseId) and itemId in skippedItems[warehouseId]:quantity = 0currentInventorySnapshot = CurrentInventorySnapshot.get_by(item_id = itemId, warehouse_id = warehouseId)if currentInventorySnapshot:currentInventorySnapshot.availability = quantityclear_item_availability_cache(itemId)else:add_inventory(itemId, warehouseId, quantity)else:raise InventoryServiceException(101, 'VendorMapping not found for: ' + itemKey)session.commit()def reset_availability_for_warehouse(warehouseId):itemIds = []for currentInventorySnapshot in CurrentInventorySnapshot.query.filter_by(warehouse_id=warehouseId).all():currentInventorySnapshot.availability = 0itemIds.append(currentInventorySnapshot.item_id)clear_item_availability_cache(itemIds)session.commit()def get_our_warehouse_id_for_vendor(vendor_id, billing_warehouse_id):try:warehouse = Warehouse.query.filter_by(vendor_id = vendor_id, warehouseType = 'OURS', inventoryType = 'GOOD', billingWarehouseId = billing_warehouse_id).first()return warehouse.idexcept Exception as e:print e;raise InventoryServiceException(101, 'No our warehouse found for vendorId: ' + str(vendor_id))def __send_mail(subject, message):try:thread = threading.Thread(target=partial(mail, mail_user, mail_password, to_addresses, subject, message))thread.start()except Exception as ex:print exdef get_shipping_locations():shippingLocationIds = {}warehouses = Warehouse.query.all()for warehouse in warehouses:if warehouse.shippingWarehouseId:shippingLocationIds[warehouse.shippingWarehouseId] = 1shippingLocations = []for shippingLocationId in shippingLocationIds:shippingLocations.append(get_Warehouse(shippingLocationId))return shippingLocationsdef get_inventory_snapshot(warehouseId):query = CurrentInventorySnapshot.queryif warehouseId:query = query.filter_by(warehouse_id = warehouseId)itemInventoryMap = {}for row in query.all():if not itemInventoryMap.has_key(row.item_id):itemInventoryMap[row.item_id] = []itemInventoryMap[row.item_id].append(row)return itemInventoryMapdef update_vendor_string(warehouseId, vendorString):warehouse = get_Warehouse(warehouseId)warehouse.vendorString = vendorStringsession.commit()def __get_item_from_master(item_id):client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()return client.getItem(item_id)def __check_risky_item(item_id, source_id):## We should get the list of strings which will identify to the catalog serversif source_id == 1:client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()client.validateRiskyStatus(item_id)if source_id == 2:client = CatalogClient("catalog_service_server_host_hotspot", "catalog_service_server_port").get_client()client.validateRiskyStatus(item_id)def __get_item_from_source(item_id, source_id):if source_id == 1:client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()return client.getItem(item_id)if source_id == 2:client = CatalogClient("catalog_service_server_host_hotspot", "catalog_service_server_port").get_client()return client.getItem(item_id)def get_monitored_warehouses_for_vendors(vendorIds):w = []for wh in Warehouse.query.filter_by(isAvailabilityMonitored = 1).all():if wh.vendor.id in (vendorIds):w.append(to_t_warehouse(wh).id)return wdef get_ignored_warehouseids_and_itemids():iw = []for i in IgnoredInventoryUpdateItems.query.all():iw.append(to_t_itemidwarehouseid(i))return iwdef insert_item_to_ignore_inventory_update_list(item_id,warehouse_id):try:ds_warehouse=IgnoredInventoryUpdateItems()ds_warehouse.item_id=item_idds_warehouse.warehouse_id=warehouse_idclear_item_availability_cache(item_id)session.commit()return Trueexcept:return Falsedef delete_item_from_ignore_inventory_update_list(item_id,warehouse_id):try:session.query(IgnoredInventoryUpdateItems).filter_by(item_id=item_id,warehouse_id=warehouse_id).delete()clear_item_availability_cache(item_id)session.commit()return Trueexcept:return Falsedef get_all_ignored_inventoryupdate_items_count():return session.query(func.count(distinct(IgnoredInventoryUpdateItems.item_id))).scalar()def get_ignored_inventoryupdate_itemids(offset=0,limit=None):itemIds = session.query(distinct(IgnoredInventoryUpdateItems.item_id))'''if limit is not None:itemIds = itemIds.limit(limit)'''print itemIds.all()return [id for (id, ) in itemIds.all()]def update_item_stock_purchase_params(item_id, numOfDaysStock, minStockLevel):if numOfDaysStock is None or minStockLevel is None:raise InventoryServiceException(108, "Bad params : numOfDaysStock = " + str(numOfDaysStock) + "minStockLevel = " + str(minStockLevel))itemStockPurchaseParams = ItemStockPurchaseParams.query.filter_by(item_id = item_id).first()if itemStockPurchaseParams is None:itemStockPurchaseParams = ItemStockPurchaseParams()itemStockPurchaseParams.item_id = item_iditemStockPurchaseParams.numOfDaysStock = numOfDaysStockitemStockPurchaseParams.minStockLevel = minStockLevelsession.commit()def get_item_stock_purchase_params(item_id):return ItemStockPurchaseParams.query.filter_by(item_id = item_id).first()def add_oos_status_for_item(oosStatusMap, date):oosDate = to_py_date(date)oosDate.replace(second=0, microsecond=0)cartAdditionStartDate = oosDate - datetime.timedelta(days = 1)client = TransactionClient().get_client()#Gets physical orders in the last dayorders = client.getPhysicalOrders(to_java_date(cartAdditionStartDate), to_java_date(oosDate))rtoOrders = client.getAllOrders([20], 0, 0, 0)orderCountByItemIdSourceId = {}rtoOrderCountByItemIdSourceId = {}for order in orders:if not orderCountByItemIdSourceId.has_key(order.lineitems[0].item_id):orderCountByItemIdSourceId[order.lineitems[0].item_id] = {}if orderCountByItemIdSourceId[order.lineitems[0].item_id].has_key(order.source):orderCountByItemIdSourceId[order.lineitems[0].item_id][order.source] = orderCountByItemIdSourceId[order.lineitems[0].item_id][order.source] + 1else:orderCountByItemIdSourceId[order.lineitems[0].item_id][order.source] = 1for order in rtoOrders:if not rtoOrderCountByItemIdSourceId.has_key(order.lineitems[0].item_id):rtoOrderCountByItemIdSourceId[order.lineitems[0].item_id] = {}if rtoOrderCountByItemIdSourceId[order.lineitems[0].item_id].has_key(order.source):rtoOrderCountByItemIdSourceId[order.lineitems[0].item_id][order.source] = rtoOrderCountByItemIdSourceId[order.lineitems[0].item_id][order.source] + 1else:rtoOrderCountByItemIdSourceId[order.lineitems[0].item_id][order.source] = 1for itemId, status in oosStatusMap.iteritems():total_order_count = 0total_rto_count = 0for sid in (1,3,4,6,7,8):oosStatus = OOSStatus()oosStatus.item_id = itemIdoosStatus.date = oosDateoosStatus.sourceId = sidorder_count = 0rto_count = 0if orderCountByItemIdSourceId.has_key(itemId) and orderCountByItemIdSourceId[itemId].has_key(sid):order_count = orderCountByItemIdSourceId[itemId][sid]if rtoOrderCountByItemIdSourceId.has_key(itemId) and rtoOrderCountByItemIdSourceId[itemId].has_key(sid):rto_count = rtoOrderCountByItemIdSourceId[itemId][sid]oosStatus.num_orders = order_countoosStatus.rto_orders = rto_countoosStatus.is_oos = statusif oosStatus.is_oos and order_count > 0:oosStatus.is_oos = Falsetotal_order_count = total_order_count + order_counttotal_rto_count = total_rto_count + rto_countoosStatus = OOSStatus()oosStatus.item_id = itemIdoosStatus.date = oosDateoosStatus.sourceId = 0oosStatus.num_orders = total_order_countoosStatus.rto_orders = total_rto_countif itemId in orderCountByItemIdSourceId and 1 in orderCountByItemIdSourceId[itemId]:order_count = orderCountByItemIdSourceId[itemId][1]oosStatus.is_oos = statusif oosStatus.is_oos and order_count > 0:oosStatus.is_oos = Falsesession.commit()itemCountMap = {}oosDate = oosDate - datetime.timedelta(days = 1) - datetime.timedelta(hours = 1)lines = session.query(OOSStatus.item_id, func.sum(OOSStatus.num_orders)/func.count(OOSStatus.num_orders)).filter(OOSStatus.date >= oosDate).filter(OOSStatus.sourceId == 1).filter(OOSStatus.is_oos == 0).group_by(OOSStatus.item_id).all()for line in lines:item_id = line[0]quantity = int(math.ceil(max(1,2*line[1])))itemCountMap[item_id] = quantitycl = CatalogClient('catalog_service_server_host_prod','catalog_service_server_port').get_client()cl.updateItemHoldInventory(itemCountMap)def get_oos_statuses_for_x_days_for_item(itemId, sourceId, days):timestamp = datetime.datetime.now()timestamp = timestamp - datetime.timedelta(days = days)return OOSStatus.query.filter_by(item_id = itemId).filter_by(sourceId = sourceId).filter(OOSStatus.date > timestamp).all()def get_oos_statuses_for_x_days(sourceId, days):timestamp = datetime.datetime.now()timestamp = timestamp - datetime.timedelta(days = days)if sourceId == -1:return OOSStatus.query.filter(OOSStatus.date > timestamp).all()else:return OOSStatus.query.filter_by(sourceId = sourceId).filter(OOSStatus.date > timestamp).all()def get_non_zero_item_stock_purchase_params():return ItemStockPurchaseParams.query.filter(or_("numOfDaysStock!=0","minStockLevel!=0"))def get_last_n_day_sale_for_item(itemId, numberOfDays):lastNdaySale = ""oosStatuses = get_oos_statuses_for_x_days_for_item(itemId, 0, numberOfDays)for oosStatus in oosStatuses:if oosStatus.is_oos == True:lastNdaySale +="X-"else:lastNdaySale +=str(oosStatus.num_orders) + "-"return lastNdaySale[:-1]def get_warehouse_name(warehouseId):row = Warehouse.get_by(id = warehouseId)return row.displayNamedef get_amazon_inventory_for_item(amazonItemId):inventory = AmazonInventorySnapshot.get_by(item_id=amazonItemId)return inventorydef get_all_amazon_inventory():return session.query(AmazonInventorySnapshot).all()def add_or_update_amazon_inventory_for_item(amazoninventorysnapshot,time):inventory = AmazonInventorySnapshot.get_by(item_id = amazoninventorysnapshot.item_id)if inventory is None:amazon_inventory = AmazonInventorySnapshot()amazon_inventory.item_id = amazoninventorysnapshot.item_idamazon_inventory.availability = amazoninventorysnapshot.availabilityamazon_inventory.reserved = amazoninventorysnapshot.reservedamazon_inventory.is_oos = amazoninventorysnapshot.is_oosif time != 0:amazon_inventory.lastUpdatedOnAmazon = to_py_date(time)else:inventory.availability = amazoninventorysnapshot.availabilityinventory.reserved = amazoninventorysnapshot.reservedif not inventory.is_oos and (to_py_date(time) - inventory.lastUpdatedOnAmazon).days == 0:passelse:inventory.is_oos = amazoninventorysnapshot.is_oosif time != 0:inventory.lastUpdatedOnAmazon = to_py_date(time)session.commit()def add_update_hold_inventory(itemId, warehouseId, holdQuantity, source):if holdQuantity <0:print "Negative holdQuantity : " + str(holdQuantity) + " is not allowed"raise InventoryServiceException(108, "Negative heldQuantity is not allowed")hold_inventory_detail = HoldInventoryDetail.get_by(item_id = itemId, warehouse_id=warehouseId, source = source)if hold_inventory_detail is None:diffTobeAddedInCIS = holdQuantityhold_inventory_detail = HoldInventoryDetail()hold_inventory_detail.item_id = itemIdhold_inventory_detail.warehouse_id = warehouseIdhold_inventory_detail.held = holdQuantityhold_inventory_detail.source = sourceelse:diffTobeAddedInCIS = holdQuantity - hold_inventory_detail.heldhold_inventory_detail.held = holdQuantitycurrent_inventory_snapshot = CurrentInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)if not current_inventory_snapshot:current_inventory_snapshot = CurrentInventorySnapshot()current_inventory_snapshot.item_id = itemIdcurrent_inventory_snapshot.warehouse_id = warehouseIdcurrent_inventory_snapshot.availability = 0current_inventory_snapshot.reserved = 0current_inventory_snapshot.held = 0current_inventory_snapshot.held = current_inventory_snapshot.held + diffTobeAddedInCISsession.commit()#**Update item availability cache**#clear_item_availability_cache(itemId)def add_or_update_amazon_fba_inventory(amazonfbainventorysnapshot):inventory = AmazonFbaInventorySnapshot.query.filter_by(item_id = amazonfbainventorysnapshot.item_id,location=amazonfbainventorysnapshot.location).first()if inventory is None:amazon_fba_inventory = AmazonFbaInventorySnapshot()amazon_fba_inventory.item_id = amazonfbainventorysnapshot.item_idamazon_fba_inventory.availability = amazonfbainventorysnapshot.availabilityamazon_fba_inventory.location = amazonfbainventorysnapshot.locationamazon_fba_inventory.reserved = amazonfbainventorysnapshot.reservedamazon_fba_inventory.inbound = amazonfbainventorysnapshot.inboundamazon_fba_inventory.unfulfillable = amazonfbainventorysnapshot.unfulfillableelse:print 'updating'inventory.availability = amazonfbainventorysnapshot.availabilityinventory.location = amazonfbainventorysnapshot.locationinventory.reserved = amazonfbainventorysnapshot.reservedinventory.inbound = amazonfbainventorysnapshot.inboundinventory.unfulfillable = amazonfbainventorysnapshot.unfulfillabledef get_amazon_fba_inventory(itemId):return AmazonFbaInventorySnapshot.query.filter_by(item_id = itemId)def get_all_amazon_fba_inventory():return AmazonFbaInventorySnapshot.query.all()def get_oursgood_warehouseids_for_location(stateId):warehouseId=[]x= session.query(Warehouse.id).filter(Warehouse.id==Warehouse.billingWarehouseId).filter(Warehouse.warehouseType=='OURS').filter(Warehouse.state_id==stateId).all()for id in x:warehouseId.append(id[0])return session.query(Warehouse.id).filter(Warehouse.inventoryType=='GOOD').filter(Warehouse.warehouseType=='OURS').filter(Warehouse.billingWarehouseId.in_(warehouseId)).all()def get_holdinventorydetail_forItem_forWarehouseId_exceptsource(item_id,warehouse_id,source):holddetails = HoldInventoryDetail.query.filter(HoldInventoryDetail.item_id == item_id).all()print holddetailshold = 0for holddetail in holddetails:if holddetail.source !=source and holddetail.warehouse_id == warehouse_id:hold = hold + holddetail.heldreturn holddef get_snapdeal_inventory_for_item(id):print SnapdealInventorySnapshot.get_by(item_id = id)return SnapdealInventorySnapshot.get_by(item_id = id)def add_or_update_snapdeal_inventor_for_item(snapdealinventoryitem):snapdeal_inventory_item = SnapdealInventorySnapshot.get_by(item_id = snapdealinventoryitem.item_id)if snapdeal_inventory_item is None:snapdeal_inventory_item = SnapdealInventorySnapshot()snapdeal_inventory_item.item_id = snapdealinventoryitem.item_idsnapdeal_inventory_item.availability = snapdealinventoryitem.availabilitysnapdeal_inventory_item.pendingOrders = snapdealinventoryitem.pendingOrderssnapdeal_inventory_item.lastUpdatedOnSnapdeal = to_py_date(snapdealinventoryitem.lastUpdatedOnSnapdeal)snapdeal_inventory_item.is_oos = snapdealinventoryitem.is_ooselse:snapdeal_inventory_item.availability = snapdealinventoryitem.availabilitysnapdeal_inventory_item.pendingOrders = snapdealinventoryitem.pendingOrdersif not snapdeal_inventory_item.is_oos and (to_py_date(snapdealinventoryitem.lastUpdatedOnSnapdeal) - snapdeal_inventory_item.lastUpdatedOnSnapdeal).days == 0:passelse:snapdeal_inventory_item.is_oos = snapdealinventoryitem.is_oossnapdeal_inventory_item.lastUpdatedOnSnapdeal = to_py_date(snapdealinventoryitem.lastUpdatedOnSnapdeal)session.commit()def get_nlc_for_warehouse(warehouse_id,itemid):warehouse = Warehouse.get_by(id=warehouse_id)if warehouse is None:return 0vendoritempricing = VendorItemPricing.query.filter_by(item_id=itemid, vendor_id=warehouse.vendor_id).first()'''vendoritempricing = VendorItemPricing.get_by(id=warehouse.vendor_id,item_id=itemid)'''if vendoritempricing is None:return 0return vendoritempricing.nlcdef get_snapdeal_inventory_snapshot():return SnapdealInventorySnapshot.query.all()def get_held_inventory_map_for_item(itemId, warehouseId):heldInventoryMap = {}holdInventories = HoldInventoryDetail.query.filter_by(item_id= itemId, warehouse_id = warehouseId).all()for holdInventory in holdInventories:heldInventoryMap[holdInventory.source] = holdInventory.heldreturn heldInventoryMapdef get_hold_inventory_details(itemId, warehouseId, source):heldInventoryQuery = HoldInventoryDetail.queryif itemId:heldInventoryQuery = heldInventoryQuery.filter_by(item_id = itemId)if warehouseId:heldInventoryQuery = heldInventoryQuery.filter_by(warehouse_id = warehouseId)if source:heldInventoryQuery = heldInventoryQuery.filter_by(source = source)holdInventoryDetails = heldInventoryQuery.all()return holdInventoryDetailsdef add_or_update_flipkart_inventory_snapshot(flipkartInventorySnapshot,time):for snapshot in flipkartInventorySnapshot:flipkart_inventory = FlipkartInventorySnapshot.get_by(item_id = snapshot.item_id)if flipkart_inventory is None:flipkart_inventory = FlipkartInventorySnapshot()flipkart_inventory.item_id = snapshot.item_idflipkart_inventory.availability = snapshot.availabilityflipkart_inventory.createdOrders = snapshot.createdOrdersflipkart_inventory.heldOrders = snapshot.heldOrdersflipkart_inventory.is_oos = snapshot.is_oosflipkart_inventory.lastUpdatedOnFlipkart = to_py_date(time)else:flipkart_inventory.availability = snapshot.availabilityflipkart_inventory.createdOrders = snapshot.createdOrdersflipkart_inventory.heldOrders = snapshot.heldOrdersif not flipkart_inventory.is_oos and (to_py_date(time) - flipkart_inventory.lastUpdatedOnFlipkart).days == 0:passelse:flipkart_inventory.is_oos = snapshot.is_oosflipkart_inventory.lastUpdatedOnFlipkart = to_py_date(time)session.commit()def get_flipkart_inventory_snapshot():return FlipkartInventorySnapshot.query.all()def get_flipkart_inventory_for_Item(itemId):return FlipkartInventorySnapshot.get_by(item_id = itemId)def get_state_master():stateIdNameMap = {}statemaster = StateMaster.query.all()for state in statemaster:stateIdNameMap[state.id] = to_t_state(state)return stateIdNameMapdef update_snapdeal_stock_at_eod(allsnapdealstock):for stockitem in allsnapdealstock:snapdealstockateod = SnapdealStockAtEOD()snapdealstockateod.item_id = stockitem.item_idsnapdealstockateod.availability = stockitem.availabilitysnapdealstockateod.date = to_py_date(stockitem.date)session.commit()def update_flipkart_stock_at_eod(allflipkartstock):for stockitem in allflipkartstock:snapdealstockateod = FlipkartStockAtEOD()snapdealstockateod.item_id = stockitem.item_idsnapdealstockateod.availability = stockitem.availabilitysnapdealstockateod.date = to_py_date(stockitem.date)session.commit()def get_wanlc_for_source(item_id,sourceId):stockWanlc = StockWeightedNlcInfo.query.filter(StockWeightedNlcInfo.itemId==item_id).filter(StockWeightedNlcInfo.source==sourceId).order_by(desc(StockWeightedNlcInfo.updatedTimestamp)).limit(1).all()if stockWanlc is None or len(stockWanlc)==0:return 0.0else:return stockWanlc[0].avgWeightedNlcdef get_all_available_amazon_inventory():return AmazonFbaInventorySnapshot.query.filter(AmazonFbaInventorySnapshot.availability>0).all()