Subversion Repositories SmartDukaan

Rev

Rev 5124 | Rev 5133 | 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:
4858 anupam.sin 1112
            if item.preferredWarehouse is not None:
1113
                warehouse = Warehouse.query.filter_by(id=item.preferredWarehouse).first()
1114
                vendors = warehouse.vendors
1115
                item_pricings = []
1116
                for vendor in vendors :
1117
                    item_pricing = VendorItemPricing.query.filter_by(item=item, vendor=vendor).first()
1118
                    if item_pricing :
1119
                        item_pricings.append(item_pricing)
1120
            else :
1121
                item_pricings = VendorItemPricing.query.filter_by(item=item).all()
4315 anupam.sin 1122
            if item_pricings:
4307 anupam.sin 1123
                for item_pricing in item_pricings:
4543 anupam.sin 1124
                    total += item_pricing.transfer_price
4315 anupam.sin 1125
                avg = total / len(item_pricings)
1126
                item_pricing.transfer_price = avg
4543 anupam.sin 1127
            else:
1128
                item_pricing = VendorItemPricing()
1129
                item_pricing.transfer_price = item.sellingPrice
1130
                vendor = Vendor()
1131
                vendor.id = vendorId
1132
                item_pricing.vendor = vendor
1133
                item_pricing.item = item
1134
 
1135
            return item_pricing
4307 anupam.sin 1136
        except:
1137
            raise InventoryServiceException(101, "Item pricing not found ")
4283 anupam.sin 1138
    vendor = Vendor.get_by(id=vendorId)    
1341 chandransh 1139
    try:
1140
        item_pricing = VendorItemPricing.query.filter_by(vendor=vendor, item=item).one()
1347 chandransh 1141
        return item_pricing
3244 chandransh 1142
    except MultipleResultsFound:
1341 chandransh 1143
        raise InventoryServiceException(110, "Multiple pricing information present for Vendor: " + vendor.name + " and Item: " + str(item_id))
3244 chandransh 1144
    except NoResultFound:
1341 chandransh 1145
        raise InventoryServiceException(111, "Missing pricing information for Vendor: " + vendor.name + " and Item: " + str(item_id))
1146
 
1970 rajveer 1147
def add_category(t_category):
1148
    category = Category.get_by(id=t_category.id)
1149
    if category is None:
1150
        category = Category()
1151
    category.id = t_category.id 
1152
    category.label = t_category.label
1153
    category.description = t_category.description
4762 phani.kuma 1154
    category.display_name = t_category.display_name
1970 rajveer 1155
    category.parent_category_id = t_category.parent_category_id 
1156
    session.commit()
1157
    return True
1158
 
1159
def get_category(id):
1160
    return Category.query.filter_by(id=id).first()
1161
 
1162
def get_all_categories():
1163
    return Category.query.all()
1164
 
1991 ankur.sing 1165
 
1166
def get_all_item_pricing(item_id):
1167
    item = Item.query.filter_by(id=item_id).first()
1168
    if item is None:
1169
        raise InventoryServiceException(101, "Bad Item")
1170
    item_pricing = VendorItemPricing.query.filter_by(item=item).all()
1171
    return item_pricing
1172
 
2116 ankur.sing 1173
def get_item_mappings(item_id):
1174
    item = Item.query.filter_by(id=item_id).first()
1175
    if item is None:
1176
        raise InventoryServiceException(101, "Bad Item")
1177
    item_mappings = VendorItemMapping.query.filter_by(item=item).all()
1178
    return item_mappings
1179
 
1180
def add_vendor_pricing(vendorItemPricing):
1991 ankur.sing 1181
    if not vendorItemPricing:
1182
        raise InventoryServiceException(108, "Bad vendorItemPricing in request")
1183
    vendorId = vendorItemPricing.vendorId
1184
    itemId = vendorItemPricing.itemId
1185
 
1186
    try:
1187
        vendor = Vendor.query.filter_by(id=vendorId).one()
1188
    except:
1189
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
1190
 
1191
    try:
1192
        item = Item.query.filter_by(id=itemId).one()
1193
    except:
5047 amit.gupta 1194
        raise InventoryServiceException(101, "Item not found for itemId " + str(itemId))
1991 ankur.sing 1195
 
2120 ankur.sing 1196
    validate_vendor_prices(to_t_item(item), vendorItemPricing)
2065 ankur.sing 1197
 
1991 ankur.sing 1198
    try:
1199
        ds_vendorItemPricing = VendorItemPricing.query.filter(and_(VendorItemPricing.vendor==vendor, VendorItemPricing.item==item)).one()
1200
    except:
2116 ankur.sing 1201
        ds_vendorItemPricing = VendorItemPricing()
1202
        ds_vendorItemPricing.vendor = vendor
1203
        ds_vendorItemPricing.item = item
1991 ankur.sing 1204
 
5047 amit.gupta 1205
    subject = ""
1206
    message = ""
1991 ankur.sing 1207
    if vendorItemPricing.mop:
1208
        ds_vendorItemPricing.mop = vendorItemPricing.mop
1209
    if vendorItemPricing.dealerPrice:
1210
        ds_vendorItemPricing.dealerPrice = vendorItemPricing.dealerPrice
1211
    if vendorItemPricing.transferPrice:
5047 amit.gupta 1212
        if vendorItemPricing.transferPrice != ds_vendorItemPricing.transfer_price:
1213
            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)
1214
            subject = "Alert:Change in Transfer Price"
2065 ankur.sing 1215
        ds_vendorItemPricing.transfer_price = vendorItemPricing.transferPrice
1991 ankur.sing 1216
 
1217
    session.commit()
5047 amit.gupta 1218
    if subject:
1219
        __send_mail(subject, message)
1991 ankur.sing 1220
    return
1221
 
2358 ankur.sing 1222
def add_vendor_item_mapping(key, vendorItemMapping):
2116 ankur.sing 1223
    if not vendorItemMapping:
1224
        raise InventoryServiceException(108, "Bad vendorItemMapping in request")
1225
    vendorId = vendorItemMapping.vendorId
1226
    itemId = vendorItemMapping.itemId
1227
 
1228
    try:
1229
        vendor = Vendor.query.filter_by(id=vendorId).one()
1230
    except:
1231
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
1232
 
1233
    try:
1234
        item = Item.query.filter_by(id=itemId).one()
1235
    except:
1236
        raise InventoryServiceException(101, "Item not found for vendorId " + str(itemId))
1237
 
1238
    try:
2358 ankur.sing 1239
        ds_vendorItemMapping = VendorItemMapping.query.filter(and_(VendorItemMapping.vendor==vendor, VendorItemMapping.item==item, VendorItemMapping.item_key==key)).one()
2116 ankur.sing 1240
    except:
1241
        ds_vendorItemMapping = VendorItemMapping()
1242
        ds_vendorItemMapping.vendor = vendor
1243
        ds_vendorItemMapping.item = item
1244
    ds_vendorItemMapping.item_key = vendorItemMapping.itemKey
4985 mandeep.dh 1245
 
2116 ankur.sing 1246
    session.commit()
4985 mandeep.dh 1247
 
1248
    # Marking the missed inventory as not ignored as the catalog dashboard user has updated their key
1249
    for missedInventoryUpdate in MissedInventoryUpdate.query.filter_by(itemKey = vendorItemMapping.itemKey).all():
1250
        missedInventoryUpdate.isIgnored = 0
1251
    session.commit()
1252
 
2116 ankur.sing 1253
    return
1254
 
2120 ankur.sing 1255
def validate_item_prices(item):
2129 ankur.sing 1256
    if item.mrp == None or item.sellingPrice == None or item.mrp == "" or item.sellingPrice == "":
1257
        return
1258
    if item.mrp < item.sellingPrice:
2120 ankur.sing 1259
        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))
1260
        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 1261
    return
2120 ankur.sing 1262
 
1263
def validate_vendor_prices(item, vendorPrices):
2129 ankur.sing 1264
    if item.mrp != None and item.mrp != "" and vendorPrices.mop != "" and item.mrp <  vendorPrices.mop:
2120 ankur.sing 1265
        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))
1266
        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)))
1267
    if vendorPrices.mop != "" and vendorPrices.transferPrice != "" and vendorPrices.transferPrice > vendorPrices.mop:
1268
        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))
1269
        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)))
1270
    return
2065 ankur.sing 1271
 
1272
def get_all_vendors():
1273
    return Vendor.query.all()
1274
 
