mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-27 08:54:45 +00:00
feat: show e-invoice preview before IRN generation
This commit is contained in:
@@ -22,11 +22,14 @@ erpnext.setup_einvoice_actions = (doctype) => {
|
|||||||
if (docstatus == 0 && !irn && !__unsaved) {
|
if (docstatus == 0 && !irn && !__unsaved) {
|
||||||
const action = () => {
|
const action = () => {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: 'erpnext.regional.india.e_invoice.utils.generate_irn',
|
method: 'erpnext.regional.india.e_invoice.utils.get_einvoice',
|
||||||
args: { doctype, docname: name },
|
args: { doctype, docname: name },
|
||||||
freeze: true,
|
freeze: true,
|
||||||
callback: () => frm.reload_doc()
|
callback: (res) => {
|
||||||
});
|
const einvoice = res.message;
|
||||||
|
show_einvoice_preview(frm, einvoice);
|
||||||
|
}
|
||||||
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
add_custom_button(__("Generate IRN"), action);
|
add_custom_button(__("Generate IRN"), action);
|
||||||
@@ -237,3 +240,69 @@ const get_ewaybill_fields = (frm) => {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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(
|
||||||
|
`<div>
|
||||||
|
<div class="print-preview">
|
||||||
|
<div class="print-format"></div>
|
||||||
|
</div>
|
||||||
|
<div class="page-break-message text-muted text-center text-medium margin-top"></div>
|
||||||
|
</div>`
|
||||||
|
);
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -94,7 +94,9 @@ def get_party_details(address_name):
|
|||||||
address_line1 = '{} {}'.format(gstin_details.get('AddrBno'), gstin_details.get('AddrFlno'))
|
address_line1 = '{} {}'.format(gstin_details.get('AddrBno'), gstin_details.get('AddrFlno'))
|
||||||
address_line2 = '{} {}'.format(gstin_details.get('AddrBnm'), gstin_details.get('AddrSt'))
|
address_line2 = '{} {}'.format(gstin_details.get('AddrBnm'), gstin_details.get('AddrSt'))
|
||||||
email_id = address.get('email_id')
|
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:
|
if state_code == 97:
|
||||||
pincode = 999999
|
pincode = 999999
|
||||||
|
|
||||||
@@ -179,7 +181,7 @@ def get_item_list(invoice):
|
|||||||
item.cgst_amount += abs(item_tax_detail[1])
|
item.cgst_amount += abs(item_tax_detail[1])
|
||||||
|
|
||||||
item.total_value = abs(
|
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
|
item.cgst_amount + item.cess_amount + item.cess_nadv_amount + item.other_charges
|
||||||
)
|
)
|
||||||
einv_item = item_schema.format(item=item)
|
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
|
# 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)
|
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')
|
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.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 invoice.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_cgst_amt = 0
|
||||||
value_details.total_sgst_amt = 0
|
value_details.total_sgst_amt = 0
|
||||||
value_details.total_igst_amt = 0
|
value_details.total_igst_amt = 0
|
||||||
@@ -301,12 +303,16 @@ def make_einvoice(invoice):
|
|||||||
errors = validate_einvoice(validations, einvoice)
|
errors = validate_einvoice(validations, einvoice)
|
||||||
if errors:
|
if errors:
|
||||||
message = "\n".join([
|
message = "\n".join([
|
||||||
"E Invoice: ", json.dumps(einvoice, default=str, indent=4),
|
"E Invoice: ", json.dumps(einvoice, indent=4),
|
||||||
"-" * 50,
|
"-" * 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.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 = ['<li>'+ d +'</li>' for d in errors]
|
||||||
|
frappe.throw("<ul style='padding-left: 20px'>{}</ul>".format(''.join(li)), title=_('E Invoice Validation Failed'))
|
||||||
|
else:
|
||||||
|
frappe.throw(errors[0], title=_('E Invoice Validation Failed'))
|
||||||
|
|
||||||
return einvoice
|
return einvoice
|
||||||
|
|
||||||
@@ -396,7 +402,7 @@ class GSPConnector():
|
|||||||
"reference_invoice": self.invoice.name if self.invoice else None,
|
"reference_invoice": self.invoice.name if self.invoice else None,
|
||||||
"url": url,
|
"url": url,
|
||||||
"headers": json.dumps(headers, indent=4) if headers else None,
|
"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
|
"response": json.dumps(res, indent=4) if res else None
|
||||||
})
|
})
|
||||||
request_log.insert(ignore_permissions=True)
|
request_log.insert(ignore_permissions=True)
|
||||||
@@ -463,7 +469,7 @@ class GSPConnector():
|
|||||||
def generate_irn(self):
|
def generate_irn(self):
|
||||||
headers = self.get_headers()
|
headers = self.get_headers()
|
||||||
einvoice = make_einvoice(self.invoice)
|
einvoice = make_einvoice(self.invoice)
|
||||||
data = json.dumps(einvoice)
|
data = json.dumps(einvoice, indent=4)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
res = self.make_request('post', self.generate_irn_url, headers, data)
|
res = self.make_request('post', self.generate_irn_url, headers, data)
|
||||||
@@ -516,7 +522,7 @@ class GSPConnector():
|
|||||||
'Irn': irn,
|
'Irn': irn,
|
||||||
'Cnlrsn': reason,
|
'Cnlrsn': reason,
|
||||||
'Cnlrem': remark
|
'Cnlrem': remark
|
||||||
})
|
}, indent=4)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
res = self.make_request('post', self.cancel_irn_url, headers, data)
|
res = self.make_request('post', self.cancel_irn_url, headers, data)
|
||||||
@@ -555,7 +561,7 @@ class GSPConnector():
|
|||||||
'TrnDocNo': eway_bill_details.document_name,
|
'TrnDocNo': eway_bill_details.document_name,
|
||||||
'VehNo': eway_bill_details.vehicle_no,
|
'VehNo': eway_bill_details.vehicle_no,
|
||||||
'VehType': eway_bill_details.vehicle_type
|
'VehType': eway_bill_details.vehicle_type
|
||||||
})
|
}, indent=4)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
res = self.make_request('post', self.generate_ewaybill_url, headers, data)
|
res = self.make_request('post', self.generate_ewaybill_url, headers, data)
|
||||||
@@ -587,7 +593,7 @@ class GSPConnector():
|
|||||||
'ewbNo': eway_bill,
|
'ewbNo': eway_bill,
|
||||||
'cancelRsnCode': reason,
|
'cancelRsnCode': reason,
|
||||||
'cancelRmrk': remark
|
'cancelRmrk': remark
|
||||||
})
|
}, indent=4)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
res = self.make_request('post', self.cancel_ewaybill_url, headers, data)
|
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.flags.ignore_validate = True
|
||||||
self.invoice.save()
|
self.invoice.save()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_einvoice(doctype, docname):
|
||||||
|
invoice = frappe.get_doc(doctype, docname)
|
||||||
|
return make_einvoice(invoice)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def generate_irn(doctype, docname):
|
def generate_irn(doctype, docname):
|
||||||
gsp_connector = GSPConnector(doctype, docname)
|
gsp_connector = GSPConnector(doctype, docname)
|
||||||
|
|||||||
Reference in New Issue
Block a user