mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-11-04 01:12:08 +01:00 
			
		
		
		
	Format JS files
This commit is contained in:
		@@ -1,57 +1,53 @@
 | 
			
		||||
$(document).ready(function () {
 | 
			
		||||
    $(".autocomplete").keyup(function(e) {
 | 
			
		||||
        let target = $("#" + e.target.id);
 | 
			
		||||
        let prefix = target.attr("id");
 | 
			
		||||
        let api_url = target.attr("api_url");
 | 
			
		||||
        let api_url_suffix = target.attr("api_url_suffix");
 | 
			
		||||
        if (!api_url_suffix)
 | 
			
		||||
            api_url_suffix = "";
 | 
			
		||||
        let name_field = target.attr("name_field");
 | 
			
		||||
        if (!name_field)
 | 
			
		||||
            name_field = "name";
 | 
			
		||||
        let input = target.val();
 | 
			
		||||
        target.addClass("is-invalid");
 | 
			
		||||
        target.removeClass("is-valid");
 | 
			
		||||
        $("#" + prefix + "_reset").removeClass("d-none");
 | 
			
		||||
  $('.autocomplete').keyup(function (e) {
 | 
			
		||||
    const target = $('#' + e.target.id)
 | 
			
		||||
    const prefix = target.attr('id')
 | 
			
		||||
    const api_url = target.attr('api_url')
 | 
			
		||||
    let api_url_suffix = target.attr('api_url_suffix')
 | 
			
		||||
    if (!api_url_suffix) { api_url_suffix = '' }
 | 
			
		||||
    let name_field = target.attr('name_field')
 | 
			
		||||
    if (!name_field) { name_field = 'name' }
 | 
			
		||||
    const input = target.val()
 | 
			
		||||
    target.addClass('is-invalid')
 | 
			
		||||
    target.removeClass('is-valid')
 | 
			
		||||
    $('#' + prefix + '_reset').removeClass('d-none')
 | 
			
		||||
 | 
			
		||||
        $.getJSON(api_url + (api_url.includes("?") ? "&" : "?") + "format=json&search=^" + input + api_url_suffix, function(objects) {
 | 
			
		||||
            let html = "";
 | 
			
		||||
    $.getJSON(api_url + (api_url.includes('?') ? '&' : '?') + 'format=json&search=^' + input + api_url_suffix, function (objects) {
 | 
			
		||||
      let html = ''
 | 
			
		||||
 | 
			
		||||
            objects.results.forEach(function (obj) {
 | 
			
		||||
                html += li(prefix + "_" + obj.id, obj[name_field]);
 | 
			
		||||
            });
 | 
			
		||||
      objects.results.forEach(function (obj) {
 | 
			
		||||
        html += li(prefix + '_' + obj.id, obj[name_field])
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
            let results_list = $("#" + prefix + "_list");
 | 
			
		||||
            results_list.html(html);
 | 
			
		||||
      const results_list = $('#' + prefix + '_list')
 | 
			
		||||
      results_list.html(html)
 | 
			
		||||
 | 
			
		||||
            objects.results.forEach(function (obj) {
 | 
			
		||||
                $("#" + prefix + "_" + obj.id).click(function() {
 | 
			
		||||
                    target.val(obj[name_field]);
 | 
			
		||||
                    $("#" + prefix + "_pk").val(obj.id);
 | 
			
		||||
      objects.results.forEach(function (obj) {
 | 
			
		||||
        $('#' + prefix + '_' + obj.id).click(function () {
 | 
			
		||||
          target.val(obj[name_field])
 | 
			
		||||
          $('#' + prefix + '_pk').val(obj.id)
 | 
			
		||||
 | 
			
		||||
                    results_list.html("");
 | 
			
		||||
                    target.removeClass("is-invalid");
 | 
			
		||||
                    target.addClass("is-valid");
 | 
			
		||||
          results_list.html('')
 | 
			
		||||
          target.removeClass('is-invalid')
 | 
			
		||||
          target.addClass('is-valid')
 | 
			
		||||
 | 
			
		||||
                    if (typeof autocompleted != 'undefined')
 | 
			
		||||
                        autocompleted(obj, prefix)
 | 
			
		||||
                });
 | 
			
		||||
          if (typeof autocompleted !== 'undefined') { autocompleted(obj, prefix) }
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
                if (input === obj[name_field])
 | 
			
		||||
                    $("#" + prefix + "_pk").val(obj.id);
 | 
			
		||||
            });
 | 
			
		||||
        if (input === obj[name_field]) { $('#' + prefix + '_pk').val(obj.id) }
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
            if (results_list.children().length === 1 && e.originalEvent.keyCode >= 32) {
 | 
			
		||||
                results_list.children().first().trigger("click");
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
      if (results_list.children().length === 1 && e.originalEvent.keyCode >= 32) {
 | 
			
		||||
        results_list.children().first().trigger('click')
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
    $(".autocomplete-reset").click(function() {
 | 
			
		||||
        let name = $(this).attr("id").replace("_reset", "");
 | 
			
		||||
        $("#" + name + "_pk").val("");
 | 
			
		||||
        $("#" + name).val("");
 | 
			
		||||
        $("#" + name + "_list").html("");
 | 
			
		||||
        $(this).addClass("d-none");
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
  $('.autocomplete-reset').click(function () {
 | 
			
		||||
    const name = $(this).attr('id').replace('_reset', '')
 | 
			
		||||
    $('#' + name + '_pk').val('')
 | 
			
		||||
    $('#' + name).val('')
 | 
			
		||||
    $('#' + name + '_list').html('')
 | 
			
		||||
    $(this).addClass('d-none')
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -1,18 +1,16 @@
 | 
			
		||||
// Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
			
		||||
// SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Convert balance in cents to a human readable amount
 | 
			
		||||
 * @param value the balance, in cents
 | 
			
		||||
 * @returns {string}
 | 
			
		||||
 */
 | 
			
		||||
function pretty_money (value) {
 | 
			
		||||
    if (value % 100 === 0)
 | 
			
		||||
        return (value < 0 ? "- " : "") + Math.floor(Math.abs(value) / 100) + " €";
 | 
			
		||||
    else
 | 
			
		||||
        return (value < 0 ? "- " : "") + Math.floor(Math.abs(value) / 100) + "."
 | 
			
		||||
            + (Math.abs(value) % 100 < 10 ? "0" : "") + (Math.abs(value) % 100) + " €";
 | 
			
		||||
  if (value % 100 === 0) { return (value < 0 ? '- ' : '') + Math.floor(Math.abs(value) / 100) + ' €' } else {
 | 
			
		||||
    return (value < 0 ? '- ' : '') + Math.floor(Math.abs(value) / 100) + '.' +
 | 
			
		||||
            (Math.abs(value) % 100 < 10 ? '0' : '') + (Math.abs(value) % 100) + ' €'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -22,19 +20,19 @@ function pretty_money (value) {
 | 
			
		||||
 * @param timeout The delay (in millis) after that the message is auto-closed. If negative, then it is ignored.
 | 
			
		||||
 */
 | 
			
		||||
function addMsg (msg, alert_type, timeout = -1) {
 | 
			
		||||
    let msgDiv = $("#messages");
 | 
			
		||||
    let html = msgDiv.html();
 | 
			
		||||
    let id = Math.floor(10000 * Math.random() + 1);
 | 
			
		||||
    html += "<div class=\"alert alert-" + alert_type + " alert-dismissible\">" +
 | 
			
		||||
        "<button id=\"close-message-" + id + "\" class=\"close\" data-dismiss=\"alert\" href=\"#\"><span aria-hidden=\"true\">×</span></button>"
 | 
			
		||||
        + msg + "</div>\n";
 | 
			
		||||
    msgDiv.html(html);
 | 
			
		||||
  const msgDiv = $('#messages')
 | 
			
		||||
  let html = msgDiv.html()
 | 
			
		||||
  const id = Math.floor(10000 * Math.random() + 1)
 | 
			
		||||
  html += '<div class="alert alert-' + alert_type + ' alert-dismissible">' +
 | 
			
		||||
        '<button id="close-message-' + id + '" class="close" data-dismiss="alert" href="#"><span aria-hidden="true">×</span></button>' +
 | 
			
		||||
        msg + '</div>\n'
 | 
			
		||||
  msgDiv.html(html)
 | 
			
		||||
 | 
			
		||||
    if (timeout > 0) {
 | 
			
		||||
        setTimeout(function () {
 | 
			
		||||
            $("#close-message-" + id).click();
 | 
			
		||||
        }, timeout);
 | 
			
		||||
    }
 | 
			
		||||
  if (timeout > 0) {
 | 
			
		||||
    setTimeout(function () {
 | 
			
		||||
      $('#close-message-' + id).click()
 | 
			
		||||
    }, timeout)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -43,34 +41,34 @@ function addMsg (msg, alert_type, timeout = -1) {
 | 
			
		||||
 * @param timeout The delay (in millis) after that the message is auto-closed. If negative, then it is ignored.
 | 
			
		||||
 */
 | 
			
		||||
function errMsg (errs_obj, timeout = -1) {
 | 
			
		||||
    for (const err_msg of Object.values(errs_obj)) {
 | 
			
		||||
        addMsg(err_msg, 'danger', timeout);
 | 
			
		||||
    }
 | 
			
		||||
  for (const err_msg of Object.values(errs_obj)) {
 | 
			
		||||
    addMsg(err_msg, 'danger', timeout)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var reloadWithTurbolinks = (function () {
 | 
			
		||||
    var scrollPosition;
 | 
			
		||||
  var scrollPosition
 | 
			
		||||
 | 
			
		||||
    function reload () {
 | 
			
		||||
        scrollPosition = [window.scrollX, window.scrollY];
 | 
			
		||||
        Turbolinks.visit(window.location.toString(), { action: 'replace' })
 | 
			
		||||
  function reload () {
 | 
			
		||||
    scrollPosition = [window.scrollX, window.scrollY]
 | 
			
		||||
    Turbolinks.visit(window.location.toString(), { action: 'replace' })
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  document.addEventListener('turbolinks:load', function () {
 | 
			
		||||
    if (scrollPosition) {
 | 
			
		||||
      window.scrollTo.apply(window, scrollPosition)
 | 
			
		||||
      scrollPosition = null
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
    document.addEventListener('turbolinks:load', function () {
 | 
			
		||||
        if (scrollPosition) {
 | 
			
		||||
            window.scrollTo.apply(window, scrollPosition);
 | 
			
		||||
            scrollPosition = null
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    return reload;
 | 
			
		||||
})();
 | 
			
		||||
  return reload
 | 
			
		||||
})()
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Reload the balance of the user on the right top corner
 | 
			
		||||
 */
 | 
			
		||||
function refreshBalance () {
 | 
			
		||||
    $("#user_balance").load("/ #user_balance");
 | 
			
		||||
  $('#user_balance').load('/ #user_balance')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -79,15 +77,15 @@ function refreshBalance () {
 | 
			
		||||
 * @param fun For each found note with the matched alias `alias`, fun(note, alias) is called.
 | 
			
		||||
 */
 | 
			
		||||
function getMatchedNotes (pattern, fun) {
 | 
			
		||||
    $.getJSON("/api/note/alias/?format=json&alias=" + pattern + "&search=user|club", fun);
 | 
			
		||||
  $.getJSON('/api/note/alias/?format=json&alias=' + pattern + '&search=user|club', fun)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Generate a <li> entry with a given id and text
 | 
			
		||||
 */
 | 
			
		||||
function li (id, text, extra_css) {
 | 
			
		||||
    return "<li class=\"list-group-item py-1 px-2 d-flex justify-content-between align-items-center text-truncate "
 | 
			
		||||
        + (extra_css ? extra_css : "") + "\"" + " id=\"" + id + "\">" + text + "</li>\n";
 | 
			
		||||
  return '<li class="list-group-item py-1 px-2 d-flex justify-content-between align-items-center text-truncate ' +
 | 
			
		||||
        (extra_css || '') + '"' + ' id="' + id + '">' + text + '</li>\n'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -95,24 +93,13 @@ function li (id, text, extra_css) {
 | 
			
		||||
 * @param note The concerned note.
 | 
			
		||||
 */
 | 
			
		||||
function displayStyle (note) {
 | 
			
		||||
    if (!note)
 | 
			
		||||
        return "";
 | 
			
		||||
    let balance = note.balance;
 | 
			
		||||
    var css = "";
 | 
			
		||||
    if (balance < -5000)
 | 
			
		||||
        css += " text-danger bg-dark";
 | 
			
		||||
    else if (balance < -1000)
 | 
			
		||||
        css += " text-danger";
 | 
			
		||||
    else if (balance < 0)
 | 
			
		||||
        css += " text-warning";
 | 
			
		||||
    else if (!note.email_confirmed)
 | 
			
		||||
        css += " text-white bg-primary";
 | 
			
		||||
    else if (!note.is_active || (note.membership && note.membership.date_end < new Date().toISOString()))
 | 
			
		||||
        css += "text-white bg-info";
 | 
			
		||||
    return css;
 | 
			
		||||
  if (!note) { return '' }
 | 
			
		||||
  const balance = note.balance
 | 
			
		||||
  var css = ''
 | 
			
		||||
  if (balance < -5000) { css += ' text-danger bg-dark' } else if (balance < -1000) { css += ' text-danger' } else if (balance < 0) { css += ' text-warning' } else if (!note.email_confirmed) { css += ' text-white bg-primary' } else if (!note.is_active || (note.membership && note.membership.date_end < new Date().toISOString())) { css += 'text-white bg-info' }
 | 
			
		||||
  return css
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Render note name and picture
 | 
			
		||||
 * @param note The note to render
 | 
			
		||||
@@ -121,23 +108,22 @@ function displayStyle (note) {
 | 
			
		||||
 * @param profile_pic_field
 | 
			
		||||
 */
 | 
			
		||||
function displayNote (note, alias, user_note_field = null, profile_pic_field = null) {
 | 
			
		||||
    if (!note.display_image) {
 | 
			
		||||
        note.display_image = '/static/member/img/default_picture.png';
 | 
			
		||||
    }
 | 
			
		||||
    let img = note.display_image;
 | 
			
		||||
    if (alias !== note.name && note.name)
 | 
			
		||||
        alias += " (aka. " + note.name + ")";
 | 
			
		||||
    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))));
 | 
			
		||||
        if (profile_pic_field != null) {
 | 
			
		||||
            $("#" + profile_pic_field).attr('src', img);
 | 
			
		||||
            $("#" + profile_pic_field + "_link").attr('href', note.resourcetype === "NoteUser" ?
 | 
			
		||||
                "/accounts/user/" + note.user : note.resourcetype === "NoteClub" ?
 | 
			
		||||
                    "/accounts/club/" + note.club : "#");
 | 
			
		||||
        }
 | 
			
		||||
  if (!note.display_image) {
 | 
			
		||||
    note.display_image = '/static/member/img/default_picture.png'
 | 
			
		||||
  }
 | 
			
		||||
  const img = note.display_image
 | 
			
		||||
  if (alias !== note.name && note.name) { alias += ' (aka. ' + note.name + ')' }
 | 
			
		||||
  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))))
 | 
			
		||||
    if (profile_pic_field != null) {
 | 
			
		||||
      $('#' + profile_pic_field).attr('src', img)
 | 
			
		||||
      $('#' + profile_pic_field + '_link').attr('href', note.resourcetype === 'NoteUser'
 | 
			
		||||
        ? '/accounts/user/' + note.user : note.resourcetype === 'NoteClub'
 | 
			
		||||
          ? '/accounts/club/' + note.club : '#')
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -152,35 +138,34 @@ function displayNote (note, alias, user_note_field = null, profile_pic_field = n
 | 
			
		||||
 *                          (useful in consumptions, put null if not used)
 | 
			
		||||
 * @returns an anonymous function to be compatible with jQuery events
 | 
			
		||||
 */
 | 
			
		||||
function removeNote (d, note_prefix = "note", notes_display, note_list_id, user_note_field = null, profile_pic_field = null) {
 | 
			
		||||
    return (function () {
 | 
			
		||||
        let new_notes_display = [];
 | 
			
		||||
        let html = "";
 | 
			
		||||
        notes_display.forEach(function (disp) {
 | 
			
		||||
            if (disp.quantity > 1 || disp.id !== d.id) {
 | 
			
		||||
                disp.quantity -= disp.id === d.id ? 1 : 0;
 | 
			
		||||
                new_notes_display.push(disp);
 | 
			
		||||
                html += li(note_prefix + "_" + disp.id, disp.name
 | 
			
		||||
                    + "<span class=\"badge badge-dark badge-pill\">" + disp.quantity + "</span>",
 | 
			
		||||
                    displayStyle(disp.note));
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
function removeNote (d, note_prefix = 'note', notes_display, note_list_id, user_note_field = null, profile_pic_field = null) {
 | 
			
		||||
  return function () {
 | 
			
		||||
    const new_notes_display = []
 | 
			
		||||
    let html = ''
 | 
			
		||||
    notes_display.forEach(function (disp) {
 | 
			
		||||
      if (disp.quantity > 1 || disp.id !== d.id) {
 | 
			
		||||
        disp.quantity -= disp.id === d.id ? 1 : 0
 | 
			
		||||
        new_notes_display.push(disp)
 | 
			
		||||
        html += li(note_prefix + '_' + disp.id, disp.name +
 | 
			
		||||
                    '<span class="badge badge-dark badge-pill">' + disp.quantity + '</span>',
 | 
			
		||||
        displayStyle(disp.note))
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
        notes_display.length = 0;
 | 
			
		||||
        new_notes_display.forEach(function (disp) {
 | 
			
		||||
            notes_display.push(disp);
 | 
			
		||||
        });
 | 
			
		||||
    notes_display.length = 0
 | 
			
		||||
    new_notes_display.forEach(function (disp) {
 | 
			
		||||
      notes_display.push(disp)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
        $("#" + note_list_id).html(html);
 | 
			
		||||
        notes_display.forEach(function (disp) {
 | 
			
		||||
            let obj = $("#" + note_prefix + "_" + disp.id);
 | 
			
		||||
            obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field, profile_pic_field));
 | 
			
		||||
            obj.hover(function () {
 | 
			
		||||
                if (disp.note)
 | 
			
		||||
                    displayNote(disp.note, disp.name, user_note_field, profile_pic_field);
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
    $('#' + note_list_id).html(html)
 | 
			
		||||
    notes_display.forEach(function (disp) {
 | 
			
		||||
      const obj = $('#' + note_prefix + '_' + disp.id)
 | 
			
		||||
      obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field, profile_pic_field))
 | 
			
		||||
      obj.hover(function () {
 | 
			
		||||
        if (disp.note) { displayNote(disp.note, disp.name, user_note_field, profile_pic_field) }
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -199,203 +184,193 @@ function removeNote (d, note_prefix = "note", notes_display, note_list_id, user_
 | 
			
		||||
 *                    the associated note is not displayed.
 | 
			
		||||
 *                    Useful for a consumption if the item is selected before.
 | 
			
		||||
 */
 | 
			
		||||
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);
 | 
			
		||||
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) {
 | 
			
		||||
  const field = $('#' + field_id)
 | 
			
		||||
 | 
			
		||||
    // Configure tooltip
 | 
			
		||||
    field.tooltip({
 | 
			
		||||
        html: true,
 | 
			
		||||
        placement: 'bottom',
 | 
			
		||||
        title: 'Loading...',
 | 
			
		||||
        trigger: 'manual',
 | 
			
		||||
        container: field.parent(),
 | 
			
		||||
        fallbackPlacement: 'clockwise'
 | 
			
		||||
    });
 | 
			
		||||
  // Configure tooltip
 | 
			
		||||
  field.tooltip({
 | 
			
		||||
    html: true,
 | 
			
		||||
    placement: 'bottom',
 | 
			
		||||
    title: 'Loading...',
 | 
			
		||||
    trigger: 'manual',
 | 
			
		||||
    container: field.parent(),
 | 
			
		||||
    fallbackPlacement: 'clockwise'
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
    // When the user clicks elsewhere, we hide the tooltip
 | 
			
		||||
    $(document).click(function(e) {
 | 
			
		||||
        if (!e.target.id.startsWith(alias_prefix)) {
 | 
			
		||||
            field.tooltip("hide");
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
  // When the user clicks elsewhere, we hide the tooltip
 | 
			
		||||
  $(document).click(function (e) {
 | 
			
		||||
    if (!e.target.id.startsWith(alias_prefix)) {
 | 
			
		||||
      field.tooltip('hide')
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
    let old_pattern = null;
 | 
			
		||||
  let old_pattern = null
 | 
			
		||||
 | 
			
		||||
    // Clear search on click
 | 
			
		||||
    field.click(function () {
 | 
			
		||||
        field.tooltip('hide');
 | 
			
		||||
        field.removeClass('is-invalid');
 | 
			
		||||
        field.val("");
 | 
			
		||||
        old_pattern = "";
 | 
			
		||||
    });
 | 
			
		||||
  // Clear search on click
 | 
			
		||||
  field.click(function () {
 | 
			
		||||
    field.tooltip('hide')
 | 
			
		||||
    field.removeClass('is-invalid')
 | 
			
		||||
    field.val('')
 | 
			
		||||
    old_pattern = ''
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
    // 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 = field.parent().find("ul li").first();
 | 
			
		||||
            displayNote(notes[0], li_obj.text(), user_note_field, profile_pic_field);
 | 
			
		||||
            li_obj.trigger("click");
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
  // When the user type "Enter", the first alias is clicked
 | 
			
		||||
  field.keypress(function (event) {
 | 
			
		||||
    if (event.originalEvent.charCode === 13 && notes.length > 0) {
 | 
			
		||||
      const li_obj = field.parent().find('ul li').first()
 | 
			
		||||
      displayNote(notes[0], li_obj.text(), user_note_field, profile_pic_field)
 | 
			
		||||
      li_obj.trigger('click')
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
    // When the user type something, the matched aliases are refreshed
 | 
			
		||||
    field.keyup(function (e) {
 | 
			
		||||
        field.removeClass('is-invalid');
 | 
			
		||||
  // When the user type something, the matched aliases are refreshed
 | 
			
		||||
  field.keyup(function (e) {
 | 
			
		||||
    field.removeClass('is-invalid')
 | 
			
		||||
 | 
			
		||||
        if (e.originalEvent.charCode === 13)
 | 
			
		||||
            return;
 | 
			
		||||
    if (e.originalEvent.charCode === 13) { return }
 | 
			
		||||
 | 
			
		||||
        let pattern = field.val();
 | 
			
		||||
    const 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;
 | 
			
		||||
    // If the pattern is not modified, we don't query the API
 | 
			
		||||
    if (pattern === old_pattern) { return }
 | 
			
		||||
    old_pattern = pattern
 | 
			
		||||
    notes.length = 0
 | 
			
		||||
 | 
			
		||||
        // get matched Alias with note associated
 | 
			
		||||
        if (pattern === "") {
 | 
			
		||||
            field.tooltip('hide');
 | 
			
		||||
            notes.length = 0;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    // get matched Alias with note associated
 | 
			
		||||
    if (pattern === '') {
 | 
			
		||||
      field.tooltip('hide')
 | 
			
		||||
      notes.length = 0
 | 
			
		||||
      return
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        $.getJSON("/api/note/consumer/?format=json&alias=" + pattern + "&search=user|club",
 | 
			
		||||
            function (consumers) {
 | 
			
		||||
                // The response arrived too late, we stop the request
 | 
			
		||||
                if (pattern !== $("#" + field_id).val())
 | 
			
		||||
                    return;
 | 
			
		||||
    $.getJSON('/api/note/consumer/?format=json&alias=' + pattern + '&search=user|club',
 | 
			
		||||
      function (consumers) {
 | 
			
		||||
        // The response arrived too late, we stop the request
 | 
			
		||||
        if (pattern !== $('#' + field_id).val()) { return }
 | 
			
		||||
 | 
			
		||||
                // Build tooltip content
 | 
			
		||||
                let aliases_matched_html = '<ul class="list-group list-group-flush">';
 | 
			
		||||
                consumers.results.forEach(function (consumer) {
 | 
			
		||||
                    let note = consumer.note;
 | 
			
		||||
                    note.email_confirmed = consumer.email_confirmed;
 | 
			
		||||
                    if (consumer.hasOwnProperty("membership") && consumer.membership)
 | 
			
		||||
                        note.membership = consumer.membership;
 | 
			
		||||
                    else
 | 
			
		||||
                        note.membership = undefined;
 | 
			
		||||
                    let extra_css = displayStyle(note);
 | 
			
		||||
                    aliases_matched_html += li(alias_prefix + '_' + consumer.id,
 | 
			
		||||
                        consumer.name,
 | 
			
		||||
                        extra_css);
 | 
			
		||||
                    notes.push(note);
 | 
			
		||||
                });
 | 
			
		||||
                aliases_matched_html += '</ul>';
 | 
			
		||||
        // Build tooltip content
 | 
			
		||||
        let aliases_matched_html = '<ul class="list-group list-group-flush">'
 | 
			
		||||
        consumers.results.forEach(function (consumer) {
 | 
			
		||||
          const note = consumer.note
 | 
			
		||||
          note.email_confirmed = consumer.email_confirmed
 | 
			
		||||
          if (consumer.hasOwnProperty('membership') && consumer.membership) { note.membership = consumer.membership } else { note.membership = undefined }
 | 
			
		||||
          const extra_css = displayStyle(note)
 | 
			
		||||
          aliases_matched_html += li(alias_prefix + '_' + consumer.id,
 | 
			
		||||
            consumer.name,
 | 
			
		||||
            extra_css)
 | 
			
		||||
          notes.push(note)
 | 
			
		||||
        })
 | 
			
		||||
        aliases_matched_html += '</ul>'
 | 
			
		||||
 | 
			
		||||
                // Show tooltip
 | 
			
		||||
                field.attr('data-original-title', aliases_matched_html).tooltip('show');
 | 
			
		||||
        // Show tooltip
 | 
			
		||||
        field.attr('data-original-title', aliases_matched_html).tooltip('show')
 | 
			
		||||
 | 
			
		||||
                consumers.results.forEach(function (consumer) {
 | 
			
		||||
                    let consumer_obj = $("#" + alias_prefix + "_" + consumer.id);
 | 
			
		||||
                    consumer_obj.hover(function () {
 | 
			
		||||
                        displayNote(consumer.note, consumer.name, user_note_field, profile_pic_field)
 | 
			
		||||
                    });
 | 
			
		||||
                    consumer_obj.click(function () {
 | 
			
		||||
                        var disp = null;
 | 
			
		||||
                        notes_display.forEach(function (d) {
 | 
			
		||||
                            // We compare the alias ids
 | 
			
		||||
                            if (d.id === consumer.id) {
 | 
			
		||||
                                d.quantity += 1;
 | 
			
		||||
                                disp = d;
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                        // In the other case, we add a new emitter
 | 
			
		||||
                        if (disp == null) {
 | 
			
		||||
                            disp = {
 | 
			
		||||
                                name: consumer.name,
 | 
			
		||||
                                id: consumer.id,
 | 
			
		||||
                                note: consumer.note,
 | 
			
		||||
                                quantity: 1
 | 
			
		||||
                            };
 | 
			
		||||
                            notes_display.push(disp);
 | 
			
		||||
                        }
 | 
			
		||||
        consumers.results.forEach(function (consumer) {
 | 
			
		||||
          const consumer_obj = $('#' + alias_prefix + '_' + consumer.id)
 | 
			
		||||
          consumer_obj.hover(function () {
 | 
			
		||||
            displayNote(consumer.note, consumer.name, user_note_field, profile_pic_field)
 | 
			
		||||
          })
 | 
			
		||||
          consumer_obj.click(function () {
 | 
			
		||||
            var disp = null
 | 
			
		||||
            notes_display.forEach(function (d) {
 | 
			
		||||
              // We compare the alias ids
 | 
			
		||||
              if (d.id === consumer.id) {
 | 
			
		||||
                d.quantity += 1
 | 
			
		||||
                disp = d
 | 
			
		||||
              }
 | 
			
		||||
            })
 | 
			
		||||
            // In the other case, we add a new emitter
 | 
			
		||||
            if (disp == null) {
 | 
			
		||||
              disp = {
 | 
			
		||||
                name: consumer.name,
 | 
			
		||||
                id: consumer.id,
 | 
			
		||||
                note: consumer.note,
 | 
			
		||||
                quantity: 1
 | 
			
		||||
              }
 | 
			
		||||
              notes_display.push(disp)
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
                        // If the function alias_click exists, it is called. If it doesn't return true, then the notes are
 | 
			
		||||
                        // note displayed. Useful for a consumption when a button is already clicked
 | 
			
		||||
                        if (alias_click && !alias_click())
 | 
			
		||||
                            return;
 | 
			
		||||
            // If the function alias_click exists, it is called. If it doesn't return true, then the notes are
 | 
			
		||||
            // note displayed. Useful for a consumption when a button is already clicked
 | 
			
		||||
            if (alias_click && !alias_click()) { return }
 | 
			
		||||
 | 
			
		||||
                        let note_list = $("#" + note_list_id);
 | 
			
		||||
                        let html = "";
 | 
			
		||||
                        notes_display.forEach(function (disp) {
 | 
			
		||||
                            html += li(note_prefix + "_" + disp.id,
 | 
			
		||||
                                disp.name
 | 
			
		||||
                                + "<span class=\"badge badge-dark badge-pill\">"
 | 
			
		||||
                                + disp.quantity + "</span>",
 | 
			
		||||
                                displayStyle(disp.note));
 | 
			
		||||
                        });
 | 
			
		||||
            const note_list = $('#' + note_list_id)
 | 
			
		||||
            let html = ''
 | 
			
		||||
            notes_display.forEach(function (disp) {
 | 
			
		||||
              html += li(note_prefix + '_' + disp.id,
 | 
			
		||||
                disp.name +
 | 
			
		||||
                                '<span class="badge badge-dark badge-pill">' +
 | 
			
		||||
                                disp.quantity + '</span>',
 | 
			
		||||
                displayStyle(disp.note))
 | 
			
		||||
            })
 | 
			
		||||
 | 
			
		||||
                        // Emitters are displayed
 | 
			
		||||
                        note_list.html(html);
 | 
			
		||||
            // Emitters are displayed
 | 
			
		||||
            note_list.html(html)
 | 
			
		||||
 | 
			
		||||
                        // Update tooltip position
 | 
			
		||||
                        field.tooltip('update');
 | 
			
		||||
            // 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
 | 
			
		||||
                            line_obj.hover(function () {
 | 
			
		||||
                                displayNote(disp.note, disp.name, user_note_field, profile_pic_field);
 | 
			
		||||
                            });
 | 
			
		||||
            notes_display.forEach(function (disp) {
 | 
			
		||||
              const line_obj = $('#' + note_prefix + '_' + disp.id)
 | 
			
		||||
              // Hover an emitter display also the profile picture
 | 
			
		||||
              line_obj.hover(function () {
 | 
			
		||||
                displayNote(disp.note, disp.name, user_note_field, profile_pic_field)
 | 
			
		||||
              })
 | 
			
		||||
 | 
			
		||||
                            // When an emitter is clicked, it is removed
 | 
			
		||||
                            line_obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field,
 | 
			
		||||
                                profile_pic_field));
 | 
			
		||||
                        });
 | 
			
		||||
                    })
 | 
			
		||||
                });
 | 
			
		||||
 | 
			
		||||
            });// end getJSON alias
 | 
			
		||||
    });
 | 
			
		||||
              // When an emitter is clicked, it is removed
 | 
			
		||||
              line_obj.click(removeNote(disp, note_prefix, notes_display, note_list_id, user_note_field,
 | 
			
		||||
                profile_pic_field))
 | 
			
		||||
            })
 | 
			
		||||
          })
 | 
			
		||||
        })
 | 
			
		||||
      })// end getJSON alias
 | 
			
		||||
  })
 | 
			
		||||
}// end function autocomplete
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
// When a validate button is clicked, we switch the validation status
 | 
			
		||||
function de_validate (id, validated, resourcetype) {
 | 
			
		||||
    let validate_obj = $("#validate_" + id);
 | 
			
		||||
  const validate_obj = $('#validate_' + id)
 | 
			
		||||
 | 
			
		||||
    if (validate_obj.data("pending"))
 | 
			
		||||
        // The button is already clicked
 | 
			
		||||
        return;
 | 
			
		||||
  if (validate_obj.data('pending'))
 | 
			
		||||
  // The button is already clicked
 | 
			
		||||
  { return }
 | 
			
		||||
 | 
			
		||||
    let invalidity_reason = $("#invalidity_reason_" + id).val();
 | 
			
		||||
    validate_obj.html("<strong style=\"font-size: 16pt;\">⟳</strong>");
 | 
			
		||||
    validate_obj.data("pending", true);
 | 
			
		||||
  const invalidity_reason = $('#invalidity_reason_' + id).val()
 | 
			
		||||
  validate_obj.html('<strong style="font-size: 16pt;">⟳</strong>')
 | 
			
		||||
  validate_obj.data('pending', true)
 | 
			
		||||
 | 
			
		||||
    // Perform a PATCH request to the API in order to update the transaction
 | 
			
		||||
    // If the user has insufficient rights, an error message will appear
 | 
			
		||||
    $.ajax({
 | 
			
		||||
        "url": "/api/note/transaction/transaction/" + id + "/",
 | 
			
		||||
        type: "PATCH",
 | 
			
		||||
        dataType: "json",
 | 
			
		||||
        headers: {
 | 
			
		||||
            "X-CSRFTOKEN": CSRF_TOKEN
 | 
			
		||||
        },
 | 
			
		||||
        data: {
 | 
			
		||||
            "resourcetype": resourcetype,
 | 
			
		||||
            "valid": !validated,
 | 
			
		||||
            "invalidity_reason": invalidity_reason,
 | 
			
		||||
        },
 | 
			
		||||
        success: function () {
 | 
			
		||||
            refreshBalance();
 | 
			
		||||
            // error if this method doesn't exist. Please define it.
 | 
			
		||||
            refreshHistory();
 | 
			
		||||
        },
 | 
			
		||||
        error: function (err) {
 | 
			
		||||
            let errObj = JSON.parse(err.responseText);
 | 
			
		||||
            let error = errObj["detail"] ? errObj["detail"] : errObj["non_field_errors"];
 | 
			
		||||
            if (!error)
 | 
			
		||||
                error = err.responseText;
 | 
			
		||||
            addMsg("Une erreur est survenue lors de la validation/dévalidation " +
 | 
			
		||||
                "de cette transaction : " + error, "danger");
 | 
			
		||||
  // Perform a PATCH request to the API in order to update the transaction
 | 
			
		||||
  // If the user has insufficient rights, an error message will appear
 | 
			
		||||
  $.ajax({
 | 
			
		||||
    url: '/api/note/transaction/transaction/' + id + '/',
 | 
			
		||||
    type: 'PATCH',
 | 
			
		||||
    dataType: 'json',
 | 
			
		||||
    headers: {
 | 
			
		||||
      'X-CSRFTOKEN': CSRF_TOKEN
 | 
			
		||||
    },
 | 
			
		||||
    data: {
 | 
			
		||||
      resourcetype: resourcetype,
 | 
			
		||||
      valid: !validated,
 | 
			
		||||
      invalidity_reason: invalidity_reason
 | 
			
		||||
    },
 | 
			
		||||
    success: function () {
 | 
			
		||||
      refreshBalance()
 | 
			
		||||
      // error if this method doesn't exist. Please define it.
 | 
			
		||||
      refreshHistory()
 | 
			
		||||
    },
 | 
			
		||||
    error: function (err) {
 | 
			
		||||
      const errObj = JSON.parse(err.responseText)
 | 
			
		||||
      let error = errObj.detail ? errObj.detail : errObj.non_field_errors
 | 
			
		||||
      if (!error) { error = err.responseText }
 | 
			
		||||
      addMsg('Une erreur est survenue lors de la validation/dévalidation ' +
 | 
			
		||||
                'de cette transaction : ' + error, 'danger')
 | 
			
		||||
 | 
			
		||||
            refreshBalance();
 | 
			
		||||
            // error if this method doesn't exist. Please define it.
 | 
			
		||||
            refreshHistory();
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
      refreshBalance()
 | 
			
		||||
      // error if this method doesn't exist. Please define it.
 | 
			
		||||
      refreshHistory()
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -404,10 +379,10 @@ function de_validate (id, validated, resourcetype) {
 | 
			
		||||
 * @param wait Debounced milliseconds
 | 
			
		||||
 */
 | 
			
		||||
function debounce (callback, wait) {
 | 
			
		||||
    let timeout;
 | 
			
		||||
    return (...args) => {
 | 
			
		||||
        const context = this;
 | 
			
		||||
        clearTimeout(timeout);
 | 
			
		||||
        timeout = setTimeout(() => callback.apply(context, args), wait);
 | 
			
		||||
    };
 | 
			
		||||
  let timeout
 | 
			
		||||
  return (...args) => {
 | 
			
		||||
    const context = this
 | 
			
		||||
    clearTimeout(timeout)
 | 
			
		||||
    timeout = setTimeout(() => callback.apply(context, args), wait)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,95 +2,92 @@
 | 
			
		||||
// SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
// When a transaction is performed, lock the interface to prevent spam clicks.
 | 
			
		||||
var LOCK = false;
 | 
			
		||||
var LOCK = false
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Refresh the history table on the consumptions page.
 | 
			
		||||
 */
 | 
			
		||||
function refreshHistory() {
 | 
			
		||||
    $("#history").load("/note/consos/ #history");
 | 
			
		||||
    $("#most_used").load("/note/consos/ #most_used");
 | 
			
		||||
function refreshHistory () {
 | 
			
		||||
  $('#history').load('/note/consos/ #history')
 | 
			
		||||
  $('#most_used').load('/note/consos/ #most_used')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$(document).ready(function() {
 | 
			
		||||
    // If hash of a category in the URL, then select this category
 | 
			
		||||
    // else select the first one
 | 
			
		||||
    if (location.hash) {
 | 
			
		||||
        $("a[href='" + location.hash + "']").tab("show");
 | 
			
		||||
    } else {
 | 
			
		||||
        $("a[data-toggle='tab']").first().tab("show");
 | 
			
		||||
$(document).ready(function () {
 | 
			
		||||
  // If hash of a category in the URL, then select this category
 | 
			
		||||
  // else select the first one
 | 
			
		||||
  if (location.hash) {
 | 
			
		||||
    $("a[href='" + location.hash + "']").tab('show')
 | 
			
		||||
  } else {
 | 
			
		||||
    $("a[data-toggle='tab']").first().tab('show')
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // When selecting a category, change URL
 | 
			
		||||
  $(document.body).on('click', "a[data-toggle='tab']", function () {
 | 
			
		||||
    location.hash = this.getAttribute('href')
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // 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')
 | 
			
		||||
    if (buttons.length > 0 && note_list_obj.text().length > 0) {
 | 
			
		||||
      $('#consos_list').html(note_list_obj.html())
 | 
			
		||||
      note_list_obj.html('')
 | 
			
		||||
 | 
			
		||||
      buttons.forEach(function (button) {
 | 
			
		||||
        $('#conso_button_' + button.id).click(function () {
 | 
			
		||||
          if (LOCK) { return }
 | 
			
		||||
          removeNote(button, 'conso_button', buttons, 'consos_list')()
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
    // When selecting a category, change URL
 | 
			
		||||
    $(document.body).on("click", "a[data-toggle='tab']", function() {
 | 
			
		||||
        location.hash = this.getAttribute("href");
 | 
			
		||||
    });
 | 
			
		||||
  $('#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')
 | 
			
		||||
 | 
			
		||||
    // 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 consos_list_obj = $('#consos_list')
 | 
			
		||||
    if (buttons.length > 0) {
 | 
			
		||||
      if (notes_display.length === 0 && consos_list_obj.text().length > 0) {
 | 
			
		||||
        $('#note_list').html(consos_list_obj.html())
 | 
			
		||||
        consos_list_obj.html('')
 | 
			
		||||
        buttons.forEach(function (button) {
 | 
			
		||||
          $('#conso_button_' + button.id).click(function () {
 | 
			
		||||
            if (LOCK) { return }
 | 
			
		||||
            removeNote(button, 'conso_button', buttons, 'note_list')()
 | 
			
		||||
          })
 | 
			
		||||
        })
 | 
			
		||||
      } else {
 | 
			
		||||
        buttons.length = 0
 | 
			
		||||
        consos_list_obj.html('')
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
        let note_list_obj = $("#note_list");
 | 
			
		||||
        if (buttons.length > 0 && note_list_obj.text().length > 0) {
 | 
			
		||||
            $("#consos_list").html(note_list_obj.html());
 | 
			
		||||
            note_list_obj.html("");
 | 
			
		||||
  // Ensure we begin in single consumption. Fix issue with TurboLinks and BootstrapJS
 | 
			
		||||
  $("label[for='double_conso']").removeClass('active')
 | 
			
		||||
 | 
			
		||||
            buttons.forEach(function(button) {
 | 
			
		||||
                $("#conso_button_" + button.id).click(function() {
 | 
			
		||||
                    if (LOCK)
 | 
			
		||||
                        return;
 | 
			
		||||
                    removeNote(button, "conso_button", buttons,"consos_list")();
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
  $('#consume_all').click(consumeAll)
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
    $("#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');
 | 
			
		||||
 | 
			
		||||
        let consos_list_obj = $("#consos_list");
 | 
			
		||||
        if (buttons.length > 0) {
 | 
			
		||||
            if (notes_display.length === 0 && consos_list_obj.text().length > 0) {
 | 
			
		||||
                $("#note_list").html(consos_list_obj.html());
 | 
			
		||||
                consos_list_obj.html("");
 | 
			
		||||
                buttons.forEach(function(button) {
 | 
			
		||||
                    $("#conso_button_" + button.id).click(function() {
 | 
			
		||||
                        if (LOCK)
 | 
			
		||||
                            return;
 | 
			
		||||
                        removeNote(button, "conso_button", buttons,"note_list")();
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                buttons.length = 0;
 | 
			
		||||
                consos_list_obj.html("");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    // Ensure we begin in single consumption. Fix issue with TurboLinks and BootstrapJS
 | 
			
		||||
    $("label[for='double_conso']").removeClass('active');
 | 
			
		||||
 | 
			
		||||
    $("#consume_all").click(consumeAll);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
notes = [];
 | 
			
		||||
notes_display = [];
 | 
			
		||||
buttons = [];
 | 
			
		||||
notes = []
 | 
			
		||||
notes_display = []
 | 
			
		||||
buttons = []
 | 
			
		||||
 | 
			
		||||
// When the user searches an alias, we update the auto-completion
 | 
			
		||||
autoCompleteNote("note", "note_list", notes, notes_display,
 | 
			
		||||
    "alias", "note", "user_note", "profile_pic", function() {
 | 
			
		||||
        if (buttons.length > 0 && $("#single_conso").is(":checked")) {
 | 
			
		||||
            consumeAll();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    });
 | 
			
		||||
autoCompleteNote('note', 'note_list', notes, notes_display,
 | 
			
		||||
  'alias', 'note', 'user_note', 'profile_pic', function () {
 | 
			
		||||
    if (buttons.length > 0 && $('#single_conso').is(':checked')) {
 | 
			
		||||
      consumeAll()
 | 
			
		||||
      return false
 | 
			
		||||
    }
 | 
			
		||||
    return true
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Add a transaction from a button.
 | 
			
		||||
@@ -102,103 +99,98 @@ autoCompleteNote("note", "note_list", notes, notes_display,
 | 
			
		||||
 * @param template_id The identifier of the button
 | 
			
		||||
 * @param template_name The name of  the button
 | 
			
		||||
 */
 | 
			
		||||
function addConso(dest, amount, type, category_id, category_name, template_id, template_name) {
 | 
			
		||||
    var button = null;
 | 
			
		||||
    buttons.forEach(function(b) {
 | 
			
		||||
        if (b.id === template_id) {
 | 
			
		||||
            b.quantity += 1;
 | 
			
		||||
            button = b;
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
    if (button == null) {
 | 
			
		||||
        button = {
 | 
			
		||||
            id: template_id,
 | 
			
		||||
            name: template_name,
 | 
			
		||||
            dest: dest,
 | 
			
		||||
            quantity: 1,
 | 
			
		||||
            amount: amount,
 | 
			
		||||
            type: type,
 | 
			
		||||
            category_id: category_id,
 | 
			
		||||
            category_name: category_name
 | 
			
		||||
        };
 | 
			
		||||
        buttons.push(button);
 | 
			
		||||
function addConso (dest, amount, type, category_id, category_name, template_id, template_name) {
 | 
			
		||||
  var button = null
 | 
			
		||||
  buttons.forEach(function (b) {
 | 
			
		||||
    if (b.id === template_id) {
 | 
			
		||||
      b.quantity += 1
 | 
			
		||||
      button = b
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let dc_obj = $("#double_conso");
 | 
			
		||||
    if (dc_obj.is(":checked") || notes_display.length === 0) {
 | 
			
		||||
        let list = dc_obj.is(":checked") ? "consos_list" : "note_list";
 | 
			
		||||
        let html = "";
 | 
			
		||||
        buttons.forEach(function(button) {
 | 
			
		||||
            html += li("conso_button_" + button.id, button.name
 | 
			
		||||
                + "<span class=\"badge badge-dark badge-pill\">" + button.quantity + "</span>");
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        $("#" + list).html(html);
 | 
			
		||||
 | 
			
		||||
        buttons.forEach(function(button) {
 | 
			
		||||
            $("#conso_button_" + button.id).click(function() {
 | 
			
		||||
                if (LOCK)
 | 
			
		||||
                    return;
 | 
			
		||||
                removeNote(button, "conso_button", buttons, list)();
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
  })
 | 
			
		||||
  if (button == null) {
 | 
			
		||||
    button = {
 | 
			
		||||
      id: template_id,
 | 
			
		||||
      name: template_name,
 | 
			
		||||
      dest: dest,
 | 
			
		||||
      quantity: 1,
 | 
			
		||||
      amount: amount,
 | 
			
		||||
      type: type,
 | 
			
		||||
      category_id: category_id,
 | 
			
		||||
      category_name: category_name
 | 
			
		||||
    }
 | 
			
		||||
    else
 | 
			
		||||
        consumeAll();
 | 
			
		||||
    buttons.push(button)
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const dc_obj = $('#double_conso')
 | 
			
		||||
  if (dc_obj.is(':checked') || notes_display.length === 0) {
 | 
			
		||||
    const list = dc_obj.is(':checked') ? 'consos_list' : 'note_list'
 | 
			
		||||
    let html = ''
 | 
			
		||||
    buttons.forEach(function (button) {
 | 
			
		||||
      html += li('conso_button_' + button.id, button.name +
 | 
			
		||||
                '<span class="badge badge-dark badge-pill">' + button.quantity + '</span>')
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    $('#' + list).html(html)
 | 
			
		||||
 | 
			
		||||
    buttons.forEach(function (button) {
 | 
			
		||||
      $('#conso_button_' + button.id).click(function () {
 | 
			
		||||
        if (LOCK) { return }
 | 
			
		||||
        removeNote(button, 'conso_button', buttons, list)()
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  } else { consumeAll() }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Reset the page as its initial state.
 | 
			
		||||
 */
 | 
			
		||||
function reset() {
 | 
			
		||||
    notes_display.length = 0;
 | 
			
		||||
    notes.length = 0;
 | 
			
		||||
    buttons.length = 0;
 | 
			
		||||
    $("#note_list").html("");
 | 
			
		||||
    $("#consos_list").html("");
 | 
			
		||||
    $("#note").val("");
 | 
			
		||||
    $("#note").attr("data-original-title", "").tooltip("hide");
 | 
			
		||||
    $("#profile_pic").attr("src", "/static/member/img/default_picture.png");
 | 
			
		||||
    $("#profile_pic_link").attr("href", "#");
 | 
			
		||||
    refreshHistory();
 | 
			
		||||
    refreshBalance();
 | 
			
		||||
    LOCK = false;
 | 
			
		||||
function reset () {
 | 
			
		||||
  notes_display.length = 0
 | 
			
		||||
  notes.length = 0
 | 
			
		||||
  buttons.length = 0
 | 
			
		||||
  $('#note_list').html('')
 | 
			
		||||
  $('#consos_list').html('')
 | 
			
		||||
  $('#note').val('')
 | 
			
		||||
  $('#note').attr('data-original-title', '').tooltip('hide')
 | 
			
		||||
  $('#profile_pic').attr('src', '/static/member/img/default_picture.png')
 | 
			
		||||
  $('#profile_pic_link').attr('href', '#')
 | 
			
		||||
  refreshHistory()
 | 
			
		||||
  refreshBalance()
 | 
			
		||||
  LOCK = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Apply all transactions: all notes in `notes` buy each item in `buttons`
 | 
			
		||||
 */
 | 
			
		||||
function consumeAll() {
 | 
			
		||||
    if (LOCK)
 | 
			
		||||
        return;
 | 
			
		||||
function consumeAll () {
 | 
			
		||||
  if (LOCK) { return }
 | 
			
		||||
 | 
			
		||||
    LOCK = true;
 | 
			
		||||
  LOCK = true
 | 
			
		||||
 | 
			
		||||
    let error = false;
 | 
			
		||||
  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 (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 (buttons.length === 0) {
 | 
			
		||||
    $('#consos_list').html(li('', '<strong>Ajoutez des consommations.</strong>', 'text-danger'))
 | 
			
		||||
    error = true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    if (error) {
 | 
			
		||||
        LOCK = false;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
  if (error) {
 | 
			
		||||
    LOCK = false
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    notes_display.forEach(function(note_display) {
 | 
			
		||||
        buttons.forEach(function(button) {
 | 
			
		||||
            consume(note_display.note, note_display.name, button.dest, button.quantity * note_display.quantity, button.amount,
 | 
			
		||||
                button.name + " (" + button.category_name + ")", button.type, button.category_id, button.id);
 | 
			
		||||
       });
 | 
			
		||||
    });
 | 
			
		||||
  notes_display.forEach(function (note_display) {
 | 
			
		||||
    buttons.forEach(function (button) {
 | 
			
		||||
      consume(note_display.note, note_display.name, button.dest, button.quantity * note_display.quantity, button.amount,
 | 
			
		||||
        button.name + ' (' + button.category_name + ')', button.type, button.category_id, button.id)
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -213,58 +205,60 @@ function consumeAll() {
 | 
			
		||||
 * @param category The category id of the button (type: int)
 | 
			
		||||
 * @param template The button id (type: int)
 | 
			
		||||
 */
 | 
			
		||||
function consume(source, source_alias, dest, quantity, amount, reason, type, category, template) {
 | 
			
		||||
    $.post("/api/note/transaction/transaction/",
 | 
			
		||||
function consume (source, source_alias, dest, quantity, amount, reason, type, category, template) {
 | 
			
		||||
  $.post('/api/note/transaction/transaction/',
 | 
			
		||||
    {
 | 
			
		||||
      csrfmiddlewaretoken: CSRF_TOKEN,
 | 
			
		||||
      quantity: quantity,
 | 
			
		||||
      amount: amount,
 | 
			
		||||
      reason: reason,
 | 
			
		||||
      valid: true,
 | 
			
		||||
      polymorphic_ctype: type,
 | 
			
		||||
      resourcetype: 'RecurrentTransaction',
 | 
			
		||||
      source: source.id,
 | 
			
		||||
      source_alias: source_alias,
 | 
			
		||||
      destination: dest,
 | 
			
		||||
      template: template
 | 
			
		||||
    })
 | 
			
		||||
    .done(function () {
 | 
			
		||||
      if (!isNaN(source.balance)) {
 | 
			
		||||
        const newBalance = source.balance - quantity * amount
 | 
			
		||||
        if (newBalance <= -5000) {
 | 
			
		||||
          addMsg('Attention, La transaction depuis la note ' + source_alias + ' a été réalisée avec ' +
 | 
			
		||||
                        'succès, mais la note émettrice ' + source_alias + ' est en négatif sévère.',
 | 
			
		||||
          'danger', 30000)
 | 
			
		||||
        } else if (newBalance < 0) {
 | 
			
		||||
          addMsg('Attention, La transaction depuis la note ' + source_alias + ' a été réalisée avec ' +
 | 
			
		||||
                        'succès, mais la note émettrice ' + source_alias + ' est en négatif.',
 | 
			
		||||
          'warning', 30000)
 | 
			
		||||
        }
 | 
			
		||||
        if (source.membership && source.membership.date_end < new Date().toISOString()) {
 | 
			
		||||
          addMsg('Attention : la note émettrice ' + source.name + " n'est plus adhérente.",
 | 
			
		||||
            'danger', 30000)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      reset()
 | 
			
		||||
    }).fail(function (e) {
 | 
			
		||||
      $.post('/api/note/transaction/transaction/',
 | 
			
		||||
        {
 | 
			
		||||
            "csrfmiddlewaretoken": CSRF_TOKEN,
 | 
			
		||||
            "quantity": quantity,
 | 
			
		||||
            "amount": amount,
 | 
			
		||||
            "reason": reason,
 | 
			
		||||
            "valid": true,
 | 
			
		||||
            "polymorphic_ctype": type,
 | 
			
		||||
            "resourcetype": "RecurrentTransaction",
 | 
			
		||||
            "source": source.id,
 | 
			
		||||
            "source_alias": source_alias,
 | 
			
		||||
            "destination": dest,
 | 
			
		||||
            "template": template
 | 
			
		||||
        })
 | 
			
		||||
        .done(function () {
 | 
			
		||||
            if (!isNaN(source.balance)) {
 | 
			
		||||
                let newBalance = source.balance - quantity * amount;
 | 
			
		||||
                if (newBalance <= -5000)
 | 
			
		||||
                    addMsg("Attention, La transaction depuis la note " + source_alias + " a été réalisée avec " +
 | 
			
		||||
                        "succès, mais la note émettrice " + source_alias + " est en négatif sévère.",
 | 
			
		||||
                        "danger", 30000);
 | 
			
		||||
                else if (newBalance < 0)
 | 
			
		||||
                    addMsg("Attention, La transaction depuis la note " + source_alias + " a été réalisée avec " +
 | 
			
		||||
                        "succès, mais la note émettrice " + source_alias + " est en négatif.",
 | 
			
		||||
                        "warning", 30000);
 | 
			
		||||
                if (source.membership && source.membership.date_end < new Date().toISOString())
 | 
			
		||||
                    addMsg("Attention : la note émettrice " + source.name + " n'est plus adhérente.",
 | 
			
		||||
                        "danger", 30000);
 | 
			
		||||
            }
 | 
			
		||||
            reset();
 | 
			
		||||
        }).fail(function (e) {
 | 
			
		||||
        $.post("/api/note/transaction/transaction/",
 | 
			
		||||
            {
 | 
			
		||||
                "csrfmiddlewaretoken": CSRF_TOKEN,
 | 
			
		||||
                "quantity": quantity,
 | 
			
		||||
                "amount": amount,
 | 
			
		||||
                "reason": reason,
 | 
			
		||||
                "valid": false,
 | 
			
		||||
                "invalidity_reason": "Solde insuffisant",
 | 
			
		||||
                "polymorphic_ctype": type,
 | 
			
		||||
                "resourcetype": "RecurrentTransaction",
 | 
			
		||||
                "source": source,
 | 
			
		||||
                "source_alias": source_alias,
 | 
			
		||||
                "destination": dest,
 | 
			
		||||
                "template": template
 | 
			
		||||
            }).done(function() {
 | 
			
		||||
                reset();
 | 
			
		||||
                addMsg("La transaction n'a pas pu être validée pour cause de solde insuffisant.", "danger", 10000);
 | 
			
		||||
            }).fail(function () {
 | 
			
		||||
                reset();
 | 
			
		||||
                errMsg(e.responseJSON);
 | 
			
		||||
            });
 | 
			
		||||
    });
 | 
			
		||||
          csrfmiddlewaretoken: CSRF_TOKEN,
 | 
			
		||||
          quantity: quantity,
 | 
			
		||||
          amount: amount,
 | 
			
		||||
          reason: reason,
 | 
			
		||||
          valid: false,
 | 
			
		||||
          invalidity_reason: 'Solde insuffisant',
 | 
			
		||||
          polymorphic_ctype: type,
 | 
			
		||||
          resourcetype: 'RecurrentTransaction',
 | 
			
		||||
          source: source,
 | 
			
		||||
          source_alias: source_alias,
 | 
			
		||||
          destination: dest,
 | 
			
		||||
          template: template
 | 
			
		||||
        }).done(function () {
 | 
			
		||||
        reset()
 | 
			
		||||
        addMsg("La transaction n'a pas pu être validée pour cause de solde insuffisant.", 'danger', 10000)
 | 
			
		||||
      }).fail(function () {
 | 
			
		||||
        reset()
 | 
			
		||||
        errMsg(e.responseJSON)
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -9,241 +9,240 @@
 | 
			
		||||
 * Licensed under the New BSD License
 | 
			
		||||
 * See: http://www.opensource.org/licenses/bsd-license.php
 | 
			
		||||
 */
 | 
			
		||||
;(function($) {
 | 
			
		||||
    $.fn.formset = function(opts)
 | 
			
		||||
    {
 | 
			
		||||
        var options = $.extend({}, $.fn.formset.defaults, opts),
 | 
			
		||||
            flatExtraClasses = options.extraClasses.join(' '),
 | 
			
		||||
            totalForms = $('#id_' + options.prefix + '-TOTAL_FORMS'),
 | 
			
		||||
            maxForms = $('#id_' + options.prefix + '-MAX_NUM_FORMS'),
 | 
			
		||||
            minForms = $('#id_' + options.prefix + '-MIN_NUM_FORMS'),
 | 
			
		||||
            childElementSelector = 'input,select,textarea,label,div',
 | 
			
		||||
            $$ = $(this),
 | 
			
		||||
;(function ($) {
 | 
			
		||||
  $.fn.formset = function (opts) {
 | 
			
		||||
    var options = $.extend({}, $.fn.formset.defaults, opts)
 | 
			
		||||
    var flatExtraClasses = options.extraClasses.join(' ')
 | 
			
		||||
    var totalForms = $('#id_' + options.prefix + '-TOTAL_FORMS')
 | 
			
		||||
    var maxForms = $('#id_' + options.prefix + '-MAX_NUM_FORMS')
 | 
			
		||||
    var minForms = $('#id_' + options.prefix + '-MIN_NUM_FORMS')
 | 
			
		||||
    var childElementSelector = 'input,select,textarea,label,div'
 | 
			
		||||
    var $$ = $(this)
 | 
			
		||||
 | 
			
		||||
            applyExtraClasses = function(row, ndx) {
 | 
			
		||||
                if (options.extraClasses) {
 | 
			
		||||
                    row.removeClass(flatExtraClasses);
 | 
			
		||||
                    row.addClass(options.extraClasses[ndx % options.extraClasses.length]);
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
    var applyExtraClasses = function (row, ndx) {
 | 
			
		||||
      if (options.extraClasses) {
 | 
			
		||||
        row.removeClass(flatExtraClasses)
 | 
			
		||||
        row.addClass(options.extraClasses[ndx % options.extraClasses.length])
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            updateElementIndex = function(elem, prefix, ndx) {
 | 
			
		||||
                var idRegex = new RegExp(prefix + '-(\\d+|__prefix__)-'),
 | 
			
		||||
                    replacement = prefix + '-' + ndx + '-';
 | 
			
		||||
                if (elem.attr("for")) elem.attr("for", elem.attr("for").replace(idRegex, replacement));
 | 
			
		||||
                if (elem.attr('id')) elem.attr('id', elem.attr('id').replace(idRegex, replacement));
 | 
			
		||||
                if (elem.attr('name')) elem.attr('name', elem.attr('name').replace(idRegex, replacement));
 | 
			
		||||
            },
 | 
			
		||||
    var updateElementIndex = function (elem, prefix, ndx) {
 | 
			
		||||
      var idRegex = new RegExp(prefix + '-(\\d+|__prefix__)-')
 | 
			
		||||
      var replacement = prefix + '-' + ndx + '-'
 | 
			
		||||
      if (elem.attr('for')) elem.attr('for', elem.attr('for').replace(idRegex, replacement))
 | 
			
		||||
      if (elem.attr('id')) elem.attr('id', elem.attr('id').replace(idRegex, replacement))
 | 
			
		||||
      if (elem.attr('name')) elem.attr('name', elem.attr('name').replace(idRegex, replacement))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            hasChildElements = function(row) {
 | 
			
		||||
                return row.find(childElementSelector).length > 0;
 | 
			
		||||
            },
 | 
			
		||||
    var hasChildElements = function (row) {
 | 
			
		||||
      return row.find(childElementSelector).length > 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            showAddButton = function() {
 | 
			
		||||
                return maxForms.length == 0 ||   // For Django versions pre 1.2
 | 
			
		||||
                    (maxForms.val() == '' || (maxForms.val() - totalForms.val() > 0));
 | 
			
		||||
            },
 | 
			
		||||
    var showAddButton = function () {
 | 
			
		||||
      return maxForms.length == 0 || // For Django versions pre 1.2
 | 
			
		||||
                    (maxForms.val() == '' || (maxForms.val() - totalForms.val() > 0))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
    /**
 | 
			
		||||
            * Indicates whether delete link(s) can be displayed - when total forms > min forms
 | 
			
		||||
            */
 | 
			
		||||
            showDeleteLinks = function() {
 | 
			
		||||
                return minForms.length == 0 ||   // For Django versions pre 1.7
 | 
			
		||||
                    (minForms.val() == '' || (totalForms.val() - minForms.val() > 0));
 | 
			
		||||
            },
 | 
			
		||||
    var showDeleteLinks = function () {
 | 
			
		||||
      return minForms.length == 0 || // For Django versions pre 1.7
 | 
			
		||||
                    (minForms.val() == '' || (totalForms.val() - minForms.val() > 0))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            insertDeleteLink = function(row) {
 | 
			
		||||
                var delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.'),
 | 
			
		||||
                    addCssSelector = $.trim(options.addCssClass).replace(/\s+/g, '.');
 | 
			
		||||
    var insertDeleteLink = function (row) {
 | 
			
		||||
      var delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.')
 | 
			
		||||
      var addCssSelector = $.trim(options.addCssClass).replace(/\s+/g, '.')
 | 
			
		||||
 | 
			
		||||
                var delButtonHTML = '<a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText +'</a>';
 | 
			
		||||
                if (options.deleteContainerClass) {
 | 
			
		||||
                    // If we have a specific container for the remove button,
 | 
			
		||||
                    // place it as the last child of that container:
 | 
			
		||||
                    row.find('[class*="' + options.deleteContainerClass + '"]').append(delButtonHTML);
 | 
			
		||||
                } else if (row.is('TR')) {
 | 
			
		||||
                    // If the forms are laid out in table rows, insert
 | 
			
		||||
                    // the remove button into the last table cell:
 | 
			
		||||
                    row.children('td:last').append(delButtonHTML);
 | 
			
		||||
                } else if (row.is('UL') || row.is('OL')) {
 | 
			
		||||
                    // If they're laid out as an ordered/unordered list,
 | 
			
		||||
                    // insert an <li> after the last list item:
 | 
			
		||||
                    row.append('<li>' + delButtonHTML + '</li>');
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Otherwise, just insert the remove button as the
 | 
			
		||||
                    // last child element of the form's container:
 | 
			
		||||
                    row.append(delButtonHTML);
 | 
			
		||||
                }
 | 
			
		||||
      var delButtonHTML = '<a class="' + options.deleteCssClass + '" href="javascript:void(0)">' + options.deleteText + '</a>'
 | 
			
		||||
      if (options.deleteContainerClass) {
 | 
			
		||||
        // If we have a specific container for the remove button,
 | 
			
		||||
        // place it as the last child of that container:
 | 
			
		||||
        row.find('[class*="' + options.deleteContainerClass + '"]').append(delButtonHTML)
 | 
			
		||||
      } else if (row.is('TR')) {
 | 
			
		||||
        // If the forms are laid out in table rows, insert
 | 
			
		||||
        // the remove button into the last table cell:
 | 
			
		||||
        row.children('td:last').append(delButtonHTML)
 | 
			
		||||
      } else if (row.is('UL') || row.is('OL')) {
 | 
			
		||||
        // If they're laid out as an ordered/unordered list,
 | 
			
		||||
        // insert an <li> after the last list item:
 | 
			
		||||
        row.append('<li>' + delButtonHTML + '</li>')
 | 
			
		||||
      } else {
 | 
			
		||||
        // Otherwise, just insert the remove button as the
 | 
			
		||||
        // last child element of the form's container:
 | 
			
		||||
        row.append(delButtonHTML)
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
                // Check if we're under the minimum number of forms - not to display delete link at rendering
 | 
			
		||||
                if (!showDeleteLinks()){
 | 
			
		||||
                    row.find('a.' + delCssSelector).hide();
 | 
			
		||||
                }
 | 
			
		||||
      // Check if we're under the minimum number of forms - not to display delete link at rendering
 | 
			
		||||
      if (!showDeleteLinks()) {
 | 
			
		||||
        row.find('a.' + delCssSelector).hide()
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
                row.find('a.' + delCssSelector).click(function() {
 | 
			
		||||
                    var row = $(this).parents('.' + options.formCssClass),
 | 
			
		||||
                        del = row.find('input:hidden[id $= "-DELETE"]'),
 | 
			
		||||
                        buttonRow = row.siblings("a." + addCssSelector + ', .' + options.formCssClass + '-add'),
 | 
			
		||||
                        forms;
 | 
			
		||||
                    if (del.length) {
 | 
			
		||||
                        // We're dealing with an inline formset.
 | 
			
		||||
                        // Rather than remove this form from the DOM, we'll mark it as deleted
 | 
			
		||||
                        // and hide it, then let Django handle the deleting:
 | 
			
		||||
                        del.val('on');
 | 
			
		||||
                        row.hide();
 | 
			
		||||
                        forms = $('.' + options.formCssClass).not(':hidden');
 | 
			
		||||
                    } else {
 | 
			
		||||
                        row.remove();
 | 
			
		||||
                        // Update the TOTAL_FORMS count:
 | 
			
		||||
                        forms = $('.' + options.formCssClass).not('.formset-custom-template');
 | 
			
		||||
                        totalForms.val(forms.length);
 | 
			
		||||
                    }
 | 
			
		||||
                    for (var i=0, formCount=forms.length; i<formCount; i++) {
 | 
			
		||||
                        // Apply `extraClasses` to form rows so they're nicely alternating:
 | 
			
		||||
                        applyExtraClasses(forms.eq(i), i);
 | 
			
		||||
                        if (!del.length) {
 | 
			
		||||
                            // Also update names and IDs for all child controls (if this isn't
 | 
			
		||||
                            // a delete-able inline formset) so they remain in sequence:
 | 
			
		||||
                            forms.eq(i).find(childElementSelector).each(function() {
 | 
			
		||||
                                updateElementIndex($(this), options.prefix, i);
 | 
			
		||||
                            });
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                    // Check if we've reached the minimum number of forms - hide all delete link(s)
 | 
			
		||||
                    if (!showDeleteLinks()){
 | 
			
		||||
                        $('a.' + delCssSelector).each(function(){$(this).hide();});
 | 
			
		||||
                    }
 | 
			
		||||
                    // Check if we need to show the add button:
 | 
			
		||||
                    if (buttonRow.is(':hidden') && showAddButton()) buttonRow.show();
 | 
			
		||||
                    // If a post-delete callback was provided, call it with the deleted form:
 | 
			
		||||
                    if (options.removed) options.removed(row);
 | 
			
		||||
                    return false;
 | 
			
		||||
                });
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
        $$.each(function(i) {
 | 
			
		||||
            var row = $(this),
 | 
			
		||||
                del = row.find('input:checkbox[id $= "-DELETE"]');
 | 
			
		||||
            if (del.length) {
 | 
			
		||||
                // If you specify "can_delete = True" when creating an inline formset,
 | 
			
		||||
                // Django adds a checkbox to each form in the formset.
 | 
			
		||||
                // Replace the default checkbox with a hidden field:
 | 
			
		||||
                if (del.is(':checked')) {
 | 
			
		||||
                    // If an inline formset containing deleted forms fails validation, make sure
 | 
			
		||||
                    // we keep the forms hidden (thanks for the bug report and suggested fix Mike)
 | 
			
		||||
                    del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" value="on" />');
 | 
			
		||||
                    row.hide();
 | 
			
		||||
                } else {
 | 
			
		||||
                    del.before('<input type="hidden" name="' + del.attr('name') +'" id="' + del.attr('id') +'" />');
 | 
			
		||||
                }
 | 
			
		||||
                // Hide any labels associated with the DELETE checkbox:
 | 
			
		||||
                $('label[for="' + del.attr('id') + '"]').hide();
 | 
			
		||||
                del.remove();
 | 
			
		||||
            }
 | 
			
		||||
            if (hasChildElements(row)) {
 | 
			
		||||
                row.addClass(options.formCssClass);
 | 
			
		||||
                if (row.is(':visible')) {
 | 
			
		||||
                    insertDeleteLink(row);
 | 
			
		||||
                    applyExtraClasses(row, i);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if ($$.length) {
 | 
			
		||||
            var hideAddButton = !showAddButton(),
 | 
			
		||||
                addButton, template;
 | 
			
		||||
            if (options.formTemplate) {
 | 
			
		||||
                // If a form template was specified, we'll clone it to generate new form instances:
 | 
			
		||||
                template = (options.formTemplate instanceof $) ? options.formTemplate : $(options.formTemplate);
 | 
			
		||||
                template.removeAttr('id').addClass(options.formCssClass + ' formset-custom-template');
 | 
			
		||||
                template.find(childElementSelector).each(function() {
 | 
			
		||||
                    updateElementIndex($(this), options.prefix, '__prefix__');
 | 
			
		||||
                });
 | 
			
		||||
                insertDeleteLink(template);
 | 
			
		||||
            } else {
 | 
			
		||||
                // Otherwise, use the last form in the formset; this works much better if you've got
 | 
			
		||||
                // extra (>= 1) forms (thnaks to justhamade for pointing this out):
 | 
			
		||||
                if (options.hideLastAddForm) $('.' + options.formCssClass + ':last').hide();
 | 
			
		||||
                template = $('.' + options.formCssClass + ':last').clone(true).removeAttr('id');
 | 
			
		||||
                template.find('input:hidden[id $= "-DELETE"]').remove();
 | 
			
		||||
                // Clear all cloned fields, except those the user wants to keep (thanks to brunogola for the suggestion):
 | 
			
		||||
                template.find(childElementSelector).not(options.keepFieldValues).each(function() {
 | 
			
		||||
                    var elem = $(this);
 | 
			
		||||
                    // If this is a checkbox or radiobutton, uncheck it.
 | 
			
		||||
                    // This fixes Issue 1, reported by Wilson.Andrew.J:
 | 
			
		||||
                    if (elem.is('input:checkbox') || elem.is('input:radio')) {
 | 
			
		||||
                        elem.attr('checked', false);
 | 
			
		||||
                    } else {
 | 
			
		||||
                        elem.val('');
 | 
			
		||||
                    }
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
            // FIXME: Perhaps using $.data would be a better idea?
 | 
			
		||||
            options.formTemplate = template;
 | 
			
		||||
 | 
			
		||||
            var addButtonHTML = '<a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a>';
 | 
			
		||||
            if (options.addContainerClass) {
 | 
			
		||||
                // If we have a specific container for the "add" button,
 | 
			
		||||
                // place it as the last child of that container:
 | 
			
		||||
                var addContainer = $('[class*="' + options.addContainerClass + '"');
 | 
			
		||||
                addContainer.append(addButtonHTML);
 | 
			
		||||
                addButton = addContainer.find('[class="' + options.addCssClass + '"]');
 | 
			
		||||
            } else if ($$.is('TR')) {
 | 
			
		||||
                // If forms are laid out as table rows, insert the
 | 
			
		||||
                // "add" button in a new table row:
 | 
			
		||||
                var numCols = $$.eq(0).children().length,   // This is a bit of an assumption :|
 | 
			
		||||
                    buttonRow = $('<tr><td colspan="' + numCols + '">' + addButtonHTML + '</tr>').addClass(options.formCssClass + '-add');
 | 
			
		||||
                $$.parent().append(buttonRow);
 | 
			
		||||
                addButton = buttonRow.find('a');
 | 
			
		||||
            } else {
 | 
			
		||||
                // Otherwise, insert it immediately after the last form:
 | 
			
		||||
                $$.filter(':last').after(addButtonHTML);
 | 
			
		||||
                addButton = $$.filter(':last').next();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (hideAddButton) addButton.hide();
 | 
			
		||||
 | 
			
		||||
            addButton.click(function() {
 | 
			
		||||
                var formCount = parseInt(totalForms.val()),
 | 
			
		||||
                    row = options.formTemplate.clone(true).removeClass('formset-custom-template'),
 | 
			
		||||
                    buttonRow = $($(this).parents('tr.' + options.formCssClass + '-add').get(0) || this),
 | 
			
		||||
                    delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.');
 | 
			
		||||
                applyExtraClasses(row, formCount);
 | 
			
		||||
                row.insertBefore(buttonRow).show();
 | 
			
		||||
                row.find(childElementSelector).each(function() {
 | 
			
		||||
                    updateElementIndex($(this), options.prefix, formCount);
 | 
			
		||||
                });
 | 
			
		||||
                totalForms.val(formCount + 1);
 | 
			
		||||
                // Check if we're above the minimum allowed number of forms -> show all delete link(s)
 | 
			
		||||
                if (showDeleteLinks()){
 | 
			
		||||
                    $('a.' + delCssSelector).each(function(){$(this).show();});
 | 
			
		||||
                }
 | 
			
		||||
                // Check if we've exceeded the maximum allowed number of forms:
 | 
			
		||||
                if (!showAddButton()) buttonRow.hide();
 | 
			
		||||
                // If a post-add callback was supplied, call it with the added form:
 | 
			
		||||
                if (options.added) options.added(row);
 | 
			
		||||
                return false;
 | 
			
		||||
            });
 | 
			
		||||
      row.find('a.' + delCssSelector).click(function () {
 | 
			
		||||
        var row = $(this).parents('.' + options.formCssClass)
 | 
			
		||||
        var del = row.find('input:hidden[id $= "-DELETE"]')
 | 
			
		||||
        var buttonRow = row.siblings('a.' + addCssSelector + ', .' + options.formCssClass + '-add')
 | 
			
		||||
        var forms
 | 
			
		||||
        if (del.length) {
 | 
			
		||||
          // We're dealing with an inline formset.
 | 
			
		||||
          // Rather than remove this form from the DOM, we'll mark it as deleted
 | 
			
		||||
          // and hide it, then let Django handle the deleting:
 | 
			
		||||
          del.val('on')
 | 
			
		||||
          row.hide()
 | 
			
		||||
          forms = $('.' + options.formCssClass).not(':hidden')
 | 
			
		||||
        } else {
 | 
			
		||||
          row.remove()
 | 
			
		||||
          // Update the TOTAL_FORMS count:
 | 
			
		||||
          forms = $('.' + options.formCssClass).not('.formset-custom-template')
 | 
			
		||||
          totalForms.val(forms.length)
 | 
			
		||||
        }
 | 
			
		||||
        for (var i = 0, formCount = forms.length; i < formCount; i++) {
 | 
			
		||||
          // Apply `extraClasses` to form rows so they're nicely alternating:
 | 
			
		||||
          applyExtraClasses(forms.eq(i), i)
 | 
			
		||||
          if (!del.length) {
 | 
			
		||||
            // Also update names and IDs for all child controls (if this isn't
 | 
			
		||||
            // a delete-able inline formset) so they remain in sequence:
 | 
			
		||||
            forms.eq(i).find(childElementSelector).each(function () {
 | 
			
		||||
              updateElementIndex($(this), options.prefix, i)
 | 
			
		||||
            })
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
        // Check if we've reached the minimum number of forms - hide all delete link(s)
 | 
			
		||||
        if (!showDeleteLinks()) {
 | 
			
		||||
          $('a.' + delCssSelector).each(function () { $(this).hide() })
 | 
			
		||||
        }
 | 
			
		||||
        // Check if we need to show the add button:
 | 
			
		||||
        if (buttonRow.is(':hidden') && showAddButton()) buttonRow.show()
 | 
			
		||||
        // If a post-delete callback was provided, call it with the deleted form:
 | 
			
		||||
        if (options.removed) options.removed(row)
 | 
			
		||||
        return false
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
        return $$;
 | 
			
		||||
    };
 | 
			
		||||
    $$.each(function (i) {
 | 
			
		||||
      var row = $(this)
 | 
			
		||||
      var del = row.find('input:checkbox[id $= "-DELETE"]')
 | 
			
		||||
      if (del.length) {
 | 
			
		||||
        // If you specify "can_delete = True" when creating an inline formset,
 | 
			
		||||
        // Django adds a checkbox to each form in the formset.
 | 
			
		||||
        // Replace the default checkbox with a hidden field:
 | 
			
		||||
        if (del.is(':checked')) {
 | 
			
		||||
          // If an inline formset containing deleted forms fails validation, make sure
 | 
			
		||||
          // we keep the forms hidden (thanks for the bug report and suggested fix Mike)
 | 
			
		||||
          del.before('<input type="hidden" name="' + del.attr('name') + '" id="' + del.attr('id') + '" value="on" />')
 | 
			
		||||
          row.hide()
 | 
			
		||||
        } else {
 | 
			
		||||
          del.before('<input type="hidden" name="' + del.attr('name') + '" id="' + del.attr('id') + '" />')
 | 
			
		||||
        }
 | 
			
		||||
        // Hide any labels associated with the DELETE checkbox:
 | 
			
		||||
        $('label[for="' + del.attr('id') + '"]').hide()
 | 
			
		||||
        del.remove()
 | 
			
		||||
      }
 | 
			
		||||
      if (hasChildElements(row)) {
 | 
			
		||||
        row.addClass(options.formCssClass)
 | 
			
		||||
        if (row.is(':visible')) {
 | 
			
		||||
          insertDeleteLink(row)
 | 
			
		||||
          applyExtraClasses(row, i)
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    /* Setup plugin defaults */
 | 
			
		||||
    $.fn.formset.defaults = {
 | 
			
		||||
        prefix: 'form',                  // The form prefix for your django formset
 | 
			
		||||
        formTemplate: null,              // The jQuery selection cloned to generate new form instances
 | 
			
		||||
        addText: 'add another',          // Text for the add link
 | 
			
		||||
        deleteText: 'remove',            // Text for the delete link
 | 
			
		||||
        addContainerClass: null,         // Container CSS class for the add link
 | 
			
		||||
        deleteContainerClass: null,      // Container CSS class for the delete link
 | 
			
		||||
        addCssClass: 'add-row',          // CSS class applied to the add link
 | 
			
		||||
        deleteCssClass: 'delete-row',    // CSS class applied to the delete link
 | 
			
		||||
        formCssClass: 'dynamic-form',    // CSS class applied to each form in a formset
 | 
			
		||||
        extraClasses: [],                // Additional CSS classes, which will be applied to each form in turn
 | 
			
		||||
        keepFieldValues: '',             // jQuery selector for fields whose values should be kept when the form is cloned
 | 
			
		||||
        added: null,                     // Function called each time a new form is added
 | 
			
		||||
        removed: null,                   // Function called each time a form is deleted
 | 
			
		||||
        hideLastAddForm: false           // When set to true, hide last empty add form (becomes visible when clicking on add button)
 | 
			
		||||
    };
 | 
			
		||||
})(jQuery);
 | 
			
		||||
    if ($$.length) {
 | 
			
		||||
      var hideAddButton = !showAddButton()
 | 
			
		||||
      var addButton; var template
 | 
			
		||||
      if (options.formTemplate) {
 | 
			
		||||
        // If a form template was specified, we'll clone it to generate new form instances:
 | 
			
		||||
        template = (options.formTemplate instanceof $) ? options.formTemplate : $(options.formTemplate)
 | 
			
		||||
        template.removeAttr('id').addClass(options.formCssClass + ' formset-custom-template')
 | 
			
		||||
        template.find(childElementSelector).each(function () {
 | 
			
		||||
          updateElementIndex($(this), options.prefix, '__prefix__')
 | 
			
		||||
        })
 | 
			
		||||
        insertDeleteLink(template)
 | 
			
		||||
      } else {
 | 
			
		||||
        // Otherwise, use the last form in the formset; this works much better if you've got
 | 
			
		||||
        // extra (>= 1) forms (thnaks to justhamade for pointing this out):
 | 
			
		||||
        if (options.hideLastAddForm) $('.' + options.formCssClass + ':last').hide()
 | 
			
		||||
        template = $('.' + options.formCssClass + ':last').clone(true).removeAttr('id')
 | 
			
		||||
        template.find('input:hidden[id $= "-DELETE"]').remove()
 | 
			
		||||
        // Clear all cloned fields, except those the user wants to keep (thanks to brunogola for the suggestion):
 | 
			
		||||
        template.find(childElementSelector).not(options.keepFieldValues).each(function () {
 | 
			
		||||
          var elem = $(this)
 | 
			
		||||
          // If this is a checkbox or radiobutton, uncheck it.
 | 
			
		||||
          // This fixes Issue 1, reported by Wilson.Andrew.J:
 | 
			
		||||
          if (elem.is('input:checkbox') || elem.is('input:radio')) {
 | 
			
		||||
            elem.attr('checked', false)
 | 
			
		||||
          } else {
 | 
			
		||||
            elem.val('')
 | 
			
		||||
          }
 | 
			
		||||
        })
 | 
			
		||||
      }
 | 
			
		||||
      // FIXME: Perhaps using $.data would be a better idea?
 | 
			
		||||
      options.formTemplate = template
 | 
			
		||||
 | 
			
		||||
      var addButtonHTML = '<a class="' + options.addCssClass + '" href="javascript:void(0)">' + options.addText + '</a>'
 | 
			
		||||
      if (options.addContainerClass) {
 | 
			
		||||
        // If we have a specific container for the "add" button,
 | 
			
		||||
        // place it as the last child of that container:
 | 
			
		||||
        var addContainer = $('[class*="' + options.addContainerClass + '"')
 | 
			
		||||
        addContainer.append(addButtonHTML)
 | 
			
		||||
        addButton = addContainer.find('[class="' + options.addCssClass + '"]')
 | 
			
		||||
      } else if ($$.is('TR')) {
 | 
			
		||||
        // If forms are laid out as table rows, insert the
 | 
			
		||||
        // "add" button in a new table row:
 | 
			
		||||
        var numCols = $$.eq(0).children().length // This is a bit of an assumption :|
 | 
			
		||||
        var buttonRow = $('<tr><td colspan="' + numCols + '">' + addButtonHTML + '</tr>').addClass(options.formCssClass + '-add')
 | 
			
		||||
        $$.parent().append(buttonRow)
 | 
			
		||||
        addButton = buttonRow.find('a')
 | 
			
		||||
      } else {
 | 
			
		||||
        // Otherwise, insert it immediately after the last form:
 | 
			
		||||
        $$.filter(':last').after(addButtonHTML)
 | 
			
		||||
        addButton = $$.filter(':last').next()
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      if (hideAddButton) addButton.hide()
 | 
			
		||||
 | 
			
		||||
      addButton.click(function () {
 | 
			
		||||
        var formCount = parseInt(totalForms.val())
 | 
			
		||||
        var row = options.formTemplate.clone(true).removeClass('formset-custom-template')
 | 
			
		||||
        var buttonRow = $($(this).parents('tr.' + options.formCssClass + '-add').get(0) || this)
 | 
			
		||||
        var delCssSelector = $.trim(options.deleteCssClass).replace(/\s+/g, '.')
 | 
			
		||||
        applyExtraClasses(row, formCount)
 | 
			
		||||
        row.insertBefore(buttonRow).show()
 | 
			
		||||
        row.find(childElementSelector).each(function () {
 | 
			
		||||
          updateElementIndex($(this), options.prefix, formCount)
 | 
			
		||||
        })
 | 
			
		||||
        totalForms.val(formCount + 1)
 | 
			
		||||
        // Check if we're above the minimum allowed number of forms -> show all delete link(s)
 | 
			
		||||
        if (showDeleteLinks()) {
 | 
			
		||||
          $('a.' + delCssSelector).each(function () { $(this).show() })
 | 
			
		||||
        }
 | 
			
		||||
        // Check if we've exceeded the maximum allowed number of forms:
 | 
			
		||||
        if (!showAddButton()) buttonRow.hide()
 | 
			
		||||
        // If a post-add callback was supplied, call it with the added form:
 | 
			
		||||
        if (options.added) options.added(row)
 | 
			
		||||
        return false
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return $$
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Setup plugin defaults */
 | 
			
		||||
  $.fn.formset.defaults = {
 | 
			
		||||
    prefix: 'form', // The form prefix for your django formset
 | 
			
		||||
    formTemplate: null, // The jQuery selection cloned to generate new form instances
 | 
			
		||||
    addText: 'add another', // Text for the add link
 | 
			
		||||
    deleteText: 'remove', // Text for the delete link
 | 
			
		||||
    addContainerClass: null, // Container CSS class for the add link
 | 
			
		||||
    deleteContainerClass: null, // Container CSS class for the delete link
 | 
			
		||||
    addCssClass: 'add-row', // CSS class applied to the add link
 | 
			
		||||
    deleteCssClass: 'delete-row', // CSS class applied to the delete link
 | 
			
		||||
    formCssClass: 'dynamic-form', // CSS class applied to each form in a formset
 | 
			
		||||
    extraClasses: [], // Additional CSS classes, which will be applied to each form in turn
 | 
			
		||||
    keepFieldValues: '', // jQuery selector for fields whose values should be kept when the form is cloned
 | 
			
		||||
    added: null, // Function called each time a new form is added
 | 
			
		||||
    removed: null, // Function called each time a form is deleted
 | 
			
		||||
    hideLastAddForm: false // When set to true, hide last empty add form (becomes visible when clicking on add button)
 | 
			
		||||
  }
 | 
			
		||||
})(jQuery)
 | 
			
		||||
 
 | 
			
		||||
@@ -6,40 +6,40 @@
 | 
			
		||||
let cursor = 0
 | 
			
		||||
const KONAMI_CODE = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65]
 | 
			
		||||
 | 
			
		||||
function afterKonami() {
 | 
			
		||||
    // Load Rythm.js
 | 
			
		||||
    var rythmScript = document.createElement('script')
 | 
			
		||||
    rythmScript.setAttribute('src','//unpkg.com/rythm.js@2.2.5/rythm.min.js')
 | 
			
		||||
    document.head.appendChild(rythmScript)
 | 
			
		||||
function afterKonami () {
 | 
			
		||||
  // Load Rythm.js
 | 
			
		||||
  var rythmScript = document.createElement('script')
 | 
			
		||||
  rythmScript.setAttribute('src', '//unpkg.com/rythm.js@2.2.5/rythm.min.js')
 | 
			
		||||
  document.head.appendChild(rythmScript)
 | 
			
		||||
 | 
			
		||||
    rythmScript.addEventListener('load', function() {
 | 
			
		||||
        // Ker-Lyon audio courtesy of @adalan, ker-lyon.fr
 | 
			
		||||
        const audioElement = new Audio('/static/song/konami.ogg')
 | 
			
		||||
        audioElement.loop = true
 | 
			
		||||
        audioElement.play()
 | 
			
		||||
  rythmScript.addEventListener('load', function () {
 | 
			
		||||
    // Ker-Lyon audio courtesy of @adalan, ker-lyon.fr
 | 
			
		||||
    const audioElement = new Audio('/static/song/konami.ogg')
 | 
			
		||||
    audioElement.loop = true
 | 
			
		||||
    audioElement.play()
 | 
			
		||||
 | 
			
		||||
        const rythm = new Rythm()
 | 
			
		||||
        rythm.connectExternalAudioElement(audioElement)
 | 
			
		||||
        rythm.addRythm('card', 'pulse', 50, 50, {
 | 
			
		||||
            min: 1,
 | 
			
		||||
            max: 1.1
 | 
			
		||||
        })
 | 
			
		||||
        rythm.addRythm('d-flex', 'color', 50, 50, {
 | 
			
		||||
           from: [64,64,64],
 | 
			
		||||
           to:[128,64,128]
 | 
			
		||||
        })
 | 
			
		||||
        rythm.addRythm('nav-link', 'jump', 150, 50, {
 | 
			
		||||
              min: 0,
 | 
			
		||||
              max: 10
 | 
			
		||||
        })
 | 
			
		||||
        rythm.start()
 | 
			
		||||
    });
 | 
			
		||||
    const rythm = new Rythm()
 | 
			
		||||
    rythm.connectExternalAudioElement(audioElement)
 | 
			
		||||
    rythm.addRythm('card', 'pulse', 50, 50, {
 | 
			
		||||
      min: 1,
 | 
			
		||||
      max: 1.1
 | 
			
		||||
    })
 | 
			
		||||
    rythm.addRythm('d-flex', 'color', 50, 50, {
 | 
			
		||||
      from: [64, 64, 64],
 | 
			
		||||
      to: [128, 64, 128]
 | 
			
		||||
    })
 | 
			
		||||
    rythm.addRythm('nav-link', 'jump', 150, 50, {
 | 
			
		||||
      min: 0,
 | 
			
		||||
      max: 10
 | 
			
		||||
    })
 | 
			
		||||
    rythm.start()
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Register custom event
 | 
			
		||||
document.addEventListener('keydown', (e) => {
 | 
			
		||||
    cursor = (e.keyCode == KONAMI_CODE[cursor]) ? cursor + 1 : 0;
 | 
			
		||||
    if (cursor == KONAMI_CODE.length) {
 | 
			
		||||
        afterKonami()
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
  cursor = (e.keyCode == KONAMI_CODE[cursor]) ? cursor + 1 : 0
 | 
			
		||||
  if (cursor == KONAMI_CODE.length) {
 | 
			
		||||
    afterKonami()
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
@@ -1,440 +1,422 @@
 | 
			
		||||
var LOCK = false;
 | 
			
		||||
var LOCK = false
 | 
			
		||||
 | 
			
		||||
sources = [];
 | 
			
		||||
sources_notes_display = [];
 | 
			
		||||
dests = [];
 | 
			
		||||
dests_notes_display = [];
 | 
			
		||||
sources = []
 | 
			
		||||
sources_notes_display = []
 | 
			
		||||
dests = []
 | 
			
		||||
dests_notes_display = []
 | 
			
		||||
 | 
			
		||||
function refreshHistory() {
 | 
			
		||||
    $("#history").load("/note/transfer/ #history");
 | 
			
		||||
function refreshHistory () {
 | 
			
		||||
  $('#history').load('/note/transfer/ #history')
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function reset(refresh=true) {
 | 
			
		||||
    sources_notes_display.length = 0;
 | 
			
		||||
    sources.length = 0;
 | 
			
		||||
    dests_notes_display.length = 0;
 | 
			
		||||
    dests.length = 0;
 | 
			
		||||
    $("#source_note_list").html("");
 | 
			
		||||
    $("#dest_note_list").html("");
 | 
			
		||||
    let source_field = $("#source_note");
 | 
			
		||||
    source_field.val("");
 | 
			
		||||
    let event = jQuery.Event("keyup");
 | 
			
		||||
    event.originalEvent = {charCode: 97};
 | 
			
		||||
    source_field.trigger(event);
 | 
			
		||||
    source_field.removeClass('is-invalid');
 | 
			
		||||
    source_field.attr("data-original-title", "").tooltip("hide");
 | 
			
		||||
    let dest_field = $("#dest_note");
 | 
			
		||||
    dest_field.val("");
 | 
			
		||||
    dest_field.trigger(event);
 | 
			
		||||
    dest_field.removeClass('is-invalid');
 | 
			
		||||
    dest_field.attr("data-original-title", "").tooltip("hide");
 | 
			
		||||
    let amount_field = $("#amount");
 | 
			
		||||
    amount_field.val("");
 | 
			
		||||
    amount_field.removeClass('is-invalid');
 | 
			
		||||
    $("#amount-required").html("");
 | 
			
		||||
    let reason_field = $("#reason");
 | 
			
		||||
    reason_field.val("");
 | 
			
		||||
    reason_field.removeClass('is-invalid');
 | 
			
		||||
    $("#reason-required").html("");
 | 
			
		||||
    $("#last_name").val("");
 | 
			
		||||
    $("#first_name").val("");
 | 
			
		||||
    $("#bank").val("");
 | 
			
		||||
    $("#user_note").val("");
 | 
			
		||||
    $("#profile_pic").attr("src", "/static/member/img/default_picture.png");
 | 
			
		||||
    $("#profile_pic_link").attr("href", "#");
 | 
			
		||||
    if (refresh) {
 | 
			
		||||
        refreshBalance();
 | 
			
		||||
        refreshHistory();
 | 
			
		||||
    }
 | 
			
		||||
function reset (refresh = true) {
 | 
			
		||||
  sources_notes_display.length = 0
 | 
			
		||||
  sources.length = 0
 | 
			
		||||
  dests_notes_display.length = 0
 | 
			
		||||
  dests.length = 0
 | 
			
		||||
  $('#source_note_list').html('')
 | 
			
		||||
  $('#dest_note_list').html('')
 | 
			
		||||
  const source_field = $('#source_note')
 | 
			
		||||
  source_field.val('')
 | 
			
		||||
  const event = jQuery.Event('keyup')
 | 
			
		||||
  event.originalEvent = { charCode: 97 }
 | 
			
		||||
  source_field.trigger(event)
 | 
			
		||||
  source_field.removeClass('is-invalid')
 | 
			
		||||
  source_field.attr('data-original-title', '').tooltip('hide')
 | 
			
		||||
  const dest_field = $('#dest_note')
 | 
			
		||||
  dest_field.val('')
 | 
			
		||||
  dest_field.trigger(event)
 | 
			
		||||
  dest_field.removeClass('is-invalid')
 | 
			
		||||
  dest_field.attr('data-original-title', '').tooltip('hide')
 | 
			
		||||
  const amount_field = $('#amount')
 | 
			
		||||
  amount_field.val('')
 | 
			
		||||
  amount_field.removeClass('is-invalid')
 | 
			
		||||
  $('#amount-required').html('')
 | 
			
		||||
  const reason_field = $('#reason')
 | 
			
		||||
  reason_field.val('')
 | 
			
		||||
  reason_field.removeClass('is-invalid')
 | 
			
		||||
  $('#reason-required').html('')
 | 
			
		||||
  $('#last_name').val('')
 | 
			
		||||
  $('#first_name').val('')
 | 
			
		||||
  $('#bank').val('')
 | 
			
		||||
  $('#user_note').val('')
 | 
			
		||||
  $('#profile_pic').attr('src', '/static/member/img/default_picture.png')
 | 
			
		||||
  $('#profile_pic_link').attr('href', '#')
 | 
			
		||||
  if (refresh) {
 | 
			
		||||
    refreshBalance()
 | 
			
		||||
    refreshHistory()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
    LOCK = false;
 | 
			
		||||
  LOCK = false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
$(document).ready(function() {
 | 
			
		||||
    /**
 | 
			
		||||
$(document).ready(function () {
 | 
			
		||||
  /**
 | 
			
		||||
     * 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;
 | 
			
		||||
  function checkUniqueNote () {
 | 
			
		||||
    if ($('#type_credit').is(':checked') || $('#type_debit').is(':checked')) {
 | 
			
		||||
      const arr = $('#type_credit').is(':checked') ? dests_notes_display : sources_notes_display
 | 
			
		||||
 | 
			
		||||
            if (arr.length === 0)
 | 
			
		||||
                return;
 | 
			
		||||
      if (arr.length === 0) { return }
 | 
			
		||||
 | 
			
		||||
            let last = arr[arr.length - 1];
 | 
			
		||||
            arr.length = 0;
 | 
			
		||||
            arr.push(last);
 | 
			
		||||
      const last = arr[arr.length - 1]
 | 
			
		||||
      arr.length = 0
 | 
			
		||||
      arr.push(last)
 | 
			
		||||
 | 
			
		||||
            last.quantity = 1;
 | 
			
		||||
      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);
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
      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
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  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)
 | 
			
		||||
 | 
			
		||||
  const source = $('#source_note')
 | 
			
		||||
  const dest = $('#dest_note')
 | 
			
		||||
 | 
			
		||||
  $('#type_transfer').change(function () {
 | 
			
		||||
    if (LOCK) { return }
 | 
			
		||||
 | 
			
		||||
    $('#source_me_div').removeClass('d-none')
 | 
			
		||||
    $('#source_note').removeClass('is-invalid')
 | 
			
		||||
    $('#dest_note').removeClass('is-invalid')
 | 
			
		||||
    $('#special_transaction_div').addClass('d-none')
 | 
			
		||||
    source.removeClass('d-none')
 | 
			
		||||
    $('#source_note_list').removeClass('d-none')
 | 
			
		||||
    $('#credit_type').addClass('d-none')
 | 
			
		||||
    dest.removeClass('d-none')
 | 
			
		||||
    $('#dest_note_list').removeClass('d-none')
 | 
			
		||||
    $('#debit_type').addClass('d-none')
 | 
			
		||||
 | 
			
		||||
    $('#source_note_label').text(select_emitters_label)
 | 
			
		||||
    $('#dest_note_label').text(select_receveirs_label)
 | 
			
		||||
 | 
			
		||||
    location.hash = 'transfer'
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  $('#type_credit').change(function () {
 | 
			
		||||
    if (LOCK) { return }
 | 
			
		||||
 | 
			
		||||
    $('#source_me_div').addClass('d-none')
 | 
			
		||||
    $('#source_note').removeClass('is-invalid')
 | 
			
		||||
    $('#dest_note').removeClass('is-invalid')
 | 
			
		||||
    $('#special_transaction_div').removeClass('d-none')
 | 
			
		||||
    $('#source_note_list').addClass('d-none')
 | 
			
		||||
    $('#dest_note_list').removeClass('d-none')
 | 
			
		||||
    source.addClass('d-none')
 | 
			
		||||
    source.tooltip('hide')
 | 
			
		||||
    $('#credit_type').removeClass('d-none')
 | 
			
		||||
    dest.removeClass('d-none')
 | 
			
		||||
    dest.val('')
 | 
			
		||||
    dest.tooltip('hide')
 | 
			
		||||
    $('#debit_type').addClass('d-none')
 | 
			
		||||
 | 
			
		||||
    $('#source_note_label').text(transfer_type_label)
 | 
			
		||||
    $('#dest_note_label').text(select_receveir_label)
 | 
			
		||||
 | 
			
		||||
    if (dests_notes_display.length > 1) {
 | 
			
		||||
      $('#dest_note_list').html('')
 | 
			
		||||
      dests_notes_display.length = 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    location.hash = 'credit'
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  $('#type_debit').change(function () {
 | 
			
		||||
    if (LOCK) { return }
 | 
			
		||||
 | 
			
		||||
    $('#source_me_div').addClass('d-none')
 | 
			
		||||
    $('#source_note').removeClass('is-invalid')
 | 
			
		||||
    $('#dest_note').removeClass('is-invalid')
 | 
			
		||||
    $('#special_transaction_div').removeClass('d-none')
 | 
			
		||||
    $('#source_note_list').removeClass('d-none')
 | 
			
		||||
    $('#dest_note_list').addClass('d-none')
 | 
			
		||||
    source.removeClass('d-none')
 | 
			
		||||
    source.val('')
 | 
			
		||||
    source.tooltip('hide')
 | 
			
		||||
    $('#credit_type').addClass('d-none')
 | 
			
		||||
    dest.addClass('d-none')
 | 
			
		||||
    dest.tooltip('hide')
 | 
			
		||||
    $('#debit_type').removeClass('d-none')
 | 
			
		||||
 | 
			
		||||
    $('#source_note_label').text(select_emitter_label)
 | 
			
		||||
    $('#dest_note_label').text(transfer_type_label)
 | 
			
		||||
 | 
			
		||||
    if (sources_notes_display.length > 1) {
 | 
			
		||||
      $('#source_note_list').html('')
 | 
			
		||||
      sources_notes_display.length = 0
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    location.hash = 'debit'
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  $('#credit_type').change(function () {
 | 
			
		||||
    const type = $('#credit_type option:selected').text()
 | 
			
		||||
    if ($('#type_credit').is(':checked')) { source.val(type) } else { dest.val(type) }
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  // Ensure we begin in transfer mode. Removing these lines may cause problems when reloading.
 | 
			
		||||
  const type_transfer = $('#type_transfer') // Default mode
 | 
			
		||||
  type_transfer.removeAttr('checked')
 | 
			
		||||
  $('#type_credit').removeAttr('checked')
 | 
			
		||||
  $('#type_debit').removeAttr('checked')
 | 
			
		||||
 | 
			
		||||
  if (location.hash) { $('#type_' + location.hash.substr(1)).click() } else { type_transfer.click() }
 | 
			
		||||
 | 
			
		||||
  $('#source_me').click(function () {
 | 
			
		||||
    if (LOCK) { return }
 | 
			
		||||
 | 
			
		||||
    // Shortcut to set the current user as the only emitter
 | 
			
		||||
    sources_notes_display.length = 0
 | 
			
		||||
    sources.length = 0
 | 
			
		||||
    $('#source_note_list').html('')
 | 
			
		||||
 | 
			
		||||
    const source_note = $('#source_note')
 | 
			
		||||
    source_note.focus()
 | 
			
		||||
    source_note.val('')
 | 
			
		||||
    let event = jQuery.Event('keyup')
 | 
			
		||||
    event.originalEvent = { charCode: 97 }
 | 
			
		||||
    source_note.trigger(event)
 | 
			
		||||
    source_note.val(username)
 | 
			
		||||
    event = jQuery.Event('keyup')
 | 
			
		||||
    event.originalEvent = { charCode: 97 }
 | 
			
		||||
    source_note.trigger(event)
 | 
			
		||||
    const fill_note = function () {
 | 
			
		||||
      if (sources.length === 0) {
 | 
			
		||||
        setTimeout(fill_note, 100)
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      event = jQuery.Event('keypress')
 | 
			
		||||
      event.originalEvent = { charCode: 13 }
 | 
			
		||||
      source_note.trigger(event)
 | 
			
		||||
 | 
			
		||||
      source_note.tooltip('hide')
 | 
			
		||||
      source_note.val('')
 | 
			
		||||
      $('#dest_note').focus()
 | 
			
		||||
    }
 | 
			
		||||
    fill_note()
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
$('#btn_transfer').click(function () {
 | 
			
		||||
  if (LOCK) { return }
 | 
			
		||||
 | 
			
		||||
  LOCK = true
 | 
			
		||||
 | 
			
		||||
  let error = false
 | 
			
		||||
 | 
			
		||||
  const amount_field = $('#amount')
 | 
			
		||||
  amount_field.removeClass('is-invalid')
 | 
			
		||||
  $('#amount-required').html('')
 | 
			
		||||
 | 
			
		||||
  const reason_field = $('#reason')
 | 
			
		||||
  reason_field.removeClass('is-invalid')
 | 
			
		||||
  $('#reason-required').html('')
 | 
			
		||||
 | 
			
		||||
  if (!amount_field.val() || isNaN(amount_field.val()) || amount_field.val() <= 0) {
 | 
			
		||||
    amount_field.addClass('is-invalid')
 | 
			
		||||
    $('#amount-required').html('<strong>Ce champ est requis et doit comporter un nombre décimal strictement positif.</strong>')
 | 
			
		||||
    error = true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const amount = Math.floor(100 * amount_field.val())
 | 
			
		||||
  if (amount > 2147483647) {
 | 
			
		||||
    amount_field.addClass('is-invalid')
 | 
			
		||||
    $('#amount-required').html('<strong>Le montant ne doit pas excéder 21474836.47 €.</strong>')
 | 
			
		||||
    error = true
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (!reason_field.val()) {
 | 
			
		||||
    reason_field.addClass('is-invalid')
 | 
			
		||||
    $('#reason-required').html('<strong>Ce champ est requis.</strong>')
 | 
			
		||||
    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) {
 | 
			
		||||
    LOCK = false
 | 
			
		||||
    return
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  let reason = reason_field.val()
 | 
			
		||||
 | 
			
		||||
  if ($('#type_transfer').is(':checked')) {
 | 
			
		||||
    // We copy the arrays to ensure that transactions are well-processed even if the form is reset
 | 
			
		||||
    [...sources_notes_display].forEach(function (source) {
 | 
			
		||||
      [...dests_notes_display].forEach(function (dest) {
 | 
			
		||||
        if (source.note.id === dest.note.id) {
 | 
			
		||||
          addMsg('Attention : la transaction de ' + pretty_money(amount) + ' de la note ' + source.name +
 | 
			
		||||
                        ' vers la note ' + dest.name + " n'a pas été faite car il s'agit de la même note au départ" +
 | 
			
		||||
                        " et à l'arrivée.", 'warning', 10000)
 | 
			
		||||
          LOCK = false
 | 
			
		||||
          return
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        $.post('/api/note/transaction/transaction/',
 | 
			
		||||
          {
 | 
			
		||||
            csrfmiddlewaretoken: CSRF_TOKEN,
 | 
			
		||||
            quantity: source.quantity * dest.quantity,
 | 
			
		||||
            amount: amount,
 | 
			
		||||
            reason: reason,
 | 
			
		||||
            valid: true,
 | 
			
		||||
            polymorphic_ctype: TRANSFER_POLYMORPHIC_CTYPE,
 | 
			
		||||
            resourcetype: 'Transaction',
 | 
			
		||||
            source: source.note.id,
 | 
			
		||||
            source_alias: source.name,
 | 
			
		||||
            destination: dest.note.id,
 | 
			
		||||
            destination_alias: dest.name
 | 
			
		||||
          }).done(function () {
 | 
			
		||||
          if (source.note.membership && source.note.membership.date_end < new Date().toISOString()) {
 | 
			
		||||
            addMsg('Attention : la note émettrice ' + source.name + " n'est plus adhérente.",
 | 
			
		||||
              'danger', 30000)
 | 
			
		||||
          }
 | 
			
		||||
          if (dest.note.membership && dest.note.membership.date_end < new Date().toISOString()) {
 | 
			
		||||
            addMsg('Attention : la note destination ' + dest.name + " n'est plus adhérente.",
 | 
			
		||||
              'danger', 30000)
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
          if (!isNaN(source.note.balance)) {
 | 
			
		||||
            const newBalance = source.note.balance - source.quantity * dest.quantity * amount
 | 
			
		||||
            if (newBalance <= -5000) {
 | 
			
		||||
              addMsg('Le transfert de ' +
 | 
			
		||||
                                    pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' +
 | 
			
		||||
                                    source.name + ' vers la note ' + dest.name + ' a été fait avec succès, ' +
 | 
			
		||||
                                    'mais la note émettrice est en négatif sévère.', 'danger', 10000)
 | 
			
		||||
              reset()
 | 
			
		||||
              return
 | 
			
		||||
            } else if (newBalance < 0) {
 | 
			
		||||
              addMsg('Le transfert de ' +
 | 
			
		||||
                                    pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' +
 | 
			
		||||
                                    source.name + ' vers la note ' + dest.name + ' a été fait avec succès, ' +
 | 
			
		||||
                                    'mais la note émettrice est en négatif.', 'warning', 10000)
 | 
			
		||||
              reset()
 | 
			
		||||
              return
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                $.getJSON("/api/user/" + last.note.user + "/", function(user) {
 | 
			
		||||
                    $("#last_name").val(user.last_name);
 | 
			
		||||
                    $("#first_name").val(user.first_name);
 | 
			
		||||
                });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
          }
 | 
			
		||||
          addMsg('Le transfert de ' +
 | 
			
		||||
                            pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
 | 
			
		||||
                            ' vers la note ' + dest.name + ' a été fait avec succès !', 'success', 10000)
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
   }
 | 
			
		||||
          reset()
 | 
			
		||||
        }).fail(function (err) { // do it again but valid = false
 | 
			
		||||
          const errObj = JSON.parse(err.responseText)
 | 
			
		||||
          if (errObj.non_field_errors) {
 | 
			
		||||
            addMsg('Le transfert de ' +
 | 
			
		||||
                                pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
 | 
			
		||||
                                ' vers la note ' + dest.name + ' a échoué : ' + errObj.non_field_errors, 'danger')
 | 
			
		||||
            LOCK = false
 | 
			
		||||
            return
 | 
			
		||||
          }
 | 
			
		||||
 | 
			
		||||
    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_transfer").change(function() {
 | 
			
		||||
        if (LOCK)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        $("#source_me_div").removeClass('d-none');
 | 
			
		||||
        $("#source_note").removeClass('is-invalid');
 | 
			
		||||
        $("#dest_note").removeClass('is-invalid');
 | 
			
		||||
        $("#special_transaction_div").addClass('d-none');
 | 
			
		||||
        source.removeClass('d-none');
 | 
			
		||||
        $("#source_note_list").removeClass('d-none');
 | 
			
		||||
        $("#credit_type").addClass('d-none');
 | 
			
		||||
        dest.removeClass('d-none');
 | 
			
		||||
        $("#dest_note_list").removeClass('d-none');
 | 
			
		||||
        $("#debit_type").addClass('d-none');
 | 
			
		||||
 | 
			
		||||
        $("#source_note_label").text(select_emitters_label);
 | 
			
		||||
        $("#dest_note_label").text(select_receveirs_label);
 | 
			
		||||
 | 
			
		||||
        location.hash = "transfer";
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $("#type_credit").change(function() {
 | 
			
		||||
        if (LOCK)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        $("#source_me_div").addClass('d-none');
 | 
			
		||||
        $("#source_note").removeClass('is-invalid');
 | 
			
		||||
        $("#dest_note").removeClass('is-invalid');
 | 
			
		||||
        $("#special_transaction_div").removeClass('d-none');
 | 
			
		||||
        $("#source_note_list").addClass('d-none');
 | 
			
		||||
        $("#dest_note_list").removeClass('d-none');
 | 
			
		||||
        source.addClass('d-none');
 | 
			
		||||
        source.tooltip('hide');
 | 
			
		||||
        $("#credit_type").removeClass('d-none');
 | 
			
		||||
        dest.removeClass('d-none');
 | 
			
		||||
        dest.val('');
 | 
			
		||||
        dest.tooltip('hide');
 | 
			
		||||
        $("#debit_type").addClass('d-none');
 | 
			
		||||
 | 
			
		||||
        $("#source_note_label").text(transfer_type_label);
 | 
			
		||||
        $("#dest_note_label").text(select_receveir_label);
 | 
			
		||||
 | 
			
		||||
        if (dests_notes_display.length > 1) {
 | 
			
		||||
            $("#dest_note_list").html('');
 | 
			
		||||
            dests_notes_display.length = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        location.hash = "credit";
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    $("#type_debit").change(function() {
 | 
			
		||||
        if (LOCK)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        $("#source_me_div").addClass('d-none');
 | 
			
		||||
        $("#source_note").removeClass('is-invalid');
 | 
			
		||||
        $("#dest_note").removeClass('is-invalid');
 | 
			
		||||
        $("#special_transaction_div").removeClass('d-none');
 | 
			
		||||
        $("#source_note_list").removeClass('d-none');
 | 
			
		||||
        $("#dest_note_list").addClass('d-none');
 | 
			
		||||
        source.removeClass('d-none');
 | 
			
		||||
        source.val('');
 | 
			
		||||
        source.tooltip('hide');
 | 
			
		||||
        $("#credit_type").addClass('d-none');
 | 
			
		||||
        dest.addClass('d-none');
 | 
			
		||||
        dest.tooltip('hide');
 | 
			
		||||
        $("#debit_type").removeClass('d-none');
 | 
			
		||||
 | 
			
		||||
        $("#source_note_label").text(select_emitter_label);
 | 
			
		||||
        $("#dest_note_label").text(transfer_type_label);
 | 
			
		||||
 | 
			
		||||
        if (sources_notes_display.length > 1) {
 | 
			
		||||
            $("#source_note_list").html('');
 | 
			
		||||
            sources_notes_display.length = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        location.hash = "debit";
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
        $("#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 transfer mode. Removing these lines may cause problems when reloading.
 | 
			
		||||
    let type_transfer = $("#type_transfer"); // Default mode
 | 
			
		||||
    type_transfer.removeAttr('checked');
 | 
			
		||||
    $("#type_credit").removeAttr('checked');
 | 
			
		||||
    $("#type_debit").removeAttr('checked');
 | 
			
		||||
 | 
			
		||||
    if (location.hash)
 | 
			
		||||
        $("#type_" + location.hash.substr(1)).click();
 | 
			
		||||
    else
 | 
			
		||||
        type_transfer.click();
 | 
			
		||||
 | 
			
		||||
    $("#source_me").click(function() {
 | 
			
		||||
        if (LOCK)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        // Shortcut to set the current user as the only emitter
 | 
			
		||||
        sources_notes_display.length = 0;
 | 
			
		||||
        sources.length = 0;
 | 
			
		||||
        $("#source_note_list").html("");
 | 
			
		||||
 | 
			
		||||
        let source_note = $("#source_note");
 | 
			
		||||
        source_note.focus();
 | 
			
		||||
        source_note.val("");
 | 
			
		||||
        let event = jQuery.Event("keyup");
 | 
			
		||||
        event.originalEvent = {charCode: 97};
 | 
			
		||||
        source_note.trigger(event);
 | 
			
		||||
        source_note.val(username);
 | 
			
		||||
        event = jQuery.Event("keyup");
 | 
			
		||||
        event.originalEvent = {charCode: 97};
 | 
			
		||||
        source_note.trigger(event);
 | 
			
		||||
        let fill_note = function() {
 | 
			
		||||
            if (sources.length === 0) {
 | 
			
		||||
                setTimeout(fill_note, 100);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            event = jQuery.Event("keypress");
 | 
			
		||||
            event.originalEvent = {charCode: 13};
 | 
			
		||||
            source_note.trigger(event);
 | 
			
		||||
 | 
			
		||||
            source_note.tooltip('hide');
 | 
			
		||||
            source_note.val('');
 | 
			
		||||
            $("#dest_note").focus();
 | 
			
		||||
        };
 | 
			
		||||
        fill_note();
 | 
			
		||||
    });
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
$("#btn_transfer").click(function() {
 | 
			
		||||
    if (LOCK)
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    LOCK = true;
 | 
			
		||||
 | 
			
		||||
    let error = false;
 | 
			
		||||
 | 
			
		||||
    let amount_field = $("#amount");
 | 
			
		||||
    amount_field.removeClass('is-invalid');
 | 
			
		||||
    $("#amount-required").html("");
 | 
			
		||||
 | 
			
		||||
    let reason_field = $("#reason");
 | 
			
		||||
    reason_field.removeClass('is-invalid');
 | 
			
		||||
    $("#reason-required").html("");
 | 
			
		||||
 | 
			
		||||
    if (!amount_field.val() || isNaN(amount_field.val()) || amount_field.val() <= 0) {
 | 
			
		||||
        amount_field.addClass('is-invalid');
 | 
			
		||||
        $("#amount-required").html("<strong>Ce champ est requis et doit comporter un nombre décimal strictement positif.</strong>");
 | 
			
		||||
        error = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let amount = Math.floor(100 * amount_field.val());
 | 
			
		||||
    if (amount > 2147483647) {
 | 
			
		||||
        amount_field.addClass('is-invalid');
 | 
			
		||||
        $("#amount-required").html("<strong>Le montant ne doit pas excéder 21474836.47 €.</strong>");
 | 
			
		||||
        error = true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!reason_field.val()) {
 | 
			
		||||
        reason_field.addClass('is-invalid');
 | 
			
		||||
        $("#reason-required").html("<strong>Ce champ est requis.</strong>");
 | 
			
		||||
        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) {
 | 
			
		||||
        LOCK = false;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let reason = reason_field.val();
 | 
			
		||||
 | 
			
		||||
    if ($("#type_transfer").is(':checked')) {
 | 
			
		||||
        // We copy the arrays to ensure that transactions are well-processed even if the form is reset
 | 
			
		||||
        [...sources_notes_display].forEach(function (source) {
 | 
			
		||||
            [...dests_notes_display].forEach(function (dest) {
 | 
			
		||||
                if (source.note.id === dest.note.id) {
 | 
			
		||||
                    addMsg("Attention : la transaction de " + pretty_money(amount) + " de la note " + source.name
 | 
			
		||||
                        + " vers la note " + dest.name + " n'a pas été faite car il s'agit de la même note au départ" +
 | 
			
		||||
                        " et à l'arrivée.","warning", 10000);
 | 
			
		||||
                    LOCK = false;
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                $.post("/api/note/transaction/transaction/",
 | 
			
		||||
                    {
 | 
			
		||||
                        "csrfmiddlewaretoken": CSRF_TOKEN,
 | 
			
		||||
                        "quantity": source.quantity * dest.quantity,
 | 
			
		||||
                        "amount": amount,
 | 
			
		||||
                        "reason": reason,
 | 
			
		||||
                        "valid": true,
 | 
			
		||||
                        "polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE,
 | 
			
		||||
                        "resourcetype": "Transaction",
 | 
			
		||||
                        "source": source.note.id,
 | 
			
		||||
                        "source_alias": source.name,
 | 
			
		||||
                        "destination": dest.note.id,
 | 
			
		||||
                        "destination_alias": dest.name
 | 
			
		||||
                    }).done(function () {
 | 
			
		||||
                        if (source.note.membership && source.note.membership.date_end < new Date().toISOString())
 | 
			
		||||
                            addMsg("Attention : la note émettrice " + source.name + " n'est plus adhérente.",
 | 
			
		||||
                                "danger", 30000);
 | 
			
		||||
                        if (dest.note.membership && dest.note.membership.date_end < new Date().toISOString())
 | 
			
		||||
                            addMsg("Attention : la note destination " + dest.name + " n'est plus adhérente.",
 | 
			
		||||
                                "danger", 30000);
 | 
			
		||||
 | 
			
		||||
                        if (!isNaN(source.note.balance)) {
 | 
			
		||||
                            let newBalance = source.note.balance - source.quantity * dest.quantity * amount;
 | 
			
		||||
                            if (newBalance <= -5000) {
 | 
			
		||||
                                addMsg("Le transfert de "
 | 
			
		||||
                                    + pretty_money(source.quantity * dest.quantity * amount) + " de la note "
 | 
			
		||||
                                    + source.name + " vers la note " + dest.name + " a été fait avec succès, " +
 | 
			
		||||
                                    "mais la note émettrice est en négatif sévère.", "danger", 10000);
 | 
			
		||||
                                reset();
 | 
			
		||||
                                return;
 | 
			
		||||
                            }
 | 
			
		||||
                            else if (newBalance < 0) {
 | 
			
		||||
                                addMsg("Le transfert de "
 | 
			
		||||
                                    + pretty_money(source.quantity * dest.quantity * amount) + " de la note "
 | 
			
		||||
                                    + source.name + " vers la note " + dest.name + " a été fait avec succès, " +
 | 
			
		||||
                                    "mais la note émettrice est en négatif.", "warning", 10000);
 | 
			
		||||
                                reset();
 | 
			
		||||
                                return;
 | 
			
		||||
                            }
 | 
			
		||||
                        }
 | 
			
		||||
                        addMsg("Le transfert de "
 | 
			
		||||
                            + pretty_money(source.quantity * dest.quantity * amount) + " de la note " + source.name
 | 
			
		||||
                            + " vers la note " + dest.name + " a été fait avec succès !", "success", 10000);
 | 
			
		||||
 | 
			
		||||
                        reset();
 | 
			
		||||
                    }).fail(function (err) { // do it again but valid = false
 | 
			
		||||
                        let errObj = JSON.parse(err.responseText);
 | 
			
		||||
                        if (errObj["non_field_errors"]) {
 | 
			
		||||
                            addMsg("Le transfert de "
 | 
			
		||||
                                + pretty_money(source.quantity * dest.quantity * amount) + " de la note " + source.name
 | 
			
		||||
                                + " vers la note " + dest.name + " a échoué : " + errObj["non_field_errors"], "danger");
 | 
			
		||||
                            LOCK = false;
 | 
			
		||||
                            return;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        $.post("/api/note/transaction/transaction/",
 | 
			
		||||
                        {
 | 
			
		||||
                            "csrfmiddlewaretoken": CSRF_TOKEN,
 | 
			
		||||
                            "quantity": source.quantity * dest.quantity,
 | 
			
		||||
                            "amount": amount,
 | 
			
		||||
                            "reason": reason,
 | 
			
		||||
                            "valid": false,
 | 
			
		||||
                            "invalidity_reason": "Solde insuffisant",
 | 
			
		||||
                            "polymorphic_ctype": TRANSFER_POLYMORPHIC_CTYPE,
 | 
			
		||||
                            "resourcetype": "Transaction",
 | 
			
		||||
                            "source": source.note.id,
 | 
			
		||||
                            "source_alias": source.name,
 | 
			
		||||
                            "destination": dest.note.id,
 | 
			
		||||
                            "destination_alias": dest.name
 | 
			
		||||
                        }).done(function () {
 | 
			
		||||
                            addMsg("Le transfert de "
 | 
			
		||||
                                + pretty_money(source.quantity * dest.quantity * amount) + " de la note " + source.name
 | 
			
		||||
                                + " vers la note " + dest.name + " a échoué : Solde insuffisant", "danger", 10000);
 | 
			
		||||
                            reset();
 | 
			
		||||
                        }).fail(function (err) {
 | 
			
		||||
                            let errObj = JSON.parse(err.responseText);
 | 
			
		||||
                            let error = errObj["detail"] ? errObj["detail"] : errObj["non_field_errors"]
 | 
			
		||||
                            if (!error)
 | 
			
		||||
                                error = err.responseText;
 | 
			
		||||
                            addMsg("Le transfert de "
 | 
			
		||||
                                + pretty_money(source.quantity * dest.quantity * amount) + " de la note " + source.name
 | 
			
		||||
                                + " vers la note " + dest.name + " a échoué : " + error, "danger");
 | 
			
		||||
                            LOCK = false;
 | 
			
		||||
                    });
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    } else if ($("#type_credit").is(':checked') || $("#type_debit").is(':checked')) {
 | 
			
		||||
        let special_note;
 | 
			
		||||
        let user_note;
 | 
			
		||||
        let alias;
 | 
			
		||||
        let given_reason = reason;
 | 
			
		||||
        let source_id, dest_id;
 | 
			
		||||
        if ($("#type_credit").is(':checked')) {
 | 
			
		||||
            special_note = $("#credit_type").val();
 | 
			
		||||
            user_note = dests_notes_display[0].note;
 | 
			
		||||
            alias = dests_notes_display[0].name;
 | 
			
		||||
            source_id = special_note;
 | 
			
		||||
            dest_id = user_note.id;
 | 
			
		||||
            reason = "Crédit " + $("#credit_type option:selected").text().toLowerCase();
 | 
			
		||||
            if (given_reason.length > 0)
 | 
			
		||||
                reason += " (" + given_reason + ")";
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            special_note = $("#debit_type").val();
 | 
			
		||||
            user_note = sources_notes_display[0].note;
 | 
			
		||||
            alias = sources_notes_display[0].name;
 | 
			
		||||
            source_id = user_note.id;
 | 
			
		||||
            dest_id = special_note;
 | 
			
		||||
            reason = "Retrait " + $("#credit_type option:selected").text().toLowerCase();
 | 
			
		||||
            if (given_reason.length > 0)
 | 
			
		||||
                reason += " (" + given_reason + ")";
 | 
			
		||||
        }
 | 
			
		||||
        $.post("/api/note/transaction/transaction/",
 | 
			
		||||
          $.post('/api/note/transaction/transaction/',
 | 
			
		||||
            {
 | 
			
		||||
                "csrfmiddlewaretoken": CSRF_TOKEN,
 | 
			
		||||
                "quantity": 1,
 | 
			
		||||
                "amount": amount,
 | 
			
		||||
                "reason": reason,
 | 
			
		||||
                "valid": true,
 | 
			
		||||
                "polymorphic_ctype": SPECIAL_TRANSFER_POLYMORPHIC_CTYPE,
 | 
			
		||||
                "resourcetype": "SpecialTransaction",
 | 
			
		||||
                "source": source_id,
 | 
			
		||||
                "source_alias": sources_notes_display.length ? alias : null,
 | 
			
		||||
                "destination": dest_id,
 | 
			
		||||
                "destination_alias": dests_notes_display.length ? alias : null,
 | 
			
		||||
                "last_name": $("#last_name").val(),
 | 
			
		||||
                "first_name": $("#first_name").val(),
 | 
			
		||||
                "bank": $("#bank").val()
 | 
			
		||||
              csrfmiddlewaretoken: CSRF_TOKEN,
 | 
			
		||||
              quantity: source.quantity * dest.quantity,
 | 
			
		||||
              amount: amount,
 | 
			
		||||
              reason: reason,
 | 
			
		||||
              valid: false,
 | 
			
		||||
              invalidity_reason: 'Solde insuffisant',
 | 
			
		||||
              polymorphic_ctype: TRANSFER_POLYMORPHIC_CTYPE,
 | 
			
		||||
              resourcetype: 'Transaction',
 | 
			
		||||
              source: source.note.id,
 | 
			
		||||
              source_alias: source.name,
 | 
			
		||||
              destination: dest.note.id,
 | 
			
		||||
              destination_alias: dest.name
 | 
			
		||||
            }).done(function () {
 | 
			
		||||
                addMsg("Le crédit/retrait a bien été effectué !", "success", 10000);
 | 
			
		||||
                if (user_note.membership && user_note.membership.date_end < new Date().toISOString())
 | 
			
		||||
                    addMsg("Attention : la note " + alias +  " n'est plus adhérente.", "danger", 10000);
 | 
			
		||||
                reset();
 | 
			
		||||
            }).fail(function (err) {
 | 
			
		||||
                let errObj = JSON.parse(err.responseText);
 | 
			
		||||
                let error = errObj["detail"] ? errObj["detail"] : errObj["non_field_errors"]
 | 
			
		||||
                if (!error)
 | 
			
		||||
                    error = err.responseText;
 | 
			
		||||
                addMsg("Le crédit/retrait a échoué : " + error, "danger", 10000);
 | 
			
		||||
                LOCK = false;
 | 
			
		||||
        });
 | 
			
		||||
            addMsg('Le transfert de ' +
 | 
			
		||||
                                pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
 | 
			
		||||
                                ' vers la note ' + dest.name + ' a échoué : Solde insuffisant', 'danger', 10000)
 | 
			
		||||
            reset()
 | 
			
		||||
          }).fail(function (err) {
 | 
			
		||||
            const errObj = JSON.parse(err.responseText)
 | 
			
		||||
            let error = errObj.detail ? errObj.detail : errObj.non_field_errors
 | 
			
		||||
            if (!error) { error = err.responseText }
 | 
			
		||||
            addMsg('Le transfert de ' +
 | 
			
		||||
                                pretty_money(source.quantity * dest.quantity * amount) + ' de la note ' + source.name +
 | 
			
		||||
                                ' vers la note ' + dest.name + ' a échoué : ' + error, 'danger')
 | 
			
		||||
            LOCK = false
 | 
			
		||||
          })
 | 
			
		||||
        })
 | 
			
		||||
      })
 | 
			
		||||
    })
 | 
			
		||||
  } else if ($('#type_credit').is(':checked') || $('#type_debit').is(':checked')) {
 | 
			
		||||
    let special_note
 | 
			
		||||
    let user_note
 | 
			
		||||
    let alias
 | 
			
		||||
    const given_reason = reason
 | 
			
		||||
    let source_id, dest_id
 | 
			
		||||
    if ($('#type_credit').is(':checked')) {
 | 
			
		||||
      special_note = $('#credit_type').val()
 | 
			
		||||
      user_note = dests_notes_display[0].note
 | 
			
		||||
      alias = dests_notes_display[0].name
 | 
			
		||||
      source_id = special_note
 | 
			
		||||
      dest_id = user_note.id
 | 
			
		||||
      reason = 'Crédit ' + $('#credit_type option:selected').text().toLowerCase()
 | 
			
		||||
      if (given_reason.length > 0) { reason += ' (' + given_reason + ')' }
 | 
			
		||||
    } else {
 | 
			
		||||
      special_note = $('#debit_type').val()
 | 
			
		||||
      user_note = sources_notes_display[0].note
 | 
			
		||||
      alias = sources_notes_display[0].name
 | 
			
		||||
      source_id = user_note.id
 | 
			
		||||
      dest_id = special_note
 | 
			
		||||
      reason = 'Retrait ' + $('#credit_type option:selected').text().toLowerCase()
 | 
			
		||||
      if (given_reason.length > 0) { reason += ' (' + given_reason + ')' }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
    $.post('/api/note/transaction/transaction/',
 | 
			
		||||
      {
 | 
			
		||||
        csrfmiddlewaretoken: CSRF_TOKEN,
 | 
			
		||||
        quantity: 1,
 | 
			
		||||
        amount: amount,
 | 
			
		||||
        reason: reason,
 | 
			
		||||
        valid: true,
 | 
			
		||||
        polymorphic_ctype: SPECIAL_TRANSFER_POLYMORPHIC_CTYPE,
 | 
			
		||||
        resourcetype: 'SpecialTransaction',
 | 
			
		||||
        source: source_id,
 | 
			
		||||
        source_alias: sources_notes_display.length ? alias : null,
 | 
			
		||||
        destination: dest_id,
 | 
			
		||||
        destination_alias: dests_notes_display.length ? alias : null,
 | 
			
		||||
        last_name: $('#last_name').val(),
 | 
			
		||||
        first_name: $('#first_name').val(),
 | 
			
		||||
        bank: $('#bank').val()
 | 
			
		||||
      }).done(function () {
 | 
			
		||||
      addMsg('Le crédit/retrait a bien été effectué !', 'success', 10000)
 | 
			
		||||
      if (user_note.membership && user_note.membership.date_end < new Date().toISOString()) { addMsg('Attention : la note ' + alias + " n'est plus adhérente.", 'danger', 10000) }
 | 
			
		||||
      reset()
 | 
			
		||||
    }).fail(function (err) {
 | 
			
		||||
      const errObj = JSON.parse(err.responseText)
 | 
			
		||||
      let error = errObj.detail ? errObj.detail : errObj.non_field_errors
 | 
			
		||||
      if (!error) { error = err.responseText }
 | 
			
		||||
      addMsg('Le crédit/retrait a échoué : ' + error, 'danger', 10000)
 | 
			
		||||
      LOCK = false
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user