Subversion Repositories SmartDukaan

Rev

Rev 4386 | Rev 4410 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
104 ashish 1
'''
2
Created on 29-Mar-2010
3
 
2536 chandransh 4
@author: Chandranshu
104 ashish 5
'''
2536 chandransh 6
 
4369 rajveer 7
from datetime import date, timedelta
2536 chandransh 8
from elixir import *
3499 mandeep.dh 9
from reportlab.lib.units import inch
2536 chandransh 10
from reportlab.pdfgen.canvas import Canvas
11
from reportlab.rl_config import defaultPageSize
3499 mandeep.dh 12
from shop2020.clients.CatalogClient import CatalogClient
13
from shop2020.clients.HelperClient import HelperClient
14
from shop2020.clients.LogisticsClient import LogisticsClient
2747 chandransh 15
from shop2020.clients.PaymentClient import PaymentClient
3499 mandeep.dh 16
from shop2020.clients.UserClient import UserClient
17
from shop2020.clients.WarehouseClient import WarehouseClient
18
from shop2020.model.v1 import order
19
from shop2020.model.v1.order.impl.DataService import Transaction, LineItem, \
4394 rajveer 20
    Order, BatchNoGenerator, InvoiceIDGenerator, \
21
    TransactionRequiringExtraProcessing, OrderInventory, Alert
2687 chandransh 22
from shop2020.model.v1.order.impl.model.ReturnOrder import ReturnOrder
3499 mandeep.dh 23
from shop2020.thriftpy.logistics.ttypes import DeliveryType
24
from shop2020.thriftpy.model.v1.catalog.ttypes import BillingType
4394 rajveer 25
from shop2020.thriftpy.model.v1.order.ttypes import \
4008 mandeep.dh 26
    TransactionServiceException, TransactionStatus, OrderStatus, DelayReason, \
4369 rajveer 27
    ExtraTransactionProcessingType, HotspotAction, TimeoutSummary
3499 mandeep.dh 28
from shop2020.thriftpy.warehouse.ttypes import PurchaseOrder, \
4365 mandeep.dh 29
    LineItem as POLineItem, ScanType
3499 mandeep.dh 30
from shop2020.utils.EmailAttachmentSender import mail, get_attachment_part
855 rajveer 31
from shop2020.utils.Utils import to_py_date, to_java_date
4008 mandeep.dh 32
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
3499 mandeep.dh 33
from sqlalchemy.sql import func
34
from sqlalchemy.sql.expression import and_, or_, desc, not_, distinct
35
from string import Template
36
from textwrap import dedent
37
import datetime
38
import logging
39
import os
4234 rajveer 40
import sys, traceback
3499 mandeep.dh 41
import time
4019 chandransh 42
from shop2020.config.client.ConfigClient import ConfigClient
3499 mandeep.dh 43
logging.basicConfig(level=logging.DEBUG)
104 ashish 44
 
3499 mandeep.dh 45
 
4019 chandransh 46
try:
47
    config_client = ConfigClient()
48
    PREPAID_SHIPPING_CUTOFF_TIME = int(config_client.get_property('delivery_cutoff_time'))
49
except Exception as ex:
50
    print "[ERROR] Unexpected config error:", sys.exc_info()[0]
51
    PREPAID_SHIPPING_CUTOFF_TIME = 15
3499 mandeep.dh 52
 
4019 chandransh 53
COD_SHIPPING_CUTOFF_TIME = 12
3499 mandeep.dh 54
 
2819 chandransh 55
PAGE_HEIGHT=defaultPageSize[1]
56
PAGE_WIDTH=defaultPageSize[0]
57
 
3499 mandeep.dh 58
ORDER_STATUS_TO_USER_TRUST_LEVEL_DELTA_DICT = {
59
        OrderStatus.SALES_RET_RESHIPPED     :  3,
60
        OrderStatus.SALES_RETURN_IN_TRANSIT : -5,
61
        OrderStatus.DELIVERY_SUCCESS        :  1,
3578 mandeep.dh 62
        OrderStatus.DOA_CERT_INVALID        : -5
3499 mandeep.dh 63
}
64
 
104 ashish 65
def get_new_transaction():
66
    transaction = Transaction()
67
    transaction.createdOn = datetime.datetime.now()
738 chandransh 68
    transaction.status = TransactionStatus.INIT
104 ashish 69
    transaction.status_message = "New transaction"
483 rajveer 70
    return transaction
71
 
72
def create_order(t_order):
73
    order = Order()
74
    if t_order.warehouse_id:
75
        order.warehouse_id = t_order.warehouse_id
509 rajveer 76
    if t_order.logistics_provider_id:    
483 rajveer 77
        order.logistics_provider_id = t_order.logistics_provider_id
78
        order.airwaybill_no = t_order.airwaybill_no
79
        order.tracking_id = t_order.tracking_id
4009 chandransh 80
    if t_order.expected_shipping_time:
4019 chandransh 81
        order.expected_shipping_time = to_py_date(t_order.expected_shipping_time).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
4102 chandransh 82
        order.promised_shipping_time = order.expected_shipping_time
4009 chandransh 83
    if t_order.expected_delivery_time:
483 rajveer 84
        order.expected_delivery_time = to_py_date(t_order.expected_delivery_time)
4102 chandransh 85
        order.promised_delivery_time = order.expected_delivery_time
483 rajveer 86
    if t_order.customer_id:    
87
        order.customer_id = t_order.customer_id
88
        order.customer_name = t_order.customer_name
860 rajveer 89
        order.customer_city = t_order.customer_city
90
        order.customer_state = t_order.customer_state
483 rajveer 91
        order.customer_mobilenumber = t_order.customer_mobilenumber
92
        order.customer_pincode = t_order.customer_pincode
738 chandransh 93
        order.customer_address1 = t_order.customer_address1
94
        order.customer_address2 = t_order.customer_address2
483 rajveer 95
        order.customer_email = t_order.customer_email
96
    #new status and status description to be added    
716 rajveer 97
    order.status = t_order.status  
483 rajveer 98
    order.statusDescription = t_order.statusDescription
99
    order.total_amount = t_order.total_amount
100
    order.total_weight = t_order.total_weight
101
    if t_order.created_timestamp:
102
        order.created_timestamp = to_py_date(t_order.created_timestamp)
103
    else:
685 chandransh 104
        order.created_timestamp = datetime.datetime.now()
483 rajveer 105
    return order
106
 
107
def create_transaction(t_transaction):
108
    t_orders = t_transaction.orders
109
    if not t_orders:
110
        raise TransactionServiceException(101, "Orders missing from the transaction")
111
    transaction = get_new_transaction()
112
    transaction.customer_id = t_transaction.customer_id 
113
    transaction.shopping_cart_id = t_transaction.shoppingCartid
2219 varun.gupt 114
    transaction.coupon_code = t_transaction.coupon_code
2815 vikas 115
    transaction.session_source = t_transaction.sessionSource
116
    transaction.session_start_time = to_py_date(t_transaction.sessionStartTime)
3858 vikas 117
    transaction.first_source = t_transaction.firstSource
118
    transaction.first_source_start_time = to_py_date(t_transaction.firstSourceTime)
483 rajveer 119
 
120
    for t_order in t_orders:
121
        order = create_order(t_order)
122
        order.transaction = transaction 
123
        for line_item in t_order.lineitems:
124
            litem = LineItem()
716 rajveer 125
            litem.item_id = line_item.item_id
963 chandransh 126
            litem.productGroup = line_item.productGroup
483 rajveer 127
            litem.brand = line_item.brand
128
            if line_item.model_number:
129
                litem.model_number = line_item.model_number
130
            if line_item.model_name:    
131
                litem.model_name = line_item.model_name
908 chandransh 132
            if line_item.color:
133
                litem.color = line_item.color
483 rajveer 134
            if line_item.extra_info:
135
                litem.extra_info = line_item.extra_info
136
            litem.quantity = line_item.quantity
137
            litem.unit_price = line_item.unit_price
138
            litem.unit_weight = line_item.unit_weight
139
            litem.total_price = line_item.total_price
140
            litem.total_weight = line_item.total_weight
4283 anupam.sin 141
            '''litem.transfer_price = line_item.transfer_price'''
4172 rajveer 142
            litem.dealText = line_item.dealText
4295 varun.gupt 143
            litem.warranty_expiry_timestamp = to_py_date(line_item.warrantry_expiry_timestamp)
1976 varun.gupt 144
 
483 rajveer 145
            """
146
            if line_item.addedOn:
147
                litem.added_on = to_py_date(line_item.addedOn)
148
            else:
149
                litem.added_on = datetime.datetime.now()
150
            """
151
            litem.order = order
152
    session.commit()
153
    return transaction.id
154
 
155
def get_transaction(transaction_id):
156
    transaction = Transaction.get_by(id=transaction_id)
157
    if not transaction:
158
        raise TransactionServiceException(108, "no such transaction")
159
 
160
    return transaction
161
 
162
def get_transactions_for_customer(customer_id, from_date, to_date, status):
163
    if not customer_id:
164
        raise TransactionServiceException(101, "bad customer id")
165
    query = Transaction.query.filter(Transaction.customer_id == customer_id)
1493 vikas 166
    if status is not None:
483 rajveer 167
        query = query.filter(Transaction.status == status)
168
    if from_date:
169
        query = query.filter(Transaction.createdOn >from_date)
170
    if to_date:
171
        query = query.filter(Transaction.createdOn < to_date)
172
    return query.all()
173
 
174
def get_transactions_for_shopping_cart_id(shopping_cart_id):
175
    transactions = Transaction.query.filter_by(shopping_cart_id=shopping_cart_id).all()
176
    return transactions
177
 
178
def get_transaction_status(transaction_id):
179
    if not transaction_id:
180
        raise TransactionServiceException(101, "bad transaction id")
181
 
182
    transaction = get_transaction(transaction_id)
183
 
184
    return transaction.status
185
 
186
def change_transaction_status(transaction_id, new_status, description):
187
    transaction = get_transaction(transaction_id)
188
    transaction.status = new_status
189
    transaction.status_message = description
690 chandransh 190
    if new_status == TransactionStatus.FAILED:
191
        for order in transaction.orders:
192
            order.status = OrderStatus.PAYMENT_FAILED
2339 chandransh 193
            order.statusDescription = "Payment Failed"
4247 rajveer 194
    elif new_status == TransactionStatus.AUTHORIZED or new_status == TransactionStatus.FLAGGED:
690 chandransh 195
        for order in transaction.orders:
4247 rajveer 196
            if new_status == TransactionStatus.AUTHORIZED:
197
                order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
198
                order.statusDescription = "Submitted to warehouse"
199
            elif new_status == TransactionStatus.FLAGGED:
200
                order.status = OrderStatus.PAYMENT_FLAGGED
201
                order.statusDescription = "Payment flagged by gateway"
3092 chandransh 202
            order.cod = False
743 rajveer 203
            #After we got payment success, we will set logistics info also 
204
            logistics_client = LogisticsClient().get_client()
205
            #FIXME line item is only one now. If multiple will come, need to fix.
871 chandransh 206
            item_id = order.lineitems[0].item_id
3044 chandransh 207
            logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.PREPAID)
743 rajveer 208
 
1730 ankur.sing 209
            current_time = datetime.datetime.now()
210
            logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
4009 chandransh 211
            logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
1730 ankur.sing 212
 
743 rajveer 213
            order.warehouse_id = logistics_info.warehouseId
214
            order.logistics_provider_id = logistics_info.providerId
215
            order.airwaybill_no = logistics_info.airway_billno
216
            order.tracking_id = order.airwaybill_no
4019 chandransh 217
            order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
4102 chandransh 218
            order.promised_shipping_time = order.expected_shipping_time
1730 ankur.sing 219
            order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
3986 chandransh 220
            order.promised_delivery_time = order.expected_delivery_time 
871 chandransh 221
 
3133 rajveer 222
            catalog_client = CatalogClient().get_client()
871 chandransh 223
            catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, order.lineitems[0].quantity)
1351 varun.gupt 224
 
4316 anupam.sin 225
            try:
226
                item_pricing = catalog_client.getItemPricing(item_id, -1)
227
                order.lineitems[0].transfer_price = item_pricing.transferPrice
228
            except:
229
                print "Not able to get transfer price. Skipping"
3064 chandransh 230
    elif new_status == TransactionStatus.COD_IN_PROCESS:
231
        for order in transaction.orders:
232
            order.status = OrderStatus.INIT
233
            order.statusDescription = "Verification Pending"
234
            order.cod = True
235
            #After we got payment success, we will set logistics info also 
236
            logistics_client = LogisticsClient().get_client()
237
            #FIXME line item is only one now. If multiple will come, need to fix.
238
            item_id = order.lineitems[0].item_id
239
            logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.COD)
240
 
241
            current_time = datetime.datetime.now()
242
            logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
4009 chandransh 243
            logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
3064 chandransh 244
 
245
            order.warehouse_id = logistics_info.warehouseId
246
            order.logistics_provider_id = logistics_info.providerId
247
            order.airwaybill_no = logistics_info.airway_billno
248
            order.tracking_id = order.airwaybill_no
4019 chandransh 249
            order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0)
4102 chandransh 250
            order.promised_shipping_time = order.expected_shipping_time
3064 chandransh 251
            order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
3986 chandransh 252
            order.promised_delivery_time = order.expected_delivery_time
3064 chandransh 253
 
3133 rajveer 254
            catalog_client = CatalogClient().get_client()
3064 chandransh 255
            catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, order.lineitems[0].quantity)
256
 
4316 anupam.sin 257
            try:
258
                item_pricing = catalog_client.getItemPricing(item_id, -1)
259
                order.lineitems[0].transfer_price = item_pricing.transferPrice        
260
            except:
261
                print "Not able to get transfer price. Skipping"
483 rajveer 262
    session.commit()
3499 mandeep.dh 263
 
3530 mandeep.dh 264
    try:
265
        if new_status == TransactionStatus.COD_IN_PROCESS:
4008 mandeep.dh 266
            transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
267
            transaction_requiring_extra_processing.category = 'COD_VERIFICATION'
268
            transaction_requiring_extra_processing.transaction_id = transaction_id
269
            session.commit()
4247 rajveer 270
        elif new_status == TransactionStatus.FLAGGED:
271
            transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
272
            transaction_requiring_extra_processing.category = 'PAYMENT_FLAGGED'
273
            transaction_requiring_extra_processing.transaction_id = transaction_id
274
            session.commit()
3530 mandeep.dh 275
    except Exception as e:
4065 mandeep.dh 276
        print "Error inserting transaction Id: " + str(transaction_id) + " due to " + str(e)
3499 mandeep.dh 277
 
483 rajveer 278
    return True
279
 
3994 chandransh 280
def __get_holidays(start_time=0L, to_time=-1L):
281
    '''
282
    Get the list of all public holidays since start time including the
283
    day of start time if it's a holiday
284
    '''
285
    midnightTime = start_time.replace(hour=0, minute=0, second=0, microsecond=0)
1730 ankur.sing 286
    fromDate = to_java_date(midnightTime)
287
    logistics_client = LogisticsClient().get_client()
3994 chandransh 288
    #TODO: This list should be cached after the first call.
289
    #Calling logistics server is also unnecessary.
290
    holidays = logistics_client.getHolidays(fromDate, to_time)
291
    holidays = [to_py_date(holiday).date() for holiday in holidays]
292
    return holidays
293
 
294
 
295
def adjust_delivery_time(start_time, delivery_days):
296
    '''
297
    Returns the actual no. of days which will pass while 'delivery_days'
298
    no. of business days will pass since 'order_time'. 
299
    '''
300
    start_date = start_time.date()
301
    end_date = start_date + datetime.timedelta(days = delivery_days)
302
    holidays = __get_holidays(start_time)
1730 ankur.sing 303
 
3994 chandransh 304
    while start_date <= end_date:
305
        if start_date.weekday() == 6 or start_date in holidays:
1730 ankur.sing 306
            delivery_days = delivery_days + 1
3994 chandransh 307
            end_date = end_date + datetime.timedelta(days = 1)
4088 chandransh 308
        start_date = start_date + datetime.timedelta(days = 1)
3994 chandransh 309
 
1730 ankur.sing 310
    return delivery_days
311
 
1398 varun.gupt 312
def enqueue_transaction_info_email(transaction_id):
1262 varun.gupt 313
    transaction = get_transaction(transaction_id)
314
 
315
    html_header = """
316
<html>
317
<body>
318
<div>
319
    <p>
320
        Hello,<br /><br />
1742 rajveer 321
        Thanks for placing order with us. Following are the details of your order:
1262 varun.gupt 322
    </p>
323
    <div>
324
        Order Date: $order_date
1569 varun.gupt 325
    </div>
1262 varun.gupt 326
    <div>
327
        <table>
1569 varun.gupt 328
        <tr><td colspan="5"><hr /></td></tr>
329
        <tr><td colspan="5" align="left"><b>Order Details</b></td></tr>
330
        <tr><td colspan="5"><hr /></td></tr>
1262 varun.gupt 331
        <tr>
2050 varun.gupt 332
            <th width="100">Order No.</th>
333
            <th>Product</th>
334
            <th width="100">Quantity</th>
335
            <th width="100">Unit Price</th>
336
            <th width="100">Amount</th>
1262 varun.gupt 337
        </tr>
338
    """
1398 varun.gupt 339
    user_email = ""
1262 varun.gupt 340
    total_amount = 0.0
341
    html_table = ""
1569 varun.gupt 342
    order_date = datetime.datetime.now()
1262 varun.gupt 343
 
344
    for order in transaction.orders:
345
        order_date = order.created_timestamp
1398 varun.gupt 346
        user_email = order.customer_email
1569 varun.gupt 347
        html_tr = "<tr><td align='center'>" + str(order.id) + "</td>"
1262 varun.gupt 348
 
349
        for lineitem in order.lineitems:
2050 varun.gupt 350
            lineitem_total_price = round(lineitem.total_price, 2)
351
 
1276 chandransh 352
            html_tr += "<td>" + str(lineitem) + "</td>"
2050 varun.gupt 353
            html_tr += "<td align='center'>" + ("%.0f" % lineitem.quantity) + "</td>"
354
            html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem.unit_price) + "</td>"
355
            html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem_total_price) + "</td></tr>"
356
            total_amount += lineitem_total_price
1262 varun.gupt 357
            html_table += html_tr
2050 varun.gupt 358
 
1262 varun.gupt 359
    html_footer = """
360
        <tr><td colspan=5>&nbsp;</td></tr>
1565 varun.gupt 361
        <tr><td colspan=5><hr /></td></tr>
1262 varun.gupt 362
        <tr>
363
            <td colspan=4>Total Amount</td>
2050 varun.gupt 364
            <td> Rs. $total_amount</td>
1262 varun.gupt 365
        </tr>
1569 varun.gupt 366
        <tr><td colspan=5><hr /></td></tr>
4232 varun.gupt 367
        <tr>
368
            <td colspan=5>
369
                Note:- With the onset of dense fog in North India, it severely hits the operations of our courier partners. 
370
                We would like to mention that our deliveries might be slightly delayed. 
371
                We look forward to your support and understanding with regards to anticipated delays.
372
            </td>
373
        </tr>
1262 varun.gupt 374
        </table>
1569 varun.gupt 375
    </div>
1262 varun.gupt 376
    <p>
377
        Best Wishes,<br />
378
        Saholic Team
379
    </p>
380
</div>
381
</body>
382
</html>
383
    """
1398 varun.gupt 384
 
1569 varun.gupt 385
    dt = datetime.datetime.strptime(str(order_date), "%Y-%m-%d %H:%M:%S")
1565 varun.gupt 386
    formated_order_date = dt.strftime("%A, %d. %B %Y %I:%M%p")
387
 
1262 varun.gupt 388
    email_header = Template(html_header).substitute(dict(order_date = formated_order_date))
2050 varun.gupt 389
    email_footer = Template(html_footer).substitute(dict(total_amount = "%.2f" % total_amount))
1262 varun.gupt 390
 
1351 varun.gupt 391
    try:
392
        helper_client = HelperClient().get_client()
1398 varun.gupt 393
        helper_client.saveUserEmailForSending(user_email, "", "Saholic - Order Details", email_header + html_table + email_footer, str(transaction_id), "TransactionInfo")
1351 varun.gupt 394
        return True
395
    except Exception as e:
396
        print e
397
        return False
1262 varun.gupt 398
 
483 rajveer 399
def get_order(order_id):
400
    order = Order.get_by(id=order_id)
401
    if not order:
402
        raise TransactionServiceException(108, "no such order")
403
 
404
    return order
405
 
3014 chandransh 406
def get_orders_for_customer(customer_id, from_date, to_date, statuses):
483 rajveer 407
    if not customer_id:
408
        raise TransactionServiceException(101, "bad customer id")
409
    query = Order.query.filter(Order.customer_id == customer_id)
3014 chandransh 410
    if statuses:
411
        query = query.filter(Order.status.in_(tuple(statuses)))
483 rajveer 412
    if from_date:
413
        query = query.filter(Order.created_timestamp >from_date)
414
    if to_date:
415
        query = query.filter(Order.created_timestamp < to_date)
416
    return query.all()
417
 
1528 ankur.sing 418
def get_order_for_customer(order_id, customer_id):
419
    if not customer_id:
420
        raise TransactionServiceException(101, "bad customer id")
421
    order = get_order(order_id)
422
    if order.customer_id != customer_id:
423
        raise TransactionServiceException(108, "order: " + str(order_id) + " does not belong to customer: " + str(customer_id))
424
    return order
425
 
483 rajveer 426
def get_all_orders(status, from_date, to_date, warehouse_id):
427
    if not warehouse_id:
428
        raise TransactionServiceException(101, "bad warehouse id")
429
    query = Order.query.filter(Order.warehouse_id == warehouse_id)
430
    if status:
431
        query = query.filter(Order.status == status)
432
    if from_date:
433
        query = query.filter(Order.created_timestamp >from_date)
434
    if to_date:
435
        query = query.filter(Order.created_timestamp < to_date)
436
    return query.all()
437
 
4133 chandransh 438
def get_orders_in_batch(statuses=[], offset=0, limit=0, warehouse_id=None):
4296 rajveer 439
    query = Order.query
440
    if warehouse_id:
441
        query = query.filter(Order.warehouse_id == warehouse_id)
4133 chandransh 442
    if statuses:
443
        query = query.filter(Order.status.in_(statuses))
444
    query = query.offset(offset)
445
    if limit:
446
        query = query.limit(limit)
447
    return query.all()
448
 
449
def get_order_count(statuses=[], warehouse_id=None):
450
    if not warehouse_id:
451
        raise TransactionServiceException(101, "bad warehouse id")
452
    query = session.query(func.count(Order.id)).filter(Order.warehouse_id == warehouse_id)
453
    if statuses:
454
        query = query.filter(Order.status.in_(statuses))
455
    return query.scalar()
456
 
1382 varun.gupt 457
def get_returnable_orders_for_customer(customer_id, limit = None):
458
    if not customer_id:
459
        raise TransactionServiceException(101, "bad customer id")
460
 
461
    query = Order.query.filter(Order.customer_id == customer_id)
462
    query = query.filter(Order.status != OrderStatus.PAYMENT_FAILED)
463
    query = query.filter(Order.status != OrderStatus.REJECTED)
464
    query = query.filter(Order.status > OrderStatus.SHIPPED_FROM_WH)
465
    query = query.filter(or_(Order.status < OrderStatus.DELIVERY_SUCCESS, Order.delivery_timestamp < datetime.datetime.now() + datetime.timedelta(hours = 48)))
466
    orders = query.all()
467
    order_ids = [order.id for order in orders]
468
    return order_ids
469
 
470
def get_cancellable_orders_for_customer(customer_id, limit = None):
471
    if not customer_id:
472
        raise TransactionServiceException(101, "bad customer id")
473
 
474
    query = Order.query.filter(Order.customer_id == customer_id)
4343 varun.gupt 475
    query = query.filter(Order.status != OrderStatus.PAYMENT_FAILED)
1382 varun.gupt 476
    query = query.filter(Order.status != OrderStatus.REJECTED)
4288 anupam.sin 477
    query = query.filter(Order.status <= OrderStatus.BILLED)
1382 varun.gupt 478
    orders = query.all()
479
    return [order.id for order in orders]
480
 
995 varun.gupt 481
def get_orders_by_billing_date(status, start_billing_date, end_billing_date, warehouse_id):
482
    if not warehouse_id:
483
        raise TransactionServiceException(101, "bad warehouse id")
484
 
485
    query = Order.query.filter(Order.warehouse_id == warehouse_id)
486
 
487
    if status:
488
        query = query.filter(Order.status == status)
489
 
490
    if start_billing_date:
491
        query = query.filter(Order.billing_timestamp >= start_billing_date)
492
 
493
    if end_billing_date:
494
        query = query.filter(Order.billing_timestamp <= end_billing_date)
495
 
496
    return query.all()
497
 
3451 chandransh 498
def get_orders_by_shipping_date(from_shipping_date, to_shipping_date, provider_id, warehouse_id, cod):
499
    query = Order.query.filter(Order.cod == cod)
3427 chandransh 500
 
501
    if warehouse_id and warehouse_id != -1:
502
        query = query.filter(Order.warehouse_id == warehouse_id)
503
 
504
    if provider_id and provider_id != -1:
505
        query = query.filter(Order.logistics_provider_id == provider_id)
506
 
507
    if from_shipping_date:
508
        query = query.filter(Order.shipping_timestamp >= from_shipping_date)
509
 
510
    if to_shipping_date:
511
        query = query.filter(Order.shipping_timestamp <= to_shipping_date)
512
 
513
    query = query.order_by(Order.airwaybill_no)
514
 
515
    return query.all()
516
 
1528 ankur.sing 517
def get_orders_for_transaction(transaction_id, customer_id):
518
    orders = Order.query.filter_by(transaction_id=transaction_id, customer_id=customer_id).all()
519
 
1208 chandransh 520
    if not orders:
521
        raise TransactionServiceException(101, "No order for the transaction")
522
    return orders
523
 
1405 ankur.sing 524
def get_undelivered_orders(provider_id, warehouse_id):
525
    query = Order.query
526
    if provider_id != -1:
527
        query = query.filter_by(Order.logistics_provider_id == provider_id)
528
    if warehouse_id != -1:
529
        query = query.filter(Order.warehouse_id == warehouse_id)
530
    query = query.filter(and_(Order.status != OrderStatus.DELIVERY_SUCCESS, Order.status != OrderStatus.FAILED, \
531
                              Order.status > OrderStatus.SHIPPED_FROM_WH))
1479 ankur.sing 532
    #Get last midnight time instead of current time as courier company would have sent the 
533
    #delivery records updated at least till midnight and not by the current time.
534
    time_format = "%Y-%m-%d %H:%M:%S.%f"
535
    t = time.strptime(str(datetime.datetime.now()), time_format)
536
    midnightTime = datetime.datetime(*t[:3])
537
    query = query.filter(Order.expected_delivery_time < midnightTime)
1405 ankur.sing 538
    orders = query.all()
539
    return orders
540
 
1208 chandransh 541
def get_line_items_for_order(order_id):
542
    query = LineItem.query.filter(LineItem.order_id == order_id)
543
    return query.all()
544
 
483 rajveer 545
def change_order_status(self, orderId, status, description):
546
    order = get_order(orderId)
547
    order.status = status
548
    order.statusDescription = description
549
    session.commit()
3578 mandeep.dh 550
    update_trust_level(order)
551
    return True
3499 mandeep.dh 552
 
3578 mandeep.dh 553
def update_trust_level(order):
3530 mandeep.dh 554
    try:
3578 mandeep.dh 555
        trust_level_delta = 0
556
        if order.cod == True:
557
            trust_level_delta = ORDER_STATUS_TO_USER_TRUST_LEVEL_DELTA_DICT.get(order.status, 0)
558
 
3530 mandeep.dh 559
        if (trust_level_delta != 0):
560
            user_client = UserClient().get_client()
561
            user_client.increaseTrustLevel(order.customer_id, trust_level_delta)
562
    except Exception as e:
563
        print e
3499 mandeep.dh 564
 
483 rajveer 565
    return True
3064 chandransh 566
 
567
def verify_order(self, orderId):
568
    logging.info("Verifying order no: " + str(orderId))
569
    order = get_order(orderId)
570
    if order.status == OrderStatus.INIT:
571
        order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
572
        order.statusDescription = "Submitted for processing"
573
        order.verification_timestamp = datetime.datetime.now()
574
        session.commit()
575
        logging.info("Successfully verified order no: " + str(orderId))
576
        return True
577
    else:
578
        logging.warning("Verify called for order no." + str(orderId) +" which is not in verification pending state");
579
        return False
580
 
4285 rajveer 581
def accept_order(orderId):
3064 chandransh 582
    logging.info("Accepting order no: " + str(orderId))
921 rajveer 583
    order = get_order(orderId)
4306 rajveer 584
    if order.status in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT]:
3064 chandransh 585
        if not order.cod:
586
            if order.transaction.status == TransactionStatus.AUTHORIZED:
587
                __capture_txn(order.transaction.id)
3010 chandransh 588
        order.status = OrderStatus.ACCEPTED
589
        order.statusDescription = "Order Accepted"
590
        order.accepted_timestamp = datetime.datetime.now()
591
        session.commit()
3064 chandransh 592
        logging.info("Successfully accepted the order no.:" + str(orderId))
3010 chandransh 593
        return True
594
    else:
3064 chandransh 595
        logging.warning("Accept called for the unacceptable order: " + str(orderId))
3010 chandransh 596
        return False
921 rajveer 597
 
3010 chandransh 598
def __capture_txn(txnId):
599
    logging.info("Capturing payment for merchant txn:" + str(txnId))
600
    payment_client = PaymentClient().get_client()
601
    capture_result = payment_client.capturePayment(txnId)
602
    if capture_result:
3064 chandransh 603
        logging.info("Successfully captured payment for merchant txn:" + str(txnId))
604
        change_transaction_status(txnId, TransactionStatus.IN_PROCESS, "Payment received")
3010 chandransh 605
    else:
606
        raise TransactionServiceException(115, "Payment capture failed.")
607
 
4285 rajveer 608
def add_billing_details(orderId, invoice_number, imeiNumber, itemNumber, billedBy, jacketNumber, billingType, vendorId):
609
    if jacketNumber is None or jacketNumber <= 0:
2783 chandransh 610
        raise TransactionServiceException(110, "Invalid jacket number")
611
 
4285 rajveer 612
    if billedBy is None or billedBy.strip() == "":
2783 chandransh 613
        raise TransactionServiceException(110, "Invalid Biller")
614
 
1149 chandransh 615
    order = Order.get_by(id=orderId)
616
    if not order:
2783 chandransh 617
        raise TransactionServiceException(101, "No order found for the given order id" + str(orderId))
618
 
4283 anupam.sin 619
    if order.status == OrderStatus.ACCEPTED:
4285 rajveer 620
        order.jacket_number = jacketNumber
621
        if billingType == BillingType.OURS:
622
            if itemNumber is None or itemNumber.strip() == "":
4283 anupam.sin 623
                raise TransactionServiceException(110, "Invalid item number")
624
 
625
            order.invoice_number = get_next_invoice_number()
626
            lineitem = order.lineitems[0]
4285 rajveer 627
            if lineitem.productGroup == "Handsets" and imeiNumber <= 0:
4283 anupam.sin 628
                raise TransactionServiceException(110, "No IMEI number supplied for a handset.")
4285 rajveer 629
            lineitem.item_number = itemNumber
630
            if imeiNumber > 0:
631
                lineitem.imei_number = str(imeiNumber)
4283 anupam.sin 632
        else:
633
            order.invoice_number = invoice_number
634
 
2842 chandransh 635
        order.status = OrderStatus.BILLED
636
        order.statusDescription = "Order Billed"
637
        order.billing_timestamp = datetime.datetime.now()
4285 rajveer 638
        order.billed_by = billedBy
2842 chandransh 639
        lineitem = order.lineitems[0]
4283 anupam.sin 640
        item_id = lineitem.item_id
641
        catalog_client = CatalogClient().get_client()
4285 rajveer 642
        item_pricing = catalog_client.getItemPricing(item_id, vendorId)
643
        order.vendorId = vendorId
4283 anupam.sin 644
        lineitems = order.lineitems
645
        for lineitem in lineitems:
646
            catalog_client.reduceReservationCount(item_id, order.warehouse_id, lineitem.quantity)
647
        order.lineitems[0].transfer_price = item_pricing.transferPrice
648
        session.commit()
4365 mandeep.dh 649
 
650
        # For Mahipal warehouse, we need to scan out items for every billed order
651
        if order.warehouse_id == 7:
652
            try:
653
                warehouse_client = WarehouseClient().get_client()
654
                warehouse_client.scanOut(itemNumber, str(imeiNumber), ScanType.SALE)
655
            except:
656
                print sys.exc_info()[0]
657
                print 'Could not scan out orders'
658
 
4283 anupam.sin 659
        return True
660
    else:
661
        return False
1149 chandransh 662
 
1220 chandransh 663
def batch_orders(warehouseId):
664
    if not warehouseId:
665
        raise TransactionServiceException(101, "bad warehouse id")
1225 chandransh 666
 
667
    batchno_generator = BatchNoGenerator()
668
    session.commit()
1220 chandransh 669
    query = Order.query.filter_by(warehouse_id=warehouseId)
4381 mandeep.dh 670
    query = query.filter(or_(Order.status == OrderStatus.SUBMITTED_FOR_PROCESSING, Order.status == OrderStatus.INVENTORY_LOW))
1220 chandransh 671
    query = query.order_by(Order.created_timestamp)
672
    pending_orders = query.all()
1225 chandransh 673
 
1220 chandransh 674
    serial_no = 1
675
    for order in pending_orders:
1276 chandransh 676
        order.batchNo = batchno_generator.id
1220 chandransh 677
        order.serialNo = serial_no
678
        serial_no += 1
679
    session.commit() 
680
    pending_orders = query.all()
681
    return pending_orders
682
 
683
def order_outofstock(orderId):
1911 chandransh 684
    '''
685
    Mark order as out of stock
686
    '''
1220 chandransh 687
    order = get_order(orderId)
688
    order.status = OrderStatus.INVENTORY_LOW
689
    order.statusDescription = "Low Inventory"
690
    order.outofstock_timestamp = datetime.datetime.now()
691
    session.commit()
692
    return True
693
 
3064 chandransh 694
def mark_orders_as_manifested(warehouse_id, provider_id, cod):
759 chandransh 695
    try:
986 chandransh 696
        current_timestamp = datetime.datetime.now()
3064 chandransh 697
        orders = Order.query.filter_by(warehouse_id = warehouse_id, logistics_provider_id = provider_id, status = OrderStatus.BILLED, cod = cod).all()
759 chandransh 698
        for order in orders:
699
            order.status = OrderStatus.SHIPPED_FROM_WH
987 chandransh 700
            order.statusDescription = "Order shipped from warehouse"
