diff --git a/passerelle/contrib/toulouse_maelis/models.py b/passerelle/contrib/toulouse_maelis/models.py
index dc7a591f..fd2a2cce 100644
--- a/passerelle/contrib/toulouse_maelis/models.py
+++ b/passerelle/contrib/toulouse_maelis/models.py
@@ -138,6 +138,14 @@ class ToulouseMaelis(BaseResource, HTTPResource):
add_text_value('Sex', child, ['sexe'])
return data
+ def replace_null_values(self, dico):
+ '''send null fields as empty SOAP tag to tell maelis to empty the value'''
+ for key, value in dico.items():
+ if isinstance(value, dict):
+ self.replace_null_values(value)
+ if value is None:
+ dico[key] = ''
+
@endpoint(
display_category=_('Family'),
description='Liste des catégories',
@@ -256,6 +264,100 @@ class ToulouseMaelis(BaseResource, HTTPResource):
data = self.get_family(family_id)
return {'data': data}
+ @endpoint(
+ display_category=_('Family'),
+ description="Informations sur un responsable légal",
+ perm='can_access',
+ name='read-rl',
+ parameters={
+ 'NameID': {'description': _('Publik ID')},
+ 'rl_id': {'description': 'Numéro du représentant légal'},
+ },
+ )
+ def read_rl(self, request, NameID, rl_id):
+ family_id = self.get_link(NameID).family_id
+ data = self.get_family(family_id)
+ if data['RL1']['num'] == rl_id:
+ data = data['RL1']
+ elif data['RL2'] and data['RL2']['num'] == rl_id:
+ data = data['RL2']
+ else:
+ raise APIError("no '%s' RL on '%s' family" % (rl_id, family_id), err_code='not-found')
+ return {'data': data}
+
+ @endpoint(
+ display_category=_('Family'),
+ description="Vérifier qu'un responsable légal existe en base",
+ perm='can_access',
+ name='is-rl-exists',
+ post={'request_body': {'schema': {'application/json': schemas.ISRLEXISTS_SCHEMA}}},
+ )
+ def is_rl_exists(self, request, post_data):
+ response = self.call('Family', 'isRLExists', **post_data)
+ return {'data': response}
+
+ @endpoint(
+ display_category=_('Family'),
+ description='Création de la famille',
+ name='create-family',
+ perm='can_access',
+ parameters={'NameID': {'description': _('Publik ID')}},
+ post={'request_body': {'schema': {'application/json': schemas.CREATE_FAMILY_SCHEMA}}},
+ )
+ def create_family(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') + data.get('childErrorList')
+ err_codes = [x.split(':')[0][:4] for x in errors]
+ raise APIError(' ; '.join(errors), err_code=', '.join(err_codes))
+
+ Link.objects.create(resource=self, name_id=NameID, family_id=family_id)
+ return {'data': data}
+
+ @endpoint(
+ display_category=_('Family'),
+ description='Modification de la famille',
+ name='update-family',
+ perm='can_access',
+ parameters={'NameID': {'description': _('Publik ID')}},
+ post={'request_body': {'schema': {'application/json': schemas.UPDATE_FAMILY_SCHEMA}}},
+ )
+ def update_family(self, request, NameID, post_data):
+ family_id = self.get_link(NameID).family_id
+ self.replace_null_values(post_data)
+
+ response = self.call('Family', 'updateFamily', dossierNumber=family_id, **post_data)
+ data = serialize_object(response)
+ family_id = data.get('number')
+ if not family_id:
+ errors = data.get('rl1ErrorList') + data.get('childErrorList')
+ 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=_('Family'),
+ description="Mise à jour des coordonnées d'une personne",
+ name='update-coordinate',
+ perm='can_access',
+ parameters={
+ 'NameID': {'description': _('Publik ID')},
+ 'rl_id': {'description': 'Numéro du représentant légal'},
+ },
+ post={'request_body': {'schema': {'application/json': schemas.UPDATE_COORDINATE_SCHEMA}}},
+ )
+ def update_coordinate(self, request, NameID, rl_id, post_data):
+ family_id = self.get_link(NameID).family_id
+ self.replace_null_values(post_data)
+
+ self.call('Family', 'updateCoordinate', numDossier=family_id, numPerson=rl_id, **post_data)
+ return {'data': 'ok'}
+
class Link(models.Model):
resource = models.ForeignKey(ToulouseMaelis, on_delete=models.CASCADE)
diff --git a/passerelle/contrib/toulouse_maelis/schemas.py b/passerelle/contrib/toulouse_maelis/schemas.py
index 2c61b4f5..19cc394d 100644
--- a/passerelle/contrib/toulouse_maelis/schemas.py
+++ b/passerelle/contrib/toulouse_maelis/schemas.py
@@ -14,6 +14,17 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+import copy
+
+BOOLEAN_TYPES = [
+ {'type': 'boolean'},
+ {
+ 'type': 'string',
+ 'pattern': '^([Oo][Uu][Ii]|[Nn][Oo][Nn]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|1|0)$',
+ 'pattern_description': 'Les valeurs "0", "1", "true", "false", "oui" ou "non" sont autorisées (insensibles à la casse).',
+ },
+]
+
LINK_SCHEMA = {
'$schema': 'http://json-schema.org/draft-04/schema#',
'title': 'Link',
@@ -36,7 +47,279 @@ LINK_SCHEMA = {
'dateBirth': {
'description': 'Date de naissance du RL1',
'type': 'string',
- 'pattern': '[0-9]{4}-[0-9]{2}-[0-9]{2}',
+ 'pattern': '^[0-9]{4}-[0-9]{2}-[0-9]{2}$',
},
},
}
+
+ISRLEXISTS_SCHEMA = {
+ '$schema': 'http://json-schema.org/draft-04/schema#',
+ 'title': 'Link',
+ 'description': "Appairage d'un usager Publik à une famille dans Maelis",
+ 'type': 'object',
+ 'required': ['firstname', 'lastname', 'datebirth'],
+ 'properties': {
+ 'firstname': {
+ 'description': 'Prénom',
+ 'type': 'string',
+ },
+ 'lastname': {
+ 'description': 'Nom',
+ 'type': 'string',
+ },
+ 'datebirth': {
+ 'description': 'Date de naissance',
+ 'type': 'string',
+ 'pattern': '^[0-9]{4}-[0-9]{2}-[0-9]{2}$',
+ },
+ },
+}
+
+ADDRESS_SCHEMA = {
+ '$schema': 'http://json-schema.org/draft-04/schema#',
+ 'title': 'Address',
+ 'description': 'Informations sur une adresse',
+ 'type': 'object',
+ 'required': ['street1', 'town', 'zipcode'],
+ 'properties': {
+ 'num': {
+ 'description': 'numéro',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'numComp': {
+ 'description': 'Complément du numéro (B, T ou Q)',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'street1': {
+ 'description': 'Libellé de la voie',
+ 'type': 'string',
+ },
+ 'street2': {
+ 'description': 'Complément de la voie',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'town': {
+ 'description': 'Ville',
+ 'type': 'string',
+ },
+ 'zipcode': {
+ 'description': 'Code postal',
+ 'type': 'string',
+ },
+ },
+}
+
+CONTACT_SCHEMA = {
+ '$schema': 'http://json-schema.org/draft-04/schema#',
+ 'title': 'Contact',
+ 'description': 'Informations sur le contact',
+ 'oneOf': [
+ {'type': 'null'},
+ {
+ 'type': 'object',
+ 'properties': {
+ 'phone': {
+ 'description': 'Téléphone',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'mobile': {
+ 'description': 'Portable',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'mail': {
+ 'description': 'Mail',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'isContactMail': {
+ 'description': 'Accepte de recevoir des mails',
+ 'oneOf': BOOLEAN_TYPES,
+ },
+ 'isContactSms': {
+ 'description': 'Accepte de recevoir des sms',
+ 'oneOf': BOOLEAN_TYPES,
+ },
+ 'isInvoicePdf': {
+ 'description': 'Accepte de ne plus recevoir de facture papier',
+ 'oneOf': BOOLEAN_TYPES,
+ },
+ },
+ },
+ ],
+}
+
+ADDRESSPROF_SCHEMA = {
+ '$schema': 'http://json-schema.org/draft-04/schema#',
+ 'title': 'Contact',
+ 'description': "Informations sur l'adresse professionnelle",
+ 'oneOf': [
+ {'type': 'null'},
+ {
+ 'type': 'object',
+ 'properties': {
+ 'num': {
+ 'description': "Numéro de l'adresse",
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'street': {
+ 'description': 'Nom de la voie',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'town': {
+ 'description': 'Ville',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'zipcode': {
+ 'description': 'Code postal',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ },
+ },
+ ],
+}
+
+PROFESSION_SCHEMA = {
+ '$schema': 'http://json-schema.org/draft-04/schema#',
+ 'title': 'Profession',
+ 'description': 'Informations sur la profession',
+ 'oneOf': [
+ {'type': 'null'},
+ {
+ 'type': 'object',
+ 'properties': {
+ 'codeCSP': {
+ 'description': 'Catégorie socio-professionnelle',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'profession': {
+ 'description': 'Profession',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'employerName': {
+ 'description': "Nom de l'employeur",
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'phone': {
+ 'description': 'Téléphone',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'addressPro': ADDRESSPROF_SCHEMA,
+ },
+ },
+ ],
+}
+
+CAFINFO_SCHEMA = {
+ '$schema': 'http://json-schema.org/draft-04/schema#',
+ 'title': 'Contact',
+ 'description': 'Informations sur la CAF',
+ 'oneOf': [
+ {'type': 'null'},
+ {
+ 'type': 'object',
+ 'properties': {
+ 'number': {
+ 'description': "Numéro d'allocataire",
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'organ': {
+ 'description': "Nom de l'organisme",
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ },
+ },
+ ],
+}
+
+RLINFO_SCHEMA = {
+ '$schema': 'http://json-schema.org/draft-04/schema#',
+ 'title': 'RLInfo',
+ 'description': "Informations sur le responsable légal",
+ 'type': 'object',
+ 'required': ['firstname', 'lastname', 'quality', 'dateBirth', 'adresse'],
+ 'properties': {
+ 'civility': {
+ 'description': 'civilité (depuis référenciel)',
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'firstname': {
+ 'description': 'Prénom',
+ 'type': 'string',
+ },
+ 'lastname': {
+ 'description': 'Nom',
+ 'type': 'string',
+ },
+ 'quality': {
+ 'description': 'Qualité',
+ 'type': 'string',
+ },
+ 'dateBirth': {
+ 'description': 'Date de naissance',
+ 'type': 'string',
+ 'pattern': '^[0-9]{4}-[0-9]{2}-[0-9]{2}$',
+ },
+ 'adresse': ADDRESS_SCHEMA,
+ 'contact': CONTACT_SCHEMA,
+ 'profession': PROFESSION_SCHEMA,
+ 'CAFInfo': CAFINFO_SCHEMA,
+ },
+}
+
+
+CREATE_FAMILY_SCHEMA = {
+ '$schema': 'http://json-schema.org/draft-04/schema#',
+ 'title': 'Family',
+ 'description': 'Informations pour créer ou mettre à jour une famille',
+ 'type': 'object',
+ 'required': ['rl1', 'categorie', 'situation'],
+ 'properties': {
+ 'categorie': {
+ 'description': 'Categorie (depuis référenciel)',
+ 'type': 'string',
+ },
+ 'situation': {
+ 'description': 'Situation familiale (depuis référenciel)',
+ 'type': 'string',
+ },
+ 'flagCom': {
+ 'description': 'Hors commune',
+ 'oneOf': BOOLEAN_TYPES,
+ },
+ 'nbChild': {
+ 'description': "Nombre d'enfants à charge",
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'nbTotalChild': {
+ 'description': "Nombre total d'enfants",
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'nbAES': {
+ 'description': "Nombre d'AES",
+ 'oneOf': [{'type': 'null'}, {'type': 'string'}],
+ },
+ 'rl1': RLINFO_SCHEMA,
+ 'rl2': RLINFO_SCHEMA,
+ },
+ 'unflatten': True,
+}
+
+
+UPDATE_FAMILY_SCHEMA = copy.deepcopy(CREATE_FAMILY_SCHEMA)
+UPDATE_FAMILY_SCHEMA['required'] = ['categorie', 'situation']
+
+
+# Schemas below describe parameters of Maelis wrapper around updateFamily endpoint
+
+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,
+ 'profession': PROFESSION_SCHEMA,
+ 'CAFInfo': CAFINFO_SCHEMA,
+ },
+ 'unflatten': True,
+}
diff --git a/tests/data/toulouse_maelis/Q_create_family.xml b/tests/data/toulouse_maelis/Q_create_family.xml
new file mode 100644
index 00000000..c52fa792
--- /dev/null
+++ b/tests/data/toulouse_maelis/Q_create_family.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+ maelis-webservice
+ maelis-password
+
+
+
+
+
+ ACCEUI
+ C
+
+ Doe
+ Jhon
+ AU
+ 1938-07-26
+
+ Chateau
+ Paris
+ 75014
+
+
+
+
+
diff --git a/tests/data/toulouse_maelis/Q_update_coordinate.xml b/tests/data/toulouse_maelis/Q_update_coordinate.xml
new file mode 100644
index 00000000..35d8c839
--- /dev/null
+++ b/tests/data/toulouse_maelis/Q_update_coordinate.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+ maelis-webservice
+ maelis-password
+
+
+
+
+
+ 1312
+ 613878
+
+ 169
+
+ Château
+
+ Paris
+ 75014
+
+
+
+
diff --git a/tests/data/toulouse_maelis/Q_update_family.xml b/tests/data/toulouse_maelis/Q_update_family.xml
new file mode 100644
index 00000000..698dcfde
--- /dev/null
+++ b/tests/data/toulouse_maelis/Q_update_family.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+ maelis-webservice
+ maelis-password
+
+
+
+
+
+ 1312
+ BI
+ C
+
+ Doe
+ Jhon
+ AU
+ 1938-07-26
+
+ Chateau
+ Paris
+ 75014
+
+
+
+
+
diff --git a/tests/data/toulouse_maelis/R_create_family.xml b/tests/data/toulouse_maelis/R_create_family.xml
new file mode 100644
index 00000000..0dad2af5
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_create_family.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+ 196545
+ 394634V2
+
+
+
+
diff --git a/tests/data/toulouse_maelis/R_create_family_error.xml b/tests/data/toulouse_maelis/R_create_family_error.xml
new file mode 100644
index 00000000..90536154
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_create_family_error.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+ 0
+ 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]
+
+
+
+
diff --git a/tests/data/toulouse_maelis/R_is_rl_exists.xml b/tests/data/toulouse_maelis/R_is_rl_exists.xml
new file mode 100644
index 00000000..d93cfcec
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_is_rl_exists.xml
@@ -0,0 +1,8 @@
+
+
+
+
+ %s
+
+
+
diff --git a/tests/data/toulouse_maelis/R_update_coordinate.xml b/tests/data/toulouse_maelis/R_update_coordinate.xml
new file mode 100644
index 00000000..6d1db10e
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_update_coordinate.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/tests/data/toulouse_maelis/R_update_family.xml b/tests/data/toulouse_maelis/R_update_family.xml
new file mode 100644
index 00000000..58a52a55
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_update_family.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+ 196544
+ BI
+ C
+
+ 613955
+ DOE
+ JHON
+ AU
+ MR
+ 1938-07-26T00:00:00+01:00
+
+
+ 0
+ Chateau
+ Paris
+ 75014
+
+
+ false
+ false
+ false
+
+
+
+
+
+
diff --git a/tests/data/toulouse_maelis/R_update_family_error.xml b/tests/data/toulouse_maelis/R_update_family_error.xml
new file mode 100644
index 00000000..23d4b59b
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_update_family_error.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+ 0
+
+
+
+
+
+ E65a : Il existe déjà un enfant correspondant au nom [ZIMMERMAN], prénom [ROBERT], date de naissance [24/05/1941] - Personne n°[614051] - Famille n°[196544]
+
+
+
+
diff --git a/tests/data/toulouse_maelis/R_update_family_soap_error.xml b/tests/data/toulouse_maelis/R_update_family_soap_error.xml
new file mode 100644
index 00000000..35842c00
--- /dev/null
+++ b/tests/data/toulouse_maelis/R_update_family_soap_error.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ soap:Server
+ Une erreur est survenue : java.sql.SQLDataException: ORA-01438: valeur incohérente avec la précision indiquée pour cette colonne
+
+
+
+ Une erreur est survenue : java.sql.SQLDataException: ORA-01438: valeur incohérente avec la précision indiquée pour cette colonne
+
+
+
+
+
+
diff --git a/tests/test_toulouse_maelis.py b/tests/test_toulouse_maelis.py
index feb73ff9..42bb8bd9 100644
--- a/tests/test_toulouse_maelis.py
+++ b/tests/test_toulouse_maelis.py
@@ -19,6 +19,7 @@ import os
import mock
import pytest
+from lxml import etree
from requests.exceptions import ConnectionError
from passerelle.contrib.toulouse_maelis.models import Link, ToulouseMaelis
@@ -46,6 +47,19 @@ READ_CIVILITIES = FakedResponse(content=get_xml_file('R_read_civility_list.xml')
READ_CSP = FakedResponse(content=get_xml_file('R_read_csp_list.xml'), status_code=200)
READ_QUALITIES = FakedResponse(content=get_xml_file('R_read_quality_list.xml'), status_code=200)
READ_SITUATIONS = FakedResponse(content=get_xml_file('R_read_situation_list.xml'), status_code=200)
+IS_RL_EXISTS_TRUE = FakedResponse(content=get_xml_file('R_is_rl_exists.xml') % b'true', status_code=200)
+IS_RL_EXISTS_FALSE = FakedResponse(content=get_xml_file('R_is_rl_exists.xml') % b'false', status_code=200)
+CREATE_FAMILY = FakedResponse(content=get_xml_file('R_create_family.xml'), status_code=200)
+CREATE_FAMILY_ERR = FakedResponse(content=get_xml_file('R_create_family_error.xml'), status_code=200)
+UPDATE_FAMILY = FakedResponse(content=get_xml_file('R_update_family.xml'), status_code=200)
+UPDATE_FAMILY_ERR = FakedResponse(content=get_xml_file('R_update_family_error.xml'), status_code=200)
+UPDATE_FAMILY_500 = FakedResponse(content=get_xml_file('R_update_family_soap_error.xml'), status_code=500)
+
+
+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()
def get_endpoint(name):
@@ -126,6 +140,30 @@ def test_check_status(mocked_post, mocked_get, get_responses, post_responses, ex
con.check_status()
+def test_replace_null_values(con):
+ payload = {
+ 'adresse': {
+ 'num': '169',
+ 'numComp': None,
+ 'street1': 'Chateau',
+ 'street2': None,
+ 'town': 'Paris',
+ 'zipcode': '75014',
+ }
+ }
+ con.replace_null_values(payload)
+ assert payload == {
+ 'adresse': {
+ 'num': '169',
+ 'numComp': '',
+ 'street1': 'Chateau',
+ 'street2': '',
+ 'town': 'Paris',
+ 'zipcode': '75014',
+ }
+ }
+
+
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_link(mocked_post, mocked_get, con, app):
@@ -420,3 +458,316 @@ def test_read_family_not_linked_error(con, app):
resp = app.get(url + '?NameID=')
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_read_rl1(mocked_post, mocked_get, con, app):
+ mocked_get.return_value = FAMILY_SERVICE_WSDL
+ mocked_post.side_effect = [READ_FAMILY, READ_CATEGORIES, READ_SITUATIONS, READ_CIVILITIES, READ_QUALITIES]
+ 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'] == 'DAMIEN'
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_read_rl2(mocked_post, mocked_get, con, app):
+ mocked_get.return_value = FAMILY_SERVICE_WSDL
+ mocked_post.side_effect = [READ_FAMILY, READ_CATEGORIES, READ_SITUATIONS, READ_CIVILITIES, READ_QUALITIES]
+ url = get_endpoint('read-rl')
+ Link.objects.create(resource=con, family_id='1312', name_id='local')
+
+ resp = app.get(url + '?NameID=local&rl_id=613879')
+ assert resp.json['err'] == 0
+ assert resp.json['data'] == {
+ 'num': '613879',
+ 'lastname': 'COSTANZE',
+ 'firstname': 'JENNIFER',
+ 'quality': 'MERE',
+ 'civility': 'MME',
+ 'dateBirth': '1987-05-21T00:00:00+02:00',
+ 'adresse': {
+ 'idStreet': 'AV0044',
+ 'num': 9,
+ 'numComp': None,
+ 'street1': 'AVENUE VALDILETTA',
+ 'street2': 'LES MANDARINIERS',
+ 'town': 'NICE',
+ 'zipcode': '06100',
+ },
+ 'contact': {
+ 'phone': None,
+ 'mobile': None,
+ 'mail': None,
+ 'isContactMail': False,
+ 'isContactSms': False,
+ 'isInvoicePdf': False,
+ },
+ 'profession': None,
+ 'CAFInfo': {'number': '51', 'organ': None},
+ 'civility_text': 'Madame',
+ 'quality_text': 'MERE',
+ }
+
+
+def test_read_rl_not_linked_error(con, app):
+ url = get_endpoint('read-rl')
+
+ resp = app.get(url + '?NameID=local&rl_id=613879')
+ 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_read_rl_not_found(mocked_post, mocked_get, con, app):
+ mocked_get.return_value = FAMILY_SERVICE_WSDL
+ mocked_post.side_effect = [READ_FAMILY, READ_CATEGORIES, READ_SITUATIONS, READ_CIVILITIES, READ_QUALITIES]
+ url = get_endpoint('read-rl')
+ Link.objects.create(resource=con, family_id='1312', name_id='local')
+
+ resp = app.get(url + '?NameID=local&rl_id=000000')
+ assert resp.json['err'] == 'not-found'
+ assert resp.json['err_desc'] == "no '000000' RL on '1312' family"
+
+
+@pytest.mark.parametrize(
+ 'post_response, result',
+ [
+ (IS_RL_EXISTS_TRUE, True),
+ (IS_RL_EXISTS_FALSE, False),
+ ],
+)
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_is_rl_exists(mocked_post, mocked_get, post_response, result, con, app):
+ mocked_get.return_value = FAMILY_SERVICE_WSDL
+ mocked_post.return_value = post_response
+ url = get_endpoint('is-rl-exists')
+
+ params = {
+ 'firstname': 'Damien',
+ 'lastname': 'Costanze',
+ 'datebirth': '1980-10-07',
+ }
+ resp = app.post_json(url, params=params)
+ assert resp.json['err'] == 0
+ assert resp.json['data'] == result
+
+
+def test_is_rl_exists_schema_error(con, app):
+ url = get_endpoint('is-rl-exists')
+
+ params = {
+ 'firstname': 'Damien',
+ 'lastname': 'Costanze',
+ 'datebirth': '1980-10-07 more text',
+ }
+ resp = app.post_json(url, params=params, status=400)
+ assert resp.json['err'] == 1
+ assert "does not match '^[0-9]{4}-[0-9]{2}-[0-9]{2}$'" in resp.json['err_desc']
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_create_family(mocked_post, mocked_get, con, app):
+ mocked_get.return_value = FAMILY_SERVICE_WSDL
+ mocked_post.return_value = CREATE_FAMILY
+ url = get_endpoint('create-family')
+ params = {
+ 'categorie': 'ACCEUI',
+ 'situation': 'C',
+ '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'] == {
+ 'number': 196545,
+ 'password': '394634V2',
+ 'rl1ErrorList': [],
+ 'childErrorList': [],
+ }
+ assert Link.objects.get(resource=con, family_id='196545', name_id='local')
+
+
+def test_create_family_already_linked_error(con, app):
+ url = get_endpoint('create-family')
+ params = {
+ 'categorie': 'ACCEUI',
+ 'situation': 'C',
+ '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_family_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-family')
+ params = {
+ 'categorie': 'ACCEUI',
+ 'situation': 'C',
+ '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'] == '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
+ url = get_endpoint('update-family')
+ params = {
+ 'categorie': 'BI',
+ 'situation': 'C',
+ '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_sent_payload(mocked_post, 'Q_update_family.xml')
+ assert resp.json['err'] == 0
+
+
+def test_update_family_already_not_linked_error(con, app):
+ url = get_endpoint('update-family')
+ params = {
+ 'categorie': 'BI',
+ 'situation': 'C',
+ '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'] == '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_family_maelis_error(mocked_post, mocked_get, con, app):
+ mocked_get.return_value = FAMILY_SERVICE_WSDL
+ mocked_post.return_value = UPDATE_FAMILY_ERR
+ url = get_endpoint('update-family')
+ params = {
+ 'categorie': 'ACCEUI',
+ 'situation': 'C',
+ 'childList/0/lastname': 'Zimmerman',
+ 'childList/0/firstname': 'Robert',
+ 'childList/0/sexe': 'M',
+ 'childList/0/birth/dateBirth': '1941-05-24',
+ 'childList/0/birth/place': 'Duluth',
+ }
+
+ Link.objects.create(resource=con, family_id='1312', name_id='local')
+ # get 500 because error repsonse is wrongly formatted
+ resp = app.post_json(url + '?NameID=local', params=params, status=500)
+ assert resp.json == {
+ 'err': 1,
+ 'err_class': 'zeep.exceptions.XMLParseError',
+ 'err_desc': "Unexpected element 'adresse', expected 'lastname'",
+ 'data': None,
+ }
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+def test_update_family_soap_error(mocked_post, mocked_get, con, app):
+ mocked_get.return_value = FAMILY_SERVICE_WSDL
+ mocked_post.return_value = UPDATE_FAMILY_500
+ url = get_endpoint('update-family')
+ params = {
+ 'nbChild': '100',
+ 'categorie': 'BI',
+ 'situation': 'C',
+ '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'] == '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_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',
+ 'adresse/numComp': None,
+ 'adresse/street1': 'Château',
+ 'adresse/street2': None,
+ 'adresse/town': 'Paris',
+ 'adresse/zipcode': '75014',
+ }
+
+ 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_coordinate.xml')
+ assert resp.json['err'] == 0
+ assert resp.json['data'] == 'ok'
+
+
+def test_update_coordinate_schema_error(con, app):
+ url = get_endpoint('update-coordinate')
+ params = {
+ 'contact/isContactMail': 'true more text',
+ }
+
+ resp = app.post_json(url + '?NameID=local&rl_id=613878', params=params, status=400)
+ assert resp.json['err'] == 1
+ assert resp.json['err_desc'] == "'true more text' is not of type 'boolean'"