Index: pykota/trunk/docs/edpykota.sgml
===================================================================
--- pykota/trunk/docs/edpykota.sgml (revision 2237)
+++ pykota/trunk/docs/edpykota.sgml (revision 2452)
@@ -60,4 +60,30 @@
   
   <para>
+    In addition to <literal>balance</literal> and <literal>quota</literal>, several other values
+    are allowed for the <literal>--limitby</literal> command line option :
+    <itemizedlist>
+      <listitem>
+        <para>
+          <literal>noquota</literal> : the user or group can print without any limitation, but
+          accounting continues to be done.
+        </para>
+      </listitem>  
+      <listitem>
+        <para>
+          <literal>nochange</literal> : the user can print without any limitation, but
+          accounting is not done.
+          This value is not supported for users groups.
+        </para>
+      </listitem>  
+      <listitem>
+        <para>
+          <literal>noprint</literal> : the user can not print.
+          This value is not supported for users groups.
+        </para>
+      </listitem>  
+    </itemizedlist>  
+  </para>
+  
+  <para>
     Here's an incomplete list of features :
     
@@ -101,10 +127,10 @@
       <listitem>
         <para>
-          Choose the way you will limit printing : either by page quota or by account balance value ;
+          Choose the way you will limit printing ;
         </para>
       </listitem>
       <listitem>
         <para>
-          Reset page counters.
+          Reset page counters ;
         </para>
       </listitem>
Index: pykota/trunk/TODO
===================================================================
--- pykota/trunk/TODO (revision 2450)
+++ pykota/trunk/TODO (revision 2452)
@@ -28,11 +28,4 @@
           a freemoney/freepages attribute which will be decremented/incremented
           **before** the real one.
-          
-        - Very good idea from Martin Oesterund : comment field for
-          payments !!!
-          
-        - Another one : add a third value like 'nocheck' or something
-          like that to allow an user to print with no limitation while
-          still updating the balance and number of pages printed.
           
         - Make the SNMP and PJL delay and number of loops a new configuration
@@ -82,13 +75,7 @@
           PyKota for example, or something like that.
         
-        - Finish implementation of quota-then-balance and 
-          balance-then-quota.
-          
         - Introduce the pkusers command.
           pkusers --nogroup
           pkusers --ingroups gr1,gr2,... --outgroups gr3,gr4,...
-          
-        - Allow the --ingroups command line option to repykota, to
-          report only users members of the specified groups.
           
         - Documentation...
Index: pykota/trunk/pykota/tool.py
===================================================================
--- pykota/trunk/pykota/tool.py (revision 2409)
+++ pykota/trunk/pykota/tool.py (revision 2452)
@@ -485,13 +485,15 @@
         # first we check any group the user is a member of
         for group in self.storage.getUserGroups(user) :
-            grouppquota = self.storage.getGroupPQuota(group, printer)
-            # for the printer and all its parents
-            for gpquota in [ grouppquota ] + grouppquota.ParentPrintersGroupPQuota :
-                if gpquota.Exists :
-                    action = self.checkGroupPQuota(gpquota)
-                    if action == "DENY" :
-                        return action
-                    elif action == "WARN" :    
-                        warned = 1
+            # No need to check anything if the group is in noquota mode
+            if group.LimitBy != "noquota" :
+                grouppquota = self.storage.getGroupPQuota(group, printer)
+                # for the printer and all its parents
+                for gpquota in [ grouppquota ] + grouppquota.ParentPrintersGroupPQuota :
+                    if gpquota.Exists :
+                        action = self.checkGroupPQuota(gpquota)
+                        if action == "DENY" :
+                            return action
+                        elif action == "WARN" :    
+                            warned = 1
                         
         # Then we check the user's account balance
Index: pykota/trunk/pykota/storages/sql.py
===================================================================
--- pykota/trunk/pykota/storages/sql.py (revision 2388)
+++ pykota/trunk/pykota/storages/sql.py (revision 2452)
@@ -514,7 +514,7 @@
             self.doModify("UPDATE users SET balance=%s WHERE id=%s" % (self.doQuote(newbalance), self.doQuote(user.ident)))
             
-    def writeNewPayment(self, user, amount) :        
+    def writeNewPayment(self, user, amount, comment="") :
         """Adds a new payment to the payments history."""
-        self.doModify("INSERT INTO payments (userid, amount) VALUES (%s, %s)" % (self.doQuote(user.ident), self.doQuote(amount)))
+        self.doModify("INSERT INTO payments (userid, amount, description) VALUES (%s, %s, %s)" % (self.doQuote(user.ident), self.doQuote(amount), self.doQuote(comment)))
         
     def writeLastJobSize(self, lastjob, jobsize, jobprice) :        
Index: pykota/trunk/pykota/storages/ldapstorage.py
===================================================================
--- pykota/trunk/pykota/storages/ldapstorage.py (revision 2418)
+++ pykota/trunk/pykota/storages/ldapstorage.py (revision 2452)
@@ -382,6 +382,13 @@
                 user.Payments = []
                 for payment in fields.get("pykotaPayments", []) :
-                    (date, amount) = payment.split(" # ")
-                    user.Payments.append((date, float(amount)))
+                    try :
+                        (date, amount, description) = payment.split(" # ")
+                    except ValueError :
+                        # Payment with no description (old Payment)
+                        (date, amount) = payment.split(" # ")
+                        description = ""
+                    else :    
+                        description = self.databaseToUserCharset(description)
+                    user.Payments.append((date, float(amount), description))
             user.Exists = 1
         return user
@@ -935,10 +942,10 @@
         return self.doModify(user.idbalance, fields)         
             
-    def writeNewPayment(self, user, amount) :        
+    def writeNewPayment(self, user, amount, comment="") :
         """Adds a new payment to the payments history."""
         payments = []
         for payment in user.Payments :
