From a3626b7e4c935e3a893127dce88dbdfd71ddc11a Mon Sep 17 00:00:00 2001 From: Tristan Peralta Date: Mon, 27 Jul 2020 02:16:15 +0800 Subject: [PATCH] Initial commit --- .formatter.exs | 4 ++++ .gitignore | 24 ++++++++++++++++++++++++ README.md | 21 +++++++++++++++++++++ lib/account.ex | 13 +++++++++++++ lib/amount.ex | 13 +++++++++++++ lib/entry.ex | 23 +++++++++++++++++++++++ lib/exledger.ex | 4 ++++ lib/ledger.ex | 27 +++++++++++++++++++++++++++ lib/transaction.ex | 15 +++++++++++++++ mix.exs | 28 ++++++++++++++++++++++++++++ test/exledger_test.exs | 22 ++++++++++++++++++++++ test/test_helper.exs | 1 + 12 files changed, 195 insertions(+) create mode 100644 .formatter.exs create mode 100644 .gitignore create mode 100644 README.md create mode 100644 lib/account.ex create mode 100644 lib/amount.ex create mode 100644 lib/entry.ex create mode 100644 lib/exledger.ex create mode 100644 lib/ledger.ex create mode 100644 lib/transaction.ex create mode 100644 mix.exs create mode 100644 test/exledger_test.exs create mode 100644 test/test_helper.exs diff --git a/.formatter.exs b/.formatter.exs new file mode 100644 index 0000000..d2cda26 --- /dev/null +++ b/.formatter.exs @@ -0,0 +1,4 @@ +# Used by "mix format" +[ + inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] +] diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e35e0c --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# The directory Mix will write compiled artifacts to. +/_build/ + +# If you run "mix test --cover", coverage assets end up here. +/cover/ + +# The directory Mix downloads your dependencies sources to. +/deps/ + +# Where third-party dependencies like ExDoc output generated docs. +/doc/ + +# Ignore .fetch files in case you like to edit your project deps locally. +/.fetch + +# If the VM crashes, it generates a dump, let's ignore it too. +erl_crash.dump + +# Also ignore archive artifacts (built via "mix archive.build"). +*.ez + +# Ignore package tarball (built via "mix hex.build"). +exledger-*.tar + diff --git a/README.md b/README.md new file mode 100644 index 0000000..eb495de --- /dev/null +++ b/README.md @@ -0,0 +1,21 @@ +# Exledger + +**TODO: Add description** + +## Installation + +If [available in Hex](https://hex.pm/docs/publish), the package can be installed +by adding `exledger` to your list of dependencies in `mix.exs`: + +```elixir +def deps do + [ + {:exledger, "~> 0.1.0"} + ] +end +``` + +Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) +and published on [HexDocs](https://hexdocs.pm). Once published, the docs can +be found at [https://hexdocs.pm/exledger](https://hexdocs.pm/exledger). + diff --git a/lib/account.ex b/lib/account.ex new file mode 100644 index 0000000..8f6c8a8 --- /dev/null +++ b/lib/account.ex @@ -0,0 +1,13 @@ +defmodule ExLedger.Account do + + defstruct ~w[name sub_accounts]a + + @type t :: %__MODULE__{ + name: String.t(), + sub_accounts: [__MODULE__.t()] + } + + def new(attrs) do + struct!(%__MODULE__{}, attrs) + end +end diff --git a/lib/amount.ex b/lib/amount.ex new file mode 100644 index 0000000..5d9fdb8 --- /dev/null +++ b/lib/amount.ex @@ -0,0 +1,13 @@ +defmodule ExLedger.Amount do + + defstruct ~w[quantity currency]a + + @type t :: %__MODULE__{ + quantity: integer(), + currency: tuple() | String.t() + } + + def new(attrs) do + struct!(%__MODULE__{}, attrs) + end +end diff --git a/lib/entry.ex b/lib/entry.ex new file mode 100644 index 0000000..0971448 --- /dev/null +++ b/lib/entry.ex @@ -0,0 +1,23 @@ +defmodule ExLedger.Entry do + + defstruct [:date, :status, :description, transactions: []] + + alias ExLedger.Transaction + + @type t :: %__MODULE__{ + date: DateTime.t(), + status: bool(), + description: String.t(), + transactions: [Transaction.t()] + } + + def new(attrs) do + struct!(%__MODULE__{}, attrs) + end + + def transactions(entries) do + entries + |> Enum.map(&(&1.transactions)) + |> List.flatten() + end +end diff --git a/lib/exledger.ex b/lib/exledger.ex new file mode 100644 index 0000000..402cbb2 --- /dev/null +++ b/lib/exledger.ex @@ -0,0 +1,4 @@ +defmodule Exledger do + + +end diff --git a/lib/ledger.ex b/lib/ledger.ex new file mode 100644 index 0000000..d86438e --- /dev/null +++ b/lib/ledger.ex @@ -0,0 +1,27 @@ +defmodule ExLedger.Ledger do + + defstruct entries: [] + + alias ExLedger.Entry + + @type t :: %__MODULE__{ + entries: [Entry.t()], + } + + def new(attrs) do + struct!(%__MODULE__{}, attrs) + end + + @spec balance(__MODULE__.t()) :: integer() + def balance(%{entries: entries} = _ledger) do + entries + |> Entry.transactions() + |> sum() + end + + def sum(transactions) do + transactions + |> Enum.map(&(&1.amount.quantity)) + |> Enum.sum() + end +end diff --git a/lib/transaction.ex b/lib/transaction.ex new file mode 100644 index 0000000..b48c429 --- /dev/null +++ b/lib/transaction.ex @@ -0,0 +1,15 @@ +defmodule ExLedger.Transaction do + + defstruct ~w[account amount]a + + alias ExLedger.{Account, Amount} + + @type t :: %__MODULE__{ + account: Account.t(), + amount: Amount.t(), + } + + def new(attrs) do + struct!(%__MODULE__{}, attrs) + end +end diff --git a/mix.exs b/mix.exs new file mode 100644 index 0000000..79ca1b4 --- /dev/null +++ b/mix.exs @@ -0,0 +1,28 @@ +defmodule Exledger.MixProject do + use Mix.Project + + def project do + [ + app: :exledger, + version: "0.1.0", + elixir: "~> 1.10", + start_permanent: Mix.env() == :prod, + deps: deps() + ] + end + + # Run "mix help compile.app" to learn about applications. + def application do + [ + extra_applications: [:logger] + ] + end + + # Run "mix help deps" to learn about dependencies. + defp deps do + [ + # {:dep_from_hexpm, "~> 0.3.0"}, + # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} + ] + end +end diff --git a/test/exledger_test.exs b/test/exledger_test.exs new file mode 100644 index 0000000..c2c2315 --- /dev/null +++ b/test/exledger_test.exs @@ -0,0 +1,22 @@ +defmodule ExledgerTest do + use ExUnit.Case + + alias ExLedger.{Ledger, Entry, Transaction, Account, Amount} + + test "Ledger.balance/1" do + account = Account.new(name: "assets") + amount = Amount.new(quantity: 1, currency: :USD) + txn1 = Transaction.new(account: account, amount: amount) + + account2 = Account.new(name: "expenses") + amount2 = Amount.new(quantity: -1, currency: :USD) + txn2 = Transaction.new(account: account2, amount: amount2) + + entry = Entry.new(date: DateTime.utc_now(), transactions: [txn1, txn2]) + + ledger = Ledger.new(entries: [entry]) + + # IO.inspect(ledger) + assert Ledger.balance(ledger) == 0 + end +end diff --git a/test/test_helper.exs b/test/test_helper.exs new file mode 100644 index 0000000..869559e --- /dev/null +++ b/test/test_helper.exs @@ -0,0 +1 @@ +ExUnit.start()