toulouse_axel: get invoices (#38230)

This commit is contained in:
Lauréline Guérin 2019-12-06 13:24:31 +01:00
parent cc85bdc81c
commit 18be45d1e8
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
8 changed files with 537 additions and 13 deletions

View File

@ -40,6 +40,33 @@ def test_link(conn, user):
pprint.pprint(res)
print('\n')
print("Get invoices")
url = conn + '/invoices?NameID=%s' % name_id
resp = requests.get(url)
resp.raise_for_status()
res = resp.json()
assert res['err'] == 0
pprint.pprint(res)
print('\n')
data = res
for invoice in data['data']:
print("GET invoice info")
url = conn + '/invoice/%s?NameID=%s' % (invoice['id'], name_id)
resp = requests.get(url)
resp.raise_for_status()
res = resp.json()
assert res['err'] == 0
pprint.pprint(res)
print('\n')
if res['data']['has_pdf']:
print("GET invoice PDF")
url = conn + '/invoice/%s/pdf?NameID=%s' % (invoice['id'], name_id)
resp = requests.get(url)
resp.raise_for_status()
print('\n')
print("Deleting link")
url = conn + '/unlink?NameID=%s' % name_id
resp = requests.post(url)

View File

@ -14,6 +14,7 @@
# 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 base64
import copy
import datetime
import logging
@ -23,6 +24,7 @@ import xml.etree.ElementTree as ET
from django.db import models
from django.http import HttpResponse
from django.utils.encoding import force_text
from django.utils.translation import ugettext_lazy as _
@ -70,10 +72,10 @@ def xml_schema_converter(name, root_element):
class Operation(object):
def __init__(self, operation):
def __init__(self, operation, prefix='Dui/'):
self.operation = operation
self.request_converter = xml_schema_converter('Dui/Q_%s.xsd' % operation, 'PORTAIL')
self.response_converter = xml_schema_converter('Dui/R_%s.xsd' % operation, 'PORTAILSERVICE')
self.request_converter = xml_schema_converter('%sQ_%s.xsd' % (prefix, operation), 'PORTAIL')
self.response_converter = xml_schema_converter('%sR_%s.xsd' % (prefix, operation), 'PORTAILSERVICE')
self.name = re.sub(
'(.?)([A-Z])',
lambda s: s.group(1) + ('-' if s.group(1) else '') + s.group(2).lower(),
@ -109,6 +111,8 @@ class Operation(object):
ref_verif_dui = Operation('RefVerifDui')
ref_famille_dui = Operation('RefFamilleDui')
ref_facture_a_payer = Operation('RefFactureAPayer')
ref_facture_pdf = Operation('RefFacturePDF', prefix='')
class ToulouseAxel(BaseResource):
@ -169,6 +173,12 @@ class ToulouseAxel(BaseResource):
raise APIError('Data conflict', err='conflict')
return {'link': link.pk, 'created': created, 'dui': link.dui}
def get_link(self, name_id, error_status=200):
try:
return self.link_set.get(name_id=name_id)
except Link.DoesNotExist:
raise APIError('Person not found', err='not-found', http_status=error_status)
@endpoint(
description=_('Delete link between user and Toulouse Axel'),
methods=['post'],
@ -177,21 +187,13 @@ class ToulouseAxel(BaseResource):
'NameID': {'description': _('Publik ID')},
})
def unlink(self, request, NameID):
try:
link = self.link_set.get(name_id=NameID)
except Link.DoesNotExist:
raise APIError('Person not found', err='not-found')
link = self.get_link(NameID)
link_id = link.pk
link.delete()
return {'link': link_id, 'deleted': True, 'dui': link.dui}
def get_family_data(self, name_id):
try:
link = self.link_set.get(name_id=name_id)
except Link.DoesNotExist:
raise APIError('Person not found', err='not-found')
link = self.get_link(name_id)
try:
result = ref_famille_dui(self, {'PORTAIL': {'DUI': {'IDDUI': link.dui}}})
except AxelError as e:
@ -224,6 +226,91 @@ class ToulouseAxel(BaseResource):
raise APIError('Child not found', err='not-found')
def get_invoices(self, name_id, error_status=200):
link = self.get_link(name_id, error_status=error_status)
try:
result = ref_facture_a_payer(self, {'PORTAIL': {'DUI': {'IDDUI': link.dui}}})
except AxelError as e:
raise APIError('Axel error: %s' % e, err='error', http_status=error_status)
data = result['DATA']['PORTAIL']['DUI']
result = []
for facture in data.get('FACTURES', []):
result.append({
'id': facture['IDFACTURE'],
'label': facture['LIBELLE'],
'amount': facture['RESTEAPAYER'],
'total_amount': facture['MONTANTTOTAL'],
'online_payment': True,
'created': facture['DATEEMISSION'],
'pay_limit_date': facture['DATEECHEANCE'],
'has_pdf': True if facture['EXISTEPDF'] == '1' else False,
'paid': False,
'vendor': {'toulouse-axel': facture},
})
return result
def get_invoice(self, name_id, invoice_id, error_status=200):
invoices_data = self.get_invoices(name_id, error_status=error_status)
for invoice in invoices_data:
if str(invoice['id']) == invoice_id:
return invoice
@endpoint(
description=_("Get invoices to pay"),
perm='can_access',
parameters={
'NameID': {'description': _('Publik ID')},
})
def invoices(self, request, NameID):
invoices_data = self.get_invoices(NameID)
return {'data': invoices_data}
@endpoint(
perm='can_access',
pattern=r'^(?P<invoice_id>\w+)/?$',
description=_('Get invoice details'),
parameters={
'NameID': {'description': _('Publik ID')},
'invoice_id': {'description': _('Invoice identifier')}
})
def invoice(self, request, invoice_id, NameID):
invoice = self.get_invoice(NameID, invoice_id)
if invoice is None:
raise APIError('Invoice not found', err='not-found')
return {'data': invoice}
@endpoint(
name='invoice',
perm='can_access',
pattern=r'^(?P<invoice_id>\w+)/pdf/?$',
description=_('Get invoice as a PDF file'),
parameters={
'NameID': {'description': _('Publik ID')},
'invoice_id': {'description': _('Invoice identifier')}
})
def invoice_pdf(self, request, invoice_id, NameID):
# check that invoice is related to current user
invoice = self.get_invoice(NameID, invoice_id, error_status=404)
if invoice is None:
raise APIError('Invoice not found', err='not-found', http_status=404)
# check that PDF is available
if not invoice['has_pdf']:
raise APIError('PDF not available', err='not-available', http_status=404)
try:
result = ref_facture_pdf(self, {'PORTAIL': {'FACTUREPDF': {'IDFACTURE': int(invoice_id)}}})
except AxelError as e:
raise APIError('Axel error: %s' % e, err='error', http_status=404)
b64content = base64.b64decode(result['DATA']['PORTAIL']['PDF']['@FILE'])
if not b64content:
raise APIError('PDF error', err='error', http_status=404)
response = HttpResponse(content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="%s.pdf"' % invoice_id
response.write(b64content)
return response
class Link(models.Model):
resource = models.ForeignKey(ToulouseAxel, on_delete=models.CASCADE)

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:all="urn:AllAxelTypes">
<xsd:import schemaLocation="../AllAxelTypes.xsd" namespace="urn:AllAxelTypes" />
<xsd:complexType name="DUIType">
<xsd:sequence>
<xsd:element ref="IDDUI" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PORTAILType">
<xsd:sequence>
<xsd:element ref="DUI"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="IDDUI" type="all:IDENTREQUIREDType"/>
<xsd:element name="DUI" type="DUIType"/>
<xsd:element name="PORTAIL" type="PORTAILType"/>
</xsd:schema>

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:all="urn:AllAxelTypes">
<xsd:import schemaLocation="../AllAxelTypes.xsd" namespace="urn:AllAxelTypes" />
<xsd:redefine schemaLocation="../R_ShemaResultat.xsd">
<xsd:simpleType name="TYPEType">
<xsd:restriction base="TYPEType">
<xsd:enumeration value="RefFactureAPayer" />
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="PORTAILType">
<xsd:complexContent>
<xsd:extension base="PORTAILType">
<xsd:sequence>
<xsd:element ref="DUI" minOccurs="0"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:redefine>
<xsd:simpleType name="CHOIXType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="" />
<xsd:enumeration value="1" />
<xsd:enumeration value="0" />
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="FACTURESType">
<xsd:sequence>
<xsd:element ref="NUMFACTURE" />
<xsd:element ref="DATEEMISSION" />
<xsd:element ref="DATEECHEANCE" />
<xsd:element ref="LIBELLE" />
<xsd:element ref="IDFACTURATION" />
<xsd:element ref="MONTANTTOTAL" />
<xsd:element ref="RESTEAPAYER" />
<xsd:element ref="IDREGIE" />
<xsd:element ref="EXISTEPDF" />
<xsd:element ref="IDFACTURE" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="DUIType">
<xsd:sequence>
<xsd:element ref="IDDUI" />
<xsd:element ref="CHOIXDEMAT" />
<xsd:element ref="NBFACTURES" />
<xsd:element ref="FACTURES" minOccurs="0" maxOccurs="unbounded"/>
</xsd:sequence>
</xsd:complexType>
<xsd:element name="IDDUI" type="all:IDENTType"/>
<xsd:element name="CHOIXDEMAT" type="CHOIXType"/>
<xsd:element name="NBFACTURES" type="all:unsignedInt-or-empty"/>
<xsd:element name="FACTURES" type="FACTURESType"/>
<xsd:element name="NUMFACTURE" type="xsd:unsignedInt"/>
<xsd:element name="DATEEMISSION" type="all:DATEREQUIREDType"/>
<xsd:element name="DATEECHEANCE" type="all:DATEREQUIREDType"/>
<xsd:element name="LIBELLE" type="xsd:string"/>
<xsd:element name="IDFACTURATION" type="all:IDType"/>
<xsd:element name="MONTANTTOTAL" type="all:MONTANTType"/>
<xsd:element name="RESTEAPAYER" type="all:MONTANTREQUIREDType"/>
<xsd:element name="IDREGIE" type="all:IDType"/>
<xsd:element name="EXISTEPDF" type="CHOIXType"/>
<xsd:element name="IDFACTURE" type="xsd:unsignedInt"/>
<xsd:element name="DUI" type="DUIType" />
</xsd:schema>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:complexType name="FACTUREPDFType">
<xsd:sequence>
<xsd:element ref="IDFACTURE" />
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PORTAILType">
<xsd:sequence>
<xsd:element ref="FACTUREPDF" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="IDFACTURE" type="xsd:positiveInteger"/>
<xsd:element name="FACTUREPDF" type="FACTUREPDFType"/>
<xsd:element name="PORTAIL" type="PORTAILType"/>
</xsd:schema>

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<xsd:redefine schemaLocation="./R_ShemaResultat.xsd">
<xsd:simpleType name="TYPEType">
<xsd:restriction base="TYPEType">
<xsd:enumeration value="RefFacturePDF" />
</xsd:restriction>
</xsd:simpleType>
<xsd:complexType name="PORTAILType">
<xsd:complexContent>
<xsd:extension base="PORTAILType">
<xsd:sequence>
<xsd:element ref="PDF" minOccurs="0"/>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:redefine>
<xsd:complexType name="PDFType">
<xsd:attribute ref="FILE"/>
</xsd:complexType>
<xsd:attribute name="FILE" type="xsd:string"/>
<xsd:element name="PDF" type="PDFType"/>
</xsd:schema>

View File

@ -0,0 +1,31 @@
<PORTAIL>
<DUI>
<IDDUI>XXX</IDDUI>
<CHOIXDEMAT>0</CHOIXDEMAT>
<NBFACTURES>2</NBFACTURES>
<FACTURES>
<NUMFACTURE>42</NUMFACTURE>
<DATEEMISSION>12/11/2019</DATEEMISSION>
<DATEECHEANCE>04/12/2019</DATEECHEANCE>
<LIBELLE>PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019</LIBELLE>
<IDFACTURATION>4242-35AA</IDFACTURATION>
<MONTANTTOTAL>44.94</MONTANTTOTAL>
<RESTEAPAYER>44.94</RESTEAPAYER>
<IDREGIE>MAREGIE</IDREGIE>
<EXISTEPDF>1</EXISTEPDF>
<IDFACTURE>42</IDFACTURE>
</FACTURES>
<FACTURES>
<NUMFACTURE>43</NUMFACTURE>
<DATEEMISSION>12/12/2019</DATEEMISSION>
<DATEECHEANCE>04/01/2020</DATEECHEANCE>
<LIBELLE>PRESTATIONS PERISCOLAIRES NOVEMBRE 2019</LIBELLE>
<IDFACTURATION>4243-35AA</IDFACTURATION>
<MONTANTTOTAL>44.94</MONTANTTOTAL>
<RESTEAPAYER>44.94</RESTEAPAYER>
<IDREGIE>MAREGIE</IDREGIE>
<EXISTEPDF>0</EXISTEPDF>
<IDFACTURE>43</IDFACTURE>
</FACTURES>
</DUI>
</PORTAIL>

View File

@ -25,6 +25,8 @@ from passerelle.contrib.toulouse_axel.models import (
Link,
ToulouseAxel,
ref_famille_dui,
ref_facture_a_payer,
ref_facture_pdf,
ref_verif_dui,
)
import utils
@ -131,6 +133,36 @@ def test_operation_ref_famille_dui(resource, content):
})
@pytest.mark.parametrize('content', [
'<PORTAIL><DUI/></PORTAIL>',
])
def test_operation_ref_facture_a_payer(resource, content):
with mock_getdata(content, 'RefFactureAPayer'):
with pytest.raises(AxelError):
ref_facture_a_payer(resource, {
'PORTAIL': {
'DUI': {
'IDDUI': 'XXX',
}
}
})
@pytest.mark.parametrize('content', [
"<PORTAIL><PDF FOO='BAR'></PDF></PORTAIL>",
])
def test_operation_ref_facture_pdf(resource, content):
with mock_getdata(content, 'RefFacturePDF'):
with pytest.raises(AxelError):
ref_facture_pdf(resource, {
'PORTAIL': {
'FACTUREPDF': {
'IDFACTURE': 42,
}
}
})
def test_link_endpoint_nameid_empty(app, resource, params):
resp = app.post_json('/toulouse-axel/test/link?NameID=', params=params, status=400)
assert resp.json['err_desc'] == "NameID is empty"
@ -306,3 +338,203 @@ def test_child_info_endpoint(app, resource):
'SANITAIRE',
'SEXE',
])
def test_invoices_endpoint_axel_error(app, resource):
Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
with mock.patch('passerelle.contrib.toulouse_axel.models.ref_facture_a_payer') as operation:
operation.side_effect = AxelError('FooBar')
resp = app.get('/toulouse-axel/test/invoices?NameID=yyy')
assert resp.json['err_desc'] == "Axel error: FooBar"
def test_invoices_endpoint_no_result(app, resource):
resp = app.get('/toulouse-axel/test/invoices?NameID=yyy')
assert resp.json['err_desc'] == "Person not found"
def test_invoices_endpoint_no_invoices(app, resource):
Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
content = '''<PORTAIL>
<DUI>
<IDDUI>XXX</IDDUI>
<CHOIXDEMAT>0</CHOIXDEMAT>
<NBFACTURES>0</NBFACTURES>
</DUI>
</PORTAIL>'''
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/invoices?NameID=yyy')
assert resp.json['err'] == 0
assert resp.json['data'] == []
def test_invoices_endpoint(app, resource):
Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
with open(filepath) as xml:
content = xml.read()
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/invoices?NameID=yyy')
assert resp.json['err'] == 0
assert resp.json['data'] == [
{
'id': 42,
'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
'amount': '44.94',
'total_amount': '44.94',
'online_payment': True,
'created': '2019-11-12',
'pay_limit_date': '2019-12-04',
'has_pdf': True,
'paid': False,
'vendor': {
'toulouse-axel': {
'IDFACTURATION': '4242-35AA',
'IDFACTURE': 42,
'IDREGIE': 'MAREGIE',
'DATEECHEANCE': '2019-12-04',
'DATEEMISSION': '2019-11-12',
'EXISTEPDF': '1',
'LIBELLE': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
'MONTANTTOTAL': '44.94',
'NUMFACTURE': 42,
'RESTEAPAYER': '44.94',
}
}
},
{
'id': 43,
'label': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019',
'amount': '44.94',
'total_amount': '44.94',
'online_payment': True,
'created': '2019-12-12',
'pay_limit_date': '2020-01-04',
'has_pdf': False,
'paid': False,
'vendor': {
'toulouse-axel': {
'IDFACTURATION': '4243-35AA',
'IDFACTURE': 43,
'DATEECHEANCE': '2020-01-04',
'DATEEMISSION': '2019-12-12',
'EXISTEPDF': '0',
'IDREGIE': 'MAREGIE',
'LIBELLE': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019',
'MONTANTTOTAL': '44.94',
'NUMFACTURE': 43,
'RESTEAPAYER': '44.94',
}
}
}
]
def test_invoice_endpoint_axel_error(app, resource):
Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
with mock.patch('passerelle.contrib.toulouse_axel.models.ref_facture_a_payer') as operation:
operation.side_effect = AxelError('FooBar')
resp = app.get('/toulouse-axel/test/invoice/42?NameID=yyy')
assert resp.json['err_desc'] == "Axel error: FooBar"
def test_invoice_endpoint_no_result(app, resource):
resp = app.get('/toulouse-axel/test/invoice/42?NameID=yyy')
assert resp.json['err_desc'] == "Person not found"
Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
with open(filepath) as xml:
content = xml.read()
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/invoice/35?NameID=yyy')
assert resp.json['err_desc'] == "Invoice not found"
def test_invoice_endpoint(app, resource):
Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
with open(filepath) as xml:
content = xml.read()
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/invoice/42?NameID=yyy')
assert resp.json['err'] == 0
assert resp.json['data'] == {
'id': 42,
'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
'amount': '44.94',
'total_amount': '44.94',
'online_payment': True,
'created': '2019-11-12',
'pay_limit_date': '2019-12-04',
'has_pdf': True,
'paid': False,
'vendor': {
'toulouse-axel': {
'IDFACTURATION': '4242-35AA',
'IDFACTURE': 42,
'IDREGIE': 'MAREGIE',
'DATEECHEANCE': '2019-12-04',
'DATEEMISSION': '2019-11-12',
'EXISTEPDF': '1',
'LIBELLE': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
'MONTANTTOTAL': '44.94',
'NUMFACTURE': 42,
'RESTEAPAYER': '44.94',
}
}
}
def test_invoice_pdf_endpoint_axel_error(app, resource):
Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
with mock.patch('passerelle.contrib.toulouse_axel.models.ref_facture_a_payer') as operation:
operation.side_effect = AxelError('FooBar')
resp = app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "Axel error: FooBar"
filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
with open(filepath) as xml:
content = xml.read()
with mock_getdata(content, 'RefFactureAPayer'):
with mock.patch('passerelle.contrib.toulouse_axel.models.ref_facture_pdf') as operation:
operation.side_effect = AxelError('FooBar')
resp = app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "Axel error: FooBar"
def test_invoice_pdf_endpoint_no_result(app, resource):
resp = app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "Person not found"
Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
with open(filepath) as xml:
content = xml.read()
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/invoice/35/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "Invoice not found"
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/invoice/43/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "PDF not available"
pdf_content = '''<PORTAIL>
<PDF FILE=''></PDF>
</PORTAIL>'''
with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_invoice') as invoice:
invoice.return_value = {'has_pdf': True}
with mock_getdata(pdf_content, 'RefFacturePDF'):
resp = app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "PDF error"
def test_invoice_pdf_endpoint(app, resource):
Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
pdf_content = '''<PORTAIL>
<PDF FILE='aGVsbG8gd29ybGQ='></PDF>
</PORTAIL>'''
with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_invoice') as invoice:
invoice.return_value = {'has_pdf': True}
with mock_getdata(pdf_content, 'RefFacturePDF'):
app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy')