toulouse_axel: endpoint to pay an invoice (#39005)
This commit is contained in:
parent
9015f164a5
commit
342121cf38
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
Loading…
Reference in New Issue