refactor(elixir): remove unused is_active field

from storage location
This commit is contained in:
Schuwi
2025-09-17 23:13:45 +02:00
parent 6a1122c3be
commit 5a1775e836
4 changed files with 286 additions and 149 deletions

View File

@@ -15,14 +15,16 @@ defmodule ComponentsElixir.Inventory do
"""
def list_storage_locations do
# Get all locations with preloaded parents in a single query
locations = StorageLocation
|> order_by([sl], [asc: sl.name])
|> preload(:parent)
|> Repo.all()
locations =
StorageLocation
|> order_by([sl], asc: sl.name)
|> preload(:parent)
|> Repo.all()
# Compute hierarchy fields for all locations efficiently
processed_locations = compute_hierarchy_fields_batch(locations)
|> Enum.sort_by(&{&1.level, &1.name})
processed_locations =
compute_hierarchy_fields_batch(locations)
|> Enum.sort_by(&{&1.level, &1.name})
# Ensure AprilTag SVGs exist for all locations
spawn(fn ->
@@ -46,24 +48,35 @@ defmodule ComponentsElixir.Inventory do
end
defp compute_level_efficient(%{parent_id: nil}, _location_map, _depth), do: 0
defp compute_level_efficient(%{parent_id: parent_id}, location_map, depth) when depth < 10 do
case Map.get(location_map, parent_id) do
nil -> 0 # Orphaned record
# Orphaned record
nil -> 0
parent -> 1 + compute_level_efficient(parent, location_map, depth + 1)
end
end
defp compute_level_efficient(_location, _location_map, _depth), do: 0 # Prevent infinite recursion
# Prevent infinite recursion
defp compute_level_efficient(_location, _location_map, _depth), do: 0
defp compute_path_efficient(%{parent_id: nil, name: name}, _location_map, _depth), do: name
defp compute_path_efficient(%{parent_id: parent_id, name: name}, location_map, depth) when depth < 10 do
defp compute_path_efficient(%{parent_id: parent_id, name: name}, location_map, depth)
when depth < 10 do
case Map.get(location_map, parent_id) do
nil -> name # Orphaned record
# Orphaned record
nil ->
name
parent ->
parent_path = compute_path_efficient(parent, location_map, depth + 1)
"#{parent_path}/#{name}"
end
end
defp compute_path_efficient(%{name: name}, _location_map, _depth), do: name # Prevent infinite recursion
# Prevent infinite recursion
defp compute_path_efficient(%{name: name}, _location_map, _depth), do: name
@doc """
Returns the list of root storage locations (no parent).
@@ -71,7 +84,7 @@ defmodule ComponentsElixir.Inventory do
def list_root_storage_locations do
StorageLocation
|> where([sl], is_nil(sl.parent_id))
|> order_by([sl], [asc: sl.name])
|> order_by([sl], asc: sl.name)
|> Repo.all()
end
@@ -79,9 +92,10 @@ defmodule ComponentsElixir.Inventory do
Gets a single storage location with computed hierarchy fields.
"""
def get_storage_location!(id) do
location = StorageLocation
|> preload(:parent)
|> Repo.get!(id)
location =
StorageLocation
|> preload(:parent)
|> Repo.get!(id)
# Compute hierarchy fields
level = compute_level_for_single(location)
@@ -91,6 +105,7 @@ defmodule ComponentsElixir.Inventory do
# Simple computation for single location (allows DB queries)
defp compute_level_for_single(%{parent_id: nil}), do: 0
defp compute_level_for_single(%{parent_id: parent_id}) do
case Repo.get(StorageLocation, parent_id) do
nil -> 0
@@ -99,6 +114,7 @@ defmodule ComponentsElixir.Inventory do
end
defp compute_path_for_single(%{parent_id: nil, name: name}), do: name
defp compute_path_for_single(%{parent_id: parent_id, name: name}) do
case Repo.get(StorageLocation, parent_id) do
nil -> name
@@ -115,7 +131,9 @@ defmodule ComponentsElixir.Inventory do
|> preload(:parent)
|> Repo.one()
|> case do
nil -> nil
nil ->
nil
location ->
level = compute_level_for_single(location)
path = compute_path_for_single(location)
@@ -130,13 +148,15 @@ defmodule ComponentsElixir.Inventory do
# Convert string keys to atoms to maintain consistency
attrs = normalize_string_keys(attrs)
result = %StorageLocation{}
|> StorageLocation.changeset(attrs)
|> Repo.insert()
result =
%StorageLocation{}
|> StorageLocation.changeset(attrs)
|> Repo.insert()
case result do
{:ok, location} ->
{:ok, location}
error ->
error
end
@@ -149,13 +169,15 @@ defmodule ComponentsElixir.Inventory do
# Convert string keys to atoms to maintain consistency
attrs = normalize_string_keys(attrs)
result = storage_location
|> StorageLocation.changeset(attrs)
|> Repo.update()
result =
storage_location
|> StorageLocation.changeset(attrs)
|> Repo.update()
case result do
{:ok, updated_location} ->
{:ok, updated_location}
error ->
error
end
@@ -182,12 +204,14 @@ defmodule ComponentsElixir.Inventory do
case get_storage_location_by_apriltag_id(apriltag_id) do
nil ->
{:error, :not_found}
location ->
{:ok, %{
type: :storage_location,
location: location,
apriltag_id: apriltag_id
}}
{:ok,
%{
type: :storage_location,
location: location,
apriltag_id: apriltag_id
}}
end
end
@@ -195,6 +219,7 @@ defmodule ComponentsElixir.Inventory do
Computes the path for a storage location (for display purposes).
"""
def compute_storage_location_path(nil), do: nil
def compute_storage_location_path(%StorageLocation{} = location) do
compute_path_for_single(location)
end
@@ -205,6 +230,7 @@ defmodule ComponentsElixir.Inventory do
{key, value}, acc when is_binary(key) ->
atom_key = String.to_atom(key)
Map.put(acc, atom_key, value)
{key, value}, acc ->
Map.put(acc, key, value)
end)
@@ -281,28 +307,33 @@ defmodule ComponentsElixir.Inventory do
{:search, search_term}, query when is_binary(search_term) and search_term != "" ->
search_pattern = "%#{search_term}%"
where(query, [c],
where(
query,
[c],
ilike(c.name, ^search_pattern) or
ilike(c.description, ^search_pattern) or
ilike(c.keywords, ^search_pattern) or
ilike(c.position, ^search_pattern)
ilike(c.description, ^search_pattern) or
ilike(c.keywords, ^search_pattern) or
ilike(c.position, ^search_pattern)
)
_, query -> query
_, query ->
query
end)
end
defp apply_component_sorting(query, opts) do
case Keyword.get(opts, :sort_criteria, "name_asc") do
"name_asc" -> order_by(query, [c], [asc: c.name])
"name_desc" -> order_by(query, [c], [desc: c.name])
"inserted_at_asc" -> order_by(query, [c], [asc: c.inserted_at])
"inserted_at_desc" -> order_by(query, [c], [desc: c.inserted_at])
"updated_at_asc" -> order_by(query, [c], [asc: c.updated_at])
"updated_at_desc" -> order_by(query, [c], [desc: c.updated_at])
"count_asc" -> order_by(query, [c], [asc: c.count])
"count_desc" -> order_by(query, [c], [desc: c.count])
_ -> order_by(query, [c], [asc: c.name]) # Default fallback
"name_asc" -> order_by(query, [c], asc: c.name)
"name_desc" -> order_by(query, [c], desc: c.name)
"inserted_at_asc" -> order_by(query, [c], asc: c.inserted_at)
"inserted_at_desc" -> order_by(query, [c], desc: c.inserted_at)
"updated_at_asc" -> order_by(query, [c], asc: c.updated_at)
"updated_at_desc" -> order_by(query, [c], desc: c.updated_at)
"count_asc" -> order_by(query, [c], asc: c.count)
"count_desc" -> order_by(query, [c], desc: c.count)
# Default fallback
_ -> order_by(query, [c], asc: c.name)
end
end
@@ -353,10 +384,12 @@ defmodule ComponentsElixir.Inventory do
def get_inventory_stats do
total_components = Repo.aggregate(Component, :count, :id)
total_stock = Component
total_stock =
Component
|> Repo.aggregate(:sum, :count)
categories_with_components = Component
categories_with_components =
Component
|> distinct([c], c.category_id)
|> Repo.aggregate(:count, :category_id)
@@ -406,6 +439,7 @@ defmodule ComponentsElixir.Inventory do
"""
def decrement_component_count(%Component{} = component) do
new_count = max(0, component.count - 1)
component
|> Component.changeset(%{count: new_count})
|> Repo.update()