Subversion Repositories SmartDukaan

Rev

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