Commit | Line | Data |
---|---|---|
dda99898 AC |
1 | structure Remind :> REMIND = |
2 | struct | |
3 | ||
4 | open Config | |
5 | ||
6 | structure C = PgClient | |
7 | ||
878a4e49 AC |
8 | fun printReal n = |
9 | if n < 0.0 then | |
10 | "-" ^ Real.fmt (StringCvt.FIX (SOME 2)) (~n) | |
11 | else | |
12 | Real.fmt (StringCvt.FIX (SOME 2)) n | |
13 | ||
dda99898 AC |
14 | fun main _ = |
15 | let | |
16 | val db = C.conn dbstring | |
17 | ||
878a4e49 AC |
18 | val totalShares = case C.oneRow db "SELECT SUM(shares) FROM WebUserPaying" of |
19 | [total] => C.intFromSql total | |
20 | | _ => raise Fail "Bad SUM(shares) row" | |
21 | ||
22 | fun doOne [user, members, shares, amount] = | |
23 | let | |
24 | val user = C.stringFromSql user | |
25 | val members = C.intFromSql members | |
26 | val shares = C.intFromSql shares | |
27 | val amount = C.realFromSql amount | |
28 | ||
bb40c5c6 AC |
29 | val perMonth = 900.0 * real shares / real totalShares |
30 | val deposit = perMonth * 3.0 | |
31 | val headsUp = perMonth * 5.0 | |
878a4e49 | 32 | in |
bb40c5c6 | 33 | if amount >= headsUp then |
878a4e49 AC |
34 | () |
35 | else | |
36 | let | |
37 | val m = Mail.mopen () | |
38 | fun write msg = Mail.mwrite (m, msg) | |
39 | in | |
bb40c5c6 | 40 | if amount < deposit then |
878a4e49 AC |
41 | write "Subject: Your NEGATIVE HCoop balance\n" |
42 | else | |
43 | write "Subject: Your low HCoop balance\n"; | |
44 | write "From: HCoop Payment Reminder Robot <payment"; | |
45 | write emailSuffix; | |
46 | write ">\n"; | |
47 | write "To: "; | |
48 | write user; | |
49 | write emailSuffix; | |
50 | write "\nCc: payment@hcoop.net"; | |
51 | write "\n\n"; | |
52 | ||
bb40c5c6 | 53 | if amount < deposit then |
878a4e49 | 54 | (write "Your HCoop balance is negative. This means that you've paid less than you've\n"; |
bb40c5c6 AC |
55 | write "been charged to date, excluding your required deposit. If your account hasn't\n"; |
56 | write "been frozen yet, expect that to happen very soon. Our bylaws allow our board\n"; | |
57 | write "of directors to vote you out of the co-op, without any obligation to contact\n"; | |
58 | write "you first, when your balance stays negative for three months. Please make a\n"; | |
59 | write "payment as soon as possible, so that we don't need to do this!\n\n") | |
878a4e49 | 60 | else |
bb40c5c6 AC |
61 | (write "With our current dues projections, you have less than two months left until\n"; |
62 | write "your HCoop balance becomes negative, based on your sliding scale pledge amount.\n"; | |
63 | write "Please make a payment as soon as you can. We will freeze your account if your\n"; | |
64 | write "balance does become negative, and the board of directors will probably vote you\n"; | |
65 | write "out of the cooperative shortly thereafter if you don't make a payment.\n\n"); | |
878a4e49 AC |
66 | |
67 | write "Your balance: US$"; | |
bb40c5c6 | 68 | write (printReal (amount - deposit)); |
878a4e49 AC |
69 | write "\nTotal number of members linked to your balance: "; |
70 | write (Int.toString members); | |
71 | write "\nTotal pledge amount: "; | |
72 | write (Int.toString shares); | |
bb40c5c6 AC |
73 | write "\nDeposit: US$"; |
74 | write (printReal deposit); | |
75 | write "\nMinimum amount to pay to not see this message again for two months: US$"; | |
76 | write (printReal (headsUp - amount)); | |
dda99898 | 77 | |
bb40c5c6 AC |
78 | write "\n\nYour deposit requirement was calculated by dividing our total monthly expenses\n"; |
79 | write "($900) by the sum of all members' pledge amounts, multiplying by your pledge amount,\n"; | |
80 | write "and then multiplying by 3. That is, the amount covers your share of three months'\n"; | |
81 | write "expenses.\n\n"; | |
dda99898 | 82 | |
878a4e49 | 83 | write "To make a payment, visit:\n"; |
4763cfb8 | 84 | write " https://members.hcoop.net/\n"; |
878a4e49 | 85 | write "and use the PayPal or Google Checkout link.\n"; |
9a4c122a | 86 | |
9a4c122a AC |
87 | write "\nIf for whatever reason you don't plan to pay the amount suggested in this e-mail,\n"; |
88 | write "_please_ don't stay silent. Reply to this message explaining your circumstances.\n"; | |
89 | write "We are doing limited-time monetary grants on request, due to the extra costs\n"; | |
90 | write "associated with setting up our new servers.\n"; | |
91 | ||
878a4e49 AC |
92 | ignore (Mail.mclose m) |
93 | end | |
94 | end | |
95 | | doOne _ = raise Fail "Bad SQL row" | |
dda99898 | 96 | in |
878a4e49 | 97 | C.app db doOne "SELECT Balance.name, COUNT(*), SUM(WebUserPaying.shares) AS shrs, Balance.amount FROM WebUserPaying JOIN Balance ON WebUserPaying.bal = Balance.id GROUP BY Balance.name, Balance.amount HAVING amount < 10"; |
dda99898 AC |
98 | C.close db; |
99 | OS.Process.success | |
878a4e49 AC |
100 | end handle C.Sql s => (print ("SQL failure: " ^ s ^ "\n"); |
101 | OS.Process.failure) | |
dda99898 AC |
102 | |
103 | end |