toulouse-maelis: notification asynchrone du paiement des factures (#76394) #182
|
@ -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',
|
||||
),
|
||||
]
|
|
@ -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
|
||||
|
||||
)
|
||||
|
||||
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'
|
||||
]
|
||||
fpeters
commented
Ç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)
nroche
commented
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)
|
||||
bdauvergne
commented
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 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']]
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
Tu peux logger des messages traduits.
Fait.