feat(api particulier): add DGFiP adapter
This commit is contained in:
parent
abad34ea7c
commit
f0913edebd
7 changed files with 419 additions and 0 deletions
|
@ -3,6 +3,7 @@ class APIParticulier::API
|
||||||
|
|
||||||
INTROSPECT_RESOURCE_NAME = "introspect"
|
INTROSPECT_RESOURCE_NAME = "introspect"
|
||||||
COMPOSITION_FAMILIALE_RESOURCE_NAME = "v2/composition-familiale"
|
COMPOSITION_FAMILIALE_RESOURCE_NAME = "v2/composition-familiale"
|
||||||
|
AVIS_IMPOSITION_RESOURCE_NAME = "v2/avis-imposition"
|
||||||
|
|
||||||
TIMEOUT = 20
|
TIMEOUT = 20
|
||||||
|
|
||||||
|
@ -20,6 +21,14 @@ class APIParticulier::API
|
||||||
codePostal: code_postal)
|
codePostal: code_postal)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def avis_imposition(numero_fiscal, reference_avis)
|
||||||
|
# NOTE: Il est possible que l'utilisateur ajoute une quatorzième lettre à la fin de sa référence d'avis.
|
||||||
|
# Il s'agit d'une clé de vérification qu'il est nécessaire de'enlever avant de contacter API Particulier.
|
||||||
|
get(AVIS_IMPOSITION_RESOURCE_NAME,
|
||||||
|
numeroFiscal: numero_fiscal.to_i.to_s.rjust(13, "0"),
|
||||||
|
referenceAvis: reference_avis.to_i.to_s.rjust(13, "0"))
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def get(resource_name, params = {})
|
def get(resource_name, params = {})
|
||||||
|
|
49
app/lib/api_particulier/dgfip_adapter.rb
Normal file
49
app/lib/api_particulier/dgfip_adapter.rb
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
class APIParticulier::DgfipAdapter
|
||||||
|
class InvalidSchemaError < ::StandardError
|
||||||
|
def initialize(errors)
|
||||||
|
super(errors.map(&:to_json).join("\n"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(api_particulier_token, numero_fiscal, reference_avis, requested_sources)
|
||||||
|
@api = APIParticulier::API.new(api_particulier_token)
|
||||||
|
@numero_fiscal = numero_fiscal
|
||||||
|
@reference_avis = reference_avis
|
||||||
|
@requested_sources = requested_sources
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_params
|
||||||
|
@api.avis_imposition(@numero_fiscal, @reference_avis)
|
||||||
|
.tap { |d| ensure_valid_schema!(d) }
|
||||||
|
.then { |d| extract_requested_sources(d) }
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def ensure_valid_schema!(data)
|
||||||
|
if !schemer.valid?(data)
|
||||||
|
errors = schemer.validate(data).to_a
|
||||||
|
raise InvalidSchemaError.new(errors)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def schemer
|
||||||
|
@schemer ||= JSONSchemer.schema(Rails.root.join('app/schemas/avis-imposition.json'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_requested_sources(data)
|
||||||
|
@requested_sources['dgfip']&.map do |(scope, sources)|
|
||||||
|
case scope
|
||||||
|
when 'foyer_fiscal'
|
||||||
|
{ scope => data['foyerFiscal'].slice(*sources).merge(data.slice(*sources)) }
|
||||||
|
when 'declarant1', 'declarant2'
|
||||||
|
sources.map { |source| { scope => data[scope].slice(*source) } }
|
||||||
|
when 'agregats_fiscaux', 'echeance_avis', 'complements'
|
||||||
|
sources.map { |source| { scope => data.slice(*source) } }
|
||||||
|
else
|
||||||
|
{ scope => data.slice(*sources) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
&.flatten&.reduce(&:deep_merge) || {}
|
||||||
|
end
|
||||||
|
end
|
81
app/schemas/avis-imposition.json
Normal file
81
app/schemas/avis-imposition.json
Normal file
|
@ -0,0 +1,81 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"$id": "http://demarches-simplifiees.fr/avis-imposition.schema.json",
|
||||||
|
"title": "avis imposition",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"declarant1": {
|
||||||
|
"$ref": "#/$defs/declarant"
|
||||||
|
},
|
||||||
|
"declarant2": {
|
||||||
|
"$ref": "#/$defs/declarant"
|
||||||
|
},
|
||||||
|
"dateRecouvrement": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[0-9]{2}/[0-9]{2}/[0-9]{4}$"
|
||||||
|
},
|
||||||
|
"dateEtablissement": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[0-9]{2}/[0-9]{2}/[0-9]{4}$"
|
||||||
|
},
|
||||||
|
"nombreParts": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"situationFamille": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"revenuBrutGlobal": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"revenuImposable": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"impotRevenuNetAvantCorrections": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"montantImpot": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"revenuFiscalReference": {
|
||||||
|
"type": "number",
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"nombrePersonnesCharge": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"anneeImpots": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[0-9]{4}$"
|
||||||
|
},
|
||||||
|
"anneeRevenus": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^[0-9]{4}$"
|
||||||
|
},
|
||||||
|
"erreurCorrectif": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"situationPartielle": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"declarant": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"nom": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"nomNaissance": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"prenoms": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
84
spec/fixtures/cassettes/api_particulier/success/avis_imposition.yml
vendored
Normal file
84
spec/fixtures/cassettes/api_particulier/success/avis_imposition.yml
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
---
|
||||||
|
http_interactions:
|
||||||
|
- request:
|
||||||
|
method: get
|
||||||
|
uri: https://particulier.api.gouv.fr/api/v2/avis-imposition?numeroFiscal=2097699999077&referenceAvis=2097699999077
|
||||||
|
body:
|
||||||
|
encoding: US-ASCII
|
||||||
|
string: ''
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- demarches-simplifiees.fr
|
||||||
|
Accept:
|
||||||
|
- application/json
|
||||||
|
X-Api-Key:
|
||||||
|
- d7e9c9f4c3ca00caadde31f50fd4521a
|
||||||
|
Expect:
|
||||||
|
- ''
|
||||||
|
response:
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
headers:
|
||||||
|
Date:
|
||||||
|
- Tue, 16 Mar 2021 17:01:19 GMT
|
||||||
|
Content-Type:
|
||||||
|
- application/json; charset=utf-8
|
||||||
|
Content-Length:
|
||||||
|
- '747'
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Keep-Alive:
|
||||||
|
- timeout=5
|
||||||
|
Access-Control-Allow-Credentials:
|
||||||
|
- 'true'
|
||||||
|
Access-Control-Expose-Headers:
|
||||||
|
- Range,Content-Range,X-Content-Range,X-API-Key
|
||||||
|
Etag:
|
||||||
|
- W/"2eb-A0NiRd+gbJKIAT0y4tR4j9tjXb0"
|
||||||
|
Server:
|
||||||
|
- nginx
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- max-age=15552000
|
||||||
|
- max-age=15552000
|
||||||
|
Vary:
|
||||||
|
- Origin, Accept
|
||||||
|
X-Gravitee-Request-Id:
|
||||||
|
- 7bfb7f99-ac2d-4443-bb7f-99ac2d0443c5
|
||||||
|
X-Gravitee-Transaction-Id:
|
||||||
|
- f5dca8b3-2ab7-4c9a-9ca8-b32ab70c9a2b
|
||||||
|
body:
|
||||||
|
encoding: ASCII-8BIT
|
||||||
|
string: '{
|
||||||
|
"declarant1": {
|
||||||
|
"nom": "FERRI",
|
||||||
|
"nomNaissance": "FERRI",
|
||||||
|
"prenoms": "Karine",
|
||||||
|
"dateNaissance": "12/08/1978"
|
||||||
|
},
|
||||||
|
"declarant2": {
|
||||||
|
"nom": "",
|
||||||
|
"nomNaissance": "",
|
||||||
|
"prenoms": "",
|
||||||
|
"dateNaissance": "12/08/1978"
|
||||||
|
},
|
||||||
|
"dateRecouvrement": "09/10/2020",
|
||||||
|
"dateEtablissement": "07/07/2020",
|
||||||
|
"nombreParts": 1,
|
||||||
|
"situationFamille": "Célibataire",
|
||||||
|
"nombrePersonnesCharge": 0,
|
||||||
|
"revenuBrutGlobal": 38814,
|
||||||
|
"revenuImposable": 38814,
|
||||||
|
"impotRevenuNetAvantCorrections": 38814,
|
||||||
|
"montantImpot": 38814,
|
||||||
|
"revenuFiscalReference": 38814,
|
||||||
|
"foyerFiscal": {
|
||||||
|
"annee": 2020,
|
||||||
|
"adresse": "13 rue de la Plage 97615 Pamanzi"
|
||||||
|
},
|
||||||
|
"anneeImpots": "2020",
|
||||||
|
"anneeRevenus": "2020",
|
||||||
|
"situationPartielle": "SUP DOM"
|
||||||
|
}'
|
||||||
|
recorded_at: Tue, 16 Mar 2021 17:01:18 GMT
|
||||||
|
recorded_with: VCR 6.0.0
|
84
spec/fixtures/cassettes/api_particulier/success/avis_imposition_invalid.yml
vendored
Normal file
84
spec/fixtures/cassettes/api_particulier/success/avis_imposition_invalid.yml
vendored
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
---
|
||||||
|
http_interactions:
|
||||||
|
- request:
|
||||||
|
method: get
|
||||||
|
uri: https://particulier.api.gouv.fr/api/v2/avis-imposition?numeroFiscal=2097699999077&referenceAvis=2097699999077
|
||||||
|
body:
|
||||||
|
encoding: US-ASCII
|
||||||
|
string: ''
|
||||||
|
headers:
|
||||||
|
User-Agent:
|
||||||
|
- demarches-simplifiees.fr
|
||||||
|
Accept:
|
||||||
|
- application/json
|
||||||
|
X-Api-Key:
|
||||||
|
- d7e9c9f4c3ca00caadde31f50fd4521a
|
||||||
|
Expect:
|
||||||
|
- ''
|
||||||
|
response:
|
||||||
|
status:
|
||||||
|
code: 200
|
||||||
|
message: OK
|
||||||
|
headers:
|
||||||
|
Date:
|
||||||
|
- Tue, 16 Mar 2021 17:01:19 GMT
|
||||||
|
Content-Type:
|
||||||
|
- application/json; charset=utf-8
|
||||||
|
Content-Length:
|
||||||
|
- '747'
|
||||||
|
Connection:
|
||||||
|
- keep-alive
|
||||||
|
Keep-Alive:
|
||||||
|
- timeout=5
|
||||||
|
Access-Control-Allow-Credentials:
|
||||||
|
- 'true'
|
||||||
|
Access-Control-Expose-Headers:
|
||||||
|
- Range,Content-Range,X-Content-Range,X-API-Key
|
||||||
|
Etag:
|
||||||
|
- W/"2eb-A0NiRd+gbJKIAT0y4tR4j9tjXb0"
|
||||||
|
Server:
|
||||||
|
- nginx
|
||||||
|
Strict-Transport-Security:
|
||||||
|
- max-age=15552000
|
||||||
|
- max-age=15552000
|
||||||
|
Vary:
|
||||||
|
- Origin, Accept
|
||||||
|
X-Gravitee-Request-Id:
|
||||||
|
- 7bfb7f99-ac2d-4443-bb7f-99ac2d0443c5
|
||||||
|
X-Gravitee-Transaction-Id:
|
||||||
|
- f5dca8b3-2ab7-4c9a-9ca8-b32ab70c9a2b
|
||||||
|
body:
|
||||||
|
encoding: ASCII-8BIT
|
||||||
|
string: '{
|
||||||
|
"declarant1": {
|
||||||
|
"nom": "FERRI",
|
||||||
|
"nomNaissance": "FERRI",
|
||||||
|
"prenoms": "Karine",
|
||||||
|
"dateNaissance": "12/08/1978"
|
||||||
|
},
|
||||||
|
"declarant2": {
|
||||||
|
"nom": "",
|
||||||
|
"nomNaissance": "",
|
||||||
|
"prenoms": "",
|
||||||
|
"dateNaissance": "12/08/1978"
|
||||||
|
},
|
||||||
|
"dateRecouvrement": "09/10/2020",
|
||||||
|
"dateEtablissement": "07/07/2020",
|
||||||
|
"nombreParts": 1,
|
||||||
|
"situationFamille": "Célibataire",
|
||||||
|
"nombrePersonnesCharge": 0,
|
||||||
|
"revenuBrutGlobal": 38814,
|
||||||
|
"revenuImposable": 38814,
|
||||||
|
"impotRevenuNetAvantCorrections": 38814,
|
||||||
|
"montantImpot": 38814,
|
||||||
|
"revenuFiscalReference": 38814,
|
||||||
|
"foyerFiscal": {
|
||||||
|
"annee": 2020,
|
||||||
|
"adresse": "13 rue de la Plage 97615 Pamanzi"
|
||||||
|
},
|
||||||
|
"anneeImpots": "99",
|
||||||
|
"anneeRevenus": "2020",
|
||||||
|
"situationPartielle": "SUP DOM"
|
||||||
|
}'
|
||||||
|
recorded_at: Tue, 16 Mar 2021 17:01:18 GMT
|
||||||
|
recorded_with: VCR 6.0.0
|
33
spec/fixtures/files/api_particulier/avis_imposition.json
vendored
Normal file
33
spec/fixtures/files/api_particulier/avis_imposition.json
vendored
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
{
|
||||||
|
"avis_imposition": {
|
||||||
|
"declarant1": {
|
||||||
|
"nom": "FERRI",
|
||||||
|
"nomNaissance": "FERRI",
|
||||||
|
"prenoms": "Karine",
|
||||||
|
"dateNaissance": "12/08/1978"
|
||||||
|
},
|
||||||
|
"declarant2": {
|
||||||
|
"nom": "",
|
||||||
|
"nomNaissance": "",
|
||||||
|
"prenoms": "",
|
||||||
|
"dateNaissance": "12/08/1978"
|
||||||
|
},
|
||||||
|
"dateRecouvrement": "09/10/2020",
|
||||||
|
"dateEtablissement": "07/07/2020",
|
||||||
|
"nombreParts": 1,
|
||||||
|
"situationFamille": "Célibataire",
|
||||||
|
"nombrePersonnesCharge": 0,
|
||||||
|
"revenuBrutGlobal": 38814,
|
||||||
|
"revenuImposable": 38814,
|
||||||
|
"impotRevenuNetAvantCorrections": 38814,
|
||||||
|
"montantImpot": 38814,
|
||||||
|
"revenuFiscalReference": 38814,
|
||||||
|
"anneeImpots": "2020",
|
||||||
|
"anneeRevenus": "2020",
|
||||||
|
"situationPartielle": "SUP DOM"
|
||||||
|
},
|
||||||
|
"foyer_fiscal": {
|
||||||
|
"annee": 2020,
|
||||||
|
"adresse": "13 rue de la Plage 97615 Pamanzi"
|
||||||
|
}
|
||||||
|
}
|
79
spec/lib/api_particulier/dgfip_adapter_spec.rb
Normal file
79
spec/lib/api_particulier/dgfip_adapter_spec.rb
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
describe APIParticulier::DgfipAdapter do
|
||||||
|
let(:adapter) { described_class.new(api_particulier_token, numero_fiscal, reference_avis, requested_sources) }
|
||||||
|
|
||||||
|
before { stub_const('API_PARTICULIER_URL', 'https://particulier.api.gouv.fr/api') }
|
||||||
|
|
||||||
|
describe '#to_params' do
|
||||||
|
let(:api_particulier_token) { '29eb50b65f64e8e00c0847a8bbcbd150e1f847' }
|
||||||
|
let(:numero_fiscal) { '2097699999077' }
|
||||||
|
let(:reference_avis) { '2097699999077' }
|
||||||
|
|
||||||
|
subject { VCR.use_cassette(cassette) { adapter.to_params } }
|
||||||
|
|
||||||
|
context 'when the api answer is valid' do
|
||||||
|
let(:cassette) { 'api_particulier/success/avis_imposition' }
|
||||||
|
|
||||||
|
context 'when the token has all the dgfip scopes' do
|
||||||
|
context 'and all the sources are requested' do
|
||||||
|
let(:requested_sources) do
|
||||||
|
{
|
||||||
|
'dgfip' => {
|
||||||
|
'avis_imposition' => [
|
||||||
|
{ 'declarant1' => ['dateNaissance', 'nom', 'nomNaissance', 'prenoms'] },
|
||||||
|
{ 'declarant2' => ['dateNaissance', 'nom', 'nomNaissance', 'prenoms'] },
|
||||||
|
'anneeImpots',
|
||||||
|
'anneeRevenus',
|
||||||
|
'dateEtablissement',
|
||||||
|
'dateRecouvrement',
|
||||||
|
'erreurCorrectif',
|
||||||
|
'impotRevenuNetAvantCorrections',
|
||||||
|
'montantImpot',
|
||||||
|
'nombreParts',
|
||||||
|
'nombrePersonnesCharge',
|
||||||
|
'revenuBrutGlobal',
|
||||||
|
'revenuFiscalReference',
|
||||||
|
'revenuImposable',
|
||||||
|
'situationFamille',
|
||||||
|
'situationPartielle'
|
||||||
|
],
|
||||||
|
'foyer_fiscal' => ['adresse', 'annee']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:result) { JSON.parse(File.read('spec/fixtures/files/api_particulier/avis_imposition.json')) }
|
||||||
|
|
||||||
|
it { is_expected.to eq(result) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when no sources is requested' do
|
||||||
|
let(:requested_sources) { {} }
|
||||||
|
|
||||||
|
it { is_expected.to eq({}) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a declarer name is requested' do
|
||||||
|
let(:requested_sources) { { 'dgfip' => { 'avis_imposition' => [{ 'declarant1' => ['nom'] }] } } }
|
||||||
|
|
||||||
|
it { is_expected.to eq('avis_imposition' => { 'declarant1' => { 'nom' => 'FERRI' } }) }
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when a revenue is requested' do
|
||||||
|
let(:requested_sources) { { 'dgfip' => { 'avis_imposition' => ['revenuBrutGlobal'] } } }
|
||||||
|
|
||||||
|
it { is_expected.to eq('avis_imposition' => { 'revenuBrutGlobal' => 38814 }) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the api answer is invalid' do
|
||||||
|
let(:cassette) { 'api_particulier/success/avis_imposition_invalid' }
|
||||||
|
|
||||||
|
context 'when no sources is requested' do
|
||||||
|
let(:requested_sources) { {} }
|
||||||
|
|
||||||
|
it { expect { subject }.to raise_error(APIParticulier::DgfipAdapter::InvalidSchemaError) }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue