gnucash maint: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Sun Mar 22 16:15:16 EDT 2020


Updated	 via  https://github.com/Gnucash/gnucash/commit/2bbf5b2c (commit)
	 via  https://github.com/Gnucash/gnucash/commit/66fce053 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/268695f1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/7a61eca4 (commit)
	from  https://github.com/Gnucash/gnucash/commit/b99dfb78 (commit)



commit 2bbf5b2ce0aa490f202045640d61648d428a53e9
Merge: b99dfb78a 66fce0530
Author: John Ralls <jralls at ceridwen.us>
Date:   Sun Mar 22 13:14:50 2020 -0700

    Merge Jean Laroche's 'fix-recurrence' into maint


commit 66fce053089e94702d52acf18359d57a3c2b08ec
Author: jean <you at example.com>
Date:   Sun Mar 22 12:34:39 2020 -0700

    Move the weekend adjust code in its own function

diff --git a/libgnucash/engine/Recurrence.c b/libgnucash/engine/Recurrence.c
index 073e2649c..b69b2d23c 100644
--- a/libgnucash/engine/Recurrence.c
+++ b/libgnucash/engine/Recurrence.c
@@ -174,6 +174,28 @@ nth_weekday_compare(const GDate *start, const GDate *next, PeriodType pt)
 }
 
 
+static void adjust_for_weekend(PeriodType pt, WeekendAdjust wadj, GDate *date)
+{
+    if (pt == PERIOD_YEAR || pt == PERIOD_MONTH || pt == PERIOD_END_OF_MONTH)
+    {
+        if (g_date_get_weekday(date) == G_DATE_SATURDAY || g_date_get_weekday(date) == G_DATE_SUNDAY)
+        {
+            switch (wadj)
+            {
+                case WEEKEND_ADJ_BACK:
+                    g_date_subtract_days(date, g_date_get_weekday(date) == G_DATE_SATURDAY ? 1 : 2);
+                    break;
+                case WEEKEND_ADJ_FORWARD:
+                    g_date_add_days(date, g_date_get_weekday(date) == G_DATE_SATURDAY ? 2 : 1);
+                    break;
+                case WEEKEND_ADJ_NONE:
+                default:
+                    break;
+            }
+        }
+    }
+}
+
 /* This is the only real algorithm related to recurrences.  It goes:
    Step 1) Go forward one period from the reference date.
    Step 2) Back up to align to the phase of the start date.
@@ -200,24 +222,7 @@ recurrenceNextInstance(const Recurrence *r, const GDate *ref, GDate *next)
      occurrence is always the start date, and we're done. */
     // However, it's possible for the start date to fall on an exception (a weekend), in that case, it needs to be corrected.
     adjusted_start = *start;
-    if (pt == PERIOD_YEAR || pt == PERIOD_MONTH || pt == PERIOD_END_OF_MONTH)
-    {
-        if (g_date_get_weekday(&adjusted_start) == G_DATE_SATURDAY || g_date_get_weekday(&adjusted_start) == G_DATE_SUNDAY)
-        {
-            switch (wadj)
-            {
-                case WEEKEND_ADJ_BACK:
-                    g_date_subtract_days(&adjusted_start, g_date_get_weekday(&adjusted_start) == G_DATE_SATURDAY ? 1 : 2);
-                    break;
-                case WEEKEND_ADJ_FORWARD:
-                    g_date_add_days(&adjusted_start, g_date_get_weekday(&adjusted_start) == G_DATE_SATURDAY ? 2 : 1);
-                    break;
-                case WEEKEND_ADJ_NONE:
-                default:
-                    break;
-            }
-        }
-    }
+    adjust_for_weekend(pt,wadj,&adjusted_start);
     if (g_date_compare(ref, &adjusted_start) < 0)
     {
         g_date_set_julian(next, g_date_get_julian(&adjusted_start));
@@ -363,25 +368,7 @@ recurrenceNextInstance(const Recurrence *r, const GDate *ref, GDate *next)
             g_date_set_day(next, g_date_get_day(start)); /*same day as start*/
 
         /* Adjust for dates on the weekend. */
-        if (pt == PERIOD_YEAR || pt == PERIOD_MONTH || pt == PERIOD_END_OF_MONTH)
-        {
-            if (g_date_get_weekday(next) == G_DATE_SATURDAY || g_date_get_weekday(next) == G_DATE_SUNDAY)
-            {
-                switch (wadj)
-                {
-                case WEEKEND_ADJ_BACK:
-                    g_date_subtract_days(next, g_date_get_weekday(next) == G_DATE_SATURDAY ? 1 : 2);
-                    break;
-                case WEEKEND_ADJ_FORWARD:
-                    g_date_add_days(next, g_date_get_weekday(next) == G_DATE_SATURDAY ? 2 : 1);
-                    break;
-                case WEEKEND_ADJ_NONE:
-                default:
-                    break;
-                }
-            }
-        }
-
+        adjust_for_weekend(pt,wadj,next);
     }
     break;
     case PERIOD_WEEK:

commit 268695f19ca22dfcabecd9f8a2dd02cf20358baf
Author: jean <you at example.com>
Date:   Sun Mar 22 12:21:33 2020 -0700

    Make test-recurrence handle wadj

diff --git a/libgnucash/engine/test/test-recurrence.c b/libgnucash/engine/test/test-recurrence.c
index 42c98680e..48c02cfbb 100644
--- a/libgnucash/engine/test/test-recurrence.c
+++ b/libgnucash/engine/test/test-recurrence.c
@@ -30,13 +30,13 @@
 
 static QofBook *book;
 
-static void check_valid(GDate *next, GDate *ref, GDate *start,
+static gboolean check_valid(GDate *next, GDate *ref, GDate *start,
                         guint16 mult, PeriodType pt, WeekendAdjust wadj)
 {
     gboolean valid;
+    GDate adj_date;
     gint startToNext;
-    /* FIXME: The WeekendAdjust argument is completely ignored for
-       now. */
+    gboolean ret_val = TRUE;
 
     valid = g_date_valid(next);
     if (pt == PERIOD_ONCE && g_date_compare(start, ref) <= 0)
@@ -44,7 +44,7 @@ static void check_valid(GDate *next, GDate *ref, GDate *start,
     else
         do_test(valid, "incorrectly invalid");
 
-    if (!valid) return;
+    if (!valid) return valid;
 
     do_test(g_date_compare(ref, next) < 0,
             "next date not strictly later than ref date");
@@ -54,13 +54,41 @@ static void check_valid(GDate *next, GDate *ref, GDate *start,
     switch (pt)
     {
     case PERIOD_YEAR:
-        do_test((g_date_get_year(next) - g_date_get_year(start)) % mult == 0,
+        ret_val &= do_test((g_date_get_year(next) - g_date_get_year(start)) % mult == 0,
                 "year period phase wrong"); // redundant
         mult *= 12;
         // fall through
     case PERIOD_END_OF_MONTH:
         if (pt == PERIOD_END_OF_MONTH)
-            do_test(g_date_is_last_of_month(next), "end of month phase wrong");
+        {
+            if(wadj == WEEKEND_ADJ_NONE)
+                ret_val &= do_test(g_date_is_last_of_month(next), "end of month phase wrong");
+            else
+            {
+                gboolean result;
+                if(!g_date_is_last_of_month(next))
+                {
+                    adj_date = *next;
+                    if(wadj == WEEKEND_ADJ_BACK)
+                    {
+                        // If adjusting back, one of the next two days to be end of month
+                        g_date_add_days(&adj_date,1);
+                        result = g_date_is_last_of_month(&adj_date);
+                        g_date_add_days(&adj_date,1);
+                        result |= g_date_is_last_of_month(&adj_date);
+                    }
+                    if(wadj == WEEKEND_ADJ_FORWARD)
+                    {
+                        // If adjusting forward, one of the two previous days has to be end of month
+                        g_date_subtract_days(&adj_date,1);
+                        result = g_date_is_last_of_month(&adj_date);
+                        g_date_subtract_days(&adj_date,1);
+                        result |= g_date_is_last_of_month(&adj_date);
+                    }
+                    ret_val &= do_test(result, "end of month phase wrong");
+                }
+            }
+        }
         // fall through
     case PERIOD_LAST_WEEKDAY:
     case PERIOD_NTH_WEEKDAY:
@@ -71,13 +99,14 @@ static void check_valid(GDate *next, GDate *ref, GDate *start,
 
         monthdiff = (g_date_get_month(next) - g_date_get_month(start)) +
                     12 * (g_date_get_year(next) - g_date_get_year(start));
-        do_test(monthdiff % mult == 0, "month or year phase wrong");
+        monthdiff %= mult;
+        ret_val &= do_test(monthdiff == 0 || (monthdiff == -1 && wadj == WEEKEND_ADJ_BACK) || (monthdiff == 1 && wadj == WEEKEND_ADJ_FORWARD), "month or year phase wrong");
 
         if (pt == PERIOD_NTH_WEEKDAY || pt == PERIOD_LAST_WEEKDAY)
         {
             guint sweek, nweek;
 
-            do_test(g_date_get_weekday(next) == g_date_get_weekday(start),
+            ret_val &= do_test(g_date_get_weekday(next) == g_date_get_weekday(start),
                     "weekday phase wrong");
             sweek = (g_date_get_day(start) - 1) / 7;
             nweek = (g_date_get_day(next) - 1) / 7;
@@ -87,7 +116,7 @@ static void check_valid(GDate *next, GDate *ref, GDate *start,
                4th, OR 'start' didn't have 5 of the weekday that
                'next' does and we want the LAST weekday, so it's the
                5th of that weekday */
-            do_test(sweek == nweek ||
+            ret_val &= do_test(sweek == nweek ||
                     (sweek == 4 && nweek == 3 && (g_date_get_day(next) + 7) >
                      g_date_get_days_in_month(
                          g_date_get_month(next), g_date_get_year(next))) ||
@@ -97,16 +126,61 @@ static void check_valid(GDate *next, GDate *ref, GDate *start,
         }
         else
         {
+            GDateWeekday week_day;
+            GDateWeekday week_day_1;
+            GDateWeekday week_day_2;
             day_start = g_date_get_day(start);
             day_next = g_date_get_day(next);
             if (day_start < 28)
-                do_test(day_start == day_next, "dom don't match");
-            else if (pt != PERIOD_END_OF_MONTH)
+            {
+                gboolean result;
+                week_day = g_date_get_weekday (next);
+                switch (wadj) {
+                    case WEEKEND_ADJ_NONE:
+                        ret_val &= do_test(day_start == day_next, "dom don't match");
+                        break;
+                    case WEEKEND_ADJ_BACK:
+                        // Week_day cannot be a weekend.
+                        result = (week_day != G_DATE_SATURDAY && week_day != G_DATE_SUNDAY);
+                        if(day_start != day_next)
+                        {
+                            // If the dom don't match day must be a Friday
+                            result &= (week_day == G_DATE_FRIDAY);
+                            // Either day_next+1 or day_next+2 matches day_start
+                            g_date_add_days(next,1);
+                            week_day_1 = g_date_get_day(next);
+                            g_date_add_days(next,1);
+                            week_day_2 = g_date_get_day(next);
+                            result &= week_day_1 == day_start || week_day_2 == day_start;
+                        }
+                        ret_val &= do_test(result, "dom don't match");
+                        break;
+                    case WEEKEND_ADJ_FORWARD:
+                        // Week_day cannot be a weekend.
+                        result = (week_day != G_DATE_SATURDAY && week_day != G_DATE_SUNDAY);
+                        if(day_start != day_next)
+                        {
+                            // If the dom don't match day must be a Monday
+                            result &= (week_day == G_DATE_MONDAY);
+                            // Either day_next-1 or day_next-2 matches day_start
+                            g_date_subtract_days(next,1);
+                            week_day_1 = g_date_get_day(next);
+                            g_date_subtract_days(next,1);
+                            week_day_2 = g_date_get_day(next);
+                            result &= week_day_1 == day_start || week_day_2 == day_start;
+                        }
+                        ret_val &= do_test(result, "dom don't match");
+                        break;
+                    default:
+                        break;
+                }
+            }
+            else if (pt != PERIOD_END_OF_MONTH && wadj == WEEKEND_ADJ_NONE)
             {
                 // the end of month case was already checked above.  near
                 // the end of the month, the days should still agree,
                 // unless they can't because of a short month.
-                do_test(day_start == day_next || g_date_is_last_of_month(next),
+                ret_val &= do_test(day_start == day_next || g_date_is_last_of_month(next),
                         "dom don't match and next is not eom");
             }
         }
@@ -116,16 +190,16 @@ static void check_valid(GDate *next, GDate *ref, GDate *start,
         mult *= 7;
         // fall through
     case PERIOD_DAY:
-        do_test((startToNext % mult) == 0, "week or day period phase wrong");
+        ret_val &= do_test((startToNext % mult) == 0, "week or day period phase wrong");
         break;
     case PERIOD_ONCE:
-        do_test(startToNext == 0, "period once not on start date");
+        ret_val &= do_test(startToNext == 0, "period once not on start date");
         break;
     default:
-        do_test(FALSE, "invalid PeriodType");
+        ret_val &=do_test(FALSE, "invalid PeriodType");
         break;
     }
-
+    return ret_val;
 }
 
 #define NUM_DATES_TO_TEST 300
@@ -168,9 +242,9 @@ static void test_all()
                         wadj_reg = recurrenceGetWeekendAdjust(&r);
 
                         recurrenceNextInstance(&r, &d_ref, &d_next);
-                        check_valid(&d_next, &d_ref, &d_start_reg,
-                                    mult_reg, pt_reg, wadj_reg);
-
+                        if (!check_valid(&d_next, &d_ref, &d_start_reg,
+                                    mult_reg, pt_reg, wadj_reg))
+                            return;
                     }
                 }
             }

commit 7a61eca4fe6de70df75982a52bdb77b5dee4aa2a
Author: jean <you at example.com>
Date:   Sat Mar 21 23:23:10 2020 -0700

    Bug 685102 - Scheduled Transactions don't always respect weekends for first occurrence

diff --git a/libgnucash/engine/Recurrence.c b/libgnucash/engine/Recurrence.c
index 9b71c9c4a..073e2649c 100644
--- a/libgnucash/engine/Recurrence.c
+++ b/libgnucash/engine/Recurrence.c
@@ -183,6 +183,7 @@ recurrenceNextInstance(const Recurrence *r, const GDate *ref, GDate *next)
 {
     PeriodType pt;
     const GDate *start;
+    GDate adjusted_start;
     guint mult;
     WeekendAdjust wadj;
 
@@ -191,20 +192,40 @@ recurrenceNextInstance(const Recurrence *r, const GDate *ref, GDate *next)
     g_return_if_fail(g_date_valid(&r->start));
     g_return_if_fail(g_date_valid(ref));
 
-    /* If the ref date comes before the start date then the next
-       occurrence is always the start date, and we're done. */
     start = &r->start;
-    if (g_date_compare(ref, start) < 0)
+    mult = r->mult;
+    pt = r->ptype;
+    wadj = r->wadj;
+    /* If the ref date comes before the start date then the next
+     occurrence is always the start date, and we're done. */
+    // However, it's possible for the start date to fall on an exception (a weekend), in that case, it needs to be corrected.
+    adjusted_start = *start;
+    if (pt == PERIOD_YEAR || pt == PERIOD_MONTH || pt == PERIOD_END_OF_MONTH)
     {
-        g_date_set_julian(next, g_date_get_julian(start));
+        if (g_date_get_weekday(&adjusted_start) == G_DATE_SATURDAY || g_date_get_weekday(&adjusted_start) == G_DATE_SUNDAY)
+        {
+            switch (wadj)
+            {
+                case WEEKEND_ADJ_BACK:
+                    g_date_subtract_days(&adjusted_start, g_date_get_weekday(&adjusted_start) == G_DATE_SATURDAY ? 1 : 2);
+                    break;
+                case WEEKEND_ADJ_FORWARD:
+                    g_date_add_days(&adjusted_start, g_date_get_weekday(&adjusted_start) == G_DATE_SATURDAY ? 2 : 1);
+                    break;
+                case WEEKEND_ADJ_NONE:
+                default:
+                    break;
+            }
+        }
+    }
+    if (g_date_compare(ref, &adjusted_start) < 0)
+    {
+        g_date_set_julian(next, g_date_get_julian(&adjusted_start));
         return;
     }
     g_date_set_julian(next, g_date_get_julian(ref)); /* start at refDate */
 
     /* Step 1: move FORWARD one period, passing exactly one occurrence. */
-    mult = r->mult;
-    pt = r->ptype;
-    wadj = r->wadj;
     switch (pt)
     {
     case PERIOD_YEAR:



Summary of changes:
 libgnucash/engine/Recurrence.c           |  60 ++++++++++-------
 libgnucash/engine/test/test-recurrence.c | 112 +++++++++++++++++++++++++------
 2 files changed, 127 insertions(+), 45 deletions(-)



More information about the gnucash-changes mailing list