QuestionnaireChild -> QuestionnaireRow

This commit is contained in:
Mira Weller committed 2026-10-30 12:39:16 +01:00
1 parent c8964fad9d
commit 2c88efceb1
19 files changed
+166 -166

No files matched your search

+25 -25
View File
@@ -61,7 +61,7 @@ from pretix.base.models import (
ItemVariation, ItemVariationMetaValue, Question, QuestionAnswer,
QuestionOption, Quota, SalesChannel,
)
from pretix.base.models.items import Questionnaire, QuestionnaireChild
from pretix.base.models.items import Questionnaire, QuestionnaireRow
from pretix.base.settings import (
PERSON_NAME_SALUTATIONS, PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS,
)
@@ -353,7 +353,7 @@ class ItemSerializer(SalesChannelMigrationMixin, I18nAwareModelSerializer):
self.fields['variations'].child.fields['limit_sales_channels'].child_relation.queryset = self.context['event'].organizer.sales_channels.all()
def get_personalized(self, item):
return QuestionnaireChild.objects.filter(questionnaire__items=item, system_datafield__in=('@attendee_name_parts', '@attendee_email')).exists()
return QuestionnaireRow.objects.filter(questionnaire__items=item, system_datafield__in=('@attendee_name_parts', '@attendee_email')).exists()
def put_personalized(self, item, new_value):
if item and new_value != self.get_personalized(item):
@@ -588,7 +588,7 @@ class DatafieldRelatedField(serializers.SlugRelatedField):
if data is None:
return {'user_datafield': None, 'system_datafield': None}
elif type(data) == str and data.startswith('@'):
if data not in QuestionnaireChild.SystemDatafield.values:
if data not in QuestionnaireRow.SystemDatafield.values:
self.fail("invalid_choice", input=data)
return {'user_datafield': None, 'system_datafield': data}
elif type(data) == str:
@@ -631,7 +631,7 @@ class QuestionFormSerializer(I18nAwareModelSerializer):
options = InlineQuestionOptionSerializer(source='user_datafield.options', many=True, required=False, default=[])
class Meta:
model = QuestionnaireChild
model = QuestionnaireRow
fields = ('datafield', 'label', 'type', 'required', 'help_text', 'items', 'options', 'position',
'show_during_checkin', 'identifier', 'dependency_question', 'dependency_values',
'print_on_invoice', 'valid_number_min',
@@ -646,7 +646,7 @@ class QuestionFormSerializer(I18nAwareModelSerializer):
if isinstance(instance, QuestionAnswer):
position = instance.cartposition if instance.cartposition_id else instance.orderposition
print('searching ', repr(instance), repr(position), repr(instance.question), instance.pk, position.item_id, self.context) # TODO remove print
qcs = list(QuestionnaireChild.objects.filter(user_datafield_id=instance.question_id, questionnaire__items__in=[position.item_id]))
qcs = list(QuestionnaireRow.objects.filter(user_datafield_id=instance.question_id, questionnaire__items__in=[position.item_id]))
return next((qc for qc in qcs if qc.questionnaire.type == self.context.get('preferred_questionnaire_type', Questionnaire.QuestionnaireType.ORDER_POSITION_SALE)), qcs[0])
return super().get_attribute(instance)
@@ -681,7 +681,7 @@ class QuestionFormSerializer(I18nAwareModelSerializer):
part["type"] = Question.FieldType.CHOICE
part["options"] = [{"id": idx + 1, "identifier": k, "answer": i18n_all_from_gettext(v, settings.locales), "position": idx + 1} for idx, (k, v) in
enumerate(PERSON_NAME_SALUTATIONS)]
part["id"] = QuestionnaireChild.cook_id_from_parts(row['questionnaire_id'], None, part["identifier"])
part["id"] = QuestionnaireRow.cook_id_from_parts(row['questionnaire_id'], None, part["identifier"])
part["required"] = part["required"] and k in REQUIRED_NAME_PARTS
mangled_results.append(part)
pos_offset += 1
@@ -720,7 +720,7 @@ class POSCompatQuestionSerializer(QuestionFormSerializer):
options = InlineQuestionOptionSerializer(source='user_datafield.options', many=True, required=False, default=[])
class Meta:
model = QuestionnaireChild
model = QuestionnaireRow
fields = ('id', 'question', 'type', 'required', 'items', 'options', 'position',
'ask_during_checkin', 'show_during_checkin', 'identifier', 'dependency_question', 'dependency_values',
'hidden', 'dependency_value', 'print_on_invoice', 'help_text', 'valid_number_min',
@@ -735,7 +735,7 @@ class POSCompatQuestionSerializer(QuestionFormSerializer):
if isinstance(instance, QuestionAnswer):
position = instance.cartposition if instance.cartposition_id else instance.orderposition
print('searching ', repr(instance), repr(position), repr(instance.question), instance.pk, position.item_id, self.context) # TODO remove print
qcs = list(QuestionnaireChild.objects.filter(user_datafield_id=instance.question_id, questionnaire__items__in=[position.item_id]))
qcs = list(QuestionnaireRow.objects.filter(user_datafield_id=instance.question_id, questionnaire__items__in=[position.item_id]))
return next((qc for qc in qcs if qc.questionnaire.type == self.context.get('preferred_questionnaire_type', Questionnaire.QuestionnaireType.ORDER_POSITION_SALE)), qcs[0])
return super().get_attribute(instance)
@@ -775,7 +775,7 @@ class POSCompatQuestionSerializer(QuestionFormSerializer):
part["type"] = Question.FieldType.CHOICE
part["options"] = [{"id": idx + 1, "identifier": k, "answer": i18n_all_from_gettext(v, settings.locales), "position": idx + 1} for idx, (k, v) in
enumerate(PERSON_NAME_SALUTATIONS)]
part["id"] = QuestionnaireChild.cook_id_from_parts(row['questionnaire_id'], None, part["identifier"])
part["id"] = QuestionnaireRow.cook_id_from_parts(row['questionnaire_id'], None, part["identifier"])
part["required"] = part["required"] and k in REQUIRED_NAME_PARTS
mangled_results.append(part)
pos_offset += 1
@@ -890,14 +890,14 @@ class RenderedMarkdownField(serializers.CharField):
return rich_text(value)
class InlineQuestionnaireChildSerializer(I18nAwareModelSerializer):
class InlineQuestionnaireRowSerializer(I18nAwareModelSerializer):
datafield = DatafieldRelatedField(allow_null=True, source='*', queryset=Question.objects.none())
dependency_question = DatafieldRelatedField(allow_null=True, required=False, queryset=Question.objects.none())
rendered_help_text = RenderedMarkdownField(read_only=True, source='help_text')
cooked_id = serializers.IntegerField(read_only=True, allow_null=True)
class Meta:
model = QuestionnaireChild
model = QuestionnaireRow
fields = ('datafield', 'required', 'label', 'help_text', 'dependency_question', 'dependency_values', 'rendered_help_text', 'cooked_id')
def __init__(self, *args, **kwargs):
@@ -925,10 +925,10 @@ class QuestionnaireSerializer(I18nAwareModelSerializer):
class Meta:
model = Questionnaire
fields = ('id', 'type', 'internal_name', 'items', 'position', 'all_sales_channels', 'limit_sales_channels', 'children')
fields = ('id', 'type', 'internal_name', 'items', 'position', 'all_sales_channels', 'limit_sales_channels', 'rows')
def __init__(self, *args, **kwargs):
self.fields['children'] = InlineQuestionnaireChildSerializer(many=True, required=True, context=kwargs['context'], partial=False)
self.fields['rows'] = InlineQuestionnaireRowSerializer(many=True, required=True, context=kwargs['context'], partial=False)
self.fields['items'].child_relation.queryset = kwargs['context']['event'].items.all()
self.fields['limit_sales_channels'].child_relation.queryset = kwargs['context']['event'].organizer.sales_channels.all()
super().__init__(*args, **kwargs)
@@ -955,14 +955,14 @@ class QuestionnaireSerializer(I18nAwareModelSerializer):
#Question.clean_items(event, full_data.get('items') or [])
if (full_data.get('type') == Questionnaire.QuestionnaireType.ORDER_POSITION_CHECKIN
and any(c['dependency_question'] for c in full_data['children'])):
and any(c['dependency_question'] for c in full_data['rows'])):
raise ValidationError('Dependencies are not supported during check-in.')
if (not full_data.get('type').startswith('position_')
and any(c['system_datafield'] for c in full_data['children'])):
and any(c['system_datafield'] for c in full_data['rows'])):
raise ValidationError('System data fields are only supported on order positions.')
system_fields = set(c['system_datafield'] for c in full_data['children'])
system_fields = set(c['system_datafield'] for c in full_data['rows'])
if ('zipcode' in system_fields or 'state' in system_fields or 'street' in system_fields or 'city' in system_fields) and 'country' not in system_fields:
raise ValidationError('The system data fields "street", "zip code", "city", and "state" can only be used in combination with the "country" field.')
@@ -970,9 +970,9 @@ class QuestionnaireSerializer(I18nAwareModelSerializer):
@transaction.atomic
def create(self, validated_data):
children_data = validated_data.pop('children') if 'children' in validated_data else []
rows_data = validated_data.pop('rows') if 'rows' in validated_data else []
questionnaire = super().create(validated_data)
self.set_children(questionnaire, children_data)
self.set_rows(questionnaire, rows_data)
try:
Questionnaire.check_constraints(questionnaire.event)
except ValidationError as e:
@@ -981,20 +981,20 @@ class QuestionnaireSerializer(I18nAwareModelSerializer):
@transaction.atomic
def update(self, instance, validated_data):
children_data = validated_data.pop('children', None)
rows_data = validated_data.pop('rows', None)
questionnaire = super().update(instance, validated_data)
if children_data is not None:
self.set_children(questionnaire, children_data)
if rows_data is not None:
self.set_rows(questionnaire, rows_data)
try:
Questionnaire.check_constraints(questionnaire.event)
except ValidationError as e:
raise DrfValidationError(as_serializer_error(e))
return questionnaire
def set_children(self, questionnaire, new_data):
def set_rows(self, questionnaire, new_data):
result = {}
child_serializer = self.fields['children'].child
existing_children = questionnaire.children.all()
child_serializer = self.fields['rows'].child
existing_rows = questionnaire.rows.all()
def q(qc):
return qc['user_datafield'] or qc['system_datafield']
for i, d in enumerate(new_data):
@@ -1004,7 +1004,7 @@ class QuestionnaireSerializer(I18nAwareModelSerializer):
d.setdefault('help_text', None)
d.setdefault('dependency_question', None)
d.setdefault('dependency_values', None)
for existing, update_data in zip_longest(existing_children, new_data):
for existing, update_data in zip_longest(existing_rows, new_data):
if update_data:
if update_data.get('dependency_question'):
try:
+6 -6
View File
@@ -58,7 +58,7 @@ from pretix.base.media import MEDIA_TYPES
from pretix.base.models import (
CachedFile, Checkin, Customer, Device, GiftCard, Invoice, InvoiceAddress,
InvoiceLine, Item, ItemVariation, Order, OrderPosition, Question,
QuestionAnswer, QuestionnaireChild, ReusableMedium, SalesChannel, Seat,
QuestionAnswer, QuestionnaireRow, ReusableMedium, SalesChannel, Seat,
SubEvent, TaxRule, Voucher,
)
from pretix.base.models.orders import (
@@ -242,7 +242,7 @@ class MixedAnswerListSerializer(ListSerializer):
def to_representation(self, instance):
answers = super().to_representation(instance.answers)
answers_dict = {answer["question"]: answer for answer in answers}
for qc in QuestionnaireChild.objects.filter(questionnaire__items__id=instance.item_id):
for qc in QuestionnaireRow.objects.filter(questionnaire__items__id=instance.item_id):
if qc.user_datafield_id:
if answer := answers_dict.get(qc.user_datafield_id):
answer = dict(answer)
@@ -251,7 +251,7 @@ class MixedAnswerListSerializer(ListSerializer):
answer["user_datafield_id"] = qc.user_datafield_id
answers.append(answer)
elif (sys_answer := instance.get_system_answer(qc.system_datafield)) is not None:
if qc.system_datafield == QuestionnaireChild.SystemDatafield.ATTENDEE_NAME_PARTS:
if qc.system_datafield == QuestionnaireRow.SystemDatafield.ATTENDEE_NAME_PARTS:
name_parts = instance.attendee_name_parts
settings = self.context['event'].settings
for k, v, w, *__ in PERSON_NAME_SCHEMES.get(settings.name_scheme)['fields']:
@@ -269,7 +269,7 @@ class MixedAnswerListSerializer(ListSerializer):
except ValueError:
pass
answers.append({
"question": QuestionnaireChild.cook_id_from_parts(qc.questionnaire_id, None, f'{qc.system_datafield}:{k}'),
"question": QuestionnaireRow.cook_id_from_parts(qc.questionnaire_id, None, f'{qc.system_datafield}:{k}'),
"answer": name_parts[k],
"question_identifier": f'{qc.system_datafield}:{k}',
"options": options,
@@ -293,9 +293,9 @@ class MixedAnswerListSerializer(ListSerializer):
settings = self.context['event'].settings
for answer in data["answers"]:
try:
qid, dfid, sys_df = QuestionnaireChild.uncook_id(answer["question"])
qid, dfid, sys_df = QuestionnaireRow.uncook_id(answer["question"])
except ValueError:
if type(answer["question"]) is str and answer["question"] in QuestionnaireChild.SYSTEM_DATAFIELD_NUMBERS:
if type(answer["question"]) is str and answer["question"] in QuestionnaireRow.SYSTEM_DATAFIELD_NUMBERS:
qid, dfid, sys_df = None, None, answer["question"]
else:
raise ValidationError('Invalid question ID')
+2 -2
View File
@@ -65,7 +65,7 @@ from pretix.api.views.order import OrderPositionFilter
from pretix.base.i18n import language
from pretix.base.models import (
CachedFile, Checkin, CheckinList, Device, Event, Order, OrderPosition,
Question, Questionnaire, QuestionnaireChild, ReusableMedium,
Question, Questionnaire, QuestionnaireRow, ReusableMedium,
RevokedTicketSecret, TeamAPIToken,
)
from pretix.base.models.orders import PrintLog
@@ -799,7 +799,7 @@ def _redeem_process(*, checkinlists, raw_barcode, answers_data, datetime, force,
# 5. Pre-validate all incoming answers, handle file upload
given_answers = {}
if answers_data:
for qc in QuestionnaireChild.objects.filter(
for qc in QuestionnaireRow.objects.filter(
questionnaire__items__in=[op.item], questionnaire__type=Questionnaire.QuestionnaireType.ORDER_POSITION_CHECKIN
):
if qc.user_datafield_id and (str(qc.user_datafield_id) in answers_data or str(qc.cooked_id) in answers_data):
+10 -10
View File
@@ -60,7 +60,7 @@ from pretix.base.models import (
CartPosition, Device, Item, ItemAddOn, ItemBundle, ItemCategory,
ItemProgramTime, ItemVariation, Question, QuestionOption, Quota,
)
from pretix.base.models.items import Questionnaire, QuestionnaireChild
from pretix.base.models.items import Questionnaire, QuestionnaireRow
from pretix.base.services.quotas import QuotaAvailability
from pretix.helpers.dicts import merge_dicts
from pretix.helpers.i18n import i18ncomp
@@ -472,7 +472,7 @@ with scopes_disabled():
ask_during_checkin = django_filters.BooleanFilter(method='ask_during_checkin_qs')
class Meta:
model = QuestionnaireChild
model = QuestionnaireRow
fields = ['required']
def ask_during_checkin_qs(self, qs, name, value):
@@ -494,7 +494,7 @@ class DeprecationWarningMixin:
class POSCompatQuestionViewSet(DeprecationWarningMixin, viewsets.ReadOnlyModelViewSet):
serializer_class = POSCompatQuestionSerializer
queryset = QuestionnaireChild.objects.none()
queryset = QuestionnaireRow.objects.none()
permission = None
sales_channel_ident = None
@@ -511,7 +511,7 @@ class POSCompatQuestionViewSet(DeprecationWarningMixin, viewsets.ReadOnlyModelVi
)
def get_queryset(self):
return QuestionnaireChild.objects.filter(
return QuestionnaireRow.objects.filter(
# only matching sales channel
Q(questionnaire__all_sales_channels=True) | Q(questionnaire__limit_sales_channels__identifier=self.sales_channel_ident),
# only real questions, no text blocks
@@ -533,7 +533,7 @@ class POSCompatQuestionViewSet(DeprecationWarningMixin, viewsets.ReadOnlyModelVi
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
qid, dfid, sys_df = QuestionnaireChild.uncook_id(self.kwargs[lookup_url_kwarg])
qid, dfid, sys_df = QuestionnaireRow.uncook_id(self.kwargs[lookup_url_kwarg])
print(self.kwargs[lookup_url_kwarg], qid, dfid, sys_df)
if qid and sys_df:
obj = get_object_or_404(queryset, questionnaire__pk=qid, system_datafield=sys_df)
@@ -608,7 +608,7 @@ class BaseDatafieldViewSet(viewsets.ModelViewSet):
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
qid, dfid, sys_df = QuestionnaireChild.uncook_id(self.kwargs[lookup_url_kwarg])
qid, dfid, sys_df = QuestionnaireRow.uncook_id(self.kwargs[lookup_url_kwarg])
self.via_questionnaire_id = qid
obj = get_object_or_404(queryset, pk=dfid)
@@ -833,7 +833,7 @@ class QuestionnaireViewSet(ConditionalListView, viewsets.ModelViewSet):
write_permission = 'event.items:write'
def get_queryset(self):
return self.request.event.questionnaires.prefetch_related('children').all()
return self.request.event.questionnaires.prefetch_related('rows').all()
def perform_create(self, serializer):
serializer.save(event=self.request.event)
@@ -871,7 +871,7 @@ class QuestionFormView(generics.GenericAPIView):
# like POSCompatQuestionViewSet, but less compat
serializer_class = QuestionFormSerializer
queryset = QuestionnaireChild.objects.none()
queryset = QuestionnaireRow.objects.none()
permission = None
sales_channel_ident = None
@@ -888,7 +888,7 @@ class QuestionFormView(generics.GenericAPIView):
)
def get_queryset(self):
return QuestionnaireChild.objects.filter(
return QuestionnaireRow.objects.filter(
Q(questionnaire__all_sales_channels=True) | Q(questionnaire__limit_sales_channels__identifier=self.sales_channel_ident),
questionnaire__event=self.request.event,
# legacy compat only for position-level questions
@@ -907,7 +907,7 @@ class QuestionFormView(generics.GenericAPIView):
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
qid, dfid, sys_df = QuestionnaireChild.uncook_id(self.kwargs[lookup_url_kwarg])
qid, dfid, sys_df = QuestionnaireRow.uncook_id(self.kwargs[lookup_url_kwarg])
print(self.kwargs[lookup_url_kwarg], qid, dfid, sys_df)
if qid and sys_df:
obj = get_object_or_404(queryset, questionnaire__pk=qid, system_datafield=sys_df)
+23 -23
View File
@@ -83,7 +83,7 @@ from pretix.base.invoicing.transmission import (
get_transmission_types, transmission_types,
)
from pretix.base.models import (
InvoiceAddress, Item, Question, Questionnaire, QuestionnaireChild,
InvoiceAddress, Item, Question, Questionnaire, QuestionnaireRow,
QuestionOption,
)
from pretix.base.models.tax import ask_for_vat_id
@@ -963,7 +963,7 @@ class OrderLevelQuestionsForm(BaseQuestionsForm):
Q(all_sales_channels=True) | Q(limit_sales_channels__identifier=container.sales_channel.identifier),
event=event, type=Questionnaire.QuestionnaireType.ORDER_SALE,
).order_by('position').prefetch_related(
Prefetch('children', QuestionnaireChild.objects.prefetch_related(
Prefetch('rows', QuestionnaireRow.objects.prefetch_related(
Prefetch('user_datafield', Question.objects.prefetch_related(
Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch(
# This prefetch statement is utter bullshit, but it actually prevents Django from doing
@@ -975,18 +975,18 @@ class OrderLevelQuestionsForm(BaseQuestionsForm):
)))
))
),
to_attr='childlist')
to_attr='questionnairerowlist')
)
answerlist = container.answers.prefetch_related('options')
idx = 0
for questionnaire in questionnaires:
for child in getattr(questionnaire, 'childlist', questionnaire.children.all()):
if child.user_datafield:
df = child.user_datafield
self.fields[child.form_field_name] = self.build_user_question_field(request, event, answerlist, child, df)
for qrow in getattr(questionnaire, 'questionnairerowlist', questionnaire.rows.all()):
if qrow.user_datafield:
df = qrow.user_datafield
self.fields[qrow.form_field_name] = self.build_user_question_field(request, event, answerlist, qrow, df)
else:
self.fields[child.form_field_name] = self.build_text_block(request, event, child)
self.fields[qrow.form_field_name] = self.build_text_block(request, event, qrow)
idx += 1
def clean(self):
@@ -1025,14 +1025,14 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
self.fields['requested_valid_from'] = self.build_requested_valid_from_field(event, pos, item)
for questionnaire in questionnaires:
for child in getattr(questionnaire, 'childlist', questionnaire.children.all()):
if child.user_datafield:
df = child.user_datafield
self.fields[child.form_field_name] = self.build_user_question_field(request, event, pos.answerlist, child, df)
elif child.system_datafield:
self.fields[child.form_field_name] = self.build_system_question_field(request, event, pos, child)
for qrow in getattr(questionnaire, 'questionnairerowlist', questionnaire.rows.all()):
if qrow.user_datafield:
df = qrow.user_datafield
self.fields[qrow.form_field_name] = self.build_user_question_field(request, event, pos.answerlist, qrow, df)
elif qrow.system_datafield:
self.fields[qrow.form_field_name] = self.build_system_question_field(request, event, pos, qrow)
else:
self.fields[child.form_field_name] = self.build_text_block(request, event, child)
self.fields[qrow.form_field_name] = self.build_text_block(request, event, qrow)
responses = question_form_fields.send(sender=event, position=pos)
data = pos.meta_info_data
@@ -1097,7 +1097,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
)
def build_system_question_field(self, request, event, pos, qc):
if qc.system_datafield == QuestionnaireChild.SystemDatafield.ATTENDEE_NAME_PARTS:
if qc.system_datafield == QuestionnaireRow.SystemDatafield.ATTENDEE_NAME_PARTS:
return NamePartsFormField(
max_length=255,
required=qc.required and not self.all_optional,
@@ -1107,7 +1107,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
help_text=rich_text(qc.help_text),
initial=pos.attendee_name_parts,
)
if qc.system_datafield == QuestionnaireChild.SystemDatafield.ATTENDEE_EMAIL:
if qc.system_datafield == QuestionnaireRow.SystemDatafield.ATTENDEE_EMAIL:
return forms.EmailField(
required=qc.required and not self.all_optional,
label=escape(qc.label),
@@ -1119,7 +1119,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
}
)
)
if qc.system_datafield == QuestionnaireChild.SystemDatafield.COMPANY:
if qc.system_datafield == QuestionnaireRow.SystemDatafield.COMPANY:
return forms.CharField(
required=qc.required and not self.all_optional,
label=escape(qc.label),
@@ -1128,7 +1128,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
initial=pos.company,
)
if qc.system_datafield == QuestionnaireChild.SystemDatafield.STREET:
if qc.system_datafield == QuestionnaireRow.SystemDatafield.STREET:
return forms.CharField(
required=qc.required and not self.all_optional,
label=escape(qc.label),
@@ -1140,7 +1140,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
}),
initial=pos.street,
)
if qc.system_datafield == QuestionnaireChild.SystemDatafield.ZIPCODE:
if qc.system_datafield == QuestionnaireRow.SystemDatafield.ZIPCODE:
return forms.CharField(
required=False,
max_length=30,
@@ -1151,7 +1151,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
'autocomplete': 'postal-code',
}),
)
if qc.system_datafield == QuestionnaireChild.SystemDatafield.CITY:
if qc.system_datafield == QuestionnaireRow.SystemDatafield.CITY:
return forms.CharField(
required=False,
label=escape(qc.label),
@@ -1162,7 +1162,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
'autocomplete': 'address-level2',
}),
)
if qc.system_datafield == QuestionnaireChild.SystemDatafield.COUNTRY:
if qc.system_datafield == QuestionnaireRow.SystemDatafield.COUNTRY:
country = pos.country or guess_country_from_request(request, event)
return CountryField(
countries=CachedCountries
@@ -1176,7 +1176,7 @@ class TicketLevelQuestionsForm(BaseQuestionsForm):
'data-trigger-address-info': 'on',
}),
)
if qc.system_datafield == QuestionnaireChild.SystemDatafield.STATE:
if qc.system_datafield == QuestionnaireRow.SystemDatafield.STATE:
country = pos.country or guess_country_from_request(request, event)
c = [('', '---')]
fprefix = str(self.prefix) + '-' if self.prefix is not None and self.prefix != '-' else ''
@@ -52,7 +52,7 @@ def migrate_questions_forward(apps, schema_editor):
Item = apps.get_model("pretixbase", "Item")
Question = apps.get_model("pretixbase", "Question")
Questionnaire = apps.get_model("pretixbase", "Questionnaire")
QuestionnaireChild = apps.get_model("pretixbase", "QuestionnaireChild")
QuestionnaireRow = apps.get_model("pretixbase", "QuestionnaireRow")
EventSettingsStore = apps.get_model('pretixbase', 'Event_SettingsStore')
qc_candidate = namedtuple('qc_candidate', ['item_info', 'question_id', 'question'])
@@ -83,33 +83,33 @@ def migrate_questions_forward(apps, schema_editor):
else:
internal_name = _('personalized products')
# create questionnaires and children
# create questionnaires and rows
questionnaire = Questionnaire.objects.create(
event=event, type=questionnaire_type, position=0, all_sales_channels=True,
internal_name=internal_name
)
questionnaire.items.set([ii.id for ii in item_infos])
deps = {}
for position, child in enumerate(questions):
print(position, child)
if isinstance(child, FakeQuestion):
QuestionnaireChild.objects.create(
for position, question in enumerate(questions):
print(position, question)
if isinstance(question, FakeQuestion):
QuestionnaireRow.objects.create(
questionnaire=questionnaire,
position=position + 1,
system_datafield=child.id,
required=child.required,
label=child.question,
system_datafield=question.id,
required=question.required,
label=question.question,
)
else:
deps[child.id] = QuestionnaireChild.objects.create(
deps[question.id] = QuestionnaireRow.objects.create(
questionnaire=questionnaire,
position=position + 1,
user_datafield=child,
required=child.required,
label=child.question,
help_text=child.help_text,
dependency_question=deps[child.dependency_question.id] if child.dependency_question else None,
dependency_values=child.dependency_values,
user_datafield=question,
required=question.required,
label=question.question,
help_text=question.help_text,
dependency_question=deps[question.dependency_question.id] if question.dependency_question else None,
dependency_values=question.dependency_values,
)
for event in Event.objects.iterator():
@@ -127,7 +127,7 @@ def migrate_questions_forward(apps, schema_editor):
personalized_items = event.items.filter(personalized=True).values_list('id', 'internal_name', 'name', 'personalized', named=True)
# [position_checkout] presale
children = chain((
rows = chain((
qc_candidate(item, 0, q)
for q in get_fake_questions(settings)
for item in personalized_items
@@ -137,45 +137,45 @@ def migrate_questions_forward(apps, schema_editor):
for item in q.items.values_list('id', 'internal_name', 'name', 'personalized', named=True)
))
create_grouped_item_questionnaires(event, children, '', Questionnaire.QuestionnaireType.ORDER_POSITION_SALE, personalized_items)
create_grouped_item_questionnaires(event, rows, '', Questionnaire.QuestionnaireType.ORDER_POSITION_SALE, personalized_items)
# [position_checkin] checkin
children = chain((
rows = chain((
qc_candidate(item, q.id, q)
for q in questions.filter(ask_during_checkin=True)
for item in q.items.values_list('id', 'internal_name', 'name', 'personalized', named=True)
))
create_grouped_item_questionnaires(event, children, _('Check-in questions for '), Questionnaire.QuestionnaireType.ORDER_POSITION_CHECKIN, personalized_items)
create_grouped_item_questionnaires(event, rows, _('Check-in questions for '), Questionnaire.QuestionnaireType.ORDER_POSITION_CHECKIN, personalized_items)
# [position_hidden] hidden
children = chain((
rows = chain((
qc_candidate(item, q.id, q)
for q in questions.filter(hidden=True, ask_during_checkin=False)
for item in q.items.values_list('id', 'internal_name', 'name', 'personalized', named=True)
))
create_grouped_item_questionnaires(event, children, _('Hidden questions for '), Questionnaire.QuestionnaireType.ORDER_POSITION_HIDDEN, personalized_items)
create_grouped_item_questionnaires(event, rows, _('Hidden questions for '), Questionnaire.QuestionnaireType.ORDER_POSITION_HIDDEN, personalized_items)
# [O] get all order-level questions
questions = list(event.questions.filter(container_type='O', hidden=False).order_by('position'))
if questions:
# create questionnaires and children
# create questionnaires and rows
questionnaire = Questionnaire.objects.create(
event=event, type=Questionnaire.QuestionnaireType.ORDER_SALE, position=0, all_sales_channels=True,
internal_name=_('Per-order questions'),
)
deps = {}
for position, child in enumerate(questions):
deps[child.id] = QuestionnaireChild.objects.create(
for position, question in enumerate(questions):
deps[question.id] = QuestionnaireRow.objects.create(
questionnaire=questionnaire,
position=position + 1,
user_datafield=child,
required=child.required,
label=child.question,
help_text=child.help_text,
dependency_question=deps[child.dependency_question.id] if child.dependency_question else None,
dependency_values=child.dependency_values,
user_datafield=question,
required=question.required,
label=question.question,
help_text=question.help_text,
dependency_question=deps[question.dependency_question.id] if question.dependency_question else None,
dependency_values=question.dependency_values,
)
@@ -208,7 +208,7 @@ class Migration(migrations.Migration):
bases=(models.Model, pretix.base.models.base.LoggingMixin),
),
migrations.CreateModel(
name='QuestionnaireChild',
name='QuestionnaireRow',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False)),
('position', models.PositiveIntegerField(default=0)),
@@ -217,8 +217,8 @@ class Migration(migrations.Migration):
('label', i18nfield.fields.I18nTextField()),
('help_text', i18nfield.fields.I18nTextField(null=True)),
('dependency_values', pretix.base.models.fields.MultiStringField(default=[])),
('dependency_question', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dependent_questions', to='pretixbase.questionnairechild')),
('questionnaire', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='children', to='pretixbase.questionnaire')),
('dependency_question', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='dependent_questions', to='pretixbase.questionnairerow')),
('questionnaire', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rows', to='pretixbase.questionnaire')),
('user_datafield', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='references', to='pretixbase.question')),
],
options={
+1 -1
View File
@@ -37,7 +37,7 @@ from .invoices import Invoice, InvoiceLine, invoice_filename
from .items import (
Item, ItemAddOn, ItemBundle, ItemCategory, ItemMetaProperty, ItemMetaValue,
ItemProgramTime, ItemVariation, ItemVariationMetaValue, Question,
Questionnaire, QuestionnaireChild, QuestionOption, Quota, SubEventItem,
Questionnaire, QuestionnaireRow, QuestionOption, Quota, SubEventItem,
SubEventItemVariation, itempicture_upload_to,
)
from .log import LogEntry
+2 -2
View File
@@ -76,7 +76,7 @@ from pretix.helpers.daterange import daterange
from pretix.helpers.hierarkey import clean_filename
from pretix.helpers.json import CustomJSONEncoder, safe_string
from pretix.helpers.thumb import get_thumbnail
from . import QuestionnaireChild
from . import QuestionnaireRow
from ..settings import settings_hierarkey
from .organizer import Organizer, Team
@@ -740,7 +740,7 @@ class Event(EventMixin, LoggedModel):
self.settings.mail_send_download_reminder_attendee = True
from . import Questionnaire
q = self.questionnaires.create(internal_name=str(_('Attendee data')), type=Questionnaire.QuestionnaireType.ORDER_POSITION_SALE, position=0)
q.children.create(label=str(_('Attendee name')), system_datafield=QuestionnaireChild.SystemDatafield.ATTENDEE_NAME_PARTS, position=0)
q.rows.create(label=str(_('Attendee name')), system_datafield=QuestionnaireRow.SystemDatafield.ATTENDEE_NAME_PARTS, position=0)
@property
def social_image(self):
+12 -12
View File
@@ -1680,7 +1680,7 @@ class Question(LoggedModel): # TODO(questionnaires) - rename to Datafield ?
choices=FieldType.choices,
verbose_name=_("Question type")
)
tbd_required = models.BooleanField( # TODO(questionnaires) : to be removed, -> QuestionnaireChild
tbd_required = models.BooleanField( # TODO(questionnaires) : to be removed, -> QuestionnaireRow
default=False,
verbose_name=_("Required question"),
db_column="required",
@@ -1692,7 +1692,7 @@ class Question(LoggedModel): # TODO(questionnaires) - rename to Datafield ?
# blank=True,
# help_text=_('This question will be asked to buyers of the selected products')
# )
tbd_position = models.PositiveIntegerField( # TODO(questionnaires) : to be removed, -> Questionnaire + QuestionnaireChild
tbd_position = models.PositiveIntegerField( # TODO(questionnaires) : to be removed, -> Questionnaire + QuestionnaireRow
default=0,
verbose_name=_("Position"),
db_column="position",
@@ -1717,10 +1717,10 @@ class Question(LoggedModel): # TODO(questionnaires) - rename to Datafield ?
verbose_name=_('Print answer on invoices'),
default=False
)
dependency_question = models.ForeignKey( # TODO(questionnaires) : to be removed, -> QuestionnaireChild
dependency_question = models.ForeignKey( # TODO(questionnaires) : to be removed, -> QuestionnaireRow
'Question', null=True, blank=True, on_delete=models.SET_NULL, related_name='dependent_questions'
)
dependency_values = MultiStringField(default=[]) # TODO(questionnaires) : to be removed, -> QuestionnaireChild
dependency_values = MultiStringField(default=[]) # TODO(questionnaires) : to be removed, -> QuestionnaireRow
valid_number_min = models.DecimalField(decimal_places=6, max_digits=30, null=True, blank=True,
verbose_name=_('Minimum value'),
help_text=_('Currently not supported in our apps and during check-in'))
@@ -2066,14 +2066,14 @@ class Questionnaire(LoggedModel):
@staticmethod
def check_constraints(event):
errors = set()
all_questionnaires = Questionnaire.objects.filter(event=event).prefetch_related('children', 'items')
all_questionnaires = Questionnaire.objects.filter(event=event).prefetch_related('rows', 'items')
all_sales_channels = event.sales_channels.all()
for (sales_channel, type, item_id, sdf, udf), fields in groupby(sorted(
(sales_channel, q.type, item.id, c.system_datafield or '', c.user_datafield_id or 0, q, item)
for q in all_questionnaires
for sales_channel in (q.limit_sales_channels if not q.all_sales_channels else all_sales_channels).values_list('pk', flat=True)
for item in q.items.all()
for c in q.children.all()
for c in q.rows.all()
), key=lambda d: (d[0], d[1], d[2], d[3], d[4])):
fields = list(fields)
if len(fields) > 1:
@@ -2092,7 +2092,7 @@ class Questionnaire(LoggedModel):
raise ValidationError(list(errors))
class QuestionnaireChild(LoggedModel):
class QuestionnaireRow(LoggedModel):
class SystemDatafield(models.TextChoices):
ATTENDEE_NAME_PARTS = '@attendee_name_parts', _('Attendee name')
ATTENDEE_EMAIL = '@attendee_email', _('Attendee email')
@@ -2122,7 +2122,7 @@ class QuestionnaireChild(LoggedModel):
questionnaire = models.ForeignKey(
Questionnaire,
related_name="children",
related_name="rows",
on_delete=models.CASCADE
)
position = models.PositiveIntegerField(
@@ -2153,7 +2153,7 @@ class QuestionnaireChild(LoggedModel):
null=True, blank=True,
)
dependency_question = models.ForeignKey(
'QuestionnaireChild', null=True, blank=True, on_delete=models.SET_NULL, related_name='dependent_questions'
'pretix.base.models.items.QuestionnaireRow', null=True, blank=True, on_delete=models.SET_NULL, related_name='dependent_questions'
)
dependency_values = MultiStringField(default=[])
@@ -2172,13 +2172,13 @@ class QuestionnaireChild(LoggedModel):
@property
def cooked_id(self):
if self.user_datafield_id or self.system_datafield:
return QuestionnaireChild.cook_id_from_parts(self.questionnaire_id, self.user_datafield_id, self.system_datafield)
return QuestionnaireRow.cook_id_from_parts(self.questionnaire_id, self.user_datafield_id, self.system_datafield)
else:
return None
@staticmethod
def cook_id_from_parts(qid, dfid, sys_df):
df_id = dfid + 100 if dfid else QuestionnaireChild.SYSTEM_DATAFIELD_NUMBERS.index(sys_df)
df_id = dfid + 100 if dfid else QuestionnaireRow.SYSTEM_DATAFIELD_NUMBERS.index(sys_df)
assert qid < 1000000000
return qid * 1000000000 + df_id
@@ -2190,7 +2190,7 @@ class QuestionnaireChild(LoggedModel):
if dfid >= 100:
return qid, dfid - 100, None
else:
return qid, None, QuestionnaireChild.SYSTEM_DATAFIELD_NUMBERS[dfid]
return qid, None, QuestionnaireRow.SYSTEM_DATAFIELD_NUMBERS[dfid]
else:
return None, id, None
+25 -25
View File
@@ -96,7 +96,7 @@ from ._transactions import (
from .base import LockModel, LoggedModel
from .event import Event, SubEvent
from .items import (
Item, ItemVariation, Question, QuestionnaireChild, QuestionOption, Quota,
Item, ItemVariation, Question, QuestionnaireRow, QuestionOption, Quota,
)
logger = logging.getLogger(__name__)
@@ -1608,37 +1608,37 @@ class AbstractPosition(RoundingCorrectionMixin, models.Model):
# answers of other items in the same cart if the question objects have been
# selected via prefetch_related
if hasattr(self.item, 'relevant_questionnaires'):
children = list(copy.copy(qc) for qq in self.item.relevant_questionnaires for qc in qq.childlist)
qrows = list(copy.copy(qc) for qq in self.item.relevant_questionnaires for qc in qq.questionnairerowlist)
else:
children = QuestionnaireChild.objects.filter(questionnaire__event_id=self.item.event_id, questionnaire__items__in=[self.item.id])
qrows = QuestionnaireRow.objects.filter(questionnaire__event_id=self.item.event_id, questionnaire__items__in=[self.item.id])
if questionnaire_type is not None:
children = children.filter(questionnaire__type=questionnaire_type)
qrows = qrows.filter(questionnaire__type=questionnaire_type)
if sales_channel is not None:
children = children.filter(Q(questionnaire__all_sales_channels=True) | Q(questionnaire__limit_sales_channels=sales_channel))
qrows = qrows.filter(Q(questionnaire__all_sales_channels=True) | Q(questionnaire__limit_sales_channels=sales_channel))
children = list(children)
qrows = list(qrows)
qc_cache = {
q.pk: q for q in children
row_cache = {
q.pk: q for q in qrows
}
def qc_is_visible(parentid, qvals):
if parentid not in qc_cache:
def row_is_visible(parentid, qvals):
if parentid not in row_cache:
return False
parentqc = qc_cache[parentid]
if parentqc.dependency_question_id and not qc_is_visible(parentqc.dependency_question_id, parentqc.dependency_values):
parentrow = row_cache[parentid]
if parentrow.dependency_question_id and not row_is_visible(parentrow.dependency_question_id, parentrow.dependency_values):
return False
answer_values = self.get_dependency_answer_values(parentqc)
answer_values = self.get_dependency_answer_values(parentrow)
return answer_values and any(qval in answer_values for qval in qvals)
self.questions = []
for qc in children:
for qrow in qrows:
try:
qc.answer = self.answer_cache[qc.user_datafield_id or qc.system_datafield]
qrow.answer = self.answer_cache[qrow.user_datafield_id or qrow.system_datafield]
except KeyError:
continue
if not qc.dependency_question_id or qc_is_visible(qc.dependency_question_id, qc.dependency_values):
self.questions.append(qc)
if not qrow.dependency_question_id or row_is_visible(qrow.dependency_question_id, qrow.dependency_values):
self.questions.append(qrow)
return self.questions
@cached_property
@@ -1667,21 +1667,21 @@ class AbstractPosition(RoundingCorrectionMixin, models.Model):
raise ValueError('Questionnaire child without datafield has no answer')
def get_system_answer(self, system_datafield_name):
if system_datafield_name == QuestionnaireChild.SystemDatafield.ATTENDEE_NAME_PARTS:
if system_datafield_name == QuestionnaireRow.SystemDatafield.ATTENDEE_NAME_PARTS:
return self.attendee_name
elif system_datafield_name == QuestionnaireChild.SystemDatafield.ATTENDEE_EMAIL:
elif system_datafield_name == QuestionnaireRow.SystemDatafield.ATTENDEE_EMAIL:
return self.attendee_email
elif system_datafield_name == QuestionnaireChild.SystemDatafield.COMPANY:
elif system_datafield_name == QuestionnaireRow.SystemDatafield.COMPANY:
return self.company
elif system_datafield_name == QuestionnaireChild.SystemDatafield.STREET:
elif system_datafield_name == QuestionnaireRow.SystemDatafield.STREET:
return self.street
elif system_datafield_name == QuestionnaireChild.SystemDatafield.ZIPCODE:
elif system_datafield_name == QuestionnaireRow.SystemDatafield.ZIPCODE:
return self.zipcode
elif system_datafield_name == QuestionnaireChild.SystemDatafield.CITY:
elif system_datafield_name == QuestionnaireRow.SystemDatafield.CITY:
return self.city
elif system_datafield_name == QuestionnaireChild.SystemDatafield.STATE:
elif system_datafield_name == QuestionnaireRow.SystemDatafield.STATE:
return self.state
elif system_datafield_name == QuestionnaireChild.SystemDatafield.COUNTRY:
elif system_datafield_name == QuestionnaireRow.SystemDatafield.COUNTRY:
return str(self.country)
else:
raise ValueError('Unknown system datafield name')
+1 -1
View File
@@ -1410,7 +1410,7 @@ class CartManager:
for k, v in self._widget_data.items():
if not k.startswith('question-'):
continue
# TODO(questionnaires) - switch to QuestionnaireChild
# TODO(questionnaires) - switch to QuestionnaireRow
q = cp.item.questions.filter(ask_during_checkin=False, identifier__iexact=k[9:]).first()
if q:
try:
+2 -2
View File
@@ -55,7 +55,7 @@ from django_scopes import scope, scopes_disabled
from pretix.base.models import (
Checkin, CheckinList, Device, Event, Gate, Item, ItemVariation, Order,
OrderPosition, Questionnaire, QuestionnaireChild, QuestionOption,
OrderPosition, Questionnaire, QuestionnaireRow, QuestionOption,
)
from pretix.base.signals import checkin_created, periodic_task
from pretix.helpers import OF_SELF
@@ -1033,7 +1033,7 @@ def perform_checkin(op: OrderPosition, clist: CheckinList, given_answers: dict,
# Do this outside of transaction so it is saved even if the checkin fails for some other reason
checkin_questions = list(
QuestionnaireChild.objects.filter(
QuestionnaireRow.objects.filter(
questionnaire__event=clist.event, questionnaire__type=Questionnaire.QuestionnaireType.ORDER_POSITION_CHECKIN,
questionnaire__items__in=[op.item_id],
).select_related('user_datafield', 'questionnaire')
+3 -3
View File
@@ -37,7 +37,7 @@ from pretix.base.forms.questions import (
)
from pretix.base.models import (
CartPosition, InvoiceAddress, OrderPosition, Question, QuestionAnswer,
Questionnaire, QuestionnaireChild, QuestionOption,
Questionnaire, QuestionnaireRow, QuestionOption,
)
from pretix.base.models.customers import AttendeeProfile
from pretix.base.models.orders import CheckoutSession, Order
@@ -364,7 +364,7 @@ class OrderQuestionsViewMixin(BaseQuestionsViewMixin):
to_attr='answerlist'),
Prefetch('item__questionnaires',
qqs.prefetch_related(
Prefetch('children', QuestionnaireChild.objects.prefetch_related(
Prefetch('rows', QuestionnaireRow.objects.prefetch_related(
Prefetch('user_datafield', Question.objects.prefetch_related(
Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch(
# This prefetch statement is utter bullshit, but it actually prevents Django from doing
@@ -376,7 +376,7 @@ class OrderQuestionsViewMixin(BaseQuestionsViewMixin):
)))
))
),
to_attr='childlist')
to_attr='questionnairerowlist')
),
to_attr='relevant_questionnaires')
))
+2 -2
View File
@@ -67,7 +67,7 @@ from pretix.api.serializers.item import (
from pretix.base.forms import I18nFormSet
from pretix.base.models import (
CartPosition, Item, ItemCategory, ItemProgramTime, ItemVariation, LogEntry,
OrderPosition, Question, QuestionAnswer, QuestionnaireChild,
OrderPosition, Question, QuestionAnswer, QuestionnaireRow,
QuestionOption, Quota, SeatCategoryMapping, Voucher,
)
from pretix.base.models.event import SubEvent
@@ -720,7 +720,7 @@ class QuestionnairesEditor(EventPermissionRequiredMixin, TemplateView):
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['questionnaire_type_choices'] = textchoices_to_json(Questionnaire.QuestionnaireType, self.request.event)
ctx['system_question_choices'] = textchoices_to_json(QuestionnaireChild.SystemDatafield, self.request.event)
ctx['system_question_choices'] = textchoices_to_json(QuestionnaireRow.SystemDatafield, self.request.event)
ctx['question_type_choices'] = textchoices_to_json(Question.FieldType, self.request.event)
return ctx
+2 -2
View File
@@ -1093,7 +1093,7 @@ class QuestionsStep(CartQuestionsViewMixin, CartMixin, TemplateFlowStep):
for cp in self._positions_for_questions:
qc_cache = {
qc.pk: qc for qq in cp.item.relevant_questionnaires for qc in qq.childlist
qc.pk: qc for qq in cp.item.relevant_questionnaires for qc in qq.questionnairerowlist
}
def question_is_visible(parentid, qvals):
@@ -1113,7 +1113,7 @@ class QuestionsStep(CartQuestionsViewMixin, CartMixin, TemplateFlowStep):
if not self.all_optional:
for qq in cp.item.relevant_questionnaires:
for qc in qq.childlist:
for qc in qq.questionnairerowlist:
if qc.user_datafield_id and question_is_required(qc) and qc.user_datafield_id not in cp.answer_cache:
if warn:
messages.warning(request, _('Please fill in answers to all required questions.'))
+3 -3
View File
@@ -54,7 +54,7 @@ from pretix.base.models import (
CartPosition, Customer, InvoiceAddress, ItemAddOn, OrderFee, Question,
QuestionAnswer, Questionnaire, QuestionOption, TaxRule,
)
from pretix.base.models.items import QuestionnaireChild
from pretix.base.models.items import QuestionnaireRow
from pretix.base.models.orders import CheckoutSession
from pretix.base.services.cart import get_fees
from pretix.base.services.pricing import apply_rounding
@@ -435,7 +435,7 @@ def get_cart_positions(request):
to_attr='answerlist'),
Prefetch('item__questionnaires',
qqs.prefetch_related(
Prefetch('children', QuestionnaireChild.objects.prefetch_related(
Prefetch('rows', QuestionnaireRow.objects.prefetch_related(
Prefetch('user_datafield', Question.objects.prefetch_related(
Prefetch('options', QuestionOption.objects.prefetch_related(Prefetch(
# This prefetch statement is utter bullshit, but it actually prevents Django from doing
@@ -447,7 +447,7 @@ def get_cart_positions(request):
)))
))
),
to_attr='childlist')
to_attr='questionnairerowlist')
),
to_attr='relevant_questionnaires')
)
@@ -42,7 +42,7 @@ function saveQuestionnaire(questionnaire) {
}
result = result.then(d => {
questionnaire.id = d.id
questionnaire.children = d.children
questionnaire.rows = d.rows
questionnaire._err_mes = null
questionnaire._loading = false
return d;
@@ -55,14 +55,14 @@ function saveQuestionnaire(questionnaire) {
}
function addPositionQuestionnaire () {
position_questionnaires.value.push({
all_sales_channels: true, children: [], limit_sales_channels: [], position: position_questionnaires.value.length,
all_sales_channels: true, rows: [], limit_sales_channels: [], position: position_questionnaires.value.length,
items: [], internal_name: "Unnamed questionnaire", type: "position_purchase",
_new_id: Date.now(),
});
}
function addOrderQuestionnaire () {
order_questionnaires.value.push({
all_sales_channels: true, children: [], limit_sales_channels: [], position: order_questionnaires.value.length,
all_sales_channels: true, rows: [], limit_sales_channels: [], position: order_questionnaires.value.length,
items: [], internal_name: "Unnamed questionnaire", type: "order_purchase",
_new_id: Date.now(),
});
@@ -34,14 +34,14 @@ const emit = defineEmits(['update', 'invalidate:datafields', 'invalidate:questio
const sales_channels: Ref<SalesChannel[]> = inject('pretix:env:organizer:sales_channels')
let nextId = 1;
watch(() => props.questionnaire.children, () => {
for (let qc of props.questionnaire.children) {
watch(() => props.questionnaire.rows, () => {
for (let qc of props.questionnaire.rows) {
if (!qc._cid) qc._cid = id + (++nextId);
}
})
function addExistingDatafield (field) {
props.questionnaire.children.push({
props.questionnaire.rows.push({
_cid: useId(),
datafield: field.identifier,
required: false,
@@ -61,7 +61,7 @@ function showAddTextblockDialog () {
}
function addTextblock () {
props.questionnaire.children.push({
props.questionnaire.rows.push({
_cid: useId(),
datafield: null,
required: false,
@@ -163,14 +163,14 @@ const isEditable = computed(() => props.selected_product && props.questionnaire.
<p v-else>{{ questionnaire._err_mes }}</p>
</div>
<div class="form-horizontal" :id="`questionListParent${props.questionnaire.id}`">
<SlickList axis="y" v-model:list="props.questionnaire.children" useDragHandle :appendTo="`#questionListParent${props.questionnaire.id}`" @update:list="emit('update')">
<SlickItem v-for="(child, index) in props.questionnaire.children" :key="child._cid" :index="index">
<SlickList axis="y" v-model:list="props.questionnaire.rows" useDragHandle :appendTo="`#questionListParent${props.questionnaire.id}`" @update:list="emit('update')">
<SlickItem v-for="(child, index) in props.questionnaire.rows" :key="child._cid" :index="index">
<QuestionElement
:datafields="props.datafields"
:question="child"
:editable="true"
:possible_dependencies="props.questionnaire.children.slice(0, index)"
@remove-self="questionnaire.children.splice(index, 1); emit('update')"
:possible_dependencies="props.questionnaire.rows.slice(0, index)"
@remove-self="questionnaire.rows.splice(index, 1); emit('update')"
@update="emit('update')"
@invalidate:datafields="emit('invalidate:datafields')"/>
</SlickItem>
@@ -45,10 +45,10 @@ export type Questionnaire = {
position: number,
all_sales_channels: boolean,
limit_sales_channels: string[],
children: QuestionnaireChild[],
rows: QuestionnaireRow[],
}
export type QuestionnaireChild = {
export type QuestionnaireRow = {
datafield: string | null,
required: boolean,
label: I18nString,