Subversion Repositories SmartDukaan

Rev

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

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