Commit b32df584 by Damien Moulard Committed by Alexis Aoun

create product orders in odoo

parent ae23416e
...@@ -222,6 +222,81 @@ class Order(models.Model): ...@@ -222,6 +222,81 @@ class Order(models.Model):
labels_data['total'] += l['product_qty'] labels_data['total'] += l['product_qty']
return labels_data return labels_data
def get_order_attachment_id(self):
res = {}
f = ["id"]
c = [['res_model', '=', 'purchase.order'], ['res_id', '=', self.id], ['type', 'in', ['binary', 'url']]]
try:
attachment = self.o_api.search_read('ir.attachment', c, f)
res = attachment[0]
except Exception as e:
res["id_po"] = self.id
res["error"] = str(e)
return res
@staticmethod
def create(supplier_id, date_planned, order_lines):
order_data = {
"partner_id": int(supplier_id),
"partner_ref": False,
"currency_id": 1,
"date_order": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"origin": "Aide à la commande",
"company_id": 1,
"order_line": [],
"notes": False,
"date_planned": date_planned,
"picking_type_id": 1,
"dest_address_id": False,
"incoterm_id": False,
"payment_term_id": False,
"fiscal_position_id": False,
"message_follower_ids": False,
"message_ids": False
}
for line in order_lines:
order_data["order_line"].append(
[
0,
False,
{
"package_qty": line["package_qty"],
"price_policy": "uom",
"indicative_package": True,
"product_id": line["product_variant_ids"][0],
"name": line["name"],
"date_planned": date_planned,
"account_analytic_id": False,
"product_qty_package":line["product_qty_package"],
"product_qty": line["product_qty"],
"product_uom": line["product_uom"],
"price_unit": line["price_unit"],
"discount": 0,
"taxes_id": [
[
6,
False,
line["supplier_taxes_id"]
]
]
}
]
)
api = OdooAPI()
id_po = api.create('purchase.order', order_data)
res_confirm = api.execute('purchase.order', 'button_confirm', [id_po])
res = {
'id_po': id_po,
'confirm_po': True
}
return res
class Orders(models.Model): class Orders(models.Model):
@staticmethod @staticmethod
......
...@@ -72,14 +72,20 @@ ...@@ -72,14 +72,20 @@
right: 0; right: 0;
} }
/* -- Supplier form */ /* -- Order data */
#supplier_form_container { #order_data_container {
margin-top: 30px; margin-top: 30px;
display: flex;
justify-content: space-evenly;
} }
#supplier_input { #supplier_input {
width: 500px; width: 350px;
margin-right: 10px; border-radius: 5px;
}
#date_planned_input {
margin-left: 40px;
border-radius: 5px; border-radius: 5px;
} }
...@@ -89,7 +95,9 @@ ...@@ -89,7 +95,9 @@
} }
#products_table_filter input{ #products_table_filter input{
height: 40px; height: 35px;
width: 300px;
border-radius: 10px;
} }
#table_header_select_all{ #table_header_select_all{
...@@ -125,6 +133,14 @@ ...@@ -125,6 +133,14 @@
cursor: pointer; cursor: pointer;
} }
/* -- Bottom action button */
#orders_creation_area {
display: flex;
justify-content: space-between;
margin: 15px 0 35px 0;
}
/* -- Suppliers list */ /* -- Suppliers list */
#suppliers_container { #suppliers_container {
display: flex; display: flex;
...@@ -143,3 +159,54 @@ ...@@ -143,3 +159,54 @@
margin-left: 5px; margin-left: 5px;
cursor: pointer; cursor: pointer;
} }
/* - Orders created screen */
.order_created_header {
margin-top: 15px;
margin-bottom: 40px;
}
#created_orders_area {
display: flex;
flex-wrap: wrap;
justify-content: space-evenly;
}
.new_order_item {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 20px;
}
.download_order_file {
margin-top: 10px;
}
.download_order_file_button:hover {
text-decoration: none;
color: white;
}
#recap_delivery_date {
font-weight: bold;
}
.mail_example_container {
display: flex;
flex-direction: column;
align-items: center;
width: 30%;
margin: 0 auto;
}
.mail_type_text {
width: 100%;
}
.mail_example {
background-color: #e7e9ed;
width: 100%;
padding: 15px;
}
\ No newline at end of file
...@@ -7,7 +7,8 @@ var suppliers_list = [], ...@@ -7,7 +7,8 @@ var suppliers_list = [],
sync = null, sync = null,
order_doc = { order_doc = {
_id: null, _id: null,
last_update : { date_planned: null,
last_update: {
timestamp: null, timestamp: null,
fingerprint: null fingerprint: null
}, },
...@@ -15,7 +16,9 @@ var suppliers_list = [], ...@@ -15,7 +16,9 @@ var suppliers_list = [],
selected_suppliers: [], selected_suppliers: [],
selected_rows: [] selected_rows: []
}, },
fingerprint = null; fingerprint = null,
date_format = "dd/mm/yy"
product_orders = [];
/* - UTILS */ /* - UTILS */
...@@ -29,6 +32,7 @@ function reset_data() { ...@@ -29,6 +32,7 @@ function reset_data() {
selected_rows = [], selected_rows = [],
order_doc = { order_doc = {
_id: null, _id: null,
date_planned: null,
last_update : { last_update : {
timestamp: null, timestamp: null,
fingerprint: null fingerprint: null
...@@ -96,7 +100,6 @@ function add_supplier() { ...@@ -96,7 +100,6 @@ function add_supplier() {
selected_suppliers.push(supplier); selected_suppliers.push(supplier);
let url = "/orders/get_supplier_products"; let url = "/orders/get_supplier_products";
url += "?sid=" + encodeURIComponent(supplier.id); url += "?sid=" + encodeURIComponent(supplier.id);
// Fetch supplier products // Fetch supplier products
...@@ -110,7 +113,7 @@ function add_supplier() { ...@@ -110,7 +113,7 @@ function add_supplier() {
save_supplier_products(supplier, data.res.products); save_supplier_products(supplier, data.res.products);
update_main_screen(); update_main_screen();
$("#supplier_input").val(""); $("#supplier_input").val("");
update_order(); update_cdb_order();
closeModal(); closeModal();
}, },
error: function(data) { error: function(data) {
...@@ -139,14 +142,14 @@ function remove_supplier(supplier_id) { ...@@ -139,14 +142,14 @@ function remove_supplier(supplier_id) {
// Remove the supplier from the products suppliers list // Remove the supplier from the products suppliers list
for (const i in products) { for (const i in products) {
products[i].suppliers = products[i].suppliers.filter(supplier => supplier.id != supplier_id); products[i].suppliersinfo = products[i].suppliersinfo.filter(supplier => supplier.supplier_id != supplier_id);
} }
// Remove products only associated to this product // Remove products only associated to this product
products = products.filter(product => product.suppliers.length > 0); products = products.filter(product => product.suppliersinfo.length > 0);
update_main_screen(); update_main_screen();
update_order(); update_cdb_order();
} }
...@@ -165,7 +168,7 @@ function save_supplier_product_association(product, supplier, cell) { ...@@ -165,7 +168,7 @@ function save_supplier_product_association(product, supplier, cell) {
supplier_id: supplier.id supplier_id: supplier.id
}; };
// Fetch supplier products // Send request to create association
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: "/orders/associate_supplier_to_product", url: "/orders/associate_supplier_to_product",
...@@ -175,6 +178,7 @@ function save_supplier_product_association(product, supplier, cell) { ...@@ -175,6 +178,7 @@ function save_supplier_product_association(product, supplier, cell) {
data: JSON.stringify(data), data: JSON.stringify(data),
success: () => { success: () => {
// Save relation locally // Save relation locally
// TODO: save supplierinfo in product
save_supplier_products(supplier, [product]); save_supplier_products(supplier, [product]);
// Update table // Update table
...@@ -216,10 +220,10 @@ function save_supplier_products(supplier, new_products) { ...@@ -216,10 +220,10 @@ function save_supplier_products(supplier, new_products) {
let index = products.findIndex(p => p.id === np.id); let index = products.findIndex(p => p.id === np.id);
if (index === -1) { if (index === -1) {
np.suppliers = [{ ...supplier }];
products.push(np); products.push(np);
} else { } else {
products[index].suppliers.push({ ...supplier }); np_supplierinfo = np.suppliersinfo[0];
products[index].suppliersinfo.push(np_supplierinfo);
} }
} }
} }
...@@ -234,9 +238,9 @@ function save_supplier_products(supplier, new_products) { ...@@ -234,9 +238,9 @@ function save_supplier_products(supplier, new_products) {
function save_product_supplier_qty(prod_id, supplier_id, val) { function save_product_supplier_qty(prod_id, supplier_id, val) {
for (const i in products) { for (const i in products) {
if (products[i].id == prod_id) { if (products[i].id == prod_id) {
for (const j in products[i].suppliers) { for (const j in products[i].suppliersinfo) {
if (products[i].suppliers[j].id == supplier_id) { if (products[i].suppliersinfo[j].supplier_id == supplier_id) {
products[i].suppliers[j].qty = val; products[i].suppliersinfo[j].qty = val;
break; break;
} }
} }
...@@ -252,7 +256,7 @@ function save_product_supplier_qty(prod_id, supplier_id, val) { ...@@ -252,7 +256,7 @@ function save_product_supplier_qty(prod_id, supplier_id, val) {
* @returns boolean * @returns boolean
*/ */
function is_product_related_to_supplier(product, supplier) { function is_product_related_to_supplier(product, supplier) {
return product.suppliers.find(s => s.id === supplier.id) !== undefined; return product.suppliersinfo.find(s => s.supplier_id === supplier.id) !== undefined;
} }
/* - PRODUCT */ /* - PRODUCT */
...@@ -282,7 +286,7 @@ function set_product_npa(p_id, npa) { ...@@ -282,7 +286,7 @@ 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"]; products[index].purchase_ok = data["purchase_ok"];
update_order(); update_cdb_order();
closeModal(); closeModal();
}, },
...@@ -325,9 +329,9 @@ function generate_inventory() { ...@@ -325,9 +329,9 @@ function generate_inventory() {
const product = products.find(p => p.id == selected_data[i].id); const product = products.find(p => p.id == selected_data[i].id);
data.lines.push(product.id); data.lines.push(product.id);
for (const supplier of product.suppliers) { for (const supplierinfo of product.suppliersinfo) {
if (data.partners_id.indexOf(supplier.id) === -1) { if (data.partners_id.indexOf(supplierinfo.supplier_id) === -1) {
data.partners_id.push(supplier.id); data.partners_id.push(supplierinfo.supplier_id);
} }
} }
} }
...@@ -351,10 +355,10 @@ function generate_inventory() { ...@@ -351,10 +355,10 @@ function generate_inventory() {
// Give time for modal to fade // Give time for modal to fade
setTimeout(function() { setTimeout(function() {
$.notify( $('#do_inventory').notify(
"Inventaire créé !", "Inventaire créé !",
{ {
globalPosition:"top left", globalPosition:"bottom center",
className: "success" className: "success"
} }
); );
...@@ -439,13 +443,18 @@ function order_pill_on_click() { ...@@ -439,13 +443,18 @@ function order_pill_on_click() {
/** /**
* Create an order in couchdb if the name doesn't exist * Create an order in couchdb if the name doesn't exist
*/ */
function create_order() { function create_cdb_order() {
const order_name = $("#new_order_name").val(); const order_name = $("#new_order_name").val();
order_doc._id = order_name; order_doc._id = order_name;
order_doc.last_update = {
timestamp: Date.now(),
fingerprint: fingerprint
};
dbc.put(order_doc, function callback(err, result) { dbc.put(order_doc, function callback(err, result) {
if (!err) { if (!err) {
order_doc._rev = result.rev; order_doc._rev = result.rev;
update_main_screen();
switch_screen(); switch_screen();
} else { } else {
if (err.status == 409) { if (err.status == 409) {
...@@ -459,7 +468,7 @@ function create_order() { ...@@ -459,7 +468,7 @@ function create_order() {
/** /**
* Update order data of an existing order in couchdb * Update order data of an existing order in couchdb
*/ */
function update_order() { function update_cdb_order() {
order_doc.products = products; order_doc.products = products;
order_doc.selected_suppliers = selected_suppliers; order_doc.selected_suppliers = selected_suppliers;
...@@ -479,6 +488,146 @@ function update_order() { ...@@ -479,6 +488,146 @@ function update_order() {
}); });
} }
/**
* Create the Product Orders in Odoo
*/
function create_orders() {
openModal();
let orders_data = {
"date_planned": order_doc.date_planned,
"suppliers_data": {}
};
// Prepare data: get products where a qty is set
for (let p of products) {
for (let p_supplierinfo of p.suppliersinfo) {
// If a qty is set for a supplier for a product
if ('qty' in p_supplierinfo) {
const supplier_id = p_supplierinfo.supplier_id;
// Create entry for this supplier in data object if doesn't exist
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,
'product_id': p.id,
'name': p.name,
'product_qty_package': p_supplierinfo.qty,
'product_qty': p_supplierinfo.qty * p_supplierinfo.package_qty,
'product_uom': p.uom_id[0],
'price_unit': p_supplierinfo.price,
'supplier_taxes_id': p.supplier_taxes_id,
'product_variant_ids': p.product_variant_ids
})
}
}
}
if (Object.keys(orders_data.suppliers_data).length === 0) {
closeModal();
alert("Commande non créée : vous n'avez rentré aucune quantité !");
return -1;
}
$.ajax({
type: "POST",
url: "/orders/create_orders",
dataType: "json",
traditional: true,
contentType: "application/json; charset=utf-8",
data: JSON.stringify(orders_data),
success: (result) => {
$('#recap_delivery_date').text($('#date_planned_input').val());
// Display new orders
for (let new_order of result.res.created) {
const supplier_name = suppliers_list.find(s => s.id == new_order.supplier_id).display_name;
product_orders.push({
'id': new_order.id_po,
'supplier_id': new_order.supplier_id,
'supplier_name': supplier_name
})
let new_order_template = $("#templates #new_order_item_template");
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(".download_order_file_button").attr('id', `download_attachment_${new_order.id_po}`);
$('#created_orders_area').append(new_order_template.html());
}
// Prepare buttons to download order attachment
get_order_attachments();
// Clear data
order_doc._deleted = true;
update_cdb_order();
reset_data();
update_order_selection_screen();
switch_screen('orders_created');
closeModal();
},
error: function(data) {
let msg = "erreur serveur lors de la création des product orders"
err = {msg: msg, ctx: 'save_supplier_product_association', data: orders_data};
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 création des commandes. Veuillez ré-essayer plus tard.');
}
});
}
/**
* Get the PO attachment id.
* Display download button when fetch is succesful.
* The file might not be created soon enough, so try again after 10s if error server
*/
function get_order_attachments() {
if (product_orders.length > 0) {
let po_ids = product_orders.map(po => po.id);
$.ajax({
type: 'GET',
url: "/orders/get_orders_attachment",
data: {
'po_ids': po_ids
},
dataType:"json",
traditional: true,
contentType: "application/json; charset=utf-8",
success: function(data) {
for (let res_po of data.res) {
$(`#download_attachment_${res_po.id_po}`).attr('href', `${odoo_server}/web/content/${res_po.id_attachment}?download=true`)
}
$('#created_orders_area .download_order_file_loading').hide()
$('#created_orders_area .download_order_file_button').show()
},
error: function(data) {
console.log(data);
$.notify(
"Échec de la récupération du lien de téléchargement des fichiers. Nouvelle tentative dans 10s.",
{
globalPosition:"top right",
className: "error"
}
);
setTimeout(get_order_attachments, 10000);
}
});
}
}
/* - DISPLAY */ /* - DISPLAY */
...@@ -487,7 +636,7 @@ function goto_main_screen(doc) { ...@@ -487,7 +636,7 @@ function goto_main_screen(doc) {
products = order_doc.products; products = order_doc.products;
selected_suppliers = order_doc.selected_suppliers; selected_suppliers = order_doc.selected_suppliers;
update_order(); update_cdb_order();
update_main_screen(); update_main_screen();
switch_screen(); switch_screen();
} }
...@@ -503,7 +652,8 @@ function back() { ...@@ -503,7 +652,8 @@ function back() {
* @returns String * @returns String
*/ */
function supplier_column_name(supplier) { function supplier_column_name(supplier) {
return `qty_supplier_${supplier.id}`; const supplier_id = ('supplier_id' in supplier) ? supplier.supplier_id : supplier.id
return `qty_supplier_${supplier_id}`;
} }
/** /**
...@@ -572,7 +722,7 @@ function prepare_datatable_data(product_ids = []) { ...@@ -572,7 +722,7 @@ function prepare_datatable_data(product_ids = []) {
}; };
// If product related to supplier: qty or null (qty to be set) // If product related to supplier: qty or null (qty to be set)
for (product_supplier of product.suppliers) { for (product_supplier of product.suppliersinfo) {
let supplier_qty = ("qty" in product_supplier) ? product_supplier.qty : null; let supplier_qty = ("qty" in product_supplier) ? product_supplier.qty : null;
item[supplier_column_name(product_supplier)] = supplier_qty; item[supplier_column_name(product_supplier)] = supplier_qty;
...@@ -695,6 +845,8 @@ function prepare_datatable_columns() { ...@@ -695,6 +845,8 @@ function prepare_datatable_columns() {
function display_products() { function display_products() {
if (products.length == 0) { if (products.length == 0) {
$('.main').hide(); $('.main').hide();
$('#create_orders').hide();
$('#do_inventory').hide();
return -1; return -1;
} }
...@@ -736,6 +888,8 @@ function display_products() { ...@@ -736,6 +888,8 @@ function display_products() {
}); });
$('.main').show(); $('.main').show();
$('#create_orders').show();
$('#do_inventory').show();
// On inputs change // On inputs change
$('#products_table').on('input', 'tbody td .product_qty_input', function () { $('#products_table').on('input', 'tbody td .product_qty_input', function () {
...@@ -756,7 +910,7 @@ function display_products() { ...@@ -756,7 +910,7 @@ function display_products() {
const new_row_data = prepare_datatable_data([product.id])[0]; const new_row_data = prepare_datatable_data([product.id])[0];
products_table.row($(this).closest('tr')).data(new_row_data).draw(); products_table.row($(this).closest('tr')).data(new_row_data).draw();
update_order(); update_cdb_order();
} }
}); });
...@@ -899,6 +1053,15 @@ function update_main_screen() { ...@@ -899,6 +1053,15 @@ function update_main_screen() {
return 0; return 0;
}); });
} }
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('');
}
} }
/** /**
...@@ -936,21 +1099,32 @@ function update_order_selection_screen() { ...@@ -936,21 +1099,32 @@ function update_order_selection_screen() {
} }
/** /**
* Switch screen between order selection & main screens * Switch between screens
* @param {String} direction target screen * @param {String} direction target screen : order_selection | main_screen | orders_created
* @param {String} from source screen : order_selection | main_screen | orders_created
*/ */
function switch_screen(direction = 'main_screen') { function switch_screen(direction = 'main_screen', from = 'main_screen') {
if (direction === 'orders_created') {
$('#main_content').hide();
$('#orders_created').show();
} else {
// Animated transition
let oldBox = null; let oldBox = null;
let newBox = null; let newBox = null;
let outerWidth = null; let outerWidth = null;
if (direction == 'main_screen') { if (direction === 'main_screen') {
oldBox = $("#select_order_content"); oldBox = $("#select_order_content");
newBox = $("#main_content"); newBox = $("#main_content");
outerWidth = oldBox.outerWidth(true); outerWidth = oldBox.outerWidth(true);
} else { } else {
if (from === 'orders_created') {
oldBox = $("#orders_created");
} else {
oldBox = $("#main_content"); oldBox = $("#main_content");
}
newBox = $("#select_order_content"); newBox = $("#select_order_content");
outerWidth = - oldBox.outerWidth(true); outerWidth = - oldBox.outerWidth(true);
...@@ -965,6 +1139,7 @@ function switch_screen(direction = 'main_screen') { ...@@ -965,6 +1139,7 @@ function switch_screen(direction = 'main_screen') {
}); });
// Slide new box to regular place // Slide new box to regular place
newBox.animate({ "left": "", "right": "" }, 800); newBox.animate({ "left": "", "right": "" }, 800);
}
} }
...@@ -985,8 +1160,8 @@ $(document).ready(function() { ...@@ -985,8 +1160,8 @@ $(document).ready(function() {
sync.on('change', function (info) { sync.on('change', function (info) {
if (info.direction === "pull") { if (info.direction === "pull") {
for (const doc of info.change.docs) { for (const doc of info.change.docs) {
if (order_doc._id === doc._id && (order_doc._rev !== doc._rev || doc._deleted === true)) {
// If current order was modified somewhere else // If current order was modified somewhere else
if (order_doc._id === doc._id && order_doc._rev !== doc._rev) {
$.notify( $.notify(
"Un autre navigateur est en train de modifier cette commande !", "Un autre navigateur est en train de modifier cette commande !",
{ {
...@@ -994,8 +1169,11 @@ $(document).ready(function() { ...@@ -994,8 +1169,11 @@ $(document).ready(function() {
className: "error" className: "error"
} }
); );
update_order_selection_screen();
back(); back();
break; break;
} else if (doc._deleted === true) {
update_order_selection_screen();
} }
} }
} }
...@@ -1008,7 +1186,7 @@ $(document).ready(function() { ...@@ -1008,7 +1186,7 @@ $(document).ready(function() {
console.log(err); console.log(err);
}); });
// Main screen listeners // Main screen
$("#supplier_form").on("submit", function(e) { $("#supplier_form").on("submit", function(e) {
e.preventDefault(); e.preventDefault();
add_supplier(); add_supplier();
...@@ -1018,16 +1196,97 @@ $(document).ready(function() { ...@@ -1018,16 +1196,97 @@ $(document).ready(function() {
generate_inventory(); generate_inventory();
}); });
$('#back_to_order_selection').on('click', function() { $('#back_to_order_selection_from_main').on('click', function() {
back(); back();
}); });
$('#create_orders').on('click', function() {
if (order_doc.date_planned === null) {
alert("Veuillez rentrer une date de livraison prévue.")
return -1
}
let modal_create_order = $('#templates #modal_create_order');
openModal(
modal_create_order.html(),
() => {
create_orders();
},
'Valider',
false
);
});
$.datepicker.regional['fr'] = {
monthNames: [
'Janvier',
'Fevrier',
'Mars',
'Avril',
'Mai',
'Juin',
'Juillet',
'Aout',
'Septembre',
'Octobre',
'Novembre',
'Decembre'
],
dayNamesMin: [
'Di',
'Lu',
'Ma',
'Me',
'Je',
'Ve',
'Sa'
],
dateFormat: date_format,
};
$.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();
$("#new_order_form").on("submit", function(e) { $("#new_order_form").on("submit", function(e) {
e.preventDefault(); e.preventDefault();
create_order(); create_cdb_order();
});
// Orders created screen
$('#back_to_order_selection_from_orders_created').on('click', function() {
switch_screen('order_selection', 'orders_created');
}); });
// Get suppliers // Get suppliers
......
...@@ -13,4 +13,6 @@ urlpatterns = [ ...@@ -13,4 +13,6 @@ urlpatterns = [
url(r'^get_suppliers$', views.get_suppliers), url(r'^get_suppliers$', views.get_suppliers),
url(r'^get_supplier_products$', views.get_supplier_products), url(r'^get_supplier_products$', views.get_supplier_products),
url(r'^associate_supplier_to_product$', views.associate_supplier_to_product), url(r'^associate_supplier_to_product$', views.associate_supplier_to_product),
url(r'^create_orders$', views.create_orders),
url(r'^get_orders_attachment$', views.get_orders_attachment),
] ]
...@@ -7,6 +7,7 @@ from products.models import CagetteProduct, CagetteProducts ...@@ -7,6 +7,7 @@ from products.models import CagetteProduct, CagetteProducts
from openpyxl import Workbook from openpyxl import Workbook
from openpyxl.writer.excel import save_virtual_workbook from openpyxl.writer.excel import save_virtual_workbook
import datetime
def as_text(value): return str(value) if value is not None else "" def as_text(value): return str(value) if value is not None else ""
...@@ -17,7 +18,8 @@ def helper(request): ...@@ -17,7 +18,8 @@ def helper(request):
context = { context = {
'title': 'Aide à la commande', 'title': 'Aide à la commande',
'couchdb_server': settings.COUCHDB['url'], 'couchdb_server': settings.COUCHDB['url'],
'db': settings.COUCHDB['dbs']['orders'] 'db': settings.COUCHDB['dbs']['orders'],
'odoo_server': settings.ODOO['url']
} }
template = loader.get_template('orders/helper.html') template = loader.get_template('orders/helper.html')
...@@ -59,6 +61,48 @@ def associate_supplier_to_product(request): ...@@ -59,6 +61,48 @@ def associate_supplier_to_product(request):
return JsonResponse({'res': res}) return JsonResponse({'res': res})
def create_orders(request):
""" Create products orders """
res = { "created": [] }
try:
data = json.loads(request.body.decode())
# suppliers id are keys in request data
for supplier_id in data["suppliers_data"].keys():
res_created = Order.create(supplier_id, data["date_planned"], data["suppliers_data"][supplier_id])
res_created["supplier_id"] = supplier_id
res["created"].append(res_created)
except Exception as e:
res["error"] = str(e)
return JsonResponse(res, status=500)
return JsonResponse({'res': res})
def get_orders_attachment(request):
""" Get order attachment: order file created after PO is finalized """
res = []
po_ids = request.GET.getlist('po_ids')
for id_po in po_ids:
m = Order(int(id_po))
attachment = m.get_order_attachment_id()
if 'error' in attachment:
res.append(attachment)
else:
res.append({
'id_po': id_po,
'id_attachment': attachment["id"]
})
for item in res:
if 'error' in item:
return JsonResponse(res, status=500)
return JsonResponse({'res': res})
def export_one(request, oid): def export_one(request, oid):
msg = '' msg = ''
try: try:
...@@ -66,7 +110,6 @@ def export_one(request, oid): ...@@ -66,7 +110,6 @@ def export_one(request, oid):
order = Order(oid) order = Order(oid)
order_data = order.export() order_data = order.export()
if ('success' in order_data) and (order_data['success'] is True): if ('success' in order_data) and (order_data['success'] is True):
import datetime
now = datetime.datetime.now() now = datetime.datetime.now()
taxes = 0 taxes = 0
company_name = '' company_name = ''
......
...@@ -436,7 +436,7 @@ class CagetteProducts(models.Model): ...@@ -436,7 +436,7 @@ class CagetteProducts(models.Model):
today = datetime.date.today().strftime("%Y-%m-%d") today = datetime.date.today().strftime("%Y-%m-%d")
# Get products/supplier relation # Get products/supplier relation
f = ["product_tmpl_id", 'date_start', 'date_end', 'package_qty'] f = ["product_tmpl_id", 'date_start', 'date_end', 'package_qty', 'price']
c = [['name', '=', int(supplier_id)]] c = [['name', '=', int(supplier_id)]]
psi = api.search_read('product.supplierinfo', c, f) psi = api.search_read('product.supplierinfo', c, f)
...@@ -449,21 +449,30 @@ class CagetteProducts(models.Model): ...@@ -449,21 +449,30 @@ class CagetteProducts(models.Model):
ptids.append(p["product_tmpl_id"][0]) ptids.append(p["product_tmpl_id"][0])
# Get products templates # Get products templates
f = ["id", "state", "name", "default_code", "qty_available", "incoming_qty", "uom_id", "purchase_ok"] f = [
# TODO fetch only 'purchase_ok' products ? "id",
"state",
"name",
"default_code",
"qty_available",
"incoming_qty",
"uom_id",
"purchase_ok",
"supplier_taxes_id",
"product_variant_ids"
]
c = [['id', 'in', ptids], ['purchase_ok', '=', True]] c = [['id', 'in', ptids], ['purchase_ok', '=', True]]
products_t = api.search_read('product.template', c, f) products_t = api.search_read('product.template', c, f)
filtered_products_t = [p for p in products_t if p["state"] != "end" and p["state"] != "obsolete"] filtered_products_t = [p for p in products_t if p["state"] != "end" and p["state"] != "obsolete"]
# Add package qty 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):
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]['supplierinfo'] = { filtered_products_t[i]['suppliersinfo'] = [{
'supplier_id': supplier_id, 'supplier_id': int(supplier_id),
'package_qty': psi_item["package_qty"] 'package_qty': psi_item["package_qty"],
} 'price': psi_item["price"]
}]
# Note: if product.product is needed, get "product_variant_ids" from product template
res["products"] = filtered_products_t res["products"] = filtered_products_t
except Exception as e: except Exception as e:
......
...@@ -24,17 +24,19 @@ ...@@ -24,17 +24,19 @@
</form> </form>
</div> </div>
<div id="existing_orders_area"> <div id="existing_orders_area">
<h2>Ou, continuer une commande existante</h2> <h2>Ou, continuer une commande en cours de création</h2>
<div id="existing_orders"></div> <div id="existing_orders"></div>
</div> </div>
</div> </div>
<div id="main_content" class="page_content" style="display:none;"> <div id="main_content" class="page_content" style="display:none;">
<div id="back_to_order_selection"> <div id="back_to_order_selection_from_main">
<button type="button" class="btn--danger"><i class="fas fa-arrow-left"></i>&nbsp; Retour</button> <button type="button" class="btn--danger"><i class="fas fa-arrow-left"></i>&nbsp; Retour</button>
</div> </div>
<div id="actions_buttons_area"> <div id="actions_buttons_area">
<button type="button" class='btn--primary' id="do_inventory">Faire un inventaire</button> <button type="button" class='btn--primary' id="do_inventory" style="display:none;">
Faire un inventaire
</button>
</div> </div>
<div class="header txtcenter"> <div class="header txtcenter">
...@@ -42,11 +44,12 @@ ...@@ -42,11 +44,12 @@
<i>Commande : <span class="order_name_container"></span></i> <i>Commande : <span class="order_name_container"></span></i>
</div> </div>
<div class="txtcenter" id="supplier_form_container"> <div class="txtcenter" id="order_data_container">
<form action="javascript:;" id="supplier_form"> <form action="javascript:;" id="supplier_form">
<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>
...@@ -56,6 +59,40 @@ ...@@ -56,6 +59,40 @@
<table id="products_table" class="display" cellspacing="0" width="100%"></table> <table id="products_table" class="display" cellspacing="0" width="100%"></table>
</div> </div>
</div> </div>
<div id="orders_creation_area">
<div class="add_product_container"></div>
<button type="button" class='btn--primary' id="create_orders" style="display:none;">
Générer les commandes
</button>
</div>
</div>
<div id="orders_created" class="page_content" style="display:none;">
<div id="back_to_order_selection_from_orders_created">
<button type="button" class="btn--danger"><i class="fas fa-arrow-left"></i>&nbsp; Retour</button>
</div>
<div class="order_created_header txtcenter">
<h2>Commandes créées !</h2>
</div>
<div class="txtcenter">
Livraison prévue le : <span id="recap_delivery_date">XX/XX/XX</span>
</div>
<div id="created_orders_area"></div>
<br/><br/><hr/><br/>
<div class="mail_example_container">
<p class="mail_type_text">Mail type :</p>
<div class="mail_example">
Objet : Cde Cagette JJ/MM<br/>
<br/>
Bonjour XXXXXXX,<br/>
<br/>
Voici la commande de La Cagette pour le XX/XX/XX.<br/>
<br/>
Merci d'avance,<br/>
Bonne journée
</div>
</div>
</div> </div>
<div id="templates" style="display:none;"> <div id="templates" style="display:none;">
...@@ -74,6 +111,19 @@ ...@@ -74,6 +111,19 @@
</div> </div>
</div> </div>
<div id="new_order_item_template">
<div class="new_order_item">
<h3 class="new_order_supplier_name"></h3>
<h3 class="new_order_po"></h3>
<div class='download_order_file'>
<i class="fas fa-spinner fa-spin download_order_file_loading"></i>
<a class='btn--success download_order_file_button' style="display:none;" href="#">
Télécharger le fichier de commande
</a>
</div>
</div>
</div>
<div id="modal_order_access"> <div id="modal_order_access">
<h3>Attention !</h3> <h3>Attention !</h3>
<br/> <br/>
...@@ -126,6 +176,15 @@ ...@@ -126,6 +176,15 @@
<p>Êtez-vous sûr ?</p> <p>Êtez-vous sûr ?</p>
<hr/> <hr/>
</div> </div>
<div id="modal_create_order">
<h3>Attention !</h3>
<p>
Vous vous apprêtez à générer les commandes à partir des données rentrées dans le tableau.
</p>
<p>Êtez-vous sûr ?</p>
<hr/>
</div>
</div> </div>
</div> </div>
...@@ -134,6 +193,7 @@ ...@@ -134,6 +193,7 @@
<script type="text/javascript"> <script type="text/javascript">
var couchdb_dbname = '{{db}}'; var couchdb_dbname = '{{db}}';
var couchdb_server = '{{couchdb_server}}' + couchdb_dbname; var couchdb_server = '{{couchdb_server}}' + couchdb_dbname;
var odoo_server = '{{odoo_server}}';
</script> </script>
<script src="{% static "js/all_common.js" %}?v="></script> <script src="{% static "js/all_common.js" %}?v="></script>
<script type="text/javascript" src="{% static 'js/orders_helper.js' %}?v="></script> <script type="text/javascript" src="{% static 'js/orders_helper.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