diff --git a/accounts/__pycache__/forms.cpython-310.pyc b/accounts/__pycache__/forms.cpython-310.pyc index b6ce4c7b6c3a22e58e1a4a233437ccad26e077fe..6f46ab7caf1f5c40769fc8bd6d36411d7152a0cb 100644 Binary files a/accounts/__pycache__/forms.cpython-310.pyc and b/accounts/__pycache__/forms.cpython-310.pyc differ diff --git a/accounts/__pycache__/models.cpython-310.pyc b/accounts/__pycache__/models.cpython-310.pyc index b396b29e48cfde618b32f972fbf4c204f494bcd4..610354c75ef7fcbc6f8a5d38775785db043a4e9a 100644 Binary files a/accounts/__pycache__/models.cpython-310.pyc and b/accounts/__pycache__/models.cpython-310.pyc differ diff --git a/accounts/__pycache__/urls.cpython-310.pyc b/accounts/__pycache__/urls.cpython-310.pyc index 6f802ce2d136f5e6765c930a099cbb11d9cd4281..b4957187ee71e435be11d8dc5445dca41c94c212 100644 Binary files a/accounts/__pycache__/urls.cpython-310.pyc and b/accounts/__pycache__/urls.cpython-310.pyc differ diff --git a/accounts/__pycache__/views.cpython-310.pyc b/accounts/__pycache__/views.cpython-310.pyc index a5d7598631a7df75b992dedca6be0819838d8f69..89daf999c01b4449eb610198f397b51bbcd2e97b 100644 Binary files a/accounts/__pycache__/views.cpython-310.pyc and b/accounts/__pycache__/views.cpython-310.pyc differ diff --git a/accounts/forms.py b/accounts/forms.py index 1e19538c00a5095d7dd8c0f6c93dd14514cf9708..c95c0e710e8c2ed056a6f1096dda6e06dad8c7ef 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -1,5 +1,5 @@ from django import forms -from .models import Account, EndOfMonthStatement +from .models import Account, EndOfMonthStatement, StudentDiscountRequest from clubs.models import Club class AccountForm(forms.ModelForm): @@ -18,4 +18,9 @@ class CardForm(forms.Form): expiryMonth = forms.IntegerField(label = "Expiry Month (number)", max_value=12, min_value=1, required=True) expiryYear = forms.IntegerField(label = "Expiry Year", min_value=2000, max_value=3000, required=True) cvc = forms.CharField(label = "CVC", max_length=3, min_length=3, required=True) - cardHolderName = forms.CharField(label = "Cardholder Name", max_length=255, required=True) \ No newline at end of file + cardHolderName = forms.CharField(label = "Cardholder Name", max_length=255, required=True) + +class StudentDiscountForm(forms.Form): + class Meta: + model = StudentDiscountRequest + fields = ['new_discount_rate', 'reason'] \ No newline at end of file diff --git a/accounts/models.py b/accounts/models.py index f94f8f0be33ad6f9a0a0dbac7504c137d99a59e3..31dfe911cd7c19bfaec1f8fe3eddbee678f55382 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -1,5 +1,6 @@ from django.db import models import datetime +from django.conf import settings # from payments.models import PaymentReceipt # Create your models here. @@ -37,4 +38,12 @@ class EndOfMonthStatement(models.Model): def update_outstanding(statement_id): statement = EndOfMonthStatement.objects.get(pk=statement_id) - return statement.total_spent - statement.total_paid \ No newline at end of file + return statement.total_spent - statement.total_paid + + +class StudentDiscountRequest(models.Model): + student = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name='discount_requests') + old_discount_rate = models.FloatField(default=0) + new_discount_rate = models.FloatField(default=0) + reason = models.CharField(max_length=255) + approved = models.BooleanField(default=False) \ No newline at end of file diff --git a/accounts/templates/accounts/create_student_discount_request.html b/accounts/templates/accounts/create_student_discount_request.html new file mode 100644 index 0000000000000000000000000000000000000000..401daabead06f573753cebf194a62f43cb676252 --- /dev/null +++ b/accounts/templates/accounts/create_student_discount_request.html @@ -0,0 +1,31 @@ +{% extends 'base.html' %} {% block content %} +<div class="container"> + <div class="row"> + <div class="col-md-8 offset-md-2"> + <h1>Create Student Discount Request</h1> + <form method="post"> + {% csrf_token %} + <div class="form-group"> + <label for="{{ form.new_discount_rate.id_for_label }}" + >New Discount Rate:</label + > + {{ form.new_discount_rate }} {% if form.new_discount_rate.errors %} + <div class="invalid-feedback"> + {%for error in form.new_discount_rate.errors%}{{ error }}{%endfor%} + </div> + {% endif %} + </div> + <div class="form-group"> + <label for="{{ form.reason.id_for_label }}">Reason:</label> + {{ form.reason }} {% if form.reason.errors %} + <div class="invalid-feedback"> + {% for error in form.reason.errors %} {{ error }} {% endfor %} + </div> + {% endif %} + </div> + <button type="submit" class="btn btn-primary">Submit</button> + </form> + </div> + </div> +</div> +{% endblock %} diff --git a/accounts/urls.py b/accounts/urls.py index 5c6874db0d557a2a03ea2c677f4f6d9568a1f54b..f01fbf85961ecea720e17c77d4c36e3044d14374 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -12,4 +12,7 @@ urlpatterns = [ path('accounts/<int:pk>/manage_statements/<int:st_pk>/update/', views.statement_update, name='update_statement'), path('accounts/<int:pk>/manage_statements/<int:st_pk>/delete/', views.statement_delete, name='delete_statement'), path('accounts/<int:pk>/top_up_balance', views.top_up_balance, name='top_up_balance'), + path('accounts/create_student_discount_request/<int:pk>/', views.create_student_discount_request, name='create_student_discount_request'), + path('accounts/approve_student_discount_request/<int:pk>/', views.approve_student_discount_request, name='approve_student_discount_request'), + path('accounts/deny_student_discount_request/<int:pk>/', views.deny_student_discount_request, name='deny_student_discount_request'), ] diff --git a/accounts/views.py b/accounts/views.py index da7f8a687334efdc67bece7dd0b8a0ec6474ec63..d737d2558b65270cc86565a11237d356bf654b67 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -1,10 +1,11 @@ from django.shortcuts import render, redirect, get_object_or_404 from django.contrib import messages +from django.conf import settings from utils.custom_decorators import get_user_permissions, is_in_group from utils.create_statement import create_statement from stripePayments.views import create_charge -from .forms import AccountForm, EndOfMonthStatementForm, CardForm -from .models import Account, EndOfMonthStatement, UserAccount, CinemaAccount, ClubAccount +from .forms import AccountForm, EndOfMonthStatementForm, CardForm, StudentDiscountRequestForm +from .models import Account, EndOfMonthStatement, UserAccount, CinemaAccount, ClubAccount, StudentDiscountRequest from clubs.models import Club from decimal import Decimal @@ -183,4 +184,33 @@ def top_up_balance(request, pk): form = CardForm() - return render(request, 'accounts/top_up_balance.html', {'form' : form}) \ No newline at end of file + return render(request, 'accounts/top_up_balance.html', {'form' : form}) + +def create_student_discount_request(request, pk): + perms = get_user_permissions(request) + if request.method == 'POST': + student = get_object_or_404(settings.USER_AUTH_MODEL, pk=pk) + form = StudentDiscountRequestForm(request.POST) + if form.is_valid(): + student_discount_request = form.save(commit=False) + student_discount_request.student = student + student_discount_request.old_discount_rate = student.account.discount_rate + student_discount_request.save() + return redirect('index') + else: + form = StudentDiscountRequestForm() + return render(request, 'clubs/create_student_discount_request.html', {'perms': perms, 'form': form}) + +def approve_student_discount_request(request, pk): + perms = get_user_permissions(request) + if perms == '0' or perms == '1': + return redirect('no_access') + discount_request = get_object_or_404(StudentDiscountRequest, pk=pk) + student = discount_request.student + student.account.discount_rate = discount_request.discount_rate + student.account.save() + discount_request.delete() + return redirect('index') + +def deny_student_discount_request(request, pk): + pass diff --git a/authentication/__pycache__/views.cpython-310.pyc b/authentication/__pycache__/views.cpython-310.pyc index f236c9cbf1ab05725680fa05a6f8ac6a38ca06b8..34318c765611a97a08acacfa4586f8779c429f59 100644 Binary files a/authentication/__pycache__/views.cpython-310.pyc and b/authentication/__pycache__/views.cpython-310.pyc differ diff --git a/booking/migrations/__pycache__/0009_reservation_reservee_email.cpython-310.pyc b/booking/migrations/__pycache__/0009_reservation_reservee_email.cpython-310.pyc index 646f501bd36c7652d36c2db022d3b27f6b8ab137..fc411cedeb1b3753c123dd2205bd2a53c1a77a1e 100644 Binary files a/booking/migrations/__pycache__/0009_reservation_reservee_email.cpython-310.pyc and b/booking/migrations/__pycache__/0009_reservation_reservee_email.cpython-310.pyc differ diff --git a/cinema/__pycache__/forms.cpython-310.pyc b/cinema/__pycache__/forms.cpython-310.pyc index 9f056839d5f98345deb6d4731bd4b99fa217262c..a07ccac6397582074123b52976121a5fefa43541 100644 Binary files a/cinema/__pycache__/forms.cpython-310.pyc and b/cinema/__pycache__/forms.cpython-310.pyc differ diff --git a/cinema/__pycache__/models.cpython-310.pyc b/cinema/__pycache__/models.cpython-310.pyc index 07d3d3f592af395575eaa9b001770f710307303a..72940bfd6a69badcde2c4f937ea65ecfdd2da5e4 100644 Binary files a/cinema/__pycache__/models.cpython-310.pyc and b/cinema/__pycache__/models.cpython-310.pyc differ diff --git a/cinema/__pycache__/urls.cpython-310.pyc b/cinema/__pycache__/urls.cpython-310.pyc index a0e6f8884d8395daf01e5e80264dcbe5083522ff..cb1bb59c035a7d8df631f3a819d26c83585ed705 100644 Binary files a/cinema/__pycache__/urls.cpython-310.pyc and b/cinema/__pycache__/urls.cpython-310.pyc differ diff --git a/cinema/__pycache__/views.cpython-310.pyc b/cinema/__pycache__/views.cpython-310.pyc index 706454da84acfeb084dee88010649f1ae6117cf4..b7860e030af1e2ec29a61efd031accdfc58ccb5a 100644 Binary files a/cinema/__pycache__/views.cpython-310.pyc and b/cinema/__pycache__/views.cpython-310.pyc differ diff --git a/cinema/forms.py b/cinema/forms.py index 2a8a7440a68abf5b1bbe29604adb7cf56cae0d2f..9dc071c06738cf57cdfbad0a20bbf9d9c69f1cd4 100644 --- a/cinema/forms.py +++ b/cinema/forms.py @@ -19,7 +19,7 @@ class ShowingForm(forms.ModelForm): film = forms.ModelChoiceField(queryset=Film.objects.all()) class Meta: model = Showing - fields = ['screen', 'film', 'start_time'] + fields = ['screen', 'film', 'start_time', 'social_distancing'] class FilmForm(forms.ModelForm): class Meta: diff --git a/cinema/migrations/0014_showing_social_distancing_seat.py b/cinema/migrations/0014_showing_social_distancing_seat.py new file mode 100644 index 0000000000000000000000000000000000000000..68f7250c80b3b0395e553411a6b22bb13e184f61 --- /dev/null +++ b/cinema/migrations/0014_showing_social_distancing_seat.py @@ -0,0 +1,42 @@ +# Generated by Django 4.1.5 on 2023-04-23 16:33 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("cinema", "0013_remove_cinema_ticket_price_showing_adult_price_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="showing", + name="social_distancing", + field=models.BooleanField(default=False), + ), + migrations.CreateModel( + name="Seat", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("seat_number", models.CharField(max_length=10)), + ("is_available", models.BooleanField(default=True)), + ( + "showing", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="seats", + to="cinema.showing", + ), + ), + ], + ), + ] diff --git a/cinema/migrations/0015_remove_showing_adult_price_and_more.py b/cinema/migrations/0015_remove_showing_adult_price_and_more.py new file mode 100644 index 0000000000000000000000000000000000000000..aa13076404a4a882e0ee559ffabde6c3ccb65442 --- /dev/null +++ b/cinema/migrations/0015_remove_showing_adult_price_and_more.py @@ -0,0 +1,24 @@ +# Generated by Django 4.1.5 on 2023-04-23 16:42 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("cinema", "0014_showing_social_distancing_seat"), + ] + + operations = [ + migrations.RemoveField( + model_name="showing", + name="adult_price", + ), + migrations.RemoveField( + model_name="showing", + name="child_price", + ), + migrations.RemoveField( + model_name="showing", + name="student_price", + ), + ] diff --git a/cinema/migrations/0016_delete_seat.py b/cinema/migrations/0016_delete_seat.py new file mode 100644 index 0000000000000000000000000000000000000000..ca98efba60412b039d164b5f0409c99643385442 --- /dev/null +++ b/cinema/migrations/0016_delete_seat.py @@ -0,0 +1,15 @@ +# Generated by Django 4.1.5 on 2023-04-23 17:02 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("cinema", "0015_remove_showing_adult_price_and_more"), + ] + + operations = [ + migrations.DeleteModel( + name="Seat", + ), + ] diff --git a/cinema/migrations/0017_showing_covid_capacity_and_more.py b/cinema/migrations/0017_showing_covid_capacity_and_more.py new file mode 100644 index 0000000000000000000000000000000000000000..319043ba1124a83f3cc5c88ec0783868761bf1c8 --- /dev/null +++ b/cinema/migrations/0017_showing_covid_capacity_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 4.1.5 on 2023-04-23 17:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("cinema", "0016_delete_seat"), + ] + + operations = [ + migrations.AddField( + model_name="showing", + name="covid_capacity", + field=models.IntegerField(default=0), + ), + migrations.AddField( + model_name="showing", + name="last_seat_number_assigned", + field=models.IntegerField(default=0), + ), + ] diff --git a/cinema/migrations/__pycache__/0014_showing_social_distancing_seat.cpython-310.pyc b/cinema/migrations/__pycache__/0014_showing_social_distancing_seat.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8cd47da895070d2a80be8d41683ea9041654c3e9 Binary files /dev/null and b/cinema/migrations/__pycache__/0014_showing_social_distancing_seat.cpython-310.pyc differ diff --git a/cinema/migrations/__pycache__/0015_remove_showing_adult_price_and_more.cpython-310.pyc b/cinema/migrations/__pycache__/0015_remove_showing_adult_price_and_more.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b4ef3948a3b3ff94aeb00b7670e792056921fe81 Binary files /dev/null and b/cinema/migrations/__pycache__/0015_remove_showing_adult_price_and_more.cpython-310.pyc differ diff --git a/cinema/migrations/__pycache__/0016_delete_seat.cpython-310.pyc b/cinema/migrations/__pycache__/0016_delete_seat.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d59927f4e06ccd415e07d4d32ab49f863cd49a4d Binary files /dev/null and b/cinema/migrations/__pycache__/0016_delete_seat.cpython-310.pyc differ diff --git a/cinema/migrations/__pycache__/0017_showing_covid_capacity_and_more.cpython-310.pyc b/cinema/migrations/__pycache__/0017_showing_covid_capacity_and_more.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ea8161304b0dbed77ae287b11e365b816599ad1 Binary files /dev/null and b/cinema/migrations/__pycache__/0017_showing_covid_capacity_and_more.cpython-310.pyc differ diff --git a/cinema/models.py b/cinema/models.py index 1a24cdfa3a95bb409b3950804fe03d3810420158..0afa544e9d56e9185cbffe9bfe08d58e66b2cf96 100644 --- a/cinema/models.py +++ b/cinema/models.py @@ -1,7 +1,4 @@ from django.db import models - -from authentication.models import User - # Create your models here. class Cinema(models.Model): @@ -26,7 +23,7 @@ class Screen(models.Model): cinema = models.ForeignKey(Cinema, on_delete=models.CASCADE, related_name='screens') screen_number = models.PositiveSmallIntegerField() seating_capacity = models.PositiveIntegerField() - + class Showing(models.Model): id = models.AutoField(primary_key=True) @@ -35,19 +32,29 @@ class Showing(models.Model): start_time = models.DateTimeField() end_time = models.DateTimeField(null=True) available_seats = models.IntegerField() - adult_price = models.FloatField() - student_price = models.FloatField() - child_price = models.FloatField() + # adult_price = models.FloatField() + # student_price = models.FloatField() + # child_price = models.FloatField() + social_distancing = models.BooleanField(default=False) + last_seat_number_assigned = models.IntegerField(default=0) + covid_capacity = models.IntegerField(default=0) - def save(self, *args, **kwargs): - self.end_time = self.start_time + self.film.length - super().save(*args, **kwargs) - def __str__(self): out = self.start_time.strftime("%d %B %Y %I:%M%p") out = out + " - " + self.film.title return out + def save(self, *args, **kwargs): + self.end_time = self.start_time + self.film.length + super().save(*args, **kwargs) + + +# class Seat(models.Model): +# row = models.CharField(max_length=1) +# number = models.IntegerField() +# screen = models.ForeignKey(Screen, on_delete=models.CASCADE, related_name='seats') + + class Ticket(models.Model): productID = models.AutoField(primary_key=True) name = models.CharField(max_length=255) diff --git a/cinema/templates/cinema/create_showing.html b/cinema/templates/cinema/create_showing.html index 5100af40cf424994232cf245d2000b1fb691b213..f5fa82da045bb92175ec40bafd9df00e3e342d7e 100644 --- a/cinema/templates/cinema/create_showing.html +++ b/cinema/templates/cinema/create_showing.html @@ -1,6 +1,4 @@ -{% extends 'base.html' %} - -{% block content %} +{% extends 'base.html' %} {% block content %} <div class="container mt-4"> <h1>Create Showing</h1> @@ -10,7 +8,7 @@ <label for="film">Film</label> <select class="form-control" id="film" name="film"> {% for film in films %} - <option value="{{ film.id }}">{{ film.title }}</option> + <option value="{{ film.id }}">{{ film.title }}</option> {% endfor %} </select> </div> @@ -18,19 +16,39 @@ <label for="screen">Screen</label> <select class="form-control" id="screen" name="screen" required> {% for screen in screens %} - <option value="{{ screen.id }}">{{ screen.cinema.name }} - Screen {{ screen.screen_number }}</option> + <option value="{{ screen.id }}"> + {{ screen.cinema.name }} - Screen {{ screen.screen_number }} + </option> {% endfor %} </select> </div> <div class="form-group"> <label for="start_time">Start Time</label> - <input type="datetime-local" class="form-control" id="start_time" name="start_time" required> + <input + type="datetime-local" + class="form-control" + id="start_time" + name="start_time" + required + /> + </div> + <div class="form-group form-check"> + <input + type="checkbox" + class="form-check-input" + id="social_distancing" + name="social_distancing" + /> + <label class="form-check-label" for="social_distancing" + >Social Distancing</label + > </div> <div class="form-group"> <button type="submit" class="btn btn-primary mr-2">Create Showing</button> - <a href="{% url 'create_film' %}" class="btn btn-secondary">Create Film</a> + <a href="{% url 'create_film' %}" class="btn btn-secondary" + >Create Film</a + > </div> - </form> </div> -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/cinema/urls.py b/cinema/urls.py index df0ccf74fd42cbfa4e095c6a4c0e982dc5554b56..24a557f4c4467190a6b6969cad43899f1e9e916f 100644 --- a/cinema/urls.py +++ b/cinema/urls.py @@ -17,7 +17,7 @@ urlpatterns = [ path('delete_film/<int:pk>', views.film_delete, name='delete_film'), path('showings/<int:pk>', views.film_showings, name='film_showings'), path('manage_tickets/', views.tickets_list, name='manage_tickets'), - path('manage_tickets/create_ticket/', views.tickets_create, name='create_ticket'), - path('manage_tickets/delete_ticket/<int:pk>/', views.tickets_delete, name='delete_ticket'), - path('manage_tickets/update_ticket/<int:pk>/', views.tickets_update, name='update_ticket'), + #path('manage_tickets/create_ticket/', views.tickets_create, name='create_ticket'), + #path('manage_tickets/delete_ticket/<int:pk>/', views.tickets_delete, name='delete_ticket'), + #path('manage_tickets/update_ticket/<int:pk>/', views.tickets_update, name='update_ticket'), ] diff --git a/cinema/views.py b/cinema/views.py index 6226adb7a67683a6bc4d959e060d36c6d08a25aa..dd5a08e661fe95e774c30f08b6b12cf237fece06 100644 --- a/cinema/views.py +++ b/cinema/views.py @@ -125,17 +125,6 @@ def screen_delete(request, pk): return redirect('screen_list') def showing_create(request): - """ - Handle a user creating a showing from an accompanying screen - - Returns: - If the request method is "POST" and the form is not valid: - The showing form, with the error message passed in as context. - If the request method is not "POST": - The showing form form. - Otherwise: - A redirect to the screen management page. - """ perms = get_user_permissions(request) if perms == '0' or perms == '1': return redirect('no_access') @@ -151,8 +140,13 @@ def showing_create(request): print(form.errors) if form.is_valid(): f = form.save(commit=False) - f.available_seats = screen.seating_capacity - f.save() + if not f.social_distancing: + f.available_seats = screen.seating_capacity + f.save() + else: + f.covid_capacity = screen.seating_capacity / 2 + f.available_seats = f.covid_capacity + f.save() return redirect('manage_screens') else: print('invalid form') @@ -230,4 +224,8 @@ def film_showings(request, pk): return render(request, 'cinema/film_showings.html', context) except AttributeError as e: context = {'film': film, 'showings': showings} - return render(request, 'cinema/film_showings.html', context) \ No newline at end of file + return render(request, 'cinema/film_showings.html', context) + + +def tickets_list(request): + pass \ No newline at end of file diff --git a/clubs/__pycache__/forms.cpython-310.pyc b/clubs/__pycache__/forms.cpython-310.pyc index b207d66296a9ad351af0494115d457dfd1c8f660..b7295f18adbaf3e04a175cef0ee13642ffcc1ccd 100644 Binary files a/clubs/__pycache__/forms.cpython-310.pyc and b/clubs/__pycache__/forms.cpython-310.pyc differ diff --git a/clubs/__pycache__/models.cpython-310.pyc b/clubs/__pycache__/models.cpython-310.pyc index b7971be96c1398ec2541aa42dcdbe280a018e2f5..bf979b55c25984da3a67eb2eee01cc21a8c57d28 100644 Binary files a/clubs/__pycache__/models.cpython-310.pyc and b/clubs/__pycache__/models.cpython-310.pyc differ diff --git a/clubs/__pycache__/urls.cpython-310.pyc b/clubs/__pycache__/urls.cpython-310.pyc index 15705d14ef32476705636715dfb5d569e8df041d..0b054ef35fa6458410201503fe50806ac7c0f138 100644 Binary files a/clubs/__pycache__/urls.cpython-310.pyc and b/clubs/__pycache__/urls.cpython-310.pyc differ diff --git a/clubs/__pycache__/views.cpython-310.pyc b/clubs/__pycache__/views.cpython-310.pyc index 24c660515263ca7c1ce68dc054876e9fdf6e3a9f..49b6420cc8e790f8d53a036ed5875d1f16ee9f0c 100644 Binary files a/clubs/__pycache__/views.cpython-310.pyc and b/clubs/__pycache__/views.cpython-310.pyc differ diff --git a/clubs/forms.py b/clubs/forms.py index ff9fe52c39cca49c84bf6987baf1b07fd31b5c11..c5854f738d7bae6324f95faab23904b1b5bec403 100644 --- a/clubs/forms.py +++ b/clubs/forms.py @@ -1,5 +1,5 @@ from django import forms -from .models import Club, ClubRepresentative +from .models import Club, ClubRepresentative, ClubDiscountRequest class ClubForm(forms.ModelForm): class Meta: @@ -11,4 +11,8 @@ class ClubRepresentativeForm(forms.ModelForm): class Meta: model = ClubRepresentative fields = ['email', 'first_name', 'last_name', 'password'] - \ No newline at end of file + +class ClubDiscountRequestForm(forms.ModelForm): + class Meta: + model = ClubDiscountRequest + fields = ['new_discount_rate', 'reason'] \ No newline at end of file diff --git a/clubs/migrations/0005_clubdiscountrequest.py b/clubs/migrations/0005_clubdiscountrequest.py new file mode 100644 index 0000000000000000000000000000000000000000..d1974a324163d6f06ca0bb9ec6fdeaf9cd1417ca --- /dev/null +++ b/clubs/migrations/0005_clubdiscountrequest.py @@ -0,0 +1,39 @@ +# Generated by Django 4.1.5 on 2023-04-23 17:48 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + dependencies = [ + ("clubs", "0004_club_active"), + ] + + operations = [ + migrations.CreateModel( + name="ClubDiscountRequest", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("old_discount_rate", models.FloatField(default=0)), + ("new_discount_rate", models.FloatField(default=0)), + ("reason", models.CharField(max_length=255)), + ("approved", models.BooleanField(default=False)), + ( + "club", + models.ForeignKey( + on_delete=django.db.models.deletion.PROTECT, + related_name="discount_requests", + to="clubs.club", + ), + ), + ], + ), + ] diff --git a/clubs/migrations/__pycache__/0004_club_active.cpython-310.pyc b/clubs/migrations/__pycache__/0004_club_active.cpython-310.pyc index 8cc2f874f4f8b20e48c0923247044b34f491080d..870ff9209a08de00eb512aeab41f4daffee52971 100644 Binary files a/clubs/migrations/__pycache__/0004_club_active.cpython-310.pyc and b/clubs/migrations/__pycache__/0004_club_active.cpython-310.pyc differ diff --git a/clubs/migrations/__pycache__/0005_clubdiscountrequest.cpython-310.pyc b/clubs/migrations/__pycache__/0005_clubdiscountrequest.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..62195a06fd88c9a58b3b81750475509a7e2ccd2c Binary files /dev/null and b/clubs/migrations/__pycache__/0005_clubdiscountrequest.cpython-310.pyc differ diff --git a/clubs/models.py b/clubs/models.py index 19f137767b479284ade47d059c2debc5b19d0515..4df7ec2a56d85dfcb6b9d0d2d83388882d5dd979 100644 --- a/clubs/models.py +++ b/clubs/models.py @@ -38,4 +38,11 @@ class Club(models.Model): null=True, blank=True ) - active = models.BooleanField(default=False) \ No newline at end of file + active = models.BooleanField(default=False) + +class ClubDiscountRequest(models.Model): + club = models.ForeignKey(Club, on_delete=models.PROTECT, related_name='discount_requests') + old_discount_rate = models.FloatField(default=0) + new_discount_rate = models.FloatField(default=0) + reason = models.CharField(max_length=255) + approved = models.BooleanField(default=False) \ No newline at end of file diff --git a/clubs/templates/clubs/create_club_discount_request.html b/clubs/templates/clubs/create_club_discount_request.html new file mode 100644 index 0000000000000000000000000000000000000000..15de5fefd5b1e7ea64ee3feaed5d333dba170bf9 --- /dev/null +++ b/clubs/templates/clubs/create_club_discount_request.html @@ -0,0 +1,31 @@ +{% extends 'base.html' %} {% block content %} +<div class="container"> + <div class="row"> + <div class="col-md-8 offset-md-2"> + <h1>Create Club Discount Request</h1> + <form method="post"> + {% csrf_token %} + <div class="form-group"> + <label for="{{ form.new_discount_rate.id_for_label }}" + >New Discount Rate:</label + > + {{ form.new_discount_rate }} {% if form.new_discount_rate.errors %} + <div class="invalid-feedback"> + {%for error in form.new_discount_rate.errors%}{{ error }}{%endfor%} + </div> + {% endif %} + </div> + <div class="form-group"> + <label for="{{ form.reason.id_for_label }}">Reason:</label> + {{ form.reason }} {% if form.reason.errors %} + <div class="invalid-feedback"> + {% for error in form.reason.errors %} {{ error }} {% endfor %} + </div> + {% endif %} + </div> + <button type="submit" class="btn btn-primary">Submit</button> + </form> + </div> + </div> +</div> +{% endblock %} diff --git a/clubs/templates/clubs/manage_clubs.html b/clubs/templates/clubs/manage_clubs.html index f6a5726a637ec60b01f0ef53ae6d462a13822c5c..416c803718f7008fd6de538c0270829042f78b02 100644 --- a/clubs/templates/clubs/manage_clubs.html +++ b/clubs/templates/clubs/manage_clubs.html @@ -137,6 +137,58 @@ {% endif %} </tbody> </table> + <h2>Club Discount Requests</h2> + <table class="table table-striped mt-3"> + <thead> + <tr> + <th>Club Name</th> + <th>Old Rate</th> + <th>Requested Rate</th> + <th>Reason</th> + <th>Action</th> + </tr> + </thead> + <tbody> + {% if discount_requests %} {% for request in discount_requests %} + <tr> + <td>{{ request.club.name }}</td> + <td>{{ request.old_discount_rate }}</td> + <td>{{ request.new_discount_rate }}</td> + <td>{{ request.reason }}</td> + <td> + <div class="dropdown"> + <button + class="btn btn-secondary dropdown-toggle" + type="button" + id="dropdownMenuButton" + data-toggle="dropdown" + aria-haspopup="true" + aria-expanded="false" + > + Actions + </button> + <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> + <a + class="dropdown-item" + href="{% url 'approve_club_discount_request' pk=request.pk %}" + >Approve</a + > + <a + class="dropdown-item" + href="{% url 'deny_club_discount_request' pk=request.pk %}" + >Deny</a + > + </div> + </div> + </td> + </tr> + {% endfor %} {% else %} + <tr> + <td colspan="4">No Requests to display.</td> + </tr> + {% endif %} + </tbody> + </table> </div> {% endblock %} diff --git a/clubs/templates/clubs/view_club.html b/clubs/templates/clubs/view_club.html index 14014888af8fbe89f87b90d24e667a688c200918..680200a4845a17563f729748e49e6ab8ee127445 100644 --- a/clubs/templates/clubs/view_club.html +++ b/clubs/templates/clubs/view_club.html @@ -4,14 +4,14 @@ <ul class="list-group mt-3"> <li class="list-group-item">Representative: {{ representative.linked_user.first_name }}{{ representative.linked_user.last_name }}</li> <li class="list-group-item"> - Address: {{ club.street_number }} {{ club.street }}, {{ club.city }}, {{ - club.postcode }} + Address: {{ club.street_number }} {{ club.street }}, {{ club.city }}, {{ club.postcode }} </li> <li class="list-group-item"> Telephone Number: {{ club.telephone_number }} </li> <li class="list-group-item">Mobile Number: {{ club.mobile_number }}</li> <li class="list-group-item">Email Address: {{ club.email_address }}</li> + <li class="list-group-item">Discount Rate: {{ club.account.discount_rate }}</li> </ul> <div class="mt-4"> @@ -40,5 +40,9 @@ >Delete Club</a > </div> + <div class="mt-4"> + <a href="{% url 'create_club_discount_request' club.pk %}" class="btn btn-danger mr-3" + >Request New Discount Rate</a> + </div> </div> {% endblock %} diff --git a/clubs/urls.py b/clubs/urls.py index 1e3fc0656fc47815ec0d0e6fbf58f07db6a07456..1f9be9676a6eeedccefbac9eb042c5265e14536f 100644 --- a/clubs/urls.py +++ b/clubs/urls.py @@ -12,4 +12,7 @@ urlpatterns = [ path('view_club/<int:pk>/', views.view_club, name='view_club'), path('deactivate_club/<int:pk>/', views.deactivate_club, name='deactivate_club'), path('activate_club/<int:pk>/', views.activate_club, name='activate_club'), + path('create_club_discount_request/<int:pk>/', views.create_club_discount_request, name='create_club_discount_request'), + path('approve_club_discount_request/<int:pk>/', views.approve_club_discount_request, name='approve_club_discount_request'), + path('deny_club_discount_request/<int:pk>', views.deny_club_discount_request, name='deny_club_discount_request'), ] diff --git a/clubs/views.py b/clubs/views.py index 2f2a91c80854caa97ac955557b9e491c164c45c0..f50cf3d52dc06fbe1d7ce9a255fd7493c5094b7b 100644 --- a/clubs/views.py +++ b/clubs/views.py @@ -4,8 +4,8 @@ from utils.custom_decorators import get_user_permissions, is_in_group from django.contrib.auth.decorators import login_required from django.contrib.auth.hashers import make_password from authentication.models import User -from .models import Club, ClubRepresentative -from .forms import ClubForm, ClubRepresentativeForm +from .models import Club, ClubRepresentative, ClubDiscountRequest +from .forms import ClubForm, ClubRepresentativeForm, ClubDiscountRequestForm from accounts.models import Account, ClubAccount import random import datetime @@ -19,9 +19,10 @@ def clubs_list(request): clubs = Club.objects.filter(active=True) clubs_for_approval = Club.objects.filter(active=False) + discount_requests = ClubDiscountRequest.objects.filter(approved=False) accounts = ClubAccount.objects.all() #representatives = User.objects.all().filter(club_rep=True) - context = {'user': request.user, 'clubs': clubs, 'inactive_clubs': clubs_for_approval,'accounts': accounts, 'perms': perms} + context = {'user': request.user, 'clubs': clubs, 'inactive_clubs': clubs_for_approval,'accounts': accounts, 'discount_requests': discount_requests, 'perms': perms} return render(request, 'clubs/manage_clubs.html', context) @login_required @@ -148,3 +149,39 @@ def representative_update(request, pk): context = {'form': form, 'user': request.user, 'club': club, 'representative': representative, 'perms': perms} return render(request, 'clubs/update_representative.html', context) +def create_club_discount_request(request, pk): + perms = get_user_permissions(request) + if perms == '0': + return redirect('no_access') + if request.method == 'POST': + club = get_object_or_404(Club, pk=pk) + form = ClubDiscountRequestForm(request.POST) + if form.is_valid(): + club_discount_request = form.save(commit=False) + club_discount_request.club = club + club_discount_request.old_discount_rate = club.account.discount_rate + club_discount_request.save() + return redirect('index') + else: + form = ClubDiscountRequestForm() + return render(request, 'clubs/create_club_discount_request.html', {'perms': perms, 'form': form}) + +def approve_club_discount_request(request, pk): + perms = get_user_permissions(request) + if perms == '0' or perms == '1': + return redirect('no_access') + discount_request = get_object_or_404(ClubDiscountRequest, pk=pk) + club = discount_request.club + club.account.discount_rate = discount_request.discount_rate + club.account.save() + discount_request.delete() + return redirect('clubs_list') + +def deny_club_discount_request(request, pk): + perms = get_user_permissions(request) + if perms == '0' or perms == '1': + return redirect('no_access') + + discount_request = get_object_or_404(ClubDiscountRequest, pk=pk) + discount_request.delete() + return redirect('clubs_list') \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 index 3f6553891d5b750de43720ed88ac65deea25bb90..443b5a5f2d813493372190708cc5bb6afd8b0a8f 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