-            payments.append("%s # %s" % (payment[0], str(payment[1])))
-        payments.append("%s # %s" % (str(DateTime.now()), str(amount)))
+            payments.append("%s # %s # %s" % (payment[0], str(payment[1]), payment[2]))
+        payments.append("%s # %s # %s" % (str(DateTime.now()), str(amount), self.userCharsetToDatabase(comment)))
         fields = {
                    "pykotaPayments" : payments,
@@ -1304,8 +1311,8 @@
         entries = [u for u in [self.getUser(name) for name in self.getAllUsersNames(uname)] if u.Exists]
         if entries :
-            result = [ ("username", "amount", "date") ]
+            result = [ ("username", "amount", "date", "description") ]
             for entry in entries :
-                for (date, amount) in entry.Payments :
-                    result.append((entry.Name, amount, date))
+                for (date, amount, description) in entry.Payments :
+                    result.append((entry.Name, amount, date, description))
             return result        
         
Index: pykota/trunk/pykota/reporter.py
===================================================================
--- pykota/trunk/pykota/reporter.py (revision 2302)
+++ pykota/trunk/pykota/reporter.py (revision 2452)
@@ -85,87 +85,5 @@
             warncount = int(quota.WarnCount or 0)
         
-        #balance
-        if entry.LimitBy and (entry.LimitBy.lower() == "balance") :    
-            if balance == 0.0 :
-                if entry.OverCharge > 0 :
-                    datelimit = "DENY"
-                    reached = "+B"
-                else :    
-                    # overcharging by a negative or nul factor means user is always allowed to print
-                    # TODO : do something when printer prices are negative as well !
-                    datelimit = ""
-                    reached = "-B"
-            elif balance < 0 :
-                datelimit = "DENY"
-                reached = "+B"
-            elif balance <= self.tool.config.getPoorMan() :
-                datelimit = "WARNING"
-                reached = "?B"
-            else :    
-                datelimit = ""
-                reached = "-B"
-
-        #balance-then-quota
-        elif entry.LimitBy and (entry.LimitBy.lower() == "balance-then-quota") :
-            if balance <= 0 :
-                if (quota.HardLimit is not None) and (pagecounter >= quota.HardLimit) :
-                    datelimit = "DENY"
-                elif (quota.HardLimit is None) and (quota.SoftLimit is not None) and (pagecounter >= quota.SoftLimit) :
-                    datelimit = "DENY"
-                elif quota.DateLimit is not None :
-                    now = DateTime.now()
-                    datelimit = DateTime.ISO.ParseDateTime(quota.DateLimit)
-                    if now >= datelimit :
-                        datelimit = "QUOTA_DENY"
-                else :
-                    datelimit = ""
-                reached = ( ((datelimit == "DENY" ) and "+B") or "-Q")
-                datelimit = ( ((datelimit == "QUOTA_DENY") and "DENY") or datelimit)
-            elif balance <= self.tool.config.getPoorMan() :
-                if (quota.HardLimit is not None) and (pagecounter >= quota.HardLimit) :
-                    datelimit = "WARNING"
-                elif (quota.HardLimit is None) and (quota.SoftLimit is not None) and (pagecounter >= quota.SoftLimit) :
-                    datelimit = "WARNING"
-                elif quota.DateLimit is not None :
-                    now = DateTime.now()
-                    datelimit = DateTime.ISO.ParseDateTime(quota.DateLimit)
-                    if now >= datelimit :
-                        datelimit = "QUOTA_DENY"
-                else :
-                    datelimit = ""
-                reached = ( ((datelimit == "WARNING" ) and "?B") or "+Q")
-                datelimit = ( ((datelimit == "QUOTA_DENY") and "WARNING") or datelimit)
-            else :
-                datelimit = ""
-                reached = "-B"
-
-        #Quota-then-balance
-        elif entry.LimitBy and (entry.LimitBy.lower() == "quota-then-balance") :
-            if (quota.HardLimit is not None) and (pagecounter >= quota.HardLimit) :
-                datelimit = "DENY"
-            elif (quota.HardLimit is None) and (quota.SoftLimit is not None) and (pagecounter >= quota.SoftLimit) :
-                datelimit = "DENY"
-            elif quota.DateLimit is not None :
-                now = DateTime.now()
-                datelimit = DateTime.ISO.ParseDateTime(quota.DateLimit)
-                if now >= datelimit :
-                    datelimit = "DENY"
-            else :
-                datelimit = ""
-                
-            reached = (((quota.SoftLimit is not None) and (pagecounter >= quota.SoftLimit) and "+") or "-") + "Q"
-
-            if (datelimit == "DENY") and (reached == "-Q") and (balance > self.tool.config.getPoorMan()) :
-                datelimit = ""
-                reached = "-B"
-            else :
-                reached = (((datelimit == "DENY") and (self.tool.config.getPoorMan() < balance ) and "-B") or reached)
-                if (datelimit == "DENY") and (self.tool.config.getPoorMan() < balance) :
-                    datelimit = ""
-                reached = (((datelimit == "DENY") and (0.0 < balance <= self.tool.config.getPoorMan()) and "?B") or reached)
-                datelimit = (((datelimit == "DENY") and (0.0 < balance <= self.tool.config.getPoorMan()) and "WARNING") or datelimit)
-
-        #Quota
-        else :
+        if (not entry.LimitBy) or (entry.LimitBy.lower() == "quota") :
             if (quota.HardLimit is not None) and (pagecounter >= quota.HardLimit) :    
                 datelimit = "DENY"
@@ -180,4 +98,34 @@
                 datelimit = ""
             reached = (((quota.SoftLimit is not None) and (pagecounter >= quota.SoftLimit) and "+") or "-") + "Q"
+        else :
+            if entry.LimitBy.lower() == "balance" :
+                if balance == 0.0 :
+                    if entry.OverCharge > 0 :
+                        datelimit = "DENY"
+                        reached = "+B"
+                    else :    
+                        # overcharging by a negative or nul factor means user is always allowed to print
+                        # TODO : do something when printer prices are negative as well !
+                        datelimit = ""
+                        reached = "-B"
+                elif balance < 0 :
+                    datelimit = "DENY"
+                    reached = "+B"
+                elif balance <= self.tool.config.getPoorMan() :
+                    datelimit = "WARNING"
+                    reached = "?B"
+                else :    
+                    datelimit = ""
+                    reached = "-B"
+            elif entry.LimitBy.lower() == "noquota" :
+                reached = "NQ"
+                datelimit = ""
+            elif entry.LimitBy.lower() == "nochange" :
+                reached = "NC"
+                datelimit = ""
+            else :
+                # noprint
+                reached = "NP"
+                datelimit = "DENY"
             
         strbalance = ("%5.2f" % balance)[:10]
Index: pykota/trunk/pykota/storage.py
===================================================================
--- pykota/trunk/pykota/storage.py (revision 2451)
+++ pykota/trunk/pykota/storage.py (revision 2452)
@@ -59,5 +59,5 @@
         self.AccountBalance = float(self.AccountBalance or 0.0) - amount
         
-    def setAccountBalance(self, balance, lifetimepaid) :    
+    def setAccountBalance(self, balance, lifetimepaid, comment="") :
         """Sets the user's account balance in case he pays more money."""
         diff = float(lifetimepaid or 0.0) - float(self.LifeTimePaid or 0.0)
@@ -65,5 +65,5 @@
         try :
             self.parent.writeUserAccountBalance(self, balance, lifetimepaid)
-            self.parent.writeNewPayment(self, diff)
+            self.parent.writeNewPayment(self, diff, comment)
         except PyKotaStorageError, msg :    
             self.parent.rollbackTransaction()
@@ -80,5 +80,6 @@
         except AttributeError :    
             limitby = "quota"
-        if limitby in ["quota", "balance", "quota-then-balance", "balance-then-quota"] :
+        if limitby in ["quota", "balance", \
+                       "noquota", "noprint", "nochange"] :
             self.parent.writeUserLimitBy(self, limitby)
             self.LimitBy = limitby
@@ -120,5 +121,5 @@
         except AttributeError :    
             limitby = "quota"
-        if limitby in ["quota", "balance"] :
+        if limitby in ["quota", "balance", "noquota"] :
             self.parent.writeGroupLimitBy(self, limitby)
             self.LimitBy = limitby
Index: pykota/trunk/pykota/version.py
===================================================================
--- pykota/trunk/pykota/version.py (revision 2447)
+++ pykota/trunk/pykota/version.py (revision 2452)
@@ -22,5 +22,5 @@
 #
 
-__version__ = "1.23alpha29_unofficial"
+__version__ = "1.23alpha30_unofficial"
 
 __doc__ = "PyKota : a complete Printing Quota Solution for CUPS and LPRng."
Index: pykota/trunk/bin/edpykota
===================================================================
--- pykota/trunk/bin/edpykota (revision 2344)
+++ pykota/trunk/bin/edpykota (revision 2452)
@@ -100,4 +100,5 @@
                        
   -n | --noquota       Doesn't set a quota but only does accounting.
+                       This is the same as --limitby noquota.
   
   -r | --reset         Resets the actual page counter for the user
@@ -112,8 +113,13 @@
                        by its account balance or by its page quota.
                        The default value is 'quota'. Allowed values
-                       are 'quota' 'balance' 'quota-then-balance' and
-                       'balance-then-quota'.
-                       WARNING : quota-then-balance and balance-then-quota
-                       are not yet implemented.
+                       are 'quota' 'balance' 'noquota' 'noprint' 
+                       and 'nochange' :
+                       
+                         - quota : limit by number of pages per printer.
+                         - balance : limit by number of credits in account.
+                         - noquota : no limit, accounting still done.
+                         - nochange : no limit, accounting not done. 
+                         - noprint : printing is denied. 
+                       NB : nochange and noprint are not supported for groups.
                        
   -b | --balance b     Sets the user's account balance to b.                     
@@ -125,4 +131,8 @@
                        Groups don't have a real balance, but the
                        sum of their users' account balance.
+                       
+  -C | --comment txt   Defines some informational text to be associated
+                       with a change to an user's account balance.
+                       Only meaningful if -b | --balance is also used.
                        
   -S | --softlimit sl  Sets the quota soft limit to sl pages.                       
@@ -237,4 +247,19 @@
         
         softlimit = hardlimit = None
+        
+        if options["noquota"] :
+            options["limitby"] = "noquota"
+            
+        limitby = options["limitby"]
+        if limitby :
+            limitby = limitby.strip().lower()
+        if limitby :
+            if limitby not in ('quota', 'balance', 'noquota', \
+                                        'noprint', 'nochange') :
+                raise PyKotaToolError, _("Invalid limitby value %s") % options["limitby"]
+            if limitby in ('noquota', 'nochange') :    
+                options["noquota"] = 1
+            if (limitby in ('nochange', 'noprint')) and options["groups"] :    
+                raise PyKotaToolError, _("Invalid limitby value %s") % options["limitby"]
 
         used = options["used"]
@@ -288,10 +313,4 @@
                     charges = [charges[0], None]
                     
-        limitby = options["limitby"]
-        if limitby :
-            limitby = limitby.strip().lower()
-        if limitby and (limitby not in ('quota', 'balance', 'quota-then-balance', 'balance-then-quota')) :    
-            raise PyKotaToolError, _("Invalid limitby value %s") % options["limitby"]
-            
         if options["ingroups"] :    
             groupnames = [gname.strip() for gname in options["ingroups"].split(',')]
@@ -474,9 +493,9 @@
                                     newbalance = float(entry.AccountBalance or 0.0) + balancevalue
                                     newlifetimepaid = float(entry.LifeTimePaid or 0.0) + balancevalue
-                                    entry.setAccountBalance(newbalance, newlifetimepaid)
+                                    entry.setAccountBalance(newbalance, newlifetimepaid, options["comment"])
                                 else :
                                     diff = balancevalue - float(entry.AccountBalance or 0.0)
                                     newlifetimepaid = float(entry.LifeTimePaid or 0.0) + diff
-                                    entry.setAccountBalance(balancevalue, newlifetimepaid)
+                                    entry.setAccountBalance(balancevalue, newlifetimepaid, options["comment"])
                                 changed[entry.Name]["balance"] = balance
                                 
@@ -506,7 +525,13 @@
         defaults = { \
                      "printer" : "*", \
+                     "comment" : "", \
                    }
-        short_options = "vhdo:c:l:b:i:naugrp:P:S:H:G:RU:"
-        long_options = ["help", "version", "overcharge=", "charge=", "delete", "limitby=", "balance=", "ingroups=", "noquota", "add", "users", "groups", "reset", "hardreset", "prototype=", "printer=", "softlimit=", "hardlimit=", "pgroups=", "used="]
+        short_options = "vhdo:c:C:l:b:i:naugrp:P:S:H:G:RU:"
+        long_options = ["help", "version", "comment=", \
+                        "overcharge=", "charge=", "delete", "limitby=", \
+                        "balance=", "ingroups=", "noquota", "add", "users", \
+                        "groups", "reset", "hardreset", "prototype=", \
+                        "printer=", "softlimit=", "hardlimit=", "pgroups=", \
+                        "used="]
         
         # Initializes the command line tool
@@ -538,4 +563,5 @@
         options["used"] = options["U"] or options["used"]
         options["overcharge"] = options["o"] or options["overcharge"]
+        options["comment"] = options["C"] or options["comment"] or defaults["comment"]
         
         if options["help"] :
@@ -553,4 +579,6 @@
         elif options["groups"] and (options["balance"] or options["ingroups"] or options["used"] or options["overcharge"]) :
             raise PyKotaToolError, _("incompatible options, see help.")
+        elif options["comment"] and not options["balance"] :    
+            raise PyKotaToolError, _("incompatible options, see help.")
         else :
             retcode = editor.main(args, options)
Index: pykota/trunk/bin/repykota
===================================================================
--- pykota/trunk/bin/repykota (revision 2344)
+++ pykota/trunk/bin/repykota (revision 2452)
@@ -55,4 +55,7 @@
   -g | --groups        Generates a report on group quota instead of users.
   
+  -i | --ingroups g1[,g2...]  Only lists users who are members of these
+                              groups. Reserved to PyKota Administrators.
+  
   -P | --printer p     Report quotas on this printer only. Actually p can
                        use wildcards characters to select only
@@ -92,7 +95,21 @@
                 # no username, means all usernames
                 ugnames = [ "*" ]
+                
+            if options["ingroups"] :
+                groupsnames = options["ingroups"].split(",")
+                groups = [self.storage.getGroup(gname) for gname in groupsnames]
+                members = {}
+                for group in groups :
+                    if not group.Exists :
+                        self.printInfo("Group %s doesn't exist." % group.Name, "warn")
+                    else :    
+                        for user in self.storage.getGroupMembers(group) :
+                            members[user.Name] = user
+                ugnames = [ m for m in members.keys() if self.matchString(m, ugnames) ]
         else :        
-            # not the root user
             # reports only the current user
+            if options["ingroups"] :
+                raise PyKotaToolError, _("Option --ingroups is reserved to PyKota Administrators.")
+                
             username = pwd.getpwuid(os.geteuid())[0]
             if options["groups"] :
@@ -118,6 +135,6 @@
                      "printer" : "*", \
                    }
-        short_options = "vhugP:"
-        long_options = ["help", "version", "users", "groups", "printer="]
+        short_options = "vhugi:P:"
+        long_options = ["help", "version", "users", "groups", "ingroups=", "printer="]
         
         # Initializes the command line tool
@@ -134,4 +151,5 @@
         options["groups"] = options["g"] or options["groups"]
         options["printer"] = options["P"] or options["printer"] or defaults["printer"]
+        options["ingroups"] = options["i"] or options["ingroups"]
         
         if options["help"] :
Index: pykota/trunk/bin/cupspykota
===================================================================
--- pykota/trunk/bin/cupspykota (revision 2449)
+++ pykota/trunk/bin/cupspykota (revision 2452)
@@ -823,4 +823,9 @@
         
         if self.Action != "DENY" :
+            if self.User.LimitBy == "noprint" :
+                self.printInfo(_("User %s is not allowed to print at this time.") % self.UserName, "warn")
+                self.Action = "DENY"
+                
+        if self.Action != "DENY" :
             # If printing is still allowed at this time, we
             # need to extract the billing code information from the database.
@@ -841,7 +846,10 @@
             # No need to do this if the job is denied, this way we
             # save some database queries.
-            self.logdebug("Checking user %s print quota entry on printer %s" \
+            if self.User.LimitBy in ('noquota', 'nochange') :
+                self.logdebug("User %s is allowed to print with no limit, no need to check quota." % self.UserName)
+            else :
+                self.logdebug("Checking user %s print quota entry on printer %s" \
                                     % (self.UserName, self.PrinterName))
-            self.Action = self.warnUserPQuota(self.UserPQuota)
+                self.Action = self.warnUserPQuota(self.UserPQuota)
             
         # exports some new environment variables
@@ -912,7 +920,12 @@
             self.printInfo(_("Job size : %i") % self.JobSize)
             
-            # update the quota for the current user on this printer 
-            self.printInfo(_("Updating user %s's quota on printer %s") % (self.UserName, self.PrinterName))
-            self.JobPrice = self.UserPQuota.increasePagesUsage(self.JobSize)
+            if self.User.LimitBy == "nochange" :
+                # no need to update the quota for the current user on this printer
+                self.printInfo(_("User %s's quota on printer %s won't be modified") % (self.UserName, self.PrinterName))
+                self.JobPrice = self.UserPQuota.computeJobPrice(self.JobSize)
+            else :
+                # update the quota for the current user on this printer 
+                self.printInfo(_("Updating user %s's quota on printer %s") % (self.UserName, self.PrinterName))
+                self.JobPrice = self.UserPQuota.increasePagesUsage(self.JobSize)
             
             # adds the current job to history    
Index: pykota/trunk/initscripts/ldap/pykota.schema
===================================================================
--- pykota/trunk/initscripts/ldap/pykota.schema (revision 2318)
+++ pykota/trunk/initscripts/ldap/pykota.schema (revision 2452)
@@ -244,11 +244,6 @@
         SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
 
-# pykotaTemporaryDenied
-attributetype ( 1.3.6.1.4.1.16868.1.1.36 NAME 'pykotaTemporaryDenied'
-        DESC 'Indicates if the user is temporarily denied to print'
-        SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
-
 # pykotaPassThrough
-attributetype ( 1.3.6.1.4.1.16868.1.1.37 NAME 'pykotaPassThrough'
+attributetype ( 1.3.6.1.4.1.16868.1.1.36 NAME 'pykotaPassThrough'
         DESC 'Indicates if the printer is in pass through mode'
         SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )
@@ -280,5 +275,5 @@
         DESC 'PyKota User Quota on a Printer'
         MUST ( cn $ pykotaUserName $ pykotaPrinterName )
-        MAY  ( pykotaMaxJobSize $ pykotaTemporaryDenied $ pykotaPageCounter $ pykotaLifePageCounter $ pykotaSoftLimit $ pykotaHardLimit $ pykotaDateLimit $ pykotaWarnCount ) )
+        MAY  ( pykotaMaxJobSize $ pykotaPageCounter $ pykotaLifePageCounter $ pykotaSoftLimit $ pykotaHardLimit $ pykotaDateLimit $ pykotaWarnCount ) )
         
 # pykotaGroupPQuota        
Index: pykota/trunk/initscripts/postgresql/README.postgresql
===================================================================
--- pykota/trunk/initscripts/postgresql/README.postgresql (revision 2303)
+++ pykota/trunk/initscripts/postgresql/README.postgresql (revision 2452)
@@ -57,6 +57,19 @@
   database schema are included. You may have to run all
   of them or only some of them, depending on your 
-  version of PyKota.
+  version of PyKota. Usually running them several times, for
+  example if you used several different development versions
+  of PyKota, won't do any harm.
   
+  * An SQL script to upgrade a 1.22 PyKota Storage DataBase to
+    1.23 is included. Launch it this way on the Quota Storage Server :
+    
+        $ psql -U postgres pykota
+        pykota=# \i upgrade-to-1.23.sql
+        pykota=# \q
+        $
+        
+    This script adds many fields to existing tables, and a table to
+    manage billing codes.
+    
   * An SQL script to upgrade a 1.20 PyKota Storage DataBase to
     1.21 is included. Launch it this way on the Quota Storage Server :
Index: pykota/trunk/initscripts/postgresql/pykota-postgresql.sql
===================================================================
--- pykota/trunk/initscripts/postgresql/pykota-postgresql.sql (revision 2342)
+++ pykota/trunk/initscripts/postgresql/pykota-postgresql.sql (revision 2452)
@@ -87,5 +87,4 @@
                         datelimit TIMESTAMP,
                         maxjobsize INT4,
-                        temporarydenied BOOLEAN DEFAULT 'f',
                         warncount INT4 DEFAULT 0); 
 CREATE INDEX userpquota_u_id_ix ON userpquota (userid);
@@ -155,4 +154,5 @@
                        userid INT4 REFERENCES users(id),
                        amount FLOAT,
+                       description TEXT,
                        date TIMESTAMP DEFAULT now());
 CREATE INDEX payments_date_ix ON payments (date);
Index: pykota/trunk/initscripts/postgresql/upgrade-to-1.23.sql
===================================================================
--- pykota/trunk/initscripts/postgresql/upgrade-to-1.23.sql (revision 2342)
+++ pykota/trunk/initscripts/postgresql/upgrade-to-1.23.sql (revision 2452)
@@ -22,5 +22,5 @@
 --
 -- This script has to be used if you already
--- have a pre-1.23alpha12 version of PyKota to upgrade
+-- have a pre-1.23alpha30 version of PyKota to upgrade
 -- your database schema. 
 --
@@ -34,6 +34,4 @@
 ALTER TABLE groups ADD COLUMN description TEXT;
 ALTER TABLE userpquota ADD COLUMN maxjobsize INT4;
-ALTER TABLE userpquota ADD COLUMN temporarydenied BOOLEAN;
-ALTER TABLE userpquota ALTER COLUMN temporarydenied SET DEFAULT 'f';
 ALTER TABLE grouppquota ADD COLUMN maxjobsize INT4;
 ALTER TABLE printers ADD COLUMN maxjobsize INT4;
