Compare commits

..

No commits in common. "66defee3ea84145039d202ec6aeeede54884cca8" and "58fe8914cfd5a0dfca76e92bde4e638b1ff827c2" have entirely different histories.

9 changed files with 43 additions and 66 deletions

View File

@ -68,8 +68,12 @@ class HistoryTable(tables.Table):
"note.change_transaction_invalidity_reason", record) else None, "note.change_transaction_invalidity_reason", record) else None,
"onmouseover": lambda record: '$("#invalidity_reason_' "onmouseover": lambda record: '$("#invalidity_reason_'
+ str(record.id) + '").show();$("#invalidity_reason_' + str(record.id) + '").show();$("#invalidity_reason_'
+ str(record.id) + '").focus();', + str(record.id) + '").focus();'
"onmouseout": lambda record: '$("#invalidity_reason_' + str(record.id) + '").hide()', if PermissionBackend.check_perm(get_current_authenticated_user(),
"note.change_transaction_invalidity_reason", record) else None,
"onmouseout": lambda record: '$("#invalidity_reason_' + str(record.id) + '").hide()'
if PermissionBackend.check_perm(get_current_authenticated_user(),
"note.change_transaction_invalidity_reason", record) else None,
} }
} }
) )
@ -97,18 +101,15 @@ class HistoryTable(tables.Table):
""" """
When the validation status is hovered, an input field is displayed to let the user specify an invalidity reason When the validation status is hovered, an input field is displayed to let the user specify an invalidity reason
""" """
has_perm = PermissionBackend\
.check_perm(get_current_authenticated_user(), "note.change_transaction_invalidity_reason", record)
val = "" if value else "" val = "" if value else ""
if not PermissionBackend\
if value and not has_perm: .check_perm(get_current_authenticated_user(), "note.change_transaction_invalidity_reason", record):
return val return val
val += "<input type='text' class='form-control' id='invalidity_reason_" + str(record.id) \ val += "<input type='text' class='form-control' id='invalidity_reason_" + str(record.id) \
+ "' value='" + (html.escape(record.invalidity_reason) + "' value='" + (html.escape(record.invalidity_reason)
if record.invalidity_reason else ("" if value else str(_("No reason specified")))) \ if record.invalidity_reason else ("" if value else str(_("No reason specified")))) \
+ "'" + ("" if value and has_perm else " disabled") \ + "'" + ("" if value else " disabled") \
+ " placeholder='" + html.escape(_("invalidity reason").capitalize()) + "'" \ + " placeholder='" + html.escape(_("invalidity reason").capitalize()) + "'" \
+ " style='position: absolute; width: 15em; margin-left: -15.5em; margin-top: -2em; display: none;'>" + " style='position: absolute; width: 15em; margin-left: -15.5em; margin-top: -2em; display: none;'>"
return format_html(val) return format_html(val)

View File

@ -4,15 +4,12 @@
import functools import functools
import json import json
import operator import operator
from copy import copy
from time import sleep from time import sleep
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.mail import mail_admins
from django.db import models from django.db import models
from django.db.models import F, Q, Model from django.db.models import F, Q, Model
from django.forms import model_to_dict
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -41,40 +38,35 @@ class InstancedPermission:
if permission_type == self.type: if permission_type == self.type:
self.update_query() self.update_query()
obj = copy(obj) # Don't increase indexes, if the primary key is an AutoField
obj.pk = 0 if not hasattr(obj, "pk") or not obj.pk:
obj.pk = 0
oldpk = None
else:
oldpk = obj.pk
# Ensure previous models are deleted # Ensure previous models are deleted
for ignored in range(1000): for ignored in range(1000):
if self.model.model_class().objects.filter(pk=0).exists(): if self.model.model_class().objects.filter(pk=obj.pk).exists():
# If the object exists, that means that one permission is currently checked. # If the object exists, that means that one permission is currently checked.
# We wait before the other permission, at most 1 second. # We wait before the other permission, at most 1 second.
sleep(0.001) sleep(0.001)
continue continue
break break
for o in self.model.model_class().objects.filter(pk=0).all(): for o in self.model.model_class().objects.filter(pk=obj.pk).all():
o._force_delete = True o._force_delete = True
Model.delete(o) Model.delete(o)
# An object with pk 0 wouldn't deleted. That's not normal, we alert admins.
msg = "Lors de la vérification d'une permission d'ajout, un objet de clé primaire nulle était "\
"encore présent.\n"\
"Type de permission : " + self.type + "\n"\
"Modèle : " + str(self.model) + "\n"\
"Objet trouvé : " + str(model_to_dict(o)) + "\n\n"\
"--\nLe BDE"
mail_admins("[Note Kfet] Un objet a été supprimé de force", msg)
# Force insertion, no data verification, no trigger # Force insertion, no data verification, no trigger
obj._force_save = True obj._force_save = True
Model.save(obj, force_insert=True) Model.save(obj, force_insert=True)
# We don't want log anything # We don't want log anything
obj._no_log = True obj._no_log = True
ret = self.model.model_class().objects.filter(self.query & Q(pk=0)).exists() ret = self.model.model_class().objects.filter(self.query & Q(pk=obj.pk)).exists()
# Delete testing object # Delete testing object
obj._force_delete = True obj._force_delete = True
Model.delete(obj) Model.delete(obj)
with open("/tmp/log", "w") as f: # If the primary key was specified, we restore it
f.write(str(obj) + ", " + str(obj.pk) + ", " + str(self.model.model_class().objects.filter(pk=0).exists())) obj.pk = oldpk
return ret return ret
if permission_type == self.type: if permission_type == self.type:

View File

