Merge pull request #40725 from barredterra/in-context-translation

diff --git a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js
index e3f90f2..0e84f88 100644
--- a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js
+++ b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js
@@ -1,8 +1,4 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-frappe.require("assets/erpnext/js/purchase_trends_filters.js", function () {
-	frappe.query_reports["Purchase Invoice Trends"] = {
-		filters: erpnext.get_purchase_trends_filters(),
-	};
-});
+frappe.query_reports["Purchase Invoice Trends"] = $.extend({}, erpnext.purchase_trends_filters);
diff --git a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js
index 292d827..bdc39f3 100644
--- a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js
+++ b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js
@@ -1,8 +1,4 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-frappe.require("assets/erpnext/js/sales_trends_filters.js", function () {
-	frappe.query_reports["Sales Invoice Trends"] = {
-		filters: erpnext.get_sales_trends_filters(),
-	};
-});
+frappe.query_reports["Sales Invoice Trends"] = $.extend({}, erpnext.sales_trends_filters);
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 7d98190..210c730 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -462,7 +462,7 @@
   },
   {
    "fieldname": "other_charges_calculation",
-   "fieldtype": "Markdown Editor",
+   "fieldtype": "Text Editor",
    "label": "Taxes and Charges Calculation",
    "no_copy": 1,
    "oldfieldtype": "HTML",
@@ -928,7 +928,7 @@
  "index_web_pages_for_search": 1,
  "is_submittable": 1,
  "links": [],
- "modified": "2024-03-27 13:10:49.116641",
+ "modified": "2024-03-28 10:20:30.231915",
  "modified_by": "Administrator",
  "module": "Buying",
  "name": "Supplier Quotation",
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
index 456ed8e..0829b27 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -71,7 +71,7 @@
 		naming_series: DF.Literal["PUR-SQTN-.YYYY.-"]
 		net_total: DF.Currency
 		opportunity: DF.Link | None
-		other_charges_calculation: DF.MarkdownEditor | None
+		other_charges_calculation: DF.TextEditor | None
 		plc_conversion_rate: DF.Float
 		price_list_currency: DF.Link | None
 		pricing_rules: DF.Table[PricingRuleDetail]
diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js
index 366fff1..56684a8 100644
--- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js
+++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js
@@ -1,8 +1,4 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-frappe.require("assets/erpnext/js/purchase_trends_filters.js", function () {
-	frappe.query_reports["Purchase Order Trends"] = {
-		filters: erpnext.get_purchase_trends_filters(),
-	};
-});
+frappe.query_reports["Purchase Order Trends"] = $.extend({}, erpnext.purchase_trends_filters);
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 5f11c59..5916f2e 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -907,7 +907,7 @@
 				self.validate_multi_currency()
 				self.validate_packed_items()
 
-				if self.get("is_internal_supplier"):
+				if self.get("is_internal_supplier") and self.docstatus == 1:
 					self.validate_internal_transfer_qty()
 			else:
 				self.validate_internal_transfer_warehouse()
diff --git a/erpnext/patches/v14_0/update_flag_for_return_invoices.py b/erpnext/patches/v14_0/update_flag_for_return_invoices.py
index ca1b296..2136d55 100644
--- a/erpnext/patches/v14_0/update_flag_for_return_invoices.py
+++ b/erpnext/patches/v14_0/update_flag_for_return_invoices.py
@@ -22,7 +22,7 @@
 		.where(
 			(si.creation.gte(creation_date))
 			& (si.docstatus == 1)
-			& (si.is_return is True)
+			& (si.is_return.eq(True))
 			& (si.return_against.notnull())
 		)
 		.run()
@@ -51,7 +51,7 @@
 		.where(
 			(pi.creation.gte(creation_date))
 			& (pi.docstatus == 1)
-			& (pi.is_return is True)
+			& (pi.is_return.eq(True))
 			& (pi.return_against.notnull())
 		)
 		.run()
diff --git a/erpnext/public/js/erpnext.bundle.js b/erpnext/public/js/erpnext.bundle.js
index b75a21b..9dd6f24 100644
--- a/erpnext/public/js/erpnext.bundle.js
+++ b/erpnext/public/js/erpnext.bundle.js
@@ -33,5 +33,7 @@
 import "./controllers/buying.js";
 import "./utils/demo.js";
 import "./financial_statements.js";
+import "./sales_trends_filters.js";
+import "./purchase_trends_filters.js";
 
 // import { sum } from 'frappe/public/utils/util.js'
diff --git a/erpnext/public/js/purchase_trends_filters.js b/erpnext/public/js/purchase_trends_filters.js
index 14ffaf8..75428d3 100644
--- a/erpnext/public/js/purchase_trends_filters.js
+++ b/erpnext/public/js/purchase_trends_filters.js
@@ -1,8 +1,8 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-erpnext.get_purchase_trends_filters = function () {
-	return [
+erpnext.purchase_trends_filters = {
+	filters: [
 		{
 			fieldname: "company",
 			label: __("Company"),
@@ -63,5 +63,5 @@
 			options: ["", { value: "Item", label: __("Item") }, { value: "Supplier", label: __("Supplier") }],
 			default: "",
 		},
-	];
+	],
 };
diff --git a/erpnext/public/js/sales_trends_filters.js b/erpnext/public/js/sales_trends_filters.js
index 85daa01..2f8e6f9 100644
--- a/erpnext/public/js/sales_trends_filters.js
+++ b/erpnext/public/js/sales_trends_filters.js
@@ -1,8 +1,8 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-erpnext.get_sales_trends_filters = function () {
-	return [
+erpnext.sales_trends_filters = {
+	filters: [
 		{
 			fieldname: "period",
 			label: __("Period"),
@@ -53,5 +53,5 @@
 			options: "Company",
 			default: frappe.defaults.get_user_default("Company"),
 		},
-	];
+	],
 };
diff --git a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py
index 6acc060..84da765 100644
--- a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py
+++ b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py
@@ -62,10 +62,10 @@
 	or_conditions = []
 	if items:
 		and_conditions.append(ip.item_code.isin([x.item_code for x in items]))
-		and_conditions.append(ip.selling is True)
+		and_conditions.append(ip.selling.eq(True))
 
-		or_conditions.append(ip.customer is None)
-		or_conditions.append(ip.price_list is None)
+		or_conditions.append(ip.customer.isnull())
+		or_conditions.append(ip.price_list.isnull())
 
 		if customer:
 			or_conditions.append(ip.customer == customer)
diff --git a/erpnext/selling/report/quotation_trends/quotation_trends.js b/erpnext/selling/report/quotation_trends/quotation_trends.js
index 8ffeda4..ff0b308 100644
--- a/erpnext/selling/report/quotation_trends/quotation_trends.js
+++ b/erpnext/selling/report/quotation_trends/quotation_trends.js
@@ -1,8 +1,4 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-frappe.require("assets/erpnext/js/sales_trends_filters.js", function () {
-	frappe.query_reports["Quotation Trends"] = {
-		filters: erpnext.get_sales_trends_filters(),
-	};
-});
+frappe.query_reports["Quotation Trends"] = $.extend({}, erpnext.sales_trends_filters);
diff --git a/erpnext/selling/report/sales_order_trends/sales_order_trends.js b/erpnext/selling/report/sales_order_trends/sales_order_trends.js
index fe38804..28bd550 100644
--- a/erpnext/selling/report/sales_order_trends/sales_order_trends.js
+++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.js
@@ -1,8 +1,4 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-frappe.require("assets/erpnext/js/sales_trends_filters.js", function () {
-	frappe.query_reports["Sales Order Trends"] = {
-		filters: erpnext.get_sales_trends_filters(),
-	};
-});
+frappe.query_reports["Sales Order Trends"] = $.extend({}, erpnext.sales_trends_filters);
diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
index 18e4c7d..c9c3c83 100644
--- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
+++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py
@@ -414,17 +414,11 @@
 			frappe.delete_doc("Communication", batch, ignore_permissions=True)
 
 	def delete_comments(self, doctype, reference_doc_names):
-		comments = frappe.get_all(
-			"Comment",
-			filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]},
-		)
-		comment_names = [c.name for c in comments]
-
-		if not comment_names:
-			return
-
-		for batch in create_batch(comment_names, self.batch_size):
-			frappe.delete_doc("Comment", batch, ignore_permissions=True)
+		if reference_doc_names:
+			comment = qb.DocType("Comment")
+			qb.from_(comment).delete().where(
+				(comment.reference_doctype == doctype) & (comment.reference_name.isin(reference_doc_names))
+			).run()
 
 	def unlink_attachments(self, doctype, reference_doc_names):
 		files = frappe.get_all(
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index c3148d6..7144908 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -1302,6 +1302,9 @@
 			for tax in get_taxes_and_charges(master_doctype, target.get("taxes_and_charges")):
 				target.append("taxes", tax)
 
+		if not target.get("items"):
+			frappe.throw(_("All items have already been received"))
+
 	def update_details(source_doc, target_doc, source_parent):
 		target_doc.inter_company_invoice_reference = source_doc.name
 		if target_doc.doctype == "Purchase Receipt":
@@ -1357,6 +1360,10 @@
 				shipping_address_name=target_doc.shipping_address_name,
 			)
 
+	def update_item(source, target, source_parent):
+		if source_parent.doctype == "Delivery Note" and source.received_qty:
+			target.qty = flt(source.qty) + flt(source.returned_qty) - flt(source.received_qty)
+
 	doclist = get_mapped_doc(
 		doctype,
 		source_name,
@@ -1377,6 +1384,8 @@
 					"Material_request_item": "material_request_item",
 				},
 				"field_no_map": ["warehouse"],
+				"condition": lambda item: item.received_qty < item.qty + item.returned_qty,
+				"postprocess": update_item,
 			},
 		},
 		target_doc,
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 6f47c26..499a4a6 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -470,6 +470,98 @@
 
 		self.assertEqual(return_dn.items[0].incoming_rate, 150)
 
+	def test_sales_return_against_serial_batch_bundle(self):
+		frappe.db.set_single_value(
+			"Stock Settings", "do_not_update_serial_batch_on_creation_of_auto_bundle", 1
+		)
+
+		batch_item = make_item(
+			"Test Sales Return Against Batch Item",
+			properties={
+				"has_batch_no": 1,
+				"is_stock_item": 1,
+				"create_new_batch": 1,
+				"batch_number_series": "BATCH-TSRABII.#####",
+			},
+		).name
+
+		serial_item = make_item(
+			"Test Sales Return Against Serial NO Item",
+			properties={
+				"has_serial_no": 1,
+				"is_stock_item": 1,
+				"serial_no_series": "SN-TSRABII.#####",
+			},
+		).name
+
+		make_stock_entry(item_code=batch_item, target="_Test Warehouse - _TC", qty=5, basic_rate=100)
+		make_stock_entry(item_code=serial_item, target="_Test Warehouse - _TC", qty=5, basic_rate=100)
+
+		dn = create_delivery_note(
+			item_code=batch_item,
+			qty=5,
+			rate=500,
+			warehouse="_Test Warehouse - _TC",
+			expense_account="Cost of Goods Sold - _TC",
+			cost_center="Main - _TC",
+			use_serial_batch_fields=0,
+			do_not_submit=1,
+		)
+
+		dn.append(
+			"items",
+			{
+				"item_code": serial_item,
+				"qty": 5,
+				"rate": 500,
+				"warehouse": "_Test Warehouse - _TC",
+				"expense_account": "Cost of Goods Sold - _TC",
+				"cost_center": "Main - _TC",
+				"use_serial_batch_fields": 0,
+			},
+		)
+
+		dn.save()
+		for row in dn.items:
+			self.assertFalse(row.use_serial_batch_fields)
+
+		dn.submit()
+		dn.reload()
+		for row in dn.items:
+			self.assertTrue(row.serial_and_batch_bundle)
+			self.assertFalse(row.use_serial_batch_fields)
+			self.assertFalse(row.serial_no)
+			self.assertFalse(row.batch_no)
+
+		from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
+		return_dn = make_return_doc(dn.doctype, dn.name)
+		for row in return_dn.items:
+			row.qty = -2
+			row.use_serial_batch_fields = 0
+		return_dn.save().submit()
+
+		for row in return_dn.items:
+			total_qty = frappe.db.get_value(
+				"Serial and Batch Bundle", row.serial_and_batch_bundle, "total_qty"
+			)
+
+			self.assertEqual(total_qty, 2)
+
+			doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle)
+			if doc.has_serial_no:
+				self.assertEqual(len(doc.entries), 2)
+
+			for entry in doc.entries:
+				if doc.has_batch_no:
+					self.assertEqual(entry.qty, 2)
+				else:
+					self.assertEqual(entry.qty, 1)
+
+		frappe.db.set_single_value(
+			"Stock Settings", "do_not_update_serial_batch_on_creation_of_auto_bundle", 0
+		)
+
 	def test_return_single_item_from_bundled_items(self):
 		company = frappe.db.get_value("Warehouse", "Stores - TCP1", "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 1ae34d0..7503531 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
@@ -375,6 +375,9 @@
 		if self.docstatus == 0:
 			self.set_incoming_rate(save=True, row=row)
 
+		if self.docstatus == 0 and parent.get("is_return") and parent.is_new():
+			self.reset_qty(row, qty_field=qty_field)
+
 		self.calculate_qty_and_amount(save=True)
 		self.validate_quantity(row, qty_field=qty_field)
 		self.set_warranty_expiry_date()
@@ -408,7 +411,11 @@
 		if not (self.voucher_type and self.voucher_no):
 			return
 
-		if self.voucher_no and not frappe.db.exists(self.voucher_type, self.voucher_no):
+		if (
+			self.docstatus == 1
+			and self.voucher_no
+			and not frappe.db.exists(self.voucher_type, self.voucher_no)
+		):
 			self.throw_error_message(f"The {self.voucher_type} # {self.voucher_no} does not exist")
 
 		if self.flags.ignore_voucher_validation:
@@ -472,24 +479,57 @@
 
 			frappe.throw(_(msg), title=_(title), exc=SerialNoExistsInFutureTransactionError)
 
+	def reset_qty(self, row, qty_field=None):
+		qty_field = self.get_qty_field(row, qty_field=qty_field)
+		qty = abs(row.get(qty_field))
+
+		idx = None
+		while qty > 0:
+			for d in self.entries:
+				row_qty = abs(d.qty)
+				if row_qty >= qty:
+					d.db_set("qty", qty if self.type_of_transaction == "Inward" else qty * -1)
+					qty = 0
+					idx = d.idx
+					break
+				else:
+					qty -= row_qty
+					idx = d.idx
+
+		if idx and len(self.entries) > idx:
+			remove_rows = []
+			for d in self.entries:
+				if d.idx > idx:
+					remove_rows.append(d)
+
+			for d in remove_rows:
+				self.entries.remove(d)
+
+			self.flags.ignore_links = True
+			self.save()
+
 	def validate_quantity(self, row, qty_field=None):
+		qty_field = self.get_qty_field(row, qty_field=qty_field)
+		qty = row.get(qty_field)
+		if qty_field == "qty" and row.get("stock_qty"):
+			qty = row.get("stock_qty")
+
+		precision = row.precision
+		if abs(abs(flt(self.total_qty, precision)) - abs(flt(qty, precision))) > 0.01:
+			self.throw_error_message(
+				f"Total quantity {abs(flt(self.total_qty))} in the Serial and Batch Bundle {bold(self.name)} does not match with the quantity {abs(flt(row.get(qty_field)))} for the Item {bold(self.item_code)} in the {self.voucher_type} # {self.voucher_no}"
+			)
+
+	def get_qty_field(self, row, qty_field=None) -> str:
 		if not qty_field:
 			qty_field = "qty"
 
-		precision = row.precision
 		if row.get("doctype") == "Subcontracting Receipt Supplied Item":
 			qty_field = "consumed_qty"
 		elif row.get("doctype") == "Stock Entry Detail":
 			qty_field = "transfer_qty"
 
-		qty = row.get(qty_field)
-		if qty_field == "qty" and row.get("stock_qty"):
-			qty = row.get("stock_qty")
-
-		if abs(abs(flt(self.total_qty, precision)) - abs(flt(qty, precision))) > 0.01:
-			self.throw_error_message(
-				f"Total quantity {abs(flt(self.total_qty))} in the Serial and Batch Bundle {bold(self.name)} does not match with the quantity {abs(flt(row.get(qty_field)))} for the Item {bold(self.item_code)} in the {self.voucher_type} # {self.voucher_no}"
-			)
+		return qty_field
 
 	def set_is_outward(self):
 		for row in self.entries:
diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js
index 5e7dc8b..67cf0ca 100644
--- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js
+++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js
@@ -1,8 +1,4 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-frappe.require("assets/erpnext/js/sales_trends_filters.js", function () {
-	frappe.query_reports["Delivery Note Trends"] = {
-		filters: erpnext.get_sales_trends_filters(),
-	};
-});
+frappe.query_reports["Delivery Note Trends"] = $.extend({}, erpnext.sales_trends_filters);
diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js
index bddfe5d..8a293e6 100644
--- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js
+++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js
@@ -1,8 +1,4 @@
 // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
 // License: GNU General Public License v3. See license.txt
 
-frappe.require("assets/erpnext/js/purchase_trends_filters.js", function () {
-	frappe.query_reports["Purchase Receipt Trends"] = {
-		filters: erpnext.get_purchase_trends_filters(),
-	};
-});
+frappe.query_reports["Purchase Receipt Trends"] = $.extend({}, erpnext.purchase_trends_filters);
diff --git a/erpnext/support/web_form/issues/issues.json b/erpnext/support/web_form/issues/issues.json
index 1df9fb7..f3834c7 100644
--- a/erpnext/support/web_form/issues/issues.json
+++ b/erpnext/support/web_form/issues/issues.json
@@ -1,14 +1,14 @@
 {
- "accept_payment": 0,
  "allow_comments": 1,
  "allow_delete": 1,
  "allow_edit": 1,
  "allow_incomplete": 0,
  "allow_multiple": 1,
  "allow_print": 0,
- "amount": 0.0,
- "amount_based_on_field": 0,
+ "anonymous": 0,
+ "apply_document_permissions": 0,
  "breadcrumbs": "[{\"label\":_(\"Issues\"), \"route\":\"issues\"}]",
+ "condition_json": "[]",
  "creation": "2016-06-24 15:50:33.186483",
  "doc_type": "Issue",
  "docstatus": 0,
@@ -16,20 +16,19 @@
  "idx": 0,
  "introduction_text": "",
  "is_standard": 1,
+ "list_columns": [],
  "login_required": 1,
  "max_attachment_size": 0,
- "modified": "2020-05-19 13:01:10.729088",
+ "modified": "2024-03-27 16:16:03.621730",
  "modified_by": "Administrator",
  "module": "Support",
  "name": "issues",
  "owner": "Administrator",
  "published": 1,
  "route": "issues",
- "route_to_success_link": 0,
  "show_attachments": 0,
- "show_in_grid": 0,
+ "show_list": 1,
  "show_sidebar": 1,
- "sidebar_items": [],
  "success_message": "",
  "success_url": "/issues",
  "title": "Issue",