Subversion Repositories SmartDukaan

Rev

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

Rev Author Line No. Line
104 ashish 1
'''
2
Created on 29-Mar-2010
3
 
2536 chandransh 4
@author: Chandranshu
104 ashish 5
'''
2536 chandransh 6
 
4369 rajveer 7
from datetime import date, timedelta
2536 chandransh 8
from elixir import *
3499 mandeep.dh 9
from reportlab.lib.units import inch
2536 chandransh 10
from reportlab.pdfgen.canvas import Canvas
11
from reportlab.rl_config import defaultPageSize
3499 mandeep.dh 12
from shop2020.clients.CatalogClient import CatalogClient
13
from shop2020.clients.HelperClient import HelperClient
14
from shop2020.clients.LogisticsClient import LogisticsClient
2747 chandransh 15
from shop2020.clients.PaymentClient import PaymentClient
4501 mandeep.dh 16
from shop2020.clients.PurchaseClient import PurchaseClient
3499 mandeep.dh 17
from shop2020.clients.UserClient import UserClient
18
from shop2020.clients.WarehouseClient import WarehouseClient
4421 mandeep.dh 19
from shop2020.config.client.ConfigClient import ConfigClient
3499 mandeep.dh 20
from shop2020.model.v1 import order
21
from shop2020.model.v1.order.impl.DataService import Transaction, LineItem, \
4435 mandeep.dh 22
    Order, BatchNoGenerator, InvoiceIDGenerator, TransactionRequiringExtraProcessing, \
4600 varun.gupt 23
    OrderInventory, Alert, PaymentSettlement, EBSSettlementSummary
2687 chandransh 24
from shop2020.model.v1.order.impl.model.ReturnOrder import ReturnOrder
4541 mandeep.dh 25
from shop2020.thriftpy.generic.ttypes import ExceptionType
3499 mandeep.dh 26
from shop2020.thriftpy.logistics.ttypes import DeliveryType
4435 mandeep.dh 27
from shop2020.thriftpy.model.v1.catalog.ttypes import BillingType, \
28
    InventoryServiceException
29
from shop2020.thriftpy.model.v1.order.ttypes import TransactionServiceException, \
30
    TransactionStatus, OrderStatus, DelayReason, ExtraTransactionProcessingType, \
31
    HotspotAction, TimeoutSummary
4421 mandeep.dh 32
from shop2020.thriftpy.payments.ttypes import PaymentException
4600 varun.gupt 33
from shop2020.thriftpy.purchase.ttypes import PurchaseOrder, LineItem as POLineItem
34
from shop2020.thriftpy.warehouse.ttypes import ScanType, WarehouseServiceException
3499 mandeep.dh 35
from shop2020.utils.EmailAttachmentSender import mail, get_attachment_part
855 rajveer 36
from shop2020.utils.Utils import to_py_date, to_java_date
4008 mandeep.dh 37
from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
3499 mandeep.dh 38
from sqlalchemy.sql import func
39
from sqlalchemy.sql.expression import and_, or_, desc, not_, distinct
40
from string import Template
41
from textwrap import dedent
42
import datetime
43
import logging
44
import os
4421 mandeep.dh 45
import sys
4435 mandeep.dh 46
import time
4421 mandeep.dh 47
import traceback
3499 mandeep.dh 48
logging.basicConfig(level=logging.DEBUG)
104 ashish 49
 
3499 mandeep.dh 50
 
4763 rajveer 51
PREPAID_SHIPPING_CUTOFF_TIME = 15
4019 chandransh 52
COD_SHIPPING_CUTOFF_TIME = 12
3499 mandeep.dh 53
 
2819 chandransh 54
PAGE_HEIGHT=defaultPageSize[1]
55
PAGE_WIDTH=defaultPageSize[0]
56
 
4763 rajveer 57
billedOrdersColorMap = {}
58
 
3499 mandeep.dh 59
ORDER_STATUS_TO_USER_TRUST_LEVEL_DELTA_DICT = {
4484 rajveer 60
        OrderStatus.RTO_RESHIPPED     :  3,
61
        OrderStatus.RTO_IN_TRANSIT : -5,
3499 mandeep.dh 62
        OrderStatus.DELIVERY_SUCCESS        :  1,
3578 mandeep.dh 63
        OrderStatus.DOA_CERT_INVALID        : -5
3499 mandeep.dh 64
}
65
 
104 ashish 66
def get_new_transaction():
67
    transaction = Transaction()
68
    transaction.createdOn = datetime.datetime.now()
738 chandransh 69
    transaction.status = TransactionStatus.INIT
104 ashish 70
    transaction.status_message = "New transaction"
483 rajveer 71
    return transaction
72
 
73
def create_order(t_order):
74
    order = Order()
75
    if t_order.warehouse_id:
76
        order.warehouse_id = t_order.warehouse_id
509 rajveer 77
    if t_order.logistics_provider_id:    
483 rajveer 78
        order.logistics_provider_id = t_order.logistics_provider_id
79
        order.airwaybill_no = t_order.airwaybill_no
80
        order.tracking_id = t_order.tracking_id
4009 chandransh 81
    if t_order.expected_shipping_time:
4019 chandransh 82
        order.expected_shipping_time = to_py_date(t_order.expected_shipping_time).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
4102 chandransh 83
        order.promised_shipping_time = order.expected_shipping_time
4009 chandransh 84
    if t_order.expected_delivery_time:
483 rajveer 85
        order.expected_delivery_time = to_py_date(t_order.expected_delivery_time)
4102 chandransh 86
        order.promised_delivery_time = order.expected_delivery_time
483 rajveer 87
    if t_order.customer_id:    
88
        order.customer_id = t_order.customer_id
89
        order.customer_name = t_order.customer_name
860 rajveer 90
        order.customer_city = t_order.customer_city
91
        order.customer_state = t_order.customer_state
483 rajveer 92
        order.customer_mobilenumber = t_order.customer_mobilenumber
93
        order.customer_pincode = t_order.customer_pincode
738 chandransh 94
        order.customer_address1 = t_order.customer_address1
95
        order.customer_address2 = t_order.customer_address2
483 rajveer 96
        order.customer_email = t_order.customer_email
97
    #new status and status description to be added    
716 rajveer 98
    order.status = t_order.status  
483 rajveer 99
    order.statusDescription = t_order.statusDescription
100
    order.total_amount = t_order.total_amount
101
    order.total_weight = t_order.total_weight
102
    if t_order.created_timestamp:
103
        order.created_timestamp = to_py_date(t_order.created_timestamp)
104
    else:
685 chandransh 105
        order.created_timestamp = datetime.datetime.now()
483 rajveer 106
    return order
107
 
108
def create_transaction(t_transaction):
109
    t_orders = t_transaction.orders
110
    if not t_orders:
111
        raise TransactionServiceException(101, "Orders missing from the transaction")
112
    transaction = get_new_transaction()
113
    transaction.customer_id = t_transaction.customer_id 
114
    transaction.shopping_cart_id = t_transaction.shoppingCartid
2219 varun.gupt 115
    transaction.coupon_code = t_transaction.coupon_code
2815 vikas 116
    transaction.session_source = t_transaction.sessionSource
117
    transaction.session_start_time = to_py_date(t_transaction.sessionStartTime)
3858 vikas 118
    transaction.first_source = t_transaction.firstSource
119
    transaction.first_source_start_time = to_py_date(t_transaction.firstSourceTime)
483 rajveer 120
 
121
    for t_order in t_orders:
122
        order = create_order(t_order)
123
        order.transaction = transaction 
124
        for line_item in t_order.lineitems:
125
            litem = LineItem()
716 rajveer 126
            litem.item_id = line_item.item_id
963 chandransh 127
            litem.productGroup = line_item.productGroup
483 rajveer 128
            litem.brand = line_item.brand
129
            if line_item.model_number:
130
                litem.model_number = line_item.model_number
131
            if line_item.model_name:    
132
                litem.model_name = line_item.model_name
908 chandransh 133
            if line_item.color:
134
                litem.color = line_item.color
483 rajveer 135
            if line_item.extra_info:
136
                litem.extra_info = line_item.extra_info
137
            litem.quantity = line_item.quantity
138
            litem.unit_price = line_item.unit_price
139
            litem.unit_weight = line_item.unit_weight
140
            litem.total_price = line_item.total_price
141
            litem.total_weight = line_item.total_weight
4283 anupam.sin 142
            '''litem.transfer_price = line_item.transfer_price'''
4172 rajveer 143
            litem.dealText = line_item.dealText
4295 varun.gupt 144
            litem.warranty_expiry_timestamp = to_py_date(line_item.warrantry_expiry_timestamp)
1976 varun.gupt 145
 
483 rajveer 146
            """
147
            if line_item.addedOn:
148
                litem.added_on = to_py_date(line_item.addedOn)
149
            else:
150
                litem.added_on = datetime.datetime.now()
151
            """
152
            litem.order = order
153
    session.commit()
154
    return transaction.id
155
 
156
def get_transaction(transaction_id):
157
    transaction = Transaction.get_by(id=transaction_id)
158
    if not transaction:
159
        raise TransactionServiceException(108, "no such transaction")
160
 
161
    return transaction
162
 
163
def get_transactions_for_customer(customer_id, from_date, to_date, status):
164
    if not customer_id:
165
        raise TransactionServiceException(101, "bad customer id")
166
    query = Transaction.query.filter(Transaction.customer_id == customer_id)
1493 vikas 167
    if status is not None:
483 rajveer 168
        query = query.filter(Transaction.status == status)
169
    if from_date:
170
        query = query.filter(Transaction.createdOn >from_date)
171
    if to_date:
172
        query = query.filter(Transaction.createdOn < to_date)
173
    return query.all()
174
 
175
def get_transactions_for_shopping_cart_id(shopping_cart_id):
176
    transactions = Transaction.query.filter_by(shopping_cart_id=shopping_cart_id).all()
177
    return transactions
178
 
179
def get_transaction_status(transaction_id):
180
    if not transaction_id:
181
        raise TransactionServiceException(101, "bad transaction id")
182
 
183
    transaction = get_transaction(transaction_id)
184
 
185
    return transaction.status
186
 
187
def change_transaction_status(transaction_id, new_status, description):
188
    transaction = get_transaction(transaction_id)
189
    transaction.status = new_status
190
    transaction.status_message = description
690 chandransh 191
    if new_status == TransactionStatus.FAILED:
192
        for order in transaction.orders:
193
            order.status = OrderStatus.PAYMENT_FAILED
2339 chandransh 194
            order.statusDescription = "Payment Failed"
4247 rajveer 195
    elif new_status == TransactionStatus.AUTHORIZED or new_status == TransactionStatus.FLAGGED:
690 chandransh 196
        for order in transaction.orders:
4247 rajveer 197
            if new_status == TransactionStatus.AUTHORIZED:
198
                order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
199
                order.statusDescription = "Submitted to warehouse"
200
            elif new_status == TransactionStatus.FLAGGED:
201
                order.status = OrderStatus.PAYMENT_FLAGGED
202
                order.statusDescription = "Payment flagged by gateway"
3092 chandransh 203
            order.cod = False
743 rajveer 204
            #After we got payment success, we will set logistics info also 
205
            logistics_client = LogisticsClient().get_client()
206
            #FIXME line item is only one now. If multiple will come, need to fix.
871 chandransh 207
            item_id = order.lineitems[0].item_id
3044 chandransh 208
            logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.PREPAID)
743 rajveer 209
 
1730 ankur.sing 210
            current_time = datetime.datetime.now()
211
            logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
4009 chandransh 212
            logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
1730 ankur.sing 213
 
743 rajveer 214
            order.warehouse_id = logistics_info.warehouseId
215
            order.logistics_provider_id = logistics_info.providerId
216
            order.airwaybill_no = logistics_info.airway_billno
217
            order.tracking_id = order.airwaybill_no
4019 chandransh 218
            order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
4102 chandransh 219
            order.promised_shipping_time = order.expected_shipping_time
1730 ankur.sing 220
            order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
3986 chandransh 221
            order.promised_delivery_time = order.expected_delivery_time 
871 chandransh 222
 
3133 rajveer 223
            catalog_client = CatalogClient().get_client()
871 chandransh 224
            catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, order.lineitems[0].quantity)
1351 varun.gupt 225
 
4316 anupam.sin 226
            try:
227
                item_pricing = catalog_client.getItemPricing(item_id, -1)
228
                order.lineitems[0].transfer_price = item_pricing.transferPrice
229
            except:
230
                print "Not able to get transfer price. Skipping"
3064 chandransh 231
    elif new_status == TransactionStatus.COD_IN_PROCESS:
232
        for order in transaction.orders:
4662 rajveer 233
            order.status = OrderStatus.COD_VERIFICATION_PENDING
3064 chandransh 234
            order.statusDescription = "Verification Pending"
235
            order.cod = True
236
            #After we got payment success, we will set logistics info also 
237
            logistics_client = LogisticsClient().get_client()
238
            #FIXME line item is only one now. If multiple will come, need to fix.
239
            item_id = order.lineitems[0].item_id
240
            logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id, DeliveryType.COD)
241
 
242
            current_time = datetime.datetime.now()
243
            logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
4009 chandransh 244
            logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
3064 chandransh 245
 
246
            order.warehouse_id = logistics_info.warehouseId
247
            order.logistics_provider_id = logistics_info.providerId
248
            order.airwaybill_no = logistics_info.airway_billno
249
            order.tracking_id = order.airwaybill_no
4019 chandransh 250
            order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0)
4102 chandransh 251
            order.promised_shipping_time = order.expected_shipping_time
3064 chandransh 252
            order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
3986 chandransh 253
            order.promised_delivery_time = order.expected_delivery_time
3064 chandransh 254
 
3133 rajveer 255
            catalog_client = CatalogClient().get_client()
3064 chandransh 256
            catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, order.lineitems[0].quantity)
257
 
4316 anupam.sin 258
            try:
259
                item_pricing = catalog_client.getItemPricing(item_id, -1)
260
                order.lineitems[0].transfer_price = item_pricing.transferPrice        
261
            except:
262
                print "Not able to get transfer price. Skipping"
483 rajveer 263
    session.commit()
3499 mandeep.dh 264
 
3530 mandeep.dh 265
    try:
266
        if new_status == TransactionStatus.COD_IN_PROCESS:
4008 mandeep.dh 267
            transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
268
            transaction_requiring_extra_processing.category = 'COD_VERIFICATION'
269
            transaction_requiring_extra_processing.transaction_id = transaction_id
270
            session.commit()
4247 rajveer 271
        elif new_status == TransactionStatus.FLAGGED:
272
            transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
273
            transaction_requiring_extra_processing.category = 'PAYMENT_FLAGGED'
274
            transaction_requiring_extra_processing.transaction_id = transaction_id
275
            session.commit()
3530 mandeep.dh 276
    except Exception as e:
4065 mandeep.dh 277
        print "Error inserting transaction Id: " + str(transaction_id) + " due to " + str(e)
3499 mandeep.dh 278
 
483 rajveer 279
    return True
280
 
3994 chandransh 281
def __get_holidays(start_time=0L, to_time=-1L):
282
    '''
283
    Get the list of all public holidays since start time including the
284
    day of start time if it's a holiday
285
    '''
286
    midnightTime = start_time.replace(hour=0, minute=0, second=0, microsecond=0)
1730 ankur.sing 287
    fromDate = to_java_date(midnightTime)
288
    logistics_client = LogisticsClient().get_client()
3994 chandransh 289
    #TODO: This list should be cached after the first call.
290
    #Calling logistics server is also unnecessary.
291
    holidays = logistics_client.getHolidays(fromDate, to_time)
292
    holidays = [to_py_date(holiday).date() for holiday in holidays]
293
    return holidays
294
 
295
 
296
def adjust_delivery_time(start_time, delivery_days):
297
    '''
298
    Returns the actual no. of days which will pass while 'delivery_days'
299
    no. of business days will pass since 'order_time'. 
300
    '''
301
    start_date = start_time.date()
302
    end_date = start_date + datetime.timedelta(days = delivery_days)
303
    holidays = __get_holidays(start_time)
1730 ankur.sing 304
 
3994 chandransh 305
    while start_date <= end_date:
306
        if start_date.weekday() == 6 or start_date in holidays:
1730 ankur.sing 307
            delivery_days = delivery_days + 1
3994 chandransh 308
            end_date = end_date + datetime.timedelta(days = 1)
4088 chandransh 309
        start_date = start_date + datetime.timedelta(days = 1)
3994 chandransh 310
 
1730 ankur.sing 311
    return delivery_days
312
 
1398 varun.gupt 313
def enqueue_transaction_info_email(transaction_id):
1262 varun.gupt 314
    transaction = get_transaction(transaction_id)
315
 
316
    html_header = """
317
<html>
318
<body>
319
<div>
320
    <p>
321
        Hello,<br /><br />
1742 rajveer 322
        Thanks for placing order with us. Following are the details of your order:
1262 varun.gupt 323
    </p>
324
    <div>
325
        Order Date: $order_date
1569 varun.gupt 326
    </div>
1262 varun.gupt 327
    <div>
328
        <table>
1569 varun.gupt 329
        <tr><td colspan="5"><hr /></td></tr>
330
        <tr><td colspan="5" align="left"><b>Order Details</b></td></tr>
331
        <tr><td colspan="5"><hr /></td></tr>
1262 varun.gupt 332
        <tr>
2050 varun.gupt 333
            <th width="100">Order No.</th>
334
            <th>Product</th>
335
            <th width="100">Quantity</th>
336
            <th width="100">Unit Price</th>
337
            <th width="100">Amount</th>
1262 varun.gupt 338
        </tr>
339
    """
1398 varun.gupt 340
    user_email = ""
1262 varun.gupt 341
    total_amount = 0.0
342
    html_table = ""
1569 varun.gupt 343
    order_date = datetime.datetime.now()
1262 varun.gupt 344
 
345
    for order in transaction.orders:
346
        order_date = order.created_timestamp
1398 varun.gupt 347
        user_email = order.customer_email
1569 varun.gupt 348
        html_tr = "<tr><td align='center'>" + str(order.id) + "</td>"
1262 varun.gupt 349
 
350
        for lineitem in order.lineitems:
2050 varun.gupt 351
            lineitem_total_price = round(lineitem.total_price, 2)
352
 
1276 chandransh 353
            html_tr += "<td>" + str(lineitem) + "</td>"
2050 varun.gupt 354
            html_tr += "<td align='center'>" + ("%.0f" % lineitem.quantity) + "</td>"
355
            html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem.unit_price) + "</td>"
356
            html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem_total_price) + "</td></tr>"
357
            total_amount += lineitem_total_price
1262 varun.gupt 358
            html_table += html_tr
2050 varun.gupt 359
 
1262 varun.gupt 360
    html_footer = """
361
        <tr><td colspan=5>&nbsp;</td></tr>
1565 varun.gupt 362
        <tr><td colspan=5><hr /></td></tr>
1262 varun.gupt 363
        <tr>
364
            <td colspan=4>Total Amount</td>
2050 varun.gupt 365
            <td> Rs. $total_amount</td>
1262 varun.gupt 366
        </tr>
367
        </table>
1569 varun.gupt 368
    </div>
1262 varun.gupt 369
    <p>
370
        Best Wishes,<br />
371
        Saholic Team
372
    </p>
373
</div>
374
</body>
375
</html>
376
    """
1398 varun.gupt 377
 
1569 varun.gupt 378
    dt = datetime.datetime.strptime(str(order_date), "%Y-%m-%d %H:%M:%S")
1565 varun.gupt 379
    formated_order_date = dt.strftime("%A, %d. %B %Y %I:%M%p")
380
 
1262 varun.gupt 381
    email_header = Template(html_header).substitute(dict(order_date = formated_order_date))
2050 varun.gupt 382
    email_footer = Template(html_footer).substitute(dict(total_amount = "%.2f" % total_amount))
1262 varun.gupt 383
 
1351 varun.gupt 384
    try:
385
        helper_client = HelperClient().get_client()
1398 varun.gupt 386
        helper_client.saveUserEmailForSending(user_email, "", "Saholic - Order Details", email_header + html_table + email_footer, str(transaction_id), "TransactionInfo")
1351 varun.gupt 387
        return True
388
    except Exception as e:
389
        print e
390
        return False
1262 varun.gupt 391
 
483 rajveer 392
def get_order(order_id):
393
    order = Order.get_by(id=order_id)
394
    if not order:
395
        raise TransactionServiceException(108, "no such order")
396
 
397
    return order
398
 
3014 chandransh 399
def get_orders_for_customer(customer_id, from_date, to_date, statuses):
483 rajveer 400
    if not customer_id:
401
        raise TransactionServiceException(101, "bad customer id")
402
    query = Order.query.filter(Order.customer_id == customer_id)
3014 chandransh 403
    if statuses:
404
        query = query.filter(Order.status.in_(tuple(statuses)))
483 rajveer 405
    if from_date:
406
        query = query.filter(Order.created_timestamp >from_date)
407
    if to_date:
408
        query = query.filter(Order.created_timestamp < to_date)
409
    return query.all()
410
 
1528 ankur.sing 411
def get_order_for_customer(order_id, customer_id):
412
    if not customer_id:
413
        raise TransactionServiceException(101, "bad customer id")
414
    order = get_order(order_id)
415
    if order.customer_id != customer_id:
416
        raise TransactionServiceException(108, "order: " + str(order_id) + " does not belong to customer: " + str(customer_id))
417
    return order
418
 
483 rajveer 419
def get_all_orders(status, from_date, to_date, warehouse_id):
420
    if not warehouse_id:
421
        raise TransactionServiceException(101, "bad warehouse id")
422
    query = Order.query.filter(Order.warehouse_id == warehouse_id)
423
    if status:
424
        query = query.filter(Order.status == status)
425
    if from_date:
426
        query = query.filter(Order.created_timestamp >from_date)
427
    if to_date:
428
        query = query.filter(Order.created_timestamp < to_date)
429
    return query.all()
430
 
4133 chandransh 431
def get_orders_in_batch(statuses=[], offset=0, limit=0, warehouse_id=None):
4296 rajveer 432
    query = Order.query
433
    if warehouse_id:
434
        query = query.filter(Order.warehouse_id == warehouse_id)
4133 chandransh 435
    if statuses:
436
        query = query.filter(Order.status.in_(statuses))
437
    query = query.offset(offset)
438
    if limit:
439
        query = query.limit(limit)
440
    return query.all()
441
 
442
def get_order_count(statuses=[], warehouse_id=None):
443
    if not warehouse_id:
444
        raise TransactionServiceException(101, "bad warehouse id")
445
    query = session.query(func.count(Order.id)).filter(Order.warehouse_id == warehouse_id)
446
    if statuses:
447
        query = query.filter(Order.status.in_(statuses))
448
    return query.scalar()
449
 
1382 varun.gupt 450
def get_returnable_orders_for_customer(customer_id, limit = None):
451
    if not customer_id:
452
        raise TransactionServiceException(101, "bad customer id")
453
 
454
    query = Order.query.filter(Order.customer_id == customer_id)
455
    query = query.filter(Order.status != OrderStatus.PAYMENT_FAILED)
456
    query = query.filter(Order.status != OrderStatus.REJECTED)
457
    query = query.filter(Order.status > OrderStatus.SHIPPED_FROM_WH)
458
    query = query.filter(or_(Order.status < OrderStatus.DELIVERY_SUCCESS, Order.delivery_timestamp < datetime.datetime.now() + datetime.timedelta(hours = 48)))
459
    orders = query.all()
460
    order_ids = [order.id for order in orders]
461
    return order_ids
462
 
463
def get_cancellable_orders_for_customer(customer_id, limit = None):
464
    if not customer_id:
465
        raise TransactionServiceException(101, "bad customer id")
466
 
467
    query = Order.query.filter(Order.customer_id == customer_id)
4343 varun.gupt 468
    query = query.filter(Order.status != OrderStatus.PAYMENT_FAILED)
1382 varun.gupt 469
    query = query.filter(Order.status != OrderStatus.REJECTED)
4288 anupam.sin 470
    query = query.filter(Order.status <= OrderStatus.BILLED)
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]
678
                        scanItemString = " ".join([item.brand, item.modelName, item.modelNumber, item.color])
679
                        lineItemString = " ".join([lineitem.brand, lineitem.model_name, lineitem.model_number, lineitem.color])
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)
4720 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_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"
2536 chandransh 1040
        session.commit()
1041
        return True
1042
    except Exception as e:
1043
        print sys.exc_info()[0]
1044
        return False
1045
 
4602 rajveer 1046
def authorize_pickup(order_id, pickup_number, providerId):
2536 chandransh 1047
    order = get_order(order_id)
4495 rajveer 1048
    if order.status not in [OrderStatus.DOA_PICKUP_REQUEST_RAISED, OrderStatus.RET_PICKUP_REQUEST_RAISED]:
2536 chandransh 1049
        return False
1050
    from_user = 'help@shop2020.in'
1051
    from_pwd = '5h0p2o2o'
1052
    subject = 'Pickup details'
1053
    filename = "/tmp/" +  str(order.id) +".pdf" 
1054
    try:
3133 rajveer 1055
        catalog_client = CatalogClient().get_client()
2536 chandransh 1056
        warehouse = catalog_client.getWarehouse(order.warehouse_id)
1057
 
1058
        logistics_client = LogisticsClient().get_client()
4602 rajveer 1059
        provider = logistics_client.getProvider(providerId)
2536 chandransh 1060
        provider_name = provider.name
1061
 
1062
        to_addr = order.customer_email
1063
        today = date.today().strftime("%d-%B-%Y (%A)")
1064
 
1065
        raw_message = '''
1066
        Dear Mr. %(customer_name)s,
1067
 
4495 rajveer 1068
        In reference to the return of order ID %(order_id)d. We request you to kindly pack the box properly with bubble wrap, and send it along with the original "DOA" certificate (in case of DOA) and the invoice.
2536 chandransh 1069
        Take a printout of the attachment in this mail which contains the delivery address and paste it on the packed parcel.
1070
        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.
1071
 
1072
        Pickup will happen from the below mentioned address:-
1073
 
1074
        %(customer_address)s
1075
 
1076
        Do let us know in case of any concerns
1077
 
1078
        Thanks & Regards
1079
        Saholic Team
1080
        '''
1081
        message = dedent(raw_message) % {'customer_name' : order.customer_name,
1082
                                         'order_id' : order_id,
1083
                                         'provider_name' : provider_name,
1084
                                         'date': today,
1085
                                         'pickup_request_no' : pickup_number,
1086
                                         'customer_address' : __get_order_address(order)}
1087
        print message
1088
        __generate_return_advice(order, warehouse, provider, filename)
3527 chandransh 1089
        mail(from_user, from_pwd, [to_addr], subject, message, [get_attachment_part(filename)])
2536 chandransh 1090
        order.pickupRequestNo = pickup_number
4495 rajveer 1091
        if order.status == OrderStatus.RET_PICKUP_REQUEST_RAISED:
1092
            order.status = OrderStatus.RET_PICKUP_CONFIRMED
1093
            order.statusDescription = "RETURN pick up confirmed"
1094
        else:
1095
            order.status = OrderStatus.DOA_PICKUP_CONFIRMED
1096
            order.statusDescription = "DOA pick up confirmed"
2764 chandransh 1097
        order.doa_auth_timestamp = datetime.datetime.now()
2536 chandransh 1098
        session.commit()
1099
        return True
1100
    except Exception as e:
4234 rajveer 1101
        print sys.exc_info()
4506 phani.kuma 1102
        traceback.print_tb(sys.exc_info()[2])
2536 chandransh 1103
        return False
1104
    finally:
1105
        if os.path.exists(filename):
1106
            os.remove(filename)
1107
 
2764 chandransh 1108
def mark_doas_as_picked_up(provider_id, pickup_details):
1109
    for pickupNo, doa_pickup_timestamp in pickup_details.iteritems():
1110
        order = Order.query.filter_by(pickupRequestNo=pickupNo, logistics_provider_id = provider_id).first()
4452 rajveer 1111
        if order == None or order.status != OrderStatus.DOA_PICKUP_CONFIRMED:
2764 chandransh 1112
            #raise TransactionServiceException(102, "No order found for the awb: " + awb)
1113
            continue
1114
        order.status = OrderStatus.DOA_RETURN_IN_TRANSIT
1115
        order.statusDescription = "DOA picked up by Courier Company"
1116
        order.doa_pickup_timestamp = doa_pickup_timestamp
1117
    session.commit()
1118
 
1119
    current_time = datetime.datetime.now()
1120
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
4741 phani.kuma 1121
    orders_not_picked_up = Order.query.filter_by(logistics_provider_id = provider_id).filter_by(status=OrderStatus.DOA_PICKUP_CONFIRMED).filter(Order.doa_auth_timestamp <= to_datetime).all()
2764 chandransh 1122
    return orders_not_picked_up
1123
 
4741 phani.kuma 1124
def mark_return_orders_as_picked_up(provider_id, pickup_details):
1125
    for pickupNo, return_order_pickup_timestamp in pickup_details.iteritems():
1126
        order = Order.query.filter_by(pickupRequestNo=pickupNo, logistics_provider_id = provider_id).first()
1127
        if order == None or order.status != OrderStatus.RET_PICKUP_CONFIRMED:
1128
            #raise TransactionServiceException(102, "No order found for the awb: " + awb)
1129
            continue
1130
        order.status = OrderStatus.RET_RETURN_IN_TRANSIT
1131
        order.statusDescription = "Return order picked up by Courier Company"
1132
        order.doa_pickup_timestamp = return_order_pickup_timestamp
1133
    session.commit()
1134
 
1135
    current_time = datetime.datetime.now()
1136
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
1137
    orders_not_picked_up = Order.query.filter_by(logistics_provider_id = provider_id).filter_by(status=OrderStatus.RET_PICKUP_CONFIRMED).filter(Order.doa_auth_timestamp <= to_datetime).all()
1138
    return orders_not_picked_up
1139
 
4479 rajveer 1140
def receive_return(order_id, receiveCondition):
2591 chandransh 1141
    order = get_order(order_id)
4365 mandeep.dh 1142
 
1143
    # FIXME: For Mahipal warehouse, we need to scan in items for every sales return
4576 mandeep.dh 1144
    if order.warehouse_id == 7:
4365 mandeep.dh 1145
        try:
1146
            warehouse_client = WarehouseClient().get_client()
1147
            lineitem = order.lineitems[0]
1148
 
1149
            # Inventory should not be updated for DOA returns 
4484 rajveer 1150
            if order.status == OrderStatus.RTO_IN_TRANSIT:
4658 mandeep.dh 1151
                warehouse_client.scanSerializedItemForOrder(lineitem.serial_number, ScanType.SALE_RET, order.id, order.warehouse_id)
4555 mandeep.dh 1152
                # In case of RTOs increasing availibility only if we receive item in prestine state
1153
                if not receiveCondition:
1154
                    catalog_client = CatalogClient().get_client()
1155
                    catalog_client.addInventory(lineitem.item_id, order.warehouse_id, lineitem.quantity)
4452 rajveer 1156
            elif order.status in [OrderStatus.DOA_PICKUP_CONFIRMED, OrderStatus.DOA_RETURN_IN_TRANSIT]:
4658 mandeep.dh 1157
                warehouse_client.scanSerializedItemForOrder(lineitem.serial_number, ScanType.DOA_IN, order.id, order.warehouse_id)
4365 mandeep.dh 1158
        except:
1159
            print sys.exc_info()[0]
1160
            print 'Could not scan in sales return orders'
1161
 
4452 rajveer 1162
    if order.status in [OrderStatus.DOA_PICKUP_CONFIRMED, OrderStatus.DOA_RETURN_IN_TRANSIT]:
4479 rajveer 1163
        if receiveCondition == 0:
1164
            order.status = OrderStatus.DOA_RECEIVED_PRESTINE
1165
            order.statusDescription = "DOA package received"
1166
        elif receiveCondition == 1:
1167
            order.status = OrderStatus.DOA_RECEIVED_DAMAGED
1168
            order.statusDescription = "DOA received damaged"
1169
        elif receiveCondition == 2:
1170
            order.status = OrderStatus.DOA_LOST_IN_TRANSIT
1171
            order.statusDescription = "DOA lost in transit"
2764 chandransh 1172
        order.received_return_timestamp = datetime.datetime.now()
2616 chandransh 1173
        session.commit()
4495 rajveer 1174
    elif order.status in [OrderStatus.RET_PICKUP_CONFIRMED, OrderStatus.RET_RETURN_IN_TRANSIT]:
1175
        if receiveCondition == 0:
1176
            order.status = OrderStatus.RET_RECEIVED_PRESTINE
1177
            order.statusDescription = "RETURN package received"
1178
        elif receiveCondition == 1:
1179
            order.status = OrderStatus.RET_RECEIVED_DAMAGED
1180
            order.statusDescription = "RETURN received damaged"
1181
        elif receiveCondition == 2:
1182
            order.status = OrderStatus.RET_LOST_IN_TRANSIT
1183
            order.statusDescription = "RETURN lost in transit"
1184
        order.received_return_timestamp = datetime.datetime.now()
1185
        session.commit()
4484 rajveer 1186
    elif order.status == OrderStatus.RTO_IN_TRANSIT :
4486 rajveer 1187
        if receiveCondition == 0:
1188
            order.status = OrderStatus.RTO_RECEIVED_PRESTINE
4515 mandeep.dh 1189
            order.statusDescription = "Returned to origin"
4486 rajveer 1190
        elif receiveCondition == 1:
1191
            order.status = OrderStatus.RTO_RECEIVED_DAMAGED
1192
            order.statusDescription = "RTO received damaged"
1193
        elif receiveCondition == 2:
1194
            order.status = OrderStatus.RTO_LOST_IN_TRANSIT
1195
            order.statusDescription = "RTO lost in transit"
2764 chandransh 1196
        order.received_return_timestamp = datetime.datetime.now()
2616 chandransh 1197
        session.commit()
1198
    else:
2591 chandransh 1199
        return False
1200
    return True
1201
 
1202
def validate_doa(order_id, is_valid):
1203
    order = get_order(order_id)
4452 rajveer 1204
    if order.status != OrderStatus.DOA_RECEIVED_PRESTINE:
2591 chandransh 1205
        return False
1206
    if is_valid:
2609 chandransh 1207
        order.status = OrderStatus.DOA_CERT_VALID
1208
        order.statusDescription = "DOA Certificate Valid"
2591 chandransh 1209
    else:
1210
        order.status = OrderStatus.DOA_CERT_INVALID
1211
        order.statusDescription = "DOA Certificate Invalid"
3578 mandeep.dh 1212
        update_trust_level(order)
2591 chandransh 1213
    session.commit()
1214
    return True
1215
 
4495 rajveer 1216
def validate_return_product(order_id, is_usable):
1217
    order = get_order(order_id)
1218
    if order.status != OrderStatus.RET_RECEIVED_PRESTINE:
1219
        return False
1220
    if is_usable:
1221
        order.status = OrderStatus.RET_PRODUCT_USABLE
1222
        order.statusDescription = "Return product usable"
4555 mandeep.dh 1223
        lineitem = order.lineitems[0]
4576 mandeep.dh 1224
        if order.warehouse_id == 7:
4555 mandeep.dh 1225
            catalog_client = CatalogClient().get_client()
1226
            catalog_client.addInventory(lineitem.item_id, order.warehouse_id, lineitem.quantity)
4495 rajveer 1227
    else:
1228
        order.status = OrderStatus.RET_PRODUCT_UNUSABLE
1229
        order.statusDescription = "Return product unusable"
1230
        update_trust_level(order)
1231
    session.commit()
1232
    return True
1233
 
2628 chandransh 1234
def reship_order(order_id):
1235
    """
4484 rajveer 1236
    If the order is in RTO_RECEIVED_PRESTINE or DOA_CERT_INVALID state, it does the following:
2628 chandransh 1237
        1. Creates a new order for processing in the BILLED state. All billing information is saved.
4484 rajveer 1238
        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 1239
 
1240
    If the order is in DOA_CERT_VALID state, it does the following:
1241
        1. Creates a new order for processing in the SUBMITTED_FOR_PROCESSING state.
1242
        2. Creates a return order for the warehouse executive to return the DOA material.
4452 rajveer 1243
        3. Marks the current order as the final DOA_VALID_RESHIPPED state.
2628 chandransh 1244
 
1245
    Returns the id of the newly created order.
1246
 
1247
    Throws an exception if the order with the given id couldn't be found.
1248
 
1249
    Parameters:
1250
     - orderId
1251
    """
1252
    order = get_order(order_id)
4484 rajveer 1253
    if order.status == OrderStatus.RTO_RECEIVED_PRESTINE:
4220 rajveer 1254
        new_order = __clone_order(order, True, order.cod)
4484 rajveer 1255
        order.status = OrderStatus.RTO_RESHIPPED
2628 chandransh 1256
        order.statusDescription = "Order Reshipped"
1257
        session.commit()
3578 mandeep.dh 1258
        update_trust_level(order)
4486 rajveer 1259
    elif order.status == OrderStatus.RTO_RECEIVED_DAMAGED:
1260
        new_order = __clone_order(order, False, order.cod)
1261
        ret_order = __create_return_order(order)
1262
        order.status = OrderStatus.RTO_DAMAGED_RESHIPPED
1263
        order.statusDescription = "Order Reshipped Received Damaged"
1264
        session.commit()
1265
    elif order.status == OrderStatus.RTO_LOST_IN_TRANSIT:
1266
        new_order = __clone_order(order, False, order.cod)
1267
        order.status = OrderStatus.RTO_LOST_IN_TRANSIT_RESHIPPED
1268
        order.statusDescription = "Order Reshipped Lost In Transit"
1269
        session.commit()
4662 rajveer 1270
    elif order.status == OrderStatus.LOST_IN_TRANSIT:
1271
        new_order = __clone_order(order, False, order.cod)
1272
        order.status = OrderStatus.LOST_IN_TRANSIT_RESHIPPED
1273
        order.statusDescription = "Order Reshipped Lost In Transit"
1274
        session.commit()
2628 chandransh 1275
    elif order.status == OrderStatus.DOA_CERT_INVALID:
4220 rajveer 1276
        new_order = __clone_order(order, True, False)
2628 chandransh 1277
        order.status = OrderStatus.DOA_INVALID_RESHIPPED
1278
        order.statusDescription = "Order Reshipped"
1279
        session.commit()
1280
    elif order.status == OrderStatus.DOA_CERT_VALID:
4220 rajveer 1281
        new_order = __clone_order(order, False, False)
2628 chandransh 1282
        ret_order = __create_return_order(order)
4452 rajveer 1283
        order.status = OrderStatus.DOA_VALID_RESHIPPED
2628 chandransh 1284
        order.statusDescription = "Order Reshipped"
1285
        session.commit()
4479 rajveer 1286
    elif order.status == OrderStatus.DOA_RECEIVED_DAMAGED:
1287
        new_order = __clone_order(order, False, False)
1288
        ret_order = __create_return_order(order)
1289
        order.status = OrderStatus.DOA_RESHIPPED_RCVD_DAMAGED
1290
        order.statusDescription = "Order Reshipped Received Damaged"
1291
        session.commit()
1292
    elif order.status == OrderStatus.DOA_LOST_IN_TRANSIT:
1293
        new_order = __clone_order(order, False, False)
1294
        order.status = OrderStatus.DOA_RESHIPPED_LOST_IN_TRANSIT
1295
        order.statusDescription = "Order Reshipped Lost In Transit"
1296
        session.commit()
4614 rajveer 1297
    elif order.status == OrderStatus.RET_PRODUCT_USABLE:
4495 rajveer 1298
        new_order = __clone_order(order, True, False)
4614 rajveer 1299
        order.status = OrderStatus.RET_PRODUCT_USABLE_RESHIPPED
4495 rajveer 1300
        order.statusDescription = "Order Reshipped"
1301
        session.commit()
4614 rajveer 1302
    elif order.status == OrderStatus.RET_PRODUCT_UNUSABLE:
4495 rajveer 1303
        new_order = __clone_order(order, False, False)
1304
        ret_order = __create_return_order(order)
4614 rajveer 1305
        order.status = OrderStatus.RET_PRODUCT_UNUSABLE_RESHIPPED
4495 rajveer 1306
        order.statusDescription = "Order Reshipped"
1307
        session.commit()
1308
    elif order.status == OrderStatus.RET_RECEIVED_DAMAGED:
1309
        new_order = __clone_order(order, False, False)
1310
        ret_order = __create_return_order(order)
1311
        order.status = OrderStatus.RET_RESHIPPED_RCVD_DAMAGED
1312
        order.statusDescription = "Order Reshipped Received Damaged"
1313
        session.commit()
1314
    elif order.status == OrderStatus.RET_LOST_IN_TRANSIT:
1315
        new_order = __clone_order(order, False, False)
1316
        order.status = OrderStatus.RET_RESHIPPED_LOST_IN_TRANSIT
1317
        order.statusDescription = "Order Reshipped Lost In Transit"
1318
        session.commit()
2628 chandransh 1319
    else:
1320
        raise TransactionServiceException(112, "This order can't be reshipped")
1321
 
2764 chandransh 1322
    order.reship_timestamp = datetime.datetime.now()
2628 chandransh 1323
    order.new_order_id = new_order.id
1324
    session.commit()
1325
 
1326
    return new_order.id
1327
 
3226 chandransh 1328
def refund_order(order_id, refunded_by, reason):
2628 chandransh 1329
    """
4484 rajveer 1330
    If the order is in RTO_RECEIVED_PRESTINE, DOA_CERT_VALID or DOA_CERT_INVALID state, it does the following:
2628 chandransh 1331
        1. Creates a refund request for batch processing.
1332
        2. Creates a return order for the warehouse executive to return the shipped material.
4484 rajveer 1333
        3. Marks the current order as RTO_REFUNDED, DOA_VALID_REFUNDED or DOA_INVALID_REFUNDED final states.
2628 chandransh 1334
 
1335
    If the order is in SUBMITTED_FOR_PROCESSING or INVENTORY_LOW state, it does the following:
1336
        1. Creates a refund request for batch processing.
3226 chandransh 1337
        2. Cancels the reservation of the item in the warehouse.
1338
        3. Marks the current order as the REFUNDED final state.
2628 chandransh 1339
 
3226 chandransh 1340
    For all COD orders, if the order is in INIT, SUBMITTED_FOR_PROCESSING or INVENTORY_LOW state, it does the following:
1341
        1. Cancels the reservation of the item in the warehouse.
1342
        2. Marks the current order as CANCELED.
1343
 
1344
    In all cases, it updates the reason for cancellation or refund and the person who performed the action.
1345
 
1346
    Returns True if it is successful, False otherwise.
2628 chandransh 1347
 
1348
    Throws an exception if the order with the given id couldn't be found.
1349
 
1350
    Parameters:
3226 chandransh 1351
     - order_id
1352
     - refunded_by
1353
     - reason
2628 chandransh 1354
    """
3414 chandransh 1355
    logging.info("Refunding order id: " + str(order_id))
2628 chandransh 1356
    order = get_order(order_id)
3186 chandransh 1357
 
1358
 
3366 chandransh 1359
 
3226 chandransh 1360
    if order.cod:
3414 chandransh 1361
        logging.info("Refunding COD order with status " + str(order.status))
4662 rajveer 1362
        status_transition = { OrderStatus.LOST_IN_TRANSIT : OrderStatus.LOST_IN_TRANSIT_REFUNDED,
1363
                     OrderStatus.RTO_RECEIVED_PRESTINE : OrderStatus.RTO_REFUNDED,
4486 rajveer 1364
                     OrderStatus.RTO_RECEIVED_DAMAGED : OrderStatus.RTO_DAMAGED_REFUNDED,
1365
                     OrderStatus.RTO_LOST_IN_TRANSIT : OrderStatus.RTO_LOST_IN_TRANSIT_REFUNDED,
3366 chandransh 1366
                     OrderStatus.DOA_CERT_INVALID : OrderStatus.DOA_INVALID_REFUNDED,
1367
                     OrderStatus.DOA_CERT_VALID : OrderStatus.DOA_VALID_REFUNDED,
4479 rajveer 1368
                     OrderStatus.DOA_RECEIVED_DAMAGED : OrderStatus.DOA_REFUNDED_RCVD_DAMAGED,
1369
                     OrderStatus.DOA_LOST_IN_TRANSIT : OrderStatus.DOA_REFUNDED_LOST_IN_TRANSIT,
4495 rajveer 1370
                     OrderStatus.RET_PRODUCT_UNUSABLE : OrderStatus.RET_PRODUCT_UNUSABLE_REFUNDED,
1371
                     OrderStatus.RET_PRODUCT_USABLE : OrderStatus.RET_PRODUCT_USABLE_REFUNDED,
1372
                     OrderStatus.RET_RECEIVED_DAMAGED : OrderStatus.RET_REFUNDED_RCVD_DAMAGED,
1373
                     OrderStatus.RET_LOST_IN_TRANSIT : OrderStatus.RET_REFUNDED_LOST_IN_TRANSIT,
4682 rajveer 1374
                     OrderStatus.COD_VERIFICATION_PENDING : OrderStatus.COD_VERIFICATION_FAILED,
1375
                     OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1376
                     OrderStatus.INVENTORY_LOW : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1377
                     OrderStatus.LOW_INV_PO_RAISED : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1378
                     OrderStatus.LOW_INV_REVERSAL_IN_PROCESS : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1379
                     OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1380
                     OrderStatus.ACCEPTED : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1381
                     OrderStatus.BILLED : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
4247 rajveer 1382
                     OrderStatus.CANCEL_REQUEST_CONFIRMED : OrderStatus.CANCELLED_ON_CUSTOMER_REQUEST
3366 chandransh 1383
                     }
1384
        if order.status not in status_transition.keys():
1385
            raise TransactionServiceException(114, "This order can't be refunded")
1386
 
3413 chandransh 1387
        if order.status < OrderStatus.BILLED:
3226 chandransh 1388
            __update_inventory_reservation(order)
1389
            order.statusDescription = "Order Cancelled"
3415 chandransh 1390
        elif order.status == OrderStatus.BILLED:
3413 chandransh 1391
            __create_return_order(order)
1392
            order.statusDescription = "Order Cancelled"
4486 rajveer 1393
        elif order.status in [OrderStatus.RTO_RECEIVED_PRESTINE, OrderStatus.RTO_RECEIVED_DAMAGED, OrderStatus.RTO_LOST_IN_TRANSIT]:
3226 chandransh 1394
            __create_return_order(order)
4486 rajveer 1395
            order.statusDescription = "RTO Refunded"
4662 rajveer 1396
        elif order.status in [OrderStatus.LOST_IN_TRANSIT]:
1397
            __create_return_order(order)
1398
            order.statusDescription = "Lost in Transit Refunded"
4479 rajveer 1399
        elif order.status in [OrderStatus.DOA_CERT_INVALID, OrderStatus.DOA_CERT_VALID, OrderStatus.DOA_RECEIVED_DAMAGED, OrderStatus.DOA_LOST_IN_TRANSIT] :
4078 chandransh 1400
            __create_return_order(order)
3226 chandransh 1401
            __create_refund(order)
4495 rajveer 1402
            order.statusDescription = "DOA Refunded"
1403
        elif order.status in [OrderStatus.RET_PRODUCT_UNUSABLE, OrderStatus.RET_PRODUCT_USABLE, OrderStatus.RET_RECEIVED_DAMAGED, OrderStatus.RET_LOST_IN_TRANSIT] :
1404
            __create_return_order(order)
1405
            __create_refund(order)
1406
            order.statusDescription = "Return Refunded"
