From 6013d7bad356f0e2797cc4820a57dbdd04ea51d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 10 Dec 2019 11:26:49 +0100
Subject: [PATCH 2/2] toulouse_axel: update family endpoint (#38124)

---
 functests/toulouse_axel/test_toulouse_axel.py |  24 +-
 passerelle/contrib/toulouse_axel/models.py    |  50 +++-
 .../xsd/Dui/Q_FormMajFamilleDui.xsd           | 137 +++++++++++
 .../xsd/Dui/R_FormMajFamilleDui.xsd           |  21 ++
 .../toulouse_axel/update_family_info.json     | 214 ++++++++++++++++++
 tests/test_toulouse_axel.py                   |  91 ++++++--
 6 files changed, 522 insertions(+), 15 deletions(-)
 create mode 100644 passerelle/contrib/toulouse_axel/xsd/Dui/Q_FormMajFamilleDui.xsd
 create mode 100644 passerelle/contrib/toulouse_axel/xsd/Dui/R_FormMajFamilleDui.xsd
 create mode 100644 tests/data/toulouse_axel/update_family_info.json

diff --git a/functests/toulouse_axel/test_toulouse_axel.py b/functests/toulouse_axel/test_toulouse_axel.py
index e9e4373f..09a3f92e 100644
--- a/functests/toulouse_axel/test_toulouse_axel.py
+++ b/functests/toulouse_axel/test_toulouse_axel.py
@@ -24,12 +24,34 @@ def test_link(conn, user):
     url = conn + '/family_info?NameID=%s' % name_id
     resp = requests.get(url)
     resp.raise_for_status()
+    data = resp.json()
+    assert res['err'] == 0
+    pprint.pprint(data)
+    print('\n')
+
+    payload = data['data']
+    for key in ['SITUATIONFAMILIALE', 'NBENFANTACTIF', 'NBRLACTIF', 'IDDUI', 'CODEMISEAJOUR']:
+        payload.pop(key)
+    payload['N0TELEDOSSIER'] = '42'
+    payload['DROITALIMAGE'] = 'NON'
+    for key in ['MONTANTTOTAL', 'DATEVALIDITE', 'SFI', 'IREVENUS', 'RNF']:
+        payload['REVENUS'].pop(key)
+    payload['REVENUS']['CHOIXREVENU'] = ''
+    for enfant in payload['ENFANT']:
+        for key in ['NOM', 'DATENAISSANCE', 'SEXE', 'PRENOMPERE', 'PRENOMMERE', 'NOMPERE', 'NOMMERE', 'RATTACHEAUTREDUI', 'PRENOM']:
+            enfant.pop(key)
+        enfant['AUTORISATIONURGENCEMEDICALE'] = 'OUI'
+
+    print("Update family info with the following payload:")
+    pprint.pprint(payload)
+    url = conn + '/update_family_info?NameID=%s' % name_id
+    resp = requests.post(url, json=payload)
+    resp.raise_for_status()
     res = resp.json()
     assert res['err'] == 0
     pprint.pprint(res)
     print('\n')
 
-    data = res
     for child in data['data']['ENFANT']:
         print("GET child info")
         url = conn + '/child_info?NameID=%s&idpersonne=%s' % (name_id, child['IDPERSONNE'])
diff --git a/passerelle/contrib/toulouse_axel/models.py b/passerelle/contrib/toulouse_axel/models.py
index 9f22fb09..de4693dc 100644
--- a/passerelle/contrib/toulouse_axel/models.py
+++ b/passerelle/contrib/toulouse_axel/models.py
@@ -91,8 +91,14 @@ class Operation(object):
 
     def __call__(self, resource, request_data):
         client = resource.soap_client()
+
         serialized_request = ET.tostring(
             self.request_converter.encode(request_data))
+        try:
+            self.request_converter.xml_schema.validate(serialized_request)
+        except xmlschema.XMLSchemaValidationError as e:
+            raise AxelError('invalid request', str(e))
+
         result = client.service.getData(
             self.operation,
             force_text(serialized_request),
@@ -106,11 +112,12 @@ class Operation(object):
         try:
             return self.response_converter.decode(ET.fromstring(result))
         except xmlschema.XMLSchemaValidationError as e:
-            raise AxelError('invalid response', e)
+            raise AxelError('invalid response', str(e))
 
 
 ref_verif_dui = Operation('RefVerifDui')
 ref_famille_dui = Operation('RefFamilleDui')
+form_maj_famille_dui = Operation('FormMajFamilleDui')
 ref_facture_a_payer = Operation('RefFactureAPayer')
 ref_facture_pdf = Operation('RefFacturePDF', prefix='')
 
@@ -136,6 +143,15 @@ class ToulouseAxel(BaseResource):
     LINK_SCHEMA['properties'].pop('IDPERSONNE')
     LINK_SCHEMA['required'].remove('IDPERSONNE')
 
+    UPDATE_FAMILY_SCHEMA = copy.deepcopy(
+        form_maj_famille_dui.request_schema['properties']['PORTAIL']['properties']['DUI'])
+    UPDATE_FAMILY_SCHEMA['properties'].pop('IDDUI')
+    UPDATE_FAMILY_SCHEMA['properties'].pop('DATEDEMANDE')
+    UPDATE_FAMILY_SCHEMA['properties'].pop('QUIACTUALISEDUI')
+    UPDATE_FAMILY_SCHEMA['required'].remove('IDDUI')
+    UPDATE_FAMILY_SCHEMA['required'].remove('DATEDEMANDE')
+    UPDATE_FAMILY_SCHEMA['required'].remove('QUIACTUALISEDUI')
+
     @endpoint(
         description=_('Create link between user and Toulouse Axel'),
         perm='can_access',
@@ -226,6 +242,38 @@ class ToulouseAxel(BaseResource):
 
         raise APIError('Child not found', err='not-found')
 
+    @endpoint(
+        description=_("Update information about user's family"),
+        perm='can_access',
+        parameters={
+            'NameID': {'description': _('Publik ID')},
+        },
+        post={
+            'request_body': {
+                'schema': {
+                    'application/json': UPDATE_FAMILY_SCHEMA,
+                }
+            }
+        })
+    def update_family_info(self, request, NameID, post_data):
+        try:
+            link = self.link_set.get(name_id=NameID)
+        except Link.DoesNotExist:
+            raise APIError('Person not found', err='not-found')
+
+        post_data['IDDUI'] = link.dui
+        post_data['DATEDEMANDE'] = datetime.date.today().strftime('%Y-%m-%d')
+        if 'RL2' in post_data and post_data['RL2'].get('IDPERSONNE') == link.person_id:
+            post_data['QUIACTUALISEDUI'] = '2'
+        else:
+            post_data['QUIACTUALISEDUI'] = '1'
+        try:
+            form_maj_famille_dui(self, {'PORTAIL': {'DUI': post_data}})
+        except AxelError as e:
+            raise APIError('Axel error: %s' % e, err='error')
+
+        return {'updated': True, 'dui': link.dui}
+
     def get_invoices(self, name_id, error_status=200):
         link = self.get_link(name_id, error_status=error_status)
         try:
diff --git a/passerelle/contrib/toulouse_axel/xsd/Dui/Q_FormMajFamilleDui.xsd b/passerelle/contrib/toulouse_axel/xsd/Dui/Q_FormMajFamilleDui.xsd
new file mode 100644
index 00000000..25e7d41d
--- /dev/null
+++ b/passerelle/contrib/toulouse_axel/xsd/Dui/Q_FormMajFamilleDui.xsd
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:all="urn:AllAxelTypes" xmlns:adr="urn:AdresseDui">
+
+	<xsd:import schemaLocation="../AllAxelTypes.xsd" namespace="urn:AllAxelTypes"  />
+	
+	<xsd:import schemaLocation="./AdresseDui.xsd" namespace="urn:AdresseDui"  />
+
+	<xsd:include schemaLocation="./ResponsableDui.xsd"/>
+	
+	<xsd:include schemaLocation="./ContactDui.xsd"/>
+	
+	<xsd:include schemaLocation="./AssuranceDui.xsd"/>
+	
+	<xsd:include schemaLocation="./FicheSanitaireDui.xsd"/>
+
+	<xsd:simpleType name="N0TELEDOSSIERType">
+		<xsd:restriction base="xsd:string">
+			<xsd:minLength value="1" />
+			<xsd:maxLength value="12" />
+		</xsd:restriction>
+	</xsd:simpleType>
+	
+	<xsd:simpleType name="QUIACTUALISEDUIType">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="1" />
+			<xsd:enumeration value="2" />
+		</xsd:restriction>
+	</xsd:simpleType>
+	
+	<xsd:simpleType name="TYPEREGIMEType">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="" />
+			<xsd:enumeration value="GENE" />
+			<xsd:enumeration value="ZAU" />
+			<xsd:enumeration value="MSA" />
+		</xsd:restriction>
+	</xsd:simpleType>
+	
+	<xsd:simpleType name="NUMEROCAFType">
+		<xsd:restriction base="xsd:string">
+			<xsd:minLength value="0" />
+			<xsd:maxLength value="7" />
+		</xsd:restriction>
+	</xsd:simpleType>
+	
+	<xsd:simpleType name="ALLOCATAIREType">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="" />
+			<xsd:enumeration value="1" />
+			<xsd:enumeration value="2" />
+		</xsd:restriction>
+	</xsd:simpleType>
+
+	<xsd:simpleType name="CHOIXREVENUType">
+		<xsd:restriction base="xsd:string">
+			<xsd:enumeration value="" />
+			<xsd:enumeration value="rnf" />
+			<xsd:enumeration value="cafprooui" />
+			<xsd:enumeration value="RNF" />
+			<xsd:enumeration value="CAFPROOUI" />
+		</xsd:restriction>
+	</xsd:simpleType>
+		
+	<xsd:complexType name="REVENUSType">
+		<xsd:sequence>
+			<xsd:element ref="TYPEREGIME" />
+			<xsd:element ref="NUMEROCAF" />
+			<xsd:element ref="ALLOCATAIRE" />
+			<xsd:element ref="CHOIXREVENU" />
+			<xsd:element ref="NBENFANTSACHARGE" />
+		</xsd:sequence> 
+	</xsd:complexType>
+	
+	<xsd:complexType name="ENFANTType">
+		<xsd:sequence>
+			<xsd:element ref="IDPERSONNE" />
+			<xsd:element ref="AUTORISATIONURGENCEMEDICALE" />			
+			<xsd:element ref="ASSURANCE" minOccurs="0" maxOccurs="1"/>
+			<xsd:element ref="SANITAIRE" minOccurs="0" maxOccurs="1"/>
+			<xsd:element ref="CONTACT" minOccurs="0" maxOccurs="3"/>
+		</xsd:sequence> 
+	</xsd:complexType>
+	
+	<xsd:complexType name="DUIType">
+		<xsd:sequence>
+			<xsd:element ref="DATEDEMANDE" />
+			<xsd:element ref="N0TELEDOSSIER" />
+			<xsd:element ref="IDDUI" />
+			<xsd:element ref="QUIACTUALISEDUI" />
+			<xsd:element ref="DROITALIMAGE" />
+			<xsd:element ref="REACTUALISATIONENLIGNE" />
+			<xsd:element ref="DEMATFACTURES" />
+			<xsd:element ref="ADRESSE" minOccurs="1" maxOccurs="1"/>
+			<xsd:element ref="TELFIXE" />
+			<xsd:element ref="RL1" minOccurs="0" maxOccurs="1"/>
+			<xsd:element ref="RL2" minOccurs="0" maxOccurs="1"/>
+			<xsd:element ref="ENFANT" minOccurs="0" maxOccurs="6"/>
+			<xsd:element ref="REVENUS" minOccurs="0" maxOccurs="1"/>
+		</xsd:sequence> 
+	</xsd:complexType>
+	
+	<xsd:complexType name="PORTAILType">
+		<xsd:sequence>
+				<xsd:element ref="DUI"/>
+		</xsd:sequence>	
+	</xsd:complexType>
+	
+	<xsd:element name="DATEDEMANDE" type="all:DATEType"/>
+	<xsd:element name="N0TELEDOSSIER" type="N0TELEDOSSIERType"/>
+	<xsd:element name="IDDUI" type="all:IDENTREQUIREDType"/>
+	<xsd:element name="QUIACTUALISEDUI" type="QUIACTUALISEDUIType"/>
+	<xsd:element name="DROITALIMAGE" type="all:OUINONREQUIREDType"/>
+	<xsd:element name="REACTUALISATIONENLIGNE" type="all:OUINONREQUIREDType"/>
+	<xsd:element name="DEMATFACTURES" type="all:OUINONREQUIREDType"/>
+	<xsd:element name="ADRESSE" type="adr:ADRESSEType"/>
+	<xsd:element name="TELFIXE" type="all:TELType"/>
+	<xsd:element name="RL1" type="RESPONSABLE1Type"/>
+	<xsd:element name="RL2" type="RESPONSABLE2Type"/>
+	<xsd:element name="ENFANT" type="ENFANTType"/>
+	<xsd:element name="REVENUS" type="REVENUSType"/>
+	
+	<xsd:element name="IDPERSONNE" type="all:IDENTREQUIREDType"/>
+	<xsd:element name="AUTORISATIONURGENCEMEDICALE" type="all:OUINONREQUIREDType"/>
+	<xsd:element name="ASSURANCE" type="ASSURANCEType"/>
+	<xsd:element name="SANITAIRE" type="SANITAIREType"/>
+	<xsd:element name="CONTACT" type="CONTACTType"/>
+	
+	<xsd:element name="TYPEREGIME" type="TYPEREGIMEType"/>
+	<xsd:element name="NUMEROCAF" type="NUMEROCAFType"/>
+	<xsd:element name="ALLOCATAIRE" type="ALLOCATAIREType"/>
+	<xsd:element name="CHOIXREVENU" type="CHOIXREVENUType"/>
+	<xsd:element name="NBENFANTSACHARGE" type="all:unsignedInt-or-empty"/>
+	
+	<xsd:element name="DUI" type="DUIType"/>
+	
+	<xsd:element name="PORTAIL" type="PORTAILType"/>
+</xsd:schema>
\ No newline at end of file
diff --git a/passerelle/contrib/toulouse_axel/xsd/Dui/R_FormMajFamilleDui.xsd b/passerelle/contrib/toulouse_axel/xsd/Dui/R_FormMajFamilleDui.xsd
new file mode 100644
index 00000000..36dd7345
--- /dev/null
+++ b/passerelle/contrib/toulouse_axel/xsd/Dui/R_FormMajFamilleDui.xsd
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+	<xsd:redefine schemaLocation="../R_ShemaResultat.xsd">
+	    <xsd:simpleType name="TYPEType">
+			<xsd:restriction base="TYPEType">
+				<xsd:enumeration value="FormMajFamilleDui" />
+			</xsd:restriction>
+	    </xsd:simpleType>
+		
+		<xsd:complexType name="PORTAILType">
+			<xsd:complexContent>
+				<xsd:extension base="PORTAILType">
+					<xsd:sequence>
+						<xsd:element name="DUI" type="xsd:string" minOccurs="0"/>
+					</xsd:sequence>	
+				</xsd:extension>
+			 </xsd:complexContent>
+		</xsd:complexType>
+	</xsd:redefine>		
+</xsd:schema>
diff --git a/tests/data/toulouse_axel/update_family_info.json b/tests/data/toulouse_axel/update_family_info.json
new file mode 100644
index 00000000..8ee1e3bd
--- /dev/null
+++ b/tests/data/toulouse_axel/update_family_info.json
@@ -0,0 +1,214 @@
+{
+    "ADRESSE": {
+        "CODEINSEEVILLE": null,
+        "CODEPOSTAL": "31400",
+        "COMPLEMENT1": "foo",
+        "COMPLEMENT2": null,
+        "COMPLEMENTVOIE": null,
+        "NUMEROSTI": "1234",
+        "NUMVOIE": "42",
+        "VILLE": "TOULOUSE",
+        "VOIE": "RUE bar"
+    },
+    "DEMATFACTURES": "NON",
+    "DROITALIMAGE": "NON",
+    "ENFANT": [
+        {
+            "ASSURANCE": {
+                "DATEFIN": "2020-12-31",
+                "NOM": "foo",
+                "NUMERO": "1234"
+            },
+            "AUTORISATIONURGENCEMEDICALE": "OUI",
+            "CONTACT": [
+                {
+                    "CHERCHERLENFANT": "OUI",
+                    "ENCASURGENCE": "OUI",
+                    "LIENPARENTE": null,
+                    "NOM": "foo",
+                    "PRENOM": "foo",
+                    "TELFIXE": "0505050505",
+                    "TELPORTABLE": "0606060606"
+                },
+                {
+                    "CHERCHERLENFANT": "OUI",
+                    "ENCASURGENCE": "OUI",
+                    "LIENPARENTE": null,
+                    "NOM": "foo",
+                    "PRENOM": "foo",
+                    "TELFIXE": "0505050505",
+                    "TELPORTABLE": "0606060606"
+                },
+                {
+                    "CHERCHERLENFANT": "OUI",
+                    "ENCASURGENCE": "OUI",
+                    "LIENPARENTE": null,
+                    "NOM": "foo",
+                    "PRENOM": "foo",
+                    "TELFIXE": "0505050505",
+                    "TELPORTABLE": "0606060606"
+                }
+            ],
+            "IDPERSONNE": "4242",
+            "SANITAIRE": {
+                "ALLERGIE": [
+                    {
+                        "ALLERGIQUE": "OUI",
+                        "NOMALLERGIE": null,
+                        "TYPE": "ASTHME"
+                    },
+                    {
+                        "ALLERGIQUE": "OUI",
+                        "NOMALLERGIE": null,
+                        "TYPE": "MEDICAMENTEUSES"
+                    },
+                    {
+                        "ALLERGIQUE": "OUI",
+                        "NOMALLERGIE": null,
+                        "TYPE": "ALIMENTAIRES"
+                    }
+                ],
+                "AUTREDIFFICULTE": null,
+                "DATEFINVALIDITE": "2020-12-31",
+                "ECOLESPECIALISEE": null,
+                "INDICATEURAUXILIAIREVS": "NON",
+                "INDICATEURECOLE": "NON",
+                "INDICATEURHANDICAP": "NON",
+                "INDICATEURNOTIFMDPH": "NON",
+                "INDICATEURPAI": "NON",
+                "INFOUTILES": null,
+                "MEDECIN": {
+                    "NOM": "foo",
+                    "TELMEDECIN": "0505050505"
+                },
+                "VACCIN": {
+                    "AUTRE": null,
+                    "BCGAJOUR": null,
+                    "COQAJOUR": null,
+                    "DAJOUR": null,
+                    "HBAJOUR": null,
+                    "HIBAJOUR": null,
+                    "IIPAJOUR": null,
+                    "MENINAJOUR": null,
+                    "PAJOUR": null,
+                    "RORAJOUR": null,
+                    "TAJOUR": null
+                }
+            }
+        },
+        {
+            "ASSURANCE": {
+                "DATEFIN": "2020-12-31",
+                "NOM": "foo",
+                "NUMERO": "1234"
+            },
+            "AUTORISATIONURGENCEMEDICALE": "OUI",
+            "IDPERSONNE": "3535",
+            "SANITAIRE": {
+                "ALLERGIE": [
+                    {
+                        "ALLERGIQUE": "OUI",
+                        "NOMALLERGIE": null,
+                        "TYPE": "ASTHME"
+                    },
+                    {
+                        "ALLERGIQUE": "OUI",
+                        "NOMALLERGIE": null,
+                        "TYPE": "MEDICAMENTEUSES"
+                    },
+                    {
+                        "ALLERGIQUE": "OUI",
+                        "NOMALLERGIE": null,
+                        "TYPE": "ALIMENTAIRES"
+                    }
+                ],
+                "AUTREDIFFICULTE": null,
+                "DATEFINVALIDITE": "2020-12-31",
+                "ECOLESPECIALISEE": null,
+                "INDICATEURAUXILIAIREVS": "NON",
+                "INDICATEURECOLE": "NON",
+                "INDICATEURHANDICAP": "NON",
+                "INDICATEURNOTIFMDPH": "NON",
+                "INDICATEURPAI": "NON",
+                "INFOUTILES": null,
+                "MEDECIN": {
+                    "NOM": "foo",
+                    "TELMEDECIN": "0505050505"
+                },
+                "VACCIN": {
+                    "AUTRE": null,
+                    "BCGAJOUR": null,
+                    "COQAJOUR": "OUI",
+                    "DAJOUR": "OUI",
+                    "HBAJOUR": "OUI",
+                    "HIBAJOUR": "OUI",
+                    "IIPAJOUR": "OUI",
+                    "MENINAJOUR": "OUI",
+                    "PAJOUR": "OUI",
+                    "RORAJOUR": "OUI",
+                    "TAJOUR": "OUI"
+                }
+            }
+        }
+    ],
+    "N0TELEDOSSIER": "42",
+    "REACTUALISATIONENLIGNE": "NON",
+    "REVENUS": {
+        "ALLOCATAIRE": "2",
+        "CHOIXREVENU": "",
+        "NBENFANTSACHARGE": 2,
+        "NUMEROCAF": "1234",
+        "TYPEREGIME": "GENE"
+    },
+    "RL1": {
+        "ADREMPLOYEUR": {
+            "CODEINSEEVILLE": null,
+            "CODEPOSTAL": null,
+            "COMPLEMENT1": null,
+            "COMPLEMENT2": null,
+            "COMPLEMENTVOIE": null,
+            "NUMEROSTI": null,
+            "NUMVOIE": null,
+            "VILLE": null,
+            "VOIE": null
+        },
+        "CIVILITE": "M",
+        "COURRIEL": "foo@bar.com",
+        "CSP": "ETU",
+        "DATENAISSANCE": "1980-01-01",
+        "IDPERSONNE": "42",
+        "INDICATEURRL": "1",
+        "NOM": "foo",
+        "NOMEMPLOYEUR": "foo",
+        "NOMJEUNEFILLE": null,
+        "PRENOM": "foo",
+        "PROFESSION": "ETUDIANT",
+        "TELPORTABLE": "0606060606"
+    },
+    "RL2": {
+        "ADREMPLOYEUR": {
+            "CODEINSEEVILLE": null,
+            "CODEPOSTAL": null,
+            "COMPLEMENT1": null,
+            "COMPLEMENT2": null,
+            "COMPLEMENTVOIE": null,
+            "NUMEROSTI": null,
+            "NUMVOIE": null,
+            "VILLE": null,
+            "VOIE": null
+        },
+        "CIVILITE": "MME",
+        "COURRIEL": "foo@bar.com",
+        "CSP": "EMP",
+        "DATENAISSANCE": "1980-01-01",
+        "IDPERSONNE": "35",
+        "INDICATEURRL": "2",
+        "NOM": "foo",
+        "NOMEMPLOYEUR": "foo",
+        "NOMJEUNEFILLE": null,
+        "PRENOM": "foo",
+        "PROFESSION": "foo",
+        "TELPORTABLE": "0606060606"
+    },
+    "TELFIXE": null
+}
diff --git a/tests/test_toulouse_axel.py b/tests/test_toulouse_axel.py
index 084f86bb..dff2cb89 100644
--- a/tests/test_toulouse_axel.py
+++ b/tests/test_toulouse_axel.py
@@ -15,6 +15,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
 from contextlib import contextmanager
+import datetime
+import json
 import mock
 import os
 
@@ -24,6 +26,7 @@ from passerelle.contrib.toulouse_axel.models import (
     AxelError,
     Link,
     ToulouseAxel,
+    form_maj_famille_dui,
     ref_famille_dui,
     ref_facture_a_payer,
     ref_facture_pdf,
@@ -41,7 +44,7 @@ def resource(db):
 
 
 @pytest.fixture
-def params():
+def link_params():
     return {
         'IDDUI': 'XXX',
         'PRENOM': 'John',
@@ -50,6 +53,14 @@ def params():
     }
 
 
+@pytest.fixture
+def update_params():
+    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/update_family_info.json')
+    with open(filepath) as jsonfile:
+        content = jsonfile.read()
+    return json.loads(content)
+
+
 def test_operation_status_error(resource):
     resp = '''
     <?xml version="1.0"?>
@@ -70,6 +81,8 @@ def test_operation_status_error(resource):
             ref_verif_dui(resource, {
                 'PORTAIL': {
                     'DUI': {
+                        'IDDUI': 'XXX',
+                        'IDPERSONNE': '42',
                         'PRENOM': 'John',
                         'NOM': 'Doe',
                         'NAISSANCE': '2010-10-10',
@@ -133,6 +146,21 @@ def test_operation_ref_famille_dui(resource, content):
             })
 
 
+@pytest.mark.parametrize('content', [
+    '<PORTAIL><DUI/></PORTAIL>',
+])
+def test_operation_form_maj_famille_dui(resource, content):
+    with mock_getdata(content, 'FormMajFamilleDui'):
+        with pytest.raises(AxelError):
+            form_maj_famille_dui(resource, {
+                'PORTAIL': {
+                    'DUI': {
+                        'IDDUI': 'XXX',
+                    }
+                }
+            })
+
+
 @pytest.mark.parametrize('content', [
     '<PORTAIL><DUI/></PORTAIL>',
 ])
@@ -163,15 +191,15 @@ def test_operation_ref_facture_pdf(resource, content):
             })
 
 
-def test_link_endpoint_nameid_empty(app, resource, params):
-    resp = app.post_json('/toulouse-axel/test/link?NameID=', params=params, status=400)
+def test_link_endpoint_nameid_empty(app, resource, link_params):
+    resp = app.post_json('/toulouse-axel/test/link?NameID=', params=link_params, status=400)
     assert resp.json['err_desc'] == "NameID is empty"
 
 
-def test_link_endpoint_axel_error(app, resource, params):
+def test_link_endpoint_axel_error(app, resource, link_params):
     with mock.patch('passerelle.contrib.toulouse_axel.models.ref_verif_dui') as operation:
         operation.side_effect = AxelError('FooBar')
-        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=params)
+        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
         assert resp.json['err_desc'] == "Axel error: FooBar"
 
 
@@ -180,18 +208,18 @@ def test_link_endpoint_axel_error(app, resource, params):
     '<IDDUI>XXX</IDDUI><IDPERSONNE>42</IDPERSONNE><CODE>1</CODE>',
     '<IDDUI>XXX</IDDUI><IDPERSONNE>42</IDPERSONNE><CODE>4</CODE>',
 ])
-def test_link_endpoint_no_result(app, resource, params, xml_response):
+def test_link_endpoint_no_result(app, resource, link_params, xml_response):
     content = '''<PORTAIL>
     <DUI>
         %s
     </DUI>
 </PORTAIL>''' % xml_response
     with mock_getdata(content, 'RefVerifDui'):
-        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=params)
+        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
     assert resp.json['err_desc'] == "Person not found"
 
 
-def test_link_endpoint_conflict(app, resource, params):
+def test_link_endpoint_conflict(app, resource, link_params):
     content = '''<PORTAIL>
     <DUI>
         <IDDUI>XXX</IDDUI>
@@ -202,7 +230,7 @@ def test_link_endpoint_conflict(app, resource, params):
     # existing link but dui is wrong
     link = Link.objects.create(resource=resource, name_id='yyy', dui='YYY', person_id='42')
     with mock_getdata(content, 'RefVerifDui'):
-        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=params)
+        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
     assert resp.json['err_desc'] == "Data conflict"
 
     # existing link but person_id is wrong
@@ -210,12 +238,12 @@ def test_link_endpoint_conflict(app, resource, params):
     link.person_id = '35'
     link.save()
     with mock_getdata(content, 'RefVerifDui'):
-        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=params)
+        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
     assert resp.json['err_desc'] == "Data conflict"
 
 
 @pytest.mark.parametrize('code', [2, 3])
-def test_link_endpoint(app, resource, params, code):
+def test_link_endpoint(app, resource, link_params, code):
     content = '''<PORTAIL>
     <DUI>
         <IDDUI>XXX</IDDUI>
@@ -224,7 +252,7 @@ def test_link_endpoint(app, resource, params, code):
     </DUI>
 </PORTAIL>''' % code
     with mock_getdata(content, 'RefVerifDui'):
-        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=params)
+        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
     assert set(resp.json.keys()) == set([
         'err', 'link', 'created', 'dui'])
     assert resp.json['err'] == 0
@@ -233,7 +261,7 @@ def test_link_endpoint(app, resource, params, code):
 
     # again
     with mock_getdata(content, 'RefVerifDui'):
-        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=params)
+        resp = app.post_json('/toulouse-axel/test/link?NameID=yyy', params=link_params)
     assert set(resp.json.keys()) == set([
         'err', 'link', 'created', 'dui'])
     assert resp.json['err'] == 0
@@ -340,6 +368,43 @@ def test_child_info_endpoint(app, resource):
     ])
 
 
+def test_update_family_info_endpoint_axel_error(app, resource, update_params):
+    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
+    with mock.patch('passerelle.contrib.toulouse_axel.models.form_maj_famille_dui') as operation:
+        operation.side_effect = AxelError('FooBar')
+        resp = app.post_json('/toulouse-axel/test/update_family_info?NameID=yyy', params=update_params)
+        assert resp.json['err_desc'] == "Axel error: FooBar"
+
+
+def test_update_family_info_endpoint_no_result(app, resource, update_params):
+    resp = app.post_json('/toulouse-axel/test/update_family_info?NameID=yyy', params=update_params)
+    assert resp.json['err_desc'] == "Person not found"
+
+
+def test_update_family_info_endpoint(app, resource, update_params):
+    link = Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
+    content = "<PORTAIL/>"
+    with mock_getdata(content, 'FormMajFamilleDui'):
+        resp = app.post_json('/toulouse-axel/test/update_family_info?NameID=yyy', params=update_params)
+    assert resp.json['err'] == 0
+    assert resp.json['dui'] == 'XXX'
+    assert resp.json['updated'] is True
+
+    with mock.patch('passerelle.contrib.toulouse_axel.models.form_maj_famille_dui') as operation:
+        resp = app.post_json('/toulouse-axel/test/update_family_info?NameID=yyy', params=update_params)
+    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI']['IDDUI'] == 'XXX'
+    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI']['DATEDEMANDE'] == datetime.date.today().strftime('%Y-%m-%d')
+    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI']['QUIACTUALISEDUI'] == '1'
+
+    link.person_id = '35'
+    link.save()
+    with mock.patch('passerelle.contrib.toulouse_axel.models.form_maj_famille_dui') as operation:
+        resp = app.post_json('/toulouse-axel/test/update_family_info?NameID=yyy', params=update_params)
+    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI']['IDDUI'] == 'XXX'
+    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI']['DATEDEMANDE'] == datetime.date.today().strftime('%Y-%m-%d')
+    assert operation.call_args_list[0][0][1]['PORTAIL']['DUI']['QUIACTUALISEDUI'] == '2'
+
+
 def test_invoices_endpoint_axel_error(app, resource):
     Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
     with mock.patch('passerelle.contrib.toulouse_axel.models.ref_facture_a_payer') as operation:
-- 
2.20.1