986 chandransh 701
            order.shipping_timestamp = current_timestamp
759 chandransh 702
        session.commit()
703
        return True
704
    except:
705
        return False
1113 chandransh 706
 
707
def mark_orders_as_picked_up(provider_id, pickup_details):
708
    for awb, pickup_timestamp in pickup_details.iteritems():
1135 chandransh 709
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
2684 chandransh 710
        if order == None or order.status != OrderStatus.SHIPPED_FROM_WH:
2220 chandransh 711
            #raise TransactionServiceException(102, "No order found for the awb: " + awb)
712
            continue
1113 chandransh 713
        order.status = OrderStatus.SHIPPED_TO_LOGST
714
        order.statusDescription = "Order picked up by Courier Company"
1246 chandransh 715
        order.pickup_timestamp = pickup_timestamp
1113 chandransh 716
    session.commit()
1217 chandransh 717
 
1132 chandransh 718
    current_time = datetime.datetime.now()
719
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
1246 chandransh 720
    from_datetime = to_datetime - datetime.timedelta(2)
1132 chandransh 721
    orders_not_picked_up = Order.query.filter_by(logistics_provider_id = provider_id).filter_by(status=OrderStatus.SHIPPED_FROM_WH).filter(Order.shipping_timestamp >= from_datetime).filter(Order.shipping_timestamp <= to_datetime).all()
1113 chandransh 722
    return orders_not_picked_up
723
 
1132 chandransh 724
def mark_orders_as_delivered(provider_id, delivered_orders):
725
    for awb, detail in delivered_orders.iteritems():
1135 chandransh 726
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
2684 chandransh 727
        if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST]:
1911 chandransh 728
            continue
729
            #raise TransactionServiceException(103, "No order found for the awb:" + awb)
1132 chandransh 730
        order.status = OrderStatus.DELIVERY_SUCCESS
731
        order.statusDescription = "Order delivered"
732
        order.delivery_timestamp, order.receiver = detail.split('|')
3578 mandeep.dh 733
        update_trust_level(order)
1132 chandransh 734
    session.commit()
735
 
1135 chandransh 736
def mark_orders_as_failed(provider_id, returned_orders):
737
    for awb, detail in returned_orders.iteritems():
738
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
2684 chandransh 739
        if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST]:
740
            continue
741
            #raise TransactionServiceException(103, "No order found for the awb:" + awb)
742
        order.status = OrderStatus.SALES_RETURN_IN_TRANSIT
1135 chandransh 743
        order.delivery_timestamp, reason = detail.split('|')
744
        order.statusDescription = "Order Returned to Origin:" + reason
3578 mandeep.dh 745
        update_trust_level(order)
1911 chandransh 746
    session.commit()
1135 chandransh 747
 
1246 chandransh 748
def update_non_delivery_reason(provider_id, undelivered_orders):
749
    for awb, reason in undelivered_orders.iteritems():
750
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
751
        if order == None:
2684 chandransh 752
            continue
753
            #raise TransactionServiceException(103, "No order found for the awb:" + awb)
1246 chandransh 754
        order.statusDescription = reason
755
    session.commit()
756
 
4394 rajveer 757
def get_alerts(type, status, timestamp):
758
    query = Alert.query.filter(Alert.type == type)
759
    query = query.filter(Alert.status == status)
760
    if timestamp:
761
        query = query.filter(Alert.timestamp >= to_py_date(timestamp))
483 rajveer 762
    return query.all()
763
 
4394 rajveer 764
def add_alert(type, description):
765
    alert = Alert()
766
    alert.type = type
767
    alert.status = 1
768
    alert.description = description
769
    alert.timestamp = datetime.datetime.now()
766 rajveer 770
    session.commit()    
771
 
1596 ankur.sing 772
def get_valid_order_count():
2851 chandransh 773
    '''
774
    Returns the number of orders which we processed. The reshipped orders are not
775
    counted since there'll always be a new compensating order present.
776
    '''
3216 chandransh 777
    return Order.query.filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).filter(not_(Order.status.in_((OrderStatus.SALES_RET_RESHIPPED, OrderStatus.DOA_INVALID_RESHIPPED, OrderStatus.DOA_RESHIPPED, OrderStatus.CANCELED)))).count()
1596 ankur.sing 778
 
1627 ankur.sing 779
def get_cust_count_with_successful_txn():
2851 chandransh 780
    '''
781
    Returns the number of distinct customers who have done successful transactions.
782
    It uses the sqlalchemy func module to construct an efficient query.
783
    '''
784
    return session.query(func.count(distinct(Order.customer_id))).filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).scalar()
1627 ankur.sing 785
 
1730 ankur.sing 786
def get_valid_orders_amount_range():
2851 chandransh 787
    '''
788
    Returns a list containing the minimum and maximum amount across all
789
    orders placed with us. No need to consider reshippped orders for min/max
790
    amount of orders.
791
    '''
792
    return session.query(func.min(Order.total_amount), func.max(Order.total_amount)).filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).one()
1627 ankur.sing 793
 
1886 ankur.sing 794
def get_valid_orders(limit):
2851 chandransh 795
    '''
796
    Returns the last limit number of valid orders placed with us. Ignored the
797
    limit if it's passed as 0. Raises an excpetion if the supplied limit is 
798
    less than 0. 
799
    '''
800
    if limit < 0:
801
        raise TransactionServiceException(101, "Invalid argument limit " + limit)
3072 chandransh 802
    query = Order.query.filter(Order.status >= OrderStatus.INIT)
1886 ankur.sing 803
    query = query.order_by(desc(Order.created_timestamp))
2851 chandransh 804
    if limit != 0:
1886 ankur.sing 805
        query = query.limit(limit)
806
    return query.all()
807
 
2783 chandransh 808
def get_next_invoice_number():
809
    entity_id = InvoiceIDGenerator.query.with_lockmode("update").one()
810
    invoice_number = entity_id.id + 1
811
    entity_id.id = invoice_number
812
    return invoice_number
813
 
2536 chandransh 814
def toggle_doa_flag(order_id):
815
    order = get_order(order_id)
816
    if(order.doaFlag):
817
        order.doaFlag = False
818
    else:
819
        order.doaFlag = True
820
    session.commit()
821
    return order.doaFlag
822
 
823
def request_pickup_number(order_id):
824
    order = get_order(order_id)
825
    if order.status != OrderStatus.DELIVERY_SUCCESS and order.status != OrderStatus.DOA_PICKUP_REQUESTED:
826
        return False
827
    from_user = 'cnc.center@shop2020.in'
828
    from_pwd = '5h0p2o2o'
829
 
4227 rajveer 830
    subject = "Pickup request from " + order.customer_city
2536 chandransh 831
    try:
3133 rajveer 832
        catalog_client = CatalogClient().get_client()
2536 chandransh 833
        warehouse = catalog_client.getWarehouse(order.warehouse_id)
834
 
835
        logistics_client = LogisticsClient().get_client()
836
        provider = logistics_client.getProvider(order.logistics_provider_id)
3051 chandransh 837
        to_addr = provider.details[DeliveryType.PREPAID].email
2536 chandransh 838
        raw_message = '''
839
        Dear Sir/Madam,
840
 
2559 chandransh 841
        Kindly arrange a pickup today from %(customer_city)s. Pickup and delivery addresses are mentioned below.
2536 chandransh 842
 
843
        Pickup CODE: %(provider_code)s
844
        Total Weight: %(order_weight)s kg
845
 
2559 chandransh 846
        Pickup Address:
2536 chandransh 847
 
848
        %(customer_address)s
849
 
850
        Delivery Address:
851
 
852
        %(warehouse_executive)s
853
        %(warehouse_address)s
854
        PIN %(warehouse_pin)s
855
        Phone: %(warehouse_phone)s
856
 
857
        Thanks and Regards
4227 rajveer 858
        Sandeep Sachdeva
2536 chandransh 859
        '''
2559 chandransh 860
        message = dedent(raw_message) % { 'customer_city' : order.customer_city,
3051 chandransh 861
                                          'provider_code' : provider.details[DeliveryType.PREPAID].accountNo,
2536 chandransh 862
                                          'order_weight' : str(order.total_weight),
863
                                          'customer_address' : __get_order_address(order),
864
                                          'warehouse_executive' : 'Parmod Kumar',
865
                                          'warehouse_address' : warehouse.location,
866
                                          'warehouse_pin' : warehouse.pincode,
867
                                          'warehouse_phone': '9971573026'}
4227 rajveer 868
        #FIXME Email is hardcoded for bluedart
869
        mail(from_user, from_pwd, [to_addr, 'sonikap@bluedart.com'], subject, message)
2536 chandransh 870
        order.status = OrderStatus.DOA_PICKUP_REQUESTED
871
        order.statusDescription = "Pick up requested for DOA"
872
        session.commit()
873
        return True
874
    except Exception as e:
875
        print sys.exc_info()[0]
876
        return False
877
 
878
def authorize_pickup(order_id, pickup_number):
879
    order = get_order(order_id)
880
    if order.status != OrderStatus.DOA_PICKUP_REQUESTED:
881
        return False
882
    from_user = 'help@shop2020.in'
883
    from_pwd = '5h0p2o2o'
884
    subject = 'Pickup details'
885
    filename = "/tmp/" +  str(order.id) +".pdf" 
886
    try:
3133 rajveer 887
        catalog_client = CatalogClient().get_client()
2536 chandransh 888
        warehouse = catalog_client.getWarehouse(order.warehouse_id)
889
 
890
        logistics_client = LogisticsClient().get_client()
891
        provider = logistics_client.getProvider(order.logistics_provider_id)
892
        provider_name = provider.name
893
 
894
        to_addr = order.customer_email
895
        today = date.today().strftime("%d-%B-%Y (%A)")
896
 
897
        raw_message = '''
898
        Dear Mr. %(customer_name)s,
899
 
900
        In reference to the return of order ID %(order_id)d. We request you to kindly pack the box properly with bubble wrap, and send it along with the original "DOA" certificate and the invoice.
901
        Take a printout of the attachment in this mail which contains the delivery address and paste it on the packed parcel.
902
        We have instructed %(provider_name)s to pick up the material from the below mentioned address on %(date)s. %(provider_name)s pickup request number is %(pickup_request_no)s. Kindly keep the parcel ready.
903
 
904
        Pickup will happen from the below mentioned address:-
905
 
906
        %(customer_address)s
907
 
908
        Do let us know in case of any concerns
909
 
910
        Thanks & Regards
911
        Saholic Team
912
        '''
913
        message = dedent(raw_message) % {'customer_name' : order.customer_name,
914
                                         'order_id' : order_id,
915
                                         'provider_name' : provider_name,
916
                                         'date': today,
917
                                         'pickup_request_no' : pickup_number,
918
                                         'customer_address' : __get_order_address(order)}
919
        print message
920
        __generate_return_advice(order, warehouse, provider, filename)
3527 chandransh 921
        mail(from_user, from_pwd, [to_addr], subject, message, [get_attachment_part(filename)])
2536 chandransh 922
        order.pickupRequestNo = pickup_number
923
        order.status = OrderStatus.DOA_RETURN_AUTHORIZED
924
        order.statusDescription = "DOA pick up authorized"
2764 chandransh 925
        order.doa_auth_timestamp = datetime.datetime.now()
2536 chandransh 926
        session.commit()
927
        return True
928
    except Exception as e:
4234 rajveer 929
        print sys.exc_info()
930
	traceback.print_tb(sys.exc_info()[2])
2536 chandransh 931
        return False
932
    finally:
933
        if os.path.exists(filename):
934
            os.remove(filename)
935
 
2764 chandransh 936
def mark_doas_as_picked_up(provider_id, pickup_details):
937
    for pickupNo, doa_pickup_timestamp in pickup_details.iteritems():
938
        order = Order.query.filter_by(pickupRequestNo=pickupNo, logistics_provider_id = provider_id).first()
939
        if order == None or order.status != OrderStatus.DOA_RETURN_AUTHORIZED:
940
            #raise TransactionServiceException(102, "No order found for the awb: " + awb)
941
            continue
942
        order.status = OrderStatus.DOA_RETURN_IN_TRANSIT
943
        order.statusDescription = "DOA picked up by Courier Company"
944
        order.doa_pickup_timestamp = doa_pickup_timestamp
945
    session.commit()
946
 
947
    current_time = datetime.datetime.now()
948
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
949
    from_datetime = to_datetime - datetime.timedelta(2)
950
    orders_not_picked_up = Order.query.filter_by(logistics_provider_id = provider_id).filter_by(status=OrderStatus.DOA_RETURN_AUTHORIZED).filter(Order.doa_auth_timestamp >= from_datetime).filter(Order.doa_auth_timestamp <= to_datetime).all()
951
    return orders_not_picked_up
952
 
2616 chandransh 953
def receive_return(order_id):
2591 chandransh 954
    order = get_order(order_id)
4365 mandeep.dh 955
 
956
    # FIXME: For Mahipal warehouse, we need to scan in items for every sales return
957
    if order.warehouse_id == 7:
958
        try:
959
            warehouse_client = WarehouseClient().get_client()
960
            lineitem = order.lineitems[0]
961
 
962
            # Inventory should not be updated for DOA returns 
963
            if order.status == OrderStatus.SALES_RETURN_IN_TRANSIT:
964
                warehouse_client.scanIn(order.id, lineitem.item_number, lineitem.imei_number, ScanType.SALE_RET)
965
                catalog_client = CatalogClient().get_client()
966
                catalog_client.reduceReservationCount(lineitem.item_id, order.warehouse_id, lineitem.quantity)
967
            elif order.status in [OrderStatus.DOA_RETURN_AUTHORIZED, OrderStatus.DOA_RETURN_IN_TRANSIT]:
968
                warehouse_client.scanIn(order.id, lineitem.item_number, lineitem.imei_number, ScanType.DOA_IN)
969
        except:
970
            print sys.exc_info()[0]
971
            print 'Could not scan in sales return orders'
972
 
2616 chandransh 973
    if order.status in [OrderStatus.DOA_RETURN_AUTHORIZED, OrderStatus.DOA_RETURN_IN_TRANSIT]:
974
        order.status = OrderStatus.DOA_RECEIVED
975
        order.statusDescription = "DOA package received"
2764 chandransh 976
        order.received_return_timestamp = datetime.datetime.now()
2616 chandransh 977
        session.commit()
978
    elif order.status == OrderStatus.SALES_RETURN_IN_TRANSIT :
979
        order.status = OrderStatus.SALES_RET_RECEIVED
980
        order.statusDescription = "Sales return received"
2764 chandransh 981
        order.received_return_timestamp = datetime.datetime.now()
2616 chandransh 982
        session.commit()
983
    else:
2591 chandransh 984
        return False
985
    return True
986
 
987
def validate_doa(order_id, is_valid):
988
    order = get_order(order_id)
2599 chandransh 989
    if order.status != OrderStatus.DOA_RECEIVED:
2591 chandransh 990
        return False
991
    if is_valid:
2609 chandransh 992
        order.status = OrderStatus.DOA_CERT_VALID
993
        order.statusDescription = "DOA Certificate Valid"
2591 chandransh 994
    else:
995
        order.status = OrderStatus.DOA_CERT_INVALID
996
        order.statusDescription = "DOA Certificate Invalid"
3578 mandeep.dh 997
        update_trust_level(order)
2591 chandransh 998
    session.commit()
999
    return True
1000
 
2628 chandransh 1001
def reship_order(order_id):
1002
    """
1003
    If the order is in SALES_RET_RECEIVED or DOA_CERT_INVALID state, it does the following:
1004
        1. Creates a new order for processing in the BILLED state. All billing information is saved.
1005
        2. Marks the current order as one of the final states SALES_RET_RESHIPPED and DOA_INVALID_RESHIPPED depending on what state the order started in.
1006
 
1007
    If the order is in DOA_CERT_VALID state, it does the following:
1008
        1. Creates a new order for processing in the SUBMITTED_FOR_PROCESSING state.
1009
        2. Creates a return order for the warehouse executive to return the DOA material.
1010
        3. Marks the current order as the final DOA_RESHIPPED state.
1011
 
1012
    Returns the id of the newly created order.
1013
 
1014
    Throws an exception if the order with the given id couldn't be found.
1015
 
1016
    Parameters:
1017
     - orderId
1018
    """
1019
    order = get_order(order_id)
1020
    if order.status == OrderStatus.SALES_RET_RECEIVED:
4220 rajveer 1021
        new_order = __clone_order(order, True, order.cod)
2628 chandransh 1022
        order.status = OrderStatus.SALES_RET_RESHIPPED
1023
        order.statusDescription = "Order Reshipped"
1024
        session.commit()
3578 mandeep.dh 1025
        update_trust_level(order)
2628 chandransh 1026
    elif order.status == OrderStatus.DOA_CERT_INVALID:
4220 rajveer 1027
        new_order = __clone_order(order, True, False)
2628 chandransh 1028
        order.status = OrderStatus.DOA_INVALID_RESHIPPED
1029
        order.statusDescription = "Order Reshipped"
1030
        session.commit()
1031
    elif order.status == OrderStatus.DOA_CERT_VALID:
4220 rajveer 1032
        new_order = __clone_order(order, False, False)
2628 chandransh 1033
        ret_order = __create_return_order(order)
1034
        order.status = OrderStatus.DOA_RESHIPPED
1035
        order.statusDescription = "Order Reshipped"
1036
        session.commit()
1037
    else:
1038
        raise TransactionServiceException(112, "This order can't be reshipped")
1039
 
2764 chandransh 1040
    order.reship_timestamp = datetime.datetime.now()
2628 chandransh 1041
    order.new_order_id = new_order.id
1042
    session.commit()
1043
 
1044
    return new_order.id
1045
 
3226 chandransh 1046
def refund_order(order_id, refunded_by, reason):
2628 chandransh 1047
    """
1048
    If the order is in SALES_RET_RECEIVED, DOA_CERT_VALID or DOA_CERT_INVALID state, it does the following:
1049
        1. Creates a refund request for batch processing.
1050
        2. Creates a return order for the warehouse executive to return the shipped material.
1051
        3. Marks the current order as SALES_RET_REFUNDED, DOA_VALID_REFUNDED or DOA_INVALID_REFUNDED final states.
1052
 
1053
    If the order is in SUBMITTED_FOR_PROCESSING or INVENTORY_LOW state, it does the following:
1054
        1. Creates a refund request for batch processing.
3226 chandransh 1055
        2. Cancels the reservation of the item in the warehouse.
1056
        3. Marks the current order as the REFUNDED final state.
2628 chandransh 1057
 
3226 chandransh 1058
    For all COD orders, if the order is in INIT, SUBMITTED_FOR_PROCESSING or INVENTORY_LOW state, it does the following:
1059
        1. Cancels the reservation of the item in the warehouse.
1060
        2. Marks the current order as CANCELED.
1061
 
1062
    In all cases, it updates the reason for cancellation or refund and the person who performed the action.
1063
 
1064
    Returns True if it is successful, False otherwise.
2628 chandransh 1065
 
1066
    Throws an exception if the order with the given id couldn't be found.
1067
 
1068
    Parameters:
3226 chandransh 1069
     - order_id
1070
     - refunded_by
1071
     - reason
2628 chandransh 1072
    """
3414 chandransh 1073
    logging.info("Refunding order id: " + str(order_id))
2628 chandransh 1074
    order = get_order(order_id)
3186 chandransh 1075
 
1076
 
3366 chandransh 1077
 
3226 chandransh 1078
    if order.cod:
3414 chandransh 1079
        logging.info("Refunding COD order with status " + str(order.status))
3366 chandransh 1080
        status_transition = {OrderStatus.SALES_RET_RECEIVED : OrderStatus.SALES_RET_REFUNDED,
1081
                     OrderStatus.DOA_CERT_INVALID : OrderStatus.DOA_INVALID_REFUNDED,
1082
                     OrderStatus.DOA_CERT_VALID : OrderStatus.DOA_VALID_REFUNDED,
1083
                     OrderStatus.INIT : OrderStatus.CANCELED,
1084
                     OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.CANCELED,
1085
                     OrderStatus.INVENTORY_LOW : OrderStatus.CANCELED,
4306 rajveer 1086
                     OrderStatus.LOW_INV_PO_RAISED : OrderStatus.CANCELED,
1087
                     OrderStatus.LOW_INV_REVERSAL_IN_PROCESS : OrderStatus.CANCELED,
1088
                     OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT : OrderStatus.CANCELED,
3366 chandransh 1089
                     OrderStatus.ACCEPTED : OrderStatus.CANCELED,
4247 rajveer 1090
                     OrderStatus.BILLED : OrderStatus.CANCELED,
1091
                     OrderStatus.CANCEL_REQUEST_CONFIRMED : OrderStatus.CANCELLED_ON_CUSTOMER_REQUEST
3366 chandransh 1092
                     }
1093
        if order.status not in status_transition.keys():
1094
            raise TransactionServiceException(114, "This order can't be refunded")
1095
 
3413 chandransh 1096
        if order.status < OrderStatus.BILLED:
3226 chandransh 1097
            __update_inventory_reservation(order)
1098
            order.statusDescription = "Order Cancelled"
3415 chandransh 1099
        elif order.status == OrderStatus.BILLED:
3413 chandransh 1100
            __create_return_order(order)
1101
            order.statusDescription = "Order Cancelled"
4078 chandransh 1102
        elif order.status == OrderStatus.SALES_RET_RECEIVED:
3226 chandransh 1103
            __create_return_order(order)
4078 chandransh 1104
            order.statusDescription = "Order Returned to origin"
1105
        elif order.status in [OrderStatus.DOA_CERT_INVALID, OrderStatus.DOA_CERT_VALID] :
1106
            __create_return_order(order)
3226 chandransh 1107
            __create_refund(order)
1108
            order.statusDescription = "Order Refunded"
4247 rajveer 1109
        elif order.status == OrderStatus.CANCEL_REQUEST_CONFIRMED:
1110
            if order.previousStatus  < OrderStatus.BILLED:
1111
                __update_inventory_reservation(order)
1112
                order.statusDescription = "Order Cancelled on customer request"
1113
            elif order.previousStatus == OrderStatus.BILLED:
1114
                __create_return_order(order)
1115
                order.statusDescription = "Order Cancelled on customer request"
3226 chandransh 1116
    else:
3366 chandransh 1117
        status_transition = {OrderStatus.SALES_RET_RECEIVED : OrderStatus.SALES_RET_REFUNDED,
1118
                     OrderStatus.DOA_CERT_INVALID : OrderStatus.DOA_INVALID_REFUNDED,
1119
                     OrderStatus.DOA_CERT_VALID : OrderStatus.DOA_VALID_REFUNDED,
1120
                     OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.REFUNDED,
3731 chandransh 1121
                     OrderStatus.INVENTORY_LOW : OrderStatus.REFUNDED,
4306 rajveer 1122
                     OrderStatus.LOW_INV_PO_RAISED : OrderStatus.REFUNDED,
1123
                     OrderStatus.LOW_INV_REVERSAL_IN_PROCESS : OrderStatus.REFUNDED,
1124
                     OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT : OrderStatus.REFUNDED,
3731 chandransh 1125
                     OrderStatus.ACCEPTED : OrderStatus.REFUNDED,
4247 rajveer 1126
                     OrderStatus.BILLED : OrderStatus.REFUNDED,
1127
                     OrderStatus.CANCEL_REQUEST_CONFIRMED : OrderStatus.CANCELLED_ON_CUSTOMER_REQUEST,
1128
                     OrderStatus.PAYMENT_FLAGGED : OrderStatus.PAYMENT_FLAGGED_DENIED
3366 chandransh 1129
                     }
1130
        if order.status not in status_transition.keys():
1131
            raise TransactionServiceException(114, "This order can't be refunded")
1132
 
3226 chandransh 1133
        if order.status in [OrderStatus.SALES_RET_RECEIVED, OrderStatus.DOA_CERT_INVALID, OrderStatus.DOA_CERT_VALID] :
1134
            __create_return_order(order)
1135
            __create_refund(order)
1136
            order.statusDescription = "Order Refunded"
4306 rajveer 1137
        elif order.status in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT]:
3191 chandransh 1138
            __create_cancellation(order)
3226 chandransh 1139
            __update_inventory_reservation(order)
1140
            order.statusDescription = "Order Refunded"
4247 rajveer 1141
        elif order.status == OrderStatus.CANCEL_REQUEST_CONFIRMED:
1142
            if order.previousStatus  < OrderStatus.BILLED:
1143
                __create_refund(order)
1144
                __update_inventory_reservation(order)
1145
                order.statusDescription = "Order Cancelled on customer request"
1146
            elif order.previousStatus == OrderStatus.BILLED:
1147
                __create_return_order(order)
1148
                __create_refund(order)
1149
                order.statusDescription = "Order Cancelled on customer request"
1150
        elif order.status == OrderStatus.PAYMENT_FLAGGED:
1151
            __update_inventory_reservation(order)
1152
            order.statusDescription = "Order Cancelled due to payment flagged"
3195 chandransh 1153
 
1154
    order.status = status_transition[order.status]
1155
    order.refund_timestamp = datetime.datetime.now()
3226 chandransh 1156
    order.refunded_by = refunded_by
1157
    order.refund_reason = reason
2628 chandransh 1158
 
1159
    session.commit()
1160
    return True
1161
 
3186 chandransh 1162
def __update_inventory_reservation(order):
3191 chandransh 1163
    '''
1164
    Reduce the reservation count for all line items of the given order.
1165
    '''
3186 chandransh 1166
    try:
1167
        catalog_client = CatalogClient().get_client()
1168
        for lineitem in order.lineitems:
1169
            catalog_client.reduceReservationCount(lineitem.item_id, order.warehouse_id, lineitem.quantity);
1170
    except:
1171
        print "Unable to reduce reservation count"
1172
 
4220 rajveer 1173
def __clone_order(order, should_copy_billing_info, is_cod):
2628 chandransh 1174
    current_time = datetime.datetime.now()
1175
    new_order = Order()
1176
    new_order.customer_id = order.customer_id
1177
    new_order.customer_name = order.customer_name
1178
    new_order.customer_city = order.customer_city
1179
    new_order.customer_state = order.customer_state
1180
    new_order.customer_mobilenumber = order.customer_mobilenumber
1181
    new_order.customer_pincode = order.customer_pincode
1182
    new_order.customer_address1 = order.customer_address1
1183
    new_order.customer_address2 = order.customer_address2
1184
    new_order.customer_email = order.customer_email
1185
    new_order.total_amount = order.total_amount
1186
    new_order.total_weight = order.total_weight
1187
    new_order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
1188
    new_order.statusDescription = 'Submitted for Processing'
1189
    new_order.created_timestamp = current_time
1190
 
1191
    new_order.transaction = order.transaction
4220 rajveer 1192
    new_order.cod = is_cod
2628 chandransh 1193
 
1194
    for line_item in order.lineitems:
1195
        litem = LineItem()
1196
        litem.item_id = line_item.item_id
1197
        litem.productGroup = line_item.productGroup
1198
        litem.brand = line_item.brand
1199
        if line_item.model_number:
1200
            litem.model_number = line_item.model_number
1201
        if line_item.model_name:    
1202
            litem.model_name = line_item.model_name
1203
        if line_item.color:
1204
            litem.color = line_item.color
1205
        if line_item.extra_info:
1206
            litem.extra_info = line_item.extra_info
1207
        litem.quantity = line_item.quantity
1208
        litem.unit_price = line_item.unit_price
1209
        litem.unit_weight = line_item.unit_weight
1210
        litem.total_price = line_item.total_price
1211
        litem.total_weight = line_item.total_weight
1212
        litem.transfer_price = line_item.transfer_price
1213
        litem.order = new_order
1214
 
1215
    logistics_client = LogisticsClient().get_client()
1216
    item_id = new_order.lineitems[0].item_id
3239 chandransh 1217
    logistics_info = logistics_client.getLogisticsInfo(new_order.customer_pincode, item_id, DeliveryType.PREPAID) #TODO: We should be able to pass another flag to suggest ignoring the inventory situation.
2628 chandransh 1218
    logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
1219
 
1220
    new_order.warehouse_id = logistics_info.warehouseId
1221
    new_order.logistics_provider_id = logistics_info.providerId
1222
    new_order.airwaybill_no = logistics_info.airway_billno
1223
    new_order.tracking_id = new_order.airwaybill_no
1224
    new_order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
3986 chandransh 1225
    new_order.promised_delivery_time = new_order.expected_delivery_time
4299 rajveer 1226
 
2628 chandransh 1227
    if should_copy_billing_info:
1228
        new_order.invoice_number = order.invoice_number
1229
        new_order.billed_by = order.billed_by
1230
        new_order.warehouse_id = order.warehouse_id
1231
        new_order.accepted_timestamp = current_time
1232
        new_order.billing_timestamp = current_time    
1233
        new_order.status = OrderStatus.BILLED
1234
        new_order.statusDescription = 'Order Billed'
1235
    else:       
3133 rajveer 1236
        catalog_client = CatalogClient().get_client()
2628 chandransh 1237
        catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, new_order.lineitems[0].quantity)
1238
 
1239
    return new_order
1240
 
1241
def __create_return_order(order):
2687 chandransh 1242
    ret_order = ReturnOrder(order)
2628 chandransh 1243
    return ret_order
1244
 
1245
def __create_refund(order):
4078 chandransh 1246
    payment_client = PaymentClient().get_client()
1247
    payment_client.createRefund(order.id, order.transaction.id, order.total_amount)
2747 chandransh 1248
    return
2628 chandransh 1249
 
3035 chandransh 1250
def __create_cancellation(order):
1251
    return
1252
 
2536 chandransh 1253
def __get_order_address(order):
1254
    address = order.customer_name + "\n" + order.customer_address1 + "\n"
1255
    if order.customer_address2:
1256
        address = address + order.customer_address2 + "\n"
1257
    address = address + order.customer_city + "\n"
1258
    address = address + order.customer_state + "\n"
1259
    address = address + "PIN " + order.customer_pincode + "\n"
1260
    address = address + "Phone: " + order.customer_mobilenumber
1261
    return address
1262
 
1263
def __generate_return_advice(order, warehouse, provider, filename):    
1264
    pdf = Canvas(filename)
1265
    pdf.setFont('Times-Bold',16)
1266
    address_text = pdf.beginText(inch, PAGE_HEIGHT - inch)
1267
    address_text.textLine("To")
1268
    address_text.textLine("Parmod Kumar")
1269
    for line in warehouse.location.split("\n"):
1270
        address_text.textLine(line)
1271
    address_text.textLine("PIN " + warehouse.pincode)
1272
    address_text.textLine("")
1273
    address_text.textLine("Phone: 9971573026")
1274
    pdf.drawText(address_text)
1275
 
1276
    pdf.setFont('Times-Roman',12)
1277
    order_text = pdf.beginText(inch, PAGE_HEIGHT - 4 * inch)
3051 chandransh 1278
    order_text.textLine("Pickup CODE: " + provider.details[DeliveryType.PREPAID].accountNo)
2536 chandransh 1279
    lineitem = order.lineitems[0] 
4234 rajveer 1280
    order_text.textLine("Content : " + lineitem.brand + " " + str(lineitem.model_number) + " " + str(lineitem.color))
2536 chandransh 1281
    order_text.textLine("Declared Value: Rs. " + str(order.total_amount))
1282
    order_text.textLine("Order ID : " + str(order.id))
1283
    pdf.drawText(order_text)
1284
    pdf.showPage()
1285
    pdf.save()
1286
    return filename
1287
 
2697 chandransh 1288
def get_return_orders(warehouse_id, from_date, to_date):
1289
    query = ReturnOrder.query.filter_by(processedStatus = False)
1290
    if warehouse_id != -1:
1291
        query = query.filter_by(warehouseId = warehouse_id)
1292
    if from_date:
4118 chandransh 1293
        query = query.filter(ReturnOrder.createdAt >from_date)
2697 chandransh 1294
    if to_date:
4118 chandransh 1295
        query = query.filter(ReturnOrder.createdAt < to_date)
2697 chandransh 1296
    return [return_order.to_thrift_object() for return_order in query.all()]
1297
 
2700 chandransh 1298
def get_return_order(id):
1299
    ret_order = ReturnOrder.get_by(orderId = id)
1300
    if ret_order is None:
1301
        raise TransactionServiceException(116, "No return order found for the given order id")
1302
    return ret_order.to_thrift_object()
1303
 
2697 chandransh 1304
def process_return(ret_order_id):
1305
    ret_order = ReturnOrder.get_by(orderId = ret_order_id)
1306
    if ret_order is None:
1307
        raise TransactionServiceException(116, "No return order found for the given order id")
1308
    ret_order.processedStatus = True
2701 chandransh 1309
    ret_order.processedAt = datetime.datetime.now()
2697 chandransh 1310
    session.commit()
1311
 
2819 chandransh 1312
def create_purchase_order(warehouseId):
1313
    if not warehouseId:
1314
        raise TransactionServiceException(101, "bad warehouse id")
1315
 
3451 chandransh 1316
    query = Order.query.with_lockmode("update").filter_by(warehouse_id=warehouseId)
4380 mandeep.dh 1317
    query = query.filter(or_(Order.status == OrderStatus.SUBMITTED_FOR_PROCESSING, Order.status == OrderStatus.INVENTORY_LOW))
2819 chandransh 1318
    query = query.filter_by(purchase_order_id=None)
1319
    query = query.order_by(Order.created_timestamp)
1320
    pending_orders = query.all()
1321
 
1322
    if not pending_orders:
1323
        return -1
1324
 
1325
    t_purchase_order = PurchaseOrder()
4365 mandeep.dh 1326
    t_purchase_order.supplierId = 3 # Putting supplier id as 3 for sarvottam for time being
2819 chandransh 1327
    t_purchase_order.warehouseId = warehouseId
1328
    t_purchase_order.lineitems = []
1329
 
1330
    lineitems = {}
1331
 
4293 rajveer 1332
    catalog_client = CatalogClient().get_client()
4292 rajveer 1333
 
2819 chandransh 1334
    for order in pending_orders:
1335
        for lineitem in order.lineitems:
1336
            item_id = lineitem.item_id
1337
            if lineitems.has_key(item_id):
1338
                lineitems[item_id].quantity = lineitems[item_id].quantity + lineitem.quantity
1339
            else:
1340
                t_po_lineitem = POLineItem()
1341
                t_po_lineitem.productGroup = lineitem.productGroup
1342
                t_po_lineitem.brand = lineitem.brand
1343
                t_po_lineitem.modelNumber = lineitem.model_number
1344
                t_po_lineitem.modelName = lineitem.model_name
1345
                t_po_lineitem.color = lineitem.color
1346
                t_po_lineitem.itemId = lineitem.item_id
1347
                t_po_lineitem.quantity = lineitem.quantity
1348
                lineitems[item_id] = t_po_lineitem
1349
                t_purchase_order.lineitems.append(t_po_lineitem)
4292 rajveer 1350
                #FIXME vendor is hardcoded for servottam
1351
                item_pricing = catalog_client.getItemPricing(item_id, 3)
1352
                t_po_lineitem.unitPrice = item_pricing.transferPrice
1353
 
1354
 
2819 chandransh 1355
    warehouse_client = WarehouseClient().get_client()
1356
    po_id = warehouse_client.createPurchaseOrder(t_purchase_order)
1357
 
1358
    for order in pending_orders:
1359
        order.purchase_order_id = po_id
1360
    session.commit()
1361
    return po_id
1362
 
3451 chandransh 1363
def update_weight(order_id, weight):
3469 chandransh 1364
    '''
1365
    Update the weight of order
1366
    '''
3451 chandransh 1367
    order = get_order(order_id)
1368
    order.total_weight = weight
3469 chandransh 1369
    lineitem = order.lineitems[0]
1370
    lineitem.total_weight = weight
1371
    lineitem.unit_weight = weight
3451 chandransh 1372
 
3469 chandransh 1373
    session.commit()
1374
    return order
1375
 
1376
def change_product(order_id, item_id):
1377
    '''
1378
    Ship a product of a different color to the customer.
1379
    '''
1380
    order = get_order(order_id)
4306 rajveer 1381
    if order.status not in [OrderStatus.INIT, OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT]:
3469 chandransh 1382
        raise TransactionServiceException(120, "This order has already been processed. Please seek help from engineering.")
1383
 
1384
    lineitem = order.lineitems[0]
3451 chandransh 1385
 
3469 chandransh 1386
    catalog_client = CatalogClient().get_client()
1387
 
1388
    old_item = catalog_client.getItem(lineitem.item_id)
1389
    item = catalog_client.getItem(item_id)
1390
    if old_item.catalogItemId != item.catalogItemId:
1391
        raise TransactionServiceException(121, "You can't ship a different item. You can only ship the same item in a different color.")
1392
 
1393
    catalog_client.reserveItemInWarehouse(item_id, order.warehouse_id, lineitem.quantity)
1394
    catalog_client.reduceReservationCount(lineitem.item_id, order.warehouse_id, lineitem.quantity)
1395
    #TODO: Check that this new item has the same price
1396
 
1397
    lineitem.item_id = item_id
1398
    lineitem.color = item.color
1399
 
3451 chandransh 1400
    session.commit()
3469 chandransh 1401
 
1402
    order = get_order(order_id)
3451 chandransh 1403
    return order
3469 chandransh 1404
 
1405
def change_warehouse(order_id, warehouse_id):
1406
    '''
1407
    Update the warehouse which will be used to fulfil this order.
1408
    '''
1409
    order = get_order(order_id)
4306 rajveer 1410
    if order.status not in [OrderStatus.INIT, OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS, OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT]:
3469 chandransh 1411
        raise TransactionServiceException(117, "This order has already been processed. Please seek help from engineering.")
1412
 
1413
    if warehouse_id == order.warehouse_id:
1414
        raise TransactionServiceException(118, "You have selected the current warehouse again. Nothing to do")
1415
 
1416
    lineitem = order.lineitems[0]
1417
    catalog_client = CatalogClient().get_client()
1418
    try:
1419
        warehouse = catalog_client.getWarehouse(warehouse_id)
1420
    except:
1421
        raise TransactionServiceException(119, "No warehouse with id" + str(warehouse_id))
1422
 
1423
    catalog_client.reserveItemInWarehouse(lineitem.item_id, warehouse_id, lineitem.quantity)
1424
    catalog_client.reduceReservationCount(lineitem.item_id, order.warehouse_id, lineitem.quantity)
3451 chandransh 1425
 
3469 chandransh 1426
    order.warehouse_id = warehouse_id
1427
    session.commit()
1428
    return order
3553 chandransh 1429
 
3986 chandransh 1430
def add_delay_reason(order_id, reason, further_delay=0):
3553 chandransh 1431
    '''
3994 chandransh 1432
    Adds a delay reason to the order and updates the expected delivery time.
3553 chandransh 1433
    '''
1434
    order = get_order(order_id)
1435
    try:
3581 chandransh 1436
        order.delay_reason = DelayReason._VALUES_TO_NAMES[reason]
4009 chandransh 1437
        actual_delivery_delay = adjust_delivery_time(order.expected_delivery_time, further_delay)
1438
        order.expected_delivery_time = order.expected_delivery_time + datetime.timedelta(days=actual_delivery_delay)
4102 chandransh 1439
 
1440
        if order.status < OrderStatus.SHIPPED_FROM_WH:
1441
            actual_shipping_delay = adjust_delivery_time(order.expected_shipping_time, further_delay)
1442
            order.expected_shipping_time = order.expected_shipping_time + datetime.timedelta(days=actual_shipping_delay)
1443
 
3553 chandransh 1444
        session.commit()
4008 mandeep.dh 1445
 
1446
        try:
1447
            transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
1448
            transaction_requiring_extra_processing.category = 'DELAYED_DELIVERY'
1449
            transaction_requiring_extra_processing.transaction_id = order.transaction_id
1450
            session.commit()
1451
        except Exception as e:
4065 mandeep.dh 1452
            print 'Could not persist transaction id: ' + str(order.transaction_id) + ' for further processing due to: ' + str(e)
4008 mandeep.dh 1453
 
3553 chandransh 1454
        return True
1455
    except:
3994 chandransh 1456
        print sys.exc_info()[0]
3553 chandransh 1457
        return False
3956 chandransh 1458
 
1459
def reconcile_cod_collection(collected_amount_map, xferBy, xferTxnId, xferDate):
1460
    unprocessed_awbs = {}
1461
    for awb, amount in collected_amount_map.iteritems():
1462
        try:
1463
            order = Order.query.filter_by(airwaybill_no=awb).one()
1464
        except NoResultFound:
1465
            unprocessed_awbs[awb] = "No order was found for the given AWB: " + awb
1466
            continue
1467
        except MultipleResultsFound:
1468
            unprocessed_awbs[awb] = "Multiple orders were found for the given AWB:" + awb
1469
            continue
1470
 
1471
        if order.cod_reconciliation_timestamp:
1472
            #This order has been processed already! This may be a re-run. Let's allow other orders to be processed.
1473
            continue
1474
 
1475
        if order.status < OrderStatus.DELIVERY_SUCCESS:
1476
            #Order has not been delivered yet. No way we can receive the payment.
1477
            unprocessed_awbs[awb] = "Payment has been received for order: " + str(order.id) + " before delivery reconciliation"
1478
            continue
1479
 
4252 rajveer 1480
        ##As per ticket #743 if amount difference less than 0.5 we should reconcile
1481
        if abs(amount - order.total_amount) > 0.5:
3956 chandransh 1482
            #Received amount is less than or more than the order amount. Mustn't let the user proceed.
1483
            unprocessed_awbs[awb] = "Payment of Rs. " + str(amount) + " has been received against the total value of Rs. " + str(order.total_amount) + " for order: " + str(order.id)
1484
            continue
1485
 
1486
        try:
1487
            payment_client = PaymentClient().get_client()
1488
            payment_client.partiallyCapturePayment(order.transaction.id, amount, xferBy, xferTxnId, xferDate)
1489
        except Exception:
1490
            unprocessed_awbs[awb] = "We were unable to partially capture payment for order id " + str(order.id) + ", AWB: " + awb
1491
            continue
1492
 
1493
        #Payment has been recorded now. We can update the order peacefully.
1494
        order.cod_reconciliation_timestamp = datetime.datetime.now()
1495
        session.commit()
3469 chandransh 1496
 
3956 chandransh 1497
    return unprocessed_awbs
4008 mandeep.dh 1498
 
1499
def get_transactions_requiring_extra_processing(category):
4065 mandeep.dh 1500
    """
1501
    Returns the list of transaction ids that require some extra processing and
1502
    which belong to a particular category. This is currently used by CRM
1503
    application. If no such transaction ids are present, it returns an empty list.
1504
    """
1505
    query = TransactionRequiringExtraProcessing.query
4008 mandeep.dh 1506
 
4065 mandeep.dh 1507
    if category is not None:
1508
        query = query.filter_by(category = ExtraTransactionProcessingType._VALUES_TO_NAMES.get(category))
4008 mandeep.dh 1509
 
4065 mandeep.dh 1510
    return [a.transaction_id for a in query.all()]
4008 mandeep.dh 1511
 
1512
def mark_transaction_as_processed(transactionId, category):
4065 mandeep.dh 1513
    """
1514
    Marks a particular transaction as processed for a particular cateogory.
1515
    It essentially deletes the transaction id record for a particular
1516
    processing type category (if present) from DB.
1517
    This is currently used by CRM application.
1518
    """
4008 mandeep.dh 1519
    TransactionRequiringExtraProcessing.query.filter_by(transaction_id = transactionId).filter_by(category = ExtraTransactionProcessingType._VALUES_TO_NAMES.get(category)).delete()
1520
    session.commit()
1521
 
4018 chandransh 1522
def get_item_wise_risky_orders_count():
1523
    """
1524
    Returns a map containing the number of risky orders keyed by item id. A risky order
1525
    is defined as one whose shipping date is about to expire.
1526
    """
1527
    item_wise_risky_orders_count = {}
4019 chandransh 1528
    shipping_cutoff_time = (datetime.datetime.now() + datetime.timedelta(days = 1)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
4018 chandransh 1529
    orders = Order.query.filter(Order.status.in_(tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.ACCEPTED]))).filter(Order.expected_shipping_time <= shipping_cutoff_time).all()
1530
    for order in orders:
1531
        for lineitem in order.lineitems:
1532
            quantity = lineitem.quantity
1533
            if item_wise_risky_orders_count.has_key(lineitem.item_id):
1534
                quantity = quantity + item_wise_risky_orders_count[lineitem.item_id]
1535
 
1536
            item_wise_risky_orders_count[lineitem.item_id] = quantity
1537
    return item_wise_risky_orders_count
1538
 
4324 mandeep.dh 1539
def update_shipment_address(order_id, address_id):
1540
    """
1541
    Updates shipment address of an order. Delivery and shipping date estimates
1542
    etc. are also updated here.
1543
 
1544
    Throws TransactionServiceException in case address change is not
1545
    possible due to certain reasons such as new pincode in address is
1546
    not serviceable etc.
1547
 
1548
    Parameters:
1549
     - orderId
1550
     - addressId
1551
    """
1552
    user_client = UserClient().get_client()
1553
    address     = user_client.getAddressById(address_id)
1554
    order       = get_order(order_id)
1555
 
1556
    logistics_client = LogisticsClient().get_client()
1557
    logistics_info = logistics_client.getLogisticsEstimation(order.lineitems[0].item_id, address.pin)
1558
 
1559
    # Check for new estimate. Raise exception if new address is not serviceable
1560
    if logistics_info.deliveryTime == -1:
1561
        raise TransactionServiceException(1, 'Location not serviceable')
1562
 
1563
    # COD services are not available everywhere
1564
    if order.cod:
1565
        if not logistics_client.isCodAllowed(address.pin):
1566
            raise TransactionServiceException(2, 'COD service not available')
1567
 
1568
    current_time = datetime.datetime.now()
1569
    logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
1570
    logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
1571
 
1572
    order.customer_name         = address.name
1573
    order.customer_pincode      = address.pin
1574
    order.customer_address1     = address.line1
1575
    order.customer_address2     = address.line2
1576
    order.customer_city         = address.city
1577
    order.customer_state        = address.state
1578
    order.customer_mobilenumber = address.phone
1579
 
1580
    if order.cod:
1581
        order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0)
1582
    else:
1583
        order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
1584
 
1585
    order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
1586
    order.promised_shipping_time = order.expected_shipping_time
1587
    order.promised_delivery_time = order.expected_delivery_time 
1588
 
1589
    session.commit()
1590
 
4247 rajveer 1591
def mark_order_cancellation_request_received(orderId):
1592
    """
1593
    Mark order as cancellation request received. If customer sends request of cancellation of
1594
    a particular order, this method will be called. It will just change status of the order
1595
    depending on its current status. It also records the previous status, so that we can move
1596
    back to that status if cancellation request is denied.
1597
 
1598
    Parameters:
1599
     - orderId
1600
    """
1601
    status_transition = {OrderStatus.INIT : OrderStatus.CANCEL_REQUEST_RECEIVED,
1602
             OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.CANCEL_REQUEST_RECEIVED,
1603
             OrderStatus.INVENTORY_LOW : OrderStatus.CANCEL_REQUEST_RECEIVED,
4306 rajveer 1604
             OrderStatus.LOW_INV_PO_RAISED : OrderStatus.CANCEL_REQUEST_RECEIVED,
1605
             OrderStatus.LOW_INV_REVERSAL_IN_PROCESS : OrderStatus.CANCEL_REQUEST_RECEIVED,
1606
             OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT : OrderStatus.CANCEL_REQUEST_RECEIVED,
4247 rajveer 1607
             OrderStatus.ACCEPTED : OrderStatus.CANCEL_REQUEST_RECEIVED,
1608
             OrderStatus.BILLED : OrderStatus.CANCEL_REQUEST_RECEIVED,
1609
             OrderStatus.PAYMENT_FLAGGED : OrderStatus.CANCEL_REQUEST_RECEIVED
1610
             }
1611
    if order.status not in status_transition.keys():
1612
        raise TransactionServiceException(114, "This order can't be considered for cancellation")
1613
 
1614
    order = get_order(orderId)
1615
    order.previousStatus = order.status
1616
    order.status = OrderStatus.CANCEL_REQUEST_RECEIVED
1617
    order.statusDescription = "Cancellation request received from user"
1618
    session.commit()
1619
 
4258 rajveer 1620
def mark_transaction_as_payment_flag_removed(transactionId):
4247 rajveer 1621
    """
4258 rajveer 1622
    If we and/or payment gateway has decided to accept the payment, this method needs to be called.
1623
    Changed transaction and all orders status to payment accepted.
4247 rajveer 1624
 
1625
    Parameters:
4258 rajveer 1626
     - transactionId
4247 rajveer 1627
    """
4258 rajveer 1628
    transaction = get_transaction(transactionId)
1629
    transaction.status = TransactionStatus.AUTHORIZED
1630
    for order in transaction.orders:
1631
        order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
1632
        order.statusDescription = "Submitted to warehouse"
4247 rajveer 1633
    session.commit()
1634
 
4259 anupam.sin 1635
def refund_transaction(transactionId, refundedBy, reason):
1636
    """
1637
    This method is called when a flagged payment is deemed unserviceable and the corresponding orders
1638
    need to be cancelled
1639
 
1640
    Parameters:
1641
     - transactionId
1642
    """
1643
    transaction = get_transaction(transactionId)
1644
    transaction.status = TransactionStatus.FAILED
1645
    for order in transaction.orders:
1646
        refund_order(order.id, refundedBy, reason)
1647
    session.commit()
1648
 
4247 rajveer 1649
def mark_order_cancellation_request_denied(orderId):
1650
    """
4283 anupam.sin 1651
    If we decide to not to cancel order, we will move the order to previous status.
4247 rajveer 1652
 
1653
    Parameters:
1654
     - orderId
1655
    """
1656
    order = get_order(orderId)
1657
    order.status = order.previousStatus
1658
    order.previousStatus = None
1659
    order.statusDescription = "Cancellation request denied"
1660
    session.commit()
1661
 
1662
def mark_order_cancellation_request_confirmed(orderId):
1663
    """
1664
    If we decide to to cancel order, CRM will call this method to move the status of order to
1665
    cancellation request confirmed. After this OM will be able to cancel the order.
1666
 
1667
    Parameters:
1668
     - orderId
1669
    """
1670
    order = get_order(orderId)
1671
    order.status = OrderStatus.CANCEL_REQUEST_CONFIRMED
1672
    order.statusDescription = "Cancellation request confirmed"
1673
    session.commit()
1674
 
4285 rajveer 1675
def accept_orders_for_item_id(itemId, inventory):
1676
    """
1677
    Marks the orders as ACCEPTED for the given itemId and inventory. It also updates the accepted timestamp. If the
1678
    given order is not a COD order, it also captures the payment if the same has not been captured.
1679
 
1680
    Parameters:
1681
     - itemId
1682
     - inventory
1683
    """
4351 rajveer 1684
    orders = Order.query.filter(Order.status.in_(tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW, OrderStatus.LOW_INV_PO_RAISED, OrderStatus.LOW_INV_REVERSAL_IN_PROCESS]))).order_by(Order.expected_shipping_time).all()
4285 rajveer 1685
    for order in orders:
4301 rajveer 1686
        if inventory > 0:
1687
            lineitem = order.lineitems[0]
1688
            if itemId == lineitem.item_id:
1689
                try:
1690
                    accept_order(order.id)
1691
                    inventory = inventory - lineitem.quantity
1692
                except:
1693
                    logging.info("Unable to accept the order")
1694
        else:
1695
            order_outofstock(order.id)
4285 rajveer 1696
    return
1697
 
4369 rajveer 1698
def mark_orders_as_po_raised(vendorId, itemId, quantity, estimate, isReminder):
1699
    if isReminder:
1700
        statuses = tuple([OrderStatus.LOW_INV_PO_RAISED_TIMEOUT, OrderStatus.LOW_INV_REVERSAL_TIMEOUT])
1701
    else:
1702
        statuses = tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW])
1703
 
1704
    orders = Order.query.filter(Order.status.in_(statuses)).order_by(Order.expected_shipping_time).all()
4303 rajveer 1705
    for order in orders:
1706
        lineitem = order.lineitems[0]
1707
        if quantity >= lineitem.quantity and itemId == lineitem.item_id:
1708
            if estimate > 0:
1709
                add_delay_reason(order.id, DelayReason.OTHERS, estimate)
1710
            order.status = OrderStatus.LOW_INV_PO_RAISED
4362 rajveer 1711
            order.statusDescription = "PO Raised"
4303 rajveer 1712
            quantity = quantity - lineitem.quantity
1713
            store_inventory_action(order, itemId, HotspotAction.PO_RAISED, estimate, lineitem.quantity)
1714
            session.commit()
1715
 
4369 rajveer 1716
def mark_orders_as_reversal_initiated(vendorId, itemId, quantity, estimate, isReminder):
1717
    if isReminder:
1718
        statuses = tuple([OrderStatus.LOW_INV_PO_RAISED_TIMEOUT, OrderStatus.LOW_INV_REVERSAL_TIMEOUT])
1719
    else:
1720
        statuses = tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW])
1721
    orders = Order.query.filter(Order.status.in_(statuses)).order_by(Order.expected_shipping_time).all()
4303 rajveer 1722
    for order in orders:
1723
        lineitem = order.lineitems[0]
1724
        if quantity >= lineitem.quantity and itemId == lineitem.item_id:
1725
            if estimate > 0:
1726
                add_delay_reason(order.id, DelayReason.OTHERS, estimate)
1727
            order.status = OrderStatus.LOW_INV_REVERSAL_IN_PROCESS
4362 rajveer 1728
            order.statusDescription = "Reversal Initiated"
4303 rajveer 1729
            quantity = quantity - lineitem.quantity
1730
            store_inventory_action(order, itemId, HotspotAction.REVERSAL_INITIATED, estimate, lineitem.quantity)
1731
            session.commit()
1732
 
4369 rajveer 1733
def mark_orders_as_not_available(vendorId, itemId, quantity, estimate, isReminder):
1734
    if isReminder:
1735
        statuses = tuple([OrderStatus.LOW_INV_PO_RAISED_TIMEOUT, OrderStatus.LOW_INV_REVERSAL_TIMEOUT])
1736
    else:
1737
        statuses = tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW])
1738
 
1739
    orders = Order.query.filter(Order.status.in_(statuses)).order_by(Order.expected_shipping_time).all()
4303 rajveer 1740
    for order in orders:
1741
        lineitem = order.lineitems[0]
1742
        if quantity >= lineitem.quantity and itemId == lineitem.item_id:
1743
            order.status = OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT
4362 rajveer 1744
            order.statusDescription = "Not Available"
4303 rajveer 1745
            quantity = quantity - lineitem.quantity
1746
            store_inventory_action(order, itemId, HotspotAction.NOT_AVAILABLE, estimate, lineitem.quantity)
1747
            session.commit()
1748
 
1749
def store_inventory_action(order, itemId, hotspotAction, estimate, quantity):
4369 rajveer 1750
    orderInv = OrderInventory.get_by(order_id=order.id)
1751
    if not orderInv:
1752
        orderInv = OrderInventory()
1753
        orderInv.order = order
4303 rajveer 1754
    orderInv.hotspotAction = hotspotAction  
1755
    orderInv.estimate = estimate
1756
    orderInv.itemId = itemId
1757
    orderInv.timestamp = datetime.datetime.now()
1758
    orderInv.quantity = quantity
1759
 
4369 rajveer 1760
 
1761
def mark_orders_as_timeout(vendorId):
1762
    ret_timeouts = {}
1763
    orderInvs = OrderInventory.query.all()
1764
    for orderInv in orderInvs:
1765
        if orderInv.timestamp +  timedelta(days=orderInv.estimate) < datetime.datetime.now():
1766
            if not ret_timeouts.has_key(orderInv.itemId):
1767
                timeout = TimeoutSummary()
1768
                ret_timeouts[orderInv.itemId] = timeout
1769
            else:
1770
                timeout = ret_timeouts.get(orderInv.itemId)
1771
            if orderInv.hotspotAction == HotspotAction.PO_RAISED and orderInv.order.status == OrderStatus.LOW_INV_PO_RAISED:
1772
                if timeout.poRaised:
1773
                    timeout.poRaised += 1
1774
                else:
1775
                    timeout.poRaised = 1
1776
 
1777
                if timeout.poEstimate:
1778
                    timeout.poEstimate += orderInv.estimate
1779
                else:
1780
                    timeout.poEstimate = orderInv.estimate
1781
                orderInv.order.status = OrderStatus.LOW_INV_PO_RAISED_TIMEOUT
1782
                orderInv.order.statusDescription = "PO Raised Timeout"
1783
            elif orderInv.hotspotAction == HotspotAction.REVERSAL_INITIATED and orderInv.order.status == OrderStatus.LOW_INV_REVERSAL_IN_PROCESS:
1784
                if timeout.reversalInitiated:
1785
                    timeout.reversalInitiated += 1
1786
                else:
1787
                    timeout.reversalInitiated = 1
1788
 
1789
                if timeout.reversalEstimate:
1790
                    timeout.reversalEstimate += orderInv.estimate
1791
                else:
1792
                    timeout.reversalEstimate = orderInv.estimate
1793
                orderInv.order.status = OrderStatus.LOW_INV_REVERSAL_TIMEOUT
1794
                orderInv.order.statusDescription = "Reversal Initiated Timeout"
1795
            else:
1796
                orderInv.delete()
1797
    session.commit()
1798
    return ret_timeouts
1799
 
4386 anupam.sin 1800
def get_order_for_awb(awb):
1801
    order = Order.get_by(airwaybill_no=awb)
1802
    if not order:
1803
        raise TransactionServiceException(108, "no such order")
1804
 
1805
    return order
4369 rajveer 1806
 
4386 anupam.sin 1807
 
766 rajveer 1808
def close_session():
1809
    if session.is_active:
1810
        print "session is active. closing it."
2783 chandransh 1811
        session.close()
3376 rajveer 1812
 
1813
def is_alive():
1814
    try:
1815
        session.query(Transaction.id).limit(1).one()
1816
        return True
1817
    except:
4343 varun.gupt 1818
        return False