Subversion Repositories SmartDukaan

Rev

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