PhoenixKitLocations.Locations (PhoenixKitLocations v0.2.1)

Copy Markdown View Source

Context module for managing locations and location types.

Locations and types have a many-to-many relationship via a join table, so a location can be both a "Showroom" and "Storage" at the same time.

Both locations and types use hard-delete only (simple reference data).

Activity logging

Every mutating function accepts opts \ []. When actor_uuid: is present in opts, the mutation is logged via PhoenixKit.Activity.log/1 under the "locations" module key. Logging failures never crash the primary operation — the helper rescues and falls back to Logger.warning.

Usage from IEx

alias PhoenixKitLocations.Locations

# Types
{:ok, showroom} = Locations.create_location_type(%{name: "Showroom"})
{:ok, storage} = Locations.create_location_type(%{name: "Storage"})

# Locations
{:ok, loc} = Locations.create_location(%{name: "HQ", address_line_1: "123 Main St"})

# Assign types
{:ok, _} = Locations.sync_location_types(loc.uuid, [showroom.uuid, storage.uuid])

# Or add/remove individually
{:ok, _} = Locations.add_location_type(loc.uuid, showroom.uuid)
{:ok, _} = Locations.remove_location_type(loc.uuid, storage.uuid)

# Query
Locations.list_locations(type_uuid: showroom.uuid)
Locations.count_locations()
Locations.get_location_by(:name, "HQ")

Summary

Functions

Adds a single type to a location. No-op if already assigned.

Returns an Ecto.Changeset for tracking location changes.

Returns an Ecto.Changeset for tracking location type changes.

Returns the total count of location types.

Returns the total count of locations.

Creates a location type. Required: :name. Optional: :description, :status, :data.

Hard-deletes a location. Cascades to type assignments.

Hard-deletes a location type. Cascades to type assignments (locations keep existing, just lose the link).

Finds locations with the same address_line_1, city, and postal_code.

Fetches a location by UUID with types preloaded. Returns nil if not found.

Fetches a location by a field value. Returns nil if not found.

Fetches a location type by UUID. Returns nil if not found.

Fetches a location type by name (case-sensitive). Returns nil if not found.

Returns true if the location has the given type assigned.

Returns a list of type UUIDs linked to a location.

Returns a list of LocationType structs linked to a location.

Lists all location types, ordered by name.

Lists all locations, ordered by name, with their types preloaded.

Logs a module enable/disable toggle. Called from the enable_system / disable_system module lifecycle functions.

Removes a single type from a location. No-op if not assigned.

Syncs the type assignments for a location (full replace).

Updates a location with the given attributes.

Updates a location type with the given attributes.

Types

list_locations_opts()

@type list_locations_opts() :: [status: String.t(), type_uuid: String.t()]

opts()

@type opts() :: keyword()

status_filter()

@type status_filter() :: [{:status, String.t()}]

Functions

add_location_type(location_uuid, type_uuid, opts \\ [])

@spec add_location_type(String.t(), String.t(), opts()) ::
  {:ok, PhoenixKitLocations.Schemas.LocationTypeAssignment.t()}
  | {:error, Ecto.Changeset.t()}

Adds a single type to a location. No-op if already assigned.

Returns {:ok, assignment} or {:error, changeset}.

change_location(location, attrs \\ %{})

Returns an Ecto.Changeset for tracking location changes.

change_location_type(location_type, attrs \\ %{})

@spec change_location_type(PhoenixKitLocations.Schemas.LocationType.t(), map()) ::
  Ecto.Changeset.t()

Returns an Ecto.Changeset for tracking location type changes.

count_location_types(opts \\ [])

@spec count_location_types(status_filter()) :: non_neg_integer()

Returns the total count of location types.

count_locations(opts \\ [])

@spec count_locations(status_filter()) :: non_neg_integer()

Returns the total count of locations.

create_location(attrs, opts \\ [])

@spec create_location(map(), opts()) ::
  {:ok, PhoenixKitLocations.Schemas.Location.t()} | {:error, Ecto.Changeset.t()}

Creates a location.

