Subversion Repositories SmartDukaan

Rev

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

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