defmodule Mv.Membership.CustomFieldValue do @moduledoc """ Ash resource representing a custom field value for a member. ## Overview CustomFieldValues implement the Entity-Attribute-Value (EAV) pattern, allowing dynamic custom fields to be attached to members. Each custom field value links a member to a custom field and stores the actual value. ## Value Storage Values are stored using Ash's union type with JSONB storage format: ```json { "type": "string", "value": "example" } ``` ## Supported Types - `:string` - Text data - `:integer` - Numeric data - `:boolean` - True/false flags - `:date` - Date values - `:email` - Validated email addresses (custom type) ## Relationships - `belongs_to :member` - The member this custom field value belongs to (CASCADE delete) - `belongs_to :custom_field` - The custom field definition ## Constraints - Each member can have only one custom field value per custom field (unique composite index) - Custom field values are deleted when the associated member is deleted (CASCADE) """ use Ash.Resource, domain: Mv.Membership, data_layer: AshPostgres.DataLayer postgres do table "custom_field_values" repo Mv.Repo references do reference :member, on_delete: :delete end end actions do defaults [:create, :read, :update, :destroy] default_accept [:value, :member_id, :custom_field_id] end attributes do uuid_primary_key :id attribute :value, :union, constraints: [ storage: :type_and_value, types: [ boolean: [type: :boolean], date: [type: :date], integer: [type: :integer], string: [type: :string], email: [type: Mv.Membership.Email] ] ] end relationships do belongs_to :member, Mv.Membership.Member belongs_to :custom_field, Mv.Membership.CustomField end calculations do calculate :value_to_string, :string, expr(value[:value] <> "") end # Ensure a member can only have one custom field value per custom field # For example: A member can have only one "phone" custom field value, one "email" custom field value, etc. identities do identity :unique_custom_field_per_member, [:member_id, :custom_field_id] end end