Subversion Repositories SmartDukaan

Rev

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