Facturation - trouver le payeur (sans garde alternée) (#74498) #31
|
@ -43,7 +43,7 @@ class PricingComputeSerializer(serializers.Serializer):
|
|||
agenda_pricing = serializers.SlugField(required=False, allow_blank=False, max_length=160)
|
||||
start_date = serializers.DateTimeField(required=False, input_formats=['iso-8601', '%Y-%m-%d'])
|
||||
user_external_id = serializers.CharField(required=True, max_length=250)
|
||||
adult_external_id = serializers.CharField(required=True, max_length=250)
|
||||
payer_external_id = serializers.CharField(required=True, max_length=250)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
@ -192,7 +192,7 @@ class PricingComputeSerializer(serializers.Serializer):
|
|||
'check_type': None,
|
||||
},
|
||||
user_external_id=self.validated_data['user_external_id'],
|
||||
adult_external_id=self.validated_data['adult_external_id'],
|
||||
payer_external_id=self.validated_data['payer_external_id'],
|
||||
)
|
||||
result.append(
|
||||
{
|
||||
|
@ -227,7 +227,7 @@ class PricingComputeSerializer(serializers.Serializer):
|
|||
self._billing_date.date_start if self._billing_date else self._agenda_pricing.date_start
|
||||
),
|
||||
user_external_id=self.validated_data['user_external_id'],
|
||||
adult_external_id=self.validated_data['adult_external_id'],
|
||||
payer_external_id=self.validated_data['payer_external_id'],
|
||||
)
|
||||
result['pricing_data'] = pricing_data
|
||||
return result
|
||||
|
|
|
@ -60,7 +60,6 @@ class AbstractInvoiceFilterSet(django_filters.FilterSet):
|
|||
)
|
||||
payer_external_id = django_filters.CharFilter(
|
||||
label=_('Payer (external ID)'),
|
||||
field_name='payer',
|
||||
)
|
||||
user_external_id = django_filters.CharFilter(
|
||||
label=_('User (external ID)'),
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('invoicing', '0017_campaign_regie'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='draftinvoiceline',
|
||||
old_name='user_name',
|
||||
new_name='user_last_name',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='invoiceline',
|
||||
old_name='user_name',
|
||||
new_name='user_last_name',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='draftinvoiceline',
|
||||
name='payer_demat',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='draftinvoiceline',
|
||||
name='payer_direct_debit',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='draftinvoiceline',
|
||||
name='payer_first_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='draftinvoiceline',
|
||||
name='payer_last_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='draftinvoiceline',
|
||||
name='user_first_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='injectedline',
|
||||
name='payer_demat',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='injectedline',
|
||||
name='payer_direct_debit',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='injectedline',
|
||||
name='payer_first_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='injectedline',
|
||||
name='payer_last_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoiceline',
|
||||
name='payer_demat',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoiceline',
|
||||
name='payer_direct_debit',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoiceline',
|
||||
name='payer_first_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoiceline',
|
||||
name='payer_last_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoiceline',
|
||||
name='user_first_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -0,0 +1,75 @@
|
|||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('invoicing', '0018_payer'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='draftinvoice',
|
||||
old_name='payer',
|
||||
new_name='payer_external_id',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='invoice',
|
||||
old_name='payer',
|
||||
new_name='payer_external_id',
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='draftinvoice',
|
||||
name='payer_external_id',
|
||||
field=models.CharField(max_length=250),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='invoice',
|
||||
name='payer_external_id',
|
||||
field=models.CharField(max_length=250),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='draftinvoice',
|
||||
name='payer_demat',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='draftinvoice',
|
||||
name='payer_direct_debit',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='draftinvoice',
|
||||
name='payer_first_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='draftinvoice',
|
||||
name='payer_last_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoice',
|
||||
name='payer_demat',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoice',
|
||||
name='payer_direct_debit',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoice',
|
||||
name='payer_first_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='invoice',
|
||||
name='payer_last_name',
|
||||
field=models.CharField(default='', max_length=250),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
|
@ -291,7 +291,11 @@ class AbstractInvoice(models.Model):
|
|||
total_amount = models.DecimalField(max_digits=9, decimal_places=2, default=0)
|
||||
date_issue = models.DateField(_('Issue date'))
|
||||
regie = models.ForeignKey(Regie, on_delete=models.PROTECT)
|
||||
payer = models.CharField(_('Payer'), max_length=300)
|
||||
payer_external_id = models.CharField(max_length=250)
|
||||
payer_first_name = models.CharField(max_length=250)
|
||||
payer_last_name = models.CharField(max_length=250)
|
||||
payer_demat = models.BooleanField(default=False)
|
||||
payer_direct_debit = models.BooleanField(default=False)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
pool = models.ForeignKey(Pool, on_delete=models.PROTECT)
|
||||
|
@ -344,6 +348,10 @@ class InjectedLine(models.Model):
|
|||
|
||||
user_external_id = models.CharField(max_length=250)
|
||||
payer_external_id = models.CharField(max_length=250)
|
||||
payer_first_name = models.CharField(max_length=250)
|
||||
payer_last_name = models.CharField(max_length=250)
|
||||
payer_demat = models.BooleanField(default=False)
|
||||
payer_direct_debit = models.BooleanField(default=False)
|
||||
regie = models.ForeignKey(Regie, on_delete=models.PROTECT)
|
||||
|
||||
|
||||
|
@ -356,8 +364,13 @@ class AbstractInvoiceLine(models.Model):
|
|||
total_amount = models.DecimalField(max_digits=9, decimal_places=2)
|
||||
|
||||
user_external_id = models.CharField(max_length=250)
|
||||
user_name = models.CharField(max_length=250)
|
||||
user_first_name = models.CharField(max_length=250)
|
||||
user_last_name = models.CharField(max_length=250)
|
||||
payer_external_id = models.CharField(max_length=250)
|
||||
payer_first_name = models.CharField(max_length=250)
|
||||
payer_last_name = models.CharField(max_length=250)
|
||||
payer_demat = models.BooleanField(default=False)
|
||||
payer_direct_debit = models.BooleanField(default=False)
|
||||
event = JSONField(default=dict)
|
||||
pricing_data = JSONField(default=dict, encoder=DjangoJSONEncoder)
|
||||
status = models.CharField(
|
||||
|
@ -374,6 +387,11 @@ class AbstractInvoiceLine(models.Model):
|
|||
class Meta:
|
||||
abstract = True
|
||||
|
||||
@property
|
||||
def user_name(self):
|
||||
user_name = '%s %s' % (self.user_first_name, self.user_last_name)
|
||||
return user_name.strip()
|
||||
|
||||
def get_error_display(self):
|
||||
if self.status == 'success':
|
||||
return
|
||||
|
@ -391,12 +409,14 @@ class AbstractInvoiceLine(models.Model):
|
|||
'PricingEventNotCheckedError': _('Event is not checked'),
|
||||
'PricingBookingNotCheckedError': _('Booking is not checked'),
|
||||
'PricingMultipleBookingError': _('Multiple booking found'),
|
||||
'PricingBookingCheckTypeError': _('Check type error: %(checktype_error)s'),
|
||||
'PricingBookingCheckTypeError': _('Check type error: %(reason)s'),
|
||||
'PayerError': _('Impossible to determine payer: %(reason)s'),
|
||||
'PayerDataError': _('Impossible to get payer %(key)s: %(reason)s'),
|
||||
}
|
||||
formats = {
|
||||
'decimal': _('decimal'),
|
||||
}
|
||||
checktype_error = None
|
||||
reason = None
|
||||
if error_details.get('reason'):
|
||||
reasons = {
|
||||
'not-found': _('not found'),
|
||||
|
@ -404,8 +424,13 @@ class AbstractInvoiceLine(models.Model):
|
|||
'not-configured': _(
|
||||
'pricing not configured (group: %(check_type_group)s, check type: %(check_type)s)'
|
||||
),
|
||||
'empty-template': _('Template is empty'),
|
||||
'empty-result': _('Result is empty'),
|
||||
'syntax-error': _('Syntax error'),
|
||||
'variable-error': _('Variable error'),
|
||||
'not-a-boolean': _('Result is not a boolean'),
|
||||
}
|
||||
checktype_error = reasons.get(error_details['reason']) % {
|
||||
reason = reasons.get(error_details['reason']) % {
|
||||
'check_type': error_details.get('check_type'),
|
||||
'check_type_group': error_details.get('check_type_group'),
|
||||
}
|
||||
|
@ -421,7 +446,8 @@ class AbstractInvoiceLine(models.Model):
|
|||
'pricing': error_details.get('pricing'),
|
||||
'wanted': formats.get(error_details.get('wanted')),
|
||||
'status': error_details.get('status'),
|
||||
'checktype_error': checktype_error,
|
||||
'reason': reason,
|
||||
'key': error_details.get('key'),
|
||||
}
|
||||
or error_class
|
||||
)
|
||||
|
|
|
@ -51,9 +51,9 @@
|
|||
{% for invoice in object_list %}
|
||||
<li class="invoice untoggled" data-invoice-id="{{ invoice.pk }}" data-invoice-lines-url="{% url 'lingo-manager-invoicing-invoice-line-list' regie_pk=regie.pk pk=object.pk pool_pk=pool.pk invoice_pk=invoice.pk %}">
|
||||
{% if pool.draft %}
|
||||
{% blocktrans with number=invoice.pk payer=invoice.payer amount=invoice.total_amount %}Invoice <a href="{{ journal_url }}?invoice_id={{ number }}">PROFORMA-{{ number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer }}">{{ payer }}</a>, amount {{ amount }}€{% endblocktrans %}
|
||||
{% blocktrans with number=invoice.pk payer=invoice.payer_external_id amount=invoice.total_amount %}Invoice <a href="{{ journal_url }}?invoice_id={{ number }}">PROFORMA-{{ number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer }}">{{ payer }}</a>, amount {{ amount }}€{% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans with invoice_number=invoice.formatted_number payer=invoice.payer amount=invoice.total_amount number=invoice.number %}Invoice <a href="{{ journal_url }}?invoice_number={{ number }}">{{ invoice_number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer }}">{{ payer }}</a>, amount {{ amount }}€{% endblocktrans %}
|
||||
{% blocktrans with invoice_number=invoice.formatted_number payer=invoice.payer_external_id amount=invoice.total_amount number=invoice.number %}Invoice <a href="{{ journal_url }}?invoice_number={{ number }}">{{ invoice_number }}</a> addressed to <a href="{{ journal_url }}?payer_external_id={{ payer }}">{{ payer }}</a>, amount {{ amount }}€{% endblocktrans %}
|
||||
{% endif %}
|
||||
<span class="togglable"></span>
|
||||
</li>
|
||||
|
|
|
@ -48,12 +48,19 @@ def get_users_from_subscriptions(agendas, pool):
|
|||
user_external_id = subscription['user_external_id']
|
||||
if user_external_id in users:
|
||||
continue
|
||||
user_name = '%s %s' % (subscription['user_first_name'], subscription['user_last_name'])
|
||||
users[user_external_id] = user_name.strip()[:250] or user_external_id
|
||||
return [(user_id, user_name) for user_id, user_name in users.items()]
|
||||
users[user_external_id] = (subscription['user_first_name'], subscription['user_last_name'])
|
||||
return list(users.items())
|
||||
|
||||
|
||||
def get_invoice_lines_for_user(agendas, agendas_pricings, user_external_id, user_name, pool):
|
||||
def get_invoice_lines_for_user(
|
||||
agendas,
|
||||
agendas_pricings,
|
||||
user_external_id,
|
||||
user_first_name,
|
||||
user_last_name,
|
||||
pool,
|
||||
payer_data_cache,
|
||||
):
|
||||
def get_agenda_pricing(agendas_pricings_for_agenda, date_event):
|
||||
# same logic as AgendaPricing.get_agenda_pricing
|
||||
for agenda_pricing in agendas_pricings_for_agenda:
|
||||
|
@ -64,6 +71,17 @@ def get_invoice_lines_for_user(agendas, agendas_pricings, user_external_id, user
|
|||
return agenda_pricing
|
||||
raise AgendaPricingNotFound
|
||||
|
||||
def get_cached_payer_data(request, payer_external_id, agenda_pricing=None, payer_data=None):
|
||||
|
||||
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(
|
||||
request, payer_external_id
|
||||
)
|
||||
elif payer_data:
|
||||
payer_data_cache[payer_external_id] = payer_data
|
||||
return payer_data_cache.get(payer_external_id) or {}
|
||||
|
||||
if not agendas:
|
||||
return []
|
||||
|
||||
|
@ -92,15 +110,19 @@ def get_invoice_lines_for_user(agendas, agendas_pricings, user_external_id, user
|
|||
event_slug = '%s@%s' % (serialized_event['agenda'], serialized_event['slug'])
|
||||
|
||||
agenda = agendas_by_slug[serialized_event['agenda']]
|
||||
payer_external_id = _('unknown')
|
||||
payer_data = {}
|
||||
try:
|
||||
agenda_pricing = get_agenda_pricing(agendas_pricings_by_agendas.get(agenda.slug), event_date)
|
||||
payer_external_id = agenda_pricing.get_payer_external_id(request, user_external_id)
|
||||
payer_data = get_cached_payer_data(request, payer_external_id, agenda_pricing=agenda_pricing)
|
||||
pricing_data = agenda_pricing.get_pricing_data_for_event(
|
||||
request=request,
|
||||
agenda=agenda,
|
||||
event=serialized_event,
|
||||
check_status=check_status['check_status'],
|
||||
user_external_id=user_external_id,
|
||||
adult_external_id=user_external_id, # XXX
|
||||
payer_external_id=payer_external_id,
|
||||
)
|
||||
except PricingError as e:
|
||||
# AgendaPricingNotFound: can happen if pricing model defined only on a part of the requested period
|
||||
|
@ -117,8 +139,13 @@ def get_invoice_lines_for_user(agendas, agendas_pricings, user_external_id, user
|
|||
unit_amount=0,
|
||||
total_amount=0,
|
||||
user_external_id=user_external_id,
|
||||
user_name=user_name,
|
||||
payer_external_id=user_external_id, # XXX
|
||||
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,
|
||||
event=serialized_event,
|
||||
pricing_data=pricing_error,
|
||||
status='warning' if isinstance(e, AgendaPricingNotFound) else 'error',
|
||||
|
@ -136,8 +163,13 @@ def get_invoice_lines_for_user(agendas, agendas_pricings, user_external_id, user
|
|||
unit_amount=pricing_data['pricing'],
|
||||
total_amount=pricing_data['pricing'],
|
||||
user_external_id=user_external_id,
|
||||
user_name=user_name,
|
||||
payer_external_id=user_external_id, # XXX
|
||||
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'],
|
||||
event=serialized_event,
|
||||
pricing_data=pricing_data,
|
||||
status='success',
|
||||
|
@ -171,6 +203,14 @@ def get_invoice_lines_for_user(agendas, agendas_pricings, user_external_id, 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,
|
||||
}
|
||||
payer_data = get_cached_payer_data(request, payer_external_id, payer_data=payer_data)
|
||||
lines.append(
|
||||
DraftInvoiceLine(
|
||||
event_date=injected_line.event_date,
|
||||
|
@ -180,8 +220,13 @@ def get_invoice_lines_for_user(agendas, agendas_pricings, user_external_id, user
|
|||
unit_amount=injected_line.unit_amount,
|
||||
total_amount=injected_line.total_amount,
|
||||
user_external_id=user_external_id,
|
||||
user_name=user_name,
|
||||
payer_external_id=injected_line.user_external_id,
|
||||
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'],
|
||||
status='success',
|
||||
pool=pool,
|
||||
from_injected_line=injected_line,
|
||||
|
@ -204,14 +249,17 @@ def get_all_invoice_lines(agendas, users, pool):
|
|||
)
|
||||
|
||||
lines = []
|
||||
for user_external_id, user_name in users:
|
||||
payer_data_cache = {}
|
||||
for user_external_id, (user_first_name, user_last_name) in users:
|
||||
# generate lines for each user
|
||||
lines += get_invoice_lines_for_user(
|
||||
agendas=agendas,
|
||||
agendas_pricings=agendas_pricings,
|
||||
user_external_id=user_external_id,
|
||||
user_name=user_name,
|
||||
user_first_name=user_first_name,
|
||||
user_last_name=user_last_name,
|
||||
pool=pool,
|
||||
payer_data_cache=payer_data_cache,
|
||||
)
|
||||
return lines
|
||||
|
||||
|
@ -226,12 +274,18 @@ def generate_invoices_from_lines(all_lines, pool):
|
|||
# ignore lines in error
|
||||
continue
|
||||
if line.payer_external_id not in lines:
|
||||
lines[line.payer_external_id] = []
|
||||
lines[line.payer_external_id].append(line)
|
||||
lines[line.payer_external_id] = {
|
||||
'payer_first_name': line.payer_first_name,
|
||||
'payer_last_name': line.payer_last_name,
|
||||
'payer_demat': line.payer_demat,
|
||||
'payer_direct_debit': line.payer_direct_debit,
|
||||
'lines': [],
|
||||
}
|
||||
lines[line.payer_external_id]['lines'].append(line)
|
||||
|
||||
# generate invoices by regie and by payer_external_id (payer)
|
||||
invoices = []
|
||||
for payer_external_id, adult_lines in lines.items():
|
||||
for payer_external_id, payer_data in lines.items():
|
||||
invoice = DraftInvoice.objects.create(
|
||||
label=_('Invoice from %(start)s to %(end)s')
|
||||
% {
|
||||
|
@ -240,10 +294,16 @@ def generate_invoices_from_lines(all_lines, pool):
|
|||
},
|
||||
date_issue=pool.campaign.date_issue,
|
||||
regie=regie,
|
||||
payer=payer_external_id,
|
||||
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'],
|
||||
pool=pool,
|
||||
)
|
||||
DraftInvoiceLine.objects.filter(pk__in=[line.pk for line in adult_lines]).update(invoice=invoice)
|
||||
DraftInvoiceLine.objects.filter(pk__in=[line.pk for line in payer_data['lines']]).update(
|
||||
invoice=invoice
|
||||
)
|
||||
invoices.append(invoice)
|
||||
|
||||
return invoices
|
||||
|
|
|
@ -635,7 +635,8 @@ class NonInvoicedLineListView(ListView):
|
|||
'total_amount',
|
||||
'user_external_id',
|
||||
'payer_external_id',
|
||||
'user_name',
|
||||
'user_first_name',
|
||||
'user_last_name',
|
||||
'event',
|
||||
'pricing_data',
|
||||
'status',
|
||||
|
@ -647,7 +648,8 @@ class NonInvoicedLineListView(ListView):
|
|||
qs2 = (
|
||||
InjectedLine.objects.filter(invoiceline__isnull=True, regie=self.regie)
|
||||
.annotate(
|
||||
user_name=Value('', output_field=CharField()),
|
||||
user_first_name=Value('', output_field=CharField()),
|
||||
user_last_name=Value('', output_field=CharField()),
|
||||
event=Value({}, output_field=JSONField()),
|
||||
pricing_data=Value({}, output_field=JSONField()),
|
||||
status=Value('injected', output_field=CharField()),
|
||||
|
@ -664,6 +666,9 @@ class NonInvoicedLineListView(ListView):
|
|||
pools = Pool.objects.filter(draft=False).in_bulk()
|
||||
for line in context['object_list']:
|
||||
if line['status'] == 'error':
|
||||
line['user_name'] = InvoiceLine(
|
||||
user_first_name=line['user_first_name'], user_last_name=line['user_last_name']
|
||||
).user_name
|
||||
line['error_display'] = InvoiceLine(
|
||||
status=line['status'], pricing_data=line['pricing_data']
|
||||
).get_error_display()
|
||||
|
|
|
@ -95,6 +95,16 @@ class PricingVariableForm(forms.Form):
|
|||
PricingVariableFormSet = forms.formset_factory(PricingVariableForm)
|
||||
|
||||
|
||||
class PricingPayerForm(forms.Form):
|
||||
key = forms.CharField(required=True, widget=forms.TextInput(attrs={'readonly': True}))
|
||||
value = forms.CharField(
|
||||
label=_('Value template'), widget=forms.TextInput(attrs={'size': 60}), required=False
|
||||
)
|
||||
|
||||
|
||||
PricingPayerFormSet = forms.formset_factory(PricingPayerForm, extra=0)
|
||||
|
||||
|
||||
class PricingCriteriaCategoryAddForm(forms.Form):
|
||||
category = forms.ModelChoiceField(
|
||||
label=_('Criteria category to add'), queryset=CriteriaCategory.objects.none(), required=True
|
||||
|
@ -296,7 +306,7 @@ class PricingTestToolForm(forms.Form):
|
|||
label=_('Billing date'), empty_label=None, queryset=BillingDate.objects.none()
|
||||
)
|
||||
user_external_id = forms.CharField(label=_('User external identifier'))
|
||||
adult_external_id = forms.CharField(label=_('Adult external identifier'))
|
||||
payer_external_id = forms.CharField(label=_('Payer external identifier'))
|
||||
booking_status = forms.ChoiceField(label=_('Booking status'), choices=[])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -409,7 +419,7 @@ class PricingTestToolForm(forms.Form):
|
|||
request=self.request,
|
||||
pricing_date=pricing_date,
|
||||
user_external_id=self.cleaned_data['user_external_id'],
|
||||
adult_external_id=self.cleaned_data['adult_external_id'],
|
||||
payer_external_id=self.cleaned_data['payer_external_id'],
|
||||
)
|
||||
|
||||
def compute_for_event(self):
|
||||
|
@ -422,7 +432,7 @@ class PricingTestToolForm(forms.Form):
|
|||
'check_type': self.check_type_slug,
|
||||
},
|
||||
user_external_id=self.cleaned_data['user_external_id'],
|
||||
adult_external_id=self.cleaned_data['adult_external_id'],
|
||||
payer_external_id=self.cleaned_data['payer_external_id'],
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('pricing', '0010_flat_fee_schedule'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='pricing',
|
||||
name='payer_variables',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict),
|
||||
),
|
||||
]
|
|
@ -40,6 +40,14 @@ class AgendaPricingNotFound(PricingError):
|
|||
pass
|
||||
|
||||
|
||||
class PayerError(PricingError):
|
||||
pass
|
||||
|
||||
|
||||
class PayerDataError(PricingError):
|
||||
pass
|
||||
|
||||
|
||||
class CriteriaConditionNotFound(PricingError):
|
||||
pass
|
||||
|
||||
|
@ -191,6 +199,7 @@ class Pricing(models.Model):
|
|||
)
|
||||
criterias = models.ManyToManyField(Criteria)
|
||||
extra_variables = JSONField(blank=True, default=dict)
|
||||
payer_variables = JSONField(blank=True, default=dict)
|
||||
|
||||
class Meta:
|
||||
ordering = ['label']
|
||||
|
@ -221,6 +230,70 @@ class Pricing(models.Model):
|
|||
def get_extra_variables_keys(self):
|
||||
return sorted((self.extra_variables or {}).keys())
|
||||
|
||||
def get_payer_variables_keys(self):
|
||||
return [
|
||||
'payer_external_id',
|
||||
'payer_first_name',
|
||||
'payer_last_name',
|
||||
'payer_demat',
|
||||
'payer_direct_debit',
|
||||
]
|
||||
|
||||
def get_payer_external_id(self, request, original_context):
|
||||
context = RequestContext(request)
|
||||
context.push(original_context)
|
||||
tplt = self.payer_variables.get('payer_external_id') or ''
|
||||
if not tplt:
|
||||
raise PayerError(details={'reason': 'empty-template'})
|
||||
try:
|
||||
value = Template(tplt).render(context)
|
||||
if not value:
|
||||
raise PayerError(details={'reason': 'empty-result'})
|
||||
return value
|
||||
except TemplateSyntaxError:
|
||||
raise PayerError(details={'reason': 'syntax-error'})
|
||||
except VariableDoesNotExist:
|
||||
raise PayerError(details={'reason': 'variable-error'})
|
||||
|
||||
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 == 'payer_external_id':
|
||||
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:
|
||||
pmarillonnet
commented
Je ne comprends pas ici en quoi le fait d’avoir une clé associée à une valeur autre que booléenne fait qu’on interdit un résultat vide :/ Je ne comprends pas ici en quoi le fait d’avoir une clé associée à une valeur autre que booléenne fait qu’on interdit un résultat vide :/
lguerin
commented
Les champs payer de type bool (demat, direct_debit) ont une valeur par défaut, "False". Les champs payer de type bool (demat, direct_debit) ont une valeur par défaut, "False".
Les autres champs n'ont pas de valeur par défaut (nom, prénom, impossible de définir une valeur par défaut), donc on ne veut pas de résultat vide.
C'est un peu arbitraire, je me suis dit qu'on pouvait ne pas définir de template pour les champs bool et ça passe quand même, alors que pour les autres champs c'est plus embêtant pour la suite (édition d'une facture etc) d'avoir une valeur vide.
Peut-être que je me suis embêtée pour rien et qu'on pourrait péter une erreur pour les bools aussi.
On verra à l'usage :)
|
||||
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, overwrite=False):
|
||||
data = data.copy()
|
||||
|
@ -400,7 +473,19 @@ class AgendaPricing(models.Model):
|
|||
|
||||
return created, agenda_pricing
|
||||
|
||||
def get_pricing_data(self, request, pricing_date, user_external_id, adult_external_id):
|
||||
def get_payer_external_id(self, request, user_external_id):
|
||||
context = {'user_external_id': user_external_id}
|
||||
if ':' in user_external_id:
|
||||
context['user_external_raw_id'] = user_external_id.split(':')[1]
|
||||
return self.pricing.get_payer_external_id(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 = {
|
||||
'pricing_date': pricing_date, # date to use for QF
|
||||
|
@ -409,7 +494,7 @@ class AgendaPricing(models.Model):
|
|||
request=request,
|
||||
data=data,
|
||||
user_external_id=user_external_id,
|
||||
adult_external_id=adult_external_id,
|
||||
payer_external_id=payer_external_id,
|
||||
)
|
||||
pricing, criterias = self.compute_pricing(context=context)
|
||||
return {
|
||||
|
@ -422,7 +507,7 @@ class AgendaPricing(models.Model):
|
|||
}
|
||||
|
||||
def get_pricing_data_for_event(
|
||||
self, request, agenda, event, check_status, user_external_id, adult_external_id
|
||||
self, request, agenda, event, check_status, user_external_id, payer_external_id
|
||||
):
|
||||
# compute pricing for an event
|
||||
event_date = datetime.datetime.fromisoformat(event['start_datetime']).date()
|
||||
|
@ -434,7 +519,7 @@ class AgendaPricing(models.Model):
|
|||
request=request,
|
||||
data=data,
|
||||
user_external_id=user_external_id,
|
||||
adult_external_id=adult_external_id,
|
||||
payer_external_id=payer_external_id,
|
||||
)
|
||||
pricing, criterias = self.compute_pricing(context=context)
|
||||
modifier = self.get_booking_modifier(agenda=agenda, check_status=check_status)
|
||||
|
@ -468,12 +553,12 @@ class AgendaPricing(models.Model):
|
|||
except (AgendaPricing.DoesNotExist, AgendaPricing.MultipleObjectsReturned):
|
||||
raise AgendaPricingNotFound
|
||||
|
||||
def get_pricing_context(self, request, data, user_external_id, adult_external_id):
|
||||
context = {'data': data, 'user_external_id': user_external_id, 'adult_external_id': adult_external_id}
|
||||
def get_pricing_context(self, request, data, user_external_id, payer_external_id):
|
||||
context = {'data': data, 'user_external_id': user_external_id, 'payer_external_id': payer_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]
|
||||
if ':' in payer_external_id:
|
||||
context['payer_external_raw_id'] = payer_external_id.split(':')[1]
|
||||
return self.pricing.get_extra_variables(request, context)
|
||||
|
||||
def format_pricing_data(self):
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<div class="pk-tabs">
|
||||
<div class="pk-tabs--tab-list" role="tablist">
|
||||
<button aria-controls="panel-variables" aria-selected="true" id="tab-variables" role="tab" tabindex="0">{% trans "Variables" %}</button>
|
||||
<button aria-controls="panel-payer" aria-selected="false" id="tab-payer" role="tab" tabindex="-1">{% trans "Payer" %}</button>
|
||||
<button aria-controls="panel-criterias" aria-selected="false" id="tab-criterias" role="tab" tabindex="-1">{% trans "Criterias" %}</button>
|
||||
<button aria-controls="panel-usage" aria-selected="false" id="tab-usage" role="tab" tabindex="-1">{% trans "Used in agendas" %}</button>
|
||||
</div>
|
||||
|
@ -35,13 +36,31 @@
|
|||
<div aria-labelledby="tab-variables" id="panel-variables" role="tabpanel" tabindex="0">
|
||||
{% if object.extra_variables %}
|
||||
<label>{% trans 'Extra variables:' %}</label>
|
||||
{% for key in object.get_extra_variables_keys %}<i>{{ key }}</i>{% if not forloop.last %}, {% endif %}{% endfor %}
|
||||
<dl>
|
||||
{% for key in object.get_extra_variables_keys %}
|
||||
<dt><b>{% blocktrans %}{{ key }}:{% endblocktrans %}</b></dt>
|
||||
<dd><pre>{{ object.extra_variables|get:key }}</pre></dd>
|
||||
pmarillonnet
commented
Là juste question d’affichage, je me dis qu’avec Là juste question d’affichage, je me dis qu’avec `<pre/>` il peut y avoir des soucis d’affichage si la valeur est trop longue et dépasse la largeur allouée dans le conteneur. Je ne sais si ça peut arriver, mais j’imagine qu’on peut gérer cela par la suite, dans une autre PR, si ça survenait.
lguerin
commented
Effectivement ça dépasse facilement, en local j'ai un template minimal pour aller chercher un champ dans une fiche et ça dépasse de l'écran. Effectivement ça dépasse facilement, en local j'ai un template minimal pour aller chercher un champ dans une fiche et ça dépasse de l'écran.
Mais j'ai voulu faire un premier truc rapide et pas forcément joli, pour avancer; on pourra faire un ticket joliesse pour améliorer tout ça.
|
||||
{% endfor %}
|
||||
</dl>
|
||||
{% endif %}
|
||||
<div class="panel--buttons">
|
||||
<a class="pk-button" rel="popup" href="{% url 'lingo-manager-pricing-variable-edit' pk=object.pk %}">{% trans 'Define variables' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div aria-labelledby="tab-payer" hidden="" id="panel-payer" role="tabpanel" tabindex="0">
|
||||
<label>{% trans 'Payer variables:' %}</label>
|
||||
<dl>
|
||||
{% for key in object.get_payer_variables_keys %}
|
||||
<dt><b>{% blocktrans %}{{ key }}:{% endblocktrans %}</b></dt>
|
||||
<dd><pre>{{ object.payer_variables|get:key|default:'' }}</pre></dd>
|
||||
{% endfor %}
|
||||
</dl>
|
||||
<div class="panel--buttons">
|
||||
<a class="pk-button" rel="popup" href="{% url 'lingo-manager-pricing-payer-edit' pk=object.pk %}">{% trans 'Define payer variables' %}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div aria-labelledby="tab-criterias" hidden="" id="panel-criterias" role="tabpanel" tabindex="0">
|
||||
{% with criterias=object.criterias.all categories=object.categories.all %}
|
||||
{% if categories %}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{% extends "lingo/pricing/manager_pricing_detail.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block breadcrumb %}
|
||||
{{ block.super }}
|
||||
<a href="{% url 'lingo-manager-pricing-payer-edit' object.pk %}">{% trans "Payer variables definition" %}</a>
|
||||
pmarillonnet
commented
Petite typo ici dans la chaîne internationalisée (ariables -> variables). Petite typo ici dans la chaîne internationalisée (ariables -> variables).
lguerin
commented
corrigé corrigé
|
||||
{% endblock %}
|
||||
|
||||
{% block appbar %}
|
||||
<h2>{% trans "Payer variables definition" %}</h2>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
{{ form.management_form }}
|
||||
<table>
|
||||
<tbody>
|
||||
pmarillonnet
commented
J’avoue que là je n’ai pas essayé de checkout la branche pour voir l’affichage du formset en tableau avec un formulaire par ligne comme ça. Totale confiance en l’auteure de la PR là-dessus :) J’avoue que là je n’ai pas essayé de checkout la branche pour voir l’affichage du formset en tableau avec un formulaire par ligne comme ça. Totale confiance en l’auteure de la PR là-dessus :)
lguerin
commented
copier/coller d'un autre template déjà existant, et on doit avoir des trucs similaires dans combo et passerelle :) copier/coller d'un autre template déjà existant, et on doit avoir des trucs similaires dans combo et passerelle :)
|
||||
{% for sub_form in form %}
|
||||
<tr>
|
||||
{% for field in sub_form %}
|
||||
<td>
|
||||
{{ field.errors.as_ul }}
|
||||
{{ field }}
|
||||
{% if forloop.first %}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="buttons">
|
||||
<button class="submit-button">{% trans "Save" %}</button>
|
||||
<a class="cancel" href="{% url 'lingo-manager-pricing-detail' object.pk %}">{% trans 'Cancel' %}</a>
|
||||
</div>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -58,6 +58,11 @@ urlpatterns = [
|
|||
views.pricing_variable_edit,
|
||||
name='lingo-manager-pricing-variable-edit',
|
||||
),
|
||||
path(
|
||||
'model/<int:pk>/payer/',
|
||||
views.pricing_payer_edit,
|
||||
name='lingo-manager-pricing-payer-edit',
|
||||
),
|
||||
path(
|
||||
'model/<int:pk>/category/add/',
|
||||
views.pricing_criteria_category_add,
|
||||
|
|
|
@ -58,6 +58,7 @@ from lingo.pricing.forms import (
|
|||
PricingCriteriaCategoryEditForm,
|
||||
PricingDuplicateForm,
|
||||
PricingMatrixForm,
|
||||
PricingPayerFormSet,
|
||||
PricingTestToolForm,
|
||||
PricingVariableFormSet,
|
||||
)
|
||||
|
@ -410,6 +411,43 @@ class PricingVariableEdit(FormView):
|
|||
pricing_variable_edit = PricingVariableEdit.as_view()
|
||||
|
||||
|
||||
class PricingPayerEdit(FormView):
|
||||
template_name = 'lingo/pricing/manager_pricing_payer_form.html'
|
||||
model = Pricing
|
||||
form_class = PricingPayerFormSet
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.object = get_object_or_404(Pricing, pk=kwargs['pk'])
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['object'] = self.object
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_initial(self):
|
||||
return list(
|
||||
{'key': k, 'value': self.object.payer_variables.get(k) or ''}
|
||||
for k in self.object.get_payer_variables_keys()
|
||||
)
|
||||
|
||||
def form_valid(self, form):
|
||||
self.object.payer_variables = {k: '' for k in self.object.get_payer_variables_keys()}
|
||||
for sub_data in form.cleaned_data:
|
||||
if not sub_data.get('key'):
|
||||
continue
|
||||
if sub_data['key'] not in self.object.payer_variables:
|
||||
continue
|
||||
self.object.payer_variables[sub_data['key']] = sub_data['value']
|
||||
self.object.save()
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def get_success_url(self):
|
||||
return '%s#open:payer' % reverse('lingo-manager-pricing-detail', args=[self.object.pk])
|
||||
|
||||
|
||||
pricing_payer_edit = PricingPayerEdit.as_view()
|
||||
|
||||
|
||||
class PricingCriteriaCategoryAddView(FormView):
|
||||
template_name = 'lingo/pricing/manager_pricing_criteria_category_form.html'
|
||||
model = Pricing
|
||||
|
|
|
@ -26,7 +26,7 @@ def test_pricing_compute_params(app, user):
|
|||
# missing slots, agenda, agenda_pricing
|
||||
resp = app.get(
|
||||
'/api/pricing/compute/',
|
||||
params={'user_external_id': 'user:1', 'adult_external_id': 'adult:1'},
|
||||
params={'user_external_id': 'user:1', 'payer_external_id': 'payer:1'},
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
|
@ -38,7 +38,7 @@ def test_pricing_compute_params(app, user):
|
|||
# missing start_date
|
||||
resp = app.get(
|
||||
'/api/pricing/compute/',
|
||||
params={'agenda': 'foo', 'user_external_id': 'user:1', 'adult_external_id': 'adult:1'},
|
||||
params={'agenda': 'foo', 'user_external_id': 'user:1', 'payer_external_id': 'payer:1'},
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
|
@ -46,7 +46,7 @@ def test_pricing_compute_params(app, user):
|
|||
assert resp.json['errors']['start_date'] == ['This field is required when using "agenda" parameter.']
|
||||
resp = app.get(
|
||||
'/api/pricing/compute/',
|
||||
params={'agenda_pricing': 'foo', 'user_external_id': 'user:1', 'adult_external_id': 'adult:1'},
|
||||
params={'agenda_pricing': 'foo', 'user_external_id': 'user:1', 'payer_external_id': 'payer:1'},
|
||||
status=400,
|
||||
)
|
||||
assert resp.json['err'] == 1
|
||||
|
@ -63,7 +63,7 @@ def test_pricing_compute_params(app, user):
|
|||
for param in params:
|
||||
# missing user_external_id
|
||||
_param = param.copy()
|
||||
_param.update({'adult_external_id': 'adult:1'})
|
||||
_param.update({'payer_external_id': 'payer:1'})
|
||||
resp = app.get(
|
||||
'/api/pricing/compute/',
|
||||
params=_param,
|
||||
|
@ -73,7 +73,7 @@ def test_pricing_compute_params(app, user):
|
|||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['user_external_id'] == ['This field is required.']
|
||||
|
||||
# missing adult_external_id
|
||||
# missing payer_external_id
|
||||
_param = param.copy()
|
||||
_param.update({'user_external_id': 'user:1'})
|
||||
resp = app.get(
|
||||
|
@ -83,7 +83,7 @@ def test_pricing_compute_params(app, user):
|
|||
)
|
||||
assert resp.json['err'] == 1
|
||||
assert resp.json['err_desc'] == 'invalid payload'
|
||||
assert resp.json['errors']['adult_external_id'] == ['This field is required.']
|
||||
assert resp.json['errors']['payer_external_id'] == ['This field is required.']
|
||||
|
||||
|
||||
def test_pricing_compute_slots(app, user):
|
||||
|
@ -95,7 +95,7 @@ def test_pricing_compute_slots(app, user):
|
|||
params={
|
||||
'slots': 'event-bar-slug',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
|
@ -107,7 +107,7 @@ def test_pricing_compute_slots(app, user):
|
|||
params={
|
||||
'slots': '@event-bar-slug',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
|
@ -119,7 +119,7 @@ def test_pricing_compute_slots(app, user):
|
|||
params={
|
||||
'slots': 'agenda@',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
|
@ -133,7 +133,7 @@ def test_pricing_compute_slots(app, user):
|
|||
params={
|
||||
'slots': 'agenda@event-bar-slug, agenda2@event-bar-slug',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
|
@ -152,7 +152,7 @@ def test_pricing_compute_agenda(app, user):
|
|||
'agenda': 'agenda',
|
||||
'start_date': '2021-09-01',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
|
@ -171,7 +171,7 @@ def test_pricing_compute_agenda_pricing(app, user):
|
|||
'agenda_pricing': 'baz',
|
||||
'start_date': '2021-09-01',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
|
@ -221,7 +221,7 @@ def test_pricing_compute_events_error(mock_events, app, user):
|
|||
params={
|
||||
'slots': 'agenda@event-bar-slug',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
status=400,
|
||||
)
|
||||
|
@ -257,7 +257,7 @@ def test_pricing_compute_for_event(mock_pricing_data_event, mock_events, app, us
|
|||
params={
|
||||
'slots': 'agenda@event-bar-slug, agenda2@recurring-event-baz-slug, agenda2@recurring-event-baz-slug:1',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == [
|
||||
|
@ -284,7 +284,7 @@ def test_pricing_compute_for_event(mock_pricing_data_event, mock_events, app, us
|
|||
params={
|
||||
'slots': 'agenda@event-bar-slug, agenda2@recurring-event-baz-slug, agenda2@recurring-event-baz-slug:1',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == [
|
||||
|
@ -306,7 +306,7 @@ def test_pricing_compute_for_event(mock_pricing_data_event, mock_events, app, us
|
|||
params={
|
||||
'slots': 'agenda@event-bar-slug, agenda2@recurring-event-baz-slug, agenda2@recurring-event-baz-slug:1',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == [
|
||||
|
@ -325,7 +325,7 @@ def test_pricing_compute_for_event(mock_pricing_data_event, mock_events, app, us
|
|||
},
|
||||
check_status={'status': 'presence', 'check_type': None},
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
),
|
||||
mock.call(
|
||||
request=mock.ANY,
|
||||
|
@ -337,7 +337,7 @@ def test_pricing_compute_for_event(mock_pricing_data_event, mock_events, app, us
|
|||
},
|
||||
check_status={'status': 'presence', 'check_type': None},
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
),
|
||||
]
|
||||
# with a start_date
|
||||
|
@ -351,7 +351,7 @@ def test_pricing_compute_for_event(mock_pricing_data_event, mock_events, app, us
|
|||
params={
|
||||
'slots': 'agenda@event-bar-slug, agenda2@recurring-event-baz-slug, agenda2@recurring-event-baz-slug:1',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
'start_date': '2021-09-06',
|
||||
},
|
||||
)
|
||||
|
@ -371,7 +371,7 @@ def test_pricing_compute_for_event(mock_pricing_data_event, mock_events, app, us
|
|||
},
|
||||
check_status={'status': 'presence', 'check_type': None},
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
),
|
||||
mock.call(
|
||||
request=mock.ANY,
|
||||
|
@ -383,7 +383,7 @@ def test_pricing_compute_for_event(mock_pricing_data_event, mock_events, app, us
|
|||
},
|
||||
check_status={'status': 'presence', 'check_type': None},
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
),
|
||||
]
|
||||
|
||||
|
@ -397,7 +397,7 @@ def test_pricing_compute_for_event(mock_pricing_data_event, mock_events, app, us
|
|||
params={
|
||||
'slots': 'agenda@event-bar-slug, agenda2@recurring-event-baz-slug, agenda2@recurring-event-baz-slug:1',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == [
|
||||
|
@ -434,7 +434,7 @@ def test_pricing_compute_for_flat_fee_schedule_with_subscription(mock_pricing_da
|
|||
'agenda': 'foo-bar',
|
||||
'start_date': '2021-09-02',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == {
|
||||
|
@ -457,7 +457,7 @@ def test_pricing_compute_for_flat_fee_schedule_with_subscription(mock_pricing_da
|
|||
'agenda': 'foo-bar',
|
||||
'start_date': '2021-09-02',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == {
|
||||
|
@ -474,7 +474,7 @@ def test_pricing_compute_for_flat_fee_schedule_with_subscription(mock_pricing_da
|
|||
'agenda': 'foo-bar',
|
||||
'start_date': '2021-09-02',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == {
|
||||
|
@ -491,7 +491,7 @@ def test_pricing_compute_for_flat_fee_schedule_with_subscription(mock_pricing_da
|
|||
'agenda': 'foo-bar',
|
||||
'start_date': '2021-09-02',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == {'agenda': 'foo-bar', 'pricing_data': {'foo': 'bar'}}
|
||||
|
@ -500,7 +500,7 @@ def test_pricing_compute_for_flat_fee_schedule_with_subscription(mock_pricing_da
|
|||
request=mock.ANY,
|
||||
pricing_date=datetime.date(2021, 9, 1),
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
),
|
||||
]
|
||||
|
||||
|
@ -512,7 +512,7 @@ def test_pricing_compute_for_flat_fee_schedule_with_subscription(mock_pricing_da
|
|||
'agenda': 'foo-bar',
|
||||
'start_date': '2021-09-02',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == {
|
||||
|
@ -538,7 +538,7 @@ def test_pricing_compute_for_flat_fee_schedule_with_subscription(mock_pricing_da
|
|||
'agenda': 'foo-bar',
|
||||
'start_date': '2021-09-16',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert mock_pricing_data.call_args_list == [
|
||||
|
@ -546,7 +546,7 @@ def test_pricing_compute_for_flat_fee_schedule_with_subscription(mock_pricing_da
|
|||
request=mock.ANY,
|
||||
pricing_date=datetime.date(2021, 9, 15),
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
),
|
||||
]
|
||||
|
||||
|
@ -571,7 +571,7 @@ def test_pricing_compute_for_flat_fee_schedule_without_subscription(mock_pricing
|
|||
'agenda_pricing': 'foo-bar-pricing',
|
||||
'start_date': '2021-09-02',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == {'agenda_pricing': 'foo-bar-pricing', 'pricing_data': {'foo': 'bar'}}
|
||||
|
@ -580,7 +580,7 @@ def test_pricing_compute_for_flat_fee_schedule_without_subscription(mock_pricing
|
|||
request=mock.ANY,
|
||||
pricing_date=datetime.date(2021, 9, 1),
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
),
|
||||
]
|
||||
|
||||
|
@ -592,7 +592,7 @@ def test_pricing_compute_for_flat_fee_schedule_without_subscription(mock_pricing
|
|||
'agenda_pricing': 'foo-bar-pricing',
|
||||
'start_date': '2021-09-02',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert resp.json['data'] == {
|
||||
|
@ -618,7 +618,7 @@ def test_pricing_compute_for_flat_fee_schedule_without_subscription(mock_pricing
|
|||
'agenda_pricing': 'foo-bar-pricing',
|
||||
'start_date': '2021-09-16',
|
||||
'user_external_id': 'user:1',
|
||||
'adult_external_id': 'adult:1',
|
||||
'payer_external_id': 'payer:1',
|
||||
},
|
||||
)
|
||||
assert mock_pricing_data.call_args_list == [
|
||||
|
@ -626,6 +626,6 @@ def test_pricing_compute_for_flat_fee_schedule_without_subscription(mock_pricing
|
|||
request=mock.ANY,
|
||||
pricing_date=datetime.date(2021, 9, 15),
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
),
|
||||
]
|
||||
|
|
|
@ -627,10 +627,10 @@ def test_detail_pool_invoices(app, admin_user, draft):
|
|||
status='completed',
|
||||
)
|
||||
invoice1 = invoice_model.objects.create(
|
||||
date_issue=datetime.date.today(), regie=regie, pool=pool, payer='payer:1'
|
||||
date_issue=datetime.date.today(), regie=regie, pool=pool, payer_external_id='payer:1'
|
||||
)
|
||||
invoice2 = invoice_model.objects.create(
|
||||
date_issue=datetime.date.today(), regie=regie, pool=pool, payer='payer:2'
|
||||
date_issue=datetime.date.today(), regie=regie, pool=pool, payer_external_id='payer:2'
|
||||
)
|
||||
if not draft:
|
||||
invoice1.set_number()
|
||||
|
@ -648,7 +648,8 @@ def test_detail_pool_invoices(app, admin_user, draft):
|
|||
pool=pool,
|
||||
label='Label 11',
|
||||
user_external_id='user:1',
|
||||
user_name='User1 Name1',
|
||||
user_first_name='User1',
|
||||
user_last_name='Name1',
|
||||
)
|
||||
line12 = line_model.objects.create(
|
||||
event_date=datetime.date(2022, 9, 2),
|
||||
|
@ -660,7 +661,8 @@ def test_detail_pool_invoices(app, admin_user, draft):
|
|||
pool=pool,
|
||||
label='Label 12',
|
||||
user_external_id='user:2',
|
||||
user_name='User2 Name2',
|
||||
user_first_name='User2',
|
||||
user_last_name='Name2',
|
||||
)
|
||||
line13 = line_model.objects.create(
|
||||
event_date=datetime.date(2022, 9, 3),
|
||||
|
@ -672,7 +674,8 @@ def test_detail_pool_invoices(app, admin_user, draft):
|
|||
pool=pool,
|
||||
label='Label 13',
|
||||
user_external_id='user:1',
|
||||
user_name='User1 Name1',
|
||||
user_first_name='User1',
|
||||
user_last_name='Name1',
|
||||
)
|
||||
|
||||
orphan_line = line_model.objects.create(
|
||||
|
@ -684,7 +687,8 @@ def test_detail_pool_invoices(app, admin_user, draft):
|
|||
pool=pool,
|
||||
label='Label 14',
|
||||
user_external_id='user:1',
|
||||
user_name='User1 Name1',
|
||||
user_first_name='User1',
|
||||
user_last_name='Name1',
|
||||
)
|
||||
|
||||
line21 = line_model.objects.create(
|
||||
|
@ -697,7 +701,8 @@ def test_detail_pool_invoices(app, admin_user, draft):
|
|||
pool=pool,
|
||||
label='Label 21',
|
||||
user_external_id='user:1',
|
||||
user_name='User1 Name1',
|
||||
user_first_name='User1',
|
||||
user_last_name='Name1',
|
||||
)
|
||||
|
||||
app = login(app)
|
||||
|
@ -983,10 +988,10 @@ def test_journal_pool_lines(app, admin_user, draft):
|
|||
status='completed',
|
||||
)
|
||||
invoice1 = invoice_model.objects.create(
|
||||
date_issue=datetime.date.today(), regie=regie, pool=pool, payer='payer:1'
|
||||
date_issue=datetime.date.today(), regie=regie, pool=pool, payer_external_id='payer:1'
|
||||
)
|
||||
invoice2 = invoice_model.objects.create(
|
||||
date_issue=datetime.date.today(), regie=regie, pool=pool, payer='payer:2'
|
||||
date_issue=datetime.date.today(), regie=regie, pool=pool, payer_external_id='payer:2'
|
||||
)
|
||||
if not draft:
|
||||
invoice1.set_number()
|
||||
|
@ -1032,6 +1037,15 @@ def test_journal_pool_lines(app, admin_user, draft):
|
|||
'PricingBookingCheckTypeError',
|
||||
{'check_type_group': 'foo-bar', 'check_type': 'foo-reason', 'reason': 'wrong-kind'},
|
||||
),
|
||||
('PayerError', {'reason': 'empty-template'}),
|
||||
('PayerError', {'reason': 'empty-result'}),
|
||||
('PayerError', {'reason': 'syntax-error'}),
|
||||
('PayerError', {'reason': 'variable-error'}),
|
||||
('PayerDataError', {'key': 'foo', 'reason': 'empty-template'}),
|
||||
('PayerDataError', {'key': 'foo', 'reason': 'empty-result'}),
|
||||
('PayerDataError', {'key': 'foo', 'reason': 'syntax-error'}),
|
||||
('PayerDataError', {'key': 'foo', 'reason': 'variable-error'}),
|
||||
('PayerDataError', {'key': 'foo', 'reason': 'not-a-boolean'}),
|
||||
]
|
||||
for error, error_details in errors:
|
||||
lines.append(
|
||||
|
@ -1181,41 +1195,115 @@ def test_journal_pool_lines(app, admin_user, draft):
|
|||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[10].pk).text().strip()
|
||||
== "{'error': 'PricingBookingCheckTypeError', 'error_details': {'reason': 'not-found'}} {'event': 'foobar'}"
|
||||
)
|
||||
if draft:
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[11].pk).text())
|
||||
== 'Error (Check type error: pricing not configured (group: foo-bar, check type: foo-reason))%s'
|
||||
% error_links
|
||||
)
|
||||
else:
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[11].pk).text())
|
||||
== 'Fixed (Check type error: pricing not configured (group: foo-bar, check type: foo-reason)) reset'
|
||||
)
|
||||
assert resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[11].pk).text().strip() == (
|
||||
"{'error': 'PricingBookingCheckTypeError', 'error_details': {'check_type': 'foo-reason', "
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[11].pk).text())
|
||||
== 'Error (Check type error: pricing not configured (group: foo-bar, check type: foo-reason))%s'
|
||||
% error_links
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[11].pk).text().strip()
|
||||
== "{'error': 'PricingBookingCheckTypeError', 'error_details': {'check_type': 'foo-reason', "
|
||||
"'check_type_group': 'foo-bar', 'reason': 'not-configured'}} {'event': 'foobar'}"
|
||||
)
|
||||
if draft:
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[12].pk).text())
|
||||
== 'Error (Check type error: wrong kind (group: foo-bar, check type: foo-reason))%s' % error_links
|
||||
)
|
||||
else:
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[12].pk).text())
|
||||
== 'Ignored (Check type error: wrong kind (group: foo-bar, check type: foo-reason)) reset'
|
||||
)
|
||||
assert resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[12].pk).text().strip() == (
|
||||
"{'error': 'PricingBookingCheckTypeError', 'error_details': {'check_type': 'foo-reason', "
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[12].pk).text())
|
||||
== 'Error (Check type error: wrong kind (group: foo-bar, check type: foo-reason))%s' % error_links
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[12].pk).text().strip()
|
||||
== "{'error': 'PricingBookingCheckTypeError', 'error_details': {'check_type': 'foo-reason', "
|
||||
"'check_type_group': 'foo-bar', 'reason': 'wrong-kind'}} {'event': 'foobar'}"
|
||||
)
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[13].pk).text())
|
||||
== 'Success (Injected)'
|
||||
== 'Error (Impossible to determine payer: Template is empty)%s' % error_links
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[13].pk).text().strip()
|
||||
== "{'error': 'PayerError', 'error_details': {'reason': 'empty-template'}} {'event': 'foobar'}"
|
||||
)
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[14].pk).text())
|
||||
== 'Error (Impossible to determine payer: Result is empty)%s' % error_links
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[14].pk).text().strip()
|
||||
== "{'error': 'PayerError', 'error_details': {'reason': 'empty-result'}} {'event': 'foobar'}"
|
||||
)
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[15].pk).text())
|
||||
== 'Error (Impossible to determine payer: Syntax error)%s' % error_links
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[15].pk).text().strip()
|
||||
== "{'error': 'PayerError', 'error_details': {'reason': 'syntax-error'}} {'event': 'foobar'}"
|
||||
)
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[16].pk).text())
|
||||
== 'Error (Impossible to determine payer: Variable error)%s' % error_links
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[16].pk).text().strip()
|
||||
== "{'error': 'PayerError', 'error_details': {'reason': 'variable-error'}} {'event': 'foobar'}"
|
||||
)
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[17].pk).text())
|
||||
== 'Error (Impossible to get payer foo: Template is empty)%s' % error_links
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[17].pk).text().strip()
|
||||
== "{'error': 'PayerDataError', 'error_details': {'key': 'foo', 'reason': 'empty-template'}} {'event': 'foobar'}"
|
||||
)
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[18].pk).text())
|
||||
== 'Error (Impossible to get payer foo: Result is empty)%s' % error_links
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[18].pk).text().strip()
|
||||
== "{'error': 'PayerDataError', 'error_details': {'key': 'foo', 'reason': 'empty-result'}} {'event': 'foobar'}"
|
||||
)
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[19].pk).text())
|
||||
== 'Error (Impossible to get payer foo: Syntax error)%s' % error_links
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[19].pk).text().strip()
|
||||
== "{'error': 'PayerDataError', 'error_details': {'key': 'foo', 'reason': 'syntax-error'}} {'event': 'foobar'}"
|
||||
)
|
||||
if draft:
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[20].pk).text())
|
||||
== 'Error (Impossible to get payer foo: Variable error)%s' % error_links
|
||||
)
|
||||
else:
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[20].pk).text())
|
||||
== 'Fixed (Impossible to get payer foo: Variable error) reset'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[20].pk).text().strip()
|
||||
== "{'error': 'PayerDataError', 'error_details': {'key': 'foo', 'reason': 'variable-error'}} {'event': 'foobar'}"
|
||||
)
|
||||
if draft:
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[21].pk).text())
|
||||
== 'Error (Impossible to get payer foo: Result is not a boolean)%s' % error_links
|
||||
)
|
||||
else:
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[21].pk).text())
|
||||
== 'Ignored (Impossible to get payer foo: Result is not a boolean) reset'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[21].pk).text().strip()
|
||||
== "{'error': 'PayerDataError', 'error_details': {'key': 'foo', 'reason': 'not-a-boolean'}} {'event': 'foobar'}"
|
||||
)
|
||||
assert (
|
||||
format_status(resp.pyquery('tr[data-line-id="%s"] td.status' % lines[22].pk).text())
|
||||
== 'Success (Injected)'
|
||||
)
|
||||
assert (
|
||||
resp.pyquery('tr[data-details-for-line-id="%s"] td pre' % lines[22].pk).text().strip()
|
||||
== "{'foo': 'bar'} {'event': 'foobar2'}"
|
||||
)
|
||||
|
||||
|
@ -1251,12 +1339,12 @@ def test_journal_pool_lines(app, admin_user, draft):
|
|||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/journal/' % (regie.pk, campaign.pk, pool.pk),
|
||||
params={'payer_external_id': 'payer:2'},
|
||||
)
|
||||
assert len(resp.pyquery('tr td.status')) == 13
|
||||
assert len(resp.pyquery('tr td.status')) == 22
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/journal/' % (regie.pk, campaign.pk, pool.pk),
|
||||
params={'user_external_id': 'user:1'},
|
||||
)
|
||||
assert len(resp.pyquery('tr td.status')) == 13
|
||||
assert len(resp.pyquery('tr td.status')) == 22
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/journal/' % (regie.pk, campaign.pk, pool.pk),
|
||||
params={'user_external_id': 'user:2'},
|
||||
|
@ -1281,13 +1369,13 @@ def test_journal_pool_lines(app, admin_user, draft):
|
|||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/journal/' % (regie.pk, campaign.pk, pool.pk),
|
||||
params={'status': 'error'},
|
||||
)
|
||||
assert len(resp.pyquery('tr td.status')) == 11
|
||||
assert len(resp.pyquery('tr td.status')) == 20
|
||||
if not draft:
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/journal/' % (regie.pk, campaign.pk, pool.pk),
|
||||
params={'status': 'error_todo'},
|
||||
)
|
||||
assert len(resp.pyquery('tr td.status')) == 9
|
||||
assert len(resp.pyquery('tr td.status')) == 18
|
||||
resp = app.get(
|
||||
'/manage/invoicing/regie/%s/campaign/%s/pool/%s/journal/' % (regie.pk, campaign.pk, pool.pk),
|
||||
params={'status': 'error_ignored'},
|
||||
|
|
|
@ -841,7 +841,7 @@ def test_detail_agenda_pricing_test_tool_for_event(mock_pricing_data_event, mock
|
|||
resp.form['agenda'] = agenda.pk
|
||||
resp.form['event_slug'] = 'foo'
|
||||
resp.form['user_external_id'] = 'user:1'
|
||||
resp.form['adult_external_id'] = 'adult:1'
|
||||
resp.form['payer_external_id'] = 'payer:1'
|
||||
resp.form['booking_status'] = 'presence'
|
||||
resp = resp.form.submit().follow()
|
||||
assert 'Computed pricing data' not in resp
|
||||
|
@ -867,7 +867,7 @@ def test_detail_agenda_pricing_test_tool_for_event(mock_pricing_data_event, mock
|
|||
event={'start_datetime': '2021-09-01T12:00:00+02:00'},
|
||||
check_status={'status': 'presence', 'check_type': None},
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
)
|
||||
]
|
||||
|
||||
|
@ -910,7 +910,7 @@ def test_detail_agenda_pricing_test_tool_for_event(mock_pricing_data_event, mock
|
|||
event={'start_datetime': '2021-09-01T00:00:00+02:00', 'recurrence_days': [1]},
|
||||
check_status={'status': 'presence', 'check_type': None},
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
)
|
||||
]
|
||||
|
||||
|
@ -933,7 +933,7 @@ def test_detail_agenda_pricing_test_tool_for_event_event_error(mock_event, app,
|
|||
resp.form['agenda'] = agenda.pk
|
||||
resp.form['event_slug'] = 'foo-event'
|
||||
resp.form['user_external_id'] = 'user:1'
|
||||
resp.form['adult_external_id'] = 'adult:1'
|
||||
resp.form['payer_external_id'] = 'payer:1'
|
||||
resp.form['booking_status'] = 'presence'
|
||||
resp = resp.form.submit().follow()
|
||||
assert resp.context['test_tool_form'].errors['event_slug'] == ['foo bar foo-event']
|
||||
|
@ -976,7 +976,7 @@ def test_detail_agenda_pricing_test_tool_for_event_booking_status(
|
|||
resp.form['agenda'] = agenda.pk
|
||||
resp.form['event_slug'] = 'foo'
|
||||
resp.form['user_external_id'] = 'user:1'
|
||||
resp.form['adult_external_id'] = 'adult:1'
|
||||
resp.form['payer_external_id'] = 'payer:1'
|
||||
resp.form['booking_status'] = 'presence'
|
||||
resp = resp.form.submit().follow()
|
||||
assert resp.form['booking_status'].options == [
|
||||
|
@ -1037,7 +1037,7 @@ def test_detail_agenda_pricing_test_tool_for_flat_fee_schedule(mock_pricing_data
|
|||
|
||||
resp.form['agenda'] = agenda.pk
|
||||
resp.form['user_external_id'] = 'user:1'
|
||||
resp.form['adult_external_id'] = 'adult:1'
|
||||
resp.form['payer_external_id'] = 'payer:1'
|
||||
mock_pricing_data.return_value = {'foo': 'bar', 'pricing': Decimal('42')}
|
||||
resp = resp.form.submit().follow()
|
||||
assert 'Computed pricing data' in resp
|
||||
|
@ -1046,7 +1046,7 @@ def test_detail_agenda_pricing_test_tool_for_flat_fee_schedule(mock_pricing_data
|
|||
request=mock.ANY,
|
||||
pricing_date=datetime.date(2021, 9, 1),
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
)
|
||||
]
|
||||
assert '<p>Pricing: 42.00</p>' in resp
|
||||
|
@ -1088,14 +1088,14 @@ def test_detail_agenda_pricing_test_tool_for_flat_fee_schedule(mock_pricing_data
|
|||
resp.form['agenda'] = agenda.pk
|
||||
resp.form['billing_date'] = billing_date1.pk
|
||||
resp.form['user_external_id'] = 'user:1'
|
||||
resp.form['adult_external_id'] = 'adult:1'
|
||||
resp.form['payer_external_id'] = 'payer:1'
|
||||
resp = resp.form.submit().follow()
|
||||
assert mock_pricing_data.call_args_list == [
|
||||
mock.call(
|
||||
request=mock.ANY,
|
||||
pricing_date=datetime.date(2021, 9, 1),
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
)
|
||||
]
|
||||
|
||||
|
@ -1107,7 +1107,7 @@ def test_detail_agenda_pricing_test_tool_for_flat_fee_schedule(mock_pricing_data
|
|||
request=mock.ANY,
|
||||
pricing_date=datetime.date(2021, 9, 15),
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
)
|
||||
]
|
||||
|
||||
|
@ -1119,14 +1119,14 @@ def test_detail_agenda_pricing_test_tool_for_flat_fee_schedule(mock_pricing_data
|
|||
assert 'agenda' not in resp.context['test_tool_form'].fields
|
||||
resp.form['billing_date'] = billing_date1.pk
|
||||
resp.form['user_external_id'] = 'user:1'
|
||||
resp.form['adult_external_id'] = 'adult:1'
|
||||
resp.form['payer_external_id'] = 'payer:1'
|
||||
resp = resp.form.submit().follow()
|
||||
assert mock_pricing_data.call_args_list == [
|
||||
mock.call(
|
||||
request=mock.ANY,
|
||||
pricing_date=datetime.date(2021, 9, 1),
|
||||
user_external_id='user:1',
|
||||
adult_external_id='adult:1',
|
||||
payer_external_id='payer:1',
|
||||
)
|
||||
]
|
||||
|
||||
|
|
|
@ -124,7 +124,8 @@ def test_pricing_edit_extra_variables(app, admin_user):
|
|||
pricing.refresh_from_db()
|
||||
assert pricing.extra_variables == {'foo': 'bar'}
|
||||
assert '<label>Extra variables:</label>' in resp.text
|
||||
assert '<i>foo</i>' in resp
|
||||
assert '<dt><b>foo:</b></dt>' in resp
|
||||
assert '<dd><pre>bar</pre></dd>' in resp
|
||||
|
||||
resp = resp.click(href='/manage/pricing/model/%s/variable/' % pricing.pk)
|
||||
assert resp.form['form-TOTAL_FORMS'].value == '2'
|
||||
|
@ -141,7 +142,10 @@ def test_pricing_edit_extra_variables(app, admin_user):
|
|||
'foo': 'bar-bis',
|
||||
'blah': 'baz',
|
||||
}
|
||||
assert '<i>blah</i>, <i>foo</i>' in resp
|
||||
assert '<dt><b>blah:</b></dt>' in resp
|
||||
assert '<dd><pre>baz</pre></dd>' in resp
|
||||
assert '<dt><b>foo:</b></dt>' in resp
|
||||
assert '<dd><pre>bar-bis</pre></dd>' in resp
|
||||
|
||||
resp = resp.click(href='/manage/pricing/model/%s/variable/' % pricing.pk)
|
||||
assert resp.form['form-TOTAL_FORMS'].value == '3'
|
||||
|
@ -159,7 +163,45 @@ def test_pricing_edit_extra_variables(app, admin_user):
|
|||
assert pricing.extra_variables == {
|
||||
'foo': 'bar',
|
||||
}
|
||||
assert '<i>foo</i>' in resp
|
||||
assert '<dt><b>foo:</b></dt>' in resp
|
||||
assert '<dd><pre>bar</pre></dd>' in resp
|
||||
|
||||
|
||||
def test_pricing_edit_payer_variables(app, admin_user):
|
||||
pricing = Pricing.objects.create(label='Model')
|
||||
assert pricing.payer_variables == {}
|
||||
|
||||
app = login(app)
|
||||
resp = app.get('/manage/pricing/model/%s/' % pricing.pk)
|
||||
assert '<label>Payer variables:</label>' in resp.text
|
||||
assert '<dt><b>payer_external_id:</b></dt>' in resp
|
||||
assert '<dt><b>payer_first_name:</b></dt>' in resp
|
||||
assert '<dt><b>payer_last_name:</b></dt>' in resp
|
||||
assert '<dt><b>payer_demat:</b></dt>' in resp
|
||||
assert '<dt><b>payer_direct_debit:</b></dt>' in resp
|
||||
assert resp.text.count('<dd><pre></pre></dd>') == 5
|
||||
resp = resp.click(href='/manage/pricing/model/%s/payer/' % pricing.pk)
|
||||
assert resp.form['form-TOTAL_FORMS'].value == '5'
|
||||
assert resp.form['form-0-key'].value == 'payer_external_id'
|
||||
assert resp.form['form-0-value'].value == ''
|
||||
assert resp.form['form-1-key'].value == 'payer_first_name'
|
||||
assert resp.form['form-1-value'].value == ''
|
||||
assert resp.form['form-2-key'].value == 'payer_last_name'
|
||||
assert resp.form['form-2-value'].value == ''
|
||||
assert resp.form['form-3-key'].value == 'payer_demat'
|
||||
assert resp.form['form-3-value'].value == ''
|
||||
assert resp.form['form-4-key'].value == 'payer_direct_debit'
|
||||
assert resp.form['form-4-value'].value == ''
|
||||
resp.form['form-0-value'] = 'payer:42'
|
||||
resp = resp.form.submit().follow()
|
||||
pricing.refresh_from_db()
|
||||
assert pricing.payer_variables == {
|
||||
'payer_external_id': 'payer:42',
|
||||
'payer_first_name': '',
|
||||
'payer_last_name': '',
|
||||
'payer_demat': '',
|
||||
'payer_direct_debit': '',
|
||||
}
|
||||
|
||||
|
||||
def test_pricing_add_category(app, admin_user):
|
||||
|
|
|
@ -16,6 +16,8 @@ from lingo.pricing.models import (
|
|||
CriteriaCategory,
|
||||
CriteriaConditionNotFound,
|
||||
MultipleDefaultCriteriaCondition,
|
||||
PayerDataError,
|
||||
PayerError,
|
||||
Pricing,
|
||||
PricingBookingCheckTypeError,
|
||||
PricingBookingNotCheckedError,
|
||||
|
@ -51,7 +53,10 @@ class MockedRequestResponse(mock.Mock):
|
|||
|
||||
|
||||
def mocked_requests_send(request, **kwargs):
|
||||
data = [{'id': 1, 'fields': {'foo': 'bar'}}, {'id': 2, 'fields': {'foo': 'baz'}}] # fake result
|
||||
data = [
|
||||
{'id': 1, 'fields': {'foo': 'bar', 'bar': False}},
|
||||
{'id': 2, 'fields': {'foo': 'baz', 'bar': True}},
|
||||
] # fake result
|
||||
return MockedRequestResponse(content=json.dumps({'data': data}))
|
||||
|
||||
|
||||
|
@ -433,7 +438,7 @@ def test_get_pricing_context(mock_send, context, nocache):
|
|||
agenda_pricing.agendas.add(agenda)
|
||||
assert (
|
||||
agenda_pricing.get_pricing_context(
|
||||
request=context['request'], data={}, user_external_id='child:42', adult_external_id='parent:35'
|
||||
request=context['request'], data={}, user_external_id='child:42', payer_external_id='parent:35'
|
||||
)
|
||||
== {}
|
||||
)
|
||||
|
@ -451,7 +456,7 @@ def test_get_pricing_context(mock_send, context, nocache):
|
|||
'event': {'foo': 42},
|
||||
}
|
||||
assert agenda_pricing.get_pricing_context(
|
||||
request=context['request'], data=data, user_external_id='child:42', adult_external_id='parent:35'
|
||||
request=context['request'], data=data, user_external_id='child:42', payer_external_id='parent:35'
|
||||
) == {
|
||||
'foo': 'bar',
|
||||
'qf': '42',
|
||||
|
@ -460,24 +465,24 @@ def test_get_pricing_context(mock_send, context, nocache):
|
|||
'event': '42',
|
||||
}
|
||||
|
||||
# user_external_id and adult_external_id can be used in variables
|
||||
# user_external_id and payer_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 }}',
|
||||
'qf': '{{ cards|objects:"qf"|filter_by:"foo"|filter_value:user_external_id|filter_by:"bar"|filter_value:payer_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'
|
||||
request=context['request'], data={}, user_external_id='child:42', payer_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 }}',
|
||||
'qf': '{{ cards|objects:"qf"|filter_by:"foo"|filter_value:user_external_raw_id|filter_by:"bar"|filter_value:payer_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'
|
||||
request=context['request'], data={}, user_external_id='child:42', payer_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
|
||||
|
@ -1030,7 +1035,7 @@ def test_get_pricing_data(context):
|
|||
request=context['request'],
|
||||
pricing_date=datetime.date(year=2021, month=9, day=1),
|
||||
user_external_id='child:42',
|
||||
adult_external_id='parent:35',
|
||||
payer_external_id='parent:35',
|
||||
) == {
|
||||
'pricing': 42,
|
||||
'calculation_details': {
|
||||
|
@ -1069,7 +1074,7 @@ def test_get_pricing_data_for_event(context):
|
|||
event={'start_datetime': make_aware(datetime.datetime(2021, 9, 15, 12, 00)).isoformat()},
|
||||
check_status={'status': 'not-booked'},
|
||||
user_external_id='child:42',
|
||||
adult_external_id='parent:35',
|
||||
payer_external_id='parent:35',
|
||||
) == {
|
||||
'pricing': 0,
|
||||
'calculation_details': {
|
||||
|
@ -1683,3 +1688,170 @@ def test_agenda_pricing_iter_pricing_matrix_empty():
|
|||
agenda_pricing.agendas.add(agenda)
|
||||
|
||||
assert list(agenda_pricing.iter_pricing_matrix()) == []
|
||||
|
||||
|
||||
@mock.patch('requests.Session.send', side_effect=mocked_requests_send)
|
||||
def test_get_payer_external_id(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),
|
||||
)
|
||||
|
||||
values = [
|
||||
('bar', 'bar'),
|
||||
('{{ 40|add:2 }}', '42'),
|
||||
('{{ cards|objects:"foo"|first|get:"id" }}', '1'),
|
||||
]
|
||||
for value, result in values:
|
||||
pricing.payer_variables = {
|
||||
'payer_external_id': value,
|
||||
}
|
||||
pricing.save()
|
||||
assert (
|
||||
agenda_pricing.get_payer_external_id(request=context['request'], user_external_id='child:42')
|
||||
== result
|
||||
)
|
||||
|
||||
values = [
|
||||
('', 'empty-template'),
|
||||
('{{ "" }}', 'empty-result'),
|
||||
('{% for %}', 'syntax-error'),
|
||||
('{{ "foo"|add:user.email }}', 'variable-error'),
|
||||
]
|
||||
for value, error in values:
|
||||
pricing.payer_variables = {
|
||||
'payer_external_id': value,
|
||||
}
|
||||
pricing.save()
|
||||
with pytest.raises(PayerError) as e:
|
||||
agenda_pricing.get_payer_external_id(request=context['request'], user_external_id='child:42')
|
||||
assert e.value.details == {'reason': error}
|
||||
|
||||
# user_external_id can be used in variables
|
||||
pricing.payer_variables = {
|
||||
'payer_external_id': '{{ cards|objects:"qf"|filter_by:"foo"|filter_value:user_external_id|first|get:"id" }}',
|
||||
}
|
||||
pricing.save()
|
||||
mock_send.reset_mock()
|
||||
agenda_pricing.get_payer_external_id(request=context['request'], user_external_id='child:42')
|
||||
assert 'filter-foo=child%3A42&' in mock_send.call_args_list[0][0][0].url
|
||||
pricing.payer_variables = {
|
||||
'payer_external_id': '{{ cards|objects:"qf"|filter_by:"foo"|filter_value:user_external_raw_id|first|get:"id" }}',
|
||||
}
|
||||
pricing.save()
|
||||
mock_send.reset_mock()
|
||||
agenda_pricing.get_payer_external_id(request=context['request'], user_external_id='child:42')
|
||||
assert 'filter-foo=42&' 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
|
||||
|
|
Ok, juste une question de forme ici j’aurais bien vu un
def get_cached_payer_data
comme nom de fonction, pour que quelqu’un qui (comme moi) ne connaît pas du tout le code comprenne plus directement, à la lecture du code qui l’utilise plus loin, l’appel à première vue un peu mystérieux :fait, merci