From 1be3a23200ec005aa4c3fb1fce94fc1ed652bc78 Mon Sep 17 00:00:00 2001 From: imperosol Date: Fri, 14 Mar 2025 15:31:34 +0100 Subject: [PATCH 1/6] remove accounting views --- accounting/admin.py | 36 - accounting/api.py | 23 - accounting/models.py | 214 +---- accounting/schemas.py | 15 - .../components/ajax-select-index.ts | 60 -- .../accounting/accountingtype_list.jinja | 27 - .../accounting/bank_account_details.jinja | 38 - .../accounting/bank_account_list.jinja | 33 - .../accounting/club_account_details.jinja | 68 -- accounting/templates/accounting/co_list.jinja | 30 - .../accounting/journal_details.jinja | 103 --- .../journal_statement_accounting.jinja | 33 - .../accounting/journal_statement_nature.jinja | 57 -- .../accounting/journal_statement_person.jinja | 68 -- .../templates/accounting/label_list.jinja | 36 - .../templates/accounting/operation_edit.jinja | 123 --- .../simplifiedaccountingtype_list.jinja | 27 - accounting/tests.py | 233 ----- accounting/urls.py | 152 +--- accounting/views.py | 813 +----------------- accounting/widgets/ajax_select.py | 42 - club/templates/club/club_tools.jinja | 8 - core/templates/core/user_tools.jinja | 20 - 23 files changed, 5 insertions(+), 2254 deletions(-) delete mode 100644 accounting/admin.py delete mode 100644 accounting/api.py delete mode 100644 accounting/schemas.py delete mode 100644 accounting/static/bundled/accounting/components/ajax-select-index.ts delete mode 100644 accounting/templates/accounting/accountingtype_list.jinja delete mode 100644 accounting/templates/accounting/bank_account_details.jinja delete mode 100644 accounting/templates/accounting/bank_account_list.jinja delete mode 100644 accounting/templates/accounting/club_account_details.jinja delete mode 100644 accounting/templates/accounting/co_list.jinja delete mode 100644 accounting/templates/accounting/journal_details.jinja delete mode 100644 accounting/templates/accounting/journal_statement_accounting.jinja delete mode 100644 accounting/templates/accounting/journal_statement_nature.jinja delete mode 100644 accounting/templates/accounting/journal_statement_person.jinja delete mode 100644 accounting/templates/accounting/label_list.jinja delete mode 100644 accounting/templates/accounting/operation_edit.jinja delete mode 100644 accounting/templates/accounting/simplifiedaccountingtype_list.jinja delete mode 100644 accounting/widgets/ajax_select.py diff --git a/accounting/admin.py b/accounting/admin.py deleted file mode 100644 index c3386eb8..00000000 --- a/accounting/admin.py +++ /dev/null @@ -1,36 +0,0 @@ -# -# Copyright 2023 © AE UTBM -# ae@utbm.fr / ae.info@utbm.fr -# -# This file is part of the website of the UTBM Student Association (AE UTBM), -# https://ae.utbm.fr. -# -# You can find the source code of the website at https://github.com/ae-utbm/sith -# -# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3) -# SEE : https://raw.githubusercontent.com/ae-utbm/sith/master/LICENSE -# OR WITHIN THE LOCAL FILE "LICENSE" -# -# - -from django.contrib import admin - -from accounting.models import ( - AccountingType, - BankAccount, - ClubAccount, - Company, - GeneralJournal, - Label, - Operation, - SimplifiedAccountingType, -) - -admin.site.register(BankAccount) -admin.site.register(ClubAccount) -admin.site.register(GeneralJournal) -admin.site.register(AccountingType) -admin.site.register(SimplifiedAccountingType) -admin.site.register(Operation) -admin.site.register(Label) -admin.site.register(Company) diff --git a/accounting/api.py b/accounting/api.py deleted file mode 100644 index 5ba6c12d..00000000 --- a/accounting/api.py +++ /dev/null @@ -1,23 +0,0 @@ -from typing import Annotated - -from annotated_types import MinLen -from ninja_extra import ControllerBase, api_controller, paginate, route -from ninja_extra.pagination import PageNumberPaginationExtra -from ninja_extra.schemas import PaginatedResponseSchema - -from accounting.models import ClubAccount, Company -from accounting.schemas import ClubAccountSchema, CompanySchema -from core.auth.api_permissions import CanAccessLookup - - -@api_controller("/lookup", permissions=[CanAccessLookup]) -class AccountingController(ControllerBase): - @route.get("/club-account", response=PaginatedResponseSchema[ClubAccountSchema]) - @paginate(PageNumberPaginationExtra, page_size=50) - def search_club_account(self, search: Annotated[str, MinLen(1)]): - return ClubAccount.objects.filter(name__icontains=search).values() - - @route.get("/company", response=PaginatedResponseSchema[CompanySchema]) - @paginate(PageNumberPaginationExtra, page_size=50) - def search_company(self, search: Annotated[str, MinLen(1)]): - return Company.objects.filter(name__icontains=search).values() diff --git a/accounting/models.py b/accounting/models.py index 6fb1a6c8..ba1024cf 100644 --- a/accounting/models.py +++ b/accounting/models.py @@ -17,15 +17,12 @@ from decimal import Decimal from django.conf import settings from django.core import validators -from django.core.exceptions import ValidationError from django.db import models -from django.template import defaultfilters -from django.urls import reverse from django.utils.translation import gettext_lazy as _ from phonenumber_field.modelfields import PhoneNumberField from club.models import Club -from core.models import SithFile, User +from core.models import SithFile class CurrencyField(models.DecimalField): @@ -74,28 +71,6 @@ class Company(models.Model): def __str__(self): return self.name - def get_absolute_url(self): - return reverse("accounting:co_edit", kwargs={"co_id": self.id}) - - def get_display_name(self): - return self.name - - def is_owned_by(self, user): - """Check if that object can be edited by the given user.""" - return user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) - - def can_be_edited_by(self, user): - """Check if that object can be edited by the given user.""" - return user.memberships.filter( - end_date=None, club__role=settings.SITH_CLUB_ROLES_ID["Treasurer"] - ).exists() - - def can_be_viewed_by(self, user): - """Check if that object can be viewed by the given user.""" - return user.memberships.filter( - end_date=None, club__role_gte=settings.SITH_CLUB_ROLES_ID["Treasurer"] - ).exists() - class BankAccount(models.Model): name = models.CharField(_("name"), max_length=30) @@ -115,18 +90,6 @@ class BankAccount(models.Model): def __str__(self): return self.name - def get_absolute_url(self): - return reverse("accounting:bank_details", kwargs={"b_account_id": self.id}) - - def is_owned_by(self, user): - """Check if that object can be edited by the given user.""" - if user.is_anonymous: - return False - if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID): - return True - m = self.club.get_membership_for(user) - return m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"] - class ClubAccount(models.Model): name = models.CharField(_("name"), max_length=30) @@ -150,37 +113,6 @@ class ClubAccount(models.Model): def __str__(self): return self.name - def get_absolute_url(self): - return reverse("accounting:club_details", kwargs={"c_account_id": self.id}) - - def is_owned_by(self, user): - """Check if that object can be edited by the given user.""" - if user.is_anonymous: - return False - return user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) - - def can_be_edited_by(self, user): - """Check if that object can be edited by the given user.""" - m = self.club.get_membership_for(user) - return m and m.role == settings.SITH_CLUB_ROLES_ID["Treasurer"] - - def can_be_viewed_by(self, user): - """Check if that object can be viewed by the given user.""" - m = self.club.get_membership_for(user) - return m and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"] - - def has_open_journal(self): - return self.journals.filter(closed=False).exists() - - def get_open_journal(self): - return self.journals.filter(closed=False).first() - - def get_display_name(self): - return _("%(club_account)s on %(bank_account)s") % { - "club_account": self.name, - "bank_account": self.bank_account, - } - class GeneralJournal(models.Model): """Class storing all the operations for a period of time.""" @@ -206,40 +138,6 @@ class GeneralJournal(models.Model): def __str__(self): return self.name - def get_absolute_url(self): - return reverse("accounting:journal_details", kwargs={"j_id": self.id}) - - def is_owned_by(self, user): - """Check if that object can be edited by the given user.""" - if user.is_anonymous: - return False - if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID): - return True - return self.club_account.can_be_edited_by(user) - - def can_be_edited_by(self, user): - """Check if that object can be edited by the given user.""" - if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID): - return True - return self.club_account.can_be_edited_by(user) - - def can_be_viewed_by(self, user): - return self.club_account.can_be_viewed_by(user) - - def update_amounts(self): - self.amount = 0 - self.effective_amount = 0 - for o in self.operations.all(): - if o.accounting_type.movement_type == "CREDIT": - if o.done: - self.effective_amount += o.amount - self.amount += o.amount - else: - if o.done: - self.effective_amount -= o.amount - self.amount -= o.amount - self.save() - class Operation(models.Model): """An operation is a line in the journal, a debit or a credit.""" @@ -328,88 +226,6 @@ class Operation(models.Model): def __str__(self): return f"{self.amount} € | {self.date} | {self.accounting_type} | {self.done}" - def __getattribute__(self, attr): - if attr == "target": - return self.get_target() - else: - return object.__getattribute__(self, attr) - - def save(self, *args, **kwargs): - if self.number is None: - self.number = self.journal.operations.count() + 1 - super().save(*args, **kwargs) - self.journal.update_amounts() - - def get_absolute_url(self): - return reverse("accounting:journal_details", kwargs={"j_id": self.journal.id}) - - def clean(self): - super().clean() - if self.date is None: - raise ValidationError(_("The date must be set.")) - elif self.date < self.journal.start_date: - raise ValidationError( - _( - """The date can not be before the start date of the journal, which is -%(start_date)s.""" - ) - % { - "start_date": defaultfilters.date( - self.journal.start_date, settings.DATE_FORMAT - ) - } - ) - if self.target_type != "OTHER" and self.get_target() is None: - raise ValidationError(_("Target does not exists")) - if self.target_type == "OTHER" and self.target_label == "": - raise ValidationError( - _("Please add a target label if you set no existing target") - ) - if not self.accounting_type and not self.simpleaccounting_type: - raise ValidationError( - _( - "You need to provide ether a simplified accounting type or a standard accounting type" - ) - ) - if self.simpleaccounting_type: - self.accounting_type = self.simpleaccounting_type.accounting_type - - @property - def target(self): - return self.get_target() - - def get_target(self): - tar = None - if self.target_type == "USER": - tar = User.objects.filter(id=self.target_id).first() - elif self.target_type == "CLUB": - tar = Club.objects.filter(id=self.target_id).first() - elif self.target_type == "ACCOUNT": - tar = ClubAccount.objects.filter(id=self.target_id).first() - elif self.target_type == "COMPANY": - tar = Company.objects.filter(id=self.target_id).first() - return tar - - def is_owned_by(self, user): - """Check if that object can be edited by the given user.""" - if user.is_anonymous: - return False - if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID): - return True - if self.journal.closed: - return False - m = self.journal.club_account.club.get_membership_for(user) - return m is not None and m.role >= settings.SITH_CLUB_ROLES_ID["Treasurer"] - - def can_be_edited_by(self, user): - """Check if that object can be edited by the given user.""" - if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID): - return True - if self.journal.closed: - return False - m = self.journal.club_account.club.get_membership_for(user) - return m is not None and m.role == settings.SITH_CLUB_ROLES_ID["Treasurer"] - class AccountingType(models.Model): """Accounting types. @@ -444,15 +260,6 @@ class AccountingType(models.Model): def __str__(self): return self.code + " - " + self.get_movement_type_display() + " - " + self.label - def get_absolute_url(self): - return reverse("accounting:type_list") - - def is_owned_by(self, user): - """Check if that object can be edited by the given user.""" - if user.is_anonymous: - return False - return user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) - class SimplifiedAccountingType(models.Model): """Simplified version of `AccountingType`.""" @@ -475,9 +282,6 @@ class SimplifiedAccountingType(models.Model): f"- {self.accounting_type.code} - {self.label}" ) - def get_absolute_url(self): - return reverse("accounting:simple_type_list") - @property def movement_type(self): return self.accounting_type.movement_type @@ -502,19 +306,3 @@ class Label(models.Model): def __str__(self): return "%s (%s)" % (self.name, self.club_account.name) - - def get_absolute_url(self): - return reverse( - "accounting:label_list", kwargs={"clubaccount_id": self.club_account.id} - ) - - def is_owned_by(self, user): - if user.is_anonymous: - return False - return self.club_account.is_owned_by(user) - - def can_be_edited_by(self, user): - return self.club_account.can_be_edited_by(user) - - def can_be_viewed_by(self, user): - return self.club_account.can_be_viewed_by(user) diff --git a/accounting/schemas.py b/accounting/schemas.py deleted file mode 100644 index 3d9edbcc..00000000 --- a/accounting/schemas.py +++ /dev/null @@ -1,15 +0,0 @@ -from ninja import ModelSchema - -from accounting.models import ClubAccount, Company - - -class ClubAccountSchema(ModelSchema): - class Meta: - model = ClubAccount - fields = ["id", "name"] - - -class CompanySchema(ModelSchema): - class Meta: - model = Company - fields = ["id", "name"] diff --git a/accounting/static/bundled/accounting/components/ajax-select-index.ts b/accounting/static/bundled/accounting/components/ajax-select-index.ts deleted file mode 100644 index 3fc93cf3..00000000 --- a/accounting/static/bundled/accounting/components/ajax-select-index.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { AjaxSelect } from "#core:core/components/ajax-select-base"; -import { registerComponent } from "#core:utils/web-components"; -import type { TomOption } from "tom-select/dist/types/types"; -import type { escape_html } from "tom-select/dist/types/utils"; -import { - type ClubAccountSchema, - type CompanySchema, - accountingSearchClubAccount, - accountingSearchCompany, -} from "#openapi"; - -@registerComponent("club-account-ajax-select") -export class ClubAccountAjaxSelect extends AjaxSelect { - protected valueField = "id"; - protected labelField = "name"; - protected searchField = ["code", "name"]; - - protected async search(query: string): Promise { - const resp = await accountingSearchClubAccount({ query: { search: query } }); - if (resp.data) { - return resp.data.results; - } - return []; - } - - protected renderOption(item: ClubAccountSchema, sanitize: typeof escape_html) { - return `
- ${sanitize(item.name)} -
`; - } - - protected renderItem(item: ClubAccountSchema, sanitize: typeof escape_html) { - return `${sanitize(item.name)}`; - } -} - -@registerComponent("company-ajax-select") -export class CompanyAjaxSelect extends AjaxSelect { - protected valueField = "id"; - protected labelField = "name"; - protected searchField = ["code", "name"]; - - protected async search(query: string): Promise { - const resp = await accountingSearchCompany({ query: { search: query } }); - if (resp.data) { - return resp.data.results; - } - return []; - } - - protected renderOption(item: CompanySchema, sanitize: typeof escape_html) { - return `
- ${sanitize(item.name)} -
`; - } - - protected renderItem(item: CompanySchema, sanitize: typeof escape_html) { - return `${sanitize(item.name)}`; - } -} diff --git a/accounting/templates/accounting/accountingtype_list.jinja b/accounting/templates/accounting/accountingtype_list.jinja deleted file mode 100644 index 7ae54014..00000000 --- a/accounting/templates/accounting/accountingtype_list.jinja +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}Accounting type list{% endtrans %} -{% endblock %} - -{% block content %} -
-

- {% trans %}Accounting{% endtrans %} > - {% trans %}Accounting types{% endtrans %} -

-
-

{% trans %}New accounting type{% endtrans %}

- {% if accountingtype_list %} -

{% trans %}Accounting type list{% endtrans %}

-
    - {% for a in accountingtype_list %} -
  • {{ a }}
  • - {% endfor %} -
- {% else %} - {% trans %}There is no types in this website.{% endtrans %} - {% endif %} -
-{% endblock %} - diff --git a/accounting/templates/accounting/bank_account_details.jinja b/accounting/templates/accounting/bank_account_details.jinja deleted file mode 100644 index f1b1e056..00000000 --- a/accounting/templates/accounting/bank_account_details.jinja +++ /dev/null @@ -1,38 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}Bank account: {% endtrans %}{{ object.name }} -{% endblock %} - -{% block content %} -
-

- {% trans %}Accounting{% endtrans %} > - {{ object.name }} -

-
-

{% trans %}Bank account: {% endtrans %}{{ object.name }}

- {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and not object.club_accounts.exists() %} - {% trans %}Delete{% endtrans %} - {% endif %} -

{% trans %}Infos{% endtrans %}

-
    -
  • {% trans %}IBAN: {% endtrans %}{{ object.iban }}
  • -
  • {% trans %}Number: {% endtrans %}{{ object.number }}
  • -
-

{% trans %}New club account{% endtrans %}

- -
-{% endblock %} - - - diff --git a/accounting/templates/accounting/bank_account_list.jinja b/accounting/templates/accounting/bank_account_list.jinja deleted file mode 100644 index bb47cfca..00000000 --- a/accounting/templates/accounting/bank_account_list.jinja +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}Bank account list{% endtrans %} -{% endblock %} - -{% block content %} -
-

- {% trans %}Accounting{% endtrans %} -

- {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %} -

{% trans %}Manage simplified types{% endtrans %}

-

{% trans %}Manage accounting types{% endtrans %}

-

{% trans %}New bank account{% endtrans %}

- {% endif %} - {% if bankaccount_list %} -

{% trans %}Bank account list{% endtrans %}

- - {% else %} - {% trans %}There is no accounts in this website.{% endtrans %} - {% endif %} -
-{% endblock %} - - - diff --git a/accounting/templates/accounting/club_account_details.jinja b/accounting/templates/accounting/club_account_details.jinja deleted file mode 100644 index b6df130e..00000000 --- a/accounting/templates/accounting/club_account_details.jinja +++ /dev/null @@ -1,68 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}Club account:{% endtrans %} {{ object.name }} -{% endblock %} - -{% block content %} -
-

- {% trans %}Accounting{% endtrans %} > - {{object.bank_account }} > - {{ object }} -

-
-

{% trans %}Club account:{% endtrans %} {{ object.name }}

- {% if user.is_root and not object.journals.exists() %} - {% trans %}Delete{% endtrans %} - {% endif %} - {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %} -

{% trans %}New label{% endtrans %}

- {% endif %} -

{% trans %}Label list{% endtrans %}

- {% if not object.has_open_journal() %} -

{% trans %}New journal{% endtrans %}

- {% else %} -

{% trans %}You can not create new journal while you still have one opened{% endtrans %}

- {% endif %} - - - - - - - - - - - - - - {% for j in object.journals.all() %} - - - - {% if j.end_date %} - - {% else %} - - {% endif %} - - - {% if j.closed %} - - {% else %} - - {% endif %} - - - {% endfor %} - -
{% trans %}Name{% endtrans %}{% trans %}Start{% endtrans %}{% trans %}End{% endtrans %}{% trans %}Amount{% endtrans %}{% trans %}Effective amount{% endtrans %}{% trans %}Closed{% endtrans %}{% trans %}Actions{% endtrans %}
{{ j.name }}{{ j.start_date }}{{ j.end_date }} - {{ j.amount }} €{{ j.effective_amount }} €{% trans %}Yes{% endtrans %}{% trans %}No{% endtrans %} {% trans %}View{% endtrans %} - {% trans %}Edit{% endtrans %} - {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and j.operations.count() == 0 %} - {% trans %}Delete{% endtrans %} - {% endif %} -
-
-{% endblock %} diff --git a/accounting/templates/accounting/co_list.jinja b/accounting/templates/accounting/co_list.jinja deleted file mode 100644 index 1d357820..00000000 --- a/accounting/templates/accounting/co_list.jinja +++ /dev/null @@ -1,30 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}Company list{% endtrans %} -{% endblock %} - -{% block content %} -
- {% if user.is_root - or user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) - %} -

{% trans %}Create new company{% endtrans %}

-{% endif %} -
- - - - - - - - {% for o in object_list %} - - - - {% endfor %} - -
{% trans %}Companies{% endtrans %}
{{ o.get_display_name() }}
-
-{% endblock %} diff --git a/accounting/templates/accounting/journal_details.jinja b/accounting/templates/accounting/journal_details.jinja deleted file mode 100644 index 8393ca8b..00000000 --- a/accounting/templates/accounting/journal_details.jinja +++ /dev/null @@ -1,103 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}General journal:{% endtrans %} {{ object.name }} -{% endblock %} - -{% block content %} -
-

- {% trans %}Accounting{% endtrans %} > - {{object.club_account.bank_account }} > - {{ object.club_account }} > - {{ object.name }} -

-
-

{% trans %}General journal:{% endtrans %} {{ object.name }}

-

{% trans %}New label{% endtrans %}

-

{% trans %}Label list{% endtrans %}

-

{% trans %}Company list{% endtrans %}

-

{% trans %}Amount: {% endtrans %}{{ object.amount }} € - - {% trans %}Effective amount: {% endtrans %}{{ object.effective_amount }} €

- {% if object.closed %} -

{% trans %}Journal is closed, you can not create operation{% endtrans %}

- {% else %} -

{% trans %}New operation{% endtrans %}

-
- {% endif %} -
- - - - - - - - - - - - - - - - - - - - {% for o in object.operations.all() %} - - - - - {% if o.accounting_type.movement_type == "DEBIT" %} - - {% else %} - - {% endif %} - - {% if o.target_type == "OTHER" %} - - {% else %} - - {% endif %} - - - {% if o.done %} - - {% else %} - - {% endif %} - - {% if o.invoice %} - - {% else %} - - {% endif %} - - - -{% endfor %} - -
{% trans %}Nb{% endtrans %}{% trans %}Date{% endtrans %}{% trans %}Label{% endtrans %}{% trans %}Amount{% endtrans %}{% trans %}Payment mode{% endtrans %}{% trans %}Target{% endtrans %}{% trans %}Code{% endtrans %}{% trans %}Nature{% endtrans %}{% trans %}Done{% endtrans %}{% trans %}Comment{% endtrans %}{% trans %}File{% endtrans %}{% trans %}Actions{% endtrans %}{% trans %}PDF{% endtrans %}
{{ o.number }}{{ o.date }}{{ o.label or "" }} {{ o.amount }} € {{ o.amount }} €{{ o.get_mode_display() }}{{ o.target_label }}{{ o.target.get_display_name() }}{{ o.accounting_type.code }}{{ o.accounting_type.label }}{% trans %}Yes{% endtrans %}{% trans %}No{% endtrans %}{{ o.remark }} - {% if not o.linked_operation and o.target_type == "ACCOUNT" and not o.target.has_open_journal() %} -

- {% trans %}Warning: this operation has no linked operation because the targeted club account has no opened journal.{% endtrans %} -

-

- {% trans url=o.target.get_absolute_url() %}Open a journal in this club account, then save this operation again to make the linked operation.{% endtrans %} -

- {% endif %} -
{{ o.invoice.name }}- - {% - if o.journal.club_account.bank_account.name not in ["AE TI", "TI"] - or user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) - %} - {% if not o.journal.closed %} - {% trans %}Edit{% endtrans %} - {% endif %} - {% endif %} - {% trans %}Generate{% endtrans %}
-
-
-{% endblock %} diff --git a/accounting/templates/accounting/journal_statement_accounting.jinja b/accounting/templates/accounting/journal_statement_accounting.jinja deleted file mode 100644 index 5641e78b..00000000 --- a/accounting/templates/accounting/journal_statement_accounting.jinja +++ /dev/null @@ -1,33 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}General journal:{% endtrans %} {{ object.name }} -{% endblock %} - - -{% block content %} -
-

{% trans %}Accounting statement: {% endtrans %} {{ object.name }}

- - - - - - - - - - {% for k,v in statement.items() %} - - - - - {% endfor %} - - -
{% trans %}Operation type{% endtrans %}{% trans %}Sum{% endtrans %}
{{ k }}{{ "%.2f" % v }}
- -

{% trans %}Amount: {% endtrans %}{{ "%.2f" % object.amount }} €

-

{% trans %}Effective amount: {% endtrans %}{{ "%.2f" %object.effective_amount }} €

-
-{% endblock %} diff --git a/accounting/templates/accounting/journal_statement_nature.jinja b/accounting/templates/accounting/journal_statement_nature.jinja deleted file mode 100644 index 0a149326..00000000 --- a/accounting/templates/accounting/journal_statement_nature.jinja +++ /dev/null @@ -1,57 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}General journal:{% endtrans %} {{ object.name }} -{% endblock %} - -{% macro display_tables(dict) %} -
-
{% trans %}Credit{% endtrans %}
- - - - - - - - - {% for k,v in dict['CREDIT'].items() %} - - - - - {% endfor %} - -
{% trans %}Nature of operation{% endtrans %}{% trans %}Sum{% endtrans %}
{{ k }}{{ "%.2f" % v }}
- {% trans %}Total: {% endtrans %}{{ "%.2f" % dict['CREDIT_sum'] }} - -
{% trans %}Debit{% endtrans %}
- - - - - - - - - {% for k,v in dict['DEBIT'].items() %} - - - - - {% endfor %} - -
{% trans %}Nature of operation{% endtrans %}{% trans %}Sum{% endtrans %}
{{ k }}{{ "%.2f" % v }}
- {% trans %}Total: {% endtrans %}{{ "%.2f" % dict['DEBIT_sum'] }} -{% endmacro %} - -{% block content %} -

{% trans %}Statement by nature: {% endtrans %} {{ object.name }}

- - {% for k,v in statement.items() %} -

{{ k }} : {{ "%.2f" % (v['CREDIT_sum'] - v['DEBIT_sum']) }}

- {{ display_tables(v) }} -
- {% endfor %} -
-{% endblock %} diff --git a/accounting/templates/accounting/journal_statement_person.jinja b/accounting/templates/accounting/journal_statement_person.jinja deleted file mode 100644 index 76482647..00000000 --- a/accounting/templates/accounting/journal_statement_person.jinja +++ /dev/null @@ -1,68 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}General journal:{% endtrans %} {{ object.name }} -{% endblock %} - - -{% block content %} -
-

{% trans %}Statement by person: {% endtrans %} {{ object.name }}

- -

{% trans %}Credit{% endtrans %}

- - - - - - - - - - {% for key in credit_statement.keys() %} - - {% if key.target_type == "OTHER" %} - - {% elif key %} - - {% else %} - - {% endif %} - - - {% endfor %} - - -
{% trans %}Target of the operation{% endtrans %}{% trans %}Sum{% endtrans %}
{{ o.target_label }}{{ key.get_display_name() }}{{ "%.2f" % credit_statement[key] }}
- -

Total : {{ "%.2f" % total_credit }}

- -

{% trans %}Debit{% endtrans %}

- - - - - - - - - - {% for key in debit_statement.keys() %} - - {% if key.target_type == "OTHER" %} - - {% elif key %} - - {% else %} - - {% endif %} - - - {% endfor %} - - -
{% trans %}Target of the operation{% endtrans %}{% trans %}Sum{% endtrans %}
{{ o.target_label }}{{ key.get_display_name() }}{{ "%.2f" % debit_statement[key] }}
- -

Total : {{ "%.2f" % total_debit }}

-
-{% endblock %} diff --git a/accounting/templates/accounting/label_list.jinja b/accounting/templates/accounting/label_list.jinja deleted file mode 100644 index 6b5a9b8b..00000000 --- a/accounting/templates/accounting/label_list.jinja +++ /dev/null @@ -1,36 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}Label list{% endtrans %} -{% endblock %} - -{% block content %} -
-

- {% trans %}Accounting{% endtrans %} > - {{object.bank_account }} > - {{ object }} -

-
-

{% trans %}Back to club account{% endtrans %}

- {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %} -

{% trans %}New label{% endtrans %}

- {% endif %} - {% if object.labels.all() %} -

{% trans %}Label list{% endtrans %}

-
    - {% for l in object.labels.all() %} -
  • {{ l }} - {% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %} - - - {% trans %}Delete{% endtrans %} - {% endif %} -
  • - {% endfor %} -
- {% else %} - {% trans %}There is no label in this club account.{% endtrans %} - {% endif %} -
-{% endblock %} - diff --git a/accounting/templates/accounting/operation_edit.jinja b/accounting/templates/accounting/operation_edit.jinja deleted file mode 100644 index 4a75cb83..00000000 --- a/accounting/templates/accounting/operation_edit.jinja +++ /dev/null @@ -1,123 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}Edit operation{% endtrans %} -{% endblock %} - -{% block content %} -
-

- {% trans %}Accounting{% endtrans %} > - {{object.club_account.bank_account }} > - {{ object.club_account }} > - {{ object.name }} > - {% trans %}Edit operation{% endtrans %} -

-
-

{% trans %}Edit operation{% endtrans %}

-
- {% csrf_token %} - {{ form.non_field_errors() }} - {{ form.journal }} - {{ form.target_id }} -

{{ form.amount.errors }} {{ form.amount }}

-

{{ form.remark.errors }} {{ form.remark }}

-
- {% trans %}Warning: if you select Account, the opposite operation will be created in the target account. If you don't want that, select Club instead of Account.{% endtrans %} -

{{ form.target_type.errors }} {{ form.target_type }}

- {{ form.user }} - {{ form.club }} - {{ form.club_account }} - {{ form.company }} - {{ form.target_label }} - {{ form.need_link }} -

{{ form.date.errors }} {{ form.date }}

-

{{ form.mode.errors }} {{ form.mode }}

-

{{ form.cheque_number.errors }} {{ - form.cheque_number }}

-

{{ form.invoice.errors }} {{ form.invoice }}

-

{{ form.simpleaccounting_type.errors }} {{ form.simpleaccounting_type }}

-

{{ form.accounting_type.errors }} {{ - form.accounting_type }}

-

{{ form.label.errors }} {{ form.label }}

-

{{ form.done.errors }} {{ form.done }}

- {% if form.instance.linked_operation %} - {% set obj = form.instance.linked_operation %} -

{% trans %}Linked operation:{% endtrans %}
- - {{obj.journal.club_account.bank_account }} > - {{ obj.journal.club_account }} > - {{ obj.journal }} > - n°{{ obj.number }} -

- {% endif %} -

-
-{% endblock %} - -{% block script %} - {{ super() }} - -
-{% endblock %} - - diff --git a/accounting/templates/accounting/simplifiedaccountingtype_list.jinja b/accounting/templates/accounting/simplifiedaccountingtype_list.jinja deleted file mode 100644 index da72a370..00000000 --- a/accounting/templates/accounting/simplifiedaccountingtype_list.jinja +++ /dev/null @@ -1,27 +0,0 @@ -{% extends "core/base.jinja" %} - -{% block title %} - {% trans %}Simplified type list{% endtrans %} -{% endblock %} - -{% block content %} -
-

- {% trans %}Accounting{% endtrans %} > - {% trans %}Simplified types{% endtrans %} -

-
-

{% trans %}New simplified type{% endtrans %}

- {% if simplifiedaccountingtype_list %} -

{% trans %}Simplified type list{% endtrans %}

-
    - {% for a in simplifiedaccountingtype_list %} -
  • {{ a }}
  • - {% endfor %} -
- {% else %} - {% trans %}There is no types in this website.{% endtrans %} - {% endif %} -
-{% endblock %} - diff --git a/accounting/tests.py b/accounting/tests.py index 30d1586b..34cf7cfb 100644 --- a/accounting/tests.py +++ b/accounting/tests.py @@ -13,18 +13,10 @@ # # -from datetime import date, timedelta from django.test import TestCase from django.urls import reverse -from accounting.models import ( - AccountingType, - GeneralJournal, - Label, - Operation, - SimplifiedAccountingType, -) from core.models import User @@ -65,228 +57,3 @@ class TestRefoundAccount(TestCase): assert response.status_code == 200 assert '
' in str(response.content) assert self.skia.customer.amount == 0 - - -class TestJournal(TestCase): - @classmethod - def setUpTestData(cls): - cls.journal = GeneralJournal.objects.get(id=1) - - def test_permission_granted(self): - self.client.force_login(User.objects.get(username="comptable")) - response_get = self.client.get( - reverse("accounting:journal_details", args=[self.journal.id]) - ) - - assert response_get.status_code == 200 - assert "M\\xc3\\xa9thode de paiement" in str(response_get.content) - - def test_permission_not_granted(self): - self.client.force_login(User.objects.get(username="skia")) - response_get = self.client.get( - reverse("accounting:journal_details", args=[self.journal.id]) - ) - - assert response_get.status_code == 403 - assert "M\xc3\xa9thode de paiement" not in str(response_get.content) - - -class TestOperation(TestCase): - def setUp(self): - self.tomorrow_formatted = (date.today() + timedelta(days=1)).strftime( - "%d/%m/%Y" - ) - self.journal = GeneralJournal.objects.filter(id=1).first() - self.skia = User.objects.filter(username="skia").first() - at = AccountingType( - code="443", label="Ce code n'existe pas", movement_type="CREDIT" - ) - at.save() - label = Label.objects.create(club_account=self.journal.club_account, name="bob") - self.client.force_login(User.objects.get(username="comptable")) - self.op1 = Operation( - journal=self.journal, - date=date.today(), - amount=1, - remark="Test bilan", - mode="CASH", - done=True, - label=label, - accounting_type=at, - target_type="USER", - target_id=self.skia.id, - ) - self.op1.save() - self.op2 = Operation( - journal=self.journal, - date=date.today(), - amount=2, - remark="Test bilan", - mode="CASH", - done=True, - label=label, - accounting_type=at, - target_type="USER", - target_id=self.skia.id, - ) - self.op2.save() - - def test_new_operation(self): - at = AccountingType.objects.get(code="604") - response = self.client.post( - reverse("accounting:op_new", args=[self.journal.id]), - { - "amount": 30, - "remark": "Un gros test", - "journal": self.journal.id, - "target_type": "OTHER", - "target_id": "", - "target_label": "Le fantome de la nuit", - "date": self.tomorrow_formatted, - "mode": "CASH", - "cheque_number": "", - "invoice": "", - "simpleaccounting_type": "", - "accounting_type": at.id, - "label": "", - "done": False, - }, - ) - self.assertFalse(response.status_code == 403) - self.assertTrue( - self.journal.operations.filter( - target_label="Le fantome de la nuit" - ).exists() - ) - response_get = self.client.get( - reverse("accounting:journal_details", args=[self.journal.id]) - ) - self.assertTrue("Le fantome de la nuit" in str(response_get.content)) - - def test_bad_new_operation(self): - AccountingType.objects.get(code="604") - response = self.client.post( - reverse("accounting:op_new", args=[self.journal.id]), - { - "amount": 30, - "remark": "Un gros test", - "journal": self.journal.id, - "target_type": "OTHER", - "target_id": "", - "target_label": "Le fantome de la nuit", - "date": self.tomorrow_formatted, - "mode": "CASH", - "cheque_number": "", - "invoice": "", - "simpleaccounting_type": "", - "accounting_type": "", - "label": "", - "done": False, - }, - ) - self.assertTrue( - "Vous devez fournir soit un type comptable simplifi\\xc3\\xa9 ou un type comptable standard" - in str(response.content) - ) - - def test_new_operation_not_authorized(self): - self.client.force_login(self.skia) - at = AccountingType.objects.filter(code="604").first() - response = self.client.post( - reverse("accounting:op_new", args=[self.journal.id]), - { - "amount": 30, - "remark": "Un gros test", - "journal": self.journal.id, - "target_type": "OTHER", - "target_id": "", - "target_label": "Le fantome du jour", - "date": self.tomorrow_formatted, - "mode": "CASH", - "cheque_number": "", - "invoice": "", - "simpleaccounting_type": "", - "accounting_type": at.id, - "label": "", - "done": False, - }, - ) - self.assertTrue(response.status_code == 403) - self.assertFalse( - self.journal.operations.filter(target_label="Le fantome du jour").exists() - ) - - def test_operation_simple_accounting(self): - sat = SimplifiedAccountingType.objects.all().first() - response = self.client.post( - reverse("accounting:op_new", args=[self.journal.id]), - { - "amount": 23, - "remark": "Un gros test", - "journal": self.journal.id, - "target_type": "OTHER", - "target_id": "", - "target_label": "Le fantome de l'aurore", - "date": self.tomorrow_formatted, - "mode": "CASH", - "cheque_number": "", - "invoice": "", - "simpleaccounting_type": sat.id, - "accounting_type": "", - "label": "", - "done": False, - }, - ) - assert response.status_code != 403 - assert self.journal.operations.filter(amount=23).exists() - response_get = self.client.get( - reverse("accounting:journal_details", args=[self.journal.id]) - ) - assert "Le fantome de l'aurore" in str(response_get.content) - - assert ( - self.journal.operations.filter(amount=23) - .values("accounting_type") - .first()["accounting_type"] - == AccountingType.objects.filter(code=6).values("id").first()["id"] - ) - - def test_nature_statement(self): - response = self.client.get( - reverse("accounting:journal_nature_statement", args=[self.journal.id]) - ) - self.assertContains(response, "bob (Troll Penché) : 3.00", status_code=200) - - def test_person_statement(self): - response = self.client.get( - reverse("accounting:journal_person_statement", args=[self.journal.id]) - ) - self.assertContains(response, "Total : 5575.72", status_code=200) - self.assertContains(response, "Total : 71.42") - content = response.content.decode() - self.assertInHTML( - """S' Kia3.00""", content - ) - self.assertInHTML( - """S' Kia823.00""", content - ) - - def test_accounting_statement(self): - response = self.client.get( - reverse("accounting:journal_accounting_statement", args=[self.journal.id]) - ) - assert response.status_code == 200 - self.assertInHTML( - """ - - 443 - Crédit - Ce code n'existe pas - 3.00 - """, - response.content.decode(), - ) - self.assertContains( - response, - """ -

Montant : -5504.30 €

-

Montant effectif: -5504.30 €

""", - ) diff --git a/accounting/urls.py b/accounting/urls.py index f1917462..3bebe3bb 100644 --- a/accounting/urls.py +++ b/accounting/urls.py @@ -15,159 +15,9 @@ from django.urls import path -from accounting.views import ( - AccountingTypeCreateView, - AccountingTypeEditView, - AccountingTypeListView, - BankAccountCreateView, - BankAccountDeleteView, - BankAccountDetailView, - BankAccountEditView, - BankAccountListView, - ClubAccountCreateView, - ClubAccountDeleteView, - ClubAccountDetailView, - ClubAccountEditView, - CompanyCreateView, - CompanyEditView, - CompanyListView, - JournalAccountingStatementView, - JournalCreateView, - JournalDeleteView, - JournalDetailView, - JournalEditView, - JournalNatureStatementView, - JournalPersonStatementView, - LabelCreateView, - LabelDeleteView, - LabelEditView, - LabelListView, - OperationCreateView, - OperationEditView, - OperationPDFView, - RefoundAccountView, - SimplifiedAccountingTypeCreateView, - SimplifiedAccountingTypeEditView, - SimplifiedAccountingTypeListView, -) +from accounting.views import RefoundAccountView urlpatterns = [ - # Accounting types - path( - "simple_type/", - SimplifiedAccountingTypeListView.as_view(), - name="simple_type_list", - ), - path( - "simple_type/create/", - SimplifiedAccountingTypeCreateView.as_view(), - name="simple_type_new", - ), - path( - "simple_type//edit/", - SimplifiedAccountingTypeEditView.as_view(), - name="simple_type_edit", - ), - # Accounting types - path("type/", AccountingTypeListView.as_view(), name="type_list"), - path("type/create/", AccountingTypeCreateView.as_view(), name="type_new"), - path( - "type//edit/", - AccountingTypeEditView.as_view(), - name="type_edit", - ), - # Bank accounts - path("", BankAccountListView.as_view(), name="bank_list"), - path("bank/create", BankAccountCreateView.as_view(), name="bank_new"), - path( - "bank//", - BankAccountDetailView.as_view(), - name="bank_details", - ), - path( - "bank//edit/", - BankAccountEditView.as_view(), - name="bank_edit", - ), - path( - "bank//delete/", - BankAccountDeleteView.as_view(), - name="bank_delete", - ), - # Club accounts - path("club/create/", ClubAccountCreateView.as_view(), name="club_new"), - path( - "club//", - ClubAccountDetailView.as_view(), - name="club_details", - ), - path( - "club//edit/", - ClubAccountEditView.as_view(), - name="club_edit", - ), - path( - "club//delete/", - ClubAccountDeleteView.as_view(), - name="club_delete", - ), - # Journals - path("journal/create/", JournalCreateView.as_view(), name="journal_new"), - path( - "journal//", - JournalDetailView.as_view(), - name="journal_details", - ), - path( - "journal//edit/", - JournalEditView.as_view(), - name="journal_edit", - ), - path( - "journal//delete/", - JournalDeleteView.as_view(), - name="journal_delete", - ), - path( - "journal//statement/nature/", - JournalNatureStatementView.as_view(), - name="journal_nature_statement", - ), - path( - "journal//statement/person/", - JournalPersonStatementView.as_view(), - name="journal_person_statement", - ), - path( - "journal//statement/accounting/", - JournalAccountingStatementView.as_view(), - name="journal_accounting_statement", - ), - # Operations - path( - "operation/create//", - OperationCreateView.as_view(), - name="op_new", - ), - path("operation//", OperationEditView.as_view(), name="op_edit"), - path("operation//pdf/", OperationPDFView.as_view(), name="op_pdf"), - # Companies - path("company/list/", CompanyListView.as_view(), name="co_list"), - path("company/create/", CompanyCreateView.as_view(), name="co_new"), - path("company//", CompanyEditView.as_view(), name="co_edit"), - # Labels - path("label/new/", LabelCreateView.as_view(), name="label_new"), - path( - "label//", - LabelListView.as_view(), - name="label_list", - ), - path("label//edit/", LabelEditView.as_view(), name="label_edit"), - path( - "label//delete/", - LabelDeleteView.as_view(), - name="label_delete", - ), # User account path("refound/account/", RefoundAccountView.as_view(), name="refound_account"), ] diff --git a/accounting/views.py b/accounting/views.py index 5f8640fd..3331d38e 100644 --- a/accounting/views.py +++ b/accounting/views.py @@ -13,829 +13,22 @@ # # -import collections from django import forms from django.conf import settings -from django.contrib.auth.mixins import PermissionRequiredMixin, UserPassesTestMixin -from django.core.exceptions import PermissionDenied, ValidationError +from django.contrib.auth.mixins import UserPassesTestMixin from django.db import transaction -from django.db.models import Sum -from django.forms import HiddenInput -from django.forms.models import modelform_factory -from django.http import HttpResponse -from django.urls import reverse, reverse_lazy +from django.urls import reverse from django.utils.translation import gettext_lazy as _ -from django.views.generic import DetailView, ListView -from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView +from django.views.generic.edit import FormView -from accounting.models import ( - AccountingType, - BankAccount, - ClubAccount, - Company, - GeneralJournal, - Label, - Operation, - SimplifiedAccountingType, -) -from accounting.widgets.ajax_select import ( - AutoCompleteSelectClubAccount, - AutoCompleteSelectCompany, -) -from club.models import Club -from club.widgets.ajax_select import AutoCompleteSelectClub -from core.auth.mixins import ( - CanCreateMixin, - CanEditMixin, - CanEditPropMixin, - CanViewMixin, -) from core.models import User -from core.views.forms import SelectDate, SelectFile -from core.views.mixins import TabedViewMixin from core.views.widgets.ajax_select import AutoCompleteSelectUser from counter.models import Counter, Product, Selling # Main accounting view -class BankAccountListView(CanViewMixin, ListView): - """A list view for the admins.""" - - model = BankAccount - template_name = "accounting/bank_account_list.jinja" - ordering = ["name"] - - -# Simplified accounting types - - -class SimplifiedAccountingTypeListView(CanViewMixin, ListView): - """A list view for the admins.""" - - model = SimplifiedAccountingType - template_name = "accounting/simplifiedaccountingtype_list.jinja" - - -class SimplifiedAccountingTypeEditView(CanViewMixin, UpdateView): - """An edit view for the admins.""" - - model = SimplifiedAccountingType - pk_url_kwarg = "type_id" - fields = ["label", "accounting_type"] - template_name = "core/edit.jinja" - - -class SimplifiedAccountingTypeCreateView(PermissionRequiredMixin, CreateView): - """Create an accounting type (for the admins).""" - - model = SimplifiedAccountingType - fields = ["label", "accounting_type"] - template_name = "core/create.jinja" - permission_required = "accounting.add_simplifiedaccountingtype" - - -# Accounting types - - -class AccountingTypeListView(CanViewMixin, ListView): - """A list view for the admins.""" - - model = AccountingType - template_name = "accounting/accountingtype_list.jinja" - - -class AccountingTypeEditView(CanViewMixin, UpdateView): - """An edit view for the admins.""" - - model = AccountingType - pk_url_kwarg = "type_id" - fields = ["code", "label", "movement_type"] - template_name = "core/edit.jinja" - - -class AccountingTypeCreateView(PermissionRequiredMixin, CreateView): - """Create an accounting type (for the admins).""" - - model = AccountingType - fields = ["code", "label", "movement_type"] - template_name = "core/create.jinja" - permission_required = "accounting.add_accountingtype" - - -# BankAccount views - - -class BankAccountEditView(CanViewMixin, UpdateView): - """An edit view for the admins.""" - - model = BankAccount - pk_url_kwarg = "b_account_id" - fields = ["name", "iban", "number", "club"] - template_name = "core/edit.jinja" - - -class BankAccountDetailView(CanViewMixin, DetailView): - """A detail view, listing every club account.""" - - model = BankAccount - pk_url_kwarg = "b_account_id" - template_name = "accounting/bank_account_details.jinja" - - -class BankAccountCreateView(CanCreateMixin, CreateView): - """Create a bank account (for the admins).""" - - model = BankAccount - fields = ["name", "club", "iban", "number"] - template_name = "core/create.jinja" - - -class BankAccountDeleteView( - CanEditPropMixin, DeleteView -): # TODO change Delete to Close - """Delete a bank account (for the admins).""" - - model = BankAccount - pk_url_kwarg = "b_account_id" - template_name = "core/delete_confirm.jinja" - success_url = reverse_lazy("accounting:bank_list") - - -# ClubAccount views - - -class ClubAccountEditView(CanViewMixin, UpdateView): - """An edit view for the admins.""" - - model = ClubAccount - pk_url_kwarg = "c_account_id" - fields = ["name", "club", "bank_account"] - template_name = "core/edit.jinja" - - -class ClubAccountDetailView(CanViewMixin, DetailView): - """A detail view, listing every journal.""" - - model = ClubAccount - pk_url_kwarg = "c_account_id" - template_name = "accounting/club_account_details.jinja" - - -class ClubAccountCreateView(CanCreateMixin, CreateView): - """Create a club account (for the admins).""" - - model = ClubAccount - fields = ["name", "club", "bank_account"] - template_name = "core/create.jinja" - - def get_initial(self): - ret = super().get_initial() - if "parent" in self.request.GET: - obj = BankAccount.objects.filter(id=int(self.request.GET["parent"])).first() - if obj is not None: - ret["bank_account"] = obj.id - return ret - - -class ClubAccountDeleteView( - CanEditPropMixin, DeleteView -): # TODO change Delete to Close - """Delete a club account (for the admins).""" - - model = ClubAccount - pk_url_kwarg = "c_account_id" - template_name = "core/delete_confirm.jinja" - success_url = reverse_lazy("accounting:bank_list") - - -# Journal views - - -class JournalTabsMixin(TabedViewMixin): - def get_tabs_title(self): - return _("Journal") - - def get_list_of_tabs(self): - return [ - { - "url": reverse( - "accounting:journal_details", kwargs={"j_id": self.object.id} - ), - "slug": "journal", - "name": _("Journal"), - }, - { - "url": reverse( - "accounting:journal_nature_statement", - kwargs={"j_id": self.object.id}, - ), - "slug": "nature_statement", - "name": _("Statement by nature"), - }, - { - "url": reverse( - "accounting:journal_person_statement", - kwargs={"j_id": self.object.id}, - ), - "slug": "person_statement", - "name": _("Statement by person"), - }, - { - "url": reverse( - "accounting:journal_accounting_statement", - kwargs={"j_id": self.object.id}, - ), - "slug": "accounting_statement", - "name": _("Accounting statement"), - }, - ] - - -class JournalCreateView(CanCreateMixin, CreateView): - """Create a general journal.""" - - model = GeneralJournal - form_class = modelform_factory( - GeneralJournal, - fields=["name", "start_date", "club_account"], - widgets={"start_date": SelectDate}, - ) - template_name = "core/create.jinja" - - def get_initial(self): - ret = super().get_initial() - if "parent" in self.request.GET: - obj = ClubAccount.objects.filter(id=int(self.request.GET["parent"])).first() - if obj is not None: - ret["club_account"] = obj.id - return ret - - -class JournalDetailView(JournalTabsMixin, CanViewMixin, DetailView): - """A detail view, listing every operation.""" - - model = GeneralJournal - pk_url_kwarg = "j_id" - template_name = "accounting/journal_details.jinja" - current_tab = "journal" - - -class JournalEditView(CanEditMixin, UpdateView): - """Update a general journal.""" - - model = GeneralJournal - pk_url_kwarg = "j_id" - fields = ["name", "start_date", "end_date", "club_account", "closed"] - template_name = "core/edit.jinja" - - -class JournalDeleteView(CanEditPropMixin, DeleteView): - """Delete a club account (for the admins).""" - - model = GeneralJournal - pk_url_kwarg = "j_id" - template_name = "core/delete_confirm.jinja" - success_url = reverse_lazy("accounting:club_details") - - def dispatch(self, request, *args, **kwargs): - self.object = self.get_object() - if self.object.operations.count() == 0: - return super().dispatch(request, *args, **kwargs) - else: - raise PermissionDenied - - -# Operation views - - -class OperationForm(forms.ModelForm): - class Meta: - model = Operation - fields = [ - "amount", - "remark", - "journal", - "target_type", - "target_id", - "target_label", - "date", - "mode", - "cheque_number", - "invoice", - "simpleaccounting_type", - "accounting_type", - "label", - "done", - ] - widgets = { - "journal": HiddenInput, - "target_id": HiddenInput, - "date": SelectDate, - "invoice": SelectFile, - } - - user = forms.ModelChoiceField( - help_text=None, - required=False, - widget=AutoCompleteSelectUser, - queryset=User.objects.all(), - ) - club_account = forms.ModelChoiceField( - help_text=None, - required=False, - widget=AutoCompleteSelectClubAccount, - queryset=ClubAccount.objects.all(), - ) - club = forms.ModelChoiceField( - help_text=None, - required=False, - widget=AutoCompleteSelectClub, - queryset=Club.objects.all(), - ) - company = forms.ModelChoiceField( - help_text=None, - required=False, - widget=AutoCompleteSelectCompany, - queryset=Company.objects.all(), - ) - need_link = forms.BooleanField( - label=_("Link this operation to the target account"), - required=False, - initial=False, - ) - - def __init__(self, *args, **kwargs): - club_account = kwargs.pop("club_account", None) - super().__init__(*args, **kwargs) - if club_account: - self.fields["label"].queryset = club_account.labels.order_by("name").all() - if self.instance.target_type == "USER": - self.fields["user"].initial = self.instance.target_id - elif self.instance.target_type == "ACCOUNT": - self.fields["club_account"].initial = self.instance.target_id - elif self.instance.target_type == "CLUB": - self.fields["club"].initial = self.instance.target_id - elif self.instance.target_type == "COMPANY": - self.fields["company"].initial = self.instance.target_id - - def clean(self): - self.cleaned_data = super().clean() - if "target_type" in self.cleaned_data: - if ( - self.cleaned_data.get("user") is None - and self.cleaned_data.get("club") is None - and self.cleaned_data.get("club_account") is None - and self.cleaned_data.get("company") is None - and self.cleaned_data.get("target_label") == "" - ): - self.add_error( - "target_type", ValidationError(_("The target must be set.")) - ) - else: - if self.cleaned_data["target_type"] == "USER": - self.cleaned_data["target_id"] = self.cleaned_data["user"].id - elif self.cleaned_data["target_type"] == "ACCOUNT": - self.cleaned_data["target_id"] = self.cleaned_data[ - "club_account" - ].id - elif self.cleaned_data["target_type"] == "CLUB": - self.cleaned_data["target_id"] = self.cleaned_data["club"].id - elif self.cleaned_data["target_type"] == "COMPANY": - self.cleaned_data["target_id"] = self.cleaned_data["company"].id - - if self.cleaned_data.get("amount") is None: - self.add_error("amount", ValidationError(_("The amount must be set."))) - - return self.cleaned_data - - def save(self): - ret = super().save() - if ( - self.instance.target_type == "ACCOUNT" - and not self.instance.linked_operation - and self.instance.target.has_open_journal() - and self.cleaned_data["need_link"] - ): - inst = self.instance - club_account = inst.target - acc_type = ( - AccountingType.objects.exclude(movement_type="NEUTRAL") - .exclude(movement_type=inst.accounting_type.movement_type) - .order_by("code") - .first() - ) # Select a random opposite accounting type - op = Operation( - journal=club_account.get_open_journal(), - amount=inst.amount, - date=inst.date, - remark=inst.remark, - mode=inst.mode, - cheque_number=inst.cheque_number, - invoice=inst.invoice, - done=False, # Has to be checked by hand - simpleaccounting_type=None, - accounting_type=acc_type, - target_type="ACCOUNT", - target_id=inst.journal.club_account.id, - target_label="", - linked_operation=inst, - ) - op.save() - self.instance.linked_operation = op - self.save() - return ret - - -class OperationCreateView(CanCreateMixin, CreateView): - """Create an operation.""" - - model = Operation - form_class = OperationForm - template_name = "accounting/operation_edit.jinja" - - def get_form(self, form_class=None): - self.journal = GeneralJournal.objects.filter(id=self.kwargs["j_id"]).first() - ca = self.journal.club_account if self.journal else None - return self.form_class(club_account=ca, **self.get_form_kwargs()) - - def get_initial(self): - ret = super().get_initial() - if self.journal is not None: - ret["journal"] = self.journal.id - return ret - - def get_context_data(self, **kwargs): - """Add journal to the context.""" - kwargs = super().get_context_data(**kwargs) - if self.journal: - kwargs["object"] = self.journal - return kwargs - - -class OperationEditView(CanEditMixin, UpdateView): - """An edit view, working as detail for the moment.""" - - model = Operation - pk_url_kwarg = "op_id" - form_class = OperationForm - template_name = "accounting/operation_edit.jinja" - - def get_context_data(self, **kwargs): - """Add journal to the context.""" - kwargs = super().get_context_data(**kwargs) - kwargs["object"] = self.object.journal - return kwargs - - -class OperationPDFView(CanViewMixin, DetailView): - """Display the PDF of a given operation.""" - - model = Operation - pk_url_kwarg = "op_id" - - def get(self, request, *args, **kwargs): - from reportlab.lib import colors - from reportlab.lib.pagesizes import letter - from reportlab.lib.units import cm - from reportlab.lib.utils import ImageReader - from reportlab.pdfbase import pdfmetrics - from reportlab.pdfbase.ttfonts import TTFont - from reportlab.pdfgen import canvas - from reportlab.platypus import Table, TableStyle - - pdfmetrics.registerFont(TTFont("DejaVu", "DejaVuSerif.ttf")) - - self.object = self.get_object() - amount = self.object.amount - remark = self.object.remark - nature = self.object.accounting_type.movement_type - num = self.object.number - date = self.object.date - mode = self.object.mode - club_name = self.object.journal.club_account.name - ti = self.object.journal.name - op_label = self.object.label - club_address = self.object.journal.club_account.club.address - id_op = self.object.id - - if self.object.target_type == "OTHER": - target = self.object.target_label - else: - target = self.object.target.get_display_name() - - response = HttpResponse(content_type="application/pdf") - response["Content-Disposition"] = 'filename="op-%d(%s_on_%s).pdf"' % ( - num, - ti, - club_name, - ) - p = canvas.Canvas(response) - - p.setFont("DejaVu", 12) - - p.setTitle("%s %d" % (_("Operation"), num)) - width, height = letter - im = ImageReader("core/static/core/img/logo.jpg") - iw, ih = im.getSize() - p.drawImage(im, 40, height - 50, width=iw / 2, height=ih / 2) - - labelStr = [["%s %s - %s %s" % (_("Journal"), ti, _("Operation"), num)]] - - label = Table(labelStr, colWidths=[150], rowHeights=[20]) - - label.setStyle(TableStyle([("ALIGN", (0, 0), (-1, -1), "RIGHT")])) - w, h = label.wrapOn(label, 0, 0) - label.drawOn(p, width - 180, height) - - p.drawString( - 90, height - 100, _("Financial proof: ") + "OP%010d" % (id_op) - ) # Justificatif du libellé - p.drawString( - 90, height - 130, _("Club: %(club_name)s") % ({"club_name": club_name}) - ) - p.drawString( - 90, - height - 160, - _("Label: %(op_label)s") - % {"op_label": op_label if op_label is not None else ""}, - ) - p.drawString(90, height - 190, _("Date: %(date)s") % {"date": date}) - - data = [] - - data += [ - ["%s" % (_("Credit").upper() if nature == "CREDIT" else _("Debit").upper())] - ] - - data += [[_("Amount: %(amount).2f €") % {"amount": amount}]] - - payment_mode = "" - for m in settings.SITH_ACCOUNTING_PAYMENT_METHOD: - if m[0] == mode: - payment_mode += "[\u00d7]" - else: - payment_mode += "[ ]" - payment_mode += " %s\n" % (m[1]) - - data += [[payment_mode]] - - data += [ - [ - "%s : %s" - % (_("Debtor") if nature == "CREDIT" else _("Creditor"), target), - "", - ] - ] - - data += [["%s \n%s" % (_("Comment:"), remark)]] - - t = Table( - data, colWidths=[(width - 90 * 2) / 2] * 2, rowHeights=[20, 20, 70, 20, 80] - ) - t.setStyle( - TableStyle( - [ - ("ALIGN", (0, 0), (-1, -1), "CENTER"), - ("VALIGN", (-2, -1), (-1, -1), "TOP"), - ("VALIGN", (0, 0), (-1, -2), "MIDDLE"), - ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black), - ("SPAN", (0, 0), (1, 0)), # line DEBIT/CREDIT - ("SPAN", (0, 1), (1, 1)), # line amount - ("SPAN", (-2, -1), (-1, -1)), # line comment - ("SPAN", (0, -2), (-1, -2)), # line creditor/debtor - ("SPAN", (0, 2), (1, 2)), # line payment_mode - ("ALIGN", (0, 2), (1, 2), "LEFT"), # line payment_mode - ("ALIGN", (-2, -1), (-1, -1), "LEFT"), - ("BOX", (0, 0), (-1, -1), 0.25, colors.black), - ] - ) - ) - - signature = [] - signature += [[_("Signature:")]] - - tSig = Table(signature, colWidths=[(width - 90 * 2)], rowHeights=[80]) - tSig.setStyle( - TableStyle( - [ - ("VALIGN", (0, 0), (-1, -1), "TOP"), - ("BOX", (0, 0), (-1, -1), 0.25, colors.black), - ] - ) - ) - - w, h = tSig.wrapOn(p, 0, 0) - tSig.drawOn(p, 90, 200) - - w, h = t.wrapOn(p, 0, 0) - - t.drawOn(p, 90, 350) - - p.drawCentredString(10.5 * cm, 2 * cm, club_name) - p.drawCentredString(10.5 * cm, 1 * cm, club_address) - - p.showPage() - p.save() - return response - - -class JournalNatureStatementView(JournalTabsMixin, CanViewMixin, DetailView): - """Display a statement sorted by labels.""" - - model = GeneralJournal - pk_url_kwarg = "j_id" - template_name = "accounting/journal_statement_nature.jinja" - current_tab = "nature_statement" - - def statement(self, queryset, movement_type): - ret = collections.OrderedDict() - statement = collections.OrderedDict() - total_sum = 0 - for sat in [ - None, - *list(SimplifiedAccountingType.objects.order_by("label")), - ]: - amount = queryset.filter( - accounting_type__movement_type=movement_type, simpleaccounting_type=sat - ).aggregate(amount_sum=Sum("amount"))["amount_sum"] - label = sat.label if sat is not None else "" - if amount: - total_sum += amount - statement[label] = amount - ret[movement_type] = statement - ret[movement_type + "_sum"] = total_sum - return ret - - def big_statement(self): - label_list = ( - self.object.operations.order_by("label").values_list("label").distinct() - ) - labels = Label.objects.filter(id__in=label_list).all() - statement = collections.OrderedDict() - gen_statement = collections.OrderedDict() - no_label_statement = collections.OrderedDict() - gen_statement.update(self.statement(self.object.operations.all(), "CREDIT")) - gen_statement.update(self.statement(self.object.operations.all(), "DEBIT")) - statement[_("General statement")] = gen_statement - no_label_statement.update( - self.statement(self.object.operations.filter(label=None).all(), "CREDIT") - ) - no_label_statement.update( - self.statement(self.object.operations.filter(label=None).all(), "DEBIT") - ) - statement[_("No label operations")] = no_label_statement - for label in labels: - l_stmt = collections.OrderedDict() - journals = self.object.operations.filter(label=label).all() - l_stmt.update(self.statement(journals, "CREDIT")) - l_stmt.update(self.statement(journals, "DEBIT")) - statement[label] = l_stmt - return statement - - def get_context_data(self, **kwargs): - """Add infos to the context.""" - kwargs = super().get_context_data(**kwargs) - kwargs["statement"] = self.big_statement() - return kwargs - - -class JournalPersonStatementView(JournalTabsMixin, CanViewMixin, DetailView): - """Calculate a dictionary with operation target and sum of operations.""" - - model = GeneralJournal - pk_url_kwarg = "j_id" - template_name = "accounting/journal_statement_person.jinja" - current_tab = "person_statement" - - def sum_by_target(self, target_id, target_type, movement_type): - return self.object.operations.filter( - accounting_type__movement_type=movement_type, - target_id=target_id, - target_type=target_type, - ).aggregate(amount_sum=Sum("amount"))["amount_sum"] - - def statement(self, movement_type): - statement = collections.OrderedDict() - for op in ( - self.object.operations.filter(accounting_type__movement_type=movement_type) - .order_by("target_type", "target_id") - .distinct() - ): - statement[op.target] = self.sum_by_target( - op.target_id, op.target_type, movement_type - ) - return statement - - def total(self, movement_type): - return sum(self.statement(movement_type).values()) - - def get_context_data(self, **kwargs): - """Add journal to the context.""" - kwargs = super().get_context_data(**kwargs) - kwargs["credit_statement"] = self.statement("CREDIT") - kwargs["debit_statement"] = self.statement("DEBIT") - kwargs["total_credit"] = self.total("CREDIT") - kwargs["total_debit"] = self.total("DEBIT") - return kwargs - - -class JournalAccountingStatementView(JournalTabsMixin, CanViewMixin, DetailView): - """Calculate a dictionary with operation type and sum of operations.""" - - model = GeneralJournal - pk_url_kwarg = "j_id" - template_name = "accounting/journal_statement_accounting.jinja" - current_tab = "accounting_statement" - - def statement(self): - statement = collections.OrderedDict() - for at in AccountingType.objects.order_by("code").all(): - sum_by_type = self.object.operations.filter( - accounting_type__code__startswith=at.code - ).aggregate(amount_sum=Sum("amount"))["amount_sum"] - if sum_by_type: - statement[at] = sum_by_type - return statement - - def get_context_data(self, **kwargs): - """Add journal to the context.""" - kwargs = super().get_context_data(**kwargs) - kwargs["statement"] = self.statement() - return kwargs - - -# Company views - - -class CompanyListView(CanViewMixin, ListView): - model = Company - template_name = "accounting/co_list.jinja" - - -class CompanyCreateView(CanCreateMixin, CreateView): - """Create a company.""" - - model = Company - fields = ["name"] - template_name = "core/create.jinja" - success_url = reverse_lazy("accounting:co_list") - - -class CompanyEditView(CanCreateMixin, UpdateView): - """Edit a company.""" - - model = Company - pk_url_kwarg = "co_id" - fields = ["name"] - template_name = "core/edit.jinja" - success_url = reverse_lazy("accounting:co_list") - - -# Label views - - -class LabelListView(CanViewMixin, DetailView): - model = ClubAccount - pk_url_kwarg = "clubaccount_id" - template_name = "accounting/label_list.jinja" - - -class LabelCreateView( - CanCreateMixin, CreateView -): # FIXME we need to check the rights before creating the object - model = Label - form_class = modelform_factory( - Label, fields=["name", "club_account"], widgets={"club_account": HiddenInput} - ) - template_name = "core/create.jinja" - - def get_initial(self): - ret = super().get_initial() - if "parent" in self.request.GET: - obj = ClubAccount.objects.filter(id=int(self.request.GET["parent"])).first() - if obj is not None: - ret["club_account"] = obj.id - return ret - - -class LabelEditView(CanEditMixin, UpdateView): - model = Label - pk_url_kwarg = "label_id" - fields = ["name"] - template_name = "core/edit.jinja" - - -class LabelDeleteView(CanEditMixin, DeleteView): - model = Label - pk_url_kwarg = "label_id" - template_name = "core/delete_confirm.jinja" - - def get_success_url(self): - return self.object.get_absolute_url() - - class CloseCustomerAccountForm(forms.Form): user = forms.ModelChoiceField( label=_("Refound this account"), diff --git a/accounting/widgets/ajax_select.py b/accounting/widgets/ajax_select.py deleted file mode 100644 index 76bd8382..00000000 --- a/accounting/widgets/ajax_select.py +++ /dev/null @@ -1,42 +0,0 @@ -from pydantic import TypeAdapter - -from accounting.models import ClubAccount, Company -from accounting.schemas import ClubAccountSchema, CompanySchema -from core.views.widgets.ajax_select import ( - AutoCompleteSelect, - AutoCompleteSelectMultiple, -) - -_js = ["bundled/accounting/components/ajax-select-index.ts"] - - -class AutoCompleteSelectClubAccount(AutoCompleteSelect): - component_name = "club-account-ajax-select" - model = ClubAccount - adapter = TypeAdapter(list[ClubAccountSchema]) - - js = _js - - -class AutoCompleteSelectMultipleClubAccount(AutoCompleteSelectMultiple): - component_name = "club-account-ajax-select" - model = ClubAccount - adapter = TypeAdapter(list[ClubAccountSchema]) - - js = _js - - -class AutoCompleteSelectCompany(AutoCompleteSelect): - component_name = "company-ajax-select" - model = Company - adapter = TypeAdapter(list[CompanySchema]) - - js = _js - - -class AutoCompleteSelectMultipleCompany(AutoCompleteSelectMultiple): - component_name = "company-ajax-select" - model = Company - adapter = TypeAdapter(list[CompanySchema]) - - js = _js diff --git a/club/templates/club/club_tools.jinja b/club/templates/club/club_tools.jinja index fa9584fa..d9245b34 100644 --- a/club/templates/club/club_tools.jinja +++ b/club/templates/club/club_tools.jinja @@ -29,14 +29,6 @@ {% endfor %} {% endif %} - {% if object.club_account.exists() %} -

{% trans %}Accounting: {% endtrans %}

- - {% endif %} {% if object.unix_name == settings.SITH_LAUNDERETTE_MANAGER['unix_name'] %}
  • {% trans %}Manage launderettes{% endtrans %}
  • {% endif %} diff --git a/core/templates/core/user_tools.jinja b/core/templates/core/user_tools.jinja index 4c9b9462..29b276a0 100644 --- a/core/templates/core/user_tools.jinja +++ b/core/templates/core/user_tools.jinja @@ -110,27 +110,7 @@ {% endif %} From cda824ad56e915cdbb2c55b55c7cfc56c35b6c85 Mon Sep 17 00:00:00 2001 From: imperosol Date: Fri, 14 Mar 2025 16:04:16 +0100 Subject: [PATCH 2/6] move CurrencyField to counter --- accounting/migrations/0001_initial.py | 8 ++--- accounting/models.py | 30 +------------------ counter/fields.py | 30 +++++++++++++++++++ counter/migrations/0001_initial.py | 14 ++++----- counter/migrations/0002_auto_20160826_1342.py | 4 +-- counter/migrations/0020_auto_20221215_1709.py | 4 +-- ..._remove_product_parent_product_and_more.py | 6 ++-- counter/models.py | 2 +- counter/views/invoice.py | 2 +- eboutic/migrations/0001_initial.py | 6 ++-- eboutic/models.py | 2 +- 11 files changed, 55 insertions(+), 53 deletions(-) create mode 100644 counter/fields.py diff --git a/accounting/migrations/0001_initial.py b/accounting/migrations/0001_initial.py index 2f22da8d..29ce0739 100644 --- a/accounting/migrations/0001_initial.py +++ b/accounting/migrations/0001_initial.py @@ -4,7 +4,7 @@ import django.core.validators import django.db.models.deletion from django.db import migrations, models -import accounting.models +import counter.fields class Migration(migrations.Migration): @@ -142,7 +142,7 @@ class Migration(migrations.Migration): ), ( "amount", - accounting.models.CurrencyField( + counter.fields.CurrencyField( decimal_places=2, default=0, verbose_name="amount", @@ -151,7 +151,7 @@ class Migration(migrations.Migration): ), ( "effective_amount", - accounting.models.CurrencyField( + counter.fields.CurrencyField( decimal_places=2, default=0, verbose_name="effective_amount", @@ -176,7 +176,7 @@ class Migration(migrations.Migration): ("number", models.IntegerField(verbose_name="number")), ( "amount", - accounting.models.CurrencyField( + counter.fields.CurrencyField( decimal_places=2, max_digits=12, verbose_name="amount" ), ), diff --git a/accounting/models.py b/accounting/models.py index ba1024cf..4ddbb739 100644 --- a/accounting/models.py +++ b/accounting/models.py @@ -13,7 +13,6 @@ # # -from decimal import Decimal from django.conf import settings from django.core import validators @@ -23,34 +22,7 @@ from phonenumber_field.modelfields import PhoneNumberField from club.models import Club from core.models import SithFile - - -class CurrencyField(models.DecimalField): - """Custom database field used for currency.""" - - def __init__(self, *args, **kwargs): - kwargs["max_digits"] = 12 - kwargs["decimal_places"] = 2 - super().__init__(*args, **kwargs) - - def to_python(self, value): - try: - return super().to_python(value).quantize(Decimal("0.01")) - except AttributeError: - return None - - -if settings.TESTING: - from model_bakery import baker - - baker.generators.add( - CurrencyField, - lambda: baker.random_gen.gen_decimal(max_digits=8, decimal_places=2), - ) -else: # pragma: no cover - # baker is only used in tests, so we don't need coverage for this part - pass - +from counter.fields import CurrencyField # Accounting classes diff --git a/counter/fields.py b/counter/fields.py new file mode 100644 index 00000000..a212059d --- /dev/null +++ b/counter/fields.py @@ -0,0 +1,30 @@ +from decimal import Decimal + +from django.conf import settings +from django.db import models + + +class CurrencyField(models.DecimalField): + """Custom database field used for currency.""" + + def __init__(self, *args, **kwargs): + kwargs["max_digits"] = 12 + kwargs["decimal_places"] = 2 + super().__init__(*args, **kwargs) + + def to_python(self, value): + if value is None: + return None + return super().to_python(value).quantize(Decimal("0.01")) + + +if settings.TESTING: + from model_bakery import baker + + baker.generators.add( + CurrencyField, + lambda: baker.random_gen.gen_decimal(max_digits=8, decimal_places=2), + ) +else: # pragma: no cover + # baker is only used in tests, so we don't need coverage for this part + pass diff --git a/counter/migrations/0001_initial.py b/counter/migrations/0001_initial.py index 34381c89..0eb145da 100644 --- a/counter/migrations/0001_initial.py +++ b/counter/migrations/0001_initial.py @@ -4,7 +4,7 @@ import django.db.models.deletion from django.conf import settings from django.db import migrations, models -import accounting.models +import counter.fields class Migration(migrations.Migration): @@ -78,7 +78,7 @@ class Migration(migrations.Migration): ), ( "amount", - accounting.models.CurrencyField( + counter.fields.CurrencyField( decimal_places=2, max_digits=12, verbose_name="amount" ), ), @@ -145,19 +145,19 @@ class Migration(migrations.Migration): ), ( "purchase_price", - accounting.models.CurrencyField( + counter.fields.CurrencyField( decimal_places=2, max_digits=12, verbose_name="purchase price" ), ), ( "selling_price", - accounting.models.CurrencyField( + counter.fields.CurrencyField( decimal_places=2, max_digits=12, verbose_name="selling price" ), ), ( "special_selling_price", - accounting.models.CurrencyField( + counter.fields.CurrencyField( decimal_places=2, max_digits=12, verbose_name="special selling price", @@ -240,7 +240,7 @@ class Migration(migrations.Migration): ), ( "amount", - accounting.models.CurrencyField( + counter.fields.CurrencyField( decimal_places=2, max_digits=12, verbose_name="amount" ), ), @@ -324,7 +324,7 @@ class Migration(migrations.Migration): ("label", models.CharField(max_length=64, verbose_name="label")), ( "unit_price", - accounting.models.CurrencyField( + counter.fields.CurrencyField( decimal_places=2, max_digits=12, verbose_name="unit price" ), ), diff --git a/counter/migrations/0002_auto_20160826_1342.py b/counter/migrations/0002_auto_20160826_1342.py index 7655a480..a9709477 100644 --- a/counter/migrations/0002_auto_20160826_1342.py +++ b/counter/migrations/0002_auto_20160826_1342.py @@ -4,7 +4,7 @@ import django.db.models.deletion from django.conf import settings from django.db import migrations, models -import accounting.models +import counter.fields class Migration(migrations.Migration): @@ -67,7 +67,7 @@ class Migration(migrations.Migration): ), ( "value", - accounting.models.CurrencyField( + counter.fields.CurrencyField( max_digits=12, verbose_name="value", decimal_places=2 ), ), diff --git a/counter/migrations/0020_auto_20221215_1709.py b/counter/migrations/0020_auto_20221215_1709.py index 22db406b..4cf6a95d 100644 --- a/counter/migrations/0020_auto_20221215_1709.py +++ b/counter/migrations/0020_auto_20221215_1709.py @@ -2,7 +2,7 @@ from django.db import migrations -import accounting.models +import counter.fields class Migration(migrations.Migration): @@ -14,7 +14,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="customer", name="amount", - field=accounting.models.CurrencyField( + field=counter.fields.CurrencyField( decimal_places=2, default=0, max_digits=12, verbose_name="amount" ), ), diff --git a/counter/migrations/0025_remove_product_parent_product_and_more.py b/counter/migrations/0025_remove_product_parent_product_and_more.py index 64a2129c..9a2d76a9 100644 --- a/counter/migrations/0025_remove_product_parent_product_and_more.py +++ b/counter/migrations/0025_remove_product_parent_product_and_more.py @@ -2,7 +2,7 @@ from django.db import migrations, models -import accounting.models +import counter.fields class Migration(migrations.Migration): @@ -18,7 +18,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="product", name="purchase_price", - field=accounting.models.CurrencyField( + field=counter.fields.CurrencyField( decimal_places=2, help_text="Initial cost of purchasing the product", max_digits=12, @@ -28,7 +28,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name="product", name="special_selling_price", - field=accounting.models.CurrencyField( + field=counter.fields.CurrencyField( decimal_places=2, help_text="Price for barmen during their permanence", max_digits=12, diff --git a/counter/models.py b/counter/models.py index dc043509..a0f8681d 100644 --- a/counter/models.py +++ b/counter/models.py @@ -38,12 +38,12 @@ from django_countries.fields import CountryField from ordered_model.models import OrderedModel from phonenumber_field.modelfields import PhoneNumberField -from accounting.models import CurrencyField from club.models import Club from core.fields import ResizedImageField from core.models import Group, Notification, User from core.utils import get_start_of_semester from counter.apps import PAYMENT_METHOD +from counter.fields import CurrencyField from sith.settings import SITH_MAIN_CLUB from subscription.models import Subscription diff --git a/counter/views/invoice.py b/counter/views/invoice.py index dbd6e7cb..cabbccdb 100644 --- a/counter/views/invoice.py +++ b/counter/views/invoice.py @@ -19,7 +19,7 @@ from django.db.models import F from django.utils import timezone from django.views.generic import TemplateView -from accounting.models import CurrencyField +from counter.fields import CurrencyField from counter.models import Refilling, Selling from counter.views.mixins import CounterAdminMixin, CounterAdminTabsMixin diff --git a/eboutic/migrations/0001_initial.py b/eboutic/migrations/0001_initial.py index 3e3730f3..1fa8c248 100644 --- a/eboutic/migrations/0001_initial.py +++ b/eboutic/migrations/0001_initial.py @@ -4,7 +4,7 @@ import django.db.models.deletion from django.conf import settings from django.db import migrations, models -import accounting.models +import counter.fields class Migration(migrations.Migration): @@ -55,7 +55,7 @@ class Migration(migrations.Migration): ("type_id", models.IntegerField(verbose_name="product type id")), ( "product_unit_price", - accounting.models.CurrencyField( + counter.fields.CurrencyField( decimal_places=2, max_digits=12, verbose_name="unit price" ), ), @@ -120,7 +120,7 @@ class Migration(migrations.Migration): ("type_id", models.IntegerField(verbose_name="product type id")), ( "product_unit_price", - accounting.models.CurrencyField( + counter.fields.CurrencyField( decimal_places=2, max_digits=12, verbose_name="unit price" ), ), diff --git a/eboutic/models.py b/eboutic/models.py index 980137b5..cd55d0ae 100644 --- a/eboutic/models.py +++ b/eboutic/models.py @@ -25,8 +25,8 @@ from django.db.models import F, OuterRef, Subquery, Sum from django.utils.functional import cached_property from django.utils.translation import gettext_lazy as _ -from accounting.models import CurrencyField from core.models import User +from counter.fields import CurrencyField from counter.models import BillingInfo, Counter, Customer, Product, Refilling, Selling From 89310c3685625f1e737907ed61f4bad31d0e53a8 Mon Sep 17 00:00:00 2001 From: imperosol Date: Fri, 14 Mar 2025 16:04:51 +0100 Subject: [PATCH 3/6] move accound refound view to counter --- accounting/urls.py | 23 ------ accounting/views.py | 78 ------------------- core/templates/core/user_tools.jinja | 2 +- core/views/user.py | 4 +- counter/forms.py | 11 +++ counter/management/commands/dump_accounts.py | 7 +- .../templates/counter}/refound_account.jinja | 0 .../tests.py => counter/tests/test_refound.py | 2 +- counter/urls.py | 7 +- counter/views/admin.py | 46 ++++++++++- sith/urls.py | 4 - 11 files changed, 65 insertions(+), 119 deletions(-) delete mode 100644 accounting/urls.py delete mode 100644 accounting/views.py rename {accounting/templates/accounting => counter/templates/counter}/refound_account.jinja (100%) rename accounting/tests.py => counter/tests/test_refound.py (96%) diff --git a/accounting/urls.py b/accounting/urls.py deleted file mode 100644 index 3bebe3bb..00000000 --- a/accounting/urls.py +++ /dev/null @@ -1,23 +0,0 @@ -# -# Copyright 2023 © AE UTBM -# ae@utbm.fr / ae.info@utbm.fr -# -# This file is part of the website of the UTBM Student Association (AE UTBM), -# https://ae.utbm.fr. -# -# You can find the source code of the website at https://github.com/ae-utbm/sith -# -# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3) -# SEE : https://raw.githubusercontent.com/ae-utbm/sith/master/LICENSE -# OR WITHIN THE LOCAL FILE "LICENSE" -# -# - -from django.urls import path - -from accounting.views import RefoundAccountView - -urlpatterns = [ - # User account - path("refound/account/", RefoundAccountView.as_view(), name="refound_account"), -] diff --git a/accounting/views.py b/accounting/views.py deleted file mode 100644 index 3331d38e..00000000 --- a/accounting/views.py +++ /dev/null @@ -1,78 +0,0 @@ -# -# Copyright 2023 © AE UTBM -# ae@utbm.fr / ae.info@utbm.fr -# -# This file is part of the website of the UTBM Student Association (AE UTBM), -# https://ae.utbm.fr. -# -# You can find the source code of the website at https://github.com/ae-utbm/sith -# -# LICENSED UNDER THE GNU GENERAL PUBLIC LICENSE VERSION 3 (GPLv3) -# SEE : https://raw.githubusercontent.com/ae-utbm/sith/master/LICENSE -# OR WITHIN THE LOCAL FILE "LICENSE" -# -# - - -from django import forms -from django.conf import settings -from django.contrib.auth.mixins import UserPassesTestMixin -from django.db import transaction -from django.urls import reverse -from django.utils.translation import gettext_lazy as _ -from django.views.generic.edit import FormView - -from core.models import User -from core.views.widgets.ajax_select import AutoCompleteSelectUser -from counter.models import Counter, Product, Selling - -# Main accounting view - - -class CloseCustomerAccountForm(forms.Form): - user = forms.ModelChoiceField( - label=_("Refound this account"), - help_text=None, - required=True, - widget=AutoCompleteSelectUser, - queryset=User.objects.all(), - ) - - -class RefoundAccountView(UserPassesTestMixin, FormView): - """Create a selling with the same amount than the current user money.""" - - template_name = "accounting/refound_account.jinja" - form_class = CloseCustomerAccountForm - - def test_func(self): - return self.request.user.is_root or self.request.user.is_in_group( - pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID - ) - - def form_valid(self, form): - self.customer = form.cleaned_data["user"] - self.create_selling() - return super().form_valid(form) - - def get_success_url(self): - return reverse("accounting:refound_account") - - def create_selling(self): - with transaction.atomic(): - uprice = self.customer.customer.amount - refound_club_counter = Counter.objects.get( - id=settings.SITH_COUNTER_REFOUND_ID - ) - refound_club = refound_club_counter.club - s = Selling( - label=_("Refound account"), - unit_price=uprice, - quantity=1, - seller=self.request.user, - customer=self.customer.customer, - club=refound_club, - counter=refound_club_counter, - product=Product.objects.get(id=settings.SITH_PRODUCT_REFOUND_ID), - ) - s.save() diff --git a/core/templates/core/user_tools.jinja b/core/templates/core/user_tools.jinja index 29b276a0..399a8c92 100644 --- a/core/templates/core/user_tools.jinja +++ b/core/templates/core/user_tools.jinja @@ -109,7 +109,7 @@

    {% trans %}Accounting{% endtrans %}

    diff --git a/core/views/user.py b/core/views/user.py index 8e7b092c..8715f36c 100644 --- a/core/views/user.py +++ b/core/views/user.py @@ -65,7 +65,7 @@ from core.views.forms import ( UserProfileForm, ) from core.views.mixins import QuickNotifMixin, TabedViewMixin -from counter.models import Refilling, Selling +from counter.models import Counter, Refilling, Selling from eboutic.models import Invoice from subscription.models import Subscription from trombi.views import UserTrombiForm @@ -385,8 +385,6 @@ class UserStatsView(UserTabsMixin, CanViewMixin, DetailView): kwargs = super().get_context_data(**kwargs) from django.db.models import Sum - from counter.models import Counter - foyer = Counter.objects.filter(name="Foyer").first() mde = Counter.objects.filter(name="MDE").first() gommette = Counter.objects.filter(name="La Gommette").first() diff --git a/counter/forms.py b/counter/forms.py index b509b515..ad32557b 100644 --- a/counter/forms.py +++ b/counter/forms.py @@ -3,6 +3,7 @@ from django.utils.translation import gettext_lazy as _ from phonenumber_field.widgets import RegionalPhoneNumberWidget from club.widgets.ajax_select import AutoCompleteSelectClub +from core.models import User from core.views.forms import NFCTextInput, SelectDate, SelectDateTime from core.views.widgets.ajax_select import ( AutoCompleteSelect, @@ -230,3 +231,13 @@ class EticketForm(forms.ModelForm): "product": AutoCompleteSelectProduct, "event_date": SelectDate, } + + +class CloseCustomerAccountForm(forms.Form): + user = forms.ModelChoiceField( + label=_("Refound this account"), + help_text=None, + required=True, + widget=AutoCompleteSelectUser, + queryset=User.objects.all(), + ) diff --git a/counter/management/commands/dump_accounts.py b/counter/management/commands/dump_accounts.py index e8219103..b25cb93c 100644 --- a/counter/management/commands/dump_accounts.py +++ b/counter/management/commands/dump_accounts.py @@ -11,7 +11,7 @@ from django.utils.timezone import now from django.utils.translation import gettext as _ from core.models import User, UserQuerySet -from counter.models import AccountDump, Counter, Customer, Selling +from counter.models import AccountDump, Counter, Customer, Product, Selling class Command(BaseCommand): @@ -106,6 +106,7 @@ class Command(BaseCommand): raise ValueError("One or more accounts were not engaged in a dump process") counter = Counter.objects.get(pk=settings.SITH_COUNTER_ACCOUNT_DUMP_ID) seller = User.objects.get(pk=settings.SITH_ROOT_USER_ID) + product = Product.objects.get(id=settings.SITH_PRODUCT_REFOUND_ID) sales = Selling.objects.bulk_create( [ Selling( @@ -113,7 +114,7 @@ class Command(BaseCommand): club=counter.club, counter=counter, seller=seller, - product=None, + product=product, customer=account, quantity=1, unit_price=account.amount, @@ -126,7 +127,7 @@ class Command(BaseCommand): sales.sort(key=attrgetter("customer_id")) # dumps and sales are linked to the same customers - # and or both ordered with the same key, so zipping them is valid + # and both ordered with the same key, so zipping them is valid for dump, sale in zip(pending_dumps, sales, strict=False): dump.dump_operation = sale AccountDump.objects.bulk_update(pending_dumps, ["dump_operation"]) diff --git a/accounting/templates/accounting/refound_account.jinja b/counter/templates/counter/refound_account.jinja similarity index 100% rename from accounting/templates/accounting/refound_account.jinja rename to counter/templates/counter/refound_account.jinja diff --git a/accounting/tests.py b/counter/tests/test_refound.py similarity index 96% rename from accounting/tests.py rename to counter/tests/test_refound.py index 34cf7cfb..25c3578c 100644 --- a/accounting/tests.py +++ b/counter/tests/test_refound.py @@ -27,7 +27,7 @@ class TestRefoundAccount(TestCase): # refill skia's account cls.skia.customer.amount = 800 cls.skia.customer.save() - cls.refound_account_url = reverse("accounting:refound_account") + cls.refound_account_url = reverse("counter:account_refound") def test_permission_denied(self): self.client.force_login(User.objects.get(username="guy")) diff --git a/counter/urls.py b/counter/urls.py index 885b4b14..30383ac1 100644 --- a/counter/urls.py +++ b/counter/urls.py @@ -30,6 +30,7 @@ from counter.views.admin import ( ProductTypeEditView, ProductTypeListView, RefillingDeleteView, + RefoundAccountView, SellingDeleteView, ) from counter.views.auth import counter_login, counter_logout @@ -51,10 +52,7 @@ from counter.views.home import ( CounterMain, ) from counter.views.invoice import InvoiceCallView -from counter.views.student_card import ( - StudentCardDeleteView, - StudentCardFormView, -) +from counter.views.student_card import StudentCardDeleteView, StudentCardFormView urlpatterns = [ path("/", CounterMain.as_view(), name="details"), @@ -151,4 +149,5 @@ urlpatterns = [ CounterRefillingListView.as_view(), name="refilling_list", ), + path("admin/refound/", RefoundAccountView.as_view(), name="account_refound"), ] diff --git a/counter/views/admin.py b/counter/views/admin.py index ffe81ea0..d0b75d51 100644 --- a/counter/views/admin.py +++ b/counter/views/admin.py @@ -15,18 +15,21 @@ from datetime import timedelta from django.conf import settings +from django.contrib.auth.mixins import UserPassesTestMixin from django.core.exceptions import PermissionDenied +from django.db import transaction from django.forms import CheckboxSelectMultiple from django.forms.models import modelform_factory from django.shortcuts import get_object_or_404 from django.urls import reverse, reverse_lazy from django.utils import timezone +from django.utils.translation import gettext as _ from django.views.generic import DetailView, ListView, TemplateView -from django.views.generic.edit import CreateView, DeleteView, UpdateView +from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView from core.auth.mixins import CanEditMixin, CanViewMixin from core.utils import get_semester_code, get_start_of_semester -from counter.forms import CounterEditForm, ProductEditForm +from counter.forms import CloseCustomerAccountForm, CounterEditForm, ProductEditForm from counter.models import Counter, Product, ProductType, Refilling, Selling from counter.utils import is_logged_in_counter from counter.views.mixins import CounterAdminMixin, CounterAdminTabsMixin @@ -253,3 +256,42 @@ class CounterRefillingListView(CounterAdminTabsMixin, CounterAdminMixin, ListVie kwargs = super().get_context_data(**kwargs) kwargs["counter"] = self.counter return kwargs + + +class RefoundAccountView(UserPassesTestMixin, FormView): + """Create a selling with the same amount as the current user money.""" + + template_name = "counter/refound_account.jinja" + form_class = CloseCustomerAccountForm + + def test_func(self): + return self.request.user.is_root or self.request.user.is_in_group( + pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID + ) + + def form_valid(self, form): + self.customer = form.cleaned_data["user"] + self.create_selling() + return super().form_valid(form) + + def get_success_url(self): + return self.request.path + + def create_selling(self): + with transaction.atomic(): + uprice = self.customer.customer.amount + refound_club_counter = Counter.objects.get( + id=settings.SITH_COUNTER_REFOUND_ID + ) + refound_club = refound_club_counter.club + s = Selling( + label=_("Refound account"), + unit_price=uprice, + quantity=1, + seller=self.request.user, + customer=self.customer.customer, + club=refound_club, + counter=refound_club_counter, + product=Product.objects.get(id=settings.SITH_PRODUCT_REFOUND_ID), + ) + s.save() diff --git a/sith/urls.py b/sith/urls.py index 0dfa1310..c2250b30 100644 --- a/sith/urls.py +++ b/sith/urls.py @@ -41,10 +41,6 @@ urlpatterns = [ path("com/", include(("com.urls", "com"), namespace="com")), path("club/", include(("club.urls", "club"), namespace="club")), path("counter/", include(("counter.urls", "counter"), namespace="counter")), - path( - "accounting/", - include(("accounting.urls", "accounting"), namespace="accounting"), - ), path("eboutic/", include(("eboutic.urls", "eboutic"), namespace="eboutic")), path( "launderette/", From 8a7056254addd47868d5b0ea247d46ceaae44a2b Mon Sep 17 00:00:00 2001 From: imperosol Date: Fri, 14 Mar 2025 17:04:50 +0100 Subject: [PATCH 4/6] clean populate.py --- core/management/commands/populate.py | 92 +++------------------------- 1 file changed, 8 insertions(+), 84 deletions(-) diff --git a/core/management/commands/populate.py b/core/management/commands/populate.py index 492f971b..700b6cef 100644 --- a/core/management/commands/populate.py +++ b/core/management/commands/populate.py @@ -36,15 +36,6 @@ from django.utils import timezone from django.utils.timezone import localdate from PIL import Image -from accounting.models import ( - AccountingType, - BankAccount, - ClubAccount, - Company, - GeneralJournal, - Operation, - SimplifiedAccountingType, -) from club.models import Club, Membership from com.ics_calendar import IcsCalendar from com.models import News, NewsDate, Sith, Weekmail @@ -509,6 +500,14 @@ Welcome to the wiki page! club=main_club, limit_age=18, ) + Product.objects.create( + name="remboursement", + code="REMBOURS", + purchase_price="0", + selling_price="0", + special_selling_price="0", + club=refound, + ) groups.subscribers.products.add( cotis, cotis2, refill, barb, cble, cors, carolus ) @@ -521,81 +520,6 @@ Welcome to the wiki page! eboutic.products.add(barb, cotis, cotis2, refill) Counter.objects.create(name="Carte AE", club=refound, type="OFFICE") - Product.objects.create( - name="remboursement", - code="REMBOURS", - purchase_price="0", - selling_price="0", - special_selling_price="0", - club=refound, - ) - - # Accounting test values: - BankAccount.objects.create(name="AE TG", club=main_club) - BankAccount.objects.create(name="Carte AE", club=main_club) - ba = BankAccount.objects.create(name="AE TI", club=main_club) - ca = ClubAccount.objects.create( - name="Troll Penché", bank_account=ba, club=troll - ) - gj = GeneralJournal.objects.create( - name="A16", start_date=date.today(), club_account=ca - ) - credit = AccountingType.objects.create( - code="74", label="Subventions d'exploitation", movement_type="CREDIT" - ) - debit = AccountingType.objects.create( - code="606", - label="Achats non stockés de matières et fournitures(*1)", - movement_type="DEBIT", - ) - debit2 = AccountingType.objects.create( - code="604", - label="Achats d'études et prestations de services(*2)", - movement_type="DEBIT", - ) - buying = AccountingType.objects.create( - code="60", label="Achats (sauf 603)", movement_type="DEBIT" - ) - comptes = AccountingType.objects.create( - code="6", label="Comptes de charge", movement_type="DEBIT" - ) - SimplifiedAccountingType.objects.create( - label="Je fais du simple 6", accounting_type=comptes - ) - woenzco = Company.objects.create(name="Woenzel & co") - - operation_list = [ - (27, "J'avais trop de bière", "CASH", buying, "USER", skia.id, None), - (4000, "Pas une opération", "CHECK", debit, "COMPANY", woenzco.id, 23), - (22, "C'est de l'argent ?", "CARD", credit, "CLUB", troll.id, None), - (37, "Je paye CASH", "CASH", debit2, "OTHER", None, None), - (300, "Paiement Guy", "CASH", buying, "USER", skia.id, None), - (32.3, "Essence", "CASH", buying, "OTHER", None, None), - (46.42, "Allumette", "CHECK", credit, "CLUB", main_club.id, 57), - (666.42, "Subvention club", "CASH", comptes, "CLUB", main_club.id, None), - (496, "Ça, c'est un 6", "CARD", comptes, "USER", skia.id, None), - (17, "La Gargotte du Korrigan", "CASH", debit2, "CLUB", bar_club.id, None), - ] - operations = [ - Operation( - number=index, - journal=gj, - date=localdate(), - amount=op[0], - remark=op[1], - mode=op[2], - done=True, - accounting_type=op[3], - target_type=op[4], - target_id=op[5], - target_label="" if op[4] != "OTHER" else "Autre source", - cheque_number=op[6], - ) - for index, op in enumerate(operation_list, start=1) - ] - for operation in operations: - operation.clean() - Operation.objects.bulk_create(operations) # Add barman to counter Counter.sellers.through.objects.bulk_create( From f14f364ca91cb6bc747deccf877c7a063d64ce9c Mon Sep 17 00:00:00 2001 From: imperosol Date: Fri, 14 Mar 2025 17:11:00 +0100 Subject: [PATCH 5/6] remove accounting models --- .../migrations/0006_remove_all_models.py | 34 +++ accounting/models.py | 266 ------------------ 2 files changed, 34 insertions(+), 266 deletions(-) create mode 100644 accounting/migrations/0006_remove_all_models.py diff --git a/accounting/migrations/0006_remove_all_models.py b/accounting/migrations/0006_remove_all_models.py new file mode 100644 index 00000000..96add19c --- /dev/null +++ b/accounting/migrations/0006_remove_all_models.py @@ -0,0 +1,34 @@ +# Generated by Django 4.2.20 on 2025-03-14 16:06 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [("accounting", "0005_auto_20170324_0917")] + + operations = [ + migrations.RemoveField(model_name="bankaccount", name="club"), + migrations.RemoveField(model_name="clubaccount", name="bank_account"), + migrations.RemoveField(model_name="clubaccount", name="club"), + migrations.DeleteModel(name="Company"), + migrations.RemoveField(model_name="generaljournal", name="club_account"), + migrations.AlterUniqueTogether(name="label", unique_together=None), + migrations.RemoveField(model_name="label", name="club_account"), + migrations.AlterUniqueTogether(name="operation", unique_together=None), + migrations.RemoveField(model_name="operation", name="accounting_type"), + migrations.RemoveField(model_name="operation", name="invoice"), + migrations.RemoveField(model_name="operation", name="journal"), + migrations.RemoveField(model_name="operation", name="label"), + migrations.RemoveField(model_name="operation", name="linked_operation"), + migrations.RemoveField(model_name="operation", name="simpleaccounting_type"), + migrations.RemoveField( + model_name="simplifiedaccountingtype", name="accounting_type" + ), + migrations.DeleteModel(name="AccountingType"), + migrations.DeleteModel(name="BankAccount"), + migrations.DeleteModel(name="ClubAccount"), + migrations.DeleteModel(name="GeneralJournal"), + migrations.DeleteModel(name="Label"), + migrations.DeleteModel(name="Operation"), + migrations.DeleteModel(name="SimplifiedAccountingType"), + ] diff --git a/accounting/models.py b/accounting/models.py index 4ddbb739..f4445e69 100644 --- a/accounting/models.py +++ b/accounting/models.py @@ -12,269 +12,3 @@ # OR WITHIN THE LOCAL FILE "LICENSE" # # - - -from django.conf import settings -from django.core import validators -from django.db import models -from django.utils.translation import gettext_lazy as _ -from phonenumber_field.modelfields import PhoneNumberField - -from club.models import Club -from core.models import SithFile -from counter.fields import CurrencyField - -# Accounting classes - - -class Company(models.Model): - name = models.CharField(_("name"), max_length=60) - street = models.CharField(_("street"), max_length=60, blank=True) - city = models.CharField(_("city"), max_length=60, blank=True) - postcode = models.CharField(_("postcode"), max_length=10, blank=True) - country = models.CharField(_("country"), max_length=32, blank=True) - phone = PhoneNumberField(_("phone"), blank=True) - email = models.EmailField(_("email"), blank=True) - website = models.CharField(_("website"), max_length=64, blank=True) - - class Meta: - verbose_name = _("company") - - def __str__(self): - return self.name - - -class BankAccount(models.Model): - name = models.CharField(_("name"), max_length=30) - iban = models.CharField(_("iban"), max_length=255, blank=True) - number = models.CharField(_("account number"), max_length=255, blank=True) - club = models.ForeignKey( - Club, - related_name="bank_accounts", - verbose_name=_("club"), - on_delete=models.CASCADE, - ) - - class Meta: - verbose_name = _("Bank account") - ordering = ["club", "name"] - - def __str__(self): - return self.name - - -class ClubAccount(models.Model): - name = models.CharField(_("name"), max_length=30) - club = models.ForeignKey( - Club, - related_name="club_account", - verbose_name=_("club"), - on_delete=models.CASCADE, - ) - bank_account = models.ForeignKey( - BankAccount, - related_name="club_accounts", - verbose_name=_("bank account"), - on_delete=models.CASCADE, - ) - - class Meta: - verbose_name = _("Club account") - ordering = ["bank_account", "name"] - - def __str__(self): - return self.name - - -class GeneralJournal(models.Model): - """Class storing all the operations for a period of time.""" - - start_date = models.DateField(_("start date")) - end_date = models.DateField(_("end date"), null=True, blank=True, default=None) - name = models.CharField(_("name"), max_length=40) - closed = models.BooleanField(_("is closed"), default=False) - club_account = models.ForeignKey( - ClubAccount, - related_name="journals", - null=False, - verbose_name=_("club account"), - on_delete=models.CASCADE, - ) - amount = CurrencyField(_("amount"), default=0) - effective_amount = CurrencyField(_("effective_amount"), default=0) - - class Meta: - verbose_name = _("General journal") - ordering = ["-start_date"] - - def __str__(self): - return self.name - - -class Operation(models.Model): - """An operation is a line in the journal, a debit or a credit.""" - - number = models.IntegerField(_("number")) - journal = models.ForeignKey( - GeneralJournal, - related_name="operations", - null=False, - verbose_name=_("journal"), - on_delete=models.CASCADE, - ) - amount = CurrencyField(_("amount")) - date = models.DateField(_("date")) - remark = models.CharField(_("comment"), max_length=128, null=True, blank=True) - mode = models.CharField( - _("payment method"), - max_length=255, - choices=settings.SITH_ACCOUNTING_PAYMENT_METHOD, - ) - cheque_number = models.CharField( - _("cheque number"), max_length=32, default="", null=True, blank=True - ) - invoice = models.ForeignKey( - SithFile, - related_name="operations", - verbose_name=_("invoice"), - null=True, - blank=True, - on_delete=models.CASCADE, - ) - done = models.BooleanField(_("is done"), default=False) - simpleaccounting_type = models.ForeignKey( - "SimplifiedAccountingType", - related_name="operations", - verbose_name=_("simple type"), - null=True, - blank=True, - on_delete=models.CASCADE, - ) - accounting_type = models.ForeignKey( - "AccountingType", - related_name="operations", - verbose_name=_("accounting type"), - null=True, - blank=True, - on_delete=models.CASCADE, - ) - label = models.ForeignKey( - "Label", - related_name="operations", - verbose_name=_("label"), - null=True, - blank=True, - on_delete=models.SET_NULL, - ) - target_type = models.CharField( - _("target type"), - max_length=10, - choices=[ - ("USER", _("User")), - ("CLUB", _("Club")), - ("ACCOUNT", _("Account")), - ("COMPANY", _("Company")), - ("OTHER", _("Other")), - ], - ) - target_id = models.IntegerField(_("target id"), null=True, blank=True) - target_label = models.CharField( - _("target label"), max_length=32, default="", blank=True - ) - linked_operation = models.OneToOneField( - "self", - related_name="operation_linked_to", - verbose_name=_("linked operation"), - null=True, - blank=True, - default=None, - on_delete=models.CASCADE, - ) - - class Meta: - unique_together = ("number", "journal") - ordering = ["-number"] - - def __str__(self): - return f"{self.amount} € | {self.date} | {self.accounting_type} | {self.done}" - - -class AccountingType(models.Model): - """Accounting types. - - Those are numbers used in accounting to classify operations - """ - - code = models.CharField( - _("code"), - max_length=16, - validators=[ - validators.RegexValidator( - r"^[0-9]*$", _("An accounting type code contains only numbers") - ) - ], - ) - label = models.CharField(_("label"), max_length=128) - movement_type = models.CharField( - _("movement type"), - choices=[ - ("CREDIT", _("Credit")), - ("DEBIT", _("Debit")), - ("NEUTRAL", _("Neutral")), - ], - max_length=12, - ) - - class Meta: - verbose_name = _("accounting type") - ordering = ["movement_type", "code"] - - def __str__(self): - return self.code + " - " + self.get_movement_type_display() + " - " + self.label - - -class SimplifiedAccountingType(models.Model): - """Simplified version of `AccountingType`.""" - - label = models.CharField(_("label"), max_length=128) - accounting_type = models.ForeignKey( - AccountingType, - related_name="simplified_types", - verbose_name=_("simplified accounting types"), - on_delete=models.CASCADE, - ) - - class Meta: - verbose_name = _("simplified type") - ordering = ["accounting_type__movement_type", "accounting_type__code"] - - def __str__(self): - return ( - f"{self.get_movement_type_display()} " - f"- {self.accounting_type.code} - {self.label}" - ) - - @property - def movement_type(self): - return self.accounting_type.movement_type - - def get_movement_type_display(self): - return self.accounting_type.get_movement_type_display() - - -class Label(models.Model): - """Label allow a club to sort its operations.""" - - name = models.CharField(_("label"), max_length=64) - club_account = models.ForeignKey( - ClubAccount, - related_name="labels", - verbose_name=_("club account"), - on_delete=models.CASCADE, - ) - - class Meta: - unique_together = ("name", "club_account") - - def __str__(self): - return "%s (%s)" % (self.name, self.club_account.name) From 0e66bf5b5c7714a63ea265f0265b9352994fc338 Mon Sep 17 00:00:00 2001 From: imperosol Date: Fri, 14 Mar 2025 18:09:26 +0100 Subject: [PATCH 6/6] remove unused translations --- locale/fr/LC_MESSAGES/django.po | 1016 +++++++------------------------ 1 file changed, 237 insertions(+), 779 deletions(-) diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 19b164df..2ad64bb7 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-02-25 16:38+0100\n" +"POT-Creation-Date: 2025-03-14 18:07+0100\n" "PO-Revision-Date: 2016-07-18\n" "Last-Translator: Maréchal \n" @@ -16,767 +16,6 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n" -#: accounting/models.py club/models.py com/models.py counter/models.py -#: forum/models.py launderette/models.py -msgid "name" -msgstr "nom" - -#: accounting/models.py -msgid "street" -msgstr "rue" - -#: accounting/models.py -msgid "city" -msgstr "ville" - -#: accounting/models.py -msgid "postcode" -msgstr "code postal" - -#: accounting/models.py -msgid "country" -msgstr "pays" - -#: accounting/models.py core/models.py -msgid "phone" -msgstr "téléphone" - -#: accounting/models.py -msgid "email" -msgstr "email" - -#: accounting/models.py -msgid "website" -msgstr "site internet" - -#: accounting/models.py -msgid "company" -msgstr "entreprise" - -#: accounting/models.py -msgid "iban" -msgstr "IBAN" - -#: accounting/models.py -msgid "account number" -msgstr "numéro de compte" - -#: accounting/models.py club/models.py com/models.py counter/models.py -#: trombi/models.py -msgid "club" -msgstr "club" - -#: accounting/models.py -msgid "Bank account" -msgstr "Compte en banque" - -#: accounting/models.py -msgid "bank account" -msgstr "compte en banque" - -#: accounting/models.py -msgid "Club account" -msgstr "Compte club" - -#: accounting/models.py -#, python-format -msgid "%(club_account)s on %(bank_account)s" -msgstr "%(club_account)s sur %(bank_account)s" - -#: accounting/models.py club/models.py counter/models.py election/models.py -#: launderette/models.py -msgid "start date" -msgstr "date de début" - -#: accounting/models.py club/models.py counter/models.py election/models.py -msgid "end date" -msgstr "date de fin" - -#: accounting/models.py -msgid "is closed" -msgstr "est fermé" - -#: accounting/models.py -msgid "club account" -msgstr "compte club" - -#: accounting/models.py counter/models.py -msgid "amount" -msgstr "montant" - -#: accounting/models.py -msgid "effective_amount" -msgstr "montant effectif" - -#: accounting/models.py -msgid "General journal" -msgstr "Classeur" - -#: accounting/models.py -msgid "number" -msgstr "numéro" - -#: accounting/models.py -msgid "journal" -msgstr "classeur" - -#: accounting/models.py core/models.py counter/models.py eboutic/models.py -#: forum/models.py -msgid "date" -msgstr "date" - -#: accounting/models.py counter/models.py pedagogy/models.py -msgid "comment" -msgstr "commentaire" - -#: accounting/models.py counter/models.py subscription/models.py -msgid "payment method" -msgstr "méthode de paiement" - -#: accounting/models.py -msgid "cheque number" -msgstr "numéro de chèque" - -#: accounting/models.py eboutic/models.py -msgid "invoice" -msgstr "facture" - -#: accounting/models.py -msgid "is done" -msgstr "est fait" - -#: accounting/models.py -msgid "simple type" -msgstr "type simplifié" - -#: accounting/models.py -msgid "accounting type" -msgstr "type comptable" - -#: accounting/models.py core/models.py counter/models.py -msgid "label" -msgstr "étiquette" - -#: accounting/models.py -msgid "target type" -msgstr "type de cible" - -#: accounting/models.py club/models.py club/templates/club/club_members.jinja -#: club/templates/club/club_old_members.jinja club/templates/club/mailing.jinja -#: counter/templates/counter/cash_summary_list.jinja -#: counter/templates/counter/stats.jinja -#: launderette/templates/launderette/launderette_admin.jinja -msgid "User" -msgstr "Utilisateur" - -#: accounting/models.py club/models.py club/templates/club/club_detail.jinja -#: com/templates/com/mailing_admin.jinja -#: com/templates/com/news_admin_list.jinja com/templates/com/weekmail.jinja -#: core/templates/core/user_clubs.jinja -#: counter/templates/counter/invoices_call.jinja -#: trombi/templates/trombi/edit_profile.jinja -#: trombi/templates/trombi/export.jinja -#: trombi/templates/trombi/user_profile.jinja -msgid "Club" -msgstr "Club" - -#: accounting/models.py core/views/user.py -msgid "Account" -msgstr "Compte" - -#: accounting/models.py -msgid "Company" -msgstr "Entreprise" - -#: accounting/models.py core/models.py sith/settings.py -msgid "Other" -msgstr "Autre" - -#: accounting/models.py -msgid "target id" -msgstr "id de la cible" - -#: accounting/models.py -msgid "target label" -msgstr "nom de la cible" - -#: accounting/models.py -msgid "linked operation" -msgstr "opération liée" - -#: accounting/models.py -msgid "The date must be set." -msgstr "La date doit être indiquée." - -#: accounting/models.py -#, python-format -msgid "" -"The date can not be before the start date of the journal, which is\n" -"%(start_date)s." -msgstr "" -"La date ne peut pas être avant la date de début du journal, qui est\n" -"%(start_date)s." - -#: accounting/models.py -msgid "Target does not exists" -msgstr "La cible n'existe pas." - -#: accounting/models.py -msgid "Please add a target label if you set no existing target" -msgstr "" -"Merci d'ajouter un nom de cible si vous ne spécifiez pas de cible existante" - -#: accounting/models.py -msgid "" -"You need to provide ether a simplified accounting type or a standard " -"accounting type" -msgstr "" -"Vous devez fournir soit un type comptable simplifié ou un type comptable " -"standard" - -#: accounting/models.py counter/models.py pedagogy/models.py -msgid "code" -msgstr "code" - -#: accounting/models.py -msgid "An accounting type code contains only numbers" -msgstr "Un code comptable ne contient que des numéros" - -#: accounting/models.py -msgid "movement type" -msgstr "type de mouvement" - -#: accounting/models.py -#: accounting/templates/accounting/journal_statement_nature.jinja -#: accounting/templates/accounting/journal_statement_person.jinja -#: accounting/views.py -msgid "Credit" -msgstr "Crédit" - -#: accounting/models.py -#: accounting/templates/accounting/journal_statement_nature.jinja -#: accounting/templates/accounting/journal_statement_person.jinja -#: accounting/views.py -msgid "Debit" -msgstr "Débit" - -#: accounting/models.py -msgid "Neutral" -msgstr "Neutre" - -#: accounting/models.py -msgid "simplified accounting types" -msgstr "type simplifié" - -#: accounting/models.py -msgid "simplified type" -msgstr "type simplifié" - -#: accounting/templates/accounting/accountingtype_list.jinja -msgid "Accounting type list" -msgstr "Liste des types comptable" - -#: accounting/templates/accounting/accountingtype_list.jinja -#: accounting/templates/accounting/bank_account_details.jinja -#: accounting/templates/accounting/bank_account_list.jinja -#: accounting/templates/accounting/club_account_details.jinja -#: accounting/templates/accounting/journal_details.jinja -#: accounting/templates/accounting/label_list.jinja -#: accounting/templates/accounting/operation_edit.jinja -#: accounting/templates/accounting/simplifiedaccountingtype_list.jinja -#: core/templates/core/user_tools.jinja -msgid "Accounting" -msgstr "Comptabilité" - -#: accounting/templates/accounting/accountingtype_list.jinja -msgid "Accounting types" -msgstr "Type comptable" - -#: accounting/templates/accounting/accountingtype_list.jinja -msgid "New accounting type" -msgstr "Nouveau type comptable" - -#: accounting/templates/accounting/accountingtype_list.jinja -#: accounting/templates/accounting/simplifiedaccountingtype_list.jinja -msgid "There is no types in this website." -msgstr "Il n'y a pas de types comptable dans ce site web." - -#: accounting/templates/accounting/bank_account_details.jinja -#: core/templates/core/user_tools.jinja -msgid "Bank account: " -msgstr "Compte en banque : " - -#: accounting/templates/accounting/bank_account_details.jinja -#: accounting/templates/accounting/club_account_details.jinja -#: accounting/templates/accounting/label_list.jinja -#: club/templates/club/club_sellings.jinja club/templates/club/mailing.jinja -#: com/templates/com/macros.jinja com/templates/com/mailing_admin.jinja -#: com/templates/com/news_admin_list.jinja com/templates/com/poster_edit.jinja -#: com/templates/com/screen_edit.jinja com/templates/com/weekmail.jinja -#: core/templates/core/file_detail.jinja -#: core/templates/core/file_moderation.jinja -#: core/templates/core/group_detail.jinja core/templates/core/group_list.jinja -#: core/templates/core/macros.jinja core/templates/core/page_prop.jinja -#: core/templates/core/user_account_detail.jinja -#: core/templates/core/user_clubs.jinja core/templates/core/user_edit.jinja -#: counter/templates/counter/fragments/create_student_card.jinja -#: counter/templates/counter/last_ops.jinja -#: election/templates/election/election_detail.jinja -#: forum/templates/forum/macros.jinja -#: launderette/templates/launderette/launderette_admin.jinja -#: launderette/views.py pedagogy/templates/pedagogy/guide.jinja -#: pedagogy/templates/pedagogy/uv_detail.jinja sas/templates/sas/album.jinja -#: sas/templates/sas/moderation.jinja sas/templates/sas/picture.jinja -#: trombi/templates/trombi/detail.jinja -#: trombi/templates/trombi/edit_profile.jinja -msgid "Delete" -msgstr "Supprimer" - -#: accounting/templates/accounting/bank_account_details.jinja club/views.py -#: core/views/user.py sas/templates/sas/picture.jinja -msgid "Infos" -msgstr "Infos" - -#: accounting/templates/accounting/bank_account_details.jinja -msgid "IBAN: " -msgstr "IBAN : " - -#: accounting/templates/accounting/bank_account_details.jinja -msgid "Number: " -msgstr "Numéro : " - -#: accounting/templates/accounting/bank_account_details.jinja -msgid "New club account" -msgstr "Nouveau compte club" - -#: accounting/templates/accounting/bank_account_details.jinja -#: accounting/templates/accounting/bank_account_list.jinja -#: accounting/templates/accounting/club_account_details.jinja -#: accounting/templates/accounting/journal_details.jinja club/views.py -#: com/templates/com/news_admin_list.jinja com/templates/com/poster_list.jinja -#: com/templates/com/screen_list.jinja com/templates/com/weekmail.jinja -#: core/templates/core/file.jinja core/templates/core/group_list.jinja -#: core/templates/core/page.jinja core/templates/core/user_tools.jinja -#: core/views/user.py counter/templates/counter/cash_summary_list.jinja -#: counter/templates/counter/counter_list.jinja -#: election/templates/election/election_detail.jinja -#: forum/templates/forum/macros.jinja -#: launderette/templates/launderette/launderette_list.jinja -#: pedagogy/templates/pedagogy/guide.jinja -#: pedagogy/templates/pedagogy/uv_detail.jinja sas/templates/sas/album.jinja -#: trombi/templates/trombi/detail.jinja -#: trombi/templates/trombi/edit_profile.jinja -msgid "Edit" -msgstr "Éditer" - -#: accounting/templates/accounting/bank_account_list.jinja -msgid "Bank account list" -msgstr "Liste des comptes en banque" - -#: accounting/templates/accounting/bank_account_list.jinja -msgid "Manage simplified types" -msgstr "Gérer les types simplifiés" - -#: accounting/templates/accounting/bank_account_list.jinja -msgid "Manage accounting types" -msgstr "Gérer les types comptable" - -#: accounting/templates/accounting/bank_account_list.jinja -msgid "New bank account" -msgstr "Nouveau compte en banque" - -#: accounting/templates/accounting/bank_account_list.jinja -msgid "There is no accounts in this website." -msgstr "Il n'y a pas de comptes dans ce site web." - -#: accounting/templates/accounting/club_account_details.jinja -msgid "Club account:" -msgstr "Compte club : " - -#: accounting/templates/accounting/club_account_details.jinja -#: accounting/templates/accounting/journal_details.jinja -#: accounting/templates/accounting/label_list.jinja -msgid "New label" -msgstr "Nouvelle étiquette" - -#: accounting/templates/accounting/club_account_details.jinja -#: accounting/templates/accounting/journal_details.jinja -#: accounting/templates/accounting/label_list.jinja -msgid "Label list" -msgstr "Liste des étiquettes" - -#: accounting/templates/accounting/club_account_details.jinja -msgid "New journal" -msgstr "Nouveau classeur" - -#: accounting/templates/accounting/club_account_details.jinja -msgid "You can not create new journal while you still have one opened" -msgstr "Vous ne pouvez pas créer de journal tant qu'il y en a un d'ouvert" - -#: accounting/templates/accounting/club_account_details.jinja -#: launderette/templates/launderette/launderette_admin.jinja -msgid "Name" -msgstr "Nom" - -#: accounting/templates/accounting/club_account_details.jinja -#: com/templates/com/news_admin_list.jinja -msgid "Start" -msgstr "Début" - -#: accounting/templates/accounting/club_account_details.jinja -#: com/templates/com/news_admin_list.jinja -msgid "End" -msgstr "Fin" - -#: accounting/templates/accounting/club_account_details.jinja -#: accounting/templates/accounting/journal_details.jinja -#: core/templates/core/user_account_detail.jinja -#: counter/templates/counter/last_ops.jinja -#: counter/templates/counter/refilling_list.jinja -msgid "Amount" -msgstr "Montant" - -#: accounting/templates/accounting/club_account_details.jinja -msgid "Effective amount" -msgstr "Montant effectif" - -#: accounting/templates/accounting/club_account_details.jinja sith/settings.py -msgid "Closed" -msgstr "Fermé" - -#: accounting/templates/accounting/club_account_details.jinja -#: accounting/templates/accounting/journal_details.jinja -#: com/templates/com/mailing_admin.jinja -#: com/templates/com/news_admin_list.jinja com/templates/com/weekmail.jinja -#: counter/templates/counter/refilling_list.jinja -msgid "Actions" -msgstr "Actions" - -#: accounting/templates/accounting/club_account_details.jinja -#: accounting/templates/accounting/journal_details.jinja -msgid "Yes" -msgstr "Oui" - -#: accounting/templates/accounting/club_account_details.jinja -#: accounting/templates/accounting/journal_details.jinja -msgid "No" -msgstr "Non" - -#: accounting/templates/accounting/club_account_details.jinja -#: com/templates/com/news_admin_list.jinja core/templates/core/file.jinja -#: core/templates/core/page.jinja -msgid "View" -msgstr "Voir" - -#: accounting/templates/accounting/co_list.jinja -#: accounting/templates/accounting/journal_details.jinja -#: core/templates/core/user_tools.jinja -msgid "Company list" -msgstr "Liste des entreprises" - -#: accounting/templates/accounting/co_list.jinja -msgid "Create new company" -msgstr "Nouvelle entreprise" - -#: accounting/templates/accounting/co_list.jinja -msgid "Companies" -msgstr "Entreprises" - -#: accounting/templates/accounting/journal_details.jinja -#: accounting/templates/accounting/journal_statement_accounting.jinja -#: accounting/templates/accounting/journal_statement_nature.jinja -#: accounting/templates/accounting/journal_statement_person.jinja -msgid "General journal:" -msgstr "Classeur : " - -#: accounting/templates/accounting/journal_details.jinja -#: accounting/templates/accounting/journal_statement_accounting.jinja -#: core/templates/core/user_account.jinja -#: core/templates/core/user_account_detail.jinja -#: counter/templates/counter/counter_click.jinja -msgid "Amount: " -msgstr "Montant : " - -#: accounting/templates/accounting/journal_details.jinja -#: accounting/templates/accounting/journal_statement_accounting.jinja -msgid "Effective amount: " -msgstr "Montant effectif: " - -#: accounting/templates/accounting/journal_details.jinja -msgid "Journal is closed, you can not create operation" -msgstr "Le classeur est fermé, vous ne pouvez pas créer d'opération" - -#: accounting/templates/accounting/journal_details.jinja -msgid "New operation" -msgstr "Nouvelle opération" - -#: accounting/templates/accounting/journal_details.jinja -msgid "Nb" -msgstr "No" - -#: accounting/templates/accounting/journal_details.jinja -#: club/templates/club/club_sellings.jinja -#: core/templates/core/user_account_detail.jinja -#: counter/templates/counter/cash_summary_list.jinja -#: counter/templates/counter/last_ops.jinja -#: counter/templates/counter/refilling_list.jinja -#: rootplace/templates/rootplace/logs.jinja sas/forms.py -#: trombi/templates/trombi/user_profile.jinja -msgid "Date" -msgstr "Date" - -#: accounting/templates/accounting/journal_details.jinja -#: club/templates/club/club_sellings.jinja -#: core/templates/core/user_account_detail.jinja -#: counter/templates/counter/last_ops.jinja -#: rootplace/templates/rootplace/logs.jinja -msgid "Label" -msgstr "Étiquette" - -#: accounting/templates/accounting/journal_details.jinja -msgid "Payment mode" -msgstr "Méthode de paiement" - -#: accounting/templates/accounting/journal_details.jinja -msgid "Target" -msgstr "Cible" - -#: accounting/templates/accounting/journal_details.jinja -msgid "Code" -msgstr "Code" - -#: accounting/templates/accounting/journal_details.jinja -msgid "Nature" -msgstr "Nature" - -#: accounting/templates/accounting/journal_details.jinja -msgid "Done" -msgstr "Effectuées" - -#: accounting/templates/accounting/journal_details.jinja -#: counter/templates/counter/cash_summary_list.jinja counter/views/cash.py -#: pedagogy/templates/pedagogy/moderation.jinja -#: pedagogy/templates/pedagogy/uv_detail.jinja -#: trombi/templates/trombi/comment.jinja -#: trombi/templates/trombi/user_tools.jinja -msgid "Comment" -msgstr "Commentaire" - -#: accounting/templates/accounting/journal_details.jinja -msgid "File" -msgstr "Fichier" - -#: accounting/templates/accounting/journal_details.jinja -msgid "PDF" -msgstr "PDF" - -#: accounting/templates/accounting/journal_details.jinja -msgid "" -"Warning: this operation has no linked operation because the targeted club " -"account has no opened journal." -msgstr "" -"Attention: cette opération n'a pas d'opération liée parce qu'il n'y a pas de " -"classeur ouvert dans le compte club cible" - -#: accounting/templates/accounting/journal_details.jinja -#, python-format -msgid "" -"Open a journal in this club account, then save this " -"operation again to make the linked operation." -msgstr "" -"Ouvrez un classeur dans ce compte club, puis sauver " -"cette opération à nouveau pour créer l'opération liée." - -#: accounting/templates/accounting/journal_details.jinja -msgid "Generate" -msgstr "Générer" - -#: accounting/templates/accounting/journal_statement_accounting.jinja -msgid "Accounting statement: " -msgstr "Bilan comptable : " - -#: accounting/templates/accounting/journal_statement_accounting.jinja -#: rootplace/templates/rootplace/logs.jinja -msgid "Operation type" -msgstr "Type d'opération" - -#: accounting/templates/accounting/journal_statement_accounting.jinja -#: accounting/templates/accounting/journal_statement_nature.jinja -#: accounting/templates/accounting/journal_statement_person.jinja -#: counter/templates/counter/invoices_call.jinja -msgid "Sum" -msgstr "Somme" - -#: accounting/templates/accounting/journal_statement_nature.jinja -msgid "Nature of operation" -msgstr "Nature de l'opération" - -#: accounting/templates/accounting/journal_statement_nature.jinja -#: club/templates/club/club_sellings.jinja -#: counter/templates/counter/counter_main.jinja -msgid "Total: " -msgstr "Total : " - -#: accounting/templates/accounting/journal_statement_nature.jinja -msgid "Statement by nature: " -msgstr "Bilan par nature : " - -#: accounting/templates/accounting/journal_statement_person.jinja -msgid "Statement by person: " -msgstr "Bilan par personne : " - -#: accounting/templates/accounting/journal_statement_person.jinja -msgid "Target of the operation" -msgstr "Cible de l'opération" - -#: accounting/templates/accounting/label_list.jinja -msgid "Back to club account" -msgstr "Retour au compte club" - -#: accounting/templates/accounting/label_list.jinja -msgid "There is no label in this club account." -msgstr "Il n'y a pas d'étiquette dans ce compte club." - -#: accounting/templates/accounting/operation_edit.jinja -msgid "Edit operation" -msgstr "Éditer l'opération" - -#: accounting/templates/accounting/operation_edit.jinja -msgid "" -"Warning: if you select Account, the opposite operation will be " -"created in the target account. If you don't want that, select Club " -"instead of Account." -msgstr "" -"Attention : si vous sélectionnez Compte, l'opération inverse sera " -"créée dans le compte cible. Si vous ne le voulez pas, sélectionnez Club à la place de Compte." - -#: accounting/templates/accounting/operation_edit.jinja -msgid "Linked operation:" -msgstr "Opération liée : " - -#: accounting/templates/accounting/operation_edit.jinja -#: com/templates/com/news_edit.jinja com/templates/com/poster_edit.jinja -#: com/templates/com/screen_edit.jinja com/templates/com/weekmail.jinja -#: core/templates/core/create.jinja core/templates/core/edit.jinja -#: core/templates/core/file_edit.jinja core/templates/core/macros_pages.jinja -#: core/templates/core/page_prop.jinja -#: core/templates/core/user_godfathers.jinja -#: core/templates/core/user_godfathers_tree.jinja -#: core/templates/core/user_preferences.jinja -#: counter/templates/counter/cash_register_summary.jinja -#: forum/templates/forum/reply.jinja -#: subscription/templates/subscription/fragments/creation_form.jinja -#: trombi/templates/trombi/comment.jinja -#: trombi/templates/trombi/edit_profile.jinja -#: trombi/templates/trombi/user_tools.jinja -msgid "Save" -msgstr "Sauver" - -#: accounting/templates/accounting/refound_account.jinja accounting/views.py -msgid "Refound account" -msgstr "Remboursement de compte" - -#: accounting/templates/accounting/refound_account.jinja -msgid "Refound" -msgstr "Rembourser" - -#: accounting/templates/accounting/simplifiedaccountingtype_list.jinja -msgid "Simplified type list" -msgstr "Liste des types simplifiés" - -#: accounting/templates/accounting/simplifiedaccountingtype_list.jinja -msgid "Simplified types" -msgstr "Types simplifiés" - -#: accounting/templates/accounting/simplifiedaccountingtype_list.jinja -msgid "New simplified type" -msgstr "Nouveau type simplifié" - -#: accounting/views.py -msgid "Journal" -msgstr "Classeur" - -#: accounting/views.py -msgid "Statement by nature" -msgstr "Bilan par nature" - -#: accounting/views.py -msgid "Statement by person" -msgstr "Bilan par personne" - -#: accounting/views.py -msgid "Accounting statement" -msgstr "Bilan comptable" - -#: accounting/views.py -msgid "Link this operation to the target account" -msgstr "Lier cette opération au compte cible" - -#: accounting/views.py -msgid "The target must be set." -msgstr "La cible doit être indiquée." - -#: accounting/views.py -msgid "The amount must be set." -msgstr "Le montant doit être indiqué." - -#: accounting/views.py -msgid "Operation" -msgstr "Opération" - -#: accounting/views.py -msgid "Financial proof: " -msgstr "Justificatif de libellé : " - -#: accounting/views.py -#, python-format -msgid "Club: %(club_name)s" -msgstr "Club : %(club_name)s" - -#: accounting/views.py -#, python-format -msgid "Label: %(op_label)s" -msgstr "Libellé : %(op_label)s" - -#: accounting/views.py -#, python-format -msgid "Date: %(date)s" -msgstr "Date : %(date)s" - -#: accounting/views.py -#, python-format -msgid "Amount: %(amount).2f €" -msgstr "Montant : %(amount).2f €" - -#: accounting/views.py -msgid "Debtor" -msgstr "Débiteur" - -#: accounting/views.py -msgid "Creditor" -msgstr "Créditeur" - -#: accounting/views.py -msgid "Comment:" -msgstr "Commentaire :" - -#: accounting/views.py -msgid "Signature:" -msgstr "Signature :" - -#: accounting/views.py -msgid "General statement" -msgstr "Bilan général" - -#: accounting/views.py -msgid "No label operations" -msgstr "Opérations sans étiquette" - -#: accounting/views.py -msgid "Refound this account" -msgstr "Rembourser ce compte" - #: antispam/forms.py msgid "Email domain is not allowed." msgstr "Le domaine de l'addresse e-mail n'est pas autorisé." @@ -881,6 +120,11 @@ msgstr "Vous devez choisir un rôle" msgid "You do not have the permission to do that" msgstr "Vous n'avez pas la permission de faire cela" +#: club/models.py com/models.py counter/models.py forum/models.py +#: launderette/models.py +msgid "name" +msgstr "nom" + #: club/models.py msgid "unix name" msgstr "nom unix" @@ -926,6 +170,18 @@ msgstr "Vous ne pouvez pas faire de boucles dans les clubs" msgid "user" msgstr "utilisateur" +#: club/models.py com/models.py counter/models.py trombi/models.py +msgid "club" +msgstr "club" + +#: club/models.py counter/models.py election/models.py launderette/models.py +msgid "start date" +msgstr "date de début" + +#: club/models.py counter/models.py election/models.py +msgid "end date" +msgstr "date de fin" + #: club/models.py core/models.py election/models.py trombi/models.py msgid "role" msgstr "rôle" @@ -939,6 +195,17 @@ msgstr "description" msgid "past member" msgstr "ancien membre" +#: club/models.py club/templates/club/club_detail.jinja +#: com/templates/com/mailing_admin.jinja +#: com/templates/com/news_admin_list.jinja com/templates/com/weekmail.jinja +#: core/templates/core/user_clubs.jinja +#: counter/templates/counter/invoices_call.jinja +#: trombi/templates/trombi/edit_profile.jinja +#: trombi/templates/trombi/export.jinja +#: trombi/templates/trombi/user_profile.jinja +msgid "Club" +msgstr "Club" + #: club/models.py msgid "Email address" msgstr "Adresse email" @@ -964,6 +231,14 @@ msgstr "Cette liste de diffusion existe déjà." msgid "Mailing" msgstr "Liste de diffusion" +#: club/models.py club/templates/club/club_members.jinja +#: club/templates/club/club_old_members.jinja club/templates/club/mailing.jinja +#: counter/templates/counter/cash_summary_list.jinja +#: counter/templates/counter/stats.jinja +#: launderette/templates/launderette/launderette_admin.jinja +msgid "User" +msgstr "Utilisateur" + #: club/models.py msgid "At least user or email is required" msgstr "Au moins un utilisateur ou un email est nécessaire" @@ -1077,10 +352,25 @@ msgstr "Quantité : " msgid "units" msgstr "unités" +#: club/templates/club/club_sellings.jinja +#: counter/templates/counter/counter_main.jinja +msgid "Total: " +msgstr "Total : " + #: club/templates/club/club_sellings.jinja msgid "Benefit: " msgstr "Bénéfice : " +#: club/templates/club/club_sellings.jinja +#: core/templates/core/user_account_detail.jinja +#: counter/templates/counter/cash_summary_list.jinja +#: counter/templates/counter/last_ops.jinja +#: counter/templates/counter/refilling_list.jinja +#: rootplace/templates/rootplace/logs.jinja sas/forms.py +#: trombi/templates/trombi/user_profile.jinja +msgid "Date" +msgstr "Date" + #: club/templates/club/club_sellings.jinja #: core/templates/core/user_account_detail.jinja #: counter/templates/counter/last_ops.jinja @@ -1094,6 +384,13 @@ msgstr "Barman" msgid "Customer" msgstr "Client" +#: club/templates/club/club_sellings.jinja +#: core/templates/core/user_account_detail.jinja +#: counter/templates/counter/last_ops.jinja +#: rootplace/templates/rootplace/logs.jinja +msgid "Label" +msgstr "Étiquette" + #: club/templates/club/club_sellings.jinja #: core/templates/core/user_account_detail.jinja #: core/templates/core/user_stats.jinja @@ -1119,6 +416,29 @@ msgstr "Total" msgid "Payment method" msgstr "Méthode de paiement" +#: club/templates/club/club_sellings.jinja club/templates/club/mailing.jinja +#: com/templates/com/macros.jinja com/templates/com/mailing_admin.jinja +#: com/templates/com/news_admin_list.jinja com/templates/com/poster_edit.jinja +#: com/templates/com/screen_edit.jinja com/templates/com/weekmail.jinja +#: core/templates/core/file_detail.jinja +#: core/templates/core/file_moderation.jinja +#: core/templates/core/group_detail.jinja core/templates/core/group_list.jinja +#: core/templates/core/macros.jinja core/templates/core/page_prop.jinja +#: core/templates/core/user_account_detail.jinja +#: core/templates/core/user_clubs.jinja core/templates/core/user_edit.jinja +#: counter/templates/counter/fragments/create_student_card.jinja +#: counter/templates/counter/last_ops.jinja +#: election/templates/election/election_detail.jinja +#: forum/templates/forum/macros.jinja +#: launderette/templates/launderette/launderette_admin.jinja +#: launderette/views.py pedagogy/templates/pedagogy/guide.jinja +#: pedagogy/templates/pedagogy/uv_detail.jinja sas/templates/sas/album.jinja +#: sas/templates/sas/moderation.jinja sas/templates/sas/picture.jinja +#: trombi/templates/trombi/detail.jinja +#: trombi/templates/trombi/edit_profile.jinja +msgid "Delete" +msgstr "Supprimer" + #: club/templates/club/club_tools.jinja core/templates/core/user_tools.jinja msgid "Club tools" msgstr "Outils club" @@ -1152,10 +472,6 @@ msgstr "Affiches" msgid "Counters:" msgstr "Comptoirs : " -#: club/templates/club/club_tools.jinja -msgid "Accounting: " -msgstr "Comptabilité : " - #: club/templates/club/club_tools.jinja msgid "Manage launderettes" msgstr "Gestion des laveries" @@ -1222,6 +538,10 @@ msgstr "Aucune page n'existe pour ce club" msgid "Club stats" msgstr "Statistiques du club" +#: club/views.py core/views/user.py sas/templates/sas/picture.jinja +msgid "Infos" +msgstr "Infos" + #: club/views.py msgid "Members" msgstr "Membres" @@ -1239,6 +559,23 @@ msgstr "Historique" msgid "Tools" msgstr "Outils" +#: club/views.py com/templates/com/news_admin_list.jinja +#: com/templates/com/poster_list.jinja com/templates/com/screen_list.jinja +#: com/templates/com/weekmail.jinja core/templates/core/file.jinja +#: core/templates/core/group_list.jinja core/templates/core/page.jinja +#: core/templates/core/user_tools.jinja core/views/user.py +#: counter/templates/counter/cash_summary_list.jinja +#: counter/templates/counter/counter_list.jinja +#: election/templates/election/election_detail.jinja +#: forum/templates/forum/macros.jinja +#: launderette/templates/launderette/launderette_list.jinja +#: pedagogy/templates/pedagogy/guide.jinja +#: pedagogy/templates/pedagogy/uv_detail.jinja sas/templates/sas/album.jinja +#: trombi/templates/trombi/detail.jinja +#: trombi/templates/trombi/edit_profile.jinja +msgid "Edit" +msgstr "Éditer" + #: club/views.py msgid "Edit club page" msgstr "Éditer la page de club" @@ -1448,6 +785,12 @@ msgstr "Nouvelle supprimée" msgid "Mailing lists administration" msgstr "Administration des mailing listes" +#: com/templates/com/mailing_admin.jinja +#: com/templates/com/news_admin_list.jinja com/templates/com/weekmail.jinja +#: counter/templates/counter/refilling_list.jinja +msgid "Actions" +msgstr "Actions" + #: com/templates/com/mailing_admin.jinja core/templates/core/file_detail.jinja #: core/templates/core/file_moderation.jinja sas/templates/sas/moderation.jinja #: sas/templates/sas/picture.jinja @@ -1521,6 +864,11 @@ msgstr "Modérateur" msgid "Dates" msgstr "Dates" +#: com/templates/com/news_admin_list.jinja core/templates/core/file.jinja +#: core/templates/core/page.jinja +msgid "View" +msgstr "Voir" + #: com/templates/com/news_admin_list.jinja msgid "Unpublish" msgstr "Dépublier" @@ -1538,6 +886,14 @@ msgstr "Événements" msgid "Displayed events" msgstr "Événements affichés" +#: com/templates/com/news_admin_list.jinja +msgid "Start" +msgstr "Début" + +#: com/templates/com/news_admin_list.jinja +msgid "End" +msgstr "Fin" + #: com/templates/com/news_admin_list.jinja msgid "Events to moderate" msgstr "Événements à modérer" @@ -1562,6 +918,23 @@ msgstr "Éditer (sera soumise de nouveau à la modération)" msgid "Edit news" msgstr "Éditer la nouvelle" +#: com/templates/com/news_edit.jinja com/templates/com/poster_edit.jinja +#: com/templates/com/screen_edit.jinja com/templates/com/weekmail.jinja +#: core/templates/core/create.jinja core/templates/core/edit.jinja +#: core/templates/core/file_edit.jinja core/templates/core/macros_pages.jinja +#: core/templates/core/page_prop.jinja +#: core/templates/core/user_godfathers.jinja +#: core/templates/core/user_godfathers_tree.jinja +#: core/templates/core/user_preferences.jinja +#: counter/templates/counter/cash_register_summary.jinja +#: forum/templates/forum/reply.jinja +#: subscription/templates/subscription/fragments/creation_form.jinja +#: trombi/templates/trombi/comment.jinja +#: trombi/templates/trombi/edit_profile.jinja +#: trombi/templates/trombi/user_tools.jinja +msgid "Save" +msgstr "Sauver" + #: com/templates/com/news_list.jinja msgid "News feed" msgstr "Flux d'actualités" @@ -1920,6 +1293,10 @@ msgstr "Homme" msgid "Woman" msgstr "Femme" +#: core/models.py sith/settings.py +msgid "Other" +msgstr "Autre" + #: core/models.py msgid "pronouns" msgstr "pronoms" @@ -2020,6 +1397,10 @@ msgstr "signature du forum" msgid "second email address" msgstr "adresse email secondaire" +#: core/models.py +msgid "phone" +msgstr "téléphone" + #: core/models.py msgid "parent phone" msgstr "téléphone des parents" @@ -2136,6 +1517,10 @@ msgstr "type mime" msgid "size" msgstr "taille" +#: core/models.py counter/models.py eboutic/models.py forum/models.py +msgid "date" +msgstr "date" + #: core/models.py msgid "asked for removal" msgstr "retrait demandé" @@ -2234,6 +1619,10 @@ msgstr "type" msgid "viewed" msgstr "vue" +#: core/models.py counter/models.py +msgid "label" +msgstr "étiquette" + #: core/models.py msgid "operation type" msgstr "type d'opération" @@ -2837,6 +2226,7 @@ msgid "Users" msgstr "Utilisateurs" #: core/templates/core/search.jinja core/views/user.py +#: counter/templates/counter/product_list.jinja msgid "Clubs" msgstr "Clubs" @@ -2859,6 +2249,12 @@ msgstr "Compte de %(user_name)s" msgid "User account" msgstr "Compte utilisateur" +#: core/templates/core/user_account.jinja +#: core/templates/core/user_account_detail.jinja +#: counter/templates/counter/counter_click.jinja +msgid "Amount: " +msgstr "Montant : " + #: core/templates/core/user_account.jinja #: core/templates/core/user_account_detail.jinja msgid "Account purchases" @@ -2885,6 +2281,12 @@ msgstr "Etickets" msgid "User has no account" msgstr "L'utilisateur n'a pas de compte" +#: core/templates/core/user_account_detail.jinja +#: counter/templates/counter/last_ops.jinja +#: counter/templates/counter/refilling_list.jinja +msgid "Amount" +msgstr "Montant" + #: core/templates/core/user_account_detail.jinja msgid "Items" msgstr "Articles" @@ -3182,7 +2584,7 @@ msgid "Bans" msgstr "Bans" #: core/templates/core/user_tools.jinja counter/forms.py -#: counter/views/mixins.py +#: counter/templates/counter/product_list.jinja counter/views/mixins.py msgid "Counters" msgstr "Comptoirs" @@ -3213,18 +2615,14 @@ msgstr "Appels à facture" msgid "Stats" msgstr "Stats" +#: core/templates/core/user_tools.jinja +msgid "Accounting" +msgstr "Comptabilité" + #: core/templates/core/user_tools.jinja msgid "Refound Account" msgstr "Rembourser un compte" -#: core/templates/core/user_tools.jinja -msgid "General accounting" -msgstr "Comptabilité générale" - -#: core/templates/core/user_tools.jinja -msgid "Club account: " -msgstr "Compte club : " - #: core/templates/core/user_tools.jinja msgid "Communication" msgstr "Communication" @@ -3416,6 +2814,10 @@ msgstr "Photos" msgid "Galaxy" msgstr "Galaxie" +#: core/views/user.py +msgid "Account" +msgstr "Compte" + #: counter/apps.py sith/settings.py msgid "Check" msgstr "Chèque" @@ -3448,6 +2850,10 @@ msgstr "" "Décrivez le produit. Si c'est un click pour un évènement, donnez quelques " "détails dessus, comme la date (en incluant l'année)." +#: counter/forms.py +msgid "Refound this account" +msgstr "Rembourser ce compte" + #: counter/management/commands/dump_accounts.py msgid "Your AE account has been emptied" msgstr "Votre compte AE a été vidé" @@ -3460,6 +2866,10 @@ msgstr "Vidange de votre compte AE" msgid "account id" msgstr "numéro de compte" +#: counter/models.py +msgid "amount" +msgstr "montant" + #: counter/models.py msgid "recorded product" msgstr "produits consignés" @@ -3520,6 +2930,10 @@ msgstr "Mettre à True si le mail a reçu une erreur" msgid "The operation that emptied the account." msgstr "L'opération qui a vidé le compte." +#: counter/models.py pedagogy/models.py +msgid "comment" +msgstr "commentaire" + #: counter/models.py msgid "A text that will be shown on the eboutic." msgstr "Un texte qui sera affiché sur l'eboutic." @@ -3528,6 +2942,10 @@ msgstr "Un texte qui sera affiché sur l'eboutic." msgid "product type" msgstr "type du produit" +#: counter/models.py pedagogy/models.py +msgid "code" +msgstr "code" + #: counter/models.py msgid "purchase price" msgstr "prix d'achat" @@ -3596,6 +3014,10 @@ msgstr "vendeurs" msgid "token" msgstr "jeton" +#: counter/models.py subscription/models.py +msgid "payment method" +msgstr "méthode de paiement" + #: counter/models.py msgid "bank" msgstr "banque" @@ -3754,6 +3176,14 @@ msgstr "Sommes théoriques" msgid "Emptied" msgstr "Coffre vidé" +#: counter/templates/counter/cash_summary_list.jinja counter/views/cash.py +#: pedagogy/templates/pedagogy/moderation.jinja +#: pedagogy/templates/pedagogy/uv_detail.jinja +#: trombi/templates/trombi/comment.jinja +#: trombi/templates/trombi/user_tools.jinja +msgid "Comment" +msgstr "Commentaire" + #: counter/templates/counter/cash_summary_list.jinja msgid "yes" msgstr "oui" @@ -3927,6 +3357,10 @@ msgstr "Choisir un autre mois : " msgid "CB Payments" msgstr "Payements en Carte Bancaire" +#: counter/templates/counter/invoices_call.jinja +msgid "Sum" +msgstr "Somme" + #: counter/templates/counter/last_ops.jinja #, python-format msgid "%(counter_name)s last operations" @@ -4076,6 +3510,14 @@ msgstr "Il n'y a pas de types de produit dans ce site web." msgid "Seller" msgstr "Vendeur" +#: counter/templates/counter/refound_account.jinja counter/views/admin.py +msgid "Refound account" +msgstr "Remboursement de compte" + +#: counter/templates/counter/refound_account.jinja +msgid "Refound" +msgstr "Rembourser" + #: counter/templates/counter/stats.jinja #, python-format msgid "%(counter_name)s stats" @@ -4242,6 +3684,10 @@ msgstr "id du type du produit" msgid "basket" msgstr "panier" +#: eboutic/models.py +msgid "invoice" +msgstr "facture" + #: eboutic/templates/eboutic/eboutic_main.jinja #: eboutic/templates/eboutic/eboutic_makecommand.jinja msgid "Current account amount: " @@ -4753,6 +4199,10 @@ msgstr "Nouvelle machine" msgid "Type" msgstr "Type" +#: launderette/templates/launderette/launderette_admin.jinja +msgid "Name" +msgstr "Nom" + #: launderette/templates/launderette/launderette_book.jinja msgid "Choose" msgstr "Choisir" @@ -5141,6 +4591,10 @@ msgstr "" "erreur 500), essayez en utilisant l'utilitaire en ligne de commande. " "Utilisez ./manage.py delete_user_messages ID." +#: rootplace/templates/rootplace/logs.jinja +msgid "Operation type" +msgstr "Type d'opération" + #: rootplace/templates/rootplace/logs.jinja msgid "Operator" msgstr "Opérateur" @@ -5219,15 +4673,15 @@ msgstr "SAS" msgid "Albums" msgstr "Albums" -#: sas/templates/sas/album.jinja -msgid "Download album" -msgstr "Télécharger l'album" - #: sas/templates/sas/album.jinja sas/templates/sas/macros.jinja #: sas/templates/sas/user_pictures.jinja msgid "To be moderated" msgstr "A modérer" +#: sas/templates/sas/album.jinja +msgid "Download album" +msgstr "Télécharger l'album" + #: sas/templates/sas/album.jinja msgid "Upload" msgstr "Envoyer" @@ -5418,6 +4872,10 @@ msgstr "ST" msgid "EXT" msgstr "EXT" +#: sith/settings.py +msgid "Closed" +msgstr "Fermé" + #: sith/settings.py msgid "Autumn" msgstr "Automne"