diff --git a/src/tixlbase/models.py b/src/tixlbase/models.py index 195237a3a..f5a698158 100644 --- a/src/tixlbase/models.py +++ b/src/tixlbase/models.py @@ -538,9 +538,10 @@ class ItemVariation(models.Model): ) active = models.BooleanField( default=True, + verbose_name=_("Active"), ) default_price = models.DecimalField( decimal_places=2, max_digits=7, null=True, blank=True, - verbose_name=_("Default price") + verbose_name=_("Default price"), ) diff --git a/src/tixlcontrol/static/tixlcontrol/less/forms.less b/src/tixlcontrol/static/tixlcontrol/less/forms.less new file mode 100644 index 000000000..46581d7a3 --- /dev/null +++ b/src/tixlcontrol/static/tixlcontrol/less/forms.less @@ -0,0 +1,16 @@ +td > .form-group { + margin-bottom: 0; +} +td > .errorlist { + color: #a94442; + list-style-type: none; + padding: 0; + margin: 0; +} +td > .form-group > .checkbox { + margin-top: 0; + margin-bottom: 0; +} +.has-success .form-control { + border-color: #cccccc; +} diff --git a/src/tixlcontrol/static/tixlcontrol/less/main.less b/src/tixlcontrol/static/tixlcontrol/less/main.less index d056d1fe2..001ae5cfc 100644 --- a/src/tixlcontrol/static/tixlcontrol/less/main.less +++ b/src/tixlcontrol/static/tixlcontrol/less/main.less @@ -1,3 +1,4 @@ @import "../../../../tixlbase/static/bootstrap/less/bootstrap.less"; @import "../../../../tixlbase/static/fontawesome/less/font-awesome.less"; @fa-font-path: "../../fontawesome/fonts"; +@import "forms.less"; diff --git a/src/tixlcontrol/templates/tixlcontrol/item/base.html b/src/tixlcontrol/templates/tixlcontrol/item/base.html index e618edf25..6f388a890 100644 --- a/src/tixlcontrol/templates/tixlcontrol/item/base.html +++ b/src/tixlcontrol/templates/tixlcontrol/item/base.html @@ -5,6 +5,7 @@

{% trans "Modify item:" %} {{ item.name }}

{% block inside %} {% endblock %} diff --git a/src/tixlcontrol/templates/tixlcontrol/item/variations_1d.html b/src/tixlcontrol/templates/tixlcontrol/item/variations_1d.html new file mode 100644 index 000000000..a8321e696 --- /dev/null +++ b/src/tixlcontrol/templates/tixlcontrol/item/variations_1d.html @@ -0,0 +1,36 @@ +{% extends "tixlcontrol/item/base.html" %} +{% load i18n %} +{% load bootstrap3 %} +{% block inside %} +

{% trans "Variations" %}

+
+ {% csrf_token %} + + + + + + + + + + {% for form in forms %} + {% bootstrap_form_errors form type='all' layout='inline' %} + + + + + + {% endfor %} + +
{{ properties.0 }}{% trans "Active" %}{% trans "Price" %}
{{ form.values.0 }}{% bootstrap_field form.active layout='inline' %}{% bootstrap_field form.default_price layout='inline' %} {{ form.default_price.errors }}
+
+
+ +
+
+
+ +{% endblock %} diff --git a/src/tixlcontrol/urls.py b/src/tixlcontrol/urls.py index 7f68a9e76..c3b048447 100644 --- a/src/tixlcontrol/urls.py +++ b/src/tixlcontrol/urls.py @@ -21,6 +21,7 @@ urlpatterns += patterns( url(r'^settings$', event.EventUpdate.as_view(), name='event.settings'), url(r'^items$', item.ItemList.as_view(), name='event.items'), url(r'^items/(?P\d+)/$', item.ItemUpdateGeneral.as_view(), name='event.item'), + url(r'^items/(?P\d+)/variations$', item.ItemVariations.as_view(), name='event.item.variations'), url(r'^categories$', item.CategoryList.as_view(), name='event.items.categories'), url(r'^properties$', item.PropertyList.as_view(), name='event.items.properties'), ) diff --git a/src/tixlcontrol/views/item.py b/src/tixlcontrol/views/item.py index 248a3f853..2ef14bc9a 100644 --- a/src/tixlcontrol/views/item.py +++ b/src/tixlcontrol/views/item.py @@ -1,9 +1,11 @@ from django.views.generic import ListView from django.views.generic.edit import UpdateView +from django.views.generic.base import TemplateView +from django.views.generic.detail import SingleObjectMixin from django.core.urlresolvers import resolve, reverse from django import forms -from tixlbase.models import Item, ItemCategory, Property +from tixlbase.models import Item, ItemCategory, Property, ItemVariation from tixlcontrol.permissions import EventPermissionRequiredMixin @@ -81,3 +83,97 @@ class ItemUpdateGeneral(EventPermissionRequiredMixin, UpdateView): 'event': self.request.event.slug, 'item': self.get_object().pk, }) + '?success=true' + + +class ItemVariationForm(forms.ModelForm): + + class Meta: + model = ItemVariation + localized_fields = '__all__' + fields = [ + 'active', + 'default_price', + ] + + +class ItemVariations(TemplateView, SingleObjectMixin): + + model = Item + context_object_name = 'item' + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.item = None + + def get_forms(self): + """ + Returns one form per possible item variation. The forms are returned + twice: The first entry in the returned tuple contains a 1-, 2- or + 3-dimensional list, depending on the number of properties associated + with this item (this is being used for form display), the second + contains all forms in one single list (this is used for processing). + """ + forms = [] + forms_flat = [] + variations = self.object.get_all_variations() + data = self.request.POST if self.request.method == 'POST' else None + if self.dimension == 1: + for var in variations: + val = [i[1] for i in sorted([it for it in var.items() if it[0] != 'variation'])] + if 'variation' in var: + form = ItemVariationForm( + data, + instance=var['variation'], + prefix=",".join([str(i) for i in val]) + ) + else: + form = ItemVariationForm( + data, + instance=ItemVariation(item=self.object), + prefix=",".join([str(i) for i in val]) + ) + form.values = val + forms.append(form) + forms_flat = forms + return forms, forms_flat + + def main(self, request, *args, **kwargs): + self.object = self.get_object() + self.properties = self.object.properties.all() + self.dimension = self.properties.count() + self.forms, self.forms_flat = self.get_forms() + + def get(self, request, *args, **kwargs): + self.main(request, *args, **kwargs) + context = self.get_context_data(object=self.object) + return self.render_to_response(context) + + def post(self, request, *args, **kwargs): + self.main(request, *args, **kwargs) + context = self.get_context_data(object=self.object) + for form in self.forms_flat: + if form.is_valid(): + if form.instance.pk is None: + form.save() + form.instance.values.add(*form.values) + else: + form.save() + return self.render_to_response(context) + + def get_object(self, queryset=None): + if not self.item: + url = resolve(self.request.path_info) + self.item = self.request.event.items.get( + id=url.kwargs['item'] + ) + return self.item + + def get_template_names(self): + if self.dimension == 1: + return ['tixlcontrol/item/variations_1d.html'] + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['forms'] = self.forms + context['properties'] = self.properties + return context