Subversion Repositories SmartDukaan

Rev

Rev 2764 | Rev 2815 | 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,\
2783 chandransh 29
    Alerts, Order, BatchNoGenerator, InvoiceIDGenerator
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:
2783 chandransh 433
        raise TransactionServiceException(101, "No order found for the given order id:" + str(orderId))
494 rajveer 434
    order.invoice_number = invoice_number
435
    order.billed_by = billed_by
436
    session.commit()
437
    return True
759 chandransh 438
 
2783 chandransh 439
def add_jacket_number(self, orderId, jacket_number, imei_number, item_number, billed_by):
440
    if jacket_number is None or jacket_number <= 0:
441
        raise TransactionServiceException(110, "Invalid jacket number")
442
 
443
    if item_number is None or item_number.strip() == "":
444
        raise TransactionServiceException(110, "Invalid item number")
445
 
446
    if billed_by is None or billed_by.strip() == "":
447
        raise TransactionServiceException(110, "Invalid Biller")
448
 
1149 chandransh 449
    order = Order.get_by(id=orderId)
450
    if not order:
2783 chandransh 451
        raise TransactionServiceException(101, "No order found for the given order id" + str(orderId))
452
 
453
    lineitem = order.lineitems[0]
454
    if lineitem.productGroup == "Handsets" and imei_number <= 0:
455
        raise TransactionServiceException(110, "No IMEI number supplied for a handset.")
456
 
457
    order.jacket_number = jacket_number
458
    order.invoice_number = get_next_invoice_number()
459
    order.billed_by = billed_by
460
    order.status = OrderStatus.BILLED
461
    order.statusDescription = "Order Billed"
462
    order.billing_timestamp = datetime.datetime.now()
463
 
464
    lineitem.item_number = item_number
465
    if imei_number > 0:
466
        lineitem.imei_number = str(imei_number)
467
 
1149 chandransh 468
    session.commit()
469
    return True
470
 
1220 chandransh 471
def batch_orders(warehouseId):
472
    if not warehouseId:
473
        raise TransactionServiceException(101, "bad warehouse id")
1225 chandransh 474
 
475
    batchno_generator = BatchNoGenerator()
476
    session.commit()
1220 chandransh 477
    query = Order.query.filter_by(warehouse_id=warehouseId)
478
    query = query.filter_by(status=OrderStatus.SUBMITTED_FOR_PROCESSING)
479
    query = query.order_by(Order.created_timestamp)
480
    pending_orders = query.all()
1225 chandransh 481
 
1220 chandransh 482
    serial_no = 1
483
    for order in pending_orders:
1276 chandransh 484
        order.batchNo = batchno_generator.id
1220 chandransh 485
        order.serialNo = serial_no
486
        serial_no += 1
487
    session.commit() 
488
    pending_orders = query.all()
489
    return pending_orders
490
 
491
def order_outofstock(orderId):
1911 chandransh 492
    '''
493
    Mark order as out of stock
494
    '''
1220 chandransh 495
    order = get_order(orderId)
496
    order.status = OrderStatus.INVENTORY_LOW
497
    order.statusDescription = "Low Inventory"
498
    order.outofstock_timestamp = datetime.datetime.now()
499
    session.commit()
500
    return True
501
 
759 chandransh 502
def mark_orders_as_manifested(warehouse_id, provider_id):
503
    try:
986 chandransh 504
        current_timestamp = datetime.datetime.now()
852 chandransh 505
        orders = Order.query.filter_by(warehouse_id = warehouse_id, logistics_provider_id = provider_id, status = OrderStatus.BILLED).all()
759 chandransh 506
        for order in orders:
507
            order.status = OrderStatus.SHIPPED_FROM_WH
987 chandransh 508
            order.statusDescription = "Order shipped from warehouse"
986 chandransh 509
            order.shipping_timestamp = current_timestamp
759 chandransh 510
        session.commit()
511
        return True
512
    except:
513
        return False
1113 chandransh 514
 
515
def mark_orders_as_picked_up(provider_id, pickup_details):
516
    for awb, pickup_timestamp in pickup_details.iteritems():
1135 chandransh 517
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
2684 chandransh 518
        if order == None or order.status != OrderStatus.SHIPPED_FROM_WH:
2220 chandransh 519
            #raise TransactionServiceException(102, "No order found for the awb: " + awb)
520
            continue
1113 chandransh 521
        order.status = OrderStatus.SHIPPED_TO_LOGST
522
        order.statusDescription = "Order picked up by Courier Company"
1246 chandransh 523
        order.pickup_timestamp = pickup_timestamp
1113 chandransh 524
    session.commit()
1217 chandransh 525
 
1132 chandransh 526
    current_time = datetime.datetime.now()
527
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
1246 chandransh 528
    from_datetime = to_datetime - datetime.timedelta(2)
1132 chandransh 529
    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 530
    return orders_not_picked_up
531
 
1132 chandransh 532
def mark_orders_as_delivered(provider_id, delivered_orders):
533
    for awb, detail in delivered_orders.iteritems():
1135 chandransh 534
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
2684 chandransh 535
        if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST]:
1911 chandransh 536
            continue
537
            #raise TransactionServiceException(103, "No order found for the awb:" + awb)
1132 chandransh 538
        order.status = OrderStatus.DELIVERY_SUCCESS
539
        order.statusDescription = "Order delivered"
540
        order.delivery_timestamp, order.receiver = detail.split('|')
541
    session.commit()
542
 
1135 chandransh 543
def mark_orders_as_failed(provider_id, returned_orders):
544
    for awb, detail in returned_orders.iteritems():
545
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
2684 chandransh 546
        if order == None or order.status not in [OrderStatus.SHIPPED_FROM_WH, OrderStatus.SHIPPED_TO_LOGST]:
547
            continue
548
            #raise TransactionServiceException(103, "No order found for the awb:" + awb)
549
        order.status = OrderStatus.SALES_RETURN_IN_TRANSIT
1135 chandransh 550
        order.delivery_timestamp, reason = detail.split('|')
551
        order.statusDescription = "Order Returned to Origin:" + reason
1911 chandransh 552
    session.commit()
1135 chandransh 553
 
1246 chandransh 554
def update_non_delivery_reason(provider_id, undelivered_orders):
555
    for awb, reason in undelivered_orders.iteritems():
556
        order = Order.query.filter_by(airwaybill_no=awb, logistics_provider_id = provider_id).first()
557
        if order == None:
2684 chandransh 558
            continue
559
            #raise TransactionServiceException(103, "No order found for the awb:" + awb)
1246 chandransh 560
        order.statusDescription = reason
561
    session.commit()
562
 
483 rajveer 563
def get_alerts(order_id, all):
564
    query = Alerts.query.filter_by(order_id=order_id)
565
    if not all:
566
        query = query.filter_by(valid=True)
567
    return query.all()
568
 
569
def set_alert(order_id, unset, type, comment):
570
    #get existing alert
571
    if unset:
572
        query = Alerts.query.filter_by(order_id=order_id)
573
        query = query.filter_by(type=type)
574
        alerts = query.all()
575
        for alert in alerts:
576
            if alert.valid:
577
                alert.valid = False
578
                alert.comment = comment
579
                alert.time_unset = datetime.datetime.now()
580
    else:
581
        alert = Alerts()
582
        alert.order_id = order_id
583
        alert.valid = True
584
        alert.type = type
585
        alert.comment = comment
586
        alert.time_set = datetime.datetime.now()
766 rajveer 587
    session.commit()    
588
 
1596 ankur.sing 589
def get_valid_order_count():
590
    return Order.query.filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).count()
591
 
1627 ankur.sing 592
def get_cust_count_with_successful_txn():
593
    return session.query(Order.customer_id).filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).distinct().count()
594
 
1730 ankur.sing 595
def get_valid_orders_amount_range():
1627 ankur.sing 596
    orders = Order.query.filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING).all()
