Subversion Repositories SmartDukaan

Rev

Rev 2628 | Rev 2684 | 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
import sys
8
import os
9
import datetime 
10
import time
11
from datetime import date
12
from textwrap import dedent
1262 varun.gupt 13
from string import Template
14
 
2536 chandransh 15
from sqlalchemy.sql.expression import and_, or_, desc
16
from sqlalchemy.sql import func
17
 
18
from elixir import *
19
 
20
from reportlab.pdfgen.canvas import Canvas
21
from reportlab.rl_config import defaultPageSize
22
from reportlab.lib.units import inch
23
PAGE_HEIGHT=defaultPageSize[1]
24
PAGE_WIDTH=defaultPageSize[0]
25
 
104 ashish 26
from shop2020.model.v1.order.impl.DataService import Transaction, LineItem,\
2628 chandransh 27
    Alerts, Order, BatchNoGenerator, ReturnOrder
104 ashish 28
from shop2020.thriftpy.model.v1.order.ttypes import Transaction as T_Transaction,\
690 chandransh 29
    TransactionServiceException, TransactionStatus, OrderStatus
855 rajveer 30
from shop2020.utils.Utils import to_py_date, to_java_date
483 rajveer 31
from shop2020.model.v1 import order
743 rajveer 32
from shop2020.clients.LogisticsClient import LogisticsClient
871 chandransh 33
from shop2020.clients.InventoryClient import InventoryClient
1262 varun.gupt 34
from shop2020.clients.UserClient import UserClient
1351 varun.gupt 35
from shop2020.clients.HelperClient import HelperClient
2536 chandransh 36
from shop2020.utils.EmailAttachmentSender import mail, get_attachment_part
104 ashish 37
 
38
def get_new_transaction():
39
    transaction = Transaction()
40
    transaction.createdOn = datetime.datetime.now()
738 chandransh 41
    transaction.status = TransactionStatus.INIT
104 ashish 42
    transaction.status_message = "New transaction"
483 rajveer 43
    return transaction
44
 
45
def create_order(t_order):
46
    order = Order()
47
    if t_order.warehouse_id:
48
        order.warehouse_id = t_order.warehouse_id
509 rajveer 49
    if t_order.logistics_provider_id:    
483 rajveer 50
        order.logistics_provider_id = t_order.logistics_provider_id
51
        order.airwaybill_no = t_order.airwaybill_no
52
        order.tracking_id = t_order.tracking_id
53
    if t_order.expected_delivery_time:    
54
        order.expected_delivery_time = to_py_date(t_order.expected_delivery_time)
55
    if t_order.customer_id:    
56
        order.customer_id = t_order.customer_id
57
        order.customer_name = t_order.customer_name
860 rajveer 58
        order.customer_city = t_order.customer_city
59
        order.customer_state = t_order.customer_state
483 rajveer 60
        order.customer_mobilenumber = t_order.customer_mobilenumber
61
        order.customer_pincode = t_order.customer_pincode
738 chandransh 62
        order.customer_address1 = t_order.customer_address1
63
        order.customer_address2 = t_order.customer_address2
483 rajveer 64
        order.customer_email = t_order.customer_email
65
    #new status and status description to be added    
716 rajveer 66
    order.status = t_order.status  
483 rajveer 67
    order.statusDescription = t_order.statusDescription
68
    order.total_amount = t_order.total_amount
69
    order.total_weight = t_order.total_weight
70
    if t_order.created_timestamp:
71
        order.created_timestamp = to_py_date(t_order.created_timestamp)
72
    else:
685 chandransh 73
        order.created_timestamp = datetime.datetime.now()
483 rajveer 74
    return order
75
 
76
def create_transaction(t_transaction):
77
    t_orders = t_transaction.orders
78
    if not t_orders:
79
        raise TransactionServiceException(101, "Orders missing from the transaction")
80
    transaction = get_new_transaction()
81
    transaction.customer_id = t_transaction.customer_id 
82
    transaction.shopping_cart_id = t_transaction.shoppingCartid
2219 varun.gupt 83
    transaction.coupon_code = t_transaction.coupon_code
483 rajveer 84
 
85
    for t_order in t_orders:
86
        order = create_order(t_order)
87
        order.transaction = transaction 
88
        for line_item in t_order.lineitems:
89
            litem = LineItem()
716 rajveer 90
            litem.item_id = line_item.item_id
963 chandransh 91
            litem.productGroup = line_item.productGroup
483 rajveer 92
            litem.brand = line_item.brand
93
            if line_item.model_number:
94
                litem.model_number = line_item.model_number
95
            if line_item.model_name:    
96
                litem.model_name = line_item.model_name
908 chandransh 97
            if line_item.color:
98
                litem.color = line_item.color
483 rajveer 99
            if line_item.extra_info:
100
                litem.extra_info = line_item.extra_info
101
            litem.quantity = line_item.quantity
102
            litem.unit_price = line_item.unit_price
103
            litem.unit_weight = line_item.unit_weight
104
            litem.total_price = line_item.total_price
105
            litem.total_weight = line_item.total_weight
1262 varun.gupt 106
            litem.transfer_price = line_item.transfer_price
1976 varun.gupt 107
 
483 rajveer 108
            """
109
            if line_item.addedOn:
110
                litem.added_on = to_py_date(line_item.addedOn)
111
            else:
112
                litem.added_on = datetime.datetime.now()
113
            """
114
            litem.order = order
115
    session.commit()
116
    return transaction.id
117
 
118
def get_transaction(transaction_id):
119
    transaction = Transaction.get_by(id=transaction_id)
120
    if not transaction:
121
        raise TransactionServiceException(108, "no such transaction")
122
 
123
    return transaction
124
 
125
def get_transactions_for_customer(customer_id, from_date, to_date, status):
126
    if not customer_id:
127
        raise TransactionServiceException(101, "bad customer id")
128
    query = Transaction.query.filter(Transaction.customer_id == customer_id)
1493 vikas 129
    if status is not None:
483 rajveer 130
        query = query.filter(Transaction.status == status)
131
    if from_date:
132
        query = query.filter(Transaction.createdOn >from_date)
133
    if to_date:
134
        query = query.filter(Transaction.createdOn < to_date)
135
    return query.all()
136
 
137
def get_transactions_for_shopping_cart_id(shopping_cart_id):
138
    transactions = Transaction.query.filter_by(shopping_cart_id=shopping_cart_id).all()
139
    return transactions
140
 
141
def get_transaction_status(transaction_id):
142
    if not transaction_id:
143
        raise TransactionServiceException(101, "bad transaction id")
144
 
145
    transaction = get_transaction(transaction_id)
146
 
147
    return transaction.status
148
 
149
def change_transaction_status(transaction_id, new_status, description):
150
    transaction = get_transaction(transaction_id)
151
    transaction.status = new_status
152
    transaction.status_message = description
690 chandransh 153
    if new_status == TransactionStatus.FAILED:
154
        for order in transaction.orders:
155
            order.status = OrderStatus.PAYMENT_FAILED
2339 chandransh 156
            order.statusDescription = "Payment Failed"
690 chandransh 157
    elif new_status == TransactionStatus.IN_PROCESS:
158
        for order in transaction.orders:
159
            order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
1219 chandransh 160
            order.statusDescription = "Submitted to warehouse"
743 rajveer 161
            #After we got payment success, we will set logistics info also 
162
            logistics_client = LogisticsClient().get_client()
163
            #FIXME line item is only one now. If multiple will come, need to fix.
871 chandransh 164
            item_id = order.lineitems[0].item_id
165
            logistics_info = logistics_client.getLogisticsInfo(order.customer_pincode, item_id)
743 rajveer 166
 
1730 ankur.sing 167
            current_time = datetime.datetime.now()
168
            logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
169
 
743 rajveer 170
            order.warehouse_id = logistics_info.warehouseId
171
            order.logistics_provider_id = logistics_info.providerId
172
            order.airwaybill_no = logistics_info.airway_billno
173
            order.tracking_id = order.airwaybill_no
1730 ankur.sing 174
            order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
871 chandransh 175
 
176
            catalog_client = InventoryClient().get_client()
177
            catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, order.lineitems[0].quantity)
1351 varun.gupt 178
 
1341 chandransh 179
            item_pricing = catalog_client.getItemPricing(item_id, order.warehouse_id)
180
            order.lineitems[0].transfer_price = item_pricing.transferPrice
1262 varun.gupt 181
 
483 rajveer 182
    session.commit()
183
    return True
184
 
1730 ankur.sing 185
def adjust_delivery_time(order_time, delivery_days):
186
    order_date = order_time.date()
187
 
188
    delivery_date = order_date + datetime.timedelta(days = delivery_days)
189
    final_delivery_date = delivery_date
190
 
191
    time_format = "%Y-%m-%d %H:%M:%S.%f"
192
    t = time.strptime(str(order_time), time_format)
193
    midnightTime = datetime.datetime(*t[:3])
194
    fromDate = to_java_date(midnightTime)
195
    toDate = -1L
196
 
197
    logistics_client = LogisticsClient().get_client()
198
    holiday_dates = logistics_client.getHolidays(fromDate, toDate)
199
    holiday_dates = [(to_py_date(h_date)).date() for h_date in holiday_dates]
200
 
201
    while order_date <= final_delivery_date:
202
        if order_date.weekday() == 6 or is_holiday(holiday_dates, order_date):
203
            delivery_days = delivery_days + 1
204
            final_delivery_date = final_delivery_date + datetime.timedelta(days = 1)
205
        order_date = order_date + datetime.timedelta(days = 1)
206
    return delivery_days
207
 
208
 
209
 
210
def is_holiday(holidays, date):
211
    try:
212
        holidays.index(date)
213
        return 1
214
    except:
215
        return 0
216
 
1398 varun.gupt 217
def enqueue_transaction_info_email(transaction_id):
1262 varun.gupt 218
    transaction = get_transaction(transaction_id)
219
 
220
    html_header = """
221
<html>
222
<body>
223
<div>
224
    <p>
225
        Hello,<br /><br />
1742 rajveer 226
        Thanks for placing order with us. Following are the details of your order:
1262 varun.gupt 227
    </p>
228
    <div>
229
        Order Date: $order_date
1569 varun.gupt 230
    </div>
1262 varun.gupt 231
    <div>
232
        <table>
1569 varun.gupt 233
        <tr><td colspan="5"><hr /></td></tr>
234
        <tr><td colspan="5" align="left"><b>Order Details</b></td></tr>
235
        <tr><td colspan="5"><hr /></td></tr>
1262 varun.gupt 236
        <tr>
2050 varun.gupt 237
            <th width="100">Order No.</th>
238
            <th>Product</th>
239
            <th width="100">Quantity</th>
240
            <th width="100">Unit Price</th>
241
            <th width="100">Amount</th>
1262 varun.gupt 242
        </tr>
243
    """
