New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Bug 798879 - RFE: [Transaction Report] add Running Total option #1617
Conversation
Overall the code is fine in itself, however, it is not desirable to add too many options in such a complicated report. What about a single "Show separate primary/secondary/total subtotals?" option somewhere in the Sorting tab? |
Yes the option dialog is getting crowded. That said keep in mind the total, subtotal options are already there. I am only adding 3 checkboxes, one each under their respective, already existing, totals or subtotal checkbox. Now I can certainly look at reorganizing or combining some of these if that's OK (I was trying to reduce UI changes). For instance the Totals / runing total on the display could be a single drop down list instead of 2 checkboxes. The drop-down list would be called Totals and have 3 options: None, Totals, Running Total. Running totals automatically enables the totals line. It could be similar for the subtotals except that then we have to deal with the "Primary/Secondary Subtotal to Date Key". So I would need to think more about how that would work. |
Ok #1618 merged in this one just needs rebasing, refactoring and removal of arity code... |
6ecc60e
to
12e90fd
Compare
Great. I have rebased and updated my code. Tested and it all seems to work fine. Also udated income-gst-statement.scm to disable runing total options in that context (that report uses all trep options by default, then removes the onces it doesn't one) I still need to work on making some tests for the running totals For your comment about the option dialog, I looked at simplifying the dialog but considering I am only adding 3 checkboxes (one under each of the 3 existing total/subtotal checkboxes) I am not sure I can simplify much. In particular the fact that the exsting subtotal options are already split between a checkbox and a drop-down list when the key is a date (to select the periods to subtotal: daily, weekly, monthly, etc) makes it difficult to combine the existing subtotal and new running subtotal options. So unless you have a more specific idea, then I don't see a way to improve this without redesigning the whole option panel which is not something I can embark on right now. |
FYI still need to rebase with your more recent changes to left-columns. Will do that later as it's bed time for me. |
I am trying to create a rather simple test from this html file (remove .log because I can't attached html here for some reason) The test simply grabs the last running total to make sure it equals the total of the test. It's -4 because there are 3 more rows for totals after that.
Yet I keep getting this error, it returns #f instead of $2,880.00
Here is the kicker however, If I try row -5, the second to last line, I have no problem whatsover
returns
I am puzzled to say the least... |
The row/col analyses the xml tree exactly as written, rather than the rendered html table. This means a cell which spans 2 column will count as 1. To increase visibility of the xml try #f in row or col. Would it work better if the option "Table for export" is enabled? |
SummaryHere are a bunch of tests I ran with various results, but you can probably skip most of it and go to the last one at the bottom where I disable all totals, subtotals and runing total and yet I still get this odd behavior: every minus EVEN row fails and every minus ODD row works. This is confirmed by my earlier tests where it just happened that the row I wanted was a minus EVEN, but when I removed one subtotal line and it moved to a minus ODD then suddently it works. Same thing happened with the columns, -2 (even) fails but -1 and -3 (odd) works. Of note is that testing from TOP or LEFT (no minuses) works on all rows. This is with table for exporting option on (makes no difference)Notice (after the fact for me) how it's a MINUS EVEN that fails (-4)
Testing from the top, it worksExact same code but coming from the top (row 36 instead of -5, etc.) it works.
Retrieving columns, second-to-last fails(Here again MINUS EVEN fails)
Retrieving columns from the left works
Without subtotalsIf I turn off the subtotals (so I have only 2 total rows at the bottom instead of 3), then it is STILL row -4 that fails (which is now is the second-to-last in the transaction list). My last row (previous row -4) is now -3 and it comes back just fine.
Final test: With no subtotals or totalsOK so forger about totals and running totals. Here is a version with all of that disabled. So just a very basic table. Yet here we see that testing the last 6 rows, every -EVEN fails while -ODD works
|
Please feel free to add your WIP test commit. I may need to get back to this 2wks from now. |
Will do and no rush. I can also most do the test without the minus row calls but will also keep investigating a bit. I don't find a lot of existing tests using a MINUS EVEN but I did find one, test-trial-balance.scm is calling "'sxml->table-row-col sxml 1 -2 #f" so I will have a look at that to see if I can find a clue there. |
Rebased to 5.1. Tests included. Moving this out of draft. As for the strange issue with the "minus even" rows and columns in the test, I am not using these in my test anymore. I will submit a separate PR to investigate this further since I can reproduce it on a much simpler example. |
Could you possibly perhaps merge into 3 separate commits to tidy this branch for merging? (1) the triad of running balances with tests (2) trep to filter |
Yes good idea. |
OK done with merge in 3 commits. |
Without delving too much into the technicalities of the old vs new running total, I wonder if the number of options can be minimised by assuming that if the Display option specifies Running Total, then the visibility of Primary/Secondary/Total running totals depends on the existence of primary/secondary sort keys. IOW if secondary sort-key is None then disable its running total. Would this help? |
@christopherlam asked me to voice my opinion as well 😉 as I'm known as the 'option guy' around here, typically raising concerns about adding options. I'll admit I have no strong opinion on this one as I'm not using these reports and haven't touched its code. If I understand correctly your feature enables running balances of three levels: globally, and at each grouping level. Your intro explains what they do, but not why they are useful in this report. So I guess my first question is which use cases do you target with each of these three running sums ? |
Your understanding of the feature is correct except possibly for the running balance vs running total part. Strictly speaking, a running balance (at least of the context of accounting) is related to an account balance, hence much more specific; this is the way the running balance works in the report. It simply takes the account balance for each transaction. Hence why it needed to be "fixed" in terms of presentation because it is only running when the transaction report is filtering, grouping and sorting exactly the same transaction as the account registers. This is what we did already in PR #1604. Hence the running balance feature is actually quite specific to a single use case: when the transaction report matches an account register (or several if using subtotals). Otherwise it's just showing the account balance at each transaction Running totals have nothing to do with account registers nor how the underlying data is sorted outside of the report. It starts with a list of numbers in whatever order that list is presented, in this case whatever filtering, grouping, ordering, sorting, the transaction report is giving us. I assume you already know the transaction report can literally filter, group, sort, and order transaction in almost any imaginable way without any connection to the underlying registers. I could filter all transactions with the word "blue in them in a specific selection of accounts, sorted by description ascending. We shall define the resulting list of transactions as (1..n). Each time there is a list of items and a total (in any context), their can be a running total. Technically. taking the list (1..n), the total is the sum of (1..n) while the running total is the sum at every point down the list; (1), sum(1..2), sum(1..3), sum(1..4), etc until sum(1..n); Hence by definition the last running total of a series is always equal to the total of the series. There isn't much more to it than that.
First there are 3 levels of running totals because there are 3 levels of totals. (edit: It's worth adding that the subtotal levels typically show more than one total each, consecutively, since they are subtotals, and the same applies to running totals). I don't think users are likely to use all 3 at once, but since we give them a choice of any of these 3 levels of totals it seemed to make sense to give them a choice of any of the same 3 levels of running totals. As to what the use case of any of these running totals is, I could say it's the same as the use case for the totals themselves, but extended. For instance; I have a list of expenses (of a certain type I selected using all of the report feature I have; it doesn't have to be expenses for a given account). We had agreed only to spend $500 for all of these things I have selected.
This is just one use case. I could come up with more but to be honest running total is just a generic concept, just as total is, I am hoping the case is made with just this. |
Indeed but again there never was an old running total. There was and still is a a running account balance (different use case in a restricted filtering and sorting context).
Worth noting that this is already the way in one direction: the running total for a given level (Primary/Secondary/Grand Total) is only available if the total for that level is enabled. So I am not against that idea going the other way as well, meaning if you turn running totals on, you turn it on for all levels where totals are enabled. It's a bit more restrictive but if that's the compromise, I am fine with it. In this case yes, instead of one additional checkbox for each level we only have one checkbox which I assume would be the one on the display panel currently only targeting the grant total. An alternative: Still only on the display and still a single option, but a drop down list with the following options:
You still lose the granularity of one subtotal level vs the other but to be honest I am not sure this is that important. I can certainly show what that one would look like (we already know what the other one with just the check box would look like since it's the same as of now on the display tab) |
Also just to add, since we are discussing option presentations. The current (not mine) option to just turn on the Grand Total on the Display tab says "Totals". Note the plural form. I don't understand why it does say that. It only turns on the "Grand Total" which by definition there is only one of for each report. That option has no impact on the totals for the other 2 levels that are enabled on the sorting display. Personally I feel it would be best to rename that option "Grand Total" which is what it is called in the report itself. Edit: OK on second thought the plural could refer to more than one column being totaled, hence totals as in several columns on the Grant Total line. Still, in this context, Grand Total (singular) still makes more sense. Yes several columns might have a grand total, but there is still ONE Grand Total line (hence the singular). Totals still just makes it sound as if it would control the subtotals as well which it doesn't. Anyway just my brain going in overdrive now so possibly just forget about this comment. |
It should be consistent. If you make it Grand Total you don't have to change the subtotal labels on the Sorting page. |
Nice. Now don't forget the options renamer mechanism somewhere in the options cpp code. |
The option renaming is the the last commit. Actually I have a question about this. Since the rename is not report specific, what would happen if 2 reports use 2 unrelated options with the same name; then one is renamed and the other is not; or worse both are renamed differently... |
Each report gets its own optiondb instance so name collisions can't happen. |
OK but isn't there only one global renaming list in gnc-optiondb.cpp? Look at my last commit 6eaa2fe ; if there was also a completely different report with a completely different options called "Totals" that I wanted to rename to "Big Totals", how and where would I differentiate that with my "Totals" to "Grand Total" renaming as it is currently coded? |
Ah, sorry I misunderstood. No, there's only one alias possible for any particular string. There are three non-trep reports that have "Totals" options: General Journal, General Ledger, and Register. The General Journal's and Register Report Totals options don't seem to do anything, the General Ledger's option produces a new line named "Grand Totals". One might argue that you should apply your That doesn't solve the more general problem; I suppose that the right way to fix that would be to make the alias list mutable and add instances that shouldn't apply to all reports from the report that needs it. I don't think we should add that complication until we really need it. |
Agree the current option renamer mechanism doesn't discriminate between different reports, and I don't particularly think this is currently an issue at this time. Hence I've typically preferred to use long report option names to be as descriptive as possible in any particular report. |
I have no further comments on this branch 👍 |
Running total are calculated based on report transactions sort order as shown. Can be displayed on the main total as well as the primary and secondary subtotal levels. Compatible with multi-currency options.
when "Show subtotals only" is selected
I squashed these last 2 commits into the main one. I think this is good to go then. |
All good :) |
I didn't get back to this PR in time. I'll just end with a 'nice work' compliment :) |
Summary
Running total are calculated based on report transactions sort order as shown. Can be displayed on the main total as well as the primary and secondary subtotal levels. Compatible with multi-currency options and multi-line (splits) display.
Context
The idea came when I was trying to use the running balance feature to create running totals and realized it wasn't doing what I wanted (although it's a usefull feature in itself). What I wanted was a running total based on the transactions in the report in whatever order and filtering I had selected.
I started to work on in gnucash version 4. My code was improved based on useful (and very patient) feedback I received on a couple of previous PR (code in the first one was pretty bad, second one better) and is now presented with additional improvements for version 5. It is a work in progress requiring more pairs of eyes both in terms of code but also presentation. Because it changes trep-engine, care must also be taken to not impact other reports and custom reports based on it, so additional testing will be done there as well. In the meantime I am opening this draft PR to get additional feedback.
The Feature
The running totals work on all 3 levels: Grand Total, Primary Subtotal and Secondary Subtotal. They can be enabled or disabled separetely. In the screenshot below all 3 are enabled at once (with a small dataset to keep the screenshot manageable).
The options to enables them are right under each of their respective options (so in the Display section for the grand total and the Sorting section for the subtotals).
The running totals are calculated as per the grouping and ordering presented in the report, not the underlying data. It is for the user to decide whether a particular grouping and ordering is relevant (such as combining transactions from several accounts in the same list ordered by date). But the running totals will follow whatever grouping and sorting option the user chooses.
Here is an example of a running total calculated by combining transactions from both wallet and checking accounts mixed together with an unusual descending date sort order. This examples is meant to highlight that the relevance of the nature and order of transaction data presented is left to the user, the running total is not judgmental in that regard.
The feature is fully compliant with the multi currency display (including showing converted and original amounts, each with their own set of running total and subtotals columns).
Caveats
Technical brief and code review
Although this introduces close to 200 lines of new code and a few lines of modified code, most additions are pretty benign boilerplate to create new options, new columns, etc. following existing code structures, and with plenty of comments to help with long-term maintainability.
There is only one major change (discussed in detail below) which calls for the introduction of a second argument in the calculated-cells calculator-functions (while preserving the support of calculator-functions with a single argument) with related code change in the add-split-row renderer.
A second smaller change was made to selecting which number columns are formatted with a link to the underlying transaction in the register is also discussed below (discussed further down).
A third small change is in removing columns that do not have subtotals enabled from calculated-cells when “Show subtotals only” is selected. This should have been done when that feature was developed, and the missing code caused empty columns to be displayed in that view (including the existing account balance column) an issue made more apparent with the new running total columns.
Changes to add-split-row and the related calculator-functions
Add-split-row is the procedure that is called to add each row of data to the report. It received a split and runs it through the calculator-function for each cell.
In the definition part of the report, the columns are divided in two groups:
One key difference between the left-columns and calculated-cells is that the function that are called to retrieve the content of the left-columns are provided with a second parameter called transaction-row? which differentiates the splits in a multiline situation (when "Display – Detail Level – One Split per Line” is selected). The flag is set to #t when the current split is the “main” split related to the account and #f when the current split is another split. This allows the left-columns to only show the date and description when the main split is shown, and leaves that information blank when not (since it is the same for the splits).
Unlike the left-columns, the calculator-functions for the calculated-cells do not have that argument. They only get the split with no way to tell if it’s a main split or “other” split. This is a problem for the running totals in a multiline environment since they should only be computed and shown on the main split, not the other splits. In fact the same problem exists on the Running Balance which for now is disabled in the multiline context.
Hence my code changes implement this additional argument for the calculator-functions (calculator-function split transaction-row?) while preserving the ability to have calculator-functions with only one argument (calculator-function split) for legacy purpose.
This results in one of the main code additions and changes in add-split-row
The code uses a guile-specific procedure called procedure-minimum-arity. It is not widely documented because not standard RnRS scheme but it was introduced in Guile version 2.0.0 (it is mentioned in the changelog for that version) and is till supported as of the current version 3.0.9. It’s pretty straightforward as explained by (help procedure-minimum-arity)
`procedure-minimum-arity' is a procedure in the (guile) module.
Scheme Procedure: procedure-minimum-arity proc
Return the "minimum arity" of a procedure.
If the procedure has only one arity, that arity is returned as a
list of three values: the number of required arguments, the number
of optional arguments, and a boolean indicating whether or not the
procedure takes rest arguments.
For a case-lambda procedure, the arity returned is the one with the
lowest minimum number of arguments, and the highest maximum number
of arguments.
If it was not possible to determine the arity of the procedure,
`#f' is returned.
See the specific commit for this procedure: https://git.savannah.gnu.org/cgit/guile.git/commit/?id=cb2ce548441824fe1284fc80a3a95394a9fc03d0
For our purpose we can ignore optional and rest argument, and are only looking at the number of required argument, assuming either 1 (for legacy purpose) or otherwise expecting 2 as the else. Hence we simply retrieve the car of the procedure-minimum-arity for the provided calculator-functions to decide to either call it with 1 or 2 arguments. This therefore requires no changes to existing calculator-functions in trep-engine or trep-engine-based report but allows for addition of new calculator-functions with the 2 arguments.
I feel the addition of the transaction-row? argument is a worthwhile addition to the calculated-cells to bring them in line with the left-columns even outside of this current feature. For instance it could also be used to fix the running balance display in the multiline context or used by custom reports for other reasons.
Alternative to changes to add-row-split and calculator-functions’ number of arguments
If this change is considered to bold, the alternative would be to disable running totals in the multiline context for now, although that brings the smaller challenge of disabling “on the fly” the subtotal options in the ui (in the sorting section) when the “One Split per Line” is selected in the display section. I don’t think there is any way to do this right now while the dialog is active, or at least there is no hook I could find when changing tabs in the dialog (I could only get it done when it is opened the next time, or apply is clicked, or when a related option is clicked in the sorting window). That said the actual option can be disabled in the actual report output regardless of option graying in the dialog. So this is purely for the visual in the dialog and could possibly be left active with additional info in the option popup message stating it doesn’t work when “One Split per Line” is selected.
Changes to which number columns are formatted as links
The current implementation offers the “Enable Links” option in the display section. When enabled, it is applied to each cell that has a number in it. One limitation of this approach is that it is even applied to number cells whose calculated number is different from the transaction amount in the register (for instance a converted amount, or a running total). Also it can clutter the report visually since the link is essentially the same for each cell in a given row, pointing to the split in the register.
Ideally there would be an additional flag in the column definition to decide whether a link should or shouldn’t be present on that column, but that would require additional changes for such a small feature with potential impact on custom reports. The alternative I propose is to limit links (when enabled) to columns already marked as displaying a subtotal so as to declutter the report. To avoid an edge case of being left with no links on the report (which could happen if no columns with subtotals are enabled), the code reverts back to putting links on any number column in case no columns with subtotals are enabled.