Subversion Repositories SmartDukaan

Rev

Rev 8363 | Rev 8497 | 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)
6013 amar.kumar 625
            #if entry.availability > entry.reserved:
8182 amar.kumar 626
            warehousesAvailability[entry.warehouse_id] = [entry.availability, entry.reserved, entry.held] 
5944 mandeep.dh 627
 
6540 rajveer 628
    if len(ignoredWhs) > 0:
629
        for whid in ignoredWhs:
630
            if warehousesAvailability.has_key(whid):
6542 rajveer 631
                warehousesAvailability[whid][0] = 0
6683 rajveer 632
                warehousesAvailability[whid][1] = 0
8182 amar.kumar 633
                warehousesAvailability[whid][2] = 0
6540 rajveer 634
 
5944 mandeep.dh 635
    for warehouse in warehouses.values():
636
        if not ignoreAvailability:
6013 amar.kumar 637
            #TODO Mistake no entry for this warehouse.id in warehouseswithAvailab
638
            if warehouse.id not in warehousesAvailability:
639
                continue
640
            entry = warehousesAvailability[warehouse.id]
641
            if warehouse.billingWarehouseId in availabilityForBillingWarehouses:
642
                if warehouse.billingWarehouseId is not None or warehouse.billingWarehouseId != 0: 
8182 amar.kumar 643
                    availabilityForBillingWarehouses[warehouse.billingWarehouseId] = availabilityForBillingWarehouses[warehouse.billingWarehouseId] + entry[0] - entry[1] - entry[2]  
5944 mandeep.dh 644
            else:
6013 amar.kumar 645
                if warehouse.billingWarehouseId is not None or warehouse.billingWarehouseId != 0: 
8182 amar.kumar 646
                    availabilityForBillingWarehouses[warehouse.billingWarehouseId] = entry[0] - entry[1] - entry[2]
647
            if entry[0] <= (entry[1] + entry[2]):
5944 mandeep.dh 648
                continue
8182 amar.kumar 649
            total_availability += entry[0] - entry[1] - entry[2]
5944 mandeep.dh 650
 
651
        # Missing transfer price cases should not impact warehouse assignment
652
        transferPrice = None
653
        if item_pricing.has_key(warehouse.vendor_id):
6778 rajveer 654
            transferPrice = item_pricing[warehouse.vendor_id].nlc
5944 mandeep.dh 655
        if minTransferPrice is None or (transferPrice and minTransferPrice > transferPrice):
656
            warehouse_retid = warehouse.id
6013 amar.kumar 657
            billing_warehouse_retid = warehouse.billingWarehouseId
5944 mandeep.dh 658
            minTransferPrice = transferPrice
6013 amar.kumar 659
 
660
 
661
    if billing_warehouse_retid in availabilityForBillingWarehouses: 
662
        availability = availabilityForBillingWarehouses[billing_warehouse_retid]
663
    else:
664
        availability = total_availability
665
 
666
    return [warehouse_retid, availability]
5944 mandeep.dh 667
 
6540 rajveer 668
def __get_warehouse_with_min_transfer_delay(warehouses, ignoredWhs, item_id, item_pricing):
5944 mandeep.dh 669
    minTransferDelay = None
670
    minTransferDelayWarehouses = {}
671
    total_availability = 0
672
 
673
    for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():
7242 amar.kumar 674
        entry.reserved = max(entry.reserved, 0)
5944 mandeep.dh 675
        if warehouses.has_key(entry.warehouse_id):
676
            warehouse = warehouses[entry.warehouse_id]
6013 amar.kumar 677
            #if entry.availability > entry.reserved:
6683 rajveer 678
            if entry.warehouse_id not in ignoredWhs:
8182 amar.kumar 679
                total_availability += entry.availability - entry.reserved - entry.held
680
            if entry.availability - entry.reserved - entry.held <= 0:
6780 amar.kumar 681
                continue
6013 amar.kumar 682
            transferDelay = warehouse.transferDelayInHours
683
            if minTransferDelay is None or minTransferDelay >= transferDelay:
684
                if minTransferDelay != transferDelay:
685
                    minTransferDelayWarehouses = {}
686
                minTransferDelayWarehouses[warehouse.id] = warehouse
687
                minTransferDelay = transferDelay
5944 mandeep.dh 688
 
6540 rajveer 689
    return [__get_warehouse_with_min_transfer_price(minTransferDelayWarehouses, ignoredWhs, item_id, item_pricing, False)[0], total_availability]
5944 mandeep.dh 690
 
691
def __get_warehouse_with_max_availability(warehouse_ids, item_id):
692
    warehouse_retid = -1
693
    max_availability = 0
694
    total_availability = 0
695
 
696
    for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():
7242 amar.kumar 697
        entry.reserved = max(entry.reserved, 0)
5944 mandeep.dh 698
        if entry.warehouse_id in warehouse_ids:
699
            availability = entry.availability - entry.reserved
700
            if availability > max_availability:
701
                warehouse_retid = entry.warehouse_id
702
                max_availability = availability
703
            total_availability += availability
704
 
705
    return [warehouse_retid, total_availability]
706
 
8491 rajveer 707
def __get_vendor_holiday_delay(vendor_id, expectedDelay):
708
    ## If vendor is closed two days continuously
5944 mandeep.dh 709
    holidayDelay = 0
8491 rajveer 710
    currentDate = datetime.date.today()
711
    expectedDate = currentDate + datetime.timedelta(days = expectedDelay)
712
    holidays = VendorHolidays.query.filter(VendorHolidays.vendor_id == vendor_id).filter(VendorHolidays.date.between(currentDate, expectedDate)).all()
713
    if holidays:
714
        holidayDelay = holidayDelay + len(holidays)
5944 mandeep.dh 715
    return holidayDelay 
716
 
717
def get_item_pricing(item_id, vendorId):
718
    '''
719
    if vendor id is -1 then we calculate an average transfer price to be populated
720
    at the time of order creation. This will be later updated with actual transfer price
721
    at the time of billing.
722
    '''
723
    if(vendorId == -1):
6778 rajveer 724
        tp_total = 0
725
        nlc_total = 0
5944 mandeep.dh 726
        try:
727
            item_pricings = []
728
            item = __get_item_from_master(item_id)
729
            if item.preferredVendor is not None:
730
                item_pricing = VendorItemPricing.query.filter_by(item_id=item_id, vendor_id=item.preferredVendor).first()
731
                if item_pricing:
732
                    item_pricings.append(item_pricing)                    
733
            else :
734
                item_pricings = VendorItemPricing.query.filter_by(item_id=item_id).all()
735
            if item_pricings:
736
                for item_pricing in item_pricings:
6778 rajveer 737
                    tp_total += item_pricing.transfer_price
738
                    nlc_total += item_pricing.nlc
739
                tp_avg = tp_total / len(item_pricings)
740
                nlc_avg = nlc_total / len(item_pricings)
741
                item_pricing.transfer_price = tp_avg
742
                item_pricing.nlc = nlc_avg
5944 mandeep.dh 743
            else:
744
                item_pricing = VendorItemPricing()
745
                item_pricing.transfer_price = item.sellingPrice
6778 rajveer 746
                item_pricing.nlc = item.sellingPrice
5944 mandeep.dh 747
                vendor = Vendor()
748
                vendor.id = vendorId
749
                item_pricing.vendor = vendor
750
                item_pricing.item_id = item_id
751
 
752
            return item_pricing
753
        except:
754
            raise InventoryServiceException(101, "Item pricing not found ")
755
    vendor = Vendor.get_by(id=vendorId)    
756
    try:
757
        item_pricing = VendorItemPricing.query.filter_by(vendor=vendor, item_id=item_id).one()
758
        return item_pricing
