Subversion Repositories SmartDukaan

Rev

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