feat: implement calendar functionality and add localization support for intervention models
This commit is contained in:
parent
56281b4821
commit
76158a3be0
9 changed files with 151 additions and 26 deletions
Binary file not shown.
|
|
@ -5444,31 +5444,31 @@ msgstr "View intervention"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:660
|
#: interventions/templates/interventions/interventions_calendar.html:660
|
||||||
msgid "Historique du membre"
|
msgid "Historique du membre"
|
||||||
msgstr "Sending history"
|
msgstr "Member history"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:675
|
#: interventions/templates/interventions/interventions_calendar.html:675
|
||||||
msgid "Technicien / Membre"
|
msgid "Technicien / Membre"
|
||||||
msgstr "Technician"
|
msgstr "Technician / Member"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:681
|
#: interventions/templates/interventions/interventions_calendar.html:681
|
||||||
msgid "Entreprise"
|
msgid "Entreprise"
|
||||||
msgstr "Maintenance"
|
msgstr "Company / Organization"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:694
|
#: interventions/templates/interventions/interventions_calendar.html:694
|
||||||
msgid "Historique des équipes"
|
msgid "Historique des équipes"
|
||||||
msgstr "Sending history"
|
msgstr "Team history"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:702
|
#: interventions/templates/interventions/interventions_calendar.html:702
|
||||||
msgid "Entrée"
|
msgid "Entrée"
|
||||||
msgstr "Maintenance"
|
msgstr "Joined"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:703
|
#: interventions/templates/interventions/interventions_calendar.html:703
|
||||||
msgid "Sortie"
|
msgid "Sortie"
|
||||||
msgstr "Sortie"
|
msgstr "Left"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:718
|
#: interventions/templates/interventions/interventions_calendar.html:718
|
||||||
msgid "Dernières interventions réalisées / assignées"
|
msgid "Dernières interventions réalisées / assignées"
|
||||||
msgstr "Export selected interventions"
|
msgstr "Recent realized / assigned interventions"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_index.html:420
|
#: interventions/templates/interventions/interventions_index.html:420
|
||||||
msgid "Choisir le type d’intervention"
|
msgid "Choisir le type d’intervention"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -5502,15 +5502,15 @@ msgstr "Technicien"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:681
|
#: interventions/templates/interventions/interventions_calendar.html:681
|
||||||
msgid "Entreprise"
|
msgid "Entreprise"
|
||||||
msgstr "Entretien"
|
msgstr "Entreprise / Organisme"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:694
|
#: interventions/templates/interventions/interventions_calendar.html:694
|
||||||
msgid "Historique des équipes"
|
msgid "Historique des équipes"
|
||||||
msgstr "Historique des envois"
|
msgstr "Historique des équipes"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:702
|
#: interventions/templates/interventions/interventions_calendar.html:702
|
||||||
msgid "Entrée"
|
msgid "Entrée"
|
||||||
msgstr "Entretien"
|
msgstr "Entrée"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:703
|
#: interventions/templates/interventions/interventions_calendar.html:703
|
||||||
msgid "Sortie"
|
msgid "Sortie"
|
||||||
|
|
@ -5518,7 +5518,7 @@ msgstr "Sortie"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:718
|
#: interventions/templates/interventions/interventions_calendar.html:718
|
||||||
msgid "Dernières interventions réalisées / assignées"
|
msgid "Dernières interventions réalisées / assignées"
|
||||||
msgstr "Exporter les interventions sélectionnées"
|
msgstr "Dernières interventions réalisées / assignées"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_index.html:420
|
#: interventions/templates/interventions/interventions_index.html:420
|
||||||
msgid "Choisir le type d’intervention"
|
msgid "Choisir le type d’intervention"
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -5458,31 +5458,31 @@ msgstr "Interventie bekijken"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:660
|
#: interventions/templates/interventions/interventions_calendar.html:660
|
||||||
msgid "Historique du membre"
|
msgid "Historique du membre"
|
||||||
msgstr "Verzendgeschiedenis"
|
msgstr "Geschiedenis van het lid"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:675
|
#: interventions/templates/interventions/interventions_calendar.html:675
|
||||||
msgid "Technicien / Membre"
|
msgid "Technicien / Membre"
|
||||||
msgstr "Technicus"
|
msgstr "Technicus / Lid"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:681
|
#: interventions/templates/interventions/interventions_calendar.html:681
|
||||||
msgid "Entreprise"
|
msgid "Entreprise"
|
||||||
msgstr "Onderhoud"
|
msgstr "Bedrijf / Organisatie"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:694
|
#: interventions/templates/interventions/interventions_calendar.html:694
|
||||||
msgid "Historique des équipes"
|
msgid "Historique des équipes"
|
||||||
msgstr "Verzendgeschiedenis"
|
msgstr "Ploegengeschiedenis"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:702
|
#: interventions/templates/interventions/interventions_calendar.html:702
|
||||||
msgid "Entrée"
|
msgid "Entrée"
|
||||||
msgstr "Onderhoud"
|
msgstr "Instroom"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:703
|
#: interventions/templates/interventions/interventions_calendar.html:703
|
||||||
msgid "Sortie"
|
msgid "Sortie"
|
||||||
msgstr ""
|
msgstr "Uitstroom"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_calendar.html:718
|
#: interventions/templates/interventions/interventions_calendar.html:718
|
||||||
msgid "Dernières interventions réalisées / assignées"
|
msgid "Dernières interventions réalisées / assignées"
|
||||||
msgstr "Geselecteerde interventies exporteren"
|
msgstr "Laatste uitgevoerde / toegewezen interventies"
|
||||||
|
|
||||||
#: interventions/templates/interventions/interventions_index.html:420
|
#: interventions/templates/interventions/interventions_index.html:420
|
||||||
msgid "Choisir le type d’intervention"
|
msgid "Choisir le type d’intervention"
|
||||||
|
|
|
||||||
|
|
@ -639,6 +639,10 @@ class Intervention(models.Model):
|
||||||
while curr <= end_date:
|
while curr <= end_date:
|
||||||
active_dates.add(curr)
|
active_dates.add(curr)
|
||||||
curr += timedelta(days=1)
|
curr += timedelta(days=1)
|
||||||
|
if self.begin_time:
|
||||||
|
active_dates.add(self.begin_time.date())
|
||||||
|
if self.end_time:
|
||||||
|
active_dates.add(self.end_time.date())
|
||||||
|
|
||||||
today = timezone.now().date()
|
today = timezone.now().date()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -272,13 +272,45 @@ class CalendarEventsTests(TestCase):
|
||||||
|
|
||||||
def test_member_history_ajax_permissions_and_data(self):
|
def test_member_history_ajax_permissions_and_data(self):
|
||||||
from contracts.models import CompanyMember, CompanyMemberHistory
|
from contracts.models import CompanyMember, CompanyMemberHistory
|
||||||
|
from interventions.models import Intervention
|
||||||
member = CompanyMember.objects.create(name="Jean Dupont", user=self.user)
|
member = CompanyMember.objects.create(name="Jean Dupont", user=self.user)
|
||||||
member.teams.add(self.team_a)
|
member.teams.add(self.team_a)
|
||||||
|
|
||||||
|
ten_days_ago = timezone.now().date() - timedelta(days=10)
|
||||||
|
two_days_ago = timezone.now().date() - timedelta(days=2)
|
||||||
|
|
||||||
CompanyMemberHistory.objects.create(
|
CompanyMemberHistory.objects.create(
|
||||||
member=member,
|
member=member,
|
||||||
team=self.team_a,
|
team=self.team_a,
|
||||||
join_date=timezone.now().date() - timedelta(days=10)
|
join_date=ten_days_ago,
|
||||||
|
leave_date=two_days_ago,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 1. Intervention assigned to team_a 5 days ago (during membership), finished by someone else
|
||||||
|
itv1 = Intervention.objects.create(
|
||||||
|
title='Team Task Finished by Manager',
|
||||||
|
status='finished',
|
||||||
|
maintain_type='corrective',
|
||||||
|
assigned_provider=self.company,
|
||||||
|
assigned_team=self.team_a,
|
||||||
|
begin_time=timezone.now() - timedelta(days=5),
|
||||||
|
end_time=timezone.now() - timedelta(days=5),
|
||||||
|
)
|
||||||
|
|
||||||
|
# 2. Intervention directly processed today
|
||||||
|
itv2 = Intervention.objects.create(
|
||||||
|
title='Directly Processed Task',
|
||||||
|
status='processed',
|
||||||
|
maintain_type='corrective',
|
||||||
|
assigned_provider=self.company,
|
||||||
|
assigned_member=member,
|
||||||
|
begin_time=timezone.now(),
|
||||||
|
end_time=timezone.now(),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Clear active teams to simulate removed member
|
||||||
|
member.teams.clear()
|
||||||
|
|
||||||
url = reverse('interventions:member_history_ajax', args=[member.id])
|
url = reverse('interventions:member_history_ajax', args=[member.id])
|
||||||
response = self.client.get(url)
|
response = self.client.get(url)
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
@ -286,10 +318,26 @@ class CalendarEventsTests(TestCase):
|
||||||
|
|
||||||
self.assertIn('member', data)
|
self.assertIn('member', data)
|
||||||
self.assertEqual(data['member']['name'], "Jean Dupont")
|
self.assertEqual(data['member']['name'], "Jean Dupont")
|
||||||
|
# Company name should still be resolved from history
|
||||||
|
self.assertEqual(data['member']['company'], self.company.name)
|
||||||
|
|
||||||
self.assertIn('team_history', data)
|
self.assertIn('team_history', data)
|
||||||
self.assertTrue(len(data['team_history']) >= 1)
|
self.assertTrue(len(data['team_history']) >= 1)
|
||||||
self.assertEqual(data['team_history'][0]['team_name'], self.team_a.name)
|
self.assertEqual(data['team_history'][0]['team_name'], self.team_a.name)
|
||||||
|
|
||||||
|
# Interventions list should include both team task and directly processed task
|
||||||
|
self.assertIn('interventions', data)
|
||||||
|
itv_ids = [itv['id'] for itv in data['interventions']]
|
||||||
|
self.assertIn(itv1.id, itv_ids)
|
||||||
|
self.assertIn(itv2.id, itv_ids)
|
||||||
|
|
||||||
|
# Check status display and codes
|
||||||
|
itv1_entry = next(item for item in data['interventions'] if item['id'] == itv1.id)
|
||||||
|
self.assertEqual(itv1_entry['status_code'], 'finished')
|
||||||
|
|
||||||
|
itv2_entry = next(item for item in data['interventions'] if item['id'] == itv2.id)
|
||||||
|
self.assertEqual(itv2_entry['status_code'], 'processed')
|
||||||
|
|
||||||
def test_unassign_sans_equipe_reverts_status_to_backlog(self):
|
def test_unassign_sans_equipe_reverts_status_to_backlog(self):
|
||||||
itv = Intervention.objects.create(
|
itv = Intervention.objects.create(
|
||||||
title='Unassigned Test',
|
title='Unassigned Test',
|
||||||
|
|
|
||||||
|
|
@ -1371,13 +1371,11 @@ def member_history_ajax(request, member_id):
|
||||||
return JsonResponse({'error': _("Permission refusée.")}, status=403)
|
return JsonResponse({'error': _("Permission refusée.")}, status=403)
|
||||||
|
|
||||||
member = get_object_or_404(CompanyMember.objects.prefetch_related('teams__company'), id=member_id)
|
member = get_object_or_404(CompanyMember.objects.prefetch_related('teams__company'), id=member_id)
|
||||||
company_names = set(t.company.name for t in member.teams.all() if getattr(t, 'company', None))
|
|
||||||
company_str = ", ".join(sorted(company_names)) if company_names else ''
|
|
||||||
|
|
||||||
team_history_qs = (
|
team_history_qs = (
|
||||||
CompanyMemberHistory.objects
|
CompanyMemberHistory.objects
|
||||||
.filter(member=member)
|
.filter(member=member)
|
||||||
.select_related('team')
|
.select_related('team__company')
|
||||||
.order_by('-join_date')
|
.order_by('-join_date')
|
||||||
)
|
)
|
||||||
team_history = [
|
team_history = [
|
||||||
|
|
@ -1390,12 +1388,51 @@ def member_history_ajax(request, member_id):
|
||||||
for h in team_history_qs
|
for h in team_history_qs
|
||||||
]
|
]
|
||||||
|
|
||||||
|
company_names = set(t.company.name for t in member.teams.all() if getattr(t, 'company', None))
|
||||||
|
if not company_names:
|
||||||
|
company_names = set(h.team.company.name for h in team_history_qs if h.team and getattr(h.team, 'company', None))
|
||||||
|
if not company_names and member.user_id:
|
||||||
|
company_names = set(
|
||||||
|
uca.contract.company.name for uca in UserContractAccess.objects.filter(user_config__user=member.user)
|
||||||
|
if uca.contract and getattr(uca.contract, 'company', None)
|
||||||
|
)
|
||||||
|
company_str = ", ".join(sorted(company_names)) if company_names else '—'
|
||||||
|
|
||||||
|
# Filter assignments:
|
||||||
|
# 1. Directly assigned to this member in InterventionAssignment
|
||||||
|
assignment_q = Q(member=member)
|
||||||
|
|
||||||
|
# 2. Directly assigned / acted upon by member user on Intervention
|
||||||
|
if member.user_id:
|
||||||
|
assignment_q |= (
|
||||||
|
Q(intervention__assigned_member=member) |
|
||||||
|
Q(intervention__realized_events__event_user=member.user) |
|
||||||
|
Q(intervention__occupations__member=member)
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. Interventions assigned to teams during the periods member was in that team
|
||||||
|
team_periods_q = Q()
|
||||||
|
for h in team_history_qs:
|
||||||
|
if h.team_id and h.join_date:
|
||||||
|
tq = Q(team_id=h.team_id, date__gte=h.join_date)
|
||||||
|
if h.leave_date:
|
||||||
|
tq &= Q(date__lte=h.leave_date)
|
||||||
|
team_periods_q |= tq
|
||||||
|
|
||||||
|
for t in member.teams.all():
|
||||||
|
if not team_history_qs.filter(team=t).exists():
|
||||||
|
team_periods_q |= Q(team=t)
|
||||||
|
|
||||||
|
if team_periods_q:
|
||||||
|
assignment_q |= team_periods_q
|
||||||
|
|
||||||
assignments_qs = (
|
assignments_qs = (
|
||||||
InterventionAssignment.objects
|
InterventionAssignment.objects
|
||||||
.filter(member=member)
|
.filter(assignment_q)
|
||||||
.select_related('intervention', 'team')
|
.select_related('intervention', 'team')
|
||||||
.order_by('-date', '-intervention__code', '-intervention__id')[:100]
|
.order_by('-date', '-intervention__code', '-intervention__id')[:100]
|
||||||
)
|
)
|
||||||
|
|
||||||
interventions_list = [
|
interventions_list = [
|
||||||
{
|
{
|
||||||
'id': a.intervention.id,
|
'id': a.intervention.id,
|
||||||
|
|
@ -1403,14 +1440,50 @@ def member_history_ajax(request, member_id):
|
||||||
'title': a.intervention.title,
|
'title': a.intervention.title,
|
||||||
'date': a.date.strftime('%d/%m/%Y') if a.date else '',
|
'date': a.date.strftime('%d/%m/%Y') if a.date else '',
|
||||||
'raw_date': a.date.isoformat() if a.date else '',
|
'raw_date': a.date.isoformat() if a.date else '',
|
||||||
'team_name': a.team.name if a.team else '—',
|
'team_name': a.team.name if a.team else (a.intervention.assigned_team.name if a.intervention.assigned_team else '—'),
|
||||||
'status_code': a.status or a.intervention.status,
|
'status_code': a.intervention.status,
|
||||||
'status_display': dict(STATUS_CHOICES).get(a.status or a.intervention.status, a.status or a.intervention.status),
|
'status_display': a.intervention.get_status_display(),
|
||||||
'detail_url': reverse('interventions:intervention_detail', args=[a.intervention.id]),
|
'detail_url': reverse('interventions:intervention_detail', args=[a.intervention.id]),
|
||||||
}
|
}
|
||||||
for a in assignments_qs
|
for a in assignments_qs
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Include any extra interventions directly assigned/realized by member without an InterventionAssignment
|
||||||
|
assigned_itv_ids = set(a.intervention.id for a in assignments_qs)
|
||||||
|
direct_itvs_q = Q(assigned_member=member)
|
||||||
|
if member.user_id:
|
||||||
|
direct_itvs_q |= Q(realized_events__event_user=member.user) | Q(occupations__member=member)
|
||||||
|
|
||||||
|
extra_itvs = (
|
||||||
|
Intervention.objects
|
||||||
|
.filter(direct_itvs_q)
|
||||||
|
.exclude(id__in=assigned_itv_ids)
|
||||||
|
.select_related('assigned_team')[:50]
|
||||||
|
)
|
||||||
|
for itv in extra_itvs:
|
||||||
|
itv_date = (itv.end_time or itv.begin_time or itv.planned_begin_time or itv.creation_time)
|
||||||
|
if itv_date:
|
||||||
|
itv_date_local = timezone.localtime(itv_date) if timezone.is_aware(itv_date) else itv_date
|
||||||
|
date_str = itv_date_local.strftime('%d/%m/%Y')
|
||||||
|
raw_date_str = itv_date_local.date().isoformat()
|
||||||
|
else:
|
||||||
|
date_str = ''
|
||||||
|
raw_date_str = ''
|
||||||
|
|
||||||
|
interventions_list.append({
|
||||||
|
'id': itv.id,
|
||||||
|
'code': itv.code,
|
||||||
|
'title': itv.title,
|
||||||
|
'date': date_str,
|
||||||
|
'raw_date': raw_date_str,
|
||||||
|
'team_name': itv.assigned_team.name if itv.assigned_team else '—',
|
||||||
|
'status_code': itv.status,
|
||||||
|
'status_display': itv.get_status_display(),
|
||||||
|
'detail_url': reverse('interventions:intervention_detail', args=[itv.id]),
|
||||||
|
})
|
||||||
|
|
||||||
interventions_list.sort(key=lambda x: (x['raw_date'], x['code'] or '', x['id']), reverse=True)
|
interventions_list.sort(key=lambda x: (x['raw_date'], x['code'] or '', x['id']), reverse=True)
|
||||||
|
interventions_list = interventions_list[:100]
|
||||||
|
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
'member': {
|
'member': {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue