Display a calculated balance on an address page.

pull/2/head
CJ Bryan and Matt Olenick 7 years ago
parent b231ac62fc
commit 1d7f22d123
  1. 21
      assets/css/components/_address.scss
  2. 1
      assets/css/components/_all.scss
  3. 42
      lib/explorer/forms/address_form.ex
  4. 2
      lib/explorer_web/controllers/address_controller.ex
  5. 10
      lib/explorer_web/templates/address/show.html.eex
  6. 2
      lib/explorer_web/templates/page/index.html.eex
  7. 2
      lib/explorer_web/templates/transaction/show.html.eex
  8. 10
      priv/gettext/default.pot
  9. 10
      priv/gettext/en/LC_MESSAGES/default.po
  10. 72
      test/explorer/forms/address_form_test.exs
  11. 4
      test/support/factories/transaction_factory.ex

@ -0,0 +1,21 @@
.address {
&__header { @extend %section-header; }
&__heading { @extend %section-header__heading; }
&__subheading { @extend %section-header__subheading; }
&__attributes {
@extend %paper;
@include explorer-typography("body1");
padding: explorer-size(-1) explorer-size(1);
}
&__item { @extend %section-list__item; }
&__item-key { @extend %section-list__item-key; }
&__item-value { @extend %section-list__item-value; }
}
@media (min-width: $explorer-breakpoint-lg) {
.address {
&__container {
max-width: explorer-size(6);
}
}
}

@ -1,5 +1,6 @@
@import "section";
@import "address";
@import "block";
@import "blocks";
@import "container";

@ -0,0 +1,42 @@
defmodule Explorer.AddressForm do
@moduledoc false
alias Explorer.Address
alias Explorer.FromAddress
alias Explorer.Repo
alias Explorer.ToAddress
alias Explorer.Transaction
import Ecto.Query
def build(address) do
address
|> Map.merge(%{
balance: address |> calculate_balance,
})
end
def calculate_balance(address) do
Decimal.sub(credits(address), debits(address))
end
def credits(address) do
query = from transaction in Transaction,
join: to_address in ToAddress,
where: to_address.transaction_id == transaction.id,
join: address in Address,
where: to_address.address_id == address.id,
select: sum(transaction.value),
where: address.id == ^address.id
Repo.one(query) || Decimal.new(0)
end
def debits(address) do
query = from transaction in Transaction,
join: from_address in FromAddress,
where: from_address.transaction_id == transaction.id,
join: address in Address,
where: from_address.address_id == address.id,
select: sum(transaction.value),
where: address.id == ^address.id
Repo.one(query) || Decimal.new(0)
end
end

@ -3,12 +3,14 @@ defmodule ExplorerWeb.AddressController do
import Ecto.Query
alias Explorer.Address
alias Explorer.Repo
alias Explorer.AddressForm
def show(conn, params) do
address = Address
|> where(hash: ^params["id"])
|> first
|> Repo.one
|> AddressForm.build
render(conn, "show.html", address: address)
end
end

@ -3,5 +3,15 @@
<h1 class="address__heading"><%= gettext "Address" %></h1>
<h3 class="address__subheading"><%= @address.hash %></h3>
</div>
<div class="address__container">
<div class="address__attributes">
<dl>
<div class="address__item">
<dt class="address__item-key"><%= gettext "Balance" %></dt>
<dd class="address__item-value" title="<%= @address.hash %>"><%= @address.balance %> <%= gettext "POA" %></dd>
</div>
</dl>
</div>
</div>
</section>

@ -50,7 +50,7 @@
</td>
<td class="transactions__column transactions__column--block"><%= transaction.block_number %></td>
<td class="transactions__column transactions__column--age"><%= transaction.age %></td>
<td class="transactions__column transactions__column--value"><%= Decimal.div(Decimal.new(transaction.value), Decimal.new(1_000_000_000_000_000_000)) |> Decimal.to_string(:normal) %> POA</td>
<td class="transactions__column transactions__column--value"><%= Decimal.div(Decimal.new(transaction.value), Decimal.new(1_000_000_000_000_000_000)) |> Decimal.to_string(:normal) %> <%= gettext "POA" %></td>
</tr>
<% end %>
</tbody>

@ -35,7 +35,7 @@
</div>
<div class="transaction__item">
<dt class="transaction__item-key"><%= gettext "Value" %></dt>
<dd class="transaction__item-value"><%= Decimal.div(Decimal.new(@transaction.value), Decimal.new(1_000_000_000_000_000_000)) |> Decimal.to_string(:normal) %> POA</dd>
<dd class="transaction__item-value"><%= Decimal.div(Decimal.new(@transaction.value), Decimal.new(1_000_000_000_000_000_000)) |> Decimal.to_string(:normal) %> <%= gettext "POA" %></dd>
</div>
<div class="transaction__item">
<dt class="transaction__item-key"><%= gettext "From" %></dt>

@ -149,3 +149,13 @@ msgstr ""
#: lib/explorer_web/templates/transaction/show.html.eex:18
msgid "Transaction Status"
msgstr ""
#: lib/explorer_web/templates/address/show.html.eex:10
msgid "Balance"
msgstr ""
#: lib/explorer_web/templates/address/show.html.eex:11
#: lib/explorer_web/templates/page/index.html.eex:53
#: lib/explorer_web/templates/transaction/show.html.eex:38
msgid "POA"
msgstr ""

@ -161,3 +161,13 @@ msgstr "Transaction Hash"
#: lib/explorer_web/templates/transaction/show.html.eex:18
msgid "Transaction Status"
msgstr "Transaction Status"
#: lib/explorer_web/templates/address/show.html.eex:10
msgid "Balance"
msgstr "Balance"
#: lib/explorer_web/templates/address/show.html.eex:11
#: lib/explorer_web/templates/page/index.html.eex:53
#: lib/explorer_web/templates/transaction/show.html.eex:38
msgid "POA"
msgstr "POA"

@ -0,0 +1,72 @@
defmodule Explorer.AddressFormTest do
use Explorer.DataCase
alias Explorer.AddressForm
describe "build/1" do
test "that it has a balance" do
address = insert(:address, %{hash: "bert"})
insert(:transaction, value: 5) |> with_addresses(%{to: "bert", from: "ernie"})
insert(:transaction, value: 5) |> with_addresses(%{to: "bert", from: "kermit"})
assert AddressForm.build(address).balance == Decimal.new(10)
end
end
describe "calculate_balance/1" do
test "when there are more credits than debits it returns a positive value" do
address = insert(:address, %{hash: "bert"})
insert(:transaction, value: 5) |> with_addresses(%{to: "bert", from: "ernie"})
insert(:transaction, value: 5) |> with_addresses(%{to: "bert", from: "kermit"})
assert AddressForm.calculate_balance(address) == Decimal.new(10)
end
test "when credits and debits are equal it returns zero" do
address = insert(:address, %{hash: "bert"})
insert(:transaction, value: 5) |> with_addresses(%{to: "bert", from: "ernie"})
insert(:transaction, value: 5) |> with_addresses(%{to: "ernie", from: "bert"})
assert AddressForm.calculate_balance(address) == Decimal.new(0)
end
test "when there are more debits than credits it returns a negative value" do
address = insert(:address, %{hash: "bert"})
insert(:transaction, value: 5) |> with_addresses(%{to: "ernie", from: "bert"})
insert(:transaction, value: 5) |> with_addresses(%{to: "ernie", from: "bert"})
assert AddressForm.calculate_balance(address) == Decimal.new(-10)
end
end
describe "credits/1" do
test "when there are no transactions" do
address = insert(:address, %{hash: "bert"})
assert AddressForm.credits(address) == Decimal.new(0)
end
test "that it calculates credits" do
address = insert(:address, %{hash: "bert"})
insert(:transaction, value: 5) |> with_addresses(%{from: "ernie", to: "bert"})
insert(:transaction, value: 5) |> with_addresses(%{from: "bert", to: "kermit"})
insert(:transaction, value: 5) |> with_addresses(%{from: "janice", to: "bert"})
assert AddressForm.credits(address) == Decimal.new(10)
end
end
describe "debits/1" do
test "when there are no transactions" do
address = insert(:address, %{hash: "bert"})
assert AddressForm.debits(address) == Decimal.new(0)
end
test "that it calculates debits" do
address = insert(:address, %{hash: "ernie"})
insert(:transaction, value: 5) |> with_addresses(%{from: "ernie", to: "bert"})
insert(:transaction, value: 5) |> with_addresses(%{from: "ernie", to: "kermit"})
insert(:transaction, value: 5) |> with_addresses(%{from: "janice", to: "ernie"})
assert AddressForm.debits(address) == Decimal.new(10)
end
end
end

@ -20,8 +20,8 @@ defmodule Explorer.TransactionFactory do
end
def with_addresses(transaction, %{to: to, from: from} \\ %{to: nil, from: nil}) do
to_address = if to, do: insert(:address, hash: to), else: insert(:address)
from_address = if from, do: insert(:address, hash: from), else: insert(:address)
to_address = if to, do: Explorer.Repo.get_by(Explorer.Address, %{hash: to}) || insert(:address, hash: to), else: insert(:address)
from_address = if from, do: Explorer.Repo.get_by(Explorer.Address, %{hash: from}) ||insert(:address, hash: from), else: insert(:address)
insert(:to_address, %{transaction_id: transaction.id, address_id: to_address.id})
insert(:from_address, %{transaction_id: transaction.id, address_id: from_address.id})
transaction

Loading…
Cancel
Save