Saltar al contenido principal

Automatiza la contabilidad con bea

Automatiza tus libros de Beancount con bea: resuelve el libro de contabilidad explícitamente, analiza el sobre JSON con jq, ramifica según los códigos de salida, ejecuta sin supervisión y programa una verificación nocturna.

Un script maneja bea con cuatro decisiones: qué libro lee, --json para una salida legible por máquina, jq para el valor que necesita y el código de salida en el que se ramifica. Esta guía explica esas cuatro decisiones de principio a fin y luego las programa.

Necesitas bea en la máquina que ejecuta el trabajo y un libro al que pueda acceder. Si estás comenzando libros nuevos, sigue primero la guía de inicio rápido de CLI. Cada dato sobre flags, claves del sobre y códigos de salida se consulta en la referencia de CLI de Beancount, no se repite aquí.

Elige el libro explícitamente

Nombra el archivo. Un comando local resuelve su destino desde --file, luego $BEA_FILE y luego ./main.bean en el directorio de trabajo, y un trabajo programado rara vez se ejecuta donde crees.

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

Las opciones globales van antes del comando, como en bea --file main.bean check. Si el archivo resuelto no existe, el comando sale con 2 y menciona las tres fuentes, de modo que un error tipográfico en una entrada de cron falla de forma visible en lugar de validar los libros equivocados. El direccionamiento alojado mediante un flag --ledger aún no existe; bea nunca sube un archivo local implícitamente.

Lee el sobre JSON

Añade --json global y todos los comandos compatibles responden con el mismo sobre: bea, target, data, truncated y limit en listas acotadas. Los montos son cadenas decimales y las fechas son ISO YYYY-MM-DD, por lo que un valor es seguro de comparar sin que un flotante entre nunca en el pipeline. Las claves del sobre están tabuladas en la referencia de JSON y códigos de salida.

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}'

Esos tres comandos — report, list e import — mantienen sus formas de resultado, por lo que una ruta de jq escrita para ellos sigue siendo válida. bea --json check y bea --json query también emiten el sobre hoy, pero son los comandos que se entregan a los ejecutables nativos de Beancount, por lo que un script debe centrarse en el estado de salida de check en lugar de su forma de salida. Elige tu política de duplicados deliberadamente: --duplicates sigue siendo necesario cuando una importación requiere una decisión, como explica el tutorial de importación.

Detente en el código de salida correcto

Ramifica según el estado y lee el objeto de error antes de reintentar cualquier cosa que escriba. En modo --json, un fallo no escribe nada en stdout y exactamente un objeto en stderr, cuya error.category nombra la clase: 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

La salida 4 es la que un script nunca debe reintentar a ciegas: significa que el resultado es un conflicto o es desconocido, como una edición externa que llega a mitad de escritura o un destino de init que ya existe. Inspecciona el libro y luego reintenta desde una lectura nueva. La salida 1 cubre fallos de validación y cualquier otro error de ejecución; error.details lleva los errores individuales del libro y error.result lleva lo que una escritura parcial realmente hizo. Una salida distinta de cero nunca garantiza que nada haya cambiado.

Ejecuta sin terminal

bea deja de solicitar entradas por sí solo. --no-input está implícito siempre que stdin no sea una terminal, siempre que --json esté configurado y siempre que CI sea verdadero (1, true, yes u on). En ese modo, una confirmación faltante falla con salida 2 en lugar de esperar para siempre.

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

Las lecturas son tolerantes en una terminal y estrictas en cualquier otro lugar. Cuando el libro tiene errores de cargador, query, list y report salen con 1 bajo --json, bajo un stdout canalizado, bajo un CI verdadero o con --strict; pasa el propio flag --allow-errors del comando para aceptar la respuesta parcial en su lugar, que también establece ledger_valid: false y llena ledger_errors en el JSON. --strict es la imagen espejo: rechaza respuestas parciales incluso en una terminal, que es lo que quieres cuando un humano ejecuta el mismo script manualmente. bea check no tiene --allow-errors — informar errores es su única función — y siempre sale con 1 cuando encuentra alguno. Establece BEA_NO_UPDATE_NOTIFIER=1 para silenciar el aviso pasivo de actualización; un CI verdadero ya lo hace.

Programa una verificación

Ejecuta una validación cada noche y deja que el código de salida sea la alerta. Ambos bloques a continuación son plantillas: las rutas, el horario y el ejecutor son tuyos.

# 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

Fija la versión cuando el trabajo deba ser reproducible y elimina la fijación cuando prefieras seguir los lanzamientos. CI ya es verdadero en GitHub Actions, por lo que las solicitudes están desactivadas y el aviso de actualización es silencioso antes de que establezcas nada. Aquí no hay paso de formato a propósito. Un trabajo programado no debería reescribir archivos que no tenía que tocar, así que recurre a bea format --check en un hook de pre-commit, que no toca nada y sale con 1 cuando un archivo necesita formato.

Usa una credencial alojada en un trabajo

Configura BEA_TOKEN, desde el almacén de secretos de tu proveedor de CI, y omite por completo el inicio de sesión en el navegador. El token se lee del entorno y nunca se escribe en el disco, por lo que nada termina en el directorio de inicio del runner para que el siguiente trabajo lo encuentre.

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

La salida 0 significa que la credencial se resolvió y el sobre nombra la cuenta a la que pertenece; la salida 3 con error.category de auth significa que no lo hizo, y el mensaje distingue una credencial no configurada de una rechazada. bea cloud logout no hace nada con un token suministrado de esta manera — no lo revoca ni lo desconfigura, ya que otro trabajo puede compartirlo — así que revoca un token filtrado desde el panel en su lugar. Los comandos locales no necesitan ninguna credencial; solo bea cloud y bea ask llegan al servicio alojado. La lista completa de variables está en la referencia de configuración.

No todo responde en JSON. bea ask rechaza el modo JSON por completo, bea cloud login necesita un humano, y un bea cloud logout o bea cloud ledger clone exitoso no devuelve un objeto JSON de éxito — lee su estado de salida en su lugar. La ayuda, la versión y la salida de completado de shell permanecen textuales.

Próximos pasos

Fuente: https://beancount.io/es/docs/Solutions/automate-bookkeeping-with-bea