Work with Transactions¶
Use this guide to bring your bank movements into aeat so they can feed your tax calculations. Import your bank statement, add any missing transactions by hand, review and correct them, then hand them to classification before running a calculation.
Your bank records are not added automatically. aeat imports only when you run an import command. Tax calculations use the transactions you have saved under the active profile.
Before you start¶
You need:
a working
aeatcommandan active taxpayer profile; see Set up your taxpayer profile
a master-key passphrase. The tool prompts for it the first time it opens your encrypted storage in a session; for a non-interactive shell, set
AEAT_SECRET_PASSPHRASEa bank statement file or directory, unless you are adding transactions by hand
for AEAT census-derived home-office ratios, reviewed censo facts; see Link Modelo 036 census information
Confirm the active profile before you write transaction data:
aeat config profile status
Statement file format¶
A bank CSV uses a semicolon (;) separator and comma decimals. The first
line is the column header; each later line is one movement:
Fecha operación;Fecha valor;Concepto;Importe;Saldo;Moneda
2026-02-10;2026-02-10;Venta cliente;1.210,00;1.210,00;EUR
2026-02-11;2026-02-11;Compra material;-605,00;605,00;EUR
The sign of Importe carries the direction: a positive amount is income, a
negative amount is an expense. Save this as statement.csv and import it with
--provider auto.
Preview an import¶
Run a dry run first. A dry run shows what aeat would import and saves no
rows:
aeat app ledger import ./statement.csv --provider auto --dry-run
--provider auto asks aeat to detect the statement format. The recognized
providers are auto, csv, ofx, qfx, xlsx, excel, n26, pdf, and
pdf-n26. If detection picks the wrong format, replace auto with the exact
provider - run aeat app ledger import --help or see the
CLI reference for the current provider list.
If the path does not exist, the command refuses cleanly and names the missing
file (El archivo de origen no existe: ...); fix the path and run it again.
Save imported rows¶
When the dry run looks right, repeat the command without --dry-run:
aeat app ledger import ./statement.csv --provider auto
Add --verify when you want import diagnostics:
aeat app ledger import ./statement.csv --provider auto --verify
If the diagnostic source should point at a different original file, pass it
with --file:
aeat app ledger import ./processed.csv --provider csv --verify --file ./statement.csv
Use --period only when you intentionally want to label the import with a
fiscal period. Leave it out — aeat assigns the period from each transaction’s
date automatically.
Add one transaction manually¶
Use ledger add when a transaction is missing from imported statements:
aeat app ledger add --date 2026-03-15 --amount 49.99 --direction OUTGOING --description "Software subscription"
Required fields are date, amount, direction, and description. Write the amount
as a positive figure. The direction carries whether money came in or went out,
and the command refuses a negative amount. OUTGOING is for expenses, money you
paid out. INCOMING is for income, money you received.
For a received payment or issued invoice, use INCOMING:
aeat app ledger add --date 2026-03-20 --amount 121.00 --direction INCOMING --description "Client payment"
For an expense or supplier invoice, use OUTGOING:
aeat app ledger add --date 2026-03-21 --amount 60.50 --direction OUTGOING --description "Office supplies"
The third direction, INTERNAL_TRANSFER, records money moved between your own
accounts.
Record tax details on a manual transaction¶
ledger add accepts the same tax fields you set during classification, so you
record a complete transaction in one step:
aeat app ledger add --date 2026-03-21 --amount 121.00 --direction OUTGOING \
--description "Office supplies" --counterparty "Papeleria SL" \
--category-id <category-id> --taxable-base 100.00 --iva-rate 0.21 --iva-amount 21.00 \
--notes "Receipt filed"
Useful optional fields:
--currencyrecords a non-euro amount; it defaults toEUR.--counterpartyrecords who you paid or were paid by.--category-idassigns the income or expense category. Runaeat app ledger categoriesto list the ids.--taxable-base,--iva-rate, and--iva-amountrecord the IVA breakdown.--irpf-categoryrecords the IRPF (personal income tax) category.--source-jurisdictionrecords the country a movement belongs to, as an ISO two-letter code, which matters for non-resident scopes.--notesadds a short operator note.
For a part-business, part-personal movement, set --classification MIXED and the
business share with --business-pct, a value from 0 to 1:
aeat app ledger add --date 2026-03-22 --amount 80.00 --direction OUTGOING \
--description "Mobile phone" --classification MIXED --business-pct 0.5 --category-id <category-id>
For the IVA category, EU member-state, and usage-ratio semantics behind these fields, see Classify transactions.
Use the invoice commands when you also need to track whether an invoice exists separately from the bank movement:
aeat app ledger invoice add --kind received --counterparty-nif B12345678 --invoice-number "2026-0142" --invoice-date 2026-03-21
aeat app ledger invoice list --kind issued
Received invoices are supplier invoices you owe. Issued invoices are customer invoices owed to you. For the full invoice-record workflow, see Attach invoices and receipts.
Review rows¶
List rows:
aeat app ledger list
Narrow the list with filters:
aeat app ledger list --filter period=03 --filter year=2026
aeat app ledger list --filter classification=NOT_YET_PROCESSED
aeat app ledger list --limit 20 --offset 20
Inspect one row before changing it:
aeat app ledger view <transaction-id>
See the event history for one row:
aeat app ledger history <transaction-id>
aeat app ledger track <transaction-id>
For a broader review queue, use:
aeat app ledger review --filter period=1T --filter year=2026
aeat app ledger check
review helps inspect selected ledger rows. check reports aggregate ledger
anomalies across periods and is local-only.
Export rows for review¶
Export the active ledger to a file:
aeat app ledger export --output ./ledger-2026-q1.csv --year 2026 --period 1T
The --year and --period filter keeps the export aligned with the tutorial
transaction dates. A transaction dated 2026-03-15 belongs in --year 2026 --period 1T, so it appears in the command above. Use the annual token 0A
when a whole year is the review scope:
aeat app ledger export --output ./ledger-2026.xlsx --export-format xlsx --year 2026 --period 0A
Exports are review snapshots. They are not a general edit-and-reimport
mutation path for existing ledger rows. To change saved rows, use ledger update, ledger classify, ledger allocate, ledger split, or ledger merge.
Update a row¶
Use ledger update for editable transaction fields:
aeat app ledger update <transaction-id> --description "Corrected description"
aeat app ledger update <transaction-id> --taxable-base 100.00 --iva-rate 0.21 --iva-amount 21.00
Use this for corrections such as date, value date, amount, direction, currency, counterparty, description, taxable base, IVA rate, IVA amount, IRPF category, notes, or group label.
Add or modify notes when you need a short operator explanation:
aeat app ledger update <transaction-id> --notes "Receipt checked against supplier PDF"
Attach secure purchase evidence to a transaction. The evidence id comes from
aeat app ledger evidence add (it prints evidence_id):
# Attach purchase evidence to a transaction
aeat app ledger attach <transaction-id> --purchase-invoice-evidence-id <evidence-id>
# Same purchase-evidence link through the link command
aeat app ledger link <transaction-id> --evidence-id <evidence-id>
link --invoice-id expects an id from the reconciliation invoice catalogue
(populated by the import and reconcile flows), not an id from
aeat app ledger invoice add. See Attach invoices and receipts
for the full evidence and invoice-record workflow, including the
--attachment-id option and its current limitation.
Pull a document straight from Google Drive into encrypted evidence storage:
aeat app ledger doclink <transaction-id> --source GOOGLE_DRIVE --reference <drive-file-id> --note "Supplier invoice"
The command downloads the Drive file, stores its bytes encrypted with the
transaction, and keeps the original link as provenance. Gmail links, arbitrary
URLs, and Drive files outside the granted scope are refused — evidence always
carries the document itself, never a bare link. For a refused source, download
the document yourself and attach it with aeat app ledger attach --attachment-id.
Split and re-join a transaction¶
Use split when one bank movement contains parts that need different
categories or business percentages. For example, split a 121.00 outgoing
movement into software and personal parts:
aeat app ledger split <transaction-id> --child-amount 100.00 --child-description "Software business part" --child-amount 21.00 --child-description "Personal part" --reason "mixed receipt" --yes
aeat replaces the original transaction with two separate entries — one for
each part. The split output prints one Id de transacción hija row per part,
each carrying the short id and the full id; copy them. Classify each one
separately:
aeat app ledger classify <business-child-id> --classification BUSINESS --category-id <category-id>
aeat app ledger classify <personal-child-id> --classification PERSONAL
If the split was wrong, merge the complete child cohort. Use the child ids the split printed:
aeat app ledger merge --child-id <business-child-id> --child-id <personal-child-id> --reason "undo split" --yes
You must include all the parts you split — aeat will not let you re-merge only some of them.
Remove, archive, stash, or reset ledger rows¶
For the full correction workflow — updating fields, removing, splitting, merging, and reviewing history — see Correct mistakes in your ledger. For attaching invoices and receipts, see Attach invoices and receipts.
Use the least destructive action that matches the problem:
archive— keep the transaction in your history but exclude it from ordinary work. Use this when a movement was imported by mistake but you want to keep a record of it.stash— set aside a transaction you are not sure about. A stashed transaction leaves the everyday lists. Both stash and archive are reversible:restorereturns the transaction to active.restore— return a stashed or archived transaction to active.remove— delete the transaction from your active records.reset— clear the entire transaction list for the active profile and start over. Use with care — this removes all imported data.
Examples:
aeat app ledger archive <transaction-id> --reason "duplicate imported row" --yes
aeat app ledger stash <transaction-id> --reason "waiting for invoice" --yes
aeat app ledger restore <transaction-id> --reason "stashed by mistake" --yes
aeat app ledger remove <transaction-id> --reason "wrong file imported" --yes
aeat app ledger reset --reason "re-importing all statements" --yes
remove --dry-run and reset --dry-run report the effects without modifying the storage. These commands are entirely local ledger changes and never contact the AEAT.
Classify rows¶
Classify rows before calculation. At a minimum, imported business rows usually need a business/personal/mixed classification, and expense rows normally need a category.
Start with:
aeat app ledger categories
aeat app ledger classify <transaction-id> --classification BUSINESS --category-id <category-id>
Use Classify transactions for the full classification workflow, including bulk CSV classification, mixed-use allocation, tax fields, and LLM-assisted suggestions.
For repeated descriptions, stored rules can classify matching unclassified transactions automatically:
aeat app ledger rule add --description-pattern "software" --classification BUSINESS --category-id <category-id>
aeat app ledger rule apply --dry-run
aeat app ledger rule apply
For model-assisted suggestions, use Classify transactions with an LLM. That page explains provider setup, what row data is sent to the local provider, what is previewed, what is applied, and how to override the result.
Batch edit classifications¶
Use batch classification when many reviewed rows need the same kind of
classification update. The implemented batch path is ledger classify --from-csv; it accepts transaction_id, classification, and optional
category_id.
Filter and export the rows you want to review:
aeat app ledger list --filter period=1T --filter year=2026 --filter classification=NOT_YET_PROCESSED aeat app ledger export --output ./ledger-2026-q1-review.csv --year 2026 --period 1T
Build a small classification CSV from the reviewed transaction ids:
transaction_id,classification,category_id <expense-id>,BUSINESS,<category-id> <personal-id>,PERSONAL,
Apply it:
aeat app ledger classify --from-csv ./classifications.csv
Review afterwards:
aeat app ledger list --filter period=1T --filter year=2026 aeat app ledger preflight --year 2026 --period 1T
This batch path does not bulk edit descriptions, amounts, IVA fields, notes, or
attachments. Edit those one row at a time with ledger update, ledger attach,
or ledger doclink.
Check readiness for a filing period¶
Run preflight before calculating a modelo:
aeat app ledger preflight --year 2026 --period 1T
Preflight reports missing facts such as category, taxable base, IVA amount, IVA rate, currency, or proportionality reference. Fix the rows it names, then run preflight again.
Check the overall ledger state:
aeat app ledger status --year 2026 --period 1T
Continue to calculation only when the active profile and target period are ready enough for the modelo you are preparing.
For calculation review in Google Sheets, see Review calculations with Google Sheets. That workflow exports a modelo calculation surface to Sheets; it is separate from ledger CSV/XLSX export.
If a command stops with an error¶
If a command reports that no profile is active, the period is invalid, or the ledger is not ready, use Diagnose and repair your local setup.