I do this using Simpletask plus a bit of Lua in it.  If I input any transaction, the add task filter will look for numbers and the magic words chf, usd or eur, and add an +accounting tag to the post.  The Lua code in the Simpletask settings goes so:

function onAdd(t,f,e)
  currs = {'chf', 'usd', 'eur'}
  for currC = 1, #currs do
    if t:lower():match(currs[currC]) then
      return t.. " +accounting"
    end
  end
  return t
end

Entering transactions this way is /incredible/.  Tap on checkmark icon, write "beer 8 chf", push save.  It's done, and when I get home, it's imported!

That todo.txt file syncs with my desktop via Syncthing.  You can also use Dropbox if you want.  No biggie.  It's transport-agnostic.

Once I'm home I fire up my beancount todo importer, which is a very dumb importer I will attach to this e-mail.  The program produces beancount transactions (to be returned to the importer) then annuls the ingested tasks in the todo.txt on disk.  The updated todo.txt file gets synced back to my phone, and bingo, I no longer carry around my cash expenses!

Naturally as you'll see the cashtodo.py program attached uses the smart_importer module which gets the contra account right 99% of the time.  That's the other part — rarely do I have to change the final accounts that enter my cash beancount file.

I also manually enter the tag +accounting for when I make an expense that isn't cash (I am careful not to type usd, chf or eur into the task) and I want to remember what that expense was without a receipt.  That way, I can remember when and how much I spent in so-and-so, and conciliate my credit card using the normal process and importer I already have, safe in the knowledge that I won't be asking myself "so what the hell were these 82.99 dollars the other day?  what's this company again?"

Useful.


On 21/04/2019 22.34, Alen Šiljak wrote:


On Sunday, 21 April 2019 22:04:53 UTC+2, Martin Blais wrote:


    What I'd find useful for myself is not so much having Beancount
    itself on the device, but a simple app that allows me to enter
    transactions easily and quickly on-the-go and to make it possible
    to extract and convert its database to partially booked Beancount
    syntax like an importer.


Great! Well, that's exactly what I'm doing (and others who ask this sort of questions on the forums are interested in).Do check-out MMEx for Android (http://android.moneymanagerex.org/) and see if you like it. It is extremely convenient for quickly entering transactions through a Single Account widget or a New Transaction shortcut that can be placed on the home screen. It uses Money Manager Ex database, which is based on categories (like Quicken) and supports only two levels of them. So far this has not been a problem for me, as I had the same structure of Expense accounts in GnuCash and I could map them in the import configuration. So, this would allow you to enter transactions right after they happen. The app uses content providers so you can use any (cloud?) storage provider as a safety file backup mechanism or for synchronization. So that part is more-or-less taken care of. The data transfer happens from a Search screen, where transactions can be queried and filtered, then shared through the Share button in the toolbar. Currently there is only QIF export because that's what I've been using for Quicken, then GnuCash. It should not take too much effort to either add a new CSV formatter or reactivate the existing one. I guess the CSV would fit all of the plain-text-accounting (PTA) tools and that part can be done on the desktop once the file is shared.

Since I'm not so keen on Java Android development lately, I wrote a Python library - https://gitlab.com/alensiljak/moneymanagerexlib - which reads the MMEx SQLite database directly (through SQLAlchemy) and, oh wait!, exports to CSV. I already forgot that I completed this but the process never worked due to bugs in GnuCash's CSV importer when multiple currencies are used.
Anyways, well, the whole chain can obviously be tested already, then!
This is exposed as a library, check it out, and it can then write any data you like but, as you already listed, there are typical fields available: status, date, account (from/to in case of transfer), category, payee, notes, transaction number, transaction type (+, -, txfer). This is just off the transaction screen so, if any fields are missing, they can be added.

I guess this could be enough for now, and it's already available on F-Droid.
--
You received this message because you are subscribed to the Google Groups "Beancount" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected] <mailto:[email protected]>. To post to this group, send email to [email protected] <mailto:[email protected]>. To view this discussion on the web visit https://groups.google.com/d/msgid/beancount/9485f16b-bfb0-4039-9972-282195be8a3a%40googlegroups.com <https://groups.google.com/d/msgid/beancount/9485f16b-bfb0-4039-9972-282195be8a3a%40googlegroups.com?utm_medium=email&utm_source=footer>. For more options, visit https://groups.google.com/d/optout <https://groups.google.com/d/optout>.


--
    Rudd-O
    https://rudd-o.com/

--
You received this message because you are subscribed to the Google Groups 
"Beancount" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/beancount/287f6e05-e5ab-aed5-648f-369b16283590%40rudd-o.com.
#!/usr/bin/python3

import copy
import csv
import datetime
import os
import re

from dateutil.parser import parse
from beancount.core import data
from beancount.core import amount
from beancount.core import account
from beancount.ingest import importer
from beancount.core.number import Decimal, round_to


import sys, os


class Importer(importer.ImporterProtocol):

    ACCT = None

    def __init__(self, account):
        self.ACCT = account

    def name(self):
        return "Cash (todo.txt)"

    def identify(self, file):
        return os.path.basename(file.name).endswith("todo.txt")

    def file_account(self, file):
        if "bean-extract" == os.path.basename(sys.argv[0]):
            return ''
        return self.ACCT

    def extract(self, file, existing_entries):
        entries = []
        text = open(file.name, "rb").read().decode("utf-8")
        lines = text.splitlines()
        processed = []
        for index, t in enumerate(lines):
            processed.append(t)
            if "+accounting" not in t:
                continue
            if t.startswith("x"):
                continue
            if "CHF" in t or "chf" in t:
                cur = "CHF"
                regex = "([0-9]+)(|[.][0-9]+)( +|)(chf|CHF|usd|USD|eur|EUR)"
            elif "USD" in t or "usd" in t:
                cur = "USD"
                regex = "([0-9]+)(|[.][0-9]+)( +|)(chf|CHF|usd|USD|eur|EUR)"
            elif "EUR" in t or "eur" in t:
                cur = "EUR"
                regex = "([0-9]+)(|[.][0-9]+)( +|)(chf|CHF|usd|USD|eur|EUR)"
            else:
                regex = "([0-9]+)(|[.][0-9]+)()()"
                cur = "CHF"
            acct = self.ACCT + ":" + cur
            tdate, trest = t.split(" ", 1)
            try:
                tdate = parse(tdate, yearfirst=True, dayfirst=False).date()
            except ValueError:
                tdate = datetime.date.today()
            trest = trest.replace("+accounting", "").strip()
            matches = re.findall(regex, trest)
            if len(matches) == 0:
                print("Skipping processing of %s." % t, file=sys.stderr)
                continue
            for match in matches:
                num1 = match[0]
                num2 = match[1]
                mcur = match[3]
                assert mcur.lower() == cur.lower() or mcur == "", (cur, mcur, t)
                narration = trest.replace("".join(match), "").strip()
                if not narration:
                    assert 0, t
                amt = amount.Amount(-Decimal(num1 + num2), cur)
                meta = data.new_metadata(file.name, index)
                postings = [
                    data.Posting(acct, amt, None, None, None, None),
                ]
                txn = data.Transaction(
                    meta, tdate, self.FLAG, None, narration, data.EMPTY_SET, set(),
                    postings
                )
            processed.pop()
            processed.append("x " + t + " +imported")
            entries.append(txn)
        with open(file.name + ".new", "w") as f:
            f.write("\n".join(processed))
        os.rename(file.name + ".new", file.name)
        return entries

Attachment: OpenPGP_signature
Description: OpenPGP digital signature

Reply via email to