Merge pull request #37554 from frappe/e_com_perms

fix: e-commerce permissions for address
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 3a2c3cb..8f76492 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -302,3 +302,30 @@
 		default_dimensions_map[dimension.company][dimension.fieldname] = dimension.default_dimension
 
 	return dimension_filters, default_dimensions_map
+
+
+def create_accounting_dimensions_for_doctype(doctype):
+	accounting_dimensions = frappe.db.get_all(
+		"Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"]
+	)
+
+	if not accounting_dimensions:
+		return
+
+	for d in accounting_dimensions:
+		field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
+
+		if field:
+			continue
+
+		df = {
+			"fieldname": d.fieldname,
+			"label": d.label,
+			"fieldtype": "Link",
+			"options": d.document_type,
+			"insert_after": "accounting_dimensions_section",
+		}
+
+		create_custom_field(doctype, df, ignore_validate=True)
+
+	frappe.clear_cache(doctype=doctype)
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index e3b671f..b9c7a0b 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -116,7 +116,7 @@
 		# build all keys, since we want to exclude vouchers beyond the report date
 		for ple in self.ple_entries:
 			# get the balance object for voucher_type
-			key = (ple.voucher_type, ple.voucher_no, ple.party)
+			key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party)
 			if not key in self.voucher_balance:
 				self.voucher_balance[key] = frappe._dict(
 					voucher_type=ple.voucher_type,
@@ -183,7 +183,7 @@
 			):
 				return
 
-		key = (ple.against_voucher_type, ple.against_voucher_no, ple.party)
+		key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party)
 
 		# If payment is made against credit note
 		# and credit note is made against a Sales Invoice
@@ -192,13 +192,13 @@
 			if ple.against_voucher_no in self.return_entries:
 				return_against = self.return_entries.get(ple.against_voucher_no)
 				if return_against:
-					key = (ple.against_voucher_type, return_against, ple.party)
+					key = (ple.account, ple.against_voucher_type, return_against, ple.party)
 
 		row = self.voucher_balance.get(key)
 
 		if not row:
 			# no invoice, this is an invoice / stand-alone payment / credit note
-			row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party))
+			row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party))
 
 		row.party_type = ple.party_type
 		return row
diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
index 4307689..cbeb6d3 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -1,6 +1,7 @@
 import unittest
 
 import frappe
+from frappe import qb
 from frappe.tests.utils import FrappeTestCase, change_settings
 from frappe.utils import add_days, flt, getdate, today
 
@@ -23,29 +24,6 @@
 	def tearDown(self):
 		frappe.db.rollback()
 
