Subversion Repositories SmartDukaan

Rev

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