Subversion Repositories SmartDukaan

Rev

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