Subversion Repositories SmartDukaan

Rev

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