Subversion Repositories SmartDukaan

Rev

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

Rev Author Line No. Line
4503 mandeep.dh 1
'''
2
Created on 29-Jul-2011
3
 
4
@author: Chandranshu
5
'''
6
from elixir import metadata, setup_all, session
4754 mandeep.dh 7
from shop2020.clients.CatalogClient import CatalogClient
8
from shop2020.clients.TransactionClient import TransactionClient
4503 mandeep.dh 9
from shop2020.purchase.main.model.LineItem import LineItem
10
from shop2020.purchase.main.model.Purchase import Purchase
11
from shop2020.purchase.main.model.PurchaseOrder import PurchaseOrder
5110 mandeep.dh 12
from shop2020.purchase.main.model.RevisionedPurchaseOrder import \
13
    RevisionedPurchaseOrder
4503 mandeep.dh 14
from shop2020.purchase.main.model.Supplier import Supplier
5110 mandeep.dh 15
from shop2020.thriftpy.model.v1.catalog.ttypes import WarehouseType, \
16
    InventoryType
4754 mandeep.dh 17
from shop2020.thriftpy.model.v1.order.ttypes import OrderStatus
18
from shop2020.thriftpy.purchase.ttypes import PurchaseServiceException, POStatus, \
19
    PurchaseOrder as TPurchaseOrder, LineItem as TLineItem
4503 mandeep.dh 20
from sqlalchemy import create_engine
21
import datetime
22
import logging
4754 mandeep.dh 23
import sys
4503 mandeep.dh 24
 
25
 
26
logging.basicConfig(level=logging.DEBUG)
27
 
28
class PurchaseServiceHandler:
29
    '''
30
    classdocs
31
    '''
32
 
33
    def __init__(self, dbname='warehouse', db_hostname='localhost',  echoOn=True):
34
        '''
35
        Constructor
36
        '''
37
        engine = create_engine('mysql://root:shop2020@' + db_hostname + '/' + dbname, pool_recycle=7200)
38
        metadata.bind = engine
39
        metadata.bind.echo = echoOn
40
        setup_all(True)
41
 
42
    def createPurchaseOrder(self, tPurchaseOrder):
43
        """
44
        Creates a purchase order based on the data in the given purchase order object.
45
        This method populates a number of missing fields
46
 
47
        Parameters:
48
         - purchaseOrder
49
        """
50
        try:
51
            purchaseOrder = PurchaseOrder(tPurchaseOrder)
52
            session.commit()
53
            purchaseOrder.set_po_number()
54
            session.commit()
55
            return purchaseOrder.id
56
        finally:
57
            self.close_session()
58
 
59
    def getAllPurchaseOrders(self, status):
60
        """
61
        Returns a list of all the purchase orders in the given state
62
 
63
        Parameters:
64
         - status
65
        """
66
        try:
67
            pos = PurchaseOrder.query.filter_by(status=status).all()
68
            return [po.to_thrift_object() for po in pos ]
69
        finally:
70
            self.close_session()
71
 
72
    def getPurchaseOrder(self, id):
73
        """
74
        Returns the purchase order with the given id. Throws an exception if there is no such purchase order.
75
 
76
        Parameters:
77
         - id
78
        """
79
        try:
80
            purchaseOrder = PurchaseOrder.get_by(id=id)
81
            if not purchaseOrder:
82
                raise PurchaseServiceException(101, "No purchase order can be found with id:" + str(id)) 
83
            return purchaseOrder.to_thrift_object()
84
        finally:
85
            self.close_session()
86
 
87
    def getSupplier(self, id):
88
        """
89
        Returns the supplier with the given order id. Throws an exception if there is no such supplier.
90
 
91
        Parameters:
92
         - id
93
        """
94
        try:
95
            return Supplier.get_by(id=id).to_thrift_object()
96
        finally:
97
            self.close_session()
98
 
99
    def startPurchase(self, purchaseOrderId, invoiceNumber, freightCharges):
100
        """
101
        Creates a purchase for the given purchase order.
102
        Throws an exception if no more purchases are allowed against the given purchase order.
103
 
104
        Parameters:
105
         - purchaseOrderId
106
         - invoiceNumber
107
         - freightCharges
108
        """
109
        try:
110
            purchase_order = PurchaseOrder.get_by(id = purchaseOrderId)
111
            purchase = Purchase(purchase_order, invoiceNumber, freightCharges)
112
            session.commit()
113
            return purchase.id
114
        finally:
115
            self.close_session()
116
 
