Subversion Repositories SmartDukaan

Rev

Rev 8019 | Rev 8182 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
5944 mandeep.dh 1
'''
2
Created on 23-Mar-2010
3
 
4
@author: ashish
5
'''
6
from elixir import *
7
from functools import partial
6531 vikram.rag 8
from shop2020.clients.CatalogClient import CatalogClient
5944 mandeep.dh 9
from shop2020.clients.TransactionClient import TransactionClient
10
from shop2020.model.v1.inventory.impl import DataService
6531 vikram.rag 11
from shop2020.model.v1.inventory.impl.Convertors import to_t_warehouse, \
12
    to_t_itemidwarehouseid
5944 mandeep.dh 13
from shop2020.model.v1.inventory.impl.DataService import Warehouse, \
14
    ItemInventoryHistory, CurrentInventorySnapshot, VendorItemPricing, \
15
    VendorItemMapping, Vendor, MissedInventoryUpdate, BadInventorySnapshot, \
6531 vikram.rag 16
    VendorItemProcurementDelay, VendorHolidays, ItemAvailabilityCache, \
7410 amar.kumar 17
    CurrentReservationSnapshot, IgnoredInventoryUpdateItems, ItemStockPurchaseParams, \
18
    OOSStatus, AmazonInventorySnapshot, StateMaster
6531 vikram.rag 19
from shop2020.thriftpy.model.v1.inventory.ttypes import \
20
    InventoryServiceException, HolidayType, InventoryType, WarehouseType
5944 mandeep.dh 21
from shop2020.thriftpy.model.v1.order.ttypes import AlertType
6531 vikram.rag 22
from shop2020.thriftpy.purchase.ttypes import PurchaseServiceException
5944 mandeep.dh 23
from shop2020.utils import EmailAttachmentSender
24
from shop2020.utils.EmailAttachmentSender import mail
6821 amar.kumar 25
from shop2020.utils.Utils import to_py_date, to_java_date
5944 mandeep.dh 26
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
7410 amar.kumar 27
from sqlalchemy.sql import or_
6531 vikram.rag 28
from sqlalchemy.sql.expression import and_, func, distinct
29
from sqlalchemy.sql.functions import count
5944 mandeep.dh 30
import calendar
31
import datetime
32
import sys
33
import threading
34
 
6550 rajveer 35
to_addresses = ["khushal.bhatia@shop2020.in", "chaitnaya.vats@shop2020.in", "chandan.kumar@shop2020.in"]
6029 rajveer 36
mail_user = "cnc.center@shop2020.in"
37
mail_password = "5h0p2o2o"
5944 mandeep.dh 38
skippedItems = { 175 : [27, 2160, 2175, 2163, 2158, 7128, 26, 2154],
39
                 193 : [5839] }
40
 
6821 amar.kumar 41
OOS_CALCULATION_TIME = 23
6498 vikram.rag 42
 
5944 mandeep.dh 43
def initialize(dbname='inventory', db_hostname="localhost"):
44
    DataService.initialize(dbname, db_hostname)
45
 
46
def get_Warehouse(warehouse_id):
47
    return Warehouse.get_by(id=warehouse_id)
48
 
49
def get_vendor(vendorId):
50
    return Vendor.get_by(id=vendorId)
51
 
7410 amar.kumar 52
def get_state(stateId):
53
    return StateMaster.get_by(id=stateId)
54
 
5944 mandeep.dh 55
def get_all_warehouses_by_status(status):
56
    return Warehouse.query.all()
57
 
58
def get_all_items_for_warehouse(warehouse_id):
59
    warehouse = get_Warehouse(warehouse_id)
60
    if not warehouse:
61
        raise InventoryServiceException(108, "bad warehouse")
62
    return warehouse.all_items
63
 
64
def add_warehouse(warehouse):
65
    if not warehouse:
66
        raise InventoryServiceException(108, "Bad warehouse")
67
    if get_Warehouse(warehouse.id):
68
        #warehouse is already present.
69
        raise InventoryServiceException(101, "Warehouse already present")
70
 
71
    ds_warehouse = Warehouse()
72
    ds_warehouse.location = warehouse.location
73
    ds_warehouse.status = 3
74
    ds_warehouse.addedOn = datetime.datetime.now()
75
    ds_warehouse.lastCheckedOn = datetime.datetime.now()
76
    ds_warehouse.tinNumber = warehouse.tinNumber
77
    ds_warehouse.pincode = warehouse.pincode
78
    ds_warehouse.billingType = warehouse.billingType
79
    ds_warehouse.billingWarehouseId = warehouse.billingWarehouseId
80
    ds_warehouse.displayName = warehouse.displayName
81
    ds_warehouse.inventoryType = InventoryType._VALUES_TO_NAMES[warehouse.inventoryType]
82
    ds_warehouse.isAvailabilityMonitored = warehouse.isAvailabilityMonitored
83
    ds_warehouse.logisticsLocation = warehouse.logisticsLocation
84
    ds_warehouse.shippingWarehouseId = warehouse.shippingWarehouseId
85
    ds_warehouse.transferDelayInHours = warehouse.transferDelayInHours
86
    ds_warehouse.vendor = get_vendor(warehouse.vendor.id)
7410 amar.kumar 87
    ds_warehouse.state = get_state(warehouse.stateId)
5944 mandeep.dh 88
    ds_warehouse.warehouseType = WarehouseType._VALUES_TO_NAMES[warehouse.warehouseType]    
89
    if warehouse.vendorString:
90
        ds_warehouse.vendorString = warehouse.vendorString
91
    session.commit()
92
    return ds_warehouse.id
93
 
6498 vikram.rag 94
def get_ignored_items(warehouse_id): 
6531 vikram.rag 95
    Ignored_inventory_items = IgnoredInventoryUpdateItems.query.filter_by(warehouse_id=warehouse_id).all()
6498 vikram.rag 96
    negativeItems = []
97
    for Ignored_inventory_item in Ignored_inventory_items:
98
        try:
99
            item_id = Ignored_inventory_item.item_id
100
            negativeItems.append(item_id)
101
        except:
102
            raise InventoryServiceException(108, "Some unforeseen error while updating inventory")
103
    return negativeItems
104
 
6510 rajveer 105
def get_ignored_warehouses(item_id): 
6539 amit.gupta 106
    Ignored_inventory_items = IgnoredInventoryUpdateItems.query.filter_by(item_id=item_id).all()
6510 rajveer 107
    warehouses = []
108
    for Ignored_inventory_item in Ignored_inventory_items:
109
        warehouses.append(Ignored_inventory_item.warehouse_id)
110
    return warehouses
111
 
5944 mandeep.dh 112
def update_inventory_history(warehouse_id, timestamp, availability):
113
    warehouse = get_Warehouse(warehouse_id)
114
    if not warehouse:
115
        raise InventoryServiceException(107, "Warehouse? Where?")
116
    vendor = warehouse.vendor
117
    time = datetime.datetime.now()
118
    for item_key, quantity in availability.iteritems():
119
        try:
120
            vendor_item_mapping = VendorItemMapping.query.filter_by(vendor=vendor, item_key=item_key).one();
5960 mandeep.dh 121
            item_id = vendor_item_mapping.item_id
5944 mandeep.dh 122
        except:
6531 vikram.rag 123
            continue  
5944 mandeep.dh 124
        try:
6510 rajveer 125
            item_inventory_history = ItemInventoryHistory()
126
            item_inventory_history.warehouse = warehouse
127
            item_inventory_history.item_id = item_id
128
            item_inventory_history.timestamp = time
129
            item_inventory_history.availability = quantity
5944 mandeep.dh 130
        except:
131
            raise InventoryServiceException(108, "Some unforeseen error while updating inventory")
132
    session.commit()
