r17725 - gnucash/trunk/src - Bug #106401: Add SX weekend occurence to be shifted to weekdays

Christian Stimming cstim at cvs.gnucash.org
Wed Nov 26 16:17:31 EST 2008


Author: cstim
Date: 2008-11-26 16:17:30 -0500 (Wed, 26 Nov 2008)
New Revision: 17725
Trac: http://svn.gnucash.org/trac/changeset/17725

Modified:
   gnucash/trunk/src/backend/file/gnc-freqspec-xml-v2.c
   gnucash/trunk/src/backend/file/gnc-recurrence-xml-v2.c
   gnucash/trunk/src/backend/file/gnc-schedxaction-xml-v2.c
   gnucash/trunk/src/engine/Recurrence.c
   gnucash/trunk/src/engine/Recurrence.h
   gnucash/trunk/src/engine/gnc-budget.c
   gnucash/trunk/src/engine/test-core/test-engine-stuff.c
   gnucash/trunk/src/engine/test/test-recurrence.c
   gnucash/trunk/src/gnome-utils/gnc-frequency.c
   gnucash/trunk/src/gnome-utils/gnc-recurrence.c
   gnucash/trunk/src/gnome/dialog-sx-from-trans.c
   gnucash/trunk/src/gnome/dialog-sx-since-last-run.c
   gnucash/trunk/src/gnome/druid-acct-period.c
   gnucash/trunk/src/gnome/druid-loan.c
   gnucash/trunk/src/gnome/glade/sched-xact.glade
   gnucash/trunk/src/gnome/gnc-plugin-page-budget.c
   gnucash/trunk/src/gnome/gnc-plugin-page-sx-list.c
Log:
Bug #106401: Add SX weekend occurence to be shifted to weekdays

The RFE wanted to specify the date of a scheduled transaction like this:
At the 15th of each month but if that is a saturday or a sunday then
at the next monday after that (or the friday before)

This patch implements this. The contributer writes:
Aside from some combinations being possible that don't make sense (because I
haven't looked at how to hide the extra combo boxes for them), and some awful
code in recurrenceNextInstance to stop it trying to go backwards (it may make
more sense to store the date that was going to be used next before it's changed
back/forward, so that that can be compared instead), it seems to work ok.

Patch by Simon Arlott.

Modified: gnucash/trunk/src/backend/file/gnc-freqspec-xml-v2.c
===================================================================
--- gnucash/trunk/src/backend/file/gnc-freqspec-xml-v2.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/backend/file/gnc-freqspec-xml-v2.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -101,6 +101,7 @@
         gint64                offset;       /* all [except once] */
         gint64                day;          /* monthly or month-relative */
         gint64                occurrence;   /* month-relative */
+        gint64                weekend_adj;  /* monthly/yearly */
         GList                *list;         /* composite */
         UIFreqType            uift;
 } fsParseData;
@@ -118,6 +119,7 @@
                 = fspd->day
                 = fspd->occurrence
                 = 0;
+        fspd->weekend_adj = WEEKEND_ADJ_NONE;
         g_date_clear( &fspd->once_day, 1 );
 }
 
@@ -243,6 +245,20 @@
 
 static
 gboolean
+fs_weekend_adj_handler( xmlNodePtr node, gpointer data )
+{
+        fsParseData *fspd = data;
+        gboolean        ret;
+        gint64          foo;
+        ret = dom_tree_to_integer( node, &foo );
+        if ( !ret )
+                return ret;
+        fspd->weekend_adj = foo;
+        return TRUE;
+}
+
+static
+gboolean
 fs_subelement_handler( xmlNodePtr node, gpointer data )
 {
         fsParseData *fspd = data;
@@ -262,7 +278,7 @@
                 {
                     // complementry hack around 'once' freqspects not being valid. :/
                     recurrence_date = recurrenceGetDate(r);
-                    recurrenceSet(r, recurrenceGetMultiplier(r), PERIOD_MONTH, &recurrence_date);
+                    recurrenceSet(r, recurrenceGetMultiplier(r), PERIOD_MONTH, &recurrence_date, recurrenceGetWeekendAdjust(r));
                 }
                 fspd->recurrence_list = g_list_append(fspd->recurrence_list, r);
             }
@@ -271,13 +287,14 @@
 }
 
 struct dom_tree_handler fs_union_dom_handlers[] = {
-        { "fs:date",       fs_date_handler,       0, 0 },
-        { "fs:interval",   fs_interval_handler,   0, 0 },
-        { "fs:offset",     fs_offset_handler,     0, 0 },
-        { "fs:day",        fs_day_handler,        0, 0 },
-        { "fs:weekday",    fs_weekday_handler,    0, 0 },
-        { "fs:occurrence", fs_occurrence_handler, 0, 0 },
-        { "gnc:freqspec",  fs_subelement_handler, 0, 0 },
+        { "fs:date",        fs_date_handler,        0, 0 },
+        { "fs:interval",    fs_interval_handler,    0, 0 },
+        { "fs:offset",      fs_offset_handler,      0, 0 },
+        { "fs:day",         fs_day_handler,         0, 0 },
+        { "fs:weekday",     fs_weekday_handler,     0, 0 },
+        { "fs:occurrence",  fs_occurrence_handler,  0, 0 },
+        { "fs:weekend_adj", fs_weekend_adj_handler, 0, 0 },
+        { "gnc:freqspec",   fs_subelement_handler,  0, 0 },
         { NULL, NULL, 0, 0 },
 };
 
@@ -304,7 +321,7 @@
                                              fspd );
         if ( !successful )
                 return FALSE;
-        recurrenceSet(fspd->recurrence, 0, PERIOD_ONCE, &fspd->once_day);
+        recurrenceSet(fspd->recurrence, 0, PERIOD_ONCE, &fspd->once_day, WEEKEND_ADJ_NONE);
         
         return TRUE;
 }
@@ -321,7 +338,7 @@
 
         g_date_clear(&offset_date, 1);
         g_date_set_julian(&offset_date, fspd->offset == 0 ? 7 : fspd->offset);
-        recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_DAY, &offset_date);
+        recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_DAY, &offset_date, WEEKEND_ADJ_NONE);
 
         return TRUE;
 }
@@ -341,7 +358,7 @@
 
         g_date_clear(&offset_date, 1);
         g_date_set_julian(&offset_date, fspd->offset == 0 ? 7 : fspd->offset);
-        recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_WEEK, &offset_date);
+        recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_WEEK, &offset_date, WEEKEND_ADJ_NONE);
 
         return TRUE;
 }
@@ -367,11 +384,11 @@
         if (fspd->uift == UIFREQ_ONCE)
         {
             // hack...
-            recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_ONCE, &offset_date);
+            recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_ONCE, &offset_date, WEEKEND_ADJ_NONE);
         }
         else
         {
-            recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_MONTH, &offset_date);
+            recurrenceSet(fspd->recurrence, fspd->interval, PERIOD_MONTH, &offset_date, fspd->weekend_adj);
         }
         
         return successful;

Modified: gnucash/trunk/src/backend/file/gnc-recurrence-xml-v2.c
===================================================================
--- gnucash/trunk/src/backend/file/gnc-recurrence-xml-v2.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/backend/file/gnc-recurrence-xml-v2.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -47,6 +47,7 @@
 #define recurrence_mult          "recurrence:mult"
 #define recurrence_period_type   "recurrence:period_type"
 #define recurrence_start         "recurrence:start"
+#define recurrence_weekend_adj   "recurrence:weekend_adj"
 
 //TODO: I think three of these functions rightly belong in Recurrence.c.
 
@@ -83,10 +84,25 @@
     return dom_tree_to_guint16(node, &((Recurrence *)r)->mult);
 }
 
+static gboolean
+recurrence_weekend_adj_handler(xmlNodePtr node, gpointer d)
+{
+    WeekendAdjust wadj;
+    char *nodeTxt;
+
+    nodeTxt = dom_tree_to_text(node);
+    g_return_val_if_fail(nodeTxt, FALSE);
+    wadj= recurrenceWeekendAdjustFromString(nodeTxt);
+    ((Recurrence *) d)->wadj = wadj;
+    g_free(nodeTxt);
+    return (wadj != -1);
+}
+
 static struct dom_tree_handler recurrence_dom_handlers[] = {
     { recurrence_mult, recurrence_mult_handler, 1, 0 },
     { recurrence_period_type, recurrence_period_type_handler, 1, 0 },
     { recurrence_start, recurrence_start_date_handler, 1, 0 },
+    { recurrence_weekend_adj, recurrence_weekend_adj_handler, 1, 0 },
     { NULL, NULL, 0, 0 }
 };
 
@@ -113,6 +129,7 @@
     xmlNodePtr n;
     PeriodType pt;
     GDate d;
+    WeekendAdjust wadj;
 
     n = xmlNewNode(NULL, tag);
     xmlSetProp(n, "version", recurrence_version_string );
@@ -123,5 +140,8 @@
                                     recurrencePeriodTypeToString(pt)));
     d = recurrenceGetDate(r);
     xmlAddChild(n, gdate_to_dom_tree(recurrence_start, &d));
+    wadj = recurrenceGetWeekendAdjust(r);
+    xmlAddChild(n, text_to_dom_tree(recurrence_weekend_adj,
+                                    recurrenceWeekendAdjustToString(wadj)));
     return n;
 }

Modified: gnucash/trunk/src/backend/file/gnc-schedxaction-xml-v2.c
===================================================================
--- gnucash/trunk/src/backend/file/gnc-schedxaction-xml-v2.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/backend/file/gnc-schedxaction-xml-v2.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -394,7 +394,8 @@
         recurrenceSet(r,
                       recurrenceGetMultiplier(r),
                       recurrenceGetPeriodType(r), 
-                      &next);
+                      &next,
+                      recurrenceGetWeekendAdjust(r));
     }
 
     if (g_list_length(schedule) == 1
@@ -403,7 +404,7 @@
         char date_buf[128];
         Recurrence *fixup = (Recurrence*)g_list_nth_data(schedule, 0);
         g_date_strftime(date_buf, 127, "%x", sx_start_date);
-        recurrenceSet(fixup, 1, PERIOD_ONCE, sx_start_date);
+        recurrenceSet(fixup, 1, PERIOD_ONCE, sx_start_date, WEEKEND_ADJ_NONE);
         g_debug("fixed up period=ONCE Recurrence to date [%s]", date_buf);
     }
 }

Modified: gnucash/trunk/src/engine/Recurrence.c
===================================================================
--- gnucash/trunk/src/engine/Recurrence.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/engine/Recurrence.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -43,8 +43,12 @@
     "once", "day", "week", "month", "end of month",
     "nth weekday", "last weekday", "year",
 };
+static gchar *weekend_adj_strings[NUM_WEEKEND_ADJS] = {
+    "none", "back", "forward",
+};
 
-#define VALID_PERIOD_TYPE(pt)  ((0 <= (pt)) && ((pt) < NUM_PERIOD_TYPES))
+#define VALID_PERIOD_TYPE(pt)    ((0 <= (pt)) && ((pt) < NUM_PERIOD_TYPES))
+#define VALID_WEEKEND_ADJ(wadj)  ((0 <= (wadj)) && ((wadj) < NUM_WEEKEND_ADJS))
 
 PeriodType
 recurrenceGetPeriodType(const Recurrence *r)
@@ -64,8 +68,14 @@
     return r ? r->start : invalid_gdate;
 }
 
+WeekendAdjust
+recurrenceGetWeekendAdjust(const Recurrence *r)
+{
+    return r ? r->wadj : WEEKEND_ADJ_INVALID;
+}
+
 void
-recurrenceSet(Recurrence *r, guint16 mult, PeriodType pt, const GDate *_start)
+recurrenceSet(Recurrence *r, guint16 mult, PeriodType pt, const GDate *_start, WeekendAdjust wadj)
 {
     r->ptype = VALID_PERIOD_TYPE(pt) ? pt : PERIOD_MONTH;
     r->mult = (pt == PERIOD_ONCE) ? 0 : (mult > 0 ? mult : 1);
@@ -97,6 +107,17 @@
         break;
     default: break;
     }
+
+    switch (r->ptype) {
+    case PERIOD_MONTH:
+    case PERIOD_END_OF_MONTH:
+    case PERIOD_YEAR:
+        r->wadj = wadj;
+        break;
+    default:
+        r->wadj = WEEKEND_ADJ_NONE;
+        break;
+    }
 }
 
 /* nth_weekday_compare() is a helper function for the
@@ -140,6 +161,7 @@
     PeriodType pt;
     const GDate *start;
     guint mult;
+    WeekendAdjust wadj;
 
     g_return_if_fail(r);
     g_return_if_fail(ref);
@@ -158,6 +180,7 @@
     /* Step 1: move FORWARD one period, passing exactly one occurrence. */
     mult = r->mult;
     pt = r->ptype;
+    wadj = r->wadj;
     switch (pt) {
     case PERIOD_YEAR:
         mult *= 12;             /* fall-through */
@@ -166,7 +189,54 @@
     case PERIOD_LAST_WEEKDAY:
     case PERIOD_END_OF_MONTH:
         /* Takes care of short months. */
-        if ( g_date_is_last_of_month(next) ||
+        if (r->wadj == WEEKEND_ADJ_BACK &&
+              (pt == PERIOD_YEAR || pt == PERIOD_MONTH || pt == PERIOD_END_OF_MONTH) &&
+              (g_date_get_weekday(next) == G_DATE_SATURDAY || g_date_get_weekday(next) == G_DATE_SUNDAY)) {
+            /* Allows the following Friday-based calculations to proceed if 'next'
+               is between Friday and the target day. */
+            g_date_subtract_days(next, g_date_get_weekday(next) == G_DATE_SATURDAY ? 1 : 2);
+        }
+        if (r->wadj == WEEKEND_ADJ_BACK &&
+              (pt == PERIOD_YEAR || pt == PERIOD_MONTH || pt == PERIOD_END_OF_MONTH) &&
+              g_date_get_weekday(next) == G_DATE_FRIDAY) {
+            GDate tmp_sat;
+            GDate tmp_sun;
+            g_date_set_julian(&tmp_sat, g_date_get_julian(next));
+            g_date_set_julian(&tmp_sun, g_date_get_julian(next));
+            g_date_add_days(&tmp_sat, 1);
+            g_date_add_days(&tmp_sun, 2);
+
+            if (pt == PERIOD_END_OF_MONTH) {
+            	if (g_date_is_last_of_month(next) ||
+                      g_date_is_last_of_month(&tmp_sat) ||
+            	      g_date_is_last_of_month(&tmp_sun))
+                	g_date_add_months(next, mult);
+            	else
+                	/* one fewer month fwd because of the occurrence in this month */
+                	g_date_add_months(next, mult - 1);
+            } else {
+			    if (g_date_get_day(&tmp_sat) == g_date_get_day(start)) {
+                    g_date_add_days(next, 1);
+                    g_date_add_months(next, mult);
+			    } else if (g_date_get_day(&tmp_sun) == g_date_get_day(start)) {
+                    g_date_add_days(next, 2);
+                    g_date_add_months(next, mult);
+			    } else if (g_date_get_day(next) >= g_date_get_day(start)) {
+                    g_date_add_months(next, mult);
+            	} else if (g_date_is_last_of_month(next)) {
+                	g_date_add_months(next, mult);
+            	} else if (g_date_is_last_of_month(&tmp_sat)) {
+                    g_date_add_days(next, 1);
+                	g_date_add_months(next, mult);
+            	} else if (g_date_is_last_of_month(&tmp_sun)) {
+                    g_date_add_days(next, 2);
+                	g_date_add_months(next, mult);
+                } else {
+                    /* one fewer month fwd because of the occurrence in this month */
+                    g_date_add_months(next, mult - 1);
+                }
+            }
+        } else if ( g_date_is_last_of_month(next) ||
              ((pt == PERIOD_MONTH || pt == PERIOD_YEAR) &&
               g_date_get_day(next) >= g_date_get_day(start)) ||
              ((pt == PERIOD_NTH_WEEKDAY || pt == PERIOD_LAST_WEEKDAY) &&
@@ -213,6 +283,23 @@
         else
             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;
+                }
+            }
+        }
+
     } break;
     case PERIOD_WEEK:
     case PERIOD_DAY:
@@ -360,6 +447,23 @@
     return -1;
 }
 
+gchar *
+recurrenceWeekendAdjustToString(WeekendAdjust wadj)
+{
+    return VALID_WEEKEND_ADJ(wadj) ? g_strdup(weekend_adj_strings[wadj]) : NULL;
+}
+
+WeekendAdjust
+recurrenceWeekendAdjustFromString(const gchar *str)
+{
+    int i;
+
+    for (i = 0; i < NUM_WEEKEND_ADJS; i++)
+        if (safe_strcmp(weekend_adj_strings[i], str) == 0)
+            return i;
+    return -1;
+}
+
 gboolean
 recurrenceListIsSemiMonthly(GList *recurrences)
 {

Modified: gnucash/trunk/src/engine/Recurrence.h
===================================================================
--- gnucash/trunk/src/engine/Recurrence.h	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/engine/Recurrence.h	2008-11-26 21:17:30 UTC (rev 17725)
@@ -57,12 +57,21 @@
     PERIOD_INVALID = -1,
 } PeriodType;
 
+typedef enum {
+    WEEKEND_ADJ_NONE,
+    WEEKEND_ADJ_BACK,    /* Previous weekday */
+    WEEKEND_ADJ_FORWARD, /* Next weekday */
+    NUM_WEEKEND_ADJS,
+    WEEKEND_ADJ_INVALID = -1,
+} WeekendAdjust;
+
 /* Recurrences represent both the phase and period of a recurring event. */
 
 typedef struct {
-    GDate start;       /* First date in the recurrence; specifies phase. */
-    PeriodType ptype;  /* see PeriodType enum */
-    guint16 mult;      /* a period multiplier */
+    GDate start;         /* First date in the recurrence; specifies phase. */
+    PeriodType ptype;    /* see PeriodType enum */
+    guint16 mult;        /* a period multiplier */
+	WeekendAdjust wadj;  /* see WeekendAdjust enum */
 } Recurrence;
 
 
@@ -90,12 +99,13 @@
 
 */
 void recurrenceSet(Recurrence *r, guint16 mult, PeriodType pt,
-                   const GDate *date);
+                   const GDate *date, WeekendAdjust wadj);
 
 /* get the fields */
 PeriodType recurrenceGetPeriodType(const Recurrence *r);
 guint recurrenceGetMultiplier(const Recurrence *r);
 GDate recurrenceGetDate(const Recurrence *r);
+WeekendAdjust recurrenceGetWeekendAdjust(const Recurrence *r);
 
 /* Get the occurence immediately after refDate.
  *
@@ -135,9 +145,11 @@
 void recurrenceListNextInstance(const GList *r, const GDate *refDate,
                                 GDate *nextDate);
 
-/* These two functions are only for xml storage, not user presentation. */
+/* These four functions are only for xml storage, not user presentation. */
 gchar *recurrencePeriodTypeToString(PeriodType pt);
 PeriodType recurrencePeriodTypeFromString(const gchar *str);
+gchar *recurrenceWeekendAdjustToString(WeekendAdjust wadj);
+WeekendAdjust recurrenceWeekendAdjustFromString(const gchar *str);
 
 /* For debugging.  Caller owns the returned string.  Not intl. */
 gchar *recurrenceToString(const Recurrence *r);

Modified: gnucash/trunk/src/engine/gnc-budget.c
===================================================================
--- gnucash/trunk/src/engine/gnc-budget.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/engine/gnc-budget.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -94,7 +94,7 @@
 	priv->num_periods = 12;
     g_date_set_time_t(&date, time(NULL));
     g_date_subtract_days(&date, g_date_get_day(&date)-1);
-    recurrenceSet(&priv->recurrence, 1, PERIOD_MONTH, &date);
+    recurrenceSet(&priv->recurrence, 1, PERIOD_MONTH, &date, WEEKEND_ADJ_NONE);
 }
 
 static void

Modified: gnucash/trunk/src/engine/test/test-recurrence.c
===================================================================
--- gnucash/trunk/src/engine/test/test-recurrence.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/engine/test/test-recurrence.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -31,10 +31,12 @@
 static QofBook *book;
 
 static void check_valid(GDate *next, GDate *ref, GDate *start,
-                        guint16 mult, PeriodType pt)
+                        guint16 mult, PeriodType pt, WeekendAdjust wadj)
 {
     gboolean valid;
     gint startToNext;
+    /* FIXME: The WeekendAdjust argument is completely ignored for
+       now. */
 
     valid = g_date_valid(next);
     if (pt == PERIOD_ONCE && g_date_compare(start, ref) <= 0)
@@ -134,27 +136,31 @@
     GDate d_ref, d_next;
     guint16 mult, mult_reg;
     PeriodType pt, pt_reg;
+    WeekendAdjust wadj, wadj_reg;
     gint32 j1, j2;
     gint i_ref;
 
     for (pt = PERIOD_ONCE; pt < NUM_PERIOD_TYPES; pt++) {
-        for (j1 = JULIAN_START; j1 < JULIAN_START + NUM_DATES_TO_TEST; j1++) {
-            g_date_set_julian(&d_start, j1);
-            for (i_ref = 0; i_ref < NUM_DATES_TO_TEST_REF; i_ref++) {
-                j2 = (guint32) get_random_int_in_range(1, 1 << 19);
-                g_date_set_julian(&d_ref, j2);
+        for (wadj = WEEKEND_ADJ_NONE; wadj < NUM_WEEKEND_ADJS; wadj++) {
+            for (j1 = JULIAN_START; j1 < JULIAN_START + NUM_DATES_TO_TEST; j1++) {
+                g_date_set_julian(&d_start, j1);
+                for (i_ref = 0; i_ref < NUM_DATES_TO_TEST_REF; i_ref++) {
+                    j2 = (guint32) get_random_int_in_range(1, 1 << 19);
+                    g_date_set_julian(&d_ref, j2);
 
-                for (mult = 0; mult < NUM_MULT_TO_TEST; mult++) {
-                    recurrenceSet(&r, mult, pt, &d_start);
-                    pt_reg = recurrenceGetPeriodType(&r);
-                    d_start_reg = recurrenceGetDate(&r);
-                    mult_reg = recurrenceGetMultiplier(&r);
+                    for (mult = 0; mult < NUM_MULT_TO_TEST; mult++) {
+                        recurrenceSet(&r, mult, pt, &d_start, wadj);
+                        pt_reg = recurrenceGetPeriodType(&r);
+                        d_start_reg = recurrenceGetDate(&r);
+                        mult_reg = recurrenceGetMultiplier(&r);
+                        wadj_reg = recurrenceGetWeekendAdjust(&r);
 
-                    recurrenceNextInstance(&r, &d_ref, &d_next);
-                    check_valid(&d_next, &d_ref, &d_start_reg,
-                                mult_reg, pt_reg);
+                        recurrenceNextInstance(&r, &d_ref, &d_next);
+                        check_valid(&d_next, &d_ref, &d_start_reg,
+                                    mult_reg, pt_reg, wadj_reg);
 
-                }
+                    }
+               }
             }
         }
     }
@@ -189,10 +195,10 @@
     g_date_set_dmy(&true_next, nd, nm, ny);
 
 
-    recurrenceSet(&r, mult, pt, &start);
+    recurrenceSet(&r, mult, pt, &start, WEEKEND_ADJ_NONE);
     recurrenceNextInstance(&r, &ref, &next);
 
-    check_valid(&next, &ref, &start, mult, pt);
+    check_valid(&next, &ref, &start, mult, pt, WEEKEND_ADJ_NONE);
     if (!test_equal(&next, &true_next)) {
         gchar s1[21], s2[21], s3[21];
         g_date_strftime(s1, 20, "%x", &start);

Modified: gnucash/trunk/src/engine/test-core/test-engine-stuff.c
===================================================================
--- gnucash/trunk/src/engine/test-core/test-engine-stuff.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/engine/test-core/test-engine-stuff.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -2148,7 +2148,7 @@
 daily_freq(GDate* start, int multiplier)
 {
      Recurrence *r = g_new0(Recurrence, 1);
-     recurrenceSet(r, multiplier, PERIOD_DAY, start);
+     recurrenceSet(r, multiplier, PERIOD_DAY, start, WEEKEND_ADJ_NONE);
      return r;
 }
 
@@ -2156,7 +2156,7 @@
 once_freq(GDate *when)
 {
      Recurrence *r = g_new0(Recurrence, 1);
-     recurrenceSet(r, 1, PERIOD_ONCE, when);
+     recurrenceSet(r, 1, PERIOD_ONCE, when, WEEKEND_ADJ_NONE);
      return r;
 }
 

Modified: gnucash/trunk/src/gnome/dialog-sx-from-trans.c
===================================================================
--- gnucash/trunk/src/gnome/dialog-sx-from-trans.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/gnome/dialog-sx-from-trans.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -302,7 +302,7 @@
   {
   case FREQ_DAILY: {
       Recurrence *r = g_new0(Recurrence, 1);
-      recurrenceSet(r, 1, PERIOD_DAY, date);
+      recurrenceSet(r, 1, PERIOD_DAY, date, WEEKEND_ADJ_NONE);
       *recurrences = g_list_append(*recurrences, r);
     } break;
 
@@ -310,7 +310,7 @@
   case FREQ_BIWEEKLY: {
       Recurrence *r = g_new0(Recurrence, 1);
       int mult = (index == FREQ_BIWEEKLY ? 2 : 1);
-      recurrenceSet(r, mult, PERIOD_WEEK, date);
+      recurrenceSet(r, mult, PERIOD_WEEK, date, WEEKEND_ADJ_NONE);
       *recurrences = g_list_append(*recurrences, r);
   } break;
 
@@ -323,7 +323,7 @@
                   : (index == FREQ_QUARTERLY
                      ? 3
                      : 12));
-      recurrenceSet(r, mult, PERIOD_MONTH, date);
+      recurrenceSet(r, mult, PERIOD_MONTH, date, recurrenceGetWeekendAdjust(r));
       *recurrences = g_list_append(*recurrences, r);
   } break;
 

Modified: gnucash/trunk/src/gnome/dialog-sx-since-last-run.c
===================================================================
--- gnucash/trunk/src/gnome/dialog-sx-since-last-run.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/gnome/dialog-sx-since-last-run.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -802,6 +802,7 @@
         gnc_ui_sx_since_last_run_dialog(inst_model, auto_created_txns);
         auto_created_txns = NULL;
     }
+#if 0
     else
     {
         if (summary.num_auto_create_no_notify_instances != 0)
@@ -817,6 +818,7 @@
                  summary.num_auto_create_no_notify_instances);
         }
     }
+#endif
     g_list_free(auto_created_txns);
     g_object_unref(G_OBJECT(inst_model));
 }

Modified: gnucash/trunk/src/gnome/druid-acct-period.c
===================================================================
--- gnucash/trunk/src/gnome/druid-acct-period.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/gnome/druid-acct-period.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -544,7 +544,7 @@
 
   {
       Recurrence *r = g_new0(Recurrence, 1);
-      recurrenceSet(r, 1, PERIOD_MONTH, &info->closing_date);
+      recurrenceSet(r, 1, PERIOD_MONTH, &info->closing_date, WEEKEND_ADJ_NONE);
       info->period = NULL;
       info->period = g_list_append(info->period, r);
   }

Modified: gnucash/trunk/src/gnome/druid-loan.c
===================================================================
--- gnucash/trunk/src/gnome/druid-loan.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/gnome/druid-loan.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -804,7 +804,7 @@
         ldd->ld.repayment_schedule = NULL;
         {
                 Recurrence *r = g_new0(Recurrence, 1);
-                recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate);
+                recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate, WEEKEND_ADJ_NONE);
                 ldd->ld.repayment_schedule = g_list_append(ldd->ld.repayment_schedule, r);
         }
 
@@ -1662,7 +1662,7 @@
 
                 if ( rod->schedule == NULL ) {
                         Recurrence *r = g_new0(Recurrence, 1);
-                        recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate);
+                        recurrenceSet(r, 1, PERIOD_MONTH, ldd->ld.startDate, WEEKEND_ADJ_NONE);
                         rod->schedule = g_list_append(rod->schedule, r);
                 }
                 if ( rod->startDate == NULL ) {

Modified: gnucash/trunk/src/gnome/glade/sched-xact.glade
===================================================================
--- gnucash/trunk/src/gnome/glade/sched-xact.glade	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/gnome/glade/sched-xact.glade	2008-11-26 21:17:30 UTC (rev 17725)
@@ -1892,6 +1892,47 @@
 			      <property name="fill">True</property>
 			    </packing>
 			  </child>
+
+			  <child>
+			    <widget class="GtkLabel" id="label847751">
+			      <property name="visible">True</property>
+			      <property name="label" translatable="yes">except on weekends:</property>
+			      <property name="use_underline">False</property>
+			      <property name="use_markup">False</property>
+			      <property name="justify">GTK_JUSTIFY_RIGHT</property>
+			      <property name="wrap">False</property>
+			      <property name="selectable">False</property>
+			      <property name="xalign">1</property>
+			      <property name="yalign">0.5</property>
+			      <property name="xpad">0</property>
+			      <property name="ypad">0</property>
+			      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			      <property name="width_chars">-1</property>
+			      <property name="single_line_mode">False</property>
+			      <property name="angle">0</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">2</property>
+			      <property name="expand">False</property>
+			      <property name="fill">True</property>
+			    </packing>
+			  </child>
+
+			  <child>
+			    <widget class="GtkComboBox" id="semimonthly_first_weekend">
+			      <property name="visible">True</property>
+			      <property name="items" translatable="yes">No change
+Use previous weekday
+Use next weekday</property>
+			      <property name="add_tearoffs">False</property>
+			      <property name="focus_on_click">True</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
 			</widget>
 			<packing>
 			  <property name="padding">3</property>
@@ -1982,6 +2023,47 @@
 			      <property name="fill">False</property>
 			    </packing>
 			  </child>
+
+			  <child>
+			    <widget class="GtkLabel" id="label847752">
+			      <property name="visible">True</property>
+			      <property name="label" translatable="yes">except on weekends:</property>
+			      <property name="use_underline">False</property>
+			      <property name="use_markup">False</property>
+			      <property name="justify">GTK_JUSTIFY_RIGHT</property>
+			      <property name="wrap">False</property>
+			      <property name="selectable">False</property>
+			      <property name="xalign">1</property>
+			      <property name="yalign">0.5</property>
+			      <property name="xpad">0</property>
+			      <property name="ypad">0</property>
+			      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			      <property name="width_chars">-1</property>
+			      <property name="single_line_mode">False</property>
+			      <property name="angle">0</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">2</property>
+			      <property name="expand">False</property>
+			      <property name="fill">True</property>
+			    </packing>
+			  </child>
+
+			  <child>
+			    <widget class="GtkComboBox" id="semimonthly_second_weekend">
+			      <property name="visible">True</property>
+			      <property name="items" translatable="yes">No change
+Use previous weekday
+Use next weekday</property>
+			      <property name="add_tearoffs">False</property>
+			      <property name="focus_on_click">True</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
 			</widget>
 			<packing>
 			  <property name="padding">3</property>
@@ -2202,16 +2284,57 @@
 			      <property name="fill">False</property>
 			    </packing>
 			  </child>
+
+			  <child>
+			    <widget class="GtkLabel" id="label847750">
+			      <property name="visible">True</property>
+			      <property name="label" translatable="yes">except on weekends:</property>
+			      <property name="use_underline">False</property>
+			      <property name="use_markup">False</property>
+			      <property name="justify">GTK_JUSTIFY_RIGHT</property>
+			      <property name="wrap">False</property>
+			      <property name="selectable">False</property>
+			      <property name="xalign">1</property>
+			      <property name="yalign">0.5</property>
+			      <property name="xpad">0</property>
+			      <property name="ypad">0</property>
+			      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+			      <property name="width_chars">-1</property>
+			      <property name="single_line_mode">False</property>
+			      <property name="angle">0</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">2</property>
+			      <property name="expand">False</property>
+			      <property name="fill">True</property>
+			    </packing>
+			  </child>
+
+			  <child>
+			    <widget class="GtkComboBox" id="monthly_weekend">
+			      <property name="visible">True</property>
+			      <property name="items" translatable="yes">No change
+Use previous weekday
+Use next weekday</property>
+			      <property name="add_tearoffs">False</property>
+			      <property name="focus_on_click">True</property>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
 			</widget>
 			<packing>
-			  <property name="padding">5</property>
+			  <property name="padding">3</property>
 			  <property name="expand">False</property>
 			  <property name="fill">True</property>
 			</packing>
 		      </child>
 		    </widget>
 		    <packing>
-		      <property name="padding">5</property>
+		      <property name="padding">3</property>
 		      <property name="expand">True</property>
 		      <property name="fill">True</property>
 		    </packing>

Modified: gnucash/trunk/src/gnome/gnc-plugin-page-budget.c
===================================================================
--- gnucash/trunk/src/gnome/gnc-plugin-page-budget.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/gnome/gnc-plugin-page-budget.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -296,7 +296,7 @@
     priv->fd.show_zero_total = TRUE;
 
     priv->sigFigs = 1;
-    recurrenceSet(&priv->r, 1, PERIOD_MONTH, NULL); 
+    recurrenceSet(&priv->r, 1, PERIOD_MONTH, NULL, WEEKEND_ADJ_NONE); 
 
     LEAVE("page %p, priv %p, action group %p",
           plugin_page, priv, action_group);
@@ -935,7 +935,8 @@
         
         gnc_date_edit_get_gdate(GNC_DATE_EDIT(gde), &date);
         recurrenceSet(&priv->r, recurrenceGetMultiplier(r), 
-                      recurrenceGetPeriodType(r), &date);
+                      recurrenceGetPeriodType(r), &date,
+                      recurrenceGetWeekendAdjust(r));
         priv->sigFigs = 
             gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(dtr));
 

Modified: gnucash/trunk/src/gnome/gnc-plugin-page-sx-list.c
===================================================================
--- gnucash/trunk/src/gnome/gnc-plugin-page-sx-list.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/gnome/gnc-plugin-page-sx-list.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -489,7 +489,7 @@
         
         g_date_clear(&now, 1);
         g_date_set_time_t(&now, time(NULL));
-        recurrenceSet(r, 1, PERIOD_MONTH, &now);
+        recurrenceSet(r, 1, PERIOD_MONTH, &now, WEEKEND_ADJ_NONE);
         schedule = gnc_sx_get_schedule(new_sx);
         schedule = g_list_append(schedule, r);
         gnc_sx_set_schedule(new_sx, schedule);

Modified: gnucash/trunk/src/gnome-utils/gnc-frequency.c
===================================================================
--- gnucash/trunk/src/gnome-utils/gnc-frequency.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/gnome-utils/gnc-frequency.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -157,11 +157,14 @@
         char *name;
         void (*fn)();
     } comboBoxes[] = {
-        { "freq_combobox",      freq_combo_changed },
-        { "semimonthly_first",  semimonthly_sel_changed },
-        { "semimonthly_second", semimonthly_sel_changed },
-        { "monthly_day",        monthly_sel_changed },
-        { NULL,                 NULL }
+        { "freq_combobox",               freq_combo_changed },
+        { "semimonthly_first",          semimonthly_sel_changed },
+        { "semimonthly_first_weekend",  semimonthly_sel_changed },
+        { "semimonthly_second",         semimonthly_sel_changed },
+        { "semimonthly_second_weekend", semimonthly_sel_changed },
+        { "monthly_day",                monthly_sel_changed },
+        { "monthly_weekend",            monthly_sel_changed },
+        { NULL,                         NULL }
     };
 
     static const struct spinvalTuple {
@@ -409,8 +412,12 @@
 
              dom_combobox = glade_xml_get_widget(gf->gxml, "semimonthly_first");
              gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), _get_monthly_combobox_index(first));
+             dom_combobox = glade_xml_get_widget(gf->gxml, "semimonthly_first_weekend");
+             gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), recurrenceGetWeekendAdjust(first));
              dom_combobox = glade_xml_get_widget(gf->gxml, "semimonthly_second");
              gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), _get_monthly_combobox_index(second));
+             dom_combobox = glade_xml_get_widget(gf->gxml, "semimonthly_second_weekend");
+             gtk_combo_box_set_active(GTK_COMBO_BOX(dom_combobox), recurrenceGetWeekendAdjust(second));
 
              gtk_notebook_set_current_page(gf->nb, PAGE_SEMI_MONTHLY);
              gtk_combo_box_set_active(gf->freqComboBox, PAGE_SEMI_MONTHLY);
@@ -461,7 +468,7 @@
          case PERIOD_YEAR:
          case PERIOD_LAST_WEEKDAY: {
              guint multiplier;
-             GtkWidget *multipler_spin, *day_of_month;
+             GtkWidget *multipler_spin, *day_of_month, *weekend_mode;
              
              multipler_spin = glade_xml_get_widget(gf->gxml, "monthly_spin");
              multiplier = recurrenceGetMultiplier(r);
@@ -471,6 +478,8 @@
 
              day_of_month = glade_xml_get_widget(gf->gxml, "monthly_day");
              gtk_combo_box_set_active(GTK_COMBO_BOX(day_of_month), _get_monthly_combobox_index(r));
+             weekend_mode = glade_xml_get_widget(gf->gxml, "monthly_weekend");
+             gtk_combo_box_set_active(GTK_COMBO_BOX(weekend_mode), recurrenceGetWeekendAdjust(r));
 
              gtk_notebook_set_current_page(gf->nb, PAGE_MONTHLY);
              gtk_combo_box_set_active(gf->freqComboBox, PAGE_MONTHLY);
@@ -498,12 +507,14 @@
 }
 
 static Recurrence*
-_get_day_of_month_recurrence(GncFrequency *gf, GDate *start_date, int multiplier, char *combo_name)
+_get_day_of_month_recurrence(GncFrequency *gf, GDate *start_date, int multiplier, char *combo_name, char *combo_weekend_name)
 {
     int last_day_of_month_option_index = 31;
     Recurrence *r;
     GtkWidget *day_of_month_combo = glade_xml_get_widget(gf->gxml, combo_name);
     int day_of_month_index = gtk_combo_box_get_active(GTK_COMBO_BOX(day_of_month_combo));
+    GtkWidget *weekend_adjust_combo = glade_xml_get_widget(gf->gxml, combo_weekend_name);
+    int weekend_adjust = gtk_combo_box_get_active(GTK_COMBO_BOX(weekend_adjust_combo));
         
     r = g_new0(Recurrence, 1);
     if (day_of_month_index > LAST_DAY_OF_MONTH_OPTION_INDEX)
@@ -514,12 +525,12 @@
         g_date_set_day(day_of_week_date, 1);
         while (g_date_get_weekday(day_of_week_date) != selected_day_of_week)
             g_date_add_days(day_of_week_date, 1);
-        recurrenceSet(r, multiplier, PERIOD_LAST_WEEKDAY, day_of_week_date);
+        recurrenceSet(r, multiplier, PERIOD_LAST_WEEKDAY, day_of_week_date, weekend_adjust);
     }
     else if (day_of_month_index == LAST_DAY_OF_MONTH_OPTION_INDEX)
     {
         GDate *day_of_month = g_date_new_julian(g_date_get_julian(start_date));
-        recurrenceSet(r, multiplier, PERIOD_END_OF_MONTH, day_of_month);
+        recurrenceSet(r, multiplier, PERIOD_END_OF_MONTH, day_of_month, weekend_adjust);
     }
     else
     {
@@ -529,7 +540,7 @@
                              g_date_get_days_in_month(g_date_get_month(day_of_month),
                                                       g_date_get_year(day_of_month)));
         g_date_set_day(day_of_month, allowable_date);
-        recurrenceSet(r, multiplier, PERIOD_MONTH, day_of_month);
+        recurrenceSet(r, multiplier, PERIOD_MONTH, day_of_month, weekend_adjust);
     }
     return r;
 }
@@ -556,13 +567,13 @@
     } break;
     case PAGE_ONCE: {
         Recurrence *r = g_new0(Recurrence, 1);
-        recurrenceSet(r, 1, PERIOD_ONCE, &start_date);
+        recurrenceSet(r, 1, PERIOD_ONCE, &start_date, WEEKEND_ADJ_NONE);
         *recurrences = g_list_append(*recurrences, r);
     } break;
     case PAGE_DAILY: {
         gint multiplier = _get_multiplier_from_widget(gf, "daily_spin");
         Recurrence *r = g_new0(Recurrence, 1);
-        recurrenceSet(r, multiplier, PERIOD_DAY, &start_date);
+        recurrenceSet(r, multiplier, PERIOD_DAY, &start_date, WEEKEND_ADJ_NONE);
         *recurrences = g_list_append(*recurrences, r);
     } break;
     case PAGE_WEEKLY: {
@@ -584,19 +595,19 @@
                 g_date_add_days(day_of_week_aligned_date, 1);
 
             r = g_new0(Recurrence, 1);
-            recurrenceSet(r, multiplier, PERIOD_WEEK, day_of_week_aligned_date);
+            recurrenceSet(r, multiplier, PERIOD_WEEK, day_of_week_aligned_date, WEEKEND_ADJ_NONE);
             
             *recurrences = g_list_append(*recurrences, r);
         }
     } break;
     case PAGE_SEMI_MONTHLY: {
         int multiplier = _get_multiplier_from_widget(gf, "semimonthly_spin");
-        *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier, "semimonthly_first"));
-        *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier, "semimonthly_second"));
+        *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier, "semimonthly_first", "semimonthly_first_weekend"));
+        *recurrences = g_list_append(*recurrences, _get_day_of_month_recurrence(gf, &start_date, multiplier, "semimonthly_second", "semimonthly_second_weekend"));
     } break;
     case PAGE_MONTHLY: {
         int multiplier = _get_multiplier_from_widget(gf, "monthly_spin");
-        Recurrence *r = _get_day_of_month_recurrence(gf, &start_date, multiplier, "monthly_day");
+        Recurrence *r = _get_day_of_month_recurrence(gf, &start_date, multiplier, "monthly_day", "monthly_weekend");
         *recurrences = g_list_append(*recurrences, r);
     } break;
     default:

Modified: gnucash/trunk/src/gnome-utils/gnc-recurrence.c
===================================================================
--- gnucash/trunk/src/gnome-utils/gnc-recurrence.c	2008-11-26 14:35:36 UTC (rev 17724)
+++ gnucash/trunk/src/gnome-utils/gnc-recurrence.c	2008-11-26 21:17:30 UTC (rev 17725)
@@ -166,7 +166,7 @@
 {
     GtkVBox *vb;
 
-    recurrenceSet(&gr->recurrence, 1, PERIOD_MONTH, NULL);
+    recurrenceSet(&gr->recurrence, 1, PERIOD_MONTH, NULL, WEEKEND_ADJ_NONE);
 
     gr->xml = gnc_glade_xml_new("budget.glade", "RecurrenceEntryVBox");
     vb = GTK_VBOX(glade_xml_get_widget(gr->xml, "RecurrenceEntryVBox"));
@@ -286,7 +286,7 @@
     }
 
 
-    recurrenceSet(&gr->recurrence, mult, pt, &start);
+    recurrenceSet(&gr->recurrence, mult, pt, &start, WEEKEND_ADJ_NONE);
     return &gr->recurrence;
 
 }



More information about the gnucash-changes mailing list