invoicing: move payer_data calculation in Payer model (#78015)
This commit is contained in:
parent
abddf1175f
commit
9d57534786
|
@ -26,6 +26,7 @@ from django.contrib.auth.models import Group
|
|||
from django.core import validators
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.db import connection, models, transaction
|
||||
from django.template import RequestContext, Template
|
||||
from django.template.loader import get_template
|
||||
from django.utils.formats import date_format
|
||||
from django.utils.text import slugify
|
||||
|
@ -47,6 +48,20 @@ class PoolPromotionError(Exception):
|
|||
self.msg = msg
|
||||
|
||||
|
||||
class InvoicingError(Exception):
|
||||
def __init__(self, details=None):
|
||||
self.details = details or {}
|
||||
super().__init__()
|
||||
|
||||
|
||||
class PayerError(InvoicingError):
|
||||
pass
|
||||
|
||||
|
||||
class PayerDataError(InvoicingError):
|
||||
pass
|
||||
|
||||
|
||||
class Payer(models.Model):
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
slug = models.SlugField(_('Identifier'), max_length=160, unique=True)
|
||||
|
@ -156,6 +171,44 @@ class Payer(models.Model):
|
|||
result.append((label, value))
|
||||
return result
|
||||
|
||||
def get_payer_data(self, request, payer_external_id):
|
||||
if not self.carddef_reference:
|
||||
raise PayerError(details={'reason': 'missing-card-model'})
|
||||
result = {}
|
||||
context = RequestContext(request)
|
||||
payer_external_raw_id = None
|
||||
if ':' in payer_external_id:
|
||||
payer_external_raw_id = payer_external_id.split(':')[1]
|
||||
context.push({'payer_external_id': payer_external_raw_id or payer_external_id})
|
||||
bool_keys = ['demat', 'direct_debit']
|
||||
for key, dummy in self.user_variables:
|
||||
if not self.user_fields_mapping.get(key):
|
||||
if key not in bool_keys:
|
||||
raise PayerDataError(details={'key': key, 'reason': 'not-defined'})
|
||||
tplt = 'False'
|
||||
else:
|
||||
tplt = (
|
||||
'{{ cards|objects:"%s"|filter_by_internal_id:payer_external_id|include_fields|first|get:"fields"|get:"%s"|default:"" }}'
|
||||
% (
|
||||
self.carddef_reference.split(':')[1],
|
||||
self.user_fields_mapping[key],
|
||||
)
|
||||
)
|
||||
value = Template(tplt).render(context)
|
||||
if not value:
|
||||
if key not in bool_keys:
|
||||
raise PayerDataError(details={'key': key, 'reason': 'empty-result'})
|
||||
value = False
|
||||
if key in bool_keys:
|
||||
if value in ('True', 'true', '1'):
|
||||
value = True
|
||||
elif value in ('False', 'false', '0'):
|
||||
value = False
|
||||
else:
|
||||
raise PayerDataError(details={'key': key, 'reason': 'not-a-boolean'})
|
||||
result[key] = value
|
||||
return result
|
||||
|
||||
|
||||
class Regie(models.Model):
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
|
@ -248,6 +301,11 @@ class Regie(models.Model):
|
|||
regie_id=self.pk,
|
||||
)
|
||||
|
||||
def get_payer_data(self, request, payer_external_id):
|
||||
if not self.payer:
|
||||
raise PayerError(details={'reason': 'missing-payer'})
|
||||
return self.payer.get_payer_data(request, payer_external_id)
|
||||
|
||||
|
||||
class Campaign(models.Model):
|
||||
label = models.CharField(_('Label'), max_length=150)
|
||||
|
|
|
@ -76,7 +76,7 @@ def get_invoice_lines_for_user(
|
|||
if payer_external_id not in payer_data_cache:
|
||||
if agenda_pricing:
|
||||
# will raise a PricingError if payer_data can not be computed
|
||||
payer_data_cache[payer_external_id] = agenda_pricing.get_payer_data(
|
||||
payer_data_cache[payer_external_id] = pool.campaign.regie.get_payer_data(
|
||||
request, payer_external_id
|
||||
)
|
||||
elif payer_data:
|
||||
|
@ -143,10 +143,10 @@ def get_invoice_lines_for_user(
|
|||
user_first_name=user_first_name,
|
||||
user_last_name=user_last_name,
|
||||
payer_external_id=payer_external_id,
|
||||
payer_first_name=payer_data.get('payer_first_name') or '',
|
||||
payer_last_name=payer_data.get('payer_last_name') or '',
|
||||
payer_demat=payer_data.get('payer_demat') or False,
|
||||
payer_direct_debit=payer_data.get('payer_direct_debit') or False,
|
||||
payer_first_name=payer_data.get('first_name') or '',
|
||||
payer_last_name=payer_data.get('last_name') or '',
|
||||
payer_demat=payer_data.get('demat') or False,
|
||||
payer_direct_debit=payer_data.get('direct_debit') or False,
|
||||
event=serialized_event,
|
||||
pricing_data=pricing_error,
|
||||
status='warning' if isinstance(e, AgendaPricingNotFound) else 'error',
|
||||
|
@ -167,10 +167,10 @@ def get_invoice_lines_for_user(
|
|||
user_first_name=user_first_name,
|
||||
user_last_name=user_last_name,
|
||||
payer_external_id=payer_external_id,
|
||||
payer_first_name=payer_data['payer_first_name'],
|
||||
payer_last_name=payer_data['payer_last_name'],
|
||||
payer_demat=payer_data['payer_demat'],
|
||||
payer_direct_debit=payer_data['payer_direct_debit'],
|
||||
payer_first_name=payer_data['first_name'],
|
||||
payer_last_name=payer_data['last_name'],
|
||||
payer_demat=payer_data['demat'],
|
||||
payer_direct_debit=payer_data['direct_debit'],
|
||||
event=serialized_event,
|
||||
pricing_data=pricing_data,
|
||||
status='success',
|
||||
|
@ -206,10 +206,10 @@ def get_invoice_lines_for_user(
|
|||
for injected_line in injected_lines:
|
||||
payer_external_id = injected_line.payer_external_id
|
||||
payer_data = {
|
||||
'payer_first_name': injected_line.payer_first_name,
|
||||
'payer_last_name': injected_line.payer_last_name,
|
||||
'payer_demat': injected_line.payer_demat,
|
||||
'payer_direct_debit': injected_line.payer_direct_debit,
|
||||
'first_name': injected_line.payer_first_name,
|
||||
'last_name': injected_line.payer_last_name,
|
||||
'demat': injected_line.payer_demat,
|
||||
'direct_debit': injected_line.payer_direct_debit,
|
||||
}
|
||||
payer_data = get_cached_payer_data(request, payer_external_id, payer_data=payer_data)
|
||||
lines.append(
|
||||
|
@ -224,10 +224,10 @@ def get_invoice_lines_for_user(
|
|||
user_first_name=user_first_name,
|
||||
user_last_name=user_last_name,
|
||||
payer_external_id=payer_external_id,
|
||||
payer_first_name=payer_data['payer_first_name'],
|
||||
payer_last_name=payer_data['payer_last_name'],
|
||||
payer_demat=payer_data['payer_demat'],
|
||||
payer_direct_debit=payer_data['payer_direct_debit'],
|
||||
payer_first_name=payer_data['first_name'],
|
||||
payer_last_name=payer_data['last_name'],
|
||||
payer_demat=payer_data['demat'],
|
||||
payer_direct_debit=payer_data['direct_debit'],
|
||||
status='success',
|
||||
pool=pool,
|
||||
from_injected_line=injected_line,
|
||||
|
|
|
@ -254,45 +254,6 @@ class Pricing(models.Model):
|
|||
def get_payer_external_id_from_nameid(self, request, original_context):
|
||||
return self.get_payer_external_id(request, original_context, key='payer_external_id_from_nameid')
|
||||
|
||||
def get_payer_data(self, request, original_context):
|
||||
result = {}
|
||||
context = RequestContext(request)
|
||||
context.push(original_context)
|
||||
bool_keys = ['payer_demat', 'payer_direct_debit']
|
||||
for key in self.get_payer_variables_keys():
|
||||
if key in ['payer_external_id', 'payer_external_id_from_nameid']:
|
||||
continue
|
||||
tplt = self.payer_variables.get(key) or ''
|
||||
if not tplt:
|
||||
if key not in bool_keys:
|
||||
raise PayerDataError(
|
||||
details={'key': key.removeprefix('payer_'), 'reason': 'empty-template'}
|
||||
)
|
||||
tplt = 'False'
|
||||
try:
|
||||
value = Template(tplt).render(context)
|
||||
if not value:
|
||||
if key not in bool_keys:
|
||||
raise PayerDataError(
|
||||
details={'key': key.removeprefix('payer_'), 'reason': 'empty-result'}
|
||||
)
|
||||
value = False
|
||||
if key in bool_keys:
|
||||
if value in ('True', 'true', '1'):
|
||||
value = True
|
||||
elif value in ('False', 'false', '0'):
|
||||
value = False
|
||||
else:
|
||||
raise PayerDataError(
|
||||
details={'key': key.removeprefix('payer_'), 'reason': 'not-a-boolean'}
|
||||
)
|
||||
result[key] = value
|
||||
except TemplateSyntaxError:
|
||||
raise PayerDataError(details={'key': key.removeprefix('payer_'), 'reason': 'syntax-error'})
|
||||
except VariableDoesNotExist:
|
||||
raise PayerDataError(details={'key': key.removeprefix('payer_'), 'reason': 'variable-error'})
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def import_json(cls, data):
|
||||
data = data.copy()
|
||||
|
@ -478,12 +439,6 @@ class AgendaPricing(models.Model):
|
|||
context = {'nameid': nameid}
|
||||
return self.pricing.get_payer_external_id_from_nameid(request, context)
|
||||
|
||||
def get_payer_data(self, request, payer_external_id):
|
||||
context = {'payer_external_id': payer_external_id}
|
||||
if ':' in payer_external_id:
|
||||
context['payer_external_raw_id'] = payer_external_id.split(':')[1]
|
||||
return self.pricing.get_payer_data(request, context)
|
||||
|
||||
def get_pricing_data(self, request, pricing_date, user_external_id, payer_external_id):
|
||||
# compute pricing for flat_fee_schedule mode
|
||||
data = {
|
||||
|
|
|
@ -1,60 +1,14 @@
|
|||
import copy
|
||||
import json
|
||||
import re
|
||||
import urllib.parse
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
from lingo.invoicing.models import Payer, Regie
|
||||
from tests.invoicing.utils import mocked_requests_send
|
||||
from tests.utils import login
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
WCS_CARDDEFS_DATA = [
|
||||
{'title': 'Card Model 1', 'slug': 'card_model_1', 'custom_views': [{'id': 'foo', 'text': 'bar'}]},
|
||||
{'title': 'Card Model 2', 'slug': 'card_model_2'},
|
||||
{'title': 'Card Model 3', 'slug': 'card_model_3'},
|
||||
]
|
||||
|
||||
WCS_CARDDEF_SCHEMAS = {
|
||||
'card_model_1': {
|
||||
'name': 'Card Model 1',
|
||||
'fields': [
|
||||
{'label': 'Field A', 'varname': 'fielda', 'type': 'string'},
|
||||
{'label': 'Field B', 'varname': 'fieldb', 'type': 'string'},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class MockedRequestResponse(mock.Mock):
|
||||
status_code = 200
|
||||
|
||||
def json(self):
|
||||
return json.loads(self.content)
|
||||
|
||||
|
||||
def get_data_from_url(url):
|
||||
if '/api/cards/@list' in url:
|
||||
return WCS_CARDDEFS_DATA
|
||||
m_schema = re.match(r'/api/cards/([a-z0-9_]+)/@schema', url)
|
||||
if m_schema:
|
||||
return WCS_CARDDEF_SCHEMAS.get(m_schema.group(1)) or {}
|
||||
return []
|
||||
|
||||
|
||||
def mocked_requests_send(request, **kwargs):
|
||||
request_url = urllib.parse.urlparse(request.url)
|
||||
data = copy.deepcopy(get_data_from_url(request_url.path))
|
||||
|
||||
if not isinstance(data, list):
|
||||
return MockedRequestResponse(content=json.dumps(data))
|
||||
|
||||
return MockedRequestResponse(content=json.dumps({'data': data}))
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
def test_add_payer(mock_send, app, admin_user):
|
||||
app = login(app)
|
||||
|
|
|
@ -526,10 +526,10 @@ def test_get_invoice_lines_for_user_check_status(
|
|||
pool=pool,
|
||||
payer_data_cache={
|
||||
'payer:1': {
|
||||
'payer_first_name': 'First1',
|
||||
'payer_last_name': 'Last1',
|
||||
'payer_demat': True,
|
||||
'payer_direct_debit': False,
|
||||
'first_name': 'First1',
|
||||
'last_name': 'Last1',
|
||||
'demat': True,
|
||||
'direct_debit': False,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -844,7 +844,7 @@ def test_get_invoice_lines_for_user_get_payer_id_error(mock_payer, mock_status):
|
|||
|
||||
@mock.patch('lingo.invoicing.utils.get_check_status')
|
||||
@mock.patch('lingo.pricing.models.AgendaPricing.get_payer_external_id')
|
||||
@mock.patch('lingo.pricing.models.AgendaPricing.get_payer_data')
|
||||
@mock.patch('lingo.invoicing.models.Regie.get_payer_data')
|
||||
def test_get_invoice_lines_for_user_get_payer_data_error(mock_payer_data, mock_payer, mock_status):
|
||||
regie = Regie.objects.create(label='Regie')
|
||||
agenda = Agenda.objects.create(label='Agenda')
|
||||
|
@ -971,21 +971,21 @@ def test_get_invoice_lines_for_user_get_payer_id_and_data(mock_status):
|
|||
def get_payer_data(ap, r, payer_external_id):
|
||||
return {
|
||||
'payer:1': {
|
||||
'payer_first_name': 'First1',
|
||||
'payer_last_name': 'Last1',
|
||||
'payer_demat': True,
|
||||
'payer_direct_debit': False,
|
||||
'first_name': 'First1',
|
||||
'last_name': 'Last1',
|
||||
'demat': True,
|
||||
'direct_debit': False,
|
||||
},
|
||||
'payer:2': {
|
||||
'payer_first_name': 'First2',
|
||||
'payer_last_name': 'Last2',
|
||||
'payer_demat': False,
|
||||
'payer_direct_debit': True,
|
||||
'first_name': 'First2',
|
||||
'last_name': 'Last2',
|
||||
'demat': False,
|
||||
'direct_debit': True,
|
||||
},
|
||||
}.get(payer_external_id)
|
||||
|
||||
payer_patch = mock.patch.object(AgendaPricing, 'get_payer_external_id', autospec=True)
|
||||
payer_data_patch = mock.patch.object(AgendaPricing, 'get_payer_data', autospec=True)
|
||||
payer_data_patch = mock.patch.object(Regie, 'get_payer_data', autospec=True)
|
||||
with payer_patch as mock_payer, payer_data_patch as mock_payer_data:
|
||||
mock_payer.side_effect = lambda *args: get_payer(*args)
|
||||
mock_payer_data.side_effect = lambda *args: get_payer_data(*args)
|
||||
|
@ -1033,16 +1033,16 @@ def test_get_invoice_lines_for_user_get_payer_id_and_data(mock_status):
|
|||
# cache is populated
|
||||
assert payer_data_cache == {
|
||||
'payer:1': {
|
||||
'payer_first_name': 'First1',
|
||||
'payer_last_name': 'Last1',
|
||||
'payer_demat': True,
|
||||
'payer_direct_debit': False,
|
||||
'first_name': 'First1',
|
||||
'last_name': 'Last1',
|
||||
'demat': True,
|
||||
'direct_debit': False,
|
||||
},
|
||||
'payer:2': {
|
||||
'payer_first_name': 'First2',
|
||||
'payer_last_name': 'Last2',
|
||||
'payer_demat': False,
|
||||
'payer_direct_debit': True,
|
||||
'first_name': 'First2',
|
||||
'last_name': 'Last2',
|
||||
'demat': False,
|
||||
'direct_debit': True,
|
||||
},
|
||||
}
|
||||
assert mock_payer.call_args_list == [
|
||||
|
@ -1051,8 +1051,8 @@ def test_get_invoice_lines_for_user_get_payer_id_and_data(mock_status):
|
|||
mock.call(agenda_pricing, mock.ANY, 'user:1'),
|
||||
]
|
||||
assert mock_payer_data.call_args_list == [
|
||||
mock.call(agenda_pricing, mock.ANY, 'payer:1'),
|
||||
mock.call(agenda_pricing, mock.ANY, 'payer:2'),
|
||||
mock.call(regie, mock.ANY, 'payer:1'),
|
||||
mock.call(regie, mock.ANY, 'payer:2'),
|
||||
# only 2 calls, payer:1 is cached after first call
|
||||
]
|
||||
|
||||
|
@ -1113,20 +1113,20 @@ def test_get_invoice_lines_for_user_get_payer_id_and_data(mock_status):
|
|||
# cache is populated
|
||||
assert payer_data_cache == {
|
||||
'payer:1': {
|
||||
'payer_first_name': 'First1',
|
||||
'payer_last_name': 'Last1',
|
||||
'payer_demat': True,
|
||||
'payer_direct_debit': False,
|
||||
'first_name': 'First1',
|
||||
'last_name': 'Last1',
|
||||
'demat': True,
|
||||
'direct_debit': False,
|
||||
},
|
||||
}
|
||||
|
||||
# but take in cache if present
|
||||
payer_data_cache = {
|
||||
'payer:1': {
|
||||
'payer_first_name': 'First1 IN CACHE',
|
||||
'payer_last_name': 'Last1 IN CACHE',
|
||||
'payer_demat': False,
|
||||
'payer_direct_debit': True,
|
||||
'first_name': 'First1 IN CACHE',
|
||||
'last_name': 'Last1 IN CACHE',
|
||||
'demat': False,
|
||||
'direct_debit': True,
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -1218,10 +1218,10 @@ def test_get_invoice_lines_for_user_check_status_agenda_pricing_dates(mock_payer
|
|||
pool=pool,
|
||||
payer_data_cache={
|
||||
'payer:1': {
|
||||
'payer_first_name': 'First1',
|
||||
'payer_last_name': 'Last1',
|
||||
'payer_demat': True,
|
||||
'payer_direct_debit': False,
|
||||
'first_name': 'First1',
|
||||
'last_name': 'Last1',
|
||||
'demat': True,
|
||||
'direct_debit': False,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -1253,10 +1253,10 @@ def test_get_invoice_lines_for_user_check_status_agenda_pricing_dates(mock_payer
|
|||
pool=pool,
|
||||
payer_data_cache={
|
||||
'payer:1': {
|
||||
'payer_first_name': 'First1',
|
||||
'payer_last_name': 'Last1',
|
||||
'payer_demat': True,
|
||||
'payer_direct_debit': False,
|
||||
'first_name': 'First1',
|
||||
'last_name': 'Last1',
|
||||
'demat': True,
|
||||
'direct_debit': False,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -1287,10 +1287,10 @@ def test_get_invoice_lines_for_user_check_status_agenda_pricing_dates(mock_payer
|
|||
pool=pool,
|
||||
payer_data_cache={
|
||||
'payer:1': {
|
||||
'payer_first_name': 'First1',
|
||||
'payer_last_name': 'Last1',
|
||||
'payer_demat': True,
|
||||
'payer_direct_debit': False,
|
||||
'first_name': 'First1',
|
||||
'last_name': 'Last1',
|
||||
'demat': True,
|
||||
'direct_debit': False,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -1399,10 +1399,10 @@ def test_get_invoice_lines_for_user_check_status_pricing_error(
|
|||
pool=pool,
|
||||
payer_data_cache={
|
||||
'payer:1': {
|
||||
'payer_first_name': 'First1',
|
||||
'payer_last_name': 'Last1',
|
||||
'payer_demat': True,
|
||||
'payer_direct_debit': False,
|
||||
'first_name': 'First1',
|
||||
'last_name': 'Last1',
|
||||
'demat': True,
|
||||
'direct_debit': False,
|
||||
}
|
||||
},
|
||||
)
|
||||
|
@ -1570,7 +1570,7 @@ def test_get_all_invoice_lines(mock_user_lines):
|
|||
|
||||
@mock.patch('lingo.invoicing.utils.get_check_status')
|
||||
@mock.patch('lingo.pricing.models.AgendaPricing.get_payer_external_id')
|
||||
@mock.patch('lingo.pricing.models.AgendaPricing.get_payer_data')
|
||||
@mock.patch('lingo.invoicing.models.Regie.get_payer_data')
|
||||
def test_get_all_invoice_lines_queryset(mock_payer_data, mock_payer, mock_status):
|
||||
# don't mock get_pricing_data_for_event to check all querysets
|
||||
category1 = CriteriaCategory.objects.create(label='Foo1', slug='foo1')
|
||||
|
@ -1675,10 +1675,10 @@ def test_get_all_invoice_lines_queryset(mock_payer_data, mock_payer, mock_status
|
|||
]
|
||||
mock_payer.return_value = 'payer:1'
|
||||
mock_payer_data.return_value = {
|
||||
'payer_first_name': 'First1',
|
||||
'payer_last_name': 'Last1',
|
||||
'payer_demat': True,
|
||||
'payer_direct_debit': False,
|
||||
'first_name': 'First1',
|
||||
'last_name': 'Last1',
|
||||
'demat': True,
|
||||
'direct_debit': False,
|
||||
}
|
||||
|
||||
with CaptureQueriesContext(connection) as ctx:
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import datetime
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
from django.db import transaction
|
||||
from django.db.utils import InternalError
|
||||
from django.template import Context
|
||||
from django.test.client import RequestFactory
|
||||
from publik_django_templatetags.wcs.context_processors import Cards
|
||||
|
||||
from lingo.invoicing.models import (
|
||||
Campaign,
|
||||
|
@ -12,14 +16,28 @@ from lingo.invoicing.models import (
|
|||
Invoice,
|
||||
InvoiceLine,
|
||||
InvoicePayment,
|
||||
Payer,
|
||||
PayerDataError,
|
||||
PayerError,
|
||||
Payment,
|
||||
Pool,
|
||||
Regie,
|
||||
)
|
||||
from tests.invoicing.utils import mocked_requests_send
|
||||
|
||||
pytestmark = pytest.mark.django_db
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def context():
|
||||
return Context(
|
||||
{
|
||||
'cards': Cards(),
|
||||
'request': RequestFactory().get('/'),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('draft', [True, False])
|
||||
@pytest.mark.parametrize('orphan', [True, False])
|
||||
def test_invoice_total_amount(draft, orphan):
|
||||
|
@ -416,3 +434,67 @@ def test_regie_format_number():
|
|||
regie.save()
|
||||
assert regie.format_number(datetime.date(2023, 2, 15), 42) == 'Ffoobar-2023-00000042'
|
||||
assert regie.format_number(datetime.date(2024, 12, 15), 42000000) == 'Ffoobar-2024-42000000'
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
def test_get_payer_data(mock_send, context, nocache):
|
||||
payer = Payer.objects.create(label='Payer')
|
||||
regie = Regie.objects.create(label='Regie')
|
||||
|
||||
with pytest.raises(PayerError) as e:
|
||||
regie.get_payer_data(request=context['request'], payer_external_id='payer:42')
|
||||
assert e.value.details == {'reason': 'missing-payer'}
|
||||
|
||||
regie.payer = payer
|
||||
regie.save()
|
||||
|
||||
with pytest.raises(PayerError) as e:
|
||||
regie.get_payer_data(request=context['request'], payer_external_id='payer:42')
|
||||
assert e.value.details == {'reason': 'missing-card-model'}
|
||||
|
||||
payer.carddef_reference = 'default:card_model_1'
|
||||
payer.save()
|
||||
|
||||
original_variables = {
|
||||
'first_name': 'fielda',
|
||||
'last_name': 'fielda',
|
||||
'demat': 'fieldb',
|
||||
'direct_debit': 'fieldb',
|
||||
}
|
||||
payer_data = {
|
||||
'first_name': 'foo',
|
||||
'last_name': 'foo',
|
||||
'demat': True,
|
||||
'direct_debit': True,
|
||||
}
|
||||
payer.user_fields_mapping = original_variables.copy()
|
||||
payer.save()
|
||||
assert regie.get_payer_data(request=context['request'], payer_external_id='payer:42') == payer_data
|
||||
assert '/api/cards/card_model_1/list?' in mock_send.call_args_list[-1][0][0].url
|
||||
assert (
|
||||
'&filter-internal-id=42&filter-internal-id-operator=eq&include-fields=on'
|
||||
in mock_send.call_args_list[-1][0][0].url
|
||||
)
|
||||
|
||||
for key in ['first_name', 'last_name']:
|
||||
payer.user_fields_mapping = original_variables.copy()
|
||||
payer.user_fields_mapping[key] = ''
|
||||
payer.save()
|
||||
with pytest.raises(PayerDataError) as e:
|
||||
regie.get_payer_data(request=context['request'], payer_external_id='payer:42')
|
||||
assert e.value.details == {'key': key, 'reason': 'not-defined'}
|
||||
|
||||
for key in ['demat', 'direct_debit']:
|
||||
payer.user_fields_mapping = original_variables.copy()
|
||||
payer.user_fields_mapping[key] = ''
|
||||
payer.save()
|
||||
data_result = payer_data.copy()
|
||||
data_result[key] = False
|
||||
assert regie.get_payer_data(request=context['request'], payer_external_id='payer:42') == data_result
|
||||
|
||||
payer.user_fields_mapping = original_variables.copy()
|
||||
payer.user_fields_mapping[key] = 'fielda'
|
||||
payer.save()
|
||||
with pytest.raises(PayerDataError) as e:
|
||||
regie.get_payer_data(request=context['request'], payer_external_id='payer:42')
|
||||
assert e.value.details == {'key': key, 'reason': 'not-a-boolean'}
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
import copy
|
||||
import json
|
||||
import re
|
||||
import urllib.parse
|
||||
from unittest import mock
|
||||
|
||||
WCS_CARDDEFS_DATA = [
|
||||
{'title': 'Card Model 1', 'slug': 'card_model_1', 'custom_views': [{'id': 'foo', 'text': 'bar'}]},
|
||||
{'title': 'Card Model 2', 'slug': 'card_model_2'},
|
||||
{'title': 'Card Model 3', 'slug': 'card_model_3'},
|
||||
]
|
||||
|
||||
WCS_CARDDEF_SCHEMAS = {
|
||||
'card_model_1': {
|
||||
'name': 'Card Model 1',
|
||||
'fields': [
|
||||
{'label': 'Field A', 'varname': 'fielda', 'type': 'string'},
|
||||
{'label': 'Field B', 'varname': 'fieldb', 'type': 'bool'},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
WCS_CARDS_DATA = {
|
||||
'card_model_1': [
|
||||
{
|
||||
'id': 42,
|
||||
'display_id': '10-42',
|
||||
'display_name': 'Card Model 1 - n°10-42',
|
||||
'digest': 'a a a',
|
||||
'text': 'aa',
|
||||
'fields': {
|
||||
'fielda': 'foo',
|
||||
'fieldb': True,
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
class MockedRequestResponse(mock.Mock):
|
||||
status_code = 200
|
||||
|
||||
def json(self):
|
||||
return json.loads(self.content)
|
||||
|
||||
|
||||
def get_data_from_url(url):
|
||||
if '/api/cards/@list' in url:
|
||||
return WCS_CARDDEFS_DATA
|
||||
m_schema = re.match(r'/api/cards/([a-z0-9_]+)/@schema', url)
|
||||
if m_schema:
|
||||
return WCS_CARDDEF_SCHEMAS.get(m_schema.group(1)) or {}
|
||||
m_list = re.match(r'/api/cards/([a-z0-9_]+)/list', url)
|
||||
if m_list:
|
||||
return WCS_CARDS_DATA.get(m_list.group(1)) or []
|
||||
return []
|
||||
|
||||
|
||||
def mocked_requests_send(request, **kwargs):
|
||||
request_url = urllib.parse.urlparse(request.url)
|
||||
data = copy.deepcopy(get_data_from_url(request_url.path))
|
||||
|
||||
if not isinstance(data, list):
|
||||
return MockedRequestResponse(content=json.dumps(data))
|
||||
|
||||
return MockedRequestResponse(content=json.dumps({'data': data}))
|
|
@ -1792,114 +1792,3 @@ def test_get_payer_external_id_from_nameid(mock_send, context, nocache):
|
|||
mock_send.reset_mock()
|
||||
agenda_pricing.get_payer_external_id_from_nameid(request=context['request'], nameid='foobar')
|
||||
assert 'filter-user-uuid=foobar&' in mock_send.call_args_list[0][0][0].url
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
def test_get_payer_data(mock_send, context, nocache):
|
||||
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),
|
||||
)
|
||||
|
||||
original_variables = {
|
||||
'payer_first_name': 'First',
|
||||
'payer_last_name': 'Last',
|
||||
'payer_demat': 'False',
|
||||
'payer_direct_debit': 'True',
|
||||
}
|
||||
payer_data = {
|
||||
'payer_first_name': 'First',
|
||||
'payer_last_name': 'Last',
|
||||
'payer_demat': False,
|
||||
'payer_direct_debit': True,
|
||||
}
|
||||
|
||||
for key in ['payer_first_name', 'payer_last_name']:
|
||||
values = [
|
||||
('bar', 'bar'),
|
||||
('{{ 40|add:2 }}', '42'),
|
||||
('{{ cards|objects:"foo"|first|get:"id" }}', '1'),
|
||||
]
|
||||
for value, result in values:
|
||||
pricing.payer_variables = original_variables.copy()
|
||||
pricing.payer_variables[key] = value
|
||||
pricing.save()
|
||||
data_result = payer_data.copy()
|
||||
data_result[key] = result
|
||||
assert (
|
||||
agenda_pricing.get_payer_data(request=context['request'], payer_external_id='payer:42')
|
||||
== data_result
|
||||
)
|
||||
|
||||
values = [
|
||||
('', 'empty-template'),
|
||||
('{{ "" }}', 'empty-result'),
|
||||
('{% for %}', 'syntax-error'),
|
||||
('{{ "foo"|add:user.email }}', 'variable-error'),
|
||||
]
|
||||
for value, error in values:
|
||||
pricing.payer_variables = original_variables.copy()
|
||||
pricing.payer_variables[key] = value
|
||||
pricing.save()
|
||||
with pytest.raises(PayerDataError) as e:
|
||||
agenda_pricing.get_payer_data(request=context['request'], payer_external_id='payer:42')
|
||||
assert e.value.details == {'key': key.removeprefix('payer_'), 'reason': error}
|
||||
|
||||
for key in ['payer_demat', 'payer_direct_debit']:
|
||||
values = [
|
||||
('', False),
|
||||
('True', True),
|
||||
('true', True),
|
||||
('1', True),
|
||||
('False', False),
|
||||
('false', False),
|
||||
('0', False),
|
||||
('{{ cards|objects:"foo"|first|get:"fields"|get:"bar" }}', False),
|
||||
('{{ cards|objects:"foo"|last|get:"fields"|get:"bar" }}', True),
|
||||
]
|
||||
for value, result in values:
|
||||
pricing.payer_variables = original_variables.copy()
|
||||
pricing.payer_variables[key] = value
|
||||
pricing.save()
|
||||
data_result = payer_data.copy()
|
||||
data_result[key] = result
|
||||
assert (
|
||||
agenda_pricing.get_payer_data(request=context['request'], payer_external_id='payer:42')
|
||||
== data_result
|
||||
)
|
||||
|
||||
values = [
|
||||
('{% for %}', 'syntax-error'),
|
||||
('{{ "foo"|add:user.email }}', 'variable-error'),
|
||||
('{{ cards|objects:"foo"|last|get:"fields"|get:"foo" }}', 'not-a-boolean'),
|
||||
]
|
||||
for value, error in values:
|
||||
pricing.payer_variables = original_variables.copy()
|
||||
pricing.payer_variables[key] = value
|
||||
pricing.save()
|
||||
with pytest.raises(PayerDataError) as e:
|
||||
agenda_pricing.get_payer_data(request=context['request'], payer_external_id='payer:42')
|
||||
assert e.value.details == {'key': key.removeprefix('payer_'), 'reason': error}
|
||||
|
||||
# payer_external_id can be used in variables
|
||||
pricing.payer_variables = original_variables.copy()
|
||||
pricing.payer_variables.update(
|
||||
{
|
||||
'payer_first_name': '{{ cards|objects:"qf"|filter_by:"foo"|filter_value:payer_external_id|first|get:"id" }}',
|
||||
}
|
||||
)
|
||||
pricing.save()
|
||||
mock_send.reset_mock()
|
||||
agenda_pricing.get_payer_data(request=context['request'], payer_external_id='child:42')
|
||||
assert 'filter-foo=child%3A42&' in mock_send.call_args_list[0][0][0].url
|
||||
pricing.payer_variables.update(
|
||||
{
|
||||
'payer_first_name': '{{ cards|objects:"qf"|filter_by:"foo"|filter_value:payer_external_raw_id|first|get:"id" }}',
|
||||
}
|
||||
)
|
||||
pricing.save()
|
||||
mock_send.reset_mock()
|
||||
agenda_pricing.get_payer_data(request=context['request'], payer_external_id='child:42')
|
||||
assert 'filter-foo=42&' in mock_send.call_args_list[0][0][0].url
|
||||
|
|
Loading…
Reference in New Issue