Make clear difference between minimum balance and minimum payment
[hcoop/portal.git] / remind / remind.sml
1 structure Remind :> REMIND =
2 struct
3
4 open Config
5
6 structure C = PgClient
7
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
14 fun main _ =
15 let
16 val db = C.conn dbstring
17
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
29 val minimum = 900.0 * real shares / real totalShares * 2.0
30 in
31 if amount >= minimum then
32 ()
33 else
34 let
35 val m = Mail.mopen ()
36 fun write msg = Mail.mwrite (m, msg)
37
38 val minBal = 900.0 * real shares / real totalShares * 2.0
39 in
40 if amount < 0.0 then
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
53 if amount < 0.0 then
54 (write "Your HCoop balance is negative. This means that you've paid less than you've\n";
55 write "been charged to date. Our bylaws allow our board of directors to vote you out\n";
56 write "of the co-op, without any obligation to contact you first, when your balance\n";
57 write "stays negative for three months. Please make a payment as soon as possible, so\n";
58 write "that we don't need to do this!\n\n")
59 else
60 (write "Your HCoop balance has dropped below your requested minimum, based on your\n";
61 write "sliding scale pledge amount. Please make a payment as soon as you can.\n\n");
62
63 write "Your balance: US$";
64 write (printReal amount);
65 write "\nTotal number of members linked to your balance: ";
66 write (Int.toString members);
67 write "\nTotal pledge amount: ";
68 write (Int.toString shares);
69 write "\nRequested minimum balance: US$";
70 write (printReal minBal);
71 write "\nPayment to get there: US$";
72 write (printReal (minBal - amount));
73
74 write "\n\nYour minimum was calculated by dividing our total monthly expenses ($900) by the\n";
75 write "sum of all members' pledge amounts, multiplying by your pledge amount, and then\n";
76 write "multiplying by 2. That is, the amount covers your share of two months' expenses.\n\n";
77
78 write "To make a payment, visit:\n";
79 write " https://members.hcoop.net/\n";
80 write "and use the PayPal or Google Checkout link.\n";
81
82 write "\nIf for whatever reason you don't plan to pay the amount suggested in this e-mail,\n";
83 write "_please_ don't stay silent. Reply to this message explaining your circumstances.\n";
84 write "We are doing limited-time monetary grants on request, due to the extra costs\n";
85 write "associated with setting up our new servers.\n";
86
87 ignore (Mail.mclose m)
88 end
89 end
90 | doOne _ = raise Fail "Bad SQL row"
91 in
92 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";
93 C.close db;
94 OS.Process.success
95 end handle C.Sql s => (print ("SQL failure: " ^ s ^ "\n");
96 OS.Process.failure)
97
98 end