]> Dogcows Code - chaz/homebank/blob - src/hb-archive.c
Merge branch 'master' into ext-perl
[chaz/homebank] / src / hb-archive.c
1 /* HomeBank -- Free, easy, personal accounting for everyone.
2 * Copyright (C) 1995-2017 Maxime DOYEN
3 *
4 * This file is part of HomeBank.
5 *
6 * HomeBank is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * HomeBank is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "homebank.h"
21 #include "hb-archive.h"
22 #include "hb-split.h"
23
24 #include "ext.h"
25 #include "refcount.h"
26
27 /****************************************************************************/
28 /* Debug macros */
29 /****************************************************************************/
30 #define MYDEBUG 0
31
32 #if MYDEBUG
33 #define DB(x) (x);
34 #else
35 #define DB(x);
36 #endif
37
38 /* our global datas */
39 extern struct HomeBank *GLOBALS;
40
41
42 /* = = = = = = = = = = = = = = = = = = = = */
43 /* Archive */
44
45 Archive *da_archive_malloc(void)
46 {
47 return rc_alloc(sizeof(Archive));
48 }
49
50 Archive *da_archive_clone(Archive *src_item)
51 {
52 Archive *new_item = rc_dup(src_item, sizeof(Archive));
53
54 if(new_item)
55 {
56 //duplicate the string
57 new_item->wording = g_strdup(src_item->wording);
58
59 if( da_splits_clone(src_item->splits, new_item->splits) > 0)
60 new_item->flags |= OF_SPLIT; //Flag that Splits are active
61 }
62 return new_item;
63 }
64
65 void da_archive_free(Archive *item)
66 {
67 if(rc_unref(item))
68 {
69 if(item->wording != NULL)
70 g_free(item->wording);
71
72 da_splits_free(item->splits);
73 //item->flags &= ~(OF_SPLIT); //Flag that Splits are cleared
74
75 rc_free(item);
76 }
77 }
78
79 void da_archive_destroy(GList *list)
80 {
81 GList *tmplist = g_list_first(list);
82
83 while (tmplist != NULL)
84 {
85 Archive *item = tmplist->data;
86 da_archive_free(item);
87 tmplist = g_list_next(tmplist);
88 }
89 g_list_free(list);
90 }
91
92 static gint da_archive_glist_compare_func(Archive *a, Archive *b)
93 {
94 return hb_string_utf8_compare(a->wording, b->wording);
95 }
96
97
98 GList *da_archive_sort(GList *list)
99 {
100 return g_list_sort(list, (GCompareFunc)da_archive_glist_compare_func);
101 }
102
103 guint da_archive_length(void)
104 {
105 return g_list_length(GLOBALS->arc_list);
106 }
107
108 void da_archive_consistency(Archive *item)
109 {
110 Account *acc;
111 Category *cat;
112 Payee *pay;
113
114 // check category exists
115 cat = da_cat_get(item->kcat);
116 if(cat == NULL)
117 {
118 g_warning("arc consistency: fixed invalid cat %d", item->kcat);
119 item->kcat = 0;
120 GLOBALS->changes_count++;
121 }
122
123 split_cat_consistency(item->splits);
124
125 // check payee exists
126 pay = da_pay_get(item->kpay);
127 if(pay == NULL)
128 {
129 g_warning("arc consistency: fixed invalid pay %d", item->kpay);
130 item->kpay = 0;
131 GLOBALS->changes_count++;
132 }
133
134 // reset dst acc for non xfer transaction
135 if( item->paymode != PAYMODE_INTXFER )
136 item->kxferacc = 0;
137
138 // delete automation if dst_acc not exists
139 if(item->paymode == PAYMODE_INTXFER)
140 {
141 acc = da_acc_get(item->kxferacc);
142 if(acc == NULL)
143 {
144 item->flags &= ~(OF_AUTO); //delete flag
145 }
146 }
147
148 }
149
150 /* = = = = = = = = = = = = = = = = = = = = */
151
152 Archive *da_archive_init_from_transaction(Archive *arc, Transaction *txn)
153 {
154 //fill it
155 arc->amount = txn->amount;
156 arc->kacc = txn->kacc;
157 arc->kxferacc = txn->kxferacc;
158 arc->paymode = txn->paymode;
159 arc->flags = txn->flags & (OF_INCOME);
160 arc->status = txn->status;
161 arc->kpay = txn->kpay;
162 arc->kcat = txn->kcat;
163 if(txn->wording != NULL)
164 arc->wording = g_strdup(txn->wording);
165 else
166 arc->wording = g_strdup(_("(new archive)"));
167
168 if( da_splits_clone(txn->splits, arc->splits) > 0)
169 arc->flags |= OF_SPLIT; //Flag that Splits are active
170
171 return arc;
172 }
173
174
175
176
177 static guint32 _sched_date_get_next_post(Archive *arc, guint32 nextdate)
178 {
179 GDate *tmpdate;
180 guint32 nextpostdate = nextdate;
181
182 DB( g_print("\n[scheduled] _sched_date_get_next_post\n") );
183
184 tmpdate = g_date_new_julian(nextpostdate);
185 switch(arc->unit)
186 {
187 case AUTO_UNIT_DAY:
188 g_date_add_days(tmpdate, arc->every);
189 break;
190 case AUTO_UNIT_WEEK:
191 g_date_add_days(tmpdate, 7 * arc->every);
192 break;
193 case AUTO_UNIT_MONTH:
194 g_date_add_months(tmpdate, arc->every);
195 break;
196 case AUTO_UNIT_YEAR:
197 g_date_add_years(tmpdate, arc->every);
198 break;
199 }
200
201 /* get the final post date and free */
202 nextpostdate = g_date_get_julian(tmpdate);
203 g_date_free(tmpdate);
204
205 return nextpostdate;
206 }
207
208
209 gboolean scheduled_is_postable(Archive *arc)
210 {
211 gdouble value;
212
213 value = hb_amount_round(arc->amount, 2);
214 if( (arc->flags & OF_AUTO) && (arc->kacc > 0) && (value != 0.0) )
215 return TRUE;
216
217 return FALSE;
218 }
219
220
221 guint32 scheduled_get_postdate(Archive *arc, guint32 postdate)
222 {
223 GDate *tmpdate;
224 GDateWeekday wday;
225 guint32 finalpostdate;
226 gint shift;
227
228 DB( g_print("\n[scheduled] scheduled_get_postdate\n") );
229
230
231 finalpostdate = postdate;
232
233 tmpdate = g_date_new_julian(finalpostdate);
234 /* manage weekend exception */
235 if( arc->weekend > 0 )
236 {
237 wday = g_date_get_weekday(tmpdate);
238
239 DB( g_print(" %s wday=%d\n", arc->wording, wday) );
240
241 if( wday >= G_DATE_SATURDAY )
242 {
243 switch(arc->weekend)
244 {
245 case 1: /* shift before : sun 7-5=+2 , sat 6-5=+1 */
246 shift = wday - G_DATE_FRIDAY;
247 DB( g_print("sub=%d\n", shift) );
248 g_date_subtract_days (tmpdate, shift);
249 break;
250
251 case 2: /* shift after : sun 8-7=1 , sat 8-6=2 */
252 shift = 8 - wday;
253 DB( g_print("add=%d\n", shift) );
254 g_date_add_days (tmpdate, shift);
255 break;
256 }
257 }
258 }
259
260 /* get the final post date and free */
261 finalpostdate = g_date_get_julian(tmpdate);
262 g_date_free(tmpdate);
263
264 return finalpostdate;
265 }
266
267
268 guint32 scheduled_get_latepost_count(Archive *arc, guint32 jrefdate)
269 {
270 guint32 curdate = jrefdate - arc->nextdate;
271 guint32 nblate = 0;
272
273 /*
274 switch(arc->unit)
275 {
276 case AUTO_UNIT_DAY:
277 nbpost = (curdate / arc->every);
278 g_print("debug d: %d => %f\n", curdate, nbpost);
279 break;
280
281 case AUTO_UNIT_WEEK:
282 nbpost = (curdate / ( 7 * arc->every));
283 g_print("debug w: %d => %f\n", curdate, nbpost);
284 break;
285
286 case AUTO_UNIT_MONTH:
287 //approximate is sufficient
288 nbpost = (curdate / (( 365.2425 / 12) * arc->every));
289 g_print("debug m: %d => %f\n", curdate, nbpost);
290 break;
291
292 case AUTO_UNIT_YEAR:
293 //approximate is sufficient
294 nbpost = (curdate / ( 365.2425 * arc->every));
295 g_print("debug y: %d => %f\n", curdate, nbpost);
296 break;
297 }
298
299 nblate = floor(nbpost);
300
301 if(arc->flags & OF_LIMIT)
302 nblate = MIN(nblate, arc->limit);
303
304 nblate = MIN(nblate, 11);
305 */
306
307
308 // pre 5.1 way
309 curdate = arc->nextdate;
310 while(curdate <= jrefdate)
311 {
312 curdate = _sched_date_get_next_post(arc, curdate);
313 nblate++;
314 // break if over limit or at 11 max (to display +10)
315 if(nblate >= arc->limit || nblate >= 11)
316 break;
317 }
318
319 return nblate;
320 }
321
322
323 /* return 0 is max number of post is reached */
324 guint32 scheduled_date_advance(Archive *arc)
325 {
326 arc->nextdate = _sched_date_get_next_post(arc, arc->nextdate);
327
328 //#1556289
329 /* check limit, update and maybe break */
330 if(arc->flags & OF_LIMIT)
331 {
332 arc->limit--;
333 if(arc->limit <= 0)
334 {
335 arc->flags ^= (OF_LIMIT | OF_AUTO); // invert flags
336 arc->nextdate = 0;
337 }
338 }
339
340 return arc->nextdate;
341 }
342
343
344 /*
345 * return the maximum date a scheduled txn can be posted to
346 */
347 guint32 scheduled_date_get_post_max(void)
348 {
349 guint nbdays;
350 GDate *today, *maxdate;
351
352 DB( g_print("\n[scheduled] date_get_post_max\n") );
353
354 //add until xx of the next month (excluded)
355 if(GLOBALS->auto_smode == 0)
356 {
357 DB( g_print(" - max is %d of next month\n", GLOBALS->auto_weekday) );
358
359 today = g_date_new_julian(GLOBALS->today);
360
361 //we compute user xx weekday of next month
362 maxdate = g_date_new_julian(GLOBALS->today);
363 g_date_set_day(maxdate, GLOBALS->auto_weekday);
364 if(g_date_get_day (today) >= GLOBALS->auto_weekday)
365 g_date_add_months(maxdate, 1);
366
367 nbdays = g_date_days_between(today, maxdate);
368
369 g_date_free(maxdate);
370 g_date_free(today);
371 }
372 else
373 {
374 nbdays = GLOBALS->auto_nbdays;
375 }
376
377 DB( hb_print_date(GLOBALS->today, "today") );
378 DB( g_print(" - %d nbdays\n", nbdays) );
379 DB( hb_print_date(GLOBALS->today + nbdays, "maxpostdate") );
380
381 return GLOBALS->today + nbdays;
382 }
383
384
385 gint scheduled_post_all_pending(void)
386 {
387 GList *list;
388 gint count;
389 guint32 maxpostdate;
390 Transaction *txn;
391
392 DB( g_print("\n[scheduled] post_all_pending\n") );
393
394 count = 0;
395
396 maxpostdate = scheduled_date_get_post_max();
397
398 txn = da_transaction_malloc();
399
400 list = g_list_first(GLOBALS->arc_list);
401 while (list != NULL)
402 {
403 Archive *arc = list->data;
404
405 DB( g_print("\n eval %d for '%s'\n", scheduled_is_postable(arc), arc->wording) );
406
407 if(scheduled_is_postable(arc) == TRUE)
408 {
409 DB( g_print(" - every %d limit %d (to %d)\n", arc->every, arc->flags & OF_LIMIT, arc->limit) );
410 DB( hb_print_date(arc->nextdate, "next post") );
411
412 if(arc->nextdate < maxpostdate)
413 {
414 guint32 mydate = arc->nextdate;
415
416 while(mydate < maxpostdate)
417 {
418 DB( hb_print_date(mydate, arc->wording) );
419
420 da_transaction_init_from_template(txn, arc);
421 txn->date = scheduled_get_postdate(arc, mydate);
422 /* todo: ? fill in cheque number */
423
424 transaction_add(txn, NULL, 0);
425 GLOBALS->changes_count++;
426 count++;
427
428 da_transaction_clean(txn);
429
430 mydate = scheduled_date_advance(arc);
431
432 //DB( hb_print_date(mydate, "next on") );
433
434 if(mydate == 0)
435 goto nextarchive;
436 }
437
438 }
439 }
440 nextarchive:
441 list = g_list_next(list);
442 }
443
444 da_transaction_free (txn);
445
446 return count;
447 }
448
This page took 0.05027 seconds and 4 git commands to generate.