Projet

Général

Profil

0002-toulouse_axel-get-invoices-38230.patch

Lauréline Guérin, 06 décembre 2019 16:41

Télécharger (26,2 ko)

Voir les différences:

Subject: [PATCH 2/2] toulouse_axel: get invoices (#38230)

 functests/toulouse_axel/test_toulouse_axel.py |  27 ++
 passerelle/contrib/toulouse_axel/models.py    | 113 ++++++++-
 .../xsd/Dui/Q_RefFactureAPayer.xsd            |  23 ++
 .../xsd/Dui/R_RefFactureAPayer.xsd            |  74 ++++++
 .../toulouse_axel/xsd/Q_RefFacturePDF.xsd     |  20 ++
 .../toulouse_axel/xsd/R_RefFacturePDF.xsd     |  30 +++
 tests/data/toulouse_axel/invoices.xml         |  31 +++
 tests/test_toulouse_axel.py                   | 232 ++++++++++++++++++
 8 files changed, 537 insertions(+), 13 deletions(-)
 create mode 100644 passerelle/contrib/toulouse_axel/xsd/Dui/Q_RefFactureAPayer.xsd
 create mode 100644 passerelle/contrib/toulouse_axel/xsd/Dui/R_RefFactureAPayer.xsd
 create mode 100644 passerelle/contrib/toulouse_axel/xsd/Q_RefFacturePDF.xsd
 create mode 100644 passerelle/contrib/toulouse_axel/xsd/R_RefFacturePDF.xsd
 create mode 100644 tests/data/toulouse_axel/invoices.xml
functests/toulouse_axel/test_toulouse_axel.py
40 40
        pprint.pprint(res)
41 41
        print('\n')
42 42

  
43
    print("Get invoices")
44
    url = conn + '/invoices?NameID=%s' % name_id
45
    resp = requests.get(url)
46
    resp.raise_for_status()
47
    res = resp.json()
48
    assert res['err'] == 0
49
    pprint.pprint(res)
50
    print('\n')
51

  
52
    data = res
53
    for invoice in data['data']:
54
        print("GET invoice info")
55
        url = conn + '/invoice/%s?NameID=%s' % (invoice['id'], name_id)
56
        resp = requests.get(url)
57
        resp.raise_for_status()
58
        res = resp.json()
59
        assert res['err'] == 0
60
        pprint.pprint(res)
61
        print('\n')
62

  
63
        if res['data']['has_pdf']:
64
            print("GET invoice PDF")
65
            url = conn + '/invoice/%s/pdf?NameID=%s' % (invoice['id'], name_id)
66
            resp = requests.get(url)
67
            resp.raise_for_status()
68
            print('\n')
69

  
43 70
    print("Deleting link")
44 71
    url = conn + '/unlink?NameID=%s' % name_id
45 72
    resp = requests.post(url)
passerelle/contrib/toulouse_axel/models.py
14 14
# You should have received a copy of the GNU Affero General Public License
15 15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import base64
17 18
import copy
18 19
import datetime
19 20
import logging
......
23 24

  
24 25

  
25 26
from django.db import models
27
from django.http import HttpResponse
26 28
from django.utils.encoding import force_text
27 29
from django.utils.translation import ugettext_lazy as _
28 30

  
......
70 72

  
71 73

  
72 74
class Operation(object):
73
    def __init__(self, operation):
75
    def __init__(self, operation, prefix='Dui/'):
74 76
        self.operation = operation
75
        self.request_converter = xml_schema_converter('Dui/Q_%s.xsd' % operation, 'PORTAIL')
76
        self.response_converter = xml_schema_converter('Dui/R_%s.xsd' % operation, 'PORTAILSERVICE')
77
        self.request_converter = xml_schema_converter('%sQ_%s.xsd' % (prefix, operation), 'PORTAIL')
78
        self.response_converter = xml_schema_converter('%sR_%s.xsd' % (prefix, operation), 'PORTAILSERVICE')
77 79
        self.name = re.sub(
78 80
            '(.?)([A-Z])',
79 81
            lambda s: s.group(1) + ('-' if s.group(1) else '') + s.group(2).lower(),
......
109 111

  
110 112
ref_verif_dui = Operation('RefVerifDui')
111 113
ref_famille_dui = Operation('RefFamilleDui')
114
ref_facture_a_payer = Operation('RefFactureAPayer')
115
ref_facture_pdf = Operation('RefFacturePDF', prefix='')
112 116

  
113 117

  
114 118
class ToulouseAxel(BaseResource):
......
169 173
            raise APIError('Data conflict', err='conflict')
170 174
        return {'link': link.pk, 'created': created, 'dui': link.dui}
171 175

  
176
    def get_link(self, name_id, error_status=200):
177
        try:
178
            return self.link_set.get(name_id=name_id)
179
        except Link.DoesNotExist:
180
            raise APIError('Person not found', err='not-found', http_status=error_status)
181

  
172 182
    @endpoint(
173 183
        description=_('Delete link between user and Toulouse Axel'),
174 184
        methods=['post'],
......
177 187
            'NameID': {'description': _('Publik ID')},
178 188
        })
179 189
    def unlink(self, request, NameID):
180
        try:
181
            link = self.link_set.get(name_id=NameID)
182
        except Link.DoesNotExist:
183
            raise APIError('Person not found', err='not-found')
184

  
190
        link = self.get_link(NameID)
185 191
        link_id = link.pk
186 192
        link.delete()
187 193
        return {'link': link_id, 'deleted': True, 'dui': link.dui}
188 194

  
189 195
    def get_family_data(self, name_id):
190
        try:
191
            link = self.link_set.get(name_id=name_id)
192
        except Link.DoesNotExist:
193
            raise APIError('Person not found', err='not-found')
194

  
196
        link = self.get_link(name_id)
195 197
        try:
196 198
            result = ref_famille_dui(self, {'PORTAIL': {'DUI': {'IDDUI': link.dui}}})
197 199
        except AxelError as e:
......
224 226

  
225 227
        raise APIError('Child not found', err='not-found')
226 228

  
229
    def get_invoices(self, name_id, error_status=200):
230
        link = self.get_link(name_id, error_status=error_status)
231
        try:
232
            result = ref_facture_a_payer(self, {'PORTAIL': {'DUI': {'IDDUI': link.dui}}})
233
        except AxelError as e:
234
            raise APIError('Axel error: %s' % e, err='error', http_status=error_status)
235
        data = result['DATA']['PORTAIL']['DUI']
236
        result = []
237
        for facture in data.get('FACTURES', []):
238
            result.append({
239
                'id': facture['IDFACTURE'],
240
                'label': facture['LIBELLE'],
241
                'amount': facture['RESTEAPAYER'],
242
                'total_amount': facture['MONTANTTOTAL'],
243
                'online_payment': True,
244
                'created': facture['DATEEMISSION'],
245
                'pay_limit_date': facture['DATEECHEANCE'],
246
                'has_pdf': True if facture['EXISTEPDF'] == '1' else False,
247
                'paid': False,
248
                'vendor': {'toulouse-axel': facture},
249
            })
250
        return result
251

  
252
    def get_invoice(self, name_id, invoice_id, error_status=200):
253
        invoices_data = self.get_invoices(name_id, error_status=error_status)
254
        for invoice in invoices_data:
255
            if str(invoice['id']) == invoice_id:
256
                return invoice
257

  
258
    @endpoint(
259
        description=_("Get invoices to pay"),
260
        perm='can_access',
261
        parameters={
262
            'NameID': {'description': _('Publik ID')},
263
        })
264
    def invoices(self, request, NameID):
265
        invoices_data = self.get_invoices(NameID)
266
        return {'data': invoices_data}
267

  
268
    @endpoint(
269
        perm='can_access',
270
        pattern=r'^(?P<invoice_id>\w+)/?$',
271
        description=_('Get invoice details'),
272
        parameters={
273
            'NameID': {'description': _('Publik ID')},
274
            'invoice_id': {'description': _('Invoice identifier')}
275
        })
276
    def invoice(self, request, invoice_id, NameID):
277
        invoice = self.get_invoice(NameID, invoice_id)
278
        if invoice is None:
279
            raise APIError('Invoice not found', err='not-found')
280

  
281
        return {'data': invoice}
282

  
283
    @endpoint(
284
        name='invoice',
285
        perm='can_access',
286
        pattern=r'^(?P<invoice_id>\w+)/pdf/?$',
287
        description=_('Get invoice as a PDF file'),
288
        parameters={
289
            'NameID': {'description': _('Publik ID')},
290
            'invoice_id': {'description': _('Invoice identifier')}
291
        })
292
    def invoice_pdf(self, request, invoice_id, NameID):
293
        # check that invoice is related to current user
294
        invoice = self.get_invoice(NameID, invoice_id, error_status=404)
295
        if invoice is None:
296
            raise APIError('Invoice not found', err='not-found', http_status=404)
297
        # check that PDF is available
298
        if not invoice['has_pdf']:
299
            raise APIError('PDF not available', err='not-available', http_status=404)
300

  
301
        try:
302
            result = ref_facture_pdf(self, {'PORTAIL': {'FACTUREPDF': {'IDFACTURE': int(invoice_id)}}})
303
        except AxelError as e:
304
            raise APIError('Axel error: %s' % e, err='error', http_status=404)
305

  
306
        b64content = base64.b64decode(result['DATA']['PORTAIL']['PDF']['@FILE'])
307
        if not b64content:
308
            raise APIError('PDF error', err='error', http_status=404)
309
        response = HttpResponse(content_type='application/pdf')
310
        response['Content-Disposition'] = 'attachment; filename="%s.pdf"' % invoice_id
311
        response.write(b64content)
312
        return response
313

  
227 314

  
228 315
class Link(models.Model):
229 316
    resource = models.ForeignKey(ToulouseAxel, on_delete=models.CASCADE)
passerelle/contrib/toulouse_axel/xsd/Dui/Q_RefFactureAPayer.xsd
1
<?xml version="1.0" encoding="utf-8" ?>
2
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:all="urn:AllAxelTypes">
3
	
4
	<xsd:import schemaLocation="../AllAxelTypes.xsd" namespace="urn:AllAxelTypes"  />
5
	
6
	<xsd:complexType name="DUIType">
7
		<xsd:sequence>
8
			<xsd:element ref="IDDUI" />
9
		</xsd:sequence> 
10
	</xsd:complexType>
11
	
12
	<xsd:complexType name="PORTAILType">
13
		<xsd:sequence>
14
				<xsd:element ref="DUI"/>
15
		</xsd:sequence>	
16
	</xsd:complexType>
17
	
18
	<xsd:element name="IDDUI" type="all:IDENTREQUIREDType"/>
19
	
20
	<xsd:element name="DUI" type="DUIType"/>
21
	
22
	<xsd:element name="PORTAIL" type="PORTAILType"/>
23
</xsd:schema>
passerelle/contrib/toulouse_axel/xsd/Dui/R_RefFactureAPayer.xsd
1
<?xml version="1.0" encoding="utf-8" ?>
2
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:all="urn:AllAxelTypes">
3
	
4
	<xsd:import schemaLocation="../AllAxelTypes.xsd" namespace="urn:AllAxelTypes"  />
5

  
6
	<xsd:redefine schemaLocation="../R_ShemaResultat.xsd">
7
	    <xsd:simpleType name="TYPEType">
8
			<xsd:restriction base="TYPEType">
9
				<xsd:enumeration value="RefFactureAPayer" />
10
			</xsd:restriction>
11
	    </xsd:simpleType>
12
		
13
		<xsd:complexType name="PORTAILType">
14
			<xsd:complexContent>
15
				<xsd:extension base="PORTAILType">
16
					<xsd:sequence>
17
							<xsd:element ref="DUI" minOccurs="0"/>
18
					</xsd:sequence>	
19
				</xsd:extension>
20
			 </xsd:complexContent>
21
		</xsd:complexType>
22
	</xsd:redefine>
23
	
24
	<xsd:simpleType name="CHOIXType">
25
		<xsd:restriction base="xsd:string">
26
			<xsd:enumeration value="" />
27
			<xsd:enumeration value="1" />
28
			<xsd:enumeration value="0" />
29
		</xsd:restriction>
30
	</xsd:simpleType>
31
	
32
	<xsd:complexType name="FACTURESType">
33
		<xsd:sequence>
34
			<xsd:element ref="NUMFACTURE" />
35
			<xsd:element ref="DATEEMISSION" />
36
			<xsd:element ref="DATEECHEANCE" />
37
			<xsd:element ref="LIBELLE" />
38
			<xsd:element ref="IDFACTURATION" />
39
			<xsd:element ref="MONTANTTOTAL" />
40
			<xsd:element ref="RESTEAPAYER" />
41
			<xsd:element ref="IDREGIE" />
42
			<xsd:element ref="EXISTEPDF" />
43
			<xsd:element ref="IDFACTURE" />
44
		</xsd:sequence> 
45
	</xsd:complexType>
46
			
47
	<xsd:complexType name="DUIType">
48
		<xsd:sequence>
49
			<xsd:element ref="IDDUI" />
50
			<xsd:element ref="CHOIXDEMAT" />
51
			<xsd:element ref="NBFACTURES" />
52
			<xsd:element ref="FACTURES" minOccurs="0" maxOccurs="unbounded"/>
53
		</xsd:sequence> 
54
	</xsd:complexType>
55

  
56
	<xsd:element name="IDDUI" type="all:IDENTType"/>
57
	<xsd:element name="CHOIXDEMAT" type="CHOIXType"/>
58
	<xsd:element name="NBFACTURES" type="all:unsignedInt-or-empty"/>
59
	<xsd:element name="FACTURES" type="FACTURESType"/>
60
	
61
	<xsd:element name="NUMFACTURE" type="xsd:unsignedInt"/>
62
	<xsd:element name="DATEEMISSION" type="all:DATEREQUIREDType"/>
63
	<xsd:element name="DATEECHEANCE" type="all:DATEREQUIREDType"/>
64
	<xsd:element name="LIBELLE" type="xsd:string"/>
65
	<xsd:element name="IDFACTURATION" type="all:IDType"/>
66
	<xsd:element name="MONTANTTOTAL" type="all:MONTANTType"/>
67
	<xsd:element name="RESTEAPAYER" type="all:MONTANTREQUIREDType"/>
68
	<xsd:element name="IDREGIE" type="all:IDType"/>
69
	<xsd:element name="EXISTEPDF" type="CHOIXType"/>
70
	<xsd:element name="IDFACTURE" type="xsd:unsignedInt"/>
71
	
72
	
73
	<xsd:element name="DUI" type="DUIType" />
74
</xsd:schema>
passerelle/contrib/toulouse_axel/xsd/Q_RefFacturePDF.xsd
1
<?xml version="1.0" encoding="utf-8" ?>
2
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3

  
4
	<xsd:complexType name="FACTUREPDFType">
5
		<xsd:sequence>
6
			<xsd:element ref="IDFACTURE" />			
7
		</xsd:sequence>
8
	</xsd:complexType>
9
	
10
	<xsd:complexType name="PORTAILType">
11
		<xsd:sequence>
12
			<xsd:element ref="FACTUREPDF" />
13
		</xsd:sequence> 
14
	</xsd:complexType>
15
					
16
	<xsd:element name="IDFACTURE" type="xsd:positiveInteger"/>
17
	<xsd:element name="FACTUREPDF" type="FACTUREPDFType"/>	
18
	<xsd:element name="PORTAIL" type="PORTAILType"/>	
19
		
20
</xsd:schema>
passerelle/contrib/toulouse_axel/xsd/R_RefFacturePDF.xsd
1
<?xml version="1.0" encoding="utf-8" ?>
2
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3

  
4
	<xsd:redefine schemaLocation="./R_ShemaResultat.xsd">
5
	    <xsd:simpleType name="TYPEType">
6
			<xsd:restriction base="TYPEType">
7
				<xsd:enumeration value="RefFacturePDF" />
8
			</xsd:restriction>
9
	    </xsd:simpleType>
10
		
11
		<xsd:complexType name="PORTAILType">
12
			<xsd:complexContent>
13
				<xsd:extension base="PORTAILType">
14
					<xsd:sequence>
15
							<xsd:element ref="PDF" minOccurs="0"/>
16
					</xsd:sequence>	
17
				</xsd:extension>
18
			 </xsd:complexContent>
19
		</xsd:complexType>
20
	</xsd:redefine>
21
	
22
	<xsd:complexType name="PDFType">
23
		<xsd:attribute ref="FILE"/>
24
	</xsd:complexType>
25
		
26
	<xsd:attribute name="FILE" type="xsd:string"/>
27
	
28
	<xsd:element name="PDF" type="PDFType"/>
29
		
30
</xsd:schema>
tests/data/toulouse_axel/invoices.xml
1
<PORTAIL>
2
  <DUI>
3
    <IDDUI>XXX</IDDUI>
4
    <CHOIXDEMAT>0</CHOIXDEMAT>
5
    <NBFACTURES>2</NBFACTURES>
6
    <FACTURES>
7
      <NUMFACTURE>42</NUMFACTURE>
8
      <DATEEMISSION>12/11/2019</DATEEMISSION>
9
      <DATEECHEANCE>04/12/2019</DATEECHEANCE>
10
      <LIBELLE>PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019</LIBELLE>
11
      <IDFACTURATION>4242-35AA</IDFACTURATION>
12
      <MONTANTTOTAL>44.94</MONTANTTOTAL>
13
      <RESTEAPAYER>44.94</RESTEAPAYER>
14
      <IDREGIE>MAREGIE</IDREGIE>
15
      <EXISTEPDF>1</EXISTEPDF>
16
      <IDFACTURE>42</IDFACTURE>
17
    </FACTURES>
18
    <FACTURES>
19
      <NUMFACTURE>43</NUMFACTURE>
20
      <DATEEMISSION>12/12/2019</DATEEMISSION>
21
      <DATEECHEANCE>04/01/2020</DATEECHEANCE>
22
      <LIBELLE>PRESTATIONS PERISCOLAIRES NOVEMBRE 2019</LIBELLE>
23
      <IDFACTURATION>4243-35AA</IDFACTURATION>
24
      <MONTANTTOTAL>44.94</MONTANTTOTAL>
25
      <RESTEAPAYER>44.94</RESTEAPAYER>
26
      <IDREGIE>MAREGIE</IDREGIE>
27
      <EXISTEPDF>0</EXISTEPDF>
28
      <IDFACTURE>43</IDFACTURE>
29
    </FACTURES>
30
  </DUI>
31
</PORTAIL>
tests/test_toulouse_axel.py
25 25
    Link,
26 26
    ToulouseAxel,
27 27
    ref_famille_dui,
28
    ref_facture_a_payer,
29
    ref_facture_pdf,
28 30
    ref_verif_dui,
29 31
)
30 32
import utils
......
131 133
            })
