Compare commits

..

No commits in common. "ee3ec2152767113b81049ff2dbea88b8739a2a83" and "4bbb5de3c5547264dbfbde1f18f3eea28691c8f8" have entirely different histories.

55 changed files with 670 additions and 1263 deletions

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-04 16:18+0100\n" "POT-Creation-Date: 2023-04-22 15:16+0200\n"
"PO-Revision-Date: 2023-04-22 15:17+0200\n" "PO-Revision-Date: 2023-04-22 15:17+0200\n"
"Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n" "Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n"
"Language-Team: \n" "Language-Team: \n"
@ -37,22 +37,6 @@ msgstr "Défaut"
msgid "Accounts" msgid "Accounts"
msgstr "Comptes" msgstr "Comptes"
#: .\account\templates\account\account_detail.html:13
msgid "Edit account"
msgstr "Modifier le compte"
#: .\account\templates\account\account_detail.html:16
msgid "Statements"
msgstr "Relevés"
#: .\account\templates\account\account_detail.html:20
msgid "Transactions"
msgstr "Transactions"
#: .\account\templates\account\account_detail.html:25
msgid "History"
msgstr "Historique"
#: .\account\templates\account\account_form.html:5 #: .\account\templates\account\account_form.html:5
msgid "Create account" msgid "Create account"
msgstr "Créer un compte" msgstr "Créer un compte"
@ -60,3 +44,15 @@ msgstr "Créer un compte"
#: .\account\templates\account\account_form.html:8 #: .\account\templates\account\account_form.html:8
msgid "New account" msgid "New account"
msgstr "Nouveau compte" msgstr "Nouveau compte"
#: .\account\templates\account\account_form.html:13
msgid "Statements"
msgstr "Relevés"
#: .\account\templates\account\account_form.html:15
msgid "Transactions"
msgstr "Transactions"
#: .\account\templates\account\account_form.html:18
msgid "History"
msgstr "Historique"

View File

@ -1,29 +0,0 @@
{% extends "main/base.html" %}
{% load main_extras %}
{% load i18n %}
{% block title %}{{ object }} {{ block.super }}{% endblock %}
{% block link %}
{{ block.super }}
{% css "main/css/table.css" %}
{% css "main/css/plot.css" %}
{% endblock %}
{% block body %}
<h2>{{ object.icon|remix }}{{ object }}</h2>
<p>
<a href="{% url "edit_account" object.pk %}">{% translate "Edit account" %}</a>
</p>
<section>
<h3>{% translate "Statements" %}</h3>
{% include "statement/statement_table.html" %}
</section>
<section>
<h3>{% translate "Transactions" %}</h3>
{% include "transaction/transaction_table.html" %}
</section>
{% if history %}
<section>
<h3>{% translate "History" %}</h3>
{% include "history/plot.html" %}
</section>
{% endif %}
{% endblock %}

View File

@ -8,3 +8,15 @@
{% translate "New account" %} {% translate "New account" %}
{% endblock %} {% endblock %}
{% block h2 %}{{ form.instance.icon|remix }}{{ form.instance }}{% endblock %} {% block h2 %}{{ form.instance.icon|remix }}{{ form.instance }}{% endblock %}
{% block tables %}
{% if not form.instance|adding %}
<h3>{% translate "Statements" %}</h3>
{% include "statement/statement_table.html" %}
<h3>{% translate "Transactions" %}</h3>
{% include "transaction/transaction_table.html" %}
{% if history.data %}
<h3>{% translate "History" %}</h3>
{% include "history/plot.html" %}
{% endif %}
{% endif %}
{% endblock %}

View File

