1 /********************************************************************\
2  * split-register-copy-ops.c -- copy/paste semantics for            *
3  *                                         transactions and splits  *
4  * Port to C of engine-interface                                    *
5  * originally written by Dave Peticolas <dave@krondo.com>           *
6  * © 2019 Geert Janssens
7  *                                                                  *
8  * This program is free software; you can redistribute it and/or    *
9  * modify it under the terms of the GNU General Public License as   *
10  * published by the Free Software Foundation; either version 2 of   *
11  * the License, or (at your option) any later version.              *
12  *                                                                  *
13  * This program is distributed in the hope that it will be useful,  *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
16  * GNU General Public License for more details.                     *
17  *                                                                  *
18  * You should have received a copy of the GNU General Public License*
19  * along with this program; if not, write to the Free Software      *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.        *
21 \********************************************************************/
22 
23 #include <glib.h>
24 
25 #include "config.h"
26 #include "split-register-copy-ops.h"
27 
28 /* accessors */
gnc_float_split_get_split(const FloatingSplit * fs)29 Split *gnc_float_split_get_split (const FloatingSplit *fs)
30 {
31     g_return_val_if_fail (fs, NULL);
32     return fs->m_split;
33 }
34 
gnc_float_split_get_account(const FloatingSplit * fs)35 Account *gnc_float_split_get_account (const FloatingSplit *fs) /* direct account pointer rather than account guid */
36 {
37     g_return_val_if_fail (fs, NULL);
38     return fs->m_account;
39 }
40 
gnc_float_split_get_transaction(const FloatingSplit * fs)41 Transaction *gnc_float_split_get_transaction (const FloatingSplit *fs) /* direct transaction pointer rather than transaction guid */
42 {
43     g_return_val_if_fail (fs, NULL);
44     return fs->m_transaction;
45 }
46 
gnc_float_split_get_memo(const FloatingSplit * fs)47 const char *gnc_float_split_get_memo (const FloatingSplit *fs)
48 {
49     g_return_val_if_fail (fs, NULL);
50     return fs->m_memo;
51 }
52 
gnc_float_split_get_action(const FloatingSplit * fs)53 const char *gnc_float_split_get_action (const FloatingSplit *fs)
54 {
55     g_return_val_if_fail (fs, NULL);
56     return fs->m_action;
57 }
58 
gnc_float_split_get_reconcile_state(const FloatingSplit * fs)59 char gnc_float_split_get_reconcile_state (const FloatingSplit *fs)
60 {
61     g_return_val_if_fail (fs, '\0');
62     return fs->m_reconcile_state;
63 }
64 
gnc_float_split_get_reconcile_date(const FloatingSplit * fs)65 time64 gnc_float_split_get_reconcile_date (const FloatingSplit *fs)
66 {
67     g_return_val_if_fail (fs, G_MAXINT64);
68     return fs->m_reconcile_date;
69 }
70 
gnc_float_split_get_amount(const FloatingSplit * fs)71 gnc_numeric gnc_float_split_get_amount (const FloatingSplit *fs)
72 {
73     g_return_val_if_fail (fs, gnc_numeric_zero());
74     return fs->m_amount;
75 }
76 
gnc_float_split_get_value(const FloatingSplit * fs)77 gnc_numeric gnc_float_split_get_value (const FloatingSplit *fs)
78 {
79     g_return_val_if_fail (fs, gnc_numeric_zero());
80     return fs->m_value;
81 }
82 
83 
84 /* modifiers */
gnc_float_split_set_split(FloatingSplit * fs,Split * split)85 void gnc_float_split_set_split(FloatingSplit *fs, Split *split)
86 {
87     g_return_if_fail (fs);
88     fs->m_split = split;
89 };
90 
gnc_float_split_set_account(FloatingSplit * fs,Account * account)91 void gnc_float_split_set_account (FloatingSplit *fs, Account *account) /* direct account pointer rather than account guid */
92 {
93     g_return_if_fail (fs);
94     fs->m_account = account;
95 };
96 
gnc_float_split_set_transaction(FloatingSplit * fs,Transaction * transaction)97 void gnc_float_split_set_transaction (FloatingSplit *fs, Transaction *transaction) /* direct transaction pointer rather than transaction guid */
98 {
99     g_return_if_fail (fs);
100     fs->m_transaction = transaction;
101 };
102 
gnc_float_split_set_memo(FloatingSplit * fs,const char * memo)103 void gnc_float_split_set_memo (FloatingSplit *fs, const char *memo)
104 {
105     g_return_if_fail (fs);
106     CACHE_REPLACE (fs->m_memo, memo);
107 };
108 
gnc_float_split_set_action(FloatingSplit * fs,const char * action)109 void gnc_float_split_set_action (FloatingSplit *fs, const char *action)
110 {
111     g_return_if_fail (fs);
112     CACHE_REPLACE (fs->m_action, action);
113 };
114 
gnc_float_split_set_reconcile_state(FloatingSplit * fs,char reconcile_state)115 void gnc_float_split_set_reconcile_state (FloatingSplit *fs, char reconcile_state)
116 {
117     g_return_if_fail (fs);
118     fs->m_reconcile_state = reconcile_state;
119 };
120 
gnc_float_split_set_reconcile_date(FloatingSplit * fs,time64 reconcile_date)121 void gnc_float_split_set_reconcile_date (FloatingSplit *fs, time64 reconcile_date)
122 {
123     g_return_if_fail (fs);
124     fs->m_reconcile_date = reconcile_date;
125 };
126 
gnc_float_split_set_amount(FloatingSplit * fs,const gnc_numeric amount)127 void gnc_float_split_set_amount (FloatingSplit *fs, const gnc_numeric amount)
128 {
129     g_return_if_fail (fs);
130 
131     fs->m_amount = amount;
132 };
133 
gnc_float_split_set_value(FloatingSplit * fs,const gnc_numeric value)134 void gnc_float_split_set_value (FloatingSplit *fs, const gnc_numeric value)
135 {
136     g_return_if_fail (fs);
137 
138     fs->m_value = value;
139 };
140 
141 /* This function takes a split and returns a representation
142    of it as a floating_split structure. Assumes the transaction is open
143    for editing.
144 */
gnc_split_to_float_split(Split * split)145 FloatingSplit *gnc_split_to_float_split (Split *split)
146 {
147     FloatingSplit *fs;
148 
149     g_return_val_if_fail (split, NULL);
150 
151     fs = g_new0 (FloatingSplit, 1);
152     fs->m_split = split;
153     fs->m_account = xaccSplitGetAccount (split);
154     fs->m_transaction = xaccSplitGetParent (split);
155     fs->m_memo = CACHE_INSERT (xaccSplitGetMemo (split));
156     fs->m_action = CACHE_INSERT (xaccSplitGetAction (split));
157     fs->m_reconcile_state = xaccSplitGetReconcile (split);
158     fs->m_reconcile_date = xaccSplitGetDateReconciled (split);
159     fs->m_amount = xaccSplitGetAmount (split);
160     fs->m_value = xaccSplitGetValue (split);
161 
162     return fs;
163 }
164 
165 /* Copy a temporary split representation onto a real split.
166    If possible, insert the split into the account of the
167    split representation. Not all values are copied. The reconcile
168    status and date are not copied. The split's guid is,
169    of course, unchanged.
170 */
gnc_float_split_to_split(const FloatingSplit * fs,Split * split)171 void gnc_float_split_to_split (const FloatingSplit *fs, Split *split)
172 {
173     g_return_if_fail(split);
174 
175     if (fs->m_memo)
176         xaccSplitSetMemo (split, fs->m_memo);
177     if (fs->m_action)
178         xaccSplitSetAction (split, fs->m_action);
179     xaccSplitSetAmount (split, fs->m_amount);
180     xaccSplitSetValue (split, fs->m_value);
181     if (fs->m_account)
182     {
183         xaccAccountBeginEdit (fs->m_account);
184         xaccSplitSetAccount (split, fs->m_account);
185         xaccAccountCommitEdit (fs->m_account);
186     }
187 }
188 
gnc_float_split_free(FloatingSplit * fs)189 void gnc_float_split_free (FloatingSplit *fs)
190 {
191     g_return_if_fail (fs);
192 
193     CACHE_REMOVE (fs->m_memo);
194     CACHE_REMOVE (fs->m_action);
195     g_free (fs);
196 }
197 
198 /* accessors */
gnc_float_txn_get_txn(const FloatingTxn * ft)199 Transaction *gnc_float_txn_get_txn (const FloatingTxn *ft)
200 {
201     g_return_val_if_fail (ft, NULL);
202     return ft->m_txn;
203 }
204 
gnc_float_txn_get_currency(const FloatingTxn * ft)205 gnc_commodity *gnc_float_txn_get_currency (const FloatingTxn *ft)
206 {
207     g_return_val_if_fail (ft, NULL);
208     return ft->m_currency;
209 }
210 
gnc_float_txn_get_date_entered(const FloatingTxn * ft)211 time64 gnc_float_txn_get_date_entered (const FloatingTxn *ft)
212 {
213     g_return_val_if_fail (ft, G_MAXINT64);
214     return ft->m_date_entered;
215 }
216 
gnc_float_txn_get_date_posted(const FloatingTxn * ft)217 time64 gnc_float_txn_get_date_posted (const FloatingTxn *ft)
218 {
219     g_return_val_if_fail (ft, G_MAXINT64);
220     return ft->m_date_posted;
221 }
222 
gnc_float_txn_get_num(const FloatingTxn * ft)223 const char *gnc_float_txn_get_num (const FloatingTxn *ft)
224 {
225     g_return_val_if_fail (ft, NULL);
226     return ft->m_num;
227 }
228 
gnc_float_txn_get_description(const FloatingTxn * ft)229 const char *gnc_float_txn_get_description (const FloatingTxn *ft)
230 {
231     g_return_val_if_fail (ft, NULL);
232     return ft->m_description;
233 }
234 
gnc_float_txn_get_notes(const FloatingTxn * ft)235 const char *gnc_float_txn_get_notes (const FloatingTxn *ft)
236 {
237     g_return_val_if_fail (ft, NULL);
238     return ft->m_notes;
239 }
240 
gnc_float_txn_get_doclink(const FloatingTxn * ft)241 const char *gnc_float_txn_get_doclink (const FloatingTxn *ft)
242 {
243     g_return_val_if_fail (ft, NULL);
244     return ft->m_doclink;
245 }
246 
gnc_float_txn_get_splits(const FloatingTxn * ft)247 SplitList *gnc_float_txn_get_splits (const FloatingTxn *ft)
248 {
249     g_return_val_if_fail (ft, NULL);
250     return ft->m_splits;
251 }
252 
gnc_float_txn_get_float_split(const FloatingTxn * ft,guint index)253 FloatingSplit *gnc_float_txn_get_float_split (const FloatingTxn *ft, guint index)
254 {
255     FloatingSplit *fs = NULL;
256     guint size = 0;
257 
258     g_return_val_if_fail (ft, NULL);
259     g_return_val_if_fail (ft->m_splits, NULL);
260     g_return_val_if_fail (index < g_list_length (ft->m_splits) , NULL);
261     return g_list_nth_data (ft->m_splits, index);
262 }
263 
gnc_float_txn_get_other_float_split(const FloatingTxn * ft,FloatingSplit * fs)264 FloatingSplit *gnc_float_txn_get_other_float_split (const FloatingTxn *ft, FloatingSplit *fs)
265 {
266     guint size = 0, other = 0;
267 
268     g_return_val_if_fail (ft, NULL);
269     g_return_val_if_fail (ft->m_splits, NULL);
270     g_return_val_if_fail (g_list_length (ft->m_splits) == 2 , NULL);
271 
272     if (g_list_nth_data (ft->m_splits, 0) == fs)
273         other = 1;
274 
275     return g_list_nth_data (ft->m_splits, other);
276 }
277 
278 /* modifiers */
gnc_float_txn_set_txn(FloatingTxn * ft,Transaction * txn)279 void gnc_float_txn_set_txn (FloatingTxn *ft, Transaction *txn)
280 {
281     g_return_if_fail (ft);
282     ft->m_txn = txn;
283 };
284 
gnc_float_txn_set_currency(FloatingTxn * ft,gnc_commodity * currency)285 void gnc_float_txn_set_currency (FloatingTxn *ft, gnc_commodity *currency)
286 {
287     g_return_if_fail (ft);
288     ft->m_currency = currency;
289 };
290 
gnc_float_txn_set_date_entered(FloatingTxn * ft,time64 date_entered)291 void gnc_float_txn_set_date_entered (FloatingTxn *ft, time64 date_entered)
292 {
293     g_return_if_fail (ft);
294     ft->m_date_entered = date_entered;
295 };
296 
gnc_float_txn_set_date_posted(FloatingTxn * ft,time64 date_posted)297 void gnc_float_txn_set_date_posted (FloatingTxn *ft, time64 date_posted)
298 {
299     g_return_if_fail (ft);
300     ft->m_date_posted = date_posted;
301 };
302 
gnc_float_txn_set_num(FloatingTxn * ft,const char * num)303 void gnc_float_txn_set_num (FloatingTxn *ft, const char *num)
304 {
305     g_return_if_fail (ft);
306     CACHE_REPLACE (ft->m_num, num);
307 };
308 
gnc_float_txn_set_description(FloatingTxn * ft,const char * description)309 void gnc_float_txn_set_description (FloatingTxn *ft, const char *description)
310 {
311     g_return_if_fail (ft);
312     CACHE_REPLACE (ft->m_description, description);
313 };
314 
gnc_float_txn_set_notes(FloatingTxn * ft,const char * notes)315 void gnc_float_txn_set_notes (FloatingTxn *ft, const char *notes)
316 {
317     g_return_if_fail (ft);
318     CACHE_REPLACE (ft->m_notes, notes);
319 };
320 
gnc_float_txn_set_doclink(FloatingTxn * ft,const char * doclink)321 void gnc_float_txn_set_doclink (FloatingTxn *ft, const char *doclink)
322 {
323     g_return_if_fail (ft);
324     CACHE_REPLACE (ft->m_doclink, doclink);
325 };
326 
gnc_float_txn_set_splits(FloatingTxn * ft,SplitList * splits)327 void gnc_float_txn_set_splits (FloatingTxn *ft, SplitList *splits)
328 {
329     g_return_if_fail (ft);
330     ft->m_splits = splits;
331 };
332 
gnc_float_txn_append_float_split(FloatingTxn * ft,FloatingSplit * fs)333 void gnc_float_txn_append_float_split (FloatingTxn *ft, FloatingSplit *fs)
334 {
335     g_return_if_fail (ft);
336     g_return_if_fail (fs);
337     ft->m_splits = g_list_append (ft->m_splits, fs);
338 }
339 
340 /* This function takes a C transaction and returns
341    a representation of it as a floating_txn. */
gnc_txn_to_float_txn(Transaction * txn,gboolean use_cut_semantics)342 FloatingTxn *gnc_txn_to_float_txn (Transaction *txn, gboolean use_cut_semantics)
343 {
344     GList *iter;
345 
346     FloatingTxn *ft = g_new0 (FloatingTxn, 1);
347 
348     ft->m_txn = txn;
349     ft->m_currency = xaccTransGetCurrency (txn);
350     ft->m_date_entered = xaccTransGetDateEntered (txn);
351     if (use_cut_semantics)
352     {
353         ft->m_date_posted = xaccTransGetDate (txn);
354         ft->m_num = CACHE_INSERT (xaccTransGetNum (txn));
355     }
356     ft->m_description = CACHE_INSERT (xaccTransGetDescription (txn));
357     ft->m_notes = CACHE_INSERT (xaccTransGetNotes (txn));
358     ft->m_doclink = CACHE_INSERT (xaccTransGetDocLink (txn));
359 
360     for (iter = xaccTransGetSplitList (txn); iter ; iter = iter->next)
361     {
362         Split *split = iter->data;
363         if (split)
364         {
365             FloatingSplit *fs = gnc_split_to_float_split (split);
366             ft->m_splits = g_list_prepend (ft->m_splits, fs);
367         }
368     }
369     ft->m_splits = g_list_reverse (ft->m_splits);
370 
371     return ft;
372 }
373 
gnc_float_txn_to_txn(const FloatingTxn * ft,Transaction * txn,gboolean do_commit)374 void gnc_float_txn_to_txn (const FloatingTxn *ft, Transaction *txn, gboolean do_commit)
375 {
376     gnc_float_txn_to_txn_swap_accounts (ft, txn, NULL, NULL, do_commit);
377 }
378 
379 /* Copy a temporary representation of a transaction onto a real transaction.
380  I f they exist the two account*s (acct1 and acct2) are used to swap accounts
381  when when creating splits. */
gnc_float_txn_to_txn_swap_accounts(const FloatingTxn * ft,Transaction * txn,Account * acct1,Account * acct2,gboolean do_commit)382 void gnc_float_txn_to_txn_swap_accounts (const FloatingTxn *ft, Transaction *txn, Account *acct1, Account *acct2, gboolean do_commit)
383 {
384     GList *iter;
385 
386     g_return_if_fail (ft);
387     g_return_if_fail (txn);
388 
389     if (!xaccTransIsOpen (txn))
390         xaccTransBeginEdit (txn);
391 
392     if (ft->m_currency)
393         xaccTransSetCurrency (txn, ft->m_currency);
394     if (ft->m_description)
395         xaccTransSetDescription (txn, ft->m_description);
396     if (ft->m_num)
397         xaccTransSetNum (txn, ft->m_num);
398     if (ft->m_notes)
399         xaccTransSetNotes (txn, ft->m_notes);
400     if (ft->m_doclink)
401         xaccTransSetDocLink (txn, ft->m_doclink);
402     if (ft->m_date_posted)
403         xaccTransSetDatePostedSecs (txn, ft->m_date_posted);
404 
405     /* strip off the old splits */
406     while (xaccTransCountSplits (txn))
407         xaccSplitDestroy (xaccTransGetSplit (txn, 0));
408 
409     /* and put on the new ones! Please note they go in the *same*
410        order as in the original transaction. This is important. */
411     for (iter = ft->m_splits; iter; iter = iter->next)
412     {
413         Account *old_acc, *new_acc;
414         Split *split;
415         FloatingSplit *fs = iter->data;
416         if (!fs)
417             continue;
418 
419         split = xaccMallocSplit (xaccTransGetBook (txn));
420 
421         old_acc = fs->m_account;
422         if (fs->m_account == acct1)
423             new_acc = acct2;
424         else if  (fs->m_account == acct2)
425             new_acc = acct1;
426         else
427             new_acc = fs->m_account;
428 
429         fs->m_account = new_acc;
430         gnc_float_split_to_split (fs, split);
431         fs->m_account = old_acc;
432         xaccSplitSetParent (split, txn);
433     }
434 
435     /* close the transaction */
436     if (do_commit)
437         xaccTransCommitEdit (txn);
438 }
439 
gnc_float_txn_free(FloatingTxn * ft)440 void gnc_float_txn_free (FloatingTxn *ft)
441 {
442     g_return_if_fail (ft);
443 
444     CACHE_REMOVE (ft->m_num);
445     CACHE_REMOVE (ft->m_description);
446     CACHE_REMOVE (ft->m_notes);
447     CACHE_REMOVE (ft->m_doclink);
448     g_list_free_full (ft->m_splits, (GDestroyNotify)gnc_float_split_free);
449     g_free (ft);
450 }
451