4725 phani.kuma 1275
def check_color_valid(color):
1276
    if color is not None:
1277
        color = color.strip().lower()
1278
        if color != '' and color != 'na' and color != 'blank' and color != '(blank)':
1279
            return True
1280
    return False
1281
 
1282
def check_similar_item(brand, model_number, model_name, color):
2129 ankur.sing 1283
    query = Item.query
2428 ankur.sing 1284
    query = query.filter_by(brand=brand)
1285
    query = query.filter_by(model_number=model_number)
4725 phani.kuma 1286
    query = query.filter_by(model_name=model_name)
1287
    similar_items = query.all()
1288
    item = None
1289
    # Check if a similar item already exists in our database
1290
    for old_item in similar_items:
1291
        if old_item.color != None and old_item.color.strip().lower() == color.strip().lower():
1292
            item = old_item
1293
            break
1294
 
1295
    # 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 1296
    if item is None:
4725 phani.kuma 1297
        for old_item in similar_items:
1298
            if not check_color_valid(old_item.color):
1299
                item = old_item
1300
                break
1301
    i = 0
1302
    color_of_similar_item = None
1303
    # Check if a similar item already exists in our database to be used to get catalog_item_id
1304
    for old_item in similar_items:
1305
        # get a similar item already existing in our database with valid color
1306
        if check_color_valid(old_item.color):
1307
            similar_item = old_item
1308
            color_of_similar_item = similar_item.color
1309
            break
1310
        i = i + 1
1311
        # get a similar item already existing in our database if similar item with valid color is not found
1312
        if i == len(similar_items):
1313
            similar_item = old_item
1314
            color_of_similar_item = similar_item.color
1315
 
1316
    # Check if a similar item that is obtained above is having a valid color
1317
    if check_color_valid(color_of_similar_item):
1318
        # 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.
1319
        # 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.
1320
        if item is None and not check_color_valid(color):
1321
            return similar_item.id
1322
 
1323
    if item is None:
2116 ankur.sing 1324
        return 0
1325
    else:
1326
        return item.id
2286 ankur.sing 1327
 
1328
def change_risky_flag(item_id, risky):
1329
    item = get_item(item_id)
1330
    if not item:
1331
        raise InventoryServiceException(101, "Item missing in our database")
1332
    try:
1333
        log_risky_flag(item_id, risky)
1334
    except:
1335
        print "Not able to log risky flag change"
1336
    item.risky = risky
4295 varun.gupt 1337
    if not risky and item.status == status.PAUSED_BY_RISK:
2368 ankur.sing 1338
        change_item_status(item.id, status.ACTIVE)
2286 ankur.sing 1339
    session.commit()
5047 amit.gupta 1340
    flag = "ON" if risky else "OFF"
1341
    subject = "Risky flag is {0} for Item {1}.".format(flag, __get_product_name(item))
1342
    __send_mail(subject,"")
2286 ankur.sing 1343
 
4957 phani.kuma 1344
def get_items_for_mastersheet(categoryName, brand):
1345
    if not categoryName or not brand:
1346
        raise InventoryServiceException(101, "Invalid category or brand in request")
1347
 
4762 phani.kuma 1348
    categories = ["Handsets", "Tablets", "Laptops"]
4957 phani.kuma 1349
    query = Item.query.filter(Item.status != status.PHASED_OUT)
1350
    if categoryName == "ALL":
1351
        pass
1352
    elif categoryName == "ALL Accessories":
1353
        query = query.filter(~Item.product_group.in_(categories))
1354
    elif categoryName == "ALL Handsets":
1355
        query = query.filter(Item.product_group.in_(categories))
1356
    elif categoryName == "Mobile Accessories":
1357
        child_categories = get_child_categories(10011)
1358
        if child_categories is not None:
1359
            child_categories.append(0)
1360
            query = query.filter(Item.category.in_(child_categories))
1361
    elif categoryName == "Laptop Accessories":
1362
        child_categories = get_child_categories(10070)
1363
        if child_categories is not None:
1364
            child_categories.append(0)
1365
            query = query.filter(Item.category.in_(child_categories))
1366
    else:
1367
        query = query.filter(Item.product_group == categoryName)
1368
 
1369
    if brand == "ALL":
1370
        pass
1371
    else:
1372
        query = query.filter(Item.brand == brand)
