diff --git a/expenses/admin.py b/expenses/admin.py index bbf5ff9..c8f8cc7 100644 --- a/expenses/admin.py +++ b/expenses/admin.py @@ -12,7 +12,7 @@ from django.db import models from util import next_payday from .admin_base import AdminBase, DeletedListFilter, DeletableAdminForm -from .models import Expense, ExpenseCategory, Timesheet, TimesheetRate +from .models import Expense, ExpenseCategory, Timesheet, TimesheetRate, Vendor from django import forms from django.utils import timezone from django.contrib.admin.widgets import AdminDateWidget, AdminSplitDateTime @@ -23,6 +23,21 @@ from admincharts.utils import months_between_dates from djmoney.contrib.exchange.models import convert_money +class VendorAdminForm(DeletableAdminForm): + class Meta: + model = Vendor + fields = "__all__" + + +@admin.register(Vendor) +class VendorAdmin(AdminBase): + list_display = ( + "name", + "description", + ) + form = VendorAdminForm + + class TimesheetRateAdminForm(DeletableAdminForm): class Meta: model = TimesheetRate @@ -169,8 +184,8 @@ class ExpenseAdminForm(DeletableAdminForm): class ExpenseAdmin(AdminBase, AdminChartMixin, ImportExportModelAdmin): form = ExpenseAdminForm - list_display = ("date", "price", "description", "category") - list_filter = ("date", ("deleted", DeletedListFilter), "category") + list_display = ("date", "price", "description", "category", "vendor") + list_filter = ("date", ("deleted", DeletedListFilter), "category", "vendor") ordering = ("-date", "-deleted") readonly_fields = ("deleted",) diff --git a/expenses/migrations/0004_vendor_remove_expense_merchant_and_more.py b/expenses/migrations/0004_vendor_remove_expense_merchant_and_more.py new file mode 100644 index 0000000..c97c07e --- /dev/null +++ b/expenses/migrations/0004_vendor_remove_expense_merchant_and_more.py @@ -0,0 +1,68 @@ +# Generated by Django 4.2.5 on 2023-09-21 04:31 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import simple_history.models + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('expenses', '0003_remove_expensecategory_default_rate_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Vendor', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('deleted', models.BooleanField(default=False)), + ('name', models.TextField(blank=True)), + ('description', models.TextField(blank=True)), + ], + options={ + 'abstract': False, + }, + ), + migrations.RemoveField( + model_name='expense', + name='merchant', + ), + migrations.RemoveField( + model_name='historicalexpense', + name='merchant', + ), + migrations.CreateModel( + name='HistoricalVendor', + fields=[ + ('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')), + ('deleted', models.BooleanField(default=False)), + ('name', models.TextField(blank=True)), + ('description', models.TextField(blank=True)), + ('history_id', models.AutoField(primary_key=True, serialize=False)), + ('history_date', models.DateTimeField(db_index=True)), + ('history_change_reason', models.CharField(max_length=100, null=True)), + ('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)), + ('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'verbose_name': 'historical vendor', + 'verbose_name_plural': 'historical vendors', + 'ordering': ('-history_date', '-history_id'), + 'get_latest_by': ('history_date', 'history_id'), + }, + bases=(simple_history.models.HistoricalChanges, models.Model), + ), + migrations.AddField( + model_name='expense', + name='vendor', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='expenses', to='expenses.vendor'), + ), + migrations.AddField( + model_name='historicalexpense', + name='vendor', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='expenses.vendor'), + ), + ] diff --git a/expenses/models.py b/expenses/models.py index 65acb4a..00ae59e 100644 --- a/expenses/models.py +++ b/expenses/models.py @@ -108,6 +108,14 @@ class ExpenseCategory(DeletableModel): return truncate_string(self.name, 48) +class Vendor(DeletableModel): + name = models.TextField(blank=True) + description = models.TextField(blank=True) + + def __str__(self): + return truncate_string(self.name, 48) + + class Expense(DeletableModel): price = MoneyField( decimal_places=3, @@ -117,7 +125,13 @@ class Expense(DeletableModel): ) date = models.DateField() description = models.TextField(blank=True) - merchant = models.CharField(max_length=255, blank=True) + vendor = models.ForeignKey( + Vendor, + on_delete=models.SET_NULL, + null=True, + blank=True, + related_name="expenses", + ) receipt = models.FileField(upload_to="receipts/", blank=True) link = models.URLField(blank=True) category = models.ForeignKey(