Subversion Repositories SmartDukaan

Rev

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