Subversion Repositories SmartDukaan

Rev

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