From cea6dad5d915959491e897f1e907b72e13067d2a Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Wed, 30 Nov 2022 10:05:32 +0100
Subject: [PATCH 3/3] toulouse-maelis: manage indicators (#71964)

---
 functests/toulouse_maelis/conftest.py         |  22 ++
 .../data/test_child_indicator.json            |   1 +
 .../data/test_create_family.json              |  17 +-
 .../data/test_rl_indicator.json               |   1 +
 .../data/test_update_child.json               |  17 +-
 .../data/test_update_family.json              |  34 ++-
 .../toulouse_maelis/data/test_update_rl2.json |  17 +-
 functests/toulouse_maelis/test_family.py      |  83 ++++++-
 passerelle/contrib/toulouse_maelis/models.py  |  67 +++++
 passerelle/contrib/toulouse_maelis/schemas.py |  61 +++++
 .../Q_update_child_indicator.xml              |  24 ++
 .../toulouse_maelis/Q_update_rl_indicator.xml |  24 ++
 tests/data/toulouse_maelis/R_read_family.xml  |  18 ++
 .../toulouse_maelis/R_read_family_relax.xml   |  18 ++
 .../R_read_family_reordered.xml               |  18 ++
 .../toulouse_maelis/R_update_indicator.xml    |   6 +
 tests/test_toulouse_maelis.py                 | 231 +++++++++++++++++-
 17 files changed, 642 insertions(+), 17 deletions(-)
 create mode 100644 functests/toulouse_maelis/data/test_child_indicator.json
 create mode 100644 functests/toulouse_maelis/data/test_rl_indicator.json
 create mode 100644 tests/data/toulouse_maelis/Q_update_child_indicator.xml
 create mode 100644 tests/data/toulouse_maelis/Q_update_rl_indicator.xml
 create mode 100644 tests/data/toulouse_maelis/R_update_indicator.xml

diff --git a/functests/toulouse_maelis/conftest.py b/functests/toulouse_maelis/conftest.py
index 4208455d..772351f0 100644
--- a/functests/toulouse_maelis/conftest.py
+++ b/functests/toulouse_maelis/conftest.py
@@ -56,16 +56,27 @@ FAMILY_PAYLOAD = {
             'employerName': 'Burns',
             'phone': '0133333333',
             'profession': 'Inspecteur de sécurité',
         },
         'CAFInfo': {
             'number': '123',
             'organ': 'A10007752822',
         },
+        'indicatorList': [
+            {
+                'code': 'AVL',
+                'isActive': True,
+            },
+            {
+                'code': 'ETABSPEC',
+                'note': 'SNPP',
+                'isActive': True,
+            },
+        ],
     },
     'childList': [
         {
             'sexe': 'M',
             'firstname': 'Bart',
             'lastname': 'Simpson',
             'birth': {'dateBirth': '1978-04-01'},
             'bPhoto': True,
@@ -101,16 +112,27 @@ FAMILY_PAYLOAD = {
                         'vaccinationDate': '2011-01-11',
                     },
                     {
                         'code': 'ROR',
                         'vaccinationDate': '2022-02-22',
                     },
                 ],
             },
+            'indicatorList': [
+                {
+                    'code': 'LUNETTE',
+                    'isActive': True,
+                },
+                {
+                    'code': 'AUTRE',
+                    'note': 'rebellious',
+                    'isActive': True,
+                },
+            ],
             'authorizedPersonList': [
                 {
                     'personInfo': {
                         'civility': 'M.',
                         'firstname': 'Abraham Jebediah',
                         'lastname': 'Simpson',
                         'dateBirth': '1927-05-24',
                         'sexe': 'M',
diff --git a/functests/toulouse_maelis/data/test_child_indicator.json b/functests/toulouse_maelis/data/test_child_indicator.json
new file mode 100644
index 00000000..fe51488c
--- /dev/null
+++ b/functests/toulouse_maelis/data/test_child_indicator.json
@@ -0,0 +1 @@
+[]
diff --git a/functests/toulouse_maelis/data/test_create_family.json b/functests/toulouse_maelis/data/test_create_family.json
index 49b3b3d1..ecfa7360 100644
--- a/functests/toulouse_maelis/data/test_create_family.json
+++ b/functests/toulouse_maelis/data/test_create_family.json
@@ -102,17 +102,32 @@
           },
           "personQuality": {
             "code": "GMP",
             "libelle": "GRAND-MERE PATERNELLE",
             "code_text": "GRAND-MERE PATERNELLE"
           }
         }
       ],
-      "indicatorList": [],
+      "indicatorList": [
+        {
+          "code": "AUTRE",
+          "label": "Autre",
+          "note": "rebellious",
+          "choice": null,
+          "code_text": "Autre"
+        },
+        {
+          "code": "LUNETTE",
+          "label": "Port de lunettes",
+          "note": null,
+          "choice": null,
+          "code_text": "Port de lunettes"
+        }
+      ],
       "medicalRecord": {
         "familyDoctor": {
           "name": "MONROE",
           "phone": "0612341234",
           "address": {
             "street1": "Alameda",
             "zipcode": "90701",
             "town": "Springfield"
diff --git a/functests/toulouse_maelis/data/test_rl_indicator.json b/functests/toulouse_maelis/data/test_rl_indicator.json
new file mode 100644
index 00000000..fe51488c
--- /dev/null
+++ b/functests/toulouse_maelis/data/test_rl_indicator.json
@@ -0,0 +1 @@
+[]
diff --git a/functests/toulouse_maelis/data/test_update_child.json b/functests/toulouse_maelis/data/test_update_child.json
index affe2b82..5fdd1893 100644
--- a/functests/toulouse_maelis/data/test_update_child.json
+++ b/functests/toulouse_maelis/data/test_update_child.json
@@ -53,17 +53,32 @@
       },
       "personQuality": {
         "code": "GMP",
         "libelle": "GRAND-MERE PATERNELLE",
         "code_text": "GRAND-MERE PATERNELLE"
       }
     }
   ],
-  "indicatorList": [],
+  "indicatorList": [
+    {
+      "code": "AUTRE",
+      "label": "Autre",
+      "note": "rebellious",
+      "choice": null,
+      "code_text": "Autre"
+    },
+    {
+      "code": "LUNETTE",
+      "label": "Port de lunettes",
+      "note": null,
+      "choice": null,
+      "code_text": "Port de lunettes"
+    }
+  ],
   "medicalRecord": {
     "familyDoctor": {
       "name": "MONROE",
       "phone": "0612341234",
       "address": {
         "street1": "Alameda",
         "zipcode": "90701",
         "town": "Springfield"
diff --git a/functests/toulouse_maelis/data/test_update_family.json b/functests/toulouse_maelis/data/test_update_family.json
index 087f74e5..018b9f8f 100644
--- a/functests/toulouse_maelis/data/test_update_family.json
+++ b/functests/toulouse_maelis/data/test_update_family.json
@@ -87,17 +87,32 @@
       },
       "codeCSP_text": "CADRE SUPERIEUR"
     },
     "CAFInfo": {
       "number": "123",
       "organ": "A10007752822",
       "organ_text": "LA COLLE SUR LOUP"
     },
-    "indicatorList": [],
+    "indicatorList": [
+      {
+        "code": "AVL",
+        "label": "Auxiliaire de Vie loisirs",
+        "note": null,
+        "choice": null,
+        "code_text": "Auxiliaire de Vie loisirs"
+      },
+      {
+        "code": "ETABSPEC",
+        "label": "Etablissement sp\u00e9cialis\u00e9",
+        "note": "SNPP",
+        "choice": null,
+        "code_text": "Etablissement sp\u00e9cialis\u00e9"
+      }
+    ],
     "quotientList": [],
     "subscribeActivityList": [],
     "civility_text": "Monsieur",
     "quality_text": "PERE"
   },
   "quotientList": [],
   "childList": [
     {
@@ -155,17 +170,32 @@
           },
           "personQuality": {
             "code": "GMP",
             "libelle": "GRAND-MERE PATERNELLE",
             "code_text": "GRAND-MERE PATERNELLE"
           }
         }
       ],
