Skip to content

fix(general-ledger): add CPA support via Party Link cross-party GL filtering#54657

Draft
shubhdoshi21 wants to merge 3 commits intofrappe:developfrom
shubhdoshi21:gl-common-party-link-condition
Draft

fix(general-ledger): add CPA support via Party Link cross-party GL filtering#54657
shubhdoshi21 wants to merge 3 commits intofrappe:developfrom
shubhdoshi21:gl-common-party-link-condition

Conversation

@shubhdoshi21
Copy link
Copy Markdown
Contributor

@shubhdoshi21 shubhdoshi21 commented Apr 29, 2026

ERPNext supports linking a Customer and Supplier as the same real-world entity via Party Link (Common Party Accounting). When such a link exists, filtering the General Ledger by the Customer should also include that entity's Supplier-side entries, and vice versa.

Before this change, the party filter used a plain party IN (...) SQL condition. Filtering by Customer returned only the Customer's GL entries — the linked Supplier's GL entries were not included. Users had to run two separate reports and reconcile them manually.

After this change, filtering the General Ledger by either party automatically includes GL entries posted under both the Customer and the linked Supplier.

Also adds a guard in validate_party — passing party without party_type previously silently returned empty data; it now throws a clear validation error.

Test coverage (3 cases):

  • test_cpa_includes_linked_party_entries — filters the GL by each side of a Party Link independently; asserts that GL entries posted under both the Customer and the linked Supplier are included in both runs.
  • test_party_filter_without_party_link — filters the GL by a Customer with no Party Link; asserts only that customer's GL entries are returned and no other party's entries bleed in.
  • test_cpa_mixed_party_list_partial_links — filters the GL by two customers at once where only one has a Party Link; asserts the linked supplier's GL entries are included while the unlinked customer's filter brings in only its own GL entries.

Why this is not a configurable filter on the report:
The Party Link relationship is an accounting truth, not a display preference. Showing only one side of a linked entity produces an incomplete and potentially misleading balance. An opt-out toggle would let accountants draw wrong conclusions from partial data without realising it. The feature is already opt-in at the data level — if no Party Link is configured, the report behaves exactly as before.

Before

Screenshot 2026-05-01 at 2 13 12 PM

After

Screenshot 2026-05-01 at 2 12 20 PM

@github-actions github-actions Bot added the needs-tests This PR needs automated unit-tests. label Apr 29, 2026
@shubhdoshi21 shubhdoshi21 marked this pull request as draft April 29, 2026 17:46
@shubhdoshi21 shubhdoshi21 changed the title feat(general-ledger): add CPA support via Party Link cross-party GL filtering fix(general-ledger): add CPA support via Party Link cross-party GL filtering Apr 29, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 29, 2026

📝 Walkthrough

Walkthrough

The general ledger report's get_conditions function has been modified to handle party filtering differently. When a specific party is provided, the function now retrieves all parties linked to it through Party Link records and constructs an expanded filter condition that matches both the original party and its linked parties across different party types. Two new helper functions support this: one fetches linked parties for a given party list and target type, and another constructs the corresponding SQL condition string. The change affects 41 lines added and 4 lines removed in a single file.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The PR title accurately describes the main change: adding CPA support via Party Link to enable cross-party GL filtering in the general ledger report, which aligns with the code changes introducing linked party fetching and party condition building.
Description check ✅ Passed The pull request description clearly explains the feature (Party Link support in General Ledger filtering), the problem it solves, and the implementation approach with test coverage details.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
erpnext/accounts/report/general_ledger/general_ledger.py (1)

350-380: Tighten the Party Link lookup and dedupe before emitting SQL.

The query currently pulls every Party Link row whose primary_party or secondary_party text matches, then filters the role in Python and emits duplicates into IN (...). It would be cleaner to constrain the role in SQL and accumulate parties in sets, which keeps the generated predicate smaller and avoids fetching irrelevant rows when names overlap across doctypes.

Refactor direction
 def get_linked_parties(party_list: list, party_type: str) -> dict:
