Subversion Repositories SmartDukaan

Rev

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