132 134

  
133 135

  
136
@pytest.mark.parametrize('content', [
137
    '<PORTAIL><DUI/></PORTAIL>',
138
])
139
def test_operation_ref_facture_a_payer(resource, content):
140
    with mock_getdata(content, 'RefFactureAPayer'):
141
        with pytest.raises(AxelError):
142
            ref_facture_a_payer(resource, {
143
                'PORTAIL': {
144
                    'DUI': {
145
                        'IDDUI': 'XXX',
146
                    }
147
                }
148
            })
149

  
150

  
151
@pytest.mark.parametrize('content', [
152
    "<PORTAIL><PDF FOO='BAR'></PDF></PORTAIL>",
153
])
154
def test_operation_ref_facture_pdf(resource, content):
155
    with mock_getdata(content, 'RefFacturePDF'):
156
        with pytest.raises(AxelError):
157
            ref_facture_pdf(resource, {
158
                'PORTAIL': {
159
                    'FACTUREPDF': {
160
                        'IDFACTURE': 42,
161
                    }
162
                }
163
            })
164

  
165

  
134 166
def test_link_endpoint_nameid_empty(app, resource, params):
135 167
    resp = app.post_json('/toulouse-axel/test/link?NameID=', params=params, status=400)
136 168
    assert resp.json['err_desc'] == "NameID is empty"
......
306 338
        'SANITAIRE',
307 339
        'SEXE',
308 340
    ])
341

  
342

  
343
def test_invoices_endpoint_axel_error(app, resource):
344
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
345
    with mock.patch('passerelle.contrib.toulouse_axel.models.ref_facture_a_payer') as operation:
346
        operation.side_effect = AxelError('FooBar')
347
        resp = app.get('/toulouse-axel/test/invoices?NameID=yyy')
348
        assert resp.json['err_desc'] == "Axel error: FooBar"
349

  
350

  
351
def test_invoices_endpoint_no_result(app, resource):
352
    resp = app.get('/toulouse-axel/test/invoices?NameID=yyy')
353
    assert resp.json['err_desc'] == "Person not found"
354

  
355

  
356
def test_invoices_endpoint_no_invoices(app, resource):
357
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
358
    content = '''<PORTAIL>
359
    <DUI>
360
        <IDDUI>XXX</IDDUI>
361
        <CHOIXDEMAT>0</CHOIXDEMAT>
362
        <NBFACTURES>0</NBFACTURES>
363
    </DUI>
364
</PORTAIL>'''
365
    with mock_getdata(content, 'RefFactureAPayer'):
366
        resp = app.get('/toulouse-axel/test/invoices?NameID=yyy')
367
    assert resp.json['err'] == 0
368
    assert resp.json['data'] == []
369

  
370

  
371
def test_invoices_endpoint(app, resource):
372
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
373
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
374
    with open(filepath) as xml:
375
        content = xml.read()
376
    with mock_getdata(content, 'RefFactureAPayer'):
377
        resp = app.get('/toulouse-axel/test/invoices?NameID=yyy')
378
    assert resp.json['err'] == 0
379
    assert resp.json['data'] == [
380
        {
381
            'id': 42,
382
            'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
383
            'amount': '44.94',
384
            'total_amount': '44.94',
385
            'online_payment': True,
386
            'created': '2019-11-12',
387
            'pay_limit_date': '2019-12-04',
388
            'has_pdf': True,
389
            'paid': False,
390
            'vendor': {
391
                'toulouse-axel': {
392
                    'IDFACTURATION': '4242-35AA',
393
                    'IDFACTURE': 42,
394
                    'IDREGIE': 'MAREGIE',
395
                    'DATEECHEANCE': '2019-12-04',
396
                    'DATEEMISSION': '2019-11-12',
397
                    'EXISTEPDF': '1',
398
                    'LIBELLE': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
399
                    'MONTANTTOTAL': '44.94',
400
                    'NUMFACTURE': 42,
401
                    'RESTEAPAYER': '44.94',
402
                 }
403
            }
404
        },
405
        {
406
            'id': 43,
407
            'label': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019',
408
            'amount': '44.94',
409
            'total_amount': '44.94',
410
            'online_payment': True,
411
            'created': '2019-12-12',
412
            'pay_limit_date': '2020-01-04',
413
            'has_pdf': False,
414
            'paid': False,
415
            'vendor': {
416
                'toulouse-axel': {
417
                    'IDFACTURATION': '4243-35AA',
418
                    'IDFACTURE': 43,
419
                    'DATEECHEANCE': '2020-01-04',
420
                    'DATEEMISSION': '2019-12-12',
421
                    'EXISTEPDF': '0',
422
                    'IDREGIE': 'MAREGIE',
423
                    'LIBELLE': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019',
424
                    'MONTANTTOTAL': '44.94',
425
                    'NUMFACTURE': 43,
426
                    'RESTEAPAYER': '44.94',
427
                }
428
            }
429
        }
430
    ]
