Subversion Repositories SmartDukaan

Rev

Rev 5885 | Rev 5978 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 5885 Rev 5944
Line 3... Line 3...
3
 
3
 
4
@author: ashish
4
@author: ashish
5
'''
5
'''
6
from elixir import *
6
from elixir import *
7
from functools import partial
7
from functools import partial
8
from pydoc import Helper
8
from shop2020.clients.CatalogClient import CatalogClient
9
from shop2020.clients.HelperClient import HelperClient
9
from shop2020.clients.HelperClient import HelperClient
10
from shop2020.clients.TransactionClient import TransactionClient
10
from shop2020.clients.InventoryClient import InventoryClient
11
from shop2020.config.client.ConfigClient import ConfigClient
11
from shop2020.config.client.ConfigClient import ConfigClient
12
from shop2020.model.v1.catalog.impl import DataService
12
from shop2020.model.v1.catalog.impl import DataService
13
from shop2020.model.v1.catalog.impl.CategoryManager import CategoryManager
13
from shop2020.model.v1.catalog.impl.CategoryManager import CategoryManager
14
from shop2020.model.v1.catalog.impl.Convertors import to_t_item, to_t_source
14
from shop2020.model.v1.catalog.impl.Convertors import to_t_item, to_t_source
15
from shop2020.model.v1.catalog.impl.DataService import Item, Warehouse, \
15
from shop2020.model.v1.catalog.impl.DataService import Item, ItemChangeLog, \
16
    ItemInventoryHistory, CurrentInventorySnapshot, ItemChangeLog, Category, \
-
 
17
    EntityIDGenerator, VendorItemPricing, VendorItemMapping, Vendor, SimilarItems, \
16
    Category, EntityIDGenerator, SimilarItems, ProductNotification, Source, \
18
    ProductNotification, Source, SourceItemPricing, AuthorizationLog, \
-
 
19
    MissedInventoryUpdate, BadInventorySnapshot, VendorItemProcurementDelay, \
-
 
20
    VendorHolidays, ItemAvailabilityCache, VoucherItemMapping
17
    SourceItemPricing, AuthorizationLog, VoucherItemMapping
21
from shop2020.thriftpy.model.v1.catalog.ttypes import InventoryServiceException, \
18
from shop2020.thriftpy.model.v1.catalog.ttypes import status, ItemShippingInfo, \
22
    status, ItemShippingInfo, HolidayType, InventoryType, WarehouseType, ItemType
19
    ItemType
23
from shop2020.thriftpy.model.v1.order.ttypes import AlertType
20
from shop2020.thriftpy.model.v1.inventory.ttypes import \
-
 
21
    InventoryServiceException
24
from shop2020.utils import EmailAttachmentSender
22
from shop2020.utils import EmailAttachmentSender
25
from shop2020.utils.EmailAttachmentSender import mail
23
from shop2020.utils.EmailAttachmentSender import mail
26
from shop2020.utils.Utils import to_py_date, log_risky_flag
24
from shop2020.utils.Utils import to_py_date, log_risky_flag
27
from sqlalchemy import desc, asc
25
from sqlalchemy import desc, asc
28
from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
-
 
29
from sqlalchemy.sql.expression import and_, or_, distinct, func
26
from sqlalchemy.sql.expression import or_, distinct, func
30
from string import Template
27
from string import Template
31
from urllib2 import HTTPBasicAuthHandler
-
 
32
import calendar
-
 
33
import datetime
28
import datetime
34
import sys
29
import sys
35
import threading
30
import threading
36
import urllib2
31
import urllib2
37
from shop2020.clients.CatalogClient import CatalogClient
-
 
38
 
32
 
39
to_addresses = ["cnc.center@shop2020.in", "ashutosh.saxena@shop2020.in"]
33
to_addresses = ["cnc.center@shop2020.in", "ashutosh.saxena@shop2020.in"]
40
from_user = "cnc.center@shop2020.in"
34
from_user = "cnc.center@shop2020.in"
41
from_pwd = "5h0p2o2o"
35
from_pwd = "5h0p2o2o"
42
skippedItems = { 175 : [27, 2160, 2175, 2163, 2158, 7128, 26, 2154],
36
skippedItems = { 175 : [27, 2160, 2175, 2163, 2158, 7128, 26, 2154],
Line 91... Line 85...
91
def is_active(item_id):
85
def is_active(item_id):
92
    t_item_shipping_info = ItemShippingInfo()
86
    t_item_shipping_info = ItemShippingInfo()
93
    try:
87
    try:
94
        item = get_item(item_id)
88
        item = get_item(item_id)
95
        t_item_shipping_info.isRisky = item.risky
89
        t_item_shipping_info.isRisky = item.risky
-
 
90
        client = InventoryClient().get_client()
96
        warehouse_id = get_item_availability_for_location(item.id)[0]
91
        itemInfo = client.getItemAvailabilityAtLocation(item.id)
-
 
92
        warehouse_id = itemInfo[0]
97
        if item.risky and item.status == status.ACTIVE:
93
        if item.risky and item.status == status.ACTIVE:
-
 
94
            availability = client.getItemAvailibilityAtWarehouse(warehouse_id, item_id)
98
            availability = 0
95
            if availability <= 0:
99
            currentInventorySnapshot = CurrentInventorySnapshot.query.filter_by(item_id = item.id, warehouse_id = warehouse_id).all()
-
 
100
            if not currentInventorySnapshot or currentInventorySnapshot[0].availability <= currentInventorySnapshot[0].reserved:
-
 
101
                add_status_change_log(item, status.PAUSED_BY_RISK)
96
                add_status_change_log(item, status.PAUSED_BY_RISK)
102
                item.status = status.PAUSED_BY_RISK
97
                item.status = status.PAUSED_BY_RISK
103
                item.status_description = "This item is currently out of stock"
98
                item.status_description = "This item is currently out of stock"
104
                session.commit()
99
                session.commit()
105
                __send_mail_for_oos_item(item)
100
                __send_mail_for_oos_item(item)
106
                #This will clear cache from tomcat
101
                #This will clear cache from tomcat
107
                __clear_homepage_cache()
102
                __clear_homepage_cache()
108
            else:
-
 
109
                availability = currentInventorySnapshot[0].availability - currentInventorySnapshot[0].reserved
-
 
110
        else:
103
        else:
111
            availability = item.get_total_inventory
104
            availability = itemInfo[4]
112
        t_item_shipping_info.isActive = (item.status == status.ACTIVE)
105
        t_item_shipping_info.isActive = (item.status == status.ACTIVE)
113
        t_item_shipping_info.quantity = availability
106
        t_item_shipping_info.quantity = availability
114
    except InventoryServiceException:
107
    except InventoryServiceException:
115
        print "[ERROR] Unexpected error:", sys.exc_info()[0]
108
        print "[ERROR] Unexpected error:", sys.exc_info()[0]
116
    return t_item_shipping_info
109
    return t_item_shipping_info
117
 
110
 
118
def get_item_status_description(itemId):
111
def get_item_status_description(itemId):
119
    item = get_item(itemId)
112
    item = get_item(itemId)
120
    return item.status_description
113
    return item.status_description
121
        
-
 
122
def get_Warehouse(warehouse_id):
-
 
123
    return Warehouse.get_by(id=warehouse_id)
-
 
124
 
-
 
125
def get_Vendor(vendorId):
-
 
126
    return Vendor.get_by(id=vendorId)
-
 
127
 
-
 
128
def get_all_warehouses_by_status(status):
-
 
129
    return Warehouse.query.all()
-
 
130
 
-
 
131
def get_all_warehouses_for_item(item_id):
-
 
132
    item = get_item(item_id)
-
 
133
    if not item:
-
 
134
        raise InventoryServiceException(108, "Some unforeseen error while obtaining item")
-
 
135
    return item.get_all_warehouses
-
 
136
 
-
 
137
def get_all_items_for_warehouse(warehouse_id):
-
 
138
    warehouse = get_Warehouse(warehouse_id)
-
 
139
    if not warehouse:
-
 
140
        raise InventoryServiceException(108, "bad warehouse")
-
 
141
    return warehouse.all_items
-
 
142
 
-
 
143
def add_warehouse(warehouse):
-
 
144
    if not warehouse:
-
 
145
        raise InventoryServiceException(108, "Bad warehouse")
-
 
146
    if get_Warehouse(warehouse.id):
-
 
147
        #warehouse is already present.
-
 
148
        raise InventoryServiceException(101, "Warehouse already present")
-
 
149
    
-
 
150
    ds_warehouse = Warehouse()
-
 
151
    ds_warehouse.location = warehouse.location
-
 
152
    ds_warehouse.status = status.ACTIVE
-
 
153
    ds_warehouse.addedOn = datetime.datetime.now()
-
 
154
    ds_warehouse.lastCheckedOn = datetime.datetime.now()
-
 
155
    ds_warehouse.tinNumber = warehouse.tinNumber
-
 
156
    ds_warehouse.pincode = warehouse.pincode
-
 
157
    ds_warehouse.billingType = warehouse.billingType
-
 
158
    ds_warehouse.billingWarehouseId = warehouse.billingWarehouseId
-
 
159
    ds_warehouse.displayName = warehouse.displayName
-
 
160
    ds_warehouse.inventoryType = InventoryType._VALUES_TO_NAMES[warehouse.inventoryType]
-
 
161
    ds_warehouse.isAvailabilityMonitored = warehouse.isAvailabilityMonitored
-
 
162
    ds_warehouse.logisticsLocation = warehouse.logisticsLocation
-
 
163
    ds_warehouse.shippingWarehouseId = warehouse.shippingWarehouseId
-
 
164
    ds_warehouse.transferDelayInHours = warehouse.transferDelayInHours
-
 
165
    ds_warehouse.vendor = get_Vendor(warehouse.vendor.id)
-
 
166
    ds_warehouse.warehouseType = WarehouseType._VALUES_TO_NAMES[warehouse.warehouseType]    
-
 
167
    if warehouse.vendorString:
-
 
168
        ds_warehouse.vendorString = warehouse.vendorString
-
 
169
    session.commit()
-
 
170
    return ds_warehouse.id
-
 
171
 
114
 
172
def update_item(item):
115
def update_item(item):
173
    if not item:
116
    if not item:
174
        raise InventoryServiceException(108, "Bad item in request")
117
        raise InventoryServiceException(108, "Bad item in request")
175
    
118
    
Line 258... Line 201...
258
    if item.expectedDelay is not None:
201
    if item.expectedDelay is not None:
259
        ds_item.expectedDelay = item.expectedDelay
202
        ds_item.expectedDelay = item.expectedDelay
260
    
203
    
261
    if item.preferredVendor:
204
    if item.preferredVendor:
262
        if item.preferredVendor != ds_item.preferredVendor:
205
        if item.preferredVendor != ds_item.preferredVendor:
-
 
206
            inventoryClient = InventoryClient().get_client()
263
            newPreferredVendorName = get_Vendor(item.preferredVendor).name
207
            newPreferredVendorName = inventoryClient.getVendor(item.preferredVendor).name
264
            oldPreferredVendorName = 'None'
208
            oldPreferredVendorName = 'None'
265
            if ds_item.preferredVendor:
209
            if ds_item.preferredVendor:
266
                oldPreferredVendorName = get_Vendor(ds_item.preferredVendor).name
210
                oldPreferredVendorName = inventoryClient.getVendor(ds_item.preferredVendor).name
267
            message += "Preferred vendor is changed from '{0}' to '{1}'.\n".format(oldPreferredVendorName, newPreferredVendorName)        
211
            message += "Preferred vendor is changed from '{0}' to '{1}'.\n".format(oldPreferredVendorName, newPreferredVendorName)        
268
        ds_item.preferredVendor = item.preferredVendor
212
        ds_item.preferredVendor = item.preferredVendor
269
    
213
    
270
    if item.isWarehousePreferenceSticky != ds_item.isWarehousePreferenceSticky:
214
    if item.isWarehousePreferenceSticky != ds_item.isWarehousePreferenceSticky:
271
        flag = "ON" if item.isWarehousePreferenceSticky else "OFF"
215
        flag = "ON" if item.isWarehousePreferenceSticky else "OFF"
Line 349... Line 293...
349
        ds_item.expectedDelay = 0
293
        ds_item.expectedDelay = 0
350
    
294
    
351
    preferredVendorName = "None"
295
    preferredVendorName = "None"
352
    if item.preferredVendor:
296
    if item.preferredVendor:
353
        ds_item.preferredVendor = item.preferredVendor
297
        ds_item.preferredVendor = item.preferredVendor
-
 
298
        inventoryClient = InventoryClient().get_client()
354
        preferredVendorName = get_Vendor(item.preferredVendor).name
299
        preferredVendorName = inventoryClient.getVendor(item.preferredVendor).name        
355
    
300
    
356
    if item.catalogItemId:
301
    if item.catalogItemId:
357
        catalog_client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
302
        catalog_client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
358
        master_items = catalog_client.getItemsByCatalogId(item.catalogItemId)
303
        master_items = catalog_client.getItemsByCatalogId(item.catalogItemId)
359
        itemStatus = status.IN_PROCESS
304
        itemStatus = status.IN_PROCESS
Line 392... Line 337...
392
    subject = "New item is added. Id is {0}".format(str(ds_item.id))
337
    subject = "New item is added. Id is {0}".format(str(ds_item.id))
393
    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)
338
    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)
394
    __send_mail(subject, message)
339
    __send_mail(subject, message)
395
    return ds_item.id
340
    return ds_item.id
396
 
341
 
397
def update_inventory_history(warehouse_id, timestamp, availability):
-
 
398
    warehouse = get_Warehouse(warehouse_id)
-
 
399
    if not warehouse:
-
 
400
        raise InventoryServiceException(107, "Warehouse? Where?")
-
 
401
    vendor = warehouse.vendor
-
 
402
    time = datetime.datetime.now()
-
 
403
    for item_key, quantity in availability.iteritems():
-
 
404
        try:
-
 
405
            vendor_item_mapping = VendorItemMapping.query.filter_by(vendor=vendor, item_key=item_key).one();
-
 
406
            item = vendor_item_mapping.item
-
 
407
        except:
-
 
408
            continue  
-
 
409
        try:
-
 
410
            item_inventory_history = ItemInventoryHistory()
-
 
411
            item_inventory_history.warehouse = warehouse
-
 
412
            item_inventory_history.item = item
-
 
413
            item_inventory_history.timestamp = time
-
 
414
            item_inventory_history.availability = quantity
-
 
415
        except:
-
 
416
            raise InventoryServiceException(108, "Some unforeseen error while updating inventory")
-
 
417
    session.commit()
-
 
418
    
-
 
419
def update_inventory(warehouse_id, timestamp, availability):
-
 
420
    warehouse = get_Warehouse(warehouse_id)
-
 
421
    if not warehouse:
-
 
422
        raise InventoryServiceException(107, "Warehouse? Where?")
-
 
423
     
-
 
424
    time = datetime.datetime.now()
-
 
425
    warehouse.lastCheckedOn = time
-
 
426
    warehouse.vendorString = timestamp
-
 
427
    vendor = warehouse.vendor
-
 
428
    session.commit()
-
 
429
    items = []
-
 
430
    for item_key, quantity in availability.iteritems():
-
 
431
        try:
-
 
432
            vendor_item_mapping = VendorItemMapping.query.filter_by(vendor=vendor, item_key=item_key).one();
-
 
433
            item = vendor_item_mapping.item
-
 
434
            items.append(item)
-
 
435
        except:
-
 
436
            print 'Skipping update for ' + item_key + ' quantity ' + str(quantity) + ' warehouse id: ' + str(warehouse_id)
