Commit a5b8f0f3 by Damien Moulard Committed by Alexis Aoun

reception: prevent concurrent access to same order

parent da6d534e
......@@ -35,6 +35,15 @@ input[type="number"] {
margin-bottom: 1em;
}
.order_last_update {
font-weight: bold;
}
.order_modified_msg {
font-size: 2rem;
color: #e62720;
}
/* PRODUITS */
.page_body {
height: 100%;
......
......@@ -11,12 +11,38 @@ var orders = [],
groups: []
},
dbc = null,
sync = null;
sync = null,
fingerprint = null;
/* UTILS */
/**
* Difference between two dates
* @param {Date} date1
* @param {Date} date2
* @returns difference object
*/
function dates_diff(date1, date2) {
var diff = {}
var tmp = date2 - date1;
tmp = Math.floor(tmp/1000);
diff.sec = tmp % 60;
tmp = Math.floor((tmp-diff.sec)/60);
diff.min = tmp % 60;
tmp = Math.floor((tmp-diff.min)/60);
diff.hours = tmp % 24;
tmp = Math.floor((tmp-diff.hours)/24);
diff.days = tmp;
return diff;
}
/**
* Wait for both ajax callbacks for reloading to avoid a js error
* -> reloading when ajax call not answered causes a popup to appear, which can be confusing
*/
......@@ -25,6 +51,48 @@ function reload() {
document.location.reload();
}
/**
* Check for concurent access to same order before going to reception page.
* @param {Int} id
*/
function check_before_goto(id) {
const order_doc_id = 'order_' + id;
dbc.get(order_doc_id).then((doc) => {
console.log(doc);
if (doc.last_update.fingerprint !== null && doc.last_update.fingerprint !== fingerprint) {
time_diff = dates_diff(new Date(doc.last_update.timestamp), new Date())
diff_str = ``
if (time_diff.days !== 0) {
diff_str += `${time_diff.days} jour(s), `
}
if (time_diff.hours !== 0) {
diff_str += `${time_diff.hours} heure(s), `
}
if (time_diff.min !== 0) {
diff_str += `${time_diff.min} min, `
}
diff_str += `${time_diff.sec}s`
let modal_order_access = $('#templates #modal_order_access');
modal_order_access.find(".order_last_update").text(diff_str);
openModal(
modal_order_access.html(),
() => {
goto(id);
},
'Valider'
);
} else {
goto(id);
}
})
.catch((err) => {
console.log(err);
})
}
function goto(id) {
document.location.href = "produits/" + id;
}
......@@ -49,7 +117,7 @@ function group_goto(group_index) {
}
// go to first order
goto(order_groups.groups[group_index][0]);
check_before_goto(order_groups.groups[group_index][0]);
}
/**
......@@ -62,7 +130,7 @@ function create_order_doc(order_data, go_to_order = false) {
dbc.get(order_doc_id).then(() => {
if (go_to_order === true) {
goto(order_data.id);
check_before_goto(order_data.id);
}
})
.catch(function (err) {
......@@ -71,21 +139,23 @@ function create_order_doc(order_data, go_to_order = false) {
let order_doc = { ...order_data };
order_doc._id = order_doc_id;
dbc.put(order_doc, (err) => {
if (!err) {
if (go_to_order === true) {
goto(order_data.id);
}
} else {
error = {
msg: 'Erreur dans la creation de la commande dans couchdb',
ctx: 'validatePrices',
details: err
};
report_JS_error(error, 'reception');
console.log(error);
order_doc.last_update = {
timestamp: Date.now(),
fingerprint: fingerprint,
};
dbc.put(order_doc).then(() => {
if (go_to_order === true) {
goto(order_data.id);
}
}).catch((err) => {
error = {
msg: 'Erreur dans la creation de la commande dans couchdb',
ctx: 'validatePrices',
details: err
};
report_JS_error(error, 'reception');
console.log(error);
});
}
});
......@@ -184,6 +254,8 @@ function group_action() {
// Create doc for each group order if doesn't exist
create_order_doc(selected_data[i]);
// TODO (en dernier): ask before grouping if at least one of the orders is being updated somewhere else
}
group_ids.sort();
......@@ -442,6 +514,8 @@ $(document).ready(function() {
openModal();
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
fingerprint = new Fingerprint({canvas: true}).get();
// Init couchdb
dbc = new PouchDB(couchdb_dbname),
sync = PouchDB.sync(couchdb_dbname, couchdb_server, {
......@@ -493,8 +567,6 @@ $(document).ready(function() {
console.log(err);
});
// TODO on button click to access order: verif fingerprint & timestamp
// Get or create order groups doc
dbc.get("grouped_orders").then((doc) => {
order_groups = doc;
......
......@@ -33,7 +33,8 @@ var reception_status = null,
barcodes = null; // Barcodes stored locally
var dbc = null,
sync = null;
sync = null,
fingerprint = null;
/* UTILS */
......@@ -115,7 +116,10 @@ function select_product_from_bc(barcode) {
* @param {int} order_id
*/
function update_distant_order(order_id) {
// TODO insert fingerprint & timestamp
orders[order_id].last_update = {
timestamp: Date.now(),
fingerprint: fingerprint,
};
dbc.put(orders[order_id], (err, result) => {
if (!err && result !== undefined) {
......@@ -127,6 +131,30 @@ function update_distant_order(order_id) {
});
}
/**
* Update distant orders with local data
* @param {int} order_id
*/
function update_distant_orders() {
for (let order_id in orders) {
orders[order_id].last_update = {
timestamp: Date.now(),
fingerprint: fingerprint,
};
}
dbc.bulkDocs(Object.values(orders)).then((response) => {
// Update rev of current orders after their update
for (let doc of response) {
let order_id = doc.id.split('_')[1];
orders[order_id]._rev = doc.rev
}
})
.catch((err) => {
console.log(err);
})
}
/* INIT */
// Get order(s) data from server
......@@ -1271,6 +1299,12 @@ function send() {
updated_products: orders[order_id].updated_products
}
orders[order_id].reception_status = updateType;
// Unlock order
orders[order_id].last_update = {
timestamp: null,
fingerprint: null,
}
// Delete temp data
delete orders[order_id].valid_products;
......@@ -1344,7 +1378,6 @@ function send() {
}
});
// TODO : for step 1, instead of saving a temp report, save data in couchdb ?
/* Create error report */
$.ajax({
type: "POST",
......@@ -1445,6 +1478,24 @@ var get_barcodes = async function() {
* @param {Array} partners_display_data
*/
function init_dom(partners_display_data) {
// Back button
$('#back_button').on('click', function () {
// Liberate current orders
for (let order_id in orders) {
orders[order_id].last_update = {
timestamp: null,
fingerprint: null,
};
}
dbc.bulkDocs(Object.values(orders)).then((response) => {
back();
})
.catch((err) => {
console.log(err);
})
})
// Grouped orders
if (Object.keys(orders).length > 1) {
$('#partner_name').html(Object.keys(orders).length + " commandes");
......@@ -1504,7 +1555,7 @@ function init_dom(partners_display_data) {
$("#modal_qtiesValidated").load("/reception/reception_qtiesValidated");
} else {
// Extra security, shouldn't get in here
// Extra security, shouldn't get in here: reception status not valid
back();
}
......@@ -1620,6 +1671,8 @@ function init_dom(partners_display_data) {
$(document).ready(function() {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
fingerprint = new Fingerprint({canvas: true}).get();
// Load barcodes
get_barcodes();
......@@ -1636,15 +1689,25 @@ $(document).ready(function() {
auto_compaction: false
});
// TODO on sync change : redirect (cf order_helper)
sync.on('change', function (info) {
console.log(info);
sync.on('change', function (info) {
if (info.direction === "pull") {
for (const doc of info.change.docs) {
// Redirect if one of the current order is being modified somewhere else
if (String(doc.id) in orders && orders[doc.id]._rev !== doc._rev) {
alert("Un autre navigateur est en train de modifier cette commande ! Vous allez être redirigé.e.");
back();
}
}
}
}).on('error', function (err) {
if (err.status === 409) {
alert("Une erreur de synchronisation s'est produite, la commande a sûrement été modifiée sur un autre navigateur. Vous allez être redirigé.e.");
back();
}
console.log('erreur sync');
console.log(err);
});
// TODO insert fingerprint & timestamp on access to order
// Disable alert errors from datatables
$.fn.dataTable.ext.errMode = 'none';
......@@ -1759,6 +1822,9 @@ $(document).ready(function() {
// Load saved user comments, get it from first order
user_comments = orders[Object.keys(orders)[0]].user_comments || "";
// Indicate that these orders are used in this navigator
update_distant_orders()
// Fetch orders data
fetch_data();
......
......@@ -46,6 +46,21 @@
<p>Êtez-vous sûr ?</p>
<hr />
</div>
<div id="modal_order_access">
<h3>Attention !</h3>
<br/>
<p class="order_modified_msg">
Un autre navigateur a commencé à réceptionner cette commande il y a <span class="order_last_update"></span>.
</p><br/>
<p>
Si quelqu'un d'autre que vous est à l'origine de cette opération et que celle-ci est récente,
nous conseillons fortement de ne pas accéder à la commande afin d'éviter les conflits.
</p><br/>
<p>Voulez-vous quand même y accéder ?</p>
<hr/>
</div>
</div>
<br/>
......
......@@ -19,7 +19,7 @@
{% endif %}
<div class="page_body">
<header class="flex-container">
<button class="right btn--danger" onclick="back()">Retour</button>
<button class="right btn--danger" id="back_button">Retour</button>
<div class="w33 arrow-block txtcenter" id="header_step_one">
<h4 id="header_step_one_content">Produits à compter </h4>
</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