Subversion Repositories SmartDukaan

Rev

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