fix: resolve VisibilityScope choices attribute error in project document upload

This commit is contained in:
kdeterme 2026-09-09 22:59:09 +02:00
parent 91d7e0b176
commit 1e7f8123de
5 changed files with 98 additions and 19 deletions

View file

@ -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'),
),
]

View file

@ -118,6 +118,9 @@ class VisibilityScope(models.TextChoices):
SCOPED = "scoped", _("Interne et externe (sur base des thématiques et contrats)") SCOPED = "scoped", _("Interne et externe (sur base des thématiques et contrats)")
VisibilityScope.CHOICES = VisibilityScope.choices
class DocumentFolder(models.Model): class DocumentFolder(models.Model):
uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
name = models.CharField(max_length=255, verbose_name=_("Name")) name = models.CharField(max_length=255, verbose_name=_("Name"))

View file

@ -1209,7 +1209,7 @@ class DocumentVisibilityAndIndependenceTests(TestCase):
class ProjectDocumentsVisibilityTests(TestCase): class ProjectDocumentsVisibilityTests(TestCase):
def setUp(self): def setUp(self):
from common.models import UserConfig, Thematic, UserThematics 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 projects.models import Project, ProjectUserAccess, ProjectTeamAccess
from documents.models import ( from documents.models import (
DocumentAttachment, DocumentAttachment,
@ -1220,7 +1220,7 @@ class ProjectDocumentsVisibilityTests(TestCase):
) )
from django.contrib.contenttypes.models import ContentType 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 # Internal user
self.int_user = User.objects.create_user(username="intern_user", password="pwd") 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_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.ext_team_config = UserConfig.objects.create(user=self.ext_team_user, is_intern=False)
self.company = Company.objects.create(name="Contractor Corp") self.company = Company.objects.create(name="Contractor Corp")
self.team = Team.objects.create(name="Project Team", company=self.company) self.team = CompanyTeam.objects.create(name="Project Team", company=self.company)
CompanyMember.objects.create(user=self.ext_team_user, company=self.company, team=self.team) member = CompanyMember.objects.create(name="ext_team_user", user=self.ext_team_user)
member.teams.add(self.team)
# External user 3 (no access) # External user 3 (no access)
self.ext_no_access = User.objects.create_user(username="ext_no_access", password="pwd") self.ext_no_access = User.objects.create_user(username="ext_no_access", password="pwd")
@ -1256,7 +1257,14 @@ class ProjectDocumentsVisibilityTests(TestCase):
# Grant team access to team # Grant team access to team
ProjectTeamAccess.objects.create(project=self.project, team=self.team, can_view=True) ProjectTeamAccess.objects.create(project=self.project, team=self.team, can_view=True)
# Create Project Folder (SCOPED) # Project Folder (SCOPED)
project_ct = ContentType.objects.get_for_model(self.project)
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( self.project_folder = DocumentFolder.objects.create(
name="P12345 Documents", name="P12345 Documents",
slug="project-p12345-boulevard", slug="project-p12345-boulevard",
@ -1264,7 +1272,6 @@ class ProjectDocumentsVisibilityTests(TestCase):
visibility=VisibilityScope.SCOPED, visibility=VisibilityScope.SCOPED,
created_by=self.int_user, created_by=self.int_user,
) )
project_ct = ContentType.objects.get_for_model(self.project)
DocumentFolderAttachment.objects.create( DocumentFolderAttachment.objects.create(
folder=self.project_folder, folder=self.project_folder,
content_type=project_ct, content_type=project_ct,

View file

@ -206,5 +206,51 @@ class ProjectViewerPermissionsTest(TestCase):
self.assertNotIn(self.proj_roads_strict, viewable) 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)

View file

@ -595,7 +595,7 @@ def projects_add_document(request, project_id):
from documents.permissions import user_can_change_visibility from documents.permissions import user_can_change_visibility
raw_visibility = request.POST.get(f'visibility_{i}', '').strip() raw_visibility = request.POST.get(f'visibility_{i}', '').strip()
visibility = None 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): if user_can_change_visibility(request.user):
visibility = raw_visibility visibility = raw_visibility
else: else: