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