Subversion Repositories SmartDukaan

Rev

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

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