Subversion Repositories SmartDukaan

Rev

Rev 5167 | Rev 5213 | 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]
5147 mandeep.dh 806
                if item.preferredVendor:
807
                    warehousesWithPreferredVendorAtPreferredShippingLocation = Warehouse.query.filter_by(shippingWarehouseId = item.preferredWarehouse, inventoryType = InventoryType._VALUES_TO_NAMES[InventoryType.GOOD], vendor_id = item.preferredVendor).all()
808
                    if warehousesWithPreferredVendorAtPreferredShippingLocation:
809
                        warehouse_retid = warehousesWithPreferredVendorAtPreferredShippingLocation[0].id
5110 mandeep.dh 810
    elif (not item.isWarehousePreferenceSticky and item.preferredWarehouse):
811
        [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(goodBillableWarehousesAtPreferredShippingLocation, item)
812
 
813
    if warehouse_retid == -1:
814
        [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(warehouse_ids, item)
815
        if warehouse_retid == -1:
816
            warehouse_retid = goodBillableWarehousesAtDefaultShippingLocation[0]
5147 mandeep.dh 817
            if item.preferredVendor:
818
                warehousesWithPreferredVendorAtDefaultShippingLocation = Warehouse.query.filter_by(shippingWarehouseId = item.defaultWarehouse, inventoryType = InventoryType._VALUES_TO_NAMES[InventoryType.GOOD], vendor_id = item.preferredVendor).all()
819
                if warehousesWithPreferredVendorAtDefaultShippingLocation:
820
                    warehouse_retid = warehousesWithPreferredVendorAtDefaultShippingLocation[0].id
5110 mandeep.dh 821
        else:
822
            [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(goodBillableWarehouses, item)
823
            if warehouse_retid == -1:
824
                if item.preferredWarehouse:
825
                    [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(nonBillableWarehousesCorrespondingToPreferredShippingLocation, item)
826
                    if warehouse_retid == -1:
827
                        # Availability at Good Billable warehouses is Zero, so can safely use all warehouse_ids to capture the thirdparty virtual/good one
828
                        [warehouse_retid, total_availability] = __get_warehouse_with_max_availability(warehouse_ids, item)
829
 
830
    warehouse = Warehouse.get_by(id=warehouse_retid)
831
    billingWarehouseId = warehouse.billingWarehouseId
4406 anupam.sin 832
 
5110 mandeep.dh 833
    # Fetching billing warehouse of a Good billable warehouse corresponding to the virtual one
834
    if warehouse.billingWarehouseId is None:
835
        for warehouse in Warehouse.query.filter_by(vendor_id = warehouse.vendor_id, inventoryType = InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]).all():
836
            if warehouse.billingWarehouseId:
837
                billingWarehouseId = warehouse.billingWarehouseId
838
                break
4897 rajveer 839
 
5110 mandeep.dh 840
    logisticsLocation = warehouse.logisticsLocation  
841
 
4897 rajveer 842
    expectedDelay = item.expectedDelay 
843
    if expectedDelay is None:
844
        print 'expectedDelay field for this item was Null. Resetting it to 0'
845
        expectedDelay = 0
846
    else:
847
        expectedDelay = int(item.expectedDelay)
848
 
849
    if total_availability <= 0:
850
        expectedDelay = expectedDelay + __get_expected_procurement_delay(item)
4979 rajveer 851
        expectedDelay = expectedDelay + __get_vendor_holiday_delay(item, expectedDelay)
4406 anupam.sin 852
 
5110 mandeep.dh 853
    return [logisticsLocation, int(warehouse_retid), total_availability, expectedDelay, billingWarehouseId]
4406 anupam.sin 854
 
5110 mandeep.dh 855
def __get_warehouse_with_max_availability(warehouse_ids, item):
4406 anupam.sin 856
    warehouse_retid = -1
857
    max_availability = 0
858
    total_availability = 0
859
 
5110 mandeep.dh 860
    if item.currentInventory:
861
        for entry in item.currentInventory:
862
            if entry.warehouse_id in warehouse_ids:
863
                availability = entry.availibility - entry.reserved
864
                if availability > max_availability:
865
                    warehouse_retid = entry.warehouse_id
866
                    max_availability = availability
867
                total_availability += availability
643 chandransh 868
 
5110 mandeep.dh 869
    return [warehouse_retid, total_availability]
2341 chandransh 870
 
4897 rajveer 871
def __get_expected_procurement_delay(item):
4979 rajveer 872
    procurementDelay = 2
4897 rajveer 873
    try:
874
        if item.preferredVendor:
875
            delays = VendorItemProcurementDelay.query.filter_by(vendor_id = item.preferredVendor, item_id = item.id).all()
876
        else:
877
            delays = VendorItemProcurementDelay.query.filter_by(item_id = item.id).all()
878
 
879
        procurementDelay= min([delay.procurementDelay for delay in delays])
880
    except Exception as e:
881
        print e
882
    return procurementDelay
883
 
4979 rajveer 884
def __get_vendor_holiday_delay(item, expectedDelay):
885
    holidayDelay = 0
886
    try:
887
        if item.preferredVendor:
888
            holidays = VendorHolidays.query.filter_by(vendor_id = item.preferredVendor).all()
889
            currentDate = datetime.date.today()
890
            expectedDate = currentDate + datetime.timedelta(days = expectedDelay)
891
            for holiday in holidays:
892
                if holiday.holidayType == HolidayType.WEEKLY and holiday.holidayValue != calendar.SUNDAY:
893
                    if currentDate.weekday() > holiday.holidayValue:
894
                        holidayDate = currentDate + datetime.timedelta(days=holiday.holidayValue-currentDate.weekday(), weeks=1)
895
                    else:
896
                        holidayDate = currentDate + datetime.timedelta(days=holiday.holidayValue-currentDate.weekday())
897
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
898
                        holidayDelay = holidayDelay + 1
899
                elif holiday.holidayType == HolidayType.MONTHLY:
900
                    holidayDate = datetime.date(currentDate.year, currentDate.month, holiday.holidayValue)
901
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
902
                        holidayDelay = holidayDelay + 1    
903
                elif holiday.holidayType == HolidayType.SPECIFIC:
5005 rajveer 904
                    holidayValue = str(holiday.holidayValue)
905
                    holidayDate = datetime.date(int(holidayValue[:4]), int(holidayValue[4:6]), int(holidayValue[6:8]))
4979 rajveer 906
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
907
                        holidayDelay = holidayDelay + 1                
908
    except Exception as e:
909
        print e
910
    return holidayDelay 
122 ashish 911
def get_warehouses_for_item(item_id):
643 chandransh 912
 
122 ashish 913
    if not item_id:
914
        raise InventoryServiceException(101, "bad item_id")
635 rajveer 915
    item = get_item(item_id)
122 ashish 916
 
917
    if not item:
918
        raise InventoryServiceException(101, "bad item")
919
 
483 rajveer 920
    warehouses = item.currentInventory.warehouse
501 rajveer 921
    return warehouses
922
 
2404 chandransh 923
def get_child_categories(category):
924
    cm = CategoryManager()
2621 varun.gupt 925
    cat = cm.getCategory(category)
926
    return cat.children_category_ids if cat else None
2404 chandransh 927
 
626 chandransh 928
def get_best_sellers(start_index, stop_index, category=-1):
2404 chandransh 929
    '''
930
    Returns the Best Sellers between the start and the stop index in the given category
931
    '''
1926 rajveer 932
    query = get_best_sellers_query(category, None)
1098 chandransh 933
    best_sellers = query.all()[start_index:stop_index]
621 chandransh 934
    return get_thrift_item_list(best_sellers)
935
 
2093 chandransh 936
def get_best_sellers_count(category=-1):
2404 chandransh 937
    '''
938
    Returns the number of best sellers in the given category
939
    '''
1926 rajveer 940
    count = get_best_sellers_query(category, None).count()
1120 rajveer 941
    if count is None:
942
        count = 0
766 rajveer 943
    return count
621 chandransh 944
 
1926 rajveer 945
def get_best_sellers_catalog_ids(start_index, stop_index, brand, category=-1):
2404 chandransh 946
    '''
947
    Returns the Best sellers for the given brand and category between the start and the stop index.
948
    Ignores the category if it's passed as -1 and the brand if it's passed as None. 
949
    '''
1926 rajveer 950
    query = get_best_sellers_query(category, brand)
1098 chandransh 951
    best_sellers = query.all()[start_index:stop_index]
621 chandransh 952
    return [item.catalog_item_id for item in best_sellers]
1970 rajveer 953
 
1926 rajveer 954
def get_best_sellers_query(category, brand):
2404 chandransh 955
    '''
956
    Returns the query to be used for getting Best Sellers.
957
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
958
    '''
1098 chandransh 959
    query = Item.query.filter_by(status=status.ACTIVE).filter(Item.bestSellingRank != None)
626 chandransh 960
    if category != -1:
1970 rajveer 961
        all_categories = [category]
962
        child_categories = get_child_categories(category)
963
        if child_categories is not None:
964
            all_categories = all_categories + child_categories 
965
        query = query.filter(Item.category.in_(all_categories))
1926 rajveer 966
    if brand is not None:
967
        query = query.filter_by(brand=brand)
1098 chandransh 968
    query = query.order_by(asc(Item.bestSellingRank))
621 chandransh 969
    return query
609 chandransh 970
 
1098 chandransh 971
def get_best_deals(category=-1):
2404 chandransh 972
    '''
973
    Returns the Best deals in the given category. Ignores the category if it's passed as -1.
974
    '''
975
    query = get_best_deals_query(Item, category, None)
1098 chandransh 976
    items = query.all()
609 chandransh 977
    return get_thrift_item_list(items)
978
 
1098 chandransh 979
def get_best_deals_count(category=-1):
2404 chandransh 980
    '''
981
    Returns the count of best deals in the given category.
982
    Ignores the category if it's -1.
983
    '''
984
    count = get_best_deals_counting_query(func.count(distinct(Item.catalog_item_id)), category, None).scalar()
1120 rajveer 985
    if count is None:
986
        count = 0
766 rajveer 987
    return count
501 rajveer 988
 
1926 rajveer 989
def get_best_deals_catalog_ids(start_index, stop_index, brand, category=-1):
2404 chandransh 990
    '''
991
    Returns the catalog_item_ids of best deal items for the given brand and category.
992
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
993
    '''
994
    query = get_best_deals_query(Item, category, brand)
1098 chandransh 995
    best_deal_items = query.all()[start_index:stop_index]
996
    return [item.catalog_item_id for item in best_deal_items]
997
 
2404 chandransh 998
def get_best_deals_counting_query(obj, category, brand):
999
    '''
1000
    Returns the query to be used to select the best deals in the given brand and category.
1001
    Ignores the category if it's passed as -1 and the brand if it's passed as None. 
1002
    '''
1003
    query = session.query(obj).filter_by(status=status.ACTIVE).filter(Item.bestDealValue != None)
626 chandransh 1004
    if category != -1:
1970 rajveer 1005
        all_categories = [category]
1006
        child_categories = get_child_categories(category)
1007
        if child_categories is not None:
1008
            all_categories = all_categories + child_categories 
1009
        query = query.filter(Item.category.in_(all_categories))
1926 rajveer 1010
    if brand is not None:
1011
        query = query.filter_by(brand=brand)
2404 chandransh 1012
    return query
1013
 
1014
def get_best_deals_query(obj, category, brand):
1015
    '''
1016
    Returns the query to be used to get the best deals in the given category and brand.
1017
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
1018
    '''
1019
    query = get_best_deals_counting_query(obj, category, brand)
1098 chandransh 1020
    query = query.group_by(Item.catalog_item_id).order_by(desc(Item.bestDealValue))
1021
    return query
609 chandransh 1022
 
1098 chandransh 1023
def get_latest_arrivals(limit, category=-1):
2404 chandransh 1024
    '''
1025
    Returns up to limit number of Latest Arrivals in the given category.
1026
    '''
2975 chandransh 1027
    categories = []
1028
    if category != -1:
1029
        categories = [category]
1030
    query = get_latest_arrivals_query(Item, categories, None)
1098 chandransh 1031
    items = query.all()[0:limit]
609 chandransh 1032
    return get_thrift_item_list(items)
598 chandransh 1033
 
1098 chandransh 1034
def get_latest_arrivals_count(limit, category=-1):
2404 chandransh 1035
    '''
1036
    Returns the number of latest arrivals which will be displayed on the website.
3016 chandransh 1037
    To ignore the categories, pass the list as empty. To ignore brand, pass it as null.
2404 chandransh 1038
    '''
2975 chandransh 1039
    categories = []
1040
    if category != -1:
1041
        categories = [category]
1042
    count = get_latest_arrivals_counting_query(func.count(distinct(Item.catalog_item_id)), categories, None).scalar()
1120 rajveer 1043
    if count is None:
1044
        count = 0
1045
    count = min(count, limit)
766 rajveer 1046
    return count
602 chandransh 1047
 
2975 chandransh 1048
def get_latest_arrivals_catalog_ids(start_index, stop_index, brand, categories=[]):
2404 chandransh 1049
    '''
1050
    Returns the catalog_item_ids of the latest arrivals between the start and the stop index
3016 chandransh 1051
    To ignore the categories, pass the list as empty. To ignore brand, pass it as null.
2404 chandransh 1052
    '''
2975 chandransh 1053
    query = get_latest_arrivals_query(Item, categories, brand)
1098 chandransh 1054
    latest_arrivals = query.all()[start_index:stop_index]
1055
    return [item.catalog_item_id for item in latest_arrivals]
1056
 
2975 chandransh 1057
def get_latest_arrivals_counting_query(obj, categories, brand):
2404 chandransh 1058
    '''
1059
    Returns the query to be used to count Latest arrivals.
3016 chandransh 1060
    To ignore the categories, pass the list as empty. To ignore brand, pass it as null.
2404 chandransh 1061
    '''
1062
    query = session.query(obj).filter_by(status=status.ACTIVE)
2975 chandransh 1063
 
1064
    all_categories = []
1065
    for category in categories:
1066
        all_categories.append(category)
1970 rajveer 1067
        child_categories = get_child_categories(category)
2975 chandransh 1068
        if child_categories:
1069
            all_categories = all_categories + child_categories
1070
    if all_categories: 
1970 rajveer 1071
        query = query.filter(Item.category.in_(all_categories))
2975 chandransh 1072
 
1926 rajveer 1073
    if brand is not None:
1074
        query = query.filter_by(brand=brand)
2404 chandransh 1075
    return query
1076
 
2975 chandransh 1077
def get_latest_arrivals_query(obj, categories, brand):
2404 chandransh 1078
    '''
1079
    Returns the query to be used to retrieve Latest Arrivals.
1080
    Ignores the category if it's passed as -1 and the brand if it's passed as None.
1081
    '''
2975 chandransh 1082
    query = get_latest_arrivals_counting_query(obj, categories, brand)
5167 rajveer 1083
    query = query.group_by(Item.catalog_item_id).order_by(desc(Item.startDate)).order_by(Item.catalog_item_id)
1098 chandransh 1084
    return query
609 chandransh 1085
 
1086
def get_thrift_item_list(items):
1098 chandransh 1087
    return [to_t_item(item) for item in items if item != None]
635 rajveer 1088
 
1155 rajveer 1089
def generate_new_entity_id():
1090
    generator =  EntityIDGenerator.query.one()
1091
    id = generator.id + 1
1092
    generator.id = id
1093
    session.commit()
1094
    return id
1095
 
635 rajveer 1096
def put_category_object(object):
1097
    category = Category.get_by(id=1)
1098
    if category is None:
1099
        category = Category()
1100
    category.object = object    
1101
    session.commit()
1102
    return True
1103
 
1104
def get_category_object():
766 rajveer 1105
    object = Category.get_by(id=1).object
1106
    return object
1107
 
4283 anupam.sin 1108
def get_item_pricing(item_id, vendorId):
1341 chandransh 1109
    item = Item.query.filter_by(id=item_id).first()
1110
    if item is None:
1111
        raise InventoryServiceException(101, "Bad Item")
4307 anupam.sin 1112
    '''
1113
    if vendor id is -1 then we calculate an average transfer price to be populated
1114
    at the time of order creation. This will be later updated with actual transfer price
1115
    at the time of billing.
1116
    '''
1117
    if(vendorId == -1):
4543 anupam.sin 1118
        total = 0
4307 anupam.sin 1119
        try:
5133 mandeep.dh 1120
            item_pricings = []
4858 anupam.sin 1121
            if item.preferredWarehouse is not None:
1122
                warehouse = Warehouse.query.filter_by(id=item.preferredWarehouse).first()
5133 mandeep.dh 1123
                item_pricing = VendorItemPricing.query.filter_by(item=item, vendor=warehouse.vendor).first()
1124
                if item_pricing:
1125
                    item_pricings.append(item_pricing)                    
4858 anupam.sin 1126
            else :
1127
                item_pricings = VendorItemPricing.query.filter_by(item=item).all()
4315 anupam.sin 1128
            if item_pricings:
4307 anupam.sin 1129
                for item_pricing in item_pricings:
4543 anupam.sin 1130
                    total += item_pricing.transfer_price
4315 anupam.sin 1131
                avg = total / len(item_pricings)
1132
                item_pricing.transfer_price = avg
4543 anupam.sin 1133
            else:
1134
                item_pricing = VendorItemPricing()
1135
                item_pricing.transfer_price = item.sellingPrice
1136
                vendor = Vendor()
1137
                vendor.id = vendorId
1138
                item_pricing.vendor = vendor
1139
                item_pricing.item = item
1140
 
1141
            return item_pricing
4307 anupam.sin 1142
        except:
1143
            raise InventoryServiceException(101, "Item pricing not found ")
4283 anupam.sin 1144
    vendor = Vendor.get_by(id=vendorId)    
1341 chandransh 1145
    try:
1146
        item_pricing = VendorItemPricing.query.filter_by(vendor=vendor, item=item).one()
1347 chandransh 1147
        return item_pricing
3244 chandransh 1148
    except MultipleResultsFound:
1341 chandransh 1149
        raise InventoryServiceException(110, "Multiple pricing information present for Vendor: " + vendor.name + " and Item: " + str(item_id))
3244 chandransh 1150
    except NoResultFound:
1341 chandransh 1151
        raise InventoryServiceException(111, "Missing pricing information for Vendor: " + vendor.name + " and Item: " + str(item_id))
1152
 
1970 rajveer 1153
def add_category(t_category):
1154
    category = Category.get_by(id=t_category.id)
1155
    if category is None:
1156
        category = Category()
1157
    category.id = t_category.id 
1158
    category.label = t_category.label
1159
    category.description = t_category.description
4762 phani.kuma 1160
    category.display_name = t_category.display_name
1970 rajveer 1161
    category.parent_category_id = t_category.parent_category_id 
1162
    session.commit()
1163
    return True
1164
 
1165
def get_category(id):
1166
    return Category.query.filter_by(id=id).first()
1167
 
1168
def get_all_categories():
1169
    return Category.query.all()
1170
 
1991 ankur.sing 1171
 
1172
def get_all_item_pricing(item_id):
1173
    item = Item.query.filter_by(id=item_id).first()
1174
    if item is None:
1175
        raise InventoryServiceException(101, "Bad Item")
1176
    item_pricing = VendorItemPricing.query.filter_by(item=item).all()
1177
    return item_pricing
1178
 
2116 ankur.sing 1179
def get_item_mappings(item_id):
1180
    item = Item.query.filter_by(id=item_id).first()
1181
    if item is None:
1182
        raise InventoryServiceException(101, "Bad Item")
1183
    item_mappings = VendorItemMapping.query.filter_by(item=item).all()
1184
    return item_mappings
1185
 
1186
def add_vendor_pricing(vendorItemPricing):
1991 ankur.sing 1187
    if not vendorItemPricing:
1188
        raise InventoryServiceException(108, "Bad vendorItemPricing in request")
1189
    vendorId = vendorItemPricing.vendorId
1190
    itemId = vendorItemPricing.itemId
1191
 
1192
    try:
1193
        vendor = Vendor.query.filter_by(id=vendorId).one()
1194
    except:
1195
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
1196
 
1197
    try:
1198
        item = Item.query.filter_by(id=itemId).one()
1199
    except:
5047 amit.gupta 1200
        raise InventoryServiceException(101, "Item not found for itemId " + str(itemId))
1991 ankur.sing 1201
 
2120 ankur.sing 1202
    validate_vendor_prices(to_t_item(item), vendorItemPricing)
2065 ankur.sing 1203
 
1991 ankur.sing 1204
    try:
1205
        ds_vendorItemPricing = VendorItemPricing.query.filter(and_(VendorItemPricing.vendor==vendor, VendorItemPricing.item==item)).one()
1206
    except:
2116 ankur.sing 1207
        ds_vendorItemPricing = VendorItemPricing()
1208
        ds_vendorItemPricing.vendor = vendor
1209
        ds_vendorItemPricing.item = item
1991 ankur.sing 1210
 
5047 amit.gupta 1211
    subject = ""
1212
    message = ""
1991 ankur.sing 1213
    if vendorItemPricing.mop:
1214
        ds_vendorItemPricing.mop = vendorItemPricing.mop
1215
    if vendorItemPricing.dealerPrice:
1216
        ds_vendorItemPricing.dealerPrice = vendorItemPricing.dealerPrice
1217
    if vendorItemPricing.transferPrice:
5047 amit.gupta 1218
        if vendorItemPricing.transferPrice != ds_vendorItemPricing.transfer_price:
1219
            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)
1220
            subject = "Alert:Change in Transfer Price"
2065 ankur.sing 1221
        ds_vendorItemPricing.transfer_price = vendorItemPricing.transferPrice
1991 ankur.sing 1222
 
1223
    session.commit()
5047 amit.gupta 1224
    if subject:
1225
        __send_mail(subject, message)
1991 ankur.sing 1226
    return
1227
 
2358 ankur.sing 1228
def add_vendor_item_mapping(key, vendorItemMapping):
2116 ankur.sing 1229
    if not vendorItemMapping:
1230
        raise InventoryServiceException(108, "Bad vendorItemMapping in request")
1231
    vendorId = vendorItemMapping.vendorId
1232
    itemId = vendorItemMapping.itemId
1233
 
1234
    try:
1235
        vendor = Vendor.query.filter_by(id=vendorId).one()
1236
    except:
1237
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
1238
 
1239
    try:
1240
        item = Item.query.filter_by(id=itemId).one()
1241
    except:
1242
        raise InventoryServiceException(101, "Item not found for vendorId " + str(itemId))
1243
 
1244
    try:
2358 ankur.sing 1245
        ds_vendorItemMapping = VendorItemMapping.query.filter(and_(VendorItemMapping.vendor==vendor, VendorItemMapping.item==item, VendorItemMapping.item_key==key)).one()
2116 ankur.sing 1246
    except:
1247
        ds_vendorItemMapping = VendorItemMapping()
1248
        ds_vendorItemMapping.vendor = vendor
1249
        ds_vendorItemMapping.item = item
1250
    ds_vendorItemMapping.item_key = vendorItemMapping.itemKey
4985 mandeep.dh 1251
 
2116 ankur.sing 1252
    session.commit()
4985 mandeep.dh 1253
 
1254
    # Marking the missed inventory as not ignored as the catalog dashboard user has updated their key
1255
    for missedInventoryUpdate in MissedInventoryUpdate.query.filter_by(itemKey = vendorItemMapping.itemKey).all():
1256
        missedInventoryUpdate.isIgnored = 0
1257
    session.commit()
1258
 
2116 ankur.sing 1259
    return
1260
 
2120 ankur.sing 1261
def validate_item_prices(item):
2129 ankur.sing 1262
    if item.mrp == None or item.sellingPrice == None or item.mrp == "" or item.sellingPrice == "":
1263
        return
1264
    if item.mrp < item.sellingPrice:
2120 ankur.sing 1265
        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))
1266
        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 1267
    return
2120 ankur.sing 1268
 
1269
def validate_vendor_prices(item, vendorPrices):
2129 ankur.sing 1270
    if item.mrp != None and item.mrp != "" and vendorPrices.mop != "" and item.mrp <  vendorPrices.mop:
2120 ankur.sing 1271
        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))
1272
        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)))
1273
    if vendorPrices.mop != "" and vendorPrices.transferPrice != "" and vendorPrices.transferPrice > vendorPrices.mop:
1274
        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))
1275
        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)))
1276
    return
2065 ankur.sing 1277
 
1278
def get_all_vendors():
1279
    return Vendor.query.all()
1280
 
4725 phani.kuma 1281
def check_color_valid(color):
1282
    if color is not None:
1283
        color = color.strip().lower()
1284
        if color != '' and color != 'na' and color != 'blank' and color != '(blank)':
1285
            return True
1286
    return False
1287
 
1288
def check_similar_item(brand, model_number, model_name, color):
2129 ankur.sing 1289
    query = Item.query
2428 ankur.sing 1290
    query = query.filter_by(brand=brand)
1291
    query = query.filter_by(model_number=model_number)
4725 phani.kuma 1292
    query = query.filter_by(model_name=model_name)
1293
    similar_items = query.all()
