diff --git a/app/apps/accounts/forms.py b/app/apps/accounts/forms.py index ba83092..748c8d0 100644 --- a/app/apps/accounts/forms.py +++ b/app/apps/accounts/forms.py @@ -53,6 +53,7 @@ class AccountGroupForm(forms.ModelForm): class AccountForm(forms.ModelForm): group = DynamicModelChoiceField( + create_field="name", label=_("Group"), model=AccountGroup, required=False, @@ -112,6 +113,7 @@ class AccountBalanceForm(forms.Form): max_digits=42, decimal_places=30, required=False, label=_("New balance") ) category = DynamicModelChoiceField( + create_field="name", model=TransactionCategory, required=False, label=_("Category"), diff --git a/app/apps/transactions/forms.py b/app/apps/transactions/forms.py index bcdc200..e2b7ecc 100644 --- a/app/apps/transactions/forms.py +++ b/app/apps/transactions/forms.py @@ -8,6 +8,7 @@ from crispy_forms.layout import ( Field, ) from django import forms +from django.db.models import Q from django.utils.translation import gettext_lazy as _ from apps.accounts.models import Account @@ -32,9 +33,11 @@ from apps.rules.signals import transaction_created, transaction_updated class TransactionForm(forms.ModelForm): category = DynamicModelChoiceField( + create_field="name", model=TransactionCategory, required=False, label=_("Category"), + queryset=TransactionCategory.objects.filter(active=True), ) tags = DynamicModelMultipleChoiceField( model=TransactionTag, @@ -42,6 +45,7 @@ class TransactionForm(forms.ModelForm): create_field="name", required=False, label=_("Tags"), + queryset=TransactionTag.objects.filter(active=True), ) entities = DynamicModelMultipleChoiceField( model=TransactionEntity, @@ -81,6 +85,24 @@ class TransactionForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + # if editing a transaction display non-archived items and it's own item even if it's archived + if self.instance.id: + self.fields["account"].queryset = Account.objects.filter( + Q(is_archived=False) | Q(transactions=self.instance.id) + ).distinct() + + self.fields["category"].queryset = TransactionCategory.objects.filter( + Q(active=True) | Q(transaction=self.instance.id) + ).distinct() + + self.fields["tags"].queryset = TransactionTag.objects.filter( + Q(active=True) | Q(transaction=self.instance.id) + ).distinct() + + self.fields["entities"].queryset = TransactionEntity.objects.filter( + Q(active=True) | Q(transactions=self.instance.id) + ).distinct() + self.helper = FormHelper() self.helper.form_tag = False self.helper.form_method = "post" @@ -181,14 +203,18 @@ class TransferForm(forms.Form): ) from_category = DynamicModelChoiceField( + create_field="name", model=TransactionCategory, required=False, label=_("Category"), + queryset=TransactionCategory.objects.filter(active=True), ) to_category = DynamicModelChoiceField( + create_field="name", model=TransactionCategory, required=False, label=_("Category"), + queryset=TransactionCategory.objects.filter(active=True), ) from_tags = DynamicModelMultipleChoiceField( @@ -197,6 +223,7 @@ class TransferForm(forms.Form): create_field="name", required=False, label=_("Tags"), + queryset=TransactionTag.objects.filter(active=True), ) to_tags = DynamicModelMultipleChoiceField( model=TransactionTag, @@ -204,6 +231,7 @@ class TransferForm(forms.Form): create_field="name", required=False, label=_("Tags"), + queryset=TransactionTag.objects.filter(active=True), ) date = forms.DateField( @@ -358,11 +386,14 @@ class InstallmentPlanForm(forms.ModelForm): create_field="name", required=False, label=_("Tags"), + queryset=TransactionTag.objects.filter(active=True), ) category = DynamicModelChoiceField( + create_field="name", model=TransactionCategory, required=False, label=_("Category"), + queryset=TransactionCategory.objects.filter(active=True), ) entities = DynamicModelMultipleChoiceField( model=TransactionEntity, @@ -370,6 +401,7 @@ class InstallmentPlanForm(forms.ModelForm): create_field="name", required=False, label=_("Entities"), + queryset=TransactionEntity.objects.filter(active=True), ) type = forms.ChoiceField(choices=Transaction.Type.choices) reference_date = MonthYearFormField(label=_("Reference Date"), required=False) @@ -401,6 +433,24 @@ class InstallmentPlanForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + # if editing display non-archived items and it's own item even if it's archived + if self.instance.id: + self.fields["account"].queryset = Account.objects.filter( + Q(is_archived=False) | Q(installmentplan=self.instance.id) + ).distinct() + + self.fields["category"].queryset = TransactionCategory.objects.filter( + Q(active=True) | Q(installmentplan=self.instance.id) + ).distinct() + + self.fields["tags"].queryset = TransactionTag.objects.filter( + Q(active=True) | Q(installmentplan=self.instance.id) + ).distinct() + + self.fields["entities"].queryset = TransactionEntity.objects.filter( + Q(active=True) | Q(installmentplan=self.instance.id) + ).distinct() + self.helper = FormHelper() self.helper.form_tag = False self.helper.form_method = "post" @@ -470,7 +520,7 @@ class InstallmentPlanForm(forms.ModelForm): class TransactionTagForm(forms.ModelForm): class Meta: model = TransactionTag - fields = ["name"] + fields = ["name", "active"] labels = {"name": _("Tag name")} def __init__(self, *args, **kwargs): @@ -479,7 +529,7 @@ class TransactionTagForm(forms.ModelForm): self.helper = FormHelper() self.helper.form_tag = False self.helper.form_method = "post" - self.helper.layout = Layout(Field("name", css_class="mb-3")) + self.helper.layout = Layout(Field("name", css_class="mb-3"), Switch("active")) if self.instance and self.instance.pk: self.helper.layout.append( @@ -502,7 +552,7 @@ class TransactionTagForm(forms.ModelForm): class TransactionEntityForm(forms.ModelForm): class Meta: model = TransactionEntity - fields = ["name"] + fields = ["name", "active"] labels = {"name": _("Entity name")} def __init__(self, *args, **kwargs): @@ -511,7 +561,7 @@ class TransactionEntityForm(forms.ModelForm): self.helper = FormHelper() self.helper.form_tag = False self.helper.form_method = "post" - self.helper.layout = Layout(Field("name", css_class="mb-3")) + self.helper.layout = Layout(Field("name"), Switch("active")) if self.instance and self.instance.pk: self.helper.layout.append( @@ -534,7 +584,7 @@ class TransactionEntityForm(forms.ModelForm): class TransactionCategoryForm(forms.ModelForm): class Meta: model = TransactionCategory - fields = ["name", "mute"] + fields = ["name", "mute", "active"] labels = {"name": _("Category name")} help_texts = { "mute": _("Muted categories won't count towards your monthly total") @@ -546,7 +596,7 @@ class TransactionCategoryForm(forms.ModelForm): self.helper = FormHelper() self.helper.form_tag = False self.helper.form_method = "post" - self.helper.layout = Layout(Field("name", css_class="mb-3"), Switch("mute")) + self.helper.layout = Layout(Field("name"), Switch("mute"), Switch("active")) if self.instance and self.instance.pk: self.helper.layout.append( @@ -578,11 +628,14 @@ class RecurringTransactionForm(forms.ModelForm): create_field="name", required=False, label=_("Tags"), + queryset=TransactionTag.objects.filter(active=True), ) category = DynamicModelChoiceField( + create_field="name", model=TransactionCategory, required=False, label=_("Category"), + queryset=TransactionCategory.objects.filter(active=True), ) entities = DynamicModelMultipleChoiceField( model=TransactionEntity, @@ -590,6 +643,7 @@ class RecurringTransactionForm(forms.ModelForm): create_field="name", required=False, label=_("Entities"), + queryset=TransactionEntity.objects.filter(active=True), ) type = forms.ChoiceField(choices=Transaction.Type.choices) reference_date = MonthYearFormField(label=_("Reference Date"), required=False) @@ -624,6 +678,25 @@ class RecurringTransactionForm(forms.ModelForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + + # if editing display non-archived items and it's own item even if it's archived + if self.instance.id: + self.fields["account"].queryset = Account.objects.filter( + Q(is_archived=False) | Q(recurringtransaction=self.instance.id) + ).distinct() + + self.fields["category"].queryset = TransactionCategory.objects.filter( + Q(active=True) | Q(recurringtransaction=self.instance.id) + ).distinct() + + self.fields["tags"].queryset = TransactionTag.objects.filter( + Q(active=True) | Q(recurringtransaction=self.instance.id) + ).distinct() + + self.fields["entities"].queryset = TransactionEntity.objects.filter( + Q(active=True) | Q(recurringtransaction=self.instance.id) + ).distinct() + self.helper = FormHelper() self.helper.form_method = "post" self.helper.form_tag = False diff --git a/app/apps/transactions/migrations/0025_transactioncategory_active_transactiontag_active.py b/app/apps/transactions/migrations/0025_transactioncategory_active_transactiontag_active.py new file mode 100644 index 0000000..5b5794e --- /dev/null +++ b/app/apps/transactions/migrations/0025_transactioncategory_active_transactiontag_active.py @@ -0,0 +1,23 @@ +# Generated by Django 5.1.3 on 2025-01-04 19:04 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('transactions', '0024_installmentplan_entities_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='transactioncategory', + name='active', + field=models.BooleanField(default=True, help_text="Deactivated categories won't be able to be selected when creating new transactions", verbose_name='Active'), + ), + migrations.AddField( + model_name='transactiontag', + name='active', + field=models.BooleanField(default=True, help_text="Deactivated tags won't be able to be selected when creating new transactions", verbose_name='Active'), + ), + ] diff --git a/app/apps/transactions/migrations/0026_transactionentity_active.py b/app/apps/transactions/migrations/0026_transactionentity_active.py new file mode 100644 index 0000000..4f66003 --- /dev/null +++ b/app/apps/transactions/migrations/0026_transactionentity_active.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.3 on 2025-01-04 19:05 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('transactions', '0025_transactioncategory_active_transactiontag_active'), + ] + + operations = [ + migrations.AddField( + model_name='transactionentity', + name='active', + field=models.BooleanField(default=True, help_text="Deactivated entities won't be able to be selected when creating new transactions", verbose_name='Active'), + ), + ] diff --git a/app/apps/transactions/models.py b/app/apps/transactions/models.py index 8728a53..b503887 100644 --- a/app/apps/transactions/models.py +++ b/app/apps/transactions/models.py @@ -18,6 +18,13 @@ logger = logging.getLogger() class TransactionCategory(models.Model): name = models.CharField(max_length=255, verbose_name=_("Name"), unique=True) mute = models.BooleanField(default=False, verbose_name=_("Mute")) + active = models.BooleanField( + default=True, + verbose_name=_("Active"), + help_text=_( + "Deactivated categories won't be able to be selected when creating new transactions" + ), + ) class Meta: verbose_name = _("Transaction Category") @@ -30,6 +37,13 @@ class TransactionCategory(models.Model): class TransactionTag(models.Model): name = models.CharField(max_length=255, verbose_name=_("Name"), unique=True) + active = models.BooleanField( + default=True, + verbose_name=_("Active"), + help_text=_( + "Deactivated tags won't be able to be selected when creating new transactions" + ), + ) class Meta: verbose_name = _("Transaction Tags") @@ -42,8 +56,13 @@ class TransactionTag(models.Model): class TransactionEntity(models.Model): name = models.CharField(max_length=255, verbose_name=_("Name")) - - # Add any other fields you might want for entities + active = models.BooleanField( + default=True, + verbose_name=_("Active"), + help_text=_( + "Deactivated entities won't be able to be selected when creating new transactions" + ), + ) class Meta: verbose_name = _("Entity")