| 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 perMonth = 900.0 * real shares / real totalShares |
| 30 | val deposit = 900.0 / real totalShares * 3.0 |
| 31 | val headsUp = deposit + perMonth * 2.0 |
| 32 | in |
| 33 | if amount >= headsUp then |
| 34 | () |
| 35 | else |
| 36 | let |
| 37 | val m = Mail.mopen () |
| 38 | fun write msg = Mail.mwrite (m, msg) |
| 39 | in |
| 40 | if amount < deposit 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 < deposit 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, 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") |
| 60 | else |
| 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"); |
| 66 | |
| 67 | write "Your balance: US$"; |
| 68 | write (printReal (amount - deposit)); |
| 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); |
| 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)); |
| 77 | |
| 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 and then multiplying by 3. That\n"; |
| 80 | write "is, the amount covers a minimal share of three months' expenses.\n\n"; |
| 81 | |
| 82 | write "To make a payment, visit:\n"; |
| 83 | write " https://members.hcoop.net/\n"; |
| 84 | write "and use the PayPal or Google Checkout link.\n"; |
| 85 | |
| 86 | write "\nIf for whatever reason you don't plan to pay the amount suggested in this e-mail,\n"; |
| 87 | write "_please_ don't stay silent. Reply to this message explaining your circumstances.\n"; |
| 88 | |
| 89 | ignore (Mail.mclose m) |
| 90 | end |
| 91 | end |
| 92 | | doOne _ = raise Fail "Bad SQL row" |
| 93 | in |
| 94 | 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"; |
| 95 | C.close db; |
| 96 | OS.Process.success |
| 97 | end handle C.Sql s => (print ("SQL failure: " ^ s ^ "\n"); |
| 98 | OS.Process.failure) |
| 99 | |
| 100 | end |