Subversion Repositories SmartDukaan

Rev

Rev 5185 | Rev 5217 | 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)])
5213 mandeep.dh 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
 
4873 mandeep.dh 435
def __send_mail_for_missing_key(item_key, quantity, warehouse_id):
4985 mandeep.dh 436
    missedInventoryUpdate = MissedInventoryUpdate.get_by(itemKey = item_key, warehouseId = warehouse_id)
437
    # One email per product key mismatch
438
    if not missedInventoryUpdate:
5110 mandeep.dh 439
        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 440
        missedInventoryUpdate = MissedInventoryUpdate()
441
        missedInventoryUpdate.itemKey = item_key
442
        missedInventoryUpdate.quantity = quantity
4985 mandeep.dh 443
        missedInventoryUpdate.isIgnored = 1
4873 mandeep.dh 444
        missedInventoryUpdate.timestamp = datetime.datetime.now()
445
        missedInventoryUpdate.warehouseId = warehouse_id
4748 mandeep.dh 446
        session.commit()
4985 mandeep.dh 447
    else:
448
        missedInventoryUpdate.quantity += quantity
449
        session.commit()
4748 mandeep.dh 450
 
4320 rajveer 451
def add_inventory(itemId, warehouseId, quantity):
452
    current_inventory_snapshot = CurrentInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)
453
    if not current_inventory_snapshot:
454
        current_inventory_snapshot = CurrentInventorySnapshot()
455
        current_inventory_snapshot.item_id = itemId
456
        current_inventory_snapshot.warehouse_id = warehouseId
457
        current_inventory_snapshot.availibility = 0
458
        current_inventory_snapshot.reserved = 0
459
    # added the difference in the current inventory    
460
    current_inventory_snapshot.availibility = current_inventory_snapshot.availibility + quantity
4813 rajveer 461
    session.commit()
4822 mandeep.dh 462
    if current_inventory_snapshot.availibility < 0:
463
        __send_alert_for_negative_availability(get_item(itemId), current_inventory_snapshot.availibility, get_Warehouse(warehouseId))
4813 rajveer 464
    check_risky_item(get_item(itemId)) 
94 ashish 465
 
5110 mandeep.dh 466
def add_bad_inventory(itemId, warehouseId, quantity):
467
    bad_inventory_snapshot = BadInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)
468
    if not bad_inventory_snapshot:
469
        bad_inventory_snapshot = BadInventorySnapshot()
470
        bad_inventory_snapshot.item_id = itemId
471
        bad_inventory_snapshot.warehouse_id = warehouseId
472
        bad_inventory_snapshot.availability = 0
473
    # added the difference in the current inventory    
474
    bad_inventory_snapshot.availability += quantity
475
    session.commit()
476
    if bad_inventory_snapshot.availability < 0:
477
        __send_alert_for_negative_availability(get_item(itemId), bad_inventory_snapshot.availability, get_Warehouse(warehouseId))
478
 
635 rajveer 479
def get_item_inventory_by_item_id(item_id):
480
    inventory = Item.get_by(id=item_id).currentInventory
379 ashish 481
    if not inventory:
482
        raise InventoryServiceException(108, "Some unforeseen error while updating inventory")
483
    return inventory
484
 
103 ashish 485
 
486
def retire_warehouse(warehouse_id):
487
    if not warehouse_id:
122 ashish 488
        raise InventoryServiceException(101, "Bad warehouse id")
103 ashish 489
    warehouse = get_Warehouse(warehouse_id)
490
    if not warehouse:
122 ashish 491
        raise InventoryServiceException(108, "warehouse id not present")
492
    warehouse.status = status.DELETED;
103 ashish 493
    session.commit()
494
 
495
def retire_item(item_id):
496
    if not item_id:
122 ashish 497
        raise InventoryServiceException(101, "bad item id")
635 rajveer 498
    item = get_item(item_id)
103 ashish 499
    if not item:
122 ashish 500
        raise InventoryServiceException(108, "item id not present")
501
    item.status = status.PHASED_OUT
502
    item.retireDate = datetime.datetime.now()
103 ashish 503
    session.commit()
122 ashish 504
 
505
#need to implement threads based solution here
103 ashish 506
def start_item_on(item_id, timestamp):
507
    if not item_id:
122 ashish 508
        raise InventoryServiceException(101, "bad item id")
635 rajveer 509
    item = get_item(item_id)
103 ashish 510
    if not item:
122 ashish 511
        raise InventoryServiceException(108, "item id not present")
103 ashish 512
 
122 ashish 513
    item.status = status.ACTIVE
514
    item.startDate = datetime.datetime.fromtimestamp(to_py_date(timestamp))
515
    add_status_change_log(item, status.ACTIVE)
103 ashish 516
    session.commit()
517
 
122 ashish 518
#need to implement threads here
103 ashish 519
def retire_item_on(item_id, timestamp):
520
    if not item_id:
122 ashish 521
        raise InventoryServiceException(101, "bad item id")
635 rajveer 522
    item = get_item(item_id)
103 ashish 523
    if not item:
122 ashish 524
        raise InventoryServiceException(108, "item id not present")
103 ashish 525
 
122 ashish 526
    item.status = status.PHASED_OUT
527
    item.retireDate = datetime.datetime.fromtimestamp(to_py_date(timestamp))
528
    add_status_change_log(item, status.PHASED_OUT)
103 ashish 529
    session.commit()
530
 
531
def add_status_change_log(item, new_status):
532
    item_change_log = ItemChangeLog()
533
    item_change_log.new_status = new_status
534
    item_change_log.old_status = item.status
535
    item_change_log.timestamp = datetime.datetime.now()
536
    item_change_log.item = item
537
    session.commit()
538
 
539
def change_item_status(item_id, new_status):
540
    if not item_id:
122 ashish 541
        raise InventoryServiceException(101, "bad item id")
635 rajveer 542
    item = get_item(item_id)
103 ashish 543
    if not item:
122 ashish 544
        raise InventoryServiceException(108, "item id not present")
2116 ankur.sing 545
    add_status_change_log(item, new_status)
122 ashish 546
    item.status = new_status
2251 ankur.sing 547
    if item.status == status.PHASED_OUT:
548
        item.status_description = "This item has been phased out"
5047 amit.gupta 549
        __send_mail("Item '{0}' is Phased-Out. Item id is {1}".format(__get_product_name(item), item_id), "")
2251 ankur.sing 550
    elif item.status == status.DELETED:
551
        item.status_description = "This item has been deleted"
3924 rajveer 552
    elif item.status == status.PAUSED:
553
        item.status_description = "This item is currently out of stock"      
554
    elif item.status == status.PAUSED_BY_RISK:
555
        item.status_description = "This item is currently out of stock"
556
        #This will clear cache from tomcat
557
        __clear_homepage_cache()  
2251 ankur.sing 558
    elif item.status == status.ACTIVE:
559
        item.status_description = "This item is active"
5047 amit.gupta 560
        __send_mail("Item '{0}' is Active. Item id is {1}".format(__get_product_name(item), item_id), "")
2251 ankur.sing 561
    elif item.status == status.IN_PROCESS:
562
        item.status_description = "This item is in process"
563
    elif item.status == status.CONTENT_COMPLETE:
564
        item.status_description = "This item is in process"
103 ashish 565
    session.commit()
566
 
567
def get_item_availability_for_warehouse(warehouse_id, item_id):
568
    if not warehouse_id:
122 ashish 569
        raise InventoryServiceException(101, "bad warehouse_id")
103 ashish 570
    if not item_id:
122 ashish 571
        raise InventoryServiceException(101, "bad item_id")
103 ashish 572
 
573
    warehouse = get_Warehouse(warehouse_id)
574
    if not warehouse:
122 ashish 575
        raise InventoryServiceException(108, "warehouse does not exist")
635 rajveer 576
    item = get_item(item_id)
504 rajveer 577
 
103 ashish 578
    if not item:
766 rajveer 579
        raise InventoryServiceException(108, "item does not exist")
122 ashish 580
 
494 rajveer 581
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id)
504 rajveer 582
    query = query.filter_by(item_id = item.id)
494 rajveer 583
    try:
584
        current_inventory_snapshot = query.one()
871 chandransh 585
        return current_inventory_snapshot.availibility - current_inventory_snapshot.reserved
494 rajveer 586
    except:
587
        return 0
588
    """
589
    current_inventory_snapshot = CurrentInventorySnapshot.query.filter(CurrentInventorySnapshot.warehouse_id == warehouse_id, CurrentInventorySnapshot.item_id == item_id).one()
103 ashish 590
    if not current_inventory_snapshot:
591
        return 0
592
    else:
593
        return current_inventory_snapshot.availibility
494 rajveer 594
    """
643 chandransh 595
 
2368 ankur.sing 596
def check_risky_item(item):
2251 ankur.sing 597
    if not item.risky:
598
        return
4813 rajveer 599
    warehouse_ids = None
600
    if item.isWarehousePreferenceSticky :
601
        warehouse_ids = [item.preferredWarehouse]
602
    availability = __get_item_availability(item, warehouse_ids)
2983 chandransh 603
    if availability <= 0:
2251 ankur.sing 604
        if item.status == status.ACTIVE:
2984 rajveer 605
            change_item_status(item.id, status.PAUSED_BY_RISK)
4797 rajveer 606
            __send_mail_for_oos_item(item)
2251 ankur.sing 607
    else:
2984 rajveer 608
        if item.status == status.PAUSED_BY_RISK:
2251 ankur.sing 609
            change_item_status(item.id, status.ACTIVE)
2368 ankur.sing 610
    session.commit()
2251 ankur.sing 611
 
4406 anupam.sin 612
'''
613
This method returns quantity of a particular item across all warehouses whose ids is provided
614
if warehouse_ids is null it checks for inventory in all warehouses.
615
'''
616
def __get_item_availability(item, warehouse_ids):
617
    if warehouse_ids is None:
618
        all_inventory = CurrentInventorySnapshot.query.filter_by(item = item).all()
619
        availability = 0
620
        reserved = 0
621
        for currInv in all_inventory:
622
            availability = availability + currInv.availibility
623
            reserved = reserved + currInv.reserved
624
        return availability - reserved
625
    else:
626
        total_availability = 0
627
        for warehouse_id in warehouse_ids:
628
            try:
629
                current_inventory_snapshot = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item.id).one()
630
                availability = current_inventory_snapshot.availibility - current_inventory_snapshot.reserved
631
            except Exception as e:
632
                print e
633
                availability = 0    
634
            total_availability = total_availability + availability
635
        return total_availability 
4400 rajveer 636
 
637
def __get_item_reserved(item):
638
    all_inventory = CurrentInventorySnapshot.query.filter_by(item = item).all()
639
    reserved = 0
640
    for currInv in all_inventory:
641
        reserved = reserved + currInv.reserved
642
    return reserved
2983 chandransh 643
 
871 chandransh 644
def reserve_item_in_warehouse(item_id, warehouse_id, quantity):    
645
    if not warehouse_id:
646
        raise InventoryServiceException(101, "bad warehouse_id")
2251 ankur.sing 647
    item = get_item(item_id)
648
    if not item:
871 chandransh 649
        raise InventoryServiceException(101, "bad item_id")
650
 
651
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)
652
    try:
653
        current_inventory_snapshot = query.one()
654
    except:
4103 chandransh 655
        current_inventory_snapshot = CurrentInventorySnapshot()
656
        current_inventory_snapshot.warehouse_id = warehouse_id
657
        current_inventory_snapshot.item_id = item_id
658
        current_inventory_snapshot.availibility = 0
659
        current_inventory_snapshot.reserved = 0
660
 
661
    current_inventory_snapshot.reserved = current_inventory_snapshot.reserved + quantity
662
    session.commit()
663
    check_risky_item(item)
664
    return True
871 chandransh 665
 
666
def reduce_reservation_count(item_id, warehouse_id, quantity):
667
    if not warehouse_id:
668
        raise InventoryServiceException(101, "bad warehouse_id")
2251 ankur.sing 669
    item = get_item(item_id)
670
    if not item:
871 chandransh 671
        raise InventoryServiceException(101, "bad item_id")
672
 
673
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)
674
    try:
675
        current_inventory_snapshot = query.one()
676
        current_inventory_snapshot.reserved = current_inventory_snapshot.reserved - quantity
677
        session.commit()
2368 ankur.sing 678
        check_risky_item(item)
4822 mandeep.dh 679
        if current_inventory_snapshot.reserved < 0:
680
            __send_alert_for_negative_reserved(get_item(item_id), current_inventory_snapshot.reserved, get_Warehouse(warehouse_id))
871 chandransh 681
        return True
682
    except:
683
        print "Unexpected error:", sys.exc_info()[0]
684
        return False
685
 
2075 rajveer 686
def mark_item_as_content_complete(entity_id, category, brand, modelName, modelNumber):
2828 rajveer 687
    '''
688
    Get all the items for this entityID and update category, brand, modelName and modelNumber for all.
689
    Update Status for only IN_PROCESS items to CONTENT_COMPLETE
690
    '''
723 chandransh 691
    content_complete_status = status.CONTENT_COMPLETE
2828 rajveer 692
    items = Item.query.filter_by(catalog_item_id=entity_id).all()
723 chandransh 693
    current_timestamp = datetime.datetime.now()
694
    for item in items:
2828 rajveer 695
        if item.status == status.IN_PROCESS:
696
            item.status = content_complete_status
697
            item_change_log = ItemChangeLog()
698
            item_change_log.old_status = item.status
699
            item_change_log.new_status = content_complete_status
700
            item_change_log.timestamp = current_timestamp
701
            item_change_log.item = item
723 chandransh 702
 
4762 phani.kuma 703
        category_object = get_category(category)
704
        if category_object is not None:
705
            item.category = category
706
            item.product_group = category_object.display_name
2075 rajveer 707
        item.brand = brand
2081 rajveer 708
        item.model_name = modelName
709
        item.model_number = modelNumber
723 chandransh 710
        item.updatedOn = current_timestamp
711
    session.commit()
712
    return True
1294 chandransh 713
 
643 chandransh 714
def get_item_availability_for_location(warehouse_loc, item_id):
3355 chandransh 715
    """
716
    Determines the warehouse that should be used to fulfil an order for the given item.
5110 mandeep.dh 717
    Algorithm explained at https://sites.google.com/a/shop2020.in/virtual-w-h-and-inventory/technical-details
718
 
719
    If item's Preferred warehouse is present
720
        If item marked sticky
721
            use GOOD type warehouse at preferred warehouse with maximum availability. 
722
            If not available at any, then use THIRD_PARTY warehouse without billing support 
723
            corresponding to preferred warehouse where available. 
724
            If still not available, then use any GOOD type warehouse with billing support.
725
        else
726
            if availability > 0 at least one of GOOD type warehouses in preferred warehouse
727
                use GOOD type warehouse at preferred warehouse with maximum availability
728
 
729
    If available at no warehouse
730
        use any GOOD type warehouse at default warehouse
731
    else
732
        if available at any warehouse of type GOOD with billing support
733
            use warehouse of type GOOD with billing support with maximum availability
734
        else
735
            if preferred warehouse is present and availability at preferred warehouse's corresponding ThirdPartyWarehouse without billing support > 0
736
                preferred warehouse's corresponding ThirdPartyWarehouse without billing support with maximum availability
737
            else
738
                use ThirdPartyWarehouse without billing support with maximum availability
739
 
3355 chandransh 740
    Returns an ordered list of size 4 with following elements in the given order:
741
    1. Logistics location of the warehouse which was finally picked up to ship the order.
742
    2. Id of the warehouse which was finally picked up.
743
    3. Inventory size in the selected warehouse.
744
    4. Expected delay added by the category manager.
5110 mandeep.dh 745
 
3355 chandransh 746
    Parameters:
747
     - warehouse_loc
748
     - item_id
749
    """
643 chandransh 750
    if warehouse_loc is None:
751
        raise InventoryServiceException(101, "Bad Warehouse Location")
752
    if not item_id:
1416 chandransh 753
        raise InventoryServiceException(101, "Bad Item id")
