Files
component-system/lib/components_elixir/inventory/component.ex
2025-09-20 11:52:43 +02:00

120 lines
3.1 KiB
Elixir

defmodule ComponentsElixir.Inventory.Component do
@moduledoc """
Schema for electronic components.
Each component belongs to a category and tracks inventory information
including count, location, and optional image and datasheet.
"""
use Ecto.Schema
import Ecto.Changeset
alias ComponentsElixir.Inventory.{Category, StorageLocation}
schema "components" do
field :name, :string
field :description, :string
field :keywords, :string
field :position, :string
field :legacy_position, :string
field :count, :integer, default: 0
field :datasheet_url, :string
field :datasheet_filename, :string
field :image_filename, :string
belongs_to :category, Category
belongs_to :storage_location, StorageLocation
timestamps(type: :naive_datetime_usec)
end
@doc false
def changeset(component, attrs) do
component
|> cast(attrs, [
:name,
:description,
:keywords,
:position,
:count,
:datasheet_url,
:datasheet_filename,
:image_filename,
:category_id,
:storage_location_id
])
|> validate_required([:name, :category_id])
|> validate_length(:name, min: 1, max: 255)
|> validate_length(:description, max: 2000)
|> validate_length(:keywords, max: 500)
|> validate_length(:position, max: 100)
|> validate_number(:count, greater_than_or_equal_to: 0)
|> validate_url(:datasheet_url)
|> foreign_key_constraint(:category_id)
|> foreign_key_constraint(:storage_location_id)
end
@doc """
Changeset for updating component count.
"""
def count_changeset(component, attrs) do
component
|> cast(attrs, [:count])
|> validate_required([:count])
|> validate_number(:count, greater_than_or_equal_to: 0)
end
@doc """
Changeset for updating component image.
"""
def image_changeset(component, attrs) do
component
|> cast(attrs, [:image_filename])
end
@doc """
Changeset for updating component datasheet.
"""
def datasheet_changeset(component, attrs) do
component
|> cast(attrs, [:datasheet_filename])
end
defp validate_url(changeset, field) do
validate_change(changeset, field, fn ^field, url ->
cond do
is_nil(url) or url == "" -> []
valid_url?(url) -> []
true -> [{field, "must be a valid URL"}]
end
end)
end
defp valid_url?(url) do
case URI.parse(url) do
%URI{scheme: scheme} when scheme in ["http", "https"] -> true
_ -> false
end
end
@doc """
Returns true if the component has an image.
"""
def has_image?(%__MODULE__{image_filename: filename}) when is_binary(filename), do: true
def has_image?(_), do: false
@doc """
Returns true if the component has a datasheet file.
"""
def has_datasheet?(%__MODULE__{datasheet_filename: filename}) when is_binary(filename), do: true
def has_datasheet?(_), do: false
@doc """
Returns the search text for this component.
"""
def search_text(%__MODULE__{} = component) do
[component.name, component.description, component.keywords]
|> Enum.filter(&is_binary/1)
|> Enum.join(" ")
end
end