Required: :name. Optional: :description, :public_notes, :address_line_1, :address_line_2, :city, :state, :postal_code, :country, :phone, :email, :website, :notes, :status, :features, :data.

create_location_type(attrs, opts \\ [])

@spec create_location_type(map(), opts()) ::
  {:ok, PhoenixKitLocations.Schemas.LocationType.t()}
  | {:error, Ecto.Changeset.t()}

Creates a location type. Required: :name. Optional: :description, :status, :data.

delete_location(location, opts \\ [])

Hard-deletes a location. Cascades to type assignments.

delete_location_type(location_type, opts \\ [])

Hard-deletes a location type. Cascades to type assignments (locations keep existing, just lose the link).

find_similar_addresses(address_line_1, city, postal_code, exclude_uuid \\ nil)

@spec find_similar_addresses(
  String.t() | nil,
  String.t() | nil,
  String.t() | nil,
  String.t() | nil
) :: [map()]

Finds locations with the same address_line_1, city, and postal_code.

Returns a list of matching locations, excluding the given exclude_uuid. Only checks if address_line_1 is non-empty. Returns [] on any error (with the error logged — treated as a soft-fail so the form still saves).

get_location(uuid)

@spec get_location(String.t()) :: PhoenixKitLocations.Schemas.Location.t() | nil

Fetches a location by UUID with types preloaded. Returns nil if not found.

get_location_by(field, value)

@spec get_location_by(:name | :email | :phone, String.t()) ::
  PhoenixKitLocations.Schemas.Location.t() | nil

Fetches a location by a field value. Returns nil if not found.

Only safe field names are accepted — unknown fields raise ArgumentError.

Examples

Locations.get_location_by(:name, "Main Office")
Locations.get_location_by(:email, "hq@example.com")

get_location_type(uuid)

@spec get_location_type(String.t()) ::
  PhoenixKitLocations.Schemas.LocationType.t() | nil

Fetches a location type by UUID. Returns nil if not found.

get_location_type_by_name(name)

@spec get_location_type_by_name(String.t()) ::
  PhoenixKitLocations.Schemas.LocationType.t() | nil

Fetches a location type by name (case-sensitive). Returns nil if not found.

has_type?(location_uuid, type_uuid)

@spec has_type?(String.t(), String.t()) :: boolean()

Returns true if the location has the given type assigned.

linked_type_uuids(location_uuid)

@spec linked_type_uuids(String.t()) :: [String.t()]

Returns a list of type UUIDs linked to a location.

linked_types(location_uuid)

Returns a list of LocationType structs linked to a location.

list_location_types(opts \\ [])

@spec list_location_types(status_filter()) :: [
  PhoenixKitLocations.Schemas.LocationType.t()
]

Lists all location types, ordered by name.

Options

  • :status — filter by status (e.g. "active", "inactive").

list_locations(opts \\ [])

Lists all locations, ordered by name, with their types preloaded.

Options

  • :status — filter by status (e.g. "active", "inactive").
  • :type_uuid — filter to only locations that have this type assigned.

log_module_toggle(state, opts \\ [])

@spec log_module_toggle(:enabled | :disabled, opts()) :: :ok

Logs a module enable/disable toggle. Called from the enable_system / disable_system module lifecycle functions.

remove_location_type(location_uuid, type_uuid, opts \\ [])

@spec remove_location_type(String.t(), String.t(), opts()) :: {:ok, 0 | 1}

Removes a single type from a location. No-op if not assigned.

Returns {:ok, count} where count is 0 or 1.

sync_location_types(location_uuid, type_uuids, opts \\ [])

@spec sync_location_types(String.t(), [String.t()], opts()) ::
  {:ok, :synced | :unchanged} | {:error, :type_assignment_failed}

Syncs the type assignments for a location (full replace).

Replaces all existing assignments with the given list of type UUIDs. Wrapped in a transaction for atomicity — if any insert fails, all changes are rolled back (existing assignments preserved).

Logs location.types_synced only when the assignment set actually changed; a no-op sync is silent.

update_location(location, attrs, opts \\ [])

Updates a location with the given attributes.

update_location_type(location_type, attrs, opts \\ [])

Updates a location type with the given attributes.