Add loan tables

Add two views for loans :
- inventory:ongoing_loans
- inventory:all_loans (permission inventory.can_see_loan_details required)
This commit is contained in:
Sylvain Gay 2024-05-06 16:24:16 +02:00
parent d8551052fd
commit 00148cd5ca
15 changed files with 156 additions and 6 deletions

View file

@ -26,6 +26,7 @@ INSTALLED_APPS = [
"django.contrib.messages",
"django.contrib.staticfiles",
"django_cas_ng",
"django_tables2",
"markdownx",
"haystack",
"website",

View file

@ -0,0 +1,31 @@
# Generated by Django 4.2.8 on 2024-05-06 14:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0003_gameloan'),
]
operations = [
migrations.AlterModelOptions(
name='category',
options={'ordering': ['name'], 'verbose_name': 'étagère'},
),
migrations.AlterModelOptions(
name='gameloan',
options={'ordering': ['borrow_date'], 'permissions': [('can_see_loan_details', 'Can see loan details')], 'verbose_name': 'emprunt', 'verbose_name_plural': 'emprunts'},
),
migrations.AlterField(
model_name='gameloan',
name='borrow_date',
field=models.DateTimeField(auto_now_add=True, verbose_name='Date demprunt'),
),
migrations.AlterField(
model_name='gameloan',
name='return_date',
field=models.DateTimeField(null=True, verbose_name='Date de retour'),
),
]

View file

@ -162,3 +162,5 @@ class GameLoan(AbstractLoan):
class Meta(AbstractLoan.Meta):
abstract = False
permissions = [("can_see_loan_details", "Can see loan details")]

33
inventory/tables.py Normal file
View file

@ -0,0 +1,33 @@
import django_tables2 as tables
from django.utils.html import format_html
from django.urls import reverse
from .models import GameLoan
class LoanTable(tables.Table):
next_pattern = "inventory:all_loans"
class Meta:
model = GameLoan
sequence = ("lent_object", "borrow_date", "return_date", "mail")
exclude = ("id",)
slug = tables.Column(verbose_name="Actions", orderable=False)
def render_lent_object(self, value, record):
return format_html("<a href='{}'>{}</a>",
reverse("inventory:game", args=[record.lent_object.slug]), value)
def render_slug(self, value, record):
res = ""
if record.return_date == None:
res = format_html("<a class='button' href='{}?next={}'>Rendre le jeu</a>",
reverse("inventory:return_game", args=[value]),
reverse(self.next_pattern))
return res
class OngoingLoansTable(LoanTable):
next_pattern = "inventory:ongoing_loans"
class Meta(LoanTable.Meta):
exclude = ("return_date", "mail", "id")

View file

@ -11,6 +11,9 @@
<hr/>
<a class="button" href="{% url "inventory:ongoing_loans" %}">
Emprunts en cours
</a>
<a class="button" href="{% url "inventory:category_list" %}">
Liste des étagères
<p class="helptext">

View file

@ -0,0 +1,9 @@
{% extends "base.html" %}
{% load render_table from django_tables2 %}
{% block"content" %}
<h1>Liste des emprunts</h1>
{% render_table table %}
{% endblock %}
{% block "main_attributes" %}class="wide-main"{% endblock %}

View file

@ -0,0 +1,7 @@
{% extends "base.html" %}
{% load render_table from django_tables2 %}
{% block"content" %}
<h1>Emprunts en cours</h1>
{% render_table table %}
{% endblock %}

View file

@ -13,6 +13,8 @@ from .views import (
GameLoanView,
BorrowGameView,
ReturnGameView,
OngoingLoansView,
DetailLoanView,
)
app_name = "inventory"
@ -34,7 +36,9 @@ urlpatterns = [
name="modify_game_comment",
),
path("search/", InventorySearchView.as_view(), name="search"),
path("loans/<slug>/", GameLoanView.as_view(), name="game_loan"),
path("loans/game/<slug>/", GameLoanView.as_view(), name="game_loan"),
path("loans/return/<slug>/", ReturnGameView.as_view(), name="return_game"),
path("loans/borrow/<slug>/", BorrowGameView.as_view(), name="borrow_game"),
path("loans/ongoing/", OngoingLoansView.as_view(), name="ongoing_loans"),
path("loans/all/", DetailLoanView.as_view(), name="all_loans"),
]

View file

@ -1,11 +1,14 @@
from django.views.generic import TemplateView, ListView, DetailView
from django.contrib.auth.mixins import PermissionRequiredMixin
from haystack.generic_views import SearchView
from haystack.forms import SearchForm
from haystack.query import SearchQuerySet
from django_tables2.views import SingleTableView
from comments.views import AddCommentView, ModifyCommentView
from loans.views import BorrowView, ReturnView, DetailLoanView
from .models import Category, Tag, Game, GameComment, GameLoan
from .forms import BorrowGameForm
from .tables import LoanTable, OngoingLoansTable
class InventoryView(TemplateView):
@ -67,6 +70,7 @@ class ModifyGameCommentView(ModifyCommentView):
template_name = "inventory/game.html"
success_pattern_name = "inventory:game"
class BorrowGameView(BorrowView):
model = Game
loan_model = GameLoan
@ -74,12 +78,27 @@ class BorrowGameView(BorrowView):
form_class = BorrowGameForm
success_pattern_name = "inventory:game_loan"
class ReturnGameView(ReturnView):
model = GameLoan
pattern_name = "inventory:game_loan"
class GameLoanView(DetailLoanView):
model = Game
loan_model = GameLoan
template_name = "inventory/loans/game_loan.html"
class OngoingLoansView(SingleTableView):
queryset = GameLoan.ongoing_loans()
table_class = OngoingLoansTable
template_name = "inventory/loans/ongoing.html"
class DetailLoanView(PermissionRequiredMixin, SingleTableView):
permission_required = "inventory.can_see_loan_details"
model = GameLoan
table_class = LoanTable
template_name = "inventory/loans/loans_table.html"

View file

@ -5,8 +5,9 @@ from django.utils.timezone import now
class AbstractLoan(models.Model):
lent_object = None # Fill this with a foreign key in subclasses
slug = AutoSlugField(unique=True, populate_from="lent_object")
borrow_date = models.DateTimeField(auto_now_add=True)
return_date = models.DateTimeField(null=True)
borrow_date = models.DateTimeField(
auto_now_add=True, verbose_name="Date demprunt")
return_date = models.DateTimeField(null=True, verbose_name="Date de retour")
mail = models.EmailField()
lent_object_slug_field = "slug"

View file

@ -21,6 +21,8 @@ class ReturnView(SingleObjectMixin, RedirectView):
kwargs[self.redirect_slug_field] = getattr(loan.lent_object,
loan.lent_object_slug_field)
messages.success(self.request, "Rendu effectué.")
if "next" in self.request.GET:
return self.request.GET["next"]
return super().get_redirect_url(*args, **kwargs)

View file

@ -7,3 +7,4 @@ Pillow
django-cas-ng
django-haystack
Whoosh
django-tables2

View file

@ -31,7 +31,6 @@ body {
}
main {
max-width: $page_width;
width: 100%;
padding: 20px 40px;
margin: 0 auto 50px;
@ -48,6 +47,10 @@ main {
}
}
main:not(.wide-main){
max-width: $page_width;
}
footer {
background-color: $footer_bg_color;
font-size: 0.7em;
@ -153,6 +156,24 @@ table {
th {
border-bottom-width: 2px;
}
th.asc::before {
display: inline;
font-family: FontAwesome;
content: '\f0de'; // Sort up symbol
}
th.desc::before {
display: inline;
font-family: FontAwesome;
content: '\f0dd'; // Sort down symbol
}
th.orderable:not(.asc, .desc)::before {
display: inline;
font-family: FontAwesome;
content: '\f0dc'; // Sort up/down symbol
}
}
iframe {

View file

@ -277,9 +277,7 @@ body {
@media (max-width: 700px) {
body {
font-size: 12pt; } }
main {
max-width: 850px;
width: 100%;
padding: 20px 40px;
margin: 0 auto 50px;
@ -291,6 +289,9 @@ main {
main.small_page {
max-width: 550px; }
main:not(.wide-main) {
max-width: 850px; }
footer {
background-color: #6bb8c4;
font-size: 0.7em;
@ -403,6 +404,18 @@ table {
text-align: left; }
table th {
border-bottom-width: 2px; }
table th.asc::before {
display: inline;
font-family: FontAwesome;
content: '\f0de'; }
table th.desc::before {
display: inline;
font-family: FontAwesome;
content: '\f0dd'; }
table th.orderable:not(.asc, .desc)::before {
display: inline;
font-family: FontAwesome;
content: '\f0dc'; }
iframe {
width: 100%;

View file

@ -11,6 +11,9 @@
<a {% if url_name == "home" %}class="current"{% endif %} href="{% url "website:home" %}"><i class="fa fa-home" aria-hidden="true"></i></a>
<a {% if url_name == "inventory" %}class="current"{% endif %} href="{% url "inventory:inventory" %}">Inventaire</a>
<a {% if url_name == "suggestions" %}class="current"{% endif %} href="{% url "suggestions:suggestions" %}">Suggestions</a>
{% if perms.inventory.can_see_loan_details %}
<a href={% url "inventory:all_loans" %}>Gestion des emprunts</a>
{% endif %}
{% if request.user.is_staff %}
<a href="{% url "admin:index" %}">Admin</a>
{% endif %}