1398 varun.gupt 244
    user_email = ""
1262 varun.gupt 245
    total_amount = 0.0
246
    html_table = ""
1569 varun.gupt 247
    order_date = datetime.datetime.now()
1262 varun.gupt 248
 
249
    for order in transaction.orders:
250
        order_date = order.created_timestamp
1398 varun.gupt 251
        user_email = order.customer_email
1569 varun.gupt 252
        html_tr = "<tr><td align='center'>" + str(order.id) + "</td>"
1262 varun.gupt 253
 
254
        for lineitem in order.lineitems:
2050 varun.gupt 255
            lineitem_total_price = round(lineitem.total_price, 2)
256
 
1276 chandransh 257
            html_tr += "<td>" + str(lineitem) + "</td>"
2050 varun.gupt 258
            html_tr += "<td align='center'>" + ("%.0f" % lineitem.quantity) + "</td>"
259
            html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem.unit_price) + "</td>"
260
            html_tr += "<td align='center'> Rs. " + ("%.2f" % lineitem_total_price) + "</td></tr>"
261
            total_amount += lineitem_total_price
1262 varun.gupt 262
            html_table += html_tr
2050 varun.gupt 263
 
1262 varun.gupt 264
    html_footer = """
265
        <tr><td colspan=5>&nbsp;</td></tr>
1565 varun.gupt 266
        <tr><td colspan=5><hr /></td></tr>
1262 varun.gupt 267
        <tr>
268
            <td colspan=4>Total Amount</td>
2050 varun.gupt 269
            <td> Rs. $total_amount</td>
1262 varun.gupt 270
        </tr>
1569 varun.gupt 271
        <tr><td colspan=5><hr /></td></tr>
1262 varun.gupt 272
        </table>
1569 varun.gupt 273
    </div>
1262 varun.gupt 274
    <p>
275
        Best Wishes,<br />
276
        Saholic Team
277
    </p>
278
</div>
279
</body>
280
</html>
281
    """
1398 varun.gupt 282
 
1569 varun.gupt 283
    dt = datetime.datetime.strptime(str(order_date), "%Y-%m-%d %H:%M:%S")
1565 varun.gupt 284
    formated_order_date = dt.strftime("%A, %d. %B %Y %I:%M%p")
285
 
1262 varun.gupt 286
    email_header = Template(html_header).substitute(dict(order_date = formated_order_date))
2050 varun.gupt 287
    email_footer = Template(html_footer).substitute(dict(total_amount = "%.2f" % total_amount))
1262 varun.gupt 288
 
1351 varun.gupt 289
    try:
290
        helper_client = HelperClient().get_client()
1398 varun.gupt 291
        helper_client.saveUserEmailForSending(user_email, "", "Saholic - Order Details", email_header + html_table + email_footer, str(transaction_id), "TransactionInfo")
1351 varun.gupt 292
        return True
293
    except Exception as e:
294
        print e
295
        return False
1262 varun.gupt 296
 
483 rajveer 297
def get_order(order_id):
298
    order = Order.get_by(id=order_id)
299
    if not order:
300
        raise TransactionServiceException(108, "no such order")
301
 
302
    return order
303
 
304
def get_orders_for_customer(customer_id, from_date, to_date, status):
305
    if not customer_id:
306
        raise TransactionServiceException(101, "bad customer id")
307
    query = Order.query.filter(Order.customer_id == customer_id)
308
    if status:
309
        query = query.filter(Order.status == status)
310
    if from_date:
311
        query = query.filter(Order.created_timestamp >from_date)
312
    if to_date:
313
        query = query.filter(Order.created_timestamp < to_date)
314
    return query.all()
315
 
1528 ankur.sing 316
def get_order_for_customer(order_id, customer_id):
317
    if not customer_id:
318
        raise TransactionServiceException(101, "bad customer id")
319
    order = get_order(order_id)
320
    if order.customer_id != customer_id:
321
        raise TransactionServiceException(108, "order: " + str(order_id) + " does not belong to customer: " + str(customer_id))
322
    return order
323
 
483 rajveer 324
def get_all_orders(status, from_date, to_date, warehouse_id):
325
    if not warehouse_id:
326
        raise TransactionServiceException(101, "bad warehouse id")
327
    query = Order.query.filter(Order.warehouse_id == warehouse_id)
328
    if status:
329
        query = query.filter(Order.status == status)
330
    if from_date:
331
        query = query.filter(Order.created_timestamp >from_date)
332
    if to_date:
333
        query = query.filter(Order.created_timestamp < to_date)
334
    return query.all()
335
 
1382 varun.gupt 336
def get_returnable_orders_for_customer(customer_id, limit = None):
337
    if not customer_id:
338
        raise TransactionServiceException(101, "bad customer id")
339
 
340
    query = Order.query.filter(Order.customer_id == customer_id)
341
    query = query.filter(Order.status != OrderStatus.PAYMENT_FAILED)
342
    query = query.filter(Order.status != OrderStatus.REJECTED)
343
    query = query.filter(Order.status > OrderStatus.SHIPPED_FROM_WH)
344
    query = query.filter(or_(Order.status < OrderStatus.DELIVERY_SUCCESS, Order.delivery_timestamp < datetime.datetime.now() + datetime.timedelta(hours = 48)))
345
    orders = query.all()
346
    order_ids = [order.id for order in orders]
347
    return order_ids
348
 
349
def get_cancellable_orders_for_customer(customer_id, limit = None):
350
    if not customer_id:
351
        raise TransactionServiceException(101, "bad customer id")
352
 
353
    query = Order.query.filter(Order.customer_id == customer_id)
354
    query = query.filter(Order.status != OrderStatus.REJECTED)
1483 varun.gupt 355
    query = query.filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING)
1382 varun.gupt 356
    query = query.filter(Order.status < OrderStatus.BILLED)
357
    orders = query.all()
358
    return [order.id for order in orders]
359
 
995 varun.gupt 360
def get_orders_by_billing_date(status, start_billing_date, end_billing_date, warehouse_id):
361
    if not warehouse_id:
362
        raise TransactionServiceException(101, "bad warehouse id")
363
 
364
    query = Order.query.filter(Order.warehouse_id == warehouse_id)
365
 
366
    if status:
367
        query = query.filter(Order.status == status)
368
 
369
    if start_billing_date:
370
        query = query.filter(Order.billing_timestamp >= start_billing_date)
371
 
372
    if end_billing_date:
373
        query = query.filter(Order.billing_timestamp <= end_billing_date)
374
 
375
    return query.all()
376
 
1528 ankur.sing 377
def get_orders_for_transaction(transaction_id, customer_id):
378
    orders = Order.query.filter_by(transaction_id=transaction_id, customer_id=customer_id).all()
379
 
1208 chandransh 380
    if not orders:
381
        raise TransactionServiceException(101, "No order for the transaction")
382
    return orders
383
 
1405 ankur.sing 384
def get_undelivered_orders(provider_id, warehouse_id):
385
    query = Order.query
386
    if provider_id != -1:
387
        query = query.filter_by(Order.logistics_provider_id == provider_id)
388
    if warehouse_id != -1:
389
        query = query.filter(Order.warehouse_id == warehouse_id)
390
    query = query.filter(and_(Order.status != OrderStatus.DELIVERY_SUCCESS, Order.status != OrderStatus.FAILED, \
391
                              Order.status > OrderStatus.SHIPPED_FROM_WH))
1479 ankur.sing 392
    #Get last midnight time instead of current time as courier company would have sent the 
393
    #delivery records updated at least till midnight and not by the current time.
394
    time_format = "%Y-%m-%d %H:%M:%S.%f"
395
    t = time.strptime(str(datetime.datetime.now()), time_format)
396
    midnightTime = datetime.datetime(*t[:3])
397
    query = query.filter(Order.expected_delivery_time < midnightTime)
1405 ankur.sing 398
    orders = query.all()
399
    return orders
400
 
1208 chandransh 401
def get_line_items_for_order(order_id):
402
    query = LineItem.query.filter(LineItem.order_id == order_id)
403
    return query.all()
404
 
483 rajveer 405
def change_order_status(self, orderId, status, description):
406
    order = get_order(orderId)
407
    order.status = status
408
    order.statusDescription = description
409
    session.commit()
410
    return True
411
 
921 rajveer 412
def accept_order(self, orderId):
413
    order = get_order(orderId)
414
    order.status = OrderStatus.ACCEPTED
415
    order.statusDescription = "Order Accepted"
416
    order.accepted_timestamp = datetime.datetime.now()
417
    session.commit()
418
    return True
419
 
420
def bill_order(self, orderId):
421
    order = get_order(orderId)
422
    order.status = OrderStatus.BILLED
423
    order.statusDescription = "Order Billed"
424
    order.billing_timestamp = datetime.datetime.now()
425
    session.commit()
1208 chandransh 426
    return True
921 rajveer 427
 
1149 chandransh 428
def add_billing_details(orderId, invoice_number, billed_by):
494 rajveer 429
    order = Order.get_by(id=orderId)
430
    if not order:
431
        raise TransactionServiceException(101, "No order found for the givem orderid")
432
    order.invoice_number = invoice_number
433
    order.billed_by = billed_by
434
    session.commit()
435
    return True
759 chandransh 436
 
1149 chandransh 437
def add_jacket_number(orderId, jacket_number):
438
    order = Order.get_by(id=orderId)
439
    if not order:
440
        raise TransactionServiceException(101, "No order found for the givem orderid")
441
    order.jacket_number = jacket_number 
442
    session.commit()
443
    return True
444
 
1220 chandransh 445
def batch_orders(warehouseId):
446
    if not warehouseId:
447
        raise TransactionServiceException(101, "bad warehouse id")
1225 chandransh 448
 
449
    batchno_generator = BatchNoGenerator()
450
    session.commit()
1220 chandransh 451
    query = Order.query.filter_by(warehouse_id=warehouseId)
452
    query = query.filter_by(status=OrderStatus.SUBMITTED_FOR_PROCESSING)
453
    query = query.order_by(Order.created_timestamp)
454
    pending_orders = query.all()
1225 chandransh 455
 
1220 chandransh 456
    serial_no = 1
457
    for order in pending_orders:
1276 chandransh 458
        order.batchNo = batchno_generator.id
1220 chandransh 459
        order.serialNo = serial_no
460
        serial_no += 1
461
    session.commit() 
462
    pending_orders = query.all()
463
    return pending_orders
464
 
465
def order_outofstock(orderId):
1911 chandransh 466
    '''
467
    Mark order as out of stock
468
    '''
1220 chandransh 469
    order = get_order(orderId)
470
    order.status = OrderStatus.INVENTORY_LOW
471
    order.statusDescription = "Low Inventory"
472
    order.outofstock_timestamp = datetime.datetime.now()
473
    session.commit()
474
    return True
475
 
759 chandransh 476
def mark_orders_as_manifested(warehouse_id, provider_id):
477
    try:
986 chandransh 478
        current_timestamp = datetime.datetime.now()
852 chandransh 479
        orders = Order.query.filter_by(warehouse_id = warehouse_id, logistics_provider_id = provider_id, status = OrderStatus.BILLED).all()
759 chandransh 480
        for order in orders:
481
            order.status = OrderStatus.SHIPPED_FROM_WH
987 chandransh 482
            order.statusDescription = "Order shipped from warehouse"
986 chandransh 483
            order.shipping_timestamp = current_timestamp
759 chandransh 484
        session.commit()
485
        return True
486
    except:
487
        return False
1113 chandransh 488
 
489
def mark_orders_as_picked_up(provider_id, pickup_details):
490
    for awb, pickup_timestamp in pickup_details.iteritems():
1135 chandransh 491
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
1113 chandransh 492
        if order == None:
2220 chandransh 493
            #raise TransactionServiceException(102, "No order found for the awb: " + awb)
494
            continue
1113 chandransh 495
        order.status = OrderStatus.SHIPPED_TO_LOGST
496
        order.statusDescription = "Order picked up by Courier Company"
1246 chandransh 497
        order.pickup_timestamp = pickup_timestamp
1113 chandransh 498
    session.commit()
1217 chandransh 499
 
1132 chandransh 500
    current_time = datetime.datetime.now()
501
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
1246 chandransh 502
    from_datetime = to_datetime - datetime.timedelta(2)
1132 chandransh 503
    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 504
    return orders_not_picked_up
505
 
1132 chandransh 506
def mark_orders_as_delivered(provider_id, delivered_orders):
507
    for awb, detail in delivered_orders.iteritems():
1135 chandransh 508
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
1132 chandransh 509
        if order == None:
1911 chandransh 510
            continue
511
            #raise TransactionServiceException(103, "No order found for the awb:" + awb)
1132 chandransh 512
        order.status = OrderStatus.DELIVERY_SUCCESS
513
        order.statusDescription = "Order delivered"
514
        order.delivery_timestamp, order.receiver = detail.split('|')
515
    session.commit()
516
 
1135 chandransh 517
def mark_orders_as_failed(provider_id, returned_orders):
518
    for awb, detail in returned_orders.iteritems():
519
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
520
        if order == None:
521
            raise TransactionServiceException(103, "No order found for the awb:" + awb)
522
        order.status = OrderStatus.FAILED
523
        order.delivery_timestamp, reason = detail.split('|')
524
        order.statusDescription = "Order Returned to Origin:" + reason
1911 chandransh 525
    session.commit()
1135 chandransh 526
 
1246 chandransh 527
def update_non_delivery_reason(provider_id, undelivered_orders):
528
    for awb, reason in undelivered_orders.iteritems():
529
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
530
        if order == None:
531
            raise TransactionServiceException(103, "No order found for the awb:" + awb)
532
        order.statusDescription = reason
533
    session.commit()
534
 
483 rajveer 535
def get_alerts(order_id, all):
536
    query = Alerts.query.filter_by(order_id=order_id)
537
    if not all:
538
        query = query.filter_by(valid=True)
539
    return query.all()
540
 
541
def set_alert(order_id, unset, type, comment):
542
    #get existing alert
543
    if unset:
544
        query = Alerts.query.filter_by(order_id=order_id)
545
        query = query.filter_by(type=type)
546
        alerts = query.all()
547
        for alert in alerts:
548
            if alert.valid:
549
                alert.valid = False
550
                alert.comment = comment
551
                alert.time_unset = datetime.datetime.now()
552
    else:
553
        alert = Alerts()
554
        alert.order_id = order_id
555
        alert.valid = True
556
        alert.type = type
557
        alert.comment = comment
558
        alert.time_set = datetime.datetime.now()
766 rajveer 559
    session.commit()    
560
 
1596 ankur.sing 561
def get_valid_order_count():
562
    return Order.query.filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).count()
563
 
1627 ankur.sing 564
def get_cust_count_with_successful_txn():
565
    return session.query(Order.customer_id).filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).distinct().count()
566
 
1730 ankur.sing 567
def get_valid_orders_amount_range():
1627 ankur.sing 568
    orders = Order.query.filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).all()
569
    order_amounts = [o.total_amount for o in orders]
1730 ankur.sing 570
    return [min(order_amounts), max(order_amounts)]
1627 ankur.sing 571
 
1886 ankur.sing 572
def get_valid_orders(limit):
573
    query = Order.query.filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING)
574
    query = query.order_by(desc(Order.created_timestamp))
575
    if limit > 0:
576
        query = query.limit(limit)
577
    elif limit < 0:
578
        raise TransactionServiceException(101, "Invalid argument limit " + limit)
579
    return query.all()
580
 
2536 chandransh 581
def toggle_doa_flag(order_id):
582
    order = get_order(order_id)
583
    if(order.doaFlag):
584
        order.doaFlag = False
585
    else:
586
        order.doaFlag = True
587
    session.commit()
588
    return order.doaFlag
589
 
590
def request_pickup_number(order_id):
591
    order = get_order(order_id)
592
    if order.status != OrderStatus.DELIVERY_SUCCESS and order.status != OrderStatus.DOA_PICKUP_REQUESTED:
593
        return False
594
    from_user = 'cnc.center@shop2020.in'
595
    from_pwd = '5h0p2o2o'
596
 
597
    subject = "Pickup request"
598
    try:
599
        catalog_client = InventoryClient().get_client()
600
        warehouse = catalog_client.getWarehouse(order.warehouse_id)
601
 
602
        logistics_client = LogisticsClient().get_client()
603
        provider = logistics_client.getProvider(order.logistics_provider_id)
604
        to_addr = provider.email
605
        raw_message = '''
606
        Dear Sir/Madam,
607
 
2559 chandransh 608
        Kindly arrange a pickup today from %(customer_city)s. Pickup and delivery addresses are mentioned below.
2536 chandransh 609
 
610
        Pickup CODE: %(provider_code)s
611
        Total Weight: %(order_weight)s kg
612
 
2559 chandransh 613
        Pickup Address:
2536 chandransh 614
 
615
        %(customer_address)s
616
 
617
        Delivery Address:
618
 
619
        %(warehouse_executive)s
620
        %(warehouse_address)s
621
        PIN %(warehouse_pin)s
622
        Phone: %(warehouse_phone)s
623
 
624
        Thanks and Regards
625
        Anand Sinha
626
        '''
2559 chandransh 627
        message = dedent(raw_message) % { 'customer_city' : order.customer_city,
628
                                          'provider_code' : provider.accountNo,
2536 chandransh 629
                                          'order_weight' : str(order.total_weight),
630
                                          'customer_address' : __get_order_address(order),
631
                                          'warehouse_executive' : 'Parmod Kumar',
632
                                          'warehouse_address' : warehouse.location,
633
                                          'warehouse_pin' : warehouse.pincode,
634
                                          'warehouse_phone': '9971573026'}
635
        mail(from_user, from_pwd, to_addr, subject, message)
636
        order.status = OrderStatus.DOA_PICKUP_REQUESTED
637
        order.statusDescription = "Pick up requested for DOA"
638
        session.commit()
639
        return True
640
    except Exception as e:
641
        print sys.exc_info()[0]
642
        return False
643
 
644
def authorize_pickup(order_id, pickup_number):
645
    order = get_order(order_id)
646
    if order.status != OrderStatus.DOA_PICKUP_REQUESTED:
647
        return False
648
    from_user = 'help@shop2020.in'
649
    from_pwd = '5h0p2o2o'
650
    subject = 'Pickup details'
651
    filename = "/tmp/" +  str(order.id) +".pdf" 
652
    try:
653
        catalog_client = InventoryClient().get_client()
654
        warehouse = catalog_client.getWarehouse(order.warehouse_id)
655
 
656
        logistics_client = LogisticsClient().get_client()
657
        provider = logistics_client.getProvider(order.logistics_provider_id)
658
        provider_name = provider.name
659
 
660
        to_addr = order.customer_email
661
        today = date.today().strftime("%d-%B-%Y (%A)")
662
 
663
        raw_message = '''
664
        Dear Mr. %(customer_name)s,
665
 
666
        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.
667
        Take a printout of the attachment in this mail which contains the delivery address and paste it on the packed parcel.
668
        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.
669
 
670
        Pickup will happen from the below mentioned address:-
671
 
672
        %(customer_address)s
673
 
674
        Do let us know in case of any concerns
675
 
676
        Thanks & Regards
677
        Saholic Team
678
        '''
679
        message = dedent(raw_message) % {'customer_name' : order.customer_name,
680
                                         'order_id' : order_id,
681
                                         'provider_name' : provider_name,
682
                                         'date': today,
683
                                         'pickup_request_no' : pickup_number,
684
                                         'customer_address' : __get_order_address(order)}
685
        print message
686
        __generate_return_advice(order, warehouse, provider, filename)
687
        mail(from_user, from_pwd, to_addr, subject, message, get_attachment_part(filename))
688
        order.pickupRequestNo = pickup_number
689
        order.status = OrderStatus.DOA_RETURN_AUTHORIZED
690
        order.statusDescription = "DOA pick up authorized"
691
        session.commit()
692
        return True
693
    except Exception as e:
694
        print sys.exc_info()[0]
695
        return False
696
    finally:
697
        if os.path.exists(filename):
698
            os.remove(filename)
699
 
2616 chandransh 700
def receive_return(order_id):
2591 chandransh 701
    order = get_order(order_id)
2616 chandransh 702
    if order.status in [OrderStatus.DOA_RETURN_AUTHORIZED, OrderStatus.DOA_RETURN_IN_TRANSIT]:
703
        order.status = OrderStatus.DOA_RECEIVED
704
        order.statusDescription = "DOA package received"
705
        session.commit()
706
    elif order.status == OrderStatus.SALES_RETURN_IN_TRANSIT :
707
        order.status = OrderStatus.SALES_RET_RECEIVED
708
        order.statusDescription = "Sales return received"
709
        session.commit()
710
    else:
2591 chandransh 711
        return False
712
    return True
713
 
714
def validate_doa(order_id, is_valid):
715
    order = get_order(order_id)
2599 chandransh 716
    if order.status != OrderStatus.DOA_RECEIVED:
2591 chandransh 717
        return False
718
    if is_valid:
2609 chandransh 719
        order.status = OrderStatus.DOA_CERT_VALID
720
        order.statusDescription = "DOA Certificate Valid"
2591 chandransh 721
    else:
722
        order.status = OrderStatus.DOA_CERT_INVALID
723
        order.statusDescription = "DOA Certificate Invalid"
724
    session.commit()
725
    return True
726
 
2628 chandransh 727
def reship_order(order_id):
728
    """
729
    If the order is in SALES_RET_RECEIVED or DOA_CERT_INVALID state, it does the following:
730
        1. Creates a new order for processing in the BILLED state. All billing information is saved.
731
        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.
732
 
733
    If the order is in DOA_CERT_VALID state, it does the following:
734
        1. Creates a new order for processing in the SUBMITTED_FOR_PROCESSING state.
735
        2. Creates a return order for the warehouse executive to return the DOA material.
736
        3. Marks the current order as the final DOA_RESHIPPED state.
737
 
738
    Returns the id of the newly created order.
739
 
740
    Throws an exception if the order with the given id couldn't be found.
741
 
742
    Parameters:
743
     - orderId
744
    """
745
    order = get_order(order_id)
746
    if order.status == OrderStatus.SALES_RET_RECEIVED:
747
        new_order = __clone_order(order, True)
748
        order.status = OrderStatus.SALES_RET_RESHIPPED
749
        order.statusDescription = "Order Reshipped"
750
        session.commit()
751
    elif order.status == OrderStatus.DOA_CERT_INVALID:
752
        new_order = __clone_order(order, True)
753
        order.status = OrderStatus.DOA_INVALID_RESHIPPED
754
        order.statusDescription = "Order Reshipped"
755
        session.commit()
756
    elif order.status == OrderStatus.DOA_CERT_VALID:
757
        new_order = __clone_order(order, False)
758
        ret_order = __create_return_order(order)
759
        order.status = OrderStatus.DOA_RESHIPPED
760
        order.statusDescription = "Order Reshipped"
761
        session.commit()
762
    else:
763
        raise TransactionServiceException(112, "This order can't be reshipped")
764
 
765
    order.new_order_id = new_order.id
766
    session.commit()
767
 
768
    return new_order.id
769
 
770
def refund_order(order_id):
771
    """
772
    If the order is in SALES_RET_RECEIVED, DOA_CERT_VALID or DOA_CERT_INVALID state, it does the following:
773
        1. Creates a refund request for batch processing.
774
        2. Creates a return order for the warehouse executive to return the shipped material.
775
        3. Marks the current order as SALES_RET_REFUNDED, DOA_VALID_REFUNDED or DOA_INVALID_REFUNDED final states.
776
 
777
    If the order is in SUBMITTED_FOR_PROCESSING or INVENTORY_LOW state, it does the following:
778
        1. Creates a refund request for batch processing.
779
        2. Marks the current order as the REFUNDED final state.
780
 
781
    Returns True if it is successful.
782
 
783
    Throws an exception if the order with the given id couldn't be found.
784
 
785
    Parameters:
786
     - orderId
787
    """
788
    status_transition = {OrderStatus.SALES_RET_RECEIVED : OrderStatus.SALES_RET_REFUNDED,
789
                         OrderStatus.DOA_CERT_INVALID : OrderStatus.DOA_INVALID_REFUNDED,
790
                         OrderStatus.DOA_CERT_VALID : OrderStatus.DOA_VALID_REFUNDED,
791
                         OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.REFUNDED,
792
                         OrderStatus.INVENTORY_LOW : OrderStatus.REFUNDED
793
                         }
794
    order = get_order(order_id)
795
    if order.status in [OrderStatus.SALES_RET_RECEIVED, OrderStatus.DOA_CERT_INVALID, OrderStatus.DOA_CERT_VALID] :
796
        __create_return_order(order)
797
        __create_refund(order)
798
        order.status = status_transition[order.status]
799
        order.statusDescription = "Refunded"
800
    elif order.status in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW]:
801
        __create_refund(order)
802
        order.status = status_transition[order.status]
803
        order.statusDescription = "Refunded"
804
    else:
805
        raise TransactionServiceException(114, "This order can't be refunded")
806
 
807
    session.commit()
808
    return True
809
 
810
def __clone_order(order, should_copy_billing_info):
811
    current_time = datetime.datetime.now()
812
    new_order = Order()
813
    new_order.customer_id = order.customer_id
814
    new_order.customer_name = order.customer_name
815
    new_order.customer_city = order.customer_city
816
    new_order.customer_state = order.customer_state
817
    new_order.customer_mobilenumber = order.customer_mobilenumber
818
    new_order.customer_pincode = order.customer_pincode
819
    new_order.customer_address1 = order.customer_address1
820
    new_order.customer_address2 = order.customer_address2
821
    new_order.customer_email = order.customer_email
822
    new_order.total_amount = order.total_amount
823
    new_order.total_weight = order.total_weight
824
    new_order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
825
    new_order.statusDescription = 'Submitted for Processing'
826
    new_order.created_timestamp = current_time
827
 
828
    new_order.transaction = order.transaction
829
 
830
    for line_item in order.lineitems:
831
        litem = LineItem()
832
        litem.item_id = line_item.item_id
833
        litem.productGroup = line_item.productGroup
834
        litem.brand = line_item.brand
835
        if line_item.model_number:
836
            litem.model_number = line_item.model_number
837
        if line_item.model_name:    
838
            litem.model_name = line_item.model_name
839
        if line_item.color:
840
            litem.color = line_item.color
841
        if line_item.extra_info:
842
            litem.extra_info = line_item.extra_info
843
        litem.quantity = line_item.quantity
844
        litem.unit_price = line_item.unit_price
845
        litem.unit_weight = line_item.unit_weight
846
        litem.total_price = line_item.total_price
847
        litem.total_weight = line_item.total_weight
848
        litem.transfer_price = line_item.transfer_price
849
        litem.order = new_order
850
 
851
    logistics_client = LogisticsClient().get_client()
852
    item_id = new_order.lineitems[0].item_id
853
    logistics_info = logistics_client.getLogisticsInfo(new_order.customer_pincode, item_id) #TODO: We should be able to pass another flag to suggest ignoring the inventory situation.
854
    logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
855
 
856
    new_order.warehouse_id = logistics_info.warehouseId
857
    new_order.logistics_provider_id = logistics_info.providerId
858
    new_order.airwaybill_no = logistics_info.airway_billno
859
    new_order.tracking_id = new_order.airwaybill_no
860
    new_order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
861
 
862
    if should_copy_billing_info:
863
        new_order.invoice_number = order.invoice_number
864
        new_order.billed_by = order.billed_by
865
        new_order.warehouse_id = order.warehouse_id
866
        new_order.accepted_timestamp = current_time
867
        new_order.billing_timestamp = current_time    
868
        new_order.status = OrderStatus.BILLED
869
        new_order.statusDescription = 'Order Billed'
870
    else:       
871
        catalog_client = InventoryClient().get_client()
872
        catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, new_order.lineitems[0].quantity)
873
 
874
        item_pricing = catalog_client.getItemPricing(item_id, order.warehouse_id)
875
        new_order.lineitems[0].transfer_price = item_pricing.transferPrice
876
 
877
    return new_order
878
 
879
def __create_return_order(order):
880
    ret_order = ReturnOrder()
881
    ret_order.order_id = order.id
882
    ret_order.jacket_number = order.jacket_number
883
    ret_order.invoice_number = order.invoice_number
884
 
885
    line_item = order.lineitems[0]
886
    ret_order.item_id = line_item.item_id
887
    ret_order.productGroup = line_item.productGroup
888
    ret_order.brand = line_item.brand
889
    ret_order.model_number = line_item.model_number
890
    ret_order.model_name = line_item.model_name
891
    ret_order.color = line_item.color
892
    ret_order.total_price = line_item.total_price
893
    ret_order.transfer_price = line_item.transfer_price
2631 chandransh 894
    ret_order.processed_status = False
2628 chandransh 895
    ret_order.created_timestamp = datetime.datetime.now()
896
    return ret_order
897
 
898
def __create_refund(order):
899
    pass
900
 
2536 chandransh 901
def __get_order_address(order):
902
    address = order.customer_name + "\n" + order.customer_address1 + "\n"
903
    if order.customer_address2:
904
        address = address + order.customer_address2 + "\n"
905
    address = address + order.customer_city + "\n"
906
    address = address + order.customer_state + "\n"
907
    address = address + "PIN " + order.customer_pincode + "\n"
908
    address = address + "Phone: " + order.customer_mobilenumber
909
    return address
910
 
911
def __generate_return_advice(order, warehouse, provider, filename):    
912
    pdf = Canvas(filename)
913
    pdf.setFont('Times-Bold',16)
914
    address_text = pdf.beginText(inch, PAGE_HEIGHT - inch)
915
    address_text.textLine("To")
916
    address_text.textLine("Parmod Kumar")
917
    for line in warehouse.location.split("\n"):
918
        address_text.textLine(line)
919
    address_text.textLine("PIN " + warehouse.pincode)
920
    address_text.textLine("")
921
    address_text.textLine("Phone: 9971573026")
922
    pdf.drawText(address_text)
923
 
924
    pdf.setFont('Times-Roman',12)
925
    order_text = pdf.beginText(inch, PAGE_HEIGHT - 4 * inch)
926
    order_text.textLine("Pickup CODE: " + provider.accountNo)
927
    lineitem = order.lineitems[0] 
928
    order_text.textLine("Content : " + lineitem.brand + " " + lineitem.model_number + " " + lineitem.color)
929
    order_text.textLine("Declared Value: Rs. " + str(order.total_amount))
930
    order_text.textLine("Order ID : " + str(order.id))
931
    pdf.drawText(order_text)
932
    pdf.showPage()
933
    pdf.save()
934
    return filename
935
 
766 rajveer 936
def close_session():
937
    if session.is_active:
938
        print "session is active. closing it."
939
        session.close()