Subversion Repositories SmartDukaan

Rev

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