pricing: add pricing variables (#64561)
This commit is contained in:
parent
eb67f72b35
commit
81722913a4
|
@ -0,0 +1,17 @@
|
|||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pricing', '0002_pricing'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='pricing',
|
||||
name='extra_variables',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict),
|
||||
),
|
||||
]
|
|
@ -18,7 +18,7 @@ import decimal
|
|||
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
from django.db import models
|
||||
from django.template import Context, Template, TemplateSyntaxError
|
||||
from django.template import Context, RequestContext, Template, TemplateSyntaxError
|
||||
from django.utils.text import slugify
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
|
@ -173,6 +173,7 @@ class Pricing(models.Model):
|
|||
through='PricingCriteriaCategory',
|
||||
)
|
||||
criterias = models.ManyToManyField(Criteria)
|
||||
extra_variables = JSONField(blank=True, default=dict)
|
||||
|
||||
class Meta:
|
||||
ordering = ['label']
|
||||
|
@ -189,6 +190,18 @@ class Pricing(models.Model):
|
|||
def base_slug(self):
|
||||
return slugify(self.label)
|
||||
|
||||
def get_extra_variables(self, request, original_context):
|
||||
result = {}
|
||||
context = RequestContext(request)
|
||||
context.push(original_context)
|
||||
for key, tplt in (self.extra_variables or {}).items():
|
||||
try:
|
||||
template = Template(tplt)
|
||||
except TemplateSyntaxError:
|
||||
continue
|
||||
result[key] = template.render(context)
|
||||
return result
|
||||
|
||||
|
||||
class PricingCriteriaCategory(models.Model):
|
||||
pricing = models.ForeignKey(Pricing, on_delete=models.CASCADE)
|
||||
|
@ -219,9 +232,9 @@ class AgendaPricing(models.Model):
|
|||
pricing_data = JSONField(null=True)
|
||||
|
||||
@staticmethod
|
||||
def get_pricing_data(event, user_external_id, adult_external_id):
|
||||
def get_pricing_data(request, event, user_external_id, adult_external_id):
|
||||
agenda_pricing = AgendaPricing.get_agenda_pricing(event)
|
||||
context = agenda_pricing.get_pricing_context(event, user_external_id, adult_external_id)
|
||||
context = agenda_pricing.get_pricing_context(request, user_external_id, adult_external_id)
|
||||
pricing, criterias = agenda_pricing.compute_pricing(context)
|
||||
modifier = agenda_pricing.get_booking_modifier(event, user_external_id)
|
||||
return agenda_pricing.aggregate_pricing_data(pricing, criterias, context, modifier)
|
||||
|
@ -262,12 +275,13 @@ class AgendaPricing(models.Model):
|
|||
except (Subscription.DoesNotExist, Subscription.MultipleObjectsReturned):
|
||||
raise PricingSubscriptionError
|
||||
|
||||
def get_pricing_context(self, event, user_external_id, adult_external_id):
|
||||
# FIXME: compute agenda pricing variables, add event data
|
||||
return {
|
||||
'qf': 2,
|
||||
'domicile': 'commune',
|
||||
}
|
||||
def get_pricing_context(self, request, user_external_id, adult_external_id):
|
||||
context = {'user_external_id': user_external_id, 'adult_external_id': adult_external_id}
|
||||
if ':' in user_external_id:
|
||||
context['user_external_raw_id'] = user_external_id.split(':')[1]
|
||||
if ':' in adult_external_id:
|
||||
context['adult_external_raw_id'] = adult_external_id.split(':')[1]
|
||||
return self.pricing.get_extra_variables(request, context)
|
||||
|
||||
def compute_pricing(self, context):
|
||||
criterias = {}
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
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, now
|
||||
from publik_django_templatetags.wcs.context_processors import Cards
|
||||
|
||||
from chrono.agendas.models import Agenda, Booking, CheckType, CheckTypeGroup, Event, Subscription
|
||||
from chrono.pricing.models import (
|
||||
|
@ -24,6 +29,28 @@ from chrono.pricing.models import (
|
|||
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'
|
||||
|
@ -204,7 +231,8 @@ def test_get_agenda_pricing_event_date(event_date, found):
|
|||
AgendaPricing.get_agenda_pricing(event)
|
||||
|
||||
|
||||
def test_get_pricing_context():
|
||||
@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', kind='events')
|
||||
event = Event.objects.create(
|
||||
agenda=agenda, start_datetime=make_aware(datetime.datetime(2021, 9, 15, 12, 00)), places=10
|
||||
|
@ -216,10 +244,39 @@ def test_get_pricing_context():
|
|||
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(event, 'child:42', 'parent:35') == {
|
||||
assert agenda_pricing.get_pricing_context(event, 'child:42', 'parent:35') == {}
|
||||
pricing.extra_variables = {
|
||||
'foo': 'bar',
|
||||
'qf': '{{ 40|add:2 }}',
|
||||
'domicile': 'commune',
|
||||
'qf': 2,
|
||||
'ids': '{{ cards|objects:"foo"|getlist:"id"|join:"," }}',
|
||||
'bad': '{% if foo %}',
|
||||
}
|
||||
pricing.save()
|
||||
assert agenda_pricing.get_pricing_context(event, 'child:42', 'parent:35') == {
|
||||
'foo': 'bar',
|
||||
'qf': '42',
|
||||
'domicile': 'commune',
|
||||
'ids': '1,2',
|
||||
}
|
||||
|
||||
# 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(event, 'child:42', '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(event, 'child:42', '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(
|
||||
|
@ -745,7 +802,7 @@ def test_get_booking_modifier_booking_presence():
|
|||
}
|
||||
|
||||
|
||||
def test_get_pricing_data():
|
||||
def test_get_pricing_data(context):
|
||||
agenda = Agenda.objects.create(label='Foo bar', kind='events')
|
||||
event = Event.objects.create(
|
||||
agenda=agenda,
|
||||
|
@ -761,7 +818,13 @@ def test_get_pricing_data():
|
|||
)
|
||||
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')
|
||||
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(
|
||||
|
@ -773,12 +836,12 @@ def test_get_pricing_data():
|
|||
'foo:bar': 42,
|
||||
},
|
||||
)
|
||||
assert AgendaPricing.get_pricing_data(event, 'child:42', 'parent:35') == {
|
||||
assert AgendaPricing.get_pricing_data(context['request'], event, 'child:42', 'parent:35') == {
|
||||
'pricing': 0,
|
||||
'calculation_details': {
|
||||
'pricing': 42,
|
||||
'criterias': {'foo': 'bar'},
|
||||
'context': {'domicile': 'commune', 'qf': 2},
|
||||
'context': {'domicile': 'commune', 'qf': '2'},
|
||||
},
|
||||
'booking_details': {
|
||||
'status': 'not-booked',
|
||||
|
|
Loading…
Reference in New Issue