Subversion Repositories SmartDukaan

Rev

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