Subversion Repositories SmartDukaan

Rev

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