1538 lines
56 KiB
Python
1538 lines
56 KiB
Python
import datetime
|
|
import json
|
|
from unittest import mock
|
|
|
|
import pytest
|
|
from django.template import Context
|
|
from django.test.client import RequestFactory
|
|
from django.utils.timezone import make_aware
|
|
from publik_django_templatetags.wcs.context_processors import Cards
|
|
|
|
from lingo.agendas.models import Agenda, CheckType, CheckTypeGroup
|
|
from lingo.pricing.models import (
|
|
AgendaPricing,
|
|
AgendaPricingNotFound,
|
|
Criteria,
|
|
CriteriaCategory,
|
|
CriteriaConditionNotFound,
|
|
MultipleDefaultCriteriaCondition,
|
|
Pricing,
|
|
PricingBookingCheckTypeError,
|
|
PricingBookingNotCheckedError,
|
|
PricingCriteriaCategory,
|
|
PricingDataError,
|
|
PricingDataFormatError,
|
|
PricingEventNotCheckedError,
|
|
PricingMatrix,
|
|
PricingMatrixCell,
|
|
PricingMatrixRow,
|
|
PricingMultipleBookingError,
|
|
PricingUnknownCheckStatusError,
|
|
)
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
@pytest.fixture
|
|
def context():
|
|
return Context(
|
|
{
|
|
'cards': Cards(),
|
|
'request': RequestFactory().get('/'),
|
|
}
|
|
)
|
|
|
|
|
|
class MockedRequestResponse(mock.Mock):
|
|
status_code = 200
|
|
|
|
def json(self):
|
|
return json.loads(self.content)
|
|
|
|
|
|
def mocked_requests_send(request, **kwargs):
|
|
data = [{'id': 1, 'fields': {'foo': 'bar'}}, {'id': 2, 'fields': {'foo': 'baz'}}] # fake result
|
|
return MockedRequestResponse(content=json.dumps({'data': data}))
|
|
|
|
|
|
def test_criteria_category_slug():
|
|
category = CriteriaCategory.objects.create(label='Foo bar')
|
|
assert category.slug == 'foo-bar'
|
|
|
|
|
|
def test_criteria_category_existing_slug():
|
|
category = CriteriaCategory.objects.create(label='Foo bar', slug='bar')
|
|
assert category.slug == 'bar'
|
|
|
|
|
|
def test_criteria_category_duplicate_slugs():
|
|
category = CriteriaCategory.objects.create(label='Foo baz')
|
|
assert category.slug == 'foo-baz'
|
|
category = CriteriaCategory.objects.create(label='Foo baz')
|
|
assert category.slug == 'foo-baz-1'
|
|
category = CriteriaCategory.objects.create(label='Foo baz')
|
|
assert category.slug == 'foo-baz-2'
|
|
|
|
|
|
def test_criteria_slug():
|
|
category = CriteriaCategory.objects.create(label='Foo')
|
|
criteria = Criteria.objects.create(label='Foo bar', category=category)
|
|
assert criteria.slug == 'foo-bar'
|
|
|
|
|
|
def test_criteria_existing_slug():
|
|
category = CriteriaCategory.objects.create(label='Foo')
|
|
criteria = Criteria.objects.create(label='Foo bar', slug='bar', category=category)
|
|
assert criteria.slug == 'bar'
|
|
|
|
|
|
def test_criteria_duplicate_slugs():
|
|
category = CriteriaCategory.objects.create(label='Foo')
|
|
category2 = CriteriaCategory.objects.create(label='Bar')
|
|
Criteria.objects.create(label='Foo baz', slug='foo-baz', category=category2)
|
|
criteria = Criteria.objects.create(label='Foo baz', category=category)
|
|
assert criteria.slug == 'foo-baz'
|
|
criteria = Criteria.objects.create(label='Foo baz', category=category)
|
|
assert criteria.slug == 'foo-baz-1'
|
|
criteria = Criteria.objects.create(label='Foo baz', category=category)
|
|
assert criteria.slug == 'foo-baz-2'
|
|
|
|
|
|
def test_criteria_order():
|
|
category = CriteriaCategory.objects.create(label='Foo')
|
|
criteria = Criteria.objects.create(label='Foo bar', category=category)
|
|
assert criteria.order == 1
|
|
criteria = Criteria.objects.create(label='Foo bar', category=category, default=True)
|
|
assert criteria.order == 0
|
|
|
|
|
|
def test_criteria_existing_order():
|
|
category = CriteriaCategory.objects.create(label='Foo')
|
|
criteria = Criteria.objects.create(label='Foo bar', order=42, category=category)
|
|
assert criteria.order == 42
|
|
criteria = Criteria.objects.create(label='Foo bar', order=42, category=category, default=True)
|
|
assert criteria.order == 0
|
|
|
|
|
|
def test_criteria_duplicate_orders():
|
|
category = CriteriaCategory.objects.create(label='Foo')
|
|
category2 = CriteriaCategory.objects.create(label='Bar')
|
|
Criteria.objects.create(label='Foo baz', order=1, category=category2)
|
|
criteria = Criteria.objects.create(label='Foo baz', category=category)
|
|
assert criteria.order == 1
|
|
criteria = Criteria.objects.create(label='Foo baz', category=category)
|
|
assert criteria.order == 2
|
|
criteria = Criteria.objects.create(label='Foo baz', category=category)
|
|
assert criteria.order == 3
|
|
criteria.default = True
|
|
criteria.save()
|
|
assert criteria.order == 0
|
|
criteria = Criteria.objects.create(label='Foo baz', category=category, default=True)
|
|
assert criteria.order == 0
|
|
|
|
|
|
def test_pricing_slug():
|
|
pricing = Pricing.objects.create(label='Foo bar')
|
|
assert pricing.slug == 'foo-bar'
|
|
|
|
|
|
def test_pricing_existing_slug():
|
|
pricing = Pricing.objects.create(label='Foo bar', slug='bar')
|
|
assert pricing.slug == 'bar'
|
|
|
|
|
|
def test_pricing_duplicate_slugs():
|
|
pricing = Pricing.objects.create(label='Foo baz')
|
|
assert pricing.slug == 'foo-baz'
|
|
pricing = Pricing.objects.create(label='Foo baz')
|
|
assert pricing.slug == 'foo-baz-1'
|
|
pricing = Pricing.objects.create(label='Foo baz')
|
|
assert pricing.slug == 'foo-baz-2'
|
|
|
|
|
|
def test_pricing_category_criteria_order():
|
|
category = CriteriaCategory.objects.create(label='Foo')
|
|
pricing = Pricing.objects.create(label='Foo bar')
|
|
pcc = PricingCriteriaCategory.objects.create(pricing=pricing, category=category)
|
|
assert pcc.order == 1
|
|
|
|
|
|
def test_pricing_category_criteria_existing_order():
|
|
category = CriteriaCategory.objects.create(label='Foo')
|
|
pricing = Pricing.objects.create(label='Foo bar')
|
|
pcc = PricingCriteriaCategory.objects.create(order=42, pricing=pricing, category=category)
|
|
assert pcc.order == 42
|
|
|
|
|
|
def test_pricing_category_criteria_duplicate_orders():
|
|
category1 = CriteriaCategory.objects.create(label='Foo')
|
|
category2 = CriteriaCategory.objects.create(label='Bar')
|
|
category3 = CriteriaCategory.objects.create(label='Baz')
|
|
pricing = Pricing.objects.create(label='Foo bar')
|
|
pricing2 = Pricing.objects.create(label='Foo baz')
|
|
PricingCriteriaCategory.objects.create(order=1, pricing=pricing2, category=category1)
|
|
PricingCriteriaCategory.objects.create(order=2, pricing=pricing2, category=category2)
|
|
PricingCriteriaCategory.objects.create(order=3, pricing=pricing2, category=category3)
|
|
pcc = PricingCriteriaCategory.objects.create(pricing=pricing, category=category1)
|
|
assert pcc.order == 1
|
|
pcc = PricingCriteriaCategory.objects.create(pricing=pricing, category=category2)
|
|
assert pcc.order == 2
|
|
pcc = PricingCriteriaCategory.objects.create(pricing=pricing, category=category3)
|
|
assert pcc.order == 3
|
|
|
|
|
|
def test_pricing_duplicate():
|
|
category1 = CriteriaCategory.objects.create(label='Cat 1')
|
|
Criteria.objects.create(label='Crit 1-1', slug='crit-1-1', category=category1, order=1)
|
|
Criteria.objects.create(label='Crit 1-2', slug='crit-1-2', category=category1, order=2)
|
|
category2 = CriteriaCategory.objects.create(label='Cat 2')
|
|
Criteria.objects.create(label='Crit 2-1', slug='crit-2-1', category=category2, order=1)
|
|
Criteria.objects.create(label='Crit 2-2', slug='crit-2-2', category=category2, order=2)
|
|
Criteria.objects.create(label='Crit 2-3', slug='crit-2-3', category=category2, order=3)
|
|
category3 = CriteriaCategory.objects.create(label='Cat 3')
|
|
Criteria.objects.create(label='Crit 3-1', slug='crit-3-1', category=category3, order=1)
|
|
Criteria.objects.create(label='Crit 3-3', slug='crit-3-3', category=category3, order=3)
|
|
Criteria.objects.create(label='Crit 3-4', slug='crit-3-4', category=category3, order=4)
|
|
Criteria.objects.create(label='Crit 3-2', slug='crit-3-2', category=category3, order=2)
|
|
not_used = Criteria.objects.create(label='Not used', slug='crit-3-notused', category=category3, order=5)
|
|
|
|
pricing = Pricing.objects.create(
|
|
label='Foo',
|
|
extra_variables={
|
|
'foo': 'bar',
|
|
},
|
|
)
|
|
pricing.categories.add(category1, through_defaults={'order': 1})
|
|
pricing.categories.add(category2, through_defaults={'order': 2})
|
|
pricing.categories.add(category3, through_defaults={'order': 3})
|
|
pricing.criterias.set(Criteria.objects.exclude(pk=not_used.pk))
|
|
|
|
new_pricing = pricing.duplicate()
|
|
assert new_pricing.label == 'Copy of Foo'
|
|
assert new_pricing.slug == 'copy-of-foo'
|
|
assert new_pricing.extra_variables == pricing.extra_variables
|
|
assert list(new_pricing.criterias.all()) == list(pricing.criterias.all())
|
|
original_pcc_categories = list(
|
|
PricingCriteriaCategory.objects.filter(pricing=pricing).values_list('category', flat=True)
|
|
)
|
|
new_pcc_categories = list(
|
|
PricingCriteriaCategory.objects.filter(pricing=new_pricing).values_list('category', flat=True)
|
|
)
|
|
assert new_pcc_categories == original_pcc_categories
|
|
original_pcc_orders = list(
|
|
PricingCriteriaCategory.objects.filter(pricing=pricing).values_list('order', flat=True)
|
|
)
|
|
new_pcc_orders = list(
|
|
PricingCriteriaCategory.objects.filter(pricing=new_pricing).values_list('order', flat=True)
|
|
)
|
|
assert new_pcc_orders == original_pcc_orders
|
|
|
|
new_pricing = pricing.duplicate(label='Bar')
|
|
assert new_pricing.label == 'Bar'
|
|
assert new_pricing.slug == 'bar'
|
|
|
|
|
|
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(),
|
|
}
|
|
|
|
# not found
|
|
with pytest.raises(AgendaPricingNotFound):
|
|
AgendaPricing.get_agenda_pricing(agenda=agenda, event=event)
|
|
|
|
# ok
|
|
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),
|
|
)
|
|
assert AgendaPricing.get_agenda_pricing(agenda=agenda, event=event) == agenda_pricing
|
|
|
|
# more than one matching
|
|
AgendaPricing.objects.create(
|
|
agenda=agenda,
|
|
pricing=pricing,
|
|
date_start=datetime.date(year=2021, month=9, day=14),
|
|
date_end=datetime.date(year=2021, month=9, day=16),
|
|
)
|
|
with pytest.raises(AgendaPricingNotFound):
|
|
AgendaPricing.get_agenda_pricing(agenda=agenda, event=event)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'event_date, found',
|
|
[
|
|
# just before first day
|
|
((2021, 8, 31, 12, 00), False),
|
|
# first day
|
|
((2021, 9, 1, 12, 00), True),
|
|
# last day
|
|
((2021, 9, 30, 12, 00), True),
|
|
# just after last day
|
|
((2021, 10, 1, 12, 00), 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(
|
|
agenda=agenda,
|
|
pricing=pricing,
|
|
date_start=datetime.date(year=2021, month=9, day=1),
|
|
date_end=datetime.date(year=2021, month=10, day=1),
|
|
)
|
|
if found:
|
|
assert AgendaPricing.get_agenda_pricing(agenda=agenda, event=event) == agenda_pricing
|
|
else:
|
|
with pytest.raises(AgendaPricingNotFound):
|
|
AgendaPricing.get_agenda_pricing(agenda=agenda, event=event)
|
|
|
|
|
|
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
|
def test_get_pricing_context(mock_send, context, nocache):
|
|
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),
|
|
)
|
|
assert (
|
|
agenda_pricing.get_pricing_context(
|
|
request=context['request'], data={}, user_external_id='child:42', adult_external_id='parent:35'
|
|
)
|
|
== {}
|
|
)
|
|
pricing.extra_variables = {
|
|
'foo': 'bar',
|
|
'qf': '{{ 40|add:2 }}',
|
|
'domicile': 'commune',
|
|
'ids': '{{ cards|objects:"foo"|getlist:"id"|join:"," }}',
|
|
'syntax_error': '{% for %}',
|
|
'variable_error': '{{ "foo"|add:user.email }}',
|
|
'event': '{{ data.event.foo }}',
|
|
'subscription': '{{ data.subscription.foo }}',
|
|
'booking': '{{ data.booking.foo }}',
|
|
}
|
|
pricing.save()
|
|
data = {
|
|
'event': {'foo': 42},
|
|
'subscription': {'foo': 43},
|
|
'booking': {'foo': 44},
|
|
}
|
|
assert agenda_pricing.get_pricing_context(
|
|
request=context['request'], data=data, user_external_id='child:42', adult_external_id='parent:35'
|
|
) == {
|
|
'foo': 'bar',
|
|
'qf': '42',
|
|
'domicile': 'commune',
|
|
'ids': '1,2',
|
|
'event': '42',
|
|
'subscription': '43',
|
|
'booking': '44',
|
|
}
|
|
|
|
# user_external_id and adult_external_id can be used in variables
|
|
pricing.extra_variables = {
|
|
'qf': '{{ cards|objects:"qf"|filter_by:"foo"|filter_value:user_external_id|filter_by:"bar"|filter_value:adult_external_id|list }}',
|
|
}
|
|
pricing.save()
|
|
mock_send.reset_mock()
|
|
agenda_pricing.get_pricing_context(
|
|
request=context['request'], data={}, user_external_id='child:42', adult_external_id='parent:35'
|
|
)
|
|
assert 'filter-foo=child%3A42&' in mock_send.call_args_list[0][0][0].url
|
|
assert 'filter-bar=parent%3A35&' in mock_send.call_args_list[0][0][0].url
|
|
pricing.extra_variables = {
|
|
'qf': '{{ cards|objects:"qf"|filter_by:"foo"|filter_value:user_external_raw_id|filter_by:"bar"|filter_value:adult_external_raw_id|list }}',
|
|
}
|
|
pricing.save()
|
|
mock_send.reset_mock()
|
|
agenda_pricing.get_pricing_context(
|
|
request=context['request'], data={}, user_external_id='child:42', adult_external_id='parent:35'
|
|
)
|
|
assert 'filter-foo=42&' in mock_send.call_args_list[0][0][0].url
|
|
assert 'filter-bar=35&' in mock_send.call_args_list[0][0][0].url
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'condition, context, result',
|
|
[
|
|
('qf < 1', {}, False),
|
|
('qf < 1', {'qf': 'foo'}, False),
|
|
('qf < 1', {'qf': 1}, False),
|
|
('qf < 1', {'qf': 0.9}, True),
|
|
('1 <= qf and qf < 2', {'qf': 0}, False),
|
|
('1 <= qf and qf < 2', {'qf': 2}, False),
|
|
('1 <= qf and qf < 2', {'qf': 10}, False),
|
|
('1 <= qf and qf < 2', {'qf': 1}, True),
|
|
('1 <= qf and qf < 2', {'qf': 1.5}, True),
|
|
# no condition
|
|
('', {}, False),
|
|
],
|
|
)
|
|
def test_compute_condition(condition, context, result):
|
|
category = CriteriaCategory.objects.create(label='QF', slug='qf')
|
|
criteria = Criteria.objects.create(label='FOO', condition=condition, category=category)
|
|
assert criteria.compute_condition(context) == result
|
|
|
|
|
|
def test_compute_pricing():
|
|
agenda = Agenda.objects.create(label='Foo bar')
|
|
category = CriteriaCategory.objects.create(label='QF', slug='qf')
|
|
pricing = Pricing.objects.create(label='Foo bar')
|
|
pricing.categories.add(category, through_defaults={'order': 1})
|
|
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),
|
|
)
|
|
# no criteria defined on agenda_pricing
|
|
with pytest.raises(CriteriaConditionNotFound) as e:
|
|
agenda_pricing.compute_pricing(context={'qf': 2})
|
|
assert e.value.details == {'category': 'qf'}
|
|
|
|
# conditions are not set
|
|
criteria1 = Criteria.objects.create(label='QF < 1', slug='qf-0', category=category)
|
|
criteria2 = Criteria.objects.create(label='QF >= 1', slug='qf-1', category=category)
|
|
pricing.criterias.add(criteria1)
|
|
pricing.criterias.add(criteria2)
|
|
with pytest.raises(CriteriaConditionNotFound) as e:
|
|
agenda_pricing.compute_pricing(context={'qf': 2})
|
|
assert e.value.details == {'category': 'qf'}
|
|
|
|
# conditions set, but no match
|
|
criteria1.condition = 'qf < 1'
|
|
criteria1.save()
|
|
criteria2.condition = 'False'
|
|
criteria2.save()
|
|
with pytest.raises(CriteriaConditionNotFound) as e:
|
|
agenda_pricing.compute_pricing(context={'qf': 2})
|
|
assert e.value.details == {'category': 'qf'}
|
|
|
|
# but with a default criteria, there is a match, but agenda_pricing.pricing_data is not defined
|
|
default_criteria1 = Criteria.objects.create(
|
|
label='Else 1', slug='else-1', category=category, default=True
|
|
)
|
|
pricing.criterias.add(default_criteria1)
|
|
with pytest.raises(PricingDataError) as e:
|
|
agenda_pricing.compute_pricing(context={'qf': 2})
|
|
assert e.value.details == {'criterias': {'qf': 'else-1'}}
|
|
# with more than one default criteria, fail
|
|
default_criteria2 = Criteria.objects.create(
|
|
label='Else 2', slug='else-2', category=category, default=True
|
|
)
|
|
pricing.criterias.add(default_criteria2)
|
|
with pytest.raises(MultipleDefaultCriteriaCondition) as e:
|
|
agenda_pricing.compute_pricing(context={'qf': 2})
|
|
assert e.value.details == {'category': 'qf'}
|
|
Criteria.objects.filter(default=True).delete() # remove default criterias
|
|
|
|
# criteria found, but agenda_pricing.pricing_data is not defined
|
|
criteria1.condition = 'qf < 1'
|
|
criteria1.save()
|
|
criteria2.condition = 'qf >= 1'
|
|
criteria2.save()
|
|
with pytest.raises(PricingDataError) as e:
|
|
agenda_pricing.compute_pricing(context={'qf': 2})
|
|
assert e.value.details == {'criterias': {'qf': 'qf-1'}}
|
|
|
|
# criteria not found in pricing_data
|
|
agenda_pricing.pricing_data = {
|
|
'qf:qf-0': 42,
|
|
}
|
|
agenda_pricing.save()
|
|
with pytest.raises(PricingDataError) as e:
|
|
agenda_pricing.compute_pricing(context={'qf': 2})
|
|
assert e.value.details == {'criterias': {'qf': 'qf-1'}}
|
|
|
|
# criteria found, but value is wrong
|
|
for value in ['foo', ['foo']]:
|
|
agenda_pricing.pricing_data = {
|
|
'qf:qf-0': 42,
|
|
'qf:qf-1': value,
|
|
}
|
|
agenda_pricing.save()
|
|
with pytest.raises(PricingDataFormatError) as e:
|
|
agenda_pricing.compute_pricing(context={'qf': 2})
|
|
assert e.value.details == {'pricing': value, 'wanted': 'decimal'}
|
|
for value in [[], {}]:
|
|
agenda_pricing.pricing_data = {
|
|
'qf:qf-0': 42,
|
|
'qf:qf-1': value,
|
|
}
|
|
agenda_pricing.save()
|
|
with pytest.raises(PricingDataError) as e:
|
|
agenda_pricing.compute_pricing(context={'qf': 2})
|
|
assert e.value.details == {'criterias': {'qf': 'qf-1'}}
|
|
|
|
# correct value (decimal)
|
|
agenda_pricing.pricing_data = {
|
|
'qf:qf-0': 42,
|
|
'qf:qf-1': 52,
|
|
}
|
|
agenda_pricing.save()
|
|
assert agenda_pricing.compute_pricing(context={'qf': 2}) == (52, {'qf': 'qf-1'})
|
|
|
|
# more complexe pricing model
|
|
category2 = CriteriaCategory.objects.create(label='Domicile', slug='domicile')
|
|
criteria1 = Criteria.objects.create(
|
|
label='Commune', slug='dom-0', condition='domicile == "commune"', category=category2, order=1
|
|
)
|
|
criteria2 = Criteria.objects.create(
|
|
label='Hors commune', slug='else', category=category2, default=True, order=0
|
|
)
|
|
pricing.categories.add(category2, through_defaults={'order': 2})
|
|
pricing.criterias.add(criteria1)
|
|
pricing.criterias.add(criteria2)
|
|
|
|
# correct definition
|
|
agenda_pricing.pricing_data = {
|
|
'domicile:dom-0': {
|
|
'qf:qf-0': 3,
|
|
'qf:qf-1': 5,
|
|
},
|
|
'domicile:else': {
|
|
'qf:qf-0': 7,
|
|
'qf:qf-1': 10,
|
|
},
|
|
}
|
|
agenda_pricing.save()
|
|
assert agenda_pricing.compute_pricing(context={'qf': 2, 'domicile': 'commune'}) == (
|
|
5,
|
|
{'domicile': 'dom-0', 'qf': 'qf-1'},
|
|
)
|
|
assert agenda_pricing.compute_pricing(context={'qf': 0, 'domicile': 'commune'}) == (
|
|
3,
|
|
{'domicile': 'dom-0', 'qf': 'qf-0'},
|
|
)
|
|
assert agenda_pricing.compute_pricing(context={'qf': 2, 'domicile': 'ext'}) == (
|
|
10,
|
|
{'domicile': 'else', 'qf': 'qf-1'},
|
|
)
|
|
assert agenda_pricing.compute_pricing(context={'qf': 0, 'domicile': 'ext'}) == (
|
|
7,
|
|
{'domicile': 'else', 'qf': 'qf-0'},
|
|
)
|
|
|
|
# category ordering doesn't matter
|
|
PricingCriteriaCategory.objects.filter(pricing=pricing, category=category).update(order=2)
|
|
PricingCriteriaCategory.objects.filter(pricing=pricing, category=category2).update(order=1)
|
|
assert agenda_pricing.compute_pricing(context={'qf': 2, 'domicile': 'commune'}) == (
|
|
5,
|
|
{'domicile': 'dom-0', 'qf': 'qf-1'},
|
|
)
|
|
assert agenda_pricing.compute_pricing(context={'qf': 0, 'domicile': 'commune'}) == (
|
|
3,
|
|
{'domicile': 'dom-0', 'qf': 'qf-0'},
|
|
)
|
|
assert agenda_pricing.compute_pricing(context={'qf': 2, 'domicile': 'ext'}) == (
|
|
10,
|
|
{'domicile': 'else', 'qf': 'qf-1'},
|
|
)
|
|
assert agenda_pricing.compute_pricing(context={'qf': 0, 'domicile': 'ext'}) == (
|
|
7,
|
|
{'domicile': 'else', 'qf': 'qf-0'},
|
|
)
|
|
|
|
|
|
def test_format_pricing_data():
|
|
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),
|
|
)
|
|
assert agenda_pricing.format_pricing_data() == {}
|
|
|
|
agenda_pricing.pricing_data = {
|
|
'cat-1:crit-1-1': {
|
|
'cat-2:crit-2-1': {
|
|
'cat-3:crit-3-1': 111,
|
|
'cat-3:crit-3-3': 'not-a-decimal',
|
|
'cat-3:crit-3-4': 114,
|
|
},
|
|
'cat-2:crit-2-3': {
|
|
'cat-3:crit-3-2': 132,
|
|
},
|
|
},
|
|
'cat-1:crit-1-2': {
|
|
'cat-2:crit-2-2': {
|
|
'cat-3:crit-3-3': 223,
|
|
},
|
|
},
|
|
}
|
|
agenda_pricing.save()
|
|
assert agenda_pricing.format_pricing_data() == {
|
|
'cat-1:crit-1-1||cat-2:crit-2-1||cat-3:crit-3-1': 111,
|
|
'cat-1:crit-1-1||cat-2:crit-2-1||cat-3:crit-3-3': 'not-a-decimal',
|
|
'cat-1:crit-1-1||cat-2:crit-2-1||cat-3:crit-3-4': 114,
|
|
'cat-1:crit-1-1||cat-2:crit-2-3||cat-3:crit-3-2': 132,
|
|
'cat-1:crit-1-2||cat-2:crit-2-2||cat-3:crit-3-3': 223,
|
|
}
|
|
|
|
agenda_pricing.pricing_data = {'foo': 42}
|
|
agenda_pricing.save()
|
|
assert agenda_pricing.format_pricing_data() == {'foo': 42}
|
|
|
|
# wrong data
|
|
agenda_pricing.pricing_data = 'foo'
|
|
agenda_pricing.save()
|
|
assert agenda_pricing.format_pricing_data() == {'': 'foo'}
|
|
agenda_pricing.pricing_data = []
|
|
agenda_pricing.save()
|
|
assert agenda_pricing.format_pricing_data() == {}
|
|
agenda_pricing.pricing_data = ['foo']
|
|
agenda_pricing.save()
|
|
assert agenda_pricing.format_pricing_data() == {'': ['foo']}
|
|
agenda_pricing.pricing_data = {'foo': []}
|
|
agenda_pricing.save()
|
|
assert agenda_pricing.format_pricing_data() == {}
|
|
|
|
|
|
def test_get_booking_modifier_unknown_check_status():
|
|
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),
|
|
)
|
|
check_status = {'status': 'unknown'}
|
|
with pytest.raises(PricingUnknownCheckStatusError):
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
|
|
|
|
def test_get_booking_modifier_event_not_checked():
|
|
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),
|
|
)
|
|
check_status = {'status': 'error', 'error_reason': 'event-not-checked'}
|
|
with pytest.raises(PricingEventNotCheckedError):
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
|
|
|
|
def test_get_booking_modifier_no_booking():
|
|
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),
|
|
)
|
|
check_status = {'status': 'not-booked'}
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'not-booked',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 0,
|
|
}
|
|
|
|
# more than one booking found !
|
|
check_status = {'status': 'error', 'error_reason': 'too-many-bookings-found'}
|
|
with pytest.raises(PricingMultipleBookingError):
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
|
|
|
|
def test_get_booking_modifier_booking_cancelled():
|
|
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),
|
|
)
|
|
check_status = {'status': 'cancelled'}
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'cancelled',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 0,
|
|
}
|
|
|
|
|
|
def test_get_booking_modifier_booking_not_checked():
|
|
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),
|
|
)
|
|
check_status = {'status': 'error', 'error_reason': 'booking-not-checked'}
|
|
with pytest.raises(PricingBookingNotCheckedError):
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
|
|
|
|
def test_get_booking_modifier_unknown_check_type():
|
|
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),
|
|
)
|
|
check_status = {'status': 'presence', 'check_type': 'unknown'}
|
|
with pytest.raises(PricingBookingCheckTypeError) as e:
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
assert e.value.details == {
|
|
'reason': 'not-found',
|
|
}
|
|
check_status = {'status': 'absence', 'check_type': 'unknown'}
|
|
with pytest.raises(PricingBookingCheckTypeError) as e:
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
assert e.value.details == {
|
|
'reason': 'not-found',
|
|
}
|
|
|
|
|
|
def test_get_booking_modifier_booking_absence():
|
|
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),
|
|
)
|
|
|
|
# no check type
|
|
check_status = {'status': 'absence', 'check_type': ''}
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'absence',
|
|
'check_type_group': None,
|
|
'check_type': None,
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 0,
|
|
}
|
|
|
|
# check_type but not configured on agenda
|
|
group = CheckTypeGroup.objects.create(label='Foo bar')
|
|
check_type = CheckType.objects.create(label='Foo reason', group=group, kind='absence')
|
|
check_status = {'status': 'absence', 'check_type': check_type.slug}
|
|
with pytest.raises(PricingBookingCheckTypeError) as e:
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
assert e.value.details == {
|
|
'reason': 'not-found',
|
|
}
|
|
# incomplete configuration
|
|
agenda.check_type_group = group
|
|
agenda.save()
|
|
with pytest.raises(PricingBookingCheckTypeError) as e:
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
assert e.value.details == {
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'reason': 'not-configured',
|
|
}
|
|
|
|
check_type.pricing = 42
|
|
check_type.save()
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'absence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'fixed',
|
|
'modifier_fixed': 42,
|
|
}
|
|
|
|
check_type.pricing = 0
|
|
check_type.save()
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'absence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'fixed',
|
|
'modifier_fixed': 0,
|
|
}
|
|
|
|
check_type.pricing = None
|
|
check_type.pricing_rate = 20
|
|
check_type.save()
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'absence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 20,
|
|
}
|
|
|
|
check_type.pricing_rate = 0
|
|
check_type.save()
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'absence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 0,
|
|
}
|
|
|
|
# bad check type kind
|
|
check_type.kind = 'presence'
|
|
check_type.save()
|
|
with pytest.raises(PricingBookingCheckTypeError) as e:
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
assert e.value.details == {
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'reason': 'wrong-kind',
|
|
}
|
|
|
|
|
|
def test_get_booking_modifier_booking_presence():
|
|
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),
|
|
)
|
|
|
|
# no check type
|
|
check_status = {'status': 'presence', 'check_type': ''}
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'presence',
|
|
'check_type_group': None,
|
|
'check_type': None,
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 100,
|
|
}
|
|
|
|
# check_type but not configured on agenda
|
|
group = CheckTypeGroup.objects.create(label='Foo bar')
|
|
check_type = CheckType.objects.create(label='Foo reason', group=group, kind='presence')
|
|
check_status = {'status': 'presence', 'check_type': check_type.slug}
|
|
with pytest.raises(PricingBookingCheckTypeError) as e:
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
assert e.value.details == {
|
|
'reason': 'not-found',
|
|
}
|
|
# incomplete configuration
|
|
agenda.check_type_group = group
|
|
agenda.save()
|
|
with pytest.raises(PricingBookingCheckTypeError) as e:
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
assert e.value.details == {
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'reason': 'not-configured',
|
|
}
|
|
|
|
check_type.pricing = 42
|
|
check_type.save()
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'presence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'fixed',
|
|
'modifier_fixed': 42,
|
|
}
|
|
|
|
check_type.pricing = 0
|
|
check_type.save()
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'presence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'fixed',
|
|
'modifier_fixed': 0,
|
|
}
|
|
|
|
check_type.pricing = None
|
|
check_type.pricing_rate = 150
|
|
check_type.save()
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'presence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 150,
|
|
}
|
|
|
|
check_type.pricing_rate = 0
|
|
check_type.save()
|
|
assert agenda_pricing.get_booking_modifier(check_status=check_status) == {
|
|
'status': 'presence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 0,
|
|
}
|
|
|
|
# bad check type kind
|
|
check_type.kind = 'absence'
|
|
check_type.save()
|
|
with pytest.raises(PricingBookingCheckTypeError) as e:
|
|
agenda_pricing.get_booking_modifier(check_status=check_status)
|
|
assert e.value.details == {
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'reason': 'wrong-kind',
|
|
}
|
|
|
|
|
|
def test_get_pricing_data(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)
|
|
pricing = Pricing.objects.create(
|
|
label='Foo bar',
|
|
extra_variables={
|
|
'domicile': 'commune',
|
|
'qf': '2',
|
|
},
|
|
)
|
|
pricing.criterias.add(criteria)
|
|
pricing.categories.add(category, through_defaults={'order': 1})
|
|
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),
|
|
pricing_data={
|
|
'foo:bar': 42,
|
|
},
|
|
)
|
|
assert AgendaPricing.get_pricing_data(
|
|
request=context['request'],
|
|
agenda=agenda,
|
|
event={'start_datetime': make_aware(datetime.datetime(2021, 9, 15, 12, 00)).isoformat()},
|
|
subscription={},
|
|
check_status={'status': 'not-booked'},
|
|
booking={},
|
|
user_external_id='child:42',
|
|
adult_external_id='parent:35',
|
|
) == {
|
|
'pricing': 0,
|
|
'calculation_details': {
|
|
'pricing': 42,
|
|
'criterias': {'foo': 'bar'},
|
|
'context': {'domicile': 'commune', 'qf': '2'},
|
|
},
|
|
'booking_details': {
|
|
'status': 'not-booked',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 0,
|
|
},
|
|
}
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
'modifier, pricing_amount',
|
|
[
|
|
# not booked
|
|
(
|
|
{
|
|
'status': 'not-booked',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 0,
|
|
},
|
|
0,
|
|
),
|
|
# cancelled
|
|
(
|
|
{
|
|
'status': 'cancelled',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 0,
|
|
},
|
|
0,
|
|
),
|
|
# absence
|
|
(
|
|
{
|
|
'status': 'absence',
|
|
'check_type_group': None,
|
|
'check_type': None,
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 0,
|
|
},
|
|
0,
|
|
),
|
|
(
|
|
{
|
|
'status': 'absence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'fixed',
|
|
'modifier_fixed': 35,
|
|
},
|
|
35,
|
|
),
|
|
(
|
|
{
|
|
'status': 'absence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'fixed',
|
|
'modifier_fixed': 0,
|
|
},
|
|
0,
|
|
),
|
|
(
|
|
{
|
|
'status': 'absence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 20,
|
|
},
|
|
8.4,
|
|
),
|
|
(
|
|
{
|
|
'status': 'absence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 0,
|
|
},
|
|
0,
|
|
),
|
|
# presence
|
|
(
|
|
{
|
|
'status': 'presence',
|
|
'check_type_group': None,
|
|
'check_type': None,
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 100,
|
|
},
|
|
42,
|
|
),
|
|
(
|
|
{
|
|
'status': 'presence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'fixed',
|
|
'modifier_fixed': 35,
|
|
},
|
|
35,
|
|
),
|
|
(
|
|
{
|
|
'status': 'presence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'fixed',
|
|
'modifier_fixed': 0,
|
|
},
|
|
0,
|
|
),
|
|
(
|
|
{
|
|
'status': 'presence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 150,
|
|
},
|
|
63,
|
|
),
|
|
(
|
|
{
|
|
'status': 'presence',
|
|
'check_type_group': 'foo-bar',
|
|
'check_type': 'foo-reason',
|
|
'modifier_type': 'rate',
|
|
'modifier_rate': 0,
|
|
},
|
|
0,
|
|
),
|
|
],
|
|
)
|
|
def test_aggregate_pricing_data(modifier, pricing_amount):
|
|
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),
|
|
)
|
|
|
|
assert agenda_pricing.aggregate_pricing_data(
|
|
pricing=42, criterias={'foo': 'bar'}, context={'domicile': 'commune', 'qf': 2}, modifier=modifier
|
|
) == {
|
|
'pricing': pricing_amount,
|
|
'calculation_details': {
|
|
'pricing': 42,
|
|
'criterias': {'foo': 'bar'},
|
|
'context': {'domicile': 'commune', 'qf': 2},
|
|
},
|
|
'booking_details': modifier,
|
|
}
|
|
|
|
|
|
def test_agenda_pricing_iter_pricing_matrix_3_categories():
|
|
category1 = CriteriaCategory.objects.create(label='Cat 1')
|
|
criteria11 = Criteria.objects.create(label='Crit 1-1', slug='crit-1-1', category=category1, order=1)
|
|
criteria12 = Criteria.objects.create(label='Crit 1-2', slug='crit-1-2', category=category1, order=2)
|
|
category2 = CriteriaCategory.objects.create(label='Cat 2')
|
|
criteria21 = Criteria.objects.create(label='Crit 2-1', slug='crit-2-1', category=category2, order=1)
|
|
criteria22 = Criteria.objects.create(label='Crit 2-2', slug='crit-2-2', category=category2, order=2)
|
|
criteria23 = Criteria.objects.create(label='Crit 2-3', slug='crit-2-3', category=category2, order=3)
|
|
category3 = CriteriaCategory.objects.create(label='Cat 3')
|
|
criteria31 = Criteria.objects.create(label='Crit 3-1', slug='crit-3-1', category=category3, order=1)
|
|
criteria33 = Criteria.objects.create(label='Crit 3-3', slug='crit-3-3', category=category3, order=3)
|
|
criteria34 = Criteria.objects.create(label='Crit 3-4', slug='crit-3-4', category=category3, order=4)
|
|
criteria32 = Criteria.objects.create(label='Crit 3-2', slug='crit-3-2', category=category3, order=2)
|
|
not_used = Criteria.objects.create(label='Not used', slug='crit-3-notused', category=category3, order=5)
|
|
category4 = CriteriaCategory.objects.create(label='Cat 4') # ignored
|
|
|
|
agenda = Agenda.objects.create(label='Foo bar')
|
|
pricing = Pricing.objects.create(label='Foo bar')
|
|
pricing.categories.add(category1, through_defaults={'order': 1})
|
|
pricing.categories.add(category2, through_defaults={'order': 2})
|
|
pricing.categories.add(category3, through_defaults={'order': 3})
|
|
pricing.categories.add(category4, through_defaults={'order': 4})
|
|
pricing.criterias.set(Criteria.objects.exclude(pk=not_used.pk))
|
|
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),
|
|
)
|
|
assert list(agenda_pricing.iter_pricing_matrix()) == [
|
|
PricingMatrix(
|
|
criteria=criteria11,
|
|
rows=[
|
|
PricingMatrixRow(
|
|
criteria=criteria31,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria32,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria33,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria34,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
PricingMatrix(
|
|
criteria=criteria12,
|
|
rows=[
|
|
PricingMatrixRow(
|
|
criteria=criteria31,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria32,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria33,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria34,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
]
|
|
|
|
# some data defined
|
|
agenda_pricing.pricing_data = {
|
|
'cat-1:crit-1-1': {
|
|
'cat-2:crit-2-1': {
|
|
'cat-3:crit-3-1': 111,
|
|
'cat-3:crit-3-3': 'not-a-decimal',
|
|
'cat-3:crit-3-4': 114,
|
|
},
|
|
'cat-2:crit-2-3': {
|
|
'cat-3:crit-3-2': 132,
|
|
},
|
|
},
|
|
'cat-1:crit-1-2': {
|
|
'cat-2:crit-2-2': {
|
|
'cat-3:crit-3-3': 223,
|
|
},
|
|
},
|
|
}
|
|
agenda_pricing.save()
|
|
# category ordering doesn't matter
|
|
PricingCriteriaCategory.objects.filter(pricing=pricing, category=category1).update(order=2)
|
|
PricingCriteriaCategory.objects.filter(pricing=pricing, category=category2).update(order=1)
|
|
assert list(agenda_pricing.iter_pricing_matrix()) == [
|
|
PricingMatrix(
|
|
criteria=criteria21,
|
|
rows=[
|
|
PricingMatrixRow(
|
|
criteria=criteria31,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=111),
|
|
PricingMatrixCell(criteria=criteria12, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria32,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=None),
|
|
PricingMatrixCell(criteria=criteria12, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria33,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=None),
|
|
PricingMatrixCell(criteria=criteria12, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria34,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=114),
|
|
PricingMatrixCell(criteria=criteria12, value=None),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
PricingMatrix(
|
|
criteria=criteria22,
|
|
rows=[
|
|
PricingMatrixRow(
|
|
criteria=criteria31,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=None),
|
|
PricingMatrixCell(criteria=criteria12, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria32,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=None),
|
|
PricingMatrixCell(criteria=criteria12, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria33,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=None),
|
|
PricingMatrixCell(criteria=criteria12, value=223),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria34,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=None),
|
|
PricingMatrixCell(criteria=criteria12, value=None),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
PricingMatrix(
|
|
criteria=criteria23,
|
|
rows=[
|
|
PricingMatrixRow(
|
|
criteria=criteria31,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=None),
|
|
PricingMatrixCell(criteria=criteria12, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria32,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=132),
|
|
PricingMatrixCell(criteria=criteria12, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria33,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=None),
|
|
PricingMatrixCell(criteria=criteria12, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria34,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria11, value=None),
|
|
PricingMatrixCell(criteria=criteria12, value=None),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
]
|
|
|
|
|
|
def test_agenda_pricing_iter_pricing_matrix_2_categories():
|
|
category2 = CriteriaCategory.objects.create(label='Cat 2')
|
|
criteria21 = Criteria.objects.create(label='Crit 2-1', slug='crit-2-1', category=category2, order=1)
|
|
criteria22 = Criteria.objects.create(label='Crit 2-2', slug='crit-2-2', category=category2, order=2)
|
|
criteria23 = Criteria.objects.create(label='Crit 2-3', slug='crit-2-3', category=category2, order=3)
|
|
category3 = CriteriaCategory.objects.create(label='Cat 3')
|
|
criteria31 = Criteria.objects.create(label='Crit 3-1', slug='crit-3-1', category=category3, order=1)
|
|
criteria33 = Criteria.objects.create(label='Crit 3-3', slug='crit-3-3', category=category3, order=3)
|
|
criteria34 = Criteria.objects.create(label='Crit 3-4', slug='crit-3-4', category=category3, order=4)
|
|
criteria32 = Criteria.objects.create(label='Crit 3-2', slug='crit-3-2', category=category3, order=2)
|
|
not_used = Criteria.objects.create(label='Not used', slug='crit-3-notused', category=category3, order=5)
|
|
|
|
agenda = Agenda.objects.create(label='Foo bar')
|
|
pricing = Pricing.objects.create(label='Foo bar')
|
|
pricing.categories.add(category2, through_defaults={'order': 2})
|
|
pricing.categories.add(category3, through_defaults={'order': 3})
|
|
pricing.criterias.set(Criteria.objects.exclude(pk=not_used.pk))
|
|
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),
|
|
)
|
|
|
|
assert list(agenda_pricing.iter_pricing_matrix()) == [
|
|
PricingMatrix(
|
|
criteria=None,
|
|
rows=[
|
|
PricingMatrixRow(
|
|
criteria=criteria31,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria32,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria33,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria34,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
]
|
|
|
|
# some data defined
|
|
agenda_pricing.pricing_data = {
|
|
'cat-2:crit-2-1': {
|
|
'cat-3:crit-3-1': 11,
|
|
'cat-3:crit-3-3': 'not-a-decimal',
|
|
'cat-3:crit-3-4': 14,
|
|
},
|
|
'cat-2:crit-2-3': {
|
|
'cat-3:crit-3-2': 32,
|
|
},
|
|
}
|
|
agenda_pricing.save()
|
|
assert list(agenda_pricing.iter_pricing_matrix()) == [
|
|
PricingMatrix(
|
|
criteria=None,
|
|
rows=[
|
|
PricingMatrixRow(
|
|
criteria=criteria31,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=11),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria32,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=32),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria33,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=None),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria34,
|
|
cells=[
|
|
PricingMatrixCell(criteria=criteria21, value=14),
|
|
PricingMatrixCell(criteria=criteria22, value=None),
|
|
PricingMatrixCell(criteria=criteria23, value=None),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
]
|
|
|
|
|
|
def test_agenda_pricing_iter_pricing_matrix_1_category():
|
|
category3 = CriteriaCategory.objects.create(label='Cat 3')
|
|
criteria31 = Criteria.objects.create(label='Crit 3-1', slug='crit-3-1', category=category3, order=1)
|
|
criteria33 = Criteria.objects.create(label='Crit 3-3', slug='crit-3-3', category=category3, order=3)
|
|
criteria34 = Criteria.objects.create(label='Crit 3-4', slug='crit-3-4', category=category3, order=4)
|
|
criteria32 = Criteria.objects.create(label='Crit 3-2', slug='crit-3-2', category=category3, order=2)
|
|
not_used = Criteria.objects.create(label='Not used', slug='crit-3-notused', category=category3, order=5)
|
|
|
|
agenda = Agenda.objects.create(label='Foo bar')
|
|
pricing = Pricing.objects.create(label='Foo bar')
|
|
pricing.categories.add(category3, through_defaults={'order': 3})
|
|
pricing.criterias.set(Criteria.objects.exclude(pk=not_used.pk))
|
|
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),
|
|
)
|
|
|
|
assert list(agenda_pricing.iter_pricing_matrix()) == [
|
|
PricingMatrix(
|
|
criteria=None,
|
|
rows=[
|
|
PricingMatrixRow(
|
|
criteria=criteria31,
|
|
cells=[
|
|
PricingMatrixCell(criteria=None, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria32,
|
|
cells=[
|
|
PricingMatrixCell(criteria=None, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria33,
|
|
cells=[
|
|
PricingMatrixCell(criteria=None, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria34,
|
|
cells=[
|
|
PricingMatrixCell(criteria=None, value=None),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
]
|
|
|
|
# some data defined
|
|
agenda_pricing.pricing_data = {
|
|
'cat-3:crit-3-1': 1,
|
|
'cat-3:crit-3-3': 'not-a-decimal',
|
|
'cat-3:crit-3-4': 4,
|
|
}
|
|
agenda_pricing.save()
|
|
assert list(agenda_pricing.iter_pricing_matrix()) == [
|
|
PricingMatrix(
|
|
criteria=None,
|
|
rows=[
|
|
PricingMatrixRow(
|
|
criteria=criteria31,
|
|
cells=[
|
|
PricingMatrixCell(criteria=None, value=1),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria32,
|
|
cells=[
|
|
PricingMatrixCell(criteria=None, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria33,
|
|
cells=[
|
|
PricingMatrixCell(criteria=None, value=None),
|
|
],
|
|
),
|
|
PricingMatrixRow(
|
|
criteria=criteria34,
|
|
cells=[
|
|
PricingMatrixCell(criteria=None, value=4),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
]
|
|
|
|
|
|
def test_agenda_pricing_iter_pricing_matrix_empty():
|
|
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),
|
|
)
|
|
|
|
assert list(agenda_pricing.iter_pricing_matrix()) == []
|