Repository: akvo/akvo-rsr

Issue:
Bug: Nuffic report adjustment
### What were you doing?

- Some columns with numeric values are formatted as strings on project result and indicator report
- Missing subtitle column on program overview results report
- Missing contributors when filtering for certain periods on program overview results report

### What should've happened?

Project and program reports should work.

### My environment

_No response_

### Additional context

_No response_


Discussion:

Resolving patch:
diff --git a/akvo/rsr/dataclasses.py b/akvo/rsr/dataclasses.py
--- a/akvo/rsr/dataclasses.py
+++ b/akvo/rsr/dataclasses.py
@@ -13,7 +13,7 @@
 
 from akvo.rsr.models import IndicatorPeriodData
 from akvo.rsr.models.result.utils import QUANTITATIVE, QUALITATIVE, PERCENTAGE_MEASURE, calculate_percentage
-from akvo.utils import ensure_decimal
+from akvo.utils import ensure_decimal, maybe_decimal
 from akvo.codelists.models import ResultType
 
 from django.conf import settings
@@ -95,6 +95,7 @@ def is_approved(self):
 class ContributorProjectData(object):
     id: Optional[int] = None
     title: str = ''
+    subtitle: str = ''
     country: Optional[str] = None
     aggregate_children: bool = True
     aggregate_to_parent: bool = True
@@ -105,6 +106,7 @@ def make(cls, data, prefix=''):
         return cls(
             id=data.get(f"{prefix}id", None),
             title=data.get(f"{prefix}title", ''),
+            subtitle=data.get(f"{prefix}subtitle", ''),
             country=data.get(f"{prefix}primary_location__country__name", None),
             aggregate_children=data.get(f"{prefix}aggregate_children", True),
             aggregate_to_parent=data.get(f"{prefix}aggregate_to_parent", True),
@@ -112,7 +114,7 @@ def make(cls, data, prefix=''):
 
 
 class ReportingPeriodMixin(ABC):
-    target_value: Optional[str] = None
+    target_value: Optional[Decimal] = None
     indicator_type: int = QUANTITATIVE
     indicator_measure: str = ''
     updates: List[PeriodUpdateData] = field(default_factory=list)
@@ -300,7 +302,7 @@ class ContributorData(ReportingPeriodMixin):
     parent: Optional[int] = None
     indicator_type: int = QUANTITATIVE
     indicator_measure: str = ''
-    target_value: Optional[str] = None
+    target_value: Optional[Decimal] = None
     indicator_baseline_value: Optional[Decimal] = None
     indicator_target_value: Optional[Decimal] = None
     project: Optional[ContributorProjectData] = None
@@ -315,7 +317,7 @@ def make(cls, data, prefix=''):
             parent=data.get(f"{prefix}parent_period", None),
             indicator_type=data.get(f"{prefix}indicator__type", QUANTITATIVE),
             indicator_measure=data.get(f"{prefix}indicator__measure", ''),
-            target_value=data.get(f"{prefix}target_value", None),
+            target_value=maybe_decimal(data.get(f"{prefix}target_value", None)),
             indicator_baseline_value=data.get(f"{prefix}indicator__baseline_value", None),
             indicator_target_value=data.get(f"{prefix}indicator__target_value", None),
             project=ContributorProjectData.make(data, 'indicator__result__project__')
@@ -344,9 +346,9 @@ class PeriodData(ReportingPeriodMixin):
     id: int
     period_start: Optional[date] = None
     period_end: Optional[date] = None
-    target_value: Optional[str] = None
+    target_value: Optional[Decimal] = None
     target_comment: str = ''
-    actual_value: str = ''
+    actual_value: Optional[Decimal] = None
     actual_comment: str = ''
     narrative: str = ''
     indicator_type: int = QUANTITATIVE
@@ -361,9 +363,9 @@ def make(cls, data, prefix=''):
             id=data[f"{prefix}id"],
             period_start=data.get(f"{prefix}period_start", None),
             period_end=data.get(f"{prefix}period_end", None),
-            target_value=data.get(f"{prefix}target_value", None),
+            target_value=maybe_decimal(data.get(f"{prefix}target_value", None)),
             target_comment=data.get(f"{prefix}target_comment", ''),
-            actual_value=data.get(f"{prefix}actual_value", ''),
+            actual_value=maybe_decimal(data.get(f"{prefix}actual_value", None)),
             actual_comment=data.get(f"{prefix}actual_comment", ''),
             narrative=data.get(f"{prefix}narrative", ''),
             indicator_type=data.get(f"{prefix}indicator__type", QUANTITATIVE),
@@ -435,7 +437,7 @@ class IndicatorData(object):
     measure: str = ''
     description: str = ''
     baseline_year: Optional[int] = None
-    baseline_value: str = ''
+    baseline_value: Optional[Decimal] = None
     baseline_comment: str = ''
     target_value: Optional[Decimal] = None
     target_comment: str = ''
@@ -450,7 +452,7 @@ def make(cls, data, prefix=''):
             measure=data.get(f"{prefix}measure", ''),
             description=data.get(f"{prefix}description", ''),
             baseline_year=data.get(f"{prefix}baseline_year", None),
-            baseline_value=data.get(f"{prefix}baseline_value", ''),
+            baseline_value=maybe_decimal(data.get(f"{prefix}baseline_value", None)),
             baseline_comment=data.get(f"{prefix}baseline_comment", ''),
             target_value=data.get(f"{prefix}target_value", None),
             target_comment=data.get(f"{prefix}target_comment", None),
diff --git a/akvo/rsr/views/py_reports/program_overview_excel_report.py b/akvo/rsr/views/py_reports/program_overview_excel_report.py
--- a/akvo/rsr/views/py_reports/program_overview_excel_report.py
+++ b/akvo/rsr/views/py_reports/program_overview_excel_report.py
@@ -64,7 +64,8 @@ def fetch_contributors(root_period_ids):
         .values(
             'id', 'parent_period', 'target_value', 'indicator__id',
             'indicator__type', 'indicator__measure', 'indicator__target_value',
-            'indicator__baseline_value', 'indicator__result__project__id', 'indicator__result__project__title',
+            'indicator__baseline_value', 'indicator__result__project__id',
+            'indicator__result__project__title', 'indicator__result__project__subtitle',
             'indicator__result__project__aggregate_children', 'indicator__result__project__aggregate_to_parent',
             'indicator__result__project__primary_location__country__name',
             'data__id', 'data__status', 'data__value', 'data__numerator', 'data__denominator',
@@ -187,7 +188,7 @@ def get_results_framework(project, start_date=None, end_date=None):
     return [r for r in lookup['results'].values()]
 
 
-AGGREGATED_TARGET_VALUE_COLUMN = 10
+AGGREGATED_TARGET_VALUE_COLUMN = 11
 
 
 def get_dynamic_column_start(aggregate_targets):
@@ -213,7 +214,7 @@ def generate_workbok(program, start_date=None, end_date=None):
     aggregate_targets = is_aggregating_targets(program)
     use_indicator_target = utils.is_using_indicator_target(program)
     disaggregations = get_disaggregations(program)
-    disaggregations_column_start = 14 if aggregate_targets else 13
+    disaggregations_column_start = 16 if aggregate_targets else 15
     disaggregation_types_length = 0
     for types in disaggregations.values():
         disaggregation_types_length += len(types.keys())
@@ -245,6 +246,7 @@ def generate_workbok(program, start_date=None, end_date=None):
         ws.set_col_style(7, Style(size=25))
         ws.set_col_style(8, Style(size=25))
         ws.set_col_style(9, Style(size=25))
+        ws.set_col_style(10, Style(size=25))
         if aggregate_targets:
             ws.set_col_style(AGGREGATED_TARGET_VALUE_COLUMN, Style(size=25))
         col = get_dynamic_column_start(aggregate_targets)
@@ -264,10 +266,11 @@ def generate_workbok(program, start_date=None, end_date=None):
         ws.set_cell_value(1, 3, 'Indicator title')
         ws.set_cell_value(1, 4, 'Reporting period')
         ws.set_cell_value(1, 5, 'Hierarchy level')
-        ws.set_cell_value(1, 6, 'Contributors')
-        ws.set_cell_value(1, 7, 'Countries')
-        ws.set_cell_value(1, 8, 'Sector')
-        ws.set_cell_value(1, 9, 'Baseline value')
+        ws.set_cell_value(1, 6, 'Contributor title')
+        ws.set_cell_value(1, 7, 'Contributor subtitle')
+        ws.set_cell_value(1, 8, 'Countries')
+        ws.set_cell_value(1, 9, 'Sector')
+        ws.set_cell_value(1, 10, 'Baseline value')
         if aggregate_targets:
             ws.set_cell_value(1, AGGREGATED_TARGET_VALUE_COLUMN, 'Aggregated target value')
         col = get_dynamic_column_start(aggregate_targets)
@@ -315,8 +318,8 @@ def generate_workbok(program, start_date=None, end_date=None):
             for indicator in result.indicators:
                 for period in indicator.periods:
                     row = render_period(ws, row, result, indicator, period, aggregate_targets, use_indicator_target, disaggregations)
-                for contributor in period.contributors:
-                    row = render_contributor_hierarchy(ws, row, result, indicator, period, contributor, aggregate_targets, use_indicator_target, disaggregations)
+                    for contributor in period.contributors:
+                        row = render_contributor_hierarchy(ws, row, result, indicator, period, contributor, aggregate_targets, use_indicator_target, disaggregations)
     return wb
 
 
@@ -329,7 +332,7 @@ def render_period(ws, row, result, indicator, period, aggregate_targets=False, u
     ws.set_cell_style(row, 3, long_text_style)
     ws.set_cell_value(row, 3, indicator.title)
     ws.set_cell_value(row, 4, f"{period.period_start} - {period.period_end}")
-    ws.set_cell_value(row, 9, maybe_decimal(indicator.baseline_value))
+    ws.set_cell_value(row, 10, maybe_decimal(indicator.baseline_value))
     col = get_dynamic_column_start(aggregate_targets)
     if aggregate_targets:
         ws.set_cell_value(row, AGGREGATED_TARGET_VALUE_COLUMN, indicator.aggregated_target_value if use_indicator_target else period.aggregated_target_value)
@@ -368,10 +371,12 @@ def render_contributor(ws, row, result, indicator, period, contributor, aggregat
     ws.set_cell_style(row, 6, long_text_style)
     ws.set_cell_value(row, 6, contributor.project.title)
     ws.set_cell_style(row, 7, long_text_style)
-    ws.set_cell_value(row, 7, contributor.project.country)
+    ws.set_cell_value(row, 7, contributor.project.subtitle)
     ws.set_cell_style(row, 8, long_text_style)
-    ws.set_cell_value(row, 8, ', '.join(contributor.project.sectors) if contributor.project.sectors else '')
-    ws.set_cell_value(row, 9, maybe_decimal(contributor.indicator_baseline_value))
+    ws.set_cell_value(row, 8, contributor.project.country)
+    ws.set_cell_style(row, 9, long_text_style)
+    ws.set_cell_value(row, 9, ', '.join(contributor.project.sectors) if contributor.project.sectors else '')
+    ws.set_cell_value(row, 10, maybe_decimal(contributor.indicator_baseline_value))
     col = get_dynamic_column_start(aggregate_targets)
     ws.set_cell_value(row, col, contributor.indicator_target_value if use_indicator_target else ensure_decimal(contributor.target_value))
     col += 2


Test patch: