diff --git a/main/admin.py b/main/admin.py index 3b7cd99..b8fdae8 100644 --- a/main/admin.py +++ b/main/admin.py @@ -1,5 +1,6 @@ from django.contrib import admin -from .models import FuelPurchase +from .models import FuelPurchase, MonthlyTrip admin.site.register(FuelPurchase) +admin.site.register(MonthlyTrip) diff --git a/main/migrations/0001_initial.py b/main/migrations/0001_initial.py index fd8440e..2119b5e 100644 --- a/main/migrations/0001_initial.py +++ b/main/migrations/0001_initial.py @@ -1,5 +1,7 @@ -# Generated by Django 5.2.5 on 2025-08-09 17:20 +# Generated by Django 5.2.5 on 2025-08-10 13:10 +import datetime +import django.db.models.deletion from django.db import migrations, models @@ -11,19 +13,37 @@ class Migration(migrations.Migration): ] operations = [ + migrations.CreateModel( + name='MonthlyTrip', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('year', models.PositiveIntegerField(verbose_name='Year')), + ('month', models.PositiveSmallIntegerField(choices=[(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April'), (5, 'May'), (6, 'June'), (7, 'July'), (8, 'August'), (9, 'September'), (10, 'October'), (11, 'November'), (12, 'December')], verbose_name='Month')), + ('kilometers', models.PositiveIntegerField(default=0, verbose_name='Kilometers')), + ], + options={ + 'verbose_name': 'Monthly Trip', + 'verbose_name_plural': 'Monthly Trips', + 'constraints': [models.UniqueConstraint(fields=('year', 'month'), name='unique_monthly_trip')], + }, + ), migrations.CreateModel( name='FuelPurchase', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('purchase_date', models.DateField(verbose_name='Purchase Date')), + ('purchase_date', models.DateField(default=datetime.date.today, verbose_name='Purchase Date')), ('total_cost', models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Total Cost (€)')), ('price_per_litre', models.DecimalField(decimal_places=3, max_digits=6, verbose_name='Price per Litre (€)')), ('amount_litres', models.DecimalField(decimal_places=2, max_digits=7, verbose_name='Amount (litres)')), + ('octane', models.PositiveSmallIntegerField(choices=[(95, '95'), (98, '98')], default=95, verbose_name='Octane')), + ('gas_station', models.PositiveSmallIntegerField(choices=[(0, 'Unknown'), (1, 'Neste'), (2, 'Teboil'), (3, 'ABC'), (4, 'Shell'), (5, 'St1'), (6, 'SEO'), (7, 'Ysi5')], default=0, verbose_name='Gas Station')), + ('car', models.PositiveSmallIntegerField(choices=[(0, 'Unknown'), (1, 'Renault'), (2, 'Nissan'), (3, 'Smart')], default=0, verbose_name='Car')), + ('monthly_trip', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='fuel_purchases', to='main.monthlytrip', verbose_name='Monthly Trip')), ], options={ 'verbose_name': 'Fuel Purchase', 'verbose_name_plural': 'Fuel Purchases', - 'constraints': [models.UniqueConstraint(fields=('purchase_date', 'total_cost', 'price_per_litre', 'amount_litres'), name='unique_fuel_purchase'), models.CheckConstraint(condition=models.Q(('total_cost__gt', 0), ('total_cost__lt', 100)), name='total_cost_range'), models.CheckConstraint(condition=models.Q(('price_per_litre__gt', 0), ('price_per_litre__lt', 5)), name='price_per_litre_range'), models.CheckConstraint(condition=models.Q(('amount_litres__gt', 0), ('amount_litres__lt', 100)), name='amount_litres_range')], + 'constraints': [models.UniqueConstraint(fields=('purchase_date', 'total_cost', 'price_per_litre', 'amount_litres', 'octane', 'gas_station', 'car'), name='unique_fuel_purchase'), models.CheckConstraint(condition=models.Q(('total_cost__gt', 0), ('total_cost__lt', 1000)), name='total_cost_range'), models.CheckConstraint(condition=models.Q(('price_per_litre__gt', 0), ('price_per_litre__lt', 5)), name='price_per_litre_range'), models.CheckConstraint(condition=models.Q(('amount_litres__gt', 0), ('amount_litres__lt', 100)), name='amount_litres_range')], }, ), ] diff --git a/main/models.py b/main/models.py index 857d2da..dd21523 100644 --- a/main/models.py +++ b/main/models.py @@ -1,20 +1,41 @@ from datetime import date from django.db import models -from django.db.models import F, Q +from django.db.models import Q + + +class MonthlyTrip(models.Model): + MONTH_CHOICES = [ + (1, 'January'), (2, 'February'), (3, 'March'), (4, 'April'), + (5, 'May'), (6, 'June'), (7, 'July'), (8, 'August'), + (9, 'September'), (10, 'October'), (11, 'November'), (12, 'December'), + ] + + year = models.PositiveIntegerField(verbose_name="Year") + month = models.PositiveSmallIntegerField(choices=MONTH_CHOICES, verbose_name="Month") + kilometers = models.PositiveIntegerField(default=0, verbose_name="Kilometers") + + class Meta: + constraints = [ + models.UniqueConstraint(fields=['year', 'month'], name='unique_monthly_trip') + ] + verbose_name = "Monthly Trip" + verbose_name_plural = "Monthly Trips" + + def __str__(self): + return f"{self.month:02d}.{self.year} : {self.kilometers} km" class FuelPurchase(models.Model): - GAS_STATION_CHOICES = [ - (0, '-'), + (0, 'Unknown'), (1, 'Neste'), (2, 'Teboil'), (3, 'ABC'), (4, 'Shell'), (5, 'St1'), (6, 'SEO'), - (7, 'ysi5'), + (7, 'Ysi5'), ] OCTANE_CHOICES = [ @@ -23,19 +44,20 @@ class FuelPurchase(models.Model): ] CAR_CHOICES = [ - (0, '_'), + (0, 'Unknown'), (1, 'Renault'), (2, 'Nissan'), (3, 'Smart'), ] - purchase_date = models.DateField(null=False, blank=False, default=date.today, verbose_name="Purchase Date") - total_cost = models.DecimalField(null=False, blank=False, max_digits=10, decimal_places=2, verbose_name="Total Cost (€)") - price_per_litre = models.DecimalField(null=False, blank=False, max_digits=6, decimal_places=3, verbose_name="Price per Litre (€)") - amount_litres = models.DecimalField(null=False, blank=False, max_digits=7, decimal_places=2, verbose_name="Amount (litres)") - octane = models.IntegerField(choices=OCTANE_CHOICES, default=95, verbose_name="Octane") - gas_station = models.IntegerField(choices=GAS_STATION_CHOICES, default=1, verbose_name="Gas Station") - car = models.IntegerField(choices=CAR_CHOICES, default=3, verbose_name="Car") + purchase_date = models.DateField(default=date.today, verbose_name="Purchase Date") + total_cost = models.DecimalField(max_digits=10, decimal_places=2, verbose_name="Total Cost (€)") + price_per_litre = models.DecimalField(max_digits=6, decimal_places=3, verbose_name="Price per Litre (€)") + amount_litres = models.DecimalField(max_digits=7, decimal_places=2, verbose_name="Amount (litres)") + octane = models.PositiveSmallIntegerField(choices=OCTANE_CHOICES, default=95, verbose_name="Octane") + gas_station = models.PositiveSmallIntegerField(choices=GAS_STATION_CHOICES, default=0, verbose_name="Gas Station") + car = models.PositiveSmallIntegerField(choices=CAR_CHOICES, default=0, verbose_name="Car") + monthly_trip = models.ForeignKey(MonthlyTrip, on_delete=models.CASCADE, related_name='fuel_purchases', verbose_name="Monthly Trip") class Meta: constraints = [ @@ -51,25 +73,28 @@ class FuelPurchase(models.Model): ], name='unique_fuel_purchase' ), - models.CheckConstraint( - check=Q(total_cost__gt=0) & Q(total_cost__lt=100), - name="total_cost_range" - ), - models.CheckConstraint( - check=Q(price_per_litre__gt=0) & Q(price_per_litre__lt=5), - name="price_per_litre_range" - ), - models.CheckConstraint( - check=Q(amount_litres__gt=0) & Q(amount_litres__lt=100), - name="amount_litres_range" - ), + models.CheckConstraint(check=Q(total_cost__gt=0) & Q(total_cost__lt=1000), name="total_cost_range"), + models.CheckConstraint(check=Q(price_per_litre__gt=0) & Q(price_per_litre__lt=5), name="price_per_litre_range"), + models.CheckConstraint(check=Q(amount_litres__gt=0) & Q(amount_litres__lt=100), name="amount_litres_range"), ] verbose_name = "Fuel Purchase" verbose_name_plural = "Fuel Purchases" def save(self, *args, **kwargs): + if not self.amount_litres and self.price_per_litre: self.amount_litres = self.total_cost / self.price_per_litre + + if not self.monthly_trip_id: + month = self.purchase_date.month + year = self.purchase_date.year + monthly_trip, created = MonthlyTrip.objects.get_or_create( + year=year, + month=month, + defaults={'kilometers': 0} + ) + self.monthly_trip = monthly_trip + super().save(*args, **kwargs) def __str__(self): diff --git a/main/templates/main/fuelpurchase_list.html b/main/templates/main/fuelpurchase_list.html index c1603e5..ca8f13b 100644 --- a/main/templates/main/fuelpurchase_list.html +++ b/main/templates/main/fuelpurchase_list.html @@ -37,6 +37,7 @@
Month | + {% for year in years %} +{{ year }} | + {% endfor %} +
---|---|
{{ month_names|dict_get:month_num }} | + {% for year in years %} +{{ table_data|dict_get:month_num|dict_get:year }} | + {% endfor %} +
Total (km) | + {% for year in years %} +{{ year_totals|dict_get:year }} | + {% endfor %} +