431

  
432

  
433
def test_invoice_endpoint_axel_error(app, resource):
434
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
435
    with mock.patch('passerelle.contrib.toulouse_axel.models.ref_facture_a_payer') as operation:
436
        operation.side_effect = AxelError('FooBar')
437
        resp = app.get('/toulouse-axel/test/invoice/42?NameID=yyy')
438
        assert resp.json['err_desc'] == "Axel error: FooBar"
439

  
440

  
441
def test_invoice_endpoint_no_result(app, resource):
442
    resp = app.get('/toulouse-axel/test/invoice/42?NameID=yyy')
443
    assert resp.json['err_desc'] == "Person not found"
444

  
445
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
446
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
447
    with open(filepath) as xml:
448
        content = xml.read()
449
    with mock_getdata(content, 'RefFactureAPayer'):
450
        resp = app.get('/toulouse-axel/test/invoice/35?NameID=yyy')
451
    assert resp.json['err_desc'] == "Invoice not found"
452

  
453

  
454
def test_invoice_endpoint(app, resource):
455
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
456
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
457
    with open(filepath) as xml:
458
        content = xml.read()
459
    with mock_getdata(content, 'RefFactureAPayer'):
460
        resp = app.get('/toulouse-axel/test/invoice/42?NameID=yyy')