643 chandransh 754
 
1416 chandransh 755
    item = Item.get_by(id=item_id)
4406 anupam.sin 756
    logisticsLocation = warehouse_loc
5110 mandeep.dh 757
    warehouse_ids = []
758
    goodBillableWarehouses = []
759
    goodBillableWarehousesAtPreferredShippingLocation = []
760
    goodBillableWarehousesAtDefaultShippingLocation = []
761
    nonBillableWarehouses = []
762
    nonBillableWarehousesCorrespondingToPreferredShippingLocation = []
763
    for warehouse in Warehouse.query.all():
764
        if (warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.BAD]):
765
            continue
766
        if warehouse.shippingWarehouseId == item.defaultWarehouse and warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD] and warehouse.billingWarehouseId:
767
            goodBillableWarehousesAtDefaultShippingLocation.append(warehouse.id)
768
        if warehouse.billingWarehouseId is None:
769
            nonBillableWarehouses.append(warehouse.id)
770
        if warehouse.logisticsLocation == logisticsLocation:
771
            warehouse_ids.append(warehouse.id)
772
            if warehouse.billingWarehouseId and warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]:
773
                goodBillableWarehouses.append(warehouse.id)
774
                if item.preferredWarehouse and item.preferredWarehouse == warehouse.shippingWarehouseId:
775
                    goodBillableWarehousesAtPreferredShippingLocation.append(warehouse.id)
776
                    nonBillableWarehousesCorrespondingToPreferredShippingLocation.extend([w.id for w in Warehouse.query.filter_by(logisticsLocation=logisticsLocation, vendor_id = warehouse.vendor_id, billingWarehouseId = None).all()])
777
 
643 chandransh 778
    warehouse_retid = -1
3503 chandransh 779
    total_availability = 0
5110 mandeep.dh 780
 
4406 anupam.sin 781
    '''
782
    If warehouse preference is set and it is sticky then we should fulfil this order from this warehouse only.
783
    But we still need to calculate total availability across warehouses.
784
    '''
5110 mandeep.dh 785
    if (item.isWarehousePreferenceSticky and item.preferredWarehouse):
786
        [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(goodBillableWarehousesAtPreferredShippingLocation, item)
787
        if warehouse_retid == -1:
788
            [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(nonBillableWarehousesCorrespondingToPreferredShippingLocation, item)
789
            if warehouse_retid == -1:
790
                warehouse_retid = goodBillableWarehousesAtPreferredShippingLocation[0]
5147 mandeep.dh 791
                if item.preferredVendor:
792
                    warehousesWithPreferredVendorAtPreferredShippingLocation = Warehouse.query.filter_by(shippingWarehouseId = item.preferredWarehouse, inventoryType = InventoryType._VALUES_TO_NAMES[InventoryType.GOOD], vendor_id = item.preferredVendor).all()
793
                    if warehousesWithPreferredVendorAtPreferredShippingLocation:
794
                        warehouse_retid = warehousesWithPreferredVendorAtPreferredShippingLocation[0].id
5110 mandeep.dh 795
    elif (not item.isWarehousePreferenceSticky and item.preferredWarehouse):
796
        [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(goodBillableWarehousesAtPreferredShippingLocation, item)
797
 
798
    if warehouse_retid == -1:
799
        [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(warehouse_ids, item)
800
        if warehouse_retid == -1:
801
            warehouse_retid = goodBillableWarehousesAtDefaultShippingLocation[0]
5147 mandeep.dh 802
            if item.preferredVendor:
803
                warehousesWithPreferredVendorAtDefaultShippingLocation = Warehouse.query.filter_by(shippingWarehouseId = item.defaultWarehouse, inventoryType = InventoryType._VALUES_TO_NAMES[InventoryType.GOOD], vendor_id = item.preferredVendor).all()
804
                if warehousesWithPreferredVendorAtDefaultShippingLocation:
805
                    warehouse_retid = warehousesWithPreferredVendorAtDefaultShippingLocation[0].id
5110 mandeep.dh 806
        else:
807
            [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(goodBillableWarehouses, item)
808
            if warehouse_retid == -1:
809
                if item.preferredWarehouse:
810
                    [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(nonBillableWarehousesCorrespondingToPreferredShippingLocation, item)
811
                    if warehouse_retid == -1:
812
                        # Availability at Good Billable warehouses is Zero, so can safely use all warehouse_ids to capture the thirdparty virtual/good one
813
                        [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(warehouse_ids, item)
814
 
815
    warehouse = Warehouse.get_by(id=warehouse_retid)
816
    billingWarehouseId = warehouse.billingWarehouseId
4406 anupam.sin 817
 
5110 mandeep.dh 818
    # Fetching billing warehouse of a Good billable warehouse corresponding to the virtual one
819
    if warehouse.billingWarehouseId is None:
820
        for warehouse in Warehouse.query.filter_by(vendor_id = warehouse.vendor_id, inventoryType = InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]).all():
821
            if warehouse.billingWarehouseId:
822
                billingWarehouseId = warehouse.billingWarehouseId
823
                break
4897 rajveer 824
 
5110 mandeep.dh 825
    logisticsLocation = warehouse.logisticsLocation  
826
 
4897 rajveer 827
    expectedDelay = item.expectedDelay 
828
    if expectedDelay is None:
829
        print 'expectedDelay field for this item was Null. Resetting it to 0'
830
        expectedDelay = 0
831
    else:
832
        expectedDelay = int(item.expectedDelay)
833
 
834
    if total_availability <= 0:
835
        expectedDelay = expectedDelay + __get_expected_procurement_delay(item)
4979 rajveer 836
        expectedDelay = expectedDelay + __get_vendor_holiday_delay(item, expectedDelay)
4406 anupam.sin 837
 
5110 mandeep.dh 838
    return [logisticsLocation, int(warehouse_retid), total_availability, expectedDelay, billingWarehouseId]
4406 anupam.sin 839
 
5110 mandeep.dh 840
def __get_warehouse_with_max_availability(warehouse_ids, item):
4406 anupam.sin 841
    warehouse_retid = -1
842
    max_availability = 0
843
    total_availability = 0
844
 
5110 mandeep.dh 845
    if item.currentInventory:
846
        for entry in item.currentInventory:
847
            if entry.warehouse_id in warehouse_ids:
848
                availability = entry.availibility - entry.reserved
849
                if availability > max_availability:
850
                    warehouse_retid = entry.warehouse_id
851
                    max_availability = availability
852
                total_availability += availability
643 chandransh 853
 
5110 mandeep.dh 854
    return [warehouse_retid, total_availability]
2341 chandransh 855
 
4897 rajveer 856
def __get_expected_procurement_delay(item):
4979 rajveer 857
    procurementDelay = 2
4897 rajveer 858
    try:
859
        if item.preferredVendor:
860
            delays = VendorItemProcurementDelay.query.filter_by(vendor_id = item.preferredVendor, item_id = item.id).all()
861
        else:
862
            delays = VendorItemProcurementDelay.query.filter_by(item_id = item.id).all()
863
 
864
        procurementDelay= min([delay.procurementDelay for delay in delays])
865
    except Exception as e:
866
        print e
867
    return procurementDelay
868
 
4979 rajveer 869
def __get_vendor_holiday_delay(item, expectedDelay):
870
    holidayDelay = 0
871
    try:
872
        if item.preferredVendor:
873
            holidays = VendorHolidays.query.filter_by(vendor_id = item.preferredVendor).all()
874
            currentDate = datetime.date.today()
875
            expectedDate = currentDate + datetime.timedelta(days = expectedDelay)
876
            for holiday in holidays:
877
                if holiday.holidayType == HolidayType.WEEKLY and holiday.holidayValue != calendar.SUNDAY:
878
                    if currentDate.weekday() > holiday.holidayValue:
879
                        holidayDate = currentDate + datetime.timedelta(days=holiday.holidayValue-currentDate.weekday(), weeks=1)
880
                    else:
881
                        holidayDate = currentDate + datetime.timedelta(days=holiday.holidayValue-currentDate.weekday())
882
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
883
                        holidayDelay = holidayDelay + 1
884
                elif holiday.holidayType == HolidayType.MONTHLY:
885
                    holidayDate = datetime.date(currentDate.year, currentDate.month, holiday.holidayValue)
886
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
887
                        holidayDelay = holidayDelay + 1    
888
                elif holiday.holidayType == HolidayType.SPECIFIC:
5005 rajveer 889
                    holidayValue = str(holiday.holidayValue)
890
                    holidayDate = datetime.date(int(holidayValue[:4]), int(holidayValue[4:6]), int(holidayValue[6:8]))
4979 rajveer 891
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
892
                        holidayDelay = holidayDelay + 1                
893
    except Exception as e:
894
        print e
895
    return holidayDelay 
122 ashish 896
def get_warehouses_for_item(item_id):
643 chandransh 897
 
122 ashish 898
    if not item_id:
899
        raise InventoryServiceException(101, "bad item_id")
635 rajveer 900
    item = get_item(item_id)
122 ashish 901
 
902
    if not item:
903
        raise InventoryServiceException(101, "bad item")
904
 
483 rajveer 905
    warehouses = item.currentInventory.warehouse
501 rajveer 906
    return warehouses
907
 
2404 chandransh 908
def get_child_categories(category):
909
    cm = CategoryManager()
2621 varun.gupt 910
    cat = cm.getCategory(category)
911
    return cat.children_category_ids if cat else None
2404 chandransh 912
 
626 chandransh 913
def get_best_sellers(start_index, stop_index, category=-1):
2404 chandransh 914
    '''
915
    Returns the Best Sellers between the start and the stop index in the given category
916
    '''
1926 rajveer 917
    query = get_best_sellers_query(category, None)
1098 chandransh 918
    best_sellers = query.all()[start_index:stop_index]
621 chandransh 919
    return get_thrift_item_list(best_sellers)
920
 
2093 chandransh 921
def get_best_sellers_count(category=-1):
2404 chandransh 922
    '''
923
    Returns the number of best sellers in the given category
924
    '''
1926 rajveer 925
    count = get_best_sellers_query(category, None).count()
1120 rajveer 926
    if count is None:
927
        count = 0
766 rajveer 928
    return count
621 chandransh 929
 
1926 rajveer 930
def get_best_sellers_catalog_ids(start_index, stop_index, brand, category=-1):
2404 chandransh 931
    '''
932
    Returns the Best sellers for the given brand and category between the start and the stop index.
933
    Ignores the category if it's passed as -1 and the brand if it's passed as None. 
934
    '''
1926 rajveer 935
    query = get_best_sellers_query(category, brand)
1098 chandransh 936
    best_sellers = query.all()[start_index:stop_index]
621 chandransh 937
    return [item.catalog_item_id for item in best_sellers]
1970 rajveer 938
 
1926 rajveer 939
def get_best_sellers_query(category, brand):
2404 chandransh 940
    '''
941
    Returns the query to be used for getting Best Sellers.
942
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
943
    '''
1098 chandransh 944
    query = Item.query.filter_by(status=status.ACTIVE).filter(Item.bestSellingRank != None)
626 chandransh 945
    if category != -1:
1970 rajveer 946
        all_categories = [category]
947
        child_categories = get_child_categories(category)
948
        if child_categories is not None:
949
            all_categories = all_categories + child_categories 
950
        query = query.filter(Item.category.in_(all_categories))
1926 rajveer 951
    if brand is not None:
952
        query = query.filter_by(brand=brand)
1098 chandransh 953
    query = query.order_by(asc(Item.bestSellingRank))
621 chandransh 954
    return query
609 chandransh 955
 
1098 chandransh 956
def get_best_deals(category=-1):
2404 chandransh 957
    '''
958
    Returns the Best deals in the given category. Ignores the category if it's passed as -1.
959
    '''
960
    query = get_best_deals_query(Item, category, None)
1098 chandransh 961
    items = query.all()
609 chandransh 962
    return get_thrift_item_list(items)
963
 
1098 chandransh 964
def get_best_deals_count(category=-1):
2404 chandransh 965
    '''
966
    Returns the count of best deals in the given category.
967
    Ignores the category if it's -1.
968
    '''
969
    count = get_best_deals_counting_query(func.count(distinct(Item.catalog_item_id)), category, None).scalar()
1120 rajveer 970
    if count is None:
971
        count = 0
766 rajveer 972
    return count
501 rajveer 973
 
1926 rajveer 974
def get_best_deals_catalog_ids(start_index, stop_index, brand, category=-1):
2404 chandransh 975
    '''
976
    Returns the catalog_item_ids of best deal items for the given brand and category.
977
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
978
    '''
979
    query = get_best_deals_query(Item, category, brand)
1098 chandransh 980
    best_deal_items = query.all()[start_index:stop_index]
981
    return [item.catalog_item_id for item in best_deal_items]
982
 
2404 chandransh 983
def get_best_deals_counting_query(obj, category, brand):
984
    '''
985
    Returns the query to be used to select the best deals in the given brand and category.
986
    Ignores the category if it's passed as -1 and the brand if it's passed as None. 
987
    '''
988
    query = session.query(obj).filter_by(status=status.ACTIVE).filter(Item.bestDealValue != None)
626 chandransh 989
    if category != -1:
1970 rajveer 990
        all_categories = [category]
991
        child_categories = get_child_categories(category)
992
        if child_categories is not None:
993
            all_categories = all_categories + child_categories 
994
        query = query.filter(Item.category.in_(all_categories))
1926 rajveer 995
    if brand is not None:
996
        query = query.filter_by(brand=brand)
2404 chandransh 997
    return query
998
 
999
def get_best_deals_query(obj, category, brand):
1000
    '''
1001
    Returns the query to be used to get the best deals in the given category and brand.
1002
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
1003
    '''
1004
    query = get_best_deals_counting_query(obj, category, brand)
1098 chandransh 1005
    query = query.group_by(Item.catalog_item_id).order_by(desc(Item.bestDealValue))
1006
    return query
609 chandransh 1007
 
1098 chandransh 1008
def get_latest_arrivals(limit, category=-1):
2404 chandransh 1009
    '''
1010
    Returns up to limit number of Latest Arrivals in the given category.
1011
    '''
2975 chandransh 1012
    categories = []
1013
    if category != -1:
1014
        categories = [category]
1015
    query = get_latest_arrivals_query(Item, categories, None)
1098 chandransh 1016
    items = query.all()[0:limit]
609 chandransh 1017
    return get_thrift_item_list(items)
598 chandransh 1018
 
1098 chandransh 1019
def get_latest_arrivals_count(limit, category=-1):
2404 chandransh 1020
    '''
1021
    Returns the number of latest arrivals which will be displayed on the website.
3016 chandransh 1022
    To ignore the categories, pass the list as empty. To ignore brand, pass it as null.
2404 chandransh 1023
    '''
2975 chandransh 1024
    categories = []
1025
    if category != -1:
1026
        categories = [category]
1027
    count = get_latest_arrivals_counting_query(func.count(distinct(Item.catalog_item_id)), categories, None).scalar()
1120 rajveer 1028
    if count is None:
1029
        count = 0
1030
    count = min(count, limit)
766 rajveer 1031
    return count
602 chandransh 1032
 
2975 chandransh 1033
def get_latest_arrivals_catalog_ids(start_index, stop_index, brand, categories=[]):
2404 chandransh 1034
    '''
1035
    Returns the catalog_item_ids of the latest arrivals between the start and the stop index
3016 chandransh 1036
    To ignore the categories, pass the list as empty. To ignore brand, pass it as null.
2404 chandransh 1037
    '''
2975 chandransh 1038
    query = get_latest_arrivals_query(Item, categories, brand)
1098 chandransh 1039
    latest_arrivals = query.all()[start_index:stop_index]
1040
    return [item.catalog_item_id for item in latest_arrivals]
1041
 
2975 chandransh 1042
def get_latest_arrivals_counting_query(obj, categories, brand):
2404 chandransh 1043
    '''
1044
    Returns the query to be used to count Latest arrivals.
3016 chandransh 1045
    To ignore the categories, pass the list as empty. To ignore brand, pass it as null.
2404 chandransh 1046
    '''
1047
    query = session.query(obj).filter_by(status=status.ACTIVE)
2975 chandransh 1048
 
1049
    all_categories = []
1050
    for category in categories:
1051
        all_categories.append(category)
1970 rajveer 1052
        child_categories = get_child_categories(category)
2975 chandransh 1053
        if child_categories:
1054
            all_categories = all_categories + child_categories
1055
    if all_categories: 
1970 rajveer 1056
        query = query.filter(Item.category.in_(all_categories))
2975 chandransh 1057
 
1926 rajveer 1058
    if brand is not None:
1059
        query = query.filter_by(brand=brand)
2404 chandransh 1060
    return query
1061
 
2975 chandransh 1062
def get_latest_arrivals_query(obj, categories, brand):
2404 chandransh 1063
    '''
1064
    Returns the query to be used to retrieve Latest Arrivals.
1065
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
1066
    '''
2975 chandransh 1067
    query = get_latest_arrivals_counting_query(obj, categories, brand)
5167 rajveer 1068
    query = query.group_by(Item.catalog_item_id).order_by(desc(Item.startDate)).order_by(Item.catalog_item_id)
1098 chandransh 1069
    return query
609 chandransh 1070
 
1071
def get_thrift_item_list(items):
1098 chandransh 1072
    return [to_t_item(item) for item in items if item != None]
635 rajveer 1073
 
1155 rajveer 1074
def generate_new_entity_id():
1075
    generator =  EntityIDGenerator.query.one()
1076
    id = generator.id + 1
1077
    generator.id = id
1078
    session.commit()
1079
    return id
1080
 
635 rajveer 1081
def put_category_object(object):
1082
    category = Category.get_by(id=1)
1083
    if category is None:
1084
        category = Category()
1085
    category.object = object    
1086
    session.commit()
1087
    return True
1088
 
1089
def get_category_object():
766 rajveer 1090
    object = Category.get_by(id=1).object
1091
    return object
1092
 
4283 anupam.sin 1093
def get_item_pricing(item_id, vendorId):
1341 chandransh 1094
    item = Item.query.filter_by(id=item_id).first()
1095
    if item is None:
1096
        raise InventoryServiceException(101, "Bad Item")
4307 anupam.sin 1097
    '''
1098
    if vendor id is -1 then we calculate an average transfer price to be populated
1099
    at the time of order creation. This will be later updated with actual transfer price
1100
    at the time of billing.
1101
    '''
1102
    if(vendorId == -1):
4543 anupam.sin 1103
        total = 0
4307 anupam.sin 1104
        try:
5133 mandeep.dh 1105
            item_pricings = []
4858 anupam.sin 1106
            if item.preferredWarehouse is not None:
1107
                warehouse = Warehouse.query.filter_by(id=item.preferredWarehouse).first()
5133 mandeep.dh 1108
                item_pricing = VendorItemPricing.query.filter_by(item=item, vendor=warehouse.vendor).first()
1109
                if item_pricing:
1110
                    item_pricings.append(item_pricing)                    
4858 anupam.sin 1111
            else :
1112
                item_pricings = VendorItemPricing.query.filter_by(item=item).all()
4315 anupam.sin 1113
            if item_pricings:
4307 anupam.sin 1114
                for item_pricing in item_pricings:
4543 anupam.sin 1115
                    total += item_pricing.transfer_price
4315 anupam.sin 1116
                avg = total / len(item_pricings)
1117
                item_pricing.transfer_price = avg
4543 anupam.sin 1118
            else:
1119
                item_pricing = VendorItemPricing()
1120
                item_pricing.transfer_price = item.sellingPrice
1121
                vendor = Vendor()
1122
                vendor.id = vendorId
1123
                item_pricing.vendor = vendor
1124
                item_pricing.item = item
1125
 
1126
            return item_pricing
4307 anupam.sin 1127
        except:
1128
            raise InventoryServiceException(101, "Item pricing not found ")
4283 anupam.sin 1129
    vendor = Vendor.get_by(id=vendorId)    
1341 chandransh 1130
    try:
1131
        item_pricing = VendorItemPricing.query.filter_by(vendor=vendor, item=item).one()
1347 chandransh 1132
        return item_pricing
3244 chandransh 1133
    except MultipleResultsFound:
1341 chandransh 1134
        raise InventoryServiceException(110, "Multiple pricing information present for Vendor: " + vendor.name + " and Item: " + str(item_id))
3244 chandransh 1135
    except NoResultFound:
1341 chandransh 1136
        raise InventoryServiceException(111, "Missing pricing information for Vendor: " + vendor.name + " and Item: " + str(item_id))
1137
 
1970 rajveer 1138
def add_category(t_category):
1139
    category = Category.get_by(id=t_category.id)
1140
    if category is None:
1141
        category = Category()
1142
    category.id = t_category.id 
1143
    category.label = t_category.label
1144
    category.description = t_category.description
4762 phani.kuma 1145
    category.display_name = t_category.display_name
1970 rajveer 1146
    category.parent_category_id = t_category.parent_category_id 
1147
    session.commit()
1148
    return True
1149
 
1150
def get_category(id):
1151
    return Category.query.filter_by(id=id).first()
1152
 
1153
def get_all_categories():
1154
    return Category.query.all()
1155
 
1991 ankur.sing 1156
 
1157
def get_all_item_pricing(item_id):
1158
    item = Item.query.filter_by(id=item_id).first()
1159
    if item is None:
1160
        raise InventoryServiceException(101, "Bad Item")
1161
    item_pricing = VendorItemPricing.query.filter_by(item=item).all()
1162
    return item_pricing
1163
 
2116 ankur.sing 1164
def get_item_mappings(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_mappings = VendorItemMapping.query.filter_by(item=item).all()
1169
    return item_mappings
1170
 
1171
def add_vendor_pricing(vendorItemPricing):
1991 ankur.sing 1172
    if not vendorItemPricing:
1173
        raise InventoryServiceException(108, "Bad vendorItemPricing in request")
1174
    vendorId = vendorItemPricing.vendorId
1175
    itemId = vendorItemPricing.itemId
1176
 
1177
    try:
1178
        vendor = Vendor.query.filter_by(id=vendorId).one()
1179
    except:
1180
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
1181
 
1182
    try:
1183
        item = Item.query.filter_by(id=itemId).one()
1184
    except:
5047 amit.gupta 1185
        raise InventoryServiceException(101, "Item not found for itemId " + str(itemId))
1991 ankur.sing 1186
 
2120 ankur.sing 1187
    validate_vendor_prices(to_t_item(item), vendorItemPricing)
2065 ankur.sing 1188
 
1991 ankur.sing 1189
    try:
1190
        ds_vendorItemPricing = VendorItemPricing.query.filter(and_(VendorItemPricing.vendor==vendor, VendorItemPricing.item==item)).one()
1191
    except:
2116 ankur.sing 1192
        ds_vendorItemPricing = VendorItemPricing()
1193
        ds_vendorItemPricing.vendor = vendor
1194
        ds_vendorItemPricing.item = item
1991 ankur.sing 1195
 
5047 amit.gupta 1196
    subject = ""
1197
    message = ""
1991 ankur.sing 1198
    if vendorItemPricing.mop:
1199
        ds_vendorItemPricing.mop = vendorItemPricing.mop
1200
    if vendorItemPricing.dealerPrice:
1201
        ds_vendorItemPricing.dealerPrice = vendorItemPricing.dealerPrice
1202
    if vendorItemPricing.transferPrice:
5047 amit.gupta 1203
        if vendorItemPricing.transferPrice != ds_vendorItemPricing.transfer_price:
1204
            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)
1205
            subject = "Alert:Change in Transfer Price"
2065 ankur.sing 1206
        ds_vendorItemPricing.transfer_price = vendorItemPricing.transferPrice
1991 ankur.sing 1207
 
1208
    session.commit()
5047 amit.gupta 1209
    if subject:
1210
        __send_mail(subject, message)
1991 ankur.sing 1211
    return
1212
 
2358 ankur.sing 1213
def add_vendor_item_mapping(key, vendorItemMapping):
2116 ankur.sing 1214
    if not vendorItemMapping:
1215
        raise InventoryServiceException(108, "Bad vendorItemMapping in request")
1216
    vendorId = vendorItemMapping.vendorId
1217
    itemId = vendorItemMapping.itemId
1218
 
1219
    try:
1220
        vendor = Vendor.query.filter_by(id=vendorId).one()
1221
    except:
1222
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
1223
 
1224
    try:
1225
        item = Item.query.filter_by(id=itemId).one()
1226
    except:
1227
        raise InventoryServiceException(101, "Item not found for vendorId " + str(itemId))
1228
 
1229
    try:
2358 ankur.sing 1230
        ds_vendorItemMapping = VendorItemMapping.query.filter(and_(VendorItemMapping.vendor==vendor, VendorItemMapping.item==item, VendorItemMapping.item_key==key)).one()
2116 ankur.sing 1231
    except:
1232
        ds_vendorItemMapping = VendorItemMapping()
1233
        ds_vendorItemMapping.vendor = vendor
1234
        ds_vendorItemMapping.item = item
1235
    ds_vendorItemMapping.item_key = vendorItemMapping.itemKey
4985 mandeep.dh 1236
 
2116 ankur.sing 1237
    session.commit()
4985 mandeep.dh 1238
 
1239
    # Marking the missed inventory as not ignored as the catalog dashboard user has updated their key
1240
    for missedInventoryUpdate in MissedInventoryUpdate.query.filter_by(itemKey = vendorItemMapping.itemKey).all():
1241
        missedInventoryUpdate.isIgnored = 0
1242
    session.commit()
1243
 
2116 ankur.sing 1244
    return
1245
 
2120 ankur.sing 1246
def validate_item_prices(item):
2129 ankur.sing 1247
    if item.mrp == None or item.sellingPrice == None or item.mrp == "" or item.sellingPrice == "":
1248
        return
1249
    if item.mrp < item.sellingPrice:
2120 ankur.sing 1250
        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))
1251
        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 1252
    return
2120 ankur.sing 1253
 
1254
def validate_vendor_prices(item, vendorPrices):
2129 ankur.sing 1255
    if item.mrp != None and item.mrp != "" and vendorPrices.mop != "" and item.mrp <  vendorPrices.mop:
2120 ankur.sing 1256
        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))
1257
        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)))
1258
    if vendorPrices.mop != "" and vendorPrices.transferPrice != "" and vendorPrices.transferPrice > vendorPrices.mop:
1259
        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))
1260
        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)))
1261
    return
2065 ankur.sing 1262
 
1263
def get_all_vendors():
1264
    return Vendor.query.all()
1265
 
4725 phani.kuma 1266
def check_color_valid(color):
1267
    if color is not None:
1268
        color = color.strip().lower()
1269
        if color != '' and color != 'na' and color != 'blank' and color != '(blank)':
1270
            return True
1271
    return False
1272
 
1273
def check_similar_item(brand, model_number, model_name, color):
2129 ankur.sing 1274
    query = Item.query
2428 ankur.sing 1275
    query = query.filter_by(brand=brand)
1276
    query = query.filter_by(model_number=model_number)
4725 phani.kuma 1277
    query = query.filter_by(model_name=model_name)
1278
    similar_items = query.all()
1279
    item = None
1280
    # Check if a similar item already exists in our database
1281
    for old_item in similar_items:
1282
        if old_item.color != None and old_item.color.strip().lower() == color.strip().lower():
1283
            item = old_item
1284
            break
1285
 
1286
    # 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 1287
    if item is None:
4725 phani.kuma 1288
        for old_item in similar_items:
