From f37b0544ea30d54ed265da75a124bcbf276f6eda Mon Sep 17 00:00:00 2001 From: Prince Kumar Date: Fri, 5 Jun 2026 23:14:00 +0530 Subject: [PATCH] feat: add GlobalConstants model and integrate into rebate logic --- home/admin.py | 50 +++++++++++++++---- home/context_processors.py | 8 ++- home/migrations/0010_globalconstants.py | 27 ++++++++++ ...ants_short_rebate_period_limit_and_more.py | 23 +++++++++ home/models/__init__.py | 2 +- home/models/home.py | 34 +++++++++++++ home/utils/rebate_checker.py | 40 +++++++++------ home/views.py | 19 ++++--- messWebsite/settings.py | 1 + templates/rebateForm.html | 6 +-- 10 files changed, 172 insertions(+), 38 deletions(-) create mode 100644 home/migrations/0010_globalconstants.py create mode 100644 home/migrations/0011_alter_globalconstants_short_rebate_period_limit_and_more.py diff --git a/home/admin.py b/home/admin.py index 46ff4f4..af14c67 100644 --- a/home/admin.py +++ b/home/admin.py @@ -37,6 +37,7 @@ StudentBills, UnregisteredStudent, Update, + GlobalConstants, ) from home.utils.rebate_checker import max_days_rebate @@ -79,7 +80,18 @@ ) REBATE_BILLS_DESC_TEXT = "This contains the rebate bills of each students." -# Register your models here +@admin.register(GlobalConstants) +class GlobalConstantsAdmin(admin.ModelAdmin): + model = GlobalConstants + + def has_add_permission(self, request): + # Only allow one instance of GlobalConstants + if self.model.objects.count() > 0: + return False + return super().has_add_permission(request) + + def has_delete_permission(self, request, obj=None): + return False @admin.register(About) @@ -451,11 +463,19 @@ def set_semester(modeladmin, request, queryset: list[LongRebate]): set_semester.__name__ = f"get_rebate_days_per_caterer_{semester}" return set_semester - try: - for semester in Semester.objects.all(): - actions.append(set_semester_action(semester.name)) - except Exception as e: - print("Semester table not available", e) + def get_actions(self, request): + actions = super().get_actions(request) + try: + for semester in Semester.objects.all(): + action = self.set_semester_action(semester.name) + actions[action.__name__] = ( + action, + action.__name__, + action.short_description, + ) + except Exception as e: + print("Semester table not available", e) + return actions @admin.register(Rebate) @@ -679,11 +699,19 @@ def set_period(modeladmin, request, queryset): set_period.__name__ = f"set_period_{semester_name}_{period_sno}" return set_period - try: - for period in Period.objects.all(): - actions.append(set_period_action(period.semester.name, period.Sno)) - except Exception as e: - print("Periods table not available", e) + def get_actions(self, request): + actions = super().get_actions(request) + try: + for period in Period.objects.all(): + action = self.set_period_action(period.semester.name, period.Sno) + actions[action.__name__] = ( + action, + action.__name__, + action.short_description, + ) + except Exception as e: + print("Periods table not available", e) + return actions @admin.action(description="Allocate the unregistered students") def allocate(self, request, queryset): diff --git a/home/context_processors.py b/home/context_processors.py index f48c787..238742c 100644 --- a/home/context_processors.py +++ b/home/context_processors.py @@ -1,4 +1,4 @@ -from .models import Caterer +from .models import Caterer, GlobalConstants """ File-name: context_processors.py @@ -9,4 +9,8 @@ def base(request): caterer = Caterer.objects.filter(visible=True).all() - return {"all_caterer": caterer} + constants = GlobalConstants.objects.first() + if not constants: + # Provide defaults if no constants exist yet + constants = GlobalConstants.objects.create() + return {"all_caterer": caterer, "constants": constants} diff --git a/home/migrations/0010_globalconstants.py b/home/migrations/0010_globalconstants.py new file mode 100644 index 0000000..2e6c0cb --- /dev/null +++ b/home/migrations/0010_globalconstants.py @@ -0,0 +1,27 @@ +# Generated by Django 5.1.14 on 2026-06-05 17:41 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0009_student_photo'), + ] + + operations = [ + migrations.CreateModel( + name='GlobalConstants', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('short_rebate_stretch_limit', models.IntegerField(default=7, help_text='Maximum number of days for a single short rebate application.', verbose_name='Short Rebate Stretch Limit')), + ('short_rebate_period_limit', models.IntegerField(default=8, help_text='Maximum total days allowed for short rebates within a single period.', verbose_name='Short Rebate Period Limit')), + ('min_rebate_days', models.IntegerField(default=2, help_text='Minimum number of days required for a rebate application.', verbose_name='Minimum Rebate Days')), + ('days_prior_notice', models.IntegerField(default=2, help_text='Minimum days before leave commencement the form must be filled.', verbose_name='Days Prior Notice')), + ], + options={ + 'verbose_name': 'Global Constant', + 'verbose_name_plural': 'Global Constants', + }, + ), + ] diff --git a/home/migrations/0011_alter_globalconstants_short_rebate_period_limit_and_more.py b/home/migrations/0011_alter_globalconstants_short_rebate_period_limit_and_more.py new file mode 100644 index 0000000..27ddb92 --- /dev/null +++ b/home/migrations/0011_alter_globalconstants_short_rebate_period_limit_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.14 on 2026-06-05 17:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0010_globalconstants'), + ] + + operations = [ + migrations.AlterField( + model_name='globalconstants', + name='short_rebate_period_limit', + field=models.IntegerField(default=10, help_text='Maximum total days allowed for short rebates within a single period.', verbose_name='Short Rebate Period Limit'), + ), + migrations.AlterField( + model_name='globalconstants', + name='short_rebate_stretch_limit', + field=models.IntegerField(default=10, help_text='Maximum number of days for a single short rebate application.', verbose_name='Short Rebate Stretch Limit'), + ), + ] diff --git a/home/models/__init__.py b/home/models/__init__.py index 0360fbe..f4d9622 100644 --- a/home/models/__init__.py +++ b/home/models/__init__.py @@ -6,7 +6,7 @@ from .caterer import Caterer from .contacts import Contact from .fees import Fee -from .home import About, Carousel, Update +from .home import About, Carousel, GlobalConstants, Update from .links import Form from .rules import Rule, ShortRebate from .students import ( diff --git a/home/models/home.py b/home/models/home.py index 29be4d2..03109bd 100644 --- a/home/models/home.py +++ b/home/models/home.py @@ -56,3 +56,37 @@ def __str__(self): class Meta: verbose_name = "Update" verbose_name_plural = "Updates" + + +class GlobalConstants(models.Model): + """ + Stores globally accessible constants for the website like rebate limits. + """ + + short_rebate_stretch_limit = models.IntegerField( + _("Short Rebate Stretch Limit"), + default=10, + help_text="Maximum number of days for a single short rebate application.", + ) + short_rebate_period_limit = models.IntegerField( + _("Short Rebate Period Limit"), + default=10, + help_text="Maximum total days allowed for short rebates within a single period.", + ) + min_rebate_days = models.IntegerField( + _("Minimum Rebate Days"), + default=2, + help_text="Minimum number of days required for a rebate application.", + ) + days_prior_notice = models.IntegerField( + _("Days Prior Notice"), + default=2, + help_text="Minimum days before leave commencement the form must be filled.", + ) + + def __str__(self): + return "Global Constants" + + class Meta: + verbose_name = "Global Constant" + verbose_name_plural = "Global Constants" diff --git a/home/utils/rebate_checker.py b/home/utils/rebate_checker.py index 84f39e4..039e624 100644 --- a/home/utils/rebate_checker.py +++ b/home/utils/rebate_checker.py @@ -1,6 +1,12 @@ from datetime import timedelta -from home.models import LeftShortRebate, LongRebate, Rebate, StudentBills +from home.models import ( + GlobalConstants, + LeftShortRebate, + LongRebate, + Rebate, + StudentBills, +) def count(start, end): @@ -54,41 +60,47 @@ def is_not_duplicate(student, new_rebate_start, new_rebate_end): def max_days_rebate(student, start, end, period): """ Checks what period rebate is being applied, - if the rebate does not exceeds 8 days for that period approves the rebate and + if the rebate does not exceeds the set limit for that period approves the rebate and adds the rebate to student bills(Commented out this feature for now, as administration wants to approve it from its side before adding to student bills) """ + constants = GlobalConstants.objects.first() + if not constants: + constants = GlobalConstants.objects.create() + + limit = constants.short_rebate_period_limit + student_bill, _ = StudentBills.objects.get_or_create( email=student, semester=period.semester ) sum = count(start, end) match period.Sno: case 1: - if student_bill.period1_short + sum <= 8: + if student_bill.period1_short + sum <= limit: return -1 else: - return 8 - student_bill.period1_short + return limit - student_bill.period1_short case 2: - if student_bill.period2_short + sum <= 8: + if student_bill.period2_short + sum <= limit: return -1 else: - return 8 - student_bill.period2_short + return limit - student_bill.period2_short case 3: - if student_bill.period3_short + sum <= 8: + if student_bill.period3_short + sum <= limit: return -1 else: - return 8 - student_bill.period3_short + return limit - student_bill.period3_short case 4: - if student_bill.period4_short + sum <= 8: + if student_bill.period4_short + sum <= limit: return -1 else: - return 8 - student_bill.period4_short + return limit - student_bill.period4_short case 5: - if student_bill.period5_short + sum <= 8: + if student_bill.period5_short + sum <= limit: return -1 else: - return 8 - student_bill.period5_short + return limit - student_bill.period5_short case 6: - if student_bill.period6_short + sum <= 8: + if student_bill.period6_short + sum <= limit: return -1 else: - return 8 - student_bill.period6_short + return limit - student_bill.period6_short diff --git a/home/views.py b/home/views.py index d5b3309..f7af5af 100644 --- a/home/views.py +++ b/home/views.py @@ -20,6 +20,7 @@ Caterer, Contact, Form, + GlobalConstants, LeftShortRebate, LongRebate, Menu, @@ -187,20 +188,24 @@ def rebate(request): request.session["text"] = message return redirect(request.path) + constants = GlobalConstants.objects.first() + if not constants: + constants = GlobalConstants.objects.create() + try: start_date = parse_date(request.POST["start_date"]) end_date = parse_date(request.POST["end_date"]) rebate_days = ((end_date - start_date).days) + 1 before_rebate_days = (start_date - date.today()).days - if rebate_days > 7: - message = "Max no of days for rebate is 7" - elif before_rebate_days < 2: - message = "Form needs to be filled atleast 2 days prior the comencement of leave." + if rebate_days > constants.short_rebate_stretch_limit: + message = f"Max no of days for rebate is {constants.short_rebate_stretch_limit}" + elif before_rebate_days < constants.days_prior_notice: + message = f"Form needs to be filled atleast {constants.days_prior_notice} days prior the comencement of leave." elif not is_not_duplicate(student, start_date, end_date): message = "You have already applied for rebate during this duration" - elif rebate_days < 2: - message = "Min no of days for rebate is 2" + elif rebate_days < constants.min_rebate_days: + message = f"Min no of days for rebate is {constants.min_rebate_days}" else: additional_message = "" if not period_obj.start_date <= start_date <= period_obj.end_date: @@ -233,7 +238,7 @@ def rebate(request): ) if upper_cap_check >= 0: message = ( - "You can only apply for max 8 days in a period. Days left for this period: " + f"You can only apply for max {constants.short_rebate_period_limit} days in a period. Days left for this period: " + str(upper_cap_check) ) elif not message: diff --git a/messWebsite/settings.py b/messWebsite/settings.py index 793ae83..fa911c4 100644 --- a/messWebsite/settings.py +++ b/messWebsite/settings.py @@ -61,6 +61,7 @@ "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", + "allauth.account.middleware.AccountMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", "messWebsite.middleware.LoginRequiredMiddleware", diff --git a/templates/rebateForm.html b/templates/rebateForm.html index 7648757..f22ab2d 100644 --- a/templates/rebateForm.html +++ b/templates/rebateForm.html @@ -8,9 +8,9 @@

Short Term Rebate Form

    -
  • Rebate can be availed for a minimum of 2 days, upto 7 days at a stretch.
  • -
  • Maximum of 8 days rebate per month will be provided through the short term rebate forms.
  • -
  • Fill the form atleast 2 days prior the comencement of leave, else the application will not be accepted.
  • +
  • Rebate can be availed for a minimum of {{ constants.min_rebate_days }} days, upto {{ constants.short_rebate_stretch_limit }} days at a stretch.
  • +
  • Maximum of {{ constants.short_rebate_period_limit }} days rebate per period will be provided through the short term rebate forms.
  • +
  • Fill the form atleast {{ constants.days_prior_notice }} days prior the comencement of leave, else the application will not be accepted.