Subversion Repositories SmartDukaan

Rev

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

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