toulouse_axel: endpoint to pay an invoice (#39005)

This commit is contained in:
Lauréline Guérin 2020-01-16 14:31:18 +01:00
parent 9015f164a5
commit 342121cf38
No known key found for this signature in database
GPG Key ID: 1FAB9B9B4F93D473
7 changed files with 400 additions and 87 deletions

View File

@ -4,7 +4,7 @@ import requests
def test_link(conn, user):
print("Get update management dates")
url = conn + '/update_management_dates'
url = conn + '/management_dates'
resp = requests.get(url)
resp.raise_for_status()
res = resp.json()
@ -129,33 +129,6 @@ 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

@ -17,6 +17,7 @@
import base64
import copy
import datetime
import json
import logging
import os
import re
@ -50,6 +51,26 @@ boolean_type = {
}
]
}
datetime_type = {
'type': 'string',
'pattern': '[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}',
}
json_date_format = '%Y-%m-%d'
json_datetime_format = '%Y-%m-%dT%H:%M:%S'
xml_date_format = '%d/%m/%Y'
xml_datetime_format = '%d/%m/%Y %H:%M:%S'
PAYMENT_SCHEMA = {
'type': 'object',
'properties': {
'transaction_date': copy.deepcopy(datetime_type),
'transaction_id': {
'type': 'string',
}
},
'required': ['transaction_date', 'transaction_id']
}
def indent(tree, space=" ", level=0):
@ -97,6 +118,13 @@ def encode_bool(obj):
return obj
def encode_datetime(obj):
try:
return datetime.datetime.strptime(obj, json_datetime_format).strftime(xml_datetime_format)
except ValueError:
return obj
class AxelSchema(JSONSchemaFromXMLSchema):
type_map = {
'{urn:AllAxelTypes}DATEREQUIREDType': 'date',
@ -114,7 +142,7 @@ class AxelSchema(JSONSchemaFromXMLSchema):
def encode_date(self, obj):
try:
return datetime.datetime.strptime(obj, '%Y-%m-%d').strftime('%d/%m/%Y')
return datetime.datetime.strptime(obj, json_date_format).strftime(xml_date_format)
except ValueError:
return obj
@ -124,7 +152,7 @@ class AxelSchema(JSONSchemaFromXMLSchema):
return self.encode_date(obj)
def decode_date(self, data):
value = datetime.datetime.strptime(data.text, '%d/%m/%Y').strftime('%Y-%m-%d')
value = datetime.datetime.strptime(data.text, xml_date_format).strftime(json_date_format)
return xmlschema.ElementData(tag=data.tag, text=value, content=data.content, attributes=data.attributes)
def decode_date_optional(self, data):
@ -249,6 +277,7 @@ ref_date_gestion_dui = Operation('RefDateGestionDui')
ref_verif_dui = Operation('RefVerifDui')
ref_famille_dui = Operation('RefFamilleDui')
form_maj_famille_dui = Operation('FormMajFamilleDui')
form_paiement_dui = Operation('FormPaiementDui')
ref_facture_a_payer = Operation('RefFactureAPayer')
ref_facture_pdf = Operation('RefFacturePDF', prefix='')
@ -471,11 +500,11 @@ class ToulouseAxel(BaseResource):
}
}
def get_link(self, name_id, error_status=200):
def get_link(self, name_id):
try:
return self.link_set.get(name_id=name_id)
except Link.DoesNotExist:
raise APIError('Person not found', err_code='not-found', http_status=error_status)
raise APIError('Person not found', err_code='not-found')
@endpoint(
description=_('Delete link between user and Toulouse Axel'),
@ -724,77 +753,104 @@ class ToulouseAxel(BaseResource):
}
}
def get_invoices(self, name_id, error_status=200):
link = self.get_link(name_id, error_status=error_status)
def normalize_invoice(self, invoice, dui):
invoice_id = '%s-%s' % (dui, invoice['IDFACTURE'])
data = {
'id': invoice_id,
'display_id': str(invoice['IDFACTURE']),
'label': invoice['LIBELLE'],
'amount': invoice['RESTEAPAYER'],
'total_amount': invoice['MONTANTTOTAL'],
'created': invoice['DATEEMISSION'],
'pay_limit_date': invoice['DATEECHEANCE'],
'has_pdf': True if invoice['EXISTEPDF'] == '1' else False,
'paid': False,
'vendor': {'toulouse-axel': invoice},
}
pay_limit_date = datetime.datetime.strptime(invoice['DATEECHEANCE'], '%Y-%m-%d').date()
data['online_payment'] = data['amount'] > 0 and pay_limit_date >= datetime.date.today()
return data
def get_invoices(self, regie_id, dui=None, name_id=None):
assert name_id or dui
if name_id:
dui = self.get_link(name_id).dui
try:
result = ref_facture_a_payer(self, {'PORTAIL': {'DUI': {'IDDUI': link.dui}}})
result = ref_facture_a_payer(self, {'PORTAIL': {'DUI': {'IDDUI': dui}}})
except AxelError as e:
raise APIError(
'Axel error: %s' % e,
err_code='error',
http_status=error_status,
data={'xml_request': e.xml_request,
'xml_response': e.xml_response})
data = result.json_response['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},
})
if facture['IDREGIE'] != regie_id:
continue
result.append(self.normalize_invoice(facture, dui))
return result
def get_invoice(self, name_id, invoice_id, error_status=200):
invoices_data = self.get_invoices(name_id, error_status=error_status)
def get_invoice(self, regie_id, invoice_id, dui=None, name_id=None):
invoices_data = self.get_invoices(regie_id=regie_id, dui=dui, name_id=name_id)
for invoice in invoices_data:
if str(invoice['id']) == invoice_id:
if invoice['display_id'] == invoice_id:
return invoice
@endpoint(
description=_("Get invoices to pay"),
name='regie',
perm='can_access',
pattern=r'^(?P<regie_id>[\w-]+)/invoices/?$',
example_pattern='{regie_id}/invoices/',
description=_("Get invoices to pay"),
parameters={
'NameID': {'description': _('Publik ID')},
'regie_id': {'description': _('Regie identifier'), 'example_value': '42-PERISCOL'}
})
def invoices(self, request, NameID):
invoices_data = self.get_invoices(NameID)
def invoices(self, request, regie_id, NameID):
invoices_data = self.get_invoices(regie_id=regie_id, name_id=NameID)
return {'data': invoices_data}
@endpoint(
name='regie',
perm='can_access',
pattern=r'^(?P<invoice_id>\w+)/?$',
pattern=r'^(?P<regie_id>[\w-]+)/invoice/(?P<invoice_id>\w+-\d+)/?$',
example_pattern='{regie_id}/invoice/{invoice_id}/',
description=_('Get invoice details'),
parameters={
'NameID': {'description': _('Publik ID')},
'invoice_id': {'description': _('Invoice identifier')}
'regie_id': {'description': _('Regie identifier'), 'example_value': '42-PERISCOL'},
'invoice_id': {'description': _('Invoice identifier'), 'example_value': 'DUI-42'}
})
def invoice(self, request, invoice_id, NameID):
invoice = self.get_invoice(NameID, invoice_id)
def invoice(self, request, regie_id, invoice_id, NameID):
invoice_id = invoice_id.split('-')[1]
invoice = self.get_invoice(regie_id=regie_id, name_id=NameID, invoice_id=invoice_id)
if invoice is None:
raise APIError('Invoice not found', err_code='not-found')
return {'data': invoice}
@endpoint(
name='invoice',
name='regie',
perm='can_access',
pattern=r'^(?P<invoice_id>\w+)/pdf/?$',
pattern=r'^(?P<regie_id>[\w-]+)/invoice/(?P<invoice_id>\w+-\d+)/pdf/?$',
example_pattern='{regie_id}/invoice/{invoice_id}/pdf/',
description=_('Get invoice as a PDF file'),
parameters={
'NameID': {'description': _('Publik ID')},
'invoice_id': {'description': _('Invoice identifier')}
'regie_id': {'description': _('Regie identifier'), 'example_value': '42-PERISCOL'},
'invoice_id': {'description': _('Invoice identifier'), 'example_value': 'DUI-42'}
})
def invoice_pdf(self, request, invoice_id, NameID):
def invoice_pdf(self, request, regie_id, invoice_id, NameID):
# check that invoice is related to current user
invoice = self.get_invoice(NameID, invoice_id, error_status=404)
invoice_id = invoice_id.split('-')[1]
try:
invoice = self.get_invoice(regie_id=regie_id, name_id=NameID, invoice_id=invoice_id)
except APIError as e:
e.http_status = 404
raise
if invoice is None:
raise APIError('Invoice not found', err_code='not-found', http_status=404)
# check that PDF is available
@ -802,7 +858,7 @@ class ToulouseAxel(BaseResource):
raise APIError('PDF not available', err_code='not-available', http_status=404)
try:
result = ref_facture_pdf(self, {'PORTAIL': {'FACTUREPDF': {'IDFACTURE': int(invoice_id)}}})
result = ref_facture_pdf(self, {'PORTAIL': {'FACTUREPDF': {'IDFACTURE': int(invoice['display_id'])}}})
except AxelError as e:
raise APIError(
'Axel error: %s' % e,
@ -819,6 +875,56 @@ class ToulouseAxel(BaseResource):
response.write(b64content)
return response
@endpoint(
name='regie',
methods=['post'],
perm='can_access',
pattern=r'^(?P<regie_id>[\w-]+)/invoice/(?P<invoice_id>\w+-\d+)/pay/?$',
example_pattern='{regie_id}/invoice/{invoice_id}/pay/',
description=_('Notify an invoice as paid'),
parameters={
'regie_id': {'description': _('Regie identifier'), 'example_value': '42-PERISCOL'},
'invoice_id': {'description': _('Invoice identifier'), 'example_value': 'DUI-42'}
},
post={
'request_body': {
'schema': {
'application/json': PAYMENT_SCHEMA,
}
}
})
def pay_invoice(self, request, regie_id, invoice_id, **kwargs):
data = json.loads(request.body)
dui, invoice_id = invoice_id.split('-')
invoices_data = self.get_invoices(regie_id=regie_id, dui=dui)
transaction_amount = None
for invoice in invoices_data:
if invoice['display_id'] == invoice_id:
transaction_amount = invoice['amount']
break
if transaction_amount is None:
raise APIError('Invoice not found', err_code='not-found')
transaction_id = data['transaction_id']
transaction_date = encode_datetime(data['transaction_date'])
post_data = {
'IDFACTURE': int(invoice_id),
'IDREGIEENCAISSEMENT': '',
'MONTANTPAYE': transaction_amount,
'DATEPAIEMENT': transaction_date,
'REFERENCE': transaction_id,
}
try:
form_paiement_dui(self, {'PORTAIL': {'DUI': post_data}})
except AxelError as e:
raise APIError(
'Axel error: %s' % e,
err_code='error',
data={'xml_request': e.xml_request,
'xml_response': e.xml_response})
return {'data': True}
class Link(models.Model):
resource = models.ForeignKey(ToulouseAxel, on_delete=models.CASCADE)

View File

@ -0,0 +1,44 @@
<?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="IDFACTURE" />
<xsd:element ref="IDREGIEENCAISSEMENT"/>
<xsd:element ref="MONTANTPAYE"/>
<xsd:element ref="DATEPAIEMENT"/>
<xsd:element ref="REFERENCE"/>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="PORTAILType">
<xsd:sequence>
<xsd:element ref="DUI"/>
</xsd:sequence>
</xsd:complexType>
<xsd:simpleType name="CHOIXType">
<xsd:restriction base="xsd:string">
<xsd:enumeration value="1" />
<xsd:enumeration value="0" />
</xsd:restriction>
</xsd:simpleType>
<xsd:simpleType name="IDENTREGIEType">
<xsd:restriction base="xsd:string">
<xsd:maxLength value="10" />
</xsd:restriction>
</xsd:simpleType>
<xsd:element name="IDFACTURE" type="xsd:unsignedInt"/>
<xsd:element name="IDREGIEENCAISSEMENT" type="IDENTREGIEType"/>
<xsd:element name="MONTANTPAYE" type="all:MONTANTREQUIREDType"/>
<xsd:element name="DATEPAIEMENT" type="all:DATETIMEType"/>
<xsd:element name="REFERENCE" type="xsd:string"/>
<xsd:element name="DUI" type="DUIType"/>
<xsd:element name="PORTAIL" type="PORTAILType"/>
</xsd:schema>

View File

@ -0,0 +1,36 @@
<?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="FormPaiementDui" />
</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:complexType name="DUIType">
<xsd:sequence>
<xsd:element ref="IDDUI" />
<xsd:element ref="CODE" />
</xsd:sequence>
</xsd:complexType>
<xsd:element name="CODE" type="all:unsignedInt-or-empty"/>
<xsd:element name="IDDUI" type="all:IDENTType"/>
<xsd:element name="DUI" type="DUIType" />
</xsd:schema>

View File

@ -27,5 +27,17 @@
<EXISTEPDF>0</EXISTEPDF>
<IDFACTURE>43</IDFACTURE>
</FACTURES>
<FACTURES>
<NUMFACTURE>44</NUMFACTURE>
<DATEEMISSION>12/01/2020</DATEEMISSION>
<DATEECHEANCE>15/01/2020</DATEECHEANCE>
<LIBELLE>PRESTATIONS PERISCOLAIRES DECEMBRE 2019</LIBELLE>
<IDFACTURATION>4244-35AA</IDFACTURATION>
<MONTANTTOTAL>44.94</MONTANTTOTAL>
<RESTEAPAYER>44.94</RESTEAPAYER>
<IDREGIE>AUTREREGIE</IDREGIE>
<EXISTEPDF>1</EXISTEPDF>
<IDFACTURE>44</IDFACTURE>
</FACTURES>
</DUI>
</PORTAIL>

View File

@ -17,6 +17,7 @@
from contextlib import contextmanager
import copy
import datetime
import decimal
import json
import mock
import os
@ -32,6 +33,7 @@ from passerelle.contrib.toulouse_axel.models import (
OperationResult,
ToulouseAxel,
form_maj_famille_dui,
form_paiement_dui,
ref_date_gestion_dui,
ref_famille_dui,
ref_facture_a_payer,
@ -327,6 +329,25 @@ def test_operation_ref_facture_pdf(resource, content):
})
@pytest.mark.parametrize('content', [
'<PORTAIL><DUI/></PORTAIL>',
])
def test_operation_form_paiement_dui(resource, content):
with mock_getdata(content, 'FormPaiementDui'):
with pytest.raises(AxelError):
form_paiement_dui(resource, {
'PORTAIL': {
'DUI': {
'IDFACTURE': '42',
'IDREGIEENCAISSEMENT': '',
'MONTANTPAYE': '42.42',
'DATEPAIEMENT': '01/01/2020 12:12:12',
'REFERENCE': '42',
}
}
})
def test_management_dates_endpoint_axel_error(app, resource):
with mock.patch('passerelle.contrib.toulouse_axel.models.ref_date_gestion_dui') as operation:
operation.side_effect = AxelError('FooBar')
@ -1195,13 +1216,13 @@ 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')
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices?NameID=yyy')
assert resp.json['err_desc'] == "Axel error: FooBar"
assert resp.json['err'] == 'error'
def test_invoices_endpoint_no_result(app, resource):
resp = app.get('/toulouse-axel/test/invoices?NameID=yyy')
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices?NameID=yyy')
assert resp.json['err_desc'] == "Person not found"
assert resp.json['err'] == 'not-found'
@ -1216,7 +1237,7 @@ def test_invoices_endpoint_no_invoices(app, resource):
</DUI>
</PORTAIL>'''
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/invoices?NameID=yyy')
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices?NameID=yyy')
assert resp.json['err'] == 0
assert resp.json['data'] == []
@ -1227,15 +1248,16 @@ def test_invoices_endpoint(app, resource):
with open(filepath) as xml:
content = xml.read()
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/invoices?NameID=yyy')
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoices?NameID=yyy')
assert resp.json['err'] == 0
assert resp.json['data'] == [
{
'id': 42,
'id': 'XXX-42',
'display_id': '42',
'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
'amount': '44.94',
'total_amount': '44.94',
'online_payment': True,
'online_payment': False,
'created': '2019-11-12',
'pay_limit_date': '2019-12-04',
'has_pdf': True,
@ -1256,11 +1278,12 @@ def test_invoices_endpoint(app, resource):
}
},
{
'id': 43,
'id': 'XXX-43',
'display_id': '43',
'label': 'PRESTATIONS PERISCOLAIRES NOVEMBRE 2019',
'amount': '44.94',
'total_amount': '44.94',
'online_payment': True,
'online_payment': False,
'created': '2019-12-12',
'pay_limit_date': '2020-01-04',
'has_pdf': False,
@ -1281,19 +1304,50 @@ def test_invoices_endpoint(app, resource):
}
}
]
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/regie/AUTREREGIE/invoices?NameID=yyy')
assert resp.json['err'] == 0
assert resp.json['data'] == [
{
'id': 'XXX-44',
'display_id': '44',
'label': 'PRESTATIONS PERISCOLAIRES DECEMBRE 2019',
'amount': '44.94',
'total_amount': '44.94',
'online_payment': False,
'created': '2020-01-12',
'pay_limit_date': '2020-01-15',
'has_pdf': True,
'paid': False,
'vendor': {
'toulouse-axel': {
'IDFACTURATION': '4244-35AA',
'IDFACTURE': 44,
'IDREGIE': 'AUTREREGIE',
'DATEECHEANCE': '2020-01-15',
'DATEEMISSION': '2020-01-12',
'EXISTEPDF': '1',
'LIBELLE': 'PRESTATIONS PERISCOLAIRES DECEMBRE 2019',
'MONTANTTOTAL': '44.94',
'NUMFACTURE': 44,
'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')
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42?NameID=yyy')
assert resp.json['err_desc'] == "Axel error: FooBar"
assert resp.json['err'] == 'error'
def test_invoice_endpoint_no_result(app, resource):
resp = app.get('/toulouse-axel/test/invoice/42?NameID=yyy')
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42?NameID=yyy')
assert resp.json['err_desc'] == "Person not found"
assert resp.json['err'] == 'not-found'
@ -1302,7 +1356,11 @@ def test_invoice_endpoint_no_result(app, resource):
with open(filepath) as xml:
content = xml.read()
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/invoice/35?NameID=yyy')
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-35?NameID=yyy')
assert resp.json['err_desc'] == "Invoice not found"
assert resp.json['err'] == 'not-found'
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-44?NameID=yyy')
assert resp.json['err_desc'] == "Invoice not found"
assert resp.json['err'] == 'not-found'
@ -1313,14 +1371,15 @@ def test_invoice_endpoint(app, resource):
with open(filepath) as xml:
content = xml.read()
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/invoice/42?NameID=yyy')
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42?NameID=yyy')
assert resp.json['err'] == 0
assert resp.json['data'] == {
'id': 42,
'id': 'XXX-42',
'display_id': '42',
'label': 'PRESTATIONS PERISCOLAIRES SEPTEMBRE-OCTOBRE 2019',
'amount': '44.94',
'total_amount': '44.94',
'online_payment': True,
'online_payment': False,
'created': '2019-11-12',
'pay_limit_date': '2019-12-04',
'has_pdf': True,
@ -1346,7 +1405,7 @@ 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)
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "Axel error: FooBar"
assert resp.json['err'] == 'error'
@ -1356,13 +1415,13 @@ def test_invoice_pdf_endpoint_axel_error(app, resource):
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)
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "Axel error: FooBar"
assert resp.json['err'] == 'error'
def test_invoice_pdf_endpoint_no_result(app, resource):
resp = app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy', status=404)
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "Person not found"
assert resp.json['err'] == 'not-found'
@ -1371,12 +1430,17 @@ def test_invoice_pdf_endpoint_no_result(app, resource):
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)
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-35/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "Invoice not found"
assert resp.json['err'] == 'not-found'
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/invoice/43/pdf?NameID=yyy', status=404)
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-44/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "Invoice not found"
assert resp.json['err'] == 'not-found'
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-43/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "PDF not available"
assert resp.json['err'] == 'not-available'
@ -1384,9 +1448,9 @@ def test_invoice_pdf_endpoint_no_result(app, resource):
<PDF FILE=''></PDF>
</PORTAIL>'''
with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_invoice') as invoice:
invoice.return_value = {'has_pdf': True}
invoice.return_value = {'has_pdf': True, 'display_id': '42'}
with mock_getdata(pdf_content, 'RefFacturePDF'):
resp = app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy', status=404)
resp = app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pdf?NameID=yyy', status=404)
assert resp.json['err_desc'] == "PDF error"
assert resp.json['err'] == 'error'
@ -1397,6 +1461,74 @@ def test_invoice_pdf_endpoint(app, resource):
<PDF FILE='aGVsbG8gd29ybGQ='></PDF>
</PORTAIL>'''
with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_invoice') as invoice:
invoice.return_value = {'has_pdf': True}
invoice.return_value = {'has_pdf': True, 'display_id': '42'}
with mock_getdata(pdf_content, 'RefFacturePDF'):
app.get('/toulouse-axel/test/invoice/42/pdf?NameID=yyy')
app.get('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pdf?NameID=yyy')
def test_pay_invoice_endpoint_axel_error(app, resource):
payload = {
'transaction_date': '2020-01-01T12:00:00',
'transaction_id': 'foo',
}
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.post_json('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pay?NameID=yyy', params=payload)
assert resp.json['err_desc'] == "Axel error: FooBar"
assert resp.json['err'] == 'error'
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.form_paiement_dui') as operation:
operation.side_effect = AxelError('FooBar')
resp = app.post_json('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pay?NameID=yyy', params=payload)
assert resp.json['err_desc'] == "Axel error: FooBar"
assert resp.json['err'] == 'error'
def test_pay_invoice_endpoint_no_result(app, resource):
payload = {
'transaction_date': '2020-01-01T12:00:00',
'transaction_id': 'foo',
}
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.post_json('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-35/pay?NameID=yyy', params=payload)
assert resp.json['err_desc'] == "Invoice not found"
assert resp.json['err'] == 'not-found'
with mock_getdata(content, 'RefFactureAPayer'):
resp = app.post_json('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-44/pay?NameID=yyy', params=payload)
assert resp.json['err_desc'] == "Invoice not found"
assert resp.json['err'] == 'not-found'
def test_pay_invoice_endpoint(app, resource):
payload = {
'transaction_date': '2020-01-01T12:00:00',
'transaction_id': 'foo',
}
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'):
with mock.patch('passerelle.contrib.toulouse_axel.models.form_paiement_dui') as operation:
resp = app.post_json('/toulouse-axel/test/regie/MAREGIE/invoice/XXX-42/pay?NameID=yyy', params=payload)
assert resp.json['err'] == 0
assert resp.json['data'] is True
assert operation.call_args_list[0][0][1] == {
'PORTAIL': {
'DUI': {
'DATEPAIEMENT': '01/01/2020 12:00:00',
'IDFACTURE': 42,
'IDREGIEENCAISSEMENT': '',
'MONTANTPAYE': decimal.Decimal('44.94'),
'REFERENCE': 'foo',
}
}
}

View File

@ -17,6 +17,7 @@
import os
from passerelle.contrib.toulouse_axel.models import AxelSchema
from passerelle.contrib.toulouse_axel.models import encode_datetime
import pytest
import xmlschema
@ -60,6 +61,15 @@ def test_date_mapping(date_type):
assert json_data['DATE'] is None
def test_encode_datetime():
# wrong format
assert encode_datetime('foo') == 'foo'
assert encode_datetime('2019-12-12') == '2019-12-12'
assert encode_datetime('2019-12-12T12:01:72') == '2019-12-12T12:01:72'
# ok
assert encode_datetime('2019-12-12T12:01:42') == '12/12/2019 12:01:42'
@pytest.mark.parametrize('bool_type', ['OUINONREQUIREDType', 'OUINONType'])
@pytest.mark.parametrize('value, expected, py_expected', [
('OUI', 'OUI', True),