Payments add modal form

* adds modal form for adding payments
* generalizes generic_table_form.html for table-form-like usage in modal_form.html
* adds css enhancements for focused input fields
* adds BaseModalForm as specification to BaseForm, which inherits the BSModalForm class as well
* adds translations
This commit is contained in:
mipel 2021-07-26 10:23:09 +02:00
parent 8885f81770
commit 23afe2654e
14 changed files with 194 additions and 75 deletions

View File

@ -5,10 +5,60 @@ Contact: michel.peltriaux@sgdnord.rlp.de
Created on: 04.12.20 Created on: 04.12.20
""" """
from konova.forms import BaseForm from django import forms
from django.db import transaction
from django.utils.translation import gettext_lazy as _
from compensation.models import Payment
from konova.forms import BaseForm, BaseModalForm
class NewCompensationForm(BaseForm): class NewCompensationForm(BaseForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
class NewPaymentForm(BaseModalForm):
amount = forms.FloatField(
min_value=0.01,
label=_("Amount"),
label_suffix=_(""),
)
due = forms.DateField(
required=False,
label=_("Due on"),
label_suffix=_(""),
widget=forms.DateInput(
attrs={
"type": "date",
"data-provide": "datepicker",
},
format="%d.%m.%Y"
)
)
transfer_note = forms.CharField(
max_length=1000,
required=False,
label_suffix=_(""),
label=_("Transfer note")
)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("request", None).user
self.intervention = kwargs.pop("intervention", None)
super().__init__(*args, **kwargs)
self.form_title = _("Payment")
self.form_caption = _("Add a payment for intervention '{}'").format(self.intervention.title)
def save(self):
with transaction.atomic():
pay = Payment.objects.create(
created_by=self.user,
amount=self.cleaned_data.get("amount", -1),
due_on=self.cleaned_data.get("due", None),
comment=self.cleaned_data.get("transfer_note", None),
intervention=self.intervention,
)
return pay

View File

@ -19,7 +19,7 @@ urlpatterns = [
path('<id>/remove', remove_view, name='remove'), path('<id>/remove', remove_view, name='remove'),
# Payment # Payment
path('pay/new', new_view, name='pay-new'), path('pay/<intervention_id>/new', new_payment_view, name='pay-new'),
path('pay/<id>', open_view, name='pay-open'), path('pay/<id>', open_view, name='pay-open'),
path('pay/<id>/edit', edit_view, name='pay-edit'), path('pay/<id>/edit', edit_view, name='pay-edit'),
path('pay/<id>/remove', remove_view, name='pay-remove'), path('pay/<id>/remove', remove_view, name='pay-remove'),

View File

@ -1,9 +1,12 @@
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.http import HttpRequest from django.http import HttpRequest
from django.shortcuts import render from django.shortcuts import render, get_object_or_404
from django.utils.translation import gettext_lazy as _
from compensation.forms import NewPaymentForm
from compensation.models import Compensation, EcoAccount from compensation.models import Compensation, EcoAccount
from compensation.tables import CompensationTable, EcoAccountTable from compensation.tables import CompensationTable, EcoAccountTable
from intervention.models import Intervention
from konova.contexts import BaseContext from konova.contexts import BaseContext
from konova.decorators import * from konova.decorators import *
@ -108,3 +111,41 @@ def account_open_view(request: HttpRequest, id: str):
def account_remove_view(request: HttpRequest, id: str): def account_remove_view(request: HttpRequest, id: str):
# ToDo # ToDo
pass pass
@login_required
def new_payment_view(request: HttpRequest, intervention_id: str):
""" Renders a modal view for adding new payments
Args:
request (HttpRequest): The incoming request
intervention_id (str): The intervention's id for which a new payment shall be added
Returns:
"""
template = "modal/modal_form.html"
intervention = get_object_or_404(Intervention, id=intervention_id)
form = NewPaymentForm(request.POST or None, intervention=intervention, request=request)
if request.method == "POST":
if form.is_valid():
payment = form.save()
messages.success(
request,
_("Payment added")
)
return redirect(request.META.get("HTTP_REFERER", "home"))
else:
messages.info(
request,
_("There was an error on this form.")
)
return redirect(request.META.get("HTTP_REFERER", "home"))
elif request.method == "GET":
context = {
"form": form,
}
context = BaseContext(request, context).context
return render(request, template, context)
else:
raise NotImplementedError

View File

@ -211,12 +211,10 @@
</div> </div>
<div class="col-sm-6"> <div class="col-sm-6">
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<a href="{% url 'compensation:pay-new' %}" title="{% trans 'Add new payment' %}"> <button class="btn btn-outline-default btn-modal" data-form-url="{% url 'compensation:pay-new' intervention.id %}" title="{% trans 'Add new payment' %}">
<button class="btn btn-outline-default"> {% fa5_icon 'plus' %}
{% fa5_icon 'plus' %} {% fa5_icon 'money-bill-wave' %}
{% fa5_icon 'money-bill-wave' %} </button>
</button>
</a>
</div> </div>
</div> </div>
</div> </div>
@ -264,7 +262,7 @@
<div class="col-sm-6"> <div class="col-sm-6">
<div class="d-flex justify-content-end"> <div class="d-flex justify-content-end">
<a href="{% url 'doc-new' %}" title="{% trans 'Add new document' %}"> <a href="{% url 'doc-new' %}" title="{% trans 'Add new document' %}">
<button class="btn btn-outline-default"> <button class="btn btn-outline-default ">
{% fa5_icon 'plus' %} {% fa5_icon 'plus' %}
{% fa5_icon 'file' %} {% fa5_icon 'file' %}
</button> </button>
@ -298,7 +296,7 @@
</td> </td>
<td class="align-middle">{{ doc.comment }}</td> <td class="align-middle">{{ doc.comment }}</td>
<td> <td>
<button data-form-url="{% url 'doc-remove' doc.id %}" class="btn btn-default del-btn" title="{% trans 'Remove document' %}"> <button data-form-url="{% url 'doc-remove' doc.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove document' %}">
{% fa5_icon 'trash' %} {% fa5_icon 'trash' %}
</button> </button>
</td> </td>
@ -311,7 +309,7 @@
</div> </div>
</div> </div>
{% with 'del-btn' as btn_class %} {% with 'btn-modal' as btn_class %}
{% include 'modal/modal_form_script.html' %} {% include 'modal/modal_form_script.html' %}
{% endwith %} {% endwith %}

View File

@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _
from django.http import HttpRequest from django.http import HttpRequest
from django.shortcuts import render, get_object_or_404 from django.shortcuts import render, get_object_or_404
from intervention.forms import NewInterventionForm, EditInterventionForm, OpenInterventionForm from intervention.forms import NewInterventionForm, EditInterventionForm
from intervention.models import Intervention from intervention.models import Intervention
from intervention.tables import InterventionTable from intervention.tables import InterventionTable
from konova.contexts import BaseContext from konova.contexts import BaseContext

View File

@ -8,12 +8,11 @@ Created on: 16.11.20
from abc import abstractmethod from abc import abstractmethod
from bootstrap_modal_forms.forms import BSModalModelForm, BSModalForm from bootstrap_modal_forms.forms import BSModalForm
from django import forms from django import forms
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.gis.forms import GeometryField, OSMWidget from django.contrib.gis.forms import GeometryField, OSMWidget
from django.contrib.gis.geos import Polygon from django.contrib.gis.geos import Polygon
from django.urls import reverse
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -99,6 +98,13 @@ class RemoveForm(BaseForm):
return self.object_to_remove return self.object_to_remove
class BaseModalForm(BaseForm, BSModalForm):
""" A specialzed form class for modal form handling
"""
is_modal_form = True
class SimpleGeomForm(BaseForm): class SimpleGeomForm(BaseForm):
""" A geometry form for rendering geometry read-only using a widget """ A geometry form for rendering geometry read-only using a widget
@ -131,7 +137,7 @@ class SimpleGeomForm(BaseForm):
self.area = geom.area self.area = geom.area
class RemoveDocumentForm(BaseForm, BSModalForm): class RemoveDocumentForm(BaseModalForm):
confirm = forms.BooleanField( confirm = forms.BooleanField(
label=_("Confirm"), label=_("Confirm"),
label_suffix=_(""), label_suffix=_(""),

View File

@ -168,7 +168,7 @@ a {
} }
input:focus, textarea:focus, select:focus{ input:focus, textarea:focus, select:focus{
border: 1px solid var(--rlp-red) !important; border-color: var(--rlp-red) !important;
box-shadow: 0 0 3px var(--rlp-red) !important; box-shadow: 0 0 3px var(--rlp-red) !important;
-moz-box-shadow: 0 0 3px var(--rlp-red) !important; -moz-box-shadow: 0 0 3px var(--rlp-red) !important;
-webkit-box-shadow: 0 0 3px var(--rlp-red) !important; -webkit-box-shadow: 0 0 3px var(--rlp-red) !important;

View File

@ -3,6 +3,6 @@
{% block body %} {% block body %}
<div class="column"> <div class="column">
{% include 'generic_table_form.html' %} {% include 'table/generic_table_form.html' %}
</div> </div>
{% endblock %} {% endblock %}

Binary file not shown.

View File

@ -3,15 +3,16 @@
# This file is distributed under the same license as the PACKAGE package. # This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR. # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
# #
#: compensation/forms.py:27 compensation/forms.py:32 compensation/forms.py:44
#: intervention/filters.py:25 intervention/filters.py:31 #: intervention/filters.py:25 intervention/filters.py:31
#: intervention/filters.py:38 intervention/filters.py:39 konova/forms.py:73 #: intervention/filters.py:38 intervention/filters.py:39 konova/forms.py:73
#: konova/forms.py:137 user/forms.py:38 #: konova/forms.py:144 user/forms.py:38
#, fuzzy #, fuzzy
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-07-23 18:26+0200\n" "POT-Creation-Date: 2021-07-26 10:18+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -21,6 +22,27 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: compensation/forms.py:26
#: intervention/templates/intervention/detail-view.html:227
msgid "Amount"
msgstr "Betrag"
#: compensation/forms.py:31
msgid "Due on"
msgstr "Fällig am"
#: compensation/forms.py:45
msgid "Transfer note"
msgstr "Verwendungszweck"
#: compensation/forms.py:53
msgid "Payment"
msgstr "Zahlung"
#: compensation/forms.py:54
msgid "Add a payment for intervention '{}'"
msgstr "Neue Ersatzzahlung zu Eingriff '{}' hinzufügen"
#: compensation/tables.py:18 compensation/tables.py:71 intervention/forms.py:26 #: compensation/tables.py:18 compensation/tables.py:71 intervention/forms.py:26
#: intervention/tables.py:23 #: intervention/tables.py:23
#: intervention/templates/intervention/detail-view.html:179 #: intervention/templates/intervention/detail-view.html:179
@ -31,7 +53,7 @@ msgstr "Kennung"
#: intervention/tables.py:28 #: intervention/tables.py:28
#: intervention/templates/intervention/detail-view.html:62 #: intervention/templates/intervention/detail-view.html:62
#: intervention/templates/intervention/detail-view.html:182 #: intervention/templates/intervention/detail-view.html:182
#: intervention/templates/intervention/detail-view.html:281 #: intervention/templates/intervention/detail-view.html:279
msgid "Title" msgid "Title"
msgstr "Bezeichnung" msgstr "Bezeichnung"
@ -72,6 +94,14 @@ msgstr "Lösche {}"
msgid "Eco Accounts" msgid "Eco Accounts"
msgstr "Ökokonten" msgstr "Ökokonten"
#: compensation/views.py:135
msgid "Payment added"
msgstr "Zahlung hinzugefügt"
#: compensation/views.py:141 konova/views.py:137
msgid "There was an error on this form."
msgstr "Es gab einen Fehler im Formular."
#: intervention/filters.py:24 #: intervention/filters.py:24
msgid "Show all" msgid "Show all"
msgstr "Alle anzeigen" msgstr "Alle anzeigen"
@ -291,31 +321,27 @@ msgstr "Ersatzzahlungen"
msgid "Add new payment" msgid "Add new payment"
msgstr "Neue Zahlung hinzufügen" msgstr "Neue Zahlung hinzufügen"
#: intervention/templates/intervention/detail-view.html:229 #: intervention/templates/intervention/detail-view.html:230
msgid "Amount"
msgstr "Betrag"
#: intervention/templates/intervention/detail-view.html:232
msgid "Transfer comment" msgid "Transfer comment"
msgstr "Verwendungszweck" msgstr "Verwendungszweck"
#: intervention/templates/intervention/detail-view.html:261 #: intervention/templates/intervention/detail-view.html:259
msgid "Documents" msgid "Documents"
msgstr "Dokumente" msgstr "Dokumente"
#: intervention/templates/intervention/detail-view.html:266 #: intervention/templates/intervention/detail-view.html:264
msgid "Add new document" msgid "Add new document"
msgstr "Neues Dokument hinzufügen" msgstr "Neues Dokument hinzufügen"
#: intervention/templates/intervention/detail-view.html:284 #: intervention/templates/intervention/detail-view.html:282
msgid "Comment" msgid "Comment"
msgstr "Kommentar" msgstr "Kommentar"
#: intervention/templates/intervention/detail-view.html:287 #: intervention/templates/intervention/detail-view.html:285
msgid "Action" msgid "Action"
msgstr "Aktionen" msgstr "Aktionen"
#: intervention/templates/intervention/detail-view.html:301 konova/forms.py:145 #: intervention/templates/intervention/detail-view.html:299 konova/forms.py:152
msgid "Remove document" msgid "Remove document"
msgstr "Dokument löschen" msgstr "Dokument löschen"
@ -357,7 +383,7 @@ msgstr "Hierfür müssen Sie einer anderen Nutzergruppe angehören!"
msgid "Not editable" msgid "Not editable"
msgstr "Nicht editierbar" msgstr "Nicht editierbar"
#: konova/forms.py:72 konova/forms.py:136 #: konova/forms.py:72 konova/forms.py:143
msgid "Confirm" msgid "Confirm"
msgstr "Bestätige" msgstr "Bestätige"
@ -369,7 +395,7 @@ msgstr "Entferne"
msgid "You are about to remove {} {}" msgid "You are about to remove {} {}"
msgstr "Sie sind dabei {} {} zu löschen" msgstr "Sie sind dabei {} {} zu löschen"
#: konova/forms.py:146 #: konova/forms.py:153
msgid "This will remove '{}'. Are you sure?" msgid "This will remove '{}'. Are you sure?"
msgstr "Hiermit wird '{}' gelöscht. Sind Sie sicher?" msgstr "Hiermit wird '{}' gelöscht. Sind Sie sicher?"
@ -425,14 +451,10 @@ msgstr "Ökokonto"
msgid "Withdraw" msgid "Withdraw"
msgstr "Abbuchen" msgstr "Abbuchen"
#: konova/views.py:133 #: konova/views.py:131
msgid "Document '{}' deleted" msgid "Document '{}' deleted"
msgstr "Dokument '{}' gelöscht" msgstr "Dokument '{}' gelöscht"
#: konova/views.py:139
msgid "There was an error on this form."
msgstr "Es gab einen Fehler im Formular."
#: news/templates/news/dashboard-news.html:12 news/templates/news/index.html:19 #: news/templates/news/dashboard-news.html:12 news/templates/news/index.html:19
msgid "Published on" msgid "Published on"
msgstr "Veröffentlicht am" msgstr "Veröffentlicht am"
@ -461,19 +483,7 @@ msgstr ""
msgid "Contact" msgid "Contact"
msgstr "Kontakt" msgstr "Kontakt"
#: templates/generic_table_form.html:37 #: templates/modal/modal_form.html:33
msgid "Fields with * are required."
msgstr "* sind Pflichtfelder."
#: templates/generic_table_form.html:41
msgid "Cancel"
msgstr "Abbrechen"
#: templates/generic_table_form.html:45
msgid "Save"
msgstr "Speichern"
#: templates/modal/modal_form.html:30
msgid "Continue" msgid "Continue"
msgstr "Weiter" msgstr "Weiter"
@ -549,6 +559,18 @@ msgstr ""
msgid "Apply filter" msgid "Apply filter"
msgstr "Filter anwenden" msgstr "Filter anwenden"
#: templates/table/generic_table_form.html:23
msgid "Cancel"
msgstr "Abbrechen"
#: templates/table/generic_table_form.html:27
msgid "Save"
msgstr "Speichern"
#: templates/table/generic_table_form_body.html:20
msgid "Fields with * are required."
msgstr "* sind Pflichtfelder."
#: user/forms.py:23 #: user/forms.py:23
msgid "Notifications" msgid "Notifications"
msgstr "Benachrichtigungen" msgstr "Benachrichtigungen"
@ -1816,9 +1838,6 @@ msgstr ""
#~ msgid "Last login on" #~ msgid "Last login on"
#~ msgstr "Zuletzt eingeloggt am" #~ msgstr "Zuletzt eingeloggt am"
#~ msgid "Add new intervention"
#~ msgstr "Neuen Eingriff hinzufügen"
#~ msgid "Delete intervention" #~ msgid "Delete intervention"
#~ msgstr "Eingriff löschen" #~ msgstr "Eingriff löschen"

View File

@ -15,6 +15,7 @@
</div> </div>
<div class="modal-body"> <div class="modal-body">
<!--
<article class="mb-5">{{form.form_caption}}</article> <article class="mb-5">{{form.form_caption}}</article>
{% for field in form %} {% for field in form %}
<div class="form-group{% if field.errors %} invalid{% endif %}"> <div class="form-group{% if field.errors %} invalid{% endif %}">
@ -25,6 +26,8 @@
{% endfor %} {% endfor %}
</div> </div>
{% endfor %} {% endfor %}
-->
{% include 'table/generic_table_form_body.html' %}
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button type="submit" class="btn btn-default">{% trans 'Continue' %}</button> <button type="submit" class="btn btn-default">{% trans 'Continue' %}</button>

View File

@ -16,25 +16,7 @@
{% endif %} {% endif %}
<form method="post" action="{{ form.action_url }}"> <form method="post" action="{{ form.action_url }}">
{% csrf_token %} {% csrf_token %}
<table class="table"> {% include 'table/generic_table_form_body.html' %}
<tbody>
{% for field in form %}
<tr title="{{ field.help_text }}" class="{% if field.errors %}error{% endif %}">
<th scope="row" class="col-sm-3">
<div>{{ field.label }}<span class="label-required">{% if field.field.required %}*{% endif %}</span></div>
<small>{{ field.help_text }}</small>
</th>
<td class="col-sm-9">
{{ field }}
{% for error in field.errors %}
<strong>{{ error }}</strong>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<small>{% trans 'Fields with * are required.' %}</small>
<div class="row"> <div class="row">
<div class="col-md"> <div class="col-md">
<a href="{{ form.cancel_redirect }}"> <a href="{{ form.cancel_redirect }}">

View File

@ -0,0 +1,20 @@
{% load i18n %}
<table class="table">
<tbody>
{% for field in form %}
<tr title="{{ field.help_text }}" class="{% if field.errors %}error{% endif %}">
<th scope="row" class="col-sm-3">
<div>{{ field.label }}<span class="label-required">{% if field.field.required %}*{% endif %}</span></div>
<small>{{ field.help_text }}</small>
</th>
<td class="col-sm-9">
{{ field }}
{% for error in field.errors %}
<strong>{{ error }}</strong>
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
<small>{% trans 'Fields with * are required.' %}</small>

View File

@ -1,5 +1,5 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block body %} {% block body %}
{% include 'generic_table_form.html' %} {% include 'table/generic_table_form.html' %}
{% endblock %} {% endblock %}