Subversion Repositories SmartDukaan

Rev

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