pricing: test tool for agenda pricing (#66892)
This commit is contained in:
parent
6fef766eab
commit
41eda72b2e
|
@ -17,12 +17,17 @@
|
|||
import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from requests.exceptions import RequestException
|
||||
|
||||
from lingo.agendas.models import Agenda
|
||||
from lingo.utils import requests
|
||||
|
||||
|
||||
class ChronoError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def is_chrono_enabled():
|
||||
return hasattr(settings, 'KNOWN_SERVICES') and settings.KNOWN_SERVICES.get('chrono')
|
||||
|
||||
|
@ -103,3 +108,27 @@ def refresh_agendas():
|
|||
for slug, agenda in existing_agendas.items():
|
||||
if slug not in seen_agendas:
|
||||
agenda.delete()
|
||||
|
||||
|
||||
def get_event(event_slug):
|
||||
result = get_chrono_json('api/agendas/events/?slots=%s' % event_slug)
|
||||
if not result:
|
||||
raise ChronoError(_('Unable to get event details'))
|
||||
if result.get('err'):
|
||||
raise ChronoError(_('Unable to get event details (%s)') % result['err_desc'])
|
||||
if not result.get('data'):
|
||||
raise ChronoError(_('Unable to get event details'))
|
||||
return result['data'][0]
|
||||
|
||||
|
||||
def get_subscriptions(agenda_slug, user_external_id):
|
||||
result = get_chrono_json(
|
||||
'api/agenda/%s/subscription/?user_external_id=%s' % (agenda_slug, user_external_id)
|
||||
)
|
||||
if not result or not result.get('data'):
|
||||
raise ChronoError(_('Unable to get subscription details'))
|
||||
if result.get('err'):
|
||||
raise ChronoError(_('Unable to get subscription details (%s)') % result['err_desc'])
|
||||
if not result.get('data'):
|
||||
raise ChronoError(_('Unable to get subscription details'))
|
||||
return result['data']
|
||||
|
|
|
@ -47,3 +47,8 @@ ul.objects-list.sortable {
|
|||
ul.objects-list.single-links li a.link::before {
|
||||
content: "\f08e"; /* fa-external-link */
|
||||
}
|
||||
|
||||
div.test-tool-result .infonotice h3 {
|
||||
margin-top: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
|
|
@ -14,14 +14,17 @@
|
|||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import datetime
|
||||
|
||||
from django import forms
|
||||
from django.forms import ValidationError
|
||||
from django.template import Template, TemplateSyntaxError
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from lingo.agendas.chrono import ChronoError, get_event, get_subscriptions
|
||||
from lingo.agendas.models import CheckType
|
||||
from lingo.pricing.models import AgendaPricing, Criteria, CriteriaCategory
|
||||
from lingo.pricing.models import AgendaPricing, Criteria, CriteriaCategory, PricingError
|
||||
|
||||
|
||||
class ExportForm(forms.Form):
|
||||
|
@ -162,6 +165,112 @@ class PricingMatrixForm(forms.Form):
|
|||
self.fields['crit_%i' % i] = forms.DecimalField(required=True, max_digits=5, decimal_places=2)
|
||||
|
||||
|
||||
class PricingTestToolForm(forms.Form):
|
||||
event_slug = forms.CharField(label=_('Event identifier'))
|
||||
user_external_id = forms.CharField(label=_('User external identifier'))
|
||||
adult_external_id = forms.CharField(label=_('Adult external identifier'))
|
||||
booking_status = forms.ChoiceField(label=_('Booking status'), choices=[])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.request = kwargs.pop('request')
|
||||
self.agenda_pricing = kwargs.pop('agenda_pricing')
|
||||
self.agenda = self.agenda_pricing.agenda
|
||||
self.serialized_event = None
|
||||
self.serialized_subscription = None
|
||||
self.check_type_slug = None
|
||||
self.booking_status = None
|
||||
super().__init__(*args, **kwargs)
|
||||
presence_check_types = (
|
||||
self.agenda.check_type_group.check_types.presences() if self.agenda.check_type_group else []
|
||||
)
|
||||
absence_check_types = (
|
||||
self.agenda.check_type_group.check_types.absences() if self.agenda.check_type_group else []
|
||||
)
|
||||
status_choices = [
|
||||
('presence', _('Presence')),
|
||||
]
|
||||
status_choices += [
|
||||
('presence::%s' % ct.slug, _('Presence (%s)') % ct.label) for ct in presence_check_types
|
||||
]
|
||||
status_choices += [('absence', _('Absence'))]
|
||||
status_choices += [
|
||||
('absence::%s' % ct.slug, _('Absence (%s)') % ct.label) for ct in absence_check_types
|
||||
]
|
||||
self.fields['booking_status'].choices = status_choices
|
||||
|
||||
def clean_event_slug(self):
|
||||
original_event_slug = self.cleaned_data['event_slug']
|
||||
event_slug = original_event_slug
|
||||
if '@' not in event_slug:
|
||||
# chrono's endpoint takes event ids like '<agenda_slug>@<event_slug>'
|
||||
event_slug = '%s@%s' % (self.agenda.slug, event_slug)
|
||||
elif event_slug.split('@')[0] != self.agenda.slug:
|
||||
raise ValidationError(_('The agenda identifier is wrong (%s)') % event_slug.split('@')[0])
|
||||
try:
|
||||
self.serialized_event = get_event(event_slug)
|
||||
except ChronoError as e:
|
||||
raise forms.ValidationError(str(e).replace(event_slug, original_event_slug))
|
||||
|
||||
event_date = datetime.datetime.fromisoformat(self.serialized_event['start_datetime']).date()
|
||||
if event_date < self.agenda_pricing.date_start or event_date >= self.agenda_pricing.date_end:
|
||||
raise ValidationError(_('This event takes place outside the period covered by this pricing'))
|
||||
|
||||
return original_event_slug
|
||||
|
||||
def clean_booking_status(self):
|
||||
original_booking_status = self.cleaned_data['booking_status']
|
||||
self.booking_status = original_booking_status
|
||||
if '::' in original_booking_status:
|
||||
# split value to get booking status and selected check_type
|
||||
self.booking_status, self.check_type_slug = original_booking_status.split('::')
|
||||
return original_booking_status
|
||||
|
||||
def get_subscription(self, user_external_id):
|
||||
start_date = datetime.datetime.fromisoformat(self.serialized_event['start_datetime']).date()
|
||||
end_date = start_date + datetime.timedelta(days=1)
|
||||
try:
|
||||
subscriptions = get_subscriptions(self.agenda.slug, user_external_id)
|
||||
except ChronoError as e:
|
||||
self.add_error('user_external_id', str(e))
|
||||
return
|
||||
for subscription in subscriptions:
|
||||
sub_start_date = datetime.date.fromisoformat(subscription['date_start'])
|
||||
sub_end_date = datetime.date.fromisoformat(subscription['date_end'])
|
||||
if sub_start_date >= end_date:
|
||||
continue
|
||||
if sub_end_date <= start_date:
|
||||
continue
|
||||
return subscription
|
||||
self.add_error('user_external_id', _('No subscription found for this event'))
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
if self.cleaned_data.get('user_external_id') and self.serialized_event:
|
||||
user_external_id = self.cleaned_data['user_external_id']
|
||||
self.serialized_subscription = self.get_subscription(user_external_id)
|
||||
|
||||
def compute(self):
|
||||
if not self.serialized_event or not self.serialized_subscription:
|
||||
return
|
||||
try:
|
||||
return AgendaPricing.get_pricing_data(
|
||||
request=self.request,
|
||||
agenda=self.agenda,
|
||||
agenda_pricing=self.agenda_pricing,
|
||||
event=self.serialized_event,
|
||||
subscription=self.serialized_subscription,
|
||||
check_status={
|
||||
'status': self.booking_status,
|
||||
'check_type': self.check_type_slug,
|
||||
},
|
||||
booking={},
|
||||
user_external_id=self.cleaned_data['user_external_id'],
|
||||
adult_external_id=self.cleaned_data['adult_external_id'],
|
||||
)
|
||||
except PricingError as e:
|
||||
return {'error': e.details}
|
||||
|
||||
|
||||
class NewCheckTypeForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = CheckType
|
||||
|
|
|
@ -363,9 +363,17 @@ class AgendaPricing(models.Model):
|
|||
|
||||
@staticmethod
|
||||
def get_pricing_data(
|
||||
request, agenda, event, subscription, check_status, booking, user_external_id, adult_external_id
|
||||
request,
|
||||
agenda,
|
||||
event,
|
||||
subscription,
|
||||
check_status,
|
||||
booking,
|
||||
user_external_id,
|
||||
adult_external_id,
|
||||
agenda_pricing=None,
|
||||
):
|
||||
agenda_pricing = AgendaPricing.get_agenda_pricing(agenda=agenda, event=event)
|
||||
agenda_pricing = agenda_pricing or AgendaPricing.get_agenda_pricing(agenda=agenda, event=event)
|
||||
data = {
|
||||
'event': event,
|
||||
'subscription': subscription,
|
||||
|
|
|
@ -20,8 +20,31 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% for matrix in object.iter_pricing_matrix %}
|
||||
<div class="section">
|
||||
<h3>{% trans "Test tool" %}</h3>
|
||||
<div>
|
||||
<form method="get" enctype="multipart/form-data">
|
||||
{{ test_tool_form.as_p }}
|
||||
<div class="buttons">
|
||||
<button class="submit-button">{% trans "Compute" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
{% if request.GET and test_tool_form.is_valid %}
|
||||
{% with test_tool_form.compute as pricing_data %}
|
||||
<div class="test-tool-result">
|
||||
<div class="infonotice">
|
||||
<h3>{% trans "Computed pricing data" %}</h3>
|
||||
{% if pricing_data.pricing is not None %}<p>{% trans "Pricing:" %} {{ pricing_data.pricing|stringformat:".2f" }}</p>{% endif %}
|
||||
<pre>{{ pricing_data|pprint }}</pre>
|
||||
</div>
|
||||
</div>
|
||||
{% endwith %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% for matrix in object.iter_pricing_matrix %}
|
||||
<div class="section pricing-matrix">
|
||||
{% if matrix.criteria %}<h3>{{ matrix.criteria.label }}</h3>{% endif %}
|
||||
<div>
|
||||
<table class="main pricing-matrix-{{ matrix.criteria.slug }}">
|
||||
|
|
|
@ -54,6 +54,7 @@ from lingo.pricing.forms import (
|
|||
PricingCriteriaCategoryEditForm,
|
||||
PricingDuplicateForm,
|
||||
PricingMatrixForm,
|
||||
PricingTestToolForm,
|
||||
PricingVariableFormSet,
|
||||
)
|
||||
from lingo.pricing.models import AgendaPricing, Criteria, CriteriaCategory, Pricing, PricingCriteriaCategory
|
||||
|
@ -717,6 +718,15 @@ class AgendaPricingDetailView(AgendaMixin, DetailView):
|
|||
'pricing__criterias__category'
|
||||
)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
form = PricingTestToolForm(
|
||||
agenda_pricing=self.object, request=self.request, data=self.request.GET or None
|
||||
)
|
||||
if self.request.GET:
|
||||
form.is_valid()
|
||||
kwargs['test_tool_form'] = form
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
agenda_pricing_detail = AgendaPricingDetailView.as_view()
|
||||
|
||||
|
|
|
@ -5,7 +5,13 @@ import pytest
|
|||
from requests.exceptions import ConnectionError
|
||||
from requests.models import Response
|
||||
|
||||
from lingo.agendas.chrono import collect_agenda_data, refresh_agendas
|
||||
from lingo.agendas.chrono import (
|
||||
ChronoError,
|
||||
collect_agenda_data,
|
||||
get_event,
|
||||
get_subscriptions,
|
||||
refresh_agendas,
|
||||
)
|
||||
from lingo.agendas.models import Agenda
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
@ -151,3 +157,120 @@ def test_refresh_agendas(mock_collect):
|
|||
mock_collect.return_value = []
|
||||
refresh_agendas()
|
||||
assert Agenda.objects.count() == 0
|
||||
|
||||
|
||||
def test_get_event_no_service(settings):
|
||||
settings.KNOWN_SERVICES = {}
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_event('foo')
|
||||
assert str(e.value) == 'Unable to get event details'
|
||||
|
||||
settings.KNOWN_SERVICES = {'other': []}
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_event('foo')
|
||||
assert str(e.value) == 'Unable to get event details'
|
||||
|
||||
|
||||
def test_get_event():
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
requests_get.side_effect = ConnectionError()
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_event('foo')
|
||||
assert str(e.value) == 'Unable to get event details'
|
||||
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
mock_resp = Response()
|
||||
mock_resp.status_code = 500
|
||||
requests_get.return_value = mock_resp
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_event('foo')
|
||||
assert str(e.value) == 'Unable to get event details'
|
||||
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
mock_resp = Response()
|
||||
mock_resp.status_code = 404
|
||||
requests_get.return_value = mock_resp
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_event('foo')
|
||||
assert str(e.value) == 'Unable to get event details'
|
||||
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
requests_get.return_value = MockedRequestResponse(content=json.dumps({'foo': 'bar'}))
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_event('foo')
|
||||
assert str(e.value) == 'Unable to get event details'
|
||||
|
||||
data = {'data': []}
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
requests_get.return_value = MockedRequestResponse(content=json.dumps(data))
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_event('foo')
|
||||
assert str(e.value) == 'Unable to get event details'
|
||||
assert requests_get.call_args_list[0][0] == ('api/agendas/events/?slots=foo',)
|
||||
assert requests_get.call_args_list[0][1]['remote_service']['url'] == 'http://chrono.example.org'
|
||||
|
||||
data = {'data': ['foo']}
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
requests_get.return_value = MockedRequestResponse(content=json.dumps(data))
|
||||
assert get_event('foo') == 'foo'
|
||||
|
||||
data = {'data': ['foo', 'bar']} # should not happen
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
requests_get.return_value = MockedRequestResponse(content=json.dumps(data))
|
||||
assert get_event('foo') == 'foo'
|
||||
|
||||
|
||||
def test_get_subscriptions_no_service(settings):
|
||||
settings.KNOWN_SERVICES = {}
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_subscriptions('foo', 'user:1')
|
||||
assert str(e.value) == 'Unable to get subscription details'
|
||||
|
||||
settings.KNOWN_SERVICES = {'other': []}
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_subscriptions('foo', 'user:1')
|
||||
assert str(e.value) == 'Unable to get subscription details'
|
||||
|
||||
|
||||
def test_get_subscriptions():
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
requests_get.side_effect = ConnectionError()
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_subscriptions('foo', 'user:1')
|
||||
assert str(e.value) == 'Unable to get subscription details'
|
||||
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
mock_resp = Response()
|
||||
mock_resp.status_code = 500
|
||||
requests_get.return_value = mock_resp
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_subscriptions('foo', 'user:1')
|
||||
assert str(e.value) == 'Unable to get subscription details'
|
||||
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
mock_resp = Response()
|
||||
mock_resp.status_code = 404
|
||||
requests_get.return_value = mock_resp
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_subscriptions('foo', 'user:1')
|
||||
assert str(e.value) == 'Unable to get subscription details'
|
||||
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
requests_get.return_value = MockedRequestResponse(content=json.dumps({'foo': 'bar'}))
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_subscriptions('foo', 'user:1')
|
||||
assert str(e.value) == 'Unable to get subscription details'
|
||||
|
||||
data = {'data': []}
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
requests_get.return_value = MockedRequestResponse(content=json.dumps(data))
|
||||
with pytest.raises(ChronoError) as e:
|
||||
get_subscriptions('foo', 'user:1')
|
||||
assert str(e.value) == 'Unable to get subscription details'
|
||||
assert requests_get.call_args_list[0][0] == ('api/agenda/foo/subscription/?user_external_id=user:1',)
|
||||
assert requests_get.call_args_list[0][1]['remote_service']['url'] == 'http://chrono.example.org'
|
||||
|
||||
data = {'data': ['foo', 'bar']}
|
||||
with mock.patch('requests.Session.get') as requests_get:
|
||||
requests_get.return_value = MockedRequestResponse(content=json.dumps(data))
|
||||
assert get_subscriptions('foo', 'user:1') == ['foo', 'bar']
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import datetime
|
||||
from decimal import Decimal
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from django.utils.timezone import now
|
||||
|
||||
from lingo.agendas.models import Agenda, CheckTypeGroup
|
||||
from lingo.pricing.models import AgendaPricing, Criteria, CriteriaCategory, Pricing
|
||||
from lingo.agendas.chrono import ChronoError
|
||||
from lingo.agendas.models import Agenda, CheckType, CheckTypeGroup
|
||||
from lingo.pricing.models import AgendaPricing, Criteria, CriteriaCategory, Pricing, PricingError
|
||||
from tests.utils import login
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
@ -353,7 +355,7 @@ def test_detail_agenda_pricing_2_categories(app, admin_user):
|
|||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/pricing/agenda/%s/pricing/%s/' % (agenda.pk, agenda_pricing.pk))
|
||||
assert '<h3>' not in resp
|
||||
assert len(resp.pyquery.find('div.section.prixing-matrix h3')) == 0
|
||||
ths = resp.pyquery.find('table thead th')
|
||||
assert len(ths) == 4
|
||||
assert ths[0].text is None
|
||||
|
@ -400,7 +402,7 @@ def test_detail_agenda_pricing_1_category(app, admin_user):
|
|||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/pricing/agenda/%s/pricing/%s/' % (agenda.pk, agenda_pricing.pk))
|
||||
assert '<h3>' not in resp
|
||||
assert len(resp.pyquery.find('div.section.prixing-matrix h3')) == 0
|
||||
ths = resp.pyquery.find('table thead')
|
||||
assert len(ths) == 0
|
||||
assert resp.pyquery.find('table tr.pricing-row-crit-3-1 th')[0].text == 'Crit 3-1'
|
||||
|
@ -413,6 +415,248 @@ def test_detail_agenda_pricing_1_category(app, admin_user):
|
|||
assert resp.pyquery.find('table tr.pricing-row-crit-3-4 td')[0].text == '114.00'
|
||||
|
||||
|
||||
@mock.patch('lingo.pricing.forms.get_event')
|
||||
@mock.patch('lingo.pricing.forms.get_subscriptions')
|
||||
@mock.patch('lingo.pricing.models.AgendaPricing.get_pricing_data')
|
||||
def test_detail_agenda_pricing_test_tool(mock_pricing_data, mock_subscriptions, mock_event, app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Foo bar')
|
||||
pricing = Pricing.objects.create(label='Foo bar')
|
||||
agenda_pricing = AgendaPricing.objects.create(
|
||||
agenda=agenda,
|
||||
pricing=pricing,
|
||||
date_start=datetime.date(year=2021, month=9, day=1),
|
||||
date_end=datetime.date(year=2021, month=10, day=1),
|
||||
)
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/pricing/agenda/%s/pricing/%s/' % (agenda.pk, agenda_pricing.pk))
|
||||
assert 'Computed pricing data' not in resp
|
||||
|
||||
# check event date
|
||||
mock_event.return_value = {'start_datetime': '2021-08-31T12:00:00+02:00'}
|
||||
resp.form['event_slug'] = 'foo'
|
||||
resp.form['user_external_id'] = 'user:1'
|
||||
resp.form['adult_external_id'] = 'adult:1'
|
||||
resp.form['booking_status'] = 'presence'
|
||||
resp = resp.form.submit()
|
||||
assert 'Computed pricing data' not in resp
|
||||
assert resp.context['test_tool_form'].errors['event_slug'] == [
|
||||
'This event takes place outside the period covered by this pricing'
|
||||
]
|
||||
assert mock_event.call_args_list == [mock.call('foo-bar@foo')]
|
||||
assert mock_pricing_data.call_args_list == []
|
||||
mock_event.return_value = {'start_datetime': '2021-10-01T12:00:00+02:00'}
|
||||
resp = resp.form.submit()
|
||||
assert 'Computed pricing data' not in resp
|
||||
assert resp.context['test_tool_form'].errors['event_slug'] == [
|
||||
'This event takes place outside the period covered by this pricing'
|
||||
]
|
||||
|
||||
mock_event.return_value = {'start_datetime': '2021-09-01T12:00:00+02:00'}
|
||||
|
||||
# check event_slug & agenda
|
||||
resp.form['event_slug'] = 'foo@foo'
|
||||
resp = resp.form.submit()
|
||||
assert resp.context['test_tool_form'].errors['event_slug'] == ['The agenda identifier is wrong (foo)']
|
||||
|
||||
# check subscriptions dates
|
||||
mock_subscriptions.return_value = []
|
||||
mock_event.reset_mock()
|
||||
resp.form['event_slug'] = 'foo-bar@foo'
|
||||
resp = resp.form.submit()
|
||||
assert mock_event.call_args_list == [mock.call('foo-bar@foo')]
|
||||
assert mock_pricing_data.call_args_list == []
|
||||
assert 'Computed pricing data' not in resp
|
||||
assert resp.context['test_tool_form'].errors['user_external_id'] == [
|
||||
'No subscription found for this event'
|
||||
]
|
||||
|
||||
mock_subscriptions.return_value = [
|
||||
{
|
||||
'date_start': '2021-08-01',
|
||||
'date_end': '2021-09-01',
|
||||
},
|
||||
{
|
||||
'date_start': '2021-09-02',
|
||||
'date_end': '2021-09-03',
|
||||
},
|
||||
]
|
||||
resp = resp.form.submit()
|
||||
assert 'Computed pricing data' not in resp
|
||||
assert resp.context['test_tool_form'].errors['user_external_id'] == [
|
||||
'No subscription found for this event'
|
||||
]
|
||||
|
||||
mock_subscriptions.return_value = [
|
||||
{
|
||||
'date_start': '2021-08-01',
|
||||
'date_end': '2021-09-01',
|
||||
},
|
||||
{
|
||||
'date_start': '2021-09-01',
|
||||
'date_end': '2021-09-02',
|
||||
},
|
||||
{
|
||||
'date_start': '2021-09-02',
|
||||
'date_end': '2021-09-03',
|
||||
},
|
||||
]
|
||||
mock_pricing_data.return_value = {'foo': 'bar', 'pricing': Decimal('42')}
|
||||
resp = resp.form.submit()
|
||||
assert 'Computed pricing data' in resp
|
||||
assert mock_pricing_data.call_args_list == [
|
||||
mock.call(
|
||||
request=mock.ANY,
|
||||
agenda=agenda,
|
||||
agenda_pricing=agenda_pricing,
|
||||
event={'start_datetime': '2021-09-01T12:00:00+02:00'},
|
||||
subscription={'date_start': '2021-09-01', 'date_end': '2021-09-02'},
|
||||
check_status={'status': 'presence', 'check_type': None},
|
||||
booking={},
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
)
|
||||
]
|
||||
assert '<p>Pricing: 42.00</p>' in resp
|
||||
assert '<pre>{'foo': 'bar', 'pricing': Decimal('42')}</pre>' in resp
|
||||
|
||||
mock_pricing_data.side_effect = PricingError(details={'foo': 'error'})
|
||||
resp = resp.form.submit()
|
||||
assert 'Computed pricing data' in resp
|
||||
assert '<pre>{'error': {'foo': 'error'}}</pre>' in resp
|
||||
|
||||
|
||||
@mock.patch('lingo.pricing.forms.get_event')
|
||||
@mock.patch('lingo.pricing.forms.get_subscriptions')
|
||||
def test_detail_agenda_pricing_test_tool_event_error(mock_subscriptions, mock_event, app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Foo bar')
|
||||
pricing = Pricing.objects.create(label='Foo bar')
|
||||
agenda_pricing = AgendaPricing.objects.create(
|
||||
agenda=agenda,
|
||||
pricing=pricing,
|
||||
date_start=datetime.date(year=2021, month=9, day=1),
|
||||
date_end=datetime.date(year=2021, month=10, day=1),
|
||||
)
|
||||
|
||||
mock_event.side_effect = ChronoError('foo bar foo-bar@foo-event')
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/pricing/agenda/%s/pricing/%s/' % (agenda.pk, agenda_pricing.pk))
|
||||
resp.form['event_slug'] = 'foo-event'
|
||||
resp.form['user_external_id'] = 'user:1'
|
||||
resp.form['adult_external_id'] = 'adult:1'
|
||||
resp.form['booking_status'] = 'presence'
|
||||
|
||||
# agenda slug is removed from error message
|
||||
resp = resp.form.submit()
|
||||
assert resp.context['test_tool_form'].errors['event_slug'] == ['foo bar foo-event']
|
||||
|
||||
# except it was included in event_slug
|
||||
resp.form['event_slug'] = 'foo-bar@foo-event'
|
||||
resp = resp.form.submit()
|
||||
assert resp.context['test_tool_form'].errors['event_slug'] == ['foo bar foo-bar@foo-event']
|
||||
|
||||
|
||||
@mock.patch('lingo.pricing.forms.get_event')
|
||||
@mock.patch('lingo.pricing.forms.get_subscriptions')
|
||||
def test_detail_agenda_pricing_test_tool_subscription_error(mock_subscriptions, mock_event, app, admin_user):
|
||||
agenda = Agenda.objects.create(label='Foo bar')
|
||||
pricing = Pricing.objects.create(label='Foo bar')
|
||||
agenda_pricing = AgendaPricing.objects.create(
|
||||
agenda=agenda,
|
||||
pricing=pricing,
|
||||
date_start=datetime.date(year=2021, month=9, day=1),
|
||||
date_end=datetime.date(year=2021, month=10, day=1),
|
||||
)
|
||||
|
||||
mock_event.return_value = {'start_datetime': '2021-09-01T12:00:00+02:00'}
|
||||
mock_subscriptions.side_effect = ChronoError('foo bar')
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/pricing/agenda/%s/pricing/%s/' % (agenda.pk, agenda_pricing.pk))
|
||||
resp.form['event_slug'] = 'foo-event'
|
||||
resp.form['user_external_id'] = 'user:1'
|
||||
resp.form['adult_external_id'] = 'adult:1'
|
||||
resp.form['booking_status'] = 'presence'
|
||||
resp = resp.form.submit()
|
||||
assert resp.context['test_tool_form'].errors['user_external_id'] == ['foo bar']
|
||||
|
||||
|
||||
@mock.patch('lingo.pricing.forms.get_event')
|
||||
@mock.patch('lingo.pricing.forms.get_subscriptions')
|
||||
@mock.patch('lingo.pricing.models.AgendaPricing.get_pricing_data')
|
||||
def test_detail_agenda_pricing_test_tool_booking_status(
|
||||
mock_pricing_data, mock_subscriptions, mock_event, app, admin_user
|
||||
):
|
||||
agenda = Agenda.objects.create(label='Foo bar')
|
||||
pricing = Pricing.objects.create(label='Foo bar')
|
||||
agenda_pricing = AgendaPricing.objects.create(
|
||||
agenda=agenda,
|
||||
pricing=pricing,
|
||||
date_start=datetime.date(year=2021, month=9, day=1),
|
||||
date_end=datetime.date(year=2021, month=10, day=1),
|
||||
)
|
||||
|
||||
mock_event.return_value = {'start_datetime': '2021-09-01T12:00:00+02:00'}
|
||||
mock_subscriptions.return_value = [
|
||||
{
|
||||
'date_start': '2021-09-01',
|
||||
'date_end': '2021-09-02',
|
||||
},
|
||||
]
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/pricing/agenda/%s/pricing/%s/' % (agenda.pk, agenda_pricing.pk))
|
||||
assert resp.form['booking_status'].options == [
|
||||
('presence', False, 'Presence'),
|
||||
('absence', False, 'Absence'),
|
||||
]
|
||||
|
||||
group = CheckTypeGroup.objects.create(label='Foo bar')
|
||||
CheckType.objects.create(label='Foo presence reason', group=group, kind='presence')
|
||||
CheckType.objects.create(label='Foo absence reason', group=group, kind='absence')
|
||||
agenda.check_type_group = group
|
||||
agenda.save()
|
||||
|
||||
resp = app.get('/manage/pricing/agenda/%s/pricing/%s/' % (agenda.pk, agenda_pricing.pk))
|
||||
assert resp.form['booking_status'].options == [
|
||||
('presence', False, 'Presence'),
|
||||
('presence::foo-presence-reason', False, 'Presence (Foo presence reason)'),
|
||||
('absence', False, 'Absence'),
|
||||
('absence::foo-absence-reason', False, 'Absence (Foo absence reason)'),
|
||||
]
|
||||
resp.form['event_slug'] = 'foo'
|
||||
resp.form['user_external_id'] = 'user:1'
|
||||
resp.form['adult_external_id'] = 'adult:1'
|
||||
resp.form['booking_status'] = 'presence'
|
||||
resp = resp.form.submit()
|
||||
assert mock_pricing_data.call_args_list[0][1]['check_status'] == {
|
||||
'check_type': None,
|
||||
'status': 'presence',
|
||||
}
|
||||
|
||||
mock_pricing_data.reset_mock()
|
||||
resp.form['booking_status'] = 'presence::foo-presence-reason'
|
||||
resp = resp.form.submit()
|
||||
assert mock_pricing_data.call_args_list[0][1]['check_status'] == {
|
||||
'check_type': 'foo-presence-reason',
|
||||
'status': 'presence',
|
||||
}
|
||||
|
||||
mock_pricing_data.reset_mock()
|
||||
resp.form['booking_status'] = 'absence'
|
||||
resp = resp.form.submit()
|
||||
assert mock_pricing_data.call_args_list[0][1]['check_status'] == {'check_type': None, 'status': 'absence'}
|
||||
|
||||
mock_pricing_data.reset_mock()
|
||||
resp.form['booking_status'] = 'absence::foo-absence-reason'
|
||||
resp = resp.form.submit()
|
||||
assert mock_pricing_data.call_args_list[0][1]['check_status'] == {
|
||||
'check_type': 'foo-absence-reason',
|
||||
'status': 'absence',
|
||||
}
|
||||
|
||||
|
||||
def test_edit_agenda_pricing_matrix_3_categories(app, admin_user):
|
||||
category1 = CriteriaCategory.objects.create(label='Cat 1')
|
||||
criteria11 = Criteria.objects.create(label='Crit 1-1', slug='crit-1-1', category=category1, order=1)
|
||||
|
|
Loading…
Reference in New Issue