133
 
134
def update_inventory(warehouse_id, timestamp, availability):
135
    warehouse = get_Warehouse(warehouse_id)
136
    if not warehouse:
137
        raise InventoryServiceException(107, "Warehouse? Where?")
6510 rajveer 138
 
5944 mandeep.dh 139
    time = datetime.datetime.now()
140
    warehouse.lastCheckedOn = time
141
    warehouse.vendorString = timestamp
142
    vendor = warehouse.vendor
143
    item_ids = []
144
    for item_key, quantity in availability.iteritems():
145
        try:
146
            vendor_item_mapping = VendorItemMapping.query.filter_by(vendor=vendor, item_key=item_key).one();
147
            item_id = vendor_item_mapping.item_id
6510 rajveer 148
            item_ids.append(item_id)
5944 mandeep.dh 149
        except:
150
            print 'Skipping update for ' + item_key + ' quantity ' + str(quantity) + ' warehouse id: ' + str(warehouse_id)
151
            __send_mail_for_missing_key(item_key, quantity, warehouse_id)
152
            continue
153
        try:
154
            current_inventory_snapshot = CurrentInventorySnapshot.get_by(item_id=item_id, warehouse=warehouse)
155
            if not current_inventory_snapshot:
156
                current_inventory_snapshot = CurrentInventorySnapshot()
157
                current_inventory_snapshot.item_id = item_id
158
                current_inventory_snapshot.warehouse = warehouse
159
                current_inventory_snapshot.availability = 0
160
                current_inventory_snapshot.reserved = 0
161
            # added the difference in the current inventory    
162
            current_inventory_snapshot.availability = current_inventory_snapshot.availability + quantity
163
            item = __get_item_from_master(item_id)
164
            try:
165
                if quantity > 0 and __get_item_reserved(item_id) > 0:
166
                    cl = TransactionClient().get_client()
167
                    #FIXME hardcoding for warehouse id 
168
                    cl.addAlert(AlertType.NEW_INVENTORY_ALERT, 5, "Inventory received for item " + item.brand + " " + item.modelName + " " + item.modelNumber + " " +  item.color)
169
            except:
170
                print "Not able to raise alert for incoming inventory" 
171
            if current_inventory_snapshot.availability < 0:
172
                __send_alert_for_negative_availability(item, current_inventory_snapshot.availability, warehouse)
173
        except:
174
            print "Some unforeseen error while updating inventory:", sys.exc_info()[0]
175
            raise InventoryServiceException(108, "Some unforeseen error while updating inventory")
176
    session.commit()
177
 
178
    #**Update item availability cache**#
179
    for item_id in item_ids:
5978 rajveer 180
        clear_item_availability_cache(item_id)
5944 mandeep.dh 181
 
182
def __send_alert_for_negative_reserved(item, reserved, warehouse):
183
    itemName = " ".join([str(item.id), str(item.brand), str(item.modelName), str(item.modelNumber), str(item.color)])
6029 rajveer 184
    EmailAttachmentSender.mail(mail_user, mail_password, 'amar.kumar@shop2020.in', 'Negative reserved: ' + str(reserved) + ' for Item Id: ' + itemName + ' warehouse id: ' + str(warehouse.id), None)
5944 mandeep.dh 185
 
186
def __send_alert_for_negative_availability(item, availability, warehouse):
187
    itemName = " ".join([str(item.id), str(item.brand), str(item.modelName), str(item.modelNumber), str(item.color)])
5964 amar.kumar 188
    # 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)
5944 mandeep.dh 189
 
190
def __send_mail_for_missing_key(item_key, quantity, warehouse_id):
191
    missedInventoryUpdate = MissedInventoryUpdate.get_by(itemKey = item_key, warehouseId = warehouse_id)
192
    # One email per product key mismatch
193
    if not missedInventoryUpdate:
194
        missedInventoryUpdate = MissedInventoryUpdate()
195
        missedInventoryUpdate.itemKey = item_key
196
        missedInventoryUpdate.quantity = quantity
197
        missedInventoryUpdate.isIgnored = 1
198
        missedInventoryUpdate.timestamp = datetime.datetime.now()
199
        missedInventoryUpdate.warehouseId = warehouse_id
200
        session.commit()
6232 rajveer 201
        try:
6617 amar.kumar 202
            EmailAttachmentSender.mail(mail_user, mail_password, ['chaitnaya.vats@shop2020.in', 'chandan.kumar@shop2020.in', 'khushal.bhatia@shop2020.in'], 'Skipped inventory update for ' + item_key + ' quantity ' + str(quantity) + ' warehouse id: ' + str(warehouse_id), None)
6232 rajveer 203
        except:
204
            print "Not able to send email. No issues, we can continue with updates."
5944 mandeep.dh 205
    else:
206
        missedInventoryUpdate.quantity += quantity
207
        session.commit()
208
 
209
def add_inventory(itemId, warehouseId, quantity):
210
    current_inventory_snapshot = CurrentInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)
211
    if not current_inventory_snapshot:
212
        current_inventory_snapshot = CurrentInventorySnapshot()
213
        current_inventory_snapshot.item_id = itemId
214
        current_inventory_snapshot.warehouse_id = warehouseId
215
        current_inventory_snapshot.availability = 0
216
        current_inventory_snapshot.reserved = 0
217
    # added the difference in the current inventory    
218
    current_inventory_snapshot.availability = current_inventory_snapshot.availability + quantity
219
    session.commit()
220
    #**Update item availability cache**#
5978 rajveer 221
    clear_item_availability_cache(itemId)
5944 mandeep.dh 222
    if current_inventory_snapshot.availability < 0:
223
        item = __get_item_from_master(itemId)
5978 rajveer 224
        __send_alert_for_negative_availability(item, current_inventory_snapshot.availability, get_Warehouse(warehouseId)) 
5944 mandeep.dh 225
 
226
def add_bad_inventory(itemId, warehouseId, quantity):
227
    bad_inventory_snapshot = BadInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)
228
    if not bad_inventory_snapshot:
229
        bad_inventory_snapshot = BadInventorySnapshot()
230
        bad_inventory_snapshot.item_id = itemId
231
        bad_inventory_snapshot.warehouse_id = warehouseId
232
        bad_inventory_snapshot.availability = 0
233
    # added the difference in the current inventory    
234
    bad_inventory_snapshot.availability += quantity
235
    session.commit()
236
    if bad_inventory_snapshot.availability < 0:
237
        item = __get_item_from_master(itemId)
238
        __send_alert_for_negative_availability(item, bad_inventory_snapshot.availability, get_Warehouse(warehouseId))
239
 
240
def get_item_inventory_by_item_id(item_id):
241
    return CurrentInventorySnapshot.query.filter_by(item_id=item_id).all()
242
 
243
def retire_warehouse(warehouse_id):
244
    if not warehouse_id:
245
        raise InventoryServiceException(101, "Bad warehouse id")
246
    warehouse = get_Warehouse(warehouse_id)
247
    if not warehouse:
248
        raise InventoryServiceException(108, "warehouse id not present")
249
    warehouse.status = 0;
250
    session.commit()
251
 
252
def get_item_availability_for_warehouse(warehouse_id, item_id):
6545 rajveer 253
    ignore = IgnoredInventoryUpdateItems.query.filter_by(item_id=item_id).filter_by(warehouse_id = warehouse_id).all()
6544 rajveer 254
    if ignore:
255
        return 0
5944 mandeep.dh 256
 
257
    try:
6544 rajveer 258
        current_inventory_snapshot = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id).filter_by(item_id = item_id).one()
5944 mandeep.dh 259
        return current_inventory_snapshot.availability - current_inventory_snapshot.reserved
260
    except:
261
        return 0
262
 
6484 amar.kumar 263
def get_item_availability_for_our_warehouses(item_ids):
7699 amar.kumar 264
    our_warehouses = Warehouse.query.filter_by(warehouseType = 'OURS', inventoryType = 'GOOD').all()
265
    our_thirdparty_warehouses = Warehouse.query.filter_by(warehouseType = 'OURS_THIRDPARTY').all()
6484 amar.kumar 266
    warehouse_ids = []
7699 amar.kumar 267
    for warehouse in our_warehouses :
6484 amar.kumar 268
        warehouse_ids.append(warehouse.id)
7699 amar.kumar 269
    for warehouse in our_thirdparty_warehouses :
270
        warehouse_ids.append(warehouse.id)
271
 
6484 amar.kumar 272
    availability_map = dict()
273
 
274
    try :
275
        for item_id in item_ids :
276
            total_availability = 0
277
            for current_inventory_snapshot in CurrentInventorySnapshot.query.filter(CurrentInventorySnapshot.warehouse_id.in_(warehouse_ids)).filter_by(item_id = item_id).all():
278
                total_availability += current_inventory_snapshot.availability
279
            if total_availability >0:
280
                availability_map[item_id] = total_availability
281
    except Exception as e:
282
        print e
283
        raise PurchaseServiceException(101, 'Exception while fetching availability of items in our warehouses')
284
 
285
    return availability_map
286
 
5944 mandeep.dh 287
'''
288
This method returns quantity of a particular item across all warehouses whose ids is provided
289
if warehouse_ids is null it checks for inventory in all warehouses.
290
'''
291
def __get_item_availability(item, warehouse_ids):
292
    if warehouse_ids is None:
293
        all_inventory = CurrentInventorySnapshot.query.filter_by(item = item).all()
294
        availability = 0
295
        reserved = 0
296
        for currInv in all_inventory:
297
            availability = availability + currInv.availability
298
            reserved = reserved + currInv.reserved
299
        return availability - reserved
300
    else:
301
        total_availability = 0
302
        for current_inventory_snapshot in CurrentInventorySnapshot.query.filter(CurrentInventorySnapshot.warehouse_id.in_(warehouse_ids)).filter_by(item_id = item.id).all():
303
            total_availability += current_inventory_snapshot.availability - current_inventory_snapshot.reserved
304
        return total_availability 
305
 
306
def __get_item_reserved(item_id):
307
    all_inventory = CurrentInventorySnapshot.query.filter_by(item_id = item_id).all()
308
    reserved = 0
309
    for currInv in all_inventory:
310
        reserved = reserved + currInv.reserved
311
    return reserved
5966 rajveer 312
 
313
def __get_item_availability_at_warehouse(warehouse_id, item_id):
314
    inventory = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id).one()
315
    return inventory.availability
316
 
317
def is_order_billable(item_id, warehouse_id, source_id, order_id):
318
    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()
319
    availability = __get_item_availability_at_warehouse(warehouse_id, item_id)
320
    for reservation in reservations:
321
        availability = availability - reservation.reserved
322
        if reservation.order_id == order_id and reservation.source_id == source_id:
323
            break
324
    if availability < 0:
325
        return False
326
    return True
5944 mandeep.dh 327
 
5966 rajveer 328
def reserve_item_in_warehouse(item_id, warehouse_id, source_id, order_id, created_timestamp, promised_shipping_timestamp, quantity):    
5944 mandeep.dh 329
    if not warehouse_id:
330
        raise InventoryServiceException(101, "bad warehouse_id")
331
 
332
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)
333
    try:
334
        current_inventory_snapshot = query.one()
335
    except:
336
        current_inventory_snapshot = CurrentInventorySnapshot()
337
        current_inventory_snapshot.warehouse_id = warehouse_id
338
        current_inventory_snapshot.item_id = item_id
339
        current_inventory_snapshot.availability = 0
340
        current_inventory_snapshot.reserved = 0
341
 
342
    current_inventory_snapshot.reserved = current_inventory_snapshot.reserved + quantity
5966 rajveer 343
 
344
    reservation = CurrentReservationSnapshot()
345
    reservation.item_id = item_id
346
    reservation.warehouse_id = warehouse_id
347
    reservation.source_id = source_id
348
    reservation.order_id = order_id
5990 rajveer 349
    reservation.created_timestamp = to_py_date(created_timestamp)
350
    reservation.promised_shipping_timestamp = to_py_date(promised_shipping_timestamp)
5966 rajveer 351
    reservation.reserved = quantity
352
 
5944 mandeep.dh 353
    session.commit()
354
    #**Update item availability cache**#
5978 rajveer 355
    clear_item_availability_cache(item_id)
5944 mandeep.dh 356
    return True
357
 
7968 amar.kumar 358
def update_reservation_for_order(item_id, warehouse_id, source_id, order_id, created_timestamp, promised_shipping_timestamp, quantity):    
359
    if not warehouse_id:
360
        raise InventoryServiceException(101, "bad warehouse_id")
361
    warehouse = get_Warehouse(warehouse_id)
362
    item_pricing = get_item_pricing(item_id, warehouse.vendor.id)
363
    if not item_pricing:
364
        raise InventoryServiceException(101, "No Pricing Info found for vendor and Item")
365
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)
366
    try:
367
        new_current_inventory_snapshot = query.one()
368
    except:
369
        new_current_inventory_snapshot = CurrentInventorySnapshot()
370
        new_current_inventory_snapshot.warehouse_id = warehouse_id
371
        new_current_inventory_snapshot.item_id = item_id
372
        new_current_inventory_snapshot.availability = 0
373
        new_current_inventory_snapshot.reserved = 0
374
 
375
    new_current_inventory_snapshot.reserved = new_current_inventory_snapshot.reserved + quantity
376
 
377
    new_reservation = CurrentReservationSnapshot()
378
    new_reservation.item_id = item_id
379
    new_reservation.warehouse_id = warehouse_id
380
    new_reservation.source_id = source_id
381
    new_reservation.order_id = order_id
382
    new_reservation.created_timestamp = to_py_date(created_timestamp)
383
    new_reservation.promised_shipping_timestamp = to_py_date(promised_shipping_timestamp)
384
    new_reservation.reserved = quantity
385
 
386
    order_client = TransactionClient().get_client()
387
    order = order_client.getOrder(order_id)
388
    for lineitem in order.lineitems:
389
        query = CurrentInventorySnapshot.query.filter_by(warehouse_id = order.fulfilmentWarehouseId, item_id = lineitem.item_id)
390
        try:
391
            current_inventory_snapshot = query.one()
392
            current_inventory_snapshot.reserved = current_inventory_snapshot.reserved - quantity
393
 
394
            reservation = CurrentReservationSnapshot.query.filter_by(warehouse_id = order.fulfilmentWarehouseId, item_id = lineitem.item_id, source_id = source_id, order_id = order_id).one()
395
            if reservation.reserved == quantity:
396
                reservation.delete()
397
            else:
398
                reservation.reserved -= quantity
399
 
400
            clear_item_availability_cache(lineitem.item_id)
401
            session.commit()
402
            try:
403
                if current_inventory_snapshot.reserved < 0:
404
                    item = __get_item_from_master(lineitem.item_id)
405
                    __send_alert_for_negative_reserved(item, current_inventory_snapshot.reserved, get_Warehouse(order.fulfilmentWarehouseId))
406
            except:
407
                print "Error in send negative reserved alert:", sys.exc_info()[0]
408
                return False
409
        except:
410
            print "Error in reducing reservation for item:", sys.exc_info()[0]
411
            return False
412
    session.commit()
413
    #**Update item availability cache**#
414
    clear_item_availability_cache(item_id)
415
    return True
416
 
417
 
