Subversion Repositories SmartDukaan

Rev

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