pricing: rename & rewrite get_pricing_data method (#67675)
renamed into get_pricing_data_for_event, to prepare flat_fee_schedule mode
This commit is contained in:
parent
fc7dc98b1f
commit
5d43627364
|
@ -22,7 +22,7 @@ from rest_framework.exceptions import ValidationError
|
|||
|
||||
from lingo.agendas.chrono import ChronoError, get_events, get_subscriptions
|
||||
from lingo.agendas.models import Agenda
|
||||
from lingo.pricing.models import AgendaPricing, PricingError
|
||||
from lingo.pricing.models import AgendaPricing, AgendaPricingNotFound, PricingError
|
||||
|
||||
|
||||
class CommaSeparatedStringField(serializers.ListField):
|
||||
|
@ -119,10 +119,13 @@ class PricingComputeSerializer(serializers.Serializer):
|
|||
event_slugs = sorted(self.serialized_events.keys())
|
||||
for event_slug in event_slugs:
|
||||
serialized_event = self.serialized_events[event_slug]
|
||||
start_date = datetime.datetime.fromisoformat(serialized_event['start_datetime']).date()
|
||||
agenda = self.agendas[serialized_event['agenda']]
|
||||
try:
|
||||
pricing_data = AgendaPricing.get_pricing_data(
|
||||
agenda_pricing = AgendaPricing.get_agenda_pricing(agenda=agenda, start_date=start_date)
|
||||
pricing_data = agenda_pricing.get_pricing_data_for_event(
|
||||
request=request,
|
||||
agenda=self.agendas[serialized_event['agenda']],
|
||||
agenda=agenda,
|
||||
event=serialized_event,
|
||||
subscription=self.event_subscriptions[event_slug],
|
||||
check_status={
|
||||
|
@ -139,6 +142,10 @@ class PricingComputeSerializer(serializers.Serializer):
|
|||
'pricing_data': pricing_data,
|
||||
}
|
||||
)
|
||||
except AgendaPricingNotFound:
|
||||
result.append(
|
||||
{'event': event_slug, 'error': _('No agenda pricing found for event %s') % event_slug}
|
||||
)
|
||||
except PricingError as e:
|
||||
result.append({'event': event_slug, 'error': e.details})
|
||||
|
||||
|
|
|
@ -389,10 +389,9 @@ class PricingTestToolForm(forms.Form):
|
|||
if not self.serialized_event or not self.serialized_subscription:
|
||||
return
|
||||
try:
|
||||
return AgendaPricing.get_pricing_data(
|
||||
return self.agenda_pricing.get_pricing_data_for_event(
|
||||
request=self.request,
|
||||
agenda=self.agenda,
|
||||
agenda_pricing=self.agenda_pricing,
|
||||
event=self.serialized_event,
|
||||
subscription=self.serialized_subscription,
|
||||
check_status={
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
|
||||
import copy
|
||||
import dataclasses
|
||||
import datetime
|
||||
import decimal
|
||||
from typing import List
|
||||
|
||||
|
@ -400,33 +399,24 @@ class AgendaPricing(models.Model):
|
|||
|
||||
return created, agenda_pricing
|
||||
|
||||
@staticmethod
|
||||
def get_pricing_data(
|
||||
request,
|
||||
agenda,
|
||||
event,
|
||||
subscription,
|
||||
check_status,
|
||||
booking,
|
||||
user_external_id,
|
||||
adult_external_id,
|
||||
agenda_pricing=None,
|
||||
def get_pricing_data_for_event(
|
||||
self, request, agenda, event, subscription, check_status, booking, user_external_id, adult_external_id
|
||||
):
|
||||
agenda_pricing = agenda_pricing or AgendaPricing.get_agenda_pricing(agenda=agenda, event=event)
|
||||
# compute pricing for an event
|
||||
data = {
|
||||
'event': event,
|
||||
'subscription': subscription,
|
||||
'booking': booking,
|
||||
}
|
||||
context = agenda_pricing.get_pricing_context(
|
||||
context = self.get_pricing_context(
|
||||
request=request,
|
||||
data=data,
|
||||
user_external_id=user_external_id,
|
||||
adult_external_id=adult_external_id,
|
||||
)
|
||||
pricing, criterias = agenda_pricing.compute_pricing(context=context)
|
||||
modifier = agenda_pricing.get_booking_modifier(agenda=agenda, check_status=check_status)
|
||||
return agenda_pricing.aggregate_pricing_data(
|
||||
pricing, criterias = self.compute_pricing(context=context)
|
||||
modifier = self.get_booking_modifier(agenda=agenda, check_status=check_status)
|
||||
return self.aggregate_pricing_data(
|
||||
pricing=pricing, criterias=criterias, context=context, modifier=modifier
|
||||
)
|
||||
|
||||
|
@ -446,12 +436,11 @@ class AgendaPricing(models.Model):
|
|||
}
|
||||
|
||||
@staticmethod
|
||||
def get_agenda_pricing(agenda, event):
|
||||
start_datetime = datetime.datetime.fromisoformat(event['start_datetime'])
|
||||
def get_agenda_pricing(agenda, start_date):
|
||||
try:
|
||||
return agenda.agendapricings.get(
|
||||
date_start__lte=start_datetime,
|
||||
date_end__gt=start_datetime,
|
||||
date_start__lte=start_date,
|
||||
date_end__gt=start_date,
|
||||
)
|
||||
except (AgendaPricing.DoesNotExist, AgendaPricing.MultipleObjectsReturned):
|
||||
raise AgendaPricingNotFound
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import datetime
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from lingo.agendas.chrono import ChronoError
|
||||
from lingo.agendas.models import Agenda
|
||||
from lingo.pricing.models import PricingError
|
||||
from lingo.pricing.models import AgendaPricing, Pricing, PricingError
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
@ -145,12 +146,13 @@ def test_pricing_compute_subscriptions_error(mock_subscriptions, mock_events, ap
|
|||
|
||||
@mock.patch('lingo.api.serializers.get_events')
|
||||
@mock.patch('lingo.api.serializers.get_subscriptions')
|
||||
@mock.patch('lingo.pricing.models.AgendaPricing.get_pricing_data')
|
||||
def test_pricing_compute(mock_pricing_data, mock_subscriptions, mock_events, app, user):
|
||||
@mock.patch('lingo.pricing.models.AgendaPricing.get_pricing_data_for_event')
|
||||
def test_pricing_compute(mock_pricing_data_event, mock_subscriptions, mock_events, app, user):
|
||||
agenda = Agenda.objects.create(label='Agenda')
|
||||
agenda2 = Agenda.objects.create(label='Agenda2')
|
||||
app.authorization = ('Basic', ('john.doe', 'password'))
|
||||
|
||||
# no subscription
|
||||
mock_events.return_value = [
|
||||
{'start_datetime': '2021-09-01T12:00:00+02:00', 'agenda': 'agenda', 'slug': 'event-bar-slug'}
|
||||
]
|
||||
|
@ -169,9 +171,10 @@ def test_pricing_compute(mock_pricing_data, mock_subscriptions, mock_events, app
|
|||
assert resp.json['errors']['user_external_id'] == [
|
||||
'No subscription found for event agenda@event-bar-slug'
|
||||
]
|
||||
assert mock_pricing_data.call_args_list == []
|
||||
assert mock_pricing_data_event.call_args_list == []
|
||||
assert mock_subscriptions.call_args_list == [mock.call('agenda', 'user:1')]
|
||||
|
||||
# no matching subscription
|
||||
mock_subscriptions.reset_mock()
|
||||
mock_subscriptions.return_value = [
|
||||
{
|
||||
|
@ -197,9 +200,10 @@ def test_pricing_compute(mock_pricing_data, mock_subscriptions, mock_events, app
|
|||
assert resp.json['errors']['user_external_id'] == [
|
||||
'No subscription found for event agenda@event-bar-slug'
|
||||
]
|
||||
assert mock_pricing_data.call_args_list == []
|
||||
assert mock_pricing_data_event.call_args_list == []
|
||||
assert mock_subscriptions.call_args_list == [mock.call('agenda', 'user:1')]
|
||||
|
||||
# no matching subscription
|
||||
mock_events.return_value = [
|
||||
{'start_datetime': '2021-09-02T12:00:00+02:00', 'agenda': 'agenda', 'slug': 'event-bar-slug'},
|
||||
{'start_datetime': '2021-09-01T12:00:00+02:00', 'agenda': 'agenda2', 'slug': 'event-baz-slug'},
|
||||
|
@ -229,12 +233,13 @@ def test_pricing_compute(mock_pricing_data, mock_subscriptions, mock_events, app
|
|||
assert resp.json['errors']['user_external_id'] == [
|
||||
'No subscription found for event agenda2@event-baz-slug'
|
||||
]
|
||||
assert mock_pricing_data.call_args_list == []
|
||||
assert mock_pricing_data_event.call_args_list == []
|
||||
assert mock_subscriptions.call_args_list == [
|
||||
mock.call('agenda', 'user:1'),
|
||||
mock.call('agenda2', 'user:1'),
|
||||
]
|
||||
|
||||
# no agenda_pricing found
|
||||
mock_subscriptions.return_value = [
|
||||
{
|
||||
'date_start': '2021-08-01',
|
||||
|
@ -249,7 +254,7 @@ def test_pricing_compute(mock_pricing_data, mock_subscriptions, mock_events, app
|
|||
'date_end': '2021-09-03',
|
||||
},
|
||||
]
|
||||
mock_pricing_data.side_effect = [
|
||||
mock_pricing_data_event.side_effect = [
|
||||
{'foo': 'baz'},
|
||||
{'foo': 'bar'},
|
||||
]
|
||||
|
@ -261,11 +266,38 @@ def test_pricing_compute(mock_pricing_data, mock_subscriptions, mock_events, app
|
|||
'adult_external_id': 'adult:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == [
|
||||
{
|
||||
'event': 'agenda2@event-baz-slug',
|
||||
'error': 'No agenda pricing found for event agenda2@event-baz-slug',
|
||||
},
|
||||
{
|
||||
'event': 'agenda@event-bar-slug',
|
||||
'error': 'No agenda pricing found for event agenda@event-bar-slug',
|
||||
},
|
||||
]
|
||||
|
||||
# ok
|
||||
pricing = Pricing.objects.create(label='Foo bar')
|
||||
agenda_pricing = AgendaPricing.objects.create(
|
||||
pricing=pricing,
|
||||
date_start=datetime.date(year=2021, month=9, day=1),
|
||||
date_end=datetime.date(year=2021, month=10, day=1),
|
||||
)
|
||||
agenda_pricing.agendas.add(agenda, agenda2)
|
||||
resp = app.get(
|
||||
'/api/pricing/compute/',
|
||||
params={
|
||||
'slots': 'agenda@event-bar-slug, agenda2@event-baz-slug',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == [
|
||||
{'event': 'agenda2@event-baz-slug', 'pricing_data': {'foo': 'baz'}},
|
||||
{'event': 'agenda@event-bar-slug', 'pricing_data': {'foo': 'bar'}},
|
||||
]
|
||||
assert mock_pricing_data.call_args_list == [
|
||||
assert mock_pricing_data_event.call_args_list == [
|
||||
mock.call(
|
||||
request=mock.ANY,
|
||||
agenda=agenda2,
|
||||
|
@ -296,7 +328,8 @@ def test_pricing_compute(mock_pricing_data, mock_subscriptions, mock_events, app
|
|||
),
|
||||
]
|
||||
|
||||
mock_pricing_data.side_effect = [
|
||||
# get_pricing_data_for_event with error
|
||||
mock_pricing_data_event.side_effect = [
|
||||
PricingError(details={'foo': 'error'}),
|
||||
{'foo': 'bar'},
|
||||
]
|
||||
|
|
|
@ -821,8 +821,10 @@ def test_detail_agenda_pricing_1_category(app, admin_user):
|
|||
|
||||
@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):
|
||||
@mock.patch('lingo.pricing.models.AgendaPricing.get_pricing_data_for_event')
|
||||
def test_detail_agenda_pricing_test_tool(
|
||||
mock_pricing_data_event, 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(
|
||||
|
@ -847,7 +849,7 @@ def test_detail_agenda_pricing_test_tool(mock_pricing_data, mock_subscriptions,
|
|||
assert resp.context['test_tool_form'].errors['event_slug'] == [
|
||||
'This event takes place outside the period covered by this pricing'
|
||||
]
|
||||
assert mock_pricing_data.call_args_list == []
|
||||
assert mock_pricing_data_event.call_args_list == []
|
||||
mock_event.return_value = {'start_datetime': '2021-10-01T12:00:00+02:00'}
|
||||
resp = resp.form.submit().follow()
|
||||
assert 'Computed pricing data' not in resp
|
||||
|
@ -870,7 +872,7 @@ def test_detail_agenda_pricing_test_tool(mock_pricing_data, mock_subscriptions,
|
|||
mock_event.reset_mock()
|
||||
resp.form['event_slug'] = 'foo-bar@foo'
|
||||
resp = resp.form.submit().follow()
|
||||
assert mock_pricing_data.call_args_list == []
|
||||
assert mock_pricing_data_event.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'
|
||||
|
@ -906,14 +908,13 @@ def test_detail_agenda_pricing_test_tool(mock_pricing_data, mock_subscriptions,
|
|||
'date_end': '2021-09-03',
|
||||
},
|
||||
]
|
||||
mock_pricing_data.return_value = {'foo': 'bar', 'pricing': Decimal('42')}
|
||||
mock_pricing_data_event.return_value = {'foo': 'bar', 'pricing': Decimal('42')}
|
||||
resp = resp.form.submit().follow()
|
||||
assert 'Computed pricing data' in resp
|
||||
assert mock_pricing_data.call_args_list == [
|
||||
assert mock_pricing_data_event.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},
|
||||
|
@ -925,7 +926,7 @@ def test_detail_agenda_pricing_test_tool(mock_pricing_data, mock_subscriptions,
|
|||
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'})
|
||||
mock_pricing_data_event.side_effect = PricingError(details={'foo': 'error'})
|
||||
resp = resp.form.submit().follow()
|
||||
assert 'Computed pricing data' in resp
|
||||
assert '<pre>{'error': {'foo': 'error'}}</pre>' in resp
|
||||
|
@ -982,9 +983,9 @@ def test_detail_agenda_pricing_test_tool_subscription_error(mock_subscriptions,
|
|||
|
||||
@mock.patch('lingo.pricing.forms.get_event')
|
||||
@mock.patch('lingo.pricing.forms.get_subscriptions')
|
||||
@mock.patch('lingo.pricing.models.AgendaPricing.get_pricing_data')
|
||||
@mock.patch('lingo.pricing.models.AgendaPricing.get_pricing_data_for_event')
|
||||
def test_detail_agenda_pricing_test_tool_booking_status(
|
||||
mock_pricing_data, mock_subscriptions, mock_event, app, admin_user
|
||||
mock_pricing_data_event, mock_subscriptions, mock_event, app, admin_user
|
||||
):
|
||||
agenda = Agenda.objects.create(label='Foo bar')
|
||||
pricing = Pricing.objects.create(label='Foo bar')
|
||||
|
@ -1032,28 +1033,31 @@ def test_detail_agenda_pricing_test_tool_booking_status(
|
|||
('absence', False, 'Absence'),
|
||||
('absence::foo-absence-reason', False, 'Absence (Foo absence reason)'),
|
||||
]
|
||||
assert mock_pricing_data.call_args_list[0][1]['check_status'] == {
|
||||
assert mock_pricing_data_event.call_args_list[0][1]['check_status'] == {
|
||||
'check_type': None,
|
||||
'status': 'presence',
|
||||
}
|
||||
|
||||
mock_pricing_data.reset_mock()
|
||||
mock_pricing_data_event.reset_mock()
|
||||
resp.form['booking_status'] = 'presence::foo-presence-reason'
|
||||
resp = resp.form.submit().follow()
|
||||
assert mock_pricing_data.call_args_list[0][1]['check_status'] == {
|
||||
assert mock_pricing_data_event.call_args_list[0][1]['check_status'] == {
|
||||
'check_type': 'foo-presence-reason',
|
||||
'status': 'presence',
|
||||
}
|
||||
|
||||
mock_pricing_data.reset_mock()
|
||||
mock_pricing_data_event.reset_mock()
|
||||
resp.form['booking_status'] = 'absence'
|
||||
resp = resp.form.submit().follow()
|
||||
assert mock_pricing_data.call_args_list[0][1]['check_status'] == {'check_type': None, 'status': 'absence'}
|
||||
assert mock_pricing_data_event.call_args_list[0][1]['check_status'] == {
|
||||
'check_type': None,
|
||||
'status': 'absence',
|
||||
}
|
||||
|
||||
mock_pricing_data.reset_mock()
|
||||
mock_pricing_data_event.reset_mock()
|
||||
resp.form['booking_status'] = 'absence::foo-absence-reason'
|
||||
resp = resp.form.submit().follow()
|
||||
assert mock_pricing_data.call_args_list[0][1]['check_status'] == {
|
||||
assert mock_pricing_data_event.call_args_list[0][1]['check_status'] == {
|
||||
'check_type': 'foo-absence-reason',
|
||||
'status': 'absence',
|
||||
}
|
||||
|
|
|
@ -283,13 +283,11 @@ def test_agenda_pricing_duplicate_slugs():
|
|||
def test_get_agenda_pricing():
|
||||
agenda = Agenda.objects.create(label='Foo bar')
|
||||
pricing = Pricing.objects.create(label='Foo bar')
|
||||
event = {
|
||||
'start_datetime': make_aware(datetime.datetime(2021, 9, 15, 12, 00)).isoformat(),
|
||||
}
|
||||
start_date = datetime.datetime(2021, 9, 15)
|
||||
|
||||
# not found
|
||||
with pytest.raises(AgendaPricingNotFound):
|
||||
AgendaPricing.get_agenda_pricing(agenda=agenda, event=event)
|
||||
AgendaPricing.get_agenda_pricing(agenda=agenda, start_date=start_date)
|
||||
|
||||
# ok
|
||||
agenda_pricing = AgendaPricing.objects.create(
|
||||
|
@ -298,7 +296,7 @@ def test_get_agenda_pricing():
|
|||
date_end=datetime.date(year=2021, month=10, day=1),
|
||||
)
|
||||
agenda_pricing.agendas.add(agenda)
|
||||
assert AgendaPricing.get_agenda_pricing(agenda=agenda, event=event) == agenda_pricing
|
||||
assert AgendaPricing.get_agenda_pricing(agenda=agenda, start_date=start_date) == agenda_pricing
|
||||
|
||||
# more than one matching
|
||||
agenda_pricing = AgendaPricing.objects.create(
|
||||
|
@ -308,39 +306,37 @@ def test_get_agenda_pricing():
|
|||
)
|
||||
agenda_pricing.agendas.add(agenda)
|
||||
with pytest.raises(AgendaPricingNotFound):
|
||||
AgendaPricing.get_agenda_pricing(agenda=agenda, event=event)
|
||||
AgendaPricing.get_agenda_pricing(agenda=agenda, start_date=start_date)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'event_date, found',
|
||||
[
|
||||
# just before first day
|
||||
((2021, 8, 31, 12, 00), False),
|
||||
((2021, 8, 31), False),
|
||||
# first day
|
||||
((2021, 9, 1, 12, 00), True),
|
||||
((2021, 9, 1), True),
|
||||
# last day
|
||||
((2021, 9, 30, 12, 00), True),
|
||||
((2021, 9, 30), True),
|
||||
# just after last day
|
||||
((2021, 10, 1, 12, 00), False),
|
||||
((2021, 10, 1), False),
|
||||
],
|
||||
)
|
||||
def test_get_agenda_pricing_event_date(event_date, found):
|
||||
agenda = Agenda.objects.create(label='Foo bar')
|
||||
pricing = Pricing.objects.create(label='Foo bar')
|
||||
event = {
|
||||
'start_datetime': make_aware(datetime.datetime(*event_date)).isoformat(),
|
||||
}
|
||||
agenda_pricing = AgendaPricing.objects.create(
|
||||
pricing=pricing,
|
||||
date_start=datetime.date(year=2021, month=9, day=1),
|
||||
date_end=datetime.date(year=2021, month=10, day=1),
|
||||
)
|
||||
agenda_pricing.agendas.add(agenda)
|
||||
start_date = datetime.date(*event_date)
|
||||
if found:
|
||||
assert AgendaPricing.get_agenda_pricing(agenda=agenda, event=event) == agenda_pricing
|
||||
assert AgendaPricing.get_agenda_pricing(agenda=agenda, start_date=start_date) == agenda_pricing
|
||||
else:
|
||||
with pytest.raises(AgendaPricingNotFound):
|
||||
AgendaPricing.get_agenda_pricing(agenda=agenda, event=event)
|
||||
AgendaPricing.get_agenda_pricing(agenda=agenda, start_date=start_date)
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
|
@ -931,7 +927,7 @@ def test_get_booking_modifier_booking_presence():
|
|||
}
|
||||
|
||||
|
||||
def test_get_pricing_data(context):
|
||||
def test_get_pricing_data_for_event(context):
|
||||
agenda = Agenda.objects.create(label='Foo bar')
|
||||
category = CriteriaCategory.objects.create(label='Foo', slug='foo')
|
||||
criteria = Criteria.objects.create(label='Bar', slug='bar', condition='True', category=category)
|
||||
|
@ -953,7 +949,7 @@ def test_get_pricing_data(context):
|
|||
},
|
||||
)
|
||||
agenda_pricing.agendas.add(agenda)
|
||||
assert AgendaPricing.get_pricing_data(
|
||||
assert agenda_pricing.get_pricing_data_for_event(
|
||||
request=context['request'],
|
||||
agenda=agenda,
|
||||
event={'start_datetime': make_aware(datetime.datetime(2021, 9, 15, 12, 00)).isoformat()},
|
||||
|
|
Loading…
Reference in New Issue