diff --git a/passerelle/contrib/toulouse_maelis/activity_schemas.py b/passerelle/contrib/toulouse_maelis/activity_schemas.py
index 58584a11..f203c42e 100644
--- a/passerelle/contrib/toulouse_maelis/activity_schemas.py
+++ b/passerelle/contrib/toulouse_maelis/activity_schemas.py
@@ -83,6 +83,42 @@ BOOKING_ACTIVITY_SCHEMA = {
],
}
+UPDATE_RECURRENT_WEEK_SCHEMA = {
+ 'type': 'object',
+ 'properties': {
+ 'person_id': {
+ 'type': 'string',
+ 'pattern': '^[0-9]+$',
+ },
+ 'activity_id': {
+ 'type': 'string',
+ 'pattern': '^[A-Za-z0-9]+$',
+ },
+ 'start_date': {
+ 'type': 'string',
+ 'pattern': '^[0-9]{4}-[0-9]{2}-[0-9]{2}$',
+ },
+ 'end_date': {
+ 'type': 'string',
+ 'pattern': '^([0-9]{4}-[0-9]{2}-[0-9]{2}){0,1}$',
+ },
+ 'recurrent_week': {
+ 'type': 'array',
+ 'items': {
+ 'type': 'string',
+ 'pattern': '^[1-7]-[A-Z]$',
+ },
+ },
+ },
+ 'required': [
+ 'person_id',
+ 'activity_id',
+ 'start_date',
+ 'end_date',
+ 'recurrent_week',
+ ],
+}
+
SUBSCRIPTION_SCHEMA = {
'type': 'object',
'properties': {
diff --git a/passerelle/contrib/toulouse_maelis/models.py b/passerelle/contrib/toulouse_maelis/models.py
index c5b71a4f..bbb1bc31 100644
--- a/passerelle/contrib/toulouse_maelis/models.py
+++ b/passerelle/contrib/toulouse_maelis/models.py
@@ -2198,6 +2198,124 @@ class ToulouseMaelis(BaseResource, HTTPResource):
'changes': updated,
}
+ @endpoint(
+ display_category='Réservation',
+ description="Obtenir la semaine type d'une activité",
+ name='get-recurrent-week',
+ perm='can_access',
+ parameters={
+ 'NameID': {'description': 'Publik NameID'},
+ 'family_id': {'description': 'Numéro de DUI'},
+ 'person_id': {'description': "Numéro du responsable légal ou de l'enfant"},
+ 'activity_id': {'description': "Numéro de l'activité"},
+ 'ref_date': {
+ 'description': "Date de référence, utilisée pour interroger l'agenda sur l'année et le mois",
+ 'type': 'date',
+ },
+ },
+ )
+ def get_recurrent_week(self, request, person_id, activity_id, ref_date, NameID=None, family_id=None):
+ family_id = family_id or self.get_link(NameID).family_id
+ self.get_rl_or_child_raw(family_id, person_id)
+
+ payload = {
+ 'requestBean': {
+ 'numDossier': family_id,
+ 'numPerson': person_id,
+ 'idAct': activity_id,
+ 'year': ref_date.year,
+ 'month': ref_date.month,
+ }
+ }
+ response = self.call('Activity', 'getPersonScheduleList', **payload)
+
+ date_min_prev = None
+ recurrent_week = []
+ day_names = ['Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi', 'Dimanche']
+ for result_data in response or []:
+ for schedule in result_data['activityScheduleList']:
+ if schedule['activity']['idAct'] != activity_id or not schedule.get('weeklyCalendar'):
+ continue
+ units = []
+ for item in schedule['unitScheduleList']:
+ key = item['unit']['calendarLetter']
+ value = item['unit']['libelle']
+ units.append((key, value))
+ if item.get('datePrevMin'):
+ if not date_min_prev or item['datePrevMin'] > date_min_prev:
+ date_min_prev = item['datePrevMin']
+ for item in schedule['weeklyCalendar'].get('dayWeekInfoList') or []:
+ if item['isOpen']:
+ day_num = item['dayNum']
+ day_str = day_names[day_num - 1]
+ for key, value in units:
+ recurrent_week.append(
+ {
+ 'id': '%s-%s' % (day_num, key),
+ 'day': day_str,
+ 'label': value,
+ 'overlaps': ['%s-%s' % (day_num, k) for k, v in units if k != key],
+ 'text': '%s %s' % (day_str, value),
+ }
+ )
+ if not recurrent_week:
+ raise APIError(
+ 'No week calendar for activity %s on %s'
+ % (activity_id, ref_date.strftime(utils.json_date_format))
+ )
+ return {
+ 'data': recurrent_week,
+ 'meta': {'date_min_prev': date_min_prev.strftime(utils.json_date_format)},
+ }
+
+ @endpoint(
+ display_category='Réservation',
+ description="Modifier la semaine type d'une inscription",
+ name='update-recurrent-week',
+ perm='can_access',
+ parameters={
+ 'NameID': {'description': 'Publik NameID'},
+ 'family_id': {'description': 'Numéro de DUI'},
+ },
+ post={
+ 'request_body': {
+ 'schema': {
+ 'application/json': activity_schemas.UPDATE_RECURRENT_WEEK_SCHEMA,
+ }
+ }
+ },
+ )
+ def update_recurrent_week(self, request, post_data, NameID=None, family_id=None):
+ family_id = family_id or self.get_link(NameID).family_id
+ self.get_rl_or_child_raw(family_id, post_data['person_id'])
+
+ recurrent_week = {}
+ for i in range(1, 8):
+ day_num = str(i)
+ recurrent_week[day_num] = {
+ 'dayNum': day_num,
+ 'calendarLetter': None,
+ 'isPresent': False,
+ }
+ for item in post_data.get('recurrent_week'):
+ day_num, key = item.split('-')
+ recurrent_week[day_num] = {
+ 'dayNum': day_num,
+ 'calendarLetter': key,
+ 'isPresent': True,
+ }
+ recurrent_week = sorted(recurrent_week.values(), key=lambda x: (x['dayNum']))
+
+ payload = {
+ 'numPerson': post_data['person_id'],
+ 'idActivity': post_data['activity_id'],
+ 'dateStart': post_data['start_date'],
+ 'dateEnd': post_data.get('end_date'),
+ 'dayWeekInfoList': recurrent_week,
+ }
+ self.call('Activity', 'updateWeekCalendar', **payload)
+ return {'data': 'ok'}
+
@endpoint(
display_category='Facture',
description="Ajouter une autorisation de prélèvement",
diff --git a/tests/data/toulouse_maelis/R_get_person_schedule_list_with_recurrent_week.xml b/tests/data/toulouse_maelis/R_get_person_schedule_list_with_recurrent_week.xml
new file mode 100644
index 00000000..def692b8
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_get_person_schedule_list_with_recurrent_week.xml
@@ -0,0 +1,466 @@
+
+
+
+
+
+
+ 261768
+ NICO
+ BART
+
+
+
+ A10049327682
+ RESTAURATION SCOLAIRE 22/23
+
+ RESTSCOL
+ Restauration scolaire
+
+ R
+ Restauration Scolaire
+
+
+
+
+ 2023
+
+ 1
+ true
+
+
+ 2
+ true
+
+
+ 3
+ false
+
+
+ 4
+ true
+
+
+ 5
+ true
+
+
+ 6
+ false
+
+
+ 7
+ false
+
+
+
+
+ A10049355140
+ PAI PANIER 22/23
+ B
+
+ 2023-03-27T00:00:00+02:00
+
+ 2023-04-01T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-02T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-03T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-04T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-05T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-06T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-07T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-08T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-09T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-10T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-11T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-12T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-13T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-14T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-15T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-16T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-17T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-18T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-19T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-20T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-21T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-22T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-23T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-24T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-25T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-26T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-27T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-28T00:00:00+02:00
+ 0
+ 0
+ WRITABLE
+ ADD_PRES_PREVI
+
+
+ 2023-04-29T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-30T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+
+
+ A10049327683
+ RESTAURATION SCOLAIRE 22/23
+ X
+
+ 2023-03-27T00:00:00+02:00
+
+ 2023-04-01T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-02T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-03T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-04T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-05T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-06T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-07T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-08T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-09T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-10T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-11T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-12T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-13T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-14T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-15T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-16T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-17T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-18T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-19T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-20T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-21T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-22T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-23T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-24T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-25T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-26T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-27T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-28T00:00:00+02:00
+ 1
+ 0
+ WRITABLE
+ DEL_PRES_PREVI
+
+
+ 2023-04-29T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+ 2023-04-30T00:00:00+02:00
+ 0
+ 0
+ NO_READ
+
+
+
+
+
+
+
+
diff --git a/tests/data/toulouse_maelis/R_update_week_calendar.xml b/tests/data/toulouse_maelis/R_update_week_calendar.xml
new file mode 100644
index 00000000..9ab307d1
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_update_week_calendar.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/tests/data/toulouse_maelis/R_update_week_calendar_error.xml b/tests/data/toulouse_maelis/R_update_week_calendar_error.xml
new file mode 100644
index 00000000..f245a654
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_update_week_calendar_error.xml
@@ -0,0 +1,13 @@
+
+
+
+ soap:Server
+ E911 : Le calendrier hebdomadaire est incohérent avec la lettre de l'unité
+
+
+ E911 : Le calendrier hebdomadaire est incohérent avec la lettre de l'unité
+
+
+
+
+
diff --git a/tests/test_toulouse_maelis.py b/tests/test_toulouse_maelis.py
index ca62bd21..19cf794d 100644
--- a/tests/test_toulouse_maelis.py
+++ b/tests/test_toulouse_maelis.py
@@ -5189,6 +5189,205 @@ def test_update_child_agenda_date_error(con, app):
assert resp.json['err_desc'] == 'start_date and end_date are in different reference year (2022 != 2023)'
+def test_get_recurrent_week(family_service, activity_service, con, app):
+ def request_check(request):
+ assert request.year == 2023
+ assert request.month == 4
+
+ family_service.add_soap_response('readFamily', get_xml_file('R_read_family.xml'))
+ activity_service.add_soap_response(
+ 'getPersonScheduleList',
+ get_xml_file('R_get_person_schedule_list_with_recurrent_week.xml'),
+ request_check=request_check,
+ )
+ url = get_endpoint('get-recurrent-week')
+ params = {
+ 'person_id': '613880',
+ 'activity_id': 'A10049327682',
+ 'ref_date': '2023-04-01',
+ }
+
+ resp = app.get(url + '?family_id=1312', params=params)
+ assert resp.json['err'] == 0
+ Link.objects.create(resource=con, family_id='1312', name_id='local')
+
+ resp = app.get(url + '?NameID=local', params=params)
+ assert resp.json['err'] == 0
+ assert resp.json['data'] == [
+ {
+ 'id': '1-B',
+ 'day': 'Lundi',
+ 'label': 'PAI PANIER 22/23',
+ 'overlaps': ['1-X'],
+ 'text': 'Lundi PAI PANIER 22/23',
+ },
+ {
+ 'id': '1-X',
+ 'day': 'Lundi',
+ 'label': 'RESTAURATION SCOLAIRE 22/23',
+ 'overlaps': ['1-B'],
+ 'text': 'Lundi RESTAURATION SCOLAIRE 22/23',
+ },
+ {
+ 'id': '2-B',
+ 'day': 'Mardi',
+ 'label': 'PAI PANIER 22/23',
+ 'overlaps': ['2-X'],
+ 'text': 'Mardi PAI PANIER 22/23',
+ },
+ {
+ 'id': '2-X',
+ 'day': 'Mardi',
+ 'label': 'RESTAURATION SCOLAIRE 22/23',
+ 'overlaps': ['2-B'],
+ 'text': 'Mardi RESTAURATION SCOLAIRE 22/23',
+ },
+ {
+ 'id': '4-B',
+ 'day': 'Jeudi',
+ 'label': 'PAI PANIER 22/23',
+ 'overlaps': ['4-X'],
+ 'text': 'Jeudi PAI PANIER 22/23',
+ },
+ {
+ 'id': '4-X',
+ 'day': 'Jeudi',
+ 'label': 'RESTAURATION SCOLAIRE 22/23',
+ 'overlaps': ['4-B'],
+ 'text': 'Jeudi RESTAURATION SCOLAIRE 22/23',
+ },
+ {
+ 'id': '5-B',
+ 'day': 'Vendredi',
+ 'label': 'PAI PANIER 22/23',
+ 'overlaps': ['5-X'],
+ 'text': 'Vendredi PAI PANIER 22/23',
+ },
+ {
+ 'id': '5-X',
+ 'day': 'Vendredi',
+ 'label': 'RESTAURATION SCOLAIRE 22/23',
+ 'overlaps': ['5-B'],
+ 'text': 'Vendredi RESTAURATION SCOLAIRE 22/23',
+ },
+ ]
+ assert resp.json['meta'] == {'date_min_prev': '2023-03-27'}
+
+ params['activity_id'] = 'plop'
+ resp = app.get(url + '?NameID=local', params=params)
+ assert resp.json['err'] == 1
+ assert resp.json['err_desc'] == 'No week calendar for activity plop on 2023-04-01'
+
+
+def test_get_recurrent_week_not_linked_error(con, app):
+ url = get_endpoint('get-recurrent-week')
+ params = {
+ 'person_id': '613880',
+ 'activity_id': 'A10049327682',
+ 'ref_date': '2023-04-01',
+ }
+ resp = app.get(url + '?NameID=local', params=params)
+ assert resp.json['err'] == 1
+ assert resp.json['err_desc'] == 'User not linked to family'
+
+
+def test_get_recurrent_week_person_not_found(family_service, con, app):
+ family_service.add_soap_response('readFamily', get_xml_file('R_read_family.xml'))
+ url = get_endpoint('get-recurrent-week')
+ params = {
+ 'person_id': '000000',
+ 'activity_id': 'A10049327682',
+ 'ref_date': '2023-04-01',
+ }
+ resp = app.get(url + '?family_id=1312', params=params)
+ assert resp.json['err'] == 1
+ assert resp.json['err_desc'] == "no '000000' RL or child on '1312' family"
+
+
+def test_update_recurrent_week(family_service, activity_service, con, app):
+ def request_check(request):
+ assert serialize_object(request.dayWeekInfoList) == [
+ {'dayNum': 1, 'isPresent': True, 'isOpen': None, 'calendarLetter': 'X'},
+ {'dayNum': 2, 'isPresent': True, 'isOpen': None, 'calendarLetter': 'B'},
+ {'dayNum': 3, 'isPresent': False, 'isOpen': None, 'calendarLetter': None},
+ {'dayNum': 4, 'isPresent': False, 'isOpen': None, 'calendarLetter': None},
+ {'dayNum': 5, 'isPresent': False, 'isOpen': None, 'calendarLetter': None},
+ {'dayNum': 6, 'isPresent': False, 'isOpen': None, 'calendarLetter': None},
+ {'dayNum': 7, 'isPresent': False, 'isOpen': None, 'calendarLetter': None},
+ ]
+
+ family_service.add_soap_response('readFamily', get_xml_file('R_read_family.xml'))
+ activity_service.add_soap_response(
+ 'updateWeekCalendar',
+ get_xml_file('R_update_week_calendar.xml'),
+ request_check=request_check,
+ )
+ url = get_endpoint('update-recurrent-week')
+ params = {
+ 'person_id': '613880',
+ 'activity_id': 'A10049327682',
+ 'start_date': '2023-04-01',
+ 'end_date': '2023-04-30',
+ 'recurrent_week': ['1-X', '2-B'],
+ }
+
+ resp = app.post_json(url + '?family_id=311323', params=params)
+ assert resp.json['err'] == 0
+ assert resp.json['data'] == 'ok'
+
+ Link.objects.create(resource=con, family_id='311323', name_id='local')
+ resp = app.post_json(url + '?NameID=local', params=params)
+ assert resp.json['err'] == 0
+ assert resp.json['data'] == 'ok'
+
+
+def test_update_recurrent_week_not_linked_error(con, app):
+ url = get_endpoint('update-recurrent-week')
+ params = {
+ 'person_id': '613880',
+ 'activity_id': 'A10049327682',
+ 'start_date': '2023-04-01',
+ 'end_date': '',
+ 'recurrent_week': ['1-X', '2-B'],
+ }
+ resp = app.post_json(url + '?NameID=local', params=params)
+ assert resp.json['err'] == 1
+ assert resp.json['err_desc'] == 'User not linked to family'
+
+
+def test_update_recurrent_week_person_not_found(family_service, con, app):
+ family_service.add_soap_response('readFamily', get_xml_file('R_read_family.xml'))
+ url = get_endpoint('update-recurrent-week')
+ params = {
+ 'person_id': '000000',
+ 'activity_id': 'A10049327682',
+ 'start_date': '2023-04-01',
+ 'end_date': '',
+ 'recurrent_week': ['1-X', '2-B'],
+ }
+ resp = app.post_json(url + '?family_id=1312', params=params)
+ assert resp.json['err'] == 1
+ assert resp.json['err_desc'] == "no '000000' RL or child on '1312' family"
+
+
+def test_update_recurrent_week_soap_error(family_service, activity_service, con, app):
+ family_service.add_soap_response('readFamily', get_xml_file('R_read_family.xml'))
+ activity_service.add_soap_response('updateWeekCalendar', get_xml_file('R_update_week_calendar_error.xml'))
+ url = get_endpoint('update-recurrent-week')
+ params = {
+ 'person_id': '613880',
+ 'activity_id': 'A10049327682',
+ 'start_date': '2023-04-01',
+ 'end_date': '2023-04-30',
+ 'recurrent_week': ['1-Z'],
+ }
+ resp = app.post_json(url + '?family_id=311323', params=params)
+ assert resp.json['err'] == 1
+ assert (
+ "E911 : Le calendrier hebdomadaire est incohérent avec la lettre de l'unité" in resp.json['err_desc']
+ )
+
+
def test_read_school_list_address_and_level(site_service, con, app):
site_service.add_soap_response(
'readSchoolForAdressAndLevel', get_xml_file('R_read_school_for_adress_and_level.xml')