Subversion Repositories SmartDukaan

Rev

Rev 4868 | Rev 4875 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

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