Subversion Repositories SmartDukaan

Rev

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