From 62ca0ea1ffc5a1c0232bf7d22595b3dc85e16945 Mon Sep 17 00:00:00 2001
From: "J.-S. Caux" <J.S.Caux@uva.nl>
Date: Sun, 18 Nov 2018 18:05:13 +0100
Subject: [PATCH] Solve bug by making UnregisteredAuthor FK to Profile instead
 of 1To1

---
 .../migrations/0053_auto_20181118_1758.py     | 21 ++++++++++++
 journals/models.py                            | 13 +-------
 profiles/forms.py                             |  3 +-
 profiles/views.py                             | 33 +++++++++++++++----
 scipost/forms.py                              |  2 +-
 5 files changed, 51 insertions(+), 21 deletions(-)
 create mode 100644 journals/migrations/0053_auto_20181118_1758.py

diff --git a/journals/migrations/0053_auto_20181118_1758.py b/journals/migrations/0053_auto_20181118_1758.py
new file mode 100644
index 000000000..f2a41d010
--- /dev/null
+++ b/journals/migrations/0053_auto_20181118_1758.py
@@ -0,0 +1,21 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2018-11-18 16:58
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('journals', '0052_journal_refereeing_period'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='unregisteredauthor',
+            name='profile',
+            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='profiles.Profile'),
+        ),
+    ]
diff --git a/journals/models.py b/journals/models.py
index 8cab95489..f9d4e0629 100644
--- a/journals/models.py
+++ b/journals/models.py
@@ -36,23 +36,12 @@ from scipost.fields import ChoiceArrayField
 class UnregisteredAuthor(models.Model):
     first_name = models.CharField(max_length=100)
     last_name = models.CharField(max_length=100)
-    profile = models.OneToOneField(
+    profile = models.ForeignKey(
         'profiles.Profile', on_delete=models.SET_NULL, null=True, blank=True)
 
     def __str__(self):
         return self.last_name + ', ' + self.first_name
 
-    def merge(self, unregistered_author):
-        """
-        Merge another UnregisteredAuthor into this object.
-        """
-        if unregistered_author == self:  # Do nothing.
-            return
-
-        self.profile = unregistered_author.profile
-        self.save()
-        unregistered_author.delete()
-
 
 class PublicationAuthorsTable(models.Model):
     publication = models.ForeignKey('journals.Publication', related_name='authors')
diff --git a/profiles/forms.py b/profiles/forms.py
index 38ba6550b..0ec066920 100644
--- a/profiles/forms.py
+++ b/profiles/forms.py
@@ -129,8 +129,7 @@ class ProfileMergeForm(forms.Form):
 
         profile.topics.add(*profile_old.topics.all())
 
-        if hasattr(profile_old, 'unregisteredauthor') and profile_old.unregisteredauthor:
-            profile.unregisteredauthor.merge(profile_old.unregisteredauthor)
+        UnregisteredAuthor.objects.filter(profile=profile_old).update(profile=profile)
 
         # Merge email
         profile_old.emails.exclude(
diff --git a/profiles/views.py b/profiles/views.py
index 24ea72e9d..4ece47f98 100644
--- a/profiles/views.py
+++ b/profiles/views.py
@@ -132,18 +132,39 @@ class ProfileCreateView(PermissionsMixin, CreateView):
 def profile_match(request, profile_id, from_type, pk):
     """
     Links an existing Profile to one of existing
-    Contributor, UnregisteredAuthor, RefereeInvitation, RegistrationInvitation.
+    Contributor, UnregisteredAuthor, RefereeInvitation or RegistrationInvitation.
+
+    Profile relates to Contributor as OneToOne.
+    Matching is thus only allowed if there are no duplicate objects for these elements.
+
+    For matching the Profile to a Contributor, the following preconditions are defined:
+    - the Profile has no association to another Contributor
+    - the Contributor has no association to another Profile
+    If these are not met, no action is taken.
     """
     profile = get_object_or_404(Profile, pk=profile_id)
     nr_rows = 0
     if from_type == 'contributor':
+        if hasattr(profile, 'contributor') and profile.contributor.id != pk:
+            messages.error(request,
+                           'Error: cannot math this Profile to this Contributor, '
+                           'since this Profile already has a different Contributor.\n'
+                           'Please merge the duplicate Contributors first.')
+            return redirect(reverse('profiles:profiles'))
+        contributor = get_object_or_404(Contributor, pk=pk)
+        if contributor.profile and contributor.profile.id != profile.id:
+            messages.error(request,
+                           'Error: cannot match this Profile to this Contributor, '
+                           'since this Contributor already has a different Profile.\n'
+                           'Please merge the duplicate Profiles first.')
+            return redirect(reverse('profiles:profiles'))
+        # Preconditions are met, match:
         nr_rows = Contributor.objects.filter(pk=pk).update(profile=profile)
         # Give priority to the email coming from Contributor
-        if nr_rows == 1:
-            profile.emails.update(primary=False)
-            email, __ = ProfileEmail.objects.get_or_create(
-                profile=profile, email=get_object_or_404(Contributor, pk=pk).user.email)
-            profile.emails.filter(id=email.id).update(primary=True, still_valid=True)
+        profile.emails.update(primary=False)
+        email, __ = ProfileEmail.objects.get_or_create(
+            profile=profile, email=contributor.user.email)
+        profile.emails.filter(id=email.id).update(primary=True, still_valid=True)
     elif from_type == 'unregisteredauthor':
         nr_rows = UnregisteredAuthor.objects.filter(pk=pk).update(profile=profile)
     elif from_type == 'refereeinvitation':
diff --git a/scipost/forms.py b/scipost/forms.py
index e4a3f09c1..9a2a80710 100644
--- a/scipost/forms.py
+++ b/scipost/forms.py
@@ -532,7 +532,7 @@ class ContributorMergeForm(forms.Form):
         publications = Publication.objects.filter(authors_registered__in=[contrib_from,]).all()
         for publication in publications:
             publication.authors_registered.remove(contrib_from)
-            publication.authors_registared.add(contrib_into)
+            publication.authors_registered.add(contrib_into)
         publications = Publication.objects.filter(authors_claims__in=[contrib_from,]).all()
         for publication in publications:
             publication.authors_claims.remove(contrib_from)
-- 
GitLab