Subversion Repositories SmartDukaan

Rev

Rev 4009 | Rev 4019 | 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
 
7
from datetime import date
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.TransactionClient import TransactionClient
17
from shop2020.clients.UserClient import UserClient
18
from shop2020.clients.WarehouseClient import WarehouseClient
19
from shop2020.model.v1 import order
20
from shop2020.model.v1.order.impl.DataService import Transaction, LineItem, \
4008 mandeep.dh 21
    Alerts, Order, BatchNoGenerator, InvoiceIDGenerator, \
22
    TransactionRequiringExtraProcessing
2687 chandransh 23
from shop2020.model.v1.order.impl.model.ReturnOrder import ReturnOrder
3499 mandeep.dh 24
from shop2020.thriftpy.logistics.ttypes import DeliveryType
25
from shop2020.thriftpy.model.v1.catalog.ttypes import BillingType
26
from shop2020.thriftpy.model.v1.order.ttypes import Transaction as T_Transaction, \
4008 mandeep.dh 27
    TransactionServiceException, TransactionStatus, OrderStatus, DelayReason, \
28
    ExtraTransactionProcessingType
3499 mandeep.dh 29
from shop2020.thriftpy.warehouse.ttypes import PurchaseOrder, \
30
    LineItem as POLineItem
31
from shop2020.utils.EmailAttachmentSender import mail, get_attachment_part
855 rajveer 32
from shop2020.utils.Utils import to_py_date, to_java_date
4008 mandeep.dh 33
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
3499 mandeep.dh 34
from sqlalchemy.sql import func
35
from sqlalchemy.sql.expression import and_, or_, desc, not_, distinct
36
from string import Template
37
from textwrap import dedent
38
import datetime
39
import logging
40
import os
41
import sys
42
import time
43
logging.basicConfig(level=logging.DEBUG)
104 ashish 44
 
3499 mandeep.dh 45
 
46
 
47
 
48
 
49
 
2819 chandransh 50
PAGE_HEIGHT=defaultPageSize[1]
51
PAGE_WIDTH=defaultPageSize[0]
52
 
3499 mandeep.dh 53
ORDER_STATUS_TO_USER_TRUST_LEVEL_DELTA_DICT = {
54
        OrderStatus.SALES_RET_RESHIPPED     :  3,
55
        OrderStatus.SALES_RETURN_IN_TRANSIT : -5,
56
        OrderStatus.DELIVERY_SUCCESS        :  1,
3578 mandeep.dh 57
        OrderStatus.DOA_CERT_INVALID        : -5
3499 mandeep.dh 58
}
59
 
104 ashish 60
def get_new_transaction():
61
    transaction = Transaction()
62
    transaction.createdOn = datetime.datetime.now()
738 chandransh 63
    transaction.status = TransactionStatus.INIT
104 ashish 64
    transaction.status_message = "New transaction"
483 rajveer 65
    return transaction
66
 
67
def create_order(t_order):
68
    order = Order()
69
    if t_order.warehouse_id:
70
        order.warehouse_id = t_order.warehouse_id
509 rajveer 71
    if t_order.logistics_provider_id:    
483 rajveer 72
        order.logistics_provider_id = t_order.logistics_provider_id
73
        order.airwaybill_no = t_order.airwaybill_no
74
        order.tracking_id = t_order.tracking_id
4009 chandransh 75
    if t_order.expected_shipping_time:
4018 chandransh 76
        order.expected_shipping_time = to_py_date(t_order.expected_shipping_time).replace(hour=15, minute=0, second=0)
4009 chandransh 77
    if t_order.expected_delivery_time:
483 rajveer 78
        order.expected_delivery_time = to_py_date(t_order.expected_delivery_time)
3986 chandransh 79
        order.promised_delivery_time = to_py_date(t_order.expected_delivery_time)
483 rajveer 80
    if t_order.customer_id:    
81
        order.customer_id = t_order.customer_id
82
        order.customer_name = t_order.customer_name
860 rajveer 83
        order.customer_city = t_order.customer_city
84
        order.customer_state = t_order.customer_state
483 rajveer 85
        order.customer_mobilenumber = t_order.customer_mobilenumber
86
        order.customer_pincode = t_order.customer_pincode
738 chandransh 87
        order.customer_address1 = t_order.customer_address1
88
        order.customer_address2 = t_order.customer_address2
483 rajveer 89
        order.customer_email = t_order.customer_email
90
    #new status and status description to be added    
716 rajveer 91
    order.status = t_order.status  
483 rajveer 92
    order.statusDescription = t_order.statusDescription
93
    order.total_amount = t_order.total_amount
94
    order.total_weight = t_order.total_weight
95
    if t_order.created_timestamp:
96
        order.created_timestamp = to_py_date(t_order.created_timestamp)
97
    else:
685 chandransh 98
        order.created_timestamp = datetime.datetime.now()
483 rajveer 99
    return order
100
 
101
def create_transaction(t_transaction):
102
    t_orders = t_transaction.orders
103
    if not t_orders:
104
        raise TransactionServiceException(101, "Orders missing from the transaction")
105
    transaction = get_new_transaction()
106
    transaction.customer_id = t_transaction.customer_id 
107
    transaction.shopping_cart_id = t_transaction.shoppingCartid
2219 varun.gupt 108
    transaction.coupon_code = t_transaction.coupon_code
2815 vikas 109
    transaction.session_source = t_transaction.sessionSource
110
    transaction.session_start_time = to_py_date(t_transaction.sessionStartTime)
3858 vikas 111
    transaction.first_source = t_transaction.firstSource
112
    transaction.first_source_start_time = to_py_date(t_transaction.firstSourceTime)
483 rajveer 113
 
114
    for t_order in t_orders:
115
        order = create_order(t_order)
116
        order.transaction = transaction 
117
        for line_item in t_order.lineitems:
118
            litem = LineItem()
716 rajveer 119
            litem.item_id = line_item.item_id
963 chandransh 120
            litem.productGroup = line_item.productGroup
483 rajveer 121
            litem.brand = line_item.brand
122
            if line_item.model_number:
123
                litem.model_number = line_item.model_number
124
            if line_item.model_name:    
125
                litem.model_name = line_item.model_name
908 chandransh 126
            if line_item.color:
127
                litem.color = line_item.color
483 rajveer 128
            if line_item.extra_info:
129
                litem.extra_info = line_item.extra_info
130
            litem.quantity = line_item.quantity
131
            litem.unit_price = line_item.unit_price
132
            litem.unit_weight = line_item.unit_weight
133
            litem.total_price = line_item.total_price
134
            litem.total_weight = line_item.total_weight
1262 varun.gupt 135
            litem.transfer_price = line_item.transfer_price
1976 varun.gupt 136
 
483 rajveer 137
            """
138
            if line_item.addedOn:
139
                litem.added_on = to_py_date(line_item.addedOn)
140
            else:
141
                litem.added_on = datetime.datetime.now()
142
            """
143
            litem.order = order
144
    session.commit()
145
    return transaction.id
146
 
147
def get_transaction(transaction_id):
148
    transaction = Transaction.get_by(id=transaction_id)
149
    if not transaction:
150
        raise TransactionServiceException(108, "no such transaction")
151
 
152
    return transaction
153
 
154
def get_transactions_for_customer(customer_id, from_date, to_date, status):
155
    if not customer_id:
156
        raise TransactionServiceException(101, "bad customer id")
157
    query = Transaction.query.filter(Transaction.customer_id == customer_id)
1493 vikas 158
    if status is not None:
483 rajveer 159
        query = query.filter(Transaction.status == status)
160
    if from_date:
161
        query = query.filter(Transaction.createdOn >from_date)
162
    if to_date:
163
        query = query.filter(Transaction.createdOn < to_date)
164
    return query.all()
165
 
166
def get_transactions_for_shopping_cart_id(shopping_cart_id):
167
    transactions = Transaction.query.filter_by(shopping_cart_id=shopping_cart_id).all()
168
    return transactions
169
 
170
def get_transaction_status(transaction_id):
171
    if not transaction_id:
172
        raise TransactionServiceException(101, "bad transaction id")
173
 
174
    transaction = get_transaction(transaction_id)
175
 
176
    return transaction.status
177
 
178
def change_transaction_status(transaction_id, new_status, description):
179
    transaction = get_transaction(transaction_id)
180
    transaction.status = new_status
181
    transaction.status_message = description
690 chandransh 182
    if new_status == TransactionStatus.FAILED:
183
        for order in transaction.orders:
184
            order.status = OrderStatus.PAYMENT_FAILED
2339 chandransh 185
            order.statusDescription = "Payment Failed"
3010 chandransh 186
    elif new_status == TransactionStatus.AUTHORIZED:
690 chandransh 187
        for order in transaction.orders:
188
            order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
1219 chandransh 189
            order.statusDescription = "Submitted to warehouse"
3092 chandransh 190
            order.cod = False
743 rajveer 191
            #After we got payment success, we will set logistics info also 
192
            logistics_client = LogisticsClient().get_client()
193
            #FIXME line item is only one now. If multiple will come, need to fix.
871 chandransh 194
            item_id = order.lineitems[0].item_id
3044 chandransh 195
            logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.PREPAID)
743 rajveer 196
 
1730 ankur.sing 197
            current_time = datetime.datetime.now()
198
            logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
4009 chandransh 199
            logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
1730 ankur.sing 200
 
743 rajveer 201
            order.warehouse_id = logistics_info.warehouseId
202
            order.logistics_provider_id = logistics_info.providerId
203
            order.airwaybill_no = logistics_info.airway_billno
204
            order.tracking_id = order.airwaybill_no
4018 chandransh 205
            order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=15, minute=0, second=0)
1730 ankur.sing 206
            order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
3986 chandransh 207
            order.promised_delivery_time = order.expected_delivery_time 
871 chandransh 208
 
3133 rajveer 209
            catalog_client = CatalogClient().get_client()
871 chandransh 210
            catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, order.lineitems[0].quantity)
1351 varun.gupt 211
 
1341 chandransh 212
            item_pricing = catalog_client.getItemPricing(item_id, order.warehouse_id)
213
            order.lineitems[0].transfer_price = item_pricing.transferPrice
3064 chandransh 214
    elif new_status == TransactionStatus.COD_IN_PROCESS:
215
        for order in transaction.orders:
216
            order.status = OrderStatus.INIT
217
            order.statusDescription = "Verification Pending"
218
            order.cod = True
219
            #After we got payment success, we will set logistics info also 
220
            logistics_client = LogisticsClient().get_client()
221
            #FIXME line item is only one now. If multiple will come, need to fix.
222
            item_id = order.lineitems[0].item_id
223
            logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.COD)
224
 
225
            current_time = datetime.datetime.now()
226
            logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
4009 chandransh 227
            logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
3064 chandransh 228
 
229
            order.warehouse_id = logistics_info.warehouseId
230
            order.logistics_provider_id = logistics_info.providerId
231
            order.airwaybill_no = logistics_info.airway_billno
232
            order.tracking_id = order.airwaybill_no
4018 chandransh 233
            order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=12, minute=0, second=0)
3064 chandransh 234
            order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
3986 chandransh 235
            order.promised_delivery_time = order.expected_delivery_time
3064 chandransh 236
 
3133 rajveer 237
            catalog_client = CatalogClient().get_client()
3064 chandransh 238
            catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, order.lineitems[0].quantity)
239
 
240
            item_pricing = catalog_client.getItemPricing(item_id, order.warehouse_id)
241
            order.lineitems[0].transfer_price = item_pricing.transferPrice        
3499 mandeep.dh 242
 
483 rajveer 243
    session.commit()
3499 mandeep.dh 244
 
3530 mandeep.dh 245
    try:
246
        if new_status == TransactionStatus.COD_IN_PROCESS:
4008 mandeep.dh 247
            transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
248
            transaction_requiring_extra_processing.category = 'COD_VERIFICATION'
249
            transaction_requiring_extra_processing.transaction_id = transaction_id
250
            session.commit()
3530 mandeep.dh 251
    except Exception as e:
4008 mandeep.dh 252
        print "Error inserting transaction Id: " + transaction_id + " due to "+ e
3499 mandeep.dh 253
 
483 rajveer 254
    return True
255
 
3994 chandransh 256
def __get_holidays(start_time=0L, to_time=-1L):
257
    '''
258
    Get the list of all public holidays since start time including the
259
    day of start time if it's a holiday
260
    '''
261
    midnightTime = start_time.replace(hour=0, minute=0, second=0, microsecond=0)
1730 ankur.sing 262
    fromDate = to_java_date(midnightTime)
263
    logistics_client = LogisticsClient().get_client()
3994 chandransh 264
    #TODO: This list should be cached after the first call.
265
    #Calling logistics server is also unnecessary.
266
    holidays = logistics_client.getHolidays(fromDate, to_time)
267
    holidays = [to_py_date(holiday).date() for holiday in holidays]
268
    return holidays
269
 
270
 
271
def adjust_delivery_time(start_time, delivery_days):
272
    '''
273
    Returns the actual no. of days which will pass while 'delivery_days'
274
    no. of business days will pass since 'order_time'. 
275
    '''
276
    start_date = start_time.date()
277
    end_date = start_date + datetime.timedelta(days = delivery_days)
278
    holidays = __get_holidays(start_time)
1730 ankur.sing 279
 
3994 chandransh 280
    while start_date <= end_date:
281
        start_date = start_date + datetime.timedelta(days = 1)
282
        if start_date.weekday() == 6 or start_date in holidays:
1730 ankur.sing 283
            delivery_days = delivery_days + 1
3994 chandransh 284
            end_date = end_date + datetime.timedelta(days = 1)
285
 
1730 ankur.sing 286
    return delivery_days
287
 
1398 varun.gupt 288
def enqueue_transaction_info_email(transaction_id):
1262 varun.gupt 289
    transaction = get_transaction(transaction_id)
290
 
291
    html_header = """
292
<html>
293
<body>
294
<div>
295
    <p>
296
        Hello,<br /><br />
1742 rajveer 297
        Thanks for placing order with us. Following are the details of your order:
1262 varun.gupt 298
    </p>
299
    <div>
300
        Order Date: $order_date
1569 varun.gupt 301
    </div>
1262 varun.gupt 302
    <div>
303
        <table>
1569 varun.gupt 304
        <tr><td colspan="5"><hr /></td></tr>
305
        <tr><td colspan="5" align="left"><b>Order Details</b></td></tr>
306
        <tr><td colspan="5"><hr /></td></tr>
1262 varun.gupt 307
        <tr>
2050 varun.gupt 308
            <th width="100">Order No.</th>
309
            <th>Product</th>
310
            <th width="100">Quantity</th>
311
            <th width="100">Unit Price</th>
312
            <th width="100">Amount</th>
1262 varun.gupt 313
        </tr>
314
    """
1398 varun.gupt 315
    user_email = ""
1262 varun.gupt 316
    total_amount = 0.0
317
    html_table = ""
1569 varun.gupt 318
    order_date = datetime.datetime.now()
1262 varun.gupt 319
 
320
    for order in transaction.orders:
321
        order_date = order.created_timestamp
1398 varun.gupt 322
        user_email = order.customer_email
1569 varun.gupt 323
        html_tr = "<tr><td align='center'>" + str(order.id) + "</td>"
1262 varun.gupt 324
 
325
        for lineitem in order.lineitems:
2050 varun.gupt 326
            lineitem_total_price = round(lineitem.total_price, 2)
327
 
1276 chandransh 328
            html_tr += "<td>" + str(lineitem) + "</td>"
2050 varun.gupt 329
            html_tr += "<td align='center'>" + ("%.0f" % lineitem.quantity) + "</td>"
330
            html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem.unit_price) + "</td>"
331
            html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem_total_price) + "</td></tr>"
332
            total_amount += lineitem_total_price
1262 varun.gupt 333
            html_table += html_tr
2050 varun.gupt 334
 
1262 varun.gupt 335
    html_footer = """
336
        <tr><td colspan=5>&nbsp;</td></tr>
1565 varun.gupt 337
        <tr><td colspan=5><hr /></td></tr>
1262 varun.gupt 338
        <tr>
339
            <td colspan=4>Total Amount</td>
2050 varun.gupt 340
            <td> Rs. $total_amount</td>
1262 varun.gupt 341
        </tr>
1569 varun.gupt 342
        <tr><td colspan=5><hr /></td></tr>
1262 varun.gupt 343
        </table>
1569 varun.gupt 344
    </div>
1262 varun.gupt 345
    <p>
346
        Best Wishes,<br />
347
        Saholic Team
348
    </p>
349
</div>
350
</body>
351
</html>
352
    """
1398 varun.gupt 353
 
1569 varun.gupt 354
    dt = datetime.datetime.strptime(str(order_date), "%Y-%m-%d %H:%M:%S")
1565 varun.gupt 355
    formated_order_date = dt.strftime("%A, %d. %B %Y %I:%M%p")
356
 
1262 varun.gupt 357
    email_header = Template(html_header).substitute(dict(order_date = formated_order_date))
2050 varun.gupt 358
    email_footer = Template(html_footer).substitute(dict(total_amount = "%.2f" % total_amount))
1262 varun.gupt 359
 
1351 varun.gupt 360
    try:
361
        helper_client = HelperClient().get_client()
1398 varun.gupt 362
        helper_client.saveUserEmailForSending(user_email, "", "Saholic - Order Details", email_header + html_table + email_footer, str(transaction_id), "TransactionInfo")
1351 varun.gupt 363
        return True
364
    except Exception as e:
365
        print e
366
        return False
1262 varun.gupt 367
 
483 rajveer 368
def get_order(order_id):
369
    order = Order.get_by(id=order_id)
370
    if not order:
371
        raise TransactionServiceException(108, "no such order")
372
 
373
    return order
374
 
3014 chandransh 375
def get_orders_for_customer(customer_id, from_date, to_date, statuses):
483 rajveer 376
    if not customer_id:
377
        raise TransactionServiceException(101, "bad customer id")
378
    query = Order.query.filter(Order.customer_id == customer_id)
3014 chandransh 379
    if statuses:
380
        query = query.filter(Order.status.in_(tuple(statuses)))
483 rajveer 381
    if from_date:
382
        query = query.filter(Order.created_timestamp >from_date)
383
    if to_date:
384
        query = query.filter(Order.created_timestamp < to_date)
385
    return query.all()
386
 
1528 ankur.sing 387
def get_order_for_customer(order_id, customer_id):
388
    if not customer_id:
389
        raise TransactionServiceException(101, "bad customer id")
390
    order = get_order(order_id)
391
    if order.customer_id != customer_id:
392
        raise TransactionServiceException(108, "order: " + str(order_id) + " does not belong to customer: " + str(customer_id))
393
    return order
394
 
483 rajveer 395
def get_all_orders(status, from_date, to_date, warehouse_id):
396
    if not warehouse_id:
397
        raise TransactionServiceException(101, "bad warehouse id")
398
    query = Order.query.filter(Order.warehouse_id == warehouse_id)
399
    if status:
400
        query = query.filter(Order.status == status)
401
    if from_date:
402
        query = query.filter(Order.created_timestamp >from_date)
403
    if to_date:
404
        query = query.filter(Order.created_timestamp < to_date)
405
    return query.all()
406
 
1382 varun.gupt 407
def get_returnable_orders_for_customer(customer_id, limit = None):
408
    if not customer_id:
409
        raise TransactionServiceException(101, "bad customer id")
410
 
411
    query = Order.query.filter(Order.customer_id == customer_id)
412
    query = query.filter(Order.status != OrderStatus.PAYMENT_FAILED)
413
    query = query.filter(Order.status != OrderStatus.REJECTED)
414
    query = query.filter(Order.status > OrderStatus.SHIPPED_FROM_WH)
415
    query = query.filter(or_(Order.status < OrderStatus.DELIVERY_SUCCESS, Order.delivery_timestamp < datetime.datetime.now() + datetime.timedelta(hours = 48)))
416
    orders = query.all()
417
    order_ids = [order.id for order in orders]
418
    return order_ids
419
 
420
def get_cancellable_orders_for_customer(customer_id, limit = None):
421
    if not customer_id:
422
        raise TransactionServiceException(101, "bad customer id")
423
 
424
    query = Order.query.filter(Order.customer_id == customer_id)
425
    query = query.filter(Order.status != OrderStatus.REJECTED)
1483 varun.gupt 426
    query = query.filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING)
1382 varun.gupt 427
    query = query.filter(Order.status < OrderStatus.BILLED)
428
    orders = query.all()
429
    return [order.id for order in orders]
430
 
995 varun.gupt 431
def get_orders_by_billing_date(status, start_billing_date, end_billing_date, warehouse_id):
432
    if not warehouse_id:
433
        raise TransactionServiceException(101, "bad warehouse id")
434
 
435
    query = Order.query.filter(Order.warehouse_id == warehouse_id)
436
 
437
    if status:
438
        query = query.filter(Order.status == status)
439
 
440
    if start_billing_date:
441
        query = query.filter(Order.billing_timestamp >= start_billing_date)
442
 
443
    if end_billing_date:
444
        query = query.filter(Order.billing_timestamp <= end_billing_date)
445
 
446
    return query.all()
447
 
3451 chandransh 448
def get_orders_by_shipping_date(from_shipping_date, to_shipping_date, provider_id, warehouse_id, cod):
449
    query = Order.query.filter(Order.cod == cod)
3427 chandransh 450
 
451
    if warehouse_id and warehouse_id != -1:
452
        query = query.filter(Order.warehouse_id == warehouse_id)
453
 
454
    if provider_id and provider_id != -1:
455
        query = query.filter(Order.logistics_provider_id == provider_id)
456
 
457
    if from_shipping_date:
458
        query = query.filter(Order.shipping_timestamp >= from_shipping_date)
459
 
460
    if to_shipping_date:
461
        query = query.filter(Order.shipping_timestamp <= to_shipping_date)
462
 
463
    query = query.order_by(Order.airwaybill_no)
464
 
465
    return query.all()
466
 
1528 ankur.sing 467
def get_orders_for_transaction(transaction_id, customer_id):
468
    orders = Order.query.filter_by(transaction_id=transaction_id, customer_id=customer_id).all()
469
 
1208 chandransh 470
    if not orders:
471
        raise TransactionServiceException(101, "No order for the transaction")
472
    return orders
473
 
1405 ankur.sing 474
def get_undelivered_orders(provider_id, warehouse_id):
475
    query = Order.query
476
    if provider_id != -1:
477
        query = query.filter_by(Order.logistics_provider_id == provider_id)
478
    if warehouse_id != -1:
479
        query = query.filter(Order.warehouse_id == warehouse_id)
480
    query = query.filter(and_(Order.status != OrderStatus.DELIVERY_SUCCESS, Order.status != OrderStatus.FAILED, \
481
                              Order.status > OrderStatus.SHIPPED_FROM_WH))
1479 ankur.sing 482
    #Get last midnight time instead of current time as courier company would have sent the 
483
    #delivery records updated at least till midnight and not by the current time.
484
    time_format = "%Y-%m-%d %H:%M:%S.%f"
485
    t = time.strptime(str(datetime.datetime.now()), time_format)
486
    midnightTime = datetime.datetime(*t[:3])
487
    query = query.filter(Order.expected_delivery_time < midnightTime)
1405 ankur.sing 488
    orders = query.all()
489
    return orders
490
 
1208 chandransh 491
def get_line_items_for_order(order_id):
492
    query = LineItem.query.filter(LineItem.order_id == order_id)
493
    return query.all()
494
 
483 rajveer 495
def change_order_status(self, orderId, status, description):
496
    order = get_order(orderId)
497
    order.status = status
498
    order.statusDescription = description
499
    session.commit()
3578 mandeep.dh 500
    update_trust_level(order)
501
    return True
3499 mandeep.dh 502
 
3578 mandeep.dh 503
def update_trust_level(order):
3530 mandeep.dh 504
    try:
3578 mandeep.dh 505
        trust_level_delta = 0
506
        if order.cod == True:
507
            trust_level_delta = ORDER_STATUS_TO_USER_TRUST_LEVEL_DELTA_DICT.get(order.status, 0)
508
 
3530 mandeep.dh 509
        if (trust_level_delta != 0):
510
            user_client = UserClient().get_client()
511
            user_client.increaseTrustLevel(order.customer_id, trust_level_delta)
512
    except Exception as e:
513
        print e
3499 mandeep.dh 514
 
483 rajveer 515
    return True
3064 chandransh 516
 
517
def verify_order(self, orderId):
518
    logging.info("Verifying order no: " + str(orderId))
519
    order = get_order(orderId)
520
    if order.status == OrderStatus.INIT:
521
        order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
522
        order.statusDescription = "Submitted for processing"
523
        order.verification_timestamp = datetime.datetime.now()
524
        session.commit()
525
        logging.info("Successfully verified order no: " + str(orderId))
526
        return True
527
    else:
528
        logging.warning("Verify called for order no." + str(orderId) +" which is not in verification pending state");
529
        return False
530
 
921 rajveer 531
def accept_order(self, orderId):
3064 chandransh 532
    logging.info("Accepting order no: " + str(orderId))
921 rajveer 533
    order = get_order(orderId)
3036 chandransh 534
    if order.status in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW]:
3064 chandransh 535
        if not order.cod:
536
            if order.transaction.status == TransactionStatus.AUTHORIZED:
537
                __capture_txn(order.transaction.id)
3010 chandransh 538
        order.status = OrderStatus.ACCEPTED
539
        order.statusDescription = "Order Accepted"
540
        order.accepted_timestamp = datetime.datetime.now()
541
        session.commit()
3064 chandransh 542
        logging.info("Successfully accepted the order no.:" + str(orderId))
3010 chandransh 543
        return True
544
    else:
3064 chandransh 545
        logging.warning("Accept called for the unacceptable order: " + str(orderId))
3010 chandransh 546
        return False
921 rajveer 547
 
3010 chandransh 548
def __capture_txn(txnId):
549
    logging.info("Capturing payment for merchant txn:" + str(txnId))
550
    payment_client = PaymentClient().get_client()
551
    capture_result = payment_client.capturePayment(txnId)
552
    if capture_result:
3064 chandransh 553
        logging.info("Successfully captured payment for merchant txn:" + str(txnId))
554
        change_transaction_status(txnId, TransactionStatus.IN_PROCESS, "Payment received")
3010 chandransh 555
    else:
556
        raise TransactionServiceException(115, "Payment capture failed.")
557
 
921 rajveer 558
def bill_order(self, orderId):
559
    order = get_order(orderId)
560
    order.status = OrderStatus.BILLED
561
    order.statusDescription = "Order Billed"
562
    order.billing_timestamp = datetime.datetime.now()
563
    session.commit()
1208 chandransh 564
    return True
921 rajveer 565
 
1149 chandransh 566
def add_billing_details(orderId, invoice_number, billed_by):
494 rajveer 567
    order = Order.get_by(id=orderId)
568
    if not order:
2783 chandransh 569
        raise TransactionServiceException(101, "No order found for the given order id:" + str(orderId))
494 rajveer 570
    order.invoice_number = invoice_number
571
    order.billed_by = billed_by
572
    session.commit()
573
    return True
759 chandransh 574
 
2842 chandransh 575
def add_jacket_number(self, orderId, jacket_number, imei_number, item_number, billed_by, billing_type):
2783 chandransh 576
    if jacket_number is None or jacket_number <= 0:
577
        raise TransactionServiceException(110, "Invalid jacket number")
578
 
579
    if item_number is None or item_number.strip() == "":
580
        raise TransactionServiceException(110, "Invalid item number")
581
 
582
    if billed_by is None or billed_by.strip() == "":
583
        raise TransactionServiceException(110, "Invalid Biller")
584
 
1149 chandransh 585
    order = Order.get_by(id=orderId)
586
    if not order:
2783 chandransh 587
        raise TransactionServiceException(101, "No order found for the given order id" + str(orderId))
588
 
589
    order.jacket_number = jacket_number
2842 chandransh 590
    if billing_type == BillingType.OURS:
591
        order.invoice_number = get_next_invoice_number()
592
        order.billed_by = billed_by
593
        order.status = OrderStatus.BILLED
594
        order.statusDescription = "Order Billed"
595
        order.billing_timestamp = datetime.datetime.now()
2783 chandransh 596
 
2842 chandransh 597
        lineitem = order.lineitems[0]
598
        if lineitem.productGroup == "Handsets" and imei_number <= 0:
599
            raise TransactionServiceException(110, "No IMEI number supplied for a handset.")
600
        lineitem.item_number = item_number
601
        if imei_number > 0:
602
            lineitem.imei_number = str(imei_number)
2783 chandransh 603
 
1149 chandransh 604
    session.commit()
605
    return True
606
 
1220 chandransh 607
def batch_orders(warehouseId):
608
    if not warehouseId:
609
        raise TransactionServiceException(101, "bad warehouse id")
1225 chandransh 610
 
611
    batchno_generator = BatchNoGenerator()
612
    session.commit()
1220 chandransh 613
    query = Order.query.filter_by(warehouse_id=warehouseId)
614
    query = query.filter_by(status=OrderStatus.SUBMITTED_FOR_PROCESSING)
615
    query = query.order_by(Order.created_timestamp)
616
    pending_orders = query.all()
1225 chandransh 617
 
1220 chandransh 618
    serial_no = 1
619
    for order in pending_orders:
1276 chandransh 620
        order.batchNo = batchno_generator.id
1220 chandransh 621
        order.serialNo = serial_no
622
        serial_no += 1
623
    session.commit() 
624
    pending_orders = query.all()
625
    return pending_orders
626
 
627
def order_outofstock(orderId):
1911 chandransh 628
    '''
629
    Mark order as out of stock
630
    '''
1220 chandransh 631
    order = get_order(orderId)
632
    order.status = OrderStatus.INVENTORY_LOW
633
    order.statusDescription = "Low Inventory"
634
    order.outofstock_timestamp = datetime.datetime.now()
635
    session.commit()
636
    return True
637
 
3064 chandransh 638
def mark_orders_as_manifested(warehouse_id, provider_id, cod):
759 chandransh 639
    try:
986 chandransh 640
        current_timestamp = datetime.datetime.now()
3064 chandransh 641
        orders = Order.query.filter_by(warehouse_id = warehouse_id, logistics_provider_id = provider_id, status = OrderStatus.BILLED, cod = cod).all()
759 chandransh 642
        for order in orders:
643
            order.status = OrderStatus.SHIPPED_FROM_WH
987 chandransh 644
            order.statusDescription = "Order shipped from warehouse"
986 chandransh 645
            order.shipping_timestamp = current_timestamp
759 chandransh 646
        session.commit()
647
        return True
648
    except:
649
        return False
1113 chandransh 650
 
651
def mark_orders_as_picked_up(provider_id, pickup_details):
652
    for awb, pickup_timestamp in pickup_details.iteritems():
1135 chandransh 653
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
2684 chandransh 654
        if order == None or order.status != OrderStatus.SHIPPED_FROM_WH:
2220 chandransh 655
            #raise TransactionServiceException(102, "No order found for the awb: " + awb)
656
            continue
1113 chandransh 657
        order.status = OrderStatus.SHIPPED_TO_LOGST
658
        order.statusDescription = "Order picked up by Courier Company"
1246 chandransh 659
        order.pickup_timestamp = pickup_timestamp
1113 chandransh 660
    session.commit()
1217 chandransh 661
 
1132 chandransh 662
    current_time = datetime.datetime.now()
663
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
1246 chandransh 664
    from_datetime = to_datetime - datetime.timedelta(2)
1132 chandransh 665
    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 666
    return orders_not_picked_up
667
 
1132 chandransh 668
def mark_orders_as_delivered(provider_id, delivered_orders):
669
    for awb, detail in delivered_orders.iteritems():
1135 chandransh 670
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
2684 chandransh 671
        if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST]:
1911 chandransh 672
            continue
673
            #raise TransactionServiceException(103, "No order found for the awb:" + awb)
1132 chandransh 674
        order.status = OrderStatus.DELIVERY_SUCCESS
675
        order.statusDescription = "Order delivered"
676
        order.delivery_timestamp, order.receiver = detail.split('|')
3578 mandeep.dh 677
        update_trust_level(order)
1132 chandransh 678
    session.commit()
679
 
1135 chandransh 680
def mark_orders_as_failed(provider_id, returned_orders):
681
    for awb, detail in returned_orders.iteritems():
682
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
2684 chandransh 683
        if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST]:
684
            continue
685
            #raise TransactionServiceException(103, "No order found for the awb:" + awb)
686
        order.status = OrderStatus.SALES_RETURN_IN_TRANSIT
1135 chandransh 687
        order.delivery_timestamp, reason = detail.split('|')
688
        order.statusDescription = "Order Returned to Origin:" + reason
3578 mandeep.dh 689
        update_trust_level(order)
1911 chandransh 690
    session.commit()
1135 chandransh 691
 
1246 chandransh 692
def update_non_delivery_reason(provider_id, undelivered_orders):
693
    for awb, reason in undelivered_orders.iteritems():
694
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
695
        if order == None:
2684 chandransh 696
            continue
697
            #raise TransactionServiceException(103, "No order found for the awb:" + awb)
1246 chandransh 698
        order.statusDescription = reason
699
    session.commit()
700
 
483 rajveer 701
def get_alerts(order_id, all):
702
    query = Alerts.query.filter_by(order_id=order_id)
703
    if not all:
704
        query = query.filter_by(valid=True)
705
    return query.all()
706
 
707
def set_alert(order_id, unset, type, comment):
708
    #get existing alert
709
    if unset:
710
        query = Alerts.query.filter_by(order_id=order_id)
711
        query = query.filter_by(type=type)
712
        alerts = query.all()
713
        for alert in alerts:
714
            if alert.valid:
715
                alert.valid = False
716
                alert.comment = comment
717
                alert.time_unset = datetime.datetime.now()
718
    else:
719
        alert = Alerts()
720
        alert.order_id = order_id
721
        alert.valid = True
722
        alert.type = type
723
        alert.comment = comment
724
        alert.time_set = datetime.datetime.now()
766 rajveer 725
    session.commit()    
726
 
1596 ankur.sing 727
def get_valid_order_count():
2851 chandransh 728
    '''
729
    Returns the number of orders which we processed. The reshipped orders are not
730
    counted since there'll always be a new compensating order present.
731
    '''
3216 chandransh 732
    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 733
 
1627 ankur.sing 734
def get_cust_count_with_successful_txn():
2851 chandransh 735
    '''
736
    Returns the number of distinct customers who have done successful transactions.
737
    It uses the sqlalchemy func module to construct an efficient query.
738
    '''
739
    return session.query(func.count(distinct(Order.customer_id))).filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).scalar()
1627 ankur.sing 740
 
1730 ankur.sing 741
def get_valid_orders_amount_range():
2851 chandransh 742
    '''
743
    Returns a list containing the minimum and maximum amount across all
744
    orders placed with us. No need to consider reshippped orders for min/max
745
    amount of orders.
746
    '''
747
    return session.query(func.min(Order.total_amount), func.max(Order.total_amount)).filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).one()
1627 ankur.sing 748
 
1886 ankur.sing 749
def get_valid_orders(limit):
2851 chandransh 750
    '''
751
    Returns the last limit number of valid orders placed with us. Ignored the
752
    limit if it's passed as 0. Raises an excpetion if the supplied limit is 
753
    less than 0. 
754
    '''
755
    if limit < 0:
756
        raise TransactionServiceException(101, "Invalid argument limit " + limit)
3072 chandransh 757
    query = Order.query.filter(Order.status >= OrderStatus.INIT)
1886 ankur.sing 758
    query = query.order_by(desc(Order.created_timestamp))
2851 chandransh 759
    if limit != 0:
1886 ankur.sing 760
        query = query.limit(limit)
761
    return query.all()
762
 
2783 chandransh 763
def get_next_invoice_number():
764
    entity_id = InvoiceIDGenerator.query.with_lockmode("update").one()
765
    invoice_number = entity_id.id + 1
766
    entity_id.id = invoice_number
767
    session.commit()
768
    return invoice_number
769
 
2536 chandransh 770
def toggle_doa_flag(order_id):
771
    order = get_order(order_id)
772
    if(order.doaFlag):
773
        order.doaFlag = False
774
    else:
775
        order.doaFlag = True
776
    session.commit()
777
    return order.doaFlag
778
 
779
def request_pickup_number(order_id):
780
    order = get_order(order_id)
781
    if order.status != OrderStatus.DELIVERY_SUCCESS and order.status != OrderStatus.DOA_PICKUP_REQUESTED:
782
        return False
783
    from_user = 'cnc.center@shop2020.in'
784
    from_pwd = '5h0p2o2o'
785
 
786
    subject = "Pickup request"
787
    try:
3133 rajveer 788
        catalog_client = CatalogClient().get_client()
2536 chandransh 789
        warehouse = catalog_client.getWarehouse(order.warehouse_id)
790
 
791
        logistics_client = LogisticsClient().get_client()
792
        provider = logistics_client.getProvider(order.logistics_provider_id)
3051 chandransh 793
        to_addr = provider.details[DeliveryType.PREPAID].email
2536 chandransh 794
        raw_message = '''
795
        Dear Sir/Madam,
796
 
2559 chandransh 797
        Kindly arrange a pickup today from %(customer_city)s. Pickup and delivery addresses are mentioned below.
2536 chandransh 798
 
799
        Pickup CODE: %(provider_code)s
800
        Total Weight: %(order_weight)s kg
801
 
2559 chandransh 802
        Pickup Address:
2536 chandransh 803
 
804
        %(customer_address)s
805
 
806
        Delivery Address:
807
 
808
        %(warehouse_executive)s
809
        %(warehouse_address)s
810
        PIN %(warehouse_pin)s
811
        Phone: %(warehouse_phone)s
812
 
813
        Thanks and Regards
814
        Anand Sinha
815
        '''
2559 chandransh 816
        message = dedent(raw_message) % { 'customer_city' : order.customer_city,
3051 chandransh 817
                                          'provider_code' : provider.details[DeliveryType.PREPAID].accountNo,
2536 chandransh 818
                                          'order_weight' : str(order.total_weight),
819
                                          'customer_address' : __get_order_address(order),
820
                                          'warehouse_executive' : 'Parmod Kumar',
821
                                          'warehouse_address' : warehouse.location,
822
                                          'warehouse_pin' : warehouse.pincode,
823
                                          'warehouse_phone': '9971573026'}
3527 chandransh 824
        mail(from_user, from_pwd, [to_addr], subject, message)
2536 chandransh 825
        order.status = OrderStatus.DOA_PICKUP_REQUESTED
826
        order.statusDescription = "Pick up requested for DOA"
827
        session.commit()
828
        return True
829
    except Exception as e:
830
        print sys.exc_info()[0]
831
        return False
832
 
833
def authorize_pickup(order_id, pickup_number):
834
    order = get_order(order_id)
835
    if order.status != OrderStatus.DOA_PICKUP_REQUESTED:
836
        return False
837
    from_user = 'help@shop2020.in'
838
    from_pwd = '5h0p2o2o'
839
    subject = 'Pickup details'
840
    filename = "/tmp/" +  str(order.id) +".pdf" 
841
    try:
3133 rajveer 842
        catalog_client = CatalogClient().get_client()
2536 chandransh 843
        warehouse = catalog_client.getWarehouse(order.warehouse_id)
844
 
845
        logistics_client = LogisticsClient().get_client()
846
        provider = logistics_client.getProvider(order.logistics_provider_id)
847
        provider_name = provider.name
848
 
849
        to_addr = order.customer_email
850
        today = date.today().strftime("%d-%B-%Y (%A)")
851
 
852
        raw_message = '''
853
        Dear Mr. %(customer_name)s,
854
 
855
        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.
856
        Take a printout of the attachment in this mail which contains the delivery address and paste it on the packed parcel.
857
        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.
858
 
859
        Pickup will happen from the below mentioned address:-
860
 
861
        %(customer_address)s
862
 
863
        Do let us know in case of any concerns
864
 
865
        Thanks & Regards
866
        Saholic Team
867
        '''
868
        message = dedent(raw_message) % {'customer_name' : order.customer_name,
869
                                         'order_id' : order_id,
870
                                         'provider_name' : provider_name,
871
                                         'date': today,
872
                                         'pickup_request_no' : pickup_number,
873
                                         'customer_address' : __get_order_address(order)}
874
        print message
875
        __generate_return_advice(order, warehouse, provider, filename)
3527 chandransh 876
        mail(from_user, from_pwd, [to_addr], subject, message, [get_attachment_part(filename)])
2536 chandransh 877
        order.pickupRequestNo = pickup_number
878
        order.status = OrderStatus.DOA_RETURN_AUTHORIZED
879
        order.statusDescription = "DOA pick up authorized"
2764 chandransh 880
        order.doa_auth_timestamp = datetime.datetime.now()
2536 chandransh 881
        session.commit()
882
        return True
883
    except Exception as e:
884
        print sys.exc_info()[0]
885
        return False
886
    finally:
887
        if os.path.exists(filename):
888
            os.remove(filename)
889
 
2764 chandransh 890
def mark_doas_as_picked_up(provider_id, pickup_details):
891
    for pickupNo, doa_pickup_timestamp in pickup_details.iteritems():
892
        order = Order.query.filter_by(pickupRequestNo=pickupNo, logistics_provider_id = provider_id).first()
893
        if order == None or order.status != OrderStatus.DOA_RETURN_AUTHORIZED:
894
            #raise TransactionServiceException(102, "No order found for the awb: " + awb)
895
            continue
896
        order.status = OrderStatus.DOA_RETURN_IN_TRANSIT
897
        order.statusDescription = "DOA picked up by Courier Company"
898
        order.doa_pickup_timestamp = doa_pickup_timestamp
899
    session.commit()
900
 
901
    current_time = datetime.datetime.now()
902
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
903
    from_datetime = to_datetime - datetime.timedelta(2)
904
    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()
905
    return orders_not_picked_up
906
 
2616 chandransh 907
def receive_return(order_id):
2591 chandransh 908
    order = get_order(order_id)
2616 chandransh 909
    if order.status in [OrderStatus.DOA_RETURN_AUTHORIZED, OrderStatus.DOA_RETURN_IN_TRANSIT]:
910
        order.status = OrderStatus.DOA_RECEIVED
911
        order.statusDescription = "DOA package received"
2764 chandransh 912
        order.received_return_timestamp = datetime.datetime.now()
2616 chandransh 913
        session.commit()
914
    elif order.status == OrderStatus.SALES_RETURN_IN_TRANSIT :
915
        order.status = OrderStatus.SALES_RET_RECEIVED
916
        order.statusDescription = "Sales return received"
2764 chandransh 917
        order.received_return_timestamp = datetime.datetime.now()
2616 chandransh 918
        session.commit()
919
    else:
2591 chandransh 920
        return False
921
    return True
922
 
923
def validate_doa(order_id, is_valid):
924
    order = get_order(order_id)
2599 chandransh 925
    if order.status != OrderStatus.DOA_RECEIVED:
2591 chandransh 926
        return False
927
    if is_valid:
2609 chandransh 928
        order.status = OrderStatus.DOA_CERT_VALID
929
        order.statusDescription = "DOA Certificate Valid"
2591 chandransh 930
    else:
931
        order.status = OrderStatus.DOA_CERT_INVALID
932
        order.statusDescription = "DOA Certificate Invalid"
3578 mandeep.dh 933
        update_trust_level(order)
2591 chandransh 934
    session.commit()
935
    return True
936
 
2628 chandransh 937
def reship_order(order_id):
938
    """
939
    If the order is in SALES_RET_RECEIVED or DOA_CERT_INVALID state, it does the following:
940
        1. Creates a new order for processing in the BILLED state. All billing information is saved.
941
        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.
942
 
943
    If the order is in DOA_CERT_VALID state, it does the following:
944
        1. Creates a new order for processing in the SUBMITTED_FOR_PROCESSING state.
945
        2. Creates a return order for the warehouse executive to return the DOA material.
946
        3. Marks the current order as the final DOA_RESHIPPED state.
947
 
948
    Returns the id of the newly created order.
949
 
950
    Throws an exception if the order with the given id couldn't be found.
951
 
952
    Parameters:
953
     - orderId
954
    """
955
    order = get_order(order_id)
956
    if order.status == OrderStatus.SALES_RET_RECEIVED:
957
        new_order = __clone_order(order, True)
958
        order.status = OrderStatus.SALES_RET_RESHIPPED
959
        order.statusDescription = "Order Reshipped"
960
        session.commit()
3578 mandeep.dh 961
        update_trust_level(order)
2628 chandransh 962
    elif order.status == OrderStatus.DOA_CERT_INVALID:
963
        new_order = __clone_order(order, True)
964
        order.status = OrderStatus.DOA_INVALID_RESHIPPED
965
        order.statusDescription = "Order Reshipped"
966
        session.commit()
967
    elif order.status == OrderStatus.DOA_CERT_VALID:
968
        new_order = __clone_order(order, False)
969
        ret_order = __create_return_order(order)
970
        order.status = OrderStatus.DOA_RESHIPPED
971
        order.statusDescription = "Order Reshipped"
972
        session.commit()
973
    else:
974
        raise TransactionServiceException(112, "This order can't be reshipped")
975
 
2764 chandransh 976
    order.reship_timestamp = datetime.datetime.now()
2628 chandransh 977
    order.new_order_id = new_order.id
978
    session.commit()
979
 
980
    return new_order.id
981
 
3226 chandransh 982
def refund_order(order_id, refunded_by, reason):
2628 chandransh 983
    """
984
    If the order is in SALES_RET_RECEIVED, DOA_CERT_VALID or DOA_CERT_INVALID state, it does the following:
985
        1. Creates a refund request for batch processing.
986
        2. Creates a return order for the warehouse executive to return the shipped material.
987
        3. Marks the current order as SALES_RET_REFUNDED, DOA_VALID_REFUNDED or DOA_INVALID_REFUNDED final states.
988
 
989
    If the order is in SUBMITTED_FOR_PROCESSING or INVENTORY_LOW state, it does the following:
990
        1. Creates a refund request for batch processing.
3226 chandransh 991
        2. Cancels the reservation of the item in the warehouse.
992
        3. Marks the current order as the REFUNDED final state.
2628 chandransh 993
 
3226 chandransh 994
    For all COD orders, if the order is in INIT, SUBMITTED_FOR_PROCESSING or INVENTORY_LOW state, it does the following:
995
        1. Cancels the reservation of the item in the warehouse.
996
        2. Marks the current order as CANCELED.
997
 
998
    In all cases, it updates the reason for cancellation or refund and the person who performed the action.
999
 
1000
    Returns True if it is successful, False otherwise.
2628 chandransh 1001
 
1002
    Throws an exception if the order with the given id couldn't be found.
1003
 
1004
    Parameters:
3226 chandransh 1005
     - order_id
1006
     - refunded_by
1007
     - reason
2628 chandransh 1008
    """
3414 chandransh 1009
    logging.info("Refunding order id: " + str(order_id))
2628 chandransh 1010
    order = get_order(order_id)
3186 chandransh 1011
 
1012
 
3366 chandransh 1013
 
3226 chandransh 1014
    if order.cod:
3414 chandransh 1015
        logging.info("Refunding COD order with status " + str(order.status))
3366 chandransh 1016
        status_transition = {OrderStatus.SALES_RET_RECEIVED : OrderStatus.SALES_RET_REFUNDED,
1017
                     OrderStatus.DOA_CERT_INVALID : OrderStatus.DOA_INVALID_REFUNDED,
1018
                     OrderStatus.DOA_CERT_VALID : OrderStatus.DOA_VALID_REFUNDED,
1019
                     OrderStatus.INIT : OrderStatus.CANCELED,
1020
                     OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.CANCELED,
1021
                     OrderStatus.INVENTORY_LOW : OrderStatus.CANCELED,
1022
                     OrderStatus.ACCEPTED : OrderStatus.CANCELED,
3731 chandransh 1023
                     OrderStatus.BILLED : OrderStatus.CANCELED
3366 chandransh 1024
                     }
1025
        if order.status not in status_transition.keys():
1026
            raise TransactionServiceException(114, "This order can't be refunded")
1027
 
3413 chandransh 1028
        if order.status < OrderStatus.BILLED:
3226 chandransh 1029
            __update_inventory_reservation(order)
1030
            order.statusDescription = "Order Cancelled"
3415 chandransh 1031
        elif order.status == OrderStatus.BILLED:
3413 chandransh 1032
            __create_return_order(order)
1033
            order.statusDescription = "Order Cancelled"
3226 chandransh 1034
        elif order.status in [OrderStatus.SALES_RET_RECEIVED, OrderStatus.DOA_CERT_INVALID, OrderStatus.DOA_CERT_VALID] :
1035
            __create_return_order(order)
1036
            __create_refund(order)
1037
            order.statusDescription = "Order Refunded"
1038
    else:
3366 chandransh 1039
        status_transition = {OrderStatus.SALES_RET_RECEIVED : OrderStatus.SALES_RET_REFUNDED,
1040
                     OrderStatus.DOA_CERT_INVALID : OrderStatus.DOA_INVALID_REFUNDED,
1041
                     OrderStatus.DOA_CERT_VALID : OrderStatus.DOA_VALID_REFUNDED,
1042
                     OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.REFUNDED,
3731 chandransh 1043
                     OrderStatus.INVENTORY_LOW : OrderStatus.REFUNDED,
1044
                     OrderStatus.ACCEPTED : OrderStatus.REFUNDED,
1045
                     OrderStatus.BILLED : OrderStatus.REFUNDED
3366 chandransh 1046
                     }
1047
        if order.status not in status_transition.keys():
1048
            raise TransactionServiceException(114, "This order can't be refunded")
1049
 
3226 chandransh 1050
        if order.status in [OrderStatus.SALES_RET_RECEIVED, OrderStatus.DOA_CERT_INVALID, OrderStatus.DOA_CERT_VALID] :
1051
            __create_return_order(order)
1052
            __create_refund(order)
1053
            order.statusDescription = "Order Refunded"
1054
        elif order.status in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW]:
3191 chandransh 1055
            __create_cancellation(order)
3226 chandransh 1056
            __update_inventory_reservation(order)
1057
            order.statusDescription = "Order Refunded"
3195 chandransh 1058
 
1059
    order.status = status_transition[order.status]
1060
    order.refund_timestamp = datetime.datetime.now()
3226 chandransh 1061
    order.refunded_by = refunded_by
1062
    order.refund_reason = reason
2628 chandransh 1063
 
1064
    session.commit()
1065
    return True
1066
 
3186 chandransh 1067
def __update_inventory_reservation(order):
3191 chandransh 1068
    '''
1069
    Reduce the reservation count for all line items of the given order.
1070
    '''
3186 chandransh 1071
    try:
1072
        catalog_client = CatalogClient().get_client()
1073
        for lineitem in order.lineitems:
1074
            catalog_client.reduceReservationCount(lineitem.item_id, order.warehouse_id, lineitem.quantity);
1075
    except:
1076
        print "Unable to reduce reservation count"
1077
 
2628 chandransh 1078
def __clone_order(order, should_copy_billing_info):
1079
    current_time = datetime.datetime.now()
1080
    new_order = Order()
1081
    new_order.customer_id = order.customer_id
1082
    new_order.customer_name = order.customer_name
1083
    new_order.customer_city = order.customer_city
1084
    new_order.customer_state = order.customer_state
1085
    new_order.customer_mobilenumber = order.customer_mobilenumber
1086
    new_order.customer_pincode = order.customer_pincode
1087
    new_order.customer_address1 = order.customer_address1
1088
    new_order.customer_address2 = order.customer_address2
1089
    new_order.customer_email = order.customer_email
1090
    new_order.total_amount = order.total_amount
1091
    new_order.total_weight = order.total_weight
1092
    new_order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
1093
    new_order.statusDescription = 'Submitted for Processing'
1094
    new_order.created_timestamp = current_time
1095
 
1096
    new_order.transaction = order.transaction
3239 chandransh 1097
    new_order.cod = False
2628 chandransh 1098
 
1099
    for line_item in order.lineitems:
1100
        litem = LineItem()
1101
        litem.item_id = line_item.item_id
1102
        litem.productGroup = line_item.productGroup
1103
        litem.brand = line_item.brand
1104
        if line_item.model_number:
1105
            litem.model_number = line_item.model_number
1106
        if line_item.model_name:    
1107
            litem.model_name = line_item.model_name
1108
        if line_item.color:
1109
            litem.color = line_item.color
1110
        if line_item.extra_info:
1111
            litem.extra_info = line_item.extra_info
1112
        litem.quantity = line_item.quantity
1113
        litem.unit_price = line_item.unit_price
1114
        litem.unit_weight = line_item.unit_weight
1115
        litem.total_price = line_item.total_price
1116
        litem.total_weight = line_item.total_weight
1117
        litem.transfer_price = line_item.transfer_price
1118
        litem.order = new_order
1119
 
1120
    logistics_client = LogisticsClient().get_client()
1121
    item_id = new_order.lineitems[0].item_id
3239 chandransh 1122
    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 1123
    logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
1124
 
1125
    new_order.warehouse_id = logistics_info.warehouseId
1126
    new_order.logistics_provider_id = logistics_info.providerId
1127
    new_order.airwaybill_no = logistics_info.airway_billno
1128
    new_order.tracking_id = new_order.airwaybill_no
1129
    new_order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
3986 chandransh 1130
    new_order.promised_delivery_time = new_order.expected_delivery_time
2628 chandransh 1131
 
1132
    if should_copy_billing_info:
1133
        new_order.invoice_number = order.invoice_number
1134
        new_order.billed_by = order.billed_by
1135
        new_order.warehouse_id = order.warehouse_id
1136
        new_order.accepted_timestamp = current_time
1137
        new_order.billing_timestamp = current_time    
1138
        new_order.status = OrderStatus.BILLED
1139
        new_order.statusDescription = 'Order Billed'
1140
    else:       
3133 rajveer 1141
        catalog_client = CatalogClient().get_client()
2628 chandransh 1142
        catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, new_order.lineitems[0].quantity)
1143
 
1144
        item_pricing = catalog_client.getItemPricing(item_id, order.warehouse_id)
1145
        new_order.lineitems[0].transfer_price = item_pricing.transferPrice
1146
 
1147
    return new_order
1148
 
1149
def __create_return_order(order):
2687 chandransh 1150
    ret_order = ReturnOrder(order)
2628 chandransh 1151
    return ret_order
1152
 
1153
def __create_refund(order):
2747 chandransh 1154
    payment_client = PaymentClient().get_client()
1155
    payment_client.createRefund(order.id, order.transaction.id, order.total_amount)
1156
    return
2628 chandransh 1157
 
3035 chandransh 1158
def __create_cancellation(order):
1159
    return
1160
 
2536 chandransh 1161
def __get_order_address(order):
1162
    address = order.customer_name + "\n" + order.customer_address1 + "\n"
1163
    if order.customer_address2:
1164
        address = address + order.customer_address2 + "\n"
1165
    address = address + order.customer_city + "\n"
1166
    address = address + order.customer_state + "\n"
1167
    address = address + "PIN " + order.customer_pincode + "\n"
1168
    address = address + "Phone: " + order.customer_mobilenumber
1169
    return address
1170
 
1171
def __generate_return_advice(order, warehouse, provider, filename):    
1172
    pdf = Canvas(filename)
1173
    pdf.setFont('Times-Bold',16)
1174
    address_text = pdf.beginText(inch, PAGE_HEIGHT - inch)
1175
    address_text.textLine("To")
1176
    address_text.textLine("Parmod Kumar")
1177
    for line in warehouse.location.split("\n"):
1178
        address_text.textLine(line)
1179
    address_text.textLine("PIN " + warehouse.pincode)
1180
    address_text.textLine("")
1181
    address_text.textLine("Phone: 9971573026")
1182
    pdf.drawText(address_text)
1183
 
1184
    pdf.setFont('Times-Roman',12)
1185
    order_text = pdf.beginText(inch, PAGE_HEIGHT - 4 * inch)
3051 chandransh 1186
    order_text.textLine("Pickup CODE: " + provider.details[DeliveryType.PREPAID].accountNo)
2536 chandransh 1187
    lineitem = order.lineitems[0] 
1188
    order_text.textLine("Content : " + lineitem.brand + " " + lineitem.model_number + " " + lineitem.color)
1189
    order_text.textLine("Declared Value: Rs. " + str(order.total_amount))
1190
    order_text.textLine("Order ID : " + str(order.id))
1191
    pdf.drawText(order_text)
1192
    pdf.showPage()
1193
    pdf.save()
1194
    return filename
1195
 
2697 chandransh 1196
def get_return_orders(warehouse_id, from_date, to_date):
1197
    query = ReturnOrder.query.filter_by(processedStatus = False)
1198
    if warehouse_id != -1:
1199
        query = query.filter_by(warehouseId = warehouse_id)
1200
    if from_date:
1201
        query = query.filter(Order.created_timestamp >from_date)
1202
    if to_date:
1203
        query = query.filter(Order.created_timestamp < to_date)
1204
    return [return_order.to_thrift_object() for return_order in query.all()]
1205
 
2700 chandransh 1206
def get_return_order(id):
1207
    ret_order = ReturnOrder.get_by(orderId = id)
1208
    if ret_order is None:
1209
        raise TransactionServiceException(116, "No return order found for the given order id")
1210
    return ret_order.to_thrift_object()
1211
 
2697 chandransh 1212
def process_return(ret_order_id):
1213
    ret_order = ReturnOrder.get_by(orderId = ret_order_id)
1214
    if ret_order is None:
1215
        raise TransactionServiceException(116, "No return order found for the given order id")
1216
    ret_order.processedStatus = True
2701 chandransh 1217
    ret_order.processedAt = datetime.datetime.now()
2697 chandransh 1218
    session.commit()
1219
 
2819 chandransh 1220
def create_purchase_order(warehouseId):
1221
    if not warehouseId:
1222
        raise TransactionServiceException(101, "bad warehouse id")
1223
 
3451 chandransh 1224
    query = Order.query.with_lockmode("update").filter_by(warehouse_id=warehouseId)
2819 chandransh 1225
    query = query.filter_by(status=OrderStatus.SUBMITTED_FOR_PROCESSING)
1226
    query = query.filter_by(purchase_order_id=None)
1227
    query = query.order_by(Order.created_timestamp)
1228
    pending_orders = query.all()
1229
 
1230
    if not pending_orders:
1231
        return -1
1232
 
1233
    t_purchase_order = PurchaseOrder()
2832 chandransh 1234
    t_purchase_order.supplierId = warehouseId
2819 chandransh 1235
    t_purchase_order.warehouseId = warehouseId
1236
    t_purchase_order.lineitems = []
1237
 
1238
    lineitems = {}
1239
 
1240
    for order in pending_orders:
1241
        for lineitem in order.lineitems:
1242
            item_id = lineitem.item_id
1243
            if lineitems.has_key(item_id):
1244
                lineitems[item_id].quantity = lineitems[item_id].quantity + lineitem.quantity
1245
            else:
1246
                t_po_lineitem = POLineItem()
1247
                t_po_lineitem.productGroup = lineitem.productGroup
1248
                t_po_lineitem.brand = lineitem.brand
1249
                t_po_lineitem.modelNumber = lineitem.model_number
1250
                t_po_lineitem.modelName = lineitem.model_name
1251
                t_po_lineitem.color = lineitem.color
1252
                t_po_lineitem.itemId = lineitem.item_id
1253
                t_po_lineitem.quantity = lineitem.quantity
1254
                t_po_lineitem.unitPrice = lineitem.transfer_price
1255
                lineitems[item_id] = t_po_lineitem
1256
                t_purchase_order.lineitems.append(t_po_lineitem)
1257
 
1258
    warehouse_client = WarehouseClient().get_client()
1259
    po_id = warehouse_client.createPurchaseOrder(t_purchase_order)
