Commit f53fd634 by Damien Moulard

Merge branch 'aide_a_la_commande' into dev_cooperatic

parents 313c7e0f 2d254598
...@@ -292,7 +292,9 @@ class Order(models.Model): ...@@ -292,7 +292,9 @@ class Order(models.Model):
res = { res = {
'id_po': id_po, 'id_po': id_po,
'confirm_po': True 'confirm_po': True,
'supplier_id': supplier_id,
'date_planned': date_planned
} }
return res return res
......
...@@ -91,11 +91,11 @@ ...@@ -91,11 +91,11 @@
#supplier_input { #supplier_input {
width: 350px; width: 350px;
border-radius: 8px; border-radius: 3px;
} }
#date_planned_input, #coverage_days_input { #date_planned_input, #coverage_days_input {
border-radius: 8px; border-radius: 3px;
} }
/* -- Table */ /* -- Table */
...@@ -106,7 +106,7 @@ ...@@ -106,7 +106,7 @@
#products_table_filter input{ #products_table_filter input{
height: 35px; height: 35px;
width: 300px; width: 300px;
border-radius: 10px; border-radius: 3px;
} }
#products_table .help {cursor: help;} #products_table .help {cursor: help;}
#table_header_select_all{ #table_header_select_all{
...@@ -154,12 +154,34 @@ ...@@ -154,12 +154,34 @@
cursor: pointer; cursor: pointer;
} }
/* -- Bottom action button */ /* -- Footer */
#main_content_footer { #main_content_footer {
margin: 10px 0 35px 0;
}
#footer_orders_recap {
width: 100%;
display: flex;
justify-content: space-evenly;
margin-bottom: 15px;
}
#footer_actions {
width: 100%;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin: 15px 0 35px 0; }
#suppliers_total_values {
display: flex;
}
.supplier_total_item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
} }
/* -- Suppliers list */ /* -- Suppliers list */
...@@ -182,27 +204,32 @@ ...@@ -182,27 +204,32 @@
} }
/* -- Attach product to supplier modal */ /* -- Attach product to supplier modal */
.new_product_supplier_input_area { .modal_input_area {
margin-bottom: 15px; margin-bottom: 15px;
width: 100%; width: 100%;
display: flex; display: flex;
justify-content: center; justify-content: center;
} }
.new_product_supplier_input, .new_product_supplier_label { .modal_input_container, .modal_input_label {
width:50%; width:50%;
margin: 0 15px 0 15px; margin: 0 15px 0 15px;
} }
.new_product_supplier_label { .modal_input_label {
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center; align-items: center;
} }
.modal_input {
width: 90%;
}
/* - Orders created screen */ /* - Orders created screen */
.order_created_header { .order_created_header {
margin-top: 5px;
margin-bottom: 40px; margin-bottom: 40px;
} }
...@@ -227,6 +254,14 @@ ...@@ -227,6 +254,14 @@
text-decoration: none; text-decoration: none;
color: white; color: white;
} }
.download_order_file_button:active {
text-decoration: none;
color: white;
}
.download_order_file_button:focus {
text-decoration: none;
color: white;
}
#recap_delivery_date { #recap_delivery_date {
font-weight: bold; font-weight: bold;
......
...@@ -15,7 +15,6 @@ var dbc = null, ...@@ -15,7 +15,6 @@ var dbc = null,
sync = null, sync = null,
order_doc = { order_doc = {
_id: null, _id: null,
date_planned: null,
coverage_days: null, coverage_days: null,
last_update: { last_update: {
timestamp: null, timestamp: null,
...@@ -42,7 +41,6 @@ function reset_data() { ...@@ -42,7 +41,6 @@ function reset_data() {
product_orders = []; product_orders = [];
order_doc = { order_doc = {
_id: null, _id: null,
date_planned: null,
coverage_days: null, coverage_days: null,
last_update : { last_update : {
timestamp: null, timestamp: null,
...@@ -112,19 +110,6 @@ function add_product() { ...@@ -112,19 +110,6 @@ function add_product() {
return -1; return -1;
} }
/*
onst product_ids = products.map(p => p.id);
if (product_ids.length > 0) {
clicked_order_pill.find('.pill_order_name').empty().append(`<i class="fas fa-spinner fa-spin"></i>`);
$.ajax({
type: 'POST',
url: '/products/get_product_for_order_helper',
data: JSON.stringify(product_ids),
dataType:"json",
*/
$.ajax({ $.ajax({
type: 'POST', type: 'POST',
url: '/products/get_product_for_order_helper', url: '/products/get_product_for_order_helper',
...@@ -167,6 +152,7 @@ function compute_products_coverage_qties() { ...@@ -167,6 +152,7 @@ function compute_products_coverage_qties() {
key, key,
product product
] of Object.entries(products)) { ] of Object.entries(products)) {
if ('suppliersinfo' in product && product.suppliersinfo.length > 0) {
let purchase_qty_for_coverage = null; let purchase_qty_for_coverage = null;
// Durée couverture produit = (stock + qté entrante + qté commandée ) / conso quotidienne // Durée couverture produit = (stock + qté entrante + qté commandée ) / conso quotidienne
...@@ -180,43 +166,60 @@ function compute_products_coverage_qties() { ...@@ -180,43 +166,60 @@ function compute_products_coverage_qties() {
// Reduce to nb of packages to purchase // Reduce to nb of packages to purchase
purchase_package_qty_for_coverage = purchase_qty_for_coverage / product.suppliersinfo[0].package_qty; purchase_package_qty_for_coverage = purchase_qty_for_coverage / product.suppliersinfo[0].package_qty;
// Round according to uom // Round up to unit for all products
if (product.uom_id[0] == 1 || product.uom_id[0] == 20) { purchase_package_qty_for_coverage = Math.ceil(purchase_package_qty_for_coverage);
purchase_package_qty_for_coverage = parseFloat(purchase_package_qty_for_coverage).toFixed(0);
} else {
purchase_package_qty_for_coverage = parseFloat(purchase_package_qty_for_coverage).toFixed(2);
}
// 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;
} }
}
} }
/** /**
* Update order products data in case they have changed. * Update order products data in case they have changed.
*/ */
function check_products_data() { function check_products_data() {
return new Promise((resolve, reject) => { return new Promise((resolve) => {
const product_ids = products.map(p => p.id); const suppliers_id = selected_suppliers.map(s => s.id);
if (suppliers_id.length > 0) {
$.notify(
"Vérfication des informations produits...",
{
globalPosition:"top left",
className: "warning"
}
);
if (product_ids.length > 0) {
clicked_order_pill.find('.pill_order_name').empty().append(`<i class="fas fa-spinner fa-spin"></i>`); clicked_order_pill.find('.pill_order_name').empty().append(`<i class="fas fa-spinner fa-spin"></i>`);
$.ajax({ $.ajax({
type: 'POST', type: 'GET',
url: '/products/get_product_for_order_helper', url: '/orders/get_supplier_products',
data: JSON.stringify(product_ids), data: {
sids: suppliers_id
},
dataType:"json", dataType:"json",
traditional: true, traditional: true,
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
success: function(data) { success: function(data) {
for (let product of data.products) { for (let product of data.res.products) {
const p_index = products.findIndex(p => p.id == product.id); const p_index = products.findIndex(p => p.id == product.id);
// Override products data with new data // Override products data with new data (without suppliersinfo so we don't override qty)
const updated_suppliersinfo = product.suppliersinfo;
delete product.suppliersinfo;
products[p_index] = { ...products[p_index], ...product }; products[p_index] = { ...products[p_index], ...product };
// Update suppliers info
for (let psi_index in products[p_index].suppliersinfo) {
const updated_psi = updated_suppliersinfo.find(psi => psi.supplier_id == products[p_index].suppliersinfo[psi_index].supplier_id);
products[p_index].suppliersinfo[psi_index].package_qty = updated_psi.package_qty;
products[p_index].suppliersinfo[psi_index].price = updated_psi.price;
}
} }
$('.notifyjs-wrapper').trigger('notify-hide');
resolve(); resolve();
}, },
error: function(data) { error: function(data) {
...@@ -227,6 +230,7 @@ function check_products_data() { ...@@ -227,6 +230,7 @@ function check_products_data() {
report_JS_error(err, 'orders'); report_JS_error(err, 'orders');
alert(`Erreur lors de la vérification des données des articles. Certaines données peuvent être erronées`); alert(`Erreur lors de la vérification des données des articles. Certaines données peuvent être erronées`);
$('.notifyjs-wrapper').trigger('notify-hide');
// Don't block process if this call fails // Don't block process if this call fails
resolve(); resolve();
} }
...@@ -249,7 +253,7 @@ function add_supplier() { ...@@ -249,7 +253,7 @@ function add_supplier() {
const user_input = $("#supplier_input").val(); const user_input = $("#supplier_input").val();
// Check if user input is a valid supplier // Check if user input is a valid supplier
const supplier = suppliers_list.find(s => s.display_name === user_input); let supplier = suppliers_list.find(s => s.display_name === user_input);
if (supplier === undefined) { if (supplier === undefined) {
alert("Le fournisseur renseigné n'est pas valide.\n" alert("Le fournisseur renseigné n'est pas valide.\n"
...@@ -268,11 +272,11 @@ function add_supplier() { ...@@ -268,11 +272,11 @@ function add_supplier() {
openModal(); openModal();
supplier.total_value = 0;
selected_suppliers.push(supplier); selected_suppliers.push(supplier);
let url = "/orders/get_supplier_products"; let url = "/orders/get_supplier_products";
url += "?sids=" + encodeURIComponent(supplier.id);
url += "?sid=" + encodeURIComponent(supplier.id);
// Fetch supplier products // Fetch supplier products
$.ajax({ $.ajax({
...@@ -465,6 +469,25 @@ function is_product_related_to_supplier(product, supplier) { ...@@ -465,6 +469,25 @@ function is_product_related_to_supplier(product, supplier) {
return product.suppliersinfo.find(s => s.supplier_id === supplier.id) !== undefined; return product.suppliersinfo.find(s => s.supplier_id === supplier.id) !== undefined;
} }
/**
* Calculate the total value purchased for all supplier
*/
function _compute_total_values_by_supplier() {
// Reinit
for (let s of selected_suppliers) {
s.total_value = 0;
}
for (let p of products) {
for (let supinfo of p.suppliersinfo) {
let supplier_index = selected_suppliers.findIndex(s => s.id == supinfo.supplier_id);
let product_supplier_value = ('qty' in supinfo) ? supinfo.qty * supinfo.price : 0;
selected_suppliers[supplier_index].total_value += product_supplier_value;
}
}
}
/* - PRODUCT */ /* - PRODUCT */
/** /**
...@@ -492,7 +515,20 @@ function set_product_npa(p_id, npa) { ...@@ -492,7 +515,20 @@ function set_product_npa(p_id, npa) {
success: () => { success: () => {
const index = products.findIndex(p => p.id == p_id); const index = products.findIndex(p => p.id == p_id);
products[index].purchase_ok = data["purchase_ok"]; // Give time for modal to fade
setTimeout(function() {
$.notify(
"Produit passé en NPA !",
{
globalPosition:"top right",
className: "success"
}
);
}, 500);
// Remove NPA products
products.splice(index, 1);
update_main_screen();
update_cdb_order(); update_cdb_order();
closeModal(); closeModal();
...@@ -550,6 +586,7 @@ function generate_inventory() { ...@@ -550,6 +586,7 @@ function generate_inventory() {
openModal( openModal(
modal_create_inventory.html(), modal_create_inventory.html(),
() => { () => {
$('#do_inventory').empty().append(`<i class="fas fa-spinner fa-spin"></i>`);
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: "/inventory/generate_inventory_list", url: "/inventory/generate_inventory_list",
...@@ -562,6 +599,7 @@ function generate_inventory() { ...@@ -562,6 +599,7 @@ function generate_inventory() {
// Give time for modal to fade // Give time for modal to fade
setTimeout(function() { setTimeout(function() {
$('#do_inventory').empty().append(`Faire un inventaire`);
$('#do_inventory').notify( $('#do_inventory').notify(
"Inventaire créé !", "Inventaire créé !",
{ {
...@@ -569,9 +607,10 @@ function generate_inventory() { ...@@ -569,9 +607,10 @@ function generate_inventory() {
className: "success" className: "success"
} }
); );
}, 500); }, 200);
}, },
error: function(data) { error: function(data) {
$('#do_inventory').empty().append(`Faire un inventaire`);
let msg = "erreur serveur lors de la création de l'inventaire". let msg = "erreur serveur lors de la création de l'inventaire".
err = {msg: msg, ctx: 'generate_inventory'}; err = {msg: msg, ctx: 'generate_inventory'};
...@@ -686,27 +725,64 @@ function update_cdb_order() { ...@@ -686,27 +725,64 @@ function update_cdb_order() {
fingerprint: fingerprint fingerprint: fingerprint
}; };
return new Promise((resolve) => {
dbc.put(order_doc, function callback(err, result) { dbc.put(order_doc, function callback(err, result) {
if (!err && result !== undefined) { if (!err && result !== undefined) {
order_doc._rev = result.rev; order_doc._rev = result.rev;
resolve();
} else { } else {
alert("Erreur lors de la sauvegarde de la commande... Si l'erreur persiste contactez un administrateur svp."); alert("Erreur lors de la sauvegarde de la commande... Si l'erreur persiste contactez un administrateur svp.");
console.log(err); console.log(err);
resolve();
} }
}); });
});
} }
/** /**
* Create the Product Orders in Odoo * Create the Product Orders in Odoo
*/ */
function create_orders() { function create_orders() {
openModal();
let orders_data = { let orders_data = {
"date_planned": order_doc.date_planned,
"suppliers_data": {} "suppliers_data": {}
}; };
// Get planned delivery date for each supplier before hiding the modal
for (let supplier of selected_suppliers) {
// Get planned date from modal
let supplier_date_planned = $(`#date_planned_supplier_${supplier.id}`).val();
let formatted_date = null;
if (supplier_date_planned !== '') {
if (date_format === "dd/mm/yy") {
// Change format [dd/mm/yy] to ISO [yy-mm-dd]
formatted_date = supplier_date_planned
.split('/')
.reverse()
.join('-') + ' 00:00:00';
} else {
formatted_date = supplier_date_planned + ' 00:00:00';
}
} else {
// Default date : tomorrow
let date_object = new Date();
date_object.setDate(date_object.getDate() + 1);
// Get ISO format bare string
formatted_date = date_object.toISOString().replace('T', ' ').split('.')[0];
}
// Create an entry for this supplier
orders_data.suppliers_data[supplier.id] = {
date_planned: formatted_date,
lines: []
}
}
openModal();
// Prepare data: get products where a qty is set // Prepare data: get products where a qty is set
for (let p of products) { for (let p of products) {
for (let p_supplierinfo of p.suppliersinfo) { for (let p_supplierinfo of p.suppliersinfo) {
...@@ -714,12 +790,7 @@ function create_orders() { ...@@ -714,12 +790,7 @@ function create_orders() {
if ('qty' in p_supplierinfo) { if ('qty' in p_supplierinfo) {
const supplier_id = p_supplierinfo.supplier_id; const supplier_id = p_supplierinfo.supplier_id;
// Create entry for this supplier in data object if doesn't exist orders_data.suppliers_data[supplier_id].lines.push({
if (orders_data.suppliers_data[supplier_id] === undefined) {
orders_data.suppliers_data[supplier_id] = [];
}
orders_data.suppliers_data[supplier_id].push({
'package_qty': p_supplierinfo.package_qty, 'package_qty': p_supplierinfo.package_qty,
'product_id': p.id, 'product_id': p.id,
'name': p.name, 'name': p.name,
...@@ -734,13 +805,6 @@ function create_orders() { ...@@ -734,13 +805,6 @@ function create_orders() {
} }
} }
if (Object.keys(orders_data.suppliers_data).length === 0) {
closeModal();
alert("Commande non créée : vous n'avez rentré aucune quantité !");
return -1;
}
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: "/orders/create_orders", url: "/orders/create_orders",
...@@ -749,12 +813,16 @@ function create_orders() { ...@@ -749,12 +813,16 @@ function create_orders() {
contentType: "application/json; charset=utf-8", contentType: "application/json; charset=utf-8",
data: JSON.stringify(orders_data), data: JSON.stringify(orders_data),
success: (result) => { success: (result) => {
$('#recap_delivery_date').text($('#date_planned_input').val());
// Display new orders // Display new orders
for (let new_order of result.res.created) { for (let new_order of result.res.created) {
const supplier_name = suppliers_list.find(s => s.id == new_order.supplier_id).display_name; const supplier_name = suppliers_list.find(s => s.id == new_order.supplier_id).display_name;
const date_planned = new_order.date_planned
.split(' ')[0]
.split('-')
.reverse()
.join('/');
product_orders.push({ product_orders.push({
'id': new_order.id_po, 'id': new_order.id_po,
'supplier_id': new_order.supplier_id, 'supplier_id': new_order.supplier_id,
...@@ -765,6 +833,7 @@ function create_orders() { ...@@ -765,6 +833,7 @@ function create_orders() {
new_order_template.find(".new_order_supplier_name").text(supplier_name); new_order_template.find(".new_order_supplier_name").text(supplier_name);
new_order_template.find(".new_order_po").text(`PO${new_order.id_po}`); new_order_template.find(".new_order_po").text(`PO${new_order.id_po}`);
new_order_template.find(".new_order_date_planned").text(`Date de livraison prévue: ${date_planned}`);
new_order_template.find(".download_order_file_button").attr('id', `download_attachment_${new_order.id_po}`); new_order_template.find(".download_order_file_button").attr('id', `download_attachment_${new_order.id_po}`);
$('#created_orders_area').append(new_order_template.html()); $('#created_orders_area').append(new_order_template.html());
...@@ -775,10 +844,10 @@ function create_orders() { ...@@ -775,10 +844,10 @@ function create_orders() {
// Clear data // Clear data
order_doc._deleted = true; order_doc._deleted = true;
update_cdb_order(); update_cdb_order().then(() => {
reset_data();
update_order_selection_screen(); update_order_selection_screen();
})
reset_data();
switch_screen('orders_created'); switch_screen('orders_created');
closeModal(); closeModal();
}, },
...@@ -992,7 +1061,7 @@ function prepare_datatable_data(product_ids = []) { ...@@ -992,7 +1061,7 @@ function prepare_datatable_data(product_ids = []) {
daily_conso: product.daily_conso, daily_conso: product.daily_conso,
purchase_ok: product.purchase_ok, purchase_ok: product.purchase_ok,
uom: product.uom_id[1], uom: product.uom_id[1],
stats: "Ecart type: " + product.sigma + ", % jours sans vente = " + (product.vpc) * 100 stats: `Ecart type: ${product.sigma} / Jours sans vente: ${Math.round((product.vpc) * 100)}%`
}; };
const computed_data = _compute_product_data(product); const computed_data = _compute_product_data(product);
...@@ -1026,7 +1095,7 @@ function prepare_datatable_columns() { ...@@ -1026,7 +1095,7 @@ function prepare_datatable_columns() {
}, },
{ {
data: "default_code", data: "default_code",
title: "Référence Produit", title: "Ref",
width: "8%", width: "8%",
render: function (data) { render: function (data) {
return (data === false) ? "" : data; return (data === false) ? "" : data;
...@@ -1115,7 +1184,7 @@ function prepare_datatable_columns() { ...@@ -1115,7 +1184,7 @@ function prepare_datatable_columns() {
columns.push({ columns.push({
data: "days_not_covered", data: "days_not_covered",
title: "Besoin non couvert", title: "Besoin non couvert (jours)",
className: "dt-body-center", className: "dt-body-center",
width: "4%" width: "4%"
}); });
...@@ -1218,6 +1287,7 @@ function display_products(params) { ...@@ -1218,6 +1287,7 @@ function display_products(params) {
.draw(); .draw();
update_cdb_order(); update_cdb_order();
display_total_values();
} else { } else {
$(this).val(''); $(this).val('');
} }
...@@ -1363,6 +1433,25 @@ function unselect_all_rows() { ...@@ -1363,6 +1433,25 @@ function unselect_all_rows() {
} }
/** /**
* Display the total values for each supplier & the global total value
*/
function display_total_values() {
_compute_total_values_by_supplier();
$('#suppliers_total_values_container').empty();
let total_values_content = '<ul>';
let order_total_value = 0;
for (let supplier of selected_suppliers) {
total_values_content += `<li>${supplier.display_name} : ${supplier.total_value}€</li>`;
order_total_value += supplier.total_value;
}
total_values_content += '</ul>';
$('#suppliers_total_values_container').append(total_values_content);
$('#order_total_value').text(`${order_total_value}€`);
}
/**
* Update DOM display on main screen * Update DOM display on main screen
*/ */
function update_main_screen(params) { function update_main_screen(params) {
...@@ -1376,6 +1465,7 @@ function update_main_screen(params) { ...@@ -1376,6 +1465,7 @@ function update_main_screen(params) {
$(".order_name_container").text(order_doc._id); $(".order_name_container").text(order_doc._id);
display_suppliers(); display_suppliers();
display_products(params); display_products(params);
display_total_values();
// Re-select previously selected rows // Re-select previously selected rows
if (selected_rows.length > 0) { if (selected_rows.length > 0) {
...@@ -1391,17 +1481,7 @@ function update_main_screen(params) { ...@@ -1391,17 +1481,7 @@ function update_main_screen(params) {
return 0; return 0;
}); });
} }
$("#select_all_products_cb").prop("checked", false);
if (order_doc.date_planned !== null) {
// Switch format from yy-mm-dd hh:mm:ss to readable dd/mm/yy
let date_to_format = order_doc.date_planned.split(' ')[0];
let readable_date = date_to_format.split('-').reverse()
.join('/');
$("#date_planned_input").val(readable_date);
} else {
$("#date_planned_input").val('');
}
if (order_doc.coverage_days !== null) { if (order_doc.coverage_days !== null) {
$("#coverage_days_input").val(order_doc.coverage_days); $("#coverage_days_input").val(order_doc.coverage_days);
...@@ -1429,7 +1509,6 @@ function update_order_selection_screen() { ...@@ -1429,7 +1509,6 @@ function update_order_selection_screen() {
} else { } else {
for (let row of result.rows) { for (let row of result.rows) {
let template = $("#templates #order_pill_template"); let template = $("#templates #order_pill_template");
template.find(".pill_order_name").text(row.id); template.find(".pill_order_name").text(row.id);
existing_orders_container.append(template.html()); existing_orders_container.append(template.html());
...@@ -1489,13 +1568,10 @@ function switch_screen(direction = 'main_screen', from = 'main_screen') { ...@@ -1489,13 +1568,10 @@ function switch_screen(direction = 'main_screen', from = 'main_screen') {
} }
$(document).ready(function() { /**
fingerprint = new Fingerprint({canvas: true}).get(); * Init the PouchDB local database & sync
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } }); */
function init_pouchdb_sync() {
openModal();
// Init CouchDB
dbc = new PouchDB(couchdb_dbname); dbc = new PouchDB(couchdb_dbname);
sync = PouchDB.sync(couchdb_dbname, couchdb_server, { sync = PouchDB.sync(couchdb_dbname, couchdb_server, {
live: true, live: true,
...@@ -1515,13 +1591,12 @@ $(document).ready(function() { ...@@ -1515,13 +1591,12 @@ $(document).ready(function() {
className: "error" className: "error"
} }
); );
update_order_selection_screen();
back(); back();
break; break;
} else if (doc._deleted === true) {
update_order_selection_screen();
} }
} }
update_order_selection_screen();
} }
}).on('error', function (err) { }).on('error', function (err) {
if (err.status === 409) { if (err.status === 409) {
...@@ -1531,6 +1606,16 @@ $(document).ready(function() { ...@@ -1531,6 +1606,16 @@ $(document).ready(function() {
console.log('erreur sync'); console.log('erreur sync');
console.log(err); console.log(err);
}); });
}
$(document).ready(function() {
fingerprint = new Fingerprint({canvas: true}).get();
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
openModal();
init_pouchdb_sync();
// Main screen // Main screen
$("#coverage_form").on("submit", function(e) { $("#coverage_form").on("submit", function(e) {
...@@ -1569,13 +1654,18 @@ $(document).ready(function() { ...@@ -1569,13 +1654,18 @@ $(document).ready(function() {
}); });
$('#create_orders').on('click', function() { $('#create_orders').on('click', function() {
if (order_doc.date_planned === null) { let modal_create_order = $('#templates #modal_create_order');
alert("Veuillez rentrer une date de livraison prévue."); modal_create_order.find('.suppliers_date_planned_area').empty();
return -1; for (let supplier of selected_suppliers) {
let supplier_date_planned_template = $('#templates #modal_create_order__supplier_date_planned');
supplier_date_planned_template.find(".supplier_name").text(supplier.display_name);
supplier_date_planned_template.find(".modal_input_container").attr('id', `container_date_planned_supplier_${supplier.id}`);
modal_create_order.find('.suppliers_date_planned_area').append(supplier_date_planned_template.html());
} }
let modal_create_order = $('#templates #modal_create_order');
openModal( openModal(
modal_create_order.html(), modal_create_order.html(),
...@@ -1586,6 +1676,26 @@ $(document).ready(function() { ...@@ -1586,6 +1676,26 @@ $(document).ready(function() {
false false
); );
// Add id to input once modal is displayed
for (let supplier of selected_suppliers) {
$(`#modal #container_date_planned_supplier_${supplier.id}`).find(".supplier_date_planned").attr('id', `date_planned_supplier_${supplier.id}`);
}
$("#modal .supplier_date_planned")
.datepicker({
defaultDate: "+1d",
minDate: new Date()
})
.on('change', function() {
try {
// When date input changes, try to read date
$.datepicker.parseDate(date_format, $(this).val());
} catch {
alert('Date invalide');
$(this).val('');
}
});
return 0; return 0;
}); });
...@@ -1617,40 +1727,6 @@ $(document).ready(function() { ...@@ -1617,40 +1727,6 @@ $(document).ready(function() {
}; };
$.datepicker.setDefaults($.datepicker.regional['fr']); $.datepicker.setDefaults($.datepicker.regional['fr']);
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
$("#date_planned_input")
.datepicker({
defaultDate: "+1d",
minDate: tomorrow
})
.on('change', function() {
try {
// When date input changes, try to read date
$.datepicker.parseDate(date_format, $(this).val());
// No exception raised: date is valid.
// Change format from readable (dd/mm/yy) to ISO (yy-mm-dd)
let formatted_date = $(this).val()
.split('/')
.reverse()
.join('-') + ' 00:00:00';
// Update doc if changed
if (formatted_date !== order_doc.date_planned) {
order_doc.date_planned = formatted_date;
update_cdb_order();
}
} catch (error) {
alert('Date invalide');
$(this).val('');
order_doc.date_planned = null;
update_cdb_order();
}
});
// Order selection screen // Order selection screen
update_order_selection_screen(); update_order_selection_screen();
......
...@@ -41,8 +41,8 @@ def get_suppliers(request): ...@@ -41,8 +41,8 @@ def get_suppliers(request):
def get_supplier_products(request): def get_supplier_products(request):
""" Get supplier products """ """ Get supplier products """
sid = request.GET.get('sid', '') suppliers_id = request.GET.getlist('sids', '')
res = CagetteProducts.get_products_for_order_helper(sid) res = CagetteProducts.get_products_for_order_helper(suppliers_id)
if 'error' in res: if 'error' in res:
return JsonResponse(res, status=500) return JsonResponse(res, status=500)
...@@ -69,8 +69,13 @@ def create_orders(request): ...@@ -69,8 +69,13 @@ def create_orders(request):
# suppliers id are keys in request data # suppliers id are keys in request data
for supplier_id in data["suppliers_data"].keys(): for supplier_id in data["suppliers_data"].keys():
res_created = Order.create(supplier_id, data["date_planned"], data["suppliers_data"][supplier_id]) supplier_data = data["suppliers_data"][supplier_id]
res_created["supplier_id"] = supplier_id
res_created = Order.create(
supplier_id,
supplier_data["date_planned"],
supplier_data["lines"]
)
res["created"].append(res_created) res["created"].append(res_created)
except Exception as e: except Exception as e:
......
...@@ -455,11 +455,11 @@ class CagetteProducts(models.Model): ...@@ -455,11 +455,11 @@ class CagetteProducts(models.Model):
return res return res
@staticmethod @staticmethod
def get_products_for_order_helper(supplier_id, pids = []): def get_products_for_order_helper(supplier_ids, pids = []):
""" """
One of the two parameters must be set. One of the two parameters must be not empty.
Get products by supplier if a supplier_id is set. Get products by supplier if one or more supplier_id is set.
If supplier_id is None, get products specified in pids. If supplier_ids is empty, get products specified in pids. In this case, suppliers info won't be fetched.
""" """
api = OdooAPI() api = OdooAPI()
res = {} res = {}
...@@ -468,10 +468,10 @@ class CagetteProducts(models.Model): ...@@ -468,10 +468,10 @@ class CagetteProducts(models.Model):
try: try:
today = datetime.date.today().strftime("%Y-%m-%d") today = datetime.date.today().strftime("%Y-%m-%d")
if supplier_id is not None: if len(supplier_ids) > 0:
# Get products/supplier relation # Get products/supplier relation
f = ["product_tmpl_id", 'date_start', 'date_end', 'package_qty', 'price'] f = ["product_tmpl_id", 'date_start', 'date_end', 'package_qty', 'price', 'name']
c = [['name', '=', int(supplier_id)]] c = [['name', 'in', [ int(x) for x in supplier_ids]]]
psi = api.search_read('product.supplierinfo', c, f) psi = api.search_read('product.supplierinfo', c, f)
# Filter valid data # Filter valid data
...@@ -503,8 +503,8 @@ class CagetteProducts(models.Model): ...@@ -503,8 +503,8 @@ class CagetteProducts(models.Model):
sales_average_params = { sales_average_params = {
'ids': ptids, 'ids': ptids,
# 'from': '2019-06-10', 'from': '2019-04-10',
# 'to': '2019-08-10', 'to': '2019-08-10',
} }
sales = CagetteProducts.get_template_products_sales_average(sales_average_params) sales = CagetteProducts.get_template_products_sales_average(sales_average_params)
...@@ -515,10 +515,10 @@ class CagetteProducts(models.Model): ...@@ -515,10 +515,10 @@ class CagetteProducts(models.Model):
# Add supplier data to product data # Add supplier data to product data
for i, fp in enumerate(filtered_products_t): for i, fp in enumerate(filtered_products_t):
if supplier_id is not None: if len(supplier_ids) > 0:
psi_item = next(item for item in psi if item["product_tmpl_id"] is not False and item["product_tmpl_id"][0] == fp["id"]) psi_item = next(item for item in psi if item["product_tmpl_id"] is not False and item["product_tmpl_id"][0] == fp["id"])
filtered_products_t[i]['suppliersinfo'] = [{ filtered_products_t[i]['suppliersinfo'] = [{
'supplier_id': int(supplier_id), 'supplier_id': int(psi_item["name"][0]),
'package_qty': psi_item["package_qty"], 'package_qty': psi_item["package_qty"],
'price': psi_item["price"] 'price': psi_item["price"]
}] }]
...@@ -531,7 +531,7 @@ class CagetteProducts(models.Model): ...@@ -531,7 +531,7 @@ class CagetteProducts(models.Model):
res["products"] = filtered_products_t res["products"] = filtered_products_t
except Exception as e: except Exception as e:
coop_logger.error('get_products_for_order_helper %s (%s)', str(e), str(supplier_id)) coop_logger.error('get_products_for_order_helper %s (%s)', str(e))
res["error"] = str(e) res["error"] = str(e)
return res return res
......
...@@ -53,7 +53,6 @@ ...@@ -53,7 +53,6 @@
<input type="text" name="supplier" id="supplier_input" placeholder="Rechercher un fournisseur par son nom"> <input type="text" name="supplier" id="supplier_input" placeholder="Rechercher un fournisseur par son nom">
<button type="submit" class='btn--primary'>Ajouter le fournisseur</button> <button type="submit" class='btn--primary'>Ajouter le fournisseur</button>
</form> </form>
<input type="text" name="date_planned" id="date_planned_input" placeholder="Date de livraison souhaitée">
</div> </div>
<div class="txtcenter" id="suppliers_container"></div> <div class="txtcenter" id="suppliers_container"></div>
...@@ -65,6 +64,15 @@ ...@@ -65,6 +64,15 @@
</div> </div>
<div id="main_content_footer" style="display:none;"> <div id="main_content_footer" style="display:none;">
<div id="footer_orders_recap">
<div id="suppliers_total_values">
<h4>Total /fournisseur : </h4><div id="suppliers_total_values_container"></div>
</div>
<div id="order_total_value_container">
<h4>Total : <span id="order_total_value"></span></h4>
</div>
</div>
<div id="footer_actions">
<div class="add_product_container"> <div class="add_product_container">
<div id="product_form_container"> <div id="product_form_container">
<form action="javascript:;" id="product_form"> <form action="javascript:;" id="product_form">
...@@ -78,6 +86,7 @@ ...@@ -78,6 +86,7 @@
</button> </button>
</div> </div>
</div> </div>
</div>
<div id="orders_created" class="page_content" style="display:none;"> <div id="orders_created" class="page_content" style="display:none;">
<div class="actions_buttons_area"> <div class="actions_buttons_area">
...@@ -88,9 +97,6 @@ ...@@ -88,9 +97,6 @@
<div class="order_created_header txtcenter"> <div class="order_created_header txtcenter">
<h2>Commandes créées !</h2> <h2>Commandes créées !</h2>
</div> </div>
<div class="txtcenter">
Livraison prévue le : <span id="recap_delivery_date">XX/XX/XX</span>
</div>
<div id="created_orders_area"></div> <div id="created_orders_area"></div>
<br/><br/><hr/><br/> <br/><br/><hr/><br/>
<div class="mail_example_container"> <div class="mail_example_container">
...@@ -128,6 +134,7 @@ ...@@ -128,6 +134,7 @@
<div class="new_order_item"> <div class="new_order_item">
<h3 class="new_order_supplier_name"></h3> <h3 class="new_order_supplier_name"></h3>
<h3 class="new_order_po"></h3> <h3 class="new_order_po"></h3>
<h4 class="new_order_date_planned"></h4>
<div class='download_order_file'> <div class='download_order_file'>
<i class="fas fa-spinner fa-spin download_order_file_loading"></i> <i class="fas fa-spinner fa-spin download_order_file_loading"></i>
<a class='btn--success download_order_file_button' style="display:none;" href="#"> <a class='btn--success download_order_file_button' style="display:none;" href="#">
...@@ -168,13 +175,17 @@ ...@@ -168,13 +175,17 @@
Vous vous apprêtez à associer le produit <span class="product_name"></span> au fournisseur <span class="supplier_name"></span>.<br/> Vous vous apprêtez à associer le produit <span class="product_name"></span> au fournisseur <span class="supplier_name"></span>.<br/>
</p> </p>
<br/> <br/>
<div class="new_product_supplier_input_area"> <div class="modal_input_area">
<span class="new_product_supplier_label">Prix du produit chez ce fournisseur: </span> <span class="modal_input_label">Prix du produit chez ce fournisseur: </span>
<input type="number" class="new_product_supplier_input new_product_supplier_price" > <div class="modal_input_container">
<input type="number" class="modal_input new_product_supplier_price" >
</div>
</div>
<div class="modal_input_area">
<span class="modal_input_label">Colisage chez ce fournisseur: </span>
<div class="modal_input_container">
<input type="number" class="modal_input new_product_supplier_package_pty">
</div> </div>
<div class="new_product_supplier_input_area">
<span class="new_product_supplier_label">Colisage chez ce fournisseur: </span>
<input type="number" class="new_product_supplier_input new_product_supplier_package_pty">
</div> </div>
<br/> <br/>
<p> <p>
...@@ -196,20 +207,35 @@ ...@@ -196,20 +207,35 @@
<h3>Attention !</h3> <h3>Attention !</h3>
<p> <p>
Vous vous apprêtez à passer le produit <span class="product_name"></span> en <span class="product_npa"></span>.<br/> Vous vous apprêtez à passer le produit <span class="product_name"></span> en <span class="product_npa"></span>.<br/>
L'information sera enregistrée dès que vous aurez cliqué sur "Valider". Dès que vous aurez cliqué sur "Valider", le produit sera retiré du tableau et l'information sera enregistrée dans Odoo.
</p> </p>
<p>Êtez-vous sûr ?</p> <p>Êtez-vous sûr ?</p>
<hr/> <hr/>
</div> </div>
<div id="modal_create_order"> <div id="modal_create_order">
<h3>Attention !</h3> <h3>Dernière étape...</h3>
<br/>
<p> <p>
Vous vous apprêtez à générer les commandes à partir des données rentrées dans le tableau. Vous vous apprêtez à générer les commandes à partir des données rentrées dans le tableau.
</p> </p>
<p>Êtez-vous sûr ?</p> <p>
Vous pouvez rentrer une date de livraison prévue pour chaque fournisseur (optionnel, la date par défaut sera celle de demain).
</p>
<br/>
<div class="suppliers_date_planned_area"></div>
<br/>
<hr/> <hr/>
</div> </div>
<div id="modal_create_order__supplier_date_planned">
<div class="modal_input_area">
<span class="modal_input_label supplier_name"></span>
<div class="modal_input_container">
<input type="text" class="modal_input supplier_date_planned" placeholder="Date de livraison prévue">
</div>
</div>
</div>
</div> </div>
</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