1294
    item = None
1295
    # Check if a similar item already exists in our database
1296
    for old_item in similar_items:
1297
        if old_item.color != None and old_item.color.strip().lower() == color.strip().lower():
1298
            item = old_item
1299
            break
1300
 
1301
    # 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 1302
    if item is None:
4725 phani.kuma 1303
        for old_item in similar_items:
1304
            if not check_color_valid(old_item.color):
1305
                item = old_item
1306
                break
1307
    i = 0
1308
    color_of_similar_item = None
1309
    # Check if a similar item already exists in our database to be used to get catalog_item_id
1310
    for old_item in similar_items:
1311
        # get a similar item already existing in our database with valid color
1312
        if check_color_valid(old_item.color):
1313
            similar_item = old_item
1314
            color_of_similar_item = similar_item.color
1315
            break
1316
        i = i + 1
1317
        # get a similar item already existing in our database if similar item with valid color is not found
1318
        if i == len(similar_items):
1319
            similar_item = old_item
1320
            color_of_similar_item = similar_item.color
1321
 
1322
    # Check if a similar item that is obtained above is having a valid color
1323
    if check_color_valid(color_of_similar_item):
1324
        # 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.
1325
        # 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.
1326
        if item is None and not check_color_valid(color):
1327
            return similar_item.id
