mirror of
https://github.com/pretix/pretix.git
synced 2026-10-01 19:14:43 +00:00
pretixPOS API compat
This commit is contained in:
@@ -995,6 +995,7 @@ class DeviceEventSettingsSerializer(EventSettingsSerializer):
|
||||
else []
|
||||
)
|
||||
)
|
||||
self.fields['system_question_order'] = serializers.JSONField(read_only=True, default=dict(), source='_invalid')
|
||||
|
||||
|
||||
class MultiLineStringField(serializers.Field):
|
||||
|
||||
@@ -551,6 +551,8 @@ class LegacyDependencyValueField(serializers.CharField):
|
||||
|
||||
|
||||
|
||||
#name_schemes_all_fields = sorted(list(set(k for scheme in PERSON_NAME_SCHEMES.values() for k, v, w, *__ in scheme['fields'])))
|
||||
name_schemes_all_fields = ['calling_name', 'degree', 'family_name', 'full_name', 'given_name', 'latin_transcription', 'middle_name', 'salutation', 'title']
|
||||
system_datafield_numbers = [
|
||||
QuestionnaireChild.SystemQuestion.ATTENDEE_NAME_PARTS.value,
|
||||
QuestionnaireChild.SystemQuestion.ATTENDEE_EMAIL.value,
|
||||
@@ -560,13 +562,19 @@ system_datafield_numbers = [
|
||||
QuestionnaireChild.SystemQuestion.CITY.value,
|
||||
QuestionnaireChild.SystemQuestion.STATE.value,
|
||||
QuestionnaireChild.SystemQuestion.COUNTRY.value,
|
||||
] + [
|
||||
QuestionnaireChild.SystemQuestion.ATTENDEE_NAME_PARTS.value + ':' + field for field in name_schemes_all_fields
|
||||
]
|
||||
|
||||
|
||||
def cook_id(questionnaire_child):
|
||||
df_id = questionnaire_child.user_datafield_id + 100 if questionnaire_child.user_datafield_id else system_datafield_numbers.index(questionnaire_child.system_datafield)
|
||||
assert questionnaire_child.questionnaire_id < 1000000000
|
||||
return questionnaire_child.questionnaire_id * 1000000000 + df_id
|
||||
return cook_id_from_parts(questionnaire_child.questionnaire_id, questionnaire_child.user_datafield_id, questionnaire_child.system_datafield)
|
||||
|
||||
|
||||
def cook_id_from_parts(qid, dfid, sys_df):
|
||||
df_id = dfid + 100 if dfid else system_datafield_numbers.index(sys_df)
|
||||
assert qid < 1000000000
|
||||
return qid * 1000000000 + df_id
|
||||
|
||||
|
||||
def uncook_id(id):
|
||||
@@ -584,7 +592,7 @@ def uncook_id(id):
|
||||
class CompatQuestionSerializer(I18nAwareModelSerializer):
|
||||
id = serializers.SerializerMethodField('cook_id', read_only=True)
|
||||
question = I18nField(source='label', allow_null=True)
|
||||
type = serializers.CharField(source='user_datafield.type', allow_null=True, default=Question.FieldType.TEXT)
|
||||
type = serializers.CharField(source='user_datafield.type', allow_null=True, default=Question.FieldType.STRING)
|
||||
#identifier = serializers.CharField(source='user_datafield.identifier', allow_null=True)
|
||||
identifier = serializers.SerializerMethodField('get_identifier', read_only=True)
|
||||
show_during_checkin = serializers.BooleanField(source='user_datafield.show_during_checkin', allow_null=True, default=False)
|
||||
@@ -604,6 +612,7 @@ class CompatQuestionSerializer(I18nAwareModelSerializer):
|
||||
dependency_value = serializers.SerializerMethodField('empty', read_only=True)
|
||||
position = serializers.SerializerMethodField('get_position', read_only=True)
|
||||
items = serializers.PrimaryKeyRelatedField(source='questionnaire.items', many=True, queryset=Item.objects.none(), allow_null=True, required=False)
|
||||
questionnaire_id = serializers.IntegerField(source='questionnaire.id', read_only=True)
|
||||
|
||||
options = InlineQuestionOptionSerializer(source='user_datafield.options', many=True, required=False, default=[])
|
||||
|
||||
@@ -613,7 +622,7 @@ class CompatQuestionSerializer(I18nAwareModelSerializer):
|
||||
'ask_during_checkin', 'show_during_checkin', 'identifier', 'dependency_question', 'dependency_values',
|
||||
'hidden', 'dependency_value', 'print_on_invoice', 'help_text', 'valid_number_min',
|
||||
'valid_number_max', 'valid_date_min', 'valid_date_max', 'valid_datetime_min', 'valid_datetime_max',
|
||||
'valid_string_length_max', 'valid_string_length_min', 'valid_file_portrait')
|
||||
'valid_string_length_max', 'valid_string_length_min', 'valid_file_portrait', 'questionnaire_id')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.fields['items'].child_relation.queryset = kwargs['context']['event'].items.all()
|
||||
|
||||
@@ -48,7 +48,8 @@ from pretix.api.serializers.event import SubEventSerializer
|
||||
from pretix.api.serializers.forms import form_field_to_serializer_field
|
||||
from pretix.api.serializers.i18n import I18nAwareModelSerializer
|
||||
from pretix.api.serializers.item import (
|
||||
InlineItemVariationSerializer, ItemSerializer, DatafieldSerializer, cook_id, system_datafield_numbers, uncook_id,
|
||||
InlineItemVariationSerializer, ItemSerializer, DatafieldSerializer, cook_id, cook_id_from_parts,
|
||||
system_datafield_numbers, uncook_id,
|
||||
)
|
||||
from pretix.api.signals import order_api_details, orderposition_api_details
|
||||
from pretix.base.decimal import round_decimal
|
||||
@@ -75,7 +76,8 @@ from pretix.base.services.pricing import (
|
||||
)
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.base.settings import (
|
||||
COUNTRIES_WITH_STATE_IN_ADDRESS, ROUNDING_MODES,
|
||||
COUNTRIES_WITH_STATE_IN_ADDRESS, PERSON_NAME_SALUTATIONS, PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS,
|
||||
ROUNDING_MODES,
|
||||
)
|
||||
from pretix.base.signals import register_ticket_outputs
|
||||
from pretix.helpers.countries import CachedCountries
|
||||
@@ -240,23 +242,58 @@ class MixedAnswerListSerializer(ListSerializer):
|
||||
|
||||
def to_representation(self, instance):
|
||||
answers = super().to_representation(instance.answers)
|
||||
for qc in QuestionnaireChild.objects.filter(questionnaire__items__id=instance.item_id, system_datafield__isnull=False):
|
||||
answers.append({
|
||||
"question": cook_id(qc),
|
||||
"answer": instance.get_system_answer(qc.system_datafield),
|
||||
"question_identifier": f'@{qc.system_datafield}',
|
||||
"options": [],
|
||||
"option_identifiers": [],
|
||||
"questionnaire_id": qc.questionnaire_id,
|
||||
"system_datafield": qc.system_datafield,
|
||||
})
|
||||
return {
|
||||
"answers": answers,
|
||||
}
|
||||
answers_dict = {answer["question"]: answer for answer in answers}
|
||||
for qc in QuestionnaireChild.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)
|
||||
answer["question"] = cook_id(qc)
|
||||
answer["questionnaire_id"] = qc.questionnaire_id
|
||||
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 == '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']:
|
||||
if k in name_parts:
|
||||
options = []
|
||||
options_ident = []
|
||||
try:
|
||||
if k == "salutation":
|
||||
salutation_keys = [key for key, value in PERSON_NAME_SALUTATIONS]
|
||||
options = [salutation_keys.index(name_parts[k]) + 1]
|
||||
options_ident = [name_parts[k]]
|
||||
elif k == "title" and settings.name_scheme_titles:
|
||||
options = [PERSON_NAME_TITLE_GROUPS.get(settings.name_scheme_titles)[1].index(name_parts[k]) + 1]
|
||||
options_ident = [name_parts[k]]
|
||||
except ValueError:
|
||||
pass
|
||||
answers.append({
|
||||
"question": 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,
|
||||
"option_identifiers": options_ident,
|
||||
"questionnaire_id": qc.questionnaire_id,
|
||||
"system_datafield": f'{qc.system_datafield}:{k}',
|
||||
})
|
||||
answers.append({
|
||||
"question": cook_id(qc),
|
||||
"answer": sys_answer,
|
||||
"question_identifier": f'@{qc.system_datafield}',
|
||||
"options": [],
|
||||
"option_identifiers": [],
|
||||
"questionnaire_id": qc.questionnaire_id,
|
||||
"system_datafield": qc.system_datafield,
|
||||
})
|
||||
return answers
|
||||
|
||||
def to_internal_value(self, data):
|
||||
result = {}
|
||||
answers = []
|
||||
attendee_name_parts = {}
|
||||
settings = self.context['event'].settings
|
||||
for answer in data["answers"]:
|
||||
try:
|
||||
qid, dfid, sys_df = uncook_id(answer["question"])
|
||||
@@ -264,13 +301,23 @@ class MixedAnswerListSerializer(ListSerializer):
|
||||
if type(answer["question"]) is str and answer["question"].lstrip("@") in system_datafield_numbers:
|
||||
qid, dfid, sys_df = None, None, answer["question"].lstrip("@")
|
||||
else:
|
||||
raise
|
||||
raise ValidationError('Invalid question ID')
|
||||
if dfid:
|
||||
answer["question"] = dfid
|
||||
answers.append(answer)
|
||||
elif sys_df == "country":
|
||||
result[sys_df] = Country(answer["answer"])
|
||||
elif sys_df == "attendee_name_parts:salutation":
|
||||
attendee_name_parts['salutation'] = PERSON_NAME_SALUTATIONS[answer["options"][0] - 1][0]
|
||||
elif sys_df == "attendee_name_parts:title" and settings.name_scheme_titles:
|
||||
attendee_name_parts['title'] = PERSON_NAME_TITLE_GROUPS.get(settings.name_scheme_titles)[1][answer["options"][0] - 1]
|
||||
elif sys_df.startswith("attendee_name_parts:"):
|
||||
attendee_name_parts[sys_df[len("attendee_name_parts:"):]] = answer["answer"]
|
||||
else:
|
||||
result[sys_df] = Country(answer["answer"]) if sys_df == "country" else answer["answer"]
|
||||
result[sys_df] = answer["answer"]
|
||||
result["answers"] = super().to_internal_value(answers)
|
||||
if attendee_name_parts:
|
||||
result["attendee_name_parts"] = attendee_name_parts
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ from django.shortcuts import get_object_or_404
|
||||
from django.utils.functional import cached_property
|
||||
from django_filters.rest_framework import DjangoFilterBackend, FilterSet
|
||||
from django_scopes import scopes_disabled
|
||||
from i18nfield.strings import LazyI18nString
|
||||
from rest_framework import viewsets
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.exceptions import PermissionDenied, ValidationError
|
||||
@@ -49,15 +50,18 @@ from pretix.api.serializers.item import (
|
||||
CompatQuestionSerializer, ItemAddOnSerializer, ItemBundleSerializer, ItemCategorySerializer,
|
||||
ItemProgramTimeSerializer, ItemSerializer, ItemVariationSerializer,
|
||||
QuestionnaireSerializer, QuestionOptionSerializer, DatafieldSerializer,
|
||||
QuotaSerializer, uncook_id,
|
||||
QuotaSerializer, cook_id_from_parts, uncook_id,
|
||||
)
|
||||
from pretix.api.views import ConditionalListView
|
||||
from pretix.base.forms.questions import REQUIRED_NAME_PARTS
|
||||
from pretix.base.models import (
|
||||
CartPosition, Item, ItemAddOn, ItemBundle, ItemCategory, ItemProgramTime,
|
||||
ItemVariation, Question, QuestionOption, Quota,
|
||||
)
|
||||
from pretix.base.models.items import Questionnaire, QuestionnaireChild
|
||||
from pretix.base.services.quotas import QuotaAvailability
|
||||
from pretix.base.settings import PERSON_NAME_SALUTATIONS, PERSON_NAME_SCHEMES, PERSON_NAME_TITLE_GROUPS
|
||||
from pretix.control.views.item import i18n_all_from_gettext
|
||||
from pretix.helpers.dicts import merge_dicts
|
||||
from pretix.helpers.i18n import i18ncomp
|
||||
|
||||
@@ -497,6 +501,38 @@ class CompatQuestionViewSet(ConditionalListView, viewsets.ModelViewSet):
|
||||
questionnaire__type__startswith='P',
|
||||
).prefetch_related('user_datafield', 'user_datafield__options', 'questionnaire').all()
|
||||
|
||||
def list(self, request, **kwargs):
|
||||
resp = super().list(request, **kwargs)
|
||||
mangled_results = []
|
||||
pos_offset = 0
|
||||
for row in resp.data['results']:
|
||||
row['position'] += pos_offset
|
||||
if row['identifier'] == '@attendee_name_parts':
|
||||
settings = self.request.event.settings
|
||||
for k, v, w, *__ in PERSON_NAME_SCHEMES.get(settings.name_scheme)['fields']:
|
||||
part = dict(row)
|
||||
part["identifier"] += ":" + k
|
||||
part["question"] = i18n_all_from_gettext(v, settings.locales)
|
||||
if k == "title" and settings.name_scheme_titles:
|
||||
part["type"] = Question.FieldType.CHOICE
|
||||
part["options"] = [{"id": idx + 1, "identifier": k, "answer": i18n_all_from_gettext(k, settings.locales), "position": idx + 1} for idx, k in
|
||||
enumerate(PERSON_NAME_TITLE_GROUPS.get(settings.name_scheme_titles)[1])]
|
||||
elif k == "salutation":
|
||||
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"] = cook_id_from_parts(row['questionnaire_id'], None, part["identifier"][1:])
|
||||
part["required"] = part["required"] and k in REQUIRED_NAME_PARTS
|
||||
mangled_results.append(part)
|
||||
pos_offset += 1
|
||||
row["position"] += 1
|
||||
else:
|
||||
if row['identifier'] == '@country':
|
||||
row['type'] = Question.FieldType.COUNTRYCODE
|
||||
mangled_results.append(row)
|
||||
resp.data['results'] = mangled_results
|
||||
return resp
|
||||
|
||||
def get_object(self):
|
||||
"""
|
||||
Returns the object the view is displaying.
|
||||
|
||||
@@ -708,10 +708,14 @@ class QuestionCreate(EventPermissionRequiredMixin, QuestionMixin, CreateView):
|
||||
|
||||
|
||||
def textchoices_to_json(choices, event):
|
||||
return [(c.name, c.value, i18n_all(event.settings.locales, LazyI18nString.from_gettext(c.label).data)) for c in choices]
|
||||
return [(c.name, c.value, i18n_all_from_gettext(c.label, event.settings.locales)) for c in choices]
|
||||
|
||||
|
||||
def i18n_all(locales, data):
|
||||
def i18n_all_from_gettext(gettext_str, locales):
|
||||
return i18n_all(LazyI18nString.from_gettext(gettext_str).data, locales)
|
||||
|
||||
|
||||
def i18n_all(data, locales):
|
||||
out = {}
|
||||
for locale in locales:
|
||||
out[locale] = data[locale]
|
||||
|
||||
Reference in New Issue
Block a user