From 36f58628835d4011dc06840e193f5a63427fde26 Mon Sep 17 00:00:00 2001
From: Nicolas ROCHE <nroche@entrouvert.com>
Date: Tue, 6 Sep 2022 10:24:50 +0200
Subject: [PATCH 1/3] toulouse-maelis: add RL1 webservices (#68788)

---
 passerelle/contrib/toulouse_maelis/models.py  |  47 +++++++
 passerelle/contrib/toulouse_maelis/schemas.py |  17 +++
 tests/data/toulouse_maelis/Q_update_rl1.xml   |  32 +++++
 .../toulouse_maelis/R_create_family_error.xml |   1 +
 .../R_read_family_with_only_rl1.xml           |  39 ++++++
 tests/test_toulouse_maelis.py                 | 129 +++++++++++++++++-
 6 files changed, 264 insertions(+), 1 deletion(-)
 create mode 100644 tests/data/toulouse_maelis/Q_update_rl1.xml
 create mode 100644 tests/data/toulouse_maelis/R_read_family_with_only_rl1.xml

diff --git a/passerelle/contrib/toulouse_maelis/models.py b/passerelle/contrib/toulouse_maelis/models.py
index 68787b34..bcd39be2 100644
--- a/passerelle/contrib/toulouse_maelis/models.py
+++ b/passerelle/contrib/toulouse_maelis/models.py
@@ -456,16 +456,63 @@ class ToulouseMaelis(BaseResource, HTTPResource):
         data = serialize_object(response)
         family_id = data.get('number')
         errors = data.get('childErrorList')
         if errors:
             err_codes = [x.split(':')[0][:4] for x in errors]
             raise APIError(' ; '.join(errors), err_code=', '.join(err_codes))
         return {'data': data}
 
+    @endpoint(
+        display_category='Famille',
+        description='Création du RL1',
+        name='create-rl1',
+        perm='can_access',
+        parameters={'NameID': {'description': 'Publik NameID'}},
+        post={'request_body': {'schema': {'application/json': schemas.CREATE_RL1_SCHEMA}}},
+    )
+    def create_rl1(self, request, NameID, post_data):
+        if self.link_set.filter(name_id=NameID).exists():
+            raise APIError('User already linked to family', err_code='already-linked')
+
+        response = self.call('Family', 'createFamily', **post_data)
+        data = serialize_object(response)
+        family_id = data.get('number')
+        if not family_id:
+            errors = data.get('rl1ErrorList') or []
+            raise APIError(' ; '.join(errors), err_code='already-rl1')
+
+        Link.objects.create(resource=self, name_id=NameID, family_id=family_id)
+        return {'data': {'family_id': family_id}}
+
+    @endpoint(
+        display_category='Famille',
+        description='Modification du RL1',
+        name='update-rl1',
+        perm='can_access',
+        parameters={'NameID': {'description': 'Publik NameID'}},
+        post={'request_body': {'schema': {'application/json': schemas.UPDATE_RL1_SCHEMA}}},
+    )
+    def update_rl1(self, request, NameID, post_data):
+        family_id = self.get_link(NameID).family_id
+        family = self.get_family_raw(family_id)
+        self.replace_null_values(post_data)
+
+        rl1 = post_data
+        rl1['num'] = family['RL1']['num']
+        rl1['adresse'] = family['RL1']['adresse']
+        payload = {
+            'dossierNumber': family_id,
+            'categorie': family['category'],
+            'situation': family['situation'],
+            'rl1': rl1,
+        }
+        self.call('Family', 'updateFamily', **payload)
+        return {'data': 'ok'}
+
     @endpoint(
         display_category='Famille',
         description="Mise à jour des coordonnées d'une personne",
         name='update-coordinate',
         perm='can_access',
         parameters={
             'NameID': {'description': 'Publik NameID'},
             'rl_id': {'description': 'Numéro du représentant légal'},
diff --git a/passerelle/contrib/toulouse_maelis/schemas.py b/passerelle/contrib/toulouse_maelis/schemas.py
index 475dd5f7..aa53e204 100644
--- a/passerelle/contrib/toulouse_maelis/schemas.py
+++ b/passerelle/contrib/toulouse_maelis/schemas.py
@@ -658,16 +658,33 @@ CREATE_FAMILY_SCHEMA = {
 
 
 UPDATE_FAMILY_SCHEMA = copy.deepcopy(CREATE_FAMILY_SCHEMA)
 UPDATE_FAMILY_SCHEMA['required'] = ['categorie', 'situation']
 
 
 # Schemas below describe parameters of Maelis wrapper around updateFamily endpoint
 
+CREATE_RL1_SCHEMA = copy.deepcopy(CREATE_FAMILY_SCHEMA)
+del CREATE_RL1_SCHEMA['properties']['rl2']
+del CREATE_RL1_SCHEMA['properties']['authorizedPersonList']
+del CREATE_RL1_SCHEMA['properties']['emergencyPersonList']
+del CREATE_RL1_SCHEMA['properties']['childList']
+del CREATE_RL1_SCHEMA['properties']['rl1']['properties']['contact']
+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', 'dateBirth']
+del UPDATE_RL1_SCHEMA['properties']['adresse']
+del UPDATE_RL1_SCHEMA['properties']['contact']
+del UPDATE_RL1_SCHEMA['properties']['profession']
+del UPDATE_RL1_SCHEMA['properties']['CAFInfo']
+
+
 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',
     'properties': {
         'adresse': ADDRESS_SCHEMA,
         'contact': CONTACT_SCHEMA,
diff --git a/tests/data/toulouse_maelis/Q_update_rl1.xml b/tests/data/toulouse_maelis/Q_update_rl1.xml
new file mode 100644
index 00000000..bafdc62e
--- /dev/null
+++ b/tests/data/toulouse_maelis/Q_update_rl1.xml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+<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:updateFamily xmlns:ns0="family.ws.maelis.sigec.com">
+      <dossierNumber>1312</dossierNumber>
+      <categorie>BI</categorie>
+      <situation>M</situation>
+      <rl1>
+        <num>613878</num>
+        <lastname>Doe</lastname>
+        <firstname>Jhonny</firstname>
+        <quality>PERE</quality>
+        <civility>M.</civility>
+        <dateBirth>1943-06-15</dateBirth>
+        <adresse>
+          <num>170</num>
+          <street1>Chateau d'eau</street1>
+          <town>Paris</town>
+          <zipcode>75014</zipcode>
+        </adresse>
+      </rl1>
+    </ns0:updateFamily>
+  </soap-env:Body>
+</soap-env:Envelope>
diff --git a/tests/data/toulouse_maelis/R_create_family_error.xml b/tests/data/toulouse_maelis/R_create_family_error.xml
index 8c1c305c..60d3b72a 100644
--- a/tests/data/toulouse_maelis/R_create_family_error.xml
+++ b/tests/data/toulouse_maelis/R_create_family_error.xml
@@ -1,11 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
 <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
     <ns2:createFamilyResponse xmlns:ns2="family.ws.maelis.sigec.com">
       <createFamilyResult>
         <number>0</number>
         <rl1ErrorList>E54a : Il existe déjà un Responsable Légal correspondant au nom [DOE], prénom [JHON], date de naissance [26/07/1938] - Personne n°[613955] - Famille n°[196544]</rl1ErrorList>
+        <rl1ErrorList>E54a : Il existe déjà un Responsable Légal correspondant au nom [DOE], prénom [JHON], date de naissance [26/07/1938] - Personne n°[613956] - Famille n°[196545]</rl1ErrorList>
       </createFamilyResult>
     </ns2:createFamilyResponse>
   </soap:Body>
 </soap:Envelope>
diff --git a/tests/data/toulouse_maelis/R_read_family_with_only_rl1.xml b/tests/data/toulouse_maelis/R_read_family_with_only_rl1.xml
new file mode 100644
index 00000000..1d0d1db3
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_read_family_with_only_rl1.xml
@@ -0,0 +1,39 @@
+<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
+  <soap:Body>
+    <ns2:readFamilyResponse xmlns:ns2="family.ws.maelis.sigec.com">
+      <familyResult>
+        <number>1312</number>
+        <category>BI</category>
+        <situation>M</situation>
+        <flagCom>true</flagCom>
+        <nbChild>2</nbChild>
+        <RL1>
+          <num>613878</num>
+          <lastname>DOE</lastname>
+          <firstname>JHON</firstname>
+          <quality>PERE</quality>
+          <civility>M.</civility>
+          <dateBirth>1938-07-26T00:00:00+01:00</dateBirth>
+          <adresse>
+            <idStreet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
+            <num>170</num>
+            <street1>Chateau d'eau</street1>
+            <town>Paris</town>
+            <zipcode>75014</zipcode>
+          </adresse>
+          <contact>
+            <phone>non</phone>
+            <mobile>0777777777</mobile>
+            <mail>djhon@example.org</mail>
+            <isContactMail>true</isContactMail>
+            <isContactSms>true</isContactSms>
+            <isInvoicePdf>true</isInvoicePdf>
+          </contact>
+          <profession>
+            <addressPro/>
+          </profession>
+        </RL1>
+      </familyResult>
+    </ns2:readFamilyResponse>
+  </soap:Body>
+</soap:Envelope>
diff --git a/tests/test_toulouse_maelis.py b/tests/test_toulouse_maelis.py
index ae105e6e..d6083645 100644
--- a/tests/test_toulouse_maelis.py
+++ b/tests/test_toulouse_maelis.py
@@ -38,16 +38,17 @@ def get_xml_file(filename):
 CONNECTION_ERROR = ConnectionError('No address associated with hostname')
 FAMILY_SERVICE_WSDL = FakedResponse(content=get_xml_file('FamilyService.wsdl'), status_code=200)
 ACTIVITY_SERVICE_WSDL = FakedResponse(content=get_xml_file('ActivityService.wsdl'), status_code=200)
 INVOICE_SERVICE_WSDL = FakedResponse(content=get_xml_file('InvoiceService.wsdl'), status_code=200)
 FAILED_AUTH = FakedResponse(content=get_xml_file('R_failed_authentication.xml'), status_code=500)
 ISWSRUNNING_TRUE = FakedResponse(content=get_xml_file('R_is_ws_running.xml') % b'true', status_code=200)
 ISWSRUNNING_FALSE = FakedResponse(content=get_xml_file('R_is_ws_running.xml') % b'false', status_code=200)
 READ_FAMILY = FakedResponse(content=get_xml_file('R_read_family.xml'), status_code=200)
+READ_RL1_FAMILY = FakedResponse(content=get_xml_file('R_read_family_with_only_rl1.xml'), status_code=200)
 READ_CATEGORIES = FakedResponse(content=get_xml_file('R_read_category_list.xml'), status_code=200)
 READ_CIVILITIES = FakedResponse(content=get_xml_file('R_read_civility_list.xml'), status_code=200)
 READ_CSP = FakedResponse(content=get_xml_file('R_read_csp_list.xml'), status_code=200)
 READ_DIETCODE = FakedResponse(content=get_xml_file('R_read_dietcode_list.xml'), status_code=200)
 READ_ORGAN = FakedResponse(content=get_xml_file('R_read_organ_list.xml'), status_code=200)
 READ_PAI = FakedResponse(content=get_xml_file('R_read_pai_list.xml'), status_code=200)
 READ_QUALITIES = FakedResponse(content=get_xml_file('R_read_quality_list.xml'), status_code=200)
 READ_QUOTIENT = FakedResponse(content=get_xml_file('R_read_quotient_list.xml'), status_code=200)
@@ -1008,17 +1009,17 @@ def test_create_family_maelis_error(mocked_post, mocked_get, con, app):
         'rl1/quality': 'AU',
         'rl1/dateBirth': '1938-07-26',
         'rl1/adresse/street1': 'Chateau',
         'rl1/adresse/town': 'Paris',
         'rl1/adresse/zipcode': '75014',
     }
 
     resp = app.post_json(url + '?NameID=local', params=params)
-    assert resp.json['err'] == 'E54a'
+    assert resp.json['err'] == 'E54a, E54a'
     assert 'Il existe déjà' in resp.json['err_desc']
 
 
 @mock.patch('passerelle.utils.Request.get')
 @mock.patch('passerelle.utils.Request.post')
 def test_update_family(mocked_post, mocked_get, con, app):
     mocked_get.return_value = FAMILY_SERVICE_WSDL
     mocked_post.return_value = UPDATE_FAMILY
@@ -1105,16 +1106,142 @@ def test_update_family_soap_error(mocked_post, mocked_get, con, app):
     }
 
     Link.objects.create(resource=con, family_id='1312', name_id='local')
     resp = app.post_json(url + '?NameID=local', params=params)
     assert resp.json['err'] == 'Family-updateFamily-soap:Server'
     assert 'Une erreur est survenue' in resp.json['err_desc']
 
 
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_create_rl1(mocked_post, mocked_get, con, app):
+    mocked_get.return_value = FAMILY_SERVICE_WSDL
+    mocked_post.return_value = CREATE_FAMILY
+    url = get_endpoint('create-rl1')
+    params = {
+        'categorie': 'ACCEUI',
+        'situation': 'C',
+        'rl1/civility': 'M.',
+        'rl1/firstname': 'Jhon',
+        'rl1/lastname': 'Doe',
+        'rl1/quality': 'AU',
+        'rl1/dateBirth': '1938-07-26',
+        'rl1/adresse/street1': 'Chateau',
+        'rl1/adresse/town': 'Paris',
+        'rl1/adresse/zipcode': '75014',
+    }
+
+    resp = app.post_json(url + '?NameID=local', params=params)
+    assert_sent_payload(mocked_post, 'Q_create_family.xml')
+    assert resp.json['err'] == 0
+    assert resp.json['data'] == {'family_id': 196545}
+    assert Link.objects.get(resource=con, family_id='196545', name_id='local')
+
+
+def test_create_rl1_already_linked_error(con, app):
+    url = get_endpoint('create-rl1')
+    params = {
+        'categorie': 'ACCEUI',
+        'situation': 'C',
+        'rl1/civility': 'M.',
+        'rl1/firstname': 'Jhon',
+        'rl1/lastname': 'Doe',
+        'rl1/quality': 'AU',
+        'rl1/dateBirth': '1938-07-26',
+        'rl1/adresse/street1': 'Chateau',
+        'rl1/adresse/town': 'Paris',
+        'rl1/adresse/zipcode': '75014',
+    }
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local', params=params)
+    assert resp.json['err'] == 'already-linked'
+    assert resp.json['err_desc'] == 'User already linked to family'
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_create_rl1_maelis_error(mocked_post, mocked_get, con, app):
+    mocked_get.return_value = FAMILY_SERVICE_WSDL
+    mocked_post.return_value = CREATE_FAMILY_ERR
+    url = get_endpoint('create-rl1')
+    params = {
+        'categorie': 'ACCEUI',
+        'situation': 'C',
+        'rl1/civility': 'M.',
+        'rl1/firstname': 'Jhon',
+        'rl1/lastname': 'Doe',
+        'rl1/quality': 'AU',
+        'rl1/dateBirth': '1938-07-26',
+        'rl1/adresse/street1': 'Chateau',
+        'rl1/adresse/town': 'Paris',
+        'rl1/adresse/zipcode': '75014',
+    }
+
+    resp = app.post_json(url + '?NameID=local', params=params)
+    assert resp.json['err'] == 'already-rl1'
+    assert 'Il existe déjà' in resp.json['err_desc']
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_update_rl1(mocked_post, mocked_get, con, app):
+    mocked_get.return_value = FAMILY_SERVICE_WSDL
+    mocked_post.side_effect = [READ_RL1_FAMILY, UPDATE_FAMILY]
+    url = get_endpoint('update-rl1')
+    params = {
+        'civility': 'M.',
+        'firstname': 'Jhonny',
+        'lastname': 'Doe',
+        'quality': 'PERE',
+        'dateBirth': '1943-06-15',
+    }
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local', params=params)
+    assert_sent_payload(mocked_post, 'Q_update_rl1.xml')
+    assert resp.json['err'] == 0
+
+
+def test_update_rl1_not_linked_error(con, app):
+    url = get_endpoint('update-rl1')
+    params = {
+        'civility': 'M.',
+        'firstname': 'Jhonny',
+        'lastname': 'Doe',
+        'quality': 'PERE',
+        'dateBirth': '1943-06-15',
+    }
+
+    resp = app.post_json(url + '?NameID=local', params=params)
+    assert resp.json['err'] == 'not-linked'
+    assert resp.json['err_desc'] == 'User not linked to family'
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_update_rl1_connection_error(mocked_post, mocked_get, con, app):
+    mocked_get.return_value = FAMILY_SERVICE_WSDL
+    mocked_post.side_effect = CONNECTION_ERROR
+    url = get_endpoint('update-rl1')
+    params = {
+        'civility': 'M.',
+        'firstname': 'Jhonny',
+        'lastname': 'Doe',
+        'quality': 'PERE',
+        'dateBirth': '1943-06-15',
+    }
+
+    Link.objects.create(resource=con, family_id='1312', name_id='local')
+    resp = app.post_json(url + '?NameID=local', params=params, status=500)
+    assert resp.json['err']
+    assert resp.json['err_desc'] == 'No address associated with hostname'
+
+
 @mock.patch('passerelle.utils.Request.get')
 @mock.patch('passerelle.utils.Request.post')
 def test_update_coordinate(mocked_post, mocked_get, con, app):
     mocked_get.return_value = FAMILY_SERVICE_WSDL
     mocked_post.return_value = UPDATE_FAMILY
     url = get_endpoint('update-coordinate')
     params = {
         'adresse/num': '169',
-- 
2.30.2

