8
0
Fork 0
mirror of https://gitlab2.federez.net/re2o/re2o synced 2024-11-21 19:03:11 +00:00

Merge branch 'release-2.9' into 'master'

Release 2.9

It is that time of the year again.

See [CHANGELOG.md](https://gitlab.federez.net/re2o/re2o/-/blob/release-2.9/CHANGELOG.md).

Many thanks to the people who made Re2o2.9 possible, thanks to their 575 commits in almost one year of development!

* Jean-Romain Garnier
* Gabriel Detraz
* Hugo Levy-Falk
* Arthur Grisel-Davy
* Corentin Canebier
* Yoann Piétri
* Jean-Marie Mineau
* Leila Bekaddour
* Maxime Bombar

See merge request re2o/re2o!592
This commit is contained in:
klafyvel 2021-01-24 17:11:46 +01:00
commit 7c18f1f5ab
608 changed files with 28568 additions and 15026 deletions

5
.gitignore vendored
View file

@ -44,5 +44,10 @@ static/logo/
# re2o specific # re2o specific
settings_local.py settings_local.py
local_routers.py
re2o.png re2o.png
media/ media/
# themes
static/css/themes/*
!static/css/themes/default.css

86
.mailmap Normal file
View file

@ -0,0 +1,86 @@
Gabriel Detraz <chirac@crans.org> <detraz@crans.org>
Gabriel Detraz <chirac@crans.org> <chirac@crans.org>
Gabriel Detraz <chirac@crans.org> <chirac@arachne>
Gabriel Detraz <chirac@crans.org> <chirac@re2o-test.auro.re>
Gabriel Detraz <chirac@crans.org> <chirac@federez.net>
Gabriel Detraz <chirac@crans.org> <chirac@raspberrypi>
Gabriel Detraz <chirac@crans.org> <detraz@re2o-test.crans.org>
Gabriel Detraz <chirac@crans.org> <detraz@re2o.adm.crans.org>
Gabriel Detraz <chirac@crans.org> <chibrac@re2o-server.crans.org>
Gabriel Detraz <chirac@crans.org> <detraz@crans.crans.org>
Gabriel Detraz <chirac@crans.org> <chirac@re2o-server.crans.org>
Gabriel Detraz <chirac@crans.org> <chirac@hydra>
Gabriel Detraz <chirac@crans.org> <chirac@re2o-bcfg2.adm.crans.org>
Gabriel Detraz <chirac@crans.org> <detrazcrans.org>
Gabriel Detraz <chirac@crans.org> <detraz@vo.crans.org>
Maël Kervella <kervella.mael@gmail.com>
Maël Kervella <kervella.mael@gmail.com> <dev@maelkervella.eu>
Maël Kervella <kervella.mael@gmail.com> <mael.kervella@supelec.fr>
Jean-Romain Garnier <jean-romain.garnier@supelec.fr>
Jean-Romain Garnier <jean-romain.garnier@supelec.fr> <git@jean-romain.com>
Hugo Levy-Falk <hugo@klafyvel.me> <hugo.levy--falk@supelec.fr>
Hugo Levy-Falk <hugo@klafyvel.me> <klafyvel@gmail.com>
Hugo Levy-Falk <hugo@klafyvel.me> <klafyvel@klafyvel.me>
Hugo Levy-Falk <hugo@klafyvel.me> <klafyvel@federez.net>
Arthur Grisel-Davy <grisel-davy@crans.org> <grisel-davy@crans.org>
Arthur Grisel-Davy <grisel-davy@crans.org> <grisel-davy@re2o-server.crans.org>
Arthur Grisel-Davy <grisel-davy@crans.org> <grisel-davy@re2o-test.crans.org>
Arthur Grisel-Davy <grisel-davy@crans.org> <grisel-davy@re2o.adm.crans.org>
Arthur Grisel-Davy <grisel-davy@crans.org> <grisel-davy@nazgul.rezometz.org>
Arthur Grisel-Davy <grisel-davy@crans.org> <grisel-davy@re2o-test.auro.re>
Arthur Grisel-Davy <grisel-davy@crans.org> <grizzly@crans.org>
Arthur Grisel-Davy <grisel-davy@crans.org> <grizzly@raspberrypi>
Lara Kermarec <lhark@lharktop> <lhark@lharktop>
Lara Kermarec <lhark@lharktop> <lhark@arachne>
Lara Kermarec <lhark@lharktop> <lhark@pwc>
Lara Kermarec <lhark@lharktop> <goulven.kermarec@supelec.fr>
Lara Kermarec <lhark@lharktop> <lhark@federez-test>
Lara Kermarec <lhark@lharktop> <lhark@hydra>
Augustin Lemesle <augustin.lemesle@supelec.fr> <augustin.lemesle@supelec.fr>
Augustin Lemesle <augustin.lemesle@supelec.fr> <Dalahro@arachne>
Corentin Canebier <corentin@canebier.fr>
Corentin Canebier <corentin@canebier.fr> <corentin.canebier@student-cs.fr>
Root `root` Root <root@arachne>
Root `root` Root <root@arachne> <root@hydra.rezometz.org>
Root `root` Root <root@arachne> <root@federez-test.crans.org>
Root `root` Root <root@arachne> <root@gorgoroth>
Root `root` Root <root@arachne> <root@raspberrypi>
Root `root` Root <root@arachne> <root@nevrast>
Root `root` Root <root@arachne> <root@pwc>
Root `root` Root <root@arachne> <root@federez.net>
Root `root` Root <root@arachne> <root@hydra>
Root `root` Root <root@arachne> <root@morgoth.rez>
Root `root` Root <root@arachne> <root@re2o-server.crans.org>
Yoann Piétri <me@nanoy.fr>
Yoann Piétri <me@nanoy.fr> <yoann.pietri@supelec.fr>
Guillaume Goessel <rezo@guimoz.fr>
Guillaume Goessel <rezo@guimoz.fr> <guillaume.goessel@supelec.fr>
Guillaume Goessel <rezo@guimoz.fr> <guillaume.goessel@hotmail.com>
Guillaume Goessel <rezo@guimoz.fr> <me+federez@guimoz.fr>
David Sinquin <david.RezoMetz@sinquin.eu>
David Sinquin <david.RezoMetz@sinquin.eu> <david.rezo@sinquin.eu>
David Sinquin <david.RezoMetz@sinquin.eu> <david.federez.ldap@sinquin.eu>
Matthieu Michelet <matthieu.michelet@supelec.fr>
Matthieu Michelet <matthieu.michelet@supelec.fr> <michelet.matthieu@supelec.fr>
Jean-Marie Mineau <mineau.jean.marie@gmail.com>
~anonymised~ <sellem+re2o@crans.org>
~anonymised~ <sellem+re2o@crans.org> <sellem@crans.org>
Thibault de Boutray <me@thibaultdeboutray.pro>
Thibault de Boutray <me@thibaultdeboutray.pro> <thibaultdeboutray@gmail.com>
Fardale <fardale+git@crans.org>
Fardale <fardale+git@crans.org> <arrighi@crans.org>
Laouen Fernet <laouen.fernet@supelec.fr>
Laouen Fernet <laouen.fernet@supelec.fr> FERNET Laouen <laouen.fernet@supelec.fr>
Bombar Maxime <bombar@crans.org>
Leïla Bekaddour <leila.bekaddour@student-cs.fr>
Leïla Bekaddour <leila.bekaddour@student-cs.fr> <leila@ec-mac-cr-sho.home>
edpibu <edpibu@crans.org>
edpibu <edpibu@crans.org> <edpibu@re2o-test.crans.org>
Éloi Alain <eloi.alain@supelec.fr>
Éloi Alain <eloi.alain@supelec.fr> <eloi.alain@metz.supelec.fr>
Gabriel Le Bouder <lebouder@crans.org>
Gabriel Le Bouder <lebouder@crans.org> <lebouder@re2o-server.crans.org>
Charlie Jacomme <jacomme@crans.org>
Charlie Jacomme <jacomme@crans.org> <jacomme@crans.org>
Benjamin Graillot <graillot@crans.org>
Benjamin Graillot <graillot@crans.org> <graillot@crans.org>

View file

@ -1,3 +1,72 @@
# Re2o 2.9
## Install steps
To install the latest version of Re2o, checkout the [dedicated wiki entry](https://gitlab.federez.net/re2o/re2o/-/wikis/User-Documentation/Quick-Start#update-re2o).
## Post-install steps
### MR 531: FreeRADIUS Python3 backend
On the Radius server, add `buster-backports` to your `/etc/apt/sources.list`:
```bash
echo "deb http://deb.debian.org/debian buster-backports main contrib" >> /etc/apt/sources.list
```
**Note:** If you are running Debian Bullseye, the package should already be available without going through backports.
Then install the new required packages:
```bash
apt update
apt install -t buster-backports freeradius
cat apt_requirements_radius.txt | xargs sudo apt -y install
```
### MR 582: Autocomplete light
On the Re2o server, install the new dependency and run `collectstatic`:
```bash
sudo pip3 install -r pip_requirements.txt
python3 manage.py collectstatic
```
### MR 589: Move LDAP to optional app
Add `ldap_sync` to your optional apps in your local settings (`re2o/settings_local.py`) if you want to keep using the LDAP synchronisation.
### Final steps
As usual, run the following commands after updating:
```bash
python3 manage.py migrate
python3 manage.py compilemessages
sudo service apache2 reload
```
## New features
Here is a list of noteworthy features brought by this update:
* [!488](https://gitlab.federez.net/re2o/re2o/-/merge_requests/488): Use `+` in searches to combine keywords (e.g. `John+Doe`).
* [!495](https://gitlab.federez.net/re2o/re2o/-/merge_requests/495): Add optional behavior allowing users to override another user's room, if that user is no longer active.
* [!496](https://gitlab.federez.net/re2o/re2o/-/merge_requests/496): Add option to allow users to choose their password during account creation. They will have to separately confirm their email address.
* [!504](https://gitlab.federez.net/re2o/re2o/-/merge_requests/504): Add setting to change the minimum password length.
* [!507](https://gitlab.federez.net/re2o/re2o/-/merge_requests/507): New form for editing lists of rights that should make everyone happier.
* [!512](https://gitlab.federez.net/re2o/re2o/-/merge_requests/512): Add ability to comment on tickets.
* [!513](https://gitlab.federez.net/re2o/re2o/-/merge_requests/513): IP and MAC address history (`Statistics > Machine history` tab) which also works for deleted interfaces. Uses already existing history so events before the upgrade are taken into account.
* [!516](https://gitlab.federez.net/re2o/re2o/-/merge_requests/516): Detailed events in history views (e.g. show `old_email -> new_email`).
* [!519](https://gitlab.federez.net/re2o/re2o/-/merge_requests/519): Add ability to filter event logs (e.g. to show all the subscriptions added by an admin).
* [!569](https://gitlab.federez.net/re2o/re2o/-/merge_requests/569): Refactor navbar to make menu navigation easier.
* [!569](https://gitlab.federez.net/re2o/re2o/-/merge_requests/569): Add ability to install custom themes (checkout [this repository](https://gitlab.federez.net/re2o/re2o-themes) for a list of Re2o themes).
* [!578](https://gitlab.federez.net/re2o/re2o/-/merge_requests/578) : Migrations squashed to ease the installation process.
* [!582](https://gitlab.federez.net/re2o/re2o/-/merge_requests/582): Improve autocomplete fields so they load faster and have a clearer behavior (no more entering a value without clicking and thinking it was taken into account).
* [!589](https://gitlab.federez.net/re2o/re2o/-/merge_requests/589): Move LDAP to a separate optional app.
* Plenty of bug fixes.
You can view the full list of closed issues [here](https://gitlab.federez.net/re2o/re2o/-/issues?scope=all&state=all&milestone_title=Re2o%202.9).
# Before Re2o 2.9
## MR 160: Datepicker ## MR 160: Datepicker
Install libjs-jquery libjs-jquery-ui libjs-jquery-timepicker libjs-bootstrap javascript-common Install libjs-jquery libjs-jquery-ui libjs-jquery-timepicker libjs-bootstrap javascript-common
@ -21,7 +90,6 @@ rm static_files/js/jquery-2.2.4.min.js
rm static/css/jquery-ui-timepicker-addon.css rm static/css/jquery-ui-timepicker-addon.css
``` ```
## MR 159: Graph topo & MR 164: branche de création de graph ## MR 159: Graph topo & MR 164: branche de création de graph
Add a graph of the network topology Add a graph of the network topology
@ -34,7 +102,6 @@ Create the *media/images* directory:
mkdir -p media/images mkdir -p media/images
``` ```
## MR 163: Fix install re2o ## MR 163: Fix install re2o
Refactored install_re2o.sh script. Refactored install_re2o.sh script.
@ -45,8 +112,6 @@ install_re2o.sh help
* The installation templates (LDIF files and `re2o/settings_locale.example.py`) have been changed to use `example.net` instead of `example.org` (more neutral and generic) * The installation templates (LDIF files and `re2o/settings_locale.example.py`) have been changed to use `example.net` instead of `example.org` (more neutral and generic)
## MR 176: Add awesome Logo ## MR 176: Add awesome Logo
Add the logo and fix somme issues on the navbar and home page. Only collecting the statics is needed: Add the logo and fix somme issues on the navbar and home page. Only collecting the statics is needed:
@ -54,7 +119,6 @@ Add the logo and fix somme issues on the navbar and home page. Only collecting t
python3 manage.py collectstatic python3 manage.py collectstatic
``` ```
## MR 172: Refactor API ## MR 172: Refactor API
Creates a new (nearly) REST API to expose all models of Re2o. See [the dedicated wiki page](https://gitlab.federez.net/federez/re2o/wikis/API/Raw-Usage) for more details on how to use it. Creates a new (nearly) REST API to expose all models of Re2o. See [the dedicated wiki page](https://gitlab.federez.net/federez/re2o/wikis/API/Raw-Usage) for more details on how to use it.
@ -75,7 +139,6 @@ OPTIONAL_APPS = (
) )
``` ```
## MR 177: Add django-debug-toolbar support ## MR 177: Add django-debug-toolbar support
Add the possibility to enable `django-debug-toolbar` in debug mode. First install the APT package: Add the possibility to enable `django-debug-toolbar` in debug mode. First install the APT package:
@ -203,3 +266,30 @@ If you use MySQL, please run
``` ```
SET GLOBAL SQL_MODE=ANSI_QUOTES; SET GLOBAL SQL_MODE=ANSI_QUOTES;
``` ```
## MR 531
To use the freeradius python3 backend, please add buster-backports sources to
your apt lists.
Then, install and update freeradius :
```bash
apt install -t buster-backports freeradius
apt install python3-dev
```
Make sure that all depending packages (python3-django etc) provided in the new
apt_requirements_radius.txt are installed.
## MR 535 : Routers
It is now possible to use a custom router file, if you want to have mutli database
support, with for example one as master database and one as replica database.
If you want to add a database routers, please fill in in settings_local.py
and add your databse.
Then, add a file "local_routers.py" in folder app re2o, and add your router path
in the settings_local.py file :
```python
LOCAL_ROUTERS = ["re2o.local_routers.DbRouter"]
```

View file

@ -1,75 +1,64 @@
**Note:** English version below.
# Re2o # Re2o
GNU public license v2.0 GNU public license v2.0
## Avant propos ## Avant propos
Re2o est un logiciel d'administration développé initialement au rezometz. Il Re2o est un logiciel d'administration développé initialement au [Rézo Metz](https://www.rezometz.org/). Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable et configurable facilement.
quelques clics.
Il utilise le framework django avec python3. Il permet de gérer les adhérents, Il utilise le framework django avec python3. Il permet de gérer les adhérents,
les machines, les factures, les droits d'accès, les switchs et la topologie du les machines, les factures, les droits d'accès, les switchs et la topologie du
réseau. réseau.
De cette manière, il est possible de pluguer très facilement des services Il est possible d'activer très facilement des services qui améliorerons les possibilités de Re2o pour convenir au mieux aux besoins de chaque association.
dessus, qui accèdent à la base de donnée en passant par django (ex : dhcp), en
chargeant la liste de toutes les mac-ip, ou la liste des mac-ip autorisées sur
le réseau (adhérent à jour de cotisation).
# Installation ## Installation
Un tutoriel pour installer le projet est disponible [sur le wiki](https://gitlab.federez.net/federez/re2o/wikis/User%20Documentation/Quick%20Start). Un tutoriel pour installer le projet est disponible [sur le wiki](https://gitlab.federez.net/federez/re2o/wikis/User%20Documentation/Quick%20Start).
# Installations Optionnelles ## Fonctionnement Général
## Générer le schéma des dépendances
Pour cela : Re2o utilise le Framework Django et suit donc le principe de toutes les applications Django. Les différents composants sont les models (qui définissent les entrées de la base de données), les templates (qui définissent les pages), les views (qui génèrent les templates avec les données pertinentes), et les forms (qui définissent les pages de modification des objets). Ce framework permet de manipuler les données comme des objets Python.
* apt install python3-django-extensions
* python3 manage.py graph_models -a -g -o re2o.png
# Fonctionnement interne Tous ces composants sont regroupés en apps (users, machines, topologie,...). Certaines de ces apps constituent le coeur de Re2o et sont indispensables à son fonctionnement. Certaines autres apps sont optionnelles et peuvent être activées en fonction des besoins de chaque association.
## Fonctionnement général ## API Rest
Re2o est séparé entre les models, qui sont visibles sur le schéma des Les données stockées dans Re2o sont disponibles via un API Rest. Les services installés sur d'autres machines (dhcp, dns, firewall,...) utilisent cet API pour avoir accès aux données des utilisateurs et fonctionner.
dépendances. Il s'agit en réalité des tables sql, et les fields étant les
colonnes.
Ceci dit il n'est jamais nécessaire de toucher directement au sql, django
procédant automatiquement à tout cela.
On crée donc différents models (user, right pour les droits des users,
interfaces, IpList pour l'ensemble des adresses ip, etc)
Du coté des forms, il s'agit des formulaires d'édition des models. Il # Wiki
s'agit de ModelForms django, qui héritent des models très simplement, voir la
documentation django models forms.
Enfin les views, générent les pages web à partir des forms et des templates. Le [Wiki](https://gitlab.federez.net/federez/re2o/-/wikis/home) est accessible sur le gitlab de Federez. Il regroupe les informations et instructions pour la plupart des composants de Re2o.
## Fonctionnement avec les services ----
Les services dhcp.py, dns.py etc accèdent aux données via des vues rest. # Re2o
Celles-ci se trouvent dans machines/views.py. Elles sont générées via
machines/serializers.py qui génère les vues. IL s'agit de vues en json utilisées
par re2o-tools pour récupérer les données.
Il est nécessaire de créer un user dans re2o avec le droit serveur qui permet
d'accéder à ces vues, utilisé par re2o-tools.
# Requète en base de donnée GNU Public license v2.0
Pour avoir un shell, lancer : ## Foreword
```.bash
python3 manage.py shell
```
Pour charger des objets (exemple avec User), faire : Re2o is a management software initially developed at [Rézo Metz](https://www.rezometz.org/). It is now in use in several student organizations. It aims to remain agnostic of the organization that uses it and be easy to setup.
```.python
from users.models import User
```
Pour charger les objets django, il suffit de faire `User.objects.all()` Re2o is based on the Django framework and Python3. Its core functionalities include managing the members, their machines, their invoices and their rights to the network but also the topology of the network and its devices.
pour tous les users par exemple. On top of this, it is possible to plug services to enhance the possibilities and fit the need of each organization.
Il est ensuite aisé de faire des requêtes, par exemple
`User.objects.filter(pseudo='test')`
Des exemples et la documentation complète sur les requêtes django sont # Setup
disponible sur le site officiel.
A tutorial is available on the [Wiki](https://gitlab.federez.net/federez/re2o/wikis/User%20Documentation/Quick%20Start) to describe the setup process.
# General Functioning
Re2o follow the general functioning of a Django project and split its components between the models (describe the database objects), the templates (that define the front end), the views (that populate and serve the templates) and the forms (that provide front end object edition/creation/removal). This framework provide an abstraction layer to manipulate SQL objects as Python objects.
Functionalities are grouped in apps (users, machines, topologie,...). Along the core functionalities, optional functionalities are available and can be activated in the preferences.
## Rest API
Re2o provide a Rest API to allow external services (dhcp, dns, firewall,...) installed on remote machines to access database informations in Json format. Those services are optional and should be installed and activated to fit each organization needs.
# Wiki
The [Wiki](https://gitlab.federez.net/federez/re2o/-/wikis/home) is available to provide information and instruction for most components of Re2o.

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
@ -31,32 +31,7 @@ from django.contrib.contenttypes.models import ContentType
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
def _create_api_permission(): def can_view(user, *args, **kwargs):
"""Creates the 'use_api' permission if not created.
The 'use_api' is a fake permission in the sense it is not associated with an
existing model and this ensure the permission is created every time this file
is imported.
"""
api_content_type, created = ContentType.objects.get_or_create(
app_label=settings.API_CONTENT_TYPE_APP_LABEL,
model=settings.API_CONTENT_TYPE_MODEL,
)
if created:
api_content_type.save()
api_permission, created = Permission.objects.get_or_create(
name=settings.API_PERMISSION_NAME,
content_type=api_content_type,
codename=settings.API_PERMISSION_CODENAME,
)
if created:
api_permission.save()
_create_api_permission()
def can_view(user):
"""Check if an user can view the application. """Check if an user can view the application.
Args: Args:

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
@ -21,7 +21,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 2.5\n" "Project-Id-Version: 2.5\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-11-19 23:43+0100\n" "POT-Creation-Date: 2021-01-24 11:51+0100\n"
"PO-Revision-Date: 2019-01-07 01:37+0100\n" "PO-Revision-Date: 2019-01-07 01:37+0100\n"
"Last-Translator: Laouen Fernet <laouen.fernet@supelec.fr>\n" "Last-Translator: Laouen Fernet <laouen.fernet@supelec.fr>\n"
"Language-Team: \n" "Language-Team: \n"
@ -31,7 +31,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n" "Plural-Forms: nplurals=2; plural=(n > 1);\n"
#: api/acl.py:77 #: api/acl.py:52
msgid "You don't have the right to view this application." msgid "You don't have the right to view this application."
msgstr "Vous n'avez pas le droit de voir cette application." msgstr "Vous n'avez pas le droit de voir cette application."

View file

@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
from django.conf import settings
def create_api_permission(apps, schema_editor):
"""Creates the 'use_api' permission if not created.
The 'use_api' is a fake permission in the sense it is not associated with an
existing model and this ensure the permission is created.
"""
ContentType = apps.get_model("contenttypes", "ContentType")
Permission = apps.get_model("auth", "Permission")
api_content_type, created = ContentType.objects.get_or_create(
app_label=settings.API_CONTENT_TYPE_APP_LABEL,
model=settings.API_CONTENT_TYPE_MODEL,
)
if created:
api_content_type.save()
api_permission, created = Permission.objects.get_or_create(
name=settings.API_PERMISSION_NAME,
content_type=api_content_type,
codename=settings.API_PERMISSION_CODENAME,
)
if created:
api_permission.save()
class Migration(migrations.Migration):
initial = True
dependencies = []
operations = [
migrations.RunPython(create_api_permission)
]

View file

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
@ -239,6 +239,11 @@ class AutodetectACLPermission(permissions.BasePermission):
if getattr(view, "_ignore_model_permissions", False): if getattr(view, "_ignore_model_permissions", False):
return True return True
# Bypass permission verifications if it is a functional view
# (permissions are handled by ACL)
if not hasattr(view, "queryset") and not hasattr(view, "get_queryset"):
return True
if not request.user or not request.user.is_authenticated: if not request.user or not request.user.is_authenticated:
return False return False

View file

@ -1,9 +1,10 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
# Copyright © 2018 Mael Kervella # Copyright © 2018 Mael Kervella
# Copyright © 2020 Corentin Canebier
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -41,6 +42,7 @@ class AllViewsRouter(DefaultRouter):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
self.view_registry = [] self.view_registry = []
self.functional_view_registry = []
super(AllViewsRouter, self).__init__(*args, **kwargs) super(AllViewsRouter, self).__init__(*args, **kwargs)
def register_viewset(self, *args, **kwargs): def register_viewset(self, *args, **kwargs):
@ -64,6 +66,19 @@ class AllViewsRouter(DefaultRouter):
name = self.get_default_name(pattern) name = self.get_default_name(pattern)
self.view_registry.append((pattern, view, name)) self.view_registry.append((pattern, view, name))
def register_functional_view(self, pattern, view, name=None):
"""Register a functional view in the router.
Args:
pattern: The URL pattern to use for this view.
view: The functional view to register.
name: An optional name for the route generated. Defaults is
based on the pattern last section (delimited by '/').
"""
if name is None:
name = self.get_default_name(pattern)
self.functional_view_registry.append((pattern, view, name))
@staticmethod @staticmethod
def get_default_name(pattern): def get_default_name(pattern):
"""Returns the name to use for the route if none was specified. """Returns the name to use for the route if none was specified.
@ -155,4 +170,7 @@ class AllViewsRouter(DefaultRouter):
for pattern, view, name in self.view_registry: for pattern, view, name in self.view_registry:
urls.append(url(pattern, view.as_view(), name=name)) urls.append(url(pattern, view.as_view(), name=name))
for pattern, view, name in self.functional_view_registry:
urls.append(url(pattern, view, name=name))
return urls return urls

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,9 +1,10 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
# Copyright © 2018 Maël Kervella # Copyright © 2018 Maël Kervella
# Copyright © 2020 Corentin Canebier
# #
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -28,114 +29,39 @@ can be generated automatically.
""" """
from django.conf.urls import url, include from django.conf.urls import url, include
from importlib import import_module
from . import views from . import views
from .routers import AllViewsRouter from .routers import AllViewsRouter
from django.conf import settings
router = AllViewsRouter() router = AllViewsRouter()
# COTISATIONS
router.register_viewset(r"cotisations/facture", views.FactureViewSet) urls_viewset = []
router.register_viewset(r"cotisations/vente", views.VenteViewSet) urls_view = []
router.register_viewset(r"cotisations/article", views.ArticleViewSet) urls_functional_view = []
router.register_viewset(r"cotisations/banque", views.BanqueViewSet)
router.register_viewset(r"cotisations/paiement", views.PaiementViewSet) for app in settings.INSTALLED_APPS:
router.register_viewset(r"cotisations/cotisation", views.CotisationViewSet) try:
# MACHINES module = import_module(".api.urls", package=app)
router.register_viewset(r"machines/machine", views.MachineViewSet) urls_viewset += getattr(module, "urls_viewset", [])
router.register_viewset(r"machines/machinetype", views.MachineTypeViewSet) urls_view += getattr(module, "urls_view", [])
router.register_viewset(r"machines/iptype", views.IpTypeViewSet) urls_functional_view += getattr(module, "urls_functional_view", [])
router.register_viewset(r"machines/vlan", views.VlanViewSet) except ImportError:
router.register_viewset(r"machines/nas", views.NasViewSet) continue
router.register_viewset(r"machines/soa", views.SOAViewSet)
router.register_viewset(r"machines/extension", views.ExtensionViewSet) for _url, viewset, name in urls_viewset:
router.register_viewset(r"machines/mx", views.MxViewSet) if name == None:
router.register_viewset(r"machines/ns", views.NsViewSet) router.register_viewset(_url, viewset)
router.register_viewset(r"machines/txt", views.TxtViewSet) else:
router.register_viewset(r"machines/dname", views.DNameViewSet) router.register_viewset(_url, viewset, basename=name)
router.register_viewset(r"machines/srv", views.SrvViewSet)
router.register_viewset(r"machines/sshfp", views.SshFpViewSet) for _url, view in urls_view:
router.register_viewset(r"machines/interface", views.InterfaceViewSet) router.register_view(_url, view)
router.register_viewset(r"machines/ipv6list", views.Ipv6ListViewSet)
router.register_viewset(r"machines/domain", views.DomainViewSet) for _url, view, name in urls_functional_view:
router.register_viewset(r"machines/iplist", views.IpListViewSet) router.register_functional_view(_url, view, name)
router.register_viewset(r"machines/service", views.ServiceViewSet)
router.register_viewset(
r"machines/servicelink", views.ServiceLinkViewSet, base_name="servicelink"
)
router.register_viewset(r"machines/ouvertureportlist", views.OuverturePortListViewSet)
router.register_viewset(r"machines/ouvertureport", views.OuverturePortViewSet)
router.register_viewset(r"machines/role", views.RoleViewSet)
# PREFERENCES
router.register_view(r"preferences/optionaluser", views.OptionalUserView),
router.register_view(r"preferences/optionalmachine", views.OptionalMachineView),
router.register_view(r"preferences/optionaltopologie", views.OptionalTopologieView),
router.register_view(r"preferences/radiusoption", views.RadiusOptionView),
router.register_view(r"preferences/generaloption", views.GeneralOptionView),
router.register_viewset(
r"preferences/service", views.HomeServiceViewSet, base_name="homeservice"
),
router.register_view(r"preferences/assooption", views.AssoOptionView),
router.register_view(r"preferences/homeoption", views.HomeOptionView),
router.register_view(r"preferences/mailmessageoption", views.MailMessageOptionView),
# TOPOLOGIE
router.register_viewset(r"topologie/stack", views.StackViewSet)
router.register_viewset(r"topologie/acesspoint", views.AccessPointViewSet)
router.register_viewset(r"topologie/switch", views.SwitchViewSet)
router.register_viewset(r"topologie/server", views.ServerViewSet)
router.register_viewset(r"topologie/modelswitch", views.ModelSwitchViewSet)
router.register_viewset(r"topologie/constructorswitch", views.ConstructorSwitchViewSet)
router.register_viewset(r"topologie/switchbay", views.SwitchBayViewSet)
router.register_viewset(r"topologie/building", views.BuildingViewSet)
router.register_viewset(
r"topologie/switchport", views.SwitchPortViewSet, base_name="switchport"
)
router.register_viewset(
r"topologie/portprofile", views.PortProfileViewSet, base_name="portprofile"
)
router.register_viewset(r"topologie/room", views.RoomViewSet)
router.register(r"topologie/portprofile", views.PortProfileViewSet)
# USERS
router.register_viewset(r"users/user", views.UserViewSet, base_name="user")
router.register_viewset(
r"users/homecreation", views.HomeCreationViewSet, base_name="homecreation"
)
router.register_viewset(
r"users/normaluser", views.NormalUserViewSet, base_name="normaluser"
)
router.register_viewset(
r"users/criticaluser", views.CriticalUserViewSet, base_name="criticaluser"
)
router.register_viewset(r"users/club", views.ClubViewSet)
router.register_viewset(r"users/adherent", views.AdherentViewSet)
router.register_viewset(r"users/serviceuser", views.ServiceUserViewSet)
router.register_viewset(r"users/school", views.SchoolViewSet)
router.register_viewset(r"users/listright", views.ListRightViewSet)
router.register_viewset(r"users/shell", views.ShellViewSet, base_name="shell")
router.register_viewset(r"users/ban", views.BanViewSet)
router.register_viewset(r"users/whitelist", views.WhitelistViewSet)
router.register_viewset(r"users/emailaddress", views.EMailAddressViewSet)
# SERVICE REGEN
router.register_viewset(
r"services/regen", views.ServiceRegenViewSet, base_name="serviceregen"
)
# DHCP
router.register_view(r"dhcp/hostmacip", views.HostMacIpView),
# LOCAL EMAILS
router.register_view(r"localemail/users", views.LocalEmailUsersView),
# Firewall
router.register_view(r"firewall/subnet-ports", views.SubnetPortsOpenView),
router.register_view(r"firewall/interface-ports", views.InterfacePortsOpenView),
# Switches config
router.register_view(r"switchs/ports-config", views.SwitchPortView),
router.register_view(r"switchs/role", views.RoleView),
# Reminder
router.register_view(r"reminder/get-users", views.ReminderView),
# DNS
router.register_view(r"dns/zones", views.DNSZonesView),
router.register_view(r"dns/reverse-zones", views.DNSReverseZonesView),
# MAILING
router.register_view(r"mailing/standard", views.StandardMailingView),
router.register_view(r"mailing/club", views.ClubMailingView),
# TOKEN AUTHENTICATION # TOKEN AUTHENTICATION
router.register_view(r"token-auth", views.ObtainExpiringAuthToken) router.register_view(r"token-auth", views.ObtainExpiringAuthToken)

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
@ -30,752 +30,17 @@ import datetime
from django.conf import settings from django.conf import settings
from django.db.models import Q from django.db.models import Q
from django.contrib.auth.models import Group
from rest_framework import viewsets, generics, views from rest_framework import viewsets, generics, views
from rest_framework.authtoken.models import Token from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.response import Response from rest_framework.response import Response
import cotisations.models as cotisations
import machines.models as machines
import preferences.models as preferences
import topologie.models as topologie
import users.models as users
from re2o.utils import all_active_interfaces, all_has_access
from . import serializers from . import serializers
from .pagination import PageSizedPagination from .pagination import PageSizedPagination
from .permissions import ACLPermission from .permissions import ACLPermission
# COTISATIONS
class FactureViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Facture` objects.
"""
queryset = cotisations.Facture.objects.all()
serializer_class = serializers.FactureSerializer
class FactureViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Facture` objects.
"""
queryset = cotisations.BaseInvoice.objects.all()
serializer_class = serializers.BaseInvoiceSerializer
class VenteViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Vente` objects.
"""
queryset = cotisations.Vente.objects.all()
serializer_class = serializers.VenteSerializer
class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Article` objects.
"""
queryset = cotisations.Article.objects.all()
serializer_class = serializers.ArticleSerializer
class BanqueViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Banque` objects.
"""
queryset = cotisations.Banque.objects.all()
serializer_class = serializers.BanqueSerializer
class PaiementViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Paiement` objects.
"""
queryset = cotisations.Paiement.objects.all()
serializer_class = serializers.PaiementSerializer
class CotisationViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Cotisation` objects.
"""
queryset = cotisations.Cotisation.objects.all()
serializer_class = serializers.CotisationSerializer
# MACHINES
class MachineViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Machine` objects.
"""
queryset = machines.Machine.objects.all()
serializer_class = serializers.MachineSerializer
class MachineTypeViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.MachineType` objects.
"""
queryset = machines.MachineType.objects.all()
serializer_class = serializers.MachineTypeSerializer
class IpTypeViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.IpType` objects.
"""
queryset = machines.IpType.objects.all()
serializer_class = serializers.IpTypeSerializer
class VlanViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Vlan` objects.
"""
queryset = machines.Vlan.objects.all()
serializer_class = serializers.VlanSerializer
class NasViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Nas` objects.
"""
queryset = machines.Nas.objects.all()
serializer_class = serializers.NasSerializer
class SOAViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.SOA` objects.
"""
queryset = machines.SOA.objects.all()
serializer_class = serializers.SOASerializer
class ExtensionViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Extension` objects.
"""
queryset = machines.Extension.objects.all()
serializer_class = serializers.ExtensionSerializer
class MxViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Mx` objects.
"""
queryset = machines.Mx.objects.all()
serializer_class = serializers.MxSerializer
class NsViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Ns` objects.
"""
queryset = machines.Ns.objects.all()
serializer_class = serializers.NsSerializer
class TxtViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Txt` objects.
"""
queryset = machines.Txt.objects.all()
serializer_class = serializers.TxtSerializer
class DNameViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.DName` objects.
"""
queryset = machines.DName.objects.all()
serializer_class = serializers.DNameSerializer
class SrvViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Srv` objects.
"""
queryset = machines.Srv.objects.all()
serializer_class = serializers.SrvSerializer
class SshFpViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.SshFp` objects.
"""
queryset = machines.SshFp.objects.all()
serializer_class = serializers.SshFpSerializer
class InterfaceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Interface` objects.
"""
queryset = machines.Interface.objects.all()
serializer_class = serializers.InterfaceSerializer
class Ipv6ListViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Ipv6List` objects.
"""
queryset = machines.Ipv6List.objects.all()
serializer_class = serializers.Ipv6ListSerializer
class DomainViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Domain` objects.
"""
queryset = machines.Domain.objects.all()
serializer_class = serializers.DomainSerializer
class IpListViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.IpList` objects.
"""
queryset = machines.IpList.objects.all()
serializer_class = serializers.IpListSerializer
class ServiceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Service` objects.
"""
queryset = machines.Service.objects.all()
serializer_class = serializers.ServiceSerializer
class ServiceLinkViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Service_link` objects.
"""
queryset = machines.Service_link.objects.all()
serializer_class = serializers.ServiceLinkSerializer
class OuverturePortListViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.OuverturePortList`
objects.
"""
queryset = machines.OuverturePortList.objects.all()
serializer_class = serializers.OuverturePortListSerializer
class OuverturePortViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.OuverturePort` objects.
"""
queryset = machines.OuverturePort.objects.all()
serializer_class = serializers.OuverturePortSerializer
class RoleViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `machines.models.Machine` objects.
"""
queryset = machines.Role.objects.all()
serializer_class = serializers.RoleSerializer
# PREFERENCES
# Those views differ a bit because there is only one object
# to display, so we don't bother with the listing part
class OptionalUserView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.` settings.
"""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.OptionalUser.can_view_all]}
serializer_class = serializers.OptionalUserSerializer
def get_object(self):
return preferences.OptionalUser.objects.first()
class OptionalMachineView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalMachine` settings.
"""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.OptionalMachine.can_view_all]}
serializer_class = serializers.OptionalMachineSerializer
def get_object(self):
return preferences.OptionalMachine.objects.first()
class OptionalTopologieView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalTopologie` settings.
"""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.OptionalTopologie.can_view_all]}
serializer_class = serializers.OptionalTopologieSerializer
def get_object(self):
return preferences.OptionalTopologie.objects.first()
class RadiusOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.OptionalTopologie` settings.
"""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.RadiusOption.can_view_all]}
serializer_class = serializers.RadiusOptionSerializer
def get_object(self):
return preferences.RadiusOption.objects.first()
class GeneralOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.GeneralOption` settings.
"""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.GeneralOption.can_view_all]}
serializer_class = serializers.GeneralOptionSerializer
def get_object(self):
return preferences.GeneralOption.objects.first()
class HomeServiceViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `preferences.models.Service` objects.
"""
queryset = preferences.Service.objects.all()
serializer_class = serializers.HomeServiceSerializer
class AssoOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.AssoOption` settings.
"""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.AssoOption.can_view_all]}
serializer_class = serializers.AssoOptionSerializer
def get_object(self):
return preferences.AssoOption.objects.first()
class HomeOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.HomeOption` settings.
"""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.HomeOption.can_view_all]}
serializer_class = serializers.HomeOptionSerializer
def get_object(self):
return preferences.HomeOption.objects.first()
class MailMessageOptionView(generics.RetrieveAPIView):
"""Exposes details of `preferences.models.MailMessageOption` settings.
"""
permission_classes = (ACLPermission,)
perms_map = {"GET": [preferences.MailMessageOption.can_view_all]}
serializer_class = serializers.MailMessageOptionSerializer
def get_object(self):
return preferences.MailMessageOption.objects.first()
# TOPOLOGIE
class StackViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Stack` objects.
"""
queryset = topologie.Stack.objects.all()
serializer_class = serializers.StackSerializer
class AccessPointViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.AccessPoint` objects.
"""
queryset = topologie.AccessPoint.objects.all()
serializer_class = serializers.AccessPointSerializer
class SwitchViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Switch` objects.
"""
queryset = topologie.Switch.objects.all()
serializer_class = serializers.SwitchSerializer
class ServerViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Server` objects.
"""
queryset = topologie.Server.objects.all()
serializer_class = serializers.ServerSerializer
class ModelSwitchViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.ModelSwitch` objects.
"""
queryset = topologie.ModelSwitch.objects.all()
serializer_class = serializers.ModelSwitchSerializer
class ConstructorSwitchViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.ConstructorSwitch`
objects.
"""
queryset = topologie.ConstructorSwitch.objects.all()
serializer_class = serializers.ConstructorSwitchSerializer
class SwitchBayViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.SwitchBay` objects.
"""
queryset = topologie.SwitchBay.objects.all()
serializer_class = serializers.SwitchBaySerializer
class BuildingViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Building` objects.
"""
queryset = topologie.Building.objects.all()
serializer_class = serializers.BuildingSerializer
class SwitchPortViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Port` objects.
"""
queryset = topologie.Port.objects.all()
serializer_class = serializers.SwitchPortSerializer
class PortProfileViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.PortProfile` objects.
"""
queryset = topologie.PortProfile.objects.all()
serializer_class = serializers.PortProfileSerializer
class RoomViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.Room` objects.
"""
queryset = topologie.Room.objects.all()
serializer_class = serializers.RoomSerializer
class PortProfileViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `topologie.models.PortProfile` objects.
"""
queryset = topologie.PortProfile.objects.all()
serializer_class = serializers.PortProfileSerializer
# USER
class UserViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.Users` objects.
"""
queryset = users.User.objects.all()
serializer_class = serializers.UserSerializer
class HomeCreationViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes infos of `users.models.Users` objects to create homes.
"""
queryset = users.User.objects.exclude(
Q(state=users.User.STATE_DISABLED)
| Q(state=users.User.STATE_NOT_YET_ACTIVE)
| Q(state=users.User.STATE_FULL_ARCHIVE)
)
serializer_class = serializers.BasicUserSerializer
class NormalUserViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes infos of `users.models.Users`without specific rights objects."""
queryset = users.User.objects.exclude(groups__listright__critical=True).distinct()
serializer_class = serializers.BasicUserSerializer
class CriticalUserViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes infos of `users.models.Users`without specific rights objects."""
queryset = users.User.objects.filter(groups__listright__critical=True).distinct()
serializer_class = serializers.BasicUserSerializer
class ClubViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.Club` objects.
"""
queryset = users.Club.objects.all()
serializer_class = serializers.ClubSerializer
class AdherentViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.Adherent` objects.
"""
queryset = users.Adherent.objects.all()
serializer_class = serializers.AdherentSerializer
class ServiceUserViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.ServiceUser` objects.
"""
queryset = users.ServiceUser.objects.all()
serializer_class = serializers.ServiceUserSerializer
class SchoolViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.School` objects.
"""
queryset = users.School.objects.all()
serializer_class = serializers.SchoolSerializer
class ListRightViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.ListRight` objects.
"""
queryset = users.ListRight.objects.all()
serializer_class = serializers.ListRightSerializer
class ShellViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.ListShell` objects.
"""
queryset = users.ListShell.objects.all()
serializer_class = serializers.ShellSerializer
class BanViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.Ban` objects.
"""
queryset = users.Ban.objects.all()
serializer_class = serializers.BanSerializer
class WhitelistViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.Whitelist` objects.
"""
queryset = users.Whitelist.objects.all()
serializer_class = serializers.WhitelistSerializer
class EMailAddressViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `users.models.EMailAddress` objects.
"""
serializer_class = serializers.EMailAddressSerializer
queryset = users.EMailAddress.objects.none()
def get_queryset(self):
if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"):
return users.EMailAddress.objects.filter(user__local_email_enabled=True)
else:
return users.EMailAddress.objects.none()
# SERVICE REGEN
class ServiceRegenViewSet(viewsets.ModelViewSet):
"""Exposes list and details of the services to regen
"""
serializer_class = serializers.ServiceRegenSerializer
def get_queryset(self):
queryset = machines.Service_link.objects.select_related(
"server__domain"
).select_related("service")
if "hostname" in self.request.GET:
hostname = self.request.GET["hostname"]
queryset = queryset.filter(server__domain__name__iexact=hostname)
return queryset
# Config des switches
class SwitchPortView(generics.ListAPIView):
"""Output each port of a switch, to be serialized with
additionnal informations (profiles etc)
"""
queryset = (
topologie.Switch.objects.all()
.select_related("switchbay")
.select_related("model__constructor")
.prefetch_related("ports__custom_profile__vlan_tagged")
.prefetch_related("ports__custom_profile__vlan_untagged")
.prefetch_related("ports__machine_interface__domain__extension")
.prefetch_related("ports__room")
)
serializer_class = serializers.SwitchPortSerializer
# Rappel fin adhésion
class ReminderView(generics.ListAPIView):
"""Output for users to remind an end of their subscription.
"""
queryset = preferences.Reminder.objects.all()
serializer_class = serializers.ReminderSerializer
class RoleView(generics.ListAPIView):
"""Output of roles for each server
"""
queryset = machines.Role.objects.all().prefetch_related("servers")
serializer_class = serializers.RoleSerializer
# LOCAL EMAILS
class LocalEmailUsersView(generics.ListAPIView):
"""Exposes all the aliases of the users that activated the internal address
"""
serializer_class = serializers.LocalEmailUsersSerializer
def get_queryset(self):
if preferences.OptionalUser.get_cached_value("local_email_accounts_enabled"):
return users.User.objects.filter(local_email_enabled=True)
else:
return users.User.objects.none()
# DHCP
class HostMacIpView(generics.ListAPIView):
"""Exposes the associations between hostname, mac address and IPv4 in
order to build the DHCP lease files.
"""
serializer_class = serializers.HostMacIpSerializer
def get_queryset(self):
return all_active_interfaces()
# Firewall
class SubnetPortsOpenView(generics.ListAPIView):
queryset = machines.IpType.objects.all()
serializer_class = serializers.SubnetPortsOpenSerializer
class InterfacePortsOpenView(generics.ListAPIView):
queryset = machines.Interface.objects.filter(port_lists__isnull=False).distinct()
serializer_class = serializers.InterfacePortsOpenSerializer
# DNS
class DNSZonesView(generics.ListAPIView):
"""Exposes the detailed information about each extension (hostnames,
IPs, DNS records, etc.) in order to build the DNS zone files.
"""
queryset = (
machines.Extension.objects.prefetch_related("soa")
.prefetch_related("ns_set")
.prefetch_related("ns_set__ns")
.prefetch_related("origin")
.prefetch_related("mx_set")
.prefetch_related("mx_set__name")
.prefetch_related("txt_set")
.prefetch_related("srv_set")
.prefetch_related("srv_set__target")
.all()
)
serializer_class = serializers.DNSZonesSerializer
class DNSReverseZonesView(generics.ListAPIView):
"""Exposes the detailed information about each extension (hostnames,
IPs, DNS records, etc.) in order to build the DNS zone files.
"""
queryset = machines.IpType.objects.all()
serializer_class = serializers.DNSReverseZonesSerializer
# MAILING
class StandardMailingView(views.APIView):
"""Exposes list and details of standard mailings (name and members) in
order to building the corresponding mailing lists.
"""
pagination_class = PageSizedPagination
permission_classes = (ACLPermission,)
perms_map = {"GET": [users.User.can_view_all]}
def get(self, request, format=None):
adherents_data = serializers.MailingMemberSerializer(
all_has_access(), many=True
).data
data = [{"name": "adherents", "members": adherents_data}]
paginator = self.pagination_class()
paginator.paginate_queryset(data, request)
return paginator.get_paginated_response(data)
class ClubMailingView(generics.ListAPIView):
"""Exposes list and details of club mailings (name, members and admins) in
order to build the corresponding mailing lists.
"""
queryset = users.Club.objects.all()
serializer_class = serializers.MailingSerializer
# TOKEN AUTHENTICATION
class ObtainExpiringAuthToken(ObtainAuthToken): class ObtainExpiringAuthToken(ObtainAuthToken):
"""Exposes a view to obtain a authentication token. """Exposes a view to obtain a authentication token.

View file

@ -6,7 +6,8 @@ python3-djangorestframework
python3-django-reversion python3-django-reversion
python3-pip python3-pip
python3-pil python3-pil
libsasl2-dev libldap2-dev libsasl2-dev
libldap2-dev
libssl-dev libssl-dev
python3-crypto python3-crypto
python3-git python3-git

View file

@ -1,14 +1,16 @@
python-django python3-django
python-dateutil python3-dateutil
texlive-latex-base texlive-latex-base
texlive-fonts-recommended texlive-fonts-recommended
python-djangorestframework python3-djangorestframework
python-django-reversion python3-django-reversion
python-pip python3-pip
libsasl2-dev libldap2-dev python3-pil
libsasl2-dev
libldap2-dev
libssl-dev libssl-dev
python-crypto python3-crypto
python-git python3-git
javascript-common javascript-common
libjs-jquery libjs-jquery
libjs-jquery-ui libjs-jquery-ui
@ -18,6 +20,11 @@ fonts-font-awesome
graphviz graphviz
git git
gettext gettext
libfreeradius3=3.0.21+dfsg-1~bpo10+1
freeradius-utils=3.0.21+dfsg-1~bpo10+1
freeradius=3.0.21+dfsg-1~bpo10+1
freeradius-common freeradius-common
freeradius-python2 freeradius-python3
python-mysqldb python3-dev
python3-mysqldb
python3-django-ldapdb

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
@ -28,7 +28,7 @@ Here are defined some functions to check acl on the application.
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
def can_view(user): def can_view(user, *args, **kwargs):
"""Check if an user can view the application. """Check if an user can view the application.
Args: Args:

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
@ -34,7 +34,7 @@ from .models import CustomInvoice, CostEstimate
class FactureAdmin(VersionAdmin): class FactureAdmin(VersionAdmin):
"""Class admin d'une facture, tous les champs""" """Admin class for invoices."""
pass pass
@ -52,32 +52,31 @@ class CustomInvoiceAdmin(VersionAdmin):
class VenteAdmin(VersionAdmin): class VenteAdmin(VersionAdmin):
"""Class admin d'une vente, tous les champs (facture related)""" """Admin class for purchases."""
pass pass
class ArticleAdmin(VersionAdmin): class ArticleAdmin(VersionAdmin):
"""Class admin d'un article en vente""" """Admin class for articles."""
pass pass
class BanqueAdmin(VersionAdmin): class BanqueAdmin(VersionAdmin):
"""Class admin de la liste des banques (facture related)""" """Admin class for banks."""
pass pass
class PaiementAdmin(VersionAdmin): class PaiementAdmin(VersionAdmin):
"""Class admin d'un moyen de paiement (facture related""" """Admin class for payment methods."""
pass pass
class CotisationAdmin(VersionAdmin): class CotisationAdmin(VersionAdmin):
"""Class admin d'une cotisation (date de debut et de fin), """Admin class for subscriptions."""
Vente related"""
pass pass

View file

View file

@ -0,0 +1,129 @@
# -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
# Copyright © 2018 Maël Kervella
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from rest_framework import serializers
import cotisations.models as cotisations
import preferences.models as preferences
from api.serializers import NamespacedHRField, NamespacedHIField, NamespacedHMSerializer
from users.api.serializers import UserSerializer
class FactureSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Facture` objects.
"""
class Meta:
model = cotisations.Facture
fields = (
"user",
"paiement",
"banque",
"cheque",
"date",
"valid",
"control",
"prix_total",
"name",
"api_url",
)
class BaseInvoiceSerializer(NamespacedHMSerializer):
class Meta:
model = cotisations.BaseInvoice
fields = "__all__"
class VenteSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Vente` objects.
"""
class Meta:
model = cotisations.Vente
fields = (
"facture",
"number",
"name",
"prix",
"duration_connection",
"duration_days_connection",
"duration_membership",
"duration_days_membership",
"prix_total",
"api_url",
)
class ArticleSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Article` objects.
"""
class Meta:
model = cotisations.Article
fields = ("name", "prix", "duration_membership", "duration_days_membership", "duration_connection", "duration_days_connection", "type_user", "api_url")
class BanqueSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Banque` objects.
"""
class Meta:
model = cotisations.Banque
fields = ("name", "api_url")
class PaiementSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Paiement` objects.
"""
class Meta:
model = cotisations.Paiement
fields = ("moyen", "api_url")
class CotisationSerializer(NamespacedHMSerializer):
"""Serialize `cotisations.models.Cotisation` objects.
"""
class Meta:
model = cotisations.Cotisation
fields = ("vente", "type_cotisation", "date_start_con", "date_end_con", "date_start_memb", "date_end_memb", "api_url")
class ReminderUsersSerializer(UserSerializer):
"""Serialize the data about a mailing member.
"""
class Meta(UserSerializer.Meta):
fields = ("get_full_name", "get_mail")
class ReminderSerializer(serializers.ModelSerializer):
"""
Serialize the data about a reminder
"""
users_to_remind = ReminderUsersSerializer(many=True)
class Meta:
model = preferences.Reminder
fields = ("days", "message", "users_to_remind")

38
cotisations/api/urls.py Normal file
View file

@ -0,0 +1,38 @@
# -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
# Copyright © 2018 Maël Kervella
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from . import views
urls_viewset = [
(r"cotisations/facture", views.FactureViewSet, None),
(r"cotisations/vente", views.VenteViewSet, None),
(r"cotisations/article", views.ArticleViewSet, None),
(r"cotisations/banque", views.BanqueViewSet, None),
(r"cotisations/paiement", views.PaiementViewSet, None),
(r"cotisations/cotisation", views.CotisationViewSet, None)
]
urls_view = [
(r"cotisations/reminder-get-users", views.ReminderView),
# Deprecated
(r"reminder/get-users", views.ReminderView),
]

89
cotisations/api/views.py Normal file
View file

@ -0,0 +1,89 @@
# -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics.
#
# Copyright © 2018 Maël Kervella
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
from rest_framework import viewsets, generics
from . import serializers
import cotisations.models as cotisations
import preferences.models as preferences
class FactureViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Facture` objects.
"""
queryset = cotisations.Facture.objects.all()
serializer_class = serializers.FactureSerializer
class FactureViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Facture` objects.
"""
queryset = cotisations.BaseInvoice.objects.all()
serializer_class = serializers.BaseInvoiceSerializer
class VenteViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Vente` objects.
"""
queryset = cotisations.Vente.objects.all()
serializer_class = serializers.VenteSerializer
class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Article` objects.
"""
queryset = cotisations.Article.objects.all()
serializer_class = serializers.ArticleSerializer
class BanqueViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Banque` objects.
"""
queryset = cotisations.Banque.objects.all()
serializer_class = serializers.BanqueSerializer
class PaiementViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Paiement` objects.
"""
queryset = cotisations.Paiement.objects.all()
serializer_class = serializers.PaiementSerializer
class CotisationViewSet(viewsets.ReadOnlyModelViewSet):
"""Exposes list and details of `cotisations.models.Cotisation` objects.
"""
queryset = cotisations.Cotisation.objects.all()
serializer_class = serializers.CotisationSerializer
class ReminderView(generics.ListAPIView):
"""Output for users to remind an end of their subscription.
"""
queryset = preferences.Reminder.objects.all()
serializer_class = serializers.ReminderSerializer

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
@ -46,6 +46,7 @@ from django.shortcuts import get_object_or_404
from re2o.field_permissions import FieldPermissionFormMixin from re2o.field_permissions import FieldPermissionFormMixin
from re2o.mixins import FormRevMixin from re2o.mixins import FormRevMixin
from re2o.widgets import AutocompleteModelWidget
from .models import ( from .models import (
Article, Article,
Paiement, Paiement,
@ -79,6 +80,10 @@ class FactureForm(FieldPermissionFormMixin, FormRevMixin, ModelForm):
class Meta: class Meta:
model = Facture model = Facture
fields = "__all__" fields = "__all__"
widgets = {
"user": AutocompleteModelWidget(url="/users/user-autocomplete"),
"banque": AutocompleteModelWidget(url="/cotisations/banque-autocomplete"),
}
def clean(self): def clean(self):
cleaned_data = super(FactureForm, self).clean() cleaned_data = super(FactureForm, self).clean()

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
@ -21,9 +21,9 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 2.5\n" "Project-Id-Version: 2.5\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-11-20 01:24+0100\n" "POT-Creation-Date: 2021-01-24 11:51+0100\n"
"PO-Revision-Date: 2018-03-31 16:09+0002\n" "PO-Revision-Date: 2018-03-31 16:09+0002\n"
"Last-Translator: Laouen Fernet <laouen.fernet@supelec.fr>\n" "Last-Translator: Yoann Piétri <me@nanoy.fr>\n"
"Language: fr_FR\n" "Language: fr_FR\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -33,83 +33,83 @@ msgstr ""
msgid "You don't have the right to view this application." msgid "You don't have the right to view this application."
msgstr "Vous n'avez pas le droit de voir cette application." msgstr "Vous n'avez pas le droit de voir cette application."
#: cotisations/forms.py:70 cotisations/forms.py:296 #: cotisations/forms.py:71 cotisations/forms.py:301
msgid "Select a payment method" msgid "Select a payment method"
msgstr "Sélectionnez un moyen de paiement" msgstr "Sélectionnez un moyen de paiement"
#: cotisations/forms.py:73 cotisations/models.py:676 #: cotisations/forms.py:74 cotisations/models.py:706
msgid "Member" msgid "Member"
msgstr "Adhérent" msgstr "Adhérent"
#: cotisations/forms.py:74 #: cotisations/forms.py:75
msgid "Select the proprietary member" msgid "Select the proprietary member"
msgstr "Sélectionnez l'adhérent propriétaire" msgstr "Sélectionnez l'adhérent propriétaire"
#: cotisations/forms.py:75 #: cotisations/forms.py:76
msgid "Validated invoice" msgid "Validated invoice"
msgstr "Facture validée" msgstr "Facture validée"
#: cotisations/forms.py:87 #: cotisations/forms.py:92
msgid "A payment method must be specified." msgid "A payment method must be specified."
msgstr "Un moyen de paiement doit être renseigné." msgstr "Un moyen de paiement doit être renseigné."
#: cotisations/forms.py:98 #: cotisations/forms.py:103
#: cotisations/templates/cotisations/aff_article.html:33 #: cotisations/templates/cotisations/aff_article.html:33
#: cotisations/templates/cotisations/facture.html:64 #: cotisations/templates/cotisations/facture.html:64
msgid "Article" msgid "Article"
msgstr "Article" msgstr "Article"
#: cotisations/forms.py:101 #: cotisations/forms.py:106
#: cotisations/templates/cotisations/edit_facture.html:50 #: cotisations/templates/cotisations/edit_facture.html:50
msgid "Quantity" msgid "Quantity"
msgstr "Quantité" msgstr "Quantité"
#: cotisations/forms.py:119 #: cotisations/forms.py:124
msgid "Discount is in percentage." msgid "Discount is in percentage."
msgstr "La réduction est en pourcentage." msgstr "La réduction est en pourcentage."
#: cotisations/forms.py:122 cotisations/templates/cotisations/facture.html:75 #: cotisations/forms.py:127 cotisations/templates/cotisations/facture.html:75
msgid "Discount" msgid "Discount"
msgstr "Réduction" msgstr "Réduction"
#: cotisations/forms.py:139 #: cotisations/forms.py:144
#, python-format #, python-format
msgid "{}% discount" msgid "{}% discount"
msgstr "{}% de réduction" msgstr "{}% de réduction"
#: cotisations/forms.py:139 #: cotisations/forms.py:144
msgid "{} € discount" msgid "{} € discount"
msgstr "{} € de réduction" msgstr "{} € de réduction"
#: cotisations/forms.py:176 #: cotisations/forms.py:181
msgid "Article name" msgid "Article name"
msgstr "Nom de l'article" msgstr "Nom de l'article"
#: cotisations/forms.py:187 #: cotisations/forms.py:192
msgid "Current articles" msgid "Current articles"
msgstr "Articles actuels" msgstr "Articles actuels"
#: cotisations/forms.py:216 #: cotisations/forms.py:221
msgid "Payment method name" msgid "Payment method name"
msgstr "Nom du moyen de paiement" msgstr "Nom du moyen de paiement"
#: cotisations/forms.py:229 #: cotisations/forms.py:234
msgid "Current payment methods" msgid "Current payment methods"
msgstr "Moyens de paiement actuels" msgstr "Moyens de paiement actuels"
#: cotisations/forms.py:256 #: cotisations/forms.py:261
msgid "Bank name" msgid "Bank name"
msgstr "Nom de la banque" msgstr "Nom de la banque"
#: cotisations/forms.py:269 #: cotisations/forms.py:274
msgid "Current banks" msgid "Current banks"
msgstr "Banques actuelles" msgstr "Banques actuelles"
#: cotisations/forms.py:288 #: cotisations/forms.py:293
msgid "Amount" msgid "Amount"
msgstr "Montant" msgstr "Montant"
#: cotisations/forms.py:290 #: cotisations/forms.py:295
#: cotisations/templates/cotisations/aff_cost_estimate.html:42 #: cotisations/templates/cotisations/aff_cost_estimate.html:42
#: cotisations/templates/cotisations/aff_cotisations.html:44 #: cotisations/templates/cotisations/aff_cotisations.html:44
#: cotisations/templates/cotisations/aff_custom_invoice.html:42 #: cotisations/templates/cotisations/aff_custom_invoice.html:42
@ -117,7 +117,7 @@ msgstr "Montant"
msgid "Payment method" msgid "Payment method"
msgstr "Moyen de paiement" msgstr "Moyen de paiement"
#: cotisations/forms.py:314 #: cotisations/forms.py:319
#, python-format #, python-format
msgid "" msgid ""
"Requested amount is too high. Your balance can't exceed " "Requested amount is too high. Your balance can't exceed "
@ -126,52 +126,56 @@ msgstr ""
"Le montant demandé est trop grand. Votre solde ne peut excéder " "Le montant demandé est trop grand. Votre solde ne peut excéder "
"%(max_online_balance)s €." "%(max_online_balance)s €."
#: cotisations/models.py:59 #: cotisations/models.py:61
msgid "date" msgid "date"
msgstr "date" msgstr "date"
#: cotisations/models.py:130 #: cotisations/models.py:66
msgid "Can view an base invoice object"
msgstr "Peut voir un objet BaseInvoice"
#: cotisations/models.py:148
msgid "cheque number" msgid "cheque number"
msgstr "numéro de chèque" msgstr "numéro de chèque"
#: cotisations/models.py:133 #: cotisations/models.py:151
msgid "validated" msgid "validated"
msgstr "validé" msgstr "validé"
#: cotisations/models.py:135 #: cotisations/models.py:153
msgid "controlled" msgid "controlled"
msgstr "contrôlé" msgstr "contrôlé"
#: cotisations/models.py:141 #: cotisations/models.py:159
msgid "Can edit the \"controlled\" state" msgid "Can edit the \"controlled\" state"
msgstr "Peut modifier l'état \"contrôlé\"" msgstr "Peut modifier l'état \"contrôlé\""
#: cotisations/models.py:142 #: cotisations/models.py:160
msgid "Can view an invoice object" msgid "Can view an invoice object"
msgstr "Peut voir un objet facture" msgstr "Peut voir un objet facture"
#: cotisations/models.py:143 #: cotisations/models.py:161
msgid "Can edit all the previous invoices" msgid "Can edit all the previous invoices"
msgstr "Peut modifier toutes les factures précédentes" msgstr "Peut modifier toutes les factures précédentes"
#: cotisations/models.py:145 cotisations/models.py:431 cotisations/views.py:378 #: cotisations/models.py:163 cotisations/models.py:458 cotisations/views.py:380
#: cotisations/views.py:573 #: cotisations/views.py:575
msgid "invoice" msgid "invoice"
msgstr "facture" msgstr "facture"
#: cotisations/models.py:146 #: cotisations/models.py:164
msgid "invoices" msgid "invoices"
msgstr "factures" msgstr "factures"
#: cotisations/models.py:160 #: cotisations/models.py:178
msgid "You don't have the right to edit an invoice." msgid "You don't have the right to edit an invoice."
msgstr "Vous n'avez pas le droit de modifier une facture." msgstr "Vous n'avez pas le droit de modifier une facture."
#: cotisations/models.py:168 #: cotisations/models.py:186
msgid "You don't have the right to edit this user's invoices." msgid "You don't have the right to edit this user's invoices."
msgstr "Vous n'avez pas le droit de modifier les factures de cet utilisateur." msgstr "Vous n'avez pas le droit de modifier les factures de cet utilisateur."
#: cotisations/models.py:177 #: cotisations/models.py:195
msgid "" msgid ""
"You don't have the right to edit an invoice already controlled or " "You don't have the right to edit an invoice already controlled or "
"invalidated." "invalidated."
@ -179,15 +183,15 @@ msgstr ""
"Vous n'avez pas le droit de modifier une facture précédemment contrôlée ou " "Vous n'avez pas le droit de modifier une facture précédemment contrôlée ou "
"invalidée." "invalidée."
#: cotisations/models.py:192 #: cotisations/models.py:210
msgid "You don't have the right to delete an invoice." msgid "You don't have the right to delete an invoice."
msgstr "Vous n'avez pas le droit de supprimer une facture." msgstr "Vous n'avez pas le droit de supprimer une facture."
#: cotisations/models.py:200 #: cotisations/models.py:218
msgid "You don't have the right to delete this user's invoices." msgid "You don't have the right to delete this user's invoices."
msgstr "Vous n'avez pas le droit de supprimer les factures de cet utilisateur." msgstr "Vous n'avez pas le droit de supprimer les factures de cet utilisateur."
#: cotisations/models.py:209 #: cotisations/models.py:227
msgid "" msgid ""
"You don't have the right to delete an invoice already controlled or " "You don't have the right to delete an invoice already controlled or "
"invalidated." "invalidated."
@ -195,137 +199,123 @@ msgstr ""
"Vous n'avez pas le droit de supprimer une facture précédemment contrôlée ou " "Vous n'avez pas le droit de supprimer une facture précédemment contrôlée ou "
"invalidée." "invalidée."
#: cotisations/models.py:223 #: cotisations/models.py:241
msgid "You don't have the right to view someone else's invoices history." msgid "You don't have the right to view someone else's invoices history."
msgstr "" msgstr ""
"Vous n'avez pas le droit de voir l'historique des factures d'un autre " "Vous n'avez pas le droit de voir l'historique des factures d'un autre "
"utilisateur." "utilisateur."
#: cotisations/models.py:231 #: cotisations/models.py:249
msgid "The invoice has been invalidated." msgid "The invoice has been invalidated."
msgstr "La facture a été invalidée." msgstr "La facture a été invalidée."
#: cotisations/models.py:246 #: cotisations/models.py:264
msgid "You don't have the right to edit the \"controlled\" state." msgid "You don't have the right to edit the \"controlled\" state."
msgstr "Vous n'avez pas le droit de modifier le statut \"contrôlé\"." msgstr "Vous n'avez pas le droit de modifier le statut \"contrôlé\"."
#: cotisations/models.py:265 #: cotisations/models.py:283
msgid "There are no payment methods that you can use." msgid "There are no payment methods that you can use."
msgstr "Il n'y a pas de moyens de paiement que vous puissiez utiliser." msgstr "Il n'y a pas de moyens de paiement que vous puissiez utiliser."
#: cotisations/models.py:271 #: cotisations/models.py:289
msgid "There are no articles that you can buy." msgid "There are no articles that you can buy."
msgstr "Il n'y a pas d'articles que vous puissiez acheter." msgstr "Il n'y a pas d'articles que vous puissiez acheter."
#: cotisations/models.py:347 #: cotisations/models.py:381
msgid "Can view a custom invoice object" msgid "Can view a custom invoice object"
msgstr "Peut voir un objet facture personnalisée" msgstr "Peut voir un objet facture personnalisée"
#: cotisations/models.py:349 #: cotisations/models.py:383
msgid "recipient" msgid "recipient"
msgstr "destinataire" msgstr "destinataire"
#: cotisations/models.py:350 #: cotisations/models.py:384
msgid "payment type" msgid "payment type"
msgstr "type de paiement" msgstr "type de paiement"
#: cotisations/models.py:351 #: cotisations/models.py:385
msgid "address" msgid "address"
msgstr "adresse" msgstr "adresse"
#: cotisations/models.py:352 #: cotisations/models.py:386
msgid "paid" msgid "paid"
msgstr "payé" msgstr "payé"
#: cotisations/models.py:353 #: cotisations/models.py:387
msgid "remark" msgid "remark"
msgstr "remarque" msgstr "remarque"
#: cotisations/models.py:358 #: cotisations/models.py:392
msgid "Can view a cost estimate object" msgid "Can view a cost estimate object"
msgstr "Peut voir un objet devis" msgstr "Peut voir un objet devis"
#: cotisations/models.py:361 #: cotisations/models.py:395
msgid "period of validity" msgid "period of validity"
msgstr "période de validité" msgstr "période de validité"
#: cotisations/models.py:396 #: cotisations/models.py:430
msgid "You don't have the right to delete a cost estimate." msgid "You don't have the right to delete a cost estimate."
msgstr "Vous n'avez pas le droit de supprimer un devis." msgstr "Vous n'avez pas le droit de supprimer un devis."
#: cotisations/models.py:402 #: cotisations/models.py:436
msgid "The cost estimate has an invoice and can't be deleted." msgid "The cost estimate has an invoice and can't be deleted."
msgstr "Le devis a une facture et ne peut pas être supprimé." msgstr "Le devis a une facture et ne peut pas être supprimé."
#: cotisations/models.py:424 cotisations/models.py:682 #: cotisations/models.py:462
#: cotisations/models.py:940
msgid "Connection"
msgstr "Connexion"
#: cotisations/models.py:425 cotisations/models.py:683
#: cotisations/models.py:941
msgid "Membership"
msgstr "Adhésion"
#: cotisations/models.py:426 cotisations/models.py:678
#: cotisations/models.py:684 cotisations/models.py:942
msgid "Both of them"
msgstr "Les deux"
#: cotisations/models.py:435
msgid "amount" msgid "amount"
msgstr "montant" msgstr "montant"
#: cotisations/models.py:438 #: cotisations/models.py:467
msgid "article" msgid "article"
msgstr "article" msgstr "article"
#: cotisations/models.py:441 #: cotisations/models.py:470
msgid "price" msgid "price"
msgstr "prix" msgstr "prix"
#: cotisations/models.py:444 cotisations/models.py:696 #: cotisations/models.py:473 cotisations/models.py:726
msgid "duration (in months)" msgid "duration of the connection (in months)"
msgstr "durée (en mois)" msgstr "durée de la connexion (en mois)"
#: cotisations/models.py:450 cotisations/models.py:702 #: cotisations/models.py:478 cotisations/models.py:730
msgid "duration (in days, will be added to duration in months)" msgid ""
msgstr "durée (en jours, sera ajoutée à la durée en mois)" "duration of the connection (in days, will be added to duration in months)"
msgstr "durée de la connexion (en jours, sera ajoutée à la durée en mois)"
#: cotisations/models.py:458 cotisations/models.py:716 #: cotisations/models.py:482 cotisations/models.py:718
#: cotisations/models.py:953 msgid "duration of the membership (in months)"
msgid "subscription type" msgstr "durée de l'adhésion (en mois)"
msgstr "type de cotisation"
#: cotisations/models.py:463 #: cotisations/models.py:487 cotisations/models.py:722
msgid ""
"duration of the membership (in days, will be added to duration in months)"
msgstr "durée de l'adhésion (en jours, sera ajoutée à la durée en mois)"
#: cotisations/models.py:493
msgid "Can view a purchase object" msgid "Can view a purchase object"
msgstr "Peut voir un objet achat" msgstr "Peut voir un objet achat"
#: cotisations/models.py:464 #: cotisations/models.py:494
msgid "Can edit all the previous purchases" msgid "Can edit all the previous purchases"
msgstr "Peut modifier tous les achats précédents" msgstr "Peut modifier tous les achats précédents"
#: cotisations/models.py:466 cotisations/models.py:947 #: cotisations/models.py:496 cotisations/models.py:970
msgid "purchase" msgid "purchase"
msgstr "achat" msgstr "achat"
#: cotisations/models.py:467 #: cotisations/models.py:497
msgid "purchases" msgid "purchases"
msgstr "achats" msgstr "achats"
#: cotisations/models.py:539 cotisations/models.py:736 #: cotisations/models.py:572
msgid "Duration must be specified for a subscription."
msgstr "La durée doit être renseignée pour une cotisation."
#: cotisations/models.py:550
msgid "You don't have the right to edit a purchase." msgid "You don't have the right to edit a purchase."
msgstr "Vous n'avez pas le droit de modifier un achat." msgstr "Vous n'avez pas le droit de modifier un achat."
#: cotisations/models.py:556 #: cotisations/models.py:578
msgid "You don't have the right to edit this user's purchases." msgid "You don't have the right to edit this user's purchases."
msgstr "Vous n'avez pas le droit de modifier les achats de cet utilisateur." msgstr "Vous n'avez pas le droit de modifier les achats de cet utilisateur."
#: cotisations/models.py:565 #: cotisations/models.py:587
msgid "" msgid ""
"You don't have the right to edit a purchase already controlled or " "You don't have the right to edit a purchase already controlled or "
"invalidated." "invalidated."
@ -333,15 +323,15 @@ msgstr ""
"Vous n'avez pas le droit de modifier un achat précédemment contrôlé ou " "Vous n'avez pas le droit de modifier un achat précédemment contrôlé ou "
"invalidé." "invalidé."
#: cotisations/models.py:580 #: cotisations/models.py:602
msgid "You don't have the right to delete a purchase." msgid "You don't have the right to delete a purchase."
msgstr "Vous n'avez pas le droit de supprimer un achat." msgstr "Vous n'avez pas le droit de supprimer un achat."
#: cotisations/models.py:586 #: cotisations/models.py:608
msgid "You don't have the right to delete this user's purchases." msgid "You don't have the right to delete this user's purchases."
msgstr "Vous n'avez pas le droit de supprimer les achats de cet utilisateur." msgstr "Vous n'avez pas le droit de supprimer les achats de cet utilisateur."
#: cotisations/models.py:593 #: cotisations/models.py:615
msgid "" msgid ""
"You don't have the right to delete a purchase already controlled or " "You don't have the right to delete a purchase already controlled or "
"invalidated." "invalidated."
@ -349,134 +339,150 @@ msgstr ""
"Vous n'avez pas le droit de supprimer un achat précédemment contrôlé ou " "Vous n'avez pas le droit de supprimer un achat précédemment contrôlé ou "
"invalidé." "invalidé."
#: cotisations/models.py:609 #: cotisations/models.py:631
msgid "You don't have the right to view someone else's purchase history." msgid "You don't have the right to view someone else's purchase history."
msgstr "" msgstr ""
"Vous n'avez pas le droit de voir l'historique des achats d'un autre " "Vous n'avez pas le droit de voir l'historique des achats d'un autre "
"utilisateur." "utilisateur."
#: cotisations/models.py:677 #: cotisations/models.py:707
msgid "Club" msgid "Club"
msgstr "Club" msgstr "Club"
#: cotisations/models.py:687 #: cotisations/models.py:708
msgid "Both of them"
msgstr "Les deux"
#: cotisations/models.py:711
msgid "designation" msgid "designation"
msgstr "désignation" msgstr "désignation"
#: cotisations/models.py:690 #: cotisations/models.py:714
msgid "unit price" msgid "unit price"
msgstr "prix unitaire" msgstr "prix unitaire"
#: cotisations/models.py:708 #: cotisations/models.py:736
msgid "need membership to be purchased"
msgstr "nécessite une adhésion pour être acheté"
#: cotisations/models.py:743
msgid "type of users concerned" msgid "type of users concerned"
msgstr "type d'utilisateurs concernés" msgstr "type d'utilisateurs concernés"
#: cotisations/models.py:719 cotisations/models.py:820 #: cotisations/models.py:746 cotisations/models.py:847
msgid "is available for every user" msgid "is available for every user"
msgstr "est disponible pour chaque utilisateur" msgstr "est disponible pour chaque utilisateur"
#: cotisations/models.py:726 #: cotisations/models.py:753
msgid "Can view an article object" msgid "Can view an article object"
msgstr "Peut voir un objet article" msgstr "Peut voir un objet article"
#: cotisations/models.py:727 #: cotisations/models.py:754
msgid "Can buy every article" msgid "Can buy every article"
msgstr "Peut acheter chaque article" msgstr "Peut acheter chaque article"
#: cotisations/models.py:734 #: cotisations/models.py:761
msgid "Solde is a reserved article name." msgid "Solde is a reserved article name."
msgstr "Solde est un nom d'article réservé." msgstr "Solde est un nom d'article réservé."
#: cotisations/models.py:759 #: cotisations/models.py:784
msgid "You can't buy this article." msgid "You can't buy this article."
msgstr "Vous ne pouvez pas acheter cet article." msgstr "Vous ne pouvez pas acheter cet article."
#: cotisations/models.py:800 #: cotisations/models.py:827
msgid "Can view a bank object" msgid "Can view a bank object"
msgstr "Peut voir un objet banque" msgstr "Peut voir un objet banque"
#: cotisations/models.py:801 #: cotisations/models.py:828
msgid "bank" msgid "bank"
msgstr "banque" msgstr "banque"
#: cotisations/models.py:802 #: cotisations/models.py:829
msgid "banks" msgid "banks"
msgstr "banques" msgstr "banques"
#: cotisations/models.py:818 #: cotisations/models.py:845
msgid "method" msgid "method"
msgstr "moyen" msgstr "moyen"
#: cotisations/models.py:825 #: cotisations/models.py:852
msgid "is user balance" msgid "is user balance"
msgstr "est solde utilisateur" msgstr "est solde utilisateur"
#: cotisations/models.py:826 #: cotisations/models.py:853
msgid "There should be only one balance payment method." msgid "There should be only one balance payment method."
msgstr "Il ne devrait y avoir qu'un moyen de paiement solde." msgstr "Il ne devrait y avoir qu'un moyen de paiement solde."
#: cotisations/models.py:832 #: cotisations/models.py:859
msgid "Can view a payment method object" msgid "Can view a payment method object"
msgstr "Peut voir un objet moyen de paiement" msgstr "Peut voir un objet moyen de paiement"
#: cotisations/models.py:833 #: cotisations/models.py:860
msgid "Can use every payment method" msgid "Can use every payment method"
msgstr "Peut utiliser chaque moyen de paiement" msgstr "Peut utiliser chaque moyen de paiement"
#: cotisations/models.py:835 #: cotisations/models.py:862
msgid "payment method" msgid "payment method"
msgstr "moyen de paiement" msgstr "moyen de paiement"
#: cotisations/models.py:836 #: cotisations/models.py:863
msgid "payment methods" msgid "payment methods"
msgstr "moyens de paiement" msgstr "moyens de paiement"
#: cotisations/models.py:875 cotisations/payment_methods/comnpay/views.py:62 #: cotisations/models.py:904 cotisations/payment_methods/comnpay/views.py:62
#, python-format #, python-format
msgid "The subscription of %(member_name)s was extended to %(end_date)s." msgid "The subscription of %(member_name)s was extended to %(end_date)s."
msgstr "La cotisation de %(member_name)s a été étendue au %(end_date)s." msgstr "La cotisation de %(member_name)s a été étendue au %(end_date)s."
#: cotisations/models.py:885 #: cotisations/models.py:914
msgid "The invoice was created." msgid "The invoice was created."
msgstr "La facture a été créée." msgstr "La facture a été créée."
#: cotisations/models.py:905 #: cotisations/models.py:934
msgid "You can't use this payment method." msgid "You can't use this payment method."
msgstr "Vous ne pouvez pas utiliser ce moyen de paiement." msgstr "Vous ne pouvez pas utiliser ce moyen de paiement."
#: cotisations/models.py:924 #: cotisations/models.py:953
msgid "No custom payment methods." msgid "No custom payment methods."
msgstr "Pas de moyens de paiement personnalisés." msgstr "Pas de moyens de paiement personnalisés."
#: cotisations/models.py:955 #: cotisations/models.py:973
msgid "start date" msgid "start date for the connection"
msgstr "date de début" msgstr "date de début pour la connexion"
#: cotisations/models.py:956 #: cotisations/models.py:975
msgid "end date" msgid "end date for the connection"
msgstr "date de fin" msgstr "date de fin pour la connexion"
#: cotisations/models.py:960 #: cotisations/models.py:977
msgid "start date for the membership"
msgstr "date de début pour l'adhésion"
#: cotisations/models.py:979
msgid "end date for the membership"
msgstr "date de fin pour l'adhésion"
#: cotisations/models.py:983
msgid "Can view a subscription object" msgid "Can view a subscription object"
msgstr "Peut voir un objet cotisation" msgstr "Peut voir un objet cotisation"
#: cotisations/models.py:961 #: cotisations/models.py:984
msgid "Can edit the previous subscriptions" msgid "Can edit the previous subscriptions"
msgstr "Peut modifier les cotisations précédentes" msgstr "Peut modifier les cotisations précédentes"
#: cotisations/models.py:963 #: cotisations/models.py:986
msgid "subscription" msgid "subscription"
msgstr "cotisation" msgstr "cotisation"
#: cotisations/models.py:964 #: cotisations/models.py:987
msgid "subscriptions" msgid "subscriptions"
msgstr "cotisations" msgstr "cotisations"
#: cotisations/models.py:970 #: cotisations/models.py:993
msgid "You don't have the right to edit a subscription." msgid "You don't have the right to edit a subscription."
msgstr "Vous n'avez pas le droit de modifier une cotisation." msgstr "Vous n'avez pas le droit de modifier une cotisation."
#: cotisations/models.py:979 #: cotisations/models.py:1002
msgid "" msgid ""
"You don't have the right to edit a subscription already controlled or " "You don't have the right to edit a subscription already controlled or "
"invalidated." "invalidated."
@ -484,11 +490,11 @@ msgstr ""
"Vous n'avez pas le droit de modifier une cotisation précédemment contrôlée " "Vous n'avez pas le droit de modifier une cotisation précédemment contrôlée "
"ou invalidée." "ou invalidée."
#: cotisations/models.py:991 #: cotisations/models.py:1014
msgid "You don't have the right to delete a subscription." msgid "You don't have the right to delete a subscription."
msgstr "Vous n'avez pas le droit de supprimer une cotisation." msgstr "Vous n'avez pas le droit de supprimer une cotisation."
#: cotisations/models.py:998 #: cotisations/models.py:1021
msgid "" msgid ""
"You don't have the right to delete a subscription already controlled or " "You don't have the right to delete a subscription already controlled or "
"invalidated." "invalidated."
@ -496,7 +502,7 @@ msgstr ""
"Vous n'avez pas le droit de supprimer une cotisation précédemment contrôlée " "Vous n'avez pas le droit de supprimer une cotisation précédemment contrôlée "
"ou invalidée." "ou invalidée."
#: cotisations/models.py:1014 #: cotisations/models.py:1037
msgid "You don't have the right to view someone else's subscription history." msgid "You don't have the right to view someone else's subscription history."
msgstr "" msgstr ""
"Vous n'avez pas le droit de voir l'historique des cotisations d'un autre " "Vous n'avez pas le droit de voir l'historique des cotisations d'un autre "
@ -646,22 +652,26 @@ msgid "Price"
msgstr "Prix" msgstr "Prix"
#: cotisations/templates/cotisations/aff_article.html:35 #: cotisations/templates/cotisations/aff_article.html:35
msgid "Subscription type" msgid "Duration membership (in months)"
msgstr "Type de cotisation" msgstr "Durée adhésion (en mois)"
#: cotisations/templates/cotisations/aff_article.html:36 #: cotisations/templates/cotisations/aff_article.html:36
msgid "Duration (in months)" msgid "Duration membership (in days)"
msgstr "Durée (en mois)" msgstr "Durée adhésion (en jours)"
#: cotisations/templates/cotisations/aff_article.html:37 #: cotisations/templates/cotisations/aff_article.html:37
msgid "Duration (in days)" msgid "Duration connection (in months)"
msgstr "Durée (en jours)" msgstr "Durée connexion (en mois)"
#: cotisations/templates/cotisations/aff_article.html:38 #: cotisations/templates/cotisations/aff_article.html:38
msgid "Duration connection (in days)"
msgstr "Durée connexion (en jours)"
#: cotisations/templates/cotisations/aff_article.html:39
msgid "Concerned users" msgid "Concerned users"
msgstr "Utilisateurs concernés" msgstr "Utilisateurs concernés"
#: cotisations/templates/cotisations/aff_article.html:39 #: cotisations/templates/cotisations/aff_article.html:40
#: cotisations/templates/cotisations/aff_paiement.html:34 #: cotisations/templates/cotisations/aff_paiement.html:34
msgid "Available for everyone" msgid "Available for everyone"
msgstr "Disponible pour tous" msgstr "Disponible pour tous"
@ -710,7 +720,7 @@ msgid "Invoice created"
msgstr "Facture créée" msgstr "Facture créée"
#: cotisations/templates/cotisations/aff_cost_estimate.html:91 #: cotisations/templates/cotisations/aff_cost_estimate.html:91
#: cotisations/templates/cotisations/aff_cotisations.html:81 #: cotisations/templates/cotisations/aff_cotisations.html:91
#: cotisations/templates/cotisations/aff_custom_invoice.html:79 #: cotisations/templates/cotisations/aff_custom_invoice.html:79
msgid "PDF" msgid "PDF"
msgstr "PDF" msgstr "PDF"
@ -725,15 +735,15 @@ msgstr "Utilisateur"
msgid "Invoice ID" msgid "Invoice ID"
msgstr "ID facture" msgstr "ID facture"
#: cotisations/templates/cotisations/aff_cotisations.html:71 #: cotisations/templates/cotisations/aff_cotisations.html:81
msgid "Controlled invoice" msgid "Controlled invoice"
msgstr "Facture contrôlée" msgstr "Facture contrôlée"
#: cotisations/templates/cotisations/aff_cotisations.html:84 #: cotisations/templates/cotisations/aff_cotisations.html:94
msgid "Invalidated invoice" msgid "Invalidated invoice"
msgstr "Facture invalidée" msgstr "Facture invalidée"
#: cotisations/templates/cotisations/aff_cotisations.html:88 #: cotisations/templates/cotisations/aff_cotisations.html:98
msgid "Voucher" msgid "Voucher"
msgstr "Reçu" msgstr "Reçu"
@ -749,6 +759,23 @@ msgstr "Type de paiement"
msgid "Custom payment method" msgid "Custom payment method"
msgstr "Moyen de paiement personnalisé" msgstr "Moyen de paiement personnalisé"
#: cotisations/templates/cotisations/aff_profil.html:35
#: cotisations/templates/cotisations/index.html:32
msgid "Subscriptions"
msgstr "Cotisations"
#: cotisations/templates/cotisations/aff_profil.html:43
msgid "Add a subscription"
msgstr "Ajouter une cotisation"
#: cotisations/templates/cotisations/aff_profil.html:48
msgid "Edit the balance"
msgstr "Modifier le solde utilisateur"
#: cotisations/templates/cotisations/aff_profil.html:57
msgid "No invoice"
msgstr "Pas de facture"
#: cotisations/templates/cotisations/control.html:30 #: cotisations/templates/cotisations/control.html:30
msgid "Invoice control" msgid "Invoice control"
msgstr "Contrôle des factures" msgstr "Contrôle des factures"
@ -784,7 +811,7 @@ msgstr "Contrôlé"
#: cotisations/templates/cotisations/control.html:107 #: cotisations/templates/cotisations/control.html:107
#: cotisations/templates/cotisations/delete.html:38 #: cotisations/templates/cotisations/delete.html:38
#: cotisations/templates/cotisations/edit_facture.html:64 #: cotisations/templates/cotisations/edit_facture.html:64
#: cotisations/views.py:168 cotisations/views.py:222 cotisations/views.py:278 #: cotisations/views.py:172 cotisations/views.py:224 cotisations/views.py:280
msgid "Confirm" msgid "Confirm"
msgstr "Confirmer" msgstr "Confirmer"
@ -801,7 +828,7 @@ msgstr ""
"Attention: voulez-vous vraiment supprimer cet objet %(objet_name)s " "Attention: voulez-vous vraiment supprimer cet objet %(objet_name)s "
"( %(objet)s ) ?" "( %(objet)s ) ?"
#: cotisations/templates/cotisations/edit_facture.html:31 #: cotisations/templates/cotisations/edit_facture.html:30
#: cotisations/templates/cotisations/facture.html:30 #: cotisations/templates/cotisations/facture.html:30
msgid "Creation and editing of invoices" msgid "Creation and editing of invoices"
msgstr "Création et modification de factures" msgstr "Création et modification de factures"
@ -813,7 +840,6 @@ msgstr "Modifier la facture"
#: cotisations/templates/cotisations/edit_facture.html:45 #: cotisations/templates/cotisations/edit_facture.html:45
#: cotisations/templates/cotisations/facture.html:59 #: cotisations/templates/cotisations/facture.html:59
#: cotisations/templates/cotisations/index_article.html:30 #: cotisations/templates/cotisations/index_article.html:30
#: cotisations/templates/cotisations/sidebar.html:55
msgid "Articles" msgid "Articles"
msgstr "Articles" msgstr "Articles"
@ -840,14 +866,9 @@ msgid "Total price: <span id=\"total_price\">0,00</span> €"
msgstr "Prix total : <span id=\"total_price\">0,00</span> €" msgstr "Prix total : <span id=\"total_price\">0,00</span> €"
#: cotisations/templates/cotisations/index.html:29 #: cotisations/templates/cotisations/index.html:29
#: cotisations/templates/cotisations/sidebar.html:40
msgid "Invoices" msgid "Invoices"
msgstr "Factures" msgstr "Factures"
#: cotisations/templates/cotisations/index.html:32
msgid "Subscriptions"
msgstr "Cotisations"
#: cotisations/templates/cotisations/index_article.html:33 #: cotisations/templates/cotisations/index_article.html:33
msgid "List of articles" msgid "List of articles"
msgstr "Liste des articles" msgstr "Liste des articles"
@ -861,7 +882,6 @@ msgid "Delete one or several articles"
msgstr "Supprimer un ou plusieurs articles" msgstr "Supprimer un ou plusieurs articles"
#: cotisations/templates/cotisations/index_banque.html:30 #: cotisations/templates/cotisations/index_banque.html:30
#: cotisations/templates/cotisations/sidebar.html:60
msgid "Banks" msgid "Banks"
msgstr "Banques" msgstr "Banques"
@ -878,7 +898,6 @@ msgid "Delete one or several banks"
msgstr "Supprimer une ou plusieurs banques" msgstr "Supprimer une ou plusieurs banques"
#: cotisations/templates/cotisations/index_cost_estimate.html:28 #: cotisations/templates/cotisations/index_cost_estimate.html:28
#: cotisations/templates/cotisations/sidebar.html:50
msgid "Cost estimates" msgid "Cost estimates"
msgstr "Devis" msgstr "Devis"
@ -887,7 +906,6 @@ msgid "List of cost estimates"
msgstr "Liste des devis" msgstr "Liste des devis"
#: cotisations/templates/cotisations/index_custom_invoice.html:28 #: cotisations/templates/cotisations/index_custom_invoice.html:28
#: cotisations/templates/cotisations/sidebar.html:45
msgid "Custom invoices" msgid "Custom invoices"
msgstr "Factures personnalisées" msgstr "Factures personnalisées"
@ -896,7 +914,6 @@ msgid "List of custom invoices"
msgstr "Liste des factures personnalisées" msgstr "Liste des factures personnalisées"
#: cotisations/templates/cotisations/index_paiement.html:30 #: cotisations/templates/cotisations/index_paiement.html:30
#: cotisations/templates/cotisations/sidebar.html:65
msgid "Payment methods" msgid "Payment methods"
msgstr "Moyens de paiement" msgstr "Moyens de paiement"
@ -921,132 +938,124 @@ msgstr "Rechargement de solde"
msgid "Pay %(amount)s €" msgid "Pay %(amount)s €"
msgstr "Payer %(amount)s €" msgstr "Payer %(amount)s €"
#: cotisations/templates/cotisations/payment.html:42 cotisations/views.py:1028 #: cotisations/templates/cotisations/payment.html:42 cotisations/views.py:1030
msgid "Pay" msgid "Pay"
msgstr "Payer" msgstr "Payer"
#: cotisations/templates/cotisations/sidebar.html:32 #: cotisations/views.py:159
msgid "Create an invoice"
msgstr "Créer une facture"
#: cotisations/templates/cotisations/sidebar.html:35
msgid "Control the invoices"
msgstr "Contrôler les factures"
#: cotisations/views.py:155
msgid "You need to choose at least one article." msgid "You need to choose at least one article."
msgstr "Vous devez choisir au moins un article." msgstr "Vous devez choisir au moins un article."
#: cotisations/views.py:169 #: cotisations/views.py:173
msgid "New invoice" msgid "New invoice"
msgstr "Nouvelle facture" msgstr "Nouvelle facture"
#: cotisations/views.py:216 #: cotisations/views.py:218
msgid "The cost estimate was created." msgid "The cost estimate was created."
msgstr "Le devis a été créé." msgstr "Le devis a été créé."
#: cotisations/views.py:226 #: cotisations/views.py:228
msgid "New cost estimate" msgid "New cost estimate"
msgstr "Nouveau devis" msgstr "Nouveau devis"
#: cotisations/views.py:272 #: cotisations/views.py:274
msgid "The custom invoice was created." msgid "The custom invoice was created."
msgstr "La facture personnalisée a été créée." msgstr "La facture personnalisée a été créée."
#: cotisations/views.py:282 #: cotisations/views.py:284
msgid "New custom invoice" msgid "New custom invoice"
msgstr "Nouvelle facture personnalisée" msgstr "Nouvelle facture personnalisée"
#: cotisations/views.py:357 cotisations/views.py:438 #: cotisations/views.py:359 cotisations/views.py:440
msgid "The invoice was edited." msgid "The invoice was edited."
msgstr "La facture a été modifiée." msgstr "La facture a été modifiée."
#: cotisations/views.py:375 cotisations/views.py:570 #: cotisations/views.py:377 cotisations/views.py:572
msgid "The invoice was deleted." msgid "The invoice was deleted."
msgstr "La facture a été supprimée." msgstr "La facture a été supprimée."
#: cotisations/views.py:398 #: cotisations/views.py:400
msgid "The cost estimate was edited." msgid "The cost estimate was edited."
msgstr "Le devis a été modifié." msgstr "Le devis a été modifié."
#: cotisations/views.py:405 #: cotisations/views.py:407
msgid "Edit cost estimate" msgid "Edit cost estimate"
msgstr "Modifier le devis" msgstr "Modifier le devis"
#: cotisations/views.py:419 #: cotisations/views.py:421
msgid "An invoice was successfully created from your cost estimate." msgid "An invoice was successfully created from your cost estimate."
msgstr "Une facture a bien été créée à partir de votre devis." msgstr "Une facture a bien été créée à partir de votre devis."
#: cotisations/views.py:445 #: cotisations/views.py:447
msgid "Edit custom invoice" msgid "Edit custom invoice"
msgstr "Modifier la facture personnalisée" msgstr "Modifier la facture personnalisée"
#: cotisations/views.py:507 #: cotisations/views.py:509
msgid "The cost estimate was deleted." msgid "The cost estimate was deleted."
msgstr "Le devis a été supprimé." msgstr "Le devis a été supprimé."
#: cotisations/views.py:510 #: cotisations/views.py:512
msgid "cost estimate" msgid "cost estimate"
msgstr "devis" msgstr "devis"
#: cotisations/views.py:594 #: cotisations/views.py:596
msgid "The article was created." msgid "The article was created."
msgstr "L'article a été créé." msgstr "L'article a été créé."
#: cotisations/views.py:599 cotisations/views.py:673 cotisations/views.py:767 #: cotisations/views.py:601 cotisations/views.py:675 cotisations/views.py:769
msgid "Add" msgid "Add"
msgstr "Ajouter" msgstr "Ajouter"
#: cotisations/views.py:600 #: cotisations/views.py:602
msgid "New article" msgid "New article"
msgstr "Nouvel article" msgstr "Nouvel article"
#: cotisations/views.py:617 #: cotisations/views.py:619
msgid "The article was edited." msgid "The article was edited."
msgstr "L'article a été modifié." msgstr "L'article a été modifié."
#: cotisations/views.py:622 cotisations/views.py:705 cotisations/views.py:791 #: cotisations/views.py:624 cotisations/views.py:707 cotisations/views.py:793
msgid "Edit" msgid "Edit"
msgstr "Modifier" msgstr "Modifier"
#: cotisations/views.py:623 #: cotisations/views.py:625
msgid "Edit article" msgid "Edit article"
msgstr "Modifier l'article" msgstr "Modifier l'article"
#: cotisations/views.py:640 #: cotisations/views.py:642
msgid "The articles were deleted." msgid "The articles were deleted."
msgstr "Les articles ont été supprimés." msgstr "Les articles ont été supprimés."
#: cotisations/views.py:645 cotisations/views.py:744 cotisations/views.py:829 #: cotisations/views.py:647 cotisations/views.py:746 cotisations/views.py:831
msgid "Delete" msgid "Delete"
msgstr "Supprimer" msgstr "Supprimer"
#: cotisations/views.py:646 #: cotisations/views.py:648
msgid "Delete article" msgid "Delete article"
msgstr "Supprimer l'article" msgstr "Supprimer l'article"
#: cotisations/views.py:667 #: cotisations/views.py:669
msgid "The payment method was created." msgid "The payment method was created."
msgstr "Le moyen de paiment a été créé." msgstr "Le moyen de paiment a été créé."
#: cotisations/views.py:674 #: cotisations/views.py:676
msgid "New payment method" msgid "New payment method"
msgstr "Nouveau moyen de paiement" msgstr "Nouveau moyen de paiement"
#: cotisations/views.py:699 #: cotisations/views.py:701
msgid "The payment method was edited." msgid "The payment method was edited."
msgstr "Le moyen de paiment a été modifié." msgstr "Le moyen de paiment a été modifié."
#: cotisations/views.py:706 #: cotisations/views.py:708
msgid "Edit payment method" msgid "Edit payment method"
msgstr "Modifier le moyen de paiement" msgstr "Modifier le moyen de paiement"
#: cotisations/views.py:728 #: cotisations/views.py:730
#, python-format #, python-format
msgid "The payment method %(method_name)s was deleted." msgid "The payment method %(method_name)s was deleted."
msgstr "Le moyen de paiement %(method_name)s a été supprimé." msgstr "Le moyen de paiement %(method_name)s a été supprimé."
#: cotisations/views.py:735 #: cotisations/views.py:737
#, python-format #, python-format
msgid "" msgid ""
"The payment method %(method_name)s can't be deleted because there are " "The payment method %(method_name)s can't be deleted because there are "
@ -1055,32 +1064,32 @@ msgstr ""
"Le moyen de paiement %(method_name)s ne peut pas être supprimé car il y a " "Le moyen de paiement %(method_name)s ne peut pas être supprimé car il y a "
"des factures qui l'utilisent." "des factures qui l'utilisent."
#: cotisations/views.py:745 #: cotisations/views.py:747
msgid "Delete payment method" msgid "Delete payment method"
msgstr "Supprimer le moyen de paiement" msgstr "Supprimer le moyen de paiement"
#: cotisations/views.py:762 #: cotisations/views.py:764
msgid "The bank was created." msgid "The bank was created."
msgstr "La banque a été créée." msgstr "La banque a été créée."
#: cotisations/views.py:768 #: cotisations/views.py:770
msgid "New bank" msgid "New bank"
msgstr "Nouvelle banque" msgstr "Nouvelle banque"
#: cotisations/views.py:786 #: cotisations/views.py:788
msgid "The bank was edited." msgid "The bank was edited."
msgstr "La banque a été modifiée." msgstr "La banque a été modifiée."
#: cotisations/views.py:792 #: cotisations/views.py:794
msgid "Edit bank" msgid "Edit bank"
msgstr "Modifier la banque" msgstr "Modifier la banque"
#: cotisations/views.py:814 #: cotisations/views.py:816
#, python-format #, python-format
msgid "The bank %(bank_name)s was deleted." msgid "The bank %(bank_name)s was deleted."
msgstr "La banque %(bank_name)s a été supprimée." msgstr "La banque %(bank_name)s a été supprimée."
#: cotisations/views.py:820 #: cotisations/views.py:822
#, python-format #, python-format
msgid "" msgid ""
"The bank %(bank_name)s can't be deleted because there are invoices using it." "The bank %(bank_name)s can't be deleted because there are invoices using it."
@ -1088,22 +1097,22 @@ msgstr ""
"La banque %(bank_name)s ne peut pas être supprimée car il y a des factures " "La banque %(bank_name)s ne peut pas être supprimée car il y a des factures "
"qui l'utilisent." "qui l'utilisent."
#: cotisations/views.py:830 #: cotisations/views.py:832
msgid "Delete bank" msgid "Delete bank"
msgstr "Supprimer la banque" msgstr "Supprimer la banque"
#: cotisations/views.py:864 #: cotisations/views.py:866
msgid "Your changes have been properly taken into account." msgid "Your changes have been properly taken into account."
msgstr "Vos modifications ont correctement été prises en compte." msgstr "Vos modifications ont correctement été prises en compte."
#: cotisations/views.py:996 #: cotisations/views.py:998
msgid "You are not allowed to credit your balance." msgid "You are not allowed to credit your balance."
msgstr "Vous n'êtes pas autorisé à créditer votre solde." msgstr "Vous n'êtes pas autorisé à créditer votre solde."
#: cotisations/views.py:1027 #: cotisations/views.py:1029
msgid "Refill your balance" msgid "Refill your balance"
msgstr "Recharger votre solde" msgstr "Recharger votre solde"
#: cotisations/views.py:1046 #: cotisations/views.py:1048
msgid "Could not find a voucher for that invoice." msgid "Could not find a voucher for that invoice."
msgstr "Impossible de trouver un reçu pour cette facture." msgstr "Impossible de trouver un reçu pour cette facture."

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -0,0 +1,970 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import django.core.validators
import re2o.mixins
import re2o.aes_field
import re2o.field_permissions
import cotisations.models
import cotisations.payment_methods.mixins
class Migration(migrations.Migration):
initial = True
dependencies = []
replaces = [
("users", "0001_initial"),
("users", "0002_auto_20160630_2301"),
("users", "0003_listrights_rights"),
("users", "0004_auto_20160701_2312"),
("users", "0005_auto_20160702_0006"),
("users", "0006_ban"),
("users", "0007_auto_20160702_2322"),
("users", "0008_user_registered"),
("users", "0009_user_room"),
("users", "0010_auto_20160703_1226"),
("users", "0011_auto_20160703_1227"),
("users", "0012_auto_20160703_1230"),
("users", "0013_auto_20160704_1547"),
("users", "0014_auto_20160704_1548"),
("users", "0015_whitelist"),
("users", "0016_auto_20160706_1220"),
("users", "0017_auto_20160707_0105"),
("users", "0018_auto_20160707_0115"),
("users", "0019_auto_20160708_1633"),
("users", "0020_request"),
("users", "0021_ldapuser"),
("users", "0022_ldapuser_sambasid"),
("users", "0023_auto_20160724_1908"),
("users", "0024_remove_ldapuser_mac_list"),
("users", "0025_listshell"),
("users", "0026_user_shell"),
("users", "0027_auto_20160726_0216"),
("users", "0028_auto_20160726_0227"),
("users", "0029_auto_20160726_0229"),
("users", "0030_auto_20160726_0357"),
("users", "0031_auto_20160726_0359"),
("users", "0032_auto_20160727_2122"),
("users", "0033_remove_ldapuser_loginshell"),
("users", "0034_auto_20161018_0037"),
("users", "0035_auto_20161018_0046"),
("users", "0036_auto_20161022_2146"),
("users", "0037_auto_20161028_1906"),
("users", "0038_auto_20161031_0258"),
("users", "0039_auto_20161119_0033"),
("users", "0040_auto_20161119_1709"),
("users", "0041_listright_details"),
("users", "0042_auto_20161126_2028"),
("users", "0043_auto_20161224_1156"),
("users", "0043_ban_state"),
("users", "0044_user_ssh_public_key"),
("users", "0045_merge"),
("users", "0046_auto_20170617_1433"),
("users", "0047_auto_20170618_0156"),
("users", "0048_auto_20170618_0210"),
("users", "0049_auto_20170618_1424"),
("users", "0050_serviceuser_comment"),
("users", "0051_user_telephone"),
("users", "0052_ldapuser_shadowexpire"),
("users", "0053_auto_20170626_2105"),
("users", "0054_auto_20170626_2219"),
("users", "0055_auto_20171003_0556"),
("users", "0056_auto_20171015_2033"),
("users", "0057_auto_20171023_0301"),
("users", "0058_auto_20171025_0154"),
("users", "0059_auto_20171025_1854"),
("users", "0060_auto_20171120_0317"),
("users", "0061_auto_20171230_2033"),
("users", "0062_auto_20171231_0056"),
("users", "0063_auto_20171231_0140"),
("users", "0064_auto_20171231_0150"),
("users", "0065_auto_20171231_2053"),
("users", "0066_grouppermissions"),
("users", "0067_serveurpermission"),
("users", "0068_auto_20180107_2245"),
("users", "0069_club_mailing"),
("users", "0070_auto_20180324_1906"),
("users", "0071_auto_20180415_1252"),
("users", "0072_auto_20180426_2021"),
("users", "0073_auto_20180629_1614"),
("users", "0074_auto_20180810_2104"),
("users", "0074_auto_20180814_1059"),
("users", "0075_merge_20180815_2202"),
("users", "0076_auto_20180818_1321"),
("users", "0077_auto_20180824_1750"),
("users", "0078_auto_20181011_1405"),
("users", "0079_auto_20181228_2039"),
("users", "0080_auto_20190108_1726"),
("users", "0081_auto_20190317_0302"),
("users", "0082_auto_20190908_1338"),
("users", "0083_user_shortcuts_enabled"),
("users", "0084_auto_20191120_0159"),
("users", "0085_user_email_state"),
("users", "0086_user_email_change_date"),
("users", "0087_request_email"),
("users", "0088_auto_20200417_2312"),
("users", "0089_auto_20200418_0112"),
("users", "0090_auto_20200421_1825"),
("users", "0091_auto_20200423_1256"),
("users", "0092_auto_20200502_0057"),
("users", "0093_user_profile_image"),
("users", "0094_remove_user_profile_image"),
("users", "0095_user_theme"),
("users", "0096_auto_20210110_1811"),
("cotisations", "0001_initial"),
("cotisations", "0002_remove_facture_article"),
("cotisations", "0003_auto_20160702_1448"),
("cotisations", "0004_auto_20160702_1528"),
("cotisations", "0005_auto_20160702_1532"),
("cotisations", "0006_auto_20160702_1534"),
("cotisations", "0007_auto_20160702_1543"),
("cotisations", "0008_auto_20160702_1614"),
("cotisations", "0009_remove_cotisation_user"),
("cotisations", "0010_auto_20160702_1840"),
("cotisations", "0011_auto_20160702_1911"),
("cotisations", "0012_auto_20160704_0118"),
("cotisations", "0013_auto_20160711_2240"),
("cotisations", "0014_auto_20160712_0245"),
("cotisations", "0015_auto_20160714_2142"),
("cotisations", "0016_auto_20160715_0110"),
("cotisations", "0017_auto_20170718_2329"),
("cotisations", "0018_paiement_type_paiement"),
("cotisations", "0019_auto_20170819_0055"),
("cotisations", "0020_auto_20170819_0057"),
("cotisations", "0021_auto_20170819_0104"),
("cotisations", "0022_auto_20170824_0128"),
("cotisations", "0023_auto_20170902_1303"),
("cotisations", "0024_auto_20171015_2033"),
("cotisations", "0025_article_type_user"),
("cotisations", "0026_auto_20171028_0126"),
("cotisations", "0027_auto_20171029_1156"),
("cotisations", "0028_auto_20171231_0007"),
("cotisations", "0029_auto_20180414_2056"),
("cotisations", "0030_custom_payment"),
("cotisations", "0031_comnpaypayment_production"),
("cotisations", "0032_custom_invoice"),
("cotisations", "0033_auto_20180818_1319"),
("cotisations", "0034_auto_20180831_1532"),
("cotisations", "0035_notepayment"),
("cotisations", "0036_custominvoice_remark"),
("cotisations", "0037_costestimate"),
("cotisations", "0038_auto_20181231_1657"),
("cotisations", "0039_freepayment"),
("cotisations", "0040_auto_20191002_2335"),
("cotisations", "0041_auto_20191103_2131"),
("cotisations", "0042_auto_20191120_0159"),
("cotisations", "0043_separation_membership_connection_p1"),
("cotisations", "0044_separation_membership_connection_p2"),
("cotisations", "0045_separation_membership_connection_p3"),
("cotisations", "0046_article_need_membership"),
("cotisations", "0047_article_need_membership_init"),
("cotisations", "0048_auto_20201017_0018"),
("cotisations", "0049_auto_20201102_2305"),
("cotisations", "0050_auto_20201102_2342"),
("cotisations", "0051_auto_20201228_1636"),
("machines", "0001_initial"),
("machines", "0002_auto_20160703_1444"),
("machines", "0003_auto_20160703_1450"),
("machines", "0004_auto_20160703_1451"),
("machines", "0005_auto_20160703_1523"),
("machines", "0006_auto_20160703_1813"),
("machines", "0007_auto_20160703_1816"),
("machines", "0008_remove_interface_ipv6"),
("machines", "0009_auto_20160703_2358"),
("machines", "0010_auto_20160704_0104"),
("machines", "0011_auto_20160704_0105"),
("machines", "0012_auto_20160704_0118"),
("machines", "0013_auto_20160705_1014"),
("machines", "0014_auto_20160706_1220"),
("machines", "0015_auto_20160707_0105"),
("machines", "0016_auto_20160708_1633"),
("machines", "0017_auto_20160708_1645"),
("machines", "0018_auto_20160708_1813"),
("machines", "0019_auto_20160718_1141"),
("machines", "0020_auto_20160718_1849"),
("machines", "0021_auto_20161006_1943"),
("machines", "0022_auto_20161011_1829"),
("machines", "0023_iplist_ip_type"),
("machines", "0024_machinetype_need_infra"),
("machines", "0025_auto_20161023_0038"),
("machines", "0026_auto_20161026_1348"),
("machines", "0027_alias"),
("machines", "0028_iptype_domaine_ip"),
("machines", "0029_iptype_domaine_range"),
("machines", "0030_auto_20161118_1730"),
("machines", "0031_auto_20161119_1709"),
("machines", "0032_auto_20161119_1850"),
("machines", "0033_extension_need_infra"),
("machines", "0034_iplist_need_infra"),
("machines", "0035_auto_20161224_1201"),
("machines", "0036_auto_20161224_1204"),
("machines", "0037_domain_cname"),
("machines", "0038_auto_20161224_1721"),
("machines", "0039_auto_20161224_1732"),
("machines", "0040_remove_interface_dns"),
("machines", "0041_remove_ns_interface"),
("machines", "0042_ns_ns"),
("machines", "0043_auto_20170721_0350"),
("machines", "0044_auto_20170808_0233"),
("machines", "0045_auto_20170808_0348"),
("machines", "0046_auto_20170808_1423"),
("machines", "0047_auto_20170809_0606"),
("machines", "0048_auto_20170823_2315"),
("machines", "0049_vlan"),
("machines", "0050_auto_20170826_0022"),
("machines", "0051_iptype_vlan"),
("machines", "0052_auto_20170828_2322"),
("machines", "0053_text"),
("machines", "0054_text_zone"),
("machines", "0055_nas"),
("machines", "0056_nas_port_access_mode"),
("machines", "0057_nas_autocapture_mac"),
("machines", "0058_auto_20171002_0350"),
("machines", "0059_iptype_prefix_v6"),
("machines", "0060_iptype_ouverture_ports"),
("machines", "0061_auto_20171015_2033"),
("machines", "0062_extension_origin_v6"),
("machines", "0063_auto_20171020_0040"),
("machines", "0064_auto_20171115_0253"),
("machines", "0065_auto_20171115_1514"),
("machines", "0066_srv"),
("machines", "0067_auto_20171116_0152"),
("machines", "0068_auto_20171116_0252"),
("machines", "0069_auto_20171116_0822"),
("machines", "0070_auto_20171231_1947"),
("machines", "0071_auto_20171231_2100"),
("machines", "0072_auto_20180108_1822"),
("machines", "0073_auto_20180128_2203"),
("machines", "0074_auto_20180129_0352"),
("machines", "0075_auto_20180130_0052"),
("machines", "0076_auto_20180130_1623"),
("machines", "0077_auto_20180409_2243"),
("machines", "0078_auto_20180415_1252"),
("machines", "0079_auto_20180416_0107"),
("machines", "0080_auto_20180502_2334"),
("machines", "0081_auto_20180521_1413"),
("machines", "0082_auto_20180525_2209"),
("machines", "0083_remove_duplicate_rights"),
("machines", "0084_dname"),
("machines", "0085_sshfingerprint"),
("machines", "0086_role"),
("machines", "0087_dnssec"),
("machines", "0088_iptype_prefix_v6_length"),
("machines", "0089_auto_20180805_1148"),
("machines", "0090_auto_20180805_1459"),
("machines", "0091_auto_20180806_2310"),
("machines", "0092_auto_20180807_0926"),
("machines", "0093_auto_20180807_1115"),
("machines", "0094_auto_20180815_1918"),
("machines", "0095_auto_20180919_2225"),
("machines", "0096_auto_20181013_1417"),
("machines", "0097_extension_dnssec"),
("machines", "0098_auto_20190102_1745"),
("machines", "0099_role_recursive_dns"),
("machines", "0100_auto_20190102_1753"),
("machines", "0101_auto_20190108_1623"),
("machines", "0102_auto_20190303_1611"),
("machines", "0103_auto_20191002_2222"),
("machines", "0104_auto_20191002_2231"),
("machines", "0105_dname_ttl"),
("machines", "0106_auto_20191120_0159"),
("machines", "0107_fix_lowercase_domain"),
("machines", "0108_ipv6list_active"),
("preferences", "0001_initial"),
("preferences", "0002_auto_20170625_1923"),
("preferences", "0003_optionaluser_solde_negatif"),
("preferences", "0004_assooption_services"),
("preferences", "0005_auto_20170824_0139"),
("preferences", "0006_auto_20170824_0143"),
("preferences", "0007_auto_20170824_2056"),
("preferences", "0008_auto_20170824_2122"),
("preferences", "0009_assooption_utilisateur_asso"),
("preferences", "0010_auto_20170825_0459"),
("preferences", "0011_auto_20170825_2307"),
("preferences", "0012_generaloption_req_expire_hrs"),
("preferences", "0013_generaloption_site_name"),
("preferences", "0014_generaloption_email_from"),
("preferences", "0015_optionaltopologie_radius_general_policy"),
("preferences", "0016_auto_20170902_1520"),
("preferences", "0017_mailmessageoption"),
("preferences", "0018_optionaltopologie_mac_autocapture"),
("preferences", "0019_remove_optionaltopologie_mac_autocapture"),
("preferences", "0020_optionalmachine_ipv6"),
("preferences", "0021_auto_20171015_1741"),
("preferences", "0022_auto_20171015_1758"),
("preferences", "0023_auto_20171015_2033"),
("preferences", "0024_optionaluser_all_can_create"),
("preferences", "0025_auto_20171231_2142"),
("preferences", "0025_generaloption_general_message"),
("preferences", "0026_auto_20171216_0401"),
("preferences", "0027_merge_20180106_2019"),
("preferences", "0028_assooption_description"),
("preferences", "0028_auto_20180111_1129"),
("preferences", "0028_auto_20180128_2203"),
("preferences", "0029_auto_20180111_1134"),
("preferences", "0029_auto_20180318_0213"),
("preferences", "0029_auto_20180318_1005"),
("preferences", "0030_auto_20180111_2346"),
("preferences", "0030_merge_20180320_1419"),
("preferences", "0031_auto_20180323_0218"),
("preferences", "0031_optionaluser_self_adhesion"),
("preferences", "0032_optionaluser_min_online_payment"),
("preferences", "0032_optionaluser_shell_default"),
("preferences", "0033_accueiloption"),
("preferences", "0033_generaloption_gtu_sum_up"),
("preferences", "0034_auto_20180114_2025"),
("preferences", "0034_auto_20180416_1120"),
("preferences", "0035_auto_20180114_2132"),
("preferences", "0035_optionaluser_allow_self_subscription"),
("preferences", "0036_auto_20180114_2141"),
("preferences", "0037_auto_20180114_2156"),
("preferences", "0038_auto_20180114_2209"),
("preferences", "0039_auto_20180115_0003"),
("preferences", "0040_auto_20180129_1745"),
("preferences", "0041_merge_20180130_0052"),
("preferences", "0042_auto_20180222_1743"),
("preferences", "0043_optionalmachine_create_machine"),
("preferences", "0044_remove_payment_pass"),
("preferences", "0045_remove_unused_payment_fields"),
("preferences", "0046_optionaluser_mail_extension"),
("preferences", "0047_mailcontact"),
("preferences", "0048_auto_20180811_1515"),
("preferences", "0049_optionaluser_self_change_shell"),
("preferences", "0050_auto_20180818_1329"),
("preferences", "0051_auto_20180919_2225"),
("preferences", "0052_optionaluser_delete_notyetactive"),
("preferences", "0053_optionaluser_self_change_room"),
("preferences", "0055_generaloption_main_site_url"),
("preferences", "0056_1_radiusoption"),
("preferences", "0056_2_radiusoption"),
("preferences", "0056_3_radiusoption"),
("preferences", "0056_4_radiusoption"),
("preferences", "0057_optionaluser_all_users_active"),
("preferences", "0058_auto_20190108_1650"),
("preferences", "0059_auto_20190120_1739"),
("preferences", "0060_auto_20190712_1821"),
("preferences", "0061_optionaluser_allow_archived_connexion"),
("preferences", "0062_auto_20190910_1909"),
("preferences", "0063_mandate"),
("preferences", "0064_auto_20191008_1335"),
("preferences", "0065_auto_20191010_1227"),
("preferences", "0066_optionalmachine_default_dns_ttl"),
("preferences", "0067_auto_20191120_0159"),
("preferences", "0068_optionaluser_allow_set_password_during_user_creation"),
("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"),
("preferences", "0070_auto_20200419_0225"),
("preferences", "0071_optionaluser_self_change_pseudo"),
("topologie", "0001_initial"),
("topologie", "0002_auto_20160703_1118"),
("topologie", "0003_room"),
("topologie", "0004_auto_20160703_1122"),
("topologie", "0005_auto_20160703_1123"),
("topologie", "0006_auto_20160703_1129"),
("topologie", "0007_auto_20160703_1148"),
("topologie", "0008_port_room"),
("topologie", "0009_auto_20160703_1200"),
("topologie", "0010_auto_20160704_2148"),
("topologie", "0011_auto_20160704_2153"),
("topologie", "0012_port_machine_interface"),
("topologie", "0013_port_related"),
("topologie", "0014_auto_20160706_1238"),
("topologie", "0015_auto_20160706_1452"),
("topologie", "0016_auto_20160706_1531"),
("topologie", "0017_auto_20160718_1141"),
("topologie", "0018_room_details"),
("topologie", "0019_auto_20161026_1348"),
("topologie", "0020_auto_20161119_0033"),
("topologie", "0021_port_radius"),
("topologie", "0022_auto_20161211_1622"),
("topologie", "0023_auto_20170817_1654"),
("topologie", "0023_auto_20170826_1530"),
("topologie", "0024_auto_20170818_1021"),
("topologie", "0024_auto_20170826_1800"),
("topologie", "0025_merge_20170902_1242"),
("topologie", "0026_auto_20170902_1245"),
("topologie", "0027_auto_20170905_1442"),
("topologie", "0028_auto_20170913_1503"),
("topologie", "0029_auto_20171002_0334"),
("topologie", "0030_auto_20171004_0235"),
("topologie", "0031_auto_20171015_2033"),
("topologie", "0032_auto_20171026_0338"),
("topologie", "0033_auto_20171231_1743"),
("topologie", "0034_borne"),
("topologie", "0035_auto_20180324_0023"),
("topologie", "0036_transferborne"),
("topologie", "0037_auto_20180325_0127"),
("topologie", "0038_transfersw"),
("topologie", "0039_port_new_switch"),
("topologie", "0040_transferports"),
("topologie", "0041_transferportsw"),
("topologie", "0042_transferswitch"),
("topologie", "0043_renamenewswitch"),
("topologie", "0044_auto_20180326_0002"),
("topologie", "0045_auto_20180326_0123"),
("topologie", "0046_auto_20180326_0129"),
("topologie", "0047_ap_machine"),
("topologie", "0048_ap_machine"),
("topologie", "0049_switchs_machine"),
("topologie", "0050_port_new_switch"),
("topologie", "0051_switchs_machine"),
("topologie", "0052_transferports"),
("topologie", "0053_finalsw"),
("topologie", "0054_auto_20180326_1742"),
("topologie", "0055_auto_20180329_0431"),
("topologie", "0056_building_switchbay"),
("topologie", "0057_auto_20180408_0316"),
("topologie", "0058_remove_switch_location"),
("topologie", "0059_auto_20180415_2249"),
("topologie", "0060_server"),
("topologie", "0061_portprofile"),
("topologie", "0062_auto_20180815_1918"),
("topologie", "0063_auto_20180919_2225"),
("topologie", "0064_switch_automatic_provision"),
("topologie", "0065_auto_20180927_1836"),
("topologie", "0066_modelswitch_commercial_name"),
("topologie", "0067_auto_20181230_1819"),
("topologie", "0068_auto_20190102_1758"),
("topologie", "0069_auto_20190108_1439"),
("topologie", "0070_auto_20190218_1743"),
("topologie", "0071_auto_20190218_1936"),
("topologie", "0072_auto_20190720_2318"),
("topologie", "0073_auto_20191120_0159"),
("topologie", "0074_auto_20200419_1640"),
]
operations = [
migrations.CreateModel(
name="BaseInvoice",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("date", models.DateTimeField(auto_now_add=True, verbose_name="date")),
],
bases=(
re2o.mixins.RevMixin,
re2o.mixins.AclMixin,
re2o.field_permissions.FieldPermissionModelMixin,
models.Model,
),
),
migrations.CreateModel(
name="Facture",
fields=[
(
"baseinvoice_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="cotisations.BaseInvoice",
),
),
(
"cheque",
models.CharField(
max_length=255, blank=True, verbose_name="cheque number"
),
),
("valid", models.BooleanField(default=False, verbose_name="validated")),
(
"control",
models.BooleanField(default=False, verbose_name="controlled"),
),
],
options={
"permissions": (
("change_facture_control", 'Can edit the "controlled" state'),
("view_facture", "Can view an invoice object"),
("change_all_facture", "Can edit all the previous invoices"),
),
"verbose_name": "invoice",
"verbose_name_plural": "invoices",
},
),
migrations.CreateModel(
name="CustomInvoice",
fields=[
(
"baseinvoice_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="cotisations.BaseInvoice",
),
),
(
"recipient",
models.CharField(max_length=255, verbose_name="recipient"),
),
(
"payment",
models.CharField(max_length=255, verbose_name="payment type"),
),
("address", models.CharField(max_length=255, verbose_name="address")),
("paid", models.BooleanField(default=False, verbose_name="paid")),
(
"remark",
models.TextField(verbose_name="remark", blank=True, null=True),
),
],
bases=("cotisations.baseinvoice",),
options={
"permissions": (
("view_custominvoice", "Can view a custom invoice object"),
)
},
),
migrations.CreateModel(
name="CostEstimate",
fields=[
(
"custominvoice_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="cotisations.CustomInvoice",
),
),
(
"validity",
models.DurationField(
verbose_name="period of validity", help_text="DD HH:MM:SS"
),
),
],
options={
"permissions": (
("view_costestimate", "Can view a cost estimate object"),
)
},
bases=("cotisations.custominvoice",),
),
migrations.CreateModel(
name="Vente",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"number",
models.IntegerField(
validators=[django.core.validators.MinValueValidator(1)],
verbose_name="amount",
),
),
("name", models.CharField(max_length=255, verbose_name="article")),
(
"prix",
models.DecimalField(
max_digits=5, decimal_places=2, verbose_name="price"
),
),
(
"duration_connection",
models.PositiveIntegerField(
default=0, verbose_name="duration of the connection (in months)"
),
),
(
"duration_days_connection",
models.PositiveIntegerField(
default=0,
verbose_name="duration of the connection (in days, will be added to duration in months)",
),
),
(
"duration_membership",
models.PositiveIntegerField(
default=0, verbose_name="duration of the membership (in months)"
),
),
(
"duration_days_membership",
models.PositiveIntegerField(
default=0,
verbose_name="duration of the membership (in days, will be added to duration in months)",
),
),
],
bases=(
re2o.mixins.RevMixin,
re2o.mixins.AclMixin,
models.Model,
),
options={
"permissions": (
("view_vente", "Can view a purchase object"),
("change_all_vente", "Can edit all the previous purchases"),
),
"verbose_name": "purchase",
"verbose_name_plural": "purchases",
},
),
migrations.CreateModel(
name="Article",
bases=(
re2o.mixins.RevMixin,
re2o.mixins.AclMixin,
models.Model,
),
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"name",
models.CharField(max_length=255, verbose_name="designation"),
),
(
"prix",
models.DecimalField(
max_digits=5, decimal_places=2, verbose_name="unit price"
),
),
(
"duration_connection",
models.PositiveIntegerField(
verbose_name="duration of the connection (in months)"
),
),
(
"duration_days_connection",
models.PositiveIntegerField(
verbose_name="duration of the connection (in days, will be added to duration in months)",
),
),
(
"duration_membership",
models.PositiveIntegerField(
verbose_name="duration of the membership (in months)"
),
),
(
"duration_days_membership",
models.PositiveIntegerField(
verbose_name="duration of the membership (in days, will be added to duration in months)",
),
),
(
"need_membership",
models.BooleanField(
default=True, verbose_name="need membership to be purchased"
),
),
(
"type_user",
models.CharField(
choices=[
("Adherent", "Member"),
("Club", "Club"),
("All", "Both of them"),
],
default="All",
max_length=255,
verbose_name="type of users concerned",
),
),
(
"available_for_everyone",
models.BooleanField(
default=False, verbose_name="is available for every user"
),
),
],
options={
"permissions": (
("view_article", "Can view an article object"),
("buy_every_article", "Can buy every article"),
),
"verbose_name": "article",
"verbose_name_plural": "articles",
},
),
migrations.CreateModel(
name="Banque",
bases=(
re2o.mixins.RevMixin,
re2o.mixins.AclMixin,
models.Model,
),
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("name", models.CharField(max_length=255)),
],
options={
"permissions": (("view_banque", "Can view a bank object"),),
"verbose_name": "bank",
"verbose_name_plural": "banks",
},
),
migrations.CreateModel(
name="Paiement",
bases=(
re2o.mixins.RevMixin,
re2o.mixins.AclMixin,
models.Model,
),
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("moyen", models.CharField(max_length=255, verbose_name="method")),
(
"available_for_everyone",
models.BooleanField(
default=False,
verbose_name="is available for every user",
),
),
(
"is_balance",
models.BooleanField(
default=False,
editable=False,
verbose_name="is user balance",
help_text="There should be only one balance payment method.",
validators=[cotisations.models.check_no_balance],
),
),
],
options={
"permissions": (
("view_paiement", "Can view a payment method object"),
("use_every_payment", "Can use every payment method"),
),
"verbose_name": "payment method",
"verbose_name_plural": "payment methods",
},
),
migrations.CreateModel(
name="Cotisation",
bases=(
re2o.mixins.RevMixin,
re2o.mixins.AclMixin,
models.Model,
),
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"date_start_con",
models.DateTimeField(verbose_name="start date for the connection"),
),
(
"date_end_con",
models.DateTimeField(verbose_name="end date for the connection"),
),
(
"date_start_memb",
models.DateTimeField(verbose_name="start date for the membership"),
),
(
"date_end_memb",
models.DateTimeField(verbose_name="end date for the membership"),
),
],
options={
"permissions": (
("view_cotisation", "Can view a subscription object"),
("change_all_cotisation", "Can edit the previous subscriptions"),
),
"verbose_name": "subscription",
"verbose_name_plural": "subscriptions",
},
),
migrations.CreateModel(
name="BalancePayment",
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model),
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"minimum_balance",
models.DecimalField(
verbose_name="minimum balance",
help_text="The minimal amount of money allowed for the balance at the end of a payment. You can specify a negative amount.",
max_digits=5,
decimal_places=2,
default=0,
),
),
(
"maximum_balance",
models.DecimalField(
verbose_name="maximum balance",
help_text="The maximal amount of money allowed for the balance.",
max_digits=5,
decimal_places=2,
default=50,
blank=True,
null=True,
),
),
(
"credit_balance_allowed",
models.BooleanField(
verbose_name="allow user to credit their balance", default=False
),
),
],
options={"verbose_name": "user balance"},
),
migrations.CreateModel(
name="ChequePayment",
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model),
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
],
options={"verbose_name": "cheque"},
),
migrations.CreateModel(
name="ComnpayPayment",
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model),
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"payment_credential",
models.CharField(
max_length=255,
default="",
blank=True,
verbose_name="ComNpay VAT Number",
),
),
(
"payment_pass",
re2o.aes_field.AESEncryptedField(
max_length=255,
null=True,
blank=True,
verbose_name="ComNpay secret key",
),
),
(
"minimum_payment",
models.DecimalField(
verbose_name="minimum payment",
help_text="The minimal amount of money you have to use when paying with ComNpay.",
max_digits=5,
decimal_places=2,
default=1,
),
),
(
"production",
models.BooleanField(
default=True,
verbose_name="production mode enabled (production URL, instead of homologation)",
),
),
],
options={"verbose_name": "ComNpay"},
),
migrations.CreateModel(
name="FreePayment",
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model),
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
],
options={"verbose_name": "Free payment"},
),
migrations.CreateModel(
name="NotePayment",
bases=(cotisations.payment_methods.mixins.PaymentMethodMixin, models.Model),
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("server", models.CharField(max_length=255, verbose_name="server")),
("port", models.PositiveIntegerField(blank=True, null=True)),
("id_note", models.PositiveIntegerField(blank=True, null=True)),
],
options={"verbose_name": "NoteKfet"},
),
]

View file

@ -0,0 +1,500 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-12-30 15:27
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('cotisations', '0001_model_creation'),
]
replaces = [
("users", "0001_initial"),
("users", "0002_auto_20160630_2301"),
("users", "0003_listrights_rights"),
("users", "0004_auto_20160701_2312"),
("users", "0005_auto_20160702_0006"),
("users", "0006_ban"),
("users", "0007_auto_20160702_2322"),
("users", "0008_user_registered"),
("users", "0009_user_room"),
("users", "0010_auto_20160703_1226"),
("users", "0011_auto_20160703_1227"),
("users", "0012_auto_20160703_1230"),
("users", "0013_auto_20160704_1547"),
("users", "0014_auto_20160704_1548"),
("users", "0015_whitelist"),
("users", "0016_auto_20160706_1220"),
("users", "0017_auto_20160707_0105"),
("users", "0018_auto_20160707_0115"),
("users", "0019_auto_20160708_1633"),
("users", "0020_request"),
("users", "0021_ldapuser"),
("users", "0022_ldapuser_sambasid"),
("users", "0023_auto_20160724_1908"),
("users", "0024_remove_ldapuser_mac_list"),
("users", "0025_listshell"),
("users", "0026_user_shell"),
("users", "0027_auto_20160726_0216"),
("users", "0028_auto_20160726_0227"),
("users", "0029_auto_20160726_0229"),
("users", "0030_auto_20160726_0357"),
("users", "0031_auto_20160726_0359"),
("users", "0032_auto_20160727_2122"),
("users", "0033_remove_ldapuser_loginshell"),
("users", "0034_auto_20161018_0037"),
("users", "0035_auto_20161018_0046"),
("users", "0036_auto_20161022_2146"),
("users", "0037_auto_20161028_1906"),
("users", "0038_auto_20161031_0258"),
("users", "0039_auto_20161119_0033"),
("users", "0040_auto_20161119_1709"),
("users", "0041_listright_details"),
("users", "0042_auto_20161126_2028"),
("users", "0043_auto_20161224_1156"),
("users", "0043_ban_state"),
("users", "0044_user_ssh_public_key"),
("users", "0045_merge"),
("users", "0046_auto_20170617_1433"),
("users", "0047_auto_20170618_0156"),
("users", "0048_auto_20170618_0210"),
("users", "0049_auto_20170618_1424"),
("users", "0050_serviceuser_comment"),
("users", "0051_user_telephone"),
("users", "0052_ldapuser_shadowexpire"),
("users", "0053_auto_20170626_2105"),
("users", "0054_auto_20170626_2219"),
("users", "0055_auto_20171003_0556"),
("users", "0056_auto_20171015_2033"),
("users", "0057_auto_20171023_0301"),
("users", "0058_auto_20171025_0154"),
("users", "0059_auto_20171025_1854"),
("users", "0060_auto_20171120_0317"),
("users", "0061_auto_20171230_2033"),
("users", "0062_auto_20171231_0056"),
("users", "0063_auto_20171231_0140"),
("users", "0064_auto_20171231_0150"),
("users", "0065_auto_20171231_2053"),
("users", "0066_grouppermissions"),
("users", "0067_serveurpermission"),
("users", "0068_auto_20180107_2245"),
("users", "0069_club_mailing"),
("users", "0070_auto_20180324_1906"),
("users", "0071_auto_20180415_1252"),
("users", "0072_auto_20180426_2021"),
("users", "0073_auto_20180629_1614"),
("users", "0074_auto_20180810_2104"),
("users", "0074_auto_20180814_1059"),
("users", "0075_merge_20180815_2202"),
("users", "0076_auto_20180818_1321"),
("users", "0077_auto_20180824_1750"),
("users", "0078_auto_20181011_1405"),
("users", "0079_auto_20181228_2039"),
("users", "0080_auto_20190108_1726"),
("users", "0081_auto_20190317_0302"),
("users", "0082_auto_20190908_1338"),
("users", "0083_user_shortcuts_enabled"),
("users", "0084_auto_20191120_0159"),
("users", "0085_user_email_state"),
("users", "0086_user_email_change_date"),
("users", "0087_request_email"),
("users", "0088_auto_20200417_2312"),
("users", "0089_auto_20200418_0112"),
("users", "0090_auto_20200421_1825"),
("users", "0091_auto_20200423_1256"),
("users", "0092_auto_20200502_0057"),
("users", "0093_user_profile_image"),
("users", "0094_remove_user_profile_image"),
("users", "0095_user_theme"),
("users", "0096_auto_20210110_1811"),
("cotisations", "0001_initial"),
("cotisations", "0002_remove_facture_article"),
("cotisations", "0003_auto_20160702_1448"),
("cotisations", "0004_auto_20160702_1528"),
("cotisations", "0005_auto_20160702_1532"),
("cotisations", "0006_auto_20160702_1534"),
("cotisations", "0007_auto_20160702_1543"),
("cotisations", "0008_auto_20160702_1614"),
("cotisations", "0009_remove_cotisation_user"),
("cotisations", "0010_auto_20160702_1840"),
("cotisations", "0011_auto_20160702_1911"),
("cotisations", "0012_auto_20160704_0118"),
("cotisations", "0013_auto_20160711_2240"),
("cotisations", "0014_auto_20160712_0245"),
("cotisations", "0015_auto_20160714_2142"),
("cotisations", "0016_auto_20160715_0110"),
("cotisations", "0017_auto_20170718_2329"),
("cotisations", "0018_paiement_type_paiement"),
("cotisations", "0019_auto_20170819_0055"),
("cotisations", "0020_auto_20170819_0057"),
("cotisations", "0021_auto_20170819_0104"),
("cotisations", "0022_auto_20170824_0128"),
("cotisations", "0023_auto_20170902_1303"),
("cotisations", "0024_auto_20171015_2033"),
("cotisations", "0025_article_type_user"),
("cotisations", "0026_auto_20171028_0126"),
("cotisations", "0027_auto_20171029_1156"),
("cotisations", "0028_auto_20171231_0007"),
("cotisations", "0029_auto_20180414_2056"),
("cotisations", "0030_custom_payment"),
("cotisations", "0031_comnpaypayment_production"),
("cotisations", "0032_custom_invoice"),
("cotisations", "0033_auto_20180818_1319"),
("cotisations", "0034_auto_20180831_1532"),
("cotisations", "0035_notepayment"),
("cotisations", "0036_custominvoice_remark"),
("cotisations", "0037_costestimate"),
("cotisations", "0038_auto_20181231_1657"),
("cotisations", "0039_freepayment"),
("cotisations", "0040_auto_20191002_2335"),
("cotisations", "0041_auto_20191103_2131"),
("cotisations", "0042_auto_20191120_0159"),
("cotisations", "0043_separation_membership_connection_p1"),
("cotisations", "0044_separation_membership_connection_p2"),
("cotisations", "0045_separation_membership_connection_p3"),
("cotisations", "0046_article_need_membership"),
("cotisations", "0047_article_need_membership_init"),
("cotisations", "0048_auto_20201017_0018"),
("cotisations", "0049_auto_20201102_2305"),
("cotisations", "0050_auto_20201102_2342"),
("cotisations", "0051_auto_20201228_1636"),
("machines", "0001_initial"),
("machines", "0002_auto_20160703_1444"),
("machines", "0003_auto_20160703_1450"),
("machines", "0004_auto_20160703_1451"),
("machines", "0005_auto_20160703_1523"),
("machines", "0006_auto_20160703_1813"),
("machines", "0007_auto_20160703_1816"),
("machines", "0008_remove_interface_ipv6"),
("machines", "0009_auto_20160703_2358"),
("machines", "0010_auto_20160704_0104"),
("machines", "0011_auto_20160704_0105"),
("machines", "0012_auto_20160704_0118"),
("machines", "0013_auto_20160705_1014"),
("machines", "0014_auto_20160706_1220"),
("machines", "0015_auto_20160707_0105"),
("machines", "0016_auto_20160708_1633"),
("machines", "0017_auto_20160708_1645"),
("machines", "0018_auto_20160708_1813"),
("machines", "0019_auto_20160718_1141"),
("machines", "0020_auto_20160718_1849"),
("machines", "0021_auto_20161006_1943"),
("machines", "0022_auto_20161011_1829"),
("machines", "0023_iplist_ip_type"),
("machines", "0024_machinetype_need_infra"),
("machines", "0025_auto_20161023_0038"),
("machines", "0026_auto_20161026_1348"),
("machines", "0027_alias"),
("machines", "0028_iptype_domaine_ip"),
("machines", "0029_iptype_domaine_range"),
("machines", "0030_auto_20161118_1730"),
("machines", "0031_auto_20161119_1709"),
("machines", "0032_auto_20161119_1850"),
("machines", "0033_extension_need_infra"),
("machines", "0034_iplist_need_infra"),
("machines", "0035_auto_20161224_1201"),
("machines", "0036_auto_20161224_1204"),
("machines", "0037_domain_cname"),
("machines", "0038_auto_20161224_1721"),
("machines", "0039_auto_20161224_1732"),
("machines", "0040_remove_interface_dns"),
("machines", "0041_remove_ns_interface"),
("machines", "0042_ns_ns"),
("machines", "0043_auto_20170721_0350"),
("machines", "0044_auto_20170808_0233"),
("machines", "0045_auto_20170808_0348"),
("machines", "0046_auto_20170808_1423"),
("machines", "0047_auto_20170809_0606"),
("machines", "0048_auto_20170823_2315"),
("machines", "0049_vlan"),
("machines", "0050_auto_20170826_0022"),
("machines", "0051_iptype_vlan"),
("machines", "0052_auto_20170828_2322"),
("machines", "0053_text"),
("machines", "0054_text_zone"),
("machines", "0055_nas"),
("machines", "0056_nas_port_access_mode"),
("machines", "0057_nas_autocapture_mac"),
("machines", "0058_auto_20171002_0350"),
("machines", "0059_iptype_prefix_v6"),
("machines", "0060_iptype_ouverture_ports"),
("machines", "0061_auto_20171015_2033"),
("machines", "0062_extension_origin_v6"),
("machines", "0063_auto_20171020_0040"),
("machines", "0064_auto_20171115_0253"),
("machines", "0065_auto_20171115_1514"),
("machines", "0066_srv"),
("machines", "0067_auto_20171116_0152"),
("machines", "0068_auto_20171116_0252"),
("machines", "0069_auto_20171116_0822"),
("machines", "0070_auto_20171231_1947"),
("machines", "0071_auto_20171231_2100"),
("machines", "0072_auto_20180108_1822"),
("machines", "0073_auto_20180128_2203"),
("machines", "0074_auto_20180129_0352"),
("machines", "0075_auto_20180130_0052"),
("machines", "0076_auto_20180130_1623"),
("machines", "0077_auto_20180409_2243"),
("machines", "0078_auto_20180415_1252"),
("machines", "0079_auto_20180416_0107"),
("machines", "0080_auto_20180502_2334"),
("machines", "0081_auto_20180521_1413"),
("machines", "0082_auto_20180525_2209"),
("machines", "0083_remove_duplicate_rights"),
("machines", "0084_dname"),
("machines", "0085_sshfingerprint"),
("machines", "0086_role"),
("machines", "0087_dnssec"),
("machines", "0088_iptype_prefix_v6_length"),
("machines", "0089_auto_20180805_1148"),
("machines", "0090_auto_20180805_1459"),
("machines", "0091_auto_20180806_2310"),
("machines", "0092_auto_20180807_0926"),
("machines", "0093_auto_20180807_1115"),
("machines", "0094_auto_20180815_1918"),
("machines", "0095_auto_20180919_2225"),
("machines", "0096_auto_20181013_1417"),
("machines", "0097_extension_dnssec"),
("machines", "0098_auto_20190102_1745"),
("machines", "0099_role_recursive_dns"),
("machines", "0100_auto_20190102_1753"),
("machines", "0101_auto_20190108_1623"),
("machines", "0102_auto_20190303_1611"),
("machines", "0103_auto_20191002_2222"),
("machines", "0104_auto_20191002_2231"),
("machines", "0105_dname_ttl"),
("machines", "0106_auto_20191120_0159"),
("machines", "0107_fix_lowercase_domain"),
("machines", "0108_ipv6list_active"),
("preferences", "0001_initial"),
("preferences", "0002_auto_20170625_1923"),
("preferences", "0003_optionaluser_solde_negatif"),
("preferences", "0004_assooption_services"),
("preferences", "0005_auto_20170824_0139"),
("preferences", "0006_auto_20170824_0143"),
("preferences", "0007_auto_20170824_2056"),
("preferences", "0008_auto_20170824_2122"),
("preferences", "0009_assooption_utilisateur_asso"),
("preferences", "0010_auto_20170825_0459"),
("preferences", "0011_auto_20170825_2307"),
("preferences", "0012_generaloption_req_expire_hrs"),
("preferences", "0013_generaloption_site_name"),
("preferences", "0014_generaloption_email_from"),
("preferences", "0015_optionaltopologie_radius_general_policy"),
("preferences", "0016_auto_20170902_1520"),
("preferences", "0017_mailmessageoption"),
("preferences", "0018_optionaltopologie_mac_autocapture"),
("preferences", "0019_remove_optionaltopologie_mac_autocapture"),
("preferences", "0020_optionalmachine_ipv6"),
("preferences", "0021_auto_20171015_1741"),
("preferences", "0022_auto_20171015_1758"),
("preferences", "0023_auto_20171015_2033"),
("preferences", "0024_optionaluser_all_can_create"),
("preferences", "0025_auto_20171231_2142"),
("preferences", "0025_generaloption_general_message"),
("preferences", "0026_auto_20171216_0401"),
("preferences", "0027_merge_20180106_2019"),
("preferences", "0028_assooption_description"),
("preferences", "0028_auto_20180111_1129"),
("preferences", "0028_auto_20180128_2203"),
("preferences", "0029_auto_20180111_1134"),
("preferences", "0029_auto_20180318_0213"),
("preferences", "0029_auto_20180318_1005"),
("preferences", "0030_auto_20180111_2346"),
("preferences", "0030_merge_20180320_1419"),
("preferences", "0031_auto_20180323_0218"),
("preferences", "0031_optionaluser_self_adhesion"),
("preferences", "0032_optionaluser_min_online_payment"),
("preferences", "0032_optionaluser_shell_default"),
("preferences", "0033_accueiloption"),
("preferences", "0033_generaloption_gtu_sum_up"),
("preferences", "0034_auto_20180114_2025"),
("preferences", "0034_auto_20180416_1120"),
("preferences", "0035_auto_20180114_2132"),
("preferences", "0035_optionaluser_allow_self_subscription"),
("preferences", "0036_auto_20180114_2141"),
("preferences", "0037_auto_20180114_2156"),
("preferences", "0038_auto_20180114_2209"),
("preferences", "0039_auto_20180115_0003"),
("preferences", "0040_auto_20180129_1745"),
("preferences", "0041_merge_20180130_0052"),
("preferences", "0042_auto_20180222_1743"),
("preferences", "0043_optionalmachine_create_machine"),
("preferences", "0044_remove_payment_pass"),
("preferences", "0045_remove_unused_payment_fields"),
("preferences", "0046_optionaluser_mail_extension"),
("preferences", "0047_mailcontact"),
("preferences", "0048_auto_20180811_1515"),
("preferences", "0049_optionaluser_self_change_shell"),
("preferences", "0050_auto_20180818_1329"),
("preferences", "0051_auto_20180919_2225"),
("preferences", "0052_optionaluser_delete_notyetactive"),
("preferences", "0053_optionaluser_self_change_room"),
("preferences", "0055_generaloption_main_site_url"),
("preferences", "0056_1_radiusoption"),
("preferences", "0056_2_radiusoption"),
("preferences", "0056_3_radiusoption"),
("preferences", "0056_4_radiusoption"),
("preferences", "0057_optionaluser_all_users_active"),
("preferences", "0058_auto_20190108_1650"),
("preferences", "0059_auto_20190120_1739"),
("preferences", "0060_auto_20190712_1821"),
("preferences", "0061_optionaluser_allow_archived_connexion"),
("preferences", "0062_auto_20190910_1909"),
("preferences", "0063_mandate"),
("preferences", "0064_auto_20191008_1335"),
("preferences", "0065_auto_20191010_1227"),
("preferences", "0066_optionalmachine_default_dns_ttl"),
("preferences", "0067_auto_20191120_0159"),
("preferences", "0068_optionaluser_allow_set_password_during_user_creation"),
("preferences", "0069_optionaluser_disable_emailnotyetconfirmed"),
("preferences", "0070_auto_20200419_0225"),
("preferences", "0071_optionaluser_self_change_pseudo"),
("topologie", "0001_initial"),
("topologie", "0002_auto_20160703_1118"),
("topologie", "0003_room"),
("topologie", "0004_auto_20160703_1122"),
("topologie", "0005_auto_20160703_1123"),
("topologie", "0006_auto_20160703_1129"),
("topologie", "0007_auto_20160703_1148"),
("topologie", "0008_port_room"),
("topologie", "0009_auto_20160703_1200"),
("topologie", "0010_auto_20160704_2148"),
("topologie", "0011_auto_20160704_2153"),
("topologie", "0012_port_machine_interface"),
("topologie", "0013_port_related"),
("topologie", "0014_auto_20160706_1238"),
("topologie", "0015_auto_20160706_1452"),
("topologie", "0016_auto_20160706_1531"),
("topologie", "0017_auto_20160718_1141"),
("topologie", "0018_room_details"),
("topologie", "0019_auto_20161026_1348"),
("topologie", "0020_auto_20161119_0033"),
("topologie", "0021_port_radius"),
("topologie", "0022_auto_20161211_1622"),
("topologie", "0023_auto_20170817_1654"),
("topologie", "0023_auto_20170826_1530"),
("topologie", "0024_auto_20170818_1021"),
("topologie", "0024_auto_20170826_1800"),
("topologie", "0025_merge_20170902_1242"),
("topologie", "0026_auto_20170902_1245"),
("topologie", "0027_auto_20170905_1442"),
("topologie", "0028_auto_20170913_1503"),
("topologie", "0029_auto_20171002_0334"),
("topologie", "0030_auto_20171004_0235"),
("topologie", "0031_auto_20171015_2033"),
("topologie", "0032_auto_20171026_0338"),
("topologie", "0033_auto_20171231_1743"),
("topologie", "0034_borne"),
("topologie", "0035_auto_20180324_0023"),
("topologie", "0036_transferborne"),
("topologie", "0037_auto_20180325_0127"),
("topologie", "0038_transfersw"),
("topologie", "0039_port_new_switch"),
("topologie", "0040_transferports"),
("topologie", "0041_transferportsw"),
("topologie", "0042_transferswitch"),
("topologie", "0043_renamenewswitch"),
("topologie", "0044_auto_20180326_0002"),
("topologie", "0045_auto_20180326_0123"),
("topologie", "0046_auto_20180326_0129"),
("topologie", "0047_ap_machine"),
("topologie", "0048_ap_machine"),
("topologie", "0049_switchs_machine"),
("topologie", "0050_port_new_switch"),
("topologie", "0051_switchs_machine"),
("topologie", "0052_transferports"),
("topologie", "0053_finalsw"),
("topologie", "0054_auto_20180326_1742"),
("topologie", "0055_auto_20180329_0431"),
("topologie", "0056_building_switchbay"),
("topologie", "0057_auto_20180408_0316"),
("topologie", "0058_remove_switch_location"),
("topologie", "0059_auto_20180415_2249"),
("topologie", "0060_server"),
("topologie", "0061_portprofile"),
("topologie", "0062_auto_20180815_1918"),
("topologie", "0063_auto_20180919_2225"),
("topologie", "0064_switch_automatic_provision"),
("topologie", "0065_auto_20180927_1836"),
("topologie", "0066_modelswitch_commercial_name"),
("topologie", "0067_auto_20181230_1819"),
("topologie", "0068_auto_20190102_1758"),
("topologie", "0069_auto_20190108_1439"),
("topologie", "0070_auto_20190218_1743"),
("topologie", "0071_auto_20190218_1936"),
("topologie", "0072_auto_20190720_2318"),
("topologie", "0073_auto_20191120_0159"),
("topologie", "0074_auto_20200419_1640"),
]
operations = [
migrations.AddField(
model_name='balancepayment',
name='payment',
field=models.OneToOneField(default=None, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method_balance', to='cotisations.Paiement'),
preserve_default=False,
),
migrations.AddField(
model_name='chequepayment',
name='payment',
field=models.OneToOneField(default=None, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method_cheque', to='cotisations.Paiement'),
preserve_default=False,
),
migrations.AddField(
model_name='comnpaypayment',
name='payment',
field=models.OneToOneField(default=None, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method_comnpay', to='cotisations.Paiement'),
preserve_default=False,
),
migrations.AddField(
model_name='costestimate',
name='final_invoice',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='origin_cost_estimate', to='cotisations.CustomInvoice'),
),
migrations.AddField(
model_name='cotisation',
name='vente',
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.CASCADE, to='cotisations.Vente', verbose_name='purchase'),
),
migrations.AddField(
model_name='facture',
name='banque',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='cotisations.Banque'),
),
migrations.AddField(
model_name='facture',
name='paiement',
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to='cotisations.Paiement'),
preserve_default=False,
),
migrations.AddField(
model_name='facture',
name='user',
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
preserve_default=False,
),
migrations.AddField(
model_name='freepayment',
name='payment',
field=models.OneToOneField(default=None, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method_free', to='cotisations.Paiement'),
preserve_default=False,
),
migrations.AddField(
model_name='notepayment',
name='payment',
field=models.OneToOneField(default=None, editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='payment_method_note', to='cotisations.Paiement'),
preserve_default=False,
),
migrations.AddField(
model_name='vente',
name='facture',
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='cotisations.BaseInvoice', verbose_name='invoice'),
preserve_default=False,
),
]

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2021-01-24 10:05
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0002_foreign_keys'),
]
operations = [
migrations.AlterModelOptions(
name='baseinvoice',
options={'permissions': (('view_baseinvoice', 'Can view an base invoice object'),)},
),
]

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -0,0 +1,117 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-09-20 17:19
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0042_auto_20191120_0159'),
]
operations = [
# migrations.RemoveField(
# model_name='article',
# name='duration',
# ),
# migrations.RemoveField(
# model_name='article',
# name='duration_days',
# ),
# migrations.RemoveField(
# model_name='article',
# name='type_cotisation',
# ),
# migrations.RemoveField(
# model_name='cotisation',
# name='date_end',
# ),
# migrations.RemoveField(
# model_name='cotisation',
# name='date_start',
# ),
# migrations.RemoveField(
# model_name='cotisation',
# name='type_cotisation',
# ),
# migrations.RemoveField(
# model_name='vente',
# name='duration',
# ),
# migrations.RemoveField(
# model_name='vente',
# name='duration_days',
# ),
# migrations.RemoveField(
# model_name='vente',
# name='type_cotisation',
# ),
migrations.AddField(
model_name='article',
name='duration_connection',
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the connection (in months)'),
),
migrations.AddField(
model_name='article',
name='duration_days_connection',
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the connection (in days, will be added to duration in months)'),
),
migrations.AddField(
model_name='article',
name='duration_days_membership',
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the membership (in days, will be added to duration in months)'),
),
migrations.AddField(
model_name='article',
name='duration_membership',
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the membership (in months)'),
),
migrations.AddField(
model_name='cotisation',
name='date_end_con',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='end date for the connection'),
preserve_default=False,
),
migrations.AddField(
model_name='cotisation',
name='date_end_memb',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='end date for the membership'),
preserve_default=False,
),
migrations.AddField(
model_name='cotisation',
name='date_start_con',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='start date for the connection'),
preserve_default=False,
),
migrations.AddField(
model_name='cotisation',
name='date_start_memb',
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='start date for the membership'),
preserve_default=False,
),
migrations.AddField(
model_name='vente',
name='duration_connection',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='duration of the connection (in months)'),
),
migrations.AddField(
model_name='vente',
name='duration_days_connection',
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the connection (in days, will be added to duration in months)'),
),
migrations.AddField(
model_name='vente',
name='duration_days_membership',
field=models.PositiveIntegerField(blank=True, null=True, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the membership (in days, will be added to duration in months)'),
),
migrations.AddField(
model_name='vente',
name='duration_membership',
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='duration of the membership (in months)'),
),
]

View file

@ -0,0 +1,140 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-09-20 17:19
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0043_separation_membership_connection_p1'),
]
def split_dates(apps, schema_editor):
db_alias = schema_editor.connection.alias
cotisation = apps.get_model("cotisations", "Cotisation")
cotisations = cotisation.objects.using(db_alias).all()
for cotis in cotisations:
cotis.date_start_con = cotis.date_start
cotis.date_start_memb = cotis.date_start
cotis.date_end_con = cotis.date_end
cotis.date_end_memb = cotis.date_end
if cotis.type_cotisation == 'Connexion':
cotis.date_end_memb = cotis.date_start
if cotis.type_cotisation == 'Adhesion':
cotis.date_end_con = cotis.date_start
cotis.save()
def split_duration_articles_and_ventes(apps, schema_editor):
def split_duration(e):
e.duration_membership = e.duration
e.duration_connection = e.duration
e.duration_days_membership = e.duration_days
e.duration_days_connection = e.duration_days
if e.type_cotisation == 'Connexion':
e.duration_membership = 0
e.duration_days_membership = 0
if e.type_cotisation == 'Adhesion':
e.duration_connection = 0
e.duration_days_connection = 0
e.save()
db_alias = schema_editor.connection.alias
article = apps.get_model("cotisations", "Article")
vente = apps.get_model("cotisations", "Vente")
for a in article.objects.using(db_alias).all():
split_duration(a)
for v in vente.objects.using(db_alias).all():
split_duration(v)
def unsplit_dates(apps, schema_editor):
db_alias = schema_editor.connection.alias
cotisation = apps.get_model("cotisations", "Cotisation")
cotisations = cotisation.objects.using(db_alias).all()
for cotis in cotisations:
connection = cotis.date_start_con != cotis.date_end_con
adhesion = cotis.date_start_memb != cotis.date_end_memb
cotis.date_start = cotis.date_start_con
cotis.date_end = max(cotis.date_end_con, cotis.date_end_memb)
if connection:
cotis.type_cotisation = 'Connexion'
if adhesion:
cotis.type_cotisation = 'Adhesion'
if connection and adhesion:
cotis.type_cotisation = 'All'
if not (connection or adhesion):
cotis.type_cotisation = None
cotis.save()
def unsplit_duration_articles_and_ventes(apps, schema_editor):
def unsplit_duration(e):
e.duration = max(e.duration_membership, e.duration_connection)
e.duration_days = max(e.duration_days_membership, e.duration_days_connection)
connection = not (((e.duration_connection == 0) or (e.duration_connection__isnull)) and \
((e.duration_days_connection == 0) or (e.duration_days_connection__isnull)))
membership = not (((e.duration_membership == 0) or (e.duration_membership__isnull)) and \
((e.duration_days_membership == 0) or (e.duration_days_membership__isnull)))
if connection:
e.type_cotisation = 'Connection'
if membership:
e.type_cotisation = 'Adhesion'
if connection and membership:
e.type_cotisation = 'All'
if not (connection or membership):
e.type_cotisation = None
e.save()
db_alias = schema_editor.connection.alias
article = apps.get_model("cotisations", "Article")
vente = apps.get_model("cotisations", "Vente")
for a in article.objects.using(db_alias).all():
unsplit_duration(a)
for v in vente.objects.using(db_alias).all():
unsplit_duration(v)
operations = [
migrations.RunPython(split_dates, unsplit_dates),
migrations.RunPython(split_duration_articles_and_ventes, unsplit_duration_articles_and_ventes),
# migrations.RemoveField(
# model_name='article',
# name='duration',
# ),
# migrations.RemoveField(
# model_name='article',
# name='duration_days',
# ),
# migrations.RemoveField(
# model_name='article',
# name='type_cotisation',
# ),
# migrations.RemoveField(
# model_name='cotisation',
# name='date_end',
# ),
# migrations.RemoveField(
# model_name='cotisation',
# name='date_start',
# ),
# migrations.RemoveField(
# model_name='cotisation',
# name='type_cotisation',
# ),
# migrations.RemoveField(
# model_name='vente',
# name='duration',
# ),
# migrations.RemoveField(
# model_name='vente',
# name='duration_days',
# ),
# migrations.RemoveField(
# model_name='vente',
# name='type_cotisation',
# ),
]

View file

@ -0,0 +1,53 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-09-20 17:19
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0044_separation_membership_connection_p2'),
]
operations = [
migrations.RemoveField(
model_name='article',
name='duration',
),
migrations.RemoveField(
model_name='article',
name='duration_days',
),
migrations.RemoveField(
model_name='article',
name='type_cotisation',
),
migrations.RemoveField(
model_name='cotisation',
name='date_end',
),
migrations.RemoveField(
model_name='cotisation',
name='date_start',
),
migrations.RemoveField(
model_name='cotisation',
name='type_cotisation',
),
migrations.RemoveField(
model_name='vente',
name='duration',
),
migrations.RemoveField(
model_name='vente',
name='duration_days',
),
migrations.RemoveField(
model_name='vente',
name='type_cotisation',
),
]

View file

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-09-25 16:45
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0045_separation_membership_connection_p3'),
]
operations = [
migrations.AddField(
model_name='article',
name='need_membership',
field=models.BooleanField(default=True, verbose_name='can be purcharsed without membership'),
),
]

View file

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-09-25 16:45
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0046_article_need_membership'),
]
def init_need_membership(apps, schema_editor):
db_alias = schema_editor.connection.alias
article = apps.get_model("cotisations", "Article")
articles = article.objects.using(db_alias).all()
for art in articles:
v = False
v = v or bool(art.duration_membership)
v = v or bool(art.duration_days_membership)
v = v or not (bool(art.duration_connection) or bool(art.duration_days_connection))
art.need_membership = v
art.save()
operations = [
migrations.RunPython(init_need_membership, lambda *args, **kargs: None),
]

View file

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-10-16 22:18
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0047_article_need_membership_init'),
]
def set_value_to_zero(apps, schema_editor):
db_alias = schema_editor.connection.alias
Vente = apps.get_model("cotisations", "Vente")
Vente.objects.using(db_alias).filter(duration_connection__isnull=True).update(duration_connection=0)
Vente.objects.using(db_alias).filter(duration_days_connection__isnull=True).update(duration_days_connection=0)
Vente.objects.using(db_alias).filter(duration_membership__isnull=True).update(duration_membership=0)
Vente.objects.using(db_alias).filter(duration_days_membership__isnull=True).update(duration_days_membership=0)
operations = [
migrations.RunPython(set_value_to_zero),
]

View file

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-11-02 22:05
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0048_auto_20201017_0018'),
]
operations = [
migrations.AlterField(
model_name='article',
name='need_membership',
field=models.BooleanField(default=True, verbose_name='need membership to be purchased'),
),
migrations.AlterField(
model_name='vente',
name='duration_connection',
field=models.PositiveIntegerField(default=0, verbose_name='duration of the connection (in months)'),
),
migrations.AlterField(
model_name='vente',
name='duration_days_connection',
field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the connection (in days, will be added to duration in months)'),
),
migrations.AlterField(
model_name='vente',
name='duration_days_membership',
field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the membership (in days, will be added to duration in months)'),
),
migrations.AlterField(
model_name='vente',
name='duration_membership',
field=models.PositiveIntegerField(default=0, verbose_name='duration of the membership (in months)'),
),
]

View file

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-11-02 22:42
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cotisations', '0049_auto_20201102_2305'),
]
operations = [
migrations.AlterField(
model_name='article',
name='duration_connection',
field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the connection (in months)'),
preserve_default=False,
),
migrations.AlterField(
model_name='article',
name='duration_days_connection',
field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the connection (in days, will be added to duration in months)'),
preserve_default=False,
),
migrations.AlterField(
model_name='article',
name='duration_days_membership',
field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the membership (in days, will be added to duration in months)'),
preserve_default=False,
),
migrations.AlterField(
model_name='article',
name='duration_membership',
field=models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)], verbose_name='duration of the membership (in months)'),
preserve_default=False,
),
]

View file

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.29 on 2020-12-28 15:36
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("cotisations", "0050_auto_20201102_2342"),
]
operations = [
migrations.AlterField(
model_name="article",
name="duration_connection",
field=models.PositiveIntegerField(
verbose_name="duration of the connection (in months)"
),
),
migrations.AlterField(
model_name="article",
name="duration_days_connection",
field=models.PositiveIntegerField(
verbose_name="duration of the connection (in days, will be added to duration in months)"
),
),
migrations.AlterField(
model_name="article",
name="duration_days_membership",
field=models.PositiveIntegerField(
verbose_name="duration of the membership (in days, will be added to duration in months)"
),
),
migrations.AlterField(
model_name="article",
name="duration_membership",
field=models.PositiveIntegerField(
verbose_name="duration of the membership (in months)"
),
),
migrations.AlterField(
model_name="vente",
name="duration_days_connection",
field=models.PositiveIntegerField(
default=0,
verbose_name="duration of the connection (in days, will be added to duration in months)",
),
),
migrations.AlterField(
model_name="vente",
name="duration_days_membership",
field=models.PositiveIntegerField(
default=0,
verbose_name="duration of the membership (in days, will be added to duration in months)",
),
),
]

View file

@ -1,4 +1,4 @@
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
@ -50,6 +50,8 @@ from preferences.models import CotisationsOption
from machines.models import regen from machines.models import regen
from re2o.field_permissions import FieldPermissionModelMixin from re2o.field_permissions import FieldPermissionModelMixin
from re2o.mixins import AclMixin, RevMixin from re2o.mixins import AclMixin, RevMixin
import users.signals
import users.models
from cotisations.utils import find_payment_method, send_mail_invoice, send_mail_voucher from cotisations.utils import find_payment_method, send_mail_invoice, send_mail_voucher
from cotisations.validators import check_no_balance from cotisations.validators import check_no_balance
@ -58,6 +60,12 @@ from cotisations.validators import check_no_balance
class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model): class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
date = models.DateTimeField(auto_now_add=True, verbose_name=_("date")) date = models.DateTimeField(auto_now_add=True, verbose_name=_("date"))
class Meta:
abstract = False
permissions = (
("view_baseinvoice", _("Can view an base invoice object")),
)
# TODO : change prix to price # TODO : change prix to price
def prix(self): def prix(self):
""" """
@ -96,6 +104,16 @@ class BaseInvoice(RevMixin, AclMixin, FieldPermissionModelMixin, models.Model):
) )
return name return name
def name_detailed(self):
"""
Return:
- a list of strings with the name of all article in the invoice
and their quantity.
"""
ventes = self.vente_set.all()
strings = ["{} x {}".format(v.number, v.name) for v in ventes]
return strings
# TODO : change facture to invoice # TODO : change facture to invoice
class Facture(BaseInvoice): class Facture(BaseInvoice):
@ -138,7 +156,7 @@ class Facture(BaseInvoice):
abstract = False abstract = False
permissions = ( permissions = (
# TODO : change facture to invoice # TODO : change facture to invoice
("change_facture_control", _("Can edit the \"controlled\" state")), ("change_facture_control", _('Can edit the "controlled" state')),
("view_facture", _("Can view an invoice object")), ("view_facture", _("Can view an invoice object")),
("change_all_facture", _("Can edit all the previous invoices")), ("change_all_facture", _("Can edit all the previous invoices")),
) )
@ -166,7 +184,7 @@ class Facture(BaseInvoice):
return ( return (
False, False,
_("You don't have the right to edit this user's invoices."), _("You don't have the right to edit this user's invoices."),
("cotisations.change_all_facture",) + permissions, ("cotisations.change_all_facture",) + (permissions or ()),
) )
elif not user_request.has_perm("cotisations.change_all_facture") and ( elif not user_request.has_perm("cotisations.change_all_facture") and (
self.control or not self.valid self.control or not self.valid
@ -198,7 +216,7 @@ class Facture(BaseInvoice):
return ( return (
False, False,
_("You don't have the right to delete this user's invoices."), _("You don't have the right to delete this user's invoices."),
("cotisations.change_all_facture",) + permissions, ("cotisations.change_all_facture",) + (permissions or ()),
) )
elif not user_request.has_perm("cotisations.change_all_facture") and ( elif not user_request.has_perm("cotisations.change_all_facture") and (
self.control or not self.valid self.control or not self.valid
@ -238,12 +256,12 @@ class Facture(BaseInvoice):
@staticmethod @staticmethod
def can_change_control(user_request, *_args, **_kwargs): def can_change_control(user_request, *_args, **_kwargs):
""" Returns True if the user can change the 'controlled' status of """Returns True if the user can change the 'controlled' status of
this invoice """ this invoice"""
can = user_request.has_perm("cotisations.change_facture_control") can = user_request.has_perm("cotisations.change_facture_control")
return ( return (
can, can,
_("You don't have the right to edit the \"controlled\" state.") _('You don\'t have the right to edit the "controlled" state.')
if not can if not can
else None, else None,
("cotisations.change_facture_control",), ("cotisations.change_facture_control",),
@ -283,7 +301,7 @@ class Facture(BaseInvoice):
"""Returns every subscription associated with this invoice.""" """Returns every subscription associated with this invoice."""
return Cotisation.objects.filter( return Cotisation.objects.filter(
vente__in=self.vente_set.filter( vente__in=self.vente_set.filter(
Q(type_cotisation="All") | Q(type_cotisation="Adhesion") ~(Q(duration_membership=0)) | ~(Q(duration_days_membership=0))
) )
) )
@ -297,42 +315,33 @@ class Facture(BaseInvoice):
for purchase in self.vente_set.all(): for purchase in self.vente_set.all():
if hasattr(purchase, "cotisation"): if hasattr(purchase, "cotisation"):
cotisation = purchase.cotisation cotisation = purchase.cotisation
if cotisation.type_cotisation == 'Connexion': cotisation.date_start_con = date_con
cotisation.date_start = date_con date_con += relativedelta(
date_con += relativedelta( months=(purchase.duration_connection or 0) * purchase.number,
months=(purchase.duration or 0) * purchase.number, days=(purchase.duration_days_connection or 0) * purchase.number,
days=(purchase.duration_days or 0) * purchase.number, )
) cotisation.date_end_con = date_con
cotisation.date_end = date_con cotisation.date_start_memb = date_adh
elif cotisation.type_cotisation == 'Adhesion': date_adh += relativedelta(
cotisation.date_start = date_adh months=(purchase.duration_membership or 0) * purchase.number,
date_adh += relativedelta( days=(purchase.duration_days_membership or 0) * purchase.number,
months=(purchase.duration or 0) * purchase.number, )
days=(purchase.duration_days or 0) * purchase.number, cotisation.date_end_memb = date_adh
)
cotisation.date_end = date_adh
else: # it is assumed that adhesion is required for a connexion
date = min(date_adh, date_con)
cotisation.date_start = date
date_adh += relativedelta(
months=(purchase.duration or 0) * purchase.number,
days=(purchase.duration_days or 0) * purchase.number,
)
date_con += relativedelta(
months=(purchase.duration or 0) * purchase.number,
days=(purchase.duration_days or 0) * purchase.number,
)
date = max(date_adh, date_con)
cotisation.date_end = date
cotisation.save() cotisation.save()
purchase.facture = self purchase.facture = self
purchase.save() purchase.save()
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
try:
request = kwargs.pop("request")
except:
request = None
super(Facture, self).save(*args, **kwargs) super(Facture, self).save(*args, **kwargs)
if not self.__original_valid and self.valid: if not self.__original_valid and self.valid:
self.reorder_purchases() self.reorder_purchases()
send_mail_invoice(self) send_mail_invoice(self, request)
if ( if (
self.is_subscription() self.is_subscription()
and not self.__original_control and not self.__original_control
@ -340,7 +349,7 @@ class Facture(BaseInvoice):
and CotisationsOption.get_cached_value("send_voucher_mail") and CotisationsOption.get_cached_value("send_voucher_mail")
and self.user.is_adherent() and self.user.is_adherent()
): ):
send_mail_voucher(self) send_mail_voucher(self, request)
def __str__(self): def __str__(self):
return str(self.user) + " " + str(self.date) return str(self.user) + " " + str(self.date)
@ -355,7 +364,7 @@ def facture_post_save(**kwargs):
if facture.valid: if facture.valid:
user = facture.user user = facture.user
user.set_active() user.set_active()
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=True, mac_refresh=False)
@receiver(post_delete, sender=Facture) @receiver(post_delete, sender=Facture)
@ -364,7 +373,7 @@ def facture_post_delete(**kwargs):
Synchronise the LDAP user after an invoice has been deleted. Synchronise the LDAP user after an invoice has been deleted.
""" """
user = kwargs["instance"].user user = kwargs["instance"].user
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) users.signals.synchronise.send(sender=users.models.User, instance=user, base=False, access_refresh=True, mac_refresh=False)
class CustomInvoice(BaseInvoice): class CustomInvoice(BaseInvoice):
@ -444,13 +453,6 @@ class Vente(RevMixin, AclMixin, models.Model):
the effect of the purchase on the time agreed for this user) the effect of the purchase on the time agreed for this user)
""" """
# TODO : change this to English
COTISATION_TYPE = (
("Connexion", _("Connection")),
("Adhesion", _("Membership")),
("All", _("Both of them")),
)
# TODO : change facture to invoice # TODO : change facture to invoice
facture = models.ForeignKey( facture = models.ForeignKey(
"BaseInvoice", on_delete=models.CASCADE, verbose_name=_("invoice") "BaseInvoice", on_delete=models.CASCADE, verbose_name=_("invoice")
@ -460,27 +462,30 @@ class Vente(RevMixin, AclMixin, models.Model):
validators=[MinValueValidator(1)], verbose_name=_("amount") validators=[MinValueValidator(1)], verbose_name=_("amount")
) )
# TODO : change this field for a ForeinKey to Article # TODO : change this field for a ForeinKey to Article
# Note: With a foreign key, modifing an Article modifis the Purchase, wich is bad.
# To use a foreign key, you need to make Article read only
name = models.CharField(max_length=255, verbose_name=_("article")) name = models.CharField(max_length=255, verbose_name=_("article"))
# TODO : change prix to price # TODO : change prix to price
# TODO : this field is not needed if you use Article ForeignKey # TODO : this field is not needed if you use Article ForeignKey
prix = models.DecimalField(max_digits=5, decimal_places=2, verbose_name=_("price")) prix = models.DecimalField(max_digits=5, decimal_places=2, verbose_name=_("price"))
# TODO : this field is not needed if you use Article ForeignKey # TODO : this field is not needed if you use Article ForeignKey
duration = models.PositiveIntegerField( duration_connection = models.PositiveIntegerField(
blank=True, null=True, verbose_name=_("duration (in months)") default=0, verbose_name=_("duration of the connection (in months)")
) )
duration_days = models.PositiveIntegerField( duration_days_connection = models.PositiveIntegerField(
blank=True, default=0,
null=True, verbose_name=_(
validators=[MinValueValidator(0)], "duration of the connection (in days, will be added to duration in months)"
verbose_name=_("duration (in days, will be added to duration in months)"), ),
) )
# TODO : this field is not needed if you use Article ForeignKey duration_membership = models.PositiveIntegerField(
type_cotisation = models.CharField( default=0, verbose_name=_("duration of the membership (in months)")
choices=COTISATION_TYPE, )
blank=True, duration_days_membership = models.PositiveIntegerField(
null=True, default=0,
max_length=255, verbose_name=_(
verbose_name=_("subscription type"), "duration of the membership (in days, will be added to duration in months)"
),
) )
class Meta: class Meta:
@ -505,34 +510,48 @@ class Vente(RevMixin, AclMixin, models.Model):
""" """
if hasattr(self, "cotisation"): if hasattr(self, "cotisation"):
cotisation = self.cotisation cotisation = self.cotisation
cotisation.date_end = cotisation.date_start + relativedelta( cotisation.date_end_memb = cotisation.date_start_memb + relativedelta(
months=(self.duration or 0) * self.number, months=(self.duration_membership or 0) * self.number,
days=(self.duration_days or 0) * self.number, days=(self.duration_days_membership or 0) * self.number,
)
cotisation.date_end_con = cotisation.date_start_con + relativedelta(
months=(self.duration_connection or 0) * self.number,
days=(self.duration_days_connection or 0) * self.number,
) )
return return
def create_cotis(self, date_start=False): def create_cotis(self, date_start_con=False, date_start_memb=False):
""" """
Creates a cotisation without initializing the dates (start and end ar set to self.facture.facture.date) and without saving it. You should use Facture.reorder_purchases to set the right dates. Creates a cotisation without initializing the dates (start and end ar set to self.facture.facture.date)
and without saving it. You should use Facture.reorder_purchases to set the right dates.
""" """
try: try:
invoice = self.facture.facture invoice = self.facture.facture
except Facture.DoesNotExist: except Facture.DoesNotExist:
return return
if not hasattr(self, "cotisation") and self.type_cotisation: if not hasattr(self, "cotisation") and self.test_membership_or_connection():
cotisation = Cotisation(vente=self) cotisation = Cotisation(vente=self)
cotisation.type_cotisation = self.type_cotisation if date_start_con:
if date_start: cotisation.date_start_con = date_start_con
cotisation.date_start = date_start cotisation.date_end_con = cotisation.date_start_con + relativedelta(
cotisation.date_end = cotisation.date_start + relativedelta( months=(self.duration_connection or 0) * self.number,
months=(self.duration or 0) * self.number, days=(self.duration_days_connection or 0) * self.number,
days=(self.duration_days or 0) * self.number, )
self.save()
cotisation.save()
if date_start_memb:
cotisation.date_start_memb = date_start_memb
cotisation.date_end_memb = cotisation.date_start_memb + relativedelta(
months=(self.duration_membership or 0) * self.number,
days=(self.duration_days_membership or 0) * self.number,
) )
self.save() self.save()
cotisation.save() cotisation.save()
else: else:
cotisation.date_start = invoice.date cotisation.date_start_con = invoice.date
cotisation.date_end = invoice.date cotisation.date_start_memb = invoice.date
cotisation.date_end_con = invoice.date
cotisation.date_end_memb = invoice.date
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
""" """
@ -540,9 +559,6 @@ class Vente(RevMixin, AclMixin, models.Model):
It also update the associated cotisation in the changes have some It also update the associated cotisation in the changes have some
effect on the user's cotisation effect on the user's cotisation
""" """
# Checking that if a cotisation is specified, there is also a duration
if self.type_cotisation and not (self.duration or self.duration_days):
raise ValidationError(_("Duration must be specified for a subscription."))
self.update_cotisation() self.update_cotisation()
super(Vente, self).save(*args, **kwargs) super(Vente, self).save(*args, **kwargs)
@ -560,7 +576,7 @@ class Vente(RevMixin, AclMixin, models.Model):
return ( return (
False, False,
_("You don't have the right to edit this user's purchases."), _("You don't have the right to edit this user's purchases."),
("cotisations.change_all_facture",) + permissions, ("cotisations.change_all_facture",) + (permissions or ()),
) )
elif not user_request.has_perm("cotisations.change_all_vente") and ( elif not user_request.has_perm("cotisations.change_all_vente") and (
self.facture.control or not self.facture.valid self.facture.control or not self.facture.valid
@ -623,6 +639,15 @@ class Vente(RevMixin, AclMixin, models.Model):
def __str__(self): def __str__(self):
return str(self.name) + " " + str(self.facture) return str(self.name) + " " + str(self.facture)
def test_membership_or_connection(self):
"""Test if the purchase include membership or connecton"""
return (
self.duration_membership
or self.duration_days_membership
or self.duration_connection
or self.duration_days_connection
)
# TODO : change vente to purchase # TODO : change vente to purchase
@receiver(post_save, sender=Vente) @receiver(post_save, sender=Vente)
@ -639,12 +664,12 @@ def vente_post_save(**kwargs):
if hasattr(purchase, "cotisation"): if hasattr(purchase, "cotisation"):
purchase.cotisation.vente = purchase purchase.cotisation.vente = purchase
purchase.cotisation.save() purchase.cotisation.save()
if purchase.type_cotisation: if purchase.test_membership_or_connection():
purchase.create_cotis() purchase.create_cotis()
purchase.cotisation.save() purchase.cotisation.save()
user = purchase.facture.facture.user user = purchase.facture.facture.user
user.set_active() user.set_active()
user.ldap_sync(base=True, access_refresh=True, mac_refresh=False) users.signals.synchronise.send(sender=users.models.User, instance=user, base=True, access_refresh=True, mac_refresh=False)
# TODO : change vente to purchase # TODO : change vente to purchase
@ -660,7 +685,7 @@ def vente_post_delete(**kwargs):
return return
if purchase.type_cotisation: if purchase.type_cotisation:
user = invoice.user user = invoice.user
user.ldap_sync(base=False, access_refresh=True, mac_refresh=False) users.signals.synchronise.send(sender=users.models.User, instance=user, base=True, access_refresh=True, mac_refresh=False)
class Article(RevMixin, AclMixin, models.Model): class Article(RevMixin, AclMixin, models.Model):
@ -671,56 +696,52 @@ class Article(RevMixin, AclMixin, models.Model):
It's represented by: It's represented by:
* a name * a name
* a price * a price
* a cotisation type (indicating if this article reprensents a * a duration for the membership
cotisation or not) * a duration for the connection
* a duration (if it is a cotisation) * if the article can be purchased without membership
* a type of user (indicating what kind of user can buy this article) * a type of user (indicating what kind of user can buy this article)
""" """
# TODO : Either use TYPE or TYPES in both choices but not both
USER_TYPES = ( USER_TYPES = (
("Adherent", _("Member")), ("Adherent", _("Member")),
("Club", _("Club")), ("Club", _("Club")),
("All", _("Both of them")), ("All", _("Both of them")),
) )
COTISATION_TYPE = (
("Connexion", _("Connection")),
("Adhesion", _("Membership")),
("All", _("Both of them")),
)
name = models.CharField(max_length=255, verbose_name=_("designation")) name = models.CharField(max_length=255, verbose_name=_("designation"))
# TODO : change prix to price # TODO : change prix to price
prix = models.DecimalField( prix = models.DecimalField(
max_digits=5, decimal_places=2, verbose_name=_("unit price") max_digits=5, decimal_places=2, verbose_name=_("unit price")
) )
duration = models.PositiveIntegerField(
blank=True, duration_membership = models.PositiveIntegerField(
null=True, verbose_name=_("duration of the membership (in months)")
validators=[MinValueValidator(0)],
verbose_name=_("duration (in months)"),
) )
duration_days = models.PositiveIntegerField( duration_days_membership = models.PositiveIntegerField(
blank=True, verbose_name=_(
null=True, "duration of the membership (in days, will be added to duration in months)"
validators=[MinValueValidator(0)], ),
verbose_name=_("duration (in days, will be added to duration in months)"),
) )
duration_connection = models.PositiveIntegerField(
verbose_name=_("duration of the connection (in months)")
)
duration_days_connection = models.PositiveIntegerField(
verbose_name=_(
"duration of the connection (in days, will be added to duration in months)"
),
)
need_membership = models.BooleanField(
default=True,
verbose_name=_("need membership to be purchased"),
)
type_user = models.CharField( type_user = models.CharField(
choices=USER_TYPES, choices=USER_TYPES,
default="All", default="All",
max_length=255, max_length=255,
verbose_name=_("type of users concerned"), verbose_name=_("type of users concerned"),
) )
type_cotisation = models.CharField(
choices=COTISATION_TYPE,
default=None,
blank=True,
null=True,
max_length=255,
verbose_name=_("subscription type"),
)
available_for_everyone = models.BooleanField( available_for_everyone = models.BooleanField(
default=False, verbose_name=_("is available for every user") default=False, verbose_name=_("is available for every user")
) )
@ -738,8 +759,6 @@ class Article(RevMixin, AclMixin, models.Model):
def clean(self): def clean(self):
if self.name.lower() == "solde": if self.name.lower() == "solde":
raise ValidationError(_("Solde is a reserved article name.")) raise ValidationError(_("Solde is a reserved article name."))
if self.type_cotisation and not (self.duration or self.duration_days):
raise ValidationError(_("Duration must be specified for a subscription."))
def __str__(self): def __str__(self):
return self.name return self.name
@ -772,7 +791,7 @@ class Article(RevMixin, AclMixin, models.Model):
Args: Args:
user: The user requesting articles. user: The user requesting articles.
target_user: The user to sell articles target_user: The user to sell articles to
""" """
if target_user is None: if target_user is None:
objects_pool = cls.objects.all() objects_pool = cls.objects.all()
@ -784,7 +803,9 @@ class Article(RevMixin, AclMixin, models.Model):
) )
if target_user is not None and not target_user.is_adherent(): if target_user is not None and not target_user.is_adherent():
objects_pool = objects_pool.filter( objects_pool = objects_pool.filter(
Q(type_cotisation="All") | Q(type_cotisation="Adhesion") Q(duration_membership__gt=0)
| Q(duration_days_membership__gt=0)
| Q(need_membership=False)
) )
if user.has_perm("cotisations.buy_every_article"): if user.has_perm("cotisations.buy_every_article"):
return objects_pool return objects_pool
@ -870,11 +891,13 @@ class Paiement(RevMixin, AclMixin, models.Model):
# So make this invoice valid, trigger send mail # So make this invoice valid, trigger send mail
invoice.valid = True invoice.valid = True
invoice.save() invoice.save(request=request)
# In case a cotisation was bought, inform the user, the # In case a cotisation was bought, inform the user, the
# cotisation time has been extended too # cotisation time has been extended too
if any(sell.type_cotisation for sell in invoice.vente_set.all()): if any(
sell.test_membership_or_connection() for sell in invoice.vente_set.all()
):
messages.success( messages.success(
request, request,
_( _(
@ -935,31 +958,25 @@ class Cotisation(RevMixin, AclMixin, models.Model):
The model defining a cotisation. It holds information about the time a user The model defining a cotisation. It holds information about the time a user
is allowed when he has paid something. is allowed when he has paid something.
It characterised by : It characterised by :
* a date_start (the date when the cotisaiton begins/began * a date_start_memb (the date when the membership begins/began
* a date_end (the date when the cotisation ends/ended * a date_end_memb (the date when the membership ends/ended
* a type of cotisation (which indicates the implication of such * a date_start_con (the date when the connection begins/began)
cotisation) * a date_end_con (the date when the connection ends/ended)
* a purchase (the related objects this cotisation is linked to) * a purchase (the related objects this cotisation is linked to)
""" """
COTISATION_TYPE = (
("Connexion", _("Connection")),
("Adhesion", _("Membership")),
("All", _("Both of them")),
)
# TODO : change vente to purchase # TODO : change vente to purchase
vente = models.OneToOneField( vente = models.OneToOneField(
"Vente", on_delete=models.CASCADE, null=True, verbose_name=_("purchase") "Vente", on_delete=models.CASCADE, null=True, verbose_name=_("purchase")
) )
type_cotisation = models.CharField( date_start_con = models.DateTimeField(
choices=COTISATION_TYPE, verbose_name=_("start date for the connection")
max_length=255,
default="All",
verbose_name=_("subscription type"),
) )
date_start = models.DateTimeField(verbose_name=_("start date")) date_end_con = models.DateTimeField(verbose_name=_("end date for the connection"))
date_end = models.DateTimeField(verbose_name=_("end date")) date_start_memb = models.DateTimeField(
verbose_name=_("start date for the membership")
)
date_end_memb = models.DateTimeField(verbose_name=_("end date for the membership"))
class Meta: class Meta:
permissions = ( permissions = (
@ -1029,9 +1046,14 @@ class Cotisation(RevMixin, AclMixin, models.Model):
return ( return (
str(self.vente) str(self.vente)
+ "from " + "from "
+ str(self.date_start) + str(self.date_start_memb)
+ " to " + " to "
+ str(self.date_end) + str(self.date_end_memb)
+ " for membership, "
+ str(self.date_start_con)
+ " to "
+ str(self.date_end_con)
+ " for the connection."
) )

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #
@ -55,7 +55,7 @@ def accept_payment(request, factureid):
) )
# In case a cotisation was bought, inform the user, the # In case a cotisation was bought, inform the user, the
# cotisation time has been extended too # cotisation time has been extended too
if any(purchase.type_cotisation for purchase in invoice.vente_set.all()): if any(purchase.test_membership_or_connection() for purchase in invoice.vente_set.all()):
messages.success( messages.success(
request, request,
_( _(

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
# -*- mode: python; coding: utf-8 -*- # -*- mode: python; coding: utf-8 -*-
# Re2o est un logiciel d'administration développé initiallement au rezometz. Il # Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
# se veut agnostique au réseau considéré, de manière à être installable en # se veut agnostique au réseau considéré, de manière à être installable en
# quelques clics. # quelques clics.
# #

View file

@ -1,5 +1,5 @@
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
@ -32,9 +32,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<tr> <tr>
<th>{% trans "Article" %}</th> <th>{% trans "Article" %}</th>
<th>{% trans "Price" %}</th> <th>{% trans "Price" %}</th>
<th>{% trans "Subscription type" %}</th> <th>{% trans "Duration membership (in months)" %}</th>
<th>{% trans "Duration (in months)" %}</th> <th>{% trans "Duration membership (in days)" %}</th>
<th>{% trans "Duration (in days)" %}</th> <th>{% trans "Duration connection (in months)" %}</th>
<th>{% trans "Duration connection (in days)" %}</th>
<th>{% trans "Concerned users" %}</th> <th>{% trans "Concerned users" %}</th>
<th>{% trans "Available for everyone" %}</th> <th>{% trans "Available for everyone" %}</th>
<th></th> <th></th>
@ -44,9 +45,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<tr> <tr>
<td>{{ article.name }}</td> <td>{{ article.name }}</td>
<td>{{ article.prix }}</td> <td>{{ article.prix }}</td>
<td>{{ article.type_cotisation }}</td> <td>{{ article.duration_membership }}</td>
<td>{{ article.duration }}</td> <td>{{ article.duration_days_membership }}</td>
<td>{{ article.duration_days }}</td> <td>{{ article.duration_connection }}</td>
<td>{{ article.duration_days_connection }}</td>
<td>{{ article.type_user }}</td> <td>{{ article.type_user }}</td>
<td>{{ article.available_for_everyone | tick }}</td> <td>{{ article.available_for_everyone | tick }}</td>
<td class="text-right"> <td class="text-right">

View file

@ -1,5 +1,5 @@
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,5 +1,5 @@
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
@ -73,9 +73,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
<td>{{ estimate.id }}</td> <td>{{ estimate.id }}</td>
<td> <td>
{% if estimate.final_invoice %} {% if estimate.final_invoice %}
<a href="{% url 'cotisations:edit-custom-invoice' estimate.final_invoice.pk %}"><i style="color: #1ECA18;" class="fa fa-check"></i></a> <a href="{% url 'cotisations:edit-custom-invoice' estimate.final_invoice.pk %}"><i class="fa fa-check text-success"></i></a>
{% else %} {% else %}
<i style="color: #D10115;" class="fa fa-times"></i> <i class="fa fa-times text-danger"></i>
{% endif %} {% endif %}
</td> </td>
<td> <td>

View file

@ -1,5 +1,5 @@
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
@ -59,7 +59,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% for facture in facture_list %} {% for facture in facture_list %}
<tr> <tr>
<td><a href="{% url 'users:profil' userid=facture.user.id %}">{{ facture.user }}</a></td> <td><a href="{% url 'users:profil' userid=facture.user.id %}">{{ facture.user }}</a></td>
<td>{{ facture.name }}</td> <td>
<table class="table-borderless">
{% for article in facture.name_detailed %}
<tr>
<td>
{{ article }}
</td>
</tr>
{% endfor %}
</table>
</td>
<td>{{ facture.prix_total }}</td> <td>{{ facture.prix_total }}</td>
<td>{{ facture.paiement }}</td> <td>{{ facture.paiement }}</td>
<td>{{ facture.date }}</td> <td>{{ facture.date }}</td>

View file

@ -1,5 +1,5 @@
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,5 +1,5 @@
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -0,0 +1,61 @@
{% comment %}
Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en
quelques clics.
Copyright © 2017 Gabriel Détraz
Copyright © 2017 Lara Kermarec
Copyright © 2017 Augustin Lemesle
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
{% endcomment %}
{% load acl %}
{% load i18n %}
{% load logs_extra %}
<div class="panel panel-default">
<div class="panel-heading clearfix profil" data-parent="#accordion" data-toggle="collapse"
data-target="#subscriptions">
<h3 class="panel-title pull-left">
<i class="fa fa-eur"></i>
{% trans "Subscriptions" %}
</h3>
</div>
<div id="subscriptions" class="panel-collapse collapse">
<div class="panel-body">
{% can_create Facture %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:new-facture' users.id %}">
<i class="fa fa-eur"></i>
{% trans "Add a subscription" %}
</a>
{% if user_solde %}
<a class="btn btn-primary btn-sm" role="button" href="{% url 'cotisations:credit-solde' users.id %}">
<i class="fa fa-eur"></i>
{% trans "Edit the balance" %}
</a>
{% endif%}
{% acl_end %}
</div>
<div class="panel-body">
{% if facture_list %}
{% include 'cotisations/aff_cotisations.html' with facture_list=facture_list %}
{% else %}
<p>{% trans "No invoice" %}</p>
{% endif %}
</div>
</div>
</div>

View file

@ -1,6 +1,6 @@
{% extends 'cotisations/sidebar.html' %} {% extends 'cotisations/sidebar.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,6 +1,6 @@
{% extends 'cotisations/sidebar.html' %} {% extends 'cotisations/sidebar.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,6 +1,6 @@
{% extends 'cotisations/sidebar.html' %} {% extends 'cotisations/sidebar.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
@ -25,13 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load bootstrap3 %} {% load bootstrap3 %}
{% load staticfiles%} {% load staticfiles%}
{% load massive_bootstrap_form %}
{% load i18n %} {% load i18n %}
{% block title %}{% trans "Creation and editing of invoices" %}{% endblock %} {% block title %}{% trans "Creation and editing of invoices" %}{% endblock %}
{% block content %} {% block content %}
{% bootstrap_form_errors factureform %} {% bootstrap_form_errors factureform %}
{{ factureform.media }}
<form class="form" method="post"> <form class="form" method="post">
{% csrf_token %} {% csrf_token %}
@ -40,7 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% else %} {% else %}
<h3>{% trans "Edit invoice" %}</h3> <h3>{% trans "Edit invoice" %}</h3>
{% endif %} {% endif %}
{% massive_bootstrap_form factureform 'user' %} {% bootstrap_form factureform %}
{{ venteform.management_form }} {{ venteform.management_form }}
<h3>{% trans "Articles" %}</h3> <h3>{% trans "Articles" %}</h3>
<table class="table table-striped"> <table class="table table-striped">

View file

@ -1,6 +1,6 @@
{% extends 'cotisations/sidebar.html' %} {% extends 'cotisations/sidebar.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,6 +1,6 @@
{% extends 'cotisations/sidebar.html' %} {% extends 'cotisations/sidebar.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,6 +1,6 @@
{% extends 'cotisations/sidebar.html' %} {% extends 'cotisations/sidebar.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,6 +1,6 @@
{% extends 'cotisations/sidebar.html' %} {% extends 'cotisations/sidebar.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,6 +1,6 @@
{% extends 'cotisations/sidebar.html' %} {% extends 'cotisations/sidebar.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,6 +1,6 @@
{% extends 'cotisations/sidebar.html' %} {% extends 'cotisations/sidebar.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,6 +1,6 @@
{% extends 'cotisations/sidebar.html' %} {% extends 'cotisations/sidebar.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,6 +1,6 @@
{% extends 'cotisations/sidebar.html' %} {% extends 'cotisations/sidebar.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.

View file

@ -1,6 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% comment %} {% comment %}
Re2o est un logiciel d'administration développé initiallement au rezometz. Il Re2o est un logiciel d'administration développé initiallement au Rézo Metz. Il
se veut agnostique au réseau considéré, de manière à être installable en se veut agnostique au réseau considéré, de manière à être installable en
quelques clics. quelques clics.
@ -27,43 +27,5 @@ with this program; if not, write to the Free Software Foundation, Inc.,
{% load i18n %} {% load i18n %}
{% block sidebar %} {% block sidebar %}
{% can_create CustomInvoice %}
<a class="list-group-item list-group-item-success" href="{% url 'cotisations:new-custom-invoice' %}">
<i class="fa fa-plus"></i> {% trans "Create an invoice" %}
</a>
<a class="list-group-item list-group-item-warning" href="{% url 'cotisations:control' %}">
<i class="fa fa-eye"></i> {% trans "Control the invoices" %}
</a>
{% acl_end %}
{% can_view_all Facture %}
<a class="list-group-item list-group-item-info" href="{% url 'cotisations:index' %}">
<i class="fa fa-list-ul"></i> {% trans "Invoices" %}
</a>
{% acl_end %}
{% can_view_all CustomInvoice %}
<a class="list-group-item list-group-item-info" href="{% url 'cotisations:index-custom-invoice' %}">
<i class="fa fa-list-ul"></i> {% trans "Custom invoices" %}
</a>
{% acl_end %}
{% can_view_all CostEstimate %}
<a class="list-group-item list-group-item-info" href="{% url 'cotisations:index-cost-estimate' %}">
<i class="fa fa-list-ul"></i> {% trans "Cost estimates" %}
</a>
{% acl_end %}
{% can_view_all Article %}
<a class="list-group-item list-group-item-info" href="{% url 'cotisations:index-article' %}">
<i class="fa fa-list-ul"></i> {% trans "Articles" %}
</a>
{% acl_end %}
{% can_view_all Banque %}
<a class="list-group-item list-group-item-info" href="{% url 'cotisations:index-banque' %}">
<i class="fa fa-list-ul"></i> {% trans "Banks" %}
</a>
{% acl_end %}
{% can_view_all Paiement %}
<a class="list-group-item list-group-item-info" href="{% url 'cotisations:index-paiement' %}">
<i class="fa fa-list-ul"></i> {% trans "Payment methods" %}
</a>
{% acl_end %}
{% endblock %} {% endblock %}

View file

@ -19,15 +19,17 @@ class VenteModelTests(TestCase):
def test_one_day_cotisation(self): def test_one_day_cotisation(self):
""" """
It should be possible to have one day membership. It should be possible to have one day membership.
Add one day of membership and one day of connection.
""" """
date = timezone.now() date = timezone.now()
purchase = Vente.objects.create( purchase = Vente.objects.create(
facture=self.f, facture=self.f,
number=1, number=1,
name="Test purchase", name="Test purchase",
duration=0, duration_connection=0,
duration_days=1, duration_days_connection=1,
type_cotisation="All", duration_membership=0,
duration_days_membership=1,
prix=0, prix=0,
) )
self.f.reorder_purchases() self.f.reorder_purchases()
@ -36,48 +38,66 @@ class VenteModelTests(TestCase):
datetime.timedelta(days=1), datetime.timedelta(days=1),
delta=datetime.timedelta(seconds=1), delta=datetime.timedelta(seconds=1),
) )
self.assertAlmostEqual(
self.user.end_adhesion() - date,
datetime.timedelta(days=1),
delta=datetime.timedelta(seconds=1),
)
def test_one_month_cotisation(self): def test_one_month_cotisation(self):
""" """
It should be possible to have one day membership. It should be possible to have one day membership.
Add one mounth of membership and one mounth of connection
""" """
date = timezone.now() date = timezone.now()
Vente.objects.create( Vente.objects.create(
facture=self.f, facture=self.f,
number=1, number=1,
name="Test purchase", name="Test purchase",
duration=1, duration_connection=1,
duration_days=0, duration_days_connection=0,
type_cotisation="All", duration_membership=1,
duration_days_membership=0,
prix=0, prix=0,
) )
self.f.reorder_purchases() self.f.reorder_purchases()
end = self.user.end_connexion() end_con = self.user.end_connexion()
end_memb = self.user.end_adhesion()
expected_end = date + relativedelta(months=1) expected_end = date + relativedelta(months=1)
self.assertEqual(end.day, expected_end.day) self.assertEqual(end_con.day, expected_end.day)
self.assertEqual(end.month, expected_end.month) self.assertEqual(end_con.month, expected_end.month)
self.assertEqual(end.year, expected_end.year) self.assertEqual(end_con.year, expected_end.year)
self.assertEqual(end_memb.day, expected_end.day)
self.assertEqual(end_memb.month, expected_end.month)
self.assertEqual(end_memb.year, expected_end.year)
def test_one_month_and_one_week_cotisation(self): def test_one_month_and_one_week_cotisation(self):
""" """
It should be possible to have one day membership. It should be possible to have one day membership.
Add one mounth and one week of membership and one mounth
and one week of connection
""" """
date = timezone.now() date = timezone.now()
Vente.objects.create( Vente.objects.create(
facture=self.f, facture=self.f,
number=1, number=1,
name="Test purchase", name="Test purchase",
duration=1, duration_connection=1,
duration_days=7, duration_days_connection=7,
type_cotisation="All", duration_membership=1,
duration_days_membership=7,
prix=0, prix=0,
) )
self.f.reorder_purchases() self.f.reorder_purchases()
end = self.user.end_connexion() end_con = self.user.end_connexion()
end_memb = self.user.end_adhesion()
expected_end = date + relativedelta(months=1, days=7) expected_end = date + relativedelta(months=1, days=7)
self.assertEqual(end.day, expected_end.day) self.assertEqual(end_con.day, expected_end.day)
self.assertEqual(end.month, expected_end.month) self.assertEqual(end_con.month, expected_end.month)
self.assertEqual(end.year, expected_end.year) self.assertEqual(end_con.year, expected_end.year)
self.assertEqual(end_memb.day, expected_end.day)
self.assertEqual(end_memb.month, expected_end.month)
self.assertEqual(end_memb.year, expected_end.year)
def test_date_start_cotisation(self): def test_date_start_cotisation(self):
""" """
@ -87,15 +107,140 @@ class VenteModelTests(TestCase):
facture=self.f, facture=self.f,
number=1, number=1,
name="Test purchase", name="Test purchase",
duration=0, duration_connection=0,
duration_days=1, duration_days_connection=1,
type_cotisation = 'All', duration_membership=0,
duration_deys_membership=1,
prix=0 prix=0
) )
v.create_cotis(date_start=timezone.make_aware(datetime.datetime(1998, 10, 16))) v.create_cotis(date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)), date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)))
v.save() v.save()
self.assertEqual(v.cotisation.date_end, timezone.make_aware(datetime.datetime(1998, 10, 17))) self.assertEqual(v.cotisation.date_end_con, timezone.make_aware(datetime.datetime(1998, 10, 17)))
self.assertEqual(v.cotisation.date_end_memb, timezone.make_aware(datetime.datetime(1998, 10, 17)))
def test_one_day_cotisation_membership_only(self):
"""
It should be possible to have one day membership without connection.
Add one day of membership and no connection.
"""
date = timezone.now()
purchase = Vente.objects.create(
facture=self.f,
number=1,
name="Test purchase",
duration_connection=0,
duration_days_connection=0,
duration_membership=0,
duration_days_membership=1,
prix=0,
)
self.f.reorder_purchases()
self.assertEqual(
self.user.end_connexion(),
None,
)
self.assertAlmostEqual(
self.user.end_adhesion() - date,
datetime.timedelta(days=1),
delta=datetime.timedelta(seconds=1),
)
def test_one_month_cotisation_membership_only(self):
"""
It should be possible to have one month membership.
Add one mounth of membership and no connection
"""
date = timezone.now()
Vente.objects.create(
facture=self.f,
number=1,
name="Test purchase",
duration_connection=0,
duration_days_connection=0,
duration_membership=1,
duration_days_membership=0,
prix=0,
)
self.f.reorder_purchases()
end_con = self.user.end_connexion()
end_memb = self.user.end_adhesion()
expected_end = date + relativedelta(months=1)
self.assertEqual(end_con, None)
self.assertEqual(end_memb.day, expected_end.day)
self.assertEqual(end_memb.month, expected_end.month)
self.assertEqual(end_memb.year, expected_end.year)
def test_one_month_and_one_week_cotisation_membership_only(self):
"""
It should be possible to have one mounth and one week membership.
Add one mounth and one week of membership and no connection.
"""
date = timezone.now()
Vente.objects.create(
facture=self.f,
number=1,
name="Test purchase",
duration_connection=0,
duration_days_connection=0,
duration_membership=1,
duration_days_membership=7,
prix=0,
)
self.f.reorder_purchases()
end_con = self.user.end_connexion()
end_memb = self.user.end_adhesion()
expected_end = date + relativedelta(months=1, days=7)
self.assertEqual(end_con, None)
self.assertEqual(end_memb.day, expected_end.day)
self.assertEqual(end_memb.month, expected_end.month)
self.assertEqual(end_memb.year, expected_end.year)
def test_date_start_cotisation_membership_only(self):
"""
It should be possible to add a cotisation with a specific start date
"""
v = Vente(
facture=self.f,
number=1,
name="Test purchase",
duration_connection=0,
duration_days_connection=0,
duration_membership=0,
duration_days_membership=1,
prix=0
)
v.create_cotis(date_start_con=timezone.make_aware(datetime.datetime(1998, 10, 16)), date_start_memb=timezone.make_aware(datetime.datetime(1998, 10, 16)))
v.save()
self.assertEqual(v.cotisation.date_end_con, timezone.make_aware(datetime.datetime(1998, 10, 17)))
self.assertEqual(v.cotisation.date_end_memb, timezone.make_aware(datetime.datetime(1998, 10, 16)))
def test_cotisation_membership_diff_connection(self):
"""
It should be possible to have purchase a membership longer
than the connection.
"""
date = timezone.now()
Vente.objects.create(
facture=self.f,
number=1,
name="Test purchase",
duration_connection=1,
duration_days_connection=0,
duration_membership=2,
duration_days_membership=0,
prix=0,
)
self.f.reorder_purchases()
end_con = self.user.end_connexion()
end_memb = self.user.end_adhesion()
expected_end_con = date + relativedelta(months=1)
expected_end_memb = date + relativedelta(months=2)
self.assertEqual(end_con.day, expected_end_con.day)
self.assertEqual(end_con.month, expected_end_con.month)
self.assertEqual(end_con.year, expected_end_con.year)
self.assertEqual(end_memb.day, expected_end_memb.day)
self.assertEqual(end_memb.month, expected_end_memb.month)
self.assertEqual(end_memb.year, expected_end_memb.year)
def tearDown(self): def tearDown(self):
self.f.delete() self.f.delete()
@ -121,9 +266,10 @@ class FactureModelTests(TestCase):
facture=invoice1, facture=invoice1,
number=1, number=1,
name="Test purchase", name="Test purchase",
duration=1, duration_connection=1,
duration_days=0, duration_days_connection=0,
type_cotisation="All", duration_membership=1,
duration_days_membership=0,
prix=0, prix=0,
) )
invoice1.reorder_purchases() invoice1.reorder_purchases()
@ -134,16 +280,20 @@ class FactureModelTests(TestCase):
facture=invoice2, facture=invoice2,
number=1, number=1,
name="Test purchase", name="Test purchase",
duration=1, duration_connection=1,
duration_days=0, duration_days_connection=0,
type_cotisation="All", duration_membership=1,
duration_days_membership=0,
prix=0, prix=0,
) )
invoice1.reorder_purchases() invoice1.reorder_purchases()
delta = relativedelta(self.user.end_connexion(), date) delta_con = relativedelta(self.user.end_connexion(), date)
delta.microseconds = 0 delta_memb = relativedelta(self.user.end_adhesion(), date)
delta_con.microseconds = 0
delta_memb.microseconds = 0
try: try:
self.assertEqual(delta, relativedelta(months=2)) self.assertEqual(delta_con, relativedelta(months=2))
self.assertEqual(delta_memb, relativedelta(months=2))
except Exception as e: except Exception as e:
invoice1.delete() invoice1.delete()
invoice2.delete() invoice2.delete()

Some files were not shown because too many files have changed in this diff Show more