Commit 191dc696 by François C.

Merge branch 'adaptation_supercafoutch' into adaptation_supercoop_supercafoutch

parents c805631f c1c80f59
Pipeline #2360 passed with stage
in 1 minute 22 seconds
...@@ -1339,7 +1339,7 @@ class CagetteServices(models.Model): ...@@ -1339,7 +1339,7 @@ class CagetteServices(models.Model):
@staticmethod @staticmethod
def get_services_at_time(time, tz_offset, with_members=True): def get_services_at_time(time, tz_offset, with_members=True):
"""Retrieve present services with member linked.""" """Retrieve present services with members linked."""
default_acceptable_minutes_after_shift_begins = getattr(settings, 'ACCEPTABLE_ENTRANCE_MINUTES_AFTER_SHIFT_BEGINS', 15) default_acceptable_minutes_after_shift_begins = getattr(settings, 'ACCEPTABLE_ENTRANCE_MINUTES_AFTER_SHIFT_BEGINS', 15)
minutes_before_shift_starts_delay = getattr(settings, 'ACCEPTABLE_ENTRANCE_MINUTES_BEFORE_SHIFT', 15) minutes_before_shift_starts_delay = getattr(settings, 'ACCEPTABLE_ENTRANCE_MINUTES_BEFORE_SHIFT', 15)
...@@ -1360,7 +1360,7 @@ class CagetteServices(models.Model): ...@@ -1360,7 +1360,7 @@ class CagetteServices(models.Model):
fields = ['name', 'week_number', 'registration_ids', fields = ['name', 'week_number', 'registration_ids',
'standard_registration_ids', 'standard_registration_ids',
'shift_template_id', 'shift_ticket_ids', 'shift_template_id', 'shift_ticket_ids',
'date_begin_tz', 'date_end_tz'] 'date_begin_tz', 'date_end_tz', 'state']
services = api.search_read('shift.shift', cond, fields,order ="date_begin_tz ASC") services = api.search_read('shift.shift', cond, fields,order ="date_begin_tz ASC")
for s in services: for s in services:
if (len(s['registration_ids']) > 0): if (len(s['registration_ids']) > 0):
...@@ -1510,13 +1510,18 @@ class CagetteServices(models.Model): ...@@ -1510,13 +1510,18 @@ class CagetteServices(models.Model):
cond = [['date_begin', '>=', date_24h_before.isoformat()], cond = [['date_begin', '>=', date_24h_before.isoformat()],
['date_begin', '<=', end_date.isoformat()], ['date_begin', '<=', end_date.isoformat()],
['state', '=', 'open']] ['state', '=', 'open']]
fields = ['state', 'partner_id', 'date_begin'] fields = ['state', 'partner_id', 'date_begin', 'shift_id']
res = api.search_read('shift.registration', cond, fields) res = api.search_read('shift.registration', cond, fields)
ids = [] ids = []
partner_ids = [] partner_ids = []
excluded_partner = [] excluded_partner = []
canceled_reg_ids = [] # for exempted people
shift_ids = []
for r in res: for r in res:
partner_ids.append(int(r['partner_id'][0])) partner_ids.append(int(r['partner_id'][0]))
shift_id = int(r['shift_id'][0])
if shift_id not in shift_ids:
shift_ids.append(shift_id)
cond = [['id', 'in', partner_ids], cond = [['id', 'in', partner_ids],
['cooperative_state', 'in', ['exempted']]] ['cooperative_state', 'in', ['exempted']]]
fields = ['id'] fields = ['id']
...@@ -1530,13 +1535,28 @@ class CagetteServices(models.Model): ...@@ -1530,13 +1535,28 @@ class CagetteServices(models.Model):
(_h, _m, _s) = h.split(':') (_h, _m, _s) = h.split(':')
if int(_h) < 21: if int(_h) < 21:
ids.append(int(r['id'])) ids.append(int(r['id']))
else:
canceled_reg_ids.append(int(r['id']))
# coop_logger.info("Traitement absences shift_registration ids %s", ids) # coop_logger.info("Traitement absences shift_registration ids %s", ids)
f = {'state': absence_status, 'date_closed': now.isoformat()} f = {'state': absence_status, 'date_closed': now.isoformat()}
update_shift_reg_result = {'update': api.update('shift.registration', ids, f), 'reg_shift': res} update_shift_reg_result = {'update': api.update('shift.registration', ids, f), 'reg_shift': res, 'errors': []}
if update_shift_reg_result['update'] is True: if update_shift_reg_result['update'] is True:
update_shift_reg_result['process_status_res'] = api.execute('res.partner','run_process_target_status', []) update_shift_reg_result['process_status_res'] = api.execute('res.partner','run_process_target_status', [])
# change shift state by triggering button_done method for all related shifts
if len(canceled_reg_ids) > 0:
f = {'state': 'cancel', 'date_closed': now.isoformat()}
api.update('shift.registration', canceled_reg_ids, f)
for sid in shift_ids:
try:
api.execute('shift.shift', 'button_done', sid)
except Exception as e:
marshal_none_error = 'cannot marshal None unless allow_none is enabled'
if not (marshal_none_error in str(e)):
update_shift_reg_result['errors'].append({'shift_id': sid, 'msg' :str(e)})
return update_shift_reg_result return update_shift_reg_result
@staticmethod @staticmethod
def close_ftop_service(): def close_ftop_service():
"""Called by cron script""" """Called by cron script"""
...@@ -1670,6 +1690,104 @@ class CagetteServices(models.Model): ...@@ -1670,6 +1690,104 @@ class CagetteServices(models.Model):
coop_logger.error("easy_validate_shift_presence : %s %s", str(coop_id), str(e)) coop_logger.error("easy_validate_shift_presence : %s %s", str(coop_id), str(e))
return res return res
class CagetteService(models.Model):
"""Class to handle cagette Odoo service."""
def __init__(self, id):
"""Init with odoo id."""
self.id = int(id)
self.o_api = OdooAPI()
def _process_associated_people_extra_shift_done(self):
cond = [['shift_id', '=', self.id],
['state', '=', 'done'],
['associate_registered', '=', 'both'],
['should_increment_extra_shift_done', '=', True]]
fields = ['id', 'state', 'partner_id', 'date_begin']
res = self.o_api.search_read('shift.registration', cond, fields)
extra_shift_done_incremented_srids = [] # shift registration ids
for r in res:
cond = [['id', '=', r['partner_id'][0]]]
fields = ['id','extra_shift_done']
res_partner = self.o_api.search_read('res.partner', cond, fields)
f = {'extra_shift_done': res_partner[0]['extra_shift_done'] + 1 }
self.o_api.update('res.partner', [r['partner_id'][0]], f)
extra_shift_done_incremented_srids.append(int(r['id']))
# Make sure the counter isn't incremented twice
f = {'should_increment_extra_shift_done': False}
self.o_api.update('shift.registration', extra_shift_done_incremented_srids, f)
def _process_related_shift_registrations(self):
now = datetime.datetime.now()
absence_status = 'excused'
res_c = self.o_api.search_read('ir.config_parameter',
[['key', '=', 'lacagette_membership.absence_status']],
['value'])
if len(res_c) == 1:
absence_status = res_c[0]['value']
cond = [['shift_id', '=', self.id],
['state', '=', 'open']]
fields = ['state', 'partner_id', 'date_begin']
res = self.o_api.search_read('shift.registration', cond, fields)
ids = []
partner_ids = []
excluded_partner = []
canceled_reg_ids = [] # for exempted people
for r in res:
partner_ids.append(int(r['partner_id'][0]))
cond = [['id', 'in', partner_ids],
['cooperative_state', 'in', ['exempted']]]
fields = ['id']
res_exempted = self.o_api.search_read('res.partner', cond, fields)
for r in res_exempted:
excluded_partner.append(int(r['id']))
for r in res:
if not (int(r['partner_id'][0]) in excluded_partner):
d_begin = r['date_begin']
(d, h) = d_begin.split(' ')
(_h, _m, _s) = h.split(':')
if int(_h) < 21:
ids.append(int(r['id']))
else:
canceled_reg_ids.append(int(r['id']))
# coop_logger.info("Traitement absences shift_registration ids %s", ids)
f = {'state': absence_status, 'date_closed': now.isoformat()}
update_shift_reg_result = {'update': self.o_api.update('shift.registration', ids, f), 'reg_shift': res, 'errors': []}
if update_shift_reg_result['update'] is True:
update_shift_reg_result['process_status_res'] = self.o_api.execute('res.partner','run_process_target_status', [])
# change shift state by triggering button_done method for all related shifts
if len(canceled_reg_ids) > 0:
f = {'state': 'cancel', 'date_closed': now.isoformat()}
self.o_api.update('shift.registration', canceled_reg_ids, f)
try:
self.o_api.execute('shift.shift', 'button_done', self.id)
except Exception as e:
marshal_none_error = 'cannot marshal None unless allow_none is enabled'
if not (marshal_none_error in str(e)):
update_shift_reg_result['errors'].append({'shift_id': self.id, 'msg' :str(e)})
return update_shift_reg_result
def record_absences(self, request):
"""Can only been executed if an Odoo user is beeing connected."""
res = {}
try:
if CagetteUser.are_credentials_ok(request) is True:
self._process_associated_people_extra_shift_done()
res = self._process_related_shift_registrations()
else:
res['error'] = 'Forbidden'
except Exception as e:
coop_logger.error("CagetteService.record_absences : %s %s", str(self.id), str(e))
res['error'] = str(e)
return res
class CagetteUser(models.Model): class CagetteUser(models.Model):
@staticmethod @staticmethod
......
...@@ -49,6 +49,7 @@ h1 .member_name {font-weight: bold;} ...@@ -49,6 +49,7 @@ h1 .member_name {font-weight: bold;}
.members_list li.btn--inverse.late {background-color: #de9b00; color: white} .members_list li.btn--inverse.late {background-color: #de9b00; color: white}
.members_list li.btn--inverse.both {background-color: #0275d8 ; color: white} .members_list li.btn--inverse.both {background-color: #0275d8 ; color: white}
.members_list.done li.btn {pointer-events: none;}
#service_entry_success {font-size: x-large;} #service_entry_success {font-size: x-large;}
#service_entry_success .explanations {margin: 25px 0; font-size: 18px;} #service_entry_success .explanations {margin: 25px 0; font-size: 18px;}
#service_entry_success .points, #service_entry_success .points,
......
...@@ -41,6 +41,7 @@ var coop_info = $('.coop-info'); ...@@ -41,6 +41,7 @@ var coop_info = $('.coop-info');
var service_data = null; var service_data = null;
const missed_begin_msg = $('#missed_begin_msg').html(); const missed_begin_msg = $('#missed_begin_msg').html();
const current_shift_process_data_actions = $('#current_shift_process_data_actions');
let no_pict_msg = $('#no-picture-msg'); let no_pict_msg = $('#no-picture-msg');
...@@ -70,6 +71,12 @@ var html_elts = { ...@@ -70,6 +71,12 @@ var html_elts = {
var chars = []; //input chars buffer var chars = []; //input chars buffer
var reset_shift_process_actions_zone = function() {
current_shift_process_data_actions.off('click', 'a');
current_shift_process_data_actions.hide();
current_shift_process_data_actions.empty();
}
function fill_member_slide(member) { function fill_member_slide(member) {
no_pict_msg.hide(); no_pict_msg.hide();
current_displayed_member = member; current_displayed_member = member;
...@@ -282,6 +289,9 @@ function fill_service_entry(s) { ...@@ -282,6 +289,9 @@ function fill_service_entry(s) {
// if (typeof s.late != "undefined" && s.late == true) { // if (typeof s.late != "undefined" && s.late == true) {
// m_list = '<ul class="members_list late">'; // m_list = '<ul class="members_list late">';
// } // }
if (s.state == 'done') {
m_list = '<ul class="members_list done">';
}
$.each(s.members, function(i, e) { $.each(s.members, function(i, e) {
var li_class = "btn"; var li_class = "btn";
var li_data = ""; var li_data = "";
...@@ -304,6 +314,9 @@ function fill_service_entry(s) { ...@@ -304,6 +314,9 @@ function fill_service_entry(s) {
} else { } else {
li_data = ' data-rid="'+e.id+'" data-mid="'+e.partner_id[0]+'"'; li_data = ' data-rid="'+e.id+'" data-mid="'+e.partner_id[0]+'"';
} }
if (s.state == 'done') {
li_data += ' disabled ';
}
m_list += '<li class="'+li_class+'" '+li_data+'>'; m_list += '<li class="'+li_class+'" '+li_data+'>';
m_list += e.partner_id[1]; m_list += e.partner_id[1];
m_list += '</li>'; m_list += '</li>';
...@@ -311,6 +324,45 @@ function fill_service_entry(s) { ...@@ -311,6 +324,45 @@ function fill_service_entry(s) {
m_list += '</ul>'; m_list += '</ul>';
} }
if (coop_is_connected()) {
// Add shift process data
reset_shift_process_actions_zone();
if (s.state == 'draft' || s.state == 'confirm') {
let btn = $('<a>').addClass('btn btn--primary txtcenter')
.text('Enregistrer les absences / présences')
.attr('id','record_shift_absences');
current_shift_process_data_actions.append(btn);
current_shift_process_data_actions.on('click', '#record_shift_absences', function(){
msg = "<p>Lancer le traitement des présences et absences de ce service</p>";
openModal(msg, function() {
btn.attr('disabled', 'true')
try {
$.ajax({
url: '/members/record_shift_absences/' + s.id,
dataType : 'json'
})
.done(function(rData) {
if (typeof rData.res.update !== "undefined" && rData.res.update == true) {
enqueue_message_for_next_loading("Données de présences traitées.");
location.reload();
} else {
closeModal();
btn.removeAttr('disabled');
alert(JSON.stringify(rData.res));
}
});
} catch (e) {
console.log(e);
}
}, 'Confirmer');
});
} else {
current_shift_process_data_actions.append("<em>Traitement des présences : " + s.state + "</em>");
}
current_shift_process_data_actions.show();
}
rattrapage_ou_volant = null; rattrapage_ou_volant = null;
shift_members.html(m_list); shift_members.html(m_list);
rattrapage_wanted.show(); rattrapage_wanted.show();
...@@ -401,6 +453,8 @@ function get_service_entry_data() { ...@@ -401,6 +453,8 @@ function get_service_entry_data() {
}) })
.done(function(rData) { .done(function(rData) {
info_place.text(''); info_place.text('');
reset_shift_process_actions_zone();
var page_title = pages.service_entry.find('h1'); var page_title = pages.service_entry.find('h1');
page_title.text('Qui es-tu ?'); page_title.text('Qui es-tu ?');
......
...@@ -43,6 +43,7 @@ urlpatterns = [ ...@@ -43,6 +43,7 @@ urlpatterns = [
url(r'^services_at_time/([0-9TZ\-\: \.]+)/([0-9\-]+)$', views.services_at_time), url(r'^services_at_time/([0-9TZ\-\: \.]+)/([0-9\-]+)$', views.services_at_time),
url(r'^service_presence/$', views.record_service_presence), url(r'^service_presence/$', views.record_service_presence),
url(r'^record_absences/?([0-9\-\ \:]*)$', views.record_absences), url(r'^record_absences/?([0-9\-\ \:]*)$', views.record_absences),
url(r'^record_shift_absences/?([0-9]+)$', views.record_shift_absences),
url(r'^close_ftop_service$', views.close_ftop_service), url(r'^close_ftop_service$', views.close_ftop_service),
url(r'^get_credentials$', views.get_credentials), url(r'^get_credentials$', views.get_credentials),
url(r'^remove_data_from_couchdb$', views.remove_data_from_CouchDB), url(r'^remove_data_from_couchdb$', views.remove_data_from_CouchDB),
......
...@@ -5,7 +5,7 @@ from outils.for_view_imports import * ...@@ -5,7 +5,7 @@ from outils.for_view_imports import *
from members.models import CagetteMember from members.models import CagetteMember
from members.models import CagetteUser from members.models import CagetteUser
from members.models import CagetteMembers from members.models import CagetteMembers
from members.models import CagetteServices from members.models import CagetteServices, CagetteService
from outils.forms import GenericExportMonthForm from outils.forms import GenericExportMonthForm
import datetime import datetime
...@@ -105,6 +105,7 @@ def inscriptions(request, type=1): ...@@ -105,6 +105,7 @@ def inscriptions(request, type=1):
'show_ftop_button': getattr(settings, 'SHOW_FTOP_BUTTON', True), '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', ''), 'ASSOCIATE_MEMBER_SHIFT' : getattr(settings, 'ASSOCIATE_MEMBER_SHIFT', ''),
'can_create_binome': getattr(settings, 'CAN_CREATE_BINOME', True),
'prepa_odoo_url' : getattr(settings, 'PREPA_ODOO_URL', '/members/prepa-odoo'), 'prepa_odoo_url' : getattr(settings, 'PREPA_ODOO_URL', '/members/prepa-odoo'),
'committees_shift_id': committees_shift_id, 'committees_shift_id': committees_shift_id,
} }
...@@ -347,6 +348,10 @@ def easy_validate_shift_presence(request): ...@@ -347,6 +348,10 @@ def easy_validate_shift_presence(request):
def record_absences(request, date): def record_absences(request, date):
return JsonResponse({'res': CagetteServices.record_absences(date)}) return JsonResponse({'res': CagetteServices.record_absences(date)})
def record_shift_absences(request, id):
shift = CagetteService(id)
return JsonResponse({'res': shift.record_absences(request)})
def close_ftop_service(request): def close_ftop_service(request):
"""Close the closest past FTOP service""" """Close the closest past FTOP service"""
return JsonResponse({'res': CagetteServices.close_ftop_service()}) return JsonResponse({'res': CagetteServices.close_ftop_service()})
......
...@@ -39,6 +39,7 @@ ...@@ -39,6 +39,7 @@
justify-content: space-between; justify-content: space-between;
} }
@media screen and (max-width:992px) { @media screen and (max-width:992px) {
#calendar_top_info { #calendar_top_info {
display: flex; display: flex;
......
var calendar = null, var calendar = null,
selected_shift = null, selected_shift = null,
vw = null; vw = null,
adding_mode = false;
/* - Logic */ /* - Logic */
...@@ -51,6 +52,9 @@ function add_or_change_shift(new_shift_id) { ...@@ -51,6 +52,9 @@ function add_or_change_shift(new_shift_id) {
if (selected_shift === null) { if (selected_shift === null) {
tUrl = '/shifts/add_shift'; tUrl = '/shifts/add_shift';
if (partner_data.makeups_to_do > 0) {
tData += '&is_makeup=1';
}
} else { } else {
tUrl = '/shifts/change_shift'; tUrl = '/shifts/change_shift';
tData = tData + '&idOldShift='+ selected_shift.shift_id[0] +'&idRegister=' + selected_shift.id; tData = tData + '&idOldShift='+ selected_shift.shift_id[0] +'&idRegister=' + selected_shift.id;
...@@ -143,6 +147,8 @@ function add_or_change_shift(new_shift_id) { ...@@ -143,6 +147,8 @@ function add_or_change_shift(new_shift_id) {
}, 300); }, 300);
} }
}); });
adding_mode = false;
$('#start_adding_shift').prop('disabled', false);
} }
return null; return null;
...@@ -315,6 +321,7 @@ function init_shifts_list() { ...@@ -315,6 +321,7 @@ function init_shifts_list() {
if (!can_exchange_shifts()) { if (!can_exchange_shifts()) {
shift_line_template.find(".selectable_shift_line").addClass("btn"); shift_line_template.find(".selectable_shift_line").addClass("btn");
shift_line_template.find(".checkbox").prop("disabled", "disabled"); shift_line_template.find(".checkbox").prop("disabled", "disabled");
$('#start_adding_shift').prop('disabled', true);
} else { } else {
if (shift.is_makeup==true) { if (shift.is_makeup==true) {
shift_line_template.find(".selectable_shift_line").addClass("btn--warning"); shift_line_template.find(".selectable_shift_line").addClass("btn--warning");
...@@ -577,12 +584,27 @@ function init_calendar_page() { ...@@ -577,12 +584,27 @@ function init_calendar_page() {
"Valider" "Valider"
); );
} else if (selected_shift === null && can_exchange_shifts()) { } else if (selected_shift === null && can_exchange_shifts()) {
/* could exchange shift but no old shift selected */ if (adding_mode === false) {
openModal( /* could exchange shift but no old shift selected */
"Je dois sélectionner un service à échanger.", openModal(
closeModal, "Je dois sélectionner un service à échanger.",
"J'ai compris" closeModal,
); "J'ai compris"
);
} else {
// Display modal
let modal_template = $("#modal_add_shift_template");
modal_template.find(".date_new_shift").text(new_shift_date);
modal_template.find(".time_new_shift").text(new_shift_time);
openModal(
modal_template.html(),
() => {
add_or_change_shift(new_shift_id);
},
"Valider"
);
}
} else if (should_select_makeup()) { } else if (should_select_makeup()) {
/* choose a makeup service */ /* choose a makeup service */
// Check if selected new shift is in less than 6 months // Check if selected new shift is in less than 6 months
...@@ -811,6 +833,19 @@ function init_shifts_exchange() { ...@@ -811,6 +833,19 @@ function init_shifts_exchange() {
init_calendar_page(); init_calendar_page();
} }
$('#start_adding_shift').click((c) => {
openModal(
"<p>Je souhaite sélectionner un service supplémentaire.</p>",
() => {
$(c.target).prop('disabled', true);
adding_mode = true;
closeModal();
},
"Confirmer",
false
);
});
$(window).smartresize(function() { $(window).smartresize(function() {
// only apply if a width threshold is passed // only apply if a width threshold is passed
if ( if (
......
...@@ -130,6 +130,8 @@ def index(request, exception=None): ...@@ -130,6 +130,8 @@ def index(request, exception=None):
if hasattr(settings, 'SHIFT_EXCHANGE_DAYS_TO_HIDE'): if hasattr(settings, 'SHIFT_EXCHANGE_DAYS_TO_HIDE'):
days_to_hide = settings.SHIFT_EXCHANGE_DAYS_TO_HIDE days_to_hide = settings.SHIFT_EXCHANGE_DAYS_TO_HIDE
context['daysToHide'] = days_to_hide context['daysToHide'] = days_to_hide
can_add_shift = getattr(settings, 'CAN_ADD_SHIFT', False)
context['canAddShift'] = "true" if can_add_shift is True else "false"
msettings = MConfig.get_settings('members') msettings = MConfig.get_settings('members')
context['forms_link'] = msettings['forms_link']['value'] if 'forms_link' in msettings else '' context['forms_link'] = msettings['forms_link']['value'] if 'forms_link' in msettings else ''
...@@ -169,7 +171,7 @@ def home(request): ...@@ -169,7 +171,7 @@ def home(request):
Consequently, the front-end url should be unknown from the server so the user is redirected to the index, Consequently, the front-end url should be unknown from the server so the user is redirected to the index,
then the front-end index will call this endpoint to load the home page then the front-end index will call this endpoint to load the home page
""" """
template = loader.get_template('members_space/home.html') template = loader.get_template(getattr(settings, 'MEMBERS_SPACE_HOME_TEMPLATE', 'members_space/home.html'))
context = { context = {
'title': 'Espace Membres', 'title': 'Espace Membres',
} }
...@@ -203,6 +205,7 @@ def shifts_exchange(request): ...@@ -203,6 +205,7 @@ def shifts_exchange(request):
template = loader.get_template('members_space/shifts_exchange.html') template = loader.get_template('members_space/shifts_exchange.html')
context = { context = {
'title': 'Échange de Services', 'title': 'Échange de Services',
'canAddShift': getattr(settings, 'CAN_ADD_SHIFT', False)
} }
return HttpResponse(template.render(context, request)) return HttpResponse(template.render(context, request))
......
...@@ -131,6 +131,10 @@ ...@@ -131,6 +131,10 @@
La Cagette use False to implement custom rules La Cagette use False to implement custom rules
- CAN_CREATE_BINOME = True (by default)
If set to False, in new member creation form, a member can be selected to be associated with.
- ASSOCIATE_MEMBER_SHIFT = '' - ASSOCIATE_MEMBER_SHIFT = ''
Id number of the associate shift template Id number of the associate shift template
...@@ -326,6 +330,11 @@ ...@@ -326,6 +330,11 @@
- MEMBERS_SPACE_FAQ_TEMPLATE = None - MEMBERS_SPACE_FAQ_TEMPLATE = None
If set to None, "FAQ menu" will not be shown. To use a custom content add a template and set it's relative path If set to None, "FAQ menu" will not be shown. To use a custom content add a template and set it's relative path
- MEMBERS_SPACE_HOME_TEMPLATE = 'members_space/supercafoutch/home.html'
If not set, 'members_space/home.html' (la Cagette)
- MEMBERS_SPACE_SHOW_UNDERSTAND_MY_STATUS = False - MEMBERS_SPACE_SHOW_UNDERSTAND_MY_STATUS = False
By default, is True. If False, tile showing explanations is not shown By default, is True. If False, tile showing explanations is not shown
...@@ -333,6 +342,10 @@ ...@@ -333,6 +342,10 @@
- BLOCK_ACTIONS_FOR_ATTACHED_PEOPLE = False - BLOCK_ACTIONS_FOR_ATTACHED_PEOPLE = False
Attached people can or not change his services Attached people can or not change his services
- CAN_ADD_SHIFT = True
By default, False. Set if coop can or not add shifts in their memberspace calendar
### Reception ### Reception
- RECEPTION_ADD_ADMIN_MODE = True - RECEPTION_ADD_ADMIN_MODE = True
......
...@@ -169,8 +169,8 @@ class CagetteShift(models.Model): ...@@ -169,8 +169,8 @@ class CagetteShift(models.Model):
"origin": 'memberspace', "origin": 'memberspace',
"is_makeup": data['is_makeup'], "is_makeup": data['is_makeup'],
"state": 'open'} "state": 'open'}
if shift_type == "standard" and data['is_makeup'] is not True: if (shift_type == "standard" and data['is_makeup'] is not True) or shift_type == "ftop":
fieldsDatas['template_created'] = 1 # It's not true but otherwise, presence add 1 standard point, which is not wanted fieldsDatas['template_created'] = 1 # It's not true but otherwise, presence add 1 standard point , which is not wanted
st_r_id = self.o_api.create('shift.registration', fieldsDatas) st_r_id = self.o_api.create('shift.registration', fieldsDatas)
except Exception as e: except Exception as e:
......
...@@ -281,8 +281,11 @@ def add_shift(request): ...@@ -281,8 +281,11 @@ def add_shift(request):
"idPartner": int(request.POST['idPartner']), "idPartner": int(request.POST['idPartner']),
"idShift":int(request.POST['idNewShift']), "idShift":int(request.POST['idNewShift']),
"shift_type":request.POST['shift_type'], "shift_type":request.POST['shift_type'],
"is_makeup":True "is_makeup": False
} }
if 'is_makeup' in request.POST and request.POST['is_makeup'] == "1":
data['is_makeup'] = True
#Insertion du nouveau shift #Insertion du nouveau shift
st_r_id = False st_r_id = False
......
...@@ -365,7 +365,17 @@ class CagetteStock(models.Model): ...@@ -365,7 +365,17 @@ class CagetteStock(models.Model):
return res return res
@staticmethod
def get_valuable_stock():
articles = []
try:
api = OdooAPI()
cond = [['qty_available','>', 0], ['active', '=', True]]
fields = ["barcode", "display_name", "qty_available", "standard_price"]
articles = api.search_read('product.product', cond, fields, 1000000)
except Exception as e:
coop_logger.error("Erreur get_valuable_stock : %s", str(e))
return articles
def set_test(): def set_test():
o_api = OdooAPI() o_api = OdooAPI()
......
$(document).ready(function() {
table_article = $('#tableArticle').DataTable({
"ajax": {
"url": "get_valuable_stock",
"data": ""
},
"columns":[
{data:"barcode", "title":"Code-barre", "width": "50%"},
{data:"display_name", "title":"Article", "width": "50%"},
{data:"qty_available", "title":"Stock", "width":"15%"
},
{data:"standard_price", "title":"Prix achat", "width":"15%"
}
],
"searching": true,
"order": [
[
2,
"desc"
]
],
"iDisplayLength": 50,
"language": {
"emptyTable": "Pas de donnée",
"info": "Affiché : lignes _START_ à _END_ sur _TOTAL_",
"infoEmpty": "Affiché : 0 ligne",
"infoFiltered": "(filtré de _MAX_ lignes au total)",
"thousands": ",",
"lengthMenu": "Afficher _MENU_ lignes",
"loadingRecords": "Loading...",
"processing": "Processing...",
"search": "Rechercher un article :",
"searchPlaceholder": "Référence, code-barre",
"zeroRecords": "Aucun résultat",
"paginate": {
"first": "Premier",
"last": "Dernier",
"next": "Suivant",
"previous": "Precedant"
},
"aria": {
"sortAscending": ": activate to sort column ascending",
"sortDescending": ": activate to sort column descending"
}
},
buttons: [
{
extend: 'excelHtml5',
text: 'Export en Excel',
className: 'btn--primary btn_export'
},
],
dom: '<lr<t>ip><"clear"><B>',
});
});
\ No newline at end of file
...@@ -46,4 +46,8 @@ urlpatterns = [ ...@@ -46,4 +46,8 @@ urlpatterns = [
url(r'^get_saleWitheNotSale', views.get_saleWitheNotSale), url(r'^get_saleWitheNotSale', views.get_saleWitheNotSale),
url(r'^get_test', views.get_test), url(r'^get_test', views.get_test),
# Values
url(r'^stockValues', views.stockValues),
url(r'^get_valuable_stock', views.get_valuable_stock),
] ]
...@@ -550,8 +550,16 @@ def get_saleWitheNotSale(request): ...@@ -550,8 +550,16 @@ def get_saleWitheNotSale(request):
return JsonResponse({"data":lArticleSale}, safe=True) return JsonResponse({"data":lArticleSale}, safe=True)
def stockValues(request):
"""Page valeurs du stock (quantités positives)."""
context = {'title': 'Stock (quantités positives valorisées)'}
template = loader.get_template('stock/stock_values.html')
return HttpResponse(template.render(context, request))
def get_valuable_stock(request):
articles = CagetteStock.get_valuable_stock()
return JsonResponse({"data":articles}, safe=True)
def get_test(request): def get_test(request):
res = CagetteStock.get_sale_qty_by_from(1) res = CagetteStock.get_sale_qty_by_from(1)
return JsonResponse({"data":res}, safe=False) return JsonResponse({"data":res}, safe=False)
...@@ -141,7 +141,9 @@ ...@@ -141,7 +141,9 @@
<div class="col-2 row-2"> <div class="col-2 row-2">
<a class="btn btn--primary" data-next="first_page" >Retour accueil</a> <a class="btn btn--primary" data-next="first_page" >Retour accueil</a>
</div> </div>
<div class="col-2 row-2"></div> <div class="col-2 row-2 txtcenter">
<div id="current_shift_process_data_actions" style="display:none;"></div>
</div>
<div class="col-2 row-2 login_area"> <div class="col-2 row-2 login_area">
{% include "common/conn_admin.html" %} {% include "common/conn_admin.html" %}
</div> </div>
......
...@@ -79,16 +79,18 @@ ...@@ -79,16 +79,18 @@
</p> </p>
{% endif %} {% endif %}
{% if ASSOCIATE_MEMBER_SHIFT %} {% if can_create_binome %}
<p id="add_binome" >+ Binomes (facultatif)</p> <p id="add_binome" >+ Binomes (facultatif)</p>
<div id="associate_area" style="display:none;"> <div id="associate_area" style="display:none;">
<div class="choice_button_area d-flex" > <div class="choice_button_area d-flex" >
<div id="existing_member_choice" class="member_choice"> <div id="existing_member_choice" class="member_choice">
A mettre en binome avec un.e membre existant.e A mettre en binome avec un.e membre existant.e
</div> </div>
{% if ASSOCIATE_MEMBER_SHIFT %}
<div id="new_member_choice" class="member_choice"> <div id="new_member_choice" class="member_choice">
A mettre en binome avec un.e nouveau membre A mettre en binome avec un.e nouveau membre
</div> </div>
{% endif %}
</div> </div>
<div id="existing_member_choice_action" style="display:none;"> <div id="existing_member_choice_action" style="display:none;">
...@@ -110,6 +112,7 @@ ...@@ -110,6 +112,7 @@
</div> </div>
</div> </div>
</div> </div>
{% if ASSOCIATE_MEMBER_SHIFT %}
<div id="new_member_choice_action" style="display:none;"> <div id="new_member_choice_action" style="display:none;">
<div > <div >
<div> <div>
...@@ -117,6 +120,7 @@ ...@@ -117,6 +120,7 @@
</div> </div>
</div> </div>
</div> </div>
{% endif %}
</div> </div>
{% endif %} {% endif %}
<div> <div>
......
...@@ -126,7 +126,7 @@ ...@@ -126,7 +126,7 @@
var days_to_hide = "{{daysToHide}}" var days_to_hide = "{{daysToHide}}"
var partner_data = { var partner_data = {
"partner_id":"{{partnerData.id}}", "partner_id":"{{partnerData.id}}",
"name":"{{partnerData.display_name}}", "name":"{{partnerData.display_name|safe}}",
"shift_type":"{{partnerData.shift_type}}", "shift_type":"{{partnerData.shift_type}}",
"date_delay_stop":"{{partnerData.date_delay_stop}}", "date_delay_stop":"{{partnerData.date_delay_stop}}",
"cooperative_state":"{{partnerData.cooperative_state}}", "cooperative_state":"{{partnerData.cooperative_state}}",
...@@ -134,25 +134,26 @@ ...@@ -134,25 +134,26 @@
"can_have_delay" : "{{partnerData.can_have_delay}}", "can_have_delay" : "{{partnerData.can_have_delay}}",
"makeups_to_do" : "{{partnerData.makeups_to_do}}", "makeups_to_do" : "{{partnerData.makeups_to_do}}",
"barcode_base" : "{{partnerData.barcode_base}}", "barcode_base" : "{{partnerData.barcode_base}}",
"street" : "{{partnerData.street}}", "street" : "{{partnerData.street|safe}}",
"street2" : "{{partnerData.street2}}", "street2" : "{{partnerData.street2|safe}}",
"zip" : "{{partnerData.zip}}", "zip" : "{{partnerData.zip}}",
"city" : "{{partnerData.city}}", "city" : "{{partnerData.city|safe}}",
"mobile" : "{{partnerData.mobile}}", "mobile" : "{{partnerData.mobile}}",
"phone" : "{{partnerData.phone}}", "phone" : "{{partnerData.phone}}",
"email" : "{{partnerData.email}}", "email" : "{{partnerData.email}}",
"is_associated_people" : "{{partnerData.is_associated_people}}", "is_associated_people" : "{{partnerData.is_associated_people}}",
"parent_id" : "{{partnerData.parent_id}}", "parent_id" : "{{partnerData.parent_id}}",
"parent_name" : "{{partnerData.parent_name}}", "parent_name" : "{{partnerData.parent_name|safe}}",
"parent_verif_token" : "{{partnerData.parent_verif_token}}", "parent_verif_token" : "{{partnerData.parent_verif_token}}",
"associated_partner_id" : "{{partnerData.associated_partner_id}}", "associated_partner_id" : "{{partnerData.associated_partner_id}}",
"associated_partner_name" : "{{partnerData.associated_partner_name}}", "associated_partner_name" : "{{partnerData.associated_partner_name|safe}}",
"verif_token" : "{{partnerData.verif_token}}", "verif_token" : "{{partnerData.verif_token}}",
"leave_stop_date": "{{partnerData.leave_stop_date}}", "leave_stop_date": "{{partnerData.leave_stop_date}}",
"comite": "{{partnerData.comite}}", "comite": "{{partnerData.comite}}",
"extra_shift_done": parseInt("{{partnerData.extra_shift_done}}", 10) "extra_shift_done": parseInt("{{partnerData.extra_shift_done}}", 10)
}; };
var block_actions_for_attached_people = '{{block_actions_for_attached_people}}'; var block_actions_for_attached_people = '{{block_actions_for_attached_people}}';
const canAddShift = {{canAddShift}};
</script> </script>
<script src="{% static "js/all_common.js" %}?v=1651853225"></script> <script src="{% static "js/all_common.js" %}?v=1651853225"></script>
<script src="{% static "js/members-space-home.js" %}?v=1651853225"></script> <script src="{% static "js/members-space-home.js" %}?v=1651853225"></script>
......
...@@ -50,6 +50,9 @@ ...@@ -50,6 +50,9 @@
<i class="fas fa-spinner fa-spin fa-lg"></i> <i class="fas fa-spinner fa-spin fa-lg"></i>
</div> </div>
<div id="shifts_list"></div> <div id="shifts_list"></div>
{% if canAddShift %}
<button class="btn--primary selectable_shift_line" id="start_adding_shift"><strong>+ Ajouter un service</strong></button>
{% endif %}
</div> </div>
<div id="calendar_explaination_area"></div> <div id="calendar_explaination_area"></div>
<button id="calendar_explaination_button" class="btn--success">Légende du calendrier</button> <button id="calendar_explaination_button" class="btn--success">Légende du calendrier</button>
......
<div id="faqBDM" class=" mt-3">
<div class="page_title txtcenter"><h1> Problèmes et demandes </h1></div>
<div class="tiles_container">
<div class="tile full_width_tile">
<div class="tile_content">
<div class="block">
<div class="faq_intro_texts">
<p>Bienvenue dans la Foire Aux Questions du Super Cafoutch. Nous espérons que tu y trouveras des réponses.</p>
</div>
<div class="param">
<button type="button" class="accordion btn_faq"><span class="full_width">1. Je suis indisponible ou dans l'incapacité d'effectuer un ou plusieurs services.</span>
</button>
<div class="input-container panel">
<div class="info_slots_shifts"><p><i class="fas fa-exclamation-circle"></i> <b>Créneau versus service,
c'est quoi la différence ?</b></p>
<p>Un créneau, c'est une plage récurrente, par exemple, tous les jeudis de semaine A de 15h15 à 18h15.</p>
<p>Un service, c'est une plage horaire en particulier, par exemple le jeudi 28 novembre de 15h15 à 18h15.</p>
</div>
<div class="grp_text"><h3><b>Je veux changer de créneau</b></h3>
<p class="attached-unblocked"> Envoie un mail au Bureau des Membres
<a href="mailto:bdm@supercafoutch.fr?subject=Demande de changement de créneau">bdm@supercafoutch.fr</a> en indiquant la semaine (A,B,C ou D), le jour de la semaine
et l’horaire du créneau. N’hésite pas à formuler plusieurs vœux car certains
créneaux sont déjà complets. </p>
</div>
<div class="grp_text"><h3><b>Je suis ponctuellement indisponible pour effectuer un service ou je m'absente pour une courte durée (4 semaines maximum)</b></h3>
<p class="attached-unblocked"> Tu peux échanger le ou les services que tu ne pourras pas assurer en autonomie sur ton espace
membre. Le lien ci-dessous te conduit sur l’onglet "Échange de services” de ton
espace
membre. Sélectionne ensuite le service auquel tu ne peux pas être présent et choisis
un
service de remplacement dans le calendrier. Nous te demandons de t’y prendre au moins 24
heures à l’avance pour des raisons logistiques. </p>
<div class="faq_link_button_area"><a href="javascript:void(0);" type="button"
class="btn--primary faq_link_button"
id="shift_exchange_btn"> J'échange mon service </a>
</div>
</div>
<div class="grp_text"><h3><b>Je suis dans l'incapacité de faire mes services pour raison de santé (plus de 4 semaines)</b></h3>
<p class="attached-unblocked"> Si ton état de santé ne te permet pas de faire tes
services, que ce soit
temporairement ou non, tu en seras exempté et tu pourras
continuer à faire tes courses. Explique ta situation au Bureau des Membres
<a href="mailto:bdm@supercafoutch.fr?subject=Incapacité à faire mes services pour raison de santé">bdm@supercafoutch.fr</a> et n'oublie pas de le dire à ton capitaine d'équipe. Si ton
incapacité est
temporaire, n’oublie pas de recontacter le Bureau des Membres à ton retour pour te
réinscrire sur un créneau. </p>
</div>
<div class="grp_text"><h3><b>Je m'absente plus longtemps pour une autre raison (plus de 4 semaines)</b></h3>
<p class="attached-unblocked"> Envoie un mail au Bureau des Membres
<a href="mailto:bdm@supercafoutch.fr?subject=Incapacité à faire mes services pour cause d'absence prolongée">bdm@supercafoutch.fr</a>
et à ton capitaine d'équipe en indiquant tes dates de début et de
fin d’absence. Pendant ton absence, tu ne fais pas de service et tu ne peux pas
faire tes courses. </p>
</div>
</div>
<button type="button" class="accordion btn_faq"><span class="full_width">2. Je suis en statut "Rattrapage" ou "Désinscrit.e" et je ne peux pas faire mes courses</span>
</button>
<div class="input-container panel">
<div class="grp_text"><h3><b>Dans quel cas ?</b></h3> Tu es en statut "Rattrapage" si tu as
manqué un service.<br/> Tu es désinscrit.e après avoir manqué trois services consécutifs sans
les rattraper. Tu es désinscrit.e de ton créneau afin d'y libérer une place. Il se peut aussi
que tu n'aies encore jamais été inscrit.e sur un créneau.
</div>
<div class="grp_text"><img src="/static/img/diagramme_etat_statut_cooperateurs.png"/>
<h3><b>Que faire ? </b></h3> <h4>Si tu es en statut "Rattrapage" :</h4>
<p class="attached-unblocked">Tu as manqué un service et tu ne t'es pas inscrit à un rattrapage. Tu ne peux plus faire tes courses.
Il faut t'inscrire au rattrapage sur ton espace membre, tu
as 6 mois pour l'effectuer. Quand tu seras inscrit à ton rattrapage, tu seras en statut
"Délai" et tu pourras de nouveau faire tes courses.<br/> Pour choisir tes rattrapages,
clique sur le bouton ci-dessous.</p>
<div class="faq_link_button_area"><a href="echange-de-services" type="button"
class="btn--primary faq_link_button"> Je
sélectionne mes rattrapages </a></div>
</div>
<div class="grp_text"><h4>Si tu es désinscrit.e :</h4> <p class="attached-unblocked">Tu as raté trois services consécutifs
sans les rattraper ou tu n'as pas effectué les rattrapages auxquels tu étais
inscrit.<br/>
Tu es désinscrit.e de ton créneau afin d'y libérer une place.<br/>Il se peut aussi
que tu n'aies encore jamais été inscrit sur un créneau.<br/><br/> Pour t'inscrire ou te
réinscrire, contacte le Bureau des Membres
<a href="mailto:bdm@supercafoutch.fr?subject=Inscription / réinscription créneau fixe">bdm@supercafoutch.fr</a>
en indiquant la semaine, le jour et l'horaire du créneau souhaité. Envoie
plusieurs vœux car certains créneaux sont déjà complets. Dès que tu es
réinscrit sur un créneau, n'oublie pas de t'inscrire à tes deux rattrapages.</p></div>
<div class="grp_text"><p class="attached-unblocked"> Si tu ne comprends pas pourquoi tu es désinscrit.e ou suspendu.e, tu
peux le signaler au Bureau des Membres <a href="mailto:bdm@supercafoutch.fr?subject=Problème désinscription créneau">bdm@supercafoutch.fr</a>
pour qu'il règle le problème.<br/> N'hésite pas à indiquer un
maximum d'informations sur ta situation pour nous aider à régler cette situation.</p>
</div>
</div>
<button type="button" class="accordion btn_faq"><span
class="full_width">3. Autres questions</span>
</button>
<div class="input-container panel">
<div class="grp_text"><h3><b>Je suis arrivé.e en retard ou j'ai oublié de pointer</b></h3>
<p class="attached-unblocked"> Si tu as effectué ton service mais que tu ne l’as pas
validé à l'entrée du magasin, il te faut prévenir le Bureau des Membres
<a href="mailto:bdm@supercafoutch.fr?subject=J'ai oublié de valider mon service">bdm@supercafoutch.fr</a> en indiquant la date et l’heure du service concerné. Le
Bureau des Membres pourra intervenir pour que tu n’aies pas à t’inscrire à un
service de rattrapage. </p>
</div>
<div class="grp_text"><h3><b>Je viens / nous venons d'avoir un bébé. Est-ce que je dois quand même faire mes services pour pouvoir faire mes courses ?</b></h3>
<p class="attached-unblocked"> Lors de la naissance (ou l’adoption) d’un enfant, les
coopérateurs peuvent continuer à faire leur courses sans faire de service pendant 12
mois.
Si les deux parents sont membres de la coopérative, ils peuvent se partager ces 12
mois comme ils l'entendent. Il peuvent prendre par exemple 6 mois chacun en même
temps ou 8 mois pour l'un puis 4 mois pour l'autre.
Envoie un mail au Bureau des Membres <a href="mailto:bdm@supercafoutch.fr?subject=Demande de congé parental de services">bdm@supercafoutch.fr</a>
avec les dates souhaitées. </p>
</div>
<div class="grp_text"><h3><b>Je veux changer d'adresse mail</b></h3>
<p class="attached-unblocked"> Il arrive qu’au moment de
l’inscription, une adresse mail erronée soit saisie. Il se peut aussi qu’un membre
change d’adresse mail. Or l'adresse mail est utilisée comme identifiant pour se
connecter à l'espace membre.
Envoie simplement un message à <a href="mailto:bdm@supercafoutch.fr?subject=Changement d'adresse mail">bdm@supercafoutch.fr</a> avec ta nouvelle adresse. </p>
</div>
<div class="grp_text"><h3><b>Je n'ai pas trouvé la réponse à ma question</b></h3>
<p class="attached-unblocked"> Envoie un mail à <a href="mailto:bdm@supercafoutch.fr?subject=Je n'ai pas trouvé la réponse à ma question">bdm@supercafoutch.fr</a>. On parie que tu
t’en doutais :-). <br/> Attention : si tu souhaites contacter le BDM pour prévenir que tu
seras absent-e à ton service cela ne sert à rien ! Il te faut déplacer ton service
via ton espace membre. Il n'est cependant pas possible d'échanger un service qui
commence dans moins de 24h pour des raisons logistiques. Si tu ne peux pas venir,
tu seras donc comptabilisé-e absent-e. Tu basculeras en statut "Rattrapage" et ne
pourras plus faire tes courses. Il te faudra sélectionner dans ton espace membre un
rattrapage à faire dans les 6 prochains mois pour basculer en statut "Délai" et
pouvoir faire de nouveau tes courses. </p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div id="home">
<div class="page_title txtcenter">
<h1>Espace Membre</h1>
</div>
<div class="tiles_container">
<div class="tile high_tile" id="home_tile_my_info">
<div class="tile_title">
<i class="fas fa-user tile_icon"></i>
<span class="member_info member_name"></span>
</div>
<div class="tile_content">
{# <p><span class="member_info member_name"></span></p> #}
<p class="member_status_text_container">Mon statut : <span class="member_info member_status"></span></p>
<div class="delay_date_stop_container">
( jusqu'au <span class="delay_date_stop"></span> )
</div>
<div id="member_status_action">
<a href="#" target="_blank" class="btn--warning unsuscribed_form_link">
J'accède au formulaire
</a>
<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>
<span class="member_shift_name member_info"></span>
</div>
<div class="member_coop_number_area">
<span>Mon numéro de coop : </span>
<span class="member_coop_number member_info"></span>
</div>
<div class="member_associated_partner_area">
<span>Je suis en binôme avec : </span>
<span class="member_associated_partner member_info"></span>
</div>
<div id="see_more_info">
<button type="button", class="btn btn--primary home_link_button" id="see_more_info_link">
Accéder à mes infos et comprendre mon statut
</button>
</div>
</div>
</div>
<div class="tile high_tile" id="home_tile_my_services">
<div class="tile_title">
<i class="fas fa-clipboard tile_icon"></i>
Mes Services
</div>
<div class="tile_content">
<h3>Services à venir</h3>
<div id="home_incoming_services">
<i class="fas fa-spinner fa-spin fa-lg"></i>
</div>
<div id="go_to_shift_history_area">
<button type="button", class="btn btn--primary" id="home_go_to_shift_history">
Accéder à mon historique
</button>
</div>
</div>
</div>
<div class="tile small_tile" id="home_tile_services_exchange">
<div class="tile_title">
<i class="fas fa-exchange-alt tile_icon"></i>
Échange de services
</div>
<div class="tile_content">
<div>
Un empêchement ? J'anticipe et déplace mes services jusqu'à 24h avant leur début !
</div>
<div class="home_link_button_area">
<button type="button" class="btn--primary home_link_button" id="go_to_shifts_calendar">
Accéder au calendrier d'échange de services
</button>
</div>
</div>
</div>
<div class="tile small_tile" id="home_tile_help">
<div class="tile_title">
<i class="fas fa-question-circle tile_icon"></i>
J'ai une demande
</div>
<div class="tile_content">
<div class="home_link_button_area">
<a
href="javascript:void(0);"
class="btn--primary home_link_button"
id="go_to_forms"
>
Accéder à la FAQ
</a>
</div>
</div>
</div>
<div class="tile small_tile" id="home_tile_shop_info">
<div class="tile_title">
<i class="fas fa-newspaper tile_icon"></i>
Informations magasins
</div>
<div id="shop_info_content">
<div class="shop_info_item shop_opening_hours">
<div class="shop_info_item_content">
<div class="opening_hours_title">
Horaires du magasin :
</div>
<div class="opening_hours_content">
{{shop_opening_hours|safe}}
</div>
</div>
</div>
<div class="shop_info_item shop_message">
<div class="shop_info_item_content shop_message_content">
{{msg_accueil|safe}}
</div>
</div>
</div>
</div>
</div>
</div>
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
<a class="dropdown-item" href="breakingArticleSet">Mettre en rupture un article sur odoo</a> <a class="dropdown-item" href="breakingArticleSet">Mettre en rupture un article sur odoo</a>
<a class="dropdown-item" href="stockQuantLastSale">Date de la dernière vente des articles</a> <a class="dropdown-item" href="stockQuantLastSale">Date de la dernière vente des articles</a>
<a class="dropdown-item" href="saleWithNotSale">Vente avec jours sans vente</a> <a class="dropdown-item" href="saleWithNotSale">Vente avec jours sans vente</a>
<a class="dropdown-item" href="stockValues">Stock (qtés > 0 valorisées)</a>
</div> </div>
</li> </li>
</ul> </ul>
......
{% extends "stock/stock_menu.html" %}
{% load static %}
{% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/datatables/jquery.dataTables.css' %}">
{% endblock %}
{% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'js/datatables/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/dataTables.plugins.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/datatables.buttons.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/buttons.html5.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/jszip.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/stock_values.js' %}"></script>
{% endblock %}
{% block content %}
<h1>Stock valorisé</h1>
<br>
<div class="main">
<table id="tableArticle" class="display" width="95%" cellspacing="0" ></table>
</div>
<br/>
<br/>
<script src="{% static "js/all_common.js" %}?v=1651853225"></script>
<script src="{% static "js/common.js" %}?v=1651853225"></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