refactor: move some fields and widgets around

This commit is contained in:
Herculino Trotta
2024-10-10 22:37:10 -03:00
parent d2cd115751
commit 5b66ac7fac
8 changed files with 110 additions and 69 deletions

View File

@@ -14,7 +14,7 @@ from apps.common.fields.forms.dynamic_select import (
from apps.common.widgets.crispy.submit import NoClassSubmit from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.common.widgets.tom_select import TomSelect from apps.common.widgets.tom_select import TomSelect
from apps.transactions.models import TransactionCategory, TransactionTag from apps.transactions.models import TransactionCategory, TransactionTag
from apps.transactions.widgets import ArbitraryDecimalDisplayNumberInput from apps.common.widgets.decimal import ArbitraryDecimalDisplayNumberInput
class AccountGroupForm(forms.ModelForm): class AccountGroupForm(forms.ModelForm):

View File

@@ -4,10 +4,10 @@ from django import forms
from django.db import models from django.db import models
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from apps.transactions.widgets import MonthYearWidget from apps.common.widgets.month_year import MonthYearWidget
class MonthYearField(models.DateField): class MonthYearModelField(models.DateField):
def to_python(self, value): def to_python(self, value):
if value is None or isinstance(value, datetime.date): if value is None or isinstance(value, datetime.date):
return value return value
@@ -27,6 +27,8 @@ class MonthYearField(models.DateField):
class MonthYearFormField(forms.DateField): class MonthYearFormField(forms.DateField):
widget = MonthYearWidget
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.input_formats = ["%Y-%m"] self.input_formats = ["%Y-%m"]
@@ -34,11 +36,13 @@ class MonthYearFormField(forms.DateField):
def to_python(self, value): def to_python(self, value):
if value in self.empty_values: if value in self.empty_values:
return None return None
if isinstance(value, datetime.datetime):
return value.date()
try: try:
date = datetime.datetime.strptime(value, "%Y-%m") date = datetime.datetime.strptime(value, "%Y-%m")
return date.replace(day=1).date() return date.replace(day=1).date()
except ValueError: except ValueError:
raise ValidationError("Invalid date format. Use YYYY-MM.") raise ValidationError(_("Invalid date format. Use YYYY-MM."))
def prepare_value(self, value): def prepare_value(self, value):
if isinstance(value, datetime.date): if isinstance(value, datetime.date):

View File

@@ -1,9 +1,6 @@
from datetime import datetime, date
from django import forms
from decimal import Decimal, InvalidOperation from decimal import Decimal, InvalidOperation
from django.template.defaultfilters import floatformat from django import forms
from django.utils.formats import get_format, number_format from django.utils.formats import get_format, number_format
@@ -25,19 +22,6 @@ def convert_to_decimal(value: str):
return None return None
class MonthYearWidget(forms.DateInput):
"""
Custom widget to display a month-year picker.
"""
input_type = "month" # Set the input type to 'month'
def format_value(self, value):
if isinstance(value, (datetime, date)):
return value.strftime("%Y-%m")
return value
class ArbitraryDecimalDisplayNumberInput(forms.TextInput): class ArbitraryDecimalDisplayNumberInput(forms.TextInput):
"""A widget for displaying and inputing decimal numbers with the least amount of trailing zeros possible. You """A widget for displaying and inputing decimal numbers with the least amount of trailing zeros possible. You
must set this on your Form's __init__ method.""" must set this on your Form's __init__ method."""

View File

@@ -0,0 +1,16 @@
from datetime import datetime, date
from django import forms
class MonthYearWidget(forms.DateInput):
"""
Custom widget to display a month-year picker.
"""
input_type = "month" # Set the input type to 'month'
def format_value(self, value):
if isinstance(value, (datetime, date)):
return value.strftime("%Y-%m")
return value

View File

@@ -1,11 +1,10 @@
from crispy_bootstrap5.bootstrap5 import Switch from crispy_bootstrap5.bootstrap5 import Switch
from crispy_forms.bootstrap import FormActions from crispy_forms.bootstrap import FormActions
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Row, Column, Field, Fieldset from crispy_forms.layout import Layout, Row, Column, Field
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django import forms from django import forms
from django.db import transaction from django.db import transaction
from django.utils.safestring import mark_safe
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from apps.accounts.models import Account from apps.accounts.models import Account
@@ -14,17 +13,15 @@ from apps.common.fields.forms.dynamic_select import (
DynamicModelMultipleChoiceField, DynamicModelMultipleChoiceField,
) )
from apps.common.widgets.crispy.submit import NoClassSubmit from apps.common.widgets.crispy.submit import NoClassSubmit
from apps.common.widgets.tom_select import TomSelect, TomSelectMultiple from apps.common.widgets.tom_select import TomSelect
from apps.transactions.models import ( from apps.transactions.models import (
Transaction, Transaction,
TransactionCategory, TransactionCategory,
TransactionTag, TransactionTag,
InstallmentPlan, InstallmentPlan,
) )
from apps.transactions.widgets import ( from apps.common.widgets.decimal import ArbitraryDecimalDisplayNumberInput
ArbitraryDecimalDisplayNumberInput, from apps.common.fields.month_year import MonthYearFormField
MonthYearWidget,
)
class TransactionForm(forms.ModelForm): class TransactionForm(forms.ModelForm):
@@ -40,6 +37,7 @@ class TransactionForm(forms.ModelForm):
required=False, required=False,
label=_("Tags"), label=_("Tags"),
) )
reference_date = MonthYearFormField(label=_("Reference Date"), required=False)
class Meta: class Meta:
model = Transaction model = Transaction
@@ -190,7 +188,7 @@ class TransferForm(forms.Form):
date = forms.DateField( date = forms.DateField(
label="Date", widget=forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d") label="Date", widget=forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d")
) )
reference_date = forms.CharField(label="Reference Date", widget=MonthYearWidget()) reference_date = MonthYearFormField(label=_("Reference Date"), required=False)
description = forms.CharField(max_length=500, label="Description") description = forms.CharField(max_length=500, label="Description")
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -324,6 +322,7 @@ class InstallmentPlanForm(forms.Form):
label=_("Start Date"), label=_("Start Date"),
widget=forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d"), widget=forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d"),
) )
reference_date = MonthYearFormField(label=_("Reference Date"), required=False)
description = forms.CharField(max_length=500, label=_("Description")) description = forms.CharField(max_length=500, label=_("Description"))
number_of_installments = forms.IntegerField( number_of_installments = forms.IntegerField(
min_value=1, label=_("Number of Installments") min_value=1, label=_("Number of Installments")
@@ -372,9 +371,13 @@ class InstallmentPlanForm(forms.Form):
"account", "account",
"description", "description",
Row( Row(
Column("start_date", css_class="form-group col-md-4 mb-0"), Column("number_of_installments", css_class="form-group col-md-6 mb-0"),
Column("number_of_installments", css_class="form-group col-md-4 mb-0"), Column("recurrence", css_class="form-group col-md-6 mb-0"),
Column("recurrence", css_class="form-group col-md-4 mb-0"), css_class="form-row",
),
Row(
Column("start_date", css_class="form-group col-md-6 mb-0"),
Column("reference_date", css_class="form-group col-md-6 mb-0"),
css_class="form-row", css_class="form-row",
), ),
"installment_amount", "installment_amount",
@@ -396,12 +399,16 @@ class InstallmentPlanForm(forms.Form):
number_of_installments = self.cleaned_data["number_of_installments"] number_of_installments = self.cleaned_data["number_of_installments"]
transaction_type = self.cleaned_data["type"] transaction_type = self.cleaned_data["type"]
start_date = self.cleaned_data["start_date"] start_date = self.cleaned_data["start_date"]
reference_date = self.cleaned_data["reference_date"] or start_date
recurrence = self.cleaned_data["recurrence"] recurrence = self.cleaned_data["recurrence"]
account = self.cleaned_data["account"] account = self.cleaned_data["account"]
description = self.cleaned_data["description"] description = self.cleaned_data["description"]
installment_amount = self.cleaned_data["installment_amount"] installment_amount = self.cleaned_data["installment_amount"]
category = self.cleaned_data["category"] category = self.cleaned_data["category"]
print(reference_date, type(reference_date))
print(start_date, type(start_date))
with transaction.atomic(): with transaction.atomic():
installment_plan = InstallmentPlan.objects.create( installment_plan = InstallmentPlan.objects.create(
account=account, account=account,
@@ -421,11 +428,13 @@ class InstallmentPlanForm(forms.Form):
delta = relativedelta(days=i) delta = relativedelta(days=i)
transaction_date = start_date + delta transaction_date = start_date + delta
transaction_reference_date = (reference_date + delta).replace(day=1)
new_transaction = Transaction.objects.create( new_transaction = Transaction.objects.create(
account=account, account=account,
type=transaction_type, type=transaction_type,
date=transaction_date, date=transaction_date,
reference_date=transaction_date.replace(day=1), is_paid=False,
reference_date=transaction_reference_date,
amount=installment_amount, amount=installment_amount,
description=description, description=description,
notes=f"{i + 1}/{number_of_installments}", notes=f"{i + 1}/{number_of_installments}",

View File

@@ -1,6 +1,6 @@
# Generated by Django 5.1.1 on 2024-09-19 02:11 # Generated by Django 5.1.1 on 2024-09-19 02:11
import apps.transactions.fields import apps.common.fields.month_year
from django.db import migrations, models from django.db import migrations, models
@@ -8,19 +8,31 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = []
]
operations = [ operations = [
migrations.CreateModel( migrations.CreateModel(
name='Transaction', name="Transaction",
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), (
('is_paid', models.BooleanField(default=True)), "id",
('date', models.DateField()), models.BigAutoField(
('reference_date', apps.transactions.fields.MonthYearField(help_text='Please enter a month and year in the format MM/YYYY.')), auto_created=True,
('description', models.CharField(max_length=500)), primary_key=True,
('notes', models.TextField(blank=True)), serialize=False,
verbose_name="ID",
),
),
("is_paid", models.BooleanField(default=True)),
("date", models.DateField()),
(
"reference_date",
apps.common.fields.month_year.MonthYearModelField(
help_text="Please enter a month and year in the format MM/YYYY."
),
),
("description", models.CharField(max_length=500)),
("notes", models.TextField(blank=True)),
], ],
), ),
] ]

View File

@@ -1,6 +1,6 @@
# Generated by Django 5.1.1 on 2024-09-19 13:35 # Generated by Django 5.1.1 on 2024-09-19 13:35
import apps.transactions.fields import apps.common.fields.month_year
import apps.transactions.validators import apps.transactions.validators
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models from django.db import migrations, models
@@ -9,46 +9,62 @@ from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
dependencies = [ dependencies = [
('accounts', '0001_initial'), ("accounts", "0001_initial"),
('transactions', '0001_initial'), ("transactions", "0001_initial"),
] ]
operations = [ operations = [
migrations.AddField( migrations.AddField(
model_name='transaction', model_name="transaction",
name='account', name="account",
field=models.ForeignKey(default=0, on_delete=django.db.models.deletion.PROTECT, to='accounts.account', verbose_name='Account'), field=models.ForeignKey(
default=0,
on_delete=django.db.models.deletion.PROTECT,
to="accounts.account",
verbose_name="Account",
),
preserve_default=False, preserve_default=False,
), ),
migrations.AddField( migrations.AddField(
model_name='transaction', model_name="transaction",
name='amount', name="amount",
field=models.DecimalField(decimal_places=18, default=0, max_digits=30, validators=[apps.transactions.validators.validate_non_negative, apps.transactions.validators.validate_decimal_places], verbose_name='Amount'), field=models.DecimalField(
decimal_places=18,
default=0,
max_digits=30,
validators=[
apps.transactions.validators.validate_non_negative,
apps.transactions.validators.validate_decimal_places,
],
verbose_name="Amount",
),
preserve_default=False, preserve_default=False,
), ),
migrations.AlterField( migrations.AlterField(
model_name='transaction', model_name="transaction",
name='date', name="date",
field=models.DateField(verbose_name='Date'), field=models.DateField(verbose_name="Date"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='transaction', model_name="transaction",
name='description', name="description",
field=models.CharField(max_length=500, verbose_name='Description'), field=models.CharField(max_length=500, verbose_name="Description"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='transaction', model_name="transaction",
name='is_paid', name="is_paid",
field=models.BooleanField(default=True, verbose_name='Paid'), field=models.BooleanField(default=True, verbose_name="Paid"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='transaction', model_name="transaction",
name='notes', name="notes",
field=models.TextField(blank=True, verbose_name='Notes'), field=models.TextField(blank=True, verbose_name="Notes"),
), ),
migrations.AlterField( migrations.AlterField(
model_name='transaction', model_name="transaction",
name='reference_date', name="reference_date",
field=apps.transactions.fields.MonthYearField(verbose_name='Reference Date'), field=apps.common.fields.month_year.MonthYearModelField(
verbose_name="Reference Date"
),
), ),
] ]

View File

@@ -6,9 +6,9 @@ from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from apps.common.functions.decimals import truncate_decimal from apps.common.functions.decimals import truncate_decimal
from apps.transactions.fields import MonthYearField
from apps.transactions.validators import validate_decimal_places, validate_non_negative from apps.transactions.validators import validate_decimal_places, validate_non_negative
from apps.currencies.utils.convert import convert from apps.currencies.utils.convert import convert
from apps.common.fields.month_year import MonthYearModelField
class TransactionCategory(models.Model): class TransactionCategory(models.Model):
@@ -76,7 +76,7 @@ class Transaction(models.Model):
) )
is_paid = models.BooleanField(default=True, verbose_name=_("Paid")) is_paid = models.BooleanField(default=True, verbose_name=_("Paid"))
date = models.DateField(verbose_name=_("Date")) date = models.DateField(verbose_name=_("Date"))
reference_date = MonthYearField(verbose_name=_("Reference Date")) reference_date = MonthYearModelField(verbose_name=_("Reference Date"))
amount = models.DecimalField( amount = models.DecimalField(
max_digits=42, max_digits=42,