Перейти до основного вмісту

Автоматизуйте бухгалтерію за допомогою bea

Скриптуйте свої книги Beancount за допомогою bea: розв'язуйте реєстр явно, аналізуйте JSON-обгортку за допомогою jq, розгалужуйтесь за кодами виходу, працюйте без нагляду та плануйте щоденну перевірку.

Скрипт керує bea за чотирма рішеннями: який журнал він читає, --json для машинно-зрозумілого виводу, jq для потрібного значення та код виходу, за яким відбувається гілкування. Цей посібник проходить ці чотири рішення від початку до кінця, а потім розкладає їх за розкладом.

Вам потрібен bea на машині, що виконує завдання, і журнал, до якого він має доступ. Якщо ви починаєте нові книги, спочатку дотримуйтесь швидкого старту CLI. Кожен факт про прапори, ключі конвертів і коди виходу шукається у довіднику Beancount CLI, тут не повторюється.

Виберіть журнал явно​

Назвіть файл. Локальна команда розв’язує свій цільовий шлях через --file, потім $BEA_FILE, потім ./main.bean у робочій директорії, а заплановане завдання рідко виконується там, де ви думаєте.

bea --file ~/books/main.bean check
BEA_FILE=~/books/main.bean bea check
cd ~/books && bea check

Глобальні опції йдуть перед командою, як у bea --file main.bean check. Якщо розв’язаний файл не існує, команда завершується з 2 і перераховує всі три джерела, тож помилка в записі cron викликає гучний збій замість перевірки неправильних книг. Веб-хостинговий таргетинг через прапор --ledger ще не існує; bea ніколи не завантажує локальний файл неявно.

Читайте JSON-конверт​

Додайте глобальний --json, і кожна підтримувана команда відповідає тим самим конвертом: bea, target, data, truncated і limit у межових списках. Суми — це рядки з десятковими числами, а дати — ISO YYYY-MM-DD, тож значення безпечне для порівняння без жодних чисел із плаваючою комою у конвеєрі. Ключі конверта наведені в довіднику JSON і кодів виходу.

bea --json --file main.bean report income-statement | jq .data.net_profit
bea --json --file main.bean list transaction --limit 2 | jq '.data[0].postings[0].units'
bea --json --file main.bean import statement.csv --csv date=Date,amount=Amount,payee=Payee \
  --account Assets:Checking --apply --duplicates skip | jq '.data | {written, ready, duplicates}'

Ці три команди — report, list і import — зберігають форми своїх результатів, тож шлях jq, написаний під них, залишається дійсним. bea --json check і bea --json query також сьогодні видають конверт, але вони — команди, що передаються рідним виконуваним файлам Beancount, тому скрипт має орієнтуватися на код виходу check, а не на форму виводу. Вибирайте політику дублювання свідомо: --duplicates все ще потрібен, коли імпорт потребує рішення, як пояснює інструкція з імпорту.

Зупиняйтеся на правильному коді виходу​

Розгалужуйтеся за статусом і читайте об’єкт помилки перед повторною спробою чогось, що записує. У режимі --json збій не пише нічого в stdout і саме один об’єкт у stderr, чиї error.category імена класу: validation (1), usage (2), auth (3), conflict (4).

#!/usr/bin/env bash
set -euo pipefail
 
out=$(mktemp)
err=$(mktemp)
status=0
 
bea --json --file main.bean report income-statement >"$out" 2>"$err" || status=$?
 
case "$status" in
  0) jq -r '.data.net_profit | to_entries[] | "net profit: \(.value) \(.key)"' "$out" ;;
  4) echo "conflict — inspect the ledger before retrying" >&2
     jq -r '.error.message' "$err" >&2
     exit 4 ;;
  *) jq -r '.error | "\(.category) (exit \(.exit_code)): \(.message)"' "$err" >&2
     exit "$status" ;;
esac

Вихід 4 — це той, який скрипт ніколи не повинен повторювати бездумно: він означає, що результат конфліктний або невідомий, наприклад, зовнішнє редагування під час запису або ціль init, яка вже існує. Перевірте книгу обліку, потім спробуйте знову почати з нового зчитування. Вихід 1 охоплює помилки валідації та будь-які інші помилки під час виконання; error.details містить окремі помилки книги обліку, а error.result — те, що фактично зробив частковий запис. Ненульовий вихід ніколи не гарантує, що нічого не змінилося.

Запуск без термінала​

bea припиняє запити самостійно. --no-input є передбаченим, коли stdin не є терміналом, коли встановлено --json, і коли CI істинний — 1, true, yes або on. У цьому режимі відсутність підтвердження призводить до невдачі з виходом 2 замість нескінченного очікування.

CI=true BEA_NO_UPDATE_NOTIFIER=1 bea --json --file main.bean report balance-sheet
bea --json --file main.bean list transaction --limit 100 --sort oldest

Зчитування є ліберальним у терміналі і суворим у всіх інших випадках. Коли у книзі обліку є помилки завантаження, query, list і report виходять із кодом 1 під --json, під конвеєром stdout, під істинним CI або з --strict; передайте власний --allow-errors команди, щоб прийняти часткову відповідь, що також встановлює ledger_valid: false і заповнює ledger_errors у JSON. --strict — це дзеркальна ситуація: він відмовляється від часткових відповідей навіть у терміналі, що саме й потрібно, коли скрипт виконується вручну людиною. bea check не має --allow-errors — звітування про помилки є його основним завданням — і завжди виходить із 1 при виявленні помилок. Встановіть BEA_NO_UPDATE_NOTIFIER=1, щоб вимкнути пасивне сповіщення про оновлення; істинний CI уже це робить.

Запланувати перевірку​

Запускайте валідацію щоночі і нехай код виходу буде сигналом тривоги. Обидва блоки нижче є шаблонами — шляхи, розклад і виконавець — ваші.

# crontab -e — 07:15 daily; cron mails you only when bea exits nonzero
15 7 * * * BEA_NO_UPDATE_NOTIFIER=1 /opt/homebrew/bin/bea --file /home/alice/books/main.bean check
name: ledger
on:
  schedule:
    - cron: "15 7 * * *"
  push:
jobs:
  check:
    runs-on: ubuntu-latest
    env:
      BEA_NO_UPDATE_NOTIFIER: "1"
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v5
      - run: uv tool install beancount-io==0.2.0
      - run: bea --file main.bean check
      - run: bea --json --file main.bean report balance-sheet > balance-sheet.json

Перша команда на основі механізму завантажує керований механізм, тому виконавець потребує доступу до мережі. Щоб повторно використовувати його в різних завданнях, кешуйте ~/.local/share/bea/engine з ключем, що містить ОС виконавця, архітектуру, версію Python та зафіксовану версію bea. Фіксуйте версію, коли завдання повинно бути відтвореним, і знімайте фіксацію, коли бажаєте слідкувати за релізами. CI вже істинний у GitHub Actions, тому підказки вимкнені, а сповіщення про оновлення — мовчазне, поки ви нічого не встановите. Тут спеціально немає кроку форматування. Заплановане завдання не повинно переписувати файли без потреби, тому використовуйте bea format main.bean --check у хуку pre-commit, який нічого не змінює і виходить з 1, якщо файл потребує форматування.

Використання розміщеної облікової інформації в завданні​

Встановіть BEA_TOKEN, взятий зі сховища секретів вашого CI провайдера, і повністю обійдіть вхід через браузер. Токен зчитується з оточення і ніколи не записується на диск, тому нічого не потрапляє до домашнього каталогу виконавця, щоб наступна задача могла це знайти.

export BEA_TOKEN="$YOUR_CI_SECRET"
bea --json cloud status

Вихід 0 означає, що облікові дані розпізнані, а конверт вказує акаунт, до якого вони належать; вихід 3 з error.category від auth означає, що ні, а повідомлення розрізняє не задані облікові дані та відхилені. bea cloud logout нічого не робить з токеном, переданим таким чином — він його не відкликає і не скасовує, оскільки інша задача може ним користуватися — тому відкличте витіклий токен через панель керування. Локальні команди зовсім не потребують облікових даних; лише bea cloud і bea ask звертаються до розміщеного сервісу. Повний список змінних наведено у довідці налаштувань.

Не все відповідає у форматі JSON. bea ask категорично відхиляє режим JSON, bea cloud login потребує людини, а успішний bea cloud logout чи bea cloud ledger clone не повертає жодного об’єкта успіху в JSON — натомість читайте їхній статус виходу. Вивід допомоги, версії та автозаповнення оболонки залишається текстовим.

Наступні кроки​

Джерело: https://beancount.io/uk/docs/Solutions/automate-bookkeeping-with-bea