diff --git a/erpnext/regional/india/e_invoice_utils.py b/erpnext/regional/india/e_invoice_utils.py
index dc38b25bed7..6109f800f7d 100644
--- a/erpnext/regional/india/e_invoice_utils.py
+++ b/erpnext/regional/india/e_invoice_utils.py
@@ -22,10 +22,12 @@ def validate_einvoice_fields(doc):
e_invoice_enabled = frappe.db.get_value("E Invoice Settings", "E Invoice Settings", "enable")
if not doc.doctype in ['Sales Invoice', 'Purchase Invoice'] or not e_invoice_enabled: return
- if doc.docstatus == 1 and doc._action == 'submit' and not doc.irn:
+ if doc.docstatus == 0 and doc._action == 'save' and doc.irn:
+ frappe.throw(_("You cannot edit the invoice after generating IRN"), title=_("Edit Not Allowed"))
+ elif doc.docstatus == 1 and doc._action == 'submit' and not doc.irn:
frappe.throw(_("You must generate IRN before submitting the document."), title=_("Missing IRN"))
elif doc.docstatus == 2 and doc._action == 'cancel' and not doc.irn_cancelled:
- frappe.throw(_("You must cancel IRN before cancelling the document."), title=_("Not Allowed"))
+ frappe.throw(_("You must cancel IRN before cancelling the document."), title=_("Cancel Not Allowed"))
def get_einv_credentials():
return frappe.get_doc("E Invoice Settings")
@@ -266,20 +268,24 @@ def get_doc_details(invoice):
))
def get_party_gstin_details(party_address):
- gstin, address_line1, address_line2, phone, email_id = frappe.db.get_value(
- "Address", party_address, ["gstin", "address_line1", "address_line2", "phone", "email_id"]
- )
- gstin_details = get_gstin_details(gstin)
- legal_name = gstin_details.get('LegalName')
- trade_name = gstin_details.get('TradeName')
- location = gstin_details.get('AddrLoc')
- state_code = gstin_details.get('StateCode')
- pincode = cint(gstin_details.get('AddrPncd'))
+ address = frappe.get_all("Address", filters={"name": party_address}, fields=["*"])[0]
+
+ # gstin_details = get_gstin_details(gstin)
+ gstin = address.get('gstin')
+ legal_name = address.get('address_title')
+ # trade_name = gstin_details.get('TradeName')
+ location = address.get('city')
+ state_code = address.get('gst_state_number')
+ pincode = cint(address.get('pincode'))
+ address_line1 = address.get('address_line1')
+ address_line2 = address.get('address_line2')
+ email_id = address.get('email_id')
+ phone = address.get('phone')
if state_code == 97:
pincode = 999999
return frappe._dict(dict(
- gstin=gstin, legal_name=legal_name, trade_name=trade_name, location=location,
+ gstin=gstin, legal_name=legal_name, location=location,
pincode=pincode, state_code=state_code, address_line1=address_line1,
address_line2=address_line2, email=email_id, phone=phone
))
@@ -338,8 +344,6 @@ def get_item_list(invoice):
e_inv_item = item_schema.format(item=item)
item_list.append(e_inv_item)
- print(e_inv_item)
-
return ", ".join(item_list)
def get_value_details(invoice):
@@ -404,10 +408,11 @@ def get_eway_bill_details(invoice):
vehicle_type=vehicle_type[invoice.gst_vehicle_type]
))
-def make_e_invoice(invoice):
+@frappe.whitelist()
+def make_e_invoice(doctype, name):
+ invoice = frappe.get_doc(doctype, name)
schema = read_json("einv_template")
- validations = read_json("einv_validation")
- validations = json.loads(validations)
+ validations = json.loads(read_json("einv_validation"))
trans_details = get_trans_details(invoice)
doc_details = get_doc_details(invoice)
@@ -451,13 +456,17 @@ def make_e_invoice(invoice):
)
e_invoice = json.loads(e_invoice)
- error_msgs = run_e_invoice_validations(validations, e_invoice, [])
+ error_msgs = validate_einvoice(validations, e_invoice, [])
if error_msgs:
- frappe.throw(_("{}").format("
".join(error_msgs)), title=_("E Invoice Validation"))
+ if len(error_msgs) > 1:
+ li = ["
"+ d +"" for d in error_msgs]
+ frappe.throw(_("""""").format("".join(li)), title=_("E Invoice Validation Failed"))
+ else:
+ frappe.throw(_("{}").format(error_msgs[0]), title=_("E Invoice Validation Failed"))
- return json.dumps(e_invoice)
+ return {'einvoice': json.dumps(e_invoice)}
-def run_e_invoice_validations(validations, e_invoice, error_msgs=[]):
+def validate_einvoice(validations, e_invoice, error_msgs=[]):
type_map = {
"string": cstr,
"number": cint,
@@ -478,9 +487,9 @@ def run_e_invoice_validations(validations, e_invoice, error_msgs=[]):
if isinstance(invoice_value, list):
for d in invoice_value:
- run_e_invoice_validations(properties, d, error_msgs)
+ validate_einvoice(properties, d, error_msgs)
else:
- run_e_invoice_validations(properties, invoice_value, error_msgs)
+ validate_einvoice(properties, invoice_value, error_msgs)
# remove keys with empty dicts
if not invoice_value:
e_invoice.pop(field, None)
@@ -507,6 +516,42 @@ def run_e_invoice_validations(validations, e_invoice, error_msgs=[]):
if value.get('type').lower() == 'number' and not (flt(invoice_value) <= should_be_less_than):
error_msgs.append("{} should be less than {}".format(field_label, should_be_less_than))
if pattern_str and not pattern.match(invoice_value):
- error_msgs.append("{} should match {}".format(field_label, pattern_str))
+ error_msgs.append(value.get('validationMsg'))
- return error_msgs
\ No newline at end of file
+ return error_msgs
+
+@frappe.whitelist()
+def download_einvoice():
+ data = frappe._dict(frappe.local.form_dict)
+ einvoice = data['einvoice']
+ name = data['name']
+
+ frappe.response['filename'] = "E-Invoice-" + name + ".json"
+ frappe.response['filecontent'] = einvoice
+ frappe.response['content_type'] = 'application/json'
+ frappe.response['type'] = 'download'
+
+@frappe.whitelist()
+def upload_einvoice():
+ signed_einvoice = json.loads(frappe.local.uploaded_file)
+ data = frappe._dict(frappe.local.form_dict)
+ doctype = data['doctype']
+ name = data['docname']
+
+ frappe.db.set_value(doctype, name, "irn", signed_einvoice.get("Irn"))
+ frappe.db.set_value(doctype, name, "ewaybill", signed_einvoice.get("EwbNo"))
+
+@frappe.whitelist()
+def download_cancel_einvoice():
+ data = frappe._dict(frappe.local.form_dict)
+ name = data['name']
+ irn = data['irn']
+ reason = data['reason']
+ remark = data['remark']
+
+ cancel_einvoice = json.dumps(dict(Irn=irn, CnlRsn=reason, CnlRem=remark))
+
+ frappe.response['filename'] = "Cancel E-Invoice " + name + ".json"
+ frappe.response['filecontent'] = cancel_einvoice
+ frappe.response['content_type'] = 'application/json'
+ frappe.response['type'] = 'download'
\ No newline at end of file
diff --git a/erpnext/regional/india/einv_validation.json b/erpnext/regional/india/einv_validation.json
index 28bfc396e30..9f0ef5006c4 100644
--- a/erpnext/regional/india/einv_validation.json
+++ b/erpnext/regional/india/einv_validation.json
@@ -59,13 +59,15 @@
"minLength": 1,
"maxLength": 16,
"pattern": "^([A-Z1-9]{1}[A-Z0-9/-]{0,15})$",
- "label": "Document Name"
+ "label": "Document Name",
+ "validationMsg": "Document name should not be starting with 0, / and -"
},
"Dt": {
"type": "string",
"minLength": 10,
"maxLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]"
+ "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+ "validationMsg": "Document Date is invalid"
}
},
"required": ["Typ", "No", "Dt"]
@@ -77,7 +79,8 @@
"type": "string",
"minLength": 15,
"maxLength": 15,
- "pattern": "([0-9]{2}[0-9A-Z]{13})"
+ "pattern": "([0-9]{2}[0-9A-Z]{13})",
+ "validationMsg": "Seller GSTIN is invalid"
},
"LglNm": {
"type": "string",
@@ -134,7 +137,8 @@
"type": "string",
"minLength": 3,
"maxLength": 15,
- "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$"
+ "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$",
+ "validationMsg": "Buyer GSTIN is invalid"
},
"LglNm": {
"type": "string",
@@ -232,7 +236,8 @@
"type": "string",
"maxLength": 15,
"minLength": 3,
- "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$"
+ "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$",
+ "validationMsg": "Shipping Address GSTIN is invalid"
},
"LglNm": {
"type": "string",
@@ -429,13 +434,15 @@
"type": "string",
"maxLength": 10,
"minLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]"
+ "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+ "validationMsg": "Expiry Date is invalid"
},
"WrDt": {
"type": "string",
"maxLength": 10,
"minLength": 10,
- "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]"
+ "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+ "validationMsg": "Warranty Date is invalid"
}
},
"required": ["Nm"]
diff --git a/erpnext/regional/india/einvoice.js b/erpnext/regional/india/einvoice.js
index 20461ea317b..4f98ffc88ab 100644
--- a/erpnext/regional/india/einvoice.js
+++ b/erpnext/regional/india/einvoice.js
@@ -7,27 +7,131 @@ erpnext.setup_einvoice_actions = (doctype) => {
|| !['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export'].includes(supply_type)) {
return;
}
+ // if (frm.doc.docstatus == 0 && !frm.doc.irn && !frm.doc.__unsaved) {
+ // frm.add_custom_button(
+ // "Generate IRN",
+ // () => {
+ // frappe.call({
+ // method: 'erpnext.regional.india.e_invoice_utils.generate_irn',
+ // args: { doctype: frm.doc.doctype, name: frm.doc.name },
+ // freeze: true,
+ // callback: (res) => {
+ // console.log(res.message);
+ // frm.set_value('irn', res.message['Irn']);
+ // frm.set_value('signed_einvoice', JSON.stringify(res.message['DecryptedSignedInvoice']));
+ // frm.set_value('signed_qr_code', JSON.stringify(res.message['DecryptedSignedQRCode']));
+
+ // if (res.message['EwbNo']) frm.set_value('ewaybill', res.message['EwbNo']);
+ // frm.save();
+ // }
+ // })
+ // }
+ // )
+ // }
+
+ // if (frm.doc.docstatus == 1 && frm.doc.irn && !frm.doc.irn_cancelled) {
+ // frm.add_custom_button(
+ // "Cancel IRN",
+ // () => {
+ // const d = new frappe.ui.Dialog({
+ // title: __('Cancel IRN'),
+ // fields: [
+ // { "label" : "Reason", "fieldname": "reason", "fieldtype": "Select", "reqd": 1, "default": "1-Duplicate",
+ // "options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"] },
+ // { "label": "Remark", "fieldname": "remark", "fieldtype": "Data", "reqd": 1 }
+ // ],
+ // primary_action: function() {
+ // const data = d.get_values();
+ // frappe.call({
+ // method: 'erpnext.regional.india.e_invoice_utils.cancel_irn',
+ // args: { irn: frm.doc.irn, reason: data.reason.split('-')[0], remark: data.remark },
+ // freeze: true,
+ // callback: () => {
+ // frm.set_value('irn_cancelled', 1);
+ // frm.save("Update");
+ // d.hide()
+ // },
+ // error: () => d.hide()
+ // })
+ // },
+ // primary_action_label: __('Submit')
+ // });
+ // d.show();
+ // }
+ // )
+ // }
+
+ // if (frm.doc.docstatus == 1 && frm.doc.irn && !frm.doc.irn_cancelled && !frm.doc.eway_bill_cancelled) {
+ // frm.add_custom_button(
+ // "Cancel E-Way Bill",
+ // () => {
+ // const d = new frappe.ui.Dialog({
+ // title: __('Cancel E-Way Bill'),
+ // fields: [
+ // { "label" : "Reason", "fieldname": "reason", "fieldtype": "Select", "reqd": 1, "default": "1-Duplicate",
+ // "options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"] },
+ // { "label": "Remark", "fieldname": "remark", "fieldtype": "Data", "reqd": 1 }
+ // ],
+ // primary_action: function() {
+ // const data = d.get_values();
+ // frappe.call({
+ // method: 'erpnext.regional.india.e_invoice_utils.cancel_eway_bill',
+ // args: { eway_bill: frm.doc.ewaybill, reason: data.reason.split('-')[0], remark: data.remark },
+ // freeze: true,
+ // callback: () => {
+ // frm.set_value('eway_bill_cancelled', 1);
+ // frm.save("Update");
+ // d.hide()
+ // },
+ // error: () => d.hide()
+ // })
+ // },
+ // primary_action_label: __('Submit')
+ // });
+ // d.show();
+ // }
+ // )
+ // }
if (frm.doc.docstatus == 0 && !frm.doc.irn && !frm.doc.__unsaved) {
frm.add_custom_button(
- "Generate IRN",
+ "Download E-Invoice",
() => {
frappe.call({
- method: 'erpnext.regional.india.e_invoice_utils.generate_irn',
+ method: 'erpnext.regional.india.e_invoice_utils.make_e_invoice',
args: { doctype: frm.doc.doctype, name: frm.doc.name },
freeze: true,
callback: (res) => {
- console.log(res.message);
- frm.set_value('irn', res.message['Irn']);
- frm.set_value('signed_einvoice', JSON.stringify(res.message['DecryptedSignedInvoice']));
- frm.set_value('signed_qr_code', JSON.stringify(res.message['DecryptedSignedQRCode']));
-
- if (res.message['EwbNo']) frm.set_value('ewaybill', res.message['EwbNo']);
- frm.save();
+ if (!res.exc) {
+ const args = {
+ cmd: 'erpnext.regional.india.e_invoice_utils.download_einvoice',
+ einvoice: res.message.einvoice,
+ name: frm.doc.name
+ };
+ open_url_post(frappe.request.url, args);
+ }
}
})
}
- )
+ );
+ }
+ if (frm.doc.docstatus == 0 && !frm.doc.irn && !frm.doc.__unsaved) {
+ frm.add_custom_button(
+ "Upload Signed E-Invoice",
+ () => {
+ new frappe.ui.FileUploader({
+ method: 'erpnext.regional.india.e_invoice_utils.upload_einvoice',
+ allow_multiple: 0,
+ doctype: frm.doc.doctype,
+ docname: frm.doc.name,
+ on_success: (attachment, r) => {
+ if (!r.exc) {
+ frm.reload_doc();
+ }
+ }
+ });
+ }
+ );
}
if (frm.doc.docstatus == 1 && frm.doc.irn && !frm.doc.irn_cancelled) {
frm.add_custom_button(
@@ -36,62 +140,30 @@ erpnext.setup_einvoice_actions = (doctype) => {
const d = new frappe.ui.Dialog({
title: __('Cancel IRN'),
fields: [
- { "label" : "Reason", "fieldname": "reason", "fieldtype": "Select", "reqd": 1, "default": "1-Duplicate",
- "options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"] },
- { "label": "Remark", "fieldname": "remark", "fieldtype": "Data", "reqd": 1 }
+ {
+ "label" : "Reason", "fieldname": "reason",
+ "fieldtype": "Select", "reqd": 1, "default": "1-Duplicate",
+ "options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"]
+ },
+ {
+ "label": "Remark", "fieldname": "remark", "fieldtype": "Data", "reqd": 1
+ }
],
primary_action: function() {
const data = d.get_values();
- frappe.call({
- method: 'erpnext.regional.india.e_invoice_utils.cancel_irn',
- args: { irn: frm.doc.irn, reason: data.reason.split('-')[0], remark: data.remark },
- freeze: true,
- callback: () => {
- frm.set_value('irn_cancelled', 1);
- frm.save("Update");
- d.hide()
- },
- error: () => d.hide()
- })
+ const args = {
+ cmd: 'erpnext.regional.india.e_invoice_utils.download_cancel_einvoice',
+ irn: frm.doc.irn, reason: data.reason.split('-')[0], remark: data.remark, name: frm.doc.name
+ };
+ open_url_post(frappe.request.url, args);
+ d.hide();
},
- primary_action_label: __('Submit')
+ primary_action_label: __('Download JSON')
});
d.show();
}
- )
- }
- if (frm.doc.docstatus == 1 && frm.doc.irn && !frm.doc.irn_cancelled && !frm.doc.eway_bill_cancelled) {
- frm.add_custom_button(
- "Cancel E-Way Bill",
- () => {
- const d = new frappe.ui.Dialog({
- title: __('Cancel E-Way Bill'),
- fields: [
- { "label" : "Reason", "fieldname": "reason", "fieldtype": "Select", "reqd": 1, "default": "1-Duplicate",
- "options": ["1-Duplicate", "2-Data Entry Error", "3-Order Cancelled", "4-Other"] },
- { "label": "Remark", "fieldname": "remark", "fieldtype": "Data", "reqd": 1 }
- ],
- primary_action: function() {
- const data = d.get_values();
- frappe.call({
- method: 'erpnext.regional.india.e_invoice_utils.cancel_eway_bill',
- args: { eway_bill: frm.doc.ewaybill, reason: data.reason.split('-')[0], remark: data.remark },
- freeze: true,
- callback: () => {
- frm.set_value('eway_bill_cancelled', 1);
- frm.save("Update");
- d.hide()
- },
- error: () => d.hide()
- })
- },
- primary_action_label: __('Submit')
- });
- d.show();
- }
- )
+ );
}
}
-
})
}
\ No newline at end of file