-	def create_usd_account(self):
-		name = "Debtors USD"
-		exists = frappe.db.get_list(
-			"Account", filters={"company": "_Test Company 2", "account_name": "Debtors USD"}
-		)
-		if exists:
-			self.debtors_usd = exists[0].name
-		else:
-			debtors = frappe.get_doc(
-				"Account",
-				frappe.db.get_list(
-					"Account", filters={"company": "_Test Company 2", "account_name": "Debtors"}
-				)[0].name,
-			)
-
-			debtors_usd = frappe.new_doc("Account")
-			debtors_usd.company = debtors.company
-			debtors_usd.account_name = "Debtors USD"
-			debtors_usd.account_currency = "USD"
-			debtors_usd.parent_account = debtors.parent_account
-			debtors_usd.account_type = debtors.account_type
-			self.debtors_usd = debtors_usd.save().name
-
 	def create_sales_invoice(self, no_payment_schedule=False, do_not_submit=False):
 		frappe.set_user("Administrator")
 		si = create_sales_invoice(
@@ -643,3 +621,94 @@
 		self.assertEqual(len(report[1]), 2)
 		output_for = set([x.party for x in report[1]])
 		self.assertEqual(output_for, expected_output)
+
+	def test_report_output_if_party_is_missing(self):
+		acc_name = "Additional Debtors"
+		if not frappe.db.get_value(
+			"Account", filters={"account_name": acc_name, "company": self.company}
+		):
+			additional_receivable_acc = frappe.get_doc(
+				{
+					"doctype": "Account",
+					"account_name": acc_name,
+					"parent_account": "Accounts Receivable - " + self.company_abbr,
+					"company": self.company,
+					"account_type": "Receivable",
+				}
+			).save()
+			self.debtors2 = additional_receivable_acc.name
+
+		je = frappe.new_doc("Journal Entry")
+		je.company = self.company
+		je.posting_date = today()
+		je.append(
+			"accounts",
+			{
+				"account": self.debit_to,
+				"party_type": "Customer",
+				"party": self.customer,
+				"debit_in_account_currency": 150,
+				"credit_in_account_currency": 0,
+				"cost_center": self.cost_center,
+			},
+		)
+		je.append(
+			"accounts",
+			{
+				"account": self.debtors2,
+				"party_type": "Customer",
+				"party": self.customer,
+				"debit_in_account_currency": 200,
+				"credit_in_account_currency": 0,
+				"cost_center": self.cost_center,
+			},
+		)
+		je.append(
+			"accounts",
+			{
+				"account": self.cash,
+				"debit_in_account_currency": 0,
+				"credit_in_account_currency": 350,
+				"cost_center": self.cost_center,
+			},
+		)
+		je.save().submit()
+
+		# manually remove party from Payment Ledger
+		ple = qb.DocType("Payment Ledger Entry")
+		qb.update(ple).set(ple.party, None).where(ple.voucher_no == je.name).run()
+
+		filters = {
+			"company": self.company,
+			"report_date": today(),
+			"range1": 30,
+			"range2": 60,
+			"range3": 90,
+			"range4": 120,
+		}
+
+		report_ouput = execute(filters)[1]
+		expected_data = [
+			[self.debtors2, je.doctype, je.name, "Customer", self.customer, 200.0, 0.0, 0.0, 200.0],
+			[self.debit_to, je.doctype, je.name, "Customer", self.customer, 150.0, 0.0, 0.0, 150.0],
+		]
+		self.assertEqual(len(report_ouput), 2)
+		# fetch only required fields
+		report_output = [
+			[
+				x.party_account,
+				x.voucher_type,
+				x.voucher_no,
+				"Customer",
+				self.customer,
+				x.invoiced,
+				x.paid,
+				x.credit_note,
+				x.outstanding,
+			]
+			for x in report_ouput
+		]
+		# use account name to sort
+		# post sorting output should be [[Additional Debtors, ...], [Debtors, ...]]
+		report_output = sorted(report_output, key=lambda x: x[0])
+		self.assertEqual(expected_data, report_output)
diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
index e10c0e2..b6e4630 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py
@@ -6,7 +6,7 @@
 
 import frappe
 from frappe import _
-from frappe.query_builder.functions import IfNull
+from frappe.query_builder.functions import IfNull, Sum
 from frappe.utils import date_diff, flt, getdate
 
 
@@ -57,7 +57,7 @@
 			po_item.qty,
 			po_item.received_qty,
 			(po_item.qty - po_item.received_qty).as_("pending_qty"),
-			IfNull(pi_item.qty, 0).as_("billed_qty"),
+			Sum(IfNull(pi_item.qty, 0)).as_("billed_qty"),
 			po_item.base_amount.as_("amount"),
 			(po_item.received_qty * po_item.base_rate).as_("received_qty_amount"),
 			(po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"),
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 7446f2c..5483a10 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -508,6 +508,7 @@
 	"Sales Invoice Item",
 	"Purchase Invoice Item",
 	"Purchase Order Item",
+	"Sales Order Item",
 	"Journal Entry Account",
 	"Material Request Item",
 	"Delivery Note Item",
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index aebad55..d59fe0e 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -339,5 +339,6 @@
 erpnext.patches.v14_0.update_invoicing_period_in_subscription
 execute:frappe.delete_doc("Page", "welcome-to-erpnext")
 erpnext.patches.v15_0.delete_payment_gateway_doctypes
+erpnext.patches.v14_0.create_accounting_dimensions_in_sales_order_item
 # below migration patch should always run last
 erpnext.patches.v14_0.migrate_gl_to_payment_ledger
\ No newline at end of file
diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_in_sales_order_item.py b/erpnext/patches/v14_0/create_accounting_dimensions_in_sales_order_item.py
new file mode 100644
index 0000000..8f77c35
--- /dev/null
+++ b/erpnext/patches/v14_0/create_accounting_dimensions_in_sales_order_item.py
@@ -0,0 +1,7 @@
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
+	create_accounting_dimensions_for_doctype,
+)
+
+
+def execute():
+	create_accounting_dimensions_for_doctype(doctype="Sales Order Item")
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index e6f7456..f82047f 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -68,6 +68,7 @@
   "total_weight",
   "column_break_21",
   "weight_uom",
+  "accounting_dimensions_section",
   "warehouse_and_reference",
   "warehouse",
   "target_warehouse",
@@ -889,12 +890,18 @@
    "label": "Production Plan Qty",
    "no_copy": 1,
    "read_only": 1
+  },
+  {
+   "collapsible": 1,
+   "fieldname": "accounting_dimensions_section",
+   "fieldtype": "Section Break",
+   "label": "Accounting Dimensions"
   }
  ],
  "idx": 1,
  "istable": 1,
  "links": [],
- "modified": "2023-07-28 14:56:42.031636",
+ "modified": "2023-10-17 18:18:26.475259",
  "modified_by": "Administrator",
  "module": "Selling",
  "name": "Sales Order Item",
@@ -905,4 +912,4 @@
  "sort_order": "DESC",
  "states": [],
  "track_changes": 1
-}
+}
\ No newline at end of file