diff --git a/lib/ex_money.ex b/lib/ex_money.ex index e2b2406a..cc8f699e 100644 --- a/lib/ex_money.ex +++ b/lib/ex_money.ex @@ -13,6 +13,7 @@ defmodule ExMoney do worker(ExMoney.IdleWorker, [], restart: :transient), worker(ExMoney.AccountsBalanceHistoryWorker, []), worker(ExMoney.Saltedge.SyncWorker, []), + worker(ExMoney.SyncLogWorker, []), worker(ExMoney.Scheduler, []), worker(ExMoney.RuleProcessor, []), worker(ExMoney.Saltedge.LoginRefreshWorker, [], restart: :transient), diff --git a/lib/ex_money/money.ex b/lib/ex_money/money.ex index aced2a40..8c809405 100644 --- a/lib/ex_money/money.ex +++ b/lib/ex_money/money.ex @@ -1,9 +1,15 @@ defmodule ExMoney.Money do + def to_millicents(value) when is_binary(value) do + {:ok, value} = Decimal.parse(value) + to_millicents(value) + end + def to_millicents(value) do - {millicents, _} = value - |> Decimal.mult(Decimal.new(1000)) - |> to_string - |> Integer.parse + {millicents, _} = + value + |> Decimal.mult(Decimal.new(1000)) + |> to_string + |> Integer.parse millicents end diff --git a/lib/ex_money/rule_processor.ex b/lib/ex_money/rule_processor.ex index c66543cc..1ce8c266 100644 --- a/lib/ex_money/rule_processor.ex +++ b/lib/ex_money/rule_processor.ex @@ -1,7 +1,7 @@ defmodule ExMoney.RuleProcessor do use GenServer - alias ExMoney.{Repo, Rule, Transaction, Category, Account} + alias ExMoney.{Repo, Rule, Transactions, Category, Account} import Ecto.Query @@ -10,9 +10,7 @@ defmodule ExMoney.RuleProcessor do end def handle_cast({:process, transaction_id}, _state) do - transaction = Transaction - |> where([tr], tr.id == ^transaction_id) - |> Repo.one + transaction = Transactions.get_transaction(transaction_id) Rule |> where([r], r.account_id == ^transaction.account_id) @@ -31,13 +29,10 @@ defmodule ExMoney.RuleProcessor do def handle_cast({:process_all, rule_id}, _state) do rule = Repo.get(Rule, rule_id) - transactions = Repo.all(from tr in Transaction, - where: tr.account_id == ^rule.account_id, - where: tr.rule_applied == false, - where: fragment("description ~* ?", ^rule.pattern) or fragment("extra->>'payee' ~* ?", ^rule.pattern)) + transactions = Transactions.search(rule.account_id, rule.pattern) - Enum.each(transactions, fn(tr) -> - GenServer.cast(:rule_processor, {:process, tr.id}) + Enum.each(transactions, fn(transaction) -> + GenServer.cast(:rule_processor, {:process, transaction.id}) end) {:noreply, %{}} @@ -47,8 +42,7 @@ defmodule ExMoney.RuleProcessor do {:ok, re} = Regex.compile(rule.pattern, "i") if Regex.match?(re, transaction_description(transaction, transaction.extra)) do - Transaction.update_changeset(transaction, %{category_id: rule.target_id, rule_applied: true}) - |> Repo.update! + Transactions.update_transaction!(transaction, %{category_id: rule.target_id, rule_applied: true}) end end @@ -62,7 +56,7 @@ defmodule ExMoney.RuleProcessor do Decimal.compare(transaction.amount, Decimal.new(0)) == Decimal.new(-1) do Repo.transaction(fn -> - Transaction.changeset_custom(%Transaction{}, + Transactions.create_custom_transaction!( %{ "amount" => transaction.amount, "category_id" => withdraw_category.id, @@ -72,10 +66,8 @@ defmodule ExMoney.RuleProcessor do "type" => "expense" } ) - |> Repo.insert! - Transaction.update_changeset(transaction, %{rule_applied: true, category_id: withdraw_category.id}) - |> Repo.update! + Transactions.update_transaction!(transaction, %{rule_applied: true, category_id: withdraw_category.id}) # transaction.amount is negative, sub with something negative -> add new_balance = Decimal.sub(account.balance, transaction.amount) diff --git a/lib/ex_money/saltedge/transactions_worker.ex b/lib/ex_money/saltedge/transactions_worker.ex index 8065fad7..19948495 100644 --- a/lib/ex_money/saltedge/transactions_worker.ex +++ b/lib/ex_money/saltedge/transactions_worker.ex @@ -3,7 +3,7 @@ defmodule ExMoney.Saltedge.TransactionsWorker do require Logger - alias ExMoney.{Repo, Transaction, Category, Account} + alias ExMoney.{Repo, Transactions, Category, Account} def start_link(_opts \\ []) do GenServer.start_link(__MODULE__, :ok, name: :transactions_worker) @@ -112,31 +112,27 @@ defmodule ExMoney.Saltedge.TransactionsWorker do end defp store(transactions, account) do - Enum.reduce(transactions, 0, fn(se_tran, acc) -> + Enum.reduce transactions, 0, fn(se_tran, acc) -> se_tran = Map.put(se_tran, "saltedge_transaction_id", se_tran["id"]) se_tran = Map.put(se_tran, "saltedge_account_id", se_tran["account_id"]) se_tran = Map.put(se_tran, "user_id", account.user_id) se_tran = Map.drop(se_tran, ["id", "account_id"]) se_tran = Map.put(se_tran, "account_id", account.id) - existing_transaction = Transaction. + existing_transaction = Transactions. by_saltedge_transaction_id(se_tran["saltedge_transaction_id"]) - |> Repo.one if !existing_transaction and !se_tran["duplicated"] do se_tran = set_category_id(se_tran) - changeset = Transaction.changeset(%Transaction{}, se_tran) - {:ok, inserted_transaction} = Repo.transaction fn -> - Repo.insert!(changeset) - end + inserted_transaction = Transactions.create_transaction!(se_tran) GenServer.cast(:rule_processor, {:process, inserted_transaction.id}) - + GenServer.cast(:sync_log_worker, {:store_transaction, inserted_transaction.id}) acc + 1 else acc end - end) + end end defp set_category_id(transaction) do @@ -162,7 +158,6 @@ defmodule ExMoney.Saltedge.TransactionsWorker do defp find_last_transaction(saltedge_account_id) do # FIXME: set last_transaction_id in cache during import - Transaction.newest(saltedge_account_id) - |> Repo.one + Transactions.newest(saltedge_account_id) end end diff --git a/lib/ex_money/sync_log_api/sync_log.ex b/lib/ex_money/sync_log_api/sync_log.ex new file mode 100644 index 00000000..3cdcdd4e --- /dev/null +++ b/lib/ex_money/sync_log_api/sync_log.ex @@ -0,0 +1,25 @@ +defmodule ExMoney.SyncLogApi.SyncLog do + use Ecto.Schema + import Ecto.Changeset + + schema "sync_log" do + field :uuid, :string + field :action, :string + field :entity, :string + field :payload, :map + field :synced_at, :naive_datetime + + timestamps() + end + + def changeset(model, params \\ %{}) do + model + |> cast(params, [:action, :entity, :payload, :synced_at]) + |> validate_required([:action, :entity, :payload]) + |> generate_uuid() + end + + defp generate_uuid(changeset) do + Ecto.Changeset.put_change(changeset, :uuid, Ecto.UUID.generate()) + end +end diff --git a/lib/ex_money/sync_log_api/sync_log_api.ex b/lib/ex_money/sync_log_api/sync_log_api.ex new file mode 100644 index 00000000..9c6c4b46 --- /dev/null +++ b/lib/ex_money/sync_log_api/sync_log_api.ex @@ -0,0 +1,35 @@ +defmodule ExMoney.SyncLogApi do + @moduledoc """ + Api functions to sync things. + """ + + import Ecto.Query, warn: false + alias ExMoney.Repo + + alias ExMoney.SyncLogApi.SyncLog + + def store(entity, action, payload) do + %SyncLog{} + |> SyncLog.changeset(%{action: action, entity: entity, payload: payload}) + |> Repo.insert + end + + def get(per_page) do + query = + from sl in SyncLog, + where: is_nil(sl.synced_at), + order_by: [asc: sl.inserted_at], + limit: ^per_page + + Repo.all(query) + end + + def get_entry!(uuid) do + Repo.get_by!(SyncLog, uuid: uuid) + end + + def mark_synced!(entry) do + SyncLog.changeset(entry, %{synced_at: DateTime.utc_now()}) + |> Repo.update! + end +end diff --git a/lib/ex_money/sync_log_worker.ex b/lib/ex_money/sync_log_worker.ex new file mode 100644 index 00000000..85257912 --- /dev/null +++ b/lib/ex_money/sync_log_worker.ex @@ -0,0 +1,21 @@ +defmodule ExMoney.SyncLogWorker do + use GenServer + require Logger + + alias ExMoney.{SyncLogApi, Transactions} + + def start_link(_opts \\ []) do + GenServer.start_link(__MODULE__, :ok, name: :sync_log_worker) + end + + def handle_cast({:store_transaction, transaction_id}, state) do + payload = + Transactions.get_transaction(transaction_id) + |> Map.from_struct() + |> Map.drop([:__meta__, :user_id, :account, :category, :saltedge_account, :user]) + + SyncLogApi.store("Transaction", "create", payload) + + {:noreply, state} + end +end diff --git a/lib/ex_money/transactions/transaction.ex b/lib/ex_money/transactions/transaction.ex new file mode 100644 index 00000000..ad2e9673 --- /dev/null +++ b/lib/ex_money/transactions/transaction.ex @@ -0,0 +1,77 @@ +defmodule ExMoney.Transactions.Transaction do + use Ecto.Schema + import Ecto.Changeset + + schema "transactions" do + field :saltedge_transaction_id, :integer + field :mode, :string + field :status, :string + field :made_on, :date + field :amount, :decimal + field :currency_code, :string + field :description, :string + field :duplicated, :boolean, default: false + field :rule_applied, :boolean, default: false + field :extra, :map + field :uuid, :string + + belongs_to :category, ExMoney.Category + belongs_to :user, ExMoney.User + belongs_to :account, ExMoney.Account + belongs_to :saltedge_account, ExMoney.Account, + foreign_key: :saltedge_account_id, + references: :saltedge_account_id + + timestamps() + end + + @required_fields ~w( + saltedge_transaction_id + mode + status + made_on + amount + currency_code + description + duplicated + saltedge_account_id + account_id + user_id + )a + @optional_fields ~w(category_id rule_applied extra)a + + def changeset(model, params \\ %{}) do + model + |> cast(params, @required_fields ++ @optional_fields) + |> validate_required(@required_fields) + |> generate_uuid() + end + + def changeset_custom(model, params \\ %{}) do + model + |> cast(params, ~w(amount category_id account_id made_on user_id description extra)a) + |> validate_required(~w(amount category_id account_id made_on user_id)a) + |> negate_amount(params) + |> generate_uuid() + end + + def update_changeset(model, params \\ %{}) do + model + |> cast(params, ~w(category_id description rule_applied extra)a) + end + + def negate_amount(changeset, params) when params == %{}, do: changeset + def negate_amount(changeset, %{"type" => "income"}), do: changeset + + def negate_amount(changeset, %{"type" => "expense"}) do + case Ecto.Changeset.fetch_change(changeset, :amount) do + {:ok, amount} -> + Ecto.Changeset.put_change(changeset, :amount, Decimal.mult(amount, Decimal.new(-1))) + :error -> changeset + end + end + + defp generate_uuid(changeset) do + Ecto.Changeset.put_change(changeset, :uuid, Ecto.UUID.generate()) + end +end diff --git a/lib/ex_money/transactions/transactions.ex b/lib/ex_money/transactions/transactions.ex new file mode 100644 index 00000000..2e5d42c7 --- /dev/null +++ b/lib/ex_money/transactions/transactions.ex @@ -0,0 +1,227 @@ +defmodule ExMoney.Transactions do + @moduledoc """ + The Transactions context. + """ + + import Ecto.Query, warn: false + alias ExMoney.Repo + + alias ExMoney.Transactions.Transaction + + def changeset(transaction) do + Transaction.changeset(transaction) + end + + def changeset_custom() do + Transaction.changeset_custom(%Transaction{}) + end + + def update_changeset(transaction) do + Transaction.update_changeset(transaction) + end + + def get_transaction(id) do + Repo.get(Transaction, id) + end + + def get_transaction!(id) do + Repo.get!(Transaction, id) + end + + def get_transaction_by(params) do + Repo.get_by(Transaction, params) + end + + def get_transaction_by!(params) do + Repo.get_by!(Transaction, params) + end + + # FIXME + def delete_transaction!(id) do + Repo.get!(Transaction, id) |> Repo.delete! + end + + def get_transaction_with_includes!(id, includes) do + query = from tr in Transaction, where: tr.id == ^id, preload: ^includes + + Repo.one(query) + end + + def create_transaction(attributes) do + Transaction.changeset(%Transaction{}, attributes) + |> Repo.insert() + end + + def create_transaction!(attributes) do + Transaction.changeset(%Transaction{}, attributes) + |> Repo.insert!() + end + + def create_custom_transaction!(attributes) do + Transaction.changeset_custom(%Transaction{}, attributes) + |> Repo.insert!() + end + + def update_transaction(transaction, attributes) do + Transaction.update_changeset(transaction, attributes) + |> Repo.update() + end + + def update_transaction!(transaction, attributes) do + Transaction.update_changeset(transaction, attributes) + |> Repo.update!() + end + + def search(account_id, pattern) do + query = + from tr in Transaction, + where: tr.account_id == ^account_id, + where: tr.rule_applied == false, + where: fragment("description ~* ?", ^pattern) or fragment("extra->>'payee' ~* ?", ^pattern) + + Repo.all(query) + end + + def by_user_id(user_id) do + from tr in Transaction, + where: tr.user_id == ^user_id + end + + def by_saltedge_transaction_id(transaction_id) do + query = + from tr in Transaction, + where: tr.saltedge_transaction_id == ^transaction_id, + limit: 1 + + Repo.one(query) + end + + def recent(user_id) do + current_date = Timex.local + from = Timex.shift(current_date, days: -15) + + query = + from tr in Transaction, + where: tr.made_on >= ^from, + where: tr.user_id == ^user_id, + preload: [:category, :account], + order_by: [desc: tr.inserted_at] + + Repo.all(query) + end + + def new_since(time, account_id) do + query = + from tr in Transaction, + where: tr.inserted_at >= ^time, + where: tr.account_id == ^account_id + + Repo.all(query) + end + + def by_month(account_ids, from, to) when is_list(account_ids) do + from tr in Transaction, + preload: [:account, :category], + where: tr.made_on >= ^from, + where: tr.made_on <= ^to, + where: tr.account_id in ^account_ids + end + + def by_month(account_id, from, to) do + from tr in Transaction, + where: tr.made_on >= ^from, + where: tr.made_on <= ^to, + where: tr.account_id == ^account_id + end + + def expenses_by_month_by_category(account_id, from, to, category_ids) do + by_month(account_id, from, to) + |> where([tr], tr.amount < 0) + |> where([tr], tr.category_id in ^(category_ids)) + |> preload([:category, :account]) + |> Repo.all() + end + + def by_month_by_category(account_id, from, to, category_ids) do + by_month(account_id, from, to) + |> where([tr], tr.category_id in ^(category_ids)) + |> preload([:category, :account]) + |> Repo.all() + end + + def expenses_by_month(account_id, from, to) do + query = + from tr in by_month(account_id, from, to), + join: c in assoc(tr, :category), + where: tr.amount < 0, + where: c.name != "withdraw", + preload: [:category, :account] + + Repo.all(query) + end + + def income_by_month_by_category(account_id, from, to, category_ids) do + by_month(account_id, from, to) + |> where([tr], tr.amount > 0 ) + |> where([tr], tr.category_id in ^(category_ids)) + |> preload([:category, :account]) + |> Repo.all() + end + + def income_by_month(account_id, from ,to) do + query = + from tr in by_month(account_id, from, to), + join: c in assoc(tr, :category), + where: tr.amount > 0, + where: c.name != "withdraw", + preload: [:category, :account] + + Repo.all(query) + end + + def group_by_month_by_category_without_withdraw(account_ids, from, to) do + query = + from tr in Transaction, + join: c in assoc(tr, :category), + where: c.name != "withdraw", + where: tr.made_on >= ^from, + where: tr.made_on <= ^to, + where: tr.account_id in ^account_ids, + where: tr.amount < 0, + group_by: [c.id], + select: {c, sum(tr.amount)} + + Repo.all(query) + end + + def group_by_month_by_category(account_id, from, to) do + query = + from tr in Transaction, + join: c in assoc(tr, :category), + where: tr.made_on >= ^from, + where: tr.made_on <= ^to, + where: tr.account_id == ^account_id, + where: tr.amount < 0, + group_by: [c.id], + select: {c, sum(tr.amount)} + + Repo.all(query) + end + + # FIXME cache instead of db + def newest(saltedge_account_id) do + query = + from tr in Transaction, + where: tr.saltedge_account_id == ^saltedge_account_id, + order_by: [desc: tr.made_on], + limit: 1 + + Repo.one(query) + end + + def newest do + from tr in Transaction, + order_by: [desc: tr.made_on], + limit: 1 + end +end diff --git a/lib/ex_money_web/controllers/api/v2/sync_controller.ex b/lib/ex_money_web/controllers/api/v2/sync_controller.ex new file mode 100644 index 00000000..e38409d0 --- /dev/null +++ b/lib/ex_money_web/controllers/api/v2/sync_controller.ex @@ -0,0 +1,23 @@ +defmodule ExMoney.Web.Api.V2.SyncController do + use ExMoney.Web, :controller + + alias ExMoney.SyncLogApi + + plug Guardian.Plug.EnsureAuthenticated, handler: ExMoney.Guardian.ApiUnauthenticated + + def index(conn, params) do + per_page = params["per_page"] || 20 + + entries = SyncLogApi.get(per_page) + + render conn, :index, entries: entries + end + + def create(conn, %{"uuid" => uuid}) do + entry = SyncLogApi.get_entry!(uuid) + + SyncLogApi.mark_synced!(entry) + + send_resp(conn, 201, "") + end +end diff --git a/lib/ex_money_web/controllers/api/v2/transaction_controller.ex b/lib/ex_money_web/controllers/api/v2/transaction_controller.ex index bc98c03d..fa778f0c 100644 --- a/lib/ex_money_web/controllers/api/v2/transaction_controller.ex +++ b/lib/ex_money_web/controllers/api/v2/transaction_controller.ex @@ -1,14 +1,14 @@ defmodule ExMoney.Web.Api.V2.TransactionController do use ExMoney.Web, :controller - alias ExMoney.Transaction + alias ExMoney.Transactions plug Guardian.Plug.EnsureAuthenticated, handler: ExMoney.Guardian.ApiUnauthenticated def recent(conn, _params) do user = Guardian.Plug.current_resource(conn) - transactions = Transaction.recent(user.id) |> Repo.all + transactions = Transactions.recent(user.id) render conn, :recent, transactions: transactions end diff --git a/lib/ex_money_web/controllers/dashboard_controller.ex b/lib/ex_money_web/controllers/dashboard_controller.ex index 6cf49225..70f8fc00 100644 --- a/lib/ex_money_web/controllers/dashboard_controller.ex +++ b/lib/ex_money_web/controllers/dashboard_controller.ex @@ -1,15 +1,14 @@ defmodule ExMoney.Web.DashboardController do use ExMoney.Web, :controller - alias ExMoney.{Repo, Transaction, Account} + alias ExMoney.{Repo, Transactions, Account} plug Guardian.Plug.EnsureAuthenticated, handler: ExMoney.Guardian.Unauthenticated def overview(conn, _params) do user = Guardian.Plug.current_resource(conn) - recent_transactions = Transaction.recent(user.id) - |> Repo.all + recent_transactions = Transactions.recent(user.id) |> Enum.group_by(fn(transaction) -> transaction.made_on end) diff --git a/lib/ex_money_web/controllers/mobile/account_controller.ex b/lib/ex_money_web/controllers/mobile/account_controller.ex index a7b0c2d1..3c8b2fb7 100644 --- a/lib/ex_money_web/controllers/mobile/account_controller.ex +++ b/lib/ex_money_web/controllers/mobile/account_controller.ex @@ -2,7 +2,7 @@ defmodule ExMoney.Web.Mobile.AccountController do use ExMoney.Web, :controller alias ExMoney.DateHelper - alias ExMoney.{Repo, Transaction, Account} + alias ExMoney.{Repo, Transactions, Account} plug Guardian.Plug.EnsureAuthenticated, handler: ExMoney.Guardian.Mobile.Unauthenticated plug :put_layout, "mobile.html" @@ -14,11 +14,9 @@ defmodule ExMoney.Web.Mobile.AccountController do account = Account.by_id_with_login(account_id) |> Repo.one - month_transactions = Transaction.by_month(account_id, from, to) - |> Repo.all + month_transactions = Transactions.by_month(account_id, from, to) |> Repo.all() - categories = Transaction.group_by_month_by_category(account_id, from, to) - |> Repo.all + categories = Transactions.group_by_month_by_category(account_id, from, to) |> Enum.reduce(%{}, fn({category, amount}, acc) -> {float_amount, _} = Decimal.to_string(amount, :normal) |> Float.parse @@ -61,8 +59,8 @@ defmodule ExMoney.Web.Mobile.AccountController do to = DateHelper.last_day_of_month(parsed_date) account_balance = account_balance(from, to, account.id) - scope = Transaction.expenses_by_month(account_id, from, to) - expenses = fetch_and_process_transactions(scope) + transactions = Transactions.expenses_by_month(account_id, from, to) + expenses = process_transactions(transactions) {:ok, formatted_date} = Timex.format(parsed_date, "%b %Y", :strftime) @@ -83,8 +81,8 @@ defmodule ExMoney.Web.Mobile.AccountController do to = DateHelper.last_day_of_month(parsed_date) account_balance = account_balance(from, to, account.id) - scope = Transaction.income_by_month(account_id, from, to) - income = fetch_and_process_transactions(scope) + transactions = Transactions.income_by_month(account_id, from, to) + income = process_transactions(transactions) {:ok, formatted_date} = Timex.format(parsed_date, "%b %Y", :strftime) @@ -110,9 +108,8 @@ defmodule ExMoney.Web.Mobile.AccountController do end) end - defp fetch_and_process_transactions(scope) do - scope - |> Repo.all + defp process_transactions(transactions) do + transactions |> Enum.group_by(fn(transaction) -> transaction.made_on end) |> Enum.sort(fn({date_1, _transactions}, {date_2, _transaction}) -> Date.compare(date_1, date_2) != :lt diff --git a/lib/ex_money_web/controllers/mobile/budget_controller.ex b/lib/ex_money_web/controllers/mobile/budget_controller.ex index 075a844f..f2824563 100644 --- a/lib/ex_money_web/controllers/mobile/budget_controller.ex +++ b/lib/ex_money_web/controllers/mobile/budget_controller.ex @@ -2,7 +2,7 @@ defmodule ExMoney.Web.Mobile.BudgetController do use ExMoney.Web, :controller alias ExMoney.DateHelper - alias ExMoney.{Repo, Transaction, Budget} + alias ExMoney.{Repo, Transactions, Budget} plug Guardian.Plug.EnsureAuthenticated, handler: ExMoney.Guardian.Mobile.Unauthenticated plug :put_layout, "mobile.html" @@ -24,11 +24,8 @@ defmodule ExMoney.Web.Mobile.BudgetController do to = DateHelper.last_day_of_month(parsed_date) account_ids = current_budget.accounts - month_transactions = Transaction.by_month(account_ids, from, to) - |> Repo.all - - categories = Transaction.group_by_month_by_category_without_withdraw(account_ids, from, to) - |> Repo.all + month_transactions = Transactions.by_month(account_ids, from, to) |> Repo.all() + categories = Transactions.group_by_month_by_category_without_withdraw(account_ids, from, to) current_month = DateHelper.current_month(parsed_date) previous_month = DateHelper.previous_month(parsed_date) @@ -63,8 +60,8 @@ defmodule ExMoney.Web.Mobile.BudgetController do from = DateHelper.first_day_of_month(parsed_date) to = DateHelper.last_day_of_month(parsed_date) - scope = Transaction.expenses_by_month(current_budget.accounts, from, to) - expenses = fetch_and_process_transactions(scope) + transactions = Transactions.expenses_by_month(current_budget.accounts, from, to) + expenses = process_transactions(transactions) {:ok, formatted_date} = Timex.format(parsed_date, "%b %Y", :strftime) @@ -83,8 +80,8 @@ defmodule ExMoney.Web.Mobile.BudgetController do from = DateHelper.first_day_of_month(parsed_date) to = DateHelper.last_day_of_month(parsed_date) - scope = Transaction.income_by_month(current_budget.accounts, from, to) - income = fetch_and_process_transactions(scope) + transactions = Transactions.income_by_month(current_budget.accounts, from, to) + income = process_transactions(transactions) {:ok, formatted_date} = Timex.format(parsed_date, "%b %Y", :strftime) @@ -96,9 +93,8 @@ defmodule ExMoney.Web.Mobile.BudgetController do from: from end - defp fetch_and_process_transactions(scope) do - scope - |> Repo.all + defp process_transactions(transactions) do + transactions |> Enum.group_by(fn(transaction) -> transaction.made_on end) |> Enum.sort(fn({date_1, _transactions}, {date_2, _transaction}) -> Date.compare(date_1, date_2) != :lt diff --git a/lib/ex_money_web/controllers/mobile/dashboard_controller.ex b/lib/ex_money_web/controllers/mobile/dashboard_controller.ex index 78bd1594..713e1c09 100644 --- a/lib/ex_money_web/controllers/mobile/dashboard_controller.ex +++ b/lib/ex_money_web/controllers/mobile/dashboard_controller.ex @@ -1,7 +1,7 @@ defmodule ExMoney.Web.Mobile.DashboardController do use ExMoney.Web, :controller - alias ExMoney.{Repo, Transaction, Account, FavouriteTransaction} + alias ExMoney.{Repo, Transactions, Account, FavouriteTransaction} plug Guardian.Plug.EnsureAuthenticated, handler: ExMoney.Guardian.Mobile.Unauthenticated plug :put_layout, "mobile.html" @@ -9,7 +9,7 @@ defmodule ExMoney.Web.Mobile.DashboardController do def overview(conn, _params) do user = Guardian.Plug.current_resource(conn) last_login_at = fetch_last_login_at() - transactions = Transaction.recent(user.id) |> Repo.all + transactions = Transactions.recent(user.id) new_recent_transactions = Enum.filter(transactions, fn(tr) -> NaiveDateTime.compare(tr.inserted_at, last_login_at) != :lt @@ -27,7 +27,7 @@ defmodule ExMoney.Web.Mobile.DashboardController do accounts = Account.show_on_dashboard |> Repo.all |> Enum.reduce([], fn(account, acc) -> - new_transactions = Transaction.new_since(last_login_at, account.id) |> Repo.all + new_transactions = Transactions.new_since(last_login_at, account.id) recent_diff = Enum.reduce(new_transactions, Decimal.new(0), fn(transaction, acc) -> Decimal.add(acc, transaction.amount) @@ -41,7 +41,7 @@ defmodule ExMoney.Web.Mobile.DashboardController do accounts: accounts, new_transaction_ids: new_transaction_ids, fav_transaction: fav_transaction(user.id), - changeset: Transaction.changeset_custom(%Transaction{}) + changeset: Transactions.changeset_custom() end defp fav_transaction(user_id) do diff --git a/lib/ex_money_web/controllers/mobile/transaction_controller.ex b/lib/ex_money_web/controllers/mobile/transaction_controller.ex index fb2995d4..f4bc6914 100644 --- a/lib/ex_money_web/controllers/mobile/transaction_controller.ex +++ b/lib/ex_money_web/controllers/mobile/transaction_controller.ex @@ -2,7 +2,7 @@ defmodule ExMoney.Web.Mobile.TransactionController do use ExMoney.Web, :controller alias ExMoney.DateHelper - alias ExMoney.{Repo, Transaction, Category, Account, FavouriteTransaction, Budget} + alias ExMoney.{Repo, Transactions, Category, Account, FavouriteTransaction, Budget} plug Guardian.Plug.EnsureAuthenticated, handler: ExMoney.Guardian.Mobile.Unauthenticated plug :put_layout, "mobile.html" @@ -19,8 +19,7 @@ defmodule ExMoney.Web.Mobile.TransactionController do from = DateHelper.first_day_of_month(parsed_date) to = DateHelper.last_day_of_month(parsed_date) - transactions = Transaction.by_month_by_category(account_id, from, to, category_ids) - |> Repo.all + transactions = Transactions.by_month_by_category(account_id, from, to, category_ids) |> Enum.group_by(fn(transaction) -> transaction.made_on end) @@ -57,21 +56,21 @@ defmodule ExMoney.Web.Mobile.TransactionController do from = DateHelper.first_day_of_month(parsed_date) to = DateHelper.last_day_of_month(parsed_date) - query = case type do + transactions = case type do "expenses" -> - Transaction.expenses_by_month_by_category(current_budget.accounts, from, to, category_ids) + Transactions.expenses_by_month_by_category(current_budget.accounts, from, to, category_ids) "income" -> - Transaction.income_by_month_by_category(current_budget.accounts, from, to, category_ids) + Transactions.income_by_month_by_category(current_budget.accounts, from, to, category_ids) end - transactions = query - |> Repo.all - |> Enum.group_by(fn(transaction) -> - transaction.made_on - end) - |> Enum.sort(fn({date_1, _transactions}, {date_2, _transaction}) -> - Date.compare(date_1, date_2) != :lt - end) + transactions = + transactions + |> Enum.group_by(fn(transaction) -> + transaction.made_on + end) + |> Enum.sort(fn({date_1, _transactions}, {date_2, _transaction}) -> + Date.compare(date_1, date_2) != :lt + end) {:ok, formatted_date} = Timex.format(parsed_date, "%b %Y", :strftime) @@ -94,7 +93,7 @@ defmodule ExMoney.Web.Mobile.TransactionController do accounts = Account.only_custom |> Repo.all - changeset = Transaction.changeset_custom(%Transaction{}) + changeset = Transactions.changeset_custom() render conn, :new, categories: categories, @@ -103,9 +102,9 @@ defmodule ExMoney.Web.Mobile.TransactionController do end def edit(conn, %{"id" => id, "from" => from}) do - transaction = Repo.get(Transaction, id) + transaction = Transactions.get_transaction(id) categories = categories_list() - changeset = Transaction.update_changeset(transaction) + changeset = Transactions.update_changeset(transaction) render conn, :edit, transaction: transaction, @@ -115,13 +114,11 @@ defmodule ExMoney.Web.Mobile.TransactionController do end def update(conn, %{"id" => id, "transaction" => transaction_params}) do - transaction = Repo.get!(Transaction, id) + transaction = Transactions.get_transaction!(id) from = validate_from_param(transaction_params["from"]) - changeset = Transaction.update_changeset(transaction, transaction_params) - - case Repo.update(changeset) do + case Transactions.update_transaction(transaction, transaction_params) do {:ok, _transaction} -> send_resp(conn, 200, from) {:error, _changeset} -> @@ -132,10 +129,9 @@ defmodule ExMoney.Web.Mobile.TransactionController do def create(conn, %{"transaction" => transaction_params}) do user = Guardian.Plug.current_resource(conn) transaction_params = Map.put(transaction_params, "user_id", user.id) - changeset = Transaction.changeset_custom(%Transaction{}, transaction_params) Repo.transaction fn -> - transaction = Repo.insert!(changeset) + transaction = Transactions.create_custom_transaction!(transaction_params) account = Repo.get!(Account, transaction.account_id) new_balance = Decimal.add(account.balance, transaction.amount) Account.update_custom_changeset(account, %{balance: new_balance}) @@ -163,10 +159,9 @@ defmodule ExMoney.Web.Mobile.TransactionController do "made_on" => made_on, "type" => "expense" } - changeset = Transaction.changeset_custom(%Transaction{}, transaction_params) Repo.transaction fn -> - transaction = Repo.insert!(changeset) + transaction = Transactions.create_custom_transaction!(transaction_params) account = Repo.get!(Account, transaction.account_id) new_balance = Decimal.add(account.balance, transaction.amount) Account.update_custom_changeset(account, %{balance: new_balance}) @@ -177,17 +172,13 @@ defmodule ExMoney.Web.Mobile.TransactionController do end def show(conn, %{"id" => id}) do - transaction = Repo.one( - from tr in Transaction, - where: tr.id == ^id, - preload: [:account, :category] - ) + transaction = Transactions.get_transaction_with_includes!(id, [:account, :category]) render conn, :show, transaction: transaction end def delete(conn, %{"id" => id}) do - transaction = Repo.get!(Transaction, id) + transaction = Transactions.get_transaction!(id) account = Repo.get!(Account, transaction.account_id) case transaction.saltedge_transaction_id do diff --git a/lib/ex_money_web/controllers/transaction_controller.ex b/lib/ex_money_web/controllers/transaction_controller.ex index 17347257..54645ff0 100644 --- a/lib/ex_money_web/controllers/transaction_controller.ex +++ b/lib/ex_money_web/controllers/transaction_controller.ex @@ -1,7 +1,8 @@ defmodule ExMoney.Web.TransactionController do use ExMoney.Web, :controller - alias ExMoney.{Transaction, Repo, Paginator, Account, Category} + alias ExMoney.{Transactions.Transaction, Repo, Paginator, Account, Category} + alias ExMoney.Transactions import Ecto.Query plug Guardian.Plug.EnsureAuthenticated, handler: ExMoney.Guardian.Unauthenticated @@ -10,7 +11,7 @@ defmodule ExMoney.Web.TransactionController do def index(conn, params) do user = Guardian.Plug.current_resource(conn) - paginator = Transaction.by_user_id(user.id) + paginator = Transactions.by_user_id(user.id) |> order_by(desc: :made_on) |> preload([:account, :saltedge_account, :category]) |> Paginator.paginate(params) @@ -24,7 +25,7 @@ defmodule ExMoney.Web.TransactionController do end def new(conn, _params) do - changeset = Transaction.changeset_custom(%Transaction{}) + changeset = Transactions.changeset_custom() accounts = Account.only_custom |> Repo.all categories_dict = Repo.all(Category) @@ -50,9 +51,8 @@ defmodule ExMoney.Web.TransactionController do def create(conn, %{"transaction" => transaction_params}) do user = Guardian.Plug.current_resource(conn) transaction_params = Map.put(transaction_params, "user_id", user.id) - changeset = Transaction.changeset_custom(%Transaction{}, transaction_params) - case Repo.insert(changeset) do + case Transactions.create_custom_transaction(transaction_params) do {:ok, _transaction} -> redirect(conn, to: transaction_path(conn, :index)) {:error, changeset} -> @@ -64,7 +64,7 @@ defmodule ExMoney.Web.TransactionController do end def show(conn, %{"id" => id}) do - transaction = Repo.get!(Transaction, id) + transaction = Transactions.get_transaction(id) render conn, :show, transaction: transaction, @@ -74,7 +74,7 @@ defmodule ExMoney.Web.TransactionController do def edit(conn, %{"id" => id}) do transaction = Repo.get!(Transaction, id) - changeset = Transaction.changeset(transaction) + changeset = Transactions.changeset(transaction) render conn, :edit, transaction: transaction, @@ -84,10 +84,9 @@ defmodule ExMoney.Web.TransactionController do end def update(conn, %{"id" => id, "transaction" => transaction_params}) do - transaction = Repo.get!(Transaction, id) - changeset = Transaction.changeset(transaction, transaction_params) + transaction = Transactions.get_transaction!(id) - case Repo.update(changeset) do + case Transactions.update_transaction(transaction, transaction_params) do {:ok, transaction} -> redirect(conn, to: transaction_path(conn, :show, transaction)) {:error, changeset} -> @@ -100,7 +99,7 @@ defmodule ExMoney.Web.TransactionController do end def delete(conn, %{"id" => id}) do - Repo.get!(Transaction, id) |> Repo.delete! + Transactions.delete_transaction!(id) redirect(conn, to: transaction_path(conn, :index)) end diff --git a/lib/ex_money_web/models/account.ex b/lib/ex_money_web/models/account.ex index 339fc511..c83e52aa 100644 --- a/lib/ex_money_web/models/account.ex +++ b/lib/ex_money_web/models/account.ex @@ -17,7 +17,7 @@ defmodule ExMoney.Account do references: :saltedge_login_id belongs_to :user, ExMoney.User has_many :rules, ExMoney.Rule - has_many :transactions, ExMoney.Transaction, + has_many :transactions, ExMoney.Transactions.Transaction, on_delete: :delete_all, foreign_key: :saltedge_account_id, references: :saltedge_account_id diff --git a/lib/ex_money_web/models/category.ex b/lib/ex_money_web/models/category.ex index 9b5466ac..d50d2d9e 100644 --- a/lib/ex_money_web/models/category.ex +++ b/lib/ex_money_web/models/category.ex @@ -11,7 +11,7 @@ defmodule ExMoney.Category do timestamps() - has_many :transactions, ExMoney.Transaction + has_many :transactions, ExMoney.Transactions.Transaction belongs_to :parent, ExMoney.Category end diff --git a/lib/ex_money_web/models/transaction.ex b/lib/ex_money_web/models/transaction.ex deleted file mode 100644 index b52d1c4b..00000000 --- a/lib/ex_money_web/models/transaction.ex +++ /dev/null @@ -1,189 +0,0 @@ -defmodule ExMoney.Transaction do - use ExMoney.Web, :model - - alias ExMoney.Transaction - - import Ecto.Query - - schema "transactions" do - field :saltedge_transaction_id, :integer - field :mode, :string - field :status, :string - field :made_on, :date - field :amount, :decimal - field :currency_code, :string - field :description, :string - field :duplicated, :boolean, default: false - field :rule_applied, :boolean, default: false - field :extra, :map - - belongs_to :category, ExMoney.Category - belongs_to :user, ExMoney.User - belongs_to :account, ExMoney.Account - belongs_to :saltedge_account, ExMoney.Account, - foreign_key: :saltedge_account_id, - references: :saltedge_account_id - - timestamps() - end - - @required_fields ~w( - saltedge_transaction_id - mode - status - made_on - amount - currency_code - description - duplicated - saltedge_account_id - account_id - user_id - )a - @optional_fields ~w(category_id rule_applied extra)a - - def changeset(model, params \\ %{}) do - model - |> cast(params, @required_fields ++ @optional_fields) - |> validate_required(@required_fields) - end - - def changeset_custom(model, params \\ %{}) do - model - |> cast(params, ~w(amount category_id account_id made_on user_id description extra)a) - |> validate_required(~w(amount category_id account_id made_on user_id)a) - |> negate_amount(params) - end - - def update_changeset(model, params \\ %{}) do - model - |> cast(params, ~w(category_id description rule_applied extra)a) - end - - def negate_amount(changeset, params) when params == %{}, do: changeset - def negate_amount(changeset, %{"type" => "income"}), do: changeset - - def negate_amount(changeset, %{"type" => "expense"}) do - case Ecto.Changeset.fetch_change(changeset, :amount) do - {:ok, amount} -> - Ecto.Changeset.put_change(changeset, :amount, Decimal.mult(amount, Decimal.new(-1))) - :error -> changeset - end - end - - def by_user_id(user_id) do - from tr in Transaction, - where: tr.user_id == ^user_id - end - - def by_saltedge_transaction_id(transaction_id) do - from tr in Transaction, - where: tr.saltedge_transaction_id == ^transaction_id, - limit: 1 - end - - def recent(user_id) do - current_date = Timex.local - from = Timex.shift(current_date, days: -15) - - from tr in Transaction, - where: tr.made_on >= ^from, - where: tr.user_id == ^user_id, - preload: [:category, :account], - order_by: [desc: tr.inserted_at] - end - - def new_since(time, account_id) do - from tr in Transaction, - where: tr.inserted_at >= ^time, - where: tr.account_id == ^account_id - end - - def by_month(account_ids, from, to) when is_list(account_ids) do - from tr in Transaction, - preload: [:account, :category], - where: tr.made_on >= ^from, - where: tr.made_on <= ^to, - where: tr.account_id in ^account_ids - end - - def by_month(account_id, from, to) do - from tr in Transaction, - where: tr.made_on >= ^from, - where: tr.made_on <= ^to, - where: tr.account_id == ^account_id - end - - def expenses_by_month_by_category(account_id, from, to, category_ids) do - Transaction.by_month(account_id, from, to) - |> where([tr], tr.amount < 0) - |> where([tr], tr.category_id in ^(category_ids)) - |> preload([:category, :account]) - end - - def by_month_by_category(account_id, from, to, category_ids) do - Transaction.by_month(account_id, from, to) - |> where([tr], tr.category_id in ^(category_ids)) - |> preload([:category, :account]) - end - - def expenses_by_month(account_id, from, to) do - from tr in Transaction.by_month(account_id, from, to), - join: c in assoc(tr, :category), - where: tr.amount < 0, - where: c.name != "withdraw", - preload: [:category, :account] - end - - def income_by_month_by_category(account_id, from, to, category_ids) do - Transaction.by_month(account_id, from, to) - |> where([tr], tr.amount > 0 ) - |> where([tr], tr.category_id in ^(category_ids)) - |> preload([:category, :account]) - end - - def income_by_month(account_id, from ,to) do - from tr in Transaction.by_month(account_id, from, to), - join: c in assoc(tr, :category), - where: tr.amount > 0, - where: c.name != "withdraw", - preload: [:category, :account] - end - - def group_by_month_by_category_without_withdraw(account_ids, from, to) do - from tr in Transaction, - join: c in assoc(tr, :category), - where: c.name != "withdraw", - where: tr.made_on >= ^from, - where: tr.made_on <= ^to, - where: tr.account_id in ^account_ids, - where: tr.amount < 0, - group_by: [c.id], - select: {c, sum(tr.amount)} - end - - def group_by_month_by_category(account_id, from, to) do - from tr in Transaction, - join: c in assoc(tr, :category), - where: tr.made_on >= ^from, - where: tr.made_on <= ^to, - where: tr.account_id == ^account_id, - where: tr.amount < 0, - group_by: [c.id], - select: {c, sum(tr.amount)} - end - - # FIXME cache instead of db - def newest(saltedge_account_id) do - from tr in Transaction, - where: tr.saltedge_account_id == ^saltedge_account_id, - order_by: [desc: tr.made_on], - limit: 1 - end - - def newest do - from tr in Transaction, - order_by: [desc: tr.made_on], - limit: 1 - end -end diff --git a/lib/ex_money_web/router.ex b/lib/ex_money_web/router.ex index 3dfdf4b2..8968a04f 100644 --- a/lib/ex_money_web/router.ex +++ b/lib/ex_money_web/router.ex @@ -128,6 +128,8 @@ defmodule ExMoney.Web.Router do resources "/accounts", AccountController, only: [:index] resources "/categories", CategoryController, only: [:index] get "/transactions/recent", TransactionController, :recent, as: :recent + get "/sync", SyncController, :index + post "/sync", SyncController, :create end scope "/api/v1", ExMoney.Web.Api.V1, as: :api do diff --git a/lib/ex_money_web/views/api/v2/sync_view.ex b/lib/ex_money_web/views/api/v2/sync_view.ex new file mode 100644 index 00000000..566ae66e --- /dev/null +++ b/lib/ex_money_web/views/api/v2/sync_view.ex @@ -0,0 +1,24 @@ +defmodule ExMoney.Web.Api.V2.SyncView do + use ExMoney.Web, :view + + alias ExMoney.Web.Api.V2.SyncView + + def render("index.json", %{entries: entries}) do + render_many(entries, SyncView, "entry.json") + end + + def render("entry.json", %{sync: entry}) do + %{ + uuid: entry.uuid, + entity: entry.entity, + action: entry.action, + payload: payload(entry.entity, entry.payload) + } + end + + defp payload(entity, %{"amount" => amount} = payload) when entity == "Transaction" do + Map.put(payload, "amount_millicents", ExMoney.Money.to_millicents(amount)) + end + + defp payload(_entity, payload), do: payload +end diff --git a/lib/ex_money_web/views/dashboard_view.ex b/lib/ex_money_web/views/dashboard_view.ex index 443750ec..d2a21080 100644 --- a/lib/ex_money_web/views/dashboard_view.ex +++ b/lib/ex_money_web/views/dashboard_view.ex @@ -1,7 +1,7 @@ defmodule ExMoney.Web.DashboardView do use ExMoney.Web, :view - alias ExMoney.Transaction + alias ExMoney.Transactions.Transaction def balance(transactions) do Enum.reduce(transactions, Decimal.new(0), fn(transaction, acc) -> diff --git a/lib/ex_money_web/views/mobile/dashboard_view.ex b/lib/ex_money_web/views/mobile/dashboard_view.ex index 83f13845..8a897d91 100644 --- a/lib/ex_money_web/views/mobile/dashboard_view.ex +++ b/lib/ex_money_web/views/mobile/dashboard_view.ex @@ -1,7 +1,7 @@ defmodule ExMoney.Web.Mobile.DashboardView do use ExMoney.Web, :view - alias ExMoney.Transaction + alias ExMoney.Transactions.Transaction def categories_chart_data([]), do: [] diff --git a/lib/ex_money_web/views/mobile/transaction_view.ex b/lib/ex_money_web/views/mobile/transaction_view.ex index 1c54c3ae..7c486b66 100644 --- a/lib/ex_money_web/views/mobile/transaction_view.ex +++ b/lib/ex_money_web/views/mobile/transaction_view.ex @@ -1,6 +1,6 @@ defmodule ExMoney.Web.Mobile.TransactionView do use ExMoney.Web, :view - alias ExMoney.Transaction + alias ExMoney.Transactions.Transaction def balance(transactions) do Enum.reduce(transactions, Decimal.new(0), fn(transaction, acc) -> diff --git a/lib/ex_money_web/views/shared_view.ex b/lib/ex_money_web/views/shared_view.ex index 25cf05fa..9fcd5e35 100644 --- a/lib/ex_money_web/views/shared_view.ex +++ b/lib/ex_money_web/views/shared_view.ex @@ -1,6 +1,6 @@ defmodule ExMoney.Web.SharedView do use ExMoney.Web, :view - alias ExMoney.Transaction + alias ExMoney.Transactions.Transaction def translate_error({message, values}) do Enum.reduce values, message, fn {k, v}, acc -> diff --git a/lib/ex_money_web/views/transaction_view.ex b/lib/ex_money_web/views/transaction_view.ex index 3aa18b97..58f23704 100644 --- a/lib/ex_money_web/views/transaction_view.ex +++ b/lib/ex_money_web/views/transaction_view.ex @@ -1,6 +1,6 @@ defmodule ExMoney.Web.TransactionView do use ExMoney.Web, :view - alias ExMoney.Transaction + alias ExMoney.Transactions.Transaction def disabled_previous_page?(page_number, total_pages) do if page_number == 1 or total_pages == 1 do diff --git a/lib/mix/tasks/migrate_transactions_info.ex b/lib/mix/tasks/migrate_transactions_info.ex index 06f25598..4f4498e0 100644 --- a/lib/mix/tasks/migrate_transactions_info.ex +++ b/lib/mix/tasks/migrate_transactions_info.ex @@ -2,7 +2,7 @@ defmodule Mix.Tasks.ExMoney.MigrateTransactionsInfo do use Mix.Task import Mix.Ecto - alias ExMoney.{Repo, Transaction, TransactionInfo} + alias ExMoney.{Repo, Transactions, TransactionInfo} @shortdoc "Migrate TransactionsInfo table to 'extra' jsonb column in Transaction" @@ -20,10 +20,9 @@ defmodule Mix.Tasks.ExMoney.MigrateTransactionsInfo do |> Enum.filter(fn {_, v} -> v != nil end) |> Enum.into(%{}) - transaction = Repo.get(Transaction, info.transaction_id) + transaction = Transactions.get_transaction!(info.transaction_id) - Transaction.update_changeset(transaction, %{extra: info_map}) - |> Repo.update + Transactions.update_transaction!(transaction, %{extra: info_map}) end end end diff --git a/priv/repo/migrations/20180215212022_add_sync_log.exs b/priv/repo/migrations/20180215212022_add_sync_log.exs new file mode 100644 index 00000000..6fefb8fb --- /dev/null +++ b/priv/repo/migrations/20180215212022_add_sync_log.exs @@ -0,0 +1,15 @@ +defmodule ExMoney.Repo.Migrations.AddSyncLog do + use Ecto.Migration + + def change do + create table(:sync_log) do + add :uid, :string + add :action, :string, null: false + add :entity, :string, null: false + add :payload, :map, null: false + add :synced_at, :naive_datetime + + timestamps() + end + end +end diff --git a/priv/repo/migrations/20180217124810_add_uid_to_transactions.exs b/priv/repo/migrations/20180217124810_add_uid_to_transactions.exs new file mode 100644 index 00000000..aea44af8 --- /dev/null +++ b/priv/repo/migrations/20180217124810_add_uid_to_transactions.exs @@ -0,0 +1,9 @@ +defmodule ExMoney.Repo.Migrations.AddUidToTransactions do + use Ecto.Migration + + def change do + alter table(:transactions) do + add :uid, :string + end + end +end diff --git a/priv/repo/migrations/20180310203724_rename_uid_to_uuid.exs b/priv/repo/migrations/20180310203724_rename_uid_to_uuid.exs new file mode 100644 index 00000000..1f612417 --- /dev/null +++ b/priv/repo/migrations/20180310203724_rename_uid_to_uuid.exs @@ -0,0 +1,8 @@ +defmodule ExMoney.Repo.Migrations.RenameUidToUuid do + use Ecto.Migration + + def change do + rename table(:sync_log), :uid, to: :uuid + rename table(:transactions), :uid, to: :uuid + end +end diff --git a/test/lib/rule_processor_test.exs b/test/lib/rule_processor_test.exs index b3048715..5a8d36cf 100644 --- a/test/lib/rule_processor_test.exs +++ b/test/lib/rule_processor_test.exs @@ -1,7 +1,7 @@ defmodule ExMoney.RuleProcessorTest do use ExUnit.Case - alias ExMoney.{RuleProcessor, Repo, Transaction} + alias ExMoney.{RuleProcessor, Transactions} import ExMoney.Factory @@ -46,7 +46,7 @@ defmodule ExMoney.RuleProcessorTest do RuleProcessor.handle_cast({:process, transaction.id}, %{}) - updated_transaction = Repo.get(Transaction, transaction.id) + updated_transaction = Transactions.get_transaction!(transaction.id) assert updated_transaction.category_id == category_2.id assert updated_transaction.rule_applied @@ -64,7 +64,7 @@ defmodule ExMoney.RuleProcessorTest do RuleProcessor.handle_cast({:process, transaction.id}, %{}) - updated_transaction = Repo.get(Transaction, transaction.id) + updated_transaction = Transactions.get_transaction!(transaction.id) refute updated_transaction.category_id == category.id refute updated_transaction.rule_applied @@ -83,7 +83,7 @@ defmodule ExMoney.RuleProcessorTest do RuleProcessor.handle_cast({:process, transaction.id}, %{}) - updated_transaction = Repo.get(Transaction, transaction.id) + updated_transaction = Transactions.get_transaction!(transaction.id) refute updated_transaction.category_id == category.id refute updated_transaction.rule_applied @@ -111,14 +111,14 @@ defmodule ExMoney.RuleProcessorTest do RuleProcessor.handle_cast({:process, transfer_transaction.id}, %{}) - updated_transaction = Repo.get(Transaction, transfer_transaction.id) - cash_transaction = Repo.get_by(Transaction, category_id: category.id, account_id: cash_account.id) - saltedge_transaction = Repo.get_by(Transaction, id: transfer_transaction.id) + updated_transaction = Transactions.get_transaction!(transfer_transaction.id) + cash_transaction = Transactions.get_transaction_by!(category_id: category.id, account_id: cash_account.id) assert updated_transaction.rule_applied + assert updated_transaction.category_id == category.id + assert cash_transaction.account_id == cash_account.id assert cash_transaction.amount == Decimal.mult(updated_transaction.amount, Decimal.new(-1)) - assert saltedge_transaction.category_id == category.id end end @@ -156,10 +156,10 @@ defmodule ExMoney.RuleProcessorTest do :timer.sleep(500) - tr_1 = Repo.get(Transaction, tr_1.id) - tr_2 = Repo.get(Transaction, tr_2.id) - tr_3 = Repo.get(Transaction, tr_3.id) - tr_4 = Repo.get(Transaction, tr_4.id) + tr_1 = Transactions.get_transaction!(tr_1.id) + tr_2 = Transactions.get_transaction!(tr_2.id) + tr_3 = Transactions.get_transaction!(tr_3.id) + tr_4 = Transactions.get_transaction!(tr_4.id) assert tr_1.rule_applied assert tr_1.category_id == rule.target_id diff --git a/test/support/factory.ex b/test/support/factory.ex index 4223f8c6..16c3aee8 100644 --- a/test/support/factory.ex +++ b/test/support/factory.ex @@ -40,7 +40,7 @@ defmodule ExMoney.Factory do account = build(:account) {:ok, made_on} = Date.new(2016, 09, 01) - %ExMoney.Transaction{ + %ExMoney.Transactions.Transaction{ saltedge_transaction_id: sequence(:saltedge_transaction_id, &(&1)) + 1, mode: "normal", status: "post",