Commit b90c7869 by Yvon

Merge branch 'dev_cooperatic' into supercafoutch-prod-20221003

parents 263c35e2 043e2a4b
Pipeline #2832 failed with stage
in 1 minute 6 seconds
...@@ -19,3 +19,4 @@ shop/errors.log ...@@ -19,3 +19,4 @@ shop/errors.log
.idea .idea
members/settings.json members/settings.json
.DS_Store .DS_Store
exec.*.log
...@@ -146,3 +146,6 @@ AMNISTIE_DATE= "2021-11-24 00:00:00" ...@@ -146,3 +146,6 @@ AMNISTIE_DATE= "2021-11-24 00:00:00"
# BDM Admin # BDM Admin
BDM_SHOW_FTOP_BUTTON = True BDM_SHOW_FTOP_BUTTON = True
# Entree
MINIMUM_SECONDS_BETWEEN_TWO_COMITEE_VALIDATION = 0
\ No newline at end of file
...@@ -93,5 +93,9 @@ VRAC_SHELFS = [] ...@@ -93,5 +93,9 @@ VRAC_SHELFS = []
# Should block service exchange if old service is happening in less than 24h # Should block service exchange if old service is happening in less than 24h
BLOCK_SERVICE_EXCHANGE_24H_BEFORE = False BLOCK_SERVICE_EXCHANGE_24H_BEFORE = False
ALLOW_FOUR_DIGITS_IN_RECEPTION_PRICE = True
REMOVE_15_MINUTES_AT_SHIFT_END = False
# Fix incompatibility between meal voucher module and an old version of michel bibikoff product label printer software used in supercafoutch # Fix incompatibility between meal voucher module and an old version of michel bibikoff product label printer software used in supercafoutch
DO_NOT_SHOW_MEAL_VOUCHER_OK_LINE_IN_PRODUCT_INFO_FOR_LABEL = True DO_NOT_SHOW_MEAL_VOUCHER_OK_LINE_IN_PRODUCT_INFO_FOR_LABEL = True
...@@ -51,7 +51,8 @@ class CagetteInventory(models.Model): ...@@ -51,7 +51,8 @@ class CagetteInventory(models.Model):
with open(os.path.join(r, file)) as json_file: with open(os.path.join(r, file)) as json_file:
file_data = json.load(json_file) file_data = json.load(json_file)
date_time = datetime.fromtimestamp(int(filename)) local_tz = pytz.timezone('Europe/Paris')
date_time = datetime.fromtimestamp(int(filename), local_tz)
d = date_time.strftime("%d/%m/%Y, %H:%M") d = date_time.strftime("%d/%m/%Y, %H:%M")
file_data['id'] = int(filename) file_data['id'] = int(filename)
......
...@@ -18,6 +18,7 @@ function init_datatable() { ...@@ -18,6 +18,7 @@ function init_datatable() {
{data: "id", title:"id", "visible": false}, {data: "id", title:"id", "visible": false},
{ {
data:"datetime_created", data:"datetime_created",
orderData:[0],
title:"Liste", title:"Liste",
render: function (data) { render: function (data) {
return "Liste du " + data; return "Liste du " + data;
......
...@@ -5,10 +5,11 @@ from outils.common import OdooAPI ...@@ -5,10 +5,11 @@ from outils.common import OdooAPI
from members.models import CagetteUser from members.models import CagetteUser
from members.models import CagetteMembers from members.models import CagetteMembers
from members.models import CagetteMember from members.models import CagetteMember
from members.models import CagetteServices from shifts.models import CagetteServices
from shifts.models import CagetteShift from shifts.models import CagetteShift
from members_space.models import CagetteMembersSpace
from outils.common import MConfig from outils.common import MConfig
from datetime import datetime from datetime import datetime, date
default_msettings = {'msg_accueil': {'title': 'Message borne accueil', default_msettings = {'msg_accueil': {'title': 'Message borne accueil',
'type': 'textarea', 'type': 'textarea',
...@@ -120,7 +121,7 @@ default_msettings = {'msg_accueil': {'title': 'Message borne accueil', ...@@ -120,7 +121,7 @@ default_msettings = {'msg_accueil': {'title': 'Message borne accueil',
'sort_order': 16 'sort_order': 16
}, },
'member_cant_have_delay_form_link': { 'member_cant_have_delay_form_link': {
'title': 'Lien vers le formulaire pour les membres n\'ayant pas rattrapé leur service après 6 mois', 'title': 'Lien vers le formulaire pour les membres n\'ayant pas rattrapé leur service après la durée de l\'extension',
'type': 'text', 'type': 'text',
'value': '', 'value': '',
'class': 'link', 'class': 'link',
...@@ -320,8 +321,11 @@ def admin(request): ...@@ -320,8 +321,11 @@ def admin(request):
def manage_makeups(request): def manage_makeups(request):
""" Administration des membres """ """ Administration des membres """
template = loader.get_template('members/admin/manage_makeups.html') template = loader.get_template('members/admin/manage_makeups.html')
m = CagetteMembersSpace()
context = {'title': 'BDM - Rattrapages', context = {'title': 'BDM - Rattrapages',
'module': 'Membres'} 'module': 'Membres',
'extension_duration': m.get_extension_duration()
}
return HttpResponse(template.render(context, request)) return HttpResponse(template.render(context, request))
def manage_shift_registrations(request): def manage_shift_registrations(request):
...@@ -342,6 +346,8 @@ def manage_regular_shifts(request): ...@@ -342,6 +346,8 @@ def manage_regular_shifts(request):
""" Administration des créneaux des membres """ """ Administration des créneaux des membres """
template = loader.get_template('members/admin/manage_regular_shifts.html') template = loader.get_template('members/admin/manage_regular_shifts.html')
committees_shift_id = CagetteServices.get_committees_shift_id() committees_shift_id = CagetteServices.get_committees_shift_id()
committees_shift_name = getattr(settings, 'COMMITTEES_SHIFT_NAME', "service des Comités")
exemptions_shift_id = CagetteServices.get_exemptions_shift_id()
context = { context = {
'title': 'BDM - Créneaux', 'title': 'BDM - Créneaux',
'module': 'Membres', 'module': 'Membres',
...@@ -353,7 +359,9 @@ def manage_regular_shifts(request): ...@@ -353,7 +359,9 @@ def manage_regular_shifts(request):
'show_ftop_button': getattr(settings, 'BDM_SHOW_FTOP_BUTTON', True), 'show_ftop_button': getattr(settings, 'BDM_SHOW_FTOP_BUTTON', True),
'has_committe_shift': committees_shift_id is not None, 'has_committe_shift': committees_shift_id is not None,
'committees_shift_id': committees_shift_id, 'committees_shift_id': committees_shift_id,
'ASSOCIATE_MEMBER_SHIFT' : getattr(settings, 'ASSOCIATE_MEMBER_SHIFT', '') 'committees_shift_name': committees_shift_name,
'ASSOCIATE_MEMBER_SHIFT' : getattr(settings, 'ASSOCIATE_MEMBER_SHIFT', ''),
'exemptions_shift_id': exemptions_shift_id,
} }
return HttpResponse(template.render(context, request)) return HttpResponse(template.render(context, request))
...@@ -400,7 +408,7 @@ def update_members_makeups(request): ...@@ -400,7 +408,7 @@ def update_members_makeups(request):
points_update = points_diff points_update = points_diff
data = { data = {
'name': "Modif manuelle des rattrapages depuis l'admin BDM", 'name': "Admin BDM - " + member_data["description"],
'shift_id': False, 'shift_id': False,
'type': member_data["member_shift_type"], 'type': member_data["member_shift_type"],
'partner_id': int(member_data["member_id"]), 'partner_id': int(member_data["member_id"]),
...@@ -408,13 +416,78 @@ def update_members_makeups(request): ...@@ -408,13 +416,78 @@ def update_members_makeups(request):
} }
cm.update_member_points(data) cm.update_member_points(data)
# Better to call run_process_target_status now, otherwise partner remains
# in alert until routine is called (every 5 minutes). It is a bit weird for users and
# allocation of rattrapages before the routine is executed will not trigger change to delay state !
# (the parner would have to go back to espace membre and click on "j'affecte mes rattrapage"
# even though it shows 'J'ai 0 rattrapages à effecter' for the delay state change to be eventually triggered)
api = OdooAPI()
api.execute('res.partner', 'run_process_target_status', [])
response = JsonResponse(res) response = JsonResponse(res)
else: else:
res["message"] = "Unauthorized" res["message"] = "Unauthorized"
response = JsonResponse(res, status=403) response = JsonResponse(res, status=403)
return response return response
def regenerate_member_delay(request):
""" From BDM admin, close existing extension if exists & recreate for 6 months """
res = {}
is_connected_user = CagetteUser.are_credentials_ok(request)
if is_connected_user is True:
raw_data = json.loads(request.body.decode())
# Close extension if has one
member_id = int(raw_data["member_id"])
cm = CagetteMember(member_id)
cm.close_extension()
# Recreate starting now
cs = CagetteShift()
data = {
"idPartner": member_id,
"start_date": date.today().isoformat()
}
duration = raw_data["duration"]
ext_name = "Délai étendue depuis l'admin BDM"
res["create_delay"] = cs.create_delay(data=data, duration=duration, ext_name=ext_name)
if (res["create_delay"]):
try:
# Add 0 pt to counter so odoo updates member status
data = {
'name': "Forcer l'entrée en délai",
'shift_id': False,
'type': "standard",
'partner_id': member_id,
'point_qty': 0
}
cm.update_member_points(data)
data = {
'name': "Forcer l'entrée en délai",
'shift_id': False,
'type': "ftop",
'partner_id': member_id,
'point_qty': 0
}
cm.update_member_points(data)
res["force_entry_delay"] = True
except Exception as e:
print(str(e))
else:
coop_logger.error("regenerate_member_delay: %s, %s", str(res["create_delay"]), str(data))
return HttpResponseServerError()
res["member_data"] = CagetteMembers.get_makeups_members([member_id])[0]
response = JsonResponse(res, safe=False)
else:
res["message"] = "Unauthorized"
response = JsonResponse(res, status=403)
return response
# --- Gestion des créneaux # --- Gestion des créneaux
...@@ -427,15 +500,16 @@ def delete_shift_registration(request): ...@@ -427,15 +500,16 @@ def delete_shift_registration(request):
member_id = int(data["member_id"]) member_id = int(data["member_id"])
shift_registration_id = int(data["shift_registration_id"]) shift_registration_id = int(data["shift_registration_id"])
shift_is_makeup = data["shift_is_makeup"] shift_is_makeup = data["shift_is_makeup"]
cancellation_description = data["cancellation_description"]
# Note: 'upcoming_registration_count' in res.partner won't change because the _compute method # Note: 'upcoming_registration_count' in res.partner won't change because the _compute method
# in odoo counts canceled shift registrations. # in odoo counts canceled shift registrations.
m = CagetteShift() m = CagetteShift()
res["cancel_shift"] = m.cancel_shift([shift_registration_id], origin='bdm') res["cancel_shift"] = m.cancel_shift([shift_registration_id], origin='bdm', description=cancellation_description)
if shift_is_makeup is True: if shift_is_makeup is True:
fields = { fields = {
'name': "Admin BDM - Suppression d'un rattrapage", 'name': "Admin BDM (annulation de rattrapage par une annulation de présence) - " + cancellation_description,
'shift_id': False, 'shift_id': False,
'type': data["member_shift_type"], 'type': data["member_shift_type"],
'partner_id': member_id, 'partner_id': member_id,
...@@ -508,15 +582,18 @@ def shift_subscription(request): ...@@ -508,15 +582,18 @@ def shift_subscription(request):
if shift_type == 1: if shift_type == 1:
# 1 = standard # 1 = standard
shift_template_id = int(data["shift_template_id"]) shift_template_id = int(data["shift_template_id"])
else: elif shift_type == 2:
# 2 = ftop # 2 = ftop
# First try to get committees shift # First try to get committees shift
shift_template_id = CagetteServices.get_committees_shift_id() shift_template_id = CagetteServices.get_committees_shift_id()
# If None, no committees shift, get the first ftop shift # If None, no committees shift, get the first ftop shift
if shift_template_id is None: if shift_template_id is None:
shift_template_id = CagetteServices.get_first_ftop_shift_id() shift_template_id = CagetteServices.get_first_ftop_shift_id()
else:
# 3 = exempté
# Get exemptions shift
shift_template_id = CagetteServices.get_exemptions_shift_id()
m = CagetteMember(partner_id) m = CagetteMember(partner_id)
...@@ -535,9 +612,10 @@ def shift_subscription(request): ...@@ -535,9 +612,10 @@ def shift_subscription(request):
) )
res["unsubscribe_member"] = m.unsubscribe_member(changing_shift = True) res["unsubscribe_member"] = m.unsubscribe_member(changing_shift = True)
m.create_coop_shift_subscription(shift_template_id, shift_type) reg_id = m.create_coop_shift_subscription(shift_template_id, shift_type)
# Return necessary data # Return necessary data
if reg_id is not None:
api = OdooAPI() api = OdooAPI()
c = [['id', '=', shift_template_id]] c = [['id', '=', shift_template_id]]
f = ['id', 'name'] f = ['id', 'name']
...@@ -546,9 +624,11 @@ def shift_subscription(request): ...@@ -546,9 +624,11 @@ def shift_subscription(request):
c = [['id', '=', partner_id]] c = [['id', '=', partner_id]]
f = ['cooperative_state'] f = ['cooperative_state']
res["cooperative_state"] = api.search_read('res.partner', c, f)[0]['cooperative_state'] res["cooperative_state"] = api.search_read('res.partner', c, f)[0]['cooperative_state']
coop_logger.info("Resultat shift_subscription : %s (données reçues = %s)", str(res), str(data))
response = JsonResponse(res) response = JsonResponse(res)
else: else:
response = JsonResponse({"message": "Subscription failed"}, status=500)
else:
response = JsonResponse({"message": "Unauthorized"}, status=403) response = JsonResponse({"message": "Unauthorized"}, status=403)
return response return response
......
...@@ -68,3 +68,12 @@ ...@@ -68,3 +68,12 @@
.btn_possible_member { .btn_possible_member {
margin: 0 1rem; margin: 0 1rem;
} }
.member_name {
font-weight: bold;
}
.title_center {
display: flex;
justify-content: center;
}
\ No newline at end of file
...@@ -29,7 +29,7 @@ video {max-width:none;} ...@@ -29,7 +29,7 @@ video {max-width:none;}
#webcam_button {min-width:115px; max-width:115px;} #webcam_button {min-width:115px; max-width:115px;}
#multi_results_preview button {margin:2px;} #multi_results_preview button {margin:2px;}
#member_slide {grid-gap:0;padding:5px;display:none;} #member_slide {grid-gap:0;padding:5px;display:none;}
#member_slide .coop-info {background: #fbfbd5;} #member_slide .coop-info {background: #449d44;}
#image_medium {width:128px;float:left;} #image_medium {width:128px;float:left;}
#image_medium:hover {cursor: url(/static/img/ip-camera.png), url(/static/img/ip-camera.svg) 5 5, pointer;} #image_medium:hover {cursor: url(/static/img/ip-camera.png), url(/static/img/ip-camera.svg) 5 5, pointer;}
#barcode {height:128px;} #barcode {height:128px;}
......
...@@ -284,6 +284,7 @@ function create_pair(payload) { ...@@ -284,6 +284,7 @@ function create_pair(payload) {
data.responseJSON.errors.map(function(error) { data.responseJSON.errors.map(function(error) {
message += ('\n' + error); message += ('\n' + error);
return null; return null;
}); });
} }
......
...@@ -56,7 +56,7 @@ function display_makeups_members() { ...@@ -56,7 +56,7 @@ function display_makeups_members() {
columns: [ columns: [
{ {
data: "id", data: "id",
title: '', title: "",
className: "dt-body-center", className: "dt-body-center",
orderable: false, orderable: false,
render: function (data) { render: function (data) {
...@@ -69,11 +69,30 @@ function display_makeups_members() { ...@@ -69,11 +69,30 @@ function display_makeups_members() {
title: "Nom" title: "Nom"
}, },
{ {
data: "shift_type", data: "id",
title: "Nb de points", title: "",
className: "dt-body-center", className: "dt-body-center",
orderable: false,
width: "10%", width: "10%",
render: function (data, type, row) { render: function (data, type, row) {
return `<button class="btn--primary extend_delay_button" data-member-id="${row.id}">Augmenter le délai</button>`;
}
},
{
data: "date_delay_stop",
title: "<div class='title_center'>Limite du délai</div>",
className: "dt-body-center",
width: "10%",
render: function (data) {
return (data === false) ? "Pas de délai en cours" : new Date(data).toLocaleDateString();
}
},
{
data: "shift_type",
title: "<div class='title_center'>Nb de points</div>",
className: "dt-body-center",
width: "5%",
render: function (data, type, row) {
if (data == 'ftop') { if (data == 'ftop') {
return row.display_ftop_points; return row.display_ftop_points;
} else if (data == 'standard') { } else if (data == 'standard') {
...@@ -85,7 +104,7 @@ function display_makeups_members() { ...@@ -85,7 +104,7 @@ function display_makeups_members() {
}, },
{ {
data: "makeups_to_do", data: "makeups_to_do",
title: "Nb rattrapages", title: "<div class='title_center'>Nb rattrapages</div>",
className: "dt-body-center", className: "dt-body-center",
width: "10%", width: "10%",
render: function (data, type, full) { render: function (data, type, full) {
...@@ -150,10 +169,18 @@ function display_makeups_members() { ...@@ -150,10 +169,18 @@ function display_makeups_members() {
const member = makeups_members.find(m => m.id == member_id); const member = makeups_members.find(m => m.id == member_id);
let modal_template = $("#modal_decr_makeup_counter");
modal_template.find(".member_name").text(member.name);
openModal( openModal(
`Enlever un rattrapage à ${member.name} ?`, modal_template.html(),
() => { () => {
update_members_makeups([member_id], "decrement"); update_members_makeups(
[member_id],
"decrement",
($("#decr-signature")[0].value || "auteur inconnu") + ' : ' + ($("#decr-explanation")[0].value || "pas d'explication")
);
}, },
"Confirmer", "Confirmer",
false false
...@@ -167,10 +194,18 @@ function display_makeups_members() { ...@@ -167,10 +194,18 @@ function display_makeups_members() {
const member = makeups_members.find(m => m.id == member_id); const member = makeups_members.find(m => m.id == member_id);
let modal_template = $("#modal_incr_makeup_counter");
modal_template.find(".member_name").text(member.name);
openModal( openModal(
`Ajouter un rattrapage à ${member.name} ?`, modal_template.html(),
() => { () => {
update_members_makeups([member_id], "increment"); update_members_makeups(
[member_id],
"increment",
($("#incr-signature")[0].value || "auteur inconnu") + ' : ' + ($("#incr-explanation")[0].value || "pas d'explication")
);
}, },
"Confirmer", "Confirmer",
false false
...@@ -198,9 +233,13 @@ function display_makeups_members() { ...@@ -198,9 +233,13 @@ function display_makeups_members() {
if (first_select) { if (first_select) {
$("#decrement_selected_members_makeups").on("click", () => { $("#decrement_selected_members_makeups").on("click", () => {
openModal( openModal(
`Enlever un rattrapage aux membres sélectionnés ?`, $("#modal_decr_selected_makeup_counter").html(),
() => { () => {
update_members_makeups(selected_rows, "decrement"); update_members_makeups(
selected_rows,
"decrement",
($("#decr-signature-selected")[0].value || "auteur inconnu") + ' : ' + ($("#decr-explanation-selected")[0].value || "pas d'explication")
);
}, },
"Confirmer", "Confirmer",
false false
...@@ -212,6 +251,24 @@ function display_makeups_members() { ...@@ -212,6 +251,24 @@ function display_makeups_members() {
.hide(); .hide();
} }
}); });
$('#makeups_members_table').on('click', 'tbody td .extend_delay_button', function () {
const member_id = this.dataset.memberId;
const member = makeups_members.find(m => m.id == member_id);
let modal = $("#modal_extend_delay_template");
modal.find(".member_name").text(member.name);
openModal(
modal.html(),
() => {
extend_member_delay(member);
},
"Confirmer",
false
);
});
} }
/** /**
...@@ -219,8 +276,9 @@ function display_makeups_members() { ...@@ -219,8 +276,9 @@ function display_makeups_members() {
* *
* @param {Array} member_ids * @param {Array} member_ids
* @param {String} action increment | decrement * @param {String} action increment | decrement
* @param description
*/ */
function update_members_makeups(member_ids, action) { function update_members_makeups(member_ids, action, description) {
openModal(); openModal();
data = []; data = [];
...@@ -255,7 +313,8 @@ function update_members_makeups(member_ids, action) { ...@@ -255,7 +313,8 @@ function update_members_makeups(member_ids, action) {
target_makeups_nb: makeups_members[member_index].makeups_to_do, target_makeups_nb: makeups_members[member_index].makeups_to_do,
member_shift_type: makeups_members[member_index].shift_type, member_shift_type: makeups_members[member_index].shift_type,
display_ftop_points: makeups_members[member_index].display_ftop_points, display_ftop_points: makeups_members[member_index].display_ftop_points,
display_std_points: makeups_members[member_index].display_std_points display_std_points: makeups_members[member_index].display_std_points,
description: description,
}); });
} }
...@@ -276,10 +335,56 @@ function update_members_makeups(member_ids, action) { ...@@ -276,10 +335,56 @@ function update_members_makeups(member_ids, action) {
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') { if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') {
err.msg += ' : ' + data.responseJSON.error; err.msg += ' : ' + data.responseJSON.error;
} }
report_JS_error(err, 'members_admin'); report_JS_error(err, 'members_admin-manage_makeups');
closeModal();
alert('Erreur serveur pour décrémenter les rattrapages. Veuillez contacer le service informatique.');
}
});
}
/**
* Send request to extend the member's delay, or create one if none open.
* @param {Object} member
*/
function extend_member_delay(member) {
openModal();
let today = new Date();
let today_plus_extension_duration = new Date();
today_plus_extension_duration.setMonth(today_plus_extension_duration.getMonth()+ extension_duration);
const diff_time = Math.abs(today_plus_extension_duration - today);
const diff_days = Math.ceil(diff_time / (1000 * 60 * 60 * 24));
let data = {
member_id: member.id,
duration: diff_days
};
$.ajax({
type: 'POST',
url: "/members/admin/regenerate_member_delay",
data: JSON.stringify(data),
dataType:"json",
traditional: true,
contentType: "application/json; charset=utf-8",
success: function(result) {
let i = makeups_members.findIndex(m => m.id == result.member_data.id);
makeups_members[i].date_delay_stop = result.member_data.date_delay_stop;
display_makeups_members();
closeModal();
},
error: function(data) {
err = {msg: "erreur serveur pour créer un délai", ctx: 'extend_member_delay'};
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') {
err.msg += ' : ' + data.responseJSON.error;
}
report_JS_error(err, 'members_admin-manage_makeups');
closeModal(); closeModal();
alert('Erreur serveur pour décrémenter les rattrapages. Ré-essayez plus tard.'); alert('Erreur serveur pour créer un délai. Veuillez contacer le service informatique.');
} }
}); });
} }
...@@ -331,10 +436,18 @@ function display_possible_members() { ...@@ -331,10 +436,18 @@ function display_possible_members() {
display_ftop_points: member.display_ftop_points display_ftop_points: member.display_ftop_points
}); });
let modal_template = $("#modal_incr_makeup_counter");
modal_template.find(".member_name").text(member.name);
openModal( openModal(
`Ajouter un rattrapage à ${member.name} ?`, modal_template.html(),
() => { () => {
update_members_makeups([member.id], "increment"); update_members_makeups(
[member.id],
"increment",
($("#incr-signature")[0].value || "auteur inconnu") + ' : ' + ($("#incr-explanation")[0].value || "pas d'explication")
);
members_search_results = []; members_search_results = [];
$('#search_member_input').val(''); $('#search_member_input').val('');
$('.search_member_results_area').hide(); $('.search_member_results_area').hide();
......
...@@ -37,7 +37,12 @@ function remove_from_shift_template() { ...@@ -37,7 +37,12 @@ function remove_from_shift_template() {
display_member_info(); display_member_info();
closeModal(); closeModal();
}, },
error: function() { error: function(err_data) {
if (err_data.status == 403) {
enqueue_message_for_next_loading("Problème d'autorisation. Merci de vous réidentifier.");
location.reload();
} else {
err = { err = {
msg: "erreur serveur lors de la suppression du membre du créneau", msg: "erreur serveur lors de la suppression du membre du créneau",
ctx: 'members.admin.manage_regular_shifts.remove_from_shift_template' ctx: 'members.admin.manage_regular_shifts.remove_from_shift_template'
...@@ -50,6 +55,8 @@ function remove_from_shift_template() { ...@@ -50,6 +55,8 @@ function remove_from_shift_template() {
className: "error" className: "error"
}); });
} }
}
}); });
} }
...@@ -57,7 +64,7 @@ function remove_from_shift_template() { ...@@ -57,7 +64,7 @@ function remove_from_shift_template() {
* Send the request to register a member to a shift template. * Send the request to register a member to a shift template.
* Ask to unsuscribe first if the member was subscribed. * Ask to unsuscribe first if the member was subscribed.
* *
* @param {int} shift_type 1 === standard ; 2 === ftop * @param {int} shift_type 1 === standard ; 2 === ftop ; 3 === exempté
* @param {int} shift_template_id null for ftop shift type * @param {int} shift_template_id null for ftop shift type
* @param {String} shift_template_name selected shift template name * @param {String} shift_template_name selected shift template name
*/ */
...@@ -115,6 +122,10 @@ function shift_subscrition(shift_type, shift_template_id = null, shift_template_ ...@@ -115,6 +122,10 @@ function shift_subscrition(shift_type, shift_template_id = null, shift_template_
true, true,
false false
); );
} else if (err_data.status == 403) {
enqueue_message_for_next_loading("Problème d'autorisation. Merci de vous réidentifier.");
location.reload();
} else { } else {
err = { err = {
msg: "erreur serveur lors de l'inscription du membre au créneau", msg: "erreur serveur lors de l'inscription du membre au créneau",
...@@ -185,18 +196,15 @@ function display_member_info() { ...@@ -185,18 +196,15 @@ function display_member_info() {
* Set calendar and associated listeners. * Set calendar and associated listeners.
*/ */
function set_subscription_area() { function set_subscription_area() {
retrieve_and_draw_shift_tempates(); retrieve_and_draw_shift_tempates({shift_listener: false});
$("#shifts_calendar_area").show(); $("#shifts_calendar_area").show();
// Wait for listeners to be set in common.js
// TODO use "signals" to avoid waiting an arbitrary time
setTimeout(() => {
// Cancel listeners from subscription page & set custom listeners // Cancel listeners from subscription page & set custom listeners
$("#shifts_calendar_area button[data-select='Volant']").off("click"); $(document).off("click", "#shifts_calendar_area button[data-select='Volant']");
$("#shifts_calendar_area button[data-select='Volant']").on("click", function() { $(document).on("click", "#shifts_calendar_area button[data-select='Volant']", function() {
// Subscribe to comitee/ftop shift // Subscribe to comitee/ftop shift
msg = (has_committe_shift === "True") msg = (has_committe_shift === "True")
? `Inscrire ${selected_member.name} au service des Comités ?` ? `Inscrire ${selected_member.name} au ${committees_shift_name} ?`
: `Inscrire ${selected_member.name} en Volant ?`; : `Inscrire ${selected_member.name} en Volant ?`;
openModal( openModal(
...@@ -209,8 +217,23 @@ function set_subscription_area() { ...@@ -209,8 +217,23 @@ function set_subscription_area() {
); );
}); });
$(".shift").off("click"); $(document).off("click", "#shifts_calendar_area button[data-select='Exemption']");
$(".shift").on("click", function() { $(document).on("click", "#shifts_calendar_area button[data-select='Exemption']", function() {
// Subscribe to comitee/ftop shift
msg = `Inscrire ${selected_member.name} en Éxempté ?`;
openModal(
msg,
() => {
shift_subscrition(3);
},
"Confirmer",
false
);
});
$(document).off("click", ".shift");
$(document).on("click", ".shift", function() {
// Subscribe to shift template // Subscribe to shift template
let shift_template_id = select_shift_among_compact(null, this, false); // method from common.js let shift_template_id = select_shift_among_compact(null, this, false); // method from common.js
let shift_template_data = shift_templates[shift_template_id].data;// shift_templates: var from common.js let shift_template_data = shift_templates[shift_template_id].data;// shift_templates: var from common.js
...@@ -225,7 +248,7 @@ function set_subscription_area() { ...@@ -225,7 +248,7 @@ function set_subscription_area() {
false false
); );
}); });
}, 1000);
} }
/** /**
...@@ -280,12 +303,12 @@ function display_possible_members() { ...@@ -280,12 +303,12 @@ function display_possible_members() {
$(document).ready(function() { $(document).ready(function() {
if (coop_is_connected()) { if (coop_is_connected()) {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } }); $.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
dbc = new PouchDB(couchdb_dbname); dbc = new PouchDB(couchdb_server);
$(".page_content").show(); $(".page_content").show();
if (has_committe_shift === "True") { if (has_committe_shift === "True") {
$("#shifts_calendar_area button[data-select='Volant']").text("Comités"); $("#shifts_calendar_area button[data-select='Volant']").text(committees_shift_name);
} }
// Set action to search for the member // Set action to search for the member
......
...@@ -102,16 +102,21 @@ function display_member_shifts() { ...@@ -102,16 +102,21 @@ function display_member_shifts() {
const shift_reg_id = row_data.id; const shift_reg_id = row_data.id;
const shift_is_makeup = row_data.is_makeup; 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>`; let modal_template = $("#modal_delete_shift_registration");
modal_template.find(".member_name").text(member.name);
modal_template.find(".service_name").text(row_data.shift_id[1]);
if (shift_is_makeup === true) { 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>`; modal_template.find("#makeup_case_explanation").show();
} }
openModal( openModal(
msg, $("#modal_delete_shift_registration").html(),
() => { () => {
delete_shift_registration(shift_reg_id, shift_is_makeup); delete_shift_registration(
shift_reg_id,
shift_is_makeup,
($("#cancellation-explanation")[0].value || "pas d'explication") + ' : ' + ($("#cancellation-signature")[0].value || "auteur inconnu"),
);
}, },
"Confirmer", "Confirmer",
false false
...@@ -123,15 +128,17 @@ function display_member_shifts() { ...@@ -123,15 +128,17 @@ function display_member_shifts() {
* Send request to delete shift registration * Send request to delete shift registration
* @param {Int} shift_reg_id Id of the shift_registration to delete * @param {Int} shift_reg_id Id of the shift_registration to delete
* @param {Boolean} shift_is_makeup Is the shift a makeup? * @param {Boolean} shift_is_makeup Is the shift a makeup?
* @param {String} description explanation and signature for the cancellation from bdm
*/ */
function delete_shift_registration(shift_reg_id, shift_is_makeup) { function delete_shift_registration(shift_reg_id, shift_is_makeup, description) {
openModal(); openModal();
data = { data = {
member_id: selected_member.id, member_id: selected_member.id,
member_shift_type: selected_member.shift_type, member_shift_type: selected_member.shift_type,
shift_registration_id: shift_reg_id, shift_registration_id: shift_reg_id,
shift_is_makeup: shift_is_makeup shift_is_makeup: shift_is_makeup,
cancellation_description: description
}; };
$.ajax({ $.ajax({
...@@ -231,7 +238,7 @@ $(document).ready(function() { ...@@ -231,7 +238,7 @@ $(document).ready(function() {
let search_str = $('#search_member_input').val(); let search_str = $('#search_member_input').val();
$.ajax({ $.ajax({
url: '/members/search/' + search_str, url: `/members/search/${search_str}?search_type=manage_shift_registrations`,
dataType : 'json', dataType : 'json',
success: function(data) { success: function(data) {
members_search_results = []; members_search_results = [];
......
...@@ -38,7 +38,7 @@ sync.on('change', function (info) { ...@@ -38,7 +38,7 @@ sync.on('change', function (info) {
}); });
if (need_reload == true) { if (need_reload == true) {
//On recharge depuis Odoo et on traite les enregistrements depuis CouchDB //On recharge depuis Odoo et on traite les enregistrements depuis CouchDB
retrieve_and_draw_shift_tempates('without_modal'); retrieve_and_draw_shift_tempates({without_modal: true, shift_listener: true});
} }
} }
...@@ -92,12 +92,12 @@ function new_coop_validation() { ...@@ -92,12 +92,12 @@ function new_coop_validation() {
coop_registration_details.find('.shift_template').text(st); coop_registration_details.find('.shift_template').text(st);
process_state.html(current_coop.firstname + ' ' +current_coop.lastname); process_state.html(current_coop.firstname + ' ' +current_coop.lastname);
coop_registration_details.find("#parentName").text("") coop_registration_details.find("#parentName").text("");
coop_registration_details.find("#parent").attr("hidden", true) coop_registration_details.find("#parent").attr("hidden", true);
if (current_coop.parent_name !== undefined) { if (current_coop.parent_name !== undefined) {
coop_registration_details.find("#parentName").text(current_coop.parent_name) coop_registration_details.find("#parentName").text(current_coop.parent_name);
coop_registration_details.find("#parent").removeAttr("hidden") coop_registration_details.find("#parent").removeAttr("hidden");
} }
if (current_coop.shift_template.data && current_coop.shift_template.data.id != ASSOCIATE_MEMBER_SHIFT) { if (current_coop.shift_template.data && current_coop.shift_template.data.id != ASSOCIATE_MEMBER_SHIFT) {
...@@ -126,7 +126,8 @@ function create_new_coop() { ...@@ -126,7 +126,8 @@ function create_new_coop() {
$('.chosen_associate').html(""); $('.chosen_associate').html("");
$('.chosen_associate_area').hide(); $('.chosen_associate_area').hide();
$('.member_choice').removeClass('choice_active'); $('.member_choice').removeClass('choice_active');
$(".remove_binome_icon").on("click", hide_chosen_associate) $(".remove_binome_icon").on("click", hide_chosen_associate);
$('input[name="binome"]').prop('checked', false);
local_in_process = getLocalInProcess(); local_in_process = getLocalInProcess();
if (getLocalInProcess().length > 0) { if (getLocalInProcess().length > 0) {
empty_waiting_local_processes(); empty_waiting_local_processes();
...@@ -268,15 +269,35 @@ function store_new_coop(event) { ...@@ -268,15 +269,35 @@ function store_new_coop(event) {
} }
} }
if ($('#associate_area').is(':visible')) {
// If user choose yes for binome, a type of association must be selected
let associated_data_selected = false;
if (
($(active_asso_area[0]).attr('id') === "new_member_choice")
||
($(active_asso_area[0]).attr('id') === "existing_member_choice")
) {
associated_data_selected = true;
}
if (associated_data_selected === false) errors.push("Un des deux choix concernant la mise en binôme doit être sélectionné");
}
if (active_asso_area.length > 0) { if (active_asso_area.length > 0) {
// If user click as if a "binôme" is beeing created, data about parent member must exist // If user click as if a "binôme" is beeing created, data about parent member must exist
let associated_data_ok = false; let associated_data_ok = false;
if ( if (
($(active_asso_area[0]).attr('id') === "new_member_choice" && $('#new_member_input').val().trim().length > 0) ($(active_asso_area[0]).attr('id') === "new_member_choice" && $('#new_member_input').val()
.trim().length > 0)
|| ||
($(active_asso_area[0]).attr('id') === "existing_member_choice" && $('#existing_member_choice_action .chosen_associate div.member').length > 0) ($(active_asso_area[0]).attr('id') === "existing_member_choice" && $('#existing_member_choice_action .chosen_associate div.member').length > 0)
) { ) {
associated_data_ok = true; associated_data_ok = true;
} else if ($(active_asso_area[0]).attr('id') === "") {
associated_data_ok = false;
errors.push("Un des deux choix doit être sélectionné");
} }
if (associated_data_ok === false) errors.push("Le membre 'titulaire' du binôme n'est pas défini"); if (associated_data_ok === false) errors.push("Le membre 'titulaire' du binôme n'est pas défini");
} }
...@@ -361,6 +382,7 @@ function modify_current_coop() { ...@@ -361,6 +382,7 @@ function modify_current_coop() {
$('#new_member_choice_action').hide(); $('#new_member_choice_action').hide();
$('#existing_member_choice').addClass('choice_active'); $('#existing_member_choice').addClass('choice_active');
var member_button = '<div>' + current_coop.parent_name + '</div>'; var member_button = '<div>' + current_coop.parent_name + '</div>';
$('.chosen_associate').html(member_button); $('.chosen_associate').html(member_button);
$('.chosen_associate_area').show(); $('.chosen_associate_area').show();
associated_old_choice = 'existing_member_choice'; associated_old_choice = 'existing_member_choice';
...@@ -608,8 +630,7 @@ $('#coop_create').submit(store_new_coop); ...@@ -608,8 +630,7 @@ $('#coop_create').submit(store_new_coop);
$('#generate_email').click(generate_email); $('#generate_email').click(generate_email);
$('#odoo_user_connect').click(); $('#odoo_user_connect').click();
$('#add_binome').click(function() { $('#no_binome').click(function() {
if ($('#associate_area').is(':visible')) {
$('#associate_area').hide(); $('#associate_area').hide();
$('#new_member_input').val(''); $('#new_member_input').val('');
$('#associate_area .choice_active').removeClass("choice_active"); $('#associate_area .choice_active').removeClass("choice_active");
...@@ -620,13 +641,15 @@ $('#add_binome').click(function() { ...@@ -620,13 +641,15 @@ $('#add_binome').click(function() {
delete current_coop.is_associated_people; delete current_coop.is_associated_people;
delete current_coop.shift_template; delete current_coop.shift_template;
} }
} else { });
$('#add_binome').click(function() {
$('#associate_area').show(); $('#associate_area').show();
$('.member_choice').removeClass('choice_active'); $('.member_choice').removeClass('choice_active');
$('#existing_member_choice_action').hide(); $('#existing_member_choice_action').hide();
$('#new_member_choice_action').hide(); $('#new_member_choice_action').hide();
associated_old_choice = null; associated_old_choice = null;
}
}); });
$('.member_choice').on('click', function() { $('.member_choice').on('click', function() {
......
...@@ -75,7 +75,7 @@ var reset_shift_process_actions_zone = function() { ...@@ -75,7 +75,7 @@ var reset_shift_process_actions_zone = function() {
current_shift_process_data_actions.off('click', 'a'); current_shift_process_data_actions.off('click', 'a');
current_shift_process_data_actions.hide(); current_shift_process_data_actions.hide();
current_shift_process_data_actions.empty(); current_shift_process_data_actions.empty();
} };
function fill_member_slide(member) { function fill_member_slide(member) {
no_pict_msg.hide(); no_pict_msg.hide();
...@@ -101,18 +101,22 @@ function fill_member_slide(member) { ...@@ -101,18 +101,22 @@ function fill_member_slide(member) {
} }
html_elts.image_medium.html('<img src="'+img_src+'" width="128" />'); html_elts.image_medium.html('<img src="'+img_src+'" width="128" />');
html_elts.cooperative_state.html(member.cooperative_state); html_elts.cooperative_state.html(member.cooperative_state);
if (member.cooperative_state == 'Rattrapage') { if (member.cooperative_state == 'Suspendu(e)') {
var explanation = "Tu as dû manquer un service! Pour pouvoir faire tes courses aujourd'hui, tu dois d'abord sélectionner un rattrapage sur ton espace membre."; var explanation = "Tu as dû manquer un service! Pour pouvoir faire tes courses aujourd'hui, tu dois d'abord sélectionner un rattrapage sur ton espace membre.";
html_elts.status_explanation.html(explanation); html_elts.status_explanation.html(explanation);
} }
if (member.cooperative_state == 'Désinscrit(e)') coop_info.addClass('b_red');
else if (member.cooperative_state == 'En alerte' || member.cooperative_state == 'Délai accordé' || member.cooperative_state == 'Rattrapage') coop_info.addClass('b_orange'); if (member.cooperative_state == 'Désinscrit(e)' || member.cooperative_state == 'Parti(e)' || member.cooperative_state == 'Rattrapage' || member.cooperative_state == 'Suspendu(e)') {
coop_info.addClass('b_red');
}
else if (member.cooperative_state == 'En alerte' || member.cooperative_state == 'Délai accordé') {
coop_info.addClass('b_yellow')
}
if (member.shifts.length > 0) { if (member.shifts.length > 0) {
html_elts.next_shifts.append('Prochains services : '); html_elts.next_shifts.append('Prochains services : ');
var slist = $('<ul>'); var slist = $('<ul>');
for (i in member.shifts) { for (i in member.shifts) {
var s = $('<li>').text(member.shifts[i].start); var s = $('<li>').text(member.shifts[i].start);
...@@ -330,12 +334,13 @@ function fill_service_entry(s) { ...@@ -330,12 +334,13 @@ function fill_service_entry(s) {
if (s.state == 'draft' || s.state == 'confirm') { if (s.state == 'draft' || s.state == 'confirm') {
let btn = $('<a>').addClass('btn btn--primary txtcenter') let btn = $('<a>').addClass('btn btn--primary txtcenter')
.text('Enregistrer les absences / présences') .text('Enregistrer les absences / présences')
.attr('id','record_shift_absences'); .attr('id', 'record_shift_absences');
current_shift_process_data_actions.append(btn); current_shift_process_data_actions.append(btn);
current_shift_process_data_actions.on('click', '#record_shift_absences', function(){ 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>"; msg = "<p>Lancer le traitement des présences et absences de ce service</p>";
openModal(msg, function() { openModal(msg, function() {
btn.attr('disabled', 'true') btn.attr('disabled', 'true');
try { try {
$.ajax({ $.ajax({
url: '/members/record_shift_absences/' + s.id, url: '/members/record_shift_absences/' + s.id,
......
...@@ -100,13 +100,11 @@ function display_current_coop_form() { ...@@ -100,13 +100,11 @@ function display_current_coop_form() {
$('#checks').show(); $('#checks').show();
for (var i = 1; i <= current_coop.checks_nb; i++) { for (var i = 1; i <= current_coop.checks_nb; i++) {
$(check_details).append('<p>Chèque #' + i +' : <input type="text" name="check_' + i + '" class="b_green check_item" required/> € </p>'); $(check_details).append('<p>Chèque #' + i + ' : <input type="text" name="check_' + i + '" class="b_green check_item" required/> € </p>');
} }
} }
} }
var show_change_shift = false;
if (current_coop.shift_template) { if (current_coop.shift_template) {
var st = current_coop.shift_template.data; var st = current_coop.shift_template.data;
...@@ -121,25 +119,20 @@ function display_current_coop_form() { ...@@ -121,25 +119,20 @@ function display_current_coop_form() {
place = 'Bureau'; place = 'Bureau';
} }
form.find('[name="place"]').val(place); form.find('[name="place"]').val(place);
if (current_coop.coop_msg) {
show_change_shift = true;
}
} else {
show_change_shift = true;
} }
let show_change_shift = current_coop.validation_state === 'waiting_validation_employee';
if (show_change_shift == true) { if (show_change_shift == true) {
chgt_shift_btn.show(); chgt_shift_btn.show();
chgt_shift_btn.on('click', open_shift_choice); chgt_shift_btn.on('click', open_shift_choice);
} }
if (typeof(coop_page) != "undefined") { if (typeof (coop_page) != "undefined") {
coop_page.show(); coop_page.show();
} }
} }
$('#payment_meaning').change(function() { $('#payment_meaning').change(function () {
if ($(this).val() == 'ch') { if ($(this).val() == 'ch') {
show_checks_nb(); show_checks_nb();
} else { } else {
......
...@@ -66,6 +66,7 @@ urlpatterns = [ ...@@ -66,6 +66,7 @@ urlpatterns = [
url(r'^delete_shift_template_registration$', admin.delete_shift_template_registration), url(r'^delete_shift_template_registration$', admin.delete_shift_template_registration),
url(r'^shift_subscription$', admin.shift_subscription), url(r'^shift_subscription$', admin.shift_subscription),
url(r'^admin/manage_attached$', admin.manage_attached), url(r'^admin/manage_attached$', admin.manage_attached),
url(r'^admin/regenerate_member_delay$', admin.regenerate_member_delay),
url(r'^admin/manage_attached/create_pair$', admin.create_pair), url(r'^admin/manage_attached/create_pair$', admin.create_pair),
url(r'^admin/manage_attached/delete_pair$', admin.delete_pair), url(r'^admin/manage_attached/delete_pair$', admin.delete_pair),
url(r'^get_makeups_members$', admin.get_makeups_members), url(r'^get_makeups_members$', admin.get_makeups_members),
......
...@@ -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, CagetteService from shifts.models import CagetteServices, CagetteService
from outils.forms import GenericExportMonthForm from outils.forms import GenericExportMonthForm
import datetime import datetime
...@@ -48,6 +48,15 @@ def index(request): ...@@ -48,6 +48,15 @@ def index(request):
return HttpResponse("Le créneau des comités n'est pas configuré dans Odoo !") return HttpResponse("Le créneau des comités n'est pas configuré dans Odoo !")
else: else:
context['committees_shift_id'] = committees_shift_id context['committees_shift_id'] = committees_shift_id
if getattr(settings, 'USE_EXEMPTIONS_SHIFT_TEMPLATE', False) is True:
exemptions_shift_id = CagetteServices.get_exemptions_shift_id()
if exemptions_shift_id is None:
return HttpResponse("Le créneau des exemptions n'est pas configuré dans Odoo !")
else:
context['exemptions_shift_id'] = exemptions_shift_id
else:
context['exemptions_shift_id'] = 0
if 'no_picture_member_advice' in msettings: if 'no_picture_member_advice' in msettings:
if len(msettings['no_picture_member_advice']['value']) > 0: if len(msettings['no_picture_member_advice']['value']) > 0:
...@@ -88,6 +97,10 @@ def inscriptions(request, type=1): ...@@ -88,6 +97,10 @@ def inscriptions(request, type=1):
template = loader.get_template('members/inscriptions.html') template = loader.get_template('members/inscriptions.html')
committees_shift_id = CagetteServices.get_committees_shift_id() committees_shift_id = CagetteServices.get_committees_shift_id()
if getattr(settings, 'USE_EXEMPTIONS_SHIFT_TEMPLATE', False) is True:
exemptions_shift_id = CagetteServices.get_exemptions_shift_id()
else:
exemptions_shift_id = 0
context = { context = {
'type': type, 'title': 'Inscriptions', 'type': type, 'title': 'Inscriptions',
'couchdb_server': settings.COUCHDB['url'], 'couchdb_server': settings.COUCHDB['url'],
...@@ -108,6 +121,7 @@ def inscriptions(request, type=1): ...@@ -108,6 +121,7 @@ def inscriptions(request, type=1):
'can_create_binome': getattr(settings, 'CAN_CREATE_BINOME', True), '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,
'exemptions_shift_id': exemptions_shift_id,
} }
response = HttpResponse(template.render(context, request)) response = HttpResponse(template.render(context, request))
...@@ -124,6 +138,12 @@ def get_shift_templates_next_shift(request, id): ...@@ -124,6 +138,12 @@ def get_shift_templates_next_shift(request, id):
def prepa_odoo(request): def prepa_odoo(request):
"""Generate coop subscription form, to be fill by BDM.""" """Generate coop subscription form, to be fill by BDM."""
template = loader.get_template('members/prepa_odoo.html') template = loader.get_template('members/prepa_odoo.html')
committees_shift_id = CagetteServices.get_committees_shift_id()
if getattr(settings, 'USE_EXEMPTIONS_SHIFT_TEMPLATE', False) is True:
exemptions_shift_id = CagetteServices.get_exemptions_shift_id()
else:
exemptions_shift_id = 0
context = {'title': 'Préparation Odoo Inscriptions', context = {'title': 'Préparation Odoo Inscriptions',
'warning_placeholder': 'Par exemple, il manque un chèque', 'warning_placeholder': 'Par exemple, il manque un chèque',
'couchdb_server': settings.COUCHDB['url'], 'couchdb_server': settings.COUCHDB['url'],
...@@ -137,7 +157,10 @@ def prepa_odoo(request): ...@@ -137,7 +157,10 @@ def prepa_odoo(request):
'ask_for_street2': getattr(settings, 'SUBSCRIPTION_ADD_STREET2', False), 'ask_for_street2': getattr(settings, 'SUBSCRIPTION_ADD_STREET2', False),
'ask_for_second_phone': getattr(settings, 'SUBSCRIPTION_ADD_SECOND_PHONE', False), 'ask_for_second_phone': getattr(settings, 'SUBSCRIPTION_ADD_SECOND_PHONE', False),
'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'],
'committees_shift_id': committees_shift_id,
'exemptions_shift_id': exemptions_shift_id,
}
# with_addr_complement # with_addr_complement
# with_second_phone # with_second_phone
...@@ -290,7 +313,8 @@ def record_service_presence(request): ...@@ -290,7 +313,8 @@ def record_service_presence(request):
stid = int(request.POST.get("stid", 0)) # shift_ticket_id stid = int(request.POST.get("stid", 0)) # shift_ticket_id
cancel = request.POST.get("cancel") == 'true' cancel = request.POST.get("cancel") == 'true'
typeAction = str(request.POST.get("type")) typeAction = str(request.POST.get("type"))
coop_logger.info("Enregistrement presence : mid = %s, rid = %s, sid = %s, stid = %s, cancel = %s, typeAction = %s",
str(mid), str(rid), str(sid), str(stid), str(cancel), typeAction)
app_env = getattr(settings, 'APP_ENV', "prod") app_env = getattr(settings, 'APP_ENV', "prod")
if (rid > -1 and mid > 0): if (rid > -1 and mid > 0):
overrided_date = "" overrided_date = ""
...@@ -312,6 +336,7 @@ def record_service_presence(request): ...@@ -312,6 +336,7 @@ def record_service_presence(request):
res['update'] = 'ok' res['update'] = 'ok'
else: else:
res['update'] = 'ko' res['update'] = 'ko'
coop_logger.info("Résultat update record_service_presence : %s", res['update'])
if res['update'] == 'ok': if res['update'] == 'ok':
members = CagetteMember.search('id', mid) members = CagetteMember.search('id', mid)
m = members[0] m = members[0]
...@@ -327,6 +352,7 @@ def record_service_presence(request): ...@@ -327,6 +352,7 @@ def record_service_presence(request):
except Exception as e: except Exception as e:
res['error'] = str(e) res['error'] = str(e)
coop_logger.error("Erreur record_service_presence : %s", str(e))
return JsonResponse({'res': res}) return JsonResponse({'res': res})
def easy_validate_shift_presence(request): def easy_validate_shift_presence(request):
......
from django.db import models from django.db import models
from outils.common_imports import * from outils.common_imports import *
from members.models import CagetteServices from shifts.models import CagetteServices
from outils.common import OdooAPI from outils.common import OdooAPI
...@@ -27,6 +27,13 @@ class CagetteMembersSpace(models.Model): ...@@ -27,6 +27,13 @@ class CagetteMembersSpace(models.Model):
answer = True answer = True
return answer return answer
def get_extension_duration(self):
"""Return nb of months"""
# TODO : add a unit parameter and convert if not month
extension_duration = OdooAPI().get_system_param('lacagette_membership.extension_duration')
nb, unit = extension_duration.split(' ')
return nb
def get_shifts_history(self, partner_id, limit, offset, date_from): def get_shifts_history(self, partner_id, limit, offset, date_from):
""" Get partner shifts history """ """ Get partner shifts history """
res = [] res = []
......
...@@ -87,6 +87,10 @@ body { ...@@ -87,6 +87,10 @@ body {
} }
} }
#comite_my_shifs_message {
min-width: 20em;
}
#home_incoming_services { #home_incoming_services {
min-height: 80px; min-height: 80px;
display: flex; display: flex;
...@@ -350,3 +354,11 @@ body { ...@@ -350,3 +354,11 @@ body {
.attached-unblocked { .attached-unblocked {
display: none; display: none;
} }
.block_service_exchange {
display: none;
}
.free_service_exchange {
display: none;
}
\ No newline at end of file
...@@ -18,6 +18,7 @@ function init_faq() { ...@@ -18,6 +18,7 @@ function init_faq() {
$(".ask_bdm_form_link").prop("href", request_form_link); $(".ask_bdm_form_link").prop("href", request_form_link);
display_messages_for_attached_people(); display_messages_for_attached_people();
display_messages_for_service_exchange_24h_before();
} }
$(document).on('click', "#shift_exchange_btn", () => { $(document).on('click', "#shift_exchange_btn", () => {
...@@ -46,3 +47,5 @@ function display_messages_for_attached_people() { ...@@ -46,3 +47,5 @@ function display_messages_for_attached_people() {
$(".attached-blocked").show(); $(".attached-blocked").show();
} }
} }
...@@ -4,8 +4,24 @@ function init_my_shifts_tile() { ...@@ -4,8 +4,24 @@ function init_my_shifts_tile() {
if (incoming_shifts.length === 0) { if (incoming_shifts.length === 0) {
$("#home_tile_my_services #home_incoming_services").text("Aucun service à venir..."); $("#home_tile_my_services #home_incoming_services").text("Aucun service à venir...");
} else { } else {
$("#home_tile_my_services #home_incoming_services").empty();
if (partner_data.comite === "True") {
let message = $('#comite_my_shifs_message').clone()
message.find('[data-type="nb_of_shifs_state"] [data-type="shifts_nb"]').text(partner_data.final_ftop_point)
if (Math.abs(partner_data.final_ftop_point) > 1) {
message.find('[data-type="nb_of_shifs_state"] [data-type="service_txt"]').text("services")
}
// let's get next ftop shift (incoming_shifts is ordered)
if (incoming_shifts.length > 0) {
const next_shift = incoming_shifts[0]
let ns_date = new Date(next_shift.date_begin)
const date_options = {dateStyle: "short"}
message.find('[data-type="next_ftop_shift_date"]').text(ns_date.toLocaleDateString('fr-FR', date_options))
}
$("#home_tile_my_services .tile_content").html(message)
} else {
$("#home_tile_my_services #home_incoming_services").empty();
let cpt = 0; let cpt = 0;
for (shift of incoming_shifts) { for (shift of incoming_shifts) {
...@@ -20,12 +36,13 @@ function init_my_shifts_tile() { ...@@ -20,12 +36,13 @@ function init_my_shifts_tile() {
} }
} }
} }
}
} }
function process_asked_shift_template_change(shift_t_id) { function process_asked_shift_template_change(shift_t_id) {
var s_data = shift_templates[shift_t_id].data; var s_data = shift_templates[shift_t_id].data;
var shift_name = get_shift_name(s_data); var shift_name = get_shift_name(s_data);
let msg = 'Inscription au créneau ' + shift_name let msg = 'Inscription au créneau ' + shift_name;
openModal( openModal(
msg, msg,
...@@ -94,8 +111,8 @@ function process_asked_shift_template_change(shift_t_id) { ...@@ -94,8 +111,8 @@ function process_asked_shift_template_change(shift_t_id) {
); );
} }
function edit_shift_template_registration(){ function edit_shift_template_registration() {
let external = true; const calendar_params = {external: true, without_modal: true, shift_listener: true};
if (calendar == null) calendar = $('#modal-calendar-choice').clone(); if (calendar == null) calendar = $('#modal-calendar-choice').clone();
if ($('#modal-calendar-choice').html().length > 0) { if ($('#modal-calendar-choice').html().length > 0) {
$('#modal-calendar-choice').empty(); $('#modal-calendar-choice').empty();
...@@ -103,8 +120,11 @@ function edit_shift_template_registration(){ ...@@ -103,8 +120,11 @@ function edit_shift_template_registration(){
} }
calendar.find('.oddeven_selector').empty(); calendar.find('.oddeven_selector').empty();
displayMsg(calendar.html()); displayMsg(calendar.html());
$('#week_types').find('input').change(()=>{filter_weeks(external)}); $('#week_types').find('input')
retrieve_and_draw_shift_tempates(external); .change(() => {
filter_weeks(calendar_params);
});
retrieve_and_draw_shift_tempates(calendar_params);
} }
...@@ -127,7 +147,8 @@ function init_home() { ...@@ -127,7 +147,8 @@ function init_home() {
}); });
$(".member_shift_name_area").on("click", ".fa-edit", (e) => { $(".member_shift_name_area").on("click", ".fa-edit", (e) => {
$('#week_types').find('input').change(filter_weeks); $('#week_types').find('input')
.change(filter_weeks);
e.preventDefault(); e.preventDefault();
edit_shift_template_registration(); edit_shift_template_registration();
}); });
...@@ -153,4 +174,6 @@ function init_home() { ...@@ -153,4 +174,6 @@ function init_home() {
load_partner_shifts(partner_data.concerned_partner_id) load_partner_shifts(partner_data.concerned_partner_id)
.then(init_my_shifts_tile); .then(init_my_shifts_tile);
} }
display_messages_for_service_exchange_24h_before();
} }
\ No newline at end of file
...@@ -177,7 +177,22 @@ function init_history() { ...@@ -177,7 +177,22 @@ function init_history() {
function init_incoming_shifts() { function init_incoming_shifts() {
$(".loading-incoming-shifts").hide(); $(".loading-incoming-shifts").hide();
$("#incoming_shifts").show(); $("#incoming_shifts").show();
if (partner_data.comite === "True") {
let message = $('#comite_my_shifs_message').clone()
message.find('[data-type="nb_of_shifs_state"] [data-type="shifts_nb"]').text(partner_data.final_ftop_point)
if (Math.abs(partner_data.final_ftop_point) > 1) {
message.find('[data-type="nb_of_shifs_state"] [data-type="service_txt"]').text("services")
}
// let's get next ftop shift (incoming_shifts is ordered)
if (incoming_shifts.length > 0) {
const next_shift = incoming_shifts[0]
let ns_date = new Date(next_shift.date_begin)
const date_options = {dateStyle: "short"}
message.find('[data-type="next_ftop_shift_date"]').text(ns_date.toLocaleDateString('fr-FR', date_options))
}
$("#incoming_shifts_area").html(message)
} else {
if (incoming_shifts.length === 0) { if (incoming_shifts.length === 0) {
$("#incoming_shifts").text("Aucun service à venir..."); $("#incoming_shifts").text("Aucun service à venir...");
} else { } else {
...@@ -211,6 +226,7 @@ function init_incoming_shifts() { ...@@ -211,6 +226,7 @@ function init_incoming_shifts() {
$("#incoming_shifts").append(shift_line_template.html()); $("#incoming_shifts").append(shift_line_template.html());
} }
} }
}
} }
function init_my_shifts() { function init_my_shifts() {
......
...@@ -119,12 +119,16 @@ function add_or_change_shift(new_shift_id) { ...@@ -119,12 +119,16 @@ function add_or_change_shift(new_shift_id) {
error: function(error) { error: function(error) {
closeModal(); closeModal();
selected_shift = null; selected_shift = null;
if (error.status === 400 && 'msg' in error.responseJSON && error.responseJSON.msg === "Old service in less than 24hours.") { if (error.status === 400 && 'msg' in error.responseJSON && error.responseJSON.msg === "Old service in less than 24hours.") {
alert(`Désolé ! Le service que tu souhaites échanger démarre dans moins de 24h. ` + alert(`Désolé ! Le service que tu souhaites échanger démarre dans moins de 24h. ` +
`Afin de faciliter la logistique des services, il n'est plus possible de l'échanger. ` + `Afin de faciliter la logistique des services, il n'est plus possible de l'échanger. ` +
`Si tu ne peux vraiment pas venir, tu seras noté.e absent.e à ton service. ` + `Si tu ne peux vraiment pas venir, tu seras noté.e absent.e à ton service. ` +
`Tu devras alors sélectionner un service de rattrapage sur ton espace membre.`); `Tu devras alors sélectionner un service de rattrapage sur ton espace membre.`);
} else if (error.status === 400 && 'msg' in error.responseJSON && error.responseJSON.msg === "Not allowed to change shift") {
alert(`Désolé ! Le service que tu souhaites échanger démarre dans trop peu de temps. ` +
`Afin de faciliter la logistique des services, il n'est plus possible de l'échanger. ` +
`Si tu ne peux vraiment pas venir, tu seras noté.e absent.e à ton service. ` +
`Tu devras alors sélectionner un service de rattrapage sur ton espace membre.`);
} else if (error.status === 500 && 'msg' in error.responseJSON && error.responseJSON.msg === "Fail to create shift") { } else if (error.status === 500 && 'msg' in error.responseJSON && error.responseJSON.msg === "Fail to create shift") {
// TODO differentiate error cases! // TODO differentiate error cases!
alert(`Une erreur est survenue. ` + alert(`Une erreur est survenue. ` +
...@@ -459,7 +463,7 @@ function init_shifts_list() { ...@@ -459,7 +463,7 @@ function init_shifts_list() {
*/ */
function init_calendar_page() { function init_calendar_page() {
let template_explanations = $("#calendar_explaination_template"); let template_explanations = $("#calendar_explaination_template");
let event_src = '/shifts/get_list_shift_calendar/' + partner_data.concerned_partner_id;
if (vw <= 992) { if (vw <= 992) {
$(".loading-calendar").show(); $(".loading-calendar").show();
...@@ -551,7 +555,7 @@ function init_calendar_page() { ...@@ -551,7 +555,7 @@ function init_calendar_page() {
contentHeight: "auto", contentHeight: "auto",
eventDisplay: "block", eventDisplay: "block",
hiddenDays: hidden_days, hiddenDays: hidden_days,
events: '/shifts/get_list_shift_calendar/' + partner_data.concerned_partner_id, events: event_src,
eventClick: function(info) { eventClick: function(info) {
if (!$(info.el).hasClass("shift_booked") && !$(info.el).hasClass("shift_booked_makeup")) { if (!$(info.el).hasClass("shift_booked") && !$(info.el).hasClass("shift_booked_makeup")) {
const new_shift_id = info.event.id; const new_shift_id = info.event.id;
...@@ -594,6 +598,7 @@ function init_calendar_page() { ...@@ -594,6 +598,7 @@ function init_calendar_page() {
} else { } else {
// Display modal // Display modal
let modal_template = $("#modal_add_shift_template"); let modal_template = $("#modal_add_shift_template");
modal_template.find(".date_new_shift").text(new_shift_date); modal_template.find(".date_new_shift").text(new_shift_date);
modal_template.find(".time_new_shift").text(new_shift_time); modal_template.find(".time_new_shift").text(new_shift_time);
openModal( openModal(
...@@ -607,12 +612,12 @@ function init_calendar_page() { ...@@ -607,12 +612,12 @@ function init_calendar_page() {
} 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 extension end
if (partner_data.date_delay_stop !== 'False') { if (partner_data.date_delay_stop !== 'False') {
date_partner_delay_stop = new Date(partner_data.date_delay_stop); date_partner_delay_stop = new Date(partner_data.date_delay_stop);
if (datetime_new_shift > date_partner_delay_stop) { if (datetime_new_shift > date_partner_delay_stop) {
let msg = `Vous avez jusqu'au ${date_partner_delay_stop.toLocaleDateString("fr-fr", date_options)} ` + let msg = `Vous avez jusqu'au ${date_partner_delay_stop.toLocaleDateString("fr-fr", date_options)} ` +
`pour sélectionner un rattrapage (soit une période de 6 mois depuis votre absence).`; `pour sélectionner un rattrapage (soit une période de ${extension_duration} mois depuis votre absence).`;
alert(msg); alert(msg);
...@@ -650,7 +655,7 @@ function init_calendar_page() { ...@@ -650,7 +655,7 @@ function init_calendar_page() {
calendar.render(); calendar.render();
} }
function init_read_only_calendar_page() { async function init_read_only_calendar_page() {
let template_explanations = $("#calendar_explaination_template"); let template_explanations = $("#calendar_explaination_template");
if (vw <= 992) { if (vw <= 992) {
...@@ -674,8 +679,8 @@ function init_read_only_calendar_page() { ...@@ -674,8 +679,8 @@ function init_read_only_calendar_page() {
if (incoming_shifts !== null) { if (incoming_shifts !== null) {
init_shifts_list(); init_shifts_list();
} else { } else {
load_partner_shifts(partner_data.concerned_partner_id) await load_partner_shifts(partner_data.concerned_partner_id)
.then(init_shifts_list); init_shifts_list();
} }
if (should_select_makeup()) { if (should_select_makeup()) {
...@@ -712,7 +717,16 @@ function init_read_only_calendar_page() { ...@@ -712,7 +717,16 @@ function init_read_only_calendar_page() {
const hidden_days = days_to_hide.length > 0 ? $.map(days_to_hide.split(", "), Number) : []; const hidden_days = days_to_hide.length > 0 ? $.map(days_to_hide.split(", "), Number) : [];
const calendarEl = document.getElementById('read_only_calendar'); const calendarEl = document.getElementById('read_only_calendar');
let event_src = '/shifts/get_list_shift_calendar/' + partner_data.concerned_partner_id;
if (partner_data.comite === "True") {
let next_evts = []
if (incoming_shifts.length > 0) {
incoming_shifts.forEach((s) => {
next_evts.push({id: s.id, title: 'Prélèvement 1 point', allDay: true, start: s.date_begin})
});
}
event_src = next_evts
}
calendar = new FullCalendar.Calendar(calendarEl, { calendar = new FullCalendar.Calendar(calendarEl, {
locale: 'fr', locale: 'fr',
initialView: default_initial_view, initialView: default_initial_view,
...@@ -728,7 +742,7 @@ function init_read_only_calendar_page() { ...@@ -728,7 +742,7 @@ function init_read_only_calendar_page() {
contentHeight: "auto", contentHeight: "auto",
eventDisplay: "block", eventDisplay: "block",
hiddenDays: hidden_days, hiddenDays: hidden_days,
events: '/shifts/get_list_shift_calendar/' + partner_data.concerned_partner_id, events: event_src,
eventDidMount: function() { eventDidMount: function() {
// Calendar is hidden at first on mobile to hide header change when data is loaded // Calendar is hidden at first on mobile to hide header change when data is loaded
$(".loading-calendar").hide(); $(".loading-calendar").hide();
...@@ -752,7 +766,8 @@ function init_delete_registration_buttons() { ...@@ -752,7 +766,8 @@ function init_delete_registration_buttons() {
if (partner_data.extra_shift_done > 0) { if (partner_data.extra_shift_done > 0) {
$(".delete_registration_button").on("click", function() { $(".delete_registration_button").on("click", function() {
let shift_name = $(this).closest("div") let shift_name = $(this).closest("div")
.parent().parent() .parent()
.parent()
.find(".shift_line_date") .find(".shift_line_date")
.text() .text()
.trim(); .trim();
......
...@@ -139,10 +139,10 @@ function request_delay() { ...@@ -139,10 +139,10 @@ function request_delay() {
const delay_start = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate(); const delay_start = today.getFullYear()+'-'+(today.getMonth()+1)+'-'+today.getDate();
let today_plus_six_month = new Date(); let today_plus_extension_duration = new Date();
today_plus_six_month.setMonth(today_plus_six_month.getMonth()+6); today_plus_extension_duration.setMonth(today_plus_extension_duration.getMonth()+ extension_duration);
const diff_time = Math.abs(today_plus_six_month - today); const diff_time = Math.abs(today_plus_extension_duration - today);
const diff_days = Math.ceil(diff_time / (1000 * 60 * 60 * 24)); const diff_days = Math.ceil(diff_time / (1000 * 60 * 60 * 24));
$.ajax({ $.ajax({
...@@ -157,7 +157,7 @@ function request_delay() { ...@@ -157,7 +157,7 @@ function request_delay() {
}, },
success: function() { success: function() {
partner_data.cooperative_state = 'delay'; 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(); partner_data.date_delay_stop = today_plus_extension_duration.getFullYear()+'-'+(today_plus_extension_duration.getMonth()+1)+'-'+today_plus_extension_duration.getDate();
resolve(); resolve();
}, },
...@@ -314,6 +314,7 @@ $(document).ready(function() { ...@@ -314,6 +314,7 @@ $(document).ready(function() {
// For associated people, their parent name is attached in their display name // For associated people, their parent name is attached in their display name
let partner_name_split = partner_data.name.split(', '); let partner_name_split = partner_data.name.split(', ');
partner_data.name = partner_name_split[partner_name_split.length - 1]; partner_data.name = partner_name_split[partner_name_split.length - 1];
base_location = (app_env === 'dev') ? '/members_space/' : '/'; base_location = (app_env === 'dev') ? '/members_space/' : '/';
...@@ -354,3 +355,11 @@ $(document).ready(function() { ...@@ -354,3 +355,11 @@ $(document).ready(function() {
}; };
})(jQuery, 'smartresize'); })(jQuery, 'smartresize');
function display_messages_for_service_exchange_24h_before() {
if (block_service_exchange_24h_before === "False") {
$(".free_service_exchange").show();
} else {
$(".block_service_exchange").show();
}
}
...@@ -29,7 +29,9 @@ def index(request, exception=None): ...@@ -29,7 +29,9 @@ def index(request, exception=None):
context = { context = {
'title': 'Espace Membre', 'title': 'Espace Membre',
'COMPANY_LOGO': getattr(settings, 'COMPANY_LOGO', None), 'COMPANY_LOGO': getattr(settings, 'COMPANY_LOGO', None),
'block_actions_for_attached_people' : getattr(settings, 'BLOCK_ACTIONS_FOR_ATTACHED_PEOPLE', True) 'block_actions_for_attached_people' : getattr(settings, 'BLOCK_ACTIONS_FOR_ATTACHED_PEOPLE', True),
'permanent_message': getattr(settings, 'PERMANENT_MESSAGE_BELOW_CONNECTION_FIELDS', None),
'block_service_exchange_24h_before' : getattr(settings, 'BLOCK_SERVICE_EXCHANGE_24H_BEFORE', True),
} }
template = loader.get_template('members_space/index.html') template = loader.get_template('members_space/index.html')
...@@ -119,6 +121,7 @@ def index(request, exception=None): ...@@ -119,6 +121,7 @@ def index(request, exception=None):
partnerData["associated_partner_name"] = str(associated_partner["barcode_base"]) + ' - ' + partnerData["associated_partner_name"] partnerData["associated_partner_name"] = str(associated_partner["barcode_base"]) + ' - ' + partnerData["associated_partner_name"]
m = CagetteMembersSpace() m = CagetteMembersSpace()
context['extension_duration'] = m.get_extension_duration()
context['show_faq'] = getattr(settings, 'MEMBERS_SPACE_FAQ_TEMPLATE', 'members_space/faq.html') context['show_faq'] = getattr(settings, 'MEMBERS_SPACE_FAQ_TEMPLATE', 'members_space/faq.html')
context['show_abcd_calendar'] = getattr(settings, 'SHOW_ABCD_CALENDAR_TAB', True) context['show_abcd_calendar'] = getattr(settings, 'SHOW_ABCD_CALENDAR_TAB', True)
partnerData["comite"] = m.is_comite(partner_id) partnerData["comite"] = m.is_comite(partner_id)
...@@ -159,6 +162,14 @@ def index(request, exception=None): ...@@ -159,6 +162,14 @@ def index(request, exception=None):
context['helper_unsubscribe_form_link'] = msettings['helper_unsubscribe_form_link']['value'] if 'helper_unsubscribe_form_link' in msettings else '' context['helper_unsubscribe_form_link'] = msettings['helper_unsubscribe_form_link']['value'] if 'helper_unsubscribe_form_link' in msettings else ''
context['covid_form_link'] = msettings['covid_form_link']['value'] if 'covid_form_link' in msettings else '' context['covid_form_link'] = msettings['covid_form_link']['value'] if 'covid_form_link' in msettings else ''
context['covid_end_form_link'] = msettings['covid_end_form_link']['value'] if 'covid_end_form_link' in msettings else '' context['covid_end_form_link'] = msettings['covid_end_form_link']['value'] if 'covid_end_form_link' in msettings else ''
if getattr(settings, 'USE_EXEMPTIONS_SHIFT_TEMPLATE', False) is True:
exemptions_shift_id = CagetteServices.get_exemptions_shift_id()
if exemptions_shift_id is None:
return HttpResponse("Le créneau des exemptions n'est pas configuré dans Odoo !")
else:
context['exemptions_shift_id'] = exemptions_shift_id
else:
context['exemptions_shift_id'] = 0
else: else:
# may arrive when switching database without cleaning cookie # may arrive when switching database without cleaning cookie
return redirect('/website/deconnect') return redirect('/website/deconnect')
...@@ -182,6 +193,9 @@ def home(request): ...@@ -182,6 +193,9 @@ def home(request):
partnerData = cs.get_data_partner(partner_id) partnerData = cs.get_data_partner(partner_id)
if partnerData['cooperative_state'] == "unsubscribed": if partnerData['cooperative_state'] == "unsubscribed":
coop_can_change_shift_template = False coop_can_change_shift_template = False
if getattr(settings, 'ASSOCIATE_PEOPLE_CAN_CHANGE_SHIFT_TEMPLE_REGISTRATION', False) is False:
if partnerData['is_associated_people'] is True:
coop_can_change_shift_template = False
context = { context = {
'title': 'Espace Membres', 'title': 'Espace Membres',
'coop_can_change_shift_template': coop_can_change_shift_template, 'coop_can_change_shift_template': coop_can_change_shift_template,
...@@ -216,9 +230,11 @@ def my_shifts(request): ...@@ -216,9 +230,11 @@ def my_shifts(request):
def shifts_exchange(request): def shifts_exchange(request):
""" Endpoint the front-end will call to load the "Shifts exchange" page. """ """ Endpoint the front-end will call to load the "Shifts exchange" page. """
template = loader.get_template('members_space/shifts_exchange.html') template = loader.get_template('members_space/shifts_exchange.html')
m = CagetteMembersSpace()
context = { context = {
'title': 'Échange de Services', 'title': 'Échange de Services',
'canAddShift': getattr(settings, 'CAN_ADD_SHIFT', False) 'canAddShift': getattr(settings, 'CAN_ADD_SHIFT', False),
'extension_duration': m.get_extension_duration()
} }
return HttpResponse(template.render(context, request)) return HttpResponse(template.render(context, request))
......
...@@ -48,7 +48,9 @@ class Order(models.Model): ...@@ -48,7 +48,9 @@ class Order(models.Model):
return result return result
def get_lines(self, forExport=False, withNullQty=False): def get_lines(self, forExport=False, withNullQty=False):
lines_data = {'lines': None, 'used_coeffs': None}
f = ['id', 'product_id', 'package_qty', 'product_qty_package', 'product_qty', 'product_uom', 'price_unit', 'partner_id'] f = ['id', 'product_id', 'package_qty', 'product_qty_package', 'product_qty', 'product_uom', 'price_unit', 'partner_id']
if forExport is True: if forExport is True:
f += ['discount', 'price_subtotal', 'price_tax', 'taxes_id'] f += ['discount', 'price_subtotal', 'price_tax', 'taxes_id']
...@@ -67,12 +69,15 @@ class Order(models.Model): ...@@ -67,12 +69,15 @@ class Order(models.Model):
f = ['barcode', 'product_tmpl_id', 'shelf_id'] f = ['barcode', 'product_tmpl_id', 'shelf_id']
if forExport is False: # i.e for reception if forExport is False: # i.e for reception
f += ['taxes_id', 'standard_price'] f += ['taxes_id', 'standard_price']
coeff = self.get_coop_main_coeff() # add the 9 product coeffs
for i in range(1,10):
f.append('coeff' + str(i) + '_id')
c = [['id', 'in', pids]] c = [['id', 'in', pids]]
res_bc = self.o_api.search_read('product.product', c, f) res_bc = self.o_api.search_read('product.product', c, f)
tmpl_ids = [] tmpl_ids = []
if res_bc: if res_bc:
taxes = {} taxes = {} # Needed to add tax coeff for each product
res_tax = self.get_taxes_data_for_lines(res_bc) res_tax = self.get_taxes_data_for_lines(res_bc)
if res_tax: if res_tax:
for tax in res_tax: for tax in res_tax:
...@@ -92,21 +97,26 @@ class Order(models.Model): ...@@ -92,21 +97,26 @@ class Order(models.Model):
if len(shelf_ids) > 0: if len(shelf_ids) > 0:
shelfs_sortorder = Shelfs.get_shelfs_sortorder(shelf_ids) shelfs_sortorder = Shelfs.get_shelfs_sortorder(shelf_ids)
found_coeffs_ids = [] # Extract from all products, to make a unique query after loop
for l in res_bc: for l in res_bc:
for p in res: for p in res:
if p['product_id'][0] == l['id']: if p['product_id'][0] == l['id']:
# coop_logger.info(str(l))
p['shelf_sortorder'] = 'X' p['shelf_sortorder'] = 'X'
p['barcode'] = l['barcode'] p['barcode'] = l['barcode']
p['product_tmpl_id'] = l['product_tmpl_id'][0] p['product_tmpl_id'] = l['product_tmpl_id'][0]
if ('standard_price' in l): if ('standard_price' in l):
p['p_price'] = l['standard_price'] p['p_price'] = l['standard_price']
p_coeff = None # Let's add product coeff order and id (needed to compute sale price in further operations : new_shelf_price for ex.)
try: for i in range(1,10):
tax_coeff = (1 + (float(taxes[str(l['taxes_id'][0])]))/100) if l['coeff' + str(i) + '_id'] is not False:
p_coeff = coeff * tax_coeff coeff_id = l['coeff' + str(i) + '_id'][0]
except Exception as e: p['coeff' + str(i) + '_id'] = coeff_id
coop_logger.warning('order get_lines : %s', str(e))
p['p_coeff'] = p_coeff if coeff_id not in found_coeffs_ids:
found_coeffs_ids.append(coeff_id)
p['tax_coeff'] = (1 + (float(taxes[str(l['taxes_id'][0])]))/100)
if l['shelf_id'] is not False: if l['shelf_id'] is not False:
for s in shelfs_sortorder: for s in shelfs_sortorder:
...@@ -116,6 +126,7 @@ class Order(models.Model): ...@@ -116,6 +126,7 @@ class Order(models.Model):
p['shelf_sortorder'] = 'X' p['shelf_sortorder'] = 'X'
tmpl_ids.append(l['product_tmpl_id'][0]) tmpl_ids.append(l['product_tmpl_id'][0])
used_coeffs = self.o_api.search_read('product.coefficient', [['id', 'in', found_coeffs_ids]], ['operation_type', 'value'])
# Adding indicative_package for every product # Adding indicative_package for every product
f = ['indicative_package','product_tmpl_id','product_code'] f = ['indicative_package','product_tmpl_id','product_code']
...@@ -133,8 +144,9 @@ class Order(models.Model): ...@@ -133,8 +144,9 @@ class Order(models.Model):
except Exception as e: except Exception as e:
# if product is not active, it is not included in res_bc result # if product is not active, it is not included in res_bc result
p['active'] = False p['active'] = False
lines_data['lines'] = res
return res lines_data['used_coeffs'] = used_coeffs
return lines_data
def get_taxes_data_for_lines(self, lines): def get_taxes_data_for_lines(self, lines):
taxes_id = [] taxes_id = []
...@@ -156,7 +168,8 @@ class Order(models.Model): ...@@ -156,7 +168,8 @@ class Order(models.Model):
c = [['id', '=', self.id]] c = [['id', '=', self.id]]
order = self.o_api.search_read('purchase.order', c, f) order = self.o_api.search_read('purchase.order', c, f)
if order: if order:
lines = self.get_lines(forExport=True) lines_data = self.get_lines(forExport=True)
lines = lines_data['lines']
res['taxes'] = self.get_taxes_data_for_lines(lines) res['taxes'] = self.get_taxes_data_for_lines(lines)
res['order'] = order[0] res['order'] = order[0]
res['lines'] = lines res['lines'] = lines
...@@ -214,7 +227,8 @@ class Order(models.Model): ...@@ -214,7 +227,8 @@ class Order(models.Model):
import re import re
fixed_prefix = getattr(settings, 'FIXED_BARCODE_PREFIX', '0490') fixed_prefix = getattr(settings, 'FIXED_BARCODE_PREFIX', '0490')
labels_data = {'total': 0, 'details': []} labels_data = {'total': 0, 'details': []}
lines = self.get_lines() lines_data = self.get_lines()
lines = lines_data['lines']
bc_pattern = re.compile('^' + fixed_prefix) bc_pattern = re.compile('^' + fixed_prefix)
for l in lines: for l in lines:
if ('barcode' in l) and not (bc_pattern.match(str(l['barcode'])) is None): if ('barcode' in l) and not (bc_pattern.match(str(l['barcode'])) is None):
...@@ -341,7 +355,8 @@ class Orders(models.Model): ...@@ -341,7 +355,8 @@ class Orders(models.Model):
try: try:
fixed_prefix = getattr(settings, 'FIXED_BARCODE_PREFIX', '0490') fixed_prefix = getattr(settings, 'FIXED_BARCODE_PREFIX', '0490')
bc_pattern = re.compile('^' + fixed_prefix) bc_pattern = re.compile('^' + fixed_prefix)
for l in Orders.get_lines(oids): lines_data = Orders.get_lines(oids)
for l in lines_data['lines']:
if not (bc_pattern.match(str(l['barcode'])) is None): if not (bc_pattern.match(str(l['barcode'])) is None):
if not (l['product_tmpl_id'] in labels_data): if not (l['product_tmpl_id'] in labels_data):
labels_data[l['product_tmpl_id']] = 0 labels_data[l['product_tmpl_id']] = 0
...@@ -351,6 +366,22 @@ class Orders(models.Model): ...@@ -351,6 +366,22 @@ class Orders(models.Model):
return labels_data return labels_data
@staticmethod
def get_orders_between_dates(date_from, date_to):
"""@depends on Odoo module lacagette_purchase"""
o_api = OdooAPI()
params = {'date_from': date_from, 'date_to': date_to}
res = {}
try:
res = o_api.execute('purchase.order', 'get_received_orders_between_dates', [], params)
except Exception as e:
res["error"] = str(e)
coop_logger.error('get_orders_between_dates : %s', str(e))
return res
class CagetteSuppliers(models.Model): class CagetteSuppliers(models.Model):
@staticmethod @staticmethod
......
...@@ -138,7 +138,7 @@ function debounceFunction(func, delay = 1000) { ...@@ -138,7 +138,7 @@ function debounceFunction(func, delay = 1000) {
*/ */
function handle_unauthorize() { function handle_unauthorize() {
alert("La session a expiré. Vous allez devoir vous reconnecter."); alert("La session a expiré. Vous allez devoir vous reconnecter.");
$( "#logout" ).trigger( "click" ); $("#logout").trigger("click");
} }
/* - PRODUCTS */ /* - PRODUCTS */
...@@ -284,9 +284,13 @@ function compute_purchase_qty_for_coverage(product, coeff, stock, incoming_qty, ...@@ -284,9 +284,13 @@ function compute_purchase_qty_for_coverage(product, coeff, stock, incoming_qty,
purchase_package_qty_for_coverage = 1; purchase_package_qty_for_coverage = 1;
} else { } else {
purchase_qty_for_coverage = days * daily_conso - stock - incoming_qty + product.minimal_stock; purchase_qty_for_coverage = days * daily_conso - stock - incoming_qty;
purchase_qty_for_coverage = (purchase_qty_for_coverage < 0) ? 0 : purchase_qty_for_coverage; purchase_qty_for_coverage = (purchase_qty_for_coverage < 0) ? 0 : purchase_qty_for_coverage;
if (purchase_qty_for_coverage + stock + incoming_qty < product.minimal_stock) {
purchase_qty_for_coverage = product.minimal_stock - stock - incoming_qty;
}
// Reduce to nb of packages to purchase // Reduce to nb of packages to purchase
purchase_package_qty_for_coverage = purchase_qty_for_coverage / product.suppliersinfo[0].package_qty; purchase_package_qty_for_coverage = purchase_qty_for_coverage / product.suppliersinfo[0].package_qty;
...@@ -314,6 +318,7 @@ function compute_and_affect_product_supplier_quantities(coeff, days) { ...@@ -314,6 +318,7 @@ function compute_and_affect_product_supplier_quantities(coeff, days) {
// Set qty to purchase for supplier with higher priority // Set qty to purchase for supplier with higher priority
let target_supplierinfo_index = 0; let target_supplierinfo_index = 0;
let min_sequence = Number.POSITIVE_INFINITY; // min sequence = higher priority let min_sequence = Number.POSITIVE_INFINITY; // min sequence = higher priority
for (let i in products[key].suppliersinfo) { for (let i in products[key].suppliersinfo) {
let suppliersinfo_sequence = products[key].suppliersinfo[i].sequence; let suppliersinfo_sequence = products[key].suppliersinfo[i].sequence;
...@@ -343,7 +348,9 @@ function compute_products_coverage_qties() { ...@@ -343,7 +348,9 @@ function compute_products_coverage_qties() {
order_doc.coeff = coeff; order_doc.coeff = coeff;
if (order_doc.coverage_days != null) { if (order_doc.coverage_days != null) {
compute_and_affect_product_supplier_quantities(coeff, order_doc.coverage_days); compute_and_affect_product_supplier_quantities(coeff, order_doc.coverage_days);
} else if (order_doc.targeted_amount != null) { }
if (order_doc.targeted_amount != null) {
const small_step = 0.1, const small_step = 0.1,
max_iter = 182; // Assume that no more than 1/2 year coverage is far enough max_iter = 182; // Assume that no more than 1/2 year coverage is far enough
let go_on = true, let go_on = true,
......
...@@ -20,7 +20,7 @@ def helper(request): ...@@ -20,7 +20,7 @@ def helper(request):
'title': 'Aide à la commande', 'title': 'Aide à la commande',
'couchdb_server': settings.COUCHDB['url'], 'couchdb_server': settings.COUCHDB['url'],
'db': settings.COUCHDB['dbs']['orders'], 'db': settings.COUCHDB['dbs']['orders'],
'odoo_server': settings.ODOO['url'], 'odoo_server': getattr(settings, 'ODOO_PUBLIC_URL', settings.ODOO['url']),
'metabase_url': getattr(settings, 'ORDERS_HELPER_METABASE_URL', ''), 'metabase_url': getattr(settings, 'ORDERS_HELPER_METABASE_URL', ''),
'nb_past_days_to_compute_sales_average': OdooAPI().get_system_param('lacagette_products.nb_past_days_to_compute_sales_average'), 'nb_past_days_to_compute_sales_average': OdooAPI().get_system_param('lacagette_products.nb_past_days_to_compute_sales_average'),
'nb_of_consecutive_non_sale_days_considered_as_break': OdooAPI().get_system_param('lacagette_products.nb_of_consecutive_non_sale_days_considered_as_break') 'nb_of_consecutive_non_sale_days_considered_as_break': OdooAPI().get_system_param('lacagette_products.nb_of_consecutive_non_sale_days_considered_as_break')
......
...@@ -133,7 +133,7 @@ ...@@ -133,7 +133,7 @@
- CAN_CREATE_BINOME = True (by default) - CAN_CREATE_BINOME = True (by default)
If set to False, in new member creation form, a member can be selected to be associated with. If set to True, in new member creation form, a member can be selected to be associated with.
- ASSOCIATE_MEMBER_SHIFT = '' - ASSOCIATE_MEMBER_SHIFT = ''
...@@ -346,6 +346,17 @@ ...@@ -346,6 +346,17 @@
By default, False. Set if coop can or not add shifts in their memberspace calendar By default, False. Set if coop can or not add shifts in their memberspace calendar
- ASSOCIATE_PEOPLE_CAN_CHANGE_SHIFT_TEMPLE_REGISTRATION = True
By default, False.
- EXTENSION_TYPE_ID = 6
By default 1. Extension type id (from shift_extension_type table) used to create extension
- COMMITTEES_SHIFT_NAME = 'Coolisses'
By default "service des Comités"
### Reception ### Reception
- RECEPTION_ADD_ADMIN_MODE = True - RECEPTION_ADD_ADMIN_MODE = True
...@@ -442,6 +453,17 @@ ...@@ -442,6 +453,17 @@
True by default True by default
- PERMANENT_MESSAGE_BELOW_CONNECTION_FIELDS = "Si vous avez des difficultés à vous connecter, ...."
Default is None
- STANDARD_BLOCK_SERVICE_EXCHANGE_DELAY = 36 (default = 24)
Define duration, in hours, before shift starts within exchange is not more available, for standard shift_type member
- REMOVE_15_MINUTES_AT_SHIFT_END = False
True by default. Remove 15 minutes to Odoo shift end (https://redmine.cooperatic.fr/issues/1680)
### BDM Admin ### BDM Admin
- BDM_SHOW_FTOP_BUTTON = True (by default) - BDM_SHOW_FTOP_BUTTON = True (by default)
......
.green {color:#60B000;} .green {color:#60B000;}
.b_green, .b_more_than_50pc {background:#d9ebd2} .b_green, .b_more_than_50pc {background:#d9ebd2 !important;}
.orange {color:#FFA500;} .orange {color:#FFA500;}
.b_orange, .b_less_than_50pc {background:#F0B000 !important;} .b_orange, .b_less_than_50pc {background:#F0B000 !important;}
.yellow {color:#F0B000;} .yellow {color:#F0B000;}
.b_yellow {background: #fcf3cc;} .b_yellow {background: #fcf3cc !important;}
.red {color:#FF0000;} .red {color:#FF0000;}
.b_red, .b_less_than_25pc {background:#ff3333 !important;} .b_red, .b_less_than_25pc {background:#ff3333 !important;}
.loading {background-image: url("/static/img/ajax-loader.gif"); background-repeat:no-repeat; background-position: center; background-color: #efefef;} .loading {background-image: url("/static/img/ajax-loader.gif"); background-repeat:no-repeat; background-position: center; background-color: #efefef;}
......
...@@ -21,6 +21,7 @@ var volant = null; ...@@ -21,6 +21,7 @@ var volant = null;
function get_displayed_weeks() { function get_displayed_weeks() {
displayed_weeks = []; displayed_weeks = [];
$('#week_types').find('.selected_weeks :checked').each(function() { $('#week_types').find('.selected_weeks :checked').each(function() {
...@@ -181,8 +182,11 @@ function draw_table(begin_hours, callback) { ...@@ -181,8 +182,11 @@ function draw_table(begin_hours, callback) {
} }
function draw_shift_templates(external) { function draw_shift_templates(params) {
if (typeof external !== "undefined" && external == true) shift_table = $('#shift_choice table'); if (params && typeof params.external !== "undefined" && params.external == true){
// Simplified calendar loaded in modal (members_space for ex.)
shift_table = $('#shift_choice table');
}
var existing_shifts = shift_table.find('.shift'); var existing_shifts = shift_table.find('.shift');
existing_shifts.off("click", single_shift_click); existing_shifts.off("click", single_shift_click);
...@@ -224,6 +228,8 @@ function draw_shift_templates(external) { ...@@ -224,6 +228,8 @@ function draw_shift_templates(external) {
}); });
draw_table(begin_hours, function() { draw_table(begin_hours, function() {
const is_inscription_page = $('body').hasClass('inscriptions');
$.each(shift_templates, function(i, e) { $.each(shift_templates, function(i, e) {
if (e.data) { if (e.data) {
var keep_it = false; var keep_it = false;
...@@ -291,8 +297,10 @@ function draw_shift_templates(external) { ...@@ -291,8 +297,10 @@ function draw_shift_templates(external) {
}); });
if (type == 1) { if (type == 1) {
if (!params || (typeof params.shift_listener !== "undefined" && params.shift_listener == true) || is_inscription_page == true){
shift_table.find('.shift').on("click", single_shift_click); shift_table.find('.shift').on("click", single_shift_click);
} }
}
if (type == 2) { if (type == 2) {
for (k in boxes) { for (k in boxes) {
var k_elts = k.split("_"); var k_elts = k.split("_");
...@@ -315,13 +323,15 @@ function draw_shift_templates(external) { ...@@ -315,13 +323,15 @@ function draw_shift_templates(external) {
} }
} }
if (!params || (typeof params.shift_listener !== "undefined" && params.shift_listener == true) || is_inscription_page == true){
shift_table.find('.shift').on("click", select_shift_among_compact); shift_table.find('.shift').on("click", select_shift_among_compact);
} }
}
sc_lat.find('.info').html(dispo + ' places disponibles<br />(/'+max+')'); sc_lat.find('.info').html(dispo + ' places disponibles<br />(/'+max+')');
if (typeof external == "undefined") { if (!params || typeof params.without_modal === "undefined" || (typeof params.without_modal !== "undefined" && params.without_modal == false)) {
closeModal(); closeModal();
} }
}); });
...@@ -329,13 +339,12 @@ function draw_shift_templates(external) { ...@@ -329,13 +339,12 @@ function draw_shift_templates(external) {
} }
function retrieve_and_draw_shift_tempates(external) { function retrieve_and_draw_shift_tempates(params) {
if (shift_table.length == 0) shift_table = $('#shift_choice table'); if (shift_table.length == 0) shift_table = $('#shift_choice table');
if (!external) { if (!params || typeof params.without_modal === "undefined" || (typeof params.without_modal !== "undefined" && params.without_modal == false)) {
openModal(); openModal();
} }
shift_table.find('.shift').remove(); shift_table.find('.shift').remove();
$.ajax({url : st_url, $.ajax({url : st_url,
dataType :'json' dataType :'json'
...@@ -374,10 +383,10 @@ function retrieve_and_draw_shift_tempates(external) { ...@@ -374,10 +383,10 @@ function retrieve_and_draw_shift_tempates(external) {
} }
}); });
draw_shift_templates(external); draw_shift_templates(params);
}); });
} else { } else {
draw_shift_templates(external); draw_shift_templates(params);
} }
...@@ -385,7 +394,7 @@ function retrieve_and_draw_shift_tempates(external) { ...@@ -385,7 +394,7 @@ function retrieve_and_draw_shift_tempates(external) {
}); });
} }
function filter_weeks(external) { function filter_weeks(params) {
var clicked = $(this); var clicked = $(this);
var week_types = $('#week_types'); var week_types = $('#week_types');
var parent_div = clicked.closest('div'); var parent_div = clicked.closest('div');
...@@ -430,7 +439,7 @@ function filter_weeks(external) { ...@@ -430,7 +439,7 @@ function filter_weeks(external) {
if (!w2.is(':checked') || !w4.is(':checked')) { if (!w2.is(':checked') || !w4.is(':checked')) {
$('#odd_weeks').prop('checked', false); $('#odd_weeks').prop('checked', false);
} }
draw_shift_templates(external); draw_shift_templates(params);
} }
function shift_loc_selection() { function shift_loc_selection() {
...@@ -438,14 +447,21 @@ function shift_loc_selection() { ...@@ -438,14 +447,21 @@ function shift_loc_selection() {
st_loc_buttons.removeClass('highlighted'); st_loc_buttons.removeClass('highlighted');
clicked.addClass('highlighted'); clicked.addClass('highlighted');
if (clicked.data('select') != 'Volant') { if (clicked.data('select') !== 'Volant' && clicked.data('select') !== 'Exemption') {
retrieve_and_draw_shift_tempates(); retrieve_and_draw_shift_tempates();
} else { } else if (clicked.data('select') === 'Volant') {
//shift_templates[volant] is not always set (when call from bdm interface)
if (typeof volant !== "undefined" && typeof shift_templates[volant] !== "undefined") {
subscribe_shift(volant); subscribe_shift(volant);
} }
} else if (clicked.data('select') === 'Exemption') {
subscribe_shift(exemptions_shift_id);
}
} }
st_loc_buttons.click(shift_loc_selection); st_loc_buttons.click(shift_loc_selection);
week_types.find('input').change(filter_weeks); week_types.find('input').change(() => {
filter_weeks({shift_listener: true});
});
...@@ -60,6 +60,11 @@ ...@@ -60,6 +60,11 @@
.replace( /ã/g, 'a' ) .replace( /ã/g, 'a' )
.replace( /õ/g, 'o' ) .replace( /õ/g, 'o' )
.replace( /ç/g, 'c' ) .replace( /ç/g, 'c' )
.replace( /â/g, 'a' )
.replace( /à/g, 'a' )
.replace( /ù/g, 'u' )
.replace( /ï/g, 'i' )
.replace( /œ/g, 'oe' )
.replace( /ì/g, 'i' ); .replace( /ì/g, 'i' );
} }
......
...@@ -20,6 +20,7 @@ from . import monitor ...@@ -20,6 +20,7 @@ from . import monitor
from .views import FieldsView from .views import FieldsView
from .views import ExportCompta from .views import ExportCompta
from .views import ExportPOS from .views import ExportPOS
from .views import ExportOrders
urlpatterns = [ urlpatterns = [
...@@ -31,6 +32,7 @@ urlpatterns = [ ...@@ -31,6 +32,7 @@ urlpatterns = [
url(r'^entity/example$', views.entity_example, name='entity example'), url(r'^entity/example$', views.entity_example, name='entity example'),
url(r'^export_compta$', ExportCompta.as_view(), name='export_compta'), url(r'^export_compta$', ExportCompta.as_view(), name='export_compta'),
url(r'^export_pos$', ExportPOS.as_view(), name='Export POS'), url(r'^export_pos$', ExportPOS.as_view(), name='Export POS'),
url(r'^export_orders$', ExportOrders.as_view(), name='export_orders'),
url(r'^monitor/$', monitor.index), url(r'^monitor/$', monitor.index),
url(r'^monitor/js_errors$', monitor.js_errors), url(r'^monitor/js_errors$', monitor.js_errors),
url(r'^members/', include('members.urls')), url(r'^members/', include('members.urls')),
......
...@@ -15,6 +15,7 @@ from .forms import ExportComptaForm ...@@ -15,6 +15,7 @@ from .forms import ExportComptaForm
from outils.lib.compta import * from outils.lib.compta import *
from openpyxl import Workbook from openpyxl import Workbook
from openpyxl.writer.excel import save_virtual_workbook from openpyxl.writer.excel import save_virtual_workbook
from orders.models import Orders
def test_compta(request): def test_compta(request):
...@@ -194,9 +195,11 @@ class ExportPOS(View): ...@@ -194,9 +195,11 @@ class ExportPOS(View):
'CHQ': 0, 'CHQ': 0,
'CB_DEJ': 0, 'CB_DEJ': 0,
'CHQ_DEJ': 0, 'CHQ_DEJ': 0,
'MonA': 0,
'TOTAL': 0} 'TOTAL': 0}
sub_total = 0 sub_total = 0
cb = chq = csh = cbd = chqd = 0 cb = chq = csh = cbd = chqd = mona = 0
coop_logger.info("payments : %s", s['payments'])
for p in s['payments']: for p in s['payments']:
# p['name'] is a sequence generated string # p['name'] is a sequence generated string
# Test order is important as CHEQDEJ contains CHEQ for ex. # Test order is important as CHEQDEJ contains CHEQ for ex.
...@@ -212,28 +215,32 @@ class ExportPOS(View): ...@@ -212,28 +215,32 @@ class ExportPOS(View):
cbd = sub_amount cbd = sub_amount
elif 'CB' in p['name']: elif 'CB' in p['name']:
cb = sub_amount cb = sub_amount
elif 'MonA' in p['name'] or 'MonA' in p['journal']:
mona = sub_amount
sub_total += sub_amount sub_total += sub_amount
totals[key]['CB'] += cb totals[key]['CB'] += cb
totals[key]['CSH'] += csh totals[key]['CSH'] += csh
totals[key]['CHQ'] += chq totals[key]['CHQ'] += chq
totals[key]['CB_DEJ'] += cbd totals[key]['CB_DEJ'] += cbd
totals[key]['CHQ_DEJ'] += chqd totals[key]['CHQ_DEJ'] += chqd
totals[key]['MonA'] += mona
totals[key]['TOTAL'] += round(sub_total, 2) totals[key]['TOTAL'] += round(sub_total, 2)
details_lines.append([mois, s['mm_dates']['min'], s['mm_dates']['min'], s['caisse'], s['name'], details_lines.append([mois, s['mm_dates']['min'], s['mm_dates']['max'], s['caisse'], s['name'],
cb, csh, chq, cbd, chqd, sub_total]) cb, csh, chq, cbd, chqd, mona, sub_total])
wb = Workbook() wb = Workbook()
ws1 = wb.create_sheet("Totaux " + mois, 0) ws1 = wb.create_sheet("Totaux " + mois, 0)
ws2 = wb.create_sheet("Détails " + mois, 1) ws2 = wb.create_sheet("Détails " + mois, 1)
ws1.append(['date', 'CB', 'CSH', 'CHQ', 'CB_DEJ', 'CHQ_DEJ', 'Total']) ws1.append(['date', 'CB', 'CSH', 'CHQ', 'CB_DEJ', 'CHQ_DEJ', 'MonA', 'Total'])
for day in sorted(totals): for day in sorted(totals):
cb = totals[day]['CB'] cb = totals[day]['CB']
csh = totals[day]['CSH'] csh = totals[day]['CSH']
chq = totals[day]['CHQ'] chq = totals[day]['CHQ']
cbd = totals[day]['CB_DEJ'] cbd = totals[day]['CB_DEJ']
chqd = totals[day]['CHQ_DEJ'] chqd = totals[day]['CHQ_DEJ']
mona = totals[day]['MonA']
total = totals[day]['TOTAL'] total = totals[day]['TOTAL']
ws1.append([day, cb, csh, chq, cbd, chqd, total]) ws1.append([day, cb, csh, chq, cbd, chqd, mona, total])
ws2.append(['mois', 'min_date', 'max_date', 'Caisse', 'session', 'CB', 'CSH','CHQ', 'CB_DEJ', 'CHQ_DEJ', 'total']) ws2.append(['mois', 'min_date', 'max_date', 'Caisse', 'session', 'CB', 'CSH','CHQ', 'CB_DEJ', 'CHQ_DEJ', 'MonA', 'total'])
for row in details_lines: for row in details_lines:
ws2.append(row) ws2.append(row)
wb_name = 'export_sessions__' + mois + '.xlsx' wb_name = 'export_sessions__' + mois + '.xlsx'
...@@ -375,3 +382,56 @@ class ExportPOS(View): ...@@ -375,3 +382,56 @@ class ExportPOS(View):
if not (month is None): if not (month is None):
response = self.__ca_sessions_ng(month) response = self.__ca_sessions_ng(month)
return response return response
class ExportOrders(View):
def get(self, request, *args, **kwargs):
u"""Display form"""
template = loader.get_template('outils/export_orders.html')
context = {'title': 'Export Commandes Réceptionnées'}
return HttpResponse(template.render(context, request))
def post(self, request, *args, **kwargs):
u"""Generate orders export between two dates"""
date_from = request.POST.get('from')
date_to = request.POST.get('to')
orders = Orders.get_orders_between_dates(date_from, date_to)
if "error" in orders:
error = "Une erreur est survenue, merci de contacter le service informatique."
return JsonResponse({'erreur': error, 'details': orders["error"]})
try:
wb = Workbook()
ws1 = wb.active
ws1.title = "Commandes réceptionnées"
ws1.append(['Fournisseur', 'Réf commande', 'Statut', 'Montant HT', 'Montant Total', 'Date réception'])
for order in orders["data"]:
supplier_name = order['supplier_name']
id_po = order['id_po']
amount_untaxed = order['amount_untaxed']
amount_total = order['amount_total']
if order['state'] == "purchase":
state = "Commande fournisseur"
elif order['state'] == "done":
state = "Terminé"
else:
state = order['state']
date_done_obj = datetime.datetime.strptime(order['date_done'], '%Y-%m-%d %H:%M:%S')
date_done = date_done_obj.strftime("%d/%m/%Y")
ws1.append([supplier_name, id_po, state, amount_untaxed, amount_total, date_done])
wb_name = 'export_orders_' + date_from + '_' + date_to + '.xlsx'
response = HttpResponse(content=save_virtual_workbook(wb),
content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename=' + wb_name
return response
except Exception as e:
error = "Une erreur est survenue, merci de contacter le service informatique."
coop_logger.error("Erreur export_orders : %s", str(e))
return JsonResponse({'erreur': error, 'details': str(e)})
...@@ -93,6 +93,8 @@ class CagetteProduct(models.Model): ...@@ -93,6 +93,8 @@ class CagetteProduct(models.Model):
v = round(float(price) / float(product['weight_net']), 2) v = round(float(price) / float(product['weight_net']), 2)
if k == 'price_volume' and len(v) > 0 and len(price) > 0 and float(price) > 0: if k == 'price_volume' and len(v) > 0 and len(price) > 0 and float(price) > 0:
v = round(float(price) / float(product['volume']), 2) v = round(float(price) / float(product['volume']), 2)
if directory != "/product_labels/" or (directory == "/product_labels/" and k != "meal_voucher_ok"):
# add parameter to text unless it's for a product label and parameter is meal_voucher_ok
txt += k + '=' + str(v).strip() + "\r\n" txt += k + '=' + str(v).strip() + "\r\n"
if not (nb is None) and len(nb) > 0: if not (nb is None) and len(nb) > 0:
txt += 'nb_impression=' + str(nb) + "\r\n" txt += 'nb_impression=' + str(nb) + "\r\n"
......
...@@ -74,9 +74,9 @@ class CagetteReception(models.Model): ...@@ -74,9 +74,9 @@ class CagetteReception(models.Model):
def implies_scale_file_generation(self): def implies_scale_file_generation(self):
answer = False answer = False
lines = Order(self.id).get_lines() lines_data = Order(self.id).get_lines()
bc_pattern = re.compile('^0493|0499') bc_pattern = re.compile('^0493|0499') # TODO : Adjust for other pattern (such as Supercoop)
for l in lines: for l in lines_data['lines']:
if not (bc_pattern.match(str(l['barcode'])) is None): if not (bc_pattern.match(str(l['barcode'])) is None):
answer = True answer = True
# print ('answer=' + str(answer)) # print ('answer=' + str(answer))
...@@ -131,7 +131,8 @@ class CagetteReception(models.Model): ...@@ -131,7 +131,8 @@ class CagetteReception(models.Model):
""" """
import json import json
processed_lines = 0 processed_lines = 0
order_lines = CagetteReception.get_order_lines_by_po(self.id, nullQty=True) order_lines_data = CagetteReception.get_order_lines_by_po(self.id, nullQty=True)
order_lines = order_lines_data['lines']
received_products = {} received_products = {}
for p in order_lines: for p in order_lines:
received_products[p['product_id'][0]] = p['product_qty'] received_products[p['product_id'][0]] = p['product_qty']
...@@ -176,7 +177,8 @@ class CagetteReception(models.Model): ...@@ -176,7 +177,8 @@ class CagetteReception(models.Model):
def update_products_price(self): def update_products_price(self):
processed = 0 processed = 0
errors = [] errors = []
order_lines = CagetteReception.get_order_lines_by_po(self.id) order_lines_data = CagetteReception.get_order_lines_by_po(self.id)
order_lines = order_lines_data['lines']
if order_lines and len(order_lines) > 0: if order_lines and len(order_lines) > 0:
# Exceptions are due to the fact API returns None whereas the action is really done !... # Exceptions are due to the fact API returns None whereas the action is really done !...
marshal_none_error = 'cannot marshal None unless allow_none is enabled' marshal_none_error = 'cannot marshal None unless allow_none is enabled'
...@@ -194,7 +196,32 @@ class CagetteReception(models.Model): ...@@ -194,7 +196,32 @@ class CagetteReception(models.Model):
success = True success = True
else: else:
success = False success = False
return {'errors': errors, 'processed': processed, 'success': success} return {'errors': errors, 'processed': processed, 'success': success, 'lines': order_lines}
def print_shelf_labels_for_updated_prices(self, lines):
import requests
# don't print barcode which begin with these codes
noprint_list = ["0493", "0492", "0499"]
pids = []
for l in lines:
pids.append(l['product_id'][0])
products_to_print = self.o_api.search_read('product.product', [['id','in', pids]], ['product_tmpl_id', 'barcode', 'to_print'])
if products_to_print:
to_reset = []
for p_to_print in products_to_print:
coop_logger.info('candidate to print %s', str(p_to_print))
if p_to_print['to_print'] is True and p_to_print['barcode'][:4] not in noprint_list:
try:
tools_url = settings.TOOLS_SERVER + '/products/label_print/'
tools_url += str(p_to_print['product_tmpl_id'][0])
requests.get(tools_url)
to_reset.append(p_to_print['id'])
except Exception as e:
coop_logger.error("Shelf label printing : %s",str(e))
if len(to_reset) > 0:
self.o_api.update('product.product', to_reset, {'to_print': 0})
def finalyze_picking(self): def finalyze_picking(self):
...@@ -234,6 +261,8 @@ class CagetteReception(models.Model): ...@@ -234,6 +261,8 @@ class CagetteReception(models.Model):
new_x_reception_status += '/error_uprice' new_x_reception_status += '/error_uprice'
if new_x_reception_status == '': if new_x_reception_status == '':
new_x_reception_status = 'done' new_x_reception_status = 'done'
self.print_shelf_labels_for_updated_prices(price_update['lines'])
if result != 'already done': if result != 'already done':
self.o_api.update('purchase.order', [self.id], {'x_reception_status': new_x_reception_status}) self.o_api.update('purchase.order', [self.id], {'x_reception_status': new_x_reception_status})
...@@ -275,6 +304,7 @@ class CagetteReception(models.Model): ...@@ -275,6 +304,7 @@ class CagetteReception(models.Model):
print_label = m.implies_scale_file_generation() print_label = m.implies_scale_file_generation()
if fp == 'processed' or fp == 'already done': if fp == 'processed' or fp == 'already done':
os.remove(p['file']) os.remove(p['file'])
processed.append({p['id']: fp}) processed.append({p['id']: fp})
os.remove('data/po_in_process_' + str(p['id'])) os.remove('data/po_in_process_' + str(p['id']))
if print_label is True: if print_label is True:
......
...@@ -221,11 +221,19 @@ div#container_edition { ...@@ -221,11 +221,19 @@ div#container_edition {
} }
tr.even td.row_product_no_qty { tr.even td.row_product_no_qty {
background-color: #f0ad4e; /*#ec971f*/ background-color: #F2B969 !important;
} }
tr.odd td.row_product_no_qty { tr.odd td.row_product_no_qty {
background-color: rgb(236, 182, 106); /*#ec971f*/ background-color: #f0ad4e !important;
}
tr.even td.row_product_qty_changed {
background-color: #ffc0b7 !important;
}
tr.odd td.row_product_qty_changed {
background-color: #FFB7AD !important;
} }
.add_products_button_container { .add_products_button_container {
......
...@@ -15,7 +15,8 @@ Sémantiquement, ici : ...@@ -15,7 +15,8 @@ Sémantiquement, ici :
* If 1 element: single order * If 1 element: single order
*/ */
var orders = {}, var orders = {},
group_ids = []; group_ids = [],
product_coeffs = [];
var reception_status = null, var reception_status = null,
list_to_process = [], list_to_process = [],
...@@ -267,6 +268,13 @@ function resetPopUpButtons() { ...@@ -267,6 +268,13 @@ function resetPopUpButtons() {
/* FETCH SERVER DATA */ /* FETCH SERVER DATA */
function store_received_product_coeffs(coeffs) {
for (let i=0; i<coeffs.length; i++) {
if (product_coeffs.indexOf(coeffs[i]) == -1)
product_coeffs.push(coeffs[i]);
}
}
/** /**
* Get order(s) data from server * Get order(s) data from server
* @param {Array} po_ids if set, fetch data for these po only * @param {Array} po_ids if set, fetch data for these po only
...@@ -285,6 +293,7 @@ function fetch_data(po_ids = null) { ...@@ -285,6 +293,7 @@ function fetch_data(po_ids = null) {
success: function(data) { success: function(data) {
// for each order // for each order
for (order_data of data.orders) { for (order_data of data.orders) {
store_received_product_coeffs(order_data.used_coeffs);
// for each product in order // for each product in order
for (i in order_data.po) { for (i in order_data.po) {
// If in step 2, find old qty in previous step data // If in step 2, find old qty in previous step data
...@@ -695,6 +704,17 @@ function initLists() { ...@@ -695,6 +704,17 @@ function initLists() {
$(cell_node).addClass('row_product_no_qty'); $(cell_node).addClass('row_product_no_qty');
} }
} else if (
row_data !== undefined
&& row_data.product_qty !== 0
&& 'old_qty' in row_data
&& row_data.old_qty != row_data.product_qty
) {
for (var j = 0; j < row.cells.length; j++) {
const cell_node = row.cells[j];
$(cell_node).addClass('row_product_qty_changed');
}
} }
} }
}); });
...@@ -726,6 +746,17 @@ function initLists() { ...@@ -726,6 +746,17 @@ function initLists() {
$(cell_node).addClass('row_product_no_qty'); $(cell_node).addClass('row_product_no_qty');
} }
} else if (
row_data !== undefined
&& row_data.product_qty !== 0
&& 'old_qty' in row_data
&& row_data.old_qty != row_data.product_qty
) {
for (var j = 0; j < row.cells.length; j++) {
const cell_node = row.cells[j];
$(cell_node).addClass('row_product_qty_changed');
}
} }
} }
}); });
...@@ -1182,7 +1213,7 @@ function setLineEdition(product) { ...@@ -1182,7 +1213,7 @@ function setLineEdition(product) {
} else { } else {
document.getElementById('product_uom').innerHTML = ' / unité'; document.getElementById('product_uom').innerHTML = ' / unité';
$('#edition_input').attr('type', 'number') $('#edition_input').attr('type', 'number')
.attr('step', 0.01) .attr('step', (allow_four_digits_in_reception_price == "True" ? 0.0001 : 0.01))
.attr('max', 9999); .attr('max', 9999);
} }
} else if (editing_product.product_uom[0] == 21) { // kg } else if (editing_product.product_uom[0] == 21) { // kg
...@@ -1194,7 +1225,7 @@ function setLineEdition(product) { ...@@ -1194,7 +1225,7 @@ function setLineEdition(product) {
} else { } else {
document.getElementById('product_uom').innerHTML = ' / kg'; document.getElementById('product_uom').innerHTML = ' / kg';
$('#edition_input').attr('type', 'number') $('#edition_input').attr('type', 'number')
.attr('step', 0.01) .attr('step', (allow_four_digits_in_reception_price == "True" ? 0.0001 : 0.01))
.attr('max', 9999); .attr('max', 9999);
} }
} }
...@@ -1345,20 +1376,37 @@ function editProductInfo (productToEdit, value = null, batch = false) { ...@@ -1345,20 +1376,37 @@ function editProductInfo (productToEdit, value = null, batch = false) {
if (reception_status == "qty_valid" && productToEdit.price_unit != newValue) { if (reception_status == "qty_valid" && productToEdit.price_unit != newValue) {
if (index == -1) { // First update if (index == -1) { // First update
productToEdit.old_price_unit = productToEdit.price_unit; productToEdit.old_price_unit = productToEdit.price_unit;
productToEdit.new_shelf_price = null; productToEdit.new_shelf_price = parseFloat(newValue);
if (! isNaN(productToEdit.p_coeff)) {
try { try {
new_shelf_price = parseFloat(newValue * productToEdit.p_coeff); // Let's compute product final price, using coeffs.
old_shelf_price = parseFloat(productToEdit.p_price * productToEdit.p_coeff); let computing_shelf_price_details = {base_value: productToEdit.new_shelf_price, intermediate_values: []};
if (Math.abs(new_shelf_price - old_shelf_price) > 0.001)
productToEdit.new_shelf_price = new_shelf_price.toFixed(2); for (let k = 1; k <10; k++) {
if (typeof productToEdit['coeff' + k + '_id'] !== "undefined") {
product_coeffs.forEach((coeff) => {
if (coeff.id == productToEdit['coeff' + k + '_id']) {
if (coeff.operation_type == "fixed") {
productToEdit.new_shelf_price += coeff.value;
computing_shelf_price_details.intermediate_values.push({msg: "Found fixed coeff " + coeff.value, new_value: productToEdit.new_shelf_price});
} else if (coeff.operation_type == "multiplier") {
productToEdit.new_shelf_price *= (1 + coeff.value);
computing_shelf_price_details.intermediate_values.push({msg: "Found multiplier coeff " + coeff.value, new_value: productToEdit.new_shelf_price});
}
}
});
}
}
productToEdit.new_shelf_price *= productToEdit.tax_coeff;
computing_shelf_price_details.intermediate_values.push({msg: "Applying tax coeff " + productToEdit.tax_coeff, new_value: productToEdit.new_shelf_price});
productToEdit.new_shelf_price = productToEdit.new_shelf_price.toFixed(2);
computing_shelf_price_details.final_value = productToEdit.new_shelf_price;
productToEdit.computing_shelf_price_details = computing_shelf_price_details;
} catch (e) { } catch (e) {
productToEdit.new_shelf_price = null;
err = {msg: e.name + ' : ' + e.message, ctx: 'computing new_shelf_price'}; err = {msg: e.name + ' : ' + e.message, ctx: 'computing new_shelf_price'};
console.error(err); console.error(err);
report_JS_error(err, 'reception'); report_JS_error(err, 'reception');
} }
}
firstUpdate = true; firstUpdate = true;
} else if (productToEdit.old_price_unit == newValue) { } else if (productToEdit.old_price_unit == newValue) {
...@@ -1484,8 +1532,20 @@ function print_product_labels() { ...@@ -1484,8 +1532,20 @@ function print_product_labels() {
try { try {
if (is_time_to('print_pdt_labels', 10000)) { if (is_time_to('print_pdt_labels', 10000)) {
$.ajax("../../orders/print_product_labels?oids=" + group_ids.join(',')) $.ajax("../../orders/print_product_labels?oids=" + group_ids.join(','))
.done(function() { .done(function(data) {
alert('Impression des étiquettes à coller sur les articles lancée.'); let success = false;
if (typeof data.res !== "undefined") {
if (typeof data.res.error === "undefined") {
success = true;
}
}
if (success == true) {
alert("l' impression des étiquettes à coller sur les articles vient d'être lancée.");
$('#barcodesToPrint').hide();
} else {
alert("Une erreur est survenue.");
}
}); });
} else { } else {
alert("Vous avez cliqué il y a moins de 10s... Patience, la demande est en cours de traitement."); alert("Vous avez cliqué il y a moins de 10s... Patience, la demande est en cours de traitement.");
...@@ -1980,10 +2040,12 @@ function add_products_action() { ...@@ -1980,10 +2040,12 @@ function add_products_action() {
for (let qty_input of qty_inputs) { for (let qty_input of qty_inputs) {
if ($(qty_input).val() === "") { if ($(qty_input).val() === "") {
has_empty_qty_input = true; has_empty_qty_input = true;
$(qty_input).closest(".product_qty").find(".product_qty_input_alert") $(qty_input).closest(".product_qty")
.find(".product_qty_input_alert")
.show(); .show();
} else { } else {
$(qty_input).closest(".product_qty").find(".product_qty_input_alert") $(qty_input).closest(".product_qty")
.find(".product_qty_input_alert")
.hide(); .hide();
} }
} }
...@@ -2025,8 +2087,10 @@ function create_orders() { ...@@ -2025,8 +2087,10 @@ function create_orders() {
for (let i = 0; i < add_products_lines.length; i++) { for (let i = 0; i < add_products_lines.length; i++) {
let line = add_products_lines[i]; let line = add_products_lines[i];
if ($(line).find(".product_name").text() === p.name) { if ($(line).find(".product_name")
product_uom = $(line).find(".product_uom").text(); .text() === p.name) {
product_uom = $(line).find(".product_uom")
.text();
if (product_uom.includes("kg")) { if (product_uom.includes("kg")) {
product_qty = parseFloat($(line).find(".product_qty_input") product_qty = parseFloat($(line).find(".product_qty_input")
...@@ -2046,15 +2110,16 @@ function create_orders() { ...@@ -2046,15 +2110,16 @@ function create_orders() {
// If package qty is > than input value, package qty will be used while creating order // If package qty is > than input value, package qty will be used while creating order
let package_qty = p_supplierinfo.package_qty; let package_qty = p_supplierinfo.package_qty;
if (product_qty < package_qty) { if (product_qty < package_qty) {
package_qty = product_qty; package_qty = product_qty;
} }
// Round differently for unit & kg products // Round differently for unit & kg products
if (product_uom.includes("kg") ) { if (product_uom.includes("kg")) {
item_qty_package = Math.round((product_qty / package_qty) * 1e2) / 1e2; item_qty_package = Math.round((product_qty / package_qty) * 1e2) / 1e2;
} else { } else {
item_qty_package = Math.round(product_qty / package_qty) item_qty_package = Math.round(product_qty / package_qty);
} }
orders_data.suppliers_data[supplier_id].lines.push({ orders_data.suppliers_data[supplier_id].lines.push({
......
...@@ -33,7 +33,7 @@ def home(request): ...@@ -33,7 +33,7 @@ def home(request):
'merge_orders_pswd': getattr(settings, 'RECEPTION_MERGE_ORDERS_PSWD', 'makeastop'), 'merge_orders_pswd': getattr(settings, 'RECEPTION_MERGE_ORDERS_PSWD', 'makeastop'),
'couchdb_server': settings.COUCHDB['url'], 'couchdb_server': settings.COUCHDB['url'],
'db': settings.COUCHDB['dbs']['reception'], 'db': settings.COUCHDB['dbs']['reception'],
'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', '') 'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', ''),
} }
template = loader.get_template('reception/index.html') template = loader.get_template('reception/index.html')
...@@ -73,8 +73,9 @@ def get_list_orders(request): ...@@ -73,8 +73,9 @@ def get_list_orders(request):
} }
if get_order_lines is True: if get_order_lines is True:
order_lines = CagetteReception.get_order_lines_by_po(int(order["id"]), nullQty = True) order_lines_data = CagetteReception.get_order_lines_by_po(int(order["id"]), nullQty = True)
ligne["po"] = order_lines ligne["po"] = order_lines_data['lines']
ligne["used_coeffs"] = order_lines_data['used_coeffs']
orders.append(ligne) orders.append(ligne)
...@@ -93,7 +94,8 @@ def produits(request, id): ...@@ -93,7 +94,8 @@ def produits(request, id):
"ADD_ALL_LEFT_IS_GOOD_QTIES": False, "ADD_ALL_LEFT_IS_GOOD_QTIES": False,
"ADD_ALL_LEFT_IS_GOOD_PRICES": False, "ADD_ALL_LEFT_IS_GOOD_PRICES": False,
'add_products_pswd': getattr(settings, 'RECEPTION_ADD_PRODUCTS_PSWD', 'makeastop'), 'add_products_pswd': getattr(settings, 'RECEPTION_ADD_PRODUCTS_PSWD', 'makeastop'),
'update_qty_pswd': getattr(settings, 'RECEPTION_UPDATE_QTY_PSWD', 'makeastop') 'update_qty_pswd': getattr(settings, 'RECEPTION_UPDATE_QTY_PSWD', 'makeastop'),
'allow_four_digits_in_reception_price': getattr(settings, 'ALLOW_FOUR_DIGITS_IN_RECEPTION_PRICE', False),
} }
fixed_barcode_prefix = '0490' fixed_barcode_prefix = '0490'
...@@ -123,17 +125,17 @@ def produits(request, id): ...@@ -123,17 +125,17 @@ def produits(request, id):
def get_order_lines(request, id_po): def get_order_lines(request, id_po):
"""Send content of an order""" """Send content of an order"""
order_lines = CagetteReception.get_order_lines_by_po(int(id_po)) order_lines_data = CagetteReception.get_order_lines_by_po(int(id_po))
return JsonResponse({'id_po': id_po, 'po': order_lines}) return JsonResponse({'id_po': id_po, 'po': order_lines_data['lines'], 'used_coeffs': order_lines_data['used_coeffs']})
def get_orders_lines(request): def get_orders_lines(request):
"""Send content of multiple orders""" """Send content of multiple orders"""
data = json.loads(request.body.decode()) data = json.loads(request.body.decode())
orders = [] orders = []
for id_po in data['po_ids']: for id_po in data['po_ids']:
order_lines = CagetteReception.get_order_lines_by_po(int(id_po), nullQty = True) order_lines_data = CagetteReception.get_order_lines_by_po(int(id_po), nullQty = True)
orders.append({'id_po': id_po, 'po': order_lines}) orders.append({'id_po': id_po, 'po': order_lines_data['lines'], 'used_coeffs': order_lines_data['used_coeffs']})
return JsonResponse({'orders': orders}) return JsonResponse({'orders': orders})
...@@ -219,14 +221,15 @@ def update_orders(request): ...@@ -219,14 +221,15 @@ def update_orders(request):
errors.append('error registering shortage on p'+order_line['id']+':'+str(e)) errors.append('error registering shortage on p'+order_line['id']+':'+str(e))
# Print etiquette with new price if update if successful and barcode is authorized # Print etiquette with new price if update if successful and barcode is authorized
if (print_labels is True) and (update is True) and (data['update_type'] == 'br_valid') and order_line['new_shelf_price'] and order_line['barcode'][:4] not in noprint_list: # Printing at that point is inhibited because of random ununderstandable wrong new_shelf_price
try: # if (print_labels is True) and (update is True) and (data['update_type'] == 'br_valid') and order_line['new_shelf_price'] and order_line['barcode'][:4] not in noprint_list:
tools_url = settings.TOOLS_SERVER + '/products/label_print/' # try:
tools_url += str(order_line['product_tmpl_id']) + '/' # tools_url = settings.TOOLS_SERVER + '/products/label_print/'
tools_url += str(order_line['new_shelf_price']) # tools_url += str(order_line['product_tmpl_id']) + '/'
requests.get(tools_url) # tools_url += str(order_line['new_shelf_price'])
except Exception as e: # requests.get(tools_url)
coop_logger.error("Shelf label printing : %s",str(e)) # except Exception as e:
# coop_logger.error("Shelf label printing : %s",str(e))
except KeyError: except KeyError:
coop_logger.info("No line to update.") coop_logger.info("No line to update.")
...@@ -301,8 +304,8 @@ def save_error_report(request): ...@@ -301,8 +304,8 @@ def save_error_report(request):
} }
data['orders'].append(group_order) # group "order" has to be last in orders list data['orders'].append(group_order) # group "order" has to be last in orders list
else: # else:
coop_logger.info("data['orders'] is a single PO (not inside group)") # coop_logger.info("data['orders'] is a single PO (not inside group)")
except Exception as e2: except Exception as e2:
coop_logger.error("Save reception report : Error while create group_order %s", str(e2)) coop_logger.error("Save reception report : Error while create group_order %s", str(e2))
...@@ -329,6 +332,7 @@ def save_error_report(request): ...@@ -329,6 +332,7 @@ def save_error_report(request):
} }
else: else:
# Read step 1 data from couch db document # Read step 1 data from couch db document
order_id = 'order_' + str(order['id']) order_id = 'order_' + str(order['id'])
order_doc = c_db.getDocById(order_id) order_doc = c_db.getDocById(order_id)
......
...@@ -29,7 +29,7 @@ class CagetteSales(models.Model): ...@@ -29,7 +29,7 @@ class CagetteSales(models.Model):
statements_orders[s] = o["name"] statements_orders[s] = o["name"]
# Get payment lines # Get payment lines
cond = [['id', 'in', statements]] cond = [['id', 'in', statements]]
fields = ["amount", "journal_id", "create_date"] fields = ["amount", "journal_id", "create_date", "meal_voucher_issuer"]
payments = self.o_api.search_read('account.bank.statement.line', cond, fields, order="create_date ASC", limit=50000) payments = self.o_api.search_read('account.bank.statement.line', cond, fields, order="create_date ASC", limit=50000)
try: try:
for payment in payments: for payment in payments:
...@@ -41,7 +41,8 @@ class CagetteSales(models.Model): ...@@ -41,7 +41,8 @@ class CagetteSales(models.Model):
"payments": [ "payments": [
{ {
"amount": round(float(payment["amount"]), 2), "amount": round(float(payment["amount"]), 2),
"journal_id": payment["journal_id"] "journal_id": payment["journal_id"],
"meal_voucher_issuer": payment["meal_voucher_issuer"]
} }
] ]
}) })
......
...@@ -49,13 +49,11 @@ function display_orders(orders) { ...@@ -49,13 +49,11 @@ function display_orders(orders) {
{ {
data:"partner", data:"partner",
title:"Membre", title:"Membre"
width: "40%"
}, },
{ {
data:"total_amount", data:"total_amount",
title: "Montant dû", title: "Montant dû",
className:"dt-body-center",
render: function (data) { render: function (data) {
return parseFloat(data).toFixed(2) + ' €'; return parseFloat(data).toFixed(2) + ' €';
} }
...@@ -74,7 +72,14 @@ function display_orders(orders) { ...@@ -74,7 +72,14 @@ function display_orders(orders) {
return res; return res;
} }
},
{
data:"payments[0].meal_voucher_issuer",
title: "Émetteur CB Déj",
render: function (data) {
return data === 'false' || data == false ? '' : data;
} }
},
], ],
order: [ order: [
[ [
......
...@@ -1230,52 +1230,6 @@ function init() { ...@@ -1230,52 +1230,6 @@ function init() {
$(this).off('wheel.disableScroll'); $(this).off('wheel.disableScroll');
}); });
// client-side validation of numeric inputs, optionally replacing separator sign(s).
$("input.number").on("keydown", function (e) {
// allow function keys and decimal separators
if (
// backspace, delete, tab, escape, enter, comma and .
$.inArray(e.keyCode, [
46,
8,
9,
27,
13,
110,
188,
190
]) !== -1 ||
// Ctrl/cmd+A, Ctrl/cmd+C, Ctrl/cmd+X
($.inArray(e.keyCode, [
65,
67,
88
]) !== -1 && (e.ctrlKey === true || e.metaKey === true)) ||
// home, end, left, right
(e.keyCode >= 35 && e.keyCode <= 39)) {
/*
// optional: replace commas with dots in real-time (for en-US locals)
if (e.keyCode === 188) {
e.preventDefault();
$(this).val($(this).val() + ".");
}
// optional: replace decimal points (num pad) and dots with commas in real-time (for EU locals)
if (e.keyCode === 110 || e.keyCode === 190) {
e.preventDefault();
$(this).val($(this).val() + ",");
}
*/
return;
}
// block any non-number
if ((e.shiftKey || (e.keyCode < 48 || e.keyCode > 57)) && (e.keyCode < 96 || e.keyCode > 105)) {
e.preventDefault();
}
});
// Manual and textual input // Manual and textual input
$('#search_input').keypress(function(e) { $('#search_input').keypress(function(e) {
if (e.which >= 48 && e.which <= 57) { // figures [0-9] if (e.which >= 48 && e.which <= 57) { // figures [0-9]
......
...@@ -99,7 +99,7 @@ def get_list_shift_calendar(request, partner_id): ...@@ -99,7 +99,7 @@ def get_list_shift_calendar(request, partner_id):
registerPartner = cs.get_shift_partner(partner_id) registerPartner = cs.get_shift_partner(partner_id)
use_new_members_space = getattr(settings, 'USE_NEW_MEMBERS_SPACE', False) use_new_members_space = getattr(settings, 'USE_NEW_MEMBERS_SPACE', False)
remove_15_minutes_at_shift_end = getattr(settings, 'REMOVE_15_MINUTES_AT_SHIFT_END', True)
listRegisterPartner = [] listRegisterPartner = []
listMakeUpShift = [] listMakeUpShift = []
for v in registerPartner: for v in registerPartner:
...@@ -113,8 +113,8 @@ def get_list_shift_calendar(request, partner_id): ...@@ -113,8 +113,8 @@ def get_list_shift_calendar(request, partner_id):
events = [] events = []
for value in listService: for value in listService:
events.append(value)
if value['shift_type_id'][0] == 1 or getattr(settings, 'USE_STANDARD_SHIFT', True) is False: if value['shift_type_id'][0] == 1 or getattr(settings, 'USE_STANDARD_SHIFT', True) is False:
# Standard ou volant si on n'utilise pas les services standards (config)
l = set(value['registration_ids']) & set(listRegisterPartner) l = set(value['registration_ids']) & set(listRegisterPartner)
# if (int(value['seats_reserved']) == int(value['seats_max']) and len(l) > 0 ) or (int(value['seats_reserved']) < int(value['seats_max'])): # if (int(value['seats_reserved']) == int(value['seats_max']) and len(l) > 0 ) or (int(value['seats_reserved']) < int(value['seats_max'])):
if (int(value['seats_available']) > 0 or len(l) > 0 ): if (int(value['seats_available']) > 0 or len(l) > 0 ):
...@@ -134,7 +134,9 @@ def get_list_shift_calendar(request, partner_id): ...@@ -134,7 +134,9 @@ def get_list_shift_calendar(request, partner_id):
event["start"] = dateIsoUTC(value['date_begin_tz']) event["start"] = dateIsoUTC(value['date_begin_tz'])
datetime_object = datetime.datetime.strptime(value['date_end_tz'], "%Y-%m-%d %H:%M:%S") - datetime.timedelta(minutes=15) datetime_object = datetime.datetime.strptime(value['date_end_tz'], "%Y-%m-%d %H:%M:%S")
if remove_15_minutes_at_shift_end is True:
datetime_object -= datetime.timedelta(minutes=15)
event["end"] = dateIsoUTC(datetime_object.strftime("%Y-%m-%d %H:%M:%S")) event["end"] = dateIsoUTC(datetime_object.strftime("%Y-%m-%d %H:%M:%S"))
if len(l) > 0: if len(l) > 0:
...@@ -177,14 +179,11 @@ def get_list_shift_calendar(request, partner_id): ...@@ -177,14 +179,11 @@ def get_list_shift_calendar(request, partner_id):
def get_list_shift_partner(request, partner_id): def get_list_shift_partner(request, partner_id):
cs = CagetteShift() cs = CagetteShift()
shiftData = cs.get_shift_partner(partner_id) shiftData = cs.get_shift_partner(partner_id)
empty_data = False
for value in shiftData: for value in shiftData:
value['date_begin'] = value['date_begin'] + "Z" value['date_begin'] = value['date_begin'] + "Z"
value['date_end'] = value['date_end'] + "Z" value['date_end'] = value['date_end'] + "Z"
if "Services des comités" in value['shift_id'][1]:
empty_data = True
if empty_data is True:
shiftData = []
return JsonResponse(shiftData, safe=False) return JsonResponse(shiftData, safe=False)
def change_shift(request): def change_shift(request):
...@@ -215,6 +214,11 @@ def change_shift(request): ...@@ -215,6 +214,11 @@ def change_shift(request):
response = {'msg': "Old service in less than 24hours."} response = {'msg': "Old service in less than 24hours."}
return JsonResponse(response, status=400) return JsonResponse(response, status=400)
if cs.is_shift_exchange_allowed(idOldShift, data["idShift"], data["shift_type"], data["idPartner"]) is False:
response = {'msg': "Not allowed to change shift"}
return JsonResponse(response, status=400)
st_r_id = False st_r_id = False
#Insertion du nouveau shift #Insertion du nouveau shift
try: try:
...@@ -329,10 +333,9 @@ def cancel_shift(request): ...@@ -329,10 +333,9 @@ def cancel_shift(request):
listRegister = [int(request.POST['idRegister'])] listRegister = [int(request.POST['idRegister'])]
try: try:
response = cs.cancel_shift(listRegister)
# decrement extra_shift_done if param exists # decrement extra_shift_done if param exists
if 'extra_shift_done' in request.POST: if 'extra_shift_done' in request.POST:
response = cs.cancel_shift(listRegister, origin='memberspace extra shift done')
target = int(request.POST["extra_shift_done"]) - 1 target = int(request.POST["extra_shift_done"]) - 1
# extra security # extra security
...@@ -341,6 +344,8 @@ def cancel_shift(request): ...@@ -341,6 +344,8 @@ def cancel_shift(request):
cm = CagetteMember(partner_id) cm = CagetteMember(partner_id)
cm.update_extra_shift_done(target) cm.update_extra_shift_done(target)
else:
response = cs.cancel_shift(listRegister)
return JsonResponse({"res" : 'response'}) return JsonResponse({"res" : 'response'})
except Exception as e: except Exception as e:
......
...@@ -373,6 +373,8 @@ class CagetteStock(models.Model): ...@@ -373,6 +373,8 @@ class CagetteStock(models.Model):
cond = [['qty_available','>', 0], ['active', '=', True]] cond = [['qty_available','>', 0], ['active', '=', True]]
fields = ["barcode", "display_name", "qty_available", "standard_price"] fields = ["barcode", "display_name", "qty_available", "standard_price"]
articles = api.search_read('product.product', cond, fields, 1000000) articles = api.search_read('product.product', cond, fields, 1000000)
for a in articles:
a['total'] = a['qty_available'] * a['standard_price']
except Exception as e: except Exception as e:
coop_logger.error("Erreur get_valuable_stock : %s", str(e)) coop_logger.error("Erreur get_valuable_stock : %s", str(e))
return articles return articles
......
...@@ -6,14 +6,29 @@ $(document).ready(function() { ...@@ -6,14 +6,29 @@ $(document).ready(function() {
"data": "" "data": ""
}, },
"columns":[ "columns":[
{data:"barcode", "title":"Code-barre", "width": "50%"}, {data:"barcode", "title":"Code-barre", "width": "10em"},
{data:"display_name", "title":"Article", "width": "50%"}, {data:"display_name", "title":"Article", "width": "50%"},
{data:"qty_available", "title":"Stock", "width":"15%" {data:"qty_available", "title":"Stock", "width":"5em",
render: function(data) {
if (data == parseInt(data,10)) {
return data
} else {
return data.toFixed(3)
}
}
}, },
{data:"standard_price", "title":"Prix achat", "width":"15%" {data:"standard_price", "title":"Prix Achat", "width":"4em",
render: function(data) {
return data.toFixed(2)
} }
},
{data: "total", "title": "Total",
render: function(data, type, full) {
return data.toFixed(2)
}
}
], ],
...@@ -24,7 +39,7 @@ $(document).ready(function() { ...@@ -24,7 +39,7 @@ $(document).ready(function() {
"desc" "desc"
] ]
], ],
"iDisplayLength": 50, "paging": true,
"language": { "language": {
"emptyTable": "Pas de donnée", "emptyTable": "Pas de donnée",
"info": "Affiché : lignes _START_ à _END_ sur _TOTAL_", "info": "Affiché : lignes _START_ à _END_ sur _TOTAL_",
...@@ -55,6 +70,6 @@ $(document).ready(function() { ...@@ -55,6 +70,6 @@ $(document).ready(function() {
className: 'btn--primary btn_export' className: 'btn--primary btn_export'
}, },
], ],
dom: '<lr<t>ip><"clear"><B>', dom: '<lr<t>ip><"clear"><B>'
}); });
}); });
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
<script src="{% static "js/modernizr-custom.min.js" %}"></script> <script src="{% static "js/modernizr-custom.min.js" %}"></script>
{% block additionnal_scripts %}{% endblock %} {% block additionnal_scripts %}{% endblock %}
</head> </head>
<body> <body class="{% block body_class %}nospecific{% endblock %}">
{% block menu %}{% endblock %} {% block menu %}{% endblock %}
<!-- The overlay --> <!-- The overlay -->
<div id="modal" class="overlay"> <div id="modal" class="overlay">
......
...@@ -56,9 +56,35 @@ ...@@ -56,9 +56,35 @@
</div> </div>
<div id="templates" style="display:none;"></div> <div id="templates" style="display:none;">
<div id="modal_extend_delay_template">
<p>
Vous vous apprêtez à rallonger le délai de
<span class="member_name"></span>
pour une durée de {{extension_duration}} mois à partir d'aujourd'hui.
</p>
</div>
<div id="modal_incr_makeup_counter">
<p>Ajouter un rattrapage à <span class="member_name"></span> ?</p>
<br><label for="incr-explanation">Explication : </label><input class="" type="text" id="incr-explanation">
<br><br><label for="incr-signature">Signature : </label><input class="" type="text" id="incr-signature">
</div>
<div id="modal_decr_makeup_counter">
<p>Enlever un rattrapage à <span class="member_name"></span> ?</p>
<br><label for="decr-explanation">Explication : </label><input class="" type="text" id="decr-explanation">
<br><br><label for="decr-signature">Signature : </label><input class="" type="text" id="decr-signature">
</div>
<div id="modal_decr_selected_makeup_counter">
<p>Enlever un rattrapage aux membres sélectionnés ?</p>
<br><label for="decr-explanation-selected">Explication : </label><input class="" type="text" id="decr-explanation-selected">
<br><br><label for="decr-signature-selected">Signature : </label><input class="" type="text" id="decr-signature-selected">
</div>
</div>
</div> </div>
<script src='{% static "js/all_common.js" %}?v=1651853225'></script> <script src='{% static "js/all_common.js" %}?v=1651853225'></script>
<script src='{% static "js/admin/manage_makeups.js" %}?v=1651853225'></script> <script src='{% static "js/admin/manage_makeups.js" %}?v=1651853225'></script>
<script>
const extension_duration = {{extension_duration}};
</script>
{% endblock %} {% endblock %}
{% extends "base.html" %} {% extends "base.html" %}
{% load static %} {% load static %}
{% block body_class %}manage_regular_shifts{% endblock %}
{% block additionnal_css %} {% block additionnal_css %}
<link rel="stylesheet" href="{% static 'css/datatables/datatables.min.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 'css/admin/manage_regular_shifts.css' %}">
...@@ -96,6 +96,8 @@ ...@@ -96,6 +96,8 @@
var couchdb_dbname = '{{db}}'; var couchdb_dbname = '{{db}}';
var couchdb_server = '{{couchdb_server}}' + couchdb_dbname; var couchdb_server = '{{couchdb_server}}' + couchdb_dbname;
var committees_shift_id = '{{committees_shift_id}}'; var committees_shift_id = '{{committees_shift_id}}';
var exemptions_shift_id = '{{exemptions_shift_id}}';
const committees_shift_name = '{{committees_shift_name}}';
var ASSOCIATE_MEMBER_SHIFT = '{{ASSOCIATE_MEMBER_SHIFT}}'; var ASSOCIATE_MEMBER_SHIFT = '{{ASSOCIATE_MEMBER_SHIFT}}';
</script> </script>
<script src='{% static "js/common.js" %}?v=1651853225'></script> <script src='{% static "js/common.js" %}?v=1651853225'></script>
......
...@@ -51,7 +51,16 @@ ...@@ -51,7 +51,16 @@
</div> </div>
</div> </div>
<div id="templates" style="display:none;"></div> <div id="templates" style="display:none;">
<div id="modal_delete_shift_registration">
<p>Enlever la présence de <b><span class="member_name"></span></b> au service du <b><span class="service_name"></span></b> ?</p>
<div id="makeup_case_explanation" style="display:none">
<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>
</div>
<br><label for="cancellation-explanation">Explication : </label><input class="" type="text" id="cancellation-explanation">
<br><br><label for="cancellation-signature">Signature : </label><input class="" type="text" id="cancellation-signature">
</div>
</div>
</div> </div>
<script src='{% static "js/all_common.js" %}?v=1651853225'></script> <script src='{% static "js/all_common.js" %}?v=1651853225'></script>
......
{% extends "members/base.html" %} {% extends "members/base.html" %}
{% block body_class %}inscriptions{% endblock %}
{% load static %} {% load static %}
{% block additionnal_css %} {% block additionnal_css %}
<link rel="stylesheet" href="{% static "css/inscriptions.css" %}?v=1651853225"> <link rel="stylesheet" href="{% static "css/inscriptions.css" %}?v=1651853225">
...@@ -80,15 +81,25 @@ ...@@ -80,15 +81,25 @@
{% endif %} {% endif %}
{% if can_create_binome %} {% if can_create_binome %}
<p id="add_binome" >+ Binomes (facultatif)</p> <div>Voulez-vous inscrire la personne en tant que deuxième personne d'un binôme ?</div>
<label>
<input type="radio" name="binome" id="no_binome" required>
Non
</label><br>
<label>
<input type="radio" name="binome" id="add_binome" required>
Oui
</label><br>
<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 binôme avec un.e membre existant.e
</div> </div>
{% if ASSOCIATE_MEMBER_SHIFT %} {% 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 binôme avec un nouveau membre
</div> </div>
{% endif %} {% endif %}
</div> </div>
...@@ -186,6 +197,7 @@ ...@@ -186,6 +197,7 @@
var max_begin_hour = '{{max_begin_hour}}'; var max_begin_hour = '{{max_begin_hour}}';
var email_domain = '{{email_domain}}'; var email_domain = '{{email_domain}}';
var committees_shift_id = '{{committees_shift_id}}'; var committees_shift_id = '{{committees_shift_id}}';
var exemptions_shift_id = '{{exemptions_shift_id}}';
</script> </script>
<script src="{% static "js/all_common.js" %}?v=1651853225"></script> <script src="{% static "js/all_common.js" %}?v=1651853225"></script>
......
...@@ -106,6 +106,8 @@ ...@@ -106,6 +106,8 @@
var mag_place_string = '{{mag_place_string}}'; var mag_place_string = '{{mag_place_string}}';
var office_place_string = '{{office_place_string}}' var office_place_string = '{{office_place_string}}'
var max_begin_hour = '{{max_begin_hour}}' var max_begin_hour = '{{max_begin_hour}}'
var committees_shift_id = '{{committees_shift_id}}';
var exemptions_shift_id = '{{exemptions_shift_id}}';
</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/common.js" %}"?v=1651853225></script> <script src="{% static "js/common.js" %}"?v=1651853225></script>
......
...@@ -7,6 +7,11 @@ ...@@ -7,6 +7,11 @@
ou ... ou ...
<br /> <br />
<button class="flex-container--column" data-select="Volant">Volant</button> <button class="flex-container--column" data-select="Volant">Volant</button>
{% if exemptions_shift_id != 0 %}
ou ...
<br />
<button class="flex-container--column" data-select="Exemption">Exempté</button>
{% endif %}
<div class="info"></div> <div class="info"></div>
</div> </div>
<div class="col-5 main_content"> <div class="col-5 main_content">
......
...@@ -9,6 +9,11 @@ ...@@ -9,6 +9,11 @@
<br /> <br />
<button class="flex-container--column" data-select="Volant">Volant</button> <button class="flex-container--column" data-select="Volant">Volant</button>
{% endif %} {% endif %}
{% if exemptions_shift_id != 0 %}
ou ...
<br />
<button class="flex-container--column" data-select="Exemption">Exempté</button>
{% endif %}
<div class="info"></div> <div class="info"></div>
</div> </div>
<div class="col-5 main_content"> <div class="col-5 main_content">
......
...@@ -488,18 +488,21 @@ ...@@ -488,18 +488,21 @@
</div> </div>
</div> </div>
<button type="button" class="accordion btn_faq"> <button type="button" class="accordion btn_faq">
<span class="full_width" >009 Faire une autre demande au BDM: seulement si les autres formulaires ne correspondent pas à ma demande </span> <span class="full_width" >009 Faire une autre demande au BDM : seulement si les autres formulaires ne correspondent pas à ma demande </span>
</button> </button>
<div class="input-container panel"> <div class="input-container panel">
<div class="grp_text"> <div class="grp_text">
<br> <div>
Nous avons créé des formulaires spécifiques pour la plupart des problèmes rencontrés par les membres. Changer de créneau, créer un binôme, ajouter un produit à la gamme, partir en vacances... <br /> Nous avons créé des formulaires spécifiques pour la plupart des problèmes rencontrés par les membres. Changer de créneau, créer un binôme, ajouter un produit à la gamme, partir en vacances... <br />
Cela dit, nous en découvrons de nouveaux tous les jours.<br /> Cela dit, nous en découvrons de nouveaux tous les jours.<br />
Si tu n'as pas su quel formulaire remplir, tu es au bon endroit. <br /> Si tu n'as pas su quel formulaire remplir, tu es au bon endroit. <br />
Vas-y dit nous tout !<br /><br /> Vas-y dit nous tout !<br /><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 de 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.<br /> </div>
<div class="block_service_exchange">
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 de 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.<br />
Merci de ne pas contacter le Bureau des membres pour cela, il te donnera exactement la même réponse.<br /> Merci de ne pas contacter le Bureau des membres pour cela, il te donnera exactement la même réponse.<br />
</div>
<div class="faq_link_button_area"> <div class="faq_link_button_area">
<a <a
href="javascript:void(0);" href="javascript:void(0);"
......
...@@ -70,9 +70,12 @@ ...@@ -70,9 +70,12 @@
Échange de services Échange de services
</div> </div>
<div class="tile_content"> <div class="tile_content">
<div> <div class="block_service_exchange">
Un empêchement ? J'anticipe et déplace mes services jusqu'à 24h avant leur début ! Un empêchement ? J'anticipe et déplace mes services jusqu'à 24h avant leur début !
</div> </div>
<div class="free_service_exchange">
Un empêchement ? J'anticipe et déplace mon service le plus tôt possible !
</div>
<div class="home_link_button_area"> <div class="home_link_button_area">
<button type="button" class="btn--primary home_link_button" id="go_to_shifts_calendar"> <button type="button" class="btn--primary home_link_button" id="go_to_shifts_calendar">
Accéder au calendrier d'échange de services Accéder au calendrier d'échange de services
...@@ -129,7 +132,8 @@ ...@@ -129,7 +132,8 @@
</div> </div>
<script> <script>
var max_begin_hour = "{{max_begin_hour}}", var max_begin_hour = "{{max_begin_hour}}",
type = 2; type = 2,
exemptions_shift_id = '{{exemptions_shift_id}}';
</script> </script>
{% endif %} {% endif %}
</div> </div>
...@@ -93,7 +93,7 @@ ...@@ -93,7 +93,7 @@
</div> </div>
<div id="cant_have_delay_msg_template"> <div id="cant_have_delay_msg_template">
<h3>Bonjour, tu avais 6 mois pour rattraper tes services manqués et il semblerait que tu ne l'aies pas fait.</h3> <h3>Bonjour, tu avais {{extension_duration}} mois pour rattraper tes services manqués et il semblerait que tu ne l'aies pas fait.</h3>
<h3>Tu ne peux plus sélectionner de rattrapages sur ton espace membre pour le moment, <h3>Tu ne peux plus sélectionner de rattrapages sur ton espace membre pour le moment,
merci de contacter le bureau des membres pour résoudre ce problème en remplissant ce formulaire : </h3> merci de contacter le bureau des membres pour résoudre ce problème en remplissant ce formulaire : </h3>
</div> </div>
...@@ -101,6 +101,14 @@ ...@@ -101,6 +101,14 @@
<h3>Vous êtes inscrit.e dans le service des comités, vous n'avez pas accès au calendrier d'échange des services car vous vous organisez directement avec le responsable du comité. Si vous avez des rattrapages à réaliser, merci de contacter le responsable du comité qui vous aidera à planifier les rattrapages ou trouver une solution.</h3> <h3>Vous êtes inscrit.e dans le service des comités, vous n'avez pas accès au calendrier d'échange des services car vous vous organisez directement avec le responsable du comité. Si vous avez des rattrapages à réaliser, merci de contacter le responsable du comité qui vous aidera à planifier les rattrapages ou trouver une solution.</h3>
<h3>Le calendrier ci-dessous est en lecture seule</h3> <h3>Le calendrier ci-dessous est en lecture seule</h3>
</div> </div>
<div id="comite_my_shifs_message">
<p data-type="nb_of_shifs_state">
Vous avez actuellement <span data-type="shifts_nb"></span> <span data-type="service_txt">service</span> d'avance.
</p>
<p data-type="next_ftop_shift">
Le système informatique viendra prélever un service sur votre compteur le <span data-type="next_ftop_shift_date"></span>.
</p>
</div>
</div> </div>
</div> </div>
...@@ -129,14 +137,16 @@ ...@@ -129,14 +137,16 @@
"partner_id":"{{partnerData.id}}", "partner_id":"{{partnerData.id}}",
"name":"{{partnerData.display_name|safe}}", "name":"{{partnerData.display_name|safe}}",
"shift_type":"{{partnerData.shift_type}}", "shift_type":"{{partnerData.shift_type}}",
"final_ftop_point":{{partnerData.final_ftop_point}},
"final_standard_point":{{partnerData.final_standard_point}},
"date_delay_stop":"{{partnerData.date_delay_stop}}", "date_delay_stop":"{{partnerData.date_delay_stop}}",
"cooperative_state":"{{partnerData.cooperative_state}}", "cooperative_state":"{{partnerData.cooperative_state}}",
"regular_shift_name":"{{partnerData.regular_shift_name}}", "regular_shift_name":"{{partnerData.regular_shift_name}}",
"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|safe}}", "street" : "{{partnerData.street}}",
"street2" : "{{partnerData.street2|safe}}", "street2" : "{{partnerData.street2}}",
"zip" : "{{partnerData.zip}}", "zip" : "{{partnerData.zip}}",
"city" : "{{partnerData.city|safe}}", "city" : "{{partnerData.city|safe}}",
"mobile" : "{{partnerData.mobile}}", "mobile" : "{{partnerData.mobile}}",
...@@ -154,7 +164,9 @@ ...@@ -154,7 +164,9 @@
"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}}';
var block_service_exchange_24h_before = '{{block_service_exchange_24h_before}}';
const canAddShift = {{canAddShift}}; const canAddShift = {{canAddShift}};
const extension_duration = {{extension_duration}};
</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/common.js" %}?v=1651853225"></script> <script src="{% static "js/common.js" %}?v=1651853225"></script>
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
<div id="suspended_content" class="shifts_exchange_page_content"> <div id="suspended_content" class="shifts_exchange_page_content">
<h3> <h3>
J'ai <span class="makeups_nb"></span> rattrapage(s) à effectuer, je dois le(s) sélectionner pour pouvoir refaire mes courses. J'ai <span class="makeups_nb"></span> rattrapage(s) à effectuer, je dois le(s) sélectionner pour pouvoir refaire mes courses.
J'ai 6 mois de délai pour le(s) rattraper. J'ai {{extension_duration}} mois de délai pour le(s) rattraper.
</h3> </h3>
<h3> <h3>
Si besoin, je peux contacter le Bureau des membres via la rubrique "J'ai une demande" pour expliquer ma situation. Si besoin, je peux contacter le Bureau des membres via la rubrique "J'ai une demande" pour expliquer ma situation.
......
...@@ -15,9 +15,6 @@ ...@@ -15,9 +15,6 @@
( jusqu'au <span class="delay_date_stop"></span> ) ( jusqu'au <span class="delay_date_stop"></span> )
</div> </div>
<div id="member_status_action"> <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"> <button type="button" class="btn--danger choose_makeups">
Je sélectionne mes rattrapages Je sélectionne mes rattrapages
</button> </button>
......
{% extends "base.html" %}
{% load static %}
{% block additionnal_scripts %}
{% endblock %}
{% block content %}
<form enctype="multipart/form-data" action="/export_orders" method="post">
{% csrf_token %}
<label for="from">Entre :</label>
<input type="date" name="from">
<label for="to">et :</label>
<input type="date" name="to">
<input type="submit" class='btn--primary' value="OK">
</form>
<script src="{% static "js/all_common.js" %}?v=1651853225"></script>
{% endblock %}
\ No newline at end of file
...@@ -245,6 +245,7 @@ ...@@ -245,6 +245,7 @@
var add_all_left_is_good_prices = "{{ADD_ALL_LEFT_IS_GOOD_PRICES}}" var add_all_left_is_good_prices = "{{ADD_ALL_LEFT_IS_GOOD_PRICES}}"
var add_products_pswd = "{{add_products_pswd}}" var add_products_pswd = "{{add_products_pswd}}"
var update_qty_pswd = "{{update_qty_pswd}}" var update_qty_pswd = "{{update_qty_pswd}}"
var allow_four_digits_in_reception_price = '{{allow_four_digits_in_reception_price}}';
</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/barcodes.js" %}?v=1651853225'></script> <script src='{% static "js/barcodes.js" %}?v=1651853225'></script>
......
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
</div> </div>
<div class="col-3 center"> <div class="col-3 center">
<label for="edition_input" id="edition_input_label">Qté : </label> <label for="edition_input" id="edition_input_label">Qté : </label>
<input type="text" class="number input_small" id="edition_input" autocomplete="off" required> <input type="number" class="input_small" id="edition_input" autocomplete="off" required>
<span id="product_uom"></span> <span id="product_uom"></span>
<i class="fa fa-undo" id="reset_to_previous_qty" style="display:none;"></i> <i class="fa fa-undo" id="reset_to_previous_qty" style="display:none;"></i>
</div> </div>
......
...@@ -24,6 +24,11 @@ ...@@ -24,6 +24,11 @@
<p><button type="submit">Connexion</button></p> <p><button type="submit">Connexion</button></p>
</form> </form>
<p style="color:red;">{{msg}}</p> <p style="color:red;">{{msg}}</p>
{% if permanent_message %}
<div id="permanent_message">
{{permanent_message}}
</div>
{% endif %}
<!--<p><a href="oubli_pass">Mot de passe oublié</a></p>--> <!--<p><a href="oubli_pass">Mot de passe oublié</a></p>-->
<script> <script>
try { try {
......
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