Commit 2c422b94 by Damien Moulard

Merge branch '2251-shift-screen-associate' of…

Merge branch '2251-shift-screen-associate' of gl.cooperatic.fr:cooperatic-foodcoops/third-party into 2251-shift-screen-associate
parents 0b9eec41 d4e58ed9
Pipeline #1919 passed with stage
in 1 minute 29 seconds
...@@ -775,9 +775,12 @@ class CagetteMember(models.Model): ...@@ -775,9 +775,12 @@ class CagetteMember(models.Model):
cond = [['name', 'ilike', str(key)]] cond = [['name', 'ilike', str(key)]]
cond.append('|') cond.append('|')
cond.append(['is_member', '=', True]) cond.append(['is_member', '=', True])
if search_type != 'members':
cond.append(['is_associated_people', '=', True]) cond.append(['is_associated_people', '=', True])
else:
cond.append(['is_associated_people', '=', False])
# cond.append(['cooperative_state', '!=', 'unsubscribed']) # cond.append(['cooperative_state', '!=', 'unsubscribed'])
if search_type == "full": if search_type == "full" or search_type == 'members':
fields = CagetteMember.m_default_fields fields = CagetteMember.m_default_fields
if not shift_id is None: if not shift_id is None:
CagetteMember.m_default_fields.append('tmpl_reg_line_ids') CagetteMember.m_default_fields.append('tmpl_reg_line_ids')
...@@ -1303,7 +1306,7 @@ class CagetteServices(models.Model): ...@@ -1303,7 +1306,7 @@ class CagetteServices(models.Model):
return api.update('shift.registration', [int(registration_id)], f) return api.update('shift.registration', [int(registration_id)], f)
@staticmethod @staticmethod
def registration_cancel(registration_id, overrided_date=""): def reopen_registration(registration_id, overrided_date=""):
api = OdooAPI() api = OdooAPI()
f = {'state': 'open'} f = {'state': 'open'}
......
...@@ -23,6 +23,9 @@ ...@@ -23,6 +23,9 @@
[data-week="4"] {border: 2px #eed000 solid;} [data-week="4"] {border: 2px #eed000 solid;}
#new_coop [name="email"] {width:25em;} #new_coop [name="email"] {width:25em;}
#new_coop{
max-width: 75%;
}
#mail_generation {position:absolute; bottom:30px;} #mail_generation {position:absolute; bottom:30px;}
#sex {padding: 0;} #sex {padding: 0;}
......
...@@ -689,7 +689,7 @@ $(document).ready(function() { ...@@ -689,7 +689,7 @@ $(document).ready(function() {
if (search_str) { if (search_str) {
$.ajax({ $.ajax({
url: '/members/search/' + search_str, url: '/members/search/' + search_str+ "?search_type=members",
dataType : 'json', dataType : 'json',
success: function(data) { success: function(data) {
members_search_results = []; members_search_results = [];
......
...@@ -318,7 +318,7 @@ def record_service_presence(request): ...@@ -318,7 +318,7 @@ def record_service_presence(request):
del m['shifts'] del m['shifts']
m['next_shift'] = next_shift m['next_shift'] = next_shift
res['member'] = m res['member'] = m
else: CagetteServices.registration_cancel(rid, overrided_date) else: CagetteServices.reopen_registration(rid, overrided_date)
except Exception as e: except Exception as e:
res['error'] = str(e) res['error'] = str(e)
......
...@@ -217,6 +217,19 @@ td{ ...@@ -217,6 +217,19 @@ td{
color: white; color: white;
} }
.fc-event.shift_booked_makeup {
background-color: #f0ad4e;
cursor: auto;
border-color: #f0ad4e;
}
.fc-event.shift_booked_makeup td {
--fc-list-event-hover-bg-color:#f0ad4e;
}
.fc-list-event.shift_booked_makeup {
color: white;
}
#calendar .fc-list-table { #calendar .fc-list-table {
table-layout: auto; table-layout: auto;
} }
......
...@@ -116,17 +116,17 @@ function add_or_change_shift(new_shift_id) { ...@@ -116,17 +116,17 @@ function add_or_change_shift(new_shift_id) {
closeModal(); closeModal();
selected_shift = null; selected_shift = null;
if (error.status === 400 && error.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 === 500 && error.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. ` +
`Il est néanmoins possible que la requête ait abouti, ` + `Il est néanmoins possible que la requête ait abouti, ` +
`veuillez patienter quelques secondes puis vérifier vos services enregistrés.`); `veuillez patienter quelques secondes puis vérifier vos services enregistrés.`);
} else if (error.status === 400 && error.msg === "Bad arguments") { } else if (error.status === 400 && 'msg' in error.responseJSON && error.responseJSON.msg === "Bad arguments") {
alert(`Une erreur est survenue. ` + alert(`Une erreur est survenue. ` +
`Il est néanmoins possible que la requête ait abouti, ` + `Il est néanmoins possible que la requête ait abouti, ` +
`veuillez patienter quelques secondes puis vérifier vos services enregistrés.`); `veuillez patienter quelques secondes puis vérifier vos services enregistrés.`);
...@@ -306,17 +306,23 @@ function init_shifts_list() { ...@@ -306,17 +306,23 @@ function init_shifts_list() {
shift_line_template.find(".shift_line_time").text(datetime_shift_start.toLocaleTimeString("fr-fr", time_options)); shift_line_template.find(".shift_line_time").text(datetime_shift_start.toLocaleTimeString("fr-fr", time_options));
// Disable or not // Disable or not
if (!can_exchange_shifts() && block_actions_for_attached_people === "True") {
shift_line_template.find(".selectable_shift_line").removeClass("btn--primary"); shift_line_template.find(".selectable_shift_line").removeClass("btn--primary");
shift_line_template.find(".selectable_shift_line").removeClass("btn");
shift_line_template.find(".selectable_shift_line").removeClass("btn--warning");
if (!can_exchange_shifts() && block_actions_for_attached_people === "True") {
shift_line_template.find(".selectable_shift_line").addClass("btn"); shift_line_template.find(".selectable_shift_line").addClass("btn");
shift_line_template.find(".checkbox").prop("disabled", "disabled"); shift_line_template.find(".checkbox").prop("disabled", "disabled");
} else { } else {
shift_line_template.find(".selectable_shift_line").removeClass("btn"); if (shift.is_makeup==true) {
shift_line_template.find(".selectable_shift_line").addClass("btn--warning");
shift_line_template.find(".checkbox").prop("disabled", false);
shift_line_template.find(".checkbox").prop("value", shift.id);
} else {
shift_line_template.find(".selectable_shift_line").addClass("btn--primary"); shift_line_template.find(".selectable_shift_line").addClass("btn--primary");
shift_line_template.find(".checkbox").prop("disabled", false); shift_line_template.find(".checkbox").prop("disabled", false);
shift_line_template.find(".checkbox").prop("value", shift.id); shift_line_template.find(".checkbox").prop("value", shift.id);
} }
}
// Set assign shift button // Set assign shift button
if (partner_data.associated_partner_id === "False" && partner_data.parent_id === "False") { if (partner_data.associated_partner_id === "False" && partner_data.parent_id === "False") {
shift_line_template.find('.affect_associate_registered').hide(); shift_line_template.find('.affect_associate_registered').hide();
...@@ -531,7 +537,7 @@ function init_calendar_page() { ...@@ -531,7 +537,7 @@ function init_calendar_page() {
hiddenDays: hidden_days, hiddenDays: hidden_days,
events: '/shifts/get_list_shift_calendar/' + partner_data.concerned_partner_id, events: '/shifts/get_list_shift_calendar/' + partner_data.concerned_partner_id,
eventClick: function(info) { eventClick: function(info) {
if (!$(info.el).hasClass("shift_booked")) { 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;
// Set new shift // Set new shift
......
...@@ -9,7 +9,8 @@ var suppliers_list = [], ...@@ -9,7 +9,8 @@ var suppliers_list = [],
new_product_supplier_association = { new_product_supplier_association = {
package_qty: null, package_qty: null,
price: null price: null
}; },
qties_values = {};
var dbc = null, var dbc = null,
sync = null, sync = null,
...@@ -30,6 +31,10 @@ var dbc = null, ...@@ -30,6 +31,10 @@ var dbc = null,
var clicked_order_pill = null; var clicked_order_pill = null;
let userAgent = navigator.userAgent;
var timerId; var timerId;
/* - UTILS */ /* - UTILS */
...@@ -124,12 +129,74 @@ function debounceFunction(func, delay = 1000) { ...@@ -124,12 +129,74 @@ function debounceFunction(func, delay = 1000) {
timerId = setTimeout(func, delay); timerId = setTimeout(func, delay);
} }
/* - PRODUCTS */ /* - PRODUCTS */
var process_new_product_qty = function(input) {
// Remove line coloring on input blur
const row = $(input).closest('tr');
row.removeClass('focused_line');
let val = ($(input).val() == '') ? 0 : $(input).val();
const id_split = $(input).attr('id')
.split('_');
const prod_id = id_split[1];
const supplier_id = id_split[3];
if (val == -1) {
let modal_end_supplier_product_association = $('#templates #modal_end_supplier_product_association');
const product = products.find(p => p.id == prod_id);
modal_end_supplier_product_association.find(".product_name").text(product.name);
const supplier = selected_suppliers.find(s => s.id == supplier_id);
modal_end_supplier_product_association.find(".supplier_name").text(supplier.display_name);
openModal(
modal_end_supplier_product_association.html(),
() => {
if (is_time_to('validate_end_supplier_product_association')) {
end_supplier_product_association(product, supplier);
}
},
'Valider',
false,
true,
() => {
// Reset value in input on cancel
const psi = product.suppliersinfo.find(psi_item => psi_item.supplier_id == supplier_id);
$(input).val(psi.qty);
}
);
} else {
val = parseFloat(val);
// If value is a number
if (!isNaN(val)) {
// Save value
save_product_supplier_qty(prod_id, supplier_id, val);
// Update row
const product = products.find(p => p.id == prod_id);
const new_row_data = prepare_datatable_data([product.id])[0];
products_table.row($(input).closest('tr')).data(new_row_data)
.draw();
debounceFunction(update_cdb_order);
display_total_values();
} else {
$(input).val('');
}
}
};
/** /**
* Add a product. * Add a product.
* *
* @returns -1 if validation failed, 0 otherwise * @returns -1 if validation failed, 0 otherwise
*/ */
function add_product() { function add_product() {
const user_input = $("#product_input").val(); const user_input = $("#product_input").val();
...@@ -191,7 +258,13 @@ function add_product() { ...@@ -191,7 +258,13 @@ function add_product() {
return 0; return 0;
} }
function compute_purchase_qty_for_coverage(product, coeff, stock, incoming_qty, daily_conso, days) { function compute_purchase_qty_for_coverage(product, coeff, stock, incoming_qty, daily_conso, days) {
let purchase_qty_for_coverage = null; let purchase_qty_for_coverage = 0,
purchase_package_qty_for_coverage = 0;
if (stock == 0 && daily_conso == 0) {
purchase_package_qty_for_coverage = 1;
} else {
purchase_qty_for_coverage = days * daily_conso - stock - incoming_qty + product.minimal_stock; 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; purchase_qty_for_coverage = (purchase_qty_for_coverage < 0) ? 0 : purchase_qty_for_coverage;
...@@ -201,6 +274,7 @@ function compute_purchase_qty_for_coverage(product, coeff, stock, incoming_qty, ...@@ -201,6 +274,7 @@ function compute_purchase_qty_for_coverage(product, coeff, stock, incoming_qty,
if (coeff != 1) { if (coeff != 1) {
purchase_package_qty_for_coverage *= coeff; purchase_package_qty_for_coverage *= coeff;
} }
}
// return Round up to unit for all products // return Round up to unit for all products
return Math.ceil(purchase_package_qty_for_coverage); return Math.ceil(purchase_package_qty_for_coverage);
} }
...@@ -217,6 +291,7 @@ function compute_and_affect_product_supplier_quantities(coeff, days) { ...@@ -217,6 +291,7 @@ function compute_and_affect_product_supplier_quantities(coeff, days) {
const stock = product.qty_available; const stock = product.qty_available;
const incoming_qty = product.incoming_qty; const incoming_qty = product.incoming_qty;
const daily_conso = product.daily_conso; const daily_conso = product.daily_conso;
purchase_package_qty_for_coverage = compute_purchase_qty_for_coverage(product, coeff, stock, incoming_qty, daily_conso, days); 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 // Set qty to purchase for first supplier only
products[key].suppliersinfo[0].qty = purchase_package_qty_for_coverage; products[key].suppliersinfo[0].qty = purchase_package_qty_for_coverage;
...@@ -234,6 +309,7 @@ function compute_products_coverage_qties() { ...@@ -234,6 +309,7 @@ function compute_products_coverage_qties() {
return new Promise((resolve) => { return new Promise((resolve) => {
const pc_adjust = $('#percent_adjust_input').val(); const pc_adjust = $('#percent_adjust_input').val();
let coeff = 1; let coeff = 1;
if (!isNaN(parseFloat(pc_adjust))) { if (!isNaN(parseFloat(pc_adjust))) {
coeff = (1 + parseFloat(pc_adjust) /100); coeff = (1 + parseFloat(pc_adjust) /100);
} }
...@@ -249,7 +325,7 @@ function compute_products_coverage_qties() { ...@@ -249,7 +325,7 @@ function compute_products_coverage_qties() {
step = 1; step = 1;
//Let's compute the nearst amount, by changing days quantity //Let's compute the nearst amount, by changing days quantity
while(go_on == true && iter < max_iter) { while (go_on == true && iter < max_iter) {
order_total_value = 0; order_total_value = 0;
compute_and_affect_product_supplier_quantities(coeff, days); compute_and_affect_product_supplier_quantities(coeff, days);
_compute_total_values_by_supplier(); _compute_total_values_by_supplier();
...@@ -1674,68 +1750,11 @@ function display_products(params) { ...@@ -1674,68 +1750,11 @@ function display_products(params) {
row.addClass('focused_line'); row.addClass('focused_line');
}); });
// Manage data on inputs blur
$('#products_table').on('blur', 'tbody td .product_qty_input', function () {
// Remove line coloring on input blur
const row = $(this).closest('tr');
row.removeClass('focused_line');
let val = ($(this).val() == '') ? 0 : $(this).val();
const id_split = $(this).attr('id')
.split('_');
const prod_id = id_split[1];
const supplier_id = id_split[3];
if (val == -1) {
let modal_end_supplier_product_association = $('#templates #modal_end_supplier_product_association');
const product = products.find(p => p.id == prod_id);
modal_end_supplier_product_association.find(".product_name").text(product.name);
const supplier = selected_suppliers.find(s => s.id == supplier_id);
modal_end_supplier_product_association.find(".supplier_name").text(supplier.display_name);
openModal(
modal_end_supplier_product_association.html(),
() => {
if (is_time_to('validate_end_supplier_product_association')) {
end_supplier_product_association(product, supplier);
}
},
'Valider',
false,
true,
() => {
// Reset value in input on cancel
const psi = product.suppliersinfo.find(psi_item => psi_item.supplier_id == supplier_id);
$(this).val(psi.qty);
}
);
} else {
val = parseFloat(val);
// If value is a number // Manage data on inputs blur
if (!isNaN(val)) { $('#products_table')
// Save value .on('blur', 'tbody td .product_qty_input', function () {
save_product_supplier_qty(prod_id, supplier_id, val); process_new_product_qty(this);
// Update row
const product = products.find(p => p.id == prod_id);
const new_row_data = prepare_datatable_data([product.id])[0];
products_table.row($(this).closest('tr')).data(new_row_data)
.draw();
debounceFunction(update_cdb_order);
display_total_values();
} else {
$(this).val('');
}
}
}) })
.on('keypress', 'tbody td .product_qty_input', function(e) { .on('keypress', 'tbody td .product_qty_input', function(e) {
// Validate on Enter pressed // Validate on Enter pressed
...@@ -2254,12 +2273,12 @@ $(document).ready(function() { ...@@ -2254,12 +2273,12 @@ $(document).ready(function() {
.then(() => { .then(() => {
debounceFunction(update_cdb_order); debounceFunction(update_cdb_order);
update_main_screen(); update_main_screen();
}) });
} else { } else {
$("#coverage_days_input").val(order_doc.coverage_days || ''); $("#coverage_days_input").val(order_doc.coverage_days || '');
$('#targeted_amount_input').val(order_doc.targeted_amount || ''); $('#targeted_amount_input').val(order_doc.targeted_amount || '');
alert("Ni le nombre de jours de couverture, ni le montant à atteindre sont correctement renseignés") alert("Ni le nombre de jours de couverture, ni le montant à atteindre sont correctement renseignés");
} }
} }
...@@ -2319,7 +2338,7 @@ $(document).ready(function() { ...@@ -2319,7 +2338,7 @@ $(document).ready(function() {
update_main_screen(); update_main_screen();
debounceFunction(update_cdb_order); debounceFunction(update_cdb_order);
closeModal(); closeModal();
}) });
}); });
} }
...@@ -2590,6 +2609,30 @@ $(document).ready(function() { ...@@ -2590,6 +2609,30 @@ $(document).ready(function() {
panel.style.display = "block"; panel.style.display = "block";
} }
}); });
if (/Firefox\//.exec(userAgent)) {
// needed to prevent bug using number input arrow to change quantity (https://bugzilla.mozilla.org/show_bug.cgi?id=1012818)
// Have to capture mousedown and mouseup events, instead of using only click event
// Indeed, capturing click only remove the ability to click to have focus on the input to type a number.
$(document).on("mousedown", '[type="number"]', function() {
const clicked = this;
qties_values[$(clicked).attr('id')] = $(clicked).val();
});
$(document).on("mouseup", '[type="number"]', function() {
const clicked = this;
try {
if ($(clicked).val() != qties_values[$(clicked).attr('id')]) {
process_new_product_qty(clicked);
}
} catch (err) {
console.log(err);
}
});
}
} else { } else {
$('#not_connected_content').show(); $('#not_connected_content').show();
} }
......
...@@ -101,8 +101,11 @@ def get_list_shift_calendar(request, partner_id): ...@@ -101,8 +101,11 @@ def get_list_shift_calendar(request, partner_id):
use_new_members_space = getattr(settings, 'USE_NEW_MEMBERS_SPACE', False) use_new_members_space = getattr(settings, 'USE_NEW_MEMBERS_SPACE', False)
listRegisterPartner = [] listRegisterPartner = []
listMakeUpShift = []
for v in registerPartner: for v in registerPartner:
listRegisterPartner.append(v['id']) listRegisterPartner.append(v['id'])
if v['is_makeup']:
listMakeUpShift.append(v['id'])
start = request.GET.get('start') start = request.GET.get('start')
end = request.GET.get('end') end = request.GET.get('end')
...@@ -136,6 +139,9 @@ def get_list_shift_calendar(request, partner_id): ...@@ -136,6 +139,9 @@ def get_list_shift_calendar(request, partner_id):
if len(l) > 0: if len(l) > 0:
if use_new_members_space is True: if use_new_members_space is True:
if set(value['registration_ids']) & set(listRegisterPartner) & set(listMakeUpShift):
event["classNames"] = ["shift_booked_makeup"]
else :
event["classNames"] = ["shift_booked"] event["classNames"] = ["shift_booked"]
else: else:
event["className"] = "shift_booked" event["className"] = "shift_booked"
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
<section class="center" id="new_coop"> <section class="center" id="new_coop">
<div class="grid-1"> <div class="grid-1">
<div class=""> <div class="item-center">
<h2 class="title"> <h2 class="title">
NOUVEAU MEMBRE NOUVEAU MEMBRE
</h2> </h2>
......
...@@ -81,10 +81,12 @@ ...@@ -81,10 +81,12 @@
<div id="calendar_explaination_template"> <div id="calendar_explaination_template">
<h4>Légende du calendrier</h4> <h4>Légende du calendrier</h4>
<a class="example-event fc-daygrid-event fc-daygrid-block-event fc-h-event fc-event fc-event-start fc-event-end fc-event-future shift_booked"><div class="fc-event-main"><div class="fc-event-main-frame"><div class="fc-event-time">06:00</div><div class="fc-event-title-container"><div class="fc-event-title fc-sticky">&nbsp;- 9/12</div></div></div></div></a>
<p>Un service colorié en noir : je suis déjà inscrit.e à ce service.</p>
<a class="example-event fc-daygrid-event fc-daygrid-block-event fc-h-event fc-event fc-event-start fc-event-end fc-event-future shift_less_alf"><div class="fc-event-main"><div class="fc-event-main-frame"><div class="fc-event-time">10:45</div><div class="fc-event-title-container"><div class="fc-event-title fc-sticky">&nbsp;- 3/12</div></div></div></div></a> <a class="example-event fc-daygrid-event fc-daygrid-block-event fc-h-event fc-event fc-event-start fc-event-end fc-event-future shift_less_alf"><div class="fc-event-main"><div class="fc-event-main-frame"><div class="fc-event-time">10:45</div><div class="fc-event-title-container"><div class="fc-event-title fc-sticky">&nbsp;- 3/12</div></div></div></div></a>
<p>Un service colorié en bleu : je peux m'inscrire à ce service.</p> <p>Un service colorié en bleu : je peux m'inscrire à ce service.</p>
<a class="example-event fc-daygrid-event fc-daygrid-block-event fc-h-event fc-event fc-event-start fc-event-end fc-event-future shift_booked"><div class="fc-event-main"><div class="fc-event-main-frame"><div class="fc-event-time">06:00</div><div class="fc-event-title-container"><div class="fc-event-title fc-sticky">&nbsp;- 9/12</div></div></div></div></a>
<p>Un service colorié en noir : je suis déjà inscrit.e à ce service.</p>
<a class="example-event fc-daygrid-event fc-daygrid-block-event fc-h-event fc-event fc-event-start fc-event-end fc-event-future shift_booked_makeup"><div class="fc-event-main"><div class="fc-event-main-frame"><div class="fc-event-time">13:30</div><div class="fc-event-title-container"><div class="fc-event-title fc-sticky">&nbsp;- 7/12</div></div></div></div></a>
<p>Un service colorié en orange : je suis inscrit.e à un rattrapage sur ce service.</p>
<p>3/12 <i class="arrow_explanation_numbers fas fa-arrow-right"></i> il y a déjà 3 places réservées à ce service sur 12 disponibles. <p>3/12 <i class="arrow_explanation_numbers fas fa-arrow-right"></i> il y a déjà 3 places réservées à ce service sur 12 disponibles.
<b>Plus le chiffre de gauche est petit, plus on a besoin de coopérateurs.rices à ce service !</b></p> <b>Plus le chiffre de gauche est petit, plus on a besoin de coopérateurs.rices à ce service !</b></p>
</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