toulouse-maelis: trigger wcs when basket subscription is removed (#76398)
This commit is contained in:
parent
80fc536e2b
commit
0276de78c2
|
@ -21,10 +21,11 @@ import json
|
|||
import re
|
||||
from decimal import Decimal
|
||||
from operator import itemgetter
|
||||
from urllib.parse import urljoin
|
||||
from urllib.parse import urljoin, urlparse
|
||||
|
||||
import zeep
|
||||
from dateutil import rrule
|
||||
from django.conf import settings
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.db import models, transaction
|
||||
from django.db.models import JSONField
|
||||
|
@ -43,6 +44,7 @@ from passerelle.utils.conversion import simplify
|
|||
from passerelle.utils.jsonresponse import APIError
|
||||
from passerelle.utils.soap import SOAPFault, SOAPServiceUnreachable
|
||||
from passerelle.utils.templates import render_to_string
|
||||
from passerelle.utils.wcs import WcsApi, WcsApiError
|
||||
|
||||
from . import activity_schemas, family_schemas, invoice_schemas, schemas, utils
|
||||
|
||||
|
@ -310,9 +312,24 @@ class ToulouseMaelis(BaseResource, HTTPResource):
|
|||
):
|
||||
invoice.cancel()
|
||||
|
||||
def trigger_subscriptions_cron(self):
|
||||
# find subscriptions removed from baskets
|
||||
pending_basket_subscriptions = self.subscription_set.filter(invoice__isnull=True)
|
||||
family_ids = {x.family_id for x in pending_basket_subscriptions}
|
||||
for family_id in family_ids:
|
||||
self.get_baskets_raw(family_id)
|
||||
|
||||
subscriptions = self.subscription_set.filter(
|
||||
wcs_trigger_date__isnull=True,
|
||||
)
|
||||
for subscription in subscriptions:
|
||||
if subscription.trigger_status() == 'triggering':
|
||||
subscription.trigger()
|
||||
|
||||
def hourly(self):
|
||||
self.notify_invoices_paid()
|
||||
self.cancel_basket_invoices()
|
||||
self.trigger_subscriptions_cron()
|
||||
|
||||
def get_referential(self, referential_name, id=None, q=None, limit=None, distinct=True):
|
||||
if id is not None:
|
||||
|
@ -4158,6 +4175,13 @@ class ToulouseMaelis(BaseResource, HTTPResource):
|
|||
return
|
||||
invoice.notify()
|
||||
|
||||
def trigger_subscription_job(self, pk):
|
||||
try:
|
||||
subscription = self.subscription_set.get(pk=pk)
|
||||
except Invoice.DoesNotExist:
|
||||
return
|
||||
subscription.trigger()
|
||||
|
||||
@endpoint(
|
||||
display_category='Facture',
|
||||
name='regie',
|
||||
|
@ -4392,13 +4416,86 @@ class Subscription(models.Model):
|
|||
return 'pending_basket'
|
||||
|
||||
def trigger_status(self):
|
||||
if self.status() in ['paid', 'cancelled', 'removed']:
|
||||
if self.wcs_trigger_date is not None:
|
||||
# wcs demand was triggered
|
||||
return 'triggered'
|
||||
if self.status() in ['removed']:
|
||||
# wcs demand can be triggered
|
||||
return 'triggering'
|
||||
else:
|
||||
# waiting for a definive subscription status
|
||||
return 'pending'
|
||||
|
||||
def set_trigger(self):
|
||||
if self.trigger_status() != 'triggering':
|
||||
return
|
||||
if self.wcs_trigger_payload:
|
||||
return
|
||||
self.wcs_trigger_payload = {
|
||||
'err': 1 if self.status() in ['removed'] else 0,
|
||||
'data': {
|
||||
'regie_id': self.regie_id,
|
||||
'regie_text': self.resource.get_referential_value('Regie', self.regie_id),
|
||||
'invoice_id': self.invoice.invoice_id if self.invoice else None,
|
||||
'invoice_status': self.invoice.status() if self.invoice else None,
|
||||
'invoice_data': self.invoice.maelis_data if self.invoice else None,
|
||||
'subscription_id': self.pk,
|
||||
'subscription_status': self.status(),
|
||||
'subscription_data': self.maelis_data,
|
||||
},
|
||||
}
|
||||
if self.status() == 'removed':
|
||||
self.wcs_trigger_payload['err_desc'] = "Le panier n'a pas été validé"
|
||||
self.save()
|
||||
self.resource.add_job(
|
||||
'trigger_subscription_job',
|
||||
pk=self.pk,
|
||||
natural_id='%s/%s' % (self.wcs_form_number, self.pk),
|
||||
)
|
||||
|
||||
def get_wcs_api(self, base_url):
|
||||
scheme, netloc, dummy, dummy, dummy, dummy = urlparse(base_url)
|
||||
services = settings.KNOWN_SERVICES.get('wcs', {})
|
||||
service = None
|
||||
for service in services.values():
|
||||
remote_url = service.get('url')
|
||||
r_scheme, r_netloc, dummy, dummy, dummy, dummy = urlparse(remote_url)
|
||||
if r_scheme == scheme and r_netloc == netloc:
|
||||
return WcsApi(
|
||||
base_url,
|
||||
orig=service.get('orig'),
|
||||
key=service.get('secret'),
|
||||
session=self.resource.requests,
|
||||
)
|
||||
|
||||
@transaction.atomic
|
||||
def trigger(self):
|
||||
obj = Subscription.objects.select_for_update().get(pk=self.pk)
|
||||
if obj.trigger_status() != 'triggering':
|
||||
return
|
||||
base_url = '%shooks/update_subscription/' % (obj.wcs_form_api_url)
|
||||
wcs_api = obj.get_wcs_api(base_url)
|
||||
if not wcs_api:
|
||||
err_desc = 'Cannot find wcs service for %s' % obj.wcs_form_api_url
|
||||
self.resource.logger.warning(err_desc)
|
||||
result = err_desc
|
||||
else:
|
||||
headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
}
|
||||
self.resource.logger.info(
|
||||
'trigger wcs: %s -> %s' % (base_url, self.wcs_trigger_payload['data']['subscription_status'])
|
||||
)
|
||||
try:
|
||||
result = wcs_api.post_json(obj.wcs_trigger_payload, [], headers=headers)
|
||||
except WcsApiError as e:
|
||||
self.resource.logger.warning(e)
|
||||
return
|
||||
obj.wcs_trigger_date = now()
|
||||
obj.wcs_trigger_response = result
|
||||
obj.save()
|
||||
|
||||
class Meta:
|
||||
ordering = ('resource', 'wcs_form_number')
|
||||
unique_together = [['resource', 'wcs_form_number']]
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
import base64
|
||||
import datetime
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from unittest import mock
|
||||
|
@ -138,6 +139,21 @@ def ape_service():
|
|||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def wcs_service(settings, requests_mock):
|
||||
wcs_service = {
|
||||
'default': {
|
||||
'title': 'test',
|
||||
'url': 'https://wcs.example.com',
|
||||
'secret': 'xxx',
|
||||
'orig': 'passerelle',
|
||||
},
|
||||
}
|
||||
settings.KNOWN_SERVICES = {'wcs': wcs_service}
|
||||
with requests_mock as mock:
|
||||
yield mock
|
||||
|
||||
|
||||
@pytest.fixture(scope='module')
|
||||
def django_db_setup(django_db_setup, django_db_blocker):
|
||||
with django_db_blocker.unblock():
|
||||
|
@ -10131,3 +10147,226 @@ def test_invoice_pdf_error(invoice_service, con, app):
|
|||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_class'] == 'django.http.response.Http404'
|
||||
assert resp.json['err_desc'] == 'Fichier PDF non trouvé'
|
||||
|
||||
|
||||
def test_trigger_wcs_on_removed_subscriptions_cron(
|
||||
family_service, activity_service, wcs_service, con, app, freezer, caplog
|
||||
):
|
||||
family_service.add_soap_response('readFamily', get_xml_file('R_read_family_for_subscription.xml'))
|
||||
activity_service.add_soap_response('getPersonUnitInfo', get_xml_file('R_get_person_unit_info.xml'))
|
||||
activity_service.add_soap_response('addPersonUnitBasket', get_xml_file('R_add_person_unit_basket.xml'))
|
||||
activity_service.add_soap_response('getFamilyBasket', get_xml_file('R_get_family_basket_empty.xml'))
|
||||
wcs_service.add(
|
||||
responses.POST,
|
||||
'https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/hooks/update_subscription/',
|
||||
json={'err': 0},
|
||||
status=200,
|
||||
)
|
||||
Link.objects.create(resource=con, family_id='1312', name_id='local')
|
||||
|
||||
# subscribe providing a wcs demand
|
||||
freezer.move_to('2023-03-03 18:20:00')
|
||||
resp = app.post_json(
|
||||
get_endpoint('add-person-basket-subscription') + '?NameID=local',
|
||||
params={
|
||||
'person_id': '266145',
|
||||
'activity_id': 'A10053179798',
|
||||
'unit_id': 'A10053179809',
|
||||
'place_id': 'A10053179757',
|
||||
'start_date': '2023-02-01',
|
||||
'end_date': '2023-06-30',
|
||||
'form_api_url': 'https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/',
|
||||
'form_number': '13-12',
|
||||
},
|
||||
)
|
||||
assert resp.json['err'] == 0
|
||||
|
||||
subscription = con.subscription_set.get(wcs_form_number='13-12')
|
||||
assert subscription.status() == 'pending_basket'
|
||||
assert [x['idIns'] for x in subscription.maelis_data['basket']['lignes']] == ['S10055641658']
|
||||
|
||||
# basket was removed, send trigger to wcs
|
||||
con.hourly()
|
||||
assert (
|
||||
'https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/hooks/update_subscription/'
|
||||
in wcs_service.calls[-1].request.url
|
||||
)
|
||||
trigger_body = json.loads(wcs_service.calls[-1].request.body)
|
||||
assert trigger_body['err'] == 1
|
||||
assert trigger_body['err_desc'] == "Le panier n'a pas été validé"
|
||||
assert trigger_body['data']['subscription_status'] == 'removed'
|
||||
assert trigger_body['data']['regie_text'] == 'DSBL'
|
||||
assert any(['trigger wcs' in x.message for x in caplog.records])
|
||||
|
||||
subscription = con.subscription_set.get(wcs_form_number='13-12')
|
||||
assert subscription.trigger_status() == 'triggered'
|
||||
|
||||
|
||||
def test_trigger_wcs_on_removed_subscriptions_job(
|
||||
family_service, activity_service, wcs_service, con, app, freezer, caplog
|
||||
):
|
||||
family_service.add_soap_response('readFamily', get_xml_file('R_read_family_for_subscription.xml'))
|
||||
activity_service.add_soap_response('getPersonUnitInfo', get_xml_file('R_get_person_unit_info.xml'))
|
||||
activity_service.add_soap_response('addPersonUnitBasket', get_xml_file('R_add_person_unit_basket.xml'))
|
||||
activity_service.add_soap_response('getFamilyBasket', get_xml_file('R_get_family_basket_empty.xml'))
|
||||
wcs_service.add(
|
||||
responses.POST,
|
||||
'https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/hooks/update_subscription/',
|
||||
json={'err': 0},
|
||||
status=200,
|
||||
)
|
||||
Link.objects.create(resource=con, family_id='1312', name_id='local')
|
||||
|
||||
# subscribe providing a wcs demand
|
||||
freezer.move_to('2023-03-03 18:20:00')
|
||||
resp = app.post_json(
|
||||
get_endpoint('add-person-basket-subscription') + '?NameID=local',
|
||||
params={
|
||||
'person_id': '266145',
|
||||
'activity_id': 'A10053179798',
|
||||
'unit_id': 'A10053179809',
|
||||
'place_id': 'A10053179757',
|
||||
'start_date': '2023-02-01',
|
||||
'end_date': '2023-06-30',
|
||||
'form_api_url': 'https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/',
|
||||
'form_number': '13-12',
|
||||
},
|
||||
)
|
||||
assert resp.json['err'] == 0
|
||||
|
||||
subscription = con.subscription_set.get(wcs_form_number='13-12')
|
||||
assert subscription.status() == 'pending_basket'
|
||||
assert [x['idIns'] for x in subscription.maelis_data['basket']['lignes']] == ['S10055641658']
|
||||
|
||||
# get basket having subscription removed
|
||||
resp = app.get(get_endpoint('get-baskets') + '?NameID=local')
|
||||
assert resp.json['err'] == 0
|
||||
|
||||
subscription = con.subscription_set.get(wcs_form_number='13-12')
|
||||
assert subscription.status() == 'removed'
|
||||
assert subscription.trigger_status() == 'triggering'
|
||||
|
||||
# send trigger to wcs
|
||||
con.jobs()
|
||||
assert (
|
||||
'https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/hooks/update_subscription/'
|
||||
in wcs_service.calls[-1].request.url
|
||||
)
|
||||
trigger_body = json.loads(wcs_service.calls[-1].request.body)
|
||||
assert trigger_body['err'] == 1
|
||||
assert trigger_body['err_desc'] == "Le panier n'a pas été validé"
|
||||
assert trigger_body['data']['subscription_status'] == 'removed'
|
||||
assert trigger_body['data']['regie_text'] == 'DSBL'
|
||||
assert any(['trigger wcs' in x.message for x in caplog.records])
|
||||
|
||||
subscription = con.subscription_set.get(wcs_form_number='13-12')
|
||||
assert subscription.trigger_status() == 'triggered'
|
||||
|
||||
|
||||
def test_trigger_wcs_service_error(family_service, activity_service, con, app, freezer, caplog):
|
||||
family_service.add_soap_response('readFamily', get_xml_file('R_read_family_for_subscription.xml'))
|
||||
activity_service.add_soap_response('getPersonUnitInfo', get_xml_file('R_get_person_unit_info.xml'))
|
||||
activity_service.add_soap_response('addPersonUnitBasket', get_xml_file('R_add_person_unit_basket.xml'))
|
||||
activity_service.add_soap_response('getFamilyBasket', get_xml_file('R_get_family_basket_empty.xml'))
|
||||
Link.objects.create(resource=con, family_id='1312', name_id='local')
|
||||
|
||||
# subscribe providing a wcs demand
|
||||
freezer.move_to('2023-03-03 18:20:00')
|
||||
resp = app.post_json(
|
||||
get_endpoint('add-person-basket-subscription') + '?NameID=local',
|
||||
params={
|
||||
'person_id': '266145',
|
||||
'activity_id': 'A10053179798',
|
||||
'unit_id': 'A10053179809',
|
||||
'place_id': 'A10053179757',
|
||||
'start_date': '2023-02-01',
|
||||
'end_date': '2023-06-30',
|
||||
'form_api_url': 'https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/',
|
||||
'form_number': '13-12',
|
||||
},
|
||||
)
|
||||
assert resp.json['err'] == 0
|
||||
|
||||
subscription = con.subscription_set.get(wcs_form_number='13-12')
|
||||
assert subscription.status() == 'pending_basket'
|
||||
|
||||
# basket was removed, send trigger to wcs
|
||||
con.hourly()
|
||||
assert caplog.records[-1].levelno == logging.WARNING
|
||||
assert (
|
||||
caplog.records[-1].message
|
||||
== 'Cannot find wcs service for https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/'
|
||||
)
|
||||
|
||||
subscription = con.subscription_set.get(wcs_form_number='13-12')
|
||||
assert subscription.trigger_status() == 'triggered'
|
||||
assert (
|
||||
subscription.wcs_trigger_response
|
||||
== 'Cannot find wcs service for https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/'
|
||||
)
|
||||
|
||||
|
||||
def test_trigger_wcs_api_error(family_service, activity_service, wcs_service, con, app, freezer):
|
||||
family_service.add_soap_response('readFamily', get_xml_file('R_read_family_for_subscription.xml'))
|
||||
activity_service.add_soap_response('getPersonUnitInfo', get_xml_file('R_get_person_unit_info.xml'))
|
||||
activity_service.add_soap_response('addPersonUnitBasket', get_xml_file('R_add_person_unit_basket.xml'))
|
||||
activity_service.add_soap_response('getFamilyBasket', get_xml_file('R_get_family_basket_empty.xml'))
|
||||
Link.objects.create(resource=con, family_id='1312', name_id='local')
|
||||
|
||||
# subscribe providing a wcs demand
|
||||
freezer.move_to('2023-03-03 18:20:00')
|
||||
resp = app.post_json(
|
||||
get_endpoint('add-person-basket-subscription') + '?NameID=local',
|
||||
params={
|
||||
'person_id': '266145',
|
||||
'activity_id': 'A10053179798',
|
||||
'unit_id': 'A10053179809',
|
||||
'place_id': 'A10053179757',
|
||||
'start_date': '2023-02-01',
|
||||
'end_date': '2023-06-30',
|
||||
'form_api_url': 'https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/',
|
||||
'form_number': '13-12',
|
||||
},
|
||||
)
|
||||
assert resp.json['err'] == 0
|
||||
|
||||
subscription = con.subscription_set.get(wcs_form_number='13-12')
|
||||
assert subscription.status() == 'pending_basket'
|
||||
|
||||
assert len([x for x in wcs_service.calls if '/hooks/' in x.request.url]) == 0
|
||||
|
||||
# basket was removed, send trigger to wcs
|
||||
wcs_service.add(
|
||||
responses.POST,
|
||||
'https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/hooks/update_subscription/',
|
||||
json={'err': 1, 'err_class': 'Access denied', 'err_desc': None},
|
||||
status=403,
|
||||
)
|
||||
con.hourly()
|
||||
assert len([x for x in wcs_service.calls if '/hooks/' in x.request.url]) == 1
|
||||
subscription = con.subscription_set.get(wcs_form_number='13-12')
|
||||
assert subscription.trigger_status() == 'triggering'
|
||||
|
||||
# retry
|
||||
wcs_service.add(
|
||||
responses.POST,
|
||||
'https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/hooks/update_subscription/',
|
||||
body=CONNECTION_ERROR,
|
||||
)
|
||||
con.hourly()
|
||||
assert len([x for x in wcs_service.calls if '/hooks/' in x.request.url]) == 2
|
||||
subscription = con.subscription_set.get(wcs_form_number='13-12')
|
||||
assert subscription.trigger_status() == 'triggering'
|
||||
|
||||
# retry again
|
||||
wcs_service.add(
|
||||
responses.POST,
|
||||
'https://wcs.example.com/api/forms/exemple-inscription-loisirs-1/12/hooks/update_subscription/',
|
||||
body='plop',
|
||||
status=500,
|
||||
)
|
||||
con.hourly()
|
||||
assert len([x for x in wcs_service.calls if '/hooks/' in x.request.url]) == 3
|
||||
subscription = con.subscription_set.get(wcs_form_number='13-12')
|
||||
assert subscription.trigger_status() == 'triggering'
|
||||
assert subscription.wcs_trigger_response is None
|
||||
|
|
Loading…
Reference in New Issue