Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 1 | # -*- coding: utf-8 -*- |
| 2 | # Copyright (c) 2018, Frappe and contributors |
| 3 | # For license information, please see license.txt |
| 4 | |
| 5 | from __future__ import unicode_literals |
| 6 | import frappe |
| 7 | from frappe.model.document import Document |
| 8 | from frappe.utils import cint,cstr, today |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 9 | from frappe import _ |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 10 | import re |
| 11 | import datetime |
| 12 | from collections import OrderedDict |
| 13 | |
| 14 | def create_bank_remittance_txt(name): |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 15 | payment_order = frappe.get_cached_doc("Payment Order", name) |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 16 | |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 17 | no_of_records = len(payment_order.get("references")) |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 18 | total_amount = sum(entry.get("amount") for entry in payment_order.get("references")) |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 19 | |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 20 | product_code, client_code, company_email = frappe.db.get_value("Company", |
Mangesh-Khairnar | 66a3c99 | 2019-05-06 16:26:05 +0530 | [diff] [blame] | 21 | filters={'name' : payment_order.company}, |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 22 | fieldname=['product_code', 'client_code', 'email']) |
Mangesh-Khairnar | 66a3c99 | 2019-05-06 16:26:05 +0530 | [diff] [blame] | 23 | |
| 24 | header, file_name = get_header_row(payment_order, client_code) |
| 25 | batch = get_batch_row(payment_order, no_of_records, total_amount, product_code) |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 26 | |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 27 | detail = [] |
| 28 | for ref_doc in payment_order.get("references"): |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 29 | detail += get_detail_row(ref_doc, payment_order, company_email) |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 30 | |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 31 | trailer = get_trailer_row(no_of_records, total_amount) |
| 32 | detail_records = "\n".join(detail) |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 33 | |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 34 | return "\n".join([header, batch, detail_records, trailer]), file_name |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 35 | |
| 36 | @frappe.whitelist() |
Mangesh-Khairnar | 08a375b | 2019-05-05 20:45:21 +0530 | [diff] [blame] | 37 | def generate_report(name): |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 38 | data, file_name = create_bank_remittance_txt(name) |
Mangesh-Khairnar | c448664 | 2019-05-05 22:36:32 +0530 | [diff] [blame] | 39 | |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 40 | f = frappe.get_doc({ |
| 41 | 'doctype': 'File', |
| 42 | 'file_name': file_name, |
| 43 | 'content': data, |
| 44 | "attached_to_doctype": 'Payment Order', |
| 45 | "attached_to_name": name, |
| 46 | 'is_private': True |
| 47 | }) |
| 48 | f.save() |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 49 | return { |
| 50 | 'file_url': f.file_url, |
| 51 | 'file_name': file_name |
| 52 | } |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 53 | |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 54 | def generate_file_name(name, company_account, date): |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 55 | ''' generate file name with format (account_code)_mmdd_(payment_order_no) ''' |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 56 | bank, acc_no = frappe.db.get_value("Bank Account", {"name": company_account}, ['bank', 'bank_account_no']) |
| 57 | return bank[:1]+str(acc_no)[-4:]+'_'+date.strftime("%m%d")+sanitize_data(name, '')[4:]+'.txt' |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 58 | |
Mangesh-Khairnar | 66a3c99 | 2019-05-06 16:26:05 +0530 | [diff] [blame] | 59 | def get_header_row(doc, client_code): |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 60 | ''' Returns header row and generated file name ''' |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 61 | file_name = generate_file_name(doc.name, doc.company_bank_account, doc.posting_date) |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 62 | header = ["H"] |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 63 | header.append(validate_field_size(client_code, "Client Code", 20)) |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 64 | header += [''] * 3 |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 65 | header.append(validate_field_size(file_name, "File Name", 20)) |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 66 | return "~".join(header), file_name |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 67 | |
Mangesh-Khairnar | 66a3c99 | 2019-05-06 16:26:05 +0530 | [diff] [blame] | 68 | def get_batch_row(doc, no_of_records, total_amount, product_code): |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 69 | batch = ["B"] |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 70 | batch.append(validate_field_size(no_of_records, "No Of Records", 5)) |
| 71 | batch.append(validate_amount(format(total_amount, '0.2f'), 17)) |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 72 | batch.append(sanitize_data(doc.name, '_')[:20]) |
| 73 | batch.append(format_date(doc.posting_date)) |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 74 | batch.append(validate_field_size(product_code,"Product Code", 20)) |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 75 | return "~".join(batch) |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 76 | |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 77 | def get_detail_row(ref_doc, payment_entry, company_email): |
| 78 | |
| 79 | payment_date = format_date(payment_entry.posting_date) |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 80 | payment_entry = frappe.get_cached_doc('Payment Entry', ref_doc.payment_entry) |
| 81 | supplier_bank_details = frappe.get_cached_doc('Bank Account', ref_doc.bank_account) |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 82 | company_bank_acc_no = frappe.db.get_value("Bank Account", {'name': payment_entry.bank_account}, ['bank_account_no']) |
| 83 | |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 84 | addr_link = frappe.db.get_value('Dynamic Link', |
| 85 | { |
| 86 | 'link_doctype': 'Supplier', |
| 87 | 'link_name': 'Sample Supplier', |
| 88 | 'parenttype':'Address', |
| 89 | 'parent': ('like', '%-Billing') |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 90 | }, 'parent') |
| 91 | |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 92 | supplier_billing_address = frappe.get_cached_doc('Address', addr_link) |
Mangesh-Khairnar | 4df61b1 | 2019-05-11 20:10:20 +0530 | [diff] [blame] | 93 | email = ','.join(filter(None, [supplier_billing_address.email_id, company_email])) |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 94 | |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 95 | detail = OrderedDict( |
| 96 | record_identifier='D', |
| 97 | payment_ref_no=sanitize_data(ref_doc.payment_entry), |
| 98 | payment_type=cstr(payment_entry.mode_of_payment)[:10], |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 99 | amount=str(validate_amount(format(ref_doc.amount, '.2f'),13)), |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 100 | payment_date=payment_date, |
| 101 | instrument_date=payment_date, |
| 102 | instrument_number='', |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 103 | dr_account_no_client=str(validate_field_size(company_bank_acc_no, "Company Bank Account", 20)), |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 104 | dr_description='', |
| 105 | dr_ref_no='', |
| 106 | cr_ref_no='', |
| 107 | bank_code_indicator='M', |
| 108 | beneficiary_code='', |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 109 | beneficiary_name=sanitize_data(validate_information(payment_entry, "party", 160), ' '), |
| 110 | beneficiary_bank=sanitize_data(validate_information(supplier_bank_details, "bank", 10)), |
| 111 | beneficiary_branch_code=cstr(validate_information(supplier_bank_details, "branch_code", 11)), |
| 112 | beneficiary_acc_no=validate_information(supplier_bank_details, "bank_account_no", 20), |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 113 | location='', |
| 114 | print_location='', |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 115 | beneficiary_address_1=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line1), ' '), " Beneficiary Address 1", 50), |
| 116 | beneficiary_address_2=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line2), ' '), " Beneficiary Address 2", 50), |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 117 | beneficiary_address_3='', |
| 118 | beneficiary_address_4='', |
| 119 | beneficiary_address_5='', |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 120 | beneficiary_city=validate_field_size(cstr(supplier_billing_address.city), "Beneficiary City", 20), |
| 121 | beneficiary_zipcode=validate_field_size(cstr(supplier_billing_address.pincode), "Pin Code", 6), |
| 122 | beneficiary_state=validate_field_size(cstr(supplier_billing_address.state), "Beneficiary State", 20), |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 123 | beneficiary_email=cstr(email)[:255], |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 124 | beneficiary_mobile=validate_field_size(cstr(supplier_billing_address.phone), "Beneficiary Mobile", 10), |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 125 | payment_details_1='', |
| 126 | payment_details_2='', |
| 127 | payment_details_3='', |
| 128 | payment_details_4='', |
| 129 | delivery_mode='' |
| 130 | ) |
| 131 | detail_record = ["~".join(list(detail.values()))] |
Mangesh-Khairnar | 0b2fc4f | 2019-05-08 15:57:29 +0530 | [diff] [blame] | 132 | |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 133 | detail_record += get_advice_rows(payment_entry) |
| 134 | return detail_record |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 135 | |
Mangesh-Khairnar | 08a375b | 2019-05-05 20:45:21 +0530 | [diff] [blame] | 136 | def get_advice_rows(payment_entry): |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 137 | ''' Returns multiple advice rows for a single detail entry ''' |
| 138 | payment_entry_date = payment_entry.posting_date.strftime("%b%y%d%m").upper() |
| 139 | mode_of_payment = payment_entry.mode_of_payment |
| 140 | advice_rows = [] |
| 141 | for record in payment_entry.references: |
| 142 | advice = ['E'] |
| 143 | advice.append(cstr(mode_of_payment)) |
| 144 | advice.append(cstr(record.total_amount)) |
| 145 | advice.append('') |
| 146 | advice.append(cstr(record.outstanding_amount)) |
| 147 | advice.append(record.reference_name) |
| 148 | advice.append(format_date(record.due_date)) |
| 149 | advice.append(payment_entry_date) |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 150 | advice_rows.append("~".join(advice)) |
| 151 | return advice_rows |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 152 | |
| 153 | def get_trailer_row(no_of_records, total_amount): |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 154 | ''' Returns trailer row ''' |
| 155 | trailer = ["T"] |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 156 | trailer.append(validate_field_size(no_of_records, "No of Records", 5)) |
| 157 | trailer.append(validate_amount(format(total_amount, "0.2f"), 17)) |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 158 | return "~".join(trailer) |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 159 | |
Mangesh-Khairnar | 66a3c99 | 2019-05-06 16:26:05 +0530 | [diff] [blame] | 160 | def sanitize_data(val, replace_str=''): |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 161 | ''' Remove all the non-alphanumeric characters from string ''' |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 162 | pattern = re.compile('[\W_]+') |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 163 | return pattern.sub(replace_str, val) |
Mangesh-Khairnar | b89db7f | 2019-05-03 21:21:37 +0530 | [diff] [blame] | 164 | |
| 165 | def format_date(val): |
Mangesh-Khairnar | 27ea171 | 2019-05-06 16:37:34 +0530 | [diff] [blame] | 166 | ''' Convert a datetime object to DD/MM/YYYY format ''' |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 167 | return val.strftime("%d/%m/%Y") |
| 168 | |
| 169 | def validate_amount(val, max_int_size): |
| 170 | ''' Validate amount to be within the allowed limits ''' |
| 171 | int_size = len(str(val).split('.')[0]) |
| 172 | |
| 173 | if int_size > max_int_size: |
Mangesh-Khairnar | 6e35864 | 2019-05-15 15:50:50 +0530 | [diff] [blame] | 174 | frappe.throw(_("Amount for a single transaction exceeds maximum allowed amount, create a separate payment order by splitting the transactions")) |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 175 | |
| 176 | return val |
| 177 | |
| 178 | def validate_information(obj, attr, max_size): |
| 179 | ''' Checks if the information is not set in the system and is within the size ''' |
| 180 | if hasattr(obj, attr): |
| 181 | return validate_field_size(getattr(obj, attr), frappe.unscrub(attr), max_size) |
| 182 | |
| 183 | else: |
Mangesh-Khairnar | 6e35864 | 2019-05-15 15:50:50 +0530 | [diff] [blame] | 184 | frappe.throw(_("{0} is mandatory for generating remittance payments, set the field and try again".format(frappe.unscrub(attr)))) |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 185 | |
| 186 | def validate_field_size(val, label, max_size): |
| 187 | ''' check the size of the val ''' |
| 188 | if len(cstr(val)) > max_size: |
Mangesh-Khairnar | 6e35864 | 2019-05-15 15:50:50 +0530 | [diff] [blame] | 189 | frappe.throw(_("{0} field is limited to size {1}".format(label, max_size))) |
Mangesh-Khairnar | 6175eb2 | 2019-05-14 18:25:57 +0530 | [diff] [blame] | 190 | return cstr(val) |