@ -179,6 +179,8 @@ class FutureUserListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableVi
| Q(profile__section__iregex=pattern) | Q(profile__section__iregex=pattern)
| Q(username__iregex="^" + pattern) | Q(username__iregex="^" + pattern)
) )
else:
qs = qs.none()
return qs[:20] return qs[:20]

@ -1 +1 @@
Subproject commit f41a5a32f7417a874b497640373ea3911eb1e133 Subproject commit 1f300c3b7bac0b7a31c1a252a83ba68a8268d33d

View File

@ -236,12 +236,14 @@ class WEIRegistrationsView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTable
pattern = self.request.GET.get("search", "") pattern = self.request.GET.get("search", "")
if not pattern: if not pattern:
qs = qs.filter( return qs.none()
Q(user__first_name__iregex=pattern)
| Q(user__last_name__iregex=pattern) qs = qs.filter(
| Q(user__note__alias__name__iregex="^" + pattern) Q(user__first_name__iregex=pattern)
| Q(user__note__alias__normalized_name__iregex="^" + Alias.normalize(pattern)) | Q(user__last_name__iregex=pattern)
) | Q(user__note__alias__name__iregex="^" + pattern)
| Q(user__note__alias__normalized_name__iregex="^" + Alias.normalize(pattern))
)
return qs[:20] return qs[:20]

View File

@ -217,7 +217,6 @@ function autoCompleteNote(field_id, note_list_id, notes, notes_display, alias_pr
// Clear search on click // Clear search on click
field.click(function () { field.click(function () {
field.tooltip('hide'); field.tooltip('hide');
field.removeClass('is-invalid');
field.val(""); field.val("");
}); });
@ -234,8 +233,6 @@ function autoCompleteNote(field_id, note_list_id, notes, notes_display, alias_pr
// When the user type something, the matched aliases are refreshed // When the user type something, the matched aliases are refreshed
field.keyup(function (e) { field.keyup(function (e) {
field.removeClass('is-invalid');
if (e.originalEvent.charCode === 13) if (e.originalEvent.charCode === 13)
return; return;

View File

@ -154,22 +154,6 @@ function reset() {
* Apply all transactions: all notes in `notes` buy each item in `buttons` * Apply all transactions: all notes in `notes` buy each item in `buttons`
*/ */
function consumeAll() { function consumeAll() {
let error = false;
if (notes_display.length === 0) {
$("#note").addClass('is-invalid');
$("#note_list").html(li("", "<strong>Ajoutez des émetteurs.</strong>", "text-danger"));
error = true;
}
if (buttons.length === 0) {
$("#consos_list").html(li("", "<strong>Ajoutez des consommations.</strong>", "text-danger"));
error = true;
}
if (error)
return;
notes_display.forEach(function(note_display) { notes_display.forEach(function(note_display) {
buttons.forEach(function(button) { buttons.forEach(function(button) {
consume(note_display.note, note_display.name, button.dest, button.quantity * note_display.quantity, button.amount, consume(note_display.note, note_display.name, button.dest, button.quantity * note_display.quantity, button.amount,

View File

@ -219,16 +219,6 @@ $("#btn_transfer").click(function() {
error = true; error = true;
} }
if (!sources_notes_display.length && !$("#type_credit").is(':checked')) {
$("#source_note").addClass('is-invalid');
error = true;
}
if (!dests_notes_display.length && !$("#type_debit").is(':checked')) {
$("#dest_note").addClass('is-invalid');
error = true;
}
if (error) if (error)
return; return;
@ -296,7 +286,6 @@ $("#btn_transfer").click(function() {
addMsg("Le transfert de " addMsg("Le transfert de "
+ pretty_money(source.quantity * dest.quantity * amount) + " de la note " + source.name + pretty_money(source.quantity * dest.quantity * amount) + " de la note " + source.name
+ " vers la note " + dest.name + " a échoué : Solde insuffisant", "danger", 10000); + " vers la note " + dest.name + " a échoué : Solde insuffisant", "danger", 10000);
reset();
}).fail(function (err) { }).fail(function (err) {
addMsg("Le transfert de " addMsg("Le transfert de "
+ pretty_money(source.quantity * dest.quantity * amount) + " de la note " + source.name + pretty_money(source.quantity * dest.quantity * amount) + " de la note " + source.name
@ -311,6 +300,11 @@ $("#btn_transfer").click(function() {
let given_reason = reason; let given_reason = reason;
let source_id, dest_id; let source_id, dest_id;
if ($("#type_credit").is(':checked')) { if ($("#type_credit").is(':checked')) {
if (!dests_notes_display.length) {
$("#dest_note").addClass('is-invalid');
return;
}
user_note = dests_notes_display[0].note.id; user_note = dests_notes_display[0].note.id;
source_id = special_note; source_id = special_note;
dest_id = user_note; dest_id = user_note;
@ -319,6 +313,11 @@ $("#btn_transfer").click(function() {
reason += " (" + given_reason + ")"; reason += " (" + given_reason + ")";
} }
else { else {
if (!sources_notes_display.length) {
$("#source_note").addClass('is-invalid');
return;
}
user_note = sources_notes_display[0].note.id; user_note = sources_notes_display[0].note.id;
source_id = user_note; source_id = user_note;
dest_id = special_note; dest_id = special_note;

View File

@ -53,9 +53,9 @@
</ul> </ul>
</div> </div>
<div class="card-footer text-center"> <div class="card-footer text-center">
<span id="consume_all" class="btn btn-primary"> <a id="consume_all" href="#" class="btn btn-primary">
{% trans "Consume!" %} {% trans "Consume!" %}
</span> </a>
</div> </div>
</div> </div>
</div> </div>