Commit a06fa29c by Etienne Freiss

Merge branch 'dev_cooperatic' into 2209-see-old-envelops

parents 5b449b09 073ef1f7
Pipeline #1771 passed with stage
in 1 minute 38 seconds
......@@ -48,7 +48,7 @@
border-bottom: 1px solid #d9534f;
position: absolute;
content: "";
width: 95%;
width: 100%;
height: 50%;
display: none;
}
......
......@@ -130,7 +130,6 @@ function set_envelop_dom(envelop, envelop_name, envelop_content_id, envelop_inde
envelop_panel.append(`<button class="btn--danger delete_envelop_button item-fluid" id="update_envelop_${envelop.type}_${envelop_index}">Supprimer l'enveloppe</button>`);
envelop_panel.append(`<button class="btn--primary update_envelop_button item-fluid" id="update_envelop_${envelop.type}_${envelop_index}">Modifier</button>`);
$(".update_envelop_button").off("click");
$(".update_envelop_button").on("click", function() {
let el_id = $(this).attr("id")
......@@ -403,7 +402,7 @@ function delete_envelop(envelop) {
* @param {String} index
*/
function archive_envelop(type, index) {
if (is_time_to('archive_envelop', 1000)) {
if (is_time_to('archive_envelop', 5000)) {
$('#envelop_cashing_error').hide();
$('#envelop_cashing_success').hide();
// Loading on
......@@ -460,6 +459,8 @@ function archive_envelop(type, index) {
alert('Erreur serveur. Merci de ne pas ré-encaisser l\'enveloppe qui a causé l\'erreur.');
}
});
} else {
alert("Par sécurité, il faut attendre 5s entre l'encaissement de deux enveloppes.");
}
}
......
......@@ -52,7 +52,7 @@ def archive_envelop(request):
# Immediately save a token than this payment has been saved
# If an error occurs, this payment won't be saved again
envelop['envelop_content'][partner_id]['payment_id'] = res['payment_id']
updated_envelop = m.c_db.updateDoc(envelop);
updated_envelop = m.c_db.updateDoc(envelop)
envelop['_rev'] = updated_envelop['_rev']
else:
# Handling error when saving payment, return data to display error message
......
......@@ -766,7 +766,7 @@ class CagetteMember(models.Model):
# member = CagetteMember(m['id'], m['email'])
# m['next_shifts'] = member.get_next_shift()
if not m['parent_name'] is False:
m['name'] += ' / ' + m['parent_name']
m['name'] += ' (en binôme avec ' + m['parent_name'] + ')'
del m['parent_name']
members.append(m)
......@@ -1206,7 +1206,7 @@ class CagetteServices(models.Model):
for m in s['members']:
for a in associated:
if int(a['parent_id'][0]) == int(m['partner_id'][0]):
m['partner_id'][1] += ' / ' + a['name']
m['partner_id'][1] += ' en binôme avec ' + a['name']
return services
......
......@@ -72,3 +72,6 @@ h1 .member_name {font-weight: bold;}
#member_advice {background: #FFF; color: red;}
.easy_shift_validate {text-align: center; margin-top: 3em;}
.button_is_member {background-color: #439D44; color: #fff;}
.button_is_associated_people {background: #0275D8; color: #fff;}
......@@ -93,8 +93,9 @@ function fill_member_slide(member) {
html_elts.image_medium.html('<img src="'+img_src+'" width="128" />');
html_elts.cooperative_state.html(member.cooperative_state);
if (member.cooperative_state == 'Rattrapage') {
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)
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);
}
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');
......@@ -147,14 +148,16 @@ function preview_results() {
for (i in results) {
if (results[i].is_member != false) {
var m = $('<button style="background-color: #00A573;">').attr('data-i', i)
.text(results[i].name);
var m = $('<button class="button_is_member">').attr('data-i', i)
.text(results[i].barcode_base + ' - ' + results[i].name);
html_elts.multi_results.append(m);
}
if (results[i].is_associated_people != false) {
var m = $('<button style="background: #0275D8;">').attr('data-i', i)
.text(results[i].name);
m = $('<button class="button_is_associated_people"></button_is_member>').attr('data-i', i)
.text('B ' + results[i].barcode_base + ' - ' + results[i].name);
html_elts.multi_results.append(m);
}
......
......@@ -197,7 +197,7 @@
min-width: 200px;
}
#date_planned_input, #coverage_days_input, #stats_date_period_select {
#date_planned_input, #coverage_days_input, #targeted_amount_input, #percent_adjust_input, #stats_date_period_select {
border-radius: 3px;
}
......@@ -210,11 +210,11 @@
margin-right: 3px;
}
#coverage_days_input, #percent_adjust_input {
#coverage_days_input, #targeted_amount_input, #percent_adjust_input {
display: block;
}
#coverage_days_input {
#coverage_days_input, #targeted_amount_input {
margin-bottom: 3px;
}
......@@ -321,7 +321,7 @@
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin: 30px 0 20px 0;
margin: 15px 0;
position: -webkit-sticky;
position: sticky;
top: 140px;
......@@ -329,8 +329,8 @@
}
.supplier_pill {
background-color: #a0daff;
border: 1px solid #6ea8cc;
background-color: #ffebcd;
border: 2px solid black;
}
.pill_supplier_name {
......
......@@ -16,6 +16,7 @@ var dbc = null,
order_doc = {
_id: null,
coverage_days: null,
targeted_amount: null,
stats_date_period: '',
last_update: {
timestamp: null,
......@@ -29,7 +30,7 @@ var dbc = null,
var clicked_order_pill = null;
var timerId
var timerId;
/* - UTILS */
/**
......@@ -43,6 +44,7 @@ function reset_data() {
order_doc = {
_id: null,
coverage_days: null,
targeted_amount: null,
stats_date_period: '',
last_update : {
timestamp: null,
......@@ -117,9 +119,9 @@ function _compute_stats_date_from() {
}
function debounceFunction(func, delay = 1000) {
clearTimeout(timerId)
clearTimeout(timerId);
timerId = setTimeout(func, delay)
timerId = setTimeout(func, delay);
}
/* - PRODUCTS */
......@@ -188,50 +190,105 @@ function add_product() {
return 0;
}
function compute_purchase_qty_for_coverage(product, coeff, stock, incoming_qty, daily_conso, days) {
let purchase_qty_for_coverage = null;
purchase_qty_for_coverage = days * daily_conso - stock - incoming_qty + product.minimal_stock;
purchase_qty_for_coverage = (purchase_qty_for_coverage < 0) ? 0 : purchase_qty_for_coverage;
// Reduce to nb of packages to purchase
purchase_package_qty_for_coverage = purchase_qty_for_coverage / product.suppliersinfo[0].package_qty;
if (coeff != 1) {
purchase_package_qty_for_coverage *= coeff;
}
// return Round up to unit for all products
return Math.ceil(purchase_package_qty_for_coverage);
}
function compute_and_affect_product_supplier_quantities(coeff, days) {
for (const [
key,
product
] of Object.entries(products)) {
if ('suppliersinfo' in product && product.suppliersinfo.length > 0) {
let purchase_qty_for_coverage = null;
// Durée couverture produit = (stock + qté entrante + qté commandée ) / conso quotidienne
const stock = product.qty_available;
const incoming_qty = product.incoming_qty;
const daily_conso = product.daily_conso;
purchase_package_qty_for_coverage = compute_purchase_qty_for_coverage(product, coeff, stock, incoming_qty, daily_conso, days);
// Set qty to purchase for first supplier only
products[key].suppliersinfo[0].qty = purchase_package_qty_for_coverage;
}
}
}
/**
* Compute the qty to buy for each product, depending the coverage days.
* Set the computed qty for the first supplier only.
*/
function compute_products_coverage_qties() {
const pc_adjust = $('#percent_adjust_input').val();
let coeff = 1;
if (!isNaN(parseFloat(pc_adjust))) {
coeff = (1 + parseFloat(pc_adjust) /100);
}
if (order_doc.coverage_days != null) {
return new Promise((resolve) => {
const pc_adjust = $('#percent_adjust_input').val();
let coeff = 1;
if (!isNaN(parseFloat(pc_adjust))) {
coeff = (1 + parseFloat(pc_adjust) /100);
}
order_doc.coeff = coeff;
for (const [
key,
product
] of Object.entries(products)) {
if ('suppliersinfo' in product && product.suppliersinfo.length > 0) {
let purchase_qty_for_coverage = null;
// Durée couverture produit = (stock + qté entrante + qté commandée ) / conso quotidienne
const stock = product.qty_available;
const incoming_qty = product.incoming_qty;
const daily_conso = product.daily_conso;
purchase_qty_for_coverage = order_doc.coverage_days * daily_conso - stock - incoming_qty + product.minimal_stock;
purchase_qty_for_coverage = (purchase_qty_for_coverage < 0) ? 0 : purchase_qty_for_coverage;
// Reduce to nb of packages to purchase
purchase_package_qty_for_coverage = purchase_qty_for_coverage / product.suppliersinfo[0].package_qty;
if (order_doc.coeff != 1) {
purchase_package_qty_for_coverage *= order_doc.coeff;
if (order_doc.coverage_days != null) {
compute_and_affect_product_supplier_quantities(coeff, order_doc.coverage_days);
resolve();
} else if (order_doc.targeted_amount != null) {
const small_step = 0.1,
max_iter = 182; // Assume that no more than 1/2 year coverage is far enough
let go_on = true,
iter = 0,
days = 1,
step = 1;
//Let's compute the nearst amount, by changing days quantity
while(go_on == true && iter < max_iter) {
order_total_value = 0;
compute_and_affect_product_supplier_quantities(coeff, days);
_compute_total_values_by_supplier();
for (let supplier of selected_suppliers) {
order_total_value += supplier.total_value;
}
let order_total_value_f = parseFloat(order_total_value),
targeted_amount_f = parseFloat(order_doc.targeted_amount);
if (order_total_value_f >= targeted_amount_f) {
if (order_total_value_f != targeted_amount_f && iter < max_iter) {
step = small_step; // we have gone too far, let's go back, using small step
days -= step;
}
/* console.log(iter)
console.log(order_total_value_f + '/' + targeted_amount_f)
console.log(days)
console.log(go_on)*/
} else {
if (step == small_step) {
// amount was above the target, let's compute again with the previous value
go_on = false;
compute_and_affect_product_supplier_quantities(coeff, days + step);
} else {
days += step;
}
}
// Round up to unit for all products
purchase_package_qty_for_coverage = Math.ceil(purchase_package_qty_for_coverage);
// Set qty to purchase for first supplier only
products[key].suppliersinfo[0].qty = purchase_package_qty_for_coverage;
iter++;
}
resolve();
}
}
/* console.log(rder_doc.coverage_days);
console.log(order_doc.targeted_amount)*/
});
}
/**
......@@ -295,15 +352,16 @@ function check_products_data() {
// Remove fetched product id from loaded products list
const loaded_p_index = loaded_products_ids.indexOf(p_id);
if (loaded_p_index > -1) {
loaded_products_ids.splice(loaded_p_index, 1);
}
}
$('.notifyjs-wrapper').trigger('notify-hide');
/**
* If loaded p_ids are remaining:
* If loaded p_ids are remaining:
* these products were loaded but don't match the conditions to be fetched anymore.
* Remove them.
*/
......@@ -311,8 +369,9 @@ function check_products_data() {
for (pid of loaded_products_ids) {
const p_index = products.findIndex(p => p.id == pid);
const p_name = products[p_index].name;
products.splice(p_index, 1);
$.notify(
`Produit "${p_name}" retiré de la commande.\nIl a probablement été passé en archivé ou en NPA sur un autre poste.`,
{
......@@ -735,8 +794,10 @@ function commit_actions_on_product(product, inputs) {
id: product.id,
name: product.name
};
inputs.each(function (i,e) {
const input = $(e)
inputs.each(function (i, e) {
const input = $(e);
if (input.attr('name') == 'npa-actions') {
if (input.prop('checked') == true) {
actions.npa.push(input.val());
......@@ -800,7 +861,7 @@ function commit_actions_on_product(product, inputs) {
try {
if (data.responseJSON.code === "archiving_with_incoming_qty") {
alert("Ce produit a des quantités entrantes, vous ne pouvez pas l'archiver.")
alert("Ce produit a des quantités entrantes, vous ne pouvez pas l'archiver.");
} else if (data.responseJSON.code === "error_stock_update") {
alert('Erreur lors de la mise à zéro du stock du produit archivé. Les actions ont bien été réalisées.');
} else {
......@@ -1695,7 +1756,10 @@ function display_products(params) {
e.preventDefault();
// On arrow up pressed, focus next row input
let next_input = $(this).closest("tr").prev().find(".product_qty_input");
let next_input = $(this).closest("tr")
.prev()
.find(".product_qty_input");
next_input.focus();
// Scroll to a position where the target input is not hidden by the sticky suppliers container
......@@ -1703,7 +1767,7 @@ function display_products(params) {
$("#suppliers_container").offset().top
- $(window).scrollTop()
+ $("#suppliers_container").outerHeight();
const next_input_top_offset = next_input.offset().top - $(window).scrollTop();
const next_input_top_offset = next_input.offset().top - $(window).scrollTop();
if (next_input_top_offset < suppliers_container_top_offset) {
window.scrollTo({
......@@ -1714,30 +1778,40 @@ function display_products(params) {
e.preventDefault();
// On arrow down pressed, focus previous row input
$(this).closest("tr").next().find(".product_qty_input").focus();
$(this).closest("tr")
.next()
.find(".product_qty_input")
.focus();
} else if (e.which == 13) {
e.preventDefault();
// On enter pressed, focus previous row input
$(this).closest("tr").next().find(".product_qty_input").focus();
$(this).closest("tr")
.next()
.find(".product_qty_input")
.focus();
}
})
.on('click', 'tbody td .product_actions', function(e){
// Save / unsave selected row
.on('click', 'tbody td .product_actions', function(e) {
// Save / unsave selected row
const p_id = products_table.row($(this).closest('tr')).data().id;
const product = products.find(p => p.id == p_id);
let modal_product_actions = $('#templates #modal_product_actions');
modal_product_actions.find(".product_name").text(product.name);
const product_can_be_archived = product.incoming_qty === 0;
if (product_can_be_archived == true) {
modal_product_actions.find('input[name="archive-action"]').prop( "disabled", false );
modal_product_actions.find('input[name="archive-action"]').closest("label").removeClass( "checkbox_action_disabled" );
modal_product_actions.find('input[name="archive-action"]').prop("disabled", false);
modal_product_actions.find('input[name="archive-action"]').closest("label")
.removeClass("checkbox_action_disabled");
} else {
modal_product_actions.find('input[name="archive-action"]').prop( "disabled", true );
modal_product_actions.find('input[name="archive-action"]').closest("label").addClass( "checkbox_action_disabled" );
modal_product_actions.find('input[name="archive-action"]').prop("disabled", true);
modal_product_actions.find('input[name="archive-action"]').closest("label")
.addClass("checkbox_action_disabled");
}
openModal(
......@@ -1750,7 +1824,7 @@ function display_products(params) {
'Valider',
false
);
modal.find('input[name="minimal_stock"]').val(product.minimal_stock)
modal.find('input[name="minimal_stock"]').val(product.minimal_stock);
});
......@@ -1968,7 +2042,11 @@ function update_main_screen(params) {
} else {
$("#coverage_days_input").val('');
}
if (order_doc.targeted_amount !== null) {
$('#targeted_amount_input').val(order_doc.targeted_amount);
} else {
$('#targeted_amount_input').val('');
}
if (order_doc.coeff && order_doc.coeff != 1) {
$("#percent_adjust_input").val(-Math.ceil((1 - order_doc.coeff) * 100));
}
......@@ -1981,7 +2059,7 @@ function update_main_screen(params) {
}
function display_average_consumption_explanation() {
openModal($('#explanations').html())
openModal($('#explanations').html());
}
/**
* Update DOM display on the order selection screen
......@@ -2169,18 +2247,28 @@ $(document).ready(function() {
$("#coverage_form").on("submit", function(e) {
e.preventDefault();
if (is_time_to('submit_coverage_form', 1000)) {
let val = $("#coverage_days_input").val();
val = parseInt(val);
let days_val = $("#coverage_days_input").val(),
amount_val = $('#targeted_amount_input').val();
days_val = parseInt(days_val);
amount_val = parseInt(amount_val);
if (isNaN(days_val)) days_val = null;
if (isNaN(amount_val)) amount_val = null;
if (days_val !== null || amount_val !== null) {
order_doc.coverage_days = days_val;
order_doc.targeted_amount = amount_val;
compute_products_coverage_qties()
.then(() => {
debounceFunction(update_cdb_order);
update_main_screen();
})
if (!isNaN(val)) {
order_doc.coverage_days = val;
compute_products_coverage_qties();
debounceFunction(update_cdb_order);
update_main_screen();
} else {
$("#coverage_days_input").val(order_doc.coverage_days);
alert(`Valeur non valide pour le nombre de jours de couverture !`);
$("#coverage_days_input").val(order_doc.coverage_days || '');
$('#targeted_amount_input').val(order_doc.targeted_amount || '');
alert("Ni le nombre de jours de couverture, ni le montant à atteindre sont correctement renseignés")
}
}
});
......@@ -2234,10 +2322,13 @@ $(document).ready(function() {
check_products_data()
.then(() => {
compute_products_coverage_qties();
update_main_screen();
debounceFunction(update_cdb_order);
closeModal();
compute_products_coverage_qties()
.then(() => {
update_main_screen();
debounceFunction(update_cdb_order);
closeModal();
})
});
}
});
......@@ -2355,7 +2446,7 @@ $(document).ready(function() {
return 0;
});
$(document).on("click",".average_consumption_explanation_icon", display_average_consumption_explanation)
$(document).on("click", ".average_consumption_explanation_icon", display_average_consumption_explanation);
$.datepicker.regional['fr'] = {
monthNames: [
......@@ -2493,18 +2584,18 @@ $(document).ready(function() {
}
});
$(document).on('click', '.accordion', function(){
$(document).on('click', '.accordion', function() {
/* Toggle between adding and removing the "active" class,
to highlight the button that controls the panel */
this.classList.toggle("active");
/* Toggle between hiding and showing the active panel */
var panel = this.nextElementSibling;
if (panel.style.display === "block") {
panel.style.display = "none";
panel.style.display = "none";
} else {
panel.style.display = "block";
panel.style.display = "block";
}
});
} else {
......
......@@ -52,7 +52,7 @@
<div class="row-1 grid-2">
<div class="col-1">
<div class="label">
Biper le badge, ou saisissez le n° de coop. ou le nom
Biper le badge, ou saisir le n° de coop. ou le nom
</div>
Recherche :
<input type="text" name="search_string" autocomplete="off" />
......@@ -112,7 +112,7 @@
<div id="next_shifts">
</div>
</div>
<a class="btn" data-next="first_page">Coopérateur suivant</a>
<a class="btn" data-next="first_page">Coopérateur.rice suivant.e</a>
</div>
<div class="col-1">
<section id="member_advice">
......@@ -208,7 +208,7 @@
{% endif %}
</section>
<div class="col-2"></div>
<a class="btn col-2" data-next="first_page">Coopérateur suivant</a>
<a class="btn col-2" data-next="first_page">Coopérateur.ice suivant.e</a>
<div class="col-2"></div>
</section>
<section class="grid-6 has-gutter" id="rattrapage_1">
......
......@@ -95,6 +95,7 @@
<form action="javascript:;" id="coverage_form" class="order_form_item">
<div class="input-wrapper">
<input type="number" name="coverage_days" id="coverage_days_input" placeholder="Nb jours de couverture" min="1">
<input type="number" name="targeted_amount" id="targeted_amount_input" placeholder="Montant en €" min="1">
<input type="number" name="percent_adjustement" id="percent_adjust_input" placeholder="ajustement en %">
</div>
<div>
......
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