cvsimport
[hcoop/zz_old/portal.git] / money.sml
index 133e37b..f4423ac 100644 (file)
--- a/money.sml
+++ b/money.sml
@@ -11,7 +11,7 @@ type transaction = {id :int, descr : string, amount : real, d : string, stamp :
 fun mkTransactionRow [id, descr, amount, d, stamp] =
     {id = C.intFromSql id, descr = C.stringFromSql descr, amount = C.realFromSql amount,
      d = C.stringFromSql d, stamp = C.timestampFromSql stamp}
-  | mkTransactionRow row = raise Fail ("Bad transaction row : " ^ makeSet id row)
+  | mkTransactionRow row = Init.rowError ("transaction", row)
 
 fun addTransaction (descr, amount, d) =
     let
@@ -19,8 +19,8 @@ fun addTransaction (descr, amount, d) =
        val id = nextSeq (db, "TransactionSeq")
     in
        C.dml db ($`INSERT INTO Transaction (id, descr, amount, d, stamp)
-                    VALUES (^id, ^(C.stringToSql descr), ^(C.realToSql amount), ^(C.stringToSql d), CURRENT_TIMESTAMP)`);
-       C.intFromSql id
+                    VALUES (^(C.intToSql id), ^(C.stringToSql descr), ^(C.realToSql amount), ^(C.stringToSql d), CURRENT_TIMESTAMP)`);
+       id
     end
 
 fun lookupTransaction id =
@@ -83,10 +83,10 @@ val mkUserRow' =
     fn (trn :: rest) =>
        (if C.isNull trn then false else true,
        mkUserRow rest)
-     | row => raise Fail ("Bad listUsers row: " ^ makeSet id row)
+     | row => Init.rowError ("listUsers", row)
 
 fun listUsers trn =
-    C.map (getDb ()) mkUserRow' ($`SELECT trn, id, name, rname, bal, joined
+    C.map (getDb ()) mkUserRow' ($`SELECT trn, id, name, rname, bal, joined, app, shares, paypal, checkout
                                   FROM WebUser LEFT OUTER JOIN Charge ON usr = id AND trn = ^(C.intToSql trn)
                                   ORDER BY name`)
 
@@ -97,7 +97,7 @@ type charge = {trn : int, usr : int, amount : real}
 
 fun mkChargeRow [trn, usr, amount] =
     {trn = C.intFromSql trn, usr = C.intFromSql usr, amount = C.realFromSql amount}
-  | mkChargeRow row = raise Fail ("Bad charge row : " ^ makeSet id row)
+  | mkChargeRow row = Init.rowError ("charge", row)
 
 fun addCharge {trn, usr, amount} =
     ignore (C.dml (getDb ()) ($`INSERT INTO Charge (trn, usr, amount)
@@ -109,7 +109,7 @@ fun listCharges trn =
 
 val mkChargeRow' =
     fn (name :: rest) => (C.stringFromSql name, mkChargeRow rest)
-     | row => raise Fail ("Bad name+charge row: " ^ makeSet id row)
+     | row => Init.rowError ("name+charge", row)
 
 fun listChargesWithNames trn =
     C.map (getDb ()) mkChargeRow' ($`SELECT name, trn, usr, amount FROM Charge, WebUser
@@ -126,9 +126,9 @@ fun clearCharges trn =
 
        fun clearCharge [bal, amount] =
            ignore (C.dml db ($`UPDATE Balance
-                               SET amount = amount - ^amount
-                               WHERE id = ^bal`))
-         | clearCharge row = raise Fail ("Bad clearCharge row : " ^ makeSet id row)
+                               SET amount = amount - ^(C.stringFromSql amount)
+                               WHERE id = ^(C.stringFromSql bal)`))
+         | clearCharge row = Init.rowError ("clearCharge", row)
     in
        C.app db clearCharge ($`SELECT bal, SUM(amount) FROM Charge, WebUser
                                WHERE trn = ^(C.intToSql trn)
@@ -137,13 +137,15 @@ fun clearCharges trn =
        ignore (C.dml db ($`DELETE FROM Charge WHERE trn = ^(C.intToSql trn)`))
     end
 
-fun applyCharges db trn =
+fun applyCharges trn =
     let
+       val db = getDb ()
+
        fun applyCharge [bal, amount] =
            ignore (C.dml db ($`UPDATE Balance
-                               SET amount = amount + ^amount
-                               WHERE id = ^bal`))
-         | applyCharge row = raise Fail ("Bad applyCharge row : " ^ makeSet id row)
+                               SET amount = amount + ^(C.stringFromSql amount)
+                               WHERE id = ^(C.stringFromSql bal)`))
+         | applyCharge row = Init.rowError ("applyCharge", row)
     in
        C.app db applyCharge ($`SELECT bal, SUM(amount) FROM Charge, WebUser
                                WHERE trn = ^(C.intToSql trn)
@@ -165,7 +167,7 @@ fun addEvenCharges (trn, usrs) =
                                VALUES (^(C.intToSql trn), ^(C.intToSql usr), ^(C.realToSql split))`))
     in
        app addCharge usrs;
-       applyCharges db trn
+       applyCharges trn
     end
 
 (* Automated hosting charges *)
@@ -202,7 +204,7 @@ fun addHostingCharges {trn, cutoff, cost, usage} =
                        in
                            walkNvs (rest,
                                     SM.insert (umap, name, extra),
-                                    amount - extra)
+                                    amount + extra)
                        end
                    else
                        walkNvs (rest, umap, amount)
@@ -211,23 +213,64 @@ fun addHostingCharges {trn, cutoff, cost, usage} =
 
        val (umap, amount) = walkNvs (nvs, SM.empty, #amount tran)
 
-       val payers = Group.groupMembers paying
-       val even = amount / real (length payers)
+       val db = getDb ()
+
+       val shares =
+           case C.oneRow db ($`SELECT SUM(shares)
+                                 FROM WebUser JOIN Membership ON usr = WebUser.id AND grp = ^(C.intToSql paying)`) of
+               [n] => C.intFromSql n
+             | row => Init.rowError ("Bad addHostingCharges share count result", row)
 
-       fun doUser (usr : Init.user, umap)  =
+       val even = amount / real shares
+
+       fun doUser ([uid, uname, shares], umap)  =
            let
+               val uid = C.intFromSql uid
+               val uname = C.stringFromSql uname
+               val shares = C.intFromSql shares
+
                val (charge, umap) =
-                   case SM.find (umap, #name usr) of
-                       NONE => (even, umap)
-                     | SOME extra => (even + extra, #1 (SM.remove (umap, #name usr)))
+                   case SM.find (umap, uname) of
+                       NONE => (even * real shares, umap)
+                     | SOME extra => (even * real shares - extra, #1 (SM.remove (umap, uname)))
            in
-               addCharge {trn = trn, usr = #id usr, amount = charge};
+               addCharge {trn = trn, usr = uid, amount = charge};
                umap
            end
+
+       val _ = if SM.numItems (C.fold db doUser umap
+                                      ($`SELECT id, name, shares
+                                           FROM WebUser JOIN Membership ON usr = WebUser.id AND grp = ^(C.intToSql paying)`))
+                  = 0 then
+                   applyCharges trn
+               else
+                   raise Fail "Usage description contains an unknown username"
+
+       val usageFile = TextIO.openOut (Init.scratchDir ^ "/usage/" ^ Int.toString trn)
     in
-       if SM.numItems (foldl doUser umap payers) = 0 then
-           ()
-       else
-           raise Fail "Usage description contains an unknown username"
+       TextIO.output (usageFile, usage);
+       TextIO.closeOut usageFile
     end
-end
\ No newline at end of file
+
+fun equalizeBalances () =
+    ignore (C.dml (getDb ()) ($`UPDATE Balance SET amount = (SELECT SUM(amount) FROM Charge JOIN WebUser ON usr = WebUser.id WHERE bal = Balance.id)`))
+
+fun lookupHostingUsage trn =
+    let
+       val usageFile = TextIO.openIn (Init.scratchDir ^ "/usage/" ^ Int.toString trn)
+
+       fun loop acc =
+           case TextIO.inputLine usageFile of
+               NONE => String.concat (List.rev acc)
+             | SOME line => loop (line :: acc)
+    in
+       SOME (loop [])
+       before TextIO.closeIn usageFile
+    end handle _ => NONE
+
+fun costBase amt =
+    case C.oneRow (getDb ()) ($`SELECT ^(C.realToSql amt) / SUM(shares) FROM WebUserPaying`) of
+       [share] => C.realFromSql share
+      | row => Init.rowError ("Bad costBase result", row)
+
+end