feat(india): Improve E-way Bill Cancellation. (#31088)
diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
index ef24ce7..580e646 100644
--- a/erpnext/regional/india/e_invoice/einvoice.js
+++ b/erpnext/regional/india/e_invoice/einvoice.js
@@ -150,26 +150,29 @@
if (irn && ewaybill && !irn_cancelled && !eway_bill_cancelled) {
const action = () => {
- let message = __('Cancellation of e-way bill is currently not supported.') + ' ';
- message += '<br><br>';
- message += __('You must first use the portal to cancel the e-way bill and then update the cancelled status in the ERPNext system.');
-
- const dialog = frappe.msgprint({
- title: __('Update E-Way Bill Cancelled Status?'),
- message: message,
- indicator: 'orange',
- primary_action: {
- action: function() {
- frappe.call({
- method: 'erpnext.regional.india.e_invoice.utils.cancel_eway_bill',
- args: { doctype, docname: name },
- freeze: true,
- callback: () => frm.reload_doc() && dialog.hide()
- });
- }
+ // This confirm is added to just reduce unnecesory API calls. All required logic is implemented on server side.
+ frappe.confirm(
+ __("Have you cancelled e-way bill on the portal?"),
+ () => {
+ frappe.call({
+ method: "erpnext.regional.india.e_invoice.utils.cancel_eway_bill",
+ args: { doctype, docname: name },
+ freeze: true,
+ callback: () => frm.reload_doc(),
+ });
},
- primary_action_label: __('Yes')
- });
+ () => {
+ frappe.show_alert(
+ {
+ message: __(
+ "Please cancel e-way bill on the portal first."
+ ),
+ indicator: "orange",
+ },
+ 5
+ );
+ }
+ );
};
add_custom_button(__("Cancel E-Way Bill"), action);
}
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index e5a1a59..9add09b 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -803,6 +803,8 @@
self.gstin_details_url = self.base_url + "/enriched/ei/api/master/gstin"
# cancel_ewaybill_url will only work if user have bought ewb api from adaequare.
self.cancel_ewaybill_url = self.base_url + "/enriched/ewb/ewayapi?action=CANEWB"
+ # ewaybill_details_url + ?irn={irn_number} will provide eway bill number and details.
+ self.ewaybill_details_url = self.base_url + "/enriched/ei/api/ewaybill/irn"
self.generate_ewaybill_url = self.base_url + "/enriched/ei/api/ewaybill"
self.get_qrcode_url = self.base_url + "/enriched/ei/others/qr/image"
@@ -1205,23 +1207,22 @@
log_error(data)
self.raise_error(True)
- def cancel_eway_bill(self, eway_bill, reason, remark):
+ def get_ewb_details(self):
+ """
+ Get e-Waybill Details by IRN API documentaion for validation is not added yet.
+ https://einv-apisandbox.nic.in/version1.03/get-ewaybill-details-by-irn.html#validations
+ NOTE: if ewaybill Validity period lapsed or scanned by officer enroute (not tested yet) it will still return status as "ACT".
+ """
headers = self.get_headers()
- data = json.dumps({"ewbNo": eway_bill, "cancelRsnCode": reason, "cancelRmrk": remark}, indent=4)
- headers["username"] = headers["user_name"]
- del headers["user_name"]
- try:
- res = self.make_request("post", self.cancel_ewaybill_url, headers, data)
- if res.get("success"):
- self.invoice.ewaybill = ""
- self.invoice.eway_bill_cancelled = 1
- self.invoice.flags.updater_reference = {
- "doctype": self.invoice.doctype,
- "docname": self.invoice.name,
- "label": _("E-Way Bill Cancelled - {}").format(remark),
- }
- self.update_invoice()
+ irn = self.invoice.irn
+ if not irn:
+ frappe.throw(_("IRN is mandatory to get E-Waybill Details. Please generate IRN first."))
+ try:
+ params = "?irn={irn}".format(irn=irn)
+ res = self.make_request("get", self.ewaybill_details_url + params, headers)
+ if res.get("success"):
+ return res.get("result")
else:
raise RequestFailed
@@ -1230,9 +1231,65 @@
self.raise_error(errors=errors)
except Exception:
- log_error(data)
+ log_error()
self.raise_error(True)
+ def update_ewb_details(self, ewb_details=None):
+ # for any reason user chooses to generate eway bill using portal this will allow to update ewaybill details in the invoice.
+ if not self.invoice.irn:
+ frappe.throw(_("IRN is mandatory to update E-Waybill Details. Please generate IRN first."))
+ if not ewb_details:
+ ewb_details = self.get_ewb_details()
+ if ewb_details:
+ self.invoice.ewaybill = ewb_details.get("EwbNo")
+ self.invoice.eway_bill_validity = ewb_details.get("EwbValidTill")
+ self.invoice.eway_bill_cancelled = 0 if ewb_details.get("Status") == "ACT" else 1
+ self.update_invoice()
+
+ def cancel_eway_bill(self):
+ ewb_details = self.get_ewb_details()
+ if ewb_details:
+ ewb_no = str(ewb_details.get("EwbNo"))
+ ewb_status = ewb_details.get("Status")
+ if ewb_status == "CNL":
+ self.invoice.ewaybill = ""
+ self.invoice.eway_bill_cancelled = 1
+ self.invoice.flags.updater_reference = {
+ "doctype": self.invoice.doctype,
+ "docname": self.invoice.name,
+ "label": _("E-Way Bill Cancelled"),
+ }
+ self.update_invoice()
+ frappe.msgprint(
+ _("E-Way Bill Cancelled successfully"),
+ indicator="green",
+ alert=True,
+ )
+ elif ewb_status == "ACT" and self.invoice.ewaybill == ewb_no:
+ msg = _("E-Way Bill {} is still active.").format(bold(ewb_no))
+ msg += "<br><br>"
+ msg += _(
+ "You must first use the portal to cancel the e-way bill and then update the cancelled status in the ERPNext system."
+ )
+ frappe.msgprint(msg)
+ elif ewb_status == "ACT" and self.invoice.ewaybill != ewb_no:
+ # if user cancelled the current eway bill and generated new eway bill using portal, then this will update new ewb number in sales invoice.
+ msg = _("E-Way Bill No. {0} doesn't match {1} saved in the invoice.").format(
+ bold(ewb_no), bold(self.invoice.ewaybill)
+ )
+ msg += "<hr/>"
+ msg += _("E-Way Bill No. {} is updated in the invoice.").format(bold(ewb_no))
+ frappe.msgprint(msg)
+ self.update_ewb_details(ewb_details=ewb_details)
+ else:
+ # this block should not be ever called but added incase there is any change in API.
+ msg = _("Unknown E-Way Status Code {}.").format(ewb_status)
+ msg += "<br><br>"
+ msg += _("Please contact your system administrator.")
+ frappe.throw(msg)
+ else:
+ frappe.msgprint(_("E-Way Bill Details not found for this IRN."))
+
def sanitize_error_message(self, message):
"""
On validation errors, response message looks something like this:
@@ -1383,12 +1440,22 @@
@frappe.whitelist()
def cancel_eway_bill(doctype, docname):
- # NOTE: cancel_eway_bill api is disabled by Adequare.
- # gsp_connector = GSPConnector(doctype, docname)
- # gsp_connector.cancel_eway_bill(eway_bill, reason, remark)
+ # NOTE: cancel_eway_bill api is disabled by NIC for E-invoice so this will only check if eway bill is canceled or not and update accordingly.
+ # https://einv-apisandbox.nic.in/version1.03/cancel-eway-bill.html#
+ gsp_connector = GSPConnector(doctype, docname)
+ gsp_connector.cancel_eway_bill()
- frappe.db.set_value(doctype, docname, "ewaybill", "")
- frappe.db.set_value(doctype, docname, "eway_bill_cancelled", 1)
+
+@frappe.whitelist()
+def get_ewb_details(doctype, docname):
+ gsp_connector = GSPConnector(doctype, docname)
+ gsp_connector.get_ewb_details()
+
+
+@frappe.whitelist()
+def update_ewb_details(doctype, docname):
+ gsp_connector = GSPConnector(doctype, docname)
+ gsp_connector.update_ewb_details()
@frappe.whitelist()