Subversion Repositories SmartDukaan

Rev

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