GnuCash c935c2f+
Loading...
Searching...
No Matches
Scrub2.cpp
1/********************************************************************\
2 * Scrub2.c -- Convert Stock Accounts to use Lots *
3 * *
4 * This program is free software; you can redistribute it and/or *
5 * modify it under the terms of the GNU General Public License as *
6 * published by the Free Software Foundation; either version 2 of *
7 * the License, or (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License*
15 * along with this program; if not, contact: *
16 * *
17 * Free Software Foundation Voice: +1-617-542-5942 *
18 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19 * Boston, MA 02110-1301, USA gnu@gnu.org *
20\********************************************************************/
21
33#include <config.h>
34
35#include <glib.h>
36
37#include "qof.h"
38#include "Account.h"
39#include "AccountP.hpp"
40#include "Account.hpp"
41#include "Transaction.h"
42#include "TransactionP.hpp"
43#include "Scrub2.h"
44#include "cap-gains.h"
45#include "gnc-engine.h"
46#include "gncInvoice.h"
47#include "gnc-lot.h"
48#include "policy-p.h"
49
50static QofLogModule log_module = GNC_MOD_LOT;
51
52/* ============================================================== */
58void
60{
61 if (!acc) return;
62
63 ENTER ("acc=%s", xaccAccountGetName(acc));
65
66restart_loop:
67 for (auto split : xaccAccountGetSplits (acc))
68 {
69 /* If already in lot, then no-op */
70 if (split->lot) continue;
71
72 /* Skip stock splits */
73 if (xaccSplitIsStockSplit(split)) continue;
74
75 /* Skip voided transactions */
76 if (gnc_numeric_zero_p (split->amount) &&
77 xaccTransGetVoidStatus(split->parent)) continue;
78
79 if (xaccSplitAssign (split)) goto restart_loop;
80 }
82 LEAVE ("acc=%s", xaccAccountGetName(acc));
83}
84
85/* ============================================================== */
86
94void
95xaccLotFill (GNCLot *lot)
96{
97 Account *acc;
98 Split *split;
99 GNCPolicy *pcy;
100
101 if (!lot) return;
102 acc = gnc_lot_get_account(lot);
103 pcy = gnc_account_get_policy(acc);
104
105 ENTER ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
106
107 /* If balance already zero, we have nothing to do. */
108 if (gnc_lot_is_closed (lot))
109 {
110 LEAVE ("Lot Closed (lot=%s, acc=%s)", gnc_lot_get_title(lot),
111 xaccAccountGetName(acc));
112 return;
113 }
114 split = pcy->PolicyGetSplit (pcy, lot);
115 if (!split)
116 {
117 LEAVE ("No Split (lot=%s, acc=%s)", gnc_lot_get_title(lot),
118 xaccAccountGetName(acc));
119 return; /* Handle the common case */
120 }
121
122 /* Reject stock split transactions */
123 if (xaccSplitIsStockSplit(split))
124 {
125 LEAVE ("Stock split transaction (lot=%s, acc=%s)",
126 gnc_lot_get_title(lot), xaccAccountGetName(acc));
127 return;
128 }
129
130 /* Reject voided transactions */
131 if (gnc_numeric_zero_p(split->amount) &&
132 xaccTransGetVoidStatus(split->parent))
133 {
134 LEAVE ("Voided transaction (lot=%s, acc=%s)",
135 gnc_lot_get_title(lot), xaccAccountGetName(acc));
136 return;
137 }
138
140
141 /* Loop until we've filled up the lot, (i.e. till the
142 * balance goes to zero) or there are no splits left. */
143 while (1)
144 {
145 Split *subsplit;
146
147 subsplit = xaccSplitAssignToLot (split, lot);
148 if (subsplit == split)
149 {
150 PERR ("Accounting Policy gave us a split that "
151 "doesn't fit into this lot\n"
152 "lot baln=%s, isclosed=%d, aplit amt=%s",
154 gnc_lot_is_closed (lot),
155 gnc_num_dbg_to_string (split->amount));
156 break;
157 }
158
159 if (gnc_lot_is_closed (lot)) break;
160
161 split = pcy->PolicyGetSplit (pcy, lot);
162 if (!split) break;
163 }
165 LEAVE ("(lot=%s, acc=%s)", gnc_lot_get_title(lot), xaccAccountGetName(acc));
166}
167
168/* ============================================================== */
169
170void
172{
173 gnc_commodity *currency = nullptr;
174 SplitList *snode;
175 GList *node;
176 gnc_numeric zero = gnc_numeric_zero();
177 gnc_numeric value = zero;
178
179 if (!lot) return;
180
181 ENTER ("lot=%s", gnc_lot_get_title(lot));
182
183 for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next)
184 {
185 Split *s = GNC_SPLIT(snode->data);
186 xaccSplitComputeCapGains (s, nullptr);
187 }
188
189 /* We double-check only closed lots */
190 if (FALSE == gnc_lot_is_closed (lot))
191 {
192 LEAVE ("lot=%s is closed", gnc_lot_get_title(lot));
193 return;
194 }
195
196 for (snode = gnc_lot_get_split_list(lot); snode; snode = snode->next)
197 {
198 Split *s = GNC_SPLIT(snode->data);
199 Transaction *trans = s->parent;
200
201 /* Check to make sure all splits in the lot have a common currency */
202 if (nullptr == currency)
203 {
204 currency = trans->common_currency;
205 }
206 if (FALSE == gnc_commodity_equiv (currency, trans->common_currency))
207 {
208 /* This lot has mixed currencies. Can't double-balance.
209 * Silently punt */
210 PWARN ("Lot with multiple currencies:\n"
211 "\ttrans=%s curr=%s", xaccTransGetDescription(trans),
212 gnc_commodity_get_fullname(trans->common_currency));
213 break;
214 }
215
216 /* Now, total up the values */
217 value = gnc_numeric_add (value, xaccSplitGetValue (s),
219 PINFO ("Split=%p value=%s Accum Lot value=%s", s,
220 gnc_num_dbg_to_string (s->value),
221 gnc_num_dbg_to_string (value));
222
223 }
224
225 if (FALSE == gnc_numeric_equal (value, zero))
226 {
227 /* Unhandled error condition. Not sure what to do here,
228 * Since the ComputeCapGains should have gotten it right.
229 * I suppose there might be small rounding errors, a penny or two,
230 * the ideal thing would to figure out why there's a rounding
231 * error, and fix that.
232 */
233 PERR ("Closed lot fails to double-balance !! lot value=%s",
234 gnc_num_dbg_to_string (value));
235 for (node = gnc_lot_get_split_list(lot); node; node = node->next)
236 {
237 Split *s = GNC_SPLIT(node->data);
238 PERR ("s=%p amt=%s val=%s", s,
239 gnc_num_dbg_to_string(s->amount),
240 gnc_num_dbg_to_string(s->value));
241 }
242 }
243
244 LEAVE ("lot=%s", gnc_lot_get_title(lot));
245}
246
247/* ================================================================= */
248
249static inline gboolean
250is_subsplit (Split *split)
251{
252
253 /* generic stop-progress conditions */
254 if (!split) return FALSE;
255 g_return_val_if_fail (split->parent, FALSE);
256
257 /* If there are no sub-splits, then there's nothing to do. */
258 return xaccSplitHasPeers (split);
259}
260
261/* ================================================================= */
262
263
264/* ================================================================= */
265
266/* Remove the guid of b from a. Note that a may not contain the guid
267 * of b, (and v.v.) in which case, it will contain other guids which
268 * establish the links. So merge them back in. */
269
270static void
271remove_guids (Split *sa, Split *sb)
272{
276}
277
278/* The merge_splits() routine causes the amount & value of sb
279 * to be merged into sa; it then destroys sb. It also performs
280 * some other misc cleanup */
281
282static void
283merge_splits (Split *sa, Split *sb)
284{
285 Account *act;
286 Transaction *txn;
287 gnc_numeric amt, val;
288
289 act = xaccSplitGetAccount (sb);
291
292 txn = sa->parent;
293 xaccTransBeginEdit (txn);
294
295 /* Remove the guid of sb from the 'gemini' of sa */
296 remove_guids (sa, sb);
297
298 /* Add amount of sb into sa, ditto for value. */
299 amt = xaccSplitGetAmount (sa);
300 amt = gnc_numeric_add_fixed (amt, xaccSplitGetAmount (sb));
301 xaccSplitSetAmount (sa, amt);
302
303 val = xaccSplitGetValue (sa);
304 val = gnc_numeric_add_fixed (val, xaccSplitGetValue (sb));
305 xaccSplitSetValue (sa, val);
306
307 /* Set reconcile to no; after this much violence,
308 * no way its reconciled. */
310
311 /* If sb has associated gains splits, trash them. */
312 if ((sb->gains_split) &&
313 (sb->gains_split->gains & GAINS_STATUS_GAINS))
314 {
315 Transaction *t = sb->gains_split->parent;
319 }
320
321 /* Finally, delete sb */
323
326}
327
328gboolean
329xaccScrubMergeSubSplits (Split *split, gboolean strict)
330{
331 gboolean rc = FALSE;
332 Transaction *txn;
333 SplitList *node;
334 GNCLot *lot;
335
336 if (strict && (FALSE == is_subsplit (split))) return FALSE;
337
338 txn = split->parent;
339
340 // Don't mess with splits from an invoice transaction
341 // Those are the responsibility of the business code
342 if (gncInvoiceGetInvoiceFromTxn (txn)) return FALSE;
343
344 lot = xaccSplitGetLot (split);
345
346 ENTER ("(Lot=%s)", gnc_lot_get_title(lot));
347restart:
348 for (node = txn->splits; node; node = node->next)
349 {
350 Split *s = GNC_SPLIT(node->data);
351 if (xaccSplitGetLot (s) != lot) continue;
352 if (s == split) continue;
353 if (qof_instance_get_destroying(s)) continue;
354
355 // Don't mess with splits from an invoice transaction
356 // Those are the responsibility of the business code
357 if (gncInvoiceGetInvoiceFromTxn (s->parent)) return FALSE;
358
359 if (strict)
360 {
361 /* OK, this split is in the same lot (and thus same account)
362 * as the indicated split. Make sure it is really a subsplit
363 * of the split we started with. It's possible to have two
364 * splits in the same lot and transaction that are not subsplits
365 * of each other, the test-period test suite does this, for
366 * example. Only worry about adjacent sub-splits. By
367 * repeatedly merging adjacent subsplits, we'll get the non-
368 * adjacent ones too. */
369 if (!xaccSplitIsPeerSplit (split, s))
370 continue;
371 }
372
373 merge_splits (split, s);
374 rc = TRUE;
375 goto restart;
376 }
377 if (rc && gnc_numeric_zero_p (split->amount))
378 {
379 time64 pdate = xaccTransGetDate (txn);
380 gchar *pdatestr = gnc_ctime (&pdate);
381 PWARN ("Result of merge has zero amt!");
382 PWARN ("Transaction details - posted date %s - description %s", pdatestr, xaccTransGetDescription(txn));
383 g_free (pdatestr);
384 }
385 LEAVE (" splits merged=%d", rc);
386 return rc;
387}
388
389gboolean
390xaccScrubMergeLotSubSplits (GNCLot *lot, gboolean strict)
391{
392 gboolean rc = FALSE;
393 SplitList *node;
394
395 if (!lot) return FALSE;
396
397 ENTER (" ");
398restart:
399 for (node = gnc_lot_get_split_list(lot); node; node = node->next)
400 {
401 Split *s = GNC_SPLIT(node->data);
402 if (!xaccScrubMergeSubSplits(s, strict)) continue;
403
404 rc = TRUE;
405 goto restart;
406 }
407 LEAVE (" splits merged=%d", rc);
408 return rc;
409}
410
411/* =========================== END OF FILE ======================= */
This is the private header for the account structure.
Account handling public routines.
Account public routines (C++ api)
Utilities to Convert Stock Accounts to use Lots.
API for Transactions and Splits (journal entries)
Utilities to Automatically Compute Capital Gains/Losses.
All type declarations for the whole Gnucash engine.
Business Invoice Interface.
const char * xaccAccountGetName(const Account *acc)
Get the account's name.
Definition Account.cpp:3289
void xaccAccountCommitEdit(Account *acc)
ThexaccAccountCommitEdit() subroutine is the second phase of a two-phase-commit wrapper for account u...
Definition Account.cpp:1516
void xaccAccountBeginEdit(Account *acc)
The xaccAccountBeginEdit() subroutine is the first phase of a two-phase-commit wrapper for account up...
Definition Account.cpp:1475
GNCPolicy * gnc_account_get_policy(Account *acc)
Get the account's lot order policy.
Definition Account.cpp:2100
Split * xaccSplitAssignToLot(Split *split, GNCLot *lot)
The xaccSplitAssignToLot() routine will fit the indicated split into the indicated lot,...
gboolean xaccSplitAssign(Split *split)
The`xaccSplitAssign() routine will take the indicated split and, if it doesn't already belong to a lo...
void xaccSplitComputeCapGains(Split *split, Account *gain_acc)
The xaccSplitComputeCapGains() routine computes the cap gains or losses for the indicated split.
gboolean gnc_commodity_equiv(const gnc_commodity *a, const gnc_commodity *b)
This routine returns TRUE if the two commodities are equivalent.
const char * gnc_commodity_get_fullname(const gnc_commodity *cm)
Retrieve the full name for the specified commodity.
char * gnc_ctime(const time64 *secs)
Return a string representation of a date from a 64-bit time value.
Definition gnc-date.cpp:256
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition gnc-date.h:87
gboolean xaccTransGetVoidStatus(const Transaction *trans)
Retrieve information on whether or not a transaction has been voided.
void xaccTransDestroy(Transaction *trans)
Destroys a transaction.
void xaccTransCommitEdit(Transaction *trans)
The xaccTransCommitEdit() method indicates that the changes to the transaction and its splits are com...
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
GList SplitList
GList of Split.
Definition gnc-engine.h:207
void xaccTransBeginEdit(Transaction *trans)
The xaccTransBeginEdit() method must be called before any changes are made to a transaction or any of...
time64 xaccTransGetDate(const Transaction *trans)
Retrieve the posted date of the transaction.
gboolean qof_instance_get_destroying(gconstpointer ptr)
Retrieve the flag that indicates whether or not this object is about to be destroyed.
GncInvoice * gncInvoiceGetInvoiceFromTxn(const Transaction *txn)
Given a transaction, find and return the Invoice.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define PERR(format, args...)
Log a serious error.
Definition qoflog.h:244
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
Account * gnc_lot_get_account(const GNCLot *lot)
Returns the account with which this lot is associated.
Definition gnc-lot.cpp:377
gnc_numeric gnc_lot_get_balance(GNCLot *lot)
Returns the lot balance.
Definition gnc-lot.cpp:487
SplitList * gnc_lot_get_split_list(const GNCLot *lot)
Returns a list of all the splits in this lot.
Definition gnc-lot.cpp:425
gboolean gnc_lot_is_closed(GNCLot *lot)
Returns closed status of the given lot.
Definition gnc-lot.cpp:367
#define GNC_DENOM_AUTO
Values that can be passed as the 'denom' argument.
gboolean gnc_numeric_zero_p(gnc_numeric a)
Returns 1 if the given gnc_numeric is 0 (zero), else returns 0.
gboolean gnc_numeric_equal(gnc_numeric a, gnc_numeric b)
Equivalence predicate: Returns TRUE (1) if a and b represent the same number.
gchar * gnc_num_dbg_to_string(gnc_numeric n)
Convert to string.
gnc_numeric gnc_numeric_add(gnc_numeric a, gnc_numeric b, gint64 denom, gint how)
Return a+b.
@ GNC_HOW_DENOM_EXACT
Use any denominator which gives an exactly correct ratio of numerator to denominator.
void xaccAccountAssignLots(Account *acc)
Loop over all splits, and make sure that every split belongs to some lot.
Definition Scrub2.cpp:59
gboolean xaccScrubMergeSubSplits(Split *split, gboolean strict)
The xaccScrubMergeSubSplits() routine will merge together all of the splits that were at one time spl...
Definition Scrub2.cpp:329
void xaccLotFill(GNCLot *lot)
The xaccLotFill() routine attempts to assign splits to the indicated lot until the lot balance goes t...
Definition Scrub2.cpp:95
gboolean xaccScrubMergeLotSubSplits(GNCLot *lot, gboolean strict)
The xaccScrubMergeLotSubSplits() routine does the same as the xaccScrubMergSubSplits,...
Definition Scrub2.cpp:390
void xaccLotScrubDoubleBalance(GNCLot *lot)
The xaccLotScrubDoubleBalance() routine examines the indicated lot.
Definition Scrub2.cpp:171
void xaccSplitSetReconcile(Split *split, char recn)
Set the reconcile flag.
gboolean xaccSplitDestroy(Split *split)
Destructor.
Definition Split.cpp:1506
void xaccSplitMergePeerSplits(Split *split, const Split *other_split)
Merge the other_split's peer splits into split's peers.
Definition Split.cpp:2138
void xaccSplitSetValue(Split *split, gnc_numeric val)
The xaccSplitSetValue() method sets the value of this split in the transaction's commodity.
void xaccSplitRemovePeerSplit(Split *split, const Split *other_split)
Remove a peer split from this split's lot-split list.
Definition Split.cpp:2121
void xaccSplitSetAmount(Split *split, gnc_numeric amt)
The xaccSplitSetAmount() method sets the amount in the account's commodity that the split should have...
GNCLot * xaccSplitGetLot(const Split *split)
Returns the pointer to the debited/credited Lot where this split belongs to, or NULL if it doesn't be...
Definition Split.cpp:1920
gnc_numeric xaccSplitGetValue(const Split *split)
Returns the value of this split in the transaction's commodity.
gboolean xaccSplitHasPeers(const Split *split)
Does this split have peers?
Definition Split.cpp:2102
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
#define NREC
not reconciled or cleared
Definition Split.h:76
gboolean xaccSplitIsPeerSplit(const Split *split, const Split *other_split)
Report if a split is a peer of this one.
Definition Split.cpp:2108
gboolean xaccSplitIsStockSplit(Split *s)
Returns true if the split is of type stock split.
Definition Split.cpp:2078
gnc_numeric xaccSplitGetAmount(const Split *split)
Returns the amount of the split in the account's commodity.
Implement Accounting Policy Private header File.
STRUCTS.