597
    order_amounts = [o.total_amount for o in orders]
1730 ankur.sing 598
    return [min(order_amounts), max(order_amounts)]
1627 ankur.sing 599
 
1886 ankur.sing 600
def get_valid_orders(limit):
601
    query = Order.query.filter(Order.status >= OrderStatus.SUBMITTED_FOR_PROCESSING)
602
    query = query.order_by(desc(Order.created_timestamp))
603
    if limit > 0:
604
        query = query.limit(limit)
605
    elif limit < 0:
606
        raise TransactionServiceException(101, "Invalid argument limit " + limit)
607
    return query.all()
608
 
2783 chandransh 609
def get_next_invoice_number():
610
    entity_id = InvoiceIDGenerator.query.with_lockmode("update").one()
611
    invoice_number = entity_id.id + 1
612
    entity_id.id = invoice_number
613
    session.commit()
614
    return invoice_number
615
 
2536 chandransh 616
def toggle_doa_flag(order_id):
617
    order = get_order(order_id)
618
    if(order.doaFlag):
619
        order.doaFlag = False
620
    else:
621
        order.doaFlag = True
622
    session.commit()
623
    return order.doaFlag
624
 
625
def request_pickup_number(order_id):
626
    order = get_order(order_id)
627
    if order.status != OrderStatus.DELIVERY_SUCCESS and order.status != OrderStatus.DOA_PICKUP_REQUESTED:
628
        return False
629
    from_user = 'cnc.center@shop2020.in'
630
    from_pwd = '5h0p2o2o'
631
 
632
    subject = "Pickup request"
633
    try:
634
        catalog_client = InventoryClient().get_client()
635
        warehouse = catalog_client.getWarehouse(order.warehouse_id)
636
 
637
        logistics_client = LogisticsClient().get_client()
638
        provider = logistics_client.getProvider(order.logistics_provider_id)
639
        to_addr = provider.email
640
        raw_message = '''
641
        Dear Sir/Madam,
642
 
2559 chandransh 643
        Kindly arrange a pickup today from %(customer_city)s. Pickup and delivery addresses are mentioned below.
2536 chandransh 644
 
645
        Pickup CODE: %(provider_code)s
646
        Total Weight: %(order_weight)s kg
647
 
2559 chandransh 648
        Pickup Address:
2536 chandransh 649
 
650
        %(customer_address)s
651
 
652
        Delivery Address:
653
 
654
        %(warehouse_executive)s
655
        %(warehouse_address)s
656
        PIN %(warehouse_pin)s
657
        Phone: %(warehouse_phone)s
658
 
659
        Thanks and Regards
660
        Anand Sinha
661
        '''
2559 chandransh 662
        message = dedent(raw_message) % { 'customer_city' : order.customer_city,
663
                                          'provider_code' : provider.accountNo,
2536 chandransh 664
                                          'order_weight' : str(order.total_weight),
665
                                          'customer_address' : __get_order_address(order),
666
                                          'warehouse_executive' : 'Parmod Kumar',
667
                                          'warehouse_address' : warehouse.location,
668
                                          'warehouse_pin' : warehouse.pincode,
669
                                          'warehouse_phone': '9971573026'}
670
        mail(from_user, from_pwd, to_addr, subject, message)
671
        order.status = OrderStatus.DOA_PICKUP_REQUESTED
672
        order.statusDescription = "Pick up requested for DOA"
673
        session.commit()
674
        return True
675
    except Exception as e:
676
        print sys.exc_info()[0]
677
        return False
678
 
679
def authorize_pickup(order_id, pickup_number):
680
    order = get_order(order_id)
681
    if order.status != OrderStatus.DOA_PICKUP_REQUESTED:
682
        return False
683
    from_user = 'help@shop2020.in'
684
    from_pwd = '5h0p2o2o'
685
    subject = 'Pickup details'
686
    filename = "/tmp/" +  str(order.id) +".pdf" 