461
    assert resp.json['err'] == 0
462
    assert resp.json['data'] == {
463
        'id': 42,
464
        'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
465
        'amount': '44.94',
466
        'total_amount': '44.94',
467
        'online_payment': True,
468
        'created': '2019-11-12',
469
        'pay_limit_date': '2019-12-04',
470
        'has_pdf': True,
471
        'paid': False,
472
        'vendor': {
473
            'toulouse-axel': {
474
                'IDFACTURATION': '4242-35AA',
475
                'IDFACTURE': 42,
476
                'IDREGIE': 'MAREGIE',
477
                'DATEECHEANCE': '2019-12-04',
478
                'DATEEMISSION': '2019-11-12',
479
                'EXISTEPDF': '1',
480
                'LIBELLE': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
481
                'MONTANTTOTAL': '44.94',
482
                'NUMFACTURE': 42,
483
                'RESTEAPAYER': '44.94',
484
            }
485
        }
486
    }
487

  
488

  
489
def test_invoice_pdf_endpoint_axel_error(app, resource):
490
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
491
    with mock.patch('passerelle.contrib.toulouse_axel.models.ref_facture_a_payer') as operation:
492
        operation.side_effect = AxelError('FooBar')
493
        resp = app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy', status=404)
494
        assert resp.json['err_desc'] == "Axel error: FooBar"
