Subversion Repositories SmartDukaan

Rev

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