1328
 
1329
    if item is None:
2116 ankur.sing 1330
        return 0
1331
    else:
1332
        return item.id
2286 ankur.sing 1333
 
1334
def change_risky_flag(item_id, risky):
1335
    item = get_item(item_id)
1336
    if not item:
1337
        raise InventoryServiceException(101, "Item missing in our database")
1338
    try:
1339
        log_risky_flag(item_id, risky)
1340
    except:
1341
        print "Not able to log risky flag change"
1342
    item.risky = risky
4295 varun.gupt 1343
    if not risky and item.status == status.PAUSED_BY_RISK:
2368 ankur.sing 1344
        change_item_status(item.id, status.ACTIVE)
2286 ankur.sing 1345
    session.commit()
5047 amit.gupta 1346
    flag = "ON" if risky else "OFF"
1347
    subject = "Risky flag is {0} for Item {1}.".format(flag, __get_product_name(item))
1348
    __send_mail(subject,"")
2286 ankur.sing 1349
 
4957 phani.kuma 1350
def get_items_for_mastersheet(categoryName, brand):
1351
    if not categoryName or not brand:
1352
        raise InventoryServiceException(101, "Invalid category or brand in request")
1353
 
4762 phani.kuma 1354
    categories = ["Handsets", "Tablets", "Laptops"]
4957 phani.kuma 1355
    query = Item.query.filter(Item.status != status.PHASED_OUT)
1356
    if categoryName == "ALL":
1357
        pass
1358
    elif categoryName == "ALL Accessories":
1359
        query = query.filter(~Item.product_group.in_(categories))
1360
    elif categoryName == "ALL Handsets":
1361
        query = query.filter(Item.product_group.in_(categories))
1362
    elif categoryName == "Mobile Accessories":
1363
        child_categories = get_child_categories(10011)
1364
        if child_categories is not None:
1365
            child_categories.append(0)
1366
            query = query.filter(Item.category.in_(child_categories))
1367
    elif categoryName == "Laptop Accessories":
1368
        child_categories = get_child_categories(10070)
1369
        if child_categories is not None:
1370
            child_categories.append(0)
1371
            query = query.filter(Item.category.in_(child_categories))
1372
    else:
1373
        query = query.filter(Item.product_group == categoryName)
1374
 
1375
    if brand == "ALL":
1376
        pass
1377
    else:
1378
        query = query.filter(Item.brand == brand)
2358 ankur.sing 1379
    items = query.all()
1380
    return items
2116 ankur.sing 1381
 
2358 ankur.sing 1382
def get_risky_items():
1383
    items = Item.query.filter_by(risky=True).all()
1384
    return items
3008 rajveer 1385
 
2809 rajveer 1386
def get_similar_items_catalog_ids(start_index, stop_index, itemId):
3008 rajveer 1387
    query = SimilarItems.query.filter_by(item_id=itemId).limit(stop_index-start_index)
1388
    similar_items = query.all()
3289 rajveer 1389
    return_list = []
1390
    for similar_item in similar_items:
1391
        isActive = False
1392
        try:
1393
            all_items = Item.query.filter_by(catalog_item_id=similar_item.catalog_item_id).all()
1394
        except:
1395
            continue
1396
        for item in all_items:
1397
            isActive = isActive or item.status == status.ACTIVE
1398
        if isActive:
1399
            return_list.append(similar_item.catalog_item_id)
1400
    return return_list
4423 phani.kuma 1401
 
1402
def get_all_similar_items_catalog_ids(itemId):
1403
    query = SimilarItems.query.filter_by(item_id=itemId)
1404
    similar_items = query.all()
1405
    return_list = []
1406
    for similar_item in similar_items:
1407
        item_query = Item.query.filter_by(catalog_item_id=similar_item.catalog_item_id).limit(1)
1408
        item = item_query.one()
1409
        return_list.append(item)
2809 rajveer 1410
 
4423 phani.kuma 1411
    return get_thrift_item_list(return_list)
1412
 
1413
def add_similar_item_catalog_id(itemId, catalog_item_id):
1414
    if not itemId or not catalog_item_id:
1415
        raise InventoryServiceException(101, "Bad itemId or catalogItemId in request")
1416
    items_for_entity = get_items_by_catalog_id(catalog_item_id)
1417
    if not len(items_for_entity):
1418
        raise InventoryServiceException(101, "catalogItemId does not exists in database")
1419
 
1420
    s_items = SimilarItems.query.filter_by(item_id=itemId, catalog_item_id=catalog_item_id).all()
1421
    if not len(s_items):
1422
        s_item = SimilarItems()
1423
        s_item.item_id=itemId
1424
        s_item.catalog_item_id=catalog_item_id
1425
        session.commit()
1426
        return items_for_entity[0]
1427
    else:
1428
        raise InventoryServiceException(101, "Already exists")
1429
 
1430
def delete_similar_item_catalog_id(itemId, catalog_item_id):
1431
    if not itemId or not catalog_item_id:
1432
        raise InventoryServiceException(101, "Bad itemId or catalogItemId in request")
1433
 
1434
    similar_item = SimilarItems.query.filter_by(item_id=itemId, catalog_item_id=catalog_item_id).all()
1435
    if len(similar_item):
1436
        similar_item[0].delete()
1437
    session.commit()
1438
    return True
1439
 
3079 rajveer 1440
def add_product_notification(itemId, email):
1441
    try:
3470 rajveer 1442
        try:
1443
            product_notification = ProductNotification.query.filter_by(item_id=itemId, email=email).one()
1444
        except:
1445
            product_notification = ProductNotification()
1446
            product_notification.email = email
1447
            product_notification.item_id = itemId
3079 rajveer 1448
        product_notification.addedOn = datetime.datetime.now()
1449
        session.commit()
1450
        return True
1451
    except:
1452
        return False
3086 rajveer 1453
 
1454
 
1455
def send_product_notifications():
1456
    product_notifications = ProductNotification.query.all()
1457
    for product_notification in product_notifications:
1458
        item = product_notification.item
5125 mandeep.dh 1459
        availability = __get_item_availability(item, None)
1460
        if item.status == status.ACTIVE and (not item.risky or availability > 0):
3086 rajveer 1461
            __enque_product_notification_email(product_notification.email, __get_product_name(item) , product_notification.addedOn, __get_product_url(item), item.id)
1462
            product_notification.delete()
1463
    session.commit()
1464
    return True
1465
 
1466
def __get_product_name(item):
1467
    product_name = item.brand + " " + item.model_name + " " + item.model_number
1468
    color = item.color
1469
    if color is not None and color != 'NA':
1470
        product_name = product_name + " (" + color + ")"
3201 rajveer 1471
    product_name = product_name.replace("  "," ")
3086 rajveer 1472
    return product_name
1473
 
1474
 
1475
def __get_product_url(item):
1476
    product_url = "http://www.saholic.com/mobile-phones/" + item.brand + "-" + item.model_name + "-" + item.model_number + "-" + str(item.catalog_item_id)
1477
    product_url = product_url.replace("--","-")
1478
    product_url = product_url.replace(" ","")
1479
    return product_url
1480
 
3348 varun.gupt 1481
def get_all_brands_by_category(category_id):
1482
    catm = CategoryManager()
1483
    child_categories = catm.getCategory(category_id).children_category_ids
1484
    brands = session.query(distinct(Item.brand)).filter(Item.category.in_(child_categories)).all()
1485
 
1486
    return [brand[0] for brand in brands]
3086 rajveer 1487
 
4957 phani.kuma 1488
def get_all_brands():
1489
    brands = session.query(distinct(Item.brand)).order_by(Item.brand).all()
1490
 
1491
    return [brand[0] for brand in brands]
1492
 
3086 rajveer 1493
def __enque_product_notification_email(email, product, date, url, itemId):
1494
 
1495
    html = """
1496
        <html>
1497
        <body>
1498
        <div>
1499
        <p>
1500
            Hi,<br /><br />
1501
            The product requested by you on $date is now available on saholic.com.
1502
        </p>
1503
 
1504
        <p>    
1505
        <strong>Product: $product </strong>
1506
        </p>
1507
 
1508
        <p>
1509
        Click the link below to visit the product: 
1510
        <br/>
1511
        $url
1512
        </p>
1513
        <p>
1514
        Regards,<br/>
1515
        Saholic Customer Support Team<br/>
1516
        www.saholic.com<br/>
1517
        Email: help@saholic.com<br/>
1518
        </p>
1519
        </div>
1520
        </body>
1521
        </html>
1522
        """
1523
 
1524
    html = Template(html).substitute(dict(product=product,date=date,url=url))
3079 rajveer 1525
 
3086 rajveer 1526
    try:
1527
        helper_client = HelperClient().get_client()
1528
        helper_client.saveUserEmailForSending(email, "", "Product requested by you is available now.", html, str(itemId), "ProductNotification")
1529
    except Exception as e:
1530
        print e
1531
 
3557 rajveer 1532
def get_all_sources():
1533
    sources = Source.query.all()
1534
    return [to_t_source(source) for source in sources]
3086 rajveer 1535
 
3557 rajveer 1536
def get_item_pricing_by_source(itemId, sourceId):
1537
    item = Item.query.filter_by(id=itemId).first()
1538
    if item is None:
1539
        raise InventoryServiceException(101, "Bad Item")
1540
 
1541
    source = Source.query.filter_by(id=sourceId).first()
1542
    if source is None:
1543
        raise InventoryServiceException(101, "Source not found for sourceId " + str(sourceId))
1544
 
1545
    item_pricing = SourceItemPricing.query.filter_by(source=source, item=item).first()
1546
    if item_pricing is None:
1547
        raise InventoryServiceException(101, "Pricing information not found for sourceId " + str(sourceId))
1548
    return item_pricing
1549
 
1550
def add_source_item_pricing(sourceItemPricing):
1551
    if not sourceItemPricing:
1552
        raise InventoryServiceException(108, "Bad sourceItemPricing in request")
1553
 
1554
    if not sourceItemPricing.sellingPrice:
4326 mandeep.dh 1555
        raise InventoryServiceException(101, "Selling Price is not defined for sourceId " + str(sourceItemPricing.sourceId))
3557 rajveer 1556
 
1557
    sourceId = sourceItemPricing.sourceId
1558
    itemId = sourceItemPricing.itemId
1559
 
1560
    item = Item.query.filter_by(id=itemId).first()
1561
    if item is None:
1562
        raise InventoryServiceException(101, "Bad Item")
1563
 
1564
    source = Source.query.filter_by(id=sourceId).first()
1565
    if source is None:
1566
        raise InventoryServiceException(101, "Source not found for sourceId " + str(sourceId))
1567
 
3564 rajveer 1568
    ds_sourceItemPricing = SourceItemPricing.get_by(source=source, item=item)
3557 rajveer 1569
    if ds_sourceItemPricing is None:
1570
        ds_sourceItemPricing = SourceItemPricing()
1571
        ds_sourceItemPricing.source = source
1572
        ds_sourceItemPricing.item = item
1573
 
1574
    if sourceItemPricing.mrp:
1575
        ds_sourceItemPricing.mrp = sourceItemPricing.mrp
1576
    ds_sourceItemPricing.sellingPrice = sourceItemPricing.sellingPrice
1577
 
1578
    session.commit()
1579
    return
1580
 
1581
def get_all_source_pricing(itemId):
1582
    item = Item.query.filter_by(id=itemId).first()
1583
    if item is None:
1584
        raise InventoryServiceException(101, "Bad Item")
1585
    source_pricing = SourceItemPricing.query.filter_by(item=item).all()
1586
    return source_pricing
1587
 
1588
 
1589
def get_item_for_source(item_id, sourceId):
1590
    item = get_item(item_id)
1591
    if sourceId == -1:
1592
        return item
1593
    try:
1594
        sip = get_item_pricing_by_source(item_id, sourceId)
1595
        item.sellingPrice = sip.sellingPrice
1596
        if sip.mrp:
1597
            item.mrp = sip.mrp
1598
    except:
1599
        print "No source pricing"
1600
    return item
1601
 
3872 chandransh 1602
def search_items(search_terms, offset, limit):
1603
    query = Item.query
1604
 
1605
    query_clause = []
1606
 
1607
    search_terms = ['%' + search_term + '%' for search_term in search_terms]
1608
 
1609
    for search_term in search_terms:
1610
        query_clause.append(Item.brand.like(search_term))
1611
        query_clause.append(Item.model_number.like(search_term))
1612
        query_clause.append(Item.model_name.like(search_term))
1613
 
1614
    query = query.filter(or_(*query_clause))
1615
 
1616
    query = query.order_by(Item.product_group, Item.brand, Item.model_number, Item.model_name).offset(offset)
1617
    if limit:
1618
        query = query.limit(limit)
1619
    items = query.all()
1620
    return items
1621
 
1622
def get_search_result_count(search_terms):
1623
    query = Item.query
1624
 
1625
    query_clause = []
1626
 
1627
    search_terms = ['%' + search_term + '%' for search_term in search_terms]
1628
 
1629
    for search_term in search_terms:
1630
        query_clause.append(Item.brand.like(search_term))
1631
        query_clause.append(Item.model_number.like(search_term))
1632
        query_clause.append(Item.model_name.like(search_term))
1633
 
1634
    query = query.filter(or_(*query_clause))
1635
 
1636
    return query.count()
1637
 
3924 rajveer 1638
def __clear_homepage_cache():
1639
    try:
1640
        # create a password manager
1641
        password_mgr = urllib2.HTTPPasswordMgrWithDefaultRealm()
1642
        # Add the username and password.
1643
        configclient = ConfigClient()
4310 rajveer 1644
        ips = configclient.get_property("production_servers_private_ips");
3924 rajveer 1645
        ips = ips.split(" ")
1646
 
1647
        for ip in ips:
4310 rajveer 1648
            try:
1649
                top_level_url = "http://" + ip + ":8080/"
1650
                password_mgr.add_password(None, top_level_url, "saholic", "shop2020")
1651
                handler = urllib2.HTTPBasicAuthHandler(password_mgr)
3924 rajveer 1652
 
4310 rajveer 1653
                opener = urllib2.build_opener(handler)
3924 rajveer 1654
 
4310 rajveer 1655
                # use the opener to fetch a URL
1656
                res = opener.open(top_level_url + "cache-admin/HomePageSnippets?_method=delete")
1657
                print "Successfully cleared home page cache" + res.read()
1658
            except:
1659
                print "Unable to clear home page cache" + res.read()
3924 rajveer 1660
    except:
1661
        print "Unable to clear cache, still should continue with other operations"
4024 chandransh 1662
 
4062 chandransh 1663
def get_pending_orders_inventory(vendor_id=1):
4024 chandransh 1664
    """
1665
    Returns a list of inventory stock for items for which there are pending orders.
1666
    """
4341 rajveer 1667
 
5110 mandeep.dh 1668
    warehouse_ids = [warehouse.id for warehouse in Warehouse.query.filter_by(vendor_id = vendor_id)]
4064 chandransh 1669
    pending_items_inventory = []
1670
    if warehouse_ids:
1671
        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 1672
    return pending_items_inventory
4295 varun.gupt 1673
 
1674
def get_product_notifications(start_datetime):
1675
    '''
1676
    Returns a list of Product Notification objects each representing user requests for notification
1677
    '''
1678
    query = ProductNotification.query
3924 rajveer 1679
 
4295 varun.gupt 1680
    if start_datetime:
1681
        query = query.filter(ProductNotification.addedOn > start_datetime)
1682
 
1683
    notifications = query.order_by(desc('addedOn')).all()
1684
    return notifications
1685
 
1686
def get_product_notification_request_count(start_datetime):
1687
    '''
1688
    Returns list of items and the counts of product notification requests
1689
    '''
1690
    print start_datetime
1691
    query = session.query(ProductNotification, func.count(ProductNotification.email).label('count'))
1692
 
1693
    if start_datetime:
1694
        query = query.filter(ProductNotification.addedOn > start_datetime)
1695
 
1696
    counts = query.group_by(ProductNotification.item_id).order_by(desc('count')).all()
1697
    return counts
1698
 
766 rajveer 1699
def close_session():
1700
    if session.is_active:
1701
        print "session is active. closing it."
1399 rajveer 1702
        session.close()
3376 rajveer 1703
 
1704
def is_alive():
1705
    try:
1706
        session.query(Item.id).limit(1).one()
1707
        return True
1708
    except:
1709
        return False
4332 anupam.sin 1710
 
1711
def add_vendor(vendor):
1712
    if not vendor:
1713
        raise InventoryServiceException(108, "Bad vendor")
1714
    if get_Vendor(vendor.id):
1715
        #vendor is already present.
1716
        raise InventoryServiceException(101, "Vendor already present")
1717
 
1718
    ds_vendor = Vendor()
1719
    ds_vendor.id = vendor.id
1720
    ds_vendor.name = vendor.name
1721
    session.commit()
1722
    return ds_vendor.id
1723
 
1724
def add_warehouse_vendor_mapping(warehouse_id, VendorId):
1725
    return True
1726
 
4649 phani.kuma 1727
def add_authorization_log_for_item(itemId, username, reason):
1728
    if not itemId or not username:
1729
        raise InventoryServiceException(101, "Bad itemId or Invalid username in request")
1730
    authorize_log = AuthorizationLog()
1731
    authorize_log.item_id = itemId
1732
    authorize_log.username = username
1733
    authorize_log.reason = reason
1734
    session.commit()
4797 rajveer 1735
    return True
1736
 
1737
def __send_mail_for_oos_item(item): 
1738
    try:
4930 rajveer 1739
        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 1740
    except Exception as e:
1741
        print e
4985 mandeep.dh 1742
 
1743
def mark_missed_inventory_updates_as_processed(itemKey, warehouseId):
1744
    MissedInventoryUpdate.query.filter_by(itemKey = itemKey, warehouseId = warehouseId).delete()
1745
    session.commit()
1746
 
1747
def get_item_keys_to_be_processed(warehouseId):
1748
    return [i.itemKey for i in MissedInventoryUpdate.query.filter_by(warehouseId = warehouseId, isIgnored = 0)]
1749
 
1750
def reset_availability(itemKey, vendorId, quantity, warehouseId):
1751
    vendorItemMapping = VendorItemMapping.get_by(vendor_id = vendorId, item_key = itemKey)
1752
    if vendorItemMapping:
1753
        itemId = vendorItemMapping.item_id
1754
        currentInventorySnapshot = CurrentInventorySnapshot.get_by(item_id = itemId, warehouse_id = warehouseId)
1755
        if currentInventorySnapshot:
1756
            currentInventorySnapshot.availibility = quantity
1757
        else:
1758
            add_inventory(itemId, warehouseId, quantity)
1759
    else:
1760
        raise InventoryServiceException(101, 'VendorMapping not found for: ' + itemKey)
1761
    session.commit()
5047 amit.gupta 1762
 
1763
def __send_mail(subject, message):
5080 amit.gupta 1764
    try:
1765
        thread = threading.Thread(target=partial(mail, from_user, from_pwd, to_addresses, subject, message))
1766
        thread.start()
1767
    except Exception as ex:
1768
        print ex    
5185 mandeep.dh 1769
 
1770
def get_shipping_locations():
1771
    shippingLocationIds = {}
1772
    warehouses = Warehouse.query.all()
1773
    for warehouse in warehouses:
1774
        if warehouse.shippingWarehouseId:
1775
            shippingLocationIds[warehouse.shippingWarehouseId] = 1
1776
 
1777
    shippingLocations = []
1778
    for shippingLocationId in shippingLocationIds:
1779
        shippingLocations.append(get_Warehouse(shippingLocationId))
1780
 
1781
    return shippingLocations