diff --git a/apps/treasury/signals.py b/apps/treasury/signals.py index 54c19c09..188be1a7 100644 --- a/apps/treasury/signals.py +++ b/apps/treasury/signals.py @@ -1,6 +1,7 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later +from note.models import NoteSpecial from treasury.models import SpecialTransactionProxy, RemittanceType @@ -8,5 +9,6 @@ def save_special_transaction(instance, created, **kwargs): """ When a special transaction is created, we create its linked proxy """ - if created and RemittanceType.objects.filter(note=instance.source).exists(): + if created and isinstance(instance.source, NoteSpecial) \ + and RemittanceType.objects.filter(note=instance.source).exists(): SpecialTransactionProxy.objects.create(transaction=instance, remittance=None).save() diff --git a/static/js/base.js b/static/js/base.js index 56c23c85..db12dae9 100644 --- a/static/js/base.js +++ b/static/js/base.js @@ -86,7 +86,7 @@ function getMatchedNotes(pattern, fun) { * Generate a
  • entry with a given id and text */ function li(id, text, extra_css) { - return "
  • " + text + "
  • \n"; } @@ -126,7 +126,7 @@ function displayNote(note, alias, user_note_field = null, profile_pic_field = nu if (user_note_field !== null) { $("#" + user_note_field).removeAttr('class'); $("#" + user_note_field).addClass(displayStyle(note)); - $("#" + user_note_field).text(alias + (note.balance == null ? "" : (":\n" + pretty_money(note.balance)))); + $("#" + user_note_field).text(alias + (note.balance == null ? "" : (" :\n" + pretty_money(note.balance)))); if (profile_pic_field != null) { $("#" + profile_pic_field).attr('src', img); $("#" + profile_pic_field).click(function () { @@ -162,7 +162,8 @@ function removeNote(d, note_prefix = "note", notes_display, note_list_id, user_n disp.quantity -= disp.id === d.id ? 1 : 0; new_notes_display.push(disp); html += li(note_prefix + "_" + disp.id, disp.name - + "" + disp.quantity + ""); + + "" + disp.quantity + "", + displayStyle(disp.note)); } }); @@ -186,7 +187,6 @@ function removeNote(d, note_prefix = "note", notes_display, note_list_id, user_n /** * Generate an auto-complete field to query a note with its alias * @param field_id The identifier of the text field where the alias is typed - * @param alias_matched_id The div block identifier where the matched aliases are displayed * @param note_list_id The div block identifier where the notes of the buyers are displayed * @param notes An array containing the note objects of the buyers * @param notes_display An array containing the infos of the buyers: [alias, note id, note object, quantity] @@ -200,11 +200,22 @@ function removeNote(d, note_prefix = "note", notes_display, note_list_id, user_n * the associated note is not displayed. * Useful for a consumption if the item is selected before. */ -function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes_display, alias_prefix = "alias", +function autoCompleteNote(field_id, note_list_id, notes, notes_display, alias_prefix = "alias", note_prefix = "note", user_note_field = null, profile_pic_field = null, alias_click = null) { let field = $("#" + field_id); - // When the user clicks on the search field, it is immediately cleared + + // Configure tooltip + field.tooltip({ + html: true, + placement: 'bottom', + title: 'Loading...', + trigger: 'manual', + container: field.parent() + }); + + // Clear search on click field.click(function () { + field.tooltip('hide'); field.val(""); }); @@ -213,7 +224,7 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes // When the user type "Enter", the first alias is clicked field.keypress(function (event) { if (event.originalEvent.charCode === 13 && notes.length > 0) { - let li_obj = $("#" + alias_matched_id + " li").first(); + let li_obj = field.parent().find("ul li").first(); displayNote(notes[0], li_obj.text(), user_note_field, profile_pic_field); li_obj.trigger("click"); } @@ -225,19 +236,16 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes return; let pattern = field.val(); + // If the pattern is not modified, we don't query the API if (pattern === old_pattern) return; old_pattern = pattern; notes.length = 0; - let aliases_matched_obj = $("#" + alias_matched_id); - let aliases_matched_html = ""; // get matched Alias with note associated if (pattern === "") { - aliases_matched_obj = $("#" + alias_matched_id); - aliases_matched_html = ""; - aliases_matched_obj.html("") + field.tooltip('hide'); notes.length = 0; return; } @@ -249,6 +257,9 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes // The response arrived too late, we stop the request if (pattern !== $("#" + field_id).val()) return; + + // Build tooltip content + let aliases_matched_html = ''; + + // Show tooltip + field.attr('data-original-title', aliases_matched_html).tooltip('show'); + consumers.results.forEach(function (consumer) { let note = consumer.note; let consumer_obj = $("#" + alias_prefix + "_" + consumer.id); @@ -266,8 +281,6 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes displayNote(consumer.note, consumer.name, user_note_field, profile_pic_field) }); consumer_obj.click(function () { - field.val(""); - old_pattern = ""; // reset input field var disp = null; notes_display.forEach(function (d) { // We compare the note ids @@ -299,12 +312,15 @@ function autoCompleteNote(field_id, alias_matched_id, note_list_id, notes, notes disp.name + "" + disp.quantity + "", - displayStyle(disp.note.balance)); + displayStyle(disp.note)); }); // Emitters are displayed note_list.html(html); + // Update tooltip position + field.tooltip('update'); + notes_display.forEach(function (disp) { let line_obj = $("#" + note_prefix + "_" + disp.id); // Hover an emitter display also the profile picture diff --git a/static/js/consos.js b/static/js/consos.js index 9450febc..2fa77249 100644 --- a/static/js/consos.js +++ b/static/js/consos.js @@ -24,13 +24,10 @@ $(document).ready(function() { }); // Switching in double consumptions mode should update the layout - let double_conso_obj = $("#double_conso"); - double_conso_obj.click(function() { - $("#consos_list_div").show(); - $("#infos_div").attr('class', 'col-sm-5 col-xl-6'); - $("#note_infos_div").attr('class', 'col-xl-3'); + $("#double_conso").click(function() { + $("#consos_list_div").removeClass('d-none'); $("#user_select_div").attr('class', 'col-xl-4'); - $("#buttons_div").attr('class', 'col-sm-7 col-xl-6'); + $("#infos_div").attr('class', 'col-sm-5 col-xl-6'); let note_list_obj = $("#note_list"); if (buttons.length > 0 && note_list_obj.text().length > 0) { @@ -44,13 +41,10 @@ $(document).ready(function() { } }); - let single_conso_obj = $("#single_conso"); - single_conso_obj.click(function() { - $("#consos_list_div").hide(); - $("#infos_div").attr('class', 'col-sm-5 col-md-4'); - $("#note_infos_div").attr('class', 'col-xl-5'); + $("#single_conso").click(function() { + $("#consos_list_div").addClass('d-none'); $("#user_select_div").attr('class', 'col-xl-7'); - $("#buttons_div").attr('class', 'col-sm-7 col-md-8'); + $("#infos_div").attr('class', 'col-sm-5 col-md-4'); let consos_list_obj = $("#consos_list"); if (buttons.length > 0) { @@ -69,12 +63,8 @@ $(document).ready(function() { } }); - // Ensure we begin in single consumption. Removing these lines may cause problems when reloading. - single_conso_obj.prop('checked', 'true'); - double_conso_obj.removeAttr('checked'); - $("label[for='double_conso']").attr('class', 'btn btn-sm btn-outline-primary'); - - $("#consos_list_div").hide(); + // Ensure we begin in single consumption. Fix issue with TurboLinks and BootstrapJS + $("label[for='double_conso']").removeClass('active'); $("#consume_all").click(consumeAll); }); @@ -84,7 +74,7 @@ notes_display = []; buttons = []; // When the user searches an alias, we update the auto-completion -autoCompleteNote("note", "alias_matched", "note_list", notes, notes_display, +autoCompleteNote("note", "note_list", notes, notes_display, "alias", "note", "user_note", "profile_pic", function() { if (buttons.length > 0 && $("#single_conso").is(":checked")) { consumeAll(); @@ -152,7 +142,6 @@ function reset() { notes.length = 0; buttons.length = 0; $("#note_list").html(""); - $("#alias_matched").html(""); $("#consos_list").html(""); $("#user_note").text(""); $("#profile_pic").attr("src", "/media/pic/default.png"); diff --git a/static/js/transfer.js b/static/js/transfer.js index 293e5071..78a8ca08 100644 --- a/static/js/transfer.js +++ b/static/js/transfer.js @@ -14,8 +14,6 @@ function reset() { dests.length = 0; $("#source_note_list").html(""); $("#dest_note_list").html(""); - $("#source_alias_matched").html(""); - $("#dest_alias_matched").html(""); $("#amount").val(""); $("#reason").val(""); $("#last_name").val(""); @@ -28,37 +26,110 @@ function reset() { } $(document).ready(function() { - autoCompleteNote("source_note", "source_alias_matched", "source_note_list", sources, sources_notes_display, - "source_alias", "source_note", "user_note", "profile_pic"); - autoCompleteNote("dest_note", "dest_alias_matched", "dest_note_list", dests, dests_notes_display, - "dest_alias", "dest_note", "user_note", "profile_pic", function() { - if ($("#type_credit").is(":checked") || $("#type_debit").is(":checked")) { - let last = dests_notes_display[dests_notes_display.length - 1]; - dests_notes_display.length = 0; - dests_notes_display.push(last); + /** + * If we are in credit/debit mode, check that only one note is entered. + * More over, get first name and last name to autocomplete fields. + */ + function checkUniqueNote() { + if ($("#type_credit").is(":checked") || $("#type_debit").is(":checked")) { + let arr = $("#type_credit").is(":checked") ? dests_notes_display : sources_notes_display; - last.quantity = 1; + if (arr.length === 0) + return; - if (!last.note.user) { - $.getJSON("/api/note/note/" + last.note.id + "/?format=json", function(note) { - last.note.user = note.user; - $.getJSON("/api/user/" + last.note.user + "/", function(user) { - $("#last_name").val(user.last_name); - $("#first_name").val(user.first_name); - }); - }); - } - else { + let last = arr[arr.length - 1]; + arr.length = 0; + arr.push(last); + + last.quantity = 1; + + if (!last.note.user) { + $.getJSON("/api/note/note/" + last.note.id + "/?format=json", function(note) { + last.note.user = note.user; $.getJSON("/api/user/" + last.note.user + "/", function(user) { $("#last_name").val(user.last_name); $("#first_name").val(user.first_name); }); - } + }); } + else { + $.getJSON("/api/user/" + last.note.user + "/", function(user) { + $("#last_name").val(user.last_name); + $("#first_name").val(user.first_name); + }); + } + } - return true; - }); + return true; + } + autoCompleteNote("source_note", "source_note_list", sources, sources_notes_display, + "source_alias", "source_note", "user_note", "profile_pic", checkUniqueNote); + autoCompleteNote("dest_note", "dest_note_list", dests, dests_notes_display, + "dest_alias", "dest_note", "user_note", "profile_pic", checkUniqueNote); + + let source = $("#source_note"); + let dest = $("#dest_note"); + + $("#type_gift").click(function() { + $("#special_transaction_div").addClass('d-none'); + source.attr('disabled', true); + source.val(username); + source.tooltip('hide'); + $("#source_note_list").addClass('d-none'); + dest.attr('disabled', false); + $("#dest_note_list").removeClass('d-none'); + }); + + $("#type_transfer").click(function() { + $("#special_transaction_div").addClass('d-none'); + source.attr('disabled', false); + $("#source_note_list").removeClass('d-none'); + dest.attr('disabled', false); + $("#dest_note_list").removeClass('d-none'); + }); + + $("#type_credit").click(function() { + $("#special_transaction_div").removeClass('d-none'); + $("#source_note_list").addClass('d-none'); + $("#dest_note_list").removeClass('d-none'); + source.attr('disabled', true); + source.val($("#credit_type option:selected").text()); + source.tooltip('hide'); + dest.attr('disabled', false); + dest.val(''); + dest.tooltip('hide'); + + if (dests_notes_display.length > 1) { + $("#dest_note_list").html(''); + dests_notes_display.length = 0; + } + }); + + $("#type_debit").click(function() { + $("#special_transaction_div").removeClass('d-none'); + $("#source_note_list").removeClass('d-none'); + $("#dest_note_list").addClass('d-none'); + source.attr('disabled', false); + source.val(''); + source.tooltip('hide'); + dest.attr('disabled', true); + dest.val($("#credit_type option:selected").text()); + dest.tooltip('hide'); + + if (sources_notes_display.length > 1) { + $("#source_note_list").html(''); + sources_notes_display.length = 0; + } + }); + + $("#credit_type").change(function() { + let type = $("#credit_type option:selected").text(); + if ($("#type_credit").is(":checked")) + source.val(type); + else + dest.val(type); + }); // Ensure we begin in gift mode. Removing these lines may cause problems when reloading. let type_gift = $("#type_gift"); // Default mode @@ -184,10 +255,11 @@ $("#btn_transfer").click(function() { }); } else if ($("#type_credit").is(':checked') || $("#type_debit").is(':checked')) { let special_note = $("#credit_type").val(); - let user_note = dests_notes_display[0].note.id; + let user_note; let given_reason = $("#reason").val(); let source, dest, reason; if ($("#type_credit").is(':checked')) { + user_note = dests_notes_display[0].note.id; source = special_note; dest = user_note; reason = "Crédit " + $("#credit_type option:selected").text().toLowerCase(); @@ -195,6 +267,7 @@ $("#btn_transfer").click(function() { reason += " (" + given_reason + ")"; } else { + user_note = sources_notes_display[0].note.id; source = user_note; dest = special_note; reason = "Retrait " + $("#credit_type option:selected").text().toLowerCase(); diff --git a/templates/base.html b/templates/base.html index 3c2c637f..7cfc474b 100644 --- a/templates/base.html +++ b/templates/base.html @@ -32,7 +32,8 @@ SPDX-License-Identifier: GPL-3.0-or-later integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous"> + href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" + crossorigin="anonymous"> {# JQuery, Bootstrap and Turbolinks JavaScript #} + {% endblock %}