Add minimal loans system

Functional loans system for users.
No manager interface yet.

See:
- app "loans"
- a few new views/templates in app "inventory"
This commit is contained in:
Sylvain Gay 2024-05-02 11:13:34 +02:00
parent a552e6d63b
commit 474083dd38
19 changed files with 318 additions and 2 deletions

0
loans/__init__.py Normal file
View file

6
loans/admin.py Normal file
View file

@ -0,0 +1,6 @@
from django.contrib import admin
class LoanAdmin(admin.ModelAdmin):
list_display = ("lent_object", "borrow_date", "return_date")
ordering = ("-borrow_date",)
list_filter = ("return_date", "borrow_date")

6
loans/apps.py Normal file
View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class LoansConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'loans'

4
loans/forms.py Normal file
View file

@ -0,0 +1,4 @@
from django import forms
class BorrowForm(forms.Form):
mail = forms.EmailField(label="Mail")

View file

@ -0,0 +1,33 @@
# Generated by Django 4.2.8 on 2024-04-23 16:45
import autoslug.fields
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('inventory', '0002_duration_range'),
]
operations = [
migrations.CreateModel(
name='Loan',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='game', unique=True)),
('borrow_date', models.DateTimeField(auto_now_add=True)),
('return_date', models.DateTimeField(null=True)),
('mail', models.EmailField(max_length=254)),
('game', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='loans', to='inventory.game', verbose_name='jeu emprunté')),
],
options={
'verbose_name': 'emprunt',
'verbose_name_plural': 'emprunts',
'ordering': ['borrow_date'],
},
),
]

View file

@ -0,0 +1,32 @@
# Generated by Django 4.2.8 on 2024-04-30 15:24
import autoslug.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('loans', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='AbstractLoan',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('slug', autoslug.fields.AutoSlugField(editable=False, populate_from='game', unique=True)),
('borrow_date', models.DateTimeField(auto_now_add=True)),
('return_date', models.DateTimeField(null=True)),
('mail', models.EmailField(max_length=254)),
],
options={
'verbose_name': 'emprunt',
'verbose_name_plural': 'emprunts',
'ordering': ['borrow_date'],
},
),
migrations.DeleteModel(
name='Loan',
),
]

View file

32
loans/models.py Normal file
View file

@ -0,0 +1,32 @@
from django.db import models
from autoslug import AutoSlugField
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)
mail = models.EmailField()
lent_object_slug_field = "slug"
class Meta:
ordering=["borrow_date"]
verbose_name = "emprunt"
verbose_name_plural = "emprunts"
def __str__(self):
return self.slug
def return_object(self):
self.return_date = now()
self.save()
@classmethod
def ongoing_loans(cls, obj = None):
ongoing = cls.objects.filter(return_date=None)
if obj != None:
return ongoing.filter(lent_object=obj)
else:
return ongoing

3
loans/tests.py Normal file
View file

@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

70
loans/views.py Normal file
View file

@ -0,0 +1,70 @@
from django.views.generic import DetailView, FormView, RedirectView
from django.views.generic.detail import SingleObjectMixin
from django.contrib import messages
from django.shortcuts import redirect
from inventory.models import Game
from .models import AbstractLoan
from .forms import BorrowForm
class ReturnView(SingleObjectMixin, RedirectView):
# Inherited classes should contain:
# model = LoanModel
# pattern_name =
redirect_slug_field = "slug"
permanent = False
def get_redirect_url(self, *args, **kwargs):
loan = self.get_object()
loan.return_object()
kwargs[self.redirect_slug_field] = getattr(loan.lent_object,
loan.lent_object_slug_field)
messages.success(self.request, "Rendu effectué.")
return super().get_redirect_url(*args, **kwargs)
class BorrowView(SingleObjectMixin, FormView):
# Inherited classes should contain:
# model = LentObjectModel
# loan_model = LoanModel
# template_name = "path/to/template.html"
form_class = BorrowForm # Update this for a more complex form
def get_initial(self):
initial = super().get_initial()
if "loan_mail" in self.request.session:
initial["mail"] = self.request.session["loan_mail"]
return initial
def get_context_data(self, **kwargs):
self.object = self.get_object()
return super().get_context_data(**kwargs)
def form_valid(self, form):
obj = self.get_object()
ongoing = self.loan_model.ongoing_loans(obj)
if ongoing.exists():
ongoing.get().return_object()
loan = self.loan_model(lent_object=obj, mail=form.cleaned_data["mail"])
loan.save()
self.request.session["loan_mail"] = loan.mail
messages.success(self.request, "Votre emprunt est enregistré.")
return redirect(self.success_pattern_name,
getattr(obj, loan.lent_object_slug_field))
class DetailLoanView(DetailView):
# Inherited classes should contain:
# model = LentObjectModel
# loan_model = LoanModel
# template_name = "path/to/template.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
loans = self.loan_model.ongoing_loans(self.get_object())
is_borrowed = loans.exists()
context["is_borrowed"] = is_borrowed
if is_borrowed:
context["loan"] = loans.get()
return context