Merge branch 'develop' of https://github.com/frappe/erpnext into ledger_preview
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 8cb2950..1ef0c51 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -88,6 +88,7 @@
 		}
 
 		this.show_general_ledger();
+		this.show_ledger_preview();
 
 		if(doc.update_stock) this.show_stock_ledger();
 
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index ab4aab3..f0d3f72 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -2157,7 +2157,7 @@
    "link_fieldname": "consolidated_invoice"
   }
  ],
- "modified": "2023-06-19 16:02:05.309332",
+ "modified": "2023-06-21 16:02:18.988799",
  "modified_by": "Administrator",
  "module": "Accounts",
  "name": "Sales Invoice",
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index cdbf6c7..0b057bc 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -845,6 +845,135 @@
 		gl_entries.append(self.get_gl_dict(gl_entry, item=item))
 
 
+@frappe.whitelist()
+def show_ledger_preview(company, doctype, docname):
+	frappe.db.savepoint("show_ledger_preview")
+
+	filters = {"company": company}
+	doc = frappe.get_doc(doctype, docname)
+
+	sl_columns, sl_data = get_stock_ledger_preview(doc, filters)
+	gl_columns, gl_data = get_accounting_ledger_preview(doc, filters)
+
+	frappe.db.rollback(save_point="show_ledger_preview")
+
+	return {
+		"gl_columns": gl_columns,
+		"gl_data": gl_data,
+		"sl_columns": sl_columns,
+		"sl_data": sl_data,
+	}
+
+
+def get_accounting_ledger_preview(doc, filters):
+	from erpnext.accounts.report.general_ledger.general_ledger import get_columns as get_gl_columns
+
+	gl_columns, gl_data = [], []
+	fields = [
+		"posting_date",
+		"account",
+		"debit",
+		"credit",
+		"against",
+		"party",
+		"party_type",
+		"against_voucher_type",
+		"against_voucher",
+	]
+
+	doc.docstatus = 1
+	doc.make_gl_entries()
+	columns = get_gl_columns(filters)
+	gl_entries = get_gl_entries_for_preview(doc.doctype, doc.name, fields)
+
+	gl_columns = get_columns(columns, fields)
+	gl_data = get_data(fields, gl_entries)
+
+	return gl_columns, gl_data
+
+
+def get_stock_ledger_preview(doc, filters):
+	from erpnext.stock.report.stock_ledger.stock_ledger import get_columns as get_sl_columns
+
+	sl_columns, sl_data = [], []
+	fields = [
+		"item_code",
+		"stock_uom",
+		"actual_qty",
+		"qty_after_transaction",
+		"warehouse",
+		"incoming_rate",
+		"valuation_rate",
+		"stock_value",
+		"stock_value_difference",
+	]
+	columns_fields = [
+		"item_code",
+		"stock_uom",
+		"in_qty",
+		"out_qty",
+		"qty_after_transaction",
+		"warehouse",
+		"incoming_rate",
+		"valuation_rate",
+		"stock_value",
+		"stock_value_difference",
+	]
+
+	if doc.update_stock or doc.doctype in ("Purchase Receipt", "Delivery Note"):
+		doc.docstatus = 1
+		doc.update_stock_ledger()
+		columns = get_sl_columns(filters)
+		sl_entries = get_sl_entries_for_preview(doc.doctype, doc.name, fields)
+
+		sl_columns = get_columns(columns, columns_fields)
+		sl_data = get_data(columns_fields, sl_entries)
+
+	return sl_columns, sl_data
+
+
+def get_sl_entries_for_preview(doctype, docname, fields):
+	sl_entries = frappe.get_all(
+		"Stock Ledger Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields
+	)
+
+	for entry in sl_entries:
+		if entry.actual_qty > 0:
+			entry["in_qty"] = entry.actual_qty
+			entry["out_qty"] = 0
+		else:
+			entry["out_qty"] = abs(entry.actual_qty)
+			entry["in_qty"] = 0
+
+	return sl_entries
+
+
+def get_gl_entries_for_preview(doctype, docname, fields):
+	return frappe.get_all(
+		"GL Entry", filters={"voucher_type": doctype, "voucher_no": docname}, fields=fields
+	)
+
+
+def get_columns(raw_columns, fields):
+	return [
+		{"name": d.get("label"), "editable": False, "width": 110}
+		for d in raw_columns
+		if not d.get("hidden") and d.get("fieldname") in fields
+	]
+
+
+def get_data(raw_columns, raw_data):
+	datatable_data = []
+	for row in raw_data:
+		data_row = []
+		for column in raw_columns:
+			data_row.append(row.get(column) or "")
+
+		datatable_data.append(data_row)
+
+	return datatable_data
+
+
 def repost_required_for_queue(doc: StockController) -> bool:
 	"""check if stock document contains repeated item-warehouse with queue based valuation.
 
diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js
index d346357..ff59348 100644
--- a/erpnext/public/js/controllers/stock_controller.js
+++ b/erpnext/public/js/controllers/stock_controller.js
@@ -66,7 +66,7 @@
 	}
 
 	show_general_ledger() {
-		var me = this;
+		let me = this;
 		if(this.frm.doc.docstatus > 0) {
 			cur_frm.add_custom_button(__('Accounting Ledger'), function() {
 				frappe.route_options = {
@@ -81,4 +81,69 @@
 			}, __("View"));
 		}
 	}
+
+	show_ledger_preview() {
+		let me = this
+		if(this.frm.doc.docstatus == 0) {
+			cur_frm.add_custom_button(__('Accounting Ledger Preview'), function() {
+				frappe.call({
+					"method": "erpnext.controllers.stock_controller.show_ledger_preview",
+					"args": {
+						"company": me.frm.doc.company,
+						"doctype": me.frm.doc.doctype,
+						"docname": me.frm.doc.name
+					},
+					"callback": function(response) {
+						me.make_dialog(response.message);
+					}
+				})
+			}, __("View"));
+		}
+	}
+
+	make_dialog(data) {
+		let me = this;
+		let gl_columns = data.gl_columns;
+		let gl_data = data.gl_data;
+		let sl_columns = data.sl_columns;
+		let sl_data = data.sl_data;
+
+		let dialog = new frappe.ui.Dialog({
+			"size": "extra-large",
+			"title": __("Ledger Preview"),
+			"fields": [
+				{
+					"fieldtype": "HTML",
+					"fieldname": "accounting_ledger_preview_html",
+					"label": __("Accounting Ledger"),
+				},
+				{
+					"fieldtype": "HTML",
+					"fieldname": "stock_ledger_preview_html",
+					"label": __("Stock Ledger"),
+				}
+			]
+		});
+
+		setTimeout(function() {
+			me.get_datatable(gl_columns, gl_data, dialog.get_field("accounting_ledger_preview_html").wrapper);
+		}, 200);
+
+		dialog.show();
+	}
+
+	get_datatable(columns, data, wrapper) {
+		const datatable_options = {
+			columns: columns,
+			data: data,
+			dynamicRowHeight: true,
+			checkboxColumn: false,
+			inlineFilters: true,
+		};
+
+		new frappe.DataTable(
+			wrapper,
+			datatable_options
+		);
+	}
 };