Business events are the application-facing input to Logistiki. They represent business intent and carry enough data for the knowledge layer to decide whether the event is allowed, whether approvals are required, whether it creates accounting impact, which policy applies, which template applies, which accounts are involved, which dimensions are required, and how audit evidence should be recorded.

Defining an event

Use the Logistiki.Event behaviour macro:

defmodule Logistiki.Event.DepositReceived do
  use Logistiki.Event, type: "deposit_received"

  defevent do
    field :cash_account_code, :string
  end
end

use Logistiki.Event, type: "..." declares the event type and generates an event_type/1 and normalize/1 implementation. defevent declares the event's embedded schema — the common normalized fields (id, amount, currency, account_code, entity_type, ...) plus any event-specific fields. Extra event-specific fields are merged into the Logistiki.Event.Normalized struct during normalization.

Built-in events

ModuleTypeAccounting impact
Logistiki.Event.DepositReceiveddeposit_receiveddebit cash, credit client liability
Logistiki.Event.TransferSettledtransfer_settleddebit destination, credit source
Logistiki.Event.FeeAssessedfee_assesseddebit client liability, credit fee income
Logistiki.Event.InterestAccruedinterest_accrueddebit interest expense, credit client liability
Logistiki.Event.RefundIssuedrefund_issueddebit client liability, credit cash
Logistiki.Event.InvoicePaidinvoice_paiddebit counterparty, credit client
Logistiki.Event.AccountOpenedaccount_openednone (audit only)

Events with no accounting impact

Some events produce no journal (e.g. a customer opened an account, a document was uploaded, an approval was granted). Declare has_accounting_impact: false:

defevent do
  field :has_accounting_impact, :boolean, default: false
end

A result with no journal is valid:

{:ok, %Logistiki.Accounting.Result{journal: nil, explanation: %{reason: :no_accounting_impact}}}

The normalized event

Logistiki.Event.Normalized is the canonical, flattened representation used by the knowledge layer. Fields include id, type, source_system, source_id, actor_id, occurred_at, effective_date, amount, currency, entity_id, account_id, account_code, cash_account_code, fee_income_account_code, interest_expense_account_code, destination_account_code, counterparty_id, product_code, jurisdiction, fee_type, entity_type, has_accounting_impact, and metadata. Not every event populates every field.

Persistence

Logistiki.Events.persist/1 stores the raw event payload alongside its normalized form in business_events, enabling replay and audit. The pipeline persists events automatically; the persisted status moves received → normalized → processed (or no_accounting_impact / blocked / requires_approval / failed).