485 lines
23 KiB
Python
485 lines
23 KiB
Python
import json
|
|
from datetime import date, timedelta
|
|
from django.test import TestCase
|
|
from django.contrib.auth import get_user_model
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from contracts.models import Company, CompanyTeam, CompanyMember, CompanyMemberHistory, CompanyMemberAbsence
|
|
from contracts.services_teams import (
|
|
get_member_team_for_date,
|
|
get_member_teams_for_date,
|
|
set_member_team_schedule,
|
|
get_teams_composition_for_period,
|
|
get_member_absences_for_period,
|
|
toggle_member_absence,
|
|
duplicate_day_schedule,
|
|
duplicate_week_schedule,
|
|
sync_member_current_teams,
|
|
)
|
|
|
|
User = get_user_model()
|
|
|
|
|
|
class TeamPlanningServicesTests(TestCase):
|
|
def setUp(self):
|
|
self.user = User.objects.create_user(username="alice", password="password123")
|
|
self.company = Company.objects.create(name="Acme Corp")
|
|
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_alice = CompanyMember.objects.create(name="Alice", user=self.user)
|
|
|
|
def test_schedule_future_team_change_preserves_today(self):
|
|
"""
|
|
Scénario clé :
|
|
- Alice est dans l'équipe A aujourd'hui (02/09).
|
|
- Le coordinateur planifie Alice dans l'équipe B pour demain (03/09).
|
|
- Alice doit rester dans l'équipe A aujourd'hui et être dans l'équipe B demain.
|
|
"""
|
|
today = date(2026, 9, 2)
|
|
tomorrow = date(2026, 9, 3)
|
|
|
|
# Alice est affectée à l'équipe A depuis le 01/09
|
|
set_member_team_schedule(self.member_alice, self.team_a.id, date(2026, 9, 1))
|
|
|
|
# Vérifier qu'elle est bien dans l'équipe A aujourd'hui
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, today), self.team_a)
|
|
|
|
# Planifier l'équipe B pour demain
|
|
set_member_team_schedule(self.member_alice, self.team_b.id, tomorrow)
|
|
|
|
# Vérifier : aujourd'hui elle est TOUJOURS dans l'équipe A
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, today), self.team_a)
|
|
# Vérifier : demain elle sera dans l'équipe B
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, tomorrow), self.team_b)
|
|
# Vérifier : après-demain elle reste dans l'équipe B (persistance)
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, date(2026, 9, 4)), self.team_b)
|
|
|
|
def test_schedule_unassign_for_future_date(self):
|
|
"""Tester la désassignation à une date future."""
|
|
d1 = date(2026, 9, 1)
|
|
d2 = date(2026, 9, 5)
|
|
|
|
set_member_team_schedule(self.member_alice, self.team_a.id, d1)
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, date(2026, 9, 3)), self.team_a)
|
|
|
|
# Désassigner à partir du 5 septembre
|
|
set_member_team_schedule(self.member_alice, None, d2)
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, date(2026, 9, 4)), self.team_a)
|
|
self.assertIsNone(get_member_team_for_date(self.member_alice, d2))
|
|
self.assertIsNone(get_member_team_for_date(self.member_alice, date(2026, 9, 10)))
|
|
|
|
def test_toggle_absence(self):
|
|
"""Tester l'activation et désactivation d'une absence."""
|
|
target_date = date(2026, 9, 3)
|
|
|
|
# Activer l'absence
|
|
absence = toggle_member_absence(self.member_alice, target_date, is_absent=True, reason='maladie', comment='Grippe')
|
|
self.assertIsNotNone(absence)
|
|
self.assertEqual(CompanyMemberAbsence.objects.filter(member=self.member_alice, date=target_date).count(), 1)
|
|
|
|
absences = get_member_absences_for_period({self.member_alice.id}, target_date, target_date)
|
|
self.assertIn((self.member_alice.id, target_date), absences)
|
|
self.assertEqual(absences[(self.member_alice.id, target_date)]['reason'], 'maladie')
|
|
|
|
# Désactiver l'absence
|
|
toggle_member_absence(self.member_alice, target_date, is_absent=False)
|
|
self.assertEqual(CompanyMemberAbsence.objects.filter(member=self.member_alice, date=target_date).count(), 0)
|
|
|
|
def test_duplicate_day_schedule(self):
|
|
"""Tester la duplication de configuration d'un jour vers un autre."""
|
|
monday = date(2026, 9, 7)
|
|
tuesday = date(2026, 9, 8)
|
|
|
|
# Lundi : Alice en Team A et absente
|
|
set_member_team_schedule(self.member_alice, self.team_a.id, monday)
|
|
toggle_member_absence(self.member_alice, monday, is_absent=True, reason='conge')
|
|
|
|
# Dupliquer vers mardi avec copie des absences
|
|
res = duplicate_day_schedule(monday, tuesday, copy_absences=True)
|
|
self.assertGreaterEqual(res['assigned_count'], 1)
|
|
self.assertEqual(res['absences_count'], 1)
|
|
|
|
# Vérifier l'affectation du mardi
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, tuesday), self.team_a)
|
|
self.assertTrue(CompanyMemberAbsence.objects.filter(member=self.member_alice, date=tuesday).exists())
|
|
|
|
def test_duplicate_week_schedule(self):
|
|
"""Tester la duplication d'une semaine complète vers la semaine suivante."""
|
|
w1_monday = date(2026, 9, 7)
|
|
w2_monday = date(2026, 9, 14)
|
|
|
|
set_member_team_schedule(self.member_alice, self.team_a.id, w1_monday)
|
|
res = duplicate_week_schedule(w1_monday, w2_monday, copy_absences=False)
|
|
|
|
self.assertGreaterEqual(res['assigned_count'], 1)
|
|
# Vérifier que le mercredi de la semaine 2, Alice est bien en Team A
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, date(2026, 9, 16)), self.team_a)
|
|
|
|
def test_multi_team_scheduling_same_day(self):
|
|
"""Tester l'affectation d'un membre à deux équipes le même jour avec action='add'."""
|
|
target_date = date(2026, 9, 2)
|
|
|
|
# 1. Affecter Alice à Team A
|
|
set_member_team_schedule(self.member_alice, self.team_a.id, target_date)
|
|
teams = get_member_teams_for_date(self.member_alice, target_date)
|
|
self.assertEqual(len(teams), 1)
|
|
self.assertEqual(teams[0], self.team_a)
|
|
|
|
# 2. Affecter aussi Alice à Team B avec action='add'
|
|
set_member_team_schedule(self.member_alice, self.team_b.id, target_date, action='add')
|
|
teams = get_member_teams_for_date(self.member_alice, target_date)
|
|
self.assertEqual(len(teams), 2)
|
|
self.assertIn(self.team_a, teams)
|
|
self.assertIn(self.team_b, teams)
|
|
|
|
# 3. Vérifier les compositions pour la matrice
|
|
comp = get_teams_composition_for_period({self.team_a.id, self.team_b.id}, target_date, target_date)
|
|
team_a_members = comp.get((self.team_a.id, target_date), [])
|
|
team_b_members = comp.get((self.team_b.id, target_date), [])
|
|
|
|
alice_in_a = next((m for m in team_a_members if m['id'] == self.member_alice.id), None)
|
|
alice_in_b = next((m for m in team_b_members if m['id'] == self.member_alice.id), None)
|
|
|
|
self.assertIsNotNone(alice_in_a)
|
|
self.assertIsNotNone(alice_in_b)
|
|
self.assertTrue(alice_in_a['is_multi_team'])
|
|
self.assertTrue(alice_in_b['is_multi_team'])
|
|
self.assertEqual(alice_in_a['other_teams'][0]['id'], self.team_b.id)
|
|
self.assertEqual(alice_in_b['other_teams'][0]['id'], self.team_a.id)
|
|
|
|
def test_multi_team_unassign_specific_team(self):
|
|
"""Tester la désassignation ciblée d'une seule équipe lorsqu'un membre en a plusieurs."""
|
|
target_date = date(2026, 9, 2)
|
|
set_member_team_schedule(self.member_alice, self.team_a.id, target_date)
|
|
set_member_team_schedule(self.member_alice, self.team_b.id, target_date, action='add')
|
|
|
|
# Désassigner Alice uniquement de Team A
|
|
set_member_team_schedule(self.member_alice, None, target_date, unassign_team_id=self.team_a.id)
|
|
teams = get_member_teams_for_date(self.member_alice, target_date)
|
|
self.assertEqual(len(teams), 1)
|
|
self.assertEqual(teams[0], self.team_b)
|
|
|
|
def test_multi_team_duplicate_day_schedule(self):
|
|
"""Tester que la duplication d'une journée duplique toutes les équipes d'un membre multi-équipes."""
|
|
day1 = date(2026, 9, 7)
|
|
day2 = date(2026, 9, 8)
|
|
set_member_team_schedule(self.member_alice, self.team_a.id, day1)
|
|
set_member_team_schedule(self.member_alice, self.team_b.id, day1, action='add')
|
|
|
|
res = duplicate_day_schedule(day1, day2)
|
|
self.assertGreaterEqual(res['assigned_count'], 2)
|
|
|
|
teams_day2 = get_member_teams_for_date(self.member_alice, day2)
|
|
self.assertEqual(len(teams_day2), 2)
|
|
self.assertIn(self.team_a, teams_day2)
|
|
self.assertIn(self.team_b, teams_day2)
|
|
|
|
|
|
class TeamPlanningAjaxViewsTests(TestCase):
|
|
def setUp(self):
|
|
from common.models import UserConfig, Role
|
|
import json
|
|
|
|
self.user = User.objects.create_user(username="admin_user", password="password123")
|
|
self.user_config = UserConfig.objects.create(user=self.user, is_intern=True)
|
|
admin_role = Role.objects.create(name="admin")
|
|
self.user_config.roles.add(admin_role)
|
|
|
|
self.company = Company.objects.create(name="Acme Corp")
|
|
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.tech_user = User.objects.create_user(username="alice", password="password123")
|
|
self.member_alice = CompanyMember.objects.create(name="Alice", user=self.tech_user)
|
|
|
|
self.client.login(username="admin_user", password="password123")
|
|
|
|
def test_planning_data_ajax(self):
|
|
url = reverse('contracts:team_planning_data_ajax')
|
|
response = self.client.get(url, {'date': '2026-09-02', 'view_mode': 'week'})
|
|
self.assertEqual(response.status_code, 200)
|
|
data = response.json()
|
|
self.assertIn('days', data)
|
|
self.assertEqual(len(data['days']), 7)
|
|
self.assertIn('teams', data)
|
|
self.assertIn('members', data)
|
|
|
|
def test_planning_assign_member_ajax(self):
|
|
target_date = timezone.now().date() + timedelta(days=2)
|
|
url = reverse('contracts:team_planning_assign_member_ajax')
|
|
payload = {
|
|
'member_id': self.member_alice.id,
|
|
'team_id': self.team_b.id,
|
|
'date': target_date.isoformat(),
|
|
}
|
|
response = self.client.post(url, data=json.dumps(payload), content_type='application/json')
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTrue(response.json()['success'])
|
|
|
|
# Vérifier l'affectation
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, target_date), self.team_b)
|
|
|
|
def test_planning_toggle_absence_ajax(self):
|
|
target_date = timezone.now().date() + timedelta(days=2)
|
|
url = reverse('contracts:team_planning_toggle_absence_ajax')
|
|
payload = {
|
|
'member_id': self.member_alice.id,
|
|
'date': target_date.isoformat(),
|
|
'is_absent': True,
|
|
'reason': 'maladie',
|
|
'comment': 'Rhume',
|
|
}
|
|
response = self.client.post(url, data=json.dumps(payload), content_type='application/json')
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTrue(response.json()['is_absent'])
|
|
self.assertEqual(CompanyMemberAbsence.objects.filter(member=self.member_alice, date=target_date).count(), 1)
|
|
|
|
def test_planning_duplicate_ajax(self):
|
|
source = timezone.now().date() + timedelta(days=1)
|
|
target = timezone.now().date() + timedelta(days=2)
|
|
# Assigner Alice à source
|
|
set_member_team_schedule(self.member_alice, self.team_a.id, source)
|
|
|
|
url = reverse('contracts:team_planning_duplicate_ajax')
|
|
payload = {
|
|
'mode': 'day',
|
|
'source_date': source.isoformat(),
|
|
'target_date': target.isoformat(),
|
|
'copy_absences': False,
|
|
}
|
|
response = self.client.post(url, data=json.dumps(payload), content_type='application/json')
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTrue(response.json()['success'])
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, target), self.team_a)
|
|
|
|
def test_planning_duplicate_friday_to_monday_ajax(self):
|
|
"""Vérifie la duplication d'un vendredi vers le lundi suivant via l'endpoint AJAX."""
|
|
friday = date(2026, 9, 11)
|
|
monday = date(2026, 9, 14)
|
|
set_member_team_schedule(self.member_alice, self.team_b.id, friday)
|
|
toggle_member_absence(self.member_alice, friday, is_absent=True, reason='conge', comment='Weekend prolonge')
|
|
|
|
url = reverse('contracts:team_planning_duplicate_ajax')
|
|
payload = {
|
|
'mode': 'day',
|
|
'source_date': friday.isoformat(),
|
|
'target_date': monday.isoformat(),
|
|
'copy_absences': True,
|
|
}
|
|
response = self.client.post(url, data=json.dumps(payload), content_type='application/json')
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTrue(response.json()['success'])
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, monday), self.team_b)
|
|
self.assertTrue(CompanyMemberAbsence.objects.filter(member=self.member_alice, date=monday).exists())
|
|
|
|
def test_teams_move_member_syncs_history(self):
|
|
url = reverse('contracts:teams_move_member', args=[self.member_alice.id])
|
|
payload = {
|
|
'team_id': self.team_a.id,
|
|
'old_team_id': None,
|
|
}
|
|
response = self.client.post(url, data=json.dumps(payload), content_type='application/json')
|
|
self.assertEqual(response.status_code, 200)
|
|
self.assertTrue(response.json()['success'])
|
|
|
|
today = timezone.now().date()
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, today), self.team_a)
|
|
|
|
def test_move_member_takes_effect_on_day_j(self):
|
|
"""Vérifie que déplacer un membre le jour J s'applique immédiatement le jour J dans les compositions, sans pré-remplir les jours futurs non planifiés."""
|
|
today = timezone.now().date()
|
|
day_j = today + timedelta(days=2)
|
|
day_next = day_j + timedelta(days=1)
|
|
|
|
# 1. Alice est dans Team A pour day_j
|
|
set_member_team_schedule(self.member_alice, self.team_a.id, day_j)
|
|
comp = get_teams_composition_for_period({self.team_a.id, self.team_b.id}, day_j, day_next)
|
|
team_a_day_j_members = [m['id'] for m in comp.get((self.team_a.id, day_j), [])]
|
|
self.assertIn(self.member_alice.id, team_a_day_j_members)
|
|
|
|
# 2. On déplace Alice vers Team B pour le jour J
|
|
set_member_team_schedule(self.member_alice, self.team_b.id, day_j)
|
|
|
|
# 3. Vérifier que sur le jour J, Alice est bien dans Team B (et plus dans Team A)
|
|
comp_after = get_teams_composition_for_period({self.team_a.id, self.team_b.id}, day_j, day_next)
|
|
team_a_day_j_after = [m['id'] for m in comp_after.get((self.team_a.id, day_j), [])]
|
|
team_b_day_j_after = [m['id'] for m in comp_after.get((self.team_b.id, day_j), [])]
|
|
team_b_day_next_after = [m['id'] for m in comp_after.get((self.team_b.id, day_next), [])]
|
|
|
|
self.assertNotIn(self.member_alice.id, team_a_day_j_after)
|
|
self.assertIn(self.member_alice.id, team_b_day_j_after)
|
|
# Le jour suivant n'ayant pas été planifié, il ne doit pas être pré-rempli dans la matrice de planification
|
|
self.assertNotIn(self.member_alice.id, team_b_day_next_after)
|
|
# Mais get_member_team_for_date assure la persistance théorique pour les interventions
|
|
self.assertEqual(get_member_team_for_date(self.member_alice, day_next), self.team_b)
|
|
|
|
def test_planning_past_date_is_forbidden(self):
|
|
"""Vérifie que les endpoints AJAX rejettent les modifications sur des dates passées."""
|
|
past_date = (timezone.now().date() - timedelta(days=2)).isoformat()
|
|
|
|
# 1. Assign
|
|
url = reverse('contracts:team_planning_assign_member_ajax')
|
|
resp = self.client.post(url, data=json.dumps({
|
|
'member_id': self.member_alice.id,
|
|
'team_id': self.team_a.id,
|
|
'date': past_date,
|
|
}), content_type='application/json')
|
|
self.assertEqual(resp.status_code, 400)
|
|
self.assertIn('passé', resp.json().get('error', '').lower())
|
|
|
|
# 2. Absence
|
|
url_abs = reverse('contracts:team_planning_toggle_absence_ajax')
|
|
resp_abs = self.client.post(url_abs, data=json.dumps({
|
|
'member_id': self.member_alice.id,
|
|
'date': past_date,
|
|
'is_absent': True,
|
|
}), content_type='application/json')
|
|
self.assertEqual(resp_abs.status_code, 400)
|
|
self.assertIn('passé', resp_abs.json().get('error', '').lower())
|
|
|
|
# 3. Duplicate target in past
|
|
url_dup = reverse('contracts:team_planning_duplicate_ajax')
|
|
resp_dup = self.client.post(url_dup, data=json.dumps({
|
|
'mode': 'day',
|
|
'source_date': timezone.now().date().isoformat(),
|
|
'target_date': past_date,
|
|
}), content_type='application/json')
|
|
self.assertEqual(resp_dup.status_code, 400)
|
|
self.assertIn('passé', resp_dup.json().get('error', '').lower())
|
|
|
|
def test_reassign_future_member_to_same_team_as_previous_day(self):
|
|
"""
|
|
Vérifie qu'un membre présent dans Team 1 le vendredi (J) et Team 2 le samedi (J+1)
|
|
peut être replanifié dans Team 1 le samedi (J+1).
|
|
"""
|
|
today = timezone.now().date()
|
|
friday = today + timedelta(days=2)
|
|
saturday = today + timedelta(days=3)
|
|
|
|
# 1. Vendredi dans Team A, Samedi dans Team B
|
|
set_member_team_schedule(self.member_alice, self.team_a.id, friday)
|
|
set_member_team_schedule(self.member_alice, self.team_b.id, saturday)
|
|
|
|
comp1 = get_teams_composition_for_period({self.team_a.id, self.team_b.id}, friday, saturday)
|
|
self.assertIn(self.member_alice.id, [m['id'] for m in comp1.get((self.team_a.id, friday), [])])
|
|
self.assertIn(self.member_alice.id, [m['id'] for m in comp1.get((self.team_b.id, saturday), [])])
|
|
|
|
# 2. Replanifier Alice dans Team A le samedi via AJAX
|
|
url = reverse('contracts:team_planning_assign_member_ajax')
|
|
resp = self.client.post(url, data=json.dumps({
|
|
'member_id': self.member_alice.id,
|
|
'team_id': self.team_a.id,
|
|
'date': saturday.isoformat(),
|
|
}), content_type='application/json')
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
# 3. Vérifier la composition : Alice est dans Team A le vendredi ET le samedi
|
|
comp2 = get_teams_composition_for_period({self.team_a.id, self.team_b.id}, friday, saturday)
|
|
team_a_friday = [m['id'] for m in comp2.get((self.team_a.id, friday), [])]
|
|
team_a_saturday = [m['id'] for m in comp2.get((self.team_a.id, saturday), [])]
|
|
team_b_saturday = [m['id'] for m in comp2.get((self.team_b.id, saturday), [])]
|
|
|
|
self.assertIn(self.member_alice.id, team_a_friday)
|
|
self.assertIn(self.member_alice.id, team_a_saturday)
|
|
self.assertNotIn(self.member_alice.id, team_b_saturday)
|
|
|
|
def test_unassign_member_for_specific_date(self):
|
|
"""Vérifie que la désassignation supprime le membre uniquement pour la date cliquée."""
|
|
today = timezone.now().date()
|
|
target_d = today + timedelta(days=2)
|
|
|
|
# 1. Assigner Alice sur target_d
|
|
set_member_team_schedule(self.member_alice, self.team_a.id, target_d)
|
|
comp1 = get_teams_composition_for_period({self.team_a.id}, target_d, target_d)
|
|
self.assertIn(self.member_alice.id, [m['id'] for m in comp1.get((self.team_a.id, target_d), [])])
|
|
|
|
# 2. Désassigner Alice pour target_d (team_id=None)
|
|
url = reverse('contracts:team_planning_assign_member_ajax')
|
|
resp = self.client.post(url, data=json.dumps({
|
|
'member_id': self.member_alice.id,
|
|
'team_id': None,
|
|
'date': target_d.isoformat(),
|
|
}), content_type='application/json')
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
# 3. Vérifier que la cellule est désormais vide
|
|
comp2 = get_teams_composition_for_period({self.team_a.id}, target_d, target_d)
|
|
self.assertNotIn(self.member_alice.id, [m['id'] for m in comp2.get((self.team_a.id, target_d), [])])
|
|
|
|
def test_multi_team_planning_assign_member_ajax_add_and_move(self):
|
|
"""Tester l'endpoint AJAX de planification avec action='add' puis action='move'."""
|
|
today = timezone.now().date()
|
|
target_date = today + timedelta(days=2)
|
|
|
|
# 1. Affecter à Team A
|
|
url = reverse('contracts:team_planning_assign_member_ajax')
|
|
resp = self.client.post(url, data=json.dumps({
|
|
'member_id': self.member_alice.id,
|
|
'team_id': self.team_a.id,
|
|
'date': target_date.isoformat(),
|
|
'action': 'move',
|
|
}), content_type='application/json')
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
# 2. Affecter aussi à Team B avec action='add'
|
|
resp = self.client.post(url, data=json.dumps({
|
|
'member_id': self.member_alice.id,
|
|
'team_id': self.team_b.id,
|
|
'date': target_date.isoformat(),
|
|
'action': 'add',
|
|
}), content_type='application/json')
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
teams = get_member_teams_for_date(self.member_alice, target_date)
|
|
self.assertEqual(len(teams), 2)
|
|
self.assertIn(self.team_a, teams)
|
|
self.assertIn(self.team_b, teams)
|
|
|
|
# 3. Déplacer vers Team A avec action='move' -> retire Team B
|
|
resp = self.client.post(url, data=json.dumps({
|
|
'member_id': self.member_alice.id,
|
|
'team_id': self.team_a.id,
|
|
'date': target_date.isoformat(),
|
|
'action': 'move',
|
|
}), content_type='application/json')
|
|
self.assertEqual(resp.status_code, 200)
|
|
|
|
teams_after = get_member_teams_for_date(self.member_alice, target_date)
|
|
self.assertEqual(len(teams_after), 1)
|
|
self.assertEqual(teams_after[0], self.team_a)
|
|
|
|
def test_multi_team_teams_move_member_immediate(self):
|
|
"""Tester le déplacement immédiat en multi-équipes via teams_move_member."""
|
|
today = timezone.now().date()
|
|
url = reverse('contracts:teams_move_member', args=[self.member_alice.id])
|
|
|
|
# Assigner à Team A
|
|
self.client.post(url, data=json.dumps({
|
|
'team_id': self.team_a.id,
|
|
'old_team_id': None,
|
|
'action': 'move',
|
|
}), content_type='application/json')
|
|
|
|
# Assigner aussi à Team B
|
|
self.client.post(url, data=json.dumps({
|
|
'team_id': self.team_b.id,
|
|
'old_team_id': self.team_a.id,
|
|
'action': 'add',
|
|
}), content_type='application/json')
|
|
|
|
self.member_alice.refresh_from_db()
|
|
self.assertEqual(self.member_alice.teams.count(), 2)
|
|
self.assertIn(self.team_a, self.member_alice.teams.all())
|
|
self.assertIn(self.team_b, self.member_alice.teams.all())
|
|
|
|
# Retirer de Team A uniquement
|
|
self.client.post(url, data=json.dumps({
|
|
'team_id': None,
|
|
'old_team_id': self.team_a.id,
|
|
}), content_type='application/json')
|
|
|
|
self.member_alice.refresh_from_db()
|
|
self.assertEqual(self.member_alice.teams.count(), 1)
|
|
self.assertEqual(self.member_alice.teams.first(), self.team_b)
|
|
|