Subversion Repositories SmartDukaan

Rev

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