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
a673fd6871
|
@ -1,9 +1,11 @@
|
||||||
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
from rest_framework import serializers
|
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):
|
class ActivityTypeSerializer(serializers.ModelSerializer):
|
||||||
|
@ -59,3 +61,17 @@ class GuestTransactionSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = GuestTransaction
|
model = GuestTransaction
|
||||||
fields = '__all__'
|
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
|
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# 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):
|
def register_activity_urls(router, path):
|
||||||
|
@ -12,3 +12,4 @@ def register_activity_urls(router, path):
|
||||||
router.register(path + '/type', ActivityTypeViewSet)
|
router.register(path + '/type', ActivityTypeViewSet)
|
||||||
router.register(path + '/guest', GuestViewSet)
|
router.register(path + '/guest', GuestViewSet)
|
||||||
router.register(path + '/entry', EntryViewSet)
|
router.register(path + '/entry', EntryViewSet)
|
||||||
|
router.register(path + '/opener', OpenerViewSet)
|
||||||
|
|
|
@ -2,11 +2,14 @@
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
from api.viewsets import ReadProtectedModelViewSet
|
from api.viewsets import ReadProtectedModelViewSet
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework.filters import SearchFilter
|
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 .serializers import ActivitySerializer, ActivityTypeSerializer, EntrySerializer, GuestSerializer, OpenerSerializer
|
||||||
from ..models import Activity, ActivityType, Entry, Guest
|
from ..models import Activity, ActivityType, Entry, Guest, Opener
|
||||||
|
|
||||||
|
|
||||||
class ActivityTypeViewSet(ReadProtectedModelViewSet):
|
class ActivityTypeViewSet(ReadProtectedModelViewSet):
|
||||||
|
@ -66,3 +69,32 @@ class EntryViewSet(ReadProtectedModelViewSet):
|
||||||
filterset_fields = ['activity', 'time', 'note', 'guest', ]
|
filterset_fields = ['activity', 'time', 'note', 'guest', ]
|
||||||
search_fields = ['$activity__name', '$note__user__email', '$note__alias__name', '$note__alias__normalized_name',
|
search_fields = ['$activity__name', '$note__user__email', '$note__alias__name', '$note__alias__normalized_name',
|
||||||
'$guest__last_name', '$guest__first_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:
|
class Meta:
|
||||||
model = Activity
|
model = Activity
|
||||||
exclude = ('creater', 'valid', 'open', )
|
exclude = ('creater', 'valid', 'open', 'opener', )
|
||||||
widgets = {
|
widgets = {
|
||||||
"organizer": Autocomplete(
|
"organizer": Autocomplete(
|
||||||
model=Club,
|
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.db.models import Q
|
||||||
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 note.models import NoteUser, Transaction
|
from note.models import NoteUser, Transaction, Note
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
@ -310,3 +310,31 @@ class GuestTransaction(Transaction):
|
||||||
@property
|
@property
|
||||||
def type(self):
|
def type(self):
|
||||||
return _('Invitation')
|
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.html import escape
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from note_kfet.middlewares import get_current_request
|
||||||
import django_tables2 as tables
|
import django_tables2 as tables
|
||||||
from django_tables2 import A
|
from django_tables2 import A
|
||||||
|
from permission.backends import PermissionBackend
|
||||||
from note.templatetags.pretty_money import pretty_money
|
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):
|
class ActivityTable(tables.Table):
|
||||||
|
@ -113,3 +115,34 @@ class EntryTable(tables.Table):
|
||||||
'data-last-name': lambda record: record.last_name,
|
'data-last-name': lambda record: record.last_name,
|
||||||
'data-first-name': lambda record: record.first_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 %}
|
{% endcomment %}
|
||||||
{% load i18n perms %}
|
{% load i18n perms %}
|
||||||
{% load render_table from django_tables2 %}
|
{% load render_table from django_tables2 %}
|
||||||
|
{% load static django_tables2 i18n %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1 class="text-white">{{ title }}</h1>
|
<h1 class="text-white">{{ title }}</h1>
|
||||||
{% include "activity/includes/activity_info.html" %}
|
{% 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 %}
|
{% if guests.data %}
|
||||||
<div class="card bg-white mb-3">
|
<div class="card bg-white mb-3">
|
||||||
<h3 class="card-header text-center">
|
<h3 class="card-header text-center">
|
||||||
|
@ -22,6 +42,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block extrajavascript %}
|
{% block extrajavascript %}
|
||||||
|
<script src="{% static "activity/js/opener.js" %}"></script>
|
||||||
|
<script src="{% static "js/autocomplete_model.js" %}"></script>
|
||||||
<script>
|
<script>
|
||||||
function remove_guest(guest_id) {
|
function remove_guest(guest_id) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
|
|
@ -24,8 +24,8 @@ from permission.backends import PermissionBackend
|
||||||
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
|
from permission.views import ProtectQuerysetMixin, ProtectedCreateView
|
||||||
|
|
||||||
from .forms import ActivityForm, GuestForm
|
from .forms import ActivityForm, GuestForm
|
||||||
from .models import Activity, Entry, Guest
|
from .models import Activity, Entry, Guest, Opener
|
||||||
from .tables import ActivityTable, EntryTable, GuestTable
|
from .tables import ActivityTable, EntryTable, GuestTable, OpenerTable
|
||||||
|
|
||||||
|
|
||||||
class ActivityCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
class ActivityCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||||
|
@ -114,8 +114,24 @@ class ActivityDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
|
||||||
.filter(PermissionBackend.filter_queryset(self.request, Guest, "view")))
|
.filter(PermissionBackend.filter_queryset(self.request, Guest, "view")))
|
||||||
context["guests"] = table
|
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["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
|
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):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('note', '0001_initial'),
|
('note', '0001_initial'),
|
||||||
|
('logs', '0001_initial'),
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
|
|
@ -3111,6 +3111,199 @@
|
||||||
"description": "Voir ceux nous ayant pour ami, pour toujours"
|
"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",
|
"model": "permission.role",
|
||||||
"pk": 1,
|
"pk": 1,
|
||||||
|
@ -3148,11 +3341,19 @@
|
||||||
187,
|
187,
|
||||||
188,
|
188,
|
||||||
189,
|
189,
|
||||||
190,
|
190,
|
||||||
191,
|
191,
|
||||||
195,
|
195,
|
||||||
196,
|
196,
|
||||||
198
|
198,
|
||||||
|
199,
|
||||||
|
200,
|
||||||
|
201,
|
||||||
|
202,
|
||||||
|
203,
|
||||||
|
204,
|
||||||
|
205,
|
||||||
|
206
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -3414,7 +3615,11 @@
|
||||||
46,
|
46,
|
||||||
148,
|
148,
|
||||||
149,
|
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
|
Modèles
|
||||||
-------
|
-------
|
||||||
|
|
||||||
L'application comporte 5 modèles : activités, types d'activité, invité⋅es, entrées et transactions d'invitation.
|
L'application comporte 6 modèles : activités, types d'activité, invité⋅es, entrées et transactions d'invitation et les ouvreur⋅ses.
|
||||||
|
|
||||||
Types d'activité
|
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``
|
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.
|
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
|
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
|
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
|
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 du club organisateur de l'événement.
|
la note de l'hôte vers la note du club 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/apps.py:10 apps/activity/models.py:127
|
||||||
#: apps/activity/models.py:167
|
#: apps/activity/models.py:167
|
||||||
|
#: apps/activity/models.py:323
|
||||||
msgid "activity"
|
msgid "activity"
|
||||||
msgstr "activité"
|
msgstr "activité"
|
||||||
|
|
||||||
|
@ -238,6 +239,16 @@ msgstr "invité·e·s"
|
||||||
msgid "Invitation"
|
msgid "Invitation"
|
||||||
msgstr "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
|
#: apps/activity/tables.py:27
|
||||||
msgid "The activity is currently open."
|
msgid "The activity is currently open."
|
||||||
msgstr "Cette activité est actuellement ouverte."
|
msgstr "Cette activité est actuellement ouverte."
|
||||||
|
|
Loading…
Reference in New Issue