@ -1,13 +1,12 @@
from django.urls import path from django.urls import path
from statement.views import StatementCreateView from statement.views import StatementCreateView
from transaction.views import TransactionMonthView, TransactionYearView from transaction.views import TransactionMonthView
from . import views from . import views
urlpatterns = [ urlpatterns = [
path("new", views.AccountCreateView.as_view(), name="new_account"), path("new", views.AccountCreateView.as_view(), name="new_account"),
path("<account>", views.AccountDetailView.as_view(), name="account"), path("<account>", views.AccountUpdateView.as_view(), name="account"),
path("<account>/edit", views.AccountUpdateView.as_view(), name="edit_account"),
path( path(
"<account>/transactions", "<account>/transactions",
views.AccountTListView.as_view(), views.AccountTListView.as_view(),
@ -28,14 +27,9 @@ urlpatterns = [
views.AccountDeleteView.as_view(), views.AccountDeleteView.as_view(),
name="del_account", name="del_account",
), ),
path(
"<account>/history/<int:year>",
TransactionYearView.as_view(),
name="account_transaction_year",
),
path( path(
"<account>/history/<int:year>/<int:month>", "<account>/history/<int:year>/<int:month>",
TransactionMonthView.as_view(), TransactionMonthView.as_view(),
name="account_transaction_month", name="transaction_month",
), ),
] ]

View File

@ -1,12 +1,7 @@
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy from django.urls import reverse_lazy
from history.utils import history from history.utils import history
from main.views import ( from main.views import NummiCreateView, NummiDeleteView, NummiUpdateView
NummiCreateView,
NummiDeleteView,
NummiDetailView,
NummiUpdateView,
)
from statement.views import StatementListView from statement.views import StatementListView
from transaction.views import TransactionListView from transaction.views import TransactionListView
@ -24,20 +19,10 @@ class AccountUpdateView(NummiUpdateView):
form_class = AccountForm form_class = AccountForm
pk_url_kwarg = "account" pk_url_kwarg = "account"
class AccountDeleteView(NummiDeleteView):
model = Account
pk_url_kwarg = "account"
class AccountDetailView(NummiDetailView):
model = Account
pk_url_kwarg = "account"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
_max = 8 _max = 8
data = super().get_context_data(**kwargs) data = super().get_context_data(**kwargs)
account = data.get("object") account = data["form"].instance
_transactions = account.transaction_set.all() _transactions = account.transaction_set.all()
if _transactions.count() > _max: if _transactions.count() > _max:
@ -60,6 +45,11 @@ class AccountDetailView(NummiDetailView):
} }
class AccountDeleteView(NummiDeleteView):
model = Account
pk_url_kwarg = "account"
class AccountMixin: class AccountMixin:
def get_queryset(self): def get_queryset(self):
self.account = get_object_or_404( self.account = get_object_or_404(

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-04 16:18+0100\n" "POT-Creation-Date: 2023-11-25 12:05+0100\n"
"PO-Revision-Date: 2023-04-22 15:18+0200\n" "PO-Revision-Date: 2023-04-22 15:18+0200\n"
"Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n" "Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n"
"Language-Team: \n" "Language-Team: \n"
@ -38,18 +38,6 @@ msgstr "Budget"
msgid "Categories" msgid "Categories"
msgstr "Catégories" msgstr "Catégories"
#: .\category\templates\category\category_detail.html:12
msgid "Edit category"
msgstr "Modifier la catégorie"
#: .\category\templates\category\category_detail.html:15
msgid "Transactions"
msgstr "Transactions"
#: .\category\templates\category\category_detail.html:20
msgid "History"
msgstr "Historique"
#: .\category\templates\category\category_form.html:5 #: .\category\templates\category\category_form.html:5
msgid "Create category" msgid "Create category"
msgstr "Créer une catégorie" msgstr "Créer une catégorie"
@ -58,6 +46,14 @@ msgstr "Créer une catégorie"
msgid "New category" msgid "New category"
msgstr "Nouvelle catégorie" msgstr "Nouvelle catégorie"
#: .\category\templates\category\category_form.html:12
msgid "Transactions"
msgstr "Transactions"
#: .\category\templates\category\category_form.html:15
msgid "History"
msgstr "Historique"
#: .\category\templates\category\category_plot.html:15 #: .\category\templates\category\category_plot.html:15
msgid "Expenses" msgid "Expenses"
msgstr "Dépenses" msgstr "Dépenses"
@ -66,10 +62,10 @@ msgstr "Dépenses"
msgid "Income" msgid "Income"
msgstr "Revenus" msgstr "Revenus"
#: .\category\templates\category\category_plot.html:62 #: .\category\templates\category\category_plot.html:56
msgid "No transaction" msgid "No transaction"
msgstr "Aucune transaction" msgstr "Aucune transaction"
#: .\category\templates\category\category_plot.html:70 #: .\category\templates\category\category_plot.html:64
msgid "Total" msgid "Total"
msgstr "Total" msgstr "Total"

View File

@ -1,24 +0,0 @@
{% extends "main/base.html" %}
{% load main_extras i18n %}
{% block title %}{{ object }} {{ block.super }}{% endblock %}
{% block link %}
{{ block.super }}
{% css "main/css/table.css" %}
{% css "main/css/plot.css" %}
{% endblock %}
{% block body %}
<h2>{{ object.icon|remix }}{{ object }}</h2>
<p>
<a href="{% url "edit_category" object.pk %}">{% translate "Edit category" %}</a>
</p>
<section>
<h3>{% translate "Transactions" %}</h3>
{% include "transaction/transaction_table.html" %}
</section>
{% if history %}
<section>
<h3>{% translate "History" %}</h3>
{% include "history/plot.html" %}
</section>
{% endif %}
{% endblock %}

View File

@ -8,3 +8,11 @@
{% translate "New category" %} {% translate "New category" %}
{% endblock %} {% endblock %}
{% block h2 %}{{ form.instance.icon|remix }}{{ form.instance }}{% endblock %} {% block h2 %}{{ form.instance.icon|remix }}{{ form.instance }}{% endblock %}
{% block tables %}
<h3>{% translate "Transactions" %}</h3>
{% include "transaction/transaction_table.html" %}
{% if history.data %}
<h3>{% translate "History" %}</h3>
{% include "history/plot.html" %}
{% endif %}
{% endblock %}

View File

@ -1,4 +1,4 @@
{% load main_extras statement_extras %} {% load main_extras %}
{% load i18n %} {% load i18n %}
<div class="plot"> <div class="plot">
<table class="full-width"> <table class="full-width">
@ -23,9 +23,7 @@
<th scope="row" class="l"> <th scope="row" class="l">
{% if cat.category %} {% if cat.category %}
{% if month %} {% if month %}
<a href="{% url "category_transaction_month" cat.category month.year month.month %}">{{ cat.category__name }}</a> <a href="{% url "transaction_month" cat.category month.year month.month %}">{{ cat.category__name }}</a>
{% elif year %}
<a href="{% url "category_transaction_year" cat.category year.year %}">{{ cat.category__name }}</a>
{% else %} {% else %}
{{ cat.category__name }} {{ cat.category__name }}
{% endif %} {% endif %}
@ -34,28 +32,24 @@
<td class="c"> <td class="c">
{% if cat.category %}{{ cat.category__icon|remix }}{% endif %} {% if cat.category %}{{ cat.category__icon|remix }}{% endif %}
</td> </td>
<td class="value">{{ cat.sum_m|pmvalue }}</td> <td class="value">{{ cat.sum_m|pmrvalue }}</td>
<td class="bar m"> <td class="bar m">
{% if cat.sum_m %} <div style="width: {% widthratio cat.sum_m max -100 %}%"></div>
<div style="width: {% widthratio cat.sum_m max -100 %}%"></div>
{% endif %}
{% if cat.sum < 0 %} {% if cat.sum < 0 %}
<div class="tot" style="width:{% widthratio cat.sum max -100 %}%"> <div class="tot" style="width:{% widthratio cat.sum max -100 %}%">
<span>{{ cat.sum|pmvalue }}</span> <span>{{ cat.sum|pmrvalue }}</span>
</div> </div>
{% endif %} {% endif %}
</td> </td>
<td class="bar p"> <td class="bar p">
{% if cat.sum_p %} <div style="width: {% widthratio cat.sum_p max 100 %}%"></div>
<div style="width: {% widthratio cat.sum_p max 100 %}%"></div>
{% endif %}
{% if cat.sum > 0 %} {% if cat.sum > 0 %}
<div class="tot" style="width:{% widthratio cat.sum max 100 %}%"> <div class="tot" style="width:{% widthratio cat.sum max 100 %}%">
<span>{{ cat.sum|pmvalue }}</span> <span>{{ cat.sum|pmrvalue }}</span>
</div> </div>
{% endif %} {% endif %}
</td> </td>
<td class="value">{{ cat.sum_p|pmvalue }}</td> <td class="value">{{ cat.sum_p|pmrvalue }}</td>
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
@ -64,16 +58,16 @@
{% endfor %} {% endfor %}
{% endspaceless %} {% endspaceless %}
</tbody> </tbody>
<tfoot> {% if categories %}
{% if categories %} <tfoot>
<tr> <tr>
<th scope="row" colspan="2" class="l">{% translate "Total" %}</th> <th scope="row" colspan="2" class="l">{% translate "Total" %}</th>
<td class="value">{{ total_m|pmvalue }}</td> <td class="value">{{ total_m|pmrvalue }}</td>
<td class="bar m"> <td class="bar m">
<div style="width: {% widthratio total_m max -100 %}%"></div> <div style="width: {% widthratio total_m max -100 %}%"></div>
{% if total < 0 %} {% if total < 0 %}
<div class="tot" style="width:{% widthratio total max -100 %}%"> <div class="tot" style="width:{% widthratio total max -100 %}%">
<span>{{ total|pmvalue }}</span> <span>{{ total|pmrvalue }}</span>
</div> </div>
{% endif %} {% endif %}
</td> </td>
@ -81,35 +75,13 @@
<div style="width: {% widthratio total_p max 100 %}%"></div> <div style="width: {% widthratio total_p max 100 %}%"></div>
{% if total > 0 %} {% if total > 0 %}
<div class="tot" style="width:{% widthratio total max 100 %}%"> <div class="tot" style="width:{% widthratio total max 100 %}%">
<span>{{ total|pmvalue }}</span> <span>{{ total|pmrvalue }}</span>
</div> </div>
{% endif %} {% endif %}
</td> </td>
<td class="value">{{ total_p|pmvalue }}</td> <td class="value">{{ total_p|pmrvalue }}</td>
</tr> </tr>
{% endif %} </tfoot>
{% if statement %} {% endif %}
<tr>
<th scope="row" class="l">{% translate "Expected total" %}</th>
<td class="c">{{ total|check:statement.diff }}</td>
<td></td>
<td class="bar m">
{% if statement.diff < 0 %}
<div class="tot" style="width:{% widthratio statement.diff max -100 %}%">
<span>{{ statement.diff|pmvalue }}</span>
</div>
{% endif %}
</td>
<td class="bar p">
{% if statement.diff >= 0 %}
<div class="tot" style="width:{% widthratio statement.diff max 100 %}%">
<span>{{ statement.diff|pmvalue }}</span>
</div>
{% endif %}
</td>
<td></td>
</tr>
{% endif %}
</tfoot>
</table> </table>
</div> </div>

View File

@ -6,11 +6,10 @@ register = template.Library()
@register.inclusion_tag("category/category_plot.html") @register.inclusion_tag("category/category_plot.html")
def category_plot(transactions, budget=True, **kwargs): def category_plot(transactions, **kwargs):
if budget:
transactions = transactions.exclude(category__budget=False)
categories = ( categories = (
transactions.values("category", "category__name", "category__icon") transactions.exclude(category__budget=False)
.values("category", "category__name", "category__icon")
.annotate( .annotate(
sum=models.Sum("value"), sum=models.Sum("value"),
sum_m=models.Sum("value", filter=models.Q(value__lt=0)), sum_m=models.Sum("value", filter=models.Q(value__lt=0)),

View File

@ -1,26 +1,20 @@
from django.urls import path from django.urls import path
from transaction.views import TransactionMonthView, TransactionYearView from transaction.views import TransactionMonthView
from . import views from . import views
urlpatterns = [ urlpatterns = [
path("new", views.CategoryCreateView.as_view(), name="new_category"), path("new", views.CategoryCreateView.as_view(), name="new_category"),
path("<category>", views.CategoryDetailView.as_view(), name="category"), path("<category>", views.CategoryUpdateView.as_view(), name="category"),
path("<category>/edit", views.CategoryUpdateView.as_view(), name="edit_category"),
path( path(
"<category>/transactions", "<category>/transactions",
views.CategoryTListView.as_view(), views.CategoryTListView.as_view(),
name="category_transactions", name="category_transactions",
), ),
path("<category>/delete", views.CategoryDeleteView.as_view(), name="del_category"), path("<category>/delete", views.CategoryDeleteView.as_view(), name="del_category"),
path(
"<category>/history/<int:year>",
TransactionYearView.as_view(),
name="category_transaction_year",
),
path( path(
"<category>/history/<int:year>/<int:month>", "<category>/history/<int:year>/<int:month>",
TransactionMonthView.as_view(), TransactionMonthView.as_view(),
name="category_transaction_month", name="transaction_month",
), ),
] ]

View File

@ -1,12 +1,7 @@
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy from django.urls import reverse_lazy
from history.utils import history from history.utils import history
from main.views import ( from main.views import NummiCreateView, NummiDeleteView, NummiUpdateView
NummiCreateView,
NummiDeleteView,
NummiDetailView,
NummiUpdateView,
)
from transaction.views import TransactionListView from transaction.views import TransactionListView
from .forms import CategoryForm from .forms import CategoryForm
@ -23,15 +18,10 @@ class CategoryUpdateView(NummiUpdateView):
form_class = CategoryForm form_class = CategoryForm
pk_url_kwarg = "category" pk_url_kwarg = "category"
class CategoryDetailView(NummiDetailView):
model = Category
pk_url_kwarg = "category"
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
_max = 8 _max = 8
data = super().get_context_data(**kwargs) data = super().get_context_data(**kwargs)
category = data["object"] category = data["form"].instance
data["transactions"] = category.transaction_set.all()[:_max] data["transactions"] = category.transaction_set.all()[:_max]
if len(data["transactions"]) == _max: if len(data["transactions"]) == _max:

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-03 15:51+0100\n" "POT-Creation-Date: 2023-04-22 15:16+0200\n"
"PO-Revision-Date: 2023-04-22 15:18+0200\n" "PO-Revision-Date: 2023-04-22 15:18+0200\n"
"Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n" "Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n"
"Language-Team: \n" "Language-Team: \n"
@ -17,18 +17,14 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"X-Generator: Poedit 3.2.2\n" "X-Generator: Poedit 3.2.2\n"
#: .\history\templates\history\plot.html:17 #: .\history\templates\history\plot.html:15
msgid "Month" msgid "Month"
msgstr "Mois" msgstr "Mois"
#: .\history\templates\history\plot.html:18 #: .\history\templates\history\plot.html:16
msgid "Expenses" msgid "Expenses"
msgstr "Dépenses" msgstr "Dépenses"
#: .\history\templates\history\plot.html:19 #: .\history\templates\history\plot.html:17
msgid "Income" msgid "Income"
msgstr "Revenus" msgstr "Revenus"
#: .\history\templates\history\plot.html:55
msgid "Year"
msgstr "Année"

View File

@ -1,8 +1,6 @@
{% load main_extras %} {% load main_extras %}
{% load history_extras %}
{% load transaction_extras %}
{% load i18n %} {% load i18n %}
<div class="history plot"> <div class="plot">
<table class="full-width"> <table class="full-width">
<colgroup> <colgroup>
<col class="icon"> <col class="icon">
@ -20,67 +18,46 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for date in history.data reversed %} {% spaceless %}
{% ifchanged %} {% for date in history.data %}
{% if date.sum_m or date.sum_p %} <tr>
<tr {% if not date.month.month|divisibleby:"2" %}class="even"{% endif %}> <td class="icon">
<td class="icon">{% up_down_icon date.sum %}</td> <span class="ri-{% if date.sum > 0 %}arrow-up-s-line green{% elif date.sum < 0 %}arrow-down-s-line red{% endif %}"></span>
<th class="date" scope="row"> </td>
{% if year %} <th class="date" scope="row">
{% month_url date.month fmt="F" %} {% if date.has_transactions %}
{% if account %}
<a href="{% url "transaction_month" account=account.pk year=date.month.year month=date.month.month %}">{{ date.month|date:"Y-m" }}</a>
{% elif category %}
<a href="{% url "transaction_month" category=category.pk year=date.month.year month=date.month.month %}">{{ date.month|date:"Y-m" }}</a>
{% else %} {% else %}
{% month_url date.month %} <a href="{% url "transaction_month" year=date.month.year month=date.month.month %}">{{ date.month|date:"Y-m" }}</a>
{% endif %} {% endif %}
</th> {% else %}
<td class="value">{{ date.sum_m|pmrvalue }}</td> {{ date.month|date:"Y-m" }}
<td class="bar m">{% plot_bar date.sum date.sum_m history.max.pm %}</td> {% endif %}
<td class="bar p">{% plot_bar date.sum date.sum_p history.max.pm %}</td> </th>
<td class="value">{{ date.sum_p|pmrvalue }}</td> <td class="value">{{ date.sum_m|pmrvalue }}</td>
</tr> <td class="bar m">
{% else %} <div style="width: {% widthratio date.sum_m history.max -100 %}%"></div>
<tr class="empty"> {% if date.sum < 0 %}
<td colspan="6" class="empty"></td> <div class="tot" style="width:{% widthratio date.sum history.max -100 %}%">
</tr> <span>{{ date.sum|pmrvalue }}</span>
{% endif %} </div>
{% endifchanged %} {% endif %}
{% endfor %} </td>
</tbody> <td class="bar p">
</table> <div style="width: {% widthratio date.sum_p history.max 100 %}%"></div>
</div> {% if date.sum > 0 %}
<div class="calendar"> <div class="tot" style="width:{% widthratio date.sum history.max 100 %}%">
<table class="full-width"> <span>{{ date.sum|pmrvalue }}</span>
<thead> </div>
<tr> {% endif %}
{% if not year %} </td>
<th scope="col">{% translate "Year" %}</th> <td class="value">{{ date.sum_p|pmrvalue }}</td>
{% endif %} </tr>
{% calendar_head %} {% endfor %}
</tr> {% endspaceless %}
</thead>
<tbody>
{% regroup history.data by month.year as years_list %}
{% for y, y_data in years_list reversed %}
<tr>
{% if not year %}
<th class="date" scope="row">{% year_url y %}</th>
{% endif %}
{% for m in y_data %}
{% if forloop.parentloop.last and forloop.first %}
{% empty_calendar_cells_start m.month.month %}
{% endif %}
{% if m %}
<td class="{% if m.sum > 0 %}p{% else %}m{% endif %}"
style="--opacity: {% calendar_opacity m.sum history.max.sum %}"
title="{{ m.sum|pmrvalue }}">{% up_down_icon m.sum %}</td>
{% else %}
<td></td>
{% endif %}
{% if forloop.parentloop.first and forloop.last %}
{% empty_calendar_cells_end m.month.month %}
{% endif %}
{% endfor %}
</tr>
{% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -1,62 +0,0 @@
import math
from django import template
from django.utils.safestring import mark_safe
from main.templatetags.main_extras import pmrvalue, remix
register = template.Library()
@register.simple_tag
def calendar_opacity(v, vmax):
return f"{math.sin(math.fabs(v/vmax)*math.pi/2):.0%}"
@register.simple_tag
def empty_calendar_cells(n):
return mark_safe(n * "<td></td>")
@register.simple_tag
def empty_calendar_cells_start(n):
return empty_calendar_cells(n - 1)
@register.simple_tag
def empty_calendar_cells_end(n):
return empty_calendar_cells(12 - n)
@register.simple_tag
def up_down_icon(val):
if val > 0:
return remix("arrow-up-s", "green")
elif val < 0:
return remix("arrow-down-s", "red")
return remix("equal", "white")
@register.simple_tag
def plot_bar(s, sum_pm, s_max):
_res = ""
if sum_pm:
_w = abs(sum_pm / s_max)
_res += f"""<div style="width: {_w:.1%}"></div>"""
if sum_pm is not None and s * sum_pm > 0:
_w = abs(s / s_max)
_res += (
f"""<div class="tot" style="width: {_w:.1%}">"""
f"""<span>{pmrvalue(s)}</span></div>"""
)
return mark_safe(_res)
@register.simple_tag
def calendar_head():
months = range(1, 13)
th = (f"""<th>{month:02d}</th>""" for month in months)
return mark_safe("".join(th))

View File

@ -1,47 +1,48 @@
import datetime from django.db import models
from django.db.models import Func, Max, Min, Q, Sum, Value
from django.db.models.functions import Now, TruncMonth
from django.db.models import Max, Min, Q, Sum
from django.db.models.functions import Abs, TruncMonth class GenerateMonth(Func):
function = "generate_series"
template = "%(function)s(%(expressions)s, '1 month')::date"
def history(transaction_set): def history(transaction_set):
if not transaction_set.exists(): if not transaction_set.exists():
return None return None
_transaction_month = transaction_set.values(month=TruncMonth("date")).order_by( _transaction_month = transaction_set.values(month=TruncMonth("date")).order_by(
"-date" "-date"
) )
_first_month = _transaction_month.last()["month"] _months = (
_last_month = _transaction_month.first()["month"] transaction_set.values(
month=GenerateMonth(
_transaction_month.last()["month"],
Now(output_field=models.DateField()),
)
)
.annotate(
sum_m=Value(0), sum_p=Value(0), sum=Value(0), has_transactions=Value(0)
)
.difference(
_transaction_month.annotate(
sum_m=Value(0), sum_p=Value(0), sum=Value(0), has_transactions=Value(0)
)
)
)
_history = _transaction_month.annotate( _history = _transaction_month.annotate(
sum_p=Sum("value", filter=Q(value__gt=0)), sum_p=Sum("value", filter=Q(value__gt=0)),
sum_m=Sum("value", filter=Q(value__lt=0)), sum_m=Sum("value", filter=Q(value__lt=0)),
sum=Sum("value"), sum=Sum("value"),
has_transactions=Value(1),
).order_by("-month") ).order_by("-month")
_data = [
_history.filter(month=datetime.date(y, m + 1, 1)).first()
or {"month": datetime.date(y, m + 1, 1), "sum": 0}
for y in range(
_first_month.year,
_last_month.year + 1,
)
for m in range(
_first_month.month - 1 if _first_month.year == y else 0,
_last_month.month if _last_month.year == y else 12,
)
]
return { return {
"data": _data, "data": _history.union(_months).order_by("-month"),
"max": { "max": max(
"pm": max( _history.aggregate(
_history.aggregate( max=Max("sum_p", default=0),
max=Max("sum_p", default=0), min=-Min("sum_m", default=0),
min=-Min("sum_m", default=0), ).values(),
).values(), ),
),
"sum": _history.aggregate(max=Max(Abs("sum")))["max"],
},
} }

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-04 16:18+0100\n" "POT-Creation-Date: 2023-11-25 12:05+0100\n"
"PO-Revision-Date: 2023-04-23 08:03+0200\n" "PO-Revision-Date: 2023-04-23 08:03+0200\n"
"Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n" "Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n"
"Language-Team: \n" "Language-Team: \n"
@ -21,51 +21,67 @@ msgstr ""
msgid "User" msgid "User"
msgstr "Utilisateur" msgstr "Utilisateur"
#: .\main\templates\main\base.html:28 #: .\main\templates\main\base.html:27
msgid "Skip to main content" msgid "Skip to main content"
msgstr "Aller au contenu principal" msgstr "Aller au contenu principal"
#: .\main\templates\main\base.html:34 #: .\main\templates\main\base.html:33
msgid "Home" msgid "Home"
msgstr "Accueil" msgstr "Accueil"
#: .\main\templates\main\base.html:39 #: .\main\templates\main\base.html:38 .\main\templates\main\index.html:40
msgid "Statements" msgid "Statements"
msgstr "Relevés" msgstr "Relevés"
#: .\main\templates\main\base.html:45 #: .\main\templates\main\base.html:44 .\main\templates\main\index.html:24
msgid "Transactions" msgid "Transactions"
msgstr "Transactions" msgstr "Transactions"
#: .\main\templates\main\base.html:51 .\main\templates\main\list.html:10 #: .\main\templates\main\base.html:50
#: .\main\templates\main\list.html:34 msgid "Create account"
msgstr "Créer un compte"
#: .\main\templates\main\base.html:55
msgid "Create statement"
msgstr "Créer un relevé"
#: .\main\templates\main\base.html:60
msgid "Create category"
msgstr "Créer une catégorie"
#: .\main\templates\main\base.html:65
msgid "Create transaction"
msgstr "Créer une transaction"
#: .\main\templates\main\base.html:70 .\main\templates\main\list.html:10
#: .\main\templates\main\list.html:36
msgid "Search" msgid "Search"
msgstr "Rechercher" msgstr "Rechercher"
#: .\main\templates\main\base.html:54 #: .\main\templates\main\base.html:73
msgid "Log out" msgid "Log out"
msgstr "Se déconnecter" msgstr "Se déconnecter"
#: .\main\templates\main\base.html:59 .\main\templates\main\form\login.html:6 #: .\main\templates\main\base.html:78 .\main\templates\main\form\login.html:6
#: .\main\templates\main\login.html:11 #: .\main\templates\main\login.html:14
msgid "Log in" msgid "Log in"
msgstr "Se connecter" msgstr "Se connecter"
#: .\main\templates\main\base.html:65 #: .\main\templates\main\base.html:84
#, python-format #, python-format
msgid "Logged in as <strong>%(user)s</strong>" msgid "Logged in as <strong>%(user)s</strong>"
msgstr "Connecté en tant que <strong>%(user)s</strong>" msgstr "Connecté en tant que <strong>%(user)s</strong>"
#: .\main\templates\main\confirm_delete.html:15 #: .\main\templates\main\confirm_delete.html:19
#, python-format #, python-format
msgid "Are you sure you want do delete <strong>%(object)s</strong> ?" msgid "Are you sure you want do delete <strong>%(object)s</strong> ?"
msgstr "Êtes-vous sûr de vouloir supprimer <strong>%(object)s</strong> ?" msgstr "Êtes-vous sûr de vouloir supprimer <strong>%(object)s</strong> ?"
#: .\main\templates\main\confirm_delete.html:19 #: .\main\templates\main\confirm_delete.html:23
msgid "Cancel" msgid "Cancel"
msgstr "Annuler" msgstr "Annuler"
#: .\main\templates\main\confirm_delete.html:20 #: .\main\templates\main\confirm_delete.html:24
msgid "Confirm" msgid "Confirm"
msgstr "Confirmer" msgstr "Confirmer"
@ -89,51 +105,26 @@ msgstr "Créer"
msgid "Save" msgid "Save"
msgstr "Enregistrer" msgstr "Enregistrer"
#: .\main\templates\main\index.html:13 #: .\main\templates\main\index.html:15
msgid "Accounts" msgid "Accounts"
msgstr "Comptes" msgstr "Comptes"
#: .\main\templates\main\index.html:17 #: .\main\templates\main\index.html:20
msgid "Account"
msgstr "Compte"
#: .\main\templates\main\index.html:18
msgid "Balance"
msgstr "Solde"
#: .\main\templates\main\index.html:19 .\main\templates\main\index.html:31
msgid "Edit"
msgstr "Modifier"
#: .\main\templates\main\index.html:36
msgid "No account" msgid "No account"
msgstr "Aucun compte" msgstr "Aucun compte"
#: .\main\templates\main\index.html:43 #: .\main\templates\main\index.html:28
msgid "Create account"
msgstr "Créer un compte"
#: .\main\templates\main\index.html:50
msgid "Categories" msgid "Categories"
msgstr "Catégories" msgstr "Catégories"
#: .\main\templates\main\index.html:56 #: .\main\templates\main\index.html:34
msgid "Create category" msgid "No category"
msgstr "Créer une catégorie" msgstr "Aucune catégorie"
#: .\main\templates\main\index.html:63 #: .\main\templates\main\index.html:44
msgid "History" msgid "History"
msgstr "Historique" msgstr "Historique"
#: .\main\views.py:69 #: .\main\views.py:68
msgid "was created successfully" msgid "was created successfully"
msgstr "a été créé avec succès" msgstr "a été créé avec succès"
#~ msgid "Create statement"
#~ msgstr "Créer un relevé"
#~ msgid "Create transaction"
#~ msgstr "Créer une transaction"
#~ msgid "No category"
#~ msgstr "Aucune catégorie"

View File

@ -5,65 +5,58 @@ form ul.errorlist {
margin: 0; margin: 0;
} }
form { form > table > tbody > tr > th {
> table > tbody > tr > th { background: var(--bg-01);
background: var(--bg-01); background-clip: padding-box;
background-clip: padding-box;
border-right: 1px solid var(--gray);
}
tbody :is(input, select, textarea) {
font: inherit;
border: none;
background: transparent;
width: 100%;
height: 100%;
line-height: 1.5;
}
input[type="checkbox"] {
width: initial;
}
tfoot {
text-align: right;
}
} }
table.file-input { form tbody input,
tr { form tbody select,
border: none; form tbody textarea {
font: inherit;
:first-child { border: none;
padding-left: 0; background: var(--bg);
} width: 100%;
:last-child { height: 100%;
padding-right: 0; line-height: 1.5;
} }
} form input[type="checkbox"] {
th { width: initial;
text-align: left; }
} table.file-input tr {
border: none;
}
table.file-input th {
text-align: left;
}
table.file-input tr :first-child {
padding-left: 0;
}
table.file-input tr :last-child {
padding-right: 0;
} }
.buttons { form tfoot {
input { text-align: right;
font: inherit; }
line-height: 1.5; .buttons input {
margin-left: var(--gap); font: inherit;
border-radius: var(--radius); line-height: 1.5;
padding: 0 var(--gap); margin-left: var(--gap);
cursor: pointer; border-radius: var(--radius);
padding: 0 var(--gap);
&:hover { cursor: pointer;
text-decoration: underline; }
} .buttons input:hover {
&[type="submit"] { text-decoration: underline;
border: 0.1rem solid var(--green); }
background: var(--green-1); .buttons input[type="submit"] {
} border: 0.1rem solid var(--green);
&[type="reset"] { background: var(--green-1);
border: 0.1rem solid var(--red); }
background: var(--red-1); .buttons input[type="reset"] {
} border: 0.1rem solid var(--red);
} background: var(--red-1);
a.del { }
color: var(--red); .buttons a.del {
} color: var(--red);
} }

View File

@ -1,5 +1,4 @@
@import "https://rsms.me/inter/inter.css"; @import "https://rsms.me/inter/inter.css";
@import "https://cdn.jsdelivr.net/npm/remixicon@4.0.0/fonts/remixicon.css";
*, *,
*::before, *::before,
@ -40,8 +39,7 @@
--border: 0.5em; --border: 0.5em;
--radius: 0.25em; --radius: 0.25em;
--default-ffs: "dlig", "ss01", "ss04"; --num: "tnum", "ss01", "ss02", "case";
--num: var(--default-ffs), "tnum", "case";
} }
body { body {
@ -53,7 +51,6 @@ body {
display: grid; display: grid;
grid-template-columns: max-content 1fr; grid-template-columns: max-content 1fr;
font-feature-settings: var(--default-ffs);
} }
p { p {
@ -63,29 +60,9 @@ a {
color: var(--text-link); color: var(--text-link);
text-decoration: none; text-decoration: none;
display: inline-block; display: inline-block;
}
&:is(:hover, :focus) { a:hover {
text-decoration: underline; text-decoration: underline;
}
&.big-link {
margin-right: 1em;
padding: 0 0.5em;
color: white;
background: var(--green);
border-radius: var(--radius);
height: 1.5rem;
line-height: 1.5rem;
[class^="ri-"] {
margin-right: 0.5em;
}
&.add {
color: var(--text-link);
background: var(--bg-01);
}
}
} }
.red { .red {
@ -106,23 +83,6 @@ main {
grid-column: 2; grid-column: 2;
grid-row: 1; grid-row: 1;
overflow-x: hidden; overflow-x: hidden;
h2.new {
opacity: 0.8;
}
.split {
display: grid;
column-gap: var(--gap);
row-gap: var(--gap);
grid-template-columns: 100%;
@media (width > 720px) {
grid-template-columns: max-content 1fr;
}
& > section > :first-child {
margin-top: 0;
}
}
} }
nav { nav {
grid-column: 1; grid-column: 1;
@ -135,46 +95,38 @@ nav {
background: var(--bg-01); background: var(--bg-01);
line-height: 2rem; line-height: 2rem;
h1 img {
height: 1cap;
}
ul {
list-style: none;
padding: 0;
margin: 0;
position: relative;
}
a {
&.skip-link {
font-weight: 300;
&:is(:active, :focus) {
font-weight: 500;
}
}
display: grid;
grid-template-columns: 1fr max-content;
align-items: baseline;
[class^="ri-"] {
height: 1.5em;
width: 1.5em;
line-height: 1.5em;
border-radius: var(--radius);
}
&.cur {
font-weight: 550;
[class^="ri-"] {
background: var(--text-link);
color: var(--bg);
}
}
}
} }
:is(nav, main) > :first-child, nav h1 img {
main > section:first-child > :first-child { height: 1cap;
}
nav ul {
list-style: none;
padding: 0;
margin: 0;
position: relative;
}
nav .skip-link {
opacity: 0.8;
font-weight: 300;
}
nav .skip-link:active,
nav .skip-link:focus {
opacity: initial;
font-weight: 500;
}
nav a {
display: block;
}
nav a.cur {
font-weight: 550;
}
nav a.cur::after {
content: "◎";
position: absolute;
right: 0;
}
nav > :first-child,
main > :first-child {
margin-top: 0; margin-top: 0;
} }
footer { footer {
@ -183,46 +135,16 @@ footer {
font-weight: 250; font-weight: 250;
} }
.pagination { #pagination {
text-align: center; text-align: center;
font-feature-settings: var(--num); font-feature-settings: var(--num);
}
a { #pagination a {
min-width: 1rem; width: 2rem;
padding: 0 0.5rem; }
#pagination a.cur {
&.cur { font-weight: 650;
font-weight: 650; text-decoration: underline dotted;
text-decoration: underline dotted;
&:is(:hover, :focus) {
text-decoration: underline;
}
}
}
@media (width > 720px) {
&.n3 {
display: grid;
grid-template-columns: repeat(3, 1fr);
width: max-content;
margin: 0.5rem auto;
.prev {
grid-column: 1;
}
.cur {
grid-column: 2;
}
.next {
grid-column: 3;
}
}
}
& + section :first-child {
margin-top: 0;
}
} }
@media (width < 1024px) { @media (width < 1024px) {
@ -238,34 +160,15 @@ footer {
height: initial; height: initial;
} }
} }
a.big-link {
margin-right: 1em;
}
a.big-link [class^="ri-"] {
margin-right: 0.5em;
}
[class^="ri-"] { [class^="ri-"] {
display: inline-block;
text-align: center;
font-weight: normal; font-weight: normal;
&.green,
&.red,
&.white {
&.green {
background: var(--green);
color: var(--bg);
}
&.red {
background: var(--red);
color: var(--bg);
}
&.white {
background: var(--bg-01);
}
border-radius: var(--radius);
height: 1.5em;
width: 1.5em;
line-height: 1.5em;
}
h2 & {
margin-right: 0.5em;
}
} }
h1, h1,
@ -285,6 +188,9 @@ h2 {
h3 { h3 {
font-size: 1.5rem; font-size: 1.5rem;
} }
main h2.new {
opacity: 0.8;
}
p { p {
margin: 0.5em 0; margin: 0.5em 0;
} }
@ -295,42 +201,18 @@ ul.messages {
margin: 0; margin: 0;
background: var(--bg-01); background: var(--bg-01);
padding: 0; padding: 0;
}
li { ul.messages li {
padding: calc(var(--gap) / 2) var(--gap); padding: calc(var(--gap) / 2) var(--gap);
border-left: var(--border) solid var(--gray); border-left: var(--border) solid var(--gray);
&.msg-level-25 {
border-left-color: var(--green-1);
}
&.msg-level-30 {
border-left-color: var(--red-1);
}
&.msg-level-40 {
border-left-color: var(--red);
}
}
} }
.backlinks { ul.messages li.msg-level-25 {
display: grid; border-left-color: var(--green-1);
grid-template-columns: repeat(2, 1fr); }
ul.messages li.msg-level-30 {
p { border-left-color: var(--red-1);
grid-column: 1; }
ul.messages li.msg-level-40 {
&.back { border-left-color: var(--red);
grid-column: 2;
text-align: right;
a {
margin-right: 0;
margin-left: 1em;
[class^="ri-"] {
margin-right: 0em;
margin-left: 0.5em;
}
}
}
}
} }

View File

@ -1,118 +1,76 @@
table.full-width col.bar { table.full-width col.bar {
width: auto; width: auto;
} }
.plot { .plot {
overflow-x: auto; overflow-x: auto;
td.bar {
position: relative;
padding: 0;
overflow: hidden;
@media (width < 720px) {
width: 0;
}
div {
position: absolute;
height: 0.5rem;
top: 0;
&:not(.tot) {
width: 0;
box-sizing: border-box;
z-index: 1;
display: inline-block;
}
&.tot {
z-index: 10;
height: 0.5rem;
span {
position: absolute;
display: inline-block;
white-space: nowrap;
margin: 0 var(--gap);
font-weight: 650;
top: 0.5rem;
line-height: 1.5rem;
height: 1.5rem;
font-feature-settings: var(--num);
}
}
}
&.p div {
left: 0;
border-radius: 0 var(--radius) var(--radius) 0;
background: var(--green-1);
&.tot {
background: var(--green);
span {
left: 0;
}
}
}
&.m div {
right: 0;
border-radius: var(--radius) 0 0 var(--radius);
background: var(--red-1);
&.tot {
background: var(--red);
span {
right: 0;
}
}
}
}
&.history tbody tr {
background: initial;
}
tbody tr {
&.empty {
height: 0.5rem;
}
&.even {
background: #eeeeff;
}
}
tfoot {
background: var(--bg-01);
}
} }
.calendar { .plot td.bar {
overflow-x: auto; position: relative;
padding: 0;
}
.plot td.bar div {
position: absolute;
height: 0.5rem;
top: 0;
}
margin-top: var(--gap); .plot td.bar div:not(.tot) {
width: 0;
box-sizing: border-box;
z-index: 1;
display: inline-block;
}
.plot td.bar.p div {
left: 0;
border-radius: 0 var(--radius) var(--radius) 0;
}
.plot td.bar.m div {
right: 0;
border-radius: var(--radius) 0 0 var(--radius);
}
.plot td.bar.m div {
background: var(--red-1);
}
.plot td.bar.p div {
background: var(--green-1);
}
.plot td.bar div.tot {
z-index: 10;
height: 0.5rem;
}
.plot td.bar.m div.tot {
background: var(--red);
}
.plot td.bar.p div.tot {
background: var(--green);
}
.plot td.bar div.tot span {
position: absolute;
display: inline-block;
white-space: nowrap;
margin: 0 var(--gap);
font-weight: 650;
top: 0.5rem;
line-height: 1.5rem;
height: 1.5rem;
font-feature-settings: var(--num); font-feature-settings: var(--num);
}
.plot td.bar.p div.tot span {
left: 0;
}
.plot td.bar.m div.tot span {
right: 0;
}
table { @media (width < 720px) {
tbody tr { .plot .bar {
background: initial; width: 0;
&:not(:last-child) { overflow: hidden;
border-bottom: none;
}
&:not(:first-child) {
border-top: none;
}
td {
text-align: center;
background-color: color-mix(
in hsl,
var(--td-bg, var(--bg)) var(--opacity),
var(--bg)
);
&.p {
--td-bg: var(--green);
}
&.m {
--td-bg: var(--red);
}
}
}
} }
} }
.plot tfoot {
background: var(--bg-01);
}

View File

@ -1,52 +1,37 @@
.table, .table,
form { form {
overflow-x: auto; overflow-x: auto;
width: 100%;
} }
table { table {
border-collapse: collapse; border-collapse: collapse;
&.full-width {
width: 100%;
col {
width: 8rem;
}
}
col.icon {
width: 1ch;
}
thead tr:not(.new) {
background: var(--bg-01);
}
tr {
border: 1px solid var(--gray);
height: 2rem;
line-height: 2rem;
tbody &:where(:nth-of-type(even)) {
background: #eeeeff;
}
&.more,
&.new {
text-align: center;
border-style: dashed;
}
}
td,
th {
padding: 0 var(--gap);
position: relative;
white-space: nowrap;
text-overflow: ellipsis;
&.empty {
text-align: center;
opacity: 0.8;
font-weight: 300;
}
}
} }
table.more tbody:last-child tr:last-child {
border-bottom: 0.1rem dashed var(--gray);
}
table.full-width {
width: 100%;
}
thead {
background: var(--bg-01);
}
table.full-width col {
width: 8rem;
}
table col.icon {
width: 1ch;
}
tr {
border: 0.1rem solid var(--gray);
height: 2rem;
line-height: 2rem;
}
td,
th {
padding: 0 var(--gap);
position: relative;
white-space: nowrap;
text-overflow: ellipsis;
}
.date, .date,
.value { .value {
font-feature-settings: var(--num); font-feature-settings: var(--num);
@ -62,3 +47,8 @@ table {
.date { .date {
text-align: center; text-align: center;
} }
td.empty {
text-align: center;
opacity: 0.8;
font-weight: 300;
}

View File

@ -1,17 +1,17 @@
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% load main_extras %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="fr"> <html lang="fr">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1"> <meta name="viewport" content="width=device-width,initial-scale=1" />
<title> <title>
{% block title %}Nummi{% endblock %} {% block title %}Nummi{% endblock %}
</title> </title>
{% block link %} {% block link %}
<link rel="icon" href="{% static "main/svg/logo.svg" %}" type="image/svg+xml"> <link rel="icon" href="{% static "main/svg/logo.svg" %}" type="image/svg+xml" />
{% css "main/css/main.css" %} <link rel="stylesheet" href="{% static "main/css/main.css" %}" type="text/css" />
<link rel="stylesheet" href="{% static "main/remixicon/remixicon.css" %}" type="text/css" />
{% endblock %} {% endblock %}
</head> </head>
<body> <body>
@ -19,7 +19,7 @@
{% spaceless %} {% spaceless %}
<nav> <nav>
<h1> <h1>
<img src="{% static "main/svg/logo.svg" %}" alt=""> <img src="{% static "main/svg/logo.svg" %}" alt="" />
Nummi Nummi
</h1> </h1>
<ul> <ul>
@ -30,52 +30,60 @@
<li> <li>
<a href="{% url "index" %}" <a href="{% url "index" %}"
class="home{% if request.resolver_match.url_name == "index" %} cur{% endif %}" class="home{% if request.resolver_match.url_name == "index" %} cur{% endif %}"
accesskey="h"> accesskey="h">{% translate "Home" %}</a>
<span>{% translate "Home" %}</span>
{{ "home"|remix }}
</a>
</li> </li>
<li> <li>
<a href="{% url "statements" %}" <a href="{% url "statements" %}"
class="{% if request.resolver_match.url_name == "statements" %}cur{% endif %}"> class="{% if request.resolver_match.url_name == "statements" %}cur{% endif %}">
<span>{% translate "Statements" %}</span> {% translate "Statements" %}
{{ "file"|remix }}
</a> </a>
</li> </li>
<li> <li>
<a href="{% url "transactions" %}" <a href="{% url "transactions" %}"
class="{% if request.resolver_match.url_name == "transactions" %}cur{% endif %}"> class="{% if request.resolver_match.url_name == "transactions" %}cur{% endif %}">
<span>{% translate "Transactions" %}</span> {% translate "Transactions" %}
{{ "receipt"|remix }}
</a> </a>
</li> </li>
<li>
<a href="{% url "new_account" %}"
class="{% if request.resolver_match.url_name == "new_account" %}cur{% endif %}"
accesskey="a">{% translate "Create account" %}</a>
</li>
<li>
<a href="{% url "new_statement" %}"
class="{% if request.resolver_match.url_name == "new_statement" %}cur{% endif %}"
accesskey="s">{% translate "Create statement" %}</a>
</li>
<li>
<a href="{% url "new_category" %}"
class="{% if request.resolver_match.url_name == "new_category" %}cur{% endif %}"
accesskey="c">{% translate "Create category" %}</a>
</li>
<li>
<a href="{% url "new_transaction" %}"
class="{% if request.resolver_match.url_name == "new_transaction" %}cur{% endif %}"
accesskey="t">{% translate "Create transaction" %}</a>
</li>
<li> <li>
<a href="{% url "search" %}" <a href="{% url "search" %}"
class="{% if request.resolver_match.url_name == "search" %}cur{% endif %}" class="{% if request.resolver_match.url_name == "search" %}cur{% endif %}"
accesskey="r"> accesskey="r">{% translate "Search" %}</a>
<span>{% translate "Search" %}</span>
{{ "search"|remix }}
</a>
</li> </li>
<li> <li>
{% blocktranslate %}Logged in as <strong>{{ user }}</strong>{% endblocktranslate %} <a href="{% url "logout" %}" accesskey="l">{% translate "Log out" %}</a>
</li>
<li>
<a href="{% url "logout" %}" accesskey="l">
<span>{% translate "Log out" %}</span>
{{ "close-circle"|remix }}
</a>
</li> </li>
{% else %} {% else %}
<li> <li>
<a {% if request.resolver_match.url_name == "login" %}class="cur"{% endif %} <a {% if request.resolver_match.url_name == "login" %}class="cur"{% endif %}
href="{% url "login" %}"> href="{% url "login" %}">{% translate "Log in" %}</a>
<span>{% translate "Log in" %}</span>
{{ "user"|remix }}
</a>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
{% if user.is_authenticated %}
<p>
{% blocktranslate %}Logged in as <strong>{{ user }}</strong>{% endblocktranslate %}
</p>
{% endif %}
</nav> </nav>
{% endspaceless %} {% endspaceless %}
{% endblock %} {% endblock %}

View File

@ -4,8 +4,12 @@
{% load i18n %} {% load i18n %}
{% block link %} {% block link %}
{{ block.super }} {{ block.super }}
{% css "main/css/form.css" %} <link rel="stylesheet"
{% css "main/css/table.css" %} href="{% static 'main/css/form.css' %}"
type="text/css" />
<link rel="stylesheet"
href="{% static 'main/css/table.css' %}"
type="text/css" />
{% endblock %} {% endblock %}
{% block body %} {% block body %}
{% spaceless %} {% spaceless %}

View File

@ -12,9 +12,15 @@
{% endblock %} {% endblock %}
{% block link %} {% block link %}
{{ block.super }} {{ block.super }}
{% css "main/css/form.css" %} <link rel="stylesheet"
{% css "main/css/table.css" %} href="{% static 'main/css/form.css' %}"
{% css "main/css/plot.css" %} type="text/css" />
<link rel="stylesheet"
href="{% static 'main/css/table.css' %}"
type="text/css" />
<link rel="stylesheet"
href="{% static 'main/css/plot.css' %}"
type="text/css" />
{% endblock %} {% endblock %}
{% block body %} {% block body %}
{% with instance=form.instance %} {% with instance=form.instance %}
@ -30,7 +36,7 @@
{% block pre %}{% endblock %} {% block pre %}{% endblock %}
<form method="post" enctype="multipart/form-data"> <form method="post" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
{% if instance|adding %}<input hidden name="next" value="{{ request.path }}">{% endif %} {% if instance|adding %}<input hidden name="next" value="{{ request.path }}" />{% endif %}
{{ form }} {{ form }}
</form> </form>
{% block tables %}{% endblock %} {% block tables %}{% endblock %}

View File

@ -29,11 +29,11 @@
{% if not form.instance|adding %} {% if not form.instance|adding %}
<a class="del" href="{{ form.instance.get_delete_url }}">{% translate "Delete" %}</a> <a class="del" href="{{ form.instance.get_delete_url }}">{% translate "Delete" %}</a>
{% endif %} {% endif %}
<input type="reset" value="{% translate "Reset" %}"> <input type="reset" value="{% translate "Reset" %}" />
{% if form.instance|adding %} {% if form.instance|adding %}
<input type="submit" value="{% translate "Create" %}"> <input type="submit" value="{% translate "Create" %}" />
{% else %} {% else %}
<input type="submit" value="{% translate "Save" %}"> <input type="submit" value="{% translate "Save" %}" />
{% endif %} {% endif %}
{% endblock %} {% endblock %}
</td> </td>

View File

@ -1,7 +1,7 @@
{% extends "main/form/form_base.html" %} {% extends "main/form/form_base.html" %}
{% load i18n %} {% load i18n %}
{% block buttons %} {% block buttons %}
<input hidden value="{{ next }}" name="next"> <input hidden value="{{ next }}" name="next" />
<input type="reset"> <input type="reset" />
<input type="submit" value="{% translate "Log in" %}"> <input type="submit" value="{% translate "Log in" %}" />
{% endblock %} {% endblock %}

View File

@ -4,66 +4,44 @@
{% load i18n %} {% load i18n %}
{% block link %} {% block link %}
{{ block.super }} {{ block.super }}
{% css "main/css/table.css" %} <link rel="stylesheet"
{% css "main/css/plot.css" %} href="{% static 'main/css/table.css' %}"
type="text/css" />
<link rel="stylesheet"
href="{% static 'main/css/plot.css' %}"
type="text/css" />
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<div class="split"> <h2>{% translate "Accounts" %}</h2>
<section class="accounts"> <p>
<h2>{% translate "Accounts" %}</h2> {% for acc in accounts %}
<div class="table"> <a class="big-link" href="{{ acc.get_absolute_url }}">{{ acc.icon|remix }}{{ acc }}</a>
<table> {% empty %}
<thead> {% translate "No account" %}
<tr> {% endfor %}
<th colspan="2">{% translate "Account" %}</th> </p>
<th>{% translate "Balance" %}</th> {% if transactions %}
<th>{% translate "Edit" %}</th> <h2>{% translate "Transactions" %}</h2>
</tr> {% include "transaction/transaction_table.html" %}
</thead> {% endif %}
<tbody> {% if categories %}
{% for acc in accounts %} <h2>{% translate "Categories" %}</h2>
<tr> {% spaceless %}
<td>{{ acc.icon|remix }}</td> <p>
<th class="l"> {% for cat in categories %}
<a href="{{ acc.get_absolute_url }}">{{ acc }}</a> <a class="big-link" href="{{ cat.get_absolute_url }}">{{ cat.icon|remix }}{{ cat }}</a>
</th> {% empty %}
<td class="value">{{ acc.statement_set.first.value|value }}</td> {% translate "No category" %}
<td> {% endfor %}
<a href="{% url "edit_account" acc.pk %}">{% translate "Edit" %}</a> </p>
</td> {% endspaceless %}
</tr> {% endif %}
{% empty %} {% if statements %}
<tr> <h2>{% translate "Statements" %}</h2>
<td class="empty" colspan="4">{% translate "No account" %}</td> {% include "statement/statement_table.html" %}
</tr> {% endif %}
{% endfor %} {% if history.data %}
</tbody> <h2>{% translate "History" %}</h2>
<tfoot> {% include "history/plot.html" %}
<tr class="new">
<td colspan="4">
<a href="{% url "new_account" %}">{% translate "Create account" %}</a>
</td>
</tr>
</tfoot>
</table>
</div>
</section>
<section class="categories">
<h2>{% translate "Categories" %}</h2>
{% spaceless %}
<p>
{% for cat in categories %}
<a class="big-link" href="{{ cat.get_absolute_url }}">{{ cat.icon|remix }}{{ cat }}</a>
{% endfor %}
<a class="big-link add" href="{% url "new_category" %}">{{ "add"|remix }}{% translate "Create category" %}</a>
</p>
{% endspaceless %}
</section>
</div>
{% if history %}
<section>
<h2>{% translate "History" %}</h2>
{% include "history/plot.html" %}
</section>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -13,32 +13,28 @@
{% endblock %} {% endblock %}
{% block link %} {% block link %}
{{ block.super }} {{ block.super }}
{% css "main/css/table.css" %} <link rel="stylesheet"
href="{% static 'main/css/table.css' %}"
type="text/css" />
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<h2> <h2>
{% block h2 %}{% endblock %} {% block h2 %}{% endblock %}
</h2> </h2>
{% if account or category or search %} {% if account %}
<div class="backlinks"> <p>
{% block backlinks %} <a class="big-link" href="{{ account.get_absolute_url }}">{{ account.icon|remix }}{{ account }}</a>
{% if account %} </p>
<p> {% endif %}
<a class="big-link" href="{{ account.get_absolute_url }}">{{ account.icon|remix }}{{ account }}</a> {% if category %}
</p> <p>
{% endif %} <a class="big-link" href="{{ category.get_absolute_url }}">{{ category.icon|remix }}{{ category }}</a>
{% if category %} </p>
<p> {% endif %}
<a class="big-link" href="{{ category.get_absolute_url }}">{{ category.icon|remix }}{{ category }}</a> {% if search %}
</p> <p>
{% endif %} <a href="{% url "search" %}">{% translate "Search" %}</a>
{% if search %} </p>
<p>
<a href="{% url "search" %}">{% translate "Search" %}</a>
</p>
{% endif %}
{% endblock %}
</div>
{% endif %} {% endif %}
{% include "main/pagination.html" %} {% include "main/pagination.html" %}
{% block table %}{% endblock %} {% block table %}{% endblock %}

View File

@ -1,11 +1,14 @@
{% extends "main/base.html" %} {% extends "main/base.html" %}
{% load main_extras %}
{% load static %} {% load static %}
{% load i18n %} {% load i18n %}
{% block link %} {% block link %}
{{ block.super }} {{ block.super }}
{% css "main/css/table.css" %} <link rel="stylesheet"
{% css "main/css/form.css" %} href="{% static 'main/css/table.css' %}"
type="text/css" />
<link rel="stylesheet"
href="{% static 'main/css/form.css' %}"
type="text/css" />
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<h2>{% translate "Log in" %}</h2> <h2>{% translate "Log in" %}</h2>

View File

@ -1,32 +1,9 @@
{% load i18n transaction_extras %} {% load i18n %}
{% if page_obj %} {% if page_obj %}
<p class="pagination"> <p id="pagination">
{% for page in paginator.page_range %} {% for page in paginator.page_range %}
<a href="?page={{ page }}" <a href="?page={{ page }}"
{% if page == page_obj.number %}class="cur"{% endif %}>{{ page }}</a> {% if page == page_obj.number %}class="cur"{% endif %}>{{ page }}</a>
{% endfor %} {% endfor %}
</p> </p>
{% endif %} {% endif %}
{% if month %}
<p class="pagination">{% year_url month %}</p>
<p class="pagination n3">
{% if previous_month %}
{% month_url previous_month fmt="F Y" %}
{% endif %}
{% month_url month cls="cur" fmt="F Y" %}
{% if next_month %}
{% month_url next_month fmt="F Y" %}
{% endif %}
</p>
{% endif %}
{% if year %}
<p class="pagination n3">
{% if previous_year %}
{% year_url previous_year cls="prev" %}
{% endif %}
{% year_url year cls="cur" %}
{% if next_year %}
{% year_url next_year cls="next" %}
{% endif %}
</p>
{% endif %}

View File

@ -1,5 +1,4 @@
from django import template from django import template
from django.templatetags.static import static
from django.utils import formats from django.utils import formats
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
@ -8,16 +7,16 @@ register = template.Library()
@register.filter @register.filter
def value(val, pm=False, r=2): def value(val, pm=False, r=2):
if val is None: if not val:
return "" return ""
_prefix = "" _prefix = ""
_suffix = "&nbsp;€" _suffix = "&nbsp;€"
_val = formats.number_format(val, decimal_pos=r, use_l10n=True, force_grouping=True) _val = formats.number_format(round(val, r), r, use_l10n=True, force_grouping=True)
if val > 0: if val > 0:
if pm: if pm:
_prefix += "&plus;&nbsp;" _prefix += "&plus;&nbsp;"
elif val < 0: else:
_val = _val[1:] _val = _val[1:]
_prefix += "&minus;&nbsp;" _prefix += "&minus;&nbsp;"
@ -39,6 +38,14 @@ def remix(icon, cls=""):
return mark_safe(f"""<span class="ri-{icon}-line {cls}"></span>""") return mark_safe(f"""<span class="ri-{icon}-line {cls}"></span>""")
@register.filter
def check(sum, diff):
if sum == diff:
return remix("check", "green")
else:
return remix("close", "red")
@register.filter @register.filter
def extension(file): def extension(file):
return file.name.split(".")[-1].upper() return file.name.split(".")[-1].upper()
@ -52,10 +59,3 @@ def verbose_name(obj):
@register.filter @register.filter
def adding(obj): def adding(obj):
return obj._state.adding return obj._state.adding
@register.simple_tag
def css(href):
return mark_safe(
f"""<link rel="stylesheet" href="{static(href)}" type="text/css">"""
)

View File

@ -9,7 +9,6 @@ from django.utils.translation import gettext as _
from django.views.generic import ( from django.views.generic import (
CreateView, CreateView,
DeleteView, DeleteView,
DetailView,
ListView, ListView,
TemplateView, TemplateView,
UpdateView, UpdateView,
@ -77,10 +76,6 @@ class NummiUpdateView(UserMixin, UpdateView):
pass pass
class NummiDetailView(UserMixin, DetailView):
pass
class NummiDeleteView(UserMixin, DeleteView): class NummiDeleteView(UserMixin, DeleteView):
template_name = "main/confirm_delete.html" template_name = "main/confirm_delete.html"
success_url = reverse_lazy("index") success_url = reverse_lazy("index")

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-02 15:52+0100\n" "POT-Creation-Date: 2023-04-22 15:16+0200\n"
"PO-Revision-Date: 2023-04-22 15:20+0200\n" "PO-Revision-Date: 2023-04-22 15:20+0200\n"
"Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n" "Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n"
"Language-Team: \n" "Language-Team: \n"
@ -18,7 +18,7 @@ msgstr ""
"X-Generator: Poedit 3.2.2\n" "X-Generator: Poedit 3.2.2\n"
#: .\search\forms.py:7 .\search\templates\search\search.html:6 #: .\search\forms.py:7 .\search\templates\search\search.html:6
#: .\search\templates\search\search.html:14 #: .\search\templates\search\search.html:18
#: .\search\templates\search\search_form.html:5 #: .\search\templates\search\search_form.html:5
msgid "Search" msgid "Search"
msgstr "Rechercher" msgstr "Rechercher"

View File

@ -7,8 +7,12 @@
{% endblock %} {% endblock %}
{% block link %} {% block link %}
{{ block.super }} {{ block.super }}
{% css "main/css/form.css" %} <link rel="stylesheet"
{% css "main/css/table.css" %} href="{% static 'main/css/form.css' %}"
type="text/css" />
<link rel="stylesheet"
href="{% static 'main/css/table.css' %}"
type="text/css" />
{% endblock %} {% endblock %}
{% block body %} {% block body %}
<h2>{% translate "Search" %}</h2> <h2>{% translate "Search" %}</h2>

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-03 15:51+0100\n" "POT-Creation-Date: 2023-11-25 12:05+0100\n"
"PO-Revision-Date: 2023-04-22 15:22+0200\n" "PO-Revision-Date: 2023-04-22 15:22+0200\n"
"Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n" "Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n"
"Language-Team: \n" "Language-Team: \n"
@ -38,7 +38,7 @@ msgid "Start value"
msgstr "Valeur initiale" msgstr "Valeur initiale"
#: .\statement\models.py:29 #: .\statement\models.py:29
#: .\statement\templates\statement\statement_table.html:30 #: .\statement\templates\statement\statement_table.html:27
msgid "Difference" msgid "Difference"
msgstr "Différence" msgstr "Différence"
@ -66,7 +66,7 @@ msgid "Statements"
msgstr "Relevés" msgstr "Relevés"
#: .\statement\templates\statement\statement_form.html:4 #: .\statement\templates\statement\statement_form.html:4
#: .\statement\templates\statement\statement_table.html:18 #: .\statement\templates\statement\statement_table.html:5
msgid "Create statement" msgid "Create statement"
msgstr "Créer un relevé" msgstr "Créer un relevé"
@ -74,32 +74,32 @@ msgstr "Créer un relevé"
msgid "New statement" msgid "New statement"
msgstr "Nouveau relevé" msgstr "Nouveau relevé"
#: .\statement\templates\statement\statement_form.html:23 #: .\statement\templates\statement\statement_form.html:22
msgid "Categories" msgid "Categories"
msgstr "Catégories" msgstr "Catégories"
#: .\statement\templates\statement\statement_form.html:27 #: .\statement\templates\statement\statement_form.html:24
#: .\statement\templates\statement\statement_table.html:31 #: .\statement\templates\statement\statement_table.html:28
msgid "Transactions" msgid "Transactions"
msgstr "Transactions" msgstr "Transactions"
#: .\statement\templates\statement\statement_table.html:25 #: .\statement\templates\statement\statement_table.html:22
msgid "Date" msgid "Date"
msgstr "Date" msgstr "Date"
#: .\statement\templates\statement\statement_table.html:27 #: .\statement\templates\statement\statement_table.html:24
msgid "Account" msgid "Account"
msgstr "Compte" msgstr "Compte"
#: .\statement\templates\statement\statement_table.html:29 #: .\statement\templates\statement\statement_table.html:26
msgid "Value" msgid "Value"
msgstr "Valeur" msgstr "Valeur"
#: .\statement\templates\statement\statement_table.html:62 #: .\statement\templates\statement\statement_table.html:56
msgid "No statement" msgid "No statement"
msgstr "Aucun relevé" msgstr "Aucun relevé"
#: .\statement\templates\statement\statement_table.html:70 #: .\statement\templates\statement\statement_table.html:64
msgid "View all statements" msgid "View all statements"
msgstr "Voir tous les relevés" msgstr "Voir tous les relevés"

View File

@ -1,12 +1,15 @@
{% extends "main/form/base.html" %} {% extends "main/form/base.html" %}
{% load i18n main_extras statement_extras category %} {% load i18n main_extras category %}
{% block title_new %} {% block title_new %}
{% translate "Create statement" %} {% translate "Create statement" %}
{% endblock %} {% endblock %}
{% block h2_new %} {% block h2_new %}
{% translate "New statement" %} {% translate "New statement" %}
{% endblock %} {% endblock %}
{% block h2 %}{{ form.instance }}{% endblock %} {% block h2 %}
{{ form.instance.sum|check:form.instance.diff }}
{{ form.instance }}
{% endblock %}
{% block pre %} {% block pre %}
{% if account %} {% if account %}
<p> <p>
@ -16,13 +19,9 @@
{% endblock %} {% endblock %}
{% block tables %} {% block tables %}
{% if not form.instance|adding %} {% if not form.instance|adding %}
<section> <h3>{% translate "Categories" %}</h3>
<h3>{% translate "Categories" %}</h3> {% category_plot transactions %}
{% category_plot transactions budget=False statement=object %} <h3>{% translate "Transactions" %} ({{ form.instance.sum|pmvalue }} / {{ form.instance.diff|pmvalue }})</h3>
</section> {% include "transaction/transaction_table.html" %}
<section>
<h3>{% translate "Transactions" %}</h3>
{% include "transaction/transaction_table.html" %}
</section>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -7,6 +7,5 @@
{% translate "Statements" %} {% translate "Statements" %}
{% endblock %} {% endblock %}
{% block table %} {% block table %}
{% url "new_statement" as new_statement_url %}
{% include "statement/statement_table.html" %} {% include "statement/statement_table.html" %}
{% endblock %} {% endblock %}

View File

@ -1,4 +1,10 @@
{% load i18n main_extras statement_extras %} {% load main_extras %}
{% load i18n %}
{% if new_statement_url %}
<p>
<a href="{{ new_statement_url }}">{% translate "Create statement" %}</a>
</p>
{% endif %}
<div id="statements" class="table"> <div id="statements" class="table">
<table class="full-width {% if statements_url %}more{% endif %}"> <table class="full-width {% if statements_url %}more{% endif %}">
<colgroup> <colgroup>
@ -11,36 +17,29 @@
<col class="value" span="3"> <col class="value" span="3">
</colgroup> </colgroup>
<thead> <thead>
{% if new_statement_url %} <th>{{ "check"|remix }}</th>
<tr class="new"> <th>{{ "attachment"|remix }}</th>
<td colspan="{% if account %}6{% else %}8{% endif %}"> <th>{% translate "Date" %}</th>
<a href="{{ new_statement_url }}">{% translate "Create statement" %}</a> {% if not account %}
</td> <th colspan="2">{% translate "Account" %}</th>
</tr>
{% endif %} {% endif %}
<tr> <th>{% translate "Value" %}</th>
<th>{{ "check"|remix }}</th> <th>{% translate "Difference" %}</th>
<th>{{ "attachment"|remix }}</th> <th>{% translate "Transactions" %}</th>
<th>{% translate "Date" %}</th>
{% if not account %}
<th colspan="2">{% translate "Account" %}</th>
{% endif %}
<th>{% translate "Value" %}</th>
<th>{% translate "Difference" %}</th>
<th>{% translate "Transactions" %}</th>
</tr>
</thead> </thead>
<tbody> <tbody>
{% for snap in statements %} {% for snap in statements %}
<tr> <tr>
<td class="c">{{ snap.sum|check:snap.diff }}</td> {% if snap.sum == snap.diff %}
<td class="c green">{{ "check"|remix }}</td>
{% else %}
<td class="c red">{{ "close"|remix }}</td>
{% endif %}
<td class="c"> <td class="c">
{% if snap.file %}<a href="{{ snap.file.url }}">{{ "attachment"|remix }}</a>{% endif %} {% if snap.file %}<a href="{{ snap.file.url }}">{{ "attachment"|remix }}</a>{% endif %}
</td> </td>
<th class="date" scope="row"> <th class="date" scope="row">
<a href="{{ snap.get_absolute_url }}"> <a href="{{ snap.get_absolute_url }}">{{ snap.date|date:"Y-m-d" }}</a>
<time datetime="{{ snap.date|date:"Y-m-d" }}">{{ snap.date|date:"Y-m-d" }}</time>
</a>
</th> </th>
{% if not account %} {% if not account %}
<td class="r">{{ snap.account.icon|remix }}</td> <td class="r">{{ snap.account.icon|remix }}</td>
@ -58,14 +57,10 @@
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
{% if statements_url %}
<tfoot>
<tr class="more">
<td colspan="{% if account %}6{% else %}8{% endif %}">
<a href="{{ statements_url }}">{% translate "View all statements" %}</a>
</td>
</tr>
</tfoot>
{% endif %}
</table> </table>
</div> </div>
{% if statements_url %}
<p>
<a href="{{ statements_url }}">{% translate "View all statements" %}</a>
</p>
{% endif %}

View File

@ -1,14 +0,0 @@
from django import template
from main.templatetags.main_extras import remix
register = template.Library()
@register.filter
def check(s, diff):
if s is None:
s = 0
if s == diff:
return remix("check", "green")
else:
return remix("close", "red")

View File

@ -7,7 +7,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: \n" "Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-01-03 15:51+0100\n" "POT-Creation-Date: 2023-11-25 12:05+0100\n"
"PO-Revision-Date: 2023-04-23 08:03+0200\n" "PO-Revision-Date: 2023-04-23 08:03+0200\n"
"Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n" "Last-Translator: Edgar P. Burkhart <traduction@edgarpierre.fr>\n"
"Language-Team: \n" "Language-Team: \n"
@ -22,8 +22,8 @@ msgid "Transaction"
msgstr "Transaction" msgstr "Transaction"
#: .\transaction\models.py:19 .\transaction\models.py:89 #: .\transaction\models.py:19 .\transaction\models.py:89
#: .\transaction\templates\transaction\invoice_table.html:10 #: .\transaction\templates\transaction\invoice_table.html:9
#: .\transaction\templates\transaction\transaction_table.html:32 #: .\transaction\templates\transaction\transaction_table.html:28
msgid "Name" msgid "Name"
msgstr "Nom" msgstr "Nom"
@ -32,11 +32,12 @@ msgid "Description"
msgstr "Description" msgstr "Description"
#: .\transaction\models.py:23 #: .\transaction\models.py:23
#: .\transaction\templates\transaction\transaction_table.html:29
msgid "Value" msgid "Value"
msgstr "Valeur" msgstr "Valeur"
#: .\transaction\models.py:25 #: .\transaction\models.py:25
#: .\transaction\templates\transaction\transaction_table.html:31 #: .\transaction\templates\transaction\transaction_table.html:27
msgid "Date" msgid "Date"
msgstr "Date" msgstr "Date"
@ -45,7 +46,7 @@ msgid "Real date"
msgstr "Date réelle" msgstr "Date réelle"
#: .\transaction\models.py:28 #: .\transaction\models.py:28
#: .\transaction\templates\transaction\transaction_table.html:35 #: .\transaction\templates\transaction\transaction_table.html:30
msgid "Trader" msgid "Trader"
msgstr "Commerçant" msgstr "Commerçant"
@ -54,7 +55,7 @@ msgid "Payment"
msgstr "Paiement" msgstr "Paiement"
#: .\transaction\models.py:38 #: .\transaction\models.py:38
#: .\transaction\templates\transaction\transaction_table.html:37 #: .\transaction\templates\transaction\transaction_table.html:32
msgid "Category" msgid "Category"
msgstr "Catégorie" msgstr "Catégorie"
@ -63,13 +64,12 @@ msgid "Statement"
msgstr "Relevé" msgstr "Relevé"
#: .\transaction\models.py:48 #: .\transaction\models.py:48
#: .\transaction\templates\transaction\transaction_table.html:40 #: .\transaction\templates\transaction\transaction_table.html:35
msgid "Account" msgid "Account"
msgstr "Compte" msgstr "Compte"
#: .\transaction\models.py:83 #: .\transaction\models.py:83
#: .\transaction\templates\transaction\transaction_archive_month.html:27 #: .\transaction\templates\transaction\transaction_archive_month.html:11
#: .\transaction\templates\transaction\transaction_archive_year.html:31
#: .\transaction\templates\transaction\transaction_list.html:4 #: .\transaction\templates\transaction\transaction_list.html:4
#: .\transaction\templates\transaction\transaction_list.html:7 #: .\transaction\templates\transaction\transaction_list.html:7
msgid "Transactions" msgid "Transactions"
@ -80,18 +80,18 @@ msgid "Invoice"
msgstr "Facture" msgstr "Facture"
#: .\transaction\models.py:94 #: .\transaction\models.py:94
#: .\transaction\templates\transaction\invoice_table.html:11 #: .\transaction\templates\transaction\invoice_table.html:10
#: .\transaction\templates\transaction\invoice_table.html:22 #: .\transaction\templates\transaction\invoice_table.html:20
msgid "File" msgid "File"
msgstr "Fichier" msgstr "Fichier"
#: .\transaction\models.py:123 #: .\transaction\models.py:123
#: .\transaction\templates\transaction\transaction_form.html:20 #: .\transaction\templates\transaction\transaction_form.html:19
msgid "Invoices" msgid "Invoices"
msgstr "Factures" msgstr "Factures"
#: .\transaction\templates\transaction\invoice_form.html:4 #: .\transaction\templates\transaction\invoice_form.html:4
#: .\transaction\templates\transaction\invoice_table.html:37 #: .\transaction\templates\transaction\invoice_table.html:35
msgid "Create invoice" msgid "Create invoice"
msgstr "Créer une facture" msgstr "Créer une facture"
@ -99,31 +99,21 @@ msgstr "Créer une facture"
msgid "New invoice" msgid "New invoice"
msgstr "Nouvelle facture" msgstr "Nouvelle facture"
#: .\transaction\templates\transaction\invoice_table.html:12 #: .\transaction\templates\transaction\invoice_table.html:11
#: .\transaction\templates\transaction\invoice_table.html:25 #: .\transaction\templates\transaction\invoice_table.html:23
msgid "Delete" msgid "Delete"
msgstr "Supprimer" msgstr "Supprimer"
#: .\transaction\templates\transaction\invoice_table.html:30 #: .\transaction\templates\transaction\invoice_table.html:28
msgid "No invoice" msgid "No invoice"
msgstr "Aucune facture" msgstr "Aucune facture"
#: .\transaction\templates\transaction\transaction_archive_month.html:14 #: .\transaction\templates\transaction\transaction_archive_month.html:14
#: .\transaction\templates\transaction\transaction_archive_year.html:13
msgid "Back"
msgstr "Retour"
#: .\transaction\templates\transaction\transaction_archive_month.html:22
#: .\transaction\templates\transaction\transaction_archive_year.html:26
msgid "Categories" msgid "Categories"
msgstr "Catégories" msgstr "Catégories"
#: .\transaction\templates\transaction\transaction_archive_year.html:20
msgid "History"
msgstr "Historique"
#: .\transaction\templates\transaction\transaction_form.html:5 #: .\transaction\templates\transaction\transaction_form.html:5
#: .\transaction\templates\transaction\transaction_table.html:25 #: .\transaction\templates\transaction\transaction_table.html:5
msgid "Create transaction" msgid "Create transaction"
msgstr "Créer une transaction" msgstr "Créer une transaction"
@ -131,18 +121,10 @@ msgstr "Créer une transaction"
msgid "New transaction" msgid "New transaction"
msgstr "Nouvelle transaction" msgstr "Nouvelle transaction"
#: .\transaction\templates\transaction\transaction_table.html:33 #: .\transaction\templates\transaction\transaction_table.html:71
msgid "Expenses"
msgstr "Dépenses"
#: .\transaction\templates\transaction\transaction_table.html:34
msgid "Income"
msgstr "Recettes"
#: .\transaction\templates\transaction\transaction_table.html:87
msgid "No transaction" msgid "No transaction"
msgstr "Aucune transaction" msgstr "Aucune transaction"
#: .\transaction\templates\transaction\transaction_table.html:95 #: .\transaction\templates\transaction\transaction_table.html:80
msgid "View all transactions" msgid "View all transactions"
msgstr "Voir toutes les transactions" msgstr "Voir toutes les transactions"

View File

@ -6,11 +6,9 @@
<col class="desc" span="3"> <col class="desc" span="3">
</colgroup> </colgroup>
<thead> <thead>
<tr> <th>{% translate "Name" %}</th>
<th>{% translate "Name" %}</th> <th>{% translate "File" %}</th>
<th>{% translate "File" %}</th> <th>{% translate "Delete" %}</th>
<th>{% translate "Delete" %}</th>
</tr>
</thead> </thead>
<tbody> <tbody>
{% for invoice in transaction.invoices %} {% for invoice in transaction.invoices %}
@ -22,21 +20,21 @@
<a href="{{ invoice.file.url }}">{% translate "File" %} [{{ invoice.file|extension }}]</a> <a href="{{ invoice.file.url }}">{% translate "File" %} [{{ invoice.file|extension }}]</a>
</td> </td>
<td> <td>
<a href="{{ invoice.get_delete_url }}">{% translate "Delete" %}</a> <a href="{{ invoice.get_delete_url }}">{% translate "Delete" %}
</td>
</tr>
{% empty %}
<tr>
<td class="empty" colspan="3">{% translate "No invoice" %}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td colspan="3">
<a href="{% url "new_invoice" transaction.pk %}">{% translate "Create invoice" %}</a>
</td> </td>
</tr> </tr>
{% empty %} </tfoot>
<tr> </table>
<td class="empty" colspan="3">{% translate "No invoice" %}</td> </div>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr class="more">
<td colspan="3">
<a href="{% url "new_invoice" transaction.pk %}">{% translate "Create invoice" %}</a>
</td>
</tr>
</tfoot>
</table>
</div>

View File

@ -1,28 +1,17 @@
{% extends "transaction/transaction_list.html" %} {% extends "transaction/transaction_list.html" %}
{% load i18n main_extras transaction_extras static category %} {% load i18n static category %}
{% block link %} {% block link %}
{{ block.super }} {{ block.super }}
{% css "main/css/plot.css" %} <link rel="stylesheet"
href="{% static 'main/css/plot.css' %}"
type="text/css" />
{% endblock %} {% endblock %}
{% block name %}{{ month|date:"F Y"|capfirst }}{% endblock %}
{% block h2 %}{{ month|date:"F Y"|capfirst }}{% endblock %} {% block h2 %}{{ month|date:"F Y"|capfirst }}{% endblock %}
{% block backlinks %}
{{ block.super }}
{% if account or category %}
<p class="back">
<a href="{% url "transaction_month" month.year month.month %}">{% translate "Back" %}{{ "arrow-go-back"|remix }}</a>
</p>
{% endif %}
{% endblock %}
{% block table %} {% block table %}
<h3>{% translate "Transactions" %}</h3>
{{ block.super }}
{% if not category %} {% if not category %}
<section> <h3>{% translate "Categories" %}</h3>
<h3>{% translate "Categories" %}</h3> {% category_plot transactions month=month %}
{% category_plot transactions month=month %}
</section>
{% endif %} {% endif %}
<section>
<h3>{% translate "Transactions" %}</h3>
{{ block.super }}
</section>
{% endblock %} {% endblock %}

View File

@ -1,34 +0,0 @@
{% extends "transaction/transaction_list.html" %}
{% load i18n main_extras static category %}
{% block link %}
{{ block.super }}
{% css "main/css/plot.css" %}
{% endblock %}
{% block name %}{{ year|date:"Y" }}{% endblock %}
{% block h2 %}{{ year|date:"Y" }}{% endblock %}
{% block backlinks %}
{{ block.super }}
{% if account or category %}
<p class="back">
<a href="{% url "transaction_year" year.year %}">{% translate "Back" %}{{ "arrow-go-back"|remix }}</a>
</p>
{% endif %}
{% endblock %}
{% block table %}
{% if history %}
<section>
<h3>{% translate "History" %}</h3>
{% include "history/plot.html" %}
</section>
{% endif %}
{% if not category %}
<section>
<h3>{% translate "Categories" %}</h3>
{% category_plot transactions year=year %}
</section>
{% endif %}
<section>
<h3>{% translate "Transactions" %}</h3>
{{ block.super }}
</section>
{% endblock %}

View File

@ -8,17 +8,15 @@
{% translate "New transaction" %} {% translate "New transaction" %}
{% endblock %} {% endblock %}
{% block pre %} {% block pre %}
{% if statement %} {% if snapshot %}
<p> <p>
<a href="{{ statement.get_absolute_url }}">{{ statement }}</a> <a href="{{ snapshot.get_absolute_url }}">{{ snapshot }}</a>
</p> </p>
{% endif %} {% endif %}
{% endblock %} {% endblock %}
{% block tables %} {% block tables %}
{% if not form.instance|adding %} {% if not form.instance|adding %}
<section> <h3>{% translate "Invoices" %}</h3>
<h3>{% translate "Invoices" %}</h3> {% include "transaction/invoice_table.html" %}
{% include "transaction/invoice_table.html" %}
</section>
{% endif %} {% endif %}
{% endblock %} {% endblock %}

View File

@ -7,6 +7,5 @@
{% translate "Transactions" %} {% translate "Transactions" %}
{% endblock %} {% endblock %}
{% block table %} {% block table %}
{% url "new_transaction" as new_transaction_url %}
{% include "transaction/transaction_table.html" %} {% include "transaction/transaction_table.html" %}
{% endblock %} {% endblock %}

View File

@ -1,13 +1,17 @@
{% load main_extras transaction_extras %} {% load main_extras %}
{% load i18n %} {% load i18n %}
{% if new_transaction_url %}
<p>
<a href="{{ new_transaction_url }}">{% translate "Create transaction" %}</a>
</p>
{% endif %}
<div id="transactions" class="table"> <div id="transactions" class="table">
<table class="full-width"> <table class="full-width {% if transactions_url %}more{% endif %}">
<colgroup> <colgroup>
<col class="icon"> <col class="icon">
<col class="date"> <col class="date">
<col class="desc"> <col class="desc">
<col class="value"> <col class="value">
<col class="value">
<col class="desc"> <col class="desc">
{% if not category %} {% if not category %}
<col class="icon"> <col class="icon">
@ -19,27 +23,17 @@
{% endif %} {% endif %}
</colgroup> </colgroup>
<thead> <thead>
{% if new_transaction_url %} <th>{{ "attachment"|remix }}</th>
<tr class="new"> <th>{% translate "Date" %}</th>
<td colspan="{% tr_colspan %}"> <th>{% translate "Name" %}</th>
<a href="{{ new_transaction_url }}">{% translate "Create transaction" %}</a> <th>{% translate "Value" %}</th>
</td> <th>{% translate "Trader" %}</th>
</tr> {% if not category %}
<th colspan="2">{% translate "Category" %}</th>
{% endif %}
{% if not account %}
<th colspan="2">{% translate "Account" %}</th>
{% endif %} {% endif %}
<tr>
<th>{{ "attachment"|remix }}</th>
<th>{% translate "Date" %}</th>
<th>{% translate "Name" %}</th>
<th>{% translate "Expenses" %}</th>
<th>{% translate "Income" %}</th>
<th>{% translate "Trader" %}</th>
{% if not category %}
<th colspan="2">{% translate "Category" %}</th>
{% endif %}
{% if not account %}
<th colspan="2">{% translate "Account" %}</th>
{% endif %}
</tr>
</thead> </thead>
<tbody> <tbody>
{% for trans in transactions %} {% for trans in transactions %}
@ -47,23 +41,11 @@
<td class="c"> <td class="c">
{% for invoice in trans.invoices %}<a href="{{ invoice.file.url }}">{{ "attachment"|remix }}</a>{% endfor %} {% for invoice in trans.invoices %}<a href="{{ invoice.file.url }}">{{ "attachment"|remix }}</a>{% endfor %}
</td> </td>
<td class="date"> <td class="date">{{ trans.date|date:"Y-m-d" }}</td>
<time datetime="{{ trans.date|date:"Y-m-d" }}">
{% if month %}
{{ trans.date|date:"l d"|capfirst }}
{% elif year %}
{{ trans.date|date:"d F" }}
{% else %}
{{ trans.date|date:"Y-m-d" }}
{% endif %}
</time>
</td>
<th scope="row" class="l"> <th scope="row" class="l">
<a href="{{ trans.get_absolute_url }}">{{ trans.name }}</a> <a href="{{ trans.get_absolute_url }}">{{ trans.name }}</a>
</th> </th>
{% if trans.value >= 0 %}<td></td>{% endif %}
<td class="value">{{ trans.value|pmvalue }}</td> <td class="value">{{ trans.value|pmvalue }}</td>
{% if trans.value < 0 %}<td></td>{% endif %}
<td>{{ trans.trader|default_if_none:"" }}</td> <td>{{ trans.trader|default_if_none:"" }}</td>
{% if not category %} {% if not category %}
{% if trans.category %} {% if trans.category %}
@ -84,18 +66,17 @@
</tr> </tr>
{% empty %} {% empty %}
<tr> <tr>
<td class="empty" colspan="{% tr_colspan %}">{% translate "No transaction" %}</td> <td class="empty"
colspan="{% if category and account %}5{% elif category or account %}7{% else %}9{% endif %}">
{% translate "No transaction" %}
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
{% if transactions_url %}
<tfoot>
<tr class="more">
<td colspan="{% tr_colspan %}">
<a href="{{ transactions_url }}">{% translate "View all transactions" %}</a>
</td>
</tr>
</tfoot>
{% endif %}
</table> </table>
</div> </div>
{% if transactions_url %}
<p>
<a href="{{ transactions_url }}">{% translate "View all transactions" %}</a>
</p>
{% endif %}

View File

@ -1,46 +0,0 @@
import datetime
from django import template
from django.template.defaultfilters import date
from django.urls import reverse
from django.utils.safestring import mark_safe
from ..utils import ac_url
register = template.Library()
@register.simple_tag(takes_context=True)
def month_url(context, month, cls="", fmt="Y-m"):
url_name, url_params = ac_url(
"transaction_month", {"year": month.year, "month": month.month}, context
)
url = reverse(url_name, kwargs=url_params)
return mark_safe(
f"""<a class="{cls}" href="{url}"><time datetime="{date(month, "Y-m")}">"""
f"""{date(month, fmt).capitalize()}</time></a>"""
)
@register.simple_tag(takes_context=True)
def year_url(context, year, cls=""):
if isinstance(year, datetime.date):
year = year.year
url_name, url_params = ac_url("transaction_year", {"year": year}, context)
url = reverse(url_name, kwargs=url_params)
return mark_safe(
f"""<a class="{cls}" href="{url}">"""
f"""<time datetime="{year}">{year}</time></a>"""
)
@register.simple_tag(takes_context=True)
def tr_colspan(context):
ncol = 10
if context.get("category"):
ncol -= 2
if context.get("account"):
ncol -= 2
return ncol

View File

@ -4,11 +4,6 @@ from . import views
urlpatterns = [ urlpatterns = [
path("list", views.TransactionListView.as_view(), name="transactions"), path("list", views.TransactionListView.as_view(), name="transactions"),
path(
"history/<int:year>",
views.TransactionYearView.as_view(),
name="transaction_year",
),
path( path(
"history/<int:year>/<int:month>", "history/<int:year>/<int:month>",
views.TransactionMonthView.as_view(), views.TransactionMonthView.as_view(),

View File

@ -1,9 +0,0 @@
def ac_url(url_name, url_params, context):
if account := context.get("account"):
url_name = "account_" + url_name
url_params |= {"account": account.pk}
elif category := context.get("category"):
url_name = "category_" + url_name
url_params |= {"category": category.pk}
return url_name, url_params

View File

@ -2,8 +2,7 @@ from account.models import Account
from category.models import Category from category.models import Category
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.views.generic.dates import MonthArchiveView, YearArchiveView from django.views.generic.dates import MonthArchiveView
from history.utils import history
from main.views import ( from main.views import (
NummiCreateView, NummiCreateView,
NummiDeleteView, NummiDeleteView,
@ -110,8 +109,11 @@ class TransactionListView(NummiListView):
context_object_name = "transactions" context_object_name = "transactions"
class TransactionACMixin: class TransactionMonthView(UserMixin, MonthArchiveView):
model = Transaction model = Transaction
date_field = "date"
context_object_name = "transactions"
month_format = "%m"
def get_queryset(self): def get_queryset(self):
if "account" in self.kwargs: if "account" in self.kwargs:
@ -136,25 +138,3 @@ class TransactionACMixin:
if "account" in self.kwargs: if "account" in self.kwargs:
return context_data | {"account": self.account} return context_data | {"account": self.account}
return context_data return context_data
class TransactionMonthView(UserMixin, TransactionACMixin, MonthArchiveView):
model = Transaction
date_field = "date"
context_object_name = "transactions"
month_format = "%m"
class TransactionYearView(UserMixin, TransactionACMixin, YearArchiveView):
model = Transaction
date_field = "date"
context_object_name = "transactions"
make_object_list = True
def get_context_data(self, **kwargs):
context_data = super().get_context_data(**kwargs)
h_data = context_data.get("transactions")
if not (context_data.get("account") or context_data.get("category")):
h_data = h_data.exclude(category__budget=False)
return context_data | {"history": history(h_data)}