From cde452add7598e35d9933ca8107fdcae711f8e61 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Mon, 14 Dec 2020 16:51:25 +0530 Subject: [PATCH] feat: show e-invoice preview before IRN generation --- erpnext/regional/india/e_invoice/einvoice.js | 75 +++++++++++++++++++- erpnext/regional/india/e_invoice/utils.py | 35 +++++---- 2 files changed, 95 insertions(+), 15 deletions(-) diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js index ff5cef0443e..7466de1ae19 100644 --- a/erpnext/regional/india/e_invoice/einvoice.js +++ b/erpnext/regional/india/e_invoice/einvoice.js @@ -22,11 +22,14 @@ erpnext.setup_einvoice_actions = (doctype) => { if (docstatus == 0 && !irn && !__unsaved) { const action = () => { frappe.call({ - method: 'erpnext.regional.india.e_invoice.utils.generate_irn', + method: 'erpnext.regional.india.e_invoice.utils.get_einvoice', args: { doctype, docname: name }, freeze: true, - callback: () => frm.reload_doc() - }); + callback: (res) => { + const einvoice = res.message; + show_einvoice_preview(frm, einvoice); + } + }) }; add_custom_button(__("Generate IRN"), action); @@ -236,4 +239,70 @@ const get_ewaybill_fields = (frm) => { 'default': frm.doc.gst_vehicle_type } ]; +}; + +const request_irn_generation = (frm, dialog) => { + frappe.call({ + method: 'erpnext.regional.india.e_invoice.utils.generate_irn', + args: { doctype: frm.doc.doctype, docname: frm.doc.name }, + freeze: true, + callback: () => frm.reload_doc() || dialog.hide(), + error: () => dialog.hide() + }); +} + +const get_preview_dialog = (frm, action) => { + return new frappe.ui.Dialog({ + title: __("E Invoice Preview"), + wide: 1, + fields: [ + { + "label": "Preview", + "fieldname": "preview_html", + "fieldtype": "HTML" + } + ], + primary_action: () => action(frm), + primary_action_label: __('Generate IRN') + }); +} + +const show_einvoice_preview = (frm, einvoice) => { + const preview_dialog = get_preview_dialog(frm, request_irn_generation); + + // initialize empty e-invoice fields + einvoice.Irn = einvoice.AckNo = einvoice.AckDate = ''; + frm.doc.signed_einvoice = JSON.stringify(einvoice); + + // initialize preview wrapper + const $preview_wrapper = preview_dialog.get_field("preview_html").$wrapper; + $preview_wrapper.html( + `
+ +
+
` + ); + + frappe.call({ + method: "frappe.www.printview.get_html_and_style", + args: { + doc: frm.doc, + print_format: "GST E-Invoice", + no_letterhead: 1 + }, + callback: function (r) { + if (!r.exc) { + $preview_wrapper.find(".print-format").html(r.message.html); + const style = ` + .print-format { font-size: 7.0pt; box-shadow: 0px 0px 5px rgba(0,0,0,0.2); padding: 0.30in; min-height: 80vh; } + .print-preview { min-height: 0px; } + .modal-dialog { width: 650px; } + ` + frappe.dom.set_style(style, "custom-print-style"); + preview_dialog.show(); + } + } + }); }; \ No newline at end of file diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py index 532e0955bc2..ed7187846c7 100644 --- a/erpnext/regional/india/e_invoice/utils.py +++ b/erpnext/regional/india/e_invoice/utils.py @@ -94,7 +94,9 @@ def get_party_details(address_name): address_line1 = '{} {}'.format(gstin_details.get('AddrBno'), gstin_details.get('AddrFlno')) address_line2 = '{} {}'.format(gstin_details.get('AddrBnm'), gstin_details.get('AddrSt')) email_id = address.get('email_id') - phone = address.get('phone').replace(" ", "")[-10:] # get last 10 digit + phone = address.get('phone') + # get last 10 digit + phone = phone.replace(" ", "")[-10:] if phone else '' if state_code == 97: pincode = 999999 @@ -179,7 +181,7 @@ def get_item_list(invoice): item.cgst_amount += abs(item_tax_detail[1]) item.total_value = abs( - item.base_amount + item.igst_amount + item.sgst_amount + + item.taxable_value + item.igst_amount + item.sgst_amount + item.cgst_amount + item.cess_amount + item.cess_nadv_amount + item.other_charges ) einv_item = item_schema.format(item=item) @@ -197,8 +199,8 @@ def get_value_details(invoice): # discount amount cannnot be -ve in an e-invoice, so if -ve include discount in round_off value_details.round_off = invoice.rounding_adjustment - (invoice.discount_amount if invoice.discount_amount and invoice.discount_amount < 0 else 0) disable_rounded = frappe.db.get_single_value('Global Defaults', 'disable_rounded_total') - value_details.base_grand_total = abs(invoice.base_grand_total) if disable_rounded else invoice.base_rounded_total - value_details.grand_total = abs(invoice.grand_total) if disable_rounded else invoice.rounded_total + value_details.base_grand_total = abs(invoice.base_grand_total) if disable_rounded else abs(invoice.base_rounded_total) + value_details.grand_total = abs(invoice.grand_total) if disable_rounded else abs(invoice.rounded_total) value_details.total_cgst_amt = 0 value_details.total_sgst_amt = 0 value_details.total_igst_amt = 0 @@ -301,12 +303,16 @@ def make_einvoice(invoice): errors = validate_einvoice(validations, einvoice) if errors: message = "\n".join([ - "E Invoice: ", json.dumps(einvoice, default=str, indent=4), + "E Invoice: ", json.dumps(einvoice, indent=4), "-" * 50, - "Errors: ", json.dumps(errors, default=str, indent=4) + "Errors: ", json.dumps(errors, indent=4) ]) frappe.log_error(title="E Invoice Validation Failed", message=message) - frappe.throw(errors, title=_('E Invoice Validation Failed'), as_list=1) + if len(errors) > 1: + li = ['
  • '+ d +'
  • ' for d in errors] + frappe.throw("".format(''.join(li)), title=_('E Invoice Validation Failed')) + else: + frappe.throw(errors[0], title=_('E Invoice Validation Failed')) return einvoice @@ -396,7 +402,7 @@ class GSPConnector(): "reference_invoice": self.invoice.name if self.invoice else None, "url": url, "headers": json.dumps(headers, indent=4) if headers else None, - "data": json.dumps(data, indent=4) if data else None, + "data": json.dumps(data, indent=4) if isinstance(data, dict) else data, "response": json.dumps(res, indent=4) if res else None }) request_log.insert(ignore_permissions=True) @@ -463,7 +469,7 @@ class GSPConnector(): def generate_irn(self): headers = self.get_headers() einvoice = make_einvoice(self.invoice) - data = json.dumps(einvoice) + data = json.dumps(einvoice, indent=4) try: res = self.make_request('post', self.generate_irn_url, headers, data) @@ -516,7 +522,7 @@ class GSPConnector(): 'Irn': irn, 'Cnlrsn': reason, 'Cnlrem': remark - }) + }, indent=4) try: res = self.make_request('post', self.cancel_irn_url, headers, data) @@ -555,7 +561,7 @@ class GSPConnector(): 'TrnDocNo': eway_bill_details.document_name, 'VehNo': eway_bill_details.vehicle_no, 'VehType': eway_bill_details.vehicle_type - }) + }, indent=4) try: res = self.make_request('post', self.generate_ewaybill_url, headers, data) @@ -587,7 +593,7 @@ class GSPConnector(): 'ewbNo': eway_bill, 'cancelRsnCode': reason, 'cancelRmrk': remark - }) + }, indent=4) try: res = self.make_request('post', self.cancel_ewaybill_url, headers, data) @@ -681,6 +687,11 @@ class GSPConnector(): self.invoice.flags.ignore_validate = True self.invoice.save() +@frappe.whitelist() +def get_einvoice(doctype, docname): + invoice = frappe.get_doc(doctype, docname) + return make_einvoice(invoice) + @frappe.whitelist() def generate_irn(doctype, docname): gsp_connector = GSPConnector(doctype, docname)