Subversion Repositories SmartDukaan

Rev

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