Subversion Repositories SmartDukaan

Rev

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