Subversion Repositories SmartDukaan

Rev

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

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