Commit 7b60a5d4 by Damien Moulard

select products & generate inventory

parent a82672f8
Pipeline #987 failed with stage
in 18 seconds
...@@ -52,7 +52,7 @@ class CagetteInventory(models.Model): ...@@ -52,7 +52,7 @@ class CagetteInventory(models.Model):
file_data = json.load(json_file) file_data = json.load(json_file)
date_time = datetime.fromtimestamp(int(filename)) date_time = datetime.fromtimestamp(int(filename))
d = date_time.strftime("%m/%d/%Y, %H:%M") d = date_time.strftime("%d/%m/%Y, %H:%M")
file_data['id'] = int(filename) file_data['id'] = int(filename)
file_data['datetime_created'] = d file_data['datetime_created'] = d
...@@ -113,7 +113,7 @@ class CagetteInventory(models.Model): ...@@ -113,7 +113,7 @@ class CagetteInventory(models.Model):
return file_data['inventory_status'] return file_data['inventory_status']
@staticmethod @staticmethod
def create_custom_inv_file(line_ids, line_type): def create_custom_inv_file(line_ids, line_type, default_partners_id=[]):
res = {} res = {}
try: try:
...@@ -127,36 +127,54 @@ class CagetteInventory(models.Model): ...@@ -127,36 +127,54 @@ class CagetteInventory(models.Model):
api = OdooAPI() api = OdooAPI()
ids = [] ids = []
order = ['', ''] order = ['', '']
user = partner = '' user = ''
fields = ['create_uid', 'product_id', 'partner_id'] partners = []
cond = [['id', 'in', line_ids]] if len(default_partners_id) > 0:
if (line_type == 'cpo'): f = ['name']
model = 'computed.purchase.order.line' c = [['id', 'in', default_partners_id]]
fields += ['computed_purchase_order_id'] partners_name = api.search_read('res.partner', c, f)
for p in partners_name:
partners.append(p['name'])
if line_type == 'product_templates':
fields = ['id']
cond = [['product_tmpl_id', 'in', line_ids]]
model = 'product.product'
user="api"
else: else:
model = 'purchase.order.line' fields = ['create_uid', 'product_id', 'partner_id']
fields += ['order_id'] cond = [['id', 'in', line_ids]]
if (line_type == 'cpo'):
model = 'computed.purchase.order.line'
fields += ['computed_purchase_order_id']
else:
model = 'purchase.order.line'
fields += ['order_id']
lines = api.search_read(model, cond, fields) lines = api.search_read(model, cond, fields)
if len(lines) == len(line_ids): if len(lines) == len(line_ids):
for l in lines: for l in lines:
ids.append(l['product_id'][0]) if line_type == 'product_templates':
user = l['create_uid'][1] ids.append(l['id'])
if (line_type == 'cpo'):
order = l['computed_purchase_order_id']
else: else:
order = l['order_id'] ids.append(l['product_id'][0])
partner = l['partner_id'][1] user = l['create_uid'][1]
if (line_type == 'cpo'):
order = l['computed_purchase_order_id']
else:
order = l['order_id']
partners.append(l['partner_id'][1])
if (line_type == 'cpo'): if (line_type == 'cpo'):
# partner_id isn't defined # partner_id isn't defined
f = ['partner_id'] f = ['partner_id']
c = [['id', '=', int(order[0])]] c = [['id', '=', int(order[0])]]
cpo = api.search_read('computed.purchase.order', c, f) cpo = api.search_read('computed.purchase.order', c, f)
if len(cpo) > 0: if len(cpo) > 0:
partner = cpo[0]['partner_id'][1] partners.append(cpo[0]['partner_id'][1])
file_data = { file_data = {
'order': order[1], 'order': order[1],
'user': user, 'user': user,
'partner': partner, 'partners': partners,
'inventory_status': '', 'inventory_status': '',
'products': ids 'products': ids
} }
......
...@@ -4,6 +4,13 @@ var shelfs_table = null, ...@@ -4,6 +4,13 @@ var shelfs_table = null,
function init_datatable() { function init_datatable() {
// For a smooth migration...
for (const i in lists) {
if (('partners' in lists[i]) === false) {
lists[i]['partners'] = [lists[i]['partner']]
}
}
return $('#lists').DataTable({ return $('#lists').DataTable({
data: lists, // data passed at page loading data: lists, // data passed at page loading
rowId: 'id', rowId: 'id',
...@@ -17,8 +24,15 @@ function init_datatable() { ...@@ -17,8 +24,15 @@ function init_datatable() {
} }
}, },
{ {
data:"partner", data:"partners",
title:"Fournisseur" title:"Fournisseur(s)",
render: function (data) {
res = "";
for (const i in data) {
res += `${data[i]}<br/>`;
}
return res;
}
}, },
{ {
data:"order", data:"order",
......
...@@ -43,7 +43,6 @@ def custom_list_inventory(request, id): ...@@ -43,7 +43,6 @@ def custom_list_inventory(request, id):
products = CagetteInventory.get_custom_list_products(id) products = CagetteInventory.get_custom_list_products(id)
if 'error' in products: if 'error' in products:
print(products)
products['data'] = [] products['data'] = []
context = {'title': 'Inventaire', context = {'title': 'Inventaire',
...@@ -112,10 +111,25 @@ def do_custom_list_inventory(request): ...@@ -112,10 +111,25 @@ def do_custom_list_inventory(request):
def generate_inventory_list(request): def generate_inventory_list(request):
"""Responding to Odoo ajax call (no csrf).""" """Responding to Odoo ajax call (no csrf)."""
res = {} res = {}
default_partners_id = []
try: try:
lines = json.loads(request.POST.get('lines')) lines = json.loads(request.POST.get('lines'))
ltype = request.POST.get('type') ltype = request.POST.get('type')
res = CagetteInventory.create_custom_inv_file(lines, ltype) except Exception as e:
try:
# POST.get() returns None when request from django
data = json.loads(request.body.decode())
lines = data["lines"]
ltype = data["type"]
if "partners_id" in data:
default_partners_id = data["partners_id"]
except Exception as ee:
res['error'] = str(ee)
coop_looger.error("generate_inventory_list : %s", str(e))
return JsonResponse(res, status=500)
try:
res = CagetteInventory.create_custom_inv_file(lines, ltype, default_partners_id)
except Exception as e: except Exception as e:
res['error'] = str(e) res['error'] = str(e)
coop_looger.error("generate_inventory_list : %s", str(e)) coop_looger.error("generate_inventory_list : %s", str(e))
......
.page_body{
position: relative;
}
#form_container { #form_container {
margin-top: 30px; margin-top: 30px;
} }
...@@ -7,7 +11,7 @@ ...@@ -7,7 +11,7 @@
} }
#products_table_filter input{ #products_table_filter input{
height: 50px; height: 40px;
} }
#supplier_input { #supplier_input {
...@@ -29,6 +33,21 @@ ...@@ -29,6 +33,21 @@
font-weight: bold; font-weight: bold;
} }
#table_header_select_all{
display: flex;
align-content: center;
justify-content: center;
flex-wrap: wrap;
}
#table_header_select_all input{
margin-left: 5px;
}
.select_product_cb {
cursor: pointer;
}
#suppliers_container { #suppliers_container {
display: flex; display: flex;
justify-content: center; justify-content: center;
...@@ -56,5 +75,8 @@ ...@@ -56,5 +75,8 @@
cursor: pointer; cursor: pointer;
} }
#actions_buttons_area {
position: absolute;
top: 0;
right: 0;
}
var suppliers_list = [], var suppliers_list = [],
products_table = null, products_table = null,
products = []
selected_suppliers = [], selected_suppliers = [],
products = []; selected_rows = [];
/* CALLS TO SERVER */
/** /**
* Add a supplier to the selected suppliers list. * Add a supplier to the selected suppliers list.
...@@ -69,13 +67,34 @@ function add_supplier() { ...@@ -69,13 +67,34 @@ function add_supplier() {
} }
/** /**
* 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_display();
}
/**
* Send to server the association product-supplier * Send to server the association product-supplier
* *
* @param {object} product * @param {object} product
* @param {object} supplier * @param {object} supplier
* @param {node} cell product's row in datatable * @param {node} cell product's row in datatable
*/ */
function save_supplier_product_association(product, supplier, cell) { function save_supplier_product_association(product, supplier, cell) {
openModal(); openModal();
const data = { const data = {
...@@ -119,26 +138,6 @@ function save_supplier_product_association(product, supplier, cell) { ...@@ -119,26 +138,6 @@ function save_supplier_product_association(product, supplier, cell) {
} }
/** /**
* 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_display();
}
/**
* When products are fetched, save them and the relation with the supplier. * When products are fetched, save them and the relation with the supplier.
* If product already saved, add the supplier to its suppliers list. * If product already saved, add the supplier to its suppliers list.
* Else, add product with supplier. * Else, add product with supplier.
...@@ -221,7 +220,6 @@ function display_suppliers() { ...@@ -221,7 +220,6 @@ function display_suppliers() {
const supplier_id = el_id[el_id.length-1]; const supplier_id = el_id[el_id.length-1];
let modal_remove_supplier = $('#templates #modal_remove_supplier'); let modal_remove_supplier = $('#templates #modal_remove_supplier');
modal_remove_supplier.find(".supplier_name").text(supplier.display_name); modal_remove_supplier.find(".supplier_name").text(supplier.display_name);
openModal( openModal(
...@@ -285,7 +283,16 @@ function prepare_datatable_columns() { ...@@ -285,7 +283,16 @@ function prepare_datatable_columns() {
{ {
data: "id", data: "id",
title: "id", title: "id",
visible: false, 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", data: "default_code",
...@@ -315,7 +322,7 @@ function prepare_datatable_columns() { ...@@ -315,7 +322,7 @@ function prepare_datatable_columns() {
columns.push({ columns.push({
data: supplier_column_name(supplier), data: supplier_column_name(supplier),
title: supplier.display_name, title: supplier.display_name,
width: "8%", width: "10%",
className: `dt-body-center supplier_input_cell`, className: `dt-body-center supplier_input_cell`,
render: (data, type, full) => { render: (data, type, full) => {
const base_id = `product_${full.id}_supplier_${supplier.id}`; const base_id = `product_${full.id}_supplier_${supplier.id}`;
...@@ -411,8 +418,6 @@ function display_products() { ...@@ -411,8 +418,6 @@ function display_products() {
// Associate product to supplier on click on 'X' in the table // Associate product to supplier on click on 'X' in the table
$('#products_table').on('click', 'tbody .product_not_from_supplier', function () { $('#products_table').on('click', 'tbody .product_not_from_supplier', function () {
// todo istimeto
// Get supplier & product id // Get supplier & product id
const el_id = $(this).children().first().attr('id') const el_id = $(this).children().first().attr('id')
.split('_'); .split('_');
...@@ -423,7 +428,6 @@ function display_products() { ...@@ -423,7 +428,6 @@ function display_products() {
const supplier = selected_suppliers.find(s => s.id == supplier_id) const supplier = selected_suppliers.find(s => s.id == supplier_id)
let modal_attach_product_to_supplier = $('#templates #modal_attach_product_to_supplier'); 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(".product_name").text(product.name);
modal_attach_product_to_supplier.find(".supplier_name").text(supplier.display_name); modal_attach_product_to_supplier.find(".supplier_name").text(supplier.display_name);
...@@ -439,15 +443,141 @@ function display_products() { ...@@ -439,15 +443,141 @@ function display_products() {
); );
}); });
// Select row(s) on checkbox change
$('#products_table').on('click', 'thead 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)
});
} 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; 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 );
});
selected_rows = []
}
/**
* Update DOM display * Update DOM display
*/ */
function update_display() { function update_display() {
// Remove listener before recreating them
$('#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');
display_suppliers(); display_suppliers();
display_products(); 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 );
}
});
}
}
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',
);
}
}
} }
$(document).ready(function() { $(document).ready(function() {
...@@ -488,4 +618,8 @@ $(document).ready(function() { ...@@ -488,4 +618,8 @@ $(document).ready(function() {
e.preventDefault(); e.preventDefault();
add_supplier(); add_supplier();
}); });
$("#do_inventory").on("click", function(e) {
generate_inventory();
});
}); });
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
lists = {{lists|safe}} var lists = {{lists|safe}}
</script> </script>
<script src="{% static "js/all_common.js" %}?v="></script> <script src="{% static "js/all_common.js" %}?v="></script>
<script src="{% static "js/common.js" %}?v="></script> <script src="{% static "js/common.js" %}?v="></script>
......
...@@ -10,10 +10,15 @@ ...@@ -10,10 +10,15 @@
{% block additionnal_scripts %} {% block additionnal_scripts %}
<script type="text/javascript" src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}?v="></script> <script type="text/javascript" src="{% static 'jquery-ui-1.12.1/jquery-ui.min.js' %}?v="></script>
<script type="text/javascript" src="{% static 'js/datatables/jquery.dataTables.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/datatables/jquery.dataTables.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/notify.min.js' %}?v="></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="page_body"> <div class="page_body">
<div id="actions_buttons_area">
<button type="button" class='btn--primary' id="do_inventory">Faire un inventaire</button>
</div>
<div class="header txtcenter"> <div class="header txtcenter">
<h1>Aide à la commande</h1> <h1>Aide à la commande</h1>
</div> </div>
...@@ -64,6 +69,14 @@ ...@@ -64,6 +69,14 @@
<p>Êtez-vous sûr ?</p> <p>Êtez-vous sûr ?</p>
<hr/> <hr/>
</div> </div>
<div id="modal_create_inventory">
<p>
Vous vous apprêtez à créer un inventaire de <span class="inventory_products_count"></span> produits.
</p>
<p>Êtez-vous sûr ?</p>
<hr/>
</div>
</div> </div>
<script src="{% static "js/all_common.js" %}?v="></script> <script src="{% static "js/all_common.js" %}?v="></script>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment