Перейти к основному содержимому
Beancount.io Logo

Автоматизация бухгалтерского учёта с помощью 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 по-прежнему требуется, когда импорту нужна проверка, как объясняется в прохождении импорта.

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

Ветвитесь по статусу и читайте объект ошибки перед любой повторной попыткой записи. В режиме --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, при piped 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.1.0
      - run: bea --file main.bean check
      - run: bea --json --file main.bean report balance-sheet > balance-sheet.json

Зафиксируйте версию, когда задание должно быть воспроизводимым, и снимите фиксацию, когда вы предпочитаете отслеживать релизы. CI уже истинно на GitHub Actions, поэтому подсказки отключены, а уведомление об обновлении незаметно до того, как вы что-либо настроите. Здесь намеренно нет шага форматирования. Планируемое задание не должно переписывать файлы, которые ему не нужно, поэтому используйте bea format --check в pre-commit hook, который ничего не трогает и завершается с кодом 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