Merge pull request #30244 from nextchamp-saqib/fix-gl-representation

fix: incorrect debit credit amount in presentation currency
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 9b3b3aa..91c07ad 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -53,7 +53,7 @@
 
 	def on_submit(self):
 		# create the loyalty point ledger entry if the customer is enrolled in any loyalty program
-		if self.loyalty_program:
+		if not self.is_return and self.loyalty_program:
 			self.make_loyalty_point_entry()
 		elif self.is_return and self.return_against and self.loyalty_program:
 			against_psi_doc = frappe.get_doc("POS Invoice", self.return_against)
@@ -87,7 +87,7 @@
 	def on_cancel(self):
 		# run on cancel method of selling controller
 		super(SalesInvoice, self).on_cancel()
-		if self.loyalty_program:
+		if not self.is_return and self.loyalty_program:
 			self.delete_loyalty_point_entry()
 		elif self.is_return and self.return_against and self.loyalty_program:
 			against_psi_doc = frappe.get_doc("POS Invoice", self.return_against)
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 54217fb..bfe72dc 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1411,12 +1411,19 @@
 		frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
 
 	def get_returned_amount(self):
-		returned_amount = frappe.db.sql("""
-			select sum(grand_total)
-			from `tabSales Invoice`
-			where docstatus=1 and is_return=1 and ifnull(return_against, '')=%s
-		""", self.name)
-		return abs(flt(returned_amount[0][0])) if returned_amount else 0
+		from frappe.query_builder.functions import Coalesce, Sum
+		doc = frappe.qb.DocType(self.doctype)
+		returned_amount = (
+			frappe.qb.from_(doc)
+			.select(Sum(doc.grand_total))
+			.where(
+				(doc.docstatus == 1)
+				& (doc.is_return == 1)
+				& (Coalesce(doc.return_against, '') == self.name)
+			)
+		).run()
+
+		return abs(returned_amount[0][0]) if returned_amount[0][0] else 0
 
 	# redeem the loyalty points.
 	def apply_loyalty_points(self):
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index 2e7d306..2c66542 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -402,7 +402,6 @@
 	frappe.local.message_log = []
 
 def set_missing_values(source, target):
-	target.ignore_pricing_rule = 1
 	target.run_method("set_missing_values")
 	target.run_method("calculate_taxes_and_totals")
 
diff --git a/erpnext/e_commerce/product_ui/views.js b/erpnext/e_commerce/product_ui/views.js
index 1b5c440..6dce79d 100644
--- a/erpnext/e_commerce/product_ui/views.js
+++ b/erpnext/e_commerce/product_ui/views.js
@@ -495,7 +495,7 @@
 
 			categories.forEach(category => {
 				sub_group_html += `
-					<a href="${ category.route || '#' }" style="text-decoration: none;">
+					<a href="/${ category.route || '#' }" style="text-decoration: none;">
 						<div class="category-pill">
 							${ category.name }
 						</div>
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 9f4ace2..5f492d7 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -48,7 +48,7 @@
 		self.validate_work_order()
 
 	def set_sub_operations(self):
-		if self.operation:
+		if not self.sub_operations and self.operation:
 			self.sub_operations = []
 			for row in frappe.get_all('Sub Operation',
 				filters = {'parent': self.operation}, fields=['operation', 'idx'], order_by='idx'):
diff --git a/erpnext/manufacturing/doctype/job_card/job_card_list.js b/erpnext/manufacturing/doctype/job_card/job_card_list.js
index 8017209..7f60bdc 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card_list.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card_list.js
@@ -1,4 +1,5 @@
 frappe.listview_settings['Job Card'] = {
+	has_indicator_for_draft: true,
 	get_indicator: function(doc) {
 		if (doc.status === "Work In Progress") {
 			return [__("Work In Progress"), "orange", "status,=,Work In Progress"];
diff --git a/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py b/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py
index 5512543..8cf1037 100644
--- a/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py
+++ b/erpnext/patches/v13_0/create_leave_policy_assignment_based_on_employee_current_leave_policy.py
@@ -6,14 +6,13 @@
 
 
 def execute():
+    frappe.reload_doc('hr', 'doctype', 'leave_policy_assignment')
+    employee_with_assignment = []
+    leave_policy = []
+
     if "leave_policy" in frappe.db.get_table_columns("Employee"):
         employees_with_leave_policy = frappe.db.sql("SELECT name, leave_policy FROM `tabEmployee` WHERE leave_policy IS NOT NULL and leave_policy != ''", as_dict = 1)
 
-        employee_with_assignment = []
-        leave_policy =[]
-
-        #for employee
-
         for employee in employees_with_leave_policy:
             alloc = frappe.db.exists("Leave Allocation", {"employee":employee.name, "leave_policy": employee.leave_policy, "docstatus": 1})
             if not alloc:
@@ -22,12 +21,10 @@
             employee_with_assignment.append(employee.name)
             leave_policy.append(employee.leave_policy)
 
-
-    if "default_leave_policy" in frappe.db.get_table_columns("Employee"):
+    if "default_leave_policy" in frappe.db.get_table_columns("Employee Grade"):
         employee_grade_with_leave_policy = frappe.db.sql("SELECT name, default_leave_policy FROM `tabEmployee Grade` WHERE default_leave_policy IS NOT NULL and default_leave_policy!=''", as_dict = 1)
 
         #for whole employee Grade
-
         for grade in employee_grade_with_leave_policy:
             employees = get_employee_with_grade(grade.name)
             for employee in employees:
@@ -47,13 +44,13 @@
                 allocation_exists=True)
 
 def create_assignment(employee, leave_policy, leave_period=None, allocation_exists = False):
+    if frappe.db.get_value("Leave Policy", leave_policy, "docstatus") == 2:
+        return
 
     filters = {"employee":employee, "leave_policy": leave_policy}
     if leave_period:
         filters["leave_period"] = leave_period
 
-    frappe.reload_doc('hr', 'doctype', 'leave_policy_assignment')
-
     if not frappe.db.exists("Leave Policy Assignment" , filters):
         lpa = frappe.new_doc("Leave Policy Assignment")
         lpa.employee = employee
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index 181a2b5..caaed1f 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -1002,7 +1002,7 @@
 
 		# apply rounding
 		if frappe.get_cached_value("Salary Component", row.salary_component, "round_to_the_nearest_integer"):
-			amount, additional_amount = rounded(amount), rounded(additional_amount)
+			amount, additional_amount = rounded(amount or 0), rounded(additional_amount or 0)
 
 		return amount, additional_amount
 
@@ -1279,9 +1279,9 @@
 	def set_base_totals(self):
 		self.base_gross_pay = flt(self.gross_pay) * flt(self.exchange_rate)
 		self.base_total_deduction = flt(self.total_deduction) * flt(self.exchange_rate)
-		self.rounded_total = rounded(self.net_pay)
+		self.rounded_total = rounded(self.net_pay or 0)
 		self.base_net_pay = flt(self.net_pay) * flt(self.exchange_rate)
-		self.base_rounded_total = rounded(self.base_net_pay)
+		self.base_rounded_total = rounded(self.base_net_pay or 0)
 		self.set_net_total_in_words()
 
 	#calculate total working hours, earnings based on hourly wages and totals
diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss
index 666043b..019496d 100644
--- a/erpnext/public/scss/shopping_cart.scss
+++ b/erpnext/public/scss/shopping_cart.scss
@@ -569,15 +569,12 @@
 }
 
 .scroll-categories {
-	white-space: nowrap;
-	overflow-x: auto;
-
 	.category-pill {
-		margin: 0px 4px;
 		display: inline-block;
-		padding: 6px 12px;
-		background-color: #ecf5fe;
 		width: fit-content;
+		padding: 6px 12px;
+		margin-bottom: 8px;
+		background-color: #ecf5fe;
 		font-size: 14px;
 		border-radius: 18px;
 		color: var(--blue-500);
diff --git a/erpnext/regional/saudi_arabia/utils.py b/erpnext/regional/saudi_arabia/utils.py
index a03c3f0..515862d 100644
--- a/erpnext/regional/saudi_arabia/utils.py
+++ b/erpnext/regional/saudi_arabia/utils.py
@@ -90,7 +90,7 @@
 		tlv_array.append(''.join([tag, length, value]))
 
 		# VAT Amount
-		vat_amount = str(doc.total_taxes_and_charges)
+		vat_amount = str(get_vat_amount(doc))
 
 		tag = bytes([5]).hex()
 		length = bytes([len(vat_amount)]).hex()
@@ -127,6 +127,22 @@
 		doc.db_set('ksa_einv_qr', _file.file_url)
 		doc.notify_update()
 
+def get_vat_amount(doc):
+	vat_settings = frappe.db.get_value('KSA VAT Setting', {'company': doc.company})
+	vat_accounts = []
+	vat_amount = 0
+
+	if vat_settings:
+		vat_settings_doc = frappe.get_cached_doc('KSA VAT Setting', vat_settings)
+
+		for row in vat_settings_doc.get('ksa_vat_sales_accounts'):
+			vat_accounts.append(row.account)
+
+	for tax in doc.get('taxes'):
+		if tax.account_head in vat_accounts:
+			vat_amount += tax.tax_amount
+
+	return vat_amount
 
 def delete_qr_code_file(doc, method=None):
 	region = get_region(doc.company)
diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py
index f350ec4..bdede84 100644
--- a/erpnext/regional/united_arab_emirates/utils.py
+++ b/erpnext/regional/united_arab_emirates/utils.py
@@ -26,9 +26,12 @@
 		elif row.item_code and itemised_tax.get(row.item_code):
 			tax_rate = sum([tax.get('tax_rate', 0) for d, tax in itemised_tax.get(row.item_code).items()])
 
-		row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
-		row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
-		row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
+		meta = frappe.get_meta(row.doctype)
+
+		if meta.has_field('tax_rate'):
+			row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
+			row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
+			row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
 
 def get_account_currency(account):
 	"""Helper function to get account currency."""
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index abbb3c9..94581b6 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -520,7 +520,6 @@
 @frappe.whitelist()
 def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False):
 	def set_missing_values(source, target):
-		target.ignore_pricing_rule = 1
 		target.run_method("set_missing_values")
 		target.run_method("set_po_nos")
 		target.run_method("calculate_taxes_and_totals")
diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py
index 5c7194b..2c53246 100644
--- a/erpnext/setup/doctype/item_group/item_group.py
+++ b/erpnext/setup/doctype/item_group/item_group.py
@@ -132,7 +132,8 @@
 	return frappe.get_all(
 		"Item Group",
 		filters=filters,
-		fields=["name", "route"]
+		fields=["name", "route"],
+		order_by="name"
 	)
 
 def get_child_item_groups(item_group_name):
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 75ccd86..e9ef331 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -445,7 +445,6 @@
 	invoiced_qty_map = get_invoiced_qty_map(source_name)
 
 	def set_missing_values(source, target):
-		target.ignore_pricing_rule = 1
 		target.run_method("set_missing_values")
 		target.run_method("set_po_nos")
 
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 52f10ea..5bb337e 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -762,7 +762,6 @@
 			frappe.throw(_("All items have already been Invoiced/Returned"))
 
 		doc = frappe.get_doc(target)
-		doc.ignore_pricing_rule = 1
 		doc.payment_terms_template = get_payment_terms_template(source.supplier, "Supplier", source.company)
 		doc.run_method("onload")
 		doc.run_method("set_missing_values")