Subversion Repositories SmartDukaan

Rev

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