Logistiki.Accounting.JournalBuilder (logistiki v0.1.0)

Copy Markdown View Source

Builds a journal draft (and its posting changesets) from a knowledge result and a normalized event.

The journal builder is the bridge between the knowledge layer (which decides facts and relationships) and the accounting runtime (which materializes Elixir structs). Datalog does not construct structs; this module does.

The draft journal is status: "draft" and carries the selected policy, template, event id, idempotency key, and an explanation map. The postings are built by Logistiki.Accounting.PostingBuilder and validated by Logistiki.Accounting.InvariantValidator before posting.

Summary

Functions

Builds a draft journal struct (with postings attached) from a knowledge result and normalized event.

Builds a reversal journal struct that exactly negates journal's postings.

Functions

build(result, event)

(since 0.1.0)
@spec build(Logistiki.Knowledge.Result.t(), Logistiki.Event.Normalized.t()) ::
  {:ok, Logistiki.Accounting.Journal.t() | nil, [Posting.t()], map()}
  | {:error, Logistiki.Error.t()}

Builds a draft journal struct (with postings attached) from a knowledge result and normalized event.

When the knowledge result has no policy (e.g. an event with no accounting impact), returns {:ok, nil, [], explanation}.

Arguments

  • result%Logistiki.Knowledge.Result{} — the knowledge layer output.
  • event%Logistiki.Event.Normalized{} — the flattened event.

Returns

  • {:ok, %Journal{}, [Posting.t()], map()} — the draft journal, its postings, and the explanation map.
  • {:ok, nil, [], map()} — no accounting impact (policy is nil).
  • {:error, %Error{}} — a posting could not be built (missing role).

Examples

iex> {:ok, journal, postings, explanation} = Logistiki.Accounting.JournalBuilder.build(knowledge_result, normalized_event)
iex> journal.status
"draft"
iex> journal.selected_policy
"cash_deposit"
iex> length(postings)
2

build_reversal(journal, postings, attrs)

(since 0.1.0)

Builds a reversal journal struct that exactly negates journal's postings.

Arguments

  • journal%Journal{} — the posted journal to reverse.
  • postings[Posting.t()] — the original journal's postings.
  • attrskeyword() or map() of options:
    • :descriptionString.t — defaults to "Reversal of journal <id>"
    • :effective_dateDate.t — defaults to Date.utc_today/0
    • :idempotency_keyString.t — defaults to "reversal:<original_key>"
    • :reasonString.t — the reason for the reversal
    • :metadatamap()

Returns

  • {:ok, %Journal{}, [Posting.t()]} — the draft reversal journal and its reversal postings.

Examples

iex> {:ok, reversal, postings} = Logistiki.Accounting.JournalBuilder.build_reversal(journal, original_postings, reason: "mistaken fee")
iex> reversal.reversal_of_id
1
iex> hd(postings).debit_credit
"credit"