diff --git a/counter/api.py b/counter/api.py
index f3f0f101..d58f0154 100644
--- a/counter/api.py
+++ b/counter/api.py
@@ -21,11 +21,12 @@ from ninja_extra import ControllerBase, api_controller, paginate, route
from ninja_extra.pagination import PageNumberPaginationExtra
from ninja_extra.schemas import PaginatedResponseSchema
-from core.api_permissions import CanAccessLookup, CanView, IsRoot
-from counter.models import Counter, Product
+from core.api_permissions import CanAccessLookup, CanView, IsLoggedInCounter, IsRoot
+from counter.models import Counter, Customer, Product
from counter.schemas import (
CounterFilterSchema,
CounterSchema,
+ CustomerBalance,
ProductSchema,
SimplifiedCounterSchema,
)
@@ -60,6 +61,13 @@ class CounterController(ControllerBase):
return filters.filter(Counter.objects.all())
+@api_controller("/customer")
+class CustomerController(ControllerBase):
+ @route.get("/balance", response=CustomerBalance, permissions=[IsLoggedInCounter])
+ def get_balance(self, customer_id: int):
+ return self.get_object_or_exception(Customer, pk=customer_id)
+
+
@api_controller("/product")
class ProductController(ControllerBase):
@route.get(
diff --git a/counter/schemas.py b/counter/schemas.py
index ec1a842d..4fbbc712 100644
--- a/counter/schemas.py
+++ b/counter/schemas.py
@@ -4,7 +4,7 @@ from annotated_types import MinLen
from ninja import Field, FilterSchema, ModelSchema
from core.schemas import SimpleUserSchema
-from counter.models import Counter, Product
+from counter.models import Counter, Customer, Product
class CounterSchema(ModelSchema):
@@ -16,6 +16,12 @@ class CounterSchema(ModelSchema):
fields = ["id", "name", "type", "club", "products"]
+class CustomerBalance(ModelSchema):
+ class Meta:
+ model = Customer
+ fields = ["amount"]
+
+
class CounterFilterSchema(FilterSchema):
search: Annotated[str, MinLen(1)] = Field(None, q="name__icontains")
diff --git a/counter/static/counter/js/counter_click.js b/counter/static/bundled/counter/counter-click-index.ts
similarity index 100%
rename from counter/static/counter/js/counter_click.js
rename to counter/static/bundled/counter/counter-click-index.ts
diff --git a/counter/templates/counter/counter_click.jinja b/counter/templates/counter/counter_click.jinja
index 3df77555..65c7d9ca 100644
--- a/counter/templates/counter/counter_click.jinja
+++ b/counter/templates/counter/counter_click.jinja
@@ -6,7 +6,7 @@
{% endblock %}
{% block additional_js %}
-
+
{% endblock %}
{% block info_boxes %}
@@ -28,7 +28,7 @@
{% trans %}Customer{% endtrans %}
{{ user_mini_profile(customer.user) }}
{{ user_subscription(customer.user) }}
- {% trans %}Amount: {% endtrans %}{{ customer.amount }} €
+ {% trans %}Amount: {% endtrans %} €
{% if counter.type == 'BAR' %}
{% trans %}Student card{% endtrans %}
@@ -105,16 +105,12 @@
- {% if (counter.type == 'BAR' and barmens_can_refill) %}
+ {% if refilling_fragment %}
{% trans %}Refilling{% endtrans %}
-
-
+
+ {{ refilling_fragment }}
{% endif %}
@@ -155,9 +151,6 @@
{% block script %}
{{ super() }}
{% endblock script %}
\ No newline at end of file
diff --git a/counter/templates/counter/fragments/create_refill.jinja b/counter/templates/counter/fragments/create_refill.jinja
new file mode 100644
index 00000000..7de612a3
--- /dev/null
+++ b/counter/templates/counter/fragments/create_refill.jinja
@@ -0,0 +1,9 @@
+
diff --git a/counter/tests/test_counter.py b/counter/tests/test_counter.py
index 855b43f6..2574865f 100644
--- a/counter/tests/test_counter.py
+++ b/counter/tests/test_counter.py
@@ -67,17 +67,24 @@ class TestCounter(TestCase):
{"code": self.richard.customer.account_id, "counter_token": counter_token},
)
counter_url = response.get("location")
- response = self.client.get(response.get("location"))
+ refill_url = reverse(
+ "counter:refilling_create",
+ kwargs={"customer_id": self.richard.customer.pk},
+ )
+
+ response = self.client.get(
+ response.get("location"),
+ )
assert ">Richard Batsbak" in str(response.content)
self.client.post(
- counter_url,
+ refill_url,
{
- "action": "refill",
"amount": "5",
"payment_method": "CASH",
"bank": "OTHER",
},
+ HTTP_REFERER=counter_url,
)
self.client.post(counter_url, "action=code&code=BARB", content_type="text/xml")
self.client.post(
@@ -110,15 +117,15 @@ class TestCounter(TestCase):
)
response = self.client.post(
- counter_url,
+ refill_url,
{
- "action": "refill",
"amount": "5",
"payment_method": "CASH",
"bank": "OTHER",
},
+ HTTP_REFERER=counter_url,
)
- assert response.status_code == 200
+ assert response.status_code == 302
self.client.post(
reverse("counter:login", kwargs={"counter_id": self.foyer.id}),
@@ -138,17 +145,23 @@ class TestCounter(TestCase):
{"code": self.richard.customer.account_id, "counter_token": counter_token},
)
counter_url = response.get("location")
+ refill_url = reverse(
+ "counter:refilling_create",
+ kwargs={
+ "customer_id": self.richard.customer.pk,
+ },
+ )
response = self.client.post(
- counter_url,
+ refill_url,
{
- "action": "refill",
"amount": "5",
"payment_method": "CASH",
"bank": "OTHER",
},
+ HTTP_REFERER=counter_url,
)
- assert response.status_code == 200
+ assert response.status_code == 302
def test_annotate_has_barman_queryset(self):
"""Test if the custom queryset method `annotate_has_barman` works as intended."""
diff --git a/counter/urls.py b/counter/urls.py
index fa659ba0..0ca77f73 100644
--- a/counter/urls.py
+++ b/counter/urls.py
@@ -39,7 +39,7 @@ from counter.views.cash import (
CashSummaryListView,
CounterCashSummaryView,
)
-from counter.views.click import CounterClick
+from counter.views.click import CounterClick, RefillingCreateView
from counter.views.eticket import (
EticketCreateView,
EticketEditView,
@@ -60,6 +60,11 @@ from counter.views.student_card import (
urlpatterns = [
path("/", CounterMain.as_view(), name="details"),
path("/click//", CounterClick.as_view(), name="click"),
+ path(
+ "refill//",
+ RefillingCreateView.as_view(),
+ name="refilling_create",
+ ),
path(
"/last_ops/",
CounterLastOperationsView.as_view(),
diff --git a/counter/views/click.py b/counter/views/click.py
index 65e889ff..1d87ac8b 100644
--- a/counter/views/click.py
+++ b/counter/views/click.py
@@ -24,11 +24,13 @@ from django.http import Http404, HttpResponseRedirect, JsonResponse
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
-from django.views.generic import DetailView
+from django.views.generic import DetailView, FormView
+from core.utils import FormFragmentTemplateData
from core.views import CanViewMixin
from counter.forms import RefillForm
from counter.models import Counter, Customer, Product, Selling
+from counter.utils import is_logged_in_counter
from counter.views.mixins import CounterTabsMixin
from counter.views.student_card import StudentCardFormView
@@ -100,7 +102,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
request.session["too_young"] = False
request.session["not_allowed"] = False
request.session["no_age"] = False
- self.refill_form = None
ret = super().get(request, *args, **kwargs)
if (self.object.type != "BAR" and not request.user.is_authenticated) or (
self.object.type == "BAR" and len(self.object.barmen_list) == 0
@@ -111,7 +112,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
def post(self, request, *args, **kwargs):
"""Handle the many possibilities of the post request."""
self.object = self.get_object()
- self.refill_form = None
if (self.object.type != "BAR" and not request.user.is_authenticated) or (
self.object.type == "BAR" and len(self.object.barmen_list) < 1
): # Check that at least one barman is logged in
@@ -148,8 +148,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
self.add_product(request)
elif action == "del_product":
self.del_product(request)
- elif action == "refill":
- self.refill(request)
elif action == "code":
return self.parse_code(request)
elif action == "cancel":
@@ -383,19 +381,6 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
reverse_lazy("counter:details", args=self.args, kwargs=kwargs)
)
- def refill(self, request):
- """Refill the customer's account."""
- if not self.object.can_refill():
- raise PermissionDenied
- form = RefillForm(request.POST)
- if form.is_valid():
- form.instance.counter = self.object
- form.instance.operator = self.operator
- form.instance.customer = self.customer
- form.instance.save()
- else:
- self.refill_form = form
-
def get_context_data(self, **kwargs):
"""Add customer to the context."""
kwargs = super().get_context_data(**kwargs)
@@ -413,9 +398,77 @@ class CounterClick(CounterTabsMixin, CanViewMixin, DetailView):
)
kwargs["customer"] = self.customer
kwargs["basket_total"] = self.sum_basket(self.request)
- kwargs["refill_form"] = self.refill_form or RefillForm()
- kwargs["barmens_can_refill"] = self.object.can_refill()
kwargs["student_card_fragment"] = StudentCardFormView.get_template_data(
self.customer
).render(self.request)
+
+ if self.object.can_refill():
+ kwargs["refilling_fragment"] = RefillingCreateView.get_template_data(
+ self.customer
+ ).render(self.request)
return kwargs
+
+
+class RefillingCreateView(FormView):
+ """This is a fragment only view which integrates with counter_click.jinja"""
+
+ form_class = RefillForm
+ template_name = "counter/fragments/create_refill.jinja"
+
+ @classmethod
+ def get_template_data(
+ cls, customer: Customer, *, form_instance: form_class | None = None
+ ) -> FormFragmentTemplateData[form_class]:
+ return FormFragmentTemplateData[cls.form_class](
+ form=form_instance if form_instance else cls.form_class(),
+ template=cls.template_name,
+ context={
+ "action": reverse_lazy(
+ "counter:refilling_create", kwargs={"customer_id": customer.pk}
+ ),
+ },
+ )
+
+ def dispatch(self, request, *args, **kwargs):
+ self.customer: Customer = get_object_or_404(Customer, pk=kwargs["customer_id"])
+ if not self.customer.can_buy:
+ raise Http404
+
+ if not is_logged_in_counter(request):
+ raise PermissionDenied
+
+ self.counter: Counter = get_object_or_404(
+ Counter, token=request.session["counter_token"]
+ )
+
+ if not self.counter.can_refill():
+ raise PermissionDenied
+
+ if self.customer_is_barman():
+ self.operator = self.customer.user
+ else:
+ self.operator = self.counter.get_random_barman()
+
+ return super().dispatch(request, *args, **kwargs)
+
+ def customer_is_barman(self) -> bool:
+ barmen = self.counter.barmen_list
+ return self.counter.type == "BAR" and self.customer.user in barmen
+
+ def form_valid(self, form):
+ res = super().form_valid(form)
+ form.clean()
+ form.instance.counter = self.counter
+ form.instance.operator = self.operator
+ form.instance.customer = self.customer
+ form.instance.save()
+ return res
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ data = self.get_template_data(self.customer, form_instance=context["form"])
+ context.update(data.context)
+ return context
+
+ def get_success_url(self, **kwargs):
+ return self.request.path
diff --git a/counter/views/student_card.py b/counter/views/student_card.py
index 35226e95..952f65eb 100644
--- a/counter/views/student_card.py
+++ b/counter/views/student_card.py
@@ -70,11 +70,11 @@ class StudentCardFormView(FormView):
@classmethod
def get_template_data(
- cls, customer: Customer
- ) -> FormFragmentTemplateData[StudentCardForm]:
+ cls, customer: Customer, *, form_instance: form_class | None = None
+ ) -> FormFragmentTemplateData[form_class]:
"""Get necessary data to pre-render the fragment"""
- return FormFragmentTemplateData(
- form=cls.form_class(),
+ return FormFragmentTemplateData[cls.form_class](
+ form=form_instance if form_instance else cls.form_class(),
template=cls.template_name,
context={
"action": reverse(
@@ -105,7 +105,7 @@ class StudentCardFormView(FormView):
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
- data = self.get_template_data(self.customer)
+ data = self.get_template_data(self.customer, form_instance=context["form"])
context.update(data.context)
return context