Merge branch 'develop' into manual_depr_entries_develop
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index b6eb3ed..01cfb58 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -550,7 +550,7 @@
 		elif term.due_date_based_on == "Day(s) after the end of the invoice month":
 			due_date = max(due_date, add_days(get_last_day(due_date), term.credit_days))
 		else:
-			due_date = max(due_date, add_months(get_last_day(due_date), term.credit_months))
+			due_date = max(due_date, get_last_day(add_months(due_date, term.credit_months)))
 	return due_date
 
 
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index f0360b2..920486a 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -10,6 +10,7 @@
 from frappe.utils.data import today
 
 from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
+from erpnext.accounts.party import get_due_date_from_template
 from erpnext.buying.doctype.purchase_order.purchase_order import make_inter_company_sales_order
 from erpnext.buying.doctype.purchase_order.purchase_order import (
 	make_purchase_invoice as make_pi_from_po,
@@ -685,6 +686,12 @@
 			else:
 				raise Exception
 
+	def test_default_payment_terms(self):
+		due_date = get_due_date_from_template(
+			"_Test Payment Term Template 1", "2023-02-03", None
+		).strftime("%Y-%m-%d")
+		self.assertEqual(due_date, "2023-03-31")
+
 	def test_terms_are_not_copied_if_automatically_fetch_payment_terms_is_unchecked(self):
 		po = create_purchase_order(do_not_save=1)
 		po.payment_terms_template = "_Test Payment Term Template"
diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js
index 1be528f..b09b715 100644
--- a/erpnext/stock/dashboard/item_dashboard.js
+++ b/erpnext/stock/dashboard/item_dashboard.js
@@ -42,7 +42,7 @@
 			let warehouse = unescape(element.attr('data-warehouse'));
 			let actual_qty = unescape(element.attr('data-actual_qty'));
 			let disable_quick_entry = Number(unescape(element.attr('data-disable_quick_entry')));
-			let entry_type = action === "Move" ? "Material Transfer" : null;
+			let entry_type = action === "Move" ? "Material Transfer" : "Material Receipt";
 
 			if (disable_quick_entry) {
 				open_stock_entry(item, warehouse, entry_type);
@@ -63,11 +63,19 @@
 		function open_stock_entry(item, warehouse, entry_type) {
 			frappe.model.with_doctype('Stock Entry', function () {
 				var doc = frappe.model.get_new_doc('Stock Entry');
-				if (entry_type) doc.stock_entry_type = entry_type;
+				if (entry_type) {
+					doc.stock_entry_type = entry_type;
+				}
 
 				var row = frappe.model.add_child(doc, 'items');
 				row.item_code = item;
-				row.s_warehouse = warehouse;
+
+				if (entry_type === "Material Transfer") {
+					row.s_warehouse = warehouse;
+				}
+				else {
+					row.t_warehouse = warehouse;
+				}
 
 				frappe.set_route('Form', doc.doctype, doc.name);
 			});
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 38bf0a5..cc06bd7 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -1662,6 +1662,48 @@
 
 		self.assertRaises(BatchExpiredError, se.save)
 
+	def test_negative_stock_reco(self):
+		from erpnext.controllers.stock_controller import BatchExpiredError
+		from erpnext.stock.doctype.batch.test_batch import make_new_batch
+
+		frappe.db.set_single_value("Stock Settings", "allow_negative_stock", 0)
+
+		item_code = "Test Negative Item - 001"
+		item_doc = create_item(item_code=item_code, is_stock_item=1, valuation_rate=10)
+
+		make_stock_entry(
+			item_code=item_code,
+			posting_date=add_days(today(), -3),
+			posting_time="00:00:00",
+			purpose="Material Receipt",
+			qty=10,
+			to_warehouse="_Test Warehouse - _TC",
+			do_not_save=True,
+		)
+
+		make_stock_entry(
+			item_code=item_code,
+			posting_date=today(),
+			posting_time="00:00:00",
+			purpose="Material Receipt",
+			qty=8,
+			from_warehouse="_Test Warehouse - _TC",
+			do_not_save=True,
+		)
+
+		sr_doc = create_stock_reconciliation(
+			purpose="Stock Reconciliation",
+			posting_date=add_days(today(), -3),
+			posting_time="00:00:00",
+			item_code=item_code,
+			warehouse="_Test Warehouse - _TC",
+			valuation_rate=10,
+			qty=7,
+			do_not_submit=True,
+		)
+
+		self.assertRaises(frappe.ValidationError, sr_doc.submit)
+
 
 def make_serialized_item(**args):
 	args = frappe._dict(args)
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index d8b12ed..08fc6fb 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -1050,7 +1050,7 @@
 			frappe.db.set_value("Bin", bin_name, updated_values, update_modified=True)
 
 
-def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
+def get_previous_sle_of_current_voucher(args, operator="<", exclude_current_voucher=False):
 	"""get stock ledger entries filtered by specific posting datetime conditions"""
 
 	args["time_format"] = "%H:%i:%s"
@@ -1076,13 +1076,13 @@
 				posting_date < %(posting_date)s or
 				(
 					posting_date = %(posting_date)s and
-					time_format(posting_time, %(time_format)s) < time_format(%(posting_time)s, %(time_format)s)
+					time_format(posting_time, %(time_format)s) {operator} time_format(%(posting_time)s, %(time_format)s)
 				)
 			)
 		order by timestamp(posting_date, posting_time) desc, creation desc
 		limit 1
 		for update""".format(
-			voucher_condition=voucher_condition
+			operator=operator, voucher_condition=voucher_condition
 		),
 		args,
 		as_dict=1,
@@ -1375,7 +1375,7 @@
 			stock_reco_qty_shift = flt(args.actual_qty)
 	else:
 		# reco is being submitted
-		last_balance = get_previous_sle_of_current_voucher(args, exclude_current_voucher=True).get(
+		last_balance = get_previous_sle_of_current_voucher(args, "<=", exclude_current_voucher=True).get(
 			"qty_after_transaction"
 		)