Subversion Repositories SmartDukaan

Rev

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