mirror of
https://github.com/ae-utbm/sith.git
synced 2025-07-09 19:40:19 +00:00
add pages to manage returnable products
This commit is contained in:
@ -17,6 +17,7 @@ from counter.models import (
|
||||
Eticket,
|
||||
Product,
|
||||
Refilling,
|
||||
ReturnableProduct,
|
||||
StudentCard,
|
||||
)
|
||||
from counter.widgets.ajax_select import (
|
||||
@ -213,6 +214,25 @@ class ProductEditForm(forms.ModelForm):
|
||||
return ret
|
||||
|
||||
|
||||
class ReturnableProductForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = ReturnableProduct
|
||||
fields = ["product", "returned_product", "max_return"]
|
||||
widgets = {
|
||||
"product": AutoCompleteSelectProduct(),
|
||||
"returned_product": AutoCompleteSelectProduct(),
|
||||
}
|
||||
|
||||
def save(self, commit: bool = True) -> ReturnableProduct: # noqa FBT
|
||||
instance: ReturnableProduct = super().save(commit=commit)
|
||||
if commit:
|
||||
# This is expensive, but we don't have a task queue to offload it.
|
||||
# Hopefully, creations and updates of returnable products
|
||||
# occur very rarely
|
||||
instance.update_balances()
|
||||
return instance
|
||||
|
||||
|
||||
class CashSummaryFormBase(forms.Form):
|
||||
begin_date = forms.DateTimeField(
|
||||
label=_("Begin date"), widget=SelectDateTime, required=False
|
||||
|
67
counter/templates/counter/returnable_list.jinja
Normal file
67
counter/templates/counter/returnable_list.jinja
Normal file
@ -0,0 +1,67 @@
|
||||
{% extends "core/base.jinja" %}
|
||||
|
||||
{% block title %}
|
||||
{% trans %}Returnable products{% endtrans %}
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_js %}
|
||||
{% endblock %}
|
||||
|
||||
{% block additional_css %}
|
||||
<link rel="stylesheet" href="{{ static("core/components/card.scss") }}">
|
||||
<link rel="stylesheet" href="{{ static("counter/css/admin.scss") }}">
|
||||
<link rel="stylesheet" href="{{ static("bundled/core/components/ajax-select-index.css") }}">
|
||||
<link rel="stylesheet" href="{{ static("core/components/ajax-select.scss") }}">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h3 class="margin-bottom">{% trans %}Returnable products{% endtrans %}</h3>
|
||||
{% if user.has_perm("counter.add_returnableproduct") %}
|
||||
<a href="{{ url('counter:create_returnable') }}" class="btn btn-blue margin-bottom">
|
||||
{% trans %}New returnable product{% endtrans %} <i class="fa fa-plus"></i>
|
||||
</a>
|
||||
{% endif %}
|
||||
<div class="product-group">
|
||||
{% for returnable in object_list %}
|
||||
{% if user.has_perm("counter.change_returnableproduct") %}
|
||||
<a
|
||||
class="card card-row shadow clickable"
|
||||
href="{{ url("counter:edit_returnable", returnable_id=returnable.id) }}"
|
||||
>
|
||||
{% else %}
|
||||
<div class="card card-row shadow">
|
||||
{% endif %}
|
||||
{% if returnable.product.icon %}
|
||||
<img
|
||||
class="card-image"
|
||||
src="{{ returnable.product.icon.url }}"
|
||||
alt="{{ returnable.product.name }}"
|
||||
>
|
||||
{% else %}
|
||||
<i class="fa-regular fa-image fa-2x card-image"></i>
|
||||
{% endif %}
|
||||
<div class="card-content">
|
||||
<strong class="card-title">{{ returnable.product }}</strong>
|
||||
<p>{% trans %}Returned product{% endtrans %} : {{ returnable.returned_product }}</p>
|
||||
</div>
|
||||
{% if user.has_perm("counter.delete_returnableproduct") %}
|
||||
<button
|
||||
x-data
|
||||
class="btn btn-red btn-no-text card-top-left"
|
||||
@click.prevent="document.location.href = '{{ url("counter:delete_returnable", returnable_id=returnable.id) }}'"
|
||||
>
|
||||
{# The delete link is a button with a JS event listener
|
||||
instead of a proper <a> element,
|
||||
because the enclosing card is already a <a>,
|
||||
and HTML forbids nested <a> #}
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if user.has_perm("counter.change_returnableproduct") %}
|
||||
</a>
|
||||
{% else %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock content %}
|
@ -30,6 +30,10 @@ from counter.views.admin import (
|
||||
ProductTypeEditView,
|
||||
ProductTypeListView,
|
||||
RefillingDeleteView,
|
||||
ReturnableProductCreateView,
|
||||
ReturnableProductDeleteView,
|
||||
ReturnableProductListView,
|
||||
ReturnableProductUpdateView,
|
||||
SellingDeleteView,
|
||||
)
|
||||
from counter.views.auth import counter_login, counter_logout
|
||||
@ -51,10 +55,7 @@ from counter.views.home import (
|
||||
CounterMain,
|
||||
)
|
||||
from counter.views.invoice import InvoiceCallView
|
||||
from counter.views.student_card import (
|
||||
StudentCardDeleteView,
|
||||
StudentCardFormView,
|
||||
)
|
||||
from counter.views.student_card import StudentCardDeleteView, StudentCardFormView
|
||||
|
||||
urlpatterns = [
|
||||
path("<int:counter_id>/", CounterMain.as_view(), name="details"),
|
||||
@ -129,6 +130,24 @@ urlpatterns = [
|
||||
ProductTypeEditView.as_view(),
|
||||
name="product_type_edit",
|
||||
),
|
||||
path(
|
||||
"admin/returnable/", ReturnableProductListView.as_view(), name="returnable_list"
|
||||
),
|
||||
path(
|
||||
"admin/returnable/create/",
|
||||
ReturnableProductCreateView.as_view(),
|
||||
name="create_returnable",
|
||||
),
|
||||
path(
|
||||
"admin/returnable/<int:returnable_id>/",
|
||||
ReturnableProductUpdateView.as_view(),
|
||||
name="edit_returnable",
|
||||
),
|
||||
path(
|
||||
"admin/returnable/delete/<int:returnable_id>/",
|
||||
ReturnableProductDeleteView.as_view(),
|
||||
name="delete_returnable",
|
||||
),
|
||||
path("admin/eticket/list/", EticketListView.as_view(), name="eticket_list"),
|
||||
path("admin/eticket/new/", EticketCreateView.as_view(), name="new_eticket"),
|
||||
path(
|
||||
|
@ -15,19 +15,28 @@
|
||||
from datetime import timedelta
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.forms import CheckboxSelectMultiple
|
||||
from django.forms.models import modelform_factory
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import DetailView, ListView, TemplateView
|
||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView
|
||||
|
||||
from core.auth.mixins import CanEditMixin, CanViewMixin
|
||||
from core.utils import get_semester_code, get_start_of_semester
|
||||
from counter.forms import CounterEditForm, ProductEditForm
|
||||
from counter.models import Counter, Product, ProductType, Refilling, Selling
|
||||
from counter.forms import CounterEditForm, ProductEditForm, ReturnableProductForm
|
||||
from counter.models import (
|
||||
Counter,
|
||||
Product,
|
||||
ProductType,
|
||||
Refilling,
|
||||
ReturnableProduct,
|
||||
Selling,
|
||||
)
|
||||
from counter.utils import is_logged_in_counter
|
||||
from counter.views.mixins import CounterAdminMixin, CounterAdminTabsMixin
|
||||
|
||||
@ -146,6 +155,69 @@ class ProductEditView(CounterAdminTabsMixin, CounterAdminMixin, UpdateView):
|
||||
current_tab = "products"
|
||||
|
||||
|
||||
class ReturnableProductListView(
|
||||
CounterAdminTabsMixin, PermissionRequiredMixin, ListView
|
||||
):
|
||||
model = ReturnableProduct
|
||||
queryset = model.objects.select_related("product", "returned_product")
|
||||
template_name = "counter/returnable_list.jinja"
|
||||
current_tab = "returnable_products"
|
||||
permission_required = "counter.view_returnableproduct"
|
||||
|
||||
|
||||
class ReturnableProductCreateView(
|
||||
CounterAdminTabsMixin, PermissionRequiredMixin, CreateView
|
||||
):
|
||||
form_class = ReturnableProductForm
|
||||
template_name = "core/create.jinja"
|
||||
current_tab = "returnable_products"
|
||||
success_url = reverse_lazy("counter:returnable_list")
|
||||
permission_required = "counter.add_returnableproduct"
|
||||
|
||||
|
||||
class ReturnableProductUpdateView(
|
||||
CounterAdminTabsMixin, PermissionRequiredMixin, UpdateView
|
||||
):
|
||||
model = ReturnableProduct
|
||||
pk_url_kwarg = "returnable_id"
|
||||
queryset = model.objects.select_related("product", "returned_product")
|
||||
form_class = ReturnableProductForm
|
||||
template_name = "core/edit.jinja"
|
||||
current_tab = "returnable_products"
|
||||
success_url = reverse_lazy("counter:returnable_list")
|
||||
permission_required = "counter.change_returnableproduct"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return super().get_context_data(**kwargs) | {
|
||||
"object_name": _("returnable product : %(returnable)s -> %(returned)s")
|
||||
% {
|
||||
"returnable": self.object.product.name,
|
||||
"returned": self.object.returned_product.name,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ReturnableProductDeleteView(
|
||||
CounterAdminTabsMixin, PermissionRequiredMixin, DeleteView
|
||||
):
|
||||
model = ReturnableProduct
|
||||
pk_url_kwarg = "returnable_id"
|
||||
queryset = model.objects.select_related("product", "returned_product")
|
||||
template_name = "core/delete_confirm.jinja"
|
||||
current_tab = "returnable_products"
|
||||
success_url = reverse_lazy("counter:returnable_list")
|
||||
permission_required = "counter.delete_returnableproduct"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return super().get_context_data(**kwargs) | {
|
||||
"object_name": _("returnable product : %(returnable)s -> %(returned)s")
|
||||
% {
|
||||
"returnable": self.object.product.name,
|
||||
"returned": self.object.returned_product.name,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class RefillingDeleteView(DeleteView):
|
||||
"""Delete a refilling (for the admins)."""
|
||||
|
||||
|
@ -98,6 +98,11 @@ class CounterAdminTabsMixin(TabedViewMixin):
|
||||
"slug": "product_types",
|
||||
"name": _("Product types"),
|
||||
},
|
||||
{
|
||||
"url": reverse_lazy("counter:returnable_list"),
|
||||
"slug": "returnable_products",
|
||||
"name": _("Returnable products"),
|
||||
},
|
||||
{
|
||||
"url": reverse_lazy("counter:cash_summary_list"),
|
||||
"slug": "cash_summary",
|
||||
|
Reference in New Issue
Block a user