-	parties_by_type = {party_type: list(party_list)}
+	parties_by_type = {party_type: set(party_list)}
 
 	party_link_dt = frappe.qb.DocType("Party Link")
 	links = (
 		frappe.qb.from_(party_link_dt)
 		.select(
 			party_link_dt.primary_role,
 			party_link_dt.primary_party,
 			party_link_dt.secondary_role,
 			party_link_dt.secondary_party,
 		)
 		.where(
-			(party_link_dt.secondary_party.isin(party_list)) | (party_link_dt.primary_party.isin(party_list))
+			(
+				(party_link_dt.secondary_role == party_type)
+				& (party_link_dt.secondary_party.isin(party_list))
+			)
+			| (
+				(party_link_dt.primary_role == party_type)
+				& (party_link_dt.primary_party.isin(party_list))
+			)
 		)
 		.run(as_dict=True)
 	)
 
 	for link in links:
 		if link.secondary_party in party_list and link.secondary_role == party_type:
-			parties_by_type.setdefault(link.primary_role, []).append(link.primary_party)
+			parties_by_type.setdefault(link.primary_role, set()).add(link.primary_party)
 		elif link.primary_party in party_list and link.primary_role == party_type:
-			parties_by_type.setdefault(link.secondary_role, []).append(link.secondary_party)
+			parties_by_type.setdefault(link.secondary_role, set()).add(link.secondary_party)
 
-	return parties_by_type
+	return {ptype: sorted(parties) for ptype, parties in parties_by_type.items()}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@erpnext/accounts/report/general_ledger/general_ledger.py` around lines 350 -
380, The Party Link lookup in get_linked_parties currently fetches rows by party
name then filters roles in Python and may emit duplicate parties; update
get_linked_parties to include role constraints in the SQL WHERE (i.e., only
select rows where primary_role = party_type when secondary_party is in
party_list, and vice versa) to avoid irrelevant rows, and accumulate results
into sets (e.g., use a set for parties_by_type entries) to deduplicate before
returning; update build_common_party_condition to consume those sets (convert to
list only for SQL emission) and ensure you still escape each party and the
party_type when building the IN (...) predicates.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@erpnext/accounts/report/general_ledger/general_ledger.py`:
- Around line 280-283: The change removed the legacy "party only" fallback and
now always uses build_common_party_condition when filters.get("party") exists;
restore correct behavior by updating the branch in general_ledger.py so that if
filters.get("party") and NOT filters.get("party_type") you append the legacy
condition "party=%(party)s" to conditions, else call
build_common_party_condition(filters); alternatively, if you prefer to require a
type, enforce this in validate_party() by raising when party is supplied without
party_type and update callers accordingly—refer to validate_party(),
build_common_party_condition, filters["party"], filters["party_type"], and the
conditions list to locate and apply the fix.

---

Nitpick comments:
In `@erpnext/accounts/report/general_ledger/general_ledger.py`:
- Around line 350-380: The Party Link lookup in get_linked_parties currently
fetches rows by party name then filters roles in Python and may emit duplicate
parties; update get_linked_parties to include role constraints in the SQL WHERE
(i.e., only select rows where primary_role = party_type when secondary_party is
in party_list, and vice versa) to avoid irrelevant rows, and accumulate results
into sets (e.g., use a set for parties_by_type entries) to deduplicate before
returning; update build_common_party_condition to consume those sets (convert to
list only for SQL emission) and ensure you still escape each party and the
party_type when building the IN (...) predicates.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: f06e4093-8874-42dd-9743-36de3837632f

📥 Commits

Reviewing files that changed from the base of the PR and between a04c028 and 8d670d2.

📒 Files selected for processing (1)
  • erpnext/accounts/report/general_ledger/general_ledger.py

Comment thread erpnext/accounts/report/general_ledger/general_ledger.py
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

❌ Patch coverage is 93.42105% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.78%. Comparing base (b001884) to head (706e122).
⚠️ Report is 26 commits behind head on develop.

Files with missing lines Patch % Lines
...ounts/report/general_ledger/test_general_ledger.py 94.44% 3 Missing ⚠️
...t/accounts/report/general_ledger/general_ledger.py 90.90% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop   #54657      +/-   ##
===========================================
+ Coverage    79.74%   79.78%   +0.03%     
===========================================
  Files         1160     1160              
  Lines       126362   126521     +159     
===========================================
+ Hits        100768   100940     +172     
+ Misses       25594    25581      -13     
Files with missing lines Coverage Δ
...t/accounts/report/general_ledger/general_ledger.py 77.63% <90.90%> (+5.60%) ⬆️
...ounts/report/general_ledger/test_general_ledger.py 98.25% <94.44%> (-1.75%) ⬇️

... and 8 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-tests This PR needs automated unit-tests.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Modify report General Ledger to support Common party accounting

1 participant