Subversion Repositories SmartDukaan

Rev

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