diff --git a/loko/contracts/services_teams.py b/loko/contracts/services_teams.py index b019ae5..2f75876 100644 --- a/loko/contracts/services_teams.py +++ b/loko/contracts/services_teams.py @@ -94,8 +94,12 @@ def set_member_team_schedule( # 2. Clôturer ou ajuster l'historique courant (actif à target_date) if current_at_target: if current_at_target.join_date == target_date: - current_at_target.leave_date = target_date - current_at_target.save(update_fields=['leave_date']) + if target_date > today or team_id is None: + # Sur une date future ou lors d'une désassignation, on supprime directement l'affectation du jour + current_at_target.delete() + else: + current_at_target.leave_date = target_date + current_at_target.save(update_fields=['leave_date']) else: # L'historique commençait avant target_date : on le clôture la veille prev_leave = target_date - timedelta(days=1) diff --git a/loko/contracts/templates/contracts/teams_index.html b/loko/contracts/templates/contracts/teams_index.html index ee67264..26195c9 100644 --- a/loko/contracts/templates/contracts/teams_index.html +++ b/loko/contracts/templates/contracts/teams_index.html @@ -1718,8 +1718,6 @@ function filterSidebarMembers() { } function unassignMemberFromDate(memberId, dateStr) { - if (!confirm("{% trans 'Désassigner ce membre à partir de cette date ?'|escapejs %}")) return; - postJSON( '/contracts/teams/planning/assign/', { diff --git a/loko/contracts/test_planning.py b/loko/contracts/test_planning.py index fe73397..c09a1f8 100644 --- a/loko/contracts/test_planning.py +++ b/loko/contracts/test_planning.py @@ -264,3 +264,62 @@ class TeamPlanningAjaxViewsTests(TestCase): 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), [])]) +