revert: "refactor!: drop e-invoicing integration from erpnext (#26940)"

This reverts commit c335962827e4927f7ada084e9ba4ab2db15e3eb6.
diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india.js b/erpnext/accounts/doctype/sales_invoice/regional/india.js
index 6336db1..f54bce8 100644
--- a/erpnext/accounts/doctype/sales_invoice/regional/india.js
+++ b/erpnext/accounts/doctype/sales_invoice/regional/india.js
@@ -1,6 +1,8 @@
 {% include "erpnext/regional/india/taxes.js" %}
+{% include "erpnext/regional/india/e_invoice/einvoice.js" %}
 
 erpnext.setup_auto_gst_taxation('Sales Invoice');
+erpnext.setup_einvoice_actions('Sales Invoice')
 
 frappe.ui.form.on("Sales Invoice", {
 	setup: function(frm) {
diff --git a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js
index d9d6634..f01325d 100644
--- a/erpnext/accounts/doctype/sales_invoice/regional/india_list.js
+++ b/erpnext/accounts/doctype/sales_invoice/regional/india_list.js
@@ -36,4 +36,139 @@
 	};
 
 	list_view.page.add_actions_menu_item(__('Generate E-Way Bill JSON'), action, false);
+
+	const generate_irns = () => {
+		const docnames = list_view.get_checked_items(true);
+		if (docnames && docnames.length) {
+			frappe.call({
+				method: 'erpnext.regional.india.e_invoice.utils.generate_einvoices',
+				args: { docnames },
+				freeze: true,
+				freeze_message: __('Generating E-Invoices...')
+			});
+		} else {
+			frappe.msgprint({
+				message: __('Please select at least one sales invoice to generate IRN'),
+				title: __('No Invoice Selected'),
+				indicator: 'red'
+			});
+		}
+	};
+
+	const cancel_irns = () => {
+		const docnames = list_view.get_checked_items(true);
+
+		const 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
+			}
+		];
+
+		const d = new frappe.ui.Dialog({
+			title: __("Cancel IRN"),
+			fields: fields,
+			primary_action: function() {
+				const data = d.get_values();
+				frappe.call({
+					method: 'erpnext.regional.india.e_invoice.utils.cancel_irns',
+					args: {
+						doctype: list_view.doctype,
+						docnames,
+						reason: data.reason.split('-')[0],
+						remark: data.remark
+					},
+					freeze: true,
+					freeze_message: __('Cancelling E-Invoices...'),
+				});
+				d.hide();
+			},
+			primary_action_label: __('Submit')
+		});
+		d.show();
+	};
+
+	let einvoicing_enabled = false;
+	frappe.db.get_single_value("E Invoice Settings", "enable").then(enabled => {
+		einvoicing_enabled = enabled;
+	});
+
+	list_view.$result.on("change", "input[type=checkbox]", () => {
+		if (einvoicing_enabled) {
+			const docnames = list_view.get_checked_items(true);
+			// show/hide e-invoicing actions when no sales invoices are checked
+			if (docnames && docnames.length) {
+				// prevent adding actions twice if e-invoicing action group already exists
+				if (list_view.page.get_inner_group_button(__('E-Invoicing')).length == 0) {
+					list_view.page.add_inner_button(__('Generate IRNs'), generate_irns, __('E-Invoicing'));
+					list_view.page.add_inner_button(__('Cancel IRNs'), cancel_irns, __('E-Invoicing'));
+				}
+			} else {
+				list_view.page.remove_inner_button(__('Generate IRNs'), __('E-Invoicing'));
+				list_view.page.remove_inner_button(__('Cancel IRNs'), __('E-Invoicing'));
+			}
+		}
+	});
+
+	frappe.realtime.on("bulk_einvoice_generation_complete", (data) => {
+		const { failures, user, invoices } = data;
+
+		if (invoices.length != failures.length) {
+			frappe.msgprint({
+				message: __('{0} e-invoices generated successfully', [invoices.length]),
+				title: __('Bulk E-Invoice Generation Complete'),
+				indicator: 'orange'
+			});
+		}
+
+		if (failures && failures.length && user == frappe.session.user) {
+			let message = `
+				Failed to generate IRNs for following ${failures.length} sales invoices:
+				<ul style="padding-left: 20px; padding-top: 5px;">
+					${failures.map(d => `<li>${d.docname}</li>`).join('')}
+				</ul>
+			`;
+			frappe.msgprint({
+				message: message,
+				title: __('Bulk E-Invoice Generation Complete'),
+				indicator: 'orange'
+			});
+		}
+	});
+
+	frappe.realtime.on("bulk_einvoice_cancellation_complete", (data) => {
+		const { failures, user, invoices } = data;
+
+		if (invoices.length != failures.length) {
+			frappe.msgprint({
+				message: __('{0} e-invoices cancelled successfully', [invoices.length]),
+				title: __('Bulk E-Invoice Cancellation Complete'),
+				indicator: 'orange'
+			});
+		}
+
+		if (failures && failures.length && user == frappe.session.user) {
+			let message = `
+				Failed to cancel IRNs for following ${failures.length} sales invoices:
+				<ul style="padding-left: 20px; padding-top: 5px;">
+					${failures.map(d => `<li>${d.docname}</li>`).join('')}
+				</ul>
+			`;
+			frappe.msgprint({
+				message: message,
+				title: __('Bulk E-Invoice Cancellation Complete'),
+				indicator: 'orange'
+			});
+		}
+	});
 };
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index f04e7ea..bc44358 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -294,6 +294,8 @@
 
 	def before_cancel(self):
 		self.check_if_consolidated_invoice()
+
+		super(SalesInvoice, self).before_cancel()
 		self.update_time_sheet(None)
 
 	def on_cancel(self):
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 55e3853..941061f 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -2100,6 +2100,54 @@
 		self.assertEqual(data['billLists'][0]['actualFromStateCode'],7)
 		self.assertEqual(data['billLists'][0]['fromStateCode'],27)
 
+	def test_einvoice_submission_without_irn(self):
+		# init
+		einvoice_settings = frappe.get_doc('E Invoice Settings')
+		einvoice_settings.enable = 1
+		einvoice_settings.applicable_from = nowdate()
+		einvoice_settings.append('credentials', {
+			'company': '_Test Company',
+			'gstin': '27AAECE4835E1ZR',
+			'username': 'test',
+			'password': 'test'
+		})
+		einvoice_settings.save()
+
+		country = frappe.flags.country
+		frappe.flags.country = 'India'
+
+		si = make_sales_invoice_for_ewaybill()
+		self.assertRaises(frappe.ValidationError, si.submit)
+
+		si.irn = 'test_irn'
+		si.submit()
+
+		# reset
+		einvoice_settings = frappe.get_doc('E Invoice Settings')
+		einvoice_settings.enable = 0
+		frappe.flags.country = country
+
+	def test_einvoice_json(self):
+		from erpnext.regional.india.e_invoice.utils import make_einvoice, validate_totals
+
+		si = get_sales_invoice_for_e_invoice()
+		si.discount_amount = 100
+		si.save()
+
+		einvoice = make_einvoice(si)
+		self.assertTrue(einvoice['EwbDtls'])
+		validate_totals(einvoice)
+
+		si.apply_discount_on = 'Net Total'
+		si.save()
+		einvoice = make_einvoice(si)
+		validate_totals(einvoice)
+
+		[d.set('included_in_print_rate', 1) for d in si.taxes]
+		si.save()
+		einvoice = make_einvoice(si)
+		validate_totals(einvoice)
+
 	def test_item_tax_net_range(self):
 		item = create_item("T Shirt")
 
diff --git a/erpnext/patches/v14_0/__init__.py b/erpnext/accounts/print_format/gst_e_invoice/__init__.py
similarity index 100%
copy from erpnext/patches/v14_0/__init__.py
copy to erpnext/accounts/print_format/gst_e_invoice/__init__.py
diff --git a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
new file mode 100644
index 0000000..7643eca
--- /dev/null
+++ b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.html
@@ -0,0 +1,162 @@
+{%- from "templates/print_formats/standard_macros.html" import add_header, render_field, print_value -%}
+{%- set einvoice = json.loads(doc.signed_einvoice) -%}
+
+<div class="page-break">
+	<div {% if print_settings.repeat_header_footer %} id="header-html" class="hidden-pdf" {% endif %}>
+		{% if letter_head and not no_letterhead %}
+			<div class="letter-head">{{ letter_head }}</div>
+		{% endif %}
+		<div class="print-heading">
+			<h2>E Invoice<br><small>{{ doc.name }}</small></h2>
+		</div>
+	</div>
+	{% if print_settings.repeat_header_footer %}
+	<div id="footer-html" class="visible-pdf">
+		{% if not no_letterhead and footer %}
+		<div class="letter-head-footer">
+			{{ footer }}
+		</div>
+		{% endif %}
+		<p class="text-center small page-number visible-pdf">
+			{{ _("Page {0} of {1}").format('<span class="page"></span>', '<span class="topage"></span>') }}
+		</p>
+	</div>
+	{% endif %}
+	<h5 class="font-bold" style="margin-top: 0px;">1. Transaction Details</h5>
+	<div class="row section-break" style="border-bottom: 1px solid #d1d8dd; padding-bottom: 10px;">
+		<div class="col-xs-8 column-break">
+			<div class="row data-field">
+				<div class="col-xs-4"><label>IRN</label></div>
+				<div class="col-xs-8 value">{{ einvoice.Irn }}</div>
+			</div>
+			<div class="row data-field">
+				<div class="col-xs-4"><label>Ack. No</label></div>
+				<div class="col-xs-8 value">{{ einvoice.AckNo }}</div>
+			</div>
+			<div class="row data-field">
+				<div class="col-xs-4"><label>Ack. Date</label></div>
+				<div class="col-xs-8 value">{{ frappe.utils.format_datetime(einvoice.AckDt, "dd/MM/yyyy hh:mm:ss") }}</div>
+			</div>
+			<div class="row data-field">
+				<div class="col-xs-4"><label>Category</label></div>
+				<div class="col-xs-8 value">{{ einvoice.TranDtls.SupTyp }}</div>
+			</div>
+			<div class="row data-field">
+				<div class="col-xs-4"><label>Document Type</label></div>
+				<div class="col-xs-8 value">{{ einvoice.DocDtls.Typ }}</div>
+			</div>
+			<div class="row data-field">
+				<div class="col-xs-4"><label>Document No</label></div>
+				<div class="col-xs-8 value">{{ einvoice.DocDtls.No }}</div>
+			</div>
+		</div>
+		<div class="col-xs-4 column-break">
+			<img src="{{ doc.qrcode_image }}" width="175px" style="float: right;">
+		</div>
+	</div>
+	<h5 class="font-bold" style="margin-top: 15px; margin-bottom: 10px;">2. Party Details</h5>
+	<div class="row section-break" style="border-bottom: 1px solid #d1d8dd; padding-bottom: 10px;">
+		{%- set seller = einvoice.SellerDtls -%}
+		<div class="col-xs-6 column-break">
+			<h5 style="margin-bottom: 5px;">Seller</h5>
+			<p>{{ seller.Gstin }}</p>
+			<p>{{ seller.LglNm }}</p>
+			<p>{{ seller.Addr1 }}</p>
+			{%- if seller.Addr2 -%} <p>{{ seller.Addr2 }}</p> {% endif %}
+			<p>{{ seller.Loc }}</p>
+			<p>{{ frappe.db.get_value("Address", doc.company_address, "gst_state") }} - {{ seller.Pin }}</p>
+
+			{%- if einvoice.ShipDtls -%}
+				{%- set shipping = einvoice.ShipDtls -%}
+				<h5 style="margin-bottom: 5px;">Shipping</h5>
+				<p>{{ shipping.Gstin }}</p>
+				<p>{{ shipping.LglNm }}</p>
+				<p>{{ shipping.Addr1 }}</p>
+				{%- if shipping.Addr2 -%} <p>{{ shipping.Addr2 }}</p> {% endif %}
+				<p>{{ shipping.Loc }}</p>
+				<p>{{ frappe.db.get_value("Address", doc.shipping_address_name, "gst_state") }} - {{ shipping.Pin }}</p>
+			{% endif %}
+		</div>
+		{%- set buyer = einvoice.BuyerDtls -%}
+		<div class="col-xs-6 column-break">
+			<h5 style="margin-bottom: 5px;">Buyer</h5>
+			<p>{{ buyer.Gstin }}</p>
+			<p>{{ buyer.LglNm }}</p>
+			<p>{{ buyer.Addr1 }}</p>
+			{%- if buyer.Addr2 -%} <p>{{ buyer.Addr2 }}</p> {% endif %}
+			<p>{{ buyer.Loc }}</p>
+			<p>{{ frappe.db.get_value("Address", doc.customer_address, "gst_state") }} - {{ buyer.Pin }}</p>
+		</div>
+	</div>
+	<div style="overflow-x: auto;">
+		<h5 class="font-bold" style="margin-top: 15px; margin-bottom: 10px;">3. Item Details</h5>
+		<table class="table table-bordered">
+			<thead>
+				<tr>
+					<th class="text-left" style="width: 3%;">Sr. No.</th>
+					<th class="text-left">Item</th>
+					<th class="text-left" style="width: 10%;">HSN Code</th>
+					<th class="text-left" style="width: 5%;">Qty</th>
+					<th class="text-left" style="width: 5%;">UOM</th>
+					<th class="text-left">Rate</th>
+					<th class="text-left" style="width: 5%;">Discount</th>
+					<th class="text-left">Taxable Amount</th>
+					<th class="text-left" style="width: 7%;">Tax Rate</th>
+					<th class="text-left" style="width: 5%;">Other Charges</th>
+					<th class="text-left">Total</th>
+				</tr>
+			</thead>
+			<tbody>
+				{% for item in einvoice.ItemList %}
+					<tr>
+						<td class="text-left" style="width: 3%;">{{ item.SlNo }}</td>
+						<td class="text-left">{{ item.PrdDesc }}</td>
+						<td class="text-left" style="width: 10%;">{{ item.HsnCd }}</td>
+						<td class="text-right" style="width: 5%;">{{ item.Qty }}</td>
+						<td class="text-left" style="width: 5%;">{{ item.Unit }}</td>
+						<td class="text-right">{{ frappe.utils.fmt_money(item.UnitPrice, None, "INR") }}</td>
+						<td class="text-right" style="width: 5%;">{{ frappe.utils.fmt_money(item.Discount, None, "INR") }}</td>
+						<td class="text-right">{{ frappe.utils.fmt_money(item.AssAmt, None, "INR") }}</td>
+						<td class="text-right" style="width: 7%;">{{ item.GstRt + item.CesRt }} %</td>
+						<td class="text-right" style="width: 5%;">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
+						<td class="text-right">{{ frappe.utils.fmt_money(item.TotItemVal, None, "INR") }}</td>
+					</tr>
+				{% endfor %}
+			</tbody>
+		</table>
+	</div>
+	<div style="overflow-x: auto;">
+		<h5 class="font-bold" style="margin-bottom: 0px;">4. Value Details</h5>
+		<table class="table table-bordered">
+			<thead>
+				<tr>
+					<th class="text-left">Taxable Amount</th>
+					<th class="text-left">CGST</th>
+					<th class="text-left"">SGST</th>
+					<th class="text-left">IGST</th>
+					<th class="text-left">CESS</th>
+					<th class="text-left" style="width: 10%;">State CESS</th>
+					<th class="text-left">Discount</th>
+					<th class="text-left" style="width: 10%;">Other Charges</th>
+					<th class="text-left" style="width: 10%;">Round Off</th>
+					<th class="text-left">Total Value</th>
+				</tr>
+			</thead>
+			<tbody>
+				{%- set value_details = einvoice.ValDtls -%}
+				<tr>
+					<td class="text-right">{{ frappe.utils.fmt_money(value_details.AssVal, None, "INR") }}</td>
+					<td class="text-right">{{ frappe.utils.fmt_money(value_details.CgstVal, None, "INR") }}</td>
+					<td class="text-right">{{ frappe.utils.fmt_money(value_details.SgstVal, None, "INR") }}</td>
+					<td class="text-right">{{ frappe.utils.fmt_money(value_details.IgstVal, None, "INR") }}</td>
+					<td class="text-right">{{ frappe.utils.fmt_money(value_details.CesVal, None, "INR") }}</td>
+					<td class="text-right">{{ frappe.utils.fmt_money(0, None, "INR") }}</td>
+					<td class="text-right">{{ frappe.utils.fmt_money(value_details.Discount, None, "INR") }}</td>
+					<td class="text-right">{{ frappe.utils.fmt_money(value_details.OthChrg, None, "INR") }}</td>
+					<td class="text-right">{{ frappe.utils.fmt_money(value_details.RndOffAmt, None, "INR") }}</td>
+					<td class="text-right">{{ frappe.utils.fmt_money(value_details.TotInvVal, None, "INR") }}</td>
+				</tr>
+			</tbody>
+		</table>
+	</div>
+</div>
diff --git a/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.json b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.json
new file mode 100644
index 0000000..1001199
--- /dev/null
+++ b/erpnext/accounts/print_format/gst_e_invoice/gst_e_invoice.json
@@ -0,0 +1,24 @@
+{
+ "align_labels_right": 1,
+ "creation": "2020-10-10 18:01:21.032914",
+ "custom_format": 0,
+ "default_print_language": "en-US",
+ "disabled": 1,
+ "doc_type": "Sales Invoice",
+ "docstatus": 0,
+ "doctype": "Print Format",
+ "font": "Default",
+ "html": "",
+ "idx": 0,
+ "line_breaks": 1,
+ "modified": "2020-10-23 19:54:40.634936",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "GST E-Invoice",
+ "owner": "Administrator",
+ "print_format_builder": 0,
+ "print_format_type": "Jinja",
+ "raw_printing": 0,
+ "show_section_headings": 1,
+ "standard": "Yes"
+}
\ No newline at end of file
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 4775f56..bab16a4 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -167,9 +167,14 @@
 
 		validate_regional(self)
 
+		validate_einvoice_fields(self)
+
 		if self.doctype != 'Material Request':
 			apply_pricing_rule_on_transaction(self)
 
+	def before_cancel(self):
+		validate_einvoice_fields(self)
+
 	def on_trash(self):
 		# delete sl and gl entries on deletion of transaction
 		if frappe.db.get_single_value('Accounts Settings', 'delete_linked_ledger_entries'):
@@ -2151,3 +2156,7 @@
 @erpnext.allow_regional
 def validate_regional(doc):
 	pass
+
+@erpnext.allow_regional
+def validate_einvoice_fields(doc):
+	pass
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index d172da3..04f5793 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -443,6 +443,7 @@
 		'erpnext.controllers.taxes_and_totals.get_regional_round_off_accounts': 'erpnext.regional.india.utils.get_regional_round_off_accounts',
 		'erpnext.hr.utils.calculate_annual_eligible_hra_exemption': 'erpnext.regional.india.utils.calculate_annual_eligible_hra_exemption',
 		'erpnext.hr.utils.calculate_hra_exemption_for_period': 'erpnext.regional.india.utils.calculate_hra_exemption_for_period',
+		'erpnext.controllers.accounts_controller.validate_einvoice_fields': 'erpnext.regional.india.e_invoice.utils.validate_einvoice_fields',
 		'erpnext.assets.doctype.asset.asset.get_depreciation_amount': 'erpnext.regional.india.utils.get_depreciation_amount',
 		'erpnext.stock.doctype.item.item.set_item_tax_from_hsn_code': 'erpnext.regional.india.utils.set_item_tax_from_hsn_code'
 	},
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 9f7e733..41cb483 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -203,6 +203,7 @@
 erpnext.patches.v13_0.move_doctype_reports_and_notification_from_hr_to_payroll #22-06-2020
 erpnext.patches.v13_0.move_payroll_setting_separately_from_hr_settings #22-06-2020
 erpnext.patches.v12_0.create_itc_reversal_custom_fields
+execute:frappe.reload_doc("regional", "doctype", "e_invoice_settings")
 erpnext.patches.v13_0.check_is_income_tax_component #22-06-2020
 erpnext.patches.v13_0.loyalty_points_entry_for_pos_invoice #22-07-2020
 erpnext.patches.v12_0.add_taxjar_integration_field
@@ -223,6 +224,7 @@
 erpnext.patches.v13_0.set_app_name
 erpnext.patches.v13_0.print_uom_after_quantity_patch
 erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account
+erpnext.patches.v12_0.setup_einvoice_fields #2020-12-02
 erpnext.patches.v13_0.updates_for_multi_currency_payroll
 erpnext.patches.v13_0.update_reason_for_resignation_in_employee
 execute:frappe.delete_doc("Report", "Quoted Item Comparison")
@@ -241,12 +243,15 @@
 erpnext.patches.v12_0.add_state_code_for_ladakh
 erpnext.patches.v13_0.item_reposting_for_incorrect_sl_and_gl
 erpnext.patches.v13_0.delete_old_bank_reconciliation_doctypes
-erpnext.patches.v13_0.update_vehicle_no_reqd_condition
+erpnext.patches.v12_0.update_vehicle_no_reqd_condition
+erpnext.patches.v12_0.add_einvoice_status_field #2021-03-17
+erpnext.patches.v12_0.add_einvoice_summary_report_permissions
 erpnext.patches.v13_0.setup_fields_for_80g_certificate_and_donation
 erpnext.patches.v13_0.rename_membership_settings_to_non_profit_settings
 erpnext.patches.v13_0.setup_gratuity_rule_for_india_and_uae
 erpnext.patches.v13_0.setup_uae_vat_fields
 execute:frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
+erpnext.patches.v12_0.add_company_link_to_einvoice_settings
 erpnext.patches.v12_0.create_taxable_value_field
 erpnext.patches.v12_0.add_gst_category_in_delivery_note
 erpnext.patches.v12_0.purchase_receipt_status
@@ -255,6 +260,7 @@
 erpnext.patches.v13_0.make_non_standard_user_type #13-04-2021 #17-01-2022
 erpnext.patches.v13_0.update_shipment_status
 erpnext.patches.v13_0.remove_attribute_field_from_item_variant_setting
+erpnext.patches.v12_0.add_ewaybill_validity_field
 erpnext.patches.v13_0.germany_make_custom_fields
 erpnext.patches.v13_0.germany_fill_debtor_creditor_number
 erpnext.patches.v13_0.set_pos_closing_as_failed
@@ -271,6 +277,7 @@
 erpnext.patches.v13_0.add_missing_fg_item_for_stock_entry
 erpnext.patches.v13_0.update_subscription_status_in_memberships
 erpnext.patches.v13_0.update_amt_in_work_order_required_items
+erpnext.patches.v12_0.show_einvoice_irn_cancelled_field
 erpnext.patches.v13_0.delete_orphaned_tables
 erpnext.patches.v13_0.update_export_type_for_gst #2021-08-16
 erpnext.patches.v13_0.update_tds_check_field #3
@@ -327,10 +334,10 @@
 [post_model_sync]
 erpnext.patches.v14_0.rename_ongoing_status_in_sla_documents
 erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template
-erpnext.patches.v14_0.delete_einvoicing_doctypes
 erpnext.patches.v14_0.delete_shopify_doctypes
 erpnext.patches.v14_0.delete_hub_doctypes
 erpnext.patches.v14_0.delete_hospitality_doctypes # 20-01-2022
 erpnext.patches.v14_0.delete_agriculture_doctypes
 erpnext.patches.v14_0.rearrange_company_fields
 erpnext.patches.v14_0.update_leave_notification_template
