Subversion Repositories SmartDukaan

Rev

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