· 8 years ago · Jan 23, 2018, 08:02 AM
1from __future__ import print_function
2
3import StringIO
4import json
5import os
6import itertools
7import traceback
8import errno
9import zipfile
10
11import pdfkit
12import string
13
14import pytz
15import xlrd
16import xlsxwriter
17import time
18
19from django.db import transaction
20from datetime import datetime, timedelta
21from dateutil.parser import parse
22from django.conf import settings
23from django.core import serializers
24from django.core.serializers.json import DjangoJSONEncoder
25from global_utils.databases import connection, in_database
26from collections import OrderedDict
27
28from django.utils.dateparse import parse_date
29
30from app.customers_related_confs import CUST_FIELDS
31from base.views.decorator import role_function_check
32from base.views._base import *
33
34from invoice.utils import invoice_mongo_utils
35
36from global_utils import aws_utils
37
38from profitability_calculation import get_customer_master_rate, store_profitability_in_order
39from customer_apis import get_customer_custom_field
40from base.views import constants
41from django.core.exceptions import ObjectDoesNotExist
42from base.views.orders import update_multiple_order_inv_status_func, update_order_invoice_status
43from messageQueue.factory import QueueFactory
44from PyPDF2 import PdfFileReader, PdfFileMerger, PdfFileWriter
45from django.template.loader import render_to_string
46from copy import deepcopy
47from base.views.service_tax import calculate_service_tax
48from base.views.license import get_fssai_license_number
49from base.views.number_2_words import Number2Words
50from courierTrack.views.envelope_operations import check_invoice_existing_envelope
51from xlrd import XLRDError
52import app.settings as settings
53from base.views import invoicing_report
54
55from base.service.audit_trail_service import AuditTrailService, AuditTrailConstants
56
57from elasticSearch.utils import get_order_information
58from invoice import invoice_config
59
60from base.views.utils import BaseClass
61from base.models import Order
62utils = BaseClass()
63
64@role_function_check("FINALIZE_INVOICE")
65def finalize_invoice_data(request, extra_info='', fields='', filters='', external='', nested=False):
66 '''
67 This API add/update the orders invoices to given data as well as set is_finalized
68 If is_finalized is true than for invoice_generation and profitability_calculation
69 data = {loading_detention,unloading_detention,loading_charge,unloading_charge}
70 inv_details are used otherwise it will b picked from customer master rate data
71 '''
72 status_msg, resp_status, result = '', 0, { }
73 request_body = utils.parse_request_body(request)
74 request_body['finalized_by'] = request.user
75 request_body['is_finalized'] = 1
76 request.internal = True
77 return update_invoice_data(request, fields=request_body)
78
79
80@role_function_check("UPDATE_RECEIVABLE")
81def update_invoice_data(request, extra_info='', fields='', filters='', external='', nested=False):
82 '''
83 This API add/update the orders invoices to given data
84 '''
85
86 status_msg, resp_status, result = '', 0, { }
87 user, order_inv_obj = None, { }
88 if isinstance(extra_info, dict):
89 user = extra_info.get('user')
90 user = user or request.user
91 mandatory_fields = []
92 current_utc_time = utils.current_time()
93 ignore_fields, update, order = [], False, None
94 mandatory_fields = ['order_id', 'lr_number', 'num_pkgs', 'invoice_number',
95 'weight', 'src_detention_days',
96 'dest_detention_days', 'truck_type']
97 date_of_arrival, date_of_delivery = -1, -1
98 '''
99 fields Will be used for finalized API
100 '''
101 if fields:
102 request_body = fields
103 else:
104 # Parse request body if not fields
105 request_body = utils.parse_request_body(request)
106 pending_status = ('Order Processing', 'Order Accepted', 'Order Blocked')
107 if request_body.get('is_finalized') and utils.get_digit(request_body.get('freight_amount')) <= 0:
108 resp_status = 1
109 status_msg = "For finalizing invoice detail please enter freight amount, loading/unloading and detention properly, bcoz these thing will not be picked up from system"
110 return status_msg, resp_status, result
111 elif not request_body.get('is_finalized'):
112 request_body.pop('freight_amount', None)
113
114 if request_body.has_key('order_id'):
115 request_body['order'] = request_body['order_id']
116 try:
117 order = Order.objects.get(pk=request_body['order'])
118 except Order.DoesNotExist:
119 resp_status = 1
120 status_msg = "Please Provide a valid order"
121 return status_msg, resp_status, result
122
123 if request_body.get('id', 0):
124 update = True
125 result['order_invoice_id'] = request_body.get('id')
126 order_inv_obj = utils.get_exact_match(OrderInvoiceRelatedInfo, request_body.get('id'))
127 if not order_inv_obj:
128 resp_status = 1
129 status_msg = "No Object found with id %s" % request_body.get('id')
130 return status_msg, resp_status, result
131 if not order:
132 order = order_inv_obj.order
133 '''
134 If multiple loading or unloading then charges are compulsary
135 '''
136 if order.num_loading_points > 1:
137 mandatory_fields.append('multiple_loading_pt_charge')
138 if order.num_unloading_points > 1:
139 mandatory_fields.append('multiple_unloading_pt_charge')
140
141 if order_inv_obj:
142 for field in mandatory_fields:
143 attr = getattr(order_inv_obj, field)
144 if attr is not None and attr != "":
145 ignore_fields.append(field)
146 '''
147 Few details used for invoicing are entered during order creation
148 So generate a new lr_number and associat it with inv_data as lr_number is unique fields
149 '''
150 if not order.order_invoice_status:
151 ignore_fields = mandatory_fields
152 # if not request_body.get('id'):
153 # loc_code = order.start_location.sublocation.location.code
154 # uppercase_letters = list(string.ascii_uppercase)
155 # index = OrderInvoiceRelatedInfo.objects.filter(order = order).count()
156 # e_lr = "ZC%s%06d%s" % (loc_code, order.id, uppercase_letters[index])
157 # request_body['lr_number'] = e_lr
158 elif request_body.has_key('weight'):
159 weight = utils.get_digit(request_body.get('weight'))
160 if not weight:
161 pass
162 elif weight < 100 or weight > 80000:
163 eq_tonnage = utils.weight_conversion(weight, to_unit='tons')
164 resp_status = 1
165 status_msg = "Please Enter valid weight in KGS, equivalent tonnage come out %s " % eq_tonnage
166 return status_msg, resp_status, result
167
168 request_body['equivalent_weight'] = utils.get_digit(request_body.get('equivalent_weight'))
169 if request_body.get('equivalent_weight'):
170 weight = request_body.get('equivalent_weight')
171 if weight < 100 or weight > 80000:
172 eq_tonnage = utils.weight_conversion(weight, to_unit='tons')
173 resp_status = 1
174 status_msg = "Please Enter valid weight in KGS, equivalent tonnage come out %s " % eq_tonnage
175 return status_msg, resp_status, result
176
177 if ignore_fields:
178 mandatory_fields = list(set(mandatory_fields) - set(ignore_fields))
179
180 if mandatory_fields:
181 status_msg, resp_status = utils.check_missing_fields(request_body, mandatory_fields)
182 if resp_status == 1:
183 return status_msg, resp_status, result
184
185 invoice_number = request_body.get('invoice_number', '')
186 lr_number = request_body.get('lr_number', '')
187
188 if request_body.get('date_of_arrival'):
189 date = utils.epoc_to_datetime(float(request_body['date_of_arrival']))
190 request_body['date_of_arrival'] = date
191 if request_body['date_of_arrival'] > current_utc_time:
192 status_msg = 'Date Of Arrival can not be a future date.'
193 resp_status = 1
194 return status_msg, resp_status, result
195 elif 'date_of_arrival' in request_body:
196 date_of_arrival = request_body.pop('date_of_arrival', None)
197
198 if request_body.get('date_of_delivery'):
199 date = utils.epoc_to_datetime(float(request_body['date_of_delivery']))
200 request_body['date_of_delivery'] = date
201 if request_body['date_of_delivery'] > current_utc_time:
202 status_msg = 'Date Of Delivery can not be a future date.'
203 resp_status = 1
204 return status_msg, resp_status, result
205 elif 'date_of_delivery' in request_body:
206 date_of_delivery = request_body.pop('date_of_delivery', None)
207
208 if request_body.get('date_of_arrival') and request_body.get('date_of_delivery'):
209 if request_body['date_of_delivery'] < request_body.get('date_of_arrival'):
210 resp_status = 1
211 status_msg = "Date of Arrival Should be less than Date of Delivery"
212 return status_msg, resp_status, result
213
214 if request_body.get('cn_date'):
215 date = utils.epoc_to_datetime(float(request_body['cn_date']))
216 request_body['cn_date'] = date
217 if request_body['cn_date'] > current_utc_time:
218 status_msg = 'Shipment date can not be a future date.'
219 resp_status = 1
220 return status_msg, resp_status, result
221 elif request_body.get('date_of_delivery') and request_body.get('cn_date') > request_body.get(
222 'date_of_delivery'):
223 resp_status = 1
224 status_msg = "Date of Arrival should be after shipment date"
225 elif request_body.get('date_of_arrival') and request_body.get('date_of_arrival') < request_body.get('cn_date'):
226 resp_status = 1
227 status_msg = "Date of Arrival at destination should be after shipment date"
228 if resp_status:
229 return status_msg, resp_status, result
230 else:
231 request_body.pop('cn_date', None)
232
233 # detention days can not be a 3 digit
234 if utils.get_digit(request_body.get('src_detention_days', 0)) > 99:
235 status_msg = 'Source Detention can not be 3 digit.'
236 resp_status = 1
237 return status_msg, resp_status, result
238
239 if utils.get_digit(request_body.get('dest_detention_days', 0)) > 99:
240 status_msg = 'Destination Detension can not be 3 digit.'
241 resp_status = 1
242 return status_msg, resp_status, result
243
244 request_body['last_modified_by'] = user
245 request_body['last_modified_date'] = current_utc_time
246
247 try:
248
249 with transaction.atomic():
250 # Create/update orderInvoice entry
251 resp_status, status_msg, inv_obj = utils.create_model_entry(OrderInvoiceRelatedInfo, request_body,
252 update=update)
253 if resp_status == 1:
254 return status_msg, resp_status, result
255
256 if isinstance(inv_obj, OrderInvoiceRelatedInfo):
257 if not inv_obj.cn_date and not order.order_invoice_status:
258 shipment_date = None
259 try:
260 shipment_date = Status.objects.get(order=inv_obj.order, status='LR Generated').dt_added
261 except Exception as e:
262 shipment_date = None
263 pass
264 inv_obj.cn_date = shipment_date
265 if not date_of_delivery:
266 inv_obj.date_of_delivery = date_of_delivery
267 if not date_of_arrival:
268 inv_obj.date_of_arrival = date_of_arrival
269 inv_obj.save()
270
271 if order.order_invoice_status == 'Invoice Ready':
272 # move the status to Invoice Pending
273 new_status = { 'status': 'Invoice Pending' }
274 s_msg, resp_od_status, result_status = update_order_invoice_status(order, new_status, user)
275 if resp_od_status != 0:
276 status_msg += s_msg
277
278 except Exception as e:
279
280 path = 'update_invoice_data'
281 if request:
282 path = request.path
283 error_msg_list = ["Error in %s " % path,
284 traceback.format_exc()]
285 utils.write_error_logs(error_msg_list)
286 resp_status = 1
287 status_msg = "Unable to update order invoice data"
288 return status_msg, resp_status, result
289
290 q_message = { 'order_id': order.id }
291 utils.push_to_message_queue(q_message, 'profitability', 3)
292 status_msg = "Successfully updated order invoice %s " % inv_obj.id
293 result['order_invoice_id'] = inv_obj.id
294 return status_msg, resp_status, result
295
296
297@role_function_check("HANDLE_ORDER")
298def get_order_invoice_by_filters(request, extra_info='', filter='', serach='', external='', nested=False):
299 '''
300 API to get order_invoices details by variour filters
301 REQUEST:
302 order_id =
303 id =
304 lr_number =
305 invoice_number =
306 Response:
307 OrderInvoice details
308 '''
309
310 status_msg, resp_status, result = '', 0, { }
311 request_body = utils.parse_request_body(request)
312 user = request.user
313 filters = request_body.get('filters', { })
314 page = request_body.get('page', { })
315 status_msg, resp_status, result = get_order_invoices(filters, page)
316 return status_msg, resp_status, result
317
318
319def get_order_invoices(filters='', page='', extra_select_fields=[]):
320 '''
321 This function gives order invoice details based on given filters and page_str
322 '''
323 status_msg, resp_status, result = '', 0, []
324
325 DEFAULT_RESULTS_PER_PAGE = 50
326 page_str = ''
327
328 # If page is being passed this return limited result
329 if page:
330 if page.has_key('opr'):
331 page_str = "limit " + `int(page['num']) * page['opr'] ` + ',' + `page['opr']`
332 else:
333 page_str = "limit " + `int(page['num']) * DEFAULT_RESULTS_PER_PAGE` + ',' + `DEFAULT_RESULTS_PER_PAGE`
334
335 order_by_fields = ['I.id']
336 left_join = OrderedDict()
337 parsed_filters = []
338
339 select_fields = ['I.id', 'I.lr_number', 'I.weight', 'I.eta',
340 'I.type_of_pkgs', 'I.num_pkgs',
341 'I.invoice_number', 'I.order_id',
342 'I.src_detention_days', 'I.dest_detention_days',
343 'I.desc_of_goods', 'I.truck_type_id',
344 'I.loading_charge', 'I.unloading_charge',
345 'I.do_sto_ssl_num', 'I.service_entry_num',
346 'I.rate_as_per_serv_no', 'I.equivalent_weight',
347 'I.checkpost_charges', 'I.qty_charged_tons',
348 'I.multiple_unloading_pt_charge', 'I.multiple_loading_pt_charge',
349 'I.loading_detention', 'I.unloading_detention',
350 'I.custom_truck_type', 'I.custom_tt_tonnage',
351 'I.invoicing_address', 'I.freight_amount',
352 'UNIX_TIMESTAMP(I.date_of_arrival)*1000 as date_of_arrival',
353 'UNIX_TIMESTAMP(I.date_of_delivery)*1000 as date_of_delivery',
354 'CONCAT_WS(" ",U.first_name,U.last_name) as last_modified_by',
355 'TT.truck_type as invoice_truck_type', 'is_finalized',
356 'UNIX_TIMESTAMP(I.cn_date)*1000 as cn_date',
357 'I.invoice_from_city', 'I.invoice_to_city',
358 'I.plywood_charges', 'I.obd_number',
359 'I.green_tax', 'finalized_by_id', 'I.gross_weight',
360 'I.lr_serial_number', 'I.advance_doc_ready',
361 'I.consignor', 'I.consignor', 'I.unloading_weight', 'I.consignee_gstn_number'
362 ]
363 if extra_select_fields:
364 select_fields.extend(extra_select_fields)
365
366 left_join['auth_user U'] = 'U.id = I.last_modified_by_id'
367 left_join['base_trucktype TT'] = 'TT.id = I.truck_type_id'
368 left_join['base_order O'] = 'O.id = I.order_id'
369 # prepare query from filter parameters
370 for key, value in filters.iteritems():
371 if key == 'customer_id':
372 filter_query = "O.user_id = \"%s\"" % (value)
373 parsed_filters.append(filter_query)
374 continue
375 elif key == 'date_filter':
376 start_date = utils.timezone_to_utc(utils.epoc_to_datetime(value.get('start')))
377 start_date = start_date.strftime('%Y-%m-%d %H:%M:%S')
378 end_date = utils.timezone_to_utc(utils.epoc_to_datetime(value.get('end')))
379 end_date = end_date.strftime('%Y-%m-%d %H:%M:%S')
380 filter_query = "O.end_date Between \"%s\" and \"%s\"" % (start_date, end_date)
381 parsed_filters.append(filter_query)
382 continue
383 # Order status choices
384 if key in ['status', 'order_invoice_status', 'settlement_document_status']:
385 if isinstance(value, list) or isinstance(value, tuple):
386 values_str = ', '.join("'" + str(status) + "'" for status in value)
387 filter_query = "O.%s in (%s)" % (key, values_str)
388 else:
389 filter_query = "O.%s = \"%s\"" % (key, value)
390 parsed_filters.append(filter_query)
391 continue
392 if isinstance(value, list) or isinstance(value, tuple):
393 values_str = ', '.join("'" + str(status) + "'" for status in value)
394 filter_query = "I.%s in (%s)" % (key, values_str)
395 else:
396 filter_query = "I.%s = \"%s\"" % (key, value)
397
398 if filter_query:
399 parsed_filters.append(filter_query)
400 parsed_filters.append('I.is_deleted = 0')
401 left_join_str = " ".join(["Left Join %s on %s" % (key, value) for key, value in left_join.iteritems()])
402 select_fields_str = ", ".join(select_fields)
403
404 query = "select %s from base_orderinvoicerelatedinfo I" % select_fields_str
405
406 if left_join_str.strip():
407 query += " " + left_join_str.strip()
408
409 try:
410 filters_str = " and ".join(filter_str for filter_str in parsed_filters if filter_str)
411 if filters_str:
412 query += ' WHERE %s ' % filters_str
413
414 order_by_field = ', '.join(order_by_fields)
415 query += ' ORDER by %s ' % order_by_field
416
417 if page_str:
418 query += page_str
419 cursor = connection.cursor()
420 cursor.execute(query)
421 result = utils.dictfetchall(cursor)
422
423 except Exception as e:
424 error_msg_list = ["Error at get_order_invoices ",
425 traceback.format_exc()]
426 utils.write_error_logs(error_msg_list)
427 status_msg = utils.get_error_msg(e)
428 resp_status = 1
429 finally:
430 cursor.close()
431 return status_msg, resp_status, result
432
433
434@role_function_check("GENERATE_INVOICE")
435def generate_invoice_api(request, obj_id, filter, fields, external, nested):
436 resp_status, status_msg, result = 0, '', { }
437 request_body = utils.parse_request_body(request)
438 user = request.user
439 order_id = request_body.get('order_id', 0)
440 try:
441 order = Order.objects.get(pk=order_id)
442 except Order.DoesNotExist:
443 resp_status = 1
444 status_msg = 'Order with ID %s not found' % order_id
445 return status_msg, resp_status, result
446 status_msg, resp_status, result, invalid_list = generate_invoice(order_ids=order_id, user=user,
447 customer_id=order.user.id,
448 location_id=order.from_city.id)
449
450 if len(result) > 0:
451 result = result[0]
452 return status_msg, resp_status, result, invalid_list
453
454
455def get_template_paths(inv_info, current_ist_time, invoice_path):
456 load_unload_template_path, detention_template_path, checkpost_template_path, summary_template_path = None, None, None, None
457 plywood_template_path = None
458 templates_path_list = []
459 current_time_str = current_ist_time.strftime('%H_%M_%S_')
460 template_path = inv_info.invoice_template_path
461 order_invoice_type = inv_info.order_invoice_type
462
463 separate_invoices = inv_info.separate_invoice
464 separate_load_unload_invoice = separate_invoices.load_unload.is_set
465 separate_detention_invoice = separate_invoices.detention.is_set
466 separate_checkpost_invoice = separate_invoices.checkpost.is_set
467 separate_plywood_invoice = separate_invoices.plywood.is_set
468 separate_summary_invoice = separate_invoices.summary_invoice.is_set
469
470 '''
471 get separate template paths other than main invoice which are uploaded to s3
472 '''
473 if separate_load_unload_invoice:
474 load_unload_template_path = inv_info.load_unload_template_path
475 if separate_detention_invoice:
476 detention_template_path = inv_info.detention_template_path
477 if separate_checkpost_invoice:
478 checkpost_template_path = inv_info.checkpost_template_path
479 if separate_summary_invoice:
480 summary_template_path = inv_info.summary_template_path
481 if separate_plywood_invoice:
482 plywood_template_path = inv_info.plywood_template_path
483
484 '''
485 Get default templates if files are not uploaded to s3
486 '''
487 if order_invoice_type == 3:
488 if separate_load_unload_invoice and not load_unload_template_path:
489 load_unload_template_s3_path = invoice_config.DEFAULT_LOAD_UNLOAD_TEMPLATE_PATH + '/load_unload_multiple_order_1_inv.html'
490 load_unload_template_path = aws_utils.download_from_s3(load_unload_template_s3_path)
491 if not load_unload_template_path:
492 load_unload_template_path = os.path.join(settings.BASE_DIR, 'app/templates/invoice/load_unload_templates/load_unload_multiple_order_1_inv.html')
493
494 if separate_detention_invoice and not detention_template_path:
495 detention_template_s3_path = invoice_config.DEFAULT_DETENTION_TEMPLATE_PATH + '/detention_multiple_order_1_inv.html'
496 detention_template_path = aws_utils.download_from_s3(detention_template_s3_path)
497 if not detention_template_path:
498 detention_template_path = os.path.join(settings.BASE_DIR, 'app/templates/invoice/detention_templates/detention_multiple_order_1_inv.html')
499
500 if separate_checkpost_invoice and not checkpost_template_path:
501 checkpost_template_s3_path = invoice_config.DEFAULT_CHECKPOST_TEMPLATE_PATH + '/checkpost_multiple_order_1_inv.html'
502 checkpost_template_path = aws_utils.download_from_s3(checkpost_template_s3_path)
503 if not checkpost_template_path:
504 checkpost_template_path = os.path.join(settings.BASE_DIR, 'app/templates/invoice/checkpost_templates/checkpost_multiple_order_1_inv.html')
505
506 if separate_summary_invoice and not summary_template_path:
507 summary_template_path = os.path.join(settings.BASE_DIR,
508 'app/templates/invoice/summary_invoice_templates/summary_invoice_multiple_order_1_inv.html')
509
510 if separate_plywood_invoice and not plywood_template_path:
511 plywood_template_s3_path = invoice_config.DEFAULT_PLYWOOD_TEMPLATE_PATH + '/plywood_multiple_order_1_inv.html'
512 plywood_template_path = aws_utils.download_from_s3(plywood_template_s3_path)
513 if not plywood_template_path:
514 plywood_template_path = os.path.join(settings.BASE_DIR,
515 'app/templates/invoice/plywood_templates/plywood_multiple_order_1_inv.html')
516
517 else:
518 if separate_load_unload_invoice and not load_unload_template_path:
519 load_unload_template_s3_path = invoice_config.DEFAULT_LOAD_UNLOAD_TEMPLATE_PATH + '/load_unload_invoice_template.html'
520 load_unload_template_path = aws_utils.download_from_s3(load_unload_template_s3_path)
521 if not load_unload_template_path:
522 load_unload_template_path = os.path.join(settings.BASE_DIR,
523 'app/templates/invoice/load_unload_templates/load_unload_invoice_template.html')
524 if separate_detention_invoice and not detention_template_path:
525 detention_template_s3_path = invoice_config.DEFAULT_DETENTION_TEMPLATE_PATH + '/detention_invoice_template.html'
526 detention_template_path = aws_utils.download_from_s3(detention_template_s3_path)
527 if not detention_template_path:
528 detention_template_path = os.path.join(settings.BASE_DIR,
529 'app/templates/invoice/detention_templates/detention_invoice_template.html')
530 if separate_checkpost_invoice and not checkpost_template_path:
531 checkpost_template_s3_path = invoice_config.DEFAULT_CHECKPOST_TEMPLATE_PATH + '/checkpost_invoice_template.html'
532 checkpost_template_path = aws_utils.download_from_s3(checkpost_template_s3_path)
533 if not checkpost_template_path:
534 checkpost_template_path = os.path.join(settings.BASE_DIR,
535 'app/templates/invoice/checkpost_templates/checkpost_invoice_template.html')
536 if separate_summary_invoice and not summary_template_path:
537 summary_template_path = os.path.join(settings.BASE_DIR,
538 'app/templates/invoice/summary_invoice_templates/summary_invoice_template.html')
539
540 if separate_plywood_invoice and not plywood_template_path:
541 plywood_template_s3_path = invoice_config.DEFAULT_PLYWOOD_TEMPLATE_PATH + '/plywood_invoice_template.html'
542 plywood_template_path = aws_utils.download_from_s3(plywood_template_s3_path)
543 if not plywood_template_path:
544 plywood_template_path = os.path.join(settings.BASE_DIR,
545 'app/templates/invoice/plywood_templates/plywood_invoice_template.html')
546
547 if not template_path:
548 if order_invoice_type == 3:
549 template_s3_path = invoice_config.DEFAULT_MASTER_TEMPLATE_PATH + '/multiple_order_invoice_template_new.html'
550 template_path = aws_utils.download_from_s3(template_s3_path)
551 if not template_path:
552 template_path = os.path.join(settings.BASE_DIR, 'app/templates/invoice/multiple_order_invoice_template_new.html')
553 else:
554 template_s3_path = invoice_config.DEFAULT_MASTER_TEMPLATE_PATH + '/one_order_invoice_template_new.html'
555 template_path = aws_utils.download_from_s3(template_s3_path)
556 if not template_path:
557 template_path = os.path.join(settings.BASE_DIR, 'app/templates/invoice/one_order_invoice_template_new.html')
558 else:
559 file_name = os.path.basename(template_path)
560 downloded_path = os.path.join(invoice_path, (current_time_str + file_name))
561 downloded_path = utils.download_from_s3(template_path, dest_file_path=downloded_path)
562 if not downloded_path: # failed to download template path
563 return []
564 template_path = downloded_path
565
566 '''
567 Download other templates from s3 if path is s3 path
568 '''
569 try:
570 if load_unload_template_path and not os.path.isfile(load_unload_template_path):
571 file_name = os.path.basename(load_unload_template_path)
572 downloded_path = os.path.join(invoice_path, (current_time_str + file_name))
573 downloded_path = utils.download_from_s3(load_unload_template_path, dest_file_path=downloded_path)
574 load_unload_template_path = downloded_path
575 if detention_template_path and not os.path.isfile(detention_template_path):
576 file_name = os.path.basename(detention_template_path)
577 downloded_path = os.path.join(invoice_path, (current_time_str + file_name))
578 downloded_path = utils.download_from_s3(detention_template_path, dest_file_path=downloded_path)
579 detention_template_path = downloded_path
580 if checkpost_template_path and not os.path.isfile(checkpost_template_path):
581 file_name = os.path.basename(checkpost_template_path)
582 downloded_path = os.path.join(invoice_path, (current_time_str + file_name))
583 downloded_path = utils.download_from_s3(checkpost_template_path, dest_file_path=downloded_path)
584 checkpost_template_path = downloded_path
585 if summary_template_path and not os.path.isfile(summary_template_path):
586 file_name = os.path.basename(summary_template_path)
587 downloded_path = os.path.join(invoice_path, (current_time_str + file_name))
588 downloded_path = utils.download_from_s3(summary_template_path, dest_file_path=downloded_path)
589 summary_template_path = downloded_path
590 if plywood_template_path and not os.path.isfile(plywood_template_path):
591 file_name = os.path.basename(plywood_template_path)
592 downloded_path = os.path.join(invoice_path, (current_time_str + file_name))
593 downloded_path = utils.download_from_s3(plywood_template_path, dest_file_path=downloded_path)
594 plywood_template_path = downloded_path
595 except Exception as e:
596 error_msg_list = ["Error in %s gettemplatepath call",
597 traceback.format_exc()]
598 utils.write_error_logs(error_msg_list)
599 return []
600 templates_path_list.append(template_path)
601 templates_path_list.append(load_unload_template_path)
602 templates_path_list.append(detention_template_path)
603 templates_path_list.append(checkpost_template_path)
604 templates_path_list.append(summary_template_path)
605 templates_path_list.append(plywood_template_path)
606 return templates_path_list
607
608
609def get_order_finance_details(order=None, order_id=None):
610 if not order and not order_id:
611 return None
612 if order_id:
613 order_results = Order.objects.filter(id=int(order_id))
614 if not order_results:
615 return None
616 order_results = order_results.filter(orderfinancedetails__isnull=False)
617 if not order_results:
618 return None
619 return order_results[0].orderfinancedetails
620 if order:
621 order_finance_details = None
622 try:
623 order_finance_details = order.orderfinancedetails
624 except Exception as e:
625 pass
626 return order_finance_details
627
628# TODO - THIS METHOD NEEDS TO BE CHANGED.
629def generate_invoice(order_ids='', freight_bill_number='', user='', fields=''):
630 invoice_formats = ['.pdf']
631 resp_status, status_msg, result = 0, '', []
632 current_ist_time = utils.timezone_now()
633 current_ist_time_str = current_ist_time.strftime('%d %b %y')
634 final_receivable_data, receivable_order_data, common_freight_bill_num = [], [], None
635 invalid_list, invalid_dict = [], { }
636 receivable_data, total_amt_dict = { 'inv_details': [] }, { }
637 custom_from_city, downloded_path = None, None
638 customer_id, location_id, invoice_date , gstin = fields.get('customer_id'), fields.get('location_id'), fields.get(
639 'invoice_date'), fields.get('gstin')
640 customer_user_profile = CustomerUserProfile.objects.get(user_id=customer_id)
641
642 # custom name of from location customer based
643 custom_from_loc_obj = get_customer_custom_field(customer_id, location_id, 1)
644 '''
645 Check if the configuration is unique for multiple orders
646 '''
647 inv_infos = InvoiceInfo.objects.filter(customer_id=customer_id).order_by('-priority')
648 inv_info = None
649 list_inv_infos = []
650 for order_id in order_ids:
651 order = Order.objects.get(id=order_id)
652 for item in inv_infos:
653 if exact_match(item, order):
654 inv_info = item
655 list_inv_infos.append(inv_info.id)
656 break
657 list_inv_infos = list(set(list_inv_infos))
658 if len(list_inv_infos) != 1 and len(order_ids) != 0:
659 resp_status = 1
660 status_msg = 'Multiple invoice configurations for the given orders'
661 return status_msg, resp_status, result, invalid_list
662 '''
663 Handling special case to generate excel invoice for Dhunseri
664 TODO-- Later move this to a config or DB
665 '''
666 '''
667 if customer_user_profile.name == 'Dhunseri Petrochem Ltd':
668 invoice_formats.append('.xlsx')
669 '''
670 if isinstance(custom_from_loc_obj, CustomCustomerLocation):
671 custom_from_city = custom_from_loc_obj.custom_location
672
673 '''
674 If epoc value is passed as invoice_date
675 '''
676 if invoice_date and not isinstance(invoice_date, datetime):
677 invoice_date = utils.utc_to_timezone(utils.epoc_to_datetime(utils.get_digit(invoice_date)))
678 elif not invoice_date:
679 invoice_date = current_ist_time
680
681 gstin = gstin or customer_user_profile.cgstin
682
683 path = "/var/log/blackbuck"
684 receivable_dir = "temp_receivable"
685 status_msg, resp_status, invoice_path = create_folder_if_not_exists(path, receivable_dir)
686 if resp_status == 1:
687 return status_msg, resp_status, result, invalid_list
688
689 if not isinstance(user, User):
690 user = utils.get_exact_match(User, user)
691 if not user:
692 resp_status = 1
693 status_msg = 'Please provide a valid user'
694 return status_msg, resp_status, result, invalid_list
695
696 if not isinstance(order_ids, list):
697 order_ids = [order_ids]
698
699 order_invoice_type = inv_info.order_invoice_type
700 # Getting ALL the template path as list no separate invoice for perticular rate then none as template path
701 templates_path_list = get_template_paths(inv_info, current_ist_time, invoice_path)
702 if not templates_path_list:
703 resp_status = 1
704 status_msg = "Unable to load template for invoice generation"
705 return status_msg, resp_status, result, invalid_list
706
707 upload_path_string = '';
708 location = Location.objects.get(id=location_id)
709 key = "uploads/receivable_invoices/" + customer_user_profile.short_name.lower() + "/" + location.code.lower()
710 for order_id in order_ids:
711
712 status_msg, resp_status, result_val = validate_order_info_invoice_generation(order_id, location_id=location_id,
713 customer_id=customer_id)
714 if resp_status:
715 invalid_dict = { 'order_id': order_id, 'reason': status_msg }
716 invalid_list.append(invalid_dict)
717 continue
718
719 order = result_val['order']
720 inv_data = result_val['inv_data']
721 cust_master_rate = result_val['customer_master_rate']
722
723 # order_finance = order.orderfinancedetails
724 order_finance = get_order_finance_details(order=order)
725 if not order_finance:
726 resp_status = 1
727 status_msg = "Unable to get finance details for the order " + str(order_id)
728 return status_msg, resp_status, result, invalid_list
729 if not cust_master_rate:
730 resp_status = 1
731 status_msg = "Unable to get customer master rate"
732 return status_msg, resp_status, result, invalid_list
733
734 store_profitability_in_order(order, order_finance.total_amount)
735
736 status_msg, resp_status, receivable_order_data = get_receivable_data_for_invoice(order, cust_master_rate,
737 inv_data, current_ist_time,
738 custom_from_city, inv_info)
739 if resp_status == 1:
740 invalid_dict = { 'order_id': order_id, 'reason': status_msg }
741 invalid_list.append(invalid_dict)
742 continue
743
744 if order_invoice_type == 3:
745 total_amt_dict = calculate_total_amts(receivable_order_data, total_amt_dict)
746 receivable_data['separate_checkpost_invoice'] = receivable_order_data[0]['separate_checkpost_invoice']
747 receivable_data['separate_detention_invoice'] = receivable_order_data[0]['separate_detention_invoice']
748 receivable_data['separate_load_unload_invoice'] = receivable_order_data[0]['separate_load_unload_invoice']
749 receivable_data['separate_summary_invoice'] = receivable_order_data[0]['separate_summary_invoice']
750 receivable_data['separate_plywood_invoice'] = receivable_order_data[0]['separate_plywood_invoice']
751 receivable_data['service_tax_in_invoice'] = receivable_order_data[0]['service_tax_in_invoice']
752 receivable_data['inv_details'].append(receivable_order_data[0])
753 continue
754 for item in receivable_order_data:
755 new_item = deepcopy(item)
756 item['inv_details'] = [new_item]
757 final_receivable_data.append(item)
758
759 if order_invoice_type == 3:
760 if invalid_list:
761 resp_status = 1
762 status_msg = 'All orders are not valid so can not generate one PDF of these order'
763 return status_msg, resp_status, result, invalid_list
764 status_msg,resp_status,common_freight_bill_num = get_or_generate_freight_bill_number(order, inv_info)
765 if resp_status:
766 return status_msg,resp_status,result, invalid_list
767 receivable_data['freight_bill_num'] = common_freight_bill_num
768 receivable_data.update(total_amt_dict)
769 final_receivable_data = [receivable_data]
770 pdf_path = ''
771
772 processed_orders = set()
773 order_details_list = ["Truck Arrival Source_dt_added", "Truck Departure Source_dt_added", "Truck In-Transit_dt_added"]
774
775 import pdb; pdb.set_trace()
776 for item in final_receivable_data:
777
778 if order_invoice_type != 3:
779 order = item['order']
780 #invoice_number = item.get('invoice_number')
781 invoice_number = item.get('id')
782 filename = generate_file_name(order_id=order.id, invoice_number=invoice_number,
783 order_invoice_type=order_invoice_type)
784 item['address'] = put_address_in_receivable(order, inv_info)
785 order_details = populate_order_details(order, order_details_list)
786 if order_details:
787 item.update(order_details)
788 processed_orders.add(order)
789
790 else:
791 order = item['inv_details'][0]['order']
792 index = 0
793 for entry in item['inv_details']:
794 order_details = populate_order_details(entry['order'], order_details_list)
795 if order_details:
796 item['inv_details'][index].update(order_details)
797 processed_orders.add(order)
798 index += 1
799
800 item['address'] = put_address_in_receivable(order, inv_info)
801 filename = generate_file_name(freight_bill_num=common_freight_bill_num,
802 order_invoice_type=order_invoice_type)
803
804 if order not in processed_orders:
805 order_details = populate_order_details(order, order_details_list)
806 if order_details:
807 item.update(order_details)
808 processed_orders.add(order)
809
810 # get shipment date
811 if item.get('cn_date'):
812 shipment_date = item['cn_date']
813 else:
814 shipment_date = item['inv_details'][0]['cn_date']
815 '''
816 Will change the total amount if service tax need to charged to customer
817 So we'll get amount in words after getting final total amount
818 '''
819 resp_status = get_service_tax(item, inv_info, shipment_date, invoice_date)
820 get_amount_in_word(item)
821 if resp_status:
822 status_msg = "Error In getting service tax"
823 invalid_dict = { 'order_id': order.id, 'reason': status_msg }
824 invalid_list.append(invalid_dict)
825 return status_msg, resp_status, result, invalid_list
826
827 s_msg, r_status = put_constant_data_in_receivable(item, current_ist_time, shipment_date, invoice_date, gstin)
828 item['without_header'] = fields.get('without_header')
829 item['vendor_code'] = customer_user_profile.our_vendor_code
830 if r_status:
831 invalid_dict = { "order_id": order.id, 'reason': s_msg }
832 invalid_list.append(invalid_dict)
833 continue
834 s3_paths = []
835 path_invoices = []
836 for invoice_format in invoice_formats:
837 s_msg, r_status, path_inv = generate_invoice_report(item, invoice_path, filename=filename,
838 template=templates_path_list, format=invoice_format)
839 if r_status:
840 '''
841 Handling incompatible invoice type configuration and template
842 '''
843 invalid_dict = { 'order_id': order.id, 'reason': s_msg }
844 invalid_list.append(invalid_dict)
845 return s_msg, r_status, result, invalid_list
846
847 if invoice_format == '.pdf':
848 utils.upload_to_s3(path_inv, key=key, content_type='application/pdf')
849 elif invoice_format == '.xlsx':
850 utils.upload_to_s3(path_inv, key=key, content_type='application/vnd.ms-excel')
851 path_invoices.append(path_inv)
852 s3_paths.append(key + "/" + os.path.basename(path_inv))
853 # path_inv = generate_pdf_for_invoice(item,invoice_path,filename=filename,template=templates_path_list)
854 # utils.upload_to_s3(path_inv,key = key,content_type='application/pdf')
855 # s3_path = key+"/" +os.path.basename(path_inv)
856 pdf_path = s3_paths[0]
857 if order_invoice_type != 3:
858
859 receivable_doc = { 'freight_bill_number': item['freight_bill_num'], 'invoice_url': s3_paths[0],
860 'invoice_date': invoice_date
861 }
862 customer_invoice_obj = update_customer_invoice(receivable_doc, user)
863 if isinstance(customer_invoice_obj, CustomerInvoice):
864 if order_invoice_type == 2:
865 customer_invoice_obj.invoice_number = invoice_number
866 customer_invoice_obj.save()
867 order.freight_bill_number.add(customer_invoice_obj)
868 OrderFreightBillNumberHistory.objects.create(
869 **{ 'order': order, 'customerinvoice': customer_invoice_obj, 'added_by': user })
870 else:
871 resp_status = 1
872 status_msg = "Error In adding customer_invoice in order"
873 invalid_dict = { 'order_id': order.id, 'reason': status_msg }
874 invalid_list.append(invalid_dict)
875 return status_msg, resp_status, result, invalid_list
876 if order.order_invoice_status in ['Invoice Pending', 'Invoice Rejected', 'Invoice Generated']:
877
878 '''
879 move order to Invoice Generated
880 '''
881 new_status = { 'status': 'Invoice Generated' }
882 s_msg, resp_od_status, result_status = update_order_invoice_status(order, new_status, user)
883 status_msg += s_msg
884 if resp_od_status != 0:
885 invalid_dict = { 'order_id': order.id, 'reason': s_msg }
886 invalid_list.append(invalid_dict)
887
888 path_dict = { 'receivable_url': s3_paths, 'file_path': path_invoices,
889 'freight_bill_num': item['freight_bill_num'] }
890 result.append(path_dict)
891
892 if order_invoice_type == 3:
893
894 receivable_doc = { 'freight_bill_number': common_freight_bill_num, 'invoice_url': pdf_path,
895 'invoice_date': invoice_date
896 }
897 customer_invoice_obj = update_customer_invoice(receivable_doc, user)
898 if not isinstance(customer_invoice_obj, CustomerInvoice):
899 resp_status = 1
900 status_msg = "Error In adding customer_invoice in order"
901 # invalid_list.append({'order_id':})
902 return status_msg, resp_status, result, invalid_list
903
904 for item in final_receivable_data[0]['inv_details']:
905 order = item['order']
906 order.freight_bill_number.add(customer_invoice_obj)
907 OrderFreightBillNumberHistory.objects.create(
908 **{ 'order': order, 'customerinvoice': customer_invoice_obj, 'added_by': user })
909 order.save()
910
911 if order.order_invoice_status in ['Invoice Pending', 'Invoice Rejected']:
912 '''
913 move order to Invoice Generated
914 '''
915 new_status = { 'status': 'Invoice Generated' }
916 s_msg, resp_od_status, result_status = update_order_invoice_status(order, new_status, user)
917 status_msg += s_msg
918 if resp_od_status != 0:
919 invalid_dict = { 'order_id': order.id, 'reason': s_msg }
920 invalid_list.append(invalid_dict)
921
922 path_dict = { 'receivable_url': s3_paths, 'file_path': path_invoices,
923 'freight_bill_num': common_freight_bill_num }
924 result.append(path_dict)
925
926 try:
927 if downloded_path:
928 os.remove(downloded_path)
929 except Exception as e:
930 pass
931
932
933
934 # Create customer_invoice entry in OrderInvoiceRelatedInfo which maps to a CustomerInvoice entry
935 create_order_invoice_related_info_customer_invoice_mapping(final_receivable_data, order_invoice_type,
936 common_freight_bill_num)
937
938 return status_msg, resp_status, result, invalid_list
939
940
941def populate_order_details(order, order_details_list):
942 """
943 Retrieve data from ES and populate invoice dict
944 :param invoice_dict:
945 :param order:
946 :param order_details_list:
947 :return:
948 """
949
950 order_details = {}
951 for key in order_details_list:
952 order_details[key] = ''
953
954 if order_details_list and order:
955 try:
956 order_id = order.id if isinstance(order, Order) else order if isinstance(order, int) or isinstance(order, long) else int(str(order)) if isinstance(order, basestring) else None
957 if order_id:
958 order_details = get_order_information(order_id, order_details_list)
959 if order_details:
960 for key, value in order_details.iteritems():
961 if ("dt_added" in key or "dt_updated" in key) and value:
962 date_value = parse(value)
963 date_value = date_value.replace(tzinfo=pytz.UTC)
964 ist_date = utils.utc_to_timezone(date_value)
965 order_details[key] = ist_date
966
967 except Exception as e:
968 utils.write_error_logs('Error retrieving/parsing ES Order details. Reason : {}'.format(e.message))
969
970 return order_details
971
972
973def create_order_invoice_related_info_customer_invoice_mapping(final_receivable_data, order_invoice_type,
974 common_freight_bill_num):
975 """
976 Creates customer_invoice entry in OrderInvoiceRelatedInfo which maps to a CustomerInvoice entry
977 :param final_receivable_data:
978 :param order_invoice_type:
979 :param common_freight_bill_num:
980 :return:
981 """
982
983 with transaction.atomic():
984 if order_invoice_type != 3:
985 for item in final_receivable_data:
986
987 # LR Number is considered only for One Order Multiple Invoice
988 lr_number = item['lr_number'] if order_invoice_type == 2 else ''
989
990 freight_bill_number = item['freight_bill_num']
991 total_invoiced_amount = float(0.0)
992 order = item.get('order', '') or item['inv_details'][0]['order']
993 order_id = int(str(order.id)) if isinstance(order, Order) else int(order)
994 customer_invoice_results = CustomerInvoice.objects.filter(freight_bill_number=freight_bill_number)
995
996 # For One Order One Invoice - filtering by order_id alone is enough to get the appropriate OrderInvoiceRelatedInfo
997 # For One Order Multiple Invoice - Filer by LR Number also to get the appropriate OrderInvoiceRelatedInfo
998 if customer_invoice_results:
999 customer_invoice = customer_invoice_results[0]
1000
1001 order_invoice_related_info_entries = OrderInvoiceRelatedInfo.objects.filter(order__id=order_id)
1002 if lr_number and order_invoice_related_info_entries:
1003 order_invoice_related_info_entries = order_invoice_related_info_entries.filter(
1004 lr_number=lr_number)
1005
1006 for entry in order_invoice_related_info_entries:
1007 # 1. Calculate total_invoiced_amount 2. Create OIINFO Customer Invoice mapping 3.Create OIINFO version and CustomerInvoice invoice_version mapping
1008 total_invoiced_amount += invoicing_report.sum_order_invoice_related_info_charges(entry)
1009 entry.customer_invoice = customer_invoice
1010 entry.last_modified_date = utils.timezone_now()
1011 entry.save()
1012
1013 oi_info_invoice_versions_mapping = OrderInvoiceInfoVersions(order_invoice_related_info=entry,
1014 order_invoice_info_version=entry.version,
1015 invoice_version=customer_invoice.invoice_version)
1016 oi_info_invoice_versions_mapping.save()
1017
1018 # Update total_invoiced_amount in CustomerInvoice
1019 customer_invoice.total_invoiced_amount = total_invoiced_amount
1020 customer_invoice.save()
1021
1022 else:
1023 for item in final_receivable_data:
1024 freight_bill_number = common_freight_bill_num
1025 total_invoiced_amount = float(0.0)
1026 customer_invoice_results = CustomerInvoice.objects.filter(freight_bill_number=freight_bill_number)
1027 no_of_orders = len(item['inv_details'])
1028 if customer_invoice_results:
1029 customer_invoice = customer_invoice_results[0]
1030 for i in range(0, no_of_orders):
1031 order = item['inv_details'][i]['order']
1032 order_id = int(str(order.id)) if isinstance(order, Order) else int(order)
1033 order_invoice_related_info_entries = OrderInvoiceRelatedInfo.objects.filter(order__id=order_id)
1034 for entry in order_invoice_related_info_entries:
1035 # 1. Calculate total_invoiced_amount 2. Create OIINFO Customer Invoice mapping 3.Create OIINFO version and CustomerInvoice invoice_version mapping
1036 total_invoiced_amount += invoicing_report.sum_order_invoice_related_info_charges(entry)
1037 entry.customer_invoice = customer_invoice
1038 entry.last_modified_date = utils.timezone_now()
1039 entry.save()
1040
1041 oi_info_invoice_versions_mapping = OrderInvoiceInfoVersions(
1042 order_invoice_related_info=entry, order_invoice_info_version=entry.version,
1043 invoice_version=customer_invoice.invoice_version)
1044 oi_info_invoice_versions_mapping.save()
1045
1046 # Update total_invoiced_amount in CustomerInvoice
1047 customer_invoice.total_invoiced_amount = total_invoiced_amount
1048 customer_invoice.save()
1049
1050
1051def construct_dict_for_mongo(item):
1052 from decimal import Decimal
1053 tmp_dict = { }
1054 for key, value in item.iteritems():
1055 if str(key) == 'inv_details':
1056 tmp_dict['inv_details'] = construct_dict_for_mongo(item[key])
1057 elif isinstance(value, Decimal):
1058 tmp_dict[str(key)] = float(value)
1059 elif isinstance(value, datetime):
1060 tmp_dict[str(key)] = value.strftime("%Y-%m-%d %H-%M-%S")
1061 else:
1062 tmp_dict[str(key)] = value
1063
1064 return tmp_dict
1065
1066
1067def modify_invoice_generation_dict(final_receivable_data, parent=''):
1068 import datetime
1069 from decimal import Decimal
1070 # invoice_dict_arr = []
1071 # for item in final_receivable_data:
1072 # invoice_dict_arr.append(construct_dict_for_mongo(item))
1073 try:
1074 for item in final_receivable_data:
1075 item.pop('constant_data', None)
1076 for item in final_receivable_data:
1077 for key, value in item.iteritems():
1078 if key == 'inv_details':
1079 inv_details = item[key]
1080 for i in range(0, len(inv_details)):
1081 for k, v in inv_details[i].iteritems():
1082 if isinstance(v, datetime.datetime) or isinstance(v, datetime.date):
1083 item['inv_details'][i][k] = datetime.datetime.strftime(v, '%Y-%m-%d %H-%M-%S')
1084 elif isinstance(value, Decimal):
1085 item['inv_details'][i][k] = str(float(v))
1086 elif key == 'order' and isinstance(value, Order):
1087 item['inv_details'][i][k] = str(v.id)
1088 else:
1089 item['inv_details'][i][k] = str(v)
1090 if isinstance(value, datetime.datetime) or isinstance(value, datetime.date):
1091 if parent:
1092 item[key] = datetime.datetime.strftime(value, '%Y-%m-%d %H-%M-%S')
1093 else:
1094 item[key] = datetime.datetime.strftime(value, '%Y-%m-%d %H-%M-%S')
1095 elif isinstance(value, Decimal):
1096 if parent:
1097 item[key] = str(float(value))
1098 item[key] = str(float(value))
1099 elif key == 'order' and isinstance(value, Order):
1100 item[key] = str(value.id)
1101 else:
1102 item[key] = str(value)
1103
1104 except Exception as e:
1105 pass
1106
1107
1108def insert_invoice_dicts_to_mongo(final_receivable_data, order_invoice_type, common_freight_bill_num, pdf_path):
1109 modify_invoice_generation_dict(final_receivable_data)
1110 for item in final_receivable_data:
1111 freight_bill_number = item['freight_bill_num'] if order_invoice_type != 3 else common_freight_bill_num
1112 customer_invoice_obj = CustomerInvoice.objects.filter(freight_bill_number=freight_bill_number)
1113 if customer_invoice_obj:
1114 invoice_version = str(customer_invoice_obj[0].invoice_version)
1115 item['invoice_version'] = invoice_version
1116
1117 try:
1118 invoice_mongo_utils.add_invoice_dicts(final_receivable_data)
1119 except Exception as e:
1120 utils.write_error_logs(['Error inserting invoice dicts to mongo : ' + e.message, traceback.format_exc()])
1121 pass
1122
1123
1124def get_amount_in_word(receivable_data):
1125 wGenerator = Number2Words()
1126
1127 # receivable_data['total_multiple_load_unload_pts_in_word'] = utils.num_to_string(receivable_data['total_multiple_load_charges'] + receivable_data['total_multiple_unload_charges']) + " Only"
1128 receivable_data['total_load_unload_in_word'] = wGenerator.convertNumberToWords(
1129 int(receivable_data['total_load_unload_charges'])) + " Only"
1130 receivable_data['total_detention_in_word'] = wGenerator.convertNumberToWords(
1131 int(receivable_data['total_detention_charges'])) + " Only"
1132 receivable_data['total_checkpost_in_word'] = wGenerator.convertNumberToWords(
1133 int(receivable_data['total_checkpost_charges'])) + " Only"
1134 receivable_data['total_amt_in_word'] = wGenerator.convertNumberToWords(
1135 int(receivable_data['total_amount'])) + " Only"
1136
1137
1138def calculate_total_amts(receivable_order_data, amt_total_dict):
1139 if not amt_total_dict:
1140 amt_total_dict = { 'total_load_charges': 0, 'total_unload_charges': 0, 'total_amount': 0,
1141 'total_load_detention_charges': 0, 'total_unload_detention_charges': 0,
1142 'total_multiple_load_charges': 0, 'total_multiple_unload_charges': 0,
1143 'total_checkpost_charges': 0, 'total_plywood_charges': 0,
1144 'total_load_unload_charges': 0, 'total_detention_charges': 0,
1145 'total_green_tax_charges': 0
1146 }
1147 for item in receivable_order_data:
1148 amt_total_dict['total_amount'] += item['total_amount']
1149 amt_total_dict['total_load_charges'] += item['total_load_charges']
1150 amt_total_dict['total_unload_charges'] += item['total_unload_charges']
1151 amt_total_dict['total_plywood_charges'] += item['total_plywood_charges']
1152 amt_total_dict['total_load_detention_charges'] += item['total_load_detention_charges']
1153 amt_total_dict['total_unload_detention_charges'] += item['total_unload_detention_charges']
1154 amt_total_dict['total_multiple_load_charges'] += item['total_multiple_load_charges']
1155 amt_total_dict['total_multiple_unload_charges'] += item['total_multiple_unload_charges']
1156 amt_total_dict['total_checkpost_charges'] += item['total_checkpost_charges']
1157 amt_total_dict['total_green_tax_charges'] += item['total_green_tax_charges']
1158 amt_total_dict['total_load_unload_charges'] = amt_total_dict['total_load_charges'] + amt_total_dict[
1159 'total_unload_charges']
1160 amt_total_dict['total_detention_charges'] = amt_total_dict['total_load_detention_charges'] + amt_total_dict[
1161 'total_unload_detention_charges']
1162
1163 return amt_total_dict
1164
1165
1166def get_service_tax(item, inv_info, shipment_date, invoice_date):
1167 '''
1168 Calculate service tax and update in current item and if we need to change tax then it will
1169 add charge to total_amount
1170 '''
1171 status_msg, resp_status = '', 0
1172 separate_invoices = inv_info.separate_invoice
1173 separate_load_unload_invoice = separate_invoices.load_unload.is_set
1174 separate_detention_invoice = separate_invoices.detention.is_set
1175 separate_checkpost_invoice = separate_invoices.checkpost.is_set
1176 separate_summary_invoice = separate_invoices.summary_invoice.is_set
1177 separate_plywood_invoice = separate_invoices.plywood.is_set
1178 charge_service_tax_to_customer = inv_info.charge_service_tax_to_customer
1179 remaining_service_tax = inv_info.remaining_service_tax_in_invoice
1180 service_tax_in_invoice = inv_info.service_tax_in_invoice
1181 if not service_tax_in_invoice and not charge_service_tax_to_customer and not remaining_service_tax:
1182 return resp_status
1183
1184 status_msg, resp_status, item['total_service_tax'] = calculate_service_tax(item['total_amount'], invoice_date,
1185 remaining_service_tax)
1186 if resp_status:
1187 return resp_status
1188 if separate_load_unload_invoice:
1189 total_charges = item['total_load_unload_charges']
1190 status_msg, resp_status, item['load_unload_service_tax'] = calculate_service_tax(total_charges, invoice_date,
1191 remaining_service_tax)
1192 if separate_detention_invoice:
1193 total_charges = item['total_detention_charges']
1194 status_msg, resp_status, item['detention_service_tax'] = calculate_service_tax(total_charges, invoice_date,
1195 remaining_service_tax)
1196 if separate_checkpost_invoice:
1197 status_msg, resp_status, item['checkpost_service_tax'] = calculate_service_tax(item['total_checkpost_charges'],
1198 invoice_date,
1199 remaining_service_tax)
1200 if separate_plywood_invoice:
1201 status_msg, resp_status, item['plywood_service_tax'] = calculate_service_tax(item['total_plywood_charges'],
1202 invoice_date,
1203 remaining_service_tax)
1204 '''
1205 If we need to charge service tax to customer than add service tax to total amounts
1206 '''
1207 if charge_service_tax_to_customer:
1208 item['total_amount'] += float(item.get('total_service_tax', { }).get('total_tax', 0))
1209 item['total_load_unload_charges'] += float(item.get('load_unload_service_tax', { }).get('total_tax', 0))
1210 item['total_detention_charges'] += float(item.get('detention_service_tax', { }).get('total_tax', 0))
1211 item['total_checkpost_charges'] += float(item.get('checkpost_service_tax', { }).get('total_tax', 0))
1212 item['total_plywood_charges'] += float(item.get('plywood_service_tax', { }).get('total_tax', 0))
1213
1214 item['charge_service_tax_to_customer'] = charge_service_tax_to_customer
1215 item['remaining_service_tax_in_invoice'] = remaining_service_tax
1216 return 0
1217
1218
1219def put_constant_data_in_receivable(invoice_dict, current_ist_time, shipment_date, invoice_date, gstin):
1220 r_status, s_msg = 0, ''
1221 constants_dict = { }
1222 constants_dict['logo_path'] = settings.LOGO_PATH
1223 constants_dict['today_date'] = invoice_date or current_ist_time # invoice date
1224 constants_dict['cgstin'] = gstin
1225 constants_dict['zinka_email'] = constants.ZINKA_INV_EMAIL_ADDRESS
1226 constants_dict['zinka_pan'] = constants.ZINKA_PAN_NO
1227 constants_dict['zinka_address'] = constants.ZINKA_ADDRESS
1228 constants_dict['zinka_phone_no'] = constants.ZINKA_INV_PHONE_NUMBER
1229 constants_dict['zinka_serv_tax_reg_no'] = constants.ZINKA_SERV_TAX_NO
1230 constants_dict['asian_serv_tax_reg_no'] = constants.ASIAN_PAINT_SERV_REG_NO
1231 constants_dict['company_name'] = constants.COMPANY_NAME
1232 constants_dict['zinka_gstin'] = constants.ZINKA_GSTIN
1233 constants_dict['zinka_sac'] = constants.ZINKA_SAC
1234 constants_dict['zinka_state_code'] = constants.ZINKA_STATE_CODE
1235 constants_dict['zinka_arn'] = constants.ZINKA_ARN
1236 s_msg, r_status, result = get_fssai_license_number(shipment_date)
1237 constants_dict['fssai_license'] = result
1238 invoice_dict['constant_data'] = constants_dict
1239 return s_msg, r_status
1240
1241
1242def put_address_in_receivable(order, inv_info):
1243 cup = order.user.customeruserprofile
1244 address = None
1245 if order.use_to_address_for_invoice:
1246 warehouse_obj = order.end_location
1247 location_obj = order.to_city
1248 else:
1249 warehouse_obj = order.start_location
1250 location_obj = order.from_city
1251 use_warehouse_address = inv_info.use_warehouse_address
1252 if use_warehouse_address:
1253 address = warehouse_obj.address
1254 return address
1255 address = inv_info.address
1256 if not address:
1257 address = cup.name + "\n" + warehouse_obj.name + "\n" + location_obj.city
1258
1259 return address
1260
1261
1262def wrap_text(text):
1263 WRAP_LENGTH = 15
1264 wrapped_text = ''
1265 if text:
1266 for i in range(0, len(text), WRAP_LENGTH):
1267 wrapped_text += text[i:i + WRAP_LENGTH] + ' '
1268
1269 return wrapped_text.strip()
1270
1271
1272def get_receivable_data_for_invoice(order, cust_master_rate, inv_data, current_ist_time, custom_from_city, inv_info):
1273 status_msg, resp_status, result = '', 0, []
1274 order_invoice_type = inv_info.order_invoice_type
1275 order_finance = order.orderfinancedetails
1276 customer_user_profile = order.user.customeruserprofile
1277 # store_rate_in_invoicedata(cust_master_rate,cust_type,inv_data)
1278 total_amount, is_first_entry, num_inv_data = 0, True, len(inv_data)
1279 custom_to_city, custom_truck_type, receivable_data = None, None, { }
1280 loading_charge, unloading_charge, total_weight = 0, 0, 0
1281 is_first_entry_2 = True
1282
1283 separate_invoices = inv_info.separate_invoice
1284 separate_load_unload_invoice = separate_invoices.load_unload.is_set
1285 separate_detention_invoice = separate_invoices.detention.is_set
1286 separate_checkpost_invoice = separate_invoices.checkpost.is_set
1287 separate_summary_invoice = separate_invoices.summary_invoice.is_set
1288 separate_plywood_invoice = separate_invoices.plywood.is_set
1289 charge_service_tax_to_customer = inv_info.charge_service_tax_to_customer
1290
1291 freight_distributed_by = inv_info.freight_distributed_by
1292 assigned_truck_type = order.assigned_truck.truck_type
1293 # custom name of to location customer based
1294 custom_to_loc_obj = get_customer_custom_field(order.user, order.to_city, 1)
1295 if isinstance(custom_to_loc_obj, CustomCustomerLocation):
1296 custom_to_city = custom_to_loc_obj.custom_location
1297
1298 is_finalized = inv_data[0].is_finalized
1299 inv_data_list = []
1300 for item_detail in inv_data:
1301 item = model_to_dict(item_detail)
1302 inv_data_list.append(item)
1303 current_item_total = 0
1304
1305 # Wrap these fields for proper display in Invoice Pdf
1306 if item.get('invoice_number'):
1307 item['invoice_number'] = wrap_text(item['invoice_number'])
1308 if item.get('do_sto_ssl_num'):
1309 item['do_sto_ssl_num'] = wrap_text(item['do_sto_ssl_num'])
1310 if item.get('service_entry_num'):
1311 item['service_entry_num'] = wrap_text(item['service_entry_num'])
1312 if item.get('obd_number'):
1313 item['obd_number'] = wrap_text(item['obd_number'])
1314
1315 item['cn_date'] = utils.utc_to_timezone(item['cn_date'])
1316 if item.get('date_of_arrival'):
1317 item['date_of_arrival'] = utils.utc_to_timezone(item['date_of_arrival'])
1318 if item.get('date_of_delivery'):
1319 item['date_of_delivery'] = utils.utc_to_timezone(item['date_of_delivery'])
1320 item['type_of_pkgs'] = item['type_of_pkgs'] or ''
1321 item['desc_of_goods'] = item['desc_of_goods'] or ''
1322 item['plywood_charges'] = float(item['plywood_charges'] or 0)
1323 item['green_tax'] = float(item['green_tax'] or 0)
1324 item['invoicing_address'] = item.get('invoicing_address', '') or ''
1325 item['checkpost_charges'] = float(item['checkpost_charges'] or 0)
1326 item['equivalent_weight'] = float(item['equivalent_weight'] or 0)
1327 item['multiple_unloading_pt_charge'] = float(item['multiple_unloading_pt_charge'] or 0)
1328 item['multiple_loading_pt_charge'] = float(item['multiple_loading_pt_charge'] or 0)
1329 item['qty_moved_tons'] = utils.weight_conversion(item['weight'], to_unit='tons')
1330 item['equivalent_weight_tons'] = utils.weight_conversion(item['equivalent_weight'], to_unit='tons')
1331 item['max_weight_eq_weight'] = float(max(item['equivalent_weight'], item['weight']))
1332 item['charged_quantity'] = float(
1333 utils.calculate_percentage(float(item.get('freight_amount') or 0), float(cust_master_rate['raw_rate'])))
1334 '''
1335 If is_finalized is set detention will be picked up from inv detail
1336 othwise system calculated value will be used
1337 '''
1338 if not is_finalized:
1339 if is_first_entry_2:
1340 item['loading_charge'] = float(cust_master_rate['loading_charge'] or 0)
1341 item['unloading_charge'] = float(cust_master_rate['unloading_charge'] or 0)
1342 item['loading_detention'] = float(cust_master_rate['loading_detention'] or 0)
1343 item['unloading_detention'] = float(cust_master_rate['unloading_detention'] or 0)
1344 is_first_entry_2 = False
1345 else:
1346 item['loading_charge'] = 0
1347 item['unloading_charge'] = 0
1348 item['loading_detention'] = 0
1349 item['unloading_detention'] = 0
1350 else:
1351 item['loading_detention'] = float(item['loading_detention'] or 0)
1352 item['unloading_detention'] = float(item['unloading_detention'] or 0)
1353 item['loading_charge'] = float(item['loading_charge'] or 0)
1354 item['unloading_charge'] = float(item['unloading_charge'] or 0)
1355 item_detail.loading_detention = item['loading_detention']
1356 item_detail.unloading_detention = item['unloading_detention']
1357 item_detail.loading_charge = item['loading_charge']
1358 item_detail.unloading_charge = item['unloading_charge']
1359 loading_charge += item['loading_charge']
1360 unloading_charge += item['unloading_charge']
1361 if order_invoice_type != 2:
1362 '''
1363 maintain master inv data if we need to put multiple inv data in one row
1364 for example : marico for location other than coimthore
1365 '''
1366 if is_first_entry:
1367
1368 if not is_finalized:
1369 item['freight_amount'] = float(cust_master_rate['rate'])
1370 item['freight_amount'] = float(item['freight_amount'] or 0)
1371 item['charged_quantity'] = float(
1372 utils.calculate_percentage(float(item['freight_amount']), float(cust_master_rate['raw_rate'])))
1373 receivable_data.update(item)
1374 is_first_entry = False
1375 else:
1376
1377 if not is_finalized:
1378 item['freight_amount'] = 0
1379
1380 item['freight_amount'] = float(item['freight_amount'] or 0)
1381
1382 receivable_data['freight_amount'] += item['freight_amount']
1383 receivable_data['loading_detention'] += item['loading_detention']
1384 receivable_data['unloading_detention'] += item['unloading_detention']
1385 receivable_data['checkpost_charges'] += item['checkpost_charges'] or 0
1386 receivable_data['plywood_charges'] += item['plywood_charges']
1387 receivable_data['green_tax'] += item['green_tax']
1388 receivable_data['multiple_loading_pt_charge'] += item['multiple_loading_pt_charge']
1389 receivable_data['multiple_unloading_pt_charge'] += item['multiple_unloading_pt_charge']
1390 if item.get('invoice_number'):
1391 receivable_data['invoice_number'] += ", " + item['invoice_number']
1392 if item.get('do_sto_ssl_num'):
1393 receivable_data['do_sto_ssl_num'] += ", " + wrap_text(item['do_sto_ssl_num'])
1394 if item.get('service_entry_num'):
1395 receivable_data['service_entry_num'] += ", " + wrap_text(item['service_entry_num'])
1396 if item.get('obd_number'):
1397 receivable_data['obd_number'] += ", " + wrap_text(item['obd_number'])
1398 receivable_data['lr_number'] += ", " + item['lr_number']
1399 if receivable_data.get('num_pkgs') is not None:
1400 receivable_data['num_pkgs'] += (item['num_pkgs'] or 0)
1401 else:
1402 receivable_data['num_pkgs'] = (item['num_pkgs'] or 0)
1403 receivable_data['weight'] += item['weight']
1404 receivable_data['unloading_weight'] += item['unloading_weight']
1405 receivable_data['equivalent_weight'] += item['equivalent_weight']
1406 receivable_data['qty_moved_tons'] += item['qty_moved_tons']
1407 receivable_data['equivalent_weight_tons'] += item['equivalent_weight_tons']
1408 receivable_data['max_weight_eq_weight'] += float(
1409 max(receivable_data['equivalent_weight'], receivable_data['weight']))
1410 receivable_data['charged_quantity'] += item.get('charged_quantity', 0)
1411 #receivable_data['truck_departure_time'] = "Hello World " # TODO -- FETCH THIS DATA FROM ES.
1412 item['total_freight'] = float(item['freight_amount'])
1413 if not separate_detention_invoice:
1414 item['total_freight'] = item['total_freight'] + item['loading_detention'] + item['unloading_detention']
1415 if not separate_checkpost_invoice:
1416 item['total_freight'] = item['total_freight'] + item['checkpost_charges']
1417 item['total_freight'] += item['multiple_loading_pt_charge']
1418 item['total_freight'] += item['multiple_unloading_pt_charge']
1419 if not separate_plywood_invoice:
1420 item['total_freight'] += item['plywood_charges']
1421 item['total_freight'] += item['green_tax']
1422 total_amount += item['total_freight']
1423 item_detail.freight_amount = item['freight_amount']
1424
1425 else:
1426 if freight_distributed_by == 1:
1427 total_weight += float(item['weight'])
1428 elif freight_distributed_by == 2:
1429 total_weight += float(item['num_pkgs'] or 0)
1430 elif freight_distributed_by == 3:
1431 total_weight += float(item['qty_moved_tons'])
1432
1433 if not is_finalized:
1434 item_detail.save()
1435
1436 if order_invoice_type != 2 and not receivable_data.get('freight_amount'):
1437 resp_status = 1
1438 status_msg = 'Looks like invoice details are finalized and freight amount is zero, Either unfinalized details so that system calculates value or enter freight amount'
1439 return status_msg, resp_status, result
1440
1441 try:
1442 truck_dept_src_date = inv_data_list[0]['cn_date']
1443 except Exception as e:
1444 truck_dept_src_date = order.order_status.get(status='Payment Done').dt_added
1445
1446 receivable_data['raw_rate'] = float(cust_master_rate['raw_rate'])
1447 receivable_data['other_charges'] = float(cust_master_rate.get('other_charges') or 0)
1448
1449 receivable_data['rate_type'] = cust_master_rate['rate_type']
1450 receivable_data['order'] = order
1451 receivable_data['customer'] = customer_user_profile.name
1452 receivable_data['from_city'] = item_detail.invoice_from_city or custom_from_city or order.from_city.city
1453 receivable_data['to_city'] = item_detail.invoice_to_city or custom_to_city or order.to_city.city
1454 receivable_data['from_warehouse'] = order.start_location.name
1455 # receivable_data['from_warehouse_details'] = utils.model_obj_to_dict(order.start_location)
1456 receivable_data['from_warehouse_code'] = order.start_location.code
1457 receivable_data['from_warehouse_address'] = order.start_location.address
1458 receivable_data['from_warehouse_customer_name'] = order.start_location.customer_name
1459 receivable_data['from_warehouse_dest_customer_name'] = order.start_location.dest_customer_name
1460 receivable_data['from_sublocation'] = order.start_location.sublocation.name
1461 receivable_data['to_sublocation'] = order.end_location.sublocation.name
1462 receivable_data['truck_number'] = order.assigned_truck.registration_number
1463 receivable_data['truck_type'] = inv_data[0].custom_truck_type or inv_data[
1464 0].truck_type.truck_type or assigned_truck_type.truck_type
1465 receivable_data['loading_charge'] = loading_charge
1466 receivable_data['unloading_charge'] = unloading_charge
1467 receivable_data['separate_checkpost_invoice'] = separate_checkpost_invoice
1468 receivable_data['separate_detention_invoice'] = separate_detention_invoice
1469 receivable_data['separate_load_unload_invoice'] = separate_load_unload_invoice
1470 receivable_data['separate_summary_invoice'] = separate_summary_invoice
1471 receivable_data['separate_plywood_invoice'] = separate_plywood_invoice
1472 receivable_data['service_tax_in_invoice'] = inv_info.service_tax_in_invoice
1473 if inv_data_list[0]['date_of_arrival']:
1474 receivable_data['transit_time_taken'] = (inv_data_list[0]['date_of_arrival'] - truck_dept_src_date).days
1475 else:
1476 receivable_data['transit_time_taken'] = ''
1477 receivable_data['sll_transit_time'] = cust_master_rate['transit_time']
1478 if item.get('rate_as_per_serv_no'):
1479 wGenerator = Number2Words()
1480 receivable_data['serv_no_rate_word'] = wGenerator.convertNumberToWords(
1481 int(item['rate_as_per_serv_no'] or 0)) + " Only"
1482
1483 if receivable_data['transit_time_taken'] and receivable_data['sll_transit_time'] \
1484 and int(receivable_data['transit_time_taken']) > int(receivable_data['sll_transit_time']):
1485 receivable_data['chargebale_tt_days'] = int(receivable_data['transit_time_taken']) - int(
1486 receivable_data['sll_transit_time'])
1487 else:
1488 receivable_data['chargebale_tt_days'] = 0
1489
1490 if not separate_load_unload_invoice:
1491 total_amount += loading_charge
1492 total_amount += unloading_charge
1493
1494 if order_invoice_type != 2:
1495 receivable_data['inv_details'] = inv_data_list
1496
1497 receivable_data['total_amount'] = total_amount
1498 if order_invoice_type == 1:
1499 status_msg,resp_status,freight_bill_no = get_or_generate_freight_bill_number(order, inv_info)
1500 if resp_status:
1501 return status_msg,resp_status,[]
1502 receivable_data['freight_bill_num'] = freight_bill_no
1503 receivable_data['total_load_charges'] = float(receivable_data['loading_charge'])
1504 receivable_data['total_unload_charges'] = float(receivable_data['unloading_charge'])
1505 receivable_data['total_load_unload_charges'] = receivable_data['total_load_charges'] + receivable_data[
1506 'total_unload_charges']
1507 receivable_data['total_load_detention_charges'] = float(receivable_data['loading_detention'])
1508 receivable_data['total_unload_detention_charges'] = float(receivable_data['unloading_detention'])
1509 receivable_data['total_detention_charges'] = receivable_data['total_load_detention_charges'] + receivable_data[
1510 'total_unload_detention_charges']
1511 receivable_data['total_multiple_load_charges'] = float(receivable_data['multiple_loading_pt_charge'])
1512 receivable_data['total_multiple_unload_charges'] = float(receivable_data['multiple_unloading_pt_charge'])
1513 receivable_data['total_checkpost_charges'] = float(receivable_data['checkpost_charges'])
1514 receivable_data['total_plywood_charges'] = float(receivable_data['plywood_charges'])
1515 receivable_data['total_green_tax_charges'] = float(receivable_data['green_tax'])
1516 result.append(receivable_data)
1517 else:
1518 index = 0
1519 for item_detail in inv_data:
1520 item = inv_data_list[index]
1521 item.pop('order', None)
1522 if len(inv_data) == 1:
1523 weitage_by_weight = 1
1524 else:
1525 s_msg, r_status, weitage_by_weight = get_weitage_by_distribution_fields(item, total_weight,
1526 freight_distributed_by)
1527 print(weitage_by_weight)
1528 if r_status == 1:
1529 return s_msg, r_status, []
1530 if not is_finalized:
1531 item['freight_amount'] = float(float(cust_master_rate['rate']) * weitage_by_weight)
1532 item_detail.freight_amount = item['freight_amount']
1533 item_detail.save()
1534 elif is_finalized and not item_detail.freight_amount:
1535 resp_status = 1
1536 status_msg = 'Freight amount should be present if invoice details are finalized'
1537 return status_msg, resp_status, result
1538
1539 item['charged_quantity'] = (float(item['freight_amount']) / float(cust_master_rate['raw_rate']))
1540 item['total_freight'] = item['freight_amount']
1541
1542 if not separate_load_unload_invoice:
1543 item['total_freight'] = item['total_freight'] + item['loading_charge'] + item['unloading_charge']
1544 if not separate_detention_invoice:
1545 item['total_freight'] = item['total_freight'] + item['loading_detention'] + item['unloading_detention']
1546 if not separate_checkpost_invoice:
1547 item['total_freight'] = item['total_freight'] + item['checkpost_charges']
1548 if not separate_plywood_invoice:
1549 item['total_freight'] += item['plywood_charges']
1550 item['total_freight'] += item['green_tax']
1551 item['total_freight'] += item['multiple_loading_pt_charge']
1552 item['total_freight'] += item['multiple_unloading_pt_charge']
1553 receivable_data.update(item)
1554 receivable_data['total_amount'] = item['total_freight']
1555 status_msg,resp_status,freight_bill_no = get_or_generate_freight_bill_number(order, inv_info, item['invoice_number'])
1556 if resp_status:
1557 return status_msg,resp_status,result
1558 receivable_data[
1559 'truck_type'] = item_detail.custom_truck_type or item_detail.truck_type.truck_type or assigned_truck_type.truck_type
1560 receivable_data['freight_bill_num'] = freight_bill_no
1561 receivable_data['total_load_charges'] = float(receivable_data['loading_charge'])
1562 receivable_data['total_unload_charges'] = float(receivable_data['unloading_charge'])
1563 receivable_data['total_load_unload_charges'] = receivable_data['total_load_charges'] + receivable_data[
1564 'total_unload_charges']
1565 receivable_data['total_checkpost_charges'] = float(receivable_data['checkpost_charges'])
1566 receivable_data['total_plywood_charges'] = float(receivable_data['plywood_charges'])
1567 receivable_data['total_green_tax_charges'] = float(receivable_data['green_tax'])
1568 receivable_data['total_load_detention_charges'] = float(receivable_data['loading_detention'])
1569 receivable_data['total_unload_detention_charges'] = float(receivable_data['unloading_detention'])
1570 receivable_data['total_detention_charges'] = receivable_data['total_load_detention_charges'] + \
1571 receivable_data['total_unload_detention_charges']
1572 receivable_data['charged_weight_per_inv'] = weitage_by_weight * float(receivable_data['qty_charged_tons'])
1573 receivable_data['from_city'] = item_detail.invoice_from_city or custom_from_city or order.from_city.city
1574 receivable_data['to_city'] = item_detail.invoice_to_city or custom_to_city or order.to_city.city
1575 receivable_data['from_sublocation'] = order.start_location.sublocation.name
1576 receivable_data['to_sublocation'] = order.end_location.sublocation.name
1577 result.append(deepcopy(receivable_data))
1578 result[index]['order'] = order
1579 index += 1
1580
1581 return status_msg, resp_status, result
1582
1583
1584def get_weitage_by_distribution_fields(item, total_weight, freight_distributed_by):
1585 '''
1586 Get weitage to divide freight among multiple invoice by distrubution field
1587 '''
1588 resp_status, status_msg, result = 0, '', { }
1589 current_field = 0
1590 if freight_distributed_by == 1:
1591 current_field = float(item['weight'])
1592 elif freight_distributed_by == 2:
1593 current_field = float(item['num_pkgs'] or 0)
1594 elif freight_distributed_by == 3:
1595 current_field = float(item['qty_charged_tons'])
1596 else:
1597 resp_status = 1
1598 status_msg = 'Please Select valid field for distruting freight'
1599 return status_msg, resp_status, result
1600 try:
1601
1602 weitage_by_weight = float(current_field) / float(total_weight)
1603 except Exception as e:
1604 resp_status = 1
1605 status_msg = "Freight Distribution value can't be zero"
1606 return status_msg, resp_status, result
1607 return status_msg, resp_status, float(weitage_by_weight)
1608
1609
1610def generate_file_name(order_id='', freight_bill_num='', invoice_number='', order_invoice_type=''):
1611 '''
1612 This function will generate the file name based on num of invoice per order
1613 It will also attach a random number to file so that if invoice is generated multiple times than
1614 it doesn't override exiting fie on s3
1615 '''
1616
1617 random_number_str = str(utils.random_with_N_digits(5))
1618 invoice_number = utils.replace_special_character_with_underscore(invoice_number)
1619
1620 if order_invoice_type == 2:
1621 return "cust_invoice_" + str(order_id) + "_" + invoice_number + "_" + random_number_str
1622 elif order_invoice_type == 3:
1623 return "cust_invoice_" + freight_bill_num.replace('/', '_') + "_" + random_number_str
1624 return "cust_invoice_" + str(order_id) + "_" + random_number_str
1625
1626
1627def validate_mandatory_fields_for_invoice_generation(business_type, oi_info_entry):
1628 """
1629 Given an OrderInvoiceRelatedInfo entry, this method validates some financial & non-financial fields depending on whether the order is GB or NB
1630 :param business_type:
1631 :param oi_info_entry:
1632 :return:
1633 """
1634
1635 if not oi_info_entry:
1636 return False
1637
1638 # weight, num_pkgs & truck_type_id are mandatory for both GB & NB orders
1639 is_valid = not (oi_info_entry.weight is None or not oi_info_entry.truck_type or oi_info_entry.truck_type.id is None)
1640 if business_type == 1: # NB
1641 return is_valid
1642
1643 # src_detention_days & dest_detention_days are mandatory only for GB
1644 if business_type == 2: # GB
1645 return is_valid and not (oi_info_entry.src_detention_days is None or oi_info_entry.num_pkgs is None or oi_info_entry.dest_detention_days is None)
1646
1647
1648def validate_order_info_invoice_generation(order, halt_charges_validate=False, location_id='', customer_id=''):
1649 from cms.utils import customer_whitelisted_cms
1650 '''
1651 This function validate various paramertes of order for invoice generation
1652 location is passed to check if given order has same from location as given location_id
1653 '''
1654
1655 status_msg, resp_status, result = '', 0, { }
1656 invoice_generation_status = (
1657 'Invoice Pending', 'Invoice Rejected', 'Invoice Generated', 'Invoice Verification Pending', 'Invoice Verified')
1658
1659 if not isinstance(order, Order):
1660 try:
1661 order = Order.objects.get(pk=order)
1662 except Order.DoesNotExist:
1663 resp_status = 1
1664 status_msg = 'Order With id %s does not exists' % order
1665 return status_msg, resp_status, result
1666
1667 if location_id and order.from_city.id != location_id:
1668 status_msg = "Order from_city is %s doesn't match with given location id %s" % (
1669 order.from_city.city, location_id)
1670 resp_status = 1
1671 return status_msg, resp_status, result
1672
1673 if customer_id and order.user.id != customer_id:
1674 status_msg = "Order customer is %s doesn't match with given customer %s" % (order.user.id, customer_id)
1675 resp_status = 1
1676 return status_msg, resp_status, result
1677
1678 if order.order_invoice_status not in invoice_generation_status:
1679 resp_status = 1
1680 status_msg = "Invoice can only be generated if order is in %s status" % str(invoice_generation_status)
1681 return status_msg, resp_status, result
1682 '''
1683 status_msg_inv,resp_status_inv,inv_data = get_order_invoices({'order_id':order.id})
1684 if resp_status == 1:
1685 status_msg += status_msg_inv + ", Error in getting invoice data"
1686 return resp_status_inv, status_msg, result
1687 '''
1688 inv_data = OrderInvoiceRelatedInfo.objects.filter(order=order, is_deleted=False)
1689 order_business_type = order.customer_sector.business_type.id
1690
1691 if not inv_data:
1692 resp_status = 1
1693 status_msg = "No invoice data for order %s" % order.id
1694 return status_msg, resp_status, result
1695 freight_amount, is_finalized = 0, False
1696
1697 for item in inv_data:
1698 # if not item.date_of_arrival or not item.date_of_delivery:
1699 # resp_status = 1
1700 # status_msg = "Date of arrival and Date of delivery should be present"
1701 # return status_msg, resp_status, result
1702 if not item.invoice_number or not item.lr_number:
1703 resp_status = 1
1704 status_msg = "Invoice Number or lr number can't be blank"
1705 if not is_finalized:
1706 is_finalized = item.is_finalized
1707
1708 if not validate_mandatory_fields_for_invoice_generation(order_business_type, item):
1709 resp_status = 1
1710 status_msg = "Please make sure invoice details are entered properly with all the mandatory field"
1711 return status_msg, resp_status, result
1712
1713 freight_amount += float(item.freight_amount or 0)
1714
1715 if is_finalized and not freight_amount:
1716 resp_status = 1
1717 status_msg = 'Invoice details are finalized and freight amount is not entered'
1718 return status_msg, resp_status, result
1719
1720 status_msg, resp_status, cust_master_rate = get_customer_master_rate(order, calculated_halt_charges=True)
1721 if resp_status and not inv_data[0].is_finalized:
1722 return status_msg, resp_status, result
1723
1724 if customer_whitelisted_cms(order.end_date, customer=order.user):
1725 cust_master_rate = cms_populated_data(order, cust_master_rate, inv_data)
1726
1727 result['order'] = order
1728 result['customer_master_rate'] = cust_master_rate
1729 result['inv_data'] = inv_data
1730 return status_msg, resp_status, result
1731
1732
1733def cms_populated_data(order, cust_master_rate, inv_data):
1734 cust_master_rate['rate'] = 0
1735 cust_master_rate['checkpost_charges'] = 0
1736 cust_master_rate['green_tax'] = 0
1737 cust_master_rate['loading_charge'] = 0
1738 cust_master_rate['loading_detention'] = 0
1739 cust_master_rate['multiple_loading_charges'] = 0
1740 cust_master_rate['multiple_unloading_charges'] = 0
1741 cust_master_rate['plywood_charges'] = 0
1742 cust_master_rate['raw_rate'] = cust_master_rate['cms_freight']['components'][0]['raw_rate']
1743 cust_master_rate['unloading_charge'] = 0
1744 cust_master_rate['unloading_detention'] = 0
1745
1746 for item in inv_data:
1747 if item.checkpost_charges:
1748 cust_master_rate['checkpost_charges'] += item.checkpost_charges
1749 if item.green_tax:
1750 cust_master_rate['green_tax'] += item.green_tax
1751 if item.loading_charge:
1752 cust_master_rate['loading_charge'] += item.loading_charge
1753 if item.loading_detention:
1754 cust_master_rate['loading_detention'] += item.loading_detention
1755 if item.multiple_loading_pt_charge:
1756 cust_master_rate['multiple_loading_charges'] += item.multiple_loading_pt_charge
1757 if item.multiple_unloading_pt_charge:
1758 cust_master_rate['multiple_unloading_charges'] += item.multiple_unloading_pt_charge
1759 if item.plywood_charges:
1760 cust_master_rate['plywood_charges'] += item.plywood_charges
1761 if item.unloading_charge:
1762 cust_master_rate['unloading_charge'] += item.unloading_charge
1763 if item.unloading_detention:
1764 cust_master_rate['unloading_detention'] += item.unloading_detention
1765 if item.freight_amount:
1766 cust_master_rate['rate'] += item.freight_amount
1767
1768 return cust_master_rate
1769
1770
1771def exact_match(inv_info, order):
1772 order_start = ''
1773 order_end = ''
1774 if inv_info.from_entity_type == 1:
1775 order_start = getattr(order, 'start_location')
1776 elif inv_info.from_entity_type == 2:
1777 order_start = getattr(order, 'from_city')
1778 if inv_info.to_entity_type == 1:
1779 order_end = getattr(order, 'end_location')
1780 elif inv_info.to_entity_type == 2:
1781 order_end = getattr(order, 'to_city')
1782 if (inv_info.from_entity_type == 3 or inv_info.from_entity == order_start.id) and (
1783 inv_info.to_entity_type == 3 or inv_info.to_entity == order_end.id):
1784 return True
1785 else:
1786 return False
1787
1788
1789@role_function_check("GENERATE_INVOICE_BACKEND")
1790def generate_invoices_multiple_order(request, obj_id, filters, fields, external, nested=False):
1791 '''
1792 This API generated multiple invoice for given orders or freight_bill_nos
1793 and email generated invoices to current user
1794 '''
1795
1796 status_msg, resp_status, result = '', 0, []
1797 order_ids, freight_bill_nos = [], []
1798 user = request.user
1799 request_body = utils.parse_request_body(request)
1800 invalid_dict, invalid_list = { }, []
1801 order_invoice_type, no_order_in_one_invoice = '', 0
1802 use_warehouse_address, inv_to_loc, is_same_to_loc_orders = False, 0, True
1803 inv_to_loc_orders, prev_loc, current_loc = [], None, None
1804 freight_distributed_by = None
1805
1806 if not request_body:
1807 resp_status = 1
1808 status_msg = 'Failed to parse request body'
1809 return status_msg, resp_status, result
1810
1811 if request_body.get('order_ids'):
1812 if not isinstance(request_body.get('order_ids'), list):
1813 order_ids = [request_body['order_ids']]
1814 else:
1815 order_ids = request_body['order_ids']
1816 elif request_body.get('freight_bill_nos'):
1817 if not isinstance(request_body.get('freight_bill_nos'), list):
1818 freight_bill_nos = [request_body['freight_bill_nos']]
1819 else:
1820 freight_bill_nos = request_body['freight_bill_nos']
1821 else:
1822 resp_status = 1
1823 status_msg = "Either 'order_ids' or 'freight_bill_nos' are required"
1824 return status_msg, resp_status, result
1825
1826 if not user.email:
1827 resp_status = 1
1828 status_msg = 'Your email_id is not added, please add you email where invoices will be emailed'
1829 return status_msg, resp_status, result
1830
1831 customer_id = request_body.get('customer_id')
1832 location_id = request_body.get('location_id')
1833
1834 req_orders = order_ids
1835
1836 if not req_orders:
1837 req_orders = get_order_id_from_freight_billno(freight_bill_nos)
1838 req_orders = [item for sublist in req_orders for item in sublist]
1839
1840 '''
1841 Check if the configuration is unique for multiple orders
1842 '''
1843 inv_infos = InvoiceInfo.objects.filter(customer_id=customer_id).order_by('-priority')
1844 inv_info = None
1845 list_inv_infos = []
1846 for order_id in req_orders:
1847 order = Order.objects.get(id=order_id)
1848 for item in inv_infos:
1849 if exact_match(item, order):
1850 inv_info = item
1851 list_inv_infos.append(inv_info.id)
1852 break
1853 list_inv_infos = list(set(list_inv_infos))
1854 if len(list_inv_infos) != 1 and len(order_ids) != 0:
1855 resp_status = 1
1856 status_msg = 'Multiple invoice configurations for the given orders'
1857 return status_msg, resp_status, result
1858
1859 '''
1860 Fetch location from warehouse
1861 '''
1862 if not location_id and request_body.get('warehouse_id'):
1863 query = "select location_id from base_warehouse W inner join base_sublocation S on sublocation_id = S.id where W.id = %s" % request_body.get(
1864 'warehouse_id')
1865 status_msg, resp_status, location_id = utils.make_db_query(query)
1866 if resp_status == 1 or not location_id:
1867 return status_msg, resp_status, result
1868 location_id = location_id[0]['location_id']
1869 request_body['location_id'] = location_id
1870 elif not location_id:
1871 resp_status = 1
1872 status_msg = 'Either location or warehouse id is required'
1873 return status_msg, resp_status, result
1874
1875 try:
1876 cust_inv_info_obj = inv_info
1877 order_invoice_type = int(cust_inv_info_obj.order_invoice_type)
1878 if order_invoice_type == 3:
1879 no_order_in_one_invoice = int(cust_inv_info_obj.no_order_in_one_invoice)
1880 use_warehouse_address = cust_inv_info_obj.use_warehouse_address
1881 if order_invoice_type == 2:
1882 freight_distributed_by = int(cust_inv_info_obj.freight_distributed_by)
1883
1884 except Exception as e:
1885 pass
1886
1887 '''
1888 customeruserprofile = utils.get_exact_match(CustomerUserProfile,value_dict={'user_id':customer_id})
1889 if not isinstance(customeruserprofile, CustomerUserProfile):
1890 resp_status = 1
1891 status_msg = "Customer Information does not exixts, Please check if customer is added successfully"
1892 return status_msg, resp_status, result
1893
1894 if not order_invoice_type:
1895 order_invoice_type = int(customeruserprofile.order_invoice_type)
1896 use_warehouse_address = customeruserprofile.use_warehouse_address
1897
1898 '''
1899 '''
1900 Many order one invoice then check for no of order in one invoice
1901 '''
1902 if order_invoice_type == 3:
1903 if not no_order_in_one_invoice or no_order_in_one_invoice < 2:
1904 resp_status = 1
1905 status_msg = 'No of invoice in one order should be greater than 2 for multiple order in one order'
1906 return status_msg, resp_status, result
1907
1908 '''
1909 check If appropriote fields is selected for diving freight
1910 '''
1911 if order_invoice_type == 2:
1912 if not freight_distributed_by:
1913 resp_status = 1
1914 status_msg = 'Please choose appropriate fields for distributing freight among different values'
1915 return status_msg, resp_status, result
1916
1917 if order_ids:
1918 # validate data required for invoice generation
1919 orders = Order.objects.filter(id__in=order_ids)
1920 # get order which are alredy assigned to some other freight bill number and return error if so
1921 already_generated_orders = orders.exclude(freight_bill_number__freight_bill_number__isnull=True).exclude(
1922 freight_bill_number__freight_bill_number__exact='')
1923 if already_generated_orders:
1924 for i_order in already_generated_orders:
1925 s_msg = 'Already assigned freight bill number %s ' % i_order.freight_bill_number.all()[
1926 0].freight_bill_number
1927 invalid_dict = { 'order_id': i_order.id, 'reason': s_msg }
1928 invalid_list.append(invalid_dict)
1929 continue
1930 resp_status = 2
1931 return status_msg, resp_status, invalid_list
1932
1933 for order in orders:
1934 s_msg, r_status, result_val = validate_order_info_invoice_generation(order, halt_charges_validate=True,
1935 location_id=location_id,
1936 customer_id=customer_id)
1937 if r_status:
1938 invalid_dict = { 'order_id': order.id, 'reason': s_msg }
1939 invalid_list.append(invalid_dict)
1940 continue
1941 elif order.order_invoice_status != 'Invoice Pending':
1942 s_msg = 'Invoice for this order is already generated and order is in %s status' % order.order_invoice_status
1943 invalid_dict = { 'order_id': order.id, 'reason': s_msg }
1944 invalid_list.append(invalid_dict)
1945 continue
1946 elif order.use_to_address_for_invoice:
1947 inv_to_loc += 1
1948 inv_to_loc_orders.append(order.id)
1949
1950 '''
1951 Multiple order one invoice nd any of the order with to location then make sure all the selected orders
1952 are belong to same location of warehouse (warehouse if invoice need to generate with warehouse address)
1953 '''
1954 if order_invoice_type == 3 and is_same_to_loc_orders:
1955 if use_warehouse_address:
1956 current_loc = order.end_location
1957 else:
1958 current_loc = order.to_city
1959 if prev_loc and current_loc != prev_loc:
1960 is_same_to_loc_orders = False
1961 prev_loc = order.end_location
1962
1963 if inv_to_loc and not is_same_to_loc_orders:
1964 s_msg = 'For order %s invoice with to location address will be generated. Please make sure ' \
1965 ' orders with same to location are selected to generate invoice' % (',').join(
1966 str(item_id) for item_id in inv_to_loc_orders)
1967 resp_status = 1
1968 return s_msg, resp_status, result
1969
1970 '''
1971 If any of the order is invalid then return
1972 '''
1973 if invalid_list:
1974 resp_status = 2
1975 return status_msg, resp_status, invalid_list
1976
1977 '''
1978 create alist of order_ids if there are multiple order in one invoice
1979 '''
1980 if no_order_in_one_invoice and order_ids:
1981
1982 final_order_ids, no_order = [], len(order_ids)
1983
1984 for index in range(0, no_order, no_order_in_one_invoice):
1985 temp_list = order_ids[index:index + no_order_in_one_invoice]
1986
1987 final_order_ids.append(temp_list)
1988 order_ids = final_order_ids
1989 else:
1990 order_ids = [order_ids]
1991 '''
1992 If any of the order is invalid then return
1993 '''
1994 if invalid_list:
1995 resp_status = 2
1996 return status_msg, resp_status, invalid_list
1997
1998 else:
1999 order_ids = get_order_id_from_freight_billno(freight_bill_nos)
2000
2001 flat_order_ids = [item for sublist in order_ids for item in sublist]
2002
2003 # check if order exists in envelope
2004 status_msg, resp_status, result = check_invoice_existing_envelope(order_list=flat_order_ids)
2005 if resp_status:
2006 return status_msg, resp_status, result
2007
2008 # check if order is already locked
2009 s_msg, r_status, _result = check_insert_invoice_lock(flat_order_ids, user)
2010 if r_status == 1:
2011 return s_msg, r_status, result
2012
2013 # Sending message to rabbit MQ for generating invoices and emailing
2014 try:
2015 q_factory = QueueFactory()
2016 q_message = { }
2017 q_message['order_ids'] = order_ids
2018 q_message['user_id'] = user.id
2019 q_message['request_body'] = request_body
2020 q_message['email'] = user.email
2021 q_message['order_invoice_type'] = order_invoice_type
2022 q_message['freight_bill_numbers'] = freight_bill_nos
2023
2024 q_factory.publish_message('invoice', q_message, 3)
2025
2026 except Exception as e:
2027 error_msg_list = ["Error in %s QueueFactory call" % request.path,
2028 traceback.format_exc()]
2029 utils.write_error_logs(error_msg_list)
2030
2031 status_msg = "PDF will be emailed to '%s'" % user.email
2032 return status_msg, resp_status, invalid_list
2033
2034
2035def update_customer_invoice(data, user):
2036 '''
2037 This will add or update customer_invoice entry
2038 it store freight_bill_number and invoice path
2039 '''
2040
2041 resp_status, status_msg, result = 0, '', ''
2042 if not data:
2043 status_msg = "Please provide data"
2044 resp_status = 1
2045 return status_msg, resp_status, result
2046
2047 if not isinstance(user, User):
2048 try:
2049 user = User.objects.get(pk=user)
2050 except Exception as e:
2051 resp_status = 1
2052 status_msg = 'Please provide valid user'
2053 return status_msg, resp_status, result
2054
2055 data['last_modified_by'] = user
2056 data['last_modified_date'] = utils.current_time()
2057 if not data.get('invoice_date'):
2058 data['invoice_date'] = data['last_modified_date']
2059
2060 resp_status, status_msg, result = utils.create_model_entry(CustomerInvoice, data, update=True)
2061 if isinstance(result, CustomerInvoice):
2062 if result.invoice_status not in (3, 4, 5): # Verified, sent, verification pending
2063 result.invoice_status = 2
2064 result.inv_generated_by = user
2065 result.inv_generation_date = utils.current_time()
2066 result.invoice_version += 1
2067 result.save()
2068 else:
2069 return status_msg
2070
2071 return result
2072
2073
2074def create_folder_if_not_exists(src_dir, folder_name, create_src_dir=True):
2075 '''
2076 This create a folder under a given source location
2077 and return path of newly created create_folder_if_not_exists
2078 if srource path does not exista then it returns ''
2079 '''
2080 resp_status, status_msg, result = 0, '', ''
2081
2082 if create_src_dir:
2083 try:
2084 os.makedirs(src_dir)
2085 except OSError as exc: # Python >2.5
2086 if exc.errno == errno.EEXIST and os.path.isdir(src_dir):
2087 pass
2088 else:
2089 resp_status = 1
2090 status_msg = utils.get_error_msg(exc)
2091 return status_msg, resp_status, result
2092
2093 if os.path.exists(src_dir) and os.path.isdir(src_dir):
2094 result = os.path.join(src_dir, folder_name)
2095 if folder_name not in os.listdir(src_dir):
2096 os.makedirs(result)
2097
2098 else:
2099 resp_status = 1
2100 status_msg = 'Source dir %s not present' % src_dir
2101 result = ''
2102 return status_msg, resp_status, result
2103
2104
2105'''
2106Special Handling for Dhunseri Request
2107TODO - Seperation of business rules
2108'''
2109
2110
2111def generate_excel_for_invoice(invoice_dict, folder_path, filename=''):
2112 path = os.path.join(folder_path, filename)
2113 workbook = xlsxwriter.Workbook(path)
2114 worksheet = workbook.add_worksheet("Bill Header")
2115 # Set up layout of the worksheet.
2116 worksheet.set_column('A:A', 50)
2117 worksheet.set_column('B:D', 15)
2118 worksheet.set_column('E:E', 10)
2119 worksheet.set_column('F:F', 15)
2120 worksheet.set_column('G:G', 30)
2121 worksheet.set_column('H:H', 15)
2122 merge_format = workbook.add_format({
2123 'align': 'center',
2124 'fg_color': '#FFFF00',
2125 'border': 1
2126 })
2127 worksheet.merge_range('A1:H1', 'Invoice', merge_format)
2128 header_format = workbook.add_format({
2129 'align': 'left',
2130 'fg_color': '#C4BD97',
2131 'border': 1
2132 })
2133 total_format = workbook.add_format({
2134 'align': 'center',
2135 'bold': True
2136 })
2137 row_header = [
2138 "Transporter's\nName", "Transporter's\nNo", "Transporter's\nInvoice Number",
2139 "Transporter's\nSite", "Location", "Transporter's\nInvoice Date\n(DD-MON-YYYY)", "Descriptions",
2140 "Delivery Station"
2141 ]
2142 worksheet.set_row(1, 40)
2143 col = 0
2144 row = 1
2145 worksheet.write_row(row, col, tuple(row_header), header_format)
2146 row += 1
2147 col = 0
2148 number_format = workbook.add_format({
2149 'align': 'center',
2150 'fg_color': '#9BBB59',
2151 'border': 1
2152 })
2153 worksheet.write_row(row, col, tuple(range(1, len(row_header) + 1)), number_format)
2154 '''
2155 for value in range(1,len(row_header)+1):
2156 worksheet.write(row,col,value,number_format)
2157 col+=1
2158 '''
2159 row += 1
2160 col = 0
2161 # Lookups table
2162 lookup_table_format = workbook.add_format({
2163 'border': 1
2164 })
2165 worksheet.write(201, 0, 'Plant - I', lookup_table_format)
2166 worksheet.write(201, 1, 102, lookup_table_format)
2167 worksheet.write(202, 0, 'Plant - II', lookup_table_format)
2168 worksheet.write(202, 1, 106, lookup_table_format)
2169 for i in range(203, 207):
2170 worksheet.write(i, 0, '', lookup_table_format)
2171 worksheet.write(i, 1, '', lookup_table_format)
2172 worksheet.write(210, 0, 'Zinka Logistics Solutions Private Limited', lookup_table_format)
2173 worksheet.write(210, 1, 'DZ0009', lookup_table_format)
2174 for i in range(211, 250):
2175 worksheet.write(i, 0, '', lookup_table_format)
2176 worksheet.write(i, 1, '', lookup_table_format)
2177 worksheet.write(210, 3, 'BENGALURU', lookup_table_format)
2178 for i in range(211, 215):
2179 worksheet.write(i, 3, '', lookup_table_format)
2180
2181 # Write Data
2182 worksheet.data_validation(row, col, row, col,
2183 { 'validate': 'list', 'source': ['Zinka Logistics Solutions Private Limited'] })
2184 worksheet.write(row, col, "Zinka Logistics Solutions Private Limited")
2185 col += 1
2186 worksheet.write_formula(row, col, '=IF(A4="","",VLOOKUP(A4,A210:B276,2,FALSE))')
2187 # worksheet.write(row, col, '=IF(A4="","",VLOOKUP(A4,A210:B276,2,FALSE))')
2188 col += 1
2189 worksheet.write(row, col, invoice_dict['freight_bill_num'])
2190 col += 1
2191 worksheet.data_validation(row, col, row, col, { 'validate': 'list', 'source': ['BENGALURU'] })
2192 worksheet.write(row, col, 'BENGALURU')
2193 col += 1
2194 worksheet.data_validation(row, col, row, col, { 'validate': 'list', 'source': ['Plant - I', 'Plant - II'] })
2195 if 'Plant I' in invoice_dict['from_warehouse']:
2196 worksheet.write(row, col, 'Plant - I')
2197 elif 'Plant II' in invoice_dict['from_warehouse']:
2198 worksheet.write(row, col, 'Plant - II')
2199 col += 1
2200 format = workbook.add_format({ 'num_format': 'd-mmm-yy' })
2201 worksheet.write(row, col, invoice_dict['constant_data']['today_date'].strftime('%d-%b-%y'))
2202 col += 1
2203 worksheet.write(row, col, 'Pet Resin')
2204 col += 1
2205 worksheet.write(row, col, invoice_dict['to_city'])
2206
2207 worksheet = workbook.add_worksheet("Bill Lines")
2208 charges_format = workbook.add_format({
2209 'align': 'center',
2210 'fg_color': '#8DB3E2',
2211 'border': 1
2212 })
2213 # Set up layout of the worksheet.
2214 worksheet.set_column('B:B', 15)
2215 worksheet.merge_range('A1:L1', '')
2216 worksheet.merge_range('M1:Q1', 'Charges', charges_format)
2217 worksheet.merge_range('R1:V1', 'Adjustment', number_format)
2218 row_header = [
2219 "SL No.", "Factory\nInvoice No.", "Grade", "CN\nNo.", "CN Date\n(DD-MMM-YYYY)", "Pack\nSize", "Truck\nNo",
2220 "Arrival Date\n(DD-MMM-YYYY)", "Actual\nWeight", "Charged\nWeight",
2221 "Rate", "Freight", "Two Point Delivery\ncharge", "Multi\npoint Delivery\ncharge", "Detention\nCharge",
2222 "Hndlg-Trnshpnt-\ndlvry charges of\nFreight charges", "Local charges of\nFreight charges",
2223 "Late Charge", "TNR", "Corrugated\nSheet", "Risk Purchase", "Short\nDelivery"
2224 ]
2225 worksheet.set_row(1, 40)
2226 col = 0
2227 row = 1
2228 worksheet.write_row(row, col, row_header, header_format)
2229 row += 1
2230 col = 0
2231 number_format = workbook.add_format({
2232 'align': 'center',
2233 'fg_color': '#9BBB59',
2234 'border': 1
2235 })
2236 date_format = workbook.add_format({ 'num_format': 'd-mmm-yy' })
2237 worksheet.write_row(row, col, tuple(range(1, len(row_header) + 1)), number_format)
2238 row += 1
2239 col = 0
2240 count = 1
2241 worksheet.write(row, 0, count)
2242 worksheet.write(row, 1, invoice_dict['invoice_number'])
2243 worksheet.data_validation(row, 2, row, 2, { 'validate': 'list',
2244 'source': ['18C', '19C', '20C', '21C', '22C', '15C', '16C', '17C',
2245 'OTHER'] })
2246 worksheet.write(row, 2, invoice_dict['desc_of_goods'])
2247 worksheet.write(row, 3, invoice_dict['order'].id)
2248 worksheet.write(row, 4, invoice_dict['cn_date'].strftime('%d-%b-%y'))
2249 worksheet.write(row, 5, 115)
2250 worksheet.write(row, 6, invoice_dict['truck_number'])
2251 if invoice_dict['date_of_arrival']:
2252 invoice_dict['date_of_arrival'] = invoice_dict['date_of_arrival'].strftime('%d-%b-%y')
2253 worksheet.write(row, 7, invoice_dict['date_of_arrival'])
2254 worksheet.write(row, 8, invoice_dict['qty_moved_tons'])
2255 worksheet.write(row, 9, invoice_dict['charged_weight_per_inv'])
2256 worksheet.write(row, 10, float(invoice_dict['freight_amount']) / invoice_dict['charged_weight_per_inv'])
2257 worksheet.write_formula('L4', '=J4*K4')
2258 if invoice_dict['order'].num_loading_points == 2:
2259 worksheet.write(row, 12, invoice_dict['multiple_loading_pt_charge'])
2260 else:
2261 worksheet.write(row, 12, '')
2262 if invoice_dict['order'].num_loading_points > 3:
2263 worksheet.write(row, 13, invoice_dict['multiple_loading_pt_charge'])
2264 else:
2265 worksheet.write(row, 13, '')
2266 worksheet.write(row, 14, invoice_dict['loading_detention'] + invoice_dict['unloading_detention'])
2267 row += 1
2268 worksheet.write_column(row, 0, tuple(range(2, 51)))
2269 worksheet.write_column(row, 11, tuple([0] * 49))
2270 worksheet.merge_range('A54:K54', 'TOTAL', total_format)
2271 worksheet.write_formula('L54', '=SUM(L4:L53)')
2272 worksheet.write_formula('M54', '=SUM(M4:M53)')
2273 worksheet.write_formula('N54', '=SUM(N4:N53)')
2274 worksheet.write_formula('O54', '=SUM(O4:O53)')
2275 worksheet.write_formula('P54', '=SUM(P4:P53)')
2276 worksheet.write_formula('Q54', '=SUM(Q4:Q53)')
2277 worksheet.write_formula('R54', '=SUM(R4:R53)')
2278 worksheet.write_formula('S54', '=SUM(S4:S53)')
2279 worksheet.write_formula('T54', '=SUM(T4:T53)')
2280 worksheet.write_formula('U54', '=SUM(U4:U53)')
2281 worksheet.write_formula('V54', '=SUM(V4:V53)')
2282
2283 workbook.close()
2284 return '', 0, path
2285
2286
2287def generate_invoice_report(invoice_dict, folder_path, filename='', template=[], format='.pdf'):
2288 if format == '.pdf':
2289 return generate_pdf_for_invoice(invoice_dict, folder_path, filename + format, template)
2290 else:
2291 return generate_excel_for_invoice(invoice_dict, folder_path, filename + format)
2292
2293
2294def generate_pdf_for_invoice(invoice_dict, folder_path, filename='', template=[]):
2295 '''
2296 generate pdf from template
2297 '''
2298 resp_status, status_msg, generated_path_list, index = 0, '', [], 0
2299 # get all template path
2300 inv_template_path, load_unload_template_path, detention_template_path, checkpost_template_path, summary_template_path, plywood_template_path = template
2301 path = os.path.join(folder_path, filename + "_%s" % index)
2302 try:
2303 rendered = render_to_string(inv_template_path, { "invoice_dict": invoice_dict })
2304 pdfkit.from_string(rendered, path)
2305 index += 1
2306 generated_path_list.append(path)
2307
2308 if load_unload_template_path and (
2309 invoice_dict.get('total_load_charges', 0) + invoice_dict.get('total_unload_charges')):
2310 path = os.path.join(folder_path, filename + "_%s" % index)
2311 rendered = render_to_string(load_unload_template_path, { "invoice_dict": invoice_dict })
2312 pdfkit.from_string(rendered, path)
2313 index += 1
2314 generated_path_list.append(path)
2315
2316 if detention_template_path and (
2317 invoice_dict.get('total_load_detention_charges', 0) + invoice_dict.get(
2318 'total_unload_detention_charges')):
2319 path = os.path.join(folder_path, filename + "_%s" % index)
2320 rendered = render_to_string(detention_template_path, { "invoice_dict": invoice_dict })
2321 pdfkit.from_string(rendered, path)
2322 index += 1
2323 generated_path_list.append(path)
2324
2325 if checkpost_template_path and invoice_dict.get('total_checkpost_charges', 0):
2326 path = os.path.join(folder_path, filename + "_%s" % index)
2327 rendered = render_to_string(checkpost_template_path, { "invoice_dict": invoice_dict })
2328 pdfkit.from_string(rendered, path)
2329 index += 1
2330 generated_path_list.append(path)
2331
2332 if summary_template_path:
2333 path = os.path.join(folder_path, filename + "_%s" % index)
2334 rendered = render_to_string(summary_template_path, { "invoice_dict": invoice_dict })
2335 pdfkit.from_string(rendered, path)
2336 index += 1
2337 generated_path_list.append(path)
2338
2339 if plywood_template_path:
2340 path = os.path.join(folder_path, filename + "_%s" % index)
2341 rendered = render_to_string(plywood_template_path, { "invoice_dict": invoice_dict })
2342 pdfkit.from_string(rendered, path)
2343 index += 1
2344 generated_path_list.append(path)
2345
2346 path = os.path.join(folder_path, filename)
2347 merge_multiple_pdf(generated_path_list, ouput_file_path=path)
2348 except Exception as e:
2349 print(e)
2350 error_msg_list = ["Error in generate_pdf_for_invoice",
2351 traceback.format_exc()]
2352 utils.write_error_logs(error_msg_list)
2353 resp_status = 1
2354 status_msg = "Error while generating PDF, Please check template and invoice_type configuration are compatible"
2355
2356 # delete files in temp_invoice directory
2357 for temp_path_inv in generated_path_list:
2358 os.remove(temp_path_inv)
2359
2360 return status_msg, resp_status, path
2361
2362
2363def get_or_generate_freight_bill_number(order, inv_info, invoice_number=None):
2364
2365 '''
2366 This function get existing freight_bill_number or generate if it does not already exists
2367 '''
2368
2369 resp_status, status_msg, freight_bill_number= 0, '', ''
2370 try:
2371 '''
2372 Check if freight_bill_number is already generated for the order
2373 If yes then use exiting bill number else create a new bill number
2374 '''
2375 customer, loc = order.user, order.from_city
2376 order_freight_bill_no_obj = None
2377
2378 order_freight_bill_objs = order.freight_bill_number.all()
2379
2380 '''
2381 One order multiple invoices then in case of existing freight_bill
2382 number take one which corresponds to invoice_number
2383 '''
2384 customer_id = order.user.id
2385 global_inv_info = InvoiceInfo.objects.filter(customer_id=customer_id, from_entity_type=3, to_entity_type=3)
2386 if len(global_inv_info) > 1:
2387 resp_status = 1
2388 status_msg = "Multiple ALL-ALL configurations found."
2389 return status_msg,resp_status,freight_bill_number
2390 order_invoice_type = inv_info.order_invoice_type
2391
2392 for obj in order_freight_bill_objs:
2393 if order_invoice_type == 2 and invoice_number:
2394 if obj.invoice_number == invoice_number:
2395 order_freight_bill_no_obj = obj
2396 break
2397 elif not obj.invoice_number:
2398 obj.invoice_number = invoice_number
2399 obj.save()
2400 order_freight_bill_no_obj = obj
2401
2402 else:
2403 order_freight_bill_no_obj = obj
2404 break
2405
2406 '''
2407 If freight_bill_number for given order, invoice_number not found then generate
2408 '''
2409 if order_freight_bill_no_obj:
2410 freight_bill_number = order_freight_bill_no_obj.freight_bill_number
2411 else:
2412 freight_bill_number = generate_freight_bill_no(order, global_inv_info)
2413
2414 except Exception as e:
2415 resp_status = 1
2416 status_msg = utils.get_error_msg(e)
2417 error_msg_list = ["Error in get_or_generate_freight_bill_number",
2418 traceback.format_exc()]
2419 utils.write_error_logs(error_msg_list)
2420
2421 return status_msg,resp_status,freight_bill_number
2422
2423
2424def generate_freight_bill_no(order, customer_invoice_info_obj):
2425 '''
2426 This function generate freight bill number used in invoice
2427 It is Z/COI/000344/000035 [z/<location_code>/cust_id/serial_number]
2428 Serial number depends on customer and from_location
2429 '''
2430 with transaction.atomic():
2431 d = datetime.now()
2432 bill_no = AutoIncrementValue.objects.select_for_update().get(name='bill_seq_number')
2433 freight_number = bill_no.value
2434 # bill_no += separator + order.from_city.code
2435 # bill_no += separator + str(order.from_city.id).zfill(5)
2436 # seq_no = (str(customer_invoice_info_obj.bill_seq_number).zfill(6))
2437 # bill_no += separator + seq_no
2438 bill_no.value = bill_no.value + 1
2439 bill_no.save()
2440 freight_bill_number = 'Z' + str(d.strftime('%Y'))
2441 freight_bill_number += str(freight_number).zfill(7)
2442 return freight_bill_number
2443
2444
2445@role_function_check("GET_INVOICE")
2446def get_customer_freight_billno(request, obj_id, fields, filters, external, nested):
2447 '''
2448 This API will get freight_bill_no based on customer and location
2449 '''
2450 status_msg, resp_status, result, extra_info = '', 0, [], { }
2451 _result = []
2452 request_body = utils.parse_request_body(request)
2453
2454 if not request_body:
2455 status_msg = 'Unable to Parse Request body'
2456 resp_status = 1
2457 return status_msg, resp_status, result
2458 page = request_body.get('page', { })
2459
2460 DEFAULT_RESULTS_PER_PAGE = 50
2461 page_str = ''
2462
2463 # If page is being passed this return limited result
2464 if page:
2465 if page.has_key('opr'):
2466 page_str = " limit " + `int(page['num']) * page['opr'] ` + ',' + `page['opr']`
2467 else:
2468 page_str = " limit " + `int(page['num']) * DEFAULT_RESULTS_PER_PAGE` + ',' + `DEFAULT_RESULTS_PER_PAGE`
2469 '''
2470 if not request_body.get('user_id',0):
2471 status_msg = 'Please provide customer id'
2472 resp_status = 1
2473 return status_msg, resp_status, result
2474 '''
2475
2476 left_join, inner_join, where_fields = OrderedDict(), OrderedDict(), []
2477 select_fields = ['O.id as order_id', 'CI.freight_bill_number', 'CI.manual_bill_number',
2478 'FL.city as from_location', 'CI.invoice_url', 'OFB.customerinvoice_id',
2479 'UNIX_TIMESTAMP(O.end_date)*1000 as end_date',
2480 'TL.city as to_location', 'FW.name as from_warehouse',
2481 'TW.name as to_warehouse', 'O.status', 'CI.invoice_status',
2482 'CUP.name as customer_name', 'IRR.reason as invoice_rejection_reason',
2483 'UNIX_TIMESTAMP(CI.inv_generation_date)*1000 as inv_generation_date',
2484 ]
2485
2486 inner_join['base_order_freight_bill_number OFB'] = 'OFB.order_id = O.id'
2487 inner_join['base_customerinvoice CI'] = 'CI.id = OFB.customerinvoice_id'
2488 inner_join['base_customeruserprofile CUP'] = 'CUP.user_id = O.user_id'
2489 inner_join['base_location FL'] = 'FL.id = O.from_city_id'
2490 inner_join['base_location TL'] = 'TL.id = O.to_city_id'
2491 inner_join['base_warehouse FW'] = 'FW.id = O.start_location_id'
2492 inner_join['base_warehouse TW'] = 'TW.id = O.end_location_id'
2493 left_join['base_invoicerejectionreason IRR'] = 'IRR.id=CI.latest_rejection_reason_id'
2494
2495 order_by = ['OFB.customerinvoice_id desc']
2496 where_fields.append('CI.is_deleted = false')
2497 for key, val in request_body.items():
2498 if key in ('session_key', 'page'):
2499 continue
2500 if key == 'order_id':
2501 '''
2502 Get filter is based on order_id then get all the order which belongs to this order invoice
2503 '''
2504 where_fields.append('CI.id in (SELECT customerinvoice_id FROM ' \
2505 'base_order_freight_bill_number WHERE order_id IN (%s))' % val)
2506 continue
2507 if key == 'invoice_status':
2508 if len(val) == 1:
2509 filter_str = "(%s)" % val[0]
2510 else:
2511 ids_tuple = tuple(val)
2512 filter_str = str(ids_tuple)
2513 where_fields.append("CI.%s in %s" % (key, filter_str))
2514 continue
2515 elif key == 'freight_bill_number':
2516 if isinstance(val, list):
2517 filter_str = "CI.freight_bill_number in (%s)" % ','.join("'" + item + "'" for item in val)
2518 else:
2519 filter_str = "CI.freight_bill_number = '%s'" % val
2520 where_fields.append(filter_str)
2521 continue
2522
2523 where_fields.append("O.%s = '%s'" % (key, val))
2524
2525 select_fields_str = ", ".join(select_fields)
2526 where_fields_str = " and ".join(field for field in where_fields)
2527
2528 inner_join_str = " ".join(["Inner Join %s on %s" % (key, value) for key, value in inner_join.iteritems()])
2529 left_join_str = " ".join(["Left Join %s on %s" % (key, value) for key, value in left_join.iteritems()])
2530 order_by_str = " ORDER BY " + " ".join(order_by)
2531
2532 base_query = 'SELECT %s from base_order O '
2533 if inner_join_str.strip():
2534 base_query += " " + inner_join_str.strip()
2535 if left_join_str.strip():
2536 base_query += " " + left_join_str.strip()
2537
2538 base_query = base_query + ' where %s ' % where_fields_str
2539 count_query = base_query
2540 base_query += order_by_str
2541 if page_str:
2542 base_query += page_str
2543 try:
2544 # print(base_query)
2545 cursor = connection.cursor()
2546 cursor.execute(base_query % select_fields_str)
2547 result.extend(utils.dictfetchall(cursor))
2548 count_query = count_query % "count(*) as count,CI.invoice_status "
2549 count_query += " Group By CI.invoice_status "
2550 cursor.execute(count_query)
2551 extra_info['invoice_count'] = utils.dictfetchall(cursor)
2552
2553 finally:
2554 cursor.close()
2555
2556 try:
2557 # Freight bill number related keys move out
2558 freight_key = ['freight_bill_number', 'invoice_url', 'manual_bill_number',
2559 'invoice_rejection_reason', 'inv_generation_date',
2560 'customerinvoice_id', 'invoice_status']
2561
2562 # Group orders by freight_bill_number
2563 for key, group in itertools.groupby(result, lambda item: item["customerinvoice_id"]):
2564 temp_dict, temp_list = { }, []
2565
2566 # moved order related data out of document detail
2567 for item in group:
2568 for k in item:
2569 if k in freight_key:
2570 temp_dict[k] = item[k]
2571
2572 # remove order_related data from document_details
2573 for k in item.keys():
2574 if k in freight_key:
2575 item.pop(k, None)
2576
2577 temp_list.append(item)
2578 temp_dict['order_details'] = temp_list
2579
2580 _result.append(temp_dict)
2581 if not _result:
2582 status_msg = 'No record found'
2583 except Exception as e:
2584 error_msg_list = ["Error at get_customer_freight_billno ",
2585 traceback.format_exc()]
2586 utils.write_error_logs(error_msg_list)
2587 status_msg = utils.get_error_msg(e)
2588 resp_status = 1
2589
2590 return status_msg, resp_status, _result, extra_info
2591
2592
2593@role_function_check("DELETE_INVOICE")
2594def detach_order_from_freight_bill_number(request, obj_id, filters, fields, external, nested=False):
2595 '''
2596 This API will remove one order from freight_bill_number and move the
2597 order to "invoice Pending" status.
2598 It only one order under freight_bill_number then remove the
2599 '''
2600 status_msg, resp_status, result = '', 0, []
2601 request_body = utils.parse_request_body(request)
2602 freight_bill_number = []
2603 user = request.user
2604 if not request_body:
2605 status_msg = 'Unable to Parse Request body'
2606 resp_status = 1
2607 return status_msg, resp_status, result
2608
2609 if not request_body.get('order_id', 0):
2610 status_msg = 'Please provide order id'
2611 resp_status = 1
2612 return status_msg, resp_status, result
2613
2614 try:
2615 order = Order.objects.get(pk=request_body.get('order_id'))
2616
2617 except Order.DoesNotExist:
2618 resp_status = 1
2619 status_msg = "Order with Id %s not found" % (request_body.get('order_id'))
2620 return status_msg, resp_status, result
2621
2622 # check if order exists in envelope
2623 status_msg, resp_status, result = check_invoice_existing_envelope(order_list=[request_body.get('order_id')])
2624 if resp_status:
2625 return status_msg, resp_status, result
2626
2627 try:
2628 freight_bill_number_objs = order.freight_bill_number.all()
2629 except Exception as e:
2630 resp_status = 1
2631 status_msg = "No freight_bill_number assigned to order"
2632 return status_msg, resp_status, result
2633
2634 try:
2635 no_of_freight_bill_obj = len(freight_bill_number_objs)
2636 if freight_bill_number_objs and no_of_freight_bill_obj > 0:
2637 for freight_obj in freight_bill_number_objs:
2638 deleted_obj = { 'order': order, 'customerinvoice': freight_obj.id, 'deleted_by': user }
2639 resp_status, status_msg, deleted_obj = utils.create_model_entry(DeletedOrderFreightBillNumber,
2640 deleted_obj)
2641 if resp_status:
2642 return status_msg, resp_status, result
2643 freight_bill_number.append(freight_obj.freight_bill_number)
2644 order.freight_bill_number.remove(freight_obj)
2645 '''
2646 if only one order was associated with freight_billnumber then
2647 mark freight object deleted
2648 '''
2649 if len(freight_obj.order_set.all()) == 0:
2650 freight_obj.is_deleted = True
2651 freight_obj.deleted_by = request.user
2652 freight_obj.save()
2653
2654 # move the status to Invoice Pending
2655 new_status = { 'status': 'Invoice Pending' }
2656 status_msg, resp_status, result_status = update_order_invoice_status(order, new_status, user)
2657 if not resp_status:
2658 status_msg = "Order %s Successfully removed from %s" % (
2659 order.id, ",".join(item for item in freight_bill_number))
2660
2661 except Exception as e:
2662 error_msg_list = ["Error at detach_order_from_freight_bill_number ",
2663 traceback.format_exc()]
2664 utils.write_error_logs(error_msg_list)
2665 status_msg = utils.get_error_msg(e)
2666 resp_status = 1
2667
2668 return status_msg, resp_status, result
2669
2670
2671def merge_multiple_excel(file_list, output_file_path=''):
2672 current_time_str = utils.timezone_now().strftime("%Y_%m_%d_%H_%M_%S")
2673 if not output_file_path:
2674 output_file_path = '/var/log/blackbuck/temp_receivable/' + "CombinedInvoices_" + current_time_str + ".zip"
2675 zf = zipfile.ZipFile(output_file_path, "w")
2676 for filename in file_list:
2677 zf.write(filename, os.path.basename(filename))
2678 zf.close()
2679 # delete files
2680 for filename in file_list:
2681 os.remove(filename)
2682 if len(file_list) > 0:
2683 return output_file_path
2684 else:
2685 return None
2686
2687
2688def merge_multiple_pdf(file_list, ouput_file_path=''):
2689 '''
2690 Given the absolute path of pdf, It will create new pdf combining all of them
2691 '''
2692 merger = PdfFileWriter()
2693
2694 for filename in file_list:
2695 append_pdf(PdfFileReader(file(filename, "rb")), merger)
2696
2697 current_time_str = utils.timezone_now().strftime("%Y_%m_%d_%H_%M_%S")
2698 if not ouput_file_path:
2699 ouput_file_path = os.path.join(os.path.dirname(filename), "CombinedInvoices_" + current_time_str + ".pdf")
2700
2701 # Writing all the collected pages to a file
2702 merger.write(file(ouput_file_path, "wb"))
2703 return ouput_file_path
2704
2705
2706# Creating a routine that appends files to the output file
2707def append_pdf(input, output):
2708 [output.addPage(input.getPage(page_num)) for page_num in range(input.numPages)]
2709
2710
2711def check_insert_invoice_lock(order_ids, user):
2712 '''
2713 This function checks if there is another ongoing invoice generation
2714 for the given order.
2715 If lock is not there then it acquire lock on given order_ids
2716 '''
2717 status_msg, resp_status, result = '', 0, []
2718 resp = 'Not Locked'
2719
2720 try:
2721 inv_lock_obj = InvoiceGenerationLock.objects.filter(order_id__in=order_ids)
2722 if len(inv_lock_obj) > 0:
2723 resp_status = 1
2724 status_msg = 'Already Locked %s Orders for invoice generation' % (
2725 ', '.join(str(item.order.id) for item in inv_lock_obj))
2726 return status_msg, resp_status, result
2727 else:
2728
2729 current_utc_time = utils.current_time()
2730 lock_obj_list = []
2731 order_ids = list(set(order_ids))
2732 for order in order_ids:
2733 dict_lock = { 'order_id': order, 'user': user, 'dt_added': current_utc_time }
2734 lock_obj_list.append(dict_lock)
2735 lock_list = [InvoiceGenerationLock(**vals) for vals in lock_obj_list]
2736 InvoiceGenerationLock.objects.bulk_create(lock_list)
2737
2738 except Exception as e:
2739 error_msg_list = ["Error at check_insert_invoice_lock ",
2740 traceback.format_exc()]
2741 utils.write_error_logs(error_msg_list)
2742 status_msg = utils.get_error_msg(e)
2743 resp_status = 1
2744
2745 return status_msg, resp_status, result
2746
2747
2748def delete_invoice_lock(order_ids):
2749 '''
2750 This function deletes entry from the InvoiceGenerationLock table
2751 '''
2752 status_msg, resp_status, result = '', 0, { }
2753 try:
2754 inv_lock_obj = InvoiceGenerationLock.objects.filter(order_id__in=order_ids)
2755 if len(inv_lock_obj) > 0:
2756 inv_lock_obj.delete()
2757 status_msg = 'Lock removed from orders %s invoice generation' % (
2758 ', '.join(item.order.id for item in inv_lock_obj))
2759 return status_msg, resp_status, result
2760 else:
2761 status_msg = 'Lock is not acquired on given objects'
2762
2763 except Exception as e:
2764 error_msg_list = ["Error in delete_invoice_lock lock",
2765 traceback.format_exc()]
2766 utils.write_error_logs(error_msg_list)
2767 resp_status = 1
2768 status_msg = utils.get_error_msg(e)
2769 return status_msg, resp_status, result
2770
2771
2772def get_order_id_from_freight_billno(bill_nos):
2773 select_fields = ['group_concat(distinct OFB.order_id) as order_id']
2774
2775 where_fields, inner_join = [], OrderedDict()
2776
2777 where_fields.append("CI.freight_bill_number in (%s) " % (','.join("'" + no + "'" for no in bill_nos)))
2778 inner_join['base_customerinvoice CI'] = 'CI.id = OFB.customerinvoice_id'
2779
2780 inner_join_str = " ".join(["Inner Join %s on %s" % (key, value) for key, value in inner_join.iteritems()])
2781
2782 select_fields_str = ", ".join(select_fields)
2783 where_fields_str = " and ".join(field for field in where_fields)
2784
2785 base_query = 'SELECT %s from base_order_freight_bill_number OFB ' % select_fields_str
2786 if inner_join_str.strip():
2787 base_query += " " + inner_join_str.strip()
2788
2789 base_query += ' where %s Group By OFB.customerinvoice_id ORDER BY OFB.customerinvoice_id ' % where_fields_str
2790
2791 try:
2792 cursor = connection.cursor()
2793 cursor.execute(base_query)
2794 _result = utils.dictfetchall(cursor)
2795 result = [item['order_id'] for item in _result]
2796
2797 _result = []
2798 for item in result:
2799 _result.append(str(item).split(','))
2800 finally:
2801 cursor.close()
2802
2803 return _result
2804
2805
2806@role_function_check('VERIFY_INVOICE')
2807def verify_invoices(request, obj_id, filters, fields, external, nested):
2808 '''
2809 Api only for verification i.e either verified or rejected
2810 '''
2811 status_msg, resp_status, result = '', 0, []
2812 user = request.user
2813 request_body = utils.parse_request_body(request)
2814
2815 if not request_body:
2816 status_msg = 'Failed to parse request body'
2817 resp_status = 1
2818 return status_msg, resp_status, result
2819
2820 status = request_body.get('status', '')
2821 '''
2822 Check if status is Verified or invoice Rejected
2823 '''
2824 if not status or status not in [1, 4]:
2825 resp_status = 1
2826 status_msg = 'Please provide valid status'
2827 return status_msg, resp_status, result
2828 if status == 1 and not request_body.get('invoice_rejection_reason', ''):
2829 resp_status = 1
2830 status_msg = 'Please provide reason to reject the invoice'
2831 return status_msg, resp_status, result
2832 status_msg, resp_status, result = update_multiple_invoice_status(request_body, user)
2833
2834 return status_msg, resp_status, result
2835
2836
2837@role_function_check('UPDATE_INVOICE_STATUS')
2838def update_invoice_status(request, obj_id, filters, fields, external, nested):
2839 '''
2840 Api for update invocie status except verifiation and rejection
2841 '''
2842 status_msg, resp_status, result = '', 0, []
2843 user = request.user
2844 request_body = utils.parse_request_body(request)
2845
2846 if not request_body:
2847 status_msg = 'Failed to parse request body'
2848 resp_status = 1
2849 return status_msg, resp_status, result
2850
2851 status = request_body.get('status', '')
2852 '''
2853 Check if status are invoice verifiation
2854 '''
2855 if not status or status in [1, 4]:
2856 resp_status = 1
2857 status_msg = 'Please provide valid status'
2858 return status_msg, resp_status, result
2859
2860 return update_multiple_invoice_status(request_body, user)
2861
2862
2863def update_multiple_invoice_status(request_body, user):
2864 '''
2865 This function update status of invoices as well order for given list of freight_bill_number or orders
2866 In case of one order multiple invoice if any of the invoice is rejected then order will move to invoice rejected
2867 If multiple order one invoice is rejeted then all the order will move to invoice rejected sttaus.
2868 It will only update order status if invoice status is updated successfully
2869 '''
2870 status_msg, resp_status, result, invalid_list = '', 0, [], []
2871 revert, is_valid, invalid_freight_bill_nos, valid_freight_bills = False, True, [], []
2872 is_mis_manager, is_finance = False, False
2873
2874 if not request_body or not user:
2875 status_msg = 'Please provice sufficient data'
2876 resp_status = 1
2877 return status_msg, resp_status, result
2878
2879 roles = user.userprofile.get_roles()
2880 '''
2881 For reject invoice both mis and finace ahave access depending on current status
2882 '''
2883 if 'Invoicing Manager' in roles:
2884 is_mis_manager = True
2885 if 'Finance Accounts' in roles or 'Finance Controller' in roles or 'Finance' in roles:
2886 is_finance = True
2887
2888 try:
2889 '''
2890 Get invoices given bill_nos or orders
2891 '''
2892 order_ids = request_body.get('order_ids', [])
2893 if not order_ids:
2894 freight_bill_nos = request_body.get('freight_bill_nos', [])
2895 if not freight_bill_nos:
2896 resp_status = 1
2897 status_msg = 'Please provide either order_ids or freight_bill_nos'
2898 return status_msg, resp_status, result
2899 cust_inv = CustomerInvoice.objects.filter(freight_bill_number__in=freight_bill_nos)
2900 else:
2901 cust_inv = CustomerInvoice.objects.filter(order__id__in=order_ids)
2902
2903 status = request_body.get('status', '')
2904 if not status:
2905 resp_status = 1
2906 status_msg = 'Please provide new status'
2907 return status_msg, resp_status, result
2908
2909 '''
2910 invoice status list contains the text of status in order corresponding to current invoice status index based
2911 '''
2912 invoice_status_list = utils.get_enum_choices(CustomerInvoice, 'invoice_status', return_list=True)
2913 status_text = invoice_status_list[status - 1]
2914 if status == 1: # invoice rejected status
2915 revert = True
2916
2917 if revert:
2918 for item in cust_inv:
2919 if item.invoice_status == 3 and not is_finance:
2920 resp_status = 1
2921 status_msg = "OOps... You don't have permission to reject Verification Pending Invoice"
2922 return status_msg, resp_status, result
2923 if item.invoice_status in [4, 5] and not is_mis_manager:
2924 resp_status = 1
2925 status_msg = "OOps... You don't have permission to reject Verified Invoice or Customer sent invoice"
2926 return status_msg, resp_status, result
2927
2928 '''
2929 validation of all the invoice based on status and
2930 attached invoice freight_bill_number to invalid_list
2931 '''
2932 for inv in cust_inv:
2933 is_valid = True
2934 if not revert:
2935
2936 if inv.invoice_status >= status:
2937 status_msg = inv.freight_bill_number + " Already passed through status %s " % status_text
2938 invalid_dict = { 'id': inv.freight_bill_number, 'reason': status_msg }
2939 invalid_list.append(invalid_dict)
2940 is_valid = False
2941 elif (status - inv.invoice_status) > 1:
2942 status_diff = invoice_status_list[inv.invoice_status:status - 1]
2943 diff_status_text = ", ".join(status_diff)
2944 status_msg = " Can't update the status %s as, Order must have " % status_text
2945 status_msg += "completed previous statuses %s" % diff_status_text
2946 invalid_dict = { 'id': inv.freight_bill_number, 'reason': status_msg }
2947 invalid_list.append(invalid_dict)
2948 is_valid = False
2949 elif inv.invoice_status not in [3, 4, 5]:
2950 status_msg = inv.freight_bill_number + " status should be in 'Invoice Verification Pending' or 'Invoice Verified' or 'Invoice Sent' for reverting"
2951 invalid_dict = { 'id': inv.freight_bill_number, 'reason': status_msg }
2952 invalid_list.append(invalid_dict)
2953 is_valid = False
2954 if is_valid:
2955 valid_freight_bills.append(inv.freight_bill_number)
2956 else:
2957 invalid_freight_bill_nos.append(inv.freight_bill_number)
2958
2959 '''
2960 add rejection reason in separate table and update latest_rejection_reason_id in customer_invoice
2961 '''
2962 cust_inv = cust_inv.exclude(freight_bill_number__in=invalid_freight_bill_nos)
2963 if cust_inv and revert:
2964 reason_mapping = { }
2965 rejection_reason = request_body.get('invoice_rejection_reason', '')
2966 for item in valid_freight_bills:
2967 inv_rejection = { 'freight_bill_number': item, 'reason': rejection_reason }
2968 rejection_obj = add_invoice_rejection_reason(inv_rejection, user)
2969 reason_mapping[item] = rejection_obj
2970 for item in cust_inv:
2971 item.latest_rejection_reason = reason_mapping[item.freight_bill_number]
2972 item.save()
2973
2974 cust_inv.update(invoice_status=status, last_modified_by=user, last_modified_date=utils.current_time())
2975
2976 '''
2977 get the order for valid freight_bill_number, to get valid order for which status need to be updated
2978 '''
2979 orders = Order.objects.filter(freight_bill_number__freight_bill_number__in=valid_freight_bills)
2980 order_ids = []
2981
2982 '''
2983 In case of one order multiple invoice status, order will only move forward if all the invoices
2984 for that order are moving forwads
2985 excluding those order for which all invoice are not moved forward
2986 '''
2987 if not revert:
2988 cust_inv_objs = CustomerInvoice.objects.filter(order__in=orders)
2989 temp_freight_bill_nos = []
2990 for item in cust_inv_objs:
2991 if item.freight_bill_number not in valid_freight_bills and item.invoice_status != status:
2992 temp_freight_bill_nos.append(item.freight_bill_number)
2993
2994 orders = orders.exclude(freight_bill_number__freight_bill_number__in=temp_freight_bill_nos)
2995
2996 for order in orders:
2997 order_ids.append(order.id)
2998
2999 if order_ids:
3000 order_status_dict = { 'status': status_text, 'order_ids': order_ids }
3001 s_msg, r_staus, result = update_multiple_order_inv_status_func(order_status_dict, user)
3002 invalid_list.extend(result)
3003 except Exception as e:
3004 resp_status = 1
3005 status_msg = utils.get_error_msg(e)
3006 error_msg_list = ["Error in update_multiple_invoice_status",
3007 traceback.format_exc()]
3008 utils.write_error_logs(error_msg_list)
3009 if not resp_status:
3010 status_msg = ''
3011
3012 return status_msg, resp_status, invalid_list
3013
3014
3015def add_invoice_rejection_reason(data, user):
3016 result = []
3017 if not isinstance(user, User):
3018 user = utils.get_exact_match(User, user)
3019 data['rejected_by'] = user
3020 result = utils.create_model_entry(InvoiceRejectionReason, data)
3021
3022 return result[2]
3023
3024
3025@role_function_check("UPDATE_RECEIVABLE")
3026def delete_invoice_data(request, obj_id='', filters='', fields='', external='', nested=False):
3027 '''
3028 This API delete the entry from db from base_orderinvoicerelatedinfo for given id
3029 '''
3030 status_msg, resp_status, result = '', 0, []
3031 request_body = utils.parse_request_body(request)
3032 user = request.user
3033
3034 return delete_invoice_data_func(request_body, user)
3035
3036
3037def delete_invoice_data_func(request_body, user):
3038 '''
3039 Delete invoice data function
3040 '''
3041 status_msg, resp_status, result = '', 0, []
3042
3043 if request_body.get('id'):
3044 try:
3045 inv_info = OrderInvoiceRelatedInfo.objects.get(pk=request_body.get('id'), is_deleted=False)
3046 except OrderInvoiceRelatedInfo.DoesNotExist:
3047 inv_info = None
3048
3049 if not inv_info:
3050 resp_status = 1
3051 else:
3052 resp_status = 1
3053 if resp_status:
3054 status_msg = "Please Provice a valid id of invoice details need to be deleted"
3055 return status_msg, resp_status, result
3056 order = inv_info.order
3057 if order.order_invoice_status not in ('Invoice Ready', 'Invoice Pending', 'Invoice Rejected'):
3058 resp_status = 1
3059 status_msg = "Invoice Info can only be deleted if order status in Invoice Pending or Invoice Rejected or Invoice Ready"
3060 return status_msg, resp_status, result
3061
3062 count = order.order_invoice_info.filter(is_deleted=0).count()
3063
3064 if count == 1:
3065 new_status = { 'status': 'Invoice Ready' }
3066 update_order_invoice_status(order, new_status, user)
3067 inv_info.deleted_by = user
3068 inv_info_dict = utils.model_to_dict_with_refrences(inv_info)
3069 inv_info_dict.pop("id", None)
3070 inv_info_dict.pop("is_deleted", None)
3071 deleted_obj = utils.create_model_entry(DeletedOrderInvoiceRelatedInfo, inv_info_dict)
3072 inv_info.delete()
3073 result = { 'id': request_body.get('id') }
3074
3075 # Push deletion event to Invoicing Service
3076 from base.service.invoicing_service import InvoicingService
3077 try:
3078 InvoicingService().push_lr_deletion_event(request_body.get('id'), order)
3079 except Exception as e:
3080 utils.write_error_logs('Invoicing Service : Error pushing lr delete event for OrderInvoiceRelatedInfo.id : {}. Reason : {}'.format(request_body.get('id'), e.message))
3081
3082 return status_msg, resp_status, result
3083
3084
3085# ############################## new APIs as part of customer customization #######################################
3086
3087
3088# ### API to save fields for AdvDocVer state
3089
3090@role_function_check("UPDATE_ORDER") # might have to create new role function
3091def save_adv_doc_ver_fields(request, *args, **kwargs):
3092 """
3093 REQUEST:
3094 order_id, customer_id
3095 """
3096
3097 request_body = utils.parse_request_body(request)
3098 order_id = request_body.get('order_id')
3099 data_array = request_body.get('data_array')
3100 if not order_id:
3101 return 'order_id required.', 1, { }
3102
3103 # check order status
3104 try:
3105 order_obj = Order.objects.get(id=order_id)
3106 if not order_obj.status == "Advance DocVerification":
3107 return "Order status has moved. Please connect with your team for this order's status.", 1, []
3108 except:
3109 return "Order ID invalid", 1, []
3110
3111 if not data_array:
3112 return 'fields_list required.', 1, { }
3113
3114 for fields_list in data_array:
3115 oiri_fields_indx = order_fields_indx = []
3116 oiri_table_id, is_revenue_updated = '', False
3117 for i, f in enumerate(fields_list):
3118 if f['db_table_to_save'] == 'OrderInvoiceRelatedInfo' or f['bb_name'] in CUST_FIELDS:
3119 oiri_fields_indx.append(i)
3120 if f.get('table_id'):
3121 oiri_table_id = f['table_id']
3122 elif f['db_table_to_save'] == 'Order':
3123 order_fields_indx.append(i)
3124
3125 # save OrderInvoiceRelatedInfo fields
3126 if oiri_table_id:
3127 try:
3128 obj = OrderInvoiceRelatedInfo.objects.get(id=oiri_table_id)
3129 except:
3130 return 'OrderInvoiceRelatedInfo table_id invalid.', 1, []
3131 else:
3132 objs = OrderInvoiceRelatedInfo.objects.filter(order_id=order_id)
3133 if objs:
3134 return 'table_id not found in request.', 1, []
3135 else:
3136 obj = OrderInvoiceRelatedInfo()
3137 if oiri_fields_indx:
3138 for i in range(len(fields_list)):
3139 if i in oiri_fields_indx:
3140 setattr(obj, fields_list[i]['db_column_to_save'], fields_list[i]['value'])
3141 try:
3142 obj.order_id = order_id
3143 obj.last_modified_by = request.user
3144 obj.save()
3145 is_revenue_updated = True
3146 except:
3147 # traceback.print_exc()
3148 return "Invalid data input format", 1, []
3149
3150 # save order fields
3151 try:
3152 obj = Order.objects.get(id=order_id)
3153 except:
3154 return 'Order table_id invalid.', 1, []
3155
3156 if order_fields_indx:
3157 for i in range(len(fields_list)):
3158 if i in order_fields_indx:
3159 setattr(obj, fields_list[i]['db_column_to_save'], fields_list[i]['value'])
3160 try:
3161 obj.save()
3162 except:
3163 # traceback.print_exc()
3164 return "Invalid data input format", 1, []
3165
3166 q_message = { 'order_id': order_id }
3167 utils.push_to_message_queue(q_message, 'profitability', priority=3)
3168
3169 # Push Revenue audit trail to Ledger
3170 if is_revenue_updated:
3171 try:
3172 payload = { 'order_id': order_id, 'event_name': 'RevenueUpdate', 'audit_type': 'Revenue', 'event_time': utils.current_time().isoformat() }
3173 AuditTrailService().push_audit_trail_event(payload, AuditTrailConstants.events_amqp_exchange.value, AuditTrailConstants.events_amqp_routing_key.value)
3174 except Exception as e:
3175 utils.write_error_logs('LEDGER : ' + e.message)
3176
3177 return 'Data Saved successfully.', 0, []
3178
3179
3180def get_order_id_from_filters(obj, source_detention=False, destination_detention=False):
3181 '''
3182 This function get the order_ids based on given filters
3183 '''
3184
3185 resp_status, status_msg, result = 0, '', []
3186 #ignore_status = ('Cancelled', 'Cancelled By Customer', 'Order Incomplete', 'Order Processing')
3187 ignore_status = utils.pending_status + utils.cancelled_status + utils.incomplete_status
3188 left_join = OrderedDict()
3189 where_fields = []
3190
3191 select_fields = ['distinct O.id as order_id']
3192
3193 where_fields.append("O.user_id = '%s' " % obj['customer'])
3194 where_fields.append("O.status not in (%s) " % (', '.join('"' + item + '"' for item in ignore_status)))
3195
3196 # location filter based on object type (rate,load,unload object)
3197 if obj.get('from_city') and obj.get('to_city'):
3198 where_fields.append("(O.from_city_id = %s and O.to_city_id = %s) " % (obj['from_city'], obj['to_city']))
3199 elif obj.get('from_city'):
3200 where_fields.append("O.from_city_id = %s" % (obj['from_city']))
3201 elif obj.get('to_city'):
3202 where_fields.append("O.to_city_id = %s" % (obj['to_city']))
3203
3204 where_fields.append("((O.truck_type_id = %s and AT.truck_type_id is Null) or AT.truck_type_id = %s) " % (
3205 obj['truck_type'], obj['truck_type']))
3206 where_fields.append("O.end_date between '%s' and '%s' " % (obj['start_time'], obj['end_time']))
3207
3208 left_join['base_truck AT'] = 'AT.id = O.assigned_truck_id'
3209 # if source_detention or destination_detention:
3210 # left_join['base_orderfinancedetails F'] = 'F.order_id = O.id'
3211 # if destination_detention:
3212 # where_fields.append('F.dest_halt_duration is not null and F.dest_halt_duration > 0')
3213 # if source_detention:
3214 # where_fields.append('F.halt_duration is not NULL and F.halt_duration > 0')
3215 select_fields_str = ", ".join(select_fields)
3216 where_fields_str = " and ".join(field for field in where_fields)
3217 left_join_str = " ".join(["Left Join %s on %s" % (key, value) for key, value in left_join.iteritems()])
3218
3219 base_query = 'SELECT %s from base_order O ' % select_fields_str
3220 if left_join_str.strip():
3221 base_query += " " + left_join_str.strip()
3222
3223 base_query += ' where %s ORDER BY O.id ' % where_fields_str
3224
3225 try:
3226 cursor = connection.cursor()
3227 cursor.execute(base_query)
3228 _result = utils.dictfetchall(cursor)
3229 result = [item['order_id'] for item in _result]
3230 except Exception as e:
3231 error_msg_list = ["Error in get_order_id_from_filters in profitability_calculation consumer",
3232 traceback.format_exc()]
3233 utils.write_error_logs(error_msg_list)
3234
3235 finally:
3236 cursor.close()
3237
3238 if result:
3239 result = list(set(result))
3240 return result
3241
3242
3243def get_revenue_components(order, revenue_data=None):
3244 resp_status, status_msg, result = 0, '', { }
3245
3246 if not revenue_data:
3247 status, resp, revenue_data = get_customer_master_rate(order)
3248
3249 total = 0
3250 from cms.utils import *
3251 if customer_whitelisted_cms(order.end_date, customer=order.user) or not order.adhoc_rate_reference:
3252 try:
3253 result['freight_amount'] = revenue_data['cms_freight']['total']
3254 result['loading_charge'] = revenue_data['cms_loading']['components'][0]['loading_charge']
3255 result['multiple_loading_pt_charge'] = revenue_data['cms_loading']['components'][1][
3256 'multiple_loading_charges']
3257 result['unloading_charge'] = revenue_data['cms_unloading']['components'][0]['unloading_charge']
3258 result['multiple_unloading_pt_charge'] = revenue_data['cms_unloading']['components'][1][
3259 'multiple_unloading_charges']
3260 result['multiple_unloading_pt_charge'] = revenue_data['cms_unloading']['components'][1][
3261 'multiple_unloading_charges']
3262 result['loading_detention'] = revenue_data['cms_detention']['components'][0]['loading_detention']
3263 result['unloading_detention'] = revenue_data['cms_detention']['components'][1]['unloading_detention']
3264 result['checkpost_charges'] = revenue_data['cms_others']['components'][0]['checkpost_charges']
3265 result['plywood_charges'] = revenue_data['cms_others']['components'][1]['plywood_charges']
3266 result['green_tax'] = revenue_data['cms_others']['components'][2]['green_tax']
3267 except Exception as e:
3268 result = { }
3269 status_msg = "CMS output invalid"
3270 resp_status = 1
3271 else:
3272 try:
3273 result['freight_amount'] = revenue_data['rate']
3274 result['loading_charge'] = revenue_data['loading_charge']
3275 result['multiple_loading_pt_charge'] = revenue_data['multiple_loading_charges']
3276 result['unloading_charge'] = revenue_data['unloading_charge']
3277 result['multiple_unloading_pt_charge'] = revenue_data['multiple_unloading_charges']
3278 result['loading_detention'] = revenue_data['loading_detention']
3279 result['unloading_detention'] = revenue_data['unloading_detention']
3280 result['checkpost_charges'] = revenue_data['checkpost_charges']
3281 result['plywood_charges'] = revenue_data['plywood_charges']
3282 result['green_tax'] = revenue_data['green_tax']
3283 except Exception as e:
3284 result = { }
3285 status_msg = "Invalid output for getCustomerMasterRate"
3286 resp_status = 1
3287
3288 for revenue_comp, value in result.iteritems():
3289 total += float(value)
3290
3291 result['total'] = total
3292 return status_msg, resp_status, result
3293
3294
3295def get_invoice_conf_for_order(order):
3296 resp_status, status_msg, output = 0, '', { }
3297
3298 try:
3299 inv_infos = InvoiceInfo.objects.filter(customer_id=order.user.id).order_by('-priority')
3300 inv_info = None
3301 for item in inv_infos:
3302 if exact_match(item, order):
3303 inv_info = item
3304 break
3305 if not inv_info:
3306 status_msg = "No Invoice Configuration found for Order #" + str(order.id)
3307 resp_status = 1
3308 return status_msg, resp_status, output
3309
3310 return status_msg, resp_status, inv_info
3311 except Exception as e:
3312 utils.write_error_logs(e.message)
3313 return resp_status, status_msg, output
3314
3315
3316def update_invoice_finance_fields(order, revenue_data=None, is_cms_event=False):
3317 resp_status, status_msg, revenue_components = 0, '', { }
3318
3319 status_msg, resp_status, inv_info = get_invoice_conf_for_order(order)
3320 if resp_status:
3321 return status_msg, resp_status, revenue_data
3322
3323 status_msg, resp_status, revenue_components = get_revenue_components(order, revenue_data)
3324
3325 if resp_status or not revenue_components:
3326 return status_msg, resp_status, revenue_components
3327
3328 total_revenue = revenue_components.get('total', 0)
3329 freight_distributed_by = inv_info.freight_distributed_by
3330 order_invoice_type = inv_info.order_invoice_type
3331 order_invoice_infos = OrderInvoiceRelatedInfo.objects.filter(order=order)
3332 total = 0
3333 for lr in order_invoice_infos:
3334 if freight_distributed_by == 1:
3335 total += lr.weight
3336 elif freight_distributed_by == 2:
3337 total += (lr.num_pkgs or 0)
3338
3339 lrs = []
3340 result = { }
3341 try:
3342 for index, lr in enumerate(order_invoice_infos):
3343 if order_invoice_type == 2:
3344 if total:
3345 if freight_distributed_by == 1:
3346 weitage_by_weight = float(lr.weight) / float(total)
3347 elif freight_distributed_by == 2:
3348 weitage_by_weight = float(lr.num_pkgs or 0) / float(total)
3349 lr.freight_amount = weitage_by_weight * revenue_components.get('freight_amount', 0)
3350 else:
3351 if index == 0:
3352 lr.freight_amount = revenue_components.get('freight_amount', 0)
3353 else:
3354 if index == 0:
3355 lr.freight_amount = revenue_components.get('freight_amount', 0)
3356 if index == 0:
3357 lr.loading_charge = revenue_components.get('loading_charge', 0)
3358 lr.unloading_charge = revenue_components.get('unloading_charge',0)
3359 lr.multiple_loading_pt_charge = revenue_components.get('multiple_loading_pt_charge',0)
3360 lr.multiple_unloading_pt_charge = revenue_components.get('multiple_unloading_pt_charge', 0)
3361 lr.loading_detention = revenue_components.get('loading_detention', 0)
3362 lr.unloading_detention = revenue_components.get('unloading_detention',0)
3363 lr.plywood_charges = revenue_components.get('plywood_charges', 0)
3364 lr.checkpost_charges = revenue_components.get('checkpost_charges', 0)
3365 lr.green_tax = revenue_components.get('green_tax', 0)
3366 else:
3367 lr.loading_charge = 0
3368 lr.unloading_charge = 0
3369 lr.loading_detention = 0
3370 lr.unloading_detention = 0
3371 lr.multiple_loading_pt_charge = 0
3372 lr.multiple_unloading_pt_charge = 0
3373 lr.plywood_charges = 0
3374 lr.checkpost_charges = 0
3375 lr.green_tax = 0
3376 lr.save()
3377 lrs.append(lr)
3378 except Exception as e:
3379 resp_status = 1
3380 status_msg = "Error while saving financial invoicing fields"
3381 return status_msg, resp_status, result
3382
3383 result['total_revenue'] = total_revenue
3384 result['items'] = lrs
3385
3386 # Push RevenueUpdate event to Ledger
3387 if not is_cms_event:
3388 try:
3389 for lr in lrs:
3390 payload = { 'order_id': lr.order.id, 'event_name': 'RevenueUpdate', 'audit_type': 'Revenue', 'event_time': utils.current_time().isoformat() }
3391 AuditTrailService().push_audit_trail_event(payload, AuditTrailConstants.events_amqp_exchange.value, AuditTrailConstants.events_amqp_routing_key.value)
3392 except Exception as e:
3393 utils.write_error_logs('LEDGER : ' + e.message)
3394
3395 return status_msg, resp_status, result
3396
3397def get_orders_to_be_updated(data):
3398 """
3399 This method retrieves order ids based on rate sheet data. These orders will be updated and pushed to profitability & ledger
3400 :param data:
3401 :return:
3402 """
3403 current_epoc = utils.current_time(epoch=True)
3404 source_detention, destination_detention, obj = False, False, { }
3405 final_obj = data.get('rate_obj', { }) or data.get('unload_detention_obj', { }) or data.get('load_detention_obj',
3406 { }) or data.get(
3407 'load_unload_obj', { })
3408 if final_obj and isinstance(final_obj, list):
3409 final_obj = final_obj[0]
3410
3411 if final_obj:
3412 obj = { 'customer': final_obj.get('customer') }
3413 obj['truck_type'] = final_obj.get('truck_type')
3414 obj['start_time'] = final_obj.get('start_time', current_epoc)
3415 if not isinstance(obj['start_time'], float):
3416 obj['start_time'] = datetime.strptime(obj['start_time'], '%d %B, %Y - %I:%M %p').strftime(
3417 '%Y-%m-%d %H:%M:%S')
3418 else:
3419 obj['start_time'] = utils.epoc_to_datetime(obj.get('start_time'), return_str=True)
3420 if final_obj['end_time'] is None:
3421 obj['end_time'] = (utils.current_time() + timedelta(days=5)).strftime('%Y-%m-%d %H:%M:%S')
3422 else:
3423 if not isinstance(final_obj['end_time'], float):
3424 obj['end_time'] = datetime.strptime(final_obj['end_time'], '%d %B, %Y - %I:%M %p').strftime(
3425 '%Y-%m-%d %H:%M:%S')
3426 else:
3427 obj['end_time'] = utils.epoc_to_datetime(final_obj.get('end_time'), return_str=True)
3428
3429 if data.has_key('unload_detention_obj'):
3430 obj['to_city'] = final_obj.get('location')
3431 destination_detention = True
3432 elif data.has_key('load_detention_obj'):
3433 obj['from_city'] = final_obj.get('location')
3434 source_detention = True
3435 else:
3436 obj['to_city'] = final_obj.get('to_city')
3437 obj['from_city'] = final_obj.get('from_city')
3438
3439 order_ids = get_order_id_from_filters(obj, source_detention, destination_detention)
3440 return order_ids