Subversion Repositories SmartDukaan

Rev

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

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