lib/plaid/investments.ex

defmodule Plaid.Investments do
  @moduledoc """
  [Plaid Investments APIs](https://plaid.com/docs/api/products/#investments)
  """

  alias Plaid.Castable

  defmodule GetHoldingsResponse do
    @moduledoc """
    [Plaid API /investments/holdings/get response schema.](https://plaid.com/docs/api/products/#investmentsholdingsget)
    """

    @behaviour Castable

    alias Plaid.Account
    alias Plaid.Investments.{Holding, Security}
    alias Plaid.Item

    @type t :: %__MODULE__{
            accounts: [Account.t()],
            holdings: [Holding.t()],
            securities: [Security.t()],
            item: Item.t(),
            request_id: String.t()
          }

    defstruct [
      :accounts,
      :holdings,
      :securities,
      :item,
      :request_id
    ]

    @impl true
    def cast(generic_map) do
      %__MODULE__{
        accounts: Castable.cast_list(Account, generic_map["accounts"]),
        holdings: Castable.cast_list(Holding, generic_map["holdings"]),
        securities: Castable.cast_list(Security, generic_map["securities"]),
        item: Castable.cast(Item, generic_map["item"]),
        request_id: generic_map["request_id"]
      }
    end
  end

  @doc """
  Get user-authorized stock position data for investment-type accounts.

  Does a `POST /investments/holdings/get` call to retrieve
  invesment holdings associated with an access_token's item.

  Params:
  * `access_token` - Token to fetch investment holdings for.

  Options:
  * `:account_ids` - Specific account ids to fetch investment holdings for.

  ## Examples

      Investments.get_holdings("access-sandbox-123xxx", client_id: "123", secret: "abc")
      {:ok, %Investments.GetHoldingsResponse{}}

  """
  @spec get_holdings(String.t(), options, Plaid.config()) ::
          {:ok, GetHoldingsResponse.t()} | {:error, Plaid.Error.t()}
        when options: %{optional(:account_ids) => [String.t()]}
  def get_holdings(access_token, options \\ %{}, config) do
    options_payload = Map.take(options, [:account_ids])

    payload = %{access_token: access_token, options: options_payload}

    Plaid.Client.call(
      "/investments/holdings/get",
      payload,
      GetHoldingsResponse,
      config
    )
  end

  defmodule GetTransactionsResponse do
    @moduledoc """
    [Plaid API /investments/transactions/get response schema.](https://plaid.com/docs/api/products/#investmentstransactionsget)
    """

    @behaviour Castable

    alias Plaid.Account
    alias Plaid.Investments.{Security, Transaction}
    alias Plaid.Item

    @type t :: %__MODULE__{
            item: Item.t(),
            accounts: [Account.t()],
            securities: [Security.t()],
            investment_transactions: [Transaction.t()],
            total_investment_transactions: integer(),
            request_id: String.t()
          }

    defstruct [
      :item,
      :accounts,
      :securities,
      :investment_transactions,
      :total_investment_transactions,
      :request_id
    ]

    @impl true
    def cast(generic_map) do
      %__MODULE__{
        item: Castable.cast(Item, generic_map["item"]),
        accounts: Castable.cast_list(Account, generic_map["accounts"]),
        securities: Castable.cast_list(Security, generic_map["securities"]),
        investment_transactions:
          Castable.cast_list(Transaction, generic_map["investment_transactions"]),
        total_investment_transactions: generic_map["total_investment_transactions"],
        request_id: generic_map["request_id"]
      }
    end
  end

  @doc """
  Get information about all available investment transactions.

  Does a `POST /investments/transactions/get` call which gives you high level
  account data along with investment transactions and associated securities
  from all investment accounts contained in the access_token's item.

  Params:
  * `access_token` - Token to fetch investment holdings for.
  * `start_date` - Start of query for investment transactions.
  * `end_date`  - End of query for investment transactions.

  Options:
  * `:account_ids` - Specific account ids to fetch investment holdings for.
  * `:count` - Amount of investment transactions to pull (optional).
  * `:offset` - Offset to start pulling investment transactions (optional).

  ## Examples

      Investments.get_transactions("access-sandbox-123xxx", "2020-01-01", "2020-01-31", client_id: "123", secret: "abc")
      {:ok, %Investments.GetTransactionsResponse{}}

  """
  @spec get_transactions(String.t(), String.t(), String.t(), options, Plaid.config()) ::
          {:ok, GetTransactionsResponse.t()} | {:error, Plaid.Error.t()}
        when options: %{
               optional(:account_ids) => [String.t()],
               optional(:count) => integer(),
               optional(:offset) => integer()
             }
  def get_transactions(access_token, start_date, end_date, options \\ %{}, config) do
    options_payload = Map.take(options, [:account_ids, :count, :offset])

    payload = %{
      access_token: access_token,
      start_date: start_date,
      end_date: end_date,
      options: options_payload
    }

    Plaid.Client.call(
      "/investments/transactions/get",
      payload,
      GetTransactionsResponse,
      config
    )
  end
end