Subversion Repositories SmartDukaan

Rev

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