@@ -42,4 +40,7 @@
 ALTER TABLE jobhistory ADD COLUMN precomputedjobsize INT4;
 ALTER TABLE jobhistory ADD COLUMN precomputedjobprice FLOAT;
+ALTER TABLE payments ADD COLUMN description TEXT;
+
+ALTER TABLE userpquota DROP COLUMN temporarydenied;
 
 --
@@ -47,5 +48,4 @@
 --
 -- Just to be sure
-UPDATE userpquota SET temporarydenied='f';
 UPDATE printers SET passthrough='f';
 
Index: pykota/trunk/NEWS
===================================================================
--- pykota/trunk/NEWS (revision 2449)
+++ pykota/trunk/NEWS (revision 2452)
@@ -22,4 +22,22 @@
 PyKota NEWS :
        
+    - 1.23alpha30 :
+    
+        - Database changes : you MUST use upgrade-to-1.23.sql again if you
+          use PostgreSQL, or copy the new schema and restart your LDAP
+          server if you use LDAP.
+          
+        - The -C | --comment command line switch was added to edpykota to  
+          attach textual information to modifications done to a user's
+          account balance.
+          
+        - The -l | --limitby command line switch to edpykota was modified  
+          to allow 'noquota', 'nochange', and 'noprint'. See the edpykota
+          command's help or manual page for details.
+          
+        - The -i | --ingroups command line switch to repykota allows  
+          PyKota Administrators to only lists members of the specified
+          group or groups (separated with commas).
+          
     - 1.23alpha29 :
     
