mirror of
https://github.com/eitchtee/WYGIWYH.git
synced 2026-04-27 02:58:40 +02:00
changes
This commit is contained in:
@@ -1,12 +1,26 @@
|
||||
from crispy_bootstrap5.bootstrap5 import Switch
|
||||
from crispy_forms.bootstrap import FormActions
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Layout, Row, Column, Field, Fieldset
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django import forms
|
||||
from django.db import transaction
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Layout, Submit, Row, Column, Div, Field, Hidden
|
||||
|
||||
from apps.accounts.models import Account
|
||||
from .models import Transaction, TransactionCategory, TransactionTag
|
||||
from apps.common.fields.forms.dynamic_select import (
|
||||
DynamicModelChoiceField,
|
||||
DynamicModelMultipleChoiceField,
|
||||
)
|
||||
from apps.common.widgets.crispy.submit import NoClassSubmit
|
||||
from apps.common.widgets.tom_select import TomSelect, TomSelectMultiple
|
||||
from apps.transactions.models import (
|
||||
Transaction,
|
||||
TransactionCategory,
|
||||
TransactionTag,
|
||||
InstallmentPlan,
|
||||
)
|
||||
from apps.transactions.widgets import (
|
||||
ArbitraryDecimalDisplayNumberInput,
|
||||
MonthYearWidget,
|
||||
@@ -14,6 +28,19 @@ from apps.transactions.widgets import (
|
||||
|
||||
|
||||
class TransactionForm(forms.ModelForm):
|
||||
category = DynamicModelChoiceField(
|
||||
model=TransactionCategory,
|
||||
required=False,
|
||||
label=_("Category"),
|
||||
)
|
||||
tags = DynamicModelMultipleChoiceField(
|
||||
model=TransactionTag,
|
||||
to_field_name="name",
|
||||
create_field="name",
|
||||
required=False,
|
||||
label=_("Tags"),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Transaction
|
||||
fields = [
|
||||
@@ -31,20 +58,21 @@ class TransactionForm(forms.ModelForm):
|
||||
widgets = {
|
||||
"date": forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d"),
|
||||
"notes": forms.Textarea(attrs={"rows": 3}),
|
||||
"account": TomSelect(),
|
||||
}
|
||||
labels = {
|
||||
"tags": mark_safe('<i class="fa-solid fa-hashtag me-1"></i>' + _("Tags")),
|
||||
"category": mark_safe(
|
||||
'<i class="fa-solid fa-icons me-1"></i>' + _("Category")
|
||||
),
|
||||
"notes": mark_safe(
|
||||
'<i class="fa-solid fa-align-justify me-1"></i>' + _("Notes")
|
||||
),
|
||||
"amount": mark_safe('<i class="fa-solid fa-coins me-1"></i>' + _("Amount")),
|
||||
"description": mark_safe(
|
||||
'<i class="fa-solid fa-quote-left me-1"></i>' + _("Name")
|
||||
),
|
||||
}
|
||||
# labels = {
|
||||
# "tags": mark_safe('<i class="fa-solid fa-hashtag me-1"></i>' + _("Tags")),
|
||||
# "category": mark_safe(
|
||||
# '<i class="fa-solid fa-icons me-1"></i>' + _("Category")
|
||||
# ),
|
||||
# "notes": mark_safe(
|
||||
# '<i class="fa-solid fa-align-justify me-1"></i>' + _("Notes")
|
||||
# ),
|
||||
# "amount": mark_safe('<i class="fa-solid fa-coins me-1"></i>' + _("Amount")),
|
||||
# "description": mark_safe(
|
||||
# '<i class="fa-solid fa-quote-left me-1"></i>' + _("Name")
|
||||
# ),
|
||||
# }
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
@@ -66,28 +94,59 @@ class TransactionForm(forms.ModelForm):
|
||||
),
|
||||
"description",
|
||||
Field("amount", inputmode="decimal"),
|
||||
Field("category", css_class="select"),
|
||||
Field("tags", css_class="multiselect", size=1),
|
||||
Row(
|
||||
Column("category", css_class="form-group col-md-6 mb-0"),
|
||||
Column("tags", css_class="form-group col-md-6 mb-0"),
|
||||
css_class="form-row",
|
||||
),
|
||||
"notes",
|
||||
Submit("submit", "Save", css_class="btn btn-warning"),
|
||||
)
|
||||
|
||||
self.fields["reference_date"].required = False
|
||||
|
||||
if self.instance and self.instance.pk:
|
||||
decimal_places = self.instance.account.currency.decimal_places
|
||||
self.fields["amount"].widget = ArbitraryDecimalDisplayNumberInput(
|
||||
decimal_places=decimal_places
|
||||
)
|
||||
else:
|
||||
self.fields["amount"].widget = ArbitraryDecimalDisplayNumberInput(
|
||||
decimal_places=2
|
||||
self.helper.layout.append(
|
||||
FormActions(
|
||||
NoClassSubmit(
|
||||
"submit", _("Update"), css_class="btn btn-outline-primary w-100"
|
||||
),
|
||||
),
|
||||
)
|
||||
else:
|
||||
self.fields["amount"].widget = ArbitraryDecimalDisplayNumberInput()
|
||||
self.helper.layout.append(
|
||||
FormActions(
|
||||
NoClassSubmit(
|
||||
"submit", _("Add"), css_class="btn btn-outline-primary w-100"
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
date = cleaned_data.get("date")
|
||||
reference_date = cleaned_data.get("reference_date")
|
||||
|
||||
if date and not reference_date:
|
||||
cleaned_data["reference_date"] = date.replace(day=1)
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class TransferForm(forms.Form):
|
||||
from_account = forms.ModelChoiceField(
|
||||
queryset=Account.objects.all(), label="From Account"
|
||||
queryset=Account.objects.all(),
|
||||
label="From Account",
|
||||
widget=TomSelect(),
|
||||
)
|
||||
to_account = forms.ModelChoiceField(
|
||||
queryset=Account.objects.all(), label="To Account"
|
||||
queryset=Account.objects.all(),
|
||||
label="To Account",
|
||||
widget=TomSelect(),
|
||||
)
|
||||
|
||||
from_amount = forms.DecimalField(
|
||||
@@ -102,20 +161,30 @@ class TransferForm(forms.Form):
|
||||
required=False,
|
||||
)
|
||||
|
||||
from_category = forms.ModelChoiceField(
|
||||
queryset=TransactionCategory.objects.all(),
|
||||
from_category = DynamicModelChoiceField(
|
||||
model=TransactionCategory,
|
||||
required=False,
|
||||
label="From Category",
|
||||
label=_("Category"),
|
||||
)
|
||||
to_category = forms.ModelChoiceField(
|
||||
queryset=TransactionCategory.objects.all(), required=False, label="To Category"
|
||||
to_category = DynamicModelChoiceField(
|
||||
model=TransactionCategory,
|
||||
required=False,
|
||||
label=_("Category"),
|
||||
)
|
||||
|
||||
from_tags = forms.ModelMultipleChoiceField(
|
||||
queryset=TransactionTag.objects.all(), required=False, label="From Tags"
|
||||
from_tags = DynamicModelMultipleChoiceField(
|
||||
model=TransactionTag,
|
||||
to_field_name="name",
|
||||
create_field="name",
|
||||
required=False,
|
||||
label=_("Tags"),
|
||||
)
|
||||
to_tags = forms.ModelMultipleChoiceField(
|
||||
queryset=TransactionTag.objects.all(), required=False, label="To Tags"
|
||||
to_tags = DynamicModelMultipleChoiceField(
|
||||
model=TransactionTag,
|
||||
to_field_name="name",
|
||||
create_field="name",
|
||||
required=False,
|
||||
label=_("Tags"),
|
||||
)
|
||||
|
||||
date = forms.DateField(
|
||||
@@ -133,16 +202,25 @@ class TransferForm(forms.Form):
|
||||
|
||||
self.helper.layout = Layout(
|
||||
Row(
|
||||
Column("date", css_class="form-group col-md-6 mb-0"),
|
||||
Column("reference_date", css_class="form-group col-md-6 mb-0"),
|
||||
Column(Field("date"), css_class="form-group col-md-6 mb-0"),
|
||||
Column(
|
||||
Field("reference_date"),
|
||||
css_class="form-group col-md-6 mb-0",
|
||||
),
|
||||
css_class="form-row",
|
||||
),
|
||||
"description",
|
||||
Field("description"),
|
||||
Row(
|
||||
Column(
|
||||
Row(
|
||||
Column("from_account", css_class="form-group col-md-6 mb-0"),
|
||||
Column("from_amount", css_class="form-group col-md-6 mb-0"),
|
||||
Column(
|
||||
"from_account",
|
||||
css_class="form-group col-md-6 mb-0",
|
||||
),
|
||||
Column(
|
||||
Field("from_amount"),
|
||||
css_class="form-group col-md-6 mb-0",
|
||||
),
|
||||
css_class="form-row",
|
||||
),
|
||||
Row(
|
||||
@@ -156,8 +234,14 @@ class TransferForm(forms.Form):
|
||||
Row(
|
||||
Column(
|
||||
Row(
|
||||
Column("to_account", css_class="form-group col-md-6 mb-0"),
|
||||
Column("to_amount", css_class="form-group col-md-6 mb-0"),
|
||||
Column(
|
||||
"to_account",
|
||||
css_class="form-group col-md-6 mb-0",
|
||||
),
|
||||
Column(
|
||||
Field("to_amount"),
|
||||
css_class="form-group col-md-6 mb-0",
|
||||
),
|
||||
css_class="form-row",
|
||||
),
|
||||
Row(
|
||||
@@ -168,16 +252,16 @@ class TransferForm(forms.Form):
|
||||
),
|
||||
css_class="p-1 mx-1 my-3 border rounded-3",
|
||||
),
|
||||
Submit("submit", "Save", css_class="btn btn-primary"),
|
||||
FormActions(
|
||||
NoClassSubmit(
|
||||
"submit", _("Tranfer"), css_class="btn btn-outline-primary w-100"
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
self.fields["from_amount"].widget = ArbitraryDecimalDisplayNumberInput(
|
||||
decimal_places=2
|
||||
)
|
||||
self.fields["from_amount"].widget = ArbitraryDecimalDisplayNumberInput()
|
||||
|
||||
self.fields["to_amount"].widget = ArbitraryDecimalDisplayNumberInput(
|
||||
decimal_places=2
|
||||
)
|
||||
self.fields["to_amount"].widget = ArbitraryDecimalDisplayNumberInput()
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
@@ -227,3 +311,195 @@ class TransferForm(forms.Form):
|
||||
to_transaction.tags.set(self.cleaned_data.get("to_tags", []))
|
||||
|
||||
return from_transaction, to_transaction
|
||||
|
||||
|
||||
class InstallmentPlanForm(forms.Form):
|
||||
type = forms.ChoiceField(choices=Transaction.Type.choices)
|
||||
account = forms.ModelChoiceField(
|
||||
queryset=Account.objects.all(),
|
||||
label=_("Account"),
|
||||
widget=TomSelect(),
|
||||
)
|
||||
start_date = forms.DateField(
|
||||
label=_("Start Date"),
|
||||
widget=forms.DateInput(attrs={"type": "date"}, format="%Y-%m-%d"),
|
||||
)
|
||||
description = forms.CharField(max_length=500, label=_("Description"))
|
||||
number_of_installments = forms.IntegerField(
|
||||
min_value=1, label=_("Number of Installments")
|
||||
)
|
||||
recurrence = forms.ChoiceField(
|
||||
choices=(
|
||||
("yearly", _("Yearly")),
|
||||
("monthly", _("Monthly")),
|
||||
("weekly", _("Weekly")),
|
||||
("daily", _("Daily")),
|
||||
),
|
||||
initial="monthly",
|
||||
widget=TomSelect(clear_button=False),
|
||||
)
|
||||
installment_amount = forms.DecimalField(
|
||||
max_digits=42,
|
||||
decimal_places=30,
|
||||
required=True,
|
||||
label=_("Installment Amount"),
|
||||
)
|
||||
category = DynamicModelChoiceField(
|
||||
model=TransactionCategory,
|
||||
required=False,
|
||||
label=_("Category"),
|
||||
)
|
||||
tags = DynamicModelMultipleChoiceField(
|
||||
model=TransactionTag,
|
||||
to_field_name="name",
|
||||
create_field="name",
|
||||
required=False,
|
||||
label=_("Tags"),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_tag = False
|
||||
self.helper.form_method = "post"
|
||||
|
||||
self.helper.layout = Layout(
|
||||
Field(
|
||||
"type",
|
||||
template="transactions/widgets/income_expense_toggle_buttons.html",
|
||||
),
|
||||
"account",
|
||||
"description",
|
||||
Row(
|
||||
Column("start_date", css_class="form-group col-md-4 mb-0"),
|
||||
Column("number_of_installments", css_class="form-group col-md-4 mb-0"),
|
||||
Column("recurrence", css_class="form-group col-md-4 mb-0"),
|
||||
css_class="form-row",
|
||||
),
|
||||
"installment_amount",
|
||||
Row(
|
||||
Column("category", css_class="form-group col-md-6 mb-0"),
|
||||
Column("tags", css_class="form-group col-md-6 mb-0"),
|
||||
css_class="form-row",
|
||||
),
|
||||
FormActions(
|
||||
NoClassSubmit(
|
||||
"submit", _("Add"), css_class="btn btn-outline-primary w-100"
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
self.fields["installment_amount"].widget = ArbitraryDecimalDisplayNumberInput()
|
||||
|
||||
def save(self):
|
||||
number_of_installments = self.cleaned_data["number_of_installments"]
|
||||
transaction_type = self.cleaned_data["type"]
|
||||
start_date = self.cleaned_data["start_date"]
|
||||
recurrence = self.cleaned_data["recurrence"]
|
||||
account = self.cleaned_data["account"]
|
||||
description = self.cleaned_data["description"]
|
||||
installment_amount = self.cleaned_data["installment_amount"]
|
||||
category = self.cleaned_data["category"]
|
||||
|
||||
with transaction.atomic():
|
||||
installment_plan = InstallmentPlan.objects.create(
|
||||
account=account,
|
||||
description=description,
|
||||
number_of_installments=number_of_installments,
|
||||
)
|
||||
|
||||
with transaction.atomic():
|
||||
for i in range(number_of_installments):
|
||||
if recurrence == "yearly":
|
||||
delta = relativedelta(years=i)
|
||||
elif recurrence == "monthly":
|
||||
delta = relativedelta(months=i)
|
||||
elif recurrence == "weekly":
|
||||
delta = relativedelta(weeks=i)
|
||||
elif recurrence == "daily":
|
||||
delta = relativedelta(days=i)
|
||||
|
||||
transaction_date = start_date + delta
|
||||
new_transaction = Transaction.objects.create(
|
||||
account=account,
|
||||
type=transaction_type,
|
||||
date=transaction_date,
|
||||
reference_date=transaction_date.replace(day=1),
|
||||
amount=installment_amount,
|
||||
description=description,
|
||||
notes=f"{i + 1}/{number_of_installments}",
|
||||
category=category,
|
||||
installment_plan=installment_plan,
|
||||
)
|
||||
|
||||
new_transaction.tags.set(self.cleaned_data.get("tags", []))
|
||||
|
||||
return installment_plan
|
||||
|
||||
|
||||
class TransactionTagForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = TransactionTag
|
||||
fields = ["name"]
|
||||
labels = {"name": _("Tag name")}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.helper = FormHelper()
|
||||
self.helper.form_tag = False
|
||||
self.helper.form_method = "post"
|
||||
self.helper.layout = Layout(Field("name", css_class="mb-3"))
|
||||
|
||||
if self.instance and self.instance.pk:
|
||||
self.helper.layout.append(
|
||||
FormActions(
|
||||
NoClassSubmit(
|
||||
"submit", _("Update"), css_class="btn btn-outline-primary w-100"
|
||||
),
|
||||
),
|
||||
)
|
||||
else:
|
||||
self.helper.layout.append(
|
||||
FormActions(
|
||||
NoClassSubmit(
|
||||
"submit", _("Add"), css_class="btn btn-outline-primary w-100"
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
class TransactionCategoryForm(forms.ModelForm):
|
||||
class Meta:
|
||||
model = TransactionCategory
|
||||
fields = ["name", "mute"]
|
||||
labels = {"name": _("Category name")}
|
||||
help_texts = {
|
||||
"mute": _("Muted categories won't count towards your monthly total")
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
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"))
|
||||
|
||||
if self.instance and self.instance.pk:
|
||||
self.helper.layout.append(
|
||||
FormActions(
|
||||
NoClassSubmit(
|
||||
"submit", _("Update"), css_class="btn btn-outline-primary w-100"
|
||||
),
|
||||
),
|
||||
)
|
||||
else:
|
||||
self.helper.layout.append(
|
||||
FormActions(
|
||||
NoClassSubmit(
|
||||
"submit", _("Add"), css_class="btn btn-outline-primary w-100"
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user