-
 
437
            __send_mail_for_missing_key(item_key, quantity, warehouse_id)
-
 
438
            continue
-
 
439
        try:
-
 
440
            current_inventory_snapshot = CurrentInventorySnapshot.get_by(item=item, warehouse=warehouse)
-
 
441
            if not current_inventory_snapshot:
-
 
442
                current_inventory_snapshot = CurrentInventorySnapshot()
-
 
443
                current_inventory_snapshot.item = item
-
 
444
                current_inventory_snapshot.warehouse = warehouse
-
 
445
                current_inventory_snapshot.availability = 0
-
 
446
                current_inventory_snapshot.reserved = 0
-
 
447
            # added the difference in the current inventory    
-
 
448
            current_inventory_snapshot.availability = current_inventory_snapshot.availability + quantity
-
 
449
            try:
-
 
450
                if quantity > 0 and __get_item_reserved(item) > 0:
-
 
451
                    cl = TransactionClient().get_client()
-
 
452
                    #FIXME hardcoding for warehouse id 
-
 
453
                    cl.addAlert(AlertType.NEW_INVENTORY_ALERT, 5, "Inventory received for item " + item.brand + " " + item.model_name + " " + item.model_number + " " +  item.color)
-
 
454
            except:
-
 
455
                print "Not able to raise alert for incoming inventory" 
-
 
456
            if current_inventory_snapshot.availability < 0:
-
 
457
                __send_alert_for_negative_availability(item, current_inventory_snapshot.availability, warehouse)
-
 
458
        except:
-
 
459
            raise InventoryServiceException(108, "Some unforeseen error while updating inventory")
-
 
460
        session.commit() 
-
 
461
        #**Update item availability cache**#
-
 
462
        for item in items:
-
 
463
            __update_item_availability_cache(item.id)
-
 
464
            check_risky_item(item)
-
 
465
 
-
 
466
def __send_alert_for_negative_reserved(item, reserved, warehouse):
-
 
467
    itemName = " ".join([str(item.id), str(item.brand), str(item.model_name), str(item.model_number), str(item.color)])
-
 
468
    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)
-
 
469
 
-
 
470
def __send_alert_for_negative_availability(item, availability, warehouse):
-
 
471
    itemName = " ".join([str(item.id), str(item.brand), str(item.model_name), str(item.model_number), str(item.color)])
-
 
472
    # 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)
-
 
473
 
-
 
474
def __send_mail_for_missing_key(item_key, quantity, warehouse_id):
-
 
475
    missedInventoryUpdate = MissedInventoryUpdate.get_by(itemKey = item_key, warehouseId = warehouse_id)
-
 
476
    # One email per product key mismatch
-
 
477
    if not missedInventoryUpdate:
-
 
478
        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)
-
 
479
        missedInventoryUpdate = MissedInventoryUpdate()
-
 
480
        missedInventoryUpdate.itemKey = item_key
-
 
481
        missedInventoryUpdate.quantity = quantity
-
 
482
        missedInventoryUpdate.isIgnored = 1
-
 
483
        missedInventoryUpdate.timestamp = datetime.datetime.now()
-
 
484
        missedInventoryUpdate.warehouseId = warehouse_id
-
 
485
        session.commit()
-
 
486
    else:
-
 
487
        missedInventoryUpdate.quantity += quantity
-
 
488
        session.commit()
-
 
489
 
-
 
490
def add_inventory(itemId, warehouseId, quantity):
-
 
491
    current_inventory_snapshot = CurrentInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)
-
 
492
    if not current_inventory_snapshot:
-
 
493
        current_inventory_snapshot = CurrentInventorySnapshot()
-
 
494
        current_inventory_snapshot.item_id = itemId
-
 
495
        current_inventory_snapshot.warehouse_id = warehouseId
-
 
496
        current_inventory_snapshot.availability = 0
-
 
497
        current_inventory_snapshot.reserved = 0
-
 
498
    # added the difference in the current inventory    
-
 
499
    current_inventory_snapshot.availability = current_inventory_snapshot.availability + quantity
-
 
500
    session.commit()
-
 
501
    #**Update item availability cache**#
-
 
502
    __update_item_availability_cache(itemId)
-
 
503
    if current_inventory_snapshot.availability < 0:
-
 
504
        __send_alert_for_negative_availability(get_item(itemId), current_inventory_snapshot.availability, get_Warehouse(warehouseId))
-
 
505
    check_risky_item(get_item(itemId)) 
-
 
506
 
-
 
507
def add_bad_inventory(itemId, warehouseId, quantity):
-
 
508
    bad_inventory_snapshot = BadInventorySnapshot.get_by(item_id=itemId, warehouse_id=warehouseId)
-
 
509
    if not bad_inventory_snapshot:
-
 
510
        bad_inventory_snapshot = BadInventorySnapshot()
-
 
511
        bad_inventory_snapshot.item_id = itemId
-
 
512
        bad_inventory_snapshot.warehouse_id = warehouseId
-
 
513
        bad_inventory_snapshot.availability = 0
-
 
514
    # added the difference in the current inventory    
-
 
515
    bad_inventory_snapshot.availability += quantity
-
 
516
    session.commit()
-
 
517
    if bad_inventory_snapshot.availability < 0:
-
 
518
        __send_alert_for_negative_availability(get_item(itemId), bad_inventory_snapshot.availability, get_Warehouse(warehouseId))
-
 
519
 
-
 
520
def get_item_inventory_by_item_id(item_id):
-
 
521
    return Item.get_by(id=item_id).currentInventory
-
 
522
    
-
 
523
def retire_warehouse(warehouse_id):
-
 
524
    if not warehouse_id:
-
 
525
        raise InventoryServiceException(101, "Bad warehouse id")
-
 
526
    warehouse = get_Warehouse(warehouse_id)
-
 
527
    if not warehouse:
-
 
528
        raise InventoryServiceException(108, "warehouse id not present")
-
 
529
    warehouse.status = status.DELETED;
-
 
530
    session.commit()
-
 
531
    
-
 
532
def retire_item(item_id):
342
def retire_item(item_id):
533
    if not item_id:
343
    if not item_id:
534
        raise InventoryServiceException(101, "bad item id")
344
        raise InventoryServiceException(101, "bad item id")
535
    item = get_item(item_id)
345
    item = get_item(item_id)
536
    if not item:
346
    if not item:
Line 599... Line 409...
599
        item.status_description = "This item is in process"
409
        item.status_description = "This item is in process"
600
    elif item.status == status.CONTENT_COMPLETE:
410
    elif item.status == status.CONTENT_COMPLETE:
601
        item.status_description = "This item is in process"
411
        item.status_description = "This item is in process"
602
    session.commit()
412
    session.commit()
603
    
413
    
604
def get_item_availability_for_warehouse(warehouse_id, item_id):
-
 
605
    if not warehouse_id:
-
 
606
        raise InventoryServiceException(101, "bad warehouse_id")
-
 
607
    if not item_id:
414
def check_risky_item(item_id):
608
        raise InventoryServiceException(101, "bad item_id")
-
 
609
    
-
 
610
    warehouse = get_Warehouse(warehouse_id)
-
 
611
    if not warehouse:
-
 
612
        raise InventoryServiceException(108, "warehouse does not exist")
-
 
613
    item = get_item(item_id)
415
    item = get_item(item_id)
614
 
-
 
615
    if not item:
-
 
616
        raise InventoryServiceException(108, "item does not exist")
-
 
617
        
-
 
618
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id)
-
 
619
    query = query.filter_by(item_id = item.id)
-
 
620
    try:
-
 
621
        current_inventory_snapshot = query.one()
-
 
622
        return current_inventory_snapshot.availability - current_inventory_snapshot.reserved
-
 
623
    except:
-
 
624
        return 0
-
 
