Repository: akvo/akvo-rsr

Issue:
indicatorPeriod.update_actual_value does not update actual value for later periods
Say we have `period1` with `update1`. `period1` has a baseline of 0 and `update1` has a change value of +5.

Say we also have `period2` which starts after `period1`, and `period2` has `update2` which has a change value of +1.

In this case, `period1` has an `actual_value` of 5 and `period2` has an `actual_value` of 6.

The problem happens when we edit `update1` after we've already created `update2`. For example, if we change `update1` from a +5 to a +6. 

Currently, the `actual_value` of `period2` remains at 6 - but `update2` still has a change value of +1. The `actual_value` of `period2` should be updated to 7.

The `actual_value` of `period2` should be changed when we call `update_actual_value` on `period1`



Discussion:

Resolving patch:
diff --git a/akvo/rsr/models/indicator.py b/akvo/rsr/models/indicator.py
--- a/akvo/rsr/models/indicator.py
+++ b/akvo/rsr/models/indicator.py
@@ -362,6 +362,20 @@ def child_periods(self):
             period_end=self.period_end
         )
 
+    def adjacent_period(self, next_period=True):
+        """
+        Returns the next or previous indicator period, if we can find one with a start date,
+        and we have a start date ourselves.
+        """
+        if not self.period_start:
+            return None
+        elif next_period:
+            return self.indicator.periods.exclude(period_start=None).filter(
+                period_start__gt=self.period_start).order_by('period_start').first()
+        else:
+            return self.indicator.periods.exclude(period_start=None).filter(
+                period_start__lt=self.period_start).order_by('-period_start').first()
+
     def update_actual_value(self, update_value):
         """
         :param update_value; String or Integer that should be castable to Decimal
@@ -380,6 +394,11 @@ def update_actual_value(self, update_value):
         if parent:
             parent.update_actual_value(update_value)
 
+        # Update next period
+        next_period = self.adjacent_period()
+        if next_period and next_period.actual_value:
+            next_period.update_actual_value(update_value)
+
     @property
     def percent_accomplishment(self):
         """
@@ -433,22 +452,17 @@ def target(self):
     @property
     def baseline(self):
         """
-        Returns the baseline value of the indicator, if it can be converted to a number. Otherwise
-        it'll return 0.
-        """
-        baseline = 0
+        Returns the baseline value of the indicator. The baseline is a calculated value:
 
-        ordered_periods = self.indicator.periods.exclude(period_start=None).order_by('period_start')
-        if ordered_periods.exists() and self == ordered_periods[0]:
+        - If the period has no previous periods, then it's the baseline value of the indicator
+        - If the period has a previous period, then it's the actual value of that period
+        - In all other cases, the baseline defaults to 0
+        """
+        previous_period = self.adjacent_period(False)
+        if not previous_period:
             baseline = self.indicator.baseline_value
-        elif self in ordered_periods:
-            prev_period = None
-            for period in ordered_periods:
-                if not self == period:
-                    prev_period = period
-                else:
-                    baseline = prev_period.actual
-                    break
+        else:
+            baseline = previous_period.actual
 
         try:
             return Decimal(baseline)


Test patch: