Subversion Repositories SmartDukaan

Rev

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