Repository: bvanelli/actualpy
Pull request #76 (2024-09-19 19:14:25)

fix: Fix issue where before date could case attribute error.
References #68 


Patch:
diff --git a/actual/schedules.py b/actual/schedules.py
index 111a5a3..17a1e34 100644
--- a/actual/schedules.py
+++ b/actual/schedules.py
@@ -214,11 +214,11 @@ class Schedule(pydantic.BaseModel):
                 if self.end_mode == EndMode.ON_DATE and value > date_to_datetime(self.end_date):
                     return None
             else:  # BEFORE
-                value_after = value - datetime.timedelta(days=value.weekday() - 4)
-                if value_after < dt_start:
+                value_before = value - datetime.timedelta(days=value.weekday() - 4)
+                if value_before < dt_start:
                     # value is in the past, skip and look for another
                     return None
-                value = value_after
+                value = value_before
         return value
 
     def before(self, date: datetime.date = None) -> typing.Optional[datetime.date]:
@@ -230,7 +230,10 @@ class Schedule(pydantic.BaseModel):
         before_datetime = rs.before(dt_start)
         if not before_datetime:
             return None
-        return self.do_skip_weekend(dt_start, before_datetime).date()
+        with_weekend_skip = self.do_skip_weekend(date_to_datetime(self.start), before_datetime)
+        if not with_weekend_skip:
+            return None
+        return with_weekend_skip.date()
 
     def xafter(self, date: datetime.date = None, count: int = 1) -> list[datetime.date]:
         if not date:
diff --git a/actual/version.py b/actual/version.py
index f29c654..147950a 100644
--- a/actual/version.py
+++ b/actual/version.py
@@ -1,2 +1,2 @@
-__version_info__ = ("0", "5", "0")
+__version_info__ = ("0", "5", "1")
 __version__ = ".".join(__version_info__)


Test patch:
diff --git a/tests/test_schedules.py b/tests/test_schedules.py
index d7dee96..7930267 100644
--- a/tests/test_schedules.py
+++ b/tests/test_schedules.py
@@ -70,10 +70,10 @@ def test_complex_schedules():
     assert str(s) == "Every month on the last Sunday, 2nd Saturday, 10th, 31st, 5th (before weekend)"
 
 
-def test_skip_weekend_schedule():
+def test_skip_weekend_after_schedule():
     s = Schedule.model_validate(
         {
-            "start": "2024-09-14",
+            "start": "2024-08-14",
             "interval": 1,
             "frequency": "monthly",
             "patterns": [],
@@ -84,11 +84,34 @@ def test_skip_weekend_schedule():
             "endDate": "2024-09-14",
         }
     )
-    after = s.xafter(date(2024, 8, 14), count=2)
+    after = s.xafter(date(2024, 9, 10), count=2)
     # we should ensure that dates that fall outside the endDate are not covered, even though actual will accept it
     assert after == []
 
 
+def test_skip_weekend_before_schedule():
+    s = Schedule.model_validate(
+        {
+            "start": "2024-04-10",
+            "interval": 1,
+            "frequency": "monthly",
+            "patterns": [],
+            "skipWeekend": True,
+            "weekendSolveMode": "before",
+            "endMode": "never",
+            "endOccurrences": 1,
+            "endDate": "2024-04-10",
+        }
+    )
+    before = s.before(date(2024, 8, 14))
+    assert before == date(2024, 8, 9)
+    # check that it wouldn't pick itself
+    assert s.before(date(2024, 7, 10)) == date(2024, 6, 10)
+    # we should ensure that dates that fall outside the endDate are not covered, even though actual will accept it
+    s.start = date(2024, 9, 21)
+    assert s.before(date(2024, 9, 22)) is None
+
+
 def test_is_approx():
     # create schedule for every 1st and last day of the month (30th or 31st)
     s = Schedule.model_validate(