Subversion Repositories SmartDukaan

Rev

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

Rev Author Line No. Line
94 ashish 1
'''
2
Created on 23-Mar-2010
3
 
4
@author: ashish
5
'''
6
from elixir import *
4748 mandeep.dh 7
from pydoc import Helper
8
from shop2020.clients.HelperClient import HelperClient
9
from shop2020.clients.TransactionClient import TransactionClient
10
from shop2020.config.client.ConfigClient import ConfigClient
94 ashish 11
from shop2020.model.v1.catalog.impl import DataService
4748 mandeep.dh 12
from shop2020.model.v1.catalog.impl.CategoryManager import CategoryManager
13
from shop2020.model.v1.catalog.impl.Convertors import to_t_item, \
3557 rajveer 14
    to_t_vendor_item_pricing, to_t_source
4748 mandeep.dh 15
from shop2020.model.v1.catalog.impl.DataService import Item, Warehouse, \
16
    ItemInventoryHistory, CurrentInventorySnapshot, ItemInfo, ItemChangeLog, \
17
    Category, EntityIDGenerator, VendorItemPricing, VendorItemMapping, Vendor, \
18
    SimilarItems, ProductNotification, Source, SourceItemPricing, AuthorizationLog, \
5110 mandeep.dh 19
    MissedInventoryUpdate, BadInventorySnapshot, VendorItemProcurementDelay, \
20
    VendorHolidays
4748 mandeep.dh 21
from shop2020.thriftpy.model.v1.catalog.ttypes import InventoryServiceException, \
5110 mandeep.dh 22
    status, ItemShippingInfo, HolidayType, InventoryType, WarehouseType
4748 mandeep.dh 23
from shop2020.thriftpy.model.v1.order.ttypes import AlertType
4873 mandeep.dh 24
from shop2020.utils import EmailAttachmentSender
4748 mandeep.dh 25
from shop2020.utils.EmailAttachmentSender import mail
2286 ankur.sing 26
from shop2020.utils.Utils import log_entry, to_py_date, log_risky_flag
621 chandransh 27
from sqlalchemy import desc, asc
3244 chandransh 28
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
3872 chandransh 29
from sqlalchemy.sql.expression import and_, or_, distinct, func
3086 rajveer 30
from string import Template
4748 mandeep.dh 31
from urllib2 import HTTPBasicAuthHandler
5110 mandeep.dh 32
import calendar
4748 mandeep.dh 33
import datetime
34
import sys
3924 rajveer 35
import urllib2
5047 amit.gupta 36
from functools import partial
94 ashish 37
 
5047 amit.gupta 38
import threading
39
 
40
to_addresses = ["cnc.center@shop2020.in", "ashutosh.saxena@shop2020.in"]
41
from_user = "cnc.center@shop2020.in"
42
from_pwd = "5h0p2o2o"
43
 
94 ashish 44
def initialize():
45
    DataService.initialize()
46
 
3849 chandransh 47
def get_all_items_by_status(status, offset=0, limit=None):
48
    query = Item.query
4539 rajveer 49
    if status is not None:
3849 chandransh 50
        query = query.filter_by(status=status)
51
    query = query.order_by(Item.product_group, Item.brand, Item.model_number, Item.model_name).offset(offset)
52
    if limit:
53
        query = query.limit(limit)
54
    items = query.all()
55
    return items
56
 
57
def get_all_items(is_active, offset=0, limit=None):
103 ashish 58
    if is_active:
3849 chandransh 59
        items = get_all_items_by_status(status.ACTIVE, offset, limit)
103 ashish 60
    else:
3849 chandransh 61
        items = get_all_items_by_status(None, offset, limit)
766 rajveer 62
    return items
63
 
3849 chandransh 64
def get_item_count_by_status(use_status, status):
65
    if use_status:
66
        return Item.query.filter_by(status=status).count()
103 ashish 67
    else:
3849 chandransh 68
        return Item.query.count()
103 ashish 69
 
635 rajveer 70
def get_item(item_id):
766 rajveer 71
    item = Item.get_by(id=item_id)
72
    return item
94 ashish 73
 
635 rajveer 74
def get_items_by_catalog_id(catalog_id):
447 rajveer 75
    query = Item.query.filter_by(catalog_item_id=catalog_id)
437 rajveer 76
    try:
635 rajveer 77
        items = query.all()
4934 amit.gupta 78
        return items
1399 rajveer 79
    except Exception as ex:
80
        print ex
437 rajveer 81
        raise InventoryServiceException(109, "Item not found")
82
 
576 chandransh 83
def is_active(item_id):
2983 chandransh 84
    t_item_shipping_info = ItemShippingInfo()
576 chandransh 85
    try:
635 rajveer 86
        item = get_item(item_id)
3281 chandransh 87
        t_item_shipping_info.isRisky = item.risky
4708 anupam.sin 88
        warehouse_ids = None
89
        if item.isWarehousePreferenceSticky :
90
            warehouse_ids = [item.preferredWarehouse]
91
        availability = __get_item_availability(item, warehouse_ids)
3281 chandransh 92
        if item.risky and availability <= 0 and item.status == status.ACTIVE:
93
            add_status_change_log(item, status.PAUSED_BY_RISK)
94
            item.status = status.PAUSED_BY_RISK
95
            item.status_description = "This item is currently out of stock"
96
            session.commit()
4797 rajveer 97
            __send_mail_for_oos_item(item)
3924 rajveer 98
            #This will clear cache from tomcat
99
            __clear_homepage_cache()
2983 chandransh 100
        t_item_shipping_info.isActive = (item.status == status.ACTIVE)
3281 chandransh 101
        t_item_shipping_info.quantity = availability
576 chandransh 102
    except InventoryServiceException:
2983 chandransh 103
        print "[ERROR] Unexpected error:", sys.exc_info()[0]
104
    return t_item_shipping_info
105
 
2035 rajveer 106
def get_item_status_description(itemId):
107
    item = get_item(itemId)
108
    return item.status_description
563 chandransh 109
 
94 ashish 110
def get_Warehouse(warehouse_id):
111
    return Warehouse.get_by(id=warehouse_id)
112
 
4332 anupam.sin 113
def get_Vendor(vendorId):
114
    return Vendor.get_by(id=vendorId)
115
 
122 ashish 116
def get_all_warehouses_by_status(status):
5114 mandeep.dh 117
    return Warehouse.query.all()
122 ashish 118
 
94 ashish 119
def get_all_warehouses_for_item(item_id):
635 rajveer 120
    item = get_item(item_id)
94 ashish 121
    if not item:
122 ashish 122
        raise InventoryServiceException(108, "Some unforeseen error while obtaining item")
123
    return item.get_all_warehouses
94 ashish 124
 
122 ashish 125
def get_all_items_for_warehouse(warehouse_id):
126
    warehouse = get_Warehouse(warehouse_id)
127
    if not warehouse:
128
        raise InventoryServiceException(108, "bad warehouse")
129
    return warehouse.all_items
130
 
94 ashish 131
def add_warehouse(warehouse):
103 ashish 132
    if not warehouse:
122 ashish 133
        raise InventoryServiceException(108, "Bad warehouse")
103 ashish 134
    if get_Warehouse(warehouse.id):
135
        #warehouse is already present.
122 ashish 136
        raise InventoryServiceException(101, "Warehouse already present")
103 ashish 137
 
94 ashish 138
    ds_warehouse = Warehouse()
139
    ds_warehouse.id = warehouse.id
140
    ds_warehouse.location = warehouse.location
122 ashish 141
    ds_warehouse.status = status.ACTIVE
103 ashish 142
    ds_warehouse.addedOn = datetime.datetime.now()
483 rajveer 143
    ds_warehouse.lastCheckedOn = datetime.datetime.now()
144
    ds_warehouse.tinNumber = warehouse.tinNumber
145
    ds_warehouse.pincode = warehouse.pincode
146
    if warehouse.vendorString:
147
        ds_warehouse.vendorString = warehouse.vendorString
94 ashish 148
    session.commit()
103 ashish 149
    return ds_warehouse.id
94 ashish 150
 
122 ashish 151
def update_item(item):
152
    if not item:
153
        raise InventoryServiceException(108, "Bad item in request")
154
 
155
    if not item.id:
609 chandransh 156
        raise InventoryServiceException(101, "Missing id for update")
122 ashish 157
 
2120 ankur.sing 158
    validate_item_prices(item)
2065 ankur.sing 159
 
635 rajveer 160
    ds_item = get_item(item.id)
5047 amit.gupta 161
    message = ""
122 ashish 162
    if not ds_item:
609 chandransh 163
        raise InventoryServiceException(101, "Item missing in our database")
122 ashish 164
 
963 chandransh 165
    if item.productGroup:
166
        ds_item.product_group = item.productGroup 
167
    if item.brand:
168
        ds_item.brand = item.brand
511 rajveer 169
    if item.modelNumber:
170
        ds_item.model_number = item.modelNumber
2497 ankur.sing 171
    ds_item.color = item.color
172
    ds_item.model_name = item.modelName
173
    ds_item.category = item.category
174
    ds_item.comments = item.comments
511 rajveer 175
 
2497 ankur.sing 176
    ds_item.catalog_item_id = item.catalogItemId
483 rajveer 177
 
2129 ankur.sing 178
    ds_item.mrp = item.mrp
5047 amit.gupta 179
    if ds_item.sellingPrice or item.sellingPrice:
180
        if ds_item.sellingPrice != item.sellingPrice:
181
            message += "Selling Price is changed from {0} to {1}.\n".format(ds_item.sellingPrice, item.sellingPrice)
182
 
2129 ankur.sing 183
    ds_item.sellingPrice = item.sellingPrice
2497 ankur.sing 184
 
2174 ankur.sing 185
    ds_item.weight = item.weight
2129 ankur.sing 186
 
2358 ankur.sing 187
    if ds_item.status != item.itemStatus:
2402 rajveer 188
        add_status_change_log(ds_item, item.itemStatus)
5047 amit.gupta 189
        if item.itemStatus == status.PHASED_OUT:
190
            message += "Item is phased out."
2358 ankur.sing 191
        ds_item.status = item.itemStatus
2035 rajveer 192
    if item.status_description:
193
        ds_item.status_description = item.status_description
122 ashish 194
 
511 rajveer 195
    if item.startDate:
2116 ankur.sing 196
        ds_item.startDate = to_py_date(item.startDate)
2497 ankur.sing 197
    else:
198
        ds_item.startDate = None
511 rajveer 199
    if item.retireDate:
2116 ankur.sing 200
        ds_item.retireDate = to_py_date(item.retireDate)
2497 ankur.sing 201
    else:
202
        ds_item.retireDate = None
511 rajveer 203
 
2497 ankur.sing 204
    ds_item.feature_id = item.featureId
205
    ds_item.feature_description = item.featureDescription
122 ashish 206
 
5047 amit.gupta 207
    if ds_item.bestDealText or item.bestDealText:
208
        if item.bestDealText != ds_item.bestDealText:
5080 amit.gupta 209
            message += "Promotion text is changed from '{0}' to '{1}'.\n".format(ds_item.bestDealText, item.bestDealText)
2129 ankur.sing 210
    ds_item.bestDealText = item.bestDealText
211
    ds_item.bestDealValue = item.bestDealValue
2065 ankur.sing 212
    ds_item.bestSellingRank = item.bestSellingRank
2497 ankur.sing 213
 
2065 ankur.sing 214
    ds_item.defaultForEntity = item.defaultForEntity
5047 amit.gupta 215
 
216
    if ds_item.risky or item.risky:
217
        if ds_item.risky != item.risky:
5080 amit.gupta 218
            message += "Risky flag is changed to '{0}'.\n".format(set)
5047 amit.gupta 219
 
2251 ankur.sing 220
    ds_item.risky = item.risky
3359 chandransh 221
 
3459 chandransh 222
    if item.expectedDelay is not None:
3359 chandransh 223
        ds_item.expectedDelay = item.expectedDelay
224
 
225
    if item.preferredWarehouse:
5080 amit.gupta 226
        if item.preferredWarehouse != ds_item.preferredWarehouse:
227
            newPreferredWareHouseName = get_Warehouse(item.preferredWarehouse).displayName
228
            oldPreferredWareHouseName = 'None'
229
            if ds_item.preferredWarehouse:
230
                oldPreferredWareHouseName = get_Warehouse(ds_item.preferredWarehouse).displayName
231
            message += "Preferred warehouse is changed from '{0}' to '{1}'.\n".format(oldPreferredWareHouseName, newPreferredWareHouseName)
3359 chandransh 232
        ds_item.preferredWarehouse = item.preferredWarehouse
4413 anupam.sin 233
 
234
    if item.defaultWarehouse:
235
        ds_item.defaultWarehouse = item.defaultWarehouse
4506 phani.kuma 236
 
237
    if item.preferredVendor:
5080 amit.gupta 238
        if item.preferredVendor != ds_item.preferredVendor:
239
            newPreferredVendorName = get_Vendor(item.preferredVendor).name
240
            oldPreferredVendorName = 'None'
241
            if ds_item.preferredVendor:
242
                oldPreferredVendorName = get_Vendor(ds_item.preferredVendor).name
243
            message += "Preferred vendor is changed from '{0}' to '{1}'.\n".format(newPreferredVendorName, oldPreferredVendorName)        
4506 phani.kuma 244
        ds_item.preferredVendor = item.preferredVendor
5080 amit.gupta 245
 
246
    if item.isWarehousePreferenceSticky != ds_item.isWarehousePreferenceSticky:
247
        flag = "ON" if item.isWarehousePreferenceSticky else "OFF"
248
        message += "Warehouse preference sticky is {0}.\n".format(flag)
249
 
4413 anupam.sin 250
    ds_item.isWarehousePreferenceSticky = item.isWarehousePreferenceSticky
3359 chandransh 251
 
2347 ankur.sing 252
    ds_item.updatedOn = datetime.datetime.now()
2065 ankur.sing 253
 
122 ashish 254
    session.commit();
5047 amit.gupta 255
    subject = "Item '{0}' is updated in Catalog. Id is {1}".format(__get_product_name(ds_item),ds_item.id)
256
    if message:
257
        __send_mail(subject, message)
122 ashish 258
    return ds_item.id
94 ashish 259
 
103 ashish 260
def add_item(item):
261
    if not item:
122 ashish 262
        raise InventoryServiceException(108, "Bad item in request")
635 rajveer 263
    if get_item(item.id):
122 ashish 264
        raise InventoryServiceException(101, "Item already exists")
2120 ankur.sing 265
 
266
    validate_item_prices(item)
267
 
103 ashish 268
    ds_item = Item()
963 chandransh 269
    if item.productGroup:
270
        ds_item.product_group = item.productGroup
271
    if item.brand:
272
        ds_item.brand = item.brand
515 rajveer 273
    if item.modelName:
274
        ds_item.model_name = item.modelName
275
    if item.modelNumber:
276
        ds_item.model_number = item.modelNumber
609 chandransh 277
    if item.color:
278
        ds_item.color = item.color
483 rajveer 279
    if item.category:
280
        ds_item.category = item.category
281
    if item.comments:
282
        ds_item.comments = item.comments
283
 
103 ashish 284
    ds_item.addedOn = datetime.datetime.now()
609 chandransh 285
    ds_item.updatedOn = datetime.datetime.now()
2116 ankur.sing 286
    if item.startDate:
287
        ds_item.startDate = to_py_date(item.startDate)
288
    if item.retireDate:
289
        ds_item.retireDate = to_py_date(item.retireDate)
609 chandransh 290
 
483 rajveer 291
    if item.mrp:
292
        ds_item.mrp = item.mrp
293
    if item.sellingPrice:
294
        ds_item.sellingPrice = item.sellingPrice
122 ashish 295
    if item.weight:
296
        ds_item.weight = item.weight
297
 
298
    if item.featureId:
299
        ds_item.feature_id = item.featureId
300
    if item.featureDescription:
301
        ds_item.feature_description = item.featureDescription
302
 
103 ashish 303
    if item.otherInfo:
304
        for k,v in item.otherInfo.iteritems():
305
            info = ItemInfo()
306
            info.key = k
307
            info.value = v
308
            ds_item.iteminfo.append(info)
2116 ankur.sing 309
 
103 ashish 310
    #check if categories present. If yes, add them to system
122 ashish 311
 
609 chandransh 312
    if item.bestDealValue:
313
        ds_item.bestDealValue = item.bestDealValue
314
    if item.bestDealText:
315
        ds_item.bestDealText = item.bestDealText
2116 ankur.sing 316
    if item.bestSellingRank:
317
        ds_item.bestSellingRank = item.bestSellingRank
318
    ds_item.defaultForEntity = item.defaultForEntity
2251 ankur.sing 319
    ds_item.risky = item.risky
609 chandransh 320
 
3467 chandransh 321
    if item.expectedDelay is not None:
3359 chandransh 322
        ds_item.expectedDelay = item.expectedDelay
3467 chandransh 323
    else:
324
        ds_item.expectedDelay = 0
3359 chandransh 325
 
326
    if item.preferredWarehouse:
327
        ds_item.preferredWarehouse = item.preferredWarehouse
328
 
4881 phani.kuma 329
    if item.defaultWarehouse:
330
        ds_item.defaultWarehouse = item.defaultWarehouse
331
 
332
    if item.preferredVendor:
333
        ds_item.preferredVendor = item.preferredVendor
334
 
2116 ankur.sing 335
    # Check if a similar item already exists in our database
4725 phani.kuma 336
    similar_item = Item.query.filter_by(brand=item.brand, model_number=item.modelNumber, model_name=item.modelName).first()
337
    print "[SIMILAR ITEM FOUND:] FOR {0} {1} {2}".format(item.brand, item.modelNumber, item.modelName)
2116 ankur.sing 338
 
339
    if similar_item is None or similar_item.catalog_item_id is None:
340
        # If there is no similar item in the database from before,
341
        # use the entity_id_generator
342
        entity_id = EntityIDGenerator.query.first()
343
        ds_item.catalog_item_id = entity_id.id + 1
344
        ds_item.status = status.IN_PROCESS
345
        ds_item.status_description = "This item is in process."
346
        entity_id.id = entity_id.id  + 1
4725 phani.kuma 347
        if similar_item is not None and similar_item.catalog_item_id is None:
348
            similar_item.catalog_item_id = entity_id.id
2116 ankur.sing 349
    else:
350
        #If a similar item already exists for a product group, brand and model_number, set it as same.
351
        ds_item.catalog_item_id = similar_item.catalog_item_id
352
        ds_item.category = similar_item.category
4762 phani.kuma 353
        ds_item.product_group = similar_item.product_group
2116 ankur.sing 354
        ds_item.status = similar_item.status
355
        ds_item.status_description = similar_item.status_description
356
 
103 ashish 357
    session.commit();
5052 amit.gupta 358
    subject = "New item is added. Id is {0}".format(str(ds_item.id))
5047 amit.gupta 359
    message = "Category : {6}, Brand : {0}, Model : {1}, Model Number : {2}\nColor : {3}, Selling Price : {4}, Mrp : {5}, \nPromotion Text : {7}".format(item.brand, item.modelNumber, item.modelName, item.color, item.sellingPrice, item.mrp, item.category, item.bestDealText)
360
    __send_mail(subject, message)
3325 chandransh 361
    return ds_item.id
362
 
363
def update_inventory_history(warehouse_id, timestamp, availability):
364
    warehouse = get_Warehouse(warehouse_id)
365
    if not warehouse:
366
        raise InventoryServiceException(107, "Warehouse? Where?")
5124 mandeep.dh 367
    vendor = warehouse.vendor
3325 chandransh 368
    time = datetime.datetime.now()
369
    for item_key, quantity in availability.iteritems():
370
        try:
371
            vendor_item_mapping = VendorItemMapping.query.filter_by(vendor=vendor, item_key=item_key).one();
372
            item = vendor_item_mapping.item
373
        except:
374
            continue  
375
        try:
376
            item_inventory_history = ItemInventoryHistory()
377
            item_inventory_history.warehouse = warehouse
378
            item_inventory_history.item = item
379
            item_inventory_history.timestamp = time
380
            item_inventory_history.availibility = quantity
381
        except:
382
            raise InventoryServiceException(108, "Some unforeseen error while updating inventory")
383
    session.commit()
103 ashish 384
 
483 rajveer 385
def update_inventory(warehouse_id, timestamp, availability):
386
    warehouse = get_Warehouse(warehouse_id)
387
    if not warehouse:
388
        raise InventoryServiceException(107, "Warehouse? Where?")
389
 
390
    time = datetime.datetime.now()
391
    warehouse.lastCheckedOn = time
392
    warehouse.vendorString = timestamp
5116 mandeep.dh 393
    vendor = warehouse.vendor
2368 ankur.sing 394
    session.commit()
1368 chandransh 395
    for item_key, quantity in availability.iteritems():
483 rajveer 396
        try:
1368 chandransh 397
            vendor_item_mapping = VendorItemMapping.query.filter_by(vendor=vendor, item_key=item_key).one();
398
            item = vendor_item_mapping.item
494 rajveer 399
        except:
4873 mandeep.dh 400
            print 'Skipping update for ' + item_key + ' quantity ' + str(quantity) + ' warehouse id: ' + str(warehouse_id)
401
            __send_mail_for_missing_key(item_key, quantity, warehouse_id)
4748 mandeep.dh 402
            continue
494 rajveer 403
        try:
483 rajveer 404
            current_inventory_snapshot = CurrentInventorySnapshot.get_by(item=item, warehouse=warehouse)
405
            if not current_inventory_snapshot:
406
                current_inventory_snapshot = CurrentInventorySnapshot()
407
                current_inventory_snapshot.item = item
408
                current_inventory_snapshot.warehouse = warehouse
409
                current_inventory_snapshot.availibility = 0
871 chandransh 410
                current_inventory_snapshot.reserved = 0
483 rajveer 411
            # added the difference in the current inventory    
412
            current_inventory_snapshot.availibility = current_inventory_snapshot.availibility + quantity
4400 rajveer 413
            try:
414
                if quantity > 0 and __get_item_reserved(item) > 0:
415
                    cl = TransactionClient().get_client()
4448 rajveer 416
                    #FIXME hardcoding for warehouse id 
417
                    cl.addAlert(AlertType.NEW_INVENTORY_ALERT, 5, "Inventory received for item " + item.brand + " " + item.model_name + " " + item.model_number + " " +  item.color)
4400 rajveer 418
            except:
419
                print "Not able to raise alert for incoming inventory" 
4822 mandeep.dh 420
            if current_inventory_snapshot.availibility < 0:
421
                __send_alert_for_negative_availability(item, current_inventory_snapshot.availibility, warehouse)
483 rajveer 422
        except:
423
            raise InventoryServiceException(108, "Some unforeseen error while updating inventory")
2368 ankur.sing 424
        session.commit() 
425
        check_risky_item(item)
483 rajveer 426
 
4822 mandeep.dh 427
def __send_alert_for_negative_reserved(item, reserved, warehouse):
4873 mandeep.dh 428
    itemName = " ".join([str(item.id), str(item.brand), str(item.model_name), str(item.model_number), str(item.color)])
429
    EmailAttachmentSender.mail('cnc.center@shop2020.in', '5h0p2o2o', 'mandeep.dhir@shop2020.in', 'Negative reserved: ' + str(reserved) + ' for Item Id: ' + itemName + ' warehouse id: ' + str(warehouse.id), None)
4822 mandeep.dh 430
 
431
def __send_alert_for_negative_availability(item, availability, warehouse):
4873 mandeep.dh 432
    itemName = " ".join([str(item.id), str(item.brand), str(item.model_name), str(item.model_number), str(item.color)])
433
    EmailAttachmentSender.mail('cnc.center@shop2020.in', '5h0p2o2o', 'mandeep.dhir@shop2020.in', 'Negative availability ' + str(availability) + ' for Item id: ' + itemName + ' warehouse id: ' + str(warehouse.id), None)
4822 mandeep.dh 434
 
4992 mandeep.dh 435
    # Let availability be auto-fixed from PLB for Hotspot warehouses
436
    if warehouse.id in [1, 2, 5]:
437
        for mapping in get_item_mappings(item.id):
438
            if mapping.vendor_id == 1:
439
                item_key = mapping.item_key
440
                break
441
 
442
        missedInventoryUpdate = MissedInventoryUpdate()
443
        missedInventoryUpdate.itemKey = item_key
444
        missedInventoryUpdate.quantity = 0
445
        missedInventoryUpdate.isIgnored = 0
446
        missedInventoryUpdate.timestamp = datetime.datetime.now()
447
        missedInventoryUpdate.warehouseId = warehouse.id
448
        session.commit()
449
 
4873 mandeep.dh 450
def __send_mail_for_missing_key(item_key, quantity, warehouse_id):
4985 mandeep.dh 451
    missedInventoryUpdate = MissedInventoryUpdate.get_by(itemKey = item_key, warehouseId = warehouse_id)
452
    # One email per product key mismatch
453
    if not missedInventoryUpdate:
5110 mandeep.dh 454
        EmailAttachmentSender.mail('cnc.center@shop2020.in', '5h0p2o2o', ['mandeep.dhir@shop2020.in', 'chaitnaya.vats@shop2020.in', 'asghar.bilgrami@shop2020.in'], 'Skipped inventory update for ' + item_key + ' quantity ' + str(quantity) + ' warehouse id: ' + str(warehouse_id), None)
4873 mandeep.dh 455
        missedInventoryUpdate = MissedInventoryUpdate()
456
        missedInventoryUpdate.itemKey = item_key
457
        missedInventoryUpdate.quantity = quantity
4985 mandeep.dh 458
        missedInventoryUpdate.isIgnored = 1
4873 mandeep.dh 459
        missedInventoryUpdate.timestamp = datetime.datetime.now()
460
        missedInventoryUpdate.warehouseId = warehouse_id
4748 mandeep.dh 461
        session.commit()
4985 mandeep.dh 462
    else:
463
        missedInventoryUpdate.quantity += quantity
464
        session.commit()
4748 mandeep.dh 465
 
4320 rajveer 466
def add_inventory(itemId, warehouseId, quantity):
467
    current_inventory_snapshot = CurrentInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)
468
    if not current_inventory_snapshot:
469
        current_inventory_snapshot = CurrentInventorySnapshot()
470
        current_inventory_snapshot.item_id = itemId
471
        current_inventory_snapshot.warehouse_id = warehouseId
472
        current_inventory_snapshot.availibility = 0
473
        current_inventory_snapshot.reserved = 0
474
    # added the difference in the current inventory    
475
    current_inventory_snapshot.availibility = current_inventory_snapshot.availibility + quantity
4813 rajveer 476
    session.commit()
4822 mandeep.dh 477
    if current_inventory_snapshot.availibility < 0:
478
        __send_alert_for_negative_availability(get_item(itemId), current_inventory_snapshot.availibility, get_Warehouse(warehouseId))
4813 rajveer 479
    check_risky_item(get_item(itemId)) 
94 ashish 480
 
5110 mandeep.dh 481
def add_bad_inventory(itemId, warehouseId, quantity):
482
    bad_inventory_snapshot = BadInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)
483
    if not bad_inventory_snapshot:
484
        bad_inventory_snapshot = BadInventorySnapshot()
485
        bad_inventory_snapshot.item_id = itemId
486
        bad_inventory_snapshot.warehouse_id = warehouseId
487
        bad_inventory_snapshot.availability = 0
488
    # added the difference in the current inventory    
489
    bad_inventory_snapshot.availability += quantity
490
    session.commit()
491
    if bad_inventory_snapshot.availability < 0:
492
        __send_alert_for_negative_availability(get_item(itemId), bad_inventory_snapshot.availability, get_Warehouse(warehouseId))
493
 
635 rajveer 494
def get_item_inventory_by_item_id(item_id):
495
    inventory = Item.get_by(id=item_id).currentInventory
379 ashish 496
    if not inventory:
497
        raise InventoryServiceException(108, "Some unforeseen error while updating inventory")
498
    return inventory
499
 
103 ashish 500
 
501
def retire_warehouse(warehouse_id):
502
    if not warehouse_id:
122 ashish 503
        raise InventoryServiceException(101, "Bad warehouse id")
103 ashish 504
    warehouse = get_Warehouse(warehouse_id)
505
    if not warehouse:
122 ashish 506
        raise InventoryServiceException(108, "warehouse id not present")
507
    warehouse.status = status.DELETED;
103 ashish 508
    session.commit()
509
 
510
def retire_item(item_id):
511
    if not item_id:
122 ashish 512
        raise InventoryServiceException(101, "bad item id")
635 rajveer 513
    item = get_item(item_id)
103 ashish 514
    if not item:
122 ashish 515
        raise InventoryServiceException(108, "item id not present")
516
    item.status = status.PHASED_OUT
517
    item.retireDate = datetime.datetime.now()
103 ashish 518
    session.commit()
122 ashish 519
 
520
#need to implement threads based solution here
103 ashish 521
def start_item_on(item_id, timestamp):
522
    if not item_id:
122 ashish 523
        raise InventoryServiceException(101, "bad item id")
635 rajveer 524
    item = get_item(item_id)
103 ashish 525
    if not item:
122 ashish 526
        raise InventoryServiceException(108, "item id not present")
103 ashish 527
 
122 ashish 528
    item.status = status.ACTIVE
529
    item.startDate = datetime.datetime.fromtimestamp(to_py_date(timestamp))
530
    add_status_change_log(item, status.ACTIVE)
103 ashish 531
    session.commit()
532
 
122 ashish 533
#need to implement threads here
103 ashish 534
def retire_item_on(item_id, timestamp):
535
    if not item_id:
122 ashish 536
        raise InventoryServiceException(101, "bad item id")
635 rajveer 537
    item = get_item(item_id)
103 ashish 538
    if not item:
122 ashish 539
        raise InventoryServiceException(108, "item id not present")
103 ashish 540
 
122 ashish 541
    item.status = status.PHASED_OUT
542
    item.retireDate = datetime.datetime.fromtimestamp(to_py_date(timestamp))
543
    add_status_change_log(item, status.PHASED_OUT)
103 ashish 544
    session.commit()
545
 
546
def add_status_change_log(item, new_status):
547
    item_change_log = ItemChangeLog()
548
    item_change_log.new_status = new_status
549
    item_change_log.old_status = item.status
550
    item_change_log.timestamp = datetime.datetime.now()
551
    item_change_log.item = item
552
    session.commit()
553
 
554
def change_item_status(item_id, new_status):
555
    if not item_id:
122 ashish 556
        raise InventoryServiceException(101, "bad item id")
635 rajveer 557
    item = get_item(item_id)
103 ashish 558
    if not item:
122 ashish 559
        raise InventoryServiceException(108, "item id not present")
2116 ankur.sing 560
    add_status_change_log(item, new_status)
122 ashish 561
    item.status = new_status
2251 ankur.sing 562
    if item.status == status.PHASED_OUT:
563
        item.status_description = "This item has been phased out"
5047 amit.gupta 564
        __send_mail("Item '{0}' is Phased-Out. Item id is {1}".format(__get_product_name(item), item_id), "")
2251 ankur.sing 565
    elif item.status == status.DELETED:
566
        item.status_description = "This item has been deleted"
3924 rajveer 567
    elif item.status == status.PAUSED:
568
        item.status_description = "This item is currently out of stock"      
569
    elif item.status == status.PAUSED_BY_RISK:
570
        item.status_description = "This item is currently out of stock"
571
        #This will clear cache from tomcat
572
        __clear_homepage_cache()  
2251 ankur.sing 573
    elif item.status == status.ACTIVE:
574
        item.status_description = "This item is active"
5047 amit.gupta 575
        __send_mail("Item '{0}' is Active. Item id is {1}".format(__get_product_name(item), item_id), "")
2251 ankur.sing 576
    elif item.status == status.IN_PROCESS:
577
        item.status_description = "This item is in process"
578
    elif item.status == status.CONTENT_COMPLETE:
579
        item.status_description = "This item is in process"
103 ashish 580
    session.commit()
581
 
582
def get_item_availability_for_warehouse(warehouse_id, item_id):
583
    if not warehouse_id:
122 ashish 584
        raise InventoryServiceException(101, "bad warehouse_id")
103 ashish 585
    if not item_id:
122 ashish 586
        raise InventoryServiceException(101, "bad item_id")
103 ashish 587
 
588
    warehouse = get_Warehouse(warehouse_id)
589
    if not warehouse:
122 ashish 590
        raise InventoryServiceException(108, "warehouse does not exist")
635 rajveer 591
    item = get_item(item_id)
504 rajveer 592
 
103 ashish 593
    if not item:
766 rajveer 594
        raise InventoryServiceException(108, "item does not exist")
122 ashish 595
 
494 rajveer 596
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id)
504 rajveer 597
    query = query.filter_by(item_id = item.id)
494 rajveer 598
    try:
599
        current_inventory_snapshot = query.one()
871 chandransh 600
        return current_inventory_snapshot.availibility - current_inventory_snapshot.reserved
494 rajveer 601
    except:
602
        return 0
603
    """
604
    current_inventory_snapshot = CurrentInventorySnapshot.query.filter(CurrentInventorySnapshot.warehouse_id == warehouse_id, CurrentInventorySnapshot.item_id == item_id).one()
103 ashish 605
    if not current_inventory_snapshot:
606
        return 0
607
    else:
608
        return current_inventory_snapshot.availibility
494 rajveer 609
    """
643 chandransh 610
 
2368 ankur.sing 611
def check_risky_item(item):
2251 ankur.sing 612
    if not item.risky:
613
        return
4813 rajveer 614
    warehouse_ids = None
615
    if item.isWarehousePreferenceSticky :
616
        warehouse_ids = [item.preferredWarehouse]
617
    availability = __get_item_availability(item, warehouse_ids)
2983 chandransh 618
    if availability <= 0:
2251 ankur.sing 619
        if item.status == status.ACTIVE:
2984 rajveer 620
            change_item_status(item.id, status.PAUSED_BY_RISK)
4797 rajveer 621
            __send_mail_for_oos_item(item)
2251 ankur.sing 622
    else:
2984 rajveer 623
        if item.status == status.PAUSED_BY_RISK:
2251 ankur.sing 624
            change_item_status(item.id, status.ACTIVE)
2368 ankur.sing 625
    session.commit()
2251 ankur.sing 626
 
4406 anupam.sin 627
'''
628
This method returns quantity of a particular item across all warehouses whose ids is provided
629
if warehouse_ids is null it checks for inventory in all warehouses.
630
'''
631
def __get_item_availability(item, warehouse_ids):
632
    if warehouse_ids is None:
633
        all_inventory = CurrentInventorySnapshot.query.filter_by(item = item).all()
634
        availability = 0
635
        reserved = 0
636
        for currInv in all_inventory:
637
            availability = availability + currInv.availibility
638
            reserved = reserved + currInv.reserved
639
        return availability - reserved
640
    else:
641
        total_availability = 0
642
        for warehouse_id in warehouse_ids:
643
            try:
644
                current_inventory_snapshot = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item.id).one()
645
                availability = current_inventory_snapshot.availibility - current_inventory_snapshot.reserved
646
            except Exception as e:
647
                print e
648
                availability = 0    
649
            total_availability = total_availability + availability
650
        return total_availability 
4400 rajveer 651
 
652
def __get_item_reserved(item):
653
    all_inventory = CurrentInventorySnapshot.query.filter_by(item = item).all()
654
    reserved = 0
655
    for currInv in all_inventory:
656
        reserved = reserved + currInv.reserved
657
    return reserved
2983 chandransh 658
 
871 chandransh 659
def reserve_item_in_warehouse(item_id, warehouse_id, quantity):    
660
    if not warehouse_id:
661
        raise InventoryServiceException(101, "bad warehouse_id")
2251 ankur.sing 662
    item = get_item(item_id)
663
    if not item:
871 chandransh 664
        raise InventoryServiceException(101, "bad item_id")
665
 
666
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)
667
    try:
668
        current_inventory_snapshot = query.one()
669
    except:
4103 chandransh 670
        current_inventory_snapshot = CurrentInventorySnapshot()
671
        current_inventory_snapshot.warehouse_id = warehouse_id
672
        current_inventory_snapshot.item_id = item_id
673
        current_inventory_snapshot.availibility = 0
674
        current_inventory_snapshot.reserved = 0
675
 
676
    current_inventory_snapshot.reserved = current_inventory_snapshot.reserved + quantity
677
    session.commit()
678
    check_risky_item(item)
679
    return True
871 chandransh 680
 
681
def reduce_reservation_count(item_id, warehouse_id, quantity):
682
    if not warehouse_id:
683
        raise InventoryServiceException(101, "bad warehouse_id")
2251 ankur.sing 684
    item = get_item(item_id)
685
    if not item:
871 chandransh 686
        raise InventoryServiceException(101, "bad item_id")
687
 
688
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)
689
    try:
690
        current_inventory_snapshot = query.one()
691
        current_inventory_snapshot.reserved = current_inventory_snapshot.reserved - quantity
692
        session.commit()
2368 ankur.sing 693
        check_risky_item(item)
4822 mandeep.dh 694
        if current_inventory_snapshot.reserved < 0:
695
            __send_alert_for_negative_reserved(get_item(item_id), current_inventory_snapshot.reserved, get_Warehouse(warehouse_id))
871 chandransh 696
        return True
697
    except:
698
        print "Unexpected error:", sys.exc_info()[0]
699
        return False
700
 
2075 rajveer 701
def mark_item_as_content_complete(entity_id, category, brand, modelName, modelNumber):
2828 rajveer 702
    '''
703
    Get all the items for this entityID and update category, brand, modelName and modelNumber for all.
704
    Update Status for only IN_PROCESS items to CONTENT_COMPLETE
705
    '''
723 chandransh 706
    content_complete_status = status.CONTENT_COMPLETE
2828 rajveer 707
    items = Item.query.filter_by(catalog_item_id=entity_id).all()
723 chandransh 708
    current_timestamp = datetime.datetime.now()
709
    for item in items:
2828 rajveer 710
        if item.status == status.IN_PROCESS:
711
            item.status = content_complete_status
712
            item_change_log = ItemChangeLog()
713
            item_change_log.old_status = item.status
714
            item_change_log.new_status = content_complete_status
715
            item_change_log.timestamp = current_timestamp
716
            item_change_log.item = item
723 chandransh 717
 
4762 phani.kuma 718
        category_object = get_category(category)
719
        if category_object is not None:
720
            item.category = category
721
            item.product_group = category_object.display_name
2075 rajveer 722
        item.brand = brand
2081 rajveer 723
        item.model_name = modelName
724
        item.model_number = modelNumber
723 chandransh 725
        item.updatedOn = current_timestamp
726
    session.commit()
727
    return True
1294 chandransh 728
 
643 chandransh 729
def get_item_availability_for_location(warehouse_loc, item_id):
3355 chandransh 730
    """
731
    Determines the warehouse that should be used to fulfil an order for the given item.
5110 mandeep.dh 732
    Algorithm explained at https://sites.google.com/a/shop2020.in/virtual-w-h-and-inventory/technical-details
733
 
734
    If item's Preferred warehouse is present
735
        If item marked sticky
736
            use GOOD type warehouse at preferred warehouse with maximum availability. 
737
            If not available at any, then use THIRD_PARTY warehouse without billing support 
738
            corresponding to preferred warehouse where available. 
739
            If still not available, then use any GOOD type warehouse with billing support.
740
        else
741
            if availability > 0 at least one of GOOD type warehouses in preferred warehouse
742
                use GOOD type warehouse at preferred warehouse with maximum availability
743
 
744
    If available at no warehouse
745
        use any GOOD type warehouse at default warehouse
746
    else
747
        if available at any warehouse of type GOOD with billing support
748
            use warehouse of type GOOD with billing support with maximum availability
749
        else
750
            if preferred warehouse is present and availability at preferred warehouse's corresponding ThirdPartyWarehouse without billing support > 0
751
                preferred warehouse's corresponding ThirdPartyWarehouse without billing support with maximum availability
752
            else
753
                use ThirdPartyWarehouse without billing support with maximum availability
754
 
3355 chandransh 755
    Returns an ordered list of size 4 with following elements in the given order:
756
    1. Logistics location of the warehouse which was finally picked up to ship the order.
757
    2. Id of the warehouse which was finally picked up.
758
    3. Inventory size in the selected warehouse.
759
    4. Expected delay added by the category manager.
5110 mandeep.dh 760
 
3355 chandransh 761
    Parameters:
762
     - warehouse_loc
763
     - item_id
764
    """
643 chandransh 765
    if warehouse_loc is None:
766
        raise InventoryServiceException(101, "Bad Warehouse Location")
767
    if not item_id:
1416 chandransh 768
        raise InventoryServiceException(101, "Bad Item id")
643 chandransh 769
 
1416 chandransh 770
    item = Item.get_by(id=item_id)
4406 anupam.sin 771
    logisticsLocation = warehouse_loc
5110 mandeep.dh 772
    warehouse_ids = []
773
    goodBillableWarehouses = []
774
    goodBillableWarehousesAtPreferredShippingLocation = []
775
    goodBillableWarehousesAtDefaultShippingLocation = []
776
    nonBillableWarehouses = []
777
    nonBillableWarehousesCorrespondingToPreferredShippingLocation = []
778
    for warehouse in Warehouse.query.all():
779
        if (warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.BAD]):
780
            continue
781
        if warehouse.shippingWarehouseId == item.defaultWarehouse and warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD] and warehouse.billingWarehouseId:
782
            goodBillableWarehousesAtDefaultShippingLocation.append(warehouse.id)
783
        if warehouse.billingWarehouseId is None:
784
            nonBillableWarehouses.append(warehouse.id)
785
        if warehouse.logisticsLocation == logisticsLocation:
786
            warehouse_ids.append(warehouse.id)
787
            if warehouse.billingWarehouseId and warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]:
788
                goodBillableWarehouses.append(warehouse.id)
789
                if item.preferredWarehouse and item.preferredWarehouse == warehouse.shippingWarehouseId:
790
                    goodBillableWarehousesAtPreferredShippingLocation.append(warehouse.id)
791
                    nonBillableWarehousesCorrespondingToPreferredShippingLocation.extend([w.id for w in Warehouse.query.filter_by(logisticsLocation=logisticsLocation, vendor_id = warehouse.vendor_id, billingWarehouseId = None).all()])
792
 
643 chandransh 793
    warehouse_retid = -1
3503 chandransh 794
    total_availability = 0
5110 mandeep.dh 795
 
4406 anupam.sin 796
    '''
797
    If warehouse preference is set and it is sticky then we should fulfil this order from this warehouse only.
798
    But we still need to calculate total availability across warehouses.
799
    '''
5110 mandeep.dh 800
    if (item.isWarehousePreferenceSticky and item.preferredWarehouse):
801
        [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(goodBillableWarehousesAtPreferredShippingLocation, item)
802
        if warehouse_retid == -1:
803
            [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(nonBillableWarehousesCorrespondingToPreferredShippingLocation, item)
804
            if warehouse_retid == -1:
805
                warehouse_retid = goodBillableWarehousesAtPreferredShippingLocation[0]
806
    elif (not item.isWarehousePreferenceSticky and item.preferredWarehouse):
807
        [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(goodBillableWarehousesAtPreferredShippingLocation, item)
808
 
809
    if warehouse_retid == -1:
810
        [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(warehouse_ids, item)
811
        if warehouse_retid == -1:
812
            warehouse_retid = goodBillableWarehousesAtDefaultShippingLocation[0]
813
        else:
814
            [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(goodBillableWarehouses, item)
815
            if warehouse_retid == -1:
816
                if item.preferredWarehouse:
817
                    [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(nonBillableWarehousesCorrespondingToPreferredShippingLocation, item)
818
                    if warehouse_retid == -1:
819
                        # Availability at Good Billable warehouses is Zero, so can safely use all warehouse_ids to capture the thirdparty virtual/good one
820
                        [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(warehouse_ids, item)
821
 
822
    warehouse = Warehouse.get_by(id=warehouse_retid)
823
    billingWarehouseId = warehouse.billingWarehouseId
4406 anupam.sin 824
 
5110 mandeep.dh 825
    # Fetching billing warehouse of a Good billable warehouse corresponding to the virtual one
826
    if warehouse.billingWarehouseId is None:
827
        for warehouse in Warehouse.query.filter_by(vendor_id = warehouse.vendor_id, inventoryType = InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]).all():
828
            if warehouse.billingWarehouseId:
829
                billingWarehouseId = warehouse.billingWarehouseId
830
                break
4897 rajveer 831
 
5110 mandeep.dh 832
    logisticsLocation = warehouse.logisticsLocation  
833
 
4897 rajveer 834
    expectedDelay = item.expectedDelay 
835
    if expectedDelay is None:
836
        print 'expectedDelay field for this item was Null. Resetting it to 0'
837
        expectedDelay = 0
838
    else:
839
        expectedDelay = int(item.expectedDelay)
840
 
841
    if total_availability <= 0:
842
        expectedDelay = expectedDelay + __get_expected_procurement_delay(item)
4979 rajveer 843
        expectedDelay = expectedDelay + __get_vendor_holiday_delay(item, expectedDelay)
4406 anupam.sin 844
 
5110 mandeep.dh 845
    return [logisticsLocation, int(warehouse_retid), total_availability, expectedDelay, billingWarehouseId]
4406 anupam.sin 846
 
5110 mandeep.dh 847
def __get_warehouse_with_max_availability(warehouse_ids, item):
4406 anupam.sin 848
    warehouse_retid = -1
849
    max_availability = 0
850
    total_availability = 0
851
 
5110 mandeep.dh 852
    if item.currentInventory:
853
        for entry in item.currentInventory:
854
            if entry.warehouse_id in warehouse_ids:
855
                availability = entry.availibility - entry.reserved
856
                if availability > max_availability:
857
                    warehouse_retid = entry.warehouse_id
858
                    max_availability = availability
859
                total_availability += availability
643 chandransh 860
 
5110 mandeep.dh 861
    return [warehouse_retid, total_availability]
2341 chandransh 862
 
4897 rajveer 863
def __get_expected_procurement_delay(item):
4979 rajveer 864
    procurementDelay = 2
4897 rajveer 865
    try:
866
        if item.preferredVendor:
867
            delays = VendorItemProcurementDelay.query.filter_by(vendor_id = item.preferredVendor, item_id = item.id).all()
868
        else:
869
            delays = VendorItemProcurementDelay.query.filter_by(item_id = item.id).all()
870
 
871
        procurementDelay= min([delay.procurementDelay for delay in delays])
872
    except Exception as e:
873
        print e
874
    return procurementDelay
875
 
4979 rajveer 876
def __get_vendor_holiday_delay(item, expectedDelay):
877
    holidayDelay = 0
878
    try:
879
        if item.preferredVendor:
880
            holidays = VendorHolidays.query.filter_by(vendor_id = item.preferredVendor).all()
881
            currentDate = datetime.date.today()
882
            expectedDate = currentDate + datetime.timedelta(days = expectedDelay)
883
            for holiday in holidays:
884
                if holiday.holidayType == HolidayType.WEEKLY and holiday.holidayValue != calendar.SUNDAY:
885
                    if currentDate.weekday() > holiday.holidayValue:
886
                        holidayDate = currentDate + datetime.timedelta(days=holiday.holidayValue-currentDate.weekday(), weeks=1)
887
                    else:
888
                        holidayDate = currentDate + datetime.timedelta(days=holiday.holidayValue-currentDate.weekday())
889
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
890
                        holidayDelay = holidayDelay + 1
891
                elif holiday.holidayType == HolidayType.MONTHLY:
892
                    holidayDate = datetime.date(currentDate.year, currentDate.month, holiday.holidayValue)
893
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
894
                        holidayDelay = holidayDelay + 1    
895
                elif holiday.holidayType == HolidayType.SPECIFIC:
5005 rajveer 896
                    holidayValue = str(holiday.holidayValue)
897
                    holidayDate = datetime.date(int(holidayValue[:4]), int(holidayValue[4:6]), int(holidayValue[6:8]))
4979 rajveer 898
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
899
                        holidayDelay = holidayDelay + 1                
900
    except Exception as e:
901
        print e
902
    return holidayDelay 
122 ashish 903
def get_warehouses_for_item(item_id):
643 chandransh 904
 
122 ashish 905
    if not item_id:
906
        raise InventoryServiceException(101, "bad item_id")
635 rajveer 907
    item = get_item(item_id)
122 ashish 908
 
909
    if not item:
910
        raise InventoryServiceException(101, "bad item")
911
 
483 rajveer 912
    warehouses = item.currentInventory.warehouse
501 rajveer 913
    return warehouses
914
 
2404 chandransh 915
def get_child_categories(category):
916
    cm = CategoryManager()
2621 varun.gupt 917
    cat = cm.getCategory(category)
918
    return cat.children_category_ids if cat else None
2404 chandransh 919
 
626 chandransh 920
def get_best_sellers(start_index, stop_index, category=-1):
2404 chandransh 921
    '''
922
    Returns the Best Sellers between the start and the stop index in the given category
923
    '''
1926 rajveer 924
    query = get_best_sellers_query(category, None)
1098 chandransh 925
    best_sellers = query.all()[start_index:stop_index]
621 chandransh 926
    return get_thrift_item_list(best_sellers)
927
 
2093 chandransh 928
def get_best_sellers_count(category=-1):
2404 chandransh 929
    '''
930
    Returns the number of best sellers in the given category
931
    '''
1926 rajveer 932
    count = get_best_sellers_query(category, None).count()
1120 rajveer 933
    if count is None:
934
        count = 0
766 rajveer 935
    return count
621 chandransh 936
 
1926 rajveer 937
def get_best_sellers_catalog_ids(start_index, stop_index, brand, category=-1):
2404 chandransh 938
    '''
939
    Returns the Best sellers for the given brand and category between the start and the stop index.
940
    Ignores the category if it's passed as -1 and the brand if it's passed as None. 
941
    '''
1926 rajveer 942
    query = get_best_sellers_query(category, brand)
1098 chandransh 943
    best_sellers = query.all()[start_index:stop_index]
621 chandransh 944
    return [item.catalog_item_id for item in best_sellers]
1970 rajveer 945
 
1926 rajveer 946
def get_best_sellers_query(category, brand):
2404 chandransh 947
    '''
948
    Returns the query to be used for getting Best Sellers.
949
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
950
    '''
1098 chandransh 951
    query = Item.query.filter_by(status=status.ACTIVE).filter(Item.bestSellingRank != None)
626 chandransh 952
    if category != -1:
1970 rajveer 953
        all_categories = [category]
954
        child_categories = get_child_categories(category)
955
        if child_categories is not None:
956
            all_categories = all_categories + child_categories 
957
        query = query.filter(Item.category.in_(all_categories))
1926 rajveer 958
    if brand is not None:
959
        query = query.filter_by(brand=brand)
1098 chandransh 960
    query = query.order_by(asc(Item.bestSellingRank))
621 chandransh 961
    return query
609 chandransh 962
 
1098 chandransh 963
def get_best_deals(category=-1):
2404 chandransh 964
    '''
965
    Returns the Best deals in the given category. Ignores the category if it's passed as -1.
966
    '''
967
    query = get_best_deals_query(Item, category, None)
1098 chandransh 968
    items = query.all()
609 chandransh 969
    return get_thrift_item_list(items)
970
 
1098 chandransh 971
def get_best_deals_count(category=-1):
2404 chandransh 972
    '''
973
    Returns the count of best deals in the given category.
974
    Ignores the category if it's -1.
975
    '''
976
    count = get_best_deals_counting_query(func.count(distinct(Item.catalog_item_id)), category, None).scalar()
1120 rajveer 977
    if count is None:
978
        count = 0
766 rajveer 979
    return count
501 rajveer 980
 
1926 rajveer 981
def get_best_deals_catalog_ids(start_index, stop_index, brand, category=-1):
2404 chandransh 982
    '''
983
    Returns the catalog_item_ids of best deal items for the given brand and category.
984
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
985
    '''
986
    query = get_best_deals_query(Item, category, brand)
1098 chandransh 987
    best_deal_items = query.all()[start_index:stop_index]
988
    return [item.catalog_item_id for item in best_deal_items]
989
 
2404 chandransh 990
def get_best_deals_counting_query(obj, category, brand):
991
    '''
992
    Returns the query to be used to select the best deals in the given brand and category.
993
    Ignores the category if it's passed as -1 and the brand if it's passed as None. 
994
    '''
995
    query = session.query(obj).filter_by(status=status.ACTIVE).filter(Item.bestDealValue != None)
626 chandransh 996
    if category != -1:
1970 rajveer 997
        all_categories = [category]
998
        child_categories = get_child_categories(category)
999
        if child_categories is not None:
1000
            all_categories = all_categories + child_categories 
1001
        query = query.filter(Item.category.in_(all_categories))
1926 rajveer 1002
    if brand is not None:
1003
        query = query.filter_by(brand=brand)
2404 chandransh 1004
    return query
1005
 
1006
def get_best_deals_query(obj, category, brand):
1007
    '''
1008
    Returns the query to be used to get the best deals in the given category and brand.
1009
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
1010
    '''
1011
    query = get_best_deals_counting_query(obj, category, brand)
1098 chandransh 1012
    query = query.group_by(Item.catalog_item_id).order_by(desc(Item.bestDealValue))
1013
    return query
609 chandransh 1014
 
1098 chandransh 1015
def get_latest_arrivals(limit, category=-1):
2404 chandransh 1016
    '''
1017
    Returns up to limit number of Latest Arrivals in the given category.
1018
    '''
2975 chandransh 1019
    categories = []
1020
    if category != -1:
1021
        categories = [category]
1022
    query = get_latest_arrivals_query(Item, categories, None)
1098 chandransh 1023
    items = query.all()[0:limit]
609 chandransh 1024
    return get_thrift_item_list(items)
598 chandransh 1025
 
1098 chandransh 1026
def get_latest_arrivals_count(limit, category=-1):
2404 chandransh 1027
    '''
1028
    Returns the number of latest arrivals which will be displayed on the website.
3016 chandransh 1029
    To ignore the categories, pass the list as empty. To ignore brand, pass it as null.
2404 chandransh 1030
    '''
2975 chandransh 1031
    categories = []
1032
    if category != -1:
1033
        categories = [category]
1034
    count = get_latest_arrivals_counting_query(func.count(distinct(Item.catalog_item_id)), categories, None).scalar()
1120 rajveer 1035
    if count is None:
1036
        count = 0
1037
    count = min(count, limit)
766 rajveer 1038
    return count
602 chandransh 1039
 
2975 chandransh 1040
def get_latest_arrivals_catalog_ids(start_index, stop_index, brand, categories=[]):
2404 chandransh 1041
    '''
1042
    Returns the catalog_item_ids of the latest arrivals between the start and the stop index
3016 chandransh 1043
    To ignore the categories, pass the list as empty. To ignore brand, pass it as null.
2404 chandransh 1044
    '''
2975 chandransh 1045
    query = get_latest_arrivals_query(Item, categories, brand)
1098 chandransh 1046
    latest_arrivals = query.all()[start_index:stop_index]
1047
    return [item.catalog_item_id for item in latest_arrivals]
1048
 
2975 chandransh 1049
def get_latest_arrivals_counting_query(obj, categories, brand):
2404 chandransh 1050
    '''
1051
    Returns the query to be used to count Latest arrivals.
3016 chandransh 1052
    To ignore the categories, pass the list as empty. To ignore brand, pass it as null.
2404 chandransh 1053
    '''
1054
    query = session.query(obj).filter_by(status=status.ACTIVE)
2975 chandransh 1055
 
1056
    all_categories = []
1057
    for category in categories:
1058
        all_categories.append(category)
1970 rajveer 1059
        child_categories = get_child_categories(category)
2975 chandransh 1060
        if child_categories:
1061
            all_categories = all_categories + child_categories
1062
    if all_categories: 
1970 rajveer 1063
        query = query.filter(Item.category.in_(all_categories))
2975 chandransh 1064
 
1926 rajveer 1065
    if brand is not None:
1066
        query = query.filter_by(brand=brand)
2404 chandransh 1067
    return query
1068
 
2975 chandransh 1069
def get_latest_arrivals_query(obj, categories, brand):
2404 chandransh 1070
    '''
1071
    Returns the query to be used to retrieve Latest Arrivals.
1072
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
1073
    '''
2975 chandransh 1074
    query = get_latest_arrivals_counting_query(obj, categories, brand)
1098 chandransh 1075
    query = query.group_by(Item.catalog_item_id).order_by(desc(Item.startDate))
1076
    return query
609 chandransh 1077
 
1078
def get_thrift_item_list(items):
1098 chandransh 1079
    return [to_t_item(item) for item in items if item != None]
635 rajveer 1080
 
1155 rajveer 1081
def generate_new_entity_id():
1082
    generator =  EntityIDGenerator.query.one()
1083
    id = generator.id + 1
1084
    generator.id = id
1085
    session.commit()
1086
    return id
1087
 
635 rajveer 1088
def put_category_object(object):
1089
    category = Category.get_by(id=1)
1090
    if category is None:
1091
        category = Category()
1092
    category.object = object    
1093
    session.commit()
1094
    return True
1095
 
1096
def get_category_object():
766 rajveer 1097
    object = Category.get_by(id=1).object
1098
    return object
1099
 
4283 anupam.sin 1100
def get_item_pricing(item_id, vendorId):
1341 chandransh 1101
    item = Item.query.filter_by(id=item_id).first()
1102
    if item is None:
1103
        raise InventoryServiceException(101, "Bad Item")
4307 anupam.sin 1104
    '''
1105
    if vendor id is -1 then we calculate an average transfer price to be populated
1106
    at the time of order creation. This will be later updated with actual transfer price
1107
    at the time of billing.
1108
    '''
1109
    if(vendorId == -1):
4543 anupam.sin 1110
        total = 0
4307 anupam.sin 1111
        try:
5133 mandeep.dh 1112
            item_pricings = []
4858 anupam.sin 1113
            if item.preferredWarehouse is not None:
1114
                warehouse = Warehouse.query.filter_by(id=item.preferredWarehouse).first()
5133 mandeep.dh 1115
                item_pricing = VendorItemPricing.query.filter_by(item=item, vendor=warehouse.vendor).first()
1116
                if item_pricing:
1117
                    item_pricings.append(item_pricing)                    
4858 anupam.sin 1118
            else :
1119
                item_pricings = VendorItemPricing.query.filter_by(item=item).all()
4315 anupam.sin 1120
            if item_pricings:
4307 anupam.sin 1121
                for item_pricing in item_pricings:
4543 anupam.sin 1122
                    total += item_pricing.transfer_price
4315 anupam.sin 1123
                avg = total / len(item_pricings)
1124
                item_pricing.transfer_price = avg
4543 anupam.sin 1125
            else:
1126
                item_pricing = VendorItemPricing()
1127
                item_pricing.transfer_price = item.sellingPrice
1128
                vendor = Vendor()
1129
                vendor.id = vendorId
1130
                item_pricing.vendor = vendor
1131
                item_pricing.item = item
1132
 
1133
            return item_pricing
4307 anupam.sin 1134
        except:
1135
            raise InventoryServiceException(101, "Item pricing not found ")
4283 anupam.sin 1136
    vendor = Vendor.get_by(id=vendorId)    
1341 chandransh 1137
    try:
1138
        item_pricing = VendorItemPricing.query.filter_by(vendor=vendor, item=item).one()
1347 chandransh 1139
        return item_pricing
3244 chandransh 1140
    except MultipleResultsFound:
1341 chandransh 1141
        raise InventoryServiceException(110, "Multiple pricing information present for Vendor: " + vendor.name + " and Item: " + str(item_id))
3244 chandransh 1142
    except NoResultFound:
1341 chandransh 1143
        raise InventoryServiceException(111, "Missing pricing information for Vendor: " + vendor.name + " and Item: " + str(item_id))
1144
 
1970 rajveer 1145
def add_category(t_category):
1146
    category = Category.get_by(id=t_category.id)
1147
    if category is None:
1148
        category = Category()
1149
    category.id = t_category.id 
1150
    category.label = t_category.label
1151
    category.description = t_category.description
4762 phani.kuma 1152
    category.display_name = t_category.display_name
1970 rajveer 1153
    category.parent_category_id = t_category.parent_category_id 
1154
    session.commit()
1155
    return True
1156
 
1157
def get_category(id):
1158
    return Category.query.filter_by(id=id).first()
1159
 
1160
def get_all_categories():
1161
    return Category.query.all()
1162
 
1991 ankur.sing 1163
 
1164
def get_all_item_pricing(item_id):
1165
    item = Item.query.filter_by(id=item_id).first()
1166
    if item is None:
1167
        raise InventoryServiceException(101, "Bad Item")
1168
    item_pricing = VendorItemPricing.query.filter_by(item=item).all()
1169
    return item_pricing
1170
 
2116 ankur.sing 1171
def get_item_mappings(item_id):
1172
    item = Item.query.filter_by(id=item_id).first()
1173
    if item is None:
1174
        raise InventoryServiceException(101, "Bad Item")
1175
    item_mappings = VendorItemMapping.query.filter_by(item=item).all()
1176
    return item_mappings
1177
 
1178
def add_vendor_pricing(vendorItemPricing):
1991 ankur.sing 1179
    if not vendorItemPricing:
1180
        raise InventoryServiceException(108, "Bad vendorItemPricing in request")
1181
    vendorId = vendorItemPricing.vendorId
1182
    itemId = vendorItemPricing.itemId
1183
 
1184
    try:
1185
        vendor = Vendor.query.filter_by(id=vendorId).one()
1186
    except:
1187
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
1188
 
1189
    try:
1190
        item = Item.query.filter_by(id=itemId).one()
1191
    except:
5047 amit.gupta 1192
        raise InventoryServiceException(101, "Item not found for itemId " + str(itemId))
1991 ankur.sing 1193
 
2120 ankur.sing 1194
    validate_vendor_prices(to_t_item(item), vendorItemPricing)
2065 ankur.sing 1195
 
1991 ankur.sing 1196
    try:
1197
        ds_vendorItemPricing = VendorItemPricing.query.filter(and_(VendorItemPricing.vendor==vendor, VendorItemPricing.item==item)).one()
1198
    except:
2116 ankur.sing 1199
        ds_vendorItemPricing = VendorItemPricing()
1200
        ds_vendorItemPricing.vendor = vendor
1201
        ds_vendorItemPricing.item = item
1991 ankur.sing 1202
 
5047 amit.gupta 1203
    subject = ""
1204
    message = ""
1991 ankur.sing 1205
    if vendorItemPricing.mop:
1206
        ds_vendorItemPricing.mop = vendorItemPricing.mop
1207
    if vendorItemPricing.dealerPrice:
1208
        ds_vendorItemPricing.dealerPrice = vendorItemPricing.dealerPrice
1209
    if vendorItemPricing.transferPrice:
5047 amit.gupta 1210
        if vendorItemPricing.transferPrice != ds_vendorItemPricing.transfer_price:
1211
            message = "Transfer price for Item '{0}' \nand Vendor:{1} is changed from {2} to {3}.".format(__get_product_name(item), vendor.name, ds_vendorItemPricing.transfer_price, vendorItemPricing.transferPrice)
1212
            subject = "Alert:Change in Transfer Price"
2065 ankur.sing 1213
        ds_vendorItemPricing.transfer_price = vendorItemPricing.transferPrice
1991 ankur.sing 1214
 
1215
    session.commit()
5047 amit.gupta 1216
    if subject:
1217
        __send_mail(subject, message)
1991 ankur.sing 1218
    return
1219
 
2358 ankur.sing 1220
def add_vendor_item_mapping(key, vendorItemMapping):
2116 ankur.sing 1221
    if not vendorItemMapping:
1222
        raise InventoryServiceException(108, "Bad vendorItemMapping in request")
1223
    vendorId = vendorItemMapping.vendorId
1224
    itemId = vendorItemMapping.itemId
1225
 
1226
    try:
1227
        vendor = Vendor.query.filter_by(id=vendorId).one()
1228
    except:
1229
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
1230
 
1231
    try:
1232
        item = Item.query.filter_by(id=itemId).one()
1233
    except:
1234
        raise InventoryServiceException(101, "Item not found for vendorId " + str(itemId))
1235
 
1236
    try:
2358 ankur.sing 1237
        ds_vendorItemMapping = VendorItemMapping.query.filter(and_(VendorItemMapping.vendor==vendor, VendorItemMapping.item==item, VendorItemMapping.item_key==key)).one()
2116 ankur.sing 1238
    except:
1239
        ds_vendorItemMapping = VendorItemMapping()
1240
        ds_vendorItemMapping.vendor = vendor
1241
        ds_vendorItemMapping.item = item
1242
    ds_vendorItemMapping.item_key = vendorItemMapping.itemKey
4985 mandeep.dh 1243
 
2116 ankur.sing 1244
    session.commit()
4985 mandeep.dh 1245
 
1246
    # Marking the missed inventory as not ignored as the catalog dashboard user has updated their key
1247
    for missedInventoryUpdate in MissedInventoryUpdate.query.filter_by(itemKey = vendorItemMapping.itemKey).all():
1248
        missedInventoryUpdate.isIgnored = 0
1249
    session.commit()
1250
 
2116 ankur.sing 1251
    return
1252
 
2120 ankur.sing 1253
def validate_item_prices(item):
2129 ankur.sing 1254
    if item.mrp == None or item.sellingPrice == None or item.mrp == "" or item.sellingPrice == "":
1255
        return
1256
    if item.mrp < item.sellingPrice:
2120 ankur.sing 1257
        print "[BAD MRP and SP:] for {0} {1} {2} {3}. MRP={4}, SP={5}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(item.mrp), str(item.sellingPrice))
1258
        raise InventoryServiceException(101, "[BAD MRP and SP:] for {0} {1} {2} {3}. MRP={4}, SP={5}".format(item.productGroup, item.brand, item.modelNumber, item.color, str(item.mrp), str(item.sellingPrice)))
2065 ankur.sing 1259
    return
2120 ankur.sing 1260
 
1261
def validate_vendor_prices(item, vendorPrices):
2129 ankur.sing 1262
    if item.mrp != None and item.mrp != "" and vendorPrices.mop != "" and item.mrp <  vendorPrices.mop:
2120 ankur.sing 1263
        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))
1264
        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)))
1265
    if vendorPrices.mop != "" and vendorPrices.transferPrice != "" and vendorPrices.transferPrice > vendorPrices.mop:
1266
        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))
1267
        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)))
1268
    return
2065 ankur.sing 1269
 
1270
def get_all_vendors():
1271
    return Vendor.query.all()
1272
 
4725 phani.kuma 1273
def check_color_valid(color):
1274
    if color is not None:
1275
        color = color.strip().lower()
1276
        if color != '' and color != 'na' and color != 'blank' and color != '(blank)':
1277
            return True
1278
    return False
1279
 
1280
def check_similar_item(brand, model_number, model_name, color):
2129 ankur.sing 1281
    query = Item.query
2428 ankur.sing 1282
    query = query.filter_by(brand=brand)
1283
    query = query.filter_by(model_number=model_number)
4725 phani.kuma 1284
    query = query.filter_by(model_name=model_name)
1285
    similar_items = query.all()
1286
    item = None
1287
    # Check if a similar item already exists in our database
1288
    for old_item in similar_items:
1289
        if old_item.color != None and old_item.color.strip().lower() == color.strip().lower():
1290
            item = old_item
1291
            break
1292
 
1293
    # Check if a similar item already exists in our database with out valid color if similar item with same color is not found
2116 ankur.sing 1294
    if item is None:
4725 phani.kuma 1295
        for old_item in similar_items:
1296
            if not check_color_valid(old_item.color):
1297
                item = old_item
1298
                break
1299
    i = 0
1300
    color_of_similar_item = None
1301
    # Check if a similar item already exists in our database to be used to get catalog_item_id
1302
    for old_item in similar_items:
1303
        # get a similar item already existing in our database with valid color
1304
        if check_color_valid(old_item.color):
1305
            similar_item = old_item
1306
            color_of_similar_item = similar_item.color
1307
            break
1308
        i = i + 1
1309
        # get a similar item already existing in our database if similar item with valid color is not found
1310
        if i == len(similar_items):
1311
            similar_item = old_item
1312
            color_of_similar_item = similar_item.color
1313
 
1314
    # Check if a similar item that is obtained above is having a valid color
1315
    if check_color_valid(color_of_similar_item):
1316
        # if a similar item that is obtained above is having a valid color and new item is about to be created with out valid color it is not done.
1317
        # since for example if their is a item with red color in our database and we are creating a new item with no color for the same product which is wrong.
1318
        if item is None and not check_color_valid(color):
1319
            return similar_item.id
1320
 
1321
    if item is None:
2116 ankur.sing 1322
        return 0
1323
    else:
1324
        return item.id
2286 ankur.sing 1325
 
1326
def change_risky_flag(item_id, risky):
1327
    item = get_item(item_id)
1328
    if not item:
1329
        raise InventoryServiceException(101, "Item missing in our database")
1330
    try:
1331
        log_risky_flag(item_id, risky)
1332
    except:
1333
        print "Not able to log risky flag change"
1334
    item.risky = risky
4295 varun.gupt 1335
    if not risky and item.status == status.PAUSED_BY_RISK:
2368 ankur.sing 1336
        change_item_status(item.id, status.ACTIVE)
2286 ankur.sing 1337
    session.commit()
5047 amit.gupta 1338
    flag = "ON" if risky else "OFF"
1339
    subject = "Risky flag is {0} for Item {1}.".format(flag, __get_product_name(item))
1340
    __send_mail(subject,"")
2286 ankur.sing 1341
 
4957 phani.kuma 1342
def get_items_for_mastersheet(categoryName, brand):
1343
    if not categoryName or not brand:
1344
        raise InventoryServiceException(101, "Invalid category or brand in request")
1345
 
4762 phani.kuma 1346
    categories = ["Handsets", "Tablets", "Laptops"]
4957 phani.kuma 1347
    query = Item.query.filter(Item.status != status.PHASED_OUT)
1348
    if categoryName == "ALL":
1349
        pass
1350
    elif categoryName == "ALL Accessories":
1351
        query = query.filter(~Item.product_group.in_(categories))
1352
    elif categoryName == "ALL Handsets":
1353
        query = query.filter(Item.product_group.in_(categories))
1354
    elif categoryName == "Mobile Accessories":
1355
        child_categories = get_child_categories(10011)
1356
        if child_categories is not None:
1357
            child_categories.append(0)
1358
            query = query.filter(Item.category.in_(child_categories))
1359
    elif categoryName == "Laptop Accessories":
1360
        child_categories = get_child_categories(10070)
1361
        if child_categories is not None:
1362
            child_categories.append(0)
1363
            query = query.filter(Item.category.in_(child_categories))
1364
    else:
1365
        query = query.filter(Item.product_group == categoryName)
1366
 
1367
    if brand == "ALL":
1368
        pass
1369
    else:
1370
        query = query.filter(Item.brand == brand)
2358 ankur.sing 1371
    items = query.all()
1372
    return items
2116 ankur.sing 1373
 
2358 ankur.sing 1374
def get_risky_items():
1375
    items = Item.query.filter_by(risky=True).all()
1376
    return items
3008 rajveer 1377
 
2809 rajveer 1378
def get_similar_items_catalog_ids(start_index, stop_index, itemId):
3008 rajveer 1379
    query = SimilarItems.query.filter_by(item_id=itemId).limit(stop_index-start_index)
1380
    similar_items = query.all()
3289 rajveer 1381
    return_list = []
1382
    for similar_item in similar_items:
1383
        isActive = False
1384
        try:
1385
            all_items = Item.query.filter_by(catalog_item_id=similar_item.catalog_item_id).all()
1386
        except:
1387
            continue
1388
        for item in all_items:
1389
            isActive = isActive or item.status == status.ACTIVE
1390
        if isActive:
1391
            return_list.append(similar_item.catalog_item_id)
1392
    return return_list
4423 phani.kuma 1393
 
1394
def get_all_similar_items_catalog_ids(itemId):
1395
    query = SimilarItems.query.filter_by(item_id=itemId)
1396
    similar_items = query.all()
1397
    return_list = []
1398
    for similar_item in similar_items:
1399
        item_query = Item.query.filter_by(catalog_item_id=similar_item.catalog_item_id).limit(1)
1400
        item = item_query.one()
1401
        return_list.append(item)
2809 rajveer 1402
 
4423 phani.kuma 1403
    return get_thrift_item_list(return_list)
1404
 
1405
def add_similar_item_catalog_id(itemId, catalog_item_id):
1406
    if not itemId or not catalog_item_id:
1407
        raise InventoryServiceException(101, "Bad itemId or catalogItemId in request")
1408
    items_for_entity = get_items_by_catalog_id(catalog_item_id)
1409
    if not len(items_for_entity):
1410
        raise InventoryServiceException(101, "catalogItemId does not exists in database")
1411
 
1412
    s_items = SimilarItems.query.filter_by(item_id=itemId, catalog_item_id=catalog_item_id).all()
1413
    if not len(s_items):
1414
        s_item = SimilarItems()
1415
        s_item.item_id=itemId
1416
        s_item.catalog_item_id=catalog_item_id
1417
        session.commit()
1418
        return items_for_entity[0]
1419
    else:
1420
        raise InventoryServiceException(101, "Already exists")
1421
 
1422
def delete_similar_item_catalog_id(itemId, catalog_item_id):
1423
    if not itemId or not catalog_item_id:
1424
        raise InventoryServiceException(101, "Bad itemId or catalogItemId in request")
1425
 
1426
    similar_item = SimilarItems.query.filter_by(item_id=itemId, catalog_item_id=catalog_item_id).all()
1427
    if len(similar_item):
1428
        similar_item[0].delete()
1429
    session.commit()
1430
    return True
1431
 
3079 rajveer 1432
def add_product_notification(itemId, email):
1433
    try:
3470 rajveer 1434
        try:
1435
            product_notification = ProductNotification.query.filter_by(item_id=itemId, email=email).one()
1436
        except:
1437
            product_notification = ProductNotification()
1438
            product_notification.email = email
1439
            product_notification.item_id = itemId
3079 rajveer 1440
        product_notification.addedOn = datetime.datetime.now()
1441
        session.commit()
1442
        return True
1443
    except:
1444
        return False
3086 rajveer 1445
 
1446
 
1447
def send_product_notifications():
1448
    product_notifications = ProductNotification.query.all()
1449
    for product_notification in product_notifications:
1450
        item = product_notification.item
5125 mandeep.dh 1451
        availability = __get_item_availability(item, None)
1452
        if item.status == status.ACTIVE and (not item.risky or availability > 0):
3086 rajveer 1453
            __enque_product_notification_email(product_notification.email, __get_product_name(item) , product_notification.addedOn, __get_product_url(item), item.id)
1454
            product_notification.delete()
1455
    session.commit()
1456
    return True
1457
 
1458
def __get_product_name(item):
1459
    product_name = item.brand + " " + item.model_name + " " + item.model_number
1460
    color = item.color
1461
    if color is not None and color != 'NA':
1462
        product_name = product_name + " (" + color + ")"
3201 rajveer 1463
    product_name = product_name.replace("  "," ")
3086 rajveer 1464
    return product_name
1465
 
1466
 
1467
def __get_product_url(item):
1468
    product_url = "http://www.saholic.com/mobile-phones/" + item.brand + "-" + item.model_name + "-" + item.model_number + "-" + str(item.catalog_item_id)
1469
    product_url = product_url.replace("--","-")
1470
    product_url = product_url.replace(" ","")
1471
    return product_url
1472
 
3348 varun.gupt 1473
def get_all_brands_by_category(category_id):
1474
    catm = CategoryManager()
1475
    child_categories = catm.getCategory(category_id).children_category_ids
1476
    brands = session.query(distinct(Item.brand)).filter(Item.category.in_(child_categories)).all()
1477
 
1478
    return [brand[0] for brand in brands]
3086 rajveer 1479
 
4957 phani.kuma 1480
def get_all_brands():
1481
    brands = session.query(distinct(Item.brand)).order_by(Item.brand).all()
1482
 
1483
    return [brand[0] for brand in brands]
1484
 
3086 rajveer 1485
def __enque_product_notification_email(email, product, date, url, itemId):
1486
 
1487
    html = """
1488
        <html>
1489
        <body>
1490
        <div>
1491
        <p>
1492
            Hi,<br /><br />
1493
            The product requested by you on $date is now available on saholic.com.
1494
        </p>
1495
 
1496
        <p>    
1497
        <strong>Product: $product </strong>
1498
        </p>
1499
 
1500
        <p>
1501
        Click the link below to visit the product: 
1502
        <br/>
1503
        $url
1504
        </p>
1505
        <p>
1506
        Regards,<br/>
1507
        Saholic Customer Support Team<br/>
1508
        www.saholic.com<br/>
1509
        Email: help@saholic.com<br/>
1510
        </p>
1511
        </div>
1512
        </body>
1513
        </html>
1514
        """
1515
 
1516
    html = Template(html).substitute(dict(product=product,date=date,url=url))
3079 rajveer 1517
 
3086 rajveer 1518
    try:
1519
        helper_client = HelperClient().get_client()
1520
        helper_client.saveUserEmailForSending(email, "", "Product requested by you is available now.", html, str(itemId), "ProductNotification")
1521
    except Exception as e:
1522
        print e
1523
 
3557 rajveer 1524
def get_all_sources():
1525
    sources = Source.query.all()
1526
    return [to_t_source(source) for source in sources]
3086 rajveer 1527
 
3557 rajveer 1528
def get_item_pricing_by_source(itemId, sourceId):
1529
    item = Item.query.filter_by(id=itemId).first()
1530
    if item is None:
1531
        raise InventoryServiceException(101, "Bad Item")
1532
 
1533
    source = Source.query.filter_by(id=sourceId).first()
1534
    if source is None:
1535
        raise InventoryServiceException(101, "Source not found for sourceId " + str(sourceId))
1536
 
1537
    item_pricing = SourceItemPricing.query.filter_by(source=source, item=item).first()
1538
    if item_pricing is None:
1539
        raise InventoryServiceException(101, "Pricing information not found for sourceId " + str(sourceId))
1540
    return item_pricing
1541
 
1542
def add_source_item_pricing(sourceItemPricing):
1543
    if not sourceItemPricing:
1544
        raise InventoryServiceException(108, "Bad sourceItemPricing in request")
1545
 
1546
    if not sourceItemPricing.sellingPrice:
4326 mandeep.dh 1547
        raise InventoryServiceException(101, "Selling Price is not defined for sourceId " + str(sourceItemPricing.sourceId))
3557 rajveer 1548
 
1549
    sourceId = sourceItemPricing.sourceId
1550
    itemId = sourceItemPricing.itemId
1551
 
1552
    item = Item.query.filter_by(id=itemId).first()
1553
    if item is None:
1554
        raise InventoryServiceException(101, "Bad Item")
1555
 
1556
    source = Source.query.filter_by(id=sourceId).first()
1557
    if source is None:
1558
        raise InventoryServiceException(101, "Source not found for sourceId " + str(sourceId))
1559
 
3564 rajveer 1560
    ds_sourceItemPricing = SourceItemPricing.get_by(source=source, item=item)
3557 rajveer 1561
    if ds_sourceItemPricing is None:
1562
        ds_sourceItemPricing = SourceItemPricing()
1563
        ds_sourceItemPricing.source = source
1564
        ds_sourceItemPricing.item = item
1565
 
1566
    if sourceItemPricing.mrp:
1567
        ds_sourceItemPricing.mrp = sourceItemPricing.mrp
1568
    ds_sourceItemPricing.sellingPrice = sourceItemPricing.sellingPrice
1569
 
1570
    session.commit()
1571
    return
1572
 
1573
def get_all_source_pricing(itemId):
1574
    item = Item.query.filter_by(id=itemId).first()
1575
    if item is None:
1576
        raise InventoryServiceException(101, "Bad Item")
1577
    source_pricing = SourceItemPricing.query.filter_by(item=item).all()
1578
    return source_pricing
1579
 
1580
 
1581
def get_item_for_source(item_id, sourceId):
1582
    item = get_item(item_id)
1583
    if sourceId == -1:
1584
        return item
1585
    try:
1586
        sip = get_item_pricing_by_source(item_id, sourceId)
1587
        item.sellingPrice = sip.sellingPrice
1588
        if sip.mrp:
1589
            item.mrp = sip.mrp
1590
    except:
1591
        print "No source pricing"
1592
    return item
1593
 
3872 chandransh 1594
def search_items(search_terms, offset, limit):
1595
    query = Item.query
1596
 
1597
    query_clause = []
1598
 
1599
    search_terms = ['%' + search_term + '%' for search_term in search_terms]
1600
 
1601
    for search_term in search_terms:
1602
        query_clause.append(Item.brand.like(search_term))
1603
        query_clause.append(Item.model_number.like(search_term))
1604
        query_clause.append(Item.model_name.like(search_term))
1605
 
1606
    query = query.filter(or_(*query_clause))
1607
 
1608
    query = query.order_by(Item.product_group, Item.brand, Item.model_number, Item.model_name).offset(offset)
1609
    if limit:
1610
        query = query.limit(limit)
1611
    items = query.all()
1612
    return items
1613
 
1614
def get_search_result_count(search_terms):
1615
    query = Item.query
1616
 
1617
    query_clause = []
1618
 
1619
    search_terms = ['%' + search_term + '%' for search_term in search_terms]
1620
 
1621
    for search_term in search_terms:
1622
        query_clause.append(Item.brand.like(search_term))
1623
        query_clause.append(Item.model_number.like(search_term))
1624
        query_clause.append(Item.model_name.like(search_term))
1625
 
1626
    query = query.filter(or_(*query_clause))
1627
 
1628
    return query.count()
1629
 
3924 rajveer 1630
def __clear_homepage_cache():
1631
    try:
1632
        # create a password manager
1633
        password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1634
        # Add the username and password.
1635
        configclient = ConfigClient()
4310 rajveer 1636
        ips = configclient.get_property("production_servers_private_ips");
3924 rajveer 1637
        ips = ips.split(" ")
1638
 
1639
        for ip in ips:
4310 rajveer 1640
            try:
1641
                top_level_url = "http://" + ip + ":8080/"
1642
                password_mgr.add_password(None, top_level_url, "saholic", "shop2020")
1643
                handler = urllib2.HTTPBasicAuthHandler(password_mgr)
3924 rajveer 1644
 
4310 rajveer 1645
                opener = urllib2.build_opener(handler)
3924 rajveer 1646
 
4310 rajveer 1647
                # use the opener to fetch a URL
1648
                res = opener.open(top_level_url + "cache-admin/HomePageSnippets?_method=delete")
1649
                print "Successfully cleared home page cache" + res.read()
1650
            except:
1651
                print "Unable to clear home page cache" + res.read()
3924 rajveer 1652
    except:
1653
        print "Unable to clear cache, still should continue with other operations"
4024 chandransh 1654
 
4062 chandransh 1655
def get_pending_orders_inventory(vendor_id=1):
4024 chandransh 1656
    """
1657
    Returns a list of inventory stock for items for which there are pending orders.
1658
    """
4341 rajveer 1659
 
5110 mandeep.dh 1660
    warehouse_ids = [warehouse.id for warehouse in Warehouse.query.filter_by(vendor_id = vendor_id)]
4064 chandransh 1661
    pending_items_inventory = []
1662
    if warehouse_ids:
1663
        pending_items_inventory = session.query(CurrentInventorySnapshot.item_id, func.sum(CurrentInventorySnapshot.availibility), func.sum(CurrentInventorySnapshot.reserved)).filter(CurrentInventorySnapshot.warehouse_id.in_(warehouse_ids)).group_by(CurrentInventorySnapshot.item_id).having(func.sum(CurrentInventorySnapshot.reserved) > 0).all()
4024 chandransh 1664
    return pending_items_inventory
4295 varun.gupt 1665
 
1666
def get_product_notifications(start_datetime):
1667
    '''
1668
    Returns a list of Product Notification objects each representing user requests for notification
1669
    '''
1670
    query = ProductNotification.query
3924 rajveer 1671
 
4295 varun.gupt 1672
    if start_datetime:
1673
        query = query.filter(ProductNotification.addedOn > start_datetime)
1674
 
1675
    notifications = query.order_by(desc('addedOn')).all()
1676
    return notifications
1677
 
1678
def get_product_notification_request_count(start_datetime):
1679
    '''
1680
    Returns list of items and the counts of product notification requests
1681
    '''
1682
    print start_datetime
1683
    query = session.query(ProductNotification, func.count(ProductNotification.email).label('count'))
1684
 
1685
    if start_datetime:
1686
        query = query.filter(ProductNotification.addedOn > start_datetime)
1687
 
1688
    counts = query.group_by(ProductNotification.item_id).order_by(desc('count')).all()
1689
    return counts
1690
 
766 rajveer 1691
def close_session():
1692
    if session.is_active:
1693
        print "session is active. closing it."
1399 rajveer 1694
        session.close()
3376 rajveer 1695
 
1696
def is_alive():
1697
    try:
1698
        session.query(Item.id).limit(1).one()
1699
        return True
1700
    except:
1701
        return False
4332 anupam.sin 1702
 
1703
def add_vendor(vendor):
1704
    if not vendor:
1705
        raise InventoryServiceException(108, "Bad vendor")
1706
    if get_Vendor(vendor.id):
1707
        #vendor is already present.
1708
        raise InventoryServiceException(101, "Vendor already present")
1709
 
1710
    ds_vendor = Vendor()
1711
    ds_vendor.id = vendor.id
1712
    ds_vendor.name = vendor.name
1713
    session.commit()
1714
    return ds_vendor.id
1715
 
1716
def add_warehouse_vendor_mapping(warehouse_id, VendorId):
1717
    return True
1718
 
4649 phani.kuma 1719
def add_authorization_log_for_item(itemId, username, reason):
1720
    if not itemId or not username:
1721
        raise InventoryServiceException(101, "Bad itemId or Invalid username in request")
1722
    authorize_log = AuthorizationLog()
1723
    authorize_log.item_id = itemId
1724
    authorize_log.username = username
1725
    authorize_log.reason = reason
1726
    session.commit()
4797 rajveer 1727
    return True
1728
 
1729
def __send_mail_for_oos_item(item): 
1730
    try:
4930 rajveer 1731
        EmailAttachmentSender.mail('cnc.center@shop2020.in', '5h0p2o2o', ['abhishek.mathur@shop2020.in', 'chaitnaya.vats@shop2020.in'], "Item is out of stock. ID: " + str(item.id)  + " " + str(item.brand) +  " " + str(item.model_name) + " " + str(item.model_number)+ " " + str(item.color), None)
4797 rajveer 1732
    except Exception as e:
1733
        print e
4985 mandeep.dh 1734
 
1735
def mark_missed_inventory_updates_as_processed(itemKey, warehouseId):
1736
    MissedInventoryUpdate.query.filter_by(itemKey = itemKey, warehouseId = warehouseId).delete()
1737
    session.commit()
1738
 
1739
def get_item_keys_to_be_processed(warehouseId):
1740
    return [i.itemKey for i in MissedInventoryUpdate.query.filter_by(warehouseId = warehouseId, isIgnored = 0)]
1741
 
1742
def reset_availability(itemKey, vendorId, quantity, warehouseId):
1743
    vendorItemMapping = VendorItemMapping.get_by(vendor_id = vendorId, item_key = itemKey)
1744
    if vendorItemMapping:
1745
        itemId = vendorItemMapping.item_id
1746
        currentInventorySnapshot = CurrentInventorySnapshot.get_by(item_id = itemId, warehouse_id = warehouseId)
1747
        if currentInventorySnapshot:
1748
            currentInventorySnapshot.availibility = quantity
1749
        else:
1750
            add_inventory(itemId, warehouseId, quantity)
1751
    else:
1752
        raise InventoryServiceException(101, 'VendorMapping not found for: ' + itemKey)
1753
    session.commit()
5047 amit.gupta 1754
 
1755
def __send_mail(subject, message):
5080 amit.gupta 1756
    try:
1757
        thread = threading.Thread(target=partial(mail, from_user, from_pwd, to_addresses, subject, message))
1758
        thread.start()
1759
    except Exception as ex:
1760
        print ex