solis_afi_mss: add solis AFI MSS connector (#47161)

This commit is contained in:
Nicolas Roche 2020-09-23 18:15:13 +02:00
parent f46622fa76
commit ad276bfdc6
20 changed files with 1425 additions and 0 deletions

View File

@ -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)',
},
),
]

View File

@ -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 <http://www.gnu.org/licenses/>.
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}

View File

@ -0,0 +1,8 @@
{
"anneeImposition": 0,
"codeCalcul": 2,
"libelleQuotient": "Quotient familial mensuel",
"montantImposition": 1444.44,
"nombrePartImposition": 2.3,
"resultatCalcul": 52.33
}

View File

@ -0,0 +1,7 @@
{
"anneeImposition" : 2011,
"indexAgent" : 389227,
"indexImposition" : 368,
"montantImposition" : 777.77,
"nombrePartImposition" : 3.2
}

View File

@ -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"
}
}

View File

@ -0,0 +1,7 @@
[
{
"links" : [],
"logref" : "29fdba95-16bb-4f1b-8b35-b157a8a785e5",
"message" : "Solis API MSS AFI - Erreur: XXX"
}
]

View File

@ -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
}

View File

@ -0,0 +1,4 @@
{
"aidesFinancieres": [],
"indexAgent": 394404
}

View File

@ -0,0 +1,7 @@
{
"anneeImposition" : 2019,
"indexAgent" : 388405,
"indexImposition" : 363,
"montantImposition" : 1000,
"nombrePartImposition" : 1
}

View File

@ -0,0 +1,16 @@
[
{
"anneeImposition": 2018,
"indexAgent": 388405,
"indexImposition": 360,
"montantImposition": 15000,
"nombrePartImposition": 1
},
{
"anneeImposition": 2019,
"indexAgent": 388405,
"indexImposition": 363,
"montantImposition": 1000,
"nombrePartImposition": 1
}
]

View File

@ -0,0 +1,3 @@
{
"response": "Solis API est opérationnel."
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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',

478
tests/test_solis_afi_mss.py Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
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