-      "indicatorList": [],
+      "indicatorList": [
+        {
+          "code": "AUTRE",
+          "label": "Autre",
+          "note": "rebellious",
+          "choice": null,
+          "code_text": "Autre"
+        },
+        {
+          "code": "LUNETTE",
+          "label": "Port de lunettes",
+          "note": null,
+          "choice": null,
+          "code_text": "Port de lunettes"
+        }
+      ],
       "medicalRecord": {
         "familyDoctor": {
           "name": "MONROE",
           "phone": "0612341234",
           "address": {
             "street1": "Alameda",
             "zipcode": "90701",
             "town": "Springfield"
diff --git a/functests/toulouse_maelis/data/test_update_rl2.json b/functests/toulouse_maelis/data/test_update_rl2.json
index da100193..d4ab021e 100644
--- a/functests/toulouse_maelis/data/test_update_rl2.json
+++ b/functests/toulouse_maelis/data/test_update_rl2.json
@@ -41,14 +41,29 @@
     },
     "codeCSP_text": "CADRE SUPERIEUR"
   },
   "CAFInfo": {
     "number": "123",
     "organ": "A10007752822",
     "organ_text": "LA COLLE SUR LOUP"
   },
-  "indicatorList": [],
+  "indicatorList": [
+    {
+      "code": "AVL",
+      "label": "Auxiliaire de Vie loisirs",
+      "note": null,
+      "choice": null,
+      "code_text": "Auxiliaire de Vie loisirs"
+    },
+    {
+      "code": "ETABSPEC",
+      "label": "Etablissement sp\u00e9cialis\u00e9",
+      "note": "SNPP",
+      "choice": null,
+      "code_text": "Etablissement sp\u00e9cialis\u00e9"
+    }
+  ],
   "quotientList": [],
   "subscribeActivityList": [],
   "civility_text": "Monsieur",
   "quality_text": "AUTRE"
 }
diff --git a/functests/toulouse_maelis/test_family.py b/functests/toulouse_maelis/test_family.py
index 6b3f78e8..075c18f4 100644
--- a/functests/toulouse_maelis/test_family.py
+++ b/functests/toulouse_maelis/test_family.py
@@ -45,16 +45,26 @@ FAMILY_RESET_PAYLOAD = {
             'employerName': '',
             'phone': '',
             'profession': '',
         },
         'CAFInfo': {
             'number': 'reset',  # cannot be removed
             'organ': '',
         },
+        'indicatorList': [
+            {
+                'code': 'AVL',
+                'isActive': False,
+            },
+            {
+                'code': 'ETABSPEC',
+                'isActive': False,
+            },
+        ],
     },
     'childList': [
         {
             'num': 'place holder',  # required for update
             'sexe': 'F',
             'firstname': 'Bartolome',  # some side effects, cf test_update_child
             'lastname': 'Simps',
             'birth': {'dateBirth': '1970-01-01'},
@@ -82,16 +92,27 @@ FAMILY_RESET_PAYLOAD = {
                 'comment1': '',
                 'comment2': '',
                 'observ1': '',
                 'observ2': '',
                 'isAuthHospital': False,
                 'hospital': '',
                 'vaccinList': [],
             },
+            'indicatorList': [
+                {
+                    'code': 'LUNETTE',
+                    'isActive': False,
+                },
+                {
+                    'code': 'AUTRE',
+                    'note': 'rebellious',
+                    'isActive': False,
+                },
+            ],
             # setting authorizedPersonList to None will remove the list
             'authorizedPersonList': [
                 {
                     'personInfo': {
                         'civility': None,
                         'firstname': 'reset',  # E704 : Le prénom de la personne est obligatoire
                         'lastname': 'reset',  # E705 : Le nom de la personne est obligatoire
                         'dateBirth': '1970-01-01',
@@ -279,17 +300,17 @@ def test_create_rl2(conn, create_data, update_data):
     link(conn, create_data)
 
     data = read_family(conn, create_data['name_id'])
     assert data['RL2'] is None
 
     # no unicity restriction on RL2 (duplicate RL2 from update_data)
     url = conn + '/create-rl2?NameID=%s' % create_data['name_id']
     payload = copy.deepcopy(update_data['family_payload']['rl2'])
-    for key in 'contact', 'profession', 'CAFInfo':
+    for key in 'contact', 'profession', 'CAFInfo', 'indicatorList':
         del payload[key]
     resp = requests.post(url, json=payload)
     resp.raise_for_status()
     assert resp.json()['err'] == 0
     assert diff_rlg(conn, create_data['name_id'], 2, 'test_create_rl2.json')
 
 
 @pytest.mark.parametrize("rl", ['1', '2'])
@@ -297,17 +318,17 @@ def test_update_rlg(conn, update_data, rl):
     rlg = 'rl' + rl
     RLG = 'RL' + rl
     unlink(conn, update_data['name_id'])
     link(conn, update_data)
     url = conn + '/update-rl%s?NameID=%s' % (rl, update_data['name_id'])
 
     # reset responsable legal
     payload = copy.deepcopy(FAMILY_RESET_PAYLOAD[rlg])
-    for key in 'adresse', 'contact', 'profession', 'CAFInfo':
+    for key in 'adresse', 'contact', 'profession', 'CAFInfo', 'indicatorList':
         if key in payload:
             del payload[key]
     resp = requests.post(url, json=payload)
     resp.raise_for_status()
     assert resp.json()['err'] == 0
     data = diff_rlg(conn, update_data['name_id'], rl, 'test_update_rl%s.json' % rl)
 
     # unchanged values
@@ -351,17 +372,17 @@ def test_update_rlg(conn, update_data, rl):
             "Le Responsable légal %s transmis n'est pas celui qui existe pour la famille" % rl
             in res['err_desc']
         )
     else:
         assert "La date de naissance ne peut pas être modifiée" in res['err_desc']
 
     # restore RL1
     payload = copy.deepcopy(update_data['family_payload'][rlg])
-    for key in 'adresse', 'contact', 'profession', 'CAFInfo':
+    for key in 'adresse', 'contact', 'profession', 'CAFInfo', 'indicatorList':
         if payload.get(key):
             del payload[key]
     resp = requests.post(url, json=payload)
     resp.raise_for_status()
     assert resp.json()['err'] == 0
     data = diff_family(conn, update_data['name_id'], 'test_update_family.json')
 
 
@@ -463,66 +484,66 @@ def test_create_child(conn, create_data, update_data):
 def test_update_child(conn, update_data, create_data):
     unlink(conn, update_data['name_id'])
     link(conn, update_data)
 
     # renaming using existing child names on same family will in fact target the existing child
     # side effect: the authorized person list is copied by the connector (from Lisa to Maggie)
     url = conn + '/update-child?NameID=%s&child_id=%s' % (update_data['name_id'], update_data['lisa_num'])
     payload = copy.deepcopy(update_data['family_payload']['childList'][2])  # Maggie content
-    for key in 'dietcode', 'paiInfoBean', 'medicalRecord', 'authorizedPersonList':
+    for key in 'dietcode', 'paiInfoBean', 'medicalRecord', 'authorizedPersonList', 'indicatorList':
         if key in payload:
             del payload[key]
     assert payload['firstname'] == 'Maggie'
     payload['sexe'] = 'M'
     resp = requests.post(url, json=payload)  # use Lisa id in url
     resp.raise_for_status()
     assert resp.json()['err'] == 0
     data = read_family(conn, update_data['name_id'])
     assert data['childList'][1]['num'] == update_data['lisa_num']
     assert data['childList'][1]['firstname'] == 'LISA'  # Lisa unchanged
     assert data['childList'][2]['sexe'] == 'M'  # Maggie updated
 
     # restore Maggie
     url = conn + '/update-child?NameID=%s&child_id=%s' % (update_data['name_id'], update_data['maggie_num'])
     payload = copy.deepcopy(update_data['family_payload']['childList'][2])
-    for key in 'dietcode', 'paiInfoBean', 'medicalRecord', 'authorizedPersonList':
+    for key in 'dietcode', 'paiInfoBean', 'medicalRecord', 'authorizedPersonList', 'indicatorList':
         if key in payload:
             del payload[key]
     resp = requests.post(url, json=payload)
     resp.raise_for_status()
     assert resp.json()['err'] == 0
     assert diff_family(conn, update_data['name_id'], 'test_update_family.json')
 
     # rename to an existing child on other family
     url = conn + '/update-child?NameID=%s&child_id=%s' % (update_data['name_id'], update_data['bart_num'])
     payload = copy.deepcopy(create_data['family_payload']['childList'][1])
-    for key in 'dietcode', 'paiInfoBean', 'medicalRecord', 'authorizedPersonList':
+    for key in 'dietcode', 'paiInfoBean', 'medicalRecord', 'authorizedPersonList', 'indicatorList':
         if key in payload:
             del payload[key]
     resp = requests.post(url, json=payload)
     resp.raise_for_status()
     assert resp.json()['err'] == 0
     data = read_family(conn, update_data['name_id'])
     assert data['childList'][0]['num'] == update_data['bart_num']
     assert data['childList'][0]['firstname'] == 'MAGGIE'
 
     # reset Bart
     payload = copy.deepcopy(FAMILY_RESET_PAYLOAD['childList'][0])
-    for key in 'dietcode', 'paiInfoBean', 'medicalRecord', 'authorizedPersonList':
+    for key in 'dietcode', 'paiInfoBean', 'medicalRecord', 'authorizedPersonList', 'indicatorList':
         if key in payload:
             del payload[key]
     resp = requests.post(url, json=payload)
     resp.raise_for_status()
     assert resp.json()['err'] == 0
     assert diff_child(conn, update_data['name_id'], 0, 'test_update_child.json')
 
     # restore Bart
     payload = copy.deepcopy(update_data['family_payload']['childList'][0])
-    for key in 'dietcode', 'paiInfoBean', 'medicalRecord', 'authorizedPersonList':
+    for key in 'dietcode', 'paiInfoBean', 'medicalRecord', 'authorizedPersonList', 'indicatorList':
         if key in payload:
             del payload[key]
     resp = requests.post(url, json=payload)
     resp.raise_for_status()
     assert resp.json()['err'] == 0
     assert diff_family(conn, update_data['name_id'], 'test_update_family.json')
 
 
@@ -680,8 +701,54 @@ def test_child_person(conn, update_data):
         update_data['bart_num'],
         abi_num,
     )
     payload = update_data['family_payload']['childList'][0]['authorizedPersonList'][0]
     resp = requests.post(url, json=payload)
     resp.raise_for_status()
     assert resp.json()['err'] == 0
     assert diff_family(conn, update_data['name_id'], 'test_update_family.json')
+
+
+def test_update_rl_indicator(conn, update_data):
+    unlink(conn, update_data['name_id'])
+    link(conn, update_data)
+    url = conn + '/update-rl-indicator?NameID=%s&rl_id=%s' % (
+        update_data['name_id'],
+        update_data['rl2_num'],
+    )
+
+    # reset RL indicators
+    payload = {'indicatorList': FAMILY_RESET_PAYLOAD['rl2']['indicatorList']}
+    resp = requests.post(url, json=payload)
+    resp.raise_for_status()
+    assert resp.json()['err'] == 0
+    assert diff_rlg(conn, update_data['name_id'], 1, 'test_rl_indicator.json', key='indicatorList')
+
+    # restore RL indicators
+    payload = {'indicatorList': update_data['family_payload']['rl2']['indicatorList']}
+    resp = requests.post(url, json=payload)
+    resp.raise_for_status()
+    assert resp.json()['err'] == 0
+    assert diff_family(conn, update_data['name_id'], 'test_update_family.json')
+
+
+def test_update_child_indicator(conn, update_data):
+    unlink(conn, update_data['name_id'])
+    link(conn, update_data)
+    url = conn + '/update-child-indicator?NameID=%s&child_id=%s' % (
+        update_data['name_id'],
+        update_data['bart_num'],
+    )
+
+    # reset Bart indicators
+    payload = {'indicatorList': FAMILY_RESET_PAYLOAD['childList'][0]['indicatorList']}
+    resp = requests.post(url, json=payload)
+    resp.raise_for_status()
+    assert resp.json()['err'] == 0
+    assert diff_child(conn, update_data['name_id'], 0, 'test_child_indicator.json', key='indicatorList')
+
+    # restore Bart indicators
+    payload = {'indicatorList': update_data['family_payload']['childList'][0]['indicatorList']}
+    resp = requests.post(url, json=payload)
+    resp.raise_for_status()
+    assert resp.json()['err'] == 0
+    assert diff_family(conn, update_data['name_id'], 'test_update_family.json')
diff --git a/passerelle/contrib/toulouse_maelis/models.py b/passerelle/contrib/toulouse_maelis/models.py
index be106668..0ff68cc1 100644
--- a/passerelle/contrib/toulouse_maelis/models.py
+++ b/passerelle/contrib/toulouse_maelis/models.py
@@ -180,41 +180,59 @@ class ToulouseMaelis(BaseResource, HTTPResource):
         last_key = keys.pop()
         for key in keys:
             if not isinstance(data, dict) or not key in data:
                 return
             data = data[key]
         if isinstance(data, dict) and last_key in data and data[last_key] is not None:
             data[last_key + '_text'] = self.get_referential_value(referential_name, data[last_key])
 
+    def add_text_value_to_rl_indicator(self, data):
+        self.add_text_value('RLIndicator', data, ['code'])
+
+    def add_text_value_to_child_indicator(self, data):
+        self.add_text_value('ChildIndicator', data, ['code'])
+
     def add_text_value_to_child_person(self, data):
         self.add_text_value('Civility', data, ['personInfo', 'civility'])
         self.add_text_value('Quality', data, ['personQuality', 'code'])
         self.add_text_value('Sex', data, ['personInfo', 'sexe'])
         return data
 
     def add_text_value_to_child(self, data):
         self.add_text_value('Sex', data, ['sexe'])
         self.add_text_value('DietCode', data, ['dietcode'])
         self.add_text_value('PAI', data, ['paiInfoBean', 'code'])
         for person in data['authorizedPersonList']:
             self.add_text_value_to_child_person(person)
+        for indicator in data['indicatorList']:
+            self.add_text_value_to_child_indicator(indicator)
+
+        # sort indicators
+        if data['indicatorList']:
+            data['indicatorList'].sort(key=lambda x: x['code'])
         return data
 
     def add_text_value_to_person(self, data):
         self.add_text_value('Civility', data, ['civility'])
         self.add_text_value('Quality', data, ['quality'])
         self.add_text_value('Sex', data, ['sexe'])
 
     def add_text_value_to_rl(self, data):
         self.add_text_value('Civility', data, ['civility'])
         self.add_text_value('Quality', data, ['quality'])
         self.add_text_value('Complement', data, ['adresse', 'numComp'])
         self.add_text_value('CSP', data, ['profession', 'codeCSP'])
         self.add_text_value('Organ', data, ['CAFInfo', 'organ'])
+        for indicator in data['indicatorList']:
+            self.add_text_value_to_rl_indicator(indicator)
+
+        # sort indicators
+        if data['indicatorList']:
+            data['indicatorList'].sort(key=lambda x: x['code'])
 
     def add_text_value_to_family(self, data):
         self.add_text_value('Category', data, ['category'])
         self.add_text_value('Situation', data, ['situation'])
         for rlg in 'RL1', 'RL2':
             if data.get(rlg):
                 self.add_text_value_to_rl(data[rlg])
         for child in data['childList']:
@@ -269,16 +287,27 @@ class ToulouseMaelis(BaseResource, HTTPResource):
                 isinstance(data, dict) and key in data
             ):
                 break
             data = data[key]
         else:
             key_value = data
         self.assert_key_in_referential(referential_name, key_value, '/'.join(str(x) for x in keys), required)
 
+    def assert_update_indicator_payload_in_referential(self, referential, post_data, parent_keys=None):
+        keys = parent_keys or []
+        data = post_data
+        for key in keys:
+            data = data[key]
+
+        for i in range(0, len(data.get('indicatorList', []))):
+            self.assert_post_data_in_referential(
+                referential, post_data, keys + ['indicatorList', i, 'code'], required=True
+            )
+
     def assert_child_medical_record_payload_in_referential(self, post_data, parent_keys=None):
         keys = parent_keys or []
         data = post_data
         for key in keys:
             data = data[key]
 
         for i in range(0, len(data.get('vaccinList', []))):
             self.assert_post_data_in_referential(
@@ -307,16 +336,17 @@ class ToulouseMaelis(BaseResource, HTTPResource):
 
         if 'dietcode' in data:
             self.assert_post_data_in_referential('DietCode', post_data, keys + ['dietcode'], required=False)
         if 'paiInfoBean' in data:
             self.assert_child_pai_payoad_in_referential(post_data, keys + ['paiInfoBean'])
         if 'medicalRecord' in data:
             # dead code as updateFamily seems not to modify medicalRecord
             self.assert_child_medical_record_payload_in_referential(post_data, keys + ['medicalRecord'])
+        self.assert_update_indicator_payload_in_referential('ChildIndicator', post_data, keys)
 
     def assert_person_payload_in_referential(self, post_data, parent_keys=None):
         keys = parent_keys or []
         self.assert_post_data_in_referential('Civility', post_data, keys + ['civility'], required=False)
         self.assert_post_data_in_referential('Sex', post_data, keys + ['sexe'], required=False)
         self.assert_post_data_in_referential('Quality', post_data, keys + ['quality'])
 
     def assert_update_coordinate_payload_in_referential(self, post_data, parent_keys=None):
@@ -329,16 +359,17 @@ class ToulouseMaelis(BaseResource, HTTPResource):
         )
         self.assert_post_data_in_referential('Organ', post_data, keys + ['CAFInfo', 'organ'], required=False)
 
     def assert_rl_payload_in_referential(self, post_data, parent_keys=None):
         keys = parent_keys or []
         self.assert_post_data_in_referential('Civility', post_data, keys + ['civility'])
         self.assert_post_data_in_referential('Quality', post_data, keys + ['quality'])
         self.assert_update_coordinate_payload_in_referential(post_data, keys)
+        self.assert_update_indicator_payload_in_referential('RLIndicator', post_data, keys)
 
     def assert_create_rl1_payload_in_referential(self, post_data):
         self.assert_post_data_in_referential('Category', post_data, ['category'])
         self.assert_post_data_in_referential('Situation', post_data, ['situation'])
         self.assert_rl_payload_in_referential(post_data, ['rl1'])
 
     def assert_family_payload_in_referential(self, post_data):
         self.assert_post_data_in_referential('Category', post_data, ['category'])
@@ -880,16 +911,34 @@ class ToulouseMaelis(BaseResource, HTTPResource):
     def update_coordinate(self, request, NameID, rl_id, post_data):
         family_id = self.get_link(NameID).family_id
         self.assert_update_coordinate_payload_in_referential(post_data)
         self.replace_null_values(post_data)
 
         self.call('Family', 'updateCoordinate', numDossier=family_id, numPerson=rl_id, **post_data)
         return {'data': 'ok'}
 
+    @endpoint(
+        display_category='Famille',
+        description="Mise à jour des indicateurs d'un responsable légal",
+        name='update-rl-indicator',
+        perm='can_access',
+        parameters={
+            'NameID': {'description': 'Publik NameID'},
+            'rl_id': {'description': 'Numéro du représentant légal'},
+        },
+        post={'request_body': {'schema': {'application/json': schemas.UPDATE_INDICATOR_SCHEMA}}},
+    )
+    def update_rl_indicator(self, request, NameID, rl_id, post_data):
+        self.get_link(NameID)
+        self.assert_update_indicator_payload_in_referential('RLIndicator', post_data)
+
+        self.call('Family', 'updatePersonIndicatorList', numPerson=rl_id, **post_data)
+        return {'data': 'ok'}
+
     @endpoint(
         display_category='Famille',
         description="Création d'une personne à prévenir en cas d'urgence",
         name='create-person',
         perm='can_access',
         parameters={
             'NameID': {'description': 'Publik NameID'},
         },
@@ -1151,16 +1200,34 @@ class ToulouseMaelis(BaseResource, HTTPResource):
 
         payload = {
             'numPerson': child_id,
             'medicalRecord': post_data,
         }
         self.call('Family', 'updateChildMedicalRecord', updateChildMedicalRecordRequest=payload)
         return {'data': 'ok'}
 
+    @endpoint(
+        display_category='Famille',
+        description="Mise à jour des indicateurs d'un enfant",
+        name='update-child-indicator',
+        perm='can_access',
+        parameters={
+            'NameID': {'description': 'Publik NameID'},
+            'child_id': {'description': "Numéro de l'enfant"},
+        },
+        post={'request_body': {'schema': {'application/json': schemas.UPDATE_INDICATOR_SCHEMA}}},
+    )
+    def update_child_indicator(self, request, NameID, child_id, post_data):
+        self.get_link(NameID)
+        self.assert_update_indicator_payload_in_referential('ChildIndicator', post_data)
+
+        self.call('Family', 'updatePersonIndicatorList', numPerson=child_id, **post_data)
+        return {'data': 'ok'}
+
 
 class Link(models.Model):
     resource = models.ForeignKey(ToulouseMaelis, on_delete=models.CASCADE)
     name_id = models.CharField(blank=False, max_length=256)
     family_id = models.CharField(blank=False, max_length=128)
     created = models.DateTimeField(auto_now_add=True)
     updated = models.DateTimeField(auto_now=True)
 
diff --git a/passerelle/contrib/toulouse_maelis/schemas.py b/passerelle/contrib/toulouse_maelis/schemas.py
index a15411c6..b0d368ed 100644
--- a/passerelle/contrib/toulouse_maelis/schemas.py
+++ b/passerelle/contrib/toulouse_maelis/schemas.py
@@ -173,16 +173,39 @@ BIRTH_SCHEMA = {
         },
         'countryCode': {
             'description': 'Pays de naissance (depuis référentiel)',
             'oneOf': [{'type': 'string'}, {'type': 'null'}],
         },
     },
 }
 
+INDICATOR_SCHEMA = {
+    '$schema': 'http://json-schema.org/draft-04/schema#',
+    'title': 'Address',
+    'description': 'Indicateurs',
+    'type': 'object',
+    'required': ['code', 'isActive'],
+    'properties': {
+        'code': {
+            'description': "Code de l'indicateur (depuis référentiel)",
+            'type': 'string',
+            'pattern': '.+',
+        },
+        'note': {
+            'description': "Commentaire pour les indicateurs de type NOTE",
+            'oneOf': [{'type': 'string'}, {'type': 'null'}],
+        },
+        'isActive': {
+            'description': "True pour ajouter/modifier l'indicateur (défault) ou False pour le retirer",
+            'oneOf': BOOLEAN_TYPES,
+        },
+    },
+}
+
 ID_PROPERTIES = {
     'firstname': {
         'description': 'Prénom',
         'type': 'string',
     },
     'lastname': {
         'description': 'Nom',
         'type': 'string',
@@ -345,16 +368,25 @@ RLINFO_SCHEMA = {
             'description': 'Qualité (depuis référentiel)',
             'type': 'string',
             'pattern': '.+',
         },
         'adresse': ADDRESS_SCHEMA,
         'contact': {'oneOf': [CONTACT_SCHEMA, {'type': 'null'}]},
         'profession': {'oneOf': [PROFESSION_SCHEMA, {'type': 'null'}]},
         'CAFInfo': {'oneOf': [CAFINFO_SCHEMA, {'type': 'null'}]},
+        'indicatorList': {
+            'oneOf': [
+                {
+                    'type': 'array',
+                    'items': INDICATOR_SCHEMA,
+                },
+                {'type': 'null'},
+            ],
+        },
     },
     'unflatten': True,
     'additionalProperties': False,
 }
 RLINFO_SCHEMA['properties'].update(ID_PROPERTIES)
 
 DOCTORADDRESS_SCHEMA = {
     '$schema': 'http://json-schema.org/draft-04/schema#',
@@ -537,16 +569,25 @@ CHILD_SCHEMA = {
             'oneOf': [
                 {
                     'type': 'array',
                     'items': AUTHORIZED_PERSON_SCHEMA,
                 },
                 {'type': 'null'},
             ],
         },
+        'indicatorList': {
+            'oneOf': [
+                {
+                    'type': 'array',
+                    'items': INDICATOR_SCHEMA,
+                },
+                {'type': 'null'},
+            ],
+        },
     },
     'additionalProperties': False,
 }
 CHILD_SCHEMA['properties'].update(ID_PROPERTIES)
 
 UPDATE_FAMILY_SCHEMA = {
     '$schema': 'http://json-schema.org/draft-04/schema#',
     'title': 'Family',
@@ -617,31 +658,34 @@ del CREATE_RL1_SCHEMA['properties']['rl1']['properties']['profession']
 del CREATE_RL1_SCHEMA['properties']['rl1']['properties']['CAFInfo']
 
 UPDATE_RL1_SCHEMA = copy.deepcopy(RLINFO_SCHEMA)
 UPDATE_RL1_SCHEMA['required'] = ['firstname', 'lastname', 'civility', 'quality', 'birth']
 del UPDATE_RL1_SCHEMA['properties']['adresse']
 del UPDATE_RL1_SCHEMA['properties']['contact']
 del UPDATE_RL1_SCHEMA['properties']['profession']
 del UPDATE_RL1_SCHEMA['properties']['CAFInfo']
+del UPDATE_RL1_SCHEMA['properties']['indicatorList']
 
 CREATE_RL2_SCHEMA = copy.deepcopy(RLINFO_SCHEMA)
 CREATE_RL2_SCHEMA['unflatten'] = True
 del CREATE_RL2_SCHEMA['properties']['contact']
 del CREATE_RL2_SCHEMA['properties']['profession']
 del CREATE_RL2_SCHEMA['properties']['CAFInfo']
+del CREATE_RL2_SCHEMA['properties']['indicatorList']
 
 UPDATE_RL2_SCHEMA = copy.deepcopy(UPDATE_RL1_SCHEMA)
 
 CREATE_CHILD_SCHEMA = copy.deepcopy(CHILD_SCHEMA)
 CREATE_CHILD_SCHEMA['unflatten'] = True
 del CREATE_CHILD_SCHEMA['properties']['dietcode']
 del CREATE_CHILD_SCHEMA['properties']['medicalRecord']
 del CREATE_CHILD_SCHEMA['properties']['paiInfoBean']
 del CREATE_CHILD_SCHEMA['properties']['authorizedPersonList']
+del CREATE_CHILD_SCHEMA['properties']['indicatorList']
 
 UPDATE_CHILD_SCHEMA = copy.deepcopy(CREATE_CHILD_SCHEMA)
 
 UPDATE_COORDINATE_SCHEMA = {
     '$schema': 'http://json-schema.org/draft-04/schema#',
     'title': 'Update coordinate',
     'description': "Mise à jour des coordonnées d'un responsable légal",
     'type': 'object',
@@ -650,8 +694,25 @@ UPDATE_COORDINATE_SCHEMA = {
         'adresse': ADDRESS_SCHEMA,
         'contact': {'oneOf': [CONTACT_SCHEMA, {'type': 'null'}]},
         'profession': {'oneOf': [PROFESSION_SCHEMA, {'type': 'null'}]},
         'CAFInfo': {'oneOf': [CAFINFO_SCHEMA, {'type': 'null'}]},
     },
     'unflatten': True,
     'additionalProperties': False,
 }
+
+UPDATE_INDICATOR_SCHEMA = {
+    '$schema': 'http://json-schema.org/draft-04/schema#',
+    'title': 'Update indicators',
+    'description': 'Mise à jour des indicateurs',
+    'type': 'object',
+    'required': ['indicatorList'],
+    'properties': {
+        'indicatorList': {
+            'type': 'array',
+            'items': INDICATOR_SCHEMA,
+            'minItems': 1,
+        }
+    },
+    'additionalProperties': False,
+    'unflatten': True,
+}
diff --git a/tests/data/toulouse_maelis/Q_update_child_indicator.xml b/tests/data/toulouse_maelis/Q_update_child_indicator.xml
new file mode 100644
index 00000000..2f4b3e3b
--- /dev/null
+++ b/tests/data/toulouse_maelis/Q_update_child_indicator.xml
@@ -0,0 +1,24 @@
+<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+  <soap-env:Header>
+    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+      <wsse:UsernameToken>
+        <wsse:Username>maelis-webservice</wsse:Username>
+        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">maelis-password</wsse:Password>
+      </wsse:UsernameToken>
+    </wsse:Security>
+  </soap-env:Header>
+  <soap-env:Body>
+    <ns0:updatePersonIndicatorList xmlns:ns0="family.ws.maelis.sigec.com">
+      <numPerson>613880</numPerson>
+      <indicatorList>
+        <code>LUNETTE</code>
+        <isActive>true</isActive>
+      </indicatorList>
+      <indicatorList>
+        <code>AUTRE</code>
+        <note>rebellious</note>
+        <isActive>true</isActive>
+      </indicatorList>
+    </ns0:updatePersonIndicatorList>
+  </soap-env:Body>
+</soap-env:Envelope>
diff --git a/tests/data/toulouse_maelis/Q_update_rl_indicator.xml b/tests/data/toulouse_maelis/Q_update_rl_indicator.xml
new file mode 100644
index 00000000..94087377
--- /dev/null
+++ b/tests/data/toulouse_maelis/Q_update_rl_indicator.xml
@@ -0,0 +1,24 @@
+<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
+  <soap-env:Header>
+    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
+      <wsse:UsernameToken>
+        <wsse:Username>maelis-webservice</wsse:Username>
+        <wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">maelis-password</wsse:Password>
+      </wsse:UsernameToken>
+    </wsse:Security>
+  </soap-env:Header>
+  <soap-env:Body>
+    <ns0:updatePersonIndicatorList xmlns:ns0="family.ws.maelis.sigec.com">
+      <numPerson>613878</numPerson>
+      <indicatorList>
+        <code>AVL</code>
+        <isActive>true</isActive>
+      </indicatorList>
+      <indicatorList>
+        <code>ETABSPEC</code>
+        <note>SNPP</note>
+        <isActive>true</isActive>
+      </indicatorList>
+    </ns0:updatePersonIndicatorList>
+  </soap-env:Body>
+</soap-env:Envelope>
diff --git a/tests/data/toulouse_maelis/R_read_family.xml b/tests/data/toulouse_maelis/R_read_family.xml
index a2ac0818..e4185a2c 100644
--- a/tests/data/toulouse_maelis/R_read_family.xml
+++ b/tests/data/toulouse_maelis/R_read_family.xml
@@ -31,16 +31,25 @@
             <mail>djhon@example.org</mail>
             <isContactMail>true</isContactMail>
             <isContactSms>true</isContactSms>
             <isInvoicePdf>true</isInvoicePdf>
           </contact>
           <profession>
             <addressPro/>
           </profession>
+          <indicatorList>
+            <code>ETABSPEC</code>
+            <label>Etablissement sp&#233;cialis&#233;</label>
+            <note>SNPP</note>
+          </indicatorList>
+          <indicatorList>
+            <code>AVL</code>
+            <label>Auxiliaire de Vie loisirs</label>
+          </indicatorList>
         </RL1>
         <RL2>
           <num>613879</num>
           <lastname>DOE</lastname>
           <firstname>JANE</firstname>
           <maidenName>SMITH</maidenName>
           <quality>MERE</quality>
           <civility>MME</civility>
@@ -115,16 +124,25 @@
                 <mail>abent@example.org</mail>
               </contact>
             </personInfo>
             <personQuality>
               <code>T</code>
               <libelle>TANTE</libelle>
             </personQuality>
           </authorizedPersonList>
+          <indicatorList>
+            <code>LUNETTE</code>
+            <label>Port de lunettes</label>
+          </indicatorList>
+          <indicatorList>
+            <code>AUTRE</code>
+            <label>Autre</label>
+            <note>rebellious</note>
+          </indicatorList>
           <medicalRecord>
             <familyDoctor>
               <name>DRE</name>
               <phone>0612341234</phone>
               <address>
                 <street1>Alameda</street1>
                 <zipcode>90220</zipcode>
                 <town>Compton</town>
diff --git a/tests/data/toulouse_maelis/R_read_family_relax.xml b/tests/data/toulouse_maelis/R_read_family_relax.xml
index 6b5c3d8e..fcaf1d85 100644
--- a/tests/data/toulouse_maelis/R_read_family_relax.xml
+++ b/tests/data/toulouse_maelis/R_read_family_relax.xml
@@ -31,16 +31,25 @@
             <mail>djhon@example.org</mail>
             <isContactMail>true</isContactMail>
             <isContactSms>true</isContactSms>
             <isInvoicePdf>true</isInvoicePdf>
           </contact>
           <profession>
             <addressPro/>
           </profession>
+          <indicatorList>
+            <code>ETABSPEC</code>
+            <label>Etablissement sp&#233;cialis&#233;</label>
+            <note>SNPP</note>
+          </indicatorList>
+          <indicatorList>
+            <code>AVL</code>
+            <label>Auxiliaire de Vie loisirs</label>
+          </indicatorList>
         </RL1>
         <RL2>
           <num>613879</num>
           <lastname>DOE</lastname>
           <firstname>JANE</firstname>
           <maidenName>SMITH</maidenName>
           <quality>MERE</quality>
           <civility>MME</civility>
@@ -115,16 +124,25 @@
                 <mail>abent@example.org</mail>
               </contact>
             </personInfo>
             <personQuality>
               <code>T</code>
               <libelle>TANTE</libelle>
             </personQuality>
           </authorizedPersonList>
+          <indicatorList>
+            <code>LUNETTE</code>
+            <label>Port de lunettes</label>
+          </indicatorList>
+          <indicatorList>
+            <code>AUTRE</code>
+            <label>Autre</label>
+            <note>rebellious</note>
+          </indicatorList>
           <medicalRecord>
             <familyDoctor>
               <name>DRE</name>
               <phone>0612341234</phone>
               <address>
                 <street1>Alameda</street1>
                 <zipcode>90220</zipcode>
                 <town>Compton</town>
diff --git a/tests/data/toulouse_maelis/R_read_family_reordered.xml b/tests/data/toulouse_maelis/R_read_family_reordered.xml
index 8c245e84..ab4498b8 100644
--- a/tests/data/toulouse_maelis/R_read_family_reordered.xml
+++ b/tests/data/toulouse_maelis/R_read_family_reordered.xml
@@ -31,16 +31,25 @@
             <mail>djhon@example.org</mail>
             <isContactMail>true</isContactMail>
             <isContactSms>true</isContactSms>
             <isInvoicePdf>true</isInvoicePdf>
           </contact>
           <profession>
             <addressPro/>
           </profession>
+          <indicatorList>
+            <code>ETABSPEC</code>
+            <label>Etablissement sp&#233;cialis&#233;</label>
+            <note>SNPP</note>
+          </indicatorList>
+          <indicatorList>
+            <code>AVL</code>
+            <label>Auxiliaire de Vie loisirs</label>
+          </indicatorList>
         </RL1>
         <RL2>
           <num>613879</num>
           <lastname>DOE</lastname>
           <firstname>JANE</firstname>
           <maidenName>SMITH</maidenName>
           <quality>MERE</quality>
           <civility>MME</civility>
@@ -115,16 +124,25 @@
                 <mail>abent@example.org</mail>
               </contact>
             </personInfo>
             <personQuality>
               <code>T</code>
               <libelle>TANTE</libelle>
             </personQuality>
           </authorizedPersonList>
+          <indicatorList>
+            <code>LUNETTE</code>
+            <label>Port de lunettes</label>
+          </indicatorList>
+          <indicatorList>
+            <code>AUTRE</code>
+            <label>Autre</label>
+            <note>rebellious</note>
+          </indicatorList>
           <medicalRecord>
             <familyDoctor>
               <name>DRE</name>
               <phone>0612341234</phone>
               <address>
                 <street1>Alameda</street1>
                 <zipcode>90220</zipcode>
                 <town>Compton</town>
diff --git a/tests/data/toulouse_maelis/R_update_indicator.xml b/tests/data/toulouse_maelis/R_update_indicator.xml
new file mode 100644
index 00000000..03801af3
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_update_indicator.xml
@@ -0,0 +1,6 @@
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
+  <soap:Body>
+    <ns2:updatePersonIndicatorListResponse
+        xmlns:ns2="family.ws.maelis.sigec.com"/>
+  </soap:Body>
+</soap:Envelope>
diff --git a/tests/test_toulouse_maelis.py b/tests/test_toulouse_maelis.py
index 18b5598a..02745302 100644
--- a/tests/test_toulouse_maelis.py
+++ b/tests/test_toulouse_maelis.py
@@ -75,16 +75,17 @@ UPDATE_FAMILY_500 = FakedResponse(content=get_xml_file('R_update_family_soap_err
 UPDATE_DIETCODE = FakedResponse(content=get_xml_file('R_update_child_dietcode.xml'), status_code=200)
 UPDATE_PAI = FakedResponse(content=get_xml_file('R_update_child_pai.xml'), status_code=200)
 UPDATE_PAI_500 = FakedResponse(content=get_xml_file('R_update_child_pai_soap_error.xml'), status_code=500)
 UPDATE_MEDICAL = FakedResponse(content=get_xml_file('R_update_child_medical_record.xml'), status_code=200)
 UPDATE_MEDICAL_500 = FakedResponse(
     content=get_xml_file('R_update_child_medical_record_soap_error.xml'), status_code=500
 )
 UPDATE_CHILD_AUTO = FakedResponse(content=get_xml_file('R_update_child_authorization.xml'), status_code=200)
+UPDATE_INDICATOR = FakedResponse(content=get_xml_file('R_update_indicator.xml'), status_code=200)
 
 
 def assert_sent_payload(mocked_post, query_file):
     soap_sent = etree.tostring(etree.fromstring(mocked_post.call_args.kwargs['data']), pretty_print=True)
     expected = etree.tostring(etree.fromstring(get_xml_file(query_file)), pretty_print=True)
     assert soap_sent.decode() == expected.decode()
 
 
@@ -636,27 +637,34 @@ def test_read_vaccin_list(mocked_post, mocked_get, con, app):
     ]
 
 
 @pytest.mark.parametrize("read_family", [READ_FAMILY, READ_FAMILY_LAX, READ_FAMILY_ORD])
 @mock.patch('passerelle.utils.Request.get')
 @mock.patch('passerelle.utils.Request.post')
 def test_read_family(mocked_post, mocked_get, read_family, con, app):
     mocked_get.return_value = FAMILY_SERVICE_WSDL
-    mocked_post.side_effect = [
+    side_effect = [
         read_family,
         READ_CATEGORIES,
         READ_SITUATIONS,
         READ_CIVILITIES,
         READ_QUALITIES,
+        READ_RL_INDICATOR,
         READ_CSP,
         READ_ORGAN,
         READ_DIETCODE,
-        READ_PAI,
     ]
+    if read_family == READ_FAMILY_LAX:
+        side_effect.append(READ_CHILD_INDICATOR)
+    else:
+        side_effect.append(READ_PAI)
+        side_effect.append(READ_CHILD_INDICATOR)
+    mocked_post.side_effect = side_effect
+
     url = get_endpoint('read-family')
     Link.objects.create(resource=con, family_id='1312', name_id='local')
 
     resp = app.get(url + '?NameID=local')
     assert resp.json['err'] == 0
     data = resp.json['data']
     del data['RL1']
     del data['RL2']
@@ -707,23 +715,39 @@ def test_read_family(mocked_post, mocked_get, read_family, con, app):
             'isContactMail': True,
             'isContactSms': True,
             'isInvoicePdf': True,
         },
         'CAFInfo': None,
         'civility_text': 'Monsieur',
         'quality_text': 'PERE',
         'quotientList': [],
-        'indicatorList': [],
+        'indicatorList': [
+            {
+                'choice': None,
+                'code': 'AVL',
+                'code_text': 'Auxiliaire de Vie loisirs',
+                'label': 'Auxiliaire de Vie loisirs',
+                'note': None,
+            },
+            {
+                'choice': None,
+                'code': 'ETABSPEC',
+                'code_text': 'Etablissement spécialisé',
+                'label': 'Etablissement spécialisé',
+                'note': 'SNPP',
+            },
+        ],
         'subscribeActivityList': [],
     }
     data = resp.json['data']['childList'][0]
     del data['medicalRecord']
     del data['authorizedPersonList']
     del data['paiInfoBean']
+    del data['indicatorList']
     assert data == {
         'num': '613880',
         'lastname': 'DOE',
         'firstname': 'JANNIS',
         'sexe': 'F',
         'sexe_text': 'Féminin',
         'birth': {
             'dateBirth': '1943-01-19T00:00:00+01:00',
@@ -731,17 +755,16 @@ def test_read_family(mocked_post, mocked_get, read_family, con, app):
             'communeCode': '91122',
             'countryCode': '99100',
         },
         'dietcode': 'RSV',
         'dietcode_text': '3- RÉGIME SANS VIANDE',
         'bPhoto': True,
         'bLeaveAlone': False,
         'insurance': None,
-        'indicatorList': [],
         'subscribeSchoolList': [],
         'mother': {'num': 613963, 'civility': 'MME', 'firstname': 'JANE', 'lastname': 'DOE'},
         'father': {'num': 613878, 'civility': 'M.', 'firstname': 'JHON', 'lastname': 'DOE'},
         'rl': None,
         'subscribeActivityList': [],
     }
     if read_family != READ_FAMILY_LAX:
         assert resp.json['data']['childList'][0]['paiInfoBean'] == {
@@ -815,16 +838,26 @@ def test_read_family(mocked_post, mocked_get, read_family, con, app):
             'contact': {'phone': '0123456789', 'mobile': '0623456789', 'mail': 'abent@example.org'},
         },
         'personQuality': {
             'code': 'T',
             'code_text': 'TANTE',
             'libelle': 'TANTE',
         },
     }
+    assert resp.json['data']['childList'][0]['indicatorList'] == [
+        {'choice': None, 'code': 'AUTRE', 'code_text': 'Autre', 'label': 'Autre', 'note': 'rebellious'},
+        {
+            'choice': None,
+            'code': 'LUNETTE',
+            'code_text': 'Port de lunettes',
+            'label': 'Port de lunettes',
+            'note': None,
+        },
+    ]
 
 
 def test_read_family_not_linked_error(con, app):
     url = get_endpoint('read-family')
 
     resp = app.get(url + '?NameID=')
     assert resp.json['err'] == 'not-linked'
     assert resp.json['err_desc'] == 'User not linked to family'
@@ -833,16 +866,17 @@ def test_read_family_not_linked_error(con, app):
 @mock.patch('passerelle.utils.Request.get')
 @mock.patch('passerelle.utils.Request.post')
 def test_read_rl1(mocked_post, mocked_get, con, app):
     mocked_get.return_value = FAMILY_SERVICE_WSDL
     mocked_post.side_effect = [
         READ_FAMILY,
         READ_CIVILITIES,
         READ_QUALITIES,
+        READ_RL_INDICATOR,
         READ_CSP,
     ]
     url = get_endpoint('read-rl')
     Link.objects.create(resource=con, family_id='1312', name_id='local')
 
     resp = app.get(url + '?NameID=local&rl_id=613878')
     assert resp.json['err'] == 0
     assert resp.json['data']['firstname'] == 'JHON'
@@ -979,16 +1013,17 @@ def test_read_person_not_found(mocked_post, mocked_get, con, app):
 def test_read_child(mocked_post, mocked_get, con, app):
     mocked_get.return_value = FAMILY_SERVICE_WSDL
     mocked_post.side_effect = [
         READ_FAMILY,
         READ_DIETCODE,
         READ_PAI,
         READ_CIVILITIES,
         READ_QUALITIES,
+        READ_CHILD_INDICATOR,
     ]
     url = get_endpoint('read-child')
     Link.objects.create(resource=con, family_id='1312', name_id='local')
 
     resp = app.get(url + '?NameID=local&child_id=613880')
     assert resp.json['err'] == 0
     assert resp.json['data']['firstname'] == 'JANNIS'
 
@@ -2922,8 +2957,196 @@ def test_update_child_medical_record_wrong_referential_key_error(mocked_post, mo
         'vaccinList/1/code': 'plop',
         'vaccinList/1/vaccinationDate': '2022-02-22',
     }
 
     Link.objects.create(resource=con, family_id='1312', name_id='local')
     resp = app.post_json(url + '?NameID=local&child_id=613878', params=params)
     assert resp.json['err'] == 'wrong-key'
     assert resp.json['err_desc'] == "vaccinList/1/code key value 'plop' do not belong to 'Vaccin' referential"
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_update_rl_indicator(mocked_post, mocked_get, con, app):
+    mocked_get.return_value = FAMILY_SERVICE_WSDL
+    mocked_post.side_effect = [READ_RL_INDICATOR, UPDATE_INDICATOR]
+    url = get_endpoint('update-rl-indicator')
+    params = {
+        'indicatorList': [
+            {
+                'code': 'AVL',
+                'isActive': True,
+            },
+            {
+                'code': 'ETABSPEC',
+                'note': 'SNPP',
+                'isActive': True,
+            },
+        ],
+    }
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local&rl_id=613878', params=params)
+    assert_sent_payload(mocked_post, 'Q_update_rl_indicator.xml')
+    assert resp.json['err'] == 0
+    assert resp.json['data'] == 'ok'
+
+
+def test_update_rl_indicator_not_linked_error(con, app):
+    url = get_endpoint('update-rl-indicator')
+    params = {
+        'indicatorList': [
+            {
+                'code': 'AVL',
+                'isActive': True,
+            },
+        ],
+    }
+
+    resp = app.post_json(url + '?NameID=local&rl_id=613878', params=params)
+    assert resp.json['err'] == 'not-linked'
+    assert resp.json['err_desc'] == 'User not linked to family'
+
+
+def test_update_rl_indicator_no_indicator_error(con, app):
+    url = get_endpoint('update-rl-indicator')
+    params = {'indicatorList': []}
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local&rl_id=613878', params=params, status=400)
+    assert resp.json['err'] == 1
+    assert resp.json['err_desc'] == 'indicatorList: [] is too short'
+
+
+def test_update_rl_indicator_empty_referential_key_error(con, app):
+    url = get_endpoint('update-rl-indicator')
+    params = {
+        'indicatorList': [
+            {
+                'code': '',
+                'isActive': True,
+            },
+        ],
+    }
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local&rl_id=613878', params=params, status=400)
+    assert resp.json['err'] == 1
+    assert resp.json['err_desc'] == "indicatorList/0/code: '' does not match '.+'"
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_update_rl_indicator_wrong_referential_key_error(mocked_post, mocked_get, con, app):
+    mocked_get.return_value = FAMILY_SERVICE_WSDL
+    mocked_post.side_effect = [READ_RL_INDICATOR]
+    url = get_endpoint('update-rl-indicator')
+    params = {
+        'indicatorList': [
+            {
+                'code': 'plop',
+                'isActive': True,
+            },
+        ],
+    }
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local&rl_id=613878', params=params)
+    assert resp.json['err'] == 'wrong-key'
+    assert (
+        resp.json['err_desc']
+        == "indicatorList/0/code key value 'plop' do not belong to 'RLIndicator' required referential"
+    )
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_update_child_indicator(mocked_post, mocked_get, con, app):
+    mocked_get.return_value = FAMILY_SERVICE_WSDL
+    mocked_post.side_effect = [READ_CHILD_INDICATOR, UPDATE_INDICATOR]
+    url = get_endpoint('update-child-indicator')
+    params = {
+        'indicatorList': [
+            {
+                'code': 'LUNETTE',
+                'isActive': True,
+            },
+            {
+                'code': 'AUTRE',
+                'note': 'rebellious',
+                'isActive': True,
+            },
+        ],
+    }
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local&child_id=613880', params=params)
+    assert_sent_payload(mocked_post, 'Q_update_child_indicator.xml')
+    assert resp.json['err'] == 0
+    assert resp.json['data'] == 'ok'
+
+
+def test_update_child_indicator_not_linked_error(con, app):
+    url = get_endpoint('update-child-indicator')
+    params = {
+        'indicatorList': [
+            {
+                'code': 'LUNETTE',
+                'isActive': True,
+            },
+        ],
+    }
+
+    resp = app.post_json(url + '?NameID=local&child_id=613880', params=params)
+    assert resp.json['err'] == 'not-linked'
+    assert resp.json['err_desc'] == 'User not linked to family'
+
+
+def test_update_child_indicator_no_indicator_error(con, app):
+    url = get_endpoint('update-child-indicator')
+    params = {'indicatorList': []}
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local&child_id=613880', params=params, status=400)
+    assert resp.json['err'] == 1
+    assert resp.json['err_desc'] == 'indicatorList: [] is too short'
+
+
+def test_update_child_indicator_empty_referential_key_error(con, app):
+    url = get_endpoint('update-child-indicator')
+    params = {
+        'indicatorList': [
+            {
+                'code': '',
+                'isActive': True,
+            },
+        ],
+    }
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local&child_id=613880', params=params, status=400)
+    assert resp.json['err'] == 1
+    assert resp.json['err_desc'] == "indicatorList/0/code: '' does not match '.+'"
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_update_child_indicator_wrong_referential_key_error(mocked_post, mocked_get, con, app):
+    mocked_get.return_value = FAMILY_SERVICE_WSDL
+    mocked_post.side_effect = [READ_CHILD_INDICATOR]
+    url = get_endpoint('update-child-indicator')
+    params = {
+        'indicatorList': [
+            {
+                'code': 'plop',
+                'isActive': True,
+            },
+        ],
+    }
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local&child_id=613880', params=params)
+    assert resp.json['err'] == 'wrong-key'
+    assert (
+        resp.json['err_desc']
+        == "indicatorList/0/code key value 'plop' do not belong to 'ChildIndicator' required referential"
+    )
-- 
2.30.2