117
    def closePurchase(self, purchaseId):
118
        """
119
        Marks a purchase as complete and updates the receivedOn time.
120
        Throws an exception if no such purchase exists.
121
 
122
        Parameters:
123
         - purchaseId
124
        """
125
        try:
126
            purchase = Purchase.get_by(id=purchaseId)
127
            purchase.receivedOn = datetime.datetime.now()
128
            session.commit()
129
        finally:
130
            self.close_session()
131
 
132
    def getAllPurchases(self, purchaseOrderId, open):
133
        """
134
        Returns all open or closed purchases for the given purchase order. Throws an exception if no such purchase order exists
135
 
136
        Parameters:
137
         - purchaseOrderId
138
         - open
139
        """
140
        try:
141
            if open:
142
                purchases = Purchase.query.filter_by(purchaseOrder_id = purchaseOrderId, receivedOn = None).all()
143
            else:
5110 mandeep.dh 144
                purchases = Purchase.query.filter_by(purchaseOrder_id = purchaseOrderId).filter(Purchase.receivedOn != None).all()
4503 mandeep.dh 145
 
146
            return [purchase.to_thrift_object() for purchase in purchases]
147
        finally:
148
            self.close_session()
149
 
4555 mandeep.dh 150
    def getPurchaseOrderForPurchase(self, purchaseId):
4503 mandeep.dh 151
        """
4555 mandeep.dh 152
        Returns the purchase order for a given purchase
4503 mandeep.dh 153
 
154
        Parameters:
155
         - purchaseId
156
        """
4555 mandeep.dh 157
        try:
158
            return self.getPurchaseOrder(Purchase.query.filter_by(id = purchaseId).one().purchaseOrder_id)
159
        finally:
160
            self.close_session()
4503 mandeep.dh 161
 
4754 mandeep.dh 162
    def getPendingPurchaseOrders(self, warehouseId):
163
        """
164
        Creates purchase order objects from pending orders
165
 
166
        Parameters:
167
         - warehouseId
168
        """
169
        try:
170
            purchaseOrders = []
171
            if not warehouseId:
172
                raise PurchaseServiceException(101, "bad warehouse id")
173
 
174
            transactionClient = TransactionClient().get_client()
175
            pending_orders = transactionClient.getOrdersInBatch([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.ACCEPTED], 0, 0, warehouseId)
176
 
177
            if not pending_orders:
178
                return purchaseOrders
179
 
180
            catalog_client = CatalogClient().get_client()
181
            availability = {}
5110 mandeep.dh 182
 
183
            warehouses = catalog_client.getWarehouses(WarehouseType.OURS, InventoryType.GOOD, 0, None, warehouseId)
184
            for warehouse in warehouses:
185
                goodWarehouseId = warehouse.id
186
                for item in catalog_client.getAllItemsForWarehouse(goodWarehouseId):
187
                    if availability.has_key(item.id):
188
                        availability[item.id] = [availability[item.id][0] + item.itemInventory.availability[goodWarehouseId], item]
189
                    else:
190
                        availability[item.id] = [item.itemInventory.availability[goodWarehouseId], item]
4754 mandeep.dh 191
 
192
            codRequirements = {}
193
            requirements = {}
194
            for order in pending_orders:
4758 mandeep.dh 195
                if order.purchaseOrderId:
4757 mandeep.dh 196
                    continue
4754 mandeep.dh 197
                for lineitem in order.lineitems:
198
                    if (requirements.has_key(lineitem.item_id)):
199
                        requirements[lineitem.item_id] += lineitem.quantity
200
                    else:
201
                        requirements[lineitem.item_id] = lineitem.quantity
202
 
203
                    if order.cod:
204
                        if (codRequirements.has_key(lineitem.item_id)):
205
                            codRequirements[lineitem.item_id] += lineitem.quantity
206
                        else:
207
                            codRequirements[lineitem.item_id] = lineitem.quantity
208
 
209
            netRequirements = {}
210
            for itemId in requirements.keys():
211
                requirementsCount = requirements.get(itemId)
212
                if  availability.has_key(itemId):
213
                    availabilityCount = availability.get(itemId)[0]
214
                    item              = availability.get(itemId)[1]
215
                    if item.preferredVendor is None or item.preferredVendor == 1:
216
                        continue
217
                    if requirementsCount > availabilityCount:
218
                        if (netRequirements.has_key(item.preferredVendor)):
219
                            netRequirements[item.preferredVendor].append([item, requirementsCount - availabilityCount])
