Commit e84f47f2 by Damien Moulard

merge couchdb in reception

parents ac8536c7 983b0430
......@@ -20,7 +20,8 @@ COUCHDB = {
'inventory': 'inventory',
'envelops': 'envelop',
'shop': 'shopping_carts',
'orders': 'orders_test'
'orders': 'orders_test',
'reception': 'reception_test',
}
}
......
......@@ -11,6 +11,10 @@ input[type="number"] {
}
/* INDEX */
.group_line {
margin-bottom: 5px;
}
#orders tbody tr {
cursor: pointer;
}
......@@ -31,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%;
......
var order = {
var orders = [],
order = {
'id' : null
},
table_orders = null,
callback_update = false,
callback_report = false,
selection_type = null,
saved_groups = [];
order_groups = {
_id: 'grouped_orders',
groups: []
},
dbc = null,
sync = null,
fingerprint = null;
/* UTILS */
// Wait for both ajax callbacks for reloading to avoid a js error
// -> reloading when ajax call not answered causes a popup to appear, can be confusing
function reload() {
if (callback_update && callback_report)
document.location.reload();
}
/**
* Difference between two dates
* @param {Date} date1
* @param {Date} date2
* @returns difference object
*/
function dates_diff(date1, date2) {
var diff = {};
var tmp = date2 - date1;
function goto(id) {
document.location.href = "produits/" + id;
}
tmp = Math.floor(tmp/1000);
diff.sec = tmp % 60;
/*
* Go to Products page for an existing group
* params :
* i : index of group in 'saved_groups' array
*/
function group_goto(i) {
// Make sure all group's orders are saved in local storage
for (j in saved_groups[i]) {
set_local_storage(saved_groups[i][j]);
}
tmp = Math.floor((tmp-diff.sec)/60);
diff.min = tmp % 60;
// go to one of group's order Products page
goto(saved_groups[i][0].id);
}
tmp = Math.floor((tmp-diff.min)/60);
diff.hours = tmp % 24;
/*
* Set local storage for given order
*/
function set_local_storage(order_data) {
if (Modernizr.localstorage) {
var stored_order = JSON.parse(localStorage.getItem('order_' + order_data.id));
tmp = Math.floor((tmp-diff.hours)/24);
diff.days = tmp;
// Set local storage if key doesn't exist
if (stored_order == null) {
localStorage.setItem("order_" + order_data.id, JSON.stringify(order_data));
}
}
return diff;
}
/*
* Remove from local storage orders that have a wrong status
* (-> order has been updated elsewhere)
/**
* 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
*/
function clean_local_storage() {
var stored_order = null;
// Loop through local storage
for (key of Object.keys(localStorage)) {
if (key.startsWith('order_')) {
stored_order = JSON.parse(localStorage.getItem(key));
// Loop through orders in table to find match
var i = 0;
var found = false;
while (i < table_orders.rows().data().length && !found) {
var uptodate_order = table_orders.rows(i).data()[0];
// If status in local storage is wrong
if (stored_order.id == uptodate_order.id
&& stored_order.reception_status != uptodate_order.reception_status) {
// Remove from local storage
localStorage.removeItem("order_" + uptodate_order.id);
function reload() {
if (callback_update && callback_report)
document.location.reload();
}
// Evolution: warn user (order modified elsewhere, local data has been deleted)
found = true;
}
/**
* 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;
i++;
}
dbc.get(order_doc_id).then((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 (!found) {
// Remove too if order isn't in server data
localStorage.removeItem("order_" + stored_order.id);
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');
function create_groups_from_server_data() {
// Get array of stored grouped orders
var grouped_orders = JSON.parse(localStorage.getItem('grouped_orders'));
modal_order_access.find(".order_last_update").text(diff_str);
// Create if not exists
if (grouped_orders == null) {
grouped_orders = [];
openModal(
modal_order_access.html(),
() => {
goto(id);
},
'Valider'
);
} else {
// Remove from server data groups already in local storage
for (stored_group of grouped_orders) {
for (sg_order_item of stored_group) {
for (i in server_stored_groups) {
if (server_stored_groups[i].includes(sg_order_item)) {
server_stored_groups.splice(i, 1);
break;
}
}
}
}
goto(id);
}
})
.catch((err) => {
console.log(err);
});
}
// Add server groups to stored groups
grouped_orders = grouped_orders.concat(server_stored_groups);
localStorage.setItem('grouped_orders', JSON.stringify(grouped_orders));
function goto(id) {
document.location.href = "produits/" + id;
}
/*
* If there are groups in local storage, extract them from the table, set the groups actions.
/**
* Go to Products page for an existing group
* @param {int} group_index index of group in groups array
*/
function extract_grouped_orders() {
var saved_grouped_orders = JSON.parse(localStorage.getItem('grouped_orders'));
var groups_to_delete = []; // indexes
// if there are grouped orders
if (saved_grouped_orders != null) {
// for each group
for (group_index in saved_grouped_orders) {
var g = [];
// for each order in group
for (group_element_id of saved_grouped_orders[group_index]) {
// Look for order in datatable
for (var i = 0; i < table_orders.rows().data().length; i++) {
if (group_element_id == table_orders.rows(i).data()[0].id) {
var order = table_orders.rows(i).data()[0];
function group_goto(group_index) {
// Make sure a couchdb document exists for all group's orders
for (let i in order_groups.groups[group_index]) {
let order_data = null;
g.push(order);
// remove raw from table
table_orders.rows(i).remove()
.draw();
// Find order data
for (let order of orders) {
if (order.id == order_groups.groups[group_index][i]) {
order_data = order;
}
}
}
// No order found, delete group and skip the rest
if (g.length == 0) {
groups_to_delete.push(group_index);
continue;
}
// Display group
document.getElementById("container_groups").hidden = false;
var group_row = "<ul> <li> Commandes de ";
for (i in g) {
if (i == g.length-1) { // last element of list
group_row += "<b>" + g[i].partner + "</b> du " + g[i].date_order + " : ";
} else {
group_row += "<b>" + g[i].partner + "</b> du " + g[i].date_order + ", ";
}
create_order_doc(order_data);
}
if (g[0].reception_status == 'False') {
group_row += "<button class='btn--primary' onClick='group_goto("
+ saved_groups.length
+ ")'>Compter les produits</button>";
} else {
group_row += "<button class='btn--success' onClick='group_goto("
+ saved_groups.length
+ ")'>Mettre à jour les prix</button>";
}
group_row += "</li>";
$('#groups_items').append(group_row);
// go to first order
check_before_goto(order_groups.groups[group_index][0]);
}
saved_groups.push(g);
}
}
/**
* Create a couchdb document for an order if it doesn't exist
* @param {Object} order_data
* @param {Boolean} goto if true, go to order page
*/
function create_order_doc(order_data, go_to_order = false) {
const order_doc_id = 'order_' + order_data.id;
dbc.get(order_doc_id).then(() => {
if (go_to_order === true) {
check_before_goto(order_data.id);
}
})
.catch(function (err) {
// Create if doesn't exist
if (err.status === 404) {
let order_doc = { ...order_data };
order_doc._id = order_doc_id;
order_doc.last_update = {
timestamp: Date.now(),
fingerprint: fingerprint
};
if (groups_to_delete.length > 0) {
for (index of groups_to_delete) {
saved_grouped_orders.splice(index, 1);
dbc.put(order_doc).then(() => {
if (go_to_order === true) {
goto(order_data.id);
}
localStorage.setItem('grouped_orders', JSON.stringify(saved_grouped_orders));
})
.catch((err) => {
error = {
msg: 'Erreur dans la creation de la commande dans couchdb',
ctx: 'create_order_doc',
details: err
};
report_JS_error(error, 'reception');
console.log(error);
});
}
});
}
/* ACTIONS */
// Validate all prices of an order
/**
* Validate all prices of an order
*/
function validatePrices() {
// Loading on
openModal();
......@@ -210,9 +187,19 @@ function validatePrices() {
contentType: "application/json; charset=utf-8",
data: JSON.stringify(update_data),
success: function() {
localStorage.removeItem("order_" + order["id"]);
// Remove order
dbc.get(`order_${order['id']}`).then((doc) => {
return dbc.remove(doc);
})
.then(() => {
callback_update = true;
reload();
})
.catch((err) => {
// No doc found
console.log(err);
reload();
});
},
error: function() {
closeModal();
......@@ -251,91 +238,117 @@ function validatePrices() {
});
}
// Action fired when orders are grouped (new group)
/**
* Action fired when orders are grouped (new group)
* @returns
*/
function group_action() {
var pswd = prompt('Merci de demander à un.e salarié.e le mot de passe pour fusionner ces commandes.');
let pswd = prompt('Merci de demander à un.e salarié.e le mot de passe pour fusionner ces commandes.');
if (pswd == merge_orders_pswd) { // Minimum security level
// Use local storage to pass order data to next page
if (Modernizr.localstorage) {
var selected_data = table_orders.rows('.selected').data();
var group_ids = [];
// Minimum security level
if (pswd == merge_orders_pswd) {
let selected_data = table_orders.rows('.selected').data();
let group_ids = [];
// Select orders id
for (var i = 0; i < selected_data.length; i++) {
for (let i = 0; i < selected_data.length; i++) {
// Select group orders id
group_ids.push(selected_data[i].id);
// Create doc for each group order if doesn't exist
create_order_doc(selected_data[i]);
}
// Notify server that group is created
$.ajax({
type: "POST",
url: "/reception/save_order_group",
dataType: "json",
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(group_ids),
success: function() {
var min_id = 9999999;
group_ids.sort();
for (var i = 0; i < selected_data.length; i++) {
// get smallest id
if (selected_data[i].id < min_id) {
min_id = selected_data[i].id;
// Save group
order_groups.groups.push(group_ids);
dbc.put(order_groups, (err) => {
if (!err) {
goto(group_ids[0]);
} else {
alert("Une erreur est survenue lors de la création du groupe. Veuillez ré-essayer plus tard svp.");
console.log(err);
}
});
// Add each order to local storage
set_local_storage(selected_data[i]);
} else if (pswd == null) {
return;
} else {
alert('Mauvais mot de passe !');
}
}
// Get array of grouped orders
var grouped_orders = JSON.parse(localStorage.getItem('grouped_orders'));
// Create if not exists
if (grouped_orders == null) {
grouped_orders = [];
}
/* DISPLAY */
// Add group
grouped_orders.push(group_ids);
/**
* Display the order groups.
* Remove the grouped orders from the order table to prevent grouping in multiple groups.
*/
function display_grouped_orders() {
if (table_orders !== null) {
$('#groups_items').empty();
let groups_display_content = "<ul>";
for (let group_index in order_groups.groups) {
let group_orders = [];
// Extract every order in the groups from the orders table
for (group_order_id of order_groups.groups[group_index]) {
// Look for order in datatable"
for (let i = 0; i < table_orders.rows().data().length; i++) {
if (group_order_id == table_orders.rows(i).data()[0].id) {
var order = table_orders.rows(i).data()[0];
// store grouped orders array
localStorage.setItem('grouped_orders', JSON.stringify(grouped_orders));
group_orders.push(order);
// Go to products page of order with smallest id
goto(min_id);
},
error: function(data) {
if (data != null && data.status == 409) {
alert("Un groupe a déjà été formé sur un autre poste "
+ "avec au moins l'une des commandes sélectionnées. Merci de rafraichir la page.");
// remove table row
table_orders.rows(i).remove()
.draw();
}
}
}
});
// Display group
document.getElementById("container_groups").hidden = false;
let group_row = `<li class="group_line"> Commandes de `;
for (let i in group_orders) {
if (i == group_orders.length-1) { // last element of list
group_row += "<b>" + group_orders[i].partner + "</b> du " + group_orders[i].date_order + " : ";
} else {
alert("Le local storage n'est pas disponible. Merci de contacter un.e salarié.e !");
group_row += "<b>" + group_orders[i].partner + "</b> du " + group_orders[i].date_order + ", ";
}
}
} else if (pswd == null) {
return;
if (group_orders[0].reception_status == 'False') {
group_row += "<button class='btn--primary' onClick='group_goto("
+ group_index
+ ")'>Compter les produits</button>";
} else {
alert('Mauvais mot de passe !');
group_row += "<button class='btn--success' onClick='group_goto("
+ group_index
+ ")'>Mettre à jour les prix</button>";
}
}
group_row += "</li>";
groups_display_content += group_row;
}
$('#container_groups').show();
$('#groups_items').append(groups_display_content);
}
}
$(document).ready(function() {
openModal();
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
// Set date format for DataTable so date ordering can work
$.fn.dataTable.moment('D/M/Y');
/**
* Display the main orders table
*/
function display_orders_table() {
if (table_orders) {
table_orders.clear().destroy();
$('#orders').empty();
}
table_orders = $('#orders').DataTable({
ajax: "get_list_orders",
data: orders,
columns:[
{
data:"id",
......@@ -417,13 +430,7 @@ $(document).ready(function() {
]
],
iDisplayLength: 25,
language: {url : '/static/js/datatables/french.json'},
initComplete: function() { // After data is loaded
clean_local_storage();
create_groups_from_server_data();
extract_grouped_orders();
closeModal();
}
language: {url : '/static/js/datatables/french.json'}
});
// Set rows event on click
......@@ -482,12 +489,10 @@ $(document).ready(function() {
document.getElementById("group_action").hidden = true;
}
} else if (this.cellIndex == 4) { // Click on last cell button -> go to products page
// Extra security if order with a different status gets lost in here
// Click action only for specific reception status
if (row_data.reception_status == "qty_valid" || row_data.reception_status == "False") {
// Use local storage to pass order data to next page
set_local_storage(row_data);
goto(row_data.id);
// Use couchdb to pass order data to next page
create_order_doc(row_data, true);
}
} else if (this.cellIndex == 3 && row_data.reception_status == "qty_valid") {
// If 'update prices' step, click on before-last cell -> validate all prices
......@@ -502,6 +507,112 @@ $(document).ready(function() {
.search(jQuery.fn.DataTable.ext.type.search.string(this.value))
.draw();
});
}
$(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, {
live: true,
retry: true,
auto_compaction: false
});
// On distant changes
sync.on('change', function (info) {
// If important data changed somewhere else, update local data
let need_to_reload = false;
if (info.direction === "pull") {
for (let doc of info.change.docs) {
if (doc._id === "grouped_orders") {
// If groups doc changed, update local groups
need_to_reload = true;
order_groups = doc;
} else if ("_deleted" in doc && doc._deleted === true) {
// If order was deleted, delete it locally
try {
const deleted_order_id = parseInt(doc._id.split('_')[1]);
let index = orders.findIndex(order => order.id == deleted_order_id);
if (index !== -1) {
orders.splice(index, 1);
need_to_reload = true;
}
} catch (error) {
console.log(error);
}
} else {
// Find updated order in local orders & update it if reception status changed
let index = orders.findIndex(order => order.id == doc.id);
if (index !== -1 && orders[index].reception_status !== doc.reception_status) {
orders[index] = doc;
need_to_reload = true;
break;
}
}
}
}
if (need_to_reload) {
display_orders_table();
display_grouped_orders();
}
}).on('error', function (err) {
console.log(err);
});
// Get or create order groups doc
dbc.get("grouped_orders").then((doc) => {
order_groups = doc;
})
.catch(function (err) {
console.log(err);
if (err.status === 404) {
// Create if doesn't exist
dbc.put(order_groups, (err, result) => {
if (!err) {
order_groups._rev = result.rev;
} else {
console.log("document pour les groupes déjà créé");
console.log(err);
}
});
}
});
// Set date format for DataTable so date ordering can work
$.fn.dataTable.moment('D/M/Y');
// Get orders
$.ajax({
type: 'GET',
url: "/reception/get_list_orders",
dataType:"json",
traditional: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
orders = data.data;
display_orders_table();
display_grouped_orders();
closeModal();
},
error: function(data) {
err = {msg: "erreur serveur lors de la récupération des commandes", ctx: 'get_list_orders'};
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') {
err.msg += ' : ' + data.responseJSON.error;
}
report_JS_error(err, 'orders');
closeModal();
alert('Erreur lors de la récupération des commandes, rechargez la page plus tard.');
}
});
});
......@@ -13,9 +13,9 @@ Sémantiquement, ici :
* Associative array of current order(s)
* If more than 1 element: group of orders
* If 1 element: single order
* -> check for Object.keys(orders).length to know if we're in a group case
*/
var orders = {},
is_group = false,
group_ids = [];
var reception_status = null,
......@@ -32,6 +32,9 @@ var reception_status = null,
updateType = "", // step 1: qty_valid; step2: br_valid
barcodes = null; // Barcodes stored locally
var dbc = null,
sync = null,
fingerprint = null;
/* UTILS */
......@@ -112,12 +115,56 @@ function select_product_from_bc(barcode) {
return 0;
}
/**
* Update couchdb order
* @param {int} order_id
*/
function update_distant_order(order_id) {
orders[order_id].last_update = {
timestamp: Date.now(),
fingerprint: fingerprint
};
dbc.put(orders[order_id], (err, result) => {
if (!err && result !== undefined) {
orders[order_id]._rev = result.rev;
} else {
alert("Erreur lors de la sauvegarde de la commande... Si l'erreur persiste contactez un administrateur svp.");
console.log(err);
}
});
}
/**
* 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
function fetch_data() {
try {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
$.ajax({
type: 'POST',
url: '../get_orders_lines',
......@@ -198,7 +245,7 @@ function initLists() {
document.getElementById("valid_all_uprices").disabled = false;
}
// Set lists with local storage content
// Set processed and to_process lists based on saved data
for (var i = 0; i < updatedProducts.length; i++) {
let product = updatedProducts[i];
......@@ -276,7 +323,7 @@ function initLists() {
defaultContent: "<select class='select_product_action'><option value=''></option><option value='supplier_shortage'>Rupture fournisseur</option></select>",
className:"dt-body-center",
orderable: false,
visible: display_autres
visible: display_autres === "True"
}
],
rowId : "product_id.0",
......@@ -356,7 +403,7 @@ function initLists() {
title:"Autres",
className:"dt-body-center",
orderable: false,
visible: display_autres,
visible: display_autres === "True",
render: function (data, type, full) {
let disabled = (full.supplier_shortage) ? "disabled" : '';
......@@ -391,17 +438,18 @@ function initLists() {
$('#table_to_process tbody').on('click', 'a#toProcess_line_valid', function () {
if (is_time_to('reception_direct_valid_order_line', 500)) {
try {
var row = table_to_process.row($(this).parents('tr'));
var data = row.data();
let row = table_to_process.row($(this).parents('tr'));
let data = row.data();
add_to_processed(data);
remove_from_toProcess(row, data);
// Update local storage of product's order
if (!orders[data.id_po]['valid_products'])
// Update product's order
if (!orders[data.id_po]['valid_products']) {
orders[data.id_po]['valid_products'] = [];
}
orders[data.id_po]['valid_products'].push(data['id']);
localStorage.setItem("order_" + data.id_po, JSON.stringify(orders[data.id_po]));
update_distant_order(data.id_po);
// Reset search
document.getElementById('search_input').value = '';
......@@ -726,8 +774,8 @@ function set_supplier_shortage(row, product, from_processed = false) {
}
add_to_processed(product);
// Update local storage of product's order
localStorage.setItem("order_" + product.id_po, JSON.stringify(orders[product.id_po]));
// Update product's order
update_distant_order(product.id_po);
// Reset search
document.getElementById('search_input').value = '';
......@@ -803,9 +851,12 @@ function clearLineEdition() {
/**
* Update a product info : qty or unit price
* If 'value' is set, use it as new value
* @param {Object} productToEdit
* @param {Float} value if set, use it as new value
* @param {Boolean} batch if true, don't update couchdb data here
* @returns
*/
function editProductInfo (productToEdit, value = null) {
function editProductInfo (productToEdit, value = null, batch = false) {
try {
// Check if the product is already in the 'updated' list
var index = searchUpdatedProduct();
......@@ -863,16 +914,16 @@ function editProductInfo (productToEdit, value = null) {
if (firstUpdate) {
updatedProducts.push(productToEdit);
/* Update local storage of product order */
// Create 'updated_products' list in order if not exists
if (!orders[productToEdit.id_po]['updated_products'])
if (!orders[productToEdit.id_po]['updated_products']) {
orders[productToEdit.id_po]['updated_products'] = [];
}
// Add product to order's updated products if first update
orders[productToEdit.id_po]['updated_products'].push(productToEdit);
// May have been directly validated then updated from processed list
// -> then: remove from 'valid_products' list
// -> remove from 'valid_products' list
for (i in orders[productToEdit.id_po]['valid_products']) {
if (orders[productToEdit.id_po]['valid_products'][i] == productToEdit['id']) {
orders[productToEdit.id_po]['valid_products'].splice(i, 1);
......@@ -888,8 +939,10 @@ function editProductInfo (productToEdit, value = null) {
}
}
// Update local storage of product order
localStorage.setItem("order_" + productToEdit.id_po, JSON.stringify(orders[productToEdit.id_po]));
if (batch === false) {
// Update product order
update_distant_order(productToEdit.id_po);
}
add_to_processed(productToEdit);
} catch (e) {
......@@ -916,16 +969,17 @@ function setAllQties() {
table_to_process.rows().every(function () {
var data = this.data();
editProductInfo(data, 0);
editProductInfo(data, 0, true);
return true;
});
list_to_process = [];
table_to_process.rows().remove()
.draw();
}
// Batch update orders
update_distant_orders();
}
/* ACTIONS */
......@@ -1017,7 +1071,6 @@ function pre_send(type) {
function data_validation() {
openModal();
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
$.ajax({
type: "POST",
url: "../data_validation",
......@@ -1059,6 +1112,7 @@ function send() {
// Loading on
openModal();
/* Prepare data for orders update */
// Only send to server the updated lines
var update_data = {
update_type: updateType,
......@@ -1072,7 +1126,8 @@ function send() {
// for each updated product, add it to its order list
for (i in updatedProducts) {
// if product was in different orders
/* ---> The following part concerns products found in different orders */
if ('other_orders_data' in updatedProducts[i]) {
// for each other order of product
for (other_order_data of updatedProducts[i].other_orders_data) {
......@@ -1129,13 +1184,44 @@ function send() {
orders[other_order_data.id_po]['updated_products'].push(product_copy);
}
}
/* <--- */
// Add product to order's prod list
prod_order_id = updatedProducts[i].id_po;
update_data.orders[prod_order_id]['po'].push(updatedProducts[i]);
}
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
/* Create the error report */
// Send changes between items to process and processed items
var error_report_data = {
'group_amount_total' : 0,
'update_type' : updateType,
'updated_products' : updatedProducts,
'user_comments': user_comments,
'orders' : []
};
for (let i in orders) {
error_report_data.group_amount_total += orders[i].amount_total;
error_report_data.orders.push(orders[i]);
}
// Send request for error report
$.ajax({
type: "POST",
url: "../save_error_report",
dataType: "json",
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(error_report_data),
success: function() {},
error: function() {
closeModal();
alert('Erreur dans l\'envoi du rapport.');
}
});
/* Update orders */
$.ajax({
type: "PUT",
url: "../update_orders",
......@@ -1147,8 +1233,9 @@ function send() {
closeModal();
try {
// If step 1 (counting), open pop-up with procedure explanation
// If step 1 (counting)
if (reception_status == "False") {
/* Open pop-up with procedure explanation */
var barcodes_to_print = false;
// Select products with local barcode and without barcode, when qty > 0
......@@ -1178,9 +1265,9 @@ function send() {
}
// Set order(s) name in popup DOM
if (Object.keys(orders).length == 1) { // Single order
if (Object.keys(orders).length === 1) { // Single order
document.getElementById("order_ref").innerHTML = orders[Object.keys(orders)[0]].name;
} else {
} else { // group
document.getElementById("success_order_name_container").hidden = true;
document.getElementById("success_orders_name_container").hidden = false;
......@@ -1205,13 +1292,35 @@ function send() {
openModal(
$('#modal_qtiesValidated').html(),
function() {
document.location.href = "/reception";
},
back,
'Retour à la liste des commandes',
true,
false
);
/* Not last step: update distant data */
for (let order_id in orders) {
// Save current step updated data
orders[order_id].previous_steps_data = {};
orders[order_id].previous_steps_data[reception_status] = {
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;
delete orders[order_id].updated_products;
}
dbc.bulkDocs(Object.values(orders)).catch((err) => {
console.log(err);
});
} else {
// Print etiquettes with new prices
if (updatedProducts.length > 0) {
......@@ -1220,49 +1329,48 @@ function send() {
openModal(
$('#templates #modal_pricesValidated').html(),
function() {
document.location.href = "/reception";
},
back,
'Retour à la liste des commandes',
true,
false
);
}
// Go back to to_process list if modal closed
$('#modal_closebtn_top').on('click', function () {
document.location.href = "/reception";
});
/* Last step: Clear distant data */
// Delete orders doc
for (let order_id in orders) {
orders[order_id]._deleted = true;
}
$('#modal_closebtn_bottom').on('click', function () {
document.location.href = "/reception";
});
// Remove orders group
dbc.get("grouped_orders").then((doc) => {
let couchdb_update_data = Object.values(orders);
// Clear local storage before leaving
for (order_id in orders) {
localStorage.removeItem("order_" + order_id);
}
// We're in a group, remove it & update groups doc
if (Object.keys(orders).length > 1) {
let groups_doc = doc;
// Delete group(s)
if (is_group) {
var grouped_orders = JSON.parse(localStorage.getItem('grouped_orders'));
let first_order_id = parseInt(Object.keys(orders)[0]);
// Remove all groups containing these orders
for (order_id in orders) {
search:
for (var h = 0; i < grouped_orders.length; h++) {
for (var j = 0; j < grouped_orders[h].length; j++) {
if (grouped_orders[h][j] == order_id) {
grouped_orders.splice(h);
break search;
}
for (let i in groups_doc.groups) {
if (groups_doc.groups[i].includes(first_order_id)) {
groups_doc.groups.splice(i, 1);
break;
}
}
couchdb_update_data.push(groups_doc);
}
localStorage.setItem('grouped_orders', JSON.stringify(grouped_orders));
return dbc.bulkDocs(couchdb_update_data);
})
.catch(function (err) {
console.log(err);
});
}
// Back if modal closed
$('#modal_closebtn_top').on('click', back);
$('#modal_closebtn_bottom').on('click', back);
} catch (ee) {
err = {msg: ee.name + ' : ' + ee.message, ctx: 'callback update_orders'};
console.error(err);
......@@ -1274,34 +1382,6 @@ function send() {
alert('Erreur lors de la sauvegarde des données.');
}
});
// Send changes between items to process and processed items
var updates = {
'group_amount_total' : 0,
'update_type' : updateType,
'updated_products' : updatedProducts,
'user_comments': user_comments,
'orders' : []
};
for (i in orders) {
updates.group_amount_total += orders[i].amount_total;
updates.orders.push(orders[i]);
}
$.ajax({
type: "POST",
url: "../save_error_report",
dataType: "json",
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(updates),
success: function(data) {},
error: function() {
closeModal();
alert('Erreur dans l\'envoi du rapport.');
}
});
} catch (e) {
err = {msg: e.name + ' : ' + e.message, ctx: 'send'};
console.error(err);
......@@ -1329,17 +1409,19 @@ function confirm_all_left_is_good() {
} else {
value = data.price_unit;
}
editProductInfo(data, value);
editProductInfo(data, value, true);
return true;
});
list_to_process = [];
table_to_process.rows().remove()
.draw();
// Batch update orders
update_distant_orders();
closeModal();
}
/* TODO: upgrade modal
-> disable background scrolling*/
function openFAQ() {
openModal($("div#modal_FAQ_content").html(), function() {}, 'Compris !', true, false);
}
......@@ -1367,10 +1449,10 @@ function openErrorReport() {
function saveErrorReport() {
user_comments = document.getElementById("error_report").value;
// Save comment in local storage, in all orders
// Save comments in all orders
for (order_id of Object.keys(orders)) {
orders[order_id].user_comments = user_comments;
localStorage.setItem("order_" + order_id, JSON.stringify(orders[order_id]));
update_distant_order(order_id);
}
document.getElementById("search_input").focus();
......@@ -1382,139 +1464,39 @@ var get_barcodes = async function() {
};
$(document).ready(function() {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
// Load barcodes
get_barcodes();
// Get Route parameter
var pathArray = window.location.pathname.split('/');
var id = pathArray[pathArray.length-1];
// Disable alert errors from datatables
$.fn.dataTable.ext.errMode = 'none';
// Listen for errors in tables with custom behavior
$('#table_to_process').on('error.dt', function (e, settings, techNote, message) {
var err_msg = message;
try {
var split = message.split(" ");
var row_number = null;
for (var i = 0; i < split.length; i++) {
if (split[i] == "row")
row_number = split[i+1];
}
row_number = row_number.replace(',', '');
var row_data = $('#table_to_process').DataTable()
.row(row_number)
.data();
err_msg += " - Order id: " + row_data.id_po;
err_msg += " - Product: " + row_data.product_id[1];
} catch (e) {
console.log(e);
/**
* Init the page according to order(s) data (texts, colors, events...)
*
* @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
};
}
err = {msg: err_msg, ctx: 'datatable: table to_process'};
console.error(err);
report_JS_error(err, 'reception');
dbc.bulkDocs(Object.values(orders)).then((response) => {
back();
})
.catch((err) => {
console.log(err);
});
$('#table_processed').on('error.dt', function (e, settings, techNote, message) {
var err_msg = message;
try {
var split = message.split(" ");
var row_number = null;
for (var i = 0; i < split.length; i++) {
if (split[i] == "row")
row_number = split[i+1];
}
row_number = row_number.replace(',', '');
var row_data = $('#table_processed').DataTable()
.row(row_number)
.data();
err_msg += " - Order id: " + row_data.id_po;
err_msg += " - Product: " + row_data.product_id[1];
} catch (e) {
console.log(e);
}
err = {msg: err_msg, ctx: 'datatable: table processed'};
console.error(err);
report_JS_error(err, 'reception');
});
try {
// Get order info from local storage (it should be there if process followed)
if (Modernizr.localstorage) {
// Look for current order in grouped orders in local storage
var grouped_orders = JSON.parse(localStorage.getItem('grouped_orders'));
if (grouped_orders != null) {
for (group of grouped_orders) {
for (group_element_id of group) {
if (group_element_id == id) {
// We're in a group!
is_group = true;
group_ids = group;
}
}
}
}
// if not in group, add current order to group
if (group_ids.length == 0) {
group_ids.push(id);
}
var stored_order = null;
var display_partners_name = [];
// for each order in order group
for (order_id of group_ids) {
// Get order data from local storage
stored_order = JSON.parse(localStorage.getItem('order_' + order_id));
// Add order to order list
if (stored_order != null) {
orders[order_id] = stored_order;
//Add each order's already updated and validated products to common list
if (stored_order["updated_products"])
updatedProducts = updatedProducts.concat(stored_order["updated_products"]);
if (stored_order["valid_products"])
validProducts = validProducts.concat(stored_order["valid_products"]);
// Prepare data to display in 'partner name' area
display_partners_name.push(stored_order['partner'] + ' du ' + stored_order['date_order']);
}
}
// Set current reception status: take first order's
reception_status = orders[Object.keys(orders)[0]].reception_status;
// Load user comments from local storage, get it from first order
user_comments = orders[Object.keys(orders)[0]].user_comments || "";
}
// Fetch orders data
fetch_data();
if (is_group) {
// Grouped orders
if (Object.keys(orders).length > 1) {
$('#partner_name').html(Object.keys(orders).length + " commandes");
// Display order data for each order
var msg = "";
for (display_partner_data of display_partners_name) {
for (display_partner_data of partners_display_data) {
if (msg != "") {
msg += ", ";
}
......@@ -1566,15 +1548,7 @@ $(document).ready(function() {
$("#modal_qtiesValidated").load("/reception/reception_qtiesValidated");
} else {
// Extra security, shouldn't get in here
document.location.href = "/reception";
}
} catch (e) {
err = {msg: e.name + ' : ' + e.message, ctx: 'page init'};
console.error(err);
report_JS_error(err, 'reception');
alert("Erreur au chargement de cette commande. Vous allez être redirigé.");
// Extra security, shouldn't get in here: reception status not valid
back();
}
......@@ -1597,16 +1571,6 @@ $(document).ready(function() {
$('#edition_input').on('focus', function () {
$(this).on('wheel.disableScroll', function (e) {
e.preventDefault();
/*
Option to possibly enable page scrolling when mouse over the input, but :
- deltaY is not in pixels in Firefox
- movement not fluid on other browsers
var scrollTo = (e.originalEvent.deltaY) + $(document.documentElement).scrollTop();
$(document.documentElement).scrollTop(scrollTo);
-> other option to allow scrolling would be to loose input focus with blur(): not acceptable
*/
});
})
.on('blur', function () {
......@@ -1695,4 +1659,191 @@ $(document).ready(function() {
.draw();
select_product_from_bc(barcode);
});
}
$(document).ready(function() {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
fingerprint = new Fingerprint({canvas: true}).get();
// Load barcodes
get_barcodes();
// Get Route parameter
let pathArray = window.location.pathname.split('/');
let id = pathArray[pathArray.length-1];
// Init couchdb
dbc = new PouchDB(couchdb_dbname),
sync = PouchDB.sync(couchdb_dbname, couchdb_server, {
live: true,
retry: true,
auto_compaction: false
});
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);
});
// Disable alert errors from datatables
$.fn.dataTable.ext.errMode = 'none';
// Listen for errors in tables with custom behavior
$('#table_to_process').on('error.dt', function (e, settings, techNote, message) {
var err_msg = message;
try {
var split = message.split(" ");
var row_number = null;
for (var i = 0; i < split.length; i++) {
if (split[i] == "row")
row_number = split[i+1];
}
row_number = row_number.replace(',', '');
var row_data = $('#table_to_process').DataTable()
.row(row_number)
.data();
err_msg += " - Order id: " + row_data.id_po;
err_msg += " - Product: " + row_data.product_id[1];
} catch (e) {
console.log(e);
}
err = {msg: err_msg, ctx: 'datatable: table to_process'};
console.error(err);
report_JS_error(err, 'reception');
});
$('#table_processed').on('error.dt', function (e, settings, techNote, message) {
var err_msg = message;
try {
var split = message.split(" ");
var row_number = null;
for (var i = 0; i < split.length; i++) {
if (split[i] == "row")
row_number = split[i+1];
}
row_number = row_number.replace(',', '');
var row_data = $('#table_processed').DataTable()
.row(row_number)
.data();
err_msg += " - Order id: " + row_data.id_po;
err_msg += " - Product: " + row_data.product_id[1];
} catch (e) {
console.log(e);
}
err = {msg: err_msg, ctx: 'datatable: table processed'};
console.error(err);
report_JS_error(err, 'reception');
});
/* Get order info from couchdb */
// Get order groups
let order_groups = [];
dbc.get("grouped_orders").then((doc) => {
order_groups = doc.groups;
for (let group of order_groups) {
for (group_order_id of group) {
if (group_order_id == id) {
// We're in a group!
group_ids = group;
}
}
}
// if not in group, add current order to group
if (group_ids.length == 0) {
group_ids.push(id);
}
let partners_display_data = [];
dbc.allDocs({
include_docs: true
}).then(function (result) {
// for each order in the group
for (let order_id of group_ids) {
// find order
let order = result.rows.find(el => el.id == 'order_' + order_id);
order = order.doc;
orders[order_id] = order;
// Add each order's already updated and validated products to common list
if (order["updated_products"]) {
updatedProducts = updatedProducts.concat(order["updated_products"]);
}
if (order["valid_products"]) {
validProducts = validProducts.concat(order["valid_products"]);
}
// Prepare data to display in 'partner name' area
partners_display_data.push(order['partner'] + ' du ' + order['date_order']);
}
// Set current reception status: take first order's
reception_status = orders[Object.keys(orders)[0]].reception_status;
// 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();
init_dom(partners_display_data);
})
.catch(function (e) {
let msg = ('message' in e && 'name' in e) ? e.name + ' : ' + e.message : '';
err = {msg, ctx: 'page init - get orders from couchdb', details: e};
console.error(err);
report_JS_error(err, 'reception');
// Should be there, redirect
alert("Erreur au chargement de cette commande. Vous allez être redirigé.");
back();
});
})
.catch(function (e) {
let msg = ('message' in e && 'name' in e) ? e.name + ' : ' + e.message : '';
err = {msg, ctx: 'page init - get grouped orders', details: e};
console.error(err);
report_JS_error(err, 'reception');
// Should be there, redirect
alert("Erreur au chargement de cette commande. Vous allez être redirigé.");
back();
});
});
......@@ -16,6 +16,5 @@ urlpatterns = [
url(r'^reception_qtiesValidated', views.reception_qtiesValidated),
url(r'^reception_pricesValidated', views.reception_pricesValidated),
# url(r'^update_order_status/([0-9]+)$', views.tmp_update_order_status),
url(r'^po_process_picking$', views.po_process_picking),
url(r'^save_order_group$', views.save_order_group)
url(r'^po_process_picking$', views.po_process_picking)
]
......@@ -27,17 +27,12 @@ def as_text(value):
def home(request):
"""Page de selection de la commande suivant un fournisseurs"""
# Get grouped orders stored on the server
try:
with open('temp/grouped_order.json', 'r') as json_file:
saved_groups = json.load(json_file)
except Exception:
saved_groups = []
context = {
'title': 'Reception',
'merge_orders_pswd': settings.RECEPTION_MERGE_ORDERS_PSWD,
'server_stored_groups' : saved_groups
'couchdb_server': settings.COUCHDB['url'],
'db': settings.COUCHDB['dbs']['reception'],
'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', '')
}
template = loader.get_template('reception/index.html')
......@@ -75,8 +70,12 @@ def get_list_orders(request):
def produits(request, id):
""" Gets Order details """
context = {'title': 'Réception des produits',
context = {
'title': 'Réception des produits',
"TOOLS_SERVER": settings.TOOLS_SERVER,
'couchdb_server': settings.COUCHDB['url'],
'db': settings.COUCHDB['dbs']['reception'],
'POUCHDB_VERSION': getattr(settings, 'POUCHDB_VERSION', ''),
"DISPLAY_AUTRES": getattr(settings, 'DISPLAY_COL_AUTRES', True),
}
fixed_barcode_prefix = '0490'
......@@ -133,40 +132,6 @@ def data_validation(request):
coop_logger.error("Orders data validation : %s", str(e))
return JsonResponse({'error': str(e)}, status=500)
def save_order_group(request):
"""
When an order group is created, save it to force group these orders later.
Raise an error if one of the orders is already in a group.
"""
order_ids = json.loads(request.body.decode())
try:
try:
# Check if any of the orders attempted to be grouped is already in a group
with open('temp/grouped_order.json', 'r') as json_file:
saved_groups = json.load(json_file)
for order_id in order_ids:
for group in saved_groups:
if order_id in group:
# Found in a group, stop
msg = 'One of the orders is already in a group'
return JsonResponse({'message': msg}, status=409)
except Exception:
saved_groups = []
# All good, save group
with open('temp/grouped_order.json', 'w+') as json_file:
saved_groups.append(order_ids)
json.dump(saved_groups, json_file)
msg = 'Group saved'
return JsonResponse({'message': msg})
except Exception as e:
print(str(e))
return JsonResponse({'message': str(e)}, status=500)
def update_orders(request):
"""Update orders lines: quantity and unit prices"""
......@@ -257,6 +222,7 @@ def update_orders(request):
# Remove order's group
try:
# TODO remove from couchdb orders & group (here?)
if os.path.exists('temp/grouped_order.json'):
with open('temp/grouped_order.json', 'r') as json_file:
saved_groups = json.load(json_file)
......@@ -272,6 +238,7 @@ def update_orders(request):
except Exception as e:
# no saved groups
print(str(e))
# TODO else if first step, save first step data (here?)
else:
coop_logger.error("update_orders errors : %s", str(errors))
rep = JsonResponse(answer_data, safe=False)
......@@ -281,7 +248,7 @@ def update_orders(request):
# """ Method used for tests purposes: Reset an order status """
# m = CagetteReception(id_po)
# m.update_order_status(id_po, False)
#
# return JsonResponse({'id_po': id_po})
def save_error_report(request):
......@@ -300,7 +267,7 @@ def save_error_report(request):
orders_partner = ""
group_ids = []
for i, order in enumerate(data['orders']) :
# list of temp files: 1 report per reception
# list of temp files: 1 report per order & group
data['orders'][i]['temp_file_name'] = "temp/" + order['name'] + "_rapport-reception_temp.xlsx"
group_ids.append(order['id'])
......@@ -391,7 +358,7 @@ def save_error_report(request):
# Create report with data from steps 1 & 2
else:
elif data['update_type'] == 'br_valid':
for order in data['orders']:
# Read step 1 data from temp file
data_qties = {}
......@@ -423,7 +390,8 @@ def save_error_report(request):
# Clear step 1 temp file
os.remove(order['temp_file_name'])
except:
data_comment_s1 = "Rapport de la première étape absent !"
data_comment_s1 = "Données de la première étape absentes !"
# Add data from step 2
data_full = []
error_total = 0
......@@ -484,6 +452,7 @@ def save_error_report(request):
# no updated products, do nothing
print("Error while updating products")
print(exp)
# Add remaining products, the ones edited only in step 1
for product in data_qties.values():
item = {
......
......@@ -11,7 +11,6 @@
<script type="text/javascript" src="{% static 'js/datatables/dataTables.plugins.js' %}"></script>
<script type="text/javascript" src="{% static 'js/moment.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/datatables/dataTables.plugin.moment_sorting.js' %}"></script>
<script type="text/javascript" src="{% static 'js/reception_index.js' %}?v="></script>
{% endblock %}
{% block content %}
......@@ -31,7 +30,6 @@
<div id="groups_items"></div>
</div>
<br>
<div id="grouped_action">
<button type="button" class='btn--primary' id='group_action' onclick="group_action()" hidden>Compter les produits des commandes sélectionnées</button>
</div>
......@@ -48,13 +46,30 @@
<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/>
<script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static 'js/pouchdb.min'|add:POUCHDB_VERSION|add:'.js' %}"></script>
<script type="text/javascript">
var merge_orders_pswd = '{{merge_orders_pswd}}';
var server_stored_groups = {{server_stored_groups}};
var couchdb_dbname = '{{db}}';
var couchdb_server = '{{couchdb_server}}' + couchdb_dbname;
</script>
<script src="{% static "js/common.js" %}?v="></script>
<script type="text/javascript" src="{% static 'js/reception_index.js' %}?v="></script>
{% endblock %}
......@@ -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>
......@@ -180,10 +180,14 @@
</div>
<br/>
</div>
<script src="{% static 'js/pouchdb.min'|add:POUCHDB_VERSION|add:'.js' %}"></script>
<script type="text/javascript">
var tools_server = '{{TOOLS_SERVER}}'
var fixed_barcode_prefix = '{{FIXED_BARCODE_PREFIX}}'
var display_autres = {{DISPLAY_AUTRES}};
var couchdb_dbname = '{{db}}';
var couchdb_server = '{{couchdb_server}}' + couchdb_dbname;
var display_autres = "{{DISPLAY_AUTRES}}";
</script>
<script src="{% static "js/all_common.js" %}?v="></script>
<script src='{% static "js/barcodes.js" %}?v='></script>
......
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