759
    except MultipleResultsFound:
760
        raise InventoryServiceException(110, "Multiple pricing information present for Vendor: " + vendor.name + " and Item: " + str(item_id))
761
    except NoResultFound:
762
        raise InventoryServiceException(111, "Missing pricing information for Vendor: " + vendor.name + " and Item: " + str(item_id))
763
 
764
def get_all_item_pricing(item_id):
765
    item_pricing = VendorItemPricing.query.filter_by(item_id=item_id).all()
766
    return item_pricing
767
 
768
def get_item_mappings(item_id):
769
    item_mappings = VendorItemMapping.query.filter_by(item_id=item_id).all()
770
    return item_mappings
771
 
772
def add_vendor_pricing(vendorItemPricing):
773
    if not vendorItemPricing:
774
        raise InventoryServiceException(108, "Bad vendorItemPricing in request")
775
    vendorId = vendorItemPricing.vendorId
776
    itemId = vendorItemPricing.itemId
777
 
778
    try:
779
        vendor = Vendor.query.filter_by(id=vendorId).one()
780
    except:
781
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
782
 
783
    try:
784
        item = __get_item_from_master(itemId)
785
    except:
786
        raise InventoryServiceException(101, "Item not found for itemId " + str(itemId))
787
 
788
    validate_vendor_prices(item, vendorItemPricing)
789
 
790
    try:
791
        ds_vendorItemPricing = VendorItemPricing.query.filter(and_(VendorItemPricing.vendor==vendor, VendorItemPricing.item_id==itemId)).one()
792
    except:
793
        ds_vendorItemPricing = VendorItemPricing()
794
        ds_vendorItemPricing.vendor = vendor
795
        ds_vendorItemPricing.item_id = itemId
796
 
797
    subject = ""
798
    message = ""
799
    if vendorItemPricing.mop:
800
        ds_vendorItemPricing.mop = vendorItemPricing.mop
801
    if vendorItemPricing.dealerPrice:
802
        ds_vendorItemPricing.dealerPrice = vendorItemPricing.dealerPrice
803
    if vendorItemPricing.transferPrice:
804
        if vendorItemPricing.transferPrice != ds_vendorItemPricing.transfer_price:
6617 amar.kumar 805
            client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
806
            item = client.getItem(itemId)
807
            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 808
            subject = "Alert:Change in Transfer Price {0} {1} {2} {3} {4}".format(item.brand, item.modelName, item.modelNumber, item.color, itemId)
5944 mandeep.dh 809
        ds_vendorItemPricing.transfer_price = vendorItemPricing.transferPrice
6751 amar.kumar 810
    if vendorItemPricing.nlc:
811
        if vendorItemPricing.nlc != ds_vendorItemPricing.nlc:
812
            client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
813
            item = client.getItem(itemId)
7315 amit.gupta 814
            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 815
            subject = "Alert:Change in NLC {0} {1} {2} {3} {4}".format(item.brand, item.modelName, item.modelNumber, item.color, itemId)
816
        ds_vendorItemPricing.nlc = vendorItemPricing.nlc
5944 mandeep.dh 817
 
818
    session.commit()
819
    if subject:
820
        __send_mail(subject, message)
821
    return
822
 
823
def add_vendor_item_mapping(key, vendorItemMapping):
824
    if not vendorItemMapping:
825
        raise InventoryServiceException(108, "Bad vendorItemMapping in request")
826
    vendorId = vendorItemMapping.vendorId
827
    itemId = vendorItemMapping.itemId
828
 
829
    try:
830
        vendor = Vendor.query.filter_by(id=vendorId).one()
831
    except:
832
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
833
 
834
    try:
835
        ds_vendorItemMapping = VendorItemMapping.query.filter(and_(VendorItemMapping.vendor==vendor, VendorItemMapping.item_id==itemId, VendorItemMapping.item_key==key)).one()
836
    except:
837
        ds_vendorItemMapping = VendorItemMapping()
838
        ds_vendorItemMapping.vendor = vendor
839
        ds_vendorItemMapping.item_id = itemId
840
    ds_vendorItemMapping.item_key = vendorItemMapping.itemKey
841
 
842
    session.commit()
843
 
844
    # Marking the missed inventory as not ignored as the catalog dashboard user has updated their key
845
    for missedInventoryUpdate in MissedInventoryUpdate.query.filter_by(itemKey = vendorItemMapping.itemKey).all():
846
        missedInventoryUpdate.isIgnored = 0
847
    session.commit()
848
 
849
    return
850
 
851
def validate_vendor_prices(item, vendorPrices):
852
    if item.mrp != None and item.mrp != "" and vendorPrices.mop != "" and item.mrp <  vendorPrices.mop:
853
        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))
854
        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)))
855
    if vendorPrices.mop != "" and vendorPrices.transferPrice != "" and vendorPrices.transferPrice > vendorPrices.mop:
856
        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))
857
        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)))
858
    return
859
 
860
def get_all_vendors():
861
    return Vendor.query.all()
862
 
863
def get_pending_orders_inventory(vendor_id=1):
864
    """
865
    Returns a list of inventory stock for items for which there are pending orders.
866
    """
867
 
868
    warehouse_ids = [warehouse.id for warehouse in Warehouse.query.filter_by(vendor_id = vendor_id)]
869
    pending_items_inventory = []
870
    if warehouse_ids:
871
        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()
872
    return pending_items_inventory
873
 
7149 amar.kumar 874
def get_billable_inventory_and_pending_orders():
875
    """
876
    Returns a list of inventory Availability and Reserved Count for items which either have real inventory
877
    or have pending orders.
878
    """
879
 
880
    warehouse_ids = [warehouse.id for warehouse in Warehouse.query.filter(Warehouse.isAvailabilityMonitored == 1).filter(or_(Warehouse.inventoryType == 'GOOD', Warehouse.warehouseType == 'OURS'))]
881
    items_inventory = []
882
    reserved_items_inventory = []
883
    available_items_inventory = []
884
    if warehouse_ids:
885
        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()
886
        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()
887
 
888
    items_inventory.extend(reserved_items_inventory)
889
    items_inventory.extend(available_items_inventory)
890
    return items_inventory
891
 
892
 
5944 mandeep.dh 893
def close_session():
894
    if session.is_active:
895
        print "session is active. closing it."
896
        session.close()
897
 
898
def is_alive():
899
    try:
900
        session.query(Vendor.id).limit(1).one()
901
        return True
902
    except:
903
        return False
904
 
905
def add_vendor(vendor):
906
    if not vendor:
907
        raise InventoryServiceException(108, "Bad vendor")
908
    if get_vendor(vendor.id):
909
        #vendor is already present.
910
        raise InventoryServiceException(101, "Vendor already present")
911
 
912
    ds_vendor = Vendor()
913
    ds_vendor.id = vendor.id
914
    ds_vendor.name = vendor.name
915
    session.commit()
916
    return ds_vendor.id
917
 
918
def add_warehouse_vendor_mapping(warehouse_id, VendorId):
919
    return True
920
 
921
def mark_missed_inventory_updates_as_processed(itemKey, warehouseId):
922
    MissedInventoryUpdate.query.filter_by(itemKey = itemKey, warehouseId = warehouseId).delete()
923
    session.commit()
924
 
925
def get_item_keys_to_be_processed(warehouseId):
926
    return [i.itemKey for i in MissedInventoryUpdate.query.filter_by(warehouseId = warehouseId, isIgnored = 0)]
927
 
928
def reset_availability(itemKey, vendorId, quantity, warehouseId):
929
    vendorItemMapping = VendorItemMapping.get_by(vendor_id = vendorId, item_key = itemKey)
930
    if vendorItemMapping:
931
        itemId = vendorItemMapping.item_id
932
 
