Skip to content

Commit 6828afb

Browse files
committed
add sanity checks
1 parent ed1f362 commit 6828afb

File tree

1 file changed

+124
-0
lines changed

1 file changed

+124
-0
lines changed

lib/algora/admin/migration.ex

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
defmodule Algora.Admin.Migration do
2+
@moduledoc false
3+
import Ecto.Query
4+
5+
alias Algora.Accounts.User
6+
alias Algora.Payments.Transaction
7+
alias Algora.Repo
8+
9+
@balances_file ".local/db/balance-2025-02-10.json"
10+
11+
def get_actual_balances(type) do
12+
with {:ok, content} <- File.read(@balances_file),
13+
{:ok, data} <- Jason.decode(content) do
14+
Enum.map(data[Atom.to_string(type)], fn %{"provider_login" => login, "balance" => balance} ->
15+
%{
16+
provider_login: login,
17+
balance: Money.new!(:USD, Decimal.new(balance))
18+
}
19+
end)
20+
else
21+
error -> raise "Failed to load balances: #{inspect(error)}"
22+
end
23+
end
24+
25+
def get_balances(type) do
26+
Enum.filter(get_balances(), &(&1.type == type))
27+
end
28+
29+
def get_balances do
30+
user_txs =
31+
from t in Transaction,
32+
group_by: [t.user_id, t.type],
33+
select: %{
34+
user_id: t.user_id,
35+
type: t.type,
36+
net_amount: sum(t.net_amount)
37+
}
38+
39+
user_balances =
40+
from ut in subquery(user_txs),
41+
group_by: ut.user_id,
42+
select: %{
43+
user_id: ut.user_id,
44+
balance:
45+
sum(
46+
fragment(
47+
"""
48+
CASE
49+
WHEN ? = 'credit' THEN ?
50+
WHEN ? = 'debit' THEN money_negate(?)
51+
WHEN ? = 'charge' THEN ?
52+
WHEN ? = 'transfer' THEN money_negate(?)
53+
ELSE ('USD', 0)::money_with_currency
54+
END
55+
""",
56+
ut.type,
57+
ut.net_amount,
58+
ut.type,
59+
ut.net_amount,
60+
ut.type,
61+
ut.net_amount,
62+
ut.type,
63+
ut.net_amount
64+
)
65+
)
66+
}
67+
68+
query =
69+
from ub in subquery(user_balances),
70+
join: u in User,
71+
on: u.id == ub.user_id,
72+
where: ub.balance != fragment("('USD', 0)::money_with_currency"),
73+
order_by: [desc: u.type, desc: ub.balance, asc: u.provider_login],
74+
select: %{
75+
type: u.type,
76+
provider_login: u.provider_login,
77+
balance: ub.balance
78+
}
79+
80+
query
81+
|> Repo.all()
82+
|> Enum.map(fn
83+
user ->
84+
{currency, amount} = user.balance
85+
%{user | balance: Money.new!(currency, amount)}
86+
end)
87+
end
88+
89+
def diff_balances(type) do
90+
actual = Enum.map(get_actual_balances(type), &Map.take(&1, [:provider_login, :balance]))
91+
current = Enum.map(get_balances(type), &Map.take(&1, [:provider_login, :balance]))
92+
93+
actual_map = Map.new(actual, &{&1.provider_login, &1.balance})
94+
current_map = Map.new(current, &{&1.provider_login, &1.balance})
95+
96+
all_logins =
97+
MapSet.union(
98+
MapSet.new(Map.keys(actual_map)),
99+
MapSet.new(Map.keys(current_map))
100+
)
101+
102+
differences =
103+
Enum.reduce(all_logins, [], fn login, acc ->
104+
actual_balance = Map.get(actual_map, login)
105+
current_balance = Map.get(current_map, login)
106+
107+
cond do
108+
actual_balance == current_balance ->
109+
acc
110+
111+
actual_balance == nil ->
112+
[{:extra_in_current, login, current_balance} | acc]
113+
114+
current_balance == nil ->
115+
[{:missing_in_current, login, actual_balance} | acc]
116+
117+
true ->
118+
[{:different, login, actual_balance, current_balance} | acc]
119+
end
120+
end)
121+
122+
Enum.sort_by(differences, &elem(&1, 1))
123+
end
124+
end

0 commit comments

Comments
 (0)