Commit a2101668 by Thibault Grandjean

Merge branch 'dev_cooperatic' of…

Merge branch 'dev_cooperatic' of https://gl.cooperatic.fr/cooperatic-foodcoops/third-party into 2170-create-delete-binome
parents 6182eb4d c71e8d67
......@@ -59,7 +59,7 @@ class CagetteEnvelops(models.Model):
# Get first invoice for which amount being paid <= amount left to pay in invoice
for invoice_item in invoice_res:
if int(float(data['amount']) * 100) <= int(float(invoice_item['residual_signed']) * 100):
invoice = invoice_ite
invoice = invoice_item
if invoice is None:
res['error'] = 'The amount is too high for the invoices found for this partner.'
......
......@@ -334,6 +334,13 @@ def manage_attached(request):
'module': 'Membres'}
return HttpResponse(template.render(context, 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'}
return HttpResponse(template.render(context, request))
def get_makeups_members(request):
""" Récupération des membres qui doivent faire des rattrapages """
res = CagetteMembers.get_makeups_members()
......@@ -398,14 +405,63 @@ def delete_shift_registration(request):
is_connected_user = CagetteUser.are_credentials_ok(request)
if is_connected_user is True:
data = json.loads(request.body.decode())
shift_registration_id = int(data["shift_registration_id"])
member_id = int(data["member_id"])
m = CagetteShift()
res["res"] = m.cancel_shift([shift_registration_id])
shift_registration_id = int(data["shift_registration_id"])
shift_is_makeup = data["shift_is_makeup"]
# Note: 'upcoming_registration_count' in res.partner won't change because the _compute method
# in odoo counts canceled shift registrations.
m = CagetteShift()
res["cancel_shift"] = m.cancel_shift([shift_registration_id])
if shift_is_makeup is True:
fields = {
'name': "Admin BDM - Suppression d'un rattrapage",
'shift_id': False,
'type': data["member_shift_type"],
'partner_id': member_id,
'point_qty': 1
}
res["update_counter"] = m.update_counter_event(fields)
response = JsonResponse(res, safe=False)
else:
res["message"] = "Unauthorized"
response = JsonResponse(res, status=403)
return response
def delete_shift_template_registration(request):
""" From BDM admin, delete a member shift template registration """
res = {}
is_connected_user = CagetteUser.are_credentials_ok(request)
if is_connected_user is True:
try:
data = json.loads(request.body.decode())
partner_id = int(data["partner_id"])
shift_template_id = int(data["shift_template_id"])
makeups_to_do = int(data["makeups_to_do"])
permanent_unsuscribe = data["permanent_unsuscribe"]
cm = CagetteMember(partner_id)
# Get partner nb of future makeup shifts
partner_makeups = cm.get_member_selected_makeups()
target_makeup = makeups_to_do + len(partner_makeups)
if target_makeup > 2:
target_makeup = 2
# Update partner makeups to do
res["update_makeups"] = cm.update_member_makeups({'target_makeups_nb': target_makeup})
# Delete all shift registrations & shift template registration
res["unsuscribe_member"] = cm.unsuscribe_member()
if permanent_unsuscribe is True:
res["set_done"] = cm.set_cooperative_state("gone")
except Exception as e:
res["error"] = str(e)
response = JsonResponse(res, safe=False)
else:
......
.header {
margin: 1rem 0;
}
.login_area {
position: absolute;
display: block;
top: 5px;
right: 5px;
}
#back_to_admin_index {
position: absolute;
top: 5px;
left: 5px;
}
/* Search members area */
#search_member_area {
margin-top: 30px;
display: flex;
flex-direction: column;
align-items: center;
}
#search_member_form_area {
display:flex;
align-items: center;
}
#search_member_form {
margin-left: 10px;
}
.search_member_results_area {
margin-top: 15px;
display: flex;
align-items: center;
}
.search_results_text {
min-width: 150px;
}
.search_member_results {
display: flex;
flex-wrap: wrap;
}
.btn_possible_member {
margin: 0.5rem 1rem;
}
/* Member info area */
#partner_data_area {
width: 80%;
display: none;
flex-direction: column;
align-items: center;
border-radius: 30px;
margin: 30px auto;
padding: 15px;
box-shadow: 2px 2px 3px rgba(10,10,10,.1),0 0 0 1px rgba(10,10,10,.1);
}
.member_name_container {
margin: 1rem 0 2rem 0;
}
.member_name_icon {
color: #00a573;
margin-right: 5px;
}
.member_info {
font-weight: bold;
}
/* Actions */
#remove_shift_template_button {
display: none;
margin: 15px;
}
.checkbox_area {
margin: 10px 0;
}
#permanent_unsuscribe {
margin-right: 5px;
}
\ No newline at end of file
......@@ -38,7 +38,11 @@
padding-top: 0.755em;
}
/* Search membres area */
/* Search members area */
.makeup_row {
background-color: #ffc854 !important;
}
#search_member_area {
margin-top: 30px;
display: flex;
......
......@@ -23,6 +23,57 @@
[data-week="4"] {border: 2px #eed000 solid;}
#new_coop [name="email"] {width:25em;}
#new_coop{
max-width: 75%;
}
#mail_generation {position:absolute; bottom:30px;}
#sex {padding: 0;}
#existing_partner{
padding-right: 15px;
width: 45%;
}
#new_partner{
padding-left: 15px;
width: 45%;
}
#add_binome{
cursor: pointer;
text-decoration: underline;
font-weight: bold;
}
#existing_member_choice, #new_member_choice{
border: 1px solid black;
width: 40%;
cursor: pointer;
text-align: center;
max-width: 400px;
background-color: #e7e9ed;
color: black;
padding: 1rem 1.5rem;
}
#existing_member_choice:hover, #new_member_choice:hover{
background-color: #ccc;
}
.choice_active{
background-color: #0275d8 !important;
color: white !important;
}
#existing_member_choice{
margin-right: 15px;
}
.choice_button_area{
margin-bottom: 10px;
}
#associate_area{
margin-bottom: 15px;
}
\ No newline at end of file
......@@ -44,8 +44,10 @@ h1 .member_name {font-weight: bold;}
.members_list {list-style: none;}
.members_list li {display:block;margin-bottom:5px;}
.members_list li.btn--inverse {background: #449d44; cursor:not-allowed; color: #FFF; }
.members_list li.btn--inverse {background: #449d44; color: #FFF; }
.members_list li.btn--inverse.not_connected {cursor:not-allowed}
.members_list li.btn--inverse.late {background-color: #de9b00; color: white}
.members_list li.btn--inverse.both {background-color: #0275d8 ; color: white}
#service_entry_success {font-size: x-large;}
#service_entry_success .explanations {margin: 25px 0; font-size: 18px;}
......@@ -64,7 +66,7 @@ h1 .member_name {font-weight: bold;}
#service_en_cours .info a {line-height: 24px; font-size:14px; margin-top:15px;}
.outside_list a {margin-left:15px;}
#rattrapage_1 .advice {margin-top:15px;}
.btn.present {background:#50C878;}
.btn.present {background:#50C878; color: white !important; font-weight: bold;}
.btn.return {background: #ff3333; color:#FFF !important; margin-top:25px;}
.msg-big {font-size: xx-large; background: #fff; padding:25px; text-align: center;}
......
......@@ -4,7 +4,7 @@ $(document).ready(function() {
$(".page_content").show();
let location = window.location.href;
let location = window.location.href.replace(/\/$/, '');
$('.management_type_button').on('click', function() {
if (this.id == 'manage_makeups_button') {
......@@ -19,6 +19,8 @@ $(document).ready(function() {
window.location.assign(location + "/create_pair");
} else if (this.id == 'manage_leaves_button') {
console.log('coming soon...');
} else if (this.id == 'manage_regular_shifts_button') {
window.location.assign(location + "/manage_regular_shifts");
}
});
} else {
......
......@@ -69,6 +69,19 @@ function display_makeups_members() {
title: "Nom"
},
{
data: "shift_type",
title: "Nb de points",
className: "dt-body-center",
width: "10%",
render: function (data, type, row) {
if (data == 'ftop') {
return row.display_ftop_points
} else if (data == 'standard') {
return row.display_std_points
}
}
},
{
data: "makeups_to_do",
title: "Nb rattrapages",
className: "dt-body-center",
......@@ -358,7 +371,7 @@ $(document).ready(function() {
error: function() {
err = {
msg: "erreur serveur lors de la recherche de membres",
ctx: 'confirm_movement.search_members'
ctx: 'members.admin.manage_makeups.search_members'
};
report_JS_error(err, 'stock');
......
var selected_member = null,
possible_cooperative_state = {
suspended: "Rattrapage",
exempted: "Exempté.e",
alert: "En alerte",
up_to_date: "À jour",
unsubscribed: "Désinscrit.e des créneaux",
delay: "En délai",
gone: "Parti.e"
};
/**
* Send request to remove partner from shift template
*/
function remove_from_shift_template() {
let permanent_unsuscribe = modal.find("#permanent_unsuscribe").prop('checked');
openModal();
let data = {
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,
};
$.ajax({
type: 'POST',
url: '/members/delete_shift_template_registration',
data: JSON.stringify(data),
dataType:"json",
traditional: true,
contentType: "application/json; charset=utf-8",
success: function() {
selected_member.shift_template_id = null;
selected_member.cooperative_state = (permanent_unsuscribe === true) ? "gone" : "unsubscribed";
display_member_info();
closeModal();
},
error: function() {
err = {
msg: "erreur serveur lors de la suppression du membre du créneau",
ctx: 'members.admin.manage_regular_shifts.remove_from_shift_template'
};
report_JS_error(err, 'members.admin');
closeModal();
$.notify("Une erreur est survenue lors du processus de suppression du membre du 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);
if (selected_member.shift_template_id === undefined || selected_member.shift_template_id === null) {
$('.member_shift').text("");
$("#remove_shift_template_button").hide();
$("#remove_shift_template_button").off();
} 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(
modal_template.html(),
remove_from_shift_template,
"Valider",
false
);
});
}
$('#search_member_input').val();
$('#partner_data_area').css('display', 'flex');
}
/**
* Display the members from the search result
*/
function display_possible_members() {
$('.search_member_results_area').show();
$('.search_member_results').empty();
$('.btn_possible_member').off();
let no_result = true;
if (members_search_results.length > 0) {
for (member of members_search_results) {
$(".search_results_text").show();
no_result = false;
// Display results (possible members) as buttons
var member_button = '<button class="btn--success btn_possible_member" member_id="'
+ member.id + '">'
+ member.barcode_base + ' - ' + member.name
+ '</button>';
$('.search_member_results').append(member_button);
}
// Set action on member button click
$('.btn_possible_member').on('click', function() {
for (member of members_search_results) {
if (member.id == $(this).attr('member_id')) {
selected_member = member;
display_member_info();
$('.search_member_results').empty();
$('.search_member_results_area').hide();
$('#search_member_input').val('');
break;
}
}
});
}
if (no_result === true) {
$(".search_results_text").hide();
$('.search_member_results').html(`<p>
<i>Aucun résultat ! Vérifiez votre recherche...</i>
</p>`);
}
}
$(document).ready(function() {
if (coop_is_connected()) {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
$(".page_content").show();
// 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',
success: function(data) {
$('#partner_data_area').hide();
if (data.res.length === 1) {
selected_member = data.res[0];
display_member_info();
} else {
members_search_results = data.res;
display_possible_members();
}
},
error: function() {
err = {
msg: "erreur serveur lors de la recherche de membres",
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"
});
}
});
});
} else {
$(".page_content").hide();
}
$('#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);
});
});
......@@ -88,17 +88,29 @@ function display_member_shifts() {
"sLoadingRecords": "Chargement en cours...",
"sZeroRecords": "Aucun &eacute;l&eacute;ment &agrave; afficher",
"sEmptyTable": "Aucun futur service pour ce.tte membre"
}
},
createdRow: function(row, rdata) {
if (rdata.is_makeup === true) {
$(row).addClass("makeup_row");
$(row).prop('title', 'Ce service est un rattrapage');
}
},
});
$('#member_shifts_table').on('click', 'tbody td .delete_shift_registration', function () {
const row_data = member_shifts_table.row($(this).parents('tr')).data();
const shift_reg_id = row_data.id;
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(
`Enlever la présence de ${member.name} au service du ${row_data.shift_id[1]} ?`,
msg,
() => {
delete_shift_registration(shift_reg_id);
delete_shift_registration(shift_reg_id, shift_is_makeup);
},
"Confirmer",
false
......@@ -109,13 +121,16 @@ function display_member_shifts() {
/**
* Send request to delete shift registration
* @param {Int} shift_reg_id Id of the shift_registration to delete
* @param {Boolean} shift_is_makeup Is the shift a makeup?
*/
function delete_shift_registration(shift_reg_id) {
function delete_shift_registration(shift_reg_id, shift_is_makeup) {
openModal();
data = {
member_id: selected_member.id,
shift_registration_id: shift_reg_id
member_shift_type: selected_member.shift_type,
shift_registration_id: shift_reg_id,
shift_is_makeup: shift_is_makeup
};
$.ajax({
......
......@@ -31,12 +31,14 @@ var search_field = $('input[name="search_string"]');
var shift_title = $('#current_shift_title');
var shift_members = $('#current_shift_members');
var service_validation = $('#service_validation');
var associated_service_validation = $('#associated_service_validation');
var validation_last_call = 0;
var rattrapage_wanted = $('[data-next="rattrapage_1"]');
var webcam_is_attached = false;
var photo_advice = $('#photo_advice');
var photo_studio = $('#photo_studio');
var coop_info = $('.coop-info');
var service_data = null;
const missed_begin_msg = $('#missed_begin_msg').html();
......@@ -264,7 +266,7 @@ function get_simple_service_name(s) {
}
function move_service_validation_to(page) {
service_validation.find('.btn').data('stid', '0');
service_data.stid=0;
page.find('.validation_wrapper')
.append(service_validation.detach());
}
......@@ -284,11 +286,21 @@ function fill_service_entry(s) {
var li_class = "btn";
var li_data = "";
if (e.state == "done") {
if (e.state == "done" && coop_is_connected()) {
li_data = ' data-rid="'+e.id+'" data-mid="'+e.partner_id[0]+'"';
li_class += "--inverse";
if (e.is_late == true) {
li_class += " late";
}
if (e.associate_registered=='both') {
li_class += " both";
}
} else if (e.state == "done" && !coop_is_connected()) {
li_data = ' data-rid="'+e.id+'" data-mid="'+e.partner_id[0]+'"';
li_class += "--inverse not_connected";
if (e.is_late == true) {
li_class += " late";
}
} else {
li_data = ' data-rid="'+e.id+'" data-mid="'+e.partner_id[0]+'"';
}
......@@ -321,13 +333,29 @@ function clean_service_entry() {
function fill_service_validation(rid, coop_num_name, coop_id) {
var coop_name_elts = coop_num_name.split(' - ');
for (member of loaded_services[0].members) {
if (member.id ==rid) {
if (member.associate_name) {
pages.service_entry_validation.find('#service_validation').hide();
pages.service_entry_validation.find('#associated_service_validation').show();
pages.service_entry_validation.find('#associated_btn').text(member.associate_name);
pages.service_entry_validation.find('#partner_btn').text(member.partner_name);
} else {
pages.service_entry_validation.find('#associated_service_validation').hide();
pages.service_entry_validation.find('#service_validation').show();
}
}
}
service_data={
rid: rid,
sid: selected_service.id,
mid: coop_id};
pages.service_entry_validation.find('span.member_name').text(coop_name_elts[1]);
move_service_validation_to(pages.service_entry_validation);
service_validation.find('.btn')
.data('rid', rid)
.data('sid', selected_service.id)
.data('mid', coop_id);
}
function select_possible_service() {
......@@ -372,7 +400,6 @@ function get_service_entry_data() {
dataType : 'json'
})
.done(function(rData) {
//console.log(rData);
info_place.text('');
var page_title = pages.service_entry.find('h1');
......@@ -397,6 +424,7 @@ function get_service_entry_data() {
page_title.text('Quel est ton service ?');
} else {
loaded_services = rData.res;
fill_service_entry(rData.res[0]);
}
}
......@@ -443,22 +471,21 @@ function fill_service_entry_sucess(member) {
}
function record_service_presence() {
function record_service_presence(e) {
var d = new Date();
var elapsed_since_last_call = d.getTime() - validation_last_call;
if (elapsed_since_last_call > 10000) {
if (elapsed_since_last_call > 1000) {
loading2.show();
validation_last_call = d.getTime();
var clicked = service_validation.find('.btn');
var rid = clicked.data('rid');
var mid = clicked.data('mid');
var sid = clicked.data('sid');
var stid = clicked.data('stid');
var rid = service_data.rid;
var mid = service_data.mid;
var sid = service_data.sid;
var stid = service_data.stid;
post_form(
'/members/service_presence/',
{'mid': mid, 'rid': rid, 'sid': sid, 'stid' : stid},
{'mid': mid, 'rid': rid, 'sid': sid, 'stid' : stid, 'cancel': false, 'type': e.data.type},
function(err, rData) {
if (!err) {
var res = rData.res;
......@@ -480,6 +507,28 @@ function record_service_presence() {
}
}
function cancel_service_presence(mid, rid) {
var d = new Date();
var elapsed_since_last_call = d.getTime() - validation_last_call;
if (elapsed_since_last_call > 1000) {
loading2.show();
validation_last_call = d.getTime();
var sid = selected_service.id;
post_form(
'/members/service_presence/',
{'mid': mid, 'rid': rid, 'sid': sid, 'stid' : 0, 'cancel': true},
function(err) {
if (!err) {
get_service_entry_data();
}
loading2.hide();
}
);
}
}
function fill_rattrapage_2() {
pages.rattrapage_2.find('span.member_name').text(current_displayed_member.name);
var msg = "Bienvenue pour ton rattrapage !";
......@@ -494,13 +543,11 @@ function fill_rattrapage_2() {
msg = "Tu es en désincrit.e ... La situation doit être réglée avez le Bureau des Membres";
} else {
move_service_validation_to(pages.rattrapage_2);
service_validation.find('.btn')
.data('rid', 0)
.data('sid', selected_service.id)
.data('stid', shift_ticket_id)
.data('mid', current_displayed_member.id);
service_data = {
rid : 0,
sid : selected_service.id,
stid : shift_ticket_id,
mid : current_displayed_member.id};
}
pages.rattrapage_2.find('h2').text(msg);
......@@ -641,7 +688,10 @@ $('.btn[data-next]').click(function() {
});
service_validation.on("click", ".btn", record_service_presence);
service_validation.on("click", ".btn", {type:'normal'}, record_service_presence);
associated_service_validation.on("click", "#associated_btn", {type:'associate'}, record_service_presence);
associated_service_validation.on("click", "#partner_btn", {type:'partner'}, record_service_presence);
associated_service_validation.on("click", "#both_btn", {type:'both'}, record_service_presence);
shift_members.on("click", '.btn[data-rid]', function() {
var clicked = $(this);
......@@ -653,6 +703,16 @@ shift_members.on("click", '.btn[data-rid]', function() {
});
shift_members.on("click", '.btn--inverse', function() {
if (coop_is_connected()) {
var clicked = $(this);
var rid = clicked.data('rid');
var mid = clicked.data('mid');
cancel_service_presence(mid, rid);
}
});
pages.shopping_entry.on('css', function() {
photo_advice.hide();
photo_studio.hide();
......
......@@ -32,6 +32,15 @@ function display_current_coop_form() {
let street2_input = form.find('[name="street2"]'),
phone_input = form.find('[name="phone"]');
if (current_coop.parent_name) {
$('#associated_member').show();
if (current_coop.parent_id)
$('#associated_member_name').text(current_coop.parent_name);
else $('#associated_member_name').text(current_coop.parent_name + " ATTENTION à faire manuellement");
} else {
$('#associated_member').hide();
}
chgt_shift_btn.hide();
chgt_shift_btn.off('click', open_shift_choice);
form.find('[name="firstname"]').val(current_coop.firstname);
......
......@@ -31,6 +31,7 @@ urlpatterns = [
url(r'^latest_coop_id/$', views.latest_coop_id),
url(r'^get/([0-9]+)$', views.get),
url(r'^exists/([a-zA-Z0-9_\-\.\+@]+)$', views.exists),
url(r'^is_associated/([0-9]+)$', views.is_associated),
url(r'^get_couchdb_odoo_markers/(.+)$', views.get_couchdb_odoo_markers),
url(r'^menu/$', views.menu),
url(r'^verify_final_state$', views.verify_final_state),
......@@ -50,13 +51,18 @@ urlpatterns = [
url(r'^easy_validate_shift_presence$', views.easy_validate_shift_presence),
# conso / groupe recherche / socio
url(r'^panel_get_purchases$', views.panel_get_purchases),
# BDM
# BDM
url(r'^save_partner_info$', views.save_partner_info),
# BDM - members admin
url(r'^admin$', admin.admin),
url(r'^admin/?$', admin.admin),
url(r'^admin/manage_makeups$', admin.manage_makeups),
url(r'^admin/manage_shift_registrations$', admin.manage_shift_registrations),
url(r'^admin/manage_regular_shifts$', admin.manage_regular_shifts),
url(r'^get_makeups_members$', admin.get_makeups_members),
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'^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),
......
......@@ -63,6 +63,10 @@ def exists(request, mail):
answer = CagetteMember.exists(mail)
return JsonResponse({'answer': answer})
def is_associated(request, id_parent):
answer = CagetteMember.is_associated(id_parent)
return JsonResponse({'answer': answer})
def getmemberimage(request, id):
m = CagetteMember(id)
call_res = m.get_image()
......@@ -97,7 +101,9 @@ def inscriptions(request, type=1):
'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', ''),
'max_chq_nb': getattr(settings, 'MAX_CHQ_NB', 12),
'show_ftop_button': getattr(settings, 'SHOW_FTOP_BUTTON', True),
'db': settings.COUCHDB['dbs']['member']}
'db': settings.COUCHDB['dbs']['member'],
'ASSOCIATE_MEMBER_SHIFT' : getattr(settings, 'ASSOCIATE_MEMBER_SHIFT', '')
}
response = HttpResponse(template.render(context, request))
return response
......@@ -277,6 +283,9 @@ def record_service_presence(request):
mid = int(request.POST.get("mid", 0)) # member id
sid = int(request.POST.get("sid", 0)) # shift id
stid = int(request.POST.get("stid", 0)) # shift_ticket_id
cancel = request.POST.get("cancel") == 'true'
typeAction = str(request.POST.get("type"))
app_env = getattr(settings, 'APP_ENV', "prod")
if (rid > -1 and mid > 0):
overrided_date = ""
......@@ -286,28 +295,31 @@ def record_service_presence(request):
if o_date:
overrided_date = re.sub(r'(%20)',' ', o_date.group(1))
# rid = 0 => C'est un rattrapage, sur le service
if sid > 0 and stid > 0:
# Add member to service and take presence into account
res['rattrapage'] = CagetteServices.record_rattrapage(mid, sid, stid)
if res['rattrapage'] is True:
res['update'] = 'ok'
else:
if (CagetteServices.registration_done(rid, overrided_date) is True):
res['update'] = 'ok'
if(not cancel):
# rid = 0 => C'est un rattrapage, sur le service
if sid > 0 and stid > 0:
# Add member to service and take presence into account
res['rattrapage'] = CagetteServices.record_rattrapage(mid, sid, stid, typeAction)
if res['rattrapage'] is True:
res['update'] = 'ok'
else:
res['update'] = 'ko'
if res['update'] == 'ok':
members = CagetteMember.search('id', mid)
m = members[0]
for k in ['image_medium', 'barcode', 'barcode_base']:
del m[k]
next_shift = {}
if len(m['shifts']) > 0:
next_shift = m['shifts'][0]
del m['shifts']
m['next_shift'] = next_shift
res['member'] = m
if (CagetteServices.registration_done(rid, overrided_date, typeAction) is True):
res['update'] = 'ok'
else:
res['update'] = 'ko'
if res['update'] == 'ok':
members = CagetteMember.search('id', mid)
m = members[0]
for k in ['image_medium', 'barcode', 'barcode_base']:
del m[k]
next_shift = {}
if len(m['shifts']) > 0:
next_shift = m['shifts'][0]
del m['shifts']
m['next_shift'] = next_shift
res['member'] = m
else: CagetteServices.reopen_registration(rid, overrided_date)
except Exception as e:
res['error'] = str(e)
return JsonResponse({'res': res})
......
......@@ -54,6 +54,13 @@
margin: 3rem 0;
}
#my_info .choose_makeups,
#my_info .unsuscribed_form_link,
#my_info .remove_future_registration {
font-size: 1.8rem;
word-break: normal;
}
#my_info #member_status_action,
#my_info .member_shift_name_area,
#my_info .member_coop_number_area {
......
......@@ -64,14 +64,39 @@
}
}
.shift_line_container {
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
}
.shift_line_extra_actions {
display: flex;
justify-content: flex-start;
flex-wrap: nowrap;
}
@media screen and (max-width:768px) {
.shift_line_container {
flex-direction: column;
}
.shift_line_extra_actions {
width: 100%;
}
.affect_associate_registered {
margin: 0.5rem 0;
}
}
.selectable_shift_line {
min-width: 325px;
display: flex;
align-items: center;
margin-left: 15px;
margin: 0.75rem 0;
border-radius: 5px;
margin-right: 15px;
}
.selectable_shift_line .checkbox {
......@@ -85,11 +110,31 @@
.affect_associate_registered {
display: flex;
align-items: center;
margin-left: 15px;
margin: 0.75rem 0;
border-radius: 5px;
}
@media screen and (min-width:768px) {
.selectable_shift_line {
margin: 0 15px;
}
.affect_associate_registered {
margin-left: 15px;
}
}
.selectable_shift{
margin: 1rem 0;
}
.delete_registration_button {
justify-content: center;
align-items: center;
margin: 0.75rem 15px;
color: #d9534f;
cursor: pointer;
display: none;
}
/* -- Calendar screen, makeups message */
......@@ -113,6 +158,21 @@
}
}
/* -- Calendar screen, can delete registrations message */
#can_delete_future_registrations_area {
display: none;
justify-content: center;
align-items: center;
margin: 0 1rem 1rem 1rem;
}
#can_delete_future_registrations_area button {
white-space: normal;
word-break: normal;
margin: 1rem;
}
/* -- Calendar screen, calendar */
#calendar {
......@@ -157,6 +217,19 @@ td{
color: white;
}
.fc-event.shift_booked_makeup {
background-color: #f0ad4e;
cursor: auto;
border-color: #f0ad4e;
}
.fc-event.shift_booked_makeup td {
--fc-list-event-hover-bg-color:#f0ad4e;
}
.fc-list-event.shift_booked_makeup {
color: white;
}
#calendar .fc-list-table {
table-layout: auto;
}
......@@ -204,4 +277,14 @@ td{
#calendar_explaination_button {
max-width: 60%;
margin: 2rem auto 0.5rem auto;
}
/* -- Assign shift modal */
.modal_affect_shift_buttons {
margin: 1rem 0;
}
.assign_shift_button {
margin: 0.25rem;
}
\ No newline at end of file
......@@ -166,6 +166,11 @@ body {
font-size: 1.5rem;
}
.remove_future_registration {
display: none;
white-space: normal;
}
.unsuscribed_form_link {
display: none;
text-decoration: none;
......
......@@ -20,7 +20,7 @@ function init_faq() {
}
$(document).on('click', "#shift_exchange_btn", () => {
goto('echange-de-services');
goto('echange-de-services');
});
$(document).on('click', '.accordion', function() {
......
/**
* Request a 6 month delay
*/
function request_delay() {
return new Promise((resolve) => {
let today = new Date();
const delay_start = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
let today_plus_six_month = new Date();
today_plus_six_month.setMonth(today_plus_six_month.getMonth()+6);
const diff_time = Math.abs(today_plus_six_month - today);
const diff_days = Math.ceil(diff_time / (1000 * 60 * 60 * 24));
$.ajax({
type: 'POST',
url: "/shifts/request_delay",
dataType:"json",
data: {
verif_token: partner_data.verif_token,
idPartner: partner_data.partner_id,
start_date: delay_start,
duration: diff_days
},
success: function() {
partner_data.cooperative_state = 'delay';
partner_data.date_delay_stop = today_plus_six_month.getFullYear()+'-'+(today_plus_six_month.getMonth()+1)+'-'+today_plus_six_month.getDate();
resolve();
},
error: function(data) {
if (data.status == 403
&& typeof data.responseJSON != 'undefined'
&& data.responseJSON.message === "delays limit reached") {
closeModal();
let msg_template = $("#cant_have_delay_msg_template");
openModal(
msg_template.html(),
() => {
window.location =member_cant_have_delay_form_link;
},
"J'accède au formulaire",
true,
false
);
} else {
err = {msg: "erreur serveur lors de la création du délai", ctx: 'request_delay'};
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') {
err.msg += ' : ' + data.responseJSON.error;
}
report_JS_error(err, 'members_space.home');
closeModal();
alert('Erreur lors de la création du délai.');
}
}
});
});
}
function init_my_shifts_tile() {
if (incoming_shifts.length === 0) {
$("#home_tile_my_services #home_incoming_services").text("Aucun service à venir...");
......
......@@ -68,8 +68,7 @@ function prepare_server_data(data) {
if (history_item.associate_registered == false || history_item.associate_registered == undefined) {
history_item.associate_registered = "";
}
else {
} else {
if (partner_data.associated_partner_id != "False") {
if (history_item.associate_registered==="partner") {
history_item.associate_registered = partner_data.name;
......
......@@ -131,6 +131,70 @@ function update_content() {
/* - Shifts */
/**
* Request a 6 month delay
*/
function request_delay() {
return new Promise((resolve) => {
let today = new Date();
const delay_start = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
let today_plus_six_month = new Date();
today_plus_six_month.setMonth(today_plus_six_month.getMonth()+6);
const diff_time = Math.abs(today_plus_six_month - today);
const diff_days = Math.ceil(diff_time / (1000 * 60 * 60 * 24));
$.ajax({
type: 'POST',
url: "/shifts/request_delay",
dataType:"json",
data: {
verif_token: (partner_data.is_associated_people === "True") ? partner_data.parent_verif_token : partner_data.verif_token,
idPartner: partner_data.concerned_partner_id,
start_date: delay_start,
duration: diff_days
},
success: function() {
partner_data.cooperative_state = 'delay';
partner_data.date_delay_stop = today_plus_six_month.getFullYear()+'-'+(today_plus_six_month.getMonth()+1)+'-'+today_plus_six_month.getDate();
resolve();
},
error: function(data) {
if (data.status == 403
&& typeof data.responseJSON != 'undefined'
&& data.responseJSON.message === "delays limit reached") {
closeModal();
let msg_template = $("#cant_have_delay_msg_template");
openModal(
msg_template.html(),
() => {
window.location =member_cant_have_delay_form_link;
},
"J'accède au formulaire",
true,
false
);
} else {
err = {msg: "erreur serveur lors de la création du délai", ctx: 'request_delay'};
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') {
err.msg += ' : ' + data.responseJSON.error;
}
report_JS_error(err, 'members_space.home');
closeModal();
alert('Erreur lors de la création du délai.');
}
}
});
});
}
/**
* Prepare a shift line to insert into the DOM.
* Is used in: Home - My Shifts tile ; My Shifts - Incoming shifts section
*
......@@ -147,7 +211,7 @@ function prepare_shift_line_template(date_begin) {
shift_line_template.find(".shift_line_date").text(f_date_shift_start);
shift_line_template.find(".shift_line_time").text(datetime_shift_start.toLocaleTimeString("fr-fr", time_options));
return shift_line_template;
}
......@@ -158,6 +222,7 @@ function prepare_shift_line_template(date_begin) {
*/
function init_my_info_data() {
$(".choose_makeups").off();
$(".remove_future_registration").off();
$(".unsuscribed_form_link").off();
$(".member_shift_name").text(partner_data.regular_shift_name);
......@@ -199,7 +264,6 @@ function init_my_info_data() {
if (
partner_data.makeups_to_do > 0
&& partner_data.is_associated_people === "False"
&& partner_data.cooperative_state !== 'unsubscribed'
) {
$(".choose_makeups").show();
......@@ -223,12 +287,17 @@ function init_my_info_data() {
}
}
if (partner_data.extra_shift_done > 0) {
$(".remove_future_registration").show();
$(".remove_future_registration").on('click', () => {
goto('echange-de-services');
});
}
$(".member_coop_number").text(partner_data.barcode_base);
}
$(document).ready(function() {
// TODO essayer de ne charger les js que au besoin
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
// If partner is associated (attached), display the pair's main partner shift data
......@@ -242,7 +311,6 @@ $(document).ready(function() {
// For associated people, their parent name is attached in their display name
let partner_name_split = partner_data.name.split(', ');
partner_data.name = partner_name_split[partner_name_split.length - 1];
base_location = (app_env === 'dev') ? '/members_space/' : '/';
......@@ -257,7 +325,7 @@ $(document).ready(function() {
// debouncing function from John Hann
// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
var debounce = function (func, threshold, execAsap) {
var timeout;
var timeout = null;
return function debounced () {
var obj = this, args = arguments;
......
......@@ -12,5 +12,6 @@ urlpatterns = [
url(r'^faqBDM$', views.faqBDM),
url(r'^no_content$', views.no_content),
url(r'^get_shifts_history$', views.get_shifts_history),
url(r'^offer_extra_shift$', views.offer_extra_shift),
url(r'^.*', views.index) # Urls unknown from the server will redirect to index
]
......@@ -99,9 +99,14 @@ def index(request, exception=None):
partnerData["parent_id"] = partnerData["parent_id"][0]
md5_calc = hashlib.md5(partnerData['parent_create_date'].encode('utf-8')).hexdigest()
partnerData['parent_verif_token'] = md5_calc
partnerData['makeups_to_do'] = partnerData['parent_makeups_to_do']
partnerData['date_delay_stop'] = partnerData['parent_date_delay_stop']
partnerData['can_have_delay'] = cs.member_can_have_delay(int(partnerData["parent_id"]))
partnerData['extra_shift_done'] = partnerData["parent_extra_shift_done"]
else:
partnerData["parent_name"] = False
partnerData['can_have_delay'] = cs.member_can_have_delay(int(partner_id))
# look for associated partner for parents
cm = CagetteMember(partner_id)
......@@ -113,8 +118,6 @@ def index(request, exception=None):
if (associated_partner is not None and partnerData["associated_partner_name"].find(str(associated_partner["barcode_base"])) == -1):
partnerData["associated_partner_name"] = str(associated_partner["barcode_base"]) + ' - ' + partnerData["associated_partner_name"]
partnerData['can_have_delay'] = cs.member_can_have_delay(int(partner_id))
m = CagetteMembersSpace()
context['show_faq'] = getattr(settings, 'MEMBERS_SPACE_FAQ_TEMPLATE', 'members_space/faq.html')
partnerData["comite"] = m.is_comite(partner_id)
......@@ -234,4 +237,13 @@ def get_shifts_history(request):
date_from = getattr(settings, 'START_DATE_FOR_SHIFTS_HISTORY', '2018-01-01')
res["data"] = m.get_shifts_history(partner_id, limit, offset, date_from)
return JsonResponse(res)
\ No newline at end of file
return JsonResponse(res)
def offer_extra_shift(request):
res = {}
partner_id = int(request.POST['partner_id'])
m = CagetteMember(partner_id)
res = m.update_extra_shift_done(0)
return JsonResponse(res)
......@@ -69,6 +69,8 @@
- COOP_BARCODE_RULE_ID = 11
- ASSOCIATE_BARCODE_RULE_ID = 12
- FUNDRAISING_CAT_ID = 1
- PARTS_PRICE_UNIT = 10.0
......@@ -129,6 +131,10 @@
La Cagette use False to implement custom rules
- ASSOCIATE_MEMBER_SHIFT = ''
Id number of the associate shift template
### Scales and labels files generation
- DAV_PATH = '/data/dav/cagette'
......
......@@ -9,7 +9,7 @@ class OdooEntityFieldsForm(forms.Form):
class ExportComptaForm(forms.Form):
mois = forms.DateField(
required=True,
widget=MonthYearWidget()
widget=MonthYearWidget(years=range(datetime.date.today().year-2,datetime.date.today().year+1))
)
#fichier = forms.FileField()
# CHOICES = [('zip', '1 fichier par journal'),('compact', '1 seul fichier')]
......
......@@ -7,7 +7,8 @@
.red {color:#FF0000;}
.b_red, .b_less_than_25pc {background:#ff3333 !important;}
.loading {background-image: url("/static/img/ajax-loader.gif"); background-repeat:no-repeat;}
.loading2 {display: none;}
.loading2 {display: none; position:absolute; top:-20px;}
.loading2-container {position:relative;}
body {background: #fff; margin:5px;}
a, a:active, a:focus,
......
......@@ -35,14 +35,15 @@ function get_shift_name(s_data) {
if (s_data && s_data.week) {
shift_name = weeks_name[s_data.week];
if (s_data.type == 2 && typeof manage_ftop != "undefined" && manage_ftop == true) {
if (s_data.type == 2 && typeof manage_ftop != "undefined" && manage_ftop == true && s_data.id != ASSOCIATE_MEMBER_SHIFT) {
shift_name = 'Volant';
} else if(s_data.id == ASSOCIATE_MEMBER_SHIFT) {
shift_name = 'Binôme';
} else {
shift_name += s_data.day + ' - ' + s_data.begin;
shift_name += ' - ' + s_data.place;
}
}
return shift_name;
}
......
......@@ -38,22 +38,28 @@ class CagetteShift(models.Model):
'cooperative_state', 'final_standard_point', 'create_date',
'final_ftop_point', 'shift_type', 'leave_ids', 'makeups_to_do', 'barcode_base',
'street', 'street2', 'zip', 'city', 'mobile', 'phone', 'email',
'is_associated_people', 'parent_id']
'is_associated_people', 'parent_id', 'extra_shift_done']
partnerData = self.o_api.search_read('res.partner', cond, fields, 1)
if partnerData:
partnerData = partnerData[0]
if partnerData['is_associated_people']:
cond = [['id', '=', partnerData['parent_id'][0]]]
fields = ['create_date']
fields = ['create_date', 'makeups_to_do', 'date_delay_stop', 'extra_shift_done']
parentData = self.o_api.search_read('res.partner', cond, fields, 1)
if parentData:
partnerData['parent_create_date'] = parentData[0]['create_date']
partnerData['parent_makeups_to_do'] = parentData[0]['makeups_to_do']
partnerData['parent_date_delay_stop'] = parentData[0]['date_delay_stop']
partnerData['parent_extra_shift_done'] = parentData[0]['extra_shift_done']
if partnerData['shift_type'] == 'standard':
partnerData['in_ftop_team'] = False
# Because 'in_ftop_team' doesn't seem to be reset to False in Odoo
cond = [['partner_id.id', '=', id]]
if partnerData['is_associated_people']:
cond = [['partner_id.id', '=', partnerData['parent_id'][0]]]
else:
cond = [['partner_id.id', '=', id]]
fields = ['shift_template_id', 'is_current']
shiftTemplate = self.o_api.search_read('shift.template.registration', cond, fields)
if (shiftTemplate and len(shiftTemplate) > 0):
......@@ -91,7 +97,7 @@ class CagetteShift(models.Model):
def get_shift_partner(self, id):
"""Récupère les shift du membre"""
fields = ['date_begin', 'date_end','final_standard_point',
'shift_id', 'shift_type','partner_id', "id", "associate_registered"] # res.partner
'shift_id', 'shift_type','partner_id', "id", "associate_registered", "is_makeup"] # res.partner
cond = [['partner_id.id', '=', id],['state', '=', 'open'],
['date_begin', '>', datetime.datetime.now().isoformat()]]
shiftData = self.o_api.search_read('shift.registration', cond, fields, order ="date_begin ASC")
......@@ -178,20 +184,30 @@ class CagetteShift(models.Model):
def affect_shift(self, data):
"""Affect shift to partner, his associate or both"""
response = None
cond = [['partner_id', '=', int(data['idPartner'])],
['id', '=', int(data['idShiftRegistration'])]]
fields = ['id']
try:
print(cond)
shit_to_affect = self.o_api.search_read('shift.registration', cond, fields, 1)
print(shit_to_affect)
if (len(shit_to_affect) == 1):
shift_res = shit_to_affect[0]
print(shift_res)
fieldsDatas = { "associate_registered":data['affected_partner']}
response = self.o_api.update('shift.registration', [shift_res['id']], fieldsDatas)
except Exception as e:
coop_logger.error("Reopen shift : %s", str(e))
# partner_id can be 'associated_people' one, which is never use as shift partner_id reference
# So, let's first retrieved data about the res.partner involved
cond = [['id', '=', int(data['idPartner'])]]
fields = ['parent_id']
partner = self.o_api.search_read('res.partner', cond, fields, 1)
if partner:
if partner[0]['parent_id']:
partner_id = partner[0]['parent_id'][0]
else:
partner_id = int(data['idPartner'])
cond = [['partner_id', '=', partner_id],
['id', '=', int(data['idShiftRegistration'])]]
fields = ['id']
try:
# make sure there is coherence between shift.registration id and partner_id (to avoid forged request)
shit_to_affect = self.o_api.search_read('shift.registration', cond, fields, 1)
if (len(shit_to_affect) == 1):
shift_res = shit_to_affect[0]
fieldsDatas = { "associate_registered":data['affected_partner']}
response = self.o_api.update('shift.registration', [shift_res['id']], fieldsDatas)
except Exception as e:
coop_logger.error("Model affect shift : %s", str(e))
else:
coop_logger.error("Model affect shift nobody found : %s", str(cond))
return response
def cancel_shift(self, idsRegisteur):
......@@ -356,4 +372,8 @@ class CagetteShift(models.Model):
def member_can_have_delay(self, partner_id):
""" Can a member have a delay? """
return self.o_api.execute('res.partner', 'can_have_extension', [partner_id])
\ No newline at end of file
return self.o_api.execute('res.partner', 'can_have_extension', [partner_id])
def update_counter_event(self, fields):
""" Add/remove points """
return self.o_api.create('shift.counter.event', fields)
\ No newline at end of file
......@@ -15,6 +15,7 @@ urlpatterns = [
url(r'^change_shift', views.change_shift),
url(r'^affect_shift', views.affect_shift),
url(r'^add_shift', views.add_shift),
url(r'^cancel_shift', views.cancel_shift),
url(r'^request_delay', views.request_delay),
url(r'^reset_members_positive_points', views.reset_members_positive_points)
]
......@@ -101,8 +101,11 @@ def get_list_shift_calendar(request, partner_id):
use_new_members_space = getattr(settings, 'USE_NEW_MEMBERS_SPACE', False)
listRegisterPartner = []
listMakeUpShift = []
for v in registerPartner:
listRegisterPartner.append(v['id'])
if v['is_makeup']:
listMakeUpShift.append(v['id'])
start = request.GET.get('start')
end = request.GET.get('end')
......@@ -136,7 +139,10 @@ def get_list_shift_calendar(request, partner_id):
if len(l) > 0:
if use_new_members_space is True:
event["classNames"] = ["shift_booked"]
if set(value['registration_ids']) & set(listRegisterPartner) & set(listMakeUpShift):
event["classNames"] = ["shift_booked_makeup"]
else :
event["classNames"] = ["shift_booked"]
else:
event["className"] = "shift_booked"
event["changed"] = False
......@@ -239,11 +245,14 @@ def affect_shift(request):
if Verification.verif_token(request.POST.get('verif_token'), int(request.POST.get('idPartner'))) is True:
cs = CagetteShift()
if 'idShiftRegistration' in request.POST and 'affected_partner' in request.POST:
# if request is made by associated people, idPartner is it's id, not "master" res.partner
# it's will be handled in model's method (affect_shift)
data = {
"idPartner": int(request.POST['idPartner']),
"idShiftRegistration": int(request.POST['idShiftRegistration']),
"affected_partner": request.POST['affected_partner'],
}
st_r_id = None
try:
st_r_id = cs.affect_shift(data)
except Exception as e:
......@@ -307,6 +316,37 @@ def add_shift(request):
else:
return HttpResponseForbidden()
def cancel_shift(request):
""" Annule une présence à un shift """
if 'verif_token' in request.POST:
partner_id = int(request.POST.get('idPartner'))
if Verification.verif_token(request.POST.get('verif_token'), partner_id) is True:
cs = CagetteShift()
listRegister = [int(request.POST['idRegister'])]
try:
response = cs.cancel_shift(listRegister)
# decrement extra_shift_done if param exists
if 'extra_shift_done' in request.POST:
target = int(request.POST["extra_shift_done"]) - 1
# extra security
if target < 0:
target = 0
cm = CagetteMember(partner_id)
cm.update_extra_shift_done(target)
return JsonResponse({"res" : 'response'})
except Exception as e:
return JsonResponse({"error" : str(e)}, status=500)
else:
return HttpResponseForbidden()
else:
return HttpResponseForbidden()
def request_delay(request):
if 'verif_token' in request.POST:
if Verification.verif_token(request.POST.get('verif_token'), int(request.POST.get('idPartner'))) is True:
......
......@@ -22,7 +22,7 @@
<div class="management_type_buttons txtcenter">
<button type="button" class="btn--primary management_type_button" id="manage_makeups_button">
Gestion des rattragapes
Gestion des rattrapages
<span class="management_type_button_icons"><i class="fas fa-arrow-right"></i></span>
</button><br>
<button type="button" class="btn--primary management_type_button" id="manage_shift_registrations_button">
......@@ -33,6 +33,10 @@
Gestion des binômes
<span class="management_type_button_icons"><i class="fas fa-arrow-right"></i></span>
</button><br>
<button type="button" class="btn--primary management_type_button" id="manage_regular_shifts_button">
Gestion des créneaux
<span class="management_type_button_icons"><i class="fas fa-arrow-right"></i></span>
</button><br>
<button type="button" class="btn--primary management_type_button" id="manage_leaves_button" disabled>
Gestion des congés
<span class="management_type_button_icons"><i class="fas fa-wrench"></i></span>
......
......@@ -11,6 +11,7 @@
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/datatables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/notify.min.js' %}?v="></script>
{% endblock %}
{% block content %}
......
{% extends "base.html" %}
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/datatables/datatables.min.css' %}">
<link rel="stylesheet" href="{% static 'css/admin/manage_regular_shifts.css' %}">
<link rel="stylesheet" href="{% static 'jquery-ui-1.12.1/jquery-ui.min.css' %}">
{% endblock %}
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/datatables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/notify.min.js' %}?v="></script>
{% endblock %}
{% block content %}
<div class="page_body">
<div id="back_to_admin_index">
<button type="button" class="btn--danger"><i class="fas fa-arrow-left"></i>&nbsp; Retour</button>
</div>
<div class="login_area">
{% include "common/conn_admin.html" %}
</div>
<div class="header txtcenter">
<h1>Gestion des Créneaux</h1>
</div>
<div class="page_content">
<div id="search_member_area">
<div id="search_member_form_area">
<h4>Rechercher un.e membre</h4>
<form id="search_member_form" action="javascript:;" method="post">
<input type="text" id="search_member_input" value="" placeholder="Nom ou numéro du coop..." required>
<button type="submit" class="btn--primary" id="search_member_button">Recherche</button>
</form>
</div>
<div class="search_member_results_area" style="display:none;">
<div class="search_results_text">
<p><i>Choisissez parmi les membres trouvés :</i></p>
</div>
<div class="search_member_results"></div>
</div>
</div>
<div id="partner_data_area">
<h4 class="member_name_container">
<i class="fas fa-user member_name_icon"></i>
<span class="member_info member_name"></span>
</h4>
<p class="shift_name_container">Créneau : <span class="member_info member_shift"></span></p>
<p class="status_container">Statut : <span class="member_info member_status"></span></p>
<p class="makeups_container">Nb rattrapage(s) : <span class="member_info member_makeups"></span></p>
<div id="actions_on_member">
<button class="btn--primary" id="remove_shift_template_button">
Désinscrire du créneau
</button>
</div>
</div>
</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>
<div class="checkbox_area">
<input type="checkbox" id="permanent_unsuscribe" name="permanent_unsuscribe">
<label for="permanent_unsuscribe">Désinscription définitive</label>
</div>
</div>
</div>
</div>
<script src='{% static "js/all_common.js" %}?v='></script>
<script src='{% static "js/admin/manage_regular_shifts.js" %}?v='></script>
{% endblock %}
......@@ -11,6 +11,7 @@
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/datatables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/notify.min.js' %}?v="></script>
{% endblock %}
{% block content %}
......
......@@ -138,9 +138,13 @@
</section>
</section>
<section class="grid-6 has-gutter" id="service_entry">
<div class="col-6 row-2">
<div class="col-2 row-2">
<a class="btn btn--primary" data-next="first_page" >Retour accueil</a>
</div>
<div class="col-2 row-2"></div>
<div class="col-2 row-2 login_area">
{% include "common/conn_admin.html" %}
</div>
<div class="col-1"></div>
<div class="col-4">
<h1 class="col-4 txtcenter">Qui es-tu ?</h1>
......@@ -178,9 +182,23 @@
<section id="service_validation" class="col-6 grid-6 has-gutter">
<div class="col-2"></div>
<a class="col-2 btn present">{{CONFIRME_PRESENT_BTN|safe}}</a>
<span class="loading2"><img width="75" src="/static/img/Pedro_luis_romani_ruiz.gif" alt="Chargement en cours...." /></span>
<div class="col-2"></div>
<div class="col-2 loading2-container">
<span class="loading2"><img width="75" src="/static/img/Pedro_luis_romani_ruiz.gif" alt="Chargement en cours...." /></span>
</div>
</section>
<div id="associated_service_validation">
<p class="col-6 txtcenter">Qui est présent à ce service ?</p>
<section class="col-6 grid-5 has-gutter">
<div class="col-1"></div>
<a id="associated_btn" class=" btn present">Membre</a>
<a id="partner_btn" class=" btn present">Associé</a>
<a id="both_btn" class=" btn present">Les deux</a>
<div class="col-1 loading2-container">
<span class="loading2"><img width="75" src="/static/img/Pedro_luis_romani_ruiz.gif" alt="Chargement en cours...." /></span>
</div>
</section>
</div>
</div>
<div class="col-2"></div>
<a class="btn col-2 return" data-next="service_entry">Retour</a>
......
......@@ -37,11 +37,11 @@
<section class="center" id="new_coop">
<div class="grid-1">
<div class="item-center">
<div class="item-center">
<h2 class="title">
NOUVEAU MEMBRE
</h2>
<form id="coop_create" lang="fr">
<form id="coop_create" lang="fr">
{% if ask_for_sex %}
<p>
{% include "members/sex_input.html" %}
......@@ -65,17 +65,58 @@
<input type="number" min="1" placeholder="Nb de chèques" name="ch_qty" id="ch_qty" style="display:none;"/>
</p>
{% if input_barcode %}
<p>
<input type="text" name="m_barcode" id="m_barcode" maxlength="13" size="13" placeholder="Code barre" autocomplete="off" required/>
</p>
{% endif %}
<button class="btn--primary">Valider</button>
</form>
</div>
<div id="mail_generation">
(*) L'adresse mail étant obligatoire, si le nouveau membre n'en a pas, veuillez en créer une en cliquant sur le bouton suivant : <a class="btn--info" id="generate_email">+</a>
</div>
{% if ASSOCIATE_MEMBER_SHIFT %}
<p id="add_binome" >+ Binomes (facultatif)</p>
<div id="associate_area" style="display:none;">
<div class="choice_button_area d-flex" >
<div id="existing_member_choice" class="member_choice">
A mettre en binome avec un.e membre existant.e
</div>
<div id="new_member_choice" class="member_choice">
A mettre en binome avec un.e nouveau membre
</div>
</div>
<div id="existing_member_choice_action" style="display:none;">
<input type="text" id="search_member_input" value="" placeholder="Nom ou numéro du coop..." >
<div class="btn--primary" id="search_member_button">Recherche</div>
<div class="search_member_results_area" style="display:none;">
<div class="search_results_text">
<p><i>Choisissez parmi les membres trouvés :</i></p>
</div>
<div class="search_member_results"></div>
</div>
<div class="chosen_associate_area" style="display:none;">
<div >
<p><i>Binôme choisit : </i></p>
</div>
<div class="chosen_associate"></div>
</div>
</div>
<div id="new_member_choice_action" style="display:none;">
<div >
<div>
<input type="text" id="new_member_input" value="" placeholder="Nom du membre" >
</div>
</div>
</div>
</div>
{% endif %}
<div>
<button class="btn--primary">Valider</button>
</div>
</form>
<div id="mail_generation">
(*) L'adresse mail étant obligatoire, si le nouveau membre n'en a pas, veuillez en créer une en cliquant sur le bouton suivant : <a class="btn--info" id="generate_email">+</a>
</div>
</div>
</section>
{% include "members/shift_template_choice.html" %}
......@@ -91,7 +132,7 @@
<p>Pensez à inscrire ce numéro temporaire de coopérateur au crayon de papier sur les deux formulaires papier.</p>
-->
<p>Créneau choisi : <span class="shift_template"></span></p>
<p>Prochain service : <span class="next_shift"></span></p>
<p id="next_shift_registration_detail">Prochain service : <span class="next_shift"></span></p>
<button class="btn--primary" id="next_coop">Coopérateur.rice suivant.e !</button>
</div>
......@@ -119,6 +160,7 @@
var couchdb_dbname = '{{db}}';
var couchdb_server = '{{couchdb_server}}' + couchdb_dbname;
var dbc = new PouchDB(couchdb_dbname);
var ASSOCIATE_MEMBER_SHIFT = '{{ASSOCIATE_MEMBER_SHIFT}}';
var sync = PouchDB.sync(couchdb_dbname, couchdb_server, {
live: true,
retry: true,
......
......@@ -60,6 +60,9 @@
<input type="text" name="m_barcode" id="m_barcode" disabled/>
</p>
{% endif %}
<div id="associated_member">
En binôme avec : <span id ="associated_member_name"></span>
</div>
</div>
<p class="buttons">
<button class="btn--success" name="valider">Tout est bon</button>
......
......@@ -21,6 +21,9 @@
<button type="button" class="btn--danger choose_makeups">
Je sélectionne mes rattrapages
</button>
<button type="button" class="btn--success remove_future_registration">
J'ai validé un service à deux, je peux supprimer une présence
</button>
</div>
<div class="member_shift_name_area">
<span>Mon créneau : </span>
......
......@@ -36,29 +36,36 @@
</div>
<div id="selectable_shift_line_template">
<div class="d-flex">
<div class="d-flex shift_line_container selectable_shift">
<div class="selectable_shift_line btn--primary">
<input type="checkbox" class="checkbox">
<div class="selectable_shift_line_text">
<span class="shift_line_date"></span> - <span class="shift_line_time"></span>
</div>
</div>
<div class="affect_associate_registered button--warning">
<div class="shift_line_extra_actions">
<div class="affect_associate_registered">
</div>
</div>
</div>
</div>
<div id="delete_registration_button_template">
<div class="delete_registration_button"><i class="fas fa-lg fa-trash"></i></div>
</div>
<div id="modal_affect_shift">
<div>Qui sera présent.e ?</div>
<div id="shift_partner" class="btn--primary">
</div>
<div id="shift_associate" class=" btn--primary">
</div>
<div id="shift_both" class=" btn--primary">
Les deux
<div class="modal_affect_shift_buttons">
<div id="shift_partner" class="btn--primary assign_shift_button">
</div>
<div id="shift_associate" class=" btn--primary assign_shift_button">
</div>
<div id="shift_both" class=" btn--primary assign_shift_button">
Les deux
</div>
</div>
</div>
<div id="modal_shift_exchange_template">
......@@ -74,10 +81,12 @@
<div id="calendar_explaination_template">
<h4>Légende du calendrier</h4>
<a class="example-event fc-daygrid-event fc-daygrid-block-event fc-h-event fc-event fc-event-start fc-event-end fc-event-future shift_booked"><div class="fc-event-main"><div class="fc-event-main-frame"><div class="fc-event-time">06:00</div><div class="fc-event-title-container"><div class="fc-event-title fc-sticky">&nbsp;- 9/12</div></div></div></div></a>
<p>Un service colorié en noir : je suis déjà inscrit.e à ce service.</p>
<a class="example-event fc-daygrid-event fc-daygrid-block-event fc-h-event fc-event fc-event-start fc-event-end fc-event-future shift_less_alf"><div class="fc-event-main"><div class="fc-event-main-frame"><div class="fc-event-time">10:45</div><div class="fc-event-title-container"><div class="fc-event-title fc-sticky">&nbsp;- 3/12</div></div></div></div></a>
<p>Un service colorié en bleu : je peux m'inscrire à ce service.</p>
<a class="example-event fc-daygrid-event fc-daygrid-block-event fc-h-event fc-event fc-event-start fc-event-end fc-event-future shift_booked"><div class="fc-event-main"><div class="fc-event-main-frame"><div class="fc-event-time">06:00</div><div class="fc-event-title-container"><div class="fc-event-title fc-sticky">&nbsp;- 9/12</div></div></div></div></a>
<p>Un service colorié en noir : je suis déjà inscrit.e à ce service.</p>
<a class="example-event fc-daygrid-event fc-daygrid-block-event fc-h-event fc-event fc-event-start fc-event-end fc-event-future shift_booked_makeup"><div class="fc-event-main"><div class="fc-event-main-frame"><div class="fc-event-time">13:30</div><div class="fc-event-title-container"><div class="fc-event-title fc-sticky">&nbsp;- 7/12</div></div></div></div></a>
<p>Un service colorié en orange : je suis inscrit.e à un rattrapage sur ce service.</p>
<p>3/12 <i class="arrow_explanation_numbers fas fa-arrow-right"></i> il y a déjà 3 places réservées à ce service sur 12 disponibles.
<b>Plus le chiffre de gauche est petit, plus on a besoin de coopérateurs.rices à ce service !</b></p>
</div>
......@@ -140,7 +149,8 @@
"associated_partner_name" : "{{partnerData.associated_partner_name}}",
"verif_token" : "{{partnerData.verif_token}}",
"leave_stop_date": "{{partnerData.leave_stop_date}}",
"comite": "{{partnerData.comite}}"
"comite": "{{partnerData.comite}}",
"extra_shift_done": parseInt("{{partnerData.extra_shift_done}}", 10)
};
var block_actions_for_attached_people = '{{block_actions_for_attached_people}}';
</script>
......
......@@ -25,6 +25,9 @@
<button type="button" class="btn--danger choose_makeups">
Je sélectionne mes rattrapages
</button>
<button type="button" class="btn--success remove_future_registration">
J'ai validé un service à deux, je peux supprimer une présence
</button>
</div>
</div>
</div>
......
......@@ -35,6 +35,14 @@
<span class="select_makeups_message_block">Je dois les sélectionner dans le calendrier. </span>
<span class="select_makeups_message_block">Je ne peux pas échanger de service tant que je n'ai pas choisi mes rattrapages. </span>
</div>
<div id="can_delete_future_registrations_area">
<button class="btn--success can_delete_future_registrations_button" id="delete_future_registration">
J'ai validé <span class="extra_shift_done"></span> service(s) à deux, je supprime un service futur
</button>
<button class="btn--success can_delete_future_registrations_button" id="offer_extra_shift">
Je souhaite donner <span class="extra_shift_done"></span> service(s) d'avance à la communauté
</button>
</div>
<div id="calendar_top_info">
<div id="partner_shifts_list">
<h4>Liste de mes services :</h4>
......
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