r20672 - gnucash/trunk/src - Bug #650598: Patches to allow scheduling of Nth Day of Month Transactions.

Christian Stimming cstim at code.gnucash.org
Mon May 23 15:13:56 EDT 2011


Author: cstim
Date: 2011-05-23 15:13:55 -0400 (Mon, 23 May 2011)
New Revision: 20672
Trac: http://svn.gnucash.org/trac/changeset/20672

Modified:
   gnucash/trunk/src/engine/Recurrence.c
   gnucash/trunk/src/gnome-utils/gnc-frequency.c
   gnucash/trunk/src/gnome/glade/sched-xact.glade
   gnucash/trunk/src/gnome/gtkbuilder/sched-xact.glade
Log:
Bug #650598: Patches to allow scheduling of Nth Day of Month Transactions.

Patch by "Rich":

I have transactions that should be scheduled on the second Wednesday of every
month.  There is presently no way to do it.  I made changes and have tested
them.
This patch enhances the scheduled transaction feature to allow nth day of week
monthly entries. The weekend adjust spinner is ignored when an nth day of week
entry is scheduled.

Modified: gnucash/trunk/src/engine/Recurrence.c
===================================================================
--- gnucash/trunk/src/engine/Recurrence.c	2011-05-21 20:16:57 UTC (rev 20671)
+++ gnucash/trunk/src/engine/Recurrence.c	2011-05-23 19:13:55 UTC (rev 20672)
@@ -140,14 +140,16 @@
 nth_weekday_compare(const GDate *start, const GDate *next, PeriodType pt)
 {
     GDateDay sd, nd;
-    gint matchday, dim;
+    gint matchday, dim, week;
 
     nd = g_date_get_day(next);
     sd = g_date_get_day(start);
-
+    week = sd / 7 > 3 ? 3 : sd / 7;
+    if (week > 0 && sd % 7 == 0 && sd != 28)
+        --week;
     /* matchday has a week part, capped at 3 weeks, and a day part,
        capped at 7 days, so max(matchday) == 3*7 + 7 == 28. */
-    matchday = 7 * ((sd - 1) / 7 == 4 ? 3 : (sd - 1) / 7) +
+    matchday = 7 * week + //((sd - 1) / 7 == 4 ? 3 : (sd - 1) / 7) +
                (nd - g_date_get_weekday(next) + g_date_get_weekday(start) + 7) % 7;
     /* That " + 7" is to avoid negative modulo in case nd < 6. */
 
@@ -155,6 +157,8 @@
               g_date_get_month(next), g_date_get_year(next));
     if ((dim - matchday) >= 7 && pt == PERIOD_LAST_WEEKDAY)
         matchday += 7;     /* Go to the fifth week, if needed */
+    if (pt == PERIOD_NTH_WEEKDAY && (matchday % 7 == 0))
+        matchday += 7;
 
     return matchday - nd;  /* Offset from 'next' to matchday */
 }
@@ -269,8 +273,7 @@
         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) &&
-                   nth_weekday_compare(start, next, pt) <= 0) )
+                  ((pt == PERIOD_NTH_WEEKDAY || pt == PERIOD_LAST_WEEKDAY)) )
             g_date_add_months(next, mult);
         else
             /* one fewer month fwd because of the occurrence in this month */
@@ -308,8 +311,17 @@
            the day in one of the three possible ways. */
         dim = g_date_get_days_in_month(g_date_get_month(next),
                                        g_date_get_year(next));
-        if (pt == PERIOD_NTH_WEEKDAY || pt == PERIOD_LAST_WEEKDAY)
-            g_date_add_days(next, nth_weekday_compare(start, next, pt));
+        if (pt == PERIOD_LAST_WEEKDAY || pt == PERIOD_NTH_WEEKDAY)
+        {
+            gint wdresult = nth_weekday_compare(start, next, pt);
+            if (wdresult < 0)
+            {
+                wdresult = -wdresult;
+                g_date_subtract_days(next, wdresult);
+            }
+            else
+                g_date_add_days(next, wdresult);
+        }
         else if (pt == PERIOD_END_OF_MONTH || g_date_get_day(start) >= dim)
             g_date_set_day(next, dim);  /* last day in the month */
         else
@@ -618,6 +630,22 @@
         /* translators: %s is an already-localized form of the day of the week. */
         g_string_append_printf(buf, _("last %s"), day_name_buf);
     }
+    else if (recurrenceGetPeriodType(r) == PERIOD_NTH_WEEKDAY)
+    {
+        int week = 0;
+        int day_of_month_index = 0;
+        const char *numerals[] = {N_("1st"), N_("2nd"), N_("3rd"), N_("4th")};
+        gchar day_name_buf[abbrev_day_name_bufsize];
+
+        gnc_dow_abbrev(day_name_buf, abbrev_day_name_bufsize, g_date_get_weekday(&date) % 7);
+        day_of_month_index = g_date_get_day(&date) - 1;
+        week = day_of_month_index / 7 > 3 ? 3 : day_of_month_index / 7;
+        if (week > 0 && day_of_month_index % 7 == 0)
+            --week;
+        /* translators: %s is the string 1st, 2nd, 3rd and so on, and
+         * %s is an already-localized form of the day of the week. */
+        g_string_append_printf(buf, _("%s %s"), _(numerals[week]), day_name_buf);
+    }
     else
     {
         /* translators: %u is the day of month */
@@ -657,7 +685,7 @@
             g_string_append_printf(buf, " ");
             if (recurrenceGetMultiplier(first) > 1)
             {
-                /* translators: %u is the recurrence multiplier */
+                /* translators: %u is the recurrence multiplier number */
                 g_string_append_printf(buf, _(" (x%u)"), recurrenceGetMultiplier(first));
             }
             g_string_append_printf(buf, ": ");
@@ -714,8 +742,16 @@
         break;
         case PERIOD_NTH_WEEKDAY:
         {
-            g_warning("nth weekday not handled");
-            g_string_printf(buf, "@fixme: nth weekday not handled");
+            //g_warning("nth weekday not handled");
+            //g_string_printf(buf, "@fixme: nth weekday not handled");
+            g_string_printf(buf, "%s", _("Monthly"));
+            if (multiplier > 1)
+            {
+                /* translators: %u is the recurrence multiplier. */
+                g_string_append_printf(buf, _(" (x%u)"), multiplier);
+            }
+            g_string_append_printf(buf, ": ");
+            _monthly_append_when(r, buf);
         }
         break;
         case PERIOD_YEAR:

Modified: gnucash/trunk/src/gnome/glade/sched-xact.glade
===================================================================
--- gnucash/trunk/src/gnome/glade/sched-xact.glade	2011-05-21 20:16:57 UTC (rev 20671)
+++ gnucash/trunk/src/gnome/glade/sched-xact.glade	2011-05-23 19:13:55 UTC (rev 20672)
@@ -1484,7 +1484,35 @@
 Last Thursday
 Last Friday
 Last Saturday
-Last Sunday</property>
+Last Sunday
+1st Mon
+1st Tue
+1st Wed
+1st Thu
+1st Fri
+1st Sat
+1st Sun
+2nd Mon
+2nd Tue
+2nd Wed
+2nd Thu
+2nd Fri
+2nd Sat
+2nd Sun
+3rd Mon
+3rd Tue
+3rd Wed
+3rd Thu
+3rd Fri
+3rd Sat
+3rd Sun
+4th Mon
+4th Tue
+4th Wed
+4th Thu
+4th Fri
+4th Sat
+4th Sun</property>
                               </widget>
                               <packing>
                                 <property name="expand">False</property>
@@ -1908,6 +1936,20 @@
                     <property name="column_spacing">5</property>
                     <property name="row_spacing">5</property>
                     <child>
+                      <widget class="GtkLabel" id="label847897">
+                        <property name="visible">True</property>
+                        <property name="xalign">1</property>
+                        <property name="label" translatable="yes">Interest Rate:</property>
+                        <property name="justify">center</property>
+                      </widget>
+                      <packing>
+                        <property name="top_attach">2</property>
+                        <property name="bottom_attach">3</property>
+                        <property name="x_options">GTK_FILL</property>
+                        <property name="y_options"></property>
+                      </packing>
+                    </child>
+                    <child>
                       <widget class="GtkLabel" id="label847899">
                         <property name="visible">True</property>
                         <property name="xalign">1</property>
@@ -2157,24 +2199,26 @@
                       </packing>
                     </child>
                     <child>
-                      <widget class="GtkComboBox" id="irate_type_combobox">
-                        <property name="visible">True</property>
-                        <property name="active">1</property>
-                        <property name="items" translatable="yes">Interest Rate
-APR (Compounded Daily)
-APR (Compounded Weekly)
-APR (Compounded Monthly)
-APR (Compounded Quarterly)
-APR (Compounded Annually)
-</property>
-                      </widget>
-                      <packing>
-                        <property name="top_attach">2</property>
-                        <property name="bottom_attach">3</property>
-                        <property name="x_options">GTK_FILL</property>
-                        <property name="y_options">GTK_FILL</property>
-                      </packing>
+                      <placeholder/>
                     </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
+                    <child>
+                      <placeholder/>
+                    </child>
                   </widget>
                   <packing>
                     <property name="position">0</property>

Modified: gnucash/trunk/src/gnome/gtkbuilder/sched-xact.glade
===================================================================
--- gnucash/trunk/src/gnome/gtkbuilder/sched-xact.glade	2011-05-21 20:16:57 UTC (rev 20671)
+++ gnucash/trunk/src/gnome/gtkbuilder/sched-xact.glade	2011-05-23 19:13:55 UTC (rev 20672)
@@ -2344,6 +2344,90 @@
       <row>
         <col id="0" translatable="yes">Last Sunday</col>
       </row>
+      <row>
+        <col id="0" translatable="yes">1st Mon</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">1st Tue</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">1st Wed</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">1st Thu</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">1st Fri</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">1st Sat</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">1st Sun</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">2nd Mon</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">2nd Tue</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">2nd Wed</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">2nd Thu</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">2nd Fri</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">2nd Sat</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">2nd Sun</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">3rd Mon</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">3rd Tue</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">3rd Wed</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">3rd Thu</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">3rd Fri</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">3rd Sat</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">3rd Sun</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">4th Mon</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">4th Tue</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">4th Wed</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">4th Thu</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">4th Fri</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">4th Sat</col>
+      </row>
+      <row>
+        <col id="0" translatable="yes">4th Sun</col>
+      </row>
     </data>
   </object>
   <object class="GtkListStore" id="liststore7">

Modified: gnucash/trunk/src/gnome-utils/gnc-frequency.c
===================================================================
--- gnucash/trunk/src/gnome-utils/gnc-frequency.c	2011-05-21 20:16:57 UTC (rev 20671)
+++ gnucash/trunk/src/gnome-utils/gnc-frequency.c	2011-05-23 19:13:55 UTC (rev 20672)
@@ -353,6 +353,7 @@
 _get_monthly_combobox_index(Recurrence *r)
 {
     GDate recurrence_date = recurrenceGetDate(r);
+    int week = 0;
     int day_of_month_index = g_date_get_day(&recurrence_date) - 1;
     if (recurrenceGetPeriodType(r) == PERIOD_END_OF_MONTH)
     {
@@ -364,6 +365,16 @@
         = LAST_DAY_OF_MONTH_OPTION_INDEX
           + g_date_get_weekday(&recurrence_date);
     }
+    else if (recurrenceGetPeriodType(r) == PERIOD_NTH_WEEKDAY)
+    {
+        week = day_of_month_index / 7 > 3 ? 3 : day_of_month_index / 7;
+        if (week > 0 && day_of_month_index % 7 == 0)
+            --week;
+        day_of_month_index = LAST_DAY_OF_MONTH_OPTION_INDEX + 7 +
+                             g_date_get_weekday(&recurrence_date) + 7 * week;
+
+
+    }
     /* else { default value } */
     return day_of_month_index;
 }
@@ -480,6 +491,7 @@
         case PERIOD_MONTH:
         case PERIOD_YEAR:
         case PERIOD_LAST_WEEKDAY:
+        case PERIOD_NTH_WEEKDAY:
         {
             guint multiplier;
             GtkWidget *multipler_spin, *day_of_month, *weekend_mode;
@@ -499,9 +511,6 @@
             gtk_combo_box_set_active(gf->freqComboBox, PAGE_MONTHLY);
         }
         break;
-        case PERIOD_NTH_WEEKDAY:
-            g_critical("unhandled period type [%d]", recurrenceGetPeriodType(r));
-            break;
         default:
             g_error("unknown recurrence period type [%d]", recurrenceGetPeriodType(r));
             break;
@@ -524,18 +533,31 @@
 static Recurrence*
 _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));
-
+    GDateWeekday selected_day_of_week;
+    GDate *day_of_week_date;
+    int selected_index, selected_week;
     r = g_new0(Recurrence, 1);
-    if (day_of_month_index > LAST_DAY_OF_MONTH_OPTION_INDEX)
+    if (day_of_month_index > LAST_DAY_OF_MONTH_OPTION_INDEX + 7)
     {
-        GDate *day_of_week_date = g_date_new_julian(g_date_get_julian(start_date));
-        GDateWeekday selected_day_of_week = day_of_month_index - LAST_DAY_OF_MONTH_OPTION_INDEX;
+        selected_index = day_of_month_index - LAST_DAY_OF_MONTH_OPTION_INDEX - 7;
+        day_of_week_date = g_date_new_julian(g_date_get_julian(start_date));
+        selected_week = (selected_index - 1) / 7 == 4 ? 3 : (selected_index - 1) / 7;
+        selected_day_of_week = selected_index - 7 * selected_week;
+        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);
+        g_date_add_days(day_of_week_date, 7 * selected_week);
+        recurrenceSet(r, multiplier, PERIOD_NTH_WEEKDAY, day_of_week_date, WEEKEND_ADJ_NONE);
+    }
+    else if (day_of_month_index > LAST_DAY_OF_MONTH_OPTION_INDEX)
+    {
+        day_of_week_date = g_date_new_julian(g_date_get_julian(start_date));
+        selected_day_of_week = day_of_month_index - LAST_DAY_OF_MONTH_OPTION_INDEX;
         // increment until we align on the DOW, but stay inside the month
         g_date_set_day(day_of_week_date, 1);
         while (g_date_get_weekday(day_of_week_date) != selected_day_of_week)



More information about the gnucash-changes mailing list