220
                        else:
221
                            netRequirements[item.preferredVendor] = [[item, requirementsCount - availabilityCount]];
222
                else:
223
                    item = catalog_client.getItem(itemId);
224
                    if item.preferredVendor is None:
225
                        raise PurchaseServiceException(101, 'Preferred Vendor missing for ' + " ".join([item.brand, item.modelName, item.modelNumber, item.color]))
226
                    if item.preferredVendor == 1:
227
                        raise PurchaseServiceException(101, 'Preferred Vendor set to HotSpot for ' + " ".join([item.brand, item.modelName, item.modelNumber, item.color]))
228
                    if (netRequirements.has_key(item.preferredVendor)):
229
                        netRequirements[item.preferredVendor].append([item, requirementsCount])
230
                    else:
231
                        netRequirements[item.preferredVendor] = [[item, requirementsCount]];
232
 
233
            if not netRequirements:
234
                return purchaseOrders
235
 
236
            for vendorId in netRequirements.keys():
237
                t_purchase_order = TPurchaseOrder()
238
                t_purchase_order.supplierId = vendorId
239
                t_purchase_order.warehouseId = warehouseId
240
                t_purchase_order.lineitems = []
241
                for key in netRequirements.get(vendorId):
242
                    item     = key[0]
243
                    quantity = key[1]
244
                    t_po_lineitem = TLineItem()
245
                    t_po_lineitem.productGroup = item.productGroup
246
                    t_po_lineitem.brand = item.brand
247
                    t_po_lineitem.modelNumber = item.modelNumber
248
                    t_po_lineitem.modelName = item.modelName
249
                    t_po_lineitem.color = item.color
250
                    t_po_lineitem.itemId = item.id
251
                    t_po_lineitem.quantity = quantity
252
                    if codRequirements.has_key(item.id):
253
                        t_po_lineitem.codCount = min(codRequirements[item.id], quantity)
254
                    try:
255
                        item_pricing = catalog_client.getItemPricing(item.id, vendorId)
256
                    except Exception as e:
257
                        vendor = self.getSupplier(vendorId)
258
                        print 'Could not find transfer price for Item id: ' + str(item.id) + ' and vendor id: ' + str(vendorId)
259
                        print e
260
                        raise PurchaseServiceException(101, 'Transfer price missing for ' + vendor.name + ' and ' + " ".join([item.brand, item.modelName, item.modelNumber, item.color]))
261
                    t_po_lineitem.unitPrice = item_pricing.transferPrice
262
                    t_purchase_order.lineitems.append(t_po_lineitem)
263
                purchaseOrders.append(t_purchase_order)
264
            return purchaseOrders
265
        finally:
266
            self.close_session()
267
 
268
    def getSuppliers(self, ):
269
        """
270
        Returns all the valid suppliers
271
        """
272
        try:
273
            return [Supplier.to_thrift_object(supplier) for supplier in Supplier.query.all()]
274
        finally:
275
            self.close_session()
276
 
5185 mandeep.dh 277
    def unFulfillPO(self, purchaseId, itemId, quantity):
278
        """
279
        Unfulfills a given purchase id and an item with its quantity.
280
 
281
        Parameters:
282
         - purchaseId
283
         - itemId
284
         - quantity
285
        """
286
        try:
287
            purchaseOrderId = Purchase.query.filter_by(id = purchaseId).one().purchaseOrder_id
288
            lineitems = LineItem.query.filter_by(purchaseOrder_id = purchaseOrderId, itemId = itemId).all()
289
            if lineitems:
290
                fulfilledQuantity = lineitems[0].quantity - lineitems[0].unfulfilledQuantity
291
                if fulfilledQuantity < quantity:
292
                    raise PurchaseServiceException(101, 'Can UnFulfill only ' + str(fulfilledQuantity) + 'quantity')
293
                else:
294
 
295
                    lineitems[0].unfulfilledQuantity += quantity
296
                    lineitems[0].fulfilled = 0
297
                    purchaseOrder = PurchaseOrder.get_by(id=purchaseOrderId)
298
                    purchaseOrder.status = POStatus.PARTIALLY_FULFILLED
299
                    session.commit()
300
                    return
301
 
302
            raise PurchaseServiceException(101, 'No lineitem found with this itemId: ' + str(itemId) + ' in PO Id: ' + str(purchaseOrderId))
303
        finally:
304
            self.close_session()        
305
 
4754 mandeep.dh 306
    def fulfillPO(self, purchaseOrderId, itemId, quantity):
