Merge pull request #38553 from s-aga-r/STOCK-VARIANCE-COMPANY

feat: `Company` filter in `Stock Ledger Variance` report
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 096bb10..7355c4b 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -8,7 +8,17 @@
 
 import frappe
 from frappe import _
-from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate
+from frappe.utils import (
+	add_days,
+	add_months,
+	cint,
+	cstr,
+	flt,
+	formatdate,
+	get_first_day,
+	getdate,
+	today,
+)
 
 from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
 	get_accounting_dimensions,
@@ -43,6 +53,8 @@
 		year_start_date = getdate(period_start_date)
 		year_end_date = getdate(period_end_date)
 
+	year_end_date = getdate(today()) if year_end_date > getdate(today()) else year_end_date
+
 	months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity]
 
 	period_list = []
diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index f0fc1aa..ef6cfb0 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -105,7 +105,7 @@
 			if self.source == "Existing Customer" and self.customer:
 				contact = frappe.db.get_value(
 					"Dynamic Link",
-					{"link_doctype": "Customer", "link_name": self.customer},
+					{"link_doctype": "Customer", "parenttype": "Contact", "link_name": self.customer},
 					"parent",
 				)
 				if contact:
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index 9267801..0de6774 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -31,8 +31,19 @@
 			secondary_action: () => this.edit_full_form(),
 		});
 
-		this.dialog.set_value("qty", this.item.qty);
+		this.dialog.set_value("qty", this.item.qty).then(() => {
+			if (this.item.serial_no) {
+				this.dialog.set_value("scan_serial_no", this.item.serial_no);
+				frappe.model.set_value(this.item.doctype, this.item.name, 'serial_no', '');
+			} else if (this.item.batch_no) {
+				this.dialog.set_value("scan_batch_no", this.item.batch_no);
+				frappe.model.set_value(this.item.doctype, this.item.name, 'batch_no', '');
+			}
+		});
+
 		this.dialog.show();
+		this.$scan_btn = this.dialog.$wrapper.find(".link-btn");
+		this.$scan_btn.css("display", "inline");
 	}
 
 	get_serial_no_filters() {
@@ -95,6 +106,7 @@
 		if (this.item.has_serial_no) {
 			fields.push({
 				fieldtype: 'Data',
+				options: 'Barcode',
 				fieldname: 'scan_serial_no',
 				label: __('Scan Serial No'),
 				get_query: () => {
@@ -106,15 +118,10 @@
 			});
 		}
 
-		if (this.item.has_batch_no && this.item.has_serial_no) {
-			fields.push({
-				fieldtype: 'Column Break',
-			});
-		}
-
-		if (this.item.has_batch_no) {
+		if (this.item.has_batch_no && !this.item.has_serial_no) {
 			fields.push({
 				fieldtype: 'Data',
+				options: 'Barcode',
 				fieldname: 'scan_batch_no',
 				label: __('Scan Batch No'),
 				onchange: () => this.update_serial_batch_no()
@@ -309,6 +316,14 @@
 	}
 
 	get_auto_data() {
+		if (this.item.serial_and_batch_bundle || this.item.rejected_serial_and_batch_bundle) {
+			return;
+		}
+
+		if (this.item.serial_no || this.item.batch_no) {
+			return;
+		}
+
 		let { qty, based_on } = this.dialog.get_values();
 
 		if (!based_on) {
@@ -340,16 +355,57 @@
 		const { scan_serial_no, scan_batch_no } = this.dialog.get_values();
 
 		if (scan_serial_no) {
-			this.dialog.fields_dict.entries.df.data.push({
-				serial_no: scan_serial_no
+			let existing_row = this.dialog.fields_dict.entries.df.data.filter(d => {
+				if (d.serial_no === scan_serial_no) {
+					return d
+				}
 			});
 
-			this.dialog.fields_dict.scan_serial_no.set_value('');
+			if (existing_row?.length) {
+				frappe.throw(__('Serial No {0} already exists', [scan_serial_no]));
+			}
+
+			if (!this.item.has_batch_no) {
+				this.dialog.fields_dict.entries.df.data.push({
+					serial_no: scan_serial_no
+				});
+
+				this.dialog.fields_dict.scan_serial_no.set_value('');
+			} else {
+				frappe.call({
+					method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.get_batch_no_from_serial_no',
+					args: {
+						serial_no: scan_serial_no,
+					},
+					callback: (r) => {
+						if (r.message) {
+							this.dialog.fields_dict.entries.df.data.push({
+								serial_no: scan_serial_no,
+								batch_no: r.message
+							});
+
+							this.dialog.fields_dict.scan_serial_no.set_value('');
+						}
+					}
+
+				})
+			}
 		} else if (scan_batch_no) {
-			this.dialog.fields_dict.entries.df.data.push({
-				batch_no: scan_batch_no
+			let existing_row = this.dialog.fields_dict.entries.df.data.filter(d => {
+				if (d.batch_no === scan_batch_no) {
+					return d
+				}
 			});
 
+			if (existing_row?.length) {
+				existing_row[0].qty += 1;
+			} else {
+				this.dialog.fields_dict.entries.df.data.push({
+					batch_no: scan_batch_no,
+					qty: 1
+				});
+			}
+
 			this.dialog.fields_dict.scan_batch_no.set_value('');
 		}
 
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 96df0ed..efb9820 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -587,7 +587,8 @@
 		"""
 		select sum(debit) - sum(credit)
 		from `tabGL Entry` where party_type = 'Customer'
-		and party = %s and company=%s {0}""".format(
+		and is_cancelled = 0 and party = %s
+		and company=%s {0}""".format(
 			cond
 		),
 		(customer, company),
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index 88929ea..108a2e0 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -1745,3 +1745,8 @@
 			batches[key].qty += d.qty
 
 	return batches
+
+
+@frappe.whitelist()
+def get_batch_no_from_serial_no(serial_no):
+	return frappe.get_cached_value("Serial No", serial_no, "batch_no")