Compensation action

* adds compensation action to compensation detail view
* adds adding/removing logic for compensation action
* adds bootstrap style to select fields in forms
* refactors UnitEnum into UnitChoices using models.TextChoices (Django 3.x)
* adds translations
This commit is contained in:
mipel
2021-08-04 10:44:02 +02:00
parent c6fe944701
commit e2ed39c900
8 changed files with 333 additions and 70 deletions

View File

@@ -12,7 +12,7 @@ from django.http import HttpRequest
from django.shortcuts import redirect, render
from django.utils.translation import gettext_lazy as _
from compensation.models import Payment, CompensationState
from compensation.models import Payment, CompensationState, CompensationAction, UnitChoices
from konova.contexts import BaseContext
from konova.forms import BaseForm, BaseModalForm
from konova.models import Deadline, DeadlineType
@@ -156,7 +156,12 @@ class NewDeadlineModalForm(BaseModalForm):
label_suffix="",
required=True,
help_text=_("Select the deadline type"),
choices=DeadlineType.choices
choices=DeadlineType.choices,
widget=forms.Select(
attrs={
"class": "custom-select"
}
)
)
date = forms.DateField(
label=_("Date"),
@@ -193,7 +198,7 @@ class NewDeadlineModalForm(BaseModalForm):
with transaction.atomic():
action = UserActionLogEntry.objects.create(
user=self.user,
action=UserAction.CREATED.value
action=UserAction.CREATED
)
deadline = Deadline.objects.create(
type=self.cleaned_data["type"],
@@ -204,3 +209,64 @@ class NewDeadlineModalForm(BaseModalForm):
self.instance.deadlines.add(deadline)
return deadline
class NewActionModalForm(BaseModalForm):
action_type = forms.CharField(
label=_("Action Type"),
label_suffix="",
required=True,
help_text=_("Select the deadline type"),
)
unit = forms.ChoiceField(
label=_("Unit"),
label_suffix="",
required=True,
help_text=_("Select the unit"),
choices=UnitChoices.choices,
widget=forms.Select(
attrs={
"class": "custom-select"
}
)
)
amount = forms.DecimalField(
label=_("Amount"),
label_suffix="",
required=True,
help_text=_("Insert the amount"),
decimal_places=2,
min_value=0.00,
)
comment = forms.CharField(
required=False,
label=_("Comment"),
label_suffix=_(""),
help_text=_("Additional comment"),
widget=forms.Textarea(
attrs={
"cols": 30,
"rows": 5,
}
)
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.form_title = _("New action")
self.form_caption = _("Insert data for the new action")
def save(self):
with transaction.atomic():
user_action = UserActionLogEntry.objects.create(
user=self.user,
action=UserAction.CREATED
)
comp_action = CompensationAction.objects.create(
action_type=self.cleaned_data["action_type"],
amount=self.cleaned_data["amount"],
unit=self.cleaned_data["unit"],
created=user_action,
)
self.instance.actions.add(comp_action)
return comp_action

View File

@@ -11,6 +11,7 @@ from django.core.validators import MinValueValidator, MaxValueValidator
from django.db import transaction
from django.utils import timezone
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from compensation.settings import COMPENSATION_IDENTIFIER_LENGTH, COMPENSATION_IDENTIFIER_TEMPLATE
from intervention.models import Intervention, ResponsibilityData
@@ -63,18 +64,45 @@ class CompensationState(UuidModel):
return "{} | {}".format(self.biotope_type, self.surface)
class UnitChoices(models.TextChoices):
"""
Predefines units for selection
"""
cm = "cm", _("cm")
m = "m", _("m")
km = "km", _("km")
qm = "qm", _("")
ha = "ha", _("ha")
st = "pcs", _("Pieces") # pieces
class CompensationAction(BaseResource):
"""
Compensations include actions like planting trees, refreshing rivers and so on.
"""
action_type = models.CharField(max_length=500, null=True, blank=True)
amount = models.FloatField()
unit = models.CharField(max_length=100, null=True, blank=True)
unit = models.CharField(max_length=100, null=True, blank=True, choices=UnitChoices.choices)
control = models.ForeignKey(CompensationControl, on_delete=models.SET_NULL, null=True, blank=True)
def __str__(self):
return "{} | {} {}".format(self.action_type, self.amount, self.unit)
@property
def unit_humanize(self):
""" Returns humanized version of enum
Used for template rendering
Returns:
"""
choices = UnitChoices.choices
for choice in choices:
if choice[0] == self.unit:
return choice[1]
return None
class AbstractCompensation(BaseObject):
"""

View File

@@ -0,0 +1,61 @@
{% load i18n l10n fontawesome_5 %}
<div id="related-documents" class="card">
<div class="card-header rlp-r">
<div class="row">
<div class="col-sm-6">
<h5>
<span class="badge badge-light">{{comp.actions.count}}</span>
{% trans 'Actions' context 'Compensation' %}
</h5>
</div>
<div class="col-sm-6">
<div class="d-flex justify-content-end">
{% if is_default_member and has_access %}
<button class="btn btn-outline-default btn-modal" data-form-url="{% url 'compensation:new-action' comp.id %}" title="{% trans 'Add new action' %}">
{% fa5_icon 'plus' %}
{% fa5_icon 'seedling' %}
</button>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card-body scroll-300">
<table class="table table-hover">
<thead>
<tr>
<th scope="col">
{% trans 'Action type' %}
</th>
<th scope="col">
{% trans 'Amount' context 'Compensation' %}
</th>
<th scope="col">
{% trans 'Unit' %}
</th>
<th scope="col">
{% trans 'Action' %}
</th>
</tr>
</thead>
<tbody>
{% for action in comp.actions.all %}
<tr>
<td class="align-middle">
{{ action.action_type }}
</td>
<td class="align-middle">{{ action.amount }}</td>
<td class="align-middle">{{ action.unit_humanize }}</td>
<td>
{% if is_default_member and has_access %}
<button data-form-url="{% url 'compensation:action-remove' action.id %}" class="btn btn-default btn-modal" title="{% trans 'Remove action' %}">
{% fa5_icon 'trash' %}
</button>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

View File

@@ -18,6 +18,7 @@ urlpatterns = [
path('<id>/edit', edit_view, name='edit'),
path('<id>/remove', remove_view, name='remove'),
path('<id>/state/new', state_new_view, name='new-state'),
path('<id>/action/new', action_new_view, name='new-action'),
path('<id>/deadline/new', deadline_new_view, name="new-deadline"),
# Documents
@@ -41,4 +42,7 @@ urlpatterns = [
# Generic state routes
path('state/<id>/remove', state_remove_view, name='state-remove'),
# Generic action routes
path('action/<id>/remove', action_remove_view, name='action-remove'),
]

View File

@@ -5,8 +5,8 @@ from django.http import HttpRequest, Http404
from django.shortcuts import render, get_object_or_404
from django.utils.translation import gettext_lazy as _
from compensation.forms import NewPaymentForm, NewStateModalForm, NewDeadlineModalForm
from compensation.models import Compensation, EcoAccount, Payment, CompensationState
from compensation.forms import NewPaymentForm, NewStateModalForm, NewDeadlineModalForm, NewActionModalForm
from compensation.models import Compensation, EcoAccount, Payment, CompensationState, CompensationAction
from compensation.tables import CompensationTable, EcoAccountTable
from intervention.models import Intervention
from konova.contexts import BaseContext
@@ -300,6 +300,25 @@ def state_new_view(request: HttpRequest, id: str):
)
@login_required
def action_new_view(request: HttpRequest, id: str):
""" Renders a form for adding new actions for a compensation
Args:
request (HttpRequest): The incoming request
id (str): The compensation's id to which the new state will be related
Returns:
"""
comp = get_object_or_404(Compensation, id=id)
form = NewActionModalForm(request.POST or None, instance=comp, user=request.user)
return form.process_request(
request,
msg_success=_("Action added")
)
@login_required
def deadline_new_view(request: HttpRequest, id: str):
""" Renders a form for adding new states for a compensation
@@ -321,9 +340,37 @@ def deadline_new_view(request: HttpRequest, id: str):
@login_required
def state_remove_view(request: HttpRequest, id: str):
""" Renders a form for removing a compensation state
Args:
request (HttpRequest): The incoming request
id (str): The state's id
Returns:
"""
state = get_object_or_404(CompensationState, id=id)
form = RemoveModalForm(request.POST or None, instance=state, user=request.user)
return form.process_request(
request,
msg_success=_("State removed")
)
@login_required
def action_remove_view(request: HttpRequest, id: str):
""" Renders a form for removing a compensation action
Args:
request (HttpRequest): The incoming request
id (str): The action's id
Returns:
"""
action = get_object_or_404(CompensationAction, id=id)
form = RemoveModalForm(request.POST or None, instance=action, user=request.user)
return form.process_request(
request,
msg_success=_("Action removed")
)