From f0e91117f14e5c34a179edd9ee851f174f6695a7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 30 Jan 2020 15:52:01 +0100
Subject: [PATCH] toulouse_axel: add registration info to family info endpoints
 (#39242)

---
 functests/toulouse_axel/test_toulouse_axel.py |   3 +-
 passerelle/contrib/toulouse_axel/models.py    |  81 ++++++++++----
 tests/test_toulouse_axel.py                   | 101 ++++++++++++++++--
 3 files changed, 155 insertions(+), 30 deletions(-)

diff --git a/functests/toulouse_axel/test_toulouse_axel.py b/functests/toulouse_axel/test_toulouse_axel.py
index b6214603..a90b1b11 100644
--- a/functests/toulouse_axel/test_toulouse_axel.py
+++ b/functests/toulouse_axel/test_toulouse_axel.py
@@ -55,7 +55,8 @@ def test_link(conn, user):
     for key in ['MONTANTTOTAL', 'DATEVALIDITE', 'SFI', 'IREVENUS', 'RNF', 'NBENFANTSACHARGE']:
         payload['REVENUS'].pop(key, None)
     for enfant in payload['ENFANT']:
-        for key in ['NOM', 'DATENAISSANCE', 'SEXE', 'PRENOMPERE', 'PRENOMMERE', 'NOMPERE', 'NOMMERE', 'RATTACHEAUTREDUI', 'PRENOM']:
+        for key in ['NOM', 'DATENAISSANCE', 'SEXE', 'PRENOMPERE', 'PRENOMMERE', 'NOMPERE', 'NOMMERE', 'RATTACHEAUTREDUI', 'PRENOM',
+                    'clae_cantine_current', 'clae_cantine_next']:
             enfant.pop(key)
         enfant['AUTORISATIONURGENCEMEDICALE'] = 'OUI'
         if 'SANITAIRE' not in enfant:
diff --git a/passerelle/contrib/toulouse_axel/models.py b/passerelle/contrib/toulouse_axel/models.py
index 4d7a0e4d..c5c7e3ee 100644
--- a/passerelle/contrib/toulouse_axel/models.py
+++ b/passerelle/contrib/toulouse_axel/models.py
@@ -452,17 +452,34 @@ class ToulouseAxel(BaseResource):
         link.delete()
         return {'link': link_id, 'deleted': True, 'dui': link.dui}
 
-    def get_family_data(self, name_id):
-        link = self.get_link(name_id)
+    def get_family_data(self, dui, check_registrations=False):
         try:
-            result = ref_famille_dui(self, {'PORTAIL': {'DUI': {'IDDUI': link.dui}}})
+            result = ref_famille_dui(self, {'PORTAIL': {'DUI': {'IDDUI': dui}}})
         except AxelError as e:
             raise APIError(
                 'Axel error: %s' % e,
                 err_code='error',
                 data={'xml_request': e.xml_request,
                       'xml_response': e.xml_response})
-        return result.json_response['DATA']['PORTAIL']['DUI']
+        family_data = result.json_response['DATA']['PORTAIL']['DUI']
+
+        if check_registrations:
+            today = datetime.date.today()
+            current_reference_year = utils.get_reference_year_from_date(today)
+            next_reference_year = current_reference_year + 1
+
+            children_registred_for_current_year = self.are_children_registered(
+                dui=dui,
+                reference_year=current_reference_year)
+            children_registred_for_next_year = self.are_children_registered(
+                dui=dui,
+                reference_year=next_reference_year)
+
+            for child in family_data.get('ENFANT', []):
+                child['clae_cantine_current'] = children_registred_for_current_year.get(child['IDPERSONNE'])
+                child['clae_cantine_next'] = children_registred_for_next_year.get(child['IDPERSONNE'])
+
+        return family_data
 
     @endpoint(
         description=_("Get information about user's family"),
@@ -471,7 +488,8 @@ class ToulouseAxel(BaseResource):
             'NameID': {'description': _('Publik ID')},
         })
     def family_info(self, request, NameID):
-        family_data = self.get_family_data(NameID)
+        link = self.get_link(NameID)
+        family_data = self.get_family_data(link.dui, check_registrations=True)
         return {'data': family_data}
 
     @endpoint(
@@ -482,9 +500,10 @@ class ToulouseAxel(BaseResource):
             'idpersonne': {'description': _('Child ID')},
         })
     def child_info(self, request, idpersonne, NameID):
-        family_data = self.get_family_data(NameID)
+        link = self.get_link(NameID)
+        family_data = self.get_family_data(link.dui, check_registrations=True)
 
-        for child in family_data['ENFANT']:
+        for child in family_data.get('ENFANT', []):
             if child['IDPERSONNE'] == idpersonne:
                 return {'data': child}
 
@@ -564,7 +583,7 @@ class ToulouseAxel(BaseResource):
 
     UPDATE_FAMILY_SCHEMA['pre_process'] = pre_sanitize_update_family_data
 
-    def sanitize_update_family_data(self, name_id, post_data):
+    def sanitize_update_family_data(self, dui, post_data):
         family_data = None
 
         for i, child_data in enumerate(post_data.get('ENFANT', [])):
@@ -576,8 +595,8 @@ class ToulouseAxel(BaseResource):
                 continue
             # get family info
             if family_data is None:
-                family_data = self.get_family_data(name_id)
-            for orig_child in family_data['ENFANT']:
+                family_data = self.get_family_data(dui)
+            for orig_child in family_data.get('ENFANT', []):
                 # find the correct child in family info
                 if orig_child['IDPERSONNE'] != child_id:
                     continue
@@ -624,7 +643,7 @@ class ToulouseAxel(BaseResource):
             if rl not in post_data:
                 continue
             if family_data is None:
-                family_data = self.get_family_data(name_id)
+                family_data = self.get_family_data(dui)
             # fill missing fields
             for key in ['IDPERSONNE', 'NOM', 'PRENOM', 'NOMJEUNEFILLE', 'DATENAISSANCE', 'CIVILITE']:
                 post_data[rl][key] = family_data[rl][key]
@@ -633,7 +652,7 @@ class ToulouseAxel(BaseResource):
         # fill NBENFANTSACHARGE
         if 'REVENUS' in post_data:
             if family_data is None:
-                family_data = self.get_family_data(name_id)
+                family_data = self.get_family_data(dui)
             post_data['REVENUS']['NBENFANTSACHARGE'] = family_data.get('REVENUS', {}).get('NBENFANTSACHARGE')
 
         # remove flags
@@ -660,7 +679,7 @@ class ToulouseAxel(BaseResource):
         post_data['IDDUI'] = link.dui
         post_data['DATEDEMANDE'] = datetime.date.today().strftime('%Y-%m-%d')
 
-        self.sanitize_update_family_data(name_id=NameID, post_data=post_data)
+        self.sanitize_update_family_data(dui=link.dui, post_data=post_data)
 
         if 'RL2' in post_data and post_data['RL2'].get('IDPERSONNE') == link.person_id:
             post_data['QUIACTUALISEDUI'] = '2'
@@ -884,7 +903,7 @@ class ToulouseAxel(BaseResource):
                       'xml_response': e.xml_response})
         return {'data': True}
 
-    def get_child_activities(self, dui, reference_year, child_id):
+    def get_children_activities(self, dui, reference_year):
         try:
             result = enfants_activites(self, {
                 'DUI': {
@@ -900,12 +919,36 @@ class ToulouseAxel(BaseResource):
                 data={'xml_request': e.xml_request,
                       'xml_response': e.xml_response})
 
-        child_activities_data = result.json_response['DATA']['PORTAIL']['DUI'].get('ENFANT', [])
-        for child in child_activities_data:
-            if child['IDPERSONNE'] == child_id:
-                return child
+        children_activities = result.json_response['DATA']['PORTAIL']['DUI'].get('ENFANT', [])
+        return {child['IDPERSONNE']: child for child in children_activities}
 
-        raise APIError('Child not found', err_code='not-found')
+    def get_child_activities(self, dui, reference_year, child_id):
+        children_activities = self.get_children_activities(dui=dui, reference_year=reference_year)
+
+        if child_id not in children_activities:
+            raise APIError('Child not found', err_code='not-found')
+
+        return children_activities[child_id]
+
+    def are_children_registered(self, dui, reference_year):
+        # check reference_year
+        today = datetime.date.today()
+        current_reference_year = utils.get_reference_year_from_date(today)
+        # don't check registration for other years than current and next
+        if reference_year not in [current_reference_year, current_reference_year + 1]:
+            return {}
+        # if next year, check dates.
+        # check registration for next year only in june or july
+        if reference_year == current_reference_year + 1 and today.month not in [6, 7]:
+            return {}
+
+        # ok, check registrations
+        try:
+            children_activities = self.get_children_activities(dui=dui, reference_year=reference_year)
+        except APIError:
+            # don't fail on the check
+            return {}
+        return {child_id: bool(child.get('ACTIVITE', [])) for child_id, child in children_activities.items()}
 
     @endpoint(
         description=_("Get information about CLAE booking"),
diff --git a/tests/test_toulouse_axel.py b/tests/test_toulouse_axel.py
index 95ab7f4f..178a2960 100644
--- a/tests/test_toulouse_axel.py
+++ b/tests/test_toulouse_axel.py
@@ -24,6 +24,7 @@ import mock
 import os
 import xml.etree.ElementTree as ET
 
+import freezegun
 import pytest
 import xmlschema
 
@@ -44,6 +45,7 @@ from passerelle.contrib.toulouse_axel.models import (
     ref_verif_dui,
     reservation_periode,
 )
+from passerelle.contrib.toulouse_axel.utils import get_reference_year_from_date
 from passerelle.utils.jsonresponse import APIError
 import utils
 
@@ -662,6 +664,26 @@ def test_family_info_endpoint(app, resource):
         'TELFIXE',
     ])
 
+    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.are_children_registered') as registered:
+        registered.side_effect = [{'4242': True}, {}]
+        with mock_getdata(content, 'RefFamilleDui'):
+            resp = app.get('/toulouse-axel/test/family_info?NameID=yyy')
+    assert resp.json['err'] == 0
+    assert resp.json['data']['ENFANT'][0]['clae_cantine_current'] is True
+    assert resp.json['data']['ENFANT'][0]['clae_cantine_next'] is None
+    assert resp.json['data']['ENFANT'][1]['clae_cantine_current'] is None
+    assert resp.json['data']['ENFANT'][1]['clae_cantine_next'] is None
+
+    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.are_children_registered') as registered:
+        registered.side_effect = [{}, {'4242': False, '3535': True}]
+        with mock_getdata(content, 'RefFamilleDui'):
+            resp = app.get('/toulouse-axel/test/family_info?NameID=yyy')
+    assert resp.json['err'] == 0
+    assert resp.json['data']['ENFANT'][0]['clae_cantine_current'] is None
+    assert resp.json['data']['ENFANT'][0]['clae_cantine_next'] is False
+    assert resp.json['data']['ENFANT'][1]['clae_cantine_current'] is None
+    assert resp.json['data']['ENFANT'][1]['clae_cantine_next'] is True
+
 
 def test_child_info_endpoint_axel_error(app, resource):
     Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
@@ -709,8 +731,26 @@ def test_child_info_endpoint(app, resource):
         'RATTACHEAUTREDUI',
         'SANITAIRE',
         'SEXE',
+        'clae_cantine_current',
+        'clae_cantine_next',
     ])
 
+    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.are_children_registered') as registered:
+        registered.side_effect = [{'4242': True}, {}]
+        with mock_getdata(content, 'RefFamilleDui'):
+            resp = app.get('/toulouse-axel/test/child_info?NameID=yyy&idpersonne=4242')
+    assert resp.json['err'] == 0
+    assert resp.json['data']['clae_cantine_current'] is True
+    assert resp.json['data']['clae_cantine_next'] is None
+
+    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.are_children_registered') as registered:
+        registered.side_effect = [{}, {'4242': False}]
+        with mock_getdata(content, 'RefFamilleDui'):
+            resp = app.get('/toulouse-axel/test/child_info?NameID=yyy&idpersonne=4242')
+    assert resp.json['err'] == 0
+    assert resp.json['data']['clae_cantine_current'] is None
+    assert resp.json['data']['clae_cantine_next'] is False
+
 
 def test_update_family_info_endpoint_axel_error(app, resource, update_params, family_data):
     Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
@@ -786,7 +826,7 @@ def test_sanitize_update_family_data_missing_rl_fields(app, resource, update_par
         assert key not in full_update_params['RL2']
 
     with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data):
-        resource.sanitize_update_family_data(name_id='yyy', post_data=full_update_params)
+        resource.sanitize_update_family_data(dui='XXX', post_data=full_update_params)
     for key in ['IDPERSONNE', 'NOM', 'PRENOM', 'NOMJEUNEFILLE', 'DATENAISSANCE', 'CIVILITE']:
         assert full_update_params['RL1'][key] == family_data['RL1'][key]
         assert full_update_params['RL2'][key] == family_data['RL2'][key]
@@ -801,14 +841,14 @@ def test_sanitize_update_family_data_missing_revenus_fields(app, resource, updat
     assert 'NBENFANTSACHARGE' not in update_params['REVENUS']
 
     with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data):
-        resource.sanitize_update_family_data(name_id='yyy', post_data=full_update_params)
+        resource.sanitize_update_family_data(dui='XXX', post_data=full_update_params)
     assert full_update_params['REVENUS']['NBENFANTSACHARGE'] == family_data['REVENUS']['NBENFANTSACHARGE']
 
     # if revenus are not set in Axel
     full_update_params = copy.deepcopy(update_params)
     family_data.pop('REVENUS')
     with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data):
-        resource.sanitize_update_family_data(name_id='yyy', post_data=full_update_params)
+        resource.sanitize_update_family_data(dui='XXX', post_data=full_update_params)
     assert full_update_params['REVENUS']['NBENFANTSACHARGE'] is None
 
 
@@ -1089,7 +1129,7 @@ def test_sanitize_update_family_data_enfant_n_sanitaire_allergie(app, resource,
     partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['ALIMENTAIRES'] = 'NON'
     partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['AUTRES'] = ''
     with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data):
-        resource.sanitize_update_family_data(name_id='yyy', post_data=partial_update_params)
+        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
     assert partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE'] == [
         {'TYPE': 'ASTHME', 'ALLERGIQUE': 'NON', 'NOMALLERGIE': None},
         {'TYPE': 'MEDICAMENTEUSES', 'ALLERGIQUE': 'NON', 'NOMALLERGIE': None},
@@ -1103,7 +1143,7 @@ def test_sanitize_update_family_data_enfant_n_sanitaire_allergie(app, resource,
     partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['ALIMENTAIRES'] = 'OUI'
     partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE']['AUTRES'] = 'accariens'
     with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data):
-        resource.sanitize_update_family_data(name_id='yyy', post_data=partial_update_params)
+        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
     assert partial_update_params['ENFANT'][0]['SANITAIRE']['ALLERGIE'] == [
         {'TYPE': 'ASTHME', 'ALLERGIQUE': 'OUI', 'NOMALLERGIE': None},
         {'TYPE': 'MEDICAMENTEUSES', 'ALLERGIQUE': 'OUI', 'NOMALLERGIE': None},
@@ -1213,7 +1253,7 @@ def test_sanitize_update_family_data_enfant_n_sanitaire_handicap(app, resource,
     resource.pre_sanitize_update_family_data(post_data=partial_update_params)
     assert '_to_reset' in partial_update_params['ENFANT'][0]['SANITAIRE']['HANDICAP']
     with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data):
-        resource.sanitize_update_family_data(name_id='yyy', post_data=partial_update_params)
+        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
     assert len(partial_update_params['ENFANT']) == 1
     assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "4242"
     # fields were set with origin values found in Axel
@@ -1228,7 +1268,7 @@ def test_sanitize_update_family_data_enfant_n_sanitaire_handicap(app, resource,
     partial_update_params['maj:enfant_0'] = False
     resource.pre_sanitize_update_family_data(post_data=partial_update_params)
     with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data):
-        resource.sanitize_update_family_data(name_id='yyy', post_data=partial_update_params)
+        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
     assert len(partial_update_params['ENFANT']) == 1
     assert partial_update_params['ENFANT'][0]['IDPERSONNE'] == "3535"
     assert 'SANITAIRE' not in partial_update_params['ENFANT'][0]
@@ -1239,7 +1279,7 @@ def test_sanitize_update_family_data_enfant_n_sanitaire_handicap(app, resource,
     partial_update_params['maj:enfant_0'] = False
     resource.pre_sanitize_update_family_data(post_data=partial_update_params)
     with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data):
-        resource.sanitize_update_family_data(name_id='yyy', post_data=partial_update_params)
+        resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
     assert 'ENFANT' not in partial_update_params
 
     # test maj:enfant_n_sanitaire_handicap not set
@@ -1252,7 +1292,7 @@ def test_sanitize_update_family_data_enfant_n_sanitaire_handicap(app, resource,
             partial_update_params['ENFANT'][0]['SANITAIRE']['HANDICAP'][key] = None
         resource.pre_sanitize_update_family_data(post_data=partial_update_params)
         with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_family_data', return_value=family_data):
-            resource.sanitize_update_family_data(name_id='yyy', post_data=partial_update_params)
+            resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
         new_values = partial_update_params['ENFANT'][0]['SANITAIRE']
         for key in handicap_fields:
             assert new_values[key] == family_data['ENFANT'][0]['SANITAIRE'][key]
@@ -1277,7 +1317,7 @@ def test_sanitize_update_family_data_axel_error(app, resource, update_params, fl
     with mock.patch('passerelle.contrib.toulouse_axel.models.ref_famille_dui') as operation:
         operation.side_effect = AxelError('FooBar')
         with pytest.raises(APIError, match='Axel error: FooBar'):
-            resource.sanitize_update_family_data(name_id='yyy', post_data=partial_update_params)
+            resource.sanitize_update_family_data(dui='XXX', post_data=partial_update_params)
 
 
 def test_update_family_info_endpoint_sanitize_axel_error(app, resource, update_params):
@@ -1945,3 +1985,44 @@ def test_clae_booking_info_endpoint(app, resource, child_activities_data):
     assert resp.json['data']['ACTIVITE'][2]['booking'] == {}
     assert resp.json['data']['ACTIVITE'][3]['IDACTIVITE'] == 'A19P1M4'
     assert resp.json['data']['ACTIVITE'][3]['booking'] == {}
+
+
+def test_are_children_registered_axel_error(resource):
+    with mock.patch('passerelle.contrib.toulouse_axel.models.enfants_activites') as operation:
+        operation.side_effect = AxelError('FooBar')
+        assert resource.are_children_registered(dui='XXX', reference_year=42) == {}
+
+
+@pytest.mark.parametrize('date_value, year, expected', [
+    # neither current year nor next year
+    ('2020-01-01', 2018, {}),
+    ('2020-01-01', 2021, {}),
+    # current year
+    ('2019-08-01', 2019, {'3535': True}),
+    ('2020-07-31', 2019, {'3535': True}),
+    # too early for next_year
+    ('2019-08-01', 2020, {}),
+    ('2020-01-01', 2020, {}),
+    ('2020-05-31', 2020, {}),
+    # good period for next year
+    ('2020-06-01', 2020, {'3535': True}),
+    ('2020-07-31', 2020, {'3535': True}),
+])
+def test_are_children_registered_reference_year(resource, date_value, year, expected):
+    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_children_activities') as get_children_activities:
+        get_children_activities.return_value = {'3535': {'ACTIVITE': [{}]}}
+        with freezegun.freeze_time(date_value):
+            assert resource.are_children_registered(dui='XXX', reference_year=year) == expected
+
+
+@pytest.mark.parametrize('activities, expected', [
+    ({}, {}),
+    ({'3535': {'ACTIVITE': []}}, {'3535': False}),
+    ({'3535': {'ACTIVITE': [{}]}}, {'3535': True}),
+    ({'3535': {'ACTIVITE': [{}]}, '4242': {}}, {'3535': True, '4242': False}),
+])
+def test_are_children_registered(resource, activities, expected):
+    today = datetime.date.today()
+    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_children_activities') as get_children_activities:
+        get_children_activities.return_value = activities
+        assert resource.are_children_registered(dui='XXX', reference_year=get_reference_year_from_date(today)) == expected
-- 
2.20.1

