Index: pykota/trunk/tests/filldb.py
===================================================================
--- pykota/trunk/tests/filldb.py (revision 2702)
+++ pykota/trunk/tests/filldb.py (revision 2706)
@@ -65,5 +65,5 @@
     argsfile.close()    
     before = time.time()
-    os.system('edpykota --arguments arguments.list') 
+    os.system('pkusers --arguments arguments.list') 
     showTiming(number, before)
 
@@ -72,5 +72,5 @@
     sys.stdout.write("Deleting users...\n")
     before = time.time()
-    os.system('edpykota --delete "test-user-*"') 
+    os.system('pkusers --delete "test-user-*"') 
     showTiming(number, before)
     
Index: pykota/trunk/pykota/storages/sql.py
===================================================================
--- pykota/trunk/pykota/storages/sql.py (revision 2698)
+++ pykota/trunk/pykota/storages/sql.py (revision 2706)
@@ -545,4 +545,9 @@
             self.doModify("INSERT INTO groupsmembers (groupid, userid) VALUES (%s, %s)" % (self.doQuote(group.ident), self.doQuote(user.ident)))
             
+    def delUserFromGroup(self, user, group) :    
+        """Removes an user from a group."""
+        self.doModify("DELETE FROM groupsmembers WHERE groupid=%s AND userid=%s" % \
+                       (self.doQuote(group.ident), self.doQuote(user.ident)))
+            
     def addUserPQuota(self, user, printer) :
         """Initializes a user print quota on a printer."""
@@ -564,16 +569,20 @@
                                  self.doQuote(printer.PricePerJob), \
                                  self.doQuote(printer.ident)))
-        
-    def writeUserOverCharge(self, user, factor) :
-        """Sets the user's overcharging coefficient."""
-        self.doModify("UPDATE users SET overcharge=%s WHERE id=%s" % (self.doQuote(factor), self.doQuote(user.ident)))
-        
-    def writeUserLimitBy(self, user, limitby) :    
-        """Sets the user's limiting factor."""
-        self.doModify("UPDATE users SET limitby=%s WHERE id=%s" % (self.doQuote(limitby), self.doQuote(user.ident)))
-        
-    def writeGroupLimitBy(self, group, limitby) :    
-        """Sets the group's limiting factor."""
-        self.doModify("UPDATE groups SET limitby=%s WHERE id=%s" % (self.doQuote(limitby), self.doQuote(group.ident)))
+                                 
+    def saveUser(self, user) :        
+        """Saves the user to the database in a single operation."""
+        self.doModify("UPDATE users SET limitby=%s, email=%s, overcharge=%s, description=%s WHERE id=%s" \
+                               % (self.doQuote(user.LimitBy or 'quota'), \
+                                  self.doQuote(user.Email), \
+                                  self.doQuote(user.OverCharge), \
+                                  self.doQuote(self.userCharsetToDatabase(user.Description)), \
+                                  self.doQuote(user.ident)))
+                                  
+    def saveGroup(self, group) :        
+        """Saves the group to the database in a single operation."""
+        self.doModify("UPDATE groups SET limitby=%s, description=%s WHERE id=%s" \
+                               % (self.doQuote(group.LimitBy or 'quota'), \
+                                  self.doQuote(self.userCharsetToDatabase(group.Description)), \
+                                  self.doQuote(group.ident)))
         
     def writeUserPQuotaDateLimit(self, userpquota, datelimit) :    
Index: pykota/trunk/pykota/storages/ldapstorage.py
===================================================================
--- pykota/trunk/pykota/storages/ldapstorage.py (revision 2697)
+++ pykota/trunk/pykota/storages/ldapstorage.py (revision 2706)
@@ -982,4 +982,8 @@
                 group.Members.append(user)
                 
+    def delUserFromGroup(self, user, group) :    
+        """Removes an user from a group."""
+        raise "Not Implemented !" # TODO !!!
+                
     def addUserPQuota(self, user, printer) :
         """Initializes a user print quota on a printer."""
@@ -1032,24 +1036,24 @@
         self.doModify(printer.ident, fields)
         
