lib/openagents/settlement/claim.ex

58e6347eeb72 · 2 KB

defmodule OpenAgents.Settlement.Claim do
  @moduledoc """
  One claimant's pinned commitment to a priced specification.

  The claim carries the fingerprint it agreed to and the self-custodial
  destination that a payment may reach. Only one claim per specification is
  live at a time; expired and rejected claims release the specification.
  """

  use Ecto.Schema
  import Ecto.Changeset

  alias OpenAgents.Settlement.BountySpec

  @primary_key {:id, :binary_id, autogenerate: true}
  @timestamps_opts [type: :utc_datetime_usec]

  @states ~w(claimed verified rejected paid expired disputed refunded)

  @fields ~w(bounty_spec_id spec_fingerprint claimant_ref work_job_ref destination_kind
             destination destination_digest state claim_digest expires_at)a

  schema "settlement_claims" do
    field :spec_fingerprint, :string
    field :claimant_ref, :string
    field :work_job_ref, :string
    field :destination_kind, :string
    field :destination, :string
    field :destination_digest, :string
    field :state, :string
    field :claim_digest, :string
    field :expires_at, :utc_datetime_usec
    belongs_to :bounty_spec, BountySpec, type: :binary_id
    timestamps()
  end

  @type t :: %__MODULE__{}

  @doc "Every claim state."
  def states, do: @states

  @doc "The states that hold a specification against a new claim."
  def live_states, do: @states -- ~w(expired rejected)

  def changeset(record, attributes) do
    record
    |> cast(attributes, @fields)
    |> validate_required(@fields)
    |> validate_inclusion(:state, @states)
    |> validate_format(:spec_fingerprint, ~r/\A[0-9a-f]{64}\z/)
    |> validate_format(:destination_digest, ~r/\A[0-9a-f]{64}\z/)
    |> validate_length(:claimant_ref, min: 1, max: 256)
    |> validate_length(:work_job_ref, min: 1, max: 256)
    |> validate_length(:destination, min: 1, max: 2048)
    |> unique_constraint(:bounty_spec_id, name: :settlement_claim_single_live_claim)
    |> foreign_key_constraint(:bounty_spec_id)
  end

  def state_changeset(record, state) when state in @states do
    record
    |> change(state: state)
    |> unique_constraint(:bounty_spec_id, name: :settlement_claim_single_live_claim)
  end
end