lib/repos/Bank/Amount.ex

defmodule SoftBank.Amount do
  @moduledoc """
  An Amount represents the individual debit or credit for a given account and is
  part of a balanced entry.
  """

  use SoftBank.Schema

  import Ecto.Changeset
  import Ecto.Query, only: [from: 2]

  alias SoftBank.Entry
  alias SoftBank.Account

  schema "softbank_amounts" do
    field(:amount, Money.Ecto.Composite.Type)
    field(:type, :string)

    belongs_to(:entry, Entry)
    belongs_to(:account, Account)

    timestamps()
  end

  @params ~w(amount type)a

  @amount_types ["credit", "debit"]

  @doc """
  Creates an amount changeset associated with a `SoftBank.Entry` and `SoftBank.Account`.
  A type ("credit" or "debit"), as well as, an amount greater than 0 must be specified.
  """

  def changeset(model, params \\ %{}) do
    model
    |> cast(params, @params)
    |> Ecto.Changeset.assoc_constraint([:entry, :account])
    |> validate_required([:amount, :type])
    |> validate_amount_type()
    |> validate_inclusion(:type, @amount_types)
  end

  def validate_amount_type(model) do
    type = model.amount.type
    valid = Enum.member?(Money.Currency.known_current_currencies(), type)

    case valid do
      true -> model
      false -> add_error(model, :type, "invalid", additional: "invalid currency type")
    end
  end

  @doc false
  def for_entry(query, entry) do
    from(c in query,
      join: p in assoc(c, :entry),
      where: p.id == ^entry.id
    )
  end

  @doc false
  def for_account(query, account) do
    from(c in query,
      join: p in assoc(c, :account),
      where: p.id == ^account.id
    )
  end

  @doc false
  def sum_type(query, type) do
    from(c in query,
      where: c.type == ^type,
      select: sum(c.amount)
    )
  end

  def select_type(query, type) do
    from(c in query,
      where: c.type == ^type,
      select: c.amount
    )
  end

  @doc false
  def dated(query, %{from_date: from_date, to_date: to_date}) do
    from(c in query,
      join: p in assoc(c, :entry),
      where: p.date >= ^from_date,
      where: p.date <= ^to_date
    )
  end

  @doc false
  def dated(query, %{from_date: from_date}) do
    from(c in query,
      join: p in assoc(c, :entry),
      where: p.date >= ^from_date
    )
  end

  @doc false
  def dated(query, %{to_date: to_date}) do
    from(c in query,
      join: p in assoc(c, :entry),
      where: p.date <= ^to_date
    )
  end

  def note(amount) do
    currency = Application.get_env(:soft_bank, :default_currency)

    if currency do
      new(amount, currency)
    else
      raise ArgumentError,
            "to use Amount.new/1 you must set a default currency in your application config."
    end
  end

  def new(_int, _currency) do
  end
end