-    def writeUserOverCharge(self, user, factor) :
-        """Sets the user's overcharging coefficient."""
-        fields = {
-                   "pykotaOverCharge" : str(factor),
-                 }
-        self.doModify(user.ident, fields)
-        
-    def writeUserLimitBy(self, user, limitby) :    
-        """Sets the user's limiting factor."""
-        fields = {
-                   "pykotaLimitBy" : limitby,
-                 }
-        self.doModify(user.ident, fields)         
-        
-    def writeGroupLimitBy(self, group, limitby) :    
-        """Sets the group's limiting factor."""
-        fields = {
-                   "pykotaLimitBy" : limitby,
-                 }
-        self.doModify(group.ident, fields)         
+    def saveUser(self, user) :
+        """Saves the user to the database in a single operation."""
+        newfields = {
+                       "pykotaLimitBy" : (user.LimitBy or "quota"),
+                       "pykotaOverCharge" : str(user.OverCharge),
+                    }   
+        if user.Email :
+            newfields.update({self.info["usermail"]: user.Email})
+        if user.Description is not None : 
+            newfields.update({"description": self.userCharsetToDatabase(user.Description)})
+        self.doModify(user.ident, newfields)
+        
+    def saveGroup(self, group) :
+        """Saves the group to the database in a single operation."""
+        newfields = {
+                       "pykotaLimitBy" : (group.LimitBy or "quota"),
+                    }   
+        if group.Description is not None : 
+            newfields.update({"description": self.userCharsetToDatabase(group.Description)})
+        self.doModify(group.ident, newfields)
         
     def writeUserPQuotaDateLimit(self, userpquota, datelimit) :    
Index: pykota/trunk/pykota/storage.py
===================================================================
--- pykota/trunk/pykota/storage.py (revision 2699)
+++ pykota/trunk/pykota/storage.py (revision 2706)
@@ -96,11 +96,11 @@
         if limitby in ["quota", "balance", \
                        "noquota", "noprint", "nochange"] :
-            self.parent.writeUserLimitBy(self, limitby)
             self.LimitBy = limitby
+            self.isDirty = True
         
     def setOverChargeFactor(self, factor) :    
         """Sets the user's overcharging coefficient."""
-        self.parent.writeUserOverCharge(self, factor)
         self.OverCharge = factor
+        self.isDirty = True
         
     def delete(self) :    
@@ -137,6 +137,14 @@
             limitby = "quota"
         if limitby in ["quota", "balance", "noquota"] :
-            self.parent.writeGroupLimitBy(self, limitby)
             self.LimitBy = limitby
+            self.isDirty = True
+        
+    def addUserToGroup(self, user) :
+        """Adds an user to an users group."""
+        self.parent.addUserToGroup(user, self)
+        
+    def delUserFromGroup(self, user) :
+        """Removes an user from an users group."""
+        self.parent.delUserFromGroup(user, self)
         
     def delete(self) :    
Index: pykota/trunk/bin/pkusers
===================================================================
--- pykota/trunk/bin/pkusers (revision 2705)
+++ pykota/trunk/bin/pkusers (revision 2706)
@@ -297,14 +297,37 @@
                     usersgroups = []
                         
-                if options["description"] :
+                description = options["description"]
+                if description :
                     description = options["description"].strip()
+                    
+                comment = options["comment"]
+                if comment :
+                    comment = options["comment"].strip()
                     
                 nbtotal = len(entries)
                 for i in range(nbtotal) :        
                     entry = entries[i]
-                    if description is not None :        # NB : "" is allowed !
+                    if description is not None : # NB : "" is allowed !
                         entry.setDescription(description)
+                    if limitby :    
+                        entry.setLimitBy(limitby)
+                        
+                    if suffix == "User" :    
+                        if overcharge is not None : # NB : 0 is allowed !     
+                            entry.setOverChargeFactor(overcharge)
+                            
                     entry.save()    
-                    if not options["groups"] :
+                    
+                    if suffix == "User" :
+                        if balance :
+                            if balance.startswith("+") or balance.startswith("-") :
+                                newbalance = float(entry.AccountBalance or 0.0) + balancevalue
+                                newlifetimepaid = float(entry.LifeTimePaid or 0.0) + balancevalue
+                                entry.setAccountBalance(newbalance, newlifetimepaid, comment)
+                            else :
+                                diff = balancevalue - float(entry.AccountBalance or 0.0)
+                                newlifetimepaid = float(entry.LifeTimePaid or 0.0) + diff
+                                entry.setAccountBalance(balancevalue, newlifetimepaid, comment)
+                                        
                         for ugroup in usersgroups :
                             if options["remove"] :
@@ -312,4 +335,5 @@
                             else :
                                 ugroup.addUserToGroup(entry)
+                                
                     percent = 100.0 * float(i) / float(nbtotal)
                     self.display("\r%.02f%%" % percent)