687
    try:
688
        catalog_client = InventoryClient().get_client()
689
        warehouse = catalog_client.getWarehouse(order.warehouse_id)
690
 
691
        logistics_client = LogisticsClient().get_client()
692
        provider = logistics_client.getProvider(order.logistics_provider_id)
693
        provider_name = provider.name
694
 
695
        to_addr = order.customer_email
696
        today = date.today().strftime("%d-%B-%Y (%A)")
697
 
698
        raw_message = '''
699
        Dear Mr. %(customer_name)s,
700
 
701
        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.
702
        Take a printout of the attachment in this mail which contains the delivery address and paste it on the packed parcel.
703
        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.
704
 
705
        Pickup will happen from the below mentioned address:-
706
 
707
        %(customer_address)s
708
 
709
        Do let us know in case of any concerns
710
 
711
        Thanks & Regards
712
        Saholic Team
713
        '''
714
        message = dedent(raw_message) % {'customer_name' : order.customer_name,
715
                                         'order_id' : order_id,
716
                                         'provider_name' : provider_name,
717
                                         'date': today,
718
                                         'pickup_request_no' : pickup_number,
719
                                         'customer_address' : __get_order_address(order)}
720
        print message
721
        __generate_return_advice(order, warehouse, provider, filename)
722
        mail(from_user, from_pwd, to_addr, subject, message, get_attachment_part(filename))
723
        order.pickupRequestNo = pickup_number
724
        order.status = OrderStatus.DOA_RETURN_AUTHORIZED
725
        order.statusDescription = "DOA pick up authorized"
2764 chandransh 726
        order.doa_auth_timestamp = datetime.datetime.now()
2536 chandransh 727
        session.commit()
728
        return True
729
    except Exception as e:
730
        print sys.exc_info()[0]
731
        return False
732
    finally:
733
        if os.path.exists(filename):
734
            os.remove(filename)
735
 
2764 chandransh 736
def mark_doas_as_picked_up(provider_id, pickup_details):
737
    for pickupNo, doa_pickup_timestamp in pickup_details.iteritems():
738
        order = Order.query.filter_by(pickupRequestNo=pickupNo, logistics_provider_id = provider_id).first()
739
        if order == None or order.status != OrderStatus.DOA_RETURN_AUTHORIZED:
740
            #raise TransactionServiceException(102, "No order found for the awb: " + awb)
741
            continue
742
        order.status = OrderStatus.DOA_RETURN_IN_TRANSIT
743
        order.statusDescription = "DOA picked up by Courier Company"
744
        order.doa_pickup_timestamp = doa_pickup_timestamp
745
    session.commit()
746
 
747
    current_time = datetime.datetime.now()
748
    to_datetime = datetime.datetime(current_time.year, current_time.month, current_time.day)
749
    from_datetime = to_datetime - datetime.timedelta(2)
750
    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()
751
    return orders_not_picked_up
752
 
2616 chandransh 753
def receive_return(order_id):
2591 chandransh 754
    order = get_order(order_id)
2616 chandransh 755
    if order.status in [OrderStatus.DOA_RETURN_AUTHORIZED, OrderStatus.DOA_RETURN_IN_TRANSIT]:
756
        order.status = OrderStatus.DOA_RECEIVED
757
        order.statusDescription = "DOA package received"
2764 chandransh 758
        order.received_return_timestamp = datetime.datetime.now()
2616 chandransh 759
        session.commit()
760
    elif order.status == OrderStatus.SALES_RETURN_IN_TRANSIT :
761
        order.status = OrderStatus.SALES_RET_RECEIVED
762
        order.statusDescription = "Sales return received"
2764 chandransh 763
        order.received_return_timestamp = datetime.datetime.now()
2616 chandransh 764
        session.commit()
765
    else:
2591 chandransh 766
        return False
767
    return True
768
 
769
def validate_doa(order_id, is_valid):
770
    order = get_order(order_id)
2599 chandransh 771
    if order.status != OrderStatus.DOA_RECEIVED:
2591 chandransh 772
        return False
773
    if is_valid:
2609 chandransh 774
        order.status = OrderStatus.DOA_CERT_VALID
775
        order.statusDescription = "DOA Certificate Valid"
2591 chandransh 776
    else:
777
        order.status = OrderStatus.DOA_CERT_INVALID
778
        order.statusDescription = "DOA Certificate Invalid"
779
    session.commit()
780
    return True
781
 
2628 chandransh 782
def reship_order(order_id):
783
    """
784
    If the order is in SALES_RET_RECEIVED or DOA_CERT_INVALID state, it does the following:
785
        1. Creates a new order for processing in the BILLED state. All billing information is saved.
786
        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.
787
 
788
    If the order is in DOA_CERT_VALID state, it does the following:
789
        1. Creates a new order for processing in the SUBMITTED_FOR_PROCESSING state.
790
        2. Creates a return order for the warehouse executive to return the DOA material.
791
        3. Marks the current order as the final DOA_RESHIPPED state.
792
 
793
    Returns the id of the newly created order.
794
 
795
    Throws an exception if the order with the given id couldn't be found.
796
 
797
    Parameters:
798
     - orderId
799
    """
800
    order = get_order(order_id)
801
    if order.status == OrderStatus.SALES_RET_RECEIVED:
802
        new_order = __clone_order(order, True)
803
        order.status = OrderStatus.SALES_RET_RESHIPPED
804
        order.statusDescription = "Order Reshipped"
805
        session.commit()
806
    elif order.status == OrderStatus.DOA_CERT_INVALID:
807
        new_order = __clone_order(order, True)
808
        order.status = OrderStatus.DOA_INVALID_RESHIPPED
809
        order.statusDescription = "Order Reshipped"
810
        session.commit()
811
    elif order.status == OrderStatus.DOA_CERT_VALID:
812
        new_order = __clone_order(order, False)
813
        ret_order = __create_return_order(order)
814
        order.status = OrderStatus.DOA_RESHIPPED
815
        order.statusDescription = "Order Reshipped"
816
        session.commit()
817
    else:
818
        raise TransactionServiceException(112, "This order can't be reshipped")
819
 
2764 chandransh 820
    order.reship_timestamp = datetime.datetime.now()
2628 chandransh 821
    order.new_order_id = new_order.id
822
    session.commit()
823
 
824
    return new_order.id
825
 
826
def refund_order(order_id):
827
    """
828
    If the order is in SALES_RET_RECEIVED, DOA_CERT_VALID or DOA_CERT_INVALID state, it does the following:
829
        1. Creates a refund request for batch processing.
830
        2. Creates a return order for the warehouse executive to return the shipped material.
831
        3. Marks the current order as SALES_RET_REFUNDED, DOA_VALID_REFUNDED or DOA_INVALID_REFUNDED final states.
832
 
833
    If the order is in SUBMITTED_FOR_PROCESSING or INVENTORY_LOW state, it does the following:
834
        1. Creates a refund request for batch processing.
835
        2. Marks the current order as the REFUNDED final state.
836
 
837
    Returns True if it is successful.
838
 
839
    Throws an exception if the order with the given id couldn't be found.
840
 
841
    Parameters:
842
     - orderId
843
    """
844
    status_transition = {OrderStatus.SALES_RET_RECEIVED : OrderStatus.SALES_RET_REFUNDED,
845
                         OrderStatus.DOA_CERT_INVALID : OrderStatus.DOA_INVALID_REFUNDED,
846
                         OrderStatus.DOA_CERT_VALID : OrderStatus.DOA_VALID_REFUNDED,
847
                         OrderStatus.SUBMITTED_FOR_PROCESSING : OrderStatus.REFUNDED,
848
                         OrderStatus.INVENTORY_LOW : OrderStatus.REFUNDED
849
                         }
850
    order = get_order(order_id)
