Display total amount preview. Close #2

This commit is contained in:
Gabriel Augendre 2023-03-27 17:35:16 +02:00
parent 3de1c13995
commit 9b381a0e18
5 changed files with 67 additions and 12 deletions

View file

@ -2,6 +2,7 @@ from crispy_forms.bootstrap import InlineRadios
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Div, Layout, Submit from crispy_forms.layout import Div, Layout, Submit
from django import forms from django import forms
from django.urls import reverse
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from purchase.layout import BasketItemField from purchase.layout import BasketItemField
@ -25,6 +26,12 @@ class BasketForm(forms.ModelForm):
self.helper = FormHelper() self.helper = FormHelper()
self.helper.form_class = "form-horizontal" self.helper.form_class = "form-horizontal"
self.helper.add_input(Submit("submit", _("Save"))) self.helper.add_input(Submit("submit", _("Save")))
self.helper.attrs = {
"hx_post": reverse("purchase:price_preview"),
"hx_trigger": "change",
"hx_target": "#price_preview",
"hx_swap": "innerHTML",
}
self.helper.layout = Layout() self.helper.layout = Layout()
products = {} products = {}
basket = kwargs.get("instance") basket = kwargs.get("instance")
@ -51,6 +58,7 @@ class BasketForm(forms.ModelForm):
css_id="products", css_id="products",
), ),
InlineRadios("payment_method"), InlineRadios("payment_method"),
Div(css_id="price_preview", css_class="mb-2"),
) )
def save(self): def save(self):

View file

@ -1,14 +1,25 @@
window.incrementValue = function (id) { window.incrementValue = function (id) {
let value = parseInt(document.getElementById(id).value); const element = document.getElementById(id);
let value = parseInt(element.value);
value = isNaN(value) ? 0 : value; value = isNaN(value) ? 0 : value;
value++; value++;
document.getElementById(id).value = value; element.value = value;
window.dispatchChanged(element);
}; };
window.decrementValue = function (id) { window.decrementValue = function (id) {
let value = parseInt(document.getElementById(id).value); const element = document.getElementById(id);
let value = parseInt(element.value);
value = isNaN(value) ? 0 : value; value = isNaN(value) ? 0 : value;
value--; value--;
value = value < 0 ? 0 : value; value = value < 0 ? 0 : value;
document.getElementById(id).value = value; element.value = value;
window.dispatchChanged(element);
};
window.dispatchChanged = function (element) {
const event = new Event("change", { bubbles: true });
element.dispatchEvent(event);
}; };

View file

@ -1,12 +1,19 @@
from django.urls import path from django.urls import path
from purchase.views import delete_basket, list_baskets, new_basket, update_basket from purchase.views import (
from purchase.views.basket import additional_unpriced_product additional_unpriced_product,
delete_basket,
list_baskets,
new_basket,
price_preview,
update_basket,
)
from purchase.views.reports import by_hour_plot_view, products_plots_view, reports from purchase.views.reports import by_hour_plot_view, products_plots_view, reports
app_name = "purchase" app_name = "purchase"
urlpatterns = [ urlpatterns = [
path("", list_baskets, name="list"), path("", list_baskets, name="list"),
path("price_preview/", price_preview, name="price_preview"),
path("new/", new_basket, name="new"), path("new/", new_basket, name="new"),
path("<int:pk>/update/", update_basket, name="update"), path("<int:pk>/update/", update_basket, name="update"),
path("<int:pk>/delete/", delete_basket, name="delete"), path("<int:pk>/delete/", delete_basket, name="delete"),

View file

@ -1,3 +1,17 @@
from .basket import delete_basket, list_baskets, new_basket, update_basket from .basket import (
additional_unpriced_product,
delete_basket,
list_baskets,
new_basket,
price_preview,
update_basket,
)
__all__ = ["new_basket", "update_basket", "delete_basket", "list_baskets"] __all__ = [
"new_basket",
"update_basket",
"delete_basket",
"list_baskets",
"additional_unpriced_product",
"price_preview",
]

View file

@ -3,7 +3,7 @@ import logging
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.decorators import permission_required from django.contrib.auth.decorators import permission_required
from django.core.handlers.wsgi import WSGIRequest from django.core.handlers.wsgi import WSGIRequest
from django.http import HttpRequest, HttpResponse from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect, render from django.shortcuts import get_object_or_404, redirect, render
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.urls import reverse from django.urls import reverse
@ -12,7 +12,7 @@ from django.utils.translation import gettext_lazy as _
from django.views.decorators.http import condition, require_http_methods from django.views.decorators.http import condition, require_http_methods
from django_htmx.http import trigger_client_event from django_htmx.http import trigger_client_event
from purchase.forms import UNPRICED_PREFIX, BasketForm from purchase.forms import PRICED_PREFIX, UNPRICED_PREFIX, BasketForm
from purchase.models import Basket, Product, reports_etag, reports_last_modified from purchase.models import Basket, Product, reports_etag, reports_last_modified
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -100,14 +100,14 @@ def additional_unpriced_product(request: WSGIRequest) -> HttpResponse:
@permission_required("purchase.view_basket") @permission_required("purchase.view_basket")
@condition(etag_func=reports_etag, last_modified_func=reports_last_modified) @condition(etag_func=reports_etag, last_modified_func=reports_last_modified)
def list_baskets(request: HttpRequest) -> HttpResponse: def list_baskets(request: WSGIRequest) -> HttpResponse:
context = {"baskets": Basket.objects.priced().order_by("-id")} context = {"baskets": Basket.objects.priced().order_by("-id")}
return TemplateResponse(request, "purchase/basket_list.html", context) return TemplateResponse(request, "purchase/basket_list.html", context)
@require_http_methods(["GET", "POST"]) @require_http_methods(["GET", "POST"])
@permission_required("purchase.delete_basket") @permission_required("purchase.delete_basket")
def delete_basket(request: HttpRequest, pk: int) -> HttpResponse: def delete_basket(request: WSGIRequest, pk: int) -> HttpResponse:
basket = get_object_or_404(Basket, pk=pk) basket = get_object_or_404(Basket, pk=pk)
if request.method == "GET": if request.method == "GET":
context = {"basket": basket} context = {"basket": basket}
@ -115,3 +115,18 @@ def delete_basket(request: HttpRequest, pk: int) -> HttpResponse:
basket.delete() basket.delete()
messages.success(request, _("Basket successfully deleted.")) messages.success(request, _("Basket successfully deleted."))
return redirect("purchase:list") return redirect("purchase:list")
@require_http_methods(["POST"])
@permission_required("purchase.add_basket")
def price_preview(request: WSGIRequest) -> HttpResponse:
total = 0
for name in request.POST:
if name.startswith(PRICED_PREFIX):
product_id = name[len(PRICED_PREFIX) :]
product = get_object_or_404(Product, pk=product_id)
total += product.unit_price_cents * int(request.POST.get(name, 0))
elif name.startswith(UNPRICED_PREFIX):
total += sum(map(int, request.POST.getlist(name)))
return HttpResponse(f"Montant total : {total/100:.2f}")