actualbudget / actual

A local-first personal finance app
https://actualbudget.org
MIT License
13.74k stars 1.1k forks source link

[Feature] Retain YNAB flags as note on import #1712

Closed kescobo closed 1 year ago

kescobo commented 1 year ago

Verified feature request does not already exist?

💻

Pitch: what problem are you trying to solve?

One of the last things blocking me (really, my wife, who does most of the budgeting work) from switching from YNAB4 to actual is our extensive use of flags. I understand from #552 that flags themselves are not going to be implemented, which is ok - we're down to change our workflow - but we have an ~ 8 year budget history, and can't really do without the information contained in the flags.

Describe your ideal solution to this problem

My proposal is to append or prepend something to the notes field on import - something like #🟥 | existing note for a red flagged transaction for example.

Looking at a current YNAB4 entry with a flag as an example:

        {
            "frequency": "Monthly",
            "payeeId": "959F5FA8-EE34-083D-0717-A77B323D85E4",
            "accountId": "9ECEDE91-6A50-9D45-6239-68F3553C32D3",
            "subTransactions": [],
            "date": "2019-02-01",
            "isTombstone": true,
            "entityVersion": "A-40234",
            "entityId": "843B65FD-0840-D23F-81BB-C6D86749A684",
            "amount": 249.25,
            "twiceAMonthStartDay": 0,
            "accepted": true,
            "memo": "wave - not paid",
            "cleared": "Uncleared",
            "entityType": "scheduledTransaction",
            "categoryId": "Category/__Split__",
            "flag": "Green"
        },

The "flag" field has a number of different possibilities, which all seem to be titlecase colors ("Green", "Red", "Blue", etc). I propose, if the "memo" field exists, prepend "#{COLOR_EMOJI} | " to it, otherwise, add a memo filed that is "#{COLOR_EMOJI}", where COLOR_EMOJI is

I am happy to take a stab at this, though I mostly only know julia and python. I think that the logic is pretty straightforward, and the only things I'd need to know to get started are

  1. Where is the import machinery that deals with importing YNAB4 (I don't know if the JSON structure of nYNAB is similar, but I'm happy to attempt to include that as well).
  2. How to I concatenate strings?
  3. How do I interact with dictionaries (or whatever datastructure is used with the transaction entries)?

Teaching and learning

It seems to me like this should be automatic during import, because if people use flags, presumably they would want to keep them, and if they don't, this has no effect. Perhaps a note added to the docs about import to show people how to search for entries with particular flags would be helpful?

github-actions[bot] commented 1 year ago

:sparkles: Thanks for sharing your idea! :sparkles:

This repository uses lodash style issue management for enhancements. That means enhancement issues are automatically closed. This doesn’t mean we don’t accept feature requests, though! We will consider implementing ones that receive many upvotes, and we welcome contributions for any feature requests marked as needing votes (just post a comment first so we can help you make a successful contribution).

The enhancement backlog can be found here: https://github.com/actualbudget/actual/issues?q=label%3A%22needs+votes%22+sort%3Areactions-%2B1-desc+

Don’t forget to upvote the top comment with 👍!

kescobo commented 1 year ago

In case someone else has need of this, I just whipped up a quick julia script that will edit your YNAB(4) budget file in this way:

#!/usr/bin/env -S julia 

using Pkg
Pkg.activate(; temp=true)
Pkg.add("JSON3")

using JSON3

flagdict = Dict(
    "Red" => "🟥",
    "Orange" => "🟧",
    "Yellow" => "🟨",
    "Green" => "🟩",
    "Blue" => "🟦",
    "Purple" => "🟪"
)

budgetpath = ARGS[1]

for (root, dirs, files) in walkdir(budgetpath)
    for file in filter(f-> f == "Budget.yfull", files)
        path = joinpath(root, file)
        fh = JSON3.read(path, Dict)
        for t in fh["transactions"]
            flag = get(t, "flag", nothing)
            isnothing(flag) && continue
            t["memo"] = haskey(t, "memo") ? string("#", flagdict[flag], " | ", t["memo"]) : string("#", flagdict[flag])
        end
        JSON3.write(path, fh)
    end
end

It should work with julia this_script.jl /path/to/budget.ynab4