Skip to content

Auto posting breaks forecasted transactions #384

@ondrejsuk1

Description

@ondrejsuk1

First of all, I'd like to thank you for this awesome app ! Paisa have th best design that I've seen from web tools for ledgers - I really felt in love with it. Sheets is very very well realtime calculator concept - I just added some functions for rounding, etc. Now it can calculate income taxes, mortgage, and many more. One big thaks for you work again !

When testing for my personal and bussiness finances, I encountered problem, when working with auto posting.

Describe your Environment
OS: Debian
Paisa Version: 0.7.4
App Variant: Oficial docker image
Backend ledger variant: hledger

Describe the bug
When auto posting transactions added, they broke internal hiding of forecasted transactions in future. Forecasted transactions for about 2 years in future shows are normal transactions in Paisa. More info in attached example journals. I'm using auto postings for envelope budgeting, which works very well in hledger itself.

To Reproduce
Steps to reproduce the behavior:

  1. Create simple journal in clean installation:
~ every month from 2025-02-05  "Transaction test1"
    ; Testing periodic transaction
    Assets:Banks:Bank1
    Expenses:Groceries                              15 $

= /Salary/
    [Assets:Budgets:Bank1:Personal]                200 $
    [Assets:Banks:Bank1]                          -200 $

= /Transaction test1/
    [Assets:Budgets:Bank1:Osobní]                   *1
    [Assets:Banks:Bank1]                           *-1

2025/12/15 * "Salary"
    ; salary:
    Income:Salary:Customer1                      -2000 $
    Assets:Banks:Bank1                            2000 $

When auto posting transaction is commented (starts with =), everything works as usual and only one transaction is shown.

  1. Open main page, or transactions page.

Expected behavior
Only one transaction should appear, not all of forecasted for about 2 years ?

Journal
I tried to investigate this problem more in depth. When I export one of problematic transaction in JSON to see metadata, it show this (when auto posting trx enabled):

  {
    "tcode": "",
    "tcomment": "",
    "tdate": "2027-12-05",
    "tdate2": null,
    "tdescription": "\"Transaction test1\"",
    "tindex": 38,
    "tpostings": [
      ...<REDACTED>...
    ],
    "tprecedingcomment": "",
    "tsourcepos": [
      {
        "sourceColumn": 1,
        "sourceLine": 1,
        "sourceName": "/root/Documents/paisa/periodic_transactions.ledger"
      },
      {
        "sourceColumn": 1,
        "sourceLine": 4,
        "sourceName": "/root/Documents/paisa/periodic_transactions.ledger"
      }
    ],
    "tstatus": "Unmarked",
    "ttags": [
      [
        "_modified",
        ""
      ],
      [
        "_generated-transaction",
        "~ every month from 2025-02-05"
      ]
    ]
  },

Interesting part is ttags. Let's compare tags between transactions where auto posting is enabled and disabled. I assuming that Paisa need something to recognize, that transaction is forecasted (generated), so something is broken about this mechanism:

Auto posting off:

"ttags": [
      [
        "_generated-transaction",
        "~ every month from 2025-02-05"
      ]
    ]

Auto posting on:

    "ttags": [
      [
        "_modified",
        ""
      ],
      [
        "_generated-transaction",
        "~ every month from 2025-02-05"
      ]
    ]

OK, auto posting apparently added new tag _modified for matched transactions. I don't know in which version of hledger is added this behavior. Never mind. Now we try to find, where these tags are used in code and where is mechanism to hide generated transactions without auto posting enabled. Because Paisa uses GO backend for hledger, before loads transactions in SQLite, I searched in GO source. Found function buildHLedgerPostings in file internal/ledger/ledger.go:

    803 func buildHLedgerPostings(p HLedgerPosting, t HLedgerTransaction, pricesTree map[string]*btree.BT    803 ree, date time.Time) ([]*posting.Posting, error) {
    804         forecast := false
    805         postings := []*posting.Posting{}
    806 
    807         var tagRecurring, tagPeriod string
    808         for _, tag := range t.Tags {
    809                 if len(tag) == 2 {
    810                         if tag[0] == "Recurring" {
    811                                 tagRecurring = tag[1]
    812                         }
    813 
    814                         if tag[0] == "Period" {
    815                                 tagPeriod = tag[1]
    816                         }
    817 
    818                         if tag[0] == "_generated-transaction" {
    819                                 forecast = true
    820                         }
    821                 }
    822                 break
    823         }
    824 
    825         for _, tag := range p.Tags {
    826                 if len(tag) == 2 && tag[0] == "Recurring" {
    827                         tagRecurring = tag[1]
    828                 }
    829 
    830                 if len(tag) == 2 && tag[0] == "Period" {
    831                         tagPeriod = tag[1]
    832                 }
    833                 break
    834         }

Construction at line 808 - for cycle, iterates only once. If there are more tags than one, processed is only the first tag, so generated transaction, modified with auto posting is not matched and not marked as forecast with forecast = true, which can be seen in SQLite database in column forecast:

sqlite> select transaction_id,date,payee,forecast from postings where transaction_id = 38;
transaction_id|date|payee|forecast
38|2027-12-05 00:00:00+02:00|"Transaction test1"|0
38|2027-12-05 00:00:00+02:00|"Transaction test1"|0
38|2027-12-05 00:00:00+02:00|"Transaction test1"|0
38|2027-12-05 00:00:00+02:00|"Transaction test1"|0

So i tried to remove break from line 822 and 833.

And it's working ! I don't know if I broke some other behavior, but it works. Can you please look at it and test if this change is OK and not broke anything ?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions