Commit 32757349 by Etienne Freiss

Merge branch '2420-regular-shift-admin' into 'dev_cooperatic'

2420 regular shift admin

See merge request !148
parents e0e9dc85 3795a517
Pipeline #2006 passed with stage
in 1 minute 28 seconds
......@@ -326,6 +326,13 @@ def manage_shift_registrations(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()
......@@ -409,3 +416,42 @@ def delete_shift_registration(request):
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:
res["message"] = "Unauthorized"
response = JsonResponse(res, status=403)
return response
\ No newline at end of file
......@@ -27,8 +27,7 @@ class CagetteMember(models.Model):
'display_ftop_points', 'display_std_points',
'is_exempted', 'cooperative_state', 'date_alert_stop']
m_shoft_default_fields = ['name', 'barcode_base', 'total_partner_owned_share',
'amount_subscription']
m_short_default_fields = ['name', 'barcode_base']
def __init__(self, id):
"""Init with odoo id."""
......@@ -97,7 +96,6 @@ class CagetteMember(models.Model):
# # # BDM
def save_partner_info(self, partner_id, fieldsDatas):
print(fieldsDatas)
return self.o_api.update('res.partner', partner_id, fieldsDatas)
......@@ -822,8 +820,27 @@ class CagetteMember(models.Model):
members.append(m)
return CagetteMember.add_next_shifts_to_members(members)
elif search_type == "shift_template_data":
fields = CagetteMember.m_short_default_fields
fields = fields + ['id', 'makeups_to_do', 'cooperative_state']
res = api.search_read('res.partner', cond, fields)
if res:
for partner in res:
c = [['partner_id', '=', int(partner['id'])], ['state', 'in', ('draft', 'open')]]
f = ['shift_template_id']
shift_template_reg = api.search_read('shift.template.registration', c, f)
if shift_template_reg:
partner['shift_template_id'] = shift_template_reg[0]['shift_template_id']
else:
partner['shift_template_id'] = None
return res
else:
fields = CagetteMember.m_shoft_default_fields
# TODO differentiate short & subscription_data searches
fields = CagetteMember.m_short_default_fields
fields = fields + ['total_partner_owned_share','amount_subscription']
res = api.search_read('res.partner', cond, fields)
return res
......@@ -911,6 +928,53 @@ class CagetteMember(models.Model):
return res
def get_member_selected_makeups(self):
res = {}
c = [["partner_id", "=", self.id], ["is_makeup", "=", True], ["state", "=", "open"]]
f=['id']
res = self.o_api.search_read("shift.registration", c, f)
return res
def unsuscribe_member(self):
res = {}
now = datetime.datetime.now().isoformat()
# Get and then delete shift template registration
c = [['partner_id', '=', self.id]]
f = ['id']
res_ids = self.o_api.search_read("shift.template.registration", c, f)
ids = [d['id'] for d in res_ids]
if ids:
res["delete_shift_template_reg"] = self.o_api.execute('shift.template.registration', 'unlink', ids)
# Get and then delete shift registrations
c = [['partner_id', '=', self.id], ['date_begin', '>', now]]
f = ['id']
res_ids = self.o_api.search_read("shift.registration", c, f)
ids = [d['id'] for d in res_ids]
if ids:
res["delete_shifts_reg"] = self.o_api.execute('shift.registration', 'unlink', ids)
# Close extensions
c = [['partner_id', '=', self.id], ['date_start', '<=', now], ['date_stop', '>=', now]]
f = ['id']
res_ids = self.o_api.search_read("shift.extension", c, f)
ids = [d['id'] for d in res_ids]
if ids:
f = {'date_stop': now}
res["close_extensions"] = self.o_api.update('shift.extension', ids, f)
return res
def set_cooperative_state(self, state):
f = {'cooperative_state': state}
return self.o_api.update('res.partner', [self.id], f)
def update_extra_shift_done(self, value):
api = OdooAPI()
res = {}
......
.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,11 +38,11 @@
padding-top: 0.755em;
}
/* Search members area */
.makeup_row {
background-color: #ffc854 !important;
}
/* Search membres area */
#search_member_area {
margin-top: 30px;
display: flex;
......
......@@ -15,6 +15,8 @@ $(document).ready(function() {
window.location.assign(location + "/manage_shift_registrations");
} 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 {
......
......@@ -371,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);
});
});
......@@ -58,7 +58,9 @@ urlpatterns = [
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),
]
......@@ -34,6 +34,10 @@
<span class="management_type_button_icons"><i class="fas fa-wrench"></i></span>
{# <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 %}
......
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