Subversion Repositories SmartDukaan

Rev

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