mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-06-30 13:11:09 +02:00
Compare commits
6 Commits
b42f5afeab
...
sheets
Author | SHA1 | Date | |
---|---|---|---|
51d60d064c
|
|||
45334e4e02
|
|||
5174c84b33
|
|||
51e5e3669e
|
|||
44994a3ae7
|
|||
ba017c38c0
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -42,7 +42,6 @@ map.json
|
|||||||
backups/
|
backups/
|
||||||
/static/
|
/static/
|
||||||
/media/
|
/media/
|
||||||
/tmp/
|
|
||||||
|
|
||||||
# Virtualenv
|
# Virtualenv
|
||||||
env/
|
env/
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
from rest_framework.pagination import PageNumberPagination
|
|
||||||
|
|
||||||
|
|
||||||
class CustomPagination(PageNumberPagination):
|
|
||||||
page_size_query_param = 'page_size'
|
|
@ -26,6 +26,10 @@ if "note" in settings.INSTALLED_APPS:
|
|||||||
from note.api.urls import register_note_urls
|
from note.api.urls import register_note_urls
|
||||||
register_note_urls(router, 'note')
|
register_note_urls(router, 'note')
|
||||||
|
|
||||||
|
if "sheets" in settings.INSTALLED_APPS:
|
||||||
|
from sheets.api.urls import register_sheets_urls
|
||||||
|
register_sheets_urls(router, 'sheets')
|
||||||
|
|
||||||
if "treasury" in settings.INSTALLED_APPS:
|
if "treasury" in settings.INSTALLED_APPS:
|
||||||
from treasury.api.urls import register_treasury_urls
|
from treasury.api.urls import register_treasury_urls
|
||||||
register_treasury_urls(router, 'treasury')
|
register_treasury_urls(router, 'treasury')
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Generated by Django 2.2.26 on 2022-09-04 21:25
|
# Generated by Django 2.2.27 on 2022-08-18 11:01
|
||||||
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
@ -263,7 +263,7 @@ class Club(models.Model):
|
|||||||
|
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
|
|
||||||
while (today - self.membership_start).days >= 365:
|
if (today - self.membership_start).days >= 365:
|
||||||
if self.membership_start:
|
if self.membership_start:
|
||||||
self.membership_start = datetime.date(self.membership_start.year + 1,
|
self.membership_start = datetime.date(self.membership_start.year + 1,
|
||||||
self.membership_start.month, self.membership_start.day)
|
self.membership_start.month, self.membership_start.day)
|
||||||
|
@ -183,7 +183,7 @@ class TestMemberships(TestCase):
|
|||||||
club = Club.objects.get(name="Kfet")
|
club = Club.objects.get(name="Kfet")
|
||||||
else:
|
else:
|
||||||
club = Club.objects.create(
|
club = Club.objects.create(
|
||||||
name="Second club without BDE",
|
name="Second club " + ("with BDE" if bde_parent else "without BDE"),
|
||||||
parent_club=None,
|
parent_club=None,
|
||||||
email="newclub@example.com",
|
email="newclub@example.com",
|
||||||
require_memberships=True,
|
require_memberships=True,
|
||||||
|
@ -753,10 +753,6 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
|
|||||||
club = old_membership.club
|
club = old_membership.club
|
||||||
user = old_membership.user
|
user = old_membership.user
|
||||||
|
|
||||||
# Update club membership date
|
|
||||||
if PermissionBackend.check_perm(self.request, "member.change_club_membership_start", club):
|
|
||||||
club.update_membership_dates()
|
|
||||||
|
|
||||||
form.instance.club = club
|
form.instance.club = club
|
||||||
|
|
||||||
# Get form data
|
# Get form data
|
||||||
|
@ -325,8 +325,8 @@ class SpecialTransaction(Transaction):
|
|||||||
def clean(self):
|
def clean(self):
|
||||||
# SpecialTransaction are only possible with NoteSpecial object
|
# SpecialTransaction are only possible with NoteSpecial object
|
||||||
if self.is_credit() == self.is_debit():
|
if self.is_credit() == self.is_debit():
|
||||||
raise ValidationError(_("A special transaction is only possible between a"
|
raise(ValidationError(_("A special transaction is only possible between a"
|
||||||
" Note associated to a payment method and a User or a Club"))
|
" Note associated to a payment method and a User or a Club")))
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
|
17
apps/note/templates/sheets/order.html
Normal file
17
apps/note/templates/sheets/order.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% comment %}
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
{% endcomment %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="card bg-light mb-3">
|
||||||
|
<h3 class="card-header text-center">
|
||||||
|
{{ title }}
|
||||||
|
</h3>
|
||||||
|
<div class="card-body">
|
||||||
|
{% crispy form %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
88
apps/note/templates/sheets/waiting_list.html
Normal file
88
apps/note/templates/sheets/waiting_list.html
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-center">
|
||||||
|
<h1>{{ food.name }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="card col-xl-6">
|
||||||
|
<div class="card-header text-center">
|
||||||
|
<h2>{% trans "queued"|capfirst %}{% if queue %} ({{ queue|length }}){% endif %}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<ul>
|
||||||
|
{% for ordered_food in queue %}
|
||||||
|
<li>
|
||||||
|
{{ ordered_food.order.note }}
|
||||||
|
{% if ordered_food.priority %}
|
||||||
|
<span class="badge badge-secondary">{{ ordered_food.priority }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% empty %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{% trans "There is no queued order." %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card col-xl-6">
|
||||||
|
<div class="card-header text-center">
|
||||||
|
<h2>{% trans "ready"|capfirst %}</h2>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<ul>
|
||||||
|
{% for ordered_food in ready %}
|
||||||
|
<li>{{ ordered_food.order.note }}</li>
|
||||||
|
{% empty %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{% trans "There is no ready order." %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h3>{% trans "Other waiting lists:" %}</h3>
|
||||||
|
<ul>
|
||||||
|
{% for other_food in food.sheet.food_set.all %}
|
||||||
|
{% if other_food != food %}
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'sheets:waiting_list' pk=other_food.pk %}">{{ other_food }}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-center">
|
||||||
|
<a href="{% url 'sheets:queued_list' pk=food.pk %}" class="btn btn-primary">
|
||||||
|
{% trans "Queued orders" %}
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'sheets:ready_list' pk=food.pk %}" class="btn btn-primary">
|
||||||
|
{% trans "Ready orders" %}
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'sheets:sheet_detail' pk=food.sheet_id %}" class="btn btn-secondary">
|
||||||
|
{% trans "Back to note sheet detail" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extrajavascript %}
|
||||||
|
<script>
|
||||||
|
function reload() {
|
||||||
|
reloadWithTurbolinks()
|
||||||
|
timeout = setTimeout(reload, 15000)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout === undefined)
|
||||||
|
var timeout = setTimeout(reload, 15000)
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
152
apps/note/templates/sheets/waiting_list_detail.html
Normal file
152
apps/note/templates/sheets/waiting_list_detail.html
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-center">
|
||||||
|
<h1>{{ title }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% for of in orders %}
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-header text-center">
|
||||||
|
<h3>{{ of.order.note }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<dl class="row">
|
||||||
|
<dt class="col-xl-3">{% trans 'date'|capfirst %}</dt>
|
||||||
|
<dd class="col-xl-9">{{ of.order.date }}</dd>
|
||||||
|
|
||||||
|
{% if of.number > 1 %}
|
||||||
|
<dt class="col-xl-3">{% trans 'order number'|capfirst %}</dt>
|
||||||
|
<dd class="col-xl-9">{{ of.number }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if of.priority %}
|
||||||
|
<dt class="col-xl-3">{% trans 'priority request'|capfirst %}</dt>
|
||||||
|
<dd class="col-xl-9">{{ of.priority }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if of.remark %}
|
||||||
|
<dt class="col-xl-3">{% trans 'remark'|capfirst %}</dt>
|
||||||
|
<dd class="col-xl-9">{{ of.remark }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if of.options.count %}
|
||||||
|
<dt class="col-xl-3">{% trans 'options'|capfirst %}</dt>
|
||||||
|
<dd class="col-xl-9">{{ of.options.all|join:', ' }}</dd>
|
||||||
|
{% endif %}
|
||||||
|
</dl>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-center">
|
||||||
|
{% if list_type != 'READY' %}
|
||||||
|
<a href="#" class="btn btn-success" onclick="setOrderStatus({{ of.pk }}, 'READY')">
|
||||||
|
{% trans "Mark as ready" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if list_type != 'SERVED' %}
|
||||||
|
<a href="#" class="btn btn-primary" onclick="setOrderStatus({{ of.pk }}, 'SERVED')">
|
||||||
|
{% trans "Mark as served" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if list_type != 'QUEUED' %}
|
||||||
|
<a href="#" class="btn btn-warning" onclick="setOrderStatus({{ of.pk }}, 'QUEUED')">
|
||||||
|
{% trans "Re-queue" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if list_type != 'CANCELED' %}
|
||||||
|
<a href="#" class="btn btn-danger" onclick="setOrderStatus({{ of.pk }}, 'CANCELED')">
|
||||||
|
{% trans "Cancel" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% empty %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{% trans "There is no queued order." %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card mt-5">
|
||||||
|
<div class="card-body">
|
||||||
|
<h3>{% trans "Other waiting lists:" %}</h3>
|
||||||
|
<ul>
|
||||||
|
{% for other_food in food.sheet.food_set.all %}
|
||||||
|
{% if other_food != food %}
|
||||||
|
<li>
|
||||||
|
{% if list_type == 'QUEUED' %}
|
||||||
|
<a href="{% url 'sheets:queued_list' pk=other_food.pk %}">{{ other_food }}</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="{% url 'sheets:ready_list' pk=other_food.pk %}">{{ other_food }}</a>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-center">
|
||||||
|
{% if list_type != 'QUEUED' %}
|
||||||
|
<a href="{% url 'sheets:queued_list' pk=food.pk %}" class="btn btn-primary">
|
||||||
|
{% trans "Queued orders" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if list_type != 'READY' %}
|
||||||
|
<a href="{% url 'sheets:ready_list' pk=food.pk %}" class="btn btn-success">
|
||||||
|
{% trans "Ready orders" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if list_type != 'SERVED' %}
|
||||||
|
<a href="{% url 'sheets:served_list' pk=food.pk %}" class="btn btn-secondary">
|
||||||
|
{% trans "Served orders" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if list_type != 'CANCELED' %}
|
||||||
|
<a href="{% url 'sheets:canceled_list' pk=food.pk %}" class="btn btn-danger">
|
||||||
|
{% trans "Canceled orders" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
<a href="{% url 'sheets:waiting_list' pk=food.pk %}" class="btn btn-primary">
|
||||||
|
{% trans "Waiting list" %}
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'sheets:sheet_detail' pk=food.sheet_id %}" class="btn btn-secondary">
|
||||||
|
{% trans "Back to note sheet detail" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extrajavascript %}
|
||||||
|
<script>
|
||||||
|
function reload() {
|
||||||
|
reloadWithTurbolinks()
|
||||||
|
timeout = setTimeout(reload, 15000)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timeout === undefined)
|
||||||
|
var timeout = setTimeout(reload, 15000)
|
||||||
|
|
||||||
|
function setOrderStatus(ordered_food_id, status) {
|
||||||
|
fetch('/api/sheets/orderedfood/' + ordered_food_id + '/', {
|
||||||
|
method: 'PATCH',
|
||||||
|
body: JSON.stringify({
|
||||||
|
status: status,
|
||||||
|
served_date: status === 'QUEUED' ? null : new Date().toISOString(),
|
||||||
|
}),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': "application/json; charset=UTF-8",
|
||||||
|
'X-CSRFTOKEN': "{{ csrf_token }}"
|
||||||
|
}
|
||||||
|
}).then(response => response.json()).then(response => {
|
||||||
|
if ('detail' in response)
|
||||||
|
addMsg("{% trans "An error occurred" %}" + " : " + response['detail'], "danger")
|
||||||
|
else {
|
||||||
|
clearTimeout(timeout)
|
||||||
|
reload()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
@ -1967,7 +1967,7 @@
|
|||||||
"note",
|
"note",
|
||||||
"transaction"
|
"transaction"
|
||||||
],
|
],
|
||||||
"query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}]]",
|
"query": "[\"AND\", [\"OR\", {\"source\": [\"club\", \"note\"]}, {\"destination\": [\"club\", \"note\"]}], [\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}]]",
|
||||||
"type": "change",
|
"type": "change",
|
||||||
"mask": 2,
|
"mask": 2,
|
||||||
"field": "valid",
|
"field": "valid",
|
||||||
@ -2607,7 +2607,7 @@
|
|||||||
"note",
|
"note",
|
||||||
"transaction"
|
"transaction"
|
||||||
],
|
],
|
||||||
"query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}]",
|
"query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}]",
|
||||||
"type": "change",
|
"type": "change",
|
||||||
"mask": 2,
|
"mask": 2,
|
||||||
"field": "valid",
|
"field": "valid",
|
||||||
@ -2623,7 +2623,7 @@
|
|||||||
"note",
|
"note",
|
||||||
"transaction"
|
"transaction"
|
||||||
],
|
],
|
||||||
"query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}]",
|
"query": "[\"OR\", {\"source__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": true}, {\"destination__balance__gte\": {\"F\": [\"SUB\", [\"MUL\", [\"F\", \"amount\"], [\"F\", \"quantity\"]], 5000]}, \"valid\": false}]",
|
||||||
"type": "change",
|
"type": "change",
|
||||||
"mask": 2,
|
"mask": 2,
|
||||||
"field": "invalidity_reason",
|
"field": "invalidity_reason",
|
||||||
@ -2928,7 +2928,7 @@
|
|||||||
"application"
|
"application"
|
||||||
],
|
],
|
||||||
"query": "{\"user\": [\"user\"]}",
|
"query": "{\"user\": [\"user\"]}",
|
||||||
"type": "create",
|
"type": "add",
|
||||||
"mask": 1,
|
"mask": 1,
|
||||||
"field": "",
|
"field": "",
|
||||||
"permanent": true,
|
"permanent": true,
|
||||||
@ -3114,10 +3114,10 @@
|
|||||||
187,
|
187,
|
||||||
188,
|
188,
|
||||||
189,
|
189,
|
||||||
190,
|
190,
|
||||||
191,
|
191,
|
||||||
195,
|
195,
|
||||||
196
|
196
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3159,8 +3159,8 @@
|
|||||||
159,
|
159,
|
||||||
160,
|
160,
|
||||||
179,
|
179,
|
||||||
189,
|
189,
|
||||||
190
|
190
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3310,10 +3310,10 @@
|
|||||||
176,
|
176,
|
||||||
177,
|
177,
|
||||||
178,
|
178,
|
||||||
188,
|
188,
|
||||||
183,
|
183,
|
||||||
186,
|
186,
|
||||||
187
|
187
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -3508,13 +3508,13 @@
|
|||||||
187,
|
187,
|
||||||
188,
|
188,
|
||||||
189,
|
189,
|
||||||
190,
|
190,
|
||||||
191,
|
191,
|
||||||
192,
|
192,
|
||||||
193,
|
193,
|
||||||
194,
|
194,
|
||||||
195,
|
195,
|
||||||
196
|
196
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -5,7 +5,6 @@ from django import forms
|
|||||||
from django.contrib.auth.forms import UserCreationForm
|
from django.contrib.auth.forms import UserCreationForm
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from member.models import Club
|
|
||||||
from note.models import NoteSpecial, Alias
|
from note.models import NoteSpecial, Alias
|
||||||
from note_kfet.inputs import AmountInput
|
from note_kfet.inputs import AmountInput
|
||||||
|
|
||||||
@ -45,14 +44,14 @@ class SignUpForm(UserCreationForm):
|
|||||||
fields = ('first_name', 'last_name', 'username', 'email', )
|
fields = ('first_name', 'last_name', 'username', 'email', )
|
||||||
|
|
||||||
|
|
||||||
# class DeclareSogeAccountOpenedForm(forms.Form):
|
class DeclareSogeAccountOpenedForm(forms.Form):
|
||||||
# soge_account = forms.BooleanField(
|
soge_account = forms.BooleanField(
|
||||||
# label=_("I declare that I opened or I will open soon a bank account in the Société générale with the BDE "
|
label=_("I declare that I opened or I will open soon a bank account in the Société générale with the BDE "
|
||||||
# "partnership."),
|
"partnership."),
|
||||||
# help_text=_("Warning: this engages you to open your bank account. If you finally decides to don't open your "
|
help_text=_("Warning: this engages you to open your bank account. If you finally decides to don't open your "
|
||||||
# "account, you will have to pay the BDE membership."),
|
"account, you will have to pay the BDE membership."),
|
||||||
# required=False,
|
required=False,
|
||||||
# )
|
)
|
||||||
|
|
||||||
|
|
||||||
class WEISignupForm(forms.Form):
|
class WEISignupForm(forms.Form):
|
||||||
@ -68,11 +67,11 @@ class ValidationForm(forms.Form):
|
|||||||
"""
|
"""
|
||||||
Validate the inscription of the new users and pay memberships.
|
Validate the inscription of the new users and pay memberships.
|
||||||
"""
|
"""
|
||||||
# soge = forms.BooleanField(
|
soge = forms.BooleanField(
|
||||||
# label=_("Inscription paid by Société Générale"),
|
label=_("Inscription paid by Société Générale"),
|
||||||
# required=False,
|
required=False,
|
||||||
# help_text=_("Check this case if the Société Générale paid the inscription."),
|
help_text=_("Check this case if the Société Générale paid the inscription."),
|
||||||
# )
|
)
|
||||||
|
|
||||||
credit_type = forms.ModelChoiceField(
|
credit_type = forms.ModelChoiceField(
|
||||||
queryset=NoteSpecial.objects,
|
queryset=NoteSpecial.objects,
|
||||||
@ -115,12 +114,3 @@ class ValidationForm(forms.Form):
|
|||||||
required=False,
|
required=False,
|
||||||
initial=True,
|
initial=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# If the bda exists
|
|
||||||
if Club.objects.filter(name__iexact="bda").exists():
|
|
||||||
# The user can join the bda club at the inscription
|
|
||||||
join_bda = forms.BooleanField(
|
|
||||||
label=_("Join BDA Club"),
|
|
||||||
required=False,
|
|
||||||
initial=False,
|
|
||||||
)
|
|
||||||
|
@ -57,13 +57,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<h4> {% trans "Validate account" %}</h4>
|
<h4> {% trans "Validate account" %}</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% comment "Soge not for membership (only WEI)" %}
|
|
||||||
{% if declare_soge_account %}
|
{% if declare_soge_account %}
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
{% trans "The user declared that he/she opened a bank account in the Société générale." %}
|
{% trans "The user declared that he/she opened a bank account in the Société générale." %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endcomment %}
|
|
||||||
|
|
||||||
<div class="card-body" id="profile_infos">
|
<div class="card-body" id="profile_infos">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
@ -78,7 +76,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% comment "Soge not for membership (only WEI)" %}
|
|
||||||
{% block extrajavascript %}
|
{% block extrajavascript %}
|
||||||
<script>
|
<script>
|
||||||
soge_field = $("#id_soge");
|
soge_field = $("#id_soge");
|
||||||
@ -121,4 +118,3 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endcomment %}
|
|
||||||
|
@ -190,7 +190,7 @@ class TestValidateRegistration(TestCase):
|
|||||||
|
|
||||||
# BDE Membership is mandatory
|
# BDE Membership is mandatory
|
||||||
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
||||||
# soge=False,
|
soge=False,
|
||||||
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
|
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
|
||||||
credit_amount=4200,
|
credit_amount=4200,
|
||||||
last_name="TOTO",
|
last_name="TOTO",
|
||||||
@ -204,7 +204,7 @@ class TestValidateRegistration(TestCase):
|
|||||||
|
|
||||||
# Same
|
# Same
|
||||||
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
||||||
# soge=False,
|
soge=False,
|
||||||
credit_type="",
|
credit_type="",
|
||||||
credit_amount=0,
|
credit_amount=0,
|
||||||
last_name="TOTO",
|
last_name="TOTO",
|
||||||
@ -218,7 +218,7 @@ class TestValidateRegistration(TestCase):
|
|||||||
|
|
||||||
# The BDE membership is not free
|
# The BDE membership is not free
|
||||||
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
||||||
# soge=False,
|
soge=False,
|
||||||
credit_type=NoteSpecial.objects.get(special_type="Espèces").id,
|
credit_type=NoteSpecial.objects.get(special_type="Espèces").id,
|
||||||
credit_amount=0,
|
credit_amount=0,
|
||||||
last_name="TOTO",
|
last_name="TOTO",
|
||||||
@ -232,7 +232,7 @@ class TestValidateRegistration(TestCase):
|
|||||||
|
|
||||||
# Last and first names are required for a credit
|
# Last and first names are required for a credit
|
||||||
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
||||||
# soge=False,
|
soge=False,
|
||||||
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
|
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
|
||||||
credit_amount=4000,
|
credit_amount=4000,
|
||||||
last_name="",
|
last_name="",
|
||||||
@ -249,7 +249,7 @@ class TestValidateRegistration(TestCase):
|
|||||||
self.user.username = "admïntoto"
|
self.user.username = "admïntoto"
|
||||||
self.user.save()
|
self.user.save()
|
||||||
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
||||||
# soge=False,
|
soge=False,
|
||||||
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
|
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
|
||||||
credit_amount=500,
|
credit_amount=500,
|
||||||
last_name="TOTO",
|
last_name="TOTO",
|
||||||
@ -275,7 +275,7 @@ class TestValidateRegistration(TestCase):
|
|||||||
self.user.profile.save()
|
self.user.profile.save()
|
||||||
|
|
||||||
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
||||||
# soge=False,
|
soge=False,
|
||||||
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
|
credit_type=NoteSpecial.objects.get(special_type="Chèque").id,
|
||||||
credit_amount=500,
|
credit_amount=500,
|
||||||
last_name="TOTO",
|
last_name="TOTO",
|
||||||
@ -290,7 +290,6 @@ class TestValidateRegistration(TestCase):
|
|||||||
self.assertTrue(NoteUser.objects.filter(user=self.user).exists())
|
self.assertTrue(NoteUser.objects.filter(user=self.user).exists())
|
||||||
self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists())
|
self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists())
|
||||||
self.assertFalse(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
|
self.assertFalse(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
|
||||||
self.assertFalse(Membership.objects.filter(club__name__iexact="BDA", user=self.user).exists())
|
|
||||||
self.assertFalse(SogeCredit.objects.filter(user=self.user).exists())
|
self.assertFalse(SogeCredit.objects.filter(user=self.user).exists())
|
||||||
self.assertEqual(Transaction.objects.filter(
|
self.assertEqual(Transaction.objects.filter(
|
||||||
Q(source=self.user.note) | Q(destination=self.user.note)).count(), 2)
|
Q(source=self.user.note) | Q(destination=self.user.note)).count(), 2)
|
||||||
@ -312,7 +311,7 @@ class TestValidateRegistration(TestCase):
|
|||||||
self.user.profile.save()
|
self.user.profile.save()
|
||||||
|
|
||||||
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
||||||
# soge=False,
|
soge=False,
|
||||||
credit_type=NoteSpecial.objects.get(special_type="Espèces").id,
|
credit_type=NoteSpecial.objects.get(special_type="Espèces").id,
|
||||||
credit_amount=4000,
|
credit_amount=4000,
|
||||||
last_name="TOTO",
|
last_name="TOTO",
|
||||||
@ -327,7 +326,6 @@ class TestValidateRegistration(TestCase):
|
|||||||
self.assertTrue(NoteUser.objects.filter(user=self.user).exists())
|
self.assertTrue(NoteUser.objects.filter(user=self.user).exists())
|
||||||
self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists())
|
self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists())
|
||||||
self.assertTrue(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
|
self.assertTrue(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
|
||||||
self.assertFalse(Membership.objects.filter(club__name__iexact="BDA", user=self.user).exists())
|
|
||||||
self.assertFalse(SogeCredit.objects.filter(user=self.user).exists())
|
self.assertFalse(SogeCredit.objects.filter(user=self.user).exists())
|
||||||
self.assertEqual(Transaction.objects.filter(
|
self.assertEqual(Transaction.objects.filter(
|
||||||
Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
|
Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
|
||||||
@ -335,43 +333,42 @@ class TestValidateRegistration(TestCase):
|
|||||||
response = self.client.get(self.user.profile.get_absolute_url())
|
response = self.client.get(self.user.profile.get_absolute_url())
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
# def test_validate_kfet_registration_with_soge(self):
|
def test_validate_kfet_registration_with_soge(self):
|
||||||
# """
|
"""
|
||||||
# The user joins the BDE and the Kfet, but the membership is paid by the Société générale.
|
The user joins the BDE and the Kfet, but the membership is paid by the Société générale.
|
||||||
# """
|
"""
|
||||||
# response = self.client.get(reverse("registration:future_user_detail", args=(self.user.pk,)))
|
response = self.client.get(reverse("registration:future_user_detail", args=(self.user.pk,)))
|
||||||
# self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
#
|
|
||||||
# response = self.client.get(self.user.profile.get_absolute_url())
|
response = self.client.get(self.user.profile.get_absolute_url())
|
||||||
# self.assertEqual(response.status_code, 404)
|
self.assertEqual(response.status_code, 404)
|
||||||
#
|
|
||||||
# self.user.profile.email_confirmed = True
|
self.user.profile.email_confirmed = True
|
||||||
# self.user.profile.save()
|
self.user.profile.save()
|
||||||
#
|
|
||||||
# response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
response = self.client.post(reverse("registration:future_user_detail", args=(self.user.pk,)), data=dict(
|
||||||
# soge=True,
|
soge=True,
|
||||||
# credit_type=NoteSpecial.objects.get(special_type="Espèces").id,
|
credit_type=NoteSpecial.objects.get(special_type="Espèces").id,
|
||||||
# credit_amount=4000,
|
credit_amount=4000,
|
||||||
# last_name="TOTO",
|
last_name="TOTO",
|
||||||
# first_name="Toto",
|
first_name="Toto",
|
||||||
# bank="Société générale",
|
bank="Société générale",
|
||||||
# join_bde=True,
|
join_bde=True,
|
||||||
# join_kfet=True,
|
join_kfet=True,
|
||||||
# ))
|
))
|
||||||
# self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200)
|
self.assertRedirects(response, self.user.profile.get_absolute_url(), 302, 200)
|
||||||
# self.user.profile.refresh_from_db()
|
self.user.profile.refresh_from_db()
|
||||||
# self.assertTrue(self.user.profile.registration_valid)
|
self.assertTrue(self.user.profile.registration_valid)
|
||||||
# self.assertTrue(NoteUser.objects.filter(user=self.user).exists())
|
self.assertTrue(NoteUser.objects.filter(user=self.user).exists())
|
||||||
# self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists())
|
self.assertTrue(Membership.objects.filter(club__name="BDE", user=self.user).exists())
|
||||||
# self.assertTrue(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
|
self.assertTrue(Membership.objects.filter(club__name="Kfet", user=self.user).exists())
|
||||||
# self.assertFalse(Membership.objects.filter(club__name__iexact="BDA", user=self.user).exists())
|
self.assertTrue(SogeCredit.objects.filter(user=self.user).exists())
|
||||||
# self.assertTrue(SogeCredit.objects.filter(user=self.user).exists())
|
self.assertEqual(Transaction.objects.filter(
|
||||||
# self.assertEqual(Transaction.objects.filter(
|
Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
|
||||||
# Q(source=self.user.note) | Q(destination=self.user.note)).count(), 3)
|
self.assertFalse(Transaction.objects.filter(valid=True).exists())
|
||||||
# self.assertFalse(Transaction.objects.filter(valid=True).exists())
|
|
||||||
#
|
response = self.client.get(self.user.profile.get_absolute_url())
|
||||||
# response = self.client.get(self.user.profile.get_absolute_url())
|
self.assertEqual(response.status_code, 200)
|
||||||
# self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_invalidate_registration(self):
|
def test_invalidate_registration(self):
|
||||||
"""
|
"""
|
||||||
|
@ -24,8 +24,7 @@ from permission.models import Role
|
|||||||
from permission.views import ProtectQuerysetMixin
|
from permission.views import ProtectQuerysetMixin
|
||||||
from treasury.models import SogeCredit
|
from treasury.models import SogeCredit
|
||||||
|
|
||||||
# from .forms import SignUpForm, ValidationForm, DeclareSogeAccountOpenedForm
|
from .forms import SignUpForm, ValidationForm, DeclareSogeAccountOpenedForm
|
||||||
from .forms import SignUpForm, ValidationForm
|
|
||||||
from .tables import FutureUserTable
|
from .tables import FutureUserTable
|
||||||
from .tokens import email_validation_token
|
from .tokens import email_validation_token
|
||||||
|
|
||||||
@ -43,7 +42,7 @@ class UserCreateView(CreateView):
|
|||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context["profile_form"] = self.second_form(self.request.POST if self.request.POST else None)
|
context["profile_form"] = self.second_form(self.request.POST if self.request.POST else None)
|
||||||
# context["soge_form"] = DeclareSogeAccountOpenedForm(self.request.POST if self.request.POST else None)
|
context["soge_form"] = DeclareSogeAccountOpenedForm(self.request.POST if self.request.POST else None)
|
||||||
del context["profile_form"].fields["section"]
|
del context["profile_form"].fields["section"]
|
||||||
del context["profile_form"].fields["report_frequency"]
|
del context["profile_form"].fields["report_frequency"]
|
||||||
del context["profile_form"].fields["last_report"]
|
del context["profile_form"].fields["last_report"]
|
||||||
@ -76,12 +75,12 @@ class UserCreateView(CreateView):
|
|||||||
|
|
||||||
user.profile.send_email_validation_link()
|
user.profile.send_email_validation_link()
|
||||||
|
|
||||||
# soge_form = DeclareSogeAccountOpenedForm(self.request.POST)
|
soge_form = DeclareSogeAccountOpenedForm(self.request.POST)
|
||||||
# if "soge_account" in soge_form.data and soge_form.data["soge_account"]:
|
if "soge_account" in soge_form.data and soge_form.data["soge_account"]:
|
||||||
# # If the user declares that a bank account got opened, prepare the soge credit to warn treasurers
|
# If the user declares that a bank account got opened, prepare the soge credit to warn treasurers
|
||||||
# soge_credit = SogeCredit(user=user)
|
soge_credit = SogeCredit(user=user)
|
||||||
# soge_credit._force_save = True
|
soge_credit._force_save = True
|
||||||
# soge_credit.save()
|
soge_credit.save()
|
||||||
|
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
@ -238,12 +237,9 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||||||
fee += bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid
|
fee += bde.membership_fee_paid if user.profile.paid else bde.membership_fee_unpaid
|
||||||
kfet = Club.objects.get(name="Kfet")
|
kfet = Club.objects.get(name="Kfet")
|
||||||
fee += kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
|
fee += kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
|
||||||
if Club.objects.filter(name__iexact="BDA").exists():
|
|
||||||
bda = Club.objects.get(name__iexact="BDA")
|
|
||||||
fee += bda.membership_fee_paid if user.profile.paid else bda.membership_fee_unpaid
|
|
||||||
ctx["total_fee"] = "{:.02f}".format(fee / 100, )
|
ctx["total_fee"] = "{:.02f}".format(fee / 100, )
|
||||||
|
|
||||||
# ctx["declare_soge_account"] = SogeCredit.objects.filter(user=user).exists()
|
ctx["declare_soge_account"] = SogeCredit.objects.filter(user=user).exists()
|
||||||
|
|
||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
@ -266,13 +262,8 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||||||
form.add_error(None, _("An alias with a similar name already exists."))
|
form.add_error(None, _("An alias with a similar name already exists."))
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
|
|
||||||
# Check if BDA exist to propose membership at regisration
|
|
||||||
bda_exists = False
|
|
||||||
if Club.objects.filter(name__iexact="BDA").exists():
|
|
||||||
bda_exists = True
|
|
||||||
|
|
||||||
# Get form data
|
# Get form data
|
||||||
# soge = form.cleaned_data["soge"]
|
soge = form.cleaned_data["soge"]
|
||||||
credit_type = form.cleaned_data["credit_type"]
|
credit_type = form.cleaned_data["credit_type"]
|
||||||
credit_amount = form.cleaned_data["credit_amount"]
|
credit_amount = form.cleaned_data["credit_amount"]
|
||||||
last_name = form.cleaned_data["last_name"]
|
last_name = form.cleaned_data["last_name"]
|
||||||
@ -280,13 +271,11 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||||||
bank = form.cleaned_data["bank"]
|
bank = form.cleaned_data["bank"]
|
||||||
join_bde = form.cleaned_data["join_bde"]
|
join_bde = form.cleaned_data["join_bde"]
|
||||||
join_kfet = form.cleaned_data["join_kfet"]
|
join_kfet = form.cleaned_data["join_kfet"]
|
||||||
if bda_exists:
|
|
||||||
join_bda = form.cleaned_data["join_bda"]
|
|
||||||
|
|
||||||
# if soge:
|
if soge:
|
||||||
# # If Société Générale pays the inscription, the user automatically joins the two clubs.
|
# If Société Générale pays the inscription, the user automatically joins the two clubs.
|
||||||
# join_bde = True
|
join_bde = True
|
||||||
# join_kfet = True
|
join_kfet = True
|
||||||
|
|
||||||
if not join_bde:
|
if not join_bde:
|
||||||
# This software belongs to the BDE.
|
# This software belongs to the BDE.
|
||||||
@ -303,21 +292,15 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||||||
kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
|
kfet_fee = kfet.membership_fee_paid if user.profile.paid else kfet.membership_fee_unpaid
|
||||||
# Add extra fee for the full membership
|
# Add extra fee for the full membership
|
||||||
fee += kfet_fee if join_kfet else 0
|
fee += kfet_fee if join_kfet else 0
|
||||||
if bda_exists:
|
|
||||||
bda = Club.objects.get(name__iexact="BDA")
|
|
||||||
bda_fee = bda.membership_fee_paid if user.profile.paid else bda.membership_fee_unpaid
|
|
||||||
# Add extra fee for the bda membership
|
|
||||||
fee += bda_fee if join_bda else 0
|
|
||||||
|
|
||||||
# # If the bank pays, then we don't credit now. Treasurers will validate the transaction
|
# If the bank pays, then we don't credit now. Treasurers will validate the transaction
|
||||||
# # and credit the note later.
|
# and credit the note later.
|
||||||
# credit_type = None if soge else credit_type
|
credit_type = None if soge else credit_type
|
||||||
|
|
||||||
# If the user does not select any payment method, then no credit will be performed.
|
# If the user does not select any payment method, then no credit will be performed.
|
||||||
credit_amount = 0 if credit_type is None else credit_amount
|
credit_amount = 0 if credit_type is None else credit_amount
|
||||||
|
|
||||||
# if fee > credit_amount and not soge:
|
if fee > credit_amount and not soge:
|
||||||
if fee > credit_amount:
|
|
||||||
# Check if the user credits enough money
|
# Check if the user credits enough money
|
||||||
form.add_error('credit_type',
|
form.add_error('credit_type',
|
||||||
_("The entered amount is not enough for the memberships, should be at least {}")
|
_("The entered amount is not enough for the memberships, should be at least {}")
|
||||||
@ -337,12 +320,12 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||||||
user.profile.save()
|
user.profile.save()
|
||||||
user.refresh_from_db()
|
user.refresh_from_db()
|
||||||
|
|
||||||
# if not soge and SogeCredit.objects.filter(user=user).exists():
|
if not soge and SogeCredit.objects.filter(user=user).exists():
|
||||||
# # If the user declared that a bank account was opened but in the validation form the SoGé case was
|
# If the user declared that a bank account was opened but in the validation form the SoGé case was
|
||||||
# # unchecked, delete the associated credit
|
# unchecked, delete the associated credit
|
||||||
# soge_credit = SogeCredit.objects.get(user=user)
|
soge_credit = SogeCredit.objects.get(user=user)
|
||||||
# soge_credit._force_delete = True
|
soge_credit._force_delete = True
|
||||||
# soge_credit.delete()
|
soge_credit.delete()
|
||||||
|
|
||||||
if credit_type is not None and credit_amount > 0:
|
if credit_type is not None and credit_amount > 0:
|
||||||
# Credit the note
|
# Credit the note
|
||||||
@ -351,8 +334,7 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||||||
destination=user.note,
|
destination=user.note,
|
||||||
quantity=1,
|
quantity=1,
|
||||||
amount=credit_amount,
|
amount=credit_amount,
|
||||||
reason="Crédit " + credit_type.special_type + " (Inscription)",
|
reason="Crédit " + ("Société générale" if soge else credit_type.special_type) + " (Inscription)",
|
||||||
# reason="Crédit " + ("Société générale" if soge else credit_type.special_type) + " (Inscription)",
|
|
||||||
last_name=last_name,
|
last_name=last_name,
|
||||||
first_name=first_name,
|
first_name=first_name,
|
||||||
bank=bank,
|
bank=bank,
|
||||||
@ -366,8 +348,8 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||||||
user=user,
|
user=user,
|
||||||
fee=bde_fee,
|
fee=bde_fee,
|
||||||
)
|
)
|
||||||
# if soge:
|
if soge:
|
||||||
# membership._soge = True
|
membership._soge = True
|
||||||
membership.save()
|
membership.save()
|
||||||
membership.refresh_from_db()
|
membership.refresh_from_db()
|
||||||
membership.roles.add(Role.objects.get(name="Adhérent BDE"))
|
membership.roles.add(Role.objects.get(name="Adhérent BDE"))
|
||||||
@ -380,29 +362,17 @@ class FutureUserDetailView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin,
|
|||||||
user=user,
|
user=user,
|
||||||
fee=kfet_fee,
|
fee=kfet_fee,
|
||||||
)
|
)
|
||||||
# if soge:
|
if soge:
|
||||||
# membership._soge = True
|
membership._soge = True
|
||||||
membership.save()
|
membership.save()
|
||||||
membership.refresh_from_db()
|
membership.refresh_from_db()
|
||||||
membership.roles.add(Role.objects.get(name="Adhérent Kfet"))
|
membership.roles.add(Role.objects.get(name="Adhérent Kfet"))
|
||||||
membership.save()
|
membership.save()
|
||||||
|
|
||||||
if bda_exists and join_bda:
|
if soge:
|
||||||
# Create membership for the user to the BDA starting today
|
soge_credit = SogeCredit.objects.get(user=user)
|
||||||
membership = Membership(
|
# Update the credit transaction amount
|
||||||
club=bda,
|
soge_credit.save()
|
||||||
user=user,
|
|
||||||
fee=bda_fee,
|
|
||||||
)
|
|
||||||
membership.save()
|
|
||||||
membership.refresh_from_db()
|
|
||||||
membership.roles.add(Role.objects.get(name="Membre de club"))
|
|
||||||
membership.save()
|
|
||||||
|
|
||||||
# if soge:
|
|
||||||
# soge_credit = SogeCredit.objects.get(user=user)
|
|
||||||
# # Update the credit transaction amount
|
|
||||||
# soge_credit.save()
|
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
Submodule apps/scripts updated: 4471307b37...86bc2d2698
4
apps/sheets/__init__.py
Normal file
4
apps/sheets/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
default_app_config = 'sheets.apps.SheetsConfig'
|
46
apps/sheets/admin.py
Normal file
46
apps/sheets/admin.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.contrib import admin
|
||||||
|
from note_kfet.admin import admin_site
|
||||||
|
from sheets.models import Sheet, Food, FoodOption, Meal, Order, OrderedMeal, OrderedFood, SheetOrderTransaction
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Sheet, site=admin_site)
|
||||||
|
class SheetAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Food, site=admin_site)
|
||||||
|
class FoodAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(FoodOption, site=admin_site)
|
||||||
|
class FoodOptionAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Meal, site=admin_site)
|
||||||
|
class MealAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(Order, site=admin_site)
|
||||||
|
class OrderAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(OrderedMeal, site=admin_site)
|
||||||
|
class OrderedMealAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(OrderedFood, site=admin_site)
|
||||||
|
class OrderedFoodAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@admin.register(SheetOrderTransaction, site=admin_site)
|
||||||
|
class SheetOrderTransactionAdmin(admin.ModelAdmin):
|
||||||
|
pass
|
0
apps/sheets/api/__init__.py
Normal file
0
apps/sheets/api/__init__.py
Normal file
55
apps/sheets/api/serializers.py
Normal file
55
apps/sheets/api/serializers.py
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
|
from ..models import Sheet, Food, FoodOption, Meal, Order, OrderedMeal, OrderedFood, SheetOrderTransaction
|
||||||
|
|
||||||
|
|
||||||
|
class SheetSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Sheet
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class FoodSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Food
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class FoodOptionSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = FoodOption
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class MealSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Meal
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class OrderSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Order
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class OrderedMealSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = OrderedMeal
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class OrderedFoodSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = OrderedFood
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
|
||||||
|
class SheetOrderTransactionSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = SheetOrderTransaction
|
||||||
|
fields = '__all__'
|
19
apps/sheets/api/urls.py
Normal file
19
apps/sheets/api/urls.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from sheets.api.views import SheetViewSet, FoodViewSet, FoodOptionViewSet, MealViewSet, OrderViewSet, \
|
||||||
|
OrderedMealViewSet, OrderedFoodViewSet, SheetOrderTransactionViewSet
|
||||||
|
|
||||||
|
|
||||||
|
def register_sheets_urls(router, path):
|
||||||
|
"""
|
||||||
|
Configure router for Sheets REST API.
|
||||||
|
"""
|
||||||
|
router.register(path + '/sheet', SheetViewSet)
|
||||||
|
router.register(path + '/food', FoodViewSet)
|
||||||
|
router.register(path + '/foodoption', FoodOptionViewSet)
|
||||||
|
router.register(path + '/meal', MealViewSet)
|
||||||
|
router.register(path + '/order', OrderViewSet)
|
||||||
|
router.register(path + '/orderedmeal', OrderedMealViewSet)
|
||||||
|
router.register(path + '/orderedfood', OrderedFoodViewSet)
|
||||||
|
router.register(path + '/sheetordertransaction', SheetOrderTransactionViewSet)
|
78
apps/sheets/api/views.py
Normal file
78
apps/sheets/api/views.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from api.viewsets import ReadProtectedModelViewSet
|
||||||
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
from rest_framework.filters import SearchFilter, OrderingFilter
|
||||||
|
|
||||||
|
from .serializers import SheetSerializer, FoodSerializer, FoodOptionSerializer, MealSerializer, OrderSerializer, \
|
||||||
|
OrderedMealSerializer, OrderedFoodSerializer, SheetOrderTransactionSerializer
|
||||||
|
from ..models import Sheet, Food, FoodOption, Meal, Order, OrderedMeal, OrderedFood, SheetOrderTransaction
|
||||||
|
|
||||||
|
|
||||||
|
class SheetViewSet(ReadProtectedModelViewSet):
|
||||||
|
queryset = Sheet.objects.order_by('id')
|
||||||
|
serializer_class = SheetSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||||
|
filterset_fields = ['name', 'date', ]
|
||||||
|
search_fields = ['$name', ]
|
||||||
|
|
||||||
|
|
||||||
|
class FoodViewSet(ReadProtectedModelViewSet):
|
||||||
|
queryset = Food.objects.order_by('id')
|
||||||
|
serializer_class = FoodSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||||
|
filterset_fields = ['name', 'sheet', 'price', 'club', 'available', ]
|
||||||
|
search_fields = ['$name', ]
|
||||||
|
|
||||||
|
|
||||||
|
class FoodOptionViewSet(ReadProtectedModelViewSet):
|
||||||
|
queryset = FoodOption.objects.order_by('id')
|
||||||
|
serializer_class = FoodOptionSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||||
|
filterset_fields = ['name', 'food', 'extra_cost', 'available', ]
|
||||||
|
search_fields = ['$name', '$food__name', ]
|
||||||
|
|
||||||
|
|
||||||
|
class MealViewSet(ReadProtectedModelViewSet):
|
||||||
|
queryset = Meal.objects.order_by('id')
|
||||||
|
serializer_class = MealSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||||
|
filterset_fields = ['name', 'content', 'price', 'available', ]
|
||||||
|
search_fields = ['$name', ]
|
||||||
|
|
||||||
|
|
||||||
|
class OrderViewSet(ReadProtectedModelViewSet):
|
||||||
|
queryset = Order.objects.order_by('id')
|
||||||
|
serializer_class = OrderSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||||
|
filterset_fields = ['sheet', 'note', 'date', 'gift', ]
|
||||||
|
search_fields = ['$sheet__name', '$note__alias__name', '$note__alias__normalized_name', ]
|
||||||
|
|
||||||
|
|
||||||
|
class OrderedMealViewSet(ReadProtectedModelViewSet):
|
||||||
|
queryset = OrderedMeal.objects.order_by('id')
|
||||||
|
serializer_class = OrderedMealSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend]
|
||||||
|
filterset_fields = ['order', 'meal', ]
|
||||||
|
|
||||||
|
|
||||||
|
class OrderedFoodViewSet(ReadProtectedModelViewSet):
|
||||||
|
queryset = OrderedFood.objects.order_by('id')
|
||||||
|
serializer_class = OrderedFoodSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend]
|
||||||
|
filterset_fields = ['order', 'meal', 'food', 'options', 'number', 'status', 'served_date', ]
|
||||||
|
|
||||||
|
|
||||||
|
class SheetOrderTransactionViewSet(ReadProtectedModelViewSet):
|
||||||
|
queryset = SheetOrderTransaction.objects.order_by('-created_at')
|
||||||
|
serializer_class = SheetOrderTransactionSerializer
|
||||||
|
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
|
||||||
|
filterset_fields = ['source', 'source_alias', 'source__alias__name', 'source__alias__normalized_name',
|
||||||
|
'destination', 'destination_alias', 'destination__alias__name',
|
||||||
|
'destination__alias__normalized_name', 'quantity', 'polymorphic_ctype', 'amount',
|
||||||
|
'created_at', 'valid', 'invalidity_reason', 'ordered_food', ]
|
||||||
|
search_fields = ['$reason', '$source_alias', '$source__alias__name', '$source__alias__normalized_name',
|
||||||
|
'$destination_alias', '$destination__alias__name', '$destination__alias__normalized_name',
|
||||||
|
'$invalidity_reason', ]
|
||||||
|
ordering_fields = ['created_at', 'amount', ]
|
10
apps/sheets/apps.py
Normal file
10
apps/sheets/apps.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.apps import AppConfig
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class SheetsConfig(AppConfig):
|
||||||
|
name = 'sheets'
|
||||||
|
verbose_name = _('note sheets')
|
67
apps/sheets/forms.py
Normal file
67
apps/sheets/forms.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
from crispy_forms.helper import FormHelper
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from member.models import Club
|
||||||
|
from note_kfet.inputs import AmountInput, Autocomplete, DateTimePickerInput
|
||||||
|
|
||||||
|
from .models import Food, FoodOption, Meal, Sheet
|
||||||
|
|
||||||
|
|
||||||
|
class SheetForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Sheet
|
||||||
|
fields = '__all__'
|
||||||
|
widgets = {
|
||||||
|
'date': DateTimePickerInput(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FoodForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Food
|
||||||
|
exclude = ('sheet', )
|
||||||
|
widgets = {
|
||||||
|
'price': AmountInput(),
|
||||||
|
'club': Autocomplete(
|
||||||
|
model=Club,
|
||||||
|
attrs={"api_url": "/api/members/club/"},
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FoodOptionForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = FoodOption
|
||||||
|
fields = '__all__'
|
||||||
|
widgets = {
|
||||||
|
'extra_cost': AmountInput(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
FoodOptionsFormSet = forms.inlineformset_factory(
|
||||||
|
Food,
|
||||||
|
FoodOption,
|
||||||
|
form=FoodOptionForm,
|
||||||
|
extra=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FoodOptionFormSetHelper(FormHelper):
|
||||||
|
def __init__(self, form=None):
|
||||||
|
super().__init__(form)
|
||||||
|
self.form_tag = False
|
||||||
|
self.form_method = 'POST'
|
||||||
|
self.form_class = 'form-inline'
|
||||||
|
self.template = 'bootstrap4/table_inline_formset.html'
|
||||||
|
|
||||||
|
|
||||||
|
class MealForm(forms.ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Meal
|
||||||
|
exclude = ('sheet', )
|
||||||
|
widgets = {
|
||||||
|
'content': forms.CheckboxSelectMultiple(),
|
||||||
|
'price': AmountInput(),
|
||||||
|
}
|
157
apps/sheets/migrations/0001_initial.py
Normal file
157
apps/sheets/migrations/0001_initial.py
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
# Generated by Django 2.2.27 on 2022-08-18 11:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
import django.utils.timezone
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('member', '0009_auto_20220818_1301'),
|
||||||
|
('note', '0006_trust'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Food',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='food')),
|
||||||
|
('price', models.IntegerField(verbose_name='price')),
|
||||||
|
('available', models.BooleanField(default=True, help_text="If set to false, this option won't be offered (in case of out of stock)", verbose_name='available')),
|
||||||
|
('club', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='member.Club', verbose_name='destination club')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'food',
|
||||||
|
'verbose_name_plural': 'food',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FoodOption',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='name')),
|
||||||
|
('extra_cost', models.IntegerField(default=0, verbose_name='extra cost')),
|
||||||
|
('available', models.BooleanField(default=True, help_text="If set to false, this option won't be offered (in case of out of stock)", verbose_name='available')),
|
||||||
|
('food', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sheets.Food', verbose_name='food')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'food option',
|
||||||
|
'verbose_name_plural': 'food options',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Meal',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='name')),
|
||||||
|
('price', models.IntegerField(verbose_name='price')),
|
||||||
|
('available', models.BooleanField(default=True, help_text="If set to false, this option won't be offered (in case of out of stock)", verbose_name='available')),
|
||||||
|
('content', models.ManyToManyField(to='sheets.Food', verbose_name='content')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'meal',
|
||||||
|
'verbose_name_plural': 'meals',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Order',
|
||||||
|
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')),
|
||||||
|
('gift', models.IntegerField(verbose_name='gift')),
|
||||||
|
('note', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='note.Note', verbose_name='note')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'order',
|
||||||
|
'verbose_name_plural': 'orders',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OrderedFood',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('remark', models.TextField(blank=True, default='', verbose_name='remark')),
|
||||||
|
('priority', models.CharField(blank=True, default='', max_length=64, verbose_name='priority request')),
|
||||||
|
('number', models.IntegerField(help_text='How many times the user ordered this.', verbose_name='number')),
|
||||||
|
('status', models.CharField(choices=[('QUEUED', 'queued'), ('READY', 'ready'), ('SERVED', 'served'), ('CANCELED', 'canceled')], max_length=8, verbose_name='status')),
|
||||||
|
('served_date', models.DateTimeField(default=None, null=True, verbose_name='served date')),
|
||||||
|
('food', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.Food', verbose_name='food')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'ordered food',
|
||||||
|
'verbose_name_plural': 'ordered food',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Sheet',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='name')),
|
||||||
|
('date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='start date')),
|
||||||
|
('description', models.TextField(verbose_name='description')),
|
||||||
|
('visible', models.BooleanField(default=False, help_text='the note sheet will be private until this field is checked.', verbose_name='visible')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'note sheet',
|
||||||
|
'verbose_name_plural': 'note sheets',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SheetOrderTransaction',
|
||||||
|
fields=[
|
||||||
|
('transaction_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='note.Transaction')),
|
||||||
|
('ordered_food', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.OrderedFood', verbose_name='ordered food')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'sheet order transaction',
|
||||||
|
'verbose_name_plural': 'sheet order transactions',
|
||||||
|
},
|
||||||
|
bases=('note.transaction',),
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='OrderedMeal',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('meal', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.Meal', verbose_name='meal')),
|
||||||
|
('order', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.Order', verbose_name='order')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'ordered meal',
|
||||||
|
'verbose_name_plural': 'ordered meals',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='orderedfood',
|
||||||
|
name='meal',
|
||||||
|
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, to='sheets.OrderedMeal', verbose_name='ordered meal'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='orderedfood',
|
||||||
|
name='options',
|
||||||
|
field=models.ManyToManyField(blank=True, to='sheets.FoodOption', verbose_name='options'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='orderedfood',
|
||||||
|
name='order',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.Order', verbose_name='order'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='order',
|
||||||
|
name='sheet',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='sheets.Sheet', verbose_name='note sheet'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='meal',
|
||||||
|
name='sheet',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sheets.Sheet', verbose_name='note sheet'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='food',
|
||||||
|
name='sheet',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sheets.Sheet', verbose_name='note sheet'),
|
||||||
|
),
|
||||||
|
]
|
34
apps/sheets/migrations/0002_auto_20220818_1713.py
Normal file
34
apps/sheets/migrations/0002_auto_20220818_1713.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
# Generated by Django 2.2.27 on 2022-08-18 15:13
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('sheets', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='order',
|
||||||
|
name='gift',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='orderedfood',
|
||||||
|
name='gift',
|
||||||
|
field=models.IntegerField(default=0, verbose_name='gift'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='orderedmeal',
|
||||||
|
name='gift',
|
||||||
|
field=models.IntegerField(default=0, verbose_name='gift'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='orderedfood',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(choices=[('QUEUED', 'queued'), ('READY', 'ready'), ('SERVED', 'served'), ('CANCELED', 'canceled')], default='QUEUED', max_length=8, verbose_name='status'),
|
||||||
|
),
|
||||||
|
]
|
0
apps/sheets/migrations/__init__.py
Normal file
0
apps/sheets/migrations/__init__.py
Normal file
289
apps/sheets/models.py
Normal file
289
apps/sheets/models.py
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.db import models
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from member.models import Club
|
||||||
|
from note.models import Note, Transaction
|
||||||
|
|
||||||
|
|
||||||
|
class Sheet(models.Model):
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_("name"),
|
||||||
|
)
|
||||||
|
|
||||||
|
date = models.DateTimeField(
|
||||||
|
verbose_name=_("start date"),
|
||||||
|
default=timezone.now,
|
||||||
|
)
|
||||||
|
|
||||||
|
description = models.TextField(
|
||||||
|
verbose_name=_("description"),
|
||||||
|
)
|
||||||
|
|
||||||
|
visible = models.BooleanField(
|
||||||
|
default=False,
|
||||||
|
verbose_name=_("visible"),
|
||||||
|
help_text=_("the note sheet will be private until this field is checked."),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_absolute_url(self):
|
||||||
|
return reverse_lazy('sheets:sheet_detail', args=(self.pk,))
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("note sheet")
|
||||||
|
verbose_name_plural = _("note sheets")
|
||||||
|
|
||||||
|
|
||||||
|
class Food(models.Model):
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_("food"),
|
||||||
|
)
|
||||||
|
|
||||||
|
sheet = models.ForeignKey(
|
||||||
|
Sheet,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name=_("note sheet"),
|
||||||
|
)
|
||||||
|
|
||||||
|
price = models.IntegerField(
|
||||||
|
verbose_name=_("price"),
|
||||||
|
)
|
||||||
|
|
||||||
|
club = models.ForeignKey(
|
||||||
|
Club,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
verbose_name=_("destination club"),
|
||||||
|
)
|
||||||
|
|
||||||
|
available = models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
verbose_name=_("available"),
|
||||||
|
help_text=_("If set to false, this option won't be offered (in case of out of stock)"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("food")
|
||||||
|
verbose_name_plural = _("food")
|
||||||
|
|
||||||
|
|
||||||
|
class FoodOption(models.Model):
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_("name"),
|
||||||
|
)
|
||||||
|
|
||||||
|
food = models.ForeignKey(
|
||||||
|
Food,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name=_("food"),
|
||||||
|
)
|
||||||
|
|
||||||
|
extra_cost = models.IntegerField(
|
||||||
|
default=0,
|
||||||
|
verbose_name=_("extra cost"),
|
||||||
|
)
|
||||||
|
|
||||||
|
available = models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
verbose_name=_("available"),
|
||||||
|
help_text=_("If set to false, this option won't be offered (in case of out of stock)"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("food option")
|
||||||
|
verbose_name_plural = _("food options")
|
||||||
|
|
||||||
|
|
||||||
|
class Meal(models.Model):
|
||||||
|
sheet = models.ForeignKey(
|
||||||
|
Sheet,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
verbose_name=_("note sheet"),
|
||||||
|
)
|
||||||
|
|
||||||
|
name = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_("name"),
|
||||||
|
)
|
||||||
|
|
||||||
|
content = models.ManyToManyField(
|
||||||
|
Food,
|
||||||
|
verbose_name=_("content"),
|
||||||
|
)
|
||||||
|
|
||||||
|
price = models.IntegerField(
|
||||||
|
verbose_name=_("price"),
|
||||||
|
)
|
||||||
|
|
||||||
|
available = models.BooleanField(
|
||||||
|
default=True,
|
||||||
|
verbose_name=_("available"),
|
||||||
|
help_text=_("If set to false, this option won't be offered (in case of out of stock)"),
|
||||||
|
)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return _("meal").capitalize() + " " + self.name
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("meal")
|
||||||
|
verbose_name_plural = _("meals")
|
||||||
|
|
||||||
|
|
||||||
|
class Order(models.Model):
|
||||||
|
sheet = models.ForeignKey(
|
||||||
|
Sheet,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
verbose_name=_("note sheet"),
|
||||||
|
)
|
||||||
|
|
||||||
|
note = models.ForeignKey(
|
||||||
|
Note,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
verbose_name=_("note"),
|
||||||
|
)
|
||||||
|
|
||||||
|
date = models.DateTimeField(
|
||||||
|
verbose_name=_("date"),
|
||||||
|
auto_now_add=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("order")
|
||||||
|
verbose_name_plural = _("orders")
|
||||||
|
|
||||||
|
|
||||||
|
class OrderedMeal(models.Model):
|
||||||
|
order = models.ForeignKey(
|
||||||
|
Order,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
verbose_name=_("order"),
|
||||||
|
)
|
||||||
|
|
||||||
|
meal = models.ForeignKey(
|
||||||
|
Meal,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
verbose_name=_("meal"),
|
||||||
|
)
|
||||||
|
|
||||||
|
gift = models.IntegerField(
|
||||||
|
verbose_name=_("gift"),
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("ordered meal")
|
||||||
|
verbose_name_plural = _("ordered meals")
|
||||||
|
|
||||||
|
|
||||||
|
class OrderedFood(models.Model):
|
||||||
|
order = models.ForeignKey(
|
||||||
|
Order,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
verbose_name=_("order"),
|
||||||
|
)
|
||||||
|
|
||||||
|
meal = models.ForeignKey(
|
||||||
|
OrderedMeal,
|
||||||
|
on_delete=models.SET_NULL,
|
||||||
|
null=True,
|
||||||
|
default=None,
|
||||||
|
verbose_name=_("ordered meal"),
|
||||||
|
)
|
||||||
|
|
||||||
|
food = models.ForeignKey(
|
||||||
|
Food,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
verbose_name=_("food"),
|
||||||
|
)
|
||||||
|
|
||||||
|
options = models.ManyToManyField(
|
||||||
|
FoodOption,
|
||||||
|
blank=True,
|
||||||
|
verbose_name=_("options"),
|
||||||
|
)
|
||||||
|
|
||||||
|
remark = models.TextField(
|
||||||
|
blank=True,
|
||||||
|
default="",
|
||||||
|
verbose_name=_("remark"),
|
||||||
|
)
|
||||||
|
|
||||||
|
priority = models.CharField(
|
||||||
|
max_length=64,
|
||||||
|
blank=True,
|
||||||
|
default="",
|
||||||
|
verbose_name=_("priority request"),
|
||||||
|
)
|
||||||
|
|
||||||
|
gift = models.IntegerField(
|
||||||
|
verbose_name=_("gift"),
|
||||||
|
)
|
||||||
|
|
||||||
|
number = models.IntegerField(
|
||||||
|
verbose_name=_("number"),
|
||||||
|
help_text=_("How many times the user ordered this."),
|
||||||
|
)
|
||||||
|
|
||||||
|
status = models.CharField(
|
||||||
|
max_length=8,
|
||||||
|
choices=[
|
||||||
|
('QUEUED', _("queued")),
|
||||||
|
('READY', _("ready")),
|
||||||
|
('SERVED', _("served")),
|
||||||
|
('CANCELED', _("canceled")),
|
||||||
|
],
|
||||||
|
default='QUEUED',
|
||||||
|
verbose_name=_("status"),
|
||||||
|
)
|
||||||
|
|
||||||
|
served_date = models.DateTimeField(
|
||||||
|
null=True,
|
||||||
|
default=None,
|
||||||
|
verbose_name=_("served date")
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("ordered food")
|
||||||
|
verbose_name_plural = _("ordered food")
|
||||||
|
|
||||||
|
|
||||||
|
class SheetOrderTransaction(Transaction):
|
||||||
|
ordered_food = models.ForeignKey(
|
||||||
|
OrderedFood,
|
||||||
|
on_delete=models.PROTECT,
|
||||||
|
verbose_name=_("ordered food"),
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
return _("note sheet")
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_price(self):
|
||||||
|
if self.ordered_food.meal:
|
||||||
|
return self.ordered_food.meal.meal.price + self.ordered_food.meal.gift + sum(
|
||||||
|
sum(opt.extra_cost for opt in ordered_food.options.all())
|
||||||
|
for ordered_food in self.ordered_food.meal.orderedfood_set.exclude(status='CANCELED').all())
|
||||||
|
elif self.ordered_food.status == 'CANCELED':
|
||||||
|
return 0
|
||||||
|
else:
|
||||||
|
return self.ordered_food.food.price + self.ordered_food.gift \
|
||||||
|
+ sum(opt.extra_cost for opt in self.ordered_food.options.all())
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("sheet order transaction")
|
||||||
|
verbose_name_plural = _("sheet order transactions")
|
22
apps/sheets/tables.py
Normal file
22
apps/sheets/tables.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
import django_tables2 as tables
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
|
||||||
|
from sheets.models import Sheet
|
||||||
|
|
||||||
|
|
||||||
|
class SheetTable(tables.Table):
|
||||||
|
class Meta:
|
||||||
|
attrs = {
|
||||||
|
'class': 'table table-condensed table-striped table-hover'
|
||||||
|
}
|
||||||
|
model = Sheet
|
||||||
|
template_name = 'django_tables2/bootstrap4.html'
|
||||||
|
fields = ('name', 'date', )
|
||||||
|
row_attrs = {
|
||||||
|
'class': 'table-row',
|
||||||
|
'id': lambda record: "row-" + str(record.pk),
|
||||||
|
'data-href': lambda record: reverse_lazy('sheets:sheet_detail', args=(record.pk,))
|
||||||
|
}
|
86
apps/sheets/templates/sheets/food_form.html
Normal file
86
apps/sheets/templates/sheets/food_form.html
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% comment %}
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
{% endcomment %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="card bg-light mb-3">
|
||||||
|
<h3 class="card-header text-center">
|
||||||
|
{{ title }}
|
||||||
|
</h3>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|crispy }}
|
||||||
|
|
||||||
|
{# The next part concerns the option formset #}
|
||||||
|
{# Generate some hidden fields that manage the number of options, and make easier the parsing #}
|
||||||
|
{{ formset.management_form }}
|
||||||
|
<table class="table table-condensed table-striped">
|
||||||
|
{# Fill initial data #}
|
||||||
|
{% for form in formset %}
|
||||||
|
{% if forloop.first %}
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ form.name.label }}<span class="asteriskField">*</span></th>
|
||||||
|
<th>{{ form.extra_cost.label }}<span class="asteriskField">*</span></th>
|
||||||
|
<th>{{ form.available.label }}<span class="asteriskField">*</span></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="form_body">
|
||||||
|
{% endif %}
|
||||||
|
<tr class="row-formset">
|
||||||
|
<td>{{ form.name }}</td>
|
||||||
|
<td>{{ form.extra_cost }}</td>
|
||||||
|
<td>{{ form.available }}</td>
|
||||||
|
{# These fields are hidden but handled by the formset to link the id and the invoice id #}
|
||||||
|
{{ form.food }}
|
||||||
|
{{ form.id }}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{# Display buttons to add and remove options #}
|
||||||
|
<div class="card-body">
|
||||||
|
<button type="button" id="add_more" class="btn btn-success">{% trans "Add option" %}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{# Hidden div that store an empty product form, to be copied into new forms #}
|
||||||
|
<div id="empty_form" style="display: none;">
|
||||||
|
<table class='no_error'>
|
||||||
|
<tbody id="for_real">
|
||||||
|
<tr class="row-formset">
|
||||||
|
<td>{{ formset.empty_form.name }}</td>
|
||||||
|
<td>{{ formset.empty_form.extra_cost }} </td>
|
||||||
|
<td>{{ formset.empty_form.available }}</td>
|
||||||
|
{{ formset.empty_form.food }}
|
||||||
|
{{ formset.empty_form.id }}
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extrajavascript %}
|
||||||
|
<script>
|
||||||
|
/* script that handles add and remove lines */
|
||||||
|
IDS = {};
|
||||||
|
|
||||||
|
$("#id_foodoption_set-TOTAL_FORMS").val($(".row-formset").length - 1);
|
||||||
|
|
||||||
|
$('#add_more').click(function () {
|
||||||
|
let form_idx = $('#id_foodoption_set-TOTAL_FORMS').val();
|
||||||
|
$('#form_body').append($('#for_real').html().replace(/__prefix__/g, form_idx));
|
||||||
|
$('#id_foodoption_set-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
||||||
|
$('#id_foodoption_set-' + parseInt(form_idx) + '-id').val(IDS[parseInt(form_idx)]);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
21
apps/sheets/templates/sheets/meal_form.html
Normal file
21
apps/sheets/templates/sheets/meal_form.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% comment %}
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
{% endcomment %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="card bg-light mb-3">
|
||||||
|
<h3 class="card-header text-center">
|
||||||
|
{{ title }}
|
||||||
|
</h3>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|crispy }}
|
||||||
|
<button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
87
apps/sheets/templates/sheets/sheet_detail.html
Normal file
87
apps/sheets/templates/sheets/sheet_detail.html
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% load i18n %}
|
||||||
|
{% load pretty_money %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header text-center">
|
||||||
|
<h1>{{ sheet.name }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="alert alert-secondary">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-11">
|
||||||
|
{{ sheet.description }}
|
||||||
|
</div>
|
||||||
|
{% if can_change_sheet %}
|
||||||
|
<div class="col-sm-1">
|
||||||
|
<a class="badge badge-primary" href="{% url 'sheets:sheet_update' pk=sheet.pk %}">
|
||||||
|
<i class="fa fa-edit"></i>
|
||||||
|
{% trans "Edit" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3>{% trans "menu"|capfirst %} :</h3>
|
||||||
|
<ul>
|
||||||
|
{% for meal in sheet.meal_set.all %}
|
||||||
|
<li{% if not meal.available %} class="text-danger" style="text-decoration: line-through !important;" title="{% trans "This product is unavailable." %}"{% endif %}>
|
||||||
|
{{ meal }} ({{ meal.price|pretty_money }})
|
||||||
|
{% if can_change_sheet %}
|
||||||
|
<a href="{% url 'sheets:meal_update' pk=meal.pk %}" class="badge badge-primary">
|
||||||
|
<i class="fa fa-edit"></i>
|
||||||
|
{% trans "Edit" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
<hr>
|
||||||
|
{% for food in sheet.food_set.all %}
|
||||||
|
<li{% if not food.available %} class="text-danger" style="text-decoration: line-through !important;" title="{% trans "This product is unavailable." %}"{% endif %}>
|
||||||
|
{{ food }} ({{ food.price|pretty_money }})
|
||||||
|
<a href="{% url 'sheets:waiting_list' pk=food.pk %}" class="badge badge-primary">
|
||||||
|
<i class="fa fa-list"></i>
|
||||||
|
{% trans "Waiting list" %}
|
||||||
|
</a>
|
||||||
|
{% if can_change_sheet %}
|
||||||
|
<a href="{% url 'sheets:food_update' pk=food.pk %}" class="badge badge-primary">
|
||||||
|
<i class="fa fa-edit"></i>
|
||||||
|
{% trans "Edit" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if food.foodoption_set.all %}
|
||||||
|
<ul>
|
||||||
|
{% for option in food.foodoption_set.all %}
|
||||||
|
<li{% if not option.available %} class="text-danger" style="text-decoration: line-through !important;" title="{% trans "This product is unavailable." %}"{% endif %}>
|
||||||
|
{{ option }}{% if option.extra_cost %} ({{ option.extra_cost|pretty_money }}){% endif %}
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% empty %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
{% trans "The menu is empty for now." %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<div class="text-center">
|
||||||
|
{% if can_add_food %}
|
||||||
|
<a href="{% url 'sheets:food_create' pk=sheet.pk %}" class="btn btn-primary">{% trans "Add new food" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if can_add_meal %}
|
||||||
|
<a href="{% url 'sheets:meal_create' pk=sheet.pk %}" class="btn btn-primary">{% trans "Add new meal" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer text-center">
|
||||||
|
<a href="{% url 'sheets:sheet_order' pk=sheet.pk %}" class="btn btn-success">
|
||||||
|
{% trans "Order now" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
21
apps/sheets/templates/sheets/sheet_form.html
Normal file
21
apps/sheets/templates/sheets/sheet_form.html
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% comment %}
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
{% endcomment %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="card bg-light mb-3">
|
||||||
|
<h3 class="card-header text-center">
|
||||||
|
{{ title }}
|
||||||
|
</h3>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form|crispy }}
|
||||||
|
<button class="btn btn-primary" type="submit">{% trans "Submit" %}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
74
apps/sheets/templates/sheets/sheet_list.html
Normal file
74
apps/sheets/templates/sheets/sheet_list.html
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% comment %}
|
||||||
|
SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
{% endcomment %}
|
||||||
|
{% load render_table from django_tables2 %}
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="row justify-content-center mb-4">
|
||||||
|
<div class="col-md-10 text-center">
|
||||||
|
<input class="form-control mx-auto w-25" type="text" onkeyup="search_field_moved()" id="search_field"/>
|
||||||
|
{% if can_create_sheet %}
|
||||||
|
<hr>
|
||||||
|
<a class="btn btn-primary text-center my-4" href="{% url 'sheets:sheet_create' %}">{% trans "Create a sheet" %}</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-10">
|
||||||
|
<div class="card card-border shadow">
|
||||||
|
<div class="card-header text-center">
|
||||||
|
<h5> {% trans "Note sheet listing" %}</h5>
|
||||||
|
</div>
|
||||||
|
<div class="card-body px-0 py-0" id="sheets_table">
|
||||||
|
{% render_table table %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
{% block extrajavascript %}
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
function getInfo() {
|
||||||
|
var asked = $("#search_field").val();
|
||||||
|
/* on ne fait la requête que si on a au moins un caractère pour chercher */
|
||||||
|
var sel = $(".table-row");
|
||||||
|
if (asked.length >= 1) {
|
||||||
|
$.getJSON("/api/sheets/sheet/?format=json&search="+asked, function(buttons){
|
||||||
|
let selected_id = buttons.results.map((a => "#row-"+a.id));
|
||||||
|
if (selected_id.length)
|
||||||
|
$(".table-row,"+selected_id.join()).show();
|
||||||
|
$(".table-row").not(selected_id.join()).hide();
|
||||||
|
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
// show everything
|
||||||
|
$('table tr').show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var timer;
|
||||||
|
var timer_on;
|
||||||
|
/* Fontion appelée quand le texte change (délenche le timer) */
|
||||||
|
function search_field_moved(secondfield) {
|
||||||
|
if (timer_on) { // Si le timer a déjà été lancé, on réinitialise le compteur.
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout("getInfo(" + secondfield + ")", 300);
|
||||||
|
}
|
||||||
|
else { // Sinon, on le lance et on enregistre le fait qu'il tourne.
|
||||||
|
timer = setTimeout("getInfo(" + secondfield + ")", 300);
|
||||||
|
timer_on = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// clickable row
|
||||||
|
$(document).ready(function($) {
|
||||||
|
$(".table-row").click(function() {
|
||||||
|
window.document.location = $(this).data("href");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
0
apps/sheets/tests/__init__.py
Normal file
0
apps/sheets/tests/__init__.py
Normal file
26
apps/sheets/urls.py
Normal file
26
apps/sheets/urls.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from sheets.views import FoodCreateView, FoodUpdateView, MealCreateView, MealUpdateView, OrderView, \
|
||||||
|
SheetCreateView, SheetDetailView, SheetListView, SheetUpdateView, WaitingListDetailView, WaitingListView
|
||||||
|
|
||||||
|
app_name = 'sheets'
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('list/', SheetListView.as_view(), name="sheet_list"),
|
||||||
|
path('create/', SheetCreateView.as_view(), name="sheet_create"),
|
||||||
|
path('update/<int:pk>/', SheetUpdateView.as_view(), name="sheet_update"),
|
||||||
|
path('detail/<int:pk>/', SheetDetailView.as_view(), name="sheet_detail"),
|
||||||
|
path('food/create/<int:pk>/', FoodCreateView.as_view(), name="food_create"),
|
||||||
|
path('food/<int:pk>/update/', FoodUpdateView.as_view(), name="food_update"),
|
||||||
|
path('meal/create/<int:pk>/', MealCreateView.as_view(), name="meal_create"),
|
||||||
|
path('meal/<int:pk>/update/', MealUpdateView.as_view(), name="meal_update"),
|
||||||
|
path('order/<int:pk>/', OrderView.as_view(), name="sheet_order"),
|
||||||
|
path('waiting-list/<int:pk>/', WaitingListView.as_view(), name="waiting_list"),
|
||||||
|
path('waiting-list/<int:pk>/queued/', WaitingListDetailView.as_view(), name="queued_list"),
|
||||||
|
path('waiting-list/<int:pk>/ready/', WaitingListDetailView.as_view(), name="ready_list"),
|
||||||
|
path('waiting-list/<int:pk>/served/', WaitingListDetailView.as_view(), name="served_list"),
|
||||||
|
path('waiting-list/<int:pk>/canceled/', WaitingListDetailView.as_view(), name="canceled_list"),
|
||||||
|
]
|
444
apps/sheets/views.py
Normal file
444
apps/sheets/views.py
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
# Copyright (C) 2018-2022 by BDE ENS Paris-Saclay
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
from crispy_forms.bootstrap import Accordion, AccordionGroup, FormActions
|
||||||
|
from crispy_forms.helper import FormHelper
|
||||||
|
from crispy_forms.layout import Fieldset, Submit, Row, Field
|
||||||
|
from django import forms
|
||||||
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
|
from django.db import transaction
|
||||||
|
from django.forms import Form
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from django.views.generic import DetailView, UpdateView, FormView
|
||||||
|
from django_tables2 import SingleTableView
|
||||||
|
|
||||||
|
from note.models import Alias, Note
|
||||||
|
from note.templatetags.pretty_money import pretty_money
|
||||||
|
from note_kfet.inputs import AmountInput, Autocomplete
|
||||||
|
from permission.backends import PermissionBackend
|
||||||
|
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
|
||||||
|
|
||||||
|
from .forms import FoodForm, MealForm, SheetForm, FoodOptionsFormSet, FoodOptionFormSetHelper
|
||||||
|
from .models import Sheet, Food, Meal, Order, OrderedMeal, OrderedFood, SheetOrderTransaction
|
||||||
|
from .tables import SheetTable
|
||||||
|
|
||||||
|
|
||||||
|
class SheetListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
|
||||||
|
model = Sheet
|
||||||
|
table_class = SheetTable
|
||||||
|
ordering = '-date'
|
||||||
|
extra_context = {"title": _("Search note sheet")}
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
context["can_create_sheet"] = PermissionBackend.check_perm(self.request, "sheets.add_sheet", Sheet(
|
||||||
|
name="Test",
|
||||||
|
date=timezone.now(),
|
||||||
|
description="Test sheet",
|
||||||
|
))
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class SheetCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||||
|
model = Sheet
|
||||||
|
form_class = SheetForm
|
||||||
|
extra_context = {"title": _("Create note sheet")}
|
||||||
|
|
||||||
|
def get_sample_object(self):
|
||||||
|
return Sheet(
|
||||||
|
name="Test",
|
||||||
|
date=timezone.now(),
|
||||||
|
description="Test",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class SheetUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
||||||
|
model = Sheet
|
||||||
|
form_class = SheetForm
|
||||||
|
extra_context = {"title": _("Update note sheet")}
|
||||||
|
|
||||||
|
|
||||||
|
class SheetDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
||||||
|
model = Sheet
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data()
|
||||||
|
|
||||||
|
context['can_change_sheet'] = PermissionBackend.check_perm(self.request, 'sheets.change_sheet', self.object)
|
||||||
|
context['can_add_meal'] = PermissionBackend.check_perm(self.request,
|
||||||
|
'sheets.add_meal',
|
||||||
|
Meal(sheet=self.object, name="Test", price=500))
|
||||||
|
context['can_add_food'] = PermissionBackend.check_perm(self.request,
|
||||||
|
'sheets.add_food',
|
||||||
|
Food(sheet=self.object, name="Test", price=500))
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
class FoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||||
|
model = Food
|
||||||
|
form_class = FoodForm
|
||||||
|
extra_context = {"title": _("Create new food")}
|
||||||
|
|
||||||
|
def get_sample_object(self):
|
||||||
|
return Food(
|
||||||
|
sheet_id=self.kwargs['pk'],
|
||||||
|
name="Test",
|
||||||
|
price=500,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
form = context['form']
|
||||||
|
form.helper = FormHelper()
|
||||||
|
# Remove form tag on the generation of the form in the template (already present on the template)
|
||||||
|
form.helper.form_tag = False
|
||||||
|
# The formset handles the set of the products
|
||||||
|
form_set = FoodOptionsFormSet(instance=form.instance)
|
||||||
|
context['formset'] = form_set
|
||||||
|
context['helper'] = FoodOptionFormSetHelper()
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.instance.sheet_id = self.kwargs['pk']
|
||||||
|
|
||||||
|
# For each product, we save it
|
||||||
|
formset = FoodOptionsFormSet(self.request.POST, instance=form.instance)
|
||||||
|
if formset.is_valid():
|
||||||
|
for f in formset:
|
||||||
|
# We don't save the product if the designation is not entered, ie. if the line is empty
|
||||||
|
if f.is_valid() and f.instance.name:
|
||||||
|
f.save()
|
||||||
|
f.instance.save()
|
||||||
|
else:
|
||||||
|
f.instance = None
|
||||||
|
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy('sheets:sheet_detail', args=(self.kwargs['pk'],))
|
||||||
|
|
||||||
|
|
||||||
|
class FoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
||||||
|
model = Food
|
||||||
|
form_class = FoodForm
|
||||||
|
extra_context = {"title": _("Update food")}
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
form = context['form']
|
||||||
|
form.helper = FormHelper()
|
||||||
|
# Remove form tag on the generation of the form in the template (already present on the template)
|
||||||
|
form.helper.form_tag = False
|
||||||
|
# The formset handles the set of the products
|
||||||
|
form_set = FoodOptionsFormSet(instance=form.instance)
|
||||||
|
context['formset'] = form_set
|
||||||
|
context['helper'] = FoodOptionFormSetHelper()
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
# For each product, we save it
|
||||||
|
formset = FoodOptionsFormSet(self.request.POST, instance=form.instance)
|
||||||
|
if formset.is_valid():
|
||||||
|
for f in formset:
|
||||||
|
# We don't save the product if the designation is not entered, ie. if the line is empty
|
||||||
|
if f.is_valid() and f.instance.name:
|
||||||
|
f.save()
|
||||||
|
f.instance.save()
|
||||||
|
else:
|
||||||
|
f.instance = None
|
||||||
|
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy('sheets:sheet_detail', args=(self.object.sheet_id,))
|
||||||
|
|
||||||
|
|
||||||
|
class MealCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||||
|
model = Meal
|
||||||
|
form_class = MealForm
|
||||||
|
extra_context = {"title": _("Create new meal")}
|
||||||
|
|
||||||
|
def get_sample_object(self):
|
||||||
|
return Meal(
|
||||||
|
sheet_id=self.kwargs['pk'],
|
||||||
|
name="Test",
|
||||||
|
price=500,
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_form(self, form_class=None):
|
||||||
|
form = super().get_form(form_class)
|
||||||
|
form.fields['content'].queryset = form.fields['content'].queryset.filter(sheet_id=self.kwargs['pk'])
|
||||||
|
return form
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
form.instance.sheet_id = self.kwargs['pk']
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy('sheets:sheet_detail', args=(self.object.sheet_id,))
|
||||||
|
|
||||||
|
|
||||||
|
class MealUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
||||||
|
model = Meal
|
||||||
|
form_class = MealForm
|
||||||
|
extra_context = {"title": _("Update meal")}
|
||||||
|
|
||||||
|
def get_form(self, form_class=None):
|
||||||
|
form = super().get_form(form_class)
|
||||||
|
form.fields['content'].queryset = form.fields['content'].queryset.filter(sheet=self.object.sheet)
|
||||||
|
return form
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy('sheets:sheet_detail', args=(self.object.sheet_id,))
|
||||||
|
|
||||||
|
|
||||||
|
class OrderView(LoginRequiredMixin, FormView, DetailView):
|
||||||
|
model = Sheet
|
||||||
|
template_name = 'sheets/order.html'
|
||||||
|
extra_context = {'title': _("Order now")}
|
||||||
|
|
||||||
|
def get_form(self, form_class=None):
|
||||||
|
form = Form()
|
||||||
|
form.helper = FormHelper()
|
||||||
|
layout_fields = []
|
||||||
|
|
||||||
|
self.object = self.get_object()
|
||||||
|
|
||||||
|
form.fields['note'] = forms.ModelChoiceField(
|
||||||
|
queryset=Note.objects.filter(PermissionBackend.filter_queryset(self.request, Note, 'note.view_note')),
|
||||||
|
label=_("Orderer"),
|
||||||
|
initial=self.request.user.note,
|
||||||
|
widget=Autocomplete(
|
||||||
|
model=Note,
|
||||||
|
attrs={
|
||||||
|
"api_url": "/api/note/note/",
|
||||||
|
'placeholder': _("Who orders")
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
layout_fields.append(Field('note', css_class='is-valid'))
|
||||||
|
|
||||||
|
for meal in self.object.meal_set.filter(available=True).all():
|
||||||
|
form.fields[f'meal_{meal.id}_quantity'] = forms.IntegerField(
|
||||||
|
label=_("Quantity"),
|
||||||
|
initial=0,
|
||||||
|
)
|
||||||
|
form.fields[f'meal_{meal.id}_gift'] = forms.IntegerField(
|
||||||
|
label=_("gift").capitalize(),
|
||||||
|
initial=0,
|
||||||
|
widget=AmountInput(),
|
||||||
|
help_text=_("Be careful: this gift will be multiplied for each order."),
|
||||||
|
)
|
||||||
|
form.fields[f'meal_{meal.id}_remark'] = forms.CharField(
|
||||||
|
max_length=255,
|
||||||
|
required=False,
|
||||||
|
label=_("remark").capitalize(),
|
||||||
|
help_text=_("Allergies,…"),
|
||||||
|
)
|
||||||
|
form.fields[f'meal_{meal.id}_priority'] = forms.CharField(
|
||||||
|
max_length=64,
|
||||||
|
required=False,
|
||||||
|
label=_("priority request").capitalize(),
|
||||||
|
help_text=_("Lesson at 13h30,…"),
|
||||||
|
)
|
||||||
|
|
||||||
|
ag = AccordionGroup(f"{meal} ({pretty_money(meal.price)})",
|
||||||
|
Row(Field(f'meal_{meal.id}_quantity', wrapper_class='col-sm-9'),
|
||||||
|
Field(f'meal_{meal.id}_gift', wrapper_class='col-sm-3')),
|
||||||
|
Row(Field(f'meal_{meal.id}_remark', wrapper_class='col-sm-9'),
|
||||||
|
Field(f'meal_{meal.id}_priority', wrapper_class='col-sm-3')))
|
||||||
|
|
||||||
|
for food in meal.content.filter(available=True).all():
|
||||||
|
if food.foodoption_set.count():
|
||||||
|
options_fieldset = Fieldset(_("Options for ") + str(food))
|
||||||
|
options_row = Row(css_class='ml-0')
|
||||||
|
for option in food.foodoption_set.filter(available=True).all():
|
||||||
|
form.fields[f'meal_{meal.id}_food_{food.id}_option_{option.id}'] = forms.BooleanField(
|
||||||
|
label=f"{option}{f' ({pretty_money(option.extra_cost)})' if option.extra_cost else ''}",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
options_row.fields.append(
|
||||||
|
Field(f'meal_{meal.id}_food_{food.id}_option_{option.id}', wrapper_class='col-sm-12'))
|
||||||
|
options_fieldset.fields.append(options_row)
|
||||||
|
ag.fields.append(options_fieldset)
|
||||||
|
|
||||||
|
layout_fields.append(ag)
|
||||||
|
|
||||||
|
for food in self.object.food_set.filter(available=True).all():
|
||||||
|
form.fields[f'food_{food.id}_quantity'] = forms.IntegerField(
|
||||||
|
label=_("quantity").capitalize(),
|
||||||
|
initial=0,
|
||||||
|
)
|
||||||
|
form.fields[f'food_{food.id}_gift'] = forms.IntegerField(
|
||||||
|
label=_("gift").capitalize(),
|
||||||
|
initial=0,
|
||||||
|
widget=AmountInput(),
|
||||||
|
help_text=_("Be careful: this gift will be multiplied for each order."),
|
||||||
|
)
|
||||||
|
form.fields[f'food_{food.id}_remark'] = forms.CharField(
|
||||||
|
max_length=255,
|
||||||
|
required=False,
|
||||||
|
label=_("remark").capitalize(),
|
||||||
|
help_text=_("Allergies,…"),
|
||||||
|
)
|
||||||
|
form.fields[f'food_{food.id}_priority'] = forms.CharField(
|
||||||
|
max_length=255,
|
||||||
|
required=False,
|
||||||
|
label=_("priority request").capitalize(),
|
||||||
|
help_text=_("Lesson at 13h30,…"),
|
||||||
|
)
|
||||||
|
|
||||||
|
ag = AccordionGroup(f"{food} ({pretty_money(food.price)})",
|
||||||
|
Row(Field(f'food_{food.id}_quantity', wrapper_class='col-sm-9'),
|
||||||
|
Field(f'food_{food.id}_gift', wrapper_class='col-sm-3')),
|
||||||
|
Row(Field(f'food_{food.id}_remark', wrapper_class='col-sm-9'),
|
||||||
|
Field(f'food_{food.id}_priority', wrapper_class='col-sm-3')))
|
||||||
|
|
||||||
|
if food.foodoption_set.count():
|
||||||
|
options_fieldset = Fieldset(_("Options"))
|
||||||
|
options_row = Row(css_class='ml-0')
|
||||||
|
for option in food.foodoption_set.all():
|
||||||
|
form.fields[f'food_{food.id}_option_{option.id}'] = forms.BooleanField(
|
||||||
|
label=f"{option}{f' ({pretty_money(option.extra_cost)})' if option.extra_cost else ''}",
|
||||||
|
required=False,
|
||||||
|
)
|
||||||
|
options_row.fields.append(Field(f'food_{food.id}_option_{option.id}', wrapper_class='col-sm-12'))
|
||||||
|
options_fieldset.fields.append(options_row)
|
||||||
|
ag.fields.append(options_fieldset)
|
||||||
|
|
||||||
|
layout_fields.append(ag)
|
||||||
|
|
||||||
|
layout_fields.append(FormActions(Submit('submit', _("Order now"))))
|
||||||
|
|
||||||
|
form.helper.layout = Accordion(*layout_fields)
|
||||||
|
|
||||||
|
if self.request.method in ['PUT', 'POST']:
|
||||||
|
form.data = self.request.POST
|
||||||
|
form.files = self.request.FILES
|
||||||
|
form.is_bound = not form.data or not form.files
|
||||||
|
|
||||||
|
return form
|
||||||
|
|
||||||
|
def form_valid(self, form):
|
||||||
|
data = form.cleaned_data
|
||||||
|
sheet = self.get_object()
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
order = Order.objects.create(sheet_id=self.kwargs['pk'], note=data['note'])
|
||||||
|
|
||||||
|
total_quantity = 0
|
||||||
|
|
||||||
|
for meal in sheet.meal_set.filter(available=True).all():
|
||||||
|
quantity = data[f'meal_{meal.id}_quantity']
|
||||||
|
if not quantity:
|
||||||
|
continue
|
||||||
|
|
||||||
|
total_quantity += quantity
|
||||||
|
gift = data[f'meal_{meal.id}_gift']
|
||||||
|
remark = data[f'meal_{meal.id}_remark'] or ''
|
||||||
|
priority = data[f'meal_{meal.id}_priority'] or ''
|
||||||
|
ordered_meal = OrderedMeal.objects.create(order=order, meal=meal, gift=gift)
|
||||||
|
|
||||||
|
for ignored in range(quantity):
|
||||||
|
for food in meal.content.filter(available=True).all():
|
||||||
|
n = OrderedFood.objects.filter(order__sheet_id=self.kwargs['pk'],
|
||||||
|
order__note=order.note,
|
||||||
|
order__date__gte=timezone.now() - timedelta(hours=6),
|
||||||
|
food=food).exclude(status='CANCELED').count()
|
||||||
|
of = OrderedFood.objects.create(order=order, meal=ordered_meal, food=food,
|
||||||
|
remark=remark, priority=priority, number=n + 1, gift=0)
|
||||||
|
|
||||||
|
for option in food.foodoption_set.filter(available=True).all():
|
||||||
|
if data[f'meal_{meal.id}_food_{food.id}_option_{option.id}']:
|
||||||
|
of.options.add(option)
|
||||||
|
of.save()
|
||||||
|
|
||||||
|
first_food = ordered_meal.orderedfood_set.first()
|
||||||
|
tr = SheetOrderTransaction(source_id=order.note_id, destination=first_food.food.club.note,
|
||||||
|
source_alias=str(order.note), destination_alias=first_food.food.club.name,
|
||||||
|
quantity=quantity, ordered_food=first_food,
|
||||||
|
reason=f"{meal.name} - {sheet.name}")
|
||||||
|
tr.amount = tr.get_price / tr.quantity
|
||||||
|
tr.save()
|
||||||
|
|
||||||
|
for food in sheet.food_set.filter(available=True).all():
|
||||||
|
quantity = data[f'food_{food.id}_quantity']
|
||||||
|
if not quantity:
|
||||||
|
continue
|
||||||
|
|
||||||
|
total_quantity += quantity
|
||||||
|
gift = data[f'food_{meal.id}_gift']
|
||||||
|
remark = data[f'food_{meal.id}_remark'] or ''
|
||||||
|
priority = data[f'food_{meal.id}_priority'] or ''
|
||||||
|
|
||||||
|
for ignored in range(quantity):
|
||||||
|
n = OrderedFood.objects.filter(order__sheet_id=self.kwargs['pk'],
|
||||||
|
order__note=order.note,
|
||||||
|
order__date__gte=timezone.now() - timedelta(hours=6),
|
||||||
|
food=food).exclude(state='CANCELED').count()
|
||||||
|
of = OrderedFood.objects.create(order=order, food=food, gift=gift,
|
||||||
|
remark=remark, priority=priority, number=n + 1)
|
||||||
|
|
||||||
|
for option in food.foodoption_set.filter(available=True).all():
|
||||||
|
if data[f'meal_{meal.id}_food_{food.id}_option_{option.id}']:
|
||||||
|
of.options.add(option)
|
||||||
|
of.options.save()
|
||||||
|
|
||||||
|
tr = SheetOrderTransaction(source_id=order.note_id, destination_id=first_food.club.note,
|
||||||
|
source_alias=str(order.note), destination_alias=first_food.club.name,
|
||||||
|
quantity=quantity, ordered_food=of,
|
||||||
|
reason=f"{food.name} - {sheet.name}")
|
||||||
|
tr.amount = tr.get_price / tr.quantity
|
||||||
|
tr.save()
|
||||||
|
|
||||||
|
if total_quantity == 0:
|
||||||
|
form.add_error(None, _("You didn't select anything."))
|
||||||
|
transaction.rollback()
|
||||||
|
return self.form_invalid(form)
|
||||||
|
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
return reverse_lazy('sheets:sheet_detail', args=(self.kwargs['pk'],))
|
||||||
|
|
||||||
|
|
||||||
|
class WaitingListView(ProtectQuerysetMixin, DetailView):
|
||||||
|
model = Food
|
||||||
|
template_name = 'sheets/waiting_list.html'
|
||||||
|
extra_context = {'title': _("Waiting list")}
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
content = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
content['queue'] = OrderedFood.objects.filter(food_id=self.kwargs['pk'], status='QUEUED')\
|
||||||
|
.order_by('-priority', 'number', 'order__date').all()
|
||||||
|
content['ready'] = OrderedFood.objects.filter(food_id=self.kwargs['pk'], status='READY')\
|
||||||
|
.order_by('served_date').all()
|
||||||
|
|
||||||
|
return content
|
||||||
|
|
||||||
|
|
||||||
|
class WaitingListDetailView(ProtectQuerysetMixin, DetailView):
|
||||||
|
model = Food
|
||||||
|
template_name = 'sheets/waiting_list_detail.html'
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
list_type = 'CANCELED' if 'canceled' in self.request.path else \
|
||||||
|
'SERVED' if 'served' in self.request.path else \
|
||||||
|
'READY' if 'ready' in self.request.path else 'QUEUED'
|
||||||
|
context['list_type'] = list_type
|
||||||
|
context['orders'] = OrderedFood.objects.filter(food_id=self.kwargs['pk'], status=list_type)\
|
||||||
|
.order_by('served_date', '-priority', 'number', 'order__date').all()
|
||||||
|
context['title'] = self.object.name + " - " + _(list_type.lower()).capitalize()
|
||||||
|
|
||||||
|
return context
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2023-01-29 22:48
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('treasury', '0004_auto_20211005_1544'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='invoice',
|
|
||||||
name='bde',
|
|
||||||
field=models.CharField(choices=[('TotalistSpies', 'Tota[list]Spies'), ('Saperlistpopette', 'Saper[list]popette'), ('Finalist', 'Fina[list]'), ('Listorique', '[List]orique'), ('Satellist', 'Satel[list]'), ('Monopolist', 'Monopo[list]'), ('Kataclist', 'Katac[list]')], default='TotalistSpies', max_length=32, verbose_name='BDE'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2023-04-14 14:51
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('treasury', '0005_auto_20230129_2348'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='invoice',
|
|
||||||
name='bde',
|
|
||||||
field=models.CharField(choices=[('SecretStorlist', 'SecretStor[list]'), ('TotalistSpies', 'Tota[list]Spies'), ('Saperlistpopette', 'Saper[list]popette'), ('Finalist', 'Fina[list]'), ('Listorique', '[List]orique'), ('Satellist', 'Satel[list]'), ('Monopolist', 'Monopo[list]'), ('Kataclist', 'Katac[list]')], default='SecretStorlist', max_length=32, verbose_name='BDE'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,5 +1,6 @@
|
|||||||
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
import datetime
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -11,8 +12,7 @@ from django.db.models import Q
|
|||||||
from django.template.loader import render_to_string
|
from django.template.loader import render_to_string
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
# from member.models import Club, Membership # Club unused because of disabled soge
|
from member.models import Club, Membership
|
||||||
from member.models import Membership
|
|
||||||
from note.models import NoteSpecial, SpecialTransaction, MembershipTransaction, NoteUser
|
from note.models import NoteSpecial, SpecialTransaction, MembershipTransaction, NoteUser
|
||||||
|
|
||||||
|
|
||||||
@ -28,10 +28,8 @@ class Invoice(models.Model):
|
|||||||
|
|
||||||
bde = models.CharField(
|
bde = models.CharField(
|
||||||
max_length=32,
|
max_length=32,
|
||||||
default='SecretStorlist',
|
default='Saperlistpopette',
|
||||||
choices=(
|
choices=(
|
||||||
('SecretStorlist', 'SecretStor[list]'),
|
|
||||||
('TotalistSpies', 'Tota[list]Spies'),
|
|
||||||
('Saperlistpopette', 'Saper[list]popette'),
|
('Saperlistpopette', 'Saper[list]popette'),
|
||||||
('Finalist', 'Fina[list]'),
|
('Finalist', 'Fina[list]'),
|
||||||
('Listorique', '[List]orique'),
|
('Listorique', '[List]orique'),
|
||||||
@ -97,7 +95,7 @@ class Invoice(models.Model):
|
|||||||
products = self.products.all()
|
products = self.products.all()
|
||||||
|
|
||||||
self.place = "Gif-sur-Yvette"
|
self.place = "Gif-sur-Yvette"
|
||||||
self.my_name = "BDE ENS Paris Saclay"
|
self.my_name = "BDE ENS Cachan"
|
||||||
self.my_address_street = "4 avenue des Sciences"
|
self.my_address_street = "4 avenue des Sciences"
|
||||||
self.my_city = "91190 Gif-sur-Yvette"
|
self.my_city = "91190 Gif-sur-Yvette"
|
||||||
self.bank_code = 30003
|
self.bank_code = 30003
|
||||||
@ -312,8 +310,8 @@ class SogeCredit(models.Model):
|
|||||||
amount = sum(transaction.total for transaction in self.transactions.all())
|
amount = sum(transaction.total for transaction in self.transactions.all())
|
||||||
if 'wei' in settings.INSTALLED_APPS:
|
if 'wei' in settings.INSTALLED_APPS:
|
||||||
from wei.models import WEIMembership
|
from wei.models import WEIMembership
|
||||||
if not WEIMembership.objects\
|
if not WEIMembership.objects.filter(club__weiclub__year=datetime.date.today().year, user=self.user)\
|
||||||
.filter(club__weiclub__year=self.credit_transaction.created_at.year, user=self.user).exists():
|
.exists():
|
||||||
# 80 € for people that don't go to WEI
|
# 80 € for people that don't go to WEI
|
||||||
amount += 8000
|
amount += 8000
|
||||||
return amount
|
return amount
|
||||||
@ -326,23 +324,22 @@ class SogeCredit(models.Model):
|
|||||||
if self.valid or not self.pk:
|
if self.valid or not self.pk:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Soge do not pay BDE and kfet memberships since 2022
|
bde = Club.objects.get(name="BDE")
|
||||||
# bde = Club.objects.get(name="BDE")
|
kfet = Club.objects.get(name="Kfet")
|
||||||
# kfet = Club.objects.get(name="Kfet")
|
bde_qs = Membership.objects.filter(user=self.user, club=bde, date_start__gte=bde.membership_start)
|
||||||
# bde_qs = Membership.objects.filter(user=self.user, club=bde, date_start__gte=bde.membership_start)
|
kfet_qs = Membership.objects.filter(user=self.user, club=kfet, date_start__gte=kfet.membership_start)
|
||||||
# kfet_qs = Membership.objects.filter(user=self.user, club=kfet, date_start__gte=kfet.membership_start)
|
|
||||||
|
|
||||||
# if bde_qs.exists():
|
if bde_qs.exists():
|
||||||
# m = bde_qs.get()
|
m = bde_qs.get()
|
||||||
# if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
|
if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
|
||||||
# if m.transaction not in self.transactions.all():
|
if m.transaction not in self.transactions.all():
|
||||||
# self.transactions.add(m.transaction)
|
self.transactions.add(m.transaction)
|
||||||
#
|
|
||||||
# if kfet_qs.exists():
|
if kfet_qs.exists():
|
||||||
# m = kfet_qs.get()
|
m = kfet_qs.get()
|
||||||
# if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
|
if MembershipTransaction.objects.filter(membership=m).exists(): # non-free membership
|
||||||
# if m.transaction not in self.transactions.all():
|
if m.transaction not in self.transactions.all():
|
||||||
# self.transactions.add(m.transaction)
|
self.transactions.add(m.transaction)
|
||||||
|
|
||||||
if 'wei' in settings.INSTALLED_APPS:
|
if 'wei' in settings.INSTALLED_APPS:
|
||||||
from wei.models import WEIClub
|
from wei.models import WEIClub
|
||||||
@ -388,6 +385,7 @@ class SogeCredit(models.Model):
|
|||||||
for tr in self.transactions.all():
|
for tr in self.transactions.all():
|
||||||
tr.valid = True
|
tr.valid = True
|
||||||
tr._force_save = True
|
tr._force_save = True
|
||||||
|
tr.created_at = timezone.now()
|
||||||
tr.save()
|
tr.save()
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
@ -436,11 +434,12 @@ class SogeCredit(models.Model):
|
|||||||
for tr in self.transactions.all():
|
for tr in self.transactions.all():
|
||||||
tr._force_save = True
|
tr._force_save = True
|
||||||
tr.valid = True
|
tr.valid = True
|
||||||
|
tr.created_at = timezone.now()
|
||||||
tr.save()
|
tr.save()
|
||||||
if self.credit_transaction:
|
if self.credit_transaction:
|
||||||
# If the soge credit is deleted while the user is not validated yet,
|
# If the soge credit is deleted while the user is not validated yet,
|
||||||
# there is not credit transaction.
|
# there is not credit transaction.
|
||||||
# There is a credit transaction if the user declares that no bank account
|
# There is a credit transaction iff the user declares that no bank account
|
||||||
# was opened after the validation of the account.
|
# was opened after the validation of the account.
|
||||||
self.credit_transaction.valid = False
|
self.credit_transaction.valid = False
|
||||||
self.credit_transaction.reason += " (invalide)"
|
self.credit_transaction.reason += " (invalide)"
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 690 KiB |
Binary file not shown.
Before Width: | Height: | Size: 77 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.0 MiB |
Binary file not shown.
Before Width: | Height: | Size: 35 KiB |
@ -105,8 +105,8 @@
|
|||||||
|
|
||||||
\renewcommand{\headrulewidth}{0pt}
|
\renewcommand{\headrulewidth}{0pt}
|
||||||
\cfoot{
|
\cfoot{
|
||||||
\small{\MonNom ~--~ \MonAdresseRue ~ \MonAdresseVille ~--~ Téléphone : +33(0)7 78 17 22 34\newline
|
\small{\MonNom ~--~ \MonAdresseRue ~ \MonAdresseVille ~--~ Téléphone : +33(0)6 89 88 56 50\newline
|
||||||
Site web : bde.ens-cachan.fr ~--~ E-mail : tresorerie.bde@lists.crans.org \newline Numéro SIRET : 399 485 838 00029
|
Site web : bde.ens-cachan.fr ~--~ E-mail : tresorerie.bde@lists.crans.org \newline Numéro SIRET : 399 485 838 00011
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
@ -38,7 +38,7 @@ class WEIRegistrationForm(forms.ModelForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = WEIRegistration
|
model = WEIRegistration
|
||||||
exclude = ('wei', 'clothing_cut')
|
exclude = ('wei', )
|
||||||
widgets = {
|
widgets = {
|
||||||
"user": Autocomplete(
|
"user": Autocomplete(
|
||||||
User,
|
User,
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm
|
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm
|
||||||
from .wei2023 import WEISurvey2023
|
from .wei2022 import WEISurvey2022
|
||||||
|
|
||||||
|
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey',
|
'WEISurvey', 'WEISurveyInformation', 'WEISurveyAlgorithm', 'CurrentSurvey',
|
||||||
]
|
]
|
||||||
|
|
||||||
CurrentSurvey = WEISurvey2023
|
CurrentSurvey = WEISurvey2022
|
||||||
|
@ -148,7 +148,7 @@ class WEISurvey2021(WEISurvey):
|
|||||||
buses = cls.get_algorithm_class().get_buses()
|
buses = cls.get_algorithm_class().get_buses()
|
||||||
return sum([cls.get_algorithm_class().get_bus_information(bus).scores[word] for bus in buses]) / buses.count()
|
return sum([cls.get_algorithm_class().get_bus_information(bus).scores[word] for bus in buses]) / buses.count()
|
||||||
|
|
||||||
# @lru_cache
|
@lru_cache()
|
||||||
def score(self, bus):
|
def score(self, bus):
|
||||||
if not self.is_complete():
|
if not self.is_complete():
|
||||||
raise ValueError("Survey is not ended, can't calculate score")
|
raise ValueError("Survey is not ended, can't calculate score")
|
||||||
@ -159,11 +159,11 @@ class WEISurvey2021(WEISurvey):
|
|||||||
- self.word_mean(getattr(self.information, 'word' + str(i))) for i in range(1, 21)) / 20
|
- self.word_mean(getattr(self.information, 'word' + str(i))) for i in range(1, 21)) / 20
|
||||||
return s
|
return s
|
||||||
|
|
||||||
# @lru_cache()
|
@lru_cache()
|
||||||
def scores_per_bus(self):
|
def scores_per_bus(self):
|
||||||
return {bus: self.score(bus) for bus in self.get_algorithm_class().get_buses()}
|
return {bus: self.score(bus) for bus in self.get_algorithm_class().get_buses()}
|
||||||
|
|
||||||
# @lru_cache()
|
@lru_cache()
|
||||||
def ordered_buses(self):
|
def ordered_buses(self):
|
||||||
values = list(self.scores_per_bus().items())
|
values = list(self.scores_per_bus().items())
|
||||||
values.sort(key=lambda item: -item[1])
|
values.sort(key=lambda item: -item[1])
|
||||||
|
@ -14,17 +14,14 @@ from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInf
|
|||||||
from ...models import WEIMembership
|
from ...models import WEIMembership
|
||||||
|
|
||||||
WORDS = [
|
WORDS = [
|
||||||
'ABBA', 'After', 'Alcoolique anonyme', 'Ambiance festive', 'Années 2000', 'Apéro', 'Art',
|
'13 organisé', '3ième mi temps', 'Années 2000', 'Apéro', 'BBQ', 'BP', 'Beauf', 'Binge drinking', 'Bon enfant',
|
||||||
'Baby foot billard biere pong', 'BBQ', 'Before', 'Bière pong', 'Bon enfant', 'Calme', 'Canapé',
|
'Cartouche', 'Catacombes', 'Chansons paillardes', 'Chansons populaires', 'Chanteur', 'Chartreuse', 'Chill',
|
||||||
'Chanson paillarde', 'Chanson populaire', 'Chartreuse', 'Cheerleader', 'Chill', 'Choré',
|
'Core', 'DJ', 'Dancefloor', 'Danse', 'David Guetta', 'Disco', 'Eau de vie', 'Électro', 'Escalade', 'Familial',
|
||||||
'Cinéma', 'Cocktail', 'Comédie musicle', 'Commercial', 'Copaing', 'Danse', 'Dancefloor',
|
'Fanfare', 'Fracassage', 'Féria', 'Hard rock', 'Hoeggarden', 'House', 'Huit-six', 'IPA', 'Inclusif', 'Inferno',
|
||||||
'Electro', 'Fanfare', 'Gin tonic', 'Inclusif', 'Jazz', "Jeux d'alcool", 'Jeux de carte',
|
'Introverti', 'Jager bomb', 'Jazz', 'Jeux d\'alcool', 'Jeux de rôles', 'Jeux vidéo', 'Jul', 'Jus de fruit',
|
||||||
'Jeux de rôle', 'Jeux de société', 'JUL', 'Jus de fruit', 'Kfet', 'Kleptomanie assurée',
|
'Karaoké', 'LGBTQI+', 'Lady Gaga', 'Loup garou', 'Morning beer', 'Métal', 'Nuit blanche', 'Ovalie', 'Psychedelic',
|
||||||
'LGBTQ+', 'Livre', 'Morning beer', 'Musique', 'NAPS', 'Paillettes', 'Pastis', 'Paté Hénaff',
|
'Pétanque', 'Rave', 'Reggae', 'Rhum', 'Ricard', 'Rock', 'Rosé', 'Rétro', 'Séducteur', 'Techno', 'Thérapie taxi',
|
||||||
'Peluche', 'Pena baiona', "Peu d'alcool", 'Pilier de bar', 'PMU', 'Poulpe', 'Punch', 'Rap',
|
'Théâtre', 'Trap', 'Turn up', 'Underground', 'Volley', 'Wati B', 'Zinédine Zidane',
|
||||||
'Réveil', 'Rock', 'Rugby', 'Sandwich', 'Serge', 'Shot', 'Sociable', 'Spectacle', 'Techno',
|
|
||||||
'Techno house', 'Thérapie Taxi', 'Tradition kchanaises', 'Troisième mi-temps', 'Turn up',
|
|
||||||
'Vodka', 'Vodka pomme', 'Volley', 'Vomi stratégique'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
@ -151,7 +148,7 @@ class WEISurvey2022(WEISurvey):
|
|||||||
buses = cls.get_algorithm_class().get_buses()
|
buses = cls.get_algorithm_class().get_buses()
|
||||||
return sum([cls.get_algorithm_class().get_bus_information(bus).scores[word] for bus in buses]) / buses.count()
|
return sum([cls.get_algorithm_class().get_bus_information(bus).scores[word] for bus in buses]) / buses.count()
|
||||||
|
|
||||||
# @lru_cache()
|
@lru_cache()
|
||||||
def score(self, bus):
|
def score(self, bus):
|
||||||
if not self.is_complete():
|
if not self.is_complete():
|
||||||
raise ValueError("Survey is not ended, can't calculate score")
|
raise ValueError("Survey is not ended, can't calculate score")
|
||||||
@ -162,11 +159,11 @@ class WEISurvey2022(WEISurvey):
|
|||||||
- self.word_mean(getattr(self.information, 'word' + str(i))) for i in range(1, 21)) / 20
|
- self.word_mean(getattr(self.information, 'word' + str(i))) for i in range(1, 21)) / 20
|
||||||
return s
|
return s
|
||||||
|
|
||||||
# @lru_cache()
|
@lru_cache()
|
||||||
def scores_per_bus(self):
|
def scores_per_bus(self):
|
||||||
return {bus: self.score(bus) for bus in self.get_algorithm_class().get_buses()}
|
return {bus: self.score(bus) for bus in self.get_algorithm_class().get_buses()}
|
||||||
|
|
||||||
# @lru_cache()
|
@lru_cache()
|
||||||
def ordered_buses(self):
|
def ordered_buses(self):
|
||||||
values = list(self.scores_per_bus().items())
|
values = list(self.scores_per_bus().items())
|
||||||
values.sort(key=lambda item: -item[1])
|
values.sort(key=lambda item: -item[1])
|
||||||
|
@ -1,296 +0,0 @@
|
|||||||
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
import time
|
|
||||||
from functools import lru_cache
|
|
||||||
from random import Random
|
|
||||||
|
|
||||||
from django import forms
|
|
||||||
from django.db import transaction
|
|
||||||
from django.db.models import Q
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
|
|
||||||
from .base import WEISurvey, WEISurveyInformation, WEISurveyAlgorithm, WEIBusInformation
|
|
||||||
from ...models import WEIMembership
|
|
||||||
|
|
||||||
WORDS = [
|
|
||||||
'ABBA', 'After', 'Alcoolique anonyme', 'Ambiance festive', 'Années 2000', 'Apéro', 'Art',
|
|
||||||
'Baby foot billard biere pong', 'BBQ', 'Before', 'Bière pong', 'Bon enfant', 'Calme', 'Canapé',
|
|
||||||
'Chanson paillarde', 'Chanson populaire', 'Chartreuse', 'Cheerleader', 'Chill', 'Choré',
|
|
||||||
'Cinéma', 'Cocktail', 'Comédie musicle', 'Commercial', 'Copaing', 'Danse', 'Dancefloor',
|
|
||||||
'Electro', 'Fanfare', 'Gin tonic', 'Inclusif', 'Jazz', "Jeux d'alcool", 'Jeux de carte',
|
|
||||||
'Jeux de rôle', 'Jeux de société', 'JUL', 'Jus de fruit', 'Kfet', 'Kleptomanie assurée',
|
|
||||||
'LGBTQ+', 'Livre', 'Morning beer', 'Musique', 'NAPS', 'Paillettes', 'Pastis', 'Paté Hénaff',
|
|
||||||
'Peluche', 'Pena baiona', "Peu d'alcool", 'Pilier de bar', 'PMU', 'Poulpe', 'Punch', 'Rap',
|
|
||||||
'Réveil', 'Rock', 'Rugby', 'Sandwich', 'Serge', 'Shot', 'Sociable', 'Spectacle', 'Techno',
|
|
||||||
'Techno house', 'Thérapie Taxi', 'Tradition kchanaises', 'Troisième mi-temps', 'Turn up',
|
|
||||||
'Vodka', 'Vodka pomme', 'Volley', 'Vomi stratégique'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class WEISurveyForm2023(forms.Form):
|
|
||||||
"""
|
|
||||||
Survey form for the year 2023.
|
|
||||||
Members choose 20 words, from which we calculate the best associated bus.
|
|
||||||
"""
|
|
||||||
|
|
||||||
word = forms.ChoiceField(
|
|
||||||
label=_("Choose a word:"),
|
|
||||||
widget=forms.RadioSelect(),
|
|
||||||
)
|
|
||||||
|
|
||||||
def set_registration(self, registration):
|
|
||||||
"""
|
|
||||||
Filter the bus selector with the buses of the current WEI.
|
|
||||||
"""
|
|
||||||
information = WEISurveyInformation2023(registration)
|
|
||||||
if not information.seed:
|
|
||||||
information.seed = int(1000 * time.time())
|
|
||||||
information.save(registration)
|
|
||||||
registration._force_save = True
|
|
||||||
registration.save()
|
|
||||||
|
|
||||||
if self.data:
|
|
||||||
self.fields["word"].choices = [(w, w) for w in WORDS]
|
|
||||||
if self.is_valid():
|
|
||||||
return
|
|
||||||
|
|
||||||
rng = Random((information.step + 1) * information.seed)
|
|
||||||
|
|
||||||
words = None
|
|
||||||
|
|
||||||
buses = WEISurveyAlgorithm2023.get_buses()
|
|
||||||
informations = {bus: WEIBusInformation2023(bus) for bus in buses}
|
|
||||||
scores = sum((list(informations[bus].scores.values()) for bus in buses), [])
|
|
||||||
average_score = sum(scores) / len(scores)
|
|
||||||
|
|
||||||
preferred_words = {bus: [word for word in WORDS
|
|
||||||
if informations[bus].scores[word] >= average_score]
|
|
||||||
for bus in buses}
|
|
||||||
while words is None or len(set(words)) != len(words):
|
|
||||||
# Ensure that there is no the same word 2 times
|
|
||||||
words = [rng.choice(words) for _ignored2, words in preferred_words.items()]
|
|
||||||
rng.shuffle(words)
|
|
||||||
words = [(w, w) for w in words]
|
|
||||||
self.fields["word"].choices = words
|
|
||||||
|
|
||||||
|
|
||||||
class WEIBusInformation2023(WEIBusInformation):
|
|
||||||
"""
|
|
||||||
For each word, the bus has a score
|
|
||||||
"""
|
|
||||||
scores: dict
|
|
||||||
|
|
||||||
def __init__(self, bus):
|
|
||||||
self.scores = {}
|
|
||||||
for word in WORDS:
|
|
||||||
self.scores[word] = 0.0
|
|
||||||
super().__init__(bus)
|
|
||||||
|
|
||||||
|
|
||||||
class WEISurveyInformation2023(WEISurveyInformation):
|
|
||||||
"""
|
|
||||||
We store the id of the selected bus. We store only the name, but is not used in the selection:
|
|
||||||
that's only for humans that try to read data.
|
|
||||||
"""
|
|
||||||
# Random seed that is stored at the first time to ensure that words are generated only once
|
|
||||||
seed = 0
|
|
||||||
step = 0
|
|
||||||
|
|
||||||
def __init__(self, registration):
|
|
||||||
for i in range(1, 21):
|
|
||||||
setattr(self, "word" + str(i), None)
|
|
||||||
super().__init__(registration)
|
|
||||||
|
|
||||||
|
|
||||||
class WEISurvey2023(WEISurvey):
|
|
||||||
"""
|
|
||||||
Survey for the year 2023.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_year(cls):
|
|
||||||
return 2023
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_survey_information_class(cls):
|
|
||||||
return WEISurveyInformation2023
|
|
||||||
|
|
||||||
def get_form_class(self):
|
|
||||||
return WEISurveyForm2023
|
|
||||||
|
|
||||||
def update_form(self, form):
|
|
||||||
"""
|
|
||||||
Filter the bus selector with the buses of the WEI.
|
|
||||||
"""
|
|
||||||
form.set_registration(self.registration)
|
|
||||||
|
|
||||||
@transaction.atomic
|
|
||||||
def form_valid(self, form):
|
|
||||||
word = form.cleaned_data["word"]
|
|
||||||
self.information.step += 1
|
|
||||||
setattr(self.information, "word" + str(self.information.step), word)
|
|
||||||
self.save()
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_algorithm_class(cls):
|
|
||||||
return WEISurveyAlgorithm2023
|
|
||||||
|
|
||||||
def is_complete(self) -> bool:
|
|
||||||
"""
|
|
||||||
The survey is complete once the bus is chosen.
|
|
||||||
"""
|
|
||||||
return self.information.step == 20
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
@lru_cache()
|
|
||||||
def word_mean(cls, word):
|
|
||||||
"""
|
|
||||||
Calculate the mid-score given by all buses.
|
|
||||||
"""
|
|
||||||
buses = cls.get_algorithm_class().get_buses()
|
|
||||||
return sum([cls.get_algorithm_class().get_bus_information(bus).scores[word] for bus in buses]) / buses.count()
|
|
||||||
|
|
||||||
@lru_cache()
|
|
||||||
def score(self, bus):
|
|
||||||
if not self.is_complete():
|
|
||||||
raise ValueError("Survey is not ended, can't calculate score")
|
|
||||||
|
|
||||||
bus_info = self.get_algorithm_class().get_bus_information(bus)
|
|
||||||
# Score is the given score by the bus subtracted to the mid-score of the buses.
|
|
||||||
s = sum(bus_info.scores[getattr(self.information, 'word' + str(i))]
|
|
||||||
- self.word_mean(getattr(self.information, 'word' + str(i))) for i in range(1, 21)) / 20
|
|
||||||
return s
|
|
||||||
|
|
||||||
@lru_cache()
|
|
||||||
def scores_per_bus(self):
|
|
||||||
return {bus: self.score(bus) for bus in self.get_algorithm_class().get_buses()}
|
|
||||||
|
|
||||||
@lru_cache()
|
|
||||||
def ordered_buses(self):
|
|
||||||
values = list(self.scores_per_bus().items())
|
|
||||||
values.sort(key=lambda item: -item[1])
|
|
||||||
return values
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def clear_cache(cls):
|
|
||||||
cls.word_mean.cache_clear()
|
|
||||||
return super().clear_cache()
|
|
||||||
|
|
||||||
|
|
||||||
class WEISurveyAlgorithm2023(WEISurveyAlgorithm):
|
|
||||||
"""
|
|
||||||
The algorithm class for the year 2023.
|
|
||||||
We use Gale-Shapley algorithm to attribute 1y students into buses.
|
|
||||||
"""
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_survey_class(cls):
|
|
||||||
return WEISurvey2023
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_bus_information_class(cls):
|
|
||||||
return WEIBusInformation2023
|
|
||||||
|
|
||||||
def run_algorithm(self, display_tqdm=False):
|
|
||||||
"""
|
|
||||||
Gale-Shapley algorithm implementation.
|
|
||||||
We modify it to allow buses to have multiple "weddings".
|
|
||||||
"""
|
|
||||||
surveys = list(self.get_survey_class()(r) for r in self.get_registrations()) # All surveys
|
|
||||||
surveys = [s for s in surveys if s.is_complete()] # Don't consider invalid surveys
|
|
||||||
# Don't manage hardcoded people
|
|
||||||
surveys = [s for s in surveys if not hasattr(s.information, 'hardcoded') or not s.information.hardcoded]
|
|
||||||
|
|
||||||
# Reset previous algorithm run
|
|
||||||
for survey in surveys:
|
|
||||||
survey.free()
|
|
||||||
survey.save()
|
|
||||||
|
|
||||||
non_men = [s for s in surveys if s.registration.gender != 'male']
|
|
||||||
men = [s for s in surveys if s.registration.gender == 'male']
|
|
||||||
|
|
||||||
quotas = {}
|
|
||||||
registrations = self.get_registrations()
|
|
||||||
non_men_total = registrations.filter(~Q(gender='male')).count()
|
|
||||||
for bus in self.get_buses():
|
|
||||||
free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count()
|
|
||||||
# Remove hardcoded people
|
|
||||||
free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True,
|
|
||||||
registration__information_json__icontains="hardcoded").count()
|
|
||||||
quotas[bus] = 4 + int(non_men_total / registrations.count() * free_seats)
|
|
||||||
|
|
||||||
tqdm_obj = None
|
|
||||||
if display_tqdm:
|
|
||||||
from tqdm import tqdm
|
|
||||||
tqdm_obj = tqdm(total=len(non_men), desc="Non-hommes")
|
|
||||||
|
|
||||||
# Repartition for non men people first
|
|
||||||
self.make_repartition(non_men, quotas, tqdm_obj=tqdm_obj)
|
|
||||||
|
|
||||||
quotas = {}
|
|
||||||
for bus in self.get_buses():
|
|
||||||
free_seats = bus.size - WEIMembership.objects.filter(bus=bus, registration__first_year=False).count()
|
|
||||||
free_seats -= sum(1 for s in non_men if s.information.selected_bus_pk == bus.pk)
|
|
||||||
# Remove hardcoded people
|
|
||||||
free_seats -= WEIMembership.objects.filter(bus=bus, registration__first_year=True,
|
|
||||||
registration__information_json__icontains="hardcoded").count()
|
|
||||||
quotas[bus] = free_seats
|
|
||||||
|
|
||||||
if display_tqdm:
|
|
||||||
tqdm_obj.close()
|
|
||||||
|
|
||||||
from tqdm import tqdm
|
|
||||||
tqdm_obj = tqdm(total=len(men), desc="Hommes")
|
|
||||||
|
|
||||||
self.make_repartition(men, quotas, tqdm_obj=tqdm_obj)
|
|
||||||
|
|
||||||
if display_tqdm:
|
|
||||||
tqdm_obj.close()
|
|
||||||
|
|
||||||
# Clear cache information after running algorithm
|
|
||||||
WEISurvey2023.clear_cache()
|
|
||||||
|
|
||||||
def make_repartition(self, surveys, quotas=None, tqdm_obj=None):
|
|
||||||
free_surveys = surveys.copy() # Remaining surveys
|
|
||||||
while free_surveys: # Some students are not affected
|
|
||||||
survey = free_surveys[0]
|
|
||||||
buses = survey.ordered_buses() # Preferences of the student
|
|
||||||
for bus, current_score in buses:
|
|
||||||
if self.get_bus_information(bus).has_free_seats(surveys, quotas):
|
|
||||||
# Selected bus has free places. Put student in the bus
|
|
||||||
survey.select_bus(bus)
|
|
||||||
survey.save()
|
|
||||||
free_surveys.remove(survey)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# Current bus has not enough places. Remove the least preferred student from the bus if existing
|
|
||||||
least_preferred_survey = None
|
|
||||||
least_score = -1
|
|
||||||
# Find the least student in the bus that has a lower score than the current student
|
|
||||||
for survey2 in surveys:
|
|
||||||
if not survey2.information.valid or survey2.information.get_selected_bus() != bus:
|
|
||||||
continue
|
|
||||||
score2 = survey2.score(bus)
|
|
||||||
if current_score <= score2: # Ignore better students
|
|
||||||
continue
|
|
||||||
if least_preferred_survey is None or score2 < least_score:
|
|
||||||
least_preferred_survey = survey2
|
|
||||||
least_score = score2
|
|
||||||
|
|
||||||
if least_preferred_survey is not None:
|
|
||||||
# Remove the least student from the bus and put the current student in.
|
|
||||||
# If it does not exist, choose the next bus.
|
|
||||||
least_preferred_survey.free()
|
|
||||||
least_preferred_survey.save()
|
|
||||||
free_surveys.append(least_preferred_survey)
|
|
||||||
survey.select_bus(bus)
|
|
||||||
survey.save()
|
|
||||||
free_surveys.remove(survey)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
raise ValueError(f"User {survey.registration.user} has no free seat")
|
|
||||||
|
|
||||||
if tqdm_obj is not None:
|
|
||||||
tqdm_obj.n = len(surveys) - len(free_surveys)
|
|
||||||
tqdm_obj.refresh()
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 2.2.26 on 2022-09-04 21:25
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('wei', '0003_bus_size'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='weiclub',
|
|
||||||
name='year',
|
|
||||||
field=models.PositiveIntegerField(default=2022, unique=True, verbose_name='year'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2023-01-28 17:50
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('wei', '0004_auto_20220904_2325'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='weiclub',
|
|
||||||
name='year',
|
|
||||||
field=models.PositiveIntegerField(default=2023, unique=True, verbose_name='year'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2023-07-09 09:51
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('wei', '0005_auto_20230128_1850'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='weiregistration',
|
|
||||||
name='clothing_cut',
|
|
||||||
field=models.CharField(choices=[('male', 'Male'), ('female', 'Female'), ('unisex', 'Unisex')], default='unisex', max_length=16, verbose_name='clothing cut'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
# Generated by Django 2.2.28 on 2023-07-09 12:46
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('wei', '0006_unisex_clothing_cut'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='weiregistration',
|
|
||||||
name='emergency_contact_name',
|
|
||||||
field=models.CharField(help_text='The emergency contact must not be a WEI participant', max_length=255, verbose_name='emergency contact name'),
|
|
||||||
),
|
|
||||||
]
|
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@ -209,9 +209,7 @@ class WEIRegistration(models.Model):
|
|||||||
choices=(
|
choices=(
|
||||||
('male', _("Male")),
|
('male', _("Male")),
|
||||||
('female', _("Female")),
|
('female', _("Female")),
|
||||||
('unisex', _("Unisex")),
|
|
||||||
),
|
),
|
||||||
default='unisex',
|
|
||||||
verbose_name=_("clothing cut"),
|
verbose_name=_("clothing cut"),
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -237,7 +235,6 @@ class WEIRegistration(models.Model):
|
|||||||
emergency_contact_name = models.CharField(
|
emergency_contact_name = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
verbose_name=_("emergency contact name"),
|
verbose_name=_("emergency contact name"),
|
||||||
help_text=_("The emergency contact must not be a WEI participant")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
emergency_contact_phone = PhoneNumberField(
|
emergency_contact_phone = PhoneNumberField(
|
||||||
|
@ -56,7 +56,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
<dd class="col-xl-6">{{ registration.get_gender_display }}</dd>
|
<dd class="col-xl-6">{{ registration.get_gender_display }}</dd>
|
||||||
|
|
||||||
<dt class="col-xl-6">{% trans 'clothing cut'|capfirst %}</dt>
|
<dt class="col-xl-6">{% trans 'clothing cut'|capfirst %}</dt>
|
||||||
<dd class="col-xl-6">{{ registration.get_clothing_cut_display }}</dd>
|
<dd class="col-xl-6">{{ registration.clothing_cut }}</dd>
|
||||||
|
|
||||||
<dt class="col-xl-6">{% trans 'clothing size'|capfirst %}</dt>
|
<dt class="col-xl-6">{% trans 'clothing size'|capfirst %}</dt>
|
||||||
<dd class="col-xl-6">{{ registration.clothing_size }}</dd>
|
<dd class="col-xl-6">{{ registration.clothing_size }}</dd>
|
||||||
|
@ -1,110 +0,0 @@
|
|||||||
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
import random
|
|
||||||
|
|
||||||
from django.contrib.auth.models import User
|
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
from ..forms.surveys.wei2023 import WEIBusInformation2023, WEISurvey2023, WORDS, WEISurveyInformation2023
|
|
||||||
from ..models import Bus, WEIClub, WEIRegistration
|
|
||||||
|
|
||||||
|
|
||||||
class TestWEIAlgorithm(TestCase):
|
|
||||||
"""
|
|
||||||
Run some tests to ensure that the WEI algorithm is working well.
|
|
||||||
"""
|
|
||||||
fixtures = ('initial',)
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
"""
|
|
||||||
Create some test data, with one WEI and 10 buses with random score attributions.
|
|
||||||
"""
|
|
||||||
self.wei = WEIClub.objects.create(
|
|
||||||
name="WEI 2023",
|
|
||||||
email="wei2023@example.com",
|
|
||||||
date_start='2023-09-16',
|
|
||||||
date_end='2023-09-18',
|
|
||||||
year=2023,
|
|
||||||
)
|
|
||||||
|
|
||||||
self.buses = []
|
|
||||||
for i in range(10):
|
|
||||||
bus = Bus.objects.create(wei=self.wei, name=f"Bus {i}", size=10)
|
|
||||||
self.buses.append(bus)
|
|
||||||
information = WEIBusInformation2023(bus)
|
|
||||||
for word in WORDS:
|
|
||||||
information.scores[word] = random.randint(0, 101)
|
|
||||||
information.save()
|
|
||||||
bus.save()
|
|
||||||
|
|
||||||
def test_survey_algorithm_small(self):
|
|
||||||
"""
|
|
||||||
There are only a few people in each bus, ensure that each person has its best bus
|
|
||||||
"""
|
|
||||||
# Add a few users
|
|
||||||
for i in range(10):
|
|
||||||
user = User.objects.create(username=f"user{i}")
|
|
||||||
registration = WEIRegistration.objects.create(
|
|
||||||
user=user,
|
|
||||||
wei=self.wei,
|
|
||||||
first_year=True,
|
|
||||||
birth_date='2000-01-01',
|
|
||||||
)
|
|
||||||
information = WEISurveyInformation2023(registration)
|
|
||||||
for j in range(1, 21):
|
|
||||||
setattr(information, f'word{j}', random.choice(WORDS))
|
|
||||||
information.step = 20
|
|
||||||
information.save(registration)
|
|
||||||
registration.save()
|
|
||||||
|
|
||||||
# Run algorithm
|
|
||||||
WEISurvey2023.get_algorithm_class()().run_algorithm()
|
|
||||||
|
|
||||||
# Ensure that everyone has its first choice
|
|
||||||
for r in WEIRegistration.objects.filter(wei=self.wei).all():
|
|
||||||
survey = WEISurvey2023(r)
|
|
||||||
preferred_bus = survey.ordered_buses()[0][0]
|
|
||||||
chosen_bus = survey.information.get_selected_bus()
|
|
||||||
self.assertEqual(preferred_bus, chosen_bus)
|
|
||||||
|
|
||||||
def test_survey_algorithm_full(self):
|
|
||||||
"""
|
|
||||||
Buses are full of first year people, ensure that they are happy
|
|
||||||
"""
|
|
||||||
# Add a lot of users
|
|
||||||
for i in range(95):
|
|
||||||
user = User.objects.create(username=f"user{i}")
|
|
||||||
registration = WEIRegistration.objects.create(
|
|
||||||
user=user,
|
|
||||||
wei=self.wei,
|
|
||||||
first_year=True,
|
|
||||||
birth_date='2000-01-01',
|
|
||||||
)
|
|
||||||
information = WEISurveyInformation2023(registration)
|
|
||||||
for j in range(1, 21):
|
|
||||||
setattr(information, f'word{j}', random.choice(WORDS))
|
|
||||||
information.step = 20
|
|
||||||
information.save(registration)
|
|
||||||
registration.save()
|
|
||||||
|
|
||||||
# Run algorithm
|
|
||||||
WEISurvey2023.get_algorithm_class()().run_algorithm()
|
|
||||||
|
|
||||||
penalty = 0
|
|
||||||
# Ensure that everyone seems to be happy
|
|
||||||
# We attribute a penalty for each user that didn't have its first choice
|
|
||||||
# The penalty is the square of the distance between the score of the preferred bus
|
|
||||||
# and the score of the attributed bus
|
|
||||||
# We consider it acceptable if the mean of this distance is lower than 5 %
|
|
||||||
for r in WEIRegistration.objects.filter(wei=self.wei).all():
|
|
||||||
survey = WEISurvey2023(r)
|
|
||||||
chosen_bus = survey.information.get_selected_bus()
|
|
||||||
buses = survey.ordered_buses()
|
|
||||||
score = min(v for bus, v in buses if bus == chosen_bus)
|
|
||||||
max_score = buses[0][1]
|
|
||||||
penalty += (max_score - score) ** 2
|
|
||||||
|
|
||||||
self.assertLessEqual(max_score - score, 25) # Always less than 25 % of tolerance
|
|
||||||
|
|
||||||
self.assertLessEqual(penalty / 100, 25) # Tolerance of 5 %
|
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright (C) 2018-2023 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2021 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -782,7 +782,7 @@ class TestDefaultWEISurvey(TestCase):
|
|||||||
WEISurvey.update_form(None, None)
|
WEISurvey.update_form(None, None)
|
||||||
|
|
||||||
self.assertEqual(CurrentSurvey.get_algorithm_class().get_survey_class(), CurrentSurvey)
|
self.assertEqual(CurrentSurvey.get_algorithm_class().get_survey_class(), CurrentSurvey)
|
||||||
self.assertEqual(CurrentSurvey.get_year(), 2023)
|
self.assertEqual(CurrentSurvey.get_year(), 2022)
|
||||||
|
|
||||||
|
|
||||||
class TestWeiAPI(TestAPI):
|
class TestWeiAPI(TestAPI):
|
||||||
|
@ -448,10 +448,6 @@ Options
|
|||||||
"value": "female",
|
"value": "female",
|
||||||
"display_name": "Femme"
|
"display_name": "Femme"
|
||||||
}
|
}
|
||||||
{
|
|
||||||
"value": "unisex",
|
|
||||||
"display_name": "Unisexe"
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"clothing_size": {
|
"clothing_size": {
|
||||||
|
@ -7,9 +7,9 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-07-09 14:46+0200\n"
|
"POT-Creation-Date: 2022-04-10 22:34+0200\n"
|
||||||
"PO-Revision-Date: 2020-11-16 20:02+0000\n"
|
"PO-Revision-Date: 2020-11-16 20:02+0000\n"
|
||||||
"Last-Translator: bleizi <bleizi@crans.org>\n"
|
"Last-Translator: Yohann D'ANELLO <ynerant@crans.org>\n"
|
||||||
"Language-Team: German <http://translate.ynerant.fr/projects/nk20/nk20/de/>\n"
|
"Language-Team: German <http://translate.ynerant.fr/projects/nk20/nk20/de/>\n"
|
||||||
"Language: de\n"
|
"Language: de\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -115,7 +115,7 @@ msgid "type"
|
|||||||
msgstr "Type"
|
msgstr "Type"
|
||||||
|
|
||||||
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
|
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
|
||||||
#: apps/note/models/notes.py:148 apps/treasury/models.py:287
|
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
|
||||||
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
||||||
#: apps/wei/templates/wei/survey.html:15
|
#: apps/wei/templates/wei/survey.html:15
|
||||||
msgid "user"
|
msgid "user"
|
||||||
@ -258,19 +258,19 @@ msgstr "Eingetreten um "
|
|||||||
msgid "remove"
|
msgid "remove"
|
||||||
msgstr "entfernen"
|
msgstr "entfernen"
|
||||||
|
|
||||||
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:201
|
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:199
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr "Type"
|
msgstr "Type"
|
||||||
|
|
||||||
#: apps/activity/tables.py:84 apps/member/forms.py:186
|
#: apps/activity/tables.py:84 apps/member/forms.py:186
|
||||||
#: apps/registration/forms.py:92 apps/treasury/forms.py:131
|
#: apps/registration/forms.py:91 apps/treasury/forms.py:131
|
||||||
#: apps/wei/forms/registration.py:104
|
#: apps/wei/forms/registration.py:104
|
||||||
msgid "Last name"
|
msgid "Last name"
|
||||||
msgstr "Nachname"
|
msgstr "Nachname"
|
||||||
|
|
||||||
#: apps/activity/tables.py:86 apps/member/forms.py:191
|
#: apps/activity/tables.py:86 apps/member/forms.py:191
|
||||||
#: apps/note/templates/note/transaction_form.html:138
|
#: apps/note/templates/note/transaction_form.html:138
|
||||||
#: apps/registration/forms.py:97 apps/treasury/forms.py:133
|
#: apps/registration/forms.py:96 apps/treasury/forms.py:133
|
||||||
#: apps/wei/forms/registration.py:109
|
#: apps/wei/forms/registration.py:109
|
||||||
msgid "First name"
|
msgid "First name"
|
||||||
msgstr "Vorname"
|
msgstr "Vorname"
|
||||||
@ -549,24 +549,24 @@ msgid "This image cannot be loaded."
|
|||||||
msgstr "Dieses Bild kann nicht geladen werden."
|
msgstr "Dieses Bild kann nicht geladen werden."
|
||||||
|
|
||||||
#: apps/member/forms.py:141 apps/member/views.py:103
|
#: apps/member/forms.py:141 apps/member/views.py:103
|
||||||
#: apps/registration/forms.py:34 apps/registration/views.py:266
|
#: apps/registration/forms.py:33 apps/registration/views.py:262
|
||||||
msgid "An alias with a similar name already exists."
|
msgid "An alias with a similar name already exists."
|
||||||
msgstr "Ein ähnliches Alias ist schon benutzt."
|
msgstr "Ein ähnliches Alias ist schon benutzt."
|
||||||
|
|
||||||
#: apps/member/forms.py:165
|
#: apps/member/forms.py:165 apps/registration/forms.py:71
|
||||||
msgid "Inscription paid by Société Générale"
|
msgid "Inscription paid by Société Générale"
|
||||||
msgstr "Mitgliedschaft von der Société Générale bezahlt"
|
msgstr "Mitgliedschaft von der Société Générale bezahlt"
|
||||||
|
|
||||||
#: apps/member/forms.py:167
|
#: apps/member/forms.py:167 apps/registration/forms.py:73
|
||||||
msgid "Check this case if the Société Générale paid the inscription."
|
msgid "Check this case if the Société Générale paid the inscription."
|
||||||
msgstr "Die Société Générale die Mitgliedschaft bezahlt."
|
msgstr "Die Société Générale die Mitgliedschaft bezahlt."
|
||||||
|
|
||||||
#: apps/member/forms.py:172 apps/registration/forms.py:79
|
#: apps/member/forms.py:172 apps/registration/forms.py:78
|
||||||
#: apps/wei/forms/registration.py:91
|
#: apps/wei/forms/registration.py:91
|
||||||
msgid "Credit type"
|
msgid "Credit type"
|
||||||
msgstr "Kredittype"
|
msgstr "Kredittype"
|
||||||
|
|
||||||
#: apps/member/forms.py:173 apps/registration/forms.py:80
|
#: apps/member/forms.py:173 apps/registration/forms.py:79
|
||||||
#: apps/wei/forms/registration.py:92
|
#: apps/wei/forms/registration.py:92
|
||||||
msgid "No credit"
|
msgid "No credit"
|
||||||
msgstr "Kein Kredit"
|
msgstr "Kein Kredit"
|
||||||
@ -575,13 +575,13 @@ msgstr "Kein Kredit"
|
|||||||
msgid "You can credit the note of the user."
|
msgid "You can credit the note of the user."
|
||||||
msgstr "Sie dûrfen diese Note kreditieren."
|
msgstr "Sie dûrfen diese Note kreditieren."
|
||||||
|
|
||||||
#: apps/member/forms.py:179 apps/registration/forms.py:85
|
#: apps/member/forms.py:179 apps/registration/forms.py:84
|
||||||
#: apps/wei/forms/registration.py:97
|
#: apps/wei/forms/registration.py:97
|
||||||
msgid "Credit amount"
|
msgid "Credit amount"
|
||||||
msgstr "Kreditanzahl"
|
msgstr "Kreditanzahl"
|
||||||
|
|
||||||
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:144
|
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:144
|
||||||
#: apps/registration/forms.py:102 apps/treasury/forms.py:135
|
#: apps/registration/forms.py:101 apps/treasury/forms.py:135
|
||||||
#: apps/wei/forms/registration.py:114
|
#: apps/wei/forms/registration.py:114
|
||||||
msgid "Bank"
|
msgid "Bank"
|
||||||
msgstr "Bank"
|
msgstr "Bank"
|
||||||
@ -1193,7 +1193,7 @@ msgstr "Speichern"
|
|||||||
msgid "Registrations"
|
msgid "Registrations"
|
||||||
msgstr "Anmeldung"
|
msgstr "Anmeldung"
|
||||||
|
|
||||||
#: apps/member/views.py:73 apps/registration/forms.py:24
|
#: apps/member/views.py:73 apps/registration/forms.py:23
|
||||||
msgid "This address must be valid."
|
msgid "This address must be valid."
|
||||||
msgstr "Diese Adresse muss gültig sein."
|
msgstr "Diese Adresse muss gültig sein."
|
||||||
|
|
||||||
@ -1249,11 +1249,11 @@ msgstr "Die Mitgliedschaft muss nach {:%m-%d-Y} anfängen."
|
|||||||
msgid "The membership must begin before {:%m-%d-%Y}."
|
msgid "The membership must begin before {:%m-%d-%Y}."
|
||||||
msgstr "Die Mitgliedschaft muss vor {:%m-%d-Y} anfängen."
|
msgstr "Die Mitgliedschaft muss vor {:%m-%d-Y} anfängen."
|
||||||
|
|
||||||
#: apps/member/views.py:880
|
#: apps/member/views.py:876
|
||||||
msgid "Manage roles of an user in the club"
|
msgid "Manage roles of an user in the club"
|
||||||
msgstr "Rollen in diesen Club bearbeiten"
|
msgstr "Rollen in diesen Club bearbeiten"
|
||||||
|
|
||||||
#: apps/member/views.py:905
|
#: apps/member/views.py:901
|
||||||
msgid "Members of the club"
|
msgid "Members of the club"
|
||||||
msgstr "Mitlglieder dieses Club"
|
msgstr "Mitlglieder dieses Club"
|
||||||
|
|
||||||
@ -1570,7 +1570,7 @@ msgstr "Sondertranskationen"
|
|||||||
msgid "membership transaction"
|
msgid "membership transaction"
|
||||||
msgstr "Mitgliedschafttransaktion"
|
msgstr "Mitgliedschafttransaktion"
|
||||||
|
|
||||||
#: apps/note/models/transactions.py:385 apps/treasury/models.py:294
|
#: apps/note/models/transactions.py:385 apps/treasury/models.py:292
|
||||||
msgid "membership transactions"
|
msgid "membership transactions"
|
||||||
msgstr "Mitgliedschaftttransaktionen"
|
msgstr "Mitgliedschaftttransaktionen"
|
||||||
|
|
||||||
@ -1689,7 +1689,7 @@ msgid "Amount"
|
|||||||
msgstr "Anzahl"
|
msgstr "Anzahl"
|
||||||
|
|
||||||
#: apps/note/templates/note/transaction_form.html:132
|
#: apps/note/templates/note/transaction_form.html:132
|
||||||
#: apps/treasury/models.py:56
|
#: apps/treasury/models.py:54
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
@ -1982,15 +1982,29 @@ msgstr "Alle Rechten"
|
|||||||
msgid "registration"
|
msgid "registration"
|
||||||
msgstr "Anmeldung"
|
msgstr "Anmeldung"
|
||||||
|
|
||||||
#: apps/registration/forms.py:40
|
#: apps/registration/forms.py:39
|
||||||
msgid "This email address is already used."
|
msgid "This email address is already used."
|
||||||
msgstr "Diese email adresse ist schon benutzt."
|
msgstr "Diese email adresse ist schon benutzt."
|
||||||
|
|
||||||
#: apps/registration/forms.py:60
|
#: apps/registration/forms.py:49
|
||||||
|
#, fuzzy
|
||||||
|
#| msgid "You already opened an account in the Société générale."
|
||||||
|
msgid ""
|
||||||
|
"I declare that I opened or I will open soon a bank account in the Société "
|
||||||
|
"générale with the BDE partnership."
|
||||||
|
msgstr "Sie haben bereits ein Konto in der Société générale eröffnet."
|
||||||
|
|
||||||
|
#: apps/registration/forms.py:51
|
||||||
|
msgid ""
|
||||||
|
"Warning: this engages you to open your bank account. If you finally decides "
|
||||||
|
"to don't open your account, you will have to pay the BDE membership."
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: apps/registration/forms.py:59
|
||||||
msgid "Register to the WEI"
|
msgid "Register to the WEI"
|
||||||
msgstr "Zu WEI anmelden"
|
msgstr "Zu WEI anmelden"
|
||||||
|
|
||||||
#: apps/registration/forms.py:62
|
#: apps/registration/forms.py:61
|
||||||
msgid ""
|
msgid ""
|
||||||
"Check this case if you want to register to the WEI. If you hesitate, you "
|
"Check this case if you want to register to the WEI. If you hesitate, you "
|
||||||
"will be able to register later, after validating your account in the Kfet."
|
"will be able to register later, after validating your account in the Kfet."
|
||||||
@ -1999,18 +2013,14 @@ msgstr ""
|
|||||||
"falls Zweifel, können Sie sich später nach Bestätigung Ihres Kontos im Kfet "
|
"falls Zweifel, können Sie sich später nach Bestätigung Ihres Kontos im Kfet "
|
||||||
"registrieren."
|
"registrieren."
|
||||||
|
|
||||||
#: apps/registration/forms.py:107
|
#: apps/registration/forms.py:106
|
||||||
msgid "Join BDE Club"
|
msgid "Join BDE Club"
|
||||||
msgstr "BDE Mitglieder werden"
|
msgstr "BDE Mitglieder werden"
|
||||||
|
|
||||||
#: apps/registration/forms.py:114
|
#: apps/registration/forms.py:113
|
||||||
msgid "Join Kfet Club"
|
msgid "Join Kfet Club"
|
||||||
msgstr "Kfet Mitglieder werden"
|
msgstr "Kfet Mitglieder werden"
|
||||||
|
|
||||||
#: apps/registration/forms.py:123
|
|
||||||
msgid "Join BDA Club"
|
|
||||||
msgstr "BDA Mitglieder werden"
|
|
||||||
|
|
||||||
#: apps/registration/templates/registration/email_validation_complete.html:15
|
#: apps/registration/templates/registration/email_validation_complete.html:15
|
||||||
msgid "Your email have successfully been validated."
|
msgid "Your email have successfully been validated."
|
||||||
msgstr "Ihre E-Mail wurde erfolgreich validiert."
|
msgstr "Ihre E-Mail wurde erfolgreich validiert."
|
||||||
@ -2059,14 +2069,14 @@ msgstr "Registrierung löschen"
|
|||||||
msgid "Validate account"
|
msgid "Validate account"
|
||||||
msgstr "Konto validieren"
|
msgstr "Konto validieren"
|
||||||
|
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:63
|
#: apps/registration/templates/registration/future_profile_detail.html:62
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
#| msgid "You already opened an account in the Société générale."
|
#| msgid "You already opened an account in the Société générale."
|
||||||
msgid ""
|
msgid ""
|
||||||
"The user declared that he/she opened a bank account in the Société générale."
|
"The user declared that he/she opened a bank account in the Société générale."
|
||||||
msgstr "Sie haben bereits ein Konto in der Société générale eröffnet."
|
msgstr "Sie haben bereits ein Konto in der Société générale eröffnet."
|
||||||
|
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:73
|
#: apps/registration/templates/registration/future_profile_detail.html:71
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:127
|
#: apps/wei/templates/wei/weimembership_form.html:127
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:186
|
#: apps/wei/templates/wei/weimembership_form.html:186
|
||||||
msgid "Validate registration"
|
msgid "Validate registration"
|
||||||
@ -2118,54 +2128,54 @@ msgstr "Danke"
|
|||||||
msgid "The Note Kfet team."
|
msgid "The Note Kfet team."
|
||||||
msgstr "Die NoteKfet Team."
|
msgstr "Die NoteKfet Team."
|
||||||
|
|
||||||
#: apps/registration/views.py:41
|
#: apps/registration/views.py:40
|
||||||
msgid "Register new user"
|
msgid "Register new user"
|
||||||
msgstr "Neuen User registrieren"
|
msgstr "Neuen User registrieren"
|
||||||
|
|
||||||
#: apps/registration/views.py:99
|
#: apps/registration/views.py:98
|
||||||
msgid "Email validation"
|
msgid "Email validation"
|
||||||
msgstr "Email validierung"
|
msgstr "Email validierung"
|
||||||
|
|
||||||
#: apps/registration/views.py:101
|
#: apps/registration/views.py:100
|
||||||
msgid "Validate email"
|
msgid "Validate email"
|
||||||
msgstr "Email validieren"
|
msgstr "Email validieren"
|
||||||
|
|
||||||
#: apps/registration/views.py:145
|
#: apps/registration/views.py:144
|
||||||
msgid "Email validation unsuccessful"
|
msgid "Email validation unsuccessful"
|
||||||
msgstr "Email validierung unerfolgreich"
|
msgstr "Email validierung unerfolgreich"
|
||||||
|
|
||||||
#: apps/registration/views.py:156
|
#: apps/registration/views.py:155
|
||||||
msgid "Email validation email sent"
|
msgid "Email validation email sent"
|
||||||
msgstr "Validierungsemail wurde gesendet"
|
msgstr "Validierungsemail wurde gesendet"
|
||||||
|
|
||||||
#: apps/registration/views.py:164
|
#: apps/registration/views.py:163
|
||||||
msgid "Resend email validation link"
|
msgid "Resend email validation link"
|
||||||
msgstr "E-Mail-Validierungslink erneut senden"
|
msgstr "E-Mail-Validierungslink erneut senden"
|
||||||
|
|
||||||
#: apps/registration/views.py:182
|
#: apps/registration/views.py:181
|
||||||
msgid "Pre-registered users list"
|
msgid "Pre-registered users list"
|
||||||
msgstr "Vorregistrierte Userliste"
|
msgstr "Vorregistrierte Userliste"
|
||||||
|
|
||||||
#: apps/registration/views.py:206
|
#: apps/registration/views.py:205
|
||||||
msgid "Unregistered users"
|
msgid "Unregistered users"
|
||||||
msgstr "Unregistrierte Users"
|
msgstr "Unregistrierte Users"
|
||||||
|
|
||||||
#: apps/registration/views.py:219
|
#: apps/registration/views.py:218
|
||||||
msgid "Registration detail"
|
msgid "Registration detail"
|
||||||
msgstr "Registrierung Detailen"
|
msgstr "Registrierung Detailen"
|
||||||
|
|
||||||
#: apps/registration/views.py:293
|
#: apps/registration/views.py:282
|
||||||
msgid "You must join the BDE."
|
msgid "You must join the BDE."
|
||||||
msgstr "Sie müssen die BDE beitreten."
|
msgstr "Sie müssen die BDE beitreten."
|
||||||
|
|
||||||
#: apps/registration/views.py:323
|
#: apps/registration/views.py:306
|
||||||
msgid ""
|
msgid ""
|
||||||
"The entered amount is not enough for the memberships, should be at least {}"
|
"The entered amount is not enough for the memberships, should be at least {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Der eingegebene Betrag reicht für die Mitgliedschaft nicht aus, sollte "
|
"Der eingegebene Betrag reicht für die Mitgliedschaft nicht aus, sollte "
|
||||||
"mindestens {} betragen"
|
"mindestens {} betragen"
|
||||||
|
|
||||||
#: apps/registration/views.py:417
|
#: apps/registration/views.py:387
|
||||||
msgid "Invalidate pre-registration"
|
msgid "Invalidate pre-registration"
|
||||||
msgstr "Ungültige Vorregistrierung"
|
msgstr "Ungültige Vorregistrierung"
|
||||||
|
|
||||||
@ -2173,7 +2183,7 @@ msgstr "Ungültige Vorregistrierung"
|
|||||||
msgid "Treasury"
|
msgid "Treasury"
|
||||||
msgstr "Quaestor"
|
msgstr "Quaestor"
|
||||||
|
|
||||||
#: apps/treasury/forms.py:26 apps/treasury/models.py:95
|
#: apps/treasury/forms.py:26 apps/treasury/models.py:93
|
||||||
#: apps/treasury/templates/treasury/invoice_form.html:22
|
#: apps/treasury/templates/treasury/invoice_form.html:22
|
||||||
msgid "This invoice is locked and can no longer be edited."
|
msgid "This invoice is locked and can no longer be edited."
|
||||||
msgstr "Diese Rechnung ist gesperrt und kann nicht mehr bearbeitet werden."
|
msgstr "Diese Rechnung ist gesperrt und kann nicht mehr bearbeitet werden."
|
||||||
@ -2186,7 +2196,7 @@ msgstr "Überweisung ist bereits geschlossen."
|
|||||||
msgid "You can't change the type of the remittance."
|
msgid "You can't change the type of the remittance."
|
||||||
msgstr "Sie können die Art der Überweisung nicht ändern."
|
msgstr "Sie können die Art der Überweisung nicht ändern."
|
||||||
|
|
||||||
#: apps/treasury/forms.py:125 apps/treasury/models.py:269
|
#: apps/treasury/forms.py:125 apps/treasury/models.py:267
|
||||||
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
|
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
|
||||||
#: apps/treasury/templates/treasury/invoice_list.html:16
|
#: apps/treasury/templates/treasury/invoice_list.html:16
|
||||||
#: apps/treasury/templates/treasury/remittance_list.html:16
|
#: apps/treasury/templates/treasury/remittance_list.html:16
|
||||||
@ -2202,116 +2212,116 @@ msgstr "Keine beigefügte Überweisung"
|
|||||||
msgid "Invoice identifier"
|
msgid "Invoice identifier"
|
||||||
msgstr "Rechnungskennung"
|
msgstr "Rechnungskennung"
|
||||||
|
|
||||||
#: apps/treasury/models.py:42
|
#: apps/treasury/models.py:40
|
||||||
msgid "BDE"
|
msgid "BDE"
|
||||||
msgstr "BDE"
|
msgstr "BDE"
|
||||||
|
|
||||||
#: apps/treasury/models.py:47
|
#: apps/treasury/models.py:45
|
||||||
msgid "Object"
|
msgid "Object"
|
||||||
msgstr "Objekt"
|
msgstr "Objekt"
|
||||||
|
|
||||||
#: apps/treasury/models.py:51
|
#: apps/treasury/models.py:49
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Beschreibung"
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
#: apps/treasury/models.py:60
|
#: apps/treasury/models.py:58
|
||||||
msgid "Address"
|
msgid "Address"
|
||||||
msgstr "Adresse"
|
msgstr "Adresse"
|
||||||
|
|
||||||
#: apps/treasury/models.py:65 apps/treasury/models.py:195
|
#: apps/treasury/models.py:63 apps/treasury/models.py:193
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Datum"
|
msgstr "Datum"
|
||||||
|
|
||||||
#: apps/treasury/models.py:69
|
#: apps/treasury/models.py:67
|
||||||
msgid "Acquitted"
|
msgid "Acquitted"
|
||||||
msgstr "Bezahlt"
|
msgstr "Bezahlt"
|
||||||
|
|
||||||
#: apps/treasury/models.py:74
|
#: apps/treasury/models.py:72
|
||||||
msgid "Locked"
|
msgid "Locked"
|
||||||
msgstr "Gesperrt"
|
msgstr "Gesperrt"
|
||||||
|
|
||||||
#: apps/treasury/models.py:75
|
#: apps/treasury/models.py:73
|
||||||
msgid "An invoice can't be edited when it is locked."
|
msgid "An invoice can't be edited when it is locked."
|
||||||
msgstr "Eine Rechnung kann nicht bearbeitet werden, wenn sie gesperrt ist."
|
msgstr "Eine Rechnung kann nicht bearbeitet werden, wenn sie gesperrt ist."
|
||||||
|
|
||||||
#: apps/treasury/models.py:81
|
#: apps/treasury/models.py:79
|
||||||
msgid "tex source"
|
msgid "tex source"
|
||||||
msgstr "Tex Quelle"
|
msgstr "Tex Quelle"
|
||||||
|
|
||||||
#: apps/treasury/models.py:115 apps/treasury/models.py:131
|
#: apps/treasury/models.py:113 apps/treasury/models.py:129
|
||||||
msgid "invoice"
|
msgid "invoice"
|
||||||
msgstr "Rechnung"
|
msgstr "Rechnung"
|
||||||
|
|
||||||
#: apps/treasury/models.py:116
|
#: apps/treasury/models.py:114
|
||||||
msgid "invoices"
|
msgid "invoices"
|
||||||
msgstr "Rechnungen"
|
msgstr "Rechnungen"
|
||||||
|
|
||||||
#: apps/treasury/models.py:119
|
#: apps/treasury/models.py:117
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invoice #{id}"
|
msgid "Invoice #{id}"
|
||||||
msgstr "Rechnung #{id}"
|
msgstr "Rechnung #{id}"
|
||||||
|
|
||||||
#: apps/treasury/models.py:136
|
#: apps/treasury/models.py:134
|
||||||
msgid "Designation"
|
msgid "Designation"
|
||||||
msgstr "Bezeichnung"
|
msgstr "Bezeichnung"
|
||||||
|
|
||||||
#: apps/treasury/models.py:142
|
#: apps/treasury/models.py:140
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
msgstr "Qualität"
|
msgstr "Qualität"
|
||||||
|
|
||||||
#: apps/treasury/models.py:147
|
#: apps/treasury/models.py:145
|
||||||
msgid "Unit price"
|
msgid "Unit price"
|
||||||
msgstr "Einzelpreis"
|
msgstr "Einzelpreis"
|
||||||
|
|
||||||
#: apps/treasury/models.py:163
|
#: apps/treasury/models.py:161
|
||||||
msgid "product"
|
msgid "product"
|
||||||
msgstr "Produkt"
|
msgstr "Produkt"
|
||||||
|
|
||||||
#: apps/treasury/models.py:164
|
#: apps/treasury/models.py:162
|
||||||
msgid "products"
|
msgid "products"
|
||||||
msgstr "Produkten"
|
msgstr "Produkten"
|
||||||
|
|
||||||
#: apps/treasury/models.py:184
|
#: apps/treasury/models.py:182
|
||||||
msgid "remittance type"
|
msgid "remittance type"
|
||||||
msgstr "Überweisungstyp"
|
msgstr "Überweisungstyp"
|
||||||
|
|
||||||
#: apps/treasury/models.py:185
|
#: apps/treasury/models.py:183
|
||||||
msgid "remittance types"
|
msgid "remittance types"
|
||||||
msgstr "Überweisungstypen"
|
msgstr "Überweisungstypen"
|
||||||
|
|
||||||
#: apps/treasury/models.py:206
|
#: apps/treasury/models.py:204
|
||||||
msgid "Comment"
|
msgid "Comment"
|
||||||
msgstr "Kommentar"
|
msgstr "Kommentar"
|
||||||
|
|
||||||
#: apps/treasury/models.py:211
|
#: apps/treasury/models.py:209
|
||||||
msgid "Closed"
|
msgid "Closed"
|
||||||
msgstr "Geschlossen"
|
msgstr "Geschlossen"
|
||||||
|
|
||||||
#: apps/treasury/models.py:215
|
#: apps/treasury/models.py:213
|
||||||
msgid "remittance"
|
msgid "remittance"
|
||||||
msgstr "Überweisung"
|
msgstr "Überweisung"
|
||||||
|
|
||||||
#: apps/treasury/models.py:216
|
#: apps/treasury/models.py:214
|
||||||
msgid "remittances"
|
msgid "remittances"
|
||||||
msgstr "Überweisungen"
|
msgstr "Überweisungen"
|
||||||
|
|
||||||
#: apps/treasury/models.py:249
|
#: apps/treasury/models.py:247
|
||||||
msgid "Remittance #{:d}: {}"
|
msgid "Remittance #{:d}: {}"
|
||||||
msgstr "Überweisung #{:d}:{}"
|
msgstr "Überweisung #{:d}:{}"
|
||||||
|
|
||||||
#: apps/treasury/models.py:273
|
#: apps/treasury/models.py:271
|
||||||
msgid "special transaction proxy"
|
msgid "special transaction proxy"
|
||||||
msgstr "spezielle Transaktion Proxy"
|
msgstr "spezielle Transaktion Proxy"
|
||||||
|
|
||||||
#: apps/treasury/models.py:274
|
#: apps/treasury/models.py:272
|
||||||
msgid "special transaction proxies"
|
msgid "special transaction proxies"
|
||||||
msgstr "spezielle Transaktion Proxies"
|
msgstr "spezielle Transaktion Proxies"
|
||||||
|
|
||||||
#: apps/treasury/models.py:300
|
#: apps/treasury/models.py:298
|
||||||
msgid "credit transaction"
|
msgid "credit transaction"
|
||||||
msgstr "Kredit Transaktion"
|
msgstr "Kredit Transaktion"
|
||||||
|
|
||||||
#: apps/treasury/models.py:432
|
#: apps/treasury/models.py:430
|
||||||
msgid ""
|
msgid ""
|
||||||
"This user doesn't have enough money to pay the memberships with its note. "
|
"This user doesn't have enough money to pay the memberships with its note. "
|
||||||
"Please ask her/him to credit the note before invalidating this credit."
|
"Please ask her/him to credit the note before invalidating this credit."
|
||||||
@ -2319,16 +2329,16 @@ msgstr ""
|
|||||||
"Dieser Benutzer hat nicht genug Geld, um die Mitgliedschaften mit seiner "
|
"Dieser Benutzer hat nicht genug Geld, um die Mitgliedschaften mit seiner "
|
||||||
"Note zu bezahlen."
|
"Note zu bezahlen."
|
||||||
|
|
||||||
#: apps/treasury/models.py:452
|
#: apps/treasury/models.py:451
|
||||||
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
|
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
|
||||||
msgid "Credit from the Société générale"
|
msgid "Credit from the Société générale"
|
||||||
msgstr "Kredit von der Société générale"
|
msgstr "Kredit von der Société générale"
|
||||||
|
|
||||||
#: apps/treasury/models.py:453
|
#: apps/treasury/models.py:452
|
||||||
msgid "Credits from the Société générale"
|
msgid "Credits from the Société générale"
|
||||||
msgstr "Krediten von der Société générale"
|
msgstr "Krediten von der Société générale"
|
||||||
|
|
||||||
#: apps/treasury/models.py:456
|
#: apps/treasury/models.py:455
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Soge credit for {user}"
|
msgid "Soge credit for {user}"
|
||||||
msgstr "Kredit von der Société générale für {user}"
|
msgstr "Kredit von der Société générale für {user}"
|
||||||
@ -2592,7 +2602,7 @@ msgid "The selected user is not validated. Please validate its account first"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
|
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
|
||||||
#: apps/wei/models.py:326
|
#: apps/wei/models.py:323
|
||||||
msgid "bus"
|
msgid "bus"
|
||||||
msgstr "Bus"
|
msgstr "Bus"
|
||||||
|
|
||||||
@ -2630,7 +2640,7 @@ msgstr "Wählen Sie die Rollen aus, an denen Sie interessiert sind."
|
|||||||
msgid "This team doesn't belong to the given bus."
|
msgid "This team doesn't belong to the given bus."
|
||||||
msgstr "Dieses Team gehört nicht zum angegebenen Bus."
|
msgstr "Dieses Team gehört nicht zum angegebenen Bus."
|
||||||
|
|
||||||
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:38
|
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:35
|
||||||
msgid "Choose a word:"
|
msgid "Choose a word:"
|
||||||
msgstr "Wählen Sie ein Wort:"
|
msgstr "Wählen Sie ein Wort:"
|
||||||
|
|
||||||
@ -2717,48 +2727,40 @@ msgstr "Nicht binär"
|
|||||||
msgid "gender"
|
msgid "gender"
|
||||||
msgstr "Geschlecht"
|
msgstr "Geschlecht"
|
||||||
|
|
||||||
#: apps/wei/models.py:212
|
#: apps/wei/models.py:213 apps/wei/templates/wei/weimembership_form.html:58
|
||||||
msgid "Unisex"
|
|
||||||
msgstr "Unisex"
|
|
||||||
|
|
||||||
#: apps/wei/models.py:215 apps/wei/templates/wei/weimembership_form.html:58
|
|
||||||
msgid "clothing cut"
|
msgid "clothing cut"
|
||||||
msgstr "Kleidung Schnitt"
|
msgstr "Kleidung Schnitt"
|
||||||
|
|
||||||
#: apps/wei/models.py:228 apps/wei/templates/wei/weimembership_form.html:61
|
#: apps/wei/models.py:226 apps/wei/templates/wei/weimembership_form.html:61
|
||||||
msgid "clothing size"
|
msgid "clothing size"
|
||||||
msgstr "Kleidergröße"
|
msgstr "Kleidergröße"
|
||||||
|
|
||||||
#: apps/wei/models.py:234 apps/wei/templates/wei/attribute_bus_1A.html:28
|
#: apps/wei/models.py:232 apps/wei/templates/wei/attribute_bus_1A.html:28
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:67
|
#: apps/wei/templates/wei/weimembership_form.html:67
|
||||||
msgid "health issues"
|
msgid "health issues"
|
||||||
msgstr "Gesundheitsprobleme"
|
msgstr "Gesundheitsprobleme"
|
||||||
|
|
||||||
#: apps/wei/models.py:239 apps/wei/templates/wei/weimembership_form.html:70
|
#: apps/wei/models.py:237 apps/wei/templates/wei/weimembership_form.html:70
|
||||||
msgid "emergency contact name"
|
msgid "emergency contact name"
|
||||||
msgstr "Notfall-Kontakt"
|
msgstr "Notfall-Kontakt"
|
||||||
|
|
||||||
#: apps/wei/models.py:240
|
#: apps/wei/models.py:242 apps/wei/templates/wei/weimembership_form.html:73
|
||||||
msgid "The emergency contact must not be a WEI participant"
|
|
||||||
msgstr "Der Notfallkontakt darf kein WEI-Teilnehmer sein"
|
|
||||||
|
|
||||||
#: apps/wei/models.py:245 apps/wei/templates/wei/weimembership_form.html:73
|
|
||||||
msgid "emergency contact phone"
|
msgid "emergency contact phone"
|
||||||
msgstr "Notfallkontakttelefon"
|
msgstr "Notfallkontakttelefon"
|
||||||
|
|
||||||
#: apps/wei/models.py:250 apps/wei/templates/wei/weimembership_form.html:52
|
#: apps/wei/models.py:247 apps/wei/templates/wei/weimembership_form.html:52
|
||||||
msgid "first year"
|
msgid "first year"
|
||||||
msgstr "Erste Jahr"
|
msgstr "Erste Jahr"
|
||||||
|
|
||||||
#: apps/wei/models.py:251
|
#: apps/wei/models.py:248
|
||||||
msgid "Tells if the user is new in the school."
|
msgid "Tells if the user is new in the school."
|
||||||
msgstr "Gibt an, ob der USer neu in der Schule ist."
|
msgstr "Gibt an, ob der USer neu in der Schule ist."
|
||||||
|
|
||||||
#: apps/wei/models.py:256
|
#: apps/wei/models.py:253
|
||||||
msgid "registration information"
|
msgid "registration information"
|
||||||
msgstr "Registrierung Detailen"
|
msgstr "Registrierung Detailen"
|
||||||
|
|
||||||
#: apps/wei/models.py:257
|
#: apps/wei/models.py:254
|
||||||
msgid ""
|
msgid ""
|
||||||
"Information about the registration (buses for old members, survey for the "
|
"Information about the registration (buses for old members, survey for the "
|
||||||
"new members), encoded in JSON"
|
"new members), encoded in JSON"
|
||||||
@ -2766,27 +2768,27 @@ msgstr ""
|
|||||||
"Informationen zur Registrierung (Busse für alte Mitglieder, Umfrage für neue "
|
"Informationen zur Registrierung (Busse für alte Mitglieder, Umfrage für neue "
|
||||||
"Mitglieder), verschlüsselt in JSON"
|
"Mitglieder), verschlüsselt in JSON"
|
||||||
|
|
||||||
#: apps/wei/models.py:315
|
#: apps/wei/models.py:312
|
||||||
msgid "WEI User"
|
msgid "WEI User"
|
||||||
msgstr "WEI User"
|
msgstr "WEI User"
|
||||||
|
|
||||||
#: apps/wei/models.py:316
|
#: apps/wei/models.py:313
|
||||||
msgid "WEI Users"
|
msgid "WEI Users"
|
||||||
msgstr "WEI Users"
|
msgstr "WEI Users"
|
||||||
|
|
||||||
#: apps/wei/models.py:336
|
#: apps/wei/models.py:333
|
||||||
msgid "team"
|
msgid "team"
|
||||||
msgstr "Team"
|
msgstr "Team"
|
||||||
|
|
||||||
#: apps/wei/models.py:346
|
#: apps/wei/models.py:343
|
||||||
msgid "WEI registration"
|
msgid "WEI registration"
|
||||||
msgstr "WEI Registrierung"
|
msgstr "WEI Registrierung"
|
||||||
|
|
||||||
#: apps/wei/models.py:350
|
#: apps/wei/models.py:347
|
||||||
msgid "WEI membership"
|
msgid "WEI membership"
|
||||||
msgstr "WEI Mitgliedschaft"
|
msgstr "WEI Mitgliedschaft"
|
||||||
|
|
||||||
#: apps/wei/models.py:351
|
#: apps/wei/models.py:348
|
||||||
msgid "WEI memberships"
|
msgid "WEI memberships"
|
||||||
msgstr "WEI Mitgliedschaften"
|
msgstr "WEI Mitgliedschaften"
|
||||||
|
|
||||||
@ -3359,10 +3361,6 @@ msgstr "Kontakt"
|
|||||||
msgid "Technical Support"
|
msgid "Technical Support"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:198
|
|
||||||
msgid "FAQ (FR)"
|
|
||||||
msgstr "FAQ (FR)"
|
|
||||||
|
|
||||||
#: note_kfet/templates/base_search.html:15
|
#: note_kfet/templates/base_search.html:15
|
||||||
msgid "Search by attribute such as name…"
|
msgid "Search by attribute such as name…"
|
||||||
msgstr "Suche nach Attributen wie Name…"
|
msgstr "Suche nach Attributen wie Name…"
|
||||||
@ -3610,16 +3608,10 @@ msgstr ""
|
|||||||
"müssen Ihre E-Mail-Adresse auch überprüfen, indem Sie dem Link folgen, den "
|
"müssen Ihre E-Mail-Adresse auch überprüfen, indem Sie dem Link folgen, den "
|
||||||
"Sie erhalten haben."
|
"Sie erhalten haben."
|
||||||
|
|
||||||
#, fuzzy
|
|
||||||
#~| msgid "You already opened an account in the Société générale."
|
|
||||||
#~ msgid ""
|
|
||||||
#~ "I declare that I opened or I will open soon a bank account in the Société "
|
|
||||||
#~ "générale with the BDE partnership."
|
|
||||||
#~ msgstr "Sie haben bereits ein Konto in der Société générale eröffnet."
|
|
||||||
|
|
||||||
#~ msgid "This user didn't give her/his caution check."
|
#~ msgid "This user didn't give her/his caution check."
|
||||||
#~ msgstr "Dieser User hat seine / ihre Vorsicht nicht überprüft."
|
#~ msgstr "Dieser User hat seine / ihre Vorsicht nicht überprüft."
|
||||||
|
|
||||||
|
#, python-format
|
||||||
#~ msgid ""
|
#~ msgid ""
|
||||||
#~ "A new version of the application is available. This instance runs "
|
#~ "A new version of the application is available. This instance runs "
|
||||||
#~ "%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
|
#~ "%(VERSION)s and the last version is %(LAST_VERSION)s. Please consider "
|
||||||
|
@ -7,9 +7,9 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-07-09 14:46+0200\n"
|
"POT-Creation-Date: 2022-04-10 22:34+0200\n"
|
||||||
"PO-Revision-Date: 2022-04-11 23:12+0200\n"
|
"PO-Revision-Date: 2022-04-11 23:12+0200\n"
|
||||||
"Last-Translator: bleizi <bleizi@crans.org>\n"
|
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
|
||||||
"Language-Team: \n"
|
"Language-Team: \n"
|
||||||
"Language: es\n"
|
"Language: es\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -114,7 +114,7 @@ msgid "type"
|
|||||||
msgstr "tipo"
|
msgstr "tipo"
|
||||||
|
|
||||||
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
|
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
|
||||||
#: apps/note/models/notes.py:148 apps/treasury/models.py:287
|
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
|
||||||
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
||||||
#: apps/wei/templates/wei/survey.html:15
|
#: apps/wei/templates/wei/survey.html:15
|
||||||
msgid "user"
|
msgid "user"
|
||||||
@ -257,19 +257,19 @@ msgstr "Entrado el "
|
|||||||
msgid "remove"
|
msgid "remove"
|
||||||
msgstr "quitar"
|
msgstr "quitar"
|
||||||
|
|
||||||
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:201
|
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:199
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr "Tipo"
|
msgstr "Tipo"
|
||||||
|
|
||||||
#: apps/activity/tables.py:84 apps/member/forms.py:186
|
#: apps/activity/tables.py:84 apps/member/forms.py:186
|
||||||
#: apps/registration/forms.py:92 apps/treasury/forms.py:131
|
#: apps/registration/forms.py:91 apps/treasury/forms.py:131
|
||||||
#: apps/wei/forms/registration.py:104
|
#: apps/wei/forms/registration.py:104
|
||||||
msgid "Last name"
|
msgid "Last name"
|
||||||
msgstr "Apellido"
|
msgstr "Apellido"
|
||||||
|
|
||||||
#: apps/activity/tables.py:86 apps/member/forms.py:191
|
#: apps/activity/tables.py:86 apps/member/forms.py:191
|
||||||
#: apps/note/templates/note/transaction_form.html:138
|
#: apps/note/templates/note/transaction_form.html:138
|
||||||
#: apps/registration/forms.py:97 apps/treasury/forms.py:133
|
#: apps/registration/forms.py:96 apps/treasury/forms.py:133
|
||||||
#: apps/wei/forms/registration.py:109
|
#: apps/wei/forms/registration.py:109
|
||||||
msgid "First name"
|
msgid "First name"
|
||||||
msgstr "Nombre"
|
msgstr "Nombre"
|
||||||
@ -546,24 +546,24 @@ msgid "This image cannot be loaded."
|
|||||||
msgstr "Esta imagen no puede ser cargada."
|
msgstr "Esta imagen no puede ser cargada."
|
||||||
|
|
||||||
#: apps/member/forms.py:141 apps/member/views.py:103
|
#: apps/member/forms.py:141 apps/member/views.py:103
|
||||||
#: apps/registration/forms.py:34 apps/registration/views.py:266
|
#: apps/registration/forms.py:33 apps/registration/views.py:262
|
||||||
msgid "An alias with a similar name already exists."
|
msgid "An alias with a similar name already exists."
|
||||||
msgstr "Un alias similar ya existe."
|
msgstr "Un alias similar ya existe."
|
||||||
|
|
||||||
#: apps/member/forms.py:165
|
#: apps/member/forms.py:165 apps/registration/forms.py:71
|
||||||
msgid "Inscription paid by Société Générale"
|
msgid "Inscription paid by Société Générale"
|
||||||
msgstr "Registración pagadas por Société Générale"
|
msgstr "Registración pagadas por Société Générale"
|
||||||
|
|
||||||
#: apps/member/forms.py:167
|
#: apps/member/forms.py:167 apps/registration/forms.py:73
|
||||||
msgid "Check this case if the Société Générale paid the inscription."
|
msgid "Check this case if the Société Générale paid the inscription."
|
||||||
msgstr "Marcar esta casilla si Société Générale pagó la registración."
|
msgstr "Marcar esta casilla si Société Générale pagó la registración."
|
||||||
|
|
||||||
#: apps/member/forms.py:172 apps/registration/forms.py:79
|
#: apps/member/forms.py:172 apps/registration/forms.py:78
|
||||||
#: apps/wei/forms/registration.py:91
|
#: apps/wei/forms/registration.py:91
|
||||||
msgid "Credit type"
|
msgid "Credit type"
|
||||||
msgstr "Tipo de crédito"
|
msgstr "Tipo de crédito"
|
||||||
|
|
||||||
#: apps/member/forms.py:173 apps/registration/forms.py:80
|
#: apps/member/forms.py:173 apps/registration/forms.py:79
|
||||||
#: apps/wei/forms/registration.py:92
|
#: apps/wei/forms/registration.py:92
|
||||||
msgid "No credit"
|
msgid "No credit"
|
||||||
msgstr "No crédito"
|
msgstr "No crédito"
|
||||||
@ -572,13 +572,13 @@ msgstr "No crédito"
|
|||||||
msgid "You can credit the note of the user."
|
msgid "You can credit the note of the user."
|
||||||
msgstr "Usted puede acreditar la note del usuario."
|
msgstr "Usted puede acreditar la note del usuario."
|
||||||
|
|
||||||
#: apps/member/forms.py:179 apps/registration/forms.py:85
|
#: apps/member/forms.py:179 apps/registration/forms.py:84
|
||||||
#: apps/wei/forms/registration.py:97
|
#: apps/wei/forms/registration.py:97
|
||||||
msgid "Credit amount"
|
msgid "Credit amount"
|
||||||
msgstr "Valor del crédito"
|
msgstr "Valor del crédito"
|
||||||
|
|
||||||
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:144
|
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:144
|
||||||
#: apps/registration/forms.py:102 apps/treasury/forms.py:135
|
#: apps/registration/forms.py:101 apps/treasury/forms.py:135
|
||||||
#: apps/wei/forms/registration.py:114
|
#: apps/wei/forms/registration.py:114
|
||||||
msgid "Bank"
|
msgid "Bank"
|
||||||
msgstr "Banco"
|
msgstr "Banco"
|
||||||
@ -1180,7 +1180,7 @@ msgstr "Guardar cambios"
|
|||||||
msgid "Registrations"
|
msgid "Registrations"
|
||||||
msgstr "Registraciones"
|
msgstr "Registraciones"
|
||||||
|
|
||||||
#: apps/member/views.py:73 apps/registration/forms.py:24
|
#: apps/member/views.py:73 apps/registration/forms.py:23
|
||||||
msgid "This address must be valid."
|
msgid "This address must be valid."
|
||||||
msgstr "Este correo tiene que ser valido."
|
msgstr "Este correo tiene que ser valido."
|
||||||
|
|
||||||
@ -1236,11 +1236,11 @@ msgstr "La afiliación tiene que empezar después del {:%d-%m-%Y}."
|
|||||||
msgid "The membership must begin before {:%m-%d-%Y}."
|
msgid "The membership must begin before {:%m-%d-%Y}."
|
||||||
msgstr "La afiliación tiene que empezar antes del {:%d-%m-%Y}."
|
msgstr "La afiliación tiene que empezar antes del {:%d-%m-%Y}."
|
||||||
|
|
||||||
#: apps/member/views.py:880
|
#: apps/member/views.py:876
|
||||||
msgid "Manage roles of an user in the club"
|
msgid "Manage roles of an user in the club"
|
||||||
msgstr "Gestionar los papeles de un usuario en el club"
|
msgstr "Gestionar los papeles de un usuario en el club"
|
||||||
|
|
||||||
#: apps/member/views.py:905
|
#: apps/member/views.py:901
|
||||||
msgid "Members of the club"
|
msgid "Members of the club"
|
||||||
msgstr "Miembros del club"
|
msgstr "Miembros del club"
|
||||||
|
|
||||||
@ -1557,7 +1557,7 @@ msgstr "Transacciones especiales"
|
|||||||
msgid "membership transaction"
|
msgid "membership transaction"
|
||||||
msgstr "transacción de afiliación"
|
msgstr "transacción de afiliación"
|
||||||
|
|
||||||
#: apps/note/models/transactions.py:385 apps/treasury/models.py:294
|
#: apps/note/models/transactions.py:385 apps/treasury/models.py:292
|
||||||
msgid "membership transactions"
|
msgid "membership transactions"
|
||||||
msgstr "transacciones de afiliación"
|
msgstr "transacciones de afiliación"
|
||||||
|
|
||||||
@ -1676,7 +1676,7 @@ msgid "Amount"
|
|||||||
msgstr "Monto"
|
msgstr "Monto"
|
||||||
|
|
||||||
#: apps/note/templates/note/transaction_form.html:132
|
#: apps/note/templates/note/transaction_form.html:132
|
||||||
#: apps/treasury/models.py:56
|
#: apps/treasury/models.py:54
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Nombre"
|
msgstr "Nombre"
|
||||||
|
|
||||||
@ -1963,15 +1963,31 @@ msgstr "Todos los permisos"
|
|||||||
msgid "registration"
|
msgid "registration"
|
||||||
msgstr "afiliación"
|
msgstr "afiliación"
|
||||||
|
|
||||||
#: apps/registration/forms.py:40
|
#: apps/registration/forms.py:39
|
||||||
msgid "This email address is already used."
|
msgid "This email address is already used."
|
||||||
msgstr "Este correo electrónico ya esta utilizado."
|
msgstr "Este correo electrónico ya esta utilizado."
|
||||||
|
|
||||||
#: apps/registration/forms.py:60
|
#: apps/registration/forms.py:49
|
||||||
|
msgid ""
|
||||||
|
"I declare that I opened or I will open soon a bank account in the Société "
|
||||||
|
"générale with the BDE partnership."
|
||||||
|
msgstr ""
|
||||||
|
"Declaro que ya abrió una cuenta a la Société Générale en colaboración con el "
|
||||||
|
"BDE."
|
||||||
|
|
||||||
|
#: apps/registration/forms.py:51
|
||||||
|
msgid ""
|
||||||
|
"Warning: this engages you to open your bank account. If you finally decides "
|
||||||
|
"to don't open your account, you will have to pay the BDE membership."
|
||||||
|
msgstr ""
|
||||||
|
"Cuidado : esto le obliga abrir su cuenta bancaria. Si cambia de idea y no "
|
||||||
|
"abre su cuenta bancaria, tendrá que pagar su afiliación al BDE."
|
||||||
|
|
||||||
|
#: apps/registration/forms.py:59
|
||||||
msgid "Register to the WEI"
|
msgid "Register to the WEI"
|
||||||
msgstr "Registrarse en el WEI"
|
msgstr "Registrarse en el WEI"
|
||||||
|
|
||||||
#: apps/registration/forms.py:62
|
#: apps/registration/forms.py:61
|
||||||
msgid ""
|
msgid ""
|
||||||
"Check this case if you want to register to the WEI. If you hesitate, you "
|
"Check this case if you want to register to the WEI. If you hesitate, you "
|
||||||
"will be able to register later, after validating your account in the Kfet."
|
"will be able to register later, after validating your account in the Kfet."
|
||||||
@ -1979,18 +1995,14 @@ msgstr ""
|
|||||||
"Marcar esta casilla si usted quiere registrarse en el WEI. Si duda, podrá "
|
"Marcar esta casilla si usted quiere registrarse en el WEI. Si duda, podrá "
|
||||||
"registrarse más tarde, después de validar su cuenta Note Kfet."
|
"registrarse más tarde, después de validar su cuenta Note Kfet."
|
||||||
|
|
||||||
#: apps/registration/forms.py:107
|
#: apps/registration/forms.py:106
|
||||||
msgid "Join BDE Club"
|
msgid "Join BDE Club"
|
||||||
msgstr "Afiliarse al club BDE"
|
msgstr "Afiliarse al club BDE"
|
||||||
|
|
||||||
#: apps/registration/forms.py:114
|
#: apps/registration/forms.py:113
|
||||||
msgid "Join Kfet Club"
|
msgid "Join Kfet Club"
|
||||||
msgstr "Afiliarse al club Kfet"
|
msgstr "Afiliarse al club Kfet"
|
||||||
|
|
||||||
#: apps/registration/forms.py:123
|
|
||||||
msgid "Join BDA Club"
|
|
||||||
msgstr "Afiliarse al club BDA"
|
|
||||||
|
|
||||||
#: apps/registration/templates/registration/email_validation_complete.html:15
|
#: apps/registration/templates/registration/email_validation_complete.html:15
|
||||||
msgid "Your email have successfully been validated."
|
msgid "Your email have successfully been validated."
|
||||||
msgstr "Su correo electrónico fue validado con éxito."
|
msgstr "Su correo electrónico fue validado con éxito."
|
||||||
@ -2039,12 +2051,12 @@ msgstr "Suprimir afiliación"
|
|||||||
msgid "Validate account"
|
msgid "Validate account"
|
||||||
msgstr "Validar la cuenta"
|
msgstr "Validar la cuenta"
|
||||||
|
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:63
|
#: apps/registration/templates/registration/future_profile_detail.html:62
|
||||||
msgid ""
|
msgid ""
|
||||||
"The user declared that he/she opened a bank account in the Société générale."
|
"The user declared that he/she opened a bank account in the Société générale."
|
||||||
msgstr "El usuario declara que ya abrió una cuenta a la Société Générale."
|
msgstr "El usuario declara que ya abrió una cuenta a la Société Générale."
|
||||||
|
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:73
|
#: apps/registration/templates/registration/future_profile_detail.html:71
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:127
|
#: apps/wei/templates/wei/weimembership_form.html:127
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:186
|
#: apps/wei/templates/wei/weimembership_form.html:186
|
||||||
msgid "Validate registration"
|
msgid "Validate registration"
|
||||||
@ -2096,54 +2108,54 @@ msgstr "Gracias"
|
|||||||
msgid "The Note Kfet team."
|
msgid "The Note Kfet team."
|
||||||
msgstr "El equipo Note Kfet."
|
msgstr "El equipo Note Kfet."
|
||||||
|
|
||||||
#: apps/registration/views.py:41
|
#: apps/registration/views.py:40
|
||||||
msgid "Register new user"
|
msgid "Register new user"
|
||||||
msgstr "Registrar un nuevo usuario"
|
msgstr "Registrar un nuevo usuario"
|
||||||
|
|
||||||
#: apps/registration/views.py:99
|
#: apps/registration/views.py:98
|
||||||
msgid "Email validation"
|
msgid "Email validation"
|
||||||
msgstr "Validación del correo electrónico"
|
msgstr "Validación del correo electrónico"
|
||||||
|
|
||||||
#: apps/registration/views.py:101
|
#: apps/registration/views.py:100
|
||||||
msgid "Validate email"
|
msgid "Validate email"
|
||||||
msgstr "Validar el correo electrónico"
|
msgstr "Validar el correo electrónico"
|
||||||
|
|
||||||
#: apps/registration/views.py:145
|
#: apps/registration/views.py:144
|
||||||
msgid "Email validation unsuccessful"
|
msgid "Email validation unsuccessful"
|
||||||
msgstr "La validación del correo electrónico fracasó"
|
msgstr "La validación del correo electrónico fracasó"
|
||||||
|
|
||||||
#: apps/registration/views.py:156
|
#: apps/registration/views.py:155
|
||||||
msgid "Email validation email sent"
|
msgid "Email validation email sent"
|
||||||
msgstr "Correo de validación enviado"
|
msgstr "Correo de validación enviado"
|
||||||
|
|
||||||
#: apps/registration/views.py:164
|
#: apps/registration/views.py:163
|
||||||
msgid "Resend email validation link"
|
msgid "Resend email validation link"
|
||||||
msgstr "Reenviar el enlace de validación"
|
msgstr "Reenviar el enlace de validación"
|
||||||
|
|
||||||
#: apps/registration/views.py:182
|
#: apps/registration/views.py:181
|
||||||
msgid "Pre-registered users list"
|
msgid "Pre-registered users list"
|
||||||
msgstr "Lista de los usuarios con afiliación pendiente"
|
msgstr "Lista de los usuarios con afiliación pendiente"
|
||||||
|
|
||||||
#: apps/registration/views.py:206
|
#: apps/registration/views.py:205
|
||||||
msgid "Unregistered users"
|
msgid "Unregistered users"
|
||||||
msgstr "Usuarios con afiliación pendiente"
|
msgstr "Usuarios con afiliación pendiente"
|
||||||
|
|
||||||
#: apps/registration/views.py:219
|
#: apps/registration/views.py:218
|
||||||
msgid "Registration detail"
|
msgid "Registration detail"
|
||||||
msgstr "Detalles de la afiliación"
|
msgstr "Detalles de la afiliación"
|
||||||
|
|
||||||
#: apps/registration/views.py:293
|
#: apps/registration/views.py:282
|
||||||
msgid "You must join the BDE."
|
msgid "You must join the BDE."
|
||||||
msgstr "Usted tiene que afiliarse al BDE."
|
msgstr "Usted tiene que afiliarse al BDE."
|
||||||
|
|
||||||
#: apps/registration/views.py:323
|
#: apps/registration/views.py:306
|
||||||
msgid ""
|
msgid ""
|
||||||
"The entered amount is not enough for the memberships, should be at least {}"
|
"The entered amount is not enough for the memberships, should be at least {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"El monto dado no es suficiente para las afiliaciones, tiene que ser al menos "
|
"El monto dado no es suficiente para las afiliaciones, tiene que ser al menos "
|
||||||
"{}"
|
"{}"
|
||||||
|
|
||||||
#: apps/registration/views.py:417
|
#: apps/registration/views.py:387
|
||||||
msgid "Invalidate pre-registration"
|
msgid "Invalidate pre-registration"
|
||||||
msgstr "Invalidar la afiliación"
|
msgstr "Invalidar la afiliación"
|
||||||
|
|
||||||
@ -2151,7 +2163,7 @@ msgstr "Invalidar la afiliación"
|
|||||||
msgid "Treasury"
|
msgid "Treasury"
|
||||||
msgstr "Tesorería"
|
msgstr "Tesorería"
|
||||||
|
|
||||||
#: apps/treasury/forms.py:26 apps/treasury/models.py:95
|
#: apps/treasury/forms.py:26 apps/treasury/models.py:93
|
||||||
#: apps/treasury/templates/treasury/invoice_form.html:22
|
#: apps/treasury/templates/treasury/invoice_form.html:22
|
||||||
msgid "This invoice is locked and can no longer be edited."
|
msgid "This invoice is locked and can no longer be edited."
|
||||||
msgstr "Esta factura esta bloqueada y no puede ser modificada."
|
msgstr "Esta factura esta bloqueada y no puede ser modificada."
|
||||||
@ -2164,7 +2176,7 @@ msgstr "El descuento ya esta cerrado."
|
|||||||
msgid "You can't change the type of the remittance."
|
msgid "You can't change the type of the remittance."
|
||||||
msgstr "No puede cambiar el tipo de descuento."
|
msgstr "No puede cambiar el tipo de descuento."
|
||||||
|
|
||||||
#: apps/treasury/forms.py:125 apps/treasury/models.py:269
|
#: apps/treasury/forms.py:125 apps/treasury/models.py:267
|
||||||
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
|
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
|
||||||
#: apps/treasury/templates/treasury/invoice_list.html:16
|
#: apps/treasury/templates/treasury/invoice_list.html:16
|
||||||
#: apps/treasury/templates/treasury/remittance_list.html:16
|
#: apps/treasury/templates/treasury/remittance_list.html:16
|
||||||
@ -2180,116 +2192,116 @@ msgstr "No hay descuento relacionado"
|
|||||||
msgid "Invoice identifier"
|
msgid "Invoice identifier"
|
||||||
msgstr "Numero de factura"
|
msgstr "Numero de factura"
|
||||||
|
|
||||||
#: apps/treasury/models.py:42
|
#: apps/treasury/models.py:40
|
||||||
msgid "BDE"
|
msgid "BDE"
|
||||||
msgstr "BDE"
|
msgstr "BDE"
|
||||||
|
|
||||||
#: apps/treasury/models.py:47
|
#: apps/treasury/models.py:45
|
||||||
msgid "Object"
|
msgid "Object"
|
||||||
msgstr "Asunto"
|
msgstr "Asunto"
|
||||||
|
|
||||||
#: apps/treasury/models.py:51
|
#: apps/treasury/models.py:49
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Descripción"
|
msgstr "Descripción"
|
||||||
|
|
||||||
#: apps/treasury/models.py:60
|
#: apps/treasury/models.py:58
|
||||||
msgid "Address"
|
msgid "Address"
|
||||||
msgstr "Dirección"
|
msgstr "Dirección"
|
||||||
|
|
||||||
#: apps/treasury/models.py:65 apps/treasury/models.py:195
|
#: apps/treasury/models.py:63 apps/treasury/models.py:193
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Fecha"
|
msgstr "Fecha"
|
||||||
|
|
||||||
#: apps/treasury/models.py:69
|
#: apps/treasury/models.py:67
|
||||||
msgid "Acquitted"
|
msgid "Acquitted"
|
||||||
msgstr "Pagada"
|
msgstr "Pagada"
|
||||||
|
|
||||||
#: apps/treasury/models.py:74
|
#: apps/treasury/models.py:72
|
||||||
msgid "Locked"
|
msgid "Locked"
|
||||||
msgstr "Bloqueada"
|
msgstr "Bloqueada"
|
||||||
|
|
||||||
#: apps/treasury/models.py:75
|
#: apps/treasury/models.py:73
|
||||||
msgid "An invoice can't be edited when it is locked."
|
msgid "An invoice can't be edited when it is locked."
|
||||||
msgstr "Une factura no puede ser modificada cuando esta bloqueada."
|
msgstr "Une factura no puede ser modificada cuando esta bloqueada."
|
||||||
|
|
||||||
#: apps/treasury/models.py:81
|
#: apps/treasury/models.py:79
|
||||||
msgid "tex source"
|
msgid "tex source"
|
||||||
msgstr "código fuente TeX"
|
msgstr "código fuente TeX"
|
||||||
|
|
||||||
#: apps/treasury/models.py:115 apps/treasury/models.py:131
|
#: apps/treasury/models.py:113 apps/treasury/models.py:129
|
||||||
msgid "invoice"
|
msgid "invoice"
|
||||||
msgstr "factura"
|
msgstr "factura"
|
||||||
|
|
||||||
#: apps/treasury/models.py:116
|
#: apps/treasury/models.py:114
|
||||||
msgid "invoices"
|
msgid "invoices"
|
||||||
msgstr "facturas"
|
msgstr "facturas"
|
||||||
|
|
||||||
#: apps/treasury/models.py:119
|
#: apps/treasury/models.py:117
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invoice #{id}"
|
msgid "Invoice #{id}"
|
||||||
msgstr "Factura n°{id}"
|
msgstr "Factura n°{id}"
|
||||||
|
|
||||||
#: apps/treasury/models.py:136
|
#: apps/treasury/models.py:134
|
||||||
msgid "Designation"
|
msgid "Designation"
|
||||||
msgstr "Designación"
|
msgstr "Designación"
|
||||||
|
|
||||||
#: apps/treasury/models.py:142
|
#: apps/treasury/models.py:140
|
||||||
msgid "Quantity"
|
msgid "Quantity"
|
||||||
msgstr "Cantidad"
|
msgstr "Cantidad"
|
||||||
|
|
||||||
#: apps/treasury/models.py:147
|
#: apps/treasury/models.py:145
|
||||||
msgid "Unit price"
|
msgid "Unit price"
|
||||||
msgstr "Precio unitario"
|
msgstr "Precio unitario"
|
||||||
|
|
||||||
#: apps/treasury/models.py:163
|
#: apps/treasury/models.py:161
|
||||||
msgid "product"
|
msgid "product"
|
||||||
msgstr "producto"
|
msgstr "producto"
|
||||||
|
|
||||||
#: apps/treasury/models.py:164
|
#: apps/treasury/models.py:162
|
||||||
msgid "products"
|
msgid "products"
|
||||||
msgstr "productos"
|
msgstr "productos"
|
||||||
|
|
||||||
#: apps/treasury/models.py:184
|
#: apps/treasury/models.py:182
|
||||||
msgid "remittance type"
|
msgid "remittance type"
|
||||||
msgstr "tipo de descuento"
|
msgstr "tipo de descuento"
|
||||||
|
|
||||||
#: apps/treasury/models.py:185
|
#: apps/treasury/models.py:183
|
||||||
msgid "remittance types"
|
msgid "remittance types"
|
||||||
msgstr "tipos de descuentos"
|
msgstr "tipos de descuentos"
|
||||||
|
|
||||||
#: apps/treasury/models.py:206
|
#: apps/treasury/models.py:204
|
||||||
msgid "Comment"
|
msgid "Comment"
|
||||||
msgstr "Comentario"
|
msgstr "Comentario"
|
||||||
|
|
||||||
#: apps/treasury/models.py:211
|
#: apps/treasury/models.py:209
|
||||||
msgid "Closed"
|
msgid "Closed"
|
||||||
msgstr "Cerrada"
|
msgstr "Cerrada"
|
||||||
|
|
||||||
#: apps/treasury/models.py:215
|
#: apps/treasury/models.py:213
|
||||||
msgid "remittance"
|
msgid "remittance"
|
||||||
msgstr "descuento"
|
msgstr "descuento"
|
||||||
|
|
||||||
#: apps/treasury/models.py:216
|
#: apps/treasury/models.py:214
|
||||||
msgid "remittances"
|
msgid "remittances"
|
||||||
msgstr "descuentos"
|
msgstr "descuentos"
|
||||||
|
|
||||||
#: apps/treasury/models.py:249
|
#: apps/treasury/models.py:247
|
||||||
msgid "Remittance #{:d}: {}"
|
msgid "Remittance #{:d}: {}"
|
||||||
msgstr "Descuento n°{:d} : {}"
|
msgstr "Descuento n°{:d} : {}"
|
||||||
|
|
||||||
#: apps/treasury/models.py:273
|
#: apps/treasury/models.py:271
|
||||||
msgid "special transaction proxy"
|
msgid "special transaction proxy"
|
||||||
msgstr "proxy de transacción especial"
|
msgstr "proxy de transacción especial"
|
||||||
|
|
||||||
#: apps/treasury/models.py:274
|
#: apps/treasury/models.py:272
|
||||||
msgid "special transaction proxies"
|
msgid "special transaction proxies"
|
||||||
msgstr "proxys de transacciones especiales"
|
msgstr "proxys de transacciones especiales"
|
||||||
|
|
||||||
#: apps/treasury/models.py:300
|
#: apps/treasury/models.py:298
|
||||||
msgid "credit transaction"
|
msgid "credit transaction"
|
||||||
msgstr "transacción de crédito"
|
msgstr "transacción de crédito"
|
||||||
|
|
||||||
#: apps/treasury/models.py:432
|
#: apps/treasury/models.py:430
|
||||||
msgid ""
|
msgid ""
|
||||||
"This user doesn't have enough money to pay the memberships with its note. "
|
"This user doesn't have enough money to pay the memberships with its note. "
|
||||||
"Please ask her/him to credit the note before invalidating this credit."
|
"Please ask her/him to credit the note before invalidating this credit."
|
||||||
@ -2298,16 +2310,16 @@ msgstr ""
|
|||||||
"afiliaciones. Por favor pídelo acreditar su note antes de invalidar este "
|
"afiliaciones. Por favor pídelo acreditar su note antes de invalidar este "
|
||||||
"crédito."
|
"crédito."
|
||||||
|
|
||||||
#: apps/treasury/models.py:452
|
#: apps/treasury/models.py:451
|
||||||
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
|
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
|
||||||
msgid "Credit from the Société générale"
|
msgid "Credit from the Société générale"
|
||||||
msgstr "Crédito de la Société Générale"
|
msgstr "Crédito de la Société Générale"
|
||||||
|
|
||||||
#: apps/treasury/models.py:453
|
#: apps/treasury/models.py:452
|
||||||
msgid "Credits from the Société générale"
|
msgid "Credits from the Société générale"
|
||||||
msgstr "Créditos de la Société Générale"
|
msgstr "Créditos de la Société Générale"
|
||||||
|
|
||||||
#: apps/treasury/models.py:456
|
#: apps/treasury/models.py:455
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Soge credit for {user}"
|
msgid "Soge credit for {user}"
|
||||||
msgstr "Crédito de la Société Générale para {user}"
|
msgstr "Crédito de la Société Générale para {user}"
|
||||||
@ -2562,7 +2574,7 @@ msgstr ""
|
|||||||
"El usuario seleccionado no ha sido validado. Validar esta cuenta primero"
|
"El usuario seleccionado no ha sido validado. Validar esta cuenta primero"
|
||||||
|
|
||||||
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
|
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
|
||||||
#: apps/wei/models.py:326
|
#: apps/wei/models.py:323
|
||||||
msgid "bus"
|
msgid "bus"
|
||||||
msgstr "bus"
|
msgstr "bus"
|
||||||
|
|
||||||
@ -2600,7 +2612,7 @@ msgstr "Elegir los papeles que le interesa."
|
|||||||
msgid "This team doesn't belong to the given bus."
|
msgid "This team doesn't belong to the given bus."
|
||||||
msgstr "Este equipo no pertenece al bus dado."
|
msgstr "Este equipo no pertenece al bus dado."
|
||||||
|
|
||||||
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:38
|
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:35
|
||||||
msgid "Choose a word:"
|
msgid "Choose a word:"
|
||||||
msgstr "Elegir una palabra :"
|
msgstr "Elegir una palabra :"
|
||||||
|
|
||||||
@ -2687,48 +2699,40 @@ msgstr "No binari@"
|
|||||||
msgid "gender"
|
msgid "gender"
|
||||||
msgstr "género"
|
msgstr "género"
|
||||||
|
|
||||||
#: apps/wei/models.py:212
|
#: apps/wei/models.py:213 apps/wei/templates/wei/weimembership_form.html:58
|
||||||
msgid "Unisex"
|
|
||||||
msgstr "Unisex"
|
|
||||||
|
|
||||||
#: apps/wei/models.py:215 apps/wei/templates/wei/weimembership_form.html:58
|
|
||||||
msgid "clothing cut"
|
msgid "clothing cut"
|
||||||
msgstr "forma de ropa"
|
msgstr "forma de ropa"
|
||||||
|
|
||||||
#: apps/wei/models.py:228 apps/wei/templates/wei/weimembership_form.html:61
|
#: apps/wei/models.py:226 apps/wei/templates/wei/weimembership_form.html:61
|
||||||
msgid "clothing size"
|
msgid "clothing size"
|
||||||
msgstr "medida de ropa"
|
msgstr "medida de ropa"
|
||||||
|
|
||||||
#: apps/wei/models.py:234 apps/wei/templates/wei/attribute_bus_1A.html:28
|
#: apps/wei/models.py:232 apps/wei/templates/wei/attribute_bus_1A.html:28
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:67
|
#: apps/wei/templates/wei/weimembership_form.html:67
|
||||||
msgid "health issues"
|
msgid "health issues"
|
||||||
msgstr "problemas de salud"
|
msgstr "problemas de salud"
|
||||||
|
|
||||||
#: apps/wei/models.py:239 apps/wei/templates/wei/weimembership_form.html:70
|
#: apps/wei/models.py:237 apps/wei/templates/wei/weimembership_form.html:70
|
||||||
msgid "emergency contact name"
|
msgid "emergency contact name"
|
||||||
msgstr "nombre del contacto de emergencia"
|
msgstr "nombre del contacto de emergencia"
|
||||||
|
|
||||||
#: apps/wei/models.py:240
|
#: apps/wei/models.py:242 apps/wei/templates/wei/weimembership_form.html:73
|
||||||
msgid "The emergency contact must not be a WEI participant"
|
|
||||||
msgstr "El contacto de emergencia no debe ser un participante de WEI"
|
|
||||||
|
|
||||||
#: apps/wei/models.py:245 apps/wei/templates/wei/weimembership_form.html:73
|
|
||||||
msgid "emergency contact phone"
|
msgid "emergency contact phone"
|
||||||
msgstr "teléfono del contacto de emergencia"
|
msgstr "teléfono del contacto de emergencia"
|
||||||
|
|
||||||
#: apps/wei/models.py:250 apps/wei/templates/wei/weimembership_form.html:52
|
#: apps/wei/models.py:247 apps/wei/templates/wei/weimembership_form.html:52
|
||||||
msgid "first year"
|
msgid "first year"
|
||||||
msgstr "primer año"
|
msgstr "primer año"
|
||||||
|
|
||||||
#: apps/wei/models.py:251
|
#: apps/wei/models.py:248
|
||||||
msgid "Tells if the user is new in the school."
|
msgid "Tells if the user is new in the school."
|
||||||
msgstr "Indica si el usuario es nuevo en la escuela."
|
msgstr "Indica si el usuario es nuevo en la escuela."
|
||||||
|
|
||||||
#: apps/wei/models.py:256
|
#: apps/wei/models.py:253
|
||||||
msgid "registration information"
|
msgid "registration information"
|
||||||
msgstr "informaciones sobre la afiliación"
|
msgstr "informaciones sobre la afiliación"
|
||||||
|
|
||||||
#: apps/wei/models.py:257
|
#: apps/wei/models.py:254
|
||||||
msgid ""
|
msgid ""
|
||||||
"Information about the registration (buses for old members, survey for the "
|
"Information about the registration (buses for old members, survey for the "
|
||||||
"new members), encoded in JSON"
|
"new members), encoded in JSON"
|
||||||
@ -2736,27 +2740,27 @@ msgstr ""
|
|||||||
"Informaciones sobre la afiliacion (bus para miembros ancianos, cuestionario "
|
"Informaciones sobre la afiliacion (bus para miembros ancianos, cuestionario "
|
||||||
"para los nuevos miembros), registrado en JSON"
|
"para los nuevos miembros), registrado en JSON"
|
||||||
|
|
||||||
#: apps/wei/models.py:315
|
#: apps/wei/models.py:312
|
||||||
msgid "WEI User"
|
msgid "WEI User"
|
||||||
msgstr "Participante WEI"
|
msgstr "Participante WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:316
|
#: apps/wei/models.py:313
|
||||||
msgid "WEI Users"
|
msgid "WEI Users"
|
||||||
msgstr "Participantes WEI"
|
msgstr "Participantes WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:336
|
#: apps/wei/models.py:333
|
||||||
msgid "team"
|
msgid "team"
|
||||||
msgstr "equipo"
|
msgstr "equipo"
|
||||||
|
|
||||||
#: apps/wei/models.py:346
|
#: apps/wei/models.py:343
|
||||||
msgid "WEI registration"
|
msgid "WEI registration"
|
||||||
msgstr "Apuntación al WEI"
|
msgstr "Apuntación al WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:350
|
#: apps/wei/models.py:347
|
||||||
msgid "WEI membership"
|
msgid "WEI membership"
|
||||||
msgstr "Afiliación al WEI"
|
msgstr "Afiliación al WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:351
|
#: apps/wei/models.py:348
|
||||||
msgid "WEI memberships"
|
msgid "WEI memberships"
|
||||||
msgstr "Afiliaciones al WEI"
|
msgstr "Afiliaciones al WEI"
|
||||||
|
|
||||||
@ -3312,10 +3316,6 @@ msgstr "Contactarnos"
|
|||||||
msgid "Technical Support"
|
msgid "Technical Support"
|
||||||
msgstr "Soporte técnico"
|
msgstr "Soporte técnico"
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:198
|
|
||||||
msgid "FAQ (FR)"
|
|
||||||
msgstr "FAQ (FR)"
|
|
||||||
|
|
||||||
#: note_kfet/templates/base_search.html:15
|
#: note_kfet/templates/base_search.html:15
|
||||||
msgid "Search by attribute such as name…"
|
msgid "Search by attribute such as name…"
|
||||||
msgstr "Buscar con atributo, como el nombre…"
|
msgstr "Buscar con atributo, como el nombre…"
|
||||||
@ -3537,21 +3537,6 @@ msgstr ""
|
|||||||
"pagar su afiliación. Tambien tiene que validar su correo electronico con el "
|
"pagar su afiliación. Tambien tiene que validar su correo electronico con el "
|
||||||
"enlace que recibió."
|
"enlace que recibió."
|
||||||
|
|
||||||
#~ msgid ""
|
|
||||||
#~ "I declare that I opened or I will open soon a bank account in the Société "
|
|
||||||
#~ "générale with the BDE partnership."
|
|
||||||
#~ msgstr ""
|
|
||||||
#~ "Declaro que ya abrió una cuenta a la Société Générale en colaboración con "
|
|
||||||
#~ "el BDE."
|
|
||||||
|
|
||||||
#~ msgid ""
|
|
||||||
#~ "Warning: this engages you to open your bank account. If you finally "
|
|
||||||
#~ "decides to don't open your account, you will have to pay the BDE "
|
|
||||||
#~ "membership."
|
|
||||||
#~ msgstr ""
|
|
||||||
#~ "Cuidado : esto le obliga abrir su cuenta bancaria. Si cambia de idea y no "
|
|
||||||
#~ "abre su cuenta bancaria, tendrá que pagar su afiliación al BDE."
|
|
||||||
|
|
||||||
#~ msgid "You are not a Kfet member, so you can't use your note account."
|
#~ msgid "You are not a Kfet member, so you can't use your note account."
|
||||||
#~ msgstr "Usted no es un miembro de la Kfet, no puede usar su cuenta note."
|
#~ msgstr "Usted no es un miembro de la Kfet, no puede usar su cuenta note."
|
||||||
|
|
||||||
|
@ -7,9 +7,9 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: \n"
|
"Project-Id-Version: \n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2023-07-09 14:46+0200\n"
|
"POT-Creation-Date: 2022-08-18 22:51+0200\n"
|
||||||
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
|
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
|
||||||
"Last-Translator: bleizi <bleizi@crans.org>\n"
|
"Last-Translator: elkmaennchen <elkmaennchen@crans.org>\n"
|
||||||
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
|
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
|
||||||
"Language: fr\n"
|
"Language: fr\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
@ -60,6 +60,7 @@ msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité."
|
|||||||
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:301
|
#: apps/note/models/transactions.py:46 apps/note/models/transactions.py:301
|
||||||
#: apps/permission/models.py:330
|
#: apps/permission/models.py:330
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:16
|
#: apps/registration/templates/registration/future_profile_detail.html:16
|
||||||
|
#: apps/sheets/models.py:16 apps/sheets/models.py:84 apps/sheets/models.py:121
|
||||||
#: apps/wei/models.py:67 apps/wei/models.py:131 apps/wei/tables.py:282
|
#: apps/wei/models.py:67 apps/wei/models.py:131 apps/wei/tables.py:282
|
||||||
#: apps/wei/templates/wei/base.html:26
|
#: apps/wei/templates/wei/base.html:26
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:14
|
#: apps/wei/templates/wei/weimembership_form.html:14
|
||||||
@ -95,7 +96,8 @@ msgstr "types d'activité"
|
|||||||
#: apps/activity/models.py:68
|
#: apps/activity/models.py:68
|
||||||
#: apps/activity/templates/activity/includes/activity_info.html:19
|
#: apps/activity/templates/activity/includes/activity_info.html:19
|
||||||
#: apps/note/models/transactions.py:81 apps/permission/models.py:110
|
#: apps/note/models/transactions.py:81 apps/permission/models.py:110
|
||||||
#: apps/permission/models.py:189 apps/wei/models.py:78 apps/wei/models.py:142
|
#: apps/permission/models.py:189 apps/sheets/models.py:25 apps/wei/models.py:78
|
||||||
|
#: apps/wei/models.py:142
|
||||||
msgid "description"
|
msgid "description"
|
||||||
msgstr "description"
|
msgstr "description"
|
||||||
|
|
||||||
@ -115,7 +117,7 @@ msgid "type"
|
|||||||
msgstr "type"
|
msgstr "type"
|
||||||
|
|
||||||
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
|
#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:307
|
||||||
#: apps/note/models/notes.py:148 apps/treasury/models.py:287
|
#: apps/note/models/notes.py:148 apps/treasury/models.py:285
|
||||||
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
#: apps/wei/models.py:173 apps/wei/templates/wei/attribute_bus_1A.html:13
|
||||||
#: apps/wei/templates/wei/survey.html:15
|
#: apps/wei/templates/wei/survey.html:15
|
||||||
msgid "user"
|
msgid "user"
|
||||||
@ -143,6 +145,7 @@ msgstr ""
|
|||||||
|
|
||||||
#: apps/activity/models.py:109
|
#: apps/activity/models.py:109
|
||||||
#: apps/activity/templates/activity/includes/activity_info.html:25
|
#: apps/activity/templates/activity/includes/activity_info.html:25
|
||||||
|
#: apps/sheets/models.py:20
|
||||||
msgid "start date"
|
msgid "start date"
|
||||||
msgstr "date de début"
|
msgstr "date de début"
|
||||||
|
|
||||||
@ -171,7 +174,7 @@ msgid "entry time"
|
|||||||
msgstr "heure d'entrée"
|
msgstr "heure d'entrée"
|
||||||
|
|
||||||
#: apps/activity/models.py:178 apps/note/apps.py:14
|
#: apps/activity/models.py:178 apps/note/apps.py:14
|
||||||
#: apps/note/models/notes.py:77
|
#: apps/note/models/notes.py:77 apps/sheets/models.py:157
|
||||||
msgid "note"
|
msgid "note"
|
||||||
msgstr "note"
|
msgstr "note"
|
||||||
|
|
||||||
@ -258,19 +261,19 @@ msgstr "Entré le "
|
|||||||
msgid "remove"
|
msgid "remove"
|
||||||
msgstr "supprimer"
|
msgstr "supprimer"
|
||||||
|
|
||||||
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:201
|
#: apps/activity/tables.py:82 apps/note/forms.py:68 apps/treasury/models.py:199
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr "Type"
|
msgstr "Type"
|
||||||
|
|
||||||
#: apps/activity/tables.py:84 apps/member/forms.py:186
|
#: apps/activity/tables.py:84 apps/member/forms.py:186
|
||||||
#: apps/registration/forms.py:92 apps/treasury/forms.py:131
|
#: apps/registration/forms.py:91 apps/treasury/forms.py:131
|
||||||
#: apps/wei/forms/registration.py:104
|
#: apps/wei/forms/registration.py:104
|
||||||
msgid "Last name"
|
msgid "Last name"
|
||||||
msgstr "Nom de famille"
|
msgstr "Nom de famille"
|
||||||
|
|
||||||
#: apps/activity/tables.py:86 apps/member/forms.py:191
|
#: apps/activity/tables.py:86 apps/member/forms.py:191
|
||||||
#: apps/note/templates/note/transaction_form.html:138
|
#: apps/note/templates/note/transaction_form.html:138
|
||||||
#: apps/registration/forms.py:97 apps/treasury/forms.py:133
|
#: apps/registration/forms.py:96 apps/treasury/forms.py:133
|
||||||
#: apps/wei/forms/registration.py:109
|
#: apps/wei/forms/registration.py:109
|
||||||
msgid "First name"
|
msgid "First name"
|
||||||
msgstr "Prénom"
|
msgstr "Prénom"
|
||||||
@ -334,7 +337,10 @@ msgstr "Entrée effectuée !"
|
|||||||
#: apps/member/templates/member/add_members.html:46
|
#: apps/member/templates/member/add_members.html:46
|
||||||
#: apps/member/templates/member/club_form.html:16
|
#: apps/member/templates/member/club_form.html:16
|
||||||
#: apps/note/templates/note/transactiontemplate_form.html:18
|
#: apps/note/templates/note/transactiontemplate_form.html:18
|
||||||
#: apps/treasury/forms.py:89 apps/treasury/forms.py:143
|
#: apps/sheets/templates/sheets/food_form.html:51
|
||||||
|
#: apps/sheets/templates/sheets/meal_form.html:17
|
||||||
|
#: apps/sheets/templates/sheets/sheet_form.html:17 apps/treasury/forms.py:89
|
||||||
|
#: apps/treasury/forms.py:143
|
||||||
#: apps/treasury/templates/treasury/invoice_form.html:74
|
#: apps/treasury/templates/treasury/invoice_form.html:74
|
||||||
#: apps/wei/templates/wei/bus_form.html:17
|
#: apps/wei/templates/wei/bus_form.html:17
|
||||||
#: apps/wei/templates/wei/busteam_form.html:17
|
#: apps/wei/templates/wei/busteam_form.html:17
|
||||||
@ -548,24 +554,24 @@ msgid "This image cannot be loaded."
|
|||||||
msgstr "Cette image ne peut pas être chargée."
|
msgstr "Cette image ne peut pas être chargée."
|
||||||
|
|
||||||
#: apps/member/forms.py:141 apps/member/views.py:103
|
#: apps/member/forms.py:141 apps/member/views.py:103
|
||||||
#: apps/registration/forms.py:34 apps/registration/views.py:266
|
#: apps/registration/forms.py:33 apps/registration/views.py:262
|
||||||
msgid "An alias with a similar name already exists."
|
msgid "An alias with a similar name already exists."
|
||||||
msgstr "Un alias avec un nom similaire existe déjà."
|
msgstr "Un alias avec un nom similaire existe déjà."
|
||||||
|
|
||||||
#: apps/member/forms.py:165
|
#: apps/member/forms.py:165 apps/registration/forms.py:71
|
||||||
msgid "Inscription paid by Société Générale"
|
msgid "Inscription paid by Société Générale"
|
||||||
msgstr "Inscription payée par la Société générale"
|
msgstr "Inscription payée par la Société générale"
|
||||||
|
|
||||||
#: apps/member/forms.py:167
|
#: apps/member/forms.py:167 apps/registration/forms.py:73
|
||||||
msgid "Check this case if the Société Générale paid the inscription."
|
msgid "Check this case if the Société Générale paid the inscription."
|
||||||
msgstr "Cochez cette case si la Société Générale a payé l'inscription."
|
msgstr "Cochez cette case si la Société Générale a payé l'inscription."
|
||||||
|
|
||||||
#: apps/member/forms.py:172 apps/registration/forms.py:79
|
#: apps/member/forms.py:172 apps/registration/forms.py:78
|
||||||
#: apps/wei/forms/registration.py:91
|
#: apps/wei/forms/registration.py:91
|
||||||
msgid "Credit type"
|
msgid "Credit type"
|
||||||
msgstr "Type de rechargement"
|
msgstr "Type de rechargement"
|
||||||
|
|
||||||
#: apps/member/forms.py:173 apps/registration/forms.py:80
|
#: apps/member/forms.py:173 apps/registration/forms.py:79
|
||||||
#: apps/wei/forms/registration.py:92
|
#: apps/wei/forms/registration.py:92
|
||||||
msgid "No credit"
|
msgid "No credit"
|
||||||
msgstr "Pas de rechargement"
|
msgstr "Pas de rechargement"
|
||||||
@ -574,13 +580,13 @@ msgstr "Pas de rechargement"
|
|||||||
msgid "You can credit the note of the user."
|
msgid "You can credit the note of the user."
|
||||||
msgstr "Vous pouvez créditer la note de l'utilisateur avant l'adhésion."
|
msgstr "Vous pouvez créditer la note de l'utilisateur avant l'adhésion."
|
||||||
|
|
||||||
#: apps/member/forms.py:179 apps/registration/forms.py:85
|
#: apps/member/forms.py:179 apps/registration/forms.py:84
|
||||||
#: apps/wei/forms/registration.py:97
|
#: apps/wei/forms/registration.py:97
|
||||||
msgid "Credit amount"
|
msgid "Credit amount"
|
||||||
msgstr "Montant à créditer"
|
msgstr "Montant à créditer"
|
||||||
|
|
||||||
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:144
|
#: apps/member/forms.py:196 apps/note/templates/note/transaction_form.html:144
|
||||||
#: apps/registration/forms.py:102 apps/treasury/forms.py:135
|
#: apps/registration/forms.py:101 apps/treasury/forms.py:135
|
||||||
#: apps/wei/forms/registration.py:114
|
#: apps/wei/forms/registration.py:114
|
||||||
msgid "Bank"
|
msgid "Bank"
|
||||||
msgstr "Banque"
|
msgstr "Banque"
|
||||||
@ -1183,7 +1189,7 @@ msgstr "Sauvegarder les changements"
|
|||||||
msgid "Registrations"
|
msgid "Registrations"
|
||||||
msgstr "Inscriptions"
|
msgstr "Inscriptions"
|
||||||
|
|
||||||
#: apps/member/views.py:73 apps/registration/forms.py:24
|
#: apps/member/views.py:73 apps/registration/forms.py:23
|
||||||
msgid "This address must be valid."
|
msgid "This address must be valid."
|
||||||
msgstr "Cette adresse doit être valide."
|
msgstr "Cette adresse doit être valide."
|
||||||
|
|
||||||
@ -1239,11 +1245,11 @@ msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}."
|
|||||||
msgid "The membership must begin before {:%m-%d-%Y}."
|
msgid "The membership must begin before {:%m-%d-%Y}."
|
||||||
msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}."
|
msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}."
|
||||||
|
|
||||||
#: apps/member/views.py:880
|
#: apps/member/views.py:876
|
||||||
msgid "Manage roles of an user in the club"
|
msgid "Manage roles of an user in the club"
|
||||||
msgstr "Gérer les rôles d'un utilisateur dans le club"
|
msgstr "Gérer les rôles d'un utilisateur dans le club"
|
||||||
|
|
||||||
#: apps/member/views.py:905
|
#: apps/member/views.py:901
|
||||||
msgid "Members of the club"
|
msgid "Members of the club"
|
||||||
msgstr "Membres du club"
|
msgstr "Membres du club"
|
||||||
|
|
||||||
@ -1470,7 +1476,7 @@ msgstr "modèles de transaction"
|
|||||||
msgid "used alias"
|
msgid "used alias"
|
||||||
msgstr "alias utilisé"
|
msgstr "alias utilisé"
|
||||||
|
|
||||||
#: apps/note/models/transactions.py:136
|
#: apps/note/models/transactions.py:136 apps/sheets/views.py:277
|
||||||
msgid "quantity"
|
msgid "quantity"
|
||||||
msgstr "quantité"
|
msgstr "quantité"
|
||||||
|
|
||||||
@ -1563,7 +1569,7 @@ msgstr "Transactions de crédit/retrait"
|
|||||||
msgid "membership transaction"
|
msgid "membership transaction"
|
||||||
msgstr "transaction d'adhésion"
|
msgstr "transaction d'adhésion"
|
||||||
|
|
||||||
#: apps/note/models/transactions.py:385 apps/treasury/models.py:294
|
#: apps/note/models/transactions.py:385 apps/treasury/models.py:292
|
||||||
msgid "membership transactions"
|
msgid "membership transactions"
|
||||||
msgstr "transactions d'adhésion"
|
msgstr "transactions d'adhésion"
|
||||||
|
|
||||||
@ -1592,8 +1598,10 @@ msgid "Delete"
|
|||||||
msgstr "Supprimer"
|
msgstr "Supprimer"
|
||||||
|
|
||||||
#: apps/note/tables.py:222 apps/note/templates/note/conso_form.html:132
|
#: apps/note/tables.py:222 apps/note/templates/note/conso_form.html:132
|
||||||
#: apps/wei/tables.py:49 apps/wei/tables.py:50
|
#: apps/sheets/templates/sheets/sheet_detail.html:21
|
||||||
#: apps/wei/templates/wei/base.html:89
|
#: apps/sheets/templates/sheets/sheet_detail.html:36
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:52 apps/wei/tables.py:49
|
||||||
|
#: apps/wei/tables.py:50 apps/wei/templates/wei/base.html:89
|
||||||
#: apps/wei/templates/wei/bus_detail.html:20
|
#: apps/wei/templates/wei/bus_detail.html:20
|
||||||
#: apps/wei/templates/wei/busteam_detail.html:20
|
#: apps/wei/templates/wei/busteam_detail.html:20
|
||||||
#: apps/wei/templates/wei/busteam_detail.html:40
|
#: apps/wei/templates/wei/busteam_detail.html:40
|
||||||
@ -1682,7 +1690,7 @@ msgid "Amount"
|
|||||||
msgstr "Montant"
|
msgstr "Montant"
|
||||||
|
|
||||||
#: apps/note/templates/note/transaction_form.html:132
|
#: apps/note/templates/note/transaction_form.html:132
|
||||||
#: apps/treasury/models.py:56
|
#: apps/treasury/models.py:54
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Nom"
|
msgstr "Nom"
|
||||||
|
|
||||||
@ -1746,6 +1754,98 @@ msgstr "Bouton affiché"
|
|||||||
msgid "An error occured"
|
msgid "An error occured"
|
||||||
msgstr "Une erreur s'est produite"
|
msgstr "Une erreur s'est produite"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list.html:14 apps/sheets/models.py:244
|
||||||
|
msgid "queued"
|
||||||
|
msgstr "en attente"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list.html:27
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:67
|
||||||
|
msgid "There is no queued order."
|
||||||
|
msgstr "Il n'y a pas de commande en attente."
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list.html:35 apps/sheets/models.py:245
|
||||||
|
msgid "ready"
|
||||||
|
msgstr "prêt"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list.html:43
|
||||||
|
msgid "There is no ready order."
|
||||||
|
msgstr "Il n'y a pas de commande prête."
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list.html:53
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:75
|
||||||
|
msgid "Other waiting lists:"
|
||||||
|
msgstr "Autres listes d'attente :"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list.html:66
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:93
|
||||||
|
msgid "Queued orders"
|
||||||
|
msgstr "Commandes en attente"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list.html:69
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:98
|
||||||
|
msgid "Ready orders"
|
||||||
|
msgstr "Commandes prêtes"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list.html:72
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:115
|
||||||
|
msgid "Back to note sheet detail"
|
||||||
|
msgstr "Retour aux détails de la feuille de note"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:18
|
||||||
|
#: apps/sheets/models.py:161
|
||||||
|
msgid "date"
|
||||||
|
msgstr "date"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:22
|
||||||
|
msgid "order number"
|
||||||
|
msgstr "nombre de commandes"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:27
|
||||||
|
#: apps/sheets/models.py:229 apps/sheets/views.py:249 apps/sheets/views.py:295
|
||||||
|
msgid "priority request"
|
||||||
|
msgstr "demande de priorité"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:32
|
||||||
|
#: apps/sheets/models.py:222 apps/sheets/views.py:243 apps/sheets/views.py:289
|
||||||
|
msgid "remark"
|
||||||
|
msgstr "remarques"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:37
|
||||||
|
#: apps/sheets/models.py:216
|
||||||
|
msgid "options"
|
||||||
|
msgstr "options"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:45
|
||||||
|
msgid "Mark as ready"
|
||||||
|
msgstr "Marquer comme prêt"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:50
|
||||||
|
msgid "Mark as served"
|
||||||
|
msgstr "Marquer comme servi"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:55
|
||||||
|
msgid "Re-queue"
|
||||||
|
msgstr "Remettre en attente"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:60
|
||||||
|
#: note_kfet/templates/oauth2_provider/application_confirm_delete.html:17
|
||||||
|
#: note_kfet/templates/oauth2_provider/authorize.html:28
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr "Annuler"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:103
|
||||||
|
msgid "Served orders"
|
||||||
|
msgstr "Commandes servies"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:108
|
||||||
|
msgid "Canceled orders"
|
||||||
|
msgstr "Commandes annulées"
|
||||||
|
|
||||||
|
#: apps/note/templates/sheets/waiting_list_detail.html:112
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:47 apps/sheets/views.py:416
|
||||||
|
msgid "Waiting list"
|
||||||
|
msgstr "Liste d'attente"
|
||||||
|
|
||||||
#: apps/note/views.py:36
|
#: apps/note/views.py:36
|
||||||
msgid "Transfer money"
|
msgid "Transfer money"
|
||||||
msgstr "Transférer de l'argent"
|
msgstr "Transférer de l'argent"
|
||||||
@ -1972,15 +2072,31 @@ msgstr "Tous les droits"
|
|||||||
msgid "registration"
|
msgid "registration"
|
||||||
msgstr "inscription"
|
msgstr "inscription"
|
||||||
|
|
||||||
#: apps/registration/forms.py:40
|
#: apps/registration/forms.py:39
|
||||||
msgid "This email address is already used."
|
msgid "This email address is already used."
|
||||||
msgstr "Cet email est déjà pris."
|
msgstr "Cet email est déjà pris."
|
||||||
|
|
||||||
#: apps/registration/forms.py:60
|
#: apps/registration/forms.py:49
|
||||||
|
msgid ""
|
||||||
|
"I declare that I opened or I will open soon a bank account in the Société "
|
||||||
|
"générale with the BDE partnership."
|
||||||
|
msgstr ""
|
||||||
|
"Je déclare avoir ouvert ou ouvrir prochainement un compte à la société "
|
||||||
|
"générale avec le partenariat du BDE."
|
||||||
|
|
||||||
|
#: apps/registration/forms.py:51
|
||||||
|
msgid ""
|
||||||
|
"Warning: this engages you to open your bank account. If you finally decides "
|
||||||
|
"to don't open your account, you will have to pay the BDE membership."
|
||||||
|
msgstr ""
|
||||||
|
"Attention : cocher cette case vous engage à ouvrir votre compte. Si vous "
|
||||||
|
"décidez de ne pas le faire, vous devrez payer l'adhésion au BDE."
|
||||||
|
|
||||||
|
#: apps/registration/forms.py:59
|
||||||
msgid "Register to the WEI"
|
msgid "Register to the WEI"
|
||||||
msgstr "S'inscrire au WEI"
|
msgstr "S'inscrire au WEI"
|
||||||
|
|
||||||
#: apps/registration/forms.py:62
|
#: apps/registration/forms.py:61
|
||||||
msgid ""
|
msgid ""
|
||||||
"Check this case if you want to register to the WEI. If you hesitate, you "
|
"Check this case if you want to register to the WEI. If you hesitate, you "
|
||||||
"will be able to register later, after validating your account in the Kfet."
|
"will be able to register later, after validating your account in the Kfet."
|
||||||
@ -1989,18 +2105,14 @@ msgstr ""
|
|||||||
"pourrez toujours vous inscrire plus tard, après avoir validé votre compte à "
|
"pourrez toujours vous inscrire plus tard, après avoir validé votre compte à "
|
||||||
"la Kfet."
|
"la Kfet."
|
||||||
|
|
||||||
#: apps/registration/forms.py:107
|
#: apps/registration/forms.py:106
|
||||||
msgid "Join BDE Club"
|
msgid "Join BDE Club"
|
||||||
msgstr "Adhérer au club BDE"
|
msgstr "Adhérer au club BDE"
|
||||||
|
|
||||||
#: apps/registration/forms.py:114
|
#: apps/registration/forms.py:113
|
||||||
msgid "Join Kfet Club"
|
msgid "Join Kfet Club"
|
||||||
msgstr "Adhérer au club Kfet"
|
msgstr "Adhérer au club Kfet"
|
||||||
|
|
||||||
#: apps/registration/forms.py:123
|
|
||||||
msgid "Join BDA Club"
|
|
||||||
msgstr "Adhérer au club BDA"
|
|
||||||
|
|
||||||
#: apps/registration/templates/registration/email_validation_complete.html:15
|
#: apps/registration/templates/registration/email_validation_complete.html:15
|
||||||
msgid "Your email have successfully been validated."
|
msgid "Your email have successfully been validated."
|
||||||
msgstr "Votre adresse e-mail a bien été validée."
|
msgstr "Votre adresse e-mail a bien été validée."
|
||||||
@ -2049,12 +2161,12 @@ msgstr "Supprimer l'inscription"
|
|||||||
msgid "Validate account"
|
msgid "Validate account"
|
||||||
msgstr "Valider le compte"
|
msgstr "Valider le compte"
|
||||||
|
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:63
|
#: apps/registration/templates/registration/future_profile_detail.html:62
|
||||||
msgid ""
|
msgid ""
|
||||||
"The user declared that he/she opened a bank account in the Société générale."
|
"The user declared that he/she opened a bank account in the Société générale."
|
||||||
msgstr "L'utilisateur a déclaré avoir ouvert un compte à la société générale."
|
msgstr "L'utilisateur a déclaré avoir ouvert un compte à la société générale."
|
||||||
|
|
||||||
#: apps/registration/templates/registration/future_profile_detail.html:73
|
#: apps/registration/templates/registration/future_profile_detail.html:71
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:127
|
#: apps/wei/templates/wei/weimembership_form.html:127
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:186
|
#: apps/wei/templates/wei/weimembership_form.html:186
|
||||||
msgid "Validate registration"
|
msgid "Validate registration"
|
||||||
@ -2104,62 +2216,293 @@ msgstr "Merci"
|
|||||||
msgid "The Note Kfet team."
|
msgid "The Note Kfet team."
|
||||||
msgstr "L'équipe de la Note Kfet."
|
msgstr "L'équipe de la Note Kfet."
|
||||||
|
|
||||||
#: apps/registration/views.py:41
|
#: apps/registration/views.py:40
|
||||||
msgid "Register new user"
|
msgid "Register new user"
|
||||||
msgstr "Enregistrer un nouvel utilisateur"
|
msgstr "Enregistrer un nouvel utilisateur"
|
||||||
|
|
||||||
#: apps/registration/views.py:99
|
#: apps/registration/views.py:98
|
||||||
msgid "Email validation"
|
msgid "Email validation"
|
||||||
msgstr "Validation de l'adresse mail"
|
msgstr "Validation de l'adresse mail"
|
||||||
|
|
||||||
#: apps/registration/views.py:101
|
#: apps/registration/views.py:100
|
||||||
msgid "Validate email"
|
msgid "Validate email"
|
||||||
msgstr "Valider l'adresse e-mail"
|
msgstr "Valider l'adresse e-mail"
|
||||||
|
|
||||||
#: apps/registration/views.py:145
|
#: apps/registration/views.py:144
|
||||||
msgid "Email validation unsuccessful"
|
msgid "Email validation unsuccessful"
|
||||||
msgstr "La validation de l'adresse mail a échoué"
|
msgstr "La validation de l'adresse mail a échoué"
|
||||||
|
|
||||||
#: apps/registration/views.py:156
|
#: apps/registration/views.py:155
|
||||||
msgid "Email validation email sent"
|
msgid "Email validation email sent"
|
||||||
msgstr "L'email de vérification de l'adresse email a bien été envoyé"
|
msgstr "L'email de vérification de l'adresse email a bien été envoyé"
|
||||||
|
|
||||||
#: apps/registration/views.py:164
|
#: apps/registration/views.py:163
|
||||||
msgid "Resend email validation link"
|
msgid "Resend email validation link"
|
||||||
msgstr "Renvoyer le lien de validation"
|
msgstr "Renvoyer le lien de validation"
|
||||||
|
|
||||||
#: apps/registration/views.py:182
|
#: apps/registration/views.py:181
|
||||||
msgid "Pre-registered users list"
|
msgid "Pre-registered users list"
|
||||||
msgstr "Liste des utilisateurs en attente d'inscription"
|
msgstr "Liste des utilisateurs en attente d'inscription"
|
||||||
|
|
||||||
#: apps/registration/views.py:206
|
#: apps/registration/views.py:205
|
||||||
msgid "Unregistered users"
|
msgid "Unregistered users"
|
||||||
msgstr "Utilisateurs en attente d'inscription"
|
msgstr "Utilisateurs en attente d'inscription"
|
||||||
|
|
||||||
#: apps/registration/views.py:219
|
#: apps/registration/views.py:218
|
||||||
msgid "Registration detail"
|
msgid "Registration detail"
|
||||||
msgstr "Détails de l'inscription"
|
msgstr "Détails de l'inscription"
|
||||||
|
|
||||||
#: apps/registration/views.py:293
|
#: apps/registration/views.py:282
|
||||||
msgid "You must join the BDE."
|
msgid "You must join the BDE."
|
||||||
msgstr "Vous devez adhérer au BDE."
|
msgstr "Vous devez adhérer au BDE."
|
||||||
|
|
||||||
#: apps/registration/views.py:323
|
#: apps/registration/views.py:306
|
||||||
msgid ""
|
msgid ""
|
||||||
"The entered amount is not enough for the memberships, should be at least {}"
|
"The entered amount is not enough for the memberships, should be at least {}"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Le montant crédité est trop faible pour adhérer, il doit être au minimum de "
|
"Le montant crédité est trop faible pour adhérer, il doit être au minimum de "
|
||||||
"{}"
|
"{}"
|
||||||
|
|
||||||
#: apps/registration/views.py:417
|
#: apps/registration/views.py:387
|
||||||
msgid "Invalidate pre-registration"
|
msgid "Invalidate pre-registration"
|
||||||
msgstr "Invalider l'inscription"
|
msgstr "Invalider l'inscription"
|
||||||
|
|
||||||
|
#: apps/sheets/apps.py:10 apps/sheets/models.py:42
|
||||||
|
msgid "note sheets"
|
||||||
|
msgstr "feuilles de notes"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:30
|
||||||
|
msgid "visible"
|
||||||
|
msgstr "visible"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:31
|
||||||
|
msgid "the note sheet will be private until this field is checked."
|
||||||
|
msgstr "la feuille de note restera privée tant que ce champ n'est pas coché."
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:41 apps/sheets/models.py:54 apps/sheets/models.py:116
|
||||||
|
#: apps/sheets/models.py:151 apps/sheets/models.py:273
|
||||||
|
msgid "note sheet"
|
||||||
|
msgstr "feuille de note"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:48 apps/sheets/models.py:77 apps/sheets/models.py:78
|
||||||
|
#: apps/sheets/models.py:90 apps/sheets/models.py:210
|
||||||
|
msgid "food"
|
||||||
|
msgstr "nourriture"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:58 apps/sheets/models.py:130
|
||||||
|
msgid "price"
|
||||||
|
msgstr "prix"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:64
|
||||||
|
msgid "destination club"
|
||||||
|
msgstr "club de destination"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:69 apps/sheets/models.py:100 apps/sheets/models.py:135
|
||||||
|
msgid "available"
|
||||||
|
msgstr "disponible"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:70 apps/sheets/models.py:101 apps/sheets/models.py:136
|
||||||
|
msgid "If set to false, this option won't be offered (in case of out of stock)"
|
||||||
|
msgstr ""
|
||||||
|
"Si mis à faux, cette option ne sera pas présentée (en cas de rupture de "
|
||||||
|
"stock)"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:95
|
||||||
|
msgid "extra cost"
|
||||||
|
msgstr "surcoût"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:108
|
||||||
|
msgid "food option"
|
||||||
|
msgstr "option de nourriture"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:109
|
||||||
|
msgid "food options"
|
||||||
|
msgstr "options de nourriture"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:126
|
||||||
|
msgid "content"
|
||||||
|
msgstr "contenu"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:140 apps/sheets/models.py:143
|
||||||
|
#: apps/sheets/models.py:180
|
||||||
|
msgid "meal"
|
||||||
|
msgstr "menu"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:144
|
||||||
|
msgid "meals"
|
||||||
|
msgstr "menus"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:166 apps/sheets/models.py:174
|
||||||
|
#: apps/sheets/models.py:196
|
||||||
|
msgid "order"
|
||||||
|
msgstr "commande"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:167
|
||||||
|
msgid "orders"
|
||||||
|
msgstr "commandes"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:184 apps/sheets/models.py:233 apps/sheets/views.py:235
|
||||||
|
#: apps/sheets/views.py:281
|
||||||
|
msgid "gift"
|
||||||
|
msgstr "don"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:188 apps/sheets/models.py:204
|
||||||
|
msgid "ordered meal"
|
||||||
|
msgstr "menu commandé"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:189
|
||||||
|
msgid "ordered meals"
|
||||||
|
msgstr "menus commandés"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:237
|
||||||
|
msgid "number"
|
||||||
|
msgstr "numéro"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:238
|
||||||
|
msgid "How many times the user ordered this."
|
||||||
|
msgstr "Combien de fois cet⋅te utilisateur⋅rice a commandé ceci."
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:246
|
||||||
|
msgid "served"
|
||||||
|
msgstr "servi"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:247
|
||||||
|
msgid "canceled"
|
||||||
|
msgstr "annulé"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:250
|
||||||
|
msgid "status"
|
||||||
|
msgstr "statut"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:256
|
||||||
|
msgid "served date"
|
||||||
|
msgstr "date de service"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:260 apps/sheets/models.py:261
|
||||||
|
#: apps/sheets/models.py:268
|
||||||
|
msgid "ordered food"
|
||||||
|
msgstr "nourriture commandée"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:288
|
||||||
|
msgid "sheet order transaction"
|
||||||
|
msgstr "transaction de commande sur feuille de note"
|
||||||
|
|
||||||
|
#: apps/sheets/models.py:289
|
||||||
|
msgid "sheet order transactions"
|
||||||
|
msgstr "transactions de commande sur feuille de note"
|
||||||
|
|
||||||
|
#: apps/sheets/templates/sheets/food_form.html:48
|
||||||
|
msgid "Add option"
|
||||||
|
msgstr "Ajouter une option"
|
||||||
|
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:28
|
||||||
|
msgid "menu"
|
||||||
|
msgstr "menu"
|
||||||
|
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:31
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:43
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:58
|
||||||
|
msgid "This product is unavailable."
|
||||||
|
msgstr "Ce produit est indisponible."
|
||||||
|
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:67
|
||||||
|
msgid "The menu is empty for now."
|
||||||
|
msgstr "Le menu est vide pour le moment."
|
||||||
|
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:74
|
||||||
|
msgid "Add new food"
|
||||||
|
msgstr "Ajouter un plat"
|
||||||
|
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:77
|
||||||
|
msgid "Add new meal"
|
||||||
|
msgstr "Ajouter un menu"
|
||||||
|
|
||||||
|
#: apps/sheets/templates/sheets/sheet_detail.html:83 apps/sheets/views.py:206
|
||||||
|
#: apps/sheets/views.py:319
|
||||||
|
msgid "Order now"
|
||||||
|
msgstr "Commander maintenant"
|
||||||
|
|
||||||
|
#: apps/sheets/templates/sheets/sheet_list.html:14
|
||||||
|
msgid "Create a sheet"
|
||||||
|
msgstr "Créer une feuille de note"
|
||||||
|
|
||||||
|
#: apps/sheets/templates/sheets/sheet_list.html:22
|
||||||
|
msgid "Note sheet listing"
|
||||||
|
msgstr "Liste des feuilles de notes"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:33
|
||||||
|
msgid "Search note sheet"
|
||||||
|
msgstr "Chercher une feuille de note"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:48
|
||||||
|
msgid "Create note sheet"
|
||||||
|
msgstr "Créer une feuille de note"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:61
|
||||||
|
msgid "Update note sheet"
|
||||||
|
msgstr "Modifier une feuille de note"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:84
|
||||||
|
msgid "Create new food"
|
||||||
|
msgstr "Créer un plat"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:130
|
||||||
|
msgid "Update food"
|
||||||
|
msgstr "Modifier un plat"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:167
|
||||||
|
msgid "Create new meal"
|
||||||
|
msgstr "Créer un menu"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:192
|
||||||
|
msgid "Update meal"
|
||||||
|
msgstr "Modifier un menu"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:217
|
||||||
|
msgid "Orderer"
|
||||||
|
msgstr "Commanditaire"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:223
|
||||||
|
msgid "Who orders"
|
||||||
|
msgstr "Qui commande"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:231 apps/treasury/models.py:140
|
||||||
|
msgid "Quantity"
|
||||||
|
msgstr "Quantité"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:238 apps/sheets/views.py:284
|
||||||
|
msgid "Be careful: this gift will be multiplied for each order."
|
||||||
|
msgstr "Attention : ce don sera multiplié pour chaque commande."
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:244 apps/sheets/views.py:290
|
||||||
|
msgid "Allergies,…"
|
||||||
|
msgstr "Allergies,…"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:250 apps/sheets/views.py:296
|
||||||
|
msgid "Lesson at 13h30,…"
|
||||||
|
msgstr "Cours à 13h30,…"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:261
|
||||||
|
msgid "Options for "
|
||||||
|
msgstr "Options pour "
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:306
|
||||||
|
msgid "Options"
|
||||||
|
msgstr "Options"
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:403
|
||||||
|
msgid "You didn't select anything."
|
||||||
|
msgstr "Vous n'avez rien sélectionné."
|
||||||
|
|
||||||
|
#: apps/sheets/views.py:432
|
||||||
|
msgid "Detailed waiting list"
|
||||||
|
msgstr "Liste d'attente détaillée"
|
||||||
|
|
||||||
#: apps/treasury/apps.py:12 note_kfet/templates/base.html:96
|
#: apps/treasury/apps.py:12 note_kfet/templates/base.html:96
|
||||||
msgid "Treasury"
|
msgid "Treasury"
|
||||||
msgstr "Trésorerie"
|
msgstr "Trésorerie"
|
||||||
|
|
||||||
#: apps/treasury/forms.py:26 apps/treasury/models.py:95
|
#: apps/treasury/forms.py:26 apps/treasury/models.py:93
|
||||||
#: apps/treasury/templates/treasury/invoice_form.html:22
|
#: apps/treasury/templates/treasury/invoice_form.html:22
|
||||||
msgid "This invoice is locked and can no longer be edited."
|
msgid "This invoice is locked and can no longer be edited."
|
||||||
msgstr "Cette facture est verrouillée et ne peut plus être éditée."
|
msgstr "Cette facture est verrouillée et ne peut plus être éditée."
|
||||||
@ -2172,7 +2515,7 @@ msgstr "La remise est déjà fermée."
|
|||||||
msgid "You can't change the type of the remittance."
|
msgid "You can't change the type of the remittance."
|
||||||
msgstr "Vous ne pouvez pas changer le type de la remise."
|
msgstr "Vous ne pouvez pas changer le type de la remise."
|
||||||
|
|
||||||
#: apps/treasury/forms.py:125 apps/treasury/models.py:269
|
#: apps/treasury/forms.py:125 apps/treasury/models.py:267
|
||||||
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
|
#: apps/treasury/tables.py:97 apps/treasury/tables.py:105
|
||||||
#: apps/treasury/templates/treasury/invoice_list.html:16
|
#: apps/treasury/templates/treasury/invoice_list.html:16
|
||||||
#: apps/treasury/templates/treasury/remittance_list.html:16
|
#: apps/treasury/templates/treasury/remittance_list.html:16
|
||||||
@ -2188,116 +2531,112 @@ msgstr "Pas de remise associée"
|
|||||||
msgid "Invoice identifier"
|
msgid "Invoice identifier"
|
||||||
msgstr "Numéro de facture"
|
msgstr "Numéro de facture"
|
||||||
|
|
||||||
#: apps/treasury/models.py:42
|
#: apps/treasury/models.py:40
|
||||||
msgid "BDE"
|
msgid "BDE"
|
||||||
msgstr "BDE"
|
msgstr "BDE"
|
||||||
|
|
||||||
#: apps/treasury/models.py:47
|
#: apps/treasury/models.py:45
|
||||||
msgid "Object"
|
msgid "Object"
|
||||||
msgstr "Objet"
|
msgstr "Objet"
|
||||||
|
|
||||||
#: apps/treasury/models.py:51
|
#: apps/treasury/models.py:49
|
||||||
msgid "Description"
|
msgid "Description"
|
||||||
msgstr "Description"
|
msgstr "Description"
|
||||||
|
|
||||||
#: apps/treasury/models.py:60
|
#: apps/treasury/models.py:58
|
||||||
msgid "Address"
|
msgid "Address"
|
||||||
msgstr "Adresse"
|
msgstr "Adresse"
|
||||||
|
|
||||||
#: apps/treasury/models.py:65 apps/treasury/models.py:195
|
#: apps/treasury/models.py:63 apps/treasury/models.py:193
|
||||||
msgid "Date"
|
msgid "Date"
|
||||||
msgstr "Date"
|
msgstr "Date"
|
||||||
|
|
||||||
#: apps/treasury/models.py:69
|
#: apps/treasury/models.py:67
|
||||||
msgid "Acquitted"
|
msgid "Acquitted"
|
||||||
msgstr "Acquittée"
|
msgstr "Acquittée"
|
||||||
|
|
||||||
#: apps/treasury/models.py:74
|
#: apps/treasury/models.py:72
|
||||||
msgid "Locked"
|
msgid "Locked"
|
||||||
msgstr "Verrouillée"
|
msgstr "Verrouillée"
|
||||||
|
|
||||||
#: apps/treasury/models.py:75
|
#: apps/treasury/models.py:73
|
||||||
msgid "An invoice can't be edited when it is locked."
|
msgid "An invoice can't be edited when it is locked."
|
||||||
msgstr "Une facture ne peut plus être modifiée si elle est verrouillée."
|
msgstr "Une facture ne peut plus être modifiée si elle est verrouillée."
|
||||||
|
|
||||||
#: apps/treasury/models.py:81
|
#: apps/treasury/models.py:79
|
||||||
msgid "tex source"
|
msgid "tex source"
|
||||||
msgstr "fichier TeX source"
|
msgstr "fichier TeX source"
|
||||||
|
|
||||||
#: apps/treasury/models.py:115 apps/treasury/models.py:131
|
#: apps/treasury/models.py:113 apps/treasury/models.py:129
|
||||||
msgid "invoice"
|
msgid "invoice"
|
||||||
msgstr "facture"
|
msgstr "facture"
|
||||||
|
|
||||||
#: apps/treasury/models.py:116
|
#: apps/treasury/models.py:114
|
||||||
msgid "invoices"
|
msgid "invoices"
|
||||||
msgstr "factures"
|
msgstr "factures"
|
||||||
|
|
||||||
#: apps/treasury/models.py:119
|
#: apps/treasury/models.py:117
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Invoice #{id}"
|
msgid "Invoice #{id}"
|
||||||
msgstr "Facture n°{id}"
|
msgstr "Facture n°{id}"
|
||||||
|
|
||||||
#: apps/treasury/models.py:136
|
#: apps/treasury/models.py:134
|
||||||
msgid "Designation"
|
msgid "Designation"
|
||||||
msgstr "Désignation"
|
msgstr "Désignation"
|
||||||
|
|
||||||
#: apps/treasury/models.py:142
|
#: apps/treasury/models.py:145
|
||||||
msgid "Quantity"
|
|
||||||
msgstr "Quantité"
|
|
||||||
|
|
||||||
#: apps/treasury/models.py:147
|
|
||||||
msgid "Unit price"
|
msgid "Unit price"
|
||||||
msgstr "Prix unitaire"
|
msgstr "Prix unitaire"
|
||||||
|
|
||||||
#: apps/treasury/models.py:163
|
#: apps/treasury/models.py:161
|
||||||
msgid "product"
|
msgid "product"
|
||||||
msgstr "produit"
|
msgstr "produit"
|
||||||
|
|
||||||
#: apps/treasury/models.py:164
|
#: apps/treasury/models.py:162
|
||||||
msgid "products"
|
msgid "products"
|
||||||
msgstr "produits"
|
msgstr "produits"
|
||||||
|
|
||||||
#: apps/treasury/models.py:184
|
#: apps/treasury/models.py:182
|
||||||
msgid "remittance type"
|
msgid "remittance type"
|
||||||
msgstr "type de remise"
|
msgstr "type de remise"
|
||||||
|
|
||||||
#: apps/treasury/models.py:185
|
#: apps/treasury/models.py:183
|
||||||
msgid "remittance types"
|
msgid "remittance types"
|
||||||
msgstr "types de remises"
|
msgstr "types de remises"
|
||||||
|
|
||||||
#: apps/treasury/models.py:206
|
#: apps/treasury/models.py:204
|
||||||
msgid "Comment"
|
msgid "Comment"
|
||||||
msgstr "Commentaire"
|
msgstr "Commentaire"
|
||||||
|
|
||||||
#: apps/treasury/models.py:211
|
#: apps/treasury/models.py:209
|
||||||
msgid "Closed"
|
msgid "Closed"
|
||||||
msgstr "Fermée"
|
msgstr "Fermée"
|
||||||
|
|
||||||
#: apps/treasury/models.py:215
|
#: apps/treasury/models.py:213
|
||||||
msgid "remittance"
|
msgid "remittance"
|
||||||
msgstr "remise"
|
msgstr "remise"
|
||||||
|
|
||||||
#: apps/treasury/models.py:216
|
#: apps/treasury/models.py:214
|
||||||
msgid "remittances"
|
msgid "remittances"
|
||||||
msgstr "remises"
|
msgstr "remises"
|
||||||
|
|
||||||
#: apps/treasury/models.py:249
|
#: apps/treasury/models.py:247
|
||||||
msgid "Remittance #{:d}: {}"
|
msgid "Remittance #{:d}: {}"
|
||||||
msgstr "Remise n°{:d} : {}"
|
msgstr "Remise n°{:d} : {}"
|
||||||
|
|
||||||
#: apps/treasury/models.py:273
|
#: apps/treasury/models.py:271
|
||||||
msgid "special transaction proxy"
|
msgid "special transaction proxy"
|
||||||
msgstr "proxy de transaction spéciale"
|
msgstr "proxy de transaction spéciale"
|
||||||
|
|
||||||
#: apps/treasury/models.py:274
|
#: apps/treasury/models.py:272
|
||||||
msgid "special transaction proxies"
|
msgid "special transaction proxies"
|
||||||
msgstr "proxys de transactions spéciales"
|
msgstr "proxys de transactions spéciales"
|
||||||
|
|
||||||
#: apps/treasury/models.py:300
|
#: apps/treasury/models.py:298
|
||||||
msgid "credit transaction"
|
msgid "credit transaction"
|
||||||
msgstr "transaction de crédit"
|
msgstr "transaction de crédit"
|
||||||
|
|
||||||
#: apps/treasury/models.py:432
|
#: apps/treasury/models.py:430
|
||||||
msgid ""
|
msgid ""
|
||||||
"This user doesn't have enough money to pay the memberships with its note. "
|
"This user doesn't have enough money to pay the memberships with its note. "
|
||||||
"Please ask her/him to credit the note before invalidating this credit."
|
"Please ask her/him to credit the note before invalidating this credit."
|
||||||
@ -2305,16 +2644,16 @@ msgstr ""
|
|||||||
"Cet utilisateur n'a pas assez d'argent pour payer les adhésions avec sa "
|
"Cet utilisateur n'a pas assez d'argent pour payer les adhésions avec sa "
|
||||||
"note. Merci de lui demander de recharger sa note avant d'invalider ce crédit."
|
"note. Merci de lui demander de recharger sa note avant d'invalider ce crédit."
|
||||||
|
|
||||||
#: apps/treasury/models.py:452
|
#: apps/treasury/models.py:451
|
||||||
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
|
#: apps/treasury/templates/treasury/sogecredit_detail.html:10
|
||||||
msgid "Credit from the Société générale"
|
msgid "Credit from the Société générale"
|
||||||
msgstr "Crédit de la Société générale"
|
msgstr "Crédit de la Société générale"
|
||||||
|
|
||||||
#: apps/treasury/models.py:453
|
#: apps/treasury/models.py:452
|
||||||
msgid "Credits from the Société générale"
|
msgid "Credits from the Société générale"
|
||||||
msgstr "Crédits de la Société générale"
|
msgstr "Crédits de la Société générale"
|
||||||
|
|
||||||
#: apps/treasury/models.py:456
|
#: apps/treasury/models.py:455
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Soge credit for {user}"
|
msgid "Soge credit for {user}"
|
||||||
msgstr "Crédit de la société générale pour l'utilisateur {user}"
|
msgstr "Crédit de la société générale pour l'utilisateur {user}"
|
||||||
@ -2574,7 +2913,7 @@ msgstr ""
|
|||||||
"compte"
|
"compte"
|
||||||
|
|
||||||
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
|
#: apps/wei/forms/registration.py:59 apps/wei/models.py:126
|
||||||
#: apps/wei/models.py:326
|
#: apps/wei/models.py:323
|
||||||
msgid "bus"
|
msgid "bus"
|
||||||
msgstr "bus"
|
msgstr "bus"
|
||||||
|
|
||||||
@ -2612,7 +2951,7 @@ msgstr "Sélectionnez les rôles qui vous intéressent."
|
|||||||
msgid "This team doesn't belong to the given bus."
|
msgid "This team doesn't belong to the given bus."
|
||||||
msgstr "Cette équipe n'appartient pas à ce bus."
|
msgstr "Cette équipe n'appartient pas à ce bus."
|
||||||
|
|
||||||
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:38
|
#: apps/wei/forms/surveys/wei2021.py:35 apps/wei/forms/surveys/wei2022.py:35
|
||||||
msgid "Choose a word:"
|
msgid "Choose a word:"
|
||||||
msgstr "Choisissez un mot :"
|
msgstr "Choisissez un mot :"
|
||||||
|
|
||||||
@ -2699,48 +3038,40 @@ msgstr "Non-binaire"
|
|||||||
msgid "gender"
|
msgid "gender"
|
||||||
msgstr "genre"
|
msgstr "genre"
|
||||||
|
|
||||||
#: apps/wei/models.py:212
|
#: apps/wei/models.py:213 apps/wei/templates/wei/weimembership_form.html:58
|
||||||
msgid "Unisex"
|
|
||||||
msgstr "Unisexe"
|
|
||||||
|
|
||||||
#: apps/wei/models.py:215 apps/wei/templates/wei/weimembership_form.html:58
|
|
||||||
msgid "clothing cut"
|
msgid "clothing cut"
|
||||||
msgstr "coupe de vêtement"
|
msgstr "coupe de vêtement"
|
||||||
|
|
||||||
#: apps/wei/models.py:228 apps/wei/templates/wei/weimembership_form.html:61
|
#: apps/wei/models.py:226 apps/wei/templates/wei/weimembership_form.html:61
|
||||||
msgid "clothing size"
|
msgid "clothing size"
|
||||||
msgstr "taille de vêtement"
|
msgstr "taille de vêtement"
|
||||||
|
|
||||||
#: apps/wei/models.py:234 apps/wei/templates/wei/attribute_bus_1A.html:28
|
#: apps/wei/models.py:232 apps/wei/templates/wei/attribute_bus_1A.html:28
|
||||||
#: apps/wei/templates/wei/weimembership_form.html:67
|
#: apps/wei/templates/wei/weimembership_form.html:67
|
||||||
msgid "health issues"
|
msgid "health issues"
|
||||||
msgstr "problèmes de santé"
|
msgstr "problèmes de santé"
|
||||||
|
|
||||||
#: apps/wei/models.py:239 apps/wei/templates/wei/weimembership_form.html:70
|
#: apps/wei/models.py:237 apps/wei/templates/wei/weimembership_form.html:70
|
||||||
msgid "emergency contact name"
|
msgid "emergency contact name"
|
||||||
msgstr "nom du contact en cas d'urgence"
|
msgstr "nom du contact en cas d'urgence"
|
||||||
|
|
||||||
#: apps/wei/models.py:240
|
#: apps/wei/models.py:242 apps/wei/templates/wei/weimembership_form.html:73
|
||||||
msgid "The emergency contact must not be a WEI participant"
|
|
||||||
msgstr "Le contact en cas d'urgence ne doit pas être une personne qui participe au WEI"
|
|
||||||
|
|
||||||
#: apps/wei/models.py:245 apps/wei/templates/wei/weimembership_form.html:73
|
|
||||||
msgid "emergency contact phone"
|
msgid "emergency contact phone"
|
||||||
msgstr "téléphone du contact en cas d'urgence"
|
msgstr "téléphone du contact en cas d'urgence"
|
||||||
|
|
||||||
#: apps/wei/models.py:250 apps/wei/templates/wei/weimembership_form.html:52
|
#: apps/wei/models.py:247 apps/wei/templates/wei/weimembership_form.html:52
|
||||||
msgid "first year"
|
msgid "first year"
|
||||||
msgstr "première année"
|
msgstr "première année"
|
||||||
|
|
||||||
#: apps/wei/models.py:251
|
#: apps/wei/models.py:248
|
||||||
msgid "Tells if the user is new in the school."
|
msgid "Tells if the user is new in the school."
|
||||||
msgstr "Indique si l'utilisateur est nouveau dans l'école."
|
msgstr "Indique si l'utilisateur est nouveau dans l'école."
|
||||||
|
|
||||||
#: apps/wei/models.py:256
|
#: apps/wei/models.py:253
|
||||||
msgid "registration information"
|
msgid "registration information"
|
||||||
msgstr "informations sur l'inscription"
|
msgstr "informations sur l'inscription"
|
||||||
|
|
||||||
#: apps/wei/models.py:257
|
#: apps/wei/models.py:254
|
||||||
msgid ""
|
msgid ""
|
||||||
"Information about the registration (buses for old members, survey for the "
|
"Information about the registration (buses for old members, survey for the "
|
||||||
"new members), encoded in JSON"
|
"new members), encoded in JSON"
|
||||||
@ -2748,27 +3079,27 @@ msgstr ""
|
|||||||
"Informations sur l'inscription (bus pour les 2A+, questionnaire pour les "
|
"Informations sur l'inscription (bus pour les 2A+, questionnaire pour les "
|
||||||
"1A), encodées en JSON"
|
"1A), encodées en JSON"
|
||||||
|
|
||||||
#: apps/wei/models.py:315
|
#: apps/wei/models.py:312
|
||||||
msgid "WEI User"
|
msgid "WEI User"
|
||||||
msgstr "Participant au WEI"
|
msgstr "Participant au WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:316
|
#: apps/wei/models.py:313
|
||||||
msgid "WEI Users"
|
msgid "WEI Users"
|
||||||
msgstr "Participants au WEI"
|
msgstr "Participants au WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:336
|
#: apps/wei/models.py:333
|
||||||
msgid "team"
|
msgid "team"
|
||||||
msgstr "équipe"
|
msgstr "équipe"
|
||||||
|
|
||||||
#: apps/wei/models.py:346
|
#: apps/wei/models.py:343
|
||||||
msgid "WEI registration"
|
msgid "WEI registration"
|
||||||
msgstr "Inscription au WEI"
|
msgstr "Inscription au WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:350
|
#: apps/wei/models.py:347
|
||||||
msgid "WEI membership"
|
msgid "WEI membership"
|
||||||
msgstr "Adhésion au WEI"
|
msgstr "Adhésion au WEI"
|
||||||
|
|
||||||
#: apps/wei/models.py:351
|
#: apps/wei/models.py:348
|
||||||
msgid "WEI memberships"
|
msgid "WEI memberships"
|
||||||
msgstr "Adhésions au WEI"
|
msgstr "Adhésions au WEI"
|
||||||
|
|
||||||
@ -3181,19 +3512,19 @@ msgstr "Répartir les 1A dans les bus"
|
|||||||
msgid "Attribute bus"
|
msgid "Attribute bus"
|
||||||
msgstr "Attribuer un bus"
|
msgstr "Attribuer un bus"
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:172
|
#: note_kfet/settings/base.py:173
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr "Allemand"
|
msgstr "Allemand"
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:173
|
#: note_kfet/settings/base.py:174
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr "Anglais"
|
msgstr "Anglais"
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:174
|
#: note_kfet/settings/base.py:175
|
||||||
msgid "Spanish"
|
msgid "Spanish"
|
||||||
msgstr "Espagnol"
|
msgstr "Espagnol"
|
||||||
|
|
||||||
#: note_kfet/settings/base.py:175
|
#: note_kfet/settings/base.py:176
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr "Français"
|
msgstr "Français"
|
||||||
|
|
||||||
@ -3331,10 +3662,6 @@ msgstr "Nous contacter"
|
|||||||
msgid "Technical Support"
|
msgid "Technical Support"
|
||||||
msgstr "Support technique"
|
msgstr "Support technique"
|
||||||
|
|
||||||
#: note_kfet/templates/base.html:198
|
|
||||||
msgid "FAQ (FR)"
|
|
||||||
msgstr "FAQ (FR)"
|
|
||||||
|
|
||||||
#: note_kfet/templates/base_search.html:15
|
#: note_kfet/templates/base_search.html:15
|
||||||
msgid "Search by attribute such as name…"
|
msgid "Search by attribute such as name…"
|
||||||
msgstr "Chercher par un attribut tel que le nom …"
|
msgstr "Chercher par un attribut tel que le nom …"
|
||||||
@ -3347,11 +3674,6 @@ msgstr "Il n'y a pas de résultat."
|
|||||||
msgid "Are you sure to delete the application"
|
msgid "Are you sure to delete the application"
|
||||||
msgstr "Êtes-vous sûr⋅e de vouloir supprimer l'application"
|
msgstr "Êtes-vous sûr⋅e de vouloir supprimer l'application"
|
||||||
|
|
||||||
#: note_kfet/templates/oauth2_provider/application_confirm_delete.html:17
|
|
||||||
#: note_kfet/templates/oauth2_provider/authorize.html:28
|
|
||||||
msgid "Cancel"
|
|
||||||
msgstr "Annuler"
|
|
||||||
|
|
||||||
#: note_kfet/templates/oauth2_provider/application_detail.html:11
|
#: note_kfet/templates/oauth2_provider/application_detail.html:11
|
||||||
msgid "Client id"
|
msgid "Client id"
|
||||||
msgstr "ID client"
|
msgstr "ID client"
|
||||||
@ -3563,17 +3885,5 @@ msgstr ""
|
|||||||
"d'adhésion. Vous devez également valider votre adresse email en suivant le "
|
"d'adhésion. Vous devez également valider votre adresse email en suivant le "
|
||||||
"lien que vous avez reçu."
|
"lien que vous avez reçu."
|
||||||
|
|
||||||
#~ msgid ""
|
#~ msgid "Detailed view"
|
||||||
#~ "I declare that I opened or I will open soon a bank account in the Société "
|
#~ msgstr "Vue détaillée"
|
||||||
#~ "générale with the BDE partnership."
|
|
||||||
#~ msgstr ""
|
|
||||||
#~ "Je déclare avoir ouvert ou ouvrir prochainement un compte à la société "
|
|
||||||
#~ "générale avec le partenariat du BDE."
|
|
||||||
|
|
||||||
#~ msgid ""
|
|
||||||
#~ "Warning: this engages you to open your bank account. If you finally "
|
|
||||||
#~ "decides to don't open your account, you will have to pay the BDE "
|
|
||||||
#~ "membership."
|
|
||||||
#~ msgstr ""
|
|
||||||
#~ "Attention : cocher cette case vous engage à ouvrir votre compte. Si vous "
|
|
||||||
#~ "décidez de ne pas le faire, vous devrez payer l'adhésion au BDE."
|
|
||||||
|
@ -18,7 +18,7 @@ MAILTO=notekfet2020@lists.crans.org
|
|||||||
# Spammer les gens en négatif
|
# Spammer les gens en négatif
|
||||||
00 5 * * 2 root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --spam --negative-amount 1 -v 0
|
00 5 * * 2 root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --spam --negative-amount 1 -v 0
|
||||||
# Envoyer le rapport mensuel aux trésoriers et respos info
|
# Envoyer le rapport mensuel aux trésoriers et respos info
|
||||||
00 8 * * 5 root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --report --add-years 1 -v 0
|
00 8 6 * * root cd /var/www/note_kfet && env/bin/python manage.py send_mail_to_negative_balances --report --add-years 1 -v 0
|
||||||
# Envoyer les rapports aux gens
|
# Envoyer les rapports aux gens
|
||||||
55 6 * * * root cd /var/www/note_kfet && env/bin/python manage.py send_reports -v 0
|
55 6 * * * root cd /var/www/note_kfet && env/bin/python manage.py send_reports -v 0
|
||||||
# Mettre à jour les boutons mis en avant
|
# Mettre à jour les boutons mis en avant
|
||||||
|
@ -75,6 +75,7 @@ INSTALLED_APPS = [
|
|||||||
'permission',
|
'permission',
|
||||||
'registration',
|
'registration',
|
||||||
'scripts',
|
'scripts',
|
||||||
|
'sheets',
|
||||||
'treasury',
|
'treasury',
|
||||||
'wei',
|
'wei',
|
||||||
]
|
]
|
||||||
@ -252,7 +253,7 @@ REST_FRAMEWORK = {
|
|||||||
'rest_framework.authentication.TokenAuthentication',
|
'rest_framework.authentication.TokenAuthentication',
|
||||||
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
|
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
|
||||||
],
|
],
|
||||||
'DEFAULT_PAGINATION_CLASS': 'apps.api.pagination.CustomPagination',
|
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
|
||||||
'PAGE_SIZE': 20,
|
'PAGE_SIZE': 20,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
79
note_kfet/static/css/custom.css
Executable file → Normal file
79
note_kfet/static/css/custom.css
Executable file → Normal file
@ -65,10 +65,7 @@ mark {
|
|||||||
|
|
||||||
/* Last BDE colors */
|
/* Last BDE colors */
|
||||||
.bg-primary {
|
.bg-primary {
|
||||||
/* background-color: rgb(18, 67, 4) !important; */
|
background-color: rgb(102, 83, 105) !important;
|
||||||
/* MODE VIEUXCON=ON */
|
|
||||||
/* background-color: rgb(166, 0, 2) !important; */
|
|
||||||
background-color: rgb(0, 0, 0) !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
@ -83,15 +80,15 @@ body {
|
|||||||
.btn-outline-primary:hover,
|
.btn-outline-primary:hover,
|
||||||
.btn-outline-primary:not(:disabled):not(.disabled).active,
|
.btn-outline-primary:not(:disabled):not(.disabled).active,
|
||||||
.btn-outline-primary:not(:disabled):not(.disabled):active {
|
.btn-outline-primary:not(:disabled):not(.disabled):active {
|
||||||
color: rgb(241, 229, 52);
|
color: #fff;
|
||||||
background-color: rgb(228, 35, 132);
|
background-color: rgb(102, 83, 105);
|
||||||
border-color: rgb(228, 35, 132);
|
border-color: rgb(102, 83, 105);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-outline-primary {
|
.btn-outline-primary {
|
||||||
color: #fff;
|
color: rgb(102, 83, 105);
|
||||||
background-color: #000;
|
background-color: rgba(248, 249, 250, 0.9);
|
||||||
border-color: #464647;
|
border-color: rgb(102, 83, 105);
|
||||||
}
|
}
|
||||||
|
|
||||||
.turbolinks-progress-bar {
|
.turbolinks-progress-bar {
|
||||||
@ -100,64 +97,40 @@ body {
|
|||||||
|
|
||||||
.btn-primary:hover,
|
.btn-primary:hover,
|
||||||
.btn-primary:not(:disabled):not(.disabled).active,
|
.btn-primary:not(:disabled):not(.disabled).active,
|
||||||
.btn-primary:not(:disabled):not(.disabled):active {
|
.btn-primary:not(:disabled):not(.disabled):active,
|
||||||
color: rgb(241, 229, 52);
|
a.badge-primary:hover,
|
||||||
background-color: rgb(228, 35, 132);
|
a.badge-primary:not(:disabled):not(.disabled).active,
|
||||||
border-color: rgb(228, 35, 132);
|
a.badge-primary:not(:disabled):not(.disabled):active {
|
||||||
|
color: #fff;
|
||||||
|
background-color: rgb(102, 83, 105);
|
||||||
|
border-color: rgb(102, 83, 105);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary, a.badge-primary {
|
||||||
color: #fff;
|
color: rgba(248, 249, 250, 0.9);
|
||||||
background-color: #000;
|
background-color: rgb(102, 83, 105);
|
||||||
border-color: #adb5bd;
|
border-color: rgb(102, 83, 105);
|
||||||
}
|
}
|
||||||
|
|
||||||
.border-primary {
|
.border-primary {
|
||||||
border-color: rgb(228, 35, 132) !important;
|
border-color: rgb(115, 15, 115) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-secondary {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #000;
|
|
||||||
border-color: #adb5bd;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-secondary:hover,
|
|
||||||
.btn-secondary:not(:disabled):not(.disabled).active,
|
|
||||||
.btn-secondary:not(:disabled):not(.disabled):active {
|
|
||||||
color: rgb(241, 229, 52);
|
|
||||||
background-color: rgb(228, 35, 132);
|
|
||||||
border-color: rgb(228, 35, 132);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.btn-outline-dark {
|
|
||||||
color: #343a40;
|
|
||||||
border-color: #343a40;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-outline-dark:hover,
|
|
||||||
.btn-outline-dark:not(:disabled):not(.disabled).active,
|
|
||||||
.btn-outline-dark:not(:disabled):not(.disabled):active {
|
|
||||||
color: rgb(241, 229, 52);
|
|
||||||
background-color: rgb(228, 35, 132);
|
|
||||||
border-color: rgb(228, 35, 132);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: rgb(228, 35, 132);
|
color: rgb(102, 83, 105);
|
||||||
}
|
}
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
color: rgb(228, 35, 132);
|
color: rgb(200, 30, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control:focus {
|
.form-control:focus {
|
||||||
box-shadow: 0 0 0 0.25rem rgb(228 35 132 / 50%);
|
box-shadow: 0 0 0 0.25rem rgba(200, 30, 200, 0.25);
|
||||||
border-color: rgb(228, 35, 132);
|
border-color: rgb(200, 30, 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-outline-primary.focus {
|
.btn-outline-primary.focus {
|
||||||
box-shadow: 0 0 0 0.25rem rgb(228 35 132 / 10%);
|
box-shadow: 0 0 0 0.25rem rgba(200, 30, 200, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -194,8 +194,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
class="text-muted">{% trans "Contact us" %}</a> —
|
class="text-muted">{% trans "Contact us" %}</a> —
|
||||||
<a href="mailto:{{ "SUPPORT_EMAIL" | getenv }}"
|
<a href="mailto:{{ "SUPPORT_EMAIL" | getenv }}"
|
||||||
class="text-muted">{% trans "Technical Support" %}</a> —
|
class="text-muted">{% trans "Technical Support" %}</a> —
|
||||||
<a href="https://note.crans.org/doc/faq/"
|
|
||||||
class="text-muted">{% trans "FAQ (FR)" %}</a> —
|
|
||||||
</span>
|
</span>
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<select title="language" name="language"
|
<select title="language" name="language"
|
||||||
|
@ -23,11 +23,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
{{ profile_form|crispy }}
|
{{ profile_form|crispy }}
|
||||||
{% comment "Soge not for membership (only WEI)" %} {{ soge_form|crispy }} {% endcomment %}
|
{{ soge_form|crispy }}
|
||||||
<button class="btn btn-success" type="submit">
|
<button class="btn btn-success" type="submit">
|
||||||
{% trans "Sign up" %}
|
{% trans "Sign up" %}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -21,6 +21,7 @@ urlpatterns = [
|
|||||||
path('activity/', include('activity.urls')),
|
path('activity/', include('activity.urls')),
|
||||||
path('treasury/', include('treasury.urls')),
|
path('treasury/', include('treasury.urls')),
|
||||||
path('wei/', include('wei.urls')),
|
path('wei/', include('wei.urls')),
|
||||||
|
path('sheets/', include('sheets.urls')),
|
||||||
|
|
||||||
# Include Django Contrib and Core routers
|
# Include Django Contrib and Core routers
|
||||||
path('i18n/', include('django.conf.urls.i18n')),
|
path('i18n/', include('django.conf.urls.i18n')),
|
||||||
|
Reference in New Issue
Block a user