5966 rajveer 418
def reduce_reservation_count(item_id, warehouse_id, source_id, order_id, quantity):
5944 mandeep.dh 419
    if not warehouse_id:
420
        raise InventoryServiceException(101, "bad warehouse_id")
421
 
422
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)
423
    try:
424
        current_inventory_snapshot = query.one()
425
        current_inventory_snapshot.reserved = current_inventory_snapshot.reserved - quantity
5966 rajveer 426
 
427
        reservation = CurrentReservationSnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id, source_id = source_id, order_id = order_id).one()
428
        if reservation.reserved == quantity:
429
            reservation.delete()
430
        else:
431
            reservation.reserved -= quantity
5944 mandeep.dh 432
        session.commit()
433
        #**Update item availability cache**#
5978 rajveer 434
        clear_item_availability_cache(item_id)
5944 mandeep.dh 435
        if current_inventory_snapshot.reserved < 0:
436
            item = __get_item_from_master(item_id)
437
            __send_alert_for_negative_reserved(item, current_inventory_snapshot.reserved, get_Warehouse(warehouse_id))
438
        return True
439
    except:
440
        print "Unexpected error:", sys.exc_info()[0]
441
        return False
442
 
5978 rajveer 443
def get_item_availability_for_location(item_id, source_id):
444
    item_availability = ItemAvailabilityCache.get_by(itemId=item_id, sourceId = source_id)
5944 mandeep.dh 445
    if item_availability:
7589 rajveer 446
        return [item_availability.warehouseId, item_availability.expectedDelay, item_availability.billingWarehouseId, item_availability.sellingPrice, item_availability.totalAvailability, item_availability.weight]
5944 mandeep.dh 447
    else:
5978 rajveer 448
        __update_item_availability_cache(item_id, source_id)
449
            ##Check risky status for the source
450
        __check_risky_item(item_id, source_id)
451
        return get_item_availability_for_location(item_id, source_id)
5944 mandeep.dh 452
 
5978 rajveer 453
def clear_item_availability_cache(item_id = None):
454
    if item_id:
455
        ItemAvailabilityCache.query.filter_by(itemId = item_id).delete()
456
    else:
457
        ItemAvailabilityCache.query.delete()
5944 mandeep.dh 458
    session.commit()
459
 
5978 rajveer 460
def __update_item_availability_cache(item_id, source_id):
5944 mandeep.dh 461
    """
462
    Determines the warehouse that should be used to fulfil an order for the given item.
463
    Algorithm explained at https://sites.google.com/a/shop2020.in/virtual-w-h-and-inventory/technical-details
464
 
465
    It will be ensured that every item has either a preferred vendor specified or at least for one vendor its transfer price should be defined.
466
    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.
467
 
468
    if item available at any OUR-GOOD warehouse
469
        // OUR-GOOD warehouses have inventory risk; So, we empty them first! 
470
        // We can start with minimum transfer price criterion but down the line we can also bring in Inventory age 
471
        assign OUR-GOOD warehouse with minimum transfer price
472
    else
473
        if Preferred vendor is specified and marked Sticky
474
            // Always purchase from Preferred if its marked sticky
475
            assign preferred vendor's THIRDPARTY GOOD/VIRTUAL warehouse
476
        else 
477
            if item available in a THIRDPARTY GOOD/VIRTUAL warehouse
478
                assign THIRDPARTY GOOD/VIRTUAL warehouse where item is available with minimal transfer delay followed by minimum transfer price
479
            else 
480
                // Item not available at any warehouse, OURS or THIRDPARTY
481
                If Preferred vendor is specified
482
                    assign preferred vendor's THIRDPARTY GOOD/VIRTUAL warehouse
483
                else
484
                    assign THIRDPARTY GOOD/VIRTUAL warehouse with minimum transfer price
485
 
486
    Returns an ordered list of size 4 with following elements in the given order:
487
    1. Logistics location of the warehouse which was finally picked up to ship the order.
488
    2. Expected delay added by the category manager.
489
    3. Id of the warehouse which was finally picked up.
490
 
491
    Parameters:
492
     - itemId
493
    """
5978 rajveer 494
    item = __get_item_from_source(item_id, source_id)
5944 mandeep.dh 495
    item_pricing = {}
496
    for vendorItemPricing in VendorItemPricing.query.filter_by(item_id=item_id).all():
497
        item_pricing[vendorItemPricing.vendor_id] = vendorItemPricing
498
 
6510 rajveer 499
    ignoredWhs = get_ignored_warehouses(item_id)
500
 
5944 mandeep.dh 501
    warehouses = {}
502
    ourGoodWarehouses = {}
503
    thirdpartyWarehouses = {}
504
    preferredThirdpartyWarehouses = {}
505
    for warehouse in Warehouse.query.all():
7410 amar.kumar 506
        if (warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.BAD] or warehouse.warehouseType == WarehouseType._VALUES_TO_NAMES[WarehouseType.OURS_THIRDPARTY]):
5944 mandeep.dh 507
            continue
508
        warehouses[warehouse.id] = warehouse
509
        if warehouse.warehouseType == WarehouseType._VALUES_TO_NAMES[WarehouseType.OURS]:
510
            if warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]:
511
                ourGoodWarehouses[warehouse.id] = warehouse
512
        else:
513
            thirdpartyWarehouses[warehouse.id] = warehouse
514
            if item.preferredVendor == warehouse.vendor_id and warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]:
515
                preferredThirdpartyWarehouses[warehouse.id] = warehouse
516
 
517
    warehouse_retid = -1
518
    total_availability = 0
519
 
6540 rajveer 520
    [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_price(ourGoodWarehouses, ignoredWhs, item_id, item_pricing, False)
5944 mandeep.dh 521
    if warehouse_retid == -1:
522
        if item.preferredVendor and item.isWarehousePreferenceSticky:
6540 rajveer 523
            [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_delay(preferredThirdpartyWarehouses, ignoredWhs, item_id, item_pricing)
5944 mandeep.dh 524
            if warehouse_retid == -1:
525
                warehouse_retid = preferredThirdpartyWarehouses.keys()[0]
526
        else:
6540 rajveer 527
            [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_delay(thirdpartyWarehouses, ignoredWhs, item_id, item_pricing)
5944 mandeep.dh 528
            if warehouse_retid == -1:
529
                if item.preferredVendor:
530
                    warehouse_retid = preferredThirdpartyWarehouses.keys()[0]
531
                else:
6540 rajveer 532
                    [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_price(thirdpartyWarehouses, ignoredWhs, item_id, item_pricing, True)
5944 mandeep.dh 533
 
534
    warehouse = warehouses[warehouse_retid]
535
    billingWarehouseId = warehouse.billingWarehouseId
536
 
537
    # Fetching billing warehouse of a Good billable warehouse corresponding to the virtual one
538
    if not warehouse.billingWarehouseId:
539
        for w in Warehouse.query.filter_by(vendor_id = warehouse.vendor_id, inventoryType = InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]).all():
540
            if w.billingWarehouseId:
541
                billingWarehouseId = w.billingWarehouseId
542
                break
543
 
544
    expectedDelay = item.expectedDelay 
545
    if expectedDelay is None:
546
        print 'expectedDelay field for this item was Null. Resetting it to 0'
547
        expectedDelay = 0
548
    else:
549
        expectedDelay = int(item.expectedDelay)
550
 
551
    if total_availability <= 0:
8026 amar.kumar 552
        if item.preferredVendor in [1, 5]:
6562 rajveer 553
            expectedDelay = expectedDelay + 3
554
        else:
555
            expectedDelay = expectedDelay + 2
6643 rajveer 556
    else:
557
        if warehouse.transferDelayInHours:
558
            expectedDelay = expectedDelay + warehouse.transferDelayInHours / 24
5944 mandeep.dh 559
 
5963 mandeep.dh 560
    total_availability = 0
561
    for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():
6545 rajveer 562
        if entry.warehouse_id not in ignoredWhs:
563
            total_availability += entry.availability - entry.reserved
5963 mandeep.dh 564
 
5978 rajveer 565
    item_availability_cache = ItemAvailabilityCache.get_by(itemId=item_id, sourceId=source_id)
5944 mandeep.dh 566
    if item_availability_cache is None:
567
        item_availability_cache = ItemAvailabilityCache()
568
        item_availability_cache.itemId = item_id
5978 rajveer 569
        item_availability_cache.sourceId = source_id
5944 mandeep.dh 570
    item_availability_cache.warehouseId = int(warehouse_retid)
571
    item_availability_cache.expectedDelay = expectedDelay
572
    item_availability_cache.billingWarehouseId = billingWarehouseId
573
    item_availability_cache.sellingPrice = item.sellingPrice
574
    item_availability_cache.totalAvailability = total_availability
7589 rajveer 575
    item_availability_cache.weight = 1000*item.weight if item.weight else 300
5944 mandeep.dh 576
    session.commit()
577
 
6540 rajveer 578
def __get_warehouse_with_min_transfer_price(warehouses, ignoredWhs, item_id, item_pricing, ignoreAvailability):
5944 mandeep.dh 579
    warehouse_retid = -1
580
    minTransferPrice = None
581
    total_availability = 0
6013 amar.kumar 582
    availabilityForBillingWarehouses = {}
583
    warehousesAvailability = {}
584
    availability = 0
585
    billing_warehouse_retid = None
5944 mandeep.dh 586
 
587
    if not ignoreAvailability:
588
        for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():
7242 amar.kumar 589
            entry.reserved = max(entry.reserved, 0)
6013 amar.kumar 590
            #if entry.availability > entry.reserved:
6542 rajveer 591
            warehousesAvailability[entry.warehouse_id] = [entry.availability, entry.reserved] 
5944 mandeep.dh 592
 
6540 rajveer 593
    if len(ignoredWhs) > 0:
594
        for whid in ignoredWhs:
595
            if warehousesAvailability.has_key(whid):
6542 rajveer 596
                warehousesAvailability[whid][0] = 0
6683 rajveer 597
                warehousesAvailability[whid][1] = 0
6540 rajveer 598
 
5944 mandeep.dh 599
    for warehouse in warehouses.values():
600
        if not ignoreAvailability:
6013 amar.kumar 601
            #TODO Mistake no entry for this warehouse.id in warehouseswithAvailab
602
            if warehouse.id not in warehousesAvailability:
603
                continue
604
            entry = warehousesAvailability[warehouse.id]
605
            if warehouse.billingWarehouseId in availabilityForBillingWarehouses:
606
                if warehouse.billingWarehouseId is not None or warehouse.billingWarehouseId != 0: 
6544 rajveer 607
                    availabilityForBillingWarehouses[warehouse.billingWarehouseId] = availabilityForBillingWarehouses[warehouse.billingWarehouseId] + entry[0] - entry[1]  
5944 mandeep.dh 608
            else:
6013 amar.kumar 609
                if warehouse.billingWarehouseId is not None or warehouse.billingWarehouseId != 0: 
6542 rajveer 610
                    availabilityForBillingWarehouses[warehouse.billingWarehouseId] = entry[0] - entry[1]
611
            if entry[0] <= entry[1]:
5944 mandeep.dh 612
                continue
6542 rajveer 613
            total_availability += entry[0] - entry[1]
5944 mandeep.dh 614
 
615
        # Missing transfer price cases should not impact warehouse assignment
616
        transferPrice = None
617
        if item_pricing.has_key(warehouse.vendor_id):
6778 rajveer 618
            transferPrice = item_pricing[warehouse.vendor_id].nlc
5944 mandeep.dh 619
        if minTransferPrice is None or (transferPrice and minTransferPrice > transferPrice):
620
            warehouse_retid = warehouse.id
6013 amar.kumar 621
            billing_warehouse_retid = warehouse.billingWarehouseId
5944 mandeep.dh 622
            minTransferPrice = transferPrice
6013 amar.kumar 623
 
624
 
625
    if billing_warehouse_retid in availabilityForBillingWarehouses: 
626
        availability = availabilityForBillingWarehouses[billing_warehouse_retid]
627
    else:
628
        availability = total_availability
629
 
630
    return [warehouse_retid, availability]
5944 mandeep.dh 631
 
6540 rajveer 632
def __get_warehouse_with_min_transfer_delay(warehouses, ignoredWhs, item_id, item_pricing):
5944 mandeep.dh 633
    minTransferDelay = None
634
    minTransferDelayWarehouses = {}
635
    total_availability = 0
636
 
637
    for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():
7242 amar.kumar 638
        entry.reserved = max(entry.reserved, 0)
5944 mandeep.dh 639
        if warehouses.has_key(entry.warehouse_id):
640
            warehouse = warehouses[entry.warehouse_id]
6013 amar.kumar 641
            #if entry.availability > entry.reserved:
6683 rajveer 642
            if entry.warehouse_id not in ignoredWhs:
643
                total_availability += entry.availability - entry.reserved
6780 amar.kumar 644
            if entry.availability - entry.reserved <= 0:
645
                continue
6013 amar.kumar 646
            transferDelay = warehouse.transferDelayInHours
647
            if minTransferDelay is None or minTransferDelay >= transferDelay:
648
                if minTransferDelay != transferDelay:
649
                    minTransferDelayWarehouses = {}
650
                minTransferDelayWarehouses[warehouse.id] = warehouse
651
                minTransferDelay = transferDelay
5944 mandeep.dh 652
 
6540 rajveer 653
    return [__get_warehouse_with_min_transfer_price(minTransferDelayWarehouses, ignoredWhs, item_id, item_pricing, False)[0], total_availability]
5944 mandeep.dh 654
 
655
def __get_warehouse_with_max_availability(warehouse_ids, item_id):
656
    warehouse_retid = -1
657
    max_availability = 0
658
    total_availability = 0
659
 
660
    for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():
7242 amar.kumar 661
        entry.reserved = max(entry.reserved, 0)
5944 mandeep.dh 662
        if entry.warehouse_id in warehouse_ids:
663
            availability = entry.availability - entry.reserved
664
            if availability > max_availability:
665
                warehouse_retid = entry.warehouse_id
666
                max_availability = availability
667
            total_availability += availability
668
 
669
    return [warehouse_retid, total_availability]
670
 
5978 rajveer 671
def __get_expected_procurement_delay(item_id, preferredVendor):
5944 mandeep.dh 672
    procurementDelay = 2
673
    try:
5978 rajveer 674
        if preferredVendor:
675
            delays = VendorItemProcurementDelay.query.filter_by(vendor_id = preferredVendor, item_id = item_id).all()
5944 mandeep.dh 676
        else:
5978 rajveer 677
            delays = VendorItemProcurementDelay.query.filter_by(item_id = item_id).all()
5944 mandeep.dh 678
 
679
        procurementDelay= min([delay.procurementDelay for delay in delays])
680
    except Exception as e:
681
        print e
682
    return procurementDelay
683
 
5978 rajveer 684
def __get_vendor_holiday_delay(preferredVendor, expectedDelay):
5944 mandeep.dh 685
    holidayDelay = 0
686
    try:
5978 rajveer 687
        if preferredVendor:
688
            holidays = VendorHolidays.query.filter_by(vendor_id = preferredVendor).all()
5944 mandeep.dh 689
            currentDate = datetime.date.today()
690
            expectedDate = currentDate + datetime.timedelta(days = expectedDelay)
691
            for holiday in holidays:
692
                if holiday.holidayType == HolidayType.WEEKLY and holiday.holidayValue != calendar.SUNDAY:
693
                    if currentDate.weekday() > holiday.holidayValue:
694
                        holidayDate = currentDate + datetime.timedelta(days=holiday.holidayValue-currentDate.weekday(), weeks=1)
695
                    else:
696
                        holidayDate = currentDate + datetime.timedelta(days=holiday.holidayValue-currentDate.weekday())
697
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
698
                        holidayDelay = holidayDelay + 1
699
                elif holiday.holidayType == HolidayType.MONTHLY:
700
                    holidayDate = datetime.date(currentDate.year, currentDate.month, holiday.holidayValue)
701
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
702
                        holidayDelay = holidayDelay + 1    
703
                elif holiday.holidayType == HolidayType.SPECIFIC:
704
                    holidayValue = str(holiday.holidayValue)
705
                    holidayDate = datetime.date(int(holidayValue[:4]), int(holidayValue[4:6]), int(holidayValue[6:8]))
706
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
707
                        holidayDelay = holidayDelay + 1                
708
    except Exception as e:
709
        print e
710
    return holidayDelay 
711
 
712
def get_item_pricing(item_id, vendorId):
713
    '''
714
    if vendor id is -1 then we calculate an average transfer price to be populated
715
    at the time of order creation. This will be later updated with actual transfer price
716
    at the time of billing.
717
    '''
718
    if(vendorId == -1):
6778 rajveer 719
        tp_total = 0
720
        nlc_total = 0
5944 mandeep.dh 721
        try:
722
            item_pricings = []
723
            item = __get_item_from_master(item_id)
724
            if item.preferredVendor is not None:
725
                item_pricing = VendorItemPricing.query.filter_by(item_id=item_id, vendor_id=item.preferredVendor).first()
726
                if item_pricing:
727
                    item_pricings.append(item_pricing)                    
728
            else :
729
                item_pricings = VendorItemPricing.query.filter_by(item_id=item_id).all()
730
            if item_pricings:
731
                for item_pricing in item_pricings:
6778 rajveer 732
                    tp_total += item_pricing.transfer_price
733
                    nlc_total += item_pricing.nlc
734
                tp_avg = tp_total / len(item_pricings)
735
                nlc_avg = nlc_total / len(item_pricings)
736
                item_pricing.transfer_price = tp_avg
737
                item_pricing.nlc = nlc_avg
5944 mandeep.dh 738
            else:
739
                item_pricing = VendorItemPricing()
740
                item_pricing.transfer_price = item.sellingPrice
6778 rajveer 741
                item_pricing.nlc = item.sellingPrice
5944 mandeep.dh 742
                vendor = Vendor()
743
                vendor.id = vendorId
744
                item_pricing.vendor = vendor
745
                item_pricing.item_id = item_id
746
 
747
            return item_pricing
748
        except:
749
            raise InventoryServiceException(101, "Item pricing not found ")
750
    vendor = Vendor.get_by(id=vendorId)    
751
    try:
752
        item_pricing = VendorItemPricing.query.filter_by(vendor=vendor, item_id=item_id).one()
753
        return item_pricing
754
    except MultipleResultsFound:
755
        raise InventoryServiceException(110, "Multiple pricing information present for Vendor: " + vendor.name + " and Item: " + str(item_id))
756
    except NoResultFound:
757
        raise InventoryServiceException(111, "Missing pricing information for Vendor: " + vendor.name + " and Item: " + str(item_id))
758
 
759
def get_all_item_pricing(item_id):
760
    item_pricing = VendorItemPricing.query.filter_by(item_id=item_id).all()
761
    return item_pricing
762
 
763
def get_item_mappings(item_id):
764
    item_mappings = VendorItemMapping.query.filter_by(item_id=item_id).all()
765
    return item_mappings
766
 
767
def add_vendor_pricing(vendorItemPricing):
768
    if not vendorItemPricing:
769
        raise InventoryServiceException(108, "Bad vendorItemPricing in request")
770
    vendorId = vendorItemPricing.vendorId
771
    itemId = vendorItemPricing.itemId
772
 
773
    try:
774
        vendor = Vendor.query.filter_by(id=vendorId).one()
775
    except:
776
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
777
 
778
    try:
779
        item = __get_item_from_master(itemId)
780
    except:
781
        raise InventoryServiceException(101, "Item not found for itemId " + str(itemId))
782
 
783
    validate_vendor_prices(item, vendorItemPricing)
784
 
785
    try:
786
        ds_vendorItemPricing = VendorItemPricing.query.filter(and_(VendorItemPricing.vendor==vendor, VendorItemPricing.item_id==itemId)).one()
787
    except:
788
        ds_vendorItemPricing = VendorItemPricing()
789
        ds_vendorItemPricing.vendor = vendor
790
        ds_vendorItemPricing.item_id = itemId
791
 
792
    subject = ""
793
    message = ""
794
    if vendorItemPricing.mop:
795
        ds_vendorItemPricing.mop = vendorItemPricing.mop
796
    if vendorItemPricing.dealerPrice:
797
        ds_vendorItemPricing.dealerPrice = vendorItemPricing.dealerPrice
798
    if vendorItemPricing.transferPrice:
799
        if vendorItemPricing.transferPrice != ds_vendorItemPricing.transfer_price:
6617 amar.kumar 800
            client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
801
            item = client.getItem(itemId)
802
            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)
6651 amar.kumar 803
            subject = "Alert:Change in Transfer Price {0} {1} {2} {3} {4}".format(item.brand, item.modelName, item.modelNumber, item.color, itemId)
5944 mandeep.dh 804
        ds_vendorItemPricing.transfer_price = vendorItemPricing.transferPrice
6751 amar.kumar 805
    if vendorItemPricing.nlc:
806
        if vendorItemPricing.nlc != ds_vendorItemPricing.nlc:
807
            client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
808
            item = client.getItem(itemId)
7315 amit.gupta 809
            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)
6751 amar.kumar 810
            subject = "Alert:Change in NLC {0} {1} {2} {3} {4}".format(item.brand, item.modelName, item.modelNumber, item.color, itemId)
811
        ds_vendorItemPricing.nlc = vendorItemPricing.nlc
5944 mandeep.dh 812
 
813
    session.commit()
814
    if subject:
815
        __send_mail(subject, message)
816
    return
817
 
818
def add_vendor_item_mapping(key, vendorItemMapping):
819
    if not vendorItemMapping:
820
        raise InventoryServiceException(108, "Bad vendorItemMapping in request")
821
    vendorId = vendorItemMapping.vendorId
822
    itemId = vendorItemMapping.itemId
823
 
824
    try:
825
        vendor = Vendor.query.filter_by(id=vendorId).one()
826
    except:
827
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
828
 
829
    try:
830
        ds_vendorItemMapping = VendorItemMapping.query.filter(and_(VendorItemMapping.vendor==vendor, VendorItemMapping.item_id==itemId, VendorItemMapping.item_key==key)).one()
831
    except:
832
        ds_vendorItemMapping = VendorItemMapping()
833
        ds_vendorItemMapping.vendor = vendor
834
        ds_vendorItemMapping.item_id = itemId
835
    ds_vendorItemMapping.item_key = vendorItemMapping.itemKey
836
 
837
    session.commit()
838
 
839
    # Marking the missed inventory as not ignored as the catalog dashboard user has updated their key
840
    for missedInventoryUpdate in MissedInventoryUpdate.query.filter_by(itemKey = vendorItemMapping.itemKey).all():
841
        missedInventoryUpdate.isIgnored = 0
842
    session.commit()
843
 
844
    return
845
 
846
def validate_vendor_prices(item, vendorPrices):
847
    if item.mrp != None and item.mrp != "" and vendorPrices.mop != "" and item.mrp <  vendorPrices.mop:
848
        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))
