Subversion Repositories SmartDukaan

Rev

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