Commit f2dff3c8 by François C.

Merge branch '7747-inventaire-par-article' into 'migration-v12'

several changes in inventaire par article app

See merge request !284
parents 6963f5a9 2123aa7b
Pipeline #4185 canceled with stage
......@@ -1918,9 +1918,19 @@ function display_products(params) {
cell.addClass('product_ref_cell');
}
}
}
},
dom: 'lrtip', // Remove the search input from that table
});
/* In order to remove the accents in the input, we could attach a keyup event to the default
* search field generated by datatables, but then two search events would be triggered on keyup :
* the default datatable event, and ours.
* To fix this problem we create our own input search so that only our event fires.
*/
$(document).on('keyup', '#custom_products_table_filter input[type="search"]', function() {
products_table
.search(jQuery.fn.DataTable.ext.type.search.string(this.value))
.draw();
});
products_table.search('');
$('.main').show();
$('#main_content_footer').show();
$('#do_inventory').show();
......
......@@ -397,6 +397,13 @@ class CagetteProducts(models.Model):
return api.search_read('product.product', cond, fields)
@staticmethod
def get_all_active_products():
api = OdooAPI()
cond = [['active', '=', True]]
fields = ['id', 'uom_id', 'name', 'qty_available', 'barcode']
return api.search_read('product.product', cond, fields, limit=False)
@staticmethod
def get_vrac_products_from_cats(cats, withCandidate=False, fields=[]):
api = OdooAPI()
cond = [['active', '=', True],
......
.main, .barcode_search_area {
margin-top: 20px;
}
#barcode_selector {
border: 1px solid #555;
border-radius:15px;
}
.stock_edit_input {
max-width: 50px;
max-width: 100px;
}
.dataTables_filter {
display: block !important; /* S'assure qu'elle est bien affichée */
}
\ No newline at end of file
/*
* Récupération d'un produit par scan et affichage des informations du produits.
* Les informations actuellements récupérées sont :
* - Numéro de Rayon
* - Stock théorique
*
* Les informations actuellement modifiables sont :
......@@ -9,143 +8,127 @@
*/
var products = [],
products_table = null,
search_chars = [];
function reset_focus() {
$('#barcode_selector').val('');
$('#barcode_selector').focus();
}
function add_product(product) {
try {
// Add to list
products.push(product);
if (products_table == null) {
// create table, show panel
products_table = $('#products_table').DataTable({
data: products,
columns:[
{data:"id", title: "id", visible: false},
{
data:"name",
title:"Produit",
width: "50%",
render: function (data, type, full, meta) {
// Add tooltip with barcode over product name
let display_barcode = "Aucun";
products_table = null;
function init(products) {
// create table, show panel
products_table = $('#products_table').DataTable({
data: products,
columns: [
{data: "id", title: "id", visible: false},
{
data: "name",
title: "Produit",
width: "50%",
render: function (data, type, full, meta) {
// Add tooltip with barcode over product name
let display_barcode = "Aucun";
if ('barcode' in full) {
display_barcode = full.barcode;
}
if ('barcode' in full) {
display_barcode = full.barcode;
}
return '<div class="tooltip">' + data
+ ' <span class="tooltiptext tt_twolines">Code barre : '
+ display_barcode + '</span> </div>';
}
},
{
data: "uom_id.1",
title: "Unité de vente",
className: "dt-body-center",
orderable: false
},
{
data: "qty_available",
title: "Stock théorique",
className: "dt-body-center",
render: function (data, type, full, meta) {
return '<input type="number" class="stock_edit_input" value="' + data + '">'
+ ' <button type="button" class="stock_edit_button btn--primary"><i class="fas fa-lg fa-check"></i></button>';
}
}
],
order: [
[
0,
"asc"
]
],
pageLength: 10,
deferRender: true,
processing: true,
searching: true,
language: {url: '/static/js/datatables/french.json'},
dom: 'lrtip', // Remove the search input from that table
});
/* In order to remove the accents in the input, we could attach a keyup event to the default
* search field generated by datatables, but then two search events would be triggered on keyup :
* the default datatable event, and ours.
* To fix this problem we create our own input search so that only our event fires.
*/
$(document).on('keyup', '#custom_products_table_filter input[type="search"]', function() {
products_table
.search(jQuery.fn.DataTable.ext.type.search.string(this.value))
.draw();
});
return '<div class="tooltip">' + data
+ ' <span class="tooltiptext tt_twolines">Code barre : '
+ display_barcode + '</span> </div>';
}
},
{data:"shelf_sortorder", title: "Rayon"},
{
data:"uom_id.1",
title: "Unité de vente",
className:"dt-body-center",
orderable: false
},
// Listener on 'Update product stock' button
$('#products_table tbody').on('click', 'button.stock_edit_button', function () {
var row = products_table.row($(this).parents('tr'));
var row_data = row.data();
// Data validation
qty = $(this).parents('tr')
.find('input')
.val();
if (row_data.uom_id[0] == 1) {
if (qty != 0 && qty / parseInt(qty) != 1) {
$.notify(
"Ce produit est vendu à l'unité : la valeur doit être un nombre entier !",
{
data:"qty_available",
title: "Stock théorique",
className:"dt-body-center",
render: function (data, type, full, meta) {
return '<input type="number" class="stock_edit_input" value="' + data + '">'
+ ' <button type="button" class="stock_edit_button btn--primary"><i class="fas fa-lg fa-check"></i></button>';
}
globalPosition: "top left",
className: "error"
}
],
order: [
[
0,
"asc"
]
],
paging: false,
dom: 'lrtip', // Remove the search input from that table
language: {url : '/static/js/datatables/french.json'}
});
// Listener on 'Update product stock' button
$('#products_table tbody').on('click', 'button.stock_edit_button', function () {
var row = products_table.row($(this).parents('tr'));
var row_data = row.data();
// Data validation
qty = $(this).parents('tr')
.find('input')
.val();
if (row_data.uom_id[0] == 1) {
if (qty/parseInt(qty) != 1) {
$.notify(
"Ce produit est vendu à l'unité : la valeur doit être un nombre entier !",
{
globalPosition:"top left",
className: "error"
}
);
);
return 0;
}
return 0;
}
qty = parseInt(qty); // product by unit
} else {
qty = parseFloat(qty);
}
qty = parseInt(qty); // product by unit
} else {
qty = parseFloat(qty);
}
// Value is valid
if (!isNaN(qty)) {
row_data.qty = $(this).parents('tr')
.find('input')
.val();
// Value is valid
if (!isNaN(qty)) {
row_data.qty = $(this).parents('tr')
.find('input')
.val();
if (row_data.qty != row_data.qty_available) {
update_product_stock(row_data);
} else {
$.notify(
"Valeur inchangée.",
{
globalPosition:"top left",
className: "info"
}
);
if (row_data.qty != row_data.qty_available) {
update_product_stock(row_data);
} else {
$.notify(
"Valeur inchangée.",
{
globalPosition: "top left",
className: "info"
}
} else {
$.notify(
"Valeur invalide.",
{
globalPosition:"top left",
className: "error"
}
);
}
});
);
$("#products_table_filter input").focus()
}
} else {
// Add row to table
var rowNode = products_table.row.add(product).draw(false)
.node();
let onAnimationEnd = function() {
rowNode.classList.remove('blink_me');
};
// Handle blinking effect for newly added row
$(rowNode).addClass('blink_me');
rowNode.addEventListener('animationend', onAnimationEnd);
rowNode.addEventListener('webkitAnimationEnd', onAnimationEnd);
$.notify(
"Valeur invalide.",
{
globalPosition: "top left",
className: "error"
}
);
$("#products_table_filter input").focus()
}
} catch (e) {
err = {msg: e.name + ' : ' + e.message, ctx: 'add_product'};
console.error(err);
report_JS_error(err, 'produits');
}
});
}
function update_product_stock(p_data) {
......@@ -162,6 +145,7 @@ function update_product_stock(p_data) {
delete p_data.qty;
closeModal();
$("#products_table_filter input").focus()
$.notify(
"Stock mis à jour !",
{
......@@ -169,8 +153,6 @@ function update_product_stock(p_data) {
className: "success"
}
);
reset_focus();
},
error: function(data) {
// server error
......@@ -189,47 +171,28 @@ function update_product_stock(p_data) {
}
// Fetch a product when barcode is read
function fetch_product_from_bc(barcode) {
if (barcode == '') {
reset_focus();
return 0;
}
for (p of products) {
if (p.barcode == barcode) {
$.notify(
"Produit déjà récupéré !",
{
globalPosition:"top left",
className: "info"
}
);
reset_focus();
return 0;
}
}
function fetch_products() {
openModal();
$.ajax({
url: "/products/get_product_data?barcode=" + barcode,
url: "/products/get_all_active_products",
success: function(data) {
reset_focus();
if (data.product.active == false) {
alert('Ce produit est archivé !');
return 0;
}
add_product(data.product);
init(data.products)
closeModal();
// Attendre que le DOM soit complètement prêt avant de donner le focus
setTimeout(function() {
// Vérifier si l'input de la recherche est disponible
var searchInput = $("#products_table_filter input");
if (searchInput.length) {
searchInput.focus(); // Mettre le focus sur le champ de recherche
}
}, 100); // Attendre un court instant pour s'assurer que DataTables est complètement initialisé
},
error: function(data) {
closeModal();
if (data.status == 404) {
// Product not found (shouldn't rise)
$.notify(
"Aucun produit trouvé avec ce code barre !",
"Aucun produit trouvé !",
{
globalPosition:"top left",
className: "error"
......@@ -238,7 +201,7 @@ function fetch_product_from_bc(barcode) {
reset_focus();
} else {
// server error
err = {msg: "erreur serveur lors de la récupération du produit", ctx: 'fetch_product_from_bc'};
err = {msg: "erreur serveur lors de la récupération du produit", ctx: 'fetch_products'};
if (typeof data.responseJSON != 'undefined' && typeof data.responseJSON.error != 'undefined') {
err.msg += ' : ' + data.responseJSON.error;
}
......@@ -252,28 +215,5 @@ function fetch_product_from_bc(barcode) {
$(document).ready(function() {
$.ajaxSetup({ headers: { "X-CSRFToken": getCookie('csrftoken') } });
reset_focus();
$('#button_barcode_selector').on('click', function () {
bc = $('#barcode_selector').val();
fetch_product_from_bc(bc);
});
// Barcode reader: listen for 13 digits read in a very short time
$('#search_input').keypress(function(e) {
if (e.which >= 48 && e.which <= 57) {
search_chars.push(String.fromCharCode(e.which));
}
if (search_chars.length >= 13) {
var barcode = search_chars.join("");
if (!isNaN(barcode)) {
search_chars = [];
setTimeout(function() {
fetch_product_from_bc(barcode);
}, 300);
}
}
});
fetch_products('');
});
......@@ -7,6 +7,7 @@ urlpatterns = [
url(r'^simple_list$', views.get_simple_list),
url(r'^get_product_for_order_helper$', views.get_product_for_order_helper),
url(r'^get_product_data$', views.get_product_data),
url(r'^get_all_active_products$', views.get_all_active_products),
url(r'^get_products_stdprices$', views.get_products_stdprices),
url(r'^update_product_stock$', views.update_product_stock),
url(r'^update_product_purchase_ok$', views.update_product_purchase_ok),
......
......@@ -55,6 +55,14 @@ def get_product_for_order_helper(request):
else:
return JsonResponse(res, safe=False)
def get_all_active_products(request):
products = CagetteProducts.get_all_active_products()
if not products:
return JsonResponse({"product": products}, status=404)
else:
return JsonResponse({"products": products})
def get_product_data(request):
barcode = request.GET['barcode']
res = CagetteProduct.get_product_from_barcode(barcode)
......@@ -63,6 +71,12 @@ def get_product_data(request):
return JsonResponse({"product": res}, status=404)
p = res[0]
add_shelf_sortorder(p)
return JsonResponse({"product": p})
def add_shelf_sortorder(p):
if p['shelf_id'] is not False:
shelfs_sortorder = Shelfs.get_shelfs_sortorder([p['shelf_id'][0]])
......@@ -73,7 +87,6 @@ def get_product_data(request):
else:
p['shelf_sortorder'] = 'X'
return JsonResponse({"product": p})
def get_products_stdprices(request):
ids = json.loads(request.body.decode())
......
<div id="custom_products_table_wrapper" class="dataTables_wrapper no-footer">
<div id="custom_products_table_filter" class="dataTables_filter">
<label for="custom_product_search_input">Recherche :
<input type="search" id="custom_product_search_input" class="form-control" placeholder="" aria-controls="products_table"/>
</label>
</div>
</div>
\ No newline at end of file
......@@ -125,6 +125,7 @@
<div class="main" style="display:none;">
<div class="table_area">
{% include "common/custom_search_input_replacing_datatable_default.html" %}
<table id="products_table" class="display" cellspacing="0" width="100%"></table>
</div>
</div>
......
......@@ -15,17 +15,12 @@
{% block content %}
<div class="header txtcenter">
<h1>Informations sur les produits du magasin</h1>
</div>
<div class="barcode_search_area txtcenter">
<p><i>Passez un produit sous la douchette ! Si rien ne se passe, cliquez sur le champ ci-dessous puis ré-essayez.</i></p>
<input type="text" id="barcode_selector" name="barcode_selector" placeholder="Codebarre">
<button type="button" class="btn--primary" id="button_barcode_selector" name="button">Recherche</button>
<h1>Inventaire par produits</h1>
</div>
<div class="main">
<table id="products_table" class="display" cellspacing="0" ></table>
{% include "common/custom_search_input_replacing_datatable_default.html" %}
<table id="products_table" class="display" cellspacing="0" ></table>
</div>
<script src="{% static "js/all_common.js" %}?v=1651853225"></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