Subversion Repositories SmartDukaan

Rev

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