# `Logistiki.Knowledge.Program`
[🔗](https://github.com/thanos/logistiki/blob/v0.1.0/lib/logistiki/knowledge/program.ex#L1)

The Datalog knowledge base for Logistiki.

This module is an `ExDatalog.Schema` that declares the relations, the
accounting policies, posting templates, account-role resolution rules, and
business rules that govern how business events become accounting entries.

## Static knowledge (compile-time facts)

Templates, template postings, required dimensions, and static account
mappings are declared as facts and are stable across events.

## Runtime knowledge (per event)

Per-event facts (`event_type`, `event_amount_cents`, `event_account`, ...) are
added at runtime by `Logistiki.Knowledge.Facts` before materialization.

## Derived knowledge

Business rules (`blocked`, `requires_approval`), accounting policies
(`policy`), and account roles (`account_role`) are derived by Datalog rules.

Datalog decides facts and relationships. Elixir materializes journals and
postings.

## Generated functions

`use ExDatalog.Schema` generates these functions:

  * `program/0` — returns the `ExDatalog.Program` with static facts (templates,
    template_posting, requires_dimension, account_role_static).
  * `new/0` — returns a blank program without compile-time facts (for runtime
    fact injection).
  * `materialize/1` — materializes the program. Returns
    `{:ok, %ExDatalog.Knowledge{}}`.
  * `query/2` — executes a named query against materialized knowledge.

## Named queries

  * `:selected_policy` — `find P where policy(:evt, P)` — the selected policy.
  * `:account_roles` — `find R, C where account_role(:evt, R, C)` — resolved
    roles.
  * `:template_postings` — `find P, S, D, R, A, C where template_posting(...)`
    — all template postings.

## Static policies

| Policy | Event type | Condition |
|--------|-----------|-----------|
| `:cash_deposit` | `deposit_received` | — |
| `:corporate_wire_fee` | `fee_assessed` | `fee_type: wire_fee`, `entity_type: corporate` |
| `:retail_wire_fee` | `fee_assessed` | `fee_type: wire_fee`, `entity_type: individual` |
| `:internal_transfer` | `transfer_settled` | — |
| `:interest_accrual` | `interest_accrued` | — |
| `:refund_paid` | `refund_issued` | — |

# `account_role`

```elixir
@spec account_role(term(), term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `account_role` relation.

    account_role(arg_1, arg_2, arg_3)
    #=> {"account_role", [arg_1, arg_2, arg_3]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `account_role_static`

```elixir
@spec account_role_static(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `account_role_static` relation.

    account_role_static(arg_1, arg_2)
    #=> {"account_role_static", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `blocked`

```elixir
@spec blocked(term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `blocked` relation.

    blocked(arg_1)
    #=> {"blocked", [arg_1]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `event_account`

```elixir
@spec event_account(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `event_account` relation.

    event_account(arg_1, arg_2)
    #=> {"event_account", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `event_amount_cents`

```elixir
@spec event_amount_cents(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `event_amount_cents` relation.

    event_amount_cents(arg_1, arg_2)
    #=> {"event_amount_cents", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `event_cash_account`

```elixir
@spec event_cash_account(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `event_cash_account` relation.

    event_cash_account(arg_1, arg_2)
    #=> {"event_cash_account", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `event_currency`

```elixir
@spec event_currency(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `event_currency` relation.

    event_currency(arg_1, arg_2)
    #=> {"event_currency", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `event_destination_account`

```elixir
@spec event_destination_account(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `event_destination_account` relation.

    event_destination_account(arg_1, arg_2)
    #=> {"event_destination_account", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `event_entity_type`

```elixir
@spec event_entity_type(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `event_entity_type` relation.

    event_entity_type(arg_1, arg_2)
    #=> {"event_entity_type", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `event_fee_income_account`

```elixir
@spec event_fee_income_account(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `event_fee_income_account` relation.

    event_fee_income_account(arg_1, arg_2)
    #=> {"event_fee_income_account", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `event_fee_type`

```elixir
@spec event_fee_type(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `event_fee_type` relation.

    event_fee_type(arg_1, arg_2)
    #=> {"event_fee_type", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `event_interest_expense_account`

```elixir
@spec event_interest_expense_account(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `event_interest_expense_account` relation.

    event_interest_expense_account(arg_1, arg_2)
    #=> {"event_interest_expense_account", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `event_product`

```elixir
@spec event_product(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `event_product` relation.

    event_product(arg_1, arg_2)
    #=> {"event_product", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `event_type`

```elixir
@spec event_type(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `event_type` relation.

    event_type(arg_1, arg_2)
    #=> {"event_type", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `materialize`

```elixir
@spec materialize(keyword()) :: {:ok, ExDatalog.Knowledge.t()} | {:error, term()}
```

Materializes this schema's program. Accepts the same options as
`ExDatalog.materialize/2`.

# `new`

```elixir
@spec new() :: ExDatalog.Program.t()
```

Returns a blank `ExDatalog.Program` from this schema's relations and
rules, **without** any compile-time facts.

Add runtime facts via the pipeable `Program.add_fact/2`:

    DeptCount.new()
    |> Program.add_fact(DeptCount.emp(:alice, :eng))
    |> Program.add_fact(DeptCount.emp(:bob, :eng))
    |> Program.materialize()

# `policy`

```elixir
@spec policy(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `policy` relation.

    policy(arg_1, arg_2)
    #=> {"policy", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `program`

```elixir
@spec program() :: ExDatalog.Program.t()
```

Returns the `ExDatalog.Program` built from this schema's relations,
compile-time facts, and rules.

Use this when all facts are declared at compile time via `fact/1`.
For runtime facts, use `new/0` and `Program.add_fact/2`.

# `queries`

```elixir
@spec queries() :: %{required(atom()) =&gt; ExDatalog.Schema.QueryMeta.t()}
```

Returns a map of query names to their metadata.

# `query`

```elixir
@spec query(atom(), ExDatalog.Knowledge.t()) :: [term()]
```

Executes a named query against materialized knowledge.

Returns a list of results. For single-column `find`, returns a list of
values. For multi-column `find`, returns a list of tuples.

# `requires_approval`

```elixir
@spec requires_approval(term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `requires_approval` relation.

    requires_approval(arg_1)
    #=> {"requires_approval", [arg_1]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `requires_dimension`

```elixir
@spec requires_dimension(term(), term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `requires_dimension` relation.

    requires_dimension(arg_1, arg_2)
    #=> {"requires_dimension", [arg_1, arg_2]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `sanctions_match`

```elixir
@spec sanctions_match(term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `sanctions_match` relation.

    sanctions_match(arg_1)
    #=> {"sanctions_match", [arg_1]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `template`

```elixir
@spec template(term()) :: {String.t(), [term()]}
```

Constructs a fact tuple for the `template` relation.

    template(arg_1)
    #=> {"template", [arg_1]}

Pass the result to `Program.add_fact/2` as a fact tuple.

# `template_posting`

```elixir
@spec template_posting(term(), term(), term(), term(), term(), term()) ::
  {String.t(), [term()]}
```

Constructs a fact tuple for the `template_posting` relation.

    template_posting(arg_1, arg_2, arg_3, arg_4, arg_5, arg_6)
    #=> {"template_posting", [arg_1, arg_2, arg_3, arg_4, arg_5, arg_6]}

Pass the result to `Program.add_fact/2` as a fact tuple.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
