toulouse-maelis: notification asynchrone du paiement des factures (#76394) #182

Merged
nroche merged 7 commits from wip/76394-parsifal-pay-invoice-async into main 2023-04-14 17:13:12 +02:00
4 changed files with 401 additions and 73 deletions

View File

@ -0,0 +1,45 @@
# Generated by Django 3.2.18 on 2023-04-13 15:57
import django.core.serializers.json
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('toulouse_maelis', '0007_invoice'),
]
operations = [
migrations.AddField(
model_name='invoice',
name='lingo_notification_date',
field=models.DateTimeField(null=True),
),
migrations.AddField(
model_name='invoice',
name='maelis_data_update_date',
field=models.DateTimeField(null=True),
),
migrations.AddField(
model_name='invoice',
name='maelis_notification_data',
field=models.JSONField(encoder=django.core.serializers.json.DjangoJSONEncoder, null=True),
),
migrations.AddField(
model_name='invoice',
name='maelis_notification_date',
field=models.DateTimeField(null=True),
),
migrations.AlterUniqueTogether(
name='invoice',
unique_together={('resource', 'regie_id', 'invoice_id')},
),
migrations.RemoveField(
model_name='invoice',
name='canceled',
),
migrations.RemoveField(
model_name='invoice',
name='notified',
),
]

View File

@ -16,14 +16,17 @@
import base64
import copy
import datetime
import difflib
import json
import re
from decimal import Decimal
from operator import itemgetter
from urllib.parse import urljoin
import zeep
from dateutil import rrule
from django.core.serializers.json import DjangoJSONEncoder
from django.db import models
from django.db import models, transaction
from django.db.models import JSONField
from django.http import Http404, HttpResponse
from django.utils import dateformat
@ -275,6 +278,17 @@ class ToulouseMaelis(BaseResource, HTTPResource):
else:
self.logger.info('Réferentiels mis à jour.')
def notify_invoices_paid(self):
invoices = self.invoice_set.filter(
maelis_notification_date__isnull=True,
lingo_notification_date__lt=now() - datetime.timedelta(minutes=15),
)
for invoice in invoices:
invoice.notify()
def hourly(self):
self.notify_invoices_paid()
def get_referential(self, referential_name, id=None, q=None, limit=None, distinct=True):
if id is not None:
queryset = self.referential.filter(referential_name=referential_name, item_id=id)
@ -3853,43 +3867,40 @@ class ToulouseMaelis(BaseResource, HTTPResource):
except SOAPServiceUnreachable:
pass
else:
last_update = now()
for item in result:
self.invoice_set.update_or_create(
resource_id=self.id,
regie_id=regie_id,
family_id=family_id,
invoice_id=item['numInvoice'],
defaults={
'maelis_data': item,
'updated': last_update,
},
)
try:
invoice = self.invoice_set.get(regie_id=regie_id, invoice_id=item['numInvoice'])
except Invoice.DoesNotExist:
invoice = self.invoice_set.create(
regie_id=regie_id,
invoice_id=item['numInvoice'],
family_id=family_id,
maelis_data=item,
maelis_data_update_date=now(),
)
self.logger.info("Ajout de %s sur la famille '%s'", repr(invoice), family_id)
else:
if invoice.family_id != family_id:
self.logger.error(
"%s sur la famille '%s' existe déjà sur la famille '%s'",
repr(invoice),
family_id,
invoice.family_id,
)
continue
content_one = json.dumps(invoice.maelis_data, sort_keys=True, indent=2)
content_two = json.dumps(item, sort_keys=True, indent=2, cls=DjangoJSONEncoder)
if content_one != content_two:
invoice.maelis_data = item
invoice.maelis_data_update_date = now()
invoice.save()
complete_diff = difflib.ndiff(content_two.split('\n'), content_one.split('\n'))
diff = [x for x in complete_diff if x[0] in ['-', '+']]
self.logger.info(
"Mise à jour de %s sur la famille '%s': %s", repr(invoice), family_id, diff

Tu peux logger des messages traduits.

Tu peux logger des messages traduits.

Fait.

Fait.
)
invoices = []
for item in self.invoice_set.filter(regie_id=regie_id, family_id=family_id):
item = item.maelis_data
invoice = {
'id': '%s-%s' % (item['numFamily'], item['numInvoice']),
'created': item['dateInvoice'][:10],
'pay_limit_date': item['dateDeadline'][:10],
'display_id': str(item['numInvoice']),
'total_amount': item['amountInvoice'],
'amount': str(float(item['amountInvoice']) - float(item['amountPaid'])),
'amount_paid': item['amountPaid'],
'label': item['libelleTTF'],
'has_pdf': bool(item['pdfName']),
'online_payment': True,
'paid': item['amountInvoice'] == item['amountPaid'],
'payment_date': None,
'no_online_payment_reason': None,
'reference_id': item['numInvoice'],
'maelis_item': item,
}
if item['amountInvoice'] == item['amountPaid']:
invoice.update({'amount': '0', 'pay_limit_date': '', 'online_payment': False})
invoices.append(invoice)
return invoices
return self.invoice_set.filter(regie_id=regie_id, family_id=family_id)
@endpoint(
display_category='Facture',
@ -3906,7 +3917,9 @@ class ToulouseMaelis(BaseResource, HTTPResource):
)
def invoices(self, request, regie_id, NameID=None, family_id=None):
family_id = family_id or self.get_link(NameID).family_id
invoices = [i for i in self.get_invoices(family_id, regie_id) if not i['paid']]
invoices = [
i.format_content() for i in self.get_invoices(family_id, regie_id) if i.status() == 'created'
]

