diff --git a/greg/migrations/0001_initial.py b/greg/migrations/0001_initial.py index c83c5a082bb0d243204f99a32b486b5e633eadf4..941e50e061e2a3d74e5594582f4805dd1e8c9506 100644 --- a/greg/migrations/0001_initial.py +++ b/greg/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 3.2.5 on 2021-08-12 10:52 +# Generated by Django 3.2.5 on 2021-08-19 12:47 import datetime import dirtyfields.dirtyfields @@ -157,9 +157,6 @@ class Migration(migrations.Migration): ('role', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='person_roles', to='greg.role')), ('unit', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='unit_person_role', to='greg.organizationalunit')), ], - options={ - 'abstract': False, - }, bases=(dirtyfields.dirtyfields.DirtyFieldsMixin, models.Model), ), migrations.CreateModel( @@ -211,6 +208,10 @@ class Migration(migrations.Migration): model_name='sponsor', constraint=models.UniqueConstraint(fields=('feide_id',), name='unique_feide_id'), ), + migrations.AddConstraint( + model_name='personrole', + constraint=models.UniqueConstraint(fields=('person_id', 'role_id', 'unit_id', 'start_date', 'end_date'), name='person_role_unique'), + ), migrations.AddConstraint( model_name='personconsent', constraint=models.UniqueConstraint(fields=('person', 'consent'), name='person_consent_unique'), diff --git a/greg/models.py b/greg/models.py index f8fddf6448f16c4e89b0deecff230b1b61a7f3dc..2a81c1e1a699aebf8ae86d9734edc7f5067d2712 100644 --- a/greg/models.py +++ b/greg/models.py @@ -103,6 +103,14 @@ class PersonRole(BaseModel): "Sponsor", on_delete=models.PROTECT, related_name="sponsor_role" ) + class Meta: + constraints = [ + models.UniqueConstraint( + fields=["person_id", "role_id", "unit_id", "start_date", "end_date"], + name="person_role_unique", + ) + ] + def __repr__(self): return "{}(id={!r}, person={!r}, role={!r})".format( self.__class__.__name__, self.pk, self.person, self.role diff --git a/greg/tests/api/test_person.py b/greg/tests/api/test_person.py index d0736f280d4bdd51fb6a73a62829325b50c01bb2..f919737287e992b60c3bf061023a1702e5ee9849 100644 --- a/greg/tests/api/test_person.py +++ b/greg/tests/api/test_person.py @@ -11,6 +11,8 @@ from greg.models import ( Role, OrganizationalUnit, Consent, + Person, + PersonRole, ) @@ -350,3 +352,28 @@ def test_remove_person( reverse("person_identity-list", kwargs={"person_id": person_foo.id}) ) assert len(response.json()["results"]) == 0 + + +@pytest.mark.django_db +def test_add_duplicate_role_fails( + client, person_foo: Person, person_foo_role: PersonRole +): + url = reverse("person_role-list", kwargs={"person_id": person_foo.id}) + roles_for_person = client.get(url).json()["results"] + + assert len(roles_for_person) == 1 + + role_data = { + "role": person_foo_role.role_id, + "start_date": person_foo_role.start_date, + "end_date": person_foo_role.end_date, + "registered_by": person_foo_role.registered_by, + "unit": person_foo_role.unit_id, + } + response = client.post(url, role_data) + # If the role cannot be create the return code is 400 + assert response.status_code == status.HTTP_400_BAD_REQUEST + + # Check that there is still only one role attached to the person + roles_for_person = client.get(url).json()["results"] + assert len(roles_for_person) == 1 diff --git a/greg/tests/api/test_sponsor.py b/greg/tests/api/test_sponsor.py index 8c4906a4b711d7049811ac2ca290807fbe26ef23..06e5eb0c74045772401d143b3e2f7eb1171e88d4 100644 --- a/greg/tests/api/test_sponsor.py +++ b/greg/tests/api/test_sponsor.py @@ -3,25 +3,6 @@ from rest_framework import status from rest_framework.reverse import reverse -from greg.models import Role, Sponsor, OrganizationalUnit, PersonRole, Person - - -@pytest.fixture -def person_foo_role( - person_foo: Person, - role_test_guest: Role, - sponsor_guy: Sponsor, - unit_foo: OrganizationalUnit, -) -> PersonRole: - return PersonRole.objects.create( - person=person_foo, - role=role_test_guest, - start_date="2021-08-02", - end_date="2021-08-06", - registered_by=sponsor_guy, - unit=unit_foo, - ) - @pytest.mark.django_db def test_add_sponsor(client): diff --git a/greg/tests/conftest.py b/greg/tests/conftest.py index 41edf5695e8067d408c302f5bd2302c04e75bbbd..63646efdd9947cd04f5ec31079c855447e8b73b2 100644 --- a/greg/tests/conftest.py +++ b/greg/tests/conftest.py @@ -6,7 +6,14 @@ from django.contrib.auth import get_user_model import pytest -from greg.models import Person, Sponsor, PersonIdentity, Role, OrganizationalUnit +from greg.models import ( + Person, + Sponsor, + PersonIdentity, + Role, + OrganizationalUnit, + PersonRole, +) # faker spams the logs with localisation warnings # see https://github.com/joke2k/faker/issues/753 @@ -82,3 +89,20 @@ def role_test_guest() -> Role: @pytest.fixture def unit_foo() -> OrganizationalUnit: return OrganizationalUnit.objects.create(orgreg_id="12345", name_en="foo_unit") + + +@pytest.fixture +def person_foo_role( + person_foo: Person, + role_test_guest: Role, + sponsor_guy: Sponsor, + unit_foo: OrganizationalUnit, +) -> PersonRole: + return PersonRole.objects.create( + person=person_foo, + role=role_test_guest, + start_date="2021-08-02", + end_date="2021-08-06", + registered_by=sponsor_guy, + unit=unit_foo, + )