feat: auto create advance entry on invoice submission
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index a16795e..e2f02f3 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -415,6 +415,8 @@
 		self.update_project()
 		update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
 
+		self.process_common_party_accounting()
+
 	def make_gl_entries(self, gl_entries=None, from_repost=False):
 		if not gl_entries:
 			gl_entries = self.get_gl_entries()
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 5fa6228..f29f7be 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -253,6 +253,8 @@
 		if "Healthcare" in active_domains:
 			manage_invoice_submit_cancel(self, "on_submit")
 
+		self.process_common_party_accounting()
+
 	def validate_pos_return(self):
 
 		if self.is_pos and self.is_return:
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 4c243d0..1b23289 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -14,7 +14,7 @@
 from erpnext.utilities.transaction_base import TransactionBase
 from erpnext.buying.utils import update_last_purchase_rate
 from erpnext.controllers.sales_and_purchase_return import validate_return
-from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled
+from erpnext.accounts.party import get_party_account_currency, validate_party_frozen_disabled, get_party_account
 from erpnext.accounts.doctype.pricing_rule.utils import (apply_pricing_rule_on_transaction,
 	apply_pricing_rule_for_free_items, get_applied_pricing_rules)
 from erpnext.exceptions import InvalidCurrency
@@ -1367,6 +1367,66 @@
 
 		return False
 
+	def process_common_party_accounting(self):
+		is_invoice = self.doctype in ['Sales Invoice', 'Purchase Invoice']
+		if not is_invoice:
+			return
+
+		if frappe.db.get_single_value('Accounts Settings', 'enable_common_party_accounting'):
+			party_link = self.get_common_party_link()
+			if party_link and self.outstanding_amount:
+				self.create_advance_and_reconcile(party_link)
+
+	def get_common_party_link(self):
+		party_type, party = self.get_party()
+		party_link = frappe.db.exists('Party Link', {'secondary_role': party_type, 'secondary_party': party})
+		if party_link:
+			return frappe.db.get_value('Party Link', party_link, ['primary_role', 'primary_party'], as_dict=True)
+
+	def create_advance_and_reconcile(self, party_link):
+		secondary_party_type, secondary_party = self.get_party()
+		primary_party_type, primary_party = party_link.primary_role, party_link.primary_party
+
+		primary_account = get_party_account(primary_party_type, primary_party, self.company)
+		secondary_account = get_party_account(secondary_party_type, secondary_party, self.company)
+
+		jv = frappe.new_doc('Journal Entry')
+		jv.voucher_type = 'Journal Entry'
+		jv.naming_series = 'ACC-JV-.YYYY.-'
+		jv.posting_date = self.posting_date
+		jv.company = self.company
+		jv.remark = 'Adjustment for {} {}'.format(self.doctype, self.name)
+
+		reconcilation_entry = frappe._dict()
+		advance_entry = frappe._dict()
+		cost_center = erpnext.get_default_cost_center(self.company)
+
+		reconcilation_entry.account = secondary_account
+		reconcilation_entry.party_type = secondary_party_type
+		reconcilation_entry.party = secondary_party
+		reconcilation_entry.reference_type = self.doctype
+		reconcilation_entry.reference_name = self.name
+		reconcilation_entry.cost_center = cost_center
+
+		advance_entry.account = primary_account
+		advance_entry.party_type = primary_party_type
+		advance_entry.party = primary_party
+		advance_entry.cost_center = cost_center
+		advance_entry.is_advance = 'Yes'
+
+		if self.doctype == 'Sales Invoice':
+			reconcilation_entry.credit_in_account_currency = self.outstanding_amount
+			advance_entry.debit_in_account_currency = self.outstanding_amount
+		else:
+			advance_entry.credit_in_account_currency = self.outstanding_amount
+			reconcilation_entry.debit_in_account_currency = self.outstanding_amount
+
+		jv.append('accounts', reconcilation_entry)
+		jv.append('accounts', advance_entry)
+
+		jv.save()
+		jv.submit()
+
 @frappe.whitelist()
 def get_tax_rate(account_head):
 	return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True)