+erpnext.patches.v14_0.restore_einvoice_fields
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py b/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py
new file mode 100644
index 0000000..e498b67
--- /dev/null
+++ b/erpnext/patches/v12_0/add_company_link_to_einvoice_settings.py
@@ -0,0 +1,18 @@
+from __future__ import unicode_literals
+
+import frappe
+
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company or not frappe.db.count('E Invoice User'):
+		return
+
+	frappe.reload_doc("regional", "doctype", "e_invoice_user")
+	for creds in frappe.db.get_all('E Invoice User', fields=['name', 'gstin']):
+		company_name = frappe.db.sql("""
+			select dl.link_name from `tabAddress` a, `tabDynamic Link` dl
+			where a.gstin = %s and dl.parent = a.name and dl.link_doctype = 'Company'
+		""", (creds.get('gstin')))
+		if company_name and len(company_name) > 0:
+			frappe.db.set_value('E Invoice User', creds.get('name'), 'company', company_name[0][0])
diff --git a/erpnext/patches/v12_0/add_einvoice_status_field.py b/erpnext/patches/v12_0/add_einvoice_status_field.py
new file mode 100644
index 0000000..aeff9ca
--- /dev/null
+++ b/erpnext/patches/v12_0/add_einvoice_status_field.py
@@ -0,0 +1,72 @@
+from __future__ import unicode_literals
+
+import json
+
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	# move hidden einvoice fields to a different section
+	custom_fields = {
+		'Sales Invoice': [
+			dict(fieldname='einvoice_section', label='E-Invoice Fields', fieldtype='Section Break', insert_after='gst_vehicle_type',
+			print_hide=1, hidden=1),
+
+			dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='einvoice_section',
+				no_copy=1, print_hide=1),
+
+			dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1),
+
+			dict(fieldname='irn_cancel_date', label='Cancel Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_date',
+				no_copy=1, print_hide=1),
+
+			dict(fieldname='signed_einvoice', label='Signed E-Invoice', fieldtype='Code', options='JSON', hidden=1, insert_after='irn_cancel_date',
+				no_copy=1, print_hide=1, read_only=1),
+
+			dict(fieldname='signed_qr_code', label='Signed QRCode', fieldtype='Code', options='JSON', hidden=1, insert_after='signed_einvoice',
+				no_copy=1, print_hide=1, read_only=1),
+
+			dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, insert_after='signed_qr_code',
+				no_copy=1, print_hide=1, read_only=1),
+
+			dict(fieldname='einvoice_status', label='E-Invoice Status', fieldtype='Select', insert_after='qrcode_image',
+				options='\nPending\nGenerated\nCancelled\nFailed', default=None, hidden=1, no_copy=1, print_hide=1, read_only=1),
+
+			dict(fieldname='failure_description', label='E-Invoice Failure Description', fieldtype='Code', options='JSON',
+				hidden=1, insert_after='einvoice_status', no_copy=1, print_hide=1, read_only=1)
+		]
+	}
+	create_custom_fields(custom_fields, update=True)
+
+	if frappe.db.exists('E Invoice Settings') and frappe.db.get_single_value('E Invoice Settings', 'enable'):
+		frappe.db.sql('''
+			UPDATE `tabSales Invoice` SET einvoice_status = 'Pending'
+			WHERE
+				posting_date >= '2021-04-01'
+				AND ifnull(irn, '') = ''
+				AND ifnull(`billing_address_gstin`, '') != ifnull(`company_gstin`, '')
+				AND ifnull(gst_category, '') in ('Registered Regular', 'SEZ', 'Overseas', 'Deemed Export')
+		''')
+
+		# set appropriate statuses
+		frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Generated'
+			WHERE ifnull(irn, '') != '' AND ifnull(irn_cancelled, 0) = 0''')
+
+		frappe.db.sql('''UPDATE `tabSales Invoice` SET einvoice_status = 'Cancelled'
+			WHERE ifnull(irn_cancelled, 0) = 1''')
+
+	# set correct acknowledgement in e-invoices
+	einvoices = frappe.get_all('Sales Invoice', {'irn': ['is', 'set']}, ['name', 'signed_einvoice'])
+
+	if einvoices:
+		for inv in einvoices:
+			signed_einvoice = inv.get('signed_einvoice')
+			if signed_einvoice:
+				signed_einvoice = json.loads(signed_einvoice)
+				frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_no', signed_einvoice.get('AckNo'), update_modified=False)
+				frappe.db.set_value('Sales Invoice', inv.get('name'), 'ack_date', signed_einvoice.get('AckDt'), update_modified=False)
diff --git a/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py b/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py
new file mode 100644
index 0000000..e837786
--- /dev/null
+++ b/erpnext/patches/v12_0/add_einvoice_summary_report_permissions.py
@@ -0,0 +1,20 @@
+from __future__ import unicode_literals
+
+import frappe
+
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	if frappe.db.exists('Report', 'E-Invoice Summary') and \
+		not frappe.db.get_value('Custom Role', dict(report='E-Invoice Summary')):
+		frappe.get_doc(dict(
+			doctype='Custom Role',
+			report='E-Invoice Summary',
+			roles= [
+				dict(role='Accounts User'),
+				dict(role='Accounts Manager')
+			]
+		)).insert()
diff --git a/erpnext/patches/v12_0/add_ewaybill_validity_field.py b/erpnext/patches/v12_0/add_ewaybill_validity_field.py
new file mode 100644
index 0000000..247140d
--- /dev/null
+++ b/erpnext/patches/v12_0/add_ewaybill_validity_field.py
@@ -0,0 +1,18 @@
+from __future__ import unicode_literals
+
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	custom_fields = {
+		'Sales Invoice': [
+			dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1,
+				depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill')
+		]
+	}
+	create_custom_fields(custom_fields, update=True)
diff --git a/erpnext/patches/v12_0/setup_einvoice_fields.py b/erpnext/patches/v12_0/setup_einvoice_fields.py
new file mode 100644
index 0000000..c17666a
--- /dev/null
+++ b/erpnext/patches/v12_0/setup_einvoice_fields.py
@@ -0,0 +1,59 @@
+from __future__ import unicode_literals
+
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+from erpnext.regional.india.setup import add_permissions, add_print_formats
+
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	frappe.reload_doc("custom", "doctype", "custom_field")
+	frappe.reload_doc("regional", "doctype", "e_invoice_settings")
+	custom_fields = {
+		'Sales Invoice': [
+			dict(fieldname='irn', label='IRN', fieldtype='Data', read_only=1, insert_after='customer', no_copy=1, print_hide=1,
+				depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'),
+
+			dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='irn', no_copy=1, print_hide=1),
+
+			dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1),
+
+			dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
+				depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
+
+			dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
+				depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
+
+			dict(fieldname='signed_einvoice', fieldtype='Code', options='JSON', hidden=1, no_copy=1, print_hide=1, read_only=1),
+
+			dict(fieldname='signed_qr_code', fieldtype='Code', options='JSON', hidden=1, no_copy=1, print_hide=1, read_only=1),
+
+			dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, no_copy=1, print_hide=1, read_only=1)
+		]
+	}
+	create_custom_fields(custom_fields, update=True)
+	add_permissions()
+	add_print_formats()
+
+	einvoice_cond = 'in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category)'
+	t = {
+		'mode_of_transport': [{'default': None}],
+		'distance': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.transporter'}],
+		'gst_vehicle_type': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.mode_of_transport == "Road"'}],
+		'lr_date': [{'mandatory_depends_on': f'eval:{einvoice_cond} && in_list(["Air", "Ship", "Rail"], doc.mode_of_transport)'}],
+		'lr_no': [{'mandatory_depends_on': f'eval:{einvoice_cond} && in_list(["Air", "Ship", "Rail"], doc.mode_of_transport)'}],
+		'vehicle_no': [{'mandatory_depends_on': f'eval:{einvoice_cond} && doc.mode_of_transport == "Road"'}],
+		'ewaybill': [
+			{'read_only_depends_on': 'eval:doc.irn && doc.ewaybill'},
+			{'depends_on': 'eval:((doc.docstatus === 1 || doc.ewaybill) && doc.eway_bill_cancelled === 0)'}
+		]
+	}
+
+	for field, conditions in t.items():
+		for c in conditions:
+			[(prop, value)] = c.items()
+			frappe.db.set_value('Custom Field', { 'fieldname': field }, prop, value)
diff --git a/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py b/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py
new file mode 100644
index 0000000..3f90a03
--- /dev/null
+++ b/erpnext/patches/v12_0/show_einvoice_irn_cancelled_field.py
@@ -0,0 +1,14 @@
+from __future__ import unicode_literals
+
+import frappe
+
+
+def execute():
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	irn_cancelled_field = frappe.db.exists('Custom Field', {'dt': 'Sales Invoice', 'fieldname': 'irn_cancelled'})
+	if irn_cancelled_field:
+		frappe.db.set_value('Custom Field', irn_cancelled_field, 'depends_on', 'eval: doc.irn')
+		frappe.db.set_value('Custom Field', irn_cancelled_field, 'read_only', 0)
diff --git a/erpnext/patches/v13_0/einvoicing_deprecation_warning.py b/erpnext/patches/v13_0/einvoicing_deprecation_warning.py
deleted file mode 100644
index e123a55..0000000
--- a/erpnext/patches/v13_0/einvoicing_deprecation_warning.py
+++ /dev/null
@@ -1,9 +0,0 @@
-import click
-
-
-def execute():
-	click.secho(
-		"Indian E-Invoicing integration is moved to a separate app and will be removed from ERPNext in version-14.\n"
-		"Please install the app to continue using the integration: https://github.com/frappe/erpnext_gst_compliance",
-		fg="yellow",
-	)
diff --git a/erpnext/patches/v14_0/delete_einvoicing_doctypes.py b/erpnext/patches/v14_0/delete_einvoicing_doctypes.py
deleted file mode 100644
index a3a8149..0000000
--- a/erpnext/patches/v14_0/delete_einvoicing_doctypes.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import frappe
-
-
-def execute():
-	frappe.delete_doc('DocType', 'E Invoice Settings', ignore_missing=True)
-	frappe.delete_doc('DocType', 'E Invoice User', ignore_missing=True)
-	frappe.delete_doc('Report', 'E-Invoice Summary', ignore_missing=True)
-	frappe.delete_doc('Print Format', 'GST E-Invoice', ignore_missing=True)
-	frappe.delete_doc('Custom Field', 'Sales Invoice-eway_bill_cancelled', ignore_missing=True)
-	frappe.delete_doc('Custom Field', 'Sales Invoice-irn_cancelled', ignore_missing=True)
diff --git a/erpnext/patches/v14_0/restore_einvoice_fields.py b/erpnext/patches/v14_0/restore_einvoice_fields.py
new file mode 100644
index 0000000..c4431fb
--- /dev/null
+++ b/erpnext/patches/v14_0/restore_einvoice_fields.py
@@ -0,0 +1,24 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
+
+from erpnext.regional.india.setup import add_permissions, add_print_formats
+
+
+def execute():
+	# restores back the 2 custom fields that was deleted while removing e-invoicing from v14
+	company = frappe.get_all('Company', filters = {'country': 'India'})
+	if not company:
+		return
+
+	custom_fields = {
+		'Sales Invoice': [
+			dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
+				depends_on='eval:(doc.irn_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
+
+			dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
+				depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
+		]
+	}
+	create_custom_fields(custom_fields, update=True)
+	add_permissions()
+	add_print_formats()
diff --git a/erpnext/patches/v14_0/__init__.py b/erpnext/regional/doctype/e_invoice_request_log/__init__.py
similarity index 100%
copy from erpnext/patches/v14_0/__init__.py
copy to erpnext/regional/doctype/e_invoice_request_log/__init__.py
diff --git a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.js b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.js
new file mode 100644
index 0000000..7b7ba96
--- /dev/null
+++ b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('E Invoice Request Log', {
+	// refresh: function(frm) {
+
+	// }
+});
diff --git a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json
new file mode 100644
index 0000000..3034370
--- /dev/null
+++ b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.json
@@ -0,0 +1,102 @@
+{
+ "actions": [],
+ "autoname": "EINV-REQ-.#####",
+ "creation": "2020-12-08 12:54:08.175992",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "user",
+  "url",
+  "headers",
+  "response",
+  "column_break_7",
+  "timestamp",
+  "reference_invoice",
+  "data"
+ ],
+ "fields": [
+  {
+   "fieldname": "user",
+   "fieldtype": "Link",
+   "label": "User",
+   "options": "User"
+  },
+  {
+   "fieldname": "reference_invoice",
+   "fieldtype": "Data",
+   "label": "Reference Invoice"
+  },
+  {
+   "fieldname": "headers",
+   "fieldtype": "Code",
+   "label": "Headers",
+   "options": "JSON"
+  },
+  {
+   "fieldname": "data",
+   "fieldtype": "Code",
+   "label": "Data",
+   "options": "JSON"
+  },
+  {
+   "default": "Now",
+   "fieldname": "timestamp",
+   "fieldtype": "Datetime",
+   "label": "Timestamp"
+  },
+  {
+   "fieldname": "response",
+   "fieldtype": "Code",
+   "label": "Response",
+   "options": "JSON"
+  },
+  {
+   "fieldname": "url",
+   "fieldtype": "Data",
+   "label": "URL"
+  },
+  {
+   "fieldname": "column_break_7",
+   "fieldtype": "Column Break"
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2021-01-13 12:06:57.253111",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "E Invoice Request Log",
+ "owner": "Administrator",
+ "permissions": [
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "System Manager",
+   "share": 1
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts User",
+   "share": 1
+  },
+  {
+   "email": 1,
+   "export": 1,
+   "print": 1,
+   "read": 1,
+   "report": 1,
+   "role": "Accounts Manager",
+   "share": 1
+  }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC"
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.py b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.py
new file mode 100644
index 0000000..38fe308
--- /dev/null
+++ b/erpnext/regional/doctype/e_invoice_request_log/e_invoice_request_log.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+# import frappe
+from frappe.model.document import Document
+
+
+class EInvoiceRequestLog(Document):
+	pass
diff --git a/erpnext/regional/doctype/e_invoice_request_log/test_e_invoice_request_log.py b/erpnext/regional/doctype/e_invoice_request_log/test_e_invoice_request_log.py
new file mode 100644
index 0000000..091cc88e
--- /dev/null
+++ b/erpnext/regional/doctype/e_invoice_request_log/test_e_invoice_request_log.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+
+class TestEInvoiceRequestLog(unittest.TestCase):
+	pass
diff --git a/erpnext/patches/v14_0/__init__.py b/erpnext/regional/doctype/e_invoice_settings/__init__.py
similarity index 100%
copy from erpnext/patches/v14_0/__init__.py
copy to erpnext/regional/doctype/e_invoice_settings/__init__.py
diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js
new file mode 100644
index 0000000..54e4886
--- /dev/null
+++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.js
@@ -0,0 +1,11 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('E Invoice Settings', {
+	refresh(frm) {
+		const docs_link = 'https://docs.erpnext.com/docs/v13/user/manual/en/regional/india/setup-e-invoicing';
+		frm.dashboard.set_headline(
+			__("Read {0} for more information on E Invoicing features.", [`<a href='${docs_link}'>documentation</a>`])
+		);
+	}
+});
diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json
new file mode 100644
index 0000000..68ed339
--- /dev/null
+++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.json
@@ -0,0 +1,73 @@
+{
+ "actions": [],
+ "creation": "2020-09-24 16:23:16.235722",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "enable",
+  "section_break_2",
+  "sandbox_mode",
+  "applicable_from",
+  "credentials",
+  "auth_token",
+  "token_expiry"
+ ],
+ "fields": [
+  {
+   "default": "0",
+   "fieldname": "enable",
+   "fieldtype": "Check",
+   "label": "Enable"
+  },
+  {
+   "depends_on": "enable",
+   "fieldname": "section_break_2",
+   "fieldtype": "Section Break"
+  },
+  {
+   "fieldname": "auth_token",
+   "fieldtype": "Data",
+   "hidden": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "token_expiry",
+   "fieldtype": "Datetime",
+   "hidden": 1,
+   "read_only": 1
+  },
+  {
+   "fieldname": "credentials",
+   "fieldtype": "Table",
+   "label": "Credentials",
+   "mandatory_depends_on": "enable",
+   "options": "E Invoice User"
+  },
+  {
+   "default": "0",
+   "fieldname": "sandbox_mode",
+   "fieldtype": "Check",
+   "label": "Sandbox Mode"
+  },
+  {
+   "fieldname": "applicable_from",
+   "fieldtype": "Date",
+   "in_list_view": 1,
+   "label": "Applicable From",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2021-03-30 12:26:25.538294",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "E Invoice Settings",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py
new file mode 100644
index 0000000..70ec2ed
--- /dev/null
+++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py
@@ -0,0 +1,14 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+from __future__ import unicode_literals
+
+import frappe
+from frappe import _
+from frappe.model.document import Document
+
+
+class EInvoiceSettings(Document):
+	def validate(self):
+		if self.enable and not self.credentials:
+			frappe.throw(_('You must add atleast one credentials to be able to use E Invoicing.'))
diff --git a/erpnext/regional/doctype/e_invoice_settings/test_e_invoice_settings.py b/erpnext/regional/doctype/e_invoice_settings/test_e_invoice_settings.py
new file mode 100644
index 0000000..10770de
--- /dev/null
+++ b/erpnext/regional/doctype/e_invoice_settings/test_e_invoice_settings.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+
+class TestEInvoiceSettings(unittest.TestCase):
+	pass
diff --git a/erpnext/patches/v14_0/__init__.py b/erpnext/regional/doctype/e_invoice_user/__init__.py
similarity index 100%
copy from erpnext/patches/v14_0/__init__.py
copy to erpnext/regional/doctype/e_invoice_user/__init__.py
diff --git a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json
new file mode 100644
index 0000000..a65b1ca
--- /dev/null
+++ b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.json
@@ -0,0 +1,57 @@
+{
+ "actions": [],
+ "creation": "2020-12-22 15:02:46.229474",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+  "company",
+  "gstin",
+  "username",
+  "password"
+ ],
+ "fields": [
+  {
+   "fieldname": "gstin",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "GSTIN",
+   "reqd": 1
+  },
+  {
+   "fieldname": "username",
+   "fieldtype": "Data",
+   "in_list_view": 1,
+   "label": "Username",
+   "reqd": 1
+  },
+  {
+   "fieldname": "password",
+   "fieldtype": "Password",
+   "in_list_view": 1,
+   "label": "Password",
+   "reqd": 1
+  },
+  {
+   "fieldname": "company",
+   "fieldtype": "Link",
+   "in_list_view": 1,
+   "label": "Company",
+   "options": "Company",
+   "reqd": 1
+  }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2021-03-22 12:16:56.365616",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "E Invoice User",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/regional/doctype/e_invoice_user/e_invoice_user.py b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.py
new file mode 100644
index 0000000..a0fe399
--- /dev/null
+++ b/erpnext/regional/doctype/e_invoice_user/e_invoice_user.py
@@ -0,0 +1,12 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+# import frappe
+from frappe.model.document import Document
+
+
+class EInvoiceUser(Document):
+	pass
diff --git a/erpnext/patches/v14_0/__init__.py b/erpnext/regional/india/e_invoice/__init__.py
similarity index 100%
rename from erpnext/patches/v14_0/__init__.py
rename to erpnext/regional/india/e_invoice/__init__.py
diff --git a/erpnext/regional/india/e_invoice/einv_item_template.json b/erpnext/regional/india/e_invoice/einv_item_template.json
new file mode 100644
index 0000000..78e5651
--- /dev/null
+++ b/erpnext/regional/india/e_invoice/einv_item_template.json
@@ -0,0 +1,31 @@
+{{
+    "SlNo": "{item.sr_no}",
+    "PrdDesc": "{item.description}",
+    "IsServc": "{item.is_service_item}",
+    "HsnCd": "{item.gst_hsn_code}",
+    "Barcde": "{item.barcode}",
+    "Unit": "{item.uom}",
+    "Qty": "{item.qty}",
+    "FreeQty": "{item.free_qty}",
+    "UnitPrice": "{item.unit_rate}",
+    "TotAmt": "{item.gross_amount}",
+    "Discount": "{item.discount_amount}",
+    "AssAmt": "{item.taxable_value}",
+    "PrdSlNo": "{item.serial_no}",
+    "GstRt": "{item.tax_rate}",
+    "IgstAmt": "{item.igst_amount}",
+    "CgstAmt": "{item.cgst_amount}",
+    "SgstAmt": "{item.sgst_amount}",
+    "CesRt": "{item.cess_rate}",
+    "CesAmt": "{item.cess_amount}",
+    "CesNonAdvlAmt": "{item.cess_nadv_amount}",
+    "StateCesRt": "{item.state_cess_rate}",
+    "StateCesAmt": "{item.state_cess_amount}",
+    "StateCesNonAdvlAmt": "{item.state_cess_nadv_amount}",
+    "OthChrg": "{item.other_charges}",
+    "TotItemVal": "{item.total_value}",
+    "BchDtls": {{
+        "Nm": "{item.batch_no}",
+        "ExpDt": "{item.batch_expiry_date}"
+    }}
+}}
\ No newline at end of file
diff --git a/erpnext/regional/india/e_invoice/einv_template.json b/erpnext/regional/india/e_invoice/einv_template.json
new file mode 100644
index 0000000..60f490d
--- /dev/null
+++ b/erpnext/regional/india/e_invoice/einv_template.json
@@ -0,0 +1,110 @@
+{{
+    "Version": "1.1",
+    "TranDtls": {{
+        "TaxSch": "{transaction_details.tax_scheme}",
+        "SupTyp": "{transaction_details.supply_type}",
+        "RegRev": "{transaction_details.reverse_charge}",
+        "EcmGstin": "{transaction_details.ecom_gstin}",
+        "IgstOnIntra": "{transaction_details.igst_on_intra}"
+    }},
+    "DocDtls": {{
+        "Typ": "{doc_details.invoice_type}",
+        "No": "{doc_details.invoice_name}",
+        "Dt": "{doc_details.invoice_date}"
+    }},
+    "SellerDtls": {{
+        "Gstin": "{seller_details.gstin}",
+        "LglNm": "{seller_details.legal_name}",
+        "TrdNm": "{seller_details.trade_name}",
+        "Loc": "{seller_details.location}",
+        "Pin": "{seller_details.pincode}",
+        "Stcd": "{seller_details.state_code}",
+        "Addr1": "{seller_details.address_line1}",
+        "Addr2": "{seller_details.address_line2}",
+        "Ph": "{seller_details.phone}",
+        "Em": "{seller_details.email}"
+    }},
+    "BuyerDtls": {{
+        "Gstin": "{buyer_details.gstin}",
+        "LglNm": "{buyer_details.legal_name}",
+        "TrdNm": "{buyer_details.trade_name}",
+        "Addr1": "{buyer_details.address_line1}",
+        "Addr2": "{buyer_details.address_line2}",
+        "Loc": "{buyer_details.location}",
+        "Pin": "{buyer_details.pincode}",
+        "Stcd": "{buyer_details.state_code}",
+        "Ph": "{buyer_details.phone}",
+        "Em": "{buyer_details.email}",
+        "Pos": "{buyer_details.place_of_supply}"
+    }},
+    "DispDtls": {{
+        "Nm": "{dispatch_details.company_name}",
+        "Addr1": "{dispatch_details.address_line1}",
+        "Addr2": "{dispatch_details.address_line2}",
+        "Loc": "{dispatch_details.location}",
+        "Pin": "{dispatch_details.pincode}",
+        "Stcd": "{dispatch_details.state_code}"
+    }},
+    "ShipDtls": {{
+        "Gstin": "{shipping_details.gstin}",
+        "LglNm": "{shipping_details.legal_name}",
+        "TrdNm": "{shipping_details.trader_name}",
+        "Addr1": "{shipping_details.address_line1}",
+        "Addr2": "{shipping_details.address_line2}",
+        "Loc": "{shipping_details.location}",
+        "Pin": "{shipping_details.pincode}",
+        "Stcd": "{shipping_details.state_code}"
+    }},
+    "ItemList": [
+        {item_list}
+    ],
+    "ValDtls": {{
+        "AssVal": "{invoice_value_details.base_total}",
+        "CgstVal": "{invoice_value_details.total_cgst_amt}",
+        "SgstVal": "{invoice_value_details.total_sgst_amt}",
+        "IgstVal": "{invoice_value_details.total_igst_amt}",
+        "CesVal": "{invoice_value_details.total_cess_amt}",
+        "Discount": "{invoice_value_details.invoice_discount_amt}",
+        "RndOffAmt": "{invoice_value_details.round_off}",
+        "OthChrg": "{invoice_value_details.total_other_charges}",
+        "TotInvVal": "{invoice_value_details.base_grand_total}",
+        "TotInvValFc": "{invoice_value_details.grand_total}"
+    }},
+    "PayDtls": {{
+        "Nm": "{payment_details.payee_name}",
+        "AccDet": "{payment_details.account_no}",
+        "Mode": "{payment_details.mode_of_payment}",
+        "FinInsBr": "{payment_details.ifsc_code}",
+        "PayTerm": "{payment_details.terms}",
+        "PaidAmt": "{payment_details.paid_amount}",
+        "PaymtDue": "{payment_details.outstanding_amount}"
+    }},
+    "RefDtls": {{
+        "DocPerdDtls": {{
+            "InvStDt": "{period_details.start_date}",
+            "InvEndDt": "{period_details.end_date}"
+        }},
+        "PrecDocDtls": [{{
+            "InvNo": "{prev_doc_details.invoice_name}",
+            "InvDt": "{prev_doc_details.invoice_date}"
+        }}]
+    }},
+    "ExpDtls": {{
+        "ShipBNo": "{export_details.bill_no}",
+        "ShipBDt": "{export_details.bill_date}",
+        "Port": "{export_details.port}",
+        "ForCur": "{export_details.foreign_curr_code}",
+        "CntCode": "{export_details.country_code}",
+        "ExpDuty": "{export_details.export_duty}"
+    }},
+    "EwbDtls": {{
+        "TransId": "{eway_bill_details.gstin}",
+        "TransName": "{eway_bill_details.name}",
+        "TransMode": "{eway_bill_details.mode_of_transport}",
+        "Distance": "{eway_bill_details.distance}",
+        "TransDocNo": "{eway_bill_details.document_name}",
+        "TransDocDt": "{eway_bill_details.document_date}",
+        "VehNo": "{eway_bill_details.vehicle_no}",
+        "VehType": "{eway_bill_details.vehicle_type}"
+    }}
+}}
\ No newline at end of file
diff --git a/erpnext/regional/india/e_invoice/einv_validation.json b/erpnext/regional/india/e_invoice/einv_validation.json
new file mode 100644
index 0000000..f4a3542
--- /dev/null
+++ b/erpnext/regional/india/e_invoice/einv_validation.json
@@ -0,0 +1,957 @@
+{
+  "Version": {
+    "type": "string",
+    "minLength": 1,
+    "maxLength": 6,
+    "description": "Version of the schema"
+  },
+  "Irn": {
+    "type": "string",
+    "minLength": 64,
+    "maxLength": 64,
+    "description": "Invoice Reference Number"
+  },
+  "TranDtls": {
+    "type": "object",
+    "properties": {
+      "TaxSch": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 10,
+        "enum": ["GST"],
+        "description": "GST- Goods and Services Tax Scheme"
+      },
+      "SupTyp": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 10,
+        "enum": ["B2B", "SEZWP", "SEZWOP", "EXPWP", "EXPWOP", "DEXP"],
+        "description": "Type of Supply: B2B-Business to Business, SEZWP - SEZ with payment, SEZWOP - SEZ without payment, EXPWP - Export with Payment, EXPWOP - Export without payment,DEXP - Deemed Export"
+      },
+      "RegRev": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 1,
+        "enum": ["Y", "N"],
+        "description": "Y- whether the tax liability is payable under reverse charge"
+      },
+      "EcmGstin": {
+        "type": "string",
+        "minLength": 15,
+        "maxLength": 15,
+        "pattern": "([0-9]{2}[0-9A-Z]{13})",
+        "description": "E-Commerce GSTIN",
+        "validationMsg": "E-Commerce GSTIN is invalid"
+      },
+      "IgstOnIntra": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 1,
+        "enum": ["Y", "N"],
+        "description": "Y- indicates the supply is intra state but chargeable to IGST"
+      }
+    },
+    "required": ["TaxSch", "SupTyp"]
+  },
+  "DocDtls": {
+    "type": "object",
+    "properties": {
+      "Typ": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 3,
+        "enum": ["INV", "CRN", "DBN"],
+        "description": "Document Type"
+      },
+      "No": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 16,
+        "pattern": "^([A-Z1-9]{1}[A-Z0-9/-]{0,15})$",
+        "description": "Document Number",
+        "validationMsg": "Document Number 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]",
+        "description": "Document Date"
+      }
+    },
+    "required": ["Typ", "No", "Dt"]
+  },
+  "SellerDtls": {
+    "type": "object",
+    "properties": {
+      "Gstin": {
+        "type": "string",
+        "minLength": 15,
+        "maxLength": 15,
+        "pattern": "([0-9]{2}[0-9A-Z]{13})",
+        "description": "Supplier GSTIN",
+        "validationMsg": "Company GSTIN is invalid"
+      },
+      "LglNm": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Legal Name"
+      },
+      "TrdNm": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Tradename"
+      },
+      "Addr1": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 100,
+        "description": "Address Line 1"
+      },
+      "Addr2": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Address Line 2"
+      },
+      "Loc": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 50,
+        "description": "Location"
+      },
+      "Pin": {
+        "type": "number",
+        "minimum": 100000,
+        "maximum": 999999,
+        "description": "Pincode"
+      },
+      "Stcd": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 2,
+        "description": "Supplier State Code"
+      },
+      "Ph": {
+        "type": "string",
+        "minLength": 6,
+        "maxLength": 12,
+        "description": "Phone"
+      },
+      "Em": {
+        "type": "string",
+        "minLength": 6,
+        "maxLength": 100,
+        "description": "Email-Id"
+      }
+    },
+    "required": ["Gstin", "LglNm", "Addr1", "Loc", "Pin", "Stcd"]
+  },
+  "BuyerDtls": {
+    "type": "object",
+    "properties": {
+      "Gstin": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 15,
+        "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$",
+        "description": "Buyer GSTIN",
+        "validationMsg": "Customer GSTIN is invalid"
+      },
+      "LglNm": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Legal Name"
+      },
+      "TrdNm": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Trade Name"
+      },
+      "Pos": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 2,
+        "description": "Place of Supply State code"
+      },
+      "Addr1": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 100,
+        "description": "Address Line 1"
+      },
+      "Addr2": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Address Line 2"
+      },
+      "Loc": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Location"
+      },
+      "Pin": {
+        "type": "number",
+        "minimum": 100000,
+        "maximum": 999999,
+        "description": "Pincode"
+      },
+      "Stcd": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 2,
+        "description": "Buyer State Code"
+      },
+      "Ph": {
+        "type": "string",
+        "minLength": 6,
+        "maxLength": 12,
+        "description": "Phone"
+      },
+      "Em": {
+        "type": "string",
+        "minLength": 6,
+        "maxLength": 100,
+        "description": "Email-Id"
+      }
+    },
+    "required": ["Gstin", "LglNm", "Pos", "Addr1", "Loc", "Stcd"]
+  },
+  "DispDtls": {
+    "type": "object",
+    "properties": {
+      "Nm": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Dispatch Address Name"
+      },
+      "Addr1": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 100,
+        "description": "Address Line 1"
+      },
+      "Addr2": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Address Line 2"
+      },
+      "Loc": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Location"
+      },
+      "Pin": {
+        "type": "number",
+        "minimum": 100000,
+        "maximum": 999999,
+        "description": "Pincode"
+      },
+      "Stcd": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 2,
+        "description": "State Code"
+      }
+    },
+    "required": ["Nm", "Addr1", "Loc", "Pin", "Stcd"]
+  },
+  "ShipDtls": {
+    "type": "object",
+    "properties": {
+      "Gstin": {
+        "type": "string",
+        "maxLength": 15,
+        "minLength": 3,
+        "pattern": "^(([0-9]{2}[0-9A-Z]{13})|URP)$",
+        "description": "Shipping Address GSTIN",
+        "validationMsg": "Shipping Address GSTIN is invalid"
+      },
+      "LglNm": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Legal Name"
+      },
+      "TrdNm": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Trade Name"
+      },
+      "Addr1": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 100,
+        "description": "Address Line 1"
+      },
+      "Addr2": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Address Line 2"
+      },
+      "Loc": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Location"
+      },
+      "Pin": {
+        "type": "number",
+        "minimum": 100000,
+        "maximum": 999999,
+        "description": "Pincode"
+      },
+      "Stcd": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 2,
+        "description": "State Code"
+      }
+    },
+    "required": ["LglNm", "Addr1", "Loc", "Pin", "Stcd"]
+  },
+  "ItemList": {
+    "type": "Array",
+    "properties": {
+      "SlNo": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 6,
+        "description": "Serial No. of Item"
+      },
+      "PrdDesc": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 300,
+        "description": "Item Name"
+      },
+      "IsServc": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 1,
+        "enum": ["Y", "N"],
+        "description": "Is Service Item"
+      },
+      "HsnCd": {
+        "type": "string",
+        "minLength": 4,
+        "maxLength": 8,
+        "description": "HSN Code"
+      },
+      "Barcde": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 30,
+        "description": "Barcode"
+      },
+      "Qty": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 9999999999.999,
+        "description": "Quantity"
+      },
+      "FreeQty": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 9999999999.999,
+        "description": "Free Quantity"
+      },
+      "Unit": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 8,
+        "description": "UOM"
+      },
+      "UnitPrice": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.999,
+        "description": "Rate"
+      },
+      "TotAmt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "Gross Amount"
+      },
+      "Discount": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "Discount"
+      },
+      "PreTaxVal": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "Pre tax value"
+      },
+      "AssAmt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "Taxable Value"
+      },
+      "GstRt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999.999,
+        "description": "GST Rate"
+      },
+      "IgstAmt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "IGST Amount"
+      },
+      "CgstAmt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "CGST Amount"
+      },
+      "SgstAmt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "SGST Amount"
+      },
+      "CesRt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999.999,
+        "description": "Cess Rate"
+      },
+      "CesAmt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "Cess Amount (Advalorem)"
+      },
+      "CesNonAdvlAmt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "Cess Amount (Non-Advalorem)"
+      },
+      "StateCesRt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999.999,
+        "description": "State CESS Rate"
+      },
+      "StateCesAmt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "State CESS Amount"
+      },
+      "StateCesNonAdvlAmt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "State CESS Amount (Non Advalorem)"
+      },
+      "OthChrg": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "Other Charges"
+      },
+      "TotItemVal": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "Total Item Value"
+      },
+      "OrdLineRef": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 50,
+        "description": "Order line reference"
+      },
+      "OrgCntry": {
+        "type": "string",
+        "minLength": 2,
+        "maxLength": 2,
+        "description": "Origin Country"
+      },
+      "PrdSlNo": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 20,
+        "description": "Serial number"
+      },
+      "BchDtls": {
+        "type": "object",
+        "properties": {
+          "Nm": {
+            "type": "string",
+            "minLength": 3,
+            "maxLength": 20,
+            "description": "Batch number"
+          },
+          "ExpDt": {
+            "type": "string",
+            "maxLength": 10,
+            "minLength": 10,
+            "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+            "description": "Batch Expiry Date"
+          },
+          "WrDt": {
+            "type": "string",
+            "maxLength": 10,
+            "minLength": 10,
+            "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+            "description": "Warranty Date"
+          }
+        },
+        "required": ["Nm"]
+      },
+      "AttribDtls": {
+        "type": "Array",
+        "Attribute": {
+          "type": "object",
+          "properties": {
+            "Nm": {
+              "type": "string",
+              "minLength": 1,
+              "maxLength": 100,
+              "description": "Attribute name of the item"
+            },
+            "Val": {
+              "type": "string",
+              "minLength": 1,
+              "maxLength": 100,
+              "description": "Attribute value of the item"
+            }
+          }
+        }
+      }
+    },
+    "required": [
+      "SlNo",
+      "IsServc",
+      "HsnCd",
+      "UnitPrice",
+      "TotAmt",
+      "AssAmt",
+      "GstRt",
+      "TotItemVal"
+    ]
+  },
+  "ValDtls": {
+    "type": "object",
+    "properties": {
+      "AssVal": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 99999999999999.99,
+        "description": "Total Assessable value of all items"
+      },
+      "CgstVal": {
+        "type": "number",
+        "maximum": 99999999999999.99,
+        "minimum": 0,
+        "description": "Total CGST value of all items"
+      },
+      "SgstVal": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 99999999999999.99,
+        "description": "Total SGST value of all items"
+      },
+      "IgstVal": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 99999999999999.99,
+        "description": "Total IGST value of all items"
+      },
+      "CesVal": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 99999999999999.99,
+        "description": "Total CESS value of all items"
+      },
+      "StCesVal": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 99999999999999.99,
+        "description": "Total State CESS value of all items"
+      },
+      "Discount": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 99999999999999.99,
+        "description": "Invoice Discount"
+      },
+      "OthChrg": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 99999999999999.99,
+        "description": "Other Charges"
+      },
+      "RndOffAmt": {
+        "type": "number",
+        "minimum": -99.99,
+        "maximum": 99.99,
+        "description": "Rounded off Amount"
+      },
+      "TotInvVal": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 99999999999999.99,
+        "description": "Final Invoice Value "
+      },
+      "TotInvValFc": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 99999999999999.99,
+        "description": "Final Invoice value in Foreign Currency"
+      }
+    },
+    "required": ["AssVal", "TotInvVal"]
+  },
+  "PayDtls": {
+    "type": "object",
+    "properties": {
+      "Nm": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 100,
+        "description": "Payee Name"
+      },
+      "AccDet": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 18,
+        "description": "Bank Account Number of Payee"
+      },
+      "Mode": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 18,
+        "description": "Mode of Payment"
+      },
+      "FinInsBr": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 11,
+        "description": "Branch or IFSC code"
+      },
+      "PayTerm": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 100,
+        "description": "Terms of Payment"
+      },
+      "PayInstr": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 100,
+        "description": "Payment Instruction"
+      },
+      "CrTrn": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 100,
+        "description": "Credit Transfer"
+      },
+      "DirDr": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 100,
+        "description": "Direct Debit"
+      },
+      "CrDay": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 9999,
+        "description": "Credit Days"
+      },
+      "PaidAmt": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 99999999999999.99,
+        "description": "Advance Amount"
+      },
+      "PaymtDue": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 99999999999999.99,
+        "description": "Outstanding Amount"
+      }
+    }
+  },
+  "RefDtls": {
+    "type": "object",
+    "properties": {
+      "InvRm": {
+        "type": "string",
+        "maxLength": 100,
+        "minLength": 3,
+        "pattern": "^[0-9A-Za-z/-]{3,100}$",
+        "description": "Remarks/Note"
+      },
+      "DocPerdDtls": {
+        "type": "object",
+        "properties": {
+          "InvStDt": {
+            "type": "string",
+            "maxLength": 10,
+            "minLength": 10,
+            "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+            "description": "Invoice Period Start Date"
+          },
+          "InvEndDt": {
+            "type": "string",
+            "maxLength": 10,
+            "minLength": 10,
+            "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+            "description": "Invoice Period End Date"
+          }
+        },
+        "required": ["InvStDt ", "InvEndDt "]
+      },
+      "PrecDocDtls": {
+        "type": "object",
+        "properties": {
+          "InvNo": {
+            "type": "string",
+            "minLength": 1,
+            "maxLength": 16,
+            "pattern": "^[1-9A-Z]{1}[0-9A-Z/-]{1,15}$",
+            "description": "Reference of Original Invoice"
+          },
+          "InvDt": {
+            "type": "string",
+            "maxLength": 10,
+            "minLength": 10,
+            "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+            "description": "Date of Orginal Invoice"
+          },
+          "OthRefNo": {
+            "type": "string",
+            "minLength": 1,
+            "maxLength": 20,
+            "description": "Other Reference"
+          }
+        }
+      },
+      "required": ["InvNo", "InvDt"],
+      "ContrDtls": {
+        "type": "object",
+        "properties": {
+          "RecAdvRefr": {
+            "type": "string",
+            "minLength": 1,
+            "maxLength": 20,
+            "pattern": "^([0-9A-Za-z/-]){1,20}$",
+            "description": "Receipt Advice No."
+          },
+          "RecAdvDt": {
+            "type": "string",
+            "minLength": 10,
+            "maxLength": 10,
+            "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+            "description": "Date of receipt advice"
+          },
+          "TendRefr": {
+            "type": "string",
+            "minLength": 1,
+            "maxLength": 20,
+            "pattern": "^([0-9A-Za-z/-]){1,20}$",
+            "description": "Lot/Batch Reference No."
+          },
+          "ContrRefr": {
+            "type": "string",
+            "minLength": 1,
+            "maxLength": 20,
+            "pattern": "^([0-9A-Za-z/-]){1,20}$",
+            "description": "Contract Reference Number"
+          },
+          "ExtRefr": {
+            "type": "string",
+            "minLength": 1,
+            "maxLength": 20,
+            "pattern": "^([0-9A-Za-z/-]){1,20}$",
+            "description": "Any other reference"
+          },
+          "ProjRefr": {
+            "type": "string",
+            "minLength": 1,
+            "maxLength": 20,
+            "pattern": "^([0-9A-Za-z/-]){1,20}$",
+            "description": "Project Reference Number"
+          },
+          "PORefr": {
+            "type": "string",
+            "minLength": 1,
+            "maxLength": 16,
+            "pattern": "^([0-9A-Za-z/-]){1,16}$",
+            "description": "PO Reference Number"
+          },
+          "PORefDt": {
+            "type": "string",
+            "minLength": 10,
+            "maxLength": 10,
+            "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+            "description": "PO Reference date"
+          }
+        }
+      }
+    }
+  },
+  "AddlDocDtls": {
+    "type": "Array",
+    "properties": {
+      "Url": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Supporting document URL"
+      },
+      "Docs": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 1000,
+        "description": "Supporting document in Base64 Format"
+      },
+      "Info": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 1000,
+        "description": "Any additional information"
+      }
+    }
+  },
+
+  "ExpDtls": {
+    "type": "object",
+    "properties": {
+      "ShipBNo": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 20,
+        "description": "Shipping Bill No."
+      },
+      "ShipBDt": {
+        "type": "string",
+        "minLength": 10,
+        "maxLength": 10,
+        "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+        "description": "Shipping Bill Date"
+      },
+      "Port": {
+        "type": "string",
+        "minLength": 2,
+        "maxLength": 10,
+        "pattern": "^[0-9A-Za-z]{2,10}$",
+        "description": "Port Code. Refer the master"
+      },
+      "RefClm": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 1,
+        "description": "Claiming Refund. Y/N"
+      },
+      "ForCur": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 16,
+        "description": "Additional Currency Code. Refer the master"
+      },
+      "CntCode": {
+        "type": "string",
+        "minLength": 2,
+        "maxLength": 2,
+        "description": "Country Code. Refer the master"
+      },
+      "ExpDuty": {
+        "type": "number",
+        "minimum": 0,
+        "maximum": 999999999999.99,
+        "description": "Export Duty"
+      }
+    }
+  },
+  "EwbDtls": {
+    "type": "object",
+    "properties": {
+      "TransId": {
+        "type": "string",
+        "minLength": 15,
+        "maxLength": 15,
+        "description": "Transporter GSTIN"
+      },
+      "TransName": {
+        "type": "string",
+        "minLength": 3,
+        "maxLength": 100,
+        "description": "Transporter Name"
+      },
+      "TransMode": {
+        "type": "string",
+        "maxLength": 1,
+        "minLength": 1,
+        "enum": ["1", "2", "3", "4"],
+        "description": "Mode of Transport"
+      },
+      "Distance": {
+        "type": "number",
+        "minimum": 1,
+        "maximum": 9999,
+        "description": "Distance"
+      },
+      "TransDocNo": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 15,
+        "pattern": "^([0-9A-Z/-]){1,15}$",
+        "description": "Tranport Document Number",
+        "validationMsg": "Transport Receipt No is invalid"
+      },
+      "TransDocDt": {
+        "type": "string",
+        "minLength": 10,
+        "maxLength": 10,
+        "pattern": "[0-3][0-9]/[0-1][0-9]/[2][0][1-2][0-9]",
+        "description": "Transport Document Date"
+      },
+      "VehNo": {
+        "type": "string",
+        "minLength": 4,
+        "maxLength": 20,
+        "description": "Vehicle Number"
+      },
+      "VehType": {
+        "type": "string",
+        "minLength": 1,
+        "maxLength": 1,
+        "enum": ["O", "R"],
+        "description": "Vehicle Type"
+      }
+    },
+    "required": ["Distance"]
+  },
+  "required": [
+    "Version",
+    "TranDtls",
+    "DocDtls",
+    "SellerDtls",
+    "BuyerDtls",
+    "ItemList",
+    "ValDtls"
+  ]
+}
diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
new file mode 100644
index 0000000..348f0c6
--- /dev/null
+++ b/erpnext/regional/india/e_invoice/einvoice.js
@@ -0,0 +1,292 @@
+erpnext.setup_einvoice_actions = (doctype) => {
+	frappe.ui.form.on(doctype, {
+		async refresh(frm) {
+			if (frm.doc.docstatus == 2) return;
+
+			const res = await frappe.call({
+				method: 'erpnext.regional.india.e_invoice.utils.validate_eligibility',
+				args: { doc: frm.doc }
+			});
+			const invoice_eligible = res.message;
+
+			if (!invoice_eligible) return;
+
+			const { doctype, irn, irn_cancelled, ewaybill, eway_bill_cancelled, name, __unsaved } = frm.doc;
+
+			const add_custom_button = (label, action) => {
+				if (!frm.custom_buttons[label]) {
+					frm.add_custom_button(label, action, __('E Invoicing'));
+				}
+			};
+
+			if (!irn && !__unsaved) {
+				const action = () => {
+					if (frm.doc.__unsaved) {
+						frappe.throw(__('Please save the document to generate IRN.'));
+					}
+					frappe.call({
+						method: 'erpnext.regional.india.e_invoice.utils.get_einvoice',
+						args: { doctype, docname: name },
+						freeze: true,
+						callback: (res) => {
+							const einvoice = res.message;
+							show_einvoice_preview(frm, einvoice);
+						}
+					});
+				};
+
+				add_custom_button(__("Generate IRN"), action);
+			}
+
+			if (irn && !irn_cancelled && !ewaybill) {
+				const 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
+					}
+				];
+				const action = () => {
+					const d = new frappe.ui.Dialog({
+						title: __("Cancel IRN"),
+						fields: fields,
+						primary_action: function() {
+							const data = d.get_values();
+							frappe.call({
+								method: 'erpnext.regional.india.e_invoice.utils.cancel_irn',
+								args: {
+									doctype,
+									docname: name,
+									irn: irn,
+									reason: data.reason.split('-')[0],
+									remark: data.remark
+								},
+								freeze: true,
+								callback: () => frm.reload_doc() || d.hide(),
+								error: () => d.hide()
+							});
+						},
+						primary_action_label: __('Submit')
+					});
+					d.show();
+				};
+				add_custom_button(__("Cancel IRN"), action);
+			}
+
+			if (irn && !irn_cancelled && !ewaybill) {
+				const action = () => {
+					const d = new frappe.ui.Dialog({
+						title: __('Generate E-Way Bill'),
+						size: "large",
+						fields: get_ewaybill_fields(frm),
+						primary_action: function() {
+							const data = d.get_values();
+							frappe.call({
+								method: 'erpnext.regional.india.e_invoice.utils.generate_eway_bill',
+								args: {
+									doctype,
+									docname: name,
+									irn,
+									...data
+								},
+								freeze: true,
+								callback: () => frm.reload_doc() || d.hide(),
+								error: () => d.hide()
+							});
+						},
+						primary_action_label: __('Submit')
+					});
+					d.show();
+				};
+
+				add_custom_button(__("Generate E-Way Bill"), action);
+			}
+
+			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()
+								});
+							}
+						},
+						primary_action_label: __('Yes')
+					});
+				};
+				add_custom_button(__("Cancel E-Way Bill"), action);
+			}
+		}
+	});
+};
+
+const get_ewaybill_fields = (frm) => {
+	return [
+		{
+			'fieldname': 'transporter',
+			'label': 'Transporter',
+			'fieldtype': 'Link',
+			'options': 'Supplier',
+			'default': frm.doc.transporter
+		},
+		{
+			'fieldname': 'gst_transporter_id',
+			'label': 'GST Transporter ID',
+			'fieldtype': 'Data',
+			'fetch_from': 'transporter.gst_transporter_id',
+			'default': frm.doc.gst_transporter_id
+		},
+		{
+			'fieldname': 'driver',
+			'label': 'Driver',
+			'fieldtype': 'Link',
+			'options': 'Driver',
+			'default': frm.doc.driver
+		},
+		{
+			'fieldname': 'lr_no',
+			'label': 'Transport Receipt No',
+			'fieldtype': 'Data',
+			'default': frm.doc.lr_no
+		},
+		{
+			'fieldname': 'vehicle_no',
+			'label': 'Vehicle No',
+			'fieldtype': 'Data',
+			'default': frm.doc.vehicle_no
+		},
+		{
+			'fieldname': 'distance',
+			'label': 'Distance (in km)',
+			'fieldtype': 'Float',
+			'default': frm.doc.distance
+		},
+		{
+			'fieldname': 'transporter_col_break',
+			'fieldtype': 'Column Break',
+		},
+		{
+			'fieldname': 'transporter_name',
+			'label': 'Transporter Name',
+			'fieldtype': 'Data',
+			'fetch_from': 'transporter.name',
+			'read_only': 1,
+			'default': frm.doc.transporter_name
+		},
+		{
+			'fieldname': 'mode_of_transport',
+			'label': 'Mode of Transport',
+			'fieldtype': 'Select',
+			'options': `\nRoad\nAir\nRail\nShip`,
+			'default': frm.doc.mode_of_transport
+		},
+		{
+			'fieldname': 'driver_name',
+			'label': 'Driver Name',
+			'fieldtype': 'Data',
+			'fetch_from': 'driver.full_name',
+			'read_only': 1,
+			'default': frm.doc.driver_name
+		},
+		{
+			'fieldname': 'lr_date',
+			'label': 'Transport Receipt Date',
+			'fieldtype': 'Date',
+			'default': frm.doc.lr_date
+		},
+		{
+			'fieldname': 'gst_vehicle_type',
+			'label': 'GST Vehicle Type',
+			'fieldtype': 'Select',
+			'options': `Regular\nOver Dimensional Cargo (ODC)`,
+			'depends_on': 'eval:(doc.mode_of_transport === "Road")',
+			'default': frm.doc.gst_vehicle_type
+		}
+	];
+};
+
+const request_irn_generation = (frm) => {
+	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()
+	});
+};
+
+const get_preview_dialog = (frm, action) => {
+	const dialog = new frappe.ui.Dialog({
+		title: __("Preview"),
+		size: "large",
+		fields: [
+			{
+				"label": "Preview",
+				"fieldname": "preview_html",
+				"fieldtype": "HTML"
+			}
+		],
+		primary_action: () => action(frm) || dialog.hide(),
+		primary_action_label: __('Generate IRN')
+	});
+	return dialog;
+};
+
+const show_einvoice_preview = (frm, einvoice) => {
+	const preview_dialog = get_preview_dialog(frm, request_irn_generation);
+
+	// initialize e-invoice fields
+	einvoice["Irn"] = einvoice["AckNo"] = ''; einvoice["AckDt"] = frappe.datetime.nowdate();
+	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 { 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: 720px; }`;
+
+				frappe.dom.set_style(style, "custom-print-style");
+				preview_dialog.show();
+			}
+		}
+	});
+};
diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py
index 4b99421..074bd52 100644
--- a/erpnext/regional/india/setup.py
+++ b/erpnext/regional/india/setup.py
@@ -60,7 +60,7 @@
 
 def add_custom_roles_for_reports():
 	for report_name in ('GST Sales Register', 'GST Purchase Register',
-		'GST Itemised Sales Register', 'GST Itemised Purchase Register', 'Eway Bill'):
+		'GST Itemised Sales Register', 'GST Itemised Purchase Register', 'Eway Bill', 'E-Invoice Summary'):
 
 		if not frappe.db.get_value('Custom Role', dict(report=report_name)):
 			frappe.get_doc(dict(
@@ -99,7 +99,7 @@
 			)).insert()
 
 def add_permissions():
-	for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate'):
+	for doctype in ('GST HSN Code', 'GST Settings', 'GSTR 3B Report', 'Lower Deduction Certificate', 'E Invoice Settings'):
 		add_permission(doctype, 'All', 0)
 		for role in ('Accounts Manager', 'Accounts User', 'System Manager'):
 			add_permission(doctype, role, 0)
@@ -115,9 +115,11 @@
 def add_print_formats():
 	frappe.reload_doc("regional", "print_format", "gst_tax_invoice")
 	frappe.reload_doc("accounts", "print_format", "gst_pos_invoice")
+	frappe.reload_doc("accounts", "print_format", "GST E-Invoice")
 
 	frappe.db.set_value("Print Format", "GST POS Invoice", "disabled", 0)
 	frappe.db.set_value("Print Format", "GST Tax Invoice", "disabled", 0)
+	frappe.db.set_value("Print Format", "GST E-Invoice", "disabled", 0)
 
 def make_property_setters(patch=False):
 	# GST rules do not allow for an invoice no. bigger than 16 characters
@@ -453,7 +455,7 @@
 			'fieldname': 'ewaybill',
 			'label': 'E-Way Bill No.',
 			'fieldtype': 'Data',
-			'depends_on': 'eval:(doc.docstatus === 1)',
+			'depends_on': 'eval:((doc.docstatus === 1 || doc.ewaybill) && doc.eway_bill_cancelled === 0)',
 			'allow_on_submit': 1,
 			'insert_after': 'tax_id',
 			'translatable': 0,
@@ -481,6 +483,46 @@
 			fetch_from='customer_address.gstin', print_hide=1, read_only=1)
 	]
 
+	si_einvoice_fields = [
+		dict(fieldname='irn', label='IRN', fieldtype='Data', read_only=1, insert_after='customer', no_copy=1, print_hide=1,
+			depends_on='eval:in_list(["Registered Regular", "SEZ", "Overseas", "Deemed Export"], doc.gst_category) && doc.irn_cancelled === 0'),
+
+		dict(fieldname='irn_cancelled', label='IRN Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
+			depends_on='eval: doc.irn', allow_on_submit=1, insert_after='customer'),
+
+		dict(fieldname='eway_bill_validity', label='E-Way Bill Validity', fieldtype='Data', no_copy=1, print_hide=1,
+			depends_on='ewaybill', read_only=1, allow_on_submit=1, insert_after='ewaybill'),
+
+		dict(fieldname='eway_bill_cancelled', label='E-Way Bill Cancelled', fieldtype='Check', no_copy=1, print_hide=1,
+			depends_on='eval:(doc.eway_bill_cancelled === 1)', read_only=1, allow_on_submit=1, insert_after='customer'),
+
+		dict(fieldname='einvoice_section', label='E-Invoice Fields', fieldtype='Section Break', insert_after='gst_vehicle_type',
+			print_hide=1, hidden=1),
+
+		dict(fieldname='ack_no', label='Ack. No.', fieldtype='Data', read_only=1, hidden=1, insert_after='einvoice_section',
+			no_copy=1, print_hide=1),
+
+		dict(fieldname='ack_date', label='Ack. Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_no', no_copy=1, print_hide=1),
+
+		dict(fieldname='irn_cancel_date', label='Cancel Date', fieldtype='Data', read_only=1, hidden=1, insert_after='ack_date',
+			no_copy=1, print_hide=1),
+
+		dict(fieldname='signed_einvoice', label='Signed E-Invoice', fieldtype='Code', options='JSON', hidden=1, insert_after='irn_cancel_date',
+			no_copy=1, print_hide=1, read_only=1),
+
+		dict(fieldname='signed_qr_code', label='Signed QRCode', fieldtype='Code', options='JSON', hidden=1, insert_after='signed_einvoice',
+			no_copy=1, print_hide=1, read_only=1),
+
+		dict(fieldname='qrcode_image', label='QRCode', fieldtype='Attach Image', hidden=1, insert_after='signed_qr_code',
+			no_copy=1, print_hide=1, read_only=1),
+
+		dict(fieldname='einvoice_status', label='E-Invoice Status', fieldtype='Select', insert_after='qrcode_image',
+			options='\nPending\nGenerated\nCancelled\nFailed', default=None, hidden=1, no_copy=1, print_hide=1, read_only=1),
+
+		dict(fieldname='failure_description', label='E-Invoice Failure Description', fieldtype='Code', options='JSON',
+			hidden=1, insert_after='einvoice_status', no_copy=1, print_hide=1, read_only=1)
+	]
+
 	custom_fields = {
 		'Address': [
 			dict(fieldname='gstin', label='Party GSTIN', fieldtype='Data',
@@ -493,7 +535,7 @@
 		'Purchase Invoice': purchase_invoice_gst_category + invoice_gst_fields + purchase_invoice_itc_fields + purchase_invoice_gst_fields,
 		'Purchase Order': purchase_invoice_gst_fields,
 		'Purchase Receipt': purchase_invoice_gst_fields,
-		'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields,
+		'Sales Invoice': sales_invoice_gst_category + invoice_gst_fields + sales_invoice_shipping_fields + sales_invoice_gst_fields + si_ewaybill_fields + si_einvoice_fields,
 		'POS Invoice': sales_invoice_gst_fields,
 		'Delivery Note': sales_invoice_gst_fields + ewaybill_fields + sales_invoice_shipping_fields + delivery_note_gst_category,
 		'Payment Entry': payment_entry_fields,
diff --git a/erpnext/patches/v14_0/__init__.py b/erpnext/regional/report/e_invoice_summary/__init__.py
similarity index 100%
copy from erpnext/patches/v14_0/__init__.py
copy to erpnext/regional/report/e_invoice_summary/__init__.py
diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js
new file mode 100644
index 0000000..4713217
--- /dev/null
+++ b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.js
@@ -0,0 +1,55 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["E-Invoice Summary"] = {
+	"filters": [
+		{
+			"fieldtype": "Link",
+			"options": "Company",
+			"reqd": 1,
+			"fieldname": "company",
+			"label": __("Company"),
+			"default": frappe.defaults.get_user_default("Company"),
+		},
+		{
+			"fieldtype": "Link",
+			"options": "Customer",
+			"fieldname": "customer",
+			"label": __("Customer")
+		},
+		{
+			"fieldtype": "Date",
+			"reqd": 1,
+			"fieldname": "from_date",
+			"label": __("From Date"),
+			"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+		},
+		{
+			"fieldtype": "Date",
+			"reqd": 1,
+			"fieldname": "to_date",
+			"label": __("To Date"),
+			"default": frappe.datetime.get_today(),
+		},
+		{
+			"fieldtype": "Select",
+			"fieldname": "status",
+			"label": __("Status"),
+			"options": "\nPending\nGenerated\nCancelled\nFailed"
+		}
+	],
+
+	"formatter": function (value, row, column, data, default_formatter) {
+		value = default_formatter(value, row, column, data);
+
+		if (column.fieldname == "einvoice_status" && value) {
+			if (value == 'Pending') value = `<span class="bold" style="color: var(--text-on-orange)">${value}</span>`;
+			else if (value == 'Generated') value = `<span class="bold" style="color: var(--text-on-green)">${value}</span>`;
+			else if (value == 'Cancelled') value = `<span class="bold" style="color: var(--text-on-red)">${value}</span>`;
+			else if (value == 'Failed') value = `<span class="bold"  style="color: var(--text-on-red)">${value}</span>`;
+		}
+
+		return value;
+	}
+};
diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
new file mode 100644
index 0000000..d0000ad
--- /dev/null
+++ b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.json
@@ -0,0 +1,28 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2021-03-12 11:23:37.312294",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "json": "{}",
+ "letter_head": "Logo",
+ "modified": "2021-03-13 12:36:48.689413",
+ "modified_by": "Administrator",
+ "module": "Regional",
+ "name": "E-Invoice Summary",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Sales Invoice",
+ "report_name": "E-Invoice Summary",
+ "report_type": "Script Report",
+ "roles": [
+  {
+   "role": "Administrator"
+  }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py
new file mode 100644
index 0000000..5cbb455
--- /dev/null
+++ b/erpnext/regional/report/e_invoice_summary/e_invoice_summary.py
@@ -0,0 +1,112 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+
+import frappe
+from frappe import _
+
+
+def execute(filters=None):
+	validate_filters(filters)
+
+	columns = get_columns()
+	data = get_data(filters)
+
+	return columns, data
+
+def validate_filters(filters=None):
+	if filters is None:
+		filters = {}
+	filters = frappe._dict(filters)
+
+	if not filters.company:
+		frappe.throw(_('{} is mandatory for generating E-Invoice Summary Report').format(_('Company')), title=_('Invalid Filter'))
+	if filters.company:
+		# validate if company has e-invoicing enabled
+		pass
+	if not filters.from_date or not filters.to_date:
+		frappe.throw(_('From Date & To Date is mandatory for generating E-Invoice Summary Report'), title=_('Invalid Filter'))
+	if filters.from_date > filters.to_date:
+		frappe.throw(_('From Date must be before To Date'), title=_('Invalid Filter'))
+
+def get_data(filters=None):
+	if filters is None:
+		filters = {}
+	query_filters = {
+		'posting_date': ['between', [filters.from_date, filters.to_date]],
+		'einvoice_status': ['is', 'set'],
+		'company': filters.company
+	}
+	if filters.customer:
+		query_filters['customer'] = filters.customer
+	if filters.status:
+		query_filters['einvoice_status'] = filters.status
+
+	data = frappe.get_all(
+		'Sales Invoice',
+		filters=query_filters,
+		fields=[d.get('fieldname') for d in get_columns()]
+	)
+
+	return data
+
+def get_columns():
+	return [
+		{
+			"fieldtype": "Date",
+			"fieldname": "posting_date",
+			"label": _("Posting Date"),
+			"width": 0
+		},
+		{
+			"fieldtype": "Link",
+			"fieldname": "name",
+			"label": _("Sales Invoice"),
+			"options": "Sales Invoice",
+			"width": 140
+		},
+		{
+			"fieldtype": "Data",
+			"fieldname": "einvoice_status",
+			"label": _("Status"),
+			"width": 100
+		},
+		{
+			"fieldtype": "Link",
+			"fieldname": "customer",
+			"options": "Customer",
+			"label": _("Customer")
+		},
+		{
+			"fieldtype": "Check",
+			"fieldname": "is_return",
+			"label": _("Is Return"),
+			"width": 85
+		},
+		{
+			"fieldtype": "Data",
+			"fieldname": "ack_no",
+			"label": "Ack. No.",
+			"width": 145
+		},
+		{
+			"fieldtype": "Data",
+			"fieldname": "ack_date",
+			"label": "Ack. Date",
+			"width": 165
+		},
+		{
+			"fieldtype": "Data",
+			"fieldname": "irn",
+			"label": _("IRN No."),
+			"width": 250
+		},
+		{
+			"fieldtype": "Currency",
+			"options": "Company:company:default_currency",
+			"fieldname": "base_grand_total",
+			"label": _("Grand Total"),
+			"width": 120
+		}
+	]