849
        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)))
850
    if vendorPrices.mop != "" and vendorPrices.transferPrice != "" and vendorPrices.transferPrice > vendorPrices.mop:
851
        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))
852
        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)))
853
    return
854
 
855
def get_all_vendors():
856
    return Vendor.query.all()
857
 
858
def get_pending_orders_inventory(vendor_id=1):
859
    """
860
    Returns a list of inventory stock for items for which there are pending orders.
861
    """
862
 
863
    warehouse_ids = [warehouse.id for warehouse in Warehouse.query.filter_by(vendor_id = vendor_id)]
864
    pending_items_inventory = []
865
    if warehouse_ids:
866
        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()
867
    return pending_items_inventory
868
 
7149 amar.kumar 869
def get_billable_inventory_and_pending_orders():
870
    """
871
    Returns a list of inventory Availability and Reserved Count for items which either have real inventory
872
    or have pending orders.
873
    """
874
 
875
    warehouse_ids = [warehouse.id for warehouse in Warehouse.query.filter(Warehouse.isAvailabilityMonitored == 1).filter(or_(Warehouse.inventoryType == 'GOOD', Warehouse.warehouseType == 'OURS'))]
876
    items_inventory = []
877
    reserved_items_inventory = []
878
    available_items_inventory = []
879
    if warehouse_ids:
880
        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()
881
        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()
882
 
883
    items_inventory.extend(reserved_items_inventory)
884
    items_inventory.extend(available_items_inventory)
885
    return items_inventory
886
 
887
 
5944 mandeep.dh 888
def close_session():
889
    if session.is_active:
890
        print "session is active. closing it."
891
        session.close()
892
 
893
def is_alive():
894
    try:
895
        session.query(Vendor.id).limit(1).one()
896
        return True
897
    except:
898
        return False
899
 
900
def add_vendor(vendor):
901
    if not vendor:
902
        raise InventoryServiceException(108, "Bad vendor")
903
    if get_vendor(vendor.id):
904
        #vendor is already present.
905
        raise InventoryServiceException(101, "Vendor already present")
906
 
907
    ds_vendor = Vendor()
908
    ds_vendor.id = vendor.id
909
    ds_vendor.name = vendor.name
910
    session.commit()
911
    return ds_vendor.id
912
 
913
def add_warehouse_vendor_mapping(warehouse_id, VendorId):
914
    return True
915
 
916
def mark_missed_inventory_updates_as_processed(itemKey, warehouseId):
917
    MissedInventoryUpdate.query.filter_by(itemKey = itemKey, warehouseId = warehouseId).delete()
918
    session.commit()
919
 
920
def get_item_keys_to_be_processed(warehouseId):
921
    return [i.itemKey for i in MissedInventoryUpdate.query.filter_by(warehouseId = warehouseId, isIgnored = 0)]
922
 
923
def reset_availability(itemKey, vendorId, quantity, warehouseId):
924
    vendorItemMapping = VendorItemMapping.get_by(vendor_id = vendorId, item_key = itemKey)
925
    if vendorItemMapping:
926
        itemId = vendorItemMapping.item_id
927
 
928
        if skippedItems.has_key(warehouseId) and itemId in skippedItems[warehouseId]:
929
            quantity = 0
930
 
931
        currentInventorySnapshot = CurrentInventorySnapshot.get_by(item_id = itemId, warehouse_id = warehouseId)
932
        if currentInventorySnapshot:
933
            currentInventorySnapshot.availability = quantity
5978 rajveer 934
            clear_item_availability_cache(itemId) 
5944 mandeep.dh 935
        else:
936
            add_inventory(itemId, warehouseId, quantity)
937
 
938
    else:
939
        raise InventoryServiceException(101, 'VendorMapping not found for: ' + itemKey)
940
    session.commit()
941
 
942
def reset_availability_for_warehouse(warehouseId):
943
    for currentInventorySnapshot in CurrentInventorySnapshot.query.filter_by(warehouse_id=warehouseId).all():
944
        currentInventorySnapshot.availability = 0
5978 rajveer 945
        clear_item_availability_cache(currentInventorySnapshot.item_id) 
5944 mandeep.dh 946
    session.commit()
947
 
7718 amar.kumar 948
def get_our_warehouse_id_for_vendor(vendor_id, billing_warehouse_id):
6467 amar.kumar 949
    try:
7718 amar.kumar 950
        warehouse = Warehouse.query.filter_by(vendor_id = vendor_id, warehouseType = 'OURS', inventoryType = 'GOOD', billingWarehouseId = billing_warehouse_id).first()
6467 amar.kumar 951
        return warehouse.id
952
    except Exception as e:
953
        print e;
7755 amar.kumar 954
        raise InventoryServiceException(101, 'No our warehouse found for vendorId: ' + str(vendor_id))
5944 mandeep.dh 955
 
956
def __send_mail(subject, message):
957
    try:
6029 rajveer 958
        thread = threading.Thread(target=partial(mail, mail_user, mail_password, to_addresses, subject, message))
5944 mandeep.dh 959
        thread.start()
960
    except Exception as ex:
961
        print ex    
962
 
963
def get_shipping_locations():
964
    shippingLocationIds = {}
965
    warehouses = Warehouse.query.all()
966
    for warehouse in warehouses:
967
        if warehouse.shippingWarehouseId:
968
            shippingLocationIds[warehouse.shippingWarehouseId] = 1
969
 
970
    shippingLocations = []
971
    for shippingLocationId in shippingLocationIds:
972
        shippingLocations.append(get_Warehouse(shippingLocationId))
973
 
974
    return shippingLocations
975
 
976
def get_inventory_snapshot(warehouseId):
977
    query = CurrentInventorySnapshot.query
978
 
979
    if warehouseId:
980
        query = query.filter_by(warehouse_id = warehouseId)
981
 
982
    itemInventoryMap = {}
983
    for row in query.all():
984
        if not itemInventoryMap.has_key(row.item_id):
985
            itemInventoryMap[row.item_id] = []
986
 
987
        itemInventoryMap[row.item_id].append(row)
988
 
989
    return itemInventoryMap
990
 
991
def update_vendor_string(warehouseId, vendorString):
992
    warehouse = get_Warehouse(warehouseId)
993
    warehouse.vendorString = vendorString
994
    session.commit()
995
 
996
def __get_item_from_master(item_id):
997
    client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
5978 rajveer 998
    return client.getItem(item_id)
999
 
1000
def __check_risky_item(item_id, source_id):
1001
    ## We should get the list of strings which will identify to the catalog servers
1002
    if source_id == 1:
1003
        client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
1004
        client.validateRiskyStatus(item_id)
1005
    if source_id == 2:
1006
        client = CatalogClient("catalog_service_server_host_hotspot", "catalog_service_server_port").get_client()
1007
        client.validateRiskyStatus(item_id)
1008
 
1009
def __get_item_from_source(item_id, source_id):
1010
    if source_id == 1:
1011
        client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
1012
        return client.getItem(item_id)
1013
    if source_id == 2:
1014
        client = CatalogClient("catalog_service_server_host_hotspot", "catalog_service_server_port").get_client()
6531 vikram.rag 1015
        return client.getItem(item_id)
1016
 
1017
def get_monitored_warehouses_for_vendors(vendorIds):
1018
    w = []
1019
    for wh in Warehouse.query.filter_by(isAvailabilityMonitored = 1).all():
1020
        if wh.vendor.id in (vendorIds):
1021
            w.append(to_t_warehouse(wh).id)
1022
    return w
1023
def get_ignored_warehouseids_and_itemids():
1024
    iw = []
