Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
BDE
nk20
Commits
5c702187
Commit
5c702187
authored
Sep 14, 2020
by
ynerant
Browse files
Merge branch 'beta' into 'master'
Corrections diverses See merge request
!123
parents
3191dba3
905d6537
Pipeline
#8736
passed with stages
in 12 minutes and 38 seconds
Changes
38
Pipelines
2
Hide whitespace changes
Inline
Side-by-side
apps/activity/models.py
View file @
5c702187
...
...
@@ -7,7 +7,7 @@ from threading import Thread
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
from
django.db
import
models
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
_
...
...
@@ -123,6 +123,7 @@ class Activity(models.Model):
verbose_name
=
_
(
'open'
),
)
@
transaction
.
atomic
def
save
(
self
,
*
args
,
**
kwargs
):
"""
Update the activity wiki page each time the activity is updated (validation, change description, ...)
...
...
@@ -194,8 +195,8 @@ class Entry(models.Model):
else
_
(
"Entry for {note} to the activity {activity}"
).
format
(
guest
=
str
(
self
.
guest
),
note
=
str
(
self
.
note
),
activity
=
str
(
self
.
activity
))
@
transaction
.
atomic
def
save
(
self
,
*
args
,
**
kwargs
):
qs
=
Entry
.
objects
.
filter
(
~
Q
(
pk
=
self
.
pk
),
activity
=
self
.
activity
,
note
=
self
.
note
,
guest
=
self
.
guest
)
if
qs
.
exists
():
raise
ValidationError
(
_
(
"Already entered on "
)
+
_
(
"{:%Y-%m-%d %H:%M:%S}"
).
format
(
qs
.
get
().
time
,
))
...
...
@@ -260,6 +261,7 @@ class Guest(models.Model):
except
AttributeError
:
return
False
@
transaction
.
atomic
def
save
(
self
,
force_insert
=
False
,
force_update
=
False
,
using
=
None
,
update_fields
=
None
):
one_year
=
timedelta
(
days
=
365
)
...
...
apps/activity/views.py
View file @
5c702187
...
...
@@ -7,6 +7,7 @@ from django.conf import settings
from
django.contrib.auth.mixins
import
LoginRequiredMixin
from
django.contrib.contenttypes.models
import
ContentType
from
django.core.exceptions
import
PermissionDenied
from
django.db
import
transaction
from
django.db.models
import
F
,
Q
from
django.http
import
HttpResponse
from
django.urls
import
reverse_lazy
...
...
@@ -44,6 +45,7 @@ class ActivityCreateView(ProtectQuerysetMixin, ProtectedCreateView):
date_end
=
timezone
.
now
(),
)
@
transaction
.
atomic
def
form_valid
(
self
,
form
):
form
.
instance
.
creater
=
self
.
request
.
user
return
super
().
form_valid
(
form
)
...
...
@@ -145,6 +147,7 @@ class ActivityInviteView(ProtectQuerysetMixin, ProtectedCreateView):
form
.
fields
[
"inviter"
].
initial
=
self
.
request
.
user
.
note
return
form
@
transaction
.
atomic
def
form_valid
(
self
,
form
):
form
.
instance
.
activity
=
Activity
.
objects
\
.
filter
(
PermissionBackend
.
filter_queryset
(
self
.
request
.
user
,
Activity
,
"view"
)).
get
(
pk
=
self
.
kwargs
[
"pk"
])
...
...
apps/member/forms.py
View file @
5c702187
...
...
@@ -8,6 +8,7 @@ from django import forms
from
django.conf
import
settings
from
django.contrib.auth.forms
import
AuthenticationForm
from
django.contrib.auth.models
import
User
from
django.db
import
transaction
from
django.forms
import
CheckboxSelectMultiple
from
django.utils
import
timezone
from
django.utils.translation
import
gettext_lazy
as
_
...
...
@@ -57,6 +58,7 @@ class ProfileForm(forms.ModelForm):
self
.
fields
[
'address'
].
widget
.
attrs
.
update
({
"placeholder"
:
"4 avenue des Sciences, 91190 GIF-SUR-YVETTE"
})
self
.
fields
[
'promotion'
].
widget
.
attrs
.
update
({
"max"
:
timezone
.
now
().
year
})
@
transaction
.
atomic
def
save
(
self
,
commit
=
True
):
if
not
self
.
instance
.
section
or
((
"department"
in
self
.
changed_data
or
"promotion"
in
self
.
changed_data
)
and
"section"
not
in
self
.
changed_data
):
...
...
@@ -161,7 +163,7 @@ class MembershipForm(forms.ModelForm):
soge
=
forms
.
BooleanField
(
label
=
_
(
"Inscription paid by Société Générale"
),
required
=
False
,
help_text
=
_
(
"Check this case i
s
the Société Générale paid the inscription."
),
help_text
=
_
(
"Check this case i
f
the Société Générale paid the inscription."
),
)
credit_type
=
forms
.
ModelChoiceField
(
...
...
apps/member/models.py
View file @
5c702187
...
...
@@ -7,7 +7,7 @@ import os
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
from
django.core.exceptions
import
ValidationError
from
django.db
import
models
from
django.db
import
models
,
transaction
from
django.db.models
import
Q
from
django.template
import
loader
from
django.urls
import
reverse
,
reverse_lazy
...
...
@@ -271,6 +271,7 @@ class Club(models.Model):
self
.
_force_save
=
True
self
.
save
(
force_update
=
True
)
@
transaction
.
atomic
def
save
(
self
,
force_insert
=
False
,
force_update
=
False
,
using
=
None
,
update_fields
=
None
):
if
not
self
.
require_memberships
:
...
...
@@ -406,6 +407,7 @@ class Membership(models.Model):
parent_membership
.
roles
.
set
(
Role
.
objects
.
filter
(
name
=
"Membre de club"
).
all
())
parent_membership
.
save
()
@
transaction
.
atomic
def
save
(
self
,
*
args
,
**
kwargs
):
"""
Calculate fee and end date before saving the membership and creating the transaction if needed.
...
...
@@ -475,8 +477,13 @@ class Membership(models.Model):
# to treasurers.
transaction
.
valid
=
False
from
treasury.models
import
SogeCredit
soge_credit
=
SogeCredit
.
objects
.
get_or_create
(
user
=
self
.
user
)[
0
]
soge_credit
.
refresh_from_db
()
if
SogeCredit
.
objects
.
filter
(
user
=
self
.
user
).
exists
():
soge_credit
=
SogeCredit
.
objects
.
get
(
user
=
self
.
user
)
else
:
soge_credit
=
SogeCredit
(
user
=
self
.
user
)
soge_credit
.
_force_save
=
True
soge_credit
.
save
(
force_insert
=
True
)
soge_credit
.
refresh_from_db
()
transaction
.
save
(
force_insert
=
True
)
transaction
.
refresh_from_db
()
soge_credit
.
transactions
.
add
(
transaction
)
...
...
apps/member/templates/member/includes/profile_info.html
View file @
5c702187
...
...
@@ -38,7 +38,7 @@
<dt
class=
"col-xl-6"
>
{% trans 'address'|capfirst %}
</dt>
<dd
class=
"col-xl-6"
>
{{ user_object.profile.address }}
</dd>
{% if "note.view_note"|has_perm:user_object.note %}
{% if
user_object.note and
"note.view_note"|has_perm:user_object.note %}
<dt
class=
"col-xl-6"
>
{% trans 'balance'|capfirst %}
</dt>
<dd
class=
"col-xl-6"
>
{{ user_object.note.balance | pretty_money }}
</dd>
...
...
@@ -47,7 +47,7 @@
{% endif %}
</dl>
{% if user_object.pk == user
_object
.pk %}
{% if user_object.pk == user.pk %}
<div
class=
"text-center"
>
<a
class=
"small badge badge-secondary"
href=
"{% url 'member:auth_token' %}"
>
<i
class=
"fa fa-cogs"
></i>
{% trans 'API token' %}
...
...
apps/member/views.py
View file @
5c702187
...
...
@@ -38,6 +38,7 @@ class CustomLoginView(LoginView):
"""
form_class
=
CustomAuthenticationForm
@
transaction
.
atomic
def
form_valid
(
self
,
form
):
logout
(
self
.
request
)
_set_current_user_and_ip
(
form
.
get_user
(),
self
.
request
.
session
,
None
)
...
...
@@ -76,6 +77,7 @@ class UserUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
return
context
@
transaction
.
atomic
def
form_valid
(
self
,
form
):
"""
Check if ProfileForm is correct
...
...
@@ -269,6 +271,7 @@ class PictureUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, FormMixin, Det
self
.
object
=
self
.
get_object
()
return
self
.
form_valid
(
form
)
if
form
.
is_valid
()
else
self
.
form_invalid
(
form
)
@
transaction
.
atomic
def
form_valid
(
self
,
form
):
"""Save image to note"""
image
=
form
.
cleaned_data
[
'image'
]
...
...
@@ -650,6 +653,7 @@ class ClubAddMemberView(ProtectQuerysetMixin, ProtectedCreateView):
return
not
error
@
transaction
.
atomic
def
form_valid
(
self
,
form
):
"""
Create membership, check that all is good, make transactions
...
...
apps/note/api/views.py
View file @
5c702187
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later
from
django.conf
import
settings
from
django.db.models
import
Q
from
django.core.exceptions
import
ValidationError
...
...
@@ -56,8 +57,9 @@ class AliasViewSet(ReadProtectedModelViewSet):
"""
queryset
=
Alias
.
objects
.
all
()
serializer_class
=
AliasSerializer
filter_backends
=
[
SearchFilter
,
OrderingFilter
]
filter_backends
=
[
SearchFilter
,
DjangoFilterBackend
,
OrderingFilter
]
search_fields
=
[
'$normalized_name'
,
'$name'
,
'$note__polymorphic_ctype__model'
,
]
filterset_fields
=
[
'note'
]
ordering_fields
=
[
'name'
,
'normalized_name'
]
def
get_serializer_class
(
self
):
...
...
@@ -106,8 +108,9 @@ class AliasViewSet(ReadProtectedModelViewSet):
class
ConsumerViewSet
(
ReadOnlyProtectedModelViewSet
):
queryset
=
Alias
.
objects
.
all
()
serializer_class
=
ConsumerSerializer
filter_backends
=
[
SearchFilter
,
OrderingFilter
]
filter_backends
=
[
SearchFilter
,
OrderingFilter
,
DjangoFilterBackend
]
search_fields
=
[
'$normalized_name'
,
'$name'
,
'$note__polymorphic_ctype__model'
,
]
filterset_fields
=
[
'note'
]
ordering_fields
=
[
'name'
,
'normalized_name'
]
def
get_queryset
(
self
):
...
...
@@ -116,29 +119,31 @@ class ConsumerViewSet(ReadOnlyProtectedModelViewSet):
:return: The filtered set of requested aliases
"""
queryset
=
super
().
get_queryset
()
queryset
=
super
().
get_queryset
()
.
distinct
()
# Sqlite doesn't support ORDER BY in subqueries
queryset
=
queryset
.
order_by
(
"name"
)
\
if
settings
.
DATABASES
[
queryset
.
db
][
"ENGINE"
]
==
'django.db.backends.postgresql'
else
queryset
alias
=
self
.
request
.
query_params
.
get
(
"alias"
,
".*"
)
alias
=
self
.
request
.
query_params
.
get
(
"alias"
,
None
)
queryset
=
queryset
.
prefetch_related
(
'note'
)
# We match first an alias if it is matched without normalization,
# then if the normalized pattern matches a normalized alias.
queryset
=
queryset
.
filter
(
name__iregex
=
"^"
+
alias
).
union
(
queryset
.
filter
(
Q
(
normalized_name__iregex
=
"^"
+
Alias
.
normalize
(
alias
))
&
~
Q
(
name__iregex
=
"^"
+
alias
)
),
all
=
True
).
union
(
queryset
.
filter
(
Q
(
normalized_name__iregex
=
"^"
+
alias
.
lower
())
&
~
Q
(
normalized_name__iregex
=
"^"
+
Alias
.
normalize
(
alias
))
&
~
Q
(
name__iregex
=
"^"
+
alias
)
),
all
=
True
)
if
alias
:
# We match first an alias if it is matched without normalization,
# then if the normalized pattern matches a normalized alias.
queryset
=
queryset
.
filter
(
name__iregex
=
"^"
+
alias
).
union
(
queryset
.
filter
(
Q
(
normalized_name__iregex
=
"^"
+
Alias
.
normalize
(
alias
))
&
~
Q
(
name__iregex
=
"^"
+
alias
)
),
all
=
True
).
union
(
queryset
.
filter
(
Q
(
normalized_name__iregex
=
"^"
+
alias
.
lower
())
&
~
Q
(
normalized_name__iregex
=
"^"
+
Alias
.
normalize
(
alias
))
&
~
Q
(
name__iregex
=
"^"
+
alias
)
),
all
=
True
)
queryset
=
queryset
if
settings
.
DATABASES
[
queryset
.
db
][
"ENGINE"
]
==
'django.db.backends.postgresql'
\
else
queryset
.
order_by
(
"name"
)
...
...
@@ -179,8 +184,11 @@ class TransactionViewSet(ReadProtectedModelViewSet):
"""
queryset
=
Transaction
.
objects
.
order_by
(
"-created_at"
).
all
()
serializer_class
=
TransactionPolymorphicSerializer
filter_backends
=
[
SearchFilter
]
filter_backends
=
[
SearchFilter
,
DjangoFilterBackend
,
OrderingFilter
]
filterset_fields
=
[
"source"
,
"source_alias"
,
"destination"
,
"destination_alias"
,
"quantity"
,
"polymorphic_ctype"
,
"amount"
,
"created_at"
,
]
search_fields
=
[
'$reason'
,
]
ordering_fields
=
[
'created_at'
,
'amount'
]
def
get_queryset
(
self
):
user
=
self
.
request
.
user
...
...
apps/note/models/notes.py
View file @
5c702187
...
...
@@ -8,7 +8,7 @@ from django.conf.global_settings import DEFAULT_FROM_EMAIL
from
django.core.exceptions
import
ValidationError
from
django.core.mail
import
send_mail
from
django.core.validators
import
RegexValidator
from
django.db
import
models
from
django.db
import
models
,
transaction
from
django.template.loader
import
render_to_string
from
django.utils
import
timezone
from
django.utils.translation
import
gettext_lazy
as
_
...
...
@@ -93,6 +93,7 @@ class Note(PolymorphicModel):
delta
=
timezone
.
now
()
-
self
.
last_negative
return
"{:d} jours"
.
format
(
delta
.
days
)
@
transaction
.
atomic
def
save
(
self
,
*
args
,
**
kwargs
):
"""
Save note with it's alias (called in polymorphic children)
...
...
@@ -108,12 +109,16 @@ class Note(PolymorphicModel):
# Save alias
a
.
note
=
self
# Consider that if the name of the note could be changed, then the alias can be created.
# It does not mean that any alias can be created.
a
.
_force_save
=
True
a
.
save
(
force_insert
=
True
)
else
:
# Check if the name of the note changed without changing the normalized form of the alias
alias
=
Alias
.
objects
.
get
(
normalized_name
=
Alias
.
normalize
(
str
(
self
)))
if
alias
.
name
!=
str
(
self
):
alias
.
name
=
str
(
self
)
alias
.
_force_save
=
True
alias
.
save
()
def
clean
(
self
,
*
args
,
**
kwargs
):
...
...
@@ -154,6 +159,7 @@ class NoteUser(Note):
def
pretty
(
self
):
return
_
(
"%(user)s's note"
)
%
{
'user'
:
str
(
self
.
user
)}
@
transaction
.
atomic
def
save
(
self
,
*
args
,
**
kwargs
):
if
self
.
pk
and
self
.
balance
<
0
:
old_note
=
NoteUser
.
objects
.
get
(
pk
=
self
.
pk
)
...
...
@@ -195,6 +201,7 @@ class NoteClub(Note):
def
pretty
(
self
):
return
_
(
"Note of %(club)s club"
)
%
{
'club'
:
str
(
self
.
club
)}
@
transaction
.
atomic
def
save
(
self
,
*
args
,
**
kwargs
):
if
self
.
pk
and
self
.
balance
<
0
:
old_note
=
NoteClub
.
objects
.
get
(
pk
=
self
.
pk
)
...
...
@@ -310,6 +317,7 @@ class Alias(models.Model):
pass
self
.
normalized_name
=
normalized_name
@
transaction
.
atomic
def
save
(
self
,
*
args
,
**
kwargs
):
self
.
clean
()
super
().
save
(
*
args
,
**
kwargs
)
...
...
apps/note/models/transactions.py
View file @
5c702187
...
...
@@ -170,19 +170,21 @@ class Transaction(PolymorphicModel):
previous_source_balance
=
self
.
source
.
balance
previous_dest_balance
=
self
.
destination
.
balance
source_balance
=
self
.
source
.
balance
dest_balance
=
self
.
destination
.
balance
source_balance
=
previous_
source
_
balance
dest_balance
=
previous_dest_
balance
created
=
self
.
pk
is
None
to_transfer
=
self
.
amount
*
self
.
quantity
if
not
created
and
not
self
.
valid
and
not
hasattr
(
self
,
"_force_save"
)
:
to_transfer
=
self
.
total
if
not
created
:
# Revert old transaction
old_transaction
=
Transaction
.
objects
.
get
(
pk
=
self
.
pk
)
# We make a select for update to avoid concurrency issues
old_transaction
=
Transaction
.
objects
.
select_for_update
().
get
(
pk
=
self
.
pk
)
# Check that nothing important changed
for
field_name
in
[
"source_id"
,
"destination_id"
,
"quantity"
,
"amount"
]:
if
getattr
(
self
,
field_name
)
!=
getattr
(
old_transaction
,
field_name
):
raise
ValidationError
(
_
(
"You can't update the {field} on a Transaction. "
"Please invalidate it and create one other."
).
format
(
field
=
field_name
))
if
not
hasattr
(
self
,
"_force_save"
):
for
field_name
in
[
"source_id"
,
"destination_id"
,
"quantity"
,
"amount"
]:
if
getattr
(
self
,
field_name
)
!=
getattr
(
old_transaction
,
field_name
):
raise
ValidationError
(
_
(
"You can't update the {field} on a Transaction. "
"Please invalidate it and create one other."
).
format
(
field
=
field_name
))
if
old_transaction
.
valid
==
self
.
valid
:
# Don't change anything
...
...
@@ -215,10 +217,6 @@ class Transaction(PolymorphicModel):
# When source == destination, no money is transferred and no transaction is created
return
# We refresh the notes with the "select for update" tag to avoid concurrency issues
self
.
source
=
Note
.
objects
.
filter
(
pk
=
self
.
source_id
).
select_for_update
().
get
()
self
.
destination
=
Note
.
objects
.
filter
(
pk
=
self
.
destination_id
).
select_for_update
().
get
()
# Check that the amounts stay between big integer bounds
diff_source
,
diff_dest
=
self
.
validate
()
...
...
@@ -237,9 +235,11 @@ class Transaction(PolymorphicModel):
super
().
save
(
*
args
,
**
kwargs
)
# Save notes
self
.
source
.
refresh_from_db
()
self
.
source
.
balance
+=
diff_source
self
.
source
.
_force_save
=
True
self
.
source
.
save
()
self
.
destination
.
refresh_from_db
()
self
.
destination
.
balance
+=
diff_dest
self
.
destination
.
_force_save
=
True
self
.
destination
.
save
()
...
...
@@ -273,6 +273,7 @@ class RecurrentTransaction(Transaction):
_
(
"The destination of this transaction must equal to the destination of the template."
))
return
super
().
clean
()
@
transaction
.
atomic
def
save
(
self
,
*
args
,
**
kwargs
):
self
.
clean
()
return
super
().
save
(
*
args
,
**
kwargs
)
...
...
@@ -323,6 +324,7 @@ class SpecialTransaction(Transaction):
raise
(
ValidationError
(
_
(
"A special transaction is only possible between a"
" Note associated to a payment method and a User or a Club"
)))
@
transaction
.
atomic
def
save
(
self
,
*
args
,
**
kwargs
):
self
.
clean
()
super
().
save
(
*
args
,
**
kwargs
)
...
...
note_kfet
/static/js/consos.js
→
apps/note
/static/
note/
js/consos.js
View file @
5c702187
...
...
@@ -29,7 +29,6 @@ $(document).ready(function () {
// Switching in double consumptions mode should update the layout
$
(
'
#double_conso
'
).
change
(
function
()
{
$
(
'
#consos_list_div
'
).
removeClass
(
'
d-none
'
)
$
(
'
#user_select_div
'
).
attr
(
'
class
'
,
'
col-xl-4
'
)
$
(
'
#infos_div
'
).
attr
(
'
class
'
,
'
col-sm-5 col-xl-6
'
)
const
note_list_obj
=
$
(
'
#note_list
'
)
...
...
@@ -48,7 +47,6 @@ $(document).ready(function () {
$
(
'
#single_conso
'
).
change
(
function
()
{
$
(
'
#consos_list_div
'
).
addClass
(
'
d-none
'
)
$
(
'
#user_select_div
'
).
attr
(
'
class
'
,
'
col-xl-7
'
)
$
(
'
#infos_div
'
).
attr
(
'
class
'
,
'
col-sm-5 col-md-4
'
)
const
consos_list_obj
=
$
(
'
#consos_list
'
)
...
...
note_kfet
/static/js/transfer.js
→
apps/note
/static/
note/
js/transfer.js
View file @
5c702187
File moved
apps/note/templates/note/conso_form.html
View file @
5c702187
...
...
@@ -10,22 +10,22 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% block content %}
<div
class=
"row mt-4"
>
<div
class=
"col-sm-5 col-md-4"
id=
"infos_div"
>
<div
class=
"row"
>
<div
class=
"row
justify-content-center justify-content-md-end
"
>
{# User details column #}
<div
class=
"col"
>
<div
class=
"card bg-light
border-success
mb-4 text-center"
>
<div
class=
"col
picture-col
"
>
<div
class=
"card bg-light mb-4 text-center"
>
<a
id=
"profile_pic_link"
href=
"#"
>
<img
src=
"{% static "
member
/
img
/
default_picture.png
"
%}"
id=
"profile_pic"
alt=
""
class=
"card-img-top"
>
id=
"profile_pic"
alt=
""
class=
"card-img-top
d-none d-sm-block
"
>
</a>
<div
class=
"card-body text-center text-break"
>
<span
id=
"user_note"
></span>
<div
class=
"card-body text-center text-break
p-2
"
>
<span
id=
"user_note"
><
i
class=
"small"
>
{% trans "Please select a note" %}
</i><
/span>
</div>
</div>
</div>
{# User selection column #}
<div
class=
"col-xl
-7
"
id=
"user_select_div"
>
<div
class=
"col-xl"
id=
"user_select_div"
>
<div
class=
"card bg-light border-success mb-4"
>
<div
class=
"card-header"
>
<p
class=
"card-text font-weight-bold"
>
...
...
@@ -44,6 +44,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
</div>
</div>
</div>
{# Summary of consumption and consume button #}
<div
class=
"col-xl-5 d-none"
id=
"consos_list_div"
>
<div
class=
"card bg-light border-info mb-4"
>
...
...
@@ -65,7 +66,6 @@ SPDX-License-Identifier: GPL-3.0-or-later
</div>
</div>
{# Show last used buttons #}
<div
class=
"card bg-light mb-4"
>
<div
class=
"card-header"
>
...
...
@@ -159,7 +159,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% endblock %}
{% block extrajavascript %}
<script
type=
"text/javascript"
src=
"{% static "
js
/
consos.js
"
%}"
></script>
<script
type=
"text/javascript"
src=
"{% static "
note
/
js
/
consos.js
"
%}"
></script>
<script
type=
"text/javascript"
>
{
%
for
button
in
highlighted
%
}
{
%
if
button
.
display
%
}
...
...
apps/note/templates/note/transaction_form.html
View file @
5c702187
...
...
@@ -34,21 +34,21 @@ SPDX-License-Identifier: GPL-2.0-or-later
</div>
</div>
<hr>
<div
class=
"row"
>
<div
class=
"row
justify-content-center
"
>
{# Preview note profile (picture, username and balance) #}
<div
class=
"col-md
-3
"
id=
"note_infos_div"
>
<div
class=
"card bg-light b
order-success shadow mb-4 pt
-4 text-center"
>
<div
class=
"col-md
picture-col
"
id=
"note_infos_div"
>
<div
class=
"card bg-light
m
b-4 text-center"
>
<a
id=
"profile_pic_link"
href=
"#"
><img
src=
"{% static "
member
/
img
/
default_picture.png
"
%}"
id=
"profile_pic"
alt=
""
class=
"img-fluid rounded mx-auto"
></a>
<div
class=
"card-body text-center"
>
<span
id=
"user_note"
></span>
<div
class=
"card-body text-center
p-2
"
>
<span
id=
"user_note"
><
i
class=
"small"
>
{% trans "Please select a note" %}
</i><
/span>
</div>
</div>
</div>
{# list of emitters #}
<div
class=
"col-md-3"
id=
"emitters_div"
>
<div
class=
"card bg-light
border-success shadow
mb-4"
>
<div
class=
"card bg-light mb-4"
>
<div
class=
"card-header"
>
<p
class=
"card-text font-weight-bold"
>
<label
for=
"source_note"
id=
"source_note_label"
>
{% trans "Select emitters" %}
</label>
...
...
@@ -75,7 +75,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
{# list of receiver #}
<div
class=
"col-md-3"
id=
"dests_div"
>
<div
class=
"card bg-light
border-info shadow
mb-4"
>
<div
class=
"card bg-light mb-4"
>
<div
class=
"card-header"
>
<p
class=
"card-text font-weight-bold"
id=
"dest_title"
>
<label
for=
"dest_note"
id=
"dest_note_label"
>
{% trans "Select receivers" %}
</label>
...
...
@@ -97,8 +97,8 @@ SPDX-License-Identifier: GPL-2.0-or-later
</div>
{# Information on transaction (amount, reason, name,...) #}
<div
class=
"col-md
-3
"
id=
"external_div"
>
<div
class=
"card bg-light
border-warning shadow
mb-4"
>
<div
class=
"col-md"
id=
"external_div"
>
<div
class=
"card bg-light mb-4"
>
<div
class=
"card-header"
>
<p
class=
"card-text font-weight-bold"
>
{% trans "Action" %}
...
...
@@ -153,7 +153,7 @@ SPDX-License-Identifier: GPL-2.0-or-later
</div>
</div>
{# transaction history #}
<div
class=
"card
shadow
mb-4"
id=
"history"
>
<div
class=
"card mb-4"
id=
"history"
>
<div
class=
"card-header"
>
<p
class=
"card-text font-weight-bold"
>
{% trans "Recent transactions history" %}
...
...
@@ -176,5 +176,5 @@ SPDX-License-Identifier: GPL-2.0-or-later
select_receveirs_label
=
"
{% trans
"
Select
receivers
"
%}
"
;
transfer_type_label
=
"
{% trans
"
Transfer
type
"
%}
"
;
</script>
<script
src=
"
/
static/js/transfer.js"
></script>
<script
src=
"
{%
static
"
note
/
js
/
transfer.js
"
%}"
></script>
{% endblock %}
apps/note/views.py
View file @
5c702187
...
...
@@ -144,7 +144,7 @@ class TransactionTemplateUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, Up
class
ConsoView
(
ProtectQuerysetMixin
,
LoginRequiredMixin
,
SingleTableView
):
"""
The Magic View that make people pay their beer and burgers.
(Most of the magic happens in the dark world of Javascript see `
note_kfet/
static/js/consos.js`)
(Most of the magic happens in the dark world of Javascript see `static/
note/
js/consos.js`)
"""
model
=
Transaction
template_name
=
"note/conso_form.html"
...
...
apps/permission/decorators.py
View file @
5c702187
...
...
@@ -4,7 +4,6 @@
from
functools
import
lru_cache
from
time
import
time
from
django.conf
import
settings
from
django.contrib.sessions.models
import
Session
from
note_kfet.middlewares
import
get_current_session
...
...
@@ -33,9 +32,9 @@ def memoize(f):
sess_funs
=
new_sess_funs
def
func
(
*
args
,
**
kwargs
):
if
settings
.
DEBUG
:
# Don't memoize in DEBUG mode
return
f
(
*
args
,
**
kwargs
)
#
if settings.DEBUG:
#
# Don't memoize in DEBUG mode
#
return f(*args, **kwargs)
nonlocal
last_collect
...
...
apps/permission/fixtures/initial.json
View file @
5c702187
...
...
@@ -2679,6 +2679,102 @@
"description"
:
"Supprimer n'importe quel alias à une note non bloquée"
}
},
{
"model"
:
"permission.permission"
,
"pk"
:
172
,
"fields"
:
{
"model"
:
[
"treasury"
,
"remittance"
],
"query"
:
"{}"
,
"type"
:
"view"
,
"mask"
:
3
,
"field"
:
""
,
"permanent"
:
false
,
"description"
:
"Voir toutes les remises"
}
},
{
"model"
:
"permission.permission"
,
"pk"
:
173
,
"fields"
:
{
"model"
:
[
"treasury"
,
"remittance"
],
"query"
:
"{}"
,
"type"
:
"add"
,
"mask"
:
3
,
"field"
:
""
,
"permanent"
:
false
,
"description"
:
"Ajouter une remise"
}
},
{
"model"
:
"permission.permission"
,
"pk"
:
174
,
"fields"
:
{
"model"
:
[
"treasury"
,
"remittance"
],
"query"
:
"{}"
,
"type"
:
"change"
,
"mask"
:
3
,
"field"
:
""
,
"permanent"
:
false
,
"description"
:
"Modifier une remise"
}