mirror of https://gitlab.crans.org/bde/nk20
Merge branch 'ouvreureuse' into 'beta'
Ouvreureuse See merge request bde/nk20!256
This commit is contained in:
commit
0dfef32c45
|
@ -1,9 +1,11 @@
|
|||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
from rest_framework.validators import UniqueTogetherValidator
|
||||
|
||||
from ..models import Activity, ActivityType, Entry, Guest, GuestTransaction
|
||||
from ..models import Activity, ActivityType, Entry, Guest, GuestTransaction, Opener
|
||||
|
||||
|
||||
class ActivityTypeSerializer(serializers.ModelSerializer):
|
||||
|
@ -59,3 +61,17 @@ class GuestTransactionSerializer(serializers.ModelSerializer):
|
|||
class Meta:
|
||||
model = GuestTransaction
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class OpenerSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
REST API Serializer for Openers.
|
||||
The djangorestframework plugin will analyse the model `Opener` and parse all fields in the API.
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = Opener
|
||||
fields = '__all__'
|
||||
validators = [UniqueTogetherValidator(
|
||||
queryset=Opener.objects.all(), fields=("opener", "activity"),
|
||||
message=_("This opener already exists"))]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from .views import ActivityTypeViewSet, ActivityViewSet, EntryViewSet, GuestViewSet
|
||||
from .views import ActivityTypeViewSet, ActivityViewSet, EntryViewSet, GuestViewSet, OpenerViewSet
|
||||
|
||||
|
||||
def register_activity_urls(router, path):
|
||||
|
@ -12,3 +12,4 @@ def register_activity_urls(router, path):
|
|||
router.register(path + '/type', ActivityTypeViewSet)
|
||||
router.register(path + '/guest', GuestViewSet)
|
||||
router.register(path + '/entry', EntryViewSet)
|
||||
router.register(path + '/opener', OpenerViewSet)
|
||||
|
|
|
@ -2,11 +2,14 @@
|
|||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from api.viewsets import ReadProtectedModelViewSet
|
||||
from django.core.exceptions import ValidationError
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.filters import SearchFilter
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
|
||||
from .serializers import ActivitySerializer, ActivityTypeSerializer, EntrySerializer, GuestSerializer
|
||||
from ..models import Activity, ActivityType, Entry, Guest
|
||||
from .serializers import ActivitySerializer, ActivityTypeSerializer, EntrySerializer, GuestSerializer, OpenerSerializer
|
||||
from ..models import Activity, ActivityType, Entry, Guest, Opener
|
||||
|
||||
|
||||
class ActivityTypeViewSet(ReadProtectedModelViewSet):
|
||||
|
@ -66,3 +69,32 @@ class EntryViewSet(ReadProtectedModelViewSet):
|
|||
filterset_fields = ['activity', 'time', 'note', 'guest', ]
|
||||
search_fields = ['$activity__name', '$note__user__email', '$note__alias__name', '$note__alias__normalized_name',
|
||||
'$guest__last_name', '$guest__first_name', ]
|
||||
|
||||
|
||||
class OpenerViewSet(ReadProtectedModelViewSet):
|
||||
"""
|
||||
REST Opener View set.
|
||||
The djangorestframework plugin will get all `Opener` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/activity/opener/
|
||||
"""
|
||||
queryset = Opener.objects
|
||||
serializer_class = OpenerSerializer
|
||||
filter_backends = [SearchFilter, DjangoFilterBackend]
|
||||
search_fields = ['$opener__alias__name', '$opener__alias__normalized_name',
|
||||
'$activity__name']
|
||||
filterset_fields = ['opener', 'opener__noteuser__user', 'activity']
|
||||
|
||||
def get_serializer_class(self):
|
||||
serializer_class = self.serializer_class
|
||||
if self.request.method in ['PUT', 'PATCH']:
|
||||
# opener-activity can't change
|
||||
serializer_class.Meta.read_only_fields = ('opener', 'acitivity',)
|
||||
return serializer_class
|
||||
|
||||
def destroy(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
try:
|
||||
self.perform_destroy(instance)
|
||||
except ValidationError as e:
|
||||
return Response({e.code: str(e)}, status.HTTP_400_BAD_REQUEST)
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
|
|
@ -43,7 +43,7 @@ class ActivityForm(forms.ModelForm):
|
|||
|
||||
class Meta:
|
||||
model = Activity
|
||||
exclude = ('creater', 'valid', 'open', )
|
||||
exclude = ('creater', 'valid', 'open', 'opener', )
|
||||
widgets = {
|
||||
"organizer": Autocomplete(
|
||||
model=Club,
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 2.2.28 on 2024-08-01 12:36
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('note', '0006_trust'),
|
||||
('activity', '0003_auto_20240323_1422'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Opener',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('activity', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='opener', to='activity.Activity', verbose_name='activity')),
|
||||
('opener', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='activity_responsible', to='note.Note', verbose_name='opener')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'opener',
|
||||
'verbose_name_plural': 'openers',
|
||||
'unique_together': {('opener', 'activity')},
|
||||
},
|
||||
),
|
||||
]
|
|
@ -11,7 +11,7 @@ from django.db import models, transaction
|
|||
from django.db.models import Q
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from note.models import NoteUser, Transaction
|
||||
from note.models import NoteUser, Transaction, Note
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
|
||||
|
@ -310,3 +310,31 @@ class GuestTransaction(Transaction):
|
|||
@property
|
||||
def type(self):
|
||||
return _('Invitation')
|
||||
|
||||
|
||||
class Opener(models.Model):
|
||||
"""
|
||||
Allow the user to make activity entries without more rights
|
||||
"""
|
||||
activity = models.ForeignKey(
|
||||
Activity,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='opener',
|
||||
verbose_name=_('activity')
|
||||
)
|
||||
|
||||
opener = models.ForeignKey(
|
||||
Note,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='activity_responsible',
|
||||
verbose_name=_('Opener')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("Opener")
|
||||
verbose_name_plural = _("Openers")
|
||||
unique_together = ("opener", "activity")
|
||||
|
||||
def __str__(self):
|
||||
return _("{opener} is opener of activity {acivity}").format(
|
||||
opener=str(self.opener), acivity=str(self.activity))
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* On form submit, create a new friendship
|
||||
*/
|
||||
function form_create_opener (e) {
|
||||
// Do not submit HTML form
|
||||
e.preventDefault()
|
||||
|
||||
// Get data and send to API
|
||||
const formData = new FormData(e.target)
|
||||
$.getJSON('/api/note/alias/'+formData.get('opener') + '/',
|
||||
function (opener_alias) {
|
||||
create_opener(formData.get('activity'), opener_alias.note)
|
||||
}).fail(function (xhr, _textStatus, _error) {
|
||||
errMsg(xhr.responseJSON)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a trust between users
|
||||
* @param trusting:Integer trusting note id
|
||||
* @param trusted:Integer trusted note id
|
||||
*/
|
||||
function create_opener(activity, opener) {
|
||||
$.post('/api/activity/opener/', {
|
||||
activity: activity,
|
||||
opener: opener,
|
||||
csrfmiddlewaretoken: CSRF_TOKEN
|
||||
}).done(function () {
|
||||
// Reload tables
|
||||
$('#opener_table').load(location.pathname + ' #opener_table')
|
||||
addMsg(gettext('Friendship successfully added'), 'success')
|
||||
}).fail(function (xhr, _textStatus, _error) {
|
||||
errMsg(xhr.responseJSON)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* On form submit, create a new friendship
|
||||
function create_opener (e) {
|
||||
// Do not submit HTML form
|
||||
e.preventDefault()
|
||||
|
||||
// Get data and send to API
|
||||
const formData = new FormData(e.target)
|
||||
$.post('/api/activity/opener/', {
|
||||
csrfmiddlewaretoken: formData.get('csrfmiddlewaretoken'),
|
||||
activity: formData.get('activity'),
|
||||
opener: formData.get('opener')
|
||||
}).done(function () {
|
||||
// Reload table
|
||||
$('#opener_table').load(location.pathname + ' #opener_table')
|
||||
addMsg(gettext('Alias successfully added'), 'success')
|
||||
}).fail(function (xhr, _textStatus, _error) {
|
||||
errMsg(xhr.responseJSON)
|
||||
})
|
||||
}*/
|
||||
|
||||
/**
|
||||
* On click of "delete", delete the trust
|
||||
* @param button_id:Integer Trust id to remove
|
||||
*/
|
||||
function delete_button (button_id) {
|
||||
$.ajax({
|
||||
url: '/api/activity/opener/' + button_id + '/',
|
||||
method: 'DELETE',
|
||||
headers: { 'X-CSRFTOKEN': CSRF_TOKEN }
|
||||
}).done(function () {
|
||||
addMsg(gettext('Friendship successfully deleted'), 'success')
|
||||
$('#opener_table').load(location.pathname + ' #opener_table')
|
||||
}).fail(function (xhr, _textStatus, _error) {
|
||||
errMsg(xhr.responseJSON)
|
||||
})
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
// Attach event
|
||||
document.getElementById('form_opener').addEventListener('submit', form_create_opener)
|
||||
})
|
|
@ -5,11 +5,13 @@ from django.utils import timezone
|
|||
from django.utils.html import escape
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from note_kfet.middlewares import get_current_request
|
||||
import django_tables2 as tables
|
||||
from django_tables2 import A
|
||||
from permission.backends import PermissionBackend
|
||||
from note.templatetags.pretty_money import pretty_money
|
||||
|
||||
from .models import Activity, Entry, Guest
|
||||
from .models import Activity, Entry, Guest, Opener
|
||||
|
||||
|
||||
class ActivityTable(tables.Table):
|
||||
|
@ -113,3 +115,34 @@ class EntryTable(tables.Table):
|
|||
'data-last-name': lambda record: record.last_name,
|
||||
'data-first-name': lambda record: record.first_name,
|
||||
}
|
||||
|
||||
|
||||
# function delete_button(id) provided in template file
|
||||
DELETE_TEMPLATE = """
|
||||
<button id="{{ record.pk }}" class="btn btn-danger btn-sm" onclick="delete_button(this.id)"> {{ delete_trans }}</button>
|
||||
"""
|
||||
|
||||
|
||||
class OpenerTable(tables.Table):
|
||||
class Meta:
|
||||
attrs = {
|
||||
'class': 'table table condensed table-striped',
|
||||
'id': "opener_table"
|
||||
}
|
||||
model = Opener
|
||||
fields = ("opener",)
|
||||
template_name = 'django_tables2/bootstrap4.html'
|
||||
|
||||
show_header = False
|
||||
opener = tables.Column(attrs={'td': {'class': 'text-center'}})
|
||||
|
||||
delete_col = tables.TemplateColumn(
|
||||
template_code=DELETE_TEMPLATE,
|
||||
extra_context={"delete_trans": _('Delete')},
|
||||
attrs={
|
||||
'td': {
|
||||
'class': lambda record: 'col-sm-1'
|
||||
+ (' d-none' if not PermissionBackend.check_perm(
|
||||
get_current_request(), "activity.delete_opener", record)
|
||||
else '')}},
|
||||
verbose_name=_("Delete"),)
|
||||
|
|
|
@ -4,11 +4,31 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
{% endcomment %}
|
||||
{% load i18n perms %}
|
||||
{% load render_table from django_tables2 %}
|
||||
{% load static django_tables2 i18n %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="text-white">{{ title }}</h1>
|
||||
{% include "activity/includes/activity_info.html" %}
|
||||
|
||||
{% if activity.activity_type.manage_entries and ".change__opener"|has_perm:activity %}
|
||||
<div class="card bg-white mb-3">
|
||||
<h3 class="card-header text-center">
|
||||
{% trans "Openers" %}
|
||||
</h3>
|
||||
<div class="card-body">
|
||||
<form class="input-group" method="POST" id="form_opener">
|
||||
{% csrf_token %}
|
||||
<input type="hidden" name="activity" value="{{ object.pk }}">
|
||||
{%include "autocomplete_model.html" %}
|
||||
<div class="input-group-append">
|
||||
<input type="submit" class="btn btn-success" value="{% trans "Add" %}">
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% render_table opener %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if guests.data %}
|
||||
<div class="card bg-white mb-3">
|
||||
<h3 class="card-header text-center">
|
||||
|
@ -22,6 +42,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
|||
{% endblock %}
|
||||
|
||||
{% block extrajavascript %}
|
||||
<script src="{% static "activity/js/opener.js" %}"></script>
|
||||
<script src="{% static "js/autocomplete_model.js" %}"></script>
|
||||
<script>
|
||||
function remove_guest(guest_id) {
|
||||
$.ajax({
|
||||
|
|
|
@ -24,8 +24,8 @@ from permission.backends import PermissionBackend
|
|||
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
|
||||
|
||||
from .forms import ActivityForm, GuestForm
|
||||
from .models import Activity, Entry, Guest
|
||||
from .tables import ActivityTable, EntryTable, GuestTable
|
||||
from .models import Activity, Entry, Guest, Opener
|
||||
from .tables import ActivityTable, EntryTable, GuestTable, OpenerTable
|
||||
|
||||
|
||||
class ActivityCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
|
@ -114,8 +114,24 @@ class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
|||
.filter(PermissionBackend.filter_queryset(self.request, Guest, "view")))
|
||||
context["guests"] = table
|
||||
|
||||
table = OpenerTable(data=self.object.opener.filter(activity=self.object)
|
||||
.filter(PermissionBackend.filter_queryset(self.request, Opener, "view")))
|
||||
context["opener"] = table
|
||||
|
||||
context["activity_started"] = timezone.now() > timezone.localtime(self.object.date_start)
|
||||
|
||||
context["widget"] = {
|
||||
"name": "opener",
|
||||
"resetable": True,
|
||||
"attrs": {
|
||||
"class": "autocomplete form-control",
|
||||
"id": "opener",
|
||||
"api_url": "/api/note/alias/?note__polymorphic_ctype__model=noteuser",
|
||||
"name_field": "name",
|
||||
"placeholder": ""
|
||||
}
|
||||
}
|
||||
|
||||
return context
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.2.28 on 2024-08-01 12:36
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('member', '0012_club_add_registration_form'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='profile',
|
||||
name='promotion',
|
||||
field=models.PositiveSmallIntegerField(default=2024, help_text='Year of entry to the school (None if not ENS student)', null=True, verbose_name='promotion'),
|
||||
),
|
||||
]
|
|
@ -18,6 +18,7 @@ def create_special_notes(apps, schema_editor):
|
|||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('note', '0001_initial'),
|
||||
('logs', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
|
|
|
@ -3111,6 +3111,199 @@
|
|||
"description": "Voir ceux nous ayant pour ami, pour toujours"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 199,
|
||||
"fields": {
|
||||
"model": [
|
||||
"activity",
|
||||
"activity"
|
||||
],
|
||||
"query": "{\"opener__in\": [\"user\", \"note\", \"activity_responsible\", [\"all\"]], \"open\": true, \"activity_type__manage_entries\":true}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Voir les activités ouvertes dont l'utilisateur⋅rice est ouvreur⋅se"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 200,
|
||||
"fields": {
|
||||
"model": [
|
||||
"activity",
|
||||
"activity"
|
||||
],
|
||||
"query": "{\"opener__in\": [\"user\", \"note\", \"activity_responsible\", [\"all\"]], \"open\": true, \"activity_type__manage_entries\":true}",
|
||||
"type": "change",
|
||||
"mask": 2,
|
||||
"field": "open",
|
||||
"permanent": false,
|
||||
"description": "Fermer les activités ouvertes dont l'utilisateur⋅rice est ouvreur⋅se"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 201,
|
||||
"fields": {
|
||||
"model": [
|
||||
"activity",
|
||||
"entry"
|
||||
],
|
||||
"query": "{\"activity__opener__in\": [\"user\", \"note\", \"activity_responsible\", [\"all\"]], \"activity__open\": true, \"activity__activity_type__manage_entries\":true}",
|
||||
"type": "add",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Faire les entrées des activités ouvertes dont l'utilisateur⋅rice est ouvreur⋅se"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 202,
|
||||
"fields": {
|
||||
"model": [
|
||||
"activity",
|
||||
"entry"
|
||||
],
|
||||
"query": "{\"activity__opener__in\": [\"user\", \"note\", \"activity_responsible\", [\"all\"]], \"activity__open\": true, \"activity__activity_type__manage_entries\":true}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Voir les entrées des activités ouvertes dont l'utilisateur⋅rice est ouvreur⋅se"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 203,
|
||||
"fields": {
|
||||
"model": [
|
||||
"activity",
|
||||
"guest"
|
||||
],
|
||||
"query": "{\"activity__pk__in\": [\"user\", \"note\", \"activity_responsible\", [\"all\"]], \"activity__open\": true, \"activity__activity_type__manage_entries\":true}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Voir les invité⋅es des activités ouvertes dont l'utilisateur⋅rice est ouvreur⋅se"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 204,
|
||||
"fields": {
|
||||
"model": [
|
||||
"activity",
|
||||
"guesttransaction"
|
||||
],
|
||||
"query": "[\"NOT\", {\"pk__isnull\": [\"user\", \"note\", \"activity_responsible\", [\"filter\", {\"activity__open\": true, \"activity__activity_type__manage_entries\":true}], [\"exists\"]]}]",
|
||||
"type": "add",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Créer une transaction d'invitation lorsque l'utilisateur⋅rice est ouvreur⋅se d'une activité ouverte"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
"model": "permission.permission",
|
||||
"pk": 205,
|
||||
"fields": {
|
||||
"model": [
|
||||
"note",
|
||||
"specialtransaction"
|
||||
],
|
||||
"query": "[\"NOT\", {\"pk__isnull\": [\"user\", \"note\", \"activity_responsible\", [\"filter\", {\"activity__open\": true, \"activity__activity_type__manage_entries\":true}], [\"exists\"]]}]",
|
||||
"type": "add",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Créer un crédit ou un retrait quelconque lorsque l'utilisateur⋅rice est ouvreur⋅se d'une activité ouverte"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 206,
|
||||
"fields": {
|
||||
"model": [
|
||||
"note",
|
||||
"notespecial"
|
||||
],
|
||||
"query": "[\"NOT\", {\"pk__isnull\": [\"user\", \"note\", \"activity_responsible\", [\"filter\", {\"activity__open\": true, \"activity__activity_type__manage_entries\":true}], [\"exists\"]]}]",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Afficher l'interface crédit/retrait lorsque l'utilisateur⋅rice est ouvreur⋅se d'une activité ouverte"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 207,
|
||||
"fields": {
|
||||
"model": [
|
||||
"activity",
|
||||
"opener"
|
||||
],
|
||||
"query": "{}",
|
||||
"type": "view",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Voir les ouvreur⋅ses des activités"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 208,
|
||||
"fields": {
|
||||
"model": [
|
||||
"activity",
|
||||
"opener"
|
||||
],
|
||||
"query": "{}",
|
||||
"type": "add",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Ajouter des ouvreur⋅ses aux activités"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 209,
|
||||
"fields": {
|
||||
"model": [
|
||||
"activity",
|
||||
"opener"
|
||||
],
|
||||
"query": "{}",
|
||||
"type": "delete",
|
||||
"mask": 2,
|
||||
"field": "",
|
||||
"permanent": false,
|
||||
"description": "Supprimer des ouvreur⋅ses aux activités"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.permission",
|
||||
"pk": 210,
|
||||
"fields": {
|
||||
"model": [
|
||||
"activity",
|
||||
"activity"
|
||||
],
|
||||
"query": "{}",
|
||||
"type": "change",
|
||||
"mask": 2,
|
||||
"field": "opener",
|
||||
"permanent": false,
|
||||
"description": "Voir le tableau des ouvreur⋅ses"
|
||||
}
|
||||
},
|
||||
{
|
||||
"model": "permission.role",
|
||||
"pk": 1,
|
||||
|
@ -3152,7 +3345,15 @@
|
|||
191,
|
||||
195,
|
||||
196,
|
||||
198
|
||||
198,
|
||||
199,
|
||||
200,
|
||||
201,
|
||||
202,
|
||||
203,
|
||||
204,
|
||||
205,
|
||||
206
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -3414,7 +3615,11 @@
|
|||
46,
|
||||
148,
|
||||
149,
|
||||
182
|
||||
182,
|
||||
207,
|
||||
208,
|
||||
209,
|
||||
210
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
@ -8,7 +8,7 @@ peuvent être diffusées via des calendriers ou la mailing list d'événements.
|
|||
Modèles
|
||||
-------
|
||||
|
||||
L'application comporte 5 modèles : activités, types d'activité, invités, entrées et transactions d'invitation.
|
||||
L'application comporte 6 modèles : activités, types d'activité, invités, entrées, transactions d'invitation et les ouvreur⋅ses.
|
||||
|
||||
Types d'activité
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
@ -71,6 +71,17 @@ comportent qu'un champ supplémentaire, de type ``OneToOneField`` vers ``Guest``
|
|||
Ce modèle aurait pu appartenir à l'application ``note``, mais afin de rester modulaire et que l'application ``note``
|
||||
ne dépende pas de cette application, on procède de cette manière.
|
||||
|
||||
Ouvreur⋅ses
|
||||
~~~~~~~~~~~
|
||||
|
||||
Depuis la page d'une activité, il est possible d'ajouter des personnes en tant qu'« ouvreur⋅se ». Cela permet à une
|
||||
personne sans aucun droit note de pouvoir faire les entrées d'une ``Activity``. Ce rôle n'est valable que pendant que
|
||||
l'activité est ouverte et sur aucune autre activité. Les ouvreur⋅ses ont aussi accès à l'interface des transactions.
|
||||
|
||||
Ce modèle regroupe :
|
||||
* Activité (clé étrangère)
|
||||
* Note (clé étrangère)
|
||||
|
||||
Graphe
|
||||
~~~~~~
|
||||
|
||||
|
@ -108,3 +119,6 @@ apparaîssent, afin de régler la taxe d'invitation : l'un prélève directement
|
|||
permettent un paiement par espèces ou par carte bancaire. En réalité, les deux derniers boutons enregistrent
|
||||
automatiquement un crédit sur la note de l'hôte, puis une transaction (de type ``GuestTransaction``) est faite depuis
|
||||
la note de l'hôte vers la note de l'organisateur de l'événement.
|
||||
|
||||
Si une personne souhaite faire les entrées, il est possible de l'ajouter dans la liste des ouvreur⋅ses depuis la page
|
||||
de l'activité.
|
||||
|
|
|
@ -20,6 +20,7 @@ msgstr ""
|
|||
|
||||
#: apps/activity/apps.py:10 apps/activity/models.py:127
|
||||
#: apps/activity/models.py:167
|
||||
#: apps/activity/models.py:323
|
||||
msgid "activity"
|
||||
msgstr "activité"
|
||||
|
||||
|
@ -238,6 +239,16 @@ msgstr "invité·e·s"
|
|||
msgid "Invitation"
|
||||
msgstr "Invitation"
|
||||
|
||||
#: apps/activity/models.py:330
|
||||
#: apps/activity/models.py:334
|
||||
msgid "Opener"
|
||||
msgstr "Ouvreur⋅se"
|
||||
|
||||
#: apps/activity/models.py:335
|
||||
#: apps/activity/templates/activity_detail.html:16
|
||||
msgid "Openers"
|
||||
msgstr "Ouvreur⋅ses"
|
||||
|
||||
#: apps/activity/tables.py:27
|
||||
msgid "The activity is currently open."
|
||||
msgstr "Cette activité est actuellement ouverte."
|
||||
|
|
Loading…
Reference in New Issue