loko/loko/contracts/tests.py
kdeterme b307791b90 feat(contracts): add global permissions to view contract posts and prices on UserConfig
- Add can_view_all_posts and can_view_all_prices boolean fields to UserConfig
- Add migration 0020 with automatic data migration for Legal Office users
- Update permissions helpers, UserConfigAdmin, copy_user_config and intervention detail view
- Add unit tests for global posts and prices permissions
2026-10-07 11:57:57 +02:00

759 lines
31 KiB
Python

from django.test import TestCase, Client
from django.contrib.auth import get_user_model
from django.utils import timezone
from django.urls import reverse
import json
from contracts.models import Company, CompanyTeam, CompanyMember, CompanyMemberHistory, Contract
from common.models import UserConfig, Role
User = get_user_model()
class CompanyMemberTeamsTests(TestCase):
def setUp(self):
# Create user and configuration
self.user = User.objects.create_user(username="testuser", password="password123")
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
# Create company and teams
self.company = Company.objects.create(name="Test Company")
self.team1 = CompanyTeam.objects.create(name="Team Alpha", company=self.company)
self.team2 = CompanyTeam.objects.create(name="Team Beta", company=self.company)
# Create member
self.member = CompanyMember.objects.create(
name="Jean Dupont",
user=self.user
)
def test_assign_multiple_teams(self):
"""Test that a member can be assigned to multiple teams and history tracks it."""
today = timezone.now().date()
# Add to Team 1
self.member.teams.add(self.team1)
self.assertEqual(self.member.teams.count(), 1)
# Verify history entry created
histories = CompanyMemberHistory.objects.filter(member=self.member, team=self.team1)
self.assertEqual(histories.count(), 1)
self.assertEqual(histories.first().join_date, today)
self.assertIsNone(histories.first().leave_date)
# Add to Team 2
self.member.teams.add(self.team2)
self.assertEqual(self.member.teams.count(), 2)
# Verify history for Team 2
histories_team2 = CompanyMemberHistory.objects.filter(member=self.member, team=self.team2)
self.assertEqual(histories_team2.count(), 1)
self.assertEqual(histories_team2.first().join_date, today)
self.assertIsNone(histories_team2.first().leave_date)
def test_remove_team(self):
"""Test that removing a team closes the history period."""
today = timezone.now().date()
self.member.teams.add(self.team1)
# Remove from Team 1
self.member.teams.remove(self.team1)
self.assertEqual(self.member.teams.count(), 0)
# Verify history leave_date updated
history = CompanyMemberHistory.objects.get(member=self.member, team=self.team1)
self.assertEqual(history.leave_date, today)
def test_clear_teams(self):
"""Test that clearing teams closes all active history periods."""
today = timezone.now().date()
self.member.teams.add(self.team1)
self.member.teams.add(self.team2)
# Clear teams
self.member.teams.clear()
self.assertEqual(self.member.teams.count(), 0)
# Verify history leave_date updated for all
h1 = CompanyMemberHistory.objects.get(member=self.member, team=self.team1)
h2 = CompanyMemberHistory.objects.get(member=self.member, team=self.team2)
self.assertEqual(h1.leave_date, today)
self.assertEqual(h2.leave_date, today)
class TeamViewsAjaxTests(TestCase):
def setUp(self):
self.client = Client()
self.user = User.objects.create_user(username="adminuser", password="password")
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
# Give admin role to bypass permission denials
self.admin_role, _ = Role.objects.get_or_create(name="admin")
self.user_config.roles.add(self.admin_role)
self.client.login(username="adminuser", password="password")
self.company = Company.objects.create(name="Test Company")
self.team_a = CompanyTeam.objects.create(name="Team A", company=self.company)
self.team_b = CompanyTeam.objects.create(name="Team B", company=self.company)
self.member = CompanyMember.objects.create(name="Tech A", user=self.user)
self.member.teams.add(self.team_a)
today = timezone.now().date()
yesterday = today - timezone.timedelta(days=1)
CompanyMemberHistory.objects.filter(member=self.member, team=self.team_a).update(join_date=yesterday)
def test_teams_move_member_transfer(self):
"""Test moving a member transfers them (removes old, adds new)."""
url = reverse('contracts:teams_move_member', args=[self.member.id])
payload = {
'old_team_id': self.team_a.id,
'team_id': self.team_b.id
}
response = self.client.post(
url,
data=json.dumps(payload),
content_type='application/json'
)
self.assertEqual(response.status_code, 200)
self.member.refresh_from_db()
# Should only belong to Team B now
self.assertEqual(list(self.member.teams.all()), [self.team_b])
# Verify history was updated
today = timezone.now().date()
yesterday = today - timezone.timedelta(days=1)
h_old = CompanyMemberHistory.objects.get(member=self.member, team=self.team_a)
self.assertEqual(h_old.leave_date, yesterday)
h_new = CompanyMemberHistory.objects.get(member=self.member, team=self.team_b)
self.assertEqual(h_new.join_date, today)
self.assertIsNone(h_new.leave_date)
def test_teams_update_member_modal(self):
"""Test updating a member via modal view with transfer."""
url = reverse('contracts:teams_update_member', args=[self.member.id])
payload = {
'name': 'Tech A Modified',
'old_team_id': self.team_a.id,
'team_id': self.team_b.id
}
response = self.client.post(
url,
data=json.dumps(payload),
content_type='application/json'
)
self.assertEqual(response.status_code, 200)
self.member.refresh_from_db()
self.assertEqual(self.member.name, 'Tech A Modified')
self.assertEqual(list(self.member.teams.all()), [self.team_b])
def test_teams_create_member_different_company_allowed_for_team_company(self):
"""Test that a non-admin user can add a member belonging to the target team's company, even if different from user's company."""
from django.contrib.auth.models import User
from common.models import UserConfig, Role
from contracts.models import Company, CompanyTeam, CompanyMember, UserCompanyTeamAccess
# Create user of Company A
other_user = User.objects.create_user(username="otheruser", password="password")
company_a = Company.objects.create(name="Company A")
other_user_config = UserConfig.objects.create(user=other_user, company=company_a)
# Create team belonging to Company B
company_b = Company.objects.create(name="Company B")
team_b = CompanyTeam.objects.create(name="Team Company B", company=company_b)
# Grant user edit permission on team_b
UserCompanyTeamAccess.objects.create(
user_config=other_user_config,
team=team_b,
can_view=True,
can_edit=True
)
# Create target user to add, belonging to Company B
target_user = User.objects.create_user(username="targetuser", password="password")
UserConfig.objects.create(user=target_user, company=company_b)
# Login other_user
self.client.logout()
self.client.login(username="otheruser", password="password")
# Call teams_create_member
url = reverse('contracts:teams_create_member')
payload = {
'name': 'New Target Member',
'user_id': target_user.id,
'team_id': team_b.id
}
response = self.client.post(
url,
data=json.dumps(payload),
content_type='application/json'
)
self.assertEqual(response.status_code, 200)
# Verify member was created and added to team_b
member = CompanyMember.objects.get(user=target_user)
self.assertEqual(member.name, 'New Target Member')
self.assertIn(team_b, member.teams.all())
class ContractDisplayNameTests(TestCase):
def setUp(self):
from datetime import date
from contracts.models import Contract, Company
self.company = Company.objects.create(name="Company Tests")
self.contract_no_desc = Contract.objects.create(
company=self.company,
contract_number="CTR-001",
start_date=date(2025, 1, 1),
end_date=date(2026, 1, 1)
)
self.contract_short_desc = Contract.objects.create(
company=self.company,
contract_number="CTR-002",
description="Entretien espaces verts",
start_date=date(2025, 1, 1),
end_date=date(2026, 1, 1)
)
self.contract_long_desc = Contract.objects.create(
company=self.company,
contract_number="CTR-003",
description="Travaux et maintenance préventive et corrective de la signalisation lumineuse tricolore",
start_date=date(2025, 1, 1),
end_date=date(2026, 1, 1)
)
def test_display_name_no_description(self):
self.assertEqual(self.contract_no_desc.display_name, "CTR-001")
def test_display_name_short_description(self):
self.assertEqual(self.contract_short_desc.display_name, "CTR-002 (Entretien espaces verts)")
def test_display_name_long_description_truncated(self):
self.assertTrue(self.contract_long_desc.display_name.startswith("CTR-003 (Travaux et maintenance préventive et correct"))
self.assertTrue(self.contract_long_desc.display_name.endswith("…)"))
class GenerateClaimDeclarationTimelineTests(TestCase):
def setUp(self):
from decimal import Decimal
from datetime import date
from contracts.models import Contract, ContractOrder, ContractPost
from common.models import UserContractAccess
from interventions.models import Intervention, InterventionTimeLine, InterventionContractPost, InterventionPostPeriodQuantity
self.client = Client()
self.user = User.objects.create_user(username="admin_user", password="password")
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
self.admin_role, _ = Role.objects.get_or_create(name="admin")
self.user_config.roles.add(self.admin_role)
self.client.login(username="admin_user", password="password")
self.company = Company.objects.create(name="Company A")
self.contract = Contract.objects.create(
company=self.company,
contract_number="CTR-TEST-001",
start_date=date(2025, 1, 1),
end_date=date(2026, 1, 1),
)
UserContractAccess.objects.create(
user_config=self.user_config,
contract=self.contract,
can_view_interventions=True,
)
self.order = ContractOrder.objects.create(
contract=self.contract,
order_code="ORD-TEST-01",
order_status="pending",
order_date=date(2025, 1, 1),
delivery_date=date(2025, 1, 15),
description="Commande test",
)
self.post = ContractPost.objects.create(
contract=self.contract,
order_number=1,
code="P1",
description_fr="Poste 1",
description_nl="Post 1",
unit="u",
unit_price=Decimal("10.00"),
)
self.intervention_validated = Intervention.objects.create(
title="Intervention Validated",
contract=self.contract,
order=self.order,
status="validated",
)
InterventionContractPost.objects.create(
intervention=self.intervention_validated,
contract_post=self.post,
quantity=Decimal("2.00"),
control_status="validated",
)
self.intervention_period = Intervention.objects.create(
title="Intervention with Validated Period",
contract=self.contract,
order=self.order,
status="to_be_processed",
)
InterventionPostPeriodQuantity.objects.create(
intervention=self.intervention_period,
contract_post=self.post,
period_number=1,
quantity=Decimal("3.00"),
validated_at=timezone.now(),
validated_by=self.user,
status="validated",
)
def test_generate_documents_creates_timeline_events(self):
from interventions.models import InterventionTimeLine
url = reverse("contracts:generate_documents_for_order", args=[self.order.id])
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
# 1. Check validated intervention status & timeline
self.intervention_validated.refresh_from_db()
self.assertEqual(self.intervention_validated.status, "invoiced")
timeline_event_1 = InterventionTimeLine.objects.filter(
intervention=self.intervention_validated,
to_status="invoiced",
).first()
self.assertIsNotNone(timeline_event_1)
self.assertEqual(timeline_event_1.from_status, "validated")
self.assertEqual(timeline_event_1.event_type, "status_change")
self.assertEqual(timeline_event_1.event_user, self.user)
# 2. Check intervention with period status & timeline
self.intervention_period.refresh_from_db()
self.assertEqual(self.intervention_period.status, "invoiced")
timeline_event_2 = InterventionTimeLine.objects.filter(
intervention=self.intervention_period,
to_status="invoiced",
).first()
self.assertIsNotNone(timeline_event_2)
self.assertEqual(timeline_event_2.from_status, "to_be_processed")
self.assertEqual(timeline_event_2.event_type, "status_change")
self.assertEqual(timeline_event_2.event_user, self.user)
class ContractOrderDetailControllerDisplayTests(TestCase):
def setUp(self):
from datetime import date
from contracts.models import Contract, ContractOrder, Company, CompanyTeam, CompanyMember
from common.models import UserContractAccess
from interventions.models import Intervention
self.client = Client()
self.user = User.objects.create_user(username="order_admin_user", password="password")
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
self.admin_role, _ = Role.objects.get_or_create(name="admin")
self.user_config.roles.add(self.admin_role)
self.client.login(username="order_admin_user", password="password")
self.company = Company.objects.create(name="Société Test Contrôle")
self.team_ctrl = CompanyTeam.objects.create(
name="Équipe Contrôle Nord",
company=self.company,
operation_types=[CompanyTeam.OPERATION_CONTROLS],
)
ctrl_user = User.objects.create_user(username="ctrl_member_user", password="password")
self.controller_member = CompanyMember.objects.create(
user=ctrl_user,
name="Jean Contrôleur",
)
self.controller_member.teams.add(self.team_ctrl)
self.contract_with_ctrl = Contract.objects.create(
company=self.company,
contract_number="CTR-CTRL-001",
start_date=date(2025, 1, 1),
end_date=date(2026, 1, 1),
auto_assign_control_team=True,
)
UserContractAccess.objects.create(
user_config=self.user_config,
contract=self.contract_with_ctrl,
can_view_interventions=True,
)
self.order_with_ctrl = ContractOrder.objects.create(
contract=self.contract_with_ctrl,
order_code="ORD-CTRL-01",
order_status="pending",
order_date=date(2025, 1, 1),
delivery_date=date(2025, 1, 15),
description="Commande avec marché contrôlé",
)
self.itv1 = Intervention.objects.create(
code="ITV-C01",
title="Intervention avec contrôleur",
contract=self.contract_with_ctrl,
order=self.order_with_ctrl,
assigned_control_team=self.team_ctrl,
assigned_controller=self.controller_member,
status="finished",
)
self.itv2 = Intervention.objects.create(
code="ITV-C02",
title="Intervention équipe seule",
contract=self.contract_with_ctrl,
order=self.order_with_ctrl,
assigned_control_team=self.team_ctrl,
status="processed",
)
self.contract_without_ctrl = Contract.objects.create(
company=self.company,
contract_number="CTR-NO-CTRL-002",
start_date=date(2025, 1, 1),
end_date=date(2026, 1, 1),
auto_assign_control_team=False,
)
UserContractAccess.objects.create(
user_config=self.user_config,
contract=self.contract_without_ctrl,
can_view_interventions=True,
)
self.order_without_ctrl = ContractOrder.objects.create(
contract=self.contract_without_ctrl,
order_code="ORD-NO-CTRL-02",
order_status="pending",
order_date=date(2025, 1, 1),
delivery_date=date(2025, 1, 15),
description="Commande sans marché contrôlé",
)
self.itv3 = Intervention.objects.create(
code="ITV-NC01",
title="Intervention classique",
contract=self.contract_without_ctrl,
order=self.order_without_ctrl,
status="finished",
)
def test_order_detail_shows_controller_when_contract_has_auto_assign_control_team(self):
url = reverse("contracts:contracts_orders_detail", kwargs={"order_id": self.order_with_ctrl.id})
response = self.client.get(url, HTTP_ACCEPT_LANGUAGE='fr')
self.assertEqual(response.status_code, 200)
# Check th exists
self.assertContains(response, 'data-field="assigned_controller"')
self.assertContains(response, 'Contrôleur assigné')
# Check td contents
self.assertContains(response, 'Jean Contrôleur')
self.assertContains(response, 'Équipe Contrôle Nord')
def test_order_detail_hides_controller_when_contract_does_not_have_auto_assign_control_team(self):
url = reverse("contracts:contracts_orders_detail", kwargs={"order_id": self.order_without_ctrl.id})
response = self.client.get(url, HTTP_ACCEPT_LANGUAGE='fr')
self.assertEqual(response.status_code, 200)
# Check th does not exist
self.assertNotContains(response, 'data-field="assigned_controller"')
self.assertNotContains(response, 'Contrôleur assigné')
class ContractGeometryTests(TestCase):
def setUp(self):
self.company = Company.objects.create(name="ETA Environnement")
def test_contract_with_donut_geometry_and_color(self):
"""Test that Contract accepts a MultiPolygon with interior ring (donut hole) and color."""
from django.contrib.gis.geos import Polygon, MultiPolygon, Point
from datetime import date
outer_ring = ((0, 0), (100, 0), (100, 100), (0, 100), (0, 0))
hole = ((20, 20), (40, 20), (40, 40), (20, 40), (20, 20))
poly_with_hole = Polygon(outer_ring, hole)
multi_poly = MultiPolygon([poly_with_hole], srid=3812)
contract = Contract.objects.create(
company=self.company,
contract_number="CTR-ETA-LOT1",
start_date=date(2026, 1, 1),
end_date=date(2028, 12, 31),
geom=multi_poly,
color="#28a745",
)
self.assertEqual(contract.color, "#28a745")
self.assertIsNotNone(contract.geom)
self.assertEqual(contract.geom.srid, 3812)
self.assertEqual(contract.geom.geom_type, "MultiPolygon")
# GeoJSON property
geojson = contract.geojson_geometry
self.assertIsNotNone(geojson)
self.assertEqual(geojson["type"], "MultiPolygon")
self.assertEqual(len(geojson["coordinates"]), 1)
self.assertEqual(len(geojson["coordinates"][0]), 2)
# Spatial queries with hole
pt_inside = Point(10, 10, srid=3812)
pt_in_hole = Point(30, 30, srid=3812)
self.assertTrue(Contract.objects.filter(pk=contract.pk, geom__intersects=pt_inside).exists())
self.assertFalse(Contract.objects.filter(pk=contract.pk, geom__intersects=pt_in_hole).exists())
def test_contract_admin_has_geom(self):
"""Test ContractAdmin has_geom method."""
from django.contrib.admin.sites import AdminSite
from contracts.admin import ContractAdmin
from datetime import date
admin_instance = ContractAdmin(Contract, AdminSite())
contract_without_geom = Contract.objects.create(
company=self.company,
contract_number="CTR-NOGEOM",
start_date=date(2026, 1, 1),
end_date=date(2028, 12, 31),
)
self.assertFalse(admin_instance.has_geom(contract_without_geom))
class ContractGeometryTests(TestCase):
def setUp(self):
self.company = Company.objects.create(name="ETA Environnement")
def test_contract_with_donut_geometry_and_color(self):
"""Test that Contract accepts a MultiPolygon with interior ring (donut hole) and color."""
from django.contrib.gis.geos import Polygon, MultiPolygon, Point
from datetime import date
# Polygon with an outer ring and an inner ring (hole)
# Outer ring: (0,0) to (100, 100)
# Inner hole: (20,20) to (40, 40)
outer_ring = ((0, 0), (100, 0), (100, 100), (0, 100), (0, 0))
hole = ((20, 20), (40, 20), (40, 40), (20, 40), (20, 20))
poly_with_hole = Polygon(outer_ring, hole)
multi_poly = MultiPolygon([poly_with_hole], srid=3812)
contract = Contract.objects.create(
company=self.company,
contract_number="CTR-ETA-LOT1",
start_date=date(2026, 1, 1),
end_date=date(2028, 12, 31),
geom=multi_poly,
color="#28a745",
)
self.assertEqual(contract.color, "#28a745")
self.assertIsNotNone(contract.geom)
self.assertEqual(contract.geom.srid, 3812)
self.assertEqual(contract.geom.geom_type, "MultiPolygon")
# GeoJSON property
geojson = contract.geojson_geometry
self.assertIsNotNone(geojson)
self.assertEqual(geojson["type"], "MultiPolygon")
# 1 polygon in multipolygon, having 2 rings (outer + hole)
self.assertEqual(len(geojson["coordinates"]), 1)
self.assertEqual(len(geojson["coordinates"][0]), 2)
# Spatial queries with hole
# Point inside outer ring but outside hole -> intersects
pt_inside = Point(10, 10, srid=3812)
# Point inside the hole -> does NOT intersect
pt_in_hole = Point(30, 30, srid=3812)
self.assertTrue(Contract.objects.filter(pk=contract.pk, geom__intersects=pt_inside).exists())
self.assertFalse(Contract.objects.filter(pk=contract.pk, geom__intersects=pt_in_hole).exists())
def test_contract_admin_has_geom(self):
"""Test ContractAdmin has_geom method."""
from django.contrib.admin.sites import AdminSite
from contracts.admin import ContractAdmin
from datetime import date
admin_instance = ContractAdmin(Contract, AdminSite())
contract_without_geom = Contract.objects.create(
company=self.company,
contract_number="CTR-NOGEOM",
start_date=date(2026, 1, 1),
end_date=date(2028, 12, 31),
)
self.assertFalse(admin_instance.has_geom(contract_without_geom))
class ContractGeojsonEndpointTests(TestCase):
def setUp(self):
from django.contrib.auth import get_user_model
from common.models import Thematic, Role, UserConfig, UserThematics, UserContractAccess
from django.contrib.gis.geos import Polygon, MultiPolygon
from datetime import date
User = get_user_model()
self.user = User.objects.create_user(username="test_nature_user", password="password")
self.role_manager = Role.objects.create(name="manager")
self.config = UserConfig.objects.create(user=self.user)
self.config.roles.add(self.role_manager)
self.thematic_nature = Thematic.objects.create(code="nature", name_fr="Nature", name_nl="Natuur")
self.thematic_roads = Thematic.objects.create(code="roads", name_fr="Voiries", name_nl="Wegen")
UserThematics.objects.create(
user_config=self.config,
thematic=self.thematic_nature,
can_view_assets=True,
can_view_interventions=True
)
self.company = Company.objects.create(name="ETA Vert")
poly = Polygon(((0, 0), (10, 0), (10, 10), (0, 10), (0, 0)))
self.multi_poly = MultiPolygon([poly], srid=3812)
# Nature contract with geom
self.contract_nature_with_geom = Contract.objects.create(
company=self.company,
contract_number="CTR-NAT-01",
start_date=date(2026, 1, 1),
end_date=date(2028, 12, 31),
geom=self.multi_poly,
color="#198754",
description="Lot 1 entretien espaces verts",
)
self.contract_nature_with_geom.thematics.add(self.thematic_nature)
# Nature contract without geom
self.contract_nature_no_geom = Contract.objects.create(
company=self.company,
contract_number="CTR-NAT-NOGEOM",
start_date=date(2026, 1, 1),
end_date=date(2028, 12, 31),
)
self.contract_nature_no_geom.thematics.add(self.thematic_nature)
# Roads contract with geom
self.contract_roads_with_geom = Contract.objects.create(
company=self.company,
contract_number="CTR-ROADS-01",
start_date=date(2026, 1, 1),
end_date=date(2028, 12, 31),
geom=self.multi_poly,
color="#0d6efd",
)
self.contract_roads_with_geom.thematics.add(self.thematic_roads)
# Grant access to nature contracts
UserContractAccess.objects.create(
user_config=self.config,
contract=self.contract_nature_with_geom,
can_view_assets=True,
can_view_interventions=True
)
UserContractAccess.objects.create(
user_config=self.config,
contract=self.contract_nature_no_geom,
can_view_assets=True,
can_view_interventions=True
)
def test_anonymous_access_forbidden(self):
url = reverse("contracts:contracts_geojson")
response = self.client.get(url)
self.assertEqual(response.status_code, 302)
def test_authenticated_nature_contracts_geojson(self):
self.client.login(username="test_nature_user", password="password")
url = reverse("contracts:contracts_geojson") + "?thematic=nature"
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertEqual(data["type"], "FeatureCollection")
# Only 1 feature should be returned (nature with geom)
self.assertEqual(len(data["features"]), 1)
feat = data["features"][0]
self.assertEqual(feat["id"], self.contract_nature_with_geom.id)
self.assertEqual(feat["properties"]["contract_number"], "CTR-NAT-01")
self.assertEqual(feat["properties"]["company_name"], "ETA Vert")
self.assertEqual(feat["properties"]["color"], "#198754")
self.assertEqual(feat["properties"]["description"], "Lot 1 entretien espaces verts")
self.assertEqual(feat["geometry"]["type"], "MultiPolygon")
def test_superuser_access_all_contracts(self):
from django.contrib.auth import get_user_model
User = get_user_model()
admin = User.objects.create_superuser(username="admin_user", password="password", email="admin@example.com")
self.client.login(username="admin_user", password="password")
url = reverse("contracts:contracts_geojson") + "?thematic=nature"
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertEqual(len(data["features"]), 1)
# For roads:
url_roads = reverse("contracts:contracts_geojson") + "?thematic=roads"
response_roads = self.client.get(url_roads)
self.assertEqual(response_roads.status_code, 200)
data_roads = response_roads.json()
self.assertEqual(len(data_roads["features"]), 1)
self.assertEqual(data_roads["features"][0]["properties"]["contract_number"], "CTR-ROADS-01")
def test_user_without_contract_access_gets_empty(self):
from django.contrib.auth import get_user_model
from common.models import Role, UserConfig, UserThematics
User = get_user_model()
other_user = User.objects.create_user(username="other_user", password="password")
config = UserConfig.objects.create(user=other_user)
role = Role.objects.get(name="manager")
config.roles.add(role)
UserThematics.objects.create(
user_config=config,
thematic=self.thematic_nature,
can_view_assets=True
)
self.client.login(username="other_user", password="password")
url = reverse("contracts:contracts_geojson") + "?thematic=nature"
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
data = response.json()
self.assertEqual(len(data["features"]), 0)
class GlobalPostsAndPricesPermissionsTests(TestCase):
def setUp(self):
self.user = User.objects.create_user(username="viewer_user", password="password123")
self.role_viewer, _ = Role.objects.get_or_create(name="viewer")
self.config = UserConfig.objects.create(
user=self.user,
can_view_all_posts=False,
can_view_all_prices=False,
)
self.config.roles.add(self.role_viewer)
self.contract = Contract.objects.create(
contract_number="CTR-TEST-001",
description="Test Contract"
)
def test_default_viewer_cannot_view_posts_or_prices(self):
from contracts.permissions import user_can_view_contract_prices, user_has_access_to_contract_posts
self.assertFalse(user_can_view_contract_prices(self.user, self.contract.id))
self.assertFalse(user_has_access_to_contract_posts(self.user, self.contract.id))
def test_viewer_with_can_view_all_posts(self):
from contracts.permissions import user_has_access_to_contract_posts
self.config.can_view_all_posts = True
self.config.save()
self.assertTrue(user_has_access_to_contract_posts(self.user, self.contract.id))
def test_viewer_with_can_view_all_prices(self):
from contracts.permissions import user_can_view_contract_prices
self.config.can_view_all_prices = True
self.config.save()
self.assertTrue(user_can_view_contract_prices(self.user, self.contract.id))