diff --git a/passerelle/contrib/solis_afi_mss/__init__.py b/passerelle/contrib/solis_afi_mss/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/passerelle/contrib/solis_afi_mss/migrations/0001_initial.py b/passerelle/contrib/solis_afi_mss/migrations/0001_initial.py
new file mode 100644
index 00000000..b2ac2c35
--- /dev/null
+++ b/passerelle/contrib/solis_afi_mss/migrations/0001_initial.py
@@ -0,0 +1,37 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.18 on 2020-10-19 10:15
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ ('base', '0022_auto_20200715_1033'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='SolisAfiMss',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('title', models.CharField(max_length=50, verbose_name='Title')),
+ ('slug', models.SlugField(unique=True, verbose_name='Identifier')),
+ ('description', models.TextField(verbose_name='Description')),
+ ('basic_auth_username', models.CharField(blank=True, max_length=128, verbose_name='Basic authentication username')),
+ ('basic_auth_password', models.CharField(blank=True, max_length=128, verbose_name='Basic authentication password')),
+ ('client_certificate', models.FileField(blank=True, null=True, upload_to='', verbose_name='TLS client certificate')),
+ ('trusted_certificate_authorities', models.FileField(blank=True, null=True, upload_to='', verbose_name='TLS trusted CAs')),
+ ('verify_cert', models.BooleanField(default=True, verbose_name='TLS verify certificates')),
+ ('http_proxy', models.CharField(blank=True, max_length=128, verbose_name='HTTP and HTTPS proxy')),
+ ('base_url', models.CharField(help_text='example: https://solis.mon-application.fr/api-mss-afi/', max_length=256, verbose_name='Service URL')),
+ ('users', models.ManyToManyField(blank=True, related_name='_solisafimss_users_+', related_query_name='+', to='base.ApiUser')),
+ ],
+ options={
+ 'verbose_name': 'Solis (mss-afi)',
+ },
+ ),
+ ]
diff --git a/passerelle/contrib/solis_afi_mss/migrations/__init__.py b/passerelle/contrib/solis_afi_mss/migrations/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/passerelle/contrib/solis_afi_mss/models.py b/passerelle/contrib/solis_afi_mss/models.py
new file mode 100644
index 00000000..7c41ca7c
--- /dev/null
+++ b/passerelle/contrib/solis_afi_mss/models.py
@@ -0,0 +1,274 @@
+# passerelle - uniform access to multiple data sources and services
+# Copyright (C) 2020 Entr'ouvert
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+from urllib.parse import urljoin
+
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+from passerelle.base.models import BaseResource, HTTPResource
+from passerelle.utils.api import endpoint
+from passerelle.utils.jsonresponse import APIError
+
+TAX_SCHEMA = {
+ '$schema': 'http://json-schema.org/draft-04/schema#',
+ "type": "object",
+ 'properties': {
+ 'email': {
+ 'description': "Agent's email address",
+ 'type': 'string',
+ },
+ 'indexImposition': {
+ 'description': 'Tax index',
+ 'type': 'string',
+ },
+ 'anneeImposition': {
+ 'description': 'Year of taxation (YYYY)',
+ 'type': 'string',
+ },
+ 'nombrePartImposition': {
+ 'description': 'Number of tax shares',
+ 'type': 'string',
+ },
+ 'montantImposition': {
+ 'description': 'Tax amount',
+ 'type': 'string',
+ },
+ }
+}
+
+DEMAND_SCHEMA = {
+ '$schema': 'http://json-schema.org/draft-04/schema#',
+ "type": "object",
+ 'properties': {
+ 'email': {
+ 'description': "Agent's email address",
+ 'type': 'string',
+ },
+ 'codeTypeAide': {
+ 'description': 'Allowance type code',
+ 'type': 'string',
+ },
+ 'natureTypeAide': {
+ 'description': 'Nature of the allowance (A, P or S)',
+ 'type': 'string',
+ },
+ 'individusConcernes': {
+ 'description': "List of related family member indexes separated by ':'",
+ 'type': 'string',
+ 'pattern': r'^[0-9 :]+$'
+ },
+ 'dateDebut': {
+ 'description': 'Start date (YYYY-MM-DD)"',
+ 'type': 'string',
+ },
+ 'dateFin': {
+ 'description': 'End date (YYYY-MM-DD)',
+ 'type': 'string',
+ },
+ 'montantFacture': {
+ 'description': 'Invoice amount',
+ 'type': 'string',
+ },
+ }
+}
+
+
+class SolisAfiMss(BaseResource, HTTPResource):
+ base_url = models.CharField(
+ max_length=256, blank=False,
+ verbose_name=_('Service URL'),
+ help_text=_('example: https://solis.mon-application.fr/api-mss-afi/')
+ )
+
+ category = _('Business Process Connectors')
+
+ class Meta:
+ verbose_name = _('Solis (mss-afi)')
+
+ def request(self, uri, params=None, json=None):
+ url = urljoin(self.base_url, uri)
+ headers = {'Accept': 'application/json'}
+ if json:
+ response = self.requests.post(url, json=json, headers=headers)
+ else:
+ response = self.requests.get(url, params=params, headers=headers)
+
+ if response.status_code // 100 != 2:
+ try:
+ json_content = response.json()
+ except ValueError:
+ json_content = None
+ raise APIError('error status:%s %r, content:%r' %
+ (response.status_code, response.reason, response.content[:1024]),
+ data={'status_code': response.status_code,
+ 'json_content': json_content})
+
+ if response.status_code == 204 or not response.content: # 204 No Content
+ return None
+
+ try:
+ return response.json()
+ except ValueError:
+ raise APIError('invalid JSON content:%r' % response.content[:1024])
+
+ def check_status(self):
+ '''
+ Raise an exception if something goes wrong.
+ '''
+ return self.request('main/isAlive/')
+
+ def search_from_email(self, email):
+ response = self.request('afi/agent/rechercherParEmail/', params={'adresseMail': email})
+ index = response.get('indexAgent')
+
+ adults = []
+ for key in ('agent', 'conjointAgent'):
+ if response.get(key):
+ adults.append(response.get(key))
+ children = response.get('enfantsAgent')
+ for record in adults + children:
+ record['id'] = record.get('indexIndividu')
+ record['text'] = '%(prenom)s %(nom)s' % record
+
+ return index, adults, children
+
+ @endpoint(
+ display_category=_('Agent'), display_order=1,
+ perm='can_access', methods=['get'],
+ description=_('Retrieve family composition'),
+ parameters={
+ 'email': {'description': _("Agent's email address")},
+ })
+ def family(self, request, email):
+ index, adults, children = self.search_from_email(email)
+ return {'data': adults + children}
+
+ @endpoint(
+ display_category=_('Agent'), display_order=1,
+ perm='can_access', methods=['get'],
+ description=_('Retrieve adults from family composition'),
+ parameters={
+ 'email': {'description': _("Agent's email address")},
+ })
+ def adults(self, request, email):
+ adults, children = self.search_from_email(email)[1:]
+ return {'data': adults}
+
+ @endpoint(
+ display_category=_('Agent'), display_order=1,
+ perm='can_access', methods=['get'],
+ description=_('Retrieve children from family composition'),
+ parameters={
+ 'email': {'description': _("Agent's email address")},
+ })
+ def children(self, request, email):
+ children = self.search_from_email(email)[2]
+ return {'data': children}
+
+ @endpoint(
+ display_category=_('Budget'), display_order=1,
+ perm='can_access', methods=['get'],
+ description=_('Retrieve the list of charges for an agent'),
+ parameters={
+ 'email': {'description': _("Agent's email address")},
+ 'year': {'description': _('Year of taxation (YYYY)')},
+ })
+ def taxes(self, request, email, year=None):
+ index = self.search_from_email(email)[0]
+ params = {'indexAgent': str(index)}
+
+ if year:
+ params['anneeImposition'] = year
+ uri = 'afi/budget/getImposition/'
+ else:
+ uri = 'afi/budget/getImpositionsParAgent/'
+
+ response = self.request(uri, params=params)
+ if year:
+ response = [response] if response else []
+
+ for record in response:
+ record['id'] = record.get('anneeImposition')
+ record['text'] = '%(anneeImposition)s: %(montantImposition)s' % record
+ return {'data': response}
+
+ @endpoint(
+ display_category=_('Budget'), display_order=2,
+ name='declare-tax', perm='can_access', methods=['post'],
+ description=_("Register an agent's tax for one year"),
+ post={'request_body': {'schema': {'application/json': TAX_SCHEMA}}})
+ def declare_tax(self, request, post_data):
+ email = post_data.pop('email')
+ post_data['indexAgent'] = str(self.search_from_email(email)[0])
+ response = self.request('afi/budget/declarerImpot/', json=post_data)
+ return {'data': response}
+
+ @endpoint(
+ display_category=_('Budget'), display_order=3,
+ name='simulate-quotient', perm='can_access', methods=['get'],
+ description=_(
+ 'Simulate the calculation of a Quotient from the tax amount and the number of shares'),
+ parameters={
+ 'email': {'description': _("Agent's email address")},
+ 'year': {'description': _('Year of taxation (YYYY)')},
+ })
+ def simulate_quotient(self, request, code, nb_parts, amount):
+ params = {
+ 'codeCalcul': code,
+ 'nbrPartImposition': nb_parts,
+ 'mntImposition': amount,
+ }
+ response = self.request('afi/budget/calculer/', params=params)
+ return {'data': response}
+
+ @endpoint(
+ display_category=_('Allowance'), display_order=1,
+ perm='can_access', methods=['get'],
+ description=_('Retrieve the list of allowance from an agent'),
+ parameters={
+ 'email': {'description': _("Agent's email address")},
+ 'text_template': {'description': _('Text template')},
+ })
+ def helps(self, request, email):
+ params = {'indexAgent': str(self.search_from_email(email)[0])}
+ response = self.request('afi/aide/getAidesParAgent/', params=params)
+
+ for record in response['aidesFinancieres']:
+ record['id'] = record.get('indexAideFinanciere')
+ record['text'] = '%(dateDemandeAide)s (%(suiviAide)s)' % record
+ return {'err': 0, 'data': response['aidesFinancieres']}
+
+ @endpoint(
+ display_category=_('Allowance'), display_order=2,
+ name='demand-help', perm='can_access', methods=['post'],
+ description=_('Submit allowance for an agent'),
+ post={'request_body': {'schema': {'application/json': DEMAND_SCHEMA}}})
+ def demand_help(self, request, post_data):
+ email = post_data.pop('email')
+ index_agent, adults, children = self.search_from_email(email)
+
+ related_persons = []
+ for person in adults + children:
+ for index in [
+ x.strip() for x in post_data['individusConcernes'].split(':') if x.strip()]:
+ if str(person['indexIndividu']) == index:
+ related_persons.append({"indexIndividu": index})
+
+ post_data['indexAgent'] = str(index_agent)
+ post_data['individusConcernes'] = related_persons
+ response = self.request('afi/aide/deposer/', json=post_data)
+ return {'data': response}
diff --git a/tests/data/solis_afi_mss/calculer.json b/tests/data/solis_afi_mss/calculer.json
new file mode 100644
index 00000000..5efa6d48
--- /dev/null
+++ b/tests/data/solis_afi_mss/calculer.json
@@ -0,0 +1,8 @@
+{
+ "anneeImposition": 0,
+ "codeCalcul": 2,
+ "libelleQuotient": "Quotient familial mensuel",
+ "montantImposition": 1444.44,
+ "nombrePartImposition": 2.3,
+ "resultatCalcul": 52.33
+}
diff --git a/tests/data/solis_afi_mss/declarerImpot_389227.json b/tests/data/solis_afi_mss/declarerImpot_389227.json
new file mode 100644
index 00000000..e9f399fa
--- /dev/null
+++ b/tests/data/solis_afi_mss/declarerImpot_389227.json
@@ -0,0 +1,7 @@
+{
+ "anneeImposition" : 2011,
+ "indexAgent" : 389227,
+ "indexImposition" : 368,
+ "montantImposition" : 777.77,
+ "nombrePartImposition" : 3.2
+}
diff --git a/tests/data/solis_afi_mss/deposer_388405.json b/tests/data/solis_afi_mss/deposer_388405.json
new file mode 100644
index 00000000..0cd49e2e
--- /dev/null
+++ b/tests/data/solis_afi_mss/deposer_388405.json
@@ -0,0 +1,39 @@
+{
+ "codeAideFinanciere" : 4,
+ "codeAideGlobale" : null,
+ "codePrestataireAideFi" : 1,
+ "codeTypeAide" : 24,
+ "dateDemandeAide" : "2020-09-29",
+ "datePremierPaiement" : null,
+ "dateReponse" : null,
+ "indexAideFinanciere" : 37152,
+ "indexDossier" : 243952,
+ "indexIndividuDemandeur" : 388405,
+ "individuDemandeur" : {
+ "codeRoleFamille" : 3,
+ "dateNaissance" : "1965-08-24",
+ "indexIndividu" : 388405,
+ "nom" : "HUREL",
+ "prenom" : "Jean-Christophe"
+ },
+ "individusConcernes" : [],
+ "montantAccorde" : 0,
+ "montantDemande" : 0,
+ "natureAide" : "A",
+ "nbPersonne" : 1,
+ "prestataireAideFinanciere" : {
+ "codePrestataire" : 1,
+ "indexOrganisme" : 1,
+ "nomCompletOrganisme" : "Ministères sociaux",
+ "nomReduitOrganisme" : "Ministère"
+ },
+ "prisesEnCharge" : [],
+ "quotient" : 80,
+ "suiviAide" : "En attente",
+ "typeAideFinanciere" : {
+ "codeCalculQf" : 6,
+ "codeTypeAideFinanciere" : 24,
+ "libelleTypeAideFinanciere" : "Séjour camping - 21 jours max (PM)",
+ "natureTypeAideFinanciere" : "A"
+ }
+}
diff --git a/tests/data/solis_afi_mss/error.json b/tests/data/solis_afi_mss/error.json
new file mode 100644
index 00000000..b56165d4
--- /dev/null
+++ b/tests/data/solis_afi_mss/error.json
@@ -0,0 +1,7 @@
+[
+ {
+ "links" : [],
+ "logref" : "29fdba95-16bb-4f1b-8b35-b157a8a785e5",
+ "message" : "Solis API MSS AFI - Erreur: XXX"
+ }
+]
diff --git a/tests/data/solis_afi_mss/getAidesParAgent_388405.json b/tests/data/solis_afi_mss/getAidesParAgent_388405.json
new file mode 100644
index 00000000..64595145
--- /dev/null
+++ b/tests/data/solis_afi_mss/getAidesParAgent_388405.json
@@ -0,0 +1,373 @@
+{
+ "aidesFinancieres": [
+ {
+ "codeAideFinanciere": 1,
+ "codeAideGlobale": null,
+ "codePrestataireAideFi": 1,
+ "codeTypeAide": 33,
+ "dateDemandeAide": "2020-05-26",
+ "datePremierPaiement": null,
+ "dateReponse": null,
+ "indexAideFinanciere": 37145,
+ "indexDossier": 243952,
+ "indexIndividuDemandeur": 388405,
+ "individuDemandeur": {
+ "codeRoleFamille": 3,
+ "dateNaissance": "1965-08-24",
+ "indexIndividu": 388405,
+ "nom": "HUREL",
+ "prenom": "Jean-Christophe"
+ },
+ "individusConcernes": [
+ {
+ "codeRoleFamille": 3,
+ "dateNaissance": "1965-08-24",
+ "indexIndividu": 388405,
+ "nom": "HUREL",
+ "prenom": "Jean-Christophe"
+ },
+ {
+ "codeRoleFamille": 1,
+ "dateNaissance": "1991-08-24",
+ "indexIndividu": 388407,
+ "nom": "HUREL",
+ "prenom": "Camille"
+ },
+ {
+ "codeRoleFamille": 1,
+ "dateNaissance": "1996-08-05",
+ "indexIndividu": 388408,
+ "nom": "HUREL",
+ "prenom": "Valentin"
+ }
+ ],
+ "montantAccorde": 50,
+ "montantDemande": 0,
+ "natureAide": "A",
+ "nbPersonne": 0,
+ "prestataireAideFinanciere": {
+ "codePrestataire": 1,
+ "indexOrganisme": 1,
+ "nomCompletOrganisme": "Ministères sociaux",
+ "nomReduitOrganisme": "Ministère"
+ },
+ "prisesEnCharge": [
+ {
+ "codeAideFinanciere": 1,
+ "codePrestataireAideFi": 1,
+ "codeTypePriseEnCharge": 26,
+ "debutPeriode": "2020-04-28",
+ "finPeriode": "2020-05-13",
+ "indexAideFinanciere": 37145,
+ "indexDossier": 243952,
+ "montantParticipationAutre": 50,
+ "montantParticipationFamiliale": 0,
+ "montantTotal": 50.0,
+ "natureAide": "A",
+ "quantite": 1,
+ "tarif": 60,
+ "typePriseEnCharge": {
+ "codeTypePriseEnCharge": 26,
+ "inactivite": false,
+ "libelle": "Activité sportive",
+ "libelleReduit": "spcultu"
+ }
+ }
+ ],
+ "quotient": 80,
+ "suiviAide": "En attente",
+ "typeAideFinanciere": {
+ "codeCalculQf": 6,
+ "codeTypeAideFinanciere": 33,
+ "libelleTypeAideFinanciere": "Activité sportive et culturelle (PM)",
+ "natureTypeAideFinanciere": "A"
+ }
+ },
+ {
+ "codeAideFinanciere": 2,
+ "codeAideGlobale": null,
+ "codePrestataireAideFi": 1,
+ "codeTypeAide": 27,
+ "dateDemandeAide": "2020-05-26",
+ "datePremierPaiement": null,
+ "dateReponse": null,
+ "indexAideFinanciere": 37146,
+ "indexDossier": 243952,
+ "indexIndividuDemandeur": 388405,
+ "individuDemandeur": {
+ "codeRoleFamille": 3,
+ "dateNaissance": "1965-08-24",
+ "indexIndividu": 388405,
+ "nom": "HUREL",
+ "prenom": "Jean-Christophe"
+ },
+ "individusConcernes": [],
+ "montantAccorde": 47.15,
+ "montantDemande": 0,
+ "natureAide": "A",
+ "nbPersonne": 0,
+ "prestataireAideFinanciere": {
+ "codePrestataire": 1,
+ "indexOrganisme": 1,
+ "nomCompletOrganisme": "Ministères sociaux",
+ "nomReduitOrganisme": "Ministère"
+ },
+ "prisesEnCharge": [
+ {
+ "codeAideFinanciere": 2,
+ "codePrestataireAideFi": 1,
+ "codeTypePriseEnCharge": 2,
+ "debutPeriode": "2020-09-01",
+ "finPeriode": "2021-06-30",
+ "indexAideFinanciere": 37146,
+ "indexDossier": 243952,
+ "montantParticipationAutre": 0,
+ "montantParticipationFamiliale": 0,
+ "montantTotal": 47.15,
+ "natureAide": "A",
+ "quantite": 1,
+ "tarif": 58.94,
+ "typePriseEnCharge": {
+ "codeTypePriseEnCharge": 2,
+ "inactivite": false,
+ "libelle": "Collège",
+ "libelleReduit": "Collège"
+ }
+ }
+ ],
+ "quotient": 80,
+ "suiviAide": "En attente",
+ "typeAideFinanciere": {
+ "codeCalculQf": 6,
+ "codeTypeAideFinanciere": 27,
+ "libelleTypeAideFinanciere": "Aide éducation collège (PM)",
+ "natureTypeAideFinanciere": "A"
+ }
+ },
+ {
+ "codeAideFinanciere": 3,
+ "codeAideGlobale": null,
+ "codePrestataireAideFi": 1,
+ "codeTypeAide": 33,
+ "dateDemandeAide": "2020-06-11",
+ "datePremierPaiement": null,
+ "dateReponse": null,
+ "indexAideFinanciere": 37149,
+ "indexDossier": 243952,
+ "indexIndividuDemandeur": 388405,
+ "individuDemandeur": {
+ "codeRoleFamille": 3,
+ "dateNaissance": "1965-08-24",
+ "indexIndividu": 388405,
+ "nom": "HUREL",
+ "prenom": "Jean-Christophe"
+ },
+ "individusConcernes": [
+ {
+ "codeRoleFamille": 3,
+ "dateNaissance": "1965-08-24",
+ "indexIndividu": 388405,
+ "nom": "HUREL",
+ "prenom": "Jean-Christophe"
+ },
+ {
+ "codeRoleFamille": 5,
+ "dateNaissance": "1990-03-02",
+ "indexIndividu": 388406,
+ "nom": "HUREL",
+ "prenom": "Carole"
+ },
+ {
+ "codeRoleFamille": 1,
+ "dateNaissance": "1991-08-24",
+ "indexIndividu": 388407,
+ "nom": "HUREL",
+ "prenom": "Camille"
+ },
+ {
+ "codeRoleFamille": 1,
+ "dateNaissance": "1996-08-05",
+ "indexIndividu": 388408,
+ "nom": "HUREL",
+ "prenom": "Valentin"
+ }
+ ],
+ "montantAccorde": 0,
+ "montantDemande": 0,
+ "natureAide": "A",
+ "nbPersonne": 0,
+ "prestataireAideFinanciere": {
+ "codePrestataire": 1,
+ "indexOrganisme": 1,
+ "nomCompletOrganisme": "Ministères sociaux",
+ "nomReduitOrganisme": "Ministère"
+ },
+ "prisesEnCharge": [],
+ "quotient": 0,
+ "suiviAide": "En attente",
+ "typeAideFinanciere": {
+ "codeCalculQf": 6,
+ "codeTypeAideFinanciere": 33,
+ "libelleTypeAideFinanciere": "Activité sportive et culturelle (PM)",
+ "natureTypeAideFinanciere": "A"
+ }
+ },
+ {
+ "codeAideFinanciere": 4,
+ "codeAideGlobale": null,
+ "codePrestataireAideFi": 1,
+ "codeTypeAide": 24,
+ "dateDemandeAide": "2020-09-29",
+ "datePremierPaiement": null,
+ "dateReponse": null,
+ "indexAideFinanciere": 37152,
+ "indexDossier": 243952,
+ "indexIndividuDemandeur": 388405,
+ "individuDemandeur": {
+ "codeRoleFamille": 3,
+ "dateNaissance": "1965-08-24",
+ "indexIndividu": 388405,
+ "nom": "HUREL",
+ "prenom": "Jean-Christophe"
+ },
+ "individusConcernes": [],
+ "montantAccorde": 0,
+ "montantDemande": 0,
+ "natureAide": "A",
+ "nbPersonne": 1,
+ "prestataireAideFinanciere": {
+ "codePrestataire": 1,
+ "indexOrganisme": 1,
+ "nomCompletOrganisme": "Ministères sociaux",
+ "nomReduitOrganisme": "Ministère"
+ },
+ "prisesEnCharge": [],
+ "quotient": 80,
+ "suiviAide": "En attente",
+ "typeAideFinanciere": {
+ "codeCalculQf": 6,
+ "codeTypeAideFinanciere": 24,
+ "libelleTypeAideFinanciere": "Séjour camping - 21 jours max (PM)",
+ "natureTypeAideFinanciere": "A"
+ }
+ },
+ {
+ "codeAideFinanciere": 5,
+ "codeAideGlobale": null,
+ "codePrestataireAideFi": 1,
+ "codeTypeAide": 24,
+ "dateDemandeAide": "2020-09-29",
+ "datePremierPaiement": null,
+ "dateReponse": null,
+ "indexAideFinanciere": 37153,
+ "indexDossier": 243952,
+ "indexIndividuDemandeur": 388405,
+ "individuDemandeur": {
+ "codeRoleFamille": 3,
+ "dateNaissance": "1965-08-24",
+ "indexIndividu": 388405,
+ "nom": "HUREL",
+ "prenom": "Jean-Christophe"
+ },
+ "individusConcernes": [],
+ "montantAccorde": 0,
+ "montantDemande": 0,
+ "natureAide": "A",
+ "nbPersonne": 1,
+ "prestataireAideFinanciere": {
+ "codePrestataire": 1,
+ "indexOrganisme": 1,
+ "nomCompletOrganisme": "Ministères sociaux",
+ "nomReduitOrganisme": "Ministère"
+ },
+ "prisesEnCharge": [],
+ "quotient": 80,
+ "suiviAide": "En attente",
+ "typeAideFinanciere": {
+ "codeCalculQf": 6,
+ "codeTypeAideFinanciere": 24,
+ "libelleTypeAideFinanciere": "Séjour camping - 21 jours max (PM)",
+ "natureTypeAideFinanciere": "A"
+ }
+ },
+ {
+ "codeAideFinanciere": 6,
+ "codeAideGlobale": null,
+ "codePrestataireAideFi": 1,
+ "codeTypeAide": 24,
+ "dateDemandeAide": "2020-09-29",
+ "datePremierPaiement": null,
+ "dateReponse": null,
+ "indexAideFinanciere": 37154,
+ "indexDossier": 243952,
+ "indexIndividuDemandeur": 388405,
+ "individuDemandeur": {
+ "codeRoleFamille": 3,
+ "dateNaissance": "1965-08-24",
+ "indexIndividu": 388405,
+ "nom": "HUREL",
+ "prenom": "Jean-Christophe"
+ },
+ "individusConcernes": [],
+ "montantAccorde": 0,
+ "montantDemande": 0,
+ "natureAide": "A",
+ "nbPersonne": 1,
+ "prestataireAideFinanciere": {
+ "codePrestataire": 1,
+ "indexOrganisme": 1,
+ "nomCompletOrganisme": "Ministères sociaux",
+ "nomReduitOrganisme": "Ministère"
+ },
+ "prisesEnCharge": [],
+ "quotient": 80,
+ "suiviAide": "En attente",
+ "typeAideFinanciere": {
+ "codeCalculQf": 6,
+ "codeTypeAideFinanciere": 24,
+ "libelleTypeAideFinanciere": "Séjour camping - 21 jours max (PM)",
+ "natureTypeAideFinanciere": "A"
+ }
+ },
+ {
+ "codeAideFinanciere": 7,
+ "codeAideGlobale": null,
+ "codePrestataireAideFi": 1,
+ "codeTypeAide": 24,
+ "dateDemandeAide": "2020-09-29",
+ "datePremierPaiement": null,
+ "dateReponse": null,
+ "indexAideFinanciere": 37155,
+ "indexDossier": 243952,
+ "indexIndividuDemandeur": 388405,
+ "individuDemandeur": {
+ "codeRoleFamille": 3,
+ "dateNaissance": "1965-08-24",
+ "indexIndividu": 388405,
+ "nom": "HUREL",
+ "prenom": "Jean-Christophe"
+ },
+ "individusConcernes": [],
+ "montantAccorde": 0,
+ "montantDemande": 0,
+ "natureAide": "A",
+ "nbPersonne": 1,
+ "prestataireAideFinanciere": {
+ "codePrestataire": 1,
+ "indexOrganisme": 1,
+ "nomCompletOrganisme": "Ministères sociaux",
+ "nomReduitOrganisme": "Ministère"
+ },
+ "prisesEnCharge": [],
+ "quotient": 80,
+ "suiviAide": "En attente",
+ "typeAideFinanciere": {
+ "codeCalculQf": 6,
+ "codeTypeAideFinanciere": 24,
+ "libelleTypeAideFinanciere": "Séjour camping - 21 jours max (PM)",
+ "natureTypeAideFinanciere": "A"
+ }
+ }
+ ],
+ "indexAgent": 388405
+}
diff --git a/tests/data/solis_afi_mss/getAidesParAgent_394404.json b/tests/data/solis_afi_mss/getAidesParAgent_394404.json
new file mode 100644
index 00000000..89776893
--- /dev/null
+++ b/tests/data/solis_afi_mss/getAidesParAgent_394404.json
@@ -0,0 +1,4 @@
+{
+ "aidesFinancieres": [],
+ "indexAgent": 394404
+}
diff --git a/tests/data/solis_afi_mss/getImposition_388405_2019.json b/tests/data/solis_afi_mss/getImposition_388405_2019.json
new file mode 100644
index 00000000..9e3ac953
--- /dev/null
+++ b/tests/data/solis_afi_mss/getImposition_388405_2019.json
@@ -0,0 +1,7 @@
+{
+ "anneeImposition" : 2019,
+ "indexAgent" : 388405,
+ "indexImposition" : 363,
+ "montantImposition" : 1000,
+ "nombrePartImposition" : 1
+}
diff --git a/tests/data/solis_afi_mss/getImpositionsParAgent_388405.json b/tests/data/solis_afi_mss/getImpositionsParAgent_388405.json
new file mode 100644
index 00000000..062be56c
--- /dev/null
+++ b/tests/data/solis_afi_mss/getImpositionsParAgent_388405.json
@@ -0,0 +1,16 @@
+[
+ {
+ "anneeImposition": 2018,
+ "indexAgent": 388405,
+ "indexImposition": 360,
+ "montantImposition": 15000,
+ "nombrePartImposition": 1
+ },
+ {
+ "anneeImposition": 2019,
+ "indexAgent": 388405,
+ "indexImposition": 363,
+ "montantImposition": 1000,
+ "nombrePartImposition": 1
+ }
+]
diff --git a/tests/data/solis_afi_mss/isAlive.json b/tests/data/solis_afi_mss/isAlive.json
new file mode 100644
index 00000000..ed5fb16a
--- /dev/null
+++ b/tests/data/solis_afi_mss/isAlive.json
@@ -0,0 +1,3 @@
+{
+ "response": "Solis API est opérationnel."
+}
diff --git a/tests/data/solis_afi_mss/rechercherParEmail_evelyne_pied.json b/tests/data/solis_afi_mss/rechercherParEmail_evelyne_pied.json
new file mode 100644
index 00000000..028bc03d
--- /dev/null
+++ b/tests/data/solis_afi_mss/rechercherParEmail_evelyne_pied.json
@@ -0,0 +1,28 @@
+{
+ "adresseMailAgent" : "evelyne.pied@sg.social.gouv.fr",
+ "agent" : {
+ "dateNaissance" : "1957-06-21",
+ "indexIndividu" : 388412,
+ "lieuNaissance" : "",
+ "nom" : "PIED",
+ "prenom" : "Louise",
+ "profession" : null,
+ "roleFamille" : 3
+ },
+ "codeConfidentielAgent" : "dWyD23Hr",
+ "conjointAgent" : null,
+ "employeurAgent" : "Direction des finances, des achats et des services",
+ "enfantsAgent" : [
+ {
+ "dateNaissance" : "1997-04-26",
+ "indexIndividu" : 388413,
+ "lieuNaissance" : "",
+ "nom" : "PIED",
+ "prenom" : "KEVIN",
+ "profession" : null,
+ "roleFamille" : 1
+ }
+ ],
+ "indexAgent" : 388412,
+ "indexDossier" : 243954
+}
diff --git a/tests/data/solis_afi_mss/rechercherParEmail_jacques_rousseau.json b/tests/data/solis_afi_mss/rechercherParEmail_jacques_rousseau.json
new file mode 100644
index 00000000..eeadf9f5
--- /dev/null
+++ b/tests/data/solis_afi_mss/rechercherParEmail_jacques_rousseau.json
@@ -0,0 +1,54 @@
+{
+ "adresseMailAgent" : "jacques.rousseau.cts@tiralarc-grand-est.fr",
+ "agent" : {
+ "dateNaissance" : "1958-04-03",
+ "indexIndividu" : 389227,
+ "lieuNaissance" : "",
+ "nom" : "ROUSSEAU",
+ "prenom" : "Jacques",
+ "profession" : null,
+ "roleFamille" : 3
+ },
+ "codeConfidentielAgent" : "DcB1QKAP",
+ "conjointAgent" : {
+ "dateNaissance" : null,
+ "indexIndividu" : 434729,
+ "lieuNaissance" : "",
+ "nom" : "DI MARINO",
+ "prenom" : "Rina",
+ "profession" : null,
+ "roleFamille" : 5
+ },
+ "employeurAgent" : "DRDJSCS Grand Est",
+ "enfantsAgent" : [
+ {
+ "dateNaissance" : "1995-07-25",
+ "indexIndividu" : 389229,
+ "lieuNaissance" : "",
+ "nom" : "ROUSSEAU",
+ "prenom" : "Lola",
+ "profession" : null,
+ "roleFamille" : 1
+ },
+ {
+ "dateNaissance" : "1990-05-04",
+ "indexIndividu" : 389230,
+ "lieuNaissance" : "",
+ "nom" : "ROUSSEAU",
+ "prenom" : "Nicolas",
+ "profession" : null,
+ "roleFamille" : 1
+ },
+ {
+ "dateNaissance" : "1992-06-10",
+ "indexIndividu" : 389231,
+ "lieuNaissance" : "",
+ "nom" : "ROUSSEAU",
+ "prenom" : "Mélina",
+ "profession" : null,
+ "roleFamille" : 1
+ }
+ ],
+ "indexAgent" : 389227,
+ "indexDossier" : 244226
+}
diff --git a/tests/data/solis_afi_mss/rechercherParEmail_jean_christophe_hurel.json b/tests/data/solis_afi_mss/rechercherParEmail_jean_christophe_hurel.json
new file mode 100644
index 00000000..2267edb6
--- /dev/null
+++ b/tests/data/solis_afi_mss/rechercherParEmail_jean_christophe_hurel.json
@@ -0,0 +1,45 @@
+{
+ "adresseMailAgent" : "jean-christophe.hurel@sante.gouv.fr",
+ "agent" : {
+ "dateNaissance" : "1965-08-24",
+ "indexIndividu" : 388405,
+ "lieuNaissance" : "",
+ "nom" : "HUREL",
+ "prenom" : "Jean-Christophe",
+ "profession" : null,
+ "roleFamille" : 3
+ },
+ "codeConfidentielAgent" : "wd5rGSMv",
+ "conjointAgent" : {
+ "dateNaissance" : null,
+ "indexIndividu" : 434727,
+ "lieuNaissance" : "",
+ "nom" : "HUREL",
+ "prenom" : "CAROLE",
+ "profession" : null,
+ "roleFamille" : 5
+ },
+ "employeurAgent" : "Cabinets - DDC",
+ "enfantsAgent" : [
+ {
+ "dateNaissance" : "1991-08-24",
+ "indexIndividu" : 388407,
+ "lieuNaissance" : "",
+ "nom" : "HUREL",
+ "prenom" : "Camille",
+ "profession" : null,
+ "roleFamille" : 1
+ },
+ {
+ "dateNaissance" : "1996-08-05",
+ "indexIndividu" : 388408,
+ "lieuNaissance" : "",
+ "nom" : "HUREL",
+ "prenom" : "Valentin",
+ "profession" : null,
+ "roleFamille" : 1
+ }
+ ],
+ "indexAgent" : 388405,
+ "indexDossier" : 243952
+}
diff --git a/tests/data/solis_afi_mss/rechercherParEmail_marie_noelle_basdevant.json b/tests/data/solis_afi_mss/rechercherParEmail_marie_noelle_basdevant.json
new file mode 100644
index 00000000..48d46692
--- /dev/null
+++ b/tests/data/solis_afi_mss/rechercherParEmail_marie_noelle_basdevant.json
@@ -0,0 +1,26 @@
+{
+ "adresseMailAgent" : "marie-noelle.basdevant@jeunesse-sports.gouv.fr",
+ "agent" : {
+ "dateNaissance" : "1967-12-31",
+ "indexIndividu" : 388420,
+ "lieuNaissance" : "",
+ "nom" : "BASDEVANT",
+ "prenom" : "Marie-Noëlle",
+ "profession" : "",
+ "roleFamille" : 3
+ },
+ "codeConfidentielAgent" : "Xi7pE4Cj",
+ "conjointAgent" : {
+ "dateNaissance" : null,
+ "indexIndividu" : 434728,
+ "lieuNaissance" : "",
+ "nom" : "BARTOLOMEO",
+ "prenom" : "PIETRO",
+ "profession" : null,
+ "roleFamille" : 5
+ },
+ "employeurAgent" : "Direction de la Jeunesse, de l'Education Populaire",
+ "enfantsAgent" : [],
+ "indexAgent" : 388420,
+ "indexDossier" : 243957
+}
diff --git a/tests/data/solis_afi_mss/rechercherParEmail_michelle.delaunay.json b/tests/data/solis_afi_mss/rechercherParEmail_michelle.delaunay.json
new file mode 100644
index 00000000..5d0d1c77
--- /dev/null
+++ b/tests/data/solis_afi_mss/rechercherParEmail_michelle.delaunay.json
@@ -0,0 +1,18 @@
+{
+ "adresseMailAgent" : "michelle.delaunay@direccte.gouv.fr",
+ "agent" : {
+ "dateNaissance" : "1964-06-10",
+ "indexIndividu" : 394404,
+ "lieuNaissance" : "",
+ "nom" : "DELAUNAY",
+ "prenom" : "Michelle",
+ "profession" : null,
+ "roleFamille" : 3
+ },
+ "codeConfidentielAgent" : "tU8hLPuD",
+ "conjointAgent" : null,
+ "employeurAgent" : "DIRECCTE Provence-Alpes-Côte d'Azur-UD13",
+ "enfantsAgent" : [],
+ "indexAgent" : 394404,
+ "indexDossier" : 245978
+}
diff --git a/tests/settings.py b/tests/settings.py
index fac70d0b..ae8f8c66 100644
--- a/tests/settings.py
+++ b/tests/settings.py
@@ -29,6 +29,7 @@ INSTALLED_APPS += (
'passerelle.contrib.nancypoll',
'passerelle.contrib.planitech',
'passerelle.contrib.solis_apa',
+ 'passerelle.contrib.solis_afi_mss',
'passerelle.contrib.strasbourg_eu',
'passerelle.contrib.stub_invoices',
'passerelle.contrib.teamnet_axel',
diff --git a/tests/test_solis_afi_mss.py b/tests/test_solis_afi_mss.py
new file mode 100644
index 00000000..4a8b0140
--- /dev/null
+++ b/tests/test_solis_afi_mss.py
@@ -0,0 +1,478 @@
+# passerelle - uniform access to multiple data sources and services
+# Copyright (C) 2020 Entr'ouvert
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+import json
+import os
+
+import mock
+import pytest
+
+import utils
+
+from passerelle.contrib.solis_afi_mss.models import SolisAfiMss
+from passerelle.utils.jsonresponse import APIError
+
+@pytest.fixture
+def connector(db):
+ return utils.setup_access_rights(SolisAfiMss.objects.create(
+ slug='test',
+ base_url='https://dummy-server.org'
+ ))
+
+
+TEST_BASE_DIR = os.path.join(os.path.dirname(__file__), 'data', 'solis_afi_mss')
+
+def json_get_data(filename):
+ with open(os.path.join(TEST_BASE_DIR, "%s.json" % filename)) as fd:
+ return json.dumps(json.load(fd))
+
+def response(status_code, content):
+ return utils.FakedResponse(content=content, status_code=status_code)
+
+
+IS_ALIVE = response(200, json_get_data('isAlive'))
+
+ERROR_MSG = 'Solis API MSS AFI - Erreur: XXX'
+ERROR = response(400, json_get_data('error'))
+
+RECHERCHE_PAR_EMAIL_1 = response(200, json_get_data('rechercherParEmail_jacques_rousseau'))
+RECHERCHE_PAR_EMAIL_2 = response(200, json_get_data('rechercherParEmail_michelle.delaunay'))
+RECHERCHE_PAR_EMAIL_3 = response(200, json_get_data('rechercherParEmail_evelyne_pied'))
+RECHERCHE_PAR_EMAIL_4 = response(200, json_get_data('rechercherParEmail_marie_noelle_basdevant'))
+RECHERCHE_PAR_EMAIL_5 = response(200, json_get_data('rechercherParEmail_jean_christophe_hurel'))
+
+GET_IMPOSITION_PAR_AGENT_5 = response(200, json_get_data('getImpositionsParAgent_388405'))
+GET_IMPOSITION_5 = response(200, json_get_data('getImposition_388405_2019'))
+GET_IMPOSITION_2 = response(204, "")
+GET_IMPOSITION_0 = response(200, "")
+
+DECLARER_IMPOT_1 = response(200, json_get_data('declarerImpot_389227'))
+CALCULER_1 = response(200, json_get_data('calculer'))
+DEPOSER_5 = response(200, json_get_data('deposer_388405'))
+
+GET_AIDES_PAR_AGENT_2 = response(200, json_get_data('getAidesParAgent_394404'))
+GET_AIDES_PAR_AGENT_5 = response(200, json_get_data('getAidesParAgent_388405'))
+
+
+def get_endpoint(name):
+ return utils.generic_endpoint_url('solis-afi-mss', name)
+
+
+@mock.patch('passerelle.utils.Request.get')
+@pytest.mark.parametrize('status_code, json_content, a_dict', [
+ (200, 'not json', None),
+ (500, '{"message": "help"}', {'message': 'help'}),
+ (500, 'not json', None),
+])
+def test_request_error(mocked_get, app, connector, status_code, json_content, a_dict):
+ mocked_get.side_effect = [response(status_code, json_content)]
+ with pytest.raises(APIError) as exc:
+ connector.request('some-url')
+ assert exc.value.err
+ if status_code == 200:
+ exc.value.http_status == 200
+ exc.value.args[0] == "invalid JSON content:'%s'" % json_content
+ else:
+ assert exc.value.data['status_code'] == status_code
+ assert exc.value.data['json_content'] == a_dict
+
+
+@mock.patch('passerelle.utils.Request.get')
+def test_check_status(mocked_get, app, connector):
+ mocked_get.side_effect = [IS_ALIVE]
+ connector.check_status()
+ assert mocked_get.mock_calls == [mock.call(
+ 'https://dummy-server.org/main/isAlive/',
+ headers={'Accept': 'application/json'},
+ params=None
+ )]
+
+
+@mock.patch('passerelle.utils.Request.get')
+def test_check_status_error(mocked_get, app, connector):
+ mocked_get.side_effect = [response(500, '')]
+ with pytest.raises(APIError):
+ connector.check_status()
+
+
+@mock.patch('passerelle.utils.Request.get')
+@pytest.mark.parametrize('response1, adults, children', [
+ (RECHERCHE_PAR_EMAIL_1, [
+ (389227, 'Jacques ROUSSEAU'),
+ (434729, 'Rina DI MARINO'),
+ ], [
+ (389229, 'Lola ROUSSEAU'),
+ (389230, 'Nicolas ROUSSEAU'),
+ (389231, 'Mélina ROUSSEAU'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_2, [
+ (394404, 'Michelle DELAUNAY'),
+ ], []),
+ (RECHERCHE_PAR_EMAIL_3, [
+ (388412, 'Louise PIED'),
+ ], [
+ (388413, 'KEVIN PIED'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_4, [
+ (388420, 'Marie-Noëlle BASDEVANT'),
+ (434728, 'PIETRO BARTOLOMEO'),
+ ], []),
+ (RECHERCHE_PAR_EMAIL_5, [
+ (388405, 'Jean-Christophe HUREL'),
+ (434727, 'CAROLE HUREL'),
+ ], [
+ (388407, 'Camille HUREL'),
+ (388408, 'Valentin HUREL'),
+ ]),
+])
+def test_search_from_email(mocked_get, app, connector, response1, adults, children):
+ mocked_get.side_effect = [response1]
+ result = connector.search_from_email('foo@dummy.org')
+ assert mocked_get.mock_calls == [mock.call(
+ 'https://dummy-server.org/afi/agent/rechercherParEmail/',
+ headers={'Accept': 'application/json'},
+ params={'adresseMail': 'foo@dummy.org'})]
+ assert result[0] == adults[0][0] # agent index
+ assert [(x['id'], x['text']) for x in result[1]] == adults
+ assert [(x['id'], x['text']) for x in result[2]] == children
+
+
+@mock.patch('passerelle.utils.Request.get')
+def test_search_from_email_error(mocked_get, app, connector):
+ mocked_get.side_effect = [ERROR]
+ with pytest.raises(APIError) as exc:
+ connector.search_from_email('foo@dummy.org')
+ exc.value.data['status_code'] == 400
+ exc.value.data['json_content'][0]['message'] == ERROR_MSG
+ assert mocked_get.mock_calls == [mock.call(
+ 'https://dummy-server.org/afi/agent/rechercherParEmail/',
+ headers={'Accept': 'application/json'},
+ params={'adresseMail': 'foo@dummy.org'})]
+
+
+@mock.patch('passerelle.utils.Request.get')
+@pytest.mark.parametrize('response1, family', [
+ (RECHERCHE_PAR_EMAIL_1, [
+ (389227, 'Jacques ROUSSEAU'),
+ (434729, 'Rina DI MARINO'),
+ (389229, 'Lola ROUSSEAU'),
+ (389230, 'Nicolas ROUSSEAU'),
+ (389231, 'Mélina ROUSSEAU'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_2, [
+ (394404, 'Michelle DELAUNAY'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_3, [
+ (388412, 'Louise PIED'),
+ (388413, 'KEVIN PIED'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_4, [
+ (388420, 'Marie-Noëlle BASDEVANT'),
+ (434728, 'PIETRO BARTOLOMEO'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_5, [
+ (388405, 'Jean-Christophe HUREL'),
+ (434727, 'CAROLE HUREL'),
+ (388407, 'Camille HUREL'),
+ (388408, 'Valentin HUREL'),
+ ]),
+])
+def test_family(mocked_get, app, connector, response1, family):
+ mocked_get.side_effect = [response1]
+ endpoint = get_endpoint('family') + '?email=foo@dummy.org'
+ resp = app.get(endpoint)
+ assert not resp.json['err']
+ assert [(x['id'], x['text']) for x in resp.json['data']] == family
+
+
+@mock.patch('passerelle.utils.Request.get')
+def test_family_error(mocked_get, app, connector):
+ mocked_get.side_effect = [ERROR]
+ endpoint = get_endpoint('family') + '?email=foo@dummy.org'
+ resp = app.get(endpoint)
+ assert resp.json['err']
+ assert ERROR_MSG in resp.json['err_desc']
+ assert resp.json['data']['json_content'][0]['message'] == ERROR_MSG
+
+
+@mock.patch('passerelle.utils.Request.get')
+@pytest.mark.parametrize('response1, adults', [
+ (RECHERCHE_PAR_EMAIL_1, [
+ (389227, 'Jacques ROUSSEAU'),
+ (434729, 'Rina DI MARINO'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_2, [
+ (394404, 'Michelle DELAUNAY'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_3, [
+ (388412, 'Louise PIED'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_4, [
+ (388420, 'Marie-Noëlle BASDEVANT'),
+ (434728, 'PIETRO BARTOLOMEO'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_5, [
+ (388405, 'Jean-Christophe HUREL'),
+ (434727, 'CAROLE HUREL'),
+ ]),
+])
+def test_adults(mocked_get, app, connector, response1, adults):
+ mocked_get.side_effect = [response1]
+ endpoint = get_endpoint('adults') + '?email=foo@dummy.org'
+ resp = app.get(endpoint)
+ assert not resp.json['err']
+ assert [(x['id'], x['text']) for x in resp.json['data']] == adults
+
+
+@mock.patch('passerelle.utils.Request.get')
+@pytest.mark.parametrize('response1, children', [
+ (RECHERCHE_PAR_EMAIL_1, [
+ (389229, 'Lola ROUSSEAU'),
+ (389230, 'Nicolas ROUSSEAU'),
+ (389231, 'Mélina ROUSSEAU'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_2, [
+ ]),
+ (RECHERCHE_PAR_EMAIL_3, [
+ (388413, 'KEVIN PIED'),
+ ]),
+ (RECHERCHE_PAR_EMAIL_4, [
+ ]),
+ (RECHERCHE_PAR_EMAIL_5, [
+ (388407, 'Camille HUREL'),
+ (388408, 'Valentin HUREL'),
+ ]),
+])
+def test_children(mocked_get, app, connector, response1, children):
+ mocked_get.side_effect = [response1]
+ endpoint = get_endpoint('children') + '?email=foo@dummy.org'
+ resp = app.get(endpoint)
+ assert not resp.json['err']
+ assert [(x['id'], x['text']) for x in resp.json['data']] == children
+
+
+@mock.patch('passerelle.utils.Request.get')
+@pytest.mark.parametrize('response1, response2, taxes', [
+ (RECHERCHE_PAR_EMAIL_5, GET_IMPOSITION_PAR_AGENT_5,
+ [(2018, '2018: 15000'), (2019, '2019: 1000')])
+])
+def test_taxes(mocked_get, app, connector, response1, response2, taxes):
+ mocked_get.side_effect = [response1, response2]
+ endpoint = get_endpoint('taxes') + '?email=foo@dummy.org'
+ resp = app.get(endpoint)
+ assert mocked_get.mock_calls[1] == mock.call(
+ 'https://dummy-server.org/afi/budget/getImpositionsParAgent/',
+ headers={'Accept': 'application/json'},
+ params={'indexAgent': str(json.loads(response1.content)['indexAgent'])})
+ assert not resp.json['err']
+ assert [(x['id'], x['text']) for x in resp.json['data']] == taxes
+
+
+@mock.patch('passerelle.utils.Request.get')
+@pytest.mark.parametrize('response1, response2, tax', [
+ (RECHERCHE_PAR_EMAIL_5, GET_IMPOSITION_5, 1000),
+ (RECHERCHE_PAR_EMAIL_5, GET_IMPOSITION_2, None),
+ (RECHERCHE_PAR_EMAIL_5, GET_IMPOSITION_0, None),
+])
+def test_taxes_for_year(mocked_get, app, connector, response1, response2, tax):
+ mocked_get.side_effect = [response1, response2]
+ endpoint = get_endpoint('taxes') + '?email=foo@dummy.org&year=2019'
+ resp = app.get(endpoint)
+ assert mocked_get.mock_calls[1] == mock.call(
+ 'https://dummy-server.org/afi/budget/getImposition/',
+ headers={'Accept': 'application/json'},
+ params={
+ 'indexAgent': str(json.loads(response1.content)['indexAgent']),
+ 'anneeImposition': '2019'})
+ assert not resp.json['err']
+ if tax:
+ assert len(resp.json['data']) == 1
+ assert resp.json['data'][0]['montantImposition'] == tax
+ else:
+ assert resp.json['data'] == []
+
+
+@mock.patch('passerelle.utils.Request.get')
+@pytest.mark.parametrize('response1, response2', [
+ (ERROR, None)
+])
+def test_taxes_error(mocked_get, app, connector, response1, response2):
+ mocked_get.side_effect = [response1, response2]
+ endpoint = get_endpoint('taxes') + '?email=foo@dummy.org'
+ resp = app.get(endpoint)
+ assert resp.json['err']
+ assert resp.json['data']['json_content'][0]['message'] == ERROR_MSG
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+@pytest.mark.parametrize('response1, response2', [
+ (RECHERCHE_PAR_EMAIL_1, DECLARER_IMPOT_1),
+])
+def test_declare_tax(mocked_post, mocked_get, app, connector, response1, response2):
+ mocked_get.side_effect = [response1]
+ mocked_post.side_effect = [response2]
+ endpoint = get_endpoint('declare-tax')
+ payload = {
+ 'email': 'foo@dummy.org',
+ 'indexImposition': '222',
+ 'anneeImposition': '2020',
+ 'nombrePartImposition': '3.2',
+ 'montantImposition': '777.77',
+ }
+ resp = app.post_json(endpoint, params=payload)
+ assert mocked_post.mock_calls == [mock.call(
+ 'https://dummy-server.org/afi/budget/declarerImpot/',
+ headers={'Accept': 'application/json'},
+ json={
+ 'indexAgent': '389227',
+ 'indexImposition': '222',
+ 'anneeImposition': '2020',
+ 'nombrePartImposition': '3.2',
+ 'montantImposition': '777.77',
+ })]
+ assert resp.json == {'err': 0, 'data': json.loads(response2.content)}
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+@pytest.mark.parametrize('response1, response2', [
+ (ERROR, None),
+ (RECHERCHE_PAR_EMAIL_5, ERROR),
+])
+def test_declare_tax_error(mocked_post, mocked_get, app, connector, response1, response2):
+ mocked_get.side_effect = [response1]
+ mocked_post.side_effect = [response2]
+ endpoint = get_endpoint('declare-tax')
+ payload = {
+ 'email': 'foo@dummy.org',
+ 'indexImposition': '222',
+ 'anneeImposition': '2020',
+ 'nombrePartImposition': '3.2',
+ 'montantImposition': '777.77',
+ }
+ resp = app.post_json(endpoint, params=payload)
+ assert resp.json['err']
+ assert resp.json['data']['json_content'][0]['message'] == ERROR_MSG
+
+
+@mock.patch('passerelle.utils.Request.get')
+@pytest.mark.parametrize('response1, ratio', [
+ (CALCULER_1, 52.33),
+])
+def test_simulate_quotient(mocked_get, app, connector, response1, ratio):
+ mocked_get.side_effect = [response1]
+ endpoint = get_endpoint('simulate-quotient') + '?code=2&nb_parts=2.2&amount=222.22'
+ resp = app.get(endpoint)
+ assert mocked_get.mock_calls == [mock.call(
+ 'https://dummy-server.org/afi/budget/calculer/',
+ headers={'Accept': 'application/json'},
+ params={'codeCalcul': '2', 'nbrPartImposition': '2.2', 'mntImposition': '222.22'})]
+ assert resp.json == {'err': 0, 'data': json.loads(response1.content)}
+ assert resp.json['data']['resultatCalcul'] == ratio
+
+
+@mock.patch('passerelle.utils.Request.get')
+def test_simulate_quotient_error(mocked_get, app, connector):
+ mocked_get.side_effect = [ERROR]
+ endpoint = get_endpoint('simulate-quotient') + '?code=2&nb_parts=2.2&amount=222.22'
+ resp = app.get(endpoint)
+ assert resp.json['err']
+ assert resp.json['data']['json_content'][0]['message'] == ERROR_MSG
+
+
+@mock.patch('passerelle.utils.Request.get')
+@pytest.mark.parametrize('response1, response2, helps', [
+ (RECHERCHE_PAR_EMAIL_5, GET_AIDES_PAR_AGENT_2, []),
+ (RECHERCHE_PAR_EMAIL_5, GET_AIDES_PAR_AGENT_5, [
+ (37145, '2020-05-26 (En attente)'),
+ (37146, '2020-05-26 (En attente)'),
+ (37149, '2020-06-11 (En attente)'),
+ (37152, '2020-09-29 (En attente)'),
+ (37153, '2020-09-29 (En attente)'),
+ (37154, '2020-09-29 (En attente)'),
+ (37155, '2020-09-29 (En attente)')]),
+])
+def test_helps(mocked_get, app, connector, response1, response2, helps):
+ mocked_get.side_effect = [response1, response2]
+ endpoint = get_endpoint('helps') + '?email=foo@dummy.org'
+ resp = app.get(endpoint)
+ assert mocked_get.mock_calls[1] == mock.call(
+ 'https://dummy-server.org/afi/aide/getAidesParAgent/',
+ headers={'Accept': 'application/json'},
+ params={'indexAgent': str(json.loads(response1.content)['indexAgent'])})
+ assert not resp.json['err']
+ assert [(x['id'], x['text']) for x in resp.json['data']] == helps
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+@pytest.mark.parametrize('response1, response2', [
+ (RECHERCHE_PAR_EMAIL_5, DEPOSER_5),
+])
+def test_demand_help(mocked_post, mocked_get, app, connector, response1, response2):
+ mocked_get.side_effect = [response1]
+ mocked_post.side_effect = [response2]
+ endpoint = get_endpoint('demand-help')
+
+ payload = {
+ 'email': 'foo@dummy.org',
+ 'codeTypeAide': '24',
+ 'natureTypeAide': 'A',
+ 'individusConcernes': '388407:388408',
+ 'dateDebut': '2020-07-15',
+ 'dateFin': '2020-07-31',
+ 'montantFacture': '2222.22',
+ }
+ resp = app.post_json(endpoint, params=payload)
+ assert mocked_post.mock_calls == [mock.call(
+ 'https://dummy-server.org/afi/aide/deposer/',
+ headers={'Accept': 'application/json'},
+ json={
+ 'indexAgent': '388405',
+ 'codeTypeAide': '24',
+ 'natureTypeAide': 'A',
+ 'individusConcernes': [{'indexIndividu': '388407'}, {'indexIndividu': '388408'}],
+ 'dateDebut': '2020-07-15',
+ 'dateFin': '2020-07-31',
+ 'montantFacture': '2222.22'
+ })]
+ assert resp.json == {'err': 0, 'data': json.loads(response2.content)}
+
+
+@mock.patch('passerelle.utils.Request.get')
+@mock.patch('passerelle.utils.Request.post')
+@pytest.mark.parametrize('response1, response2', [
+ (ERROR, None),
+ (RECHERCHE_PAR_EMAIL_5, ERROR),
+])
+def test_demand_help_error(mocked_post, mocked_get, app, connector, response1, response2):
+ mocked_get.side_effect = [response1]
+ mocked_post.side_effect = [response2]
+ endpoint = get_endpoint('demand-help')
+
+ payload = {
+ 'email': 'foo@dummy.org',
+ 'codeTypeAide': '24',
+ 'natureTypeAide': 'A',
+ 'individusConcernes': '388407:388408',
+ 'dateDebut': '2020-07-15',
+ 'dateFin': '2020-07-31',
+ 'montantFacture': '2222.22',
+ }
+ resp = app.post_json(endpoint, params=payload)
+ assert resp.json['err']
+ assert resp.json['data']['json_content'][0]['message'] == ERROR_MSG