Skip to main content

Banking

Banking entities separate logical business accounts from physical open-banking sessions. Logical accounts are used by contracts, properties, holders, and transactions. Integration sessions are provider-specific connection records.

bank_accounts

Purpose

Logical representation of a bank account.

Suggested attributes

FieldDescription
idPrimary identifier.
bank_nameBank name.
ibanIBAN.
bicBIC/SWIFT code.
account_nameHuman-readable account name.
currencyAccount currency.
initial_balanceInitial balance entered when a bank account is created manually. Default 0.00.
initial_balance_currencyCurrency of the initial balance. Defaults to account currency.
current_balanceLast account balance amount fetched during direct bank transaction synchronization. Nullable for manual accounts or providers that do not return balances.
current_balance_currencyCurrency returned with the balance snapshot. Falls back to account currency when the provider omits it.
current_balance_atTimestamp associated with the balance snapshot, or the sync timestamp when the provider omits a reference date.
current_balance_sourceProvider balance type selected for display, for example interimAvailable, interimBooked, or closingBooked.
current_balance_raw_jsonRaw provider balance object selected for the persisted snapshot.
is_dedicated_to_rentalsWhether the account is dedicated to rental operations.
notesFree-form notes.
created_atCreation timestamp.
updated_atLast update timestamp.

Relationships

RelationshipDescription
bank_accounts to bank_account_holdersOne account can have many holders.
bank_accounts to property_bank_accountsOne account can be linked to many properties.
bank_accounts to bank_account_connectionsOne account can be linked to provider sessions.
bank_accounts to bank_transactionsOne account has many imported transactions.

Notes and design rationale

Logical account data should remain stable even if the open-banking provider, session, or provider account identifier changes. For direct connections, the current balance fields are refreshed from the provider during transaction sync. For manually created accounts with CSV imports, banking-service recalculates current_balance as initial_balance + SUM(bank_transactions.amount) after each successful CSV import. These balance fields are a display snapshot, not a ledger or source of truth for historical balances.

bank_account_holders

Purpose

Tracks holders and co-holders of bank accounts.

Suggested attributes

FieldDescription
idPrimary identifier.
bank_account_idLinked logical bank account.
party_idHolder party.
roleHOLDER, CO_HOLDER, or AUTHORIZED_USER.
ownership_percentageAccount ownership or beneficial share when relevant.
notesFree-form notes.

Relationships

RelationshipDescription
bank_account_holders.bank_account_id to bank_accounts.idMany holders can belong to one account.
bank_account_holders.party_id to parties.idOne party can hold many accounts.

Notes and design rationale

This table supports co-owned accounts and delegated users without duplicating bank account records.

property_bank_accounts

Purpose

Links logical bank accounts to properties.

Suggested attributes

FieldDescription
idPrimary identifier.
property_idLinked property.
bank_account_idLinked logical bank account.
purposeRENT_COLLECTION, EXPENSES, DEPOSIT, or GENERIC.
start_dateLink start date.
end_dateLink end date.
is_defaultWhether this is the default account for the purpose.
notesFree-form notes.

Relationships

RelationshipDescription
property_bank_accounts.property_id to properties.idOne property can use many accounts.
property_bank_accounts.bank_account_id to bank_accounts.idOne account can serve many properties.

Notes and design rationale

One bank account may be used for several properties. One property may use different accounts for rent collection, deposits, and expenses.

banking_sessions

Purpose

Persists OAuth AIS authorization sessions created via Enable Banking. One record per user per bank per environment. Survives service restarts and allows audit, status tracking, and session recovery without re-authorization.

Attributes

FieldTypeDescription
idINT UNSIGNEDPrimary identifier.
user_idVARCHAR(128)Platform user who authorized the session. References users.id.
envVARCHAR(16)Runtime environment (LOCAL, TEST, LIVE). Part of the unique key to isolate environments.
bank_nameVARCHAR(100)Bank name as returned by Enable Banking (e.g. UniCredit).
countryCHAR(2)ISO 3166-1 alpha-2 country code of the bank (e.g. IT).
session_idVARCHAR(128)Enable Banking session identifier. Used to call /sessions/{session_id} and DELETE /sessions/{session_id}.
authorization_idVARCHAR(128)Enable Banking authorization flow identifier. Nullable.
valid_untilDATETIMEConsent expiry date as declared by Enable Banking (typically 90 days from authorization).
authorized_atDATETIMETimestamp when the OAuth callback completed successfully.
statusENUMAUTHORIZED, EXPIRED, REVOKED.
accountsJSONArray of Enable Banking account UIDs authorized in this session.
session_jsonJSONFull session payload as returned by the callback flow. Used for complete recovery after service restart.
request_countINT UNSIGNEDNumber of API calls made using this session. Observability field. Default 0.
last_accessed_atDATETIMETimestamp of the last API call using this session. Nullable.
last_actionVARCHAR(64)Last operation performed (balances, transactions, details, authorized). Nullable.
last_account_idVARCHAR(128)Enable Banking UID of the last account accessed. Nullable.
created_atDATETIMERow creation timestamp.
updated_atDATETIMELast row update timestamp. Auto-managed by the database.

Constraints

ConstraintFieldsDescription
PRIMARY KEYidAuto-increment surrogate key.
UNIQUE uq_user_bankuser_id, bank_name, country, envOne active session per user per bank per environment. A new authorization for the same bank overwrites via upsert.
INDEX idx_session_idsession_idFast lookup by Enable Banking session ID for revocation.
INDEX idx_user_iduser_idFast lookup of all sessions for a user.
INDEX idx_valid_untilvalid_untilSupports expiry sweeps.
INDEX idx_statusstatusFilters active vs revoked sessions.

Relationships

RelationshipDescription
banking_sessions.user_id to users.idEach session belongs to one platform user.
banking_sessions to bank_account_connectionsA session can expose or connect to many logical bank accounts.

Notes and design rationale

banking_sessions represents integration and authorization state, not a business bank account. The user_id column links the OAuth consent to a specific platform user, enabling per-user session management and audit.

The session_json column stores the complete callback payload so that account details, IBAN data, and authorization metadata can be recovered after a service restart without requiring the user to re-authorize.

The status field is the authoritative state for the consent lifecycle. A session transitions from AUTHORIZED to REVOKED only through an explicit DELETE /sessions/{session_id} call to Enable Banking. EXPIRED is derived at query time from valid_until when no explicit revocation has occurred.

bank_account_connections

Purpose

Links logical bank accounts to physical or open-banking sessions.

Suggested attributes

FieldDescription
idPrimary identifier.
bank_account_idLogical bank account.
banking_session_idPhysical/open-banking session.
provider_account_idAccount identifier from the provider.
connection_statusCurrent connection status.
last_sync_atLast successful synchronization timestamp.
notesFree-form notes.

Relationships

RelationshipDescription
bank_account_connections.bank_account_id to bank_accounts.idOne logical account can have many integration connections over time.
bank_account_connections.banking_session_id to banking_sessions.idOne session can connect several logical accounts.

Notes and design rationale

This avoids mixing business account data with provider-specific connection state.

bank_csv_import_templates

Purpose

Stores reusable CSV bank statement import mappings. Templates can be private to one user or public after administrative approval.

Suggested attributes

FieldDescription
idPrimary identifier.
nameHuman-readable template name.
bank_nameBank or issuer name.
visibilityPRIVATE or PUBLIC.
owner_user_idOwner for private templates.
source_template_idOriginal private template when a public clone is approved.
delimiter, encoding, has_header, skip_rowsCSV parsing options.
date_format, decimal_separator, thousands_separator, amount_modeField parsing conventions.
mapping_jsonMapping from CSV columns to canonical bank transaction fields.
transform_jsonOptional transformation rules.
dedupe_jsonDeduplication strategy.
sample_headers_jsonNon-sensitive header sample for recognition and review.

Canonical CSV bank transaction model

CSV templates do not map columns directly to the physical bank_transactions table. They map source columns to a canonical movement model owned by banking-service; the service then converts canonical records into database rows. This keeps frontend builders stable when the physical schema changes.

The canonical amount is signed. During persistence, banking-service stores an absolute amount and derives transaction_type as DEBIT for negative values and CREDIT for positive values, unless an explicit valid type is supplied.

Canonical fieldRequiredCSV mappablePhysical targetNotes
booking_dateYesYestransaction_dateDate normalized as YYYY-MM-DD.
value_dateNoYesvalue_dateDefaults to booking_date.
amountYesYesamount, transaction_typeSigned decimal in the canonical model.
currencyNoYescurrencyISO 4217 code. Defaults to EUR.
descriptionNoYesdescriptionBank statement description.
counterparty_nameNoYescounterparty_nameCounterparty display name.
counterparty_ibanNoYescounterparty_ibanCounterparty IBAN.
referenceNoYesexternal_referenceBank reference, CRO, TRN, mandate reference, or equivalent.
external_idNoYesexternal_transaction_idStable transaction identifier from the bank when available.
transaction_typeNoYestransaction_typeOptional CREDIT or DEBIT; normally derived from amount.
balance_afterNoYesraw_json.balance_afterKept in raw_json until a dedicated physical column is needed.
category_hintNoYesraw_json.category_hintOptional bank-provided classification hint.
property_idNoNoproperty_idImport context field, not mapped from CSV by default.
raw_rowNoNoraw_json.raw_rowParsed raw CSV row snapshot for audit/debugging.

The code-level contract is exposed by backend/banking-service/modules/bankTransactionCanonicalModel.js.

CSV import backend APIs

banking-service owns CSV parsing and template persistence. Browser-facing flows must call banking-service; the service validates account write access through data-service before any endpoint writes or synchronizes transactions for a bank_account_id.

EndpointPurpose
POST /csv-import/previewParses a CSV payload and returns detected columns, preview rows, inferred formats, canonical mapping suggestions, and warnings. Accepts JSON { csvText, options } or raw text/csv.
POST /csv-import/validate-mappingConverts CSV rows to the canonical bank transaction model, validates required fields, returns normalized preview, row-level errors, and duplicate estimates.
POST /csv-import/runsCreates an import run, inserts non-duplicate bank_transactions, stores row errors, and returns inserted/duplicate/rejected totals.
GET /csv-import/runsLists import runs for the authenticated user, optionally filtered by bank_account_id.
GET /csv-import/runs/:idReturns one import run with row errors and transactions created by that run.
GET /csv-import/templatesLists active private CSV templates owned by the authenticated user plus approved public templates.
POST /csv-import/templatesCreates a private template owned by the authenticated user. visibility is forced to PRIVATE.
POST /csv-import/templates/:id/publication-requestsSubmits an owned private template for administrative publication review. Duplicate open submissions return the existing request.
PUT /csv-import/templates/:idUpdates an active private template owned by the authenticated user and increments its version.
DELETE /csv-import/templates/:idDeletes an unused private template, or archives it when import runs already reference it.
GET /csv-import/template-publication-requestsAdmin-only review queue for publication requests. Supports optional status.
GET /csv-import/template-publication-requests/:idAdmin-only detail including requester, reviewed template, and public clone when available.
POST /csv-import/template-publication-requests/:id/approveAdmin-only approval. Clones the submitted private template as PUBLIC.
POST /csv-import/template-publication-requests/:id/rejectAdmin-only rejection with optional review note.

CSV preview supports ;, ,, tab, and pipe delimiters, optional header rows, skipped rows, row/size limits, and basic inference for dates, decimal separators, amount columns, and common bank-statement headers.

CSV import deduplication is skip-only. Existing transactions are never updated by CSV import. When external_transaction_id is present, it is checked first for the same bank_account_id; otherwise the importer uses deterministic import_hash. Duplicates inside the same uploaded file are also skipped.

Public CSV templates are created only by cloning an approved private template. The public clone stores parsing options, canonical field mapping, and sanitized header samples; it does not store uploaded bank statement rows, transaction values, import-run payloads, or live banking connection data.

CSV import hardening applies at multiple points: parser requests enforce size and row limits, uploaded file names are sanitized before audit persistence, public-template samples are limited to header names, and import logs are row/error oriented so they avoid exposing full statement contents unless needed for private run debugging.

bank_csv_import_runs

Purpose

Audits each CSV import execution against a bank account.

Suggested attributes

FieldDescription
idPrimary identifier.
template_idTemplate used for the import.
bank_account_idTarget logical account.
uploaded_by_user_idUser who started the import.
original_file_nameSource file name.
file_sha256File hash for audit and duplicate detection.
statusImport lifecycle status.
total_rows, parsed_rows, inserted_rows, duplicate_rows, rejected_rowsExecution counters.
summary_json, preview_json, error_jsonStructured execution details.

bank_csv_import_run_errors

Purpose

Stores row-level warnings and errors for CSV imports.

Suggested attributes

FieldDescription
idPrimary identifier.
run_idImport run.
row_numberCSV row number.
severityWARNING or ERROR.
error_codeMachine-readable code.
error_messageHuman-readable description.
source_columnCSV column involved when relevant.
raw_row_jsonRaw row snapshot for audit/debugging.

bank_csv_import_template_publication_requests

Purpose

Tracks requests to promote a private CSV import template to a public reusable template.

Suggested attributes

FieldDescription
idPrimary identifier.
template_idPrivate template submitted for review.
requested_by_user_idUser who submitted the request.
reviewed_by_user_idAdmin reviewer.
statusSUBMITTED, APPROVED, REJECTED, or CANCELLED.
request_note, review_noteFree-form notes.
public_template_idPublic clone created on approval.

bank_transactions

Purpose

Represents actual imported bank movements.

Suggested attributes

FieldDescription
idPrimary identifier.
bank_account_idLogical account where the transaction occurred.
property_idOptional property attribution. Nullable at import time.
source_import_run_idCSV import run that created the movement, when applicable.
transaction_dateBooking transaction date.
value_dateValue date.
amountSigned or absolute transaction amount, according to implementation convention.
currencyTransaction currency.
descriptionBank description.
counterparty_nameCounterparty name.
counterparty_ibanCounterparty IBAN.
transaction_typeCREDIT or DEBIT.
external_transaction_idProvider or bank transaction identifier when available.
external_referenceProvider or bank reference.
import_hashDeterministic hash used to skip duplicate CSV-imported transactions per account.
classification_statusUNCLASSIFIED, AUTO_CLASSIFIED, or MANUALLY_CLASSIFIED.
raw_jsonRaw canonical/import row for CSV/manual imports.
raw_payloadOriginal provider payload for audit/debugging.
created_atCreation timestamp.
updated_atLast update timestamp.

Relationships

RelationshipDescription
bank_transactions.bank_account_id to bank_accounts.idEach transaction belongs to one account.
bank_transactions.property_id to properties.idOptional direct property classification.
bank_transactions.source_import_run_id to bank_csv_import_runs.idLinks transactions back to a CSV import run.
bank_transactions to bank_transaction_splitsOne transaction can be split into many accounting lines.
bank_transactions to rent_payment_matchesTransactions can reconcile rent installments.
bank_transactions to expense_payment_matchesTransactions can reconcile expected expenses.

Notes and design rationale

property_id may be null when a transaction is first imported. Later classification can assign a property and create one or more split lines.

Graphical local diagrams

bank_accounts

bank_account_holders

property_bank_accounts

banking_sessions

bank_account_connections

bank_transactions