Subversion Repositories SmartDukaan

Rev

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

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