933
        if skippedItems.has_key(warehouseId) and itemId in skippedItems[warehouseId]:
934
            quantity = 0
935
 
936
        currentInventorySnapshot = CurrentInventorySnapshot.get_by(item_id = itemId, warehouse_id = warehouseId)
937
        if currentInventorySnapshot:
938
            currentInventorySnapshot.availability = quantity
5978 rajveer 939
            clear_item_availability_cache(itemId) 
5944 mandeep.dh 940
        else:
941
            add_inventory(itemId, warehouseId, quantity)
942
 
943
    else:
944
        raise InventoryServiceException(101, 'VendorMapping not found for: ' + itemKey)
945
    session.commit()
946
 
947
def reset_availability_for_warehouse(warehouseId):
948
    for currentInventorySnapshot in CurrentInventorySnapshot.query.filter_by(warehouse_id=warehouseId).all():
949
        currentInventorySnapshot.availability = 0
5978 rajveer 950
        clear_item_availability_cache(currentInventorySnapshot.item_id) 
5944 mandeep.dh 951
    session.commit()
952
 
7718 amar.kumar 953
def get_our_warehouse_id_for_vendor(vendor_id, billing_warehouse_id):
6467 amar.kumar 954
    try:
7718 amar.kumar 955
        warehouse = Warehouse.query.filter_by(vendor_id = vendor_id, warehouseType = 'OURS', inventoryType = 'GOOD', billingWarehouseId = billing_warehouse_id).first()
6467 amar.kumar 956
        return warehouse.id
957
    except Exception as e:
958
        print e;
7755 amar.kumar 959
        raise InventoryServiceException(101, 'No our warehouse found for vendorId: ' + str(vendor_id))
5944 mandeep.dh 960
 
961
def __send_mail(subject, message):
962
    try:
6029 rajveer 963
        thread = threading.Thread(target=partial(mail, mail_user, mail_password, to_addresses, subject, message))
5944 mandeep.dh 964
        thread.start()
965
    except Exception as ex:
966
        print ex    
967
 
968
def get_shipping_locations():
969
    shippingLocationIds = {}
970
    warehouses = Warehouse.query.all()
971
    for warehouse in warehouses:
972
        if warehouse.shippingWarehouseId:
973
            shippingLocationIds[warehouse.shippingWarehouseId] = 1
974
 
975
    shippingLocations = []
976
    for shippingLocationId in shippingLocationIds:
977
        shippingLocations.append(get_Warehouse(shippingLocationId))
978
 
979
    return shippingLocations
980
 
981
def get_inventory_snapshot(warehouseId):
982
    query = CurrentInventorySnapshot.query
983
 
984
    if warehouseId:
985
        query = query.filter_by(warehouse_id = warehouseId)
986
 
987
    itemInventoryMap = {}
988
    for row in query.all():
989
        if not itemInventoryMap.has_key(row.item_id):
990
            itemInventoryMap[row.item_id] = []
991
 
992
        itemInventoryMap[row.item_id].append(row)
993
 
994
    return itemInventoryMap
995
 
996
def update_vendor_string(warehouseId, vendorString):
997
    warehouse = get_Warehouse(warehouseId)
998
    warehouse.vendorString = vendorString
999
    session.commit()
1000
 
1001
def __get_item_from_master(item_id):
1002
    client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
5978 rajveer 1003
    return client.getItem(item_id)
1004
 
1005
def __check_risky_item(item_id, source_id):
1006
    ## We should get the list of strings which will identify to the catalog servers
1007
    if source_id == 1:
1008
        client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
1009
        client.validateRiskyStatus(item_id)
1010
    if source_id == 2:
1011
        client = CatalogClient("catalog_service_server_host_hotspot", "catalog_service_server_port").get_client()
1012
        client.validateRiskyStatus(item_id)
1013
 
1014
def __get_item_from_source(item_id, source_id):
1015
    if source_id == 1:
1016
        client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
1017
        return client.getItem(item_id)
1018
    if source_id == 2:
1019
        client = CatalogClient("catalog_service_server_host_hotspot", "catalog_service_server_port").get_client()
6531 vikram.rag 1020
        return client.getItem(item_id)
1021
 
1022
def get_monitored_warehouses_for_vendors(vendorIds):
1023
    w = []
1024
    for wh in Warehouse.query.filter_by(isAvailabilityMonitored = 1).all():
1025
        if wh.vendor.id in (vendorIds):
1026
            w.append(to_t_warehouse(wh).id)
1027
    return w
1028
def get_ignored_warehouseids_and_itemids():
1029
    iw = []
1030
    for i in IgnoredInventoryUpdateItems.query.all():
1031
        iw.append(to_t_itemidwarehouseid(i)) 
1032
    return iw
1033
def insert_item_to_ignore_inventory_update_list(item_id,warehouse_id):
1034
    try:
1035
        ds_warehouse=IgnoredInventoryUpdateItems()
1036
        ds_warehouse.item_id=item_id
1037
        ds_warehouse.warehouse_id=warehouse_id
6532 amit.gupta 1038
        clear_item_availability_cache(item_id)
6531 vikram.rag 1039
        session.commit()
1040
        return True
1041
    except:
1042
        return False       
1043
def delete_item_from_ignore_inventory_update_list(item_id,warehouse_id):
1044
    try:
1045
        session.query(IgnoredInventoryUpdateItems).filter_by(item_id=item_id,warehouse_id=warehouse_id).delete()
6532 amit.gupta 1046
        clear_item_availability_cache(item_id)
6531 vikram.rag 1047
        session.commit()
1048
        return True
1049
    except:
1050
        return False           
1051
 
1052
def get_all_ignored_inventoryupdate_items_count():
1053
    return  session.query(func.count(distinct(IgnoredInventoryUpdateItems.item_id))).scalar()
1054
 
1055
def get_ignored_inventoryupdate_itemids(offset=0,limit=None):
1056
    itemIds = session.query(distinct(IgnoredInventoryUpdateItems.item_id))
1057
    '''if limit is not None:
1058
        itemIds = itemIds.limit(limit)'''
1059
    print itemIds.all()
1060
    return [id for (id, ) in itemIds.all()]
6821 amar.kumar 1061
 
1062
def update_item_stock_purchase_params(item_id, numOfDaysStock, minStockLevel):
1063
    if numOfDaysStock is None or minStockLevel is None:
1064
        raise InventoryServiceException(108, "Bad params : numOfDaysStock = " + str(numOfDaysStock) + "minStockLevel = " + str(minStockLevel))
1065
    itemStockPurchaseParams = ItemStockPurchaseParams.query.filter_by(item_id = item_id).first()
1066
    if itemStockPurchaseParams is None:
1067
        itemStockPurchaseParams = ItemStockPurchaseParams()
1068
    itemStockPurchaseParams.item_id = item_id
1069
    itemStockPurchaseParams.numOfDaysStock = numOfDaysStock
1070
    itemStockPurchaseParams.minStockLevel = minStockLevel
1071
    session.commit()
1072
 
1073
def get_item_stock_purchase_params(item_id):
1074
    return ItemStockPurchaseParams.query.filter_by(item_id = item_id).first()
1075
 
1076
def add_oos_status_for_item(oosStatusMap, date):
1077
 
1078
    oosDate = to_py_date(date)
1079
    oosDate.replace(second=0, microsecond=0)
1080
 
1081
    cartAdditionStartDate = oosDate - datetime.timedelta(days = 1)
1082
 
1083
    client = TransactionClient().get_client()
1084
 
1085
    #Gets physical orders in the last day
1086
    orders = client.getPhysicalOrders(to_java_date(cartAdditionStartDate), to_java_date(oosDate))
8019 amar.kumar 1087
    rtoOrders = client.getAllOrders([20], 0, 0, 0)
1088
    orderCountByItemId = {}
1089
    rtoOrderCountByItemId = {}
6821 amar.kumar 1090
 
1091
    for order in orders:
1092
        if orderCountByItemId.has_key(order.lineitems[0].item_id):
1093
            orderCountByItemId[order.lineitems[0].item_id] = orderCountByItemId[order.lineitems[0].item_id] + 1 
1094
        else:
1095
            orderCountByItemId[order.lineitems[0].item_id] = 1
8019 amar.kumar 1096
 
1097
    for order in rtoOrders:
1098
        if rtoOrderCountByItemId.has_key(order.lineitems[0].item_id):
1099
            rtoOrderCountByItemId[order.lineitems[0].item_id] = rtoOrderCountByItemId[order.lineitems[0].item_id] + 1 
1100
        else:
1101
            rtoOrderCountByItemId[order.lineitems[0].item_id] = 1
6821 amar.kumar 1102
 
1103
    for itemId, status in oosStatusMap.iteritems():
1104
        if OOSStatus.query.filter_by(item_id = itemId, date = oosDate).first() is None: 
1105
            oosStatus = OOSStatus()
1106
            oosStatus.item_id = itemId
1107
            oosStatus.date = oosDate
6832 amar.kumar 1108
            oosStatus.is_oos = status
6857 amar.kumar 1109
            order_count = 0
8019 amar.kumar 1110
            rto_count = 0
6821 amar.kumar 1111
            if status == False:
1112
                if orderCountByItemId.has_key(itemId):
1113
                    order_count = orderCountByItemId[itemId]
8019 amar.kumar 1114
            if rtoOrderCountByItemId.has_key(itemId):
1115
                rto_count = rtoOrderCountByItemId[itemId]
6821 amar.kumar 1116
            oosStatus.num_orders = order_count
8019 amar.kumar 1117
            oosStatus.rto_orders = rto_count
6821 amar.kumar 1118
            session.commit()
1119
        else:
1120
            print "OOS Status already exists for ItemID:"+str(itemId)
1121
            """raise InventoryServiceException(101, "OOS Status already exists for ItemID:"+str(itemId) + " & Date:"+oosDate)"""
1122
 
6832 amar.kumar 1123
def get_oos_statuses_for_x_days_for_item(itemId, days):
1124
    timestamp = datetime.datetime.now()
1125
    timestamp = timestamp - datetime.timedelta(days = 6)
6857 amar.kumar 1126
    return OOSStatus.query.filter_by(item_id = itemId).filter(OOSStatus.date > timestamp).all()
1127
 
1128
def get_non_zero_item_stock_purchase_params():
7281 kshitij.so 1129
    return ItemStockPurchaseParams.query.filter(or_("numOfDaysStock!=0","minStockLevel!=0"))
1130
 
7972 amar.kumar 1131
def get_last_n_day_sale_for_item(itemId, numberOfDays):
1132
    lastNdaySale = ""
1133
    oosStatuses = get_oos_statuses_for_x_days_for_item(itemId, numberOfDays)
1134
    for oosStatus in oosStatuses:
1135
        if oosStatus.is_oos == True:
1136
            lastNdaySale +="X-"
1137
        else:
1138
            lastNdaySale +=str(oosStatus.num_orders) + "-"
1139
    return lastNdaySale[:-1] 
1140
 
7281 kshitij.so 1141
def get_warehouse_name(warehouseId):
1142
    row = Warehouse.get_by(id = warehouseId)
1143
    return row.displayName
1144
 
1145
def get_amazon_inventory_for_item(amazonItemId):
1146
    inventory = AmazonInventorySnapshot.get_by(item_id=amazonItemId)
1147
    return inventory
1148
 
1149
def get_all_amazon_inventory():
1150
    return session.query(AmazonInventorySnapshot).all()
1151
 
1152
def add_or_update_amazon_inventory_for_item(amazoninventorysnapshot):
1153
    inventory = AmazonInventorySnapshot.get_by(item_id = amazoninventorysnapshot.item_id)
1154
    if inventory is None:
1155
        amazon_inventory = AmazonInventorySnapshot()
1156
        amazon_inventory.item_id = amazoninventorysnapshot.item_id
1157
        amazon_inventory.availability = amazoninventorysnapshot.availability
1158
        amazon_inventory.reserved = amazoninventorysnapshot.reserved
1159
    else:
1160
        inventory.availability = amazoninventorysnapshot.availability
1161
        inventory.reserved = amazoninventorysnapshot.reserved
1162
    session.commit()
1163
 
8182 amar.kumar 1164
def add_update_hold_inventory(itemId, warehouseId, holdQuantity, source):
8197 amar.kumar 1165
    hold_inventory_detail = HoldInventoryDetail.get_by(item_id = itemId, warehouse_id=warehouseId, source = source)
1166
    if  hold_inventory_detail is None:
8182 amar.kumar 1167
        diffTobeAddedInCIS = holdQuantity
1168
        hold_inventory_detail = HoldInventoryDetail()
1169
        hold_inventory_detail.item_id = itemId 
1170
        hold_inventory_detail.warehouse_id = warehouseId 
1171
        hold_inventory_detail.held = holdQuantity 
1172
        hold_inventory_detail.source = source
1173
    else:
1174
        hold_inventory_detail.held = holdQuantity
8197 amar.kumar 1175
        diffTobeAddedInCIS = holdQuantity - hold_inventory_detail.held
8182 amar.kumar 1176
 
1177
    current_inventory_snapshot = CurrentInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)
1178
    if not current_inventory_snapshot:
1179
        current_inventory_snapshot = CurrentInventorySnapshot()
1180
        current_inventory_snapshot.item_id = itemId
1181
        current_inventory_snapshot.warehouse_id = warehouseId
1182
        current_inventory_snapshot.availability = 0
1183
        current_inventory_snapshot.reserved = 0
1184
        current_inventory_snapshot.held = 0
1185
    current_inventory_snapshot.held = current_inventory_snapshot.held + diffTobeAddedInCIS
1186
    session.commit()
1187
    #**Update item availability cache**#
1188
    clear_item_availability_cache(itemId)
8282 kshitij.so 1189
 
1190
def add_or_update_amazon_fba_inventory(amazonfbainventorysnapshot):
1191
    inventory = AmazonFbaInventorySnapshot.get_by(item_id = amazonfbainventorysnapshot.item_id)
1192
    if inventory is None:
1193
        amazon_fba_inventory = AmazonFbaInventorySnapshot()
1194
        amazon_fba_inventory.item_id = amazonfbainventorysnapshot.item_id
1195
        amazon_fba_inventory.availability = amazonfbainventorysnapshot.availability
1196
    else:
1197
        inventory.availability = amazonfbainventorysnapshot.availability
1198
    session.commit()
1199
 
1200
 
1201
def get_amazon_fba_inventory(itemId):
1202
    row = AmazonFbaInventorySnapshot.get_by(item_id=itemId)
1203
    return row.availability
1204
 
8363 vikram.rag 1205
def get_all_amazon_fba_inventory():
1206
    return AmazonFbaInventorySnapshot.query.all() 
1207
 
1208
def get_oursgood_warehouseids_for_location(state_id):
1209
    warehouseId=[]
1210
    x= session.query(Warehouse.id).filter(Warehouse.id==Warehouse.billingWarehouseId).filter(Warehouse.warehouseType=='OURS').filter(Warehouse.state_id==1).all()
1211
    for id in x:
1212
        warehouseId.append(id[0])
1213
    return session.query(Warehouse.id).filter(Warehouse.inventoryType=='GOOD').filter(Warehouse.warehouseType=='OURS').filter(Warehouse.billingWarehouseId.in_(warehouseId)).all()