Subversion Repositories SmartDukaan

Rev

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

Rev 11879 Rev 11880
Line 17... Line 17...
17
    CurrentReservationSnapshot, IgnoredInventoryUpdateItems, ItemStockPurchaseParams, \
17
    CurrentReservationSnapshot, IgnoredInventoryUpdateItems, ItemStockPurchaseParams, \
18
    OOSStatus, AmazonInventorySnapshot, StateMaster, HoldInventoryDetail, AmazonFbaInventorySnapshot, \
18
    OOSStatus, AmazonInventorySnapshot, StateMaster, HoldInventoryDetail, AmazonFbaInventorySnapshot, \
19
    SnapdealInventorySnapshot, FlipkartInventorySnapshot, SnapdealStockAtEOD, FlipkartStockAtEOD
19
    SnapdealInventorySnapshot, FlipkartInventorySnapshot, SnapdealStockAtEOD, FlipkartStockAtEOD
20
from shop2020.thriftpy.model.v1.inventory.ttypes import \
20
from shop2020.thriftpy.model.v1.inventory.ttypes import \
21
    InventoryServiceException, HolidayType, InventoryType, WarehouseType
21
    InventoryServiceException, HolidayType, InventoryType, WarehouseType
-
 
22
from shop2020.model.v1.inventory.impl.DataAcessors import get_item_availability_for_location
22
from shop2020.model.v1.inventory.impl.Convertors import to_t_item_inventory
23
from shop2020.model.v1.inventory.impl.Convertors import to_t_item_inventory
23
 
24
 
24
 
25
 
25
if __name__ == '__main__' and __package__ is None:
26
if __name__ == '__main__' and __package__ is None:
26
    import sys
27
    import sys
Line 28... Line 29...
28
    sys.path.insert(0, os.getcwd())
29
    sys.path.insert(0, os.getcwd())
29
 
30
 
30
categories={}
31
categories={}
31
catm = CategoryManager()
32
catm = CategoryManager()
32
 
33
 
33
def get_item_availability_for_location(item_id, source_id):
-
 
34
    item_availability = ItemAvailabilityCache.get_by(itemId=item_id, sourceId = source_id)
-
 
35
    if item_availability:
-
 
36
        return [item_availability.warehouseId, item_availability.expectedDelay, item_availability.billingWarehouseId, item_availability.sellingPrice, item_availability.totalAvailability, item_availability.weight]
-
 
37
    else:
-
 
38
        __update_item_availability_cache(item_id, source_id)
-
 
39
            ##Check risky status for the source
-
 
40
        __check_risky_item(item_id, source_id)
-
 
41
        return get_item_availability_for_location(item_id, source_id)
-
 
42
    
-
 
43
def __get_item_from_source(item_id, source_id):
-
 
44
    if source_id == 1:
-
 
45
        client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
-
 
46
        return client.getItem(item_id)
-
 
47
    if source_id == 2:
-
 
48
        client = CatalogClient("catalog_service_server_host_hotspot", "catalog_service_server_port").get_client()
-
 
49
        return client.getItem(item_id)
-
 
50
 
-
 
51
def get_ignored_warehouses(item_id): 
-
 
52
    Ignored_inventory_items = IgnoredInventoryUpdateItems.query.filter_by(item_id=item_id).all()
-
 
53
    warehouses = []
-
 
54
    for Ignored_inventory_item in Ignored_inventory_items:
-
 
55
        warehouses.append(Ignored_inventory_item.warehouse_id)
-
 
56
    return warehouses
-
 
57
 
-
 
58
def __get_warehouse_with_min_transfer_price(warehouses, ignoredWhs, item_id, item_pricing, ignoreAvailability):
-
 
59
    warehouse_retid = -1
-
 
60
    minTransferPrice = None
-
 
61
    total_availability = 0
-
 
62
    availabilityForBillingWarehouses = {}
-
 
63
    warehousesAvailability = {}
-
 
64
    availability = 0
-
 
65
    billing_warehouse_retid = None
-
 
66
    
-
 
67
    if not ignoreAvailability:
-
 
68
        for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():
-
 
69
            entry.reserved = max(entry.reserved, 0)
-
 
70
            entry.held = max(entry.held, 0)
-
 
71
            #if entry.availability > entry.reserved:
-
 
72
            warehousesAvailability[entry.warehouse_id] = [entry.availability, entry.reserved, entry.held] 
-
 
73
 
-
 
74
    if len(ignoredWhs) > 0:
-
 
75
        for whid in ignoredWhs:
-
 
76
            if warehousesAvailability.has_key(whid):
-
 
77
                warehousesAvailability[whid][0] = 0
-
 
78
                warehousesAvailability[whid][1] = 0
-
 
79
                warehousesAvailability[whid][2] = 0
-
 
80
        
-
 
81
    for warehouse in warehouses.values():
-
 
82
        if not ignoreAvailability:
-
 
83
            #TODO Mistake no entry for this warehouse.id in warehouseswithAvailab
-
 
84
            if warehouse.id not in warehousesAvailability:
-
 
85
                continue
-
 
86
            entry = warehousesAvailability[warehouse.id]
-
 
87
            if warehouse.billingWarehouseId in availabilityForBillingWarehouses:
-
 
88
                if warehouse.billingWarehouseId is not None or warehouse.billingWarehouseId != 0: 
-
 
89
                    availabilityForBillingWarehouses[warehouse.billingWarehouseId] = availabilityForBillingWarehouses[warehouse.billingWarehouseId] + entry[0] - entry[1] - entry[2]  
-
 
90
            else:
-
 
91
                if warehouse.billingWarehouseId is not None or warehouse.billingWarehouseId != 0: 
-
 
92
                    availabilityForBillingWarehouses[warehouse.billingWarehouseId] = entry[0] - entry[1] - entry[2]
-
 
93
            if entry[0] <= (entry[1] + entry[2]):
-
 
94
                continue
-
 
95
            total_availability += entry[0] - entry[1] - entry[2]
-
 
96
 
-
 
97
        # Missing transfer price cases should not impact warehouse assignment
-
 
98
        transferPrice = None
-
 
99
        if item_pricing.has_key(warehouse.vendor_id):
-
 
100
            transferPrice = item_pricing[warehouse.vendor_id].nlc
-
 
101
        if minTransferPrice is None or (transferPrice and minTransferPrice > transferPrice):
-
 
102
            warehouse_retid = warehouse.id
-
 
103
            billing_warehouse_retid = warehouse.billingWarehouseId
-
 
104
            minTransferPrice = transferPrice
-
 
105
    
-
 
106
    
-
 
107
    if billing_warehouse_retid in availabilityForBillingWarehouses: 
-
 
108
        availability = availabilityForBillingWarehouses[billing_warehouse_retid]
-
 
109
    else:
-
 
110
        availability = total_availability
-
 
111
    
-
 
112
    return [warehouse_retid, availability]
-
 
113
 
-
 
114
def __get_warehouse_with_min_transfer_delay(warehouses, ignoredWhs, item_id, item_pricing):
-
 
115
    minTransferDelay = None
-
 
116
    minTransferDelayWarehouses = {}
-
 
117
    total_availability = 0
-
 
118
 
-
 
119
    for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():
-
 
120
        entry.reserved = max(entry.reserved, 0)
-
 
121
        entry.held = max(entry.held, 0)
-
 
122
        if warehouses.has_key(entry.warehouse_id):
-
 
123
            warehouse = warehouses[entry.warehouse_id]
-
 
124
            #if entry.availability > entry.reserved:
-
 
125
            if entry.warehouse_id not in ignoredWhs:
-
 
126
                total_availability += entry.availability - entry.reserved - entry.held
-
 
127
            if entry.availability - entry.reserved - entry.held <= 0:
-
 
128
                continue
-
 
129
            transferDelay = warehouse.transferDelayInHours
-
 
130
            if minTransferDelay is None or minTransferDelay >= transferDelay:
-
 
131
                if minTransferDelay != transferDelay:
-
 
132
                    minTransferDelayWarehouses = {}
-
 
133
                minTransferDelayWarehouses[warehouse.id] = warehouse
-
 
134
                minTransferDelay = transferDelay
-
 
135
 
-
 
136
    return [__get_warehouse_with_min_transfer_price(minTransferDelayWarehouses, ignoredWhs, item_id, item_pricing, False)[0], total_availability]
-
 
137
 
-
 
138
def __get_vendor_holiday_delay(vendor_id, expectedDelay):
-
 
139
    ## If vendor is closed two days continuously
-
 
140
    holidayDelay = 0
-
 
141
    currentDate = datetime.date.today()
-
 
142
    expectedDate = currentDate + datetime.timedelta(days = expectedDelay)
-
 
143
    holidays = VendorHolidays.query.filter(VendorHolidays.vendor_id == vendor_id).filter(VendorHolidays.date.between(currentDate, expectedDate)).all()
-
 
144
    if holidays:
-
 
145
        holidayDelay = holidayDelay + len(holidays)
-
 
146
    return holidayDelay 
-
 
147
 
-
 
148
    
-
 
149
def __update_item_availability_cache(item_id, source_id):
-
 
150
    """
-
 
151
    Determines the warehouse that should be used to fulfil an order for the given item.
-
 
152
    Algorithm explained at https://sites.google.com/a/shop2020.in/virtual-w-h-and-inventory/technical-details
-
 
153
 
-
 
154
    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.
-
 
155
    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.
-
 
156
    
-
 
157
    if item available at any OUR-GOOD warehouse
-
 
158
        // OUR-GOOD warehouses have inventory risk; So, we empty them first! 
-
 
159
        // We can start with minimum transfer price criterion but down the line we can also bring in Inventory age 
-
 
160
        assign OUR-GOOD warehouse with minimum transfer price
-
 
161
    else
-
 
162
        if Preferred vendor is specified and marked Sticky
-
 
163
            // Always purchase from Preferred if its marked sticky
-
 
164
            assign preferred vendor's THIRDPARTY GOOD/VIRTUAL warehouse
-
 
165
        else 
-
 
166
            if item available in a THIRDPARTY GOOD/VIRTUAL warehouse
-
 
167
                assign THIRDPARTY GOOD/VIRTUAL warehouse where item is available with minimal transfer delay followed by minimum transfer price
-
 
168
            else 
-
 
169
                // Item not available at any warehouse, OURS or THIRDPARTY
-
 
170
                If Preferred vendor is specified
-
 
171
                    assign preferred vendor's THIRDPARTY GOOD/VIRTUAL warehouse
-
 
172
                else
-
 
173
                    assign THIRDPARTY GOOD/VIRTUAL warehouse with minimum transfer price
-
 
174
    
-
 
175
    Returns an ordered list of size 4 with following elements in the given order:
-
 
176
    1. Logistics location of the warehouse which was finally picked up to ship the order.
-
 
177
    2. Expected delay added by the category manager.
-
 
178
    3. Id of the warehouse which was finally picked up.
-
 
179
 
-
 
180
    Parameters:
-
 
181
     - itemId
-
 
182
    """
-
 
183
    item = __get_item_from_source(item_id, source_id)
-
 
184
    item_pricing = {}
-
 
185
    for vendorItemPricing in VendorItemPricing.query.filter_by(item_id=item_id).all():
-
 
186
        item_pricing[vendorItemPricing.vendor_id] = vendorItemPricing
-
 
187
 
-
 
188
    ignoredWhs = get_ignored_warehouses(item_id)
-
 
189
 
-
 
190
    warehouses = {}
-
 
191
    ourGoodWarehouses = {}
-
 
192
    thirdpartyWarehouses = {}
-
 
193
    preferredThirdpartyWarehouses = {}
-
 
194
    for warehouse in Warehouse.query.all():
-
 
195
        if (warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.BAD] or warehouse.warehouseType == WarehouseType._VALUES_TO_NAMES[WarehouseType.OURS_THIRDPARTY]):
-
 
196
            continue
-
 
197
        warehouses[warehouse.id] = warehouse
-
 
198
        if warehouse.warehouseType == WarehouseType._VALUES_TO_NAMES[WarehouseType.OURS]:
-
 
199
            if warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]:
-
 
200
                ourGoodWarehouses[warehouse.id] = warehouse
-
 
201
        else:
-
 
202
            thirdpartyWarehouses[warehouse.id] = warehouse
-
 
203
            if item.preferredVendor == warehouse.vendor_id and warehouse.inventoryType == InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]:
-
 
204
                preferredThirdpartyWarehouses[warehouse.id] = warehouse
-
 
205
 
-
 
206
    warehouse_retid = -1
-
 
207
    total_availability = 0
-
 
208
 
-
 
209
    [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_price(ourGoodWarehouses, ignoredWhs, item_id, item_pricing, False)
-
 
210
    if warehouse_retid == -1:
-
 
211
        if item.preferredVendor and item.isWarehousePreferenceSticky:
-
 
212
            [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_delay(preferredThirdpartyWarehouses, ignoredWhs, item_id, item_pricing)
-
 
213
            if warehouse_retid == -1:
-
 
214
                warehouse_retid = preferredThirdpartyWarehouses.keys()[0]
-
 
215
        else:
-
 
216
            [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_delay(thirdpartyWarehouses, ignoredWhs, item_id, item_pricing)
-
 
217
            if warehouse_retid == -1:
-
 
218
                if item.preferredVendor:
-
 
219
                    warehouse_retid = preferredThirdpartyWarehouses.keys()[0]
-
 
220
                else:
-
 
221
                    [warehouse_retid, total_availability] = __get_warehouse_with_min_transfer_price(thirdpartyWarehouses, ignoredWhs, item_id, item_pricing, True)
-
 
222
 
-
 
223
    warehouse = warehouses[warehouse_retid]
-
 
224
    billingWarehouseId = warehouse.billingWarehouseId
-
 
225
 
-
 
226
    # Fetching billing warehouse of a Good billable warehouse corresponding to the virtual one
-
 
227
    if not warehouse.billingWarehouseId:
-
 
228
        for w in Warehouse.query.filter_by(vendor_id = warehouse.vendor_id, inventoryType = InventoryType._VALUES_TO_NAMES[InventoryType.GOOD]).all():
-
 
229
            if w.billingWarehouseId:
-
 
230
                billingWarehouseId = w.billingWarehouseId
-
 
231
                break
-
 
232
 
-
 
233
    expectedDelay = item.expectedDelay 
-
 
234
    if expectedDelay is None:
-
 
235
        print 'expectedDelay field for this item was Null. Resetting it to 0'
-
 
236
        expectedDelay = 0
-
 
237
    else:
-
 
238
        expectedDelay = int(item.expectedDelay)
-
 
239
 
-
 
240
    if total_availability <= 0:
-
 
241
        if item.preferredVendor in [1, 5]:
-
 
242
            expectedDelay = expectedDelay + 3
-
 
243
        else:
-
 
244
            expectedDelay = expectedDelay + 2
-
 
245
    else:
-
 
246
        if warehouse.transferDelayInHours:
-
 
247
            expectedDelay = expectedDelay + warehouse.transferDelayInHours / 24
-
 
248
 
-
 
249
    if warehouse.warehouseType == WarehouseType.THIRD_PARTY:
-
 
250
        expectedDelay = expectedDelay + __get_vendor_holiday_delay(warehouse.vendor_id, expectedDelay) 
-
 
251
 
-
 
252
    total_availability = 0
-
 
253
    for entry in CurrentInventorySnapshot.query.filter_by(item_id = item_id).all():
-
 
254
        if entry.warehouse_id not in ignoredWhs:
-
 
255
            total_availability += entry.availability - entry.reserved
-
 
256
 
-
 
257
    item_availability_cache = ItemAvailabilityCache.get_by(itemId=item_id, sourceId=source_id)
-
 
258
    if item_availability_cache is None:
-
 
259
        item_availability_cache = ItemAvailabilityCache()
-
 
260
        item_availability_cache.itemId = item_id
-
 
261
        item_availability_cache.sourceId = source_id
-
 
262
    item_availability_cache.warehouseId = int(warehouse_retid)
-
 
263
    item_availability_cache.expectedDelay = expectedDelay
-
 
264
    item_availability_cache.billingWarehouseId = billingWarehouseId
-
 
265
    item_availability_cache.sellingPrice = item.sellingPrice
-
 
266
    item_availability_cache.totalAvailability = total_availability
-
 
267
    item_availability_cache.weight = 1000*item.weight if item.weight else 300
-
 
268
    session.commit()
-
 
269
    
-
 
270
def __check_risky_item(item_id, source_id):
-
 
271
    ## We should get the list of strings which will identify to the catalog servers
-
 
272
    if source_id == 1:
-
 
273
        client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
-
 
274
        client.validateRiskyStatus(item_id)
-
 
275
    if source_id == 2:
-
 
276
        client = CatalogClient("catalog_service_server_host_hotspot", "catalog_service_server_port").get_client()
-
 
277
        client.validateRiskyStatus(item_id)
-
 
278
 
-
 
279
def get_item_inventory_by_item_id(item_id):
34
def get_item_inventory_by_item_id(item_id):
280
    inventory =  CurrentInventorySnapshot.query.filter_by(item_id=item_id).all()
35
    inventory =  CurrentInventorySnapshot.query.filter_by(item_id=item_id).all()
281
    return to_t_item_inventory(inventory, item_id)
36
    return to_t_item_inventory(inventory, item_id)
282
 
37
 
283
def main():
38
def main():
Line 309... Line 64...
309
    sheet.write(0, 13, "Status", heading_xf)
64
    sheet.write(0, 13, "Status", heading_xf)
310
    sheet.write(0, 14, "Entity Id", heading_xf)
65
    sheet.write(0, 14, "Entity Id", heading_xf)
311
    sheet.write(0, 15, "Ships By", heading_xf)
66
    sheet.write(0, 15, "Ships By", heading_xf)
312
    
67
    
313
    DataService.initialize('inventory','192.168.190.114')
68
    DataService.initialize('inventory','192.168.190.114')
314
    iclient = InventoryClient().get_client()
-
 
315
    cclient = CatalogClient().get_client()
69
    cclient = CatalogClient().get_client()
316
#    items = cclient.getAllItems(False)
70
#    items = cclient.getAllItems(False)
317
    
71
    
318
    xls_fields = []    
72
    xls_fields = []    
319
#    for item in items:
73
#    for item in items:
Line 411... Line 165...
411
    
165
    
412
    timestring = datetime.datetime.now().strftime("%Y-%m-%d")
166
    timestring = datetime.datetime.now().strftime("%Y-%m-%d")
413
    filename = "/tmp/stockreport" + timestring + ".xls" 
167
    filename = "/tmp/stockreport" + timestring + ".xls" 
414
    wbk.save(filename)
168
    wbk.save(filename)
415
    filenames = [filename]
169
    filenames = [filename]
416
    mail_html("cnc.center@shop2020.in", "5h0p2o2o", ["rajveer.singh@shop2020.in", "chaitnaya.vats@shop2020.in", "chandan.kumar@shop2020.in", "khushal.bhatia@shop2020.in", "rajneesh.arora@shop2020.in", "manoj.kumar@shop2020.in"], "Inventory Stock Report for " + timestring, body, [get_attachment_part(filename) for filename in filenames])
170
    mail_html("cnc.center@shop2020.in", "5h0p2o2o", ["anikendra.das@shop2020.in", "chaitnaya.vats@shop2020.in", "chandan.kumar@shop2020.in", "khushal.bhatia@shop2020.in", "rajneesh.arora@shop2020.in", "manoj.kumar@shop2020.in"], "Inventory Stock Report for " + timestring, body, [get_attachment_part(filename) for filename in filenames])
417
    
171
    
418
def removeNonAscii(s): return "".join(i for i in s if ord(i)<128)
172
def removeNonAscii(s): return "".join(i for i in s if ord(i)<128)
419
 
173
 
420
if __name__ == '__main__':
174
if __name__ == '__main__':
421
    main()
175
    main()