From 0b44a7cb3923f66c2eb07db55ac22b33d563487d Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Thu, 28 May 2020 17:04:48 +0200 Subject: [PATCH] avis_imposition: mimic avis-imposition API particulier endpoint (#43479) Return value is the same as API particulier for the avis-imposition endpoint, except for some data massaging put into alternative keys: * dates are converted to ISO format and put in keys with @_iso@ suffix, * family situation is simplified to ASCII and put in key with @_simple@ suffix, * addresse is returned as oneline joined with newlines as in API particulier, but also with individual lines separated into 3 keys : adresse1, adresse2, adresse3. --- passerelle/apps/avis_imposition/__init__.py | 0 .../migrations/0001_initial.py | 30 +++ .../avis_imposition/migrations/__init__.py | 0 passerelle/apps/avis_imposition/models.py | 233 ++++++++++++++++++ passerelle/settings.py | 1 + tests/data/avis-imposition.html | 85 +++++++ tests/data/avis-imposition.json | 86 +++++++ tests/test_avis_imposition.py | 116 +++++++++ 8 files changed, 551 insertions(+) create mode 100644 passerelle/apps/avis_imposition/__init__.py create mode 100644 passerelle/apps/avis_imposition/migrations/0001_initial.py create mode 100644 passerelle/apps/avis_imposition/migrations/__init__.py create mode 100644 passerelle/apps/avis_imposition/models.py create mode 100644 tests/data/avis-imposition.html create mode 100644 tests/data/avis-imposition.json create mode 100644 tests/test_avis_imposition.py diff --git a/passerelle/apps/avis_imposition/__init__.py b/passerelle/apps/avis_imposition/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/passerelle/apps/avis_imposition/migrations/0001_initial.py b/passerelle/apps/avis_imposition/migrations/0001_initial.py new file mode 100644 index 00000000..b205680f --- /dev/null +++ b/passerelle/apps/avis_imposition/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2020-05-28 19:49 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('base', '0020_auto_20200515_1923'), + ] + + operations = [ + migrations.CreateModel( + name='AvisImposition', + 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')), + ('users', models.ManyToManyField(blank=True, related_name='_avisimposition_users_+', related_query_name='+', to='base.ApiUser')), + ], + options={ + 'verbose_name': 'API Particulier', + }, + ), + ] diff --git a/passerelle/apps/avis_imposition/migrations/__init__.py b/passerelle/apps/avis_imposition/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/passerelle/apps/avis_imposition/models.py b/passerelle/apps/avis_imposition/models.py new file mode 100644 index 00000000..edc14716 --- /dev/null +++ b/passerelle/apps/avis_imposition/models.py @@ -0,0 +1,233 @@ +# coding: utf-8 +# passerelle - uniform access to multiple data sources and services +# Copyright (C) 2017 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 . + +'''Interface with "Service de vérification des avis" of impots.gouv.fr +''' + +from __future__ import unicode_literals + +import datetime +import logging +import re + +import lxml.html +import requests + +from django.utils.translation import ugettext_lazy as _ +from django.utils.six.moves.urllib_parse import urljoin + +from passerelle.base.models import BaseResource +from passerelle.utils.api import endpoint +from passerelle.utils.jsonresponse import APIError +from passerelle.utils.xml import text_content +from passerelle.utils.json import unflatten + +AVIS_IMPOSITION_GOUV_FR_URL = 'https://cfsmsp.impots.gouv.fr/secavis/' + + +def remove_spaces(value): + return re.sub(r'\s', '', value, flags=re.U) + + +def simplify_spaces(value): + return re.sub(r'\s+', ' ', value, flags=re.U) + + +FIELD_INDEXES = { + 'declarant1/nom': 4, + 'declarant2/nom': 5, + 'declarant1/nomNaissance': 7, + 'declarant2/nomNaissance': 8, + 'declarant1/prenom': 10, + 'declarant2/prenom': 11, + 'declarant1/dateNaissance': 13, + 'declarant2/dateNaissance': 14, + 'declarant1/dateNaissance_iso': 13, + 'declarant2/dateNaissance_iso': 14, + 'dateRecouvrement': 24, + 'dateEtablissement': 26, + 'dateRecouvrement_iso': 24, + 'dateEtablissement_iso': 26, + 'nombreParts': 28, + 'situationFamille': 30, + 'situationFamille_simple': 30, + 'nombrePersonnesCharge': 32, + 'revenuBrutGlobal': 34, + 'revenuImposable': 36, + 'impotRevenuNetAvantCorrections': 38, + 'montantImpot': 40, + 'revenuFiscalReference': 42, + 'foyerFiscal/adresse1': 16, + 'foyerFiscal/adresse2': 19, + 'foyerFiscal/adresse3': 21, +} + + +def nombre_de_parts(value): + try: + return float(value) + except (ValueError, TypeError): + return None + + +def euros(value): + if not value: + return value + value = remove_spaces(value) + value = value.rstrip('€') + try: + return int(value) + except ValueError: + return value + + +def situation_familiale(value): + if value == 'Pacs\xe9(e)s': + return 'pacs/mariage' + if value == 'Mari\xe9(e)s': + return 'pacs/mariage' + if value == 'Divorc\xe9(e)': + return 'divorce' + if value == 'C\xe9libataire': + return 'celibataire' + raise NotImplementedError(value) + + +def date(value): + if not value: + return '' + try: + return datetime.datetime.strptime(value, '%d/%m/%Y').date() + except (ValueError, TypeError): + return '' + +ADAPTERS = { + 'nombreParts': nombre_de_parts, + 'revenuBrutGlobal': euros, + 'revenuImposable': euros, + 'impotRevenuNetAvantCorrections': euros, + 'revenuFiscalReference': euros, + 'montantImpot': euros, + 'situationFamille_simple': situation_familiale, + 'dateEtablissement_iso': date, + 'dateRecouvrement_iso': date, + 'declarant1/dateNaissance_iso': date, + 'declarant2/dateNaissance_iso': date, + 'nombrePersonnesCharge': nombre_de_parts, +} + + +def get_form(logger=None): + logger = logger or logging + try: + response = requests.get(AVIS_IMPOSITION_GOUV_FR_URL) + response.raise_for_status() + except requests.RequestException as e: + raise APIError('service-is-down', data=str(e)) + + if 'Saisissez les identifiants' not in response.text: + raise RuntimeError('service has changed') + + html = lxml.html.fromstring(response.content) + data = {} + for form_elt in html.xpath('//form'): + if 'action' not in form_elt.attrib: + continue + action_url = form_elt.attrib['action'] + break + else: + raise RuntimeError('service has changed') + logger.debug('using found action_url %s', action_url) + + for input_elt in html.xpath('//input'): + if 'value' in input_elt.attrib: + data[input_elt.attrib['name']] = input_elt.attrib['value'] + + return action_url, data + + +def get_avis_imposition(numero_fiscal, reference_avis, logger=None): + logger = logger or logging + + action_url, data = get_form() + data['j_id_7:spi'] = numero_fiscal + data['j_id_7:num_facture'] = reference_avis + + logger.debug('sending data %s', data) + + try: + response = requests.post(urljoin(AVIS_IMPOSITION_GOUV_FR_URL, action_url), params=data) + response.raise_for_status() + except requests.RequestException as e: + raise APIError('service-is-down', data=str(e)) + + if 'Saisissez les identifiants' in response.text: + raise APIError('not-found') + + response_html = lxml.html.fromstring(response.content) + td_contents = [simplify_spaces(text_content(td).strip()) for td in response_html.xpath('//td')] + + logger.debug('got td_contents %s', td_contents) + + data = { + # https://github.com/betagouv/svair-api/blob/master/utils/year.js + 'foyerFiscal/year': int('20' + reference_avis[:2]), + } + for field, index in FIELD_INDEXES.items(): + try: + data[field] = td_contents[index] or '' + except IndexError: + raise RuntimeError('service has changed') + if field in ADAPTERS: + data[field] = ADAPTERS[field](data[field]) + for situation_partielle_elt in response_html.xpath('//*[@id="situationPartielle"]'): + data['situationPartielle'] = text_content(situation_partielle_elt).strip() + break + return data + + +class AvisImposition(BaseResource): + @endpoint(perm='can_access', + description=_('Get citizen\'s fiscal informations'), + parameters={ + 'numero_fiscal': { + 'description': _('fiscal identifier'), + 'example_value': '1562456789521', + }, + 'reference_avis': { + 'description': _('tax notice number'), + 'example_value': '1512456789521', + }, + }) + def verify(self, request, numero_fiscal, reference_avis): + numero_fiscal = remove_spaces(numero_fiscal) + reference_avis = remove_spaces(reference_avis) + data = get_avis_imposition(numero_fiscal, reference_avis) + unflattened = unflatten(data) + foyer_fiscal = unflattened['foyerFiscal'] + foyer_fiscal['adresse'] = ' '.join( + value for key, value in sorted(foyer_fiscal.items()) if key.startswith('adresse') and value + ) + return {'data': unflattened} + + def check_status(self): + get_form() + + category = _('Business Process Connectors') + + class Meta: + verbose_name = _('API Particulier') diff --git a/passerelle/settings.py b/passerelle/settings.py index 9bf601e7..85e16338 100644 --- a/passerelle/settings.py +++ b/passerelle/settings.py @@ -129,6 +129,7 @@ INSTALLED_APPS = ( 'passerelle.apps.astregs', 'passerelle.apps.atal', 'passerelle.apps.atos_genesys', + 'passerelle.apps.avis_imposition', 'passerelle.apps.base_adresse', 'passerelle.apps.bdp', 'passerelle.apps.cartads_cs', diff --git a/tests/data/avis-imposition.html b/tests/data/avis-imposition.html new file mode 100644 index 00000000..d802c1ef --- /dev/null +++ b/tests/data/avis-imposition.html @@ -0,0 +1,85 @@ + + + + +Impots.gouv.fr - Service de vérification en ligne des avis + + + + + + + +
+
+
+
+
+
+ + +

Le service permet de vérifier l'authenticité des avis (Impôt sur le revenu) présentés par un usager

+
+
Accès au service de vérification
+
+
Saisissez les identifiants
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

+
+ aideSPI +
+
+ aideReferenceAvis +
+
+
+ +
+
+ +
+
+
+
* données obligatoires
+
+
© Ministère de l'Économie et des Finances
+
+ +
\ No newline at end of file diff --git a/tests/data/avis-imposition.json b/tests/data/avis-imposition.json new file mode 100644 index 00000000..a81bbbca --- /dev/null +++ b/tests/data/avis-imposition.json @@ -0,0 +1,86 @@ +[ + { + "numero_fiscal": "1234", + "reference_avis": "18abcd", + "response": "\n\n\t\n\n\n\nImpots.gouv.fr - Service de v\u00e9rification en ligne des avis\n\n\n\n\n\n
\n
\n\t\t\t
\"\"
\n\t\t\t
\"\"
\n
\n
\n
\nL'administration fiscale certifie l'authenticit\u00e9 du document pr\u00e9sent\u00e9 pour les donn\u00e9es suivantes :\n
\n\n
\nImp\u00f4t 2019 sur les revenus de l'ann\u00e9e 2018 \n\n
\t\t\n\t\t\n\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\n\t\t\t
\n\t\t\t\t\tD\u00e9clarant 1\n\t\t\t\t\tD\u00e9clarant 2\n\t\t\t\t\t
Nom\n\t\t\t\t\tDOE\n\t\t\t\t\tDOE\n\t\t\t\t\t
Nom de naissance\n\t\t\t\t\tDOE\n\t\t\t\t\tDOE\n\t\t\t\t\t
Pr\u00e9nom(s)\n\t\t\t\t\tJOHN\n\t\t\t\t\tJANE\n\t\t\t\t\t
Date de naissance\n\t\t\t\t\t01/01/1970\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\tAdresse d\u00e9clar\u00e9e au 1er janvier 2019\n\t\t\t\t\tR\u00c9SIDENCE DU CALVAIRE\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\tRUE VICTOR HUGO\n\t\t\t\t\t
\n\t\t\t\t\t75014 PARIS\n\t\t\t\t\t
Date de mise en recouvrement de l'avis d'imp\u00f4t\n\t\t\t\t\t31/12/2019\n\t\t\t\t\t
Date d'\u00e9tablissement\n\t\t\t\t\t09/12/2019\n\t\t\t\t\t
Nombre de part(s)\n\t\t\t\t\t4.00\n\t\t\t\t\t
Situation de famille\n\t\t\t\t\tPacs\u00e9(e)s\n\t\t\t\t\t
Nombre de personne(s) \u00e0 charge\n\t\t\t\t\t4\n\t\t\t\t\t
Revenu brut global\n\t\t\t\t\t48\u00a0473 \u20ac\n\t\t\t\t\t
Revenu imposable\n\t\t\t\t\t48\u00a0473 \u20ac\n\t\t\t\t\t
Imp\u00f4t sur le revenu net avant corrections\n\t\t\t\t\t112 \u20ac\n\t\t\t\t\t
Montant de l'imp\u00f4t\n\t\t\t\t\tNon imposable\n\t\t\t\t\t
Revenu fiscal de r\u00e9f\u00e9rence\n\t\t\t\t\t48\u00a0473 \u20ac\n\t\t\t\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\n\n\t\t
\n\t\t
\u00a9 Minist\u00e8re de l'\u00c9conomie et des Finances
\"\"\n\t
\n\n", + "result": { + "dateEtablissement": "09/12/2019", + "dateRecouvrement": "31/12/2019", + "dateEtablissement_iso": "2019-12-09", + "dateRecouvrement_iso": "2019-12-31", + "declarant1": { + "dateNaissance": "01/01/1970", + "dateNaissance_iso": "1970-01-01", + "nom": "DOE", + "nomNaissance": "DOE", + "prenom": "JOHN" + }, + "declarant2": { + "dateNaissance": "", + "dateNaissance_iso": "", + "nom": "DOE", + "nomNaissance": "DOE", + "prenom": "JANE" + }, + "foyerFiscal": { + "adresse": "R\u00c9SIDENCE DU CALVAIRE RUE VICTOR HUGO 75014 PARIS", + "adresse1": "R\u00c9SIDENCE DU CALVAIRE", + "adresse2": "RUE VICTOR HUGO", + "adresse3": "75014 PARIS", + "year": 2018 + }, + "impotRevenuNetAvantCorrections": 112, + "montantImpot": "Nonimposable", + "nombreParts": 4.00, + "nombrePersonnesCharge": 4.00, + "revenuBrutGlobal": 48473, + "revenuFiscalReference": 48473, + "revenuImposable": 48473, + "situationFamille": "Pacsé(e)s", + "situationFamille_simple": "pacs/mariage", + "situationPartielle": "" + } + }, + { + "numero_fiscal": "1234", + "reference_avis": "18abcd", + "response": "\n\n\t\n\n\n\nImpots.gouv.fr - Service de v\u00e9rification en ligne des avis\n\n\n\n\n\n
\n
\n\t\t\t
\"\"
\n\t\t\t
\"\"
\n
\n
\n
\nL'administration fiscale certifie l'authenticit\u00e9 du document pr\u00e9sent\u00e9 pour les donn\u00e9es suivantes :\n
\n\n
\nImp\u00f4t 2019 sur les revenus de l'ann\u00e9e 2018 \n\n
\t\t\n\t\t\n\n\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\n\n\t\t\t
\n\t\t\t\t\tD\u00e9clarant 1\n\t\t\t\t\tD\u00e9clarant 2\n\t\t\t\t\t
Nom\n\t\t\t\t\tDOE\n\t\t\t\t\t\n\t\t\t\t\t
Nom de naissance\n\t\t\t\t\tDOE\n\t\t\t\t\t\n\t\t\t\t\t
Pr\u00e9nom(s)\n\t\t\t\t\tJOHN\n\t\t\t\t\t\n\t\t\t\t\t
Date de naissance\n\t\t\t\t\t01/01/1970\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\t\tAdresse d\u00e9clar\u00e9e au 1er janvier 2019\n\t\t\t\t\tR\u00c9SIDENCE DU CALVAIRE\n\t\t\t\t\t\n\t\t\t\t\t
\n\t\t\t\t\tRUE VICTOR HUGO\n\t\t\t\t\t
\n\t\t\t\t\t75014 PARIS\n\t\t\t\t\t
Date de mise en recouvrement de l'avis d'imp\u00f4t\n\t\t\t\t\t31/07/2019\n\t\t\t\t\t
Date d'\u00e9tablissement\n\t\t\t\t\t09/07/2019\n\t\t\t\t\t
Nombre de part(s)\n\t\t\t\t\t2.00\n\t\t\t\t\t
Situation de famille\n\t\t\t\t\tDivorc\u00e9(e)\n\t\t\t\t\t
Nombre de personne(s) \u00e0 charge\n\t\t\t\t\t2\n\t\t\t\t\t
Revenu brut global\n\t\t\t\t\t48\u00a0473 \u20ac\n\t\t\t\t\t
Revenu imposable\n\t\t\t\t\t48\u00a0473 \u20ac\n\t\t\t\t\t
Imp\u00f4t sur le revenu net avant corrections\n\t\t\t\t\t5\u00a0084 \u20ac\n\t\t\t\t\t
Montant de l'imp\u00f4t\n\t\t\t\t\tNon imposable\n\t\t\t\t\t
Revenu fiscal de r\u00e9f\u00e9rence\n\t\t\t\t\t48\u00a0473 \u20ac\n\t\t\t\t\t
\n\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\n\n\t\t
\n\t\t
\u00a9 Minist\u00e8re de l'\u00c9conomie et des Finances
\"\"\n\t
\n\n", + "result": { + "dateEtablissement": "09/07/2019", + "dateRecouvrement": "31/07/2019", + "dateEtablissement_iso": "2019-07-09", + "dateRecouvrement_iso": "2019-07-31", + "declarant1": { + "dateNaissance": "01/01/1970", + "dateNaissance_iso": "1970-01-01", + "nom": "DOE", + "nomNaissance": "DOE", + "prenom": "JOHN" + }, + "declarant2": { + "dateNaissance": "", + "dateNaissance_iso": "", + "nom": "", + "nomNaissance": "", + "prenom": "" + }, + "foyerFiscal": { + "adresse": "R\u00c9SIDENCE DU CALVAIRE RUE VICTOR HUGO 75014 PARIS", + "adresse1": "R\u00c9SIDENCE DU CALVAIRE", + "adresse2": "RUE VICTOR HUGO", + "adresse3": "75014 PARIS", + "year": 2018 + }, + "impotRevenuNetAvantCorrections": 5084, + "montantImpot": "Nonimposable", + "nombreParts": 2.00, + "nombrePersonnesCharge": 2.00, + "revenuBrutGlobal": 48473, + "revenuFiscalReference": 48473, + "revenuImposable": 48473, + "situationFamille": "Divorcé(e)", + "situationFamille_simple": "divorce", + "situationPartielle": "" + } + } +] diff --git a/tests/test_avis_imposition.py b/tests/test_avis_imposition.py new file mode 100644 index 00000000..97e7eff8 --- /dev/null +++ b/tests/test_avis_imposition.py @@ -0,0 +1,116 @@ +# passerelle - uniform access to multiple data sources and services +# Copyright (C) 2016 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 __future__ import unicode_literals + +import json + +import requests + +import httmock + +import pytest + +from passerelle.apps.avis_imposition.models import AvisImposition + +import utils + + +# Content from the form page +with open('tests/data/avis-imposition.html') as fd: + html = fd.read() + +# Contents from the submit result page +with open('tests/data/avis-imposition.json') as fd: + data = json.load(fd) + + +@pytest.fixture +def avis_imposition(db): + return utils.make_resource(AvisImposition, slug='test') + + +@pytest.mark.parametrize('data', data) +def test_ok(avis_imposition, data, app): + @httmock.urlmatch() + def http_response(url, request): + if request.method == 'GET': + return html + else: + return data['response'] + + with httmock.HTTMock(http_response): + response = utils.endpoint_get( + expected_url='/avis-imposition/test/verify', + app=app, + resource=avis_imposition, + endpoint='verify', + params={ + 'numero_fiscal': data['numero_fiscal'], + 'reference_avis': data['reference_avis'], + }) + assert {key: value for key, value in response.json['data'].items()} == data['result'] + + +def test_not_found(avis_imposition, app): + @httmock.urlmatch() + def http_response(url, request): + return html + + with httmock.HTTMock(http_response): + response = utils.endpoint_get( + expected_url='/avis-imposition/test/verify', + app=app, + resource=avis_imposition, + endpoint='verify', + params={ + 'numero_fiscal': '1234', + 'reference_avis': 'abcd', + }) + assert response.json['err_desc'] == 'not-found' + + +@pytest.mark.parametrize('error', [ + 'exception-on-get', + '500-on-get', + 'exception-on-post', + '500-on-post', +]) +def test_request_error(avis_imposition, app, error): + @httmock.urlmatch() + def http_response(url, request): + if error == 'exception-on-get': + raise requests.RequestException('boom') + if error == '500-on-get': + return {'status_code': 500} + if request.method == 'GET': + return html + if error == 'exception-on-post': + raise requests.RequestException('boom') + if error == '500-on-post': + return {'status_code': 500} + + with httmock.HTTMock(http_response): + response = utils.endpoint_get( + expected_url='/avis-imposition/test/verify', + app=app, + resource=avis_imposition, + endpoint='verify', + params={ + 'numero_fiscal': '1234', + 'reference_avis': 'abcd', + }) + assert response.json['err_desc'] == 'service-is-down' -- 2.26.2