Subversion Repositories SmartDukaan

Rev

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