Repository: modoboa/modoboa

Issue:
Creating/editing an account with aliases fails if that alias already exists
# Impacted versions

* Modoboa: 1.17

# Steps to reproduce

- Create an alias : test-alias@ -> whocares@
- Try the creation of a test account test-bug@ using legacy interface

# Current behavior

```
IntegrityError at /admin/accounts/new/
ERREUR:  la valeur d'une clé dupliquée rompt la contrainte unique « admin_alias_address_internal_701cee88_uniq »
DETAIL:  La clé « (address, internal)=(test-alias@, f) » existe déjà.
```
# Expected behavior

Account creation and update alias to extends its recipients.


Discussion:

Resolving patch:
diff --git a/modoboa/admin/forms/account.py b/modoboa/admin/forms/account.py
--- a/modoboa/admin/forms/account.py
+++ b/modoboa/admin/forms/account.py
@@ -420,10 +420,12 @@ def _update_aliases(self, user, account):
                     alias__address=alias).exists():
                 continue
             local_part, domname = split_mailbox(alias)
-            al = models.Alias(address=alias, enabled=account.is_active)
-            al.domain = models.Domain.objects.get(name=domname)
-            al.save()
-            al.set_recipients([self.mb.full_address])
+            al, _ = models.Alias.objects.get_or_create(
+                address=alias,
+                domain=models.Domain.objects.get(name=domname),
+                defaults={'enabled': account.is_active},
+            )
+            al.add_recipients([self.mb.full_address])
             al.post_create(user)
 
     def _update_sender_addresses(self):
diff --git a/modoboa/admin/models/alias.py b/modoboa/admin/models/alias.py
--- a/modoboa/admin/models/alias.py
+++ b/modoboa/admin/models/alias.py
@@ -137,8 +137,8 @@ def post_create(self, creator):
             for admin in self.domain.admins:
                 grant_access_to_object(admin, self)
 
-    def set_recipients(self, address_list):
-        """Set recipients for this alias.
+    def add_recipients(self, address_list):
+        """Add recipients for this alias.
 
         Special recipients:
         * local mailbox + extension: r_mailbox will be set to local mailbox
@@ -187,6 +187,11 @@ def set_recipients(self, address_list):
                     kwargs["r_mailbox"] = rcpt
             AliasRecipient(**kwargs).save()
 
+    def set_recipients(self, address_list):
+        """Set recipients for this alias."""
+
+        self.add_recipients(address_list)
+
         # Remove old recipients
         self.aliasrecipient_set.exclude(
             address__in=address_list).delete()


Test patch:
diff --git a/modoboa/admin/tests/test_account.py b/modoboa/admin/tests/test_account.py
--- a/modoboa/admin/tests/test_account.py
+++ b/modoboa/admin/tests/test_account.py
@@ -119,6 +119,22 @@ def test_create_account_default_lang(self):
         account = User.objects.get(username=values["username"])
         self.assertEqual(account.language, "fr")
 
+    def test_create_account_with_existing_alias(self):
+        alias = models.Alias.objects.get(address="alias@test.com")
+        alias.set_recipients(["user@external.com"])
+        values = {
+            "email": "tester@test.com",
+            "username": "tester@test.com",
+            "role": "SimpleUsers",
+            "random_password": "on",
+            "quota_act": True,
+            "is_active": True,
+            "stepid": "step2",
+            "aliases": "alias@test.com",
+        }
+        self.ajax_post(reverse("admin:account_add"), values)
+        self.assertTrue(alias.aliasrecipient_set.count() == 2)
+
     def test_aliases_update_on_disable(self):
         """Check if aliases are updated when account is disabled."""
         account = User.objects.get(username="user@test.com")