Перейти к основному содержимому

Автоматизация бухгалтерии с помощью bea

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

Скрипт управляет bea с помощью четырех решений: какой реестр он читает, --json для машиночитаемого вывода, jq для нужного значения и код выхода, по которому он ветвится. Это руководство последовательно рассматривает эти четыре решения, а затем планирует их.

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

Выберите реестр явно​

Укажите имя файла. Локальная команда определяет свою цель сначала из --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 по-прежнему обязателен, когда импорту требуется решение, как объясняется в walkthrough по импорту.

Останавливайтесь на правильном коде выхода​

Ветвитесь по статусу и прочитайте объект ошибки перед повторной попыткой любого действия, которое записывает данные. В режиме --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/ru/docs/Solutions/automate-bookkeeping-with-bea