defmodule SoftBank.Transfer do
use SoftBank.Schema
import Ecto.Query
import Ecto.Changeset
alias SoftBank.Amount
alias SoftBank.Account
alias SoftBank.Transfer
alias SoftBank.Entry
alias SoftBank.Repo
@moduledoc """
Transfer from one account to another.
"""
embedded_schema do
field(:amount, Money.Ecto.Composite.Type)
field(:message, :integer)
field(:description, :string)
embeds_one(:sender, Account)
embeds_one(:recipient, Account)
end
def changeset(from_account, struct, to_account \\ %{}, params \\ %{}) do
struct
|> cast(params, [:message, :description, :amount])
|> put_embed(:sender, from_account)
|> put_destination_customer(to_account)
end
defp put_destination_customer(%{valid?: false} = changeset, _), do: changeset
defp put_destination_customer(changeset, to) do
account_number = to.account_number
applied_changeset = apply_changes(changeset)
if account_number == applied_changeset.sender.account_number do
add_error(changeset, :recipient, "cannot transfer to the same account")
else
config = SoftBank.Config.new()
case Repo.one(config, from(a in Account, where: a.account_number == ^account_number)) do
%Account{} = account ->
put_embed(changeset, :recipient, account)
nil ->
add_error(changeset, :recipient, "is invalid")
end
end
end
def send(from_account_struct, to_account_struct, params) do
changeset = changeset(from_account_struct, %Transfer{}, to_account_struct, params)
if changeset.valid? do
transfer = apply_changes(changeset)
source_account = transfer.sender
destination_account = transfer.recipient
amount = transfer.amount
{_, account_balance} = Account.balance(Repo, source_account, nil)
sum_amt = Money.sub!(account_balance, amount)
zero_amt = Money.new(:USD, 0)
case Money.compare(sum_amt, zero_amt) == :gt do
true ->
entry_changeset = %Entry{
description:
"Transfer : " <>
to_string(amount.amount) <>
" from " <>
source_account.account_number <> " to " <> destination_account.account_number,
date: DateTime.utc_now(),
amounts: [
%Amount{
amount: amount,
type: "credit",
account_id: destination_account.id
},
%Amount{
amount: amount,
type: "debit",
account_id: source_account.id
}
]
}
config = SoftBank.Config.new()
Repo.insert(config, entry_changeset)
{:ok, transfer}
false ->
changeset = add_error(changeset, :message, "insufficient funds")
{:error, changeset}
end
else
{:error, changeset}
end
end
end