4247 rajveer 1407
        elif order.status == OrderStatus.CANCEL_REQUEST_CONFIRMED:
1408
            if order.previousStatus  < OrderStatus.BILLED:
1409
                __update_inventory_reservation(order)
1410
                order.statusDescription = "Order Cancelled on customer request"
1411
            elif order.previousStatus == OrderStatus.BILLED:
1412
                __create_return_order(order)
1413
                order.statusDescription = "Order Cancelled on customer request"
3226 chandransh 1414
    else:
4662 rajveer 1415
        status_transition = {OrderStatus.LOST_IN_TRANSIT : OrderStatus.LOST_IN_TRANSIT_REFUNDED,
1416
                     OrderStatus.RTO_RECEIVED_PRESTINE : OrderStatus.RTO_REFUNDED,
4486 rajveer 1417
                     OrderStatus.RTO_RECEIVED_DAMAGED : OrderStatus.RTO_DAMAGED_REFUNDED,
1418
                     OrderStatus.RTO_LOST_IN_TRANSIT : OrderStatus.RTO_LOST_IN_TRANSIT_REFUNDED,
3366 chandransh 1419
                     OrderStatus.DOA_CERT_INVALID : OrderStatus.DOA_INVALID_REFUNDED,
1420
                     OrderStatus.DOA_CERT_VALID : OrderStatus.DOA_VALID_REFUNDED,
4479 rajveer 1421
                     OrderStatus.DOA_RECEIVED_DAMAGED : OrderStatus.DOA_REFUNDED_RCVD_DAMAGED,
1422
                     OrderStatus.DOA_LOST_IN_TRANSIT : OrderStatus.DOA_REFUNDED_LOST_IN_TRANSIT,
4495 rajveer 1423
                     OrderStatus.RET_PRODUCT_UNUSABLE : OrderStatus.RET_PRODUCT_UNUSABLE_REFUNDED,
1424
                     OrderStatus.RET_PRODUCT_USABLE : OrderStatus.RET_PRODUCT_USABLE_REFUNDED,
1425
                     OrderStatus.RET_RECEIVED_DAMAGED : OrderStatus.RET_REFUNDED_RCVD_DAMAGED,
1426
                     OrderStatus.RET_LOST_IN_TRANSIT : OrderStatus.RET_REFUNDED_LOST_IN_TRANSIT,
4682 rajveer 1427
                     OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1428
                     OrderStatus.INVENTORY_LOW : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1429
                     OrderStatus.LOW_INV_PO_RAISED : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1430
                     OrderStatus.LOW_INV_REVERSAL_IN_PROCESS : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1431
                     OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1432
                     OrderStatus.ACCEPTED : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
1433
                     OrderStatus.BILLED : OrderStatus.CANCELLED_DUE_TO_LOW_INVENTORY,
4247 rajveer 1434
                     OrderStatus.CANCEL_REQUEST_CONFIRMED : OrderStatus.CANCELLED_ON_CUSTOMER_REQUEST,
1435
                     OrderStatus.PAYMENT_FLAGGED : OrderStatus.PAYMENT_FLAGGED_DENIED
3366 chandransh 1436
                     }
1437
        if order.status not in status_transition.keys():
1438
            raise TransactionServiceException(114, "This order can't be refunded")
1439
 
4495 rajveer 1440
        if order.status in [OrderStatus.RTO_RECEIVED_PRESTINE, OrderStatus.RTO_RECEIVED_DAMAGED, OrderStatus.RTO_LOST_IN_TRANSIT] :
3226 chandransh 1441
            __create_return_order(order)
1442
            __create_refund(order)
4495 rajveer 1443
            order.statusDescription = "RTO Refunded"
4662 rajveer 1444
        elif order.status in [OrderStatus.LOST_IN_TRANSIT]:
1445
            __create_return_order(order)
1446
            __create_refund(order)
1447
            order.statusDescription = "Lost in Transit Refunded"
4495 rajveer 1448
        elif order.status in [OrderStatus.DOA_CERT_INVALID, OrderStatus.DOA_CERT_VALID, OrderStatus.DOA_RECEIVED_DAMAGED, OrderStatus.DOA_LOST_IN_TRANSIT] :
1449
            __create_return_order(order)
1450
            __create_refund(order)
1451
            order.statusDescription = "DOA Refunded"
1452
        elif order.status in [OrderStatus.RET_PRODUCT_UNUSABLE, OrderStatus.RET_PRODUCT_USABLE, OrderStatus.RET_RECEIVED_DAMAGED, OrderStatus.RET_LOST_IN_TRANSIT] :
1453
            __create_return_order(order)
1454
            __create_refund(order)
1455
            order.statusDescription = "Return Refunded"
4306 rajveer 1456
        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]:
3191 chandransh 1457
            __create_cancellation(order)
3226 chandransh 1458
            __update_inventory_reservation(order)
1459
            order.statusDescription = "Order Refunded"
4247 rajveer 1460
        elif order.status == OrderStatus.CANCEL_REQUEST_CONFIRMED:
1461
            if order.previousStatus  < OrderStatus.BILLED:
4672 rajveer 1462
                if order.previousStatus == OrderStatus.ACCEPTED:
1463
                    __create_refund(order)
4247 rajveer 1464
                __update_inventory_reservation(order)
1465
                order.statusDescription = "Order Cancelled on customer request"
1466
            elif order.previousStatus == OrderStatus.BILLED:
1467
                __create_return_order(order)
1468
                __create_refund(order)
1469
                order.statusDescription = "Order Cancelled on customer request"
1470
        elif order.status == OrderStatus.PAYMENT_FLAGGED:
1471
            __update_inventory_reservation(order)
1472
            order.statusDescription = "Order Cancelled due to payment flagged"
3195 chandransh 1473
 
1474
    order.status = status_transition[order.status]
1475
    order.refund_timestamp = datetime.datetime.now()
3226 chandransh 1476
    order.refunded_by = refunded_by
1477
    order.refund_reason = reason
2628 chandransh 1478
 
1479
    session.commit()
1480
    return True
1481
 
3186 chandransh 1482
def __update_inventory_reservation(order):
3191 chandransh 1483
    '''
1484
    Reduce the reservation count for all line items of the given order.
1485
    '''
3186 chandransh 1486
    try:
1487
        catalog_client = CatalogClient().get_client()
1488
        for lineitem in order.lineitems:
1489
            catalog_client.reduceReservationCount(lineitem.item_id, order.warehouse_id, lineitem.quantity);
1490
    except:
1491
        print "Unable to reduce reservation count"
1492
 
4220 rajveer 1493
def __clone_order(order, should_copy_billing_info, is_cod):
2628 chandransh 1494
    current_time = datetime.datetime.now()
1495
    new_order = Order()
1496
    new_order.customer_id = order.customer_id
1497
    new_order.customer_name = order.customer_name
1498
    new_order.customer_city = order.customer_city
1499
    new_order.customer_state = order.customer_state
1500
    new_order.customer_mobilenumber = order.customer_mobilenumber
1501
    new_order.customer_pincode = order.customer_pincode
1502
    new_order.customer_address1 = order.customer_address1
1503
    new_order.customer_address2 = order.customer_address2
1504
    new_order.customer_email = order.customer_email
1505
    new_order.total_amount = order.total_amount
1506
    new_order.total_weight = order.total_weight
1507
    new_order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
1508
    new_order.statusDescription = 'Submitted for Processing'
1509
    new_order.created_timestamp = current_time
1510
 
1511
    new_order.transaction = order.transaction
4220 rajveer 1512
    new_order.cod = is_cod
2628 chandransh 1513
 
1514
    for line_item in order.lineitems:
1515
        litem = LineItem()
1516
        litem.item_id = line_item.item_id
1517
        litem.productGroup = line_item.productGroup
1518
        litem.brand = line_item.brand
1519
        if line_item.model_number:
1520
            litem.model_number = line_item.model_number
1521
        if line_item.model_name:    
1522
            litem.model_name = line_item.model_name
1523
        if line_item.color:
1524
            litem.color = line_item.color
1525
        if line_item.extra_info:
1526
            litem.extra_info = line_item.extra_info
1527
        litem.quantity = line_item.quantity
1528
        litem.unit_price = line_item.unit_price
1529
        litem.unit_weight = line_item.unit_weight
1530
        litem.total_price = line_item.total_price
1531
        litem.total_weight = line_item.total_weight
1532
        litem.transfer_price = line_item.transfer_price
1533
        litem.order = new_order
1534
 
1535
    logistics_client = LogisticsClient().get_client()
1536
    item_id = new_order.lineitems[0].item_id
4479 rajveer 1537
    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 1538
    logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
1539
 
1540
    new_order.warehouse_id = logistics_info.warehouseId
1541
    new_order.logistics_provider_id = logistics_info.providerId
1542
    new_order.airwaybill_no = logistics_info.airway_billno
1543
    new_order.tracking_id = new_order.airwaybill_no
1544
    new_order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
3986 chandransh 1545
    new_order.promised_delivery_time = new_order.expected_delivery_time
4299 rajveer 1546
 
2628 chandransh 1547
    if should_copy_billing_info:
1548
        new_order.invoice_number = order.invoice_number
1549
        new_order.billed_by = order.billed_by
1550
        new_order.warehouse_id = order.warehouse_id
1551
        new_order.accepted_timestamp = current_time
1552
        new_order.billing_timestamp = current_time    
1553
        new_order.status = OrderStatus.BILLED
1554
        new_order.statusDescription = 'Order Billed'
1555
    else:       
3133 rajveer 1556
        catalog_client = CatalogClient().get_client()
2628 chandransh 1557
        catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, new_order.lineitems[0].quantity)
1558
 
1559
    return new_order
1560
 
1561
def __create_return_order(order):
2687 chandransh 1562
    ret_order = ReturnOrder(order)
2628 chandransh 1563
    return ret_order
1564
 
1565
def __create_refund(order):
4078 chandransh 1566
    payment_client = PaymentClient().get_client()
1567
    payment_client.createRefund(order.id, order.transaction.id, order.total_amount)
2747 chandransh 1568
    return
2628 chandransh 1569
 
3035 chandransh 1570
def __create_cancellation(order):
1571
    return
1572
 
2536 chandransh 1573
def __get_order_address(order):
1574
    address = order.customer_name + "\n" + order.customer_address1 + "\n"
1575
    if order.customer_address2:
1576
        address = address + order.customer_address2 + "\n"
1577
    address = address + order.customer_city + "\n"
1578
    address = address + order.customer_state + "\n"
1579
    address = address + "PIN " + order.customer_pincode + "\n"
1580
    address = address + "Phone: " + order.customer_mobilenumber
1581
    return address
1582
 
1583
def __generate_return_advice(order, warehouse, provider, filename):    
1584
    pdf = Canvas(filename)
1585
    pdf.setFont('Times-Bold',16)
1586
    address_text = pdf.beginText(inch, PAGE_HEIGHT - inch)
1587
    address_text.textLine("To")
1588
    address_text.textLine("Parmod Kumar")
1589
    for line in warehouse.location.split("\n"):
1590
        address_text.textLine(line)
1591
    address_text.textLine("PIN " + warehouse.pincode)
1592
    address_text.textLine("")
1593
    address_text.textLine("Phone: 9971573026")
1594
    pdf.drawText(address_text)
1595
 
1596
    pdf.setFont('Times-Roman',12)
1597
    order_text = pdf.beginText(inch, PAGE_HEIGHT - 4 * inch)
3051 chandransh 1598
    order_text.textLine("Pickup CODE: " + provider.details[DeliveryType.PREPAID].accountNo)
2536 chandransh 1599
    lineitem = order.lineitems[0] 
4234 rajveer 1600
    order_text.textLine("Content : " + lineitem.brand + " " + str(lineitem.model_number) + " " + str(lineitem.color))
2536 chandransh 1601
    order_text.textLine("Declared Value: Rs. " + str(order.total_amount))
1602
    order_text.textLine("Order ID : " + str(order.id))
1603
    pdf.drawText(order_text)
1604
    pdf.showPage()
1605
    pdf.save()
1606
    return filename
1607
 
