Repository: modoboa/modoboa (language: python)

Issue:
Missing information on import error
* Modoboa: 2.0.3

Hi. If you try to import a csv with entities and there is a quota error, the message is generic and does not tell which domain is responsible for the error.


Discussion / hints:
The error message is (in french) : _Quota du domaine dépassé_

Fix patch:
diff --git a/modoboa/admin/models/domain.py b/modoboa/admin/models/domain.py
index 66bb412a5..8b8fc0a1b 100644
--- a/modoboa/admin/models/domain.py
+++ b/modoboa/admin/models/domain.py
@@ -74,7 +74,7 @@ class Meta:
 
     def __init__(self, *args, **kwargs):
         """Save name for further use."""
-        super(Domain, self).__init__(*args, **kwargs)
+        super().__init__(*args, **kwargs)
         self.old_mail_homes = None
         self.oldname = self.name
         self.old_dkim_key_length = self.dkim_key_length
@@ -320,7 +320,7 @@ def save(self, *args, **kwargs):
         if self.old_dkim_key_length != self.dkim_key_length:
             self.dkim_public_key = ""
             self.dkim_private_key_path = ""
-        super(Domain, self).save(*args, **kwargs)
+        super().save(*args, **kwargs)
 
     def delete(self, fromuser, keepdir=False):
         """Custom delete method."""
@@ -336,7 +336,7 @@ def delete(self, fromuser, keepdir=False):
         elif self.mailbox_set.count():
             Quota.objects.filter(username__contains="@%s" % self.name).delete()
             ungrant_access_to_objects(self.mailbox_set.all())
-        super(Domain, self).delete()
+        super().delete()
 
     def __str__(self):
         return smart_text(self.name)
@@ -364,27 +364,27 @@ def from_csv(self, user, row):
         if domains_must_have_authorized_mx and not user.is_superuser:
             if not lib.domain_has_authorized_mx(self.name):
                 raise BadRequest(
-                    _("No authorized MX record found for domain {}")
+                    _("{}: no authorized MX record found for domain")
                     .format(self.name)
                 )
         try:
             self.quota = int(row[2].strip())
         except ValueError:
             raise BadRequest(
-                _("Invalid quota value for domain '{}'")
+                _("{}: invalid quota value for domain")
                 .format(self.name)
             )
         try:
             self.default_mailbox_quota = int(row[3].strip())
         except ValueError:
             raise BadRequest(
-                _("Invalid default mailbox quota value for domain '{}'")
+                _("{}: invalid default mailbox quota value for domain")
                 .format(self.name)
             )
         if self.quota != 0 and self.default_mailbox_quota > self.quota:
             raise BadRequest(
-                _("Default mailbox quota cannot be greater than domain "
-                  "quota")
+                _("{}: default mailbox quota cannot be greater than domain "
+                  "quota").format(self.name)
             )
         self.enabled = (row[4].strip().lower() in ["true", "1", "yes", "y"])
         core_signals.can_create_object.send(
diff --git a/modoboa/admin/models/mailbox.py b/modoboa/admin/models/mailbox.py
index 0eda0fef9..4f0021429 100644
--- a/modoboa/admin/models/mailbox.py
+++ b/modoboa/admin/models/mailbox.py
@@ -257,7 +257,9 @@ def set_quota(self, value=None, override_rules=False):
             if old_quota:
                 quota_usage -= old_quota
             if quota_usage + self.quota > self.domain.quota:
-                raise lib_exceptions.BadRequest(_("Domain quota exceeded"))
+                raise lib_exceptions.BadRequest(
+                    _("{}: domain quota exceeded").format(self.domain.name)
+                )
 
     def get_quota(self):
         """Get quota limit.


Test patch:
diff --git a/modoboa/admin/tests/test_import_.py b/modoboa/admin/tests/test_import_.py
index 6c4ab117e..3494f5a86 100644
--- a/modoboa/admin/tests/test_import_.py
+++ b/modoboa/admin/tests/test_import_.py
@@ -66,17 +66,18 @@ def test_domain_import_bad_syntax(self):
         f = ContentFile("domain; domain1.com; XX; 100; True",
                         name="domains.csv")
         response = self.client.post(url, {"sourcefile": f})
-        self.assertContains(response, "Invalid quota value")
+        self.assertContains(response, "domain1.com: invalid quota value")
         f = ContentFile("domain; domain1.com; 100; XX; True",
                         name="domains.csv")
         response = self.client.post(url, {"sourcefile": f})
-        self.assertContains(response, "Invalid default mailbox quota")
+        self.assertContains(
+            response, "domain1.com: invalid default mailbox quota")
         f = ContentFile("domain; domain1.com; 10; 100; True",
                         name="domains.csv")
         response = self.client.post(url, {"sourcefile": f})
         self.assertContains(
             response,
-            "Default mailbox quota cannot be greater than domain quota")
+            "domain1.com: default mailbox quota cannot be greater than domain quota")
 
     @mock.patch.object(dns.resolver.Resolver, "resolve")
     @mock.patch("socket.getaddrinfo")
@@ -97,7 +98,8 @@ def test_domain_import_with_mx_check(self, mock_getaddrinfo, mock_query):
                 "sourcefile": f
             }
         )
-        self.assertContains(resp, "No authorized MX record found for domain")
+        self.assertContains(
+            resp, "test3.com: no authorized MX record found for domain")
 
         mock_getaddrinfo.side_effect = utils.mock_ip_query_result
         f = ContentFile(
@@ -253,7 +255,7 @@ def test_import_quota_too_big(self):
             reverse("admin:identity_import"),
             {"sourcefile": f, "crypt_password": True}
         )
-        self.assertIn("Domain quota exceeded", resp.content.decode())
+        self.assertIn("test.com: domain quota exceeded", resp.content.decode())
 
     def test_import_missing_quota(self):
         f = ContentFile(b"""