Generic registry/dispatch for the source_refs decoupling contract.
phoenix_kit_warehouse documents (Internal Orders, Goods Issues) link back
to arbitrary host-owned "source" records — a sub-order, a top-level order,
or anything else a consuming app wants to link — via a source_refs JSONB
column shaped [%{"kind" => "sub_order", "uuid" => "..."}, ...]. This
module never queries a host table directly; instead the host registers,
per kind, three callbacks in its own config:
config :phoenix_kit_warehouse,
source_kinds: [
%{
kind: "sub_order",
label: "Sub-order",
search: {MyApp.Warehouse.Integration, :search_sub_orders, []},
resolve: {MyApp.Warehouse.Integration, :resolve_sub_order, []},
build_lines: {MyApp.Warehouse.Integration, :build_sub_order_lines, []}
}
]With no source_kinds configured at all, every function in this module
degrades gracefully: search_candidates/1 returns [], resolve/2
returns :error (callers show a plain UUID), and build_lines/3 returns
{:error, :unsupported_kind}. This is what makes the package usable
standalone, with no host "order" concept at all.
build_lines is optional per kind — a kind can be searchable/resolvable
(linkable, shows up in pickers and renders as a link) without being
importable (lines cannot be built from it), by simply omitting the key.
Summary
Types
An {module, function, extra_args} tuple dispatched via apply(m, f, extra_args ++ dynamic_args).
Functions
Builds line-item data for a chosen import candidate — used when a draft
Internal Order imports lines "from" a picked source. Returns
{:error, :unsupported_kind} when the kind isn't registered or doesn't
declare a build_lines callback.
All kinds registered by the host app, in config order.
Resolves an existing %{"kind" => kind, "uuid" => uuid} source ref to a
%{label:, path:} map for rendering a link. Returns :error if the kind
isn't registered, the callback doesn't return the expected shape, or the
callback itself raises — callers fall back to showing the plain UUID.
Searches every registered kind for query and returns the merged
candidate list, each tagged with its kind and label (as
label_prefix) so a picker can group them. A kind whose search
callback raises is skipped (logged), not fatal to the other kinds.
Types
An {module, function, extra_args} tuple dispatched via apply(m, f, extra_args ++ dynamic_args).
Functions
@spec build_lines(String.t(), String.t(), String.t()) :: {:ok, [map()]} | :error | {:error, :unsupported_kind}
Builds line-item data for a chosen import candidate — used when a draft
Internal Order imports lines "from" a picked source. Returns
{:error, :unsupported_kind} when the kind isn't registered or doesn't
declare a build_lines callback.
@spec list_kinds() :: [kind_config()]
All kinds registered by the host app, in config order.
Resolves an existing %{"kind" => kind, "uuid" => uuid} source ref to a
%{label:, path:} map for rendering a link. Returns :error if the kind
isn't registered, the callback doesn't return the expected shape, or the
callback itself raises — callers fall back to showing the plain UUID.
@spec search_candidates(String.t()) :: [ %{ kind: String.t(), label_prefix: String.t(), uuid: String.t(), label: String.t(), extra: term() } ]
Searches every registered kind for query and returns the merged
candidate list, each tagged with its kind and label (as
label_prefix) so a picker can group them. A kind whose search
callback raises is skipped (logged), not fatal to the other kinds.