Ça me semble une complexité superflue, et 10 secondes de perdues, on peut juste tout le temps créer le job asynchrone.

Plus globalement, je serais ici pour 1/ faire le job asynchrone, puis 2/ si dans le job asynchrone ça échoue, tant pis, 3/ avoir un job hourly qui prenne les factures en attente depuis plus de 15 minutes et qui essaie de les notifier. (histoire de ne pas empiler des jobs lors d'une indispo Maélis).

(et plus tard on pourrait aussi avoir l'info des factures qui sont en attente de notification depuis plus de N heures (ou jours?), ce qui révélerait une situation anormale côté Maélis)

Ça me semble une complexité superflue, et 10 secondes de perdues, on peut juste tout le temps créer le job asynchrone. Plus globalement, je serais ici pour 1/ faire le job asynchrone, puis 2/ si dans le job asynchrone ça échoue, tant pis, 3/ avoir un job hourly qui prenne les factures en attente depuis plus de 15 minutes et qui essaie de les notifier. (histoire de ne pas empiler des jobs lors d'une indispo Maélis). (et *plus tard* on pourrait aussi avoir l'info des factures qui sont en attente de notification depuis plus de N heures (ou jours?), ce qui révélerait une situation anormale côté Maélis)

Fait.

Fait.
return {'data': invoices}
@endpoint(
@ -3924,16 +3937,19 @@ class ToulouseMaelis(BaseResource, HTTPResource):
)
def invoices_history(self, request, regie_id, NameID=None, family_id=None):
family_id = family_id or self.get_link(NameID).family_id
invoices = [i for i in self.get_invoices(family_id, regie_id) if i['paid']]
invoices = [
i.format_content()
for i in self.get_invoices(family_id, regie_id)
if i.status() in ['paid', 'notified']
]
return {'data': invoices}
def get_invoice(self, regie_id, invoice_id):
real_invoice_id = invoice_id.split('-')[-1]
family_id = invoice_id[: -(len(real_invoice_id) + 1)]
for invoice in self.get_invoices(family_id, regie_id):
if invoice['id'] == invoice_id:
break
else:
try:
invoice = self.get_invoices(family_id, regie_id).get(invoice_id=real_invoice_id)
except Invoice.DoesNotExist:
raise APIError('Invoice not found')
return invoice
@ -3950,7 +3966,7 @@ class ToulouseMaelis(BaseResource, HTTPResource):
},
)
def invoice(self, request, regie_id, invoice_id, **kwargs):
return {'data': self.get_invoice(regie_id, invoice_id)}
return {'data': self.get_invoice(regie_id, invoice_id).format_content()}
@endpoint(
display_category='Facture',
@ -3973,19 +3989,26 @@ class ToulouseMaelis(BaseResource, HTTPResource):
)
def pay_invoice(self, request, regie_id, invoice_id, post_data, **kwargs):
invoice = self.get_invoice(regie_id, invoice_id)
if invoice.status() != 'created':
raise APIError('Invoice already paid')
self.call(
'Invoice',
'payInvoices',
numDossier=invoice['maelis_item']['numFamily'],
codeRegie=regie_id,
amount=invoice['amount'],
datePaiement=post_data['transaction_date'],
refTransaction=post_data['transaction_id'],
numInvoices=[invoice['display_id']],
numPerson=invoice['maelis_item']['payer']['num'],
invoice.lingo_data = post_data
invoice.lingo_notification_date = now()
invoice.save(update_fields=['updated', 'lingo_notification_date', 'lingo_data'])
self.add_job(
'notify_invoice_paid_job',
regie_id=regie_id,
invoice_id=invoice.invoice_id,
natural_id='%s/%s' % (regie_id, invoice.invoice_id),
)
return {'data': True}
return {'data': 'ok'}
def notify_invoice_paid_job(self, regie_id, invoice_id):
try:
invoice = self.invoice_set.get(regie_id=regie_id, invoice_id=invoice_id)
except Invoice.DoesNotExist:
return
invoice.notify()
@endpoint(
display_category='Facture',
@ -4073,14 +4096,77 @@ class Invoice(models.Model):
invoice_id = models.CharField(blank=False, max_length=128)
maelis_data = JSONField('Data', encoder=DjangoJSONEncoder)
lingo_data = JSONField('Data', encoder=DjangoJSONEncoder, null=True)
maelis_notification_data = JSONField(encoder=DjangoJSONEncoder, null=True)
created = models.DateTimeField('Created', auto_now_add=True)
updated = models.DateTimeField('Updated', auto_now=True)
canceled = models.DateTimeField('Canceled', null=True)
notified = models.DateTimeField('Notified', null=True)
maelis_data_update_date = models.DateTimeField(null=True)
lingo_notification_date = models.DateTimeField(null=True)
maelis_notification_date = models.DateTimeField(null=True)
def __repr__(self):
return '<Invoice "%s/%s">' % (self.regie_id, self.invoice_id)
def status(self):
if (
self.maelis_data['amountInvoice'] == self.maelis_data['amountPaid']
or self.maelis_notification_date is not None
):
return 'notified'
if self.lingo_notification_date is not None:
return 'paid'
return 'created'
def format_content(self):
item = self.maelis_data
paid = self.status() in ['paid', 'notified']
amount_paid = item['amountInvoice'] if paid else item['amountPaid']
invoice = {
'id': '%s-%s' % (item['numFamily'], item['numInvoice']),
'created': item['dateInvoice'][:10],
'pay_limit_date': item['dateDeadline'][:10],
'display_id': str(item['numInvoice']),
'total_amount': item['amountInvoice'],
'amount': str(Decimal(item['amountInvoice']) - Decimal(amount_paid)),
'amount_paid': amount_paid,
'label': item['libelleTTF'],
'has_pdf': bool(item['pdfName']),
'online_payment': True,
'paid': paid,
'payment_date': None,
'no_online_payment_reason': None,
'reference_id': item['numInvoice'],
'maelis_item': item,
}
if paid:
invoice.update({'pay_limit_date': '', 'online_payment': False})
return invoice
@transaction.atomic
def notify(self):
obj = Invoice.objects.select_for_update().get(pk=self.pk)

Et là je me rend compte que le code idiot dans lingo ne remonte pas le montant payé, je serai franchement pour ouvrir tout de suite un ticket pour avoir ça dans la notif et ici pouvoir écrite self.lingo_data['amount'] parce que si la facture a été partiellement payée par ailleurs on ne sait quel montant a été payé.

J'ai ouvert #76572 que je vais traiter de ce pas.

Et là je me rend compte que le code idiot dans lingo ne remonte pas le montant payé, je serai franchement pour ouvrir tout de suite un ticket pour avoir ça dans la notif et ici pouvoir écrite `self.lingo_data['amount']` parce que si la facture a été partiellement payée par ailleurs on ne sait quel montant a été payé. J'ai ouvert #76572 que je vais traiter de ce pas.
if obj.maelis_notification_date is not None:
return True
try:
result = obj.resource.call(
'Invoice',
'payInvoices',
numDossier=obj.family_id,
codeRegie=obj.regie_id,
amount=str(
Decimal(obj.maelis_data['amountInvoice']) - Decimal(obj.maelis_data['amountPaid'])
),
datePaiement=obj.lingo_data['transaction_date'],
refTransaction=obj.lingo_data['transaction_id'],
numInvoices=[obj.invoice_id],
numPerson=obj.maelis_data['payer']['num'],
)
except SOAPServiceUnreachable:
return False
obj.maelis_notification_date = now()
obj.maelis_notification_data = result
obj.save()
return True
class Meta:
ordering = ('resource', 'regie_id', 'invoice_id')
unique_together = [['resource', 'invoice_id']]
unique_together = [['resource', 'regie_id', 'invoice_id']]

View File

@ -9,7 +9,7 @@
<code>102</code>
<libelle>CANTINE / CLAE</libelle>
</regie>
<numFamily>322411</numFamily>
<numFamily>1312</numFamily>
<name>SIMPSON MARGE</name>
<amountInvoice>952503.6</amountInvoice>
<amountPaid>952503.6</amountPaid>
@ -109,7 +109,7 @@
<code>102</code>
<libelle>CANTINE / CLAE</libelle>
</regie>
<numFamily>322411</numFamily>
<numFamily>1312</numFamily>
<name>SIMPSON MARGE</name>
<amountInvoice>162.3</amountInvoice>
<amountPaid>0</amountPaid>

View File

@ -28,6 +28,7 @@ from zeep import Settings
from zeep.helpers import serialize_object
from passerelle.apps.base_adresse.models import BaseAdresse, CityModel
from passerelle.base.models import Job
from passerelle.contrib.toulouse_maelis.models import Link, Referential, ToulouseMaelis
from passerelle.contrib.toulouse_maelis.utils import get_public_criterias
from passerelle.utils.jsonresponse import APIError
@ -8825,7 +8826,7 @@ def test_create_nursery_demand_wrong_referential_key_error(con, app):
)
def test_invoices(invoice_service, con, app):
def test_invoices(invoice_service, con, app, caplog, freezer):
def request_check(request):
assert request.numDossier == 1312
assert request.codeRegie == 104
@ -8836,10 +8837,15 @@ def test_invoices(invoice_service, con, app):
)
url = get_endpoint('regie/104/invoices')
freezer.move_to('2023-03-03 18:00:00')
resp = app.get(url + '?family_id=1312')
assert resp.json['err'] == 0
Link.objects.create(resource=con, family_id='1312', name_id='local')
assert len([x for x in caplog.records if 'Ajout de <Invoice "104/' in x.message]) == 2
assert len([x for x in caplog.records if 'Mise à jour de <Invoice "104/' in x.message]) == 0
assert caplog.records[-1].levelno == logging.INFO
assert caplog.records[-1].message == 'Ajout de <Invoice "104/30"> sur la famille \'1312\''
Link.objects.create(resource=con, family_id='1312', name_id='local')
resp = app.get(url + '?NameID=local')
assert resp.json['err'] == 0
assert len(resp.json['data'])
@ -8851,7 +8857,7 @@ def test_invoices(invoice_service, con, app):
data = resp.json['data'][0]
del data['maelis_item']
assert data == {
'id': '322411-30',
'id': '1312-30',
'created': '2023-03-01',
'pay_limit_date': '2023-04-30',
'display_id': '30',
@ -8866,6 +8872,42 @@ def test_invoices(invoice_service, con, app):
'no_online_payment_reason': None,
'reference_id': '30',
}
assert len([x for x in caplog.records if 'Ajout de <Invoice "104/' in x.message]) == 2
assert len([x for x in caplog.records if 'Mise à jour de <Invoice "104/' in x.message]) == 0
# No change
freezer.move_to('2023-03-03 18:10:00')
resp = app.get(url + '?NameID=local')
assert resp.json['err'] == 0
assert len([x for x in caplog.records if 'Ajout de <Invoice "104/' in x.message]) == 2
assert len([x for x in caplog.records if 'Mise à jour de <Invoice "104/' in x.message]) == 0
invoice = con.invoice_set.get(regie_id=104, invoice_id=30)
assert invoice.created.strftime('%Y-%m-%d %H:%M:%S') == '2023-03-03 18:00:00'
assert invoice.updated.strftime('%Y-%m-%d %H:%M:%S') == '2023-03-03 18:00:00'
assert invoice.maelis_data_update_date.strftime('%Y-%m-%d %H:%M:%S') == '2023-03-03 18:00:00'
assert invoice.status() == 'created'
# Simulate change
invoice.maelis_data['pdfName'] = 'invoice.pdf'
invoice.save()
# Invoice updated
freezer.move_to('2023-03-03 18:20:00')
resp = app.get(url + '?NameID=local')
assert resp.json['err'] == 0
assert len([x for x in caplog.records if 'Ajout de <Invoice "104/' in x.message]) == 2
assert len([x for x in caplog.records if 'Mise à jour de <Invoice "104/' in x.message]) == 1
assert (
caplog.records[-1].message
== 'Mise à jour de <Invoice "104/30"> sur la famille \'1312\': [\'- "pdfName": null,\', \'+ "pdfName": "invoice.pdf",\']'
)
invoice = con.invoice_set.get(regie_id=104, invoice_id=30)
assert invoice.created.strftime('%Y-%m-%d %H:%M:%S') == '2023-03-03 18:00:00'
assert invoice.updated.strftime('%Y-%m-%d %H:%M:%S') == '2023-03-03 18:20:00'
assert invoice.maelis_data_update_date.strftime('%Y-%m-%d %H:%M:%S') == '2023-03-03 18:20:00'
assert invoice.status() == 'created'
@mock.patch('passerelle.utils.Request.get')
@ -8914,6 +8956,31 @@ def test_invoices_wrong_referential_key_error(con, app):
)
def test_invoices_unicity(invoice_service, con, app, caplog):
invoice_service.add_soap_response('readInvoices', get_xml_file('R_read_invoices.xml'))
url = get_endpoint('regie/104/invoices')
resp = app.get(url + '?family_id=1312')
assert resp.json['err'] == 0
assert con.invoice_set.filter(regie_id=104).count() == 2
assert con.invoice_set.filter(regie_id=109).count() == 0
url = get_endpoint('regie/109/invoices')
resp = app.get(url + '?family_id=1312')
assert resp.json['err'] == 0
assert con.invoice_set.filter(regie_id=109).count() == 2
url = get_endpoint('regie/104/invoices')
resp = app.get(url + '?family_id=1234')
assert resp.json['err'] == 0
assert con.invoice_set.filter(regie_id=104).count() == 2
assert caplog.records[-1].levelno == logging.ERROR
assert [x.message for x in caplog.records][-2:] == [
'<Invoice "104/8"> sur la famille \'1234\' existe déjà sur la famille \'1312\'',
'<Invoice "104/30"> sur la famille \'1234\' existe déjà sur la famille \'1312\'',
]
def test_invoices_history(invoice_service, con, app):
def request_check(request):
assert request.numDossier == 1312
@ -8939,12 +9006,12 @@ def test_invoices_history(invoice_service, con, app):
data = resp.json['data'][0]
del data['maelis_item']
assert data == {
'id': '322411-8',
'id': '1312-8',
'created': '2023-02-24',
'pay_limit_date': '',
'display_id': '8',
'total_amount': '952503.6',
'amount': '0',
'amount': '0.0',
'amount_paid': '952503.6',
'label': 'CLAE JANVIER 2023',
'has_pdf': False,
@ -8955,6 +9022,9 @@ def test_invoices_history(invoice_service, con, app):
'reference_id': '8',
}
invoice = con.invoice_set.get(regie_id=104, invoice_id=8)
assert invoice.status() == 'notified'
def test_invoices_history_not_linked_error(con, app):
url = get_endpoint('regie/104/invoices/history')
@ -8975,14 +9045,14 @@ def test_invoices_history_wrong_referential_key_error(con, app):
def test_invoice(invoice_service, con, app):
def request_check(request):
assert request.numDossier == 322411
assert request.numDossier == 1312
assert request.codeRegie == 104
assert request.dateStart == datetime.datetime(1970, 1, 1, 0, 0)
invoice_service.add_soap_response(
'readInvoices', get_xml_file('R_read_invoices.xml'), request_check=request_check
)
url = get_endpoint('regie/104/invoice/322411-8')
url = get_endpoint('regie/104/invoice/1312-8')
resp = app.get(url + '?NameID=ignored')
assert resp.json['err'] == 0
@ -8991,7 +9061,7 @@ def test_invoice(invoice_service, con, app):
def test_invoice_wrong_referential_key_error(con, app):
url = get_endpoint('regie/plop/invoice/322411-8')
url = get_endpoint('regie/plop/invoice/1312-8')
resp = app.get(url)
assert resp.json['err'] == 1
assert (
@ -9008,36 +9078,138 @@ def test_invoice_not_found_invoice(invoice_service, con, app):
assert resp.json['err_desc'] == 'Invoice not found'
def test_pay_invoice(invoice_service, con, app):
def test_pay_invoice(invoice_service, con, app, freezer):
invoice_service.add_soap_response('readInvoices', get_xml_file('R_read_invoices.xml'))
resp = app.get(get_endpoint('regie/104/invoice/1312-30') + '?family_id=1312')
assert resp.json['err'] == 0
assert resp.json['data']['amount_paid'] == '0'
assert resp.json['data']['paid'] is False
assert resp.json['data']['amount'] == '162.3'
assert resp.json['data']['online_payment'] is True
assert resp.json['data']['pay_limit_date'] == '2023-04-30'
url = get_endpoint('regie/104/invoice/1312-30/pay/')
data = {
'transaction_date': '2023-03-03T18:38:00',
'transaction_id': 'xxx',
}
freezer.move_to('2023-03-03 18:39:00')
resp = app.post_json(url + '?NameID=ignored', params=data)
assert resp.json['err'] == 0
invoice = con.invoice_set.get(regie_id=104, invoice_id=30)
assert invoice.lingo_notification_date.strftime('%Y-%m-%d %H:%M:%S') == '2023-03-03 18:39:00'
assert invoice.maelis_notification_date is None
assert invoice.status() == 'paid'
resp = app.get(get_endpoint('regie/104/invoice/1312-30') + '?family_id=1312')
assert resp.json['err'] == 0
assert resp.json['data']['amount_paid'] == '162.3'
assert resp.json['data']['paid'] is True
assert resp.json['data']['amount'] == '0.0'
assert resp.json['data']['online_payment'] is False
assert resp.json['data']['pay_limit_date'] == ''
def test_pay_invoice_job(invoice_service, con, app, freezer):
def request_check(request):
assert dict(serialize_object(request)) == {
'numDossier': 322411,
'numDossier': 1312,
'numPerson': 261483,
'lastName': None,
'firstName': None,
'codeRegie': 104,
'amount': 0.0,
'amount': 162.3,
'datePaiement': datetime.datetime(2023, 3, 3, 18, 38),
'refTransaction': 'xxx',
'numInvoices': [8],
'numInvoices': [30],
}
invoice_service.add_soap_response('readInvoices', get_xml_file('R_read_invoices.xml'))
invoice_service.add_soap_response(
'payInvoices', get_xml_file('R_pay_invoices.xml'), request_check=request_check
)
url = get_endpoint('regie/104/invoice/322411-8/pay/')
url = get_endpoint('regie/104/invoice/1312-30/pay/')
data = {
'transaction_date': '2023-03-03T18:38:00',
'transaction_id': 'xxx',
}
freezer.move_to('2023-03-03 18:39:00')
resp = app.post_json(url + '?NameID=ignored', params=data)
assert resp.json['err'] == 0
invoice = con.invoice_set.get(regie_id=104, invoice_id=30)
assert invoice.lingo_notification_date.strftime('%Y-%m-%d %H:%M:%S') == '2023-03-03 18:39:00'
assert invoice.maelis_notification_date is None
assert invoice.maelis_notification_data is None
assert invoice.status() == 'paid'
job = Job.objects.get(method_name='notify_invoice_paid_job', natural_id='104/30')
assert job.status == 'registered'
freezer.move_to('2023-03-03 18:40:00')
con.jobs()
invoice = con.invoice_set.get(regie_id=104, invoice_id=30)
assert invoice.maelis_notification_date.strftime('%Y-%m-%d %H:%M:%S') == '2023-03-03 18:40:00'
assert invoice.maelis_notification_data == 4
assert invoice.status() == 'notified'
job = Job.objects.get(method_name='notify_invoice_paid_job', natural_id='104/30')
assert job.status == 'completed'
assert job.update_timestamp > job.creation_timestamp
def test_pay_invoice_cron(invoice_service, con, app, freezer):
def request_check(request):
assert dict(serialize_object(request)) == {
'numDossier': 1312,
'numPerson': 261483,
'lastName': None,
'firstName': None,
'codeRegie': 104,
'amount': 162.3,
'datePaiement': datetime.datetime(2023, 3, 3, 18, 38),
'refTransaction': 'xxx',
'numInvoices': [30],
}
invoice_service.add_soap_response('readInvoices', get_xml_file('R_read_invoices.xml'))
invoice_service.add_soap_response(
'payInvoices', get_xml_file('R_pay_invoices.xml'), request_check=request_check
)
url = get_endpoint('regie/104/invoice/1312-30/pay/')
data = {
'transaction_date': '2023-03-03T18:38:00',
'transaction_id': 'xxx',
}
freezer.move_to('2023-03-03 18:39:00')
resp = app.post_json(url + '?NameID=ignored', params=data)
assert resp.json['err'] == 0
invoice = con.invoice_set.get(regie_id=104, invoice_id=30)
assert invoice.lingo_notification_date.strftime('%Y-%m-%d %H:%M:%S') == '2023-03-03 18:39:00'
assert invoice.maelis_notification_date is None
assert invoice.maelis_notification_data is None
assert invoice.status() == 'paid'
freezer.move_to('2023-03-03 18:40:00')
con.notify_invoices_paid()
invoice = con.invoice_set.get(regie_id=104, invoice_id=30)
assert invoice.maelis_notification_date is None
assert invoice.maelis_notification_data is None
assert invoice.status() == 'paid'
freezer.move_to('2023-03-03 18:55:00')
con.notify_invoices_paid()
invoice = con.invoice_set.get(regie_id=104, invoice_id=30)
assert invoice.maelis_notification_date.strftime('%Y-%m-%d %H:%M:%S') == '2023-03-03 18:55:00'
assert invoice.maelis_notification_data == 4
assert invoice.status() == 'notified'
def test_pay_invoice_wrong_referential_key_error(con, app):
url = get_endpoint('regie/plop/invoice/322411-8/pay/')
url = get_endpoint('regie/plop/invoice/1312-8/pay/')
data = {
'transaction_date': '2023-03-03T18:38:00',
'transaction_id': 'xxx',
@ -9050,6 +9222,31 @@ def test_pay_invoice_wrong_referential_key_error(con, app):
)
def test_pay_invoice_not_found_invoice(invoice_service, con, app):
invoice_service.add_soap_response('readInvoices', get_xml_file('R_read_invoices.xml'))
url = get_endpoint('regie/104/invoice/1-2/pay')
data = {
'transaction_date': '2023-03-03T18:38:00',
'transaction_id': 'xxx',
}
resp = app.post_json(url, params=data)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'Invoice not found'
def test_pay_historical_invoice(invoice_service, con, app):
invoice_service.add_soap_response('readInvoices', get_xml_file('R_read_invoices.xml'))
url = get_endpoint('regie/104/invoice/1312-8/pay/')
data = {
'transaction_date': '2023-03-03T18:38:00',
'transaction_id': 'xxx',
}
resp = app.post_json(url + '?NameID=ignored', params=data)
assert resp.json['err'] == 1
assert resp.json['err_desc'] == 'Invoice already paid'
def test_invoice_pdf(invoice_service, con, app):
def request_check(request):
assert request.codeRegie == 104