You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

491 lines
18 KiB

/* Copyright 2018 GRAP - Sylvain LE GAL
Copyright 2018 Tecnativa - David Vidal
Copyright 2019 Druidoo - Ivan Todorovich
License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). */
odoo.define('pos_order_mgmt.widgets', function (require) {
"use strict";
var core = require('web.core');
var _t = core._t;
var PosBaseWidget = require('point_of_sale.BaseWidget');
var screens = require('point_of_sale.screens');
var gui = require('point_of_sale.gui');
var chrome = require('point_of_sale.chrome');
var models = require('point_of_sale.models');
var QWeb = core.qweb;
var ScreenWidget = screens.ScreenWidget;
var DomCache = screens.DomCache;
screens.ReceiptScreenWidget.include({
render_receipt: function () {
if (!this.pos.reloaded_order) {
return this._super();
}
var order = this.pos.reloaded_order;
this.$('.pos-receipt-container').html(QWeb.render('PosTicket', {
widget: this,
pos: this.pos,
order: order,
receipt: order.export_for_printing(),
orderlines: order.get_orderlines(),
paymentlines: order.get_paymentlines(),
}));
this.pos.from_loaded_order = true;
},
click_next: function () {
if (!this.pos.from_loaded_order) {
return this._super();
}
this.pos.from_loaded_order = false;
// When reprinting a loaded order we temporarily set it as the
// active one. When we get out from the printing screen, we set
// it back to the one that was active
if (this.pos.current_order) {
this.pos.set_order(this.pos.current_order);
this.pos.current_order = false;
}
return this.gui.show_screen(this.gui.startup_screen);
},
});
var OrderListScreenWidget = ScreenWidget.extend({
template: 'OrderListScreenWidget',
init: function (parent, options) {
this._super(parent, options);
this.order_cache = new DomCache();
this.orders = [];
this.unknown_products = [];
this.search_query = false;
this.perform_search();
},
auto_back: true,
show: function () {
var self = this;
var previous_screen = false;
if (this.pos.get_order()) {
previous_screen = this.pos.get_order().get_screen_data(
'previous-screen');
}
if (previous_screen === 'receipt') {
this.gui.screen_instances.receipt.click_next();
this.gui.show_screen('orderlist');
}
this._super();
this.renderElement();
this.old_order = this.pos.get_order();
this.$('.back').click(function () {
return self.gui.show_screen(self.gui.startup_screen);
});
if (this.pos.config.iface_vkeyboard &&
this.chrome.widget.keyboard) {
this.chrome.widget.keyboard.connect(
this.$('.searchbox input'));
}
var search_timeout = null;
this.$('.searchbox input').on('keyup', function () {
self.search_query = this.value;
clearTimeout(search_timeout);
search_timeout = setTimeout(function () {
self.perform_search();
}, 70);
});
this.$('.searchbox .search-clear').click(function () {
self.clear_search();
});
this.perform_search();
},
render_list: function () {
var self = this;
var orders = this.orders;
var contents = this.$el[0].querySelector('.order-list-contents');
contents.innerHTML = "";
for (
var i = 0, len = Math.min(orders.length, 1000); i < len; i++
) {
var order = orders[i];
var orderline = this.order_cache.get_node(
order.id || order.uid);
if (!orderline) {
var orderline_html = QWeb.render('OrderLine', {
widget: this,
order: order,
});
orderline = document.createElement('tbody');
orderline.innerHTML = orderline_html;
orderline = orderline.childNodes[1];
this.order_cache.cache_node(
order.id || order.uid, orderline);
}
if (order === this.old_order) {
orderline.classList.add('highlight');
} else {
orderline.classList.remove('highlight');
}
contents.appendChild(orderline);
}
// FIXME: Everytime the list is rendered we need to reassing the
// button events.
this.$('.order-list-return').off('click');
this.$('.order-list-reprint').off('click');
this.$('.order-list-copy').off('click');
this.$('.order-list-reprint').click(function (event) {
self.order_list_actions(event, 'print');
});
this.$('.order-list-copy').click(function (event) {
self.order_list_actions(event, 'copy');
});
this.$('.order-list-return').click(function (event) {
self.order_list_actions(event, 'return');
});
},
order_list_actions: function (event, action) {
var self = this;
var dataset = event.target.parentNode.dataset;
self.load_order_data(parseInt(dataset.orderId, 10))
.then(function (order_data) {
self.order_action(order_data, action);
});
},
order_action: function (order_data, action) {
if (this.old_order !== null) {
this.gui.back();
}
var order = this.load_order_from_data(order_data, action);
if (!order) {
// The load of the order failed. (products not found, ...
// We cancel the action
return;
}
this['action_' + action](order_data, order);
},
action_print: function (order_data, order) {
// We store temporarily the current order so we can safely compute
// taxes based on fiscal position
this.pos.current_order = this.pos.get_order();
this.pos.set_order(order);
this.pos.reloaded_order = order;
var skip_screen_state = this.pos.config.iface_print_skip_screen;
// Disable temporarily skip screen if set
this.pos.config.iface_print_skip_screen = false;
this.gui.show_screen('receipt');
this.pos.reloaded_order = false;
// Set skip screen to whatever previous state
this.pos.config.iface_print_skip_screen = skip_screen_state;
// If it's invoiced, we also print the invoice
if (order_data.to_invoice) {
this.pos.chrome.do_action('point_of_sale.pos_invoice_report', {
additional_context: { active_ids: [order_data.id] }
})
}
// Destroy the order so it's removed from localStorage
// Otherwise it will stay there and reappear on browser refresh
order.destroy();
},
action_copy: function (order_data, order) {
order.trigger('change');
this.pos.get('orders').add(order);
this.pos.set('selectedOrder', order);
return order;
},
action_return: function (order_data, order) {
order.trigger('change');
this.pos.get('orders').add(order);
this.pos.set('selectedOrder', order);
return order;
},
_prepare_order_from_order_data: function (order_data, action) {
var self = this;
var order = new models.Order({}, {
pos: this.pos,
});
// Get Customer
if (order_data.partner_id) {
order.set_client(
this.pos.db.get_partner_by_id(order_data.partner_id));
}
// Get fiscal position
if (order_data.fiscal_position && this.pos.fiscal_positions) {
var fiscal_positions = this.pos.fiscal_positions;
order.fiscal_position = fiscal_positions.filter(function (p) {
return p.id === order_data.fiscal_position;
})[0];
order.trigger('change');
}
// Get order lines
self._prepare_orderlines_from_order_data(
order, order_data, action);
// Get Name
if (['print'].indexOf(action) !== -1) {
order.name = order_data.pos_reference;
} else if (['return'].indexOf(action) !== -1) {
order.name = _t("Refund ") + order.uid;
}
// Get to invoice
if (['return', 'copy'].indexOf(action) !== -1) {
// If previous order was invoiced, we need a refund too
order.set_to_invoice(order_data.to_invoice);
}
// Get returned Order
if (['print'].indexOf(action) !== -1) {
// Get the same value as the original
order.returned_order_id = order_data.returned_order_id;
order.returned_order_reference =
order_data.returned_order_reference;
} else if (['return'].indexOf(action) !== -1) {
order.returned_order_id = order_data.id;
order.returned_order_reference = order_data.pos_reference;
}
// Get Date
if (['print'].indexOf(action) !== -1) {
order.formatted_validation_date =
moment(order_data.date_order).format('YYYY-MM-DD HH:mm:ss');
}
// Get Payment lines
if (['print'].indexOf(action) !== -1) {
var paymentLines = order_data.statement_ids || [];
_.each(paymentLines, function (paymentLine) {
var line = paymentLine;
// In case of local data
if (line.length === 3) {
line = line[2];
}
_.each(self.pos.cashregisters, function (cashregister) {
if (cashregister.journal.id === line.journal_id) {
if (line.amount > 0) {
// If it is not change
order.add_paymentline(cashregister);
order.selected_paymentline.set_amount(
line.amount);
}
}
});
});
}
return order;
},
_prepare_orderlines_from_order_data: function (
order, order_data, action) {
var orderLines = order_data.line_ids || order_data.lines || [];
var self = this;
_.each(orderLines, function (orderLine) {
var line = orderLine;
// In case of local data
if (line.length === 3) {
line = line[2];
}
var product = self.pos.db.get_product_by_id(line.product_id);
// Check if product are available in pos
if (_.isUndefined(product)) {
self.unknown_products.push(String(line.product_id));
} else {
// Create a new order line
order.add_product(product,
self._prepare_product_options_from_orderline_data(
order, line, action));
// Restore lot information.
if (['return'].indexOf(action) !== -1) {
var orderline = order.get_selected_orderline()
if (orderline.pack_lot_lines) {
_.each(orderline.return_pack_lot_names, function(lot_name) {
orderline.pack_lot_lines.add(new models.Packlotline(
{'lot_name': lot_name}, {'order_line': orderline}
));
})
orderline.trigger('change', orderline);
}
}
}
});
},
_prepare_product_options_from_orderline_data: function (
order, line, action) {
var qty = line.qty;
if (['return'].indexOf(action) !== -1) {
// Invert line quantities
qty *= -1;
}
return {
price: line.price_unit,
quantity: qty,
discount: line.discount,
merge: false,
extras: {
return_pack_lot_names: line.pack_lot_names,
},
}
},
load_order_data: function (order_id) {
var self = this;
return this._rpc({
model: 'pos.order',
method: 'load_done_order_for_pos',
args: [order_id],
}).fail(function (error) {
if (parseInt(error.code, 10) === 200) {
// Business Logic Error, not a connection problem
self.gui.show_popup(
'error-traceback', {
'title': error.data.message,
'body': error.data.debug,
});
} else {
self.gui.show_popup('error', {
'title': _t('Connection error'),
'body': _t(
'Can not execute this action because the POS' +
' is currently offline'),
});
}
});
},
load_order_from_data: function (order_data, action) {
var self = this;
this.unknown_products = [];
var order = self._prepare_order_from_order_data(
order_data, action);
// Forbid POS Order loading if some products are unknown
if (self.unknown_products.length > 0) {
self.gui.show_popup('error-traceback', {
'title': _t('Unknown Products'),
'body': _t('Unable to load some order lines because the ' +
'products are not available in the POS cache.\n\n' +
'Please check that lines :\n\n * ') +
self.unknown_products.join("; \n *"),
});
return false;
}
return order;
},
// Search Part
search_done_orders: function (query) {
var self = this;
return this._rpc({
model: 'pos.order',
method: 'search_done_orders_for_pos',
args: [query || '', this.pos.pos_session.id],
}).then(function (result) {
self.orders = result;
// Get the date in local time
_.each(self.orders, function (order) {
if (order.date_order) {
order.date_order = moment.utc(order.date_order)
.local().format('YYYY-MM-DD HH:mm:ss');
}
});
}).fail(function (error, event) {
if (parseInt(error.code, 10) === 200) {
// Business Logic Error, not a connection problem
self.gui.show_popup(
'error-traceback', {
'title': error.data.message,
'body': error.data.debug,
}
);
} else {
self.gui.show_popup('error', {
'title': _t('Connection error'),
'body': _t(
'Can not execute this action because the POS' +
' is currently offline'),
});
}
event.preventDefault();
});
},
perform_search: function () {
var self = this;
return this.search_done_orders(self.search_query)
.done(function () {
self.render_list();
});
},
clear_search: function () {
var self = this;
self.$('.searchbox input')[0].value = '';
self.$('.searchbox input').focus();
self.search_query = false;
self.perform_search();
},
});
gui.define_screen({
name: 'orderlist',
widget: OrderListScreenWidget,
});
var ListOrderButtonWidget = PosBaseWidget.extend({
template: 'ListOrderButtonWidget',
init: function (parent, options) {
var opts = options || {};
this._super(parent, opts);
this.action = opts.action;
this.label = opts.label;
},
button_click: function () {
this.gui.show_screen('orderlist');
},
renderElement: function () {
var self = this;
this._super();
this.$el.click(function () {
self.button_click();
});
},
});
var widgets = chrome.Chrome.prototype.widgets;
widgets.push({
'name': 'list_orders',
'widget': ListOrderButtonWidget,
'prepend': '.pos-rightheader',
'args': {
'label': 'All Orders',
},
});
return {
ListOrderButtonWidget: ListOrderButtonWidget,
OrderListScreenWidget: OrderListScreenWidget,
};
});