Subversion Repositories SmartDukaan

Rev

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