From 1e7f8123de9a6c7c4327ecae087fe5662d94c330 Mon Sep 17 00:00:00 2001 From: kdeterme Date: Wed, 9 Sep 2026 22:59:09 +0200 Subject: [PATCH] fix: resolve VisibilityScope choices attribute error in project document upload --- ...lter_documentfolder_visibility_and_more.py | 23 ++++++++++ loko/documents/models.py | 3 ++ loko/documents/tests.py | 43 +++++++++-------- loko/projects/tests.py | 46 +++++++++++++++++++ loko/projects/views.py | 2 +- 5 files changed, 98 insertions(+), 19 deletions(-) create mode 100644 loko/documents/migrations/0004_alter_documentfolder_visibility_and_more.py diff --git a/loko/documents/migrations/0004_alter_documentfolder_visibility_and_more.py b/loko/documents/migrations/0004_alter_documentfolder_visibility_and_more.py new file mode 100644 index 0000000..7dcefb1 --- /dev/null +++ b/loko/documents/migrations/0004_alter_documentfolder_visibility_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 6.0.7 on 2026-09-09 20:58 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('documents', '0003_documentfolder_visibility_manageddocument_thematics_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='documentfolder', + name='visibility', + field=models.CharField(choices=[('private', 'Privé (Créateur uniquement)'), ('restricted', 'Partagé avec certains utilisateurs'), ('internal', 'Interne (sur base des thématiques et contrats)'), ('scoped', 'Interne et externe (sur base des thématiques et contrats)')], db_index=True, default='internal', max_length=20, verbose_name='Visibility'), + ), + migrations.AlterField( + model_name='manageddocument', + name='visibility', + field=models.CharField(choices=[('private', 'Privé (Créateur uniquement)'), ('restricted', 'Partagé avec certains utilisateurs'), ('internal', 'Interne (sur base des thématiques et contrats)'), ('scoped', 'Interne et externe (sur base des thématiques et contrats)')], db_index=True, default='internal', max_length=20, verbose_name='Visibility'), + ), + ] diff --git a/loko/documents/models.py b/loko/documents/models.py index df87fb0..d6800bf 100644 --- a/loko/documents/models.py +++ b/loko/documents/models.py @@ -118,6 +118,9 @@ class VisibilityScope(models.TextChoices): SCOPED = "scoped", _("Interne et externe (sur base des thématiques et contrats)") +VisibilityScope.CHOICES = VisibilityScope.choices + + class DocumentFolder(models.Model): uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) name = models.CharField(max_length=255, verbose_name=_("Name")) diff --git a/loko/documents/tests.py b/loko/documents/tests.py index 931d252..1430f79 100644 --- a/loko/documents/tests.py +++ b/loko/documents/tests.py @@ -1209,7 +1209,7 @@ class DocumentVisibilityAndIndependenceTests(TestCase): class ProjectDocumentsVisibilityTests(TestCase): def setUp(self): from common.models import UserConfig, Thematic, UserThematics - from contracts.models import Company, Team, CompanyMember + from contracts.models import Company, CompanyTeam, CompanyMember from projects.models import Project, ProjectUserAccess, ProjectTeamAccess from documents.models import ( DocumentAttachment, @@ -1220,7 +1220,7 @@ class ProjectDocumentsVisibilityTests(TestCase): ) from django.contrib.contenttypes.models import ContentType - self.thematic = Thematic.objects.create(name="Voirie", code="VOI") + self.thematic = Thematic.objects.create(name_fr="Voirie", code="VOI") # Internal user self.int_user = User.objects.create_user(username="intern_user", password="pwd") @@ -1235,8 +1235,9 @@ class ProjectDocumentsVisibilityTests(TestCase): self.ext_team_user = User.objects.create_user(username="ext_team_user", password="pwd") self.ext_team_config = UserConfig.objects.create(user=self.ext_team_user, is_intern=False) self.company = Company.objects.create(name="Contractor Corp") - self.team = Team.objects.create(name="Project Team", company=self.company) - CompanyMember.objects.create(user=self.ext_team_user, company=self.company, team=self.team) + self.team = CompanyTeam.objects.create(name="Project Team", company=self.company) + member = CompanyMember.objects.create(name="ext_team_user", user=self.ext_team_user) + member.teams.add(self.team) # External user 3 (no access) self.ext_no_access = User.objects.create_user(username="ext_no_access", password="pwd") @@ -1256,21 +1257,27 @@ class ProjectDocumentsVisibilityTests(TestCase): # Grant team access to team ProjectTeamAccess.objects.create(project=self.project, team=self.team, can_view=True) - # Create Project Folder (SCOPED) - self.project_folder = DocumentFolder.objects.create( - name="P12345 Documents", - slug="project-p12345-boulevard", - application_label="projects", - visibility=VisibilityScope.SCOPED, - created_by=self.int_user, - ) + # Project Folder (SCOPED) project_ct = ContentType.objects.get_for_model(self.project) - DocumentFolderAttachment.objects.create( - folder=self.project_folder, - content_type=project_ct, - object_id=self.project.pk, - is_default=True, - ) + dfa = DocumentFolderAttachment.objects.filter(content_type=project_ct, object_id=self.project.pk, is_default=True).first() + if dfa: + self.project_folder = dfa.folder + self.project_folder.visibility = VisibilityScope.SCOPED + self.project_folder.save() + else: + self.project_folder = DocumentFolder.objects.create( + name="P12345 Documents", + slug="project-p12345-boulevard", + application_label="projects", + visibility=VisibilityScope.SCOPED, + created_by=self.int_user, + ) + DocumentFolderAttachment.objects.create( + folder=self.project_folder, + content_type=project_ct, + object_id=self.project.pk, + is_default=True, + ) # Create Scoped Document self.scoped_doc = ManagedDocument.objects.create( diff --git a/loko/projects/tests.py b/loko/projects/tests.py index 6329a8e..f30fa39 100644 --- a/loko/projects/tests.py +++ b/loko/projects/tests.py @@ -206,5 +206,51 @@ class ProjectViewerPermissionsTest(TestCase): self.assertNotIn(self.proj_roads_strict, viewable) +class ProjectAddDocumentTest(TestCase): + def setUp(self): + from common.models import Role + from projects.signals import ensure_project_document_folder + + self.user = User.objects.create_user(username="projadmin", password="password123") + self.user_config = UserConfig.objects.create(user=self.user, is_intern=True) + admin_role, _ = Role.objects.get_or_create(name='admin') + self.user_config.roles.add(admin_role) + + self.thematic = Thematic.objects.create(code="them_test", name_fr="Them Test", name_nl="Them Test") + self.project = Project.objects.create( + name="Test Project", + created_by=self.user, + status="in_progress" + ) + self.project.thematics.add(self.thematic) + ensure_project_document_folder(self.project) + + self.client = Client() + self.client.login(username="projadmin", password="password123") + + def test_visibility_scope_choices_compatibility(self): + from documents.models import VisibilityScope + self.assertTrue(hasattr(VisibilityScope, 'CHOICES')) + self.assertEqual(dict(VisibilityScope.CHOICES), dict(VisibilityScope.choices)) + + def test_add_document_with_visibility(self): + from django.core.files.uploadedfile import SimpleUploadedFile + from documents.models import ManagedDocument, VisibilityScope + + upload = SimpleUploadedFile("test_doc.pdf", b"%PDF-1.4 test content", content_type="application/pdf") + post_data = { + "files": [upload], + "file_count": "1", + "visibility_0": "internal", + } + url = reverse("projects:projects_add_document", kwargs={"project_id": self.project.id}) + response = self.client.post(url, post_data, follow=True) + self.assertEqual(response.status_code, 200) + + doc = ManagedDocument.objects.filter(title="test_doc").first() + self.assertIsNotNone(doc) + self.assertEqual(doc.visibility, VisibilityScope.INTERNAL) + + diff --git a/loko/projects/views.py b/loko/projects/views.py index 0cc3a1b..e35658f 100644 --- a/loko/projects/views.py +++ b/loko/projects/views.py @@ -595,7 +595,7 @@ def projects_add_document(request, project_id): from documents.permissions import user_can_change_visibility raw_visibility = request.POST.get(f'visibility_{i}', '').strip() visibility = None - if raw_visibility and raw_visibility in dict(VisibilityScope.CHOICES): + if raw_visibility and raw_visibility in VisibilityScope.values: if user_can_change_visibility(request.user): visibility = raw_visibility else: