Subversion Repositories SmartDukaan

Rev

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