495

  
496
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
497
    with open(filepath) as xml:
498
        content = xml.read()
499
    with mock_getdata(content, 'RefFactureAPayer'):
500
        with mock.patch('passerelle.contrib.toulouse_axel.models.ref_facture_pdf') as operation:
501
            operation.side_effect = AxelError('FooBar')
502
            resp = app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy', status=404)
503
            assert resp.json['err_desc'] == "Axel error: FooBar"
504

  
505

  
506
def test_invoice_pdf_endpoint_no_result(app, resource):
507
    resp = app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy', status=404)
508
    assert resp.json['err_desc'] == "Person not found"
509

  
510
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
511
    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/invoices.xml')
512
    with open(filepath) as xml:
513
        content = xml.read()
514
    with mock_getdata(content, 'RefFactureAPayer'):
515
        resp = app.get('/toulouse-axel/test/invoice/35/pdf?NameID=yyy', status=404)
516
    assert resp.json['err_desc'] == "Invoice not found"
517

  
518
    with mock_getdata(content, 'RefFactureAPayer'):
519
        resp = app.get('/toulouse-axel/test/invoice/43/pdf?NameID=yyy', status=404)
520
    assert resp.json['err_desc'] == "PDF not available"
521

  
522
    pdf_content = '''<PORTAIL>
523
    <PDF FILE=''></PDF>
524
</PORTAIL>'''
525
    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_invoice') as invoice:
526
        invoice.return_value = {'has_pdf': True}
527
        with mock_getdata(pdf_content, 'RefFacturePDF'):
528
            resp = app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy', status=404)
529
    assert resp.json['err_desc'] == "PDF error"
530

  
531

  
532
def test_invoice_pdf_endpoint(app, resource):
533
    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
534
    pdf_content = '''<PORTAIL>
535
    <PDF FILE='aGVsbG8gd29ybGQ='></PDF>
536
</PORTAIL>'''
537
    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_invoice') as invoice:
538
        invoice.return_value = {'has_pdf': True}
539
        with mock_getdata(pdf_content, 'RefFacturePDF'):
540
            app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy')
309
-