Commit a82672f8 by Damien Moulard

make a product available for a supplier

parent d8d04777
......@@ -279,4 +279,4 @@ class CagetteSuppliers(models.Model):
c = [['supplier', '=', 1], ['parent_id', '=', False]]
res = api.search_read('res.partner', c, f)
return res
\ No newline at end of file
return res
......@@ -22,8 +22,12 @@
.product_not_from_supplier {
background-color: #e7e9ed;
cursor: pointer;
}
.product_name, .supplier_name {
font-weight: bold;
}
#suppliers_container {
display: flex;
......
......@@ -3,6 +3,9 @@ var suppliers_list = [],
selected_suppliers = [],
products = [];
/* CALLS TO SERVER */
/**
* Add a supplier to the selected suppliers list.
*
......@@ -66,6 +69,56 @@ function add_supplier() {
}
/**
* 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.');
}
});
}
/**
* Remove a supplier from the selected list & its associated products
*
* @param {int} supplier_id
......@@ -156,7 +209,7 @@ function display_suppliers() {
for (supplier of selected_suppliers) {
let template = $("#templates #supplier_pill");
template.find(".supplier_name").text(supplier.display_name);
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());
......@@ -184,12 +237,20 @@ function display_suppliers() {
/* DATATABLE */
/**
* @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() {
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) {
for (product of products_to_format) {
let item = {
id: product.id,
name: product.name,
......@@ -250,18 +311,21 @@ function prepare_datatable_columns() {
},
];
for (supplier of selected_suppliers) {
for (const supplier of selected_suppliers) {
columns.push({
data: supplier_column_name(supplier),
title: supplier.display_name,
width: "8%",
className:"dt-body-center supplier_input_cell",
render: function (data, type, full) {
className: `dt-body-center supplier_input_cell`,
render: (data, type, full) => {
const base_id = `product_${full.id}_supplier_${supplier.id}`;
if (data === false) {
return "X";
return `<div id="${base_id}_cell_content" class="cell_content">X</div>`;
} else {
const input_id = `product_${full.id}_supplier_${supplier.id}_qty_input`;
return `<input type="number" class="product_qty_input" id=${input_id} value=${data}>`;
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>`;
}
}
});
......@@ -311,6 +375,7 @@ function display_products() {
createdRow: function( row, data, dataIndex ) {
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' );
......@@ -344,6 +409,36 @@ function display_products() {
}
});
// Associate product to supplier on click on 'X' in the table
$('#products_table').on('click', 'tbody .product_not_from_supplier', function () {
// todo istimeto
// 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);
const cell = this
openModal(
modal_attach_product_to_supplier.html(),
() => {
save_supplier_product_association(product, supplier, cell);
},
'Valider',
false
);
});
return 0;
}
......@@ -393,6 +488,4 @@ $(document).ready(function() {
e.preventDefault();
add_supplier();
});
// TODO: on click on 'X' change to input, make product available for this supplier
});
......@@ -12,4 +12,5 @@ urlpatterns = [
url(r'^helper$', views.helper),
url(r'^get_suppliers$', views.get_suppliers),
url(r'^get_supplier_products$', views.get_supplier_products),
url(r'^associate_supplier_to_product$', views.associate_supplier_to_product),
]
......@@ -45,6 +45,18 @@ def get_supplier_products(request):
else:
return JsonResponse({'res': res})
def associate_supplier_to_product(request):
""" This product is now supplied by this supplier """
res = {}
try:
data = json.loads(request.body.decode())
res = CagetteProduct.associate_supplier_to_product(data["product_tmpl_id"], data["supplier_id"])
except Exception as e:
res["error"] = str(e)
return JsonResponse(res, status=500)
return JsonResponse({'res': res})
def export_one(request, oid):
msg = ''
try:
......
......@@ -127,6 +127,27 @@ class CagetteProduct(models.Model):
res = api.create('product.supplier.shortage', f)
return res
@staticmethod
def associate_supplier_to_product(product_tmpl_id, partner_id):
api = OdooAPI()
f = ["id", "standard_price", "purchase_ok"]
c = [['product_tmpl_id', '=', product_tmpl_id]]
res_products = api.search_read('product.product', c, f)
product = res_products[0]
f = {
'product_tmpl_id' : product_tmpl_id,
'product_id' : product["id"],
'name' : partner_id,
'product_purchase_ok': product["purchase_ok"],
'price': product["standard_price"], # By default, use product price
'base_price': product["standard_price"],
}
res = api.create('product.supplierinfo', f)
return res
class CagetteProducts(models.Model):
"""Initially used to make massive barcode update."""
......
......@@ -38,7 +38,7 @@
<div id="supplier_pill">
<div class="supplier_pill">
<div class="supplier_name_container">
<span class="supplier_name"></span>
<span class="pill_supplier_name"></span>
<i class="fas fa-times remove_supplier_icon"></i>
</div>
</div>
......@@ -47,12 +47,21 @@
<div id="modal_remove_supplier">
<h3>Attention !</h3>
<p>
Vous vous apprêtez à supprimer le fournisseur <span class="supplier_name"></span>.<br/>
Les produits associés à ce fournisseur uniquement seront supprimés.<br/>
Vous vous apprêtez à supprimer le fournisseur <span class="supplier_name"></span> de la sélection.<br/>
Les produits associés uniquement à ce fournisseur seront supprimés du tableau.<br/>
Les données renseignées dans la colonne de ce fournisseur seront perdues.
</p>
<p>Êtez-vous sûr ?</p>
<ul id="list_unprocessable_porducts"></ul>
<hr/>
</div>
<div id="modal_attach_product_to_supplier">
<h3>Attention !</h3>
<p>
Vous vous apprêtez à associer le produit <span class="product_name"></span> au fournisseur <span class="supplier_name"></span>.<br/>
L'association sera sauvegardée dès que vous aurez cliqué sur "Valider".<br/>
</p>
<p>Êtez-vous sûr ?</p>
<hr/>
</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