Commit 1e561340 by François C.

Merge branch '2844-bdm-admin-shifts-template' into 'dev_cooperatic'

2844 bdm admin shifts template

See merge request !154
parents 01c46ba9 1520f01b
Pipeline #2106 passed with stage
in 1 minute 28 seconds
......@@ -115,6 +115,7 @@ ENTRANCE_EASY_SHIFT_VALIDATE_MSG = """Si vous faites un service dans un comité,
valider votre présence en cherchant<br/>
votre nom ou numéro ci-dessous
"""
# Members space / shifts
UNSUBSCRIBED_FORM_LINK = 'https://docs.google.com/forms/d/e/1FAIpQLScWcpls-ruYIp7HdrjRF1B1TyuzdqhvlUIcUWynbEujfj3dTg/viewform'
UNSUBSCRIBED_MSG = 'Vous êtes désincrit·e, merci de remplir <a href="https://docs.google.com/forms/d/e/1FAIpQLSfPiC2PkSem9x_B5M7LKpoFNLDIz0k0V5I2W3Mra9AnqnQunw/viewform">ce formulaire</a> pour vous réinscrire sur un créneau.<br />Vous pouvez également contacter le Bureau des Membres en remplissant <a href="https://docs.google.com/forms/d/e/1FAIpQLSeZP0m5-EXPVJxEKJk6EjwSyZJtnbiGdYDuAeFI3ENsHAOikg/viewform">ce formulaire</a>'
......
......@@ -5,6 +5,7 @@ from outils.common import OdooAPI
from members.models import CagetteUser
from members.models import CagetteMembers
from members.models import CagetteMember
from members.models import CagetteServices
from shifts.models import CagetteShift
from outils.common import MConfig
from datetime import datetime
......@@ -337,8 +338,19 @@ def manage_attached(request):
def manage_regular_shifts(request):
""" Administration des créneaux des membres """
template = loader.get_template('members/admin/manage_regular_shifts.html')
context = {'title': 'BDM - Créneaux',
'module': 'Membres'}
committees_shift_id = CagetteServices.get_committees_shift_id()
context = {
'title': 'BDM - Créneaux',
'module': 'Membres',
'couchdb_server': settings.COUCHDB['url'],
'db': settings.COUCHDB['dbs']['member'],
'max_begin_hour': settings.MAX_BEGIN_HOUR,
'mag_place_string': settings.MAG_NAME,
'open_on_sunday': getattr(settings, 'OPEN_ON_SUNDAY', False),
'show_ftop_button': getattr(settings, 'SHOW_FTOP_BUTTON', True),
'has_committe_shift': committees_shift_id is not None,
'ASSOCIATE_MEMBER_SHIFT' : getattr(settings, 'ASSOCIATE_MEMBER_SHIFT', '')
}
return HttpResponse(template.render(context, request))
def get_makeups_members(request):
......@@ -399,6 +411,9 @@ def update_members_makeups(request):
response = JsonResponse(res, status=403)
return response
# --- Gestion des créneaux
def delete_shift_registration(request):
""" From BDM admin, delete (cancel) a member shift registration """
res = {}
......@@ -469,7 +484,47 @@ def delete_shift_template_registration(request):
response = JsonResponse(res, status=403)
return response
# Gestion des binômes
def shift_subscription(request):
""" Inscrit un membre désinscrit à un shift template """
res = {}
if CagetteUser.are_credentials_ok(request):
data = json.loads(request.body.decode())
partner_id = int(data["partner_id"])
shift_type = data["shift_type"]
if shift_type == 1:
# 1 = standard
shift_template_id = data["shift_template_id"]
else:
# 2 = ftop
# First try to get committees shift
shift_template_id = CagetteServices.get_committees_shift_id()
# If None, no committees shift, get the first ftop shift
if shift_template_id is None:
shift_template_id = CagetteServices.get_first_ftop_shift_id()
m = CagetteMember(partner_id)
m.create_coop_shift_subscription(shift_template_id, shift_type)
# Retrurn necessary data
api = OdooAPI()
c = [['id', '=', shift_template_id]]
f = ['id', 'name']
res["shift_template"] = api.search_read('shift.template', c, f)[0]
c = [['id', '=', partner_id]]
f = ['cooperative_state']
res["cooperative_state"] = api.search_read('res.partner', c, f)[0]['cooperative_state']
response = JsonResponse(res)
else:
response = JsonResponse({"message": "Unauthorized"}, status=403)
return response
# --- Gestion des binômes
def get_member_info(request, id):
"""Retrieve information about a member."""
......
......@@ -820,6 +820,11 @@ class CagetteMember(models.Model):
members.append(m)
return CagetteMember.add_next_shifts_to_members(members)
elif search_type == "makeups_data":
fields = CagetteMember.m_short_default_fields
fields = fields + ['shift_type', 'makeups_to_do', 'display_ftop_points', 'display_std_points', 'shift_type']
return api.search_read('res.partner', cond, fields)
elif search_type == "shift_template_data":
fields = CagetteMember.m_short_default_fields
fields = fields + ['id', 'makeups_to_do', 'cooperative_state']
......@@ -1533,6 +1538,29 @@ class CagetteServices(models.Model):
return shift_id
@staticmethod
def get_first_ftop_shift_id():
shift_id = None
try:
api = OdooAPI()
res = api.search_read('shift.template',
[['shift_type_id','=', 2]],
['id', 'registration_qty'])
# Get the ftop shift template with the max registrations: most likely the one in use
ftop_shift = {'id': None, 'registration_qty': 0}
for shift_reg in res:
if shift_reg["registration_qty"] > ftop_shift["registration_qty"]:
ftop_shift = shift_reg
try:
shift_id = int(ftop_shift['id'])
except:
pass
except:
pass
return shift_id
@staticmethod
def easy_validate_shift_presence(coop_id):
"""Add a presence point if the request is valid."""
res = {}
......
......@@ -75,7 +75,8 @@
}
/* Actions */
#remove_shift_template_button {
#remove_shift_template_button,
#subscribe_to_shift_template_button {
display: none;
margin: 15px;
}
......@@ -85,4 +86,26 @@
}
#permanent_unsuscribe {
margin-right: 5px;
}
\ No newline at end of file
}
/* Calendar */
#shifts_calendar_area {
display: none;
margin-top: 15px;
}
.shift[data-place="Cleme"], [data-select="Cleme"] {background: #c8deff;}
#subs_cap {width: 200px;}
.lat_menu button {margin-bottom:5px;}
.oddeven_selector {margin-right:25px;}
.main_content .shift {float:left;}
.main_content .shift.full {display:none;}
.shift {margin:2px; padding:2px; cursor: cell;}
.shift.alert,span.alert {border-bottom: 3px #e52121 solid;}
.highlighted {box-shadow: 10px 10px 5px grey;}
.lat_menu.highlighted {border: 2px #fffc07 dotted;}
#coop_list_view td.coop:hover,
#coop_list_view td.c_shift:hover {background:#fffc07; cursor:pointer;}
.b_red {color:#ffffff;}
.shift[data-type="compact"] {border: 1px solid #000000;border-radius: 5px; padding:5px; cursor: pointer;}
.shift_template, .next_shift {font-weight: bold;}
\ No newline at end of file
......@@ -26,13 +26,12 @@ function load_member_infos(divId, memberId) {
traditional: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
console.log(data.member)
if (divId === 'parentInfo') {
parentId = data.member.id;
parentName = data.member.barcode_base + ' ' + data.member.name
parentName = data.member.barcode_base + ' ' + data.member.name;
} else if (divId === 'childInfo') {
childId = data.member.id;
childName = data.member.barcode_base + ' ' + data.member.name
childName = data.member.barcode_base + ' ' + data.member.name;
}
display_member_infos(divId, data.member);
},
......@@ -72,7 +71,7 @@ function display_member_infos(divId, memberData) {
if (parentId != null && childId != null) {
$("#createPair").prop("disabled", false);
$("#createPair").addClass("btn--primary")
$("#createPair").addClass("btn--primary");
}
}
......@@ -199,10 +198,13 @@ function display_attached_members() {
function delete_pair(childId, gone_checked) {
var payload = {"child": {"id": childId}, "gone": []};
if (gone_checked.length > 0) {
$.each(gone_checked, function(i,e) {
const elts = $(e).attr('name').split("_")
payload['gone'].push(elts[0])
$.each(gone_checked, function(i, e) {
const elts = $(e).attr('name')
.split("_");
payload['gone'].push(elts[0]);
});
}
......@@ -231,49 +233,52 @@ function delete_pair(childId, gone_checked) {
function confirmDeletion(childId) {
var modalContent = $('#confirmModal')
modalContent.find("#parentName").text(parentName)
modalContent.find("#childName").text(childName)
modalContent = modalContent.html();
openModal(modalContent, () => {
if (is_time_to('delete_pair')) {
const gone_checked = $('input.after_unattached_state:checked');
closeModal();
openModal();
delete_pair(childId, gone_checked)
}
}, 'Valider', false);
var modalContent = $('#confirmModal');
modalContent.find("#parentName").text(parentName);
modalContent.find("#childName").text(childName);
modalContent = modalContent.html();
openModal(modalContent, () => {
if (is_time_to('delete_pair')) {
const gone_checked = $('input.after_unattached_state:checked');
closeModal();
openModal();
delete_pair(childId, gone_checked);
}
}, 'Valider', false);
}
function create_pair(payload) {
$.ajax({
type: 'POST',
url: "/members/admin/manage_attached/create_pair",
dataType:"json",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(payload),
success: function(data) {
enqueue_message_for_next_loading("Binôme créé.");
location.reload()
},
error: function(data) {
err = {msg: "erreur serveur", ctx: 'create pair'};
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.errors != 'undefined') {
err.msg += ' : ' + data.responseJSON.errors;
}
report_JS_error(err, 'members.admin');
closeModal();
var message = 'Erreur lors de création du binôme.';
data.responseJSON.errors.map(function(error) {
message += ('\n' + error);
});
alert(message);
}
});
$.ajax({
type: 'POST',
url: "/members/admin/manage_attached/create_pair",
dataType:"json",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(payload),
success: function() {
enqueue_message_for_next_loading("Binôme créé.");
location.reload();
},
error: function(data) {
err = {msg: "erreur serveur", ctx: 'create pair'};
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.errors != 'undefined') {
err.msg += ' : ' + data.responseJSON.errors;
}
report_JS_error(err, 'members.admin');
closeModal();
var message = 'Erreur lors de création du binôme.';
data.responseJSON.errors.map(function(error) {
message += ('\n' + error);
return null;
});
alert(message);
}
});
}
......@@ -326,10 +331,10 @@ $(document).ready(function() {
});
},
minLength: 1,
search: function(event, ui) {
search: function() {
$('#spinner1').show();
},
response: function(event, ui) {
response: function() {
$('#spinner1').hide();
},
select: function(event, ui) {
......@@ -340,6 +345,8 @@ $(document).ready(function() {
return false;
}
return null;
}
});
......@@ -376,10 +383,10 @@ $(document).ready(function() {
});
},
minLength: 1,
search: function(event, ui) {
search: function() {
$('#spinner2').show();
},
response: function(event, ui) {
response: function() {
$('#spinner2').hide();
},
select: function(event, ui) {
......@@ -389,29 +396,32 @@ $(document).ready(function() {
return false;
}
return null;
}
});
$("#createPair").on('click', function() {
if (parentId && childId) { // Note : after reload, button "Créer le binôme" is clickable...It shouldn't
var payload = {
"parent": {"id": parentId},
"child": {"id": childId}
"parent": {"id": parentId},
"child": {"id": childId}
};
var modalContent = $('#confirmModal')
modalContent.find("#parentName").text(parentName)
modalContent.find("#childName").text(childName)
var modalContent = $('#confirmModal');
modalContent.find("#parentName").text(parentName);
modalContent.find("#childName").text(childName);
modalContent = modalContent.html();
openModal(modalContent, () => {
if (is_time_to('create_pair')) {
closeModal();
openModal(); // Show gears
create_pair(payload)
}
if (is_time_to('create_pair')) {
closeModal();
openModal(); // Show gears
create_pair(payload);
}
}, 'Valider', false);
}
});
if ($("#attached_members_table") != "undefined") {
......@@ -420,6 +430,7 @@ $(document).ready(function() {
$(document).on('click', '.delete_pair', function (event) {
var childId = event.target.id.split('_').slice(-1)[0];
$.ajax({
type: 'GET',
url: "/members/get_member_info/" + childId,
......@@ -427,8 +438,8 @@ $(document).ready(function() {
traditional: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
parentName = data.member.parent_barcode_base + ' - ' + data.member.parent_name
childName = data.member.barcode_base + ' - ' + data.member.name
parentName = data.member.parent_barcode_base + ' - ' + data.member.parent_name;
childName = data.member.barcode_base + ' - ' + data.member.name;
confirmDeletion(childId);
},
error: function(data) {
......
......@@ -75,10 +75,12 @@ function display_makeups_members() {
width: "10%",
render: function (data, type, row) {
if (data == 'ftop') {
return row.display_ftop_points
return row.display_ftop_points;
} else if (data == 'standard') {
return row.display_std_points
return row.display_std_points;
}
return null;
}
},
{
......@@ -247,11 +249,13 @@ function update_members_makeups(member_ids, action) {
makeups_members[member_index].display_ftop_points += 1;
}
}
//console.log(makeups_members[member_index])
data.push({
member_id: mid,
target_makeups_nb: makeups_members[member_index].makeups_to_do,
member_shift_type: makeups_members[member_index].shift_type
member_shift_type: makeups_members[member_index].shift_type,
display_ftop_points: makeups_members[member_index].display_ftop_points,
display_std_points: makeups_members[member_index].display_std_points
});
}
......@@ -322,7 +326,9 @@ function display_possible_members() {
id: member.id,
name: member.name,
makeups_to_do: 0,
shift_type: member.shift_type
shift_type: member.shift_type,
display_std_points: member.display_std_points,
display_ftop_points: member.display_ftop_points
});
openModal(
......@@ -373,16 +379,11 @@ $(document).ready(function() {
let search_str = $('#search_member_input').val();
$.ajax({
url: '/members/search/' + search_str,
url: '/members/search/' + search_str +'?search_type=makeups_data',
dataType : 'json',
success: function(data) {
members_search_results = [];
for (member of data.res) {
if (member.is_member || member.is_associated_people) {
members_search_results.push(member);
}
}
members_search_results = data.res;
display_possible_members();
},
......
......@@ -21,7 +21,7 @@ function remove_from_shift_template() {
partner_id: selected_member.id,
shift_template_id: selected_member.shift_template_id[0],
permanent_unsuscribe: permanent_unsuscribe,
makeups_to_do: selected_member.makeups_to_do,
makeups_to_do: selected_member.makeups_to_do
};
$.ajax({
......@@ -54,25 +54,124 @@ function remove_from_shift_template() {
}
/**
* Send the request to register a member to a shift template
* @param {int} shift_type 1 === standard ; 2 === ftop
* @param {int} shift_template_id null for ftop shift type
*/
function shift_subscrition(shift_type, shift_template_id = null) {
openModal();
let data = {
partner_id: selected_member.id,
shift_type: shift_type,
shift_template_id: shift_template_id
};
$.ajax({
type: 'POST',
url: '/members/shift_subscription',
data: JSON.stringify(data),
dataType:"json",
traditional: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
stdata = data.shift_template;
selected_member.shift_template_id = [
stdata.id,
stdata.name
];
selected_member.cooperative_state = data.cooperative_state;
display_member_info();
$("#shifts_calendar_area").hide();
closeModal();
},
error: function() {
err = {
msg: "erreur serveur lors de l'inscription du membre au créneau",
ctx: 'members.admin.manage_regular_shifts.shift_subscrition'
};
report_JS_error(err, 'members.admin');
closeModal();
$.notify("Une erreur est survenue lors de l'inscription du membre au créneau.", {
globalPosition:"top right",
className: "error"
});
}
});
}
/**
* When a member is selected, display the selected member relevant info
*/
function display_member_info() {
$('.member_name').text(`${selected_member.barcode_base} - ${selected_member.name}`);
$('.member_status').text(possible_cooperative_state[selected_member.cooperative_state]);
$('.member_makeups').text(selected_member.makeups_to_do);
$('.search_member_results_area').hide();
$("#remove_shift_template_button").hide();
$("#remove_shift_template_button").off();
$("#subscribe_to_shift_template_button").hide();
$("#subscribe_to_shift_template_button").off();
$("#shifts_calendar_area").hide();
if (selected_member.shift_template_id === undefined || selected_member.shift_template_id === null) {
$('.member_shift').text("");
$('.member_shift').text("X");
$("#subscribe_to_shift_template_button").show();
$("#subscribe_to_shift_template_button").on("click", () => {
retrieve_and_draw_shift_tempates();
$("#shifts_calendar_area").show();
$("#remove_shift_template_button").hide();
$("#remove_shift_template_button").off();
// Wait for listeners to be set in common.js
// TODO use "signals" to avoid waiting an arbitrary time
setTimeout(() => {
// Cancel listeners from subscription page & set custom listeners
$("#shifts_calendar_area button[data-select='Volant']").off("click");
$("#shifts_calendar_area button[data-select='Volant']").on("click", function() {
// Subscribe to comitee/ftop shift
msg = (has_committe_shift === "True")
? `Inscrire ${selected_member.name} au service des Comités ?`
: `Inscrire ${selected_member.name} en Volant ?`;
openModal(
msg,
() => {
shift_subscrition(2);
},
"Confirmer",
false
);
});
$(".shift").off("click");
$(".shift").on("click", function() {
// Subscribe to shift template
let shift_template_id = select_shift_among_compact(this, false); // method from common.js
let shift_template_data = shift_templates[shift_template_id].data;// shift_templates: var from common.js
let shift_template_name = get_shift_name(shift_template_data);
openModal(
`Inscrire ${selected_member.name} au créneau ${shift_template_name} ?`,
() => {
shift_subscrition(1, parseInt(shift_template_id));
},
"Confirmer",
false
);
});
}, 500);
});
} else {
$('.member_shift').text(selected_member.shift_template_id[1]);
$("#remove_shift_template_button").show();
$("#remove_shift_template_button").off();
$("#remove_shift_template_button").on("click", () => {
let modal_template = $("#modal_remove_shift_template");
modal_template.find(".shift_template_name").text(selected_member.shift_template_id[1]);
openModal(
......@@ -91,7 +190,7 @@ function display_member_info() {
/**
* Display the members from the search result
*/
function display_possible_members() {
function display_possible_members() {
$('.search_member_results_area').show();
$('.search_member_results').empty();
$('.btn_possible_member').off();
......@@ -140,12 +239,18 @@ function display_member_info() {
$(document).ready(function() {
if (coop_is_connected()) {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
dbc = new PouchDB(couchdb_dbname);
$(".page_content").show();
if (has_committe_shift === "True") {
$("#shifts_calendar_area button[data-select='Volant']").text("Comités");
}
// Set action to search for the member
$('#search_member_form').submit(function() {
let search_str = $('#search_member_input').val();
$.ajax({
url: `/members/search/${search_str}?search_type=shift_template_data`,
dataType : 'json',
......@@ -165,7 +270,7 @@ $(document).ready(function() {
ctx: 'members.admin.manage_regular_shifts.search_members'
};
report_JS_error(err, 'members.admin');
$.notify("Erreur lors de la recherche de membre, il faut ré-essayer plus tard...", {
globalPosition:"top right",
className: "error"
......@@ -179,6 +284,7 @@ $(document).ready(function() {
$('#back_to_admin_index').on('click', function() {
let base_location = window.location.href.split("manage_regular_shifts")[0].slice(0, -1);
window.location.assign(base_location);
});
});
......@@ -94,7 +94,7 @@ function display_member_shifts() {
$(row).addClass("makeup_row");
$(row).prop('title', 'Ce service est un rattrapage');
}
},
}
});
$('#member_shifts_table').on('click', 'tbody td .delete_shift_registration', function () {
......@@ -103,9 +103,10 @@ function display_member_shifts() {
const shift_is_makeup = row_data.is_makeup;
let msg = `<p>Enlever la présence de <b>${member.name}</b> au service du <b>${row_data.shift_id[1]}</b> ?</p>`;
if (shift_is_makeup === true) {
msg += `<p><i class="fas fa-exclamation-triangle"></i> Ce service est un rattrapage. Le supprimer ajoutera un point au compteur de ce.tte membre.</p>`;
}
}
openModal(
msg,
......
......@@ -63,6 +63,7 @@ urlpatterns = [
url(r'^update_members_makeups$', admin.update_members_makeups),
url(r'^delete_shift_registration$', admin.delete_shift_registration),
url(r'^delete_shift_template_registration$', admin.delete_shift_template_registration),
url(r'^shift_subscription$', admin.shift_subscription),
url(r'^admin/manage_attached$', admin.manage_attached),
url(r'^admin/manage_attached/create_pair$', admin.create_pair),
url(r'^admin/manage_attached/delete_pair$', admin.delete_pair),
......
......@@ -99,8 +99,8 @@ function single_shift_click() {
}
}
function select_shift_among_compact() {
var clicked = $(this);
function select_shift_among_compact(clicked_item = null, subscribe = true) {
var clicked = clicked_item === null ? $(this) : $(clicked_item);
var day = clicked.closest('td').attr('class');
var hour = clicked.closest('tr').data('begin');
var selected = null;
......@@ -129,9 +129,11 @@ function select_shift_among_compact() {
}
}
});
//console.log(worst_score)
if (selected)
if (selected && subscribe === true)
subscribe_shift(selected);
return selected
}
......
......@@ -55,10 +55,18 @@
<button class="btn--primary" id="remove_shift_template_button">
Désinscrire du créneau
</button>
<button class="btn--primary" id="subscribe_to_shift_template_button">
Réinscrire à un créneau
</button>
</div>
</div>
</div>
</div>
<div id="shifts_calendar_area">
{% include "members/shift_template_choice.html" %}
</div>
<div id="templates" style="display:none;">
<div id="modal_remove_shift_template">
<p>Voulez vraiment désinscrire ce membre du créneau <span class="shift_template_name"></span> ?</p>
......@@ -70,7 +78,16 @@
</div>
</div>
<script src="{% static "js/pouchdb.min.js" %}"></script>
<script type="text/javascript">
var type = 2;
var has_committe_shift = '{{has_committe_shift}}'
var max_begin_hour = '{{max_begin_hour}}'
var couchdb_dbname = '{{db}}';
var couchdb_server = '{{couchdb_server}}' + couchdb_dbname;
var ASSOCIATE_MEMBER_SHIFT = '{{ASSOCIATE_MEMBER_SHIFT}}';
</script>
<script src='{% static "js/common.js" %}?v='></script>
<script src='{% static "js/all_common.js" %}?v='></script>
<script src='{% static "js/admin/manage_regular_shifts.js" %}?v='></script>
{% endblock %}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment