|
|
|
var suppliers_list = [],
|
|
|
|
products_table = null,
|
|
|
|
products = [],
|
|
|
|
selected_suppliers = [],
|
|
|
|
selected_rows = [],
|
|
|
|
dbc = null,
|
|
|
|
sync = null,
|
|
|
|
order_doc = {
|
|
|
|
_id: null,
|
|
|
|
last_update : {
|
|
|
|
timestamp: null,
|
|
|
|
fingerprint: null,
|
|
|
|
},
|
|
|
|
products: [],
|
|
|
|
selected_suppliers: [],
|
|
|
|
selected_rows: []
|
|
|
|
},
|
|
|
|
fingerprint = null;
|
|
|
|
|
|
|
|
|
|
|
|
/* - UTILS */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reset data that changes between screens
|
|
|
|
*/
|
|
|
|
function reset_data() {
|
|
|
|
products = [],
|
|
|
|
selected_suppliers = [],
|
|
|
|
selected_rows = [],
|
|
|
|
order_doc = {
|
|
|
|
_id: null,
|
|
|
|
last_update : {
|
|
|
|
timestamp: null,
|
|
|
|
fingerprint: null,
|
|
|
|
},
|
|
|
|
products: [],
|
|
|
|
selected_suppliers: []
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* - SUPPLIERS */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add a supplier to the selected suppliers list.
|
|
|
|
*
|
|
|
|
* @returns -1 if validation failed, 0 otherwise
|
|
|
|
*/
|
|
|
|
function add_supplier() {
|
|
|
|
const user_input = $("#supplier_input").val();
|
|
|
|
|
|
|
|
// Check if user input is a valid supplier
|
|
|
|
const supplier = suppliers_list.find(s => s.display_name === user_input);
|
|
|
|
|
|
|
|
if (supplier === undefined) {
|
|
|
|
alert("Le fournisseur renseigné n'est pas valide.\n"
|
|
|
|
+ "Veuillez sélectionner un fournisseur dans la liste déroulante.");
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const supplier_selected = selected_suppliers.find(s => s.display_name === user_input);
|
|
|
|
|
|
|
|
if (supplier_selected !== undefined) {
|
|
|
|
alert("Ce fournisseur est déjà sélectionné.");
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
openModal();
|
|
|
|
|
|
|
|
selected_suppliers.push(supplier);
|
|
|
|
|
|
|
|
let url = "/orders/get_supplier_products";
|
|
|
|
|
|
|
|
url += "?sid=" + encodeURIComponent(supplier.id);
|
|
|
|
|
|
|
|
// Fetch supplier products
|
|
|
|
$.ajax({
|
|
|
|
type: 'GET',
|
|
|
|
url: url,
|
|
|
|
dataType:"json",
|
|
|
|
traditional: true,
|
|
|
|
contentType: "application/json; charset=utf-8",
|
|
|
|
success: function(data) {
|
|
|
|
save_supplier_products(supplier, data.res.products);
|
|
|
|
update_main_screen();
|
|
|
|
$("#supplier_input").val("");
|
|
|
|
update_order();
|
|
|
|
closeModal();
|
|
|
|
},
|
|
|
|
error: function(data) {
|
|
|
|
err = {msg: "erreur serveur lors de la récupération des produits", ctx: 'get_supplier_products'};
|
|
|
|
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 produits, réessayer plus tard.');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Remove a supplier from the selected list & its associated products
|
|
|
|
*
|
|
|
|
* @param {int} supplier_id
|
|
|
|
*/
|
|
|
|
function remove_supplier(supplier_id) {
|
|
|
|
// Remove from suppliers list
|
|
|
|
selected_suppliers = selected_suppliers.filter(supplier => supplier.id != supplier_id);
|
|
|
|
|
|
|
|
// Remove the supplier from the products suppliers list
|
|
|
|
for (const i in products) {
|
|
|
|
products[i].suppliers = products[i].suppliers.filter(supplier => supplier.id != supplier_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove products only associated to this product
|
|
|
|
products = products.filter(product => product.suppliers.length > 0);
|
|
|
|
|
|
|
|
update_main_screen();
|
|
|
|
update_order();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Send to server the association product-supplier
|
|
|
|
*
|
|
|
|
* @param {object} product
|
|
|
|
* @param {object} supplier
|
|
|
|
* @param {node} cell product's row in datatable
|
|
|
|
*/
|
|
|
|
function save_supplier_product_association(product, supplier, cell) {
|
|
|
|
openModal();
|
|
|
|
|
|
|
|
const data = {
|
|
|
|
product_tmpl_id: product.id,
|
|
|
|
supplier_id: supplier.id
|
|
|
|
};
|
|
|
|
|
|
|
|
// Fetch supplier products
|
|
|
|
$.ajax({
|
|
|
|
type: "POST",
|
|
|
|
url: "/orders/associate_supplier_to_product",
|
|
|
|
dataType: "json",
|
|
|
|
traditional: true,
|
|
|
|
contentType: "application/json; charset=utf-8",
|
|
|
|
data: JSON.stringify(data),
|
|
|
|
success: () => {
|
|
|
|
// Save relation locally
|
|
|
|
save_supplier_products(supplier, [product]);
|
|
|
|
|
|
|
|
// Update table
|
|
|
|
$(cell).removeClass("product_not_from_supplier");
|
|
|
|
const row = $(cell).closest("tr");
|
|
|
|
const new_row_data = prepare_datatable_data([product.id])[0];
|
|
|
|
|
|
|
|
products_table.row(row).data(new_row_data)
|
|
|
|
.draw();
|
|
|
|
|
|
|
|
closeModal();
|
|
|
|
},
|
|
|
|
error: function(data) {
|
|
|
|
let msg = "erreur serveur lors de la sauvegarde de l'association product/supplier".
|
|
|
|
msg += ` (product_tmpl_id: ${product.id}; supplier_id: ${supplier.id})`;
|
|
|
|
|
|
|
|
err = {msg: msg, ctx: 'save_supplier_product_association'};
|
|
|
|
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 sauvegarde de l\'association. Veuillez ré-essayer plus tard.');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* When products are fetched, save them and the relation with the supplier.
|
|
|
|
* If product already saved, add the supplier to its suppliers list.
|
|
|
|
* Else, add product with supplier.
|
|
|
|
*
|
|
|
|
* @param {object} supplier
|
|
|
|
* @param {array} new_products
|
|
|
|
*/
|
|
|
|
function save_supplier_products(supplier, new_products) {
|
|
|
|
for (np of new_products) {
|
|
|
|
let index = products.findIndex(p => p.id === np.id);
|
|
|
|
|
|
|
|
if (index === -1) {
|
|
|
|
np.suppliers = [{ ...supplier }];
|
|
|
|
products.push(np);
|
|
|
|
} else {
|
|
|
|
products[index].suppliers.push({ ...supplier });
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Save the quantity set for a product/supplier
|
|
|
|
*
|
|
|
|
* @param {int} prod_id
|
|
|
|
* @param {int} supplier_id
|
|
|
|
* @param {float} val
|
|
|
|
*/
|
|
|
|
function save_product_supplier_qty(prod_id, supplier_id, val) {
|
|
|
|
for (const i in products) {
|
|
|
|
if (products[i].id == prod_id) {
|
|
|
|
for (const j in products[i].suppliers) {
|
|
|
|
if (products[i].suppliers[j].id == supplier_id) {
|
|
|
|
products[i].suppliers[j].qty = val;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Look in the 'suppliers' property of a product
|
|
|
|
*
|
|
|
|
* @param {object} product
|
|
|
|
* @param {object} supplier
|
|
|
|
* @returns boolean
|
|
|
|
*/
|
|
|
|
function is_product_related_to_supplier(product, supplier) {
|
|
|
|
return product.suppliers.find(s => s.id === supplier.id) !== undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* - INVENTORY */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an inventory with the selected lines in the table
|
|
|
|
*/
|
|
|
|
function generate_inventory() {
|
|
|
|
if (products_table !== null) {
|
|
|
|
const selected_data = products_table.rows('.selected').data();
|
|
|
|
|
|
|
|
if (selected_data.length == 0) {
|
|
|
|
alert("Veuillez sélectionner les produits à inventorier en cochant les cases sur la gauche du tableau.");
|
|
|
|
} else {
|
|
|
|
data = {
|
|
|
|
lines: [],
|
|
|
|
partners_id: [],
|
|
|
|
type: 'product_templates'
|
|
|
|
};
|
|
|
|
|
|
|
|
for (var i = 0; i < selected_data.length; i++) {
|
|
|
|
const product = products.find(p => p.id == selected_data[i].id);
|
|
|
|
|
|
|
|
data.lines.push(product.id);
|
|
|
|
for (const supplier of product.suppliers) {
|
|
|
|
if (data.partners_id.indexOf(supplier.id) === -1) {
|
|
|
|
data.partners_id.push(supplier.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let modal_create_inventory = $('#templates #modal_create_inventory');
|
|
|
|
|
|
|
|
modal_create_inventory.find(".inventory_products_count").text(data.lines.length);
|
|
|
|
|
|
|
|
openModal(
|
|
|
|
modal_create_inventory.html(),
|
|
|
|
() => {
|
|
|
|
$.ajax({
|
|
|
|
type: "POST",
|
|
|
|
url: "/inventory/generate_inventory_list",
|
|
|
|
dataType: "json",
|
|
|
|
traditional: true,
|
|
|
|
contentType: "application/json; charset=utf-8",
|
|
|
|
data: JSON.stringify(data),
|
|
|
|
success: () => {
|
|
|
|
unselect_all_rows();
|
|
|
|
|
|
|
|
// Give time for modal to fade
|
|
|
|
setTimeout(function() {
|
|
|
|
$.notify(
|
|
|
|
"Inventaire créé !",
|
|
|
|
{
|
|
|
|
globalPosition:"top left",
|
|
|
|
className: "success"
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}, 500);
|
|
|
|
},
|
|
|
|
error: function(data) {
|
|
|
|
let msg = "erreur serveur lors de la création de l'inventaire".
|
|
|
|
err = {msg: msg, ctx: 'generate_inventory'};
|
|
|
|
|
|
|
|
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') {
|
|
|
|
err.msg += ' : ' + data.responseJSON.error;
|
|
|
|
}
|
|
|
|
report_JS_error(err, 'orders');
|
|
|
|
|
|
|
|
alert("Erreur lors de la création de l'inventaire. Réessayez plus tard.");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
'Valider'
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* - DISPLAY */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create a string to represent a supplier column in product data
|
|
|
|
* @returns String
|
|
|
|
*/
|
|
|
|
function supplier_column_name(supplier) {
|
|
|
|
return `qty_supplier_${supplier.id}`;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Display the selected suppliers
|
|
|
|
*/
|
|
|
|
function display_suppliers() {
|
|
|
|
let supplier_container = $("#suppliers_container");
|
|
|
|
|
|
|
|
$("#suppliers_container").empty();
|
|
|
|
$(".remove_supplier_icon").off();
|
|
|
|
|
|
|
|
for (supplier of selected_suppliers) {
|
|
|
|
let template = $("#templates #supplier_pill_template");
|
|
|
|
|
|
|
|
template.find(".pill_supplier_name").text(supplier.display_name);
|
|
|
|
template.find(".remove_supplier_icon").attr('id', `remove_supplier_${supplier.id}`);
|
|
|
|
|
|
|
|
supplier_container.append(template.html());
|
|
|
|
}
|
|
|
|
|
|
|
|
$(".remove_supplier_icon").on("click", function() {
|
|
|
|
const el_id = $(this).attr('id')
|
|
|
|
.split('_');
|
|
|
|
const supplier_id = el_id[el_id.length-1];
|
|
|
|
|
|
|
|
let modal_remove_supplier = $('#templates #modal_remove_supplier');
|
|
|
|
|
|
|
|
modal_remove_supplier.find(".supplier_name").text(supplier.display_name);
|
|
|
|
|
|
|
|
openModal(
|
|
|
|
modal_remove_supplier.html(),
|
|
|
|
() => {
|
|
|
|
remove_supplier(supplier_id);
|
|
|
|
},
|
|
|
|
'Valider'
|
|
|
|
);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param {array} product_ids if set, return formatted data for these products only
|
|
|
|
* @returns Array of formatted data for datatable data setup
|
|
|
|
*/
|
|
|
|
function prepare_datatable_data(product_ids = []) {
|
|
|
|
let data = [];
|
|
|
|
let products_to_format = [];
|
|
|
|
|
|
|
|
if (product_ids.length > 0) {
|
|
|
|
products_to_format = products.filter(p => product_ids.includes(p.id));
|
|
|
|
} else {
|
|
|
|
products_to_format = products;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (product of products_to_format) {
|
|
|
|
let item = {
|
|
|
|
id: product.id,
|
|
|
|
name: product.name,
|
|
|
|
default_code: product.default_code,
|
|
|
|
incoming_qty: +parseFloat(product.incoming_qty).toFixed(3), // + sign removes unecessary zeroes at the end
|
|
|
|
qty_available: +parseFloat(product.qty_available).toFixed(3),
|
|
|
|
uom: product.uom_id[1]
|
|
|
|
};
|
|
|
|
|
|
|
|
// If product not related to supplier : false ; else null (qty to be set) or qty
|
|
|
|
for (product_supplier of product.suppliers) {
|
|
|
|
item[supplier_column_name(product_supplier)] = ("qty" in product_supplier) ? product_supplier.qty : null;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (supplier of selected_suppliers) {
|
|
|
|
if (!is_product_related_to_supplier(product, supplier)) {
|
|
|
|
item[supplier_column_name(supplier)] = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
data.push(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @returns Array of formatted data for datatable columns setup
|
|
|
|
*/
|
|
|
|
function prepare_datatable_columns() {
|
|
|
|
columns = [
|
|
|
|
{
|
|
|
|
data: "id",
|
|
|
|
title:` <div id="table_header_select_all">
|
|
|
|
Tout
|
|
|
|
<input type="checkbox" class="select_product_cb" id="select_all_products_cb" value="all">
|
|
|
|
</div>`,
|
|
|
|
className:"dt-body-center",
|
|
|
|
orderable: false,
|
|
|
|
render: function (data) {
|
|
|
|
return `<input type="checkbox" class="select_product_cb" id="select_product_${data}" value="${data}">`;
|
|
|
|
},
|
|
|
|
width: "4%"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: "default_code",
|
|
|
|
title: "Référence Produit",
|
|
|
|
width: "8%",
|
|
|
|
render: function (data) {
|
|
|
|
return (data === false) ? "" : data;
|
|
|
|
}
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: "name",
|
|
|
|
title: "Produit"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: "qty_available",
|
|
|
|
title: "Stock",
|
|
|
|
width: "4%"
|
|
|
|
},
|
|
|
|
{
|
|
|
|
data: "incoming_qty",
|
|
|
|
title: "Quantité entrante",
|
|
|
|
width: "4%"
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
for (const supplier of selected_suppliers) {
|
|
|
|
columns.push({
|
|
|
|
data: supplier_column_name(supplier),
|
|
|
|
title: supplier.display_name,
|
|
|
|
width: "10%",
|
|
|
|
className: `dt-body-center supplier_input_cell`,
|
|
|
|
render: (data, type, full) => {
|
|
|
|
const base_id = `product_${full.id}_supplier_${supplier.id}`;
|
|
|
|
|
|
|
|
if (data === false) {
|
|
|
|
return `<div id="${base_id}_cell_content" class="cell_content">X</div>`;
|
|
|
|
} else {
|
|
|
|
return `<div id="${base_id}_cell_content" class="cell_content">
|
|
|
|
<input type="number" class="product_qty_input" id="${base_id}_qty_input" value=${data}>
|
|
|
|
</div>`;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
columns.push({
|
|
|
|
data: "uom",
|
|
|
|
title: "UDM",
|
|
|
|
width: "4%"
|
|
|
|
});
|
|
|
|
|
|
|
|
return columns;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Display the Datatable containing the products
|
|
|
|
*/
|
|
|
|
function display_products() {
|
|
|
|
if (products.length == 0) {
|
|
|
|
$('.main').hide();
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Empty datatable if it already exists
|
|
|
|
if (products_table) {
|
|
|
|
products_table.clear().destroy();
|
|
|
|
$('#products_table').empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
const data = prepare_datatable_data();
|
|
|
|
const columns = prepare_datatable_columns();
|
|
|
|
|
|
|
|
products_table = $('#products_table').DataTable({
|
|
|
|
data: data,
|
|
|
|
columns: columns,
|
|
|
|
order: [
|
|
|
|
[
|
|
|
|
5, // Order by default by first supplier
|
|
|
|
"asc"
|
|
|
|
]
|
|
|
|
],
|
|
|
|
stripeClasses: [], // Remove datatable cells coloring
|
|
|
|
orderClasses: false,
|
|
|
|
iDisplayLength: 100,
|
|
|
|
scrollX: true,
|
|
|
|
language: {url : '/static/js/datatables/french.json'},
|
|
|
|
createdRow: function(row) {
|
|
|
|
for (const cell_node of row.cells) {
|
|
|
|
const cell = $(cell_node);
|
|
|
|
|
|
|
|
if (cell.hasClass("supplier_input_cell")) {
|
|
|
|
if (cell.text() == "X") {
|
|
|
|
cell.addClass('product_not_from_supplier');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
$('.main').show();
|
|
|
|
|
|
|
|
// Save value on inputs change
|
|
|
|
$('#products_table').on('input', 'tbody td .product_qty_input', function () {
|
|
|
|
let val = parseFloat($(this).val());
|
|
|
|
|
|
|
|
// If value is a number
|
|
|
|
if (!isNaN(val)) {
|
|
|
|
const id_split = $(this).attr('id')
|
|
|
|
.split('_');
|
|
|
|
const prod_id = id_split[1];
|
|
|
|
const supplier_id = id_split[3];
|
|
|
|
|
|
|
|
save_product_supplier_qty(prod_id, supplier_id, val);
|
|
|
|
update_order();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// Associate product to supplier on click on 'X' in the table
|
|
|
|
$('#products_table').on('click', 'tbody .product_not_from_supplier', function () {
|
|
|
|
// Get supplier & product id
|
|
|
|
const el_id = $(this).children()
|
|
|
|
.first()
|
|
|
|
.attr('id')
|
|
|
|
.split('_');
|
|
|
|
const product_id = el_id[1];
|
|
|
|
const supplier_id = el_id[3];
|
|
|
|
|
|
|
|
const product = products.find(p => p.id == product_id);
|
|
|
|
const supplier = selected_suppliers.find(s => s.id == supplier_id);
|
|
|
|
|
|
|
|
let modal_attach_product_to_supplier = $('#templates #modal_attach_product_to_supplier');
|
|
|
|
|
|
|
|
modal_attach_product_to_supplier.find(".product_name").text(product.name);
|
|
|
|
modal_attach_product_to_supplier.find(".supplier_name").text(supplier.display_name);
|
|
|
|
|
|
|
|
openModal(
|
|
|
|
modal_attach_product_to_supplier.html(),
|
|
|
|
() => {
|
|
|
|
save_supplier_product_association(product, supplier, this);
|
|
|
|
},
|
|
|
|
'Valider',
|
|
|
|
false
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Select row(s) on checkbox change
|
|
|
|
$(products_table.table().header()).on('click', 'th #select_all_products_cb', function () {
|
|
|
|
if (this.checked) {
|
|
|
|
selected_rows = [];
|
|
|
|
products_table.rows().every(function() {
|
|
|
|
const node = $(this.node());
|
|
|
|
|
|
|
|
node.addClass('selected');
|
|
|
|
node.find(".select_product_cb").first()
|
|
|
|
.prop("checked", true);
|
|
|
|
|
|
|
|
// Save selected rows in case the table is updated
|
|
|
|
selected_rows.push(this.data().id);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
unselect_all_rows();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
$('#products_table').on('click', 'tbody td .select_product_cb', function () {
|
|
|
|
$(this).closest('tr')
|
|
|
|
.toggleClass('selected');
|
|
|
|
|
|
|
|
// Save / unsave selected row
|
|
|
|
p_id = products_table.row($(this).closest('tr')).data().id;
|
|
|
|
if (this.checked) {
|
|
|
|
selected_rows.push(p_id);
|
|
|
|
} else {
|
|
|
|
const i = selected_rows.findIndex(id => id == p_id);
|
|
|
|
|
|
|
|
selected_rows.splice(i, 1);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unselect all rows from datatable.
|
|
|
|
*/
|
|
|
|
function unselect_all_rows() {
|
|
|
|
products_table.rows().every(function() {
|
|
|
|
const node = $(this.node());
|
|
|
|
|
|
|
|
node.removeClass('selected');
|
|
|
|
node.find(".select_product_cb").first()
|
|
|
|
.prop("checked", false);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
|
|
|
|
selected_rows = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update DOM display on main screen
|
|
|
|
*/
|
|
|
|
function update_main_screen() {
|
|
|
|
// Remove listener before recreating them
|
|
|
|
$('#products_table').off('input', 'tbody td .product_qty_input');
|
|
|
|
$('#products_table').off('click', 'tbody .product_not_from_supplier');
|
|
|
|
$('#products_table').off('click', 'thead th #select_all_products_cb');
|
|
|
|
$('#products_table').off('click', 'tbody td .select_product_cb');
|
|
|
|
$(".remove_supplier_icon").off();
|
|
|
|
|
|
|
|
$(".order_name_container").text(order_doc._id);
|
|
|
|
display_suppliers();
|
|
|
|
display_products();
|
|
|
|
|
|
|
|
// Re-select previously selected rows
|
|
|
|
if (selected_rows.length > 0) {
|
|
|
|
products_table.rows().every(function() {
|
|
|
|
if (selected_rows.includes(this.data().id)) {
|
|
|
|
const node = $(this.node());
|
|
|
|
|
|
|
|
node.addClass('selected');
|
|
|
|
node.find(".select_product_cb").first()
|
|
|
|
.prop("checked", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update DOM display on the order selection screen
|
|
|
|
*/
|
|
|
|
function update_order_selection_screen() {
|
|
|
|
// Remove listener before recreating them
|
|
|
|
$(".order_pill").off();
|
|
|
|
|
|
|
|
let existing_orders_container = $("#existing_orders");
|
|
|
|
existing_orders_container.empty();
|
|
|
|
|
|
|
|
dbc.allDocs({
|
|
|
|
include_docs: true
|
|
|
|
}).then(function (result) {
|
|
|
|
if (result.rows.length === 0) {
|
|
|
|
existing_orders_container.append(`<i>Aucune commande en cours...</i>`);
|
|
|
|
} else {
|
|
|
|
for (let row of result.rows) {
|
|
|
|
let template = $("#templates #order_pill_template");
|
|
|
|
template.find(".pill_order_name").text(row.id);
|
|
|
|
|
|
|
|
existing_orders_container.append(template.html());
|
|
|
|
}
|
|
|
|
|
|
|
|
$(".order_pill").on("click", order_pill_on_click);
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(function (err) {
|
|
|
|
alert('Erreur lors de la synchronisation des commandes. Vous pouvez créer une nouvelle commande.');
|
|
|
|
console.log(err);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Switch screen between order selection & main screens
|
|
|
|
* @param {String} direction target screen
|
|
|
|
*/
|
|
|
|
function switch_screen(direction = 'main_screen') {
|
|
|
|
let oldBox = null;
|
|
|
|
let newBox = null;
|
|
|
|
let outerWidth = null;
|
|
|
|
|
|
|
|
if (direction == 'main_screen') {
|
|
|
|
oldBox = $("#select_order_content");
|
|
|
|
newBox = $("#main_content");
|
|
|
|
|
|
|
|
outerWidth = oldBox.outerWidth(true);
|
|
|
|
} else {
|
|
|
|
oldBox = $("#main_content");
|
|
|
|
newBox = $("#select_order_content");
|
|
|
|
|
|
|
|
outerWidth = - oldBox.outerWidth(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Display the new box and place it on the right of the screen
|
|
|
|
newBox.css({ "left": outerWidth + "px", "right": -outerWidth + "px", "display": "" });
|
|
|
|
// Make the old content slide to the left
|
|
|
|
oldBox.animate({ "left": -outerWidth + "px", "right": outerWidth + "px" }, 800, function() {
|
|
|
|
// Hide old content after animation
|
|
|
|
oldBox.css({ "left": "", "right": "", "display": "none" });
|
|
|
|
});
|
|
|
|
// Slide new box to regular place
|
|
|
|
newBox.animate({ "left": "", "right": "" }, 800);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* - ORDER */
|
|
|
|
|
|
|
|
function goto_main_screen(doc) {
|
|
|
|
order_doc = doc;
|
|
|
|
products = order_doc.products;
|
|
|
|
selected_suppliers = order_doc.selected_suppliers;
|
|
|
|
|
|
|
|
update_order()
|
|
|
|
update_main_screen();
|
|
|
|
switch_screen();
|
|
|
|
}
|
|
|
|
|
|
|
|
function back() {
|
|
|
|
reset_data();
|
|
|
|
update_order_selection_screen();
|
|
|
|
switch_screen('order_selection');
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Event fct: on click on an order button
|
|
|
|
*/
|
|
|
|
function order_pill_on_click() {
|
|
|
|
let order_name_container = $(this).find('.pill_order_name');
|
|
|
|
let doc_id = $(order_name_container).text();
|
|
|
|
|
|
|
|
dbc.get(doc_id).then((doc) => {
|
|
|
|
if (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_main_screen(doc)
|
|
|
|
},
|
|
|
|
'Valider'
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
goto_main_screen(doc)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.catch(function (err) {
|
|
|
|
alert('Erreur lors de la récupération de la commande. Si l\'erreur persiste, contactez un administrateur svp.');
|
|
|
|
console.log(err);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an order in couchdb if the name doesn't exist
|
|
|
|
*/
|
|
|
|
function create_order() {
|
|
|
|
const order_name = $("#new_order_name").val();
|
|
|
|
|
|
|
|
order_doc._id = order_name;
|
|
|
|
dbc.put(order_doc, function callback(err, result) {
|
|
|
|
if (!err) {
|
|
|
|
order_doc._rev = result.rev;
|
|
|
|
switch_screen();
|
|
|
|
} else {
|
|
|
|
if (err.status == 409) {
|
|
|
|
alert("Une commande porte déjà ce nom !");
|
|
|
|
}
|
|
|
|
console.log(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update order data of an existing order in couchdb
|
|
|
|
*/
|
|
|
|
function update_order() {
|
|
|
|
order_doc.products = products;
|
|
|
|
order_doc.selected_suppliers = selected_suppliers;
|
|
|
|
|
|
|
|
// Save that current user last updated the order
|
|
|
|
order_doc.last_update = {
|
|
|
|
timestamp: Date.now(),
|
|
|
|
fingerprint: fingerprint,
|
|
|
|
};
|
|
|
|
|
|
|
|
dbc.put(order_doc, function callback(err, result) {
|
|
|
|
if (!err && result !== undefined) {
|
|
|
|
order_doc._rev = result.rev;
|
|
|
|
} else {
|
|
|
|
alert("Erreur lors de la sauvegarde de la commande... Si l'erreur persiste contactez un administrateur svp.");
|
|
|
|
console.log(err);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$(document).ready(function() {
|
|
|
|
fingerprint = new Fingerprint({canvas: true}).get();
|
|
|
|
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
|
|
|
|
|
|
|
|
openModal();
|
|
|
|
|
|
|
|
// 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) {
|
|
|
|
// If current order was modified somewhere else
|
|
|
|
if (order_doc._id === doc._id && order_doc._rev !== doc._rev) {
|
|
|
|
$.notify(
|
|
|
|
"Un autre navigateur est en train de modifier cette commande !",
|
|
|
|
{
|
|
|
|
globalPosition:"top right",
|
|
|
|
className: "error"
|
|
|
|
}
|
|
|
|
);
|
|
|
|
back();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}).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);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Main screen listeners
|
|
|
|
$("#supplier_form").on("submit", function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
add_supplier();
|
|
|
|
});
|
|
|
|
|
|
|
|
$("#do_inventory").on("click", function() {
|
|
|
|
generate_inventory();
|
|
|
|
});
|
|
|
|
|
|
|
|
$('#back_to_order_selection').on('click', function() {
|
|
|
|
back();
|
|
|
|
});
|
|
|
|
|
|
|
|
// Order selection screen
|
|
|
|
update_order_selection_screen();
|
|
|
|
|
|
|
|
$("#new_order_form").on("submit", function(e) {
|
|
|
|
e.preventDefault();
|
|
|
|
create_order();
|
|
|
|
});
|
|
|
|
|
|
|
|
// Get suppliers
|
|
|
|
$.ajax({
|
|
|
|
type: 'GET',
|
|
|
|
url: "/orders/get_suppliers",
|
|
|
|
dataType:"json",
|
|
|
|
traditional: true,
|
|
|
|
contentType: "application/json; charset=utf-8",
|
|
|
|
success: function(data) {
|
|
|
|
suppliers_list = data.res;
|
|
|
|
|
|
|
|
// Set up autocomplete on supplier input
|
|
|
|
$("#supplier_input").autocomplete({
|
|
|
|
source: suppliers_list.map(a => a.display_name)
|
|
|
|
});
|
|
|
|
|
|
|
|
closeModal();
|
|
|
|
},
|
|
|
|
error: function(data) {
|
|
|
|
err = {msg: "erreur serveur lors de la récupération des fournisseurs", ctx: 'get_suppliers'};
|
|
|
|
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 fournisseurs, rechargez la page plus tard');
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}); |