Subversion Repositories SmartDukaan

Rev

Rev 5213 | Rev 5295 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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