1025
    for i in IgnoredInventoryUpdateItems.query.all():
1026
        iw.append(to_t_itemidwarehouseid(i)) 
1027
    return iw
1028
def insert_item_to_ignore_inventory_update_list(item_id,warehouse_id):
1029
    try:
1030
        ds_warehouse=IgnoredInventoryUpdateItems()
1031
        ds_warehouse.item_id=item_id
1032
        ds_warehouse.warehouse_id=warehouse_id
6532 amit.gupta 1033
        clear_item_availability_cache(item_id)
6531 vikram.rag 1034
        session.commit()
1035
        return True
1036
    except:
1037
        return False       
1038
def delete_item_from_ignore_inventory_update_list(item_id,warehouse_id):
1039
    try:
1040
        session.query(IgnoredInventoryUpdateItems).filter_by(item_id=item_id,warehouse_id=warehouse_id).delete()
6532 amit.gupta 1041
        clear_item_availability_cache(item_id)
6531 vikram.rag 1042
        session.commit()
1043
        return True
1044
    except:
1045
        return False           
1046
 
1047
def get_all_ignored_inventoryupdate_items_count():
1048
    return  session.query(func.count(distinct(IgnoredInventoryUpdateItems.item_id))).scalar()
1049
 
1050
def get_ignored_inventoryupdate_itemids(offset=0,limit=None):
1051
    itemIds = session.query(distinct(IgnoredInventoryUpdateItems.item_id))
1052
    '''if limit is not None:
1053
        itemIds = itemIds.limit(limit)'''
1054
    print itemIds.all()
1055
    return [id for (id, ) in itemIds.all()]
6821 amar.kumar 1056
 
1057
def update_item_stock_purchase_params(item_id, numOfDaysStock, minStockLevel):
1058
    if numOfDaysStock is None or minStockLevel is None:
1059
        raise InventoryServiceException(108, "Bad params : numOfDaysStock = " + str(numOfDaysStock) + "minStockLevel = " + str(minStockLevel))
1060
    itemStockPurchaseParams = ItemStockPurchaseParams.query.filter_by(item_id = item_id).first()
1061
    if itemStockPurchaseParams is None:
1062
        itemStockPurchaseParams = ItemStockPurchaseParams()
1063
    itemStockPurchaseParams.item_id = item_id
1064
    itemStockPurchaseParams.numOfDaysStock = numOfDaysStock
1065
    itemStockPurchaseParams.minStockLevel = minStockLevel
1066
    session.commit()
1067
 
1068
def get_item_stock_purchase_params(item_id):
1069
    return ItemStockPurchaseParams.query.filter_by(item_id = item_id).first()
1070
 
1071
def add_oos_status_for_item(oosStatusMap, date):
1072
 
1073
    oosDate = to_py_date(date)
1074
    oosDate.replace(second=0, microsecond=0)
1075
 
1076
    cartAdditionStartDate = oosDate - datetime.timedelta(days = 1)
1077
 
1078
    client = TransactionClient().get_client()
1079
 
1080
    #Gets physical orders in the last day
1081
    orders = client.getPhysicalOrders(to_java_date(cartAdditionStartDate), to_java_date(oosDate))
8019 amar.kumar 1082
    rtoOrders = client.getAllOrders([20], 0, 0, 0)
1083
    orderCountByItemId = {}
1084
    rtoOrderCountByItemId = {}
6821 amar.kumar 1085
 
1086
    for order in orders:
1087
        if orderCountByItemId.has_key(order.lineitems[0].item_id):
1088
            orderCountByItemId[order.lineitems[0].item_id] = orderCountByItemId[order.lineitems[0].item_id] + 1 
1089
        else:
1090
            orderCountByItemId[order.lineitems[0].item_id] = 1
8019 amar.kumar 1091
 
1092
    for order in rtoOrders:
1093
        if rtoOrderCountByItemId.has_key(order.lineitems[0].item_id):
1094
            rtoOrderCountByItemId[order.lineitems[0].item_id] = rtoOrderCountByItemId[order.lineitems[0].item_id] + 1 
1095
        else:
1096
            rtoOrderCountByItemId[order.lineitems[0].item_id] = 1
6821 amar.kumar 1097
 
1098
    for itemId, status in oosStatusMap.iteritems():
1099
        if OOSStatus.query.filter_by(item_id = itemId, date = oosDate).first() is None: 
1100
            oosStatus = OOSStatus()
1101
            oosStatus.item_id = itemId
1102
            oosStatus.date = oosDate
6832 amar.kumar 1103
            oosStatus.is_oos = status
6857 amar.kumar 1104
            order_count = 0
8019 amar.kumar 1105
            rto_count = 0
6821 amar.kumar 1106
            if status == False:
1107
                if orderCountByItemId.has_key(itemId):
1108
                    order_count = orderCountByItemId[itemId]
8019 amar.kumar 1109
            if rtoOrderCountByItemId.has_key(itemId):
1110
                rto_count = rtoOrderCountByItemId[itemId]
6821 amar.kumar 1111
            oosStatus.num_orders = order_count
8019 amar.kumar 1112
            oosStatus.rto_orders = rto_count
6821 amar.kumar 1113
            session.commit()
1114
        else:
1115
            print "OOS Status already exists for ItemID:"+str(itemId)
1116
            """raise InventoryServiceException(101, "OOS Status already exists for ItemID:"+str(itemId) + " & Date:"+oosDate)"""
1117
 
6832 amar.kumar 1118
def get_oos_statuses_for_x_days_for_item(itemId, days):
1119
    timestamp = datetime.datetime.now()
1120
    timestamp = timestamp - datetime.timedelta(days = 6)
6857 amar.kumar 1121
    return OOSStatus.query.filter_by(item_id = itemId).filter(OOSStatus.date > timestamp).all()
1122
 
1123
def get_non_zero_item_stock_purchase_params():
7281 kshitij.so 1124
    return ItemStockPurchaseParams.query.filter(or_("numOfDaysStock!=0","minStockLevel!=0"))
1125
 
7972 amar.kumar 1126
def get_last_n_day_sale_for_item(itemId, numberOfDays):
1127
    lastNdaySale = ""
1128
    oosStatuses = get_oos_statuses_for_x_days_for_item(itemId, numberOfDays)
1129
    for oosStatus in oosStatuses:
1130
        if oosStatus.is_oos == True:
1131
            lastNdaySale +="X-"
1132
        else:
1133
            lastNdaySale +=str(oosStatus.num_orders) + "-"
1134
    return lastNdaySale[:-1] 
1135
 
7281 kshitij.so 1136
def get_warehouse_name(warehouseId):
1137
    row = Warehouse.get_by(id = warehouseId)
1138
    return row.displayName
1139
 
1140
def get_amazon_inventory_for_item(amazonItemId):
1141
    inventory = AmazonInventorySnapshot.get_by(item_id=amazonItemId)
1142
    return inventory
1143
 
1144
def get_all_amazon_inventory():
1145
    return session.query(AmazonInventorySnapshot).all()
1146
 
1147
def add_or_update_amazon_inventory_for_item(amazoninventorysnapshot):
1148
    inventory = AmazonInventorySnapshot.get_by(item_id = amazoninventorysnapshot.item_id)
1149
    if inventory is None:
1150
        amazon_inventory = AmazonInventorySnapshot()
1151
        amazon_inventory.item_id = amazoninventorysnapshot.item_id
1152
        amazon_inventory.availability = amazoninventorysnapshot.availability
1153
        amazon_inventory.reserved = amazoninventorysnapshot.reserved
1154
    else:
1155
        inventory.availability = amazoninventorysnapshot.availability
1156
        inventory.reserved = amazoninventorysnapshot.reserved
1157
    session.commit()
1158
 
1159
 
1160
 
1161