Skip to content

feat(accounts): support multi-company financial report templates#53992

Draft
barredterra wants to merge 1 commit intofrappe:developfrom
barredterra:consolidated-financial-report
Draft

feat(accounts): support multi-company financial report templates#53992
barredterra wants to merge 1 commit intofrappe:developfrom
barredterra:consolidated-financial-report

Conversation

@barredterra
Copy link
Copy Markdown
Collaborator

@barredterra barredterra commented Apr 2, 2026

Summary

This change unblocks consolidated reporting use cases for template-backed financial statements, especially intercompany elimination setups where rows from one company are intended to offset rows from another.

  • add a companies multi-select filter for template-backed Balance Sheet, Profit and Loss Statement, Cash Flow, and Custom Financial Statement reports
  • update the financial report template engine to collect and aggregate account data across the selected companies instead of forcing a single company filter
  • preserve row-level company filtering so elimination logic can be modeled directly in report template rows
  • make GL drilldowns safer for multi-company rows by only linking when a single company can be determined
  • reject multi-company template reports when the selected companies have different default currencies

Why

Today, custom financial report templates are effectively blocked from consolidated multi-company reporting because the report flow assumes a single company throughout execution.

With this change, users can build consolidated templates where intercompany income and expense rows cancel each other out and are hidden through the existing template row and formula system.

Testing

  • bench --site alyf-dev run-tests --module erpnext.accounts.doctype.financial_report_template.test_financial_report_multi_company

@barredterra barredterra marked this pull request as draft April 2, 2026 10:59
@barredterra barredterra requested review from vorasmit and removed request for ruthra-kumar April 2, 2026 10:59
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

This PR implements multi-company support for financial report templates. Three new public functions—get_report_companies, get_primary_company, and get_report_currency—normalize company filter inputs and derive a primary company and single report currency. The financial report engine refactors to query account balances per-company via separate FinancialQueryBuilder runs, merging results and propagating company metadata through AccountData objects and detail row payloads. Three query reports (Balance Sheet, Profit and Loss, Custom Financial Statement) each add a new companies multi-select filter field. Helper functions are added to financial_statements.js to route ledger links and navigation using the derived company. A new integration test suite validates multi-company execution behavior and currency consistency validation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Detailed notes

The core refactoring of financial_report_engine.py introduces substantial structural changes to data collection logic with per-company querying and metadata propagation. The test suite adds 250 lines across multiple test methods with setup infrastructure, requiring understanding of the multi-company behavior expectations. Three .js report filter additions follow a homogeneous pattern but remain distinct files. The routing changes in financial_statements.js affect navigation and ledger link generation logic, demanding careful verification of conditional routing paths. Heterogeneity across file types and logic density in the engine refactoring increase review complexity.

🚥 Pre-merge checks | ✅ 2 | ❌ 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 (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding multi-company support to financial report templates, matching the core objective across all modified files.
Description check ✅ Passed The description is directly related to the changeset, providing clear context about multi-company reporting support, consolidated statements, and intercompany elimination logic.

✏️ 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.

🧹 Nitpick comments (3)
erpnext/accounts/doctype/financial_report_template/financial_report_engine.py (1)

230-248: Consider handling missing company records gracefully.

If a company in report_companies doesn't exist, frappe.get_cached_value returns None, which would be added to the default_currencies set. This could result in a set like {None, "INR"} with length 2, triggering a misleading "same default currency" error instead of a "company not found" error.

♻️ Proposed fix to filter out None values
 def get_report_currency(filters: dict[str, Any]) -> str | None:
 	report_companies = get_report_companies(filters)

 	if not report_companies:
 		return None

 	default_currencies = {
-		frappe.get_cached_value("Company", company, "default_currency") for company in report_companies
+		currency
+		for company in report_companies
+		if (currency := frappe.get_cached_value("Company", company, "default_currency"))
 	}

 	if len(default_currencies) > 1:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@erpnext/accounts/doctype/financial_report_template/financial_report_engine.py`
around lines 230 - 248, The get_report_currency function builds
default_currencies using frappe.get_cached_value which can return None for
missing Company records; filter out None results from the set and if any None
were present, raise a clear error listing the missing company identifiers (or a
message like "Company not found") instead of letting a mixed set produce the
misleading currency mismatch error; update get_report_currency to collect
results from frappe.get_cached_value("Company", company, "default_currency"),
track which companies returned None (from report_companies), remove None before
checking len(default_currencies), and if any missing companies exist raise
frappe.throw with a descriptive message referencing the affected company
identifiers.
erpnext/accounts/doctype/financial_report_template/test_financial_report_multi_company.py (2)

66-70: Minor: Potential IndexError if no cost center exists.

The helper assumes at least one non-group cost center exists for the company. While this is typically true for newly created companies, consider adding a guard or assertion for clearer test failure messages.

♻️ Optional defensive improvement
 `@staticmethod`
 def _get_cost_center(company: str) -> str:
-	return frappe.get_all(
+	cost_centers = frappe.get_all(
 		"Cost Center", filters={"company": company, "is_group": 0}, pluck="name", order_by="lft", limit=1
-	)[0]
+	)
+	if not cost_centers:
+		raise AssertionError(f"No cost center found for company {company}")
+	return cost_centers[0]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@erpnext/accounts/doctype/financial_report_template/test_financial_report_multi_company.py`
around lines 66 - 70, The helper _get_cost_center currently indexes the result
of frappe.get_all directly which can raise IndexError if no non-group cost
center exists; update _get_cost_center to check the returned list from
frappe.get_all (called in _get_cost_center) and either assert/raise a clear
error like "No non-group Cost Center found for company X" or create a fallback
cost center for the given company so tests fail with a descriptive message
instead of an IndexError.

161-233: Test template is not cleaned up after test execution.

The dynamically created template (line 207) is not deleted in the finally block. While the unique hash name prevents conflicts, this accumulates test data over repeated runs.

♻️ Proposed fix to clean up template
 		try:
 			journal_entries.append(self._make_journal_entry(company_a, asset_a, income_a, 200, "2027-04-05"))
 			# ... rest of test
 		finally:
 			for journal_entry in reversed(journal_entries):
 				journal_entry.cancel()
+			if template:
+				template.delete()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@erpnext/accounts/doctype/financial_report_template/test_financial_report_multi_company.py`
around lines 161 - 233, The created report template (variable template from
create_test_template_with_rows) isn't removed after the test; update the finally
block to also delete the template when present (e.g., if template: call
template.delete()/frappe.delete_doc for the Financial Report Template) after
cancelling journal_entries to avoid accumulating test fixtures; reference the
template variable and create_test_template_with_rows to locate where to add the
cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@erpnext/accounts/doctype/financial_report_template/financial_report_engine.py`:
- Around line 230-248: The get_report_currency function builds
default_currencies using frappe.get_cached_value which can return None for
missing Company records; filter out None results from the set and if any None
were present, raise a clear error listing the missing company identifiers (or a
message like "Company not found") instead of letting a mixed set produce the
misleading currency mismatch error; update get_report_currency to collect
results from frappe.get_cached_value("Company", company, "default_currency"),
track which companies returned None (from report_companies), remove None before
checking len(default_currencies), and if any missing companies exist raise
frappe.throw with a descriptive message referencing the affected company
identifiers.

In
`@erpnext/accounts/doctype/financial_report_template/test_financial_report_multi_company.py`:
- Around line 66-70: The helper _get_cost_center currently indexes the result of
frappe.get_all directly which can raise IndexError if no non-group cost center
exists; update _get_cost_center to check the returned list from frappe.get_all
(called in _get_cost_center) and either assert/raise a clear error like "No
non-group Cost Center found for company X" or create a fallback cost center for
the given company so tests fail with a descriptive message instead of an
IndexError.
- Around line 161-233: The created report template (variable template from
create_test_template_with_rows) isn't removed after the test; update the finally
block to also delete the template when present (e.g., if template: call
template.delete()/frappe.delete_doc for the Financial Report Template) after
cancelling journal_entries to avoid accumulating test fixtures; reference the
template variable and create_test_template_with_rows to locate where to add the
cleanup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: b2f9c2e5-3eb9-4b39-b854-63ccf4189ad4

📥 Commits

Reviewing files that changed from the base of the PR and between efd716e and 51820da.

📒 Files selected for processing (6)
  • erpnext/accounts/doctype/financial_report_template/financial_report_engine.py
  • erpnext/accounts/doctype/financial_report_template/test_financial_report_multi_company.py
  • erpnext/accounts/report/balance_sheet/balance_sheet.js
  • erpnext/accounts/report/custom_financial_statement/custom_financial_statement.js
  • erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js
  • erpnext/public/js/financial_statements.js

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 2, 2026

Codecov Report

❌ Patch coverage is 95.13889% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 79.27%. Comparing base (7846548) to head (51820da).
⚠️ Report is 1 commits behind head on develop.

Files with missing lines Patch % Lines
...nancial_report_template/financial_report_engine.py 90.00% 5 Missing ⚠️
...rt_template/test_financial_report_multi_company.py 97.87% 2 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop   #53992      +/-   ##
===========================================
+ Coverage    79.05%   79.27%   +0.21%     
===========================================
  Files         1159     1160       +1     
  Lines       124773   124934     +161     
===========================================
+ Hits         98644    99045     +401     
+ Misses       26129    25889     -240     
Files with missing lines Coverage Δ
...rt_template/test_financial_report_multi_company.py 97.87% <97.87%> (ø)
...nancial_report_template/financial_report_engine.py 70.59% <90.00%> (+26.85%) ⬆️
🚀 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

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant