Subversion Repositories SmartDukaan

Rev

Rev 5944 | Rev 6467 | 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()
6385 amar.kumar 154
 
155
    def getPurchasesForPO(self, purchaseOrderId):
156
        """
157
        Returns all purchases for the given purchase order. Throws an exception if no such purchase order exists
158
 
159
        Parameters:
160
         - purchaseOrderId
161
         - open
162
        """
163
        try:
164
            purchases = Purchase.query.filter_by(purchaseOrder_id = purchaseOrderId).all()
165
            return [purchase.to_thrift_object() for purchase in purchases]
166
        finally:
167
            self.close_session()
4503 mandeep.dh 168
 
6385 amar.kumar 169
 
4555 mandeep.dh 170
    def getPurchaseOrderForPurchase(self, purchaseId):
4503 mandeep.dh 171
        """
4555 mandeep.dh 172
        Returns the purchase order for a given purchase
4503 mandeep.dh 173
 
174
        Parameters:
175
         - purchaseId
176
        """
4555 mandeep.dh 177
        try:
178
            return self.getPurchaseOrder(Purchase.query.filter_by(id = purchaseId).one().purchaseOrder_id)
179
        finally:
180
            self.close_session()
4503 mandeep.dh 181
 
4754 mandeep.dh 182
    def getPendingPurchaseOrders(self, warehouseId):
183
        """
184
        Creates purchase order objects from pending orders
185
 
186
        Parameters:
187
         - warehouseId
188
        """
189
        try:
190
            purchaseOrders = []
191
            if not warehouseId:
192
                raise PurchaseServiceException(101, "bad warehouse id")
193
 
194
            transactionClient = TransactionClient().get_client()
195
            pending_orders = transactionClient.getOrdersInBatch([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.ACCEPTED], 0, 0, warehouseId)
196
 
197
            if not pending_orders:
198
                return purchaseOrders
199
 
5944 mandeep.dh 200
            inventory_client = InventoryClient().get_client()
4754 mandeep.dh 201
            availability = {}
5110 mandeep.dh 202
 
5944 mandeep.dh 203
            ourGoodWarehouseIds = [w.id for w in inventory_client.getWarehouses(WarehouseType.OURS, InventoryType.GOOD, 0, None, warehouseId)]
204
            itemInventorySnapshot = inventory_client.getInventorySnapshot(0)
205
            for itemId, itemInventory in itemInventorySnapshot.iteritems():
206
                item = self.__get_item_from_master(itemId)
207
                for warehouseId, quantity in itemInventory.availability.iteritems():
208
                    if warehouseId in ourGoodWarehouseIds:
209
                        if availability.has_key(item.id):
210
                            availability[item.id] = [availability[item.id][0] + quantity, item]
211
                        else:
212
                            availability[item.id] = [quantity, item]
4754 mandeep.dh 213
 
5238 mandeep.dh 214
            unfulfilledPurchaseOrders = PurchaseOrder.query.filter(or_(PurchaseOrder.status == POStatus.PARTIALLY_FULFILLED, PurchaseOrder.status == POStatus.READY)).all()
215
            for purchaseOrder in unfulfilledPurchaseOrders:
216
                for lineitem in purchaseOrder.lineitems:
217
                    if availability.has_key(lineitem.itemId):
218
                        availability[lineitem.itemId] = [availability[lineitem.itemId][0] + lineitem.unfulfilledQuantity, availability[lineitem.itemId][1]]
219
                    else:
5944 mandeep.dh 220
                        item = self.__get_item_from_master(lineitem.itemId)
5238 mandeep.dh 221
                        availability[item.id] = [lineitem.unfulfilledQuantity, item]
222
 
4754 mandeep.dh 223
            codRequirements = {}
224
            requirements = {}
225
            for order in pending_orders:
4758 mandeep.dh 226
                if order.purchaseOrderId:
4757 mandeep.dh 227
                    continue
4754 mandeep.dh 228
                for lineitem in order.lineitems:
229
                    if (requirements.has_key(lineitem.item_id)):
230
                        requirements[lineitem.item_id] += lineitem.quantity
231
                    else:
232
                        requirements[lineitem.item_id] = lineitem.quantity
233
 
234
                    if order.cod:
235
                        if (codRequirements.has_key(lineitem.item_id)):
236
                            codRequirements[lineitem.item_id] += lineitem.quantity
237
                        else:
238
                            codRequirements[lineitem.item_id] = lineitem.quantity
239
 
240
            netRequirements = {}
241
            for itemId in requirements.keys():
242
                requirementsCount = requirements.get(itemId)
243
                if  availability.has_key(itemId):
244
                    availabilityCount = availability.get(itemId)[0]
245
                    item              = availability.get(itemId)[1]
246
                    if requirementsCount > availabilityCount:
5238 mandeep.dh 247
                        if item.preferredVendor is None:
248
                            raise PurchaseServiceException(101, 'Preferred Vendor missing for ' + " ".join([str(item.brand), str(item.modelName), str(item.modelNumber), str(item.color)]))
4754 mandeep.dh 249
                        if (netRequirements.has_key(item.preferredVendor)):
250
                            netRequirements[item.preferredVendor].append([item, requirementsCount - availabilityCount])
251
                        else:
252
                            netRequirements[item.preferredVendor] = [[item, requirementsCount - availabilityCount]];
253
                else:
5944 mandeep.dh 254
                    item = self.__get_item_from_master(itemId)
4754 mandeep.dh 255
                    if item.preferredVendor is None:
5238 mandeep.dh 256
                        raise PurchaseServiceException(101, 'Preferred Vendor missing for ' + " ".join([str(item.brand), str(item.modelName), str(item.modelNumber), str(item.color)]))
4754 mandeep.dh 257
                    if (netRequirements.has_key(item.preferredVendor)):
258
                        netRequirements[item.preferredVendor].append([item, requirementsCount])
259
                    else:
260
                        netRequirements[item.preferredVendor] = [[item, requirementsCount]];
5238 mandeep.dh 261
 
4754 mandeep.dh 262
            if not netRequirements:
263
                return purchaseOrders
264
 
265
            for vendorId in netRequirements.keys():
266
                t_purchase_order = TPurchaseOrder()
267
                t_purchase_order.supplierId = vendorId
268
                t_purchase_order.warehouseId = warehouseId
269
                t_purchase_order.lineitems = []
270
                for key in netRequirements.get(vendorId):
271
                    item     = key[0]
272
                    quantity = key[1]
273
                    t_po_lineitem = TLineItem()
274
                    t_po_lineitem.productGroup = item.productGroup
275
                    t_po_lineitem.brand = item.brand
276
                    t_po_lineitem.modelNumber = item.modelNumber
277
                    t_po_lineitem.modelName = item.modelName
278
                    t_po_lineitem.color = item.color
279
                    t_po_lineitem.itemId = item.id
280
                    t_po_lineitem.quantity = quantity
281
                    if codRequirements.has_key(item.id):
282
                        t_po_lineitem.codCount = min(codRequirements[item.id], quantity)
283
                    try:
5944 mandeep.dh 284
                        item_pricing = inventory_client.getItemPricing(item.id, vendorId)
4754 mandeep.dh 285
                    except Exception as e:
286
                        vendor = self.getSupplier(vendorId)
287
                        print 'Could not find transfer price for Item id: ' + str(item.id) + ' and vendor id: ' + str(vendorId)
288
                        print e
289
                        raise PurchaseServiceException(101, 'Transfer price missing for ' + vendor.name + ' and ' + " ".join([item.brand, item.modelName, item.modelNumber, item.color]))
290
                    t_po_lineitem.unitPrice = item_pricing.transferPrice
291
                    t_purchase_order.lineitems.append(t_po_lineitem)
292
                purchaseOrders.append(t_purchase_order)
293
            return purchaseOrders
294
        finally:
295
            self.close_session()
296
 
297
    def getSuppliers(self, ):
298
        """
299
        Returns all the valid suppliers
300
        """
301
        try:
302
            return [Supplier.to_thrift_object(supplier) for supplier in Supplier.query.all()]
303
        finally:
304
            self.close_session()
305
 
5185 mandeep.dh 306
    def unFulfillPO(self, purchaseId, itemId, quantity):
307
        """
308
        Unfulfills a given purchase id and an item with its quantity.
309
 
310
        Parameters:
311
         - purchaseId
312
         - itemId
313
         - quantity
314
        """
315
        try:
316
            purchaseOrderId = Purchase.query.filter_by(id = purchaseId).one().purchaseOrder_id
317
            lineitems = LineItem.query.filter_by(purchaseOrder_id = purchaseOrderId, itemId = itemId).all()
318
            if lineitems:
319
                fulfilledQuantity = lineitems[0].quantity - lineitems[0].unfulfilledQuantity
320
                if fulfilledQuantity < quantity:
321
                    raise PurchaseServiceException(101, 'Can UnFulfill only ' + str(fulfilledQuantity) + 'quantity')
322
                else:
5437 mandeep.dh 323
                    lineitems[0].unfulfilledQuantity = lineitems[0].unfulfilledQuantity + quantity
5185 mandeep.dh 324
                    lineitems[0].fulfilled = 0
325
                    purchaseOrder = PurchaseOrder.get_by(id=purchaseOrderId)
326
                    purchaseOrder.status = POStatus.PARTIALLY_FULFILLED
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
 
4754 mandeep.dh 334
    def fulfillPO(self, purchaseOrderId, itemId, quantity):
335
        """
336
        Fulfills a given purchase order with an item.
337
 
338
        Parameters:
339
         - purchaseOrderId
340
         - itemId
341
         - quantity
342
        """
343
        try:
344
            lineitems = LineItem.query.filter_by(purchaseOrder_id = purchaseOrderId, itemId = itemId).all()
345
            if lineitems:
346
                if lineitems[0].unfulfilledQuantity < quantity:
5361 mandeep.dh 347
                    raise PurchaseServiceException(101, 'Can fulfill only ' + str(lineitems[0].unfulfilledQuantity) + ' quantity')
4754 mandeep.dh 348
                else:
5361 mandeep.dh 349
                    lineitems[0].unfulfilledQuantity = lineitems[0].unfulfilledQuantity - quantity
4754 mandeep.dh 350
                    if not lineitems[0].unfulfilledQuantity:
351
                        lineitems[0].fulfilled = 1
5361 mandeep.dh 352
                        session.commit()
4754 mandeep.dh 353
                        if not LineItem.query.filter_by(purchaseOrder_id = purchaseOrderId, fulfilled = 0).all():
5361 mandeep.dh 354
                            purchaseOrder = PurchaseOrder.get_by(id=purchaseOrderId)
4754 mandeep.dh 355
                            purchaseOrder.status = POStatus.CLOSED
356
                    session.commit()
357
                    return
358
 
359
            raise PurchaseServiceException(101, 'No lineitem found with this itemId: ' + str(itemId) + ' in PO Id: ' + str(purchaseOrderId) )
360
        finally:
361
            self.close_session()
362
 
363
    def updatePurchaseOrder(self, purchaseOrder):
364
        """
365
        Amends a PO sa per the new lineitems passed
366
 
367
        Parameters:
368
         - purchaseOrder
369
        """
370
        try:
371
            existingPurchaseOrder = PurchaseOrder.get_by(id = purchaseOrder.id)
372
            maxRevision = 0
373
            existingRevisions = RevisionedPurchaseOrder.query.filter_by(purchaseOrderId = purchaseOrder.id).all()
374
            if existingRevisions:
375
                maxRevision = max([a.revision for a in existingRevisions]) + 1
5147 mandeep.dh 376
 
377
            newPOItems = {}
378
            for t_lineitem in purchaseOrder.lineitems:
379
                newPOItems[t_lineitem.itemId] = t_lineitem
380
 
4754 mandeep.dh 381
            for lineitem in existingPurchaseOrder.lineitems:
5147 mandeep.dh 382
                fulfilledQuantity = lineitem.quantity - lineitem.unfulfilledQuantity
383
                if fulfilledQuantity:
384
                    if not newPOItems.has_key(lineitem.itemId):
385
                        raise PurchaseServiceException(101, 'Cannot remove fulfilled item id: ' + str(lineitem.itemId) + ' from PO')
386
                    else:
387
                        if newPOItems[lineitem.itemId].quantity < fulfilledQuantity:
388
                            raise PurchaseServiceException(101, 'More quantity already fulfilled for item id: ' + str(lineitem.itemId))
389
                        else:
5437 mandeep.dh 390
                            newPOItems[lineitem.itemId].unfulfilledQuantity = newPOItems[lineitem.itemId].unfulfilledQuantity - fulfilledQuantity
4754 mandeep.dh 391
                revisionedPurchaseOrder = RevisionedPurchaseOrder()
392
                revisionedPurchaseOrder.purchaseOrderId = purchaseOrder.id
393
                revisionedPurchaseOrder.revision = maxRevision
394
                revisionedPurchaseOrder.itemId = lineitem.itemId
395
                revisionedPurchaseOrder.unfulfilledQuantity = lineitem.unfulfilledQuantity
396
                revisionedPurchaseOrder.unitPrice = lineitem.unitPrice
397
                revisionedPurchaseOrder.createdAt = lineitem.createdAt
398
                revisionedPurchaseOrder.quantity = lineitem.quantity
399
                lineitem.delete()
400
            existingPurchaseOrder.lineitems = [LineItem(existingPurchaseOrder, t_lineitem) for t_lineitem in purchaseOrder.lineitems]
401
            existingPurchaseOrder.totalCost = sum([t_lineitem.quantity * t_lineitem.unitPrice for t_lineitem in purchaseOrder.lineitems])
402
            session.commit()
403
        finally:
404
            self.close_session()
405
 
4503 mandeep.dh 406
    def close_session(self):
407
        if session.is_active:
408
            print "session is active. closing it."
409
            session.close()
410
 
5443 mandeep.dh 411
    def getInvoices(self, date):
412
        """
413
        Fetches all invoices for a given date
414
 
415
        Parameters:
416
         - date
417
        """
418
        try:
419
            return [i.to_thrift_object() for i in Invoice.query.filter(Invoice.date > to_py_date(date)).all()]
420
        finally:
421
            self.close_session()
422
 
423
    def createInvoice(self, invoice):
424
        """
425
        Creates an invoice object
426
 
427
        Parameters:
428
         - invoice
429
        """
430
        try:
5768 mandeep.dh 431
            if Invoice.query.filter_by(supplierId = invoice.supplierId, date = to_py_date(invoice.date), invoiceNumber = invoice.invoiceNumber).all():
432
                raise PurchaseServiceException(ExceptionType.ILLEGAL_ARGUMENTS, "Already received such invoice")
5443 mandeep.dh 433
            invoiceObj = Invoice()
434
            invoiceObj.invoiceNumber = invoice.invoiceNumber
435
            invoiceObj.date = to_py_date(invoice.date)
436
            invoiceObj.receivedFrom = invoice.receivedFrom
437
            invoiceObj.numItems = invoice.numItems
438
            invoiceObj.supplierId = invoice.supplierId
439
            session.commit()
440
        finally:
441
            self.close_session()        
442
 
5591 mandeep.dh 443
    def addSupplier(self, supplier):
444
        """
445
        Creates a supplier
446
 
447
        Parameters:
448
         - supplier
449
        """
450
        try:
451
            supplierObj = Supplier()
452
            supplierObj.communicationAddress = supplier.communicationAddress
453
            supplierObj.contactEmail = supplier.contactEmail
454
            supplierObj.contactFax = supplier.contactFax
455
            supplierObj.contactName = supplier.contactName
456
            supplierObj.contactPhone = supplier.contactPhone
457
            supplierObj.fax = supplier.fax
458
            supplierObj.headDesignation = supplier.headDesignation
459
            supplierObj.headEmail = supplier.headEmail
460
            supplierObj.headName = supplier.headName
461
            supplierObj.name = supplier.name
462
            supplierObj.pan = supplier.pan
463
            supplierObj.phone = supplier.phone
464
            supplierObj.registeredAddress = supplier.registeredAddress
465
            supplierObj.tin = supplier.tin
466
            session.commit()
467
            return self.getSupplier(supplierObj.id)
468
        finally:
469
            self.close_session()
470
 
471
    def updateSupplier(self, supplier):
472
        """
473
        Updates a supplier
474
 
475
        Parameters:
476
         - supplier
477
        """
478
        try:
479
            supplierObj = Supplier.get(supplier.id)
480
            supplierObj.communicationAddress = supplier.communicationAddress
481
            supplierObj.contactEmail = supplier.contactEmail
482
            supplierObj.contactFax = supplier.contactFax
483
            supplierObj.contactName = supplier.contactName
484
            supplierObj.contactPhone = supplier.contactPhone
485
            supplierObj.fax = supplier.fax
486
            supplierObj.headDesignation = supplier.headDesignation
487
            supplierObj.headEmail = supplier.headEmail
488
            supplierObj.headName = supplier.headName
489
            supplierObj.name = supplier.name
490
            supplierObj.pan = supplier.pan
491
            supplierObj.phone = supplier.phone
492
            supplierObj.registeredAddress = supplier.registeredAddress
493
            supplierObj.tin = supplier.tin
494
            session.commit()
495
        finally:
496
            self.close_session()
497
 
6385 amar.kumar 498
    def getInvoicesForPO(self, poNumber):
499
        '''
500
        For getting invoiceNumbers for a Purchase Order
501
        '''
502
        try:
503
 
504
            purchases = Purchase.query.filter_by(purchaseOrder = poNumber)
505
            for purchase in purchases:
506
                invoice = purchase.invoiceNumber
507
        except:
508
            return None
509
        finally:
510
            self.close_session()
511
 
4503 mandeep.dh 512
    def isAlive(self, ):
513
        """
514
        For checking weather service is active alive or not. It also checks connectivity with database
515
        """
516
        try:
517
            session.query(Supplier.id).limit(1).all()
518
            return True
519
        except:
520
            return False
521
        finally:
522
            self.close_session()
5944 mandeep.dh 523
 
524
    def __get_item_from_master(self, item_id):
525
        client = CatalogClient("catalog_service_server_host_master", "catalog_service_server_port").get_client()
526
        return client.getItem(item_id)