Commit 67d289d4 by Damien Moulard

fix merge conflict

parents 4ee73655 e5f44f2b
Pipeline #2181 passed with stage
in 1 minute 28 seconds
......@@ -355,6 +355,10 @@
- RECEPTION_SHELF_LABEL_PRINT = True
- DISPLAY_COL_AUTRES = True
Display "Autres" column (showing select with action "rupture fournisseur")
- COEFF_MAG_ID = 1
DB coeff id, needed to compute product shelf price
......
......@@ -188,6 +188,11 @@ div#container_edition {
padding-bottom: 15px;
}
.title_partner_key {
font-weight: bolder;
font-size: 2rem;
}
/* Accordion style */
/* Style the buttons that are used to open and close the accordion panel */
......@@ -247,5 +252,9 @@ hr {
#main_content {width: 100%;}
.select_product_action {
max-width: 15px;
max-width: 5px;
}
.toProcess_line_edit, .toProcess_line_valid {
min-width: 11px;
}
\ No newline at end of file
......@@ -365,9 +365,7 @@ function display_orders_table() {
table_orders.clear().destroy();
$('#orders').empty();
}
for (let j in orders) {
console.log(orders[j].id);
}
table_orders = $('#orders').DataTable({
data: orders,
columns:[
......
......@@ -282,6 +282,11 @@ function fetch_data() {
}
} else {
// Add order key in products
let order_full_data = orders[order_data.id_po];
order_data.po[i].order_key = order_full_data.key;
// Add product to list_to_process
list_to_process.push(order_data.po[i]);
......@@ -310,7 +315,7 @@ function fetch_data() {
// Init Data & listeners
function initLists() {
try {
// Un-disable validation buttons now the data's here
// Enable validation buttons now the data's here
if (reception_status == "False") {
document.getElementById("valid_qty").disabled = false;
document.getElementById("valid_all_qties").disabled = false;
......@@ -344,10 +349,18 @@ function initLists() {
}
}
// Init table for to_process content
table_to_process = $('#table_to_process').DataTable({
data: list_to_process,
columns:[
let columns_to_process = [];
let columns_processed = [];
// In case of group orders, add "Order" as first column for ordering
if (Object.keys(orders).length > 1) {
columns_to_process.push({
data:"order_key", title: "n°", className: "dt-body-center",
width: "20px"
});
}
columns_to_process = columns_to_process.concat([
{data:"product_id.0", title: "id", visible: false},
{data:"shelf_sortorder", title: "Rayon", className: "dt-body-center"},
{
......@@ -382,41 +395,26 @@ function initLists() {
},
{
title:"Editer",
defaultContent: "<a class='btn' id='toProcess_line_edit' href='#'><i class='far fa-edit'></i></a>",
defaultContent: "<a class='btn toProcess_line_edit' href='#'><i class='far fa-edit'></i></a>",
className:"dt-body-center",
orderable: false
},
{
title:"Valider",
defaultContent: "<a class='btn' id='toProcess_line_valid' href='#'><i class='far fa-check-square'></i></a>",
defaultContent: "<a class='btn toProcess_line_valid' href='#'><i class='far fa-check-square'></i></a>",
className:"dt-body-center",
orderable: false
},
{
title:"Autres",
title:"",
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 === "True"
}
],
rowId : "product_id.0",
order: [
[
0,
"asc"
]
],
scrollY: "33vh",
scrollCollapse: true,
paging: false,
dom: 'lrtip', // Remove the search input from that table
language: {url : '/static/js/datatables/french.json'}
});
// Init table for processed content
table_processed = $('#table_processed').DataTable({
data: list_processed,
columns:[
]);
columns_processed = [
{data:"row_counter", title:"row_counter", visible: false}, // Hidden counter to display last row first
{data:"shelf_sortorder", title: "Rayon", className:"dt-body-center"},
{
......@@ -487,7 +485,33 @@ function initLists() {
+ "</select>";
}
}
];
console.log(columns_to_process);
// Init table for to_process content
table_to_process = $('#table_to_process').DataTable({
data: list_to_process,
columns: columns_to_process,
rowId : "product_id.0",
order: [
[
0,
"asc"
]
],
scrollY: "33vh",
scrollCollapse: true,
paging: false,
dom: 'lrtip', // Remove the search input from that table
language: {url : '/static/js/datatables/french.json'}
});
// Init table for processed content
table_processed = $('#table_processed').DataTable({
data: list_processed,
columns: columns_processed,
rowId : "product_id.0",
order: [
[
......@@ -509,7 +533,7 @@ function initLists() {
/* Listeners */
// Direct valid from to_process
$('#table_to_process tbody').on('click', 'a#toProcess_line_valid', function () {
$('#table_to_process tbody').on('click', 'a.toProcess_line_valid', function () {
if (is_time_to('reception_direct_valid_order_line', 500)) {
try {
let row = table_to_process.row($(this).parents('tr'));
......@@ -542,7 +566,7 @@ function initLists() {
});
// Edit to_process line
$('#table_to_process tbody').on('click', 'a#toProcess_line_edit', function () {
$('#table_to_process tbody').on('click', 'a.toProcess_line_edit', function () {
try {
// Prevent editing mutiple lines at a time
if (editing_product == null) {
......@@ -1657,7 +1681,7 @@ function init_dom(partners_display_data) {
};
}
dbc.bulkDocs(Object.values(orders)).then((response) => {
dbc.bulkDocs(Object.values(orders)).then(() => {
back();
})
.catch((err) => {
......@@ -1964,12 +1988,13 @@ $(document).ready(function() {
include_docs: true
}).then(function (result) {
// for each order in the group
for (let order_id of group_ids) {
for (let i in group_ids) {
// find order
let order_id = group_ids[i];
let order = result.rows.find(el => el.id == 'order_' + order_id);
order = order.doc;
order.key = parseInt(i) + 1;
orders[order_id] = order;
// Add each order's already updated and validated products to common list
......@@ -1982,7 +2007,7 @@ $(document).ready(function() {
}
// Prepare data to display in 'partner name' area
partners_display_data.push(order['partner'] + ' du ' + order['date_order']);
partners_display_data.push(`<span class="title_partner_key">${order.key}.</span> ${order.partner} du ${order.date_order}`);
}
// Set current reception status: take first order's
......
......@@ -58,7 +58,11 @@ def add_products(request):
try:
id = int(request.POST.get('shelf_id'))
barcodes = json.loads(request.POST.get('bc'))
result = Shelf(id).add_products_by_barcodes(barcodes)
m = Shelf(id)
result = m.add_products_by_barcodes(barcodes)
# Update shelf last product added date
result["update_last_product_added_date"] = m.update_last_product_added_date()
except Exception as e:
result['error'] = str(e)
else:
......
......@@ -6,7 +6,7 @@ from products.models import CagetteProducts
from inventory.models import CagetteInventory
import os
from datetime import date
from datetime import date, datetime
from openpyxl import Workbook
from openpyxl.styles import Alignment, Font
from statistics import *
......@@ -14,6 +14,7 @@ from statistics import *
# Prefix for temp shelf inventory files
tmp_inv_file_prefix = 'temp/inventory_shelf_'
default_inventory_start_datetime = "0001-01-01 00:00:00"
def as_text(value):
""" Utils """
......@@ -106,6 +107,7 @@ class Shelf(models.Model):
# Inventory all done
f['inventory_status'] = ''
f['date_last_inventory'] = date.today().strftime("%Y-%m-%d")
f['ongoing_inv_start_datetime'] = default_inventory_start_datetime # Reset
if 'last_inventory_id' in params:
f['last_inventory_id'] = params['last_inventory_id']
......@@ -180,6 +182,18 @@ class Shelf(models.Model):
res['error'] = "L'enregistrement n'a pas pu se réaliser"
return res
def set_begin_inventory_datetime(self):
res = {}
now = datetime.now().isoformat()
f = {'ongoing_inv_start_datetime': now}
try:
res["update"] = self.o_api.update('product.shelfs', self.id, f)
res["inventory_begin_datetime"] = now
except Exception as e:
res['error'] = str(e)
return res
def save_tmp_inventory(self, inventory_data):
"""Save inventory data in a json temp file"""
......
......@@ -12,21 +12,23 @@ var validation_msg = $('#validation_msg'),
process_all_items_msg = $('#process_all_items_msg'),
faq_content = $("#FAQ_modal_content"),
issues_reporting = $("#issues_reporting"),
add_product_form = $("#add_product_form"),
add_product_input = $("#add_product_input");
add_product_form = $("#add_product_form");
var shelf,
var shelf = null,
parent_location = '/shelfs',
originView = "shelf", // or custom_list (create from order view)
list_to_process = [],
table_to_process,
table_processed,
table_to_process = null,
table_processed = null,
editing_item = null, // Store the item currently being edited
editing_origin, // Keep track of where editing_item comes from
editing_origin = "", // Keep track of where editing_item comes from
processed_row_counter = 0, // Keep count of the order the item were added in processed list
search_chars = [],
user_comments = '',
adding_product = false; // True if modal to add a product is open
adding_product = false, // True if modal to add a product is open.
// datetime for which shelf's ongoing_inv_start_datetime is considered null
default_inventory_start_datetime = "0001-01-01 00:00:00";
/* UTILS */
......@@ -48,6 +50,10 @@ function back() {
document.location.href = parent_location;
}
function refresh() {
location.reload();
}
// Directly send a line to edition when barcode is read
function select_product_from_bc(barcode) {
if (editing_item == null) {
......@@ -95,7 +101,7 @@ function handle_blinking_effect(element) {
element.addEventListener('animationend', onAnimationEnd);
element.addEventListener('webkitAnimationEnd', onAnimationEnd);
function onAnimationEnd(e) {
function onAnimationEnd() {
element.classList.remove('blink_me');
}
}
......@@ -169,7 +175,7 @@ function clearLineEdition() {
}
// Validate product edition
function validateEdition(form) {
function validateEdition() {
if (editing_item != null) {
if (editProductInfo(editing_item)) {
clearLineEdition();
......@@ -405,10 +411,12 @@ function confirmProcessAllItems() {
openModal();
// Iterate over all rows in table of items to process
table_to_process.rows().every(function (rowIdx, tableLoop, rowLoop) {
table_to_process.rows().every(function () {
var data = this.data();
editProductInfo(data, 0);
return 1;
});
// Reset data
......@@ -446,7 +454,6 @@ function send() {
var url = "../do_" + originView + "_inventory";
var call_begin_at = new Date().getTime();
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
$.ajax({
type: "PUT",
url: url,
......@@ -469,18 +476,19 @@ function send() {
}
}
var msg = (originView == 'shelf') ? 'Retour à la liste des rayons' : 'Retour';
var msg = (originView == 'shelf') ? 'OK, je passe à la suite !' : 'Retour';
openModal(inventory_validated_msg.html(), back, msg, true, false);
// Go to step 2 if step 1 is validated and modal closed
openModal(inventory_validated_msg.html(), refresh, msg, true, false);
// Go back to list if modal closed
$('#modal_closebtn_top').on('click', back);
$('#modal_closebtn_bottom').on('click', back);
// Go to step 2 if modal is closed
$('#modal_closebtn_top').on('click', refresh);
$('#modal_closebtn_bottom').on('click', refresh);
// Clear local storage before leaving
localStorage.removeItem(originView + '_' + shelf.id);
},
error: function(jqXHR, textStatus) { // 500 error has been thrown or web server sent a timeout
error: function(jqXHR) { // 500 error has been thrown or web server sent a timeout
if (jqXHR.status == 504) {
/*
django is too long to respond.
......@@ -547,6 +555,35 @@ function exit_adding_product() {
adding_product = false;
}
/**
* Set the ongoing inventory start datetime.
* This operation is invisible to the user.
*/
function set_begin_inventory_datetime() {
if (originView === 'shelf' &&
(
shelf.ongoing_inv_start_datetime === default_inventory_start_datetime
|| shelf.ongoing_inv_start_datetime === undefined
)
) {
$.ajax({
type: "POST",
url: "/shelfs/"+shelf.id+"/set_begin_inventory_datetime",
dataType: "json",
traditional: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
shelf.ongoing_inv_start_datetime = data.res.inventory_begin_datetime;
// Update local storage
localStorage.setItem(originView + "_" + shelf.id, JSON.stringify(shelf));
},
error: function() {
console.log("Impossible de mettre à jour la date de début d'inventaire");
}
});
}
}
// Add a product that's not in the list
function open_adding_product() {
if (originView == 'shelf') {
......@@ -563,7 +600,6 @@ function do_add_product() {
};
openModal();
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
$.ajax({
type: "POST",
url: "../"+shelf.id+"/add_product",
......@@ -571,7 +607,7 @@ function do_add_product() {
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(prod_data),
success: function(data) {
success: function() {
exit_adding_product();
closeModal();
alert('Produit ajouté !');
......@@ -618,8 +654,6 @@ function saveIssuesReport() {
// Get shelf data from server if not in local storage
function get_shelf_data() {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
var url = (originView == 'shelf') ? '../' + shelf.id : '../get_custom_list_data?id=' + shelf.id;
$.ajax({
......@@ -631,6 +665,7 @@ function get_shelf_data() {
success: function(data) {
shelf = data.res;
init();
set_begin_inventory_datetime();
},
error: function(data) {
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') {
......@@ -646,7 +681,6 @@ function init() {
// Products passed at page loading
// TODO: get products by ajax for better ui experience (? -> warning js at loading)
// TODO : What happens if products are being put or removed from the self before the end of the inventory ?
//console.log(shelf)
list_to_process = products;
initLists();
......@@ -703,7 +737,7 @@ function init() {
$(document).on('click', 'button#add_product_to_shelf', open_adding_product);
$(document).on('click', 'button#open_issues_report', openIssuesReport);
$(document).on('click', 'button#open_faq', openFAQ);
$(document).on('click', 'button#process_all_items', function (e) {
$(document).on('click', 'button#process_all_items', function () {
openModal(process_all_items_msg.html(), confirmProcessAllItems, 'Confirmer', false);
});
......@@ -720,7 +754,7 @@ function init() {
handle_blinking_effect(container_edition);
// Disable mousewheel on an input number field when in focus
$('#edition_input').on('focus', function (e) {
$('#edition_input').on('focus', function () {
$(this).on('wheel.disableScroll', function (e) {
e.preventDefault();
/*
......@@ -733,7 +767,7 @@ function init() {
*/
});
})
.on('blur', function (e) {
.on('blur', function () {
$(this).off('wheel.disableScroll');
});
......@@ -815,6 +849,8 @@ function init() {
$(document).ready(function() {
// Get Route parameter
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
var pathArray = window.location.pathname.split('/');
shelf = {id: pathArray[pathArray.length-1]};
......@@ -834,7 +870,9 @@ $(document).ready(function() {
if (stored_shelf != null) {
shelf = stored_shelf;
init();
set_begin_inventory_datetime();
} else {
// Get shelf info if not coming from shelves list
get_shelf_data();
......
......@@ -6,8 +6,8 @@ Informations affichées :
*/
var parent_location = '/shelfs',
shelf,
table_products,
shelf = null,
table_products = null,
search_chars = [];
/* UTILS */
......@@ -54,7 +54,7 @@ function initList() {
title:"Delta (dernier inv.)",
width: "10%",
className:"dt-body-center",
render: function (data, type, full, meta) {
render: function (data, type) {
if (type == "sort" || type == 'type')
return data;
......@@ -70,7 +70,7 @@ function initList() {
title:"Pertes (dernier inv.)",
width: "10%",
className:"dt-body-center",
render: function (data, type, full, meta) {
render: function (data, type) {
if (type == "sort" || type == 'type')
return data;
......
......@@ -137,6 +137,8 @@ var update_shelf = function() {
rData.res.p_nb = data.p_nb;
this.data(rData.res).draw();
}
return 1;
});
closeModal();
} else alert(rData.res.error);
......@@ -203,12 +205,6 @@ var downloadInventoryReport = function() {
}
};
var rowUpdate = function (row, rdata) {
//console.log(row, rdata)
};
// TODO put datatable common methods such as following in a file useable for all modules
var rowGetData = function(clicked) {
var row = shelfs_table.row(clicked.parents('tr'));
......@@ -217,7 +213,7 @@ var rowGetData = function(clicked) {
return row.data();
};
function coop_init_datatable(params, data, domsel, cols, action_btn) {
function coop_init_datatable(params, data, domsel, cols) {
var buttons = [];
var columns = [];
......@@ -278,10 +274,6 @@ function coop_init_datatable(params, data, domsel, cols, action_btn) {
rowId : "id",
data : data,
language: {url : '/static/js/datatables/french.json'},
createdRow: function(row, rdata, index) {
rowUpdate(row, rdata);
},
initComplete: function() {
/*
if (! coop_is_connected())
......@@ -318,7 +310,7 @@ var init_and_fill_selfs_list = function() {
data:"last_inventory_id",
title:"Rapport dernier inventaire",
className: "action",
render: function (data, type, full, meta) {
render: function (data) {
if (typeof data != "undefined" && data != 0) {
return download_icon;
} else {
......
......@@ -13,11 +13,46 @@ function init_datatable() {
className:"dt-body-center"
},
{data:"name", title:"Nom"},
{data:"description", title:"Description", orderable: false},
// {data:"description", title:"Description", orderable: false},
{
data:"ongoing_inv_start_datetime",
title:"Début inventaire en cours",
render: function (data, type) {
// Sort on data, not rendering
if (type == "sort" || type == 'type')
return data;
if (data == '0001-01-01 00:00:00')
return "";
else {
var date = new Date(data);
return `${date.toLocaleDateString('fr-FR')} ${date.toLocaleTimeString('fr-FR')}`;
}
}
},
{
data:"date_last_product_added",
title:"Dernier ajout produit",
render: function (data, type) {
// Sort on data, not rendering
if (type == "sort" || type == 'type')
return data;
if (data == '0001-01-01')
return "";
else {
var date = new Date(data);
return date.toLocaleDateString('fr-FR');
}
}
},
{
data:"date_last_inventory",
title:"Date dernier inventaire",
render: function (data, type, full, meta) {
title:"Dernier inventaire",
render: function (data, type) {
// Sort on data, not rendering
if (type == "sort" || type == 'type')
return data;
......@@ -36,7 +71,7 @@ function init_datatable() {
{
data:"shelf_value",
title:"Valeur théorique du rayon",
render: function (data, type, full, meta) {
render: function (data, type) {
if (type == "sort" || type == 'type')
return data;
......@@ -51,44 +86,45 @@ function init_datatable() {
width: "5%",
className:"dt-body-center"
},
{
data:"last_inv_delta_percentage",
title:"Delta (dernier inv.)",
width: "5%",
className:"dt-body-center",
render: function (data, type, full, meta) {
if (type == "sort" || type == 'type')
return data;
if (data == -99999999) {
return '/';
} else {
return data + ' %';
}
}
},
{
data:"last_inv_losses_percentage",
title:"Pertes (dernier inv.)",
width: "5%",
className:"dt-body-center",
render: function (data, type, full, meta) {
if (type == "sort" || type == 'type')
return data;
if (data == -99999999) {
return '/';
} else {
return data + ' %';
}
}
},
/* NOT IN USE */
// {
// data:"last_inv_delta_percentage",
// title:"Delta (dernier inv.)",
// width: "5%",
// className:"dt-body-center",
// render: function (data, type) {
// if (type == "sort" || type == 'type')
// return data;
// if (data == -99999999) {
// return '/';
// } else {
// return data + ' %';
// }
// }
// },
// {
// data:"last_inv_losses_percentage",
// title:"Pertes (dernier inv.)",
// width: "5%",
// className:"dt-body-center",
// render: function (data, type) {
// if (type == "sort" || type == 'type')
// return data;
// if (data == -99999999) {
// return '/';
// } else {
// return data + ' %';
// }
// }
// },
{
data:"inventory_status",
title:"Inventaire à faire",
className:"dt-body-center",
width: "15%",
render: function (data, type, full, meta) {
render: function (data) {
if (data == '')
return "<button class='btn--primary do_shelf_inventory'>Inventaire en rayon</button>";
else
......@@ -144,11 +180,13 @@ function get_shelfs_extra_data() {
}
function set_null_to_extra_data() {
shelfs_table.rows().every(function (rowIdx, tableLoop, rowLoop) {
shelfs_table.rows().every(function () {
var d = this.data();
d.shelf_value = -2;
this.invalidate(); // invalidate the data DataTables has cached for this row
return 1;
});
shelfs_table.draw();
......@@ -157,7 +195,6 @@ function set_null_to_extra_data() {
var getRowData = function(clicked) {
var row = shelfs_table.row(clicked.parents('tr'));
return row.data();
};
......
......@@ -17,7 +17,7 @@ function init_datatable() {
{
data:"date_last_inventory",
title:"Date dernier inventaire",
render: function (data, type, full, meta) {
render: function (data, type) {
// Sort on data, not rendering
if (type == "sort" || type == 'type')
return data;
......@@ -37,7 +37,7 @@ function init_datatable() {
title:"",
className:"dt-body-center",
width: "15%",
render: function (data, type, full, meta) {
render: function () {
return "<button class='btn--success do_export_sales_data'>Export Ventes</button>";
}
}
......
......@@ -14,6 +14,7 @@ urlpatterns = [
url(r'^(?P<shelf_id>\d+)$', views.shelf_data),
url(r'^(?P<shelf_id>\d+)/products$', views.products),
url(r'^(?P<shelf_id>\d+)/add_product$', views.add_product),
url(r'^(?P<shelf_id>\d+)/set_begin_inventory_datetime$', views.set_begin_inventory_datetime),
url(r'^do_shelf_inventory$', views.do_shelf_inventory),
url(r'^(?P<shelf_id>\d+)/last_inventory_report$', views.get_last_inventory_report),
url(r'^shelf_inventory_FAQ', views.shelf_inventory_FAQ),
......
......@@ -61,6 +61,15 @@ def shelf_data(request, shelf_id):
else:
return JsonResponse({'res': shelf})
def set_begin_inventory_datetime(request, shelf_id):
""" Set the ongoing inventory start datetime. Set it to now. """
res = Shelf(shelf_id).set_begin_inventory_datetime()
if 'error' in res:
return JsonResponse(res, status=500)
else:
return JsonResponse({'res': res})
def all(request):
"""Get all shelves data"""
......
......@@ -112,14 +112,14 @@
</div>
<div id="validation_msg">
<h3>Attention !</h3>
<p>Vous vous apprêtez valider le comptage des produits<span class="validation_msg_step2" style="display:none;"> en réserve</span> de ce rayon.</p>
<p>Vous vous apprêtez à valider le comptage des produits<span class="validation_msg_step2" style="display:none;"> en réserve</span> de ce rayon.</p>
<p class="validation_msg_step2" style="display:none;"><i>Cette opération pourra pendre un peu de temps. (ex: 5min pour 120 produits)</i><br/><br/></p>
<p>Êtez-vous sûr ?</p>
<hr />
</div>
<div id="inventory_validated">
<div class="txtcenter">
<h3>Bravo, l'inventaire de ce rayon est terminé !</h3>
<h3>Bravo, la première partie de l'inventaire de ce rayon est terminé !</h3>
<div id="products_missed_container" style="display:none;">
<br />
<h4>Attention, les produits suivants n'ont pas pu être inventoriés : </h4>
......@@ -130,7 +130,8 @@
</div>
<div id="step1_validated" style="display:none;">
<br/>
<p>Vous pouvez continuer l'inventaire en allant comptabiliser les produits en réserve, si ce n'est pas déjà fait.</p>
<p><strong>Vous allez maintenent passer à l'inventaire en réserve.</strong></p>
<p>Si vous avez un doute sur ce qu'est l'inventaire en réserve, la présence de stock ou non, demandez au.à la salarié.e responsable.</p>
</div>
</div>
<hr />
......
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