2358 ankur.sing 1373
    items = query.all()
1374
    return items
2116 ankur.sing 1375
 
2358 ankur.sing 1376
def get_risky_items():
1377
    items = Item.query.filter_by(risky=True).all()
1378
    return items
3008 rajveer 1379
 
2809 rajveer 1380
def get_similar_items_catalog_ids(start_index, stop_index, itemId):
3008 rajveer 1381
    query = SimilarItems.query.filter_by(item_id=itemId).limit(stop_index-start_index)
1382
    similar_items = query.all()
3289 rajveer 1383
    return_list = []
1384
    for similar_item in similar_items:
1385
        isActive = False
1386
        try:
1387
            all_items = Item.query.filter_by(catalog_item_id=similar_item.catalog_item_id).all()
1388
        except:
1389
            continue
1390
        for item in all_items:
1391
            isActive = isActive or item.status == status.ACTIVE
1392
        if isActive:
1393
            return_list.append(similar_item.catalog_item_id)
1394
    return return_list
4423 phani.kuma 1395
 
1396
def get_all_similar_items_catalog_ids(itemId):
1397
    query = SimilarItems.query.filter_by(item_id=itemId)
1398
    similar_items = query.all()
1399
    return_list = []
1400
    for similar_item in similar_items:
1401
        item_query = Item.query.filter_by(catalog_item_id=similar_item.catalog_item_id).limit(1)
1402
        item = item_query.one()
1403
        return_list.append(item)
2809 rajveer 1404
 
4423 phani.kuma 1405
    return get_thrift_item_list(return_list)
1406
 
1407
def add_similar_item_catalog_id(itemId, catalog_item_id):
1408
    if not itemId or not catalog_item_id:
1409
        raise InventoryServiceException(101, "Bad itemId or catalogItemId in request")
1410
    items_for_entity = get_items_by_catalog_id(catalog_item_id)
1411
    if not len(items_for_entity):
1412
        raise InventoryServiceException(101, "catalogItemId does not exists in database")
1413
 
1414
    s_items = SimilarItems.query.filter_by(item_id=itemId, catalog_item_id=catalog_item_id).all()
1415
    if not len(s_items):
1416
        s_item = SimilarItems()
1417
        s_item.item_id=itemId
1418
        s_item.catalog_item_id=catalog_item_id
1419
        session.commit()
1420
        return items_for_entity[0]
1421
    else:
1422
        raise InventoryServiceException(101, "Already exists")
1423
 
1424
def delete_similar_item_catalog_id(itemId, catalog_item_id):
1425
    if not itemId or not catalog_item_id:
1426
        raise InventoryServiceException(101, "Bad itemId or catalogItemId in request")
1427
 
1428
    similar_item = SimilarItems.query.filter_by(item_id=itemId, catalog_item_id=catalog_item_id).all()
1429
    if len(similar_item):
1430
        similar_item[0].delete()
1431
    session.commit()
1432
    return True
1433
 
3079 rajveer 1434
def add_product_notification(itemId, email):
1435
    try:
3470 rajveer 1436
        try:
1437
            product_notification = ProductNotification.query.filter_by(item_id=itemId, email=email).one()
1438
        except:
1439
            product_notification = ProductNotification()
1440
            product_notification.email = email
1441
            product_notification.item_id = itemId
3079 rajveer 1442
        product_notification.addedOn = datetime.datetime.now()
1443
        session.commit()
1444
        return True
1445
    except:
1446
        return False
3086 rajveer 1447
 
1448
 
1449
def send_product_notifications():
1450
    product_notifications = ProductNotification.query.all()
1451
    for product_notification in product_notifications:
1452
        item = product_notification.item
5125 mandeep.dh 1453
        availability = __get_item_availability(item, None)
1454
        if item.status == status.ACTIVE and (not item.risky or availability > 0):
3086 rajveer 1455
            __enque_product_notification_email(product_notification.email, __get_product_name(item) , product_notification.addedOn, __get_product_url(item), item.id)
1456
            product_notification.delete()
1457
    session.commit()
1458
    return True
1459
 
1460
def __get_product_name(item):
1461
    product_name = item.brand + " " + item.model_name + " " + item.model_number
1462
    color = item.color
1463
    if color is not None and color != 'NA':
1464
        product_name = product_name + " (" + color + ")"
3201 rajveer 1465
    product_name = product_name.replace("  "," ")
3086 rajveer 1466
    return product_name
1467
 
1468
 
1469
def __get_product_url(item):
1470
    product_url = "http://www.saholic.com/mobile-phones/" + item.brand + "-" + item.model_name + "-" + item.model_number + "-" + str(item.catalog_item_id)
1471
    product_url = product_url.replace("--","-")
1472
    product_url = product_url.replace(" ","")
1473
    return product_url
1474
 
3348 varun.gupt 1475
def get_all_brands_by_category(category_id):
1476
    catm = CategoryManager()
1477
    child_categories = catm.getCategory(category_id).children_category_ids
1478
    brands = session.query(distinct(Item.brand)).filter(Item.category.in_(child_categories)).all()
1479
 
1480
    return [brand[0] for brand in brands]
3086 rajveer 1481
 
4957 phani.kuma 1482
def get_all_brands():
1483
    brands = session.query(distinct(Item.brand)).order_by(Item.brand).all()
1484
 
1485
    return [brand[0] for brand in brands]
1486
 
3086 rajveer 1487
def __enque_product_notification_email(email, product, date, url, itemId):
1488
 
1489
    html = """
1490
        <html>
1491
        <body>
1492
        <div>
1493
        <p>
1494
            Hi,<br /><br />
1495
            The product requested by you on $date is now available on saholic.com.
1496
        </p>
1497
 
1498
        <p>    
1499
        <strong>Product: $product </strong>
1500
        </p>
1501
 
1502
        <p>
1503
        Click the link below to visit the product: 
1504
        <br/>
1505
        $url
1506
        </p>
1507
        <p>
1508
        Regards,<br/>
1509
        Saholic Customer Support Team<br/>
1510
        www.saholic.com<br/>
1511
        Email: help@saholic.com<br/>
1512
        </p>
1513
        </div>
1514
        </body>
1515
        </html>
1516
        """
1517
 
1518
    html = Template(html).substitute(dict(product=product,date=date,url=url))
3079 rajveer 1519
 
3086 rajveer 1520
    try:
1521
        helper_client = HelperClient().get_client()
1522
        helper_client.saveUserEmailForSending(email, "", "Product requested by you is available now.", html, str(itemId), "ProductNotification")
1523
    except Exception as e:
1524
        print e
1525
 
3557 rajveer 1526
def get_all_sources():
1527
    sources = Source.query.all()
1528
    return [to_t_source(source) for source in sources]
3086 rajveer 1529
 
3557 rajveer 1530
def get_item_pricing_by_source(itemId, sourceId):
1531
    item = Item.query.filter_by(id=itemId).first()
1532
    if item is None:
1533
        raise InventoryServiceException(101, "Bad Item")
1534
 
1535
    source = Source.query.filter_by(id=sourceId).first()
1536
    if source is None:
1537
        raise InventoryServiceException(101, "Source not found for sourceId " + str(sourceId))
1538
 
1539
    item_pricing = SourceItemPricing.query.filter_by(source=source, item=item).first()
1540
    if item_pricing is None:
1541
        raise InventoryServiceException(101, "Pricing information not found for sourceId " + str(sourceId))
1542
    return item_pricing
1543
 
1544
def add_source_item_pricing(sourceItemPricing):
1545
    if not sourceItemPricing:
1546
        raise InventoryServiceException(108, "Bad sourceItemPricing in request")
1547
 
1548
    if not sourceItemPricing.sellingPrice:
4326 mandeep.dh 1549
        raise InventoryServiceException(101, "Selling Price is not defined for sourceId " + str(sourceItemPricing.sourceId))
3557 rajveer 1550
 
1551
    sourceId = sourceItemPricing.sourceId
1552
    itemId = sourceItemPricing.itemId
1553
 
1554
    item = Item.query.filter_by(id=itemId).first()
1555
    if item is None:
1556
        raise InventoryServiceException(101, "Bad Item")
1557
 
1558
    source = Source.query.filter_by(id=sourceId).first()
1559
    if source is None:
1560
        raise InventoryServiceException(101, "Source not found for sourceId " + str(sourceId))
1561
 
3564 rajveer 1562
    ds_sourceItemPricing = SourceItemPricing.get_by(source=source, item=item)
3557 rajveer 1563
    if ds_sourceItemPricing is None:
1564
        ds_sourceItemPricing = SourceItemPricing()
1565
        ds_sourceItemPricing.source = source
1566
        ds_sourceItemPricing.item = item
1567
 
1568
    if sourceItemPricing.mrp:
1569
        ds_sourceItemPricing.mrp = sourceItemPricing.mrp
1570
    ds_sourceItemPricing.sellingPrice = sourceItemPricing.sellingPrice
1571
 
1572
    session.commit()
1573
    return
1574
 
1575
def get_all_source_pricing(itemId):
1576
    item = Item.query.filter_by(id=itemId).first()
1577
    if item is None:
1578
        raise InventoryServiceException(101, "Bad Item")
1579
    source_pricing = SourceItemPricing.query.filter_by(item=item).all()
1580
    return source_pricing
1581
 
1582
 
1583
def get_item_for_source(item_id, sourceId):
1584
    item = get_item(item_id)
1585
    if sourceId == -1:
1586
        return item
1587
    try:
1588
        sip = get_item_pricing_by_source(item_id, sourceId)
1589
        item.sellingPrice = sip.sellingPrice
1590
        if sip.mrp:
1591
            item.mrp = sip.mrp
1592
    except:
1593
        print "No source pricing"
1594
    return item
1595
 
3872 chandransh 1596
def search_items(search_terms, offset, limit):
1597
    query = Item.query
1598
 
1599
    query_clause = []
1600
 
1601
    search_terms = ['%' + search_term + '%' for search_term in search_terms]
1602
 
1603
    for search_term in search_terms:
1604
        query_clause.append(Item.brand.like(search_term))
1605
        query_clause.append(Item.model_number.like(search_term))
1606
        query_clause.append(Item.model_name.like(search_term))
1607
 
1608
    query = query.filter(or_(*query_clause))
1609
 
1610
    query = query.order_by(Item.product_group, Item.brand, Item.model_number, Item.model_name).offset(offset)
1611
    if limit:
1612
        query = query.limit(limit)
1613
    items = query.all()
1614
    return items
1615
 
1616
def get_search_result_count(search_terms):
1617
    query = Item.query
1618
 
1619
    query_clause = []
1620
 
1621
    search_terms = ['%' + search_term + '%' for search_term in search_terms]
1622
 
1623
    for search_term in search_terms:
1624
        query_clause.append(Item.brand.like(search_term))
1625
        query_clause.append(Item.model_number.like(search_term))
1626
        query_clause.append(Item.model_name.like(search_term))
1627
 
1628
    query = query.filter(or_(*query_clause))
1629
 
1630
    return query.count()
1631
 
3924 rajveer 1632
def __clear_homepage_cache():
1633
    try:
1634
        # create a password manager
1635
        password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1636
        # Add the username and password.
1637
        configclient = ConfigClient()
4310 rajveer 1638
        ips = configclient.get_property("production_servers_private_ips");
3924 rajveer 1639
        ips = ips.split(" ")
1640
 
1641
        for ip in ips:
4310 rajveer 1642
            try:
1643
                top_level_url = "http://" + ip + ":8080/"
1644
                password_mgr.add_password(None, top_level_url, "saholic", "shop2020")
1645
                handler = urllib2.HTTPBasicAuthHandler(password_mgr)
3924 rajveer 1646
 
4310 rajveer 1647
                opener = urllib2.build_opener(handler)
3924 rajveer 1648
 
4310 rajveer 1649
                # use the opener to fetch a URL
1650
                res = opener.open(top_level_url + "cache-admin/HomePageSnippets?_method=delete")
1651
                print "Successfully cleared home page cache" + res.read()
1652
            except:
1653
                print "Unable to clear home page cache" + res.read()
3924 rajveer 1654
    except:
1655
        print "Unable to clear cache, still should continue with other operations"
4024 chandransh 1656
 
4062 chandransh 1657
def get_pending_orders_inventory(vendor_id=1):
4024 chandransh 1658
    """
1659
    Returns a list of inventory stock for items for which there are pending orders.
1660
    """
4341 rajveer 1661
 
5110 mandeep.dh 1662
    warehouse_ids = [warehouse.id for warehouse in Warehouse.query.filter_by(vendor_id = vendor_id)]
4064 chandransh 1663
    pending_items_inventory = []
1664
    if warehouse_ids:
1665
        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 1666
    return pending_items_inventory
4295 varun.gupt 1667
 
1668
def get_product_notifications(start_datetime):
1669
    '''
1670
    Returns a list of Product Notification objects each representing user requests for notification
1671
    '''
1672
    query = ProductNotification.query
3924 rajveer 1673
 
4295 varun.gupt 1674
    if start_datetime:
1675
        query = query.filter(ProductNotification.addedOn > start_datetime)
1676
 
1677
    notifications = query.order_by(desc('addedOn')).all()
1678
    return notifications
1679
 
1680
def get_product_notification_request_count(start_datetime):
1681
    '''
1682
    Returns list of items and the counts of product notification requests
1683
    '''
1684
    print start_datetime
1685
    query = session.query(ProductNotification, func.count(ProductNotification.email).label('count'))
1686
 
1687
    if start_datetime:
1688
        query = query.filter(ProductNotification.addedOn > start_datetime)
1689
 
1690
    counts = query.group_by(ProductNotification.item_id).order_by(desc('count')).all()
1691
    return counts
1692
 
766 rajveer 1693
def close_session():
1694
    if session.is_active:
1695
        print "session is active. closing it."
1399 rajveer 1696
        session.close()
3376 rajveer 1697
 
1698
def is_alive():
1699
    try:
1700
        session.query(Item.id).limit(1).one()
1701
        return True
1702
    except:
1703
        return False
4332 anupam.sin 1704
 
1705
def add_vendor(vendor):
1706
    if not vendor:
1707
        raise InventoryServiceException(108, "Bad vendor")
1708
    if get_Vendor(vendor.id):
1709
        #vendor is already present.
1710
        raise InventoryServiceException(101, "Vendor already present")
1711
 
1712
    ds_vendor = Vendor()
1713
    ds_vendor.id = vendor.id
1714
    ds_vendor.name = vendor.name
1715
    session.commit()
1716
    return ds_vendor.id
1717
 
1718
def add_warehouse_vendor_mapping(warehouse_id, VendorId):
1719
    return True
1720
 
4649 phani.kuma 1721
def add_authorization_log_for_item(itemId, username, reason):
1722
    if not itemId or not username:
1723
        raise InventoryServiceException(101, "Bad itemId or Invalid username in request")
1724
    authorize_log = AuthorizationLog()
1725
    authorize_log.item_id = itemId
1726
    authorize_log.username = username
1727
    authorize_log.reason = reason
1728
    session.commit()
4797 rajveer 1729
    return True
1730
 
1731
def __send_mail_for_oos_item(item): 
1732
    try:
4930 rajveer 1733
        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 1734
    except Exception as e:
1735
        print e
4985 mandeep.dh 1736
 
1737
def mark_missed_inventory_updates_as_processed(itemKey, warehouseId):
1738
    MissedInventoryUpdate.query.filter_by(itemKey = itemKey, warehouseId = warehouseId).delete()
1739
    session.commit()
1740
 
1741
def get_item_keys_to_be_processed(warehouseId):
1742
    return [i.itemKey for i in MissedInventoryUpdate.query.filter_by(warehouseId = warehouseId, isIgnored = 0)]
1743
 
1744
def reset_availability(itemKey, vendorId, quantity, warehouseId):
1745
    vendorItemMapping = VendorItemMapping.get_by(vendor_id = vendorId, item_key = itemKey)
1746
    if vendorItemMapping:
1747
        itemId = vendorItemMapping.item_id
1748
        currentInventorySnapshot = CurrentInventorySnapshot.get_by(item_id = itemId, warehouse_id = warehouseId)
1749
        if currentInventorySnapshot:
1750
            currentInventorySnapshot.availibility = quantity
1751
        else:
1752
            add_inventory(itemId, warehouseId, quantity)
1753
    else:
1754
        raise InventoryServiceException(101, 'VendorMapping not found for: ' + itemKey)
1755
    session.commit()
5047 amit.gupta 1756
 
1757
def __send_mail(subject, message):
5080 amit.gupta 1758
    try:
1759
        thread = threading.Thread(target=partial(mail, from_user, from_pwd, to_addresses, subject, message))
1760
        thread.start()
1761
    except Exception as ex:
1762
        print ex