851
    if order.status in [OrderStatus.SALES_RET_RECEIVED, OrderStatus.DOA_CERT_INVALID, OrderStatus.DOA_CERT_VALID] :
852
        __create_return_order(order)
853
        __create_refund(order)
854
        order.status = status_transition[order.status]
855
        order.statusDescription = "Refunded"
856
    elif order.status in [OrderStatus.SUBMITTED_FOR_PROCESSING, OrderStatus.INVENTORY_LOW]:
857
        __create_refund(order)
2687 chandransh 858
        try:
859
            #Reduce the reservation count for all lineitems.
860
            catalog_client = InventoryClient().get_client()
861
            for lineitem in order.lineitems:
862
                catalog_client.reduceReservationCount(lineitem.item_id, order.warehouse_id, lineitem.quantity);
863
        except:
864
            print "Unable to reduce reservation count"
2628 chandransh 865
        order.status = status_transition[order.status]
866
        order.statusDescription = "Refunded"
867
    else:
868
        raise TransactionServiceException(114, "This order can't be refunded")
869
 
2764 chandransh 870
    order.refund_timestamp = datetime.datetime.now()
2628 chandransh 871
    session.commit()
872
    return True
873
 
874
def __clone_order(order, should_copy_billing_info):
875
    current_time = datetime.datetime.now()
876
    new_order = Order()
877
    new_order.customer_id = order.customer_id
878
    new_order.customer_name = order.customer_name
879
    new_order.customer_city = order.customer_city
880
    new_order.customer_state = order.customer_state
881
    new_order.customer_mobilenumber = order.customer_mobilenumber
882
    new_order.customer_pincode = order.customer_pincode
883
    new_order.customer_address1 = order.customer_address1
884
    new_order.customer_address2 = order.customer_address2
885
    new_order.customer_email = order.customer_email
886
    new_order.total_amount = order.total_amount
887
    new_order.total_weight = order.total_weight
888
    new_order.status = OrderStatus.SUBMITTED_FOR_PROCESSING
889
    new_order.statusDescription = 'Submitted for Processing'
890
    new_order.created_timestamp = current_time
891
 
892
    new_order.transaction = order.transaction
893
 
894
    for line_item in order.lineitems:
895
        litem = LineItem()
896
        litem.item_id = line_item.item_id
897
        litem.productGroup = line_item.productGroup
898
        litem.brand = line_item.brand
899
        if line_item.model_number:
900
            litem.model_number = line_item.model_number
901
        if line_item.model_name:    
902
            litem.model_name = line_item.model_name
903
        if line_item.color:
904
            litem.color = line_item.color
905
        if line_item.extra_info:
906
            litem.extra_info = line_item.extra_info
907
        litem.quantity = line_item.quantity
908
        litem.unit_price = line_item.unit_price
909
        litem.unit_weight = line_item.unit_weight
910
        litem.total_price = line_item.total_price
911
        litem.total_weight = line_item.total_weight
912
        litem.transfer_price = line_item.transfer_price
913
        litem.order = new_order
914
 
915
    logistics_client = LogisticsClient().get_client()
916
    item_id = new_order.lineitems[0].item_id
917
    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.
918
    logistics_info.deliveryTime = adjust_delivery_time(current_time, logistics_info.deliveryTime)
919
 
920
    new_order.warehouse_id = logistics_info.warehouseId
921
    new_order.logistics_provider_id = logistics_info.providerId
922
    new_order.airwaybill_no = logistics_info.airway_billno
923
    new_order.tracking_id = new_order.airwaybill_no
924
    new_order.expected_delivery_time = current_time + datetime.timedelta(days=logistics_info.deliveryTime)
925
 
926
    if should_copy_billing_info:
927
        new_order.invoice_number = order.invoice_number
928
        new_order.billed_by = order.billed_by
929
        new_order.warehouse_id = order.warehouse_id
930
        new_order.accepted_timestamp = current_time
931
        new_order.billing_timestamp = current_time    
932
        new_order.status = OrderStatus.BILLED
933
        new_order.statusDescription = 'Order Billed'
934
    else:       
935
        catalog_client = InventoryClient().get_client()
936
        catalog_client.reserveItemInWarehouse(item_id, logistics_info.warehouseId, new_order.lineitems[0].quantity)
937
 
938
        item_pricing = catalog_client.getItemPricing(item_id, order.warehouse_id)
939
        new_order.lineitems[0].transfer_price = item_pricing.transferPrice
940
 
941
    return new_order
942
 
943
def __create_return_order(order):
2687 chandransh 944
    ret_order = ReturnOrder(order)
2628 chandransh 945
    return ret_order
946
 
947
def __create_refund(order):
2747 chandransh 948
    payment_client = PaymentClient().get_client()
949
    payment_client.createRefund(order.id, order.transaction.id, order.total_amount)
950
    return
2628 chandransh 951
 
2536 chandransh 952
def __get_order_address(order):
953
    address = order.customer_name + "\n" + order.customer_address1 + "\n"
954
    if order.customer_address2:
955
        address = address + order.customer_address2 + "\n"
956
    address = address + order.customer_city + "\n"
957
    address = address + order.customer_state + "\n"
958
    address = address + "PIN " + order.customer_pincode + "\n"
959
    address = address + "Phone: " + order.customer_mobilenumber
960
    return address
961
 
962
def __generate_return_advice(order, warehouse, provider, filename):    
963
    pdf = Canvas(filename)
964
    pdf.setFont('Times-Bold',16)
965
    address_text = pdf.beginText(inch, PAGE_HEIGHT - inch)
966
    address_text.textLine("To")
967
    address_text.textLine("Parmod Kumar")
968
    for line in warehouse.location.split("\n"):
969
        address_text.textLine(line)
970
    address_text.textLine("PIN " + warehouse.pincode)
971
    address_text.textLine("")
972
    address_text.textLine("Phone: 9971573026")
973
    pdf.drawText(address_text)
974
 
975
    pdf.setFont('Times-Roman',12)
976
    order_text = pdf.beginText(inch, PAGE_HEIGHT - 4 * inch)
977
    order_text.textLine("Pickup CODE: " + provider.accountNo)
978
    lineitem = order.lineitems[0] 
979
    order_text.textLine("Content : " + lineitem.brand + " " + lineitem.model_number + " " + lineitem.color)
980
    order_text.textLine("Declared Value: Rs. " + str(order.total_amount))
981
    order_text.textLine("Order ID : " + str(order.id))
982
    pdf.drawText(order_text)
983
    pdf.showPage()
984
    pdf.save()
985
    return filename
986
 
2697 chandransh 987
def get_return_orders(warehouse_id, from_date, to_date):
988
    query = ReturnOrder.query.filter_by(processedStatus = False)
989
    if warehouse_id != -1:
990
        query = query.filter_by(warehouseId = warehouse_id)
991
    if from_date:
992
        query = query.filter(Order.created_timestamp >from_date)
993
    if to_date:
994
        query = query.filter(Order.created_timestamp < to_date)
995
    return [return_order.to_thrift_object() for return_order in query.all()]
996
 
2700 chandransh 997
def get_return_order(id):
998
    ret_order = ReturnOrder.get_by(orderId = id)
999
    if ret_order is None:
1000
        raise TransactionServiceException(116, "No return order found for the given order id")
1001
    return ret_order.to_thrift_object()
1002
 
2697 chandransh 1003
def process_return(ret_order_id):
1004
    ret_order = ReturnOrder.get_by(orderId = ret_order_id)
1005
    if ret_order is None:
1006
        raise TransactionServiceException(116, "No return order found for the given order id")
1007
    ret_order.processedStatus = True
2701 chandransh 1008
    ret_order.processedAt = datetime.datetime.now()
2697 chandransh 1009
    session.commit()
1010
 
766 rajveer 1011
def close_session():
1012
    if session.is_active:
1013
        print "session is active. closing it."
2783 chandransh 1014
        session.close()