625
    """
-
 
626
    current_inventory_snapshot = CurrentInventorySnapshot.query.filter(CurrentInventorySnapshot.warehouse_id == warehouse_id, CurrentInventorySnapshot.item_id == item_id).one()
-
 
627
    if not current_inventory_snapshot:
-
 
628
        return 0
-
 
629
    else:
-
 
630
        return current_inventory_snapshot.availability
-
 
631
    """
-
 
632
    
-
 
633
def check_risky_item(item):
-
 
634
    if not item.risky:
416
    if not item.risky:
635
        return
417
        return
-
 
418
    client = InventoryClient().get_client()
636
    warehouse_id = get_item_availability_for_location(item.id)[0]
419
    itemInfo = client.getItemAvailabilityAtLocation(item.id)    
-
 
420
    warehouse_id = itemInfo[0]
637
    currentInventorySnapshot = CurrentInventorySnapshot.query.filter_by(item_id = item.id, warehouse_id = warehouse_id).all()
421
    availability = client.getItemAvailibilityAtWarehouse(warehouse_id, item.id)
638
    if not currentInventorySnapshot or currentInventorySnapshot[0].availability <= currentInventorySnapshot[0].reserved:
422
    if availability <= 0:
639
        if item.status == status.ACTIVE:
423
        if item.status == status.ACTIVE:
640
            change_item_status(item.id, status.PAUSED_BY_RISK)
424
            change_item_status(item.id, status.PAUSED_BY_RISK)
641
            __send_mail_for_oos_item(item)
425
            __send_mail_for_oos_item(item)
642
    else:
426
    else:
643
        if item.status == status.PAUSED_BY_RISK:
427
        if item.status == status.PAUSED_BY_RISK:
644
            change_item_status(item.id, status.ACTIVE)
428
            change_item_status(item.id, status.ACTIVE)
645
    session.commit()
429
    session.commit()
646
 
-
 
647
'''
-
 
648
This method returns quantity of a particular item across all warehouses whose ids is provided
-
 
649
if warehouse_ids is null it checks for inventory in all warehouses.
-
 
650
'''
-
 
651
def __get_item_availability(item, warehouse_ids):
-
 
652
    if warehouse_ids is None:
-
 
653
        all_inventory = CurrentInventorySnapshot.query.filter_by(item = item).all()
-
 
654
        availability = 0
-
 
655
        reserved = 0
-
 
656
        for currInv in all_inventory:
-
 
657
            availability = availability + currInv.availability
-
 
658
            reserved = reserved + currInv.reserved
-
 
659
        return availability - reserved
-
 
660
    else:
-
 
661
        total_availability = 0
-
 
662
        for current_inventory_snapshot in CurrentInventorySnapshot.query.filter(CurrentInventorySnapshot.warehouse_id.in_(warehouse_ids)).filter_by(item_id = item.id).all():
-
 
663
            total_availability += current_inventory_snapshot.availability - current_inventory_snapshot.reserved
-
 
664
        return total_availability 
-
 
665
 
-
 
666
def __get_item_reserved(item):
-
 
667
    all_inventory = CurrentInventorySnapshot.query.filter_by(item = item).all()
-
 
668
    reserved = 0
-
 
669
    for currInv in all_inventory:
-
 
670
        reserved = reserved + currInv.reserved
-
 
671
    return reserved
-
 
672
    
-
 
673
def reserve_item_in_warehouse(item_id, warehouse_id, quantity):    
-
 
674
    if not warehouse_id:
-
 
675
        raise InventoryServiceException(101, "bad warehouse_id")
-
 
676
    item = get_item(item_id)
-
 
677
    if not item:
-
 
678
        raise InventoryServiceException(101, "bad item_id")
-
 
679
        
-
 
680
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)
-
 
681
    try:
-
 
682
        current_inventory_snapshot = query.one()
-
 
683
    except:
-
 
684
        current_inventory_snapshot = CurrentInventorySnapshot()
-
 
685
        current_inventory_snapshot.warehouse_id = warehouse_id
-
 
686
        current_inventory_snapshot.item_id = item_id
-
 
687
        current_inventory_snapshot.availability = 0
-
 
688
        current_inventory_snapshot.reserved = 0
-
 
689
        
-
 
690
    current_inventory_snapshot.reserved = current_inventory_snapshot.reserved + quantity
-
 
691
    session.commit()
-
 
692
    #**Update item availability cache**#
-
 
693
    __update_item_availability_cache(item_id)
-
 
694
    check_risky_item(item)
-
 
695
    return True
-
 
696
 
-
 
697
def reduce_reservation_count(item_id, warehouse_id, quantity):
-
 
698
    if not warehouse_id:
-
 
699
        raise InventoryServiceException(101, "bad warehouse_id")
-
 
700
    item = get_item(item_id)
-
 
701
    if not item:
-
 
702
        raise InventoryServiceException(101, "bad item_id")
-
 
703
        
-
 
704
    query = CurrentInventorySnapshot.query.filter_by(warehouse_id = warehouse_id, item_id = item_id)
-
 
705
    try:
-
 
706
        current_inventory_snapshot = query.one()
-
 
707
        current_inventory_snapshot.reserved = current_inventory_snapshot.reserved - quantity
-
 
708
        session.commit()
-
 
709
        #**Update item availability cache**#
-
 
710
        __update_item_availability_cache(item_id)
-
 
711
        check_risky_item(item)
-
 
712
        if current_inventory_snapshot.reserved < 0:
-
 
713
            __send_alert_for_negative_reserved(get_item(item_id), current_inventory_snapshot.reserved, get_Warehouse(warehouse_id))
-
 
714
        return True
-
 
715
    except:
-
 
716
        print "Unexpected error:", sys.exc_info()[0]
-
 
717
        return False
-
 
718
    
430
    
719
def mark_item_as_content_complete(entity_id, category, brand, modelName, modelNumber):
431
def mark_item_as_content_complete(entity_id, category, brand, modelName, modelNumber):
720
    '''
432
    '''
721
    Get all the items for this entityID and update category, brand, modelName and modelNumber for all.
433
    Get all the items for this entityID and update category, brand, modelName and modelNumber for all.
722
    Update Status for only IN_PROCESS items to CONTENT_COMPLETE
434
    Update Status for only IN_PROCESS items to CONTENT_COMPLETE
Line 742... Line 454...
742
        item.model_number = modelNumber
454
        item.model_number = modelNumber
743
        item.updatedOn = current_timestamp
455
        item.updatedOn = current_timestamp
744
    session.commit()
456
    session.commit()
745
    return True
457
    return True
746
 
458
 
747
def get_item_availability_for_location(item_id):
-
 
748
    item_availability = ItemAvailabilityCache.get_by(itemId=item_id)
-
 
749
    if item_availability:
-
 
750
        return [item_availability.warehouseId, item_availability.expectedDelay, item_availability.billingWarehouseId, item_availability.sellingPrice]
-
 
751
    else:
-
 
752
        __update_item_availability_cache(item_id)
-
 
753
        return get_item_availability_for_location(item_id)
-
 
754
    
-
 
755
def clear_item_availability_cache():
-
 
756
    ItemAvailabilityCache.query.delete()
-
 
757
    session.commit()
-
 
758
 
-
 
759
def __update_item_availability_cache(item_id):
-
 
760
    """
-
 
761
    Determines the warehouse that should be used to fulfil an order for the given item.
-
 
762
    Algorithm explained at https://sites.google.com/a/shop2020.in/virtual-w-h-and-inventory/technical-details
-
 
763
 
-
 
764
    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.
-
 
765
    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.
-
 
766
    
-
 
767
    if item available at any OUR-GOOD warehouse
-
 
768
        // OUR-GOOD warehouses have inventory risk; So, we empty them first! 
-
 
769
        // We can start with minimum transfer price criterion but down the line we can also bring in Inventory age 
-
 
770
        assign OUR-GOOD warehouse with minimum transfer price
-
 
771
    else
-
 
772
        if Preferred vendor is specified and marked Sticky
-
 
773
            // Always purchase from Preferred if its marked sticky
-
 
774
            assign preferred vendor's THIRDPARTY GOOD/VIRTUAL warehouse
-
 
775
        else 
-
 
776
            if item available in a THIRDPARTY GOOD/VIRTUAL warehouse
-
 
777
                assign THIRDPARTY GOOD/VIRTUAL warehouse where item is available with minimal transfer delay followed by minimum transfer price
-
 
778
            else 
-
 
779
                // Item not available at any warehouse, OURS or THIRDPARTY
-
 
780
                If Preferred vendor is specified
-
 
781
                    assign preferred vendor's THIRDPARTY GOOD/VIRTUAL warehouse
-
 
782
                else
-
 
783
                    assign THIRDPARTY GOOD/VIRTUAL warehouse with minimum transfer price
-
 
784
    
-
 
785
    Returns an ordered list of size 4 with following elements in the given order:
-
 
786
    1. Logistics location of the warehouse which was finally picked up to ship the order.
-
 
787
    2. Expected delay added by the category manager.
-
 
788
    3. Id of the warehouse which was finally picked up.
-
 
789
 
-
 
790
    Parameters:
-
 
791
     - itemId
-
 
792
    """
-
 
793
    item = Item.get_by(id=item_id)
-
 
794
    item_pricing = {}
-
 
795
    for vendorItemPricing in VendorItemPricing.query.filter_by(item=item).all():
-
 
796
        item_pricing[vendorItemPricing.vendor_id] = vendorItemPricing
-
 
797
 
-
 
798
    warehouses = {}
-
 
799
    ourGoodWarehouses = {}
-
 
800
    thirdpartyWarehouses = {}
-
 
801
    preferredThirdpartyWarehouses = {}
-
 
802
    for warehouse in Warehouse.query.all():
-
 
803
        if (warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.BAD]):
-
 
804
            continue
-
 
805
        warehouses[warehouse.id] = warehouse
-
 
806
        if warehouse.warehouseType == WarehouseType._VALUES_TO_NAMES[WarehouseType.OURS]:
-
 
807
            if warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]:
-
 
808
                ourGoodWarehouses[warehouse.id] = warehouse
-
 
809
        else:
-
 
810
            thirdpartyWarehouses[warehouse.id] = warehouse
-
 
811
            if item.preferredVendor == warehouse.vendor_id and warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]:
-
 
812
                preferredThirdpartyWarehouses[warehouse.id] = warehouse
-
 
813
 
-
 
814
    warehouse_retid = -1
-
 
815
    total_availability = 0
-
 
816
 
-
 