1260
 
1261
    for order in pending_orders:
1262
        order.purchase_order_id = po_id
1263
    session.commit()
1264
    return po_id
1265
 
3451 chandransh 1266
def update_weight(order_id, weight):
3469 chandransh 1267
    '''
1268
    Update the weight of order
1269
    '''
3451 chandransh 1270
    order = get_order(order_id)
1271
    order.total_weight = weight
3469 chandransh 1272
    lineitem = order.lineitems[0]
1273
    lineitem.total_weight = weight
1274
    lineitem.unit_weight = weight
3451 chandransh 1275
 
3469 chandransh 1276
    session.commit()
1277
    return order
1278
 
1279
def change_product(order_id, item_id):
1280
    '''
1281
    Ship a product of a different color to the customer.
1282
    '''
1283
    order = get_order(order_id)
1284
    if order.status not in [OrderStatus.INIT, OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW]:
1285
        raise TransactionServiceException(120, "This order has already been processed. Please seek help from engineering.")
1286
 
1287
    lineitem = order.lineitems[0]
3451 chandransh 1288
 
3469 chandransh 1289
    catalog_client = CatalogClient().get_client()
1290
 
1291
    old_item = catalog_client.getItem(lineitem.item_id)
1292
    item = catalog_client.getItem(item_id)
1293
    if old_item.catalogItemId != item.catalogItemId:
1294
        raise TransactionServiceException(121, "You can't ship a different item. You can only ship the same item in a different color.")
1295
 
1296
    catalog_client.reserveItemInWarehouse(item_id, order.warehouse_id, lineitem.quantity)
1297
    catalog_client.reduceReservationCount(lineitem.item_id, order.warehouse_id, lineitem.quantity)
1298
    #TODO: Check that this new item has the same price
1299
 
1300
    lineitem.item_id = item_id
1301
    lineitem.color = item.color
1302
 
3451 chandransh 1303
    session.commit()
3469 chandransh 1304
 
1305
    order = get_order(order_id)
3451 chandransh 1306
    return order
3469 chandransh 1307
 
1308
def change_warehouse(order_id, warehouse_id):
1309
    '''
1310
    Update the warehouse which will be used to fulfil this order.
1311
    '''
1312
    order = get_order(order_id)
1313
    if order.status not in [OrderStatus.INIT, OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW]:
1314
        raise TransactionServiceException(117, "This order has already been processed. Please seek help from engineering.")
1315
 
1316
    if warehouse_id == order.warehouse_id:
1317
        raise TransactionServiceException(118, "You have selected the current warehouse again. Nothing to do")
1318
 
1319
    lineitem = order.lineitems[0]
1320
    catalog_client = CatalogClient().get_client()
1321
    try:
1322
        warehouse = catalog_client.getWarehouse(warehouse_id)
1323
    except:
1324
        raise TransactionServiceException(119, "No warehouse with id" + str(warehouse_id))
1325
 
1326
    catalog_client.reserveItemInWarehouse(lineitem.item_id, warehouse_id, lineitem.quantity)
1327
    catalog_client.reduceReservationCount(lineitem.item_id, order.warehouse_id, lineitem.quantity)
3451 chandransh 1328
 
3469 chandransh 1329
    order.warehouse_id = warehouse_id
1330
    session.commit()
1331
    return order
3553 chandransh 1332
 
3986 chandransh 1333
def add_delay_reason(order_id, reason, further_delay=0):
3553 chandransh 1334
    '''
3994 chandransh 1335
    Adds a delay reason to the order and updates the expected delivery time.
3553 chandransh 1336
    '''
1337
    order = get_order(order_id)
1338
    try:
3581 chandransh 1339
        order.delay_reason = DelayReason._VALUES_TO_NAMES[reason]
4009 chandransh 1340
        actual_shipping_delay = adjust_delivery_time(order.expected_shipping_time, further_delay) 
1341
        actual_delivery_delay = adjust_delivery_time(order.expected_delivery_time, further_delay)
1342
        order.expected_shipping_time = order.expected_shipping_time + datetime.timedelta(days=actual_shipping_delay)
1343
        order.expected_delivery_time = order.expected_delivery_time + datetime.timedelta(days=actual_delivery_delay)
3553 chandransh 1344
        session.commit()
4008 mandeep.dh 1345
 
1346
        try:
1347
            transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
1348
            transaction_requiring_extra_processing.category = 'DELAYED_DELIVERY'
1349
            transaction_requiring_extra_processing.transaction_id = order.transaction_id
1350
            session.commit()
1351
        except Exception as e:
1352
            print 'Could not persist transaction id: ' + order.transaction_id + ' for further processing due to: ' + e
1353
 
3553 chandransh 1354
        return True
1355
    except:
3994 chandransh 1356
        print sys.exc_info()[0]
3553 chandransh 1357
        return False
3956 chandransh 1358
 
1359
def reconcile_cod_collection(collected_amount_map, xferBy, xferTxnId, xferDate):
1360
    unprocessed_awbs = {}
1361
    for awb, amount in collected_amount_map.iteritems():
1362
        try:
1363
            order = Order.query.filter_by(airwaybill_no=awb).one()
1364
        except NoResultFound:
1365
            unprocessed_awbs[awb] = "No order was found for the given AWB: " + awb
1366
            continue
1367
        except MultipleResultsFound:
1368
            unprocessed_awbs[awb] = "Multiple orders were found for the given AWB:" + awb
1369
            continue
1370
 
1371
        if order.cod_reconciliation_timestamp:
1372
            #This order has been processed already! This may be a re-run. Let's allow other orders to be processed.
1373
            continue
1374
 
1375
        if order.status < OrderStatus.DELIVERY_SUCCESS:
1376
            #Order has not been delivered yet. No way we can receive the payment.
1377
            unprocessed_awbs[awb] = "Payment has been received for order: " + str(order.id) + " before delivery reconciliation"
1378
            continue
1379
 
1380
        if amount != order.total_amount:
1381
            #Received amount is less than or more than the order amount. Mustn't let the user proceed.
1382
            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)
1383
            continue
1384
 
1385
        try:
1386
            payment_client = PaymentClient().get_client()
1387
            payment_client.partiallyCapturePayment(order.transaction.id, amount, xferBy, xferTxnId, xferDate)
1388
        except Exception:
1389
            unprocessed_awbs[awb] = "We were unable to partially capture payment for order id " + str(order.id) + ", AWB: " + awb
1390
            continue
1391
 
1392
        #Payment has been recorded now. We can update the order peacefully.
1393
        order.cod_reconciliation_timestamp = datetime.datetime.now()
1394
        session.commit()
3469 chandransh 1395
 
3956 chandransh 1396
    return unprocessed_awbs
4008 mandeep.dh 1397
 
1398
def get_transactions_requiring_extra_processing(category):
1399
    transactions = []
1400
 
1401
    if category is None:
1402
        transactions = [a.transaction_id for a in TransactionRequiringExtraProcessing.query.all()]
1403
    else:
1404
        transactions = [a.transaction_id for a in TransactionRequiringExtraProcessing.query.filter_by(category = ExtraTransactionProcessingType._VALUES_TO_NAMES.get(category)).all()]
1405
 
1406
    return transactions
1407
 
1408
def mark_transaction_as_processed(transactionId, category):
1409
    TransactionRequiringExtraProcessing.query.filter_by(transaction_id = transactionId).filter_by(category = ExtraTransactionProcessingType._VALUES_TO_NAMES.get(category)).delete()
1410
    session.commit()
1411
    return
1412
 
4018 chandransh 1413
def get_item_wise_risky_orders_count():
1414
    """
1415
    Returns a map containing the number of risky orders keyed by item id. A risky order
1416
    is defined as one whose shipping date is about to expire.
1417
    """
1418
    item_wise_risky_orders_count = {}
1419
    shipping_cutoff_time = (datetime.datetime.now() + datetime.timedelta(days = 1)).replace(hour=15, minute=0, second=0)
1420
    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()
1421
    for order in orders:
1422
        for lineitem in order.lineitems:
1423
            quantity = lineitem.quantity
1424
            if item_wise_risky_orders_count.has_key(lineitem.item_id):
1425
                quantity = quantity + item_wise_risky_orders_count[lineitem.item_id]
1426
 
1427
            item_wise_risky_orders_count[lineitem.item_id] = quantity
1428
    return item_wise_risky_orders_count
1429
 
766 rajveer 1430
def close_session():
1431
    if session.is_active:
1432
        print "session is active. closing it."
2783 chandransh 1433
        session.close()
3376 rajveer 1434
 
1435
def is_alive():
1436
    try:
1437
        session.query(Transaction.id).limit(1).one()
1438
        return True
1439
    except:
1440
        return False
3413 chandransh 1441