Skip to content
Snippets Groups Projects
Commit e28c6fa9 authored by Tom Teichler's avatar Tom Teichler :beers:
Browse files

Add ui to manage invoices and invoice groups

parent d32d285a
No related branches found
No related tags found
No related merge requests found
from material import Layout
from material import Layout, Row
from aleksis.core.mixins import ExtensibleForm
from .models.base import Client
from .models.invoice import InvoiceGroup
class EditClientForm(ExtensibleForm):
"""Form to create or edit clients."""
......@@ -12,3 +13,14 @@ class EditClientForm(ExtensibleForm):
class Meta:
model = Client
exclude = []
class EditInvoiceGroupForm(ExtensibleForm):
layout = Layout(
Row("name", "template_name")
)
class Meta:
model = InvoiceGroup
exclude = ["client"]
......@@ -21,6 +21,10 @@ class InvoiceGroup(ExtensibleModel):
template_name = models.CharField(verbose_name=_("Template to render invoices with as PDF"), blank=True, max_length=255)
def __str__(self) -> str:
return self.name
class Meta:
constraints = [
models.UniqueConstraint(fields=["client", "name"], name="group_uniq_per_client")
......
......@@ -41,6 +41,12 @@ class ClientsTable(tables.Table):
name = tables.Column()
view = tables.LinkColumn(
"client_by_pk",
args=[A("id")],
verbose_name=_("View"),
text=_("View"),
)
edit = tables.LinkColumn(
"edit_client_by_pk",
args=[A("id")],
......@@ -53,3 +59,47 @@ class ClientsTable(tables.Table):
verbose_name=_("Delete"),
text=_("Delete"),
)
class InvoiceGroupsTable(tables.Table):
name = tables.Column()
template_name = tables.Column()
view = tables.LinkColumn(
"invoice_group_by_pk",
args=[A("id")],
verbose_name=_("View"),
text=_("View"),
)
edit = tables.LinkColumn(
"edit_invoice_group_by_pk",
args=[A("id")],
verbose_name=_("Edit"),
text=_("Edit"),
)
delete = tables.LinkColumn(
"delete_invoice_group_by_pk",
args=[A("id")],
verbose_name=_("Delete"),
text=_("Delete"),
)
class InvoicesTable(tables.Table):
transaction_id = tables.Column()
status = tables.Column()
created = tables.DateColumn()
billing_first_name = tables.Column()
billing_last_name = tables.Column()
total = tables.Column()
view = tables.LinkColumn(
"invoice_by_pk",
args=[A("id")],
verbose_name=_("View"),
text=_("View"),
)
print = tables.LinkColumn(
"get_invoice_by_pk",
args=[A("id")],
verbose_name=_("Print"),
text=_("Print"),
)
{% extends "core/base.html" %}
{% load material_form i18n %}
{% load render_table from django_tables2 %}
{% block page_title %}{% blocktrans %}Client{% endblocktrans %} {{ client }}{% endblock %}
{% block browser_title %}{% blocktrans %}Client{% endblocktrans %} {{ client }}{% endblock %}
{% block content %}
<a class="btn colour-primary waves-effect waves-light" href="{% url 'create_invoice_group' client.id %}">{% trans "Add invoice group" %}</a>
{% render_table invoice_groups_table %}
{% endblock %}
{% extends "core/base.html" %}
{% load material_form i18n %}
{% load render_table from django_tables2 %}
{% block browser_title %}{{ object.transaction_id }}{% endblock %}
{% block content %}
<h1>{% trans "Invoice" %} {{ object.transaction_id }} — {{ object.created.date }}</h1>
<a class="btn colour-primary waves-effect waves-light" href="{% url 'invoice_group_by_pk' object.group.pk %}">{% trans "Back" %}</a>
{% render_table object.purchased_items_table %}
{% render_table object.totals_table %}
{% endblock %}
{% extends "core/base.html" %}
{% load material_form i18n %}
{% block page_title %}{% blocktrans %}Create invoice group{% endblocktrans %}{% endblock %}
{% block browser_title %}{% blocktrans %}Create invoice group{% endblocktrans %}{% endblock %}
{% block extra_head %}
{{ form.media.css }}
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{% form form=form %}{% endform %}
{% include "core/partials/save_button.html" %}
</form>
{{ form.media.js }}
{% endblock %}
{% extends "core/base.html" %}
{% load material_form i18n %}
{% block page_title %}{% blocktrans %}Edit invoice group{% endblocktrans %}{% endblock %}
{% block browser_title %}{% blocktrans %}Edit invoice group{% endblocktrans %}{% endblock %}
{% block extra_head %}
{{ form.media.css }}
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{% form form=form %}{% endform %}
{% include "core/partials/save_button.html" %}
</form>
{{ form.media.js }}
{% endblock %}
{% extends "core/base.html" %}
{% load material_form i18n %}
{% load render_table from django_tables2 %}
{% block page_title %}{{ object }}{% endblock %}
{% block browser_title %}{{ object }}{% endblock %}
{% block content %}
<a class="btn colour-primary waves-effect waves-light" href="{% url 'client_by_pk' object.client.pk %}">{% trans "Back" %}</a>
{% render_table invoices_table %}
{% endblock %}
from django.utils.translation import gettext_lazy as _
MENUS = {
"NAV_MENU_CORE": [
{
"name": _("Payments"),
"url": "#",
"root": True,
"icon": "price_check",
"validators": [
"menu_generator.validators.is_authenticated",
"aleksis.core.util.core_helpers.has_person",
],
"submenu": [
{
"name": _("Clients"),
"url": "clients",
"icon": "account_balance",
"validators": [
(
"aleksis.core.util.predicates.permission_validator",
"tezor.can_view_clients",
)
],
},
],
}
]
}
......@@ -25,4 +25,34 @@ urlpatterns = [
views.ClientDeleteView.as_view(),
name="delete_client_by_pk",
),
path(
"clients/<int:pk>/",
views.ClientDetailView.as_view(),
name="client_by_pk",
),
path(
"client/<int:pk>/invoice_groups/create",
views.InvoiceGroupCreateView.as_view(),
name="create_invoice_group",
),
path(
"invoice_groups/<int:pk>/edit",
views.InvoiceGroupEditView.as_view(),
name="edit_invoice_group_by_pk",
),
path(
"invoice_groups/<int:pk>/",
views.InvoiceGroupDetailView.as_view(),
name="invoice_group_by_pk",
),
path(
"invoice_groups/<int:pk>/delete",
views.InvoiceGroupDeleteView.as_view(),
name="delete_invoice_group_by_pk",
),
path(
"invoice/<int:pk>/",
views.InvoiceDetailView.as_view(),
name="invoice_by_pk",
),
]
......@@ -8,15 +8,15 @@ from django.urls import reverse, reverse_lazy
from django.views.generic.detail import DetailView
from rules.contrib.views import PermissionRequiredMixin
from django_tables2.views import SingleTableView
from django_tables2.views import SingleTableView, RequestConfig
from aleksis.core.views import RenderPDFView
from aleksis.core.mixins import AdvancedCreateView, AdvancedDeleteView, AdvancedEditView
from .tables import ClientsTable
from .forms import EditClientForm
from .tables import ClientsTable, InvoiceGroupsTable, InvoicesTable
from .forms import EditClientForm, EditInvoiceGroupForm
from .models.base import Client
from .models.invoice import Invoice
from .models.invoice import Invoice, InvoiceGroup
class GetInvoicePDF(PermissionRequiredMixin, RenderPDFView):
......@@ -73,3 +73,83 @@ class ClientDeleteView(PermissionRequiredMixin, AdvancedDeleteView):
template_name = "core/pages/delete.html"
success_url = reverse_lazy("clients")
success_message = _("The client has been deleted.")
class ClientDetailView(PermissionRequiredMixin, DetailView):
model = Client
permission_required = "tezor.view_client"
template_name = "tezor/client/full.html"
def get_context_data(self, object):
context = super().get_context_data()
invoice_groups = object.invoice_groups.all()
invoice_groups_table = InvoiceGroupsTable(invoice_groups)
RequestConfig(self.request).configure(invoice_groups_table)
context["invoice_groups_table"] = invoice_groups_table
return context
class InvoiceGroupDetailView(PermissionRequiredMixin, DetailView):
model = InvoiceGroup
permission_required = "tezor.view_invoice_group"
template_name = "tezor/invoice_group/full.html"
def get_context_data(self, object):
context = super().get_context_data()
invoices = object.invoices.all()
invoices_table = InvoicesTable(invoices)
RequestConfig(self.request).configure(invoices_table)
context["invoices_table"] = invoices_table
return context
@method_decorator(never_cache, name="dispatch")
class InvoiceGroupCreateView(PermissionRequiredMixin, AdvancedCreateView):
"""Create view for invoice_groups."""
model = InvoiceGroup
form_class = EditInvoiceGroupForm
permission_required = "tezor.add_invoice_groups"
template_name = "tezor/invoice_group/create.html"
success_url = reverse_lazy("clients")
success_message = _("The invoice_group has been created.")
def form_valid(self, form):
client = Client.objects.get(id=self.kwargs["pk"])
InvoiceGroup.objects.create(client=client, name=form.cleaned_data["name"], template_name=form.cleaned_data["template_name"])
return super().form_valid(form)
@method_decorator(never_cache, name="dispatch")
class InvoiceGroupEditView(PermissionRequiredMixin, AdvancedEditView):
"""Edit view for invoice_groups."""
model = InvoiceGroup
form_class = EditInvoiceGroupForm
permission_required = "tezor.edit_invoice_groups"
template_name = "tezor/invoice_group/edit.html"
success_url = reverse_lazy("invoice_groups")
success_message = _("The invoice_group has been saved.")
class InvoiceGroupDeleteView(PermissionRequiredMixin, AdvancedDeleteView):
"""Delete view for invoice_group."""
model = InvoiceGroup
permission_required = "tezor.delete_invoice_group"
template_name = "core/pages/delete.html"
success_url = reverse_lazy("invoice_groups")
success_message = _("The invoice_group has been deleted.")
class InvoiceDetailView(PermissionRequiredMixin, DetailView):
model = Invoice
permission_required = "tezor.view_invoice"
template_name = "tezor/invoice/full.html"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment