# lingo - payment and billing system # Copyright (C) 2022 Entr'ouvert # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . import datetime import json from collections import defaultdict from operator import itemgetter from django import forms from django.contrib import messages from django.db.models import Prefetch from django.http import Http404, HttpResponse, HttpResponseBadRequest, HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.urls import reverse, reverse_lazy from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext from django.views.generic import ( CreateView, DeleteView, DetailView, FormView, ListView, RedirectView, UpdateView, ) from django.views.generic.detail import SingleObjectMixin from lingo.agendas.chrono import refresh_agendas from lingo.agendas.models import Agenda, CheckType, CheckTypeGroup from lingo.agendas.views import AgendaMixin from lingo.pricing.forms import ( AgendaPricingForm, CheckTypeForm, CriteriaForm, ExportForm, ImportForm, NewCheckTypeForm, NewCriteriaForm, PricingCriteriaCategoryAddForm, PricingCriteriaCategoryEditForm, PricingDuplicateForm, PricingMatrixForm, PricingVariableFormSet, ) from lingo.pricing.models import AgendaPricing, Criteria, CriteriaCategory, Pricing, PricingCriteriaCategory from lingo.pricing.utils import export_site, import_site from lingo.utils.misc import AgendaImportError class ConfigExportView(FormView): form_class = ExportForm template_name = 'lingo/pricing/export.html' def form_valid(self, form): response = HttpResponse(content_type='application/json') today = datetime.date.today() response['Content-Disposition'] = 'attachment; filename="export_pricing_config_{}.json"'.format( today.strftime('%Y%m%d') ) json.dump(export_site(**form.cleaned_data), response, indent=2) return response config_export = ConfigExportView.as_view() class ConfigImportView(FormView): form_class = ImportForm template_name = 'lingo/pricing/import.html' success_url = reverse_lazy('lingo-manager-pricing-list') def form_valid(self, form): try: config_json = json.loads(force_text(self.request.FILES['config_json'].read())) except ValueError: form.add_error('config_json', _('File is not in the expected JSON format.')) return self.form_invalid(form) try: results = import_site(config_json, overwrite=False) except AgendaImportError as exc: form.add_error('config_json', '%s' % exc) return self.form_invalid(form) except KeyError as exc: form.add_error('config_json', _('Key "%s" is missing.') % exc.args[0]) return self.form_invalid(form) import_messages = { 'agendas': { 'update_noop': _('No agenda updated.'), 'update': lambda x: ungettext( 'An agenda has been updated.', '%(count)d agendas have been updated.', x, ), }, 'check_type_groups': { 'create_noop': _('No check type group created.'), 'create': lambda x: ungettext( 'A check type group has been created.', '%(count)d check type groups have been created.', x, ), 'update_noop': _('No check type group updated.'), 'update': lambda x: ungettext( 'A check type group has been updated.', '%(count)d check type groups have been updated.', x, ), }, 'pricing_categories': { 'create_noop': _('No pricing criteria category created.'), 'create': lambda x: ungettext( 'A pricing criteria category has been created.', '%(count)d pricing criteria categories have been created.', x, ), 'update_noop': _('No pricing criteria category updated.'), 'update': lambda x: ungettext( 'A pricing criteria category has been updated.', '%(count)d pricing criteria categories have been updated.', x, ), }, 'pricing_models': { 'create_noop': _('No pricing model created.'), 'create': lambda x: ungettext( 'A pricing model has been created.', '%(count)d pricing models have been created.', x, ), 'update_noop': _('No pricing model updated.'), 'update': lambda x: ungettext( 'A pricing model has been updated.', '%(count)d pricing models have been updated.', x, ), }, } global_noop = True for obj_name, obj_results in results.items(): if obj_results['all']: global_noop = False count = len(obj_results['created']) if not count: message1 = import_messages[obj_name].get('create_noop') else: message1 = import_messages[obj_name]['create'](count) % {'count': count} count = len(obj_results['updated']) if not count: message2 = import_messages[obj_name]['update_noop'] else: message2 = import_messages[obj_name]['update'](count) % {'count': count} obj_results['messages'] = "%s %s" % (message1, message2) a_count, ct_count, pc_count, pm_count = ( len(results['agendas']['all']), len(results['check_type_groups']['all']), len(results['pricing_categories']['all']), len(results['pricing_models']['all']), ) if (a_count, ct_count, pc_count, pm_count) == (1, 0, 0, 0): # only one agenda imported, redirect to agenda page return HttpResponseRedirect( reverse( 'lingo-manager-agenda-detail', kwargs={'pk': results['agendas']['all'][0].pk}, ) ) if (a_count, ct_count, pc_count, pm_count) == (0, 1, 0, 0): # only one check type group imported, redirect to check type page return HttpResponseRedirect(reverse('lingo-manager-check-type-list')) if (a_count, ct_count, pc_count, pm_count) == (0, 0, 1, 0): # only one criteria category imported, redirect to criteria page return HttpResponseRedirect(reverse('lingo-manager-pricing-criteria-list')) if (a_count, ct_count, pc_count, pm_count) == (0, 0, 0, 1): # only one pricing imported, redirect to pricing page return HttpResponseRedirect( reverse( 'lingo-manager-pricing-detail', kwargs={'pk': results['pricing_models']['all'][0].pk}, ) ) if global_noop: messages.info(self.request, _('No data found.')) else: messages.info(self.request, results['agendas']['messages']) messages.info(self.request, results['check_type_groups']['messages']) messages.info(self.request, results['pricing_categories']['messages']) messages.info(self.request, results['pricing_models']['messages']) return super().form_valid(form) config_import = ConfigImportView.as_view() class PricingListView(ListView): template_name = 'lingo/pricing/manager_pricing_list.html' model = Pricing pricing_list = PricingListView.as_view() class CriteriaListView(ListView): template_name = 'lingo/pricing/manager_criteria_list.html' model = CriteriaCategory def get_queryset(self): return CriteriaCategory.objects.prefetch_related('criterias') criteria_list = CriteriaListView.as_view() class PricingAddView(CreateView): template_name = 'lingo/pricing/manager_pricing_form.html' model = Pricing fields = ['label'] def get_success_url(self): return reverse('lingo-manager-pricing-detail', args=[self.object.pk]) pricing_add = PricingAddView.as_view() class PricingDetailView(DetailView): template_name = 'lingo/pricing/manager_pricing_detail.html' model = Pricing def get_queryset(self): return ( super() .get_queryset() .prefetch_related( Prefetch( 'categories', queryset=CriteriaCategory.objects.order_by('pricingcriteriacategory__order') ) ) ) def get_context_data(self, **kwargs): kwargs['agendas'] = Agenda.objects.filter( pk__in=AgendaPricing.objects.filter(pricing=self.object).values('agenda') ) return super().get_context_data(**kwargs) pricing_detail = PricingDetailView.as_view() class PricingEditView(UpdateView): template_name = 'lingo/pricing/manager_pricing_form.html' model = Pricing fields = ['label', 'slug'] def get_success_url(self): return reverse('lingo-manager-pricing-detail', args=[self.object.pk]) pricing_edit = PricingEditView.as_view() class PricingDeleteView(DeleteView): template_name = 'lingo/manager_confirm_delete.html' model = Pricing def get_success_url(self): return reverse('lingo-manager-pricing-list') pricing_delete = PricingDeleteView.as_view() class PricingDuplicate(SingleObjectMixin, FormView): template_name = 'lingo/pricing/manager_pricing_duplicate_form.html' model = Pricing form_class = PricingDuplicateForm def dispatch(self, request, *args, **kwargs): self.object = self.get_object() return super().dispatch(request, *args, **kwargs) def get_success_url(self): return reverse('lingo-manager-pricing-detail', kwargs={'pk': self.new_pricing.pk}) def form_valid(self, form): self.new_pricing = self.object.duplicate(label=form.cleaned_data['label']) return super().form_valid(form) pricing_duplicate = PricingDuplicate.as_view() class PricingExport(DetailView): model = Pricing def get(self, request, *args, **kwargs): response = HttpResponse(content_type='application/json') today = datetime.date.today() attachment = 'attachment; filename="export_pricing_{}_{}.json"'.format( self.get_object().slug, today.strftime('%Y%m%d') ) response['Content-Disposition'] = attachment json.dump({'pricing_models': [self.get_object().export_json()]}, response, indent=2) return response pricing_export = PricingExport.as_view() class PricingVariableEdit(FormView): template_name = 'lingo/pricing/manager_pricing_variable_form.html' model = Pricing form_class = PricingVariableFormSet 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 sorted( ({'key': k, 'value': v} for k, v in self.object.extra_variables.items()), key=itemgetter('key'), ) def form_valid(self, form): self.object.extra_variables = {} for sub_data in form.cleaned_data: if not sub_data.get('key'): continue self.object.extra_variables[sub_data['key']] = sub_data['value'] self.object.save() return HttpResponseRedirect(self.get_success_url()) def get_success_url(self): return reverse('lingo-manager-pricing-detail', args=[self.object.pk]) pricing_variable_edit = PricingVariableEdit.as_view() class PricingCriteriaCategoryAddView(FormView): template_name = 'lingo/pricing/manager_pricing_criteria_category_form.html' model = Pricing form_class = PricingCriteriaCategoryAddForm def dispatch(self, request, *args, **kwargs): self.object = get_object_or_404(Pricing, pk=kwargs['pk']) if self.object.categories.count() >= 3: raise Http404 return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['pricing'] = self.object return kwargs def get_context_data(self, **kwargs): kwargs['object'] = self.object return super().get_context_data(**kwargs) def form_valid(self, form): PricingCriteriaCategory.objects.create(pricing=self.object, category=form.cleaned_data['category']) return super().form_valid(form) def get_success_url(self): return reverse('lingo-manager-pricing-detail', args=[self.object.pk]) pricing_criteria_category_add = PricingCriteriaCategoryAddView.as_view() class PricingCriteriaCategoryEditView(FormView): template_name = 'lingo/pricing/manager_pricing_criteria_category_form.html' model = Pricing form_class = PricingCriteriaCategoryEditForm def dispatch(self, request, *args, **kwargs): self.object = get_object_or_404(Pricing, pk=kwargs['pk']) self.category = get_object_or_404(self.object.categories, pk=kwargs['category_pk']) return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs['pricing'] = self.object kwargs['category'] = self.category return kwargs def get_context_data(self, **kwargs): kwargs['object'] = self.object kwargs['category'] = self.category return super().get_context_data(**kwargs) def form_valid(self, form): old_criterias = self.object.criterias.filter(category=self.category) new_criterias = form.cleaned_data['criterias'] removed_criterias = set(old_criterias) - set(new_criterias) self.object.criterias.remove(*removed_criterias) self.object.criterias.add(*new_criterias) return super().form_valid(form) def get_success_url(self): return reverse('lingo-manager-pricing-detail', args=[self.object.pk]) pricing_criteria_category_edit = PricingCriteriaCategoryEditView.as_view() class PricingCriteriaCategoryDeleteView(DeleteView): template_name = 'lingo/manager_confirm_delete.html' model = CriteriaCategory pk_url_kwarg = 'category_pk' def dispatch(self, request, *args, **kwargs): self.pricing = get_object_or_404(Pricing, pk=kwargs['pk']) return super().dispatch(request, *args, **kwargs) def get_queryset(self): return self.pricing.categories.all() def delete(self, request, *args, **kwargs): self.object = self.get_object() self.pricing.categories.remove(self.object) self.pricing.criterias.remove(*self.pricing.criterias.filter(category=self.object)) return HttpResponseRedirect(self.get_success_url()) def get_success_url(self): return reverse('lingo-manager-pricing-detail', args=[self.pricing.pk]) pricing_criteria_category_delete = PricingCriteriaCategoryDeleteView.as_view() class PricingCriteriaCategoryOrder(DetailView): model = Pricing def get(self, request, *args, **kwargs): if 'new-order' not in request.GET: return HttpResponseBadRequest('missing new-order parameter') pricing = self.get_object() try: new_order = [int(x) for x in request.GET['new-order'].split(',')] except ValueError: return HttpResponseBadRequest('incorrect new-order parameter') categories = pricing.categories.all() if set(new_order) != {x.pk for x in categories} or len(new_order) != len(categories): return HttpResponseBadRequest('incorrect new-order parameter') for i, c_id in enumerate(new_order): PricingCriteriaCategory.objects.filter(pricing=pricing, category=c_id).update(order=i + 1) return HttpResponse(status=204) pricing_criteria_category_order = PricingCriteriaCategoryOrder.as_view() class CriteriaCategoryAddView(CreateView): template_name = 'lingo/pricing/manager_criteria_category_form.html' model = CriteriaCategory fields = ['label'] def get_success_url(self): return reverse('lingo-manager-pricing-criteria-list') criteria_category_add = CriteriaCategoryAddView.as_view() class CriteriaCategoryEditView(UpdateView): template_name = 'lingo/pricing/manager_criteria_category_form.html' model = CriteriaCategory fields = ['label', 'slug'] def get_success_url(self): return reverse('lingo-manager-pricing-criteria-list') criteria_category_edit = CriteriaCategoryEditView.as_view() class CriteriaCategoryDeleteView(DeleteView): template_name = 'lingo/manager_confirm_delete.html' model = CriteriaCategory def get_success_url(self): return reverse('lingo-manager-pricing-criteria-list') criteria_category_delete = CriteriaCategoryDeleteView.as_view() class CriteriaCategoryExport(DetailView): model = CriteriaCategory def get(self, request, *args, **kwargs): response = HttpResponse(content_type='application/json') today = datetime.date.today() attachment = 'attachment; filename="export_pricing_category_{}_{}.json"'.format( self.get_object().slug, today.strftime('%Y%m%d') ) response['Content-Disposition'] = attachment json.dump({'pricing_categories': [self.get_object().export_json()]}, response, indent=2) return response criteria_category_export = CriteriaCategoryExport.as_view() class CriteriaOrder(DetailView): model = CriteriaCategory def get(self, request, *args, **kwargs): if 'new-order' not in request.GET: return HttpResponseBadRequest('missing new-order parameter') category = self.get_object() try: new_order = [int(x) for x in request.GET['new-order'].split(',')] except ValueError: return HttpResponseBadRequest('incorrect new-order parameter') criterias = category.criterias.filter(default=False) if set(new_order) != {x.pk for x in criterias} or len(new_order) != len(criterias): return HttpResponseBadRequest('incorrect new-order parameter') criterias_by_id = {c.pk: c for c in criterias} for i, c_id in enumerate(new_order): criterias_by_id[c_id].order = i + 1 criterias_by_id[c_id].save() return HttpResponse(status=204) criteria_order = CriteriaOrder.as_view() class CriteriaAddView(CreateView): template_name = 'lingo/pricing/manager_criteria_form.html' model = Criteria form_class = NewCriteriaForm def dispatch(self, request, *args, **kwargs): self.category_pk = kwargs.pop('category_pk') return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self): kwargs = super().get_form_kwargs() if not kwargs.get('instance'): kwargs['instance'] = self.model() kwargs['instance'].category_id = self.category_pk return kwargs def get_success_url(self): return reverse('lingo-manager-pricing-criteria-list') criteria_add = CriteriaAddView.as_view() class CriteriaEditView(UpdateView): template_name = 'lingo/pricing/manager_criteria_form.html' model = Criteria form_class = CriteriaForm def dispatch(self, request, *args, **kwargs): self.category_pk = kwargs.pop('category_pk') return super().dispatch(request, *args, **kwargs) def get_queryset(self): return Criteria.objects.filter(category=self.category_pk) def get_success_url(self): return reverse('lingo-manager-pricing-criteria-list') criteria_edit = CriteriaEditView.as_view() class CriteriaDeleteView(DeleteView): template_name = 'lingo/manager_confirm_delete.html' model = Criteria def dispatch(self, request, *args, **kwargs): self.category_pk = kwargs.pop('category_pk') return super().dispatch(request, *args, **kwargs) def get_queryset(self): return Criteria.objects.filter(category=self.category_pk) def get_success_url(self): return reverse('lingo-manager-pricing-criteria-list') criteria_delete = CriteriaDeleteView.as_view() class AgendaListView(ListView): template_name = 'lingo/pricing/manager_agenda_list.html' model = Agenda def get_queryset(self): queryset = super().get_queryset() return queryset.order_by('category_label', 'label') agenda_list = AgendaListView.as_view() class AgendaSyncView(RedirectView): def get(self, request, *args, **kwargs): refresh_agendas() messages.info(self.request, _('Agendas refreshed.')) return super().get(request, *args, **kwargs) def get_redirect_url(self, *args, **kwargs): return reverse('lingo-manager-agenda-list') agenda_sync = AgendaSyncView.as_view() class AgendaDetailView(AgendaMixin, DetailView): template_name = 'lingo/pricing/manager_agenda_detail.html' model = Agenda def get_context_data(self, **kwargs): kwargs['agenda_pricings'] = ( AgendaPricing.objects.filter(agenda=self.agenda) .select_related('pricing') .order_by('date_start', 'date_end') ) return super().get_context_data(**kwargs) agenda_detail = AgendaDetailView.as_view() class AgendaDetailRedirectView(RedirectView): def get_redirect_url(self, *args, **kwargs): agenda = get_object_or_404(Agenda, slug=kwargs['slug']) return reverse('lingo-manager-agenda-detail', kwargs={'pk': agenda.pk}) agenda_detail_redirect = AgendaDetailRedirectView.as_view() class AgendaExport(AgendaMixin, DetailView): model = Agenda def get(self, request, *args, **kwargs): response = HttpResponse(content_type='application/json') today = datetime.date.today() response['Content-Disposition'] = 'attachment; filename="export_pricing_agenda_{}_{}.json"'.format( self.get_object().slug, today.strftime('%Y%m%d') ) json.dump({'agendas': [self.get_object().export_json()]}, response, indent=2) return response agenda_export = AgendaExport.as_view() class AgendaBookingCheckSettingsView(AgendaMixin, UpdateView): template_name = 'lingo/pricing/manager_agenda_form.html' model = Agenda fields = ['check_type_group'] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context['form_url'] = reverse('lingo-manager-agenda-booking-check-settings', args=[self.agenda.pk]) context['title'] = _("Configure booking check options") return context agenda_booking_check_settings = AgendaBookingCheckSettingsView.as_view() class AgendaPricingAddView(AgendaMixin, CreateView): template_name = 'lingo/pricing/manager_agenda_pricing_form.html' model = AgendaPricing form_class = AgendaPricingForm def get_success_url(self): return reverse('lingo-manager-agenda-pricing-detail', args=[self.agenda.pk, self.object.pk]) agenda_pricing_add = AgendaPricingAddView.as_view() class AgendaPricingDetailView(AgendaMixin, DetailView): model = AgendaPricing pk_url_kwarg = 'pricing_pk' template_name = 'lingo/pricing/manager_agenda_pricing_detail.html' def get_queryset(self): return AgendaPricing.objects.filter(agenda=self.agenda).prefetch_related( 'pricing__criterias__category' ) agenda_pricing_detail = AgendaPricingDetailView.as_view() class AgendaPricingEditView(AgendaMixin, UpdateView): template_name = 'lingo/pricing/manager_agenda_pricing_form.html' model = AgendaPricing pk_url_kwarg = 'pricing_pk' form_class = AgendaPricingForm def get_queryset(self): return AgendaPricing.objects.filter(agenda=self.agenda) def get_success_url(self): return reverse('lingo-manager-agenda-pricing-detail', args=[self.agenda.pk, self.object.pk]) agenda_pricing_edit = AgendaPricingEditView.as_view() class AgendaPricingDeleteView(AgendaMixin, DeleteView): template_name = 'lingo/manager_confirm_delete.html' model = AgendaPricing pk_url_kwarg = 'pricing_pk' def get_queryset(self): return AgendaPricing.objects.filter(agenda=self.agenda) agenda_pricing_delete = AgendaPricingDeleteView.as_view() class AgendaPricingMatrixEdit(AgendaMixin, FormView): template_name = 'lingo/pricing/manager_agenda_pricing_matrix_form.html' def set_agenda(self, **kwargs): super().set_agenda(**kwargs) self.object = get_object_or_404( AgendaPricing.objects.filter(agenda=self.agenda), pk=kwargs['pricing_pk'] ) matrix_list = list(self.object.iter_pricing_matrix()) if not matrix_list: raise Http404 self.matrix = None if kwargs.get('slug'): for matrix in matrix_list: if matrix.criteria is None: continue if matrix.criteria.slug == kwargs['slug']: self.matrix = matrix break else: if matrix_list[0].criteria is None: self.matrix = matrix_list[0] if self.matrix is None: raise Http404 def get_context_data(self, **kwargs): kwargs['object'] = self.object kwargs['matrix'] = self.matrix return super().get_context_data(**kwargs) def get_form(self): count = len(self.matrix.rows) PricingMatrixFormSet = forms.formset_factory( PricingMatrixForm, min_num=count, max_num=count, extra=0, can_delete=False ) kwargs = { 'initial': [ {'crit_%i' % i: cell.value for i, cell in enumerate(row.cells)} for row in self.matrix.rows ] } if self.request.method == 'POST': kwargs.update( { 'data': self.request.POST, } ) return PricingMatrixFormSet(form_kwargs={'matrix': self.matrix}, **kwargs) def post(self, *args, **kwargs): form = self.get_form() if form.is_valid(): # build prixing_data for this matrix matrix_pricing_data = defaultdict(dict) for i, sub_data in enumerate(form.cleaned_data): row = self.matrix.rows[i] for j, cell in enumerate(row.cells): value = sub_data['crit_%s' % j] key = cell.criteria.identifier if cell.criteria else None matrix_pricing_data[key][row.criteria.identifier] = float(value) if self.matrix.criteria: # full pricing model with 3 categories self.object.pricing_data = self.object.pricing_data or {} self.object.pricing_data[self.matrix.criteria.identifier] = matrix_pricing_data elif list(matrix_pricing_data.keys()) == [None]: # only one category self.object.pricing_data = matrix_pricing_data[None] else: # 2 categories self.object.pricing_data = matrix_pricing_data self.object.save() return self.form_valid(form) else: return self.form_invalid(form) def get_success_url(self): return reverse('lingo-manager-agenda-pricing-detail', args=[self.agenda.pk, self.object.pk]) agenda_pricing_matrix_edit = AgendaPricingMatrixEdit.as_view() class CheckTypeListView(ListView): template_name = 'lingo/pricing/manager_check_type_list.html' model = CheckTypeGroup def get_queryset(self): return CheckTypeGroup.objects.prefetch_related('check_types') check_type_list = CheckTypeListView.as_view() class CheckTypeGroupAddView(CreateView): template_name = 'lingo/pricing/manager_check_type_group_form.html' model = CheckTypeGroup fields = ['label'] def get_success_url(self): return reverse('lingo-manager-check-type-list') check_type_group_add = CheckTypeGroupAddView.as_view() class CheckTypeGroupEditView(UpdateView): template_name = 'lingo/pricing/manager_check_type_group_form.html' model = CheckTypeGroup fields = ['label', 'slug'] def get_success_url(self): return reverse('lingo-manager-check-type-list') check_type_group_edit = CheckTypeGroupEditView.as_view() class CheckTypeGroupDeleteView(DeleteView): template_name = 'lingo/manager_confirm_delete.html' model = CheckTypeGroup def get_success_url(self): return reverse('lingo-manager-check-type-list') check_type_group_delete = CheckTypeGroupDeleteView.as_view() class CheckTypeGroupExport(DetailView): model = CheckTypeGroup def get(self, request, *args, **kwargs): response = HttpResponse(content_type='application/json') today = datetime.date.today() attachment = 'attachment; filename="export_check_type_group_{}_{}.json"'.format( self.get_object().slug, today.strftime('%Y%m%d') ) response['Content-Disposition'] = attachment json.dump({'check_type_groups': [self.get_object().export_json()]}, response, indent=2) return response check_type_group_export = CheckTypeGroupExport.as_view() class CheckTypeAddView(CreateView): template_name = 'lingo/pricing/manager_check_type_form.html' model = CheckType form_class = NewCheckTypeForm def dispatch(self, request, *args, **kwargs): self.group_pk = kwargs.pop('group_pk') return super().dispatch(request, *args, **kwargs) def get_form_kwargs(self): kwargs = super().get_form_kwargs() if not kwargs.get('instance'): kwargs['instance'] = self.model() kwargs['instance'].group_id = self.group_pk return kwargs def get_success_url(self): return reverse('lingo-manager-check-type-list') check_type_add = CheckTypeAddView.as_view() class CheckTypeEditView(UpdateView): template_name = 'lingo/pricing/manager_check_type_form.html' model = CheckType form_class = CheckTypeForm def dispatch(self, request, *args, **kwargs): self.group_pk = kwargs.pop('group_pk') return super().dispatch(request, *args, **kwargs) def get_queryset(self): return CheckType.objects.filter(group=self.group_pk) def get_success_url(self): return reverse('lingo-manager-check-type-list') check_type_edit = CheckTypeEditView.as_view() class CheckTypeDeleteView(DeleteView): template_name = 'lingo/manager_confirm_delete.html' model = CheckType def dispatch(self, request, *args, **kwargs): self.group_pk = kwargs.pop('group_pk') return super().dispatch(request, *args, **kwargs) def get_queryset(self): return CheckType.objects.filter(group=self.group_pk) def get_success_url(self): return reverse('lingo-manager-check-type-list') check_type_delete = CheckTypeDeleteView.as_view()