From d033cf34e25ddc4851ec484b6a49b45756582199 Mon Sep 17 00:00:00 2001 From: Enzo Date: Fri, 17 Jan 2025 21:08:08 +0100 Subject: [PATCH] chore(balance): make balance error an embedded schema --- apps/app/lib/app/balance.ex | 16 ++---- apps/app/lib/app/balance/balance_error.ex | 36 +++++++++++++ apps/app/test/app/balance_test.exs | 5 +- .../app_web/live/books/book_balance_live.ex | 54 ++++++++++--------- 4 files changed, 72 insertions(+), 39 deletions(-) create mode 100644 apps/app/lib/app/balance/balance_error.ex diff --git a/apps/app/lib/app/balance.ex b/apps/app/lib/app/balance.ex index 39e8c70b..6f0dbea1 100644 --- a/apps/app/lib/app/balance.ex +++ b/apps/app/lib/app/balance.ex @@ -7,12 +7,11 @@ defmodule App.Balance do alias App.Repo + alias App.Balance.BalanceError alias App.Books.BookMember alias App.Transfers alias App.Transfers.Peer - @type error_reasons :: [%{uniq_hash: String.t(), kind: atom(), extra: map(), private: map()}] - @doc """ Compute the `:balance` field of book members. """ @@ -116,14 +115,9 @@ defmodule App.Balance do defp maybe_set_weight_by_income_total_weight(transfer, peers_without_revenues) do error_reasons = Enum.map(peers_without_revenues, fn peer -> - %{ - uniq_hash: "revenues_missing_#{peer.member_id}", - kind: :revenues_missing, - extra: %{ - member_id: peer.member_id - }, - private: %{} - } + BalanceError.new(:revenues_missing, %{ + member_id: peer.member_id + }) end) {:error, error_reasons, transfer} @@ -282,7 +276,7 @@ defmodule App.Balance do The total sum of balanced money must be equal to 0, otherwise the function will crash. """ - @spec transactions([BookMember.t()]) :: {:ok, [transaction()]} | {:error, error_reasons()} + @spec transactions([BookMember.t()]) :: {:ok, [transaction()]} | {:error, [BalanceError.t()]} def transactions(members) do error_reasons = Enum.find_value(members, fn member -> diff --git a/apps/app/lib/app/balance/balance_error.ex b/apps/app/lib/app/balance/balance_error.ex new file mode 100644 index 00000000..48278769 --- /dev/null +++ b/apps/app/lib/app/balance/balance_error.ex @@ -0,0 +1,36 @@ +defmodule App.Balance.BalanceError do + @moduledoc """ + Balance errors are instantiated when the balance of a book cannot be computed, + and are used to store the kind of error along with some extra information. + """ + use Ecto.Schema + + @type t :: %__MODULE__{ + kind: atom(), + extra: map(), + uniq_hash: String.t(), + private: map() + } + + @primary_key false + embedded_schema do + field :kind, Ecto.Enum, values: [:missing_revenues] + field :extra, :map + + field :uniq_hash, :string, virtual: true + field :private, :map, virtual: true, default: %{} + end + + @spec new(kind :: atom(), extra :: map()) :: t() + def new(kind, extra) when is_atom(kind) and is_map(extra) do + %__MODULE__{ + kind: kind, + extra: extra, + uniq_hash: uniq_hash(kind, extra) + } + end + + defp uniq_hash(:revenues_missing, extra) do + "revenues_missing_#{extra.member_id}" + end +end diff --git a/apps/app/test/app/balance_test.exs b/apps/app/test/app/balance_test.exs index 681e6f23..f2d4a5d0 100644 --- a/apps/app/test/app/balance_test.exs +++ b/apps/app/test/app/balance_test.exs @@ -7,6 +7,7 @@ defmodule App.BalanceTest do import App.TransfersFixtures alias App.Balance + alias App.Balance.BalanceError describe "fill_members_balance/1" do setup do @@ -299,7 +300,7 @@ defmodule App.BalanceTest do expected_hash = "revenues_missing_#{member1_id}" assert member1.balance_errors == [ - %{ + %BalanceError{ kind: :revenues_missing, uniq_hash: expected_hash, extra: %{member_id: member1_id}, @@ -308,7 +309,7 @@ defmodule App.BalanceTest do ] assert member2.balance_errors == [ - %{ + %BalanceError{ kind: :revenues_missing, uniq_hash: expected_hash, extra: %{member_id: member1_id}, diff --git a/apps/app_web/lib/app_web/live/books/book_balance_live.ex b/apps/app_web/lib/app_web/live/books/book_balance_live.ex index 6d1eea02..cb3625e9 100644 --- a/apps/app_web/lib/app_web/live/books/book_balance_live.ex +++ b/apps/app_web/lib/app_web/live/books/book_balance_live.ex @@ -4,6 +4,7 @@ defmodule AppWeb.BookBalanceLive do import AppWeb.BooksComponents, only: [balance_card: 1] alias App.Balance + alias App.Balance.BalanceError alias App.Books.Book alias App.Books.BookMember alias App.Books.Members @@ -33,15 +34,15 @@ defmodule AppWeb.BookBalanceLive do - <%= if @transaction_errors != nil do %> + <%= if @balance_errors != nil do %>
<.alert kind={:error}> {gettext("Some information is missing to balance the book")} - <.transaction_error_tile - :for={transaction_error <- @transaction_errors} + <.balance_error_tile + :for={balance_error <- @balance_errors} book={@book} - {transaction_error} + balance_error={balance_error} />
<% else %> @@ -75,24 +76,25 @@ defmodule AppWeb.BookBalanceLive do end attr :book, Book, required: true - attr :kind, :atom, required: true - attr :extra, :map, required: true - attr :private, :map, required: true - - defp transaction_error_tile(%{kind: :revenues_missing} = assigns) do - ~H""" - <.link navigate={~p"/books/#{@book}/members/#{@extra.member_id}"} class="block"> - <.tile class="justify-between"> -
- {@private.member_nickname} - did not set their revenues. -
- <.button kind={:ghost}> - {gettext("Fix it")} <.icon name={:chevron_right} /> - - - - """ + attr :balance_error, BalanceError, required: true + + defp balance_error_tile(assigns) do + case assigns.balance_error.kind do + :revenues_missing -> + ~H""" + <.link navigate={~p"/books/#{@book}/members/#{@balance_error.extra.member_id}"} class="block"> + <.tile class="justify-between"> +
+ {@balance_error.private.member_nickname} + did not set their revenues. +
+ <.button kind={:ghost}> + {gettext("Fix it")} <.icon name={:chevron_right} /> + + + + """ + end end # Highlight the nickname of the current member @@ -131,18 +133,18 @@ defmodule AppWeb.BookBalanceLive do ) |> assign_transactions(members) - {:ok, socket, temporary_assigns: [transaction_errors: []]} + {:ok, socket, temporary_assigns: [balance_errors: []]} end defp assign_transactions(socket, members) do case Balance.transactions(members) do {:ok, transactions} -> socket - |> assign(transaction_errors: nil) + |> assign(balance_errors: nil) |> stream(:transactions, transactions) - {:error, reasons} -> - assign(socket, transaction_errors: reasons) + {:error, balance_errors} -> + assign(socket, balance_errors: balance_errors) end end end