Subversion Repositories SmartDukaan

Rev

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

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