2697 chandransh 1608
def get_return_orders(warehouse_id, from_date, to_date):
1609
    query = ReturnOrder.query.filter_by(processedStatus = False)
1610
    if warehouse_id != -1:
1611
        query = query.filter_by(warehouseId = warehouse_id)
1612
    if from_date:
4118 chandransh 1613
        query = query.filter(ReturnOrder.createdAt >from_date)
2697 chandransh 1614
    if to_date:
4118 chandransh 1615
        query = query.filter(ReturnOrder.createdAt < to_date)
2697 chandransh 1616
    return [return_order.to_thrift_object() for return_order in query.all()]
1617
 
2700 chandransh 1618
def get_return_order(id):
1619
    ret_order = ReturnOrder.get_by(orderId = id)
1620
    if ret_order is None:
1621
        raise TransactionServiceException(116, "No return order found for the given order id")
1622
    return ret_order.to_thrift_object()
1623
 
2697 chandransh 1624
def process_return(ret_order_id):
1625
    ret_order = ReturnOrder.get_by(orderId = ret_order_id)
1626
    if ret_order is None:
1627
        raise TransactionServiceException(116, "No return order found for the given order id")
1628
    ret_order.processedStatus = True
2701 chandransh 1629
    ret_order.processedAt = datetime.datetime.now()
2697 chandransh 1630
    session.commit()
1631
 
4757 mandeep.dh 1632
def update_orders_as_PORaised(itemIdQuantityMap, purchaseOrderId, warehouseId):
1633
    """
1634
    Updates orders as PO raised. Also updates purchase order id in orders. Pass a map of items mapped to
1635
    the quantities for which the PO is raised.
1636
 
1637
    Parameters:
1638
     - itemIdQuantityMap
1639
     - purchaseOrderId
1640
     - warehouseId
1641
    """
3451 chandransh 1642
    query = Order.query.with_lockmode("update").filter_by(warehouse_id=warehouseId)
4757 mandeep.dh 1643
    query = query.filter(or_(Order.status == OrderStatus.SUBMITTED_FOR_PROCESSING, Order.status == OrderStatus.INVENTORY_LOW))
2819 chandransh 1644
    query = query.filter_by(purchase_order_id=None)
1645
    query = query.order_by(Order.created_timestamp)
1646
    pending_orders = query.all()
4586 mandeep.dh 1647
 
1648
    for order in pending_orders:
4757 mandeep.dh 1649
        lineitem = order.lineitems[0]
1650
        if itemIdQuantityMap.has_key(lineitem.item_id):
1651
            if itemIdQuantityMap[lineitem.item_id] >= lineitem.quantity:
1652
                order.status = OrderStatus.LOW_INV_PO_RAISED
1653
                order.statusDescription = 'In Process'
1654
                order.purchase_order_id = purchaseOrderId
1655
                itemIdQuantityMap[lineitem.item_id] -= lineitem.quantity
4434 mandeep.dh 1656
 
2819 chandransh 1657
    session.commit()
1658
 
3451 chandransh 1659
def update_weight(order_id, weight):
3469 chandransh 1660
    '''
1661
    Update the weight of order
1662
    '''
3451 chandransh 1663
    order = get_order(order_id)
1664
    order.total_weight = weight
3469 chandransh 1665
    lineitem = order.lineitems[0]
1666
    lineitem.total_weight = weight
1667
    lineitem.unit_weight = weight
3451 chandransh 1668
 
3469 chandransh 1669
    session.commit()
1670
    return order
1671
 
1672
def change_product(order_id, item_id):
1673
    '''
1674
    Ship a product of a different color to the customer.
1675
    '''
1676
    order = get_order(order_id)
4662 rajveer 1677
    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 1678
        raise TransactionServiceException(120, "This order has already been processed. Please seek help from engineering.")
1679
 
1680
    lineitem = order.lineitems[0]
3451 chandransh 1681
 
3469 chandransh 1682
    catalog_client = CatalogClient().get_client()
1683
 
1684
    old_item = catalog_client.getItem(lineitem.item_id)
1685
    item = catalog_client.getItem(item_id)
1686
    if old_item.catalogItemId != item.catalogItemId:
1687
        raise TransactionServiceException(121, "You can't ship a different item. You can only ship the same item in a different color.")
1688
 
1689
    catalog_client.reserveItemInWarehouse(item_id, order.warehouse_id, lineitem.quantity)
1690
    catalog_client.reduceReservationCount(lineitem.item_id, order.warehouse_id, lineitem.quantity)
1691
    #TODO: Check that this new item has the same price
1692
 
1693
    lineitem.item_id = item_id
1694
    lineitem.color = item.color
1695
 
3451 chandransh 1696
    session.commit()
3469 chandransh 1697
 
1698
    order = get_order(order_id)
3451 chandransh 1699
    return order
3469 chandransh 1700
 
1701
def change_warehouse(order_id, warehouse_id):
1702
    '''
1703
    Update the warehouse which will be used to fulfil this order.
1704
    '''
1705
    order = get_order(order_id)
4662 rajveer 1706
    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 1707
        raise TransactionServiceException(117, "This order has already been processed. Please seek help from engineering.")
1708
 
1709
    if warehouse_id == order.warehouse_id:
1710
        raise TransactionServiceException(118, "You have selected the current warehouse again. Nothing to do")
1711
 
1712
    lineitem = order.lineitems[0]
1713
    catalog_client = CatalogClient().get_client()
1714
    try:
1715
        warehouse = catalog_client.getWarehouse(warehouse_id)
1716
    except:
1717
        raise TransactionServiceException(119, "No warehouse with id" + str(warehouse_id))
1718
 
1719
    catalog_client.reserveItemInWarehouse(lineitem.item_id, warehouse_id, lineitem.quantity)
1720
    catalog_client.reduceReservationCount(lineitem.item_id, order.warehouse_id, lineitem.quantity)
3451 chandransh 1721
 
3469 chandransh 1722
    order.warehouse_id = warehouse_id
1723
    session.commit()
1724
    return order
3553 chandransh 1725
 
4647 rajveer 1726
def add_delay_reason(order_id, reason, further_delay, delayReasonText):
3553 chandransh 1727
    '''
3994 chandransh 1728
    Adds a delay reason to the order and updates the expected delivery time.
3553 chandransh 1729
    '''
1730
    order = get_order(order_id)
1731
    try:
3581 chandransh 1732
        order.delay_reason = DelayReason._VALUES_TO_NAMES[reason]
4647 rajveer 1733
        order.delayReasonText = delayReasonText
4009 chandransh 1734
        actual_delivery_delay = adjust_delivery_time(order.expected_delivery_time, further_delay)
1735
        order.expected_delivery_time = order.expected_delivery_time + datetime.timedelta(days=actual_delivery_delay)
4102 chandransh 1736
 
1737
        if order.status < OrderStatus.SHIPPED_FROM_WH:
1738
            actual_shipping_delay = adjust_delivery_time(order.expected_shipping_time, further_delay)
1739
            order.expected_shipping_time = order.expected_shipping_time + datetime.timedelta(days=actual_shipping_delay)
1740
 
3553 chandransh 1741
        session.commit()
4008 mandeep.dh 1742
 
1743
        try:
1744
            transaction_requiring_extra_processing = TransactionRequiringExtraProcessing()
1745
            transaction_requiring_extra_processing.category = 'DELAYED_DELIVERY'
1746
            transaction_requiring_extra_processing.transaction_id = order.transaction_id
1747
            session.commit()
1748
        except Exception as e:
4065 mandeep.dh 1749
            print 'Could not persist transaction id: ' + str(order.transaction_id) + ' for further processing due to: ' + str(e)
4008 mandeep.dh 1750
 
3553 chandransh 1751
        return True
1752
    except:
3994 chandransh 1753
        print sys.exc_info()[0]
3553 chandransh 1754
        return False
3956 chandransh 1755
 
1756
def reconcile_cod_collection(collected_amount_map, xferBy, xferTxnId, xferDate):
1757
    unprocessed_awbs = {}
1758
    for awb, amount in collected_amount_map.iteritems():
1759
        try:
1760
            order = Order.query.filter_by(airwaybill_no=awb).one()
1761
        except NoResultFound:
1762
            unprocessed_awbs[awb] = "No order was found for the given AWB: " + awb
1763
            continue
1764
        except MultipleResultsFound:
1765
            unprocessed_awbs[awb] = "Multiple orders were found for the given AWB:" + awb
1766
            continue
1767
 
1768
        if order.cod_reconciliation_timestamp:
1769
            #This order has been processed already! This may be a re-run. Let's allow other orders to be processed.
1770
            continue
1771
 
1772
        if order.status < OrderStatus.DELIVERY_SUCCESS:
1773
            #Order has not been delivered yet. No way we can receive the payment.
1774
            unprocessed_awbs[awb] = "Payment has been received for order: " + str(order.id) + " before delivery reconciliation"
1775
            continue
1776
 
4252 rajveer 1777
        ##As per ticket #743 if amount difference less than 0.5 we should reconcile
1778
        if abs(amount - order.total_amount) > 0.5:
3956 chandransh 1779
            #Received amount is less than or more than the order amount. Mustn't let the user proceed.
1780
            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)
1781
            continue
1782
 
1783
        try:
1784
            payment_client = PaymentClient().get_client()
1785
            payment_client.partiallyCapturePayment(order.transaction.id, amount, xferBy, xferTxnId, xferDate)
1786
        except Exception:
1787
            unprocessed_awbs[awb] = "We were unable to partially capture payment for order id " + str(order.id) + ", AWB: " + awb
1788
            continue
1789
 
1790
        #Payment has been recorded now. We can update the order peacefully.
1791
        order.cod_reconciliation_timestamp = datetime.datetime.now()
1792
        session.commit()
3469 chandransh 1793
 
3956 chandransh 1794
    return unprocessed_awbs
4008 mandeep.dh 1795
 
1796
def get_transactions_requiring_extra_processing(category):
4065 mandeep.dh 1797
    """
1798
    Returns the list of transaction ids that require some extra processing and
1799
    which belong to a particular category. This is currently used by CRM
1800
    application. If no such transaction ids are present, it returns an empty list.
1801
    """
1802
    query = TransactionRequiringExtraProcessing.query
4008 mandeep.dh 1803
 
4065 mandeep.dh 1804
    if category is not None:
1805
        query = query.filter_by(category = ExtraTransactionProcessingType._VALUES_TO_NAMES.get(category))
4008 mandeep.dh 1806
 
4065 mandeep.dh 1807
    return [a.transaction_id for a in query.all()]
4008 mandeep.dh 1808
 
1809
def mark_transaction_as_processed(transactionId, category):
4065 mandeep.dh 1810
    """
1811
    Marks a particular transaction as processed for a particular cateogory.
1812
    It essentially deletes the transaction id record for a particular
1813
    processing type category (if present) from DB.
1814
    This is currently used by CRM application.
1815
    """
4008 mandeep.dh 1816
    TransactionRequiringExtraProcessing.query.filter_by(transaction_id = transactionId).filter_by(category = ExtraTransactionProcessingType._VALUES_TO_NAMES.get(category)).delete()
1817
    session.commit()
1818
 
4018 chandransh 1819
def get_item_wise_risky_orders_count():
1820
    """
1821
    Returns a map containing the number of risky orders keyed by item id. A risky order
1822
    is defined as one whose shipping date is about to expire.
1823
    """
1824
    item_wise_risky_orders_count = {}
4019 chandransh 1825
    shipping_cutoff_time = (datetime.datetime.now() + datetime.timedelta(days = 1)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
4018 chandransh 1826
    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()
1827
    for order in orders:
1828
        for lineitem in order.lineitems:
1829
            quantity = lineitem.quantity
1830
            if item_wise_risky_orders_count.has_key(lineitem.item_id):
1831
                quantity = quantity + item_wise_risky_orders_count[lineitem.item_id]
1832
 
1833
            item_wise_risky_orders_count[lineitem.item_id] = quantity
1834
    return item_wise_risky_orders_count
1835
 
4324 mandeep.dh 1836
def update_shipment_address(order_id, address_id):
1837
    """
1838
    Updates shipment address of an order. Delivery and shipping date estimates
1839
    etc. are also updated here.
1840
 
1841
    Throws TransactionServiceException in case address change is not
1842
    possible due to certain reasons such as new pincode in address is
1843
    not serviceable etc.
1844
 
1845
    Parameters:
1846
     - orderId
1847
     - addressId
1848
    """
1849
    user_client = UserClient().get_client()
1850
    address     = user_client.getAddressById(address_id)
1851
    order       = get_order(order_id)
1852
 
4630 mandeep.dh 1853
    type = DeliveryType.PREPAID
1854
    if order.cod:
1855
        type = DeliveryType.COD
1856
 
4324 mandeep.dh 1857
    logistics_client = LogisticsClient().get_client()
4630 mandeep.dh 1858
    logistics_info = logistics_client.getLogisticsEstimation(order.lineitems[0].item_id, address.pin, type)
4324 mandeep.dh 1859
 
1860
    # Check for new estimate. Raise exception if new address is not serviceable
1861
    if logistics_info.deliveryTime == -1:
1862
        raise TransactionServiceException(1, 'Location not serviceable')
1863
 
1864
    # COD services are not available everywhere
1865
    if order.cod:
1866
        if not logistics_client.isCodAllowed(address.pin):
1867
            raise TransactionServiceException(2, 'COD service not available')
1868
 
1869
    current_time = datetime.datetime.now()
1870
    logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
1871
    logistics_info.shippingTime = adjust_delivery_time(current_time, logistics_info.shippingTime)
1872
 
1873
    order.customer_name         = address.name
1874
    order.customer_pincode      = address.pin
1875
    order.customer_address1     = address.line1
1876
    order.customer_address2     = address.line2
1877
    order.customer_city         = address.city
1878
    order.customer_state        = address.state
1879
    order.customer_mobilenumber = address.phone
1880
 
1881
    if order.cod:
1882
        order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=COD_SHIPPING_CUTOFF_TIME, minute=0, second=0)
1883
    else:
1884
        order.expected_shipping_time = (current_time + datetime.timedelta(days=logistics_info.shippingTime)).replace(hour=PREPAID_SHIPPING_CUTOFF_TIME, minute=0, second=0)
1885
 
1886
    order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
1887
    order.promised_shipping_time = order.expected_shipping_time
1888
    order.promised_delivery_time = order.expected_delivery_time 
1889
 
1890
    session.commit()
1891
 
4247 rajveer 1892
def mark_order_cancellation_request_received(orderId):
1893
    """
1894
    Mark order as cancellation request received. If customer sends request of cancellation of
1895
    a particular order, this method will be called. It will just change status of the order
1896
    depending on its current status. It also records the previous status, so that we can move
1897
    back to that status if cancellation request is denied.
1898
 
1899
    Parameters:
1900
     - orderId
1901
    """
4662 rajveer 1902
    status_transition = {OrderStatus.COD_VERIFICATION_PENDING : OrderStatus.CANCEL_REQUEST_RECEIVED,
4247 rajveer 1903
             OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.CANCEL_REQUEST_RECEIVED,
1904
             OrderStatus.INVENTORY_LOW : OrderStatus.CANCEL_REQUEST_RECEIVED,
4306 rajveer 1905
             OrderStatus.LOW_INV_PO_RAISED : OrderStatus.CANCEL_REQUEST_RECEIVED,
1906
             OrderStatus.LOW_INV_REVERSAL_IN_PROCESS : OrderStatus.CANCEL_REQUEST_RECEIVED,
1907
             OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT : OrderStatus.CANCEL_REQUEST_RECEIVED,
4247 rajveer 1908
             OrderStatus.ACCEPTED : OrderStatus.CANCEL_REQUEST_RECEIVED,
1909
             OrderStatus.BILLED : OrderStatus.CANCEL_REQUEST_RECEIVED,
1910
             OrderStatus.PAYMENT_FLAGGED : OrderStatus.CANCEL_REQUEST_RECEIVED
1911
             }
4662 rajveer 1912
 
1913
    order = get_order(orderId)
4247 rajveer 1914
    if order.status not in status_transition.keys():
1915
        raise TransactionServiceException(114, "This order can't be considered for cancellation")
1916
 
4662 rajveer 1917
 
4247 rajveer 1918
    order.previousStatus = order.status
1919
    order.status = OrderStatus.CANCEL_REQUEST_RECEIVED
1920
    order.statusDescription = "Cancellation request received from user"
1921
    session.commit()
1922
 
4662 rajveer 1923
def mark_order_as_lost_in_transit(orderId):
1924
    """
1925
    Parameters:
1926
     - orderId
1927
    """
1928
    order = get_order(orderId)
1929
    if order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST]:
1930
        raise TransactionServiceException(115, "This order can't be marked as lost in transit")
1931
    order.status = OrderStatus.LOST_IN_TRANSIT
1932
    order.statusDescription = "Lost in Transit"
1933
    session.commit()
1934
 
4258 rajveer 1935
def mark_transaction_as_payment_flag_removed(transactionId):
4247 rajveer 1936
    """
4258 rajveer 1937
    If we and/or payment gateway has decided to accept the payment, this method needs to be called.
1938
    Changed transaction and all orders status to payment accepted.
4247 rajveer 1939
 
1940
    Parameters:
4258 rajveer 1941
     - transactionId
4247 rajveer 1942
    """
4258 rajveer 1943
    transaction = get_transaction(transactionId)
1944
    transaction.status = TransactionStatus.AUTHORIZED
1945
    for order in transaction.orders:
1946
        order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
1947
        order.statusDescription = "Submitted to warehouse"
4247 rajveer 1948
    session.commit()
1949
 
4259 anupam.sin 1950
def refund_transaction(transactionId, refundedBy, reason):
1951
    """
1952
    This method is called when a flagged payment is deemed unserviceable and the corresponding orders
1953
    need to be cancelled
1954
 
1955
    Parameters:
1956
     - transactionId
1957
    """
1958
    transaction = get_transaction(transactionId)
1959
    transaction.status = TransactionStatus.FAILED
1960
    for order in transaction.orders:
1961
        refund_order(order.id, refundedBy, reason)
1962
    session.commit()
1963
 
4247 rajveer 1964
def mark_order_cancellation_request_denied(orderId):
1965
    """
4283 anupam.sin 1966
    If we decide to not to cancel order, we will move the order to previous status.
4247 rajveer 1967
 
1968
    Parameters:
1969
     - orderId
1970
    """
1971
    order = get_order(orderId)
1972
    order.status = order.previousStatus
1973
    order.previousStatus = None
1974
    order.statusDescription = "Cancellation request denied"
1975
    session.commit()
1976
 
1977
def mark_order_cancellation_request_confirmed(orderId):
1978
    """
1979
    If we decide to to cancel order, CRM will call this method to move the status of order to
1980
    cancellation request confirmed. After this OM will be able to cancel the order.
1981
 
1982
    Parameters:
1983
     - orderId
1984
    """
1985
    order = get_order(orderId)
1986
    order.status = OrderStatus.CANCEL_REQUEST_CONFIRMED
1987
    order.statusDescription = "Cancellation request confirmed"
1988
    session.commit()
1989
 
4285 rajveer 1990
def accept_orders_for_item_id(itemId, inventory):
1991
    """
1992
    Marks the orders as ACCEPTED for the given itemId and inventory. It also updates the accepted timestamp. If the
1993
    given order is not a COD order, it also captures the payment if the same has not been captured.
1994
 
1995
    Parameters:
1996
     - itemId
1997
     - inventory
1998
    """
4351 rajveer 1999
    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 2000
    for order in orders:
4301 rajveer 2001
        if inventory > 0:
2002
            lineitem = order.lineitems[0]
2003
            if itemId == lineitem.item_id:
2004
                try:
2005
                    accept_order(order.id)
2006
                    inventory = inventory - lineitem.quantity
2007
                except:
2008
                    logging.info("Unable to accept the order")
2009
        else:
2010
            order_outofstock(order.id)
4285 rajveer 2011
    return
2012
 
4607 rajveer 2013
def get_slipped_sipping_date_orders():
4662 rajveer 2014
    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 2015
 
2016
def get_cancelled_orders(cancelDateFrom, cancelDateTo):
2017
    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 2018
 
4369 rajveer 2019
def mark_orders_as_po_raised(vendorId, itemId, quantity, estimate, isReminder):
2020
    if isReminder:
2021
        statuses = tuple([OrderStatus.LOW_INV_PO_RAISED_TIMEOUT, OrderStatus.LOW_INV_REVERSAL_TIMEOUT])
2022
    else:
2023
        statuses = tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW])
2024
 
2025
    orders = Order.query.filter(Order.status.in_(statuses)).order_by(Order.expected_shipping_time).all()
4303 rajveer 2026
    for order in orders:
2027
        lineitem = order.lineitems[0]
2028
        if quantity >= lineitem.quantity and itemId == lineitem.item_id:
2029
            if estimate > 0:
2030
                add_delay_reason(order.id, DelayReason.OTHERS, estimate)
2031
            order.status = OrderStatus.LOW_INV_PO_RAISED
4362 rajveer 2032
            order.statusDescription = "PO Raised"
4303 rajveer 2033
            quantity = quantity - lineitem.quantity
2034
            store_inventory_action(order, itemId, HotspotAction.PO_RAISED, estimate, lineitem.quantity)
2035
            session.commit()
2036
 
4369 rajveer 2037
def mark_orders_as_reversal_initiated(vendorId, itemId, quantity, estimate, isReminder):
2038
    if isReminder:
2039
        statuses = tuple([OrderStatus.LOW_INV_PO_RAISED_TIMEOUT, OrderStatus.LOW_INV_REVERSAL_TIMEOUT])
2040
    else:
2041
        statuses = tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW])
2042
    orders = Order.query.filter(Order.status.in_(statuses)).order_by(Order.expected_shipping_time).all()
4303 rajveer 2043
    for order in orders:
2044
        lineitem = order.lineitems[0]
2045
        if quantity >= lineitem.quantity and itemId == lineitem.item_id:
2046
            if estimate > 0:
2047
                add_delay_reason(order.id, DelayReason.OTHERS, estimate)
2048
            order.status = OrderStatus.LOW_INV_REVERSAL_IN_PROCESS
4362 rajveer 2049
            order.statusDescription = "Reversal Initiated"
4303 rajveer 2050
            quantity = quantity - lineitem.quantity
2051
            store_inventory_action(order, itemId, HotspotAction.REVERSAL_INITIATED, estimate, lineitem.quantity)
2052
            session.commit()
2053
 
4369 rajveer 2054
def mark_orders_as_not_available(vendorId, itemId, quantity, estimate, isReminder):
2055
    if isReminder:
2056
        statuses = tuple([OrderStatus.LOW_INV_PO_RAISED_TIMEOUT, OrderStatus.LOW_INV_REVERSAL_TIMEOUT])
2057
    else:
2058
        statuses = tuple([OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW])
2059
 
2060
    orders = Order.query.filter(Order.status.in_(statuses)).order_by(Order.expected_shipping_time).all()
4303 rajveer 2061
    for order in orders:
2062
        lineitem = order.lineitems[0]
2063
        if quantity >= lineitem.quantity and itemId == lineitem.item_id:
2064
            order.status = OrderStatus.LOW_INV_NOT_AVAILABLE_AT_HOTSPOT
4362 rajveer 2065
            order.statusDescription = "Not Available"
4303 rajveer 2066
            quantity = quantity - lineitem.quantity
2067
            store_inventory_action(order, itemId, HotspotAction.NOT_AVAILABLE, estimate, lineitem.quantity)
2068
            session.commit()
2069
 
2070
def store_inventory_action(order, itemId, hotspotAction, estimate, quantity):
4369 rajveer 2071
    orderInv = OrderInventory.get_by(order_id=order.id)
2072
    if not orderInv:
2073
        orderInv = OrderInventory()
2074
        orderInv.order = order
4303 rajveer 2075
    orderInv.hotspotAction = hotspotAction  
2076
    orderInv.estimate = estimate
2077
    orderInv.itemId = itemId
2078
    orderInv.timestamp = datetime.datetime.now()
2079
    orderInv.quantity = quantity
2080
 
4556 mandeep.dh 2081
'''    
4369 rajveer 2082
def mark_orders_as_timeout(vendorId):
2083
    ret_timeouts = {}
2084
    orderInvs = OrderInventory.query.all()
2085
    for orderInv in orderInvs:
2086
        if orderInv.timestamp +  timedelta(days=orderInv.estimate) < datetime.datetime.now():
2087
            if not ret_timeouts.has_key(orderInv.itemId):
2088
                timeout = TimeoutSummary()
2089
                ret_timeouts[orderInv.itemId] = timeout
2090
            else:
2091
                timeout = ret_timeouts.get(orderInv.itemId)
2092
            if orderInv.hotspotAction == HotspotAction.PO_RAISED and orderInv.order.status == OrderStatus.LOW_INV_PO_RAISED:
2093
                if timeout.poRaised:
2094
                    timeout.poRaised += 1
2095
                else:
2096
                    timeout.poRaised = 1
2097
 
2098
                if timeout.poEstimate:
2099
                    timeout.poEstimate += orderInv.estimate
2100
                else:
2101
                    timeout.poEstimate = orderInv.estimate
2102
                orderInv.order.status = OrderStatus.LOW_INV_PO_RAISED_TIMEOUT
2103
                orderInv.order.statusDescription = "PO Raised Timeout"
2104
            elif orderInv.hotspotAction == HotspotAction.REVERSAL_INITIATED and orderInv.order.status == OrderStatus.LOW_INV_REVERSAL_IN_PROCESS:
2105
                if timeout.reversalInitiated:
2106
                    timeout.reversalInitiated += 1
2107
                else:
2108
                    timeout.reversalInitiated = 1
2109
 
2110
                if timeout.reversalEstimate:
2111
                    timeout.reversalEstimate += orderInv.estimate
2112
                else:
2113
                    timeout.reversalEstimate = orderInv.estimate
2114
                orderInv.order.status = OrderStatus.LOW_INV_REVERSAL_TIMEOUT
2115
                orderInv.order.statusDescription = "Reversal Initiated Timeout"
2116
            else:
2117
                orderInv.delete()
2118
    session.commit()
2119
    return ret_timeouts
4556 mandeep.dh 2120
'''
2121
 
2122
 
2123
def mark_orders_as_timeout(vendorId):
2124
    ret_timeouts = {}
2125
    orderInvs = OrderInventory.query.all()
2126
    for orderInv in orderInvs:
2127
        if not ret_timeouts.has_key(orderInv.itemId):
2128
            timeout = TimeoutSummary()
2129
            ret_timeouts[orderInv.itemId] = timeout
2130
        else:
2131
            timeout = ret_timeouts.get(orderInv.itemId)
2132
        if orderInv.hotspotAction == HotspotAction.PO_RAISED and orderInv.order.status == OrderStatus.LOW_INV_PO_RAISED:
2133
            if timeout.poRaised:
2134
                timeout.poRaised += 1
2135
            else:
2136
                timeout.poRaised = 1
2137
 
2138
            if timeout.poEstimate:
2139
                timeout.poEstimate = max(orderInv.estimate, timeout.poEstimate)
2140
            else:
2141
                timeout.poEstimate = orderInv.estimate
2142
        elif orderInv.hotspotAction == HotspotAction.REVERSAL_INITIATED and orderInv.order.status == OrderStatus.LOW_INV_REVERSAL_IN_PROCESS:
2143
            if timeout.reversalInitiated:
2144
                timeout.reversalInitiated += 1
2145
            else:
2146
                timeout.reversalInitiated = 1
2147
 
2148
            if timeout.reversalEstimate:
2149
                timeout.reversalEstimate = max(orderInv.estimate, timeout.reversalEstimate)
2150
            else:
2151
                timeout.reversalEstimate = orderInv.estimate
2152
        orderInv.order.status = OrderStatus.INVENTORY_LOW
2153
        orderInv.order.statusDescription = "Low Inventory"
2154
        orderInv.delete()
2155
    session.commit()
2156
    return ret_timeouts
2157
 
4386 anupam.sin 2158
def get_order_for_awb(awb):
2159
    order = Order.get_by(airwaybill_no=awb)
2160
    if not order:
2161
        raise TransactionServiceException(108, "no such order")
2162
 
2163
    return order
4369 rajveer 2164
 
4506 phani.kuma 2165
def get_orders_for_provider_for_status(provider_id, order_status):
2166
    if not provider_id or not order_status:
2167
        raise TransactionServiceException(101, "bad provider id or order status")
2168
    query = Order.query.filter_by(logistics_provider_id = provider_id, status = order_status)
2169
    return query.all()
4386 anupam.sin 2170
 
4600 varun.gupt 2171
def get_billed_orders_for_vendor(vendor_id, billing_date_from, billing_date_to):
2172
    query = Order.query.filter_by(vendorId = vendor_id)
2173
    query = query.filter(Order.status != OrderStatus.PAYMENT_PENDING)
2174
    if billing_date_from:
2175
        query = query.filter(Order.billing_timestamp >= billing_date_from)
2176
 
2177
    if billing_date_to:
2178
        query = query.filter(Order.billing_timestamp <= billing_date_to)
2179
 
2180
    return query.all()
2181
 
2182
def save_bluedart_settlements(map_awb_and_amount):
2183
    payment_client = PaymentClient().get_client()
2184
 
2185
    for awb, amount in map_awb_and_amount.iteritems():
2186
        order = Order.get_by(airwaybill_no = awb)
2187
 
2188
        payment = payment_client.getSuccessfulPaymentForTxnId(order.transaction_id)
2189
        settlement = PaymentSettlement()
2190
        settlement.paymentId = payment.paymentId
2191
        settlement.paymentGatewayId = payment.gatewayId
2192
        settlement.netCollection = amount
2193
        session.commit()
2194
 
2195
def save_payment_settlements(settlementDate, paymentGatewayId, paymentId, serviceTax, otherCharges, netCollection):
2196
 
2197
    settlement = PaymentSettlement()
2198
    settlement.settlementDate = settlementDate
2199
    settlement.paymentGatewayId = paymentGatewayId
2200
    settlement.paymentId = paymentId
2201
    settlement.serviceTax = serviceTax
2202
    settlement.otherCharges = otherCharges
2203
    settlement.netCollection = netCollection
2204
    session.commit()
2205
 
2206
def get_settlement_for_paymentId(payment_id):
2207
    payment_settlement = PaymentSettlement.get_by(paymentId = payment_id)
2208
 
2209
    return payment_settlement
2210
 
2211
def save_ebs_settlement_summary(settlement_id, settlement_date, transaction_date_from, transaction_date_to, amount):
2212
    summary = EBSSettlementSummary()
2213
    summary.settlementId = settlement_id
2214
    summary.settlementDate = settlement_date
2215
    summary.transactionDateFrom = transaction_date_from
2216
    summary.transactionDateTo = transaction_date_to
2217
    summary.amount = amount
2218
    summary.detailsUploaded = False
2219
    session.commit()
2220
 
2221
def get_ebs_settlement_summaries():
2222
    summaries = EBSSettlementSummary.query.filter_by(detailsUploaded = False).order_by(EBSSettlementSummary.transactionDateFrom).all()
2223
    summary_map = {}
2224
 
2225
    for summary in summaries:
2226
        transactionDateFrom = str(summary.transactionDateFrom.year) + '-' + str(summary.transactionDateFrom.month) + '-' + str(summary.transactionDateFrom.day)
2227
        transactionDateTo = str(summary.transactionDateTo.year) + '-' + str(summary.transactionDateTo.month) + '-' + str(summary.transactionDateTo.day)
2228
 
2229
        summary_map[summary.settlementId] = "%s to %s" %(transactionDateFrom, transactionDateTo)
2230
 
2231
    return summary_map
2232
 
2233
def get_ebs_settlement_date(settlement_id):
2234
    summary = EBSSettlementSummary.get_by(settlementId = settlement_id)
2235
 
2236
    if summary:
2237
        return summary.settlementDate
2238
 
2239
def mark_ebs_settlement_uploaded(settlement_id):
2240
    summary = EBSSettlementSummary.get_by(settlementId = settlement_id)
2241
 
2242
    if summary:
2243
        summary.detailsUploaded = True
2244
        session.commit()
4715 varun.gupt 2245
 
2246
def get_settlements_by_date(settlement_date_from, settlement_date_to, is_refund):
2247
    query = PaymentSettlement.query
4600 varun.gupt 2248
 
4715 varun.gupt 2249
    if settlement_date_from:
2250
        query = query.filter(PaymentSettlement.settlementDate >= settlement_date_from)
2251
 
2252
    if settlement_date_to:
2253
        query = query.filter(PaymentSettlement.settlementDate <= settlement_date_to)
2254
 
4749 varun.gupt 2255
    if is_refund is True:
2256
        query = query.filter(PaymentSettlement.netCollection < 0.0)
2257
    else:
2258
        query = query.filter(PaymentSettlement.netCollection > 0.0)
4715 varun.gupt 2259
 
4749 varun.gupt 2260
    return query.all()
4715 varun.gupt 2261
 
2262
def get_reshipped_order_ids(order_ids):
2263
    reshipped_order_ids = []
2264
    return [order.new_order_id for order in Order.query.filter(Order.new_order_id.in_(order_ids)).all()]
2265
 
4749 varun.gupt 2266
def get_shipping_confirmation_email_body(order):
2267
 
2268
    html = """
2269
<html>
2270
<body>
2271
<div>
2272
    <p>Dear Customer,</p>
2273
    <p>
2274
        We are pleased to inform that we have shipped the order : $order_id for your product $product_name on $shipping_datetime. 
2275
        The shipment will be delivered to you via Bluedart/Aramex Courier Service vide AWB# $awb.
2276
    </p>
2277
    <p>
2278
        Expected delivery date for your order is $expected_delivery_date. 
2279
        The product can be delivered to you between business hours of 10am to 6pm. 
2280
        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.
2281
    </p>
2282
    <p>
2283
        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#.
2284
    </p>
2285
    <p>Bluedart: 011-66111234<br />Aramex:  0124- 39419900</p>
2286
    <p>
2287
        Warm Regards,<br />
2288
        Saholic Team
2289
    </p>
4750 varun.gupt 2290
</div>
4749 varun.gupt 2291
</body>
2292
</html>
2293
    """
2294
    dt = datetime.datetime.strptime(str(order.shipping_timestamp), "%Y-%m-%d %H:%M:%S")
2295
    shipping_date = dt.strftime("%A, %d. %B %Y %I:%M%p")
2296
    product = "%s %s %s" % (order.lineitems[0].brand, order.lineitems[0].model_name, order.lineitems[0].model_number)
2297
    new_expected_delivery_date = adjust_delivery_time(datetime.datetime.now(), (order.promised_delivery_time - order.promised_shipping_time).days)
2298
 
2299
    info = dict(order_id = order.id, product_name = product, shipping_datetime = shipping_date, awb = order.airwaybill_no, expected_delivery_date = new_expected_delivery_date)
2300
    email_html = Template(html).substitute(info)
2301
 
2302
def enqueue_shipping_confirmation_email(orders):
2303
 
2304
    helperClient = HelperClient().get_client()
2305
 
2306
    for order in orders:
2307
        try:
2308
            emailSubject = 'Shipping Details for Order ID: ' + str(order.id)
2309
            emailBody = get_shipping_confirmation_email_body(order)
2310
            helperClient.saveUserEmailForSending(order, None, emailSubject, emailBody, '', 'ShippingConfirmation')
2311
        except Exception as e:
2312
            print e
2313
 
766 rajveer 2314
def close_session():
2315
    if session.is_active:
2316
        print "session is active. closing it."
2783 chandransh 2317
        session.close()
3376 rajveer 2318
 
2319
def is_alive():
2320
    try:
2321
        session.query(Transaction.id).limit(1).one()
2322
        return True
2323
    except:
4343 varun.gupt 2324
        return False