307
        """
308
        Fulfills a given purchase order with an item.
309
 
310
        Parameters:
311
         - purchaseOrderId
312
         - itemId
313
         - quantity
314
        """
315
        try:
316
            lineitems = LineItem.query.filter_by(purchaseOrder_id = purchaseOrderId, itemId = itemId).all()
317
            if lineitems:
318
                if lineitems[0].unfulfilledQuantity < quantity:
319
                    raise PurchaseServiceException(101, 'Can fulfill only ' + str(lineitems[0].unfulfilledQuantity) + 'quantity')
320
                else:
321
                    lineitems[0].unfulfilledQuantity -= quantity
322
                    if not lineitems[0].unfulfilledQuantity:
323
                        lineitems[0].fulfilled = 1
324
                        if not LineItem.query.filter_by(purchaseOrder_id = purchaseOrderId, fulfilled = 0).all():
325
                            purchaseOrder = self.getPurchaseOrder(purchaseOrderId)
326
                            purchaseOrder.status = POStatus.CLOSED
327
                    session.commit()
328
                    return
329
 
330
            raise PurchaseServiceException(101, 'No lineitem found with this itemId: ' + str(itemId) + ' in PO Id: ' + str(purchaseOrderId) )
331
        finally:
332
            self.close_session()
333
 
334
    def updatePurchaseOrder(self, purchaseOrder):
335
        """
336
        Amends a PO sa per the new lineitems passed
337
 
338
        Parameters:
339
         - purchaseOrder
340
        """
341
        try:
342
            existingPurchaseOrder = PurchaseOrder.get_by(id = purchaseOrder.id)
343
            maxRevision = 0
344
            existingRevisions = RevisionedPurchaseOrder.query.filter_by(purchaseOrderId = purchaseOrder.id).all()
345
            if existingRevisions:
346
                maxRevision = max([a.revision for a in existingRevisions]) + 1
5147 mandeep.dh 347
 
348
            newPOItems = {}
349
            for t_lineitem in purchaseOrder.lineitems:
350
                newPOItems[t_lineitem.itemId] = t_lineitem
351
 
4754 mandeep.dh 352
            for lineitem in existingPurchaseOrder.lineitems:
5147 mandeep.dh 353
                fulfilledQuantity = lineitem.quantity - lineitem.unfulfilledQuantity
354
                if fulfilledQuantity:
355
                    if not newPOItems.has_key(lineitem.itemId):
356
                        raise PurchaseServiceException(101, 'Cannot remove fulfilled item id: ' + str(lineitem.itemId) + ' from PO')
357
                    else:
358
                        if newPOItems[lineitem.itemId].quantity < fulfilledQuantity:
359
                            raise PurchaseServiceException(101, 'More quantity already fulfilled for item id: ' + str(lineitem.itemId))
360
                        else:
361
                            newPOItems[lineitem.itemId].unfulfilledQuantity -= fulfilledQuantity
4754 mandeep.dh 362
                revisionedPurchaseOrder = RevisionedPurchaseOrder()
363
                revisionedPurchaseOrder.purchaseOrderId = purchaseOrder.id
364
                revisionedPurchaseOrder.revision = maxRevision
365
                revisionedPurchaseOrder.itemId = lineitem.itemId
366
                revisionedPurchaseOrder.unfulfilledQuantity = lineitem.unfulfilledQuantity
367
                revisionedPurchaseOrder.unitPrice = lineitem.unitPrice
368
                revisionedPurchaseOrder.createdAt = lineitem.createdAt
369
                revisionedPurchaseOrder.quantity = lineitem.quantity
370
                lineitem.delete()
371
            existingPurchaseOrder.lineitems = [LineItem(existingPurchaseOrder, t_lineitem) for t_lineitem in purchaseOrder.lineitems]
372
            existingPurchaseOrder.totalCost = sum([t_lineitem.quantity * t_lineitem.unitPrice for t_lineitem in purchaseOrder.lineitems])
373
            session.commit()
374
        finally:
375
            self.close_session()
376
 
4503 mandeep.dh 377
    def close_session(self):
378
        if session.is_active:
379
            print "session is active. closing it."
380
            session.close()
381
 
382
    def isAlive(self, ):
383
        """
384
        For checking weather service is active alive or not. It also checks connectivity with database
385
        """
386
        try:
387
            session.query(Supplier.id).limit(1).all()
388
            return True
389
        except:
390
            return False
391
        finally:
392
            self.close_session()
393