1289
            if not check_color_valid(old_item.color):
1290
                item = old_item
1291
                break
1292
    i = 0
1293
    color_of_similar_item = None
1294
    # Check if a similar item already exists in our database to be used to get catalog_item_id
1295
    for old_item in similar_items:
1296
        # get a similar item already existing in our database with valid color
1297
        if check_color_valid(old_item.color):
1298
            similar_item = old_item
1299
            color_of_similar_item = similar_item.color
1300
            break
1301
        i = i + 1
1302
        # get a similar item already existing in our database if similar item with valid color is not found
1303
        if i == len(similar_items):
1304
            similar_item = old_item
1305
            color_of_similar_item = similar_item.color
1306
 
1307
    # Check if a similar item that is obtained above is having a valid color
1308
    if check_color_valid(color_of_similar_item):
1309
        # 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.
1310
        # 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.
1311
        if item is None and not check_color_valid(color):
1312
            return similar_item.id
1313
 
1314
    if item is None:
2116 ankur.sing 1315
        return 0
1316
    else:
1317
        return item.id
2286 ankur.sing 1318
 
1319
def change_risky_flag(item_id, risky):
1320
    item = get_item(item_id)
1321
    if not item:
1322
        raise InventoryServiceException(101, "Item missing in our database")
1323
    try:
1324
        log_risky_flag(item_id, risky)
1325
    except:
1326
        print "Not able to log risky flag change"
1327
    item.risky = risky
4295 varun.gupt 1328
    if not risky and item.status == status.PAUSED_BY_RISK:
2368 ankur.sing 1329
        change_item_status(item.id, status.ACTIVE)
2286 ankur.sing 1330
    session.commit()
5047 amit.gupta 1331
    flag = "ON" if risky else "OFF"
1332
    subject = "Risky flag is {0} for Item {1}.".format(flag, __get_product_name(item))
1333
    __send_mail(subject,"")
2286 ankur.sing 1334
 
4957 phani.kuma 1335
def get_items_for_mastersheet(categoryName, brand):
1336
    if not categoryName or not brand:
1337
        raise InventoryServiceException(101, "Invalid category or brand in request")
1338
 
4762 phani.kuma 1339
    categories = ["Handsets", "Tablets", "Laptops"]
4957 phani.kuma 1340
    query = Item.query.filter(Item.status != status.PHASED_OUT)
1341
    if categoryName == "ALL":
1342
        pass
1343
    elif categoryName == "ALL Accessories":
1344
        query = query.filter(~Item.product_group.in_(categories))
1345
    elif categoryName == "ALL Handsets":
1346
        query = query.filter(Item.product_group.in_(categories))
1347
    elif categoryName == "Mobile Accessories":
1348
        child_categories = get_child_categories(10011)
1349
        if child_categories is not None:
1350
            child_categories.append(0)
1351
            query = query.filter(Item.category.in_(child_categories))
1352
    elif categoryName == "Laptop Accessories":
1353
        child_categories = get_child_categories(10070)
1354
        if child_categories is not None:
1355
            child_categories.append(0)
1356
            query = query.filter(Item.category.in_(child_categories))
1357
    else:
1358
        query = query.filter(Item.product_group == categoryName)
1359
 
1360
    if brand == "ALL":
1361
        pass
1362
    else:
1363
        query = query.filter(Item.brand == brand)
2358 ankur.sing 1364
    items = query.all()
1365
    return items
2116 ankur.sing 1366
 
2358 ankur.sing 1367
def get_risky_items():
1368
    items = Item.query.filter_by(risky=True).all()
1369
    return items
3008 rajveer 1370
 
2809 rajveer 1371
def get_similar_items_catalog_ids(start_index, stop_index, itemId):
3008 rajveer 1372
    query = SimilarItems.query.filter_by(item_id=itemId).limit(stop_index-start_index)
1373
    similar_items = query.all()
3289 rajveer 1374
    return_list = []
1375
    for similar_item in similar_items:
1376
        isActive = False
1377
        try:
1378
            all_items = Item.query.filter_by(catalog_item_id=similar_item.catalog_item_id).all()
1379
        except:
1380
            continue
1381
        for item in all_items:
1382
            isActive = isActive or item.status == status.ACTIVE
1383
        if isActive:
1384
            return_list.append(similar_item.catalog_item_id)
1385
    return return_list
4423 phani.kuma 1386
 
1387
def get_all_similar_items_catalog_ids(itemId):
1388
    query = SimilarItems.query.filter_by(item_id=itemId)
1389
    similar_items = query.all()
1390
    return_list = []
1391
    for similar_item in similar_items:
1392
        item_query = Item.query.filter_by(catalog_item_id=similar_item.catalog_item_id).limit(1)
1393
        item = item_query.one()
1394
        return_list.append(item)
2809 rajveer 1395
 
4423 phani.kuma 1396
    return get_thrift_item_list(return_list)
1397
 
1398
def add_similar_item_catalog_id(itemId, catalog_item_id):
1399
    if not itemId or not catalog_item_id:
1400
        raise InventoryServiceException(101, "Bad itemId or catalogItemId in request")
1401
    items_for_entity = get_items_by_catalog_id(catalog_item_id)
1402
    if not len(items_for_entity):
1403
        raise InventoryServiceException(101, "catalogItemId does not exists in database")
1404
 
1405
    s_items = SimilarItems.query.filter_by(item_id=itemId, catalog_item_id=catalog_item_id).all()
1406
    if not len(s_items):
1407
        s_item = SimilarItems()
1408
        s_item.item_id=itemId
1409
        s_item.catalog_item_id=catalog_item_id
1410
        session.commit()
1411
        return items_for_entity[0]
1412
    else:
1413
        raise InventoryServiceException(101, "Already exists")
1414
 
1415
def delete_similar_item_catalog_id(itemId, catalog_item_id):
1416
    if not itemId or not catalog_item_id:
1417
        raise InventoryServiceException(101, "Bad itemId or catalogItemId in request")
1418
 
1419
    similar_item = SimilarItems.query.filter_by(item_id=itemId, catalog_item_id=catalog_item_id).all()
1420
    if len(similar_item):
1421
        similar_item[0].delete()
1422
    session.commit()
1423
    return True
1424
 
3079 rajveer 1425
def add_product_notification(itemId, email):
1426
    try:
3470 rajveer 1427
        try:
1428
            product_notification = ProductNotification.query.filter_by(item_id=itemId, email=email).one()
1429
        except:
1430
            product_notification = ProductNotification()
1431
            product_notification.email = email
1432
            product_notification.item_id = itemId
3079 rajveer 1433
        product_notification.addedOn = datetime.datetime.now()
1434
        session.commit()
1435
        return True
1436
    except:
1437
        return False
3086 rajveer 1438
 
1439
 
1440
def send_product_notifications():
1441
    product_notifications = ProductNotification.query.all()
1442
    for product_notification in product_notifications:
1443
        item = product_notification.item
5125 mandeep.dh 1444
        availability = __get_item_availability(item, None)
1445
        if item.status == status.ACTIVE and (not item.risky or availability > 0):
3086 rajveer 1446
            __enque_product_notification_email(product_notification.email, __get_product_name(item) , product_notification.addedOn, __get_product_url(item), item.id)
1447
            product_notification.delete()
1448
    session.commit()
1449
    return True
1450
 
1451
def __get_product_name(item):
1452
    product_name = item.brand + " " + item.model_name + " " + item.model_number
1453
    color = item.color
1454
    if color is not None and color != 'NA':
1455
        product_name = product_name + " (" + color + ")"
3201 rajveer 1456
    product_name = product_name.replace("  "," ")
3086 rajveer 1457
    return product_name
1458
 
1459
 
1460
def __get_product_url(item):
1461
    product_url = "http://www.saholic.com/mobile-phones/" + item.brand + "-" + item.model_name + "-" + item.model_number + "-" + str(item.catalog_item_id)
1462
    product_url = product_url.replace("--","-")
1463
    product_url = product_url.replace(" ","")
1464
    return product_url
1465
 
3348 varun.gupt 1466
def get_all_brands_by_category(category_id):
1467
    catm = CategoryManager()
1468
    child_categories = catm.getCategory(category_id).children_category_ids
1469
    brands = session.query(distinct(Item.brand)).filter(Item.category.in_(child_categories)).all()
1470
 
1471
    return [brand[0] for brand in brands]
3086 rajveer 1472
 
4957 phani.kuma 1473
def get_all_brands():
1474
    brands = session.query(distinct(Item.brand)).order_by(Item.brand).all()
1475
 
1476
    return [brand[0] for brand in brands]
1477
 
3086 rajveer 1478
def __enque_product_notification_email(email, product, date, url, itemId):
1479
 
1480
    html = """
1481
        <html>
1482
        <body>
1483
        <div>
1484
        <p>
1485
            Hi,<br /><br />
1486
            The product requested by you on $date is now available on saholic.com.
1487
        </p>
1488
 
1489
        <p>    
1490
        <strong>Product: $product </strong>
1491
        </p>
1492
 
1493
        <p>
1494
        Click the link below to visit the product: 
1495
        <br/>
1496
        $url
1497
        </p>
1498
        <p>
1499
        Regards,<br/>
1500
        Saholic Customer Support Team<br/>
1501
        www.saholic.com<br/>
1502
        Email: help@saholic.com<br/>
1503
        </p>
1504
        </div>
1505
        </body>
1506
        </html>
1507
        """
1508
 
1509
    html = Template(html).substitute(dict(product=product,date=date,url=url))
3079 rajveer 1510
 
3086 rajveer 1511
    try:
1512
        helper_client = HelperClient().get_client()
1513
        helper_client.saveUserEmailForSending(email, "", "Product requested by you is available now.", html, str(itemId), "ProductNotification")
1514
    except Exception as e:
1515
        print e
1516
 
3557 rajveer 1517
def get_all_sources():
1518
    sources = Source.query.all()
1519
    return [to_t_source(source) for source in sources]
3086 rajveer 1520
 
3557 rajveer 1521
def get_item_pricing_by_source(itemId, sourceId):
1522
    item = Item.query.filter_by(id=itemId).first()
1523
    if item is None:
1524
        raise InventoryServiceException(101, "Bad Item")
1525
 
1526
    source = Source.query.filter_by(id=sourceId).first()
1527
    if source is None:
1528
        raise InventoryServiceException(101, "Source not found for sourceId " + str(sourceId))
1529
 
1530
    item_pricing = SourceItemPricing.query.filter_by(source=source, item=item).first()
1531
    if item_pricing is None:
1532
        raise InventoryServiceException(101, "Pricing information not found for sourceId " + str(sourceId))
1533
    return item_pricing
1534
 
1535
def add_source_item_pricing(sourceItemPricing):
1536
    if not sourceItemPricing:
1537
        raise InventoryServiceException(108, "Bad sourceItemPricing in request")
1538
 
1539
    if not sourceItemPricing.sellingPrice:
4326 mandeep.dh 1540
        raise InventoryServiceException(101, "Selling Price is not defined for sourceId " + str(sourceItemPricing.sourceId))
3557 rajveer 1541
 
1542
    sourceId = sourceItemPricing.sourceId
1543
    itemId = sourceItemPricing.itemId
1544
 
1545
    item = Item.query.filter_by(id=itemId).first()
1546
    if item is None:
1547
        raise InventoryServiceException(101, "Bad Item")
1548
 
1549
    source = Source.query.filter_by(id=sourceId).first()
1550
    if source is None:
1551
        raise InventoryServiceException(101, "Source not found for sourceId " + str(sourceId))
1552
 
3564 rajveer 1553
    ds_sourceItemPricing = SourceItemPricing.get_by(source=source, item=item)
3557 rajveer 1554
    if ds_sourceItemPricing is None:
1555
        ds_sourceItemPricing = SourceItemPricing()
1556
        ds_sourceItemPricing.source = source
1557
        ds_sourceItemPricing.item = item
1558
 
1559
    if sourceItemPricing.mrp:
1560
        ds_sourceItemPricing.mrp = sourceItemPricing.mrp
1561
    ds_sourceItemPricing.sellingPrice = sourceItemPricing.sellingPrice
1562
 
1563
    session.commit()
1564
    return
1565
 
1566
def get_all_source_pricing(itemId):
1567
    item = Item.query.filter_by(id=itemId).first()
1568
    if item is None:
1569
        raise InventoryServiceException(101, "Bad Item")
1570
    source_pricing = SourceItemPricing.query.filter_by(item=item).all()
1571
    return source_pricing
1572
 
1573
 
1574
def get_item_for_source(item_id, sourceId):
1575
    item = get_item(item_id)
1576
    if sourceId == -1:
1577
        return item
1578
    try:
1579
        sip = get_item_pricing_by_source(item_id, sourceId)
1580
        item.sellingPrice = sip.sellingPrice
1581
        if sip.mrp:
1582
            item.mrp = sip.mrp
1583
    except:
1584
        print "No source pricing"
1585
    return item
1586
 
3872 chandransh 1587
def search_items(search_terms, offset, limit):
1588
    query = Item.query
1589
 
1590
    query_clause = []
1591
 
1592
    search_terms = ['%' + search_term + '%' for search_term in search_terms]
1593
 
1594
    for search_term in search_terms:
1595
        query_clause.append(Item.brand.like(search_term))
1596
        query_clause.append(Item.model_number.like(search_term))
1597
        query_clause.append(Item.model_name.like(search_term))
1598
 
1599
    query = query.filter(or_(*query_clause))
1600
 
1601
    query = query.order_by(Item.product_group, Item.brand, Item.model_number, Item.model_name).offset(offset)
1602
    if limit:
1603
        query = query.limit(limit)
1604
    items = query.all()
1605
    return items
1606
 
1607
def get_search_result_count(search_terms):
1608
    query = Item.query
1609
 
1610
    query_clause = []
1611
 
1612
    search_terms = ['%' + search_term + '%' for search_term in search_terms]
1613
 
1614
    for search_term in search_terms:
1615
        query_clause.append(Item.brand.like(search_term))
1616
        query_clause.append(Item.model_number.like(search_term))
1617
        query_clause.append(Item.model_name.like(search_term))
1618
 
1619
    query = query.filter(or_(*query_clause))
1620
 
1621
    return query.count()
1622
 
3924 rajveer 1623
def __clear_homepage_cache():
1624
    try:
1625
        # create a password manager
1626
        password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1627
        # Add the username and password.
1628
        configclient = ConfigClient()
4310 rajveer 1629
        ips = configclient.get_property("production_servers_private_ips");
3924 rajveer 1630
        ips = ips.split(" ")
1631
 
1632
        for ip in ips:
4310 rajveer 1633
            try:
1634
                top_level_url = "http://" + ip + ":8080/"
1635
                password_mgr.add_password(None, top_level_url, "saholic", "shop2020")
1636
                handler = urllib2.HTTPBasicAuthHandler(password_mgr)
3924 rajveer 1637
 
4310 rajveer 1638
                opener = urllib2.build_opener(handler)
3924 rajveer 1639
 
4310 rajveer 1640
                # use the opener to fetch a URL
1641
                res = opener.open(top_level_url + "cache-admin/HomePageSnippets?_method=delete")
1642
                print "Successfully cleared home page cache" + res.read()
1643
            except:
1644
                print "Unable to clear home page cache" + res.read()
3924 rajveer 1645
    except:
1646
        print "Unable to clear cache, still should continue with other operations"
4024 chandransh 1647
 
4062 chandransh 1648
def get_pending_orders_inventory(vendor_id=1):
4024 chandransh 1649
    """
1650
    Returns a list of inventory stock for items for which there are pending orders.
1651
    """
4341 rajveer 1652
 
5110 mandeep.dh 1653
    warehouse_ids = [warehouse.id for warehouse in Warehouse.query.filter_by(vendor_id = vendor_id)]
4064 chandransh 1654
    pending_items_inventory = []
1655
    if warehouse_ids:
1656
        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 1657
    return pending_items_inventory
4295 varun.gupt 1658
 
1659
def get_product_notifications(start_datetime):
1660
    '''
1661
    Returns a list of Product Notification objects each representing user requests for notification
1662
    '''
1663
    query = ProductNotification.query
3924 rajveer 1664
 
4295 varun.gupt 1665
    if start_datetime:
1666
        query = query.filter(ProductNotification.addedOn > start_datetime)
1667
 
1668
    notifications = query.order_by(desc('addedOn')).all()
1669
    return notifications
1670
 
1671
def get_product_notification_request_count(start_datetime):
1672
    '''
1673
    Returns list of items and the counts of product notification requests
1674
    '''
1675
    print start_datetime
1676
    query = session.query(ProductNotification, func.count(ProductNotification.email).label('count'))
1677
 
1678
    if start_datetime:
1679
        query = query.filter(ProductNotification.addedOn > start_datetime)
1680
 
1681
    counts = query.group_by(ProductNotification.item_id).order_by(desc('count')).all()
1682
    return counts
1683
 
766 rajveer 1684
def close_session():
1685
    if session.is_active:
1686
        print "session is active. closing it."
1399 rajveer 1687
        session.close()
3376 rajveer 1688
 
1689
def is_alive():
1690
    try:
1691
        session.query(Item.id).limit(1).one()
1692
        return True
1693
    except:
1694
        return False
4332 anupam.sin 1695
 
1696
def add_vendor(vendor):
1697
    if not vendor:
1698
        raise InventoryServiceException(108, "Bad vendor")
1699
    if get_Vendor(vendor.id):
1700
        #vendor is already present.
1701
        raise InventoryServiceException(101, "Vendor already present")
1702
 
1703
    ds_vendor = Vendor()
1704
    ds_vendor.id = vendor.id
1705
    ds_vendor.name = vendor.name
1706
    session.commit()
1707
    return ds_vendor.id
1708
 
1709
def add_warehouse_vendor_mapping(warehouse_id, VendorId):
1710
    return True
1711
 
4649 phani.kuma 1712
def add_authorization_log_for_item(itemId, username, reason):
1713
    if not itemId or not username:
1714
        raise InventoryServiceException(101, "Bad itemId or Invalid username in request")
1715
    authorize_log = AuthorizationLog()
1716
    authorize_log.item_id = itemId
1717
    authorize_log.username = username
1718
    authorize_log.reason = reason
1719
    session.commit()
4797 rajveer 1720
    return True
1721
 
1722
def __send_mail_for_oos_item(item): 
1723
    try:
4930 rajveer 1724
        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 1725
    except Exception as e:
1726
        print e
4985 mandeep.dh 1727
 
1728
def mark_missed_inventory_updates_as_processed(itemKey, warehouseId):
1729
    MissedInventoryUpdate.query.filter_by(itemKey = itemKey, warehouseId = warehouseId).delete()
1730
    session.commit()
1731
 
1732
def get_item_keys_to_be_processed(warehouseId):
1733
    return [i.itemKey for i in MissedInventoryUpdate.query.filter_by(warehouseId = warehouseId, isIgnored = 0)]
1734
 
1735
def reset_availability(itemKey, vendorId, quantity, warehouseId):
1736
    vendorItemMapping = VendorItemMapping.get_by(vendor_id = vendorId, item_key = itemKey)
1737
    if vendorItemMapping:
1738
        itemId = vendorItemMapping.item_id
1739
        currentInventorySnapshot = CurrentInventorySnapshot.get_by(item_id = itemId, warehouse_id = warehouseId)
1740
        if currentInventorySnapshot:
1741
            currentInventorySnapshot.availibility = quantity
1742
        else:
1743
            add_inventory(itemId, warehouseId, quantity)
1744
    else:
1745
        raise InventoryServiceException(101, 'VendorMapping not found for: ' + itemKey)
1746
    session.commit()
5047 amit.gupta 1747
 
1748
def __send_mail(subject, message):
5080 amit.gupta 1749
    try:
1750
        thread = threading.Thread(target=partial(mail, from_user, from_pwd, to_addresses, subject, message))
1751
        thread.start()
1752
    except Exception as ex:
1753
        print ex    
5185 mandeep.dh 1754
 
1755
def get_shipping_locations():
1756
    shippingLocationIds = {}
1757
    warehouses = Warehouse.query.all()
1758
    for warehouse in warehouses:
1759
        if warehouse.shippingWarehouseId:
1760
            shippingLocationIds[warehouse.shippingWarehouseId] = 1
1761
 
1762
    shippingLocations = []
1763
    for shippingLocationId in shippingLocationIds:
1764
        shippingLocations.append(get_Warehouse(shippingLocationId))
1765
 
1766
    return shippingLocations