817
    [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_price(ourGoodWarehouses, item, item_pricing, False)
-
 
818
    if warehouse_retid == -1:
-
 
819
        if item.preferredVendor and item.isWarehousePreferenceSticky:
-
 
820
            [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_delay(preferredThirdpartyWarehouses, item, item_pricing)
-
 
821
            if warehouse_retid == -1:
-
 
822
                warehouse_retid = preferredThirdpartyWarehouses.keys()[0]
-
 
823
        else:
-
 
824
            [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_delay(thirdpartyWarehouses, item, item_pricing)
-
 
825
            if warehouse_retid == -1:
-
 
826
                if item.preferredVendor:
-
 
827
                    warehouse_retid = preferredThirdpartyWarehouses.keys()[0]
-
 
828
                else:
-
 
829
                    [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_price(thirdpartyWarehouses, item, item_pricing, True)
-
 
830
 
-
 
831
    warehouse = warehouses[warehouse_retid]
-
 
832
    billingWarehouseId = warehouse.billingWarehouseId
-
 
833
 
-
 
834
    # Fetching billing warehouse of a Good billable warehouse corresponding to the virtual one
-
 
835
    if not warehouse.billingWarehouseId:
-
 
836
        for w in Warehouse.query.filter_by(vendor_id = warehouse.vendor_id, inventoryType = InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]).all():
-
 
837
            if w.billingWarehouseId:
-
 
838
                billingWarehouseId = w.billingWarehouseId
-
 
839
                break
-
 
840
 
-
 
841
    expectedDelay = item.expectedDelay 
-
 
842
    if expectedDelay is None:
-
 
843
        print 'expectedDelay field for this item was Null. Resetting it to 0'
-
 
844
        expectedDelay = 0
-
 
845
    else:
-
 
846
        expectedDelay = int(item.expectedDelay)
-
 
847
 
-
 
848
    if total_availability <= 0:
-
 
849
        expectedDelay = expectedDelay + __get_expected_procurement_delay(item)
-
 
850
        expectedDelay = expectedDelay + __get_vendor_holiday_delay(item, expectedDelay)
-
 
851
    else:
-
 
852
        if warehouse.transferDelayInHours:
-
 
853
            expectedDelay = expectedDelay + warehouse.transferDelayInHours / 24
-
 
854
 
-
 
855
    item_availability_cache = ItemAvailabilityCache.get_by(itemId=item_id)
-
 
856
    if item_availability_cache is None:
-
 
857
        item_availability_cache = ItemAvailabilityCache()
-
 
858
        item_availability_cache.itemId = item_id
-
 
859
    item_availability_cache.warehouseId = int(warehouse_retid)
-
 
860
    item_availability_cache.expectedDelay = expectedDelay
-
 
861
    item_availability_cache.billingWarehouseId = billingWarehouseId
-
 
862
    item_availability_cache.sellingPrice = item.sellingPrice
-
 
863
    session.commit()
-
 
864
 
-
 
865
def __get_warehouse_with_min_transfer_price(warehouses, item, item_pricing, ignoreAvailability):
-
 
866
    warehouse_retid = -1
-
 
867
    minTransferPrice = None
-
 
868
    total_availability = 0
-
 
869
    warehousesWithAvailability = {}
-
 
870
    
-
 
871
    if not ignoreAvailability:
-
 
872
        if item.currentInventory:
-
 
873
            for entry in item.currentInventory:
-
 
874
                if entry.availability > entry.reserved:
-
 
875
                    warehousesWithAvailability[entry.warehouse_id] = entry
-
 
876
 
-
 
877
    for warehouse in warehouses.values():
-
 
878
        if not ignoreAvailability:
-
 
879
            if warehousesWithAvailability.has_key(warehouse.id):
-
 
880
                entry = warehousesWithAvailability[warehouse.id]
-
 
881
                total_availability += entry.availability - entry.reserved
-
 
882
            else:
-
 
883
                continue
-
 
884
 
-
 
885
        # Missing transfer price cases should not impact warehouse assignment
-
 
886
        transferPrice = None
-
 
887
        if item_pricing.has_key(warehouse.vendor_id):
-
 
888
            transferPrice = item_pricing[warehouse.vendor_id].transfer_price
-
 
889
        if minTransferPrice is None or (transferPrice and minTransferPrice > transferPrice):
-
 
890
            warehouse_retid = warehouse.id
-
 
891
            minTransferPrice = transferPrice
-
 
892
 
-
 
893
    return [warehouse_retid, total_availability]
-
 
894
 
-
 
895
def __get_warehouse_with_min_transfer_delay(warehouses, item, item_pricing):
-
 
896
    minTransferDelay = None
-
 
897
    minTransferDelayWarehouses = {}
-
 
898
    total_availability = 0
-
 
899
 
-
 
900
    if item.currentInventory:
-
 
901
        for entry in item.currentInventory:
-
 
902
            if warehouses.has_key(entry.warehouse_id):
-
 
903
                warehouse = warehouses[entry.warehouse_id]
-
 
904
                if entry.availability > entry.reserved:
-
 
905
                    total_availability += entry.availability - entry.reserved
-
 
906
                    transferDelay = warehouse.transferDelayInHours
-
 
907
                    if minTransferDelay is None or minTransferDelay >= transferDelay:
-
 
908
                        if minTransferDelay != transferDelay:
-
 
909
                            minTransferDelayWarehouses = {}
-
 
910
                        minTransferDelayWarehouses[warehouse.id] = warehouse
-
 
911
                        minTransferDelay = transferDelay
-
 
912
 
-
 
913
    return [__get_warehouse_with_min_transfer_price(minTransferDelayWarehouses, item, item_pricing, False)[0], total_availability]
-
 
914
 
-
 
915
def __get_warehouse_with_max_availability(warehouse_ids, item):
-
 
916
    warehouse_retid = -1
-
 
917
    max_availability = 0
-
 
918
    total_availability = 0
-
 
919
 
-
 
920
    if item.currentInventory:
-
 
921
        for entry in item.currentInventory:
-
 
922
            if entry.warehouse_id in warehouse_ids:
-
 
923
                availability = entry.availability - entry.reserved
-
 
924
                if availability > max_availability:
-
 
925
                    warehouse_retid = entry.warehouse_id
-
 
926
                    max_availability = availability
-
 
927
                total_availability += availability
-
 
928
 
-
 
929
    return [warehouse_retid, total_availability]
-
 
930
 
-
 
931
def __get_expected_procurement_delay(item):
-
 
932
    procurementDelay = 2
-
 
933
    try:
-
 
934
        if item.preferredVendor:
-
 
935
            delays = VendorItemProcurementDelay.query.filter_by(vendor_id = item.preferredVendor, item_id = item.id).all()
-
 
936
        else:
-
 
937
            delays = VendorItemProcurementDelay.query.filter_by(item_id = item.id).all()
-
 
938
        
-
 
939
        procurementDelay= min([delay.procurementDelay for delay in delays])
-
 
940
    except Exception as e:
-
 
941
        print e
-
 
942
    return procurementDelay
-
 
943
    
-
 
944
def __get_vendor_holiday_delay(item, expectedDelay):
-
 
945
    holidayDelay = 0
-
 
946
    try:
-
 
947
        if item.preferredVendor:
-
 
948
            holidays = VendorHolidays.query.filter_by(vendor_id = item.preferredVendor).all()
-
 
949
            currentDate = datetime.date.today()
-
 
950
            expectedDate = currentDate + datetime.timedelta(days = expectedDelay)
-
 
951
            for holiday in holidays:
-
 
952
                if holiday.holidayType == HolidayType.WEEKLY and holiday.holidayValue != calendar.SUNDAY:
-
 
953
                    if currentDate.weekday() > holiday.holidayValue:
-
 
954
                        holidayDate = currentDate + datetime.timedelta(days=holiday.holidayValue-currentDate.weekday(), weeks=1)
-
 
955
                    else:
-
 
956
                        holidayDate = currentDate + datetime.timedelta(days=holiday.holidayValue-currentDate.weekday())
-
 
957
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
-
 
958
                        holidayDelay = holidayDelay + 1
-
 
959
                elif holiday.holidayType == HolidayType.MONTHLY:
-
 
960
                    holidayDate = datetime.date(currentDate.year, currentDate.month, holiday.holidayValue)
-
 
961
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
-
 
962
                        holidayDelay = holidayDelay + 1    
-
 
963
                elif holiday.holidayType == HolidayType.SPECIFIC:
-
 
964
                    holidayValue = str(holiday.holidayValue)
-
 
965
                    holidayDate = datetime.date(int(holidayValue[:4]), int(holidayValue[4:6]), int(holidayValue[6:8]))
-
 
966
                    if holidayDate >=  currentDate and holidayDate <= expectedDate:
-
 
967
                        holidayDelay = holidayDelay + 1                
-
 
968
    except Exception as e:
-
 
969
        print e
-
 
970
    return holidayDelay 
-
 
971
 
-
 
972
def get_warehouses_for_item(item_id):
-
 
973
    
-
 
974
    if not item_id:
-
 
975
        raise InventoryServiceException(101, "bad item_id")
-
 
976
    item = get_item(item_id)
-
 
977
    
-
 
978
    if not item:
-
 
979
        raise InventoryServiceException(101, "bad item")
-
 
980
    
-
 
981
    warehouses = item.currentInventory.warehouse
-
 
982
    return warehouses
-
 
983
 
-
 
984
def get_child_categories(category):
459
def get_child_categories(category):
985
    cm = CategoryManager()
460
    cm = CategoryManager()
986
    cat = cm.getCategory(category)
461
    cat = cm.getCategory(category)
987
    return cat.children_category_ids if cat else None
462
    return cat.children_category_ids if cat else None
988
 
463
 
Line 1216... Line 691...
1216
 
691
 
1217
def get_category_object():
692
def get_category_object():
1218
    object = Category.get_by(id=1).object
693
    object = Category.get_by(id=1).object
1219
    return object
694
    return object
1220
 
695
 
1221
def get_item_pricing(item_id, vendorId):
-
 
1222
    item = Item.query.filter_by(id=item_id).first()
-
 
1223
    if item is None:
-
 
1224
        raise InventoryServiceException(101, "Bad Item")
-
 
1225
    '''
-
 
1226
    if vendor id is -1 then we calculate an average transfer price to be populated
-
 
1227
    at the time of order creation. This will be later updated with actual transfer price
-
 
1228
    at the time of billing.
-
 
1229
    '''
-
 
1230
    if(vendorId == -1):
-
 
1231
        total = 0
-
 
1232
        try:
-
 
1233
            item_pricings = []
-
 
1234
            if item.preferredVendor is not None:
-
 
1235
                item_pricing = VendorItemPricing.query.filter_by(item=item, vendor_id=item.preferredVendor).first()
-
 
1236
                if item_pricing:
-
 
1237
                    item_pricings.append(item_pricing)                    
-
 
1238
            else :
-
 
1239
                item_pricings = VendorItemPricing.query.filter_by(item=item).all()
-
 
1240
            if item_pricings:
-
 
1241
                for item_pricing in item_pricings:
-
 
1242
                    total += item_pricing.transfer_price
-
 
1243
                avg = total / len(item_pricings)
-
 
1244
                item_pricing.transfer_price = avg
-
 
1245
            else:
-
 
1246
                item_pricing = VendorItemPricing()
-
 
1247
                item_pricing.transfer_price = item.sellingPrice
-
 
1248
                vendor = Vendor()
-
 
1249
                vendor.id = vendorId
-
 
1250
                item_pricing.vendor = vendor
-
 
1251
                item_pricing.item = item
-
 
1252
                
-
 
1253
            return item_pricing
-
 
1254
        except:
-
 
1255
            raise InventoryServiceException(101, "Item pricing not found ")
-
 
1256
    vendor = Vendor.get_by(id=vendorId)    
-
 
1257
    try:
-
 
1258
        item_pricing = VendorItemPricing.query.filter_by(vendor=vendor, item=item).one()
-
 
1259
        return item_pricing
-
 
1260
    except MultipleResultsFound:
-
 
1261
        raise InventoryServiceException(110, "Multiple pricing information present for Vendor: " + vendor.name + " and Item: " + str(item_id))
-
 
1262
    except NoResultFound:
-
 
1263
        raise InventoryServiceException(111, "Missing pricing information for Vendor: " + vendor.name + " and Item: " + str(item_id))
-
 
1264
 
-
 
1265
def add_category(t_category):
696
def add_category(t_category):
1266
    category = Category.get_by(id=t_category.id)
697
    category = Category.get_by(id=t_category.id)
1267
    if category is None:
698
    if category is None:
1268
        category = Category()
699
        category = Category()
1269
    category.id = t_category.id 
700
    category.id = t_category.id 
Line 1278... Line 709...
1278
    return Category.query.filter_by(id=id).first()
709
    return Category.query.filter_by(id=id).first()
1279
 
710
 
1280
def get_all_categories():
711
def get_all_categories():
1281
    return Category.query.all()
712
    return Category.query.all()
1282
 
713
 
1283
 
-
 
1284
def get_all_item_pricing(item_id):
-
 
1285
    item = Item.query.filter_by(id=item_id).first()
-
 
1286
    if item is None:
-
 
1287
        raise InventoryServiceException(101, "Bad Item")
-
 
1288
    item_pricing = VendorItemPricing.query.filter_by(item=item).all()
-
 
1289
    return item_pricing
-
 
1290
 
-
 
1291
def get_item_mappings(item_id):
-
 
1292
    item = Item.query.filter_by(id=item_id).first()
-
 
1293
    if item is None:
-
 
1294
        raise InventoryServiceException(101, "Bad Item")
-
 
1295
    item_mappings = VendorItemMapping.query.filter_by(item=item).all()
-
 
1296
    return item_mappings
-
 
1297
 
-
 
1298
def add_vendor_pricing(vendorItemPricing):
-
 
1299
    if not vendorItemPricing:
-
 
1300
        raise InventoryServiceException(108, "Bad vendorItemPricing in request")
-
 
1301
    vendorId = vendorItemPricing.vendorId
-
 
1302
    itemId = vendorItemPricing.itemId
-
 
1303
    
-
 
1304
    try:
-
 
1305
        vendor = Vendor.query.filter_by(id=vendorId).one()
-
 
1306
    except:
-
 
1307
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
-
 
1308
    
-
 
1309
    try:
-
 
1310
        item = Item.query.filter_by(id=itemId).one()
-
 
1311
    except:
-
 
1312
        raise InventoryServiceException(101, "Item not found for itemId " + str(itemId))
-
 
1313
    
-
 
1314
    validate_vendor_prices(to_t_item(item), vendorItemPricing)
-
 
1315
    
-
 
1316
    try:
-
 
1317
        ds_vendorItemPricing = VendorItemPricing.query.filter(and_(VendorItemPricing.vendor==vendor, VendorItemPricing.item==item)).one()
-
 
1318
    except:
-
 
1319
        ds_vendorItemPricing = VendorItemPricing()
-
 
1320
        ds_vendorItemPricing.vendor = vendor
-
 
1321
        ds_vendorItemPricing.item = item
-
 
1322
    
-
 
1323
    subject = ""
-
 
1324
    message = ""
-
 
1325
    if vendorItemPricing.mop:
-
 
1326
        ds_vendorItemPricing.mop = vendorItemPricing.mop
-
 
1327
    if vendorItemPricing.dealerPrice:
-
 
1328
        ds_vendorItemPricing.dealerPrice = vendorItemPricing.dealerPrice
-
 
1329
    if vendorItemPricing.transferPrice:
-
 
1330
        if vendorItemPricing.transferPrice != ds_vendorItemPricing.transfer_price:
-
 
1331
            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)
-
 
1332
            subject = "Alert:Change in Transfer Price"
-
 
1333
        ds_vendorItemPricing.transfer_price = vendorItemPricing.transferPrice
-
 
1334
    
-
 
1335
    session.commit()
-
 
1336
    if subject:
-
 
1337
        __send_mail(subject, message)
-
 
1338
    return
-
 
1339
 
-
 
1340
def add_vendor_item_mapping(key, vendorItemMapping):
-
 
1341
    if not vendorItemMapping:
-
 
1342
        raise InventoryServiceException(108, "Bad vendorItemMapping in request")
-
 
1343
    vendorId = vendorItemMapping.vendorId
-
 
1344
    itemId = vendorItemMapping.itemId
-
 
1345
    
-
 
1346
    try:
-
 
1347
        vendor = Vendor.query.filter_by(id=vendorId).one()
-
 
1348
    except:
-
 
1349
        raise InventoryServiceException(101, "Vendor not found for vendorId " + str(vendorId))
-
 
1350
    
-
 
1351
    try:
-
 
1352
        item = Item.query.filter_by(id=itemId).one()
-
 
1353
    except:
-
 
1354
        raise InventoryServiceException(101, "Item not found for vendorId " + str(itemId))
-
 
1355
    
-
 
1356
    try:
-
 
1357
        ds_vendorItemMapping = VendorItemMapping.query.filter(and_(VendorItemMapping.vendor==vendor, VendorItemMapping.item==item, VendorItemMapping.item_key==key)).one()
-
 
1358
    except:
-
 
1359
        ds_vendorItemMapping = VendorItemMapping()
-
 
1360
        ds_vendorItemMapping.vendor = vendor
-
 
1361
        ds_vendorItemMapping.item = item
-
 
1362
    ds_vendorItemMapping.item_key = vendorItemMapping.itemKey
-
 
1363
 
-
 
1364
    session.commit()
-
 
1365
 
-
 
1366
    # Marking the missed inventory as not ignored as the catalog dashboard user has updated their key
-
 
1367
    for missedInventoryUpdate in MissedInventoryUpdate.query.filter_by(itemKey = vendorItemMapping.itemKey).all():
-
 
1368
        missedInventoryUpdate.isIgnored = 0
-
 
1369
    session.commit()
-
 
1370
 
-
 
1371
    return
-
 
1372
 
-
 
1373
def validate_item_prices(item):
714
def validate_item_prices(item):
1374
    if item.mrp == None or item.sellingPrice == None or item.mrp == "" or item.sellingPrice == "":
715
    if item.mrp == None or item.sellingPrice == None or item.mrp == "" or item.sellingPrice == "":
1375
        return
716
        return
1376
    if item.mrp < item.sellingPrice:
717
    if item.mrp < item.sellingPrice:
1377
        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))
718
        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))
Line 1385... Line 726...
1385
    if vendorPrices.mop != "" and vendorPrices.transferPrice != "" and vendorPrices.transferPrice > vendorPrices.mop:
726
    if vendorPrices.mop != "" and vendorPrices.transferPrice != "" and vendorPrices.transferPrice > vendorPrices.mop:
1386
        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))
727
        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))
1387
        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)))
728
        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)))
1388
    return
729
    return
1389
 
730
 
1390
def get_all_vendors():
-
 
1391
    return Vendor.query.all()
-
 
1392
 
-
 
1393
def check_color_valid(color):
731
def check_color_valid(color):
1394
    if color is not None:
732
    if color is not None:
1395
        color = color.strip().lower()
733
        color = color.strip().lower()
1396
        if color != '' and color != 'na' and color != 'blank' and color != '(blank)':
734
        if color != '' and color != 'na' and color != 'blank' and color != '(blank)':
1397
            return True
735
            return True
Line 1614... Line 952...
1614
 
952
 
1615
def send_product_notifications():
953
def send_product_notifications():
1616
    product_notifications = ProductNotification.query.all()
954
    product_notifications = ProductNotification.query.all()
1617
    for product_notification in product_notifications:
955
    for product_notification in product_notifications:
1618
        item = product_notification.item
956
        item = product_notification.item
-
 
957
        client = InventoryClient().get_client()
1619
        availability = __get_item_availability(item, None)
958
        availability = client.getItemAvailabilityAtLocation(item.id)[4]
1620
        if item.status == status.ACTIVE and (not item.risky or availability > 0):
959
        if item.status == status.ACTIVE and (not item.risky or availability > 0):
1621
            __enque_product_notification_email(product_notification.email, __get_product_name(item) , product_notification.addedOn, __get_product_url(item), item.id)
960
            __enque_product_notification_email(product_notification.email, __get_product_name(item) , product_notification.addedOn, __get_product_url(item), item.id)
1622
            product_notification.delete()
961
            product_notification.delete()
1623
    session.commit()
962
    session.commit()
1624
    return True
963
    return True
Line 1818... Line 1157...
1818
            except:
1157
            except:
1819
                print "Unable to clear home page cache" + res.read()
1158
                print "Unable to clear home page cache" + res.read()
1820
    except:
1159
    except:
1821
        print "Unable to clear cache, still should continue with other operations"
1160
        print "Unable to clear cache, still should continue with other operations"
1822
        
1161
        
1823
def get_pending_orders_inventory(vendor_id=1):
-
 
1824
    """
-
 
1825
    Returns a list of inventory stock for items for which there are pending orders.
-
 
1826
    """
-
 
1827
    
-
 
1828
    warehouse_ids = [warehouse.id for warehouse in Warehouse.query.filter_by(vendor_id = vendor_id)]
-
 
1829
    pending_items_inventory = []
-
 
1830
    if warehouse_ids:
-
 
1831
        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()
-
 
1832
    return pending_items_inventory
-
 
1833
 
-
 
1834
def get_product_notifications(start_datetime):
1162
def get_product_notifications(start_datetime):
1835
    '''
1163
    '''
1836
    Returns a list of Product Notification objects each representing user requests for notification
1164
    Returns a list of Product Notification objects each representing user requests for notification
1837
    '''
1165
    '''
1838
    query = ProductNotification.query
1166
    query = ProductNotification.query
Line 1866... Line 1194...
1866
        session.query(Item.id).limit(1).one()
1194
        session.query(Item.id).limit(1).one()
1867
        return True
1195
        return True
1868
    except:
1196
    except:
1869
        return False
1197
        return False
1870
 
1198
 
1871
def add_vendor(vendor):
-
 
1872
    if not vendor:
-
 
1873
        raise InventoryServiceException(108, "Bad vendor")
-
 
1874
    if get_Vendor(vendor.id):
-
 
1875
        #vendor is already present.
-
 
1876
        raise InventoryServiceException(101, "Vendor already present")
-
 
1877
    
-
 
1878
    ds_vendor = Vendor()
-
 
1879
    ds_vendor.id = vendor.id
-
 
1880
    ds_vendor.name = vendor.name
-
 
1881
    session.commit()
-
 
1882
    return ds_vendor.id
-
 
1883
 
-
 
1884
def add_warehouse_vendor_mapping(warehouse_id, VendorId):
-
 
1885
    return True
-
 
1886
 
-
 
1887
def add_authorization_log_for_item(itemId, username, reason):
1199
def add_authorization_log_for_item(itemId, username, reason):
1888
    if not itemId or not username:
1200
    if not itemId or not username:
1889
        raise InventoryServiceException(101, "Bad itemId or Invalid username in request")
1201
        raise InventoryServiceException(101, "Bad itemId or Invalid username in request")
1890
    authorize_log = AuthorizationLog()
1202
    authorize_log = AuthorizationLog()
1891
    authorize_log.item_id = itemId
1203
    authorize_log.item_id = itemId
Line 1898... Line 1210...
1898
    try:
1210
    try:
1899
        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)
1211
        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)
1900
    except Exception as e:
1212
    except Exception as e:
1901
        print e
1213
        print e
1902
 
1214
 
1903
def mark_missed_inventory_updates_as_processed(itemKey, warehouseId):
-
 
1904
    MissedInventoryUpdate.query.filter_by(itemKey = itemKey, warehouseId = warehouseId).delete()
-
 
1905
    session.commit()
-
 
1906
 
-
 
1907
def get_item_keys_to_be_processed(warehouseId):
-
 
1908
    return [i.itemKey for i in MissedInventoryUpdate.query.filter_by(warehouseId = warehouseId, isIgnored = 0)]
-
 
1909
 
-
 
1910
def reset_availability(itemKey, vendorId, quantity, warehouseId):
-
 
1911
    vendorItemMapping = VendorItemMapping.get_by(vendor_id = vendorId, item_key = itemKey)
-
 
1912
    if vendorItemMapping:
-
 
1913
        itemId = vendorItemMapping.item_id
-
 
1914
 
-
 
1915
        if skippedItems.has_key(warehouseId) and itemId in skippedItems[warehouseId]:
-
 
1916
            quantity = 0
-
 
1917
 
-
 
1918
        currentInventorySnapshot = CurrentInventorySnapshot.get_by(item_id = itemId, warehouse_id = warehouseId)
-
 
1919
        if currentInventorySnapshot:
-
 
1920
            currentInventorySnapshot.availability = quantity
-
 
1921
            __update_item_availability_cache(itemId) 
-
 
1922
            check_risky_item(currentInventorySnapshot.item)
-
 
1923
        else:
-
 
1924
            add_inventory(itemId, warehouseId, quantity)
-
 
1925
 
-
 
1926
    else:
-
 
1927
        raise InventoryServiceException(101, 'VendorMapping not found for: ' + itemKey)
-
 
1928
    session.commit()
-
 
1929
 
-
 
1930
def reset_availability_for_warehouse(warehouseId):
-
 
1931
    for currentInventorySnapshot in CurrentInventorySnapshot.query.filter_by(warehouse_id=warehouseId).all():
-
 
1932
        currentInventorySnapshot.availability = 0
-
 
1933
        __update_item_availability_cache(currentInventorySnapshot.item_id)
-
 
1934
        check_risky_item(currentInventorySnapshot.item) 
-
 
1935
    session.commit()
-
 
1936
 
-
 
1937
def __send_mail(subject, message):
1215
def __send_mail(subject, message):
1938
    try:
1216
    try:
1939
        thread = threading.Thread(target=partial(mail, from_user, from_pwd, to_addresses, subject, message))
1217
        thread = threading.Thread(target=partial(mail, from_user, from_pwd, to_addresses, subject, message))
1940
        thread.start()
1218
        thread.start()
1941
    except Exception as ex:
1219
    except Exception as ex:
1942
        print ex    
1220
        print ex    
1943
 
1221
 
1944
def get_shipping_locations():
-
 
1945
    shippingLocationIds = {}
-
 
1946
    warehouses = Warehouse.query.all()
-
 
1947
    for warehouse in warehouses:
-
 
1948
        if warehouse.shippingWarehouseId:
-
 
1949
            shippingLocationIds[warehouse.shippingWarehouseId] = 1
-
 
1950
    
-
 
1951
    shippingLocations = []
-
 
1952
    for shippingLocationId in shippingLocationIds:
-
 
1953
        shippingLocations.append(get_Warehouse(shippingLocationId))
-
 
1954
        
-
 
1955
    return shippingLocations
-
 
1956
 
-
 
1957
def get_inventory_snapshot(warehouseId):
-
 
1958
    query = CurrentInventorySnapshot.query
-
 
1959
 
-
 
1960
    if warehouseId:
-
 
1961
        query = query.filter_by(warehouse_id = warehouseId)
-
 
1962
 
-
 
1963
    itemInventoryMap = {}
-
 
1964
    for row in query.all():
-
 
1965
        if not itemInventoryMap.has_key(row.item_id):
-
 
1966
            itemInventoryMap[row.item_id] = []
-
 
1967
 
-
 
1968
        itemInventoryMap[row.item_id].append(row)
-
 
1969
 
-
 
1970
    return itemInventoryMap
-
 
1971
 
-
 
1972
def get_clearance_sale_catalog_ids():
1222
def get_clearance_sale_catalog_ids():
1973
    all_status = [status.ACTIVE, status.PAUSED, status.PAUSED_BY_RISK]
1223
    all_status = [status.ACTIVE, status.PAUSED, status.PAUSED_BY_RISK]
1974
    query = Item.query.filter_by(clearance=True)
1224
    query = Item.query.filter_by(clearance=True)
1975
    query = query.filter(Item.status.in_(all_status))
1225
    query = query.filter(Item.status.in_(all_status))
1976
    query = query.group_by(Item.catalog_item_id).order_by(desc(Item.startDate)).order_by(Item.catalog_item_id)
1226
    query = query.group_by(Item.catalog_item_id).order_by(desc(Item.startDate)).order_by(Item.catalog_item_id)
1977
    clearance_sales = query.all()
1227
    clearance_sales = query.all()
1978
    return [item.catalog_item_id for item in clearance_sales]
1228
    return [item.catalog_item_id for item in clearance_sales]
1979
 
-
 
1980
def update_vendor_string(warehouseId, vendorString):
-
 
1981
    warehouse = get_Warehouse(warehouseId)
-
 
1982
    warehouse.vendorString = vendorString
-
 
1983
    session.commit()
-