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