Subversion Repositories SmartDukaan

Rev

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