# `Logistiki.BusinessEntities`
[🔗](https://github.com/thanos/logistiki/blob/v0.1.0/lib/logistiki/business_entities/context.ex#L1)

The context for hierarchical business entities.

Business entities represent legal, operational, customer, organizational, or
ownership structures. They form a tree maintained through a closure table
(`Logistiki.BusinessEntities.BusinessEntityClosure`).

## Closure table rules

  * a self-row (depth 0) is always present
  * cycles are prevented on move
  * the closure is rewritten on insert and on `move_entity/2`
  * subtree and ancestor queries are O(closure rows), not recursive CTEs

# `ancestor_ids`
*since 0.1.0* 

```elixir
@spec ancestor_ids(Logistiki.BusinessEntities.BusinessEntity.t()) :: [integer()]
```

Returns the ids of all ancestors of `entity` (including itself).

## Arguments

  * `entity` — `%BusinessEntity{}`.

## Returns

  * `[integer()]` — ancestor ids including `entity.id`.

## Examples

    iex> Logistiki.BusinessEntities.ancestor_ids(acme_trading)
    [2, 1]

# `create_entity`
*since 0.1.0* 

```elixir
@spec create_entity(map()) ::
  {:ok, Logistiki.BusinessEntities.BusinessEntity.t()} | {:error, term()}
```

Creates a new business entity. When `:parent_id` is given the entity is
linked as a child and the closure table is updated.

## Arguments

  * `attrs` — `map()` or `keyword()` of entity attributes:
      * `:name` — `String.t` — **required** (e.g. `"Acme Holdings"`)
      * `:entity_type` — `String.t` — **required** (e.g. `"company"`, `"trust"`)
      * `:status` — `String.t` — defaults to `"pending"`
      * `:parent_id` — `integer() | nil` — the parent entity id; `nil` for roots
      * `:legal_name` — `String.t`
      * `:jurisdiction` — `String.t` — e.g. `"US"`
      * `:external_id` — `String.t` — must be unique
      * `:metadata` — `map()`

## Returns

  * `{:ok, %BusinessEntity{}}` — the created entity.
  * `{:error, %Ecto.Changeset{}}` — validation failed (e.g. invalid `entity_type`).
  * `{:error, :parent_closure_missing}` — the parent has no closure rows (should not happen).

## Examples

    iex> {:ok, root} = Logistiki.BusinessEntities.create_entity(%{name: "Acme Holdings", entity_type: "company", status: "active"})
    iex> root.parent_id
    nil

    iex> {:ok, child} = Logistiki.BusinessEntities.create_entity(%{
    ...>   name: "Acme Trading Ltd", entity_type: "company", parent_id: root.id
    ...> })
    iex> child.parent_id
    1

    iex> {:error, changeset} = Logistiki.BusinessEntities.create_entity(%{name: "X", entity_type: "wizard"})
    iex> errors_on(changeset)[:entity_type]
    ["is invalid"]

# `descendant_ids`
*since 0.1.0* 

```elixir
@spec descendant_ids(Logistiki.BusinessEntities.BusinessEntity.t()) :: [integer()]
```

Returns the ids of all descendants of `entity` (including itself).

Uses the closure table directly — faster than `list_descendants/1` when only
ids are needed (e.g. for balance aggregation queries).

## Arguments

  * `entity` — `%BusinessEntity{}`.

## Returns

  * `[integer()]` — descendant ids including `entity.id`.

## Examples

    iex> Logistiki.BusinessEntities.descendant_ids(acme_holdings)
    [1, 2, 3]

# `get_entity`
*since 0.1.0* 

```elixir
@spec get_entity(integer()) ::
  {:ok, Logistiki.BusinessEntities.BusinessEntity.t()} | {:error, :not_found}
```

Fetches a single entity by id.

## Arguments

  * `id` — `integer()` — the entity primary key.

## Returns

  * `{:ok, %BusinessEntity{}}` — the entity was found.
  * `{:error, :not_found}` — no entity with that id.

## Examples

    iex> {:ok, entity} = Logistiki.BusinessEntities.get_entity(1)
    iex> entity.name
    "Acme Holdings"

    iex> {:error, :not_found} = Logistiki.BusinessEntities.get_entity(999)

# `get_entity!`
*since 0.1.0* 

```elixir
@spec get_entity!(integer()) :: Logistiki.BusinessEntities.BusinessEntity.t()
```

Fetches a single entity by id, raising if not found.

## Arguments

  * `id` — `integer()` — the entity primary key.

## Returns

  * `%BusinessEntity{}` — the entity. Raises `Ecto.NoResultsError` if not found.

## Examples

    iex> entity = Logistiki.BusinessEntities.get_entity!(1)
    iex> entity.name
    "Acme Holdings"

# `list_ancestors`
*since 0.1.0* 

```elixir
@spec list_ancestors(Logistiki.BusinessEntities.BusinessEntity.t()) :: [
  Logistiki.BusinessEntities.BusinessEntity.t()
]
```

Lists all ancestors of `entity` ordered from nearest to farthest, using the
closure table.

## Arguments

  * `entity` — `%BusinessEntity{}` — the descendant entity.

## Returns

  * `[BusinessEntity.t()]` — ordered by `depth` ascending. Empty list if the
    entity is a root.

## Examples

    iex> Logistiki.BusinessEntities.list_ancestors(acme_trading)
    [%BusinessEntity{name: "Acme Holdings", ...}]

# `list_children`
*since 0.1.0* 

```elixir
@spec list_children(Logistiki.BusinessEntities.BusinessEntity.t()) :: [
  Logistiki.BusinessEntities.BusinessEntity.t()
]
```

Lists direct children of `entity`.

## Arguments

  * `entity` — `%BusinessEntity{}` — the parent entity.

## Returns

  * `[BusinessEntity.t()]` — ordered by name. Empty list if no children.

## Examples

    iex> Logistiki.BusinessEntities.list_children(acme_holdings)
    [%BusinessEntity{name: "Acme Trading Ltd", ...}, %BusinessEntity{name: "Acme Treasury Ltd", ...}]

# `list_descendants`
*since 0.1.0* 

```elixir
@spec list_descendants(Logistiki.BusinessEntities.BusinessEntity.t()) :: [
  Logistiki.BusinessEntities.BusinessEntity.t()
]
```

Lists all descendants of `entity` (excluding the entity itself), using the
closure table for O(rows) performance.

## Arguments

  * `entity` — `%BusinessEntity{}` — the root of the subtree.

## Returns

  * `[BusinessEntity.t()]` — ordered by name. Empty list if no descendants.

## Examples

    iex> Logistiki.BusinessEntities.list_descendants(acme_holdings)
    [%BusinessEntity{name: "Acme Trading Ltd", ...}, %BusinessEntity{name: "Acme Treasury Ltd", ...}]

# `list_entities`
*since 0.1.0* 

```elixir
@spec list_entities(keyword()) :: [Logistiki.BusinessEntities.BusinessEntity.t()]
```

Lists entities, optionally filtered.

## Arguments

  * `opts` — `keyword()` of options:
      * `:status` — `String.t` — e.g. `"active"`, `"frozen"`
      * `:entity_type` — `String.t` — e.g. `"company"`, `"trust"`
      * `:parent_id` — `integer() | nil` — filter by parent; `nil` lists roots

## Returns

  * `[BusinessEntity.t()]` — ordered by name ascending. Empty list if none match.

## Examples

    iex> Logistiki.BusinessEntities.list_entities(status: "active")
    [%BusinessEntity{name: "Acme Holdings", ...}, %BusinessEntity{name: "Bluewater Trust", ...}]

    iex> Logistiki.BusinessEntities.list_entities(parent_id: nil)
    [%BusinessEntity{name: "Acme Holdings", ...}]

# `move_entity`
*since 0.1.0* 

```elixir
@spec move_entity(
  Logistiki.BusinessEntities.BusinessEntity.t(),
  Logistiki.BusinessEntities.BusinessEntity.t() | integer() | nil
) :: {:ok, Logistiki.BusinessEntities.BusinessEntity.t()} | {:error, term()}
```

Moves `entity` under `new_parent` (which may be `nil` for a root), rewriting
the closure table for the entire subtree.

## Arguments

  * `entity` — `%BusinessEntity{}` — the entity to move.
  * `new_parent` — one of:
      * `%BusinessEntity{}` — the new parent
      * `nil` — move to root (no parent)
      * `integer()` — the new parent's id

## Returns

  * `{:ok, %BusinessEntity{}}` — the moved entity with updated `parent_id`.
  * `{:error, term()}` — the move failed (rolled back).

## Examples

    iex> {:ok, moved} = Logistiki.BusinessEntities.move_entity(acme_trading, bluewater_trust)
    iex> moved.parent_id
    5

    iex> {:ok, root} = Logistiki.BusinessEntities.move_entity(acme_trading, nil)
    iex> root.parent_id
    nil

# `update_entity`
*since 0.1.0* 

```elixir
@spec update_entity(Logistiki.BusinessEntities.BusinessEntity.t(), map()) ::
  {:ok, Logistiki.BusinessEntities.BusinessEntity.t()}
  | {:error, Ecto.Changeset.t()}
```

Updates a business entity's mutable attributes (not its parent — use
`move_entity/2` to change the parent).

## Arguments

  * `entity` — `%BusinessEntity{}` — the entity to update.
  * `attrs` — `map()` or `keyword()` of attributes to change (e.g.
    `%{status: "active", legal_name: "Acme Holdings LLC"}`).

## Returns

  * `{:ok, %BusinessEntity{}}` — the updated entity.
  * `{:error, %Ecto.Changeset{}}` — validation failed.

## Examples

    iex> {:ok, updated} = Logistiki.BusinessEntities.update_entity(entity, %{status: "active"})
    iex> updated.status
    "active"

---

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