Subversion Repositories SmartDukaan

Rev

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