Created
June 11, 2016 00:01
-
-
Save boriscy/eb744132c30539c40ab96f172e87b530 to your computer and use it in GitHub Desktop.
Payment
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| defmodule Bonsai.Payment do | |
| use Bonsai.Web, :model | |
| alias Bonsai.{User, Ledger, Repo} | |
| @zero Decimal.new(0) | |
| @primary_key {:id, :binary_id, autogenerate: true} | |
| embedded_schema do | |
| field :amount, :decimal | |
| field :date, Ecto.Date | |
| field :note, :string | |
| field :user_id, :binary_id | |
| field :image, :map | |
| timestamps | |
| end | |
| @required_fields ~w(amount date user_id) | |
| @optional_fields ~w(note) | |
| defp changeset(model, params) do | |
| model | |
| |> cast(params, @required_fields, @optional_fields) | |
| |> validate_required([:amount, :date, :user_id]) | |
| end | |
| @doc """ | |
| Changeset to store Ledger with type = "income" | |
| """ | |
| def changeset(model, params, "income") do | |
| changeset(model, params) | |
| |> validate_number(:amount, greater_than: @zero) | |
| end | |
| @doc """ | |
| Changeset to store Ledger with type = "expense" | |
| """ | |
| def changeset(model, params, "expense") do | |
| changeset(model, params) | |
| |> validate_number(:amount, less_than: @zero) | |
| end | |
| def create(ledger_id, params, user_id) when is_binary(ledger_id) do | |
| case Repo.get_by(Ledger, id: ledger_id, type: params["type"]) do | |
| nil -> | |
| {:error, "invalid ledger"} | |
| ledger -> | |
| create(ledger, Map.merge(params, %{"user_id" => user_id}) ) | |
| end | |
| end | |
| def create(ledger, params) do | |
| if compare_balance(ledger.type, ledger) do | |
| create_payment(ledger, params) | |
| else | |
| {:error, "invalid balance"} | |
| end | |
| end | |
| defp create_payment(ledger, params) do | |
| payment = build_payment(ledger, params) | |
| if payment.valid? do | |
| bal = Decimal.sub(ledger.balance, payment.changes.amount) | |
| Ledger.changeset_payment(ledger, %{balance: bal, info: %{updated_by: params["user_id"]} }) | |
| |> put_embed(:payments, Enum.map(ledger.payments, fn(l) -> change(l) end) ++ [payment] ) | |
| |> Repo.update | |
| else | |
| {:error, payment} | |
| end | |
| end | |
| defp compare_balance("income", ledger) do | |
| Decimal.compare(@zero, ledger.balance) == Decimal.new(-1) | |
| end | |
| defp compare_balance("expense", ledger) do | |
| Decimal.compare(@zero, ledger.balance) == Decimal.new(1) | |
| end | |
| defp build_payment(ledger, %{"type" => "income"} = params) do | |
| Bonsai.Payment.changeset(%Bonsai.Payment{}, params, "income") | |
| |> validate_number(:amount, less_than_or_equal_to: ledger.balance) | |
| end | |
| defp build_payment(ledger, %{"type" => "expense"} = params) do | |
| Bonsai.Payment.changeset(%Bonsai.Payment{}, params, "expense") | |
| |> validate_number(:amount, greater_than_or_equal_to: ledger.balance) | |
| end | |
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| defmodule Bonsai.PaymentTest do | |
| use Bonsai.ModelCase | |
| import Bonsai.Factory | |
| alias Bonsai.{Payment, Ledger} | |
| ################################################## | |
| # Income | |
| @attrs_inc %{amount: "120.5", date: "2010-04-17", ledger_id: "40d8d7a7-9499-4b3c-8a57-024f14e5b895", user_id: "8a4afd36-d842-44d6-a107-e6ddf423f417"} | |
| @invalid_attrs %{} | |
| @user_id "019a0df4-4ee7-4f45-81ac-260450236f21" | |
| test "changeset with valid attributes income" do | |
| changeset = Payment.changeset(%Payment{}, @attrs_inc, "income") | |
| assert changeset.valid? | |
| end | |
| test "valid amount for income" do | |
| changeset = Payment.changeset(%Payment{}, Map.merge(@attrs_inc, %{amount: "-10"}), "income") | |
| refute changeset.valid? | |
| end | |
| test "changeset with invalid attributes income" do | |
| changeset = Payment.changeset(%Payment{}, %{amount: "-10", date: "aaa", ledger_id: ""}, "income") | |
| refute changeset.valid? | |
| refute changeset.errors[:date] == nil | |
| refute changeset.errors[:amount] == nil | |
| end | |
| ################################################## | |
| # Expense | |
| @attrs_exp %{amount: "-120.5", date: "2010-04-17", user_id: "8a4afd36-d842-44d6-a107-e6ddf423f417"} | |
| test "changeset with valid attributes expense" do | |
| changeset = Payment.changeset(%Payment{}, @attrs_exp, "expense") | |
| assert changeset.valid? | |
| end | |
| test "valid amount for expense" do | |
| changeset = Payment.changeset(%Payment{}, Map.merge(@attrs_inc, %{amount: "10"}), "expense") | |
| refute changeset.valid? | |
| end | |
| test "changeset with invalid attributes expense" do | |
| changeset = Payment.changeset(%Payment{}, %{amount: "10", date: "aaa", ledger_id: ""}, "expense") | |
| refute changeset.valid? | |
| refute changeset.errors[:date] == nil | |
| refute changeset.errors[:amount] == nil | |
| end | |
| ############################################################## | |
| # Payments income | |
| def create_organization, do: insert(:organization) | |
| def create_contact, do: insert(:contact, organization_id: create_organization.id) | |
| @income_attrs %{"amount" => "100", "balance" => "100", "date" => "2016-06-01", | |
| "description" => "An Income", "currency" => "USD", "type" => "income"} | |
| @valid_inc_attrs %{"amount" => "10", "date" => to_string(Ecto.Date.utc), "type" => "income" } | |
| def create_income(attrs, user_id \\ @user_id) do | |
| cont = create_contact | |
| Ledger.create(Map.merge(attrs, %{"contact_name" => cont.name, "organization_id" => cont.organization_id }), user_id) | |
| end | |
| test "invalid ledger type" do | |
| {:ok, ledger} = create_income @income_attrs | |
| assert {:error, "invalid ledger"} == Payment.create(ledger.id, Map.merge(@valid_inc_attrs, %{"type" => "invalid type"}), @user_id) | |
| end | |
| test "invalid ledger id" do | |
| assert {:error, "invalid ledger"} == Payment.create(@user_id, Map.merge(@valid_inc_attrs, %{"type" => "invalid type"}), @user_id) | |
| end | |
| test "income invalid ledger balance" do | |
| {:ok, ledger} = create_income Map.merge(@income_attrs, %{"balance" => "0"}) | |
| assert {:error, "invalid balance"} == Payment.create(ledger.id, @valid_inc_attrs, @user_id) | |
| end | |
| test "income invalid amount" do | |
| {:ok, ledger} = create_income Map.merge(@income_attrs, %{"balance" => "2"}) | |
| {:error, cs} = Payment.create(ledger.id, @valid_inc_attrs, @user_id) | |
| assert cs.errors[:amount] | |
| end | |
| test "income payments 1" do | |
| {:ok, ledger} = create_income @income_attrs | |
| assert ledger.payments == [] | |
| {:ok, ledger} = Payment.create(ledger.id, @valid_inc_attrs, @user_id) | |
| payment = List.last ledger.payments | |
| assert ledger.balance == Decimal.new("90") | |
| assert ledger.info.updated_by == @user_id | |
| assert payment.amount == Decimal.new("10") | |
| {:ok, date} = Ecto.Date.cast(@valid_inc_attrs["date"]) | |
| assert payment.date == date | |
| assert payment.user_id == @user_id | |
| attrs = Map.merge(@valid_inc_attrs, %{"amount" => 30}) | |
| {:ok, ledger} = Payment.create(ledger.id, attrs, @user_id) | |
| assert ledger.balance == Decimal.new("60") | |
| payment = List.last ledger.payments | |
| assert payment.amount == Decimal.new("30") | |
| end | |
| test "invalid amount" do | |
| {:ok, ledger} = create_income @income_attrs | |
| attrs = Map.merge(@valid_inc_attrs, %{"amount" => "150"}) | |
| {:error, payment} = Payment.create(ledger.id, attrs, @user_id) | |
| assert payment.errors[:amount] == {"must be less than or equal to %{number}", [number: Decimal.new("100")]} | |
| end | |
| test "invalid balance" do | |
| {:ok, ledger} = create_income Map.merge(@income_attrs, %{"balance" => 0}) | |
| assert ledger.balance == Decimal.new(0) | |
| {:error, "invalid balance"} = Payment.create(ledger.id, @valid_inc_attrs, @user_id) | |
| end | |
| ############################################################## | |
| # Payments expense | |
| @expense_attrs %{"amount" => "-100", "balance" => "-100", "date" => "2016-06-01", | |
| "description" => "An Expense", "currency" => "USD", "type" => "expense"} | |
| @valid_exp_attrs %{"amount" => -10, "date" => to_string(Ecto.Date.utc), "type" => "expense"} | |
| def create_expense(attrs, user_id \\ @user_id) do | |
| cont = create_contact | |
| Ledger.create(Map.merge(attrs, %{"contact_name" => cont.name, "organization_id" => cont.organization_id }), user_id) | |
| end | |
| test "expense payments" do | |
| {:ok, ledger} = create_expense @expense_attrs | |
| assert ledger.payments == [] | |
| {:ok, ledger} = Payment.create(ledger.id, @valid_exp_attrs, @user_id) | |
| payment = List.last ledger.payments | |
| assert ledger.balance == Decimal.new(-90) | |
| assert ledger.info.updated_by == @user_id | |
| assert payment.amount == Decimal.new(-10) | |
| {:ok, date} = Ecto.Date.cast(@valid_exp_attrs["date"]) | |
| assert payment.date == date | |
| assert payment.user_id == @user_id | |
| attrs = Map.merge(@valid_exp_attrs, %{"amount" => -30, "type" => "expense", "ledger_id" => ledger.id }) | |
| {:ok, ledger} = Payment.create(ledger.id, attrs, @user_id) | |
| assert ledger.balance == Decimal.new(-60) | |
| payment = List.last ledger.payments | |
| assert payment.amount == Decimal.new(-30) | |
| end | |
| test "invalid amount expense" do | |
| {:ok, ledger} = create_expense @expense_attrs | |
| attrs = Map.merge(@valid_exp_attrs, %{"amount" => "-150"}) | |
| {:error, payment} = Payment.create(ledger.id, attrs, @user_id) | |
| assert payment.errors[:amount] == {"must be greater than or equal to %{number}", [number: Decimal.new("-100")]} | |
| end | |
| test "invalid balance expense" do | |
| {:ok, ledger} = create_expense Map.merge(@expense_attrs, %{"balance" => 0}) | |
| assert ledger.balance == Decimal.new(0) | |
| {:error, "invalid balance"} = Payment.create(ledger.id, @valid_exp_attrs, @user_id) | |
| end | |
| test "invalid ledger" do | |
| {:ok, ledger} = create_income @income_attrs | |
| assert ledger.type == "income" | |
| attrs = Map.merge(@valid_inc_attrs, %{"type" => "expense"}) | |
| {:error, "invalid ledger"} = Payment.create(ledger.id, attrs, @user_id) | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment