from __future__ import annotations from typing import Any, Dict, List from django import forms from django.db.models import Q from django.http import HttpRequest, HttpResponse from django.shortcuts import redirect from django.views import View from django.views.generic.detail import SingleObjectMixin from django.views.generic.edit import FormMixin from django.views.generic.list import MultipleObjectMixin from inertia import render from labs.models import CreditVoucher from .base import PermissionRequiredAdminMixin class CreditVoucherForm(forms.ModelForm): """Form for editing credit vouchers.""" class Meta: model = CreditVoucher fields = [ "unique_code", "credits", "valid_before", "marketing_code", "category", "customer_organization", ] class CreditVoucherListView(PermissionRequiredAdminMixin, MultipleObjectMixin, View): """List view for credit vouchers.""" model = CreditVoucher permission_required = "labs.view_creditvoucher" paginate_by = 10 page_kwarg = "page" ordering = "-created_at" search_fields = ["unique_code"] component = "credit-vouchers/Index" def get_queryset(self): queryset = self.model._default_manager.all() if self.ordering: queryset = queryset.order_by(self.ordering) query = self.request.GET.get("q", "").strip() if query: conditions = Q() for field in self.search_fields: conditions |= Q(**{f"{field}__icontains": query}) queryset = queryset.filter(conditions) used = self.request.GET.get("used") if used in ("Y", "N"): queryset = queryset.filter(used=used) return queryset def serialize_voucher(self, voucher: CreditVoucher) -> Dict[str, Any]: return { "id": str(voucher.id), "unique_code": voucher.unique_code, "credits": voucher.credits, "valid_before": voucher.valid_before.isoformat() if voucher.valid_before else None, "used": voucher.used, "marketing_code": voucher.marketing_code, "category": voucher.category, "customer_organization": voucher.customer_organization, "created_at": voucher.created_at.isoformat(), "updated_at": voucher.updated_at.isoformat(), } def get(self, request: HttpRequest) -> HttpResponse: self.object_list = self.get_queryset() paginate_by = self.get_paginate_by(self.object_list) if paginate_by: paginator, page, object_list, is_paginated = self.paginate_queryset( self.object_list, paginate_by ) else: paginator = None page = None object_list = self.object_list is_paginated = False vouchers: List[Dict[str, Any]] = [ self.serialize_voucher(v) for v in object_list ] pagination: Dict[str, Any] | None = None if paginator and page: pagination = { "page": page.number, "per_page": paginator.per_page, "total": paginator.count, "num_pages": paginator.num_pages, "is_paginated": is_paginated, } return render( request, self.component, { "vouchers": vouchers, "filters": { "q": request.GET.get("q", "").strip(), "used": request.GET.get("used"), }, "pagination": pagination, }, ) class CreditVoucherCreateView(PermissionRequiredAdminMixin, FormMixin, View): """Create view for credit vouchers.""" permission_required = "labs.add_creditvoucher" form_class = CreditVoucherForm component = "credit-vouchers/Create" def get(self, request: HttpRequest) -> HttpResponse: return render( request, self.component, { "form": {}, "errors": {}, }, ) def post(self, request: HttpRequest) -> HttpResponse: form = self.get_form() if form.is_valid(): return self.form_valid(form) return self.form_invalid(form) def form_valid(self, form: forms.ModelForm) -> HttpResponse: voucher = form.save() self.request.session["flash_success"] = "Credit voucher created" return redirect(f"/admin/credit-vouchers/{voucher.id}/") def form_invalid(self, form: forms.ModelForm) -> HttpResponse: return render( self.request, self.component, { "form": dict(form.data), "errors": form.errors, }, ) class CreditVoucherDetailView( PermissionRequiredAdminMixin, FormMixin, SingleObjectMixin, View ): """Detail/Edit view for a single credit voucher.""" model = CreditVoucher permission_required = "labs.view_creditvoucher" form_class = CreditVoucherForm component = "credit-vouchers/Detail" def serialize_voucher(self, voucher: CreditVoucher) -> Dict[str, Any]: return { "id": str(voucher.id), "unique_code": voucher.unique_code, "credits": voucher.credits, "valid_before": voucher.valid_before.isoformat() if voucher.valid_before else None, "used": voucher.used, "marketing_code": voucher.marketing_code, "category": voucher.category, "customer_organization": voucher.customer_organization, "created_at": voucher.created_at.isoformat(), "updated_at": voucher.updated_at.isoformat(), "redeem_at": voucher.redeem_at.isoformat() if voucher.redeem_at else None, "redeem_by": voucher.redeem_by_id, } def get_form_kwargs(self) -> Dict[str, Any]: kwargs = super().get_form_kwargs() kwargs["instance"] = self.object return kwargs def get(self, request: HttpRequest, pk: int) -> HttpResponse: self.object = self.get_object() return render( request, self.component, { "voucher": self.serialize_voucher(self.object), }, ) def post(self, request: HttpRequest, pk: int) -> HttpResponse: if not request.user.has_perm("labs.change_creditvoucher"): return redirect(f"/admin/credit-vouchers/{pk}/") self.object = self.get_object() form = self.get_form() if form.is_valid(): return self.form_valid(form) return self.form_invalid(form) def form_valid(self, form: forms.ModelForm) -> HttpResponse: form.save() self.request.session["flash_success"] = "Credit voucher updated" return redirect(f"/admin/credit-vouchers/{self.object.id}/") def form_invalid(self, form: forms.ModelForm) -> HttpResponse: return render( self.request, self.component, { "voucher": self.serialize_voucher(self.object), "errors": form.errors, "form": dict(form.data), }, )