toulouse-maelis: make payInvoices call asynchronous (#76394)
gitea/passerelle/pipeline/head This commit looks good Details

This commit is contained in:
Nicolas Roche 2023-04-12 18:34:20 +02:00
parent 324b787f33
commit 29f4774d2b
2 changed files with 176 additions and 16 deletions

View File

@ -26,7 +26,7 @@ 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
@ -278,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)
@ -3978,21 +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')
result = self.call(
'Invoice',
'payInvoices',
numDossier=invoice.family_id,
codeRegie=regie_id,
amount=str(
Decimal(invoice.maelis_data['amountInvoice']) - Decimal(invoice.maelis_data['amountPaid'])
),
datePaiement=post_data['transaction_date'],
refTransaction=post_data['transaction_id'],
numInvoices=[invoice.invoice_id],
numPerson=invoice.maelis_data['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': result}
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',
@ -4125,6 +4141,32 @@ class Invoice(models.Model):
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)
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', 'regie_id', 'invoice_id']]

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
@ -9021,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')
@ -9074,7 +9078,41 @@ 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': 1312,
@ -9092,15 +9130,82 @@ def test_pay_invoice(invoice_service, con, app):
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
assert resp.json['data'] == 4
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):
@ -9129,6 +9234,19 @@ def test_pay_invoice_not_found_invoice(invoice_service, con, app):
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