From 04e7457cea188631fe32e57e5a8eedf126c64393 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Tue, 12 May 2026 13:36:34 +0530 Subject: [PATCH 1/4] refactor(financial-report): fix row transformation for growth calculations (cherry picked from commit c179460c9892665b53c71f6584e84ff2cf74fa1f) --- .../financial_report_engine.py | 52 +++++++++++++++---- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py b/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py index 47c7e2e6366..a68540a881b 100644 --- a/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py +++ b/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py @@ -1858,23 +1858,55 @@ class GrowthViewTransformer: if row_data.get("is_blank_line"): continue - transformed_values = {} - for i in range(len(self.period_list)): - current_period = self.period_list[i]["key"] + if row_data.get("segment_values"): + self._transform_segmented_row(row_data) + else: + self._transform_single_row(row_data) - current_value = row_data[current_period] - previous_value = row_data[self.period_list[i - 1]["key"]] if i != 0 else 0 + def _transform_single_row(self, row_data: dict) -> None: + transformed_values = {} + for i, period in enumerate(self.period_list): + current_period = period["key"] + current_value = row_data.get(current_period) + + if not isinstance(current_value, int | float): + continue + + if i == 0: + transformed_values[current_period] = current_value + else: + previous_period = self.period_list[i - 1]["key"] + previous_value = row_data.get(previous_period) or 0 + transformed_values[current_period] = self._calculate_growth(previous_value, current_value) + + row_data.update(transformed_values) + + def _transform_segmented_row(self, row_data: dict) -> None: + for seg_id, seg_data in row_data.get("segment_values", {}).items(): + if seg_data.get("is_blank_line"): + continue + + transformed = {} + for i, period in enumerate(self.period_list): + current_period = period["key"] + current_value = seg_data.get(current_period) + + if not isinstance(current_value, int | float): + continue if i == 0: - transformed_values[current_period] = current_value + transformed[current_period] = current_value else: - growth_percent = self._calculate_growth(previous_value, current_value) - transformed_values[current_period] = growth_percent + previous_period = self.period_list[i - 1]["key"] + previous_value = seg_data.get(previous_period) or 0 + transformed[current_period] = self._calculate_growth(previous_value, current_value) - row_data.update(transformed_values) + row_data[f"{seg_id}_{current_period}"] = transformed[current_period] + + seg_data.update(transformed) def _calculate_growth(self, previous_value: float, current_value: float) -> float | None: - if current_value is None: + if current_value is None or current_value == "": return None if previous_value == 0 and current_value > 0: From 1f5281d3b8cca8f39d00fbe2c3d0578e229b074d Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Wed, 20 May 2026 11:55:03 +0530 Subject: [PATCH 2/4] refactor: simple utility for growth value computation for custom FS report (cherry picked from commit 698876672d6eab6780f26f3d5eeff5e55c7fe70f) --- .../financial_report_engine.py | 47 ++++++++----------- 1 file changed, 19 insertions(+), 28 deletions(-) diff --git a/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py b/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py index a68540a881b..c79cbfe1448 100644 --- a/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py +++ b/erpnext/accounts/doctype/financial_report_template/financial_report_engine.py @@ -1853,7 +1853,7 @@ class GrowthViewTransformer: self.formatted_rows = context.raw_data.get("formatted_data", []) self.period_list = context.period_list - def transform(self) -> None: + def transform(self): for row_data in self.formatted_rows: if row_data.get("is_blank_line"): continue @@ -1863,50 +1863,41 @@ class GrowthViewTransformer: else: self._transform_single_row(row_data) - def _transform_single_row(self, row_data: dict) -> None: - transformed_values = {} + def _compute_growth_values(self, source: dict) -> dict: + transformed = {} + for i, period in enumerate(self.period_list): current_period = period["key"] - current_value = row_data.get(current_period) + current_value = source.get(current_period) - if not isinstance(current_value, int | float): + if current_value in (None, ""): continue if i == 0: - transformed_values[current_period] = current_value + transformed[current_period] = current_value else: previous_period = self.period_list[i - 1]["key"] - previous_value = row_data.get(previous_period) or 0 - transformed_values[current_period] = self._calculate_growth(previous_value, current_value) + previous_value = source.get(previous_period) or 0 + transformed[current_period] = self._calculate_growth(previous_value, current_value) - row_data.update(transformed_values) + return transformed - def _transform_segmented_row(self, row_data: dict) -> None: + def _transform_single_row(self, row_data: dict): + row_data.update(self._compute_growth_values(row_data)) + + def _transform_segmented_row(self, row_data: dict): for seg_id, seg_data in row_data.get("segment_values", {}).items(): if seg_data.get("is_blank_line"): continue - transformed = {} - for i, period in enumerate(self.period_list): - current_period = period["key"] - current_value = seg_data.get(current_period) - - if not isinstance(current_value, int | float): - continue - - if i == 0: - transformed[current_period] = current_value - else: - previous_period = self.period_list[i - 1]["key"] - previous_value = seg_data.get(previous_period) or 0 - transformed[current_period] = self._calculate_growth(previous_value, current_value) - - row_data[f"{seg_id}_{current_period}"] = transformed[current_period] - + transformed = self._compute_growth_values(seg_data) seg_data.update(transformed) + for period_key, value in transformed.items(): + row_data[f"{seg_id}_{period_key}"] = value + def _calculate_growth(self, previous_value: float, current_value: float) -> float | None: - if current_value is None or current_value == "": + if current_value in (None, ""): return None if previous_value == 0 and current_value > 0: From da3844c4df0fe679448a326c596d3f748b00fc98 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Wed, 20 May 2026 15:10:18 +0530 Subject: [PATCH 3/4] fix: update formatting of growth view for FS report (cherry picked from commit 4c7499600c2abb9cc9eb4e8896d7bb02c0708c58) --- erpnext/public/js/financial_statements.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 58a8803eca0..7eef83dac8d 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -41,6 +41,22 @@ erpnext.financial_statements = { _is_special_view: function (column, data) { if (!data) return false; const view = get_filter_value("selected_view"); + + if (!["Growth", "Margin"].includes(view)) return false; + + if (get_filter_value("report_template")) { + const columnInfo = erpnext.financial_statements._parse_column_info(column.fieldname, data); + // Account column + if (columnInfo.isAccount) return false; + + if (view === "Growth") { + const periodKeys = data._segment_info?.period_keys || []; + // First period of new segment + if (periodKeys[0] === columnInfo.fieldname) return false; + } + return true; + } + return (view === "Growth" && column.colIndex >= 3) || (view === "Margin" && column.colIndex >= 2); }, From 3f87836536113db8e9c2b2610c019c9db5a29635 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Thu, 25 Jun 2026 18:54:25 +0530 Subject: [PATCH 4/4] fix: enhance growth view filtering by validating period keys (cherry picked from commit aad287d09ef157572e28eb5bc62705d7293eeebe) --- erpnext/public/js/financial_statements.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 7eef83dac8d..af062e80e9c 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -49,11 +49,15 @@ erpnext.financial_statements = { // Account column if (columnInfo.isAccount) return false; + const periodKeys = data._segment_info?.period_keys || []; + + if (!periodKeys.includes(columnInfo.fieldname)) return false; + if (view === "Growth") { - const periodKeys = data._segment_info?.period_keys || []; // First period of